pax_global_header00006660000000000000000000000064145570173130014520gustar00rootroot0000000000000052 comment=5ad744922aeed83ecf2bbdb83c783bbfc23b4192 poppler-24.02.0/000077500000000000000000000000001455701731300133465ustar00rootroot00000000000000poppler-24.02.0/.clang-tidy000066400000000000000000000017621455701731300154100ustar00rootroot00000000000000--- 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,-bugprone-assignment-in-if-condition,-bugprone-unchecked-optional-access' WarningsAsErrors: '*' HeaderFilterRegex: '.' AnalyzeTemporaryDtors: false FormatStyle: none User: user ... poppler-24.02.0/.git-blame-ignore-revs000066400000000000000000000005021455701731300174430ustar00rootroot00000000000000# _clang_format added 814fbda28cc8a37fed3134c2db8da28f86fb5ee0 # readability-braces-around-statements added f7466e31408ba884c44728f7da04227863177241 # clang-format-14 wanted a bit of reformat a94a444e5acff86e04d1688d862e81a82108cf03 # clang-format-15 wanted a bit of reformat also b5aa09403e0f106f51292683c1841908167337ea poppler-24.02.0/.gitattributes000066400000000000000000000002041455701731300162350ustar00rootroot00000000000000.gitattributes export-ignore .gitignore export-ignore */.gitignore export-ignore */*/.gitignore export-ignore regtest export-ignore poppler-24.02.0/.gitignore000066400000000000000000000005501455701731300153360ustar00rootroot00000000000000Makefile ChangeLog gtkdoc.pyc poppler-cairo.pc poppler-cpp.pc poppler-glib.pc poppler-qt.pc poppler-qt5.pc poppler-splash.pc poppler.pc gtk-doc.make *.o *~ *.exe poppler-cairo-uninstalled.pc poppler-cpp-uninstalled.pc poppler-glib-uninstalled.pc poppler-qt-uninstalled.pc poppler-qt5-uninstalled.pc poppler-splash-uninstalled.pc poppler-uninstalled.pc /build/ poppler-24.02.0/.gitlab-ci.yml000066400000000000000000000263471455701731300160160ustar00rootroot00000000000000image: debian:unstable stages: - build - document - publish 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 libbrotli-dev libboost-container-dev qt6-base-dev - echo 'en_US.UTF-8 UTF-8' >> /etc/locale.gen - locale-gen variables: LANG: en_US.UTF-8 LANGUAGE: en_US:en LC_ALL: en_US.UTF-8 DEBIAN_FRONTEND: noninteractive TEST_DATA_URL: https://gitlab.freedesktop.org/${CI_PROJECT_NAMESPACE}/test.git UPSTREAM_TEST_DATA_URL: https://gitlab.freedesktop.org/poppler/test.git cache: key: "$CI_JOB_NAME" paths: - build/ clang_format: stage: build before_script: - apt-get update - apt-get install --yes --no-install-recommends git clang-format-16 script: - find . \( -name "*.cpp" -or -name "*.h" -or -name "*.c" -or -name "*.cc" \) -exec clang-format-16 -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 -DCMAKE_PREFIX_PATH=$PWD/gnupg .. - ninja -j ${FDO_CI_CONCURRENT} - ctest --output-on-failure build_clang16_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 - 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-16-dev llvm-16-dev libc++-16-dev libc++abi-16-dev clang-tidy-16 clang-16 libunwind-16-dev gperf jq - srcdir=`pwd` && mkdir -p /tmp/poppler_build && cd /tmp/poppler_build - clang++-16 -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-16/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-16 CXX=clang++-16 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" -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 - cp compile_commands.aux.json compile_commands.json - echo "Cheat a bit and remove the moc includes so that we don't lint autogenerated code" - 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-16 build_ubuntu_20_04: stage: build image: ubuntu:20.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 wget p7zip-full git - wget -r -l1 -np "https://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt6_620/qt.qt6.620.gcc_64/" -A "6.2.0-0-*qtbase-Linux-RHEL_8_2-GCC-Linux-RHEL_8_2-X86_64.7z" - 7z x download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt6_620/qt.qt6.620.gcc_64/6.2.0-0-*qtbase-Linux-RHEL_8_2-GCC-Linux-RHEL_8_2-X86_64.7z - wget -r -l1 -np "https://download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt6_620/qt.qt6.620.gcc_64/" -A "6.2.0-0-*icu-linux-Rhel7.2-x64.7z" - 7z x download.qt.io/online/qtsdkrepository/linux_x64/desktop/qt6_620/qt.qt6.620.gcc_64/6.2.0-0-*icu-linux-Rhel7.2-x64.7z 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 -DENABLE_GPGME=OFF -DTESTDATADIR=$PWD/../test-data -DCMAKE_PREFIX_PATH=$PWD/../6.2.0/gcc_64/lib/cmake .. - ninja - ctest --output-on-failure build_mingw64_fedora39: stage: build image: fedora:39 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 mingw64-gcc-c++ cmake-rpm-macros script: - mkdir -p build && cd build - mingw64-cmake -DENABLE_NSS3=OFF -DENABLE_GPGME=OFF -G Ninja .. - ninja build_clazy_clang16: stage: build script: - apt-get install --yes --no-install-recommends clazy clang-16 - mkdir -p build && cd build - CC=clang-16 CXX=clazy CXXFLAGS="-Werror -Wno-deprecated-declarations" cmake -DENABLE_GPGME=OFF -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_qt5_android: stage: build image: kdeorg/android-sdk 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 script: - 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_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_QT6=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 -j ${FDO_CI_CONCURRENT} build_qt5_android_generic: stage: build image: kdeorg/android-sdk 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 script: - 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_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_QT6=OFF -DENABLE_NSS3=OFF -DENABLE_GPGME=OFF -DFONT_CONFIGURATION=generic -DCMAKE_CXX_FLAGS="-Werror -Wno-deprecated-declarations" -DCMAKE_TOOLCHAIN_FILE=/opt/nativetooling/share/ECM/toolchain/Android.cmake' - ninja -j ${FDO_CI_CONCURRENT} build_qt6_android: stage: build image: invent-registry.kde.org/sysadmin/ci-images/android-qt65 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 script: - 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=/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: - master stage: document variables: QT_SELECT: qt5 script: - apt-get install --yes --no-install-recommends doxygen graphviz qtchooser qttools5-dev-tools - cd qt5/src - doxygen cache: {} artifacts: paths: - qt5/src/APIDOCS-html qt6_docs: only: - master stage: document script: - apt-get install --yes --no-install-recommends doxygen graphviz qt6-documentation-tools - cd qt6/src - ( cat Doxyfile ; echo "QHG_LOCATION=/usr/lib/qt6/bin/qhelpgenerator" ) | doxygen - cache: {} artifacts: paths: - qt6/src/APIDOCS-html cpp_docs: only: - master stage: document script: - apt-get install --yes --no-install-recommends doxygen graphviz - cd cpp - doxygen cache: {} artifacts: paths: - cpp/APIDOCS-html glib_docs: only: - master stage: document script: - apt-get install --yes --no-install-recommends gtk-doc-tools - mkdir -p build && cd build - cmake -G Ninja -DENABLE_GTK_DOC=YES -DENABLE_GPGME=OFF .. - ninja -j ${FDO_CI_CONCURRENT} glib-docs cache: {} artifacts: paths: - build/glib/reference/html trigger_pages: only: - master stage: publish image: alpine:latest before_script: - apk --update upgrade - apk add curl ca-certificates script: - 'curl --request POST --form "token=$WEB_PAGE_TRIGGER" --form ref=master https://gitlab.freedesktop.org/api/v4/projects/poppler%2Fpoppler-web-page/trigger/pipeline' cache: {} poppler-24.02.0/.gitlab/000077500000000000000000000000001455701731300146665ustar00rootroot00000000000000poppler-24.02.0/.gitlab/merge_request_templates/000077500000000000000000000000001455701731300216135ustar00rootroot00000000000000poppler-24.02.0/.gitlab/merge_request_templates/merge_request_template.md000066400000000000000000000003301455701731300266730ustar00rootroot00000000000000Please make sure you check the "Allow commits from members who can merge to the target branch" option at the bottom of the page. Makes our life much easier since we can rebase and merge from the web user interface. poppler-24.02.0/AUTHORS000066400000000000000000000002211455701731300144110ustar00rootroot00000000000000xpdf is written by Derek Noonburg libpoppler is a fork of xpdf-3.00 Current Maintainer: Albert Astals Cid poppler-24.02.0/CMakeLists.txt000066400000000000000000000752671455701731300161270ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.16.0 FATAL_ERROR) project(poppler) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules) include(PopplerDefaults) include(PopplerMacros) # Ensure that the user-provided C_FLAGS are used for try_compile() calls. # This is needed since PopplerMacros.cmake clears CMAKE_C_FLAGS and if # CMAKE_TRY_COMPILE_CONFIGURATION is empty CMake only uses the flags # specified in CMAKE_C_FLAGS (https://gitlab.kitware.com/cmake/cmake/-/issues/22414 # and https://gitlab.kitware.com/cmake/cmake/-/issues/19512). # We therefore have to explicitly set CMAKE_TRY_COMPILE_CONFIGURATION until we # depend on a CMake release that includes a fix for those issues. # This is set after including PopplerMacros since that sets the default # CMAKE_BUILD_TYPE and also sets _CMAKE_BUILD_TYPE_UPPER. set(CMAKE_TRY_COMPILE_CONFIGURATION "${_CMAKE_BUILD_TYPE_UPPER}") include(MacroOptionalFindPackage) find_package(PkgConfig) include(TestBigEndian) test_big_endian(WORDS_BIGENDIAN) include(CheckFileOffsetBits) CHECK_FILE_OFFSET_BITS() include(GenerateExportHeader) include(GNUInstallDirs) include(CMakePushCheckState) set(ENABLE_FUZZER FALSE) find_package (ECM 1.6.0 QUIET NO_MODULE) if (ECM_FOUND) include("${ECM_MODULE_DIR}/ECMEnableSanitizers.cmake") if(ECM_ENABLE_SANITIZERS MATCHES fuzzer) set(ENABLE_FUZZER TRUE) endif() endif() set(POPPLER_MAJOR_VERSION "24") set(POPPLER_MINOR_VERSION_STRING "02") # 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_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_C_STANDARD 11) 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) # command line switches option(ENABLE_UNSTABLE_API_ABI_HEADERS "Install API/ABI unstable xpdf headers." OFF) option(BUILD_GTK_TESTS "Whether to compile the GTK+ test programs." ON) option(BUILD_QT5_TESTS "Whether to compile the Qt5 test programs." ON) option(BUILD_QT6_TESTS "Whether to compile the Qt6 test programs." ON) option(BUILD_CPP_TESTS "Whether to compile the CPP test programs." ON) option(BUILD_MANUAL_TESTS "Whether to compile manual test programs." ON) option(ENABLE_BOOST "Use boost (for Splash backend performance)." ON) option(ENABLE_UTILS "Compile poppler command line utils." ON) option(ENABLE_CPP "Compile poppler cpp wrapper." ON) option(ENABLE_GLIB "Compile poppler glib wrapper." ON) option(ENABLE_GOBJECT_INTROSPECTION "Whether to generate GObject introspection." ON) 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_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_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_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) if(WIN32) option(ENABLE_RELOCATABLE "Do not hardcode the poppler library location (on Windows)." ON) else() set(ENABLE_RELOCATABLE OFF) endif() option(EXTRA_WARN "Enable extra compile warnings" OFF) set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) set(TESTDATADIR "${CMAKE_SOURCE_DIR}/../test" CACHE STRING "Specify test data dir.") if(NOT (EXISTS ${TESTDATADIR} AND EXISTS ${TESTDATADIR}/test-poppler.c)) message(WARNING " No test data found in $testdatadir. You will not be able to run 'make test' successfully. The test data is not included in the source packages and is also not part of the main git repository. Instead, you can checkout the test data from its own git repository with: git clone git://git.freedesktop.org/git/poppler/test You should checkout the test data as a sibling of your poppler source folder or specify the location of your checkout with -DTESTDATADIR=/path/to/checkoutdir/test. ") endif() if(WIN32) set(_default_fontconfiguration "win32") elseif(ANDROID) set(_default_fontconfiguration "android") else() set(_default_fontconfiguration "fontconfig") endif() 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 win32, android, or fontconfig specific code") else() message(FATAL_ERROR "Invalid font configuration setting: ${FONT_CONFIGURATION}") endif() # Enable these unconditionally. set(OPI_SUPPORT ON) set(TEXTOUT_WORD_LIST ON) # setting the minimum required versions for some components set(CAIRO_VERSION "1.16.0") set(GLIB_REQUIRED "2.64") set(GTK_REQUIRED "3.24") set(GDK_PIXBUF_REQUIRED "2.40") set(FREETYPE_VERSION "2.10") set(FONTCONFIG_VERSION "2.13") find_package(Freetype ${FREETYPE_VERSION} REQUIRED) if(WITH_FONTCONFIGURATION_FONTCONFIG) 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.1) macro_optional_find_package(JPEG) macro_optional_find_package(PNG) 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.") message(FATAL_ERROR "Install libjpeg before trying to build poppler. 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(HAVE_DCT_DECODER ON) elseif(ENABLE_DCTDECODER STREQUAL "unmaintained") set(ENABLE_LIBJPEG OFF) set(HAVE_DCT_DECODER ON) elseif(ENABLE_DCTDECODER STREQUAL "none") set(ENABLE_LIBJPEG OFF) set(HAVE_DCT_DECODER OFF) else() message(FATAL_ERROR "Invalid ENABLE_DCTDECODER value.") endif() set(QT5_VERSION "5.12") # Update QT_DISABLE_DEPRECATED_BEFORE in qt5/CMakeLists.txt when changing this 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}) 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.71.0) if(Boost_FOUND) set(USE_BOOST_HEADERS ON) elseif(ENABLE_BOOST) message(FATAL_ERROR "-- Boost recommended for Splash. Use ENABLE_BOOST=OFF to skip.") endif() if(CAIRO_FOUND) set(HAVE_CAIRO ${CAIRO_FOUND}) set(CAIRO_FEATURE "#define POPPLER_HAS_CAIRO 1") set(CAIRO_REQ "cairo") set(POPPLER_GLIB_DISABLE_DEPRECATED "") set(POPPLER_GLIB_DISABLE_SINGLE_INCLUDES "") if(ENABLE_GLIB) macro_optional_find_package(GLIB) if(NOT GLIB_FOUND) set(ENABLE_GLIB OFF) endif() endif() if(ENABLE_GLIB) if(ENABLE_GOBJECT_INTROSPECTION) # Check for introspection macro_optional_find_package(GObjectIntrospection 1.64.0) set(HAVE_INTROSPECTION ${INTROSPECTION_FOUND}) endif() set(POPPLER_GLIB_DISABLE_DEPRECATED "${POPPLER_GLIB_DISABLE_DEPRECATED} -DG_DISABLE_DEPRECATED") set(POPPLER_GLIB_DISABLE_SINGLE_INCLUDES "${POPPLER_GLIB_DISABLE_SINGLE_INCLUDES} -DG_DISABLE_SINGLE_INCLUDES") macro_optional_find_package(GTK) endif() else() set(CAIRO_FEATURE "#undef POPPLER_HAS_CAIRO") set(ENABLE_GLIB OFF) endif() # GTK API docs require both the gtk-doc package & python3 support if(ENABLE_GTK_DOC) # Stop the build & raise an error if the package is missing find_package(GtkDoc) if(NOT GtkDoc_FOUND) message(FATAL_ERROR "Install the gtk-doc package to generate GTK API documentation, or set ENABLE_GTK_DOC to Off.") endif() # NOTE: The FindPythonInterp module is deprecated, but the newer FindPython3 module requires CMake >=3.12 find_package(PythonInterp 3) # Also bail out with an error if Python3 is missing if(NOT PYTHONINTERP_FOUND) message(FATAL_ERROR "Install python3 in order to generate GTK API documentation, or set ENABLE_GTK_DOC to Off.") endif() 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() find_package(ZLIB REQUIRED) set(WITH_OPENJPEG FALSE) if(ENABLE_LIBOPENJPEG STREQUAL "openjpeg2") find_package(OpenJPEG) set(WITH_OPENJPEG ${OpenJPEG_FOUND}) if(NOT OpenJPEG_FOUND OR OPENJPEG_MAJOR_VERSION VERSION_LESS 2) message(STATUS "Could NOT find openjpeg2.") message(FATAL_ERROR "Install libopenjpeg2 before trying to build poppler. You \ can also decide to use the internal unmaintained JPX decoder or none at all.\n\ Possible options are: -DENABLE_LIBOPENJPEG=openjpeg2, -DENABLE_LIBOPENJPEG=none, \ -DENABLE_LIBOPENJPEG=unmaintained,") endif() set(HAVE_JPX_DECODER ON) elseif(ENABLE_LIBOPENJPEG STREQUAL "unmaintained") set(WITH_OPENJPEG OFF) set(HAVE_JPX_DECODER ON) elseif(ENABLE_LIBOPENJPEG STREQUAL "none") set(WITH_OPENJPEG OFF) set(HAVE_JPX_DECODER OFF) else() message(FATAL_ERROR "Invalid ENABLE_LIBOPENJPEG value: ${ENABLE_LIBOPENJPEG}") endif() set(ENABLE_LIBOPENJPEG "${WITH_OPENJPEG}") find_soft_mandatory_package(ENABLE_LCMS LCMS2 2.9) set(USE_CMS ${ENABLE_LCMS}) find_soft_mandatory_package(ENABLE_LIBCURL CURL 7.68) set(POPPLER_HAS_CURL_SUPPORT ${ENABLE_LIBCURL}) if(MINGW) # Use mingw's ansi stdio extensions add_definitions(-D__USE_MINGW_ANSI_STDIO=1) endif() if(WITH_FONTCONFIGURATION_WIN32) if(MINGW) # Set the minimum required Internet Explorer version to 5.0 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(PNG_FOUND) set(ENABLE_LIBPNG ON) endif() 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. # Use it to determine the encoding data path, if available. # Default to the same prefix otherwise. pkg_check_modules(POPPLER_DATA poppler-data) if(POPPLER_DATA_FOUND) execute_process(COMMAND "${PKG_CONFIG_EXECUTABLE}" --variable=poppler_datadir poppler-data RESULT_VARIABLE _result_var OUTPUT_VARIABLE _output_var OUTPUT_STRIP_TRAILING_WHITESPACE) if(_result_var STREQUAL "0" AND NOT _output_var STREQUAL "") set(POPPLER_DATADIR "${_output_var}") endif() endif() if(NOT DEFINED POPPLER_DATADIR) set(POPPLER_DATADIR "${CMAKE_INSTALL_PREFIX}/share/poppler") endif() if(EXTRA_WARN) set(CMAKE_C_FLAGS "-Wall ${CMAKE_C_FLAGS}") set(CMAKE_CXX_FLAGS "${DEFAULT_COMPILE_WARNINGS_EXTRA} ${CMAKE_CXX_FLAGS}") else() set(CMAKE_C_FLAGS "-Wall ${CMAKE_C_FLAGS}") set(CMAKE_CXX_FLAGS "${DEFAULT_COMPILE_WARNINGS} ${CMAKE_CXX_FLAGS}") endif() include(ConfigureChecks.cmake) configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) configure_file(poppler/poppler-config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/poppler/poppler-config.h) find_program(GPERF gperf) find_program(SED sed) set(poppler_SRCS goo/GooString.cc goo/GooTimer.cc goo/ImgWriter.cc goo/JpegWriter.cc goo/NetPBMWriter.cc goo/PNGWriter.cc goo/TiffWriter.cc goo/ft_utils.cc goo/gbase64.cc goo/gbasename.cc goo/gfile.cc goo/glibc.cc goo/glibc_strtok_r.cc goo/grandom.cc goo/gstrtod.cc fofi/FoFiBase.cc fofi/FoFiEncodings.cc fofi/FoFiTrueType.cc fofi/FoFiType1.cc fofi/FoFiType1C.cc fofi/FoFiIdentifier.cc poppler/Annot.cc poppler/AnnotStampImageHelper.cc poppler/Array.cc poppler/CachedFile.cc poppler/Catalog.cc poppler/CharCodeToUnicode.cc poppler/CMap.cc poppler/CryptoSignBackend.cc poppler/DateInfo.cc poppler/Decrypt.cc poppler/Dict.cc poppler/Error.cc poppler/FDPDFDocBuilder.cc poppler/FILECacheLoader.cc poppler/FileSpec.cc poppler/FlateEncoder.cc poppler/FontEncodingTables.cc poppler/Form.cc poppler/FontInfo.cc poppler/Function.cc poppler/Gfx.cc poppler/GfxFont.cc poppler/GfxState.cc poppler/GlobalParams.cc poppler/Hints.cc poppler/ImageEmbeddingUtils.cc poppler/JArithmeticDecoder.cc poppler/JBIG2Stream.cc poppler/JSInfo.cc poppler/Lexer.cc poppler/Link.cc poppler/Linearization.cc poppler/LocalPDFDocBuilder.cc poppler/MarkedContentOutputDev.cc poppler/NameToCharCode.cc poppler/Object.cc poppler/OptionalContent.cc poppler/Outline.cc poppler/OutputDev.cc poppler/Page.cc poppler/PageTransition.cc poppler/Parser.cc poppler/PDFDoc.cc poppler/PDFDocBuilder.cc poppler/PDFDocEncoding.cc poppler/PDFDocFactory.cc poppler/ProfileData.cc poppler/PreScanOutputDev.cc poppler/PSTokenizer.cc poppler/SignatureInfo.cc poppler/Stream.cc poppler/StructTreeRoot.cc poppler/StructElement.cc poppler/UnicodeMap.cc poppler/UnicodeMapFuncs.cc poppler/UnicodeTypeTable.cc poppler/UTF.cc poppler/XRef.cc poppler/PSOutputDev.cc poppler/TextOutputDev.cc poppler/PageLabelInfo.cc poppler/SecurityHandler.cc poppler/Sound.cc poppler/ViewerPreferences.cc poppler/Movie.cc poppler/Rendition.cc poppler/CertificateInfo.cc poppler/BBoxOutputDev.cc poppler/SplashOutputDev.cc splash/Splash.cc splash/SplashBitmap.cc splash/SplashClip.cc splash/SplashFTFont.cc splash/SplashFTFontEngine.cc splash/SplashFTFontFile.cc splash/SplashFont.cc splash/SplashFontEngine.cc splash/SplashFontFile.cc splash/SplashFontFileID.cc splash/SplashPath.cc splash/SplashPattern.cc splash/SplashScreen.cc splash/SplashState.cc splash/SplashXPath.cc splash/SplashXPathScanner.cc ) set(poppler_LIBS Freetype::Freetype ZLIB::ZLIB) if(FONTCONFIG_FOUND) set(poppler_LIBS ${poppler_LIBS} Fontconfig::Fontconfig) endif() if(Androidlib) set(poppler_LIBS ${poppler_LIBS} ${Androidlib}) endif() if(JPEG_FOUND) set(poppler_SRCS ${poppler_SRCS} poppler/DCTStream.cc ) set(poppler_LIBS ${poppler_LIBS} JPEG::JPEG) endif() if(ENABLE_ZLIB_UNCOMPRESS) set(poppler_SRCS ${poppler_SRCS} poppler/FlateStream.cc ) endif() if(ENABLE_LIBCURL) set(poppler_SRCS ${poppler_SRCS} poppler/CurlCachedFile.cc poppler/CurlPDFDocBuilder.cc ) set(poppler_LIBS ${poppler_LIBS} CURL::libcurl) endif() if (ENABLE_NSS3) set(poppler_SRCS ${poppler_SRCS} 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) else () set(poppler_SRCS ${poppler_SRCS} poppler/JPXStream.cc ) endif() if(USE_CMS) set(poppler_LIBS ${poppler_LIBS} ${LCMS2_LIBRARIES}) endif() if(WIN32) # use clean APIs add_definitions(-DWIN32_LEAN_AND_MEAN) # gdi32 is needed under win32 set(poppler_LIBS ${poppler_LIBS} gdi32) endif() if(PNG_FOUND) set(poppler_LIBS ${poppler_LIBS} PNG::PNG) endif() if(ENABLE_LIBTIFF) set(poppler_LIBS ${poppler_LIBS} TIFF::TIFF) endif() if(Boost_FOUND) set(poppler_LIBS ${poppler_LIBS} Boost::boost) endif() if (GPERF AND SED AND RUN_GPERF_IF_PRESENT) macro(ADD_GPERF_FILE input) add_custom_command(OUTPUT poppler/${input}.c COMMAND ${GPERF} poppler/${input}.gperf > ${CMAKE_CURRENT_BINARY_DIR}/poppler/${input}.c COMMAND ${GPERF} poppler/${input}.gperf > ${CMAKE_CURRENT_SOURCE_DIR}/poppler/${input}.pregenerated.c COMMAND ${SED} -i -e "s#${GPERF}#gperf#" ${CMAKE_CURRENT_SOURCE_DIR}/poppler/${input}.pregenerated.c COMMAND clang-format -i ${CMAKE_CURRENT_SOURCE_DIR}/poppler/${input}.pregenerated.c || true DEPENDS poppler/${input}.gperf WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) set(poppler_SRCS ${poppler_SRCS} poppler/${input}.c ) endmacro() else() macro(ADD_GPERF_FILE input) set(poppler_SRCS ${poppler_SRCS} poppler/${input}.pregenerated.c ) endmacro() endif() ADD_GPERF_FILE(CourierWidths) ADD_GPERF_FILE(CourierBoldWidths) ADD_GPERF_FILE(CourierBoldObliqueWidths) ADD_GPERF_FILE(CourierObliqueWidths) ADD_GPERF_FILE(HelveticaWidths) ADD_GPERF_FILE(HelveticaBoldWidths) ADD_GPERF_FILE(HelveticaBoldObliqueWidths) ADD_GPERF_FILE(HelveticaObliqueWidths) ADD_GPERF_FILE(SymbolWidths) ADD_GPERF_FILE(TimesBoldWidths) ADD_GPERF_FILE(TimesBoldItalicWidths) ADD_GPERF_FILE(TimesItalicWidths) ADD_GPERF_FILE(TimesRomanWidths) ADD_GPERF_FILE(ZapfDingbatsWidths) set(POPPLER_SOVERSION_NUMBER "134") 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} ${LINKER_SCRIPT}) 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 ${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}") endif() 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 poppler/Annot.h poppler/AnnotStampImageHelper.h poppler/Array.h poppler/CachedFile.h poppler/Catalog.h poppler/CharCodeToUnicode.h poppler/CMap.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 poppler/Gfx.h poppler/GfxFont.h poppler/GfxState.h poppler/GfxState_helpers.h poppler/GlobalParams.h poppler/Hints.h poppler/JArithmeticDecoder.h poppler/JBIG2Stream.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 poppler/OutputDev.h poppler/Page.h poppler/PageTransition.h poppler/Parser.h poppler/PDFDoc.h poppler/PDFDocBuilder.h poppler/PDFDocEncoding.h poppler/PDFDocFactory.h poppler/PopplerCache.h poppler/ProfileData.h poppler/PreScanOutputDev.h poppler/PSTokenizer.h poppler/Rendition.h poppler/CertificateInfo.h poppler/Stream-CCITT.h poppler/Stream.h poppler/StructElement.h poppler/StructTreeRoot.h poppler/UnicodeMap.h poppler/UnicodeMapFuncs.h poppler/UnicodeMapTables.h poppler/UnicodeTypeTable.h poppler/UnicodeCClassTables.h poppler/UnicodeCompTables.h poppler/UnicodeDecompTables.h poppler/ViewerPreferences.h poppler/XRef.h poppler/CharTypes.h poppler/ErrorCodes.h 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 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(ENABLE_LIBTIFF) 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 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 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 splash/SplashFontFileID.h splash/SplashGlyphBitmap.h 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) install(FILES poppler/CairoFontEngine.h poppler/CairoOutputDev.h poppler/CairoRescaleBox.h DESTINATION include/poppler) endif() endif() if(ENABLE_UTILS) add_subdirectory(utils) endif() if(ENABLE_GLIB) add_subdirectory(glib) endif() if (BUILD_MANUAL_TESTS) add_subdirectory(test) endif() if(ENABLE_QT5) add_subdirectory(qt5) endif() if(ENABLE_QT6) add_subdirectory(qt6) endif() if(ENABLE_CPP) add_subdirectory(cpp) endif() # Configure "Requires" field & install .pc files for packagers set(PC_REQUIRES "") set(PC_REQUIRES_PRIVATE "Requires.private: poppler = ${POPPLER_VERSION}") if(PKG_CONFIG_EXECUTABLE) poppler_create_install_pkgconfig(poppler.pc ${CMAKE_INSTALL_LIBDIR}/pkgconfig) if(ENABLE_QT5) poppler_create_install_pkgconfig(poppler-qt5.pc ${CMAKE_INSTALL_LIBDIR}/pkgconfig) endif() if(ENABLE_QT6) poppler_create_install_pkgconfig(poppler-qt6.pc ${CMAKE_INSTALL_LIBDIR}/pkgconfig) endif() if(ENABLE_GLIB) poppler_create_install_pkgconfig(poppler-glib.pc ${CMAKE_INSTALL_LIBDIR}/pkgconfig) endif() if(ENABLE_CPP) poppler_create_install_pkgconfig(poppler-cpp.pc ${CMAKE_INSTALL_LIBDIR}/pkgconfig) endif() else() MESSAGE(STATUS ".pc files will not be installed becasue of missing 'pkg-config'!") endif() # Summarize build options & display warnings for user message("Building Poppler with support for:") show_end_message("font configuration" ${font_configuration}) show_end_message_yesno("use boost (Splash)" ENABLE_BOOST) show_end_message_yesno("cairo output" CAIRO_FOUND) show_end_message_yesno("qt5 wrapper" ENABLE_QT5) show_end_message_yesno("qt6 wrapper" ENABLE_QT6) show_end_message_yesno("glib wrapper" ENABLE_GLIB) show_end_message_yesno(" introspection" INTROSPECTION_FOUND) show_end_message_yesno(" gtk-doc" ENABLE_GTK_DOC) 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 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("use curl" ENABLE_LIBCURL) show_end_message_yesno("use libopenjpeg2" WITH_OPENJPEG) show_end_message_yesno("use lcms2" USE_CMS) show_end_message_yesno("command line utils" ENABLE_UTILS) show_end_message_yesno("fuzz target" ENABLE_FUZZER) show_end_message("test data dir" ${TESTDATADIR}) if(NOT ENABLE_LIBJPEG AND HAVE_DCT_DECODER) message("Warning: Using libjpeg is recommended. The internal DCT decoder is unmaintained.") endif() if(NOT HAVE_DCT_DECODER) message("Warning: You're not compiling any DCT decoder. Some files will fail to display properly.") endif() if(ENABLE_ZLIB_UNCOMPRESS) message("Warning: Using zlib is not totally safe") endif() if(NOT WITH_OPENJPEG AND HAVE_JPX_DECODER) message("Warning: Using libopenjpeg2 is recommended. The internal JPX decoder is unmaintained.") endif() if(NOT HAVE_JPX_DECODER) message("Warning: You're not compiling any JPX decoder. Some files will fail to display properly.") endif() if(NOT ENABLE_BOOST) message("Warning: Use of boost is recommended for better performance.") endif() set(ARCHIVE_NAME ${CMAKE_PROJECT_NAME}-${POPPLER_VERSION}) add_custom_target(dist COMMAND COMMAND git log --stat | fmt --split-only > ${CMAKE_BINARY_DIR}/ChangeLog COMMAND git archive --prefix=${ARCHIVE_NAME}/ HEAD > ${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar COMMAND tar -C ${CMAKE_BINARY_DIR} -rf ${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar ChangeLog --transform='s,,${ARCHIVE_NAME}/,' --owner root:0 --group root:0 COMMAND tar -C ${CMAKE_BINARY_DIR} -rf ${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar glib/reference/html --transform='s,,${ARCHIVE_NAME}/,' --owner root:0 --group root:0 COMMAND xz -9 ${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) poppler-24.02.0/COPYING000066400000000000000000000431031455701731300144020ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 of the License, 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. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. poppler-24.02.0/COPYING3000066400000000000000000001045131455701731300144700ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 3 of the License, 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, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . poppler-24.02.0/ConfigureChecks.cmake000066400000000000000000000037611455701731300174210ustar00rootroot00000000000000# Copyright 2008 Pino Toscano, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. include(CheckIncludeFile) include(CheckIncludeFileCXX) include(CheckIncludeFiles) include(CheckSymbolExists) include(CheckFunctionExists) include(CheckLibraryExists) include(CheckTypeSize) include(CheckCSourceCompiles) include(CMakePushCheckState) cmake_push_check_state() # this is going to be defined via config.h, and impacts Android's stdio.h if (_FILE_OFFSET_BITS) set(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} -D_FILE_OFFSET_BITS=${_FILE_OFFSET_BITS}) endif() check_include_files(dlfcn.h HAVE_DLFCN_H) check_include_files(fcntl.h HAVE_FCNTL_H) check_include_files(stdlib.h HAVE_STDLIB_H) check_include_files(sys/mman.h HAVE_SYS_MMAN_H) check_include_files(sys/stat.h HAVE_SYS_STAT_H) check_include_files(unistd.h HAVE_UNISTD_H) check_function_exists(fseek64 HAVE_FSEEK64) check_symbol_exists(fseeko "stdio.h" HAVE_FSEEKO) check_function_exists(ftell64 HAVE_FTELL64) check_function_exists(pread64 HAVE_PREAD64) check_function_exists(lseek64 HAVE_LSEEK64) check_function_exists(gmtime_r HAVE_GMTIME_R) check_function_exists(timegm HAVE_TIMEGM) check_function_exists(gettimeofday HAVE_GETTIMEOFDAY) check_function_exists(localtime_r HAVE_LOCALTIME_R) check_function_exists(popen HAVE_POPEN) check_function_exists(mkstemp HAVE_MKSTEMP) check_function_exists(strtok_r HAVE_STRTOK_R) macro(CHECK_FOR_DIR include var) check_c_source_compiles( "#include <${include}> int main(int argc, char *argv[]) { DIR* d = 0; return 0; } " ${var}) endmacro(CHECK_FOR_DIR) check_for_dir("dirent.h" HAVE_DIRENT_H) check_for_dir("ndir.h" HAVE_NDIR_H) check_for_dir("sys/dir.h" HAVE_SYS_DIR_H) check_for_dir("sys/ndir.h" HAVE_SYS_NDIR_H) check_function_exists("nanosleep" HAVE_NANOSLEEP) if(NOT HAVE_NANOSLEEP) check_library_exists("rt" "nanosleep" "" LIB_RT_HAS_NANOSLEEP) endif(NOT HAVE_NANOSLEEP) cmake_pop_check_state() poppler-24.02.0/INSTALL000066400000000000000000000050651455701731300144050ustar00rootroot00000000000000Installation Instructions ************************* Basic Installation ================== mkdir build cd build cmake .. make make install CMake configuration options can be set using the -D option. eg cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=release Build Options ============= Set install prefix: -DCMAKE_INSTALL_PREFIX= Set build type. This sets the standard compiler flags for the build type. -DCMAKE_BUILD_TYPE=debug or -DCMAKE_BUILD_TYPE=release Set compiler flags: -DCMAKE_CXX_FLAGS= or set CXXFLAGS environment variable Set linker flags: -DCMAKE_LD_FLAGS= or set LDFLAGS environment variable Optional Features ================= -D= eg -DENABLE_LIBCURL=ON -DBUILD_GTK_TESTS=OFF A list of all options can be display with the commmand: egrep '^ *(option|set.*STRING)' CMakeLists.txt Alternatively, the options can be edited by running "ccmake ." in the build directory. Cross Compiling =============== A toolchain file is required to specify the target specific compiler tools. Run cmake with the option: -DCMAKE_TOOLCHAIN_FILE= A sample toolchain for a 64-bit mingw build is shown below. Replace /path/to/win/root with the install prefix for the target environment. set(CMAKE_SYSTEM_NAME Windows) set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++) set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres) set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32 /path/to/win/root ) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) Debugging Options ================= Debug Build Types ----------------- Release build with debugging information: -DCMAKE_BUILD_TYPE=relwithdebinfo Debug build with optimization except for some code re-ordering optimizations: -DCMAKE_BUILD_TYPE=debug Debug build with no optimization: -DCMAKE_BUILD_TYPE=debugfull Release build with debugging and profiling information: -DCMAKE_BUILD_TYPE=profile Address Sanitizer ----------------- Ensure the extra cmake modules are available (may be a separate package) then use -DECM_ENABLE_SANITIZERS to specify the santizers. eg -DECM_ENABLE_SANITIZERS='address;leak;undefined' Some options may only be available with clang. Use -DCMAKE_CXX_COMPILER=clang++ to build with clang. The sanitizer can also be combined with fuzz testing by using Clang 6.0 or later and additionally enabling the sanitizer fuzzer which will enable the fuzz target cpp/tests/pdf_fuzzer. poppler-24.02.0/NEWS000066400000000000000000004663531455701731300140660ustar00rootroot00000000000000Release 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 Release 22.11.0: core: * CairoOutputDev: Update font after restore * Protect against broken files * Small code refactoring Release 22.10.0: core: * SplashOutputDev::tilingPatternFill: Properly restore CTM on failure. Issue #1292 * Protect against malformed files * Refactor code to not use strndup * Other small code refactoring utils: * pdftoppm: Avoid round-off errors when determining raster dimensions * pdftocairo: Avoid round-off errors when determining raster dimensions * pdftotext: Simplify memory handling qt: * Take into account flagNoView when getting/setting the visible status. KDE bug #456313 build system: * Fix sed invocation Release 22.09.0: core: * Splash: Do not truncate line dash patterns with more than 20 entries. Issue #1281 * Various signature related improvements * Fix FormField::getFullyQualifiedName in some scenarios * Splash: Small optimization on dash pattern handling * JBIG2Stream::readHalftoneRegionSeg: Fix potential memory leak * Fix crashes on malformed files. Including CVE-2022-38784 * Fix string formatting in error reporting glib: * Fix two potential memory leaks in poppler_document_create_dests_tree utils: * pdfsig: List signature field names when listing signature information * pdfsig: Add support for specifying signature by field name * pdfunite: Fix crashes on malformed files * pdfunite: Fix potential memory leak of docs Release 22.08.0: core: * Fix rendering text on some forms * CairoOutputDev: Support Type3 charprocs having Resources * Fix crashes on malformed files Release 22.07.0: core: * Fix crash when filling in forms in some files. Issue #1258 * Fix first lines of Annotations sometimes being cut off. Issue #1246 * Signatures: Don't crash if the signature doesn't have a common name * CairoFontEngine: increment font_face reference when retrieving from the cache * Add ToUnicode support for lessorequalslant and greaterorequalslant glib: * Add support for stamp annotation build system: * Tweaks on how gperf is run Release 22.06.0: core: * Forms: Fix crash in forms with their own DR * Refactor CairoFontEngine caching * CairoOutputDev: preserve text color when drawing type 3 glyphs * Windows: font code simplification * Minor code improvements cpp: * Add missing header utils: * pdfattach: Assume filename is utf8 encoded * pdftohtml: Fix type 3 font size calculation Release 22.05.0: core: * Annotations: Make sure we embed fonts for the FreeText annots * Forms: Make sure we embedd fonts as needed * Signatures: Make sure we embed the needed fonts * CairoOutputDev: color type 3 fonts * fix two bugs in multiline find_text() * code improvements utils: * pdftotext: added TSV mode * HtmlOutputDev: don't use png.h cpp: * Use time_t for time * Add page_transition::durationReal qt: * Pass leftFontSize down to `FormWidgetSignature::signDocumentWithAppearence` Release 22.04.0: core: * Fix underline sometimes being drawn only partially * Fix Adobe Reader not reading some of the contents we write correctly * Fix code that workarounds some broken-ish files * FoFiTrueType: Parse CFF2 fonts too * FoFiTrueType: Support cmap types 2 and 13 * Fix a few small memory leaks * code improvements qt: * Handle SaveAs named action * Annotations: don't change the text color when changing the font utils: * pdftotext: print creation and modification date when using htmlmeta param glib: * Fix returning internal data of temporary strings cpp: * Fix code incompatibility with MSVC build system: * poppler internal library is no longer forced to static on MSVC * Error out if iconv is not available and the cpp frontend is enabled * Require FreeType 2.8 Release 22.03.0: core: * Signature: Fix finding Signatures that are in Pages not not in the global the Forms object * Signature: Improve getting the path to the firefox certificate database * Splash: Fix rendering of some joints. Issue #1212 * Fix get_poppler_localdir for relocatable Windows builds * Minor code improvements qt: * Minor code improvements utils: * pdfimages: Fix the wrong Stream being passed for drawMaskedImage build system: * Small code improvements Release 22.02.0: core: * Signature: Add a way to detect unsigned FormFieldSignature * Signature: Suport background image when using left and right text * Signature: Fix path where to search for Firefox NSS in Windows * Signature: Fix NSS code to work correctly in Windows/Android * Count only signature fields in PDFDoc::getNumSignatureFields * Minor code improvements qt: * Allow signing unsigned signature fields * Allow passing a background image for the signature when signing * Allow passing the document password when signing * Fix leftFontSize being ignored when signing glib: * try with utf8 password if latin1 fails * New method for getting all signature fields of a document * Fix compile with MSVC utils: * pdfsig: Fix compile with MSVC build system: * Fix NSS cmake check for MSVC Release 22.01.0: core: * Allow local (relative to dll) fonts dir on Windows * TextOutputDev: require more spacing between columns. Issue #1093 * Fix crash in Splash::gouraudTriangleShadedFill. Issue #1183 * Fix crash when calling Form::reset() * GfxSeparationColorSpace: Check validity of colorspace and function. Issue #1184 * Minor code improvements glib: * Include glib.h before using defines from it * Close file descriptors on error * Plug some memory leaks * Replace use of deprecated g_memdup/g_time_zone_new * Remove FD-taking functions on windows utils: * pdfsig: Add support for documents with passwords * pdfsig: Fix signing with -sign if nss password is needed Release 21.12.0: core: * Add API to add images * CairoOutputDev: Fix de-duping of Flate images * Fix crash on broken files when using non-default ENABLE_ZLIB_UNCOMPRESS. Issue #393 * Minor code improvements glib: * Add API for validation of signatures * Add API to read/save to file descriptor utils: * pdftohtml: Reduce sensitivity of duplicate detection. Issue #1117 build system: * Increase C++ standard to 17 Release 21.11.0: core: * Fix rendering of some non-standard confirming annotations * Support rendering of some non-standard Type3 charprocs. Issue #1150 * TextOutputDev: Respect orientation when selecting words. Issue #499 * CairoOutputDev: Don't override the antialias settings from the cairo_t * StructElement: support MCID in XObjects * Fix detection of monospace fonts * Ignore Adobe-Identity for non embedded CID fonts * PageLabelInfo::labelToIndex: work on some special no style intervals * Fix crash in malformed files * Minor code improvements utils: * pdfinfo: add -url option to print all URLs in a PDF * pdftohtml: document what zoom means in regard to DPI qt6: * Require Qt 6.1 * Minor code improvements Release 21.10.0: core: * Add support for setting custom stamp annotations * Add default appearance for the well known stamp names * Correct encoding of signature's properties Reason & Location * Splash: Fix rendering of some odd patterns * SignatureHandler::validateCertificate: Add option to not do OCSP revocation check * SignatureHandler::validateCertificate: Add support for AIA fetching to verify certificates * greallocn: if memory allocation fails, free the previous pointer to avoid memory leak * Fix issues with malformed files * Internal code improvements utils: * pdfsig: Add a way to list certificate nicknames * pdfsig: You can now add signatures from pdfsig * pdfsig: Add option to not do OCSP revocation check * pdfsig: Add option for AIA fetching to verify certificates * pdfinfo: Add -custom option to print custom metadata * pdfinfo: add metadata flags qt: * Add support for setting custom stamp annotations * Add getters for signature's properties Reason & Location * Internal code improvements glib: * Remove incorrect PopplerAttachment deprecation Release 21.09.0: core: * Splash: Massive spped improvement on files that use lots of save/restore (q/Q) operators * Correct decoding of signature properties Reason & Location when they are Unicode * Fix issues with malformed files * MSVC build fixes build system: * Call cmake_minium_required() before project() * Always append to CMAKE_{C,CXX}_FLAGS_${CMAKE_BUILD_TYPE} * correctly forward user-provided flags to try_compile() Release 21.08.0: core: * Add API to allow addition and modification of outlines into a PDF * Use additional samples to test for constant parts of an axial gradient * forms: Create fallback fonts for some well known font names * Support reading the PDF Version from the Catalog * Fix XRef::copy when there are modified objects * Take into account that Date string may be in unicode * JBIG2Stream: Fix regression in "Do not consider a size-0 to be an error" * Replace a local bubble sort implementation by std::sort * Fix issues with malformed files build system: * Better error message when libjpeg is not found * Better error messages when libopenjpeg2 is not found qt5/qt6: * Document that a document has to outlive its pages * Make getPdfVersion return a dedicated version object glib: * mimick TextSelectionDumper logic change for spaceAfter Release 21.07.0: core: * JBIG2Stream: Do not consider a size-0 to be an error. Issue #535 * PSOutputDev: fix off-by-one error for image masking in L1/L2 output. Issue #1088 * CairoOutputDev: Fix memory leak on broken files * Minor code improvements build system: * set C standard to 11 without extensions Release 21.06.1: glib: * fix poppler_rectangle_free() regression. Issue #1087 Release 21.06.0: core: * Fix rendering of some extended latin1 characters in annotations. Issue #1070 * Support some not so well formed documents with password. Issue #1083 * Add API to get notified if the xref is reconstructed * Add somewhat fancier left/right signature visual representation * Fix crashes in malformed files * Minor code improvements qt6: * Change some functions to return std::unique_ptr qt5/qt6: * Add API to get notified if the xref is reconstructed * Add somewhat fancier left/right signature visual representation * Don't assert when trying to invert singular matrices build system: * make boost opt-out if building splash Release 21.05.0: core: * Fix crashes in malformed files * Export SplashFont* symbols used by Scribus * Minor code improvements glib: * Enhance find to support multi-line matching qt5/qt6: * Make sure new signatures are always properly oriented * Allow to pass the border width when signing utils: * pdftoppm: Fix regression when using single scaleTo. Issue #1062 build system: * Allow to disable building manual tests Release 21.04.0: core: * Hide symbols by default * TextSelectionDumper: fix word order for RTL text * Fix rendering of text in some files. Issue #1052 * Implement rendering of Masks of Image subtype. Issue #1058 * Forms: fix unclicking standalone form buttons. Issue #1034 glib: * Expose more fields from MediaRendition in PopplerMedia * Use stock glib macro to define boxed type * Remove incorrecly used volatile from enum type registration code qt5: * Fix crash in files with malformed signatures * Fix memory leak when QImage constructor "fails" qt6: * Fix crash in files with malformed signatures * Fix memory leak when QImage constructor "fails" utils: * pdfsig: New paragraph for "-sign" in manpage * pdfimages: Do not assert in "too big images". Issue #1061 build system: * Require cmake >= 3.10 * Require Qt 5 >= 5.9 * Require glib >= 2.56 * Require gtk 3 >= 3.22 * Require gdk-pixbuf >= 2.36 Release 21.03.0: core: * Fix parsing text in some broken pdf files. Issue #1040 * Fix memory issue when using threads. Issue #1050 * TextSelectionDumper: Fix getText() for space after word * Change signature of OutputDev:tilingPatternFill * Make PDFDocBuilder return a std::unique_ptr * Improve well formed check for shading functions * Fix leak in case of fread failing * Fix memory leak in broken file in JBIG2Stream::readGenericBitmap * PSOutputDev: Fix stack overflow in broken files glib: * poppler_annot_free_text_get_callout_line: Fix wrong static cast * poppler-structure-element: fix memleak * Improve documentation * demo: keep same visual appearance between displayed and copied text utils: * pdftotext: Add -cropbox option * pdftoppm: Add -progress option * pdftoppm: Fix rounding bug in computation of output bitmap size. Issue #927 qt6: * Add missing poppler-qt6.pc.cmake Release 21.02.0: core: * GfxCal*ColorSpace: introduce Bradford transform for chromatic adaptation * Fix memory leak if saving the file fails * Internal code improvements * Fix various issues handling broken files * Make checkedAdd work for long long in MSVC qt5: * Properly export NewSignatureData class * Fix regression in QIODeviceOutStream + MSVC qt6: * Properly export NewSignatureData class * Fix regression in QIODeviceOutStream + MSVC utils: * pdftohtml: Fix error() parameter type Release 21.01.0: core: * Faster routines for jpeg decoding * Fix reading signatures in encrypted files * Add white point correction when lcms is used * JBIG2Stream: Fix byte counting * Fix potential data loss if we try to fetch a non existing Ref after modifying the document * Specifically use DeviceGray instead of DefaultGray for softmasks * Fix various issues handling broken files utils: * pdftocairo: Setmode binary for windows * pdfsig: Add hability to digitally sign files * pdftoppm: add options to set DeviceGray/DeviceRGB/DeviceCMYK * pdftops: add options to set DeviceGray/DeviceRGB/DeviceCMYK * pdfimages: Account for rotation in PPI calculation qt5: * Add hability to digitally sign files qt6: * Add hability to digitally sign files build system: * Enable clang-tidy bugprone-signed-char-misuse Release 20.12.1: core: * PSOutputDev: fixing regression in the rasterization code. Issue #1002 * Add missing profile copy operation in GfxICCBasedColorSpace::copy() * Fix issue in broken files build system: * Use modern CMake linking for Qt and boost Release 20.12.0: core: * Draw better circles for circle annotations * Fix annotation line width if no appearance stream or style are given * Tweak rendering of highlight annotations * Fix border rendering of some annotations * Fix rendering of some files. Issue #976 Issue #567 * PSOutputDev: provide options to set the rasterization color space and ICC profile * PSOutputDev: for splashModeCMYK8 and language level >=2 activate overprint emulation * PSOutputDev: use the DeviceN8 bitmap for rasterization with CMYK-output + overprint * Use the font name without subset tag when querying for a system font * Splash: Fix wrong x adjustment during clipping * Splash: Fix blitImage in uncolored tiling patterns * timeToDateString: We forgot the ' after the minutes * Move psLevel to PSOutputDev creation * Fix several issues in broken files utils: * pdftops: provide options to set the rasterization color space and ICC profile * pdftops: for splashModeCMYK8 and language level >=2 activate overprint emulation cpp: * New fuzzers glib: * New fuzzers qt5: * New fuzzers build system: * gcc: Enable -fno-operator-names * Remove obsolete bool-to-binary macro * Remove obsolete version-check macro for pkgconfig * Remove .pc files for private back-ends * Remove redundant unit-test macro Release 20.11.0: core: * More work on rendering of standalone Annot Widgets. Issue #806 * Fix crashes in embedded file handling on broken files. Issue #967 * Fix uninitialized memory read on broken files * Save a bit of memory in Dict data cpp: * Fix crashes in embedded file handling on broken files. Issue #966 utils: * pdftohtml: HTML and XML output includes font opacity. qt5: * Rename ArthurOutputDev to QPainterOutputDev build system: * Fix linker error when gtk is not in a default location * Add some checks for gtk-doc support * Reorganize GObject introspection config * Enable CMAKE_LINK_DEPENDS_NO_SHARED Release 20.10.0: core: * Filter out repeated forms * Implement EmbedStream::reset() * CairoOutputDev: evict just font faces owned solely by cache. * Splash: Rename Yd to Ydown, Xu to Xup, etc. * Splash: fix crash in out-of-memory situation. * Fix some undefined behaviour situation with forged files Release 20.09.0: core: * Compability fix for Forms * Fix fetching of Objects failing in some cases * Fix clearing date in Annot setModified/setDate * TextSelectionPainter: support glyphless fonts * Splash: Don't try read past end of image * avoid abort() on large memory allocation * Fix memory leak on broken files * Fix potential invalid memory read * Small code improvements qt5: * Document TextAnnotation::inplaceAlign * Make Annotation::setModification/CreationDate work on existing annots * Be a bit more stubborn converting dates that come from xml * Clean as many null characters from the end as possible when converting strings glib: * Add accessor functions for PopplerAttachment * Deprecate PopplerAttachment GTime fields * Deprecate PopplerDocument date properties utils: * pdftoppm: report error and exit if output file cannot be written * Document that PDF-file can be '-' to read it from stdin build system: * cmake: Modern way to link against libpng, zlib and libtiff * cmake: Remove stray support for lcms1 in pdftocairo Release 20.08.0: core: * Sub-page objects: initialize clip max values considering the render resolution. Issue #937 * Splash: Set initial line width to 1. Issue #674 * Fix stack overflow with specially crafted files * GfxShading: Simplify holding the Function * Splash: Fix x86 + windows asm build qt5: * Deprecate Document::toc * Deprecate AnnotationUtils Release 0.90.1: core: * Fix regression on PS conversion. Regression only happened on applications that are locale enabled i.e. Okular but not pdftops, when using a quite new lcms and the user locale uses , as decimal separator instead of . * Add UTF16LE support to TextStringToUCS4. Even if the standard clearly says it should be UTF16BE qt6: * Add work in progress qt6 port. Ignore for now :) Release 0.90.0: core: * Color profile tweaks * Small signature improvements * BBoxOutputDev: Fix calculation when type3 fonts are involved * Fix potential crash when reading Forms * Fix infinite loop in broken file glib: * Fix adding annots in rotated pages * Add ability to reset forms * Several fixes to the documentation qt5: * Make it clear we require Qt 5.5 * demo: Fix crash on broken files * Small documentation improvements utils: * pdftoppm: Add option to set display profile * pdftops: Add a -rasterize option with values always, never, or whenneeded build system: * Require cmake 3.5 * More modern cmake way to link against curl Release 0.89.0: core: * Add support for ResetForm action. Issue #225 * Fix crash in PDFDoc::getSignatureFields when there's no Forms at all * Fix exporting to PS of some files with CID fonts * Use ICC profiles in PS output (if new enough lcms is used) * Allow almost-singular tiling pattern matrices. Issue #894 * Fix memory leak when failing to load some fonts * CairoOutputDev: Use stroke opacity when clipping to a stroke path * CairoOutputDev: Fix tiling patterns when pattern cell is too far. Issue #190 glib: * Add poppler_movie_get_aspect cpp: * Add the font infos to the text_box object Release 0.88.0: core: * Support Widget Annotation Buttons not linked to any Form * SplashOuputDev: Use stroking opacity when clipping to a stroke path * Handle 1 bit RGB images in ICC colorspace * Internal code improvements qt5: * Add Document::signatures. Returns signatures not attached to any page * ArthurOutputDev: Fix font hinting * ArthurOutputDev: Set the opacity when filling with axial gradients * ArthurOutputDev: Implement the clipToStrokePath method * ArthurOutputDev: Use stroking opacity when clipping to a stroke path glib: * Add poppler_page_get_bounding_box * Add poppler_form_field_get_alternate_ui_name * Implement rotation for 'flagNoRotate' annots. Issue #767 cpp: * Add non_raw_non_physical layout for page::text() utils: * pdftohtml: Fix noRoundedCoordinates->noroundcoord in man page * pdfsig: Show also signatures that aren't attached to any page Release 0.87.0: core: * Fix leak in broken files * Internal code improvements qt5: * Add option to get form choice for export value * ArthurOutputDev: Avoid division by zero in updateLineDash. Issue #695 glib: * Internal code improvements utils: * pdftohtml: Fix memory leak in HtmlOutputDev::getLinkDest Release 0.86.1: core: * Fix regression in Browse Link handling * Internal code improvements Release 0.86.0: core: * Fix link content exfiltration attack * Splash: Implement gouraudTriangleShadedFill for some non parametrized shadings. Issue #881 * Fix case unsensitive search for Old Hungarian, Warang Citi, Medefaidrin and Adlam * Internal code improvements glib: * Automatic handle of page's cropbox on annots. Issue #129 * Fix memory leak if poppler_document_new_from_file fails * Minor speed optimization on poppler_page_get_annot_mapping utils: * pdfdetach: add 'savefile' option * pdftoppm/pdftocairo: Fix more odd/even mismatch qt5: * Fix loading from iodevice Release 0.85.0: core: * Fix case unsensitive search for Deseret and Osage. Issue #853 * Fix crash in unicodeToAscii7 * CairoOutputDev: make initialisation thread-safe * Fix crash on broken files. Issues #869, #870 * Internal code improvements utils: * pdftoppm/pdftocairo: Fix -e/-o printing the wrong pages. Issue #873 * pdftohtml: Fix issue with the font size sometimes being huge qt5: * Fix FormField::name encoding * Accept UTF-16 uiNames for form fields * Fix search for "complex" characters * Allow to load document from QIODevice glib: * make the frontend initialization thread safe. Release 0.84.0: core: * Fix crash when converting from Unicode to ASCII-7 * Splash::scaleImageYdXu: Protect against crash if srcWidth is too big * JBIG2Stream: fix potential crash in malformed documents * JBIG2Stream: fix leak in reset() if called several times * Internal code improvements utils: * pdfimages: Add error message if first page is larger then number of pages. * pdfinfo: Improved paper size recognition * pdfsig: Fix exit code when dumping signatures * pdftocairo: Error out when even/odd selects 0 pages * pdftohtml: Fix memory leak * pdftoppm: Add an option to scale before rotate * pdftoppm: Add -hide-annotations option * pdftoppm: Error out when even/odd selects 0 pages * pdftops: Improve -optimizecolorspace qt5: * Code cleanups glib: * Fix compiler warrnings Release 0.83.0: core: * Improve when a file is recognized as Linearized * Improve const-ness of the code * Make code a bit more readable/maintanable * Fix uninitialized memory uses in broken files utils: * pdffonts: Make code a bit more readable/maintanable * pdftohtml: Make code a bit more readable/maintanable qt5: * Remove a bunch of unused internal functions * trUtf8 -> tr (less warnings) build system: * make-glib-api-docs: switch to python3 Release 0.82.0: core: * Fix not being able to open some files. Issue #832 * Fix crashes in malformed files * Fix memory leak on broken files * Minor performance improvements * Minor code improvements glib: * Add poppler_document_new_from_bytes * PopplerAttachment: Silence deprecation warnings for ctime/mtime build system: * pdf-inspector: Support builddir != srcdir * Install Cairo* headers if Cairo has been found Release 0.81.0: core: * Splash: Always enable support for CMYK rasterization * CairoOutputDev: Check scaled dimensions for 0. Issue #737 * BaseCryptStream: Fix potential uninitialized memory read * SplashBitmap: Fix wrong width condition for splashModeDeviceN8 * Fix crashes in malformed files Release 0.80.0: core: * Annotations: Implement support for setting a different text in the appearance stream than the real text * Splash: Optionally use small_vector from boost * Fix memory leaks on broken files * Fix abort on broken files * Small code simplifications * Remove USE_FIXEDPOINT support. Issue #821 qt5: * Fix MSVC build * Add subsitute-font information * Fix since marker of some functions * Fix leak when aborting text extraction * Small code simplifications glib: * Make print scaling getter visible * Make Duplex/NumCopies/PrintPageRange preference available in API * Complement Movie API utils: * pdftotext: Add -nodiag flag to remove diagonal text on output build system: * Mark external lib include dirs as SYSTEM Release 0.79.0: core: * Fix regression on TextSelectionPainter * Fix parsing of DefaultAppearance * Fix memory leak in PostScriptFunction * Fix crashes in fuzzed files qt5: * Implemented support for setIcon by changing appearance * Added option to set the form available to print * QString::null is deprecated, use QString() * Replace deprecated qStableSort with std::stable_sort build system: * Turn README into README.md and expand it Release 0.78.0: core: * Fix line annotation arrows for usage in dimensioning * Handle Ink annots without an InkList but with an AP * Fix typos preventing parsing of Movie start and duration * Fix crash on malformed files glib: * Add poppler_document_create_dests_tree() * Don't use the deprecated g_type_class_add_private() * Document the differences between render() and render_for_printing() * Fix introspection for poppler_document_new_from_data * Don't create PopplerInputStream with length 0. Issue #414 * Document G_IO_ERROR as a possible error condition * docs: Add index for API new in 0.78 build system: * Fixes cross compilation of gir in Void Linux * Add -Wshadow to the default warning flags * install pkg-config pc files if pkg-config is found Release 0.77.0: core: * Fix crash on signature handling. Issue #766 * Fix small memory leak in SignatureHandler::getCertificateInfo * Splash: Restrict filling of overlapping boxes. Issue #750 * Fix crash on malformed files qt5: * Fix optional content handling with exclusive layers cpp: * Make render_page thread-safe utils: * pdfsig: Fix small memory leak * pdftotext: Fix typo in manpage Release 0.76.1: core: * Make the mul tables be calculated at compile time with constexpr. * splash: Fix compile with SPLASH_CMYK enabled * Some typo fixing in error messages qt5: * Fix regression in annotation handling build system: * Fix some typos in build system output and comments Release 0.76.0: core: * Fix regression on case-insensitive search. Issue #743 * Remove GooList, use std::vector instead * Fix radiobutton reporting wrong state. Issue #159 * Handle UTF16-LE strings * Don't error out if there's no DA in FreeText annotation * cairo: Compute correct coverage values for box filter. * cairo: Constrain number of cycles in rescale filter. * Read more fields from ViewerPreferences * Introduce and use Ref::INVALID * Fix crashes in broken files * Fix mismatched free/delete * Add missing include guards utils: * pdftohtml: Properly initialize HtmlOutputDev::page to avoid SIGSEGV upon error exit. Issue #742 Release 0.75.0: core: * Fix rendering of some annotations * Fix crashes in broken files * Small internal code improvements cpp: * Improve documentation * tests: Add showing version information to poppler-dump utils: * pdfattach: new util * pdftohtml: add -dataurls parameter * pdftoppm: add -sep and -forcenum parameters * pdftohtml: make singleHtml and stout not mutually exclusive * pdfsig: fix use after free Release 0.74.0: core: * Remove support for obsolete systems. Issue #709 * Include timezone in timeToDateString() * Fix/silence some warnings * Fix issues with broken files build system: * Fix linking in FreeBSD * Fix fseeko configure check on Android for API level < 24 * Remove unused MacroPushRequiredVars.cmake qt5: * Add API that lazily builds an outline by wrapping the internal objects * Demo: Use new API to build Table Of Contents lazily glib: * Improve documentation * Fix cast from 'GTime *' (aka 'int *') to 'time_t *' (aka 'long *') utils: * pdfsig: add -nssdir option cpp: * Add a way to get all the named destinations in a document. Release 0.73.0: core: * Fix regression reading some encrypted files. Issue #690 * Add X509CertificateInfo classes * Add new 'IgnoreDiacritics' option to ::findText(). Issue #637 * Open files with CLOEXEC flag set * Remove Gulong, Guint, Gushort, Guchar typedefs * Fix handling of some broken files. cpp: * Make initialization of globalParams threadsafe * Fix page::text_list encoding issue * Improve handling of UTF-16 by considering Endianess * Add API to specify a custom data directory qt5: * Expose X509CertificateInfo * Add the possibility of getting version * Add new 'IgnoreDiacritics' search flag. Issue #637 * Make initialization of globalParams threadsafe * ArthurOutputDev: Remove all Splash code usage glib: * add new 'POPPLER_FIND_IGNORE_DIACRITICS' find flag. Issue #637 * Fix named destinations. Issue #631 * Make PrintScaling preference available in API. Bug #92779 build system: * Rename ENABLE_XPDF_HEADERS to ENABLE_UNSTABLE_API_ABI_HEADERS * support enabling NSS on mingw * Windows: only set SOVERSION for shared libs Release 0.72.0: core: * Fix checkbox lacking AP not bein able to change state. Issue #655 * Draw line annotation endings (arrow, circle, ...) * cairo: Don't use UNIQUE_ID for PS output, to avoid using PS memory on cairo >= 1.5.10 * Be more stubborn looking for a nssdb. Issue #669 * GooString::fromInt: Repair the return value. * Minor performance improvements * Avoid cycles in PDF parsing * Stream::makeFilter: Fix memory leak * Fix various issues with malformed files * Rename GooString::getCString to GooString::c_str * Regenerate UnicodeDecompTables.h from python 3.7.1 utils: * pdfdetach: Check for valid embedded file before trying to save it. Issue #661 * pdfdetach: Check for valid file name of embedded file before using it to determine save path. Issue #660 * Fix typos in utils. glib: * Fix missing PopplerAttachment destructor call * Support getting form widget additional actions. * docs: Small improvements qt5: * Internally compile with -DQT_NO_SIGNALS_SLOTS_KEYWORDS Release 0.71.0: core: * Replace the implementation of GooString by std::string but keep the exact interface intact. * Replace GBool, gTrue, and gFalse by bool, true, false, resp. * Splash: Fix crash if document is malformed (too wide) qt5: * Fix crash when adding Highlight Annotations * Default to hidden symbols * Fix two leaks in a test glib: * demo: Fix build on Windows * demo: Align property labels to top of cell cpp: * Fix typos in documentation build system: * Enable searching for GTK on Windows * Remove unused files * Add fuzzer target from oss-fuzz project Release 0.70.1 glib: * Install missing file Release 0.70.0 core: * FreeText annotations: default to font from default appearance string * Splash: Speed improvements * Fix security issues found by oss-fuzz * Improve page lable parsing * Use std some std classes instead of self grown ones * Various internal improvements qt5: * Add Page::index() method * Improve method to get the page from a label string glib: * Fix crash on missing embedded file * Add support for PDF subtype property * Only export symbols in the public API utils: * pdftohtml: Improve font handling Release 0.69.0 core: * Add annotation font color * Splash: Some speed improvements * PSOutputDev: add native support for type 7 shadings when using level 3 * Add support for PDF subtype property * Link: Fix memory leak regarding next actions * Fix handling of Signature Info Location and Reason * Fix errors in computation of type3 glyphs transformation matrix * Reimplement Dict class in a more modern way * Fix security issues found by oss-fuzz * Fix memory issues in GfxImageColorMap copy ctor * Don't abort if the SampleFunction has too many samples. Issue #634 * Document the OutputDev::clip and OutputDev::oeClip methods * fix macOS compilation due to boolean define in jpeglib * Split GDir and GDirEntry out of gfile.h. Issue #370 qt5: * Add annotation font color cpp: utils: * pdfinfo: Show PDF subtype * pdftotext: Fix only outputs first page content with -bbox-layout option. Issue #88 * pdftotext: Fix memory leak in printLine build system: * Require C++14 Release 0.68.0 core: * Add Reason and Location to SignatureInfo. Bug #107299 * Fix memory misuse on signature handling * Fix security issues found by oss-fuzz * Don't give a warning when Marked value is false. Bug #107430 qt5: * Add Reason and Location to SignatureInfo. Bug #107299 cpp: * Add rotation() to text_box. Bug #106562 * Fix build with MSVC utils: * pdftoppm: Add -jpegopt optimize option support * pdftocairo: Add -jpegopt optimize option support * pdftohtml: Add option to not round coordinates * pdftohtml: Fix possible crash. Bug #107316 build system: * Use OpenJpeg cmake config file instead of pkgconfig * Remove wchar_t- on MSVC Release 0.67.0 core: * Fix lots of security/leak issues found by oss-fuzz * Splash: Optimize some files, making them 20% faster (now for AABGR8) utils: * pdfsig: Compile with libc != glibc. Bug #106783 Release 0.66.0 core: * Fix lots of security/leak issues found by oss-fuzz * Splash: Optimize some files, making them 20% faster * Splash: Correctly manipulate spot colors if SPOT_NCOMPS != 4 * Fix compilation with some strict compilers Release 0.65.0 core: * SplashOutputDev: Add the invisible character check beginType3Char. Bug #106244 * XRef: Fix runtime undefined behaviour. Bug #105970 * Fix issues with malformed documents. Bug #104942, #103238 * Remove GooHash after replacing it by std::unordered_map * Add conversion methods between GooString and std::string. cpp: * Add newline after error message * Expose more image modes, add option to select mode in renderer. Bug #105558 build system: * Fix compilation with libc++ * Small improvement to FindLIBOPENJPEG2.cmake qt5: * Add widget annot actions to FormFields utils: * pdffonts: Minor formatting changes in the man page. Bug #105194 Release 0.64.0 core: * Workaround form field text not being drawn on broken files. Bug #103245 * Add read only setter for form fields * Add support for Link Hide action * Add support for Next actions in Links * Fix parsing of Annot focus out actions * Fix PDFDoc::checkHeader() for PDFs smaller than 1 KiB. Bug #105674 * Add const to several classes and members * gfile: Fix build on some platforms * Fix issues with on malformed documents. Bug #105972, #105969, #106059, #106061 * Several small code improvements qt5: * Allow setting of Form visibility status * Allow setting of Form read only status * Add support for Link Hide action * Add support for Next actions in Links * ArthurOutputDev: Implement axialShadedFill * ArthurOutputDev: Implement drawImageMask. Bug #105531 * ArthurOutputDev: Implement Type3 font support utils: * pdfsig: Add -dump which writes signatures to disk. Bug #104881 glib: * less deprecated calls build system: * bring back the option to disable GObject introspection * Add iconv include dir when compiling * Make it possible to build poppler without fontconfig. Default for Android Release 0.63.0 core: * CairoOutputDev: support embedding CCITT image data. Bug #103399 * CairoOutputDev: limit image size when printing. Bug #103399 * CairoOutputDev: use GOOD instead of BEST as the default cairo filter for scaling. Bug #103136 * Error out on save if file has changed since we opened it. Bug #103793 * PDFDoc: use %c instead of \x to output binary. Bug #103873 * Fix index out of bounds undefined behaviour in PSTokenizer. Bug #103583 * Fix opening files with OutlineItem loops. Bug #102914 * Fix some bugs in StructTreeRoot parsing of parent tree. Bug #103912 * Remove error for wrong child type for tagged pdf. Bug #103587 * FoFiTrueType::readPostTable() from xpdf 4.00. Bug #102880 * GfxFontDict: merge reference generation from xpdf 4.00. Bug #104565 * Reset lastAbortCheck on updateLevel reset * PDFDoc::setup: Fail early if base stream length is 0. Bug #103552 * Check curStr is actually a Stream before doing Stream operations. Bug #104518 * Fix new Object API porting bug. Bug #104517 * Check return code of getChar(), abort reading on error. Bug #104502 * TextPage: Add horizontal scaling to font matrix. Bug #105259 * Fix EmbedStream replay. Bug #103446 * Fix memory leak on error condition * Fix assert on malformed documents. Bug #104354 * Fix abort in Gfx::opBeginMarkedContent if args[1] is not a name. Bug #104468 * GfxGouraudTriangleShading::parse: Don't abort on malformed documents. Bug #104567 * GfxFunctionShading::parse: Fix abort in malformed document. Bug #104581 * Remove the extern C from glib.h. Bug #103621 * Don't let ArthurOutputDev be friend of SplashPath anymore * Fix undefined sanitizer warning about qsort * Form.h: include time.h for time_t * Various code improvements qt5: * Add cancellation support to renderToImage and textList * Do not assume all Screen annotation actions are Renditions. KDE bug #388175 * qt5: Implement operator= for PageTransition * ArthurOutputDev: 'clip' should intersect new and old clipping path * ArthurOutputDev: Implement updateBlendMode * ArthurOutputDev: Replace the QPainter by a stack of QPainters * ArthurOutputDev: Rudimentary support for transparency groups * Remove stale libcms1 code. Bug #104358 * demo: don't crash if page is malformed * Fix warnings due to the use of deprecated overloads of Poppler::Page::Search in tests. utils: * pdfimages: Fix for files with flate encoded inline images. Bug #103446 * pdftocairo: Remove stale libcms1 code. Bug #104358 * pdfimages: Fix build without libtiff and libpng * pdfseparate: Fix buffer size warning due to missing space for null terminator build system: * Enable building all libs as static libs * Enable no-missing-field-initializers * Remove unused FindLIBOPENJPEG.cmake * add "--owner root:0 --group root:0" options to tar command in dist target. Bug #104398 * Add python3 support to gtkdoc.py * gtkdoc.py: Make it work with newer gtk-doc. Bug #105075 cpp: * Add page::text_list Release 0.62.0 core: * Stop supporting lcms1, you really want to use lcms2 :) * Stop supporting openjpeg1, you really want to use openjpeg2 :) * Open files that state 8 bits as third field of W. Bug #103469 * GfxLabColorSpace::parse: Fix crash in broken documents. Bug #103582 * Fix leak if parseDA fails * Include glibc.h where needed * Document the meaning of the 'type' integer of a shading * Fix UTF test fail * INSTALL: add debug options qt5: * Add API to let the rendering process callback to get a partial rendering. Bug #103372 qt4: * Remove the Qt4 frontend utils: * Support unicode on windows console * pdfsig: install man page * sort encoding list glib: * demo: fix warning Release 0.61.1 core: * CairoOutputDev: don't overflow y * stride when accessing image data cpp: * Fix for corrupted image files on Windows. Bug #102494 build system: * Fix incorrect paths in .pc files. Bug #103578 * add the custom buildtests target only once. Bug #103003 Release 0.61.0 core: * Fix crashes in broken files * Cleanup unused functions from GlobalParams * Tweak LZWStream::processNextCode error handling. Bug #103174 * Warning fixes * Remove t1lib code qt5: * Clean up the remaining Splash code in Arthur backend. Bug #103117 * ArthurOutputDev: Properly implement saveState/restoreState. Bug #103118 * Fix leak in ArthurOutputDev::updateFont. Bug #103508 build system: * Use GNUInstallDirs. Bug #103211 * mingw: Install pkg-config files * mingw: change library names to include the soversion. Bug #103157 * Fix installing a .cc file as header * Use -pthread flag instead of -lpthread Release 0.60.1 qt5: * ArthurOutputDev: Add missing 'return' in error paths build system: * FindLIBOPENJPEG.cmake: Add CheckCXXSourceCompiles Release 0.60.0 core: * Enable libcurl support by default * PSOutputDev: Fix wrong text generation. Bug #102760 * Added methods to get and set the font size of text fields. Bug #101692 * CairoOutputDev: Do not extend the pattern in drawImageMaskRegular * CairoOutputDev: do not use the custom downscaling for rendering images when using cairo >= 1.14 * Fix build with old clang * Fix various crashes in broken files * Fix some warnings * Add some constness to the basic classes * Remove unused functions from GlobalParams qt5: * Added methods to get and set the font size of text fields. Bug #101692 * Add whether renderToImage shows annotations * ArthurOutputDev: Replace Splash font rendering by Qt font rendering * ArthurOutputDev: Implement the drawSoftMaskedImage method * ArthurOutputDev: Fix several small bugs related to dash pattern handling * Fix two minor typos build system: * cmake is now the default build system * autotools based build system has been removed utils: * pdfinfo: don't truncate dest name Release 0.59.0 core: * Fix infinite recursion in NameTree parsing in broken files utils: * pdfunite: Fix API porting error that caused abort in some cases * pdfinfo: Fix crashes and memory leaks when using -dests * pdfinfo: use GooString.append instead of sprintf/strcat * pdfimages: Fix warning when compiling with cygwin build system: * Fix cygwin 32-bit compile * cmake tweaks Release 0.58.0 core: * CairoOutputDev: cairo 1.14 now has high quality downscaling * Signature related improvements. Bug #99271 * Tweak which cmap we use. Bug #101855 * Memory leak fixes * Substantial rework of the internals * win32: call ANSI functions directly. Bug #100312 * Add some documentation qt5: * Expose signature information. * ArthurOutputDev: initialize the image with the paper color. Bug #102129 * Fix copy'n'paste bugs: Qt4 -> Qt5 * ArthurOutputDev: Properly set the QPainter transformation * ArthurOutputDev: Use Qt::SvgMiterJoin instead of Qt::MiterJoin. Bug #102356 utils: * pdfinfo: add -dests option to print named destinations. Bug #97262 * pdftocairo: add -jpegopt for setting jpeg compression parameters. Bug #45727 * pdftoppm: add -jpegopt for setting jpeg compression parameters. Bug #45727 * pdfimages: support listing/extracting inline images. Bug #25625 build system: * cmake: Various Windows fixes * cmake: Use -std=c++11 instead of -std=gnu++11 cpp: * Fix page.text() not taking page orientation into account. Bug #94517 Release 0.57.0 core: * Fix parsing of Type 1 fonts with newlines in encoding sequences. Bug #101728 * Fix crash in broken documents utils: * pdfunite: Fix crash with broken documents. Bug #101208 * pdftohtml: skip control characters Bug #101770 * pdfseparate: minor improvement to the documentation. Bug #101800 build system: * cmake: Set RUNPATH for poppler shared libs. Bug #101945 * configure: fix --disable-FEATURE actually enabling the feature Release 0.56.0 core: * FormFieldButton::setState() shouldn't check the field is readOnly * Fix crashes on multiple broken files utils: * pdfunite: Fix crash with broken documents. Bugs #101153 #101149 Release 0.55.0 core: * Fix abort in files with broken Decode arrays. KDE bug #379835 * Fix memory leak (and probably logic bug) parsing broken XRef entries. Bug #100775 * Fix memory leak when reconstructing broken files. Bug #100776 * Minor optimization * Fix regression in GfxIndexedColorSpace::mapColorToBase. Bug #100931 * Fix memory leak in error condition cpp: * Return nullptr if the page at index can't be fetched. Bug #100981 build system: * Fail by default if libjpeg is not available * Fail by default if libopenjpeg2/1 is not available Release 0.54.0 core: * Make XRef reconstruction a bit better. Bug #100509 glib: * Expose movie play mode. Bug #99625 * demo: Show play mode in movie properties view qt5: * Compile with -DQT_NO_CAST_FROM_BYTEARRAY. Bug #100311 utils: * pdfimages: don't fail listing if inline image data contains 'EI'. Bug #100737 Release 0.53.0 core: * Form support improvements * SplashOutputDev: Fix memory leak when rendering images with colormap and matte color * Minor fix in GlobalParams documentation qt5: * Expose form calculate order * Expose Form additional actions utils: * pdfimages: support 16bpc png and tiff images. Bug #99988 * pdftohtml: fix small memory leak when constructing some filenames * pdfinfo: fix leak when printing JS build sytem: * Compile in C++11 mode Release 0.52.0 core: * Fix assert on reading some OCGs. Bug #99768 * Properly initialize some RichMedia variables in corner cases. Bug #99767 qt4: * optcontent structure was leaking the headers items. Bug #99449 * Cleanup objects in tests to fix memory leaks. Bug #99449 qt5: * optcontent structure was leaking the headers items. Bug #99449 * Cleanup objects in tests to fix memory leaks. Bug #99449 utils: * pdftocairo.1: Fix typo Release 0.51.0 core: * Check for error from NSS in SignatureHandler construct. Bug #99363 * Add Form[Field|Widget]::setPartialName * Fix memory leak in PDFDoc::markAnnotations qt5: * Implement digital signature support. Bug #94378 * Add Poppler::FormField::setName * Fix segfault/assert if LinkDestination is constructed with invalid input string. Bug #99357 utils: * pdfunite: add fields to AcroForm dict. Bug #99141 Release 0.50.0 core: * PSOutputDev: Fix PS conversion for some files. Bug #63963 * Fix Outline parsing on broken documents. Bug #98732 * Fix PDFDoc::saveIncrementalUpdate()'s detection of document being modified. Bug #96561 * SplashOutputDev: Read softmask into memstrean in case of matte. Bug #97803 * Bail out if Hints nBitsNumObjects or nBitsDiffGroupLength are greater than 32. Bug #94941 * CairoOutputDev: initialize CairoOutputDev::antialias. Bug #98983 * Fix crash when loading some thumbnails. Bug #97870 utils: * pdftoppm: Fix -tiff -gray/-mono incorrect output. * pdftops: add -passlevel1customcolor. Bug #97193 build system: * Default to libopenjpeg2 instead of libopenjpeg1 qt: * Support OCG state change links glib: * Use g_slice_new0 for PopplerActionLayer. Bug #98786 Release 0.49.0 core: * Merge type3 glyph handling from xpdf 3.04. Bug #96667 * Continue rendering in case of 'Singular matrix in shading pattern fill. Bug #98623 * Fix memory leak in parametrized gouraudTriangleShadedFill * Fix crash on broken files * PDFDoc::setDocInfoStringEntry(): treat value consisting of just the unicode marker as an empty string * Fix UBSAN warning * Misc compile fixes utils: * pdfseparate: remove extra '%' in error message build system: * configure: Fix typo in disable nss help string Release 0.48.0 core: * Fix crashes and memory leaks in invalid files. * Small memory usage improvements. * TextOutputDev: Remove null characters from PDF text. Bug #97144 * TextOutputDev: Break words on all whitespace characters. Bug #97399 * Fix UTF16 decoding of document outline title. Bug #97156 * Add functions for named destination name in name-tree/dict glib: * Increase glib requirement to 2.41 Release 0.47.0 core: * Fix abort on documents where the docinfo obj is not a dict. Bug #97134 * Check for XRefEntry existing before using it. Bug #97005 * Fix memory leak on PDFDoc::setDocInfoStringEntry() with empty string * Don't presume that DocInfo is a dictionary in XRef::createDocInfoIfNoneExists() build system: * configure: Work with non gnu greps Release 0.46.0 core: * cairo: fix bug in setAntialias() * cairo: Fix tiling patterns with BBox with non-zero x,y * cairo: try finding glyphs in substitute fonts by unicode value. Bug #96994 * Added XRef modification flag * Added DocInfo setters & getters * Be less strict when parsing FitH Link destinations. Bug #96661 utils: * pdftocairo: revert the use of groups for blending into white page * pdftocairo: Use fprintf for printing errors * pdfinfo: Don't print pdf info when printing metadata, javascript, or structure. Bug #96801 glib: * Added document property setters & simplified getters * make document metatag gobject properties writeable cpp: * pass len to GooString constructor in detail::ustring_to_unicode_GooString(). Bug #96426 * Added functions to save a document * Added document property setters & getters qt4: * Added document property setters & simplified getters qt5: * Added document property setters & simplified getters build system: * configure: Don't use -fPIC on cygwin * configure: Work with non gnu greps Release 0.45.0 core: * SplashOutputDev: Fix iccTransform + splashModeXBGR8 * Fix memory leaks * Fix crash in broken files. Bug #95567. Bug #96027 * Emulate some non portable glibc functions when not available utils: * pdftohtml: Fix crash in broken files. Bug #95563 * pdfinfo: convert dates to local time zone * pdfinfo: add -isodates for printing dates in ISO-8601 format * pdfinfo: Fix memory leaks glib: * return date in UTC instead of local time. Bug #94173 cpp: * switched from detail::convert_date() to core's dateStringToTime() Release 0.44.0 core: * Fix Compile in 32bit linux. Bug #95492 * Splash: type 3 chars. restore the current position also in output device. Bug #95344 * Splash: Improve rendering of some dotted lines. Bug #84693 * Refactor GooString::Set(). Bug #94201 * Fix typo in GfxPatchMeshShading::parse * Fix memory leak in PSOutputDev::filterPSLabel * Fix memory leak in SignatureHandler::getDefaultFirefoxCertDB_Linux * Fix potential crash in SplashOutputDev::doUpdateFont * Fix potential crash in TextPage::coalesce * Remove call that does nothing utils: * pdftocairo: add -antialias option. Bug #94977 Release 0.43.0 core: * Implement sanity check for linearization usage. Bug #92482 * Add SymbolMT as an alias for the Symbol font. Bug #93168 * Fix some blank files. Bug #94756 * cairo: fix fillToStrokePathClip crash and rendering. Bug #62905 * cairo: Check if PDF knows the width of 'm' in case of substituted font. Bug #94054 * cairo: save mask state and don't extend image mask. bug #94234 * SplashOuputDev: Compile with C++11 compilers that don't define isinfinite. Bug #94761 * typo fixes utils: * pdftocairo: Calculate rotation before scaling. Bug #94655 qt4: * Fix crash on certain PDF form item activation actions. Bug #94873 qt5: * Fix crash on certain PDF form item activation actions. Bug #94873 Release 0.42.0 core: * Add the support for version 5 + revision 6 documents. Bug #85368 * Add initial support for Signature handling * Initialize gamut mapping multipliers in ::copy() functions. Bug #90697 * Implement jpx streams support with depth < 8 * Handle SMaskInData = 0 for JPX encoded images. Bug #93468 * Fix rendering of some broken PDF files. Bug #92508 * PSOutputDev: Support for LZW encoding * PSOutputDev: Add support for Flate compression in Level 3 output. * SplashOuputDev: Implement function shading. Bug #94441 * SplashOuputDev: Improve rendering of some non embedded fonts. Bug #94054 * SplashOuputDev: Fall back to Gfx implementation of tiling pattern if repetition rate is small. Bug #90596 * SplashOuputDev: Implementation of Matte entries in softmasks of softmasked images. Bug #22473 * SplashOuputDev: assure line width > 0 in case of text stroke. Bug #94038 * TextOuputDev: Cache result of inner loop in visitDepthFirst. Bug #77087 * Avoid attempting a tiling pattern fill with a singular transform matrix utils: * pdfinfo: Add option to show document structure * pdfsig: New command that gives information about signature qt4: * Fix bug in links to remote documents getting the page number wrong sometimes qt5: * Fix bug in links to remote documents getting the page number wrong sometimes Release 0.41.0 core: * CairoOutputDev: add missing font types (fontCIDType0COT and fontTrueTypeOT). Bug #93559 * SplashOutputDev: Adjust limit check and check in addition bitmap pointer. Bug #94053 utils: * pdfseparate: Refine resource detection * pdfinfo: fix man page Release 0.40.0 core: * CairoOutputDev: Use shape mask with soft mask. Bug #91931 * TextOutputDev: Handle right-to-left text in search * TextOutputDev: Fix finding Arabic Presentation Forms ligatures * Fix crash in invalid file. Bug #93476 * Regression test improvements utils: * pdftocairo: fix writing to stdout out with image output * pdftocairo: document that -singlefile appends file type. Bug #86254 * pdftocairo: ensure surface flushed before accessing image data * pdftocairo: check for invalid use of options. Bug #92195 * pdfunite: Fix typo in manual build system: * Improve cmake build system Release 0.39.0 core: * Ignore the alternateSpace and tintTransform. Bug #92381 * CairoOutputDev: Scale radial pattern. Bug #22098 * CairoOutputDev: Implement function shading using mesh gradients. Bug #88394 * Regression test improvements * Fix typos in error messages build system: * Visual Studio 2015 now supports snprintf. Bug #93116 utils: * pdftops: fix %%PageBoundingBox. Bug #87161 * pdftocairo: Fix double free when both user and owner passwords are given glib: * Add duration_real to PopplerPageTransition. Bug #92040 * Remove enum PopplerOrientation from API. Bug #93229 * documentation improvements * glib-demo improvements Release 0.38.0 core: * Splash: Multiply opacity in case of pattern colorspace. Bug #92592 * Small form improvements on non ascii character rendering * Clarify README build system: * Clarify internal DCT and JPX are only provided as deprecated fallbacks utils: * pdftocairo: fix fit to page transformation Release 0.37.0 core: * CairoOutputDev: Use mask for even-odd fill. Bug #84527 * SplashOuputDev: Protect calls to set/getAA with the proper #if guards. Bug #92006 * SplashOuputDev: Try to use an external font if the internal one is invalid * PageTransition D is a number not an int. Bug #92040 * Catalog::getNumPages(): validate page count * Catalog::cachePageTree(): recover from out of memory condition * Fix crashes in malformed documents build system: * configure: fix openjpeg detection Release 0.36.0 core: * Patch to support RichMedia annotations * Splash: Fix wrong memory access. Bug #91686 * Cairo: fix size of transparency group surface. Bug #66229 * Fix bounds check in Linearization::getPageFirst. Bug #91200 * File Saving improvements * Add premultiplied alpha channel to SplashBitmap * Fix for xref table creation. Bug #90790 * Fix JBIG2Decode infinite loop and stack overflow. Bug #91186 * Minor optimization in text extraction qt4: * Basic support for RichMedia annotations * Change default image format * Minor optimizations qt5: * Basic support for RichMedia annotations * Change default image format * Minor optimizations cpp: * Fix utf8/utf16 conversion. Bug #91644 build system: * Do not hardcode -fPIC in Makefile.am * cmake: Allow configuring SHARE_INSTALL_DIR. Bug #90293 utils: * pdfunite: Insert embedded files in result pdf. Bug #90066 * pdftotext: Add -bbox-layout option. Bug #89941 Release 0.35.0 core: * Fix assert in broken file. Bug #91344 * Adjust memory layout computation of GooString * Make SplashBitmap XBGR transfer alpha channel * Splash: Fix wrong writes on non rgb outputs. Bug #90570 * Splash: remove ifndef in Windows code * GlobalParamsWin bugfixes. Bug #91053 qt4: * Switch default image format * Add IgnorePaperColor render flag qt5: * Improve efficiency of Poppler::Page::renderToImage * Switch default image format * Add IgnorePaperColor render flag build system: * Allow configuring SPLASH_CMYK support * Add configure --enable-build-type. Bug #90796 glib: * Explicitly link against pthread * Deprecation fixes utils: * pdftocairo: Fix cast to pointer from integer of different size on win64 Release 0.34.0 core: * Splash: Fix crash in PDF with nested softmasks. Bug #91240 * Splash: Speed up of rendering icc based images. Bug #90171 * PSOutputDev: Embed Type1 fonts to PostScript files correctly. Bug #19747 * Fix pedantic memory leak glib: * update new symbols section build system: * cmake: Make sure ENABLE_LIBOPENJPEG is either 0 or 1 Release 0.33.0 core: * Fix regression in pdftops parameter passing. Bug #89827 * Combine base characters and diacritical marks. Bug #87215 * Use width from W array for WMode positioning. Bug #89621 * Fixed adding annotation of Subtype Popup to pdf page. Bug #89136 * CairoOutputDev: Fix memory leak in CairoFreeTypeFont::create * SplashOutputDev: memset on error to have reproducible outputs qt4: * Fix PDF Text String -> QString conversion. KDE Bug #344849 qt5: * Fix PDF Text String -> QString conversion. KDE Bug #344849 glib: * Add poppler_annot_markup_set_popup_rectangle() * Fix segfault when creating PopplerAction. Bug #90093 utils: * pdftohtml: Set exit status adecuately. Bug #83609 build system: * configure: Fix invalid shell comparaison in libtiff test Release 0.32.0 core: * Annotations: Fix rendering of empty BG/BC arrays * Splash: Fix wrong colour shown when GouraudTriangleShFill uses a DeviceN colorspace. Bug #89182 * Splash: Fix use of uninitialized variable in Splash::pipeRun * Remove unnecesary check for font validity. Bug #88939 * Small optimization in GooString::appendfv(). Bug #89096 * Fix crashes in malformed files utils: * pdftops: Make colorpsace optimization an option instead of default * pdfseparate: use always an unique instance for PDFDoc for savePageAs build system: * cmake: If extra-cmake-modules is around include the Sanitizers module Release 0.31.0 core: * CairoOutputDev: support embedding JBIG2 image data * Accept malformed documents whose root is a Page instead of a Pages. Bug #88172 * Fix crash on broken documents * JPEG2000Stream: Inline doGetChar and doLookChar * GlobalParams cleaning utils: * pdftops: Add rasterization option. Bug #85934 qt4: * Expose whole-words search option qt5: * Expose whole-words search option Release 0.30.0 core: * Openjpeg2 support (openjpeg 1 is preferred). Bug #58906 * Fix potential memory corruption on TextSelectionDumper. Bug #84555 * Check for invalid matrix in annotation. Bug #84990 * Open some not conforming files. Bug #85919 * PSOutputDev: Accept a list of pages indeces instead of first, last. Bug #84833 * Fix memory leak on error condition cpp: * New API to set debug output function build system: * configure: Improve support with older clang versions. Bug #76963 utils: * pdfunite: Support output intents, optional content and acroform Release 0.29.0 core: * Use correct LAB byte array for lcms input. Bug #86388 * Write correct size in trailer dict. Bug #86063 * Use Default colorspaces if present instead of Device colorspaces * Solve blend mode problem in CYMK and DeviceN for separable blend modes * Compilation/warning fixes on SunOS * Regression test improvements glib: * demo: Compilation fixlets build system: * cofigure: print "no" instead of "auto" if lcms not found Release 0.28.0 core: * Fix rendering of file with a wrong embedded font. Bug #84270 * Use alt colorspace to get CMYK values for an ICC based CMYK colorspace. Bug #79019 * Map Standard/Expert encoding ligatures to AGLFN names. Bug #80093 * Make Attribute::getName() work when UTF-16BE is used. Bug #84722 * Fix memory leak in Dict::remove. Bug #84607 * Fix crashes in broken files * SplashOutputDev: Improve Overprintmode and shadings. Bug #80998 * CairoOutputDev: fix crash when no group color space. Bug #85137 * CairoOutputDev: Don't render text when text matrix is not invertable. Bug #78042 * CairoOutputDev: Only embed mime data for gray/rgb/cmyk colorspaces. Bug #80719 * CairoOutputDev: Only embed mime data if image decode map is identity * cairo: Use matrix to determine pattern size. Bug #33364 * Fix compile warnings * regression test improvements glib: * Fix use of uninitialized members in PopplerInputStream. Bug #82630 * Documentation improvements * Do not dist gir_DATA * Remove use of GTK deprecated functions. Bug #82384. Bug #82385 * Build introspection linking to the uninstalled libraries. Big #84526 qt4: * Add a new Page::annotations() that let's you specify subtypes qt5: * Add a new Page::annotations() that let's you specify subtypes utils: * pdfseparate: additonal handling for annotations. Bug #77549 * pdfdetach: fix crash when getPage() returns null. Bug #85145 * pdftocairo: Add support for printing to a Windows printer. Bug #79936 build system: * Move automake version check from autogen.sh to configure.ac. Bug #79797 * Makefile.am cleanups. Bug #79411 * Use poppler-data pkg-config * Make autogen.sh work with variables with spaces * Don't use -fPIC on mingw * Fix build with --disable-utils. Bug #84448 Release 0.26.4 core: * CairoOutputDev: Make sure we always push a transparency group in setSoftMaskFromImageMask(). Bug #81624 * Fix a crash when adding Annotation without contents * Improve non-latin characters in inline notes. Bug #65956 * Don't check for inlineImg twice. Bug #82059 * printf() -> error() glib: * Return NULL in poppler_annot_get_contents also for empty strings * Fix a memory leak when getting text layout and attributes Release 0.26.3 qt5: * autoconf: Improve moc-qt5 detection * Fix compilation with MinGW glib: * Fix typo in api docs * use C90-style comments in public headers core: * Error out instead of exiting if allInter grows too much. Bug #78714 qt4: * Update required version to Qt 4.7.0 build system: * Include stdio.h from poppler-config.h misc: * Update .gitignore files Release 0.26.2 core: * Make sure we have an xref before using. KDE Bug #335413 build system: * autoconf: Fix typo in configure.ac utils: * pdftohtml: exit with 0 with -v and -h Release 0.26.1 core: * Use field value V for radio buttons. Bug #75979 * Fix extraction of text in some files. Bug #78145 * Only add annotations of the current page when splitting. Bug #77549 build system: * autoconf: Fix libopenjpeg 1.5 detection on some systems. Bug #78389 glib: * Fix multiple definition of PopplerTextSpan Release 0.26.0 qt4: * Fix mismatched boolean logic in TextAnnotation::setInplaceIntent qt5: * Fix mismatched boolean logic in TextAnnotation::setInplaceIntent core: * Very small code cleanup cpp: * Very small code cleanup Release 0.25.3 core: * Fix crashes on broken files * Avoid MinGW/Cygwin warnings due to redefinition of NOMINMAX * Fix some small memory leaks qt5: * Fix some kinds of OCG models * Cleanup some deprecated methods glib: * Fix the first coord of the quadrilateral in create_poppler_quads_from_annot_quads(). Bug #76504 utils: * pdftohtml: Fix typo in manpage qt4: * Fix some kinds of OCG models Release 0.25.2 core: * Tagged-PDF support * Open some broken files. Bug #75232 * Fix crashes on broken files * Fix regression parsing some broken files. KDE Bug #329600 * Improve compilation under Win 8 with Visual Studio 2012. Bug #73111 * PSOutputDev: Ensure paper size takes into account rotation. Bug #72312 * PSOutputDev: Fix DocumentMedia/Page/Media/PageBBox DSC comments * PSOutputDev: Use crop box as page size * PSOutputDev: Remove origpagesizes mode and make -origpagesizes an alias for -paper match * PSOutputDev: Only change paper size when different to previous size * PSOutputDev: Ensure there is always a page size in the output * PSOutputDev: Fix regression when creating level1 PS. Bug #75241 * CairoOutputDev: Clip to crop box. Gnome Bug #649886 * Splash: Blend usage in PDF with spot colors casue random output. Bug #74883 * Splash: Fix off by one that caused crash in a file. Bug #76387 * Make sure number of least objects in hints table is valid. Bug #74741 * Limit numeric parsing of character names. Bug #38456 glib: * Tagged-PDF support * Annotation improvements * Install error callback. Bug #73269 * Fix gobject-introspection warnings * demo: Fix performance in text markup annotations * Increase gtk3 dependency qt4: * Improve naming of internal export/import macros * Add GCC visibility export attributes * Expose document-supplied text direction qt5: * Improve naming of internal export/import macros * Add GCC visibility export attributes * Expose document-supplied text direction utils: * pdftocairo: Ensure page size and crop box works the same as pdftops * Fix TIFF writting in Windows. Bug #75969 buildsystem: * Learn about automake 1.14 * Do not define -ansi. Bug #72499 * cmake: Install JpegWriter.h depending on libjpeg * cmake: Use c99 for the c compiler Release 0.25.1 core: * GooString format: Added some tests + improved documentation * GooString format: fixed bug with printing LLONG_MIN * regression test improvements qt4: * Arthur backend font rendering improvements * test program to save to file qt5: * Arthur backend font rendering improvements * Improve detection of Qt5 moc. Bug #72744 * test program to save to file utils: * pdfunite: Work even if there's a single file given * pdfunite: do not lose fonts when merging some files Release 0.25.0 core: * Annotation improvements * Tagged PDF work * Improve speed on some files using ICC color space * Use ICC profile in OutputIntents. Bug #34053 * Limit use of ZapfDingbats character names. Bug #60243 * Splash: correction for knockout transparency groups * regression test improvements utils: * pdftoppm: Added thinlinemode option setting * pdfinfo: Indicate if pdf contains javascript * pdfinfo: Add option to print out javascript * pdfimages: Print size, ratio, and ppi * pdfimages: More image output format support * pdfseparate: allow zero-padded pagespecs glib: * Annotation improvements * Add API to get text, text layout and text attributes for a given area * demo improvements Release 0.24.5 core: * Fix crash due to wrong formatting of error message. KDE Bug #328511 Release 0.24.4 core: * Fix regression in broken endstream detection. Bug #70854 * Catalog: sort entries of NameTrees to make sure lookup works. Bug #26049 * Don't infinite loop if reading from GooFile::read fails. Bug #71835 utils: * pdftotext: Do not close stdout. Bug #71639 * pdftotext: Silence warning for may be used uninitialized variable. Bug #71640 * pdftotext: Escape the text of the xml headers * Warn the user if he provides a wrong range qt4: * Fix typo in xml API. Bug #71643 qt5: * Fix typo in xml API. Bug #71643 Release 0.24.3 core: * PSOutputDev: Fix PFB font embedding. Bug #69717 * CairoOutputDev: Do not set an invalid matrix in drawImage(). Bug #70085 qt4: * Don't crash if getXRef()->copy() fails qt5: * Don't crash if getXRef()->copy() fails utils: * pdfseparate: Allow only one %d in the filename. Bug #69434 Release 0.24.2 core: * Windows: Fix CreateFile fails with ERROR_SHARING_VIOLATION. Bug #69597 utils: * pdfseparate: improve the path building * pdftocairo: check file opening failure in beginDocument() Release 0.24.1 core: * SplashOutputDev: use getRGBLine images if available. Bug #66928 * SplashOutputDev: Don't copy bitmap if we don't need to. * PSOutputDev: Fix regression in -eps -level1sep rendering. Bug #68321 * Fix crash in malformed file 1026.asan.0.42.pdf * use copyString instead of strdup where memory is freed with gfree. Bug #67666 utils: * pdfdetach: don't mention xpdfrc * pdftotext: Fix -bbox with stdin as input. Bug #45163 * pdftohtml: Fix jpeg image export. Bug #48270 * pdfimages: Fix typos in man page glib: * demo: Remove GTK_DISABLE_DEPRECATED compilation flag qt4: * Fix small typo in documentation qt5: * Fix small typo in documentation Release 0.24.0 core: * TextOutputDev: Do not draw ligatures more than once when selected. Bug #9001 * PSOutputDev: Make some pdftops conversions much faster * PSOutputDev: Initialize t3FillColorOnly * SplashOutputDev: Fallback to 1x1 bitmap if we fail to create the real size Release 0.23.4 core: * TextOutputDev: clip the selected text rendering to the selection box. Bug #66983 * CairoImageOutputDev: Fix the bounding box of saved images build system: * Improve linking against pthreads Release 0.23.3 core: * Annotation improvements * Fix crashes on malformed files * TextSelectionPainter: Draw glyphs after selection background * TextOutputDev: add a method to TextPage to get the selection as a list of words qt5: * Initial Qt5 port qt4: * Windows compile fixes * Demo: Allow the choose the page rotation build system: * Fix mingw build * Minor autotools fixes Release 0.23.2 core: * SplashOutputDev: Speed-up some tiling on a 10x factor * Improve caching of lcms2 ICC color profiles * Put some private classes in an anonymous namespace qt4: * Add a thread stresser tool build system: * Fix mingw build Release 0.23.1 core: * XRef stream writing: Write 32-bit offsets when possible * Fix splashModeBGR8 rendering (Bug #64381) glib: * Do not use deprecated gtk_scrolled_window_add_with_viewport() (Bug #64683) build system: * Fix Large file support when using cmake Release 0.23.0 core: * Make rendering thread safe * Large file support * Implement Crypt filter (Bug #62800) * Fix endstream detection (Bug #62985) * CairoOutputDev: support uncolored tiling patterns (Bug #59179) * SplashOutputDev: Introduce Thin Line mode support (Bug #37347) qt4: * Expose Thin Line mode support Release 0.22.4 core: * Always consider a softmask transfer function (Bug #63587) * Fix crash on malformed files (Bug #63190) * Splash: Fix compilation with fixed point mode enabled utils: * Fix crash on some files (Bug #63909) qt4: * Fix name decoding of some attachments (KDE Bug #307786) build system: * Fix compilation with mingw-w64 compiler Release 0.22.3 core: * Check order bounding box values in tiling pattern (Bug #62369) * CairoImageOutputDev: Don't change image interpolation when printing (Bug #62418) * TextOutputDev: Set text matrix when painting selection (Bug #61042) * Only write the file once when saving (Bug #62739) * Fix for complete rewrites in repaired files * Fixlet regarding spec interpretation for Link Zoom value * Fix typos in man pages * Fix compile when not using libjpeg glib: * Always start from the beginning when starting a new search on a page (Bug #59972) qt4: * Fix crash in files with LinkRendition (KDE Bug #317710) build system: * Small cmake improvements Release 0.22.2 core: * Correct rendering of underline and strike out annotations (Bug #61518) * Workaround broken jpeg stream definitions (Bug #61994) * SplashOutputDev: Restore CTM on early exits (Bug #61413) * SplashOutputDev: Make sure we don't try to paint in x < 0 (KDE Bug #315432) * Fix latin page labels. (Bug #61034) * Fix compilation with jpeglib9 * Fix minor valgrind warning utils: * pdfimages: Fix extraction of some images (Bug #61168) build system: * Fix the build with automake-1.13 Release 0.22.1 core: * Fix crash in some pdf files when extracting text (Bug #59561) * Fix crashes in wrongly formed files * Fix wrong warning when opening some files (Bug #58966) build system: * Improve autoconf jpeglib.h detection (Bug #59186) Release 0.22.0 core: * Fix crash in invalid files that define a <= 0 bits per image value * Fix a few issues in JPX decoding when not using OpenJPEG * TextOutputDev: Use page size for max value in TextPage::visitSelection * Fix typo in error message utils: * Fix pdfunite regression (Bug #58569) * Demo fixes and improvements misc: * pdf-inspector improvements Release 0.21.4 core: * SplashOutputDev: Fix crash when rendering in monochrome mode * SplashOutputDev: Fix line widths in monochrome mode (Bug #57294) * PSOutputDev: Fix crop on EPS conversion (Bug #30692) * TextOutputDev: Fix minor logic mistake * Fix assert on some malformed files (Bug #58257) * Move #include "jpeglib.h" into .cc file (Bug #57687) * Filter text that may end up being written to the shell * Fix windows compile warnings glib: * Add poppler_annot_set_flags (Bug #58015) * Demo fixes and improvements qt4: * Fix check_lexer on 32-bit systems Release 0.21.3 core: * Splash: Implement bilinear image scaling (Bug #22138) * CairoOutputDev: Update fill and stroke color in startPage (Bug #54526) * Fix GooString::insert() * Allow large chars in TextPage * Fix crash on ActualText::end * Don't use memcpy to copy classes * Fix warnings glib: * Check if words end with spaces (Bug #54504) * Ensure text is only computed on first render * Fix warnings while generating introspection file * Fix returns tag in PopplerAttachmentSaveFunc api doc * Minor demo fixes Release 0.21.2 core: * CairoOutputDev: make drawImage work with images > 32767 in width/height (Bug #56858) * CairoOutputDev: Fix soft mask when image resolution != smask resolution (Bug #57070) * CairoOutputDev: Fix crash in CairoImageOutputDev with setSoftMaskFromImageMask (Bug #57067) * Remove a check on fonts that we don't need (Bug #56753) * Misc code cleanups utils: * pdftocairo: Add tiff output support (Bug #57006) * pdfunite: Fix -v (Bug #56817) * Misc code cleanups Release 0.21.1 core: * Annotation improvements * Form improvements * CairoImageOutputDev: Support parameterized Gouraud shading (Bug #56463) * UTF validation fixes * Do not call drawing routines if we don't need non text (Bug #54617) * Fix Memory leak in CharCodeToUnicode (Bug #54702) qt4: * Make LinkRendition properties available (Bug #55378) * Accessors for FormWidgetChoice::editChoice * Implement overprint Release 0.21.0 core: * Support the modification of files with Encrypt * Annotation improvements * Form improvements * Splash: Implement DeviceN support * Splash: Avoid bogus memory error for tilingPattern * TextOutputDev: Allow multiple fonts in a TextWord * Kill the concept of base dir * PSOutputDev: Always write HiResBoundingBox (Bug #53159) * Convert UTF-16 to UCS-4 when reading toUnicode cmap * GooString formatting: add support for uppercase hexadecimal * Use error() instead of fprintf(stderr, ...) in Annot::layoutText * poppler-config.h: remove WITH_FONTCONFIGURATION_* macros glib: * Annotation improvements * Add poppler_page_remove_annot() * Add poppler_document_new_from_stream * Add poppler_document_new_from_gfile * Add poppler_page_find_text_with_options (Bug #2951) * Demo improvements * Port tests and demo to GTK+3 qt4: * Add accessor methods for movie poster information * Make 'additional actions' available in Annotation API (Bug #53589) * Add whole-page search method to Poppler::Page * Small changes in tests utils: * pdftohtml: Make the output more xhtml compliant * pdftohtml: Add -fontfullname. (Bug #49872) * pdftohtml: Do not invoke gs anymore build system: * Add the possibility of using lcms1 even if lcms2 is installed * Remove extra fontconfig CFLAGS and LIBS Release 0.20.5 core: * Fix crashes in malformed documents * Fix parsing of very big numbers * Splash: Do not render invalid font outlines (Bug #55573) * Check for NaN in TextPage::addChar build system: * Fix build using mingw64 with winpthread * autotools: Fix compilation when lcms is on non standard locations (Bug #55326) * Support automake-1.12 (Bug #55541) glib: * Chain up finalize to the parent class (Bug #55521) Release 0.20.4 core: * Improvements regarding embedded file handling. (KDE Bug #306008) * Fix opening some broken files (Bug #14303) * Fix memory leaks * Fix crashes in various broken files * Refine warning to only complain when really needed * Remove function declared but not implemented * Remove execution permissions from a header file qt4: * Improvements regarding embedded file handling. (KDE Bug #306008) Release 0.20.3 core: * If NULL, NULL fails as password try EMPTY, EMPTY before failing (Bug #3498) * SplashOutputDev: Fix bogus memory allocation size in Splash::arbitraryTransformImage (Bug #49523) * SplashOutputDev: Fix segfault when scaleImage returns NULL (Bug #52488) * SplashOutputDev: Blend mode enhancements for CMYK * PSOutputDev: Fix conversion when creating multiple strips (Bug #51982) * PSOutputDev: Fix Bitmaps in level2sep or level3sep (Bug #52384) * PSOutputDev: Fix DeviceN images with alternate Lab colorspace in level 3 PostScript (Bug #51822) * PSOutputDev: Make sure xScale and yScale are always initialized (Bug #52215) * Unify poppler-config.h includes in core "installed" headers (Bug #52193) * Replace c++ style includes with c style ones (Bug #52426) utils: * pdfseparate: Return 0 on success Release 0.20.2 core: * Fix compilation on Windows * Copy resources content defined in the pages dict on save (Bug #51369) * PSOutputDev: Correct %%DocumentCustomColors (Bug #51479) * PSOutputDev: Fix handling of DeviceN images in level 3 PostScript (Bug #51548) * Fix crash in malformed documents qt4: * Do not hang on malformed /Annots objects (Bug #51361) Release 0.20.1 core: * Remove unnecesary transparency group handling in splash (Bug #13487) * Include substitute font name in system font cache (Bug #49826) * Fix logic on SplashBitmap::writeImgFile * PSOutputDev: use setoverprintmode only if rip knows it * Fix crash in malformed documents qt4: * Make TextAnnotation constructor public * Fix saving of default TextAnnotation to xml * Keep page rotation into account when normalizing annotation coords glib: * Fix memory leak when document fails to load * Make sure password is always converted to latin1 * Fix typo in documentation build system: * Distribute cmake/modules/FindLCMS2.cmake (Bug #49818) utils: * pdftohtml: Determine if font is bold or italic based on FontDescriptor (Bug #49758) * pdfseparate: Syntax fixes in the man page Release 0.20.0 core: * Reconstruct xref table if xref needed but missing (Bug #40719) * Fix getFullyQualifiedName with unicode field names (Bug #49256) * SplashOutputDev: Fix rendering of knockout groups (Bug #12185) * SplashOutputDev: Fix cmyk transfer bug (Bug #49341) * Fix crashes in broken documents * Bring back the Outputdev::begin/endMarkedContent virtuals * Build fixes qt4: * Convert propertly unicode encoded field qualified names glib: * glib: Use delete[] to free array allocated with new[] (Bug #48447) Release 0.19.4 core: * Annotation improvements * More compatible file writing * SplashOutputDev: Fix slow rendering of pdf with a lot of image masks in pattern colorspace * Fix crashes in broken documents * Fix spurious warning messages utils: * pdftotext: Add missing section heading to man page * pdftohtml: Fix crash when the destination file does not exist build system: * autoconf: Do not append "-ansi" to CXXFLAG, if "-std=XXX" is already specified. * autoconf: Do not clear FREETYPE_CFLAGS, FREETYPE_LIBS before PKG_CHECK_MODULES() * autoconf: Copying graphics library CFLAGS to cpp frontend Makefile.am Release 0.19.3 core: * Annotation improvements * CairoOutputDev: Fix regression caused by mesh gradients * CairoOutputDev: Use correct userfont font bbox (Bug #48399) * CairoOutputDev: Fix paintTransparencyGroup when both mask and fill opacity are required (Bug #48453) * CairoOutputDev: Ensure 0 width lines with stroke_adjust are aligned * CairoOutputDev: Only align stroke coords for horizontal and vertical lines (Bug #48318) * CairoOutputDev: Fix stroke pattern with transparency group (Bug #48468) * Fix crash in JBIG2Stream decoding * Fix memory leak when looking for a substitute font * Fix page labels to not have a null character at the end * Fix Splash CMYK merge error * ttc<->ttf fallback is expected for CJK font list in for Windows (Bug #48046) qt4: * Annotations can now be modified * Annotations can now be added * Annotations can now be removed utils: * pdftohtml: Add producer and version to xml output * pdftohtml: Fix the mask inversion for PNG Release 0.19.2 core: * Annotation improvements * CairoOutputDev: update cairo mesh pattern to 1.12 api * CairoOutputDev: fix some transparency issues (Bug #47739) * CairoOutputDev: Fix regression in some shadings * TextOutputDev: Don't add newline to last line extracted by TextSelectionDumper (Bug #45955) * CJK font improvements * Improve font matching for non embedded fonts * Fix regression regarding forceRasterize in PSOutputDev * Fix typos glyph names in truetype 'post' table standard mac ordering build system: * minor cmake fixes * misc autoconf fixes * POPPLER_VERSION is now wrapped in quotes utils: * pdftohtml: extract mask images even if they are not JPEG (Bug #47186) * pdftohtml: Flip images if they need to (Bug #32340) Release 0.19.1 core: * Improve CJK suport in PSOutputDev * CJK substitute implementation on WIndows platforms * Do not crash on malformed files with 0 bits in the color map of an image * Fix regression in some PSOutputDev array sizing * Improvements to Annotation editing * Fix logic error in Rendition parsing code (Bug #47063) * Minor API changes to SplashOutputDev (Bug #46622) * Fix mismatch in some functions declarations * Update poppler copyright year utils: * pdftops: Fix -passfonts regression. (Bug #46744) * pdffonts: List the encoding of each font. (Bug #46888) * pdftohtml: Add possibilty of controlling word breaks percentage. (Bug #47022) qt4: * Support for LinkMovie object (Bug #40561) * Support for Media Rendition glib: * Add poppler_fonts_iter_get_encoding * Improvements to the demo Release 0.19.0 core: * Merge Xpdf 3.03 * Add support for lcms2 * SplashOutputDev: Implement Overprint * PSOutputDev: Implement Overprint * Expand glyph name ligatures such as "ff", "ffi" etc to normal form (Bug #7002) * Use an Identity CharCodeToUnicode for Adobe-Identity and Adobe-UCS collections (Bug #35468) * CairoOutputDev: Avoid setting huge clip area when printing (Bug #44002) * CairoOutputDev: Fix test for rotation (Bug #14619) * CairoOutputDev: Don't read inline image streams twice (Bug #45668) * CairoOutputDev: set mask matrix before drawing an image with a mask (Bug #40828) * Update glyph names to Unicode values mapping (Bug #13131) * Only use Hints table when there are no parse errors (Bug #46459) * Expose POPPLER_VERSION in poppler-config.h utils: * pdftohtml: Output images in -xml mode if no -i option is specified * pdftohtml: Get rid of static data members; merge duplicated jpeg dumping code * pdftohtml: Be more consistent generating the outlines * pdftohtml: Generate outlines in pdftohtml in -xml mode (Bug #56993) * pdftohtml: Combine UTF16 surrogate pairs (Bug #46521) * pdfinfo: Report page rotation * pdfinfo: Decode utf-16 surrogate pairs * pdftoppm: Allow one of -scale-to-[xy] = -1 to mean the aspect ratio is to be preserved (Bug #43393) * pdftocairo: Allow one of -scale-to-[xy] = -1 to mean the aspect ratio is to be preserved * pdffonts: Add -subst option to list the substitute font name and filename * pdfseparate: Produce PDF/X conformant pdf pages if the original PDF was PDF/X conformant * pdfimages: Add -list option to list all images (Bug #46066) * Improve various manpages glib: * Add poppler_fonts_iter_get_substitute_name * Demo improvements * Update gtk-doc makefile and m4 file * Fix typos in documentation qt4: * Add the option of PSConverter creating EPS * Form support improvements build system: * autotools: Print the cairo version required if not found (Bug #44619) * autotools: Print the glib version required if not found * autotools: Use pkgconfig to check for libopenjpeg (Bug #21789) * autotools: Replace openjpeg compile test with a version test * Add a configuration option for the test data dir Release 0.18.4 core: * CairoOutputDev: Restore temporary clip used in CairoOutputDev::fill when painting a mask * CairoOutputDev: Ensure paintTransparencyGroup uses same ctm as beginTransparencyGroup. Bug #29968 * CairoOutputDev: Use fabs when comparing the transformed line width. Bug #43441 * CairoOutputDev: Remove unused variable in CairoFontEngine.cc. Bug #45442 * SplashOutputDev: Do not use 50Kb of stack in SplashXPath::addCurve. Bug #44905 * JpegWriter: set image parameters after jpeg_set_defaults(). Bug #45224 * OpenJPEG decoder: Set OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG if you have it. Bug #43414 * Lexer: convert integer to real when overflow occurs. Bug #45605 glib: * Various minor introspection and documentation improvements. Bug #44790 * Fix return values. Bug #45440. Bug #45441 * gtk-doc improvements. Bug #45549 * Introspection improvements. Bug #45455 utils: * HtmlOutputDev: Proper unicode support when dumping PDF outline. Bug #45572 * HtmlOutputDev: Fix leaks. Bug #45805 * HtmlOutputDev: Close li tags in generated outlines. Bug #45807 * man pages: fix minor issues with hypens and % build system: * automake: Link to lcms if needed * automake: Fix build for builddir != srcdir. Bug #45434 * automake: Improve moc detection when cross compiling * Fix build with latest mingw-w64 headers. Bug #45407 qt4: * remove non-existing 'qt' include dirs Release 0.18.3 core: * Do not fail if we are trying to save a file with Encrypt that has not been modified. KDE Bug #288045 * Include .otf fonts when finding substitute fonts. Bug #44412 * Fix stack overflow in PDFDoc::markObject(). Bug #44660 * Include strings.h as we use memcpy. Bug #43558 utils: * pdfunite: Properly initialize globalParams. Bug #44659 * pdfseparate: Properly initialize globalParams * Fix iniliazialization of GooString arguments build system: * autoconf: Check for cairo-ft and other cairo backends. Bug #43969 Release 0.18.2 core: * Fix leak in GooString when resizing to a smaller string * Fix crash if failing to parse the colorspace. Bug #42793 * Make GfxColorSpace::parse accept dicts qt4: * Use PDFDoc(wchar_t *, ...) on Windows. Bug #35378 * Add missing include * Minor fixes in documentation utils: * pdftocairo: Fix crash when rendering only odd/even pages in a printing format build system: * Fix pkg-config files Release 0.18.1 core: * PSOutputDev: Output PS that does not confuse libspectre * PSOutputDev: Fix tiling pattern fill matrix. Bug #41374 * PSOutputDev: Emit non repeating patterns just once * PSOutputDev: Fix uncolored tiling patterns. Bug #41462 * CairoOutputDev: Fix crash when using poppler_page_get_image() * CairoOutputDev: Fix various setSoftMask bugs. Bug #41005 utils: * pdftocairo: Flush/close files one we are done using them * pdftocairo: Compile in Windows build system: * CMake: Fix typo in option description * CMake: Correctly include fontconfig include dir * Remove poppler-cairo dependency from poppler-glib pkg-config file qt4: * Minor fixes in documentation Release 0.18.0 core: * Fix small memory leak when dealing with marked content * Remove DCTStream::getRawStream since Stream::getNextStream does the same utils: * Rename pdfmerge to pdfunite * Rename pdfextract to pdfseparate * pdfseparate: Complain if %d is not present and it should * Add pdfseparate and pdfunite man pages build system: * Minor cleanup in regarding removed qt code Release 0.17.4 (0.18 RC) core: * SplashOutputDev: Compile when defining USE_FIXEDPOINT * PNGWriter: Compile with libpng >= 1.5.0 Release 0.17.3 (0.18 Beta 3) core: * PSOutputDev: Use Patterns for tiling fill when PS level >= 2 * PSOutputDev: Avoid using /PatternType if only one instance of the pattern is used * PSOutputDev: Add poppler version as comment in the file * CairoOutputDev: Set mime data for soft masked images (Bug #40192) * CairoOutputDev: Assume printer pixel size is 1/600" when stroking 0 width lines (Bug #39067) * CairoOutputDev: Use cairo_show_text_glyphs() when printing * CairoOutputDev: Fix stroke patterns (Bug #11719) * CairoOutputDev: Fix unique id mime data * CairoOutputDev: fix stroking of very thin lines * CairoOutputDev: align strokes when Stroke Adjust is true and line width <= 1 (Bug #4536) * TextOutputDev: Add TextFontInfo::matches() * Improve PNGWriter * Rework writing of PDF files utils: * Introduce pdftocairo - utility for creating png/jpeg/ps/eps/pdf/svg using CairoOutputDev * Introduce pdfextract - utility to extract PDF pages * Introduce pdfmerge - utility to merge PDF files * Fix compilation warning * pdftohtml: Support text rotation (Bug #38586) * Update SEE ALSO section of man pages glib: * Add poppler_page_get_text_attributes() * Add text attributes information to text demo qt4: * Add a way to get the fully qualified name of a FormField * Minor documentation improvements Release 0.17.2 (0.18 Beta 2) core: * EmbeddedFile improvements * don't gmalloc(-1) upon ftell failure * Fix missing content in some pages (Bug #39637) * Improve selection of CJK fonts (Bug #36474) * SplashOutputDev: Implement overprint * SplashOutputDev: Render dots for 0 length dashed lines (Bug #34150) * SplashOutputDev: Fix bad memory access when not using antialias (Bug #37189) * PSOutputDev: Make level2sep and level3sep write cmyk instead of rgb * PSOutputDev: Make level1sep, level2sep and level3sep write gray instead of rgb for gray images * Fix numerical overflow in libopenjpeg JPXStream (Bug #39361) * Fix crash on truncated JPEG/DCT stream (Bug #36693) * Make sure the dict is a page dict (Bugs #35925 #39072) * Fix calculation of startXRefPos * Handle missing startxref properly (Bug #38209) * Parse the "Medium" modifier when asking fontconfig for a font * Header cleanup * Include cleanup * Define cleanup glib: * Add missing permissions flags to PopplerPermissions * Add missing permission flags to info demo * Update gtk-doc.make * Add poppler_document_get_n_attachments() utils: * pdftohtml: Fix encoding of PDF document metadata (Bug #37900) * pdftohtml: Fix vertical spacing issues (Bug #38019) * pdftotext: Fix -htmlmeta to correctly output U+2019 in PDF metadata (Bug #37900) * pdftoppm: Implement overprint qt4: * Rework EmbeddedFile internals * Fix possible crash in test Release 0.17.1 (0.18 Beta 1) core: * Rework the way form fields tree is built * Cleanup unused parameters/variables glib: * Add JavaScript actions * demo: Show javascript actions in actions view qt4: * tests: Turn some assignments to bool into QVERIFY checks Release 0.17.0 (0.18 Alpha) core: * Splash: Implement tiling patterns * Splash: Support slight hinting * Splash: Radial shading improvements * Splash: General speed improvements * Arthur: Add Hinting API * Cairo: Implement Type 4,5,6,7 shadings using cairo mesh gradients * Cairo: Use the new cairo unique id to set the surface id when printing * PS: Add PS level1 non standard binary output option * PS: Allow setting the rasterization resolution * Form support improvements * Annotation support improvements * General speed improvements * Add support for handling ViewerPreferences * Remove abiword output device utils: * pdftoppm: Add -singlefile option (Bug #32025) * pdftoppm: Add TIFF output format support (Bug #32027) * pdftops: Add PS level1 non standard binary output option * pdftops: Allow setting the rasterization resolution * pdftoabw has been removed glib: * Add poppler_form_field_get_action() (Bug 33174) * Remove GDK API * Remove test-poppler-glib * demo: Add a tooltip with current selected character in text demo * demo: show the activation action of form fields if there's one cpp: * Add TIFF output possibility * Add PNM output possibility qt4: * Support slight hinting * Form support improvements qt3: * The Qt3 frontend has been removed tests: * Merge splash and cairo tests into a single gtk-test tool Release 0.16.4 core: * Small improvements in Annot parsing glib: * Add g_return macros to make sure index is correct in form field choice methods * Fix a crash when a choice form field has no items selected in glib-demo utils: * Small fixes to the pdftohtml manpage * Fix copyright years qt4: * Fix caption of push button fields Release 0.16.3 core: * Increase precision in PS output device * Workaround bug when converting pdf to ps with level1 (Bug #31926) * Fix crash in Splash output device in some broken pdf * Fix infinite loop in some broken files * Fix rendering of some substituted fonts (Bug #34522) * Do not ask Freetype for 0x0 fonts in Splash output device (Bug #34602) * Don't assume y1 > y3 for points of a highlight annotation (Gnome Bug #643028) * Handle fontCIDType2OT when creating freetype font in Cairo output device (Gnome Bug #643273) * Fix crash in some pdf that use ICC color space (Bug #34357) glib: * Don't use an uninitialized local variable in demo * Add some introspection markers qt4: * Fix crash regression in unicodeToQString (again) utils: * pdftotext: Do not crash when using -bbox Release 0.16.2 core: * Fix text extraction for some files qt4: * Fix crash regression in unicodeToQString Release 0.16.1 core: * Fix colorspace issues in the Cairo backend (Bug #32746) * Remove declaration of function without implementation * Do not crash in case jpeg_create_decompress fails (Bug #32890) * Fix variable access mismatch (Bug #33063) * Fix converting some pdf to ps with -level1sep (Bug #32365) * Fix line selection, dont check y for Line selections * Include zlib header in PNGWriter.cc * Fix leak in Splash backend when doing axial shaded fills * Fix label to index conversion on multiple prefixes glib: * Use NULL instead of FALSE for functions returning a pointer * Fix memory leak in poppler_page_get_text_layout() for pages with no text qt4: * Fix unicodeToQString() to correctly decode the Unicode sequence Release 0.16.0 core: * Improve the correctness of radial shadings (Bug #32349) * Adapt the zlib-based FlateStream code to API changes (Bug #32065) * Make PreScanOutputDev be less agressive when deciding to rasterize (Bug #30107) * Fix some warnings in newer gcc in Splash backend * Fix the preliminary bbox/clip calculation in Splash backend * Use A1 instead of A8 for imagemask in the Cairo backend * Windows compile fixes utils: * Do not return 99 (or 1) with -h, -v and -printenc (Bug #32149) * Misc style improvements to pdftohtml code * pdftohtml: Remove the -resolution flag introduced in 0.15.0 and fix the existing -zoom flag build system: * Add more warning flags to the default gcc builds * Enable GObject introspection support in the cmake build system qt4: * Windows compile fixes Release 0.15.3 (0.16 RC) core: * Improve rendering of radial shadings * Open a broken file (Bug #31861) * Correct parsing of linearization table (Bug #31627) * Find fonts inside patterns (Bug #31948) * [win32] Simplify strtok_r implementation * Use a std::vector instead of a var-length-array of chars * Fix crashes in broken files * Use sets instead of arrays for looking for duplicate fonts cpp: * Include correction utils: * pdffonts: Remove duplicated code Release 0.15.2 (0.16 Beta 2) core: * Improve shadings and antialias in the Splash backend (Bug #30436) * Linearization improvements * Small improvements to the Arthur backend * Fix calculation of the size of some pages (Bug #30784) * Fix crashes in broken documents qt4: * Add Page::renderToPainter() method * Add setDebugErrorFunction() method cpp: * Add the hability to render pages to an image utils: * Add -p flag to pdfimages build system: * Remove -ansi flag for cywin and mingw Release 0.15.1 (0.16 Beta 1) core: * Consider render value when colorizing text (Bug #2807) * Improve rendering of Shading Type 6 and 7 * Improve dict lookup speed for big dicts * Fix multiple crashes in malformed PDF files * Fix memory leak in in malformed PDF files * Fix memory leak in the Catalog names * Fix uninitialized uses on DCTScanInfo * Fix a crash when drawing square/circle annots without a border (Bug #30580) * Only clip boxes to mediabox if we are at the page level (Bug #30784) * Do not omit the notdef glyph in the Splash backend * Fix a crash when redering documents with invalid type 3 fonts in the Cairo backend * Form improvements * Add a method to get the PDF file identifier glib: * Add more printing options to the API * Add a method to get the PDF file identifier * Add accessor for all PopplerDocument properties * Form improvements * Documentation improvements * Improvements to the demo qt4: * Add a callback to know which page has been printed * Add a method to get the PDF file identifier * Optimize GooString to QString conversion * Some more autotests * Update Doxyfile (enables .qch file for assistant) build system: * Require Cairo 1.10 utils: * pdftohtml: Add -s option to generate a single HTML page * pdftotext: Add -bbox option cpp: * Add the possibility of loading a document from raw data * Add a method to get the PDF file identifier * Improve Unicode to ustring conversion * Documentation improvements * Update Doxyfile Release 0.15.0 (0.16 Alpha) core: * Remove exception support * Improve creation of Annotations * Fix failure to parse PDF with damaged internal structure. (Bugs #29189 #3870) * Add a way to access the raw text of a page * Speed improvements when reading multiple characters from a given Stream * Speed improvements in the Splash backend * Speed improvement in gray color space calculations * Speed improvement in ICC color space calculations * Speed improvement when reading some fonts * Make GBool a bool instead of an int glib: * Add GObject introspection support * Improve creation of Annotations * Add a way to get the coordinates of each character of a page * Add a way to get the page label * Documentation improvements * Support password protected documents in the demo * Support for selection in the demo * Support for adding annotationss in the demo * Misc improvements in the internals qt4: * Add a way to access the raw text of a page * Recognize "Print" as named action * Documentation improvements build system: * Add option for autogen.sh to skip configure * Nicer autogen.sh output * Improvements when build the glib frontend with CMake utils: * pdftohtml: Use splash instead of external gs invocation to render the background * pdftohtml: Let the user specify the resolution of the background. (Bug #29551) cpp: * Add a way to access the raw text of a page Release 0.14.3 core: * Tell Windows we are writing/reading binary data from stdout/stdio (Bug #29329) * Fix crash when parsing some Movie elements (KDE Bug #249586) Release 0.14.2 core: * Fix rendering of some documents involving tilingPatternFill in the cairo output device * Improve rendering of some annotations * Handle ColorTransform in DCT streams when using libjpeg (Bug #28873) * Fix crash in the ps output device in some files (KDE Bug #246269) * Fix crash in some malformed files (Bug #28842) build system: * Improve build on windows * Add uninstalled .pc file support when using autoconf glib: * Fix a crash when a layer doesn't have a name (Bug #28842) utils: * Fix padding of names in pdftoppm Release 0.14.1 core: * Add ObjectStream caching, makes opening some files ten times faster (Bug #26759) * Fix crash when writing to negative coordinates (Bug #28480) * Check objects are the type we want them to be when parsing GfxICCBasedColorSpace * Optimize Splash::compositeBackground * Optimize color space calculations by using sqrt instead of pow 0.5 * Fix crash in JBIG2Stream with malformed documents build system: * Make sure we ship two needed cmake files * Do not distribute glib/poppler-features.h and poppler/poppler-config.h * Improve compilation with Sun Studio * Fix linking of the cpp frontend when using autotools glib: * Fix links/annots area for some documents (Bug #28588) * Fix poppler_page_find_tex() when called more than once (Bug #27927) utils: * Add -cropbox to pdftoppm manual Release 0.14.0 core: * Fix crash when parsing pdf with broken JBIG2Stream (Bug #28170) * Do not follow loops blindly when parsing XRef (Bug #28172) * Allow quality & progressive mode to be utilised in JpegWriter * Fix potential assert in Lexer code (KDE bug #240208) * Fix opening of files whose /P is stored as unsigned integer * Do not exit() when trying to allocate memory for the XRef fails cpp: * Minor bugfixes * Documentation improvements build system: * Fix build in mingw32 when using autotools * Preserve compiler flags when using cmake Release 0.13.4 (0.14 RC 1) core: * Include standard float.h instead of unportable values.h * Fix first color stop offset of linear gradients. Bug #27837 * Fix compilation if JPEG nor PNG is used * Use fabs for doubles instead of abs * Use strtok_r instead strtok * Adjust bbox for line annots when y1 = y2 * Some fixes and regressions in the cairo output device * Better check of overlapping of table cells when selecting text cpp: * Make the pkg-config files really work * Fix in/out buffer sizes in some functions Release 0.13.3 (0.14 Beta 2) core: * Fix roll optimization in the PS function interpreter * Correctly parse numbers with '+' sign. Gnome bug #614549 * Add support for cached files * Add support for reading a cached file from stdin * Add HTTP support using libcurl, disabled by default * Add some const correctnes to GooString * Rework DCTStream error handling. Bug #26280 * Use current fill_opacity when drawing soft masked images in Cairo backend. Gnome bug #614915 * Use the topleft of the Rect of text annots to draw * Fix saving update docs that have a compressed xref table. Bug #27450 * Parse varius part of the document catalog on demand * Implement colorizing image masks with pattern colorspace in Cairo backend * Fix a crash when rendering 0x0 images in Cairo backend * Check pattern status after setting matrix when rendering images * Improve text selection/extraction order. Bug #3188 * Fix pattern size when bbox is not at 0,0 * Improve colorizing text and masks in pattern colorspace. Bug #27482 * Silence some Illegal entry in bfrange block in ToUnicode CMap. Bug #27728 utils: * Add the -o[dd] and -e[ven] options to pdftoppm * Allow read from stdin using the new cached files feature * Fix crash in pdftohtml when output filename was shorter than 5 characters glib: * Use existing cairo api when rendering to a pixbuf * Compile with -DGSEAL_ENABLE. Bug #27579 Release 0.13.2 (0.14 Beta 1) core: * Improve Movie support * Fix experimental Arthur backend to compile when if Splash backend is disable * Fix usage of some streams in the Cairo backend * Small improvements in the experimental Arthur backend * Minor annotation improvements * Rework LinkRendition to follow the spec * Add support for Set-OCG-State actions * Correctly initialize the grayscale softmask color in the Splash backend * Correctly initialize actualText in TextOutputDev when initialization fails * Various MSVC fixes glib: * Add support for Movie objects * Add support for Screen annotations * Add support for rendition actions * Add support for OCG State actions * Improvements to the demo qt4: * Always compile the experimental Arthur backend * Minor speed improvement in QPainter usage * Add a search overload that takes doubles instead of QRectF cpp: * Fix iconv usage * use gmtime() when gmtime_r() is not available * Fix building in autotools in windows * {from,to}_utf_8() -> {from,to}_utf8() build system: * Multiple CMake build system fixes * Fix of some DIST targets in autotools * Make finding of Qt3 in autotools use pkg-config Release 0.13.1 (0.14 Alpha 2) core: * New C++ frontend to interface with Poppler using only STL * Use the right matrix for the mask in drawMaskedImage in Cairo output device. Bug #16906 * Fix downscaling images when document is rotated in Cairo output device. Bug #26264 * GooVector rewrite, old version had "unknown" origins/license * Fix use after free in a error condition * Improve handling of broken commands. Bug #24575 * Fix potential use after free in Cairo output device. * Fix regression in painting. Bug #26243 * Improve handling of FontConfig. Bug #26544 * Only assume the OC is not visible if it exists and is set to no. Bug #26532 * Fix a potential crash in Splash font handling on out of memory conditions * Implement writeImgFile for splashModeXBGR8 * Several speed increases (around 40% in some documents) in the Splash output device * Improve printing on the Cairo output device * Do not use '\' character in PostScript names * Omit writing of embedded fonts into temporary files in the Cairo output device. Bug #26694 * Improve filtering of some images in the Cairo output device. Bugs #25268, #9860 utils: * pdftoppm: Only swap w with h if rotation is 90 or 270 build system: * Add POPPLER_WITH_GDK in cmake build system. Bug #26247 * Fix typo: "MULTITHREAD" -> "MULTITHREADED in cmake build system * Wrap #include in extern "C" to fix build. Bug #26351 * Add the Win32-specific ENABLE_RELOCATABLE option to cmake build system * Reflect that poppler-glib needs cairo now in cmake build system * Use pkgconfig to detect libpng on autotools build system * Detect the need for nanosleep in solaris in cmake build system. Bug #26650 Release 0.13.0 (0.14 Alpha) core: * Improvements to Annotation rendering. Bug #23108 * Do not give an error when opening files without pages. Bug #24720 * Try to read streams without Length * Do not crop the transformation matrix at an arbitrary value. Bug #25763 * Make poppler (optionally) relocatable on Windows * Use a small object cache in GfxResources to cache GState objects * Reduce the number of redundant pattern creations in the Cairo output device * Use colToDbl() to avoid rounding error in the Cairo output device * Fix problems with mask handling in the Cairo output device. Bug #8474 * Use a better scale down implementation in the Cairo output device * Various optimizations to the Splash output device * Add the possibility to use floats instead of doubles in the Splash output device. Bug #25578 * Write out fixed-content portion of Type 1 fonts in the PS output device build system: * Improvements to the CMake build system * Enable AM_SILENT_RULES by default in autotools * Require glib 2.18 * Require GTK+ 2.14 * Make fontconfig optional with mingw compiler * Remove makefile.vc glib: * Add support for file attachment annotations * Improvements to the demo * Use TextOutputDev to get TextPage when we haven't rendered the page * Remove support for the Splash output device utils: * pdftoppm can now write to jpeg * pdftoppm embeds the correct resolution in png and jpeg files qt4: * Minor improvements to the tests Release 0.12.3 core: * Be more lenient with /Decode key on images. Bug #17439 * Correctly initialize fileName in LinkGoToR. Bug #25221 * Improve the reconstruction of the XRef for broken files * [Cairo backend] Do not crash on malformed files. Bug #24575 * Accept Fontname if FontName is not present. KDE bug #217013 * Make PSOutputDev code a bit more resilient * Fix writing of null objects. Bug #25465 * [Cairo backend] Fix crash in some documents. GNOME bug #603934 * Correctly initialize profileCommands in Gfx constructor build system: * Check for openjpeg in the C++ part as it uses bool in the header. Bug #25103 Release 0.12.2 core: * Fix a memory leak when converting to PostScript * Fix crash when reading a font fails. Bug #24525 * Make the ICC cache per page instead of global. Bug #24686 * Do not accept negative interval lengths in the page labels tree. Bug #24721 * Do not crash on files Aspect of Movie objects are reals instead of integers. Bug #24733 * Do not render patterns when using CairoImageOutputDev * Allow Transitions dictionary to be a Ref * Do not crash if jpeg_start_decompress fails. KDE bug #214317 glib: * Fix CVE-2009-3607 qt4: * Use '.' in the annotations XML instead of the decimal separator of the current locale Release 0.12.1 core: * Fix compilation on some compilers * Only initialize the font list once in Windows32/MSVC * Do not crash on fonts without CharCodeToUnicode. Bug #24036 * Fix regression due to not setting LC_NUMERIC anymore * Improve realibility for Streams with broken Length. Bug #6841 * Write the Info into the trailer dict if there is one. Bug #24091 * Do not crash when saving files that come from a stream without name. Bug #24090 * Improve relability of the save function * Fix the Length value if it was wrong when saving * Fix includes for those using internal headers * Rework how hinting is used in the splash backend. It is disabled by default now * fix constructor of DCTStream when using internal decoder * Security fixes based xpdf 3.02pl4 qt4: * Add the possibility of setting wheter to use or not font hinting * Add a way for converters to return more exact errors they had when converting * Check the document is not locked when converting to PS build system: * Compile on Cygwin * Use _WIN32 instead of WIN32. Bug #24259 * Add the possibility to pass LIB_SUFFIX when using CMake Release 0.12.0 core: * Fix printf format security warnings * Improve rendering of radial shadings. Bug #20238 * Better fallback when there's a font type mismatch. Bug #17252 * Do not crash on attachments without data stream. Bug #10386 * Fix infinite loop in JBIG2Decoder. Bug #23025 build system: * Minimizes pkg-config dependencies for Qt frontends * Add automake 1.11 support * Use the newest automake found and not the oldest * Support AM_SILENT_RULES when using automake 1.11 utils: * Add common options to pdftoabw Release 0.11.3 (0.12 RC 1) core: * Optimization in the Cairo renderer for some fonts * Do not apply masks when fill color space mode is csPattern in the Cairo renderer. Bug #22216 * Check for overflow when parsing integers. Bug #23078 * Do not save the font file twice for FreeType fonts in the Cairo renderer. Bug #20491 * Use current fill_opacity when drawing images in the Cairo renderer * Fix alpha rendering in some files in the Splash renderer. Bug #22143, #22152 * Implement tiling patterns in the Cairo renderer * When converting a cm matrix to PS write 4 significant digits for numbers < 1 not 4 decimals. Bug #23332 * Fix changing of locale, now poppler no longer changes LC_NUMERIC to "C" * Return PDF version as two integers instead of as a double Qt4: * Addition of the Color Management API * Small fix to documentation * Fix backwards text search utils: * Add the -png flag to pdftoppm to output to PNG Release 0.11.2 (0.12 Beta 2) core: * Make DecryptStream return sane values for getPos(). Bug #19706 * Fix bug when printing pdf with multiple page sizes in duplex mode * Initilize AnnotColot properly when the Array is not correct * Fix crash on some files with forms. Bug #22485 * Fix crash in files with invalid embedded files. Bug #22551 * Improve FileSpec attribute parsing * Cairo output device improvements. Bugs #10942, #18017, #14160 * Implement blend modes in cairo backend * Handle fontType1COT fonts in CairoFontEngine * Fix generation of PS for some files. Bug #18908 * Don't use byte_lookup table when color space doesn't support getLine methods. Bug #11027 * Fix rendering of PDF files with malformed patterns. Bug #22835 * Add the possibility of disabling font substitution in pdftops. Bug #23030 * Fix some radio buttons not being detected as such glib: * Improvements to the demo Qt4: * Improvements to the demo build system: * Use gtkbuilder rather than libglade for some tests utils: * Fix bug with noCrop parameter in pdftops Release 0.11.1 (0.12 Beta 1) core: * Support colorizing text in pattern colorspace. Bug #19670 and #19994 * Add the possibility of forcing no hinting of fonts in the Splash backend * Support multiple page sizes when converting to PS. Bug #19777 * Also tokens with leading 00 when parsing the char to unicode map. Bug #22025 * Improvements of rendering speed in documents using PS transformations a lot. Bug #21562 * More work on Annotations support * Use Interpolate flag to decide whether applying image interpolation during rendering. Bug #9860 * Handle Streams in CMap definitions. Bug #22334 * Fix some bugs in JBIG2Stream handling * Fix dashed line in page 1 of bug 20011 * Fix exit(1) when rendering a file * Fix pdftops crash on file from KDE bug #174899 * Fix PS generation in some files. Bug #20420 * Do not create the GfxColorTransform if the lcms could not be created. Bug #20108 * Check Mask entries are int before using them, if they are real cast to int and try to use them. Bug #21841 * Use the correct value when creating the V field for form combo boxes * Give an error when using level1sep in pdftops without having CMYK support. Bug #22026 * Don't include lcms.h in GfxState.h * Fix splashColorModeNComps to correctly include all values for each SplashColorMode * Add splashClearColor that assigns white to the given colorptr * Kill support for specifying extension in openTmpFile. Bug #21713 * Fix "Conditional jump or move depends on uninitialised value". Bug #20011 glib: * Add poppler_annot_markup_has_popup() * Hyphenate UTF-8 and UTF-16BE. Bug #21953 * Use g_strerror instead of strerror. Bug #22095 * Fix a crash when a destination points to an invalid page * Improvements to the demo Qt4: * Add LinkDestination::destinationName() * Do not try to resolve named destinations for GoTo links pointing to external documents * Add Page::thumbnail() * Improvements to the demo * Improvements to the documentation build system: * Build fix for MSVC * Better lcms cmake check comming from kdelibs * Use pkgconfig for autotools lcms check * Remove unneeded files from repo. Bug #22094 Release 0.11.0 (0.12 Alpha) core: * Add initial support for color management * Remove case-insensitive matching of filenames in PDFDoc constructor * Fix extraction of some ActualText content * More work on Annotations support * Improve font rendering in Cairo output device * Fix bug in cairo backend with nested masks * Fix cairo luminosity smask rendering * Add optionally text support to Cairo output device * Add the possibility of setting the datadir on runtime * Return an error code instead of a boolean when saving * Make the font scanner more versatile * Small opimization in documents that use PostScriptFunction transforms * Minor optimization to Stream handling * Fix some compile warnings glib: * Optional content support * More work on Annotations support * Improvements to the demo * Documentation improvements * Fix build when compiling with GTK_DISABLE_SINGLE_INCLUDES Qt4: * Support URI actions for Table Of Contents items * Documentation improvements * Improvements to the demo * Add a FontIterator for iterating through the fonts of the document utils: * Allow the use of cropbox in pdftoppm * Make pdftohtml output png images when the image stream is not a jpeg * Make pdftotext accept cropping options like pdftoppm * Support rendering non-square pixels in pdftoppm build system: * Require Cairo 1.8.4 for the Cairo output device * Require CMake 2.6 when using the CMake build system * Optionally require libpng for pdftohtml * Optionally require libcms for color management Release 0.10.6 core: * Fix problems that happen when parsing broken JBIG2 files. CVE-2009-0799, CVE-2009-0800, CVE-2009-1179, CVE-2009-1180 CVE-2009-1181, CVE-2009-1182, CVE-2009-1183, CVE-2009-1187, CVE-2009-1188 * Fix parsing of incorrect border arrays. Bug #19761 * Fix clip test for fonts. Bug #20950 * Fix getGlyphAdvance to behave correctly on font size changes. Bug #20769 * Misc build fixes build system: * Fix the Qt4 version we need Release 0.10.5 core: * Read the UF entry if present and prefer it over F in Filespec dictionary * Fix typo that was making CairoOutputDev crash on some files. Bug #17337 * Make JBIG2Stream more robust to corrupt input data * Do not blindly follow loops parsing OutlineItem. Bug #18364 * Set up the error manager before calling jpeg_create_decompress. Bug #20484 * Check there is an optional content config before using it. Bug #20587 * Fix rendering of some PDF with OpenType fonts. Bug #20605 build system: * Yet more support for build on windows * Use AC_CHECK_HEADER to find headers. Bug #20538 * Check for pkgconfig before using it * General autotools improvements Release 0.10.4 core: * Fix a memory leak when asking for a document-level JS * Do not crash in some PDF we do not parse correctly. Bug #19702 * Fix crash on unexepcted form Opt value. Bug #19790 utils: * Fix pdfimages to extract i color components per pixel jpeg images. Bug #19789 Release 0.10.3 core: * Fix a crash on documents with malformed outline. Bug #19024 * Fix leak on AnnotScreen destructor. Bug #19095 * Fix wrong PS generation when a large image is in Patterns. Bug #18908 * Remove BaseFile.h it was never used. Bug #19298 * Improve document saving * Fix PS generation of PDF with malformed font Length2 definition * Fix a leak while parsing annotations * Fix rendering of some checkboxes Qt4: * Fix positioning of Form rects on PDF with cropbox * Fix positioning of Annotation rects on PDF with cropbox. Bug #18558. * Small documentation improvements * Make Document::fonts() work when called more than once. Bug #19405 build system: * CMake: look harder for openjpeg * CMake: update the poppler core headers installation * Autotools: do not install Function.cc as it's not a header Qt: * Fix deserialization of links right coordinate Release 0.10.2 core: * Fix a crash when selecting text in word mode * Fix a crash in some malformed documents (second argument of opMarkPoint is not a dictionary) * Ensure cairo font matrix is invertable. Fixes bugs #18254 and #18429 * Fix a memory leak (Bug #18924) Qt4: * Fix deserization of links right coordinate misc: * Fix build on Solaris 10 + Sun Studio 12 * Compile with -pedantic Release 0.10.1 core: * Improvements in Optional Content support * Small fix in Form support * Fix memory leak in case of error * Fix potential crash on text search * Try render documents with invalid indexed color space parameters. Bug #18374 * Fix crash on text extraction when poppler-data is not installed. Bug #18023 Qt: * Fix two memory leaks Qt4: * Small documentation improvement * Fix memory leak in the demo code Release 0.10.0 core: * Fix crashes on PDF using Stitching or Axial Shading painting * Fix rendering of PDF with Type1 fonts that have more than one encoding definition per line * Do not try to save documents that have Encryption as we do not support that and the user ended with a broken file * Fix crash on files with OptionalContentGroup but no Name Qt4: * Fix the area of the links to be correctly reported on rotated documents misc: * Mingw+Msys should work Release 0.9.3 (0.10 RC 2) core: * Fix rendering regression on some embedded fonts * Fix rendering regression of some special fonts * Fix crash on documents with bogus jpeg data Qt4: * The printing flag defaults to true on PSConverter * Documentation improvement utils: * Fix regression that made HmtlOutputDev ignore jpeg images misc: * Improve compilation on mingw Release 0.9.2 (0.10 RC 1) core: * Fix conversion to PS some files (bug #17645) * Small Form fixes * Small JS fixes * Improve memory usage of the cairo renderer utils: * Fix mismatched free/delete in pdftohtml * Fix memory leak in pdftohtml * Fix crash in pdftohtml glib: * Fix a crash in forms demo misc: * Compile with -pedantic Release 0.9.1 (0.10 Beta 2) Core: * Fix crash on some AESv2 encrypted files (bugs #13972, #16092, #17523) * Improve parsing of broken files (bug #17568) glib frontend: * Minor improvements to the demo application utils: * pdftohtml: Generate the outline file in the same place of the other generated files (bug #17504) Release 0.9.0 (0.10 Beta 1) Core: * Initial JavaScript support * Annotation improvements * Improvements in the Arthur based renderer * Improvements in the Cairo based renderer * Added a JPEG2000 decoder based on OpenJPEG * Small fixes in ActualText implementation * Fix jpeg rendering when not using the libjpeg based decoder * Movie fixes * Do not get out of memory on documents that specify huge fonts * Emulate Adobe Reader behaviour on documents with duplicate keys in Dictionaries * Forms improvements Qt4 frontend: * Annotation improvements * Forms improvements * Add the possibility of extracting embedded fonts * Initial Movie support * Documentation improvements * Small improvements in the PS exporter glib frontend: * Annotation improvements * Attachment fixes utils: * updated man pages * Added -listenc to pdfinfo and pdftotext Release 0.8.7 Core: * Fix regression in Form rendering * Fix memory leak in the cairo backend Release 0.8.6 Core: * Call error() when font loading fails * Be less strict parsing TTF tables (bug #16940) * Fix crash due to uninitialized variable Qt 4 frontend: * Make the paper color setting working as it should * Make sure to use the correct page width/height for form widgets coordinates Release 0.8.5 Core: * Fix crash on PDF that define a page thumbnail but it's not a Stream * Fix crash when Annots object is not of the desired type * Fix crash when obtaining fonts in PDF where XObjects link themselves in loops * Fix crash on documents with an IRT object * Saving should work much better now * Plug some memory leaks in Annotation handling Utils: * pdftohtml: Don't crash on documents that specify an invalid named dest for a link * pdftohtml: Make html output to keep all the spaces with   * pdftohtml: Improve a bit text layout * pdftohtml: Make xml output valid xml Release 0.8.4 Core: * Fix leak in ABWOutputDev.cc * Fix uninitialized variable that broke file saving in some cases * Use a single global FT_Library in CairoOutputDev. Fixes some crashes in CairoOutputDev. Qt 4 frontend: * Fix saving over existing files build system: * Make sure Qt4 moc is used to generate moc files in Qt4 frontend Release 0.8.3 Core: * Fix crash when reading some PDF with annotations * Fix crash on PDF that reference Optional Content elements that don't exist * Fix leaks on error conditions * Do not limit CharCodeToUnicodeString to 8 characters * Support for surrogates outside the BMP plane Qt 3 frontend: * Fix crash when reading PDF with password * Fix leak when calling scanForFonts() Qt 4 frontend: * Fix the text() method Splash renderer: * Fix compilation with --enable-fixedpoint Release 0.8.2 core: * Fix call broken by a fix introduced in 0.8.1 Release 0.8.1 core: * Do not call FT_Done_Face on a live cairo_font_face_t as it might cause crashes * Do not take into account Colorspace resource subdictionary for image XObjects * Downsample 16 bit per component images to 8 bit per component so they render build system: * Link to pthread when the system needs it windows: * Fix comparing against NULL instead against INVALID_HANDLE_VALUE when calling FindFirstFile Release 0.8.0 * Fix caching of members in the glib frontend causing issues with rendering * Change glib public api to have a correct naming * Some better error handling on corner cases * Check the document stream is seekable when opening it * Build fixes with autotools and with cmake * Fix infinite recursion on some malformed documents when consulting the fonts * Fix possible crash when asking for Movie contents Release 0.7.3 (0.8 RC 2) * Fix regression in Splash renderer * Fix off-by-one write in Splash * Plug some minor leaks in Optional Content code * Improve error handling when creating a document in the glib frontend Release 0.7.2 (0.8 RC 1) Major Changes: * Improve font matching not forcing default values onto Fontconfig * Add preliminary annotations support in the glib frontend * Initial Movie support in the core * Make GDK dependency optional in glib bindings Minor Changes: * Make the core able to read mime types of embedded files * Qt4 API for accessing mime types of embedded files * Handle correctly check state of optional content groups regarding parents state * Avoid setting singular CTM matrices on the Cairo backend * Improved Qt4 API to get character position * Qt4 api documentation improvements * Qt4 minor stability fixes * Proper lib64 Qt detection * Fix build when compiling without cairo support Release 0.7.1 (0.8 Beta 2) Major Changes: * Really distribute CMake files as optional build tool * Initial Optional Content support in core and in the Qt4 frontend Minor Changes: * Allow grouped checkboxes to be selected individually * Qt4 demo program improvements * Keep cairo and cairo_shape consistent * Safety checks on Splash renderer so that it does not draw outside the allocated bitmap * Do not try to display bitmaps of invalid size * Fix building with exceptions * Improvements for building with MSVC and CMake Release 0.7.0 (0.8 Beta 1) * Saving support * Partial annotation support * Forms improvements * Add support for ActualText entries * Display characters outside of unicode BMP with TT font * CJK rendering fixes * Implement Adobe Glyph Naming convention for fonts * CMake as optional build tool * Better font scaling for non embedded fonts * Preserve PDF page labels when we output as postscript Release 0.6.4 Qt4 frontend: * Fix crash on links that point to a non existent page * Make Document::renderHints return the correct render hints * Fix infinite loop when parsing LineAnnotation core: * Fix crash in the Splash renderer when T3 fonts are badly defined * Draw underlined Links correctly utils: * Fix two use after free bugs in HtmlOutputDev.cc build system: * Fix build on mingw32 tests: * Distribute the glade file of pdf-inspector Release 0.6.3 core: * Fix crash in extra debug code glib frontend: * Make sure passwords are passed correctly to poppler core Qt frontend: * Fix crash on documents that specify an empty date build system: * Disable gtk tests if the user disabled glib frontend Release 0.6.2 poppler core: * Fix CVE-2007-4352, CVE-2007-5392 and CVE-2007-5393 * Fix a crash on documents with wrong CCITTFaxStream * Fix a crash in the Cairo renderer with invalid embedded fonts * Fix a crash with invalid TrueType fonts * Check if font is inside the clip area before rendering it to a temporary bitmap in the Splash renderer. Fixes crashes on incorrect documents * Do not use exit(1) on DCTStream errors * Detect form fields at any depth level * Do not generate appearance stream for radio buttons that are not active * mingw fixes build system: * Require fontconfig >= 2.0 * builddir != srcdir fixes Qt4 frontend: * Improved documentation misc: * Fix FSF address Release 0.6.1 poppler core: * Fix printing with different x and y scale * Fix crash when Form Fields array contains references to non existent objects * Fix crash in CairoOutputDev::drawMaskedImage() * Fix embedded file description not working on some cases Qt4 frontend: * Fix printing issue * Avoid double free * Fix memory leak when dealing with embedded files glib frontend: * Fix build with --disable-cairo-output * Do not return unknown field type for signature form fields build system: * Support automake-1.10 * More compatible sh code in qt.m4 utils: * Fix build on Sun Studio compiler Release 0.6 - CairoOutputDev fixes - Allow pdftoppm to read/write from stdin/stdout - API work on Qt4 frontend - Fix pdfimages produces inverted image for black & white image - Fix error on the NFKC text matching routine - Add support for word and line selections - Do not enforce %%EOF at the end of file - Pad zeroes instead of aborting when rendering 1-bit images and the stream is too short - Update glib bindings documentation Release 0.5.91 (0.6 Release Candidate 2) - Various memory leaks fixed - Compile with --enable-fixedpoint. Bug #11110 - Header cleanup - Remove dependency on debugxml. Bug #11187 - Allow access to document metadata in glib and qt4 frontends - Several glib API frontend improvements - Fix crash on accessing embedded files - Build on Sun Force compiler - Render '*' instead of the actual content in password form fields - Fix pdftohtml complex output. Bug #9746 and #11610 - Windows build fixes - Improve Japanese font support. Bug #11413 - Do not exit the program on files that confuse libjpeg - Update required cairo version to 1.4 - Fix CVE-2007-3387 Release 0.5.9 (0.6 Release Candidate) - Merge xpdf 3.02 changes - Qt4 frontend is not marked anymore as unstable - Support for Sound objects - Support for Opening/Closing page actions - Support for page duration - Improve PS Tokenizer performance thanks to Scott Turner - Various speed ups by Krzysztof Kowalczyk - Beginning of Interactive Form support by Julien Rebetez - xpdfrc is no longer used for anything - Add AbiWord output device and pdftoabw program by Jauco Noordzij - Fix security issue MOAB-06-01-2007 - Lots of bugs fixed Release 0.5.4 - Automatically read in CJK encoding files if they're installed (#2984, #7105, #7093). This works with the new poppler-data package. - Speed ups by Krzysztof Kowalczyk (#8112) - Patch from Dom Lachowicz to let the utils take input on stdin. - Bugs fixed (#8182, #4649, #7906, #8048, #7113, #4515, #3948, #7924, #7780, #7646, #6948, #7788, #7661, #7005) Release 0.5.3 - Add poppler as a private requires of poppler-glib. - Allow CairoFont creation to fail more gracefully (#4030). - Back out the rest of krh's type3 font work. - Revert splashModeRGB8 changes. - Add missing poppler-annotation-helper.h. Release 0.5.2 - Much improved Qt bindings (Albert Astals Cid). - Cairo backend now supports masked images (Jeff Muizelaar, #6174). - Patches from Kouhei Sutou to make glib bindings more language binding friendly (#6907, #6897, #6899, #6905). - Search now works with ligatures (Ed Catmull, #2929). - The glib bindings now has an entry point to render to a cairo_t. - GCC 4.1 and MSVC compilation fixes. - Memory leaks plugged: #6908, #6947, #6765, #6764, #6187 - Misc bug fixes: #6984, #6896, #6913, #6926, #4481, #5951, #6551, #6500, #6492, #6454, #6079, #6167. Release 0.5.1 - Support for embedded files. - Handle 0-width lines correctly. - Avoid external file use when opening fonts. - Only use vector fonts returned from fontconfig (#5758). - Fix scaled 1x1 pixmaps use for drawing lines (#3387). - drawSoftMaskedImage support in cairo backend. - Misc bug fixes: #5922, #5946, #5749, #5952, #4030, #5420. Release 0.5.0 - Font matching code for non embedded fonts now use fontconfig instead of hard coded list of fonts. - Merge in Xpdf 3.01 changes. - Add command line tools from Xpdf. - Make install of Xpdf header files ./configure'able. Release 0.4.0 - Real text selection. - API breakage in glib wrapper: dropping dest_x and dest_y arguments from poppler_page_render_to_pixbuf(). Release 0.3.3 - New glib API to get document font information (Marco). - More document properties available as glib properties (Emil Soleyman-Zomalan, #3359) - Optimize color conversion for images. - Support for constant opacity. - Fix problems with pkg-config files. - Bugs fixes: #3491, #2911, #3362, #3340, #3265, #3239, #3396. Release 0.3.2 - New API to get poppler version and backend type. - Various font fixes from Albert Astals Cid. - Update to cairo 0.5.0 API, including better font support. - Meta data for the glib binding. Release 0.3.1 - Add qt/poppler-private.h to SOURCES - Jeff's path to use zlib instead of builtin decompression. - Bug fixes: #2934, segfault on invalid links, #3114 Release 0.3.0 - First cut at qt wrapper, including a getText() method for getting text from a page. - More glib functionality: meta data, set page orientation, print to PS - Performance fixes for glib cairo - Bug fixes Release 0.2.0 (Tue Apr 5 12:32:10 EDT 2005) - Add glib wrapper for poppler, which will use cairo rendering if available - Support for page labels - configure and build fixes. Release 0.1.2 (Wed Mar 9 10:45:58 EST 2005) - cairo optimizations and fixes from Jeff Muizelaar - Bump cairo requirement to 0.4 - Make cairo and gtk checks fail gracefully Release 0.1.1 - Fix issues with installed header files including config.h - Fix a couple of typos in pkg-config files - Install splash and cairo header files when necessary Release 0.1 - no date yet - First release - More NEWS here poppler-24.02.0/README-XPDF000066400000000000000000000354531455701731300147770ustar00rootroot00000000000000Xpdf ==== version 3.03 2011-aug-15 The Xpdf software and documentation are copyright 1996-2011, 2022 Glyph & Cog, LLC. Email: derekn@foolabs.com WWW: http://www.foolabs.com/xpdf/ The PDF data structures, operators, and specification are copyright 1985-2006 Adobe Systems Inc. What is Xpdf? ------------- Xpdf is an open source viewer for Portable Document Format (PDF) files. (These are also sometimes also called 'Acrobat' files, from the name of Adobe's PDF software.) The Xpdf project also includes a PDF text extractor, PDF-to-PostScript converter, and various other utilities. Xpdf runs under the X Window System on UNIX, VMS, and OS/2. The non-X components (pdftops, pdftotext, etc.) also run on Windows and Mac OSX systems and should run on pretty much any system with a decent C++ compiler. Xpdf will run on 32-bit and 64-bit machines. License & Distribution ---------------------- Xpdf is licensed under the GNU General Pulbic License (GPL), version 2 or 3. This means that you can distribute derivatives of Xpdf under any of the following: - GPL v2 only - GPL v3 only - GPL v2 or v3 The Xpdf source package includes the text of both GPL versions: COPYING for GPL v2, COPYING3 for GPL v3. Please note that Xpdf is NOT licensed under "any later version" of the GPL, as I have no idea what those versions will look like. If you are redistributing unmodified copies of Xpdf (or any of the Xpdf tools) in binary form, you need to include all of the documentation: README, man pages (or help files), COPYING, and COPYING3. If you want to incorporate the Xpdf source code into another program (or create a modified version of Xpdf), and you are distributing that program, you have two options: release your program under the GPL (v2 and/or v3), or purchase a commercial Xpdf source license. If you're interested in commercial licensing, please see the Glyph & Cog web site: http://www.glyphandcog.com/ Compatibility ------------- Xpdf is developed and tested on Linux. In addition, it has been compiled by others on Solaris, AIX, HP-UX, Digital Unix, Irix, and numerous other Unix implementations, as well as VMS and OS/2. It should work on pretty much any system which runs X11 and has Unix-like libraries. You'll need ANSI C++ and C compilers to compile it. The non-X components of Xpdf (pdftops, pdftotext, pdfinfo, pdffonts, pdfdetach, pdftoppm, and pdfimages) can also be compiled on Windows and Mac OSX systems. See the Xpdf web page for details. If you compile Xpdf for a system not listed on the web page, please let me know. If you're willing to make your binary available by ftp or on the web, I'll be happy to add a link from the Xpdf web page. I have decided not to host any binaries I didn't compile myself (for disk space and support reasons). If you can't get Xpdf to compile on your system, send me email and I'll try to help. Xpdf has been ported to the Acorn, Amiga, BeOS, and EPOC. See the Xpdf web page for links. Getting Xpdf ------------ The latest version is available from: http://www.foolabs.com/xpdf/ or: ftp://ftp.foolabs.com/pub/xpdf/ Source code and several precompiled executables are available. Announcements of new versions are posted to comp.text.pdf and emailed to a list of people. If you'd like to receive email notification of new versions, just let me know. Running Xpdf ------------ To run xpdf, simply type: xpdf file.pdf To generate a PostScript file, hit the "print" button in xpdf, or run pdftops: pdftops file.pdf To generate a plain text file, run pdftotext: pdftotext file.pdf There are five additional utilities (which are fully described in their man pages): pdfinfo -- dumps a PDF file's Info dictionary (plus some other useful information) pdffonts -- lists the fonts used in a PDF file along with various information for each font pdfdetach -- lists or extracts embedded files (attachments) from a PDF file pdftoppm -- converts a PDF file to a series of PPM/PGM/PBM-format bitmaps pdfimages -- extracts the images from a PDF file Command line options and many other details are described in the man pages (xpdf(1), etc.) and the VMS help files (xpdf.hlp, etc.). All of these utilities read an optional configuration file: see the xpdfrc(5) man page. Upgrading from Xpdf 3.02 (and earlier) -------------------------------------- The font configuration system has been changed. Previous versions used mostly separate commands to configure fonts for display and for PostScript output. As of 3.03, configuration options that make sense for both display and PS output have been unified. The following xpdfrc commands have been removed: * displayFontT1, displayFontTT: replaced with fontFile * displayNamedCIDFontT1, displayNamedCIDFontTT: replaced with fontFile * displayCIDFontT1, displayCIDFontTT: replaced with fontFileCC * psFont: replaced with psResidentFont * psNamedFont16: replaced with psResidentFont16 * psFont16: replaced with psResidentFontCC See the xpdfrc(5) man page for more information on the new commands. Pdftops will now embed external 16-bit fonts (configured with the fontFileCC command) when the PDF file refers to a non-embedded font. It does not do any subsetting (yet), so the resulting PS files will be large. Compiling Xpdf -------------- See the separate file, INSTALL. Bugs ---- If you find a bug in Xpdf, i.e., if it prints an error message, crashes, or incorrectly displays a document, and you don't see that bug listed here, please send me email, with a pointer (URL, ftp site, etc.) to the PDF file. Acknowledgments --------------- Thanks to: * Patrick Voigt for help with the remote server code. * Patrick Moreau, Martin P.J. Zinser, and David Mathog for the VMS port. * David Boldt and Rick Rodgers for sample man pages. * Brendan Miller for the icon idea. * Olly Betts for help testing pdftotext. * Peter Ganten for the OS/2 port. * Michael Richmond for the Win32 port of pdftops and pdftotext and the xpdf/cygwin/XFree86 build instructions. * Frank M. Siegert for improvements in the PostScript code. * Leo Smiers for the decryption patches. * Rainer Menzner for creating t1lib, and for helping me adapt it to xpdf. * Pine Tree Systems A/S for funding the OPI and EPS support in pdftops. * Easy Software Products for funding several improvements to the PostScript output code. * Tom Kacvinsky for help with FreeType and for being my interface to the FreeType team. * Theppitak Karoonboonyanan for help with Thai support. * Leonard Rosenthol for help and contributions on a bunch of things. * Alexandros Diamantidis and Maria Adaloglou for help with Greek support. * Lawrence Lai for help with the CJK Unicode maps. Various people have contributed modifications made for use by the pdftex project: * Han The Thanh * Martin Schröder of ArtCom GmbH References ---------- Adobe Systems Inc., _PDF Reference, sixth edition: Adobe Portable Document Format version 1.7_. http://www.adobe.com/devnet/pdf/pdf_reference.html [The manual for PDF version 1.7.] Adobe Systems Inc., "Errata for the PDF Reference, sixth edition, version 1.7", October 16, 2006. http://www.adobe.com/devnet/pdf/pdf_reference.html [The errata for the PDF 1.7 spec.] Adobe Systems Inc., _PostScript Language Reference_, 3rd ed. Addison-Wesley, 1999, ISBN 0-201-37922-8. [The official PostScript manual.] Adobe Systems, Inc., _The Type 42 Font Format Specification_, Adobe Developer Support Technical Specification #5012. 1998. http://partners.adobe.com/asn/developer/pdfs/tn/5012.Type42_Spec.pdf [Type 42 is the format used to embed TrueType fonts in PostScript files.] Adobe Systems, Inc., _Adobe CMap and CIDFont Files Specification_, Adobe Developer Support Technical Specification #5014. 1995. http://www.adobe.com/supportservice/devrelations/PDFS/TN/5014.CIDFont_Spec.pdf [CMap file format needed for Japanese and Chinese font support.] Adobe Systems, Inc., _Adobe-Japan1-4 Character Collection for CID-Keyed Fonts_, Adobe Developer Support Technical Note #5078. 2000. http://partners.adobe.com/asn/developer/PDFS/TN/5078.CID_Glyph.pdf [The Adobe Japanese character set.] Adobe Systems, Inc., _Adobe-GB1-4 Character Collection for CID-Keyed Fonts_, Adobe Developer Support Technical Note #5079. 2000. http://partners.adobe.com/asn/developer/pdfs/tn/5079.Adobe-GB1-4.pdf [The Adobe Chinese GB (simplified) character set.] Adobe Systems, Inc., _Adobe-CNS1-3 Character Collection for CID-Keyed Fonts_, Adobe Developer Support Technical Note #5080. 2000. http://partners.adobe.com/asn/developer/PDFS/TN/5080.CNS_CharColl.pdf [The Adobe Chinese CNS (traditional) character set.] Adobe Systems Inc., _Supporting the DCT Filters in PostScript Level 2_, Adobe Developer Support Technical Note #5116. 1992. http://www.adobe.com/supportservice/devrelations/PDFS/TN/5116.PS2_DCT.PDF [Description of the DCTDecode filter parameters.] Adobe Systems Inc., _Open Prepress Interface (OPI) Specification - Version 2.0_, Adobe Developer Support Technical Note #5660. 2000. http://partners.adobe.com/asn/developer/PDFS/TN/5660.OPI_2.0.pdf Adobe Systems Inc., CMap files. ftp://ftp.oreilly.com/pub/examples/nutshell/cjkv/adobe/ [The actual CMap files for the 16-bit CJK encodings.] Adobe Systems Inc., Unicode glyph lists. http://partners.adobe.com/asn/developer/type/unicodegn.html http://partners.adobe.com/asn/developer/type/glyphlist.txt http://partners.adobe.com/asn/developer/type/corporateuse.txt http://partners.adobe.com/asn/developer/type/zapfdingbats.txt [Mappings between character names to Unicode.] Adobe Systems Inc., OpenType Specification v. 1.4. http://partners.adobe.com/public/developer/opentype/index_spec.html [The OpenType font format spec.] Aldus Corp., _OPI: Open Prepress Interface Specification 1.3_. 1993. http://partners.adobe.com/asn/developer/PDFS/TN/OPI_13.pdf Anonymous, RC4 source code. ftp://ftp.ox.ac.uk/pub/crypto/misc/rc4.tar.gz ftp://idea.sec.dsi.unimi.it/pub/crypt/code/rc4.tar.gz [This is the algorithm used to encrypt PDF files.] T. Boutell, et al., "PNG (Portable Network Graphics) Specification, Version 1.0". RFC 2083. [PDF uses the PNG filter algorithms.] CCITT, "Information Technology - Digital Compression and Coding of Continuous-tone Still Images - Requirements and Guidelines", CCITT Recommendation T.81. http://www.w3.org/Graphics/JPEG/ [The official JPEG spec.] A. Chernov, "Registration of a Cyrillic Character Set". RFC 1489. [Documentation for the KOI8-R Cyrillic encoding.] Roman Czyborra, "The ISO 8859 Alphabet Soup". http://czyborra.com/charsets/iso8859.html [Documentation on the various ISO 859 encodings.] L. Peter Deutsch, "ZLIB Compressed Data Format Specification version 3.3". RFC 1950. [Information on the general format used in FlateDecode streams.] L. Peter Deutsch, "DEFLATE Compressed Data Format Specification version 1.3". RFC 1951. [The definition of the compression algorithm used in FlateDecode streams.] Morris Dworkin, "Recommendation for Block Cipher Modes of Operation", National Institute of Standards, NIST Special Publication 800-38A, 2001. [The cipher block chaining (CBC) mode used with AES in PDF files.] Federal Information Processing Standards Publication 197 (FIPS PUBS 197), "Advanced Encryption Standard (AES)", November 26, 2001. [AES encryption, used in PDF 1.6.] Jim Flowers, "X Logical Font Description Conventions", Version 1.5, X Consortium Standard, X Version 11, Release 6.1. ftp://ftp.x.org/pub/R6.1/xc/doc/hardcopy/XLFD/xlfd.PS.Z [The official specification of X font descriptors, including font transformation matrices.] Foley, van Dam, Feiner, and Hughes, _Computer Graphics: Principles and Practice_, 2nd ed. Addison-Wesley, 1990, ISBN 0-201-12110-7. [Colorspace conversion functions, Bezier spline math.] Robert L. Hummel, _Programmer's Technical Reference: Data and Fax Communications_. Ziff-Davis Press, 1993, ISBN 1-56276-077-7. [CCITT Group 3 and 4 fax decoding.] ISO/IEC, _Information technology -- Lossy/lossless coding of bi-level images_. ISO/IEC 14492, First edition (2001-12-15). http://webstore.ansi.org/ [The official JBIG2 standard. The final draft of this spec is available from http://www.jpeg.org/jbighomepage.html.] ISO/IEC, _Information technology -- JPEG 2000 image coding system -- Part 1: Core coding system_. ISO/IEC 15444-1, First edition (2000-12-15). http://webstore.ansi.org/ [The official JPEG 2000 standard. The final committee draft of this spec is available from http://www.jpeg.org/JPEG2000.html, but there were changes made to the bitstream format between that draft and the published spec.] ITU, "Standardization of Group 3 facsimile terminals for document transmission", ITU-T Recommendation T.4, 1999. ITU, "Facsimile coding schemes and coding control functions for Group 4 facsimile apparatus", ITU-T Recommendation T.6, 1993. http://www.itu.int/ [The official Group 3 and 4 fax standards - used by the CCITTFaxDecode stream, as well as the JBIG2Decode stream.] B. Kaliski, "PKCS #5: Password-Based Cryptography Specification, Version 2.0". RFC 2898. [Defines the padding scheme used with AES encryption in PDF files.] Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz, "Practical Fast 1-D DCT Algorithms with 11 Multiplications". IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989, 988-991. [The fast IDCT algorithm used in the DCTDecode filter.] Microsoft, _TrueType 1.0 Font Files_, rev. 1.66. 1995. http://www.microsoft.com/typography/tt/tt.htm [The TrueType font spec (in MS Word format, naturally).] V. Ostromoukhov, R.D. Hersch, "Stochastic Clustered-Dot Dithering", Conf. Color Imaging: Device-Independent Color, Color Hardcopy, and Graphic Arts IV, 1999, SPIE Vol. 3648, 496-505. http://diwww.epfl.ch/w3lsp/publications/colour/scd.html [The stochastic dithering algorithm used in Xpdf.] P. Peterlin, "ISO 8859-2 (Latin 2) Resources". http://sizif.mf.uni-lj.si/linux/cee/iso8859-2.html [This is a web page with all sorts of useful Latin-2 character set and font information.] Charles Poynton, "Color FAQ". http://www.inforamp.net/~poynton/ColorFAQ.html [The mapping from the CIE 1931 (XYZ) color space to RGB.] R. Rivest, "The MD5 Message-Digest Algorithm". RFC 1321. [MD5 is used in PDF document encryption.] Thai Industrial Standard, "Standard for Thai Character Codes for Computers", TIS-620-2533 (1990). http://www.nectec.or.th/it-standards/std620/std620.htm [The TIS-620 Thai encoding.] Unicode Consortium, "Unicode Home Page". http://www.unicode.org/ [Online copy of the Unicode spec.] W3C Recommendation, "PNG (Portable Network Graphics) Specification Version 1.0". http://www.w3.org/Graphics/PNG/ [Defines the PNG image predictor.] Gregory K. Wallace, "The JPEG Still Picture Compression Standard". ftp://ftp.uu.net/graphics/jpeg/wallace.ps.gz [Good description of the JPEG standard. Also published in CACM, April 1991, and submitted to IEEE Transactions on Consumer Electronics.] F. Yergeau, "UTF-8, a transformation format of ISO 10646". RFC 2279. [A commonly used Unicode encoding.] poppler-24.02.0/README.contributors000066400000000000000000000042201455701731300167600ustar00rootroot00000000000000If you want to become or are a poppler contributor, this is a README for you, keep reading! Licensing --------- Only send patches to poppler if you agree to license (or relicense) them under GPLv2 and later (or something more permissive that can be "upgraded" to GPLv2 and later). If you do not agree to this license, please explain the problem / bug and how you would solve it in words instead of code. By default all patches attached to the gitlab instance or sent to the mailing list will be assumed to agree with the licensing expressed here. 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 Do not hesitate to drop by talk to people there. clang-format ------------ We introduced clang-format mandatory usage in July 2020. If you want git blame to ignore the revision in which we did the mass change you can do git config blame.ignoreRevsFile .git-blame-ignore-revs on your clone 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 15 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 to help and they will reformat your commits :) Merge requests -------------- When creating a new merge request on gitlab make sure it has a clear title and the description includes any extra details that might be helpful for the reviewer, such as what the aim of the change is and decisions made during implementation. Also, check "Allow commits from members who can merge to the target branch" as that enables rebase on landing. See the gitlab docs for details: https://docs.gitlab.com/ee/user/project/merge_requests/allow_collaboration.html And keep hacking on poppler! poppler-24.02.0/README.md000066400000000000000000000111361455701731300146270ustar00rootroot00000000000000Poppler, a PDF rendering library ================================ This is Poppler, a library for rendering PDF files, and examining or modifying their structure. Poppler originally came from the XPDF sources; please see the file [README-XPDF](README-XPDF) for the original xpdf-3.03 README. Note that **Poppler is licensed under the GPL**, not the LGPL, so programs which call Poppler must be licensed under the GPL as well. See the section [History and GPL licensing](#history-and-gpl-licensing) for more information. # Source code Poppler's source code is maintained as a Git repository in [`gitlab.freedesktop.org`][gitlab]. You can fork that repository and submit merge requests. [gitlab]: https://gitlab.freedesktop.org/poppler/poppler # Reporting bugs Please report bugs at https://gitlab.freedesktop.org/poppler/poppler/issues If you want to report a rendering or parsing bug, or a missing PDF feature, please provide an example PDF file as an attachment to your bug report. It really helps if you can minimize the PDF to only the items required to reproduce the bug or the missing feature, but it is not absolutely required. **Please be careful** of publishing PDF files that you don't want other people to see, or files whose copyright does not allow redistribution; the bug tracker is a public resource and attachments are visible to everyone. # Security Poppler is highly sensitive to security bugs, since it deals mainly with untrusted files downloaded from the Internet. If you find a crash in Poppler, or if a tool like Valgrind/asan/ubsan/msan detect a problem, please report a bug at https://gitlab.freedesktop.org/poppler/poppler/issues # Stable and unstable APIs Poppler provides stable, public APIs for its various front-ends, and an unstable API for Poppler's own internal use. The following directories in Poppler's source tree have the **stable APIs**: * [cpp](cpp) - Stable C++ API for examining the structure of a PDF file and rendering it to a raster image. * [glib](glib) - Stable C API with Glib/GObject idioms, to examine the structure of a PDF file, and to render its pages to [Cairo] contexts. * [qt5](qt5) - Stable C++ API with [Qt5] idioms, to examine the structure of a PDF file, and to render its pages to `QPainter` or `QImage` objects. **WARNING:** Poppler also provides direct access to its internals, since various tools historically use the C++ header files that came from XPDF and which became the basis for Poppler. * [poppler](poppler) - **UNSTABLE, INTERNAL C++ API** to operate directly on Poppler's internal representation of PDF files. *If you use this API, you are on your own*. This API may change at any time, even among minor versions of Poppler! [Cairo]: https://www.cairographics.org/ [Qt5]: https://www.qt.io/ # History and GPL licensing Poppler is a fork of the xpdf PDF viewer developed by Derek Noonburg of Glyph and Cog, LLC. The purpose of forking xpdf is twofold. First, we want to provide PDF rendering functionality as a shared library, to centralize the maintenance effort. Today a number of applications incorporate the xpdf code base, and whenever a security issue is discovered, all these applications exchange patches and put out new releases. In turn, all distributions must package and release new version of these xpdf based viewers. It's safe to say that there's a lot of duplicated effort with the current situation. Even if poppler in the short term introduces yet another xpdf derived code base to the world, we hope that over time these applications will adopt poppler. After all, we only need one application to use poppler to break even. Second, we would like to move libpoppler forward in a number of areas that don't fit within the goals of xpdf. By design, xpdf depends on very few libraries and runs a wide range of X based platforms. This is a strong feature and reasonable design goal. However, with poppler we would like to replace parts of xpdf that are now available as standard components of modern Unix desktop environments. One such example is fontconfig, which solves the problem of matching and locating fonts on the system, in a standardized and well understood way. Another example is cairo, which provides high quality 2D rendering. Please note that xpdf, and thus poppler, is licensed under the GPL, not the LGPL. Consequently, any application using poppler must also be licensed under the GPL. If you want to incorporate Xpdf based PDF rendering in a closed source product, please contact Glyph & Cog (www.glyphandcog.com) for commercial licensing options. Note that this only allows you to use xpdf in a closed source product, not poppler itself. poppler-24.02.0/_clang-format000066400000000000000000000050341455701731300160040ustar00rootroot00000000000000# Copyright (C) 2016 Olivier Goffart # # You may use this file under the terms of the 3-clause BSD license. # See the file LICENSE from this package for details. --- BasedOnStyle: WebKit Standard: Cpp11 ColumnLimit: 240 # How much weight do extra characters after the line length limit have. # PenaltyExcessCharacter: 4 # Disable reflow of qdoc comments: indentation rules are different. # Translation comments are also excluded. CommentPragmas: "^!|^:" # We want a space between the type and the star for pointer types. PointerBindsToType: false # We use template< without space. SpaceAfterTemplateKeyword: false # We want to break before the operators, but not before a '='. BreakBeforeBinaryOperators: NonAssignment # Braces are usually attached, but not after functions or class declarations. BreakBeforeBraces: Custom BraceWrapping: AfterClass: true AfterControlStatement: false AfterEnum: true AfterFunction: true AfterNamespace: false AfterObjCDeclaration: false AfterStruct: true AfterUnion: false BeforeCatch: false BeforeElse: false IndentBraces: false # When constructor initializers do not fit on one line, put them each on a new line. ConstructorInitializerAllOnOneLineOrOnePerLine: true # Indent initializers by 4 spaces ConstructorInitializerIndentWidth: 4 # Indent width for line continuations. ContinuationIndentWidth: 8 # No indentation for namespaces. NamespaceIndentation: None # Allow indentation for preprocessing directives (if/ifdef/endif). https://reviews.llvm.org/rL312125 IndentPPDirectives: AfterHash # Horizontally align arguments after an open bracket. # The coding style does not specify the following, but this is what gives # results closest to the existing code. AlignAfterOpenBracket: true AlwaysBreakTemplateDeclarations: true # Ideally we should also allow less short function in a single line, but # clang-format does not handle that. AllowShortFunctionsOnASingleLine: Inline # The coding style specifies some include order categories, but also tells to # separate categories with an empty line. It does not specify the order within # the categories. Since the SortInclude feature of clang-format does not # re-order includes separated by empty lines, the feature is not used. SortIncludes: false # macros for which the opening brace stays attached. ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, forever, Q_FOREVER, QBENCHMARK, QBENCHMARK_ONCE ] # Break constructor initializers before the colon and after the commas. BreakConstructorInitializers: BeforeColon poppler-24.02.0/cmake/000077500000000000000000000000001455701731300144265ustar00rootroot00000000000000poppler-24.02.0/cmake/modules/000077500000000000000000000000001455701731300160765ustar00rootroot00000000000000poppler-24.02.0/cmake/modules/COPYING-CMAKE-SCRIPTS000066400000000000000000000024571455701731300211040ustar00rootroot00000000000000Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. poppler-24.02.0/cmake/modules/CheckFileOffsetBits.c000066400000000000000000000004501455701731300220470ustar00rootroot00000000000000#include #define KB ((off_t)1024) #define MB ((off_t)1024 * KB) #define GB ((off_t)1024 * MB) #define TB ((off_t)1024 * GB) int t2[(((64 * GB - 1) % 671088649) == 268434537) && (((TB - (64 * GB - 1) + 255) % 1792151290) == 305159546) ? 1 : -1]; int main() { ; return 0; } poppler-24.02.0/cmake/modules/CheckFileOffsetBits.cmake000066400000000000000000000036751455701731300227210ustar00rootroot00000000000000# - Check if _FILE_OFFSET_BITS macro needed for large files # CHECK_FILE_OFFSET_BITS () # # The following variables may be set before calling this macro to # modify the way the check is run: # # CMAKE_REQUIRED_FLAGS = string of compile command line flags # CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) # CMAKE_REQUIRED_INCLUDES = list of include directories # Copyright (c) 2009, Michihiro NAKAJIMA # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. #INCLUDE(CheckCXXSourceCompiles) GET_FILENAME_COMPONENT(_selfdir_CheckFileOffsetBits "${CMAKE_CURRENT_LIST_FILE}" PATH) MACRO (CHECK_FILE_OFFSET_BITS) IF(NOT DEFINED _FILE_OFFSET_BITS) MESSAGE(STATUS "Checking _FILE_OFFSET_BITS for large files") TRY_COMPILE(__WITHOUT_FILE_OFFSET_BITS_64 ${CMAKE_CURRENT_BINARY_DIR} ${_selfdir_CheckFileOffsetBits}/CheckFileOffsetBits.c COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}) IF(NOT __WITHOUT_FILE_OFFSET_BITS_64) TRY_COMPILE(__WITH_FILE_OFFSET_BITS_64 ${CMAKE_CURRENT_BINARY_DIR} ${_selfdir_CheckFileOffsetBits}/CheckFileOffsetBits.c COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} -D_FILE_OFFSET_BITS=64) ENDIF(NOT __WITHOUT_FILE_OFFSET_BITS_64) IF(NOT __WITHOUT_FILE_OFFSET_BITS_64 AND __WITH_FILE_OFFSET_BITS_64) SET(_FILE_OFFSET_BITS 64 CACHE INTERNAL "_FILE_OFFSET_BITS macro needed for large files") MESSAGE(STATUS "Checking _FILE_OFFSET_BITS for large files - needed") ELSE(NOT __WITHOUT_FILE_OFFSET_BITS_64 AND __WITH_FILE_OFFSET_BITS_64) SET(_FILE_OFFSET_BITS "" CACHE INTERNAL "_FILE_OFFSET_BITS macro needed for large files") MESSAGE(STATUS "Checking _FILE_OFFSET_BITS for large files - not needed") ENDIF(NOT __WITHOUT_FILE_OFFSET_BITS_64 AND __WITH_FILE_OFFSET_BITS_64) ENDIF(NOT DEFINED _FILE_OFFSET_BITS) ENDMACRO (CHECK_FILE_OFFSET_BITS) poppler-24.02.0/cmake/modules/FindCairo.cmake000066400000000000000000000033131455701731300207360ustar00rootroot00000000000000# - try to find Cairo # Once done this will define # # CAIRO_FOUND - system has Cairo # CAIRO_CFLAGS - the Cairo CFlags # CAIRO_INCLUDE_DIRS - the Cairo include directories # CAIRO_LIBRARIES - Link these to use Cairo # # Copyright (C) 2007, 2010, Pino Toscano, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. if(CAIRO_INCLUDE_DIRS AND CAIRO_LIBRARIES) # in cache already set(CAIRO_FOUND TRUE) else(CAIRO_INCLUDE_DIRS AND CAIRO_LIBRARIES) 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 REQUIRED) if(Cairo_FIND_VERSION_COUNT GREATER 0) set(_cairo_version_cmp ">=${Cairo_FIND_VERSION}") endif(Cairo_FIND_VERSION_COUNT GREATER 0) pkg_check_modules(_pc_cairo cairo${_cairo_version_cmp}) if(_pc_cairo_FOUND) set(CAIRO_FOUND TRUE) endif(_pc_cairo_FOUND) else(NOT WIN32) # assume so, for now set(CAIRO_FOUND TRUE) endif(NOT WIN32) if(CAIRO_FOUND) # set it back as false set(CAIRO_FOUND FALSE) find_library(CAIRO_LIBRARY cairo HINTS ${_pc_cairo_LIBRARY_DIRS} ) set(CAIRO_LIBRARIES "${CAIRO_LIBRARY}") find_path(CAIRO_INCLUDE_DIR cairo.h HINTS ${_pc_cairo_INCLUDE_DIRS} PATH_SUFFIXES cairo ) 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) endif(CAIRO_FOUND) endif(CAIRO_INCLUDE_DIRS AND CAIRO_LIBRARIES) mark_as_advanced( CAIRO_CFLAGS CAIRO_INCLUDE_DIRS CAIRO_LIBRARIES ) poppler-24.02.0/cmake/modules/FindGLIB.cmake000066400000000000000000000013231455701731300204150ustar00rootroot00000000000000# - try to find the GLIB libraries # Once done this will define # # GLIB_FOUND - system has GLib # GLIB2_CFLAGS - the GLib CFlags # GLIB2_LIBRARIES - Link these to use GLib # # Copyright 2008-2010 Pino Toscano, # Copyright 2013 Michael Weiser, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. include(FindPackageHandleStandardArgs) find_package(PkgConfig REQUIRED) pkg_check_modules(GLIB2 IMPORTED_TARGET "glib-2.0>=${GLIB_REQUIRED}" "gobject-2.0>=${GLIB_REQUIRED}" "gio-2.0>=${GLIB_REQUIRED}") find_package_handle_standard_args(GLIB DEFAULT_MSG GLIB2_LIBRARIES GLIB2_CFLAGS) poppler-24.02.0/cmake/modules/FindGObjectIntrospection.cmake000066400000000000000000000037651455701731300240120ustar00rootroot00000000000000# - try to find gobject-introspection # # Once done this will define # # INTROSPECTION_FOUND - system has gobject-introspection # INTROSPECTION_SCANNER - the gobject-introspection scanner, g-ir-scanner # INTROSPECTION_COMPILER - the gobject-introspection compiler, g-ir-compiler # INTROSPECTION_GENERATE - the gobject-introspection generate, g-ir-generate # INTROSPECTION_GIRDIR # INTROSPECTION_TYPELIBDIR # INTROSPECTION_CFLAGS # INTROSPECTION_LIBS # # Copyright (C) 2010, Pino Toscano, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. macro(_GIR_GET_PKGCONFIG_VAR _outvar _varname) execute_process( COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=${_varname} gobject-introspection-1.0 OUTPUT_VARIABLE _result RESULT_VARIABLE _null ) if (_null) else() string(REGEX REPLACE "[\r\n]" " " _result "${_result}") string(REGEX REPLACE " +$" "" _result "${_result}") separate_arguments(_result) set(${_outvar} ${_result} CACHE INTERNAL "") endif() endmacro(_GIR_GET_PKGCONFIG_VAR) find_package(PkgConfig) if(PKG_CONFIG_FOUND) if(PACKAGE_FIND_VERSION_COUNT GREATER 0) set(_gir_version_cmp ">=${PACKAGE_FIND_VERSION}") endif() pkg_check_modules(_pc_gir gobject-introspection-1.0${_gir_version_cmp}) if(_pc_gir_FOUND) set(INTROSPECTION_FOUND TRUE) _gir_get_pkgconfig_var(INTROSPECTION_SCANNER "g_ir_scanner") _gir_get_pkgconfig_var(INTROSPECTION_COMPILER "g_ir_compiler") _gir_get_pkgconfig_var(INTROSPECTION_GENERATE "g_ir_generate") _gir_get_pkgconfig_var(INTROSPECTION_GIRDIR "girdir") _gir_get_pkgconfig_var(INTROSPECTION_TYPELIBDIR "typelibdir") set(INTROSPECTION_CFLAGS "${_pc_gir_CFLAGS}") set(INTROSPECTION_LIBS "${_pc_gir_LIBS}") endif() endif() mark_as_advanced( INTROSPECTION_SCANNER INTROSPECTION_COMPILER INTROSPECTION_GENERATE INTROSPECTION_GIRDIR INTROSPECTION_TYPELIBDIR INTROSPECTION_CFLAGS INTROSPECTION_LIBS ) poppler-24.02.0/cmake/modules/FindGTK.cmake000066400000000000000000000012671455701731300203340ustar00rootroot00000000000000# - try to find GTK libraries # Once done this will define # # GTK_FOUND - system has GTK # GTK3_CFLAGS - the GTK CFlags # GTK3_LIBRARIES - Link these to use GTK # # Copyright 2008-2010 Pino Toscano, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # NOTE: As of cmake v3.18, built-in FindGTK is *only* valid for GTK1 include(FindPackageHandleStandardArgs) find_package(PkgConfig REQUIRED) pkg_check_modules(GTK3 IMPORTED_TARGET "gtk+-3.0>=${GTK_REQUIRED}" "gdk-pixbuf-2.0>=${GDK_PIXBUF_REQUIRED}") find_package_handle_standard_args(GTK DEFAULT_MSG GTK3_LIBRARIES GTK3_CFLAGS) poppler-24.02.0/cmake/modules/FindLCMS2.cmake000066400000000000000000000047571455701731300205360ustar00rootroot00000000000000# - Find LCMS2 # Find the LCMS2 includes and library # This module defines # LCMS2_INCLUDE_DIR, where to find lcms.h # LCMS2_LIBRARIES, the libraries needed to use LCMS2. # LCMS2_VERSION, The value of LCMS_VERSION defined in lcms.h # LCMS2_FOUND, If false, do not try to use LCMS2. # Copyright (c) 2008, Adrian Page, # Copyright (c) 2009, Cyrille Berger, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # use pkg-config to get the directories and then use these values # in the FIND_PATH() and FIND_LIBRARY() calls if(NOT WIN32) find_package(PkgConfig) pkg_check_modules(PC_LCMS2 lcms2) set(LCMS2_DEFINITIONS ${PC_LCMS2_CFLAGS_OTHER}) endif(NOT WIN32) find_path(LCMS2_INCLUDE_DIR lcms2.h PATHS ${PC_LCMS2_INCLUDEDIR} ${PC_LCMS2_INCLUDE_DIRS} PATH_SUFFIXES lcms2 liblcms2 ) find_library(LCMS2_LIBRARIES NAMES lcms2 liblcms2 lcms-2 liblcms-2 PATHS ${PC_LCMS2_LIBDIR} ${PC_LCMS2_LIBRARY_DIRS} PATH_SUFFIXES lcms2 ) if(LCMS2_INCLUDE_DIR AND LCMS2_LIBRARIES) set(LCMS2_FOUND TRUE) else(LCMS2_INCLUDE_DIR AND LCMS2_LIBRARIES) set(LCMS2_FOUND FALSE) endif(LCMS2_INCLUDE_DIR AND LCMS2_LIBRARIES) if(LCMS2_FOUND) file(READ ${LCMS2_INCLUDE_DIR}/lcms2.h LCMS2_VERSION_CONTENT) string(REGEX MATCH "#define LCMS_VERSION[ ]*[0-9]*\n" LCMS2_VERSION_MATCH ${LCMS2_VERSION_CONTENT}) if(LCMS2_VERSION_MATCH) string(REGEX REPLACE "#define LCMS_VERSION[ ]*([0-9]*)\n" "\\1" LCMS2_VERSION ${LCMS2_VERSION_MATCH}) if(NOT LCMS2_FIND_QUIETLY) string(SUBSTRING ${LCMS2_VERSION} 0 1 LCMS2_MAJOR_VERSION) string(SUBSTRING ${LCMS2_VERSION} 1 2 LCMS2_MINOR_VERSION) message(STATUS "Found lcms version ${LCMS2_MAJOR_VERSION}.${LCMS2_MINOR_VERSION}, ${LCMS2_LIBRARIES}") endif(NOT LCMS2_FIND_QUIETLY) else(LCMS2_VERSION_MATCH) if(NOT LCMS2_FIND_QUIETLY) message(STATUS "Found lcms2 but failed to find version ${LCMS2_LIBRARIES}") endif(NOT LCMS2_FIND_QUIETLY) set(LCMS2_VERSION NOTFOUND) endif(LCMS2_VERSION_MATCH) else(LCMS2_FOUND) if(NOT LCMS2_FIND_QUIETLY) if(LCMS2_FIND_REQUIRED) message(FATAL_ERROR "Required package lcms2 NOT found") else(LCMS2_FIND_REQUIRED) message(STATUS "lcms2 NOT found") endif(LCMS2_FIND_REQUIRED) endif(NOT LCMS2_FIND_QUIETLY) endif(LCMS2_FOUND) mark_as_advanced(LCMS2_INCLUDE_DIR LCMS2_LIBRARIES LCMS2_VERSION) poppler-24.02.0/cmake/modules/FindNSS3.cmake000066400000000000000000000012221455701731300204240ustar00rootroot00000000000000# - try to find NSS3 libraries # Once done this will define # # NSS3_FOUND - system has NSS3 # PkgConfig::NSS3 - Use this in target_link_libraries to bring both includes and link libraries # # Copyright 2015 André Guerreiro, # Copyright 2022 Albert Astals Cid, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. include(FindPackageHandleStandardArgs) find_package(PkgConfig REQUIRED) pkg_check_modules(NSS3 IMPORTED_TARGET "nss>=3.49") find_package_handle_standard_args(NSS3 DEFAULT_MSG NSS3_LIBRARIES NSS3_CFLAGS) poppler-24.02.0/cmake/modules/GObjectIntrospectionMacros.cmake000066400000000000000000000067601455701731300243540ustar00rootroot00000000000000# Copyright (C) 2010, Pino Toscano, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. macro(_gir_list_prefix _outvar _listvar _prefix) set(${_outvar}) foreach(_item IN LISTS ${_listvar}) list(APPEND ${_outvar} ${_prefix}${_item}) endforeach() endmacro(_gir_list_prefix) macro(gir_add_introspections introspections_girs) set(_gir_girs) set(_gir_typelibs) foreach(gir IN LISTS ${introspections_girs}) set(_gir_name "${gir}") ## Transform the gir filename to something which can reference through a variable ## without automake/make complaining, eg Gtk-2.0.gir -> Gtk_2_0_gir string(REPLACE "-" "_" _gir_name "${_gir_name}") string(REPLACE "." "_" _gir_name "${_gir_name}") # Namespace and Version is either fetched from the gir filename # or the _NAMESPACE/_VERSION variable combo set(_gir_namespace "${${_gir_name}_NAMESPACE}") if (_gir_namespace STREQUAL "") string(REGEX REPLACE "([^-]+)-.*" "\\1" _gir_namespace "${gir}") endif () set(_gir_version "${${_gir_name}_VERSION}") if (_gir_version STREQUAL "") string(REGEX REPLACE ".*-([^-]+).gir" "\\1" _gir_version "${gir}") endif () # _PROGRAM is an optional variable which needs it's own --program argument set(_gir_program "${${_gir_name}_PROGRAM}") if (NOT _gir_program STREQUAL "") set(_gir_program "--program=${_gir_program}") endif () # Variables which provides a list of things _gir_list_prefix(_gir_libraries ${_gir_name}_LIBS "--library=") _gir_list_prefix(_gir_packages ${_gir_name}_PACKAGES "--pkg=") _gir_list_prefix(_gir_includes ${_gir_name}_INCLUDES "--include=") _gir_list_prefix(_gir_export_packages ${_gir_name}_EXPORT_PACKAGES "--pkg-export=") # Reuse the LIBTOOL variable from by automake if it's set set(_gir_libtool "--no-libtool") add_custom_command( COMMAND ${INTROSPECTION_SCANNER} ${INTROSPECTION_SCANNER_ARGS} --namespace=${_gir_namespace} --nsversion=${_gir_version} ${_gir_libtool} ${_gir_program} ${_gir_libraries} ${_gir_packages} ${_gir_includes} ${_gir_export_packages} ${${_gir_name}_SCANNERFLAGS} ${${_gir_name}_CFLAGS} ${${_gir_name}_FILES} --output ${CMAKE_CURRENT_BINARY_DIR}/${gir} DEPENDS ${${_gir_name}_FILES} ${${_gir_name}_LIBS} OUTPUT ${gir} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} VERBATIM ) list(APPEND _gir_girs ${CMAKE_CURRENT_BINARY_DIR}/${gir}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${gir} DESTINATION ${CMAKE_INSTALL_DATADIR}/gir-1.0) string(REPLACE ".gir" ".typelib" _typelib "${gir}") add_custom_command( COMMAND ${INTROSPECTION_COMPILER} ${INTROSPECTION_COMPILER_ARGS} --includedir=. ${CMAKE_CURRENT_BINARY_DIR}/${gir} -o ${CMAKE_CURRENT_BINARY_DIR}/${_typelib} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${gir} OUTPUT ${_typelib} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) list(APPEND _gir_typelibs ${CMAKE_CURRENT_BINARY_DIR}/${_typelib}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${_typelib} DESTINATION ${CMAKE_INSTALL_LIBDIR}/girepository-1.0) endforeach() add_custom_target(gir-typelibs ALL DEPENDS ${_gir_typelibs}) endmacro(gir_add_introspections) poppler-24.02.0/cmake/modules/MacroOptionalFindPackage.cmake000066400000000000000000000036471455701731300237360ustar00rootroot00000000000000# - MACRO_OPTIONAL_FIND_PACKAGE() combines FIND_PACKAGE() with an OPTION() # MACRO_OPTIONAL_FIND_PACKAGE( [QUIT] ) # This macro is a combination of OPTION() and FIND_PACKAGE(), it # works like FIND_PACKAGE(), but additionally it automatically creates # an option name WITH_, which can be disabled via the cmake GUI. # or via -DWITH_=OFF # The standard _FOUND variables can be used in the same way # as when using the normal FIND_PACKAGE() # Copyright (c) 2006-2010 Alexander Neundorf, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # This is just a helper macro to set a bunch of variables empty. # We don't know whether the package uses UPPERCASENAME or CamelCaseName, so we try both: macro(_MOFP_SET_EMPTY_IF_DEFINED _name _var) if(DEFINED ${_name}_${_var}) set(${_name}_${_var} "") endif(DEFINED ${_name}_${_var}) string(TOUPPER ${_name} _nameUpper) if(DEFINED ${_nameUpper}_${_var}) set(${_nameUpper}_${_var} "") endif(DEFINED ${_nameUpper}_${_var}) endmacro(_MOFP_SET_EMPTY_IF_DEFINED _package _var) macro (MACRO_OPTIONAL_FIND_PACKAGE _name ) option(WITH_${_name} "Search for ${_name} package" ON) if (WITH_${_name}) find_package(${_name} ${ARGN}) else (WITH_${_name}) string(TOUPPER ${_name} _nameUpper) set(${_name}_FOUND FALSE) set(${_nameUpper}_FOUND FALSE) _mofp_set_empty_if_defined(${_name} INCLUDE_DIRS) _mofp_set_empty_if_defined(${_name} INCLUDE_DIR) _mofp_set_empty_if_defined(${_name} INCLUDES) _mofp_set_empty_if_defined(${_name} LIBRARY) _mofp_set_empty_if_defined(${_name} LIBRARIES) _mofp_set_empty_if_defined(${_name} LIBS) _mofp_set_empty_if_defined(${_name} FLAGS) _mofp_set_empty_if_defined(${_name} DEFINITIONS) endif (WITH_${_name}) endmacro (MACRO_OPTIONAL_FIND_PACKAGE) poppler-24.02.0/cmake/modules/PopplerDefaults.cmake000066400000000000000000000003621455701731300222120ustar00rootroot00000000000000# enable the testing facilities enable_testing() # put the include directories of the sources before other include paths # (eg, system includes) set(CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE ON) # colored output set(CMAKE_COLOR_MAKEFILE ON) poppler-24.02.0/cmake/modules/PopplerMacros.cmake000066400000000000000000000206011455701731300216650ustar00rootroot00000000000000# Copyright 2008 Pino Toscano, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. macro(POPPLER_ADD_TEST exe build_flag) set(build_test ${${build_flag}}) # Omit the disabled test binaries from the "all" target if(NOT build_test) set(_add_executable_param ${_add_executable_param} EXCLUDE_FROM_ALL) endif(NOT build_test) add_executable(${exe} ${_add_executable_param} ${ARGN}) # if the tests are EXCLUDE_FROM_ALL, add a target "buildtests" to build all tests # Don't try to use custom targets if building with Visual Studio if(NOT build_test AND NOT MSVC_IDE) get_property(_buildtestsAdded GLOBAL PROPERTY BUILDTESTS_ADDED) if(NOT _buildtestsAdded) add_custom_target(buildtests) set_property(GLOBAL PROPERTY BUILDTESTS_ADDED TRUE) endif(NOT _buildtestsAdded) add_dependencies(buildtests ${exe}) endif(NOT build_test AND NOT MSVC_IDE) endmacro(POPPLER_ADD_TEST) macro(POPPLER_CREATE_INSTALL_PKGCONFIG generated_file install_location) configure_file(${generated_file}.cmake ${CMAKE_CURRENT_BINARY_DIR}/${generated_file} @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${generated_file} DESTINATION ${install_location}) endmacro(POPPLER_CREATE_INSTALL_PKGCONFIG) macro(SHOW_END_MESSAGE what value) string(LENGTH ${what} length_what) math(EXPR left_char "20 - ${length_what}") set(blanks) foreach(_i RANGE 1 ${left_char}) set(blanks "${blanks} ") endforeach(_i) message(" ${what}:${blanks} ${value}") endmacro(SHOW_END_MESSAGE) macro(SHOW_END_MESSAGE_YESNO what enabled) if(${enabled}) set(enabled_string "yes") else(${enabled}) set(enabled_string "no") endif(${enabled}) show_end_message("${what}" "${enabled_string}") endmacro(SHOW_END_MESSAGE_YESNO) macro(POPPLER_CHECK_LINK_FLAG flag var) set(_save_CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES}") include(CheckCXXSourceCompiles) set(CMAKE_REQUIRED_LIBRARIES "${flag}") check_cxx_source_compiles("int main() { return 0; }" ${var}) set(CMAKE_REQUIRED_LIBRARIES "${_save_CMAKE_REQUIRED_LIBRARIES}") endmacro(POPPLER_CHECK_LINK_FLAG) set(CMAKE_SYSTEM_INCLUDE_PATH ${CMAKE_SYSTEM_INCLUDE_PATH} "${CMAKE_INSTALL_PREFIX}/include" ) set(CMAKE_SYSTEM_PROGRAM_PATH ${CMAKE_SYSTEM_PROGRAM_PATH} "${CMAKE_INSTALL_PREFIX}/bin" ) set(CMAKE_SYSTEM_LIBRARY_PATH ${CMAKE_SYSTEM_LIBRARY_PATH} "${CMAKE_INSTALL_PREFIX}/lib" ) # under Windows dlls may be also installed in bin/ if(WIN32) set(CMAKE_SYSTEM_LIBRARY_PATH ${CMAKE_SYSTEM_LIBRARY_PATH} "${CMAKE_INSTALL_PREFIX}/bin" ) endif(WIN32) if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set(CMAKE_BUILD_TYPE RelWithDebInfo) endif(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) string(TOUPPER "${CMAKE_BUILD_TYPE}" _CMAKE_BUILD_TYPE_UPPER) set(_known_build_types RELWITHDEBINFO;RELEASE;DEBUG;DEBUGFULL;PROFILE) # We override CMAKE_CXX_FLAGS_${_CMAKE_BUILD_TYPE_UPPER} below. If the user # selects a CMAKE_BUILD_TYPE that is not handled by the logic below, we will # end up dropping the previous flags (e.g. those set in a cross-compilation # CMake toolchain file). To avoid surprising compilation errors, we emit an # error in that case, so that the user can handle the passed CMAKE_BUILD_TYPE # in the compiler flags logic below. if (NOT "${_CMAKE_BUILD_TYPE_UPPER}" IN_LIST _known_build_types) message(FATAL_ERROR "Unsupported CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") endif() set(_save_cflags "${CMAKE_C_FLAGS}") set(_save_cxxflags "${CMAKE_CXX_FLAGS}") if(CMAKE_COMPILER_IS_GNUCXX) # set the default compile warnings set(_warn "-Wall -Wextra -Wpedantic") set(_warn "${_warn} -Wno-unused-parameter") set(_warn "${_warn} -Wcast-align") set(_warn "${_warn} -Wformat-security") set(_warn "${_warn} -Wframe-larger-than=65536") set(_warn "${_warn} -Wlogical-op") set(_warn "${_warn} -Wmissing-format-attribute") set(_warn "${_warn} -Wnon-virtual-dtor") set(_warn "${_warn} -Woverloaded-virtual") set(_warn "${_warn} -Wmissing-declarations") set(_warn "${_warn} -Wundef") set(_warn "${_warn} -Wzero-as-null-pointer-constant") set(_warn "${_warn} -Wshadow") set(_warn "${_warn} -Wsuggest-override") # set extra warnings set(_warnx "${_warnx} -Wconversion") set(_warnx "${_warnx} -Wuseless-cast") set(DEFAULT_COMPILE_WARNINGS "${_warn}") set(DEFAULT_COMPILE_WARNINGS_EXTRA "${_warn} ${_warnx}") set(CMAKE_CXX_FLAGS "-fno-exceptions -fno-check-new -fno-common -fno-operator-names -D_DEFAULT_SOURCE") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g") set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG") set(CMAKE_CXX_FLAGS_DEBUG "-g -O2 -fno-reorder-blocks -fno-schedule-insns -fno-inline") set(CMAKE_CXX_FLAGS_DEBUGFULL "-g3 -fno-inline") set(CMAKE_CXX_FLAGS_PROFILE "-g3 -fno-inline -ftest-coverage -fprofile-arcs") set(CMAKE_C_FLAGS "-std=c99 -D_DEFAULT_SOURCE") set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g") set(CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG") set(CMAKE_C_FLAGS_DEBUG "-g -O2 -fno-reorder-blocks -fno-schedule-insns -fno-inline") set(CMAKE_C_FLAGS_DEBUGFULL "-g3 -fno-inline") set(CMAKE_C_FLAGS_PROFILE "-g3 -fno-inline -ftest-coverage -fprofile-arcs") poppler_check_link_flag("-Wl,--as-needed" GCC_HAS_AS_NEEDED) if(GCC_HAS_AS_NEEDED) set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--as-needed") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--as-needed") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed") endif(GCC_HAS_AS_NEEDED) set(_compiler_flags_changed 1) endif (CMAKE_COMPILER_IS_GNUCXX) if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") # set the default compile warnings set(_warn "-Wall -Wextra -Wpedantic") set(_warn "${_warn} -Wno-unused-parameter") set(_warn "${_warn} -Wcast-align") set(_warn "${_warn} -Wformat-security") set(_warn "${_warn} -Wframe-larger-than=65536") set(_warn "${_warn} -Wmissing-format-attribute") set(_warn "${_warn} -Wnon-virtual-dtor") set(_warn "${_warn} -Woverloaded-virtual") set(_warn "${_warn} -Wmissing-declarations") set(_warn "${_warn} -Wundef") set(_warn "${_warn} -Wzero-as-null-pointer-constant") set(_warn "${_warn} -Wshadow") set(_warn "${_warn} -Wweak-vtables") # set extra warnings set(_warnx "${_warnx} -Wconversion") set(DEFAULT_COMPILE_WARNINGS "${_warn}") set(DEFAULT_COMPILE_WARNINGS_EXTRA "${_warn} ${_warnx}") set(CMAKE_CXX_FLAGS "-fno-exceptions -fno-check-new -fno-common -D_DEFAULT_SOURCE") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g") set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG") # clang does not support -fno-reorder-blocks -fno-schedule-insns, so do not use -O2 set(CMAKE_CXX_FLAGS_DEBUG "-g") set(CMAKE_CXX_FLAGS_DEBUGFULL "-g3 -fno-inline") set(CMAKE_CXX_FLAGS_PROFILE "-g3 -fno-inline -ftest-coverage -fprofile-arcs") set(CMAKE_C_FLAGS "-std=c99 -D_DEFAULT_SOURCE") set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g") set(CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG") # clang does not support -fno-reorder-blocks -fno-schedule-insns, so do not use -O2 set(CMAKE_C_FLAGS_DEBUG "-g") set(CMAKE_C_FLAGS_DEBUGFULL "-g3 -fno-inline") set(CMAKE_C_FLAGS_PROFILE "-g3 -fno-inline -ftest-coverage -fprofile-arcs") set(_compiler_flags_changed 1) endif() if(CMAKE_C_COMPILER MATCHES "icc") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g") set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG") set(CMAKE_CXX_FLAGS_DEBUG "-O2 -g -0b0 -noalign") set(CMAKE_CXX_FLAGS_DEBUGFULL "-g -Ob0 -noalign") set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g") set(CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG") set(CMAKE_C_FLAGS_DEBUG "-O2 -g -Ob0 -noalign") set(CMAKE_C_FLAGS_DEBUGFULL "-g -Ob0 -noalign") set(_compiler_flags_changed 1) endif(CMAKE_C_COMPILER MATCHES "icc") if(_compiler_flags_changed) # Ensure that the previous CMAKE_{C,CXX}_FLAGS are included in the current configuration flags. foreach(_build_type ${_known_build_types}) set(CMAKE_CXX_FLAGS_${_build_type} "${CMAKE_CXX_FLAGS_${_build_type}} ${_save_cxxflags}") set(CMAKE_C_FLAGS_${_build_type} "${CMAKE_C_FLAGS_${_build_type}} ${_save_cflags}") endforeach() endif() poppler-24.02.0/config.h.cmake000066400000000000000000000131731455701731300160500ustar00rootroot00000000000000/* config.h. Generated from config.h.cmake by cmake. */ /* Build against libcurl. */ #cmakedefine ENABLE_LIBCURL 1 /* Use libjpeg instead of builtin jpeg decoder. */ #cmakedefine ENABLE_LIBJPEG 1 /* Use libopenjpeg instead of builtin jpeg2000 decoder. */ #cmakedefine ENABLE_LIBOPENJPEG 1 /* Build against libtiff. */ #cmakedefine ENABLE_LIBTIFF 1 /* Build against libpng. */ #cmakedefine ENABLE_LIBPNG 1 /* Do not hardcode the library location */ #cmakedefine ENABLE_RELOCATABLE 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 /* Signatures enabled */ #cmakedefine ENABLE_SIGNATURES 1 /* Default signature backend */ #cmakedefine DEFAULT_SIGNATURE_BACKEND "${DEFAULT_SIGNATURE_BACKEND}" /* Use cairo for rendering. */ #cmakedefine HAVE_CAIRO 1 /* Do we have any DCT decoder?. */ #cmakedefine HAVE_DCT_DECODER 1 /* Do we have any JPX decoder?. */ #cmakedefine HAVE_JPX_DECODER 1 /* Define to 1 if you have the header file, and it defines `DIR'. */ #cmakedefine HAVE_DIRENT_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_DLFCN_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_FCNTL_H 1 /* Define to 1 if you have the `fseek64' function. */ #cmakedefine HAVE_FSEEK64 1 /* Define to 1 if fseeko (and presumably ftello) exists and is declared. */ #cmakedefine HAVE_FSEEKO 1 /* Define to 1 if you have the `ftell64' function. */ #cmakedefine HAVE_FTELL64 1 /* Define to 1 if you have the `pread64' function. */ #cmakedefine HAVE_PREAD64 1 /* Define to 1 if you have the `lseek64' function. */ #cmakedefine HAVE_LSEEK64 1 /* Defines if gettimeofday is available on your system */ #cmakedefine HAVE_GETTIMEOFDAY 1 /* Defines if gmtime_r is available on your system */ #cmakedefine HAVE_GMTIME_R 1 /* Defines if timegm is available on your system */ #cmakedefine HAVE_TIMEGM 1 /* Define to 1 if you have the `z' library (-lz). */ #cmakedefine HAVE_LIBZ 1 /* Defines if localtime_r is available on your system */ #cmakedefine HAVE_LOCALTIME_R 1 /* Define to 1 if you have the `mkstemp' function. */ #cmakedefine HAVE_MKSTEMP 1 /* Defines if strtok_r is available on your system */ #cmakedefine HAVE_STRTOK_R 1 /* Define to 1 if you have the header file, and it defines `DIR'. */ #cmakedefine HAVE_NDIR_H 1 /* Define to 1 if you have the `popen' function. */ #cmakedefine HAVE_POPEN 1 /* Define to 1 if you have the header file, and it defines `DIR'. */ #cmakedefine HAVE_SYS_DIR_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_MMAN_H 1 /* Define to 1 if you have the header file, and it defines `DIR'. */ #cmakedefine HAVE_SYS_NDIR_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_STAT_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_UNISTD_H 1 /* Define to 1 if you have a big endian machine */ #cmakedefine WORDS_BIGENDIAN 1 /* Define as const if the declaration of iconv() needs const. */ #define ICONV_CONST ${ICONV_CONST} /* Generate OPI comments in PS output. */ #cmakedefine OPI_SUPPORT 1 /* Name of package */ #define PACKAGE "poppler" /* Define to the address where bug reports for this package should be sent. */ #define PACKAGE_BUGREPORT "https://bugs.freedesktop.org/enter_bug.cgi?product=poppler" /* Define to the full name of this package. */ #define PACKAGE_NAME "poppler" /* Define to the full name and version of this package. */ #define PACKAGE_STRING "poppler ${POPPLER_VERSION}" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "poppler" /* Define to the home page for this package. */ #define PACKAGE_URL "" /* Define to the version of this package. */ #define PACKAGE_VERSION "${POPPLER_VERSION}" /* Poppler data dir */ #define POPPLER_DATADIR "${POPPLER_DATADIR}" /* Support for curl based doc builder is compiled in. */ #cmakedefine POPPLER_HAS_CURL_SUPPORT 1 /* Enable word list support. */ #cmakedefine TEXTOUT_WORD_LIST 1 /* Defines if use cms */ #cmakedefine USE_CMS 1 /* Use single precision arithmetic in the Splash backend */ #cmakedefine USE_FLOAT 1 /* Version number of package */ #define VERSION "${POPPLER_VERSION}" /* Use fontconfig font configuration backend */ #cmakedefine WITH_FONTCONFIGURATION_FONTCONFIG 1 /* 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 /* MS defined snprintf as deprecated but then added it in Visual Studio 2015. */ #if defined(_MSC_VER) && _MSC_VER < 1900 #define snprintf _snprintf #endif //------------------------------------------------------------------------ // popen //------------------------------------------------------------------------ #if defined(_MSC_VER) || defined(__BORLANDC__) #define popen _popen #define pclose _pclose #define strncasecmp _strnicmp #define strcasecmp _stricmp #endif /* Number of bits in a file offset, on hosts where this is settable. */ #cmakedefine _FILE_OFFSET_BITS @_FILE_OFFSET_BITS@ /* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */ /* TODO This is wrong, port if needed #undef _LARGEFILE_SOURCE */ /* Define for large files, on AIX-style hosts. */ /* TODO This is wrong, port if needed #undef _LARGE_FILES */ poppler-24.02.0/cpp/000077500000000000000000000000001455701731300141305ustar00rootroot00000000000000poppler-24.02.0/cpp/.gitignore000066400000000000000000000001121455701731300161120ustar00rootroot00000000000000.deps .libs *.la *.lo Makefile Makefile.in poppler-version.h APIDOCS-html poppler-24.02.0/cpp/CMakeLists.txt000066400000000000000000000030751455701731300166750ustar00rootroot00000000000000include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) configure_file(poppler-version.h.in ${CMAKE_CURRENT_BINARY_DIR}/poppler-version.h @ONLY) add_subdirectory(tests) set(poppler_cpp_SRCS poppler-destination.cpp poppler-document.cpp poppler-embedded-file.cpp poppler-font.cpp poppler-global.cpp poppler-image.cpp poppler-page.cpp poppler-page-renderer.cpp poppler-page-transition.cpp poppler-private.cpp poppler-rectangle.cpp poppler-toc.cpp poppler-version.cpp ) 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) 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::Iconv) install(TARGETS poppler-cpp RUNTIME DESTINATION bin LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) install(FILES poppler-destination.h poppler-document.h poppler-embedded-file.h poppler-font.h poppler-font-private.h poppler-global.h poppler-image.h poppler-page.h poppler-page-renderer.h poppler-page-transition.h poppler-rectangle.h poppler-toc.h ${CMAKE_CURRENT_BINARY_DIR}/poppler_cpp_export.h ${CMAKE_CURRENT_BINARY_DIR}/poppler-version.h DESTINATION include/poppler/cpp) poppler-24.02.0/cpp/Doxyfile000066400000000000000000002050351455701731300156430ustar00rootroot00000000000000# Doxyfile 1.7.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = "Poppler CPP" # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = 24.02.0 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = NO # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = YES # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this # tag. The format is ext=language, where ext is a file extension, and language # is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = in=C++ # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen to replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penality. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will rougly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = NO # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = YES # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = NO # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = YES # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = YES # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = YES # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = NO # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. The create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text " # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = *.cpp \ *.h \ *.h.in \ *.dox # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = *-private.* # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = poppler::detail, \ poppler::*_private # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 4 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = APIDOCS-html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the stylesheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = NO # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 1 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = NO # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvances is that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = APIDOCS-latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = YES # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = "POPPLER_CPP_EXPORT=" # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = NO # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES poppler-24.02.0/cpp/Mainpage.dox000066400000000000000000000005121455701731300163630ustar00rootroot00000000000000/** \mainpage The Poppler CPP interface library The Poppler CPP interface library, called libpoppler-cpp, is a library that allows C++ programmers to easily load and render PDF files using the Poppler library. Unlike the other Poppler frontends, it has no additional requirements, so can be used in any C++ application. */ poppler-24.02.0/cpp/poppler-destination-private.h000066400000000000000000000025521455701731300217550ustar00rootroot00000000000000/* * Copyright (C) 2019, Masamichi Hosoda * * 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_DESTINATION_PRIVATE_H #define POPPLER_DESTINATION_PRIVATE_H #include "poppler-global.h" #include "poppler-destination.h" #include "Object.h" class PDFDoc; class LinkDest; namespace poppler { class destination_private { public: destination_private(const LinkDest *ld, PDFDoc *doc); destination::type_enum type; bool page_number_unresolved; union { Ref page_ref; int page_number; }; double left, bottom; double right, top; double zoom; bool change_left : 1, change_top : 1; bool change_zoom : 1; PDFDoc *pdf_doc; }; } #endif poppler-24.02.0/cpp/poppler-destination.cpp000066400000000000000000000153171455701731300206430ustar00rootroot00000000000000/* * Copyright (C) 2019, Masamichi Hosoda * Copyright (C) 2019 Albert Astals Cid * Copyright (C) 2022, Oliver Sander * * 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. */ /** \file poppler-destination.h */ #include "poppler-destination.h" #include "poppler-destination-private.h" #include "PDFDoc.h" #include "Link.h" #include using namespace poppler; destination_private::destination_private(const LinkDest *ld, PDFDoc *doc) : pdf_doc(doc) { if (!ld) { type = destination::unknown; return; } switch (ld->getKind()) { case ::destXYZ: type = destination::xyz; break; case ::destFit: type = destination::fit; break; case ::destFitH: type = destination::fit_h; break; case ::destFitV: type = destination::fit_v; break; case ::destFitR: type = destination::fit_r; break; case ::destFitB: type = destination::fit_b; break; case ::destFitBH: type = destination::fit_b_h; break; case ::destFitBV: type = destination::fit_b_v; break; default: type = destination::unknown; break; } if (!ld->isPageRef()) { // The page number has been resolved. page_number_unresolved = false; page_number = ld->getPageNum(); } else if (doc) { // It is necessary to resolve the page number by its accessor. page_number_unresolved = true; page_ref = ld->getPageRef(); } else { // The page number cannot be resolved because there is no PDFDoc. page_number_unresolved = false; page_number = 0; } left = ld->getLeft(); bottom = ld->getBottom(); right = ld->getRight(); top = ld->getTop(); zoom = ld->getZoom(); change_left = ld->getChangeLeft(); change_top = ld->getChangeTop(); change_zoom = ld->getChangeZoom(); } /** \class poppler::destination poppler-destination.h "poppler/cpp/poppler-destination.h" The information about a destination used in a PDF %document. */ /** \enum poppler::destination::type_enum The various types of destinations available in a PDF %document. */ /** \var poppler::destination::type_enum poppler::destination::unknown unknown destination */ /** \var poppler::destination::type_enum poppler::destination::xyz go to page with coordinates (left, top) positioned at the upper-left corner of the window and the contents of the page magnified by the factor zoom */ /** \var poppler::destination::type_enum poppler::destination::fit go to page with its contents magnified just enough to fit the entire page within the window both horizontally and vertically */ /** \var poppler::destination::type_enum poppler::destination::fit_h go to page with the vertical coordinate top positioned at the top edge of the window and the contents of the page magnified just enough to fit the entire width of the page within the window */ /** \var poppler::destination::type_enum poppler::destination::fit_v go to page with the horizontal coordinate left positioned at the left edge of the window and the contents of the page magnified just enough to fit the entire height of the page within the window */ /** \var poppler::destination::type_enum poppler::destination::fit_r go to page with its contents magnified just enough to fit the rectangle specified by the coordinates left, bottom, right, and top entirely within the window both horizontally and vertically */ /** \var poppler::destination::type_enum poppler::destination::fit_b go to page with its contents magnified just enough to fit its bounding box entirely within the window both horizontally and vertically */ /** \var poppler::destination::type_enum poppler::destination::fit_b_h go to page with the vertical coordinate top positioned at the top edge of the window and the contents of the page magnified just enough to fit the entire width of its bounding box within the window */ /** \var poppler::destination::type_enum poppler::destination::fit_b_v go to page with the horizontal coordinate left positioned at the left edge of the window and the contents of the page magnified just enough to fit the entire height of its bounding box within the window */ destination::destination(destination_private *dd) : d(dd) { } /** Move constructor. */ destination::destination(destination &&other) noexcept { *this = std::move(other); } /** \returns the type of the destination */ destination::type_enum destination::type() const { return d->type; } /** \note It is necessary not to destruct parent poppler::document before calling this function for the first time. \returns the page number of the destination */ int destination::page_number() const { if (d->page_number_unresolved) { d->page_number_unresolved = false; d->page_number = d->pdf_doc->findPage(d->page_ref); } return d->page_number; } /** \returns the left coordinate of the destination */ double destination::left() const { return d->left; } /** \returns the bottom coordinate of the destination */ double destination::bottom() const { return d->bottom; } /** \returns the right coordinate of the destination */ double destination::right() const { return d->right; } /** \returns the top coordinate of the destination */ double destination::top() const { return d->top; } /** \returns the scale factor of the destination */ double destination::zoom() const { return d->zoom; } /** \returns whether left coordinate should be changed */ bool destination::is_change_left() const { return d->change_left; } /** \returns whether top coordinate should be changed */ bool destination::is_change_top() const { return d->change_top; } /** \returns whether scale factor should be changed */ bool destination::is_change_zoom() const { return d->change_zoom; } /** Move assignment operator. */ destination &destination::operator=(destination &&other) noexcept = default; /** Destructor. */ destination::~destination() = default; poppler-24.02.0/cpp/poppler-destination.h000066400000000000000000000035121455701731300203020ustar00rootroot00000000000000/* * Copyright (C) 2019, Masamichi Hosoda * Copyright (C) 2019, 2021, Albert Astals Cid * Copyright (C) 2022, Oliver Sander * * 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_DESTINATION_H #define POPPLER_DESTINATION_H #include #include "poppler-global.h" namespace poppler { class destination_private; class POPPLER_CPP_EXPORT destination : public poppler::noncopyable { public: enum type_enum { unknown, xyz, fit, fit_h, fit_v, fit_r, fit_b, fit_b_h, fit_b_v }; ~destination(); destination(destination &&other) noexcept; type_enum type() const; int page_number() const; double left() const; double bottom() const; double right() const; double top() const; double zoom() const; bool is_change_left() const; bool is_change_top() const; bool is_change_zoom() const; destination &operator=(destination &&other) noexcept; private: explicit destination(destination_private *dd); std::unique_ptr d; friend class document; }; } #endif poppler-24.02.0/cpp/poppler-document-private.h000066400000000000000000000036461455701731300212570ustar00rootroot00000000000000/* * Copyright (C) 2009-2011, Pino Toscano * Copyright (C) 2018, 2020, 2022, Albert Astals Cid * Copyright (C) 2018, 2020, Adam Reichold * * 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_DOCUMENT_PRIVATE_H #define POPPLER_DOCUMENT_PRIVATE_H #include "poppler-global.h" #include "poppler-config.h" #include "GooString.h" #include "PDFDoc.h" #include "GlobalParams.h" #include namespace poppler { class document; class embedded_file; class document_private : private GlobalParamsIniter { public: document_private(std::unique_ptr &&file_path, const std::string &owner_password, const std::string &user_password); document_private(byte_array *file_data, const std::string &owner_password, const std::string &user_password); document_private(const char *file_data, int file_data_length, const std::string &owner_password, const std::string &user_password); ~document_private(); static document *check_document(document_private *doc, byte_array *file_data); PDFDoc *doc; byte_array doc_data; const char *raw_doc_data; int raw_doc_data_length; bool is_locked; std::vector embedded_files; private: document_private(); }; } #endif poppler-24.02.0/cpp/poppler-document.cpp000066400000000000000000000751661455701731300201500ustar00rootroot00000000000000/* * Copyright (C) 2009-2011, Pino Toscano * Copyright (C) 2016 Jakub Alba * Copyright (C) 2017, 2022, Albert Astals Cid * Copyright (C) 2018, 2020, Adam Reichold * Copyright (C) 2019, Masamichi Hosoda * Copyright (C) 2019, 2020, Oliver Sander * * 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. */ /** \file poppler-document.h */ #include "poppler-destination.h" #include "poppler-document.h" #include "poppler-embedded-file.h" #include "poppler-page.h" #include "poppler-toc.h" #include "poppler-destination-private.h" #include "poppler-document-private.h" #include "poppler-embedded-file-private.h" #include "poppler-page-private.h" #include "poppler-private.h" #include "poppler-toc-private.h" #include "Catalog.h" #include "DateInfo.h" #include "ErrorCodes.h" #include "GlobalParams.h" #include "Link.h" #include "Outline.h" #include #include #include using namespace poppler; document_private::document_private(std::unique_ptr &&file_path, const std::string &owner_password, const std::string &user_password) : document_private() { doc = new PDFDoc(std::move(file_path), GooString(owner_password.c_str()), GooString(user_password.c_str())); } document_private::document_private(byte_array *file_data, const std::string &owner_password, const std::string &user_password) : document_private() { file_data->swap(doc_data); MemStream *memstr = new MemStream(&doc_data[0], 0, doc_data.size(), Object(objNull)); doc = new PDFDoc(memstr, GooString(owner_password.c_str()), GooString(user_password.c_str())); } document_private::document_private(const char *file_data, int file_data_length, const std::string &owner_password, const std::string &user_password) : document_private() { raw_doc_data = file_data; raw_doc_data_length = file_data_length; MemStream *memstr = new MemStream(raw_doc_data, 0, raw_doc_data_length, Object(objNull)); doc = new PDFDoc(memstr, GooString(owner_password.c_str()), GooString(user_password.c_str())); } document_private::document_private() : GlobalParamsIniter(detail::error_function), doc(nullptr), raw_doc_data(nullptr), raw_doc_data_length(0), is_locked(false) { } document_private::~document_private() { delete_all(embedded_files); delete doc; } document *document_private::check_document(document_private *doc, byte_array *file_data) { if (doc->doc->isOk() || doc->doc->getErrorCode() == errEncrypted) { if (doc->doc->getErrorCode() == errEncrypted) { doc->is_locked = true; } return new document(*doc); } else { // put back the document data where it was before if (file_data) { file_data->swap(doc->doc_data); } delete doc; } return nullptr; } /** \class poppler::document poppler-document.h "poppler/cpp/poppler-document.h" Represents a PDF %document. */ /** \enum poppler::document::page_mode_enum The various page modes available in a PDF %document. */ /** \var poppler::document::page_mode_enum poppler::document::use_none The %document specifies no particular page mode. */ /** \var poppler::document::page_mode_enum poppler::document::use_outlines The %document specifies its TOC (table of contents) should be open. */ /** \var poppler::document::page_mode_enum poppler::document::use_thumbs The %document specifies that should be open a view of the thumbnails of its pages. */ /** \var poppler::document::page_mode_enum poppler::document::fullscreen The %document specifies it wants to be open in a fullscreen mode. */ /** \var poppler::document::page_mode_enum poppler::document::use_oc The %document specifies that should be open a view of its Optional Content (also known as layers). */ /** \var poppler::document::page_mode_enum poppler::document::use_attach The %document specifies that should be open a view of its %document-level attachments. */ document::document(document_private &dd) : d(&dd) { } document::~document() { delete d; } /** \returns whether the current %document is locked */ bool document::is_locked() const { return d->is_locked; } /** Unlocks the current document, if locked. \returns the new locking status of the document */ bool document::unlock(const std::string &owner_password, const std::string &user_password) { if (d->is_locked) { document_private *newdoc = nullptr; if (d->doc_data.size() > 0) { 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); } else { newdoc = new document_private(std::make_unique(d->doc->getFileName()), owner_password, user_password); } if (!newdoc->doc->isOk()) { d->doc_data.swap(newdoc->doc_data); delete newdoc; } else { delete d; d = newdoc; d->is_locked = false; } } return d->is_locked; } /** \returns the eventual page mode specified by the current PDF %document */ document::page_mode_enum document::page_mode() const { switch (d->doc->getCatalog()->getPageMode()) { case Catalog::pageModeNone: return use_none; case Catalog::pageModeOutlines: return use_outlines; case Catalog::pageModeThumbs: return use_thumbs; case Catalog::pageModeFullScreen: return fullscreen; case Catalog::pageModeOC: return use_oc; case Catalog::pageModeAttach: return use_attach; default: return use_none; } } /** \returns the eventual page layout specified by the current PDF %document */ document::page_layout_enum document::page_layout() const { switch (d->doc->getCatalog()->getPageLayout()) { case Catalog::pageLayoutNone: return no_layout; case Catalog::pageLayoutSinglePage: return single_page; case Catalog::pageLayoutOneColumn: return one_column; case Catalog::pageLayoutTwoColumnLeft: return two_column_left; case Catalog::pageLayoutTwoColumnRight: return two_column_right; case Catalog::pageLayoutTwoPageLeft: return two_page_left; case Catalog::pageLayoutTwoPageRight: return two_page_right; default: return no_layout; } } /** Gets the version of the current PDF %document. Example: \code poppler::document *doc = ...; // for example, if the document is PDF 1.6: int major = 0, minor = 0; doc->get_pdf_version(&major, &minor); // major == 1 // minor == 6 \endcode \param major if not NULL, will be set to the "major" number of the version \param minor if not NULL, will be set to the "minor" number of the version */ void document::get_pdf_version(int *major, int *minor) const { if (major) { *major = d->doc->getPDFMajorVersion(); } if (minor) { *minor = d->doc->getPDFMinorVersion(); } } /** \returns all the information keys available in the %document \see info_key, info_date */ std::vector document::info_keys() const { if (d->is_locked) { return std::vector(); } Object info = d->doc->getDocInfo(); if (!info.isDict()) { return std::vector(); } Dict *info_dict = info.getDict(); std::vector keys(info_dict->getLength()); for (int i = 0; i < info_dict->getLength(); ++i) { keys[i] = std::string(info_dict->getKey(i)); } return keys; } /** Gets the value of the specified \p key of the document information. \returns the value for the \p key, or an empty string if not available \see info_keys, info_date */ ustring document::info_key(const std::string &key) const { if (d->is_locked) { return ustring(); } std::unique_ptr goo_value(d->doc->getDocInfoStringEntry(key.c_str())); if (!goo_value.get()) { return ustring(); } return detail::unicode_GooString_to_ustring(goo_value.get()); } /** Sets the value of the specified \p key of the %document information to \p val. If \p val is empty, the entry specified by \p key is removed. \returns true on success, false on failure */ bool document::set_info_key(const std::string &key, const ustring &val) { if (d->is_locked) { return false; } GooString *goo_val; if (val.empty()) { goo_val = nullptr; } else { goo_val = detail::ustring_to_unicode_GooString(val); } d->doc->setDocInfoStringEntry(key.c_str(), goo_val); return true; } /** Gets the time_type value of the specified \p key of the document information. \returns the time_t value for the \p key \see info_keys, info_date */ time_type document::info_date(const std::string &key) const { if (d->is_locked) { return time_type(-1); } std::unique_ptr goo_date(d->doc->getDocInfoStringEntry(key.c_str())); if (!goo_date.get()) { return time_type(-1); } return static_cast(dateStringToTime(goo_date.get())); } /** Gets the time_t value of the specified \p key of the document information. \returns the time_t value for the \p key \see info_keys, info_date */ time_t document::info_date_t(const std::string &key) const { if (d->is_locked) { return time_t(-1); } std::unique_ptr goo_date(d->doc->getDocInfoStringEntry(key.c_str())); if (!goo_date.get()) { return time_t(-1); } return dateStringToTime(goo_date.get()); } /** Sets the time_type value of the specified \p key of the %document information to \p val. If \p val == time_type(-1), the entry specified by \p key is removed. \returns true on success, false on failure */ bool document::set_info_date(const std::string &key, time_type val) { if (d->is_locked) { return false; } GooString *goo_date; if (val == time_type(-1)) { goo_date = nullptr; } else { time_t t = static_cast(val); goo_date = timeToDateString(&t); } d->doc->setDocInfoStringEntry(key.c_str(), goo_date); return true; } /** Sets the time_t value of the specified \p key of the %document information to \p val. If \p val == time_t(-1), the entry specified by \p key is removed. \returns true on success, false on failure */ bool document::set_info_date_t(const std::string &key, time_t val) { if (d->is_locked) { return false; } GooString *goo_date; if (val == time_t(-1)) { goo_date = nullptr; } else { goo_date = timeToDateString(&val); } d->doc->setDocInfoStringEntry(key.c_str(), goo_date); return true; } /** Gets the %document's title. \returns the document's title, or an empty string if not available \see set_title, info_key */ ustring document::get_title() const { if (d->is_locked) { return ustring(); } std::unique_ptr goo_title(d->doc->getDocInfoTitle()); if (!goo_title.get()) { return ustring(); } return detail::unicode_GooString_to_ustring(goo_title.get()); } /** Sets the %document's title to \p title. If \p title is empty, the %document's title is removed. \returns true on success, false on failure */ bool document::set_title(const ustring &title) { if (d->is_locked) { return false; } GooString *goo_title; if (title.empty()) { goo_title = nullptr; } else { goo_title = detail::ustring_to_unicode_GooString(title); } d->doc->setDocInfoTitle(goo_title); return true; } /** Gets the document's author. \returns the document's author, or an empty string if not available \see set_author, info_key */ ustring document::get_author() const { if (d->is_locked) { return ustring(); } std::unique_ptr goo_author(d->doc->getDocInfoAuthor()); if (!goo_author.get()) { return ustring(); } return detail::unicode_GooString_to_ustring(goo_author.get()); } /** Sets the %document's author to \p author. If \p author is empty, the %document's author is removed. \returns true on success, false on failure */ bool document::set_author(const ustring &author) { if (d->is_locked) { return false; } GooString *goo_author; if (author.empty()) { goo_author = nullptr; } else { goo_author = detail::ustring_to_unicode_GooString(author); } d->doc->setDocInfoAuthor(goo_author); return true; } /** Gets the document's subject. \returns the document's subject, or an empty string if not available \see set_subject, info_key */ ustring document::get_subject() const { if (d->is_locked) { return ustring(); } std::unique_ptr goo_subject(d->doc->getDocInfoSubject()); if (!goo_subject.get()) { return ustring(); } return detail::unicode_GooString_to_ustring(goo_subject.get()); } /** Sets the %document's subject to \p subject. If \p subject is empty, the %document's subject is removed. \returns true on success, false on failure */ bool document::set_subject(const ustring &subject) { if (d->is_locked) { return false; } GooString *goo_subject; if (subject.empty()) { goo_subject = nullptr; } else { goo_subject = detail::ustring_to_unicode_GooString(subject); } d->doc->setDocInfoSubject(goo_subject); return true; } /** Gets the document's keywords. \returns the document's keywords, or an empty string if not available \see set_keywords, info_key */ ustring document::get_keywords() const { if (d->is_locked) { return ustring(); } std::unique_ptr goo_keywords(d->doc->getDocInfoKeywords()); if (!goo_keywords.get()) { return ustring(); } return detail::unicode_GooString_to_ustring(goo_keywords.get()); } /** Sets the %document's keywords to \p keywords. If \p keywords is empty, the %document's keywords are removed. \returns true on success, false on failure */ bool document::set_keywords(const ustring &keywords) { if (d->is_locked) { return false; } GooString *goo_keywords; if (keywords.empty()) { goo_keywords = nullptr; } else { goo_keywords = detail::ustring_to_unicode_GooString(keywords); } d->doc->setDocInfoKeywords(goo_keywords); return true; } /** Gets the document's creator. \returns the document's creator, or an empty string if not available \see set_creator, info_key */ ustring document::get_creator() const { if (d->is_locked) { return ustring(); } std::unique_ptr goo_creator(d->doc->getDocInfoCreator()); if (!goo_creator.get()) { return ustring(); } return detail::unicode_GooString_to_ustring(goo_creator.get()); } /** Sets the %document's creator to \p creator. If \p creator is empty, the %document's creator is removed. \returns true on success, false on failure */ bool document::set_creator(const ustring &creator) { if (d->is_locked) { return false; } GooString *goo_creator; if (creator.empty()) { goo_creator = nullptr; } else { goo_creator = detail::ustring_to_unicode_GooString(creator); } d->doc->setDocInfoCreator(goo_creator); return true; } /** Gets the document's producer. \returns the document's producer, or an empty string if not available \see set_producer, info_key */ ustring document::get_producer() const { if (d->is_locked) { return ustring(); } std::unique_ptr goo_producer(d->doc->getDocInfoProducer()); if (!goo_producer.get()) { return ustring(); } return detail::unicode_GooString_to_ustring(goo_producer.get()); } /** Sets the %document's producer to \p producer. If \p producer is empty, the %document's producer is removed. \returns true on success, false on failure */ bool document::set_producer(const ustring &producer) { if (d->is_locked) { return false; } GooString *goo_producer; if (producer.empty()) { goo_producer = nullptr; } else { goo_producer = detail::ustring_to_unicode_GooString(producer); } d->doc->setDocInfoProducer(goo_producer); return true; } /** Gets the document's creation date as a time_type value. \returns the document's creation date as a time_type value \see set_creation_date, info_date */ time_type document::get_creation_date() const { if (d->is_locked) { return time_type(-1); } std::unique_ptr goo_creation_date(d->doc->getDocInfoCreatDate()); if (!goo_creation_date.get()) { return time_type(-1); } return static_cast(dateStringToTime(goo_creation_date.get())); } /** Gets the document's creation date as a time_t value. \returns the document's creation date as a time_t value \see set_creation_date, info_date */ time_t document::get_creation_date_t() const { if (d->is_locked) { return time_t(-1); } std::unique_ptr goo_creation_date(d->doc->getDocInfoCreatDate()); if (!goo_creation_date.get()) { return time_t(-1); } return dateStringToTime(goo_creation_date.get()); } /** Sets the %document's creation date to \p creation_date. If \p creation_date == time_type(-1), the %document's creation date is removed. \returns true on success, false on failure */ bool document::set_creation_date(time_type creation_date) { if (d->is_locked) { return false; } GooString *goo_creation_date; if (creation_date == time_type(-1)) { goo_creation_date = nullptr; } else { time_t t = static_cast(creation_date); goo_creation_date = timeToDateString(&t); } d->doc->setDocInfoCreatDate(goo_creation_date); return true; } /** Sets the %document's creation date to \p creation_date. If \p creation_date == time_t(-1), the %document's creation date is removed. \returns true on success, false on failure */ bool document::set_creation_date_t(time_t creation_date) { if (d->is_locked) { return false; } GooString *goo_creation_date; if (creation_date == time_t(-1)) { goo_creation_date = nullptr; } else { goo_creation_date = timeToDateString(&creation_date); } d->doc->setDocInfoCreatDate(goo_creation_date); return true; } /** Gets the document's modification date as a time_type value. \returns the document's modification date as a time_type value \see set_modification_date, info_date */ time_type document::get_modification_date() const { if (d->is_locked) { return time_type(-1); } std::unique_ptr goo_modification_date(d->doc->getDocInfoModDate()); if (!goo_modification_date.get()) { return time_type(-1); } return static_cast(dateStringToTime(goo_modification_date.get())); } /** Gets the document's modification date as a time_t value. \returns the document's modification date as a time_t value \see set_modification_date, info_date */ time_t document::get_modification_date_t() const { if (d->is_locked) { return time_t(-1); } std::unique_ptr goo_modification_date(d->doc->getDocInfoModDate()); if (!goo_modification_date.get()) { return time_t(-1); } return dateStringToTime(goo_modification_date.get()); } /** Sets the %document's modification date to \p mod_date. If \p mod_date == time_type(-1), the %document's modification date is removed. \returns true on success, false on failure */ bool document::set_modification_date(time_type mod_date) { if (d->is_locked) { return false; } GooString *goo_mod_date; if (mod_date == time_type(-1)) { goo_mod_date = nullptr; } else { time_t t = static_cast(mod_date); goo_mod_date = timeToDateString(&t); } d->doc->setDocInfoModDate(goo_mod_date); return true; } /** Sets the %document's modification date to \p mod_date. If \p mod_date == time_t(-1), the %document's modification date is removed. \returns true on success, false on failure */ bool document::set_modification_date_t(time_t mod_date) { if (d->is_locked) { return false; } GooString *goo_mod_date; if (mod_date == time_t(-1)) { goo_mod_date = nullptr; } else { goo_mod_date = timeToDateString(&mod_date); } d->doc->setDocInfoModDate(goo_mod_date); return true; } /** Removes the %document's Info dictionary. \returns true on success, false on failure */ bool document::remove_info() { if (d->is_locked) { return false; } d->doc->removeDocInfo(); return true; } /** \returns whether the document is encrypted */ bool document::is_encrypted() const { return d->doc->isEncrypted(); } /** \returns whether the document is linearized */ bool document::is_linearized() const { return d->doc->isLinearized(); } /** Check for available "document permission". \returns whether the specified permission is allowed */ bool document::has_permission(permission_enum which) const { switch (which) { case perm_print: return d->doc->okToPrint(); case perm_change: return d->doc->okToChange(); case perm_copy: return d->doc->okToCopy(); case perm_add_notes: return d->doc->okToAddNotes(); case perm_fill_forms: return d->doc->okToFillForm(); case perm_accessibility: return d->doc->okToAccessibility(); case perm_assemble: return d->doc->okToAssemble(); case perm_print_high_resolution: return d->doc->okToPrintHighRes(); } return true; } /** Reads the %document metadata string. \return the %document metadata string */ ustring document::metadata() const { std::unique_ptr md(d->doc->getCatalog()->readMetadata()); if (md.get()) { return detail::unicode_GooString_to_ustring(md.get()); } return ustring(); } /** Gets the IDs of the current PDF %document, if available. \param permanent_id if not NULL, will be set to the permanent ID of the %document \param update_id if not NULL, will be set to the update ID of the %document \returns whether the document has the IDs \since 0.16 */ bool document::get_pdf_id(std::string *permanent_id, std::string *update_id) const { GooString goo_permanent_id; GooString goo_update_id; if (!d->doc->getID(permanent_id ? &goo_permanent_id : nullptr, update_id ? &goo_update_id : nullptr)) { return false; } if (permanent_id) { *permanent_id = goo_permanent_id.c_str(); } if (update_id) { *update_id = goo_update_id.c_str(); } return true; } /** Document page count. \returns the number of pages of the document */ int document::pages() const { return d->doc->getNumPages(); } /** Document page by label reading. This creates a new page representing the %document %page whose label is the specified \p label. If there is no page with that \p label, NULL is returned. \returns a new page object or NULL */ page *document::create_page(const ustring &label) const { std::unique_ptr goolabel(detail::ustring_to_unicode_GooString(label)); int index = 0; if (!d->doc->getCatalog()->labelToIndex(goolabel.get(), &index)) { return nullptr; } return create_page(index); } /** Document page by index reading. This creates a new page representing the \p index -th %page of the %document. \note the page indexes are in the range [0, pages()[. \returns a new page object or NULL */ page *document::create_page(int index) const { if (index >= 0 && index < d->doc->getNumPages()) { page *p = new page(d, index); if (p->d->page) { return p; } else { delete p; return nullptr; } } else { return nullptr; } } /** Reads all the font information of the %document. \note this can be slow for big documents; prefer the use of a font_iterator to read incrementally page by page \see create_font_iterator */ std::vector document::fonts() const { std::vector result; 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)); } return result; } /** Creates a new font iterator. This creates a new font iterator for reading the font information of the %document page by page, starting at the specified \p start_page (0 if not specified). \returns a new font iterator */ font_iterator *document::create_font_iterator(int start_page) const { return new font_iterator(start_page, d); } /** Reads the TOC (table of contents) of the %document. \returns a new toc object if a TOC is available, NULL otherwise */ toc *document::create_toc() const { return toc_private::load_from_outline(d->doc->getOutline()); } /** Reads whether the current document has %document-level embedded files (attachments). This is a very fast way to know whether there are embedded files (also known as "attachments") at the %document-level. Note this does not take into account files embedded in other ways (e.g. to annotations). \returns whether the document has embedded files */ bool document::has_embedded_files() const { return d->doc->getCatalog()->numEmbeddedFiles() > 0; } /** Reads all the %document-level embedded files of the %document. \returns the %document-level embedded files */ std::vector document::embedded_files() const { if (d->is_locked) { return std::vector(); } if (d->embedded_files.empty() && d->doc->getCatalog()->numEmbeddedFiles() > 0) { const int num = d->doc->getCatalog()->numEmbeddedFiles(); d->embedded_files.resize(num); for (int i = 0; i < num; ++i) { std::unique_ptr fs = d->doc->getCatalog()->embeddedFile(i); d->embedded_files[i] = embedded_file_private::create(std::move(fs)); } } return d->embedded_files; } /** Creates a map of all the named destinations in the %document. \note The destination names may contain \\0 and other binary values so they are not printable and cannot convert to null-terminated C strings. \returns the map of the each name and destination \since 0.74 */ std::map document::create_destination_map() const { std::map m; Catalog *catalog = d->doc->getCatalog(); if (!catalog) { return m; } // Iterate from name-dict const int nDests = catalog->numDests(); for (int i = 0; i < nDests; ++i) { std::string key(catalog->getDestsName(i)); std::unique_ptr link_dest = catalog->getDestsDest(i); if (link_dest) { destination dest(new destination_private(link_dest.get(), d->doc)); m.emplace(std::move(key), std::move(dest)); } } // Iterate from name-tree const int nDestsNameTree = catalog->numDestNameTree(); for (int i = 0; i < nDestsNameTree; ++i) { std::string key(catalog->getDestNameTreeName(i)->c_str(), catalog->getDestNameTreeName(i)->getLength()); std::unique_ptr link_dest = catalog->getDestNameTreeDest(i); if (link_dest) { destination dest(new destination_private(link_dest.get(), d->doc)); m.emplace(std::move(key), std::move(dest)); } } return m; } /** Saves the %document to file \p file_name. \returns true on success, false on failure */ bool document::save(const std::string &file_name) const { if (d->is_locked) { return false; } GooString fname(file_name.c_str()); return d->doc->saveAs(fname) == errNone; } /** Saves the original version of the %document to file \p file_name. \returns true on success, false on failure */ bool document::save_a_copy(const std::string &file_name) const { if (d->is_locked) { return false; } GooString fname(file_name.c_str()); return d->doc->saveWithoutChangesAs(fname) == errNone; } /** Tries to load a PDF %document from the specified file. \param file_name the file to open \returns a new document if the load succeeded (even if the document is locked), NULL otherwise */ document *document::load_from_file(const std::string &file_name, const std::string &owner_password, const std::string &user_password) { document_private *doc = new document_private(std::make_unique(file_name.c_str()), owner_password, user_password); return document_private::check_document(doc, nullptr); } /** Tries to load a PDF %document from the specified data. \note if the loading succeeds, the document takes ownership of the \p file_data (swap()ing it) \param file_data the data representing a document to open \returns a new document if the load succeeded (even if the document is locked), NULL otherwise */ document *document::load_from_data(byte_array *file_data, const std::string &owner_password, const std::string &user_password) { if (!file_data || file_data->size() < 10) { return nullptr; } document_private *doc = new document_private(file_data, owner_password, user_password); return document_private::check_document(doc, file_data); } /** Tries to load a PDF %document from the specified data buffer. \note the buffer must remain valid for the whole lifetime of the returned document \param file_data the data buffer representing a document to open \param file_data_length the length of the data buffer \returns a new document if the load succeeded (even if the document is locked), NULL otherwise \since 0.16 */ document *document::load_from_raw_data(const char *file_data, int file_data_length, const std::string &owner_password, const std::string &user_password) { if (!file_data || file_data_length < 10) { return nullptr; } document_private *doc = new document_private(file_data, file_data_length, owner_password, user_password); return document_private::check_document(doc, nullptr); } poppler-24.02.0/cpp/poppler-document.h000066400000000000000000000112331455701731300175760ustar00rootroot00000000000000/* * Copyright (C) 2009-2010, Pino Toscano * Copyright (C) 2016 Jakub Alba * Copyright (C) 2019, Masamichi Hosoda * Copyright (C) 2019, 2021, 2022, 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 * 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_DOCUMENT_H #define POPPLER_DOCUMENT_H #include "poppler-global.h" #include "poppler-font.h" #include namespace poppler { class destination; class document_private; class embedded_file; class page; class toc; class POPPLER_CPP_EXPORT document : public poppler::noncopyable { public: enum page_mode_enum { use_none, use_outlines, use_thumbs, fullscreen, use_oc, use_attach }; enum page_layout_enum { no_layout, single_page, one_column, two_column_left, two_column_right, two_page_left, two_page_right }; ~document(); bool is_locked() const; bool unlock(const std::string &owner_password, const std::string &user_password); page_mode_enum page_mode() const; page_layout_enum page_layout() const; void get_pdf_version(int *major, int *minor) const; std::vector info_keys() const; ustring info_key(const std::string &key) const; bool set_info_key(const std::string &key, const ustring &val); [[deprecated]] time_type info_date(const std::string &key) const; [[deprecated]] bool set_info_date(const std::string &key, time_type val); time_t info_date_t(const std::string &key) const; bool set_info_date_t(const std::string &key, time_t val); ustring get_title() const; bool set_title(const ustring &title); ustring get_author() const; bool set_author(const ustring &author); ustring get_subject() const; bool set_subject(const ustring &subject); ustring get_keywords() const; bool set_keywords(const ustring &keywords); ustring get_creator() const; bool set_creator(const ustring &creator); ustring get_producer() const; bool set_producer(const ustring &producer); [[deprecated]] time_type get_creation_date() const; [[deprecated]] bool set_creation_date(time_type creation_date); time_t get_creation_date_t() const; bool set_creation_date_t(time_t creation_date); [[deprecated]] time_type get_modification_date() const; [[deprecated]] bool set_modification_date(time_type mod_date); time_t get_modification_date_t() const; bool set_modification_date_t(time_t mod_date); bool remove_info(); bool is_encrypted() const; bool is_linearized() const; bool has_permission(permission_enum which) const; ustring metadata() const; bool get_pdf_id(std::string *permanent_id, std::string *update_id) const; int pages() const; page *create_page(const ustring &label) const; page *create_page(int index) const; std::vector fonts() const; font_iterator *create_font_iterator(int start_page = 0) const; toc *create_toc() const; bool has_embedded_files() const; std::vector embedded_files() const; // Named destinations are bytestrings, not string. // So we use std::string instead of ustring. std::map create_destination_map() const; bool save(const std::string &file_name) const; bool save_a_copy(const std::string &file_name) const; static document *load_from_file(const std::string &file_name, const std::string &owner_password = std::string(), const std::string &user_password = std::string()); static document *load_from_data(byte_array *file_data, const std::string &owner_password = std::string(), const std::string &user_password = std::string()); static document *load_from_raw_data(const char *file_data, int file_data_length, const std::string &owner_password = std::string(), const std::string &user_password = std::string()); private: explicit document(document_private &dd); document_private *d; friend class document_private; }; } #endif poppler-24.02.0/cpp/poppler-embedded-file-private.h000066400000000000000000000022761455701731300221050ustar00rootroot00000000000000/* * Copyright (C) 2009, 2011, Pino Toscano * Copyright (C) 2018, 2021, 2022, 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 * 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_EMBEDDED_FILE_PRIVATE_H #define POPPLER_EMBEDDED_FILE_PRIVATE_H #include #include namespace poppler { class embedded_file_private { public: explicit embedded_file_private(std::unique_ptr &&fs); static embedded_file *create(std::unique_ptr &&fs); std::unique_ptr file_spec; }; } #endif poppler-24.02.0/cpp/poppler-embedded-file.cpp000066400000000000000000000123751455701731300207710ustar00rootroot00000000000000/* * Copyright (C) 2009-2011, Pino Toscano * Copyright (C) 2016 Jakub Alba * Copyright (C) 2018, 2020, 2022 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 * 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. */ /** \file poppler-embedded-file.h */ #include "poppler-embedded-file.h" #include "poppler-embedded-file-private.h" #include "poppler-private.h" #include "Object.h" #include "Stream.h" #include "Catalog.h" #include "FileSpec.h" #include "DateInfo.h" using namespace poppler; embedded_file_private::embedded_file_private(std::unique_ptr &&fs) : file_spec(std::move(fs)) { } embedded_file *embedded_file_private::create(std::unique_ptr &&fs) { return new embedded_file(*new embedded_file_private(std::move(fs))); } /** \class poppler::embedded_file poppler-embedded-file.h "poppler/cpp/poppler-embedded-file.h" Represents a file embedded in a PDF %document. */ embedded_file::embedded_file(embedded_file_private &dd) : d(&dd) { } /** Destroys the embedded file. */ embedded_file::~embedded_file() { delete d; } /** \returns whether the embedded file is valid */ bool embedded_file::is_valid() const { return d->file_spec->isOk(); } /** \returns the name of the embedded file */ std::string embedded_file::name() const { const GooString *goo = d->file_spec->getFileName(); return goo ? std::string(goo->c_str()) : std::string(); } /** \returns the description of the embedded file */ ustring embedded_file::description() const { const GooString *goo = d->file_spec->getDescription(); return goo ? detail::unicode_GooString_to_ustring(goo) : ustring(); } /** \note this is not always available in the PDF %document, in that case this will return \p -1. \returns the size of the embedded file, if known */ int embedded_file::size() const { const EmbFile *ef = d->file_spec->getEmbeddedFile(); return ef ? ef->size() : -1; } /** \returns the time_type representing the modification date of the embedded file, if available */ time_type embedded_file::modification_date() const { const EmbFile *ef = d->file_spec->getEmbeddedFile(); const GooString *goo = ef ? ef->modDate() : nullptr; return goo ? static_cast(dateStringToTime(goo)) : time_type(-1); } /** \returns the time_type representing the creation date of the embedded file, if available */ time_type embedded_file::creation_date() const { const EmbFile *ef = d->file_spec->getEmbeddedFile(); const GooString *goo = ef ? ef->createDate() : nullptr; return goo ? static_cast(dateStringToTime(goo)) : time_type(-1); } /** \returns the time_t representing the modification date of the embedded file, if available */ time_t embedded_file::modification_date_t() const { const EmbFile *ef = d->file_spec->getEmbeddedFile(); const GooString *goo = ef ? ef->modDate() : nullptr; return goo ? dateStringToTime(goo) : time_t(-1); } /** \returns the time_t representing the creation date of the embedded file, if available */ time_t embedded_file::creation_date_t() const { const EmbFile *ef = d->file_spec->getEmbeddedFile(); const GooString *goo = ef ? ef->createDate() : nullptr; return goo ? dateStringToTime(goo) : time_t(-1); } /** \returns the checksum of the embedded file */ byte_array embedded_file::checksum() const { const EmbFile *ef = d->file_spec->getEmbeddedFile(); const GooString *cs = ef ? ef->checksum() : nullptr; if (!cs) { return byte_array(); } const char *ccs = cs->c_str(); byte_array data(cs->getLength()); for (int i = 0; i < cs->getLength(); ++i) { data[i] = ccs[i]; } return data; } /** \returns the MIME type of the embedded file, if available */ std::string embedded_file::mime_type() const { const EmbFile *ef = d->file_spec->getEmbeddedFile(); const GooString *goo = ef ? ef->mimeType() : nullptr; return goo ? std::string(goo->c_str()) : std::string(); } /** Reads all the data of the embedded file. \returns the data of the embedded file */ byte_array embedded_file::data() const { if (!is_valid()) { return byte_array(); } EmbFile *ef = d->file_spec->getEmbeddedFile(); Stream *stream = ef ? ef->stream() : nullptr; if (!stream) { return byte_array(); } stream->reset(); byte_array ret(1024); size_t data_len = 0; int i; while ((i = stream->getChar()) != EOF) { if (data_len == ret.size()) { ret.resize(ret.size() * 2); } ret[data_len] = (char)i; ++data_len; } ret.resize(data_len); return ret; } poppler-24.02.0/cpp/poppler-embedded-file.h000066400000000000000000000031531455701731300204300ustar00rootroot00000000000000/* * Copyright (C) 2009-2010, Pino Toscano * Copyright (C) 2021, 2022, 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 * 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_EMBEDDED_FILE_H #define POPPLER_EMBEDDED_FILE_H #include "poppler-global.h" #include namespace poppler { class embedded_file_private; class POPPLER_CPP_EXPORT embedded_file : public poppler::noncopyable { public: ~embedded_file(); bool is_valid() const; std::string name() const; ustring description() const; int size() const; [[deprecated]] time_type modification_date() const; [[deprecated]] time_type creation_date() const; time_t modification_date_t() const; time_t creation_date_t() const; byte_array checksum() const; std::string mime_type() const; byte_array data() const; private: explicit embedded_file(embedded_file_private &dd); embedded_file_private *d; friend class embedded_file_private; }; } #endif poppler-24.02.0/cpp/poppler-font-private.h000066400000000000000000000041461455701731300204030ustar00rootroot00000000000000/* * Copyright (C) 2009, Pino Toscano * Copyright (C) 2015, Tamas Szekeres * Copyright (C) 2020, Suzuki Toshiya * Copyright (C) 2021, 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 * 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 "poppler-font.h" #include "poppler-document-private.h" #include "FontInfo.h" #include using namespace poppler; class poppler::font_info_private { public: 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(); } if (fi->getFile()) { font_file = fi->getFile()->c_str(); } ref = fi->getRef(); emb_ref = fi->getEmbRef(); } std::string font_name; std::string font_file; font_info::type_enum type : 5; bool is_embedded : 1; bool is_subset : 1; Ref ref; Ref emb_ref; }; 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() { } FontInfoScanner font_info_scanner; int total_pages; int current_page; }; poppler-24.02.0/cpp/poppler-font.cpp000066400000000000000000000103751455701731300172670ustar00rootroot00000000000000/* * Copyright (C) 2009, Pino Toscano * Copyright (C) 2015, Tamas Szekeres * Copyright (C) 2018, Adam Reichold * Copyright (C) 2019, Oliver Sander * Copyright (C) 2020, Suzuki Toshiya * * 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. */ /** \file poppler-font.h */ #include "poppler-font.h" #include "poppler-font-private.h" #include "poppler-document-private.h" #include "FontInfo.h" #include using namespace poppler; /** \class poppler::font_info poppler-font.h "poppler/cpp/poppler-font.h" The information about a font used in a PDF %document. */ /** \enum poppler::font_info::type_enum The various types of fonts available in a PDF %document. */ /** Constructs an invalid font information. */ font_info::font_info() : d(new font_info_private()) { } font_info::font_info(font_info_private &dd) : d(&dd) { } /** Copy constructor. */ font_info::font_info(const font_info &fi) : d(new font_info_private(*fi.d)) { } /** Destructor. */ font_info::~font_info() { delete d; } /** \returns the name of the font */ std::string font_info::name() const { return d->font_name; } /** \returns the file name of the font, in case the font is not embedded nor subset */ std::string font_info::file() const { return d->font_file; } /** \returns whether the font is totally embedded in the %document */ bool font_info::is_embedded() const { return d->is_embedded; } /** \returns whether there is a subset of the font embedded in the %document */ bool font_info::is_subset() const { return d->is_subset; } /** \returns the type of the font */ font_info::type_enum font_info::type() const { return d->type; } /** Assignment operator. */ font_info &font_info::operator=(const font_info &fi) { if (this != &fi) { *d = *fi.d; } return *this; } /** \class poppler::font_iterator poppler-font.h "poppler/cpp/poppler-font.h" Reads the fonts in the PDF %document page by page. font_iterator is the way to collect the list of the fonts used in a PDF %document, reading them incrementally page by page. A typical usage of this might look like: \code poppler::font_iterator *it = doc->create_font_iterator(); while (it->has_next()) { std::vector fonts = it->next(); // do domething with the fonts } // after we are done with the iterator, it must be deleted delete it; \endcode */ font_iterator::font_iterator(int start_page, document_private *dd) : d(new font_iterator_private(start_page, dd)) { } /** Destructor. */ font_iterator::~font_iterator() { delete d; } /** \returns the fonts of the current page and advances to the next one. */ std::vector font_iterator::next() { if (!has_next()) { return std::vector(); } ++d->current_page; /* FontInfoScanner::scan() receives a number how many pages to * be scanned from the *current page*, not from the beginning. * We restrict the font scanning to the current page only. */ const std::vector items = d->font_info_scanner.scan(1); std::vector fonts; fonts.reserve(items.size()); for (FontInfo *entry : items) { fonts.push_back(font_info(*new font_info_private(entry))); delete entry; } return fonts; } /** \returns whether the iterator has more pages to advance to */ bool font_iterator::has_next() const { return d->current_page < d->total_pages; } /** \returns the current page */ int font_iterator::current_page() const { return d->current_page; } poppler-24.02.0/cpp/poppler-font.h000066400000000000000000000042461455701731300167340ustar00rootroot00000000000000/* * Copyright (C) 2009, Pino Toscano * Copyright (C) 2020, Suzuki Toshiya * Copyright (C) 2021, 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 * 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_FONT_H #define POPPLER_FONT_H #include "poppler-global.h" #include namespace poppler { class document; class document_private; class font_info_private; class font_iterator; class font_iterator_private; class POPPLER_CPP_EXPORT font_info { public: enum type_enum { unknown, type1, type1c, type1c_ot, type3, truetype, truetype_ot, cid_type0, cid_type0c, cid_type0c_ot, cid_truetype, cid_truetype_ot }; font_info(); font_info(const font_info &fi); ~font_info(); std::string name() const; std::string file() const; bool is_embedded() const; bool is_subset() const; type_enum type() const; font_info &operator=(const font_info &fi); private: explicit font_info(font_info_private &dd); font_info_private *d; friend class font_iterator; friend class page; }; class POPPLER_CPP_EXPORT font_iterator : public poppler::noncopyable { public: ~font_iterator(); std::vector next(); bool has_next() const; int current_page() const; private: font_iterator(int, document_private *dd); font_iterator_private *d; friend class document; friend class page; friend class page_private; }; } #endif poppler-24.02.0/cpp/poppler-global.cpp000066400000000000000000000244321455701731300175600ustar00rootroot00000000000000/* * Copyright (C) 2009-2010, Pino Toscano * Copyright (C) 2010, Hib Eris * Copyright (C) 2014, 2015 Hans-Peter Deifel * Copyright (C) 2015, Tamas Szekeres * Copyright (C) 2016 Jakub Alba * Copyright (C) 2018, 2020-2022, Albert Astals Cid * Copyright (C) 2018 Suzuki Toshiya * Copyright (C) 2018, 2020, Adam Reichold * Copyright (C) 2022, Oliver Sander * * 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. */ /** \file poppler-global.h */ #include "poppler-global.h" #include "poppler-private.h" #include "poppler-document-private.h" #include "DateInfo.h" #include #include #include #include #include #include #include "config.h" namespace { struct MiniIconv { MiniIconv(const char *to_code, const char *from_code) : i_(iconv_open(to_code, from_code)) { } ~MiniIconv() { if (is_valid()) { iconv_close(i_); } } MiniIconv(const MiniIconv &) = delete; MiniIconv &operator=(const MiniIconv &) = delete; bool is_valid() const { return i_ != (iconv_t)-1; } explicit operator iconv_t() const { return i_; } iconv_t i_; }; } using namespace poppler; /** \namespace poppler Single namespace containing all the classes and functions of poppler-cpp. */ /** \class poppler::noncopyable A class that cannot be copied. */ /** \enum poppler::rotation_enum The case sensitivity. */ /** \var poppler::rotation_enum poppler::rotate_0 A rotation of 0 degrees clockwise. */ /** \var poppler::rotation_enum poppler::rotate_90 A rotation of 90 degrees clockwise. */ /** \var poppler::rotation_enum poppler::rotate_180 A rotation of 180 degrees clockwise. */ /** \var poppler::rotation_enum poppler::rotate_270 A rotation of 270 degrees clockwise. */ /** \enum poppler::page_box_enum A possible box of a page in a PDF %document. */ /** \var poppler::page_box_enum poppler::media_box The "media" box. */ /** \var poppler::page_box_enum poppler::crop_box The "crop" box. */ /** \var poppler::page_box_enum poppler::bleed_box The "bleed" box. */ /** \var poppler::page_box_enum poppler::trim_box The "trim" box. */ /** \var poppler::page_box_enum poppler::art_box The "art" box. */ /** \enum poppler::permission_enum A possible permission in a PDF %document. */ /** \var poppler::permission_enum poppler::perm_print The permission to allow the print of a %document. */ /** \var poppler::permission_enum poppler::perm_change The permission to change a %document. This is a generic "change" permission, so other permissions could affect some types of changes. */ /** \var poppler::permission_enum poppler::perm_copy The permission to allow the copy or extraction of the text in a %document. */ /** \var poppler::permission_enum poppler::perm_add_notes The permission to allow the addition or editing of annotations, and the filling of interactive form fields (including signature fields). */ /** \var poppler::permission_enum poppler::perm_fill_forms The permission to allow the filling of interactive form fields (including signature fields). \note this permission can be set even when the \ref poppler::perm_add_notes "perm_add_notes" is not: this means that only the filling of forms is allowed. */ /** \var poppler::permission_enum poppler::perm_accessibility The permission to allow the extracting of content (for example, text) for accessibility usage (e.g. for a screen reader). */ /** \var poppler::permission_enum poppler::perm_assemble The permission to allow to "assemble" a %document. This implies operations such as the insertion, the rotation and the deletion of pages; the creation of bookmarks and thumbnail images. \note this permission can be set even when the \ref poppler::perm_change "perm_change" is not */ /** \var poppler::permission_enum poppler::perm_print_high_resolution The permission to allow the high resolution print of a %document. */ /** \enum poppler::case_sensitivity_enum The case sensitivity. */ noncopyable::noncopyable() { } noncopyable::~noncopyable() { } noncopyable &noncopyable::operator=(noncopyable &&other) noexcept = default; ustring::ustring() { } ustring::ustring(size_type len, value_type ch) : std::basic_string(len, ch) { } ustring::~ustring() { } byte_array ustring::to_utf8() const { if (!size()) { return byte_array(); } #ifdef WORDS_BIGENDIAN MiniIconv ic("UTF-8", "UTF-16BE"); #else MiniIconv ic("UTF-8", "UTF-16LE"); #endif if (!ic.is_valid()) { return byte_array(); } const value_type *me_data = data(); byte_array str(size() * sizeof(value_type)); char *str_data = &str[0]; size_t me_len_char = size() * sizeof(value_type); size_t str_len_left = str.size(); size_t ir = iconv(static_cast(ic), (ICONV_CONST char **)&me_data, &me_len_char, &str_data, &str_len_left); if ((ir == (size_t)-1) && (errno == E2BIG)) { const size_t delta = str_data - &str[0]; str_len_left += str.size(); str.resize(str.size() * 2); str_data = &str[delta]; ir = iconv(static_cast(ic), (ICONV_CONST char **)&me_data, &me_len_char, &str_data, &str_len_left); if (ir == (size_t)-1) { return byte_array(); } } str.resize(str.size() - str_len_left); return str; } std::string ustring::to_latin1() const { if (!size()) { return std::string(); } const size_type mylength = size(); std::string ret(mylength, '\0'); const value_type *me = data(); for (size_type i = 0; i < mylength; ++i) { ret[i] = (char)*me++; } return ret; } ustring ustring::from_utf8(const char *str, int len) { if (len <= 0) { len = std::strlen(str); if (len <= 0) { return ustring(); } } #ifdef WORDS_BIGENDIAN MiniIconv ic("UTF-16BE", "UTF-8"); #else MiniIconv ic("UTF-16LE", "UTF-8"); #endif if (!ic.is_valid()) { return ustring(); } // +1, because iconv inserts byte order marks ustring ret(len + 1, 0); char *ret_data = reinterpret_cast(&ret[0]); char *str_data = const_cast(str); size_t str_len_char = len; size_t ret_len_left = ret.size() * sizeof(ustring::value_type); size_t ir = iconv(static_cast(ic), (ICONV_CONST char **)&str_data, &str_len_char, &ret_data, &ret_len_left); if ((ir == (size_t)-1) && (errno == E2BIG)) { const size_t delta = ret_data - reinterpret_cast(&ret[0]); ret_len_left += ret.size() * sizeof(ustring::value_type); ret.resize(ret.size() * 2); ret_data = reinterpret_cast(&ret[0]) + delta; ir = iconv(static_cast(ic), (ICONV_CONST char **)&str_data, &str_len_char, &ret_data, &ret_len_left); if (ir == (size_t)-1) { return ustring(); } } ret.resize(ret.size() - ret_len_left / sizeof(ustring::value_type)); return ret; } ustring ustring::from_latin1(const std::string &str) { const size_type l = str.size(); if (!l) { return ustring(); } const char *c = str.data(); ustring ret(l, 0); for (size_type i = 0; i < l; ++i) { ret[i] = static_cast(*c); c++; } return ret; } /** Converts a string representing a PDF date to a value compatible with time_type. */ time_type poppler::convert_date(const std::string &date) { GooString gooDateStr(date.c_str()); return static_cast(dateStringToTime(&gooDateStr)); } /** Converts a string representing a PDF date to a value compatible with time_t. */ time_t poppler::convert_date_t(const std::string &date) { GooString gooDateStr(date.c_str()); return dateStringToTime(&gooDateStr); } std::ostream &poppler::operator<<(std::ostream &stream, const byte_array &array) { stream << "["; const std::ios_base::fmtflags f = stream.flags(); std::hex(stream); const char *data = &array[0]; const byte_array::size_type out_len = std::min(array.size(), 50); for (byte_array::size_type i = 0; i < out_len; ++i) { if (i != 0) { stream << " "; } stream << ((data[i] & 0xf0) >> 4) << (data[i] & 0xf); } stream.flags(f); if (out_len < array.size()) { stream << " ..."; } stream << "]"; return stream; } /** * Sets a custom data directory for initialization of global parameters * * If no instances of \see document currently exist, this will save the * given path as a custom data directory to be used when the first instance * of the \see document is constructed. * * \returns true on success, false on failure * * \since 0.73.0 */ bool poppler::set_data_dir(const std::string &new_data_dir) { return GlobalParamsIniter::setCustomDataDir(new_data_dir); } /** \typedef poppler::debug_func Debug/error function. This function type is used for debugging & error output; the first parameter is the actual message, the second is the unaltered closure argument which was passed to the set_debug_error_function() call. \since 0.30.0 */ /** Set a new debug/error output function. If not set, by default error and debug messages will be sent to stderr. \param debug_function the new debug function \param closure user data which will be passed as-is to the debug function \since 0.30.0 */ void poppler::set_debug_error_function(debug_func debug_function, void *closure) { poppler::detail::user_debug_function = debug_function; poppler::detail::debug_closure = closure; } poppler-24.02.0/cpp/poppler-global.h000066400000000000000000000066601455701731300172300ustar00rootroot00000000000000/* * Copyright (C) 2009-2010, Pino Toscano * Copyright (C) 2010, Patrick Spendrin * Copyright (C) 2014, Hans-Peter Deifel * Copyright (C) 2018, Adam Reichold * Copyright (C) 2021, 2022, Albert Astals Cid * Copyright (C) 2022, Tobias C. Berner * Copyright (C) 2022, Oliver Sander * * 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_GLOBAL_H #define POPPLER_GLOBAL_H #include "poppler_cpp_export.h" #include #include #include #include namespace poppler { /// \cond DOXYGEN_SKIP_THIS namespace detail { class POPPLER_CPP_EXPORT noncopyable { public: noncopyable(const noncopyable &) = delete; const noncopyable &operator=(const noncopyable &) = delete; protected: noncopyable(); ~noncopyable(); noncopyable &operator=(noncopyable &&other) noexcept; }; } typedef detail::noncopyable noncopyable; /// \endcond enum rotation_enum { rotate_0, rotate_90, rotate_180, rotate_270 }; enum page_box_enum { media_box, crop_box, bleed_box, trim_box, art_box }; enum permission_enum { perm_print, perm_change, perm_copy, perm_add_notes, perm_fill_forms, perm_accessibility, perm_assemble, perm_print_high_resolution }; enum case_sensitivity_enum { case_sensitive, case_insensitive }; typedef std::vector byte_array; typedef unsigned int /* time_t */ time_type; // to disable warning only for this occurrence #ifdef _MSC_VER # 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 { public: ustring(); ustring(size_type len, value_type ch); ~ustring(); byte_array to_utf8() const; std::string to_latin1() const; static ustring from_utf8(const char *str, int len = -1); static ustring from_latin1(const std::string &str); private: // forbid implicit std::string conversions explicit ustring(const std::string &); explicit operator std::string() const; ustring &operator=(const std::string &); }; #ifdef _MSC_VER # pragma warning(pop) #endif [[deprecated]] POPPLER_CPP_EXPORT time_type convert_date(const std::string &date); POPPLER_CPP_EXPORT time_t convert_date_t(const std::string &date); POPPLER_CPP_EXPORT std::ostream &operator<<(std::ostream &stream, const byte_array &array); POPPLER_CPP_EXPORT bool set_data_dir(const std::string &new_data_dir); typedef void (*debug_func)(const std::string &, void *); POPPLER_CPP_EXPORT void set_debug_error_function(debug_func debug_function, void *closure); } #endif poppler-24.02.0/cpp/poppler-image-private.h000066400000000000000000000030071455701731300205120ustar00rootroot00000000000000/* * Copyright (C) 2010, Pino Toscano * Copyright (C) 2018, 2022, 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 * 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_IMAGE_PRIVATE_H #define POPPLER_IMAGE_PRIVATE_H #include "poppler-image.h" namespace poppler { class image_private { public: image_private(int iwidth, int iheight, image::format_enum iformat); ~image_private(); image_private(const image_private &) = delete; image_private &operator=(const image_private &) = delete; static image_private *create_data(int width, int height, image::format_enum format); static image_private *create_data(char *data, int width, int height, image::format_enum format); int ref; char *data; int width; int height; int bytes_per_row; int bytes_num; image::format_enum format; bool own_data : 1; }; } #endif poppler-24.02.0/cpp/poppler-image.cpp000066400000000000000000000306751455701731300174100ustar00rootroot00000000000000/* * Copyright (C) 2010-2011, Pino Toscano * Copyright (C) 2013 Adrian Johnson * Copyright (C) 2017-2019, 2021, Albert Astals Cid * Copyright (C) 2017, Jeroen Ooms * Copyright (C) 2018, Zsombor Hollay-Horvath * Copyright (C) 2018, Adam Reichold * * 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. */ /** \file poppler-image.h */ #include "poppler-image.h" #include "poppler-image-private.h" #include #include "ImgWriter.h" #if defined(ENABLE_LIBPNG) # include "PNGWriter.h" #endif #if defined(ENABLE_LIBJPEG) # include "JpegWriter.h" #endif #if defined(ENABLE_LIBTIFF) # include "TiffWriter.h" #endif #include "NetPBMWriter.h" #include #include #include #include #include namespace { struct FileCloser { inline explicit FileCloser(FILE *ff) : f(ff) { } inline ~FileCloser() { (void)close(); } FileCloser(const FileCloser &) = delete; FileCloser &operator=(const FileCloser &) = delete; inline bool close() { if (f) { const int c = fclose(f); f = nullptr; return c == 0; } return true; } FILE *f; }; int calc_bytes_per_row(int width, poppler::image::format_enum format) { switch (format) { case poppler::image::format_invalid: return 0; case poppler::image::format_mono: return (width + 7) >> 3; case poppler::image::format_gray8: return (width + 3) >> 2 << 2; case poppler::image::format_rgb24: case poppler::image::format_bgr24: return (width * 3 + 3) >> 2 << 2; case poppler::image::format_argb32: return width * 4; } return 0; } NetPBMWriter::Format pnm_format(poppler::image::format_enum format) { switch (format) { case poppler::image::format_invalid: // unused, anyway case poppler::image::format_mono: return NetPBMWriter::MONOCHROME; case poppler::image::format_gray8: case poppler::image::format_rgb24: case poppler::image::format_bgr24: case poppler::image::format_argb32: return NetPBMWriter::RGB; } return NetPBMWriter::RGB; } } using namespace poppler; image_private::image_private(int iwidth, int iheight, image::format_enum iformat) : ref(1), data(nullptr), width(iwidth), height(iheight), bytes_per_row(0), bytes_num(0), format(iformat), own_data(true) { } image_private::~image_private() { if (own_data) { std::free(data); } } image_private *image_private::create_data(int width, int height, image::format_enum format) { if (width <= 0 || height <= 0) { return nullptr; } int bpr = calc_bytes_per_row(width, format); if (bpr <= 0) { return nullptr; } auto d = std::make_unique(width, height, format); d->bytes_num = bpr * height; d->data = reinterpret_cast(std::malloc(d->bytes_num)); if (!d->data) { return nullptr; } d->own_data = true; d->bytes_per_row = bpr; return d.release(); } image_private *image_private::create_data(char *data, int width, int height, image::format_enum format) { if (width <= 0 || height <= 0 || !data) { return nullptr; } int bpr = calc_bytes_per_row(width, format); if (bpr <= 0) { return nullptr; } image_private *d = new image_private(width, height, format); d->bytes_num = bpr * height; d->data = data; d->own_data = false; d->bytes_per_row = bpr; return d; } /** \class poppler::image poppler-image.h "poppler/cpp/poppler-image.h" A simple representation of image, with direct access to the data. This class uses implicit sharing for the internal data, so it can be used as value class. This also means any non-const operation will make sure that the data used by current instance is not shared with other instances (ie \em detaching), copying the shared data. \since 0.16 */ /** \enum poppler::image::format_enum The possible formats for an image. format_gray8 and format_bgr24 were introduced in poppler 0.65. */ /** Construct an invalid image. */ image::image() : d(nullptr) { } /** Construct a new image. It allocates the storage needed for the image data; if the allocation fails, the image is an invalid one. \param iwidth the width for the image \param iheight the height for the image \param iformat the format for the bits of the image */ image::image(int iwidth, int iheight, image::format_enum iformat) : d(image_private::create_data(iwidth, iheight, iformat)) { } /** Construct a new image. It uses the provide data buffer for the image, so you \b must ensure it remains valid for the whole lifetime of the image. \param idata the buffer to use for the image \param iwidth the width for the image \param iheight the height for the image \param iformat the format for the bits of the image */ image::image(char *idata, int iwidth, int iheight, image::format_enum iformat) : d(image_private::create_data(idata, iwidth, iheight, iformat)) { } /** Copy constructor. */ image::image(const image &img) : d(img.d) { if (d) { ++d->ref; } } /** Destructor. */ image::~image() { if (d && !--d->ref) { delete d; } } /** Image validity check. \returns whether the image is valid. */ bool image::is_valid() const { return d && d->format != format_invalid; } /** \returns the format of the image */ image::format_enum image::format() const { return d ? d->format : format_invalid; } /** \returns whether the width of the image */ int image::width() const { return d ? d->width : 0; } /** \returns whether the height of the image */ int image::height() const { return d ? d->height : 0; } /** \returns the number of bytes in each row of the image */ int image::bytes_per_row() const { return d ? d->bytes_per_row : 0; } /** Access to the image bits. This function will detach and copy the shared data. \returns the pointer to the first pixel */ char *image::data() { if (!d) { return nullptr; } detach(); return d->data; } /** Access to the image bits. This function provides const access to the data. \returns the pointer to the first pixel */ const char *image::const_data() const { return d ? d->data : nullptr; } /** Copy of a slice of the image. \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 */ image image::copy(const rect &r) const { if (r.is_empty()) { image img(*this); img.detach(); return img; } // ### FIXME return *this; } /** Saves the current image to file. Using this function it is possible to save the image to the specified \p file_name, in the specified \p out_format and with a resolution of the specified \p dpi. Image formats commonly supported are: \li PNG: \c png \li JPEG: \c jpeg, \c jpg \li TIFF: \c tiff \li PNM: \c pnm (with Poppler >= 0.18) If an image format is not supported (check the result of supported_image_formats()), the saving fails. \returns whether the saving succeeded */ bool image::save(const std::string &file_name, const std::string &out_format, int dpi) const { if (!is_valid() || file_name.empty() || out_format.empty()) { return false; } std::string fmt = out_format; std::transform(fmt.begin(), fmt.end(), fmt.begin(), tolower); std::unique_ptr w; const int actual_dpi = dpi == -1 ? 75 : dpi; if (false) { } #if defined(ENABLE_LIBPNG) else if (fmt == "png") { w = std::make_unique(); } #endif #if defined(ENABLE_LIBJPEG) else if (fmt == "jpeg" || fmt == "jpg") { w = std::make_unique(); } #endif #if defined(ENABLE_LIBTIFF) else if (fmt == "tiff") { w = std::make_unique(); } #endif else if (fmt == "pnm") { w = std::make_unique(pnm_format(d->format)); } if (!w.get()) { return false; } FILE *f = fopen(file_name.c_str(), "wb"); if (!f) { return false; } const FileCloser fc(f); if (!w->init(f, d->width, d->height, actual_dpi, actual_dpi)) { return false; } switch (d->format) { case format_invalid: return false; case format_mono: return false; case format_gray8: { std::vector row(3 * d->width); char *hptr = d->data; for (int y = 0; y < d->height; ++y) { unsigned char *rowptr = &row[0]; for (int x = 0; x < d->width; ++x, rowptr += 3) { rowptr[0] = *reinterpret_cast(hptr + x); rowptr[1] = *reinterpret_cast(hptr + x); rowptr[2] = *reinterpret_cast(hptr + x); } rowptr = &row[0]; if (!w->writeRow(&rowptr)) { return false; } hptr += d->bytes_per_row; } break; } case format_bgr24: { std::vector row(3 * d->width); char *hptr = d->data; for (int y = 0; y < d->height; ++y) { unsigned char *rowptr = &row[0]; for (int x = 0; x < d->width; ++x, rowptr += 3) { rowptr[0] = *reinterpret_cast(hptr + x * 3 + 2); rowptr[1] = *reinterpret_cast(hptr + x * 3 + 1); rowptr[2] = *reinterpret_cast(hptr + x * 3); } rowptr = &row[0]; if (!w->writeRow(&rowptr)) { return false; } hptr += d->bytes_per_row; } break; } case format_rgb24: { char *hptr = d->data; for (int y = 0; y < d->height; ++y) { if (!w->writeRow(reinterpret_cast(&hptr))) { return false; } hptr += d->bytes_per_row; } break; } case format_argb32: { std::vector row(3 * d->width); char *hptr = d->data; for (int y = 0; y < d->height; ++y) { unsigned char *rowptr = &row[0]; for (int x = 0; x < d->width; ++x, rowptr += 3) { const unsigned int pixel = *reinterpret_cast(hptr + x * 4); rowptr[0] = (pixel >> 16) & 0xff; rowptr[1] = (pixel >> 8) & 0xff; rowptr[2] = pixel & 0xff; } rowptr = &row[0]; if (!w->writeRow(&rowptr)) { return false; } hptr += d->bytes_per_row; } break; } } if (!w->close()) { return false; } return true; } /** \returns a list of the supported image formats */ std::vector image::supported_image_formats() { std::vector formats; #if defined(ENABLE_LIBPNG) formats.emplace_back("png"); #endif #if defined(ENABLE_LIBJPEG) formats.emplace_back("jpeg"); formats.emplace_back("jpg"); #endif #if defined(ENABLE_LIBTIFF) formats.emplace_back("tiff"); #endif formats.emplace_back("pnm"); return formats; } /** Assignment operator. */ image &image::operator=(const image &img) { if (this == &img) { return *this; } if (img.d) { ++img.d->ref; } image_private *old_d = d; d = img.d; if (old_d && !--old_d->ref) { delete old_d; } return *this; } void image::detach() { if (d->ref == 1) { return; } image_private *old_d = d; d = image_private::create_data(old_d->width, old_d->height, old_d->format); if (d) { std::memcpy(d->data, old_d->data, old_d->bytes_num); --old_d->ref; } else { d = old_d; } } poppler-24.02.0/cpp/poppler-image.h000066400000000000000000000036021455701731300170430ustar00rootroot00000000000000/* * Copyright (C) 2010, Pino Toscano * Copyright (C) 2018, Zsombor Hollay-Horvath * * 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_IMAGE_H #define POPPLER_IMAGE_H #include "poppler-global.h" #include "poppler-rectangle.h" namespace poppler { class image_private; class POPPLER_CPP_EXPORT image { public: enum format_enum { format_invalid, format_mono, format_rgb24, format_argb32, format_gray8, format_bgr24 }; image(); image(int iwidth, int iheight, format_enum iformat); image(char *idata, int iwidth, int iheight, format_enum iformat); image(const image &img); ~image(); bool is_valid() const; format_enum format() const; int width() const; int height() const; char *data(); const char *const_data() const; int bytes_per_row() const; image copy(const rect &r = rect()) const; bool save(const std::string &file_name, const std::string &out_format, int dpi = -1) const; static std::vector supported_image_formats(); image &operator=(const image &img); private: void detach(); image_private *d; friend class image_private; }; } #endif poppler-24.02.0/cpp/poppler-page-private.h000066400000000000000000000031321455701731300203430ustar00rootroot00000000000000/* * Copyright (C) 2009, Pino Toscano * Copyright (C) 2018, 2020, Albert Astals Cid * Copyright (C) 2020, Suzuki Toshiya * * 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_PAGE_PRIVATE_H #define POPPLER_PAGE_PRIVATE_H #include "poppler-page.h" class Page; namespace poppler { class document_private; class page_transition; class font_info; class page_private { public: page_private(document_private *doc, int index); ~page_private(); page_private(const page_private &) = delete; page_private &operator=(const page_private &) = delete; document_private *doc; Page *page; int index; page_transition *transition; static inline page_private *get(const poppler::page *p) { return const_cast(p)->d; } std::vector font_info_cache; bool font_info_cache_initialized; void init_font_info_cache(); }; } #endif poppler-24.02.0/cpp/poppler-page-renderer.cpp000066400000000000000000000170141455701731300210360ustar00rootroot00000000000000/* * Copyright (C) 2010, Pino Toscano * Copyright (C) 2015 William Bader * Copyright (C) 2018, Zsombor Hollay-Horvath * Copyright (C) 2019, Julián Unrrein * Copyright (C) 2020, Albert Astals Cid * Copyright (C) 2021, Hubert Figuiere * * 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. */ /** \file poppler-page-renderer.h */ #include "poppler-page-renderer.h" #include "poppler-document-private.h" #include "poppler-page-private.h" #include "poppler-image.h" #include #include #include "PDFDoc.h" #include "SplashOutputDev.h" #include "splash/SplashBitmap.h" using namespace poppler; class poppler::page_renderer_private { public: page_renderer_private() : paper_color(0xffffffff), hints(0), image_format(image::format_enum::format_argb32), line_mode(page_renderer::line_mode_enum::line_default) { } static bool conv_color_mode(image::format_enum mode, SplashColorMode &splash_mode); static bool conv_line_mode(page_renderer::line_mode_enum mode, SplashThinLineMode &splash_mode); argb paper_color; unsigned int hints; image::format_enum image_format; page_renderer::line_mode_enum line_mode; }; bool page_renderer_private::conv_color_mode(image::format_enum mode, SplashColorMode &splash_mode) { switch (mode) { case image::format_enum::format_mono: splash_mode = splashModeMono1; break; case image::format_enum::format_gray8: splash_mode = splashModeMono8; break; case image::format_enum::format_rgb24: splash_mode = splashModeRGB8; break; case image::format_enum::format_bgr24: splash_mode = splashModeBGR8; break; case image::format_enum::format_argb32: splash_mode = splashModeXBGR8; break; default: return false; } return true; } bool page_renderer_private::conv_line_mode(page_renderer::line_mode_enum mode, SplashThinLineMode &splash_mode) { switch (mode) { case page_renderer::line_mode_enum::line_default: splash_mode = splashThinLineDefault; break; case page_renderer::line_mode_enum::line_solid: splash_mode = splashThinLineSolid; break; case page_renderer::line_mode_enum::line_shape: splash_mode = splashThinLineShape; break; default: return false; } return true; } /** \class poppler::page_renderer poppler-page-renderer.h "poppler/cpp/poppler-renderer.h" Simple way to render a page of a PDF %document. \since 0.16 */ /** \enum poppler::page_renderer::render_hint A flag of an option taken into account when rendering */ /** Constructs a new %page renderer. */ page_renderer::page_renderer() : d(new page_renderer_private()) { } /** Destructor. */ page_renderer::~page_renderer() { delete d; } /** The color used for the "paper" of the pages. The default color is opaque solid white (0xffffffff). \returns the paper color */ argb page_renderer::paper_color() const { return d->paper_color; } /** Set a new color for the "paper". \param c the new color */ void page_renderer::set_paper_color(argb c) { d->paper_color = c; } /** The hints used when rendering. By default no hint is set. \returns the render hints set */ unsigned int page_renderer::render_hints() const { return d->hints; } /** Enable or disable a single render %hint. \param hint the hint to modify \param on whether enable it or not */ void page_renderer::set_render_hint(page_renderer::render_hint hint, bool on) { if (on) { d->hints |= hint; } else { d->hints &= ~(int)hint; } } /** Set new render %hints at once. \param hints the new set of render hints */ void page_renderer::set_render_hints(unsigned int hints) { d->hints = hints; } /** The image format used when rendering. By default ARGB32 is set. \returns the image format \since 0.65 */ image::format_enum page_renderer::image_format() const { return d->image_format; } /** Set new image format used when rendering. \param format the new image format \since 0.65 */ void page_renderer::set_image_format(image::format_enum format) { d->image_format = format; } /** The line mode used when rendering. By default default mode is set. \returns the line mode \since 0.65 */ page_renderer::line_mode_enum page_renderer::line_mode() const { return d->line_mode; } /** Set new line mode used when rendering. \param mode the new line mode \since 0.65 */ void page_renderer::set_line_mode(page_renderer::line_mode_enum mode) { d->line_mode = mode; } /** Render the specified page. This functions renders the specified page on an image following the specified parameters, returning it. \param p the page to render \param xres the X resolution, in dot per inch (DPI) \param yres the Y resolution, in dot per inch (DPI) \param x the X top-right coordinate, in pixels \param y the Y top-right coordinate, in pixels \param w the width in pixels of the area to render \param h the height in pixels of the area to render \param rotate the rotation to apply when rendering the page \returns the rendered image, or a null one in case of errors \see can_render */ image page_renderer::render_page(const page *p, double xres, double yres, int x, int y, int w, int h, rotation_enum rotate) const { if (!p) { return image(); } page_private *pp = page_private::get(p); PDFDoc *pdfdoc = pp->doc->doc; SplashColorMode colorMode; SplashThinLineMode lineMode; if (!d->conv_color_mode(d->image_format, colorMode) || !d->conv_line_mode(d->line_mode, lineMode)) { return image(); } SplashColor bgColor; bgColor[0] = d->paper_color & 0xff; bgColor[1] = (d->paper_color >> 8) & 0xff; bgColor[2] = (d->paper_color >> 16) & 0xff; SplashOutputDev splashOutputDev(colorMode, 4, false, bgColor, true, lineMode); splashOutputDev.setFontAntialias(d->hints & text_antialiasing ? true : false); splashOutputDev.setVectorAntialias(d->hints & antialiasing ? true : false); splashOutputDev.setFreeTypeHinting(d->hints & text_hinting ? true : false, false); splashOutputDev.startDoc(pdfdoc); pdfdoc->displayPageSlice(&splashOutputDev, pp->index + 1, xres, yres, int(rotate) * 90, false, true, false, x, y, w, h, nullptr, nullptr, nullptr, nullptr, true); SplashBitmap *bitmap = splashOutputDev.getBitmap(); const int bw = bitmap->getWidth(); const int bh = bitmap->getHeight(); SplashColorPtr data_ptr = bitmap->getDataPtr(); const image img(reinterpret_cast(data_ptr), bw, bh, d->image_format); return img.copy(); } /** Rendering capability test. page_renderer can render only if a render backend ('Splash') is compiled in Poppler. \returns whether page_renderer can render */ bool page_renderer::can_render() { return true; } poppler-24.02.0/cpp/poppler-page-renderer.h000066400000000000000000000040671455701731300205070ustar00rootroot00000000000000/* * Copyright (C) 2010, Pino Toscano * Copyright (C) 2018, Zsombor Hollay-Horvath * * 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_PAGE_RENDERER_H #define POPPLER_PAGE_RENDERER_H #include "poppler-global.h" #include "poppler-image.h" namespace poppler { typedef unsigned int argb; class page; class page_renderer_private; class POPPLER_CPP_EXPORT page_renderer : public poppler::noncopyable { public: enum render_hint { antialiasing = 0x00000001, text_antialiasing = 0x00000002, text_hinting = 0x00000004 }; enum line_mode_enum { line_default, line_solid, line_shape }; page_renderer(); ~page_renderer(); argb paper_color() const; void set_paper_color(argb c); unsigned int render_hints() const; void set_render_hint(render_hint hint, bool on = true); void set_render_hints(unsigned int hints); image::format_enum image_format() const; void set_image_format(image::format_enum format); line_mode_enum line_mode() const; void set_line_mode(line_mode_enum mode); image render_page(const page *p, double xres = 72.0, double yres = 72.0, int x = -1, int y = -1, int w = -1, int h = -1, rotation_enum rotate = rotate_0) const; static bool can_render(); private: page_renderer_private *d; friend class page_renderer_private; }; } #endif poppler-24.02.0/cpp/poppler-page-transition.cpp000066400000000000000000000056321455701731300214250ustar00rootroot00000000000000/* * Copyright (C) 2009-2010, Pino Toscano * Copyright (C) 2011, 2021, 2022, 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 * 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. */ /** \file poppler-page-transition.h */ #include "poppler-page-transition.h" #include "PageTransition.h" using namespace poppler; class poppler::page_transition_private { public: explicit page_transition_private(Object *trans) : pt(trans) { } PageTransition pt; }; /** \class poppler::page_transition poppler-page-transition.h "poppler/cpp/poppler-page-transition.h" A transition between two pages in a PDF %document. Usually shown in a presentation mode of a PDF viewer. */ /** \enum poppler::page_transition::type_enum The possible types of a %page transition. */ /** \enum poppler::page_transition::alignment_enum The alignment of a %page transition. */ /** \enum poppler::page_transition::direction_enum The direction of an animation in a %page transition. */ page_transition::page_transition(Object *params) : d(new page_transition_private(params)) { } /** Copy constructor. */ page_transition::page_transition(const page_transition &pt) : d(new page_transition_private(*pt.d)) { } /** Destructor. */ page_transition::~page_transition() { delete d; } page_transition::type_enum page_transition::type() const { return (page_transition::type_enum)d->pt.getType(); } int page_transition::duration() const { return static_cast(d->pt.getDuration()); } double page_transition::durationReal() const { return d->pt.getDuration(); } page_transition::alignment_enum page_transition::alignment() const { return (page_transition::alignment_enum)d->pt.getAlignment(); } page_transition::direction_enum page_transition::direction() const { return (page_transition::direction_enum)d->pt.getDirection(); } int page_transition::angle() const { return d->pt.getAngle(); } double page_transition::scale() const { return d->pt.getScale(); } bool page_transition::is_rectangular() const { return d->pt.isRectangular(); } page_transition &page_transition::operator=(const page_transition &pt) { if (&pt != this) { page_transition_private *new_d = new page_transition_private(*pt.d); delete d; d = new_d; } return *this; } poppler-24.02.0/cpp/poppler-page-transition.h000066400000000000000000000036351455701731300210730ustar00rootroot00000000000000/* * Copyright (C) 2009, Pino Toscano * Copyright (C) 2021, 2022, 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 * 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_PAGE_TRANSITION_H #define POPPLER_PAGE_TRANSITION_H #include "poppler-global.h" class Object; namespace poppler { class page; class page_transition_private; class POPPLER_CPP_EXPORT page_transition { public: enum type_enum { replace = 0, split, blinds, box, wipe, dissolve, glitter, fly, push, cover, uncover, fade }; enum alignment_enum { horizontal = 0, vertical }; enum direction_enum { inward = 0, outward }; page_transition(const page_transition &pt); ~page_transition(); type_enum type() const; [[deprecated]] int duration() const; double durationReal() const; alignment_enum alignment() const; direction_enum direction() const; int angle() const; double scale() const; bool is_rectangular() const; page_transition &operator=(const page_transition &pt); private: explicit page_transition(Object *params); page_transition_private *d; friend class page; }; } #endif poppler-24.02.0/cpp/poppler-page.cpp000066400000000000000000000340221455701731300172300ustar00rootroot00000000000000/* * Copyright (C) 2009-2010, Pino Toscano * Copyright (C) 2017-2020, Albert Astals Cid * Copyright (C) 2017, Jason Alan Palmer * Copyright (C) 2018, 2020, Suzuki Toshiya * Copyright (C) 2018, 2020, Adam Reichold * Copyright (C) 2018, Zsombor Hollay-Horvath * Copyright (C) 2018, Aleksey Nikolaev * Copyright (C) 2020, Jiri Jakes * * 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. */ /** \file poppler-page.h */ #include "poppler-page.h" #include "poppler-page-transition.h" #include "poppler-document-private.h" #include "poppler-page-private.h" #include "poppler-private.h" #include "poppler-font-private.h" #include "poppler-font.h" #include "TextOutputDev.h" #include #include #include using namespace poppler; page_private::page_private(document_private *_doc, int _index) : doc(_doc), page(doc->doc->getCatalog()->getPage(_index + 1)), index(_index), transition(nullptr), font_info_cache_initialized(false) { } page_private::~page_private() { delete transition; } void page_private::init_font_info_cache() { if (font_info_cache_initialized) { return; } poppler::font_iterator it(index, doc); if (it.has_next()) { font_info_cache = it.next(); } font_info_cache_initialized = true; return; } /** \class poppler::page poppler-page.h "poppler/cpp/poppler-page.h" A page in a PDF %document. */ /** \enum poppler::page::orientation_enum The possible orientation of a page. */ /** \enum poppler::page::search_direction_enum The direction/action to follow when performing a text search. */ /** \enum poppler::page::text_layout_enum A layout of the text of a page. */ page::page(document_private *doc, int index) : d(new page_private(doc, index)) { } /** Destructor. */ page::~page() { delete d; } /** \returns the orientation of the page */ page::orientation_enum page::orientation() const { const int rotation = d->page->getRotate(); switch (rotation) { case 90: return landscape; break; case 180: return upside_down; break; case 270: return seascape; break; default: return portrait; } } /** The eventual duration the page can be hinted to be shown in a presentation. If this value is positive (usually different than -1) then a PDF viewer, when showing the page in a presentation, should show the page for at most for this number of seconds, and then switch to the next page (if any). Note this is purely a presentation attribute, it has no influence on the behaviour. \returns the duration time (in seconds) of the page */ double page::duration() const { return d->page->getDuration(); } /** Returns the size of one rect of the page. \returns the size of the specified page rect */ rectf page::page_rect(page_box_enum box) const { const PDFRectangle *r = nullptr; switch (box) { case media_box: r = d->page->getMediaBox(); break; case crop_box: r = d->page->getCropBox(); break; case bleed_box: r = d->page->getBleedBox(); break; case trim_box: r = d->page->getTrimBox(); break; case art_box: r = d->page->getArtBox(); break; } if (r) { return detail::pdfrectangle_to_rectf(*r); } return rectf(); } /** \returns the label of the page, if any */ ustring page::label() const { GooString goo; if (!d->doc->doc->getCatalog()->indexToLabel(d->index, &goo)) { return ustring(); } return detail::unicode_GooString_to_ustring(&goo); } /** The transition from this page to the next one. If it is set, then a PDF viewer in a presentation should perform the specified transition effect when switching from this page to the next one. \returns the transition effect for the switch to the next page, if any */ page_transition *page::transition() const { if (!d->transition) { Object o = d->page->getTrans(); if (o.isDict()) { d->transition = new page_transition(&o); } } return d->transition; } /** Search the page for some text. \param text the text to search \param[in,out] r the area where to start search, which will be set to the area of the match (if any) \param direction in which direction search for text \param case_sensitivity whether search in a case sensitive way \param rotation the rotation assumed for the page */ bool page::search(const ustring &text, rectf &r, search_direction_enum direction, case_sensitivity_enum case_sensitivity, rotation_enum rotation) const { const size_t len = text.length(); if (len == 0) { return false; } std::vector u(len); for (size_t i = 0; i < len; ++i) { u[i] = text[i]; } const bool sCase = case_sensitivity == case_sensitive; const int rotation_value = (int)rotation * 90; bool found = false; double rect_left = r.left(); double rect_top = r.top(); double rect_right = r.right(); double rect_bottom = r.bottom(); TextOutputDev td(nullptr, true, 0, false, false); d->doc->doc->displayPage(&td, d->index + 1, 72, 72, rotation_value, false, true, false); TextPage *text_page = td.takeText(); switch (direction) { case search_from_top: found = text_page->findText(&u[0], len, true, true, false, false, sCase, false, false, &rect_left, &rect_top, &rect_right, &rect_bottom); break; case search_next_result: found = text_page->findText(&u[0], len, false, true, true, false, sCase, false, false, &rect_left, &rect_top, &rect_right, &rect_bottom); break; case search_previous_result: found = text_page->findText(&u[0], len, false, true, true, false, sCase, true, false, &rect_left, &rect_top, &rect_right, &rect_bottom); break; } text_page->decRefCnt(); r.set_left(rect_left); r.set_top(rect_top); r.set_right(rect_right); r.set_bottom(rect_bottom); return found; } /** Returns the text in the page, in its physical layout. \param r if not empty, it will be extracted the text in it; otherwise, the text of the whole page \returns the text of the page in the specified rect or in the whole page */ ustring page::text(const rectf &r) const { return text(r, physical_layout); } static void appendToGooString(void *stream, const char *text, int len) { ((GooString *)stream)->append(text, len); } /** Returns the text in the page. \param rect if not empty, it will be extracted the text in it; otherwise, the text of the whole page \param layout_mode the layout of the text \returns the text of the page in the specified rect or in the whole page \since 0.16 */ ustring page::text(const rectf &r, text_layout_enum layout_mode) const { std::unique_ptr out(new GooString()); const bool use_raw_order = (layout_mode == raw_order_layout); const bool use_physical_layout = (layout_mode == physical_layout); TextOutputDev td(&appendToGooString, out.get(), use_physical_layout, 0, use_raw_order, false); if (r.is_empty()) { d->doc->doc->displayPage(&td, d->index + 1, 72, 72, 0, false, true, false); } else { d->doc->doc->displayPageSlice(&td, d->index + 1, 72, 72, 0, false, true, false, r.left(), r.top(), r.width(), r.height()); } return ustring::from_utf8(out->c_str()); } /* * text_box_font_info object for text_box */ text_box_font_info_data::~text_box_font_info_data() = default; /* * text_box object for page::text_list() */ text_box_data::~text_box_data() = default; text_box::~text_box() = default; text_box &text_box::operator=(text_box &&a) noexcept = default; text_box::text_box(text_box &&a) noexcept = default; text_box::text_box(text_box_data *data) : m_data { data } { } ustring text_box::text() const { return m_data->text; } rectf text_box::bbox() const { return m_data->bbox; } int text_box::rotation() const { return m_data->rotation; } rectf text_box::char_bbox(size_t i) const { if (i < m_data->char_bboxes.size()) { return m_data->char_bboxes[i]; } return rectf(0, 0, 0, 0); } bool text_box::has_space_after() const { return m_data->has_space_after; } bool text_box::has_font_info() const { return (m_data->text_box_font != nullptr); } text_box::writing_mode_enum text_box::get_wmode(int i) const { if (this->has_font_info()) { return m_data->text_box_font->wmodes[i]; } else { return text_box::invalid_wmode; } } double text_box::get_font_size() const { if (this->has_font_info()) { return m_data->text_box_font->font_size; } else { return -1; } } std::string text_box::get_font_name(int i) const { if (!this->has_font_info()) { return std::string("*ignored*"); } int j = m_data->text_box_font->glyph_to_cache_index[i]; if (j < 0) { return std::string(""); } return m_data->text_box_font->font_info_cache[j].name(); } std::vector page::text_list(int opt_flag) const { std::vector output_list; /* config values are same with Qt5 Page::TextList() */ auto output_dev = std::make_unique(nullptr, /* char* fileName */ false, /* bool physLayoutA */ 0, /* double fixedPitchA */ false, /* bool rawOrderA */ false /* bool append */ ); /* * config values are same with Qt5 Page::TextList(), * but rotation is fixed to zero. * Few people use non-zero values. */ d->doc->doc->displayPageSlice(output_dev.get(), d->index + 1, /* page */ 72, 72, 0, /* hDPI, vDPI, rot */ false, false, false, /* useMediaBox, crop, printing */ -1, -1, -1, -1, /* sliceX, sliceY, sliceW, sliceH */ nullptr, nullptr, /* abortCheckCbk(), abortCheckCbkData */ nullptr, nullptr, /* annotDisplayDecideCbk(), annotDisplayDecideCbkData */ true); /* copyXRef */ if (std::unique_ptr word_list { output_dev->makeWordList() }) { output_list.reserve(word_list->getLength()); for (int i = 0; i < word_list->getLength(); i++) { TextWord *word = word_list->get(i); std::unique_ptr gooWord { word->getText() }; ustring ustr = ustring::from_utf8(gooWord->c_str()); double xMin, yMin, xMax, yMax; word->getBBox(&xMin, &yMin, &xMax, &yMax); text_box tb { new text_box_data { ustr, { xMin, yMin, xMax - xMin, yMax - yMin }, word->getRotation(), {}, word->hasSpaceAfter() == true, nullptr } }; std::unique_ptr tb_font_info = nullptr; if (opt_flag & page::text_list_include_font) { d->init_font_info_cache(); std::unique_ptr tb_font { new text_box_font_info_data { word->getFontSize(), // double font_size {}, // std::vector wmodes; d->font_info_cache, // std::vector font_info_cache; {} // std::vector glyph_to_cache_index; } }; tb_font_info = std::move(tb_font); }; tb.m_data->char_bboxes.reserve(word->getLength()); for (int j = 0; j < word->getLength(); j++) { word->getCharBBox(j, &xMin, &yMin, &xMax, &yMax); tb.m_data->char_bboxes.emplace_back(xMin, yMin, xMax - xMin, yMax - yMin); } if (tb_font_info && d->font_info_cache_initialized) { tb_font_info->glyph_to_cache_index.reserve(word->getLength()); for (int j = 0; j < word->getLength(); j++) { const TextFontInfo *cur_text_font_info = word->getFontInfo(j); // filter-out the invalid WMode value here. switch (cur_text_font_info->getWMode()) { case 0: tb_font_info->wmodes.push_back(text_box::horizontal_wmode); break; case 1: tb_font_info->wmodes.push_back(text_box::vertical_wmode); break; default: tb_font_info->wmodes.push_back(text_box::invalid_wmode); }; tb_font_info->glyph_to_cache_index.push_back(-1); for (size_t k = 0; k < tb_font_info->font_info_cache.size(); k++) { if (cur_text_font_info->matches(&(tb_font_info->font_info_cache[k].d->ref))) { tb_font_info->glyph_to_cache_index[j] = k; break; } } } tb.m_data->text_box_font = std::move(tb_font_info); } output_list.push_back(std::move(tb)); } } return output_list; } std::vector page::text_list() const { return text_list(0); } poppler-24.02.0/cpp/poppler-page.h000066400000000000000000000135161455701731300167020ustar00rootroot00000000000000/* * Copyright (C) 2009-2010, Pino Toscano * Copyright (C) 2018, 2020, Suzuki Toshiya * Copyright (C) 2018-2022, Albert Astals Cid * Copyright (C) 2018, Zsombor Hollay-Horvath * Copyright (C) 2018, Aleksey Nikolaev * Copyright (C) 2020, Jiri Jakes * Copyright (C) 2020, Adam Reichold * * 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_PAGE_H #define POPPLER_PAGE_H #include "poppler-global.h" #include "poppler-rectangle.h" #include namespace poppler { struct text_box_data; class POPPLER_CPP_EXPORT text_box { friend class page; public: text_box(text_box &&) noexcept; text_box &operator=(text_box &&) noexcept; ~text_box(); ustring text() const; rectf bbox() const; /** \since 0.68 */ int rotation() const; /** Get a bbox for the i-th glyph This method returns a rectf of the bounding box for the i-th glyph in the text_box. \note The text_box object owns the rectf objects, the caller is not needed to free them. \warning For too large glyph index, rectf(0,0,0,0) is returned. The number of the glyphs and ustring codepoints might be different in some complex scripts. */ rectf char_bbox(size_t i) const; bool has_space_after() const; /** \since 0.89 */ bool has_font_info() const; /** Get a writing mode for the i-th glyph This method returns an enum of the writing mode for the i-th glyph in the text_box. \note Usually all glyphs in one text_box have the same writing mode. Thus the default value of the glyph index is 0. */ enum writing_mode_enum { invalid_wmode = -1, horizontal_wmode = 0, vertical_wmode = 1 }; /** \since 0.89 */ writing_mode_enum get_wmode(int i = 0) const; /** Get a font size of this text_box instance. This method return a double floating value of the font size from the text_box instance. */ /** \since 0.89 */ double get_font_size() const; /** Get a font name for the i-th glyph This method returns a std::string object holding the font name for the i-th glyph. \note The randomization prefix of the embedded fonts are not removed. The font names including these prefixes are insuffucient to determine whether the two fonts are same or different. \note The clients should not assume that the encoding of the font name is one of the ASCII, Latin1 or UTF-8. Some legacy PDF producers used in CJK market use GBK, Big5, Wansung or Shift-JIS. */ /** \since 0.89 */ std::string get_font_name(int i = 0) const; private: explicit text_box(text_box_data *data); std::unique_ptr m_data; }; class document; class document_private; class page_private; class page_transition; class POPPLER_CPP_EXPORT page : public poppler::noncopyable { public: enum orientation_enum { landscape, portrait, seascape, upside_down }; enum search_direction_enum { search_from_top, search_next_result, search_previous_result }; enum text_layout_enum { physical_layout, raw_order_layout, non_raw_non_physical_layout ///< \since 0.88 }; ~page(); orientation_enum orientation() const; double duration() const; rectf page_rect(page_box_enum box = crop_box) const; ustring label() const; page_transition *transition() const; bool search(const ustring &text, rectf &r, search_direction_enum direction, case_sensitivity_enum case_sensitivity, rotation_enum rotation = rotate_0) const; ustring text(const rectf &r = rectf()) const; ustring text(const rectf &r, text_layout_enum layout_mode) const; /** Returns a list of text of the page This method returns a std::vector of text_box that contain all the text of the page, with roughly one text word of text per text_box item. For text written in western languages (left-to-right and up-to-down), the std::vector contains the text in the proper order. \since 0.63 \note The page object owns the text_box objects as unique_ptr, the caller is not needed to free them. \warning This method is not tested with Asian scripts */ std::vector text_list() const; /* * text_list_option_enum is a bitmask-style flags for text_list(), * 0 means the default & simplest behaviour. */ enum text_list_option_enum { text_list_include_font = 1 // \since 0.89 }; /** Extended version of text_list() taking an option flag. The option flag should be the multiple of text_list_option_enum. \since 0.89 */ std::vector text_list(int opt_flag) const; private: page(document_private *doc, int index); page_private *d; friend class page_private; friend class document; }; } #endif poppler-24.02.0/cpp/poppler-private.cpp000066400000000000000000000070071455701731300177710ustar00rootroot00000000000000/* * Copyright (C) 2009-2010, Pino Toscano * Copyright (C) 2013 Adrian Johnson * Copyright (C) 2014, Hans-Peter Deifel * Copyright (C) 2016 Jakub Alba * Copyright (C) 2017-2019 Albert Astals Cid * Copyright (C) 2018 Suzuki Toshiya * Copyright (C) 2020 Adam Reichold * * 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 "poppler-private.h" #include "GooString.h" #include "Page.h" #include #include #include using namespace poppler; static void stderr_debug_function(const std::string &msg, void * /*data*/) { std::cerr << "poppler/" << msg << std::endl; } debug_func detail::user_debug_function = stderr_debug_function; void *detail::debug_closure = nullptr; void detail::error_function(ErrorCategory /*category*/, Goffset pos, const char *msg) { std::ostringstream oss; if (pos >= 0) { oss << "error (" << pos << "): "; } else { oss << "error: "; } oss << msg; detail::user_debug_function(oss.str(), detail::debug_closure); } rectf detail::pdfrectangle_to_rectf(const PDFRectangle &pdfrect) { return rectf(pdfrect.x1, pdfrect.y1, pdfrect.x2 - pdfrect.x1, pdfrect.y2 - pdfrect.y1); } 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; int i = is_unicode ? 2 : 0; ustring::size_type ret_len = len - i; if (is_unicode) { ret_len >>= 1; } ustring ret(ret_len, 0); size_t ret_index = 0; ustring::value_type u; if (is_unicode) { while (i < len) { u = is_unicodeLE ? ((data[i + 1] & 0xff) << 8) | (data[i] & 0xff) : ((data[i] & 0xff) << 8) | (data[i + 1] & 0xff); i += 2; ret[ret_index++] = u; } } else { while (i < len) { u = data[i] & 0xff; ++i; ret[ret_index++] = u; } } return ret; } ustring detail::unicode_to_ustring(const Unicode *u, int length) { ustring str(length, 0); ustring::iterator it = str.begin(); const Unicode *uu = u; for (int i = 0; i < length; ++i) { *it++ = ustring::value_type(*uu++ & 0xffff); } return str; } GooString *detail::ustring_to_unicode_GooString(const ustring &str) { const size_t len = str.size() * 2 + 2; const ustring::value_type *me = str.data(); byte_array ba(len); ba[0] = (char)0xfe; ba[1] = (char)0xff; for (size_t i = 0; i < str.size(); ++i, ++me) { ba[i * 2 + 2] = ((*me >> 8) & 0xff); ba[i * 2 + 3] = (*me & 0xff); } GooString *goo = new GooString(&ba[0], len); return goo; } poppler-24.02.0/cpp/poppler-private.h000066400000000000000000000063001455701731300174310ustar00rootroot00000000000000/* * Copyright (C) 2009, Pino Toscano * Copyright (C) 2013 Adrian Johnson * Copyright (C) 2014, Hans-Peter Deifel * 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, Zsombor Hollay-Horvath * * 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_PRIVATE_H #define POPPLER_PRIVATE_H #include "poppler-global.h" #include "poppler-rectangle.h" #include "poppler-page.h" // to use text_box::writing_mode_enum #include "Error.h" #include "CharTypes.h" #include class GooString; class PDFRectangle; #define PSTR(str) const_cast(str) namespace poppler { namespace detail { extern debug_func user_debug_function; extern void *debug_closure; void error_function(ErrorCategory category, Goffset pos, const char *msg); 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); } template void delete_all(ConstIterator it, ConstIterator end) { while (it != end) { delete *it++; } } template void delete_all(const Collection &c) { delete_all(c.begin(), c.end()); } class font_info; struct text_box_font_info_data { ~text_box_font_info_data(); double font_size; std::vector wmodes; /* * a duplication of the font_info_cache created by the * poppler::font_iterator and owned by the poppler::page * object. Its lifetime might differ from that of text_box * object (think about collecting all text_box objects * from all pages), so we have to duplicate it into all * text_box instances. */ std::vector font_info_cache; /* * a std::vector from the glyph index in the owner * text_box to the font_info index in font_info_cache. * The "-1" means no corresponding fonts found in the * cache. */ std::vector glyph_to_cache_index; }; class font_info; struct text_box_data { ~text_box_data(); ustring text; rectf bbox; int rotation; std::vector char_bboxes; bool has_space_after; std::unique_ptr text_box_font; }; } #endif poppler-24.02.0/cpp/poppler-rectangle.cpp000066400000000000000000000030101455701731300202510ustar00rootroot00000000000000/* * Copyright (C) 2009-2010, Pino Toscano * * 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. */ /** \file poppler-rectangle.h */ #include "poppler-rectangle.h" #include using namespace poppler; /** \class poppler::rectangle poppler-rectangle.h "poppler/cpp/poppler-rectangle.h" A rectangle. */ /** \typedef poppler::rect A rectangle with int dimensions and coordinates. */ /** \typedef poppler::rectf A rectangle with float (double) dimensions and coordinates. */ std::ostream &poppler::operator<<(std::ostream &stream, const rect &r) { stream << "[" << r.x() << "," << r.y() << " " << r.width() << "+" << r.height() << "]"; return stream; } std::ostream &poppler::operator<<(std::ostream &stream, const rectf &r) { stream << "[" << r.x() << "," << r.y() << " " << r.width() << "+" << r.height() << "]"; return stream; } poppler-24.02.0/cpp/poppler-rectangle.h000066400000000000000000000035201455701731300177240ustar00rootroot00000000000000/* * Copyright (C) 2009-2010, Pino Toscano * * 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_RECTANGLE_H #define POPPLER_RECTANGLE_H #include "poppler-global.h" namespace poppler { template 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() { } bool is_empty() const { return (x1 == x2) && (y1 == y2); } T x() const { return x1; } T y() const { return y1; } T width() const { return x2 - x1; } T height() const { return y2 - y1; } T left() const { return x1; } T top() const { return y1; } T right() const { return x2; } T bottom() const { return y2; } void set_left(T value) { x1 = value; } void set_top(T value) { y1 = value; } void set_right(T value) { x2 = value; } void set_bottom(T value) { y2 = value; } private: T x1, y1, x2, y2; }; typedef rectangle rect; typedef rectangle rectf; POPPLER_CPP_EXPORT std::ostream &operator<<(std::ostream &stream, const rect &r); POPPLER_CPP_EXPORT std::ostream &operator<<(std::ostream &stream, const rectf &r); } #endif poppler-24.02.0/cpp/poppler-toc-private.h000066400000000000000000000031351455701731300202170ustar00rootroot00000000000000/* * Copyright (C) 2009, Pino Toscano * Copyright (C) 2018, Albert Astals Cid * Copyright (C) 2019, Oliver Sander * * 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_TOC_PRIVATE_H #define POPPLER_TOC_PRIVATE_H #include "poppler-global.h" #include "poppler-toc.h" #include class Outline; class OutlineItem; namespace poppler { class toc_private { public: toc_private(); ~toc_private(); static toc *load_from_outline(Outline *outline); toc_item root; }; class toc_item_private { public: toc_item_private(); ~toc_item_private(); toc_item_private(const toc_item_private &) = delete; toc_item_private &operator=(const toc_item_private &) = delete; void load(const OutlineItem *item); void load_children(const std::vector *items); std::vector children; ustring title; bool is_open; }; } #endif poppler-24.02.0/cpp/poppler-toc.cpp000066400000000000000000000106401455701731300171010ustar00rootroot00000000000000/* * Copyright (C) 2009-2010, Pino Toscano * Copyright (C) 2018, 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 * 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. */ /** \file poppler-toc.h */ #include "poppler-toc.h" #include "poppler-toc-private.h" #include "poppler-private.h" #include "Outline.h" using namespace poppler; toc_private::toc_private() { } toc_private::~toc_private() { } toc *toc_private::load_from_outline(Outline *outline) { if (!outline) { return nullptr; } const std::vector *items = outline->getItems(); if (!items || items->size() < 1) { return nullptr; } toc *newtoc = new toc(); newtoc->d->root.d->is_open = true; newtoc->d->root.d->load_children(items); return newtoc; } toc_item_private::toc_item_private() : is_open(false) { } toc_item_private::~toc_item_private() { delete_all(children); } void toc_item_private::load(const OutlineItem *item) { const std::vector &title_unicode = item->getTitle(); title = detail::unicode_to_ustring(title_unicode.data(), title_unicode.size()); is_open = item->isOpen(); } void toc_item_private::load_children(const std::vector *items) { const int num_items = items->size(); children.resize(num_items); for (int i = 0; i < num_items; ++i) { OutlineItem *item = (*items)[i]; toc_item *new_item = new toc_item(); new_item->d->load(item); children[i] = new_item; item->open(); const std::vector *item_children = item->getKids(); if (item_children) { new_item->d->load_children(item_children); } } } /** \class poppler::toc poppler-toc.h "poppler/cpp/poppler-toc.h" Represents the TOC (Table of Contents) of a PDF %document. The TOC of a PDF %document is represented as a tree of items. */ toc::toc() : d(new toc_private()) { } /** Destroys the TOC. */ toc::~toc() { delete d; } /** Returns the "invisible item" representing the root of the TOC. This item is special, it has no title nor actions, it is open and its children are the effective root items of the TOC. This is provided as a convenience when iterating through the TOC. \returns the root "item" */ toc_item *toc::root() const { return &d->root; } /** \class poppler::toc_item poppler-toc.h "poppler/cpp/poppler-toc.h" Represents an item of the TOC (Table of Contents) of a PDF %document. */ /** \typedef std::vector::const_iterator poppler::toc_item::iterator An iterator for the children of a TOC item. */ toc_item::toc_item() : d(new toc_item_private()) { } /** Destroys the TOC item. */ toc_item::~toc_item() { delete d; } /** \returns the title of the TOC item */ ustring toc_item::title() const { return d->title; } /** Returns whether the TOC item should be represented as open when showing the TOC. This is not a functional behaviour, but a visualisation hint of the item. Regardless of this state, the item can be expanded and collapsed freely when represented in a TOC view of a PDF viewer. \returns whether the TOC item should be open */ bool toc_item::is_open() const { return d->is_open; } /** \returns the children of the TOC item */ std::vector toc_item::children() const { return d->children; } /** \returns an iterator to the being of the list of children of the TOC item */ toc_item::iterator toc_item::children_begin() const { return d->children.begin(); } /** \returns an iterator to the end of the list of children of the TOC item */ toc_item::iterator toc_item::children_end() const { return d->children.end(); } poppler-24.02.0/cpp/poppler-toc.h000066400000000000000000000031071455701731300165460ustar00rootroot00000000000000/* * Copyright (C) 2009, Pino Toscano * * 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_TOC_H #define POPPLER_TOC_H #include "poppler-global.h" #include namespace poppler { class toc_private; class toc_item; class toc_item_private; class POPPLER_CPP_EXPORT toc : public poppler::noncopyable { public: ~toc(); toc_item *root() const; private: toc(); toc_private *d; friend class toc_private; }; class POPPLER_CPP_EXPORT toc_item : public poppler::noncopyable { public: typedef std::vector::const_iterator iterator; ~toc_item(); ustring title() const; bool is_open() const; std::vector children() const; iterator children_begin() const; iterator children_end() const; private: toc_item(); toc_item_private *d; friend class toc; friend class toc_private; friend class toc_item_private; }; } #endif poppler-24.02.0/cpp/poppler-version.cpp000066400000000000000000000043471455701731300200100ustar00rootroot00000000000000/* * Copyright (C) 2009-2010, Pino Toscano * * 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. */ /** \file poppler-version.h.in \brief poppler-version.h \remark poppler-version.h.in is the file name in Poppler's source files. The file name as an include file is poppler-version.h. */ #include "poppler-version.h" /** \def POPPLER_VERSION \brief The version string of the poppler-cpp header files. e.g. in poppler version 0.1.2 this is "0.1.2". */ /** \def POPPLER_VERSION_MAJOR \brief The "major" version number of the poppler-cpp header files. e.g. in poppler version 0.1.2 this is 0. */ /** \def POPPLER_VERSION_MINOR \brief The "minor" version number of the poppler-cpp header files. e.g. in poppler version 0.1.2 this is 1. */ /** \def POPPLER_VERSION_MICRO \brief The "micro" version number of the poppler-cpp header files. e.g. in poppler version 0.1.2 this is 2. */ using namespace poppler; /** \returns the version string of the current poppler-cpp library */ std::string poppler::version_string() { return std::string(POPPLER_VERSION); } /** \returns the "major" number of the version of the current poppler-cpp library */ unsigned int poppler::version_major() { return POPPLER_VERSION_MAJOR; } /** \returns the "minor" number of the version of the current poppler-cpp library */ unsigned int poppler::version_minor() { return POPPLER_VERSION_MINOR; } /** \returns the "micro" number of the version of the current poppler-cpp library */ unsigned int poppler::version_micro() { return POPPLER_VERSION_MICRO; } poppler-24.02.0/cpp/poppler-version.h.in000066400000000000000000000024031455701731300200510ustar00rootroot00000000000000/* * Copyright (C) 2009, Pino Toscano * * 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_VERSION_H #define POPPLER_VERSION_H #include "poppler-global.h" #define POPPLER_VERSION "@POPPLER_VERSION@" #define POPPLER_VERSION_MAJOR @POPPLER_MAJOR_VERSION@ #define POPPLER_VERSION_MINOR @POPPLER_MINOR_VERSION@ #define POPPLER_VERSION_MICRO @POPPLER_MICRO_VERSION@ namespace poppler { POPPLER_CPP_EXPORT std::string version_string(); POPPLER_CPP_EXPORT unsigned int version_major(); POPPLER_CPP_EXPORT unsigned int version_minor(); POPPLER_CPP_EXPORT unsigned int version_micro(); } #endif poppler-24.02.0/cpp/tests/000077500000000000000000000000001455701731300152725ustar00rootroot00000000000000poppler-24.02.0/cpp/tests/.gitignore000066400000000000000000000000341455701731300172570ustar00rootroot00000000000000poppler-dump poppler-render poppler-24.02.0/cpp/tests/CMakeLists.txt000066400000000000000000000016621455701731300200370ustar00rootroot00000000000000include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_BINARY_DIR}/.. ${CMAKE_SOURCE_DIR}/utils ) macro(CPP_ADD_SIMPLETEST exe) string(REPLACE "-" "" test_name ${exe}) set(${test_name}_SOURCES ${ARGN} ) poppler_add_test(${exe} BUILD_CPP_TESTS ${${test_name}_SOURCES}) target_link_libraries(${exe} poppler-cpp poppler) endmacro(CPP_ADD_SIMPLETEST) cpp_add_simpletest(poppler-dump poppler-dump.cpp ${CMAKE_SOURCE_DIR}/utils/parseargs.cc) cpp_add_simpletest(poppler-render poppler-render.cpp ${CMAKE_SOURCE_DIR}/utils/parseargs.cc) if(ENABLE_FUZZER) cpp_add_simpletest(doc_fuzzer ./fuzzing/doc_fuzzer.cc) cpp_add_simpletest(pdf_fuzzer ./fuzzing/pdf_fuzzer.cc) cpp_add_simpletest(pdf_file_fuzzer ./fuzzing/pdf_file_fuzzer.cc) cpp_add_simpletest(page_label_fuzzer ./fuzzing/page_label_fuzzer.cc) cpp_add_simpletest(page_search_fuzzer ./fuzzing/page_search_fuzzer.cc) endif() poppler-24.02.0/cpp/tests/fuzzing/000077500000000000000000000000001455701731300167665ustar00rootroot00000000000000poppler-24.02.0/cpp/tests/fuzzing/FuzzedDataProvider.h000066400000000000000000000370271455701731300227240ustar00rootroot00000000000000//===- FuzzedDataProvider.h - Utility header for fuzz targets ---*- C++ -* ===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // A single header library providing an utility class to break up an array of // bytes. Whenever run on the same input, provides the same output, as long as // its methods are called in the same order, with the same arguments. //===----------------------------------------------------------------------===// #ifndef LLVM_FUZZER_FUZZED_DATA_PROVIDER_H_ #define LLVM_FUZZER_FUZZED_DATA_PROVIDER_H_ #include #include #include #include #include #include #include #include #include #include // In addition to the comments below, the API is also briefly documented at // https://github.com/google/fuzzing/blob/master/docs/split-inputs.md#fuzzed-data-provider class FuzzedDataProvider { public: // |data| is an array of length |size| that the FuzzedDataProvider wraps to // provide more granular access. |data| must outlive the FuzzedDataProvider. FuzzedDataProvider(const uint8_t *data, size_t size) : data_ptr_(data), remaining_bytes_(size) { } ~FuzzedDataProvider() = default; // See the implementation below (after the class definition) for more verbose // comments for each of the methods. // Methods returning std::vector of bytes. These are the most popular choice // when splitting fuzzing input into pieces, as every piece is put into a // separate buffer (i.e. ASan would catch any under-/overflow) and the memory // will be released automatically. template std::vector ConsumeBytes(size_t num_bytes); template std::vector ConsumeBytesWithTerminator(size_t num_bytes, T terminator = 0); template std::vector ConsumeRemainingBytes(); // Methods returning strings. Use only when you need a std::string or a null // terminated C-string. Otherwise, prefer the methods returning std::vector. std::string ConsumeBytesAsString(size_t num_bytes); std::string ConsumeRandomLengthString(size_t max_length); std::string ConsumeRandomLengthString(); std::string ConsumeRemainingBytesAsString(); // Methods returning integer values. template T ConsumeIntegral(); template T ConsumeIntegralInRange(T min, T max); // Methods returning floating point values. template T ConsumeFloatingPoint(); template T ConsumeFloatingPointInRange(T min, T max); // 0 <= return value <= 1. template T ConsumeProbability(); bool ConsumeBool(); // Returns a value chosen from the given enum. template T ConsumeEnum(); // Returns a value from the given array. template T PickValueInArray(const T (&array)[size]); template T PickValueInArray(std::initializer_list list); // Writes data to the given destination and returns number of bytes written. size_t ConsumeData(void *destination, size_t num_bytes); // Reports the remaining bytes available for fuzzed input. size_t remaining_bytes() { return remaining_bytes_; } private: FuzzedDataProvider(const FuzzedDataProvider &) = delete; FuzzedDataProvider &operator=(const FuzzedDataProvider &) = delete; void CopyAndAdvance(void *destination, size_t num_bytes); void Advance(size_t num_bytes); template std::vector ConsumeBytes(size_t size, size_t num_bytes); template TS ConvertUnsignedToSigned(TU value); const uint8_t *data_ptr_; size_t remaining_bytes_; }; // Returns a std::vector containing |num_bytes| of input data. If fewer than // |num_bytes| of data remain, returns a shorter std::vector containing all // of the data that's left. Can be used with any byte sized type, such as // char, unsigned char, uint8_t, etc. template std::vector FuzzedDataProvider::ConsumeBytes(size_t num_bytes) { num_bytes = std::min(num_bytes, remaining_bytes_); return ConsumeBytes(num_bytes, num_bytes); } // Similar to |ConsumeBytes|, but also appends the terminator value at the end // of the resulting vector. Useful, when a mutable null-terminated C-string is // needed, for example. But that is a rare case. Better avoid it, if possible, // and prefer using |ConsumeBytes| or |ConsumeBytesAsString| methods. template std::vector FuzzedDataProvider::ConsumeBytesWithTerminator(size_t num_bytes, T terminator) { num_bytes = std::min(num_bytes, remaining_bytes_); std::vector result = ConsumeBytes(num_bytes + 1, num_bytes); result.back() = terminator; return result; } // Returns a std::vector containing all remaining bytes of the input data. template std::vector FuzzedDataProvider::ConsumeRemainingBytes() { return ConsumeBytes(remaining_bytes_); } // Returns a std::string containing |num_bytes| of input data. Using this and // |.c_str()| on the resulting string is the best way to get an immutable // null-terminated C string. If fewer than |num_bytes| of data remain, returns // a shorter std::string containing all of the data that's left. inline std::string FuzzedDataProvider::ConsumeBytesAsString(size_t num_bytes) { static_assert(sizeof(std::string::value_type) == sizeof(uint8_t), "ConsumeBytesAsString cannot convert the data to a string."); num_bytes = std::min(num_bytes, remaining_bytes_); std::string result(reinterpret_cast(data_ptr_), num_bytes); Advance(num_bytes); return result; } // Returns a std::string of length from 0 to |max_length|. When it runs out of // input data, returns what remains of the input. Designed to be more stable // with respect to a fuzzer inserting characters than just picking a random // length and then consuming that many bytes with |ConsumeBytes|. inline std::string FuzzedDataProvider::ConsumeRandomLengthString(size_t max_length) { // Reads bytes from the start of |data_ptr_|. Maps "\\" to "\", and maps "\" // followed by anything else to the end of the string. As a result of this // logic, a fuzzer can insert characters into the string, and the string // will be lengthened to include those new characters, resulting in a more // stable fuzzer than picking the length of a string independently from // picking its contents. std::string result; // Reserve the anticipated capaticity to prevent several reallocations. result.reserve(std::min(max_length, remaining_bytes_)); for (size_t i = 0; i < max_length && remaining_bytes_ != 0; ++i) { char next = ConvertUnsignedToSigned(data_ptr_[0]); Advance(1); if (next == '\\' && remaining_bytes_ != 0) { next = ConvertUnsignedToSigned(data_ptr_[0]); Advance(1); if (next != '\\') break; } result += next; } result.shrink_to_fit(); return result; } // Returns a std::string of length from 0 to |remaining_bytes_|. inline std::string FuzzedDataProvider::ConsumeRandomLengthString() { return ConsumeRandomLengthString(remaining_bytes_); } // Returns a std::string containing all remaining bytes of the input data. // Prefer using |ConsumeRemainingBytes| unless you actually need a std::string // object. inline std::string FuzzedDataProvider::ConsumeRemainingBytesAsString() { return ConsumeBytesAsString(remaining_bytes_); } // Returns a number in the range [Type's min, Type's max]. The value might // not be uniformly distributed in the given range. If there's no input data // left, always returns |min|. template T FuzzedDataProvider::ConsumeIntegral() { return ConsumeIntegralInRange(std::numeric_limits::min(), std::numeric_limits::max()); } // Returns a number in the range [min, max] by consuming bytes from the // input data. The value might not be uniformly distributed in the given // range. If there's no input data left, always returns |min|. |min| must // be less than or equal to |max|. template T FuzzedDataProvider::ConsumeIntegralInRange(T min, T max) { static_assert(std::is_integral::value, "An integral type is required."); static_assert(sizeof(T) <= sizeof(uint64_t), "Unsupported integral type."); if (min > max) abort(); // Use the biggest type possible to hold the range and the result. uint64_t range = static_cast(max) - min; uint64_t result = 0; size_t offset = 0; while (offset < sizeof(T) * CHAR_BIT && (range >> offset) > 0 && remaining_bytes_ != 0) { // Pull bytes off the end of the seed data. Experimentally, this seems to // allow the fuzzer to more easily explore the input space. This makes // sense, since it works by modifying inputs that caused new code to run, // and this data is often used to encode length of data read by // |ConsumeBytes|. Separating out read lengths makes it easier modify the // contents of the data that is actually read. --remaining_bytes_; result = (result << CHAR_BIT) | data_ptr_[remaining_bytes_]; offset += CHAR_BIT; } // Avoid division by 0, in case |range + 1| results in overflow. if (range != std::numeric_limits::max()) result = result % (range + 1); return static_cast(min + result); } // Returns a floating point value in the range [Type's lowest, Type's max] by // consuming bytes from the input data. If there's no input data left, always // returns approximately 0. template T FuzzedDataProvider::ConsumeFloatingPoint() { return ConsumeFloatingPointInRange(std::numeric_limits::lowest(), std::numeric_limits::max()); } // Returns a floating point value in the given range by consuming bytes from // the input data. If there's no input data left, returns |min|. Note that // |min| must be less than or equal to |max|. template T FuzzedDataProvider::ConsumeFloatingPointInRange(T min, T max) { if (min > max) abort(); T range = .0; T result = min; constexpr T zero(.0); if (max > zero && min < zero && max > min + std::numeric_limits::max()) { // The diff |max - min| would overflow the given floating point type. Use // the half of the diff as the range and consume a bool to decide whether // the result is in the first of the second part of the diff. range = (max / 2.0) - (min / 2.0); if (ConsumeBool()) { result += range; } } else { range = max - min; } return result + range * ConsumeProbability(); } // Returns a floating point number in the range [0.0, 1.0]. If there's no // input data left, always returns 0. template T FuzzedDataProvider::ConsumeProbability() { static_assert(std::is_floating_point::value, "A floating point type is required."); // Use different integral types for different floating point types in order // to provide better density of the resulting values. using IntegralType = typename std::conditional<(sizeof(T) <= sizeof(uint32_t)), uint32_t, uint64_t>::type; T result = static_cast(ConsumeIntegral()); result /= static_cast(std::numeric_limits::max()); return result; } // Reads one byte and returns a bool, or false when no data remains. inline bool FuzzedDataProvider::ConsumeBool() { return 1 & ConsumeIntegral(); } // Returns an enum value. The enum must start at 0 and be contiguous. It must // also contain |kMaxValue| aliased to its largest (inclusive) value. Such as: // enum class Foo { SomeValue, OtherValue, kMaxValue = OtherValue }; template T FuzzedDataProvider::ConsumeEnum() { static_assert(std::is_enum::value, "|T| must be an enum type."); return static_cast(ConsumeIntegralInRange(0, static_cast(T::kMaxValue))); } // Returns a copy of the value selected from the given fixed-size |array|. template T FuzzedDataProvider::PickValueInArray(const T (&array)[size]) { static_assert(size > 0, "The array must be non empty."); return array[ConsumeIntegralInRange(0, size - 1)]; } template T FuzzedDataProvider::PickValueInArray(std::initializer_list list) { // TODO(Dor1s): switch to static_assert once C++14 is allowed. if (!list.size()) abort(); return *(list.begin() + ConsumeIntegralInRange(0, list.size() - 1)); } // Writes |num_bytes| of input data to the given destination pointer. If there // is not enough data left, writes all remaining bytes. Return value is the // number of bytes written. // In general, it's better to avoid using this function, but it may be useful // in cases when it's necessary to fill a certain buffer or object with // fuzzing data. inline size_t FuzzedDataProvider::ConsumeData(void *destination, size_t num_bytes) { num_bytes = std::min(num_bytes, remaining_bytes_); CopyAndAdvance(destination, num_bytes); return num_bytes; } // Private methods. inline void FuzzedDataProvider::CopyAndAdvance(void *destination, size_t num_bytes) { std::memcpy(destination, data_ptr_, num_bytes); Advance(num_bytes); } inline void FuzzedDataProvider::Advance(size_t num_bytes) { if (num_bytes > remaining_bytes_) abort(); data_ptr_ += num_bytes; remaining_bytes_ -= num_bytes; } template std::vector FuzzedDataProvider::ConsumeBytes(size_t size, size_t num_bytes) { static_assert(sizeof(T) == sizeof(uint8_t), "Incompatible data type."); // The point of using the size-based constructor below is to increase the // odds of having a vector object with capacity being equal to the length. // That part is always implementation specific, but at least both libc++ and // libstdc++ allocate the requested number of bytes in that constructor, // which seems to be a natural choice for other implementations as well. // To increase the odds even more, we also call |shrink_to_fit| below. std::vector result(size); if (size == 0) { if (num_bytes != 0) abort(); return result; } CopyAndAdvance(result.data(), num_bytes); // Even though |shrink_to_fit| is also implementation specific, we expect it // to provide an additional assurance in case vector's constructor allocated // a buffer which is larger than the actual amount of data we put inside it. result.shrink_to_fit(); return result; } template TS FuzzedDataProvider::ConvertUnsignedToSigned(TU value) { static_assert(sizeof(TS) == sizeof(TU), "Incompatible data types."); static_assert(!std::numeric_limits::is_signed, "Source type must be unsigned."); // TODO(Dor1s): change to `if constexpr` once C++17 becomes mainstream. if (std::numeric_limits::is_modulo) return static_cast(value); // Avoid using implementation-defined unsigned to signed conversions. // To learn more, see https://stackoverflow.com/questions/13150449. if (value <= std::numeric_limits::max()) { return static_cast(value); } else { constexpr auto TS_min = std::numeric_limits::min(); return TS_min + static_cast(value - TS_min); } } #endif // LLVM_FUZZER_FUZZED_DATA_PROVIDER_H_ poppler-24.02.0/cpp/tests/fuzzing/doc_fuzzer.cc000066400000000000000000000037161455701731300214560ustar00rootroot00000000000000#include #include #include #include "FuzzedDataProvider.h" const size_t input_size = 32; const size_t count = 6; static void dummy_error_function(const std::string &, void *) { } extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { if (size < input_size * count) { return 0; } poppler::set_debug_error_function(dummy_error_function, nullptr); poppler::document *doc = poppler::document::load_from_raw_data((const char *)data, size); if (!doc || doc->is_locked()) { delete doc; return 0; } FuzzedDataProvider data_provider(data, size); std::string in_auth = data_provider.ConsumeBytesAsString(input_size); std::string in_creat = data_provider.ConsumeBytesAsString(input_size); std::string in_key = data_provider.ConsumeBytesAsString(input_size); std::string in_prod = data_provider.ConsumeBytesAsString(input_size); std::string in_sub = data_provider.ConsumeBytesAsString(input_size); std::string in_title = data_provider.ConsumeBytesAsString(input_size); // Testing both methods for conversion to ustring doc->set_author(poppler::ustring::from_latin1(in_auth)); doc->set_creator(poppler::ustring::from_latin1(in_creat)); doc->set_keywords(poppler::ustring::from_latin1(in_key)); doc->set_producer(poppler::ustring::from_latin1(in_prod)); doc->set_subject(poppler::ustring::from_latin1(in_sub)); doc->set_title(poppler::ustring::from_latin1(in_title)); doc->set_author(poppler::ustring::from_utf8(in_auth.c_str(), -1)); doc->set_creator(poppler::ustring::from_utf8(in_creat.c_str(), -1)); doc->set_keywords(poppler::ustring::from_utf8(in_key.c_str(), -1)); doc->set_producer(poppler::ustring::from_utf8(in_prod.c_str(), -1)); doc->set_subject(poppler::ustring::from_utf8(in_sub.c_str(), -1)); doc->set_title(poppler::ustring::from_utf8(in_title.c_str(), -1)); delete doc; return 0; } poppler-24.02.0/cpp/tests/fuzzing/fuzzer_temp_file.h000066400000000000000000000045621455701731300225170ustar00rootroot00000000000000// Copyright 2018 Google Inc. // // 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. // Adapter utility from fuzzer input to a temporary file, for fuzzing APIs that // require a file instead of an input buffer. #ifndef FUZZER_TEMP_FILE_H_ #define FUZZER_TEMP_FILE_H_ #include #include #include #include #include // Pure-C interface for creating and cleaning up temporary files. static char *fuzzer_get_tmpfile(const uint8_t *data, size_t size) { char *filename_buffer = strdup("/tmp/generate_temporary_file.XXXXXX"); if (!filename_buffer) { perror("Failed to allocate file name buffer."); abort(); } const int file_descriptor = mkstemp(filename_buffer); if (file_descriptor < 0) { perror("Failed to make temporary file."); abort(); } FILE *file = fdopen(file_descriptor, "wb"); if (!file) { perror("Failed to open file descriptor."); close(file_descriptor); abort(); } const size_t bytes_written = fwrite(data, sizeof(uint8_t), size, file); if (bytes_written < size) { close(file_descriptor); fprintf(stderr, "Failed to write all bytes to file (%zu out of %zu)", bytes_written, size); abort(); } fclose(file); return filename_buffer; } static void fuzzer_release_tmpfile(char *filename) { if (unlink(filename) != 0) { perror("WARNING: Failed to delete temporary file."); } free(filename); } // C++ RAII object for creating temporary files. #ifdef __cplusplus class FuzzerTemporaryFile { public: FuzzerTemporaryFile(const uint8_t *data, size_t size) : filename_(fuzzer_get_tmpfile(data, size)) { } ~FuzzerTemporaryFile() { fuzzer_release_tmpfile(filename_); } const char *filename() const { return filename_; } private: char *filename_; }; #endif #endif // FUZZER_TEMP_FILE_H_ poppler-24.02.0/cpp/tests/fuzzing/page_label_fuzzer.cc000066400000000000000000000021041455701731300227520ustar00rootroot00000000000000#include #include #include #include #include #include "FuzzedDataProvider.h" const size_t input_size = 32; static void dummy_error_function(const std::string &, void *) { } extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { if (size < input_size) { return 0; } poppler::set_debug_error_function(dummy_error_function, nullptr); poppler::document *doc = poppler::document::load_from_raw_data((const char *)data, size); if (!doc || doc->is_locked()) { delete doc; return 0; } poppler::page_renderer r; FuzzedDataProvider data_provider(data, size); std::string in_label = data_provider.ConsumeBytesAsString(input_size); for (int i = 0; i < doc->pages(); i++) { poppler::page *p = doc->create_page(poppler::ustring::from_utf8(in_label.c_str(), -1)); if (!p) { continue; } r.render_page(p); p->label(); delete p; } delete doc; return 0; } poppler-24.02.0/cpp/tests/fuzzing/page_search_fuzzer.cc000066400000000000000000000023541455701731300231470ustar00rootroot00000000000000#include #include #include #include #include #include "FuzzedDataProvider.h" const size_t input_size = 32; static void dummy_error_function(const std::string &, void *) { } extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { if (size < input_size) { return 0; } poppler::set_debug_error_function(dummy_error_function, nullptr); poppler::document *doc = poppler::document::load_from_raw_data((const char *)data, size); if (!doc || doc->is_locked()) { delete doc; return 0; } poppler::page_renderer r; FuzzedDataProvider data_provider(data, size); std::string in_text = data_provider.ConsumeBytesAsString(input_size); for (int i = 0; i < doc->pages(); i++) { poppler::page *p = doc->create_page(i); if (!p) { continue; } poppler::rectf rect = p->page_rect(); poppler::ustring text = poppler::ustring::from_utf8(in_text.c_str(), -1); p->search(text, rect, poppler::page::search_from_top, poppler::case_insensitive, poppler::rotate_0); r.render_page(p); delete p; } delete doc; return 0; } poppler-24.02.0/cpp/tests/fuzzing/pdf_file_fuzzer.cc000066400000000000000000000016701455701731300224560ustar00rootroot00000000000000#include #include #include #include #include #include "fuzzer_temp_file.h" static void dummy_error_function(const std::string &, void *) { } extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { poppler::set_debug_error_function(dummy_error_function, nullptr); char *tmpfile = fuzzer_get_tmpfile(data, size); std::string fname(tmpfile); poppler::document *doc = poppler::document::load_from_file(fname); if (!doc || doc->is_locked()) { delete doc; fuzzer_release_tmpfile(tmpfile); return 0; } poppler::page_renderer r; for (int i = 0; i < doc->pages(); i++) { poppler::page *p = doc->create_page(i); if (!p) { continue; } r.render_page(p); delete p; } delete doc; fuzzer_release_tmpfile(tmpfile); return 0; } poppler-24.02.0/cpp/tests/fuzzing/pdf_fuzzer.cc000066400000000000000000000017211455701731300214540ustar00rootroot00000000000000#include #include #include #include #include #include static void dummy_error_function(const std::string &, void *) { } extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { poppler::set_debug_error_function(dummy_error_function, nullptr); poppler::document *doc = poppler::document::load_from_raw_data((const char *)data, size); if (!doc || doc->is_locked()) { delete doc; return 0; } doc->metadata(); doc->create_destination_map(); doc->embedded_files(); doc->fonts(); poppler::page_renderer r; for (int i = 0; i < doc->pages(); i++) { poppler::page *p = doc->create_page(i); if (!p) { continue; } r.render_page(p); p->text_list(poppler::page::text_list_include_font); delete p; } delete doc; return 0; } poppler-24.02.0/cpp/tests/poppler-dump.cpp000066400000000000000000000503641455701731300204320ustar00rootroot00000000000000/* * Copyright (C) 2009-2010, Pino Toscano * Copyright (C) 2017-2019, 2022, Albert Astals Cid * Copyright (C) 2017, Jason Alan Palmer * Copyright (C) 2018, 2020, Suzuki Toshiya * Copyright (C) 2019, Masamichi Hosoda * Copyright (C) 2020, Jiri Jakes * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "parseargs.h" #include "config.h" static const int out_width = 30; bool show_all = false; bool show_info = false; bool show_perm = false; bool show_metadata = false; bool show_toc = false; bool show_fonts = false; bool show_embedded_files = false; bool show_pages = false; bool show_destinations = false; bool show_help = false; bool show_version = false; char show_text[32]; bool show_text_list = false; bool show_text_list_with_font = false; poppler::page::text_layout_enum show_text_layout = poppler::page::physical_layout; static const ArgDesc the_args[] = { { "--show-all", argFlag, &show_all, 0, "show all the available information" }, { "--show-info", argFlag, &show_info, 0, "show general document information" }, { "--show-perm", argFlag, &show_perm, 0, "show document permissions" }, { "--show-metadata", argFlag, &show_metadata, 0, "show document metadata" }, { "--show-toc", argFlag, &show_toc, 0, "show the TOC" }, { "--show-fonts", argFlag, &show_fonts, 0, "show the document fonts" }, { "--show-embedded-files", argFlag, &show_embedded_files, 0, "show the document-level embedded files" }, { "--show-pages", argFlag, &show_pages, 0, "show pages information" }, { "--show-destinations", argFlag, &show_destinations, 0, "show named destinations" }, { "--show-text", argString, &show_text, sizeof(show_text), "show text (physical|raw|none) extracted from all pages" }, { "--show-text-list", argFlag, &show_text_list, 0, "show text list (experimental)" }, { "--show-text-list-with-font", argFlag, &show_text_list_with_font, 0, "show text list with font info (experimental)" }, { "-h", argFlag, &show_help, 0, "print usage information" }, { "--help", argFlag, &show_help, 0, "print usage information" }, { "--version", argFlag, &show_version, 0, "print poppler version" }, { nullptr, argFlag, nullptr, 0, nullptr } }; static void error(const std::string &msg) { std::cerr << "Error: " << msg << std::endl; std::cerr << "Exiting..." << std::endl; exit(1); } static std::ostream &operator<<(std::ostream &stream, const poppler::ustring &str) { const poppler::byte_array ba = str.to_utf8(); for (const char c : ba) { stream << c; } return stream; } static std::string out_date(std::time_t date) { if (date != std::time_t(-1)) { struct tm time; gmtime_r(&date, &time); struct tm *t = &time; char buf[32]; strftime(buf, sizeof(buf) - 1, "%d/%m/%Y %H:%M:%S", t); return std::string(buf); } return std::string("n/a"); } static std::string out_size(int size) { if (size >= 0) { std::ostringstream ss; ss << size; return ss.str(); } return std::string("n/a"); } static char charToHex(int x) { return x < 10 ? x + '0' : x - 10 + 'a'; } static std::string out_hex_string(const poppler::byte_array &str) { std::string ret(str.size() * 2, '\0'); const char *str_p = &str[0]; for (unsigned int i = 0; i < str.size(); ++i, ++str_p) { ret[i * 2] = charToHex((*str_p & 0xf0) >> 4); ret[i * 2 + 1] = charToHex(*str_p & 0xf); } return ret; } static std::string out_page_orientation(poppler::page::orientation_enum o) { switch (o) { case poppler::page::landscape: return "landscape (90)"; case poppler::page::portrait: return "portrait (0)"; case poppler::page::seascape: return "seascape (270)"; case poppler::page::upside_down: return "upside_downs (180)"; }; return ""; } static std::string out_font_info_type(poppler::font_info::type_enum t) { #define OUT_FONT_TYPE(thetype) \ case poppler::font_info::thetype: \ return #thetype switch (t) { OUT_FONT_TYPE(unknown); OUT_FONT_TYPE(type1); OUT_FONT_TYPE(type1c); OUT_FONT_TYPE(type1c_ot); OUT_FONT_TYPE(type3); OUT_FONT_TYPE(truetype); OUT_FONT_TYPE(truetype_ot); OUT_FONT_TYPE(cid_type0); OUT_FONT_TYPE(cid_type0c); OUT_FONT_TYPE(cid_type0c_ot); OUT_FONT_TYPE(cid_truetype); OUT_FONT_TYPE(cid_truetype_ot); } return ""; #undef OUT_FONT_TYPE } static void print_info(poppler::document *doc) { std::cout << "Document information:" << std::endl; int major = 0, minor = 0; doc->get_pdf_version(&major, &minor); std::cout << std::setw(out_width) << "PDF version" << ": " << major << "." << minor << std::endl; std::string permanent_id, update_id; if (doc->get_pdf_id(&permanent_id, &update_id)) { std::cout << std::setw(out_width) << "PDF IDs" << ": P: " << permanent_id << " - U: " << update_id << std::endl; } else { std::cout << std::setw(out_width) << "PDF IDs" << ": " << std::endl; } const std::vector keys = doc->info_keys(); std::vector::const_iterator key_it = keys.begin(), key_end = keys.end(); for (; key_it != key_end; ++key_it) { std::cout << std::setw(out_width) << *key_it << ": " << doc->info_key(*key_it) << std::endl; } std::cout << std::setw(out_width) << "Date (creation)" << ": " << out_date(doc->info_date_t("CreationDate")) << std::endl; std::cout << std::setw(out_width) << "Date (modification)" << ": " << out_date(doc->info_date_t("ModDate")) << std::endl; std::cout << std::setw(out_width) << "Number of pages" << ": " << doc->pages() << std::endl; std::cout << std::setw(out_width) << "Linearized" << ": " << doc->is_linearized() << std::endl; std::cout << std::setw(out_width) << "Encrypted" << ": " << doc->is_encrypted() << std::endl; std::cout << std::endl; } static void print_perm(poppler::document *doc) { std::cout << "Document permissions:" << std::endl; #define OUT_PERM(theperm) std::cout << std::setw(out_width) << #theperm << ": " << doc->has_permission(poppler::perm_##theperm) << std::endl OUT_PERM(print); OUT_PERM(change); OUT_PERM(copy); OUT_PERM(add_notes); OUT_PERM(fill_forms); OUT_PERM(accessibility); OUT_PERM(assemble); OUT_PERM(print_high_resolution); std::cout << std::endl; #undef OUT_PERM } static void print_metadata(poppler::document *doc) { std::cout << std::setw(out_width) << "Metadata" << ":" << std::endl << doc->metadata() << std::endl; std::cout << std::endl; } static void print_toc_item(poppler::toc_item *item, int indent) { std::cout << std::setw(indent * 2) << " " << "+ " << item->title() << " (" << item->is_open() << ")" << std::endl; poppler::toc_item::iterator it = item->children_begin(), it_end = item->children_end(); for (; it != it_end; ++it) { print_toc_item(*it, indent + 1); } } static void print_toc(poppler::toc *doctoc) { std::cout << "Document TOC:" << std::endl; if (doctoc) { print_toc_item(doctoc->root(), 0); } else { std::cout << "" << std::endl; } std::cout << std::endl; } static void print_fonts(poppler::document *doc) { std::cout << "Document fonts:" << std::endl; std::vector fl = doc->fonts(); if (!fl.empty()) { std::vector::const_iterator it = fl.begin(), it_end = fl.end(); const std::ios_base::fmtflags f = std::cout.flags(); std::left(std::cout); for (; it != it_end; ++it) { std::cout << " " << std::setw(out_width + 10) << it->name() << " " << std::setw(15) << out_font_info_type(it->type()) << " " << std::setw(5) << it->is_embedded() << " " << std::setw(5) << it->is_subset() << " " << it->file() << std::endl; } std::cout.flags(f); } else { std::cout << "" << std::endl; } std::cout << std::endl; } static void print_embedded_files(poppler::document *doc) { std::cout << "Document embedded files:" << std::endl; std::vector ef = doc->embedded_files(); if (!ef.empty()) { std::vector::const_iterator it = ef.begin(), it_end = ef.end(); const std::ios_base::fmtflags flags = std::cout.flags(); std::left(std::cout); for (; it != it_end; ++it) { poppler::embedded_file *f = *it; std::cout << " " << std::setw(out_width + 10) << f->name() << " " << std::setw(10) << out_size(f->size()) << " " << std::setw(20) << out_date(f->creation_date_t()) << " " << std::setw(20) << out_date(f->modification_date_t()) << std::endl << " "; if (f->description().empty()) { std::cout << ""; } else { std::cout << f->description(); } std::cout << std::endl << " " << std::setw(35) << (f->checksum().empty() ? std::string("") : out_hex_string(f->checksum())) << " " << (f->mime_type().empty() ? std::string("") : f->mime_type()) << std::endl; } std::cout.flags(flags); } else { std::cout << "" << std::endl; } std::cout << std::endl; } static void print_page(poppler::page *p) { if (p) { std::cout << std::setw(out_width) << "Rect" << ": " << p->page_rect() << std::endl; std::cout << std::setw(out_width) << "Label" << ": " << p->label() << std::endl; std::cout << std::setw(out_width) << "Duration" << ": " << p->duration() << std::endl; std::cout << std::setw(out_width) << "Orientation" << ": " << out_page_orientation(p->orientation()) << std::endl; } else { std::cout << std::setw(out_width) << "Broken Page. Could not be parsed" << std::endl; } std::cout << std::endl; } static void print_destination(const poppler::destination *d) { if (d) { std::cout << std::setw(out_width) << "Type" << ": "; switch (d->type()) { case poppler::destination::unknown: std::cout << "unknown" << std::endl; break; case poppler::destination::xyz: std::cout << "xyz" << std::endl << std::setw(out_width) << "Page" << ": " << d->page_number() << std::endl << std::setw(out_width) << "Left" << ": " << d->left() << std::endl << std::setw(out_width) << "Top" << ": " << d->top() << std::endl << std::setw(out_width) << "Zoom" << ": " << d->zoom() << std::endl; break; case poppler::destination::fit: std::cout << "fit" << std::endl << std::setw(out_width) << "Page" << ": " << d->page_number() << std::endl; break; case poppler::destination::fit_h: std::cout << "fit_h" << std::endl << std::setw(out_width) << "Page" << ": " << d->page_number() << std::endl << std::setw(out_width) << "Top" << ": " << d->top() << std::endl; break; case poppler::destination::fit_v: std::cout << "fit_v" << std::endl << std::setw(out_width) << "Page" << ": " << d->page_number() << std::endl << std::setw(out_width) << "Left" << ": " << d->left() << std::endl; break; case poppler::destination::fit_r: std::cout << "fit_r" << std::endl << std::setw(out_width) << "Page" << ": " << d->page_number() << std::endl << std::setw(out_width) << "Left" << ": " << d->left() << std::endl << std::setw(out_width) << "Bottom" << ": " << d->bottom() << std::endl << std::setw(out_width) << "Right" << ": " << d->right() << std::endl << std::setw(out_width) << "Top" << ": " << d->top() << std::endl; break; case poppler::destination::fit_b: std::cout << "fit_b" << std::endl << std::setw(out_width) << "Page" << ": " << d->page_number() << std::endl; break; case poppler::destination::fit_b_h: std::cout << "fit_b_h" << std::endl << std::setw(out_width) << "Page" << ": " << d->page_number() << std::endl << std::setw(out_width) << "Top" << ": " << d->top() << std::endl; break; case poppler::destination::fit_b_v: std::cout << "fit_b_v" << std::endl << std::setw(out_width) << "Page" << ": " << d->page_number() << std::endl << std::setw(out_width) << "Left" << ": " << d->left() << std::endl; break; default: std::cout << "error" << std::endl; break; } } std::cout << std::endl; } static void print_page_text(poppler::page *p) { if (p) { std::cout << p->text(poppler::rectf(), show_text_layout) << std::endl; } else { std::cout << std::setw(out_width) << "Broken Page. Could not be parsed" << std::endl; } std::cout << std::endl; } static void print_page_text_list(poppler::page *p, int opt_flag = 0) { if (!p) { std::cout << std::setw(out_width) << "Broken Page. Could not be parsed" << std::endl; std::cout << std::endl; return; } auto text_list = p->text_list(opt_flag); std::cout << "---" << std::endl; for (const poppler::text_box &text : text_list) { poppler::rectf bbox = text.bbox(); poppler::ustring ustr = text.text(); int wmode = text.get_wmode(); double font_size = text.get_font_size(); std::string font_name = text.get_font_name(); std::cout << "[" << ustr << "] @ "; std::cout << "( x=" << bbox.x() << " y=" << bbox.y() << " w=" << bbox.width() << " h=" << bbox.height() << " )"; if (text.has_font_info()) { std::cout << "( fontname=" << font_name << " fontsize=" << font_size << " wmode=" << wmode << " )"; } std::cout << std::endl; } std::cout << "---" << std::endl; } int main(int argc, char *argv[]) { if (!parseArgs(the_args, &argc, argv) || argc < 2 || show_help) { printUsage(argv[0], "DOCUMENT", the_args); exit(1); } if (show_text[0]) { if (!memcmp(show_text, "physical", 9)) { show_text_layout = poppler::page::physical_layout; } else if (!memcmp(show_text, "raw", 4)) { show_text_layout = poppler::page::raw_order_layout; } else if (!memcmp(show_text, "none", 5)) { show_text_layout = poppler::page::non_raw_non_physical_layout; } else { error(std::string("unrecognized text mode: '") + show_text + "'"); } } std::string file_name(argv[1]); std::unique_ptr doc(poppler::document::load_from_file(file_name)); if (!doc.get()) { error("loading error"); } if (doc->is_locked()) { error("encrypted document"); } std::cout.setf(std::ios_base::boolalpha); if (show_all) { show_info = true; show_perm = true; show_metadata = true; show_toc = true; show_fonts = true; show_embedded_files = true; show_pages = true; } if (show_version) { std::cout << std::setw(out_width) << "Compiled" << ": poppler-cpp " << POPPLER_VERSION << std::endl << std::setw(out_width) << "Running" << ": poppler-cpp " << poppler::version_string() << std::endl; } if (show_info) { print_info(doc.get()); } if (show_perm) { print_perm(doc.get()); } if (show_metadata) { print_metadata(doc.get()); } if (show_toc) { std::unique_ptr doctoc(doc->create_toc()); print_toc(doctoc.get()); } if (show_fonts) { print_fonts(doc.get()); } if (show_embedded_files) { print_embedded_files(doc.get()); } if (show_pages) { const int pages = doc->pages(); for (int i = 0; i < pages; ++i) { std::cout << "Page " << (i + 1) << "/" << pages << ":" << std::endl; std::unique_ptr p(doc->create_page(i)); print_page(p.get()); } } if (show_destinations) { auto map = doc->create_destination_map(); for (const auto &pair : map) { std::string s = pair.first; for (auto &c : s) { if (c < 0x20 || c > 0x7e) { c = '.'; } } std::cout << "Named destination \"" << s << "\":" << std::endl; print_destination(&pair.second); } } if (show_text[0]) { const int pages = doc->pages(); for (int i = 0; i < pages; ++i) { std::cout << "Page " << (i + 1) << "/" << pages << ":" << std::endl; std::unique_ptr p(doc->create_page(i)); print_page_text(p.get()); } } if (show_text_list || show_text_list_with_font) { const int pages = doc->pages(); for (int i = 0; i < pages; ++i) { std::cout << "Page " << (i + 1) << "/" << pages << ":" << std::endl; std::unique_ptr p(doc->create_page(i)); if (show_text_list_with_font) { print_page_text_list(p.get(), poppler::page::text_list_include_font); } else { print_page_text_list(p.get(), 0); } } } return 0; } poppler-24.02.0/cpp/tests/poppler-render.cpp000066400000000000000000000067001455701731300207370ustar00rootroot00000000000000/* * Copyright (C) 2010, Pino Toscano * Copyright (C) 2017, 2019, 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 * 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 #include #include #include #include #include "parseargs.h" bool show_help = false; bool show_formats = false; char out_filename[4096]; int doc_page = 0; static const ArgDesc the_args[] = { { "-f", argFlag, &show_formats, 0, "show supported output image formats" }, { "--page", argInt, &doc_page, 0, "select page to render" }, { "-o", argString, &out_filename, sizeof(out_filename), "output filename for the resulting PNG image" }, { "-h", argFlag, &show_help, 0, "print usage information" }, { "--help", argFlag, &show_help, 0, "print usage information" }, { nullptr, argFlag, nullptr, 0, nullptr } }; static void error(const std::string &msg) { std::cerr << "Error: " << msg << std::endl; std::cerr << "Exiting..." << std::endl; exit(1); } int main(int argc, char *argv[]) { if (!parseArgs(the_args, &argc, argv) || (argc < 2 && !show_formats) || show_help) { printUsage(argv[0], "DOCUMENT", the_args); exit(1); } if (show_formats) { const std::vector formats = poppler::image::supported_image_formats(); std::cout << "Supported image formats:" << std::endl; for (const std::string &format : formats) { std::cout << " " << format << std::endl; } exit(0); } if (!out_filename[0]) { error("missing output filename (-o)"); } if (!poppler::page_renderer::can_render()) { error("renderer compiled without Splash support"); } const std::string file_name(argv[1]); std::unique_ptr doc(poppler::document::load_from_file(file_name)); if (!doc.get()) { error("loading error"); } if (doc->is_locked()) { error("encrypted document"); } if (doc_page < 0 || doc_page >= doc->pages()) { error("specified page number out of page count"); } std::unique_ptr p(doc->create_page(doc_page)); if (!p.get()) { error("NULL page"); } poppler::page_renderer pr; pr.set_render_hint(poppler::page_renderer::antialiasing, true); pr.set_render_hint(poppler::page_renderer::text_antialiasing, true); poppler::image img = pr.render_page(p.get()); if (!img.is_valid()) { error("rendering failed"); } if (!img.save(out_filename, "png")) { error("saving to file failed"); } return 0; } poppler-24.02.0/do-the-gnupg-2.4-dance.sh000066400000000000000000000021731455701731300175540ustar00rootroot00000000000000#! /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.1 GPGME_VERSION=1.19.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 poppler-24.02.0/fofi/000077500000000000000000000000001455701731300142715ustar00rootroot00000000000000poppler-24.02.0/fofi/.gitignore000066400000000000000000000000741455701731300162620ustar00rootroot00000000000000.cvsignore .deps .libs Makefile Makefile.in *.la *.lo *.loT poppler-24.02.0/fofi/FoFiBase.cc000066400000000000000000000114661455701731300162260ustar00rootroot00000000000000//======================================================================== // // FoFiBase.cc // // Copyright 1999-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) 2008 Ed Avis // Copyright (C) 2011 Jim Meyering // Copyright (C) 2016, 2018, 2020 Albert Astals Cid // Copyright (C) 2019 Christian Persch // Copyright (C) 2019 LE GARREC Vincent // Copyright (C) 2022 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 // //======================================================================== #include #include #include #include "goo/gfile.h" #include "goo/gmem.h" #include "poppler/Error.h" #include "FoFiBase.h" //------------------------------------------------------------------------ // FoFiBase //------------------------------------------------------------------------ FoFiBase::FoFiBase(const unsigned char *fileA, int lenA, bool freeFileDataA) { file = fileA; len = lenA; freeFileData = freeFileDataA; } FoFiBase::~FoFiBase() { if (freeFileData) { gfree((char *)file); } } char *FoFiBase::readFile(const char *fileName, int *fileLen) { FILE *f; char *buf; int n; if (!(f = openFile(fileName, "rb"))) { error(errIO, -1, "Cannot open '{0:s}'", fileName); return nullptr; } if (fseek(f, 0, SEEK_END) != 0) { error(errIO, -1, "Cannot seek to end of '{0:s}'", fileName); fclose(f); return nullptr; } n = (int)ftell(f); if (n < 0) { error(errIO, -1, "Cannot determine length of '{0:s}'", fileName); fclose(f); return nullptr; } if (fseek(f, 0, SEEK_SET) != 0) { error(errIO, -1, "Cannot seek to start of '{0:s}'", fileName); fclose(f); return nullptr; } buf = (char *)gmalloc(n); if ((int)fread(buf, 1, n, f) != n) { gfree(buf); fclose(f); return nullptr; } fclose(f); *fileLen = n; return buf; } int FoFiBase::getS8(int pos, bool *ok) const { int x; if (pos < 0 || pos >= len) { *ok = false; return 0; } x = file[pos]; if (x & 0x80) { x |= ~0xff; } return x; } int FoFiBase::getU8(int pos, bool *ok) const { if (pos < 0 || pos >= len) { *ok = false; return 0; } return file[pos]; } int FoFiBase::getS16BE(int pos, bool *ok) const { int x; if (pos < 0 || pos > INT_MAX - 1 || pos + 1 >= len) { *ok = false; return 0; } x = file[pos]; x = (x << 8) + file[pos + 1]; if (x & 0x8000) { x |= ~0xffff; } return x; } int FoFiBase::getU16BE(int pos, bool *ok) const { int x; if (pos < 0 || pos > INT_MAX - 1 || pos + 1 >= len) { *ok = false; return 0; } x = file[pos]; x = (x << 8) + file[pos + 1]; return x; } int FoFiBase::getS32BE(int pos, bool *ok) const { int x; if (pos < 0 || pos > INT_MAX - 3 || pos + 3 >= len) { *ok = false; return 0; } x = file[pos]; x = (x << 8) + file[pos + 1]; x = (x << 8) + file[pos + 2]; x = (x << 8) + file[pos + 3]; if (x & 0x80000000) { x |= ~0xffffffff; } return x; } unsigned int FoFiBase::getU32BE(int pos, bool *ok) const { unsigned int x; if (pos < 0 || pos > INT_MAX - 3 || pos + 3 >= len) { *ok = false; return 0; } x = file[pos]; x = (x << 8) + file[pos + 1]; x = (x << 8) + file[pos + 2]; x = (x << 8) + file[pos + 3]; return x; } unsigned int FoFiBase::getU32LE(int pos, bool *ok) const { unsigned int x; if (pos < 0 || pos > INT_MAX - 3 || pos + 3 >= len) { *ok = false; return 0; } x = file[pos + 3]; x = (x << 8) + file[pos + 2]; x = (x << 8) + file[pos + 1]; x = (x << 8) + file[pos]; return x; } unsigned int FoFiBase::getUVarBE(int pos, int size, bool *ok) const { unsigned int x; int i; if (pos < 0 || pos > INT_MAX - size || pos + size > len) { *ok = false; return 0; } x = 0; for (i = 0; i < size; ++i) { x = (x << 8) + file[pos + i]; } return x; } bool FoFiBase::checkRegion(int pos, int size) const { return pos >= 0 && size >= 0 && pos < INT_MAX - size && size < INT_MAX - pos && pos + size >= pos && pos + size <= len; } poppler-24.02.0/fofi/FoFiBase.h000066400000000000000000000041011455701731300160540ustar00rootroot00000000000000//======================================================================== // // FoFiBase.h // // Copyright 1999-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) 2018, 2022 Albert Astals Cid // Copyright (C) 2022 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 // //======================================================================== #ifndef FOFIBASE_H #define FOFIBASE_H //------------------------------------------------------------------------ using FoFiOutputFunc = void (*)(void *stream, const char *data, size_t len); //------------------------------------------------------------------------ // FoFiBase //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT FoFiBase { public: FoFiBase(const FoFiBase &) = delete; FoFiBase &operator=(const FoFiBase &other) = delete; virtual ~FoFiBase(); protected: FoFiBase(const unsigned char *fileA, int lenA, bool freeFileDataA); static char *readFile(const char *fileName, int *fileLen); // S = signed / U = unsigned // 8/16/32/Var = word length, in bytes // BE = big endian int getS8(int pos, bool *ok) const; int getU8(int pos, bool *ok) const; int getS16BE(int pos, bool *ok) const; int getU16BE(int pos, bool *ok) const; int getS32BE(int pos, bool *ok) const; unsigned int getU32BE(int pos, bool *ok) const; unsigned int getU32LE(int pos, bool *ok) const; unsigned int getUVarBE(int pos, int size, bool *ok) const; bool checkRegion(int pos, int size) const; const unsigned char *file; int len; bool freeFileData; }; #endif poppler-24.02.0/fofi/FoFiEncodings.cc000066400000000000000000001605321455701731300172640ustar00rootroot00000000000000//======================================================================== // // FoFiEncodings.cc // // Copyright 1999-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) 2016 Albert Astals Cid // Copyright (C) 2019 Volker Krause // // 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 #include #include "FoFiEncodings.h" //------------------------------------------------------------------------ // Type 1 and 1C font data //------------------------------------------------------------------------ const char *const fofiType1StandardEncoding[256] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "exclamdown", "cent", "sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle", "quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl", nullptr, "endash", "dagger", "daggerdbl", "periodcentered", nullptr, "paragraph", "bullet", "quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", "ellipsis", "perthousand", nullptr, "questiondown", nullptr, "grave", "acute", "circumflex", "tilde", "macron", "breve", "dotaccent", "dieresis", nullptr, "ring", "cedilla", nullptr, "hungarumlaut", "ogonek", "caron", "emdash", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "AE", nullptr, "ordfeminine", nullptr, nullptr, nullptr, nullptr, "Lslash", "Oslash", "OE", "ordmasculine", nullptr, nullptr, nullptr, nullptr, nullptr, "ae", nullptr, nullptr, nullptr, "dotlessi", nullptr, nullptr, "lslash", "oslash", "oe", "germandbls", nullptr, nullptr, nullptr, nullptr }; const char *const fofiType1ExpertEncoding[256] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "space", "exclamsmall", "Hungarumlautsmall", nullptr, "dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall", "parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader", "comma", "hyphen", "period", "fraction", "zerooldstyle", "oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon", "semicolon", "commasuperior", "threequartersemdash", "periodsuperior", "questionsmall", nullptr, "asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", nullptr, nullptr, nullptr, "isuperior", nullptr, nullptr, "lsuperior", "msuperior", "nsuperior", "osuperior", nullptr, nullptr, "rsuperior", "ssuperior", "tsuperior", nullptr, "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior", nullptr, "parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall", "Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall", "Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall", "Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall", "Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "exclamdownsmall", "centoldstyle", "Lslashsmall", nullptr, nullptr, "Scaronsmall", "Zcaronsmall", "Dieresissmall", "Brevesmall", "Caronsmall", nullptr, "Dotaccentsmall", nullptr, nullptr, "Macronsmall", nullptr, nullptr, "figuredash", "hypheninferior", nullptr, nullptr, "Ogoneksmall", "Ringsmall", "Cedillasmall", nullptr, nullptr, nullptr, "onequarter", "onehalf", "threequarters", "questiondownsmall", "oneeighth", "threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds", nullptr, nullptr, "zerosuperior", "onesuperior", "twosuperior", "threesuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior", "centinferior", "dollarinferior", "periodinferior", "commainferior", "Agravesmall", "Aacutesmall", "Acircumflexsmall", "Atildesmall", "Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall", "Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall", "Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall", "Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall", "Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall", "Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall", "Ydieresissmall" }; //------------------------------------------------------------------------ // Type 1C font data //------------------------------------------------------------------------ const char *fofiType1CStdStrings[391] = { ".notdef", "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", "exclamdown", "cent", "sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle", "quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl", "endash", "dagger", "daggerdbl", "periodcentered", "paragraph", "bullet", "quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", "ellipsis", "perthousand", "questiondown", "grave", "acute", "circumflex", "tilde", "macron", "breve", "dotaccent", "dieresis", "ring", "cedilla", "hungarumlaut", "ogonek", "caron", "emdash", "AE", "ordfeminine", "Lslash", "Oslash", "OE", "ordmasculine", "ae", "dotlessi", "lslash", "oslash", "oe", "germandbls", "onesuperior", "logicalnot", "mu", "trademark", "Eth", "onehalf", "plusminus", "Thorn", "onequarter", "divide", "brokenbar", "degree", "thorn", "threequarters", "twosuperior", "registered", "minus", "eth", "multiply", "threesuperior", "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave", "Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex", "Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis", "Igrave", "Ntilde", "Oacute", "Ocircumflex", "Odieresis", "Ograve", "Otilde", "Scaron", "Uacute", "Ucircumflex", "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron", "aacute", "acircumflex", "adieresis", "agrave", "aring", "atilde", "ccedilla", "eacute", "ecircumflex", "edieresis", "egrave", "iacute", "icircumflex", "idieresis", "igrave", "ntilde", "oacute", "ocircumflex", "odieresis", "ograve", "otilde", "scaron", "uacute", "ucircumflex", "udieresis", "ugrave", "yacute", "ydieresis", "zcaron", "exclamsmall", "Hungarumlautsmall", "dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall", "parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader", "zerooldstyle", "oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "commasuperior", "threequartersemdash", "periodsuperior", "questionsmall", "asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior", "lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior", "tsuperior", "ff", "ffi", "ffl", "parenleftinferior", "parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall", "Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall", "Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall", "Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall", "Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall", "exclamdownsmall", "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall", "Dieresissmall", "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall", "figuredash", "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall", "questiondownsmall", "oneeighth", "threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds", "zerosuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior", "centinferior", "dollarinferior", "periodinferior", "commainferior", "Agravesmall", "Aacutesmall", "Acircumflexsmall", "Atildesmall", "Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall", "Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall", "Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall", "Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall", "Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall", "Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall", "Ydieresissmall", "001.000", "001.001", "001.002", "001.003", "Black", "Bold", "Book", "Light", "Medium", "Regular", "Roman", "Semibold" }; const unsigned short fofiType1CISOAdobeCharset[229] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228 }; const unsigned short fofiType1CExpertCharset[166] = { 0, 1, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 13, 14, 15, 99, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 27, 28, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 109, 110, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 158, 155, 163, 319, 320, 321, 322, 323, 324, 325, 326, 150, 164, 169, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378 }; const unsigned short fofiType1CExpertSubsetCharset[87] = { 0, 1, 231, 232, 235, 236, 237, 238, 13, 14, 15, 99, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 27, 28, 249, 250, 251, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 109, 110, 267, 268, 269, 270, 272, 300, 301, 302, 305, 314, 315, 158, 155, 163, 320, 321, 322, 323, 324, 325, 326, 150, 164, 169, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346 }; poppler-24.02.0/fofi/FoFiEncodings.h000066400000000000000000000030411455701731300171150ustar00rootroot00000000000000//======================================================================== // // FoFiEncodings.h // // Copyright 1999-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) 2016 Albert Astals Cid // Copyright (C) 2019 Volker Krause // // 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 FOFIENCODINGS_H #define FOFIENCODINGS_H //------------------------------------------------------------------------ // Type 1 and 1C font data //------------------------------------------------------------------------ extern const char *const fofiType1StandardEncoding[256]; extern const char *const fofiType1ExpertEncoding[256]; //------------------------------------------------------------------------ // Type 1C font data //------------------------------------------------------------------------ extern const char *fofiType1CStdStrings[391]; extern const unsigned short fofiType1CISOAdobeCharset[229]; extern const unsigned short fofiType1CExpertCharset[166]; extern const unsigned short fofiType1CExpertSubsetCharset[87]; #endif poppler-24.02.0/fofi/FoFiIdentifier.cc000066400000000000000000000412061455701731300174310ustar00rootroot00000000000000//======================================================================== // // FoFiIdentifier.cc // // Copyright 2009 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) 2013 Christoph Duelli // Copyright (C) 2018, 2021 Albert Astals Cid // Copyright (C) 2019 Christian Persch // Copyright (C) 2019 LE GARREC Vincent // // 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 #include #include #include "goo/gfile.h" #include "goo/GooCheckedOps.h" #include "FoFiIdentifier.h" //------------------------------------------------------------------------ namespace { // do not pollute global namespace class Reader { public: Reader() = default; Reader(const Reader &) = delete; Reader &operator=(const Reader &other) = delete; virtual ~Reader() { } // Read one byte. Returns -1 if past EOF. virtual int getByte(int pos) = 0; // Read a big-endian unsigned 16-bit integer. Fills in *val and // returns true if successful. virtual bool getU16BE(int pos, int *val) = 0; // Read a big-endian unsigned 32-bit integer. Fills in *val and // returns true if successful. virtual bool getU32BE(int pos, unsigned int *val) = 0; // Read a little-endian unsigned 32-bit integer. Fills in *val and // returns true if successful. virtual bool getU32LE(int pos, unsigned int *val) = 0; // Read a big-endian unsigned -byte integer, where 1 <= size // <= 4. Fills in *val and returns true if successful. virtual bool getUVarBE(int pos, int size, unsigned int *val) = 0; // Compare against a string. Returns true if equal. virtual bool cmp(int pos, const char *s) = 0; }; //------------------------------------------------------------------------ class MemReader : public Reader { public: static MemReader *make(const char *bufA, int lenA); ~MemReader() override; int getByte(int pos) override; bool getU16BE(int pos, int *val) override; bool getU32BE(int pos, unsigned int *val) override; bool getU32LE(int pos, unsigned int *val) override; bool getUVarBE(int pos, int size, unsigned int *val) override; bool cmp(int pos, const char *s) override; private: MemReader(const char *bufA, int lenA); const char *buf; int len; }; MemReader *MemReader::make(const char *bufA, int lenA) { return new MemReader(bufA, lenA); } MemReader::MemReader(const char *bufA, int lenA) { buf = bufA; len = lenA; } MemReader::~MemReader() { } int MemReader::getByte(int pos) { if (pos < 0 || pos >= len) { return -1; } return buf[pos] & 0xff; } bool MemReader::getU16BE(int pos, int *val) { if (pos < 0 || pos > len - 2) { return false; } *val = ((buf[pos] & 0xff) << 8) + (buf[pos + 1] & 0xff); return true; } bool MemReader::getU32BE(int pos, unsigned int *val) { if (pos < 0 || pos > len - 4) { return false; } *val = ((buf[pos] & 0xff) << 24) + ((buf[pos + 1] & 0xff) << 16) + ((buf[pos + 2] & 0xff) << 8) + (buf[pos + 3] & 0xff); return true; } bool MemReader::getU32LE(int pos, unsigned int *val) { if (pos < 0 || pos > len - 4) { return false; } *val = (buf[pos] & 0xff) + ((buf[pos + 1] & 0xff) << 8) + ((buf[pos + 2] & 0xff) << 16) + ((buf[pos + 3] & 0xff) << 24); return true; } bool MemReader::getUVarBE(int pos, int size, unsigned int *val) { int i; if (size < 1 || size > 4 || pos < 0 || pos > len - size) { return false; } *val = 0; for (i = 0; i < size; ++i) { *val = (*val << 8) + (buf[pos + i] & 0xff); } return true; } bool MemReader::cmp(int pos, const char *s) { int n; n = (int)strlen(s); if (pos < 0 || len < n || pos > len - n) { return false; } return !memcmp(buf + pos, s, n); } //------------------------------------------------------------------------ class FileReader : public Reader { public: static FileReader *make(const char *fileName); ~FileReader() override; int getByte(int pos) override; bool getU16BE(int pos, int *val) override; bool getU32BE(int pos, unsigned int *val) override; bool getU32LE(int pos, unsigned int *val) override; bool getUVarBE(int pos, int size, unsigned int *val) override; bool cmp(int pos, const char *s) override; private: explicit FileReader(FILE *fA); bool fillBuf(int pos, int len); FILE *f; char buf[1024]; int bufPos, bufLen; }; FileReader *FileReader::make(const char *fileName) { FILE *fA; if (!(fA = openFile(fileName, "rb"))) { return nullptr; } return new FileReader(fA); } FileReader::FileReader(FILE *fA) { f = fA; bufPos = 0; bufLen = 0; } FileReader::~FileReader() { fclose(f); } int FileReader::getByte(int pos) { if (!fillBuf(pos, 1)) { return -1; } return buf[pos - bufPos] & 0xff; } bool FileReader::getU16BE(int pos, int *val) { if (!fillBuf(pos, 2)) { return false; } *val = ((buf[pos - bufPos] & 0xff) << 8) + (buf[pos - bufPos + 1] & 0xff); return true; } bool FileReader::getU32BE(int pos, unsigned int *val) { if (!fillBuf(pos, 4)) { return false; } *val = ((buf[pos - bufPos] & 0xff) << 24) + ((buf[pos - bufPos + 1] & 0xff) << 16) + ((buf[pos - bufPos + 2] & 0xff) << 8) + (buf[pos - bufPos + 3] & 0xff); return true; } bool FileReader::getU32LE(int pos, unsigned int *val) { if (!fillBuf(pos, 4)) { return false; } *val = (buf[pos - bufPos] & 0xff) + ((buf[pos - bufPos + 1] & 0xff) << 8) + ((buf[pos - bufPos + 2] & 0xff) << 16) + ((buf[pos - bufPos + 3] & 0xff) << 24); return true; } bool FileReader::getUVarBE(int pos, int size, unsigned int *val) { int i; if (size < 1 || size > 4 || !fillBuf(pos, size)) { return false; } *val = 0; for (i = 0; i < size; ++i) { *val = (*val << 8) + (buf[pos - bufPos + i] & 0xff); } return true; } bool FileReader::cmp(int pos, const char *s) { int n; n = (int)strlen(s); if (!fillBuf(pos, n)) { return false; } return !memcmp(buf - bufPos + pos, s, n); } bool FileReader::fillBuf(int pos, int len) { if (pos < 0 || len < 0 || len > (int)sizeof(buf) || pos > INT_MAX - (int)sizeof(buf)) { return false; } if (pos >= bufPos && pos + len <= bufPos + bufLen) { return true; } if (fseek(f, pos, SEEK_SET)) { return false; } bufPos = pos; bufLen = (int)fread(buf, 1, sizeof(buf), f); if (bufLen < len) { return false; } return true; } //------------------------------------------------------------------------ class StreamReader : public Reader { public: static StreamReader *make(int (*getCharA)(void *data), void *dataA); ~StreamReader() override; int getByte(int pos) override; bool getU16BE(int pos, int *val) override; bool getU32BE(int pos, unsigned int *val) override; bool getU32LE(int pos, unsigned int *val) override; bool getUVarBE(int pos, int size, unsigned int *val) override; bool cmp(int pos, const char *s) override; private: StreamReader(int (*getCharA)(void *data), void *dataA); bool fillBuf(int pos, int len); int (*getChar)(void *data); void *data; int streamPos; char buf[1024]; int bufPos, bufLen; }; StreamReader *StreamReader::make(int (*getCharA)(void *data), void *dataA) { return new StreamReader(getCharA, dataA); } StreamReader::StreamReader(int (*getCharA)(void *data), void *dataA) { getChar = getCharA; data = dataA; streamPos = 0; bufPos = 0; bufLen = 0; } StreamReader::~StreamReader() { } int StreamReader::getByte(int pos) { if (!fillBuf(pos, 1)) { return -1; } return buf[pos - bufPos] & 0xff; } bool StreamReader::getU16BE(int pos, int *val) { if (!fillBuf(pos, 2)) { return false; } *val = ((buf[pos - bufPos] & 0xff) << 8) + (buf[pos - bufPos + 1] & 0xff); return true; } bool StreamReader::getU32BE(int pos, unsigned int *val) { if (!fillBuf(pos, 4)) { return false; } *val = ((buf[pos - bufPos] & 0xff) << 24) + ((buf[pos - bufPos + 1] & 0xff) << 16) + ((buf[pos - bufPos + 2] & 0xff) << 8) + (buf[pos - bufPos + 3] & 0xff); return true; } bool StreamReader::getU32LE(int pos, unsigned int *val) { if (!fillBuf(pos, 4)) { return false; } *val = (buf[pos - bufPos] & 0xff) + ((buf[pos - bufPos + 1] & 0xff) << 8) + ((buf[pos - bufPos + 2] & 0xff) << 16) + ((buf[pos - bufPos + 3] & 0xff) << 24); return true; } bool StreamReader::getUVarBE(int pos, int size, unsigned int *val) { int i; if (size < 1 || size > 4 || !fillBuf(pos, size)) { return false; } *val = 0; for (i = 0; i < size; ++i) { *val = (*val << 8) + (buf[pos - bufPos + i] & 0xff); } return true; } bool StreamReader::cmp(int pos, const char *s) { const int n = (int)strlen(s); if (!fillBuf(pos, n)) { return false; } const int posDiff = pos - bufPos; return !memcmp(buf + posDiff, s, n); } bool StreamReader::fillBuf(int pos, int len) { int c; if (pos < 0 || len < 0 || len > (int)sizeof(buf) || pos > INT_MAX - (int)sizeof(buf)) { return false; } if (pos < bufPos) { return false; } // if requested region will not fit in the current buffer... if (pos + len > bufPos + (int)sizeof(buf)) { // if the start of the requested data is already in the buffer, move // it to the start of the buffer if (pos < bufPos + bufLen) { bufLen -= pos - bufPos; memmove(buf, buf + (pos - bufPos), bufLen); bufPos = pos; // otherwise discard data from the // stream until we get to the requested position } else { bufPos += bufLen; bufLen = 0; while (bufPos < pos) { if ((c = (*getChar)(data)) < 0) { return false; } ++bufPos; } } } // read the rest of the requested data while (bufPos + bufLen < pos + len) { if ((c = (*getChar)(data)) < 0) { return false; } buf[bufLen++] = (char)c; } return true; } } //------------------------------------------------------------------------ static FoFiIdentifierType identify(Reader *reader); static FoFiIdentifierType identifyOpenType(Reader *reader); static FoFiIdentifierType identifyCFF(Reader *reader, int start); FoFiIdentifierType FoFiIdentifier::identifyMem(const char *file, int len) { MemReader *reader; FoFiIdentifierType type; if (!(reader = MemReader::make(file, len))) { return fofiIdError; } type = identify(reader); delete reader; return type; } FoFiIdentifierType FoFiIdentifier::identifyFile(const char *fileName) { FileReader *reader; FoFiIdentifierType type; if (!(reader = FileReader::make(fileName))) { return fofiIdError; } type = identify(reader); delete reader; return type; } FoFiIdentifierType FoFiIdentifier::identifyStream(int (*getChar)(void *data), void *data) { StreamReader *reader; FoFiIdentifierType type; if (!(reader = StreamReader::make(getChar, data))) { return fofiIdError; } type = identify(reader); delete reader; return type; } static FoFiIdentifierType identify(Reader *reader) { unsigned int n; //----- PFA if (reader->cmp(0, "%!PS-AdobeFont-1") || reader->cmp(0, "%!FontType1")) { return fofiIdType1PFA; } //----- PFB if (reader->getByte(0) == 0x80 && reader->getByte(1) == 0x01 && reader->getU32LE(2, &n)) { if ((n >= 16 && reader->cmp(6, "%!PS-AdobeFont-1")) || (n >= 11 && reader->cmp(6, "%!FontType1"))) { return fofiIdType1PFB; } } //----- TrueType if ((reader->getByte(0) == 0x00 && reader->getByte(1) == 0x01 && reader->getByte(2) == 0x00 && reader->getByte(3) == 0x00) || (reader->getByte(0) == 0x74 && // 'true' reader->getByte(1) == 0x72 && reader->getByte(2) == 0x75 && reader->getByte(3) == 0x65)) { return fofiIdTrueType; } if (reader->getByte(0) == 0x74 && // 'ttcf' reader->getByte(1) == 0x74 && reader->getByte(2) == 0x63 && reader->getByte(3) == 0x66) { return fofiIdTrueTypeCollection; } //----- OpenType if (reader->getByte(0) == 0x4f && // 'OTTO reader->getByte(1) == 0x54 && reader->getByte(2) == 0x54 && reader->getByte(3) == 0x4f) { return identifyOpenType(reader); } //----- CFF if (reader->getByte(0) == 0x01 && reader->getByte(1) == 0x00) { return identifyCFF(reader, 0); } // some tools embed CFF fonts with an extra whitespace char at the // beginning if (reader->getByte(1) == 0x01 && reader->getByte(2) == 0x00) { return identifyCFF(reader, 1); } return fofiIdUnknown; } static FoFiIdentifierType identifyOpenType(Reader *reader) { FoFiIdentifierType type; unsigned int offset; int nTables, i; if (!reader->getU16BE(4, &nTables)) { return fofiIdUnknown; } for (i = 0; i < nTables; ++i) { if (reader->cmp(12 + i * 16, "CFF ")) { if (reader->getU32BE(12 + i * 16 + 8, &offset) && offset < (unsigned int)INT_MAX) { type = identifyCFF(reader, (int)offset); if (type == fofiIdCFF8Bit) { type = fofiIdOpenTypeCFF8Bit; } else if (type == fofiIdCFFCID) { type = fofiIdOpenTypeCFFCID; } return type; } return fofiIdUnknown; } } return fofiIdUnknown; } static FoFiIdentifierType identifyCFF(Reader *reader, int start) { unsigned int offset0, offset1; int hdrSize, offSize0, offSize1, pos, endPos, b0, n, i; //----- read the header if (reader->getByte(start) != 0x01 || reader->getByte(start + 1) != 0x00) { return fofiIdUnknown; } if ((hdrSize = reader->getByte(start + 2)) < 0) { return fofiIdUnknown; } if ((offSize0 = reader->getByte(start + 3)) < 1 || offSize0 > 4) { return fofiIdUnknown; } pos = start + hdrSize; if (pos < 0) { return fofiIdUnknown; } //----- skip the name index if (!reader->getU16BE(pos, &n)) { return fofiIdUnknown; } if (n == 0) { pos += 2; } else { if ((offSize1 = reader->getByte(pos + 2)) < 1 || offSize1 > 4) { return fofiIdUnknown; } if (!reader->getUVarBE(pos + 3 + n * offSize1, offSize1, &offset1) || offset1 > (unsigned int)INT_MAX) { return fofiIdUnknown; } pos += 3 + (n + 1) * offSize1 + (int)offset1 - 1; } if (pos < 0) { return fofiIdUnknown; } //----- parse the top dict index if (!reader->getU16BE(pos, &n) || n < 1) { return fofiIdUnknown; } if ((offSize1 = reader->getByte(pos + 2)) < 1 || offSize1 > 4) { return fofiIdUnknown; } if (!reader->getUVarBE(pos + 3, offSize1, &offset0) || offset0 > (unsigned int)INT_MAX || !reader->getUVarBE(pos + 3 + offSize1, offSize1, &offset1) || offset1 > (unsigned int)INT_MAX || offset0 > offset1) { return fofiIdUnknown; } if (checkedAdd(pos + 3 + (n + 1) * offSize1, (int)offset0 - 1, &pos) || checkedAdd(pos + 3 + (n + 1) * offSize1, (int)offset1 - 1, &endPos) || pos < 0 || endPos < 0 || pos > endPos) { return fofiIdUnknown; } //----- parse the top dict, look for ROS as first entry // for a CID font, the top dict starts with: // ROS for (i = 0; i < 3; ++i) { b0 = reader->getByte(pos++); if (b0 == 0x1c) { pos += 2; } else if (b0 == 0x1d) { pos += 4; } else if (b0 >= 0xf7 && b0 <= 0xfe) { pos += 1; } else if (b0 < 0x20 || b0 > 0xf6) { return fofiIdCFF8Bit; } if (pos >= endPos || pos < 0) { return fofiIdCFF8Bit; } } if (pos + 1 < endPos && reader->getByte(pos) == 12 && reader->getByte(pos + 1) == 30) { return fofiIdCFFCID; } else { return fofiIdCFF8Bit; } } poppler-24.02.0/fofi/FoFiIdentifier.h000066400000000000000000000033241455701731300172720ustar00rootroot00000000000000//======================================================================== // // FoFiIdentifier.h // // Copyright 2009 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) 2018 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 // //======================================================================== #ifndef FOFIIDENTIFIER_H #define FOFIIDENTIFIER_H //------------------------------------------------------------------------ // FoFiIdentifier //------------------------------------------------------------------------ enum FoFiIdentifierType { fofiIdType1PFA, // Type 1 font in PFA format fofiIdType1PFB, // Type 1 font in PFB format fofiIdCFF8Bit, // 8-bit CFF font fofiIdCFFCID, // CID CFF font fofiIdTrueType, // TrueType font fofiIdTrueTypeCollection, // TrueType collection fofiIdOpenTypeCFF8Bit, // OpenType wrapper with 8-bit CFF font fofiIdOpenTypeCFFCID, // OpenType wrapper with CID CFF font fofiIdUnknown, // unknown type fofiIdError // error in reading the file }; class FoFiIdentifier { public: static FoFiIdentifierType identifyMem(const char *file, int len); static FoFiIdentifierType identifyFile(const char *fileName); static FoFiIdentifierType identifyStream(int (*getChar)(void *data), void *data); }; #endif poppler-24.02.0/fofi/FoFiTrueType.cc000066400000000000000000002135351455701731300171360ustar00rootroot00000000000000//======================================================================== // // FoFiTrueType.cc // // Copyright 1999-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 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 Tomas Are Haavet // Copyright (C) 2012 Suzuki Toshiya // Copyright (C) 2012, 2017 Adrian Johnson // Copyright (C) 2014 Thomas Freitag // Copyright (C) 2015 Aleksei Volkov // Copyright (C) 2015, 2016 William Bader // Copyright (C) 2018 Adam Reichold // Copyright (C) 2022 Zachary Travis // Copyright (C) 2022 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 // //======================================================================== #include #include #include #include #include #include "goo/gmem.h" #include "goo/GooLikely.h" #include "goo/GooString.h" #include "FoFiType1C.h" #include "FoFiTrueType.h" #include "poppler/Error.h" // // Terminology // ----------- // // character code = number used as an element of a text string // // character name = glyph name = name for a particular glyph within a // font // // glyph index = GID = position (within some internal table in the font) // where the instructions to draw a particular glyph are // stored // // Type 1 fonts // ------------ // // Type 1 fonts contain: // // Encoding: array of glyph names, maps char codes to glyph names // // Encoding[charCode] = charName // // CharStrings: dictionary of instructions, keyed by character names, // maps character name to glyph data // // CharStrings[charName] = glyphData // // TrueType fonts // -------------- // // TrueType fonts contain: // // 'cmap' table: mapping from character code to glyph index; there may // be multiple cmaps in a TrueType font // // cmap[charCode] = gid // // 'post' table: mapping from glyph index to glyph name // // post[gid] = glyphName // // Type 42 fonts // ------------- // // Type 42 fonts contain: // // Encoding: array of glyph names, maps char codes to glyph names // // Encoding[charCode] = charName // // CharStrings: dictionary of glyph indexes, keyed by character names, // maps character name to glyph index // // CharStrings[charName] = gid // //------------------------------------------------------------------------ #define ttcfTag 0x74746366 //------------------------------------------------------------------------ struct TrueTypeTable { unsigned int tag; unsigned int checksum; int offset; int origOffset; int len; }; struct TrueTypeCmap { int platform; int encoding; int offset; int len; int fmt; }; struct TrueTypeLoca { int idx; int origOffset; int newOffset; int len; }; #define cmapTag 0x636d6170 #define glyfTag 0x676c7966 #define headTag 0x68656164 #define hheaTag 0x68686561 #define hmtxTag 0x686d7478 #define locaTag 0x6c6f6361 #define nameTag 0x6e616d65 #define os2Tag 0x4f532f32 #define postTag 0x706f7374 #define vrt2Tag 0x76727432 #define vertTag 0x76657274 struct cmpTrueTypeLocaOffsetFunctor { bool operator()(const TrueTypeLoca loca1, const TrueTypeLoca loca2) { if (loca1.origOffset == loca2.origOffset) { return loca1.idx < loca2.idx; } return loca1.origOffset < loca2.origOffset; } }; struct cmpTrueTypeLocaIdxFunctor { bool operator()(const TrueTypeLoca loca1, const TrueTypeLoca loca2) { return loca1.idx < loca2.idx; } }; struct cmpTrueTypeTableTagFunctor { bool operator()(const TrueTypeTable &tab1, const TrueTypeTable &tab2) { return tab1.tag < tab2.tag; } }; //------------------------------------------------------------------------ struct T42Table { const char *tag; // 4-byte tag bool required; // required by the TrueType spec? }; // TrueType tables to be embedded in Type 42 fonts. // NB: the table names must be in alphabetical order here. #define nT42Tables 11 static const T42Table t42Tables[nT42Tables] = { { "cvt ", true }, { "fpgm", true }, { "glyf", true }, { "head", true }, { "hhea", true }, { "hmtx", true }, { "loca", true }, { "maxp", true }, { "prep", true }, { "vhea", false }, { "vmtx", false } }; #define t42HeadTable 3 #define t42LocaTable 6 #define t42GlyfTable 2 #define t42VheaTable 9 #define t42VmtxTable 10 //------------------------------------------------------------------------ // Glyph names in some arbitrary standard order that Apple uses for // their TrueType fonts. static const char *macGlyphNames[258] = { ".notdef", "null", "CR", "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quotesingle", "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", "grave", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", "Adieresis", "Aring", "Ccedilla", "Eacute", "Ntilde", "Odieresis", "Udieresis", "aacute", "agrave", "acircumflex", "adieresis", "atilde", "aring", "ccedilla", "eacute", "egrave", "ecircumflex", "edieresis", "iacute", "igrave", "icircumflex", "idieresis", "ntilde", "oacute", "ograve", "ocircumflex", "odieresis", "otilde", "uacute", "ugrave", "ucircumflex", "udieresis", "dagger", "degree", "cent", "sterling", "section", "bullet", "paragraph", "germandbls", "registered", "copyright", "trademark", "acute", "dieresis", "notequal", "AE", "Oslash", "infinity", "plusminus", "lessequal", "greaterequal", "yen", "mu", "partialdiff", "summation", "product", "pi", "integral", "ordfeminine", "ordmasculine", "Omega", "ae", "oslash", "questiondown", "exclamdown", "logicalnot", "radical", "florin", "approxequal", "increment", "guillemotleft", "guillemotright", "ellipsis", "nbspace", "Agrave", "Atilde", "Otilde", "OE", "oe", "endash", "emdash", "quotedblleft", "quotedblright", "quoteleft", "quoteright", "divide", "lozenge", "ydieresis", "Ydieresis", "fraction", "currency", "guilsinglleft", "guilsinglright", "fi", "fl", "daggerdbl", "periodcentered", "quotesinglbase", "quotedblbase", "perthousand", "Acircumflex", "Ecircumflex", "Aacute", "Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis", "Igrave", "Oacute", "Ocircumflex", "applelogo", "Ograve", "Uacute", "Ucircumflex", "Ugrave", "dotlessi", "circumflex", "tilde", "overscore", "breve", "dotaccent", "ring", "cedilla", "hungarumlaut", "ogonek", "caron", "Lslash", "lslash", "Scaron", "scaron", "Zcaron", "zcaron", "brokenbar", "Eth", "eth", "Yacute", "yacute", "Thorn", "thorn", "minus", "multiply", "onesuperior", "twosuperior", "threesuperior", "onehalf", "onequarter", "threequarters", "franc", "Gbreve", "gbreve", "Idot", "Scedilla", "scedilla", "Cacute", "cacute", "Ccaron", "ccaron", "dmacron" }; //------------------------------------------------------------------------ // FoFiTrueType //------------------------------------------------------------------------ std::unique_ptr FoFiTrueType::make(const unsigned char *fileA, int lenA, int faceIndexA) { // Cannot use std::make_unique, because the constructor is private auto ff = new FoFiTrueType(fileA, lenA, false, faceIndexA); if (!ff->parsedOk) { delete ff; return nullptr; } return std::unique_ptr(ff); } std::unique_ptr FoFiTrueType::load(const char *fileName, int faceIndexA) { char *fileA; int lenA; if (!(fileA = FoFiBase::readFile(fileName, &lenA))) { return nullptr; } // Cannot use std::make_unique, because the constructor is private auto ff = new FoFiTrueType((unsigned char *)fileA, lenA, true, faceIndexA); if (!ff->parsedOk) { delete ff; return nullptr; } return std::unique_ptr(ff); } 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; gsubLookupList = 0; parse(); } FoFiTrueType::~FoFiTrueType() { gfree(tables); gfree(cmaps); } int FoFiTrueType::getNumCmaps() const { return nCmaps; } int FoFiTrueType::getCmapPlatform(int i) const { return cmaps[i].platform; } int FoFiTrueType::getCmapEncoding(int i) const { return cmaps[i].encoding; } int FoFiTrueType::findCmap(int platform, int encoding) const { int i; for (i = 0; i < nCmaps; ++i) { if (cmaps[i].platform == platform && cmaps[i].encoding == encoding) { return i; } } return -1; } int FoFiTrueType::mapCodeToGID(int i, unsigned int c) const { int gid; unsigned int segCnt, segEnd, segStart, segDelta, segOffset; unsigned int cmapFirst, cmapLen; int pos, a, b, m; unsigned int high, low, idx; bool ok; if (i < 0 || i >= nCmaps) { return 0; } ok = true; pos = cmaps[i].offset; switch (cmaps[i].fmt) { case 0: if (c + 6 >= (unsigned int)cmaps[i].len) { return 0; } gid = getU8(cmaps[i].offset + 6 + c, &ok); break; case 2: high = c >> 8; low = c & 0xFFU; idx = getU16BE(pos + 6 + high * 2, &ok); segStart = getU16BE(pos + 6 + 512 + idx, &ok); segCnt = getU16BE(pos + 6 + 512 + idx + 2, &ok); segDelta = getS16BE(pos + 6 + 512 + idx + 4, &ok); segOffset = getU16BE(pos + 6 + 512 + idx + 6, &ok); if (low < segStart || low >= segStart + segCnt) { gid = 0; } else { int val = getU16BE(pos + 6 + 512 + idx + 6 + segOffset + (low - segStart) * 2, &ok); gid = val == 0 ? 0 : (val + segDelta) & 0xFFFFU; } break; case 4: segCnt = getU16BE(pos + 6, &ok) / 2; a = -1; b = segCnt - 1; segEnd = getU16BE(pos + 14 + 2 * b, &ok); if (c > segEnd) { // malformed font -- the TrueType spec requires the last segEnd // to be 0xffff return 0; } // invariant: seg[a].end < code <= seg[b].end while (b - a > 1 && ok) { m = (a + b) / 2; segEnd = getU16BE(pos + 14 + 2 * m, &ok); if (segEnd < c) { a = m; } else { b = m; } } segStart = getU16BE(pos + 16 + 2 * segCnt + 2 * b, &ok); segDelta = getU16BE(pos + 16 + 4 * segCnt + 2 * b, &ok); segOffset = getU16BE(pos + 16 + 6 * segCnt + 2 * b, &ok); if (c < segStart) { return 0; } if (segOffset == 0) { gid = (c + segDelta) & 0xffff; } else { gid = getU16BE(pos + 16 + 6 * segCnt + 2 * b + segOffset + 2 * (c - segStart), &ok); if (gid != 0) { gid = (gid + segDelta) & 0xffff; } } break; case 6: cmapFirst = getU16BE(pos + 6, &ok); cmapLen = getU16BE(pos + 8, &ok); if (c < cmapFirst || c >= cmapFirst + cmapLen) { return 0; } gid = getU16BE(pos + 10 + 2 * (c - cmapFirst), &ok); break; case 12: case 13: segCnt = getU32BE(pos + 12, &ok); a = -1; b = segCnt - 1; segEnd = getU32BE(pos + 16 + 12 * b + 4, &ok); if (c > segEnd) { return 0; } // invariant: seg[a].end < code <= seg[b].end while (b - a > 1 && ok) { m = (a + b) / 2; segEnd = getU32BE(pos + 16 + 12 * m + 4, &ok); if (segEnd < c) { a = m; } else { b = m; } } segStart = getU32BE(pos + 16 + 12 * b, &ok); segDelta = getU32BE(pos + 16 + 12 * b + 8, &ok); if (c < segStart) { return 0; } // In format 12, the glyph codes increment through // each segment; in format 13 the same glyph code is used // for an entire segment. gid = segDelta + (cmaps[i].fmt == 12 ? (c - segStart) : 0); break; default: return 0; } if (!ok) { return 0; } return gid; } int FoFiTrueType::mapNameToGID(const char *name) const { const auto gid = nameToGID.find(name); if (gid == nameToGID.end()) { return 0; } return gid->second; } bool FoFiTrueType::getCFFBlock(char **start, int *length) const { int i; if (!openTypeCFF || !tables) { return false; } i = seekTable("CFF "); if (i < 0 || !checkRegion(tables[i].offset, tables[i].len)) { return false; } *start = (char *)file + tables[i].offset; *length = tables[i].len; return true; } int *FoFiTrueType::getCIDToGIDMap(int *nCIDs) const { char *start; int length; FoFiType1C *ff; int *map; *nCIDs = 0; if (!getCFFBlock(&start, &length)) { return nullptr; } if (!(ff = FoFiType1C::make((unsigned char *)start, length))) { return nullptr; } map = ff->getCIDToGIDMap(nCIDs); delete ff; return map; } int FoFiTrueType::getEmbeddingRights() const { int i, fsType; bool ok; if ((i = seekTable("OS/2")) < 0) { return 4; } ok = true; fsType = getU16BE(tables[i].offset + 8, &ok); if (!ok) { return 4; } if (fsType & 0x0008) { return 2; } if (fsType & 0x0004) { return 1; } if (fsType & 0x0002) { return 0; } return 3; } void FoFiTrueType::getFontMatrix(double *mat) const { char *start; int length; FoFiType1C *ff; if (!getCFFBlock(&start, &length)) { return; } if (!(ff = FoFiType1C::make((unsigned char *)start, length))) { return; } ff->getFontMatrix(mat); delete ff; } void FoFiTrueType::convertToType42(const char *psName, char **encoding, int *codeToGID, FoFiOutputFunc outputFunc, void *outputStream) const { int maxUsedGlyph; bool ok; if (openTypeCFF) { return; } // 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()); // begin the font dictionary (*outputFunc)(outputStream, "10 dict begin\n", 14); (*outputFunc)(outputStream, "/FontName /", 11); (*outputFunc)(outputStream, psName, strlen(psName)); (*outputFunc)(outputStream, " def\n", 5); (*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, "/PaintType 0 def\n", 17); // write the guts of the dictionary cvtEncoding(encoding, outputFunc, outputStream); cvtCharStrings(encoding, codeToGID, outputFunc, outputStream); cvtSfnts(outputFunc, outputStream, nullptr, false, &maxUsedGlyph); // end the dictionary and define the font (*outputFunc)(outputStream, "FontName currentdict end definefont pop\n", 40); } void FoFiTrueType::convertToType1(const char *psName, const char **newEncoding, bool ascii, FoFiOutputFunc outputFunc, void *outputStream) const { char *start; int length; FoFiType1C *ff; if (!getCFFBlock(&start, &length)) { return; } if (!(ff = FoFiType1C::make((unsigned char *)start, length))) { return; } ff->convertToType1(psName, newEncoding, ascii, outputFunc, outputStream); delete ff; } void FoFiTrueType::convertToCIDType2(const char *psName, const int *cidMap, int nCIDs, bool needVerticalMetrics, FoFiOutputFunc outputFunc, void *outputStream) const { int cid, maxUsedGlyph; bool ok; int i, j, k; if (openTypeCFF) { return; } // 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()); // begin the font dictionary (*outputFunc)(outputStream, "20 dict begin\n", 14); (*outputFunc)(outputStream, "/CIDFontName /", 14); (*outputFunc)(outputStream, psName, strlen(psName)); (*outputFunc)(outputStream, " def\n", 5); (*outputFunc)(outputStream, "/CIDFontType 2 def\n", 19); (*outputFunc)(outputStream, "/FontType 42 def\n", 17); (*outputFunc)(outputStream, "/CIDSystemInfo 3 dict dup begin\n", 32); (*outputFunc)(outputStream, " /Registry (Adobe) def\n", 24); (*outputFunc)(outputStream, " /Ordering (Identity) def\n", 27); (*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) { (*outputFunc)(outputStream, "/CIDMap [", 9); for (i = 0; i < nCIDs; i += 32768 - 16) { (*outputFunc)(outputStream, "<\n", 2); for (j = 0; j < 32768 - 16 && i + j < nCIDs; j += 16) { (*outputFunc)(outputStream, " ", 2); for (k = 0; k < 16 && i + j + k < nCIDs; ++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, "\n", 1); } (*outputFunc)(outputStream, " >", 3); } (*outputFunc)(outputStream, "\n", 1); (*outputFunc)(outputStream, "] def\n", 6); } else { (*outputFunc)(outputStream, "/CIDMap <\n", 10); for (i = 0; i < nCIDs; i += 16) { (*outputFunc)(outputStream, " ", 2); for (j = 0; j < 16 && i + j < nCIDs; ++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, "\n", 1); } (*outputFunc)(outputStream, "> def\n", 6); } } 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()); 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()); buf = GooString::format(" 2 copy dup 2 mul exch {0:d} add -8 bitshift put\n", i); (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); 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, " } 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()); buf = GooString::format(" 0 1 {0:d} {{\n", nGlyphs - 1); (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); (*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); (*outputFunc)(outputStream, "def\n", 4); } } (*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, "/PaintType 0 def\n", 17); (*outputFunc)(outputStream, "/Encoding [] readonly def\n", 26); (*outputFunc)(outputStream, "/CharStrings 1 dict dup begin\n", 30); (*outputFunc)(outputStream, " /.notdef 0 def\n", 17); (*outputFunc)(outputStream, " end readonly def\n", 19); // write the guts of the dictionary cvtSfnts(outputFunc, outputStream, nullptr, needVerticalMetrics, &maxUsedGlyph); // end the dictionary and define the font (*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 { char *start; int length; FoFiType1C *ff; if (!getCFFBlock(&start, &length)) { return; } if (!(ff = FoFiType1C::make((unsigned char *)start, length))) { return; } ff->convertToCIDType0(psName, cidMap, nCIDs, outputFunc, outputStream); delete ff; } void FoFiTrueType::convertToType0(const char *psName, int *cidMap, int nCIDs, bool needVerticalMetrics, int *maxValidGlyph, FoFiOutputFunc outputFunc, void *outputStream) const { GooString *sfntsName; int maxUsedGlyph, n, i, j; *maxValidGlyph = -1; if (openTypeCFF) { return; } // write the Type 42 sfnts array sfntsName = (new GooString(psName))->append("_sfnts"); cvtSfnts(outputFunc, outputStream, sfntsName, needVerticalMetrics, &maxUsedGlyph); delete sfntsName; // write the descendant Type 42 fonts // (The following is a kludge: nGlyphs is the glyph count from the // maxp table; maxUsedGlyph is the max glyph number that has a // non-zero-length description, from the loca table. The problem is // that some TrueType font subsets fail to change the glyph count, // i.e., nGlyphs is much larger than maxUsedGlyph+1, which results // in an unnecessarily huge Type 0 font. But some other PDF files // have fonts with only zero or one used glyph, and a content stream // that refers to one of the unused glyphs -- this results in PS // errors if we simply use maxUsedGlyph+1 for the Type 0 font. So // we compromise by always defining at least 256 glyphs.) // Some fonts have a large nGlyphs but maxUsedGlyph of 0. // These fonts might reference any glyph. // Return the last written glyph number in maxValidGlyph. // PSOutputDev::drawString() can use maxValidGlyph to avoid // 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; } else if (nGlyphs > maxUsedGlyph + 256) { if (maxUsedGlyph <= 255) { n = 256; } else { n = maxUsedGlyph + 1; } } else { n = nGlyphs; } *maxValidGlyph = n - 1; for (i = 0; i < n; i += 256) { (*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()); (*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, "/PaintType 0 def\n", 17); (*outputFunc)(outputStream, "/sfnts ", 7); (*outputFunc)(outputStream, psName, strlen(psName)); (*outputFunc)(outputStream, "_sfnts def\n", 11); (*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, "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()); } (*outputFunc)(outputStream, "end readonly def\n", 17); (*outputFunc)(outputStream, "FontName currentdict end definefont pop\n", 40); } // write the Type 0 parent font (*outputFunc)(outputStream, "16 dict begin\n", 14); (*outputFunc)(outputStream, "/FontName /", 11); (*outputFunc)(outputStream, psName, strlen(psName)); (*outputFunc)(outputStream, " def\n", 5); (*outputFunc)(outputStream, "/FontType 0 def\n", 16); (*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 < n; i += 256) { const std::unique_ptr buf = GooString::format("{0:d}\n", i >> 8); (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); } (*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()); } (*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 { char *start; int length; FoFiType1C *ff; if (!getCFFBlock(&start, &length)) { return; } if (!(ff = FoFiType1C::make((unsigned char *)start, length))) { return; } ff->convertToType0(psName, cidMap, nCIDs, outputFunc, outputStream); delete ff; } void FoFiTrueType::cvtEncoding(char **encoding, FoFiOutputFunc outputFunc, void *outputStream) const { const char *name; int i; (*outputFunc)(outputStream, "/Encoding 256 array\n", 20); if (encoding) { for (i = 0; i < 256; ++i) { if (!(name = encoding[i])) { name = ".notdef"; } const std::unique_ptr buf = GooString::format("dup {0:d} /", i); (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); (*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()); } } (*outputFunc)(outputStream, "readonly def\n", 13); } void FoFiTrueType::cvtCharStrings(char **encoding, const int *codeToGID, FoFiOutputFunc outputFunc, void *outputStream) const { const char *name; char buf2[16]; int i, k; // always define '.notdef' (*outputFunc)(outputStream, "/CharStrings 256 dict dup begin\n", 32); (*outputFunc)(outputStream, "/.notdef 0 def\n", 15); // if there's no 'cmap' table, punt if (nCmaps == 0) { goto err; } // map char name to glyph index: // 1. use encoding to map name to char code // 2. use codeToGID to map char code to glyph index // N.B. We do this in reverse order because font subsets can have // weird encodings that use the same character name twice, and // the first definition is probably the one we want. k = 0; // make gcc happy for (i = 255; i >= 0; --i) { if (encoding) { name = encoding[i]; } else { sprintf(buf2, "c%02x", i); name = buf2; } if (name && strcmp(name, ".notdef")) { k = codeToGID[i]; // note: Distiller (maybe Adobe's PS interpreter in general) // doesn't like TrueType fonts that have CharStrings entries // which point to nonexistent glyphs, hence the (k < nGlyphs) // test 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()); } } } err: (*outputFunc)(outputStream, "end readonly def\n", 17); } void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc, void *outputStream, const GooString *name, bool needVerticalMetrics, int *maxUsedGlyph) const { unsigned char headData[54]; TrueTypeLoca *locaTable; unsigned char *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] = { 0, 1, 0, 0, // table version number 0, 0, // ascent 0, 0, // descent 0, 0, // reserved 0, 0, // max advance height 0, 0, // min top side bearing 0, 0, // min bottom side bearing 0, 0, // y max extent 0, 0, // caret slope rise 0, 1, // caret slope run 0, 0, // caret offset 0, 0, // reserved 0, 0, // reserved 0, 0, // reserved 0, 0, // reserved 0, 0, // metric data format 0, 1 // number of advance heights in vmtx table }; unsigned char *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) { return; } pos = tables[i].offset; if (!checkRegion(pos, 54)) { return; } memcpy(headData, 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 // (I've encountered fonts with loca format set to 0x0100 instead of 0x0001) if (locaFmt != 0 && locaFmt != 1) { headData[50] = 0; headData[51] = 1; } // read the original 'loca' table, pad entries out to 4 bytes, and // sort it into proper order -- some (non-compliant) fonts have // out-of-order loca tables; in order to correctly handle the case // where (compliant) fonts have empty entries in the middle of the // 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)); i = seekTable("loca"); pos = tables[i].offset; i = seekTable("glyf"); glyfTableLen = tables[i].len; ok = true; for (i = 0; i <= nGlyphs; ++i) { locaTable[i].idx = i; if (locaFmt) { locaTable[i].origOffset = (int)getU32BE(pos + i * 4, &ok); } else { locaTable[i].origOffset = 2 * getU16BE(pos + i * 2, &ok); } if (locaTable[i].origOffset > glyfTableLen) { locaTable[i].origOffset = glyfTableLen; } } std::sort(locaTable, locaTable + nGlyphs + 1, 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()); pos = 0; for (i = 0; i <= nGlyphs; ++i) { locaTable[i].newOffset = pos; int newPos; if (unlikely(checkedAdd(pos, locaTable[i].len, &newPos))) { ok = false; } else { pos = newPos; if (pos & 3) { pos += 4 - (pos & 3); } } if (locaTable[i].len > 0) { *maxUsedGlyph = i; } } // construct the new 'loca' table locaData = (unsigned char *)gmallocn(nGlyphs + 1, (locaFmt ? 4 : 2)); for (i = 0; i <= nGlyphs; ++i) { pos = locaTable[i].newOffset; if (locaFmt) { locaData[4 * i] = (unsigned char)(pos >> 24); locaData[4 * i + 1] = (unsigned char)(pos >> 16); locaData[4 * i + 2] = (unsigned char)(pos >> 8); locaData[4 * i + 3] = (unsigned char)pos; } else { locaData[2 * i] = (unsigned char)(pos >> 9); locaData[2 * i + 1] = (unsigned char)(pos >> 1); } } // count the number of tables nNewTables = 0; for (i = 0; i < nT42Tables; ++i) { if (t42Tables[i].required || seekTable(t42Tables[i].tag) >= 0) { ++nNewTables; } } vmtxTab = nullptr; // make gcc happy vmtxTabLength = 0; advance = 0; // make gcc happy if (needVerticalMetrics) { needVhea = seekTable("vhea") < 0; needVmtx = seekTable("vmtx") < 0; if (needVhea || needVmtx) { i = seekTable("head"); advance = getU16BE(tables[i].offset + 18, &ok); // units per em if (needVhea) { ++nNewTables; } if (needVmtx) { ++nNewTables; } } } // construct the new table headers, including table checksums // (pad each table out to a multiple of 4 bytes) pos = 12 + nNewTables * 16; k = 0; for (i = 0; i < nT42Tables; ++i) { length = -1; checksum = 0; // make gcc happy if (i == t42HeadTable) { length = 54; checksum = computeTableChecksum(headData, 54); } else if (i == t42LocaTable) { length = (nGlyphs + 1) * (locaFmt ? 4 : 2); checksum = computeTableChecksum(locaData, length); } else if (i == t42GlyfTable) { length = 0; checksum = 0; glyfPos = tables[seekTable("glyf")].offset; for (j = 0; j < nGlyphs; ++j) { length += locaTable[j].len; if (length & 3) { length += 4 - (length & 3); } if (checkRegion(glyfPos + locaTable[j].origOffset, locaTable[j].len)) { checksum += computeTableChecksum(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); } } else if (needVerticalMetrics && i == t42VheaTable) { vheaTab[10] = advance / 256; // max advance height vheaTab[11] = advance % 256; length = sizeof(vheaTab); checksum = computeTableChecksum(vheaTab, length); } else if (needVerticalMetrics && i == t42VmtxTable) { length = 4 + (nGlyphs - 1) * 2; vmtxTabLength = length; vmtxTab = (unsigned char *)gmalloc(length); 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); } else if (t42Tables[i].required) { //~ error(-1, "Embedded TrueType font is missing a required table ('%s')", //~ t42Tables[i].tag); length = 0; checksum = 0; } } if (length >= 0) { newTables[k].tag = ((t42Tables[i].tag[0] & 0xff) << 24) | ((t42Tables[i].tag[1] & 0xff) << 16) | ((t42Tables[i].tag[2] & 0xff) << 8) | (t42Tables[i].tag[3] & 0xff); newTables[k].checksum = checksum; newTables[k].offset = pos; newTables[k].len = length; int newPos; if (unlikely(checkedAdd(pos, length, &newPos))) { ok = false; } else { pos = newPos; if (pos & 3) { pos += 4 - (length & 3); } } ++k; } } if (unlikely(k < nNewTables)) { error(errSyntaxWarning, -1, "unexpected number of tables"); nNewTables = k; } // construct the table directory tableDir[0] = 0x00; // sfnt version tableDir[1] = 0x01; tableDir[2] = 0x00; tableDir[3] = 0x00; tableDir[4] = 0; // numTables tableDir[5] = nNewTables; tableDir[6] = 0; // searchRange tableDir[7] = (unsigned char)128; tableDir[8] = 0; // entrySelector tableDir[9] = 3; tableDir[10] = 0; // rangeShift tableDir[11] = (unsigned char)(16 * nNewTables - 128); pos = 12; for (i = 0; i < nNewTables; ++i) { tableDir[pos] = (unsigned char)(newTables[i].tag >> 24); tableDir[pos + 1] = (unsigned char)(newTables[i].tag >> 16); tableDir[pos + 2] = (unsigned char)(newTables[i].tag >> 8); tableDir[pos + 3] = (unsigned char)newTables[i].tag; tableDir[pos + 4] = (unsigned char)(newTables[i].checksum >> 24); tableDir[pos + 5] = (unsigned char)(newTables[i].checksum >> 16); tableDir[pos + 6] = (unsigned char)(newTables[i].checksum >> 8); tableDir[pos + 7] = (unsigned char)newTables[i].checksum; tableDir[pos + 8] = (unsigned char)(newTables[i].offset >> 24); tableDir[pos + 9] = (unsigned char)(newTables[i].offset >> 16); tableDir[pos + 10] = (unsigned char)(newTables[i].offset >> 8); tableDir[pos + 11] = (unsigned char)newTables[i].offset; tableDir[pos + 12] = (unsigned char)(newTables[i].len >> 24); tableDir[pos + 13] = (unsigned char)(newTables[i].len >> 16); tableDir[pos + 14] = (unsigned char)(newTables[i].len >> 8); tableDir[pos + 15] = (unsigned char)newTables[i].len; pos += 16; } // compute the font checksum and store it in the head table checksum = computeTableChecksum(tableDir, 12 + nNewTables * 16); for (i = 0; i < nNewTables; ++i) { checksum += newTables[i].checksum; } checksum = 0xb1b0afba - checksum; // because the TrueType spec says so headData[8] = (unsigned char)(checksum >> 24); headData[9] = (unsigned char)(checksum >> 16); headData[10] = (unsigned char)(checksum >> 8); headData[11] = (unsigned char)checksum; // start the sfnts array if (name) { (*outputFunc)(outputStream, "/", 1); (*outputFunc)(outputStream, name->c_str(), name->getLength()); (*outputFunc)(outputStream, " [\n", 3); } else { (*outputFunc)(outputStream, "/sfnts [\n", 9); } // write the table directory dumpString(tableDir, 12 + nNewTables * 16, outputFunc, outputStream); // write the tables for (i = 0; i < nNewTables; ++i) { if (i == t42HeadTable) { dumpString(headData, 54, outputFunc, outputStream); } else if (i == t42LocaTable) { length = (nGlyphs + 1) * (locaFmt ? 4 : 2); dumpString(locaData, length, 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); } } } else { // length == 0 means the table is missing and the error was // already reported during the construction of the table // 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); } 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); } else if (needVerticalMetrics && i == t42VmtxTable) { if (unlikely(length > vmtxTabLength)) { error(errSyntaxWarning, -1, "length bigger than vmtxTab size"); length = vmtxTabLength; } dumpString(vmtxTab, length, outputFunc, outputStream); } } } } // 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 { 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()); } if (i % (65536 - 32) == 65536 - 64) { (*outputFunc)(outputStream, ">\n<", 3); } else if (i + 32 < length) { (*outputFunc)(outputStream, "\n", 1); } } if (length & 3) { pad = 4 - (length & 3); for (i = 0; i < pad; ++i) { (*outputFunc)(outputStream, "00", 2); } } // add an extra zero byte because the Adobe Type 42 spec says so (*outputFunc)(outputStream, "00>\n", 4); } unsigned int FoFiTrueType::computeTableChecksum(const unsigned char *data, int length) const { 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); checksum += word; } if (length & 3) { word = 0; i = length & ~3; switch (length & 3) { case 3: word |= (data[i + 2] & 0xff) << 8; // fallthrough case 2: word |= (data[i + 1] & 0xff) << 16; // fallthrough case 1: word |= (data[i] & 0xff) << 24; break; } checksum += word; } return checksum; } void FoFiTrueType::parse() { unsigned int topTag; int pos, ver, i, j; parsedOk = true; // look for a collection (TTC) topTag = getU32BE(0, &parsedOk); if (!parsedOk) { return; } if (topTag == ttcfTag) { /* TTC font */ int dircount; dircount = getU32BE(8, &parsedOk); if (!parsedOk) { return; } if (!dircount) { parsedOk = false; return; } if (faceIndex >= dircount) { faceIndex = 0; } pos = getU32BE(12 + faceIndex * 4, &parsedOk); if (!parsedOk) { return; } } else { pos = 0; } // check the sfnt version ver = getU32BE(pos, &parsedOk); if (!parsedOk) { return; } openTypeCFF = ver == 0x4f54544f; // 'OTTO' // read the table directory nTables = getU16BE(pos + 4, &parsedOk); if (!parsedOk) { return; } tables = (TrueTypeTable *)gmallocn(nTables, sizeof(TrueTypeTable)); pos += 12; j = 0; for (i = 0; i < nTables; ++i) { tables[j].tag = getU32BE(pos, &parsedOk); tables[j].checksum = getU32BE(pos + 4, &parsedOk); tables[j].offset = (int)getU32BE(pos + 8, &parsedOk); tables[j].len = (int)getU32BE(pos + 12, &parsedOk); if (unlikely((tables[j].offset < 0) || (tables[j].len < 0) || (tables[j].offset < INT_MAX - tables[j].len) || (tables[j].len > INT_MAX - tables[j].offset) || (tables[j].offset + tables[j].len >= tables[j].offset && tables[j].offset + tables[j].len <= len))) { // ignore any bogus entries in the table directory ++j; } pos += 16; } if (nTables != j) { nTables = j; tables = (TrueTypeTable *)greallocn_checkoverflow(tables, nTables, sizeof(TrueTypeTable)); } if (!parsedOk || tables == nullptr) { parsedOk = false; return; } // check for tables that are required by both the TrueType spec and // the Type 42 spec if (seekTable("head") < 0 || seekTable("hhea") < 0 || seekTable("maxp") < 0 || (!openTypeCFF && seekTable("loca") < 0) || (!openTypeCFF && seekTable("glyf") < 0) || (openTypeCFF && (seekTable("CFF ") < 0 && seekTable("CFF2") < 0))) { parsedOk = false; return; } // read the cmaps if ((i = seekTable("cmap")) >= 0) { pos = tables[i].offset + 2; nCmaps = getU16BE(pos, &parsedOk); pos += 2; if (!parsedOk) { return; } cmaps = (TrueTypeCmap *)gmallocn(nCmaps, sizeof(TrueTypeCmap)); for (j = 0; j < nCmaps; ++j) { cmaps[j].platform = getU16BE(pos, &parsedOk); cmaps[j].encoding = getU16BE(pos + 2, &parsedOk); cmaps[j].offset = tables[i].offset + getU32BE(pos + 4, &parsedOk); pos += 8; cmaps[j].fmt = getU16BE(cmaps[j].offset, &parsedOk); cmaps[j].len = getU16BE(cmaps[j].offset + 2, &parsedOk); } if (!parsedOk) { return; } } else { nCmaps = 0; } // get the number of glyphs from the maxp table i = seekTable("maxp"); nGlyphs = getU16BE(tables[i].offset + 4, &parsedOk); if (!parsedOk) { return; } // get the bbox and loca table format from the head table i = seekTable("head"); bbox[0] = getS16BE(tables[i].offset + 36, &parsedOk); bbox[1] = getS16BE(tables[i].offset + 38, &parsedOk); bbox[2] = getS16BE(tables[i].offset + 40, &parsedOk); bbox[3] = getS16BE(tables[i].offset + 42, &parsedOk); locaFmt = getS16BE(tables[i].offset + 50, &parsedOk); if (!parsedOk) { return; } // read the post table readPostTable(); } void FoFiTrueType::readPostTable() { std::string name; int tablePos, postFmt, stringIdx, stringPos; bool ok; int i, j, n, m; ok = true; if ((i = seekTable("post")) < 0) { return; } tablePos = tables[i].offset; postFmt = getU32BE(tablePos, &ok); if (!ok) { goto err; } if (postFmt == 0x00010000) { nameToGID.reserve(258); for (i = 0; i < 258; ++i) { nameToGID.emplace(macGlyphNames[i], i); } } else if (postFmt == 0x00020000) { nameToGID.reserve(258); n = getU16BE(tablePos + 32, &ok); if (!ok) { goto err; } if (n > nGlyphs) { n = nGlyphs; } stringIdx = 0; stringPos = tablePos + 34 + 2 * n; for (i = 0; i < n; ++i) { ok = true; j = getU16BE(tablePos + 34 + 2 * i, &ok); if (j < 258) { nameToGID[macGlyphNames[j]] = i; } else { j -= 258; if (j != stringIdx) { for (stringIdx = 0, stringPos = tablePos + 34 + 2 * n; stringIdx < j; ++stringIdx, stringPos += 1 + getU8(stringPos, &ok)) { ; } if (!ok) { continue; } } m = getU8(stringPos, &ok); if (!ok || !checkRegion(stringPos + 1, m)) { continue; } name.assign((char *)&file[stringPos + 1], m); nameToGID[name] = i; ++stringIdx; stringPos += 1 + m; } } } else if (postFmt == 0x00028000) { nameToGID.reserve(258); for (i = 0; i < nGlyphs; ++i) { j = getU8(tablePos + 32 + i, &ok); if (!ok) { continue; } if (j < 258) { nameToGID[macGlyphNames[j]] = i; } } } return; err: nameToGID.clear(); } 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) { if (tables[i].tag == tagI) { return i; } } return -1; } unsigned int FoFiTrueType::charToTag(const char *tagName) { int n = strlen(tagName); unsigned int tag = 0; int i; if (n > 4) { n = 4; } for (i = 0; i < n; i++) { tag <<= 8; tag |= tagName[i] & 0xff; } for (; i < 4; i++) { tag <<= 8; tag |= ' '; } return tag; } /* setup GSUB table data Only supporting vertical text substitution. */ int FoFiTrueType::setupGSUB(const char *scriptName) { return setupGSUB(scriptName, nullptr); } /* setup GSUB table data Only supporting vertical text substitution. */ int FoFiTrueType::setupGSUB(const char *scriptName, const char *languageName) { unsigned int gsubTable; unsigned int i; unsigned int scriptList, featureList; unsigned int scriptCount; unsigned int tag; unsigned int scriptTable = 0; unsigned int langSys; unsigned int featureCount; unsigned int featureIndex; unsigned int ftable = 0; unsigned int llist; unsigned int scriptTag; int x; unsigned int pos; if (scriptName == nullptr) { gsubFeatureTable = 0; return 0; } scriptTag = charToTag(scriptName); /* read GSUB Header */ if ((x = seekTable("GSUB")) < 0) { return 0; /* GSUB table not found */ } gsubTable = tables[x].offset; pos = gsubTable + 4; scriptList = getU16BE(pos, &parsedOk); pos += 2; featureList = getU16BE(pos, &parsedOk); pos += 2; llist = getU16BE(pos, &parsedOk); gsubLookupList = llist + gsubTable; /* change to offset from top of file */ /* read script list table */ pos = gsubTable + scriptList; scriptCount = getU16BE(pos, &parsedOk); pos += 2; /* find script */ for (i = 0; i < scriptCount; i++) { tag = getU32BE(pos, &parsedOk); pos += 4; scriptTable = getU16BE(pos, &parsedOk); pos += 2; if (tag == scriptTag) { /* found */ break; } } if (i >= scriptCount) { /* not found */ return 0; } /* read script table */ /* use default language system */ pos = gsubTable + scriptList + scriptTable; langSys = 0; if (languageName) { unsigned int langTag = charToTag(languageName); unsigned int langCount = getU16BE(pos + 2, &parsedOk); for (i = 0; i < langCount && langSys == 0; i++) { tag = getU32BE(pos + 4 + i * (4 + 2), &parsedOk); if (tag == langTag) { langSys = getU16BE(pos + 4 + i * (4 + 2) + 4, &parsedOk); } } } if (langSys == 0) { /* default language system */ langSys = getU16BE(pos, &parsedOk); } /* read LangSys table */ if (langSys == 0) { /* no default LangSys */ return 0; } pos = gsubTable + scriptList + scriptTable + langSys + 2; featureIndex = getU16BE(pos, &parsedOk); /* ReqFeatureIndex */ pos += 2; if (featureIndex != 0xffff) { unsigned int tpos; /* read feature record */ tpos = gsubTable + featureList; featureCount = getU16BE(tpos, &parsedOk); tpos = gsubTable + featureList + 2 + featureIndex * (4 + 2); tag = getU32BE(tpos, &parsedOk); tpos += 4; if (tag == vrt2Tag) { /* vrt2 is preferred, overwrite vert */ ftable = getU16BE(tpos, &parsedOk); /* convert to offset from file top */ gsubFeatureTable = ftable + gsubTable + featureList; return 0; } else if (tag == vertTag) { ftable = getU16BE(tpos, &parsedOk); } } featureCount = getU16BE(pos, &parsedOk); pos += 2; /* find 'vrt2' or 'vert' feature */ for (i = 0; i < featureCount; i++) { unsigned int oldPos; featureIndex = getU16BE(pos, &parsedOk); pos += 2; oldPos = pos; /* save position */ /* read feature record */ pos = gsubTable + featureList + 2 + featureIndex * (4 + 2); tag = getU32BE(pos, &parsedOk); pos += 4; if (tag == vrt2Tag) { /* vrt2 is preferred, overwrite vert */ ftable = getU16BE(pos, &parsedOk); break; } else if (ftable == 0 && tag == vertTag) { ftable = getU16BE(pos, &parsedOk); } pos = oldPos; /* restore old position */ } if (ftable == 0) { /* vert nor vrt2 are not found */ return 0; } /* convert to offset from file top */ gsubFeatureTable = ftable + gsubTable + featureList; return 0; } unsigned int FoFiTrueType::doMapToVertGID(unsigned int orgGID) { unsigned int lookupCount; unsigned int lookupListIndex; unsigned int i; unsigned int gid = 0; unsigned int pos; pos = gsubFeatureTable + 2; lookupCount = getU16BE(pos, &parsedOk); pos += 2; for (i = 0; i < lookupCount; i++) { lookupListIndex = getU16BE(pos, &parsedOk); pos += 2; if ((gid = scanLookupList(lookupListIndex, orgGID)) != 0) { break; } } return gid; } unsigned int FoFiTrueType::mapToVertGID(unsigned int orgGID) { unsigned int mapped; if (gsubFeatureTable == 0) { return orgGID; } if ((mapped = doMapToVertGID(orgGID)) != 0) { return mapped; } return orgGID; } unsigned int FoFiTrueType::scanLookupList(unsigned int listIndex, unsigned int orgGID) { unsigned int lookupTable; unsigned int subTableCount; unsigned int subTable; unsigned int i; unsigned int gid = 0; unsigned int pos; if (gsubLookupList == 0) { return 0; /* no lookup list */ } pos = gsubLookupList + 2 + listIndex * 2; lookupTable = getU16BE(pos, &parsedOk); /* read lookup table */ pos = gsubLookupList + lookupTable + 4; subTableCount = getU16BE(pos, &parsedOk); pos += 2; ; for (i = 0; i < subTableCount; i++) { subTable = getU16BE(pos, &parsedOk); pos += 2; if ((gid = scanLookupSubTable(gsubLookupList + lookupTable + subTable, orgGID)) != 0) { break; } } return gid; } unsigned int FoFiTrueType::scanLookupSubTable(unsigned int subTable, unsigned int orgGID) { unsigned int format; unsigned int coverage; int delta; int glyphCount; unsigned int substitute; unsigned int gid = 0; int coverageIndex; int pos; pos = subTable; format = getU16BE(pos, &parsedOk); pos += 2; coverage = getU16BE(pos, &parsedOk); pos += 2; if ((coverageIndex = checkGIDInCoverage(subTable + coverage, orgGID)) >= 0) { switch (format) { case 1: /* format 1 */ delta = getS16BE(pos, &parsedOk); pos += 2; gid = orgGID + delta; break; case 2: /* format 2 */ glyphCount = getS16BE(pos, &parsedOk); pos += 2; if (glyphCount > coverageIndex) { pos += coverageIndex * 2; substitute = getU16BE(pos, &parsedOk); gid = substitute; } break; default: /* unknown format */ break; } } return gid; } int FoFiTrueType::checkGIDInCoverage(unsigned int coverage, unsigned int orgGID) { int index = -1; unsigned int format; unsigned int count; unsigned int i; unsigned int pos; pos = coverage; format = getU16BE(pos, &parsedOk); pos += 2; switch (format) { case 1: count = getU16BE(pos, &parsedOk); pos += 2; // In some poor CJK fonts, key GIDs are not sorted, // thus we cannot finish checking even when the range // including orgGID seems to have already passed. for (i = 0; i < count; i++) { unsigned int gid; gid = getU16BE(pos, &parsedOk); pos += 2; if (gid == orgGID) { /* found */ index = i; break; } } break; case 2: count = getU16BE(pos, &parsedOk); pos += 2; for (i = 0; i < count; i++) { unsigned int startGID, endGID; unsigned int startIndex; startGID = getU16BE(pos, &parsedOk); pos += 2; endGID = getU16BE(pos, &parsedOk); pos += 2; startIndex = getU16BE(pos, &parsedOk); pos += 2; // In some poor CJK fonts, key GIDs are not sorted, // thus we cannot finish checking even when the range // including orgGID seems to have already passed. if (startGID <= orgGID && orgGID <= endGID) { /* found */ index = startIndex + orgGID - startGID; break; } } break; default: break; } return index; } poppler-24.02.0/fofi/FoFiTrueType.h000066400000000000000000000204611455701731300167720ustar00rootroot00000000000000//======================================================================== // // FoFiTrueType.h // // Copyright 1999-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 Takashi Iwai // Copyright (C) 2007 Koji Otani // Copyright (C) 2011, 2012, 2018-2020 Albert Astals Cid // Copyright (C) 2012 Suzuki Toshiya // Copyright (C) 2016 William Bader // Copyright (C) 2018 Adam Reichold // Copyright (C) 2022 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 // //======================================================================== #ifndef FOFITRUETYPE_H #define FOFITRUETYPE_H #include #include #include #include #include "FoFiBase.h" #include "poppler_private_export.h" class GooString; struct TrueTypeTable; struct TrueTypeCmap; //------------------------------------------------------------------------ // FoFiTrueType //------------------------------------------------------------------------ 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); // Create a FoFiTrueType object from a file on disk. static std::unique_ptr load(const char *fileName, int faceIndexA = 0); ~FoFiTrueType() override; // Returns true if this an OpenType font containing CFF data, false // if it's a TrueType font (or OpenType font with TrueType data). bool isOpenTypeCFF() const { return openTypeCFF; } // Return the number of cmaps defined by this font. int getNumCmaps() const; // Return the platform ID of the th cmap. int getCmapPlatform(int i) const; // Return the encoding ID of the th cmap. int getCmapEncoding(int i) const; // Return the index of the cmap for , . Returns // -1 if there is no corresponding cmap. int findCmap(int platform, int encoding) const; // Return the GID corresponding to according to the th cmap. int mapCodeToGID(int i, unsigned int c) const; // map gid to vertical glyph gid if exist. // if not exist return original gid unsigned int mapToVertGID(unsigned int orgGID); // Returns the GID corresponding to according to the post // table. Returns 0 if there is no mapping for or if the // 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 // useful for OpenType CFF fonts.) int *getCIDToGIDMap(int *nCIDs) const; // Returns the least restrictive embedding licensing right (as // defined by the TrueType spec): // * 4: OS/2 table is missing or invalid // * 3: installable embedding // * 2: editable embedding // * 1: preview & print embedding // * 0: restricted license embedding int getEmbeddingRights() const; // Return the font matrix as an array of six numbers. (Only useful // for OpenType CFF fonts.) void getFontMatrix(double *mat) const; // Convert to a Type 42 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 specifies the mapping from char codes to names. // 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; // Convert to a Type 1 font, suitable for embedding in a PostScript // file. This is only useful with 8-bit fonts. If is // not NULL, it will be used in place of the encoding in the Type 1C // font. If is true the eexec section will be hex-encoded, // otherwise it will be left as binary data. If is // non-NULL, it will be used as the PostScript font name. (Only // useful for OpenType CFF fonts.) void convertToType1(const char *psName, const char **newEncoding, bool ascii, FoFiOutputFunc outputFunc, void *outputStream) const; // Convert to a Type 2 CIDFont, 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 convertToCIDType2(const char *psName, const int *cidMap, int nCIDs, 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; // 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; // 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; // Returns a pointer to the CFF font embedded in this OpenType font. // If successful, sets * and *, and returns true. // Otherwise returns false. (Only useful for OpenType CFF fonts). bool getCFFBlock(char **start, int *length) const; // setup vert/vrt2 GSUB for default lang int setupGSUB(const char *scriptName); // setup vert/vrt2 GSUB for specified lang int setupGSUB(const char *scriptName, const char *languageName); 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 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; void parse(); void readPostTable(); int seekTable(const char *tag) const; unsigned int charToTag(const char *tagName); unsigned int doMapToVertGID(unsigned int orgGID); unsigned int scanLookupList(unsigned int listIndex, unsigned int orgGID); 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; int nGlyphs; int locaFmt; int bbox[4]; std::unordered_map nameToGID; bool openTypeCFF; bool parsedOk; int faceIndex; unsigned int gsubFeatureTable; unsigned int gsubLookupList; }; #endif poppler-24.02.0/fofi/FoFiType1.cc000066400000000000000000000253361455701731300163570ustar00rootroot00000000000000//======================================================================== // // FoFiType1.cc // // Copyright 1999-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) 2005, 2008, 2010, 2018, 2021-2023 Albert Astals Cid // Copyright (C) 2005 Kristian Høgsberg // Copyright (C) 2010 Jakub Wilk // Copyright (C) 2014 Carlos Garcia Campos // Copyright (C) 2017 Adrian Johnson // Copyright (C) 2017 Jean Ghali // Copyright (C) 2022 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 // //======================================================================== #include #include #include #include #include #include #include "goo/glibc.h" #include "goo/gmem.h" #include "goo/GooLikely.h" #include "FoFiEncodings.h" #include "FoFiType1.h" #include "poppler/Error.h" //------------------------------------------------------------------------ // FoFiType1 //------------------------------------------------------------------------ FoFiType1 *FoFiType1::make(const unsigned char *fileA, int lenA) { return new FoFiType1(fileA, lenA, false); } FoFiType1::FoFiType1(const unsigned char *fileA, int lenA, bool freeFileDataA) : FoFiBase(fileA, lenA, freeFileDataA) { encoding = nullptr; parsed = false; undoPFB(); } FoFiType1::~FoFiType1() { if (encoding && encoding != fofiType1StandardEncoding) { for (int i = 0; i < 256; ++i) { gfree(encoding[i]); } gfree(encoding); } } std::string FoFiType1::getName() { if (!parsed) { parse(); } return name; } char **FoFiType1::getEncoding() { if (!parsed) { parse(); } return encoding; } void FoFiType1::writeEncoded(const char **newEncoding, FoFiOutputFunc outputFunc, void *outputStream) const { char buf[512]; char *line, *line2, *p; int i; // copy everything up to the encoding for (line = (char *)file; line && strncmp(line, "/Encoding", 9); line = getNextLine(line)) { ; } if (!line) { // no encoding - just copy the whole font file (*outputFunc)(outputStream, (char *)file, len); return; } (*outputFunc)(outputStream, (char *)file, line - (char *)file); // write the new encoding (*outputFunc)(outputStream, "/Encoding 256 array\n", 20); (*outputFunc)(outputStream, "0 1 255 {1 index exch /.notdef put} for\n", 40); for (i = 0; i < 256; ++i) { if (newEncoding[i]) { sprintf(buf, "dup %d /%s put\n", i, newEncoding[i]); (*outputFunc)(outputStream, buf, strlen(buf)); } } (*outputFunc)(outputStream, "readonly def\n", 13); // find the end of the encoding data //~ this ought to parse PostScript tokens if (!strncmp(line, "/Encoding StandardEncoding def", 30)) { line = getNextLine(line); } else { // skip "/Encoding" + one whitespace char, // then look for 'def' preceded by PostScript whitespace p = line + 10; line = nullptr; for (; p < (char *)file + len; ++p) { if ((*p == ' ' || *p == '\t' || *p == '\x0a' || *p == '\x0d' || *p == '\x0c' || *p == '\0') && p + 4 <= (char *)file + len && !strncmp(p + 1, "def", 3)) { line = p + 4; break; } } } // 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) { ; } if (i < 20 && line2) { (*outputFunc)(outputStream, line, line2 - line); if (!strncmp(line2, "/Encoding StandardEncoding def", 30)) { line = getNextLine(line2); } else { // skip "/Encoding" + one whitespace char, // then look for 'def' preceded by PostScript whitespace p = line2 + 10; line = nullptr; for (; p < (char *)file + len; ++p) { if ((*p == ' ' || *p == '\t' || *p == '\x0a' || *p == '\x0d' || *p == '\x0c' || *p == '\0') && p + 4 <= (char *)file + len && !strncmp(p + 1, "def", 3)) { line = p + 4; break; } } } } // copy everything after the encoding if (line) { (*outputFunc)(outputStream, line, ((char *)file + len) - line); } } } char *FoFiType1::getNextLine(char *line) const { while (line < (char *)file + len && *line != '\x0a' && *line != '\x0d') { ++line; } if (line < (char *)file + len && *line == '\x0d') { ++line; } if (line < (char *)file + len && *line == '\x0a') { ++line; } if (line >= (char *)file + len) { return nullptr; } 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) { std::string_view token = stringView.substr(currentPos, length - currentPos); currentPos = length; return token; } 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() { 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; } // Skip the / name = fontNameToken->substr(1); } else if (!encoding && token == "/Encoding") { const std::optional token2 = tokenizer.getToken(); if (!token2) { break; } 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; } while (true) { const std::optional encodingToken = tokenizer.getToken(); if (!encodingToken) { break; } if (encodingToken == "dup") { std::optional codeToken = tokenizer.getToken(); if (!codeToken) { break; } 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 (!nameToken) { break; } 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 && nameToken->length() > 1) { gfree(encoding[code]); encoding[code] = copyString(nameToken->data() + 1, nameToken->length() - 1); } } else if (encodingToken == "def") { break; } } } } } parsed = true; } // Undo the PFB encoding, i.e., remove the PFB headers. void FoFiType1::undoPFB() { bool ok; unsigned char *file2; int pos1, pos2, type; unsigned int segLen; ok = true; if (getU8(0, &ok) != 0x80 || !ok) { return; } file2 = (unsigned char *)gmalloc(len); pos1 = pos2 = 0; while (getU8(pos1, &ok) == 0x80 && ok) { type = getU8(pos1 + 1, &ok); if (type < 1 || type > 2 || !ok) { break; } segLen = getU32LE(pos1 + 2, &ok); pos1 += 6; if (!ok || !checkRegion(pos1, segLen)) { break; } memcpy(file2 + pos2, file + pos1, segLen); pos1 += segLen; pos2 += segLen; } if (freeFileData) { gfree((char *)file); } file = file2; freeFileData = true; len = pos2; } poppler-24.02.0/fofi/FoFiType1.h000066400000000000000000000035041455701731300162120ustar00rootroot00000000000000//======================================================================== // // FoFiType1.h // // Copyright 1999-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) 2018, 2022, 2023 Albert Astals Cid // Copyright (C) 2022 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 // //======================================================================== #ifndef FOFITYPE1_H #define FOFITYPE1_H #include "FoFiBase.h" #include //------------------------------------------------------------------------ // FoFiType1 //------------------------------------------------------------------------ class FoFiType1 : public FoFiBase { public: // Create a FoFiType1 object from a memory buffer. static FoFiType1 *make(const unsigned char *fileA, int lenA); ~FoFiType1() override; // Return the font name. std::string getName(); // Return the encoding, as an array of 256 names (any of which may // be NULL). char **getEncoding(); // Write a version of the Type 1 font file with a new encoding. void writeEncoded(const char **newEncoding, FoFiOutputFunc outputFunc, void *outputStream) const; private: FoFiType1(const unsigned char *fileA, int lenA, bool freeFileDataA); char *getNextLine(char *line) const; void parse(); void undoPFB(); std::string name; char **encoding; bool parsed; }; #endif poppler-24.02.0/fofi/FoFiType1C.cc000066400000000000000000003046621455701731300164640ustar00rootroot00000000000000//======================================================================== // // FoFiType1C.cc // // Copyright 1999-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) 2009, 2010, 2017-2022 Albert Astals Cid // Copyright (C) 2012 Thomas Freitag // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019 Tomoyuki Kubota // Copyright (C) 2019 Volker Krause // Copyright (C) 2022 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 // //======================================================================== #include #include #include #include #include "goo/gmem.h" #include "goo/gstrtod.h" #include "goo/GooLikely.h" #include "goo/GooString.h" #include "poppler/Error.h" #include "FoFiEncodings.h" #include "FoFiType1C.h" //------------------------------------------------------------------------ static const char hexChars[17] = "0123456789ABCDEF"; //------------------------------------------------------------------------ // FoFiType1C //------------------------------------------------------------------------ FoFiType1C *FoFiType1C::make(const unsigned char *fileA, int lenA) { FoFiType1C *ff = new FoFiType1C(fileA, lenA, false); if (!ff->parse()) { delete ff; return nullptr; } return ff; } FoFiType1C *FoFiType1C::load(const char *fileName) { FoFiType1C *ff; char *fileA; int lenA; if (!(fileA = FoFiBase::readFile(fileName, &lenA))) { return nullptr; } ff = new FoFiType1C((unsigned char *)fileA, lenA, true); if (!ff->parse()) { delete ff; return nullptr; } return ff; } FoFiType1C::FoFiType1C(const unsigned char *fileA, int lenA, bool freeFileDataA) : FoFiBase(fileA, lenA, freeFileDataA) { name = nullptr; encoding = nullptr; privateDicts = nullptr; fdSelect = nullptr; charset = nullptr; charsetLength = 0; } FoFiType1C::~FoFiType1C() { int i; if (name) { delete name; } if (encoding && encoding != fofiType1StandardEncoding && encoding != fofiType1ExpertEncoding) { for (i = 0; i < 256; ++i) { gfree(encoding[i]); } gfree(encoding); } if (privateDicts) { gfree(privateDicts); } if (fdSelect) { gfree(fdSelect); } if (charset && charset != fofiType1CISOAdobeCharset && charset != fofiType1CExpertCharset && charset != fofiType1CExpertSubsetCharset) { gfree(const_cast(charset)); } } const char *FoFiType1C::getName() const { return name ? name->c_str() : nullptr; } char **FoFiType1C::getEncoding() const { return encoding; } GooString *FoFiType1C::getGlyphName(int gid) const { char buf[256]; bool ok; ok = true; if (gid < 0 || gid >= charsetLength) { return nullptr; } getString(charset[gid], buf, &ok); if (!ok) { return nullptr; } return new GooString(buf); } int *FoFiType1C::getCIDToGIDMap(int *nCIDs) 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; } // in a CID font, the charset data is the GID-to-CID mapping, so all // we have to do is reverse it n = 0; for (i = 0; i < nGlyphs && i < charsetLength; ++i) { if (charset[i] > n) { n = charset[i]; } } ++n; map = (int *)gmallocn(n, sizeof(int)); memset(map, 0, n * sizeof(int)); for (i = 0; i < nGlyphs; ++i) { map[charset[i]] = i; } *nCIDs = n; return map; } void FoFiType1C::getFontMatrix(double *mat) const { int i; if (topDict.firstOp == 0x0c1e && privateDicts[0].hasFontMatrix) { if (topDict.hasFontMatrix) { mat[0] = topDict.fontMatrix[0] * privateDicts[0].fontMatrix[0] + topDict.fontMatrix[1] * privateDicts[0].fontMatrix[2]; mat[1] = topDict.fontMatrix[0] * privateDicts[0].fontMatrix[1] + topDict.fontMatrix[1] * privateDicts[0].fontMatrix[3]; mat[2] = topDict.fontMatrix[2] * privateDicts[0].fontMatrix[0] + topDict.fontMatrix[3] * privateDicts[0].fontMatrix[2]; mat[3] = topDict.fontMatrix[2] * privateDicts[0].fontMatrix[1] + topDict.fontMatrix[3] * privateDicts[0].fontMatrix[3]; mat[4] = topDict.fontMatrix[4] * privateDicts[0].fontMatrix[0] + topDict.fontMatrix[5] * privateDicts[0].fontMatrix[2]; mat[5] = topDict.fontMatrix[4] * privateDicts[0].fontMatrix[1] + topDict.fontMatrix[5] * privateDicts[0].fontMatrix[3]; } else { for (i = 0; i < 6; ++i) { mat[i] = privateDicts[0].fontMatrix[i]; } } } else { for (i = 0; i < 6; ++i) { mat[i] = topDict.fontMatrix[i]; } } } void FoFiType1C::convertToType1(const char *psName, const char **newEncoding, bool ascii, FoFiOutputFunc outputFunc, void *outputStream) { int psNameLen; Type1CEexecBuf eb; Type1CIndex subrIdx; Type1CIndexVal val; char buf2[256]; bool ok; int i; if (psName) { psNameLen = strlen(psName); } else { psName = name->c_str(); psNameLen = name->getLength(); } // write header and font dictionary, up to encoding ok = true; (*outputFunc)(outputStream, "%!FontType1-1.0: ", 17); (*outputFunc)(outputStream, psName, psNameLen); if (topDict.versionSID != 0) { getString(topDict.versionSID, buf2, &ok); (*outputFunc)(outputStream, buf2, strlen(buf2)); } (*outputFunc)(outputStream, "\n", 1); // the dictionary needs room for 12 entries: the following 9, plus // Private and CharStrings (in the eexec section) and FID (which is // added by definefont) (*outputFunc)(outputStream, "12 dict begin\n", 14); (*outputFunc)(outputStream, "/FontInfo 10 dict dup begin\n", 28); if (topDict.versionSID != 0) { (*outputFunc)(outputStream, "/version ", 9); writePSString(buf2, outputFunc, outputStream); (*outputFunc)(outputStream, " readonly def\n", 14); } if (topDict.noticeSID != 0) { getString(topDict.noticeSID, buf2, &ok); (*outputFunc)(outputStream, "/Notice ", 8); writePSString(buf2, outputFunc, outputStream); (*outputFunc)(outputStream, " readonly def\n", 14); } if (topDict.copyrightSID != 0) { getString(topDict.copyrightSID, buf2, &ok); (*outputFunc)(outputStream, "/Copyright ", 11); writePSString(buf2, outputFunc, outputStream); (*outputFunc)(outputStream, " readonly def\n", 14); } if (topDict.fullNameSID != 0) { getString(topDict.fullNameSID, buf2, &ok); (*outputFunc)(outputStream, "/FullName ", 10); writePSString(buf2, outputFunc, outputStream); (*outputFunc)(outputStream, " readonly def\n", 14); } if (topDict.familyNameSID != 0) { getString(topDict.familyNameSID, buf2, &ok); (*outputFunc)(outputStream, "/FamilyName ", 12); writePSString(buf2, outputFunc, outputStream); (*outputFunc)(outputStream, " readonly def\n", 14); } if (topDict.weightSID != 0) { getString(topDict.weightSID, buf2, &ok); (*outputFunc)(outputStream, "/Weight ", 8); writePSString(buf2, outputFunc, outputStream); (*outputFunc)(outputStream, " readonly def\n", 14); } if (topDict.isFixedPitch) { (*outputFunc)(outputStream, "/isFixedPitch true def\n", 23); } 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()); buf = GooString::format("/UnderlinePosition {0:.4g} def\n", topDict.underlinePosition); (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); buf = GooString::format("/UnderlineThickness {0:.4g} def\n", topDict.underlineThickness); (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); (*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, "/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()); 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()); buf = GooString::format("/StrokeWidth {0:.4g} def\n", topDict.strokeWidth); (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); if (topDict.uniqueID != 0) { buf = GooString::format("/UniqueID {0:d} def\n", topDict.uniqueID); (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); } // write the encoding (*outputFunc)(outputStream, "/Encoding ", 10); if (!newEncoding && encoding == fofiType1StandardEncoding) { (*outputFunc)(outputStream, "StandardEncoding def\n", 21); } else { (*outputFunc)(outputStream, "256 array\n", 10); (*outputFunc)(outputStream, "0 1 255 {1 index exch /.notdef put} for\n", 40); const char **enc = newEncoding ? newEncoding : (const char **)encoding; 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, "readonly def\n", 13); } (*outputFunc)(outputStream, "currentdict end\n", 16); // start the binary section (*outputFunc)(outputStream, "currentfile eexec\n", 18); eb.outputFunc = outputFunc; eb.outputStream = outputStream; eb.ascii = ascii; eb.r1 = 55665; eb.line = 0; // write the private dictionary eexecWrite(&eb, "\x83\xca\x73\xd5"); eexecWrite(&eb, "dup /Private 32 dict dup begin\n"); eexecWrite(&eb, "/RD {string currentfile exch readstring pop}" " executeonly def\n"); eexecWrite(&eb, "/ND {noaccess def} executeonly def\n"); eexecWrite(&eb, "/NP {noaccess put} executeonly def\n"); eexecWrite(&eb, "/MinFeature {16 16} def\n"); eexecWrite(&eb, "/password 5839 def\n"); if (privateDicts[0].nBlueValues) { 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, "] def\n"); } if (privateDicts[0].nOtherBlues) { 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, "] def\n"); } if (privateDicts[0].nFamilyBlues) { 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, "] def\n"); } if (privateDicts[0].nFamilyOtherBlues) { 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, "] def\n"); } if (privateDicts[0].blueScale != 0.039625) { buf = GooString::format("/BlueScale {0:.4g} def\n", privateDicts[0].blueScale); 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()); } if (privateDicts[0].blueFuzz != 1) { buf = GooString::format("/BlueFuzz {0:d} def\n", privateDicts[0].blueFuzz); 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()); } if (privateDicts[0].hasStdVW) { buf = GooString::format("/StdVW [{0:.4g}] def\n", privateDicts[0].stdVW); 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, "] def\n"); } if (privateDicts[0].nStemSnapV) { 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, "] def\n"); } if (privateDicts[0].hasForceBold) { buf = GooString::format("/ForceBold {0:s} def\n", privateDicts[0].forceBold ? "true" : "false"); 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()); } if (privateDicts[0].languageGroup != 0) { buf = GooString::format("/LanguageGroup {0:d} def\n", privateDicts[0].languageGroup); 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()); } // set up subroutines ok = true; getIndex(privateDicts[0].subrsOffset, &subrIdx, &ok); if (!ok) { subrIdx.pos = -1; } // write the CharStrings buf = GooString::format("2 index /CharStrings {0:d} dict dup begin\n", nGlyphs); eexecWrite(&eb, buf->c_str()); for (i = 0; i < nGlyphs; ++i) { ok = true; getIndexVal(&charStringsIdx, i, &val, &ok); if (ok && i < charsetLength) { getString(charset[i], buf2, &ok); if (ok) { eexecCvtGlyph(&eb, buf2, val.pos, val.len, &subrIdx, &privateDicts[0]); } } } eexecWrite(&eb, "end\n"); eexecWrite(&eb, "end\n"); eexecWrite(&eb, "readonly put\n"); eexecWrite(&eb, "noaccess put\n"); eexecWrite(&eb, "dup /FontName get exch definefont pop\n"); eexecWrite(&eb, "mark currentfile closefile\n"); // trailer if (ascii && eb.line > 0) { (*outputFunc)(outputStream, "\n", 1); } for (i = 0; i < 8; ++i) { (*outputFunc)(outputStream, "0000000000000000000000000000000000000000000000000000000000000000\n", 65); } (*outputFunc)(outputStream, "cleartomark\n", 12); } void FoFiType1C::convertToCIDType0(const char *psName, const int *codeMap, int nCodes, FoFiOutputFunc outputFunc, void *outputStream) { int *cidMap; GooString *charStrings; int *charStringOffsets; Type1CIndex subrIdx; Type1CIndexVal val; int nCIDs, gdBytes; char buf2[256]; bool ok; int gid, offset, n, i, 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]; } else { cidMap[i] = -1; } } } else if (topDict.firstOp == 0x0c1e) { nCIDs = 0; for (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[charset[i]] = i; } } else { nCIDs = nGlyphs; cidMap = (int *)gmallocn(nCIDs, sizeof(int)); for (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[i] = charStrings->getLength(); if ((gid = cidMap[i]) >= 0) { ok = true; getIndexVal(&charStringsIdx, gid, &val, &ok); if (ok) { getIndex(privateDicts[fdSelect ? fdSelect[gid] : 0].subrsOffset, &subrIdx, &ok); if (!ok) { subrIdx.pos = -1; } std::set offsetBeingParsed; cvtGlyph(val.pos, val.len, charStrings, &subrIdx, &privateDicts[fdSelect ? fdSelect[gid] : 0], true, offsetBeingParsed); } } } charStringOffsets[nCIDs] = 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(); if (i < 0x100) { gdBytes = 1; } else if (i < 0x10000) { gdBytes = 2; } else if (i < 0x1000000) { gdBytes = 3; } else { gdBytes = 4; } // begin the font dictionary (*outputFunc)(outputStream, "/CIDInit /ProcSet findresource begin\n", 37); (*outputFunc)(outputStream, "20 dict begin\n", 14); (*outputFunc)(outputStream, "/CIDFontName /", 14); (*outputFunc)(outputStream, psName, strlen(psName)); (*outputFunc)(outputStream, " def\n", 5); (*outputFunc)(outputStream, "/CIDFontType 0 def\n", 19); (*outputFunc)(outputStream, "/CIDSystemInfo 3 dict dup begin\n", 32); if (topDict.registrySID > 0 && topDict.orderingSID > 0) { ok = true; getString(topDict.registrySID, buf2, &ok); if (ok) { (*outputFunc)(outputStream, " /Registry (", 13); (*outputFunc)(outputStream, buf2, strlen(buf2)); (*outputFunc)(outputStream, ") def\n", 6); } ok = true; getString(topDict.orderingSID, buf2, &ok); if (ok) { (*outputFunc)(outputStream, " /Ordering (", 13); (*outputFunc)(outputStream, buf2, strlen(buf2)); (*outputFunc)(outputStream, ") def\n", 6); } } else { (*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()); (*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()); } 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, "/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()); (*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, "/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()); buf = GooString::format("/StrokeWidth {0:.4g} def\n", topDict.strokeWidth); (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); } // FDArray entry buf = GooString::format("/FDArray {0:d} array\n", nFDs); (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); 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, "/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()); } 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, "/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, "] def\n", 6); } if (privateDicts[i].nOtherBlues) { (*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, "] def\n", 6); } if (privateDicts[i].nFamilyBlues) { (*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, "] def\n", 6); } if (privateDicts[i].nFamilyOtherBlues) { (*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, "] 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()); } if (privateDicts[i].blueShift != 7) { buf = GooString::format("/BlueShift {0:d} def\n", privateDicts[i].blueShift); (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); } if (privateDicts[i].blueFuzz != 1) { buf = GooString::format("/BlueFuzz {0:d} def\n", privateDicts[i].blueFuzz); (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); } if (privateDicts[i].hasStdHW) { buf = GooString::format("/StdHW [{0:.4g}] def\n", privateDicts[i].stdHW); (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); } if (privateDicts[i].hasStdVW) { buf = GooString::format("/StdVW [{0:.4g}] def\n", privateDicts[i].stdVW); (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); } 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, "] def\n", 6); } if (privateDicts[i].nStemSnapV) { (*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, "] 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()); } if (privateDicts[i].forceBoldThreshold != 0) { buf = GooString::format("/ForceBoldThreshold {0:.4g} def\n", privateDicts[i].forceBoldThreshold); (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); } if (privateDicts[i].languageGroup != 0) { buf = GooString::format("/LanguageGroup {0:d} def\n", privateDicts[i].languageGroup); (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); } 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, "currentdict end def\n", 20); (*outputFunc)(outputStream, "currentdict end put\n", 20); } (*outputFunc)(outputStream, "def\n", 4); // start the binary section offset = (nCIDs + 1) * (1 + gdBytes); buf = GooString::format("(Hex) {0:d} StartData\n", offset + charStrings->getLength()); (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); // 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) { buf2[0] = (char)fdSelect[cidMap[i + j]]; } else { buf2[0] = (char)0; } n = offset + charStringOffsets[i + j]; for (k = gdBytes; k >= 1; --k) { buf2[k] = (char)(n & 0xff); n >>= 8; } for (k = 0; k <= gdBytes; ++k) { buf = GooString::format("{0:02x}", buf2[k] & 0xff); (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); } } (*outputFunc)(outputStream, "\n", 1); } // write the charstring data n = charStrings->getLength(); 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()); } if (i + 32 >= n) { (*outputFunc)(outputStream, ">", 1); } (*outputFunc)(outputStream, "\n", 1); } gfree(charStringOffsets); delete charStrings; gfree(cidMap); } void FoFiType1C::convertToType0(const char *psName, const int *codeMap, int nCodes, FoFiOutputFunc outputFunc, void *outputStream) { int *cidMap; Type1CIndex subrIdx; Type1CIndexVal val; int nCIDs; Type1CEexecBuf eb; bool ok; int fd, i, 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]; } else { cidMap[i] = -1; } } } else if (topDict.firstOp == 0x0c1e) { nCIDs = 0; for (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[charset[i]] = i; } } else { nCIDs = nGlyphs; cidMap = (int *)gmallocn(nCIDs, sizeof(int)); for (i = 0; i < nCIDs; ++i) { cidMap[i] = i; } } if (privateDicts) { // write the descendant Type 1 fonts for (i = 0; i < nCIDs; i += 256) { //~ this assumes that all CIDs in this block have the same FD -- //~ to handle multiple FDs correctly, need to somehow divide the //~ font up by FD; as a kludge we ignore CID 0, which is .notdef 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) { if (cidMap[i + j] >= 0) { fd = fdSelect[cidMap[i + j]]; break; } } } if (fd >= nFDs) { continue; } // font dictionary (unencrypted section) (*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()); (*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()); } 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()); buf = GooString::format("/PaintType {0:d} def\n", topDict.paintType); (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); if (topDict.paintType != 0) { buf = GooString::format("/StrokeWidth {0:.4g} def\n", topDict.strokeWidth); (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); } (*outputFunc)(outputStream, "/Encoding 256 array\n", 20); for (j = 0; j < 256 && i + j < nCIDs; ++j) { buf = GooString::format("dup {0:d} /c{1:02x} put\n", j, j); (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); } 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, "readonly def\n", 13); (*outputFunc)(outputStream, "currentdict end\n", 16); // start the binary section (*outputFunc)(outputStream, "currentfile eexec\n", 18); eb.outputFunc = outputFunc; eb.outputStream = outputStream; eb.ascii = true; eb.r1 = 55665; eb.line = 0; // start the private dictionary eexecWrite(&eb, "\x83\xca\x73\xd5"); eexecWrite(&eb, "dup /Private 32 dict dup begin\n"); eexecWrite(&eb, "/RD {string currentfile exch readstring pop}" " executeonly def\n"); eexecWrite(&eb, "/ND {noaccess def} executeonly def\n"); eexecWrite(&eb, "/NP {noaccess put} executeonly def\n"); eexecWrite(&eb, "/MinFeature {16 16} def\n"); eexecWrite(&eb, "/password 5839 def\n"); if (privateDicts[fd].nBlueValues) { 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, "] def\n"); } if (privateDicts[fd].nOtherBlues) { 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, "] def\n"); } if (privateDicts[fd].nFamilyBlues) { 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, "] def\n"); } if (privateDicts[fd].nFamilyOtherBlues) { 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, "] def\n"); } if (privateDicts[fd].blueScale != 0.039625) { buf = GooString::format("/BlueScale {0:.4g} def\n", privateDicts[fd].blueScale); 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()); } if (privateDicts[fd].blueFuzz != 1) { buf = GooString::format("/BlueFuzz {0:d} def\n", privateDicts[fd].blueFuzz); 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()); } if (privateDicts[fd].hasStdVW) { buf = GooString::format("/StdVW [{0:.4g}] def\n", privateDicts[fd].stdVW); 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, "] def\n"); } if (privateDicts[fd].nStemSnapV) { 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, "] def\n"); } if (privateDicts[fd].hasForceBold) { buf = GooString::format("/ForceBold {0:s} def\n", privateDicts[fd].forceBold ? "true" : "false"); 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()); } if (privateDicts[fd].languageGroup != 0) { buf = GooString::format("/LanguageGroup {0:d} def\n", privateDicts[fd].languageGroup); 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()); } // set up the subroutines ok = true; getIndex(privateDicts[fd].subrsOffset, &subrIdx, &ok); if (!ok) { subrIdx.pos = -1; } // start the CharStrings eexecWrite(&eb, "2 index /CharStrings 256 dict dup begin\n"); // write the .notdef CharString ok = true; getIndexVal(&charStringsIdx, 0, &val, &ok); if (ok) { eexecCvtGlyph(&eb, ".notdef", val.pos, val.len, &subrIdx, &privateDicts[fd]); } // write the CharStrings for (j = 0; j < 256 && i + j < nCIDs; ++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]); } } } eexecWrite(&eb, "end\n"); eexecWrite(&eb, "end\n"); eexecWrite(&eb, "readonly put\n"); eexecWrite(&eb, "noaccess put\n"); eexecWrite(&eb, "dup /FontName get exch definefont pop\n"); eexecWrite(&eb, "mark currentfile closefile\n"); // trailer if (eb.line > 0) { (*outputFunc)(outputStream, "\n", 1); } for (j = 0; j < 8; ++j) { (*outputFunc)(outputStream, "0000000000000000000000000000000000000000000000000000000000000000\n", 65); } (*outputFunc)(outputStream, "cleartomark\n", 12); } } else { error(errSyntaxError, -1, "FoFiType1C::convertToType0 without privateDicts"); } // write the Type 0 parent font (*outputFunc)(outputStream, "16 dict begin\n", 14); (*outputFunc)(outputStream, "/FontName /", 11); (*outputFunc)(outputStream, psName, strlen(psName)); (*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()); } 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()); } (*outputFunc)(outputStream, "] def\n", 6); (*outputFunc)(outputStream, "/FDepVector [\n", 14); for (i = 0; i < nCIDs; 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()); } (*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) { GooString *charBuf; // generate the charstring charBuf = new GooString(); 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()); eexecWriteCharstring(eb, (unsigned char *)charBuf->c_str(), charBuf->getLength()); eexecWrite(eb, " ND\n"); delete charBuf; } void FoFiType1C::cvtGlyph(int offset, int nBytes, GooString *charBuf, const Type1CIndex *subrIdx, const Type1CPrivateDict *pDict, bool top, std::set &offsetBeingParsed) { Type1CIndexVal val; bool ok, dFP; double d, dx, dy; unsigned short r2; unsigned char byte; int pos, subrBias, start, i, k; if (offsetBeingParsed.find(offset) != offsetBeingParsed.end()) { return; } auto offsetEmplaceResult = offsetBeingParsed.emplace(offset); start = charBuf->getLength(); if (top) { charBuf->append('\x49'); // 73; charBuf->append('\x3A'); // 58; charBuf->append('\x93'); // 147; charBuf->append('\x86'); // 134; nOps = 0; nHints = 0; firstOp = true; openPath = false; } pos = offset; while (pos < offset + nBytes) { ok = true; pos = getOp(pos, true, &ok); if (!ok) { break; } if (!ops[nOps - 1].isNum) { --nOps; // drop the operator switch (ops[nOps].op) { case 0x0001: // hstem if (firstOp) { cvtGlyphWidth(nOps & 1, charBuf, pDict); firstOp = false; } if (nOps & 1) { //~ error(-1, "Wrong number of args (%d) to Type 2 hstem", nOps); } d = 0; dFP = false; for (k = 0; k < nOps; k += 2) { // convert Type 2 edge hints (-20 or -21) to Type 1 ghost hints if (ops[k + 1].num < 0) { d += ops[k].num + ops[k + 1].num; dFP |= ops[k].isFP | ops[k + 1].isFP; cvtNum(d, dFP, charBuf); cvtNum(-ops[k + 1].num, ops[k + 1].isFP, charBuf); } else { d += ops[k].num; dFP |= ops[k].isFP; cvtNum(d, dFP, charBuf); cvtNum(ops[k + 1].num, ops[k + 1].isFP, charBuf); d += ops[k + 1].num; dFP |= ops[k + 1].isFP; } charBuf->append((char)1); } nHints += nOps / 2; nOps = 0; break; case 0x0003: // vstem if (firstOp) { cvtGlyphWidth(nOps & 1, charBuf, pDict); firstOp = false; } if (nOps & 1) { //~ error(-1, "Wrong number of args (%d) to Type 2 vstem", nOps); } d = 0; dFP = false; for (k = 0; k < nOps; k += 2) { // convert Type 2 edge hints (-20 or -21) to Type 1 ghost hints if (ops[k + 1].num < 0) { d += ops[k].num + ops[k + 1].num; dFP |= ops[k].isFP | ops[k + 1].isFP; cvtNum(d, dFP, charBuf); cvtNum(-ops[k + 1].num, ops[k + 1].isFP, charBuf); } else { d += ops[k].num; dFP |= ops[k].isFP; cvtNum(d, dFP, charBuf); cvtNum(ops[k + 1].num, ops[k + 1].isFP, charBuf); d += ops[k + 1].num; dFP |= ops[k + 1].isFP; } charBuf->append((char)3); } nHints += nOps / 2; nOps = 0; break; case 0x0004: // vmoveto if (firstOp) { cvtGlyphWidth(nOps == 2, charBuf, pDict); firstOp = false; } if (openPath) { charBuf->append((char)9); openPath = false; } if (nOps != 1) { //~ error(-1, "Wrong number of args (%d) to Type 2 vmoveto", nOps); } cvtNum(ops[0].num, ops[0].isFP, charBuf); charBuf->append((char)4); nOps = 0; break; case 0x0005: // rlineto if (nOps < 2 || nOps % 2 != 0) { //~ error(-1, "Wrong number of args (%d) to Type 2 rlineto", nOps); } for (k = 0; k < nOps; k += 2) { cvtNum(ops[k].num, ops[k].isFP, charBuf); cvtNum(ops[k + 1].num, ops[k + 1].isFP, charBuf); charBuf->append((char)5); } nOps = 0; openPath = true; break; case 0x0006: // hlineto if (nOps < 1) { //~ error(-1, "Wrong number of args (%d) to Type 2 hlineto", nOps); } for (k = 0; k < nOps; ++k) { cvtNum(ops[k].num, ops[k].isFP, charBuf); charBuf->append((char)((k & 1) ? 7 : 6)); } nOps = 0; openPath = true; break; case 0x0007: // vlineto if (nOps < 1) { //~ error(-1, "Wrong number of args (%d) to Type 2 vlineto", nOps); } for (k = 0; k < nOps; ++k) { cvtNum(ops[k].num, ops[k].isFP, charBuf); charBuf->append((char)((k & 1) ? 6 : 7)); } nOps = 0; openPath = true; break; case 0x0008: // rrcurveto if (nOps < 6 || nOps % 6 != 0) { //~ error(-1, "Wrong number of args (%d) to Type 2 rrcurveto", nOps); } for (k = 0; k < nOps; k += 6) { cvtNum(ops[k].num, ops[k].isFP, charBuf); cvtNum(ops[k + 1].num, ops[k + 1].isFP, charBuf); cvtNum(ops[k + 2].num, ops[k + 2].isFP, charBuf); cvtNum(ops[k + 3].num, ops[k + 3].isFP, charBuf); cvtNum(ops[k + 4].num, ops[k + 4].isFP, charBuf); cvtNum(ops[k + 5].num, ops[k + 5].isFP, charBuf); charBuf->append((char)8); } nOps = 0; openPath = true; break; case 0x000a: // callsubr if (nOps >= 1) { subrBias = (subrIdx->len < 1240) ? 107 : (subrIdx->len < 33900) ? 1131 : 32768; k = subrBias + (int)ops[nOps - 1].num; --nOps; ok = true; getIndexVal(subrIdx, k, &val, &ok); if (likely(ok && val.pos != offset)) { cvtGlyph(val.pos, val.len, charBuf, subrIdx, pDict, false, offsetBeingParsed); } } else { //~ error(-1, "Too few args to Type 2 callsubr"); } // don't clear the stack break; case 0x000b: // return // don't clear the stack break; case 0x000e: // endchar / seac if (firstOp) { cvtGlyphWidth(nOps == 1 || nOps == 5, charBuf, pDict); firstOp = false; } if (openPath) { charBuf->append((char)9); openPath = false; } if (nOps == 4) { cvtNum(0, false, charBuf); cvtNum(ops[0].num, ops[0].isFP, charBuf); cvtNum(ops[1].num, ops[1].isFP, charBuf); cvtNum(ops[2].num, ops[2].isFP, charBuf); cvtNum(ops[3].num, ops[3].isFP, charBuf); charBuf->append((char)12)->append((char)6); } else if (nOps == 0) { charBuf->append((char)14); } else { //~ error(-1, "Wrong number of args (%d) to Type 2 endchar", nOps); } nOps = 0; break; case 0x000f: // (obsolete) // this op is ignored, but we need the glyph width if (firstOp) { cvtGlyphWidth(nOps > 0, charBuf, pDict); firstOp = false; } nOps = 0; break; case 0x0010: // blend //~ error(-1, "Unimplemented Type 2 charstring op: %d", file[i]); nOps = 0; break; case 0x0012: // hstemhm // ignored if (firstOp) { cvtGlyphWidth(nOps & 1, charBuf, pDict); firstOp = false; } if (nOps & 1) { //~ error(-1, "Wrong number of args (%d) to Type 2 hstemhm", nOps); } nHints += nOps / 2; nOps = 0; break; case 0x0013: // hintmask // ignored if (firstOp) { cvtGlyphWidth(nOps & 1, charBuf, pDict); firstOp = false; } if (nOps > 0) { if (nOps & 1) { //~ error(-1, "Wrong number of args (%d) to Type 2 hintmask/vstemhm", //~ nOps); } nHints += nOps / 2; } pos += (nHints + 7) >> 3; nOps = 0; break; case 0x0014: // cntrmask // ignored if (firstOp) { cvtGlyphWidth(nOps & 1, charBuf, pDict); firstOp = false; } if (nOps > 0) { if (nOps & 1) { //~ error(-1, "Wrong number of args (%d) to Type 2 cntrmask/vstemhm", //~ nOps); } nHints += nOps / 2; } pos += (nHints + 7) >> 3; nOps = 0; break; case 0x0015: // rmoveto if (firstOp) { cvtGlyphWidth(nOps == 3, charBuf, pDict); firstOp = false; } if (openPath) { charBuf->append((char)9); openPath = false; } if (nOps != 2) { //~ error(-1, "Wrong number of args (%d) to Type 2 rmoveto", nOps); } cvtNum(ops[0].num, ops[0].isFP, charBuf); cvtNum(ops[1].num, ops[1].isFP, charBuf); charBuf->append((char)21); nOps = 0; break; case 0x0016: // hmoveto if (firstOp) { cvtGlyphWidth(nOps == 2, charBuf, pDict); firstOp = false; } if (openPath) { charBuf->append((char)9); openPath = false; } if (nOps != 1) { //~ error(-1, "Wrong number of args (%d) to Type 2 hmoveto", nOps); } cvtNum(ops[0].num, ops[0].isFP, charBuf); charBuf->append((char)22); nOps = 0; break; case 0x0017: // vstemhm // ignored if (firstOp) { cvtGlyphWidth(nOps & 1, charBuf, pDict); firstOp = false; } if (nOps & 1) { //~ error(-1, "Wrong number of args (%d) to Type 2 vstemhm", nOps); } nHints += nOps / 2; nOps = 0; break; case 0x0018: // rcurveline if (nOps < 8 || (nOps - 2) % 6 != 0) { //~ error(-1, "Wrong number of args (%d) to Type 2 rcurveline", nOps); } for (k = 0; k < nOps - 2; k += 6) { cvtNum(ops[k].num, ops[k].isFP, charBuf); cvtNum(ops[k + 1].num, ops[k + 1].isFP, charBuf); cvtNum(ops[k + 2].num, ops[k + 2].isFP, charBuf); cvtNum(ops[k + 3].num, ops[k + 3].isFP, charBuf); cvtNum(ops[k + 4].num, ops[k + 4].isFP, charBuf); cvtNum(ops[k + 5].num, ops[k + 5].isFP, charBuf); charBuf->append((char)8); } if (likely(k + 1 < nOps)) { cvtNum(ops[k].num, ops[k].isFP, charBuf); cvtNum(ops[k + 1].num, ops[k + 1].isFP, charBuf); charBuf->append((char)5); } nOps = 0; openPath = true; break; case 0x0019: // rlinecurve if (nOps < 8 || (nOps - 6) % 2 != 0) { //~ error(-1, "Wrong number of args (%d) to Type 2 rlinecurve", nOps); } for (k = 0; k < nOps - 6; k += 2) { cvtNum(ops[k].num, ops[k].isFP, charBuf); cvtNum(ops[k + 1].num, ops[k + 1].isFP, charBuf); charBuf->append((char)5); } cvtNum(ops[k].num, ops[k].isFP, charBuf); cvtNum(ops[k + 1].num, ops[k + 1].isFP, charBuf); cvtNum(ops[k + 2].num, ops[k + 2].isFP, charBuf); cvtNum(ops[k + 3].num, ops[k + 3].isFP, charBuf); cvtNum(ops[k + 4].num, ops[k + 4].isFP, charBuf); cvtNum(ops[k + 5].num, ops[k + 5].isFP, charBuf); charBuf->append((char)8); nOps = 0; openPath = true; break; case 0x001a: // vvcurveto if (nOps < 4 || !(nOps % 4 == 0 || (nOps - 1) % 4 == 0)) { //~ error(-1, "Wrong number of args (%d) to Type 2 vvcurveto", nOps); } if (nOps % 2 == 1) { cvtNum(ops[0].num, ops[0].isFP, charBuf); cvtNum(ops[1].num, ops[1].isFP, charBuf); cvtNum(ops[2].num, ops[2].isFP, charBuf); cvtNum(ops[3].num, ops[3].isFP, charBuf); cvtNum(0, false, charBuf); cvtNum(ops[4].num, ops[4].isFP, charBuf); charBuf->append((char)8); k = 5; } else { k = 0; } for (; k < nOps; k += 4) { cvtNum(0, false, charBuf); cvtNum(ops[k].num, ops[k].isFP, charBuf); cvtNum(ops[k + 1].num, ops[k + 1].isFP, charBuf); cvtNum(ops[k + 2].num, ops[k + 2].isFP, charBuf); cvtNum(0, false, charBuf); cvtNum(ops[k + 3].num, ops[k + 3].isFP, charBuf); charBuf->append((char)8); } nOps = 0; openPath = true; break; case 0x001b: // hhcurveto if (nOps < 4 || !(nOps % 4 == 0 || (nOps - 1) % 4 == 0)) { //~ error(-1, "Wrong number of args (%d) to Type 2 hhcurveto", nOps); } if (nOps % 2 == 1) { cvtNum(ops[1].num, ops[1].isFP, charBuf); cvtNum(ops[0].num, ops[0].isFP, charBuf); cvtNum(ops[2].num, ops[2].isFP, charBuf); cvtNum(ops[3].num, ops[3].isFP, charBuf); cvtNum(ops[4].num, ops[4].isFP, charBuf); cvtNum(0, false, charBuf); charBuf->append((char)8); k = 5; } else { k = 0; } for (; k < nOps; k += 4) { cvtNum(ops[k].num, ops[k].isFP, charBuf); cvtNum(0, false, charBuf); cvtNum(ops[k + 1].num, ops[k + 1].isFP, charBuf); cvtNum(ops[k + 2].num, ops[k + 2].isFP, charBuf); cvtNum(ops[k + 3].num, ops[k + 3].isFP, charBuf); cvtNum(0, false, charBuf); charBuf->append((char)8); } nOps = 0; openPath = true; break; case 0x001d: // callgsubr if (nOps >= 1) { k = gsubrBias + (int)ops[nOps - 1].num; --nOps; ok = true; getIndexVal(&gsubrIdx, k, &val, &ok); if (likely(ok && val.pos != offset)) { cvtGlyph(val.pos, val.len, charBuf, subrIdx, pDict, false, offsetBeingParsed); } } else { //~ error(-1, "Too few args to Type 2 callgsubr"); } // don't clear the stack break; case 0x001e: // vhcurveto if (nOps < 4 || !(nOps % 4 == 0 || (nOps - 1) % 4 == 0)) { //~ error(-1, "Wrong number of args (%d) to Type 2 vhcurveto", nOps); } for (k = 0; k < nOps && k != nOps - 5; k += 4) { if (k % 8 == 0) { cvtNum(ops[k].num, ops[k].isFP, charBuf); cvtNum(ops[k + 1].num, ops[k + 1].isFP, charBuf); cvtNum(ops[k + 2].num, ops[k + 2].isFP, charBuf); cvtNum(ops[k + 3].num, ops[k + 3].isFP, charBuf); charBuf->append((char)30); } else { cvtNum(ops[k].num, ops[k].isFP, charBuf); cvtNum(ops[k + 1].num, ops[k + 1].isFP, charBuf); cvtNum(ops[k + 2].num, ops[k + 2].isFP, charBuf); cvtNum(ops[k + 3].num, ops[k + 3].isFP, charBuf); charBuf->append((char)31); } } if (k == nOps - 5) { if (k % 8 == 0) { cvtNum(0, false, charBuf); cvtNum(ops[k].num, ops[k].isFP, charBuf); cvtNum(ops[k + 1].num, ops[k + 1].isFP, charBuf); cvtNum(ops[k + 2].num, ops[k + 2].isFP, charBuf); cvtNum(ops[k + 3].num, ops[k + 3].isFP, charBuf); cvtNum(ops[k + 4].num, ops[k + 4].isFP, charBuf); } else { cvtNum(ops[k].num, ops[k].isFP, charBuf); cvtNum(0, false, charBuf); cvtNum(ops[k + 1].num, ops[k + 1].isFP, charBuf); cvtNum(ops[k + 2].num, ops[k + 2].isFP, charBuf); cvtNum(ops[k + 4].num, ops[k + 4].isFP, charBuf); cvtNum(ops[k + 3].num, ops[k + 3].isFP, charBuf); } charBuf->append((char)8); } nOps = 0; openPath = true; break; case 0x001f: // hvcurveto if (nOps < 4 || !(nOps % 4 == 0 || (nOps - 1) % 4 == 0)) { //~ error(-1, "Wrong number of args (%d) to Type 2 hvcurveto", nOps); } for (k = 0; k < nOps && k != nOps - 5; k += 4) { if (k % 8 == 0) { cvtNum(ops[k].num, ops[k].isFP, charBuf); cvtNum(ops[k + 1].num, ops[k + 1].isFP, charBuf); cvtNum(ops[k + 2].num, ops[k + 2].isFP, charBuf); cvtNum(ops[k + 3].num, ops[k + 3].isFP, charBuf); charBuf->append((char)31); } else { cvtNum(ops[k].num, ops[k].isFP, charBuf); cvtNum(ops[k + 1].num, ops[k + 1].isFP, charBuf); cvtNum(ops[k + 2].num, ops[k + 2].isFP, charBuf); cvtNum(ops[k + 3].num, ops[k + 3].isFP, charBuf); charBuf->append((char)30); } } if (k == nOps - 5) { if (k % 8 == 0) { cvtNum(ops[k].num, ops[k].isFP, charBuf); cvtNum(0, false, charBuf); cvtNum(ops[k + 1].num, ops[k + 1].isFP, charBuf); cvtNum(ops[k + 2].num, ops[k + 2].isFP, charBuf); cvtNum(ops[k + 4].num, ops[k + 4].isFP, charBuf); cvtNum(ops[k + 3].num, ops[k + 3].isFP, charBuf); } else { cvtNum(0, false, charBuf); cvtNum(ops[k].num, ops[k].isFP, charBuf); cvtNum(ops[k + 1].num, ops[k + 1].isFP, charBuf); cvtNum(ops[k + 2].num, ops[k + 2].isFP, charBuf); cvtNum(ops[k + 3].num, ops[k + 3].isFP, charBuf); cvtNum(ops[k + 4].num, ops[k + 4].isFP, charBuf); } charBuf->append((char)8); } nOps = 0; openPath = true; break; case 0x0c00: // dotsection (should be Type 1 only?) // ignored nOps = 0; break; case 0x0c03: // and case 0x0c04: // or case 0x0c05: // not case 0x0c08: // store case 0x0c09: // abs case 0x0c0a: // add case 0x0c0b: // sub case 0x0c0c: // div case 0x0c0d: // load case 0x0c0e: // neg case 0x0c0f: // eq case 0x0c12: // drop case 0x0c14: // put case 0x0c15: // get case 0x0c16: // ifelse case 0x0c17: // random case 0x0c18: // mul case 0x0c1a: // sqrt case 0x0c1b: // dup case 0x0c1c: // exch case 0x0c1d: // index case 0x0c1e: // roll //~ error(-1, "Unimplemented Type 2 charstring op: 12.%d", file[i+1]); nOps = 0; break; case 0x0c22: // hflex if (nOps != 7) { //~ error(-1, "Wrong number of args (%d) to Type 2 hflex", nOps); } cvtNum(ops[0].num, ops[0].isFP, charBuf); cvtNum(0, false, charBuf); cvtNum(ops[1].num, ops[1].isFP, charBuf); cvtNum(ops[2].num, ops[2].isFP, charBuf); cvtNum(ops[3].num, ops[3].isFP, charBuf); cvtNum(0, false, charBuf); charBuf->append((char)8); cvtNum(ops[4].num, ops[4].isFP, charBuf); cvtNum(0, false, charBuf); cvtNum(ops[5].num, ops[5].isFP, charBuf); cvtNum(-ops[2].num, ops[2].isFP, charBuf); cvtNum(ops[6].num, ops[6].isFP, charBuf); cvtNum(0, false, charBuf); charBuf->append((char)8); nOps = 0; openPath = true; break; case 0x0c23: // flex if (nOps != 13) { //~ error(-1, "Wrong number of args (%d) to Type 2 flex", nOps); } cvtNum(ops[0].num, ops[0].isFP, charBuf); cvtNum(ops[1].num, ops[1].isFP, charBuf); cvtNum(ops[2].num, ops[2].isFP, charBuf); cvtNum(ops[3].num, ops[3].isFP, charBuf); cvtNum(ops[4].num, ops[4].isFP, charBuf); cvtNum(ops[5].num, ops[5].isFP, charBuf); charBuf->append((char)8); cvtNum(ops[6].num, ops[6].isFP, charBuf); cvtNum(ops[7].num, ops[7].isFP, charBuf); cvtNum(ops[8].num, ops[8].isFP, charBuf); cvtNum(ops[9].num, ops[9].isFP, charBuf); cvtNum(ops[10].num, ops[10].isFP, charBuf); cvtNum(ops[11].num, ops[11].isFP, charBuf); charBuf->append((char)8); nOps = 0; openPath = true; break; case 0x0c24: // hflex1 if (nOps != 9) { //~ error(-1, "Wrong number of args (%d) to Type 2 hflex1", nOps); } cvtNum(ops[0].num, ops[0].isFP, charBuf); cvtNum(ops[1].num, ops[1].isFP, charBuf); cvtNum(ops[2].num, ops[2].isFP, charBuf); cvtNum(ops[3].num, ops[3].isFP, charBuf); cvtNum(ops[4].num, ops[4].isFP, charBuf); cvtNum(0, false, charBuf); charBuf->append((char)8); cvtNum(ops[5].num, ops[5].isFP, charBuf); cvtNum(0, false, charBuf); cvtNum(ops[6].num, ops[6].isFP, charBuf); cvtNum(ops[7].num, ops[7].isFP, charBuf); cvtNum(ops[8].num, ops[8].isFP, charBuf); cvtNum(-(ops[1].num + ops[3].num + ops[7].num), ops[1].isFP | ops[3].isFP | ops[7].isFP, charBuf); charBuf->append((char)8); nOps = 0; openPath = true; break; case 0x0c25: // flex1 if (nOps != 11) { //~ error(-1, "Wrong number of args (%d) to Type 2 flex1", nOps); } cvtNum(ops[0].num, ops[0].isFP, charBuf); cvtNum(ops[1].num, ops[1].isFP, charBuf); cvtNum(ops[2].num, ops[2].isFP, charBuf); cvtNum(ops[3].num, ops[3].isFP, charBuf); cvtNum(ops[4].num, ops[4].isFP, charBuf); cvtNum(ops[5].num, ops[5].isFP, charBuf); charBuf->append((char)8); cvtNum(ops[6].num, ops[6].isFP, charBuf); cvtNum(ops[7].num, ops[7].isFP, charBuf); cvtNum(ops[8].num, ops[8].isFP, charBuf); cvtNum(ops[9].num, ops[9].isFP, charBuf); dx = ops[0].num + ops[2].num + ops[4].num + ops[6].num + ops[8].num; dy = ops[1].num + ops[3].num + ops[5].num + ops[7].num + ops[9].num; if (fabs(dx) > fabs(dy)) { cvtNum(ops[10].num, ops[10].isFP, charBuf); cvtNum(-dy, ops[1].isFP | ops[3].isFP | ops[5].isFP | ops[7].isFP | ops[9].isFP, charBuf); } else { cvtNum(-dx, ops[0].isFP | ops[2].isFP | ops[4].isFP | ops[6].isFP | ops[8].isFP, charBuf); cvtNum(ops[10].num, ops[10].isFP, charBuf); } charBuf->append((char)8); nOps = 0; openPath = true; break; default: //~ error(-1, "Illegal Type 2 charstring op: %04x", //~ ops[nOps].op); nOps = 0; break; } } } // charstring encryption if (top) { r2 = 4330; for (i = start; i < charBuf->getLength(); ++i) { byte = charBuf->getChar(i) ^ (r2 >> 8); charBuf->setChar(i, byte); r2 = (byte + r2) * 52845 + 22719; } } offsetBeingParsed.erase(offsetEmplaceResult.first); } void FoFiType1C::cvtGlyphWidth(bool useOp, GooString *charBuf, const Type1CPrivateDict *pDict) { double w; bool wFP; int i; if (useOp) { w = pDict->nominalWidthX + ops[0].num; wFP = pDict->nominalWidthXFP | ops[0].isFP; for (i = 1; i < nOps; ++i) { ops[i - 1] = ops[i]; } --nOps; } else { w = pDict->defaultWidthX; wFP = pDict->defaultWidthXFP; } cvtNum(0, false, charBuf); cvtNum(w, wFP, charBuf); charBuf->append((char)13); } void FoFiType1C::cvtNum(double x, bool isFP, GooString *charBuf) const { unsigned char buf[12]; int y, n; n = 0; if (isFP) { if (x >= -32768 && x < 32768) { y = (int)(x * 256.0); buf[0] = 255; buf[1] = (unsigned char)(y >> 24); buf[2] = (unsigned char)(y >> 16); buf[3] = (unsigned char)(y >> 8); buf[4] = (unsigned char)y; buf[5] = 255; buf[6] = 0; buf[7] = 0; buf[8] = 1; buf[9] = 0; buf[10] = 12; buf[11] = 12; n = 12; } else { //~ error(-1, "Type 2 fixed point constant out of range"); } } else { y = (int)x; if (y >= -107 && y <= 107) { buf[0] = (unsigned char)(y + 139); n = 1; } else if (y > 107 && y <= 1131) { y -= 108; buf[0] = (unsigned char)((y >> 8) + 247); buf[1] = (unsigned char)(y & 0xff); n = 2; } else if (y < -107 && y >= -1131) { y = -y - 108; buf[0] = (unsigned char)((y >> 8) + 251); buf[1] = (unsigned char)(y & 0xff); n = 2; } else { buf[0] = 255; buf[1] = (unsigned char)(y >> 24); buf[2] = (unsigned char)(y >> 16); buf[3] = (unsigned char)(y >> 8); buf[4] = (unsigned char)y; n = 5; } } charBuf->append((char *)buf, n); } void FoFiType1C::eexecWrite(Type1CEexecBuf *eb, const char *s) const { unsigned char *p; unsigned char x; for (p = (unsigned char *)s; *p; ++p) { x = *p ^ (eb->r1 >> 8); eb->r1 = (x + eb->r1) * 52845 + 22719; if (eb->ascii) { (*eb->outputFunc)(eb->outputStream, &hexChars[x >> 4], 1); (*eb->outputFunc)(eb->outputStream, &hexChars[x & 0x0f], 1); eb->line += 2; if (eb->line == 64) { (*eb->outputFunc)(eb->outputStream, "\n", 1); eb->line = 0; } } else { (*eb->outputFunc)(eb->outputStream, (char *)&x, 1); } } } void FoFiType1C::eexecWriteCharstring(Type1CEexecBuf *eb, const unsigned char *s, int n) const { unsigned char x; int i; // eexec encryption for (i = 0; i < n; ++i) { x = s[i] ^ (eb->r1 >> 8); eb->r1 = (x + eb->r1) * 52845 + 22719; if (eb->ascii) { (*eb->outputFunc)(eb->outputStream, &hexChars[x >> 4], 1); (*eb->outputFunc)(eb->outputStream, &hexChars[x & 0x0f], 1); eb->line += 2; if (eb->line == 64) { (*eb->outputFunc)(eb->outputStream, "\n", 1); eb->line = 0; } } else { (*eb->outputFunc)(eb->outputStream, (char *)&x, 1); } } } void FoFiType1C::writePSString(const char *s, FoFiOutputFunc outputFunc, void *outputStream) const { char buf[80]; const char *p; int i, c; i = 0; buf[i++] = '('; for (p = s; *p; ++p) { c = *p & 0xff; if (c == '(' || c == ')' || c == '\\') { buf[i++] = '\\'; buf[i++] = c; } else if (c < 0x20 || c >= 0x80) { buf[i++] = '\\'; buf[i++] = '0' + ((c >> 6) & 7); buf[i++] = '0' + ((c >> 3) & 7); buf[i++] = '0' + (c & 7); } else { buf[i++] = c; } if (i >= 64) { buf[i++] = '\\'; buf[i++] = '\n'; (*outputFunc)(outputStream, buf, i); i = 0; } } buf[i++] = ')'; (*outputFunc)(outputStream, buf, i); } bool FoFiType1C::parse() { Type1CIndex fdIdx; Type1CIndexVal val; int i; parsedOk = true; // some tools embed Type 1C fonts with an extra whitespace char at // the beginning if (len > 0 && file[0] != '\x01') { ++file; --len; } // find the indexes getIndex(getU8(2, &parsedOk), &nameIdx, &parsedOk); getIndex(nameIdx.endPos, &topDictIdx, &parsedOk); getIndex(topDictIdx.endPos, &stringIdx, &parsedOk); getIndex(stringIdx.endPos, &gsubrIdx, &parsedOk); if (!parsedOk) { return false; } gsubrBias = (gsubrIdx.len < 1240) ? 107 : (gsubrIdx.len < 33900) ? 1131 : 32768; // read the first font name getIndexVal(&nameIdx, 0, &val, &parsedOk); if (!parsedOk) { return false; } name = new GooString((char *)&file[val.pos], val.len); // read the top dict for the first font readTopDict(); // for CID fonts: read the FDArray dicts and private dicts if (topDict.firstOp == 0x0c1e) { if (topDict.fdArrayOffset == 0) { nFDs = 1; privateDicts = (Type1CPrivateDict *)gmalloc(sizeof(Type1CPrivateDict)); readPrivateDict(0, 0, &privateDicts[0]); } else { getIndex(topDict.fdArrayOffset, &fdIdx, &parsedOk); if (!parsedOk || fdIdx.len <= 0) { return false; } nFDs = fdIdx.len; privateDicts = (Type1CPrivateDict *)gmallocn(nFDs, sizeof(Type1CPrivateDict)); for (i = 0; i < nFDs; ++i) { getIndexVal(&fdIdx, i, &val, &parsedOk); if (!parsedOk) { return false; } readFD(val.pos, val.len, &privateDicts[i]); } } // for 8-bit fonts: read the private dict } else { nFDs = 1; privateDicts = (Type1CPrivateDict *)gmalloc(sizeof(Type1CPrivateDict)); readPrivateDict(topDict.privateOffset, topDict.privateSize, &privateDicts[0]); } // check for parse errors in the private dict(s) if (!parsedOk) { return false; } // get the charstrings index if (topDict.charStringsOffset <= 0) { parsedOk = false; return false; } getIndex(topDict.charStringsOffset, &charStringsIdx, &parsedOk); if (!parsedOk) { return false; } nGlyphs = charStringsIdx.len; // for CID fonts: read the FDSelect table if (topDict.firstOp == 0x0c1e) { readFDSelect(); if (!parsedOk) { return false; } } // read the charset if (!readCharset()) { parsedOk = false; return false; } // for 8-bit fonts: build the encoding if (topDict.firstOp != 0x0c14 && topDict.firstOp != 0x0c1e) { buildEncoding(); if (!parsedOk) { return false; } } return parsedOk; } void FoFiType1C::readTopDict() { Type1CIndexVal topDictPtr; int pos; topDict.firstOp = -1; topDict.versionSID = 0; topDict.noticeSID = 0; topDict.copyrightSID = 0; topDict.fullNameSID = 0; topDict.familyNameSID = 0; topDict.weightSID = 0; topDict.isFixedPitch = 0; topDict.italicAngle = 0; topDict.underlinePosition = -100; topDict.underlineThickness = 50; topDict.paintType = 0; topDict.charstringType = 2; topDict.fontMatrix[0] = 0.001; topDict.fontMatrix[1] = 0; topDict.fontMatrix[2] = 0; topDict.fontMatrix[3] = 0.001; topDict.fontMatrix[4] = 0; topDict.fontMatrix[5] = 0; topDict.hasFontMatrix = false; topDict.uniqueID = 0; topDict.fontBBox[0] = 0; topDict.fontBBox[1] = 0; topDict.fontBBox[2] = 0; topDict.fontBBox[3] = 0; topDict.strokeWidth = 0; topDict.charsetOffset = 0; topDict.encodingOffset = 0; topDict.charStringsOffset = 0; topDict.privateSize = 0; topDict.privateOffset = 0; topDict.registrySID = 0; topDict.orderingSID = 0; topDict.supplement = 0; topDict.fdArrayOffset = 0; topDict.fdSelectOffset = 0; getIndexVal(&topDictIdx, 0, &topDictPtr, &parsedOk); if (!parsedOk) { return; } pos = topDictPtr.pos; nOps = 0; while (pos < topDictPtr.pos + topDictPtr.len) { pos = getOp(pos, false, &parsedOk); if (!parsedOk) { break; } if (!ops[nOps - 1].isNum) { --nOps; // drop the operator if (topDict.firstOp < 0) { topDict.firstOp = ops[nOps].op; } switch (ops[nOps].op) { case 0x0000: topDict.versionSID = (int)ops[0].num; break; case 0x0001: topDict.noticeSID = (int)ops[0].num; break; case 0x0c00: topDict.copyrightSID = (int)ops[0].num; break; case 0x0002: topDict.fullNameSID = (int)ops[0].num; break; case 0x0003: topDict.familyNameSID = (int)ops[0].num; break; case 0x0004: topDict.weightSID = (int)ops[0].num; break; case 0x0c01: topDict.isFixedPitch = (int)ops[0].num; break; case 0x0c02: topDict.italicAngle = ops[0].num; break; case 0x0c03: topDict.underlinePosition = ops[0].num; break; case 0x0c04: topDict.underlineThickness = ops[0].num; break; case 0x0c05: topDict.paintType = (int)ops[0].num; break; case 0x0c06: topDict.charstringType = (int)ops[0].num; break; case 0x0c07: topDict.fontMatrix[0] = ops[0].num; topDict.fontMatrix[1] = ops[1].num; topDict.fontMatrix[2] = ops[2].num; topDict.fontMatrix[3] = ops[3].num; topDict.fontMatrix[4] = ops[4].num; topDict.fontMatrix[5] = ops[5].num; topDict.hasFontMatrix = true; break; case 0x000d: topDict.uniqueID = (int)ops[0].num; break; case 0x0005: topDict.fontBBox[0] = ops[0].num; topDict.fontBBox[1] = ops[1].num; topDict.fontBBox[2] = ops[2].num; topDict.fontBBox[3] = ops[3].num; break; case 0x0c08: topDict.strokeWidth = ops[0].num; break; case 0x000f: topDict.charsetOffset = (int)ops[0].num; break; case 0x0010: topDict.encodingOffset = (int)ops[0].num; break; case 0x0011: topDict.charStringsOffset = (int)ops[0].num; break; case 0x0012: topDict.privateSize = (int)ops[0].num; topDict.privateOffset = (int)ops[1].num; break; case 0x0c1e: topDict.registrySID = (int)ops[0].num; topDict.orderingSID = (int)ops[1].num; topDict.supplement = (int)ops[2].num; break; case 0x0c24: topDict.fdArrayOffset = (int)ops[0].num; break; case 0x0c25: topDict.fdSelectOffset = (int)ops[0].num; break; } nOps = 0; } } } // Read a CID font dict (FD) - this pulls out the private dict // pointer, and reads the private dict. It also pulls the FontMatrix // (if any) out of the FD. void FoFiType1C::readFD(int offset, int length, Type1CPrivateDict *pDict) { int pSize, pOffset; double fontMatrix[6] = { 0 }; bool hasFontMatrix; hasFontMatrix = false; fontMatrix[0] = fontMatrix[1] = fontMatrix[2] = 0; // make gcc happy fontMatrix[3] = fontMatrix[4] = fontMatrix[5] = 0; pSize = pOffset = 0; int posEnd; if (checkedAdd(offset, length, &posEnd)) { return; } int pos = offset; nOps = 0; while (pos < posEnd) { pos = getOp(pos, false, &parsedOk); if (!parsedOk) { return; } if (!ops[nOps - 1].isNum) { if (ops[nOps - 1].op == 0x0012) { if (nOps < 3) { parsedOk = false; return; } pSize = (int)ops[0].num; pOffset = (int)ops[1].num; break; } else if (ops[nOps - 1].op == 0x0c07) { fontMatrix[0] = ops[0].num; fontMatrix[1] = ops[1].num; fontMatrix[2] = ops[2].num; fontMatrix[3] = ops[3].num; fontMatrix[4] = ops[4].num; fontMatrix[5] = ops[5].num; hasFontMatrix = true; } nOps = 0; } } readPrivateDict(pOffset, pSize, pDict); if (hasFontMatrix) { pDict->fontMatrix[0] = fontMatrix[0]; pDict->fontMatrix[1] = fontMatrix[1]; pDict->fontMatrix[2] = fontMatrix[2]; pDict->fontMatrix[3] = fontMatrix[3]; pDict->fontMatrix[4] = fontMatrix[4]; pDict->fontMatrix[5] = fontMatrix[5]; pDict->hasFontMatrix = true; } } void FoFiType1C::readPrivateDict(int offset, int length, Type1CPrivateDict *pDict) { pDict->hasFontMatrix = false; pDict->nBlueValues = 0; pDict->nOtherBlues = 0; pDict->nFamilyBlues = 0; pDict->nFamilyOtherBlues = 0; pDict->blueScale = 0.039625; pDict->blueShift = 7; pDict->blueFuzz = 1; pDict->hasStdHW = false; pDict->hasStdVW = false; pDict->nStemSnapH = 0; pDict->nStemSnapV = 0; pDict->hasForceBold = false; pDict->forceBoldThreshold = 0; pDict->languageGroup = 0; pDict->expansionFactor = 0.06; pDict->initialRandomSeed = 0; pDict->subrsOffset = 0; pDict->defaultWidthX = 0; pDict->defaultWidthXFP = false; pDict->nominalWidthX = 0; pDict->nominalWidthXFP = false; // no dictionary if (offset == 0 || length == 0) { return; } int posEnd; if (checkedAdd(offset, length, &posEnd)) { return; } int pos = offset; nOps = 0; while (pos < posEnd) { pos = getOp(pos, false, &parsedOk); if (!parsedOk) { break; } if (!ops[nOps - 1].isNum) { --nOps; // drop the operator switch (ops[nOps].op) { case 0x0006: pDict->nBlueValues = getDeltaIntArray(pDict->blueValues, type1CMaxBlueValues); break; case 0x0007: pDict->nOtherBlues = getDeltaIntArray(pDict->otherBlues, type1CMaxOtherBlues); break; case 0x0008: pDict->nFamilyBlues = getDeltaIntArray(pDict->familyBlues, type1CMaxBlueValues); break; case 0x0009: pDict->nFamilyOtherBlues = getDeltaIntArray(pDict->familyOtherBlues, type1CMaxOtherBlues); break; case 0x0c09: pDict->blueScale = ops[0].num; break; case 0x0c0a: pDict->blueShift = (int)ops[0].num; break; case 0x0c0b: pDict->blueFuzz = (int)ops[0].num; break; case 0x000a: pDict->stdHW = ops[0].num; pDict->hasStdHW = true; break; case 0x000b: pDict->stdVW = ops[0].num; pDict->hasStdVW = true; break; case 0x0c0c: pDict->nStemSnapH = getDeltaFPArray(pDict->stemSnapH, type1CMaxStemSnap); break; case 0x0c0d: pDict->nStemSnapV = getDeltaFPArray(pDict->stemSnapV, type1CMaxStemSnap); break; case 0x0c0e: pDict->forceBold = ops[0].num != 0; pDict->hasForceBold = true; break; case 0x0c0f: pDict->forceBoldThreshold = ops[0].num; break; case 0x0c11: pDict->languageGroup = (int)ops[0].num; break; case 0x0c12: pDict->expansionFactor = ops[0].num; break; case 0x0c13: pDict->initialRandomSeed = (int)ops[0].num; break; case 0x0013: pDict->subrsOffset = offset + (int)ops[0].num; break; case 0x0014: pDict->defaultWidthX = ops[0].num; pDict->defaultWidthXFP = ops[0].isFP; break; case 0x0015: pDict->nominalWidthX = ops[0].num; pDict->nominalWidthXFP = ops[0].isFP; break; } nOps = 0; } } } void FoFiType1C::readFDSelect() { int fdSelectFmt, pos, nRanges, gid0, gid1, fd; fdSelect = (unsigned char *)gmalloc(nGlyphs); if (topDict.fdSelectOffset == 0) { for (int i = 0; i < nGlyphs; ++i) { fdSelect[i] = 0; } } else { pos = topDict.fdSelectOffset; fdSelectFmt = getU8(pos++, &parsedOk); if (!parsedOk) { return; } if (fdSelectFmt == 0) { if (!checkRegion(pos, nGlyphs)) { parsedOk = false; return; } memcpy(fdSelect, file + pos, nGlyphs); } else if (fdSelectFmt == 3) { nRanges = getU16BE(pos, &parsedOk); pos += 2; gid0 = getU16BE(pos, &parsedOk); pos += 2; for (int i = 1; i <= nRanges; ++i) { fd = getU8(pos++, &parsedOk); gid1 = getU16BE(pos, &parsedOk); if (!parsedOk) { return; } pos += 2; if (gid0 > gid1 || gid1 > nGlyphs) { //~ error(-1, "Bad FDSelect table in CID font"); parsedOk = false; return; } for (int j = gid0; j < gid1; ++j) { fdSelect[j] = fd; } gid0 = gid1; } for (int i = gid0; i < nGlyphs; ++i) { fdSelect[i] = 0; } } else { //~ error(-1, "Unknown FDSelect table format in CID font"); for (int i = 0; i < nGlyphs; ++i) { fdSelect[i] = 0; } } } } void FoFiType1C::buildEncoding() { char buf[256]; int nCodes, nRanges, encFormat; int pos, c, sid, nLeft, nSups, i, j; if (topDict.encodingOffset == 0) { encoding = (char **)fofiType1StandardEncoding; } else if (topDict.encodingOffset == 1) { encoding = (char **)fofiType1ExpertEncoding; } else { encoding = (char **)gmallocn(256, sizeof(char *)); for (i = 0; i < 256; ++i) { encoding[i] = nullptr; } pos = topDict.encodingOffset; encFormat = getU8(pos++, &parsedOk); if (!parsedOk) { return; } if ((encFormat & 0x7f) == 0) { nCodes = 1 + getU8(pos++, &parsedOk); if (!parsedOk) { return; } if (nCodes > nGlyphs) { nCodes = nGlyphs; } for (i = 1; i < nCodes && i < charsetLength; ++i) { c = getU8(pos++, &parsedOk); if (!parsedOk) { return; } if (encoding[c]) { gfree(encoding[c]); } encoding[c] = copyString(getString(charset[i], buf, &parsedOk)); } } else if ((encFormat & 0x7f) == 1) { nRanges = getU8(pos++, &parsedOk); if (!parsedOk) { return; } nCodes = 1; for (i = 0; i < nRanges; ++i) { c = getU8(pos++, &parsedOk); nLeft = getU8(pos++, &parsedOk); if (!parsedOk) { return; } for (j = 0; j <= nLeft && nCodes < nGlyphs && nCodes < charsetLength; ++j) { if (c < 256) { if (encoding[c]) { gfree(encoding[c]); } encoding[c] = copyString(getString(charset[nCodes], buf, &parsedOk)); } ++nCodes; ++c; } } } if (encFormat & 0x80) { nSups = getU8(pos++, &parsedOk); if (!parsedOk) { return; } for (i = 0; i < nSups; ++i) { c = getU8(pos++, &parsedOk); ; if (!parsedOk) { return; ; } sid = getU16BE(pos, &parsedOk); pos += 2; if (!parsedOk) { return; } if (encoding[c]) { gfree(encoding[c]); } encoding[c] = copyString(getString(sid, buf, &parsedOk)); } } } } bool FoFiType1C::readCharset() { int charsetFormat, c, pos; int nLeft, i, j; if (topDict.charsetOffset == 0) { charset = fofiType1CISOAdobeCharset; charsetLength = sizeof(fofiType1CISOAdobeCharset) / sizeof(unsigned short); } else if (topDict.charsetOffset == 1) { charset = fofiType1CExpertCharset; charsetLength = sizeof(fofiType1CExpertCharset) / sizeof(unsigned short); } else if (topDict.charsetOffset == 2) { charset = fofiType1CExpertSubsetCharset; charsetLength = sizeof(fofiType1CExpertSubsetCharset) / sizeof(unsigned short); } else { unsigned short *customCharset = (unsigned short *)gmallocn(nGlyphs, sizeof(unsigned short)); charsetLength = nGlyphs; for (i = 0; i < nGlyphs; ++i) { customCharset[i] = 0; } pos = topDict.charsetOffset; charsetFormat = getU8(pos++, &parsedOk); if (charsetFormat == 0) { for (i = 1; i < nGlyphs; ++i) { customCharset[i] = (unsigned short)getU16BE(pos, &parsedOk); pos += 2; if (!parsedOk) { break; } } } else if (charsetFormat == 1) { i = 1; while (i < nGlyphs) { c = getU16BE(pos, &parsedOk); pos += 2; nLeft = getU8(pos++, &parsedOk); if (!parsedOk) { break; } for (j = 0; j <= nLeft && i < nGlyphs; ++j) { customCharset[i++] = (unsigned short)c++; } } } else if (charsetFormat == 2) { i = 1; while (i < nGlyphs) { c = getU16BE(pos, &parsedOk); pos += 2; nLeft = getU16BE(pos, &parsedOk); pos += 2; if (!parsedOk) { break; } for (j = 0; j <= nLeft && i < nGlyphs; ++j) { customCharset[i++] = (unsigned short)c++; } } } if (!parsedOk) { gfree(customCharset); charset = nullptr; charsetLength = 0; return false; } charset = customCharset; } return true; } int FoFiType1C::getOp(int pos, bool charstring, bool *ok) { static const char nybChars[16] = "0123456789.ee -"; Type1COp op; char buf[65]; int b0, b1, nyb0, nyb1, x, i; b0 = getU8(pos++, ok); if (b0 == 28) { x = getU8(pos++, ok); x = (x << 8) | getU8(pos++, ok); if (x & 0x8000) { x |= ~0xffff; } op.num = x; } else if (!charstring && b0 == 29) { x = getU8(pos++, ok); x = (x << 8) | getU8(pos++, ok); x = (x << 8) | getU8(pos++, ok); x = (x << 8) | getU8(pos++, ok); if (x & 0x80000000) { x |= ~0xffffffff; } op.num = x; } else if (!charstring && b0 == 30) { i = 0; do { b1 = getU8(pos++, ok); nyb0 = b1 >> 4; nyb1 = b1 & 0x0f; if (nyb0 == 0xf) { break; } buf[i++] = nybChars[nyb0]; if (i == 64) { break; } if (nyb0 == 0xc) { buf[i++] = '-'; } if (i == 64) { break; } if (nyb1 == 0xf) { break; } buf[i++] = nybChars[nyb1]; if (i == 64) { break; } if (nyb1 == 0xc) { buf[i++] = '-'; } } while (i < 64); buf[i] = '\0'; op.num = gatof(buf); op.isFP = true; } else if (b0 >= 32 && b0 <= 246) { op.num = b0 - 139; } else if (b0 >= 247 && b0 <= 250) { op.num = ((b0 - 247) << 8) + getU8(pos++, ok) + 108; } else if (b0 >= 251 && b0 <= 254) { op.num = -((b0 - 251) << 8) - getU8(pos++, ok) - 108; } else if (charstring && b0 == 255) { x = getU8(pos++, ok); x = (x << 8) | getU8(pos++, ok); x = (x << 8) | getU8(pos++, ok); x = (x << 8) | getU8(pos++, ok); if (x & 0x80000000) { x |= ~0xffffffff; } op.num = (double)x / 65536.0; op.isFP = true; } else if (b0 == 12) { op.isNum = false; op.op = 0x0c00 + getU8(pos++, ok); } else { op.isNum = false; op.op = b0; } if (nOps < 49) { ops[nOps++] = op; } return pos; } // Convert the delta-encoded ops array to an array of ints. int FoFiType1C::getDeltaIntArray(int *arr, int maxLen) const { int x; int n, i; if ((n = nOps) > maxLen) { n = maxLen; } x = 0; for (i = 0; i < n; ++i) { int y; if (unlikely(std::isinf(ops[i].num))) { return i; } if (checkedAdd(x, (int)ops[i].num, &y)) { return i; } x = y; arr[i] = x; } return n; } // Convert the delta-encoded ops array to an array of doubles. int FoFiType1C::getDeltaFPArray(double *arr, int maxLen) const { double x; int n, i; if ((n = nOps) > maxLen) { n = maxLen; } x = 0; for (i = 0; i < n; ++i) { x += ops[i].num; arr[i] = x; } return n; } void FoFiType1C::getIndex(int pos, Type1CIndex *idx, bool *ok) const { idx->pos = pos; idx->len = getU16BE(pos, ok); if (idx->len == 0) { // empty indexes are legal and contain just the length field idx->offSize = 0; idx->startPos = idx->endPos = pos + 2; } else { idx->offSize = getU8(pos + 2, ok); if (idx->offSize < 1 || idx->offSize > 4) { *ok = false; } idx->startPos = pos + 3 + (idx->len + 1) * idx->offSize - 1; if (idx->startPos < 0 || idx->startPos >= len) { *ok = false; } idx->endPos = idx->startPos + getUVarBE(pos + 3 + idx->len * idx->offSize, idx->offSize, ok); if (idx->endPos < idx->startPos || idx->endPos > len) { *ok = false; } } } void FoFiType1C::getIndexVal(const Type1CIndex *idx, int i, Type1CIndexVal *val, bool *ok) const { int pos0, pos1; if (i < 0 || i >= idx->len) { *ok = false; return; } pos0 = idx->startPos + getUVarBE(idx->pos + 3 + i * idx->offSize, idx->offSize, ok); pos1 = idx->startPos + getUVarBE(idx->pos + 3 + (i + 1) * idx->offSize, idx->offSize, ok); if (pos0 < idx->startPos || pos0 > idx->endPos || pos1 <= idx->startPos || pos1 > idx->endPos || pos1 < pos0) { *ok = false; return; } val->pos = pos0; val->len = pos1 - pos0; } char *FoFiType1C::getString(int sid, char *buf, bool *ok) const { Type1CIndexVal val; int n; if (sid < 0) { buf[0] = '\0'; } else if (sid < 391) { strcpy(buf, fofiType1CStdStrings[sid]); } else { sid -= 391; getIndexVal(&stringIdx, sid, &val, ok); if (*ok) { if ((n = val.len) > 255) { n = 255; } strncpy(buf, (char *)&file[val.pos], n); buf[n] = '\0'; } else { buf[0] = '\0'; } } return buf; } poppler-24.02.0/fofi/FoFiType1C.h000066400000000000000000000212421455701731300163140ustar00rootroot00000000000000//======================================================================== // // FoFiType1C.h // // Copyright 1999-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 Takashi Iwai // Copyright (C) 2012 Thomas Freitag // Copyright (C) 2018-2020 Albert Astals Cid // Copyright (C) 2022 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 // //======================================================================== #ifndef FOFITYPE1C_H #define FOFITYPE1C_H #include "FoFiBase.h" #include "poppler_private_export.h" #include class GooString; //------------------------------------------------------------------------ struct Type1CIndex { int pos; // absolute position in file int len; // length (number of entries) int offSize; // offset size int startPos; // position of start of index data - 1 int endPos; // position one byte past end of the index }; struct Type1CIndexVal { int pos; // absolute position in file int len; // length, in bytes }; struct Type1CTopDict { int firstOp; int versionSID; int noticeSID; int copyrightSID; int fullNameSID; int familyNameSID; int weightSID; int isFixedPitch; double italicAngle; double underlinePosition; double underlineThickness; int paintType; int charstringType; double fontMatrix[6]; bool hasFontMatrix; // CID fonts are allowed to put their // FontMatrix in the FD instead of the // top dict int uniqueID; double fontBBox[4]; double strokeWidth; int charsetOffset; int encodingOffset; int charStringsOffset; int privateSize; int privateOffset; // CIDFont entries int registrySID; int orderingSID; int supplement; int fdArrayOffset; int fdSelectOffset; }; #define type1CMaxBlueValues 14 #define type1CMaxOtherBlues 10 #define type1CMaxStemSnap 12 struct Type1CPrivateDict { double fontMatrix[6]; bool hasFontMatrix; int blueValues[type1CMaxBlueValues]; int nBlueValues; int otherBlues[type1CMaxOtherBlues]; int nOtherBlues; int familyBlues[type1CMaxBlueValues]; int nFamilyBlues; int familyOtherBlues[type1CMaxOtherBlues]; int nFamilyOtherBlues; double blueScale; int blueShift; int blueFuzz; double stdHW; bool hasStdHW; double stdVW; bool hasStdVW; double stemSnapH[type1CMaxStemSnap]; int nStemSnapH; double stemSnapV[type1CMaxStemSnap]; int nStemSnapV; bool forceBold; bool hasForceBold; double forceBoldThreshold; int languageGroup; double expansionFactor; int initialRandomSeed; int subrsOffset; double defaultWidthX; bool defaultWidthXFP; double nominalWidthX; bool nominalWidthXFP; }; struct Type1COp { bool isNum = true; // true -> number, false -> operator bool isFP = false; // true -> floating point number, false -> int union { double num = 0; // if num is true int op; // if num is false }; }; struct Type1CEexecBuf { FoFiOutputFunc outputFunc; void *outputStream; bool ascii; // ASCII encoding? unsigned short r1; // eexec encryption key int line; // number of eexec chars left on current line }; //------------------------------------------------------------------------ // FoFiType1C //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT FoFiType1C : public FoFiBase { public: // Create a FoFiType1C object from a memory buffer. static FoFiType1C *make(const unsigned char *fileA, int lenA); // Create a FoFiType1C object from a file on disk. static FoFiType1C *load(const char *fileName); ~FoFiType1C() override; // Return the font name. const char *getName() const; // Return the encoding, as an array of 256 names (any of which may // be NULL). This is only useful with 8-bit fonts. char **getEncoding() const; // Get the glyph names. int getNumGlyphs() const { return nGlyphs; } GooString *getGlyphName(int gid) const; // 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; // Return the font matrix as an array of six numbers. void getFontMatrix(double *mat) const; // Convert to a Type 1 font, suitable for embedding in a PostScript // file. This is only useful with 8-bit fonts. If is // not NULL, it will be used in place of the encoding in the Type 1C // font. If is true the eexec section will be hex-encoded, // otherwise it will be left as binary data. If is non-NULL, // it will be used as the PostScript font name. void convertToType1(const char *psName, const char **newEncoding, bool ascii, FoFiOutputFunc outputFunc, void *outputStream); // Convert to a Type 0 CIDFont, suitable for embedding in a // PostScript file. will be used as the PostScript font // name. There are three cases for the CID-to-GID mapping: // (1) if is non-NULL, then it is the CID-to-GID mapping // (2) if is NULL and this is a CID CFF font, then the // 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); // 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. There are three cases for the CID-to-GID // mapping: // (1) if is non-NULL, then it is the CID-to-GID mapping // (2) if is NULL and this is a CID CFF font, then the // 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); private: FoFiType1C(const unsigned char *fileA, int lenA, bool freeFileDataA); void eexecCvtGlyph(Type1CEexecBuf *eb, const char *glyphName, int offset, int nBytes, const Type1CIndex *subrIdx, const Type1CPrivateDict *pDict); void cvtGlyph(int offset, int nBytes, GooString *charBuf, const Type1CIndex *subrIdx, const Type1CPrivateDict *pDict, bool top, std::set &offsetBeingParsed); void cvtGlyphWidth(bool useOp, GooString *charBuf, const Type1CPrivateDict *pDict); void cvtNum(double x, bool isFP, GooString *charBuf) const; void eexecWrite(Type1CEexecBuf *eb, const char *s) const; void eexecWriteCharstring(Type1CEexecBuf *eb, const unsigned char *s, int n) const; void writePSString(const char *s, FoFiOutputFunc outputFunc, void *outputStream) const; bool parse(); void readTopDict(); void readFD(int offset, int length, Type1CPrivateDict *pDict); void readPrivateDict(int offset, int length, Type1CPrivateDict *pDict); void readFDSelect(); void buildEncoding(); bool readCharset(); int getOp(int pos, bool charstring, bool *ok); int getDeltaIntArray(int *arr, int maxLen) const; int getDeltaFPArray(double *arr, int maxLen) const; void getIndex(int pos, Type1CIndex *idx, bool *ok) const; void getIndexVal(const Type1CIndex *idx, int i, Type1CIndexVal *val, bool *ok) const; char *getString(int sid, char *buf, bool *ok) const; GooString *name; char **encoding; Type1CIndex nameIdx; Type1CIndex topDictIdx; Type1CIndex stringIdx; Type1CIndex gsubrIdx; Type1CIndex charStringsIdx; Type1CTopDict topDict; Type1CPrivateDict *privateDicts; int nGlyphs; int nFDs; unsigned char *fdSelect; const unsigned short *charset; unsigned short charsetLength; int gsubrBias; bool parsedOk; Type1COp ops[49]; // operands and operator int nOps; // number of operands int nHints; // number of hints for the current glyph bool firstOp; // true if we haven't hit the first op yet bool openPath; // true if there is an unclosed path }; #endif poppler-24.02.0/glib/000077500000000000000000000000001455701731300142635ustar00rootroot00000000000000poppler-24.02.0/glib/.gitignore000066400000000000000000000002101455701731300162440ustar00rootroot00000000000000*.la *.lo .deps .libs Makefile Makefile.in poppler-enums.c poppler-enums.h poppler-features.h stamp-* test-poppler-glib *.gir *.typelib poppler-24.02.0/glib/CMakeLists.txt000066400000000000000000000130431455701731300170240ustar00rootroot00000000000000include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) include_directories( SYSTEM ${GLIB2_INCLUDE_DIRS} ${CAIRO_INCLUDE_DIRS} ) add_definitions( -DG_LOG_DOMAIN=\"Poppler\" ${GLIB2_CFLAGS_OTHER} ${CAIRO_CFLAGS} ${POPPLER_GLIB_DISABLE_DEPRECATED} ${POPPLER_GLIB_DISABLE_SINGLE_INCLUDES} ) configure_file(poppler-features.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/poppler-features.h @ONLY) if (GTK_FOUND AND BUILD_GTK_TESTS AND NOT MSVC) add_subdirectory(demo) add_subdirectory(tests) endif () set(poppler_glib_public_headers poppler-action.h poppler-date.h poppler-document.h poppler-page.h poppler-attachment.h poppler-form-field.h poppler-annot.h poppler-layer.h poppler-movie.h poppler-media.h poppler.h poppler-structure-element.h ) find_program(GLIB2_MKENUMS glib-mkenums) find_program(GLIB2_MKENUMS_PYTHON NAMES python3 python) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/poppler-enums.h COMMAND ${GLIB2_MKENUMS_PYTHON} ARGS ${GLIB2_MKENUMS} --template poppler-enums.h.template ${poppler_glib_public_headers} > ${CMAKE_CURRENT_BINARY_DIR}/poppler-enums.h WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${poppler_glib_public_headers} ${CMAKE_CURRENT_SOURCE_DIR}/poppler-enums.h.template ) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/poppler-enums.c COMMAND ${GLIB2_MKENUMS_PYTHON} ARGS ${GLIB2_MKENUMS} --template poppler-enums.c.template ${poppler_glib_public_headers} > ${CMAKE_CURRENT_BINARY_DIR}/poppler-enums.c WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${poppler_glib_public_headers} ${CMAKE_CURRENT_BINARY_DIR}/poppler-enums.h ${CMAKE_CURRENT_SOURCE_DIR}/poppler-enums.c.template ) set(poppler_glib_SRCS poppler-action.cc poppler-date.cc poppler-document.cc poppler-page.cc poppler-attachment.cc poppler-form-field.cc poppler-annot.cc poppler-layer.cc poppler-movie.cc poppler-media.cc poppler.cc poppler-cached-file-loader.cc poppler-input-stream.cc poppler-structure-element.cc ) set(poppler_glib_generated_SRCS ${CMAKE_CURRENT_BINARY_DIR}/poppler-enums.c ${CMAKE_SOURCE_DIR}/poppler/CairoFontEngine.cc ${CMAKE_SOURCE_DIR}/poppler/CairoOutputDev.cc ${CMAKE_SOURCE_DIR}/poppler/CairoRescaleBox.cc ) 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.26.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}) if (ENABLE_NSS3) target_include_directories(poppler-glib SYSTEM PRIVATE ${NSS3_INCLUDE_DIRS}) endif() install(FILES ${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) set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${CMAKE_CURRENT_BINARY_DIR}/poppler-enums.h ${CMAKE_CURRENT_BINARY_DIR}/poppler-enums.c" ) # GObject Introspection if (HAVE_INTROSPECTION AND BUILD_SHARED_LIBS) include(GObjectIntrospectionMacros) # General gir: Reset object-list for introspection & load tool args set(INTROSPECTION_GIRS) set(INTROSPECTION_SCANNER_ARGS "--add-include-path=${CMAKE_CURRENT_SOURCE_DIR}" "--warn-all") set(INTROSPECTION_COMPILER_ARGS ${INTROSPECTION_COMPILER_ARGS} "--includedir=${CMAKE_CURRENT_SOURCE_DIR}") # Poppler: Assign package to gir & export keys set(Poppler_0_18_gir "poppler-glib") set(Poppler_0_18_gir_EXPORT_PACKAGES "poppler-glib") # Then load library and header lists set(Poppler_0_18_gir_LIBS "poppler-glib" "poppler") set(Poppler_0_18_gir_INCLUDES "GObject-2.0" "Gio-2.0" "cairo-1.0") # Format list of include directories as compiler flags get_directory_property(_tmp_includes INCLUDE_DIRECTORIES) _gir_list_prefix(_includes _tmp_includes "-I") # And set flags for gir compiler and scanner set(Poppler_0_18_gir_CFLAGS ${_includes} -L${CMAKE_BINARY_DIR} -L${CMAKE_CURRENT_BINARY_DIR}) set(Poppler_0_18_gir_SCANNERFLAGS "--c-include=poppler.h") # Load temporary source-file lists, including a few generated at build set(orig_introspect_srcs ${poppler_glib_SRCS} ${poppler_glib_public_headers}) set(gen_introspect_srcs "poppler-enums.c" "poppler-enums.h" "poppler-features.h" "poppler-macros.h") # Prefix the files with their correct directories for full paths _gir_list_prefix(_orig_introspect_paths orig_introspect_srcs "${CMAKE_CURRENT_SOURCE_DIR}/") _gir_list_prefix(_gen_introspect_paths gen_introspect_srcs "${CMAKE_CURRENT_BINARY_DIR}/") # Now load them to the final file list set(Poppler_0_18_gir_FILES ${_orig_introspect_paths} ${_gen_introspect_paths}) # Finally, load the list of objects for introspection & invoke macro list(APPEND INTROSPECTION_GIRS Poppler-0.18.gir) gir_add_introspections(INTROSPECTION_GIRS) endif () if(ENABLE_GTK_DOC) add_subdirectory(reference) endif() check_function_exists(explicit_bzero HAVE_EXPLICIT_BZERO) poppler-24.02.0/glib/demo/000077500000000000000000000000001455701731300152075ustar00rootroot00000000000000poppler-24.02.0/glib/demo/.gitignore000066400000000000000000000000221455701731300171710ustar00rootroot00000000000000poppler-glib-demo poppler-24.02.0/glib/demo/CMakeLists.txt000066400000000000000000000006531455701731300177530ustar00rootroot00000000000000set(poppler_glib_demo_SRCS main.c find.c fonts.c forms.c info.cc images.c links.c outline.c page.c print.c render.c text.c transitions.c utils.c annots.c attachments.c 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) poppler-24.02.0/glib/demo/annots.c000066400000000000000000001371021455701731300166610ustar00rootroot00000000000000/* * Copyright (C) 2008 Inigo Martinez * * 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 "annots.h" #include "utils.h" #define STAMP_CUSTOM_IMAGE "Custom image" enum { ANNOTS_TYPE_COLUMN, ANNOTS_COLOR_COLUMN, ANNOTS_FLAG_INVISIBLE_COLUMN, ANNOTS_FLAG_HIDDEN_COLUMN, ANNOTS_FLAG_PRINT_COLUMN, ANNOTS_COLUMN, N_COLUMNS }; enum { SELECTED_TYPE_COLUMN, SELECTED_LABEL_COLUMN, SELECTED_N_COLUMNS }; typedef struct { const guint type; const gchar *label; } 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" }, }; static const char *stamp_types[] = { [POPPLER_ANNOT_STAMP_ICON_UNKNOWN] = "Unknown", [POPPLER_ANNOT_STAMP_ICON_APPROVED] = "APPROVED", [POPPLER_ANNOT_STAMP_ICON_AS_IS] = "AS_IS", [POPPLER_ANNOT_STAMP_ICON_CONFIDENTIAL] = "CONFIDENTIAL", [POPPLER_ANNOT_STAMP_ICON_FINAL] = "FINAL", [POPPLER_ANNOT_STAMP_ICON_EXPERIMENTAL] = "EXPERIMENTAL", [POPPLER_ANNOT_STAMP_ICON_EXPIRED] = "EXPIRED", [POPPLER_ANNOT_STAMP_ICON_NOT_APPROVED] = "NOT_APPROVED", [POPPLER_ANNOT_STAMP_ICON_NOT_FOR_PUBLIC_RELEASE] = "NOT_FOR_PUBLIC_RELEASE", [POPPLER_ANNOT_STAMP_ICON_SOLD] = "SOLD", [POPPLER_ANNOT_STAMP_ICON_DEPARTMENTAL] = "DEPARTMENTAL", [POPPLER_ANNOT_STAMP_ICON_FOR_COMMENT] = "FOR_COMMENT", [POPPLER_ANNOT_STAMP_ICON_FOR_PUBLIC_RELEASE] = "FOR_PUBLIC_RELEASE", [POPPLER_ANNOT_STAMP_ICON_TOP_SECRET] = "TOP_SECRET", [POPPLER_ANNOT_STAMP_ICON_NONE] = "None" }; typedef enum { MODE_NORMAL, /* Regular use as pointer in the page */ MODE_ADD, /* To add simple annotations */ MODE_EDIT, /* To move/edit an annotation */ MODE_DRAWING /* To add annotations that require mouse interactions */ } ModeType; typedef struct { PopplerDocument *doc; PopplerPage *page; PopplerAnnot *active_annot; GtkWidget *tree_view; GtkListStore *model; GtkWidget *darea; GtkWidget *annot_view; GtkWidget *timer_label; GtkWidget *remove_button; GtkWidget *type_selector; GtkWidget *stamp_selector; GtkWidget *main_box; gint num_page; gint annot_type; char *custom_image_filename; ModeType mode; cairo_surface_t *surface; GdkRGBA annot_color; GdkPoint start; GdkPoint stop; GdkCursorType cursor; guint annotations_idle; } PgdAnnotsDemo; static void pgd_annots_viewer_queue_redraw(PgdAnnotsDemo *demo); static void pgd_annots_free(PgdAnnotsDemo *demo) { if (!demo) { return; } if (demo->annotations_idle > 0) { g_source_remove(demo->annotations_idle); demo->annotations_idle = 0; } if (demo->doc) { g_object_unref(demo->doc); demo->doc = NULL; } if (demo->page) { g_object_unref(demo->page); demo->page = NULL; } if (demo->model) { g_object_unref(demo->model); demo->model = NULL; } g_free(demo); } static GtkWidget *pgd_annot_view_new(void) { GtkWidget *frame, *label; frame = gtk_frame_new(NULL); gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE); label = gtk_label_new(NULL); gtk_label_set_markup(GTK_LABEL(label), "Annotation Properties"); gtk_frame_set_label_widget(GTK_FRAME(frame), label); gtk_widget_show(label); return frame; } const gchar *get_annot_type(PopplerAnnot *poppler_annot) { switch (poppler_annot_get_annot_type(poppler_annot)) { case POPPLER_ANNOT_TEXT: return "Text"; case POPPLER_ANNOT_LINK: return "Link"; case POPPLER_ANNOT_FREE_TEXT: return "Free Text"; case POPPLER_ANNOT_LINE: return "Line"; case POPPLER_ANNOT_SQUARE: return "Square"; case POPPLER_ANNOT_CIRCLE: return "Circle"; case POPPLER_ANNOT_POLYGON: return "Polygon"; case POPPLER_ANNOT_POLY_LINE: return "Poly Line"; case POPPLER_ANNOT_HIGHLIGHT: return "Highlight"; case POPPLER_ANNOT_UNDERLINE: return "Underline"; case POPPLER_ANNOT_SQUIGGLY: return "Squiggly"; case POPPLER_ANNOT_STRIKE_OUT: return "Strike Out"; case POPPLER_ANNOT_STAMP: return "Stamp"; case POPPLER_ANNOT_CARET: return "Caret"; case POPPLER_ANNOT_INK: return "Ink"; case POPPLER_ANNOT_POPUP: return "Popup"; case POPPLER_ANNOT_FILE_ATTACHMENT: return "File Attachment"; case POPPLER_ANNOT_SOUND: return "Sound"; case POPPLER_ANNOT_MOVIE: return "Movie"; case POPPLER_ANNOT_WIDGET: return "Widget"; case POPPLER_ANNOT_SCREEN: return "Screen"; case POPPLER_ANNOT_PRINTER_MARK: return "Printer Mark"; case POPPLER_ANNOT_TRAP_NET: return "Trap Net"; case POPPLER_ANNOT_WATERMARK: return "Watermark"; case POPPLER_ANNOT_3D: return "3D"; default: break; } return "Unknown"; } GdkPixbuf *get_annot_color(PopplerAnnot *poppler_annot) { PopplerColor *poppler_color; if ((poppler_color = poppler_annot_get_color(poppler_annot))) { GdkPixbuf *pixbuf_tmp, *pixbuf; pixbuf_tmp = pgd_pixbuf_new_for_color(poppler_color); pixbuf = gdk_pixbuf_scale_simple(pixbuf_tmp, 16, 16, GDK_INTERP_BILINEAR); g_object_unref(pixbuf_tmp); g_free(poppler_color); return pixbuf; } return NULL; } gchar *get_markup_date(PopplerAnnotMarkup *poppler_annot) { GDate *date; struct tm t; time_t timet; date = poppler_annot_markup_get_date(poppler_annot); if (!date) { return NULL; } g_date_to_struct_tm(date, &t); g_date_free(date); timet = mktime(&t); return timet == (time_t)-1 ? NULL : pgd_format_date(timet); } const gchar *get_markup_reply_to(PopplerAnnotMarkup *poppler_annot) { switch (poppler_annot_markup_get_reply_to(poppler_annot)) { case POPPLER_ANNOT_MARKUP_REPLY_TYPE_R: return "Type R"; case POPPLER_ANNOT_MARKUP_REPLY_TYPE_GROUP: return "Type Group"; default: break; } return "Unknown"; } const gchar *get_markup_external_data(PopplerAnnotMarkup *poppler_annot) { switch (poppler_annot_markup_get_external_data(poppler_annot)) { case POPPLER_ANNOT_EXTERNAL_DATA_MARKUP_3D: return "Markup 3D"; default: break; } return "Unknown"; } const gchar *get_text_state(PopplerAnnotText *poppler_annot) { switch (poppler_annot_text_get_state(poppler_annot)) { case POPPLER_ANNOT_TEXT_STATE_MARKED: return "Marked"; case POPPLER_ANNOT_TEXT_STATE_UNMARKED: return "Unmarked"; case POPPLER_ANNOT_TEXT_STATE_ACCEPTED: return "Accepted"; case POPPLER_ANNOT_TEXT_STATE_REJECTED: return "Rejected"; case POPPLER_ANNOT_TEXT_STATE_CANCELLED: return "Cancelled"; case POPPLER_ANNOT_TEXT_STATE_COMPLETED: return "Completed"; case POPPLER_ANNOT_TEXT_STATE_NONE: return "None"; case POPPLER_ANNOT_TEXT_STATE_UNKNOWN: return "Unknown"; default: break; } return "Unknown"; } const gchar *get_free_text_quadding(PopplerAnnotFreeText *poppler_annot) { switch (poppler_annot_free_text_get_quadding(poppler_annot)) { case POPPLER_ANNOT_FREE_TEXT_QUADDING_LEFT_JUSTIFIED: return "Left Justified"; case POPPLER_ANNOT_FREE_TEXT_QUADDING_CENTERED: return "Centered"; case POPPLER_ANNOT_FREE_TEXT_QUADDING_RIGHT_JUSTIFIED: return "Right Justified"; default: break; } return "Unknown"; } gchar *get_free_text_callout_line(PopplerAnnotFreeText *poppler_annot) { PopplerAnnotCalloutLine *callout; gchar *text; if ((callout = poppler_annot_free_text_get_callout_line(poppler_annot))) { text = g_strdup_printf("%f,%f,%f,%f", callout->x1, callout->y1, callout->x2, callout->y2); if (callout->multiline) { text = g_strdup_printf("%s,%f,%f", text, callout->x3, callout->y3); } return text; } return NULL; } static void pgd_annots_update_cursor(PgdAnnotsDemo *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_annots_start_add_annot(GtkWidget *button, PgdAnnotsDemo *demo) { GtkTreeModel *model; GtkTreeIter iter; gtk_combo_box_get_active_iter(GTK_COMBO_BOX(demo->type_selector), &iter); model = gtk_combo_box_get_model(GTK_COMBO_BOX(demo->type_selector)); gtk_tree_model_get(model, &iter, SELECTED_TYPE_COLUMN, &(demo->annot_type), -1); demo->mode = MODE_ADD; pgd_annots_update_cursor(demo, GDK_TCROSS); } static void pgd_annots_remove_annot(GtkWidget *button, PgdAnnotsDemo *demo) { GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter; selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(demo->tree_view)); if (gtk_tree_selection_get_selected(selection, &model, &iter)) { PopplerAnnot *annot; gtk_tree_model_get(model, &iter, ANNOTS_COLUMN, &annot, -1); poppler_page_remove_annot(demo->page, annot); g_object_unref(annot); gtk_list_store_remove(GTK_LIST_STORE(model), &iter); pgd_annots_viewer_queue_redraw(demo); } } static void pgd_annot_view_set_annot_markup(GtkWidget *table, PopplerAnnotMarkup *markup, gint *row) { gchar *text; PopplerRectangle rect; text = poppler_annot_markup_get_label(markup); pgd_table_add_property(GTK_GRID(table), "Label:", text, row); g_free(text); if (poppler_annot_markup_has_popup(markup)) { pgd_table_add_property(GTK_GRID(table), "Popup is open:", poppler_annot_markup_get_popup_is_open(markup) ? "Yes" : "No", row); poppler_annot_markup_get_popup_rectangle(markup, &rect); text = g_strdup_printf("X1: %.2f, Y1: %.2f, X2: %.2f, Y2: %.2f", rect.x1, rect.y1, rect.x2, rect.y2); pgd_table_add_property(GTK_GRID(table), "Popup Rectangle:", text, row); g_free(text); } text = g_strdup_printf("%f", poppler_annot_markup_get_opacity(markup)); pgd_table_add_property(GTK_GRID(table), "Opacity:", text, row); g_free(text); text = get_markup_date(markup); pgd_table_add_property(GTK_GRID(table), "Date:", text, row); g_free(text); text = poppler_annot_markup_get_subject(markup); pgd_table_add_property(GTK_GRID(table), "Subject:", text, row); g_free(text); pgd_table_add_property(GTK_GRID(table), "Reply To:", get_markup_reply_to(markup), row); pgd_table_add_property(GTK_GRID(table), "External Data:", get_markup_external_data(markup), row); } static void pgd_annot_view_set_annot_text(GtkWidget *table, PopplerAnnotText *annot, gint *row) { gchar *text; pgd_table_add_property(GTK_GRID(table), "Is open:", poppler_annot_text_get_is_open(annot) ? "Yes" : "No", row); text = poppler_annot_text_get_icon(annot); pgd_table_add_property(GTK_GRID(table), "Icon:", text, row); g_free(text); pgd_table_add_property(GTK_GRID(table), "State:", get_text_state(annot), row); } static void pgd_annot_color_changed(GtkButton *button, GParamSpec *pspec, PgdAnnotsDemo *demo) { gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(button), &demo->annot_color); } static void pgd_annot_view_set_annot_text_markup(GtkWidget *table, PopplerAnnotTextMarkup *annot, gint *row) { gint i; gchar *text = NULL; gchar *prev_text = NULL; GArray *quads_array = NULL; PopplerQuadrilateral quadrilateral; quads_array = poppler_annot_text_markup_get_quadrilaterals(annot); prev_text = g_strdup(""); for (i = 0; i < quads_array->len; i++) { quadrilateral = g_array_index(quads_array, PopplerQuadrilateral, i); text = g_strdup_printf("%s%2d:(%.2f,%.2f) (%.2f,%.2f)\n" " (%.2f,%.2f) (%.2f,%.2f)\n", prev_text, i + 1, quadrilateral.p1.x, quadrilateral.p1.y, quadrilateral.p2.x, quadrilateral.p2.y, quadrilateral.p3.x, quadrilateral.p3.y, quadrilateral.p4.x, quadrilateral.p4.y); g_free(prev_text); prev_text = text; } text = g_strchomp(text); pgd_table_add_property(GTK_GRID(table), "Quadrilaterals:", text, row); g_array_free(quads_array, TRUE); g_free(text); } static void pgd_annot_view_set_annot_free_text(GtkWidget *table, PopplerAnnotFreeText *annot, gint *row) { gchar *text; pgd_table_add_property(GTK_GRID(table), "Quadding:", get_free_text_quadding(annot), row); text = get_free_text_callout_line(annot); pgd_table_add_property(GTK_GRID(table), "Callout:", text, row); g_free(text); } static void pgd_annot_view_set_annot_stamp(GtkWidget *table, PopplerAnnotStamp *annot, gint *row) { PopplerAnnotStampIcon icon; icon = poppler_annot_stamp_get_icon(annot); pgd_table_add_property(GTK_GRID(table), "Icon Name:", stamp_types[icon], row); } static void pgd_annots_file_attachment_save_dialog_response(GtkFileChooser *file_chooser, gint response, PopplerAttachment *attachment) { gchar *filename; GError *error = NULL; if (response != GTK_RESPONSE_ACCEPT) { g_object_unref(attachment); gtk_widget_destroy(GTK_WIDGET(file_chooser)); return; } filename = gtk_file_chooser_get_filename(file_chooser); if (!poppler_attachment_save(attachment, filename, &error)) { g_warning("%s", error->message); g_error_free(error); } g_free(filename); g_object_unref(attachment); gtk_widget_destroy(GTK_WIDGET(file_chooser)); } static void pgd_annot_save_file_attachment_button_clicked(GtkButton *button, PopplerAnnotFileAttachment *annot) { GtkWidget *file_chooser; PopplerAttachment *attachment; attachment = poppler_annot_file_attachment_get_attachment(annot); if (!attachment) { return; } file_chooser = gtk_file_chooser_dialog_new("Save attachment", GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(button))), GTK_FILE_CHOOSER_ACTION_SAVE, "_Cancel", GTK_RESPONSE_CANCEL, "_Save", GTK_RESPONSE_ACCEPT, NULL); gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(file_chooser), poppler_attachment_get_name(attachment)); g_signal_connect(G_OBJECT(file_chooser), "response", G_CALLBACK(pgd_annots_file_attachment_save_dialog_response), (gpointer)attachment); gtk_widget_show(file_chooser); } static void pgd_annot_view_set_annot_file_attachment(GtkWidget *table, PopplerAnnotFileAttachment *annot, gint *row) { GtkWidget *button; gchar *text; text = poppler_annot_file_attachment_get_name(annot); pgd_table_add_property(GTK_GRID(table), "Attachment Name:", text, row); g_free(text); button = gtk_button_new_with_label("Save Attachment"); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(pgd_annot_save_file_attachment_button_clicked), (gpointer)annot); pgd_table_add_property_with_custom_widget(GTK_GRID(table), "File Attachment:", button, row); gtk_widget_show(button); } static void pgd_annot_view_set_annot_movie(GtkWidget *table, PopplerAnnotMovie *annot, gint *row) { GtkWidget *movie_view; gchar *text; text = poppler_annot_movie_get_title(annot); pgd_table_add_property(GTK_GRID(table), "Movie Title:", text, row); g_free(text); movie_view = pgd_movie_view_new(); pgd_movie_view_set_movie(movie_view, poppler_annot_movie_get_movie(annot)); pgd_table_add_property_with_custom_widget(GTK_GRID(table), "Movie:", movie_view, row); gtk_widget_show(movie_view); } static void pgd_annot_view_set_annot_screen(GtkWidget *table, PopplerAnnotScreen *annot, gint *row) { GtkWidget *action_view; action_view = pgd_action_view_new(NULL); pgd_action_view_set_action(action_view, poppler_annot_screen_get_action(annot)); pgd_table_add_property_with_custom_widget(GTK_GRID(table), "Action:", action_view, row); gtk_widget_show(action_view); } static void pgd_annot_view_set_annot(PgdAnnotsDemo *demo, PopplerAnnot *annot) { GtkWidget *table; gint row = 0; gchar *text; time_t timet; PopplerRectangle rect; table = gtk_bin_get_child(GTK_BIN(demo->annot_view)); if (table) { gtk_container_remove(GTK_CONTAINER(demo->annot_view), table); } if (!annot) { return; } 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); text = poppler_annot_get_contents(annot); pgd_table_add_property(GTK_GRID(table), "Contents:", text, &row); g_free(text); text = poppler_annot_get_name(annot); pgd_table_add_property(GTK_GRID(table), "Name:", text, &row); g_free(text); text = poppler_annot_get_modified(annot); if (poppler_date_parse(text, &timet)) { g_free(text); text = pgd_format_date(timet); } pgd_table_add_property(GTK_GRID(table), "Modified:", text, &row); g_free(text); poppler_annot_get_rectangle(annot, &rect); text = g_strdup_printf("(%.2f;%.2f) (%.2f;%.2f)", rect.x1, rect.y1, rect.x2, rect.y2); pgd_table_add_property(GTK_GRID(table), "Coords:", text, &row); g_free(text); if (POPPLER_IS_ANNOT_MARKUP(annot)) { pgd_annot_view_set_annot_markup(table, POPPLER_ANNOT_MARKUP(annot), &row); } switch (poppler_annot_get_annot_type(annot)) { case POPPLER_ANNOT_TEXT: pgd_annot_view_set_annot_text(table, POPPLER_ANNOT_TEXT(annot), &row); break; case POPPLER_ANNOT_HIGHLIGHT: case POPPLER_ANNOT_UNDERLINE: case POPPLER_ANNOT_SQUIGGLY: case POPPLER_ANNOT_STRIKE_OUT: pgd_annot_view_set_annot_text_markup(table, POPPLER_ANNOT_TEXT_MARKUP(annot), &row); break; case POPPLER_ANNOT_FREE_TEXT: pgd_annot_view_set_annot_free_text(table, POPPLER_ANNOT_FREE_TEXT(annot), &row); break; case POPPLER_ANNOT_FILE_ATTACHMENT: pgd_annot_view_set_annot_file_attachment(table, POPPLER_ANNOT_FILE_ATTACHMENT(annot), &row); break; case POPPLER_ANNOT_MOVIE: pgd_annot_view_set_annot_movie(table, POPPLER_ANNOT_MOVIE(annot), &row); break; case POPPLER_ANNOT_SCREEN: pgd_annot_view_set_annot_screen(table, POPPLER_ANNOT_SCREEN(annot), &row); break; case POPPLER_ANNOT_STAMP: pgd_annot_view_set_annot_stamp(table, POPPLER_ANNOT_STAMP(annot), &row); break; default: break; } gtk_container_add(GTK_CONTAINER(demo->annot_view), table); gtk_widget_show(table); } static void pgd_annots_add_annot_to_model(PgdAnnotsDemo *demo, PopplerAnnot *annot, PopplerRectangle area, gboolean selected) { GtkTreeIter iter; GdkPixbuf *pixbuf; PopplerAnnotFlag flags; pixbuf = get_annot_color(annot); flags = poppler_annot_get_flags(annot); gtk_list_store_append(demo->model, &iter); gtk_list_store_set(demo->model, &iter, ANNOTS_TYPE_COLUMN, get_annot_type(annot), ANNOTS_COLOR_COLUMN, pixbuf, ANNOTS_FLAG_INVISIBLE_COLUMN, (flags & POPPLER_ANNOT_FLAG_INVISIBLE), ANNOTS_FLAG_HIDDEN_COLUMN, (flags & POPPLER_ANNOT_FLAG_HIDDEN), ANNOTS_FLAG_PRINT_COLUMN, (flags & POPPLER_ANNOT_FLAG_PRINT), ANNOTS_COLUMN, annot, -1); if (selected) { GtkTreePath *path; path = gtk_tree_model_get_path(GTK_TREE_MODEL(demo->model), &iter); gtk_tree_view_set_cursor(GTK_TREE_VIEW(demo->tree_view), path, NULL, FALSE); gtk_tree_path_free(path); } if (pixbuf) { g_object_unref(pixbuf); } } static void pgd_annots_get_annots(PgdAnnotsDemo *demo) { GList *mapping, *l; gint n_fields; GTimer *timer; gtk_list_store_clear(demo->model); pgd_annot_view_set_annot(demo, NULL); if (demo->page) { g_object_unref(demo->page); demo->page = NULL; } demo->page = poppler_document_get_page(demo->doc, demo->num_page); if (!demo->page) { return; } timer = g_timer_new(); mapping = poppler_page_get_annot_mapping(demo->page); g_timer_stop(timer); n_fields = g_list_length(mapping); if (n_fields > 0) { gchar *str; str = g_strdup_printf("%d annotations found in %.4f seconds", n_fields, g_timer_elapsed(timer, NULL)); gtk_label_set_markup(GTK_LABEL(demo->timer_label), str); g_free(str); } else { gtk_label_set_markup(GTK_LABEL(demo->timer_label), "No annotations found"); } g_timer_destroy(timer); for (l = mapping; l; l = g_list_next(l)) { PopplerAnnotMapping *amapping; amapping = (PopplerAnnotMapping *)l->data; pgd_annots_add_annot_to_model(demo, amapping->annot, amapping->area, FALSE); } poppler_page_free_annot_mapping(mapping); } static void pgd_annots_page_selector_value_changed(GtkSpinButton *spinbutton, PgdAnnotsDemo *demo) { demo->num_page = (gint)gtk_spin_button_get_value(spinbutton) - 1; pgd_annots_viewer_queue_redraw(demo); pgd_annots_get_annots(demo); } static void pgd_annots_selection_changed(GtkTreeSelection *treeselection, PgdAnnotsDemo *demo) { GtkTreeModel *model; GtkTreeIter iter; if (gtk_tree_selection_get_selected(treeselection, &model, &iter)) { PopplerAnnot *annot; gtk_tree_model_get(model, &iter, ANNOTS_COLUMN, &annot, -1); pgd_annot_view_set_annot(demo, annot); g_object_unref(annot); gtk_widget_set_sensitive(demo->remove_button, TRUE); } else { pgd_annot_view_set_annot(demo, NULL); gtk_widget_set_sensitive(demo->remove_button, FALSE); } } static void pgd_annots_flags_toggled(GtkCellRendererToggle *renderer, gchar *path_str, PgdAnnotsDemo *demo, gint column, PopplerAnnotFlag flag_bit) { GtkTreeIter iter; gboolean fixed; PopplerAnnot *annot; PopplerAnnotFlag flags; GtkTreePath *path = gtk_tree_path_new_from_string(path_str); GtkTreeModel *model = GTK_TREE_MODEL(demo->model); gtk_tree_model_get_iter(model, &iter, path); gtk_tree_model_get(model, &iter, column, &fixed, ANNOTS_COLUMN, &annot, -1); fixed ^= 1; flags = poppler_annot_get_flags(annot); if (fixed) { flags |= flag_bit; } else { flags &= ~flag_bit; } poppler_annot_set_flags(annot, flags); gtk_list_store_set(GTK_LIST_STORE(model), &iter, column, fixed, -1); pgd_annot_view_set_annot(demo, annot); gtk_tree_path_free(path); pgd_annots_viewer_queue_redraw(demo); } static void pgd_annots_hidden_flag_toggled(GtkCellRendererToggle *renderer, gchar *path_str, PgdAnnotsDemo *demo) { pgd_annots_flags_toggled(renderer, path_str, demo, ANNOTS_FLAG_HIDDEN_COLUMN, POPPLER_ANNOT_FLAG_HIDDEN); } static void pgd_annots_print_flag_toggled(GtkCellRendererToggle *renderer, gchar *path_str, PgdAnnotsDemo *demo) { pgd_annots_flags_toggled(renderer, path_str, demo, ANNOTS_FLAG_PRINT_COLUMN, POPPLER_ANNOT_FLAG_PRINT); } static void pgd_annots_invisible_flag_toggled(GtkCellRendererToggle *renderer, gchar *path_str, PgdAnnotsDemo *demo) { pgd_annots_flags_toggled(renderer, path_str, demo, ANNOTS_FLAG_INVISIBLE_COLUMN, POPPLER_ANNOT_FLAG_INVISIBLE); } static inline void pgd_annots_set_poppler_quad_from_rectangle(PopplerQuadrilateral *quad, PopplerRectangle *rect) { quad->p1.x = rect->x1; quad->p1.y = rect->y1; quad->p2.x = rect->x2; quad->p2.y = rect->y1; quad->p3.x = rect->x1; quad->p3.y = rect->y2; quad->p4.x = rect->x2; quad->p4.y = rect->y2; } static GArray *pgd_annots_create_quads_array_for_rectangle(PopplerRectangle *rect) { GArray *quads_array; PopplerQuadrilateral *quad; quads_array = g_array_sized_new(FALSE, FALSE, sizeof(PopplerQuadrilateral), 1); g_array_set_size(quads_array, 1); quad = &g_array_index(quads_array, PopplerQuadrilateral, 0); pgd_annots_set_poppler_quad_from_rectangle(quad, rect); return quads_array; } static PopplerAnnotStampIcon get_icon_from_stamp_text(gchar *icon_text) { int i; for (i = 1; i < G_N_ELEMENTS(stamp_types) - 1; i++) { if (strcmp(stamp_types[i], icon_text) == 0) { return (PopplerAnnotStampIcon)i; } } return POPPLER_ANNOT_STAMP_ICON_UNKNOWN; } static void pgd_annots_add_annot(PgdAnnotsDemo *demo) { PopplerRectangle rect; PopplerColor color; PopplerAnnot *annot; gdouble height; g_assert(demo->mode == MODE_ADD); poppler_page_get_size(demo->page, NULL, &height); rect.x1 = demo->start.x; rect.y1 = height - demo->start.y; rect.x2 = demo->stop.x; rect.y2 = height - demo->stop.y; color.red = CLAMP((guint)(demo->annot_color.red * 65535), 0, 65535); color.green = CLAMP((guint)(demo->annot_color.green * 65535), 0, 65535); color.blue = CLAMP((guint)(demo->annot_color.blue * 65535), 0, 65535); switch (demo->annot_type) { case POPPLER_ANNOT_TEXT: annot = poppler_annot_text_new(demo->doc, &rect); break; case POPPLER_ANNOT_LINE: { PopplerPoint start, end; start.x = rect.x1; start.y = rect.y1; end.x = rect.x2; end.y = rect.y2; annot = poppler_annot_line_new(demo->doc, &rect, &start, &end); } break; case POPPLER_ANNOT_SQUARE: annot = poppler_annot_square_new(demo->doc, &rect); break; case POPPLER_ANNOT_CIRCLE: annot = poppler_annot_circle_new(demo->doc, &rect); break; case POPPLER_ANNOT_HIGHLIGHT: { GArray *quads_array; quads_array = pgd_annots_create_quads_array_for_rectangle(&rect); annot = poppler_annot_text_markup_new_highlight(demo->doc, &rect, quads_array); g_array_free(quads_array, TRUE); } break; case POPPLER_ANNOT_UNDERLINE: { GArray *quads_array; quads_array = pgd_annots_create_quads_array_for_rectangle(&rect); annot = poppler_annot_text_markup_new_underline(demo->doc, &rect, quads_array); g_array_free(quads_array, TRUE); } break; case POPPLER_ANNOT_SQUIGGLY: { GArray *quads_array; quads_array = pgd_annots_create_quads_array_for_rectangle(&rect); annot = poppler_annot_text_markup_new_squiggly(demo->doc, &rect, quads_array); g_array_free(quads_array, TRUE); } break; case POPPLER_ANNOT_STRIKE_OUT: { GArray *quads_array; quads_array = pgd_annots_create_quads_array_for_rectangle(&rect); annot = poppler_annot_text_markup_new_strikeout(demo->doc, &rect, quads_array); g_array_free(quads_array, TRUE); } break; case POPPLER_ANNOT_STAMP: { annot = poppler_annot_stamp_new(demo->doc, &rect); gchar *stamp_type = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(demo->stamp_selector)); GError *error = NULL; if (strcmp(stamp_type, STAMP_CUSTOM_IMAGE) == 0 && demo->custom_image_filename) { cairo_surface_t *img = cairo_image_surface_create_from_png(demo->custom_image_filename); if (cairo_surface_status(img) == CAIRO_STATUS_SUCCESS) { poppler_annot_stamp_set_custom_image(POPPLER_ANNOT_STAMP(annot), img, &error); if (error) { g_warning("%s", error->message); g_error_free(error); } } cairo_surface_destroy(img); } else { poppler_annot_stamp_set_icon(POPPLER_ANNOT_STAMP(annot), get_icon_from_stamp_text(stamp_type)); } g_free(stamp_type); } break; default: g_assert_not_reached(); } demo->active_annot = annot; if (demo->annot_type != POPPLER_ANNOT_STAMP) { poppler_annot_set_color(annot, &color); } poppler_page_add_annot(demo->page, annot); pgd_annots_add_annot_to_model(demo, annot, rect, TRUE); g_object_unref(annot); } static void pgd_annots_finish_add_annot(PgdAnnotsDemo *demo) { g_assert(demo->mode == MODE_ADD || demo->mode == MODE_DRAWING); demo->mode = MODE_NORMAL; demo->start.x = -1; pgd_annots_update_cursor(demo, GDK_LAST_CURSOR); pgd_annots_viewer_queue_redraw(demo); gtk_label_set_text(GTK_LABEL(demo->timer_label), NULL); } static void pgd_annots_update_selected_text(PgdAnnotsDemo *demo) { PopplerRectangle doc_area, *rects = NULL, *r = NULL; gdouble width, height; GArray *quads_array = NULL; guint n_rects; gint i, lines = 0; GList *l_rects = NULL, *list; poppler_page_get_size(demo->page, &width, &height); doc_area.x1 = demo->start.x; doc_area.y1 = demo->start.y; doc_area.x2 = demo->stop.x; doc_area.y2 = demo->stop.y; if (!poppler_page_get_text_layout_for_area(demo->page, &doc_area, &rects, &n_rects)) { return; } r = g_slice_new(PopplerRectangle); r->x1 = G_MAXDOUBLE; r->y1 = G_MAXDOUBLE; r->x2 = G_MINDOUBLE; r->y2 = G_MINDOUBLE; for (i = 0; i < n_rects; i++) { /* Check if the rectangle belongs to the same line. On a new line, start a new target rectangle. On the same line, make an union of rectangles at the same line */ if (ABS(r->y2 - rects[i].y2) > 0.0001) { if (i > 0) { l_rects = g_list_append(l_rects, r); } r = g_slice_new(PopplerRectangle); r->x1 = rects[i].x1; r->y1 = rects[i].y1; r->x2 = rects[i].x2; r->y2 = rects[i].y2; lines++; } else { r->x1 = MIN(r->x1, rects[i].x1); r->y1 = MIN(r->y1, rects[i].y1); r->x2 = MAX(r->x2, rects[i].x2); r->y2 = MAX(r->y2, rects[i].y2); } } l_rects = g_list_append(l_rects, r); l_rects = g_list_reverse(l_rects); quads_array = g_array_sized_new(TRUE, TRUE, sizeof(PopplerQuadrilateral), lines); g_array_set_size(quads_array, lines); for (list = l_rects, i = 0; list; list = list->next, i++) { PopplerQuadrilateral *quad; quad = &g_array_index(quads_array, PopplerQuadrilateral, i); r = (PopplerRectangle *)list->data; quad->p1.x = r->x1; quad->p1.y = height - r->y1; quad->p2.x = r->x2; quad->p2.y = height - r->y1; quad->p3.x = r->x1; quad->p3.y = height - r->y2; quad->p4.x = r->x2; quad->p4.y = height - r->y2; g_slice_free(PopplerRectangle, r); } poppler_annot_text_markup_set_quadrilaterals(POPPLER_ANNOT_TEXT_MARKUP(demo->active_annot), quads_array); g_array_free(quads_array, TRUE); g_free(rects); g_list_free(l_rects); } /* Render area */ static cairo_surface_t *pgd_annots_render_page(PgdAnnotsDemo *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); gtk_widget_set_size_request(demo->darea, width, height); surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height); cr = cairo_create(surface); 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 gboolean pgd_annots_view_drawing_area_draw(GtkWidget *area, cairo_t *cr, PgdAnnotsDemo *demo) { if (demo->num_page == -1) { return FALSE; } if (!demo->surface) { demo->surface = pgd_annots_render_page(demo); if (!demo->surface) { return FALSE; } } cairo_set_source_surface(cr, demo->surface, 0, 0); cairo_paint(cr); return TRUE; } static gboolean pgd_annots_viewer_redraw(PgdAnnotsDemo *demo) { cairo_surface_destroy(demo->surface); demo->surface = NULL; gtk_widget_queue_draw(demo->darea); demo->annotations_idle = 0; return FALSE; } static void pgd_annots_viewer_queue_redraw(PgdAnnotsDemo *demo) { if (demo->annotations_idle == 0) { demo->annotations_idle = g_idle_add((GSourceFunc)pgd_annots_viewer_redraw, demo); } } static void pgd_annots_drawing_area_realize(GtkWidget *area, PgdAnnotsDemo *demo) { gtk_widget_add_events(area, GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); } static gboolean pgd_annots_drawing_area_button_press(GtkWidget *area, GdkEventButton *event, PgdAnnotsDemo *demo) { if (!demo->page || demo->mode != MODE_ADD || event->button != 1) { return FALSE; } demo->start.x = event->x; demo->start.y = event->y; demo->stop = demo->start; pgd_annots_add_annot(demo); pgd_annots_viewer_queue_redraw(demo); demo->mode = MODE_DRAWING; return TRUE; } static void choose_custom_image(PgdAnnotsDemo *demo) { GtkFileChooser *chooser_dialog; gint response; const gchar *chooser_dir = "/usr/share/pixmaps"; const gchar *pics_dir; GtkFileFilter *filter; chooser_dialog = GTK_FILE_CHOOSER(gtk_file_chooser_dialog_new("Select PNG Image", NULL, GTK_FILE_CHOOSER_ACTION_OPEN, "gtk-cancel", GTK_RESPONSE_CANCEL, "gtk-open", GTK_RESPONSE_ACCEPT, NULL)); gtk_window_set_modal(GTK_WINDOW(chooser_dialog), TRUE); gtk_dialog_set_default_response(GTK_DIALOG(chooser_dialog), GTK_RESPONSE_ACCEPT); gtk_file_chooser_add_shortcut_folder(chooser_dialog, chooser_dir, NULL); pics_dir = g_get_user_special_dir(G_USER_DIRECTORY_PICTURES); if (pics_dir != NULL) { gtk_file_chooser_add_shortcut_folder(chooser_dialog, pics_dir, NULL); } if (!g_file_test(chooser_dir, G_FILE_TEST_IS_DIR)) { chooser_dir = g_get_home_dir(); } gtk_file_chooser_set_current_folder(chooser_dialog, chooser_dir); filter = gtk_file_filter_new(); gtk_file_filter_set_name(filter, "PNG images"); gtk_file_filter_add_mime_type(filter, "image/png"); gtk_file_chooser_add_filter(chooser_dialog, filter); filter = gtk_file_filter_new(); gtk_file_filter_set_name(filter, "All Files"); gtk_file_filter_add_pattern(filter, "*"); gtk_file_chooser_add_filter(chooser_dialog, filter); response = gtk_dialog_run(GTK_DIALOG(chooser_dialog)); if (response == GTK_RESPONSE_ACCEPT) { if (demo->custom_image_filename) { g_free(demo->custom_image_filename); } demo->custom_image_filename = gtk_file_chooser_get_filename(chooser_dialog); } else { if (demo->custom_image_filename) { g_free(demo->custom_image_filename); demo->custom_image_filename = NULL; } } gtk_widget_destroy(GTK_WIDGET(chooser_dialog)); } static void stamp_selector_changed(GtkComboBox *combo_box, PgdAnnotsDemo *demo) { GtkTreeModel *model; GtkTreeIter iter; gchar *active; model = gtk_combo_box_get_model(combo_box); gtk_combo_box_get_active_iter(combo_box, &iter); gtk_tree_model_get(model, &iter, 0, &active, -1); if (strcmp(active, STAMP_CUSTOM_IMAGE) == 0) { choose_custom_image(demo); } } static void type_selector_changed(GtkComboBox *combo_box, PgdAnnotsDemo *demo) { GtkTreeModel *model; GtkTreeIter iter; int active; model = gtk_combo_box_get_model(combo_box); gtk_combo_box_get_active_iter(combo_box, &iter); gtk_tree_model_get(model, &iter, SELECTED_TYPE_COLUMN, &active, -1); gtk_widget_set_sensitive(demo->stamp_selector, active == POPPLER_ANNOT_STAMP); } static gboolean pgd_annots_drawing_area_motion_notify(GtkWidget *area, GdkEventMotion *event, PgdAnnotsDemo *demo) { PopplerRectangle rect; PopplerPoint start, end; gdouble width, height; if (!demo->page || demo->mode != MODE_DRAWING || demo->start.x == -1) { return FALSE; } demo->stop.x = event->x; demo->stop.y = event->y; poppler_page_get_size(demo->page, &width, &height); /* Keep the drawing within the page */ demo->stop.x = CLAMP(demo->stop.x, 0, width); demo->stop.y = CLAMP(demo->stop.y, 0, height); rect.x1 = start.x = demo->start.x; rect.y1 = start.y = height - demo->start.y; rect.x2 = end.x = demo->stop.x; rect.y2 = end.y = height - demo->stop.y; poppler_annot_set_rectangle(demo->active_annot, &rect); if (demo->annot_type == POPPLER_ANNOT_LINE) { poppler_annot_line_set_vertices(POPPLER_ANNOT_LINE(demo->active_annot), &start, &end); } if (POPPLER_IS_ANNOT_TEXT_MARKUP(demo->active_annot)) { pgd_annots_update_selected_text(demo); } pgd_annot_view_set_annot(demo, demo->active_annot); pgd_annots_viewer_queue_redraw(demo); return TRUE; } static gboolean pgd_annots_drawing_area_button_release(GtkWidget *area, GdkEventButton *event, PgdAnnotsDemo *demo) { if (!demo->page || demo->mode != MODE_DRAWING || event->button != 1) { return FALSE; } pgd_annots_finish_add_annot(demo); return TRUE; } /* Main UI */ GtkWidget *pgd_annots_create_widget(PopplerDocument *document) { PgdAnnotsDemo *demo; GtkWidget *label; GtkWidget *vbox, *vbox2; GtkWidget *button; GtkWidget *hbox, *hbox2, *page_selector; GtkWidget *hpaned; GtkWidget *swindow, *treeview; GtkTreeSelection *selection; GtkCellRenderer *renderer; GtkTreeViewColumn *column; GtkListStore *model; GtkTreeIter iter; gchar *str; gint n_pages; gint i; demo = g_new0(PgdAnnotsDemo, 1); demo->doc = g_object_ref(document); demo->cursor = GDK_LAST_CURSOR; demo->mode = MODE_NORMAL; n_pages = poppler_document_get_n_pages(document); vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12); vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6); hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); hbox2 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12); 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_annots_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); demo->remove_button = gtk_button_new_with_mnemonic("_Remove"); gtk_widget_set_sensitive(demo->remove_button, FALSE); g_signal_connect(G_OBJECT(demo->remove_button), "clicked", G_CALLBACK(pgd_annots_remove_annot), (gpointer)demo); gtk_box_pack_end(GTK_BOX(hbox), demo->remove_button, FALSE, FALSE, 6); gtk_widget_show(demo->remove_button); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), hbox2, FALSE, TRUE, 0); button = gtk_button_new_with_mnemonic("_Add"); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(pgd_annots_start_add_annot), (gpointer)demo); gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0); gtk_widget_show(button); model = gtk_list_store_new(SELECTED_N_COLUMNS, G_TYPE_INT, G_TYPE_STRING); for (i = 0; i < G_N_ELEMENTS(supported_annots); i++) { gtk_list_store_append(model, &iter); gtk_list_store_set(model, &iter, SELECTED_TYPE_COLUMN, supported_annots[i].type, SELECTED_LABEL_COLUMN, supported_annots[i].label, -1); } demo->type_selector = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model)); g_signal_connect(demo->type_selector, "changed", G_CALLBACK(type_selector_changed), (gpointer)demo); g_object_unref(model); demo->stamp_selector = gtk_combo_box_text_new(); for (i = 1; i < G_N_ELEMENTS(stamp_types) - 1; i++) { gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(demo->stamp_selector), stamp_types[i]); } gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(demo->stamp_selector), STAMP_CUSTOM_IMAGE); gtk_combo_box_set_active(GTK_COMBO_BOX(demo->stamp_selector), 0); g_signal_connect(demo->stamp_selector, "changed", G_CALLBACK(stamp_selector_changed), (gpointer)demo); gtk_widget_set_sensitive(demo->stamp_selector, FALSE); label = gtk_label_new("Stamp type: "); gtk_widget_set_sensitive(label, FALSE); g_object_bind_property(demo->stamp_selector, "sensitive", label, "sensitive", 0); gtk_box_pack_end(GTK_BOX(hbox2), demo->stamp_selector, FALSE, FALSE, 0); gtk_box_pack_end(GTK_BOX(hbox2), label, FALSE, TRUE, 0); gtk_widget_show_all(hbox2); renderer = gtk_cell_renderer_text_new(); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(demo->type_selector), renderer, TRUE); gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(demo->type_selector), renderer, "text", SELECTED_LABEL_COLUMN, NULL); gtk_combo_box_set_active(GTK_COMBO_BOX(demo->type_selector), 0); gtk_box_pack_end(GTK_BOX(hbox), demo->type_selector, FALSE, FALSE, 0); gtk_widget_show(demo->type_selector); button = gtk_color_button_new(); demo->annot_color.red = 65535; demo->annot_color.alpha = 1.0; gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(button), &demo->annot_color); g_signal_connect(button, "notify::color", G_CALLBACK(pgd_annot_color_changed), (gpointer)demo); gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, TRUE, 0); gtk_widget_show(button); gtk_widget_show(hbox); gtk_widget_show(hbox2); demo->timer_label = gtk_label_new(NULL); gtk_label_set_markup(GTK_LABEL(demo->timer_label), "No annotations found"); g_object_set(G_OBJECT(demo->timer_label), "xalign", 1.0, NULL); gtk_box_pack_start(GTK_BOX(vbox), demo->timer_label, FALSE, TRUE, 0); gtk_widget_show(demo->timer_label); hpaned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); demo->annot_view = pgd_annot_view_new(); swindow = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); demo->model = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_OBJECT); treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(demo->model)); demo->tree_view = treeview; column = gtk_tree_view_column_new(); gtk_tree_view_column_set_title(column, "Type"); gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); renderer = gtk_cell_renderer_pixbuf_new(); gtk_tree_view_column_pack_start(column, renderer, TRUE); gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", ANNOTS_COLOR_COLUMN); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_column_pack_start(column, renderer, TRUE); gtk_tree_view_column_add_attribute(column, renderer, "text", ANNOTS_TYPE_COLUMN); renderer = gtk_cell_renderer_toggle_new(); g_signal_connect(renderer, "toggled", G_CALLBACK(pgd_annots_invisible_flag_toggled), (gpointer)demo); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), ANNOTS_FLAG_INVISIBLE_COLUMN, "Invisible", renderer, "active", ANNOTS_FLAG_INVISIBLE_COLUMN, NULL); renderer = gtk_cell_renderer_toggle_new(); g_signal_connect(renderer, "toggled", G_CALLBACK(pgd_annots_hidden_flag_toggled), (gpointer)demo); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), ANNOTS_FLAG_HIDDEN_COLUMN, "Hidden", renderer, "active", ANNOTS_FLAG_HIDDEN_COLUMN, NULL); renderer = gtk_cell_renderer_toggle_new(); g_signal_connect(renderer, "toggled", G_CALLBACK(pgd_annots_print_flag_toggled), (gpointer)demo); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), ANNOTS_FLAG_PRINT_COLUMN, "Print", renderer, "active", ANNOTS_FLAG_PRINT_COLUMN, NULL); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(pgd_annots_selection_changed), (gpointer)demo); /* Annotation's list */ gtk_container_add(GTK_CONTAINER(swindow), treeview); gtk_widget_show(treeview); gtk_box_pack_start(GTK_BOX(vbox2), swindow, TRUE, TRUE, 0); gtk_widget_show(swindow); /* Annotation Properties */ swindow = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_container_add(GTK_CONTAINER(swindow), demo->annot_view); gtk_widget_show(demo->annot_view); gtk_widget_show(swindow); gtk_box_pack_start(GTK_BOX(vbox2), swindow, TRUE, TRUE, 6); gtk_widget_show(swindow); gtk_paned_add1(GTK_PANED(hpaned), vbox2); gtk_widget_show(vbox2); /* Demo Area (Render) */ demo->darea = gtk_drawing_area_new(); g_signal_connect(demo->darea, "draw", G_CALLBACK(pgd_annots_view_drawing_area_draw), demo); g_signal_connect(demo->darea, "realize", G_CALLBACK(pgd_annots_drawing_area_realize), (gpointer)demo); g_signal_connect(demo->darea, "button_press_event", G_CALLBACK(pgd_annots_drawing_area_button_press), (gpointer)demo); g_signal_connect(demo->darea, "motion_notify_event", G_CALLBACK(pgd_annots_drawing_area_motion_notify), (gpointer)demo); 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); gtk_widget_show(swindow); gtk_paned_set_position(GTK_PANED(hpaned), 300); gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0); gtk_widget_show(hpaned); g_object_weak_ref(G_OBJECT(vbox), (GWeakNotify)pgd_annots_free, demo); pgd_annots_viewer_queue_redraw(demo); pgd_annots_get_annots(demo); demo->main_box = vbox; return vbox; } poppler-24.02.0/glib/demo/annots.h000066400000000000000000000017251455701731300166670ustar00rootroot00000000000000/* * Copyright (C) 2008 Inigo Martinez * * 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 _ANNOTS_H_ # define _ANNOTS_H_ G_BEGIN_DECLS GtkWidget *pgd_annots_create_widget(PopplerDocument *document); G_END_DECLS #endif /* _ANNOTS_H_ */ poppler-24.02.0/glib/demo/attachments.c000066400000000000000000000244131455701731300176720ustar00rootroot00000000000000/* * Copyright (C) 2008 Carlos Garcia Campos * * 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 "attachments.h" #include "utils.h" enum { ATTACHMENTS_NAME_COLUMN, ATTACHMENTS_DESCRIPTION_COLUMN, ATTACHMENTS_SIZE_COLUMN, ATTACHMENTS_CTIME_COLUMN, ATTACHMENTS_MTIME_COLUMN, ATTACHMENTS_ATTACHMENT_COLUMN, N_COLUMNS }; static void pgd_attachments_fill_model(GtkListStore *model, PopplerDocument *document) { GList *list, *l; list = poppler_document_get_attachments(document); for (l = list; l && l->data; l = g_list_next(l)) { PopplerAttachment *attachment = POPPLER_ATTACHMENT(l->data); GtkTreeIter iter; gchar *size; GDateTime *ctime, *mtime; gchar *ctime_str, *mtime_str; const gchar *name; const gchar *description; name = poppler_attachment_get_name(attachment); description = poppler_attachment_get_description(attachment); size = g_strdup_printf("%" G_GSIZE_FORMAT, poppler_attachment_get_size(attachment)); ctime = poppler_attachment_get_ctime(attachment); ctime_str = ctime ? g_date_time_format(ctime, "%c") : NULL; mtime = poppler_attachment_get_mtime(attachment); mtime_str = mtime ? g_date_time_format(mtime, "%c") : NULL; gtk_list_store_append(model, &iter); gtk_list_store_set(model, &iter, ATTACHMENTS_NAME_COLUMN, name ? name : "Unknown", ATTACHMENTS_DESCRIPTION_COLUMN, description ? description : "Unknown", ATTACHMENTS_SIZE_COLUMN, size ? size : "Unknown", ATTACHMENTS_CTIME_COLUMN, ctime_str ? ctime_str : "Unknown", ATTACHMENTS_MTIME_COLUMN, mtime_str ? mtime_str : "Unknown", ATTACHMENTS_ATTACHMENT_COLUMN, attachment, -1); g_free(size); g_free(ctime_str); g_free(mtime_str); g_object_unref(attachment); } g_list_free(list); } static GtkWidget *pgd_attachments_create_list(GtkTreeModel *model) { GtkWidget *treeview; GtkCellRenderer *renderer; treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), TRUE); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 0, "Name", renderer, "text", ATTACHMENTS_NAME_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 1, "Description", renderer, "text", ATTACHMENTS_DESCRIPTION_COLUMN, NULL); g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL); g_object_set(G_OBJECT(gtk_tree_view_get_column(GTK_TREE_VIEW(treeview), 1)), "expand", TRUE, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 2, "Size", renderer, "text", ATTACHMENTS_SIZE_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 3, "Creation Date", renderer, "text", ATTACHMENTS_CTIME_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 4, "Modification Date", renderer, "text", ATTACHMENTS_MTIME_COLUMN, NULL); return treeview; } static void pgd_attachments_save_dialog_response(GtkFileChooser *file_chooser, gint response, PopplerAttachment *attachment) { gchar *filename; GError *error = NULL; if (response != GTK_RESPONSE_ACCEPT) { g_object_unref(attachment); gtk_widget_destroy(GTK_WIDGET(file_chooser)); return; } filename = gtk_file_chooser_get_filename(file_chooser); if (!poppler_attachment_save(attachment, filename, &error)) { g_warning("%s", error->message); g_error_free(error); } g_free(filename); g_object_unref(attachment); gtk_widget_destroy(GTK_WIDGET(file_chooser)); } static void pgd_attachments_save_button_clicked(GtkButton *button, GtkTreeView *treeview) { GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter; GtkWidget *file_chooser; PopplerAttachment *attachment; selection = gtk_tree_view_get_selection(treeview); if (!gtk_tree_selection_get_selected(selection, &model, &iter)) { return; } gtk_tree_model_get(model, &iter, ATTACHMENTS_ATTACHMENT_COLUMN, &attachment, -1); if (!attachment) { return; } file_chooser = gtk_file_chooser_dialog_new("Save attachment", GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(treeview))), GTK_FILE_CHOOSER_ACTION_SAVE, "_Cancel", GTK_RESPONSE_CANCEL, "_Save", GTK_RESPONSE_ACCEPT, NULL); gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(file_chooser), poppler_attachment_get_name(attachment)); g_signal_connect(G_OBJECT(file_chooser), "response", G_CALLBACK(pgd_attachments_save_dialog_response), (gpointer)attachment); gtk_widget_show(file_chooser); } static gboolean attachment_save_callback(const gchar *buf, gsize count, gpointer data, GError **error) { GChecksum *cs = (GChecksum *)data; g_checksum_update(cs, (guchar *)buf, count); return TRUE; } static void message_dialog_run(GtkWindow *parent, const gchar *message) { GtkWidget *dialog; dialog = gtk_message_dialog_new(parent, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "%s", message); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); } static void pgd_attachments_validate_button_clicked(GtkButton *button, GtkTreeView *treeview) { GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter; const GString *checksum; GChecksum *cs; guint8 *digest; gsize digest_len; PopplerAttachment *attachment; gboolean valid = TRUE; selection = gtk_tree_view_get_selection(treeview); if (!gtk_tree_selection_get_selected(selection, &model, &iter)) { return; } gtk_tree_model_get(model, &iter, ATTACHMENTS_ATTACHMENT_COLUMN, &attachment, -1); if (!attachment) { return; } checksum = poppler_attachment_get_checksum(attachment); if (checksum->len == 0) { message_dialog_run(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(treeview))), "Impossible to validate attachment: checksum is not available"); g_object_unref(attachment); return; } cs = g_checksum_new(G_CHECKSUM_MD5); poppler_attachment_save_to_callback(attachment, attachment_save_callback, (gpointer)cs, NULL); digest_len = g_checksum_type_get_length(G_CHECKSUM_MD5); digest = (guint8 *)g_malloc(digest_len); g_checksum_get_digest(cs, digest, &digest_len); g_checksum_free(cs); if (checksum->len == digest_len) { gint i; for (i = 0; i < digest_len; i++) { if ((guint8)checksum->str[i] != digest[i]) { valid = FALSE; break; } } } if (valid) { message_dialog_run(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(treeview))), "Attachment is valid"); } else { message_dialog_run(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(treeview))), "Attachment is not valid: the checksum does not match"); } g_free(digest); g_object_unref(attachment); } GtkWidget *pgd_attachments_create_widget(PopplerDocument *document) { GtkWidget *vbox; GtkWidget *treeview; GtkListStore *model; GtkWidget *swindow; GtkWidget *hbox, *button; gboolean has_attachments; vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12); swindow = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); has_attachments = poppler_document_has_attachments(document); if (has_attachments) { model = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_OBJECT); pgd_attachments_fill_model(model, document); treeview = pgd_attachments_create_list(GTK_TREE_MODEL(model)); } else { GtkCellRenderer *renderer; GtkTreeIter iter; gchar *markup; model = gtk_list_store_new(1, G_TYPE_STRING); gtk_list_store_append(model, &iter); markup = g_strdup_printf("%s", "The document doesn't contain attachments"); gtk_list_store_set(model, &iter, 0, markup, -1); g_free(markup); treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 0, "Name", renderer, "markup", 0, NULL); } g_object_unref(model); gtk_container_add(GTK_CONTAINER(swindow), treeview); gtk_widget_show(treeview); gtk_box_pack_start(GTK_BOX(vbox), swindow, TRUE, TRUE, 0); gtk_widget_show(swindow); if (!has_attachments) { return vbox; } hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL); gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_SPREAD); button = gtk_button_new_with_label("Save"); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(pgd_attachments_save_button_clicked), (gpointer)treeview); gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); gtk_widget_show(button); button = gtk_button_new_with_label("Validate"); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(pgd_attachments_validate_button_clicked), (gpointer)treeview); gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); gtk_widget_show(button); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 6); gtk_widget_show(hbox); return vbox; } poppler-24.02.0/glib/demo/attachments.h000066400000000000000000000017531455701731300177010ustar00rootroot00000000000000/* * Copyright (C) 2008 Carlos Garcia Campos * * 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 _ATTACHMENTS_H_ # define _ATTACHMENTS_H_ G_BEGIN_DECLS GtkWidget *pgd_attachments_create_widget(PopplerDocument *document); G_END_DECLS #endif /* _ATTACHMENTS_H_ */ poppler-24.02.0/glib/demo/find.c000066400000000000000000000366461455701731300163120ustar00rootroot00000000000000/* * Copyright (C) 2008 Carlos Garcia Campos * * 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 "find.h" enum { TITLE_COLUMN, X1_COLUMN, Y1_COLUMN, X2_COLUMN, Y2_COLUMN, VISIBLE_COLUMN, PAGE_COLUMN, PAGE_RECT, N_COLUMNS }; typedef struct { PopplerDocument *doc; GtkWidget *treeview; GtkWidget *darea; GtkWidget *entry; GtkWidget *progress; PopplerFindFlags options; gint n_pages; gint page_index; guint idle_id; cairo_surface_t *surface; gint selected_page; GdkRectangle selected_match; } PgdFindDemo; static void pgd_find_free(PgdFindDemo *demo) { if (!demo) { return; } if (demo->idle_id > 0) { g_source_remove(demo->idle_id); demo->idle_id = 0; } if (demo->doc) { g_object_unref(demo->doc); demo->doc = NULL; } if (demo->surface) { cairo_surface_destroy(demo->surface); demo->surface = NULL; } g_free(demo); } static void pgd_find_update_progress(PgdFindDemo *demo, gint scanned) { gchar *str; str = g_strdup_printf("Searching ... (%d%%)", MIN(scanned * 100 / demo->n_pages, 100)); gtk_progress_bar_set_text(GTK_PROGRESS_BAR(demo->progress), str); gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(demo->progress), MIN((gdouble)scanned / demo->n_pages, 1.0)); g_free(str); } static void pgd_find_append_match(PgdFindDemo *demo, GtkTreeModel *model, GtkTreeIter *iter_child, PopplerRectangle *rect, int match_id) { char *x1, *y1, *x2, *y2, *str; str = g_strdup_printf("Match %d", match_id + 1); x1 = g_strdup_printf("%.2f", rect->x1); y1 = g_strdup_printf("%.2f", rect->y1); x2 = g_strdup_printf("%.2f", rect->x2); y2 = g_strdup_printf("%.2f", rect->y2); gtk_tree_store_set(GTK_TREE_STORE(model), iter_child, TITLE_COLUMN, str, X1_COLUMN, x1, Y1_COLUMN, y1, X2_COLUMN, x2, Y2_COLUMN, y2, VISIBLE_COLUMN, TRUE, PAGE_COLUMN, demo->page_index, PAGE_RECT, rect, -1); g_free(str); g_free(x1); g_free(y1); g_free(x2); g_free(y2); g_object_weak_ref(G_OBJECT(model), (GWeakNotify)poppler_rectangle_free, rect); } static gboolean pgd_find_find_text(PgdFindDemo *demo) { PopplerPage *page; GList *matches; GTimer *timer; GtkTreeModel *model; page = poppler_document_get_page(demo->doc, demo->page_index); if (!page) { demo->page_index++; return demo->page_index < demo->n_pages; } model = gtk_tree_view_get_model(GTK_TREE_VIEW(demo->treeview)); timer = g_timer_new(); matches = poppler_page_find_text_with_options(page, gtk_entry_get_text(GTK_ENTRY(demo->entry)), demo->options); g_timer_stop(timer); if (matches) { GtkTreeIter iter, iter_child; gchar *str; GList *l; gdouble height; gint n_match = 0; gtk_tree_store_append(GTK_TREE_STORE(model), &iter, NULL); poppler_page_get_size(page, NULL, &height); for (l = matches; l && l->data; l = g_list_next(l)) { PopplerRectangle *rect = (PopplerRectangle *)l->data; gdouble tmp; tmp = rect->y1; rect->y1 = height - rect->y2; rect->y2 = height - tmp; gtk_tree_store_append(GTK_TREE_STORE(model), &iter_child, &iter); pgd_find_append_match(demo, model, &iter_child, rect, n_match); if (!poppler_rectangle_find_get_match_continued(rect)) { ++n_match; } } g_list_free(matches); str = g_strdup_printf("%d matches found on page %d in %.4f seconds", n_match, demo->page_index + 1, g_timer_elapsed(timer, NULL)); gtk_tree_store_set(GTK_TREE_STORE(model), &iter, TITLE_COLUMN, str, VISIBLE_COLUMN, FALSE, PAGE_COLUMN, demo->page_index, -1); g_free(str); } g_timer_destroy(timer); g_object_unref(page); demo->page_index++; pgd_find_update_progress(demo, demo->page_index); return demo->page_index < demo->n_pages; } static void find_text_idle_finish(PgdFindDemo *demo) { demo->idle_id = 0; } static cairo_surface_t *pgd_find_render_page(PgdFindDemo *demo) { cairo_t *cr; PopplerPage *page; gdouble width, height; cairo_surface_t *surface = NULL; page = poppler_document_get_page(demo->doc, demo->selected_page); if (!page) { return NULL; } poppler_page_get_size(page, &width, &height); gtk_widget_set_size_request(demo->darea, width, height); surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height); cr = cairo_create(surface); 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 gboolean pgd_find_viewer_drawing_area_draw(GtkWidget *area, cairo_t *cr, PgdFindDemo *demo) { if (demo->selected_page == -1) { return FALSE; } if (!demo->surface) { demo->surface = pgd_find_render_page(demo); if (!demo->surface) { return FALSE; } } cairo_set_source_surface(cr, demo->surface, 0, 0); cairo_paint(cr); if (demo->selected_match.width > 0 && demo->selected_match.height > 0) { cairo_set_source_rgb(cr, 1., 1., 0.); cairo_set_operator(cr, CAIRO_OPERATOR_MULTIPLY); gdk_cairo_rectangle(cr, &demo->selected_match); cairo_fill(cr); } return TRUE; } static gboolean pgd_find_viewer_redraw(PgdFindDemo *demo) { cairo_surface_destroy(demo->surface); demo->surface = NULL; gtk_widget_queue_draw(demo->darea); return FALSE; } static void pgd_find_viewer_queue_redraw(PgdFindDemo *demo) { g_idle_add((GSourceFunc)pgd_find_viewer_redraw, demo); } static GtkTreeModel *pgd_find_create_model() { return GTK_TREE_MODEL(gtk_tree_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_UINT, G_TYPE_POINTER)); } static void pgd_find_button_clicked(GtkButton *button, PgdFindDemo *demo) { GtkTreeModel *model; /* Delete the model and create a new one instead of * just clearing it to make sure rectangle are free. * This is a workaround because GtkTreeModel doesn't * support boxed types and we have to store rectangles * as pointers that are freed when the model is deleted. */ model = pgd_find_create_model(); gtk_tree_view_set_model(GTK_TREE_VIEW(demo->treeview), model); g_object_unref(model); demo->selected_page = -1; pgd_find_viewer_queue_redraw(demo); demo->page_index = 0; pgd_find_update_progress(demo, demo->page_index); if (demo->idle_id > 0) { g_source_remove(demo->idle_id); } demo->idle_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, (GSourceFunc)pgd_find_find_text, demo, (GDestroyNotify)find_text_idle_finish); } static void pgd_find_button_sensitivity_cb(GtkWidget *button, GtkEntry *entry) { const gchar *text; text = gtk_entry_get_text(entry); gtk_widget_set_sensitive(button, text != NULL && text[0] != '\0'); } static void pgd_find_selection_changed(GtkTreeSelection *treeselection, PgdFindDemo *demo) { GtkTreeModel *model; GtkTreeIter iter; if (gtk_tree_selection_get_selected(treeselection, &model, &iter)) { guint page_index; PopplerRectangle *rect; gtk_tree_model_get(model, &iter, PAGE_COLUMN, &page_index, PAGE_RECT, &rect, -1); if (rect) { demo->selected_match.x = rect->x1; demo->selected_match.y = rect->y1; demo->selected_match.width = rect->x2 - rect->x1; demo->selected_match.height = rect->y2 - rect->y1; } else { demo->selected_match.width = 0; demo->selected_match.height = 0; } if (page_index != demo->selected_page) { demo->selected_page = page_index; pgd_find_viewer_queue_redraw(demo); } else { gtk_widget_queue_draw(demo->darea); } } } static void pgd_find_case_sensitive_toggled(GtkToggleButton *togglebutton, PgdFindDemo *demo) { if (gtk_toggle_button_get_active(togglebutton)) { demo->options |= POPPLER_FIND_CASE_SENSITIVE; } else { demo->options &= ~POPPLER_FIND_CASE_SENSITIVE; } } static void pgd_find_backwards_toggled(GtkToggleButton *togglebutton, PgdFindDemo *demo) { if (gtk_toggle_button_get_active(togglebutton)) { demo->options |= POPPLER_FIND_BACKWARDS; } else { demo->options &= ~POPPLER_FIND_BACKWARDS; } } static void pgd_find_multiline_toggled(GtkToggleButton *togglebutton, PgdFindDemo *demo) { if (gtk_toggle_button_get_active(togglebutton)) { demo->options |= POPPLER_FIND_MULTILINE; } else { demo->options &= ~POPPLER_FIND_MULTILINE; } } static void pgd_find_ignore_diacritics_toggled(GtkToggleButton *togglebutton, PgdFindDemo *demo) { if (gtk_toggle_button_get_active(togglebutton)) { demo->options |= POPPLER_FIND_IGNORE_DIACRITICS; } else { demo->options &= ~POPPLER_FIND_IGNORE_DIACRITICS; } } static void pgd_find_whole_words_toggled(GtkToggleButton *togglebutton, PgdFindDemo *demo) { if (gtk_toggle_button_get_active(togglebutton)) { demo->options |= POPPLER_FIND_WHOLE_WORDS_ONLY; } else { demo->options &= ~POPPLER_FIND_WHOLE_WORDS_ONLY; } } GtkWidget *pgd_find_create_widget(PopplerDocument *document) { PgdFindDemo *demo; GtkWidget *vbox, *hbox; GtkWidget *button; GtkWidget *swindow; GtkWidget *checkbutton; GtkTreeModel *model; GtkWidget *treeview; GtkCellRenderer *renderer; GtkWidget *hpaned; GtkTreeSelection *selection; demo = g_new0(PgdFindDemo, 1); demo->doc = g_object_ref(document); demo->n_pages = poppler_document_get_n_pages(document); demo->selected_page = -1; demo->options = POPPLER_FIND_DEFAULT; hpaned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); gtk_paned_set_position(GTK_PANED(hpaned), 300); vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12); hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); checkbutton = gtk_check_button_new_with_label("Multi-line"); g_signal_connect(checkbutton, "toggled", G_CALLBACK(pgd_find_multiline_toggled), demo); gtk_box_pack_start(GTK_BOX(hbox), checkbutton, FALSE, FALSE, 0); gtk_widget_show(checkbutton); checkbutton = gtk_check_button_new_with_label("Ignore diacritics"); g_signal_connect(checkbutton, "toggled", G_CALLBACK(pgd_find_ignore_diacritics_toggled), demo); gtk_box_pack_start(GTK_BOX(hbox), checkbutton, FALSE, FALSE, 0); gtk_widget_show(checkbutton); demo->entry = gtk_entry_new(); gtk_box_pack_start(GTK_BOX(hbox), demo->entry, FALSE, TRUE, 0); gtk_widget_show(demo->entry); demo->progress = gtk_progress_bar_new(); gtk_progress_bar_set_ellipsize(GTK_PROGRESS_BAR(demo->progress), PANGO_ELLIPSIZE_END); gtk_box_pack_start(GTK_BOX(hbox), demo->progress, TRUE, TRUE, 0); gtk_widget_show(demo->progress); button = gtk_button_new_with_label("Find"); gtk_widget_set_sensitive(button, FALSE); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(pgd_find_button_clicked), (gpointer)demo); g_signal_connect_swapped(G_OBJECT(demo->entry), "changed", G_CALLBACK(pgd_find_button_sensitivity_cb), (gpointer)button); gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0); gtk_widget_show(button); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 6); gtk_widget_show(hbox); hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); checkbutton = gtk_check_button_new_with_label("Case sensitive"); g_signal_connect(checkbutton, "toggled", G_CALLBACK(pgd_find_case_sensitive_toggled), demo); gtk_box_pack_start(GTK_BOX(hbox), checkbutton, FALSE, FALSE, 0); gtk_widget_show(checkbutton); checkbutton = gtk_check_button_new_with_label("Backwards"); g_signal_connect(checkbutton, "toggled", G_CALLBACK(pgd_find_backwards_toggled), demo); gtk_box_pack_start(GTK_BOX(hbox), checkbutton, FALSE, FALSE, 0); gtk_widget_show(checkbutton); checkbutton = gtk_check_button_new_with_label("Whole words only"); g_signal_connect(checkbutton, "toggled", G_CALLBACK(pgd_find_whole_words_toggled), demo); gtk_box_pack_start(GTK_BOX(hbox), checkbutton, FALSE, FALSE, 0); gtk_widget_show(checkbutton); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); gtk_widget_show(hbox); swindow = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); model = pgd_find_create_model(); treeview = gtk_tree_view_new_with_model(model); g_object_unref(model); demo->treeview = treeview; selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE); g_signal_connect(selection, "changed", G_CALLBACK(pgd_find_selection_changed), demo); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), TITLE_COLUMN, "Matches", renderer, "text", TITLE_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), X1_COLUMN, "X1", renderer, "text", X1_COLUMN, "visible", VISIBLE_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), Y1_COLUMN, "Y1", renderer, "text", Y1_COLUMN, "visible", VISIBLE_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), X2_COLUMN, "X2", renderer, "text", X2_COLUMN, "visible", VISIBLE_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), Y2_COLUMN, "Y2", renderer, "text", Y2_COLUMN, "visible", VISIBLE_COLUMN, NULL); gtk_container_add(GTK_CONTAINER(swindow), treeview); gtk_widget_show(treeview); gtk_paned_add1(GTK_PANED(hpaned), swindow); gtk_widget_show(swindow); demo->darea = gtk_drawing_area_new(); 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); gtk_widget_show(swindow); gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0); gtk_widget_show(hpaned); g_object_weak_ref(G_OBJECT(vbox), (GWeakNotify)pgd_find_free, (gpointer)demo); return vbox; } poppler-24.02.0/glib/demo/find.h000066400000000000000000000017171455701731300163060ustar00rootroot00000000000000/* * Copyright (C) 2008 Carlos Garcia Campos * * 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 _FIND_H_ # define _FIND_H_ G_BEGIN_DECLS GtkWidget *pgd_find_create_widget(PopplerDocument *document); G_END_DECLS #endif /* _FIND_H_ */ poppler-24.02.0/glib/demo/fonts.c000066400000000000000000000174031455701731300165110ustar00rootroot00000000000000/* * Copyright (C) 2007 Carlos Garcia Campos * * 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 "fonts.h" enum { FONTS_NAME_COLUMN, FONTS_DETAILS_COLUMN, N_COLUMNS }; typedef struct { PopplerDocument *doc; GtkWidget *treeview; GtkWidget *progress; guint idle_id; } PgdFontsDemo; static void pgd_fonts_free(PgdFontsDemo *demo) { if (!demo) { return; } if (demo->idle_id > 0) { g_source_remove(demo->idle_id); demo->idle_id = 0; } if (demo->doc) { g_object_unref(demo->doc); demo->doc = NULL; } g_free(demo); } static void pdg_fonts_cell_data_func(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data) { char *name; char *details; char *markup; gtk_tree_model_get(model, iter, FONTS_NAME_COLUMN, &name, FONTS_DETAILS_COLUMN, &details, -1); if (details) { markup = g_strdup_printf("%s\n%s", name, details); } else { markup = g_strdup_printf("%s", name); } g_object_set(renderer, "markup", markup, NULL); g_free(markup); g_free(details); g_free(name); } static const gchar *font_type_to_string(PopplerFontType type) { switch (type) { case POPPLER_FONT_TYPE_TYPE1: return "Type 1"; case POPPLER_FONT_TYPE_TYPE1C: return "Type 1C"; case POPPLER_FONT_TYPE_TYPE3: return "Type 3"; case POPPLER_FONT_TYPE_TRUETYPE: return "TrueType"; case POPPLER_FONT_TYPE_CID_TYPE0: return "Type 1 (CID)"; case POPPLER_FONT_TYPE_CID_TYPE0C: return "Type 1C (CID)"; case POPPLER_FONT_TYPE_CID_TYPE2: return "TrueType (CID)"; default: return "Unknown font type"; } } static void pgd_fonts_update_progress(PgdFontsDemo *demo, gint n_pages, gint scanned) { gchar *str; str = g_strdup_printf("Scanning fonts (%d%%)", MIN(scanned * 100 / n_pages, 100)); gtk_progress_bar_set_text(GTK_PROGRESS_BAR(demo->progress), str); gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(demo->progress), MIN((gdouble)scanned / n_pages, 1.0)); g_free(str); } static gboolean pgd_fonts_fill_model(PgdFontsDemo *demo) { GtkTreeModel *model; PopplerFontInfo *font_info; PopplerFontsIter *fonts_iter; gint n_pages, scanned = 0; n_pages = poppler_document_get_n_pages(demo->doc); model = gtk_tree_view_get_model(GTK_TREE_VIEW(demo->treeview)); g_object_ref(model); gtk_list_store_clear(GTK_LIST_STORE(model)); font_info = poppler_font_info_new(demo->doc); while (poppler_font_info_scan(font_info, 20, &fonts_iter)) { pgd_fonts_update_progress(demo, n_pages, scanned); while (gtk_events_pending()) { gtk_main_iteration(); } scanned += 20; if (!fonts_iter) { continue; } do { GtkTreeIter iter; const gchar *name; const gchar *type; const gchar *embedded; const gchar *substitute; const gchar *filename; const gchar *encoding; gchar *details; name = poppler_fonts_iter_get_name(fonts_iter); if (!name) { name = "No name"; } encoding = poppler_fonts_iter_get_encoding(fonts_iter); if (!encoding) { encoding = "None"; } type = font_type_to_string(poppler_fonts_iter_get_font_type(fonts_iter)); if (poppler_fonts_iter_is_embedded(fonts_iter)) { if (poppler_fonts_iter_is_subset(fonts_iter)) { embedded = "Embedded subset"; } else { embedded = "Embedded"; } } else { embedded = "Not embedded"; } substitute = poppler_fonts_iter_get_substitute_name(fonts_iter); filename = poppler_fonts_iter_get_file_name(fonts_iter); if (substitute && filename) { details = g_markup_printf_escaped("%s\nEncoding: %s\n%s, substituting with %s\n(%s)", type, encoding, embedded, substitute, filename); } else { details = g_markup_printf_escaped("%s\nEncoding: %s\n%s", type, encoding, embedded); } gtk_list_store_append(GTK_LIST_STORE(model), &iter); gtk_list_store_set(GTK_LIST_STORE(model), &iter, FONTS_NAME_COLUMN, name, FONTS_DETAILS_COLUMN, details, -1); g_free(details); } while (poppler_fonts_iter_next(fonts_iter)); poppler_fonts_iter_free(fonts_iter); } pgd_fonts_update_progress(demo, n_pages, scanned); g_object_unref(font_info); g_object_unref(model); return FALSE; } static void pgd_fonts_scan_button_clicked(GtkButton *button, PgdFontsDemo *demo) { demo->idle_id = g_idle_add((GSourceFunc)pgd_fonts_fill_model, demo); } GtkWidget *pgd_fonts_create_widget(PopplerDocument *document) { PgdFontsDemo *demo; GtkWidget *vbox; GtkListStore *model; GtkCellRenderer *renderer; GtkTreeViewColumn *column; GtkWidget *swindow; GtkWidget *hbox, *button; demo = g_new0(PgdFontsDemo, 1); demo->doc = g_object_ref(document); vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12); hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); demo->progress = gtk_progress_bar_new(); gtk_progress_bar_set_ellipsize(GTK_PROGRESS_BAR(demo->progress), PANGO_ELLIPSIZE_END); gtk_box_pack_start(GTK_BOX(hbox), demo->progress, TRUE, TRUE, 0); gtk_widget_show(demo->progress); button = gtk_button_new_with_label("Scan"); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(pgd_fonts_scan_button_clicked), (gpointer)demo); gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0); gtk_widget_show(button); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 6); gtk_widget_show(hbox); swindow = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); model = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING); demo->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(demo->treeview), FALSE); gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(demo->treeview)), GTK_SELECTION_NONE); g_object_unref(model); column = gtk_tree_view_column_new(); gtk_tree_view_append_column(GTK_TREE_VIEW(demo->treeview), column); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), renderer, FALSE); gtk_tree_view_column_set_cell_data_func(column, renderer, pdg_fonts_cell_data_func, NULL, NULL); gtk_container_add(GTK_CONTAINER(swindow), demo->treeview); gtk_widget_show(demo->treeview); gtk_box_pack_start(GTK_BOX(vbox), swindow, TRUE, TRUE, 0); gtk_widget_show(swindow); g_object_weak_ref(G_OBJECT(swindow), (GWeakNotify)pgd_fonts_free, (gpointer)demo); return vbox; } poppler-24.02.0/glib/demo/fonts.h000066400000000000000000000017231455701731300165140ustar00rootroot00000000000000/* * Copyright (C) 2007 Carlos Garcia Campos * * 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 _FONTS_H_ # define _FONTS_H_ G_BEGIN_DECLS GtkWidget *pgd_fonts_create_widget(PopplerDocument *document); G_END_DECLS #endif /* _FONTS_H_ */ poppler-24.02.0/glib/demo/forms.c000066400000000000000000000453461455701731300165150ustar00rootroot00000000000000/* * Copyright (C) 2007 Carlos Garcia Campos * Copyright (C) 2021 André Guerreiro * Copyright (C) 2021 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 * 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 "forms.h" #include "utils.h" enum { FORMS_FIELD_TYPE_COLUMN, FORMS_ID_COLUMN, FORMS_READ_ONLY_COLUMN, FORMS_X1_COLUMN, FORMS_Y1_COLUMN, FORMS_X2_COLUMN, FORMS_Y2_COLUMN, FORMS_FIELD_COLUMN, N_COLUMNS }; typedef struct { PopplerDocument *doc; GtkListStore *model; GtkWidget *field_view; GtkWidget *timer_label; gint page; } PgdFormsDemo; static void pgd_forms_free(PgdFormsDemo *demo) { if (!demo) { return; } if (demo->doc) { g_object_unref(demo->doc); demo->doc = NULL; } if (demo->model) { g_object_unref(demo->model); demo->model = NULL; } g_free(demo); } static GtkWidget *pgd_form_field_view_new(void) { GtkWidget *frame, *label; frame = gtk_frame_new(NULL); gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE); label = gtk_label_new(NULL); gtk_label_set_markup(GTK_LABEL(label), "Form Field Properties"); gtk_frame_set_label_widget(GTK_FRAME(frame), label); gtk_widget_show(label); return frame; } static void pgd_form_field_view_add_choice_items(GtkGrid *table, PopplerFormField *field, gint *selected, gint *row) { GtkWidget *label; GtkWidget *textview, *swindow; GtkTextBuffer *buffer; gint i; label = gtk_label_new(NULL); g_object_set(G_OBJECT(label), "xalign", 0.0, NULL); gtk_label_set_markup(GTK_LABEL(label), "Items:"); gtk_grid_attach(GTK_GRID(table), label, 0, *row, 1, 1); gtk_widget_show(label); swindow = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); textview = gtk_text_view_new(); gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE); buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview)); for (i = 0; i < poppler_form_field_choice_get_n_items(field); i++) { gchar *item; item = poppler_form_field_choice_get_item(field, i); gtk_text_buffer_insert_at_cursor(buffer, item, strlen(item)); gtk_text_buffer_insert_at_cursor(buffer, "\n", strlen("\n")); g_free(item); if (poppler_form_field_choice_is_item_selected(field, i)) { *selected = i; } } gtk_container_add(GTK_CONTAINER(swindow), textview); gtk_widget_show(textview); gtk_grid_attach(GTK_GRID(table), swindow, 1, *row, 1, 1); gtk_widget_show(swindow); *row += 1; } static void pgd_form_field_view_set_field(GtkWidget *field_view, PopplerFormField *field) { GtkWidget *table; PopplerAction *action; GEnumValue *enum_value; gchar *text; gint row = 0; PopplerSignatureInfo *psig_info; table = gtk_bin_get_child(GTK_BIN(field_view)); if (table) { gtk_container_remove(GTK_CONTAINER(field_view), table); } if (!field) { return; } 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); text = poppler_form_field_get_name(field); if (text) { pgd_table_add_property(GTK_GRID(table), "Name:", text, &row); g_free(text); } text = poppler_form_field_get_partial_name(field); if (text) { pgd_table_add_property(GTK_GRID(table), "Partial Name:", text, &row); g_free(text); } text = poppler_form_field_get_mapping_name(field); if (text) { pgd_table_add_property(GTK_GRID(table), "Mapping Name:", text, &row); g_free(text); } action = poppler_form_field_get_action(field); if (action) { GtkWidget *action_view; action_view = pgd_action_view_new(NULL); pgd_action_view_set_action(action_view, action); pgd_table_add_property_with_custom_widget(GTK_GRID(table), "Action:", action_view, &row); gtk_widget_show(action_view); } action = poppler_form_field_get_additional_action(field, POPPLER_ADDITIONAL_ACTION_FIELD_MODIFIED); if (action) { GtkWidget *action_view; action_view = pgd_action_view_new(NULL); pgd_action_view_set_action(action_view, action); pgd_table_add_property_with_custom_widget(GTK_GRID(table), "Field Modified Action:", action_view, &row); gtk_widget_show(action_view); } action = poppler_form_field_get_additional_action(field, POPPLER_ADDITIONAL_ACTION_FORMAT_FIELD); if (action) { GtkWidget *action_view; action_view = pgd_action_view_new(NULL); pgd_action_view_set_action(action_view, action); pgd_table_add_property_with_custom_widget(GTK_GRID(table), "Field Format Action:", action_view, &row); gtk_widget_show(action_view); } action = poppler_form_field_get_additional_action(field, POPPLER_ADDITIONAL_ACTION_VALIDATE_FIELD); if (action) { GtkWidget *action_view; action_view = pgd_action_view_new(NULL); pgd_action_view_set_action(action_view, action); pgd_table_add_property_with_custom_widget(GTK_GRID(table), "Validate Field Action:", action_view, &row); gtk_widget_show(action_view); } action = poppler_form_field_get_additional_action(field, POPPLER_ADDITIONAL_ACTION_CALCULATE_FIELD); if (action) { GtkWidget *action_view; action_view = pgd_action_view_new(NULL); pgd_action_view_set_action(action_view, action); pgd_table_add_property_with_custom_widget(GTK_GRID(table), "Calculate Field Action:", action_view, &row); gtk_widget_show(action_view); } switch (poppler_form_field_get_field_type(field)) { case POPPLER_FORM_FIELD_BUTTON: enum_value = g_enum_get_value((GEnumClass *)g_type_class_ref(POPPLER_TYPE_FORM_BUTTON_TYPE), poppler_form_field_button_get_button_type(field)); pgd_table_add_property(GTK_GRID(table), "Button Type:", enum_value->value_name, &row); pgd_table_add_property(GTK_GRID(table), "Button State:", poppler_form_field_button_get_state(field) ? "Active" : "Inactive", &row); break; case POPPLER_FORM_FIELD_TEXT: enum_value = g_enum_get_value((GEnumClass *)g_type_class_ref(POPPLER_TYPE_FORM_TEXT_TYPE), poppler_form_field_text_get_text_type(field)); pgd_table_add_property(GTK_GRID(table), "Text Type:", enum_value->value_name, &row); text = poppler_form_field_text_get_text(field); pgd_table_add_property(GTK_GRID(table), "Contents:", text, &row); g_free(text); text = g_strdup_printf("%d", poppler_form_field_text_get_max_len(field)); pgd_table_add_property(GTK_GRID(table), "Max Length:", text, &row); g_free(text); pgd_table_add_property(GTK_GRID(table), "Do spellcheck:", poppler_form_field_text_do_spell_check(field) ? "Yes" : "No", &row); pgd_table_add_property(GTK_GRID(table), "Do scroll:", poppler_form_field_text_do_scroll(field) ? "Yes" : "No", &row); pgd_table_add_property(GTK_GRID(table), "Rich Text:", poppler_form_field_text_is_rich_text(field) ? "Yes" : "No", &row); pgd_table_add_property(GTK_GRID(table), "Pasword type:", poppler_form_field_text_is_password(field) ? "Yes" : "No", &row); break; case POPPLER_FORM_FIELD_CHOICE: { gchar *item; gint selected = -1; enum_value = g_enum_get_value((GEnumClass *)g_type_class_ref(POPPLER_TYPE_FORM_CHOICE_TYPE), poppler_form_field_choice_get_choice_type(field)); pgd_table_add_property(GTK_GRID(table), "Choice Type:", enum_value->value_name, &row); pgd_table_add_property(GTK_GRID(table), "Editable:", poppler_form_field_choice_is_editable(field) ? "Yes" : "No", &row); pgd_table_add_property(GTK_GRID(table), "Multiple Selection:", poppler_form_field_choice_can_select_multiple(field) ? "Yes" : "No", &row); pgd_table_add_property(GTK_GRID(table), "Do spellcheck:", poppler_form_field_choice_do_spell_check(field) ? "Yes" : "No", &row); pgd_table_add_property(GTK_GRID(table), "Commit on Change:", poppler_form_field_choice_commit_on_change(field) ? "Yes" : "No", &row); text = g_strdup_printf("%d", poppler_form_field_choice_get_n_items(field)); pgd_table_add_property(GTK_GRID(table), "Number of items:", text, &row); g_free(text); pgd_form_field_view_add_choice_items(GTK_GRID(table), field, &selected, &row); if (selected >= 0 && poppler_form_field_choice_get_n_items(field) > selected) { item = poppler_form_field_choice_get_item(field, selected); text = g_strdup_printf("%d (%s)", selected, item); g_free(item); pgd_table_add_property(GTK_GRID(table), "Selected item:", text, &row); g_free(text); } text = poppler_form_field_choice_get_text(field); pgd_table_add_property(GTK_GRID(table), "Contents:", text, &row); g_free(text); } break; case POPPLER_FORM_FIELD_SIGNATURE: { const gchar *signer_name; psig_info = poppler_form_field_signature_validate_sync( field, POPPLER_SIGNATURE_VALIDATION_FLAG_VALIDATE_CERTIFICATE | POPPLER_SIGNATURE_VALIDATION_FLAG_WITHOUT_OCSP_REVOCATION_CHECK | POPPLER_SIGNATURE_VALIDATION_FLAG_USE_AIA_CERTIFICATE_FETCH, NULL, NULL); signer_name = poppler_signature_info_get_signer_name(psig_info); pgd_table_add_property(GTK_GRID(table), "Signer Name:", signer_name ? signer_name : "Signers name found", &row); text = g_date_time_format(poppler_signature_info_get_local_signing_time(psig_info), "%b %d %Y %H:%M:%S"); pgd_table_add_property(GTK_GRID(table), "Signing Time:", text, &row); pgd_table_add_property(GTK_GRID(table), "Signature Val Status:", poppler_signature_info_get_signature_status(psig_info) == POPPLER_SIGNATURE_VALID ? "Signature is Valid" : "Signature is Invalid", &row); pgd_table_add_property(GTK_GRID(table), "Certificate Val Status:", poppler_signature_info_get_certificate_status(psig_info) == POPPLER_CERTIFICATE_TRUSTED ? "Certificate is Trusted" : "Certificate cannot be Trusted", &row); poppler_signature_info_free(psig_info); g_free(text); } break; case POPPLER_FORM_FIELD_UNKNOWN: break; default: g_assert_not_reached(); } gtk_container_add(GTK_CONTAINER(field_view), table); gtk_widget_show(table); } const gchar *get_form_field_type(PopplerFormField *field) { switch (poppler_form_field_get_field_type(field)) { case POPPLER_FORM_FIELD_TEXT: return "Text"; case POPPLER_FORM_FIELD_BUTTON: return "Button"; case POPPLER_FORM_FIELD_CHOICE: return "Choice"; case POPPLER_FORM_FIELD_SIGNATURE: return "Signature"; case POPPLER_FORM_FIELD_UNKNOWN: default: break; } return "Unknown"; } static void pgd_forms_get_form_fields(GtkWidget *button, PgdFormsDemo *demo) { PopplerPage *page; GList *mapping, *l; gint n_fields; GTimer *timer; gtk_list_store_clear(demo->model); pgd_form_field_view_set_field(demo->field_view, NULL); page = poppler_document_get_page(demo->doc, demo->page); if (!page) { return; } timer = g_timer_new(); mapping = poppler_page_get_form_field_mapping(page); g_timer_stop(timer); n_fields = g_list_length(mapping); if (n_fields > 0) { gchar *str; str = g_strdup_printf("%d form fields found in %.4f seconds", n_fields, g_timer_elapsed(timer, NULL)); gtk_label_set_markup(GTK_LABEL(demo->timer_label), str); g_free(str); } else { gtk_label_set_markup(GTK_LABEL(demo->timer_label), "No form fields found"); } g_timer_destroy(timer); for (l = mapping; l; l = g_list_next(l)) { PopplerFormFieldMapping *fmapping; GtkTreeIter iter; gchar *x1, *y1, *x2, *y2; fmapping = (PopplerFormFieldMapping *)l->data; x1 = g_strdup_printf("%.2f", fmapping->area.x1); y1 = g_strdup_printf("%.2f", fmapping->area.y1); x2 = g_strdup_printf("%.2f", fmapping->area.x2); y2 = g_strdup_printf("%.2f", fmapping->area.y2); gtk_list_store_append(demo->model, &iter); gtk_list_store_set(demo->model, &iter, FORMS_FIELD_TYPE_COLUMN, get_form_field_type(fmapping->field), FORMS_ID_COLUMN, poppler_form_field_get_id(fmapping->field), FORMS_READ_ONLY_COLUMN, poppler_form_field_is_read_only(fmapping->field), FORMS_X1_COLUMN, x1, FORMS_Y1_COLUMN, y1, FORMS_X2_COLUMN, x2, FORMS_Y2_COLUMN, y2, FORMS_FIELD_COLUMN, fmapping->field, -1); g_free(x1); g_free(y1); g_free(x2); g_free(y2); } poppler_page_free_form_field_mapping(mapping); g_object_unref(page); } static void pgd_forms_page_selector_value_changed(GtkSpinButton *spinbutton, PgdFormsDemo *demo) { demo->page = (gint)gtk_spin_button_get_value(spinbutton) - 1; } static void pgd_forms_selection_changed(GtkTreeSelection *treeselection, PgdFormsDemo *demo) { GtkTreeModel *model; GtkTreeIter iter; if (gtk_tree_selection_get_selected(treeselection, &model, &iter)) { PopplerFormField *field; gtk_tree_model_get(model, &iter, FORMS_FIELD_COLUMN, &field, -1); pgd_form_field_view_set_field(demo->field_view, field); g_object_unref(field); } } GtkWidget *pgd_forms_create_widget(PopplerDocument *document) { PgdFormsDemo *demo; GtkWidget *label; GtkWidget *vbox; GtkWidget *hbox, *page_selector; GtkWidget *button; GtkWidget *hpaned; GtkWidget *swindow, *treeview; GtkTreeSelection *selection; GtkCellRenderer *renderer; gchar *str; gint n_pages; demo = g_new0(PgdFormsDemo, 1); demo->doc = g_object_ref(document); 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_forms_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); button = gtk_button_new_with_label("Get Forms Fields"); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(pgd_forms_get_form_fields), (gpointer)demo); gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0); gtk_widget_show(button); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); gtk_widget_show(hbox); demo->timer_label = gtk_label_new(NULL); gtk_label_set_markup(GTK_LABEL(demo->timer_label), "No form fields found"); g_object_set(G_OBJECT(demo->timer_label), "xalign", 1.0, NULL); gtk_box_pack_start(GTK_BOX(vbox), demo->timer_label, FALSE, TRUE, 0); gtk_widget_show(demo->timer_label); hpaned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); demo->field_view = pgd_form_field_view_new(); swindow = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); demo->model = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_OBJECT); treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(demo->model)); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 0, "Form Field Type", renderer, "text", FORMS_FIELD_TYPE_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 1, "Form Field Id", renderer, "text", FORMS_ID_COLUMN, NULL); renderer = gtk_cell_renderer_toggle_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 2, "Read Only", renderer, "active", FORMS_READ_ONLY_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 3, "X1", renderer, "text", FORMS_X1_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 4, "Y1", renderer, "text", FORMS_Y1_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 5, "X2", renderer, "text", FORMS_X2_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 6, "Y2", renderer, "text", FORMS_Y2_COLUMN, NULL); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(pgd_forms_selection_changed), (gpointer)demo); gtk_container_add(GTK_CONTAINER(swindow), treeview); gtk_widget_show(treeview); gtk_paned_add1(GTK_PANED(hpaned), swindow); gtk_widget_show(swindow); gtk_paned_add2(GTK_PANED(hpaned), demo->field_view); gtk_widget_show(demo->field_view); gtk_paned_set_position(GTK_PANED(hpaned), 300); gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0); gtk_widget_show(hpaned); g_object_weak_ref(G_OBJECT(vbox), (GWeakNotify)pgd_forms_free, demo); return vbox; } poppler-24.02.0/glib/demo/forms.h000066400000000000000000000017231455701731300165110ustar00rootroot00000000000000/* * Copyright (C) 2007 Carlos Garcia Campos * * 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 _FORMS_H_ # define _FORMS_H_ G_BEGIN_DECLS GtkWidget *pgd_forms_create_widget(PopplerDocument *document); G_END_DECLS #endif /* _FORMS_H_ */ poppler-24.02.0/glib/demo/images.c000066400000000000000000000215641455701731300166300ustar00rootroot00000000000000/* * Copyright (C) 2008 Carlos Garcia Campos * * 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 "config.h" #include #include "images.h" enum { IMAGES_ID_COLUMN, IMAGES_X1_COLUMN, IMAGES_Y1_COLUMN, IMAGES_X2_COLUMN, IMAGES_Y2_COLUMN, N_COLUMNS }; typedef struct { PopplerDocument *doc; GtkListStore *model; GtkWidget *timer_label; GtkWidget *image_view; gint page; } PgdImagesDemo; static void pgd_images_free(PgdImagesDemo *demo) { if (!demo) { return; } if (demo->doc) { g_object_unref(demo->doc); demo->doc = NULL; } if (demo->model) { g_object_unref(demo->model); demo->model = NULL; } g_free(demo); } static gboolean pgd_image_view_drawing_area_draw(GtkWidget *area, cairo_t *cr, GtkWidget *image_view) { cairo_surface_t *image; image = g_object_get_data(G_OBJECT(image_view), "image-surface"); if (!image) { return FALSE; } gtk_widget_set_size_request(area, cairo_image_surface_get_width(image), cairo_image_surface_get_height(image)); cairo_set_source_surface(cr, image, 0, 0); cairo_paint(cr); return TRUE; } static GtkWidget *pgd_image_view_new() { GtkWidget *swindow; GtkWidget *darea; swindow = gtk_scrolled_window_new(NULL, NULL); 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; } static void pgd_image_view_set_image(GtkWidget *image_view, cairo_surface_t *image) { g_object_set_data_full(G_OBJECT(image_view), "image-surface", image, (GDestroyNotify)cairo_surface_destroy); gtk_widget_queue_draw(image_view); } static void pgd_images_get_images(GtkWidget *button, PgdImagesDemo *demo) { PopplerPage *page; GList *mapping, *l; gint n_images; GTimer *timer; gtk_list_store_clear(demo->model); pgd_image_view_set_image(demo->image_view, NULL); page = poppler_document_get_page(demo->doc, demo->page); if (!page) { return; } timer = g_timer_new(); mapping = poppler_page_get_image_mapping(page); g_timer_stop(timer); n_images = g_list_length(mapping); if (n_images > 0) { gchar *str; str = g_strdup_printf("%d images found in %.4f seconds", n_images, g_timer_elapsed(timer, NULL)); gtk_label_set_markup(GTK_LABEL(demo->timer_label), str); g_free(str); } else { gtk_label_set_markup(GTK_LABEL(demo->timer_label), "No images found"); } g_timer_destroy(timer); for (l = mapping; l; l = g_list_next(l)) { PopplerImageMapping *imapping; GtkTreeIter iter; gchar *x1, *y1, *x2, *y2; imapping = (PopplerImageMapping *)l->data; x1 = g_strdup_printf("%.2f", imapping->area.x1); y1 = g_strdup_printf("%.2f", imapping->area.y1); x2 = g_strdup_printf("%.2f", imapping->area.x2); y2 = g_strdup_printf("%.2f", imapping->area.y2); gtk_list_store_append(demo->model, &iter); gtk_list_store_set(demo->model, &iter, IMAGES_ID_COLUMN, imapping->image_id, IMAGES_X1_COLUMN, x1, IMAGES_Y1_COLUMN, y1, IMAGES_X2_COLUMN, x2, IMAGES_Y2_COLUMN, y2, -1); g_free(x1); g_free(y1); g_free(x2); g_free(y2); } poppler_page_free_image_mapping(mapping); g_object_unref(page); } static void pgd_images_page_selector_value_changed(GtkSpinButton *spinbutton, PgdImagesDemo *demo) { demo->page = (gint)gtk_spin_button_get_value(spinbutton) - 1; } static void pgd_images_selection_changed(GtkTreeSelection *treeselection, PgdImagesDemo *demo) { GtkTreeModel *model; GtkTreeIter iter; if (gtk_tree_selection_get_selected(treeselection, &model, &iter)) { PopplerPage *page; gint image_id; gtk_tree_model_get(model, &iter, IMAGES_ID_COLUMN, &image_id, -1); page = poppler_document_get_page(demo->doc, demo->page); pgd_image_view_set_image(demo->image_view, poppler_page_get_image(page, image_id)); g_object_unref(page); } } GtkWidget *pgd_images_create_widget(PopplerDocument *document) { PgdImagesDemo *demo; GtkWidget *label; GtkWidget *vbox; GtkWidget *hbox, *page_selector; GtkWidget *button; GtkWidget *hpaned; GtkWidget *swindow, *treeview; GtkTreeSelection *selection; GtkCellRenderer *renderer; gchar *str; gint n_pages; demo = g_new0(PgdImagesDemo, 1); demo->doc = g_object_ref(document); 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_images_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); button = gtk_button_new_with_label("Get Images"); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(pgd_images_get_images), (gpointer)demo); gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0); gtk_widget_show(button); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); gtk_widget_show(hbox); demo->timer_label = gtk_label_new(NULL); gtk_label_set_markup(GTK_LABEL(demo->timer_label), "No images found"); g_object_set(G_OBJECT(demo->timer_label), "xalign", 1.0, NULL); gtk_box_pack_start(GTK_BOX(vbox), demo->timer_label, FALSE, TRUE, 0); gtk_widget_show(demo->timer_label); hpaned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); demo->image_view = pgd_image_view_new(); swindow = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); demo->model = gtk_list_store_new(N_COLUMNS, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(demo->model)); renderer = gtk_cell_renderer_text_new(); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 0, "Image", renderer, "text", IMAGES_ID_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 1, "X1", renderer, "text", IMAGES_X1_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 2, "Y1", renderer, "text", IMAGES_Y1_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 3, "X2", renderer, "text", IMAGES_X2_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 4, "Y2", renderer, "text", IMAGES_Y2_COLUMN, NULL); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(pgd_images_selection_changed), (gpointer)demo); gtk_container_add(GTK_CONTAINER(swindow), treeview); gtk_widget_show(treeview); gtk_paned_add1(GTK_PANED(hpaned), swindow); gtk_widget_show(swindow); gtk_paned_add2(GTK_PANED(hpaned), demo->image_view); gtk_widget_show(demo->image_view); gtk_paned_set_position(GTK_PANED(hpaned), 300); gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0); gtk_widget_show(hpaned); g_object_weak_ref(G_OBJECT(vbox), (GWeakNotify)pgd_images_free, demo); return vbox; } poppler-24.02.0/glib/demo/images.h000066400000000000000000000017271455701731300166340ustar00rootroot00000000000000/* * Copyright (C) 2008 Carlos Garcia Campos * * 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 _IMAGES_H_ # define _IMAGES_H_ G_BEGIN_DECLS GtkWidget *pgd_images_create_widget(PopplerDocument *document); G_END_DECLS #endif /* _IMAGES_H_ */ poppler-24.02.0/glib/demo/info.cc000066400000000000000000000226631455701731300164620ustar00rootroot00000000000000/* * Copyright (C) 2007 Carlos Garcia Campos * * 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 "config.h" #include "info.h" #include "utils.h" static void pgd_info_add_permissions(GtkGrid *table, PopplerPermissions permissions, gint *row) { GtkWidget *label, *hbox, *vbox; GtkWidget *checkbox; label = gtk_label_new(nullptr); g_object_set(G_OBJECT(label), "xalign", 0.0, NULL); gtk_label_set_markup(GTK_LABEL(label), "Permissions:"); gtk_grid_attach(GTK_GRID(table), label, 0, *row, 1, 1); gtk_widget_show(label); vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); checkbox = gtk_check_button_new_with_label("Print"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox), (permissions & POPPLER_PERMISSIONS_OK_TO_PRINT)); gtk_box_pack_start(GTK_BOX(hbox), checkbox, FALSE, TRUE, 0); gtk_widget_show(checkbox); checkbox = gtk_check_button_new_with_label("Copy"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox), (permissions & POPPLER_PERMISSIONS_OK_TO_COPY)); gtk_box_pack_start(GTK_BOX(hbox), checkbox, FALSE, TRUE, 0); gtk_widget_show(checkbox); checkbox = gtk_check_button_new_with_label("Modify"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox), (permissions & POPPLER_PERMISSIONS_OK_TO_MODIFY)); gtk_box_pack_start(GTK_BOX(hbox), checkbox, FALSE, TRUE, 0); gtk_widget_show(checkbox); checkbox = gtk_check_button_new_with_label("Add notes"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox), (permissions & POPPLER_PERMISSIONS_OK_TO_ADD_NOTES)); gtk_box_pack_start(GTK_BOX(hbox), checkbox, FALSE, TRUE, 0); gtk_widget_show(checkbox); checkbox = gtk_check_button_new_with_label("Fill forms"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox), (permissions & POPPLER_PERMISSIONS_OK_TO_FILL_FORM)); gtk_box_pack_start(GTK_BOX(hbox), checkbox, FALSE, TRUE, 0); gtk_widget_show(checkbox); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); gtk_widget_show(hbox); hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); checkbox = gtk_check_button_new_with_label("Extract contents"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox), (permissions & POPPLER_PERMISSIONS_OK_TO_EXTRACT_CONTENTS)); gtk_box_pack_start(GTK_BOX(hbox), checkbox, FALSE, TRUE, 0); gtk_widget_show(checkbox); checkbox = gtk_check_button_new_with_label("Assemble"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox), (permissions & POPPLER_PERMISSIONS_OK_TO_ASSEMBLE)); gtk_box_pack_start(GTK_BOX(hbox), checkbox, FALSE, TRUE, 0); gtk_widget_show(checkbox); checkbox = gtk_check_button_new_with_label("Print at high resolution"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox), (permissions & POPPLER_PERMISSIONS_OK_TO_PRINT_HIGH_RESOLUTION)); gtk_box_pack_start(GTK_BOX(hbox), checkbox, FALSE, TRUE, 0); gtk_widget_show(checkbox); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); gtk_widget_show(hbox); gtk_grid_attach(GTK_GRID(table), vbox, 1, *row, 1, 1); gtk_widget_show(vbox); *row += 1; } static void pgd_info_add_metadata(GtkGrid *table, const gchar *metadata, gint *row) { GtkWidget *label; GtkWidget *textview, *swindow; GtkTextBuffer *buffer; label = gtk_label_new(nullptr); g_object_set(G_OBJECT(label), "xalign", 0.0, NULL); gtk_label_set_markup(GTK_LABEL(label), "Metadata:"); gtk_grid_attach(GTK_GRID(table), label, 0, *row, 1, 1); gtk_widget_show(label); swindow = gtk_scrolled_window_new(nullptr, nullptr); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); textview = gtk_text_view_new(); gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE); buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview)); if (metadata) { gtk_text_buffer_set_text(buffer, metadata, -1); } gtk_container_add(GTK_CONTAINER(swindow), textview); gtk_widget_show(textview); gtk_grid_attach(GTK_GRID(table), swindow, 1, *row, 1, 1); gtk_widget_set_hexpand(swindow, TRUE); gtk_widget_set_vexpand(swindow, TRUE); gtk_widget_show(swindow); *row += 1; } GtkWidget *pgd_info_create_widget(PopplerDocument *document) { GtkWidget *vbox; GtkWidget *label; GtkWidget *frame, *table; gchar *str; gchar *title, *format, *author, *subject; gchar *keywords, *creator, *producer; gchar *metadata; gchar *perm_id; gchar *up_id; gboolean linearized; GDateTime *creation_date, *mod_date; GEnumValue *enum_value; PopplerBackend backend; PopplerPageLayout layout; PopplerPageMode mode; PopplerPermissions permissions; PopplerViewerPreferences view_prefs; gint row = 0; g_object_get(document, "title", &title, "format", &format, "author", &author, "subject", &subject, "keywords", &keywords, "creation-datetime", &creation_date, "mod-datetime", &mod_date, "creator", &creator, "producer", &producer, "linearized", &linearized, "page-mode", &mode, "page-layout", &layout, "permissions", &permissions, "viewer-preferences", &view_prefs, "metadata", &metadata, NULL); vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12); backend = poppler_get_backend(); enum_value = g_enum_get_value((GEnumClass *)g_type_class_ref(POPPLER_TYPE_BACKEND), backend); str = g_strdup_printf("Poppler %s (%s)", poppler_get_version(), enum_value->value_name); label = gtk_label_new(nullptr); gtk_label_set_markup(GTK_LABEL(label), str); g_free(str); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 12); gtk_widget_show(label); frame = gtk_frame_new(nullptr); gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE); label = gtk_label_new(nullptr); gtk_label_set_markup(GTK_LABEL(label), "Document properties"); gtk_frame_set_label_widget(GTK_FRAME(frame), label); gtk_widget_show(label); 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); pgd_table_add_property(GTK_GRID(table), "Format:", format, &row); g_free(format); pgd_table_add_property(GTK_GRID(table), "Title:", title, &row); g_free(title); pgd_table_add_property(GTK_GRID(table), "Author:", author, &row); g_free(author); pgd_table_add_property(GTK_GRID(table), "Subject:", subject, &row); g_free(subject); pgd_table_add_property(GTK_GRID(table), "Keywords:", keywords, &row); g_free(keywords); pgd_table_add_property(GTK_GRID(table), "Creator:", creator, &row); g_free(creator); pgd_table_add_property(GTK_GRID(table), "Producer:", producer, &row); g_free(producer); pgd_table_add_property(GTK_GRID(table), "Linearized:", linearized ? "Yes" : "No", &row); str = g_date_time_format(creation_date, "%c"); pgd_table_add_property(GTK_GRID(table), "Creation Date:", str, &row); g_clear_pointer(&creation_date, g_date_time_unref); g_free(str); str = g_date_time_format(mod_date, "%c"); pgd_table_add_property(GTK_GRID(table), "Modification Date:", str, &row); g_clear_pointer(&mod_date, g_date_time_unref); g_free(str); enum_value = g_enum_get_value((GEnumClass *)g_type_class_peek(POPPLER_TYPE_PAGE_MODE), mode); pgd_table_add_property(GTK_GRID(table), "Page Mode:", enum_value->value_name, &row); enum_value = g_enum_get_value((GEnumClass *)g_type_class_peek(POPPLER_TYPE_PAGE_LAYOUT), layout); pgd_table_add_property(GTK_GRID(table), "Page Layout:", enum_value->value_name, &row); if (poppler_document_get_id(document, &perm_id, &up_id)) { str = g_strndup(perm_id, 32); g_free(perm_id); pgd_table_add_property(GTK_GRID(table), "Permanent ID:", str, &row); g_free(str); str = g_strndup(up_id, 32); g_free(up_id); pgd_table_add_property(GTK_GRID(table), "Update ID:", str, &row); g_free(str); } pgd_info_add_permissions(GTK_GRID(table), permissions, &row); pgd_info_add_metadata(GTK_GRID(table), metadata, &row); g_free(metadata); /* TODO: view_prefs */ gtk_container_add(GTK_CONTAINER(frame), table); gtk_widget_show(table); gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0); gtk_widget_show(frame); return vbox; } poppler-24.02.0/glib/demo/info.h000066400000000000000000000017171455701731300163210ustar00rootroot00000000000000/* * Copyright (C) 2007 Carlos Garcia Campos * * 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 _INFO_H_ # define _INFO_H_ G_BEGIN_DECLS GtkWidget *pgd_info_create_widget(PopplerDocument *document); G_END_DECLS #endif /* _INFO_H_ */ poppler-24.02.0/glib/demo/layers.c000066400000000000000000000262341455701731300166610ustar00rootroot00000000000000/* * Copyright (C) 2008 Carlos Garcia Campos * * 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 "config.h" #include #include "layers.h" enum { LAYERS_TITLE_COLUMN, LAYERS_VISIBILITY_COLUMN, LAYERS_ENABLE_COLUMN, LAYERS_SHOWTOGGLE_COLUMN, LAYERS_RB_GROUP_COLUMN, LAYERS_LAYER_COLUMN, N_COLUMNS }; typedef struct { PopplerDocument *doc; guint page; GtkWidget *treeview; GtkWidget *darea; cairo_surface_t *surface; } PgdLayersDemo; static void pgd_layers_free(PgdLayersDemo *demo) { if (!demo) { return; } if (demo->doc) { g_object_unref(demo->doc); demo->doc = NULL; } if (demo->surface) { cairo_surface_destroy(demo->surface); demo->surface = NULL; } g_free(demo); } static void build_tree(PopplerDocument *document, GtkTreeModel *model, GtkTreeIter *parent, PopplerLayersIter *iter) { do { GtkTreeIter tree_iter; PopplerLayersIter *child; PopplerLayer *layer; gboolean visible; gchar *markup; gint rb_group = 0; layer = poppler_layers_iter_get_layer(iter); if (layer) { markup = g_markup_escape_text(poppler_layer_get_title(layer), -1); visible = poppler_layer_is_visible(layer); rb_group = poppler_layer_get_radio_button_group_id(layer); } else { gchar *title; title = poppler_layers_iter_get_title(iter); markup = g_markup_escape_text(title, -1); g_free(title); visible = FALSE; layer = NULL; } gtk_tree_store_append(GTK_TREE_STORE(model), &tree_iter, parent); gtk_tree_store_set(GTK_TREE_STORE(model), &tree_iter, LAYERS_TITLE_COLUMN, markup, LAYERS_VISIBILITY_COLUMN, visible, LAYERS_ENABLE_COLUMN, TRUE, /* FIXME */ LAYERS_SHOWTOGGLE_COLUMN, (layer != NULL), LAYERS_RB_GROUP_COLUMN, rb_group, LAYERS_LAYER_COLUMN, layer, -1); if (layer) { g_object_unref(layer); } g_free(markup); child = poppler_layers_iter_get_child(iter); if (child) { build_tree(document, model, &tree_iter, child); } poppler_layers_iter_free(child); } while (poppler_layers_iter_next(iter)); } GtkTreeModel *pgd_layers_create_model(PopplerDocument *document) { GtkTreeModel *model; PopplerLayersIter *iter; iter = poppler_layers_iter_new(document); if (iter) { model = GTK_TREE_MODEL(gtk_tree_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_INT, G_TYPE_OBJECT)); build_tree(document, model, NULL, iter); poppler_layers_iter_free(iter); } else { GtkTreeIter tree_iter; gchar *markup; model = GTK_TREE_MODEL(gtk_list_store_new(1, G_TYPE_STRING)); gtk_list_store_append(GTK_LIST_STORE(model), &tree_iter); markup = g_strdup_printf("%s", "The document doesn't contain layers"); gtk_list_store_set(GTK_LIST_STORE(model), &tree_iter, 0, markup, -1); g_free(markup); } return model; } static cairo_surface_t *pgd_layers_render_page(PgdLayersDemo *demo) { cairo_t *cr; PopplerPage *page; gdouble width, height; cairo_surface_t *surface = NULL; page = poppler_document_get_page(demo->doc, demo->page); if (!page) { return NULL; } poppler_page_get_size(page, &width, &height); gtk_widget_set_size_request(demo->darea, width, height); surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height); cr = cairo_create(surface); 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 gboolean pgd_layers_viewer_drawing_area_draw(GtkWidget *area, cairo_t *cr, PgdLayersDemo *demo) { if (!demo->surface) { demo->surface = pgd_layers_render_page(demo); if (!demo->surface) { return FALSE; } } cairo_set_source_surface(cr, demo->surface, 0, 0); cairo_paint(cr); return TRUE; } static gboolean pgd_layers_viewer_redraw(PgdLayersDemo *demo) { cairo_surface_destroy(demo->surface); demo->surface = NULL; gtk_widget_queue_draw(demo->darea); return FALSE; } static void pgd_layers_viewer_queue_redraw(PgdLayersDemo *demo) { g_idle_add((GSourceFunc)pgd_layers_viewer_redraw, demo); } static void pgd_layers_page_selector_value_changed(GtkSpinButton *spinbutton, PgdLayersDemo *demo) { demo->page = (gint)gtk_spin_button_get_value(spinbutton) - 1; pgd_layers_viewer_queue_redraw(demo); } static GtkWidget *pgd_layers_create_viewer(PgdLayersDemo *demo) { GtkWidget *vbox, *hbox; GtkWidget *label; GtkWidget *swindow; GtkWidget *page_selector; guint n_pages; gchar *str; vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6); 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); n_pages = poppler_document_get_n_pages(demo->doc); page_selector = gtk_spin_button_new_with_range(1, n_pages, 1); g_signal_connect(G_OBJECT(page_selector), "value-changed", G_CALLBACK(pgd_layers_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); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); gtk_widget_show(hbox); demo->darea = gtk_drawing_area_new(); g_signal_connect(G_OBJECT(demo->darea), "draw", G_CALLBACK(pgd_layers_viewer_drawing_area_draw), (gpointer)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); gtk_widget_show(swindow); return vbox; } static gboolean update_kids(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, GtkTreeIter *parent) { if (gtk_tree_store_is_ancestor(GTK_TREE_STORE(model), parent, iter)) { gboolean visible; gtk_tree_model_get(model, parent, LAYERS_VISIBILITY_COLUMN, &visible, -1); gtk_tree_store_set(GTK_TREE_STORE(model), iter, LAYERS_ENABLE_COLUMN, visible, -1); } return FALSE; } static gboolean clear_rb_group(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gint *rb_group) { gint group; gtk_tree_model_get(model, iter, LAYERS_RB_GROUP_COLUMN, &group, -1); if (group == *rb_group) { gtk_tree_store_set(GTK_TREE_STORE(model), iter, LAYERS_VISIBILITY_COLUMN, FALSE, -1); } return FALSE; } static void pgd_layers_visibility_changed(GtkCellRendererToggle *cell, gchar *path_str, PgdLayersDemo *demo) { GtkTreeModel *model; GtkTreePath *path; GtkTreeIter iter; gboolean visible; PopplerLayer *layer; model = gtk_tree_view_get_model(GTK_TREE_VIEW(demo->treeview)); path = gtk_tree_path_new_from_string(path_str); gtk_tree_model_get_iter(model, &iter, path); gtk_tree_model_get(model, &iter, LAYERS_VISIBILITY_COLUMN, &visible, LAYERS_LAYER_COLUMN, &layer, -1); visible = !visible; visible ? poppler_layer_show(layer) : poppler_layer_hide(layer); if (visible) { gint rb_group; rb_group = poppler_layer_get_radio_button_group_id(layer); if (rb_group) { gtk_tree_model_foreach(model, (GtkTreeModelForeachFunc)clear_rb_group, &rb_group); } } gtk_tree_store_set(GTK_TREE_STORE(model), &iter, LAYERS_VISIBILITY_COLUMN, visible, -1); if (poppler_layer_is_parent(layer)) { gtk_tree_model_foreach(model, (GtkTreeModelForeachFunc)update_kids, &iter); } pgd_layers_viewer_queue_redraw(demo); gtk_tree_path_free(path); g_object_unref(layer); } GtkWidget *pgd_layers_create_widget(PopplerDocument *document) { PgdLayersDemo *demo; GtkWidget *swindow; GtkWidget *treeview; GtkTreeModel *model; GtkCellRenderer *renderer; GtkWidget *hpaned, *viewer; demo = g_new0(PgdLayersDemo, 1); demo->doc = g_object_ref(document); hpaned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); viewer = pgd_layers_create_viewer(demo); swindow = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); model = pgd_layers_create_model(document); treeview = gtk_tree_view_new_with_model(model); demo->treeview = treeview; g_object_unref(model); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 0, "Layer", renderer, "markup", LAYERS_TITLE_COLUMN, NULL); g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL); g_object_set(G_OBJECT(gtk_tree_view_get_column(GTK_TREE_VIEW(treeview), 0)), "expand", TRUE, NULL); if (GTK_IS_TREE_STORE(model)) { renderer = gtk_cell_renderer_toggle_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 1, "Show/Hide", renderer, "active", LAYERS_VISIBILITY_COLUMN, "activatable", LAYERS_ENABLE_COLUMN, "visible", LAYERS_SHOWTOGGLE_COLUMN, NULL); g_signal_connect(renderer, "toggled", G_CALLBACK(pgd_layers_visibility_changed), (gpointer)demo); gtk_tree_view_column_set_clickable(gtk_tree_view_get_column(GTK_TREE_VIEW(treeview), 1), TRUE); } gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), GTK_SELECTION_NONE); gtk_container_add(GTK_CONTAINER(swindow), treeview); gtk_widget_show(treeview); gtk_paned_add1(GTK_PANED(hpaned), swindow); gtk_widget_show(swindow); gtk_paned_add2(GTK_PANED(hpaned), viewer); gtk_widget_show(viewer); gtk_paned_set_position(GTK_PANED(hpaned), 150); g_object_weak_ref(G_OBJECT(hpaned), (GWeakNotify)pgd_layers_free, (gpointer)demo); return hpaned; } poppler-24.02.0/glib/demo/layers.h000066400000000000000000000017271455701731300166660ustar00rootroot00000000000000/* * Copyright (C) 2008 Carlos Garcia Campos * * 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 _LAYERS_H_ # define _LAYERS_H_ G_BEGIN_DECLS GtkWidget *pgd_layers_create_widget(PopplerDocument *document); G_END_DECLS #endif /* _LAYERS_H_ */ poppler-24.02.0/glib/demo/links.c000066400000000000000000000174351455701731300165050ustar00rootroot00000000000000/* * Copyright (C) 2007 Carlos Garcia Campos * * 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 "links.h" #include "utils.h" enum { LINKS_ACTION_TYPE_COLUMN, LINKS_X1_COLUMN, LINKS_Y1_COLUMN, LINKS_X2_COLUMN, LINKS_Y2_COLUMN, LINKS_ACTION_COLUMN, N_COLUMNS }; typedef struct { PopplerDocument *doc; GtkListStore *model; GtkWidget *action_view; GtkWidget *timer_label; gint page; } PgdLinksDemo; static void pgd_links_free(PgdLinksDemo *demo) { if (!demo) { return; } if (demo->doc) { g_object_unref(demo->doc); demo->doc = NULL; } if (demo->model) { g_object_unref(demo->model); demo->model = NULL; } g_free(demo); } static void pgd_links_get_links(GtkWidget *button, PgdLinksDemo *demo) { PopplerPage *page; GList *mapping, *l; gint n_links; GTimer *timer; gtk_list_store_clear(demo->model); pgd_action_view_set_action(demo->action_view, NULL); page = poppler_document_get_page(demo->doc, demo->page); if (!page) { return; } timer = g_timer_new(); mapping = poppler_page_get_link_mapping(page); g_timer_stop(timer); n_links = g_list_length(mapping); if (n_links > 0) { gchar *str; str = g_strdup_printf("%d links found in %.4f seconds", n_links, g_timer_elapsed(timer, NULL)); gtk_label_set_markup(GTK_LABEL(demo->timer_label), str); g_free(str); } else { gtk_label_set_markup(GTK_LABEL(demo->timer_label), "No links found"); } g_timer_destroy(timer); for (l = mapping; l; l = g_list_next(l)) { PopplerLinkMapping *lmapping; PopplerAction *action; GEnumValue *enum_value; GtkTreeIter iter; gchar *x1, *y1, *x2, *y2; lmapping = (PopplerLinkMapping *)l->data; action = poppler_action_copy(lmapping->action); enum_value = g_enum_get_value((GEnumClass *)g_type_class_ref(POPPLER_TYPE_ACTION_TYPE), action->type); x1 = g_strdup_printf("%.2f", lmapping->area.x1); y1 = g_strdup_printf("%.2f", lmapping->area.y1); x2 = g_strdup_printf("%.2f", lmapping->area.x2); y2 = g_strdup_printf("%.2f", lmapping->area.y2); gtk_list_store_append(demo->model, &iter); gtk_list_store_set(demo->model, &iter, LINKS_ACTION_TYPE_COLUMN, enum_value->value_name, LINKS_X1_COLUMN, x1, LINKS_Y1_COLUMN, y1, LINKS_X2_COLUMN, x2, LINKS_Y2_COLUMN, y2, LINKS_ACTION_COLUMN, action, -1); g_free(x1); g_free(y1); g_free(x2); g_free(y2); g_object_weak_ref(G_OBJECT(demo->model), (GWeakNotify)poppler_action_free, action); } poppler_page_free_link_mapping(mapping); g_object_unref(page); } static void pgd_links_page_selector_value_changed(GtkSpinButton *spinbutton, PgdLinksDemo *demo) { demo->page = (gint)gtk_spin_button_get_value(spinbutton) - 1; } static void pgd_links_selection_changed(GtkTreeSelection *treeselection, PgdLinksDemo *demo) { GtkTreeModel *model; GtkTreeIter iter; if (gtk_tree_selection_get_selected(treeselection, &model, &iter)) { PopplerAction *action; gtk_tree_model_get(model, &iter, LINKS_ACTION_COLUMN, &action, -1); pgd_action_view_set_action(demo->action_view, action); } } GtkWidget *pgd_links_create_widget(PopplerDocument *document) { PgdLinksDemo *demo; GtkWidget *label; GtkWidget *vbox; GtkWidget *hbox, *page_selector; GtkWidget *button; GtkWidget *hpaned; GtkWidget *swindow, *treeview; GtkTreeSelection *selection; GtkCellRenderer *renderer; gchar *str; gint n_pages; demo = g_new0(PgdLinksDemo, 1); demo->doc = g_object_ref(document); 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_links_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); button = gtk_button_new_with_label("Get Links"); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(pgd_links_get_links), (gpointer)demo); gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0); gtk_widget_show(button); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); gtk_widget_show(hbox); demo->timer_label = gtk_label_new(NULL); gtk_label_set_markup(GTK_LABEL(demo->timer_label), "No links found"); g_object_set(G_OBJECT(demo->timer_label), "xalign", 1.0, NULL); gtk_box_pack_start(GTK_BOX(vbox), demo->timer_label, FALSE, TRUE, 0); gtk_widget_show(demo->timer_label); hpaned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); demo->action_view = pgd_action_view_new(document); swindow = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); demo->model = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER); treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(demo->model)); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 0, "Action Type", renderer, "text", LINKS_ACTION_TYPE_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 1, "X1", renderer, "text", LINKS_X1_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 2, "Y1", renderer, "text", LINKS_Y1_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 3, "X2", renderer, "text", LINKS_X2_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 4, "Y2", renderer, "text", LINKS_Y2_COLUMN, NULL); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(pgd_links_selection_changed), (gpointer)demo); gtk_container_add(GTK_CONTAINER(swindow), treeview); gtk_widget_show(treeview); gtk_paned_add1(GTK_PANED(hpaned), swindow); gtk_widget_show(swindow); gtk_paned_add2(GTK_PANED(hpaned), demo->action_view); gtk_widget_show(demo->action_view); gtk_paned_set_position(GTK_PANED(hpaned), 300); gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0); gtk_widget_show(hpaned); g_object_weak_ref(G_OBJECT(vbox), (GWeakNotify)pgd_links_free, demo); return vbox; } poppler-24.02.0/glib/demo/links.h000066400000000000000000000017231455701731300165030ustar00rootroot00000000000000/* * Copyright (C) 2007 Carlos Garcia Campos * * 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 _LINKS_H_ # define _LINKS_H_ G_BEGIN_DECLS GtkWidget *pgd_links_create_widget(PopplerDocument *document); G_END_DECLS #endif /* _LINKS_H_ */ poppler-24.02.0/glib/demo/main.c000066400000000000000000000273041455701731300163050ustar00rootroot00000000000000/* * Copyright (C) 2007 Carlos Garcia Campos * * 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 #include #include "info.h" #include "fonts.h" #include "render.h" #include "page.h" #include "outline.h" #include "links.h" #include "forms.h" #include "transitions.h" #include "images.h" #include "annots.h" #include "attachments.h" #include "layers.h" #include "text.h" #include "taggedstruct.h" #include "find.h" #include "print.h" #include "selections.h" #include "signature.h" enum { PGD_TITLE_COLUMN, PGD_NPAGE_COLUMN, PGD_WIDGET_COLUMN, N_COLUMNS }; typedef struct { const gchar *name; GtkWidget *(*create_widget)(PopplerDocument *document); } PopplerGlibDemo; static const PopplerGlibDemo demo_list[] = { { "Info", pgd_info_create_widget }, { "Fonts", pgd_fonts_create_widget }, { "Render", pgd_render_create_widget }, { "Selections", pgd_selections_create_widget }, { "Page Info", pgd_page_create_widget }, { "Outline", pgd_outline_create_widget }, { "Links", pgd_links_create_widget }, { "Forms", pgd_forms_create_widget }, { "Page Transitions", pgd_transitions_create_widget }, { "Images", pgd_images_create_widget }, { "Annotations", pgd_annots_create_widget }, { "Attachments", pgd_attachments_create_widget }, { "Layers", pgd_layers_create_widget }, { "Text", pgd_text_create_widget }, { "Tagged Structure", pgd_taggedstruct_create_widget }, { "Find", pgd_find_create_widget }, { "Print", pgd_print_create_widget }, { "Signature", pgd_signature_create_widget } }; static void pgd_demo_changed(GtkTreeSelection *selection, GtkNotebook *notebook) { GtkTreeModel *model; GtkTreeIter iter; if (gtk_tree_selection_get_selected(selection, &model, &iter)) { gint n_page; gtk_tree_model_get(model, &iter, PGD_NPAGE_COLUMN, &n_page, -1); gtk_notebook_set_current_page(notebook, n_page); } } static GtkWidget *pgd_demo_list_create(void) { GtkWidget *treeview; GtkListStore *model; GtkCellRenderer *renderer; gint i; model = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_INT, G_TYPE_POINTER); treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 0, "Demos", renderer, "text", PGD_TITLE_COLUMN, NULL); for (i = 0; i < G_N_ELEMENTS(demo_list); i++) { GtkTreeIter iter; gtk_list_store_append(model, &iter); gtk_list_store_set(model, &iter, PGD_TITLE_COLUMN, demo_list[i].name, PGD_NPAGE_COLUMN, i, -1); } g_object_unref(model); return treeview; } static GtkWidget *pgd_demo_notebook_create(PopplerDocument *document) { GtkWidget *notebook; gint i; notebook = gtk_notebook_new(); gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE); gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE); for (i = 0; i < G_N_ELEMENTS(demo_list); i++) { GtkWidget *demo_widget; demo_widget = demo_list[i].create_widget(document); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), demo_widget, NULL); gtk_widget_show(demo_widget); } return notebook; } static void pgd_demo_auth_dialog_entry_changed(GtkEditable *editable, GtkDialog *dialog) { const char *text; text = gtk_entry_get_text(GTK_ENTRY(editable)); gtk_dialog_set_response_sensitive(dialog, GTK_RESPONSE_OK, (text != NULL && *text != '\0')); g_object_set_data(G_OBJECT(dialog), "pgd-password", (gpointer)text); } static void pgd_demo_auth_dialog_entry_activated(GtkEntry *entry, GtkDialog *dialog) { gtk_dialog_response(dialog, GTK_RESPONSE_OK); } static GtkDialog *pgd_demo_get_auth_dialog(GFile *uri_file) { GtkDialog *dialog; GtkWidget *content_area; GtkWidget *password_entry; GtkWidget *hbox, *main_vbox, *vbox, *icon; GtkWidget *table; GtkWidget *label; gchar *format, *markup, *file_name; dialog = GTK_DIALOG(gtk_dialog_new()); content_area = gtk_dialog_get_content_area(dialog); /* Set the dialog up with HIG properties */ gtk_container_set_border_width(GTK_CONTAINER(dialog), 5); gtk_box_set_spacing(GTK_BOX(content_area), 2); /* 2 * 5 + 2 = 12 */ gtk_window_set_title(GTK_WINDOW(dialog), "Enter password"); gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE); gtk_window_set_icon_name(GTK_WINDOW(dialog), "dialog-password"); gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); gtk_dialog_add_buttons(dialog, "_Cancel", GTK_RESPONSE_CANCEL, "_Unlock Document", GTK_RESPONSE_OK, NULL); gtk_dialog_set_default_response(dialog, GTK_RESPONSE_OK); gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), GTK_RESPONSE_OK, FALSE); /* Build contents */ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12); gtk_container_set_border_width(GTK_CONTAINER(hbox), 5); gtk_box_pack_start(GTK_BOX(content_area), hbox, TRUE, TRUE, 0); gtk_widget_show(hbox); icon = gtk_image_new_from_icon_name("dialog-password", GTK_ICON_SIZE_DIALOG); gtk_widget_set_halign(icon, GTK_ALIGN_CENTER); gtk_widget_set_valign(icon, GTK_ALIGN_START); gtk_box_pack_start(GTK_BOX(hbox), icon, FALSE, FALSE, 0); gtk_widget_show(icon); main_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18); gtk_box_pack_start(GTK_BOX(hbox), main_vbox, TRUE, TRUE, 0); 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."); markup = g_markup_printf_escaped(format, file_name); gtk_label_set_markup(GTK_LABEL(label), markup); g_free(format); g_free(markup); g_free(file_name); gtk_box_pack_start(GTK_BOX(main_vbox), label, FALSE, FALSE, 0); gtk_widget_show(label); vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6); gtk_box_pack_start(GTK_BOX(main_vbox), vbox, FALSE, FALSE, 0); gtk_widget_show(vbox); /* The table that holds the entries */ table = gtk_grid_new(); gtk_grid_set_column_spacing(GTK_GRID(table), 12); gtk_grid_set_row_spacing(GTK_GRID(table), 6); gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); 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); g_signal_connect(password_entry, "changed", G_CALLBACK(pgd_demo_auth_dialog_entry_changed), dialog); g_signal_connect(password_entry, "activate", G_CALLBACK(pgd_demo_auth_dialog_entry_activated), dialog); gtk_grid_attach(GTK_GRID(table), label, 0, 0, 1, 1); gtk_widget_show(label); gtk_grid_attach(GTK_GRID(table), password_entry, 1, 0, 1, 1); gtk_widget_set_hexpand(password_entry, TRUE); gtk_widget_show(password_entry); gtk_label_set_mnemonic_widget(GTK_LABEL(label), password_entry); return dialog; } gint main(gint argc, gchar **argv) { PopplerDocument *document; GtkWidget *win; GtkWidget *hbox; GtkWidget *notebook; GtkWidget *treeview; GtkTreeSelection *selection; GFile *file; GTimer *timer; GError *error = NULL; GtkAccelGroup *gtk_accel; GClosure *closure; if (argc != 2) { g_print("Usage: poppler-glib-demo FILE\n"); return 1; } gtk_init(&argc, &argv); file = g_file_new_for_commandline_arg(argv[1]); timer = g_timer_new(); document = poppler_document_new_from_gfile(file, NULL, NULL, &error); g_timer_stop(timer); if (error) { while (g_error_matches(error, POPPLER_ERROR, POPPLER_ERROR_ENCRYPTED)) { GtkDialog *dialog; const gchar *password; dialog = pgd_demo_get_auth_dialog(file); if (gtk_dialog_run(dialog) != GTK_RESPONSE_OK) { g_print("Error: no password provided\n"); g_object_unref(file); return 1; } g_clear_error(&error); password = g_object_get_data(G_OBJECT(dialog), "pgd-password"); g_timer_start(timer); document = poppler_document_new_from_gfile(file, password, NULL, &error); g_timer_stop(timer); gtk_widget_destroy(GTK_WIDGET(dialog)); } if (error) { g_print("Error: %s\n", error->message); g_error_free(error); g_object_unref(file); return 1; } } g_object_unref(file); g_print("Document successfully loaded in %.4f seconds\n", g_timer_elapsed(timer, NULL)); g_timer_destroy(timer); /* Main window */ win = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768); gtk_window_set_title(GTK_WINDOW(win), "Poppler GLib Demo"); g_signal_connect(G_OBJECT(win), "delete-event", G_CALLBACK(gtk_main_quit), NULL); gtk_accel = gtk_accel_group_new(); closure = g_cclosure_new(G_CALLBACK(gtk_main_quit), NULL, NULL); gtk_accel_group_connect(gtk_accel, gdk_keyval_from_name("q"), GDK_CONTROL_MASK, 0, closure); g_closure_unref(closure); gtk_window_add_accel_group(GTK_WINDOW(win), gtk_accel); hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); treeview = pgd_demo_list_create(); gtk_box_pack_start(GTK_BOX(hbox), treeview, FALSE, TRUE, 0); gtk_widget_show(treeview); notebook = pgd_demo_notebook_create(document); gtk_box_pack_start(GTK_BOX(hbox), notebook, TRUE, TRUE, 0); gtk_widget_show(notebook); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(pgd_demo_changed), (gpointer)notebook); gtk_container_add(GTK_CONTAINER(win), hbox); gtk_widget_show(hbox); gtk_widget_show(win); gtk_main(); g_object_unref(document); return 0; } poppler-24.02.0/glib/demo/outline.c000066400000000000000000000141511455701731300170340ustar00rootroot00000000000000/* * Copyright (C) 2007 Carlos Garcia Campos * * 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 "outline.h" #include "utils.h" enum { OUTLINE_TITLE_COLUMN, OUTLINE_ACTION_TYPE_COLUMN, OUTLINE_EXPAND_COLUMN, OUTLINE_ACTION_COLUMN, N_COLUMNS }; static void build_tree(PopplerDocument *document, GtkTreeModel *model, GtkTreeIter *parent, PopplerIndexIter *iter) { do { GtkTreeIter tree_iter; PopplerIndexIter *child; PopplerAction *action; gboolean expand; gchar *markup; GEnumValue *enum_value; action = poppler_index_iter_get_action(iter); expand = poppler_index_iter_is_open(iter); if (!action) { continue; } markup = g_markup_escape_text(action->any.title, -1); enum_value = g_enum_get_value((GEnumClass *)g_type_class_ref(POPPLER_TYPE_ACTION_TYPE), action->type); if (action->type == POPPLER_ACTION_GOTO_DEST && action->goto_dest.dest->type == POPPLER_DEST_NAMED) { /* TODO */ } gtk_tree_store_append(GTK_TREE_STORE(model), &tree_iter, parent); gtk_tree_store_set(GTK_TREE_STORE(model), &tree_iter, OUTLINE_TITLE_COLUMN, markup, OUTLINE_ACTION_TYPE_COLUMN, enum_value->value_name, OUTLINE_EXPAND_COLUMN, expand, OUTLINE_ACTION_COLUMN, action, -1); g_object_weak_ref(G_OBJECT(model), (GWeakNotify)poppler_action_free, action); g_free(markup); child = poppler_index_iter_get_child(iter); if (child) { build_tree(document, model, &tree_iter, child); } poppler_index_iter_free(child); } while (poppler_index_iter_next(iter)); } GtkTreeModel *pgd_outline_create_model(PopplerDocument *document) { GtkTreeModel *model; PopplerIndexIter *iter; iter = poppler_index_iter_new(document); if (iter) { model = GTK_TREE_MODEL(gtk_tree_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_POINTER)); build_tree(document, model, NULL, iter); poppler_index_iter_free(iter); } else { GtkTreeIter tree_iter; gchar *markup; model = GTK_TREE_MODEL(gtk_list_store_new(1, G_TYPE_STRING)); gtk_list_store_append(GTK_LIST_STORE(model), &tree_iter); markup = g_strdup_printf("%s", "The document doesn't contain outline"); gtk_list_store_set(GTK_LIST_STORE(model), &tree_iter, 0, markup, -1); g_free(markup); } return model; } static void expand_open_links(GtkTreeView *tree_view, GtkTreeModel *model, GtkTreeIter *parent) { GtkTreeIter iter; gboolean expand; if (gtk_tree_model_iter_children(model, &iter, parent)) { do { gtk_tree_model_get(model, &iter, OUTLINE_EXPAND_COLUMN, &expand, -1); if (expand) { GtkTreePath *path; path = gtk_tree_model_get_path(model, &iter); gtk_tree_view_expand_row(tree_view, path, FALSE); gtk_tree_path_free(path); } expand_open_links(tree_view, model, &iter); } while (gtk_tree_model_iter_next(model, &iter)); } } static void pgd_outline_selection_changed(GtkTreeSelection *treeselection, GtkWidget *action_view) { GtkTreeModel *model; GtkTreeIter iter; if (gtk_tree_selection_get_selected(treeselection, &model, &iter)) { PopplerAction *action; gtk_tree_model_get(model, &iter, OUTLINE_ACTION_COLUMN, &action, -1); pgd_action_view_set_action(action_view, action); } } GtkWidget *pgd_outline_create_widget(PopplerDocument *document) { GtkWidget *swindow; GtkWidget *treeview; GtkTreeModel *model; GtkCellRenderer *renderer; GtkTreeSelection *selection; GtkWidget *hpaned, *action; hpaned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); action = pgd_action_view_new(document); swindow = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); model = pgd_outline_create_model(document); treeview = gtk_tree_view_new_with_model(model); g_object_unref(model); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 0, "Title", renderer, "markup", OUTLINE_TITLE_COLUMN, NULL); g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL); g_object_set(G_OBJECT(gtk_tree_view_get_column(GTK_TREE_VIEW(treeview), 0)), "expand", TRUE, NULL); if (GTK_IS_TREE_STORE(model)) { renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 1, "Action Type", renderer, "text", OUTLINE_ACTION_TYPE_COLUMN, NULL); expand_open_links(GTK_TREE_VIEW(treeview), model, NULL); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(pgd_outline_selection_changed), (gpointer)action); } else { gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), GTK_SELECTION_NONE); } gtk_container_add(GTK_CONTAINER(swindow), treeview); gtk_widget_show(treeview); gtk_paned_add1(GTK_PANED(hpaned), swindow); gtk_widget_show(swindow); gtk_paned_add2(GTK_PANED(hpaned), action); gtk_widget_show(action); gtk_paned_set_position(GTK_PANED(hpaned), 300); return hpaned; } poppler-24.02.0/glib/demo/outline.h000066400000000000000000000017331455701731300170430ustar00rootroot00000000000000/* * Copyright (C) 2007 Carlos Garcia Campos * * 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 _OUTLINE_H_ # define _OUTLINE_H_ G_BEGIN_DECLS GtkWidget *pgd_outline_create_widget(PopplerDocument *document); G_END_DECLS #endif /* _OUTLINE_H_ */ poppler-24.02.0/glib/demo/page.c000066400000000000000000000233111455701731300162670ustar00rootroot00000000000000/* * Copyright (C) 2007 Carlos Garcia Campos * * 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 "config.h" #include #include "page.h" #include "utils.h" typedef struct { PopplerDocument *doc; GtkWidget *index; GtkWidget *label; GtkWidget *size; GtkWidget *duration; GtkWidget *thumbnail; GtkWidget *thumbnail_size; gint page; } PgdPageDemo; static void pgd_page_free(PgdPageDemo *demo) { if (!demo) { return; } if (demo->doc) { g_object_unref(demo->doc); demo->doc = NULL; } g_free(demo); } #ifndef POPPLER_WITH_GDK static void image_set_from_surface(GtkImage *gtkimage, cairo_surface_t *surface) { GdkPixbuf *pixbuf; cairo_surface_t *image; cairo_t *cr; gboolean has_alpha; gint width, height; cairo_format_t surface_format; gint pixbuf_n_channels; gint pixbuf_rowstride; guchar *pixbuf_pixels; gint x, y; width = cairo_image_surface_get_width(surface); height = cairo_image_surface_get_height(surface); surface_format = cairo_image_surface_get_format(surface); has_alpha = (surface_format == CAIRO_FORMAT_ARGB32); pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height); pixbuf_n_channels = gdk_pixbuf_get_n_channels(pixbuf); pixbuf_rowstride = gdk_pixbuf_get_rowstride(pixbuf); pixbuf_pixels = gdk_pixbuf_get_pixels(pixbuf); image = cairo_image_surface_create_for_data(pixbuf_pixels, surface_format, width, height, pixbuf_rowstride); cr = cairo_create(image); cairo_set_source_surface(cr, surface, 0, 0); if (has_alpha) { cairo_mask_surface(cr, surface, 0, 0); } else { cairo_paint(cr); } cairo_destroy(cr); cairo_surface_destroy(image); for (y = 0; y < height; y++) { guchar *p = pixbuf_pixels + y * pixbuf_rowstride; for (x = 0; x < width; x++) { guchar tmp; # if G_BYTE_ORDER == G_LITTLE_ENDIAN tmp = p[0]; p[0] = p[2]; p[2] = tmp; p[3] = (has_alpha) ? p[3] : 0xff; # else tmp = p[0]; p[0] = (has_alpha) ? p[3] : 0xff; p[3] = p[2]; p[2] = p[1]; p[1] = tmp; # endif p += pixbuf_n_channels; } } gtk_image_set_from_pixbuf(gtkimage, pixbuf); g_object_unref(pixbuf); } #endif /* !POPPLER_WITH_GDK */ static void pgd_page_set_page(PgdPageDemo *demo, PopplerPage *page) { #ifdef POPPLER_WITH_GDK GdkPixbuf *thumbnail; #else cairo_surface_t *thumbnail; #endif gchar *str; str = page ? g_strdup_printf("%d", poppler_page_get_index(page)) : NULL; gtk_label_set_text(GTK_LABEL(demo->index), str); g_free(str); if (page) { str = poppler_page_get_label(page); gtk_label_set_text(GTK_LABEL(demo->label), str); g_free(str); } else { gtk_label_set_text(GTK_LABEL(demo->label), NULL); } if (page) { gdouble width, height; poppler_page_get_size(page, &width, &height); str = g_strdup_printf("%.2f x %.2f", width, height); gtk_label_set_text(GTK_LABEL(demo->size), str); g_free(str); } else { gtk_label_set_text(GTK_LABEL(demo->size), NULL); } str = page ? g_strdup_printf("%.2f seconds", poppler_page_get_duration(page)) : NULL; gtk_label_set_text(GTK_LABEL(demo->duration), str); g_free(str); #ifdef POPPLER_WITH_GDK thumbnail = page ? poppler_page_get_thumbnail_pixbuf(page) : NULL; #else thumbnail = page ? poppler_page_get_thumbnail(page) : NULL; #endif if (thumbnail) { gint width, height; poppler_page_get_thumbnail_size(page, &width, &height); str = g_strdup_printf("%d x %d", width, height); gtk_label_set_text(GTK_LABEL(demo->thumbnail_size), str); g_free(str); #ifdef POPPLER_WITH_GDK gtk_image_set_from_pixbuf(GTK_IMAGE(demo->thumbnail), thumbnail); g_object_unref(thumbnail); #else image_set_from_surface(GTK_IMAGE(demo->thumbnail), thumbnail); cairo_surface_destroy(thumbnail); #endif } else { str = g_strdup("No thumbnail found"); gtk_label_set_markup(GTK_LABEL(demo->thumbnail_size), str); g_free(str); gtk_image_set_from_icon_name(GTK_IMAGE(demo->thumbnail), "image-missing", GTK_ICON_SIZE_DIALOG); } } static void pgd_page_get_info(GtkWidget *button, PgdPageDemo *demo) { PopplerPage *page; page = poppler_document_get_page(demo->doc, demo->page); pgd_page_set_page(demo, page); g_object_unref(page); } static void pgd_page_page_selector_value_changed(GtkSpinButton *spinbutton, PgdPageDemo *demo) { demo->page = (gint)gtk_spin_button_get_value(spinbutton) - 1; } GtkWidget *pgd_page_create_widget(PopplerDocument *document) { PgdPageDemo *demo; GtkWidget *vbox; GtkWidget *hbox, *page_selector; GtkWidget *button; GtkWidget *frame; GtkWidget *table; GtkWidget *label; GtkWidget *thumnail_box; gchar *str; gint n_pages; gint row = 0; demo = g_new0(PgdPageDemo, 1); demo->doc = g_object_ref(document); 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_page_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); button = gtk_button_new_with_label("Get Info"); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(pgd_page_get_info), (gpointer)demo); gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0); gtk_widget_show(button); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); gtk_widget_show(hbox); hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); frame = gtk_frame_new(NULL); gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE); label = gtk_label_new(NULL); gtk_label_set_markup(GTK_LABEL(label), "Page Properties"); gtk_frame_set_label_widget(GTK_FRAME(frame), label); gtk_widget_show(label); 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); pgd_table_add_property_with_value_widget(GTK_GRID(table), "Page Index:", &(demo->index), NULL, &row); pgd_table_add_property_with_value_widget(GTK_GRID(table), "Page Label:", &(demo->label), NULL, &row); pgd_table_add_property_with_value_widget(GTK_GRID(table), "Page Size:", &(demo->size), NULL, &row); pgd_table_add_property_with_value_widget(GTK_GRID(table), "Page Duration:", &(demo->duration), NULL, &row); gtk_container_add(GTK_CONTAINER(frame), table); gtk_widget_show(table); gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0); gtk_widget_show(frame); frame = gtk_frame_new(NULL); gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE); gtk_frame_set_label_align(GTK_FRAME(frame), 0.5, 0.5); label = gtk_label_new(NULL); gtk_label_set_markup(GTK_LABEL(label), "Page Thumbnail"); gtk_frame_set_label_widget(GTK_FRAME(frame), label); gtk_widget_show(label); 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); gtk_widget_show(demo->thumbnail); demo->thumbnail_size = gtk_label_new(NULL); g_object_set(G_OBJECT(demo->thumbnail_size), "xalign", 0.5, NULL); gtk_box_pack_start(GTK_BOX(thumnail_box), demo->thumbnail_size, TRUE, TRUE, 0); gtk_widget_show(demo->thumbnail_size); gtk_container_add(GTK_CONTAINER(frame), thumnail_box); gtk_widget_show(thumnail_box); gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0); gtk_widget_show(frame); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); gtk_widget_show(hbox); g_object_weak_ref(G_OBJECT(vbox), (GWeakNotify)pgd_page_free, (gpointer)demo); return vbox; } poppler-24.02.0/glib/demo/page.h000066400000000000000000000017171455701731300163020ustar00rootroot00000000000000/* * Copyright (C) 2007 Carlos Garcia Campos * * 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 _PAGE_H_ # define _PAGE_H_ G_BEGIN_DECLS GtkWidget *pgd_page_create_widget(PopplerDocument *document); G_END_DECLS #endif /* _PAGE_H_ */ poppler-24.02.0/glib/demo/print.c000066400000000000000000000142051455701731300165110ustar00rootroot00000000000000/* * Copyright (C) 2009 Carlos Garcia Campos * * 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 "config.h" #include #include "print.h" typedef enum { PRINT_DOCUMENT, PRINT_DOCUMENT_MARKUPS, PRINT_DOCUMENT_STAMPS } PgdPrintOptions; typedef struct { PopplerDocument *doc; GtkWidget *options_combo; PgdPrintOptions options; } PgdPrintDemo; #define PGD_PRINT_OPTIONS "pgd-print-options" static void pgd_print_free(PgdPrintDemo *demo) { if (!demo) { return; } if (demo->doc) { g_object_unref(demo->doc); demo->doc = NULL; } g_free(demo); } static void pgd_print_begin_print(GtkPrintOperation *op, GtkPrintContext *context, PgdPrintDemo *demo) { gtk_print_operation_set_n_pages(op, poppler_document_get_n_pages(demo->doc)); } static void pgd_print_draw_page(GtkPrintOperation *op, GtkPrintContext *context, gint page_nr, PgdPrintDemo *demo) { PopplerPage *page; cairo_t *cr; #if 0 GtkPrintSettings *settings; #endif PgdPrintOptions options; PopplerPrintFlags flags = 0; page = poppler_document_get_page(demo->doc, page_nr); if (!page) { return; } #if 0 settings = gtk_print_operation_get_print_settings (op); options = gtk_print_settings_get_int_with_default (settings, PGD_PRINT_OPTIONS, PRINT_DOCUMENT_MARKUPS); #else /* Workaround for gtk+ bug, we need to save the options ourselves */ options = demo->options; #endif switch (options) { case PRINT_DOCUMENT: flags |= POPPLER_PRINT_DOCUMENT; break; case PRINT_DOCUMENT_MARKUPS: flags |= POPPLER_PRINT_MARKUP_ANNOTS; break; case PRINT_DOCUMENT_STAMPS: flags |= POPPLER_PRINT_STAMP_ANNOTS_ONLY; break; default: g_assert_not_reached(); } cr = gtk_print_context_get_cairo_context(context); poppler_page_render_for_printing_with_options(page, cr, flags); g_object_unref(page); } static GObject *pgd_print_create_custom_widget(GtkPrintOperation *op, PgdPrintDemo *demo) { GtkWidget *hbox; GtkWidget *label, *combo; GtkPrintSettings *settings; PgdPrintOptions options; settings = gtk_print_operation_get_print_settings(op); options = gtk_print_settings_get_int_with_default(settings, PGD_PRINT_OPTIONS, PRINT_DOCUMENT_MARKUPS); hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); gtk_container_set_border_width(GTK_CONTAINER(hbox), 12); label = gtk_label_new("Print: "); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); gtk_widget_show(label); combo = gtk_combo_box_text_new(); gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "Document"); gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "Document and markup"); gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "Document and stamps"); demo->options_combo = combo; gtk_combo_box_set_active(GTK_COMBO_BOX(combo), options); gtk_box_pack_start(GTK_BOX(hbox), combo, FALSE, FALSE, 0); gtk_widget_show(combo); return G_OBJECT(hbox); } static void pgd_print_custom_widget_apply(GtkPrintOperation *op, GtkWidget *widget, PgdPrintDemo *demo) { GtkPrintSettings *settings; PgdPrintOptions options; settings = gtk_print_operation_get_print_settings(op); options = gtk_combo_box_get_active(GTK_COMBO_BOX(demo->options_combo)); /* Workaround for gtk+ bug, we need to save the options ourselves */ demo->options = options; gtk_print_settings_set_int(settings, PGD_PRINT_OPTIONS, options); } static void pgd_print_print(GtkWidget *button, PgdPrintDemo *demo) { GtkPrintOperation *op; GError *error = NULL; op = gtk_print_operation_new(); gtk_print_operation_set_custom_tab_label(op, "PDF Options"); g_signal_connect(op, "begin-print", G_CALLBACK(pgd_print_begin_print), demo); g_signal_connect(op, "draw-page", G_CALLBACK(pgd_print_draw_page), demo); g_signal_connect(op, "create_custom_widget", G_CALLBACK(pgd_print_create_custom_widget), demo); g_signal_connect(op, "custom_widget_apply", G_CALLBACK(pgd_print_custom_widget_apply), demo); gtk_print_operation_run(op, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, GTK_WINDOW(gtk_widget_get_toplevel(button)), &error); if (error) { GtkWidget *dialog; dialog = gtk_message_dialog_new(GTK_WINDOW(gtk_widget_get_toplevel(button)), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", error->message); g_error_free(error); g_signal_connect(dialog, "response", G_CALLBACK(gtk_widget_destroy), NULL); gtk_widget_show(dialog); } g_object_unref(op); } GtkWidget *pgd_print_create_widget(PopplerDocument *document) { PgdPrintDemo *demo; GtkWidget *vbox; GtkWidget *hbox; GtkWidget *button; demo = g_new0(PgdPrintDemo, 1); demo->doc = g_object_ref(document); vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12); hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); button = gtk_button_new_with_label("Print..."); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(pgd_print_print), (gpointer)demo); gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0); gtk_widget_show(button); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); gtk_widget_show(hbox); g_object_weak_ref(G_OBJECT(vbox), (GWeakNotify)pgd_print_free, (gpointer)demo); return vbox; } poppler-24.02.0/glib/demo/print.h000066400000000000000000000017231455701731300165170ustar00rootroot00000000000000/* * Copyright (C) 2009 Carlos Garcia Campos * * 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 _PRINT_H_ # define _PRINT_H_ G_BEGIN_DECLS GtkWidget *pgd_print_create_widget(PopplerDocument *document); G_END_DECLS #endif /* _PRINT_H_ */ poppler-24.02.0/glib/demo/render.c000066400000000000000000000327741455701731300166470ustar00rootroot00000000000000/* * Copyright (C) 2007 Carlos Garcia Campos * * 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 "config.h" #include #include #include "render.h" typedef struct { PopplerDocument *doc; /* Properties */ gint page; gdouble scale; gint rotate; GdkRectangle slice; gboolean printing; GtkWidget *swindow; GtkWidget *darea; GtkWidget *slice_x; GtkWidget *slice_y; GtkWidget *slice_w; GtkWidget *slice_h; GtkWidget *timer_label; cairo_surface_t *surface; } PgdRenderDemo; static void pgd_render_free(PgdRenderDemo *demo) { if (!demo) { return; } if (demo->doc) { g_object_unref(demo->doc); demo->doc = NULL; } if (demo->surface) { cairo_surface_destroy(demo->surface); demo->surface = NULL; } g_free(demo); } static gboolean pgd_render_drawing_area_draw(GtkWidget *area, cairo_t *cr, PgdRenderDemo *demo) { if (!demo->surface) { return FALSE; } cairo_set_source_surface(cr, demo->surface, 0, 0); cairo_paint(cr); return TRUE; } static void pgd_render_start(GtkButton *button, PgdRenderDemo *demo) { PopplerPage *page; gdouble page_width, page_height; gdouble width, height; gint x, y; gchar *str; GTimer *timer; cairo_t *cr; page = poppler_document_get_page(demo->doc, demo->page); if (!page) { return; } if (demo->surface) { cairo_surface_destroy(demo->surface); } demo->surface = NULL; poppler_page_get_size(page, &page_width, &page_height); if (demo->rotate == 0 || demo->rotate == 180) { width = demo->slice.width * demo->scale; height = demo->slice.height * demo->scale; x = demo->slice.x * demo->scale; y = demo->slice.y * demo->scale; } else { width = demo->slice.height * demo->scale; height = demo->slice.width * demo->scale; x = demo->slice.y * demo->scale; y = demo->slice.x * demo->scale; } timer = g_timer_new(); demo->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); cr = cairo_create(demo->surface); cairo_save(cr); switch (demo->rotate) { case 90: cairo_translate(cr, x + width, -y); break; case 180: cairo_translate(cr, x + width, y + height); break; case 270: cairo_translate(cr, -x, y + height); break; default: cairo_translate(cr, -x, -y); } if (demo->scale != 1.0) { cairo_scale(cr, demo->scale, demo->scale); } if (demo->rotate != 0) { cairo_rotate(cr, demo->rotate * G_PI / 180.0); } if (demo->printing) { poppler_page_render_for_printing(page, cr); } else { poppler_page_render(page, cr); } cairo_restore(cr); cairo_set_operator(cr, CAIRO_OPERATOR_DEST_OVER); cairo_set_source_rgb(cr, 1., 1., 1.); cairo_paint(cr); g_timer_stop(timer); cairo_destroy(cr); g_object_unref(page); str = g_strdup_printf("Page rendered in %.4f seconds", g_timer_elapsed(timer, NULL)); gtk_label_set_markup(GTK_LABEL(demo->timer_label), str); g_free(str); g_timer_destroy(timer); gtk_widget_set_size_request(demo->darea, width, height); gtk_widget_queue_draw(demo->darea); } static void pgd_render_slice_selector_setup(PgdRenderDemo *demo) { PopplerPage *page; gdouble width, height; page = poppler_document_get_page(demo->doc, demo->page); if (!page) { return; } poppler_page_get_size(page, &width, &height); gtk_spin_button_set_range(GTK_SPIN_BUTTON(demo->slice_x), 0, width); gtk_spin_button_set_range(GTK_SPIN_BUTTON(demo->slice_y), 0, height); gtk_spin_button_set_range(GTK_SPIN_BUTTON(demo->slice_w), 0, width); gtk_spin_button_set_range(GTK_SPIN_BUTTON(demo->slice_h), 0, height); gtk_spin_button_set_value(GTK_SPIN_BUTTON(demo->slice_x), 0); gtk_spin_button_set_value(GTK_SPIN_BUTTON(demo->slice_y), 0); gtk_spin_button_set_value(GTK_SPIN_BUTTON(demo->slice_w), width); gtk_spin_button_set_value(GTK_SPIN_BUTTON(demo->slice_h), height); g_object_unref(page); } static void pgd_render_page_selector_value_changed(GtkSpinButton *spinbutton, PgdRenderDemo *demo) { demo->page = (gint)gtk_spin_button_get_value(spinbutton) - 1; pgd_render_slice_selector_setup(demo); } static void pgd_render_scale_selector_value_changed(GtkSpinButton *spinbutton, PgdRenderDemo *demo) { demo->scale = gtk_spin_button_get_value(spinbutton); } static void pgd_render_rotate_selector_changed(GtkComboBox *combobox, PgdRenderDemo *demo) { demo->rotate = gtk_combo_box_get_active(combobox) * 90; } static void pgd_render_printing_selector_changed(GtkToggleButton *tooglebutton, PgdRenderDemo *demo) { demo->printing = 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)); demo->slice.y = (gint)gtk_spin_button_get_value(GTK_SPIN_BUTTON(demo->slice_y)); demo->slice.width = (gint)gtk_spin_button_get_value(GTK_SPIN_BUTTON(demo->slice_w)); demo->slice.height = (gint)gtk_spin_button_get_value(GTK_SPIN_BUTTON(demo->slice_h)); } GtkWidget *pgd_render_properties_selector_create(PgdRenderDemo *demo) { GtkWidget *hbox, *vbox; GtkWidget *label; GtkWidget *page_hbox, *page_selector; GtkWidget *scale_hbox, *scale_selector; GtkWidget *rotate_hbox, *rotate_selector; GtkWidget *printing_selector; GtkWidget *slice_hbox; GtkWidget *button; gint n_pages; gchar *str; n_pages = poppler_document_get_n_pages(demo->doc); vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6); hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12); gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); gtk_widget_show(hbox); page_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); label = gtk_label_new("Page:"); gtk_box_pack_start(GTK_BOX(page_hbox), label, TRUE, 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_render_page_selector_value_changed), (gpointer)demo); gtk_box_pack_start(GTK_BOX(page_hbox), page_selector, TRUE, 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(page_hbox), label, TRUE, TRUE, 0); gtk_widget_show(label); g_free(str); gtk_box_pack_start(GTK_BOX(hbox), page_hbox, FALSE, TRUE, 0); gtk_widget_show(page_hbox); 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_render_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); rotate_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); label = gtk_label_new("Rotate:"); gtk_box_pack_start(GTK_BOX(rotate_hbox), label, TRUE, TRUE, 0); gtk_widget_show(label); rotate_selector = gtk_combo_box_text_new(); gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(rotate_selector), "0"); gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(rotate_selector), "90"); gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(rotate_selector), "180"); gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(rotate_selector), "270"); gtk_combo_box_set_active(GTK_COMBO_BOX(rotate_selector), 0); g_signal_connect(G_OBJECT(rotate_selector), "changed", G_CALLBACK(pgd_render_rotate_selector_changed), (gpointer)demo); gtk_box_pack_start(GTK_BOX(rotate_hbox), rotate_selector, TRUE, TRUE, 0); gtk_widget_show(rotate_selector); 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); hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12); gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); gtk_widget_show(hbox); slice_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); label = gtk_label_new("x:"); gtk_box_pack_start(GTK_BOX(slice_hbox), label, TRUE, TRUE, 0); gtk_widget_show(label); demo->slice_x = gtk_spin_button_new_with_range(0, 0, 1.0); g_signal_connect(G_OBJECT(demo->slice_x), "value-changed", G_CALLBACK(pgd_render_slice_selector_value_changed), (gpointer)demo); gtk_box_pack_start(GTK_BOX(slice_hbox), demo->slice_x, TRUE, TRUE, 0); gtk_widget_show(demo->slice_x); gtk_box_pack_start(GTK_BOX(hbox), slice_hbox, FALSE, TRUE, 0); gtk_widget_show(slice_hbox); slice_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); label = gtk_label_new("y:"); gtk_box_pack_start(GTK_BOX(slice_hbox), label, TRUE, TRUE, 0); gtk_widget_show(label); demo->slice_y = gtk_spin_button_new_with_range(0, 0, 1.0); g_signal_connect(G_OBJECT(demo->slice_y), "value-changed", G_CALLBACK(pgd_render_slice_selector_value_changed), (gpointer)demo); gtk_box_pack_start(GTK_BOX(slice_hbox), demo->slice_y, TRUE, TRUE, 0); gtk_widget_show(demo->slice_y); gtk_box_pack_start(GTK_BOX(hbox), slice_hbox, FALSE, TRUE, 0); gtk_widget_show(slice_hbox); slice_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); label = gtk_label_new("width:"); gtk_box_pack_start(GTK_BOX(slice_hbox), label, TRUE, TRUE, 0); gtk_widget_show(label); demo->slice_w = gtk_spin_button_new_with_range(0, 0, 1.0); g_signal_connect(G_OBJECT(demo->slice_w), "value-changed", G_CALLBACK(pgd_render_slice_selector_value_changed), (gpointer)demo); gtk_box_pack_start(GTK_BOX(slice_hbox), demo->slice_w, TRUE, TRUE, 0); gtk_widget_show(demo->slice_w); gtk_box_pack_start(GTK_BOX(hbox), slice_hbox, FALSE, TRUE, 0); gtk_widget_show(slice_hbox); slice_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); label = gtk_label_new("height:"); gtk_box_pack_start(GTK_BOX(slice_hbox), label, TRUE, TRUE, 0); gtk_widget_show(label); demo->slice_h = gtk_spin_button_new_with_range(0, 0, 1.0); g_signal_connect(G_OBJECT(demo->slice_h), "value-changed", G_CALLBACK(pgd_render_slice_selector_value_changed), (gpointer)demo); gtk_box_pack_start(GTK_BOX(slice_hbox), demo->slice_h, TRUE, TRUE, 0); gtk_widget_show(demo->slice_h); gtk_box_pack_start(GTK_BOX(hbox), slice_hbox, FALSE, TRUE, 0); gtk_widget_show(slice_hbox); pgd_render_slice_selector_setup(demo); button = gtk_button_new_with_label("Render"); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(pgd_render_start), (gpointer)demo); gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, TRUE, 0); gtk_widget_show(button); demo->timer_label = gtk_label_new(NULL); gtk_label_set_markup(GTK_LABEL(demo->timer_label), "No page rendered"); g_object_set(G_OBJECT(demo->timer_label), "xalign", 1.0, NULL); gtk_box_pack_end(GTK_BOX(vbox), demo->timer_label, FALSE, TRUE, 0); gtk_widget_show(demo->timer_label); return vbox; } GtkWidget *pgd_render_create_widget(PopplerDocument *document) { PgdRenderDemo *demo; GtkWidget *vbox, *hbox; demo = g_new0(PgdRenderDemo, 1); demo->doc = g_object_ref(document); demo->scale = 1.0; vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6); hbox = pgd_render_properties_selector_create(demo); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 6); gtk_widget_show(hbox); demo->darea = gtk_drawing_area_new(); g_signal_connect(G_OBJECT(demo->darea), "draw", G_CALLBACK(pgd_render_drawing_area_draw), (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); gtk_widget_show(demo->swindow); g_object_weak_ref(G_OBJECT(demo->swindow), (GWeakNotify)pgd_render_free, (gpointer)demo); return vbox; } poppler-24.02.0/glib/demo/render.h000066400000000000000000000017271455701731300166460ustar00rootroot00000000000000/* * Copyright (C) 2007 Carlos Garcia Campos * * 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 _RENDER_H_ # define _RENDER_H_ G_BEGIN_DECLS GtkWidget *pgd_render_create_widget(PopplerDocument *document); G_END_DECLS #endif /* _RENDER_H_ */ poppler-24.02.0/glib/demo/selections.c000066400000000000000000000460621455701731300175330ustar00rootroot00000000000000/* * Copyright (C) 2010 Carlos Garcia Campos * * 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 "config.h" #include #include #include "selections.h" typedef struct { PopplerDocument *doc; /* Properties */ gint page_index; gdouble scale; GtkWidget *swindow; GtkWidget *darea; GtkWidget *fg_color_button; GtkWidget *bg_color_button; GtkWidget *copy_button; PopplerPage *page; cairo_surface_t *surface; GdkPoint start; GdkPoint stop; PopplerRectangle doc_area; cairo_surface_t *selection_surface; PopplerSelectionStyle style; PopplerColor glyph_color; PopplerColor background_color; guint selections_idle; cairo_region_t *selection_region; cairo_region_t *selected_region; GdkCursorType cursor; gchar *selected_text; } PgdSelectionsDemo; static void pgd_selections_clear_selections(PgdSelectionsDemo *demo) { demo->start.x = -1; if (demo->selection_surface) { cairo_surface_destroy(demo->selection_surface); demo->selection_surface = NULL; } if (demo->selection_region) { cairo_region_destroy(demo->selection_region); demo->selection_region = NULL; } if (demo->selected_text) { g_free(demo->selected_text); demo->selected_text = NULL; } if (demo->selected_region) { cairo_region_destroy(demo->selected_region); demo->selected_region = NULL; } } static void pgd_selections_free(PgdSelectionsDemo *demo) { if (!demo) { return; } if (demo->selections_idle > 0) { g_source_remove(demo->selections_idle); demo->selections_idle = 0; } if (demo->doc) { g_object_unref(demo->doc); demo->doc = NULL; } if (demo->page) { g_object_unref(demo->page); demo->page = NULL; } if (demo->surface) { cairo_surface_destroy(demo->surface); demo->surface = NULL; } pgd_selections_clear_selections(demo); g_free(demo); } static void pgd_selections_update_selection_region(PgdSelectionsDemo *demo) { PopplerRectangle area = { 0, 0, 0, 0 }; if (demo->selection_region) { cairo_region_destroy(demo->selection_region); } poppler_page_get_size(demo->page, &area.x2, &area.y2); demo->selection_region = poppler_page_get_selected_region(demo->page, 1.0, POPPLER_SELECTION_GLYPH, &area); } static void pgd_selections_update_selected_text(PgdSelectionsDemo *demo) { gchar *text; if (demo->selected_region) { cairo_region_destroy(demo->selected_region); } demo->selected_region = poppler_page_get_selected_region(demo->page, 1.0, demo->style, &demo->doc_area); if (demo->selected_text) { g_free(demo->selected_text); } demo->selected_text = NULL; text = poppler_page_get_selected_text(demo->page, demo->style, &demo->doc_area); if (text) { /* For copying text from the document to the clipboard, we want a normalization * that preserves 'canonical equivalence' i.e. that text after normalization * is not visually different than the original text. Issue #724 */ demo->selected_text = g_utf8_normalize(text, -1, G_NORMALIZE_NFC); g_free(text); gtk_widget_set_sensitive(demo->copy_button, TRUE); } } static void pgd_selections_update_cursor(PgdSelectionsDemo *demo, GdkCursorType cursor_type) { GdkWindow *window = gtk_widget_get_window(demo->darea); 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->darea), cursor_type); } demo->cursor = cursor_type; gdk_window_set_cursor(window, cursor); gdk_display_flush(gtk_widget_get_display(demo->darea)); if (cursor) { g_object_unref(cursor); } } static gboolean pgd_selections_render_selections(PgdSelectionsDemo *demo) { PopplerRectangle doc_area; gdouble page_width, page_height; cairo_t *cr; if (!demo->page || demo->start.x == -1) { demo->selections_idle = 0; return FALSE; } poppler_page_get_size(demo->page, &page_width, &page_height); page_width *= demo->scale; page_height *= demo->scale; doc_area.x1 = demo->start.x / demo->scale; doc_area.y1 = demo->start.y / demo->scale; doc_area.x2 = demo->stop.x / demo->scale; doc_area.y2 = demo->stop.y / demo->scale; if (demo->selection_surface) { cairo_surface_destroy(demo->selection_surface); } demo->selection_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, page_width, page_height); cr = cairo_create(demo->selection_surface); if (demo->scale != 1.0) { cairo_scale(cr, demo->scale, demo->scale); } poppler_page_render_selection(demo->page, cr, &doc_area, &demo->doc_area, demo->style, &demo->glyph_color, &demo->background_color); cairo_destroy(cr); demo->doc_area = doc_area; gtk_widget_queue_draw(demo->darea); demo->selections_idle = 0; return FALSE; } static gboolean pgd_selections_drawing_area_draw(GtkWidget *area, cairo_t *cr, PgdSelectionsDemo *demo) { if (!demo->surface) { return FALSE; } cairo_save(cr); cairo_set_source_surface(cr, demo->surface, 0, 0); cairo_paint(cr); cairo_restore(cr); if (demo->selection_surface) { cairo_set_source_surface(cr, demo->selection_surface, 0, 0); cairo_paint(cr); } return TRUE; } static gboolean pgd_selections_drawing_area_button_press(GtkWidget *area, GdkEventButton *event, PgdSelectionsDemo *demo) { if (!demo->page) { return FALSE; } if (event->button != 1) { return FALSE; } demo->start.x = event->x; demo->start.y = event->y; demo->stop = demo->start; switch (event->type) { case GDK_2BUTTON_PRESS: demo->style = POPPLER_SELECTION_WORD; break; case GDK_3BUTTON_PRESS: demo->style = POPPLER_SELECTION_LINE; break; default: demo->style = POPPLER_SELECTION_GLYPH; } pgd_selections_render_selections(demo); return TRUE; } static gboolean pgd_selections_drawing_area_motion_notify(GtkWidget *area, GdkEventMotion *event, PgdSelectionsDemo *demo) { if (!demo->page) { return FALSE; } if (demo->start.x != -1) { demo->stop.x = event->x; demo->stop.y = event->y; if (demo->selections_idle == 0) { demo->selections_idle = g_idle_add((GSourceFunc)pgd_selections_render_selections, demo); } } else { gboolean over_text; over_text = cairo_region_contains_point(demo->selection_region, event->x / demo->scale, event->y / demo->scale); pgd_selections_update_cursor(demo, over_text ? GDK_XTERM : GDK_LAST_CURSOR); } return TRUE; } static gboolean pgd_selections_drawing_area_button_release(GtkWidget *area, GdkEventButton *event, PgdSelectionsDemo *demo) { if (!demo->page) { return FALSE; } if (event->button != 1) { return FALSE; } if (demo->start.x != -1) { pgd_selections_update_selected_text(demo); } demo->start.x = -1; if (demo->selections_idle > 0) { g_source_remove(demo->selections_idle); demo->selections_idle = 0; } return TRUE; } static void pgd_selections_drawing_area_realize(GtkWidget *area, PgdSelectionsDemo *demo) { GtkStyleContext *style_context = gtk_widget_get_style_context(area); GdkRGBA rgba; gtk_widget_add_events(area, GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); g_object_set(area, "has-tooltip", TRUE, NULL); gtk_style_context_get_color(style_context, GTK_STATE_FLAG_SELECTED, &rgba); gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(demo->fg_color_button), &rgba); gtk_style_context_get(style_context, GTK_STATE_FLAG_SELECTED, "background-color", &rgba, NULL); gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(demo->bg_color_button), &rgba); } static gboolean pgd_selections_drawing_area_query_tooltip(GtkWidget *area, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip, PgdSelectionsDemo *demo) { gboolean over_selection; if (!demo->selected_text) { return FALSE; } over_selection = cairo_region_contains_point(demo->selected_region, x / demo->scale, y / demo->scale); if (over_selection) { GdkRectangle selection_area; cairo_region_get_extents(demo->selected_region, (cairo_rectangle_int_t *)&selection_area); selection_area.x *= demo->scale; selection_area.y *= demo->scale; selection_area.width *= demo->scale; selection_area.height *= demo->scale; gtk_tooltip_set_text(tooltip, demo->selected_text); gtk_tooltip_set_tip_area(tooltip, &selection_area); return TRUE; } return FALSE; } static void pgd_selections_render(GtkButton *button, PgdSelectionsDemo *demo) { gdouble page_width, page_height; cairo_t *cr; if (!demo->page) { demo->page = poppler_document_get_page(demo->doc, demo->page_index); } if (!demo->page) { return; } pgd_selections_clear_selections(demo); pgd_selections_update_selection_region(demo); gtk_widget_set_sensitive(demo->copy_button, FALSE); if (demo->surface) { cairo_surface_destroy(demo->surface); } demo->surface = NULL; poppler_page_get_size(demo->page, &page_width, &page_height); page_width *= demo->scale; page_height *= demo->scale; demo->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, page_width, page_height); cr = cairo_create(demo->surface); cairo_save(cr); if (demo->scale != 1.0) { cairo_scale(cr, demo->scale, demo->scale); } poppler_page_render(demo->page, cr); cairo_restore(cr); cairo_set_operator(cr, CAIRO_OPERATOR_DEST_OVER); cairo_set_source_rgb(cr, 1., 1., 1.); cairo_paint(cr); cairo_destroy(cr); gtk_widget_set_size_request(demo->darea, page_width, page_height); gtk_widget_queue_draw(demo->darea); } static void pgd_selections_copy(GtkButton *button, PgdSelectionsDemo *demo) { GtkClipboard *clipboard = gtk_clipboard_get_for_display(gdk_display_get_default(), GDK_SELECTION_CLIPBOARD); gtk_clipboard_set_text(clipboard, demo->selected_text, -1); } static void pgd_selections_page_selector_value_changed(GtkSpinButton *spinbutton, PgdSelectionsDemo *demo) { demo->page_index = (gint)gtk_spin_button_get_value(spinbutton) - 1; if (demo->page) { g_object_unref(demo->page); } demo->page = NULL; } static void pgd_selections_scale_selector_value_changed(GtkSpinButton *spinbutton, PgdSelectionsDemo *demo) { demo->scale = gtk_spin_button_get_value(spinbutton); } static void pgd_selections_fg_color_changed(GtkColorButton *button, GParamSpec *pspec, PgdSelectionsDemo *demo) { GdkRGBA color; gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(button), &color); demo->glyph_color.red = CLAMP((guint)(color.red * 65535), 0, 65535); demo->glyph_color.green = CLAMP((guint)(color.green * 65535), 0, 65535); demo->glyph_color.blue = CLAMP((guint)(color.blue * 65535), 0, 65535); } static void pgd_selections_bg_color_changed(GtkColorButton *button, GParamSpec *pspec, PgdSelectionsDemo *demo) { GdkRGBA color; gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(button), &color); demo->background_color.red = CLAMP((guint)(color.red * 65535), 0, 65535); demo->background_color.green = CLAMP((guint)(color.green * 65535), 0, 65535); demo->background_color.blue = CLAMP((guint)(color.blue * 65535), 0, 65535); } GtkWidget *pgd_selections_properties_selector_create(PgdSelectionsDemo *demo) { GtkWidget *hbox, *vbox; GtkWidget *label; GtkWidget *page_hbox, *page_selector; GtkWidget *scale_hbox, *scale_selector; GtkWidget *rotate_hbox, *rotate_selector; GtkWidget *color_hbox; GtkWidget *button; gint n_pages; gchar *str; n_pages = poppler_document_get_n_pages(demo->doc); vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6); hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12); gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); gtk_widget_show(hbox); page_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); label = gtk_label_new("Page:"); gtk_box_pack_start(GTK_BOX(page_hbox), label, TRUE, 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_selections_page_selector_value_changed), (gpointer)demo); gtk_box_pack_start(GTK_BOX(page_hbox), page_selector, TRUE, 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(page_hbox), label, TRUE, TRUE, 0); gtk_widget_show(label); g_free(str); gtk_box_pack_start(GTK_BOX(hbox), page_hbox, FALSE, TRUE, 0); gtk_widget_show(page_hbox); 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_selections_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); rotate_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); label = gtk_label_new("Rotate:"); gtk_box_pack_start(GTK_BOX(rotate_hbox), label, TRUE, TRUE, 0); gtk_widget_show(label); rotate_selector = gtk_combo_box_text_new(); gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(rotate_selector), "0"); gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(rotate_selector), "90"); gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(rotate_selector), "180"); gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(rotate_selector), "270"); gtk_combo_box_set_active(GTK_COMBO_BOX(rotate_selector), 0); #if 0 g_signal_connect (G_OBJECT (rotate_selector), "changed", G_CALLBACK (pgd_selections_rotate_selector_changed), (gpointer)demo); #endif gtk_box_pack_start(GTK_BOX(rotate_hbox), rotate_selector, TRUE, TRUE, 0); gtk_widget_show(rotate_selector); gtk_box_pack_start(GTK_BOX(hbox), rotate_hbox, FALSE, TRUE, 0); gtk_widget_show(rotate_hbox); hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12); gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); gtk_widget_show(hbox); color_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); label = gtk_label_new("Foreground Color:"); gtk_box_pack_start(GTK_BOX(color_hbox), label, TRUE, TRUE, 0); gtk_widget_show(label); demo->fg_color_button = gtk_color_button_new(); g_signal_connect(demo->fg_color_button, "notify::color", G_CALLBACK(pgd_selections_fg_color_changed), (gpointer)demo); gtk_box_pack_start(GTK_BOX(color_hbox), demo->fg_color_button, TRUE, TRUE, 0); gtk_widget_show(demo->fg_color_button); gtk_box_pack_start(GTK_BOX(hbox), color_hbox, FALSE, TRUE, 0); gtk_widget_show(color_hbox); color_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); label = gtk_label_new("Background Color:"); gtk_box_pack_start(GTK_BOX(color_hbox), label, TRUE, TRUE, 0); gtk_widget_show(label); demo->bg_color_button = gtk_color_button_new(); g_signal_connect(demo->bg_color_button, "notify::color", G_CALLBACK(pgd_selections_bg_color_changed), (gpointer)demo); gtk_box_pack_start(GTK_BOX(color_hbox), demo->bg_color_button, TRUE, TRUE, 0); gtk_widget_show(demo->bg_color_button); gtk_box_pack_start(GTK_BOX(hbox), color_hbox, FALSE, TRUE, 0); gtk_widget_show(color_hbox); demo->copy_button = gtk_button_new_with_label("Copy"); g_signal_connect(G_OBJECT(demo->copy_button), "clicked", G_CALLBACK(pgd_selections_copy), (gpointer)demo); gtk_box_pack_end(GTK_BOX(hbox), demo->copy_button, FALSE, TRUE, 0); gtk_widget_set_sensitive(demo->copy_button, FALSE); gtk_widget_show(demo->copy_button); button = gtk_button_new_with_label("Render"); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(pgd_selections_render), (gpointer)demo); gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, TRUE, 0); gtk_widget_show(button); return vbox; } GtkWidget *pgd_selections_create_widget(PopplerDocument *document) { PgdSelectionsDemo *demo; GtkWidget *vbox, *hbox; demo = g_new0(PgdSelectionsDemo, 1); demo->doc = g_object_ref(document); demo->scale = 1.0; demo->cursor = GDK_LAST_CURSOR; pgd_selections_clear_selections(demo); vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6); hbox = pgd_selections_properties_selector_create(demo); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 6); gtk_widget_show(hbox); demo->darea = gtk_drawing_area_new(); g_signal_connect(demo->darea, "realize", G_CALLBACK(pgd_selections_drawing_area_realize), (gpointer)demo); g_signal_connect(demo->darea, "draw", G_CALLBACK(pgd_selections_drawing_area_draw), (gpointer)demo); g_signal_connect(demo->darea, "button_press_event", G_CALLBACK(pgd_selections_drawing_area_button_press), (gpointer)demo); g_signal_connect(demo->darea, "motion_notify_event", G_CALLBACK(pgd_selections_drawing_area_motion_notify), (gpointer)demo); g_signal_connect(demo->darea, "button_release_event", G_CALLBACK(pgd_selections_drawing_area_button_release), (gpointer)demo); 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); gtk_widget_show(demo->swindow); g_object_weak_ref(G_OBJECT(demo->swindow), (GWeakNotify)pgd_selections_free, (gpointer)demo); return vbox; } poppler-24.02.0/glib/demo/selections.h000066400000000000000000000017471455701731300175410ustar00rootroot00000000000000/* * Copyright (C) 2010 Carlos Garcia Campos * * 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 _SELECTIONS_H_ # define _SELECTIONS_H_ G_BEGIN_DECLS GtkWidget *pgd_selections_create_widget(PopplerDocument *document); G_END_DECLS #endif /* _SELECTIONS_H_ */ poppler-24.02.0/glib/demo/signature.c000066400000000000000000000326111455701731300173570ustar00rootroot00000000000000/* * 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); #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_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; } poppler-24.02.0/glib/demo/signature.h000066400000000000000000000017441455701731300173670ustar00rootroot00000000000000/* * 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_ */ poppler-24.02.0/glib/demo/taggedstruct.c000066400000000000000000000170021455701731300200530ustar00rootroot00000000000000/* * Copyright (C) 2013 Igalia S.L. * * 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 "text.h" #include "utils.h" typedef struct { GtkWidget *view; GtkTreeStore *store; GtkWidget *type_value; GtkWidget *lang_value; GtkWidget *abbr_value; GtkWidget *id_value; GtkWidget *title_value; GtkTextBuffer *text_buffer; } PgdTaggedStructDemo; static void pgd_taggedstruct_free(PgdTaggedStructDemo *demo) { if (!demo) { return; } if (demo->store) { g_object_unref(demo->store); demo->store = NULL; } g_free(demo); } static void populate_store_aux(GtkTreeStore *store, GtkTreeIter *parent, PopplerStructureElementIter *iter) { do { PopplerStructureElementIter *child = poppler_structure_element_iter_get_child(iter); PopplerStructureElement *element = poppler_structure_element_iter_get_element(iter); GEnumClass *enum_class = G_ENUM_CLASS(g_type_class_ref(POPPLER_TYPE_STRUCTURE_ELEMENT_KIND)); GEnumValue *enum_value = g_enum_get_value(enum_class, poppler_structure_element_get_kind(element)); GtkTreeIter pos; gtk_tree_store_append(store, &pos, parent); gtk_tree_store_set(store, &pos, 0, enum_value->value_nick, 1, element, -1); if (child) { populate_store_aux(store, &pos, child); poppler_structure_element_iter_free(child); } } while (poppler_structure_element_iter_next(iter)); } static GtkTreeStore *populate_store(PopplerStructureElementIter *iter) { GtkTreeStore *store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER); if (iter) { populate_store_aux(store, NULL, iter); } else { GtkTreeIter pos; gtk_tree_store_append(store, &pos, NULL); gtk_tree_store_set(store, &pos, 0, "Not a Tagged-PDF", 1, NULL, -1); } return store; } /*static void pgd_row_activated (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, PgdTaggedStructDemo *demo) {*/ static void pgd_selection_changed(GtkTreeSelection *selection, PgdTaggedStructDemo *demo) { GtkTreeModel *model; PopplerStructureElement *element; GtkTreeIter iter; gpointer elementptr; if (!gtk_tree_selection_get_selected(selection, &model, &iter)) { return; } gtk_tree_model_get(model, &iter, 1, &elementptr, -1); element = POPPLER_STRUCTURE_ELEMENT(elementptr); gtk_label_set_text(GTK_LABEL(demo->id_value), poppler_structure_element_get_id(element)); gtk_label_set_text(GTK_LABEL(demo->title_value), poppler_structure_element_get_title(element)); gtk_label_set_text(GTK_LABEL(demo->lang_value), poppler_structure_element_get_language(element)); gtk_label_set_text(GTK_LABEL(demo->abbr_value), poppler_structure_element_get_abbreviation(element)); gtk_text_buffer_set_text(demo->text_buffer, "", -1); if (poppler_structure_element_is_content(element)) { const gchar *text = poppler_structure_element_get_text(element, FALSE); if (text) { gtk_text_buffer_set_text(demo->text_buffer, text, -1); } gtk_label_set_text(GTK_LABEL(demo->type_value), "Content"); } else { if (poppler_structure_element_is_inline(element)) { gtk_label_set_text(GTK_LABEL(demo->type_value), "Inline"); } else if (poppler_structure_element_is_block(element)) { gtk_label_set_text(GTK_LABEL(demo->type_value), "Block"); } else { gtk_label_set_text(GTK_LABEL(demo->type_value), "Structure"); } } } GtkWidget *pgd_taggedstruct_create_widget(PopplerDocument *document) { PopplerStructureElementIter *iter; PgdTaggedStructDemo *demo; GtkCellRenderer *renderer; GtkTreeSelection *selection; GtkWidget *hbox; GtkWidget *vbox; GtkWidget *grid; GtkWidget *scroll; GtkWidget *w; gint row; demo = g_new0(PgdTaggedStructDemo, 1); iter = poppler_structure_element_iter_new(document); demo->store = populate_store(iter); poppler_structure_element_iter_free(iter); demo->view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(demo->store)); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(demo->view), 0, "Type", renderer, "markup", 0, NULL); g_object_set(G_OBJECT(gtk_tree_view_get_column(GTK_TREE_VIEW(demo->view), 0)), "expand", TRUE, NULL); gtk_tree_view_expand_all(GTK_TREE_VIEW(demo->view)); gtk_tree_view_set_show_expanders(GTK_TREE_VIEW(demo->view), TRUE); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(demo->view), TRUE); gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(demo->view), FALSE); gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(demo->view), TRUE); hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); scroll = gtk_scrolled_window_new(NULL, NULL); gtk_container_add(GTK_CONTAINER(scroll), demo->view); gtk_widget_show(demo->view); gtk_box_pack_start(GTK_BOX(hbox), scroll, TRUE, TRUE, 0); gtk_widget_show(scroll); row = 0; grid = gtk_grid_new(); gtk_container_set_border_width(GTK_CONTAINER(grid), 12); gtk_grid_set_row_homogeneous(GTK_GRID(grid), FALSE); gtk_grid_set_column_spacing(GTK_GRID(grid), 6); gtk_grid_set_row_spacing(GTK_GRID(grid), 6); pgd_table_add_property_with_value_widget(GTK_GRID(grid), "Type:", &demo->type_value, NULL, &row); pgd_table_add_property_with_value_widget(GTK_GRID(grid), "ID:", &demo->id_value, NULL, &row); pgd_table_add_property_with_value_widget(GTK_GRID(grid), "Title:", &demo->title_value, NULL, &row); pgd_table_add_property_with_value_widget(GTK_GRID(grid), "Language:", &demo->lang_value, NULL, &row); pgd_table_add_property_with_value_widget(GTK_GRID(grid), "Abbreviation:", &demo->abbr_value, NULL, &row); vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6); gtk_box_pack_start(GTK_BOX(vbox), grid, FALSE, FALSE, 0); gtk_widget_show(grid); scroll = gtk_scrolled_window_new(NULL, NULL); gtk_container_set_border_width(GTK_CONTAINER(scroll), 12); gtk_box_pack_end(GTK_BOX(vbox), scroll, TRUE, TRUE, 0); gtk_widget_show(scroll); gtk_container_add(GTK_CONTAINER(scroll), (w = gtk_text_view_new())); gtk_widget_show(w); demo->text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(w)); gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(w), GTK_WRAP_WORD_CHAR); gtk_text_view_set_editable(GTK_TEXT_VIEW(w), FALSE); gtk_text_buffer_set_text(demo->text_buffer, "", -1); gtk_widget_show(w); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(demo->view)); g_signal_connect(selection, "changed", G_CALLBACK(pgd_selection_changed), demo); gtk_box_pack_end(GTK_BOX(hbox), vbox, TRUE, TRUE, 0); gtk_widget_show(vbox); g_object_weak_ref(G_OBJECT(hbox), (GWeakNotify)pgd_taggedstruct_free, demo); gtk_widget_show(hbox); return hbox; } poppler-24.02.0/glib/demo/taggedstruct.h000066400000000000000000000017201455701731300200600ustar00rootroot00000000000000/* * Copyright (C) 2013 Igalia S.L. * * 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 _TAGGEDSTRUCT_H_ # define _TAGGEDSTRUCT_H_ G_BEGIN_DECLS GtkWidget *pgd_taggedstruct_create_widget(PopplerDocument *document); G_END_DECLS #endif /* _TAGGEDSTRUCT_H_ */ poppler-24.02.0/glib/demo/text.c000066400000000000000000000420731455701731300163450ustar00rootroot00000000000000/* * Copyright (C) 2008 Carlos Garcia Campos * * 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 "text.h" #include "utils.h" enum { TEXT_X1_COLUMN, TEXT_Y1_COLUMN, TEXT_X2_COLUMN, TEXT_Y2_COLUMN, TEXT_OFFSET_COLUMN, TEXT_OFFPTR_COLUMN, N_COLUMNS }; typedef struct { PopplerDocument *doc; GtkWidget *timer_label; GtkTextBuffer *buffer; GtkWidget *treeview; GtkListStore *model; GtkWidget *area_x1; GtkWidget *area_y1; GtkWidget *area_x2; GtkWidget *area_y2; /* Text attributes */ GList *text_attrs; GtkWidget *font_name; GtkWidget *font_size; GtkWidget *is_underlined; GtkWidget *text_color; gint page; PopplerRectangle area; } PgdTextDemo; static void pgd_text_free(PgdTextDemo *demo) { if (!demo) { return; } if (demo->doc) { g_object_unref(demo->doc); demo->doc = NULL; } if (demo->buffer) { g_object_unref(demo->buffer); demo->buffer = NULL; } if (demo->text_attrs) { poppler_page_free_text_attributes(demo->text_attrs); demo->text_attrs = NULL; } if (demo->model) { g_object_unref(demo->model); demo->model = NULL; } g_free(demo); } static void pgd_text_get_text(GtkWidget *button, PgdTextDemo *demo) { PopplerPage *page; PopplerRectangle *recs = NULL; guint n_recs; gchar *text; GTimer *timer; gint i; page = poppler_document_get_page(demo->doc, demo->page); if (!page) { return; } gtk_list_store_clear(demo->model); if (demo->text_attrs) { poppler_page_free_text_attributes(demo->text_attrs); } demo->text_attrs = NULL; timer = g_timer_new(); text = poppler_page_get_text_for_area(page, &demo->area); g_timer_stop(timer); if (text) { gchar *str; gdouble text_elapsed, layout_elapsed; text_elapsed = g_timer_elapsed(timer, NULL); g_timer_start(timer); poppler_page_get_text_layout_for_area(page, &demo->area, &recs, &n_recs); g_timer_stop(timer); layout_elapsed = g_timer_elapsed(timer, NULL); g_timer_start(timer); demo->text_attrs = poppler_page_get_text_attributes_for_area(page, &demo->area); g_timer_stop(timer); str = g_strdup_printf("got %ld chars in %.4f seconds, %u layout units in %.4f seconds, text attrs in %.4f seconds", g_utf8_strlen(text, -1), text_elapsed, n_recs, layout_elapsed, g_timer_elapsed(timer, NULL)); gtk_label_set_markup(GTK_LABEL(demo->timer_label), str); g_free(str); } else { gtk_label_set_markup(GTK_LABEL(demo->timer_label), "No text found"); n_recs = 0; } g_timer_destroy(timer); g_object_unref(page); if (text) { gtk_text_buffer_set_text(demo->buffer, text, strlen(text)); g_free(text); } for (i = 0; i < n_recs; i++) { GtkTreeIter iter; gchar *x1, *y1, *x2, *y2; gchar *offset; x1 = g_strdup_printf("%.2f", recs[i].x1); y1 = g_strdup_printf("%.2f", recs[i].y1); x2 = g_strdup_printf("%.2f", recs[i].x2); y2 = g_strdup_printf("%.2f", recs[i].y2); offset = g_strdup_printf("%d", i); gtk_list_store_append(demo->model, &iter); gtk_list_store_set(demo->model, &iter, TEXT_X1_COLUMN, x1, TEXT_Y1_COLUMN, y1, TEXT_X2_COLUMN, x2, TEXT_Y2_COLUMN, y2, TEXT_OFFSET_COLUMN, offset, TEXT_OFFPTR_COLUMN, GINT_TO_POINTER(i), -1); g_free(x1); g_free(y1); g_free(x2); g_free(y2); g_free(offset); } g_free(recs); } static void pgd_text_set_text_attrs_for_offset(PgdTextDemo *demo, gint offset) { GList *l; for (l = demo->text_attrs; l; l = g_list_next(l)) { PopplerTextAttributes *attrs = (PopplerTextAttributes *)l->data; if (offset >= attrs->start_index && offset <= attrs->end_index) { gchar *str; GdkPixbuf *pixbuf; gtk_label_set_text(GTK_LABEL(demo->font_name), attrs->font_name); str = g_strdup_printf("%.2f", attrs->font_size); gtk_label_set_text(GTK_LABEL(demo->font_size), str); g_free(str); gtk_label_set_text(GTK_LABEL(demo->is_underlined), attrs->is_underlined ? "Yes" : "No"); pixbuf = pgd_pixbuf_new_for_color(&(attrs->color)); gtk_image_set_from_pixbuf(GTK_IMAGE(demo->text_color), pixbuf); g_object_unref(pixbuf); } } } static void pgd_text_selection_changed(GtkTreeSelection *treeselection, PgdTextDemo *demo) { GtkTreeModel *model; GtkTreeIter iter; if (gtk_tree_selection_get_selected(treeselection, &model, &iter)) { gpointer offset; GtkTextIter begin_iter, end_iter; gtk_tree_model_get(model, &iter, TEXT_OFFPTR_COLUMN, &offset, -1); gtk_text_buffer_get_iter_at_offset(demo->buffer, &begin_iter, GPOINTER_TO_INT(offset)); end_iter = begin_iter; gtk_text_iter_forward_char(&end_iter); gtk_text_buffer_select_range(demo->buffer, &begin_iter, &end_iter); pgd_text_set_text_attrs_for_offset(demo, GPOINTER_TO_INT(offset)); } } static void pgd_text_buffer_selection_changed(GtkTextBuffer *buffer, GParamSpec *pspec, GtkWidget *textview) { gtk_widget_set_has_tooltip(textview, gtk_text_buffer_get_has_selection(buffer)); } static gboolean pgd_text_view_query_tooltip(GtkTextView *textview, gint x, gint y, gboolean keyboard_tip, GtkTooltip *tooltip, PgdTextDemo *demo) { GtkTreeModel *model; GtkTreeIter iter; GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(demo->treeview)); if (gtk_tree_selection_get_selected(selection, &model, &iter)) { PopplerPage *page; gchar *x1, *y1, *x2, *y2; PopplerRectangle rect; gchar *text; gtk_tree_model_get(model, &iter, TEXT_X1_COLUMN, &x1, TEXT_Y1_COLUMN, &y1, TEXT_X2_COLUMN, &x2, TEXT_Y2_COLUMN, &y2, -1); rect.x1 = g_strtod(x1, NULL); rect.y1 = g_strtod(y1, NULL); rect.x2 = g_strtod(x2, NULL); rect.y2 = g_strtod(y2, NULL); g_free(x1); g_free(y1); g_free(x2); g_free(y2); page = poppler_document_get_page(demo->doc, demo->page); text = poppler_page_get_selected_text(page, POPPLER_SELECTION_GLYPH, &rect); gtk_tooltip_set_text(tooltip, text); g_free(text); g_object_unref(page); return TRUE; } else { return FALSE; } } static void pgd_text_area_selector_setup(PgdTextDemo *demo) { PopplerPage *page; gdouble width, height; page = poppler_document_get_page(demo->doc, demo->page); if (!page) { return; } poppler_page_get_size(page, &width, &height); gtk_spin_button_set_range(GTK_SPIN_BUTTON(demo->area_x1), -10, width - 10); gtk_spin_button_set_range(GTK_SPIN_BUTTON(demo->area_y1), -10, height - 10); gtk_spin_button_set_range(GTK_SPIN_BUTTON(demo->area_x2), 0, width + 10); gtk_spin_button_set_range(GTK_SPIN_BUTTON(demo->area_y2), 0, height + 10); gtk_spin_button_set_value(GTK_SPIN_BUTTON(demo->area_x1), 0); gtk_spin_button_set_value(GTK_SPIN_BUTTON(demo->area_y1), 0); gtk_spin_button_set_value(GTK_SPIN_BUTTON(demo->area_x2), width); gtk_spin_button_set_value(GTK_SPIN_BUTTON(demo->area_y2), height); g_object_unref(page); } static void pgd_text_area_selector_value_changed(GtkSpinButton *spinbutton, PgdTextDemo *demo) { demo->area.x1 = gtk_spin_button_get_value(GTK_SPIN_BUTTON(demo->area_x1)); demo->area.y1 = gtk_spin_button_get_value(GTK_SPIN_BUTTON(demo->area_y1)); demo->area.x2 = gtk_spin_button_get_value(GTK_SPIN_BUTTON(demo->area_x2)); demo->area.y2 = gtk_spin_button_get_value(GTK_SPIN_BUTTON(demo->area_y2)); } static void pgd_text_page_selector_value_changed(GtkSpinButton *spinbutton, PgdTextDemo *demo) { demo->page = (gint)gtk_spin_button_get_value(spinbutton) - 1; } GtkWidget *pgd_text_create_widget(PopplerDocument *document) { PgdTextDemo *demo; GtkWidget *label; GtkWidget *vbox, *vbox2; GtkWidget *hbox, *page_selector, *area_hbox; GtkWidget *button; GtkWidget *swindow, *textview, *treeview; GtkTreeSelection *selection; GtkWidget *frame, *table; GtkWidget *hpaned; GtkCellRenderer *renderer; gchar *str; gint n_pages; gint row = 0; demo = g_new0(PgdTextDemo, 1); demo->doc = g_object_ref(document); n_pages = poppler_document_get_n_pages(document); vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12); vbox2 = 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_text_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); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); gtk_widget_show(hbox); hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12); area_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); label = gtk_label_new("X1:"); gtk_box_pack_start(GTK_BOX(area_hbox), label, TRUE, TRUE, 0); gtk_widget_show(label); demo->area_x1 = gtk_spin_button_new_with_range(0, 0, 0.01); g_signal_connect(demo->area_x1, "value-changed", G_CALLBACK(pgd_text_area_selector_value_changed), demo); gtk_box_pack_start(GTK_BOX(area_hbox), demo->area_x1, TRUE, TRUE, 0); gtk_widget_show(demo->area_x1); gtk_box_pack_start(GTK_BOX(hbox), area_hbox, FALSE, TRUE, 0); gtk_widget_show(area_hbox); area_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); label = gtk_label_new("Y1:"); gtk_box_pack_start(GTK_BOX(area_hbox), label, TRUE, TRUE, 0); gtk_widget_show(label); demo->area_y1 = gtk_spin_button_new_with_range(0, 0, 0.01); g_signal_connect(demo->area_y1, "value-changed", G_CALLBACK(pgd_text_area_selector_value_changed), demo); gtk_box_pack_start(GTK_BOX(area_hbox), demo->area_y1, TRUE, TRUE, 0); gtk_widget_show(demo->area_y1); gtk_box_pack_start(GTK_BOX(hbox), area_hbox, FALSE, TRUE, 0); gtk_widget_show(area_hbox); area_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); label = gtk_label_new("X2:"); gtk_box_pack_start(GTK_BOX(area_hbox), label, TRUE, TRUE, 0); gtk_widget_show(label); demo->area_x2 = gtk_spin_button_new_with_range(0, 0, 0.01); g_signal_connect(demo->area_x2, "value-changed", G_CALLBACK(pgd_text_area_selector_value_changed), demo); gtk_box_pack_start(GTK_BOX(area_hbox), demo->area_x2, TRUE, TRUE, 0); gtk_widget_show(demo->area_x2); gtk_box_pack_start(GTK_BOX(hbox), area_hbox, FALSE, TRUE, 0); gtk_widget_show(area_hbox); area_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); label = gtk_label_new("Y2:"); gtk_box_pack_start(GTK_BOX(area_hbox), label, TRUE, TRUE, 0); gtk_widget_show(label); demo->area_y2 = gtk_spin_button_new_with_range(0, 0, 0.01); g_signal_connect(demo->area_y2, "value-changed", G_CALLBACK(pgd_text_area_selector_value_changed), demo); gtk_box_pack_start(GTK_BOX(area_hbox), demo->area_y2, TRUE, TRUE, 0); gtk_widget_show(demo->area_y2); gtk_box_pack_start(GTK_BOX(hbox), area_hbox, FALSE, TRUE, 0); gtk_widget_show(area_hbox); pgd_text_area_selector_setup(demo); button = gtk_button_new_with_label("Get Text"); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(pgd_text_get_text), (gpointer)demo); gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0); gtk_widget_show(button); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); gtk_widget_show(hbox); demo->timer_label = gtk_label_new(NULL); gtk_label_set_markup(GTK_LABEL(demo->timer_label), "No text found"); g_object_set(G_OBJECT(demo->timer_label), "xalign", 1.0, NULL); gtk_box_pack_start(GTK_BOX(vbox), demo->timer_label, FALSE, TRUE, 0); gtk_widget_show(demo->timer_label); hpaned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); gtk_paned_set_position(GTK_PANED(hpaned), 300); swindow = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); demo->model = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER); treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(demo->model)); demo->treeview = treeview; renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), TEXT_X1_COLUMN, "X1", renderer, "text", TEXT_X1_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), TEXT_Y1_COLUMN, "Y1", renderer, "text", TEXT_Y1_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), TEXT_X2_COLUMN, "X2", renderer, "text", TEXT_X2_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), TEXT_Y2_COLUMN, "Y2", renderer, "text", TEXT_Y2_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), TEXT_OFFSET_COLUMN, "Offset", renderer, "text", TEXT_OFFSET_COLUMN, NULL); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); g_signal_connect(selection, "changed", G_CALLBACK(pgd_text_selection_changed), (gpointer)demo); gtk_container_add(GTK_CONTAINER(swindow), treeview); gtk_widget_show(treeview); gtk_box_pack_start(GTK_BOX(vbox2), swindow, TRUE, TRUE, 0); gtk_widget_show(swindow); /* Text attributes */ frame = gtk_frame_new(NULL); gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE); label = gtk_label_new(NULL); gtk_label_set_markup(GTK_LABEL(label), "Text Attributes"); gtk_frame_set_label_widget(GTK_FRAME(frame), label); gtk_widget_show(label); 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); demo->font_name = gtk_label_new(NULL); pgd_table_add_property_with_custom_widget(GTK_GRID(table), "Font Name:", demo->font_name, &row); demo->font_size = gtk_label_new(NULL); pgd_table_add_property_with_custom_widget(GTK_GRID(table), "Font Size:", demo->font_size, &row); demo->is_underlined = gtk_label_new(NULL); pgd_table_add_property_with_custom_widget(GTK_GRID(table), "Underlined:", demo->is_underlined, &row); demo->text_color = gtk_image_new(); pgd_table_add_property_with_custom_widget(GTK_GRID(table), "Color:", demo->text_color, &row); gtk_container_add(GTK_CONTAINER(frame), table); gtk_widget_show(table); gtk_box_pack_start(GTK_BOX(vbox2), frame, FALSE, FALSE, 12); gtk_widget_show(frame); gtk_paned_add1(GTK_PANED(hpaned), vbox2); gtk_widget_show(vbox2); swindow = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); demo->buffer = gtk_text_buffer_new(NULL); textview = gtk_text_view_new_with_buffer(demo->buffer); g_signal_connect(textview, "query-tooltip", G_CALLBACK(pgd_text_view_query_tooltip), demo); g_signal_connect(demo->buffer, "notify::has-selection", G_CALLBACK(pgd_text_buffer_selection_changed), textview); gtk_container_add(GTK_CONTAINER(swindow), textview); gtk_widget_show(textview); gtk_paned_add2(GTK_PANED(hpaned), swindow); gtk_widget_show(swindow); gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0); gtk_widget_show(hpaned); g_object_weak_ref(G_OBJECT(vbox), (GWeakNotify)pgd_text_free, demo); return vbox; } poppler-24.02.0/glib/demo/text.h000066400000000000000000000017171455701731300163520ustar00rootroot00000000000000/* * Copyright (C) 2008 Carlos Garcia Campos * * 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 _TEXT_H_ # define _TEXT_H_ G_BEGIN_DECLS GtkWidget *pgd_text_create_widget(PopplerDocument *document); G_END_DECLS #endif /* _TEXT_H_ */ poppler-24.02.0/glib/demo/transitions.c000066400000000000000000000216741455701731300177420ustar00rootroot00000000000000/* * Copyright (C) 2007 Carlos Garcia Campos * * 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 "transitions.h" enum { TRANSITIONS_PAGE_COLUMN, TRANSITIONS_TYPE_COLUMN, TRANSITIONS_ALIGNMENT_COLUMN, TRANSITIONS_DIRECTION_COLUMN, TRANSITIONS_DURATION_COLUMN, TRANSITIONS_ANGLE_COLUMN, TRANSITIONS_SCALE_COLUMN, TRANSITIONS_RECTANGULAR_COLUMN, N_COLUMNS }; typedef struct { PopplerDocument *doc; GtkWidget *treeview; GtkWidget *progress; guint idle_id; } PgdTransitionsDemo; static void pgd_transitions_free(PgdTransitionsDemo *demo) { if (!demo) { return; } if (demo->idle_id > 0) { g_source_remove(demo->idle_id); demo->idle_id = 0; } if (demo->doc) { g_object_unref(demo->doc); demo->doc = NULL; } g_free(demo); } static const gchar *transition_type_to_string(PopplerPageTransitionType type) { switch (type) { case POPPLER_PAGE_TRANSITION_REPLACE: return "Replace"; case POPPLER_PAGE_TRANSITION_SPLIT: return "Split"; case POPPLER_PAGE_TRANSITION_BLINDS: return "Blinds"; case POPPLER_PAGE_TRANSITION_BOX: return "Box"; case POPPLER_PAGE_TRANSITION_WIPE: return "Wipe"; case POPPLER_PAGE_TRANSITION_DISSOLVE: return "Dissolve"; case POPPLER_PAGE_TRANSITION_GLITTER: return "Glitter"; case POPPLER_PAGE_TRANSITION_FLY: return "Fly"; case POPPLER_PAGE_TRANSITION_PUSH: return "Push"; case POPPLER_PAGE_TRANSITION_COVER: return "Cover"; case POPPLER_PAGE_TRANSITION_UNCOVER: return "Uncover"; case POPPLER_PAGE_TRANSITION_FADE: return "Fade"; } return "Unknown"; } static const gchar *transition_alignment_to_string(PopplerPageTransitionAlignment alignment) { return alignment == POPPLER_PAGE_TRANSITION_HORIZONTAL ? "Horizontal" : "Vertical"; } static const gchar *transition_direction_to_string(PopplerPageTransitionDirection direction) { return direction == POPPLER_PAGE_TRANSITION_INWARD ? "Inward" : "Outward"; } static void pgd_transitions_update_progress(PgdTransitionsDemo *demo, gint n_pages, gint scanned) { gchar *str; str = g_strdup_printf("Scanning transitions (%d%%)", MIN(scanned * 100 / n_pages, 100)); gtk_progress_bar_set_text(GTK_PROGRESS_BAR(demo->progress), str); gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(demo->progress), MIN((gdouble)scanned / n_pages, 1.0)); g_free(str); } static gboolean pgd_transitions_fill_model(PgdTransitionsDemo *demo) { GtkTreeModel *model; gint i, n_pages; n_pages = poppler_document_get_n_pages(demo->doc); model = gtk_tree_view_get_model(GTK_TREE_VIEW(demo->treeview)); g_object_ref(model); for (i = 0; i < n_pages; i++) { PopplerPage *page; PopplerPageTransition *transition; pgd_transitions_update_progress(demo, n_pages, i); while (gtk_events_pending()) { gtk_main_iteration(); } page = poppler_document_get_page(demo->doc, i); if (!page) { continue; } transition = poppler_page_get_transition(page); if (transition) { GtkTreeIter iter; gchar *npage; gchar *duration; gchar *angle; gchar *scale; npage = g_strdup_printf("%d", i + 1); duration = g_strdup_printf("%.2f", transition->duration_real); angle = g_strdup_printf("%d", transition->angle); scale = g_strdup_printf("%.2f", transition->scale); gtk_list_store_append(GTK_LIST_STORE(model), &iter); gtk_list_store_set(GTK_LIST_STORE(model), &iter, TRANSITIONS_PAGE_COLUMN, npage, TRANSITIONS_TYPE_COLUMN, transition_type_to_string(transition->type), TRANSITIONS_ALIGNMENT_COLUMN, transition_alignment_to_string(transition->alignment), TRANSITIONS_DIRECTION_COLUMN, transition_direction_to_string(transition->direction), TRANSITIONS_DURATION_COLUMN, duration, TRANSITIONS_ANGLE_COLUMN, angle, TRANSITIONS_SCALE_COLUMN, scale, TRANSITIONS_RECTANGULAR_COLUMN, transition->rectangular ? "Yes" : "No", -1); g_free(npage); g_free(duration); g_free(angle); g_free(scale); poppler_page_transition_free(transition); } g_object_unref(page); } pgd_transitions_update_progress(demo, n_pages, n_pages); g_object_unref(model); return FALSE; } static void pgd_transitions_scan_button_clicked(GtkButton *button, PgdTransitionsDemo *demo) { if (demo->idle_id > 0) { g_source_remove(demo->idle_id); } demo->idle_id = g_idle_add((GSourceFunc)pgd_transitions_fill_model, demo); } static GtkWidget *pgd_transitions_create_list(GtkTreeModel *model) { GtkWidget *treeview; GtkCellRenderer *renderer; treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), TRUE); gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), GTK_SELECTION_NONE); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 0, "Page", renderer, "text", TRANSITIONS_PAGE_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 1, "Type", renderer, "text", TRANSITIONS_TYPE_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 2, "Alignment", renderer, "text", TRANSITIONS_ALIGNMENT_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 3, "Direction", renderer, "text", TRANSITIONS_DIRECTION_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 4, "Duration", renderer, "text", TRANSITIONS_DURATION_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 5, "Angle", renderer, "text", TRANSITIONS_ANGLE_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 6, "Scale", renderer, "text", TRANSITIONS_SCALE_COLUMN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 7, "Rectangular", renderer, "text", TRANSITIONS_RECTANGULAR_COLUMN, NULL); return treeview; } GtkWidget *pgd_transitions_create_widget(PopplerDocument *document) { PgdTransitionsDemo *demo; GtkWidget *vbox; GtkListStore *model; GtkWidget *swindow; GtkWidget *hbox, *button; demo = g_new0(PgdTransitionsDemo, 1); demo->doc = g_object_ref(document); vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12); hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); demo->progress = gtk_progress_bar_new(); gtk_progress_bar_set_ellipsize(GTK_PROGRESS_BAR(demo->progress), PANGO_ELLIPSIZE_END); gtk_box_pack_start(GTK_BOX(hbox), demo->progress, TRUE, TRUE, 0); gtk_widget_show(demo->progress); button = gtk_button_new_with_label("Scan"); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(pgd_transitions_scan_button_clicked), (gpointer)demo); gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0); gtk_widget_show(button); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 6); gtk_widget_show(hbox); swindow = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); model = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); demo->treeview = pgd_transitions_create_list(GTK_TREE_MODEL(model)); g_object_unref(model); gtk_container_add(GTK_CONTAINER(swindow), demo->treeview); gtk_widget_show(demo->treeview); gtk_box_pack_start(GTK_BOX(vbox), swindow, TRUE, TRUE, 0); gtk_widget_show(swindow); g_object_weak_ref(G_OBJECT(swindow), (GWeakNotify)pgd_transitions_free, (gpointer)demo); return vbox; } poppler-24.02.0/glib/demo/transitions.h000066400000000000000000000017531455701731300177430ustar00rootroot00000000000000/* * Copyright (C) 2007 Carlos Garcia Campos * * 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 _TRANSITIONS_H_ # define _TRANSITIONS_H_ G_BEGIN_DECLS GtkWidget *pgd_transitions_create_widget(PopplerDocument *document); G_END_DECLS #endif /* _TRANSITIONS_H_ */ poppler-24.02.0/glib/demo/utils.c000066400000000000000000000504211455701731300165150ustar00rootroot00000000000000/* * Copyright (C) 2007 Carlos Garcia Campos * * 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 #include #include "utils.h" void pgd_table_add_property_with_custom_widget(GtkGrid *table, const gchar *markup, GtkWidget *widget, gint *row) { GtkWidget *label; label = gtk_label_new(NULL); g_object_set(G_OBJECT(label), "xalign", 0.0, "yalign", 0.0, NULL); gtk_label_set_markup(GTK_LABEL(label), markup); gtk_grid_attach(GTK_GRID(table), label, 0, *row, 1, 1); gtk_widget_show(label); gtk_grid_attach(GTK_GRID(table), widget, 1, *row, 1, 1); gtk_widget_set_hexpand(widget, TRUE); gtk_widget_show(widget); *row += 1; } void pgd_table_add_property_with_value_widget(GtkGrid *table, const gchar *markup, GtkWidget **value_widget, const gchar *value, gint *row) { GtkWidget *label; *value_widget = label = gtk_label_new(value); g_object_set(G_OBJECT(label), "xalign", 0.0, "selectable", TRUE, "ellipsize", PANGO_ELLIPSIZE_END, NULL); pgd_table_add_property_with_custom_widget(table, markup, label, row); } void pgd_table_add_property(GtkGrid *table, const gchar *markup, const gchar *value, gint *row) { GtkWidget *label; pgd_table_add_property_with_value_widget(table, markup, &label, value, row); } GtkWidget *pgd_action_view_new(PopplerDocument *document) { GtkWidget *frame, *label; frame = gtk_frame_new(NULL); gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE); label = gtk_label_new(NULL); gtk_label_set_markup(GTK_LABEL(label), "Action Properties"); gtk_frame_set_label_widget(GTK_FRAME(frame), label); gtk_widget_show(label); g_object_set_data(G_OBJECT(frame), "document", document); return frame; } static void pgd_action_view_add_destination(GtkWidget *action_view, GtkGrid *table, PopplerDest *dest, gboolean remote, gint *row) { PopplerDocument *document; GEnumValue *enum_value; gchar *str; pgd_table_add_property(table, "Type:", "Destination", row); enum_value = g_enum_get_value((GEnumClass *)g_type_class_ref(POPPLER_TYPE_DEST_TYPE), dest->type); pgd_table_add_property(table, "Destination Type:", enum_value->value_name, row); document = g_object_get_data(G_OBJECT(action_view), "document"); if (dest->type != POPPLER_DEST_NAMED) { str = NULL; if (document && !remote) { PopplerPage *poppler_page; gchar *page_label; poppler_page = poppler_document_get_page(document, MAX(0, dest->page_num - 1)); g_object_get(G_OBJECT(poppler_page), "label", &page_label, NULL); if (page_label) { str = g_strdup_printf("%d (%s)", dest->page_num, page_label); g_free(page_label); } } if (!str) { str = g_strdup_printf("%d", dest->page_num); } pgd_table_add_property(table, "Page:", str, row); g_free(str); str = g_strdup_printf("%.2f", dest->left); pgd_table_add_property(table, "Left:", str, row); g_free(str); str = g_strdup_printf("%.2f", dest->right); pgd_table_add_property(table, "Right:", str, row); g_free(str); str = g_strdup_printf("%.2f", dest->top); pgd_table_add_property(table, "Top:", str, row); g_free(str); str = g_strdup_printf("%.2f", dest->bottom); pgd_table_add_property(table, "Bottom:", str, row); g_free(str); str = g_strdup_printf("%.2f", dest->zoom); pgd_table_add_property(table, "Zoom:", str, row); g_free(str); } else { if (document && !remote) { PopplerDest *new_dest; new_dest = poppler_document_find_dest(document, dest->named_dest); if (new_dest) { GtkWidget *new_table; gint new_row = 0; 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); pgd_action_view_add_destination(action_view, GTK_GRID(new_table), new_dest, FALSE, &new_row); poppler_dest_free(new_dest); gtk_grid_attach(GTK_GRID(table), new_table, 0, *row, 1, 1); gtk_widget_show(new_table); *row += 1; } } } } static const gchar *get_movie_op(PopplerActionMovieOperation op) { switch (op) { case POPPLER_ACTION_MOVIE_PLAY: return "Play"; case POPPLER_ACTION_MOVIE_PAUSE: return "Pause"; case POPPLER_ACTION_MOVIE_RESUME: return "Resume"; case POPPLER_ACTION_MOVIE_STOP: return "Stop"; } return NULL; } static void free_tmp_file(GFile *tmp_file) { g_file_delete(tmp_file, NULL, NULL); g_object_unref(tmp_file); } static gboolean save_helper(const gchar *buf, gsize count, gpointer data, GError **error) { gint fd = GPOINTER_TO_INT(data); return write(fd, buf, count) == count; } static void pgd_action_view_play_rendition(GtkWidget *button, PopplerMedia *media) { GFile *file = NULL; gchar *uri; if (poppler_media_is_embedded(media)) { gint fd; gchar *tmp_filename = NULL; fd = g_file_open_tmp(NULL, &tmp_filename, NULL); if (fd != -1) { if (poppler_media_save_to_callback(media, save_helper, GINT_TO_POINTER(fd), NULL)) { file = g_file_new_for_path(tmp_filename); g_object_set_data_full(G_OBJECT(media), "tmp-file", g_object_ref(file), (GDestroyNotify)free_tmp_file); } else { g_free(tmp_filename); } close(fd); } else if (tmp_filename) { g_free(tmp_filename); } } else { const gchar *filename; filename = poppler_media_get_filename(media); if (g_path_is_absolute(filename)) { file = g_file_new_for_path(filename); } else if (g_strrstr(filename, "://")) { file = g_file_new_for_uri(filename); } else { gchar *cwd; gchar *path; // FIXME: relative to doc uri, not cwd cwd = g_get_current_dir(); path = g_build_filename(cwd, filename, NULL); g_free(cwd); file = g_file_new_for_path(path); g_free(path); } } if (file) { 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); } } } static void pgd_action_view_do_action_layer(GtkWidget *button, GList *state_list) { GList *l, *m; for (l = state_list; l; l = g_list_next(l)) { PopplerActionLayer *action_layer = (PopplerActionLayer *)l->data; for (m = action_layer->layers; m; m = g_list_next(m)) { PopplerLayer *layer = (PopplerLayer *)m->data; switch (action_layer->action) { case POPPLER_ACTION_LAYER_ON: poppler_layer_show(layer); break; case POPPLER_ACTION_LAYER_OFF: poppler_layer_hide(layer); break; case POPPLER_ACTION_LAYER_TOGGLE: if (poppler_layer_is_visible(layer)) { poppler_layer_hide(layer); } else { poppler_layer_show(layer); } break; } } } } void pgd_action_view_set_action(GtkWidget *action_view, PopplerAction *action) { GtkWidget *table; gint row = 0; table = gtk_bin_get_child(GTK_BIN(action_view)); if (table) { gtk_container_remove(GTK_CONTAINER(action_view), table); } if (!action) { return; } 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); pgd_table_add_property(GTK_GRID(table), "Title:", action->any.title, &row); switch (action->type) { case POPPLER_ACTION_UNKNOWN: pgd_table_add_property(GTK_GRID(table), "Type:", "Unknown", &row); break; case POPPLER_ACTION_NONE: pgd_table_add_property(GTK_GRID(table), "Type:", "None", &row); break; case POPPLER_ACTION_GOTO_DEST: pgd_action_view_add_destination(action_view, GTK_GRID(table), action->goto_dest.dest, FALSE, &row); break; case POPPLER_ACTION_GOTO_REMOTE: pgd_table_add_property(GTK_GRID(table), "Type:", "Remote Destination", &row); pgd_table_add_property(GTK_GRID(table), "Filename:", action->goto_remote.file_name, &row); pgd_action_view_add_destination(action_view, GTK_GRID(table), action->goto_remote.dest, TRUE, &row); break; case POPPLER_ACTION_LAUNCH: pgd_table_add_property(GTK_GRID(table), "Type:", "Launch", &row); pgd_table_add_property(GTK_GRID(table), "Filename:", action->launch.file_name, &row); pgd_table_add_property(GTK_GRID(table), "Params:", action->launch.params, &row); break; case POPPLER_ACTION_URI: pgd_table_add_property(GTK_GRID(table), "Type:", "External URI", &row); pgd_table_add_property(GTK_GRID(table), "URI", action->uri.uri, &row); break; case POPPLER_ACTION_NAMED: pgd_table_add_property(GTK_GRID(table), "Type:", "Named Action", &row); pgd_table_add_property(GTK_GRID(table), "Name:", action->named.named_dest, &row); break; case POPPLER_ACTION_MOVIE: { GtkWidget *movie_view = pgd_movie_view_new(); pgd_table_add_property(GTK_GRID(table), "Type:", "Movie", &row); pgd_table_add_property(GTK_GRID(table), "Operation:", get_movie_op(action->movie.operation), &row); pgd_movie_view_set_movie(movie_view, action->movie.movie); pgd_table_add_property_with_custom_widget(GTK_GRID(table), "Movie:", movie_view, &row); } break; case POPPLER_ACTION_RENDITION: { gchar *text; pgd_table_add_property(GTK_GRID(table), "Type:", "Rendition", &row); text = g_strdup_printf("%d", action->rendition.op); pgd_table_add_property(GTK_GRID(table), "Operation:", text, &row); g_free(text); if (action->rendition.media) { gboolean embedded = poppler_media_is_embedded(action->rendition.media); GtkWidget *button; pgd_table_add_property(GTK_GRID(table), "Embedded:", embedded ? "Yes" : "No", &row); if (embedded) { const gchar *mime_type = poppler_media_get_mime_type(action->rendition.media); pgd_table_add_property(GTK_GRID(table), "Mime type:", mime_type ? mime_type : "", &row); } else { pgd_table_add_property(GTK_GRID(table), "Filename:", poppler_media_get_filename(action->rendition.media), &row); } button = gtk_button_new_with_mnemonic("_Play"); g_signal_connect(button, "clicked", G_CALLBACK(pgd_action_view_play_rendition), action->rendition.media); pgd_table_add_property_with_custom_widget(GTK_GRID(table), NULL, button, &row); gtk_widget_show(button); } } break; case POPPLER_ACTION_OCG_STATE: { GList *l; GtkWidget *button; pgd_table_add_property(GTK_GRID(table), "Type:", "OCGState", &row); for (l = action->ocg_state.state_list; l; l = g_list_next(l)) { PopplerActionLayer *action_layer = (PopplerActionLayer *)l->data; gchar *text = NULL; gint n_layers = g_list_length(action_layer->layers); switch (action_layer->action) { case POPPLER_ACTION_LAYER_ON: text = g_strdup_printf("%d layers On", n_layers); break; case POPPLER_ACTION_LAYER_OFF: text = g_strdup_printf("%d layers Off", n_layers); break; case POPPLER_ACTION_LAYER_TOGGLE: text = g_strdup_printf("%d layers Toggle", n_layers); break; } pgd_table_add_property(GTK_GRID(table), "Action:", text, &row); g_free(text); } button = gtk_button_new_with_label("Do action"); g_signal_connect(button, "clicked", G_CALLBACK(pgd_action_view_do_action_layer), action->ocg_state.state_list); pgd_table_add_property_with_custom_widget(GTK_GRID(table), NULL, button, &row); gtk_widget_show(button); } break; case POPPLER_ACTION_JAVASCRIPT: { GtkTextBuffer *buffer; GtkWidget *textview; GtkWidget *swindow; pgd_table_add_property(GTK_GRID(table), "Type:", "JavaScript", &row); buffer = gtk_text_buffer_new(NULL); if (action->javascript.script) { gtk_text_buffer_set_text(buffer, action->javascript.script, -1); } textview = gtk_text_view_new_with_buffer(buffer); gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE); g_object_unref(buffer); swindow = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_container_add(GTK_CONTAINER(swindow), textview); gtk_widget_show(textview); pgd_table_add_property_with_custom_widget(GTK_GRID(table), NULL, swindow, &row); gtk_widget_show(swindow); } break; case POPPLER_ACTION_RESET_FORM: pgd_table_add_property(GTK_GRID(table), "Type:", "ResetForm", &row); break; default: g_assert_not_reached(); } gtk_container_add(GTK_CONTAINER(action_view), table); gtk_widget_show(table); } gchar *pgd_format_date(time_t utime) { GDateTime *dt = NULL; gchar *s = NULL; if (utime == 0) { return NULL; } dt = g_date_time_new_from_unix_local(utime); if (dt == NULL) { return NULL; } s = g_date_time_format(dt, "%c"); g_date_time_unref(dt); return s; } GtkWidget *pgd_movie_view_new(void) { GtkWidget *frame, *label; frame = gtk_frame_new(NULL); gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE); label = gtk_label_new(NULL); gtk_label_set_markup(GTK_LABEL(label), "Movie Properties"); gtk_frame_set_label_widget(GTK_FRAME(frame), label); gtk_widget_show(label); return frame; } static void pgd_movie_view_play_movie(GtkWidget *button, PopplerMovie *movie) { const gchar *filename; GFile *file; gchar *uri; filename = poppler_movie_get_filename(movie); if (g_path_is_absolute(filename)) { file = g_file_new_for_path(filename); } else if (g_strrstr(filename, "://")) { file = g_file_new_for_uri(filename); } else { gchar *cwd; gchar *path; // FIXME: relative to doc uri, not cwd cwd = g_get_current_dir(); path = g_build_filename(cwd, filename, NULL); g_free(cwd); file = g_file_new_for_path(path); g_free(path); } 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); } } void pgd_movie_view_set_movie(GtkWidget *movie_view, PopplerMovie *movie) { GtkWidget *table; GtkWidget *button; GEnumValue *enum_value; gint width, height; gint row = 0; table = gtk_bin_get_child(GTK_BIN(movie_view)); if (table) { gtk_container_remove(GTK_CONTAINER(movie_view), table); } if (!movie) { return; } 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); pgd_table_add_property(GTK_GRID(table), "Filename:", poppler_movie_get_filename(movie), &row); pgd_table_add_property(GTK_GRID(table), "Need Poster:", poppler_movie_need_poster(movie) ? "Yes" : "No", &row); pgd_table_add_property(GTK_GRID(table), "Show Controls:", poppler_movie_show_controls(movie) ? "Yes" : "No", &row); enum_value = g_enum_get_value((GEnumClass *)g_type_class_ref(POPPLER_TYPE_MOVIE_PLAY_MODE), poppler_movie_get_play_mode(movie)); pgd_table_add_property(GTK_GRID(table), "Play Mode:", enum_value->value_name, &row); pgd_table_add_property(GTK_GRID(table), "Synchronous Play:", poppler_movie_is_synchronous(movie) ? "Yes" : "No", &row); pgd_table_add_property(GTK_GRID(table), "Volume:", g_strdup_printf("%g", poppler_movie_get_volume(movie)), &row); pgd_table_add_property(GTK_GRID(table), "Rate:", g_strdup_printf("%g", poppler_movie_get_rate(movie)), &row); pgd_table_add_property(GTK_GRID(table), "Start:", g_strdup_printf("%g s", poppler_movie_get_start(movie) / 1e9), &row); pgd_table_add_property(GTK_GRID(table), "Duration:", g_strdup_printf("%g s", poppler_movie_get_duration(movie) / 1e9), &row); pgd_table_add_property(GTK_GRID(table), "Rotation Angle:", g_strdup_printf("%u", poppler_movie_get_rotation_angle(movie)), &row); poppler_movie_get_aspect(movie, &width, &height); pgd_table_add_property(GTK_GRID(table), "Aspect:", g_strdup_printf("%dx%d", width, height), &row); button = gtk_button_new_with_mnemonic("_Play"); g_signal_connect(button, "clicked", G_CALLBACK(pgd_movie_view_play_movie), movie); pgd_table_add_property_with_custom_widget(GTK_GRID(table), NULL, button, &row); gtk_widget_show(button); gtk_container_add(GTK_CONTAINER(movie_view), table); gtk_widget_show(table); } GdkPixbuf *pgd_pixbuf_new_for_color(PopplerColor *poppler_color) { GdkPixbuf *pixbuf; gint num, x; guchar *pixels; if (!poppler_color) { return NULL; } pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, 64, 16); pixels = gdk_pixbuf_get_pixels(pixbuf); num = gdk_pixbuf_get_width(pixbuf) * gdk_pixbuf_get_height(pixbuf); for (x = 0; x < num; x++) { pixels[0] = poppler_color->red; pixels[1] = poppler_color->green; pixels[2] = poppler_color->blue; pixels += 3; } return pixbuf; } poppler-24.02.0/glib/demo/utils.h000066400000000000000000000031301455701731300165150ustar00rootroot00000000000000/* * Copyright (C) 2007 Carlos Garcia Campos * * 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 _UTILS_H_ # define _UTILS_H_ G_BEGIN_DECLS void pgd_table_add_property(GtkGrid *table, const gchar *markup, const gchar *value, gint *row); void pgd_table_add_property_with_value_widget(GtkGrid *table, const gchar *markup, GtkWidget **value_widget, const gchar *value, gint *row); void pgd_table_add_property_with_custom_widget(GtkGrid *table, const gchar *markup, GtkWidget *widget, gint *row); GtkWidget *pgd_action_view_new(PopplerDocument *document); void pgd_action_view_set_action(GtkWidget *action_view, PopplerAction *action); gchar *pgd_format_date(time_t utime); GtkWidget *pgd_movie_view_new(void); void pgd_movie_view_set_movie(GtkWidget *movie_view, PopplerMovie *movie); GdkPixbuf *pgd_pixbuf_new_for_color(PopplerColor *poppler_color); G_END_DECLS #endif /* _UTILS_H_ */ poppler-24.02.0/glib/poppler-action.cc000066400000000000000000000475541455701731300175450ustar00rootroot00000000000000/* poppler-action.cc: glib wrapper for poppler -*- c-basic-offset: 8 -*- * Copyright (C) 2005, Red Hat, Inc. * * 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 "poppler.h" #include "poppler-private.h" /** * SECTION:poppler-action * @short_description: Action links * @title: PopplerAction */ G_DEFINE_BOXED_TYPE(PopplerDest, poppler_dest, poppler_dest_copy, poppler_dest_free) /** * poppler_dest_copy: * @dest: a #PopplerDest * * Copies @dest, creating an identical #PopplerDest. * * Return value: a new destination identical to @dest **/ PopplerDest *poppler_dest_copy(PopplerDest *dest) { PopplerDest *new_dest; new_dest = g_slice_dup(PopplerDest, dest); if (dest->named_dest) { new_dest->named_dest = g_strdup(dest->named_dest); } return new_dest; } /** * poppler_dest_free: * @dest: a #PopplerDest * * Frees @dest **/ void poppler_dest_free(PopplerDest *dest) { if (!dest) { return; } if (dest->named_dest) { g_free(dest->named_dest); } g_slice_free(PopplerDest, dest); } static void poppler_action_layer_free(PopplerActionLayer *action_layer) { if (!action_layer) { return; } if (action_layer->layers) { g_list_free_full(action_layer->layers, g_object_unref); action_layer->layers = nullptr; } g_slice_free(PopplerActionLayer, action_layer); } static PopplerActionLayer *poppler_action_layer_copy(PopplerActionLayer *action_layer) { PopplerActionLayer *retval = g_slice_dup(PopplerActionLayer, action_layer); retval->layers = g_list_copy(action_layer->layers); for (GList *l = retval->layers; l != nullptr; l = l->next) { g_object_ref(l->data); } return retval; } G_DEFINE_BOXED_TYPE(PopplerAction, poppler_action, poppler_action_copy, poppler_action_free) /** * poppler_action_free: * @action: a #PopplerAction * * Frees @action **/ void poppler_action_free(PopplerAction *action) { if (action == nullptr) { return; } /* Action specific stuff */ switch (action->type) { case POPPLER_ACTION_GOTO_DEST: poppler_dest_free(action->goto_dest.dest); break; case POPPLER_ACTION_GOTO_REMOTE: poppler_dest_free(action->goto_remote.dest); g_free(action->goto_remote.file_name); break; case POPPLER_ACTION_URI: g_free(action->uri.uri); break; case POPPLER_ACTION_LAUNCH: g_free(action->launch.file_name); g_free(action->launch.params); break; case POPPLER_ACTION_NAMED: g_free(action->named.named_dest); break; case POPPLER_ACTION_MOVIE: if (action->movie.movie) { g_object_unref(action->movie.movie); } break; case POPPLER_ACTION_RENDITION: if (action->rendition.media) { g_object_unref(action->rendition.media); } break; case POPPLER_ACTION_OCG_STATE: if (action->ocg_state.state_list) { g_list_free_full(action->ocg_state.state_list, (GDestroyNotify)poppler_action_layer_free); } break; case POPPLER_ACTION_JAVASCRIPT: if (action->javascript.script) { g_free(action->javascript.script); } break; case POPPLER_ACTION_RESET_FORM: if (action->reset_form.fields) { g_list_free_full(action->reset_form.fields, g_free); } break; default: break; } g_free(action->any.title); g_slice_free(PopplerAction, action); } /** * poppler_action_copy: * @action: a #PopplerAction * * Copies @action, creating an identical #PopplerAction. * * Return value: a new action identical to @action **/ PopplerAction *poppler_action_copy(PopplerAction *action) { PopplerAction *new_action; g_return_val_if_fail(action != nullptr, NULL); /* Do a straight copy of the memory */ new_action = g_slice_dup(PopplerAction, action); if (action->any.title != nullptr) { new_action->any.title = g_strdup(action->any.title); } switch (action->type) { case POPPLER_ACTION_GOTO_DEST: new_action->goto_dest.dest = poppler_dest_copy(action->goto_dest.dest); break; case POPPLER_ACTION_GOTO_REMOTE: new_action->goto_remote.dest = poppler_dest_copy(action->goto_remote.dest); if (action->goto_remote.file_name) { new_action->goto_remote.file_name = g_strdup(action->goto_remote.file_name); } break; case POPPLER_ACTION_URI: if (action->uri.uri) { new_action->uri.uri = g_strdup(action->uri.uri); } break; case POPPLER_ACTION_LAUNCH: if (action->launch.file_name) { new_action->launch.file_name = g_strdup(action->launch.file_name); } if (action->launch.params) { new_action->launch.params = g_strdup(action->launch.params); } break; case POPPLER_ACTION_NAMED: if (action->named.named_dest) { new_action->named.named_dest = g_strdup(action->named.named_dest); } break; case POPPLER_ACTION_MOVIE: if (action->movie.movie) { new_action->movie.movie = (PopplerMovie *)g_object_ref(action->movie.movie); } break; case POPPLER_ACTION_RENDITION: if (action->rendition.media) { new_action->rendition.media = (PopplerMedia *)g_object_ref(action->rendition.media); } break; case POPPLER_ACTION_OCG_STATE: if (action->ocg_state.state_list) { GList *l; GList *new_list = nullptr; for (l = action->ocg_state.state_list; l; l = g_list_next(l)) { PopplerActionLayer *alayer = (PopplerActionLayer *)l->data; new_list = g_list_prepend(new_list, poppler_action_layer_copy(alayer)); } new_action->ocg_state.state_list = g_list_reverse(new_list); } break; case POPPLER_ACTION_JAVASCRIPT: if (action->javascript.script) { new_action->javascript.script = g_strdup(action->javascript.script); } break; case POPPLER_ACTION_RESET_FORM: if (action->reset_form.fields) { GList *iter; new_action->reset_form.fields = nullptr; for (iter = action->reset_form.fields; iter != nullptr; iter = iter->next) { new_action->reset_form.fields = g_list_append(new_action->reset_form.fields, g_strdup((char *)iter->data)); } } break; default: break; } return new_action; } static PopplerDest *dest_new_goto(PopplerDocument *document, const LinkDest *link_dest) { PopplerDest *dest; dest = g_slice_new0(PopplerDest); if (link_dest == nullptr) { dest->type = POPPLER_DEST_UNKNOWN; return dest; } switch (link_dest->getKind()) { case destXYZ: dest->type = POPPLER_DEST_XYZ; break; case destFit: dest->type = POPPLER_DEST_FIT; break; case destFitH: dest->type = POPPLER_DEST_FITH; break; case destFitV: dest->type = POPPLER_DEST_FITV; break; case destFitR: dest->type = POPPLER_DEST_FITR; break; case destFitB: dest->type = POPPLER_DEST_FITB; break; case destFitBH: dest->type = POPPLER_DEST_FITBH; break; case destFitBV: dest->type = POPPLER_DEST_FITBV; break; default: dest->type = POPPLER_DEST_UNKNOWN; } if (link_dest->isPageRef()) { if (document) { const Ref page_ref = link_dest->getPageRef(); dest->page_num = document->doc->findPage(page_ref); } else { /* FIXME: We don't keep areound the page_ref for the * remote doc, so we can't look this up. Guess that * it's 0*/ dest->page_num = 0; } } else { dest->page_num = link_dest->getPageNum(); } dest->left = link_dest->getLeft(); dest->bottom = link_dest->getBottom(); dest->right = link_dest->getRight(); dest->top = link_dest->getTop(); dest->zoom = link_dest->getZoom(); dest->change_left = link_dest->getChangeLeft(); dest->change_top = link_dest->getChangeTop(); dest->change_zoom = link_dest->getChangeZoom(); if (document && dest->page_num > 0) { PopplerPage *page; page = poppler_document_get_page(document, dest->page_num - 1); if (page) { dest->left -= page->page->getCropBox()->x1; dest->bottom -= page->page->getCropBox()->x1; dest->right -= page->page->getCropBox()->y1; dest->top -= page->page->getCropBox()->y1; g_object_unref(page); } else { g_warning("Invalid page %d in Link Destination\n", dest->page_num); dest->page_num = 0; } } return dest; } static PopplerDest *dest_new_named(const GooString *named_dest) { PopplerDest *dest; dest = g_slice_new0(PopplerDest); if (named_dest == nullptr) { dest->type = POPPLER_DEST_UNKNOWN; return dest; } const std::string &str = named_dest->toStr(); dest->type = POPPLER_DEST_NAMED; dest->named_dest = poppler_named_dest_from_bytestring((const guint8 *)str.data(), str.size()); return dest; } static void build_goto_dest(PopplerDocument *document, PopplerAction *action, const LinkGoTo *link) { const LinkDest *link_dest; const GooString *named_dest; /* Return if it isn't OK */ if (!link->isOk()) { action->goto_dest.dest = dest_new_goto(nullptr, nullptr); return; } link_dest = link->getDest(); named_dest = link->getNamedDest(); if (link_dest != nullptr) { action->goto_dest.dest = dest_new_goto(document, link_dest); } else if (named_dest != nullptr) { action->goto_dest.dest = dest_new_named(named_dest); } else { action->goto_dest.dest = dest_new_goto(document, nullptr); } } static void build_goto_remote(PopplerAction *action, const LinkGoToR *link) { const LinkDest *link_dest; const GooString *named_dest; /* Return if it isn't OK */ if (!link->isOk()) { action->goto_remote.dest = dest_new_goto(nullptr, nullptr); return; } action->goto_remote.file_name = _poppler_goo_string_to_utf8(link->getFileName()); link_dest = link->getDest(); named_dest = link->getNamedDest(); if (link_dest != nullptr) { action->goto_remote.dest = dest_new_goto(nullptr, link_dest); } else if (named_dest != nullptr) { action->goto_remote.dest = dest_new_named(named_dest); } else { action->goto_remote.dest = dest_new_goto(nullptr, nullptr); } } static void build_launch(PopplerAction *action, const LinkLaunch *link) { if (link->getFileName()) { action->launch.file_name = g_strdup(link->getFileName()->c_str()); } if (link->getParams()) { action->launch.params = g_strdup(link->getParams()->c_str()); } } static void build_uri(PopplerAction *action, const LinkURI *link) { const gchar *uri = link->getURI().c_str(); if (uri != nullptr) { action->uri.uri = g_strdup(uri); } } static void build_named(PopplerAction *action, const LinkNamed *link) { const gchar *name = link->getName().c_str(); if (name != nullptr) { action->named.named_dest = g_strdup(name); } } static AnnotMovie *find_annot_movie_for_action(PopplerDocument *document, const LinkMovie *link) { AnnotMovie *annot = nullptr; XRef *xref = document->doc->getXRef(); Object annotObj; if (link->hasAnnotRef()) { const Ref *ref = link->getAnnotRef(); annotObj = xref->fetch(*ref); } else if (link->hasAnnotTitle()) { const std::string &title = link->getAnnotTitle(); int i; for (i = 1; i <= document->doc->getNumPages(); ++i) { Page *p = document->doc->getPage(i); if (!p) { continue; } Object annots = p->getAnnotsObject(); if (annots.isArray()) { int j; bool found = false; for (j = 0; j < annots.arrayGetLength() && !found; ++j) { annotObj = annots.arrayGet(j); if (annotObj.isDict()) { Object obj1 = annotObj.dictLookup("Subtype"); if (!obj1.isName("Movie")) { continue; } obj1 = annotObj.dictLookup("T"); if (obj1.isString() && obj1.getString()->toStr() == title) { found = true; } } if (!found) { annotObj.setToNull(); } } if (found) { break; } else { annotObj.setToNull(); } } } } if (annotObj.isDict()) { Object tmp; annot = new AnnotMovie(document->doc, std::move(annotObj), &tmp); if (!annot->isOk()) { delete annot; annot = nullptr; } } return annot; } static void build_movie(PopplerDocument *document, PopplerAction *action, const LinkMovie *link) { AnnotMovie *annot; switch (link->getOperation()) { case LinkMovie::operationTypePause: action->movie.operation = POPPLER_ACTION_MOVIE_PAUSE; break; case LinkMovie::operationTypeResume: action->movie.operation = POPPLER_ACTION_MOVIE_RESUME; break; case LinkMovie::operationTypeStop: action->movie.operation = POPPLER_ACTION_MOVIE_STOP; break; default: case LinkMovie::operationTypePlay: action->movie.operation = POPPLER_ACTION_MOVIE_PLAY; break; } annot = find_annot_movie_for_action(document, link); if (annot) { action->movie.movie = _poppler_movie_new(annot->getMovie()); delete annot; } } static void build_javascript(PopplerAction *action, const LinkJavaScript *link) { if (link->isOk()) { const GooString script(link->getScript()); action->javascript.script = _poppler_goo_string_to_utf8(&script); } } static void build_reset_form(PopplerAction *action, const LinkResetForm *link) { const std::vector &fields = link->getFields(); if (action->reset_form.fields != nullptr) { g_list_free_full(action->reset_form.fields, g_free); } action->reset_form.fields = nullptr; for (const auto &field : fields) { action->reset_form.fields = g_list_append(action->reset_form.fields, g_strdup(field.c_str())); } action->reset_form.exclude = link->getExclude(); } static void build_rendition(PopplerAction *action, const LinkRendition *link) { action->rendition.op = link->getOperation(); if (link->getMedia()) { action->rendition.media = _poppler_media_new(link->getMedia()); } // TODO: annotation reference } static PopplerLayer *get_layer_for_ref(PopplerDocument *document, GList *layers, const Ref ref, gboolean preserve_rb) { GList *l; for (l = layers; l; l = g_list_next(l)) { Layer *layer = (Layer *)l->data; if (layer->oc) { const Ref ocgRef = layer->oc->getRef(); if (ref == ocgRef) { GList *rb_group = nullptr; if (preserve_rb) { rb_group = _poppler_document_get_layer_rbgroup(document, layer); } return _poppler_layer_new(document, layer, rb_group); } } if (layer->kids) { PopplerLayer *retval = get_layer_for_ref(document, layer->kids, ref, preserve_rb); if (retval) { return retval; } } } return nullptr; } static void build_ocg_state(PopplerDocument *document, PopplerAction *action, const LinkOCGState *ocg_state) { const std::vector &st_list = ocg_state->getStateList(); bool preserve_rb = ocg_state->getPreserveRB(); GList *layer_state = nullptr; if (!document->layers) { if (!_poppler_document_get_layers(document)) { return; } } for (const LinkOCGState::StateList &list : st_list) { PopplerActionLayer *action_layer = g_slice_new0(PopplerActionLayer); switch (list.st) { case LinkOCGState::On: action_layer->action = POPPLER_ACTION_LAYER_ON; break; case LinkOCGState::Off: action_layer->action = POPPLER_ACTION_LAYER_OFF; break; case LinkOCGState::Toggle: action_layer->action = POPPLER_ACTION_LAYER_TOGGLE; break; } for (const Ref &ref : list.list) { PopplerLayer *layer = get_layer_for_ref(document, document->layers, ref, preserve_rb); action_layer->layers = g_list_prepend(action_layer->layers, layer); } layer_state = g_list_prepend(layer_state, action_layer); } action->ocg_state.state_list = g_list_reverse(layer_state); } PopplerAction *_poppler_action_new(PopplerDocument *document, const LinkAction *link, const gchar *title) { PopplerAction *action; action = g_slice_new0(PopplerAction); if (title) { action->any.title = g_strdup(title); } if (link == nullptr) { action->type = POPPLER_ACTION_NONE; return action; } switch (link->getKind()) { case actionGoTo: action->type = POPPLER_ACTION_GOTO_DEST; build_goto_dest(document, action, static_cast(link)); break; case actionGoToR: action->type = POPPLER_ACTION_GOTO_REMOTE; build_goto_remote(action, static_cast(link)); break; case actionLaunch: action->type = POPPLER_ACTION_LAUNCH; build_launch(action, static_cast(link)); break; case actionURI: action->type = POPPLER_ACTION_URI; build_uri(action, static_cast(link)); break; case actionNamed: action->type = POPPLER_ACTION_NAMED; build_named(action, static_cast(link)); break; case actionMovie: action->type = POPPLER_ACTION_MOVIE; build_movie(document, action, static_cast(link)); break; case actionRendition: action->type = POPPLER_ACTION_RENDITION; build_rendition(action, static_cast(link)); break; case actionOCGState: action->type = POPPLER_ACTION_OCG_STATE; build_ocg_state(document, action, static_cast(link)); break; case actionJavaScript: action->type = POPPLER_ACTION_JAVASCRIPT; build_javascript(action, static_cast(link)); break; case actionResetForm: action->type = POPPLER_ACTION_RESET_FORM; build_reset_form(action, dynamic_cast(link)); break; case actionUnknown: default: action->type = POPPLER_ACTION_UNKNOWN; break; } return action; } PopplerDest *_poppler_dest_new_goto(PopplerDocument *document, LinkDest *link_dest) { return dest_new_goto(document, link_dest); } poppler-24.02.0/glib/poppler-action.h000066400000000000000000000276431455701731300174040ustar00rootroot00000000000000/* poppler-action.h: glib interface to poppler * Copyright (C) 2004, Red Hat, Inc. * * 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_ACTION_H__ #define __POPPLER_ACTION_H__ #include #include "poppler.h" G_BEGIN_DECLS /** * PopplerActionType: * @POPPLER_ACTION_UNKNOWN: unknown action * @POPPLER_ACTION_NONE: no action specified * @POPPLER_ACTION_GOTO_DEST: go to destination * @POPPLER_ACTION_GOTO_REMOTE: go to destination in another document * @POPPLER_ACTION_LAUNCH: launch app (or open document) * @POPPLER_ACTION_URI: URI * @POPPLER_ACTION_NAMED: predefined action * @POPPLER_ACTION_MOVIE: play movies. Since 0.14 * @POPPLER_ACTION_RENDITION: play multimedia content. Since 0.14 * @POPPLER_ACTION_OCG_STATE: state of layer. Since 0.14 * @POPPLER_ACTION_JAVASCRIPT: Javascript. Since 0.18 * @POPPLER_ACTION_RESET_FORM: resets form. Since 0.90 * * Action types */ typedef enum { POPPLER_ACTION_UNKNOWN, /* unknown action */ POPPLER_ACTION_NONE, /* no action specified */ POPPLER_ACTION_GOTO_DEST, /* go to destination */ POPPLER_ACTION_GOTO_REMOTE, /* go to destination in new file */ POPPLER_ACTION_LAUNCH, /* launch app (or open document) */ POPPLER_ACTION_URI, /* URI */ POPPLER_ACTION_NAMED, /* named action*/ POPPLER_ACTION_MOVIE, /* movie action */ POPPLER_ACTION_RENDITION, /* rendition action */ POPPLER_ACTION_OCG_STATE, /* Set-OCG-State action */ POPPLER_ACTION_JAVASCRIPT, /* Javascript action */ POPPLER_ACTION_RESET_FORM /* ResetForm action */ } PopplerActionType; /** * PopplerDestType: * @POPPLER_DEST_UNKNOWN: unknown destination * @POPPLER_DEST_XYZ: go to page with coordinates (left, top) * positioned at the upper-left corner of the window and the contents of * the page magnified by the factor zoom * @POPPLER_DEST_FIT: go to page with its contents magnified just * enough to fit the entire page within the window both horizontally and * vertically * @POPPLER_DEST_FITH: go to page with the vertical coordinate top * positioned at the top edge of the window and the contents of the page * magnified just enough to fit the entire width of the page within the window * @POPPLER_DEST_FITV: go to page with the horizontal coordinate * left positioned at the left edge of the window and the contents of the * page magnified just enough to fit the entire height of the page within the window * @POPPLER_DEST_FITR: go to page with its contents magnified just * enough to fit the rectangle specified by the coordinates left, bottom, * right, and top entirely within the window both horizontally and vertically * @POPPLER_DEST_FITB: go to page with its contents magnified just enough to fit * its bounding box entirely within the window both horizontally and vertically * @POPPLER_DEST_FITBH: go to page with the vertical * coordinate top positioned at the top edge of the window and the * contents of the page magnified just enough to fit the entire width of its * bounding box within the window * @POPPLER_DEST_FITBV: go to page with the horizontal * coordinate left positioned at the left edge of the window and the * contents of the page magnified just enough to fit the entire height of its * bounding box within the window * @POPPLER_DEST_NAMED: got to page specified by a name. See poppler_document_find_dest() * * Destination types */ typedef enum { POPPLER_DEST_UNKNOWN, POPPLER_DEST_XYZ, POPPLER_DEST_FIT, POPPLER_DEST_FITH, POPPLER_DEST_FITV, POPPLER_DEST_FITR, POPPLER_DEST_FITB, POPPLER_DEST_FITBH, POPPLER_DEST_FITBV, POPPLER_DEST_NAMED } PopplerDestType; /** * PopplerActionMovieOperation: * @POPPLER_ACTION_MOVIE_PLAY: play movie * @POPPLER_ACTION_MOVIE_PAUSE: pause playing movie * @POPPLER_ACTION_MOVIE_RESUME: resume paused movie * @POPPLER_ACTION_MOVIE_STOP: stop playing movie * * Movie operations * * Since: 0.14 */ typedef enum { POPPLER_ACTION_MOVIE_PLAY, POPPLER_ACTION_MOVIE_PAUSE, POPPLER_ACTION_MOVIE_RESUME, POPPLER_ACTION_MOVIE_STOP } PopplerActionMovieOperation; /** * PopplerActionLayerAction: * @POPPLER_ACTION_LAYER_ON: set layer visibility on * @POPPLER_ACTION_LAYER_OFF: set layer visibility off * @POPPLER_ACTION_LAYER_TOGGLE: reverse the layer visibility state * * Layer actions * * Since: 0.14 */ typedef enum { POPPLER_ACTION_LAYER_ON, POPPLER_ACTION_LAYER_OFF, POPPLER_ACTION_LAYER_TOGGLE } PopplerActionLayerAction; /* Define the PopplerAction types */ typedef struct _PopplerActionAny PopplerActionAny; typedef struct _PopplerActionGotoDest PopplerActionGotoDest; typedef struct _PopplerActionGotoRemote PopplerActionGotoRemote; typedef struct _PopplerActionLaunch PopplerActionLaunch; typedef struct _PopplerActionUri PopplerActionUri; typedef struct _PopplerActionNamed PopplerActionNamed; typedef struct _PopplerActionMovie PopplerActionMovie; typedef struct _PopplerActionRendition PopplerActionRendition; typedef struct _PopplerActionOCGState PopplerActionOCGState; typedef struct _PopplerActionJavascript PopplerActionJavascript; typedef struct _PopplerActionResetForm PopplerActionResetForm; /** * PopplerDest: * @type: type of destination * @page_num: page number * @left: left coordinate * @bottom: bottom coordinate * @right: right coordinate * @top: top coordinate * @zoom: scale factor * @named_dest: name of the destination (#POPPLER_DEST_NAMED only) * @change_left: whether left coordinate should be changed * @change_top: whether top coordinate should be changed * @change_zoom: whether scale factor should be changed * * Data structure for holding a destination * * Note that @named_dest is the string representation of the named * destination. This is the right form to pass to poppler functions, * e.g. poppler_document_find_dest(), but to get the destination as * it appears in the PDF itself, you need to convert it to a bytestring * 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. */ struct _PopplerDest { PopplerDestType type; int page_num; double left; double bottom; double right; double top; double zoom; gchar *named_dest; guint change_left : 1; guint change_top : 1; guint change_zoom : 1; }; /** * PopplerActionLayer: * @action: a #PopplerActionLayerAction * @layers: (element-type PopplerLayer): list of #PopplerLayers * * Action to perform over a list of layers */ struct _PopplerActionLayer { PopplerActionLayerAction action; GList *layers; }; /** * PopplerActionAny: * @type: action type * @title: action title * * Fields common to all #PopplerActions */ struct _PopplerActionAny { PopplerActionType type; gchar *title; }; /** * PopplerActionGotoDest: * @type: action type (%POPPLER_ACTION_GOTO_DEST) * @title: action title * @dest: destination * * Go to destination */ struct _PopplerActionGotoDest { PopplerActionType type; gchar *title; PopplerDest *dest; }; /** * PopplerActionGotoRemote: * @type: action type (%POPPLER_ACTION_GOTO_REMOTE) * @title: action title * @file_name: file name * @dest: destination * * Go to destination in another document */ struct _PopplerActionGotoRemote { PopplerActionType type; gchar *title; gchar *file_name; PopplerDest *dest; }; /** * PopplerActionLaunch: * @type: action type (%POPPLER_ACTION_LAUNCH) * @title: action title * @file_name: file name * @params: parameters * * Launch app (or open document) */ struct _PopplerActionLaunch { PopplerActionType type; gchar *title; gchar *file_name; gchar *params; }; /** * PopplerActionUri: * @type: action type (%POPPLER_ACTION_URI) * @title: action title * @uri: URI * * URI */ struct _PopplerActionUri { PopplerActionType type; gchar *title; char *uri; }; /** * PopplerActionNamed: * @type: action type (%POPPLER_ACTION_NAMED) * @title: action title * @named_dest: named destination * * Predefined action */ struct _PopplerActionNamed { PopplerActionType type; gchar *title; gchar *named_dest; }; /** * PopplerActionMovie: * @type: action type (%POPPLER_ACTION_MOVIE) * @title: action title * @operation: operation * @movie: movie * * Play movies. * * Since: 0.14 */ struct _PopplerActionMovie { PopplerActionType type; gchar *title; PopplerActionMovieOperation operation; PopplerMovie *movie; }; /** * PopplerActionRendition: * @type: action type (%POPPLER_ACTION_RENDITION) * @title: action title * @op: operation * @media: media * * Play multimedia content. * * Since: 0.14 */ struct _PopplerActionRendition { PopplerActionType type; gchar *title; gint op; PopplerMedia *media; }; /** * PopplerActionOCGState: * @type: action type (%POPPLER_ACTION_OCG_STATE) * @title: action title * @state_list: (element-type PopplerActionLayer): list of #PopplerActionLayers * * State of layer. * * Since: 0.14 */ struct _PopplerActionOCGState { PopplerActionType type; gchar *title; GList *state_list; }; /** * PopplerActionJavascript: * @type: action type (%POPPLER_ACTION_JAVASCRIPT) * @title: action title * @script: javascript * * Javascript. * * Since: 0.18 */ struct _PopplerActionJavascript { PopplerActionType type; gchar *title; gchar *script; }; /** * PopplerActionResetForm: * @type: action type (%POPPLER_ACTION_RESET_FORM) * @title: action title * @fields: (element-type utf8) (nullable): list of field names to * reset / retain * @exclude: whether to reset all but the listed fields * * Resets some or all fields within a PDF form. * * The default behavior resets only the list of @fields, but setting * @exclude to %TRUE will cause the action to reset all fields but those * listed. Providing an empty list of fields resets the entire form. * * Since: 0.90 */ struct _PopplerActionResetForm { PopplerActionType type; gchar *title; GList *fields; gboolean exclude; }; /** * PopplerAction: * * A generic wrapper for actions that exposes only #PopplerActionType. */ union _PopplerAction { PopplerActionType type; PopplerActionAny any; PopplerActionGotoDest goto_dest; PopplerActionGotoRemote goto_remote; PopplerActionLaunch launch; PopplerActionUri uri; PopplerActionNamed named; PopplerActionMovie movie; PopplerActionRendition rendition; PopplerActionOCGState ocg_state; PopplerActionJavascript javascript; PopplerActionResetForm reset_form; }; #define POPPLER_TYPE_ACTION (poppler_action_get_type()) #define POPPLER_ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), POPPLER_TYPE_ACTION, PopplerAction)) POPPLER_PUBLIC GType poppler_action_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC void poppler_action_free(PopplerAction *action); POPPLER_PUBLIC PopplerAction *poppler_action_copy(PopplerAction *action); #define POPPLER_TYPE_DEST (poppler_dest_get_type()) POPPLER_PUBLIC GType poppler_dest_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC void poppler_dest_free(PopplerDest *dest); POPPLER_PUBLIC PopplerDest *poppler_dest_copy(PopplerDest *dest); POPPLER_PUBLIC char *poppler_named_dest_from_bytestring(const guint8 *data, gsize length); POPPLER_PUBLIC guint8 *poppler_named_dest_to_bytestring(const char *name, gsize *length); G_END_DECLS #endif /* __POPPLER_GLIB_H__ */ poppler-24.02.0/glib/poppler-annot.cc000066400000000000000000002033631455701731300173770ustar00rootroot00000000000000/* poppler-annot.cc: glib interface to poppler * * Copyright (C) 2007 Inigo Martinez * Copyright (C) 2009 Carlos Garcia Campos * Copyright (C) 2013 German Poo-Caamano * * 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 "config.h" #include "poppler.h" #include "poppler-private.h" #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); /** * SECTION:poppler-annot * @short_description: Annotations * @title: PopplerAnnot */ typedef struct _PopplerAnnotClass PopplerAnnotClass; typedef struct _PopplerAnnotMarkupClass PopplerAnnotMarkupClass; typedef struct _PopplerAnnotFreeTextClass PopplerAnnotFreeTextClass; typedef struct _PopplerAnnotTextClass PopplerAnnotTextClass; typedef struct _PopplerAnnotTextMarkupClass PopplerAnnotTextMarkupClass; typedef struct _PopplerAnnotFileAttachmentClass PopplerAnnotFileAttachmentClass; typedef struct _PopplerAnnotMovieClass PopplerAnnotMovieClass; typedef struct _PopplerAnnotScreenClass PopplerAnnotScreenClass; typedef struct _PopplerAnnotLineClass PopplerAnnotLineClass; typedef struct _PopplerAnnotCircleClass PopplerAnnotCircleClass; typedef struct _PopplerAnnotSquareClass PopplerAnnotSquareClass; typedef struct _PopplerAnnotStampClass PopplerAnnotStampClass; struct _PopplerAnnotClass { GObjectClass parent_class; }; struct _PopplerAnnotMarkup { PopplerAnnot parent_instance; }; struct _PopplerAnnotMarkupClass { PopplerAnnotClass parent_class; }; struct _PopplerAnnotText { PopplerAnnotMarkup parent_instance; }; struct _PopplerAnnotTextClass { PopplerAnnotMarkupClass parent_class; }; struct _PopplerAnnotTextMarkup { PopplerAnnotMarkup parent_instance; }; struct _PopplerAnnotTextMarkupClass { PopplerAnnotMarkupClass parent_class; }; struct _PopplerAnnotFreeText { PopplerAnnotMarkup parent_instance; }; struct _PopplerAnnotFreeTextClass { PopplerAnnotMarkupClass parent_class; }; struct _PopplerAnnotFileAttachment { PopplerAnnotMarkup parent_instance; }; struct _PopplerAnnotFileAttachmentClass { PopplerAnnotMarkupClass parent_class; }; struct _PopplerAnnotMovie { PopplerAnnot parent_instance; PopplerMovie *movie; }; struct _PopplerAnnotMovieClass { PopplerAnnotClass parent_class; }; struct _PopplerAnnotScreen { PopplerAnnot parent_instance; PopplerAction *action; }; struct _PopplerAnnotScreenClass { PopplerAnnotClass parent_class; }; struct _PopplerAnnotLine { PopplerAnnotMarkup parent_instance; }; struct _PopplerAnnotLineClass { PopplerAnnotMarkupClass parent_class; }; struct _PopplerAnnotCircle { PopplerAnnotMarkup parent_instance; }; struct _PopplerAnnotCircleClass { PopplerAnnotMarkupClass parent_class; }; struct _PopplerAnnotSquare { PopplerAnnotMarkup parent_instance; }; struct _PopplerAnnotSquareClass { PopplerAnnotMarkupClass parent_class; }; struct _PopplerAnnotStamp { PopplerAnnot parent_instance; }; struct _PopplerAnnotStampClass { PopplerAnnotClass parent_class; }; G_DEFINE_TYPE(PopplerAnnot, poppler_annot, G_TYPE_OBJECT) G_DEFINE_TYPE(PopplerAnnotMarkup, poppler_annot_markup, POPPLER_TYPE_ANNOT) G_DEFINE_TYPE(PopplerAnnotTextMarkup, poppler_annot_text_markup, POPPLER_TYPE_ANNOT_MARKUP) G_DEFINE_TYPE(PopplerAnnotText, poppler_annot_text, POPPLER_TYPE_ANNOT_MARKUP) G_DEFINE_TYPE(PopplerAnnotFreeText, poppler_annot_free_text, POPPLER_TYPE_ANNOT_MARKUP) G_DEFINE_TYPE(PopplerAnnotFileAttachment, poppler_annot_file_attachment, POPPLER_TYPE_ANNOT_MARKUP) G_DEFINE_TYPE(PopplerAnnotMovie, poppler_annot_movie, POPPLER_TYPE_ANNOT) 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) static PopplerAnnot *_poppler_create_annot(GType annot_type, Annot *annot) { PopplerAnnot *poppler_annot; poppler_annot = POPPLER_ANNOT(g_object_new(annot_type, nullptr)); poppler_annot->annot = annot; annot->incRefCnt(); return poppler_annot; } static void poppler_annot_finalize(GObject *object) { PopplerAnnot *poppler_annot = POPPLER_ANNOT(object); if (poppler_annot->annot) { poppler_annot->annot->decRefCnt(); poppler_annot->annot = nullptr; } G_OBJECT_CLASS(poppler_annot_parent_class)->finalize(object); } static void poppler_annot_init(PopplerAnnot *poppler_annot) { } static void poppler_annot_class_init(PopplerAnnotClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS(klass); gobject_class->finalize = poppler_annot_finalize; } PopplerAnnot *_poppler_annot_new(Annot *annot) { return _poppler_create_annot(POPPLER_TYPE_ANNOT, annot); } static void poppler_annot_markup_init(PopplerAnnotMarkup *poppler_annot) { } static void poppler_annot_markup_class_init(PopplerAnnotMarkupClass *klass) { } static void poppler_annot_text_init(PopplerAnnotText *poppler_annot) { } static void poppler_annot_text_class_init(PopplerAnnotTextClass *klass) { } PopplerAnnot *_poppler_annot_text_new(Annot *annot) { return _poppler_create_annot(POPPLER_TYPE_ANNOT_TEXT, annot); } /** * poppler_annot_text_new: * @doc: a #PopplerDocument * @rect: a #PopplerRectangle * * Creates a new Text annotation that will be * located on @rect when added to a page. See * poppler_page_add_annot() * * Return value: A newly created #PopplerAnnotText annotation * * Since: 0.16 */ PopplerAnnot *poppler_annot_text_new(PopplerDocument *doc, PopplerRectangle *rect) { Annot *annot; PDFRectangle pdf_rect(rect->x1, rect->y1, rect->x2, rect->y2); annot = new AnnotText(doc->doc, &pdf_rect); return _poppler_annot_text_new(annot); } PopplerAnnot *_poppler_annot_text_markup_new(Annot *annot) { return _poppler_create_annot(POPPLER_TYPE_ANNOT_TEXT_MARKUP, annot); } static AnnotQuadrilaterals *create_annot_quads_from_poppler_quads(GArray *quads) { g_assert(quads->len > 0); auto quads_array = std::make_unique(quads->len); for (guint i = 0; i < quads->len; i++) { PopplerQuadrilateral *quadrilateral = &g_array_index(quads, PopplerQuadrilateral, i); quads_array[i] = AnnotQuadrilaterals::AnnotQuadrilateral(quadrilateral->p1.x, quadrilateral->p1.y, quadrilateral->p2.x, quadrilateral->p2.y, quadrilateral->p3.x, quadrilateral->p3.y, quadrilateral->p4.x, quadrilateral->p4.y); } return new AnnotQuadrilaterals(std::move(quads_array), quads->len); } /* If @crop_box parameter is non null, it will substract the crop_box offset * from the coordinates of the returned #PopplerQuadrilateral array */ static GArray *create_poppler_quads_from_annot_quads(AnnotQuadrilaterals *quads_array, const PDFRectangle *crop_box) { GArray *quads; guint quads_len; PDFRectangle zerobox; if (!crop_box) { zerobox = PDFRectangle(); crop_box = &zerobox; } quads_len = quads_array->getQuadrilateralsLength(); quads = g_array_sized_new(FALSE, FALSE, sizeof(PopplerQuadrilateral), quads_len); g_array_set_size(quads, quads_len); for (guint i = 0; i < quads_len; ++i) { PopplerQuadrilateral *quadrilateral = &g_array_index(quads, PopplerQuadrilateral, i); quadrilateral->p1.x = quads_array->getX1(i) - crop_box->x1; quadrilateral->p1.y = quads_array->getY1(i) - crop_box->y1; quadrilateral->p2.x = quads_array->getX2(i) - crop_box->x1; quadrilateral->p2.y = quads_array->getY2(i) - crop_box->y1; quadrilateral->p3.x = quads_array->getX3(i) - crop_box->x1; quadrilateral->p3.y = quads_array->getY3(i) - crop_box->y1; quadrilateral->p4.x = quads_array->getX4(i) - crop_box->x1; quadrilateral->p4.y = quads_array->getY4(i) - crop_box->y1; } return quads; } static void poppler_annot_text_markup_init(PopplerAnnotTextMarkup *poppler_annot) { } static void poppler_annot_text_markup_class_init(PopplerAnnotTextMarkupClass *klass) { } /** * poppler_annot_text_markup_new_highlight: * @doc: a #PopplerDocument * @rect: a #PopplerRectangle * @quadrilaterals: (element-type PopplerQuadrilateral): A #GArray of * #PopplerQuadrilaterals * * Creates a new Highlight Text annotation that will be * located on @rect when added to a page. See poppler_page_add_annot() * * Return value: (transfer full): A newly created #PopplerAnnotTextMarkup annotation * * Since: 0.26 */ PopplerAnnot *poppler_annot_text_markup_new_highlight(PopplerDocument *doc, PopplerRectangle *rect, GArray *quadrilaterals) { PopplerAnnot *poppler_annot; AnnotTextMarkup *annot; PDFRectangle pdf_rect(rect->x1, rect->y1, rect->x2, rect->y2); annot = new AnnotTextMarkup(doc->doc, &pdf_rect, Annot::typeHighlight); poppler_annot = _poppler_annot_text_markup_new(annot); poppler_annot_text_markup_set_quadrilaterals(POPPLER_ANNOT_TEXT_MARKUP(poppler_annot), quadrilaterals); return poppler_annot; } /** * poppler_annot_text_markup_new_squiggly: * @doc: a #PopplerDocument * @rect: a #PopplerRectangle * @quadrilaterals: (element-type PopplerQuadrilateral): A #GArray of * #PopplerQuadrilaterals * * Creates a new Squiggly Text annotation that will be * located on @rect when added to a page. See poppler_page_add_annot() * * Return value: (transfer full): A newly created #PopplerAnnotTextMarkup annotation * * Since: 0.26 */ PopplerAnnot *poppler_annot_text_markup_new_squiggly(PopplerDocument *doc, PopplerRectangle *rect, GArray *quadrilaterals) { PopplerAnnot *poppler_annot; AnnotTextMarkup *annot; PDFRectangle pdf_rect(rect->x1, rect->y1, rect->x2, rect->y2); g_return_val_if_fail(quadrilaterals != nullptr && quadrilaterals->len > 0, NULL); annot = new AnnotTextMarkup(doc->doc, &pdf_rect, Annot::typeSquiggly); poppler_annot = _poppler_annot_text_markup_new(annot); poppler_annot_text_markup_set_quadrilaterals(POPPLER_ANNOT_TEXT_MARKUP(poppler_annot), quadrilaterals); return poppler_annot; } /** * poppler_annot_text_markup_new_strikeout: * @doc: a #PopplerDocument * @rect: a #PopplerRectangle * @quadrilaterals: (element-type PopplerQuadrilateral): A #GArray of * #PopplerQuadrilaterals * * Creates a new Strike Out Text annotation that will be * located on @rect when added to a page. See poppler_page_add_annot() * * Return value: (transfer full): A newly created #PopplerAnnotTextMarkup annotation * * Since: 0.26 */ PopplerAnnot *poppler_annot_text_markup_new_strikeout(PopplerDocument *doc, PopplerRectangle *rect, GArray *quadrilaterals) { PopplerAnnot *poppler_annot; AnnotTextMarkup *annot; PDFRectangle pdf_rect(rect->x1, rect->y1, rect->x2, rect->y2); g_return_val_if_fail(quadrilaterals != nullptr && quadrilaterals->len > 0, NULL); annot = new AnnotTextMarkup(doc->doc, &pdf_rect, Annot::typeStrikeOut); poppler_annot = _poppler_annot_text_markup_new(annot); poppler_annot_text_markup_set_quadrilaterals(POPPLER_ANNOT_TEXT_MARKUP(poppler_annot), quadrilaterals); return poppler_annot; } /** * poppler_annot_text_markup_new_underline: * @doc: a #PopplerDocument * @rect: a #PopplerRectangle * @quadrilaterals: (element-type PopplerQuadrilateral): A #GArray of * #PopplerQuadrilaterals * * Creates a new Underline Text annotation that will be * located on @rect when added to a page. See poppler_page_add_annot() * * Return value: (transfer full): A newly created #PopplerAnnotTextMarkup annotation * * Since: 0.26 */ PopplerAnnot *poppler_annot_text_markup_new_underline(PopplerDocument *doc, PopplerRectangle *rect, GArray *quadrilaterals) { PopplerAnnot *poppler_annot; AnnotTextMarkup *annot; PDFRectangle pdf_rect(rect->x1, rect->y1, rect->x2, rect->y2); g_return_val_if_fail(quadrilaterals != nullptr && quadrilaterals->len > 0, NULL); annot = new AnnotTextMarkup(doc->doc, &pdf_rect, Annot::typeUnderline); poppler_annot = _poppler_annot_text_markup_new(annot); poppler_annot_text_markup_set_quadrilaterals(POPPLER_ANNOT_TEXT_MARKUP(poppler_annot), quadrilaterals); return poppler_annot; } static void poppler_annot_free_text_init(PopplerAnnotFreeText *poppler_annot) { } static void poppler_annot_free_text_class_init(PopplerAnnotFreeTextClass *klass) { } PopplerAnnot *_poppler_annot_free_text_new(Annot *annot) { return _poppler_create_annot(POPPLER_TYPE_ANNOT_FREE_TEXT, annot); } static void poppler_annot_file_attachment_init(PopplerAnnotFileAttachment *poppler_annot) { } static void poppler_annot_file_attachment_class_init(PopplerAnnotFileAttachmentClass *klass) { } PopplerAnnot *_poppler_annot_file_attachment_new(Annot *annot) { return _poppler_create_annot(POPPLER_TYPE_ANNOT_FILE_ATTACHMENT, annot); } static void poppler_annot_movie_finalize(GObject *object) { PopplerAnnotMovie *annot_movie = POPPLER_ANNOT_MOVIE(object); if (annot_movie->movie) { g_object_unref(annot_movie->movie); annot_movie->movie = nullptr; } G_OBJECT_CLASS(poppler_annot_movie_parent_class)->finalize(object); } static void poppler_annot_movie_init(PopplerAnnotMovie *poppler_annot) { } static void poppler_annot_movie_class_init(PopplerAnnotMovieClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS(klass); gobject_class->finalize = poppler_annot_movie_finalize; } PopplerAnnot *_poppler_annot_movie_new(Annot *annot) { PopplerAnnot *poppler_annot; AnnotMovie *annot_movie; poppler_annot = _poppler_create_annot(POPPLER_TYPE_ANNOT_MOVIE, annot); annot_movie = static_cast(poppler_annot->annot); POPPLER_ANNOT_MOVIE(poppler_annot)->movie = _poppler_movie_new(annot_movie->getMovie()); return poppler_annot; } static void poppler_annot_screen_finalize(GObject *object) { PopplerAnnotScreen *annot_screen = POPPLER_ANNOT_SCREEN(object); if (annot_screen->action) { poppler_action_free(annot_screen->action); annot_screen->action = nullptr; } G_OBJECT_CLASS(poppler_annot_screen_parent_class)->finalize(object); } static void poppler_annot_screen_init(PopplerAnnotScreen *poppler_annot) { } static void poppler_annot_screen_class_init(PopplerAnnotScreenClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS(klass); gobject_class->finalize = poppler_annot_screen_finalize; } PopplerAnnot *_poppler_annot_screen_new(PopplerDocument *doc, Annot *annot) { PopplerAnnot *poppler_annot; AnnotScreen *annot_screen; LinkAction *action; poppler_annot = _poppler_create_annot(POPPLER_TYPE_ANNOT_SCREEN, annot); annot_screen = static_cast(poppler_annot->annot); action = annot_screen->getAction(); if (action) { POPPLER_ANNOT_SCREEN(poppler_annot)->action = _poppler_action_new(doc, action, nullptr); } return poppler_annot; } PopplerAnnot *_poppler_annot_line_new(Annot *annot) { return _poppler_create_annot(POPPLER_TYPE_ANNOT_LINE, annot); } static void poppler_annot_line_init(PopplerAnnotLine *poppler_annot) { } static void poppler_annot_line_class_init(PopplerAnnotLineClass *klass) { } /** * poppler_annot_line_new: * @doc: a #PopplerDocument * @rect: a #PopplerRectangle * @start: a #PopplerPoint of the starting vertice * @end: a #PopplerPoint of the ending vertice * * Creates a new Line annotation that will be * located on @rect when added to a page. See * poppler_page_add_annot() * * Return value: A newly created #PopplerAnnotLine annotation * * Since: 0.26 */ PopplerAnnot *poppler_annot_line_new(PopplerDocument *doc, PopplerRectangle *rect, PopplerPoint *start, PopplerPoint *end) { PopplerAnnot *poppler_annot; Annot *annot; PDFRectangle pdf_rect(rect->x1, rect->y1, rect->x2, rect->y2); annot = new AnnotLine(doc->doc, &pdf_rect); poppler_annot = _poppler_annot_line_new(annot); poppler_annot_line_set_vertices(POPPLER_ANNOT_LINE(poppler_annot), start, end); return poppler_annot; } PopplerAnnot *_poppler_annot_circle_new(Annot *annot) { return _poppler_create_annot(POPPLER_TYPE_ANNOT_CIRCLE, annot); } static void poppler_annot_circle_init(PopplerAnnotCircle *poppler_annot) { } static void poppler_annot_circle_class_init(PopplerAnnotCircleClass *klass) { } /** * poppler_annot_circle_new: * @doc: a #PopplerDocument * @rect: a #PopplerRectangle * * Creates a new Circle annotation that will be * located on @rect when added to a page. See * poppler_page_add_annot() * * Return value: a newly created #PopplerAnnotCircle annotation * * Since: 0.26 **/ PopplerAnnot *poppler_annot_circle_new(PopplerDocument *doc, PopplerRectangle *rect) { Annot *annot; PDFRectangle pdf_rect(rect->x1, rect->y1, rect->x2, rect->y2); annot = new AnnotGeometry(doc->doc, &pdf_rect, Annot::typeCircle); return _poppler_annot_circle_new(annot); } PopplerAnnot *_poppler_annot_square_new(Annot *annot) { return _poppler_create_annot(POPPLER_TYPE_ANNOT_SQUARE, annot); } static void poppler_annot_square_init(PopplerAnnotSquare *poppler_annot) { } static void poppler_annot_square_class_init(PopplerAnnotSquareClass *klass) { } /** * poppler_annot_square_new: * @doc: a #PopplerDocument * @rect: a #PopplerRectangle * * Creates a new Square annotation that will be * located on @rect when added to a page. See * poppler_page_add_annot() * * Return value: a newly created #PopplerAnnotSquare annotation * * Since: 0.26 **/ PopplerAnnot *poppler_annot_square_new(PopplerDocument *doc, PopplerRectangle *rect) { Annot *annot; PDFRectangle pdf_rect(rect->x1, rect->y1, rect->x2, rect->y2); annot = new AnnotGeometry(doc->doc, &pdf_rect, Annot::typeSquare); return _poppler_annot_square_new(annot); } static void poppler_annot_stamp_finalize(GObject *object) { G_OBJECT_CLASS(poppler_annot_stamp_parent_class)->finalize(object); } static void poppler_annot_stamp_init(PopplerAnnotStamp *poppler_annot) { } static void poppler_annot_stamp_class_init(PopplerAnnotStampClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS(klass); gobject_class->finalize = poppler_annot_stamp_finalize; } PopplerAnnot *_poppler_annot_stamp_new(Annot *annot) { PopplerAnnot *poppler_annot; poppler_annot = _poppler_create_annot(POPPLER_TYPE_ANNOT_STAMP, annot); return poppler_annot; } /** * poppler_annot_stamp_new: * @doc: a #PopplerDocument * @rect: a #PopplerRectangle * * Creates a new Stamp annotation that will be * located on @rect when added to a page. See * poppler_page_add_annot() * * Return value: a newly created #PopplerAnnotStamp annotation * * Since: 22.07.0 **/ PopplerAnnot *poppler_annot_stamp_new(PopplerDocument *doc, PopplerRectangle *rect) { Annot *annot; PDFRectangle pdf_rect(rect->x1, rect->y1, rect->x2, rect->y2); annot = new AnnotStamp(doc->doc, &pdf_rect); return _poppler_annot_stamp_new(annot); } static gboolean get_raw_data_from_cairo_image(cairo_surface_t *image, cairo_format_t format, const int width, const int height, const size_t rowstride_c, GByteArray *data, GByteArray *soft_mask_data) { gboolean has_alpha = format == CAIRO_FORMAT_ARGB32; #if G_BYTE_ORDER == G_LITTLE_ENDIAN static const size_t CAIRO_B = 0; static const size_t CAIRO_G = 1; static const size_t CAIRO_R = 2; static const size_t CAIRO_A = 3; #elif G_BYTE_ORDER == G_BIG_ENDIAN static const size_t CAIRO_A = 0; static const size_t CAIRO_R = 1; static const size_t CAIRO_G = 2; static const size_t CAIRO_B = 3; #else # error "Unsupported endian type" #endif cairo_surface_flush(image); unsigned char *pixels_c = cairo_image_surface_get_data(image); if (format == CAIRO_FORMAT_ARGB32 || format == CAIRO_FORMAT_RGB24) { unsigned char pixel[3]; for (int h = 0; h < height; h++) { unsigned char *iter_c = pixels_c + h * rowstride_c; for (int w = 0; w < width; w++) { pixel[0] = iter_c[CAIRO_R]; pixel[1] = iter_c[CAIRO_G]; pixel[2] = iter_c[CAIRO_B]; iter_c += 4; g_byte_array_append(data, (guint8 *)pixel, 3); if (has_alpha) { g_byte_array_append(soft_mask_data, (guint8 *)&iter_c[CAIRO_A], 1); } } } return TRUE; } return FALSE; } AnnotStampImageHelper *_poppler_convert_cairo_image_to_stamp_image_helper(cairo_surface_t *image, PDFDoc *doc, GError **error) { AnnotStampImageHelper *annotImg; GByteArray *data; GByteArray *sMaskData; int bitsPerComponent; const int width = cairo_image_surface_get_width(image); const int height = cairo_image_surface_get_height(image); const size_t rowstride_c = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); cairo_format_t format = cairo_image_surface_get_format(image); ColorSpace colorSpace; if (format == CAIRO_FORMAT_ARGB32 || format == CAIRO_FORMAT_RGB24) { colorSpace = ColorSpace::DeviceRGB; bitsPerComponent = 8; } else { g_set_error(error, POPPLER_ERROR, POPPLER_ERROR_INVALID, "Invalid or unsupported cairo image type %u", (unsigned int)format); return nullptr; } data = g_byte_array_sized_new((guint)((width * 4) + rowstride_c) * height); sMaskData = g_byte_array_sized_new((guint)((width * 4) + rowstride_c) * height); if (!get_raw_data_from_cairo_image(image, format, width, height, rowstride_c, data, sMaskData)) { g_set_error(error, POPPLER_ERROR, POPPLER_ERROR_INVALID, "Failed to get raw data from cairo image"); g_byte_array_unref(data); g_byte_array_unref(sMaskData); return nullptr; } if (sMaskData->len > 0) { AnnotStampImageHelper sMask(doc, width, height, ColorSpace::DeviceGray, 8, (char *)sMaskData->data, (int)sMaskData->len); annotImg = new AnnotStampImageHelper(doc, width, height, colorSpace, bitsPerComponent, (char *)data->data, (int)data->len, sMask.getRef()); } else { annotImg = new AnnotStampImageHelper(doc, width, height, colorSpace, bitsPerComponent, (char *)data->data, (int)data->len); } g_byte_array_unref(data); g_byte_array_unref(sMaskData); return annotImg; } /* Public methods */ /** * poppler_annot_get_annot_type: * @poppler_annot: a #PopplerAnnot * * Gets the type of @poppler_annot * * Return value: #PopplerAnnotType of @poppler_annot. **/ PopplerAnnotType poppler_annot_get_annot_type(PopplerAnnot *poppler_annot) { g_return_val_if_fail(POPPLER_IS_ANNOT(poppler_annot), POPPLER_ANNOT_UNKNOWN); switch (poppler_annot->annot->getType()) { case Annot::typeText: return POPPLER_ANNOT_TEXT; case Annot::typeLink: return POPPLER_ANNOT_LINK; case Annot::typeFreeText: return POPPLER_ANNOT_FREE_TEXT; case Annot::typeLine: return POPPLER_ANNOT_LINE; case Annot::typeSquare: return POPPLER_ANNOT_SQUARE; case Annot::typeCircle: return POPPLER_ANNOT_CIRCLE; case Annot::typePolygon: return POPPLER_ANNOT_POLYGON; case Annot::typePolyLine: return POPPLER_ANNOT_POLY_LINE; case Annot::typeHighlight: return POPPLER_ANNOT_HIGHLIGHT; case Annot::typeUnderline: return POPPLER_ANNOT_UNDERLINE; case Annot::typeSquiggly: return POPPLER_ANNOT_SQUIGGLY; case Annot::typeStrikeOut: return POPPLER_ANNOT_STRIKE_OUT; case Annot::typeStamp: return POPPLER_ANNOT_STAMP; case Annot::typeCaret: return POPPLER_ANNOT_CARET; case Annot::typeInk: return POPPLER_ANNOT_INK; case Annot::typePopup: return POPPLER_ANNOT_POPUP; case Annot::typeFileAttachment: return POPPLER_ANNOT_FILE_ATTACHMENT; case Annot::typeSound: return POPPLER_ANNOT_SOUND; case Annot::typeMovie: return POPPLER_ANNOT_MOVIE; case Annot::typeWidget: return POPPLER_ANNOT_WIDGET; case Annot::typeScreen: return POPPLER_ANNOT_SCREEN; case Annot::typePrinterMark: return POPPLER_ANNOT_PRINTER_MARK; case Annot::typeTrapNet: return POPPLER_ANNOT_TRAP_NET; case Annot::typeWatermark: return POPPLER_ANNOT_WATERMARK; case Annot::type3D: return POPPLER_ANNOT_3D; default: g_warning("Unsupported Annot Type"); } return POPPLER_ANNOT_UNKNOWN; } /** * poppler_annot_get_contents: * @poppler_annot: a #PopplerAnnot * * Retrieves the contents of @poppler_annot. * * Return value: a new allocated string with the contents of @poppler_annot. It * must be freed with g_free() when done. **/ gchar *poppler_annot_get_contents(PopplerAnnot *poppler_annot) { const GooString *contents; g_return_val_if_fail(POPPLER_IS_ANNOT(poppler_annot), NULL); contents = poppler_annot->annot->getContents(); return contents && contents->getLength() > 0 ? _poppler_goo_string_to_utf8(contents) : nullptr; } /** * poppler_annot_set_contents: * @poppler_annot: a #PopplerAnnot * @contents: a text string containing the new contents * * Sets the contents of @poppler_annot to the given value, * replacing the current contents. * * Since: 0.12 **/ void poppler_annot_set_contents(PopplerAnnot *poppler_annot, const gchar *contents) { gchar *tmp; gsize length = 0; g_return_if_fail(POPPLER_IS_ANNOT(poppler_annot)); tmp = contents ? g_convert(contents, -1, "UTF-16BE", "UTF-8", nullptr, &length, nullptr) : nullptr; poppler_annot->annot->setContents(std::make_unique(tmp, length)); g_free(tmp); } /** * poppler_annot_get_name: * @poppler_annot: a #PopplerAnnot * * Retrieves the name of @poppler_annot. * * Return value: a new allocated string with the name of @poppler_annot. It must * be freed with g_free() when done. **/ gchar *poppler_annot_get_name(PopplerAnnot *poppler_annot) { const GooString *name; g_return_val_if_fail(POPPLER_IS_ANNOT(poppler_annot), NULL); name = poppler_annot->annot->getName(); return name ? _poppler_goo_string_to_utf8(name) : nullptr; } /** * poppler_annot_get_modified: * @poppler_annot: a #PopplerAnnot * * Retrieves the last modification data of @poppler_annot. The returned * string will be either a PDF format date or a text string. * See also #poppler_date_parse() * * Return value: a new allocated string with the last modification data of * @poppler_annot. It must be freed with g_free() when done. **/ gchar *poppler_annot_get_modified(PopplerAnnot *poppler_annot) { const GooString *text; g_return_val_if_fail(POPPLER_IS_ANNOT(poppler_annot), NULL); text = poppler_annot->annot->getModified(); return text ? _poppler_goo_string_to_utf8(text) : nullptr; } /** * poppler_annot_get_flags: * @poppler_annot: a #PopplerAnnot * * Retrieves the flag field specifying various characteristics of the * @poppler_annot. * * Return value: the flag field of @poppler_annot. **/ PopplerAnnotFlag poppler_annot_get_flags(PopplerAnnot *poppler_annot) { g_return_val_if_fail(POPPLER_IS_ANNOT(poppler_annot), (PopplerAnnotFlag)0); return (PopplerAnnotFlag)poppler_annot->annot->getFlags(); } /** * poppler_annot_set_flags: * @poppler_annot: a #PopplerAnnot * @flags: a #PopplerAnnotFlag * * Sets the flag field specifying various characteristics of the * @poppler_annot. * * Since: 0.22 **/ void poppler_annot_set_flags(PopplerAnnot *poppler_annot, PopplerAnnotFlag flags) { g_return_if_fail(POPPLER_IS_ANNOT(poppler_annot)); if (poppler_annot_get_flags(poppler_annot) == flags) { return; } 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 * * Retrieves the color of @poppler_annot. * * Return value: a new allocated #PopplerColor with the color values of * @poppler_annot, or %NULL. It must be freed with g_free() when done. **/ 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()); } /** * poppler_annot_set_color: * @poppler_annot: a #PopplerAnnot * @poppler_color: (allow-none): a #PopplerColor, or %NULL * * Sets the color of @poppler_annot. * * Since: 0.16 */ 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_get_page_index: * @poppler_annot: a #PopplerAnnot * * Returns the page index to which @poppler_annot is associated, or -1 if unknown * * Return value: page index or -1 * * Since: 0.14 **/ gint poppler_annot_get_page_index(PopplerAnnot *poppler_annot) { gint page_num; g_return_val_if_fail(POPPLER_IS_ANNOT(poppler_annot), -1); page_num = poppler_annot->annot->getPageNum(); return page_num <= 0 ? -1 : page_num - 1; } /* Returns cropbox rect for the page where the passed in @poppler_annot is in, * or NULL when could not retrieve the cropbox. If @page_out is non-null then * it will be set with the page that @poppler_annot is in. */ const PDFRectangle *_poppler_annot_get_cropbox_and_page(PopplerAnnot *poppler_annot, Page **page_out) { int page_index; /* A returned zero means annot is not added to any page yet */ page_index = poppler_annot->annot->getPageNum(); if (page_index) { Page *page; page = poppler_annot->annot->getDoc()->getPage(page_index); if (page) { if (page_out) { *page_out = page; } return page->getCropBox(); } } return nullptr; } /* Returns cropbox rect for the page where the passed in @poppler_annot is in, * or NULL when could not retrieve the cropbox */ const PDFRectangle *_poppler_annot_get_cropbox(PopplerAnnot *poppler_annot) { return _poppler_annot_get_cropbox_and_page(poppler_annot, nullptr); } /** * poppler_annot_get_rectangle: * @poppler_annot: a #PopplerAnnot * @poppler_rect: (out): a #PopplerRectangle to store the annotation's coordinates * * Retrieves the rectangle representing the page coordinates where the * annotation @poppler_annot is placed. * * Since: 0.26 */ void poppler_annot_get_rectangle(PopplerAnnot *poppler_annot, PopplerRectangle *poppler_rect) { const PDFRectangle *crop_box; PDFRectangle zerobox; Page *page = nullptr; g_return_if_fail(POPPLER_IS_ANNOT(poppler_annot)); g_return_if_fail(poppler_rect != nullptr); crop_box = _poppler_annot_get_cropbox_and_page(poppler_annot, &page); if (!crop_box) { zerobox = PDFRectangle(); crop_box = &zerobox; } const PDFRectangle &annot_rect = poppler_annot->annot->getRect(); poppler_rect->x1 = annot_rect.x1 - crop_box->x1; poppler_rect->x2 = annot_rect.x2 - crop_box->x1; poppler_rect->y1 = annot_rect.y1 - crop_box->y1; poppler_rect->y2 = annot_rect.y2 - crop_box->y1; } /** * poppler_annot_set_rectangle: * @poppler_annot: a #PopplerAnnot * @poppler_rect: a #PopplerRectangle with the new annotation's coordinates * * Move the annotation to the rectangle representing the page coordinates * where the annotation @poppler_annot should be placed. * * Since: 0.26 */ void poppler_annot_set_rectangle(PopplerAnnot *poppler_annot, PopplerRectangle *poppler_rect) { const PDFRectangle *crop_box; PDFRectangle zerobox; double x1, y1, x2, y2; Page *page = nullptr; g_return_if_fail(POPPLER_IS_ANNOT(poppler_annot)); g_return_if_fail(poppler_rect != nullptr); crop_box = _poppler_annot_get_cropbox_and_page(poppler_annot, &page); if (!crop_box) { zerobox = PDFRectangle(); crop_box = &zerobox; } x1 = poppler_rect->x1; y1 = poppler_rect->y1; x2 = poppler_rect->x2; y2 = poppler_rect->y2; if (page && SUPPORTED_ROTATION(page->getRotate())) { /* annot is inside a rotated page, as core poppler rect must be saved * un-rotated, let's proceed to un-rotate rect before saving */ _unrotate_rect_for_annot_and_page(page, poppler_annot->annot, &x1, &y1, &x2, &y2); } poppler_annot->annot->setRect(x1 + crop_box->x1, y1 + crop_box->y1, x2 + crop_box->x1, y2 + crop_box->y1); } /* PopplerAnnotMarkup */ /** * poppler_annot_markup_get_label: * @poppler_annot: a #PopplerAnnotMarkup * * Retrieves the label text of @poppler_annot. * * Return value: the label text of @poppler_annot. */ gchar *poppler_annot_markup_get_label(PopplerAnnotMarkup *poppler_annot) { AnnotMarkup *annot; const GooString *text; g_return_val_if_fail(POPPLER_IS_ANNOT_MARKUP(poppler_annot), NULL); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); text = annot->getLabel(); return text ? _poppler_goo_string_to_utf8(text) : nullptr; } /** * poppler_annot_markup_set_label: * @poppler_annot: a #PopplerAnnotMarkup * @label: (allow-none): a text string containing the new label, or %NULL * * Sets the label text of @poppler_annot, replacing the current one * * Since: 0.16 */ void poppler_annot_markup_set_label(PopplerAnnotMarkup *poppler_annot, const gchar *label) { AnnotMarkup *annot; gchar *tmp; gsize length = 0; g_return_if_fail(POPPLER_IS_ANNOT_MARKUP(poppler_annot)); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); tmp = label ? g_convert(label, -1, "UTF-16BE", "UTF-8", nullptr, &length, nullptr) : nullptr; annot->setLabel(std::make_unique(tmp, length)); g_free(tmp); } /** * poppler_annot_markup_has_popup: * @poppler_annot: a #PopplerAnnotMarkup * * Return %TRUE if the markup annotation has a popup window associated * * Return value: %TRUE, if @poppler_annot has popup, %FALSE otherwise * * Since: 0.12 **/ gboolean poppler_annot_markup_has_popup(PopplerAnnotMarkup *poppler_annot) { AnnotMarkup *annot; g_return_val_if_fail(POPPLER_IS_ANNOT_MARKUP(poppler_annot), FALSE); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); return annot->getPopup() != nullptr; } /** * poppler_annot_markup_set_popup: * @poppler_annot: a #PopplerAnnotMarkup * @popup_rect: a #PopplerRectangle * * Associates a new popup window for editing contents of @poppler_annot. * Popup window shall be displayed by viewers at @popup_rect on the page. * * Since: 0.16 */ void poppler_annot_markup_set_popup(PopplerAnnotMarkup *poppler_annot, PopplerRectangle *popup_rect) { AnnotMarkup *annot; PDFRectangle pdf_rect(popup_rect->x1, popup_rect->y1, popup_rect->x2, popup_rect->y2); g_return_if_fail(POPPLER_IS_ANNOT_MARKUP(poppler_annot)); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); annot->setPopup(std::make_unique(annot->getDoc(), &pdf_rect)); } /** * poppler_annot_markup_get_popup_is_open: * @poppler_annot: a #PopplerAnnotMarkup * * Retrieves the state of the popup window related to @poppler_annot. * * Return value: the state of @poppler_annot. %TRUE if it's open, %FALSE in * other case. **/ gboolean poppler_annot_markup_get_popup_is_open(PopplerAnnotMarkup *poppler_annot) { AnnotMarkup *annot; AnnotPopup *annot_popup; g_return_val_if_fail(POPPLER_IS_ANNOT_MARKUP(poppler_annot), FALSE); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); if ((annot_popup = annot->getPopup())) { return annot_popup->getOpen(); } return FALSE; } /** * poppler_annot_markup_set_popup_is_open: * @poppler_annot: a #PopplerAnnotMarkup * @is_open: whether popup window should initially be displayed open * * Sets the state of the popup window related to @poppler_annot. * * Since: 0.16 **/ void poppler_annot_markup_set_popup_is_open(PopplerAnnotMarkup *poppler_annot, gboolean is_open) { AnnotMarkup *annot; AnnotPopup *annot_popup; g_return_if_fail(POPPLER_IS_ANNOT_MARKUP(poppler_annot)); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); annot_popup = annot->getPopup(); if (!annot_popup) { return; } if (annot_popup->getOpen() != is_open) { annot_popup->setOpen(is_open); } } /** * poppler_annot_markup_get_popup_rectangle: * @poppler_annot: a #PopplerAnnotMarkup * @poppler_rect: (out): a #PopplerRectangle to store the popup rectangle * * Retrieves the rectangle of the popup window related to @poppler_annot. * * Return value: %TRUE if #PopplerRectangle was correctly filled, %FALSE otherwise * * Since: 0.12 **/ gboolean poppler_annot_markup_get_popup_rectangle(PopplerAnnotMarkup *poppler_annot, PopplerRectangle *poppler_rect) { AnnotMarkup *annot; Annot *annot_popup; g_return_val_if_fail(POPPLER_IS_ANNOT_MARKUP(poppler_annot), FALSE); g_return_val_if_fail(poppler_rect != nullptr, FALSE); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); annot_popup = annot->getPopup(); if (!annot_popup) { return FALSE; } const PDFRectangle &annot_rect = annot_popup->getRect(); poppler_rect->x1 = annot_rect.x1; poppler_rect->x2 = annot_rect.x2; poppler_rect->y1 = annot_rect.y1; poppler_rect->y2 = annot_rect.y2; return TRUE; } /** * poppler_annot_markup_set_popup_rectangle: * @poppler_annot: a #PopplerAnnotMarkup * @poppler_rect: a #PopplerRectangle to set * * Sets the rectangle of the popup window related to @poppler_annot. * This doesn't have any effect if @poppler_annot doesn't have a * popup associated, use poppler_annot_markup_set_popup() to associate * a popup window to a #PopplerAnnotMarkup. * * Since: 0.33 */ void poppler_annot_markup_set_popup_rectangle(PopplerAnnotMarkup *poppler_annot, PopplerRectangle *poppler_rect) { AnnotMarkup *annot; Annot *annot_popup; g_return_if_fail(POPPLER_IS_ANNOT_MARKUP(poppler_annot)); g_return_if_fail(poppler_rect != nullptr); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); annot_popup = annot->getPopup(); if (!annot_popup) { return; } annot_popup->setRect(poppler_rect->x1, poppler_rect->y1, poppler_rect->x2, poppler_rect->y2); } /** * poppler_annot_markup_get_opacity: * @poppler_annot: a #PopplerAnnotMarkup * * Retrieves the opacity value of @poppler_annot. * * Return value: the opacity value of @poppler_annot, * between 0 (transparent) and 1 (opaque) */ gdouble poppler_annot_markup_get_opacity(PopplerAnnotMarkup *poppler_annot) { AnnotMarkup *annot; g_return_val_if_fail(POPPLER_IS_ANNOT_MARKUP(poppler_annot), 0); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); return annot->getOpacity(); } /** * poppler_annot_markup_set_opacity: * @poppler_annot: a #PopplerAnnotMarkup * @opacity: a constant opacity value, between 0 (transparent) and 1 (opaque) * * Sets the opacity of @poppler_annot. This value applies to * all visible elements of @poppler_annot in its closed state, * but not to the pop-up window that appears when it's openened * * Since: 0.16 */ void poppler_annot_markup_set_opacity(PopplerAnnotMarkup *poppler_annot, gdouble opacity) { AnnotMarkup *annot; g_return_if_fail(POPPLER_IS_ANNOT_MARKUP(poppler_annot)); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); annot->setOpacity(opacity); } /** * poppler_annot_markup_get_date: * @poppler_annot: a #PopplerAnnotMarkup * * Returns the date and time when the annotation was created * * Return value: (transfer full): a #GDate representing the date and time * when the annotation was created, or %NULL */ GDate *poppler_annot_markup_get_date(PopplerAnnotMarkup *poppler_annot) { AnnotMarkup *annot; const GooString *annot_date; time_t timet; g_return_val_if_fail(POPPLER_IS_ANNOT_MARKUP(poppler_annot), NULL); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); annot_date = annot->getDate(); if (!annot_date) { return nullptr; } if (_poppler_convert_pdf_date_to_gtime(annot_date, &timet)) { GDate *date; date = g_date_new(); g_date_set_time_t(date, timet); return date; } return nullptr; } /** * poppler_annot_markup_get_subject: * @poppler_annot: a #PopplerAnnotMarkup * * Retrives the subject text of @poppler_annot. * * Return value: the subject text of @poppler_annot. */ gchar *poppler_annot_markup_get_subject(PopplerAnnotMarkup *poppler_annot) { AnnotMarkup *annot; const GooString *text; g_return_val_if_fail(POPPLER_IS_ANNOT_MARKUP(poppler_annot), NULL); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); text = annot->getSubject(); return text ? _poppler_goo_string_to_utf8(text) : nullptr; } /** * poppler_annot_markup_get_reply_to: * @poppler_annot: a #PopplerAnnotMarkup * * Gets the reply type of @poppler_annot. * * Return value: #PopplerAnnotMarkupReplyType of @poppler_annot. */ PopplerAnnotMarkupReplyType poppler_annot_markup_get_reply_to(PopplerAnnotMarkup *poppler_annot) { AnnotMarkup *annot; g_return_val_if_fail(POPPLER_IS_ANNOT_MARKUP(poppler_annot), POPPLER_ANNOT_MARKUP_REPLY_TYPE_R); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); switch (annot->getReplyTo()) { case AnnotMarkup::replyTypeR: return POPPLER_ANNOT_MARKUP_REPLY_TYPE_R; case AnnotMarkup::replyTypeGroup: return POPPLER_ANNOT_MARKUP_REPLY_TYPE_GROUP; default: g_warning("Unsupported Annot Markup Reply To Type"); } return POPPLER_ANNOT_MARKUP_REPLY_TYPE_R; } /** * poppler_annot_markup_get_external_data: * @poppler_annot: a #PopplerAnnotMarkup * * Gets the external data type of @poppler_annot. * * Return value: #PopplerAnnotExternalDataType of @poppler_annot. */ PopplerAnnotExternalDataType poppler_annot_markup_get_external_data(PopplerAnnotMarkup *poppler_annot) { AnnotMarkup *annot; g_return_val_if_fail(POPPLER_IS_ANNOT_MARKUP(poppler_annot), POPPLER_ANNOT_EXTERNAL_DATA_MARKUP_UNKNOWN); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); switch (annot->getExData()) { case annotExternalDataMarkup3D: return POPPLER_ANNOT_EXTERNAL_DATA_MARKUP_3D; case annotExternalDataMarkupUnknown: return POPPLER_ANNOT_EXTERNAL_DATA_MARKUP_UNKNOWN; default: g_warning("Unsupported Annot Markup External Data"); } return POPPLER_ANNOT_EXTERNAL_DATA_MARKUP_UNKNOWN; } /* PopplerAnnotText */ /** * poppler_annot_text_get_is_open: * @poppler_annot: a #PopplerAnnotText * * Retrieves the state of @poppler_annot. * * Return value: the state of @poppler_annot. %TRUE if it's open, %FALSE in * other case. **/ gboolean poppler_annot_text_get_is_open(PopplerAnnotText *poppler_annot) { AnnotText *annot; g_return_val_if_fail(POPPLER_IS_ANNOT_TEXT(poppler_annot), FALSE); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); return annot->getOpen(); } /** * poppler_annot_text_set_is_open: * @poppler_annot: a #PopplerAnnotText * @is_open: whether annotation should initially be displayed open * * Sets whether @poppler_annot should initially be displayed open * * Since: 0.16 */ void poppler_annot_text_set_is_open(PopplerAnnotText *poppler_annot, gboolean is_open) { AnnotText *annot; g_return_if_fail(POPPLER_IS_ANNOT_TEXT(poppler_annot)); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); annot->setOpen(is_open); } /** * poppler_annot_text_get_icon: * @poppler_annot: a #PopplerAnnotText * * Gets name of the icon of @poppler_annot. * * Return value: a new allocated string containing the icon name */ gchar *poppler_annot_text_get_icon(PopplerAnnotText *poppler_annot) { AnnotText *annot; const GooString *text; g_return_val_if_fail(POPPLER_IS_ANNOT_TEXT(poppler_annot), NULL); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); text = annot->getIcon(); return text ? _poppler_goo_string_to_utf8(text) : nullptr; } /** * poppler_annot_text_set_icon: * @poppler_annot: a #PopplerAnnotText * @icon: the name of an icon * * Sets the icon of @poppler_annot. The following predefined * icons are currently supported: * * * #POPPLER_ANNOT_TEXT_ICON_NOTE * * * #POPPLER_ANNOT_TEXT_ICON_COMMENT * * * #POPPLER_ANNOT_TEXT_ICON_KEY * * * #POPPLER_ANNOT_TEXT_ICON_HELP * * * #POPPLER_ANNOT_TEXT_ICON_NEW_PARAGRAPH * * * #POPPLER_ANNOT_TEXT_ICON_PARAGRAPH * * * #POPPLER_ANNOT_TEXT_ICON_INSERT * * * #POPPLER_ANNOT_TEXT_ICON_CROSS * * * #POPPLER_ANNOT_TEXT_ICON_CIRCLE * * * * Since: 0.16 */ void poppler_annot_text_set_icon(PopplerAnnotText *poppler_annot, const gchar *icon) { AnnotText *annot; GooString *text; g_return_if_fail(POPPLER_IS_ANNOT_TEXT(poppler_annot)); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); text = new GooString(icon); annot->setIcon(text); delete text; } /** * poppler_annot_text_get_state: * @poppler_annot: a #PopplerAnnotText * * Retrieves the state of @poppler_annot. * * Return value: #PopplerAnnotTextState of @poppler_annot. **/ PopplerAnnotTextState poppler_annot_text_get_state(PopplerAnnotText *poppler_annot) { AnnotText *annot; g_return_val_if_fail(POPPLER_IS_ANNOT_TEXT(poppler_annot), POPPLER_ANNOT_TEXT_STATE_UNKNOWN); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); switch (annot->getState()) { case AnnotText::stateUnknown: return POPPLER_ANNOT_TEXT_STATE_UNKNOWN; case AnnotText::stateMarked: return POPPLER_ANNOT_TEXT_STATE_MARKED; case AnnotText::stateUnmarked: return POPPLER_ANNOT_TEXT_STATE_UNMARKED; case AnnotText::stateAccepted: return POPPLER_ANNOT_TEXT_STATE_ACCEPTED; case AnnotText::stateRejected: return POPPLER_ANNOT_TEXT_STATE_REJECTED; case AnnotText::stateCancelled: return POPPLER_ANNOT_TEXT_STATE_CANCELLED; case AnnotText::stateCompleted: return POPPLER_ANNOT_TEXT_STATE_COMPLETED; case AnnotText::stateNone: return POPPLER_ANNOT_TEXT_STATE_NONE; default: g_warning("Unsupported Annot Text State"); } return POPPLER_ANNOT_TEXT_STATE_UNKNOWN; } /* PopplerAnnotTextMarkup */ /** * poppler_annot_text_markup_set_quadrilaterals: * @poppler_annot: A #PopplerAnnotTextMarkup * @quadrilaterals: (element-type PopplerQuadrilateral): A #GArray of * #PopplerQuadrilaterals * * Set the regions (Quadrilaterals) to apply the text markup in @poppler_annot. * * Since: 0.26 **/ void poppler_annot_text_markup_set_quadrilaterals(PopplerAnnotTextMarkup *poppler_annot, GArray *quadrilaterals) { AnnotQuadrilaterals *quads, *quads_temp; AnnotTextMarkup *annot; const PDFRectangle *crop_box; Page *page = nullptr; g_return_if_fail(POPPLER_IS_ANNOT_TEXT_MARKUP(poppler_annot)); g_return_if_fail(quadrilaterals != nullptr && quadrilaterals->len > 0); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); crop_box = _poppler_annot_get_cropbox_and_page(POPPLER_ANNOT(poppler_annot), &page); quads = create_annot_quads_from_poppler_quads(quadrilaterals); if (page && SUPPORTED_ROTATION(page->getRotate())) { quads_temp = _page_new_quads_unrotated(page, quads); delete quads; quads = quads_temp; } if (!ZERO_CROPBOX(crop_box)) { quads_temp = quads; quads = new_quads_from_offset_cropbox(crop_box, quads, TRUE); delete quads_temp; } annot->setQuadrilaterals(quads); delete quads; } /** * poppler_annot_text_markup_get_quadrilaterals: * @poppler_annot: A #PopplerAnnotTextMarkup * * Returns a #GArray of #PopplerQuadrilateral items that map from a * location on @page to a #PopplerAnnotTextMarkup. This array must be freed * when done. * * Return value: (element-type PopplerQuadrilateral) (transfer full): A #GArray of #PopplerQuadrilateral * * Since: 0.26 **/ GArray *poppler_annot_text_markup_get_quadrilaterals(PopplerAnnotTextMarkup *poppler_annot) { const PDFRectangle *crop_box; AnnotTextMarkup *annot; g_return_val_if_fail(POPPLER_IS_ANNOT_TEXT_MARKUP(poppler_annot), NULL); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); crop_box = _poppler_annot_get_cropbox(POPPLER_ANNOT(poppler_annot)); AnnotQuadrilaterals *quads = annot->getQuadrilaterals(); return create_poppler_quads_from_annot_quads(quads, crop_box); } /* PopplerAnnotFreeText */ /** * poppler_annot_free_text_get_quadding: * @poppler_annot: a #PopplerAnnotFreeText * * Retrieves the justification of the text of @poppler_annot. * * Return value: #PopplerAnnotFreeTextQuadding of @poppler_annot. **/ PopplerAnnotFreeTextQuadding poppler_annot_free_text_get_quadding(PopplerAnnotFreeText *poppler_annot) { AnnotFreeText *annot; g_return_val_if_fail(POPPLER_IS_ANNOT_FREE_TEXT(poppler_annot), POPPLER_ANNOT_FREE_TEXT_QUADDING_LEFT_JUSTIFIED); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); switch (annot->getQuadding()) { case VariableTextQuadding::leftJustified: return POPPLER_ANNOT_FREE_TEXT_QUADDING_LEFT_JUSTIFIED; case VariableTextQuadding::centered: return POPPLER_ANNOT_FREE_TEXT_QUADDING_CENTERED; case VariableTextQuadding::rightJustified: return POPPLER_ANNOT_FREE_TEXT_QUADDING_RIGHT_JUSTIFIED; default: g_warning("Unsupported Annot Free Text Quadding"); } return POPPLER_ANNOT_FREE_TEXT_QUADDING_LEFT_JUSTIFIED; } /** * poppler_annot_free_text_get_callout_line: * @poppler_annot: a #PopplerAnnotFreeText * * Retrieves a #PopplerAnnotCalloutLine of four or six numbers specifying a callout * line attached to the @poppler_annot. * * Return value: a new allocated #PopplerAnnotCalloutLine if the annot has a callout * line, %NULL in other case. It must be freed with g_free() when * done. **/ PopplerAnnotCalloutLine *poppler_annot_free_text_get_callout_line(PopplerAnnotFreeText *poppler_annot) { AnnotFreeText *annot; AnnotCalloutLine *line; g_return_val_if_fail(POPPLER_IS_ANNOT_FREE_TEXT(poppler_annot), NULL); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); if ((line = annot->getCalloutLine())) { AnnotCalloutMultiLine *multiline; PopplerAnnotCalloutLine *callout = g_new0(PopplerAnnotCalloutLine, 1); callout->x1 = line->getX1(); callout->y1 = line->getY1(); callout->x2 = line->getX2(); callout->y2 = line->getY2(); if ((multiline = dynamic_cast(line))) { callout->multiline = TRUE; callout->x3 = multiline->getX3(); callout->y3 = multiline->getY3(); return callout; } callout->multiline = FALSE; return callout; } return nullptr; } /* PopplerAnnotFileAttachment */ /** * poppler_annot_file_attachment_get_attachment: * @poppler_annot: a #PopplerAnnotFileAttachment * * Creates a #PopplerAttachment for the file of the file attachment annotation @annot. * The #PopplerAttachment must be unrefed with g_object_unref by the caller. * * Return value: (transfer full): @PopplerAttachment * * Since: 0.14 **/ PopplerAttachment *poppler_annot_file_attachment_get_attachment(PopplerAnnotFileAttachment *poppler_annot) { AnnotFileAttachment *annot; PopplerAttachment *attachment; g_return_val_if_fail(POPPLER_IS_ANNOT_FILE_ATTACHMENT(poppler_annot), NULL); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); FileSpec *file = new FileSpec(annot->getFile()); attachment = _poppler_attachment_new(file); delete file; return attachment; } /** * poppler_annot_file_attachment_get_name: * @poppler_annot: a #PopplerAnnotFileAttachment * * Retrieves the name of @poppler_annot. * * Return value: a new allocated string with the name of @poppler_annot. It must * be freed with g_free() when done. * Since: 0.14 **/ gchar *poppler_annot_file_attachment_get_name(PopplerAnnotFileAttachment *poppler_annot) { AnnotFileAttachment *annot; const GooString *name; g_return_val_if_fail(POPPLER_IS_ANNOT_FILE_ATTACHMENT(poppler_annot), NULL); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); name = annot->getName(); return name ? _poppler_goo_string_to_utf8(name) : nullptr; } /* PopplerAnnotCalloutLine */ G_DEFINE_BOXED_TYPE(PopplerAnnotCalloutLine, poppler_annot_callout_line, poppler_annot_callout_line_copy, poppler_annot_callout_line_free) /** * poppler_annot_callout_line_new: * * Creates a new empty #PopplerAnnotCalloutLine. * * Return value: a new allocated #PopplerAnnotCalloutLine, %NULL in other case. * It must be freed when done. **/ PopplerAnnotCalloutLine *poppler_annot_callout_line_new(void) { return g_new0(PopplerAnnotCalloutLine, 1); } /** * poppler_annot_callout_line_copy: * @callout: the #PopplerAnnotCalloutLine to be copied. * * It does copy @callout to a new #PopplerAnnotCalloutLine. * * Return value: a new allocated #PopplerAnnotCalloutLine as exact copy of * @callout, %NULL in other case. It must be freed when done. **/ PopplerAnnotCalloutLine *poppler_annot_callout_line_copy(PopplerAnnotCalloutLine *callout) { PopplerAnnotCalloutLine *new_callout; g_return_val_if_fail(callout != nullptr, NULL); new_callout = g_new0(PopplerAnnotCalloutLine, 1); *new_callout = *callout; return new_callout; } /** * poppler_annot_callout_line_free: * @callout: a #PopplerAnnotCalloutLine * * Frees the memory used by #PopplerAnnotCalloutLine. **/ void poppler_annot_callout_line_free(PopplerAnnotCalloutLine *callout) { g_free(callout); } /* PopplerAnnotMovie */ /** * poppler_annot_movie_get_title: * @poppler_annot: a #PopplerAnnotMovie * * Retrieves the movie title of @poppler_annot. * * Return value: the title text of @poppler_annot. * * Since: 0.14 */ gchar *poppler_annot_movie_get_title(PopplerAnnotMovie *poppler_annot) { AnnotMovie *annot; const GooString *title; g_return_val_if_fail(POPPLER_IS_ANNOT_MOVIE(poppler_annot), NULL); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); title = annot->getTitle(); return title ? _poppler_goo_string_to_utf8(title) : nullptr; } /** * poppler_annot_movie_get_movie: * @poppler_annot: a #PopplerAnnotMovie * * Retrieves the movie object (PopplerMovie) stored in the @poppler_annot. * * Return value: (transfer none): the movie object stored in the @poppler_annot. The returned * object is owned by #PopplerAnnotMovie and should not be freed * * Since: 0.14 */ PopplerMovie *poppler_annot_movie_get_movie(PopplerAnnotMovie *poppler_annot) { return poppler_annot->movie; } /* PopplerAnnotScreen */ /** * poppler_annot_screen_get_action: * @poppler_annot: a #PopplerAnnotScreen * * Retrieves the action (#PopplerAction) that shall be performed when @poppler_annot is activated * * Return value: (transfer none): the action to perform. The returned * object is owned by @poppler_annot and should not be freed * * Since: 0.14 */ PopplerAction *poppler_annot_screen_get_action(PopplerAnnotScreen *poppler_annot) { return poppler_annot->action; } /* PopplerAnnotLine */ /** * poppler_annot_line_set_vertices: * @poppler_annot: a #PopplerAnnotLine * @start: a #PopplerPoint of the starting vertice * @end: a #PopplerPoint of the ending vertice * * Set the coordinate points where the @poppler_annot starts and ends. * * Since: 0.26 */ void poppler_annot_line_set_vertices(PopplerAnnotLine *poppler_annot, PopplerPoint *start, PopplerPoint *end) { AnnotLine *annot; g_return_if_fail(POPPLER_IS_ANNOT_LINE(poppler_annot)); g_return_if_fail(start != nullptr); g_return_if_fail(end != nullptr); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); annot->setVertices(start->x, start->y, end->x, end->y); } /* PopplerAnnotCircle and PopplerAnnotSquare helpers */ static PopplerColor *poppler_annot_geometry_get_interior_color(PopplerAnnot *poppler_annot) { AnnotGeometry *annot; annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); return create_poppler_color_from_annot_color(annot->getInteriorColor()); } static void poppler_annot_geometry_set_interior_color(PopplerAnnot *poppler_annot, PopplerColor *poppler_color) { AnnotGeometry *annot; annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); annot->setInteriorColor(create_annot_color_from_poppler_color(poppler_color)); } /* PopplerAnnotCircle */ /** * poppler_annot_circle_get_interior_color: * @poppler_annot: a #PopplerAnnotCircle * * Retrieves the interior color of @poppler_annot. * * Return value: a new allocated #PopplerColor with the color values of * @poppler_annot, or %NULL. It must be freed with g_free() when done. * * Since: 0.26 */ PopplerColor *poppler_annot_circle_get_interior_color(PopplerAnnotCircle *poppler_annot) { g_return_val_if_fail(POPPLER_IS_ANNOT_CIRCLE(poppler_annot), NULL); return poppler_annot_geometry_get_interior_color(POPPLER_ANNOT(poppler_annot)); } /** * poppler_annot_circle_set_interior_color: * @poppler_annot: a #PopplerAnnotCircle * @poppler_color: (allow-none): a #PopplerColor, or %NULL * * Sets the interior color of @poppler_annot. * * Since: 0.26 */ void poppler_annot_circle_set_interior_color(PopplerAnnotCircle *poppler_annot, PopplerColor *poppler_color) { g_return_if_fail(POPPLER_IS_ANNOT_CIRCLE(poppler_annot)); poppler_annot_geometry_set_interior_color(POPPLER_ANNOT(poppler_annot), poppler_color); } /* PopplerAnnotSquare */ /** * poppler_annot_square_get_interior_color: * @poppler_annot: a #PopplerAnnotSquare * * Retrieves the interior color of @poppler_annot. * * Return value: a new allocated #PopplerColor with the color values of * @poppler_annot, or %NULL. It must be freed with g_free() when done. * * Since: 0.26 */ PopplerColor *poppler_annot_square_get_interior_color(PopplerAnnotSquare *poppler_annot) { g_return_val_if_fail(POPPLER_IS_ANNOT_SQUARE(poppler_annot), NULL); return poppler_annot_geometry_get_interior_color(POPPLER_ANNOT(poppler_annot)); } /** * poppler_annot_square_set_interior_color: * @poppler_annot: a #PopplerAnnotSquare * @poppler_color: (allow-none): a #PopplerColor, or %NULL * * Sets the interior color of @poppler_annot. * * Since: 0.26 */ void poppler_annot_square_set_interior_color(PopplerAnnotSquare *poppler_annot, PopplerColor *poppler_color) { g_return_if_fail(POPPLER_IS_ANNOT_SQUARE(poppler_annot)); poppler_annot_geometry_set_interior_color(POPPLER_ANNOT(poppler_annot), poppler_color); } /** * poppler_annot_stamp_get_icon: * @poppler_annot: a #PopplerAnnotStamp * * Return value: the corresponding #PopplerAnnotStampIcon of the icon * * Since: 22.07.0 */ PopplerAnnotStampIcon poppler_annot_stamp_get_icon(PopplerAnnotStamp *poppler_annot) { AnnotStamp *annot; const GooString *text; g_return_val_if_fail(POPPLER_IS_ANNOT_STAMP(poppler_annot), POPPLER_ANNOT_STAMP_ICON_UNKNOWN); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); text = annot->getIcon(); if (!text) { return POPPLER_ANNOT_STAMP_ICON_NONE; } if (!text->cmp("Approved")) { return POPPLER_ANNOT_STAMP_ICON_APPROVED; } else if (!text->cmp("AsIs")) { return POPPLER_ANNOT_STAMP_ICON_AS_IS; } else if (!text->cmp("Confidential")) { return POPPLER_ANNOT_STAMP_ICON_CONFIDENTIAL; } else if (!text->cmp("Final")) { return POPPLER_ANNOT_STAMP_ICON_FINAL; } else if (!text->cmp("Experimental")) { return POPPLER_ANNOT_STAMP_ICON_EXPERIMENTAL; } else if (!text->cmp("Expired")) { return POPPLER_ANNOT_STAMP_ICON_EXPIRED; } else if (!text->cmp("NotApproved")) { return POPPLER_ANNOT_STAMP_ICON_NOT_APPROVED; } else if (!text->cmp("NotForPublicRelease")) { return POPPLER_ANNOT_STAMP_ICON_NOT_FOR_PUBLIC_RELEASE; } else if (!text->cmp("Sold")) { return POPPLER_ANNOT_STAMP_ICON_SOLD; } else if (!text->cmp("Departmental")) { return POPPLER_ANNOT_STAMP_ICON_DEPARTMENTAL; } else if (!text->cmp("ForComment")) { return POPPLER_ANNOT_STAMP_ICON_FOR_COMMENT; } else if (!text->cmp("ForPublicRelease")) { return POPPLER_ANNOT_STAMP_ICON_FOR_PUBLIC_RELEASE; } else if (!text->cmp("TopSecret")) { return POPPLER_ANNOT_STAMP_ICON_TOP_SECRET; } return POPPLER_ANNOT_STAMP_ICON_UNKNOWN; } /** * poppler_annot_stamp_set_icon: * @poppler_annot: a #PopplerAnnotStamp * @icon: the #PopplerAnnotStampIcon type of the icon * * Sets the icon of @poppler_annot to be one of the predefined values in #PopplerAnnotStampIcon * * Since: 22.07.0 */ void poppler_annot_stamp_set_icon(PopplerAnnotStamp *poppler_annot, PopplerAnnotStampIcon icon) { AnnotStamp *annot; GooString *goo_str; const gchar *text; g_return_if_fail(POPPLER_IS_ANNOT_STAMP(poppler_annot)); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); if (icon == POPPLER_ANNOT_STAMP_ICON_NONE) { annot->setIcon(nullptr); return; } if (icon == POPPLER_ANNOT_STAMP_ICON_APPROVED) { text = "Approved"; } else if (icon == POPPLER_ANNOT_STAMP_ICON_AS_IS) { text = "AsIs"; } else if (icon == POPPLER_ANNOT_STAMP_ICON_CONFIDENTIAL) { text = "Confidential"; } else if (icon == POPPLER_ANNOT_STAMP_ICON_FINAL) { text = "Final"; } else if (icon == POPPLER_ANNOT_STAMP_ICON_EXPERIMENTAL) { text = "Experimental"; } else if (icon == POPPLER_ANNOT_STAMP_ICON_EXPIRED) { text = "Expired"; } else if (icon == POPPLER_ANNOT_STAMP_ICON_NOT_APPROVED) { text = "NotApproved"; } else if (icon == POPPLER_ANNOT_STAMP_ICON_NOT_FOR_PUBLIC_RELEASE) { text = "NotForPublicRelease"; } else if (icon == POPPLER_ANNOT_STAMP_ICON_SOLD) { text = "Sold"; } else if (icon == POPPLER_ANNOT_STAMP_ICON_DEPARTMENTAL) { text = "Departmental"; } else if (icon == POPPLER_ANNOT_STAMP_ICON_FOR_COMMENT) { text = "ForComment"; } else if (icon == POPPLER_ANNOT_STAMP_ICON_FOR_PUBLIC_RELEASE) { text = "ForPublicRelease"; } else if (icon == POPPLER_ANNOT_STAMP_ICON_TOP_SECRET) { text = "TopSecret"; } else { return; /* POPPLER_ANNOT_STAMP_ICON_UNKNOWN */ } goo_str = new GooString(text); annot->setIcon(goo_str); delete goo_str; } /** * poppler_annot_stamp_set_custom_image: * @poppler_annot: a #PopplerAnnotStamp * @image: an image cairo surface * @error: (nullable): return location for error, or %NULL. * * Sets the custom image of @poppler_annot to be @image * * Return value: %TRUE on success, %FALSE otherwise. * * Since: 22.07.0 */ gboolean poppler_annot_stamp_set_custom_image(PopplerAnnotStamp *poppler_annot, cairo_surface_t *image, GError **error) { AnnotStamp *annot; AnnotStampImageHelper *annot_image_helper; g_return_val_if_fail(POPPLER_IS_ANNOT_STAMP(poppler_annot), FALSE); annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); annot_image_helper = _poppler_convert_cairo_image_to_stamp_image_helper(image, annot->getDoc(), error); if (!annot_image_helper) { return FALSE; } annot->setCustomImage(annot_image_helper); return TRUE; } poppler-24.02.0/glib/poppler-annot.h000066400000000000000000000362161455701731300172420ustar00rootroot00000000000000/* poppler-annot.h: glib interface to poppler * * Copyright (C) 2007 Inigo Martinez * Copyright (C) 2009 Carlos Garcia Campos * * 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_ANNOT_H__ #define __POPPLER_ANNOT_H__ #include #include "poppler.h" G_BEGIN_DECLS #define POPPLER_TYPE_ANNOT (poppler_annot_get_type()) #define POPPLER_ANNOT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), POPPLER_TYPE_ANNOT, PopplerAnnot)) #define POPPLER_IS_ANNOT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), POPPLER_TYPE_ANNOT)) #define POPPLER_TYPE_ANNOT_MARKUP (poppler_annot_markup_get_type()) #define POPPLER_ANNOT_MARKUP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), POPPLER_TYPE_ANNOT_MARKUP, PopplerAnnotMarkup)) #define POPPLER_IS_ANNOT_MARKUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), POPPLER_TYPE_ANNOT_MARKUP)) #define POPPLER_TYPE_ANNOT_TEXT (poppler_annot_text_get_type()) #define POPPLER_ANNOT_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), POPPLER_TYPE_ANNOT_TEXT, PopplerAnnotText)) #define POPPLER_IS_ANNOT_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), POPPLER_TYPE_ANNOT_TEXT)) #define POPPLER_TYPE_ANNOT_TEXT_MARKUP (poppler_annot_text_markup_get_type()) #define POPPLER_ANNOT_TEXT_MARKUP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), POPPLER_TYPE_ANNOT_TEXT_MARKUP, PopplerAnnotTextMarkup)) #define POPPLER_IS_ANNOT_TEXT_MARKUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), POPPLER_TYPE_ANNOT_TEXT_MARKUP)) #define POPPLER_TYPE_ANNOT_FREE_TEXT (poppler_annot_free_text_get_type()) #define POPPLER_ANNOT_FREE_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), POPPLER_TYPE_ANNOT_FREE_TEXT, PopplerAnnotFreeText)) #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_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()) #define POPPLER_ANNOT_MOVIE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), POPPLER_TYPE_ANNOT_MOVIE, PopplerAnnotMovie)) #define POPPLER_IS_ANNOT_MOVIE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), POPPLER_TYPE_ANNOT_MOVIE)) #define POPPLER_TYPE_ANNOT_SCREEN (poppler_annot_screen_get_type()) #define POPPLER_ANNOT_SCREEN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), POPPLER_TYPE_ANNOT_SCREEN, PopplerAnnotScreen)) #define POPPLER_IS_ANNOT_SCREEN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), POPPLER_TYPE_ANNOT_SCREEN)) #define POPPLER_TYPE_ANNOT_LINE (poppler_annot_line_get_type()) #define POPPLER_ANNOT_LINE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), POPPLER_TYPE_ANNOT_LINE, PopplerAnnotLine)) #define POPPLER_IS_ANNOT_LINE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), POPPLER_TYPE_ANNOT_LINE)) #define POPPLER_TYPE_ANNOT_CALLOUT_LINE (poppler_annot_callout_line_get_type()) #define POPPLER_TYPE_ANNOT_CIRCLE (poppler_annot_circle_get_type()) #define POPPLER_ANNOT_CIRCLE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), POPPLER_TYPE_ANNOT_CIRCLE, PopplerAnnotCircle)) #define POPPLER_IS_ANNOT_CIRCLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), POPPLER_TYPE_ANNOT_CIRCLE)) #define POPPLER_TYPE_ANNOT_SQUARE (poppler_annot_square_get_type()) #define POPPLER_ANNOT_SQUARE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), POPPLER_TYPE_ANNOT_SQUARE, PopplerAnnotSquare)) #define POPPLER_IS_ANNOT_SQUARE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), POPPLER_TYPE_ANNOT_SQUARE)) #define POPPLER_TYPE_ANNOT_STAMP (poppler_annot_stamp_get_type()) #define POPPLER_ANNOT_STAMP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), POPPLER_TYPE_ANNOT_STAMP, PopplerAnnotStamp)) #define POPPLER_IS_ANNOT_STAMP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), POPPLER_TYPE_ANNOT_STAMP)) typedef enum { POPPLER_ANNOT_UNKNOWN, POPPLER_ANNOT_TEXT, POPPLER_ANNOT_LINK, POPPLER_ANNOT_FREE_TEXT, POPPLER_ANNOT_LINE, POPPLER_ANNOT_SQUARE, POPPLER_ANNOT_CIRCLE, POPPLER_ANNOT_POLYGON, POPPLER_ANNOT_POLY_LINE, POPPLER_ANNOT_HIGHLIGHT, POPPLER_ANNOT_UNDERLINE, POPPLER_ANNOT_SQUIGGLY, POPPLER_ANNOT_STRIKE_OUT, POPPLER_ANNOT_STAMP, POPPLER_ANNOT_CARET, POPPLER_ANNOT_INK, POPPLER_ANNOT_POPUP, POPPLER_ANNOT_FILE_ATTACHMENT, POPPLER_ANNOT_SOUND, POPPLER_ANNOT_MOVIE, POPPLER_ANNOT_WIDGET, POPPLER_ANNOT_SCREEN, POPPLER_ANNOT_PRINTER_MARK, POPPLER_ANNOT_TRAP_NET, POPPLER_ANNOT_WATERMARK, POPPLER_ANNOT_3D } PopplerAnnotType; typedef enum /*< flags >*/ { POPPLER_ANNOT_FLAG_UNKNOWN = 0, POPPLER_ANNOT_FLAG_INVISIBLE = 1 << 0, POPPLER_ANNOT_FLAG_HIDDEN = 1 << 1, POPPLER_ANNOT_FLAG_PRINT = 1 << 2, POPPLER_ANNOT_FLAG_NO_ZOOM = 1 << 3, POPPLER_ANNOT_FLAG_NO_ROTATE = 1 << 4, POPPLER_ANNOT_FLAG_NO_VIEW = 1 << 5, POPPLER_ANNOT_FLAG_READ_ONLY = 1 << 6, POPPLER_ANNOT_FLAG_LOCKED = 1 << 7, POPPLER_ANNOT_FLAG_TOGGLE_NO_VIEW = 1 << 8, POPPLER_ANNOT_FLAG_LOCKED_CONTENTS = 1 << 9 } PopplerAnnotFlag; typedef enum { POPPLER_ANNOT_MARKUP_REPLY_TYPE_R, POPPLER_ANNOT_MARKUP_REPLY_TYPE_GROUP } PopplerAnnotMarkupReplyType; typedef enum { POPPLER_ANNOT_EXTERNAL_DATA_MARKUP_3D, POPPLER_ANNOT_EXTERNAL_DATA_MARKUP_UNKNOWN } PopplerAnnotExternalDataType; #define POPPLER_ANNOT_TEXT_ICON_NOTE "Note" #define POPPLER_ANNOT_TEXT_ICON_COMMENT "Comment" #define POPPLER_ANNOT_TEXT_ICON_KEY "Key" #define POPPLER_ANNOT_TEXT_ICON_HELP "Help" #define POPPLER_ANNOT_TEXT_ICON_NEW_PARAGRAPH "NewParagraph" #define POPPLER_ANNOT_TEXT_ICON_PARAGRAPH "Paragraph" #define POPPLER_ANNOT_TEXT_ICON_INSERT "Insert" #define POPPLER_ANNOT_TEXT_ICON_CROSS "Cross" #define POPPLER_ANNOT_TEXT_ICON_CIRCLE "Circle" typedef enum { POPPLER_ANNOT_TEXT_STATE_MARKED, POPPLER_ANNOT_TEXT_STATE_UNMARKED, POPPLER_ANNOT_TEXT_STATE_ACCEPTED, POPPLER_ANNOT_TEXT_STATE_REJECTED, POPPLER_ANNOT_TEXT_STATE_CANCELLED, POPPLER_ANNOT_TEXT_STATE_COMPLETED, POPPLER_ANNOT_TEXT_STATE_NONE, POPPLER_ANNOT_TEXT_STATE_UNKNOWN } PopplerAnnotTextState; typedef enum { POPPLER_ANNOT_FREE_TEXT_QUADDING_LEFT_JUSTIFIED, POPPLER_ANNOT_FREE_TEXT_QUADDING_CENTERED, POPPLER_ANNOT_FREE_TEXT_QUADDING_RIGHT_JUSTIFIED } PopplerAnnotFreeTextQuadding; struct _PopplerAnnotCalloutLine { gboolean multiline; gdouble x1; gdouble y1; gdouble x2; gdouble y2; gdouble x3; gdouble y3; }; typedef enum { POPPLER_ANNOT_STAMP_ICON_UNKNOWN = 0, POPPLER_ANNOT_STAMP_ICON_APPROVED, POPPLER_ANNOT_STAMP_ICON_AS_IS, POPPLER_ANNOT_STAMP_ICON_CONFIDENTIAL, POPPLER_ANNOT_STAMP_ICON_FINAL, POPPLER_ANNOT_STAMP_ICON_EXPERIMENTAL, POPPLER_ANNOT_STAMP_ICON_EXPIRED, POPPLER_ANNOT_STAMP_ICON_NOT_APPROVED, POPPLER_ANNOT_STAMP_ICON_NOT_FOR_PUBLIC_RELEASE, POPPLER_ANNOT_STAMP_ICON_SOLD, POPPLER_ANNOT_STAMP_ICON_DEPARTMENTAL, POPPLER_ANNOT_STAMP_ICON_FOR_COMMENT, POPPLER_ANNOT_STAMP_ICON_FOR_PUBLIC_RELEASE, POPPLER_ANNOT_STAMP_ICON_TOP_SECRET, POPPLER_ANNOT_STAMP_ICON_NONE } PopplerAnnotStampIcon; POPPLER_PUBLIC GType poppler_annot_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerAnnotType poppler_annot_get_annot_type(PopplerAnnot *poppler_annot); POPPLER_PUBLIC gchar *poppler_annot_get_contents(PopplerAnnot *poppler_annot); POPPLER_PUBLIC void poppler_annot_set_contents(PopplerAnnot *poppler_annot, const gchar *contents); POPPLER_PUBLIC gchar *poppler_annot_get_name(PopplerAnnot *poppler_annot); POPPLER_PUBLIC gchar *poppler_annot_get_modified(PopplerAnnot *poppler_annot); POPPLER_PUBLIC PopplerAnnotFlag poppler_annot_get_flags(PopplerAnnot *poppler_annot); POPPLER_PUBLIC void poppler_annot_set_flags(PopplerAnnot *poppler_annot, PopplerAnnotFlag flags); POPPLER_PUBLIC PopplerColor *poppler_annot_get_color(PopplerAnnot *poppler_annot); POPPLER_PUBLIC void poppler_annot_set_color(PopplerAnnot *poppler_annot, PopplerColor *poppler_color); POPPLER_PUBLIC gint poppler_annot_get_page_index(PopplerAnnot *poppler_annot); 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); /* PopplerAnnotMarkup */ POPPLER_PUBLIC GType poppler_annot_markup_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC gchar *poppler_annot_markup_get_label(PopplerAnnotMarkup *poppler_annot); POPPLER_PUBLIC void poppler_annot_markup_set_label(PopplerAnnotMarkup *poppler_annot, const gchar *label); POPPLER_PUBLIC gboolean poppler_annot_markup_has_popup(PopplerAnnotMarkup *poppler_annot); POPPLER_PUBLIC void poppler_annot_markup_set_popup(PopplerAnnotMarkup *poppler_annot, PopplerRectangle *popup_rect); POPPLER_PUBLIC gboolean poppler_annot_markup_get_popup_is_open(PopplerAnnotMarkup *poppler_annot); POPPLER_PUBLIC void poppler_annot_markup_set_popup_is_open(PopplerAnnotMarkup *poppler_annot, gboolean is_open); POPPLER_PUBLIC gboolean poppler_annot_markup_get_popup_rectangle(PopplerAnnotMarkup *poppler_annot, PopplerRectangle *poppler_rect); POPPLER_PUBLIC void poppler_annot_markup_set_popup_rectangle(PopplerAnnotMarkup *poppler_annot, PopplerRectangle *poppler_rect); POPPLER_PUBLIC gdouble poppler_annot_markup_get_opacity(PopplerAnnotMarkup *poppler_annot); POPPLER_PUBLIC void poppler_annot_markup_set_opacity(PopplerAnnotMarkup *poppler_annot, gdouble opacity); POPPLER_PUBLIC GDate *poppler_annot_markup_get_date(PopplerAnnotMarkup *poppler_annot); POPPLER_PUBLIC gchar *poppler_annot_markup_get_subject(PopplerAnnotMarkup *poppler_annot); POPPLER_PUBLIC PopplerAnnotMarkupReplyType poppler_annot_markup_get_reply_to(PopplerAnnotMarkup *poppler_annot); POPPLER_PUBLIC PopplerAnnotExternalDataType poppler_annot_markup_get_external_data(PopplerAnnotMarkup *poppler_annot); /* PopplerAnnotText */ POPPLER_PUBLIC GType poppler_annot_text_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerAnnot *poppler_annot_text_new(PopplerDocument *doc, PopplerRectangle *rect); POPPLER_PUBLIC gboolean poppler_annot_text_get_is_open(PopplerAnnotText *poppler_annot); POPPLER_PUBLIC void poppler_annot_text_set_is_open(PopplerAnnotText *poppler_annot, gboolean is_open); POPPLER_PUBLIC gchar *poppler_annot_text_get_icon(PopplerAnnotText *poppler_annot); POPPLER_PUBLIC void poppler_annot_text_set_icon(PopplerAnnotText *poppler_annot, const gchar *icon); POPPLER_PUBLIC PopplerAnnotTextState poppler_annot_text_get_state(PopplerAnnotText *poppler_annot); /* PopplerAnnotTextMarkup */ POPPLER_PUBLIC GType poppler_annot_text_markup_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerAnnot *poppler_annot_text_markup_new_highlight(PopplerDocument *doc, PopplerRectangle *rect, GArray *quadrilaterals); POPPLER_PUBLIC PopplerAnnot *poppler_annot_text_markup_new_squiggly(PopplerDocument *doc, PopplerRectangle *rect, GArray *quadrilaterals); POPPLER_PUBLIC PopplerAnnot *poppler_annot_text_markup_new_strikeout(PopplerDocument *doc, PopplerRectangle *rect, GArray *quadrilaterals); POPPLER_PUBLIC PopplerAnnot *poppler_annot_text_markup_new_underline(PopplerDocument *doc, PopplerRectangle *rect, GArray *quadrilaterals); POPPLER_PUBLIC void poppler_annot_text_markup_set_quadrilaterals(PopplerAnnotTextMarkup *poppler_annot, GArray *quadrilaterals); POPPLER_PUBLIC GArray *poppler_annot_text_markup_get_quadrilaterals(PopplerAnnotTextMarkup *poppler_annot); /* PopplerAnnotFreeText */ POPPLER_PUBLIC GType poppler_annot_free_text_get_type(void) G_GNUC_CONST; 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); /* PopplerAnnotFileAttachment */ POPPLER_PUBLIC GType poppler_annot_file_attachment_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerAttachment *poppler_annot_file_attachment_get_attachment(PopplerAnnotFileAttachment *poppler_annot); POPPLER_PUBLIC gchar *poppler_annot_file_attachment_get_name(PopplerAnnotFileAttachment *poppler_annot); /* PopplerAnnotMovie */ POPPLER_PUBLIC GType poppler_annot_movie_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC gchar *poppler_annot_movie_get_title(PopplerAnnotMovie *poppler_annot); POPPLER_PUBLIC PopplerMovie *poppler_annot_movie_get_movie(PopplerAnnotMovie *poppler_annot); /* PopplerAnnotScreen */ POPPLER_PUBLIC GType poppler_annot_screen_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerAction *poppler_annot_screen_get_action(PopplerAnnotScreen *poppler_annot); /* PopplerAnnotLine */ POPPLER_PUBLIC GType poppler_annot_line_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerAnnot *poppler_annot_line_new(PopplerDocument *doc, PopplerRectangle *rect, PopplerPoint *start, PopplerPoint *end); POPPLER_PUBLIC void poppler_annot_line_set_vertices(PopplerAnnotLine *poppler_annot, PopplerPoint *start, PopplerPoint *end); /* PopplerAnnotCalloutLine */ POPPLER_PUBLIC GType poppler_annot_callout_line_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerAnnotCalloutLine *poppler_annot_callout_line_new(void); POPPLER_PUBLIC PopplerAnnotCalloutLine *poppler_annot_callout_line_copy(PopplerAnnotCalloutLine *callout); POPPLER_PUBLIC void poppler_annot_callout_line_free(PopplerAnnotCalloutLine *callout); /* PopplerAnnotCircle */ POPPLER_PUBLIC GType poppler_annot_circle_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerAnnot *poppler_annot_circle_new(PopplerDocument *doc, PopplerRectangle *rect); POPPLER_PUBLIC void poppler_annot_circle_set_interior_color(PopplerAnnotCircle *poppler_annot, PopplerColor *poppler_color); POPPLER_PUBLIC PopplerColor *poppler_annot_circle_get_interior_color(PopplerAnnotCircle *poppler_annot); /* PopplerAnnotGeometry */ POPPLER_PUBLIC GType poppler_annot_square_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerAnnot *poppler_annot_square_new(PopplerDocument *doc, PopplerRectangle *rect); POPPLER_PUBLIC void poppler_annot_square_set_interior_color(PopplerAnnotSquare *poppler_annot, PopplerColor *poppler_color); POPPLER_PUBLIC PopplerColor *poppler_annot_square_get_interior_color(PopplerAnnotSquare *poppler_annot); /* PopplerAnnotStamp */ POPPLER_PUBLIC GType poppler_annot_stamp_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerAnnot *poppler_annot_stamp_new(PopplerDocument *doc, PopplerRectangle *rect); POPPLER_PUBLIC PopplerAnnotStampIcon poppler_annot_stamp_get_icon(PopplerAnnotStamp *poppler_annot); POPPLER_PUBLIC void poppler_annot_stamp_set_icon(PopplerAnnotStamp *poppler_annot, PopplerAnnotStampIcon icon); POPPLER_PUBLIC gboolean poppler_annot_stamp_set_custom_image(PopplerAnnotStamp *poppler_annot, cairo_surface_t *image, GError **error); G_END_DECLS #endif /* __POPPLER_ANNOT_H__ */ poppler-24.02.0/glib/poppler-attachment.cc000066400000000000000000000261231455701731300204050ustar00rootroot00000000000000/* poppler-attachment.cc: glib wrapper for poppler * Copyright (C) 2006, Red Hat, Inc. * * 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 "config.h" #include #include #include "poppler.h" #include "poppler-private.h" #include /** * SECTION:poppler-attachment * @short_description: Attachments * @title: PopplerAttachment */ /* FIXME: We need to add gettext support sometime */ #define _(x) (x) struct PopplerAttachmentPrivate { Object obj_stream {}; GDateTime *mtime; GDateTime *ctime; }; static void poppler_attachment_finalize(GObject *obj); G_DEFINE_TYPE_WITH_PRIVATE(PopplerAttachment, poppler_attachment, G_TYPE_OBJECT) #define GET_PRIVATE(obj) ((PopplerAttachmentPrivate *)poppler_attachment_get_instance_private(obj)) static void poppler_attachment_init(PopplerAttachment *attachment) { void *place; place = GET_PRIVATE(attachment); new (place) PopplerAttachmentPrivate(); } static void poppler_attachment_class_init(PopplerAttachmentClass *klass) { G_OBJECT_CLASS(klass)->finalize = poppler_attachment_finalize; } static void poppler_attachment_finalize(GObject *obj) { PopplerAttachment *attachment; PopplerAttachmentPrivate *priv; attachment = (PopplerAttachment *)obj; priv = GET_PRIVATE(attachment); if (attachment->name) { g_free(attachment->name); } attachment->name = nullptr; if (attachment->description) { g_free(attachment->description); } attachment->description = nullptr; if (attachment->checksum) { g_string_free(attachment->checksum, TRUE); } attachment->checksum = nullptr; g_clear_pointer(&priv->mtime, g_date_time_unref); g_clear_pointer(&priv->ctime, g_date_time_unref); priv->~PopplerAttachmentPrivate(); G_OBJECT_CLASS(poppler_attachment_parent_class)->finalize(obj); } /* Public functions */ PopplerAttachment *_poppler_attachment_new(FileSpec *emb_file) { PopplerAttachment *attachment; PopplerAttachmentPrivate *priv; EmbFile *embFile; g_assert(emb_file != nullptr); attachment = (PopplerAttachment *)g_object_new(POPPLER_TYPE_ATTACHMENT, nullptr); priv = GET_PRIVATE(attachment); if (emb_file->getFileName()) { attachment->name = _poppler_goo_string_to_utf8(emb_file->getFileName()); } if (emb_file->getDescription()) { attachment->description = _poppler_goo_string_to_utf8(emb_file->getDescription()); } embFile = emb_file->getEmbeddedFile(); if (embFile != nullptr && embFile->streamObject()->isStream()) { attachment->size = embFile->size(); if (embFile->createDate()) { priv->ctime = _poppler_convert_pdf_date_to_date_time(embFile->createDate()); G_GNUC_BEGIN_IGNORE_DEPRECATIONS /* This will overflow on dates from after 2038. This field is * deprecated, only kept for backward compatibility. */ attachment->ctime = (GTime)g_date_time_to_unix(priv->ctime); G_GNUC_END_IGNORE_DEPRECATIONS } if (embFile->modDate()) { priv->mtime = _poppler_convert_pdf_date_to_date_time(embFile->modDate()); G_GNUC_BEGIN_IGNORE_DEPRECATIONS /* This will overflow on dates from after 2038. This field is * deprecated, only kept for backward compatibility. */ attachment->mtime = (GTime)g_date_time_to_unix(priv->mtime); G_GNUC_END_IGNORE_DEPRECATIONS } if (embFile->checksum() && embFile->checksum()->getLength() > 0) { attachment->checksum = g_string_new_len(embFile->checksum()->c_str(), embFile->checksum()->getLength()); } priv->obj_stream = embFile->streamObject()->copy(); } else { g_warning("Missing stream object for embedded file"); g_clear_object(&attachment); } return attachment; } /** * poppler_attachment_get_checksum: * @attachment: a #PopplerAttachment * * Returns: The attachment's checksum. * * Since: 20.09.0 */ const GString *poppler_attachment_get_checksum(PopplerAttachment *attachment) { return attachment->checksum; } /** * poppler_attachment_get_ctime: * @attachment: a #PopplerAttachment * * Returns: (transfer none) (nullable): The attachment's creation date and time * as a #GDateTime, or %NULL if the creation date and time is not available. * * Since: 20.09.0 */ GDateTime *poppler_attachment_get_ctime(PopplerAttachment *attachment) { return GET_PRIVATE(attachment)->ctime; } /** * poppler_attachment_get_description: * @attachment: a #PopplerAttachment * * Returns: The attachment's descriptive text. * * Since: 20.09.0 */ const gchar *poppler_attachment_get_description(PopplerAttachment *attachment) { return attachment->description; } /** * poppler_attachment_get_mtime: * @attachment: a #PopplerAttachment * * Returns: (transfer none) (nullable): The attachment's modification date and * time as a #GDateTime, or %NULL if the modification date and time is not * available. * * Since: 20.09.0 */ GDateTime *poppler_attachment_get_mtime(PopplerAttachment *attachment) { return GET_PRIVATE(attachment)->mtime; } /** * poppler_attachment_get_name: * @attachment: a #PopplerAttachment * * Returns: The attachment's name. * * Since: 20.09.0 */ const gchar *poppler_attachment_get_name(PopplerAttachment *attachment) { return attachment->name; } /** * poppler_attachment_get_size: * @attachment: a #PopplerAttachment * * Returns: The attachment's size. * * Since: 20.09.0 */ gsize poppler_attachment_get_size(PopplerAttachment *attachment) { return attachment->size; } static gboolean save_helper(const gchar *buf, gsize count, gpointer data, GError **error) { FILE *f = (FILE *)data; gsize n; n = fwrite(buf, 1, count, f); if (n != count) { int errsv = errno; g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errsv), _("Error writing to image file: %s"), g_strerror(errsv)); return FALSE; } return TRUE; } /** * poppler_attachment_save: * @attachment: A #PopplerAttachment. * @filename: name of file to save * @error: (allow-none): return location for error, or %NULL. * * 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 * and whatever the save function generates. * * Return value: %TRUE, if the file successfully saved **/ gboolean poppler_attachment_save(PopplerAttachment *attachment, const char *filename, GError **error) { gboolean result; FILE *f; g_return_val_if_fail(POPPLER_IS_ATTACHMENT(attachment), FALSE); f = openFile(filename, "wb"); if (f == nullptr) { gchar *display_name = g_filename_display_name(filename); g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errno), _("Failed to open '%s' for writing: %s"), display_name, g_strerror(errno)); g_free(display_name); return FALSE; } result = poppler_attachment_save_to_callback(attachment, save_helper, f, error); if (fclose(f) < 0) { gchar *display_name = g_filename_display_name(filename); g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errno), _("Failed to close '%s', all data may not have been saved: %s"), display_name, g_strerror(errno)); g_free(display_name); return FALSE; } return result; } #ifndef G_OS_WIN32 /** * poppler_attachment_save_to_fd: * @attachment: A #PopplerAttachment. * @fd: a valid file descriptor open for writing * @error: (allow-none): return location for error, or %NULL. * * 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 * and whatever the save function generates. * Note that this function takes ownership of @fd; you must not operate on it * again, nor close it. * * Return value: %TRUE, if the file successfully saved * * Since: 21.12.0 **/ gboolean poppler_attachment_save_to_fd(PopplerAttachment *attachment, int fd, GError **error) { gboolean result; FILE *f; g_return_val_if_fail(POPPLER_IS_ATTACHMENT(attachment), FALSE); g_return_val_if_fail(fd != -1, FALSE); g_return_val_if_fail(error == nullptr || *error == nullptr, FALSE); f = fdopen(fd, "wb"); if (f == nullptr) { int errsv = errno; g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errsv), _("Failed to open FD %d for writing: %s"), fd, g_strerror(errsv)); close(fd); return FALSE; } result = poppler_attachment_save_to_callback(attachment, save_helper, f, error); if (fclose(f) < 0) { int errsv = errno; g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errsv), _("Failed to close FD %d, all data may not have been saved: %s"), fd, g_strerror(errsv)); return FALSE; } return result; } #endif /* !G_OS_WIN32 */ #define BUF_SIZE 1024 /** * poppler_attachment_save_to_callback: * @attachment: A #PopplerAttachment. * @save_func: (scope call): a function that is called to save each block of data that the save routine generates. * @user_data: user data to pass to the save function. * @error: (allow-none): return location for error, or %NULL. * * 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 * whatever the save function generates. * * Return value: %TRUE, if the save successfully completed **/ gboolean poppler_attachment_save_to_callback(PopplerAttachment *attachment, PopplerAttachmentSaveFunc save_func, gpointer user_data, GError **error) { PopplerAttachmentPrivate *priv; Stream *stream; gchar buf[BUF_SIZE]; int i; gboolean eof_reached = FALSE; g_return_val_if_fail(POPPLER_IS_ATTACHMENT(attachment), FALSE); priv = GET_PRIVATE(attachment); stream = priv->obj_stream.getStream(); stream->reset(); do { int data; for (i = 0; i < BUF_SIZE; i++) { data = stream->getChar(); if (data == EOF) { eof_reached = TRUE; break; } buf[i] = data; } if (i > 0) { if (!(save_func)(buf, i, user_data, error)) { return FALSE; } } } while (!eof_reached); return TRUE; } poppler-24.02.0/glib/poppler-attachment.h000066400000000000000000000107601455701731300202470ustar00rootroot00000000000000/* poppler-attachment.h: glib interface to poppler * Copyright (C) 2004, Red Hat, Inc. * * 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_ATTACHMENT_H__ #define __POPPLER_ATTACHMENT_H__ #include #include #include "poppler.h" G_BEGIN_DECLS #define POPPLER_TYPE_ATTACHMENT (poppler_attachment_get_type()) #define POPPLER_ATTACHMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), POPPLER_TYPE_ATTACHMENT, PopplerAttachment)) #define POPPLER_IS_ATTACHMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), POPPLER_TYPE_ATTACHMENT)) /** * PopplerAttachmentSaveFunc: * @buf: (array length=count) (element-type guint8): buffer containing * bytes to be written. * @count: number of bytes in @buf. * @data: (closure): user data passed to poppler_attachment_save_to_callback() * @error: GError to set on error, or %NULL * * 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 * @error and return %FALSE, in which case poppler_attachment_save_to_callback() * will fail with the same error. * * Returns: %TRUE if successful, %FALSE (with @error set) if failed. */ typedef gboolean (*PopplerAttachmentSaveFunc)(const gchar *buf, gsize count, gpointer data, GError **error); /** * PopplerAttachment: * @name: The filename. Deprecated in poppler 20.09.0. Use * poppler_attachment_get_name() instead. * @description: Descriptive text. Deprecated in poppler 20.09.0. Use * poppler_attachment_get_description() instead. * @size: The size of the file. Deprecated in poppler 20.09.0. Use * poppler_attachment_get_size() instead. * @mtime: The date and time when the file was last modified. Deprecated in * poppler 20.09.0. Use poppler_attachment_get_mtime() instead. * @ctime: The date and time when the file was created. Deprecated in poppler * 20.09.0. Use poppler_attachment_get_ctime() instead. * @checksum: A 16-byte checksum of the file. Deprecated in poppler 20.09.0. Use * poppler_attachment_get_checksum() instead. */ struct _PopplerAttachment { GObject parent; /*< public >*/ gchar *name; gchar *description; gsize size; /* GTime is deprecated, but is part of our ABI here (see #715, #765). */ G_GNUC_BEGIN_IGNORE_DEPRECATIONS GTime mtime; GTime ctime; G_GNUC_END_IGNORE_DEPRECATIONS GString *checksum; }; /* This struct was not intended to be public, but can't be moved to * poppler-attachment.cc without breaking the API stability. */ /** * PopplerAttachmentClass: * * The GObject class structure of #PopplerAttachment. */ typedef struct _PopplerAttachmentClass { GObjectClass parent_class; } PopplerAttachmentClass; POPPLER_PUBLIC GType poppler_attachment_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC const GString *poppler_attachment_get_checksum(PopplerAttachment *attachment); POPPLER_PUBLIC GDateTime *poppler_attachment_get_ctime(PopplerAttachment *attachment); POPPLER_PUBLIC const gchar *poppler_attachment_get_description(PopplerAttachment *attachment); POPPLER_PUBLIC GDateTime *poppler_attachment_get_mtime(PopplerAttachment *attachment); POPPLER_PUBLIC const gchar *poppler_attachment_get_name(PopplerAttachment *attachment); POPPLER_PUBLIC gsize poppler_attachment_get_size(PopplerAttachment *attachment); POPPLER_PUBLIC gboolean poppler_attachment_save(PopplerAttachment *attachment, const char *filename, GError **error); #ifndef G_OS_WIN32 POPPLER_PUBLIC gboolean poppler_attachment_save_to_fd(PopplerAttachment *attachment, int fd, GError **error); #endif POPPLER_PUBLIC gboolean poppler_attachment_save_to_callback(PopplerAttachment *attachment, PopplerAttachmentSaveFunc save_func, gpointer user_data, GError **error); G_END_DECLS #endif /* __POPPLER_ATTACHMENT_H__ */ poppler-24.02.0/glib/poppler-cached-file-loader.cc000066400000000000000000000065571455701731300216560ustar00rootroot00000000000000/* poppler-cached-file-loader.h: glib interface to poppler * * Copyright (C) 2012 Carlos Garcia Campos * Copyright (C) 2022 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 * 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 "config.h" #include "poppler-cached-file-loader.h" PopplerCachedFileLoader::PopplerCachedFileLoader(GInputStream *inputStreamA, GCancellable *cancellableA, goffset lengthA) { inputStream = (GInputStream *)g_object_ref(inputStreamA); cancellable = cancellableA ? (GCancellable *)g_object_ref(cancellableA) : nullptr; length = lengthA; cachedFile = nullptr; } PopplerCachedFileLoader::~PopplerCachedFileLoader() { g_object_unref(inputStream); if (cancellable) { g_object_unref(cancellable); } } size_t PopplerCachedFileLoader::init(CachedFile *cachedFileA) { size_t size; gssize bytesRead; char buf[CachedFileChunkSize]; cachedFile = cachedFileA; if (length != (goffset)-1) { return length; } if (G_IS_FILE_INPUT_STREAM(inputStream)) { GFileInfo *info; info = g_file_input_stream_query_info(G_FILE_INPUT_STREAM(inputStream), G_FILE_ATTRIBUTE_STANDARD_SIZE, cancellable, nullptr); if (!info) { error(errInternal, -1, "Failed to get size."); return (size_t)-1; } length = g_file_info_get_size(info); g_object_unref(info); return length; } // Unknown stream length, read the whole stream and return the size. CachedFileWriter writer = CachedFileWriter(cachedFile, nullptr); size = 0; do { bytesRead = g_input_stream_read(inputStream, buf, CachedFileChunkSize, cancellable, nullptr); if (bytesRead == -1) { break; } writer.write(buf, bytesRead); size += bytesRead; } while (bytesRead > 0); return size; } int PopplerCachedFileLoader::load(const std::vector &ranges, CachedFileWriter *writer) { char buf[CachedFileChunkSize]; gssize bytesRead; size_t rangeBytesRead, bytesToRead; if (length == (goffset)-1) { return 0; } for (const ByteRange &range : ranges) { bytesToRead = MIN(CachedFileChunkSize, range.length); rangeBytesRead = 0; g_seekable_seek(G_SEEKABLE(inputStream), range.offset, G_SEEK_SET, cancellable, nullptr); do { bytesRead = g_input_stream_read(inputStream, buf, bytesToRead, cancellable, nullptr); if (bytesRead == -1) { return -1; } writer->write(buf, bytesRead); rangeBytesRead += bytesRead; bytesToRead = range.length - rangeBytesRead; } while (bytesRead > 0 && bytesToRead > 0); } return 0; } poppler-24.02.0/glib/poppler-cached-file-loader.h000066400000000000000000000031001455701731300214750ustar00rootroot00000000000000/* poppler-cached-file-loader.h: glib interface to poppler * * Copyright (C) 2012 Carlos Garcia Campos * Copyright (C) 2022 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 * 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_CACHED_FILE_LOADER_H__ #define __POPPLER_CACHED_FILE_LOADER_H__ #include #ifndef __GI_SCANNER__ # include class PopplerCachedFileLoader : public CachedFileLoader { public: PopplerCachedFileLoader(GInputStream *inputStreamA, GCancellable *cancellableA, goffset lengthA = -1); ~PopplerCachedFileLoader() override; size_t init(CachedFile *cachedFile) override; int load(const std::vector &ranges, CachedFileWriter *writer) override; private: GInputStream *inputStream; GCancellable *cancellable; goffset length; CachedFile *cachedFile; }; #endif /* __GI_SCANNER__ */ #endif /* __POPPLER_CACHED_FILE_LOADER_H__ */ poppler-24.02.0/glib/poppler-date.cc000066400000000000000000000026641455701731300171760ustar00rootroot00000000000000/* poppler-date.cc: glib interface to poppler * * Copyright (C) 2009 Carlos Garcia Campos * * 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 "poppler-date.h" /** * poppler_date_parse: * @date: string to parse * @timet: an uninitialized #time_t * * 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 * * Return value: #TRUE, if @timet was set * * Since: 0.12 **/ gboolean poppler_date_parse(const gchar *date, time_t *timet) { time_t t; GooString dateString(date); t = dateStringToTime(&dateString); if (t == (time_t)-1) { return FALSE; } *timet = t; return TRUE; } poppler-24.02.0/glib/poppler-date.h000066400000000000000000000020201455701731300170220ustar00rootroot00000000000000/* poppler-date.h: glib interface to poppler * * Copyright (C) 2009 Carlos Garcia Campos * * 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_DATE_H__ #define __POPPLER_DATE_H__ #include "poppler.h" G_BEGIN_DECLS POPPLER_PUBLIC gboolean poppler_date_parse(const gchar *date, time_t *timet); G_END_DECLS #endif /* __POPPLER_DATE_H__ */ poppler-24.02.0/glib/poppler-document.cc000066400000000000000000003455601455701731300201040ustar00rootroot00000000000000/* poppler-document.cc: glib wrapper for poppler * Copyright (C) 2005, Red Hat, Inc. * * 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) 2020, 2022 Albert Astals Cid * Copyright (C) 2021 André Guerreiro * 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 * 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 "config.h" #include #include #ifndef G_OS_WIN32 # include # include # include # include #endif #ifndef __GI_SCANNER__ # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include "UTF.h" #endif #include "poppler.h" #include "poppler-private.h" #include "poppler-enums.h" #include "poppler-input-stream.h" #include "poppler-cached-file-loader.h" #ifdef G_OS_WIN32 # include #endif /** * SECTION:poppler-document * @short_description: Information about a document * @title: PopplerDocument * * The #PopplerDocument is an object used to refer to a main document. */ enum { PROP_0, PROP_TITLE, PROP_FORMAT, PROP_FORMAT_MAJOR, PROP_FORMAT_MINOR, PROP_SUBTYPE, PROP_SUBTYPE_STRING, PROP_SUBTYPE_PART, PROP_SUBTYPE_CONF, PROP_AUTHOR, PROP_SUBJECT, PROP_KEYWORDS, PROP_CREATOR, PROP_PRODUCER, PROP_CREATION_DATE, PROP_MOD_DATE, PROP_LINEARIZED, PROP_PAGE_LAYOUT, PROP_PAGE_MODE, PROP_VIEWER_PREFERENCES, PROP_PERMISSIONS, PROP_METADATA, PROP_PRINT_SCALING, PROP_PRINT_DUPLEX, PROP_PRINT_N_COPIES, PROP_CREATION_DATETIME, PROP_MOD_DATETIME }; static void poppler_document_layers_free(PopplerDocument *document); typedef struct _PopplerDocumentClass PopplerDocumentClass; struct _PopplerDocumentClass { GObjectClass parent_class; }; G_DEFINE_TYPE(PopplerDocument, poppler_document, G_TYPE_OBJECT) static PopplerDocument *_poppler_document_new_from_pdfdoc(std::unique_ptr &&initer, PDFDoc *newDoc, GError **error) { PopplerDocument *document; if (!newDoc->isOk()) { int fopen_errno; switch (newDoc->getErrorCode()) { case errOpenFile: // If there was an error opening the file, count it as a G_FILE_ERROR // and set the GError parameters accordingly. (this assumes that the // only way to get an errOpenFile error is if newDoc was created using // a filename and thus fopen was called, which right now is true. fopen_errno = newDoc->getFopenErrno(); g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(fopen_errno), "%s", g_strerror(fopen_errno)); break; case errBadCatalog: g_set_error(error, POPPLER_ERROR, POPPLER_ERROR_BAD_CATALOG, "Failed to read the document catalog"); break; case errDamaged: g_set_error(error, POPPLER_ERROR, POPPLER_ERROR_DAMAGED, "PDF document is damaged"); break; case errEncrypted: g_set_error(error, POPPLER_ERROR, POPPLER_ERROR_ENCRYPTED, "Document is encrypted"); break; default: g_set_error(error, POPPLER_ERROR, POPPLER_ERROR_INVALID, "Failed to load document"); } delete newDoc; return nullptr; } document = (PopplerDocument *)g_object_new(POPPLER_TYPE_DOCUMENT, nullptr); document->initer = std::move(initer); document->doc = newDoc; document->output_dev = new CairoOutputDev(); document->output_dev->startDoc(document->doc); return document; } static std::optional poppler_password_to_latin1(const gchar *password) { gchar *password_latin; if (!password) { return {}; } password_latin = g_convert(password, -1, "ISO-8859-1", "UTF-8", nullptr, nullptr, nullptr); std::optional password_g = GooString(password_latin); g_free(password_latin); return password_g; } /** * poppler_document_new_from_file: * @uri: uri of the file to load * @password: (allow-none): password to unlock the file with, or %NULL * @error: (allow-none): Return location for an error, or %NULL * * 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 * domains. * * Return value: A newly created #PopplerDocument, or %NULL **/ PopplerDocument *poppler_document_new_from_file(const char *uri, const char *password, GError **error) { PDFDoc *newDoc; char *filename; auto initer = std::make_unique(_poppler_error_cb); filename = g_filename_from_uri(uri, nullptr, error); if (!filename) { return nullptr; } const std::optional password_g = poppler_password_to_latin1(password); #ifdef G_OS_WIN32 wchar_t *filenameW; int length; length = MultiByteToWideChar(CP_UTF8, 0, filename, -1, nullptr, 0); filenameW = new WCHAR[length]; if (!filenameW) return nullptr; length = MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, length); newDoc = new PDFDoc(filenameW, length, password_g, password_g); if (!newDoc->isOk() && newDoc->getErrorCode() == errEncrypted && password) { /* Try again with original password (which comes from GTK in UTF8) Issue #824 */ delete newDoc; newDoc = new PDFDoc(filenameW, length, GooString(password), GooString(password)); } delete[] filenameW; #else newDoc = new PDFDoc(std::make_unique(filename), password_g, password_g); if (!newDoc->isOk() && newDoc->getErrorCode() == errEncrypted && password) { /* Try again with original password (which comes from GTK in UTF8) Issue #824 */ delete newDoc; newDoc = new PDFDoc(std::make_unique(filename), GooString(password), GooString(password)); } #endif g_free(filename); return _poppler_document_new_from_pdfdoc(std::move(initer), newDoc, error); } /** * poppler_document_new_from_data: * @data: (array length=length) (element-type guint8): the pdf data * @length: the length of #data * @password: (nullable): password to unlock the file with, or %NULL * @error: (nullable): Return location for an error, or %NULL * * 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 * domains. * * Note that @data is not copied nor is a new reference to it created. * It must remain valid and cannot be destroyed as long as the returned * document exists. * * Return value: A newly created #PopplerDocument, or %NULL * * Deprecated: 0.82: This requires directly managing @length and @data. * Use poppler_document_new_from_bytes() instead. **/ PopplerDocument *poppler_document_new_from_data(char *data, int length, const char *password, GError **error) { PDFDoc *newDoc; MemStream *str; auto initer = std::make_unique(_poppler_error_cb); // create stream str = new MemStream(data, 0, length, Object(objNull)); const std::optional password_g = poppler_password_to_latin1(password); newDoc = new PDFDoc(str, password_g, password_g); if (!newDoc->isOk() && newDoc->getErrorCode() == errEncrypted && password) { /* Try again with original password (which comes from GTK in UTF8) Issue #824 */ str = dynamic_cast(str->copy()); delete newDoc; newDoc = new PDFDoc(str, GooString(password), GooString(password)); } return _poppler_document_new_from_pdfdoc(std::move(initer), newDoc, error); } class BytesStream : public MemStream { std::unique_ptr m_bytes; public: BytesStream(GBytes *bytes, Object &&dictA) : MemStream(static_cast(g_bytes_get_data(bytes, nullptr)), 0, g_bytes_get_size(bytes), std::move(dictA)), m_bytes { g_bytes_ref(bytes), &g_bytes_unref } { } ~BytesStream() override; }; BytesStream::~BytesStream() = default; class OwningFileStream final : public FileStream { public: OwningFileStream(std::unique_ptr fileA, Object &&dictA) : FileStream(fileA.get(), 0, false, fileA->size(), std::move(dictA)), file(std::move(fileA)) { } ~OwningFileStream() override; private: std::unique_ptr file; }; OwningFileStream::~OwningFileStream() = default; /** * poppler_document_new_from_bytes: * @bytes: a #GBytes * @password: (allow-none): password to unlock the file with, or %NULL * @error: (allow-none): Return location for an error, or %NULL * * Creates a new #PopplerDocument from @bytes. The returned document * will hold a reference to @bytes. * * On error, %NULL is returned, with @error set. Possible errors include * those in the #POPPLER_ERROR and #G_FILE_ERROR domains. * * Return value: (transfer full): a newly created #PopplerDocument, or %NULL * * Since: 0.82 **/ PopplerDocument *poppler_document_new_from_bytes(GBytes *bytes, const char *password, GError **error) { PDFDoc *newDoc; BytesStream *str; g_return_val_if_fail(bytes != nullptr, nullptr); g_return_val_if_fail(error == nullptr || *error == nullptr, nullptr); auto initer = std::make_unique(_poppler_error_cb); // create stream str = new BytesStream(bytes, Object(objNull)); const std::optional password_g = poppler_password_to_latin1(password); newDoc = new PDFDoc(str, password_g, password_g); if (!newDoc->isOk() && newDoc->getErrorCode() == errEncrypted && password) { /* Try again with original password (which comes from GTK in UTF8) Issue #824 */ str = dynamic_cast(str->copy()); delete newDoc; newDoc = new PDFDoc(str, GooString(password), GooString(password)); } return _poppler_document_new_from_pdfdoc(std::move(initer), newDoc, error); } static inline gboolean stream_is_memory_buffer_or_local_file(GInputStream *stream) { return G_IS_MEMORY_INPUT_STREAM(stream) || (G_IS_FILE_INPUT_STREAM(stream) && strcmp(g_type_name_from_instance((GTypeInstance *)stream), "GLocalFileInputStream") == 0); } /** * poppler_document_new_from_stream: * @stream: a #GInputStream to read from * @length: the stream length, or -1 if not known * @password: (allow-none): password to unlock the file with, or %NULL * @cancellable: (allow-none): a #GCancellable, or %NULL * @error: (allow-none): Return location for an error, or %NULL * * Creates a new #PopplerDocument reading the PDF contents from @stream. * 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. * * Returns: (transfer full): a new #PopplerDocument, or %NULL * * Since: 0.22 */ PopplerDocument *poppler_document_new_from_stream(GInputStream *stream, goffset length, const char *password, GCancellable *cancellable, GError **error) { PDFDoc *newDoc; BaseStream *str; g_return_val_if_fail(G_IS_INPUT_STREAM(stream), NULL); g_return_val_if_fail(length == (goffset)-1 || length > 0, NULL); auto initer = std::make_unique(_poppler_error_cb); if (!G_IS_SEEKABLE(stream) || !g_seekable_can_seek(G_SEEKABLE(stream))) { g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Stream is not seekable"); return nullptr; } if (stream_is_memory_buffer_or_local_file(stream)) { if (length == (goffset)-1) { if (!g_seekable_seek(G_SEEKABLE(stream), 0, G_SEEK_END, cancellable, error)) { g_prefix_error(error, "Unable to determine length of stream: "); return nullptr; } length = g_seekable_tell(G_SEEKABLE(stream)); } str = new PopplerInputStream(stream, cancellable, 0, false, length, Object(objNull)); } else { CachedFile *cachedFile = new CachedFile(new PopplerCachedFileLoader(stream, cancellable, length)); str = new CachedFileStream(cachedFile, 0, false, cachedFile->getLength(), Object(objNull)); } const std::optional password_g = poppler_password_to_latin1(password); newDoc = new PDFDoc(str, password_g, password_g); if (!newDoc->isOk() && newDoc->getErrorCode() == errEncrypted && password) { /* Try again with original password (which comes from GTK in UTF8) Issue #824 */ str = str->copy(); delete newDoc; newDoc = new PDFDoc(str, GooString(password), GooString(password)); } return _poppler_document_new_from_pdfdoc(std::move(initer), newDoc, error); } /** * poppler_document_new_from_gfile: * @file: a #GFile to load * @password: (allow-none): password to unlock the file with, or %NULL * @cancellable: (allow-none): a #GCancellable, or %NULL * @error: (allow-none): Return location for an error, or %NULL * * Creates a new #PopplerDocument reading the PDF contents from @file. * Possible errors include those in the #POPPLER_ERROR and #G_FILE_ERROR * domains. * * Returns: (transfer full): a new #PopplerDocument, or %NULL * * Since: 0.22 */ PopplerDocument *poppler_document_new_from_gfile(GFile *file, const char *password, GCancellable *cancellable, GError **error) { PopplerDocument *document; GFileInputStream *stream; g_return_val_if_fail(G_IS_FILE(file), NULL); if (g_file_is_native(file)) { gchar *uri; uri = g_file_get_uri(file); document = poppler_document_new_from_file(uri, password, error); g_free(uri); return document; } stream = g_file_read(file, cancellable, error); if (!stream) { return nullptr; } document = poppler_document_new_from_stream(G_INPUT_STREAM(stream), -1, password, cancellable, error); g_object_unref(stream); return document; } #ifndef G_OS_WIN32 /** * poppler_document_new_from_fd: * @fd: a valid file descriptor * @password: (allow-none): password to unlock the file with, or %NULL * @error: (allow-none): Return location for an error, or %NULL * * 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 * domains. * Note that this function takes ownership of @fd; you must not operate on it * again, nor close it. * * Returns: (transfer full): a new #PopplerDocument, or %NULL * * Since: 21.12.0 */ PopplerDocument *poppler_document_new_from_fd(int fd, const char *password, GError **error) { struct stat statbuf; int flags; BaseStream *stream; PDFDoc *newDoc; g_return_val_if_fail(fd != -1, nullptr); auto initer = std::make_unique(_poppler_error_cb); if (fstat(fd, &statbuf) == -1 || (flags = fcntl(fd, F_GETFL, &flags)) == -1) { int errsv = errno; g_set_error_literal(error, G_FILE_ERROR, g_file_error_from_errno(errsv), g_strerror(errsv)); close(fd); return nullptr; } switch (flags & O_ACCMODE) { case O_RDONLY: case O_RDWR: break; case O_WRONLY: default: g_set_error(error, G_FILE_ERROR, G_FILE_ERROR_BADF, "File descriptor %d is not readable", fd); close(fd); return nullptr; } if (fd == fileno(stdin) || !S_ISREG(statbuf.st_mode)) { FILE *file; if (fd == fileno(stdin)) { file = stdin; } else { file = fdopen(fd, "rb"); if (!file) { int errsv = errno; g_set_error_literal(error, G_FILE_ERROR, g_file_error_from_errno(errsv), g_strerror(errsv)); close(fd); return nullptr; } } CachedFile *cachedFile = new CachedFile(new FILECacheLoader(file)); stream = new CachedFileStream(cachedFile, 0, false, cachedFile->getLength(), Object(objNull)); } else { stream = new OwningFileStream(GooFile::open(fd), Object(objNull)); } const std::optional password_g = poppler_password_to_latin1(password); newDoc = new PDFDoc(stream, password_g, password_g); if (!newDoc->isOk() && newDoc->getErrorCode() == errEncrypted && password) { /* Try again with original password (which comes from GTK in UTF8) Issue #824 */ stream = stream->copy(); delete newDoc; newDoc = new PDFDoc(stream, GooString(password), GooString(password)); } return _poppler_document_new_from_pdfdoc(std::move(initer), newDoc, error); } #endif /* !G_OS_WIN32 */ static gboolean handle_save_error(int err_code, GError **error) { switch (err_code) { case errNone: break; case errOpenFile: g_set_error(error, POPPLER_ERROR, POPPLER_ERROR_OPEN_FILE, "Failed to open file for writing"); break; case errEncrypted: g_set_error(error, POPPLER_ERROR, POPPLER_ERROR_ENCRYPTED, "Document is encrypted"); break; default: g_set_error(error, POPPLER_ERROR, POPPLER_ERROR_INVALID, "Failed to save document"); } return err_code == errNone; } /** * poppler_document_save: * @document: a #PopplerDocument * @uri: uri of file to save * @error: (allow-none): return location for an error, or %NULL * * 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. * * Return value: %TRUE, if the document was successfully saved **/ gboolean poppler_document_save(PopplerDocument *document, const char *uri, GError **error) { char *filename; gboolean retval = FALSE; g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), FALSE); filename = g_filename_from_uri(uri, nullptr, error); if (filename != nullptr) { GooString fname(filename); int err_code; g_free(filename); err_code = document->doc->saveAs(fname); retval = handle_save_error(err_code, error); } return retval; } /** * poppler_document_save_a_copy: * @document: a #PopplerDocument * @uri: uri of file to save * @error: (allow-none): return location for an error, or %NULL * * 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. * * Return value: %TRUE, if the document was successfully saved **/ gboolean poppler_document_save_a_copy(PopplerDocument *document, const char *uri, GError **error) { char *filename; gboolean retval = FALSE; g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), FALSE); filename = g_filename_from_uri(uri, nullptr, error); if (filename != nullptr) { GooString fname(filename); int err_code; g_free(filename); err_code = document->doc->saveWithoutChangesAs(fname); retval = handle_save_error(err_code, error); } return retval; } #ifndef G_OS_WIN32 /** * poppler_document_save_to_fd: * @document: a #PopplerDocument * @fd: a valid file descriptor open for writing * @include_changes: whether to include user changes (e.g. form fills) * @error: (allow-none): return location for an error, or %NULL * * 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 * @include_changes 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. * * Return value: %TRUE, if the document was successfully saved * * Since: 21.12.0 **/ gboolean poppler_document_save_to_fd(PopplerDocument *document, int fd, gboolean include_changes, GError **error) { FILE *file; OutStream *stream; int rv; g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), FALSE); g_return_val_if_fail(fd != -1, FALSE); file = fdopen(fd, "wb"); if (file == nullptr) { int errsv = errno; g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errsv), "Failed to open FD %d for writing: %s", fd, g_strerror(errsv)); return FALSE; } stream = new FileOutStream(file, 0); if (include_changes) { rv = document->doc->saveAs(stream); } else { rv = document->doc->saveWithoutChangesAs(stream); } delete stream; return handle_save_error(rv, error); } #endif /* !G_OS_WIN32 */ static void poppler_document_finalize(GObject *object) { PopplerDocument *document = POPPLER_DOCUMENT(object); poppler_document_layers_free(document); delete document->output_dev; delete document->doc; delete document->initer.release(); G_OBJECT_CLASS(poppler_document_parent_class)->finalize(object); } /** * poppler_document_get_id: * @document: A #PopplerDocument * @permanent_id: (out) (allow-none): location to store an allocated string, use g_free() to free the returned string * @update_id: (out) (allow-none): location to store an allocated string, use g_free() to free the returned string * * 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 * contents at the time it was originally created, so that this identifer * never changes. @update_id is the update identifier that is built based on * the file contents at the time it was last updated. * * Note that returned strings are not null-terminated, they have a fixed * size of 32 bytes. * * Returns: %TRUE if the @document contains an id, %FALSE otherwise * * Since: 0.16 */ gboolean poppler_document_get_id(PopplerDocument *document, gchar **permanent_id, gchar **update_id) { GooString permanent; GooString update; gboolean retval = FALSE; g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), FALSE); if (permanent_id) { *permanent_id = nullptr; } if (update_id) { *update_id = nullptr; } if (document->doc->getID(permanent_id ? &permanent : nullptr, update_id ? &update : nullptr)) { if (permanent_id) { *permanent_id = g_new(char, 32); memcpy(*permanent_id, permanent.c_str(), 32); } if (update_id) { *update_id = g_new(char, 32); memcpy(*update_id, update.c_str(), 32); } retval = TRUE; } return retval; } /** * poppler_document_get_n_pages: * @document: A #PopplerDocument * * Returns the number of pages in a loaded document. * * Return value: Number of pages **/ int poppler_document_get_n_pages(PopplerDocument *document) { g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), 0); return document->doc->getNumPages(); } /** * poppler_document_get_page: * @document: A #PopplerDocument * @index: a page index * * Returns the #PopplerPage indexed at @index. This object is owned by the * caller. * * Return value: (transfer full) : The #PopplerPage at @index **/ PopplerPage *poppler_document_get_page(PopplerDocument *document, int index) { Page *page; g_return_val_if_fail(0 <= index && index < poppler_document_get_n_pages(document), NULL); page = document->doc->getPage(index + 1); if (!page) { return nullptr; } return _poppler_page_new(document, page, index); } /** * poppler_document_get_page_by_label: * @document: A #PopplerDocument * @label: a page label * * Returns the #PopplerPage reference by @label. This object is owned by the * caller. @label is a human-readable string representation of the page number, * and can be document specific. Typically, it is a value such as "iii" or "3". * * By default, "1" refers to the first page. * * Return value: (transfer full) :The #PopplerPage referenced by @label **/ PopplerPage *poppler_document_get_page_by_label(PopplerDocument *document, const char *label) { Catalog *catalog; GooString label_g(label); int index; catalog = document->doc->getCatalog(); if (!catalog->labelToIndex(&label_g, &index)) { return nullptr; } return poppler_document_get_page(document, index); } /** * poppler_document_get_n_attachments: * @document: A #PopplerDocument * * Returns the number of attachments in a loaded document. * See also poppler_document_get_attachments() * * Return value: Number of attachments * * Since: 0.18 */ guint poppler_document_get_n_attachments(PopplerDocument *document) { Catalog *catalog; g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), 0); catalog = document->doc->getCatalog(); return catalog && catalog->isOk() ? catalog->numEmbeddedFiles() : 0; } /** * poppler_document_has_attachments: * @document: A #PopplerDocument * * Returns %TRUE of @document has any attachments. * * Return value: %TRUE, if @document has attachments. **/ gboolean poppler_document_has_attachments(PopplerDocument *document) { g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), FALSE); return (poppler_document_get_n_attachments(document) != 0); } /** * poppler_document_get_attachments: * @document: A #PopplerDocument * * Returns a #GList containing #PopplerAttachments. These attachments * are unowned, and must be unreffed, and the list must be freed with * g_list_free(). * * Return value: (element-type PopplerAttachment) (transfer full): a list of available attachments. **/ GList *poppler_document_get_attachments(PopplerDocument *document) { Catalog *catalog; int n_files, i; GList *retval = nullptr; g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL); catalog = document->doc->getCatalog(); if (catalog == nullptr || !catalog->isOk()) { return nullptr; } n_files = catalog->numEmbeddedFiles(); for (i = 0; i < n_files; i++) { PopplerAttachment *attachment; const std::unique_ptr emb_file = catalog->embeddedFile(i); if (!emb_file->isOk() || !emb_file->getEmbeddedFile()->isOk()) { continue; } attachment = _poppler_attachment_new(emb_file.get()); if (attachment != nullptr) { retval = g_list_prepend(retval, attachment); } } return g_list_reverse(retval); } /** * poppler_named_dest_from_bytestring: * @data: (array length=length): the bytestring data * @length: the bytestring 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(). * * Returns: (transfer full): the named dest * * Since: 0.73 */ char *poppler_named_dest_from_bytestring(const guint8 *data, gsize length) { const guint8 *p, *pend; char *dest, *q; g_return_val_if_fail(length != 0 || data != nullptr, nullptr); /* Each source byte needs maximally 2 destination chars (\\ or \0) */ q = dest = (gchar *)g_malloc(length * 2 + 1); pend = data + length; for (p = data; p < pend; ++p) { switch (*p) { case '\0': *q++ = '\\'; *q++ = '0'; break; case '\\': *q++ = '\\'; *q++ = '\\'; break; default: *q++ = *p; break; } } *q = 0; /* zero terminate */ return dest; } /** * poppler_named_dest_to_bytestring: * @name: the named dest string * @length: (out): a location to store the length of the returned bytestring * * 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(). * * Returns: (array length=length) (transfer full) (nullable): a new bytestring, * or %NULL * * Since: 0.73 */ guint8 *poppler_named_dest_to_bytestring(const char *name, gsize *length) { const char *p; guint8 *data, *q; gsize len; g_return_val_if_fail(name != nullptr, nullptr); g_return_val_if_fail(length != nullptr, nullptr); len = strlen(name); q = data = (guint8 *)g_malloc(len); for (p = name; *p; ++p) { if (*p == '\\') { p++; len--; if (*p == '0') { *q++ = '\0'; } else if (*p == '\\') { *q++ = '\\'; } else { goto invalid; } } else { *q++ = *p; } } *length = len; return data; invalid: g_free(data); *length = 0; return nullptr; } /** * poppler_document_find_dest: * @document: A #PopplerDocument * @link_name: a named destination * * Creates a #PopplerDest for the named destination @link_name in @document. * * Note that named destinations are bytestrings, not string. That means that * unless @link_name was returned by a poppler function (e.g. is * #PopplerDest.named_dest), it needs to be converted to string * using poppler_named_dest_from_bytestring() before being passed to this * function. * * The returned value must be freed with poppler_dest_free(). * * Return value: (transfer full): a new #PopplerDest destination, or %NULL if * @link_name is not a destination. **/ PopplerDest *poppler_document_find_dest(PopplerDocument *document, const gchar *link_name) { g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), nullptr); g_return_val_if_fail(link_name != nullptr, nullptr); gsize len; guint8 *data = poppler_named_dest_to_bytestring(link_name, &len); if (data == nullptr) { return nullptr; } GooString g_link_name((const char *)data, (int)len); g_free(data); std::unique_ptr link_dest = document->doc->findDest(&g_link_name); if (link_dest == nullptr) { return nullptr; } PopplerDest *dest = _poppler_dest_new_goto(document, link_dest.get()); return dest; } static gint _poppler_dest_compare_keys(gconstpointer a, gconstpointer b, gpointer user_data) { return g_strcmp0(static_cast(a), static_cast(b)); } static void _poppler_dest_destroy_value(gpointer value) { poppler_dest_free(static_cast(value)); } /** * poppler_document_create_dests_tree: * @document: A #PopplerDocument * * 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(). * * Returns: (transfer full) (nullable): the #GTree, or %NULL * Since: 0.78 **/ GTree *poppler_document_create_dests_tree(PopplerDocument *document) { GTree *tree; Catalog *catalog; PopplerDest *dest; int i; g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), nullptr); catalog = document->doc->getCatalog(); if (catalog == nullptr) { return nullptr; } tree = g_tree_new_full(_poppler_dest_compare_keys, nullptr, g_free, _poppler_dest_destroy_value); // Iterate from name-dict const int nDests = catalog->numDests(); for (i = 0; i < nDests; ++i) { // The names of name-dict cannot contain \0, // so we can use strlen(). auto name = catalog->getDestsName(i); std::unique_ptr link_dest = catalog->getDestsDest(i); if (link_dest) { gchar *key = poppler_named_dest_from_bytestring(reinterpret_cast(name), strlen(name)); dest = _poppler_dest_new_goto(document, link_dest.get()); g_tree_insert(tree, key, dest); } } // Iterate form name-tree const int nDestsNameTree = catalog->numDestNameTree(); for (i = 0; i < nDestsNameTree; ++i) { auto name = catalog->getDestNameTreeName(i); std::unique_ptr link_dest = catalog->getDestNameTreeDest(i); if (link_dest) { gchar *key = poppler_named_dest_from_bytestring(reinterpret_cast(name->c_str()), name->getLength()); dest = _poppler_dest_new_goto(document, link_dest.get()); g_tree_insert(tree, key, dest); } } return tree; } char *_poppler_goo_string_to_utf8(const GooString *s) { if (s == nullptr) { return nullptr; } char *result; if (s->hasUnicodeMarker()) { result = g_convert(s->c_str() + 2, s->getLength() - 2, "UTF-8", "UTF-16BE", nullptr, nullptr, nullptr); } else if (s->hasUnicodeMarkerLE()) { result = g_convert(s->c_str() + 2, s->getLength() - 2, "UTF-8", "UTF-16LE", nullptr, nullptr, nullptr); } else { int len; gunichar *ucs4_temp; int i; len = s->getLength(); ucs4_temp = g_new(gunichar, len + 1); for (i = 0; i < len; ++i) { ucs4_temp[i] = pdfDocEncoding[(unsigned char)s->getChar(i)]; } ucs4_temp[i] = 0; result = g_ucs4_to_utf8(ucs4_temp, -1, nullptr, nullptr, nullptr); g_free(ucs4_temp); } return result; } static GooString *_poppler_goo_string_from_utf8(const gchar *src) { if (src == nullptr) { return nullptr; } gsize outlen; gchar *utf16 = g_convert(src, -1, "UTF-16BE", "UTF-8", nullptr, &outlen, nullptr); if (utf16 == nullptr) { return nullptr; } GooString *result = new GooString(utf16, outlen); g_free(utf16); if (!result->hasUnicodeMarker()) { result->prependUnicodeMarker(); } return result; } static PopplerPageLayout convert_page_layout(Catalog::PageLayout pageLayout) { switch (pageLayout) { case Catalog::pageLayoutSinglePage: return POPPLER_PAGE_LAYOUT_SINGLE_PAGE; case Catalog::pageLayoutOneColumn: return POPPLER_PAGE_LAYOUT_ONE_COLUMN; case Catalog::pageLayoutTwoColumnLeft: return POPPLER_PAGE_LAYOUT_TWO_COLUMN_LEFT; case Catalog::pageLayoutTwoColumnRight: return POPPLER_PAGE_LAYOUT_TWO_COLUMN_RIGHT; case Catalog::pageLayoutTwoPageLeft: return POPPLER_PAGE_LAYOUT_TWO_PAGE_LEFT; case Catalog::pageLayoutTwoPageRight: return POPPLER_PAGE_LAYOUT_TWO_PAGE_RIGHT; case Catalog::pageLayoutNone: default: return POPPLER_PAGE_LAYOUT_UNSET; } } static PopplerPageMode convert_page_mode(Catalog::PageMode pageMode) { switch (pageMode) { case Catalog::pageModeOutlines: return POPPLER_PAGE_MODE_USE_OUTLINES; case Catalog::pageModeThumbs: return POPPLER_PAGE_MODE_USE_THUMBS; case Catalog::pageModeFullScreen: return POPPLER_PAGE_MODE_FULL_SCREEN; case Catalog::pageModeOC: return POPPLER_PAGE_MODE_USE_OC; case Catalog::pageModeAttach: return POPPLER_PAGE_MODE_USE_ATTACHMENTS; case Catalog::pageModeNone: default: return POPPLER_PAGE_MODE_UNSET; } } static PopplerPDFSubtype convert_pdf_subtype(PDFSubtype pdfSubtype) { switch (pdfSubtype) { case subtypePDFA: return POPPLER_PDF_SUBTYPE_PDF_A; case subtypePDFE: return POPPLER_PDF_SUBTYPE_PDF_E; case subtypePDFUA: return POPPLER_PDF_SUBTYPE_PDF_UA; case subtypePDFVT: return POPPLER_PDF_SUBTYPE_PDF_VT; case subtypePDFX: return POPPLER_PDF_SUBTYPE_PDF_X; case subtypeNone: return POPPLER_PDF_SUBTYPE_NONE; case subtypeNull: default: return POPPLER_PDF_SUBTYPE_UNSET; } } static PopplerPDFPart convert_pdf_subtype_part(PDFSubtypePart pdfSubtypePart) { switch (pdfSubtypePart) { case subtypePart1: return POPPLER_PDF_SUBTYPE_PART_1; case subtypePart2: return POPPLER_PDF_SUBTYPE_PART_2; case subtypePart3: return POPPLER_PDF_SUBTYPE_PART_3; case subtypePart4: return POPPLER_PDF_SUBTYPE_PART_4; case subtypePart5: return POPPLER_PDF_SUBTYPE_PART_5; case subtypePart6: return POPPLER_PDF_SUBTYPE_PART_6; case subtypePart7: return POPPLER_PDF_SUBTYPE_PART_7; case subtypePart8: return POPPLER_PDF_SUBTYPE_PART_8; case subtypePartNone: return POPPLER_PDF_SUBTYPE_PART_NONE; case subtypePartNull: default: return POPPLER_PDF_SUBTYPE_PART_UNSET; } } static PopplerPDFConformance convert_pdf_subtype_conformance(PDFSubtypeConformance pdfSubtypeConf) { switch (pdfSubtypeConf) { case subtypeConfA: return POPPLER_PDF_SUBTYPE_CONF_A; case subtypeConfB: return POPPLER_PDF_SUBTYPE_CONF_B; case subtypeConfG: return POPPLER_PDF_SUBTYPE_CONF_G; case subtypeConfN: return POPPLER_PDF_SUBTYPE_CONF_N; case subtypeConfP: return POPPLER_PDF_SUBTYPE_CONF_P; case subtypeConfPG: return POPPLER_PDF_SUBTYPE_CONF_PG; case subtypeConfU: return POPPLER_PDF_SUBTYPE_CONF_U; case subtypeConfNone: return POPPLER_PDF_SUBTYPE_CONF_NONE; case subtypeConfNull: default: return POPPLER_PDF_SUBTYPE_CONF_UNSET; } } /** * poppler_document_get_pdf_version_string: * @document: A #PopplerDocument * * Returns the PDF version of @document as a string (e.g. PDF-1.6) * * Return value: a new allocated string containing the PDF version * of @document, or %NULL * * Since: 0.16 **/ gchar *poppler_document_get_pdf_version_string(PopplerDocument *document) { gchar *retval; g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL); retval = g_strndup("PDF-", 15); /* allocates 16 chars, pads with \0s */ g_ascii_formatd(retval + 4, 15 + 1 - 4, "%.2g", document->doc->getPDFMajorVersion() + document->doc->getPDFMinorVersion() / 10.0); return retval; } /** * poppler_document_get_pdf_version: * @document: A #PopplerDocument * @major_version: (out) (nullable): return location for the PDF major version number * @minor_version: (out) (nullable): return location for the PDF minor version number * * Updates values referenced by @major_version & @minor_version with the * major and minor PDF versions of @document. * * Since: 0.16 **/ void poppler_document_get_pdf_version(PopplerDocument *document, guint *major_version, guint *minor_version) { g_return_if_fail(POPPLER_IS_DOCUMENT(document)); if (major_version) { *major_version = document->doc->getPDFMajorVersion(); } if (minor_version) { *minor_version = document->doc->getPDFMinorVersion(); } } /** * poppler_document_get_title: * @document: A #PopplerDocument * * Returns the document's title * * Return value: a new allocated string containing the title * of @document, or %NULL * * Since: 0.16 **/ gchar *poppler_document_get_title(PopplerDocument *document) { g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL); const std::unique_ptr goo_title = document->doc->getDocInfoTitle(); return _poppler_goo_string_to_utf8(goo_title.get()); } /** * poppler_document_set_title: * @document: A #PopplerDocument * @title: A new title * * Sets the document's title. If @title is %NULL, Title entry * is removed from the document's Info dictionary. * * Since: 0.46 **/ 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 { goo_title = _poppler_goo_string_from_utf8(title); if (!goo_title) { return; } } document->doc->setDocInfoTitle(goo_title); } /** * poppler_document_get_author: * @document: A #PopplerDocument * * Returns the author of the document * * Return value: a new allocated string containing the author * of @document, or %NULL * * Since: 0.16 **/ gchar *poppler_document_get_author(PopplerDocument *document) { g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL); const std::unique_ptr goo_author = document->doc->getDocInfoAuthor(); return _poppler_goo_string_to_utf8(goo_author.get()); } /** * poppler_document_set_author: * @document: A #PopplerDocument * @author: A new author * * Sets the document's author. If @author is %NULL, Author * entry is removed from the document's Info dictionary. * * Since: 0.46 **/ 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 { goo_author = _poppler_goo_string_from_utf8(author); if (!goo_author) { return; } } document->doc->setDocInfoAuthor(goo_author); } /** * poppler_document_get_subject: * @document: A #PopplerDocument * * Returns the subject of the document * * Return value: a new allocated string containing the subject * of @document, or %NULL * * Since: 0.16 **/ gchar *poppler_document_get_subject(PopplerDocument *document) { g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL); const std::unique_ptr goo_subject = document->doc->getDocInfoSubject(); return _poppler_goo_string_to_utf8(goo_subject.get()); } /** * poppler_document_set_subject: * @document: A #PopplerDocument * @subject: A new subject * * Sets the document's subject. If @subject is %NULL, Subject * entry is removed from the document's Info dictionary. * * Since: 0.46 **/ void poppler_document_set_subject(PopplerDocument *document, const gchar *subject) { g_return_if_fail(POPPLER_IS_DOCUMENT(document)); GooString *goo_subject; if (!subject) { goo_subject = nullptr; } else { goo_subject = _poppler_goo_string_from_utf8(subject); if (!goo_subject) { return; } } document->doc->setDocInfoSubject(goo_subject); } /** * poppler_document_get_keywords: * @document: A #PopplerDocument * * Returns the keywords associated to the document * * Return value: a new allocated string containing keywords associated * to @document, or %NULL * * Since: 0.16 **/ gchar *poppler_document_get_keywords(PopplerDocument *document) { g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL); const std::unique_ptr goo_keywords = document->doc->getDocInfoKeywords(); return _poppler_goo_string_to_utf8(goo_keywords.get()); } /** * poppler_document_set_keywords: * @document: A #PopplerDocument * @keywords: New keywords * * Sets the document's keywords. If @keywords is %NULL, * Keywords entry is removed from the document's Info dictionary. * * Since: 0.46 **/ void poppler_document_set_keywords(PopplerDocument *document, const gchar *keywords) { g_return_if_fail(POPPLER_IS_DOCUMENT(document)); GooString *goo_keywords; if (!keywords) { goo_keywords = nullptr; } else { goo_keywords = _poppler_goo_string_from_utf8(keywords); if (!goo_keywords) { return; } } document->doc->setDocInfoKeywords(goo_keywords); } /** * poppler_document_get_creator: * @document: A #PopplerDocument * * Returns the creator of the document. If the document was converted * from another format, the creator is the name of the product * that created the original document from which it was converted. * * Return value: a new allocated string containing the creator * of @document, or %NULL * * Since: 0.16 **/ gchar *poppler_document_get_creator(PopplerDocument *document) { g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL); const std::unique_ptr goo_creator = document->doc->getDocInfoCreator(); return _poppler_goo_string_to_utf8(goo_creator.get()); } /** * poppler_document_set_creator: * @document: A #PopplerDocument * @creator: A new creator * * Sets the document's creator. If @creator is %NULL, Creator * entry is removed from the document's Info dictionary. * * Since: 0.46 **/ void poppler_document_set_creator(PopplerDocument *document, const gchar *creator) { g_return_if_fail(POPPLER_IS_DOCUMENT(document)); GooString *goo_creator; if (!creator) { goo_creator = nullptr; } else { goo_creator = _poppler_goo_string_from_utf8(creator); if (!goo_creator) { return; } } document->doc->setDocInfoCreator(goo_creator); } /** * poppler_document_get_producer: * @document: A #PopplerDocument * * Returns the producer of the document. If the document was converted * from another format, the producer is the name of the product * that converted it to PDF * * Return value: a new allocated string containing the producer * of @document, or %NULL * * Since: 0.16 **/ gchar *poppler_document_get_producer(PopplerDocument *document) { g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL); const std::unique_ptr goo_producer = document->doc->getDocInfoProducer(); return _poppler_goo_string_to_utf8(goo_producer.get()); } /** * poppler_document_set_producer: * @document: A #PopplerDocument * @producer: A new producer * * Sets the document's producer. If @producer is %NULL, * Producer entry is removed from the document's Info dictionary. * * Since: 0.46 **/ void poppler_document_set_producer(PopplerDocument *document, const gchar *producer) { g_return_if_fail(POPPLER_IS_DOCUMENT(document)); GooString *goo_producer; if (!producer) { goo_producer = nullptr; } else { goo_producer = _poppler_goo_string_from_utf8(producer); if (!goo_producer) { return; } } document->doc->setDocInfoProducer(goo_producer); } /** * poppler_document_get_creation_date: * @document: A #PopplerDocument * * Returns the date the document was created as seconds since the Epoch * * Return value: the date the document was created, or -1 * * Since: 0.16 **/ time_t poppler_document_get_creation_date(PopplerDocument *document) { g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), (time_t)-1); const std::unique_ptr str = document->doc->getDocInfoCreatDate(); if (!str) { return (time_t)-1; } time_t date; gboolean success = _poppler_convert_pdf_date_to_gtime(str.get(), &date); return (success) ? date : (time_t)-1; } /** * poppler_document_set_creation_date: * @document: A #PopplerDocument * @creation_date: A new creation date * * Sets the document's creation date. If @creation_date is -1, CreationDate * entry is removed from the document's Info dictionary. * * Since: 0.46 **/ void poppler_document_set_creation_date(PopplerDocument *document, time_t creation_date) { g_return_if_fail(POPPLER_IS_DOCUMENT(document)); GooString *str = creation_date == (time_t)-1 ? nullptr : timeToDateString(&creation_date); document->doc->setDocInfoCreatDate(str); } /** * poppler_document_get_creation_date_time: * @document: A #PopplerDocument * * Returns the date the document was created as a #GDateTime * * Returns: (nullable): the date the document was created, or %NULL * * Since: 20.09.0 **/ GDateTime *poppler_document_get_creation_date_time(PopplerDocument *document) { g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), nullptr); std::unique_ptr str { document->doc->getDocInfoCreatDate() }; if (!str) { return nullptr; } return _poppler_convert_pdf_date_to_date_time(str.get()); } /** * poppler_document_set_creation_date_time: * @document: A #PopplerDocument * @creation_datetime: (nullable): A new creation #GDateTime * * Sets the document's creation date. If @creation_datetime is %NULL, * CreationDate entry is removed from the document's Info dictionary. * * Since: 20.09.0 **/ void poppler_document_set_creation_date_time(PopplerDocument *document, GDateTime *creation_datetime) { g_return_if_fail(POPPLER_IS_DOCUMENT(document)); GooString *str = nullptr; if (creation_datetime) { str = _poppler_convert_date_time_to_pdf_date(creation_datetime); } document->doc->setDocInfoCreatDate(str); } /** * poppler_document_get_modification_date: * @document: A #PopplerDocument * * Returns the date the document was most recently modified as seconds since the Epoch * * Return value: the date the document was most recently modified, or -1 * * Since: 0.16 **/ time_t poppler_document_get_modification_date(PopplerDocument *document) { g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), (time_t)-1); const std::unique_ptr str = document->doc->getDocInfoModDate(); if (!str) { return (time_t)-1; } time_t date; gboolean success = _poppler_convert_pdf_date_to_gtime(str.get(), &date); return (success) ? date : (time_t)-1; } /** * poppler_document_set_modification_date: * @document: A #PopplerDocument * @modification_date: A new modification date * * Sets the document's modification date. If @modification_date is -1, ModDate * entry is removed from the document's Info dictionary. * * Since: 0.46 **/ void poppler_document_set_modification_date(PopplerDocument *document, time_t modification_date) { g_return_if_fail(POPPLER_IS_DOCUMENT(document)); GooString *str = modification_date == (time_t)-1 ? nullptr : timeToDateString(&modification_date); document->doc->setDocInfoModDate(str); } /** * poppler_document_get_modification_date_time: * @document: A #PopplerDocument * * Returns the date the document was most recently modified as a #GDateTime * * Returns: (nullable): the date the document was modified, or %NULL * * Since: 20.09.0 **/ GDateTime *poppler_document_get_modification_date_time(PopplerDocument *document) { g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), nullptr); std::unique_ptr str { document->doc->getDocInfoModDate() }; if (!str) { return nullptr; } return _poppler_convert_pdf_date_to_date_time(str.get()); } /** * poppler_document_set_modification_date_time: * @document: A #PopplerDocument * @modification_datetime: (nullable): A new modification #GDateTime * * Sets the document's modification date. If @modification_datetime is %NULL, * ModDate entry is removed from the document's Info dictionary. * * Since: 20.09.0 **/ void poppler_document_set_modification_date_time(PopplerDocument *document, GDateTime *modification_datetime) { g_return_if_fail(POPPLER_IS_DOCUMENT(document)); GooString *str = nullptr; if (modification_datetime) { str = _poppler_convert_date_time_to_pdf_date(modification_datetime); } document->doc->setDocInfoModDate(str); } /** * poppler_document_is_linearized: * @document: A #PopplerDocument * * Returns whether @document is linearized or not. Linearization of PDF * enables efficient incremental access of the PDF file in a network environment. * * Return value: %TRUE if @document is linearized, %FALSE otherwise * * Since: 0.16 **/ gboolean poppler_document_is_linearized(PopplerDocument *document) { g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), FALSE); return document->doc->isLinearized(); } /** * poppler_document_get_n_signatures: * @document: A #PopplerDocument * * Returns how many digital signatures @document contains. * PDF digital signatures ensure that the content hash not been altered since last edit and * that it was produced by someone the user can trust * * Return value: The number of signatures found in the document * * Since: 21.12.0 **/ gint poppler_document_get_n_signatures(const PopplerDocument *document) { g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), 0); return document->doc->getSignatureFields().size(); } /** * poppler_document_get_signature_fields: * @document: A #PopplerDocument * * Returns a #GList containing all signature #PopplerFormFields in the document. * * Return value: (element-type PopplerFormField) (transfer full): a list of all signature form fields. * * Since: 22.02.0 **/ GList *poppler_document_get_signature_fields(PopplerDocument *document) { std::vector signature_fields; FormWidget *widget; GList *result = nullptr; gsize i; signature_fields = document->doc->getSignatureFields(); for (i = 0; i < signature_fields.size(); i++) { widget = signature_fields[i]->getCreateWidget(); if (widget != nullptr) { result = g_list_prepend(result, _poppler_form_field_new(document, widget)); } } return g_list_reverse(result); } /** * poppler_document_get_page_layout: * @document: A #PopplerDocument * * Returns the page layout that should be used when the document is opened * * Return value: a #PopplerPageLayout that should be used when the document is opened * * Since: 0.16 **/ PopplerPageLayout poppler_document_get_page_layout(PopplerDocument *document) { Catalog *catalog; g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), POPPLER_PAGE_LAYOUT_UNSET); catalog = document->doc->getCatalog(); if (catalog && catalog->isOk()) { return convert_page_layout(catalog->getPageLayout()); } return POPPLER_PAGE_LAYOUT_UNSET; } /** * poppler_document_get_page_mode: * @document: A #PopplerDocument * * Returns a #PopplerPageMode representing how the document should * be initially displayed when opened. * * Return value: a #PopplerPageMode that should be used when document is opened * * Since: 0.16 **/ PopplerPageMode poppler_document_get_page_mode(PopplerDocument *document) { Catalog *catalog; g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), POPPLER_PAGE_MODE_UNSET); catalog = document->doc->getCatalog(); if (catalog && catalog->isOk()) { return convert_page_mode(catalog->getPageMode()); } return POPPLER_PAGE_MODE_UNSET; } /** * poppler_document_get_print_scaling: * @document: A #PopplerDocument * * Returns the print scaling value suggested by author of the document. * * Return value: a #PopplerPrintScaling that should be used when document is printed * * Since: 0.73 **/ PopplerPrintScaling poppler_document_get_print_scaling(PopplerDocument *document) { Catalog *catalog; ViewerPreferences *preferences; PopplerPrintScaling print_scaling = POPPLER_PRINT_SCALING_APP_DEFAULT; g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), POPPLER_PRINT_SCALING_APP_DEFAULT); catalog = document->doc->getCatalog(); if (catalog && catalog->isOk()) { preferences = catalog->getViewerPreferences(); if (preferences) { switch (preferences->getPrintScaling()) { default: case ViewerPreferences::PrintScaling::printScalingAppDefault: print_scaling = POPPLER_PRINT_SCALING_APP_DEFAULT; break; case ViewerPreferences::PrintScaling::printScalingNone: print_scaling = POPPLER_PRINT_SCALING_NONE; break; } } } return print_scaling; } /** * poppler_document_get_print_duplex: * @document: A #PopplerDocument * * Returns the duplex mode value suggested for printing by author of the document. * Value POPPLER_PRINT_DUPLEX_NONE means that the document does not specify this * preference. * * Returns: a #PopplerPrintDuplex that should be used when document is printed * * Since: 0.80 **/ PopplerPrintDuplex poppler_document_get_print_duplex(PopplerDocument *document) { Catalog *catalog; ViewerPreferences *preferences; PopplerPrintDuplex duplex = POPPLER_PRINT_DUPLEX_NONE; g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), POPPLER_PRINT_DUPLEX_NONE); catalog = document->doc->getCatalog(); if (catalog && catalog->isOk()) { preferences = catalog->getViewerPreferences(); if (preferences) { switch (preferences->getDuplex()) { default: case ViewerPreferences::Duplex::duplexNone: duplex = POPPLER_PRINT_DUPLEX_NONE; break; case ViewerPreferences::Duplex::duplexSimplex: duplex = POPPLER_PRINT_DUPLEX_SIMPLEX; break; case ViewerPreferences::Duplex::duplexDuplexFlipShortEdge: duplex = POPPLER_PRINT_DUPLEX_DUPLEX_FLIP_SHORT_EDGE; break; case ViewerPreferences::Duplex::duplexDuplexFlipLongEdge: duplex = POPPLER_PRINT_DUPLEX_DUPLEX_FLIP_LONG_EDGE; break; } } } return duplex; } /** * poppler_document_get_print_n_copies: * @document: A #PopplerDocument * * Returns the suggested number of copies to be printed. * This preference should be applied only if returned value * is greater than 1 since value 1 usually means that * the document does not specify it. * * Returns: Number of copies * * Since: 0.80 **/ gint poppler_document_get_print_n_copies(PopplerDocument *document) { Catalog *catalog; ViewerPreferences *preferences; gint retval = 1; g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), 1); catalog = document->doc->getCatalog(); if (catalog && catalog->isOk()) { preferences = catalog->getViewerPreferences(); if (preferences) { retval = preferences->getNumCopies(); } } return retval; } /** * poppler_document_get_print_page_ranges: * @document: A #PopplerDocument * @n_ranges: (out): return location for number of 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 * for printing. * * Returns: (array length=n_ranges) (transfer full): an array * of #PopplerPageRanges or %NULL. Free the array when * it is no longer needed. * * Since: 0.80 **/ PopplerPageRange *poppler_document_get_print_page_ranges(PopplerDocument *document, int *n_ranges) { Catalog *catalog; ViewerPreferences *preferences; std::vector> ranges; PopplerPageRange *result = nullptr; g_return_val_if_fail(n_ranges != nullptr, nullptr); *n_ranges = 0; g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), nullptr); catalog = document->doc->getCatalog(); if (catalog && catalog->isOk()) { preferences = catalog->getViewerPreferences(); if (preferences) { ranges = preferences->getPrintPageRange(); *n_ranges = ranges.size(); result = g_new(PopplerPageRange, ranges.size()); for (size_t i = 0; i < ranges.size(); ++i) { result[i].start_page = ranges[i].first; result[i].end_page = ranges[i].second; } } } return result; } /** * poppler_document_get_permissions: * @document: A #PopplerDocument * * Returns the flags specifying which operations are permitted when the document is opened. * * Return value: a set of flags from #PopplerPermissions enumeration * * Since: 0.16 **/ PopplerPermissions poppler_document_get_permissions(PopplerDocument *document) { guint flag = 0; g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), POPPLER_PERMISSIONS_FULL); if (document->doc->okToPrint()) { flag |= POPPLER_PERMISSIONS_OK_TO_PRINT; } if (document->doc->okToChange()) { flag |= POPPLER_PERMISSIONS_OK_TO_MODIFY; } if (document->doc->okToCopy()) { flag |= POPPLER_PERMISSIONS_OK_TO_COPY; } if (document->doc->okToAddNotes()) { flag |= POPPLER_PERMISSIONS_OK_TO_ADD_NOTES; } if (document->doc->okToFillForm()) { flag |= POPPLER_PERMISSIONS_OK_TO_FILL_FORM; } if (document->doc->okToAccessibility()) { flag |= POPPLER_PERMISSIONS_OK_TO_EXTRACT_CONTENTS; } if (document->doc->okToAssemble()) { flag |= POPPLER_PERMISSIONS_OK_TO_ASSEMBLE; } if (document->doc->okToPrintHighRes()) { flag |= POPPLER_PERMISSIONS_OK_TO_PRINT_HIGH_RESOLUTION; } return (PopplerPermissions)flag; } /** * poppler_document_get_pdf_subtype_string: * @document: A #PopplerDocument * * Returns the PDF subtype version of @document as a string. * * Returns: (transfer full) (nullable): a newly allocated string containing * the PDF subtype version of @document, or %NULL * * Since: 0.70 **/ gchar *poppler_document_get_pdf_subtype_string(PopplerDocument *document) { g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL); std::unique_ptr infostring; switch (document->doc->getPDFSubtype()) { case subtypePDFA: infostring = document->doc->getDocInfoStringEntry("GTS_PDFA1Version"); break; case subtypePDFE: infostring = document->doc->getDocInfoStringEntry("GTS_PDFEVersion"); break; case subtypePDFUA: infostring = document->doc->getDocInfoStringEntry("GTS_PDFUAVersion"); break; case subtypePDFVT: infostring = document->doc->getDocInfoStringEntry("GTS_PDFVTVersion"); break; case subtypePDFX: infostring = document->doc->getDocInfoStringEntry("GTS_PDFXVersion"); break; case subtypeNone: case subtypeNull: default: { /* nothing */ } } return _poppler_goo_string_to_utf8(infostring.get()); } /** * poppler_document_get_pdf_subtype: * @document: A #PopplerDocument * * Returns the subtype of @document as a #PopplerPDFSubtype. * * Returns: the document's subtype * * Since: 0.70 **/ PopplerPDFSubtype poppler_document_get_pdf_subtype(PopplerDocument *document) { g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), POPPLER_PDF_SUBTYPE_NONE); return convert_pdf_subtype(document->doc->getPDFSubtype()); } /** * poppler_document_get_pdf_part: * @document: A #PopplerDocument * * Returns the part of the conforming standard that the @document adheres to * as a #PopplerPDFSubtype. * * Returns: the document's subtype part * * Since: 0.70 **/ PopplerPDFPart poppler_document_get_pdf_part(PopplerDocument *document) { g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), POPPLER_PDF_SUBTYPE_PART_NONE); return convert_pdf_subtype_part(document->doc->getPDFSubtypePart()); } /** * poppler_document_get_pdf_conformance: * @document: A #PopplerDocument * * Returns the conformance level of the @document as #PopplerPDFConformance. * * Returns: the document's subtype conformance level * * Since: 0.70 **/ PopplerPDFConformance poppler_document_get_pdf_conformance(PopplerDocument *document) { g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), POPPLER_PDF_SUBTYPE_CONF_NONE); return convert_pdf_subtype_conformance(document->doc->getPDFSubtypeConformance()); } /** * poppler_document_get_metadata: * @document: A #PopplerDocument * * Returns the XML metadata string of the document * * Return value: a new allocated string containing the XML * metadata, or %NULL * * Since: 0.16 **/ gchar *poppler_document_get_metadata(PopplerDocument *document) { Catalog *catalog; gchar *retval = nullptr; g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL); catalog = document->doc->getCatalog(); if (catalog && catalog->isOk()) { std::unique_ptr s = catalog->readMetadata(); if (s) { retval = g_strdup(s->c_str()); } } return retval; } /** * poppler_document_reset_form: * @document: A #PopplerDocument * @fields: (element-type utf8) (nullable): list of fields to reset * @exclude_fields: whether to reset all fields except those in @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 * if fields is empty. * * Since: 0.90 **/ void poppler_document_reset_form(PopplerDocument *document, GList *fields, gboolean exclude_fields) { std::vector list; Catalog *catalog; GList *iter; Form *form; g_return_if_fail(POPPLER_IS_DOCUMENT(document)); catalog = document->doc->getCatalog(); if (catalog && catalog->isOk()) { form = catalog->getForm(); if (form) { for (iter = fields; iter != nullptr; iter = iter->next) { list.emplace_back((char *)iter->data); } form->reset(list, exclude_fields); } } } /** * poppler_document_has_javascript: * @document: A #PopplerDocument * * Returns whether @document has any javascript in it. * * Since: 0.90 **/ gboolean poppler_document_has_javascript(PopplerDocument *document) { g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), FALSE); return document->doc->hasJavascript(); } static void poppler_document_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { PopplerDocument *document = POPPLER_DOCUMENT(object); guint version; switch (prop_id) { case PROP_TITLE: g_value_take_string(value, poppler_document_get_title(document)); break; case PROP_FORMAT: g_value_take_string(value, poppler_document_get_pdf_version_string(document)); break; case PROP_FORMAT_MAJOR: poppler_document_get_pdf_version(document, &version, nullptr); g_value_set_uint(value, version); break; case PROP_FORMAT_MINOR: poppler_document_get_pdf_version(document, nullptr, &version); g_value_set_uint(value, version); break; case PROP_AUTHOR: g_value_take_string(value, poppler_document_get_author(document)); break; case PROP_SUBJECT: g_value_take_string(value, poppler_document_get_subject(document)); break; case PROP_KEYWORDS: g_value_take_string(value, poppler_document_get_keywords(document)); break; case PROP_CREATOR: g_value_take_string(value, poppler_document_get_creator(document)); break; case PROP_PRODUCER: g_value_take_string(value, poppler_document_get_producer(document)); break; case PROP_CREATION_DATE: g_value_set_int(value, poppler_document_get_creation_date(document)); break; case PROP_CREATION_DATETIME: g_value_take_boxed(value, poppler_document_get_creation_date_time(document)); break; case PROP_MOD_DATE: g_value_set_int(value, poppler_document_get_modification_date(document)); break; case PROP_MOD_DATETIME: g_value_take_boxed(value, poppler_document_get_modification_date_time(document)); break; case PROP_LINEARIZED: g_value_set_boolean(value, poppler_document_is_linearized(document)); break; case PROP_PAGE_LAYOUT: g_value_set_enum(value, poppler_document_get_page_layout(document)); break; case PROP_PAGE_MODE: g_value_set_enum(value, poppler_document_get_page_mode(document)); break; case PROP_VIEWER_PREFERENCES: /* FIXME: write... */ g_value_set_flags(value, POPPLER_VIEWER_PREFERENCES_UNSET); break; case PROP_PRINT_SCALING: g_value_set_enum(value, poppler_document_get_print_scaling(document)); break; case PROP_PRINT_DUPLEX: g_value_set_enum(value, poppler_document_get_print_duplex(document)); break; case PROP_PRINT_N_COPIES: g_value_set_int(value, poppler_document_get_print_n_copies(document)); break; case PROP_PERMISSIONS: g_value_set_flags(value, poppler_document_get_permissions(document)); break; case PROP_SUBTYPE: g_value_set_enum(value, poppler_document_get_pdf_subtype(document)); break; case PROP_SUBTYPE_STRING: g_value_take_string(value, poppler_document_get_pdf_subtype_string(document)); break; case PROP_SUBTYPE_PART: g_value_set_enum(value, poppler_document_get_pdf_part(document)); break; case PROP_SUBTYPE_CONF: g_value_set_enum(value, poppler_document_get_pdf_conformance(document)); break; case PROP_METADATA: g_value_take_string(value, poppler_document_get_metadata(document)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); } } static void poppler_document_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { PopplerDocument *document = POPPLER_DOCUMENT(object); switch (prop_id) { case PROP_TITLE: poppler_document_set_title(document, g_value_get_string(value)); break; case PROP_AUTHOR: poppler_document_set_author(document, g_value_get_string(value)); break; case PROP_SUBJECT: poppler_document_set_subject(document, g_value_get_string(value)); break; case PROP_KEYWORDS: poppler_document_set_keywords(document, g_value_get_string(value)); break; case PROP_CREATOR: poppler_document_set_creator(document, g_value_get_string(value)); break; case PROP_PRODUCER: poppler_document_set_producer(document, g_value_get_string(value)); break; case PROP_CREATION_DATE: poppler_document_set_creation_date(document, g_value_get_int(value)); break; case PROP_CREATION_DATETIME: poppler_document_set_creation_date_time(document, (GDateTime *)g_value_get_boxed(value)); break; case PROP_MOD_DATE: poppler_document_set_modification_date(document, g_value_get_int(value)); break; case PROP_MOD_DATETIME: poppler_document_set_modification_date_time(document, (GDateTime *)g_value_get_boxed(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); } } static void poppler_document_class_init(PopplerDocumentClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS(klass); gobject_class->finalize = poppler_document_finalize; gobject_class->get_property = poppler_document_get_property; gobject_class->set_property = poppler_document_set_property; /** * PopplerDocument:title: * * The document's title or %NULL */ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_TITLE, g_param_spec_string("title", "Document Title", "The title of the document", nullptr, G_PARAM_READWRITE)); /** * PopplerDocument:format: * * The PDF version as string. See also poppler_document_get_pdf_version_string() */ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_FORMAT, g_param_spec_string("format", "PDF Format", "The PDF version of the document", nullptr, G_PARAM_READABLE)); /** * PopplerDocument:format-major: * * The PDF major version number. See also poppler_document_get_pdf_version() */ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_FORMAT_MAJOR, g_param_spec_uint("format-major", "PDF Format Major", "The PDF major version number of the document", 0, G_MAXUINT, 1, G_PARAM_READABLE)); /** * PopplerDocument:format-minor: * * The PDF minor version number. See also poppler_document_get_pdf_version() */ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_FORMAT_MINOR, g_param_spec_uint("format-minor", "PDF Format Minor", "The PDF minor version number of the document", 0, G_MAXUINT, 0, G_PARAM_READABLE)); /** * PopplerDocument:author: * * The author of the document */ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_AUTHOR, g_param_spec_string("author", "Author", "The author of the document", nullptr, G_PARAM_READWRITE)); /** * PopplerDocument:subject: * * The subject of the document */ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_SUBJECT, g_param_spec_string("subject", "Subject", "Subjects the document touches", nullptr, G_PARAM_READWRITE)); /** * PopplerDocument:keywords: * * The keywords associated to the document */ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_KEYWORDS, g_param_spec_string("keywords", "Keywords", "Keywords", nullptr, G_PARAM_READWRITE)); /** * PopplerDocument:creator: * * The creator of the document. See also poppler_document_get_creator() */ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_CREATOR, g_param_spec_string("creator", "Creator", "The software that created the document", nullptr, G_PARAM_READWRITE)); /** * PopplerDocument:producer: * * The producer of the document. See also poppler_document_get_producer() */ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_PRODUCER, g_param_spec_string("producer", "Producer", "The software that converted the document", nullptr, G_PARAM_READWRITE)); /** * PopplerDocument:creation-date: * * The date the document was created as seconds since the Epoch, or -1 * * Deprecated: 20.09.0: This will overflow in 2038. Use creation-datetime * instead. */ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_CREATION_DATE, g_param_spec_int("creation-date", "Creation Date", "The date and time the document was created", -1, G_MAXINT, -1, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_DEPRECATED))); /** * PopplerDocument:creation-datetime: * The #GDateTime the document was created. * * Since: 20.09.0 */ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_CREATION_DATETIME, g_param_spec_boxed("creation-datetime", "Creation DateTime", "The date and time the document was created", G_TYPE_DATE_TIME, G_PARAM_READWRITE)); /** * PopplerDocument:mod-date: * * The date the document was most recently modified as seconds since the Epoch, or -1 * * Deprecated: 20.09.0: This will overflow in 2038. Use mod-datetime instead. */ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_MOD_DATE, g_param_spec_int("mod-date", "Modification Date", "The date and time the document was modified", -1, G_MAXINT, -1, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_DEPRECATED))); /** * PopplerDocument:mod-datetime: * * The #GDateTime the document was most recently modified. * * Since: 20.09.0 */ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_MOD_DATETIME, g_param_spec_boxed("mod-datetime", "Modification DateTime", "The date and time the document was modified", G_TYPE_DATE_TIME, G_PARAM_READWRITE)); /** * PopplerDocument:linearized: * * Whether document is linearized. See also poppler_document_is_linearized() */ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_LINEARIZED, g_param_spec_boolean("linearized", "Fast Web View Enabled", "Is the document optimized for web viewing?", FALSE, G_PARAM_READABLE)); /** * PopplerDocument:page-layout: * * The page layout that should be used when the document is opened */ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_PAGE_LAYOUT, g_param_spec_enum("page-layout", "Page Layout", "Initial Page Layout", POPPLER_TYPE_PAGE_LAYOUT, POPPLER_PAGE_LAYOUT_UNSET, G_PARAM_READABLE)); /** * PopplerDocument:page-mode: * * The mode that should be used when the document is opened */ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_PAGE_MODE, g_param_spec_enum("page-mode", "Page Mode", "Page Mode", POPPLER_TYPE_PAGE_MODE, POPPLER_PAGE_MODE_UNSET, G_PARAM_READABLE)); /** * PopplerDocument:viewer-preferences: */ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_VIEWER_PREFERENCES, g_param_spec_flags("viewer-preferences", "Viewer Preferences", "Viewer Preferences", POPPLER_TYPE_VIEWER_PREFERENCES, POPPLER_VIEWER_PREFERENCES_UNSET, G_PARAM_READABLE)); /** * PopplerDocument:print-scaling: * * Since: 0.73 */ g_object_class_install_property( G_OBJECT_CLASS(klass), PROP_PRINT_SCALING, g_param_spec_enum("print-scaling", "Print Scaling", "Print Scaling Viewer Preference", POPPLER_TYPE_PRINT_SCALING, POPPLER_PRINT_SCALING_APP_DEFAULT, (GParamFlags)(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); /** * PopplerDocument:print-duplex: * * Since: 0.80 */ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_PRINT_DUPLEX, g_param_spec_enum("print-duplex", "Print Duplex", "Duplex Viewer Preference", POPPLER_TYPE_PRINT_DUPLEX, POPPLER_PRINT_DUPLEX_NONE, (GParamFlags)(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); /** * PopplerDocument:print-n-copies: * * Suggested number of copies to be printed for this document * * Since: 0.80 */ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_PRINT_N_COPIES, g_param_spec_int("print-n-copies", "Number of Copies to Print", "Number of Copies Viewer Preference", 1, G_MAXINT, 1, (GParamFlags)(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); /** * PopplerDocument:permissions: * * Flags specifying which operations are permitted when the document is opened */ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_PERMISSIONS, g_param_spec_flags("permissions", "Permissions", "Permissions", POPPLER_TYPE_PERMISSIONS, POPPLER_PERMISSIONS_FULL, G_PARAM_READABLE)); /** * PopplerDocument:subtype: * * Document PDF subtype type */ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_SUBTYPE, g_param_spec_enum("subtype", "PDF Format Subtype Type", "The PDF subtype of the document", POPPLER_TYPE_PDF_SUBTYPE, POPPLER_PDF_SUBTYPE_UNSET, G_PARAM_READABLE)); /** * PopplerDocument:subtype-string: * * Document PDF subtype. See also poppler_document_get_pdf_subtype_string() */ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_SUBTYPE_STRING, g_param_spec_string("subtype-string", "PDF Format Subtype", "The PDF subtype of the document", nullptr, G_PARAM_READABLE)); /** * PopplerDocument:subtype-part: * * Document PDF subtype part */ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_SUBTYPE_PART, g_param_spec_enum("subtype-part", "PDF Format Subtype Part", "The part of PDF conformance", POPPLER_TYPE_PDF_PART, POPPLER_PDF_SUBTYPE_PART_UNSET, G_PARAM_READABLE)); /** * PopplerDocument:subtype-conformance: * * Document PDF subtype conformance */ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_SUBTYPE_CONF, g_param_spec_enum("subtype-conformance", "PDF Format Subtype Conformance", "The conformance level of PDF subtype", POPPLER_TYPE_PDF_CONFORMANCE, POPPLER_PDF_SUBTYPE_CONF_UNSET, G_PARAM_READABLE)); /** * PopplerDocument:metadata: * * Document metadata in XML format, or %NULL */ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_METADATA, g_param_spec_string("metadata", "XML Metadata", "Embedded XML metadata", nullptr, G_PARAM_READABLE)); } static void poppler_document_init(PopplerDocument *document) { } /* PopplerIndexIter: For determining the index of a tree */ struct _PopplerIndexIter { PopplerDocument *document; const std::vector *items; int index; }; G_DEFINE_BOXED_TYPE(PopplerIndexIter, poppler_index_iter, poppler_index_iter_copy, poppler_index_iter_free) /** * poppler_index_iter_copy: * @iter: a #PopplerIndexIter * * Creates a new #PopplerIndexIter as a copy of @iter. This must be freed with * poppler_index_iter_free(). * * Return value: a new #PopplerIndexIter **/ PopplerIndexIter *poppler_index_iter_copy(PopplerIndexIter *iter) { PopplerIndexIter *new_iter; g_return_val_if_fail(iter != nullptr, NULL); new_iter = g_slice_dup(PopplerIndexIter, iter); new_iter->document = (PopplerDocument *)g_object_ref(new_iter->document); return new_iter; } /** * poppler_index_iter_new: * @document: a #PopplerDocument * * Returns the root #PopplerIndexIter for @document, 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 * contents. Each node in the index will contain a #PopplerAction that can be * displayed to the user — typically a #POPPLER_ACTION_GOTO_DEST or a * #POPPLER_ACTION_URI. * * Here is a simple example of some code that walks the full index: * * * 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); * } * * * Return value: a new #PopplerIndexIter **/ PopplerIndexIter *poppler_index_iter_new(PopplerDocument *document) { PopplerIndexIter *iter; Outline *outline; const std::vector *items; outline = document->doc->getOutline(); if (outline == nullptr) { return nullptr; } items = outline->getItems(); if (items == nullptr) { return nullptr; } iter = g_slice_new(PopplerIndexIter); iter->document = (PopplerDocument *)g_object_ref(document); iter->items = items; iter->index = 0; return iter; } /** * poppler_index_iter_get_child: * @parent: a #PopplerIndexIter * * Returns a newly created child of @parent, or %NULL if the iter has no child. * See poppler_index_iter_new() for more information on this function. * * Return value: a new #PopplerIndexIter **/ PopplerIndexIter *poppler_index_iter_get_child(PopplerIndexIter *parent) { PopplerIndexIter *child; OutlineItem *item; g_return_val_if_fail(parent != nullptr, NULL); item = (*parent->items)[parent->index]; item->open(); if (!(item->hasKids() && item->getKids())) { return nullptr; } child = g_slice_new0(PopplerIndexIter); child->document = (PopplerDocument *)g_object_ref(parent->document); child->items = item->getKids(); g_assert(child->items); return child; } static gchar *unicode_to_char(const Unicode *unicode, int len) { const UnicodeMap *uMap = globalParams->getUtf8Map(); GooString gstr; gchar buf[8]; /* 8 is enough for mapping an unicode char to a string */ int i, n; for (i = 0; i < len; ++i) { n = uMap->mapUnicode(unicode[i], buf, sizeof(buf)); gstr.append(buf, n); } return g_strdup(gstr.c_str()); } /** * poppler_index_iter_is_open: * @iter: a #PopplerIndexIter * * 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 * initially. * * Return value: %TRUE, if the document wants @iter to be expanded **/ gboolean poppler_index_iter_is_open(PopplerIndexIter *iter) { OutlineItem *item; item = (*iter->items)[iter->index]; return item->isOpen(); } /** * poppler_index_iter_get_action: * @iter: a #PopplerIndexIter * * Returns the #PopplerAction associated with @iter. It must be freed with * poppler_action_free(). * * Return value: a new #PopplerAction **/ PopplerAction *poppler_index_iter_get_action(PopplerIndexIter *iter) { OutlineItem *item; const LinkAction *link_action; PopplerAction *action; gchar *title; g_return_val_if_fail(iter != nullptr, NULL); item = (*iter->items)[iter->index]; link_action = item->getAction(); 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); return action; } /** * poppler_index_iter_next: * @iter: a #PopplerIndexIter * * Sets @iter to point to the next action at the current level, if valid. See * poppler_index_iter_new() for more information. * * Return value: %TRUE, if @iter was set to the next action **/ gboolean poppler_index_iter_next(PopplerIndexIter *iter) { g_return_val_if_fail(iter != nullptr, FALSE); iter->index++; if (iter->index >= (int)iter->items->size()) { return FALSE; } return TRUE; } /** * poppler_index_iter_free: * @iter: a #PopplerIndexIter * * Frees @iter. **/ void poppler_index_iter_free(PopplerIndexIter *iter) { if (G_UNLIKELY(iter == nullptr)) { return; } g_object_unref(iter->document); g_slice_free(PopplerIndexIter, iter); } struct _PopplerFontsIter { std::vector items; int index; }; G_DEFINE_BOXED_TYPE(PopplerFontsIter, poppler_fonts_iter, poppler_fonts_iter_copy, poppler_fonts_iter_free) /** * poppler_fonts_iter_get_full_name: * @iter: a #PopplerFontsIter * * Returns the full name of the font associated with @iter * * Returns: the font full name */ const char *poppler_fonts_iter_get_full_name(PopplerFontsIter *iter) { FontInfo *info; info = iter->items[iter->index]; const std::optional &name = info->getName(); if (name) { return name->c_str(); } else { return nullptr; } } /** * poppler_fonts_iter_get_name: * @iter: a #PopplerFontsIter * * Returns the name of the font associated with @iter * * Returns: the font name */ const char *poppler_fonts_iter_get_name(PopplerFontsIter *iter) { FontInfo *info; const char *name; name = poppler_fonts_iter_get_full_name(iter); info = iter->items[iter->index]; if (info->getSubset() && name) { while (*name && *name != '+') { name++; } if (*name) { name++; } } return name; } /** * poppler_fonts_iter_get_substitute_name: * @iter: a #PopplerFontsIter * * The name of the substitute font of the font associated with @iter or %NULL if * the font is embedded * * Returns: the name of the substitute font or %NULL if font is embedded * * Since: 0.20 */ const char *poppler_fonts_iter_get_substitute_name(PopplerFontsIter *iter) { FontInfo *info; info = iter->items[iter->index]; const std::optional &name = info->getSubstituteName(); if (name) { return name->c_str(); } else { return nullptr; } } /** * poppler_fonts_iter_get_file_name: * @iter: a #PopplerFontsIter * * The filename of the font associated with @iter or %NULL if * the font is embedded * * Returns: the filename of the font or %NULL if font is embedded */ const char *poppler_fonts_iter_get_file_name(PopplerFontsIter *iter) { FontInfo *info; info = iter->items[iter->index]; const std::optional &file = info->getFile(); if (file) { return file->c_str(); } else { return nullptr; } } /** * poppler_fonts_iter_get_font_type: * @iter: a #PopplerFontsIter * * Returns the type of the font associated with @iter * * Returns: the font type */ PopplerFontType poppler_fonts_iter_get_font_type(PopplerFontsIter *iter) { FontInfo *info; g_return_val_if_fail(iter != nullptr, POPPLER_FONT_TYPE_UNKNOWN); info = iter->items[iter->index]; return (PopplerFontType)info->getType(); } /** * poppler_fonts_iter_get_encoding: * @iter: a #PopplerFontsIter * * Returns the encoding of the font associated with @iter * * Returns: the font encoding * * Since: 0.20 */ const char *poppler_fonts_iter_get_encoding(PopplerFontsIter *iter) { FontInfo *info; info = iter->items[iter->index]; const std::string &encoding = info->getEncoding(); if (!encoding.empty()) { return encoding.c_str(); } else { return nullptr; } } /** * poppler_fonts_iter_is_embedded: * @iter: a #PopplerFontsIter * * Returns whether the font associated with @iter is embedded in the document * * Returns: %TRUE if font is embedded, %FALSE otherwise */ gboolean poppler_fonts_iter_is_embedded(PopplerFontsIter *iter) { FontInfo *info; info = iter->items[iter->index]; return info->getEmbedded(); } /** * poppler_fonts_iter_is_subset: * @iter: a #PopplerFontsIter * * Returns whether the font associated with @iter is a subset of another font * * Returns: %TRUE if font is a subset, %FALSE otherwise */ gboolean poppler_fonts_iter_is_subset(PopplerFontsIter *iter) { FontInfo *info; info = iter->items[iter->index]; return info->getSubset(); } /** * poppler_fonts_iter_next: * @iter: a #PopplerFontsIter * * Sets @iter to point to the next font * * Returns: %TRUE, if @iter was set to the next font **/ gboolean poppler_fonts_iter_next(PopplerFontsIter *iter) { g_return_val_if_fail(iter != nullptr, FALSE); iter->index++; if (iter->index >= (int)iter->items.size()) { return FALSE; } return TRUE; } /** * poppler_fonts_iter_copy: * @iter: a #PopplerFontsIter to copy * * Creates a copy of @iter * * Returns: a new allocated copy of @iter */ PopplerFontsIter *poppler_fonts_iter_copy(PopplerFontsIter *iter) { PopplerFontsIter *new_iter; g_return_val_if_fail(iter != nullptr, NULL); new_iter = g_slice_dup(PopplerFontsIter, iter); new_iter->items.resize(iter->items.size()); for (std::size_t i = 0; i < iter->items.size(); i++) { FontInfo *info = iter->items[i]; new_iter->items[i] = new FontInfo(*info); } return new_iter; } /** * poppler_fonts_iter_free: * @iter: a #PopplerFontsIter * * Frees the given #PopplerFontsIter */ void poppler_fonts_iter_free(PopplerFontsIter *iter) { if (G_UNLIKELY(iter == nullptr)) { return; } for (auto entry : iter->items) { delete entry; } iter->items.~vector(); g_slice_free(PopplerFontsIter, iter); } static PopplerFontsIter *poppler_fonts_iter_new(std::vector &&items) { PopplerFontsIter *iter; iter = g_slice_new(PopplerFontsIter); new ((void *)&iter->items) std::vector(std::move(items)); iter->index = 0; return iter; } typedef struct _PopplerFontInfoClass PopplerFontInfoClass; struct _PopplerFontInfoClass { GObjectClass parent_class; }; G_DEFINE_TYPE(PopplerFontInfo, poppler_font_info, G_TYPE_OBJECT) static void poppler_font_info_finalize(GObject *object); static void poppler_font_info_class_init(PopplerFontInfoClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS(klass); gobject_class->finalize = poppler_font_info_finalize; } static void poppler_font_info_init(PopplerFontInfo *font_info) { font_info->document = nullptr; font_info->scanner = nullptr; } static void poppler_font_info_finalize(GObject *object) { PopplerFontInfo *font_info = POPPLER_FONT_INFO(object); delete font_info->scanner; g_object_unref(font_info->document); G_OBJECT_CLASS(poppler_font_info_parent_class)->finalize(object); } /** * poppler_font_info_new: * @document: a #PopplerDocument * * Creates a new #PopplerFontInfo object * * Returns: a new #PopplerFontInfo instance */ PopplerFontInfo *poppler_font_info_new(PopplerDocument *document) { PopplerFontInfo *font_info; g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL); font_info = (PopplerFontInfo *)g_object_new(POPPLER_TYPE_FONT_INFO, nullptr); font_info->document = (PopplerDocument *)g_object_ref(document); font_info->scanner = new FontInfoScanner(document->doc); return font_info; } /** * poppler_font_info_scan: * @font_info: a #PopplerFontInfo * @n_pages: number of pages to scan * @iter: (out): return location for a #PopplerFontsIter * * Scans the document associated with @font_info for fonts. At most * @n_pages will be scanned starting from the current iterator. @iter will * point to the first font scanned. * * Here is a simple example of code to scan fonts in a document * * * 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); * } * * * Returns: %TRUE, if there are more fonts left to scan */ gboolean poppler_font_info_scan(PopplerFontInfo *font_info, int n_pages, PopplerFontsIter **iter) { g_return_val_if_fail(iter != nullptr, FALSE); std::vector items = font_info->scanner->scan(n_pages); if (items.empty()) { *iter = nullptr; return FALSE; } else { *iter = poppler_fonts_iter_new(std::move(items)); } return TRUE; } /* For backward compatibility */ void poppler_font_info_free(PopplerFontInfo *font_info) { g_return_if_fail(font_info != nullptr); g_object_unref(font_info); } /* Optional content (layers) */ static Layer *layer_new(OptionalContentGroup *oc) { Layer *layer; layer = g_slice_new0(Layer); layer->oc = oc; return layer; } static void layer_free(Layer *layer) { if (G_UNLIKELY(!layer)) { return; } if (layer->kids) { g_list_free_full(layer->kids, (GDestroyNotify)layer_free); } if (layer->label) { g_free(layer->label); } g_slice_free(Layer, layer); } static GList *get_optional_content_rbgroups(OCGs *ocg) { Array *rb; GList *groups = nullptr; rb = ocg->getRBGroupsArray(); if (rb) { int i, j; for (i = 0; i < rb->getLength(); ++i) { Array *rb_array; GList *group = nullptr; Object obj = rb->get(i); if (!obj.isArray()) { continue; } rb_array = obj.getArray(); for (j = 0; j < rb_array->getLength(); ++j) { OptionalContentGroup *oc; const Object &ref = rb_array->getNF(j); if (!ref.isRef()) { continue; } oc = ocg->findOcgByRef(ref.getRef()); group = g_list_prepend(group, oc); } groups = g_list_prepend(groups, group); } } return groups; } GList *_poppler_document_get_layer_rbgroup(PopplerDocument *document, Layer *layer) { GList *l; for (l = document->layers_rbgroups; l && l->data; l = g_list_next(l)) { GList *group = (GList *)l->data; if (g_list_find(group, layer->oc)) { return group; } } return nullptr; } static GList *get_optional_content_items_sorted(OCGs *ocg, Layer *parent, Array *order) { GList *items = nullptr; Layer *last_item = parent; int i; for (i = 0; i < order->getLength(); ++i) { Object orderItem = order->get(i); if (orderItem.isDict()) { const Object &ref = order->getNF(i); if (ref.isRef()) { OptionalContentGroup *oc = ocg->findOcgByRef(ref.getRef()); Layer *layer = layer_new(oc); items = g_list_prepend(items, layer); last_item = layer; } } else if (orderItem.isArray() && orderItem.arrayGetLength() > 0) { if (!last_item) { last_item = layer_new(nullptr); items = g_list_prepend(items, last_item); } last_item->kids = get_optional_content_items_sorted(ocg, last_item, orderItem.getArray()); last_item = nullptr; } else if (orderItem.isString()) { last_item->label = _poppler_goo_string_to_utf8(orderItem.getString()); } } return g_list_reverse(items); } static GList *get_optional_content_items(OCGs *ocg) { Array *order; GList *items = nullptr; order = ocg->getOrderArray(); if (order) { items = get_optional_content_items_sorted(ocg, nullptr, order); } else { const auto &ocgs = ocg->getOCGs(); for (const auto &oc : ocgs) { Layer *layer = layer_new(oc.second.get()); items = g_list_prepend(items, layer); } items = g_list_reverse(items); } return items; } GList *_poppler_document_get_layers(PopplerDocument *document) { if (!document->layers) { Catalog *catalog = document->doc->getCatalog(); OCGs *ocg = catalog->getOptContentConfig(); if (!ocg) { return nullptr; } document->layers = get_optional_content_items(ocg); document->layers_rbgroups = get_optional_content_rbgroups(ocg); } return document->layers; } static void poppler_document_layers_free(PopplerDocument *document) { if (G_UNLIKELY(!document->layers)) { return; } g_list_free_full(document->layers, (GDestroyNotify)layer_free); g_list_free_full(document->layers_rbgroups, (GDestroyNotify)g_list_free); document->layers = nullptr; document->layers_rbgroups = nullptr; } /* PopplerLayersIter */ struct _PopplerLayersIter { PopplerDocument *document; GList *items; int index; }; G_DEFINE_BOXED_TYPE(PopplerLayersIter, poppler_layers_iter, poppler_layers_iter_copy, poppler_layers_iter_free) /** * poppler_layers_iter_copy: * @iter: a #PopplerLayersIter * * Creates a new #PopplerLayersIter as a copy of @iter. This must be freed with * poppler_layers_iter_free(). * * Return value: a new #PopplerLayersIter * * Since 0.12 **/ PopplerLayersIter *poppler_layers_iter_copy(PopplerLayersIter *iter) { PopplerLayersIter *new_iter; g_return_val_if_fail(iter != nullptr, NULL); new_iter = g_slice_dup(PopplerLayersIter, iter); new_iter->document = (PopplerDocument *)g_object_ref(new_iter->document); return new_iter; } /** * poppler_layers_iter_free: * @iter: a #PopplerLayersIter * * Frees @iter. * * Since: 0.12 **/ void poppler_layers_iter_free(PopplerLayersIter *iter) { if (G_UNLIKELY(iter == nullptr)) { return; } g_object_unref(iter->document); g_slice_free(PopplerLayersIter, iter); } /** * poppler_layers_iter_new: * @document: a #PopplerDocument * * Since: 0.12 **/ PopplerLayersIter *poppler_layers_iter_new(PopplerDocument *document) { PopplerLayersIter *iter; GList *items; items = _poppler_document_get_layers(document); if (!items) { return nullptr; } iter = g_slice_new0(PopplerLayersIter); iter->document = (PopplerDocument *)g_object_ref(document); iter->items = items; return iter; } /** * poppler_layers_iter_get_child: * @parent: a #PopplerLayersIter * * Returns a newly created child of @parent, or %NULL if the iter has no child. * See poppler_layers_iter_new() for more information on this function. * * Return value: a new #PopplerLayersIter, or %NULL * * Since: 0.12 **/ PopplerLayersIter *poppler_layers_iter_get_child(PopplerLayersIter *parent) { PopplerLayersIter *child; Layer *layer; g_return_val_if_fail(parent != nullptr, NULL); layer = (Layer *)g_list_nth_data(parent->items, parent->index); if (!layer || !layer->kids) { return nullptr; } child = g_slice_new0(PopplerLayersIter); child->document = (PopplerDocument *)g_object_ref(parent->document); child->items = layer->kids; g_assert(child->items); return child; } /** * poppler_layers_iter_get_title: * @iter: a #PopplerLayersIter * * Returns the title associated with @iter. It must be freed with * g_free(). * * Return value: a new string containing the @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. * * Since: 0.12 **/ gchar *poppler_layers_iter_get_title(PopplerLayersIter *iter) { Layer *layer; g_return_val_if_fail(iter != nullptr, NULL); layer = (Layer *)g_list_nth_data(iter->items, iter->index); return layer->label ? g_strdup(layer->label) : nullptr; } /** * poppler_layers_iter_get_layer: * @iter: a #PopplerLayersIter * * Returns the #PopplerLayer associated with @iter. * * Return value: (transfer full): a new #PopplerLayer, or %NULL if * there isn't any layer associated with @iter * * Since: 0.12 **/ PopplerLayer *poppler_layers_iter_get_layer(PopplerLayersIter *iter) { Layer *layer; PopplerLayer *poppler_layer = nullptr; g_return_val_if_fail(iter != nullptr, NULL); layer = (Layer *)g_list_nth_data(iter->items, iter->index); if (layer->oc) { GList *rb_group = nullptr; rb_group = _poppler_document_get_layer_rbgroup(iter->document, layer); poppler_layer = _poppler_layer_new(iter->document, layer, rb_group); } return poppler_layer; } /** * poppler_layers_iter_next: * @iter: a #PopplerLayersIter * * Sets @iter to point to the next action at the current level, if valid. See * poppler_layers_iter_new() for more information. * * Return value: %TRUE, if @iter was set to the next action * * Since: 0.12 **/ gboolean poppler_layers_iter_next(PopplerLayersIter *iter) { g_return_val_if_fail(iter != nullptr, FALSE); iter->index++; if (iter->index >= (gint)g_list_length(iter->items)) { return FALSE; } return TRUE; } typedef struct _PopplerPSFileClass PopplerPSFileClass; struct _PopplerPSFileClass { GObjectClass parent_class; }; G_DEFINE_TYPE(PopplerPSFile, poppler_ps_file, G_TYPE_OBJECT) static void poppler_ps_file_finalize(GObject *object); static void poppler_ps_file_class_init(PopplerPSFileClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS(klass); gobject_class->finalize = poppler_ps_file_finalize; } static void poppler_ps_file_init(PopplerPSFile *ps_file) { ps_file->out = nullptr; ps_file->fd = -1; ps_file->filename = nullptr; ps_file->paper_width = -1; ps_file->paper_height = -1; ps_file->duplex = FALSE; } static void poppler_ps_file_finalize(GObject *object) { PopplerPSFile *ps_file = POPPLER_PS_FILE(object); delete ps_file->out; g_object_unref(ps_file->document); g_free(ps_file->filename); #ifndef G_OS_WIN32 if (ps_file->fd != -1) { close(ps_file->fd); } #endif /* !G_OS_WIN32 */ G_OBJECT_CLASS(poppler_ps_file_parent_class)->finalize(object); } /** * poppler_ps_file_new: * @document: a #PopplerDocument * @filename: the path of the output filename * @first_page: the first page to print * @n_pages: the number of pages to print * * Create a new postscript file to render to * * Return value: (transfer full): a PopplerPSFile **/ PopplerPSFile *poppler_ps_file_new(PopplerDocument *document, const char *filename, int first_page, int n_pages) { PopplerPSFile *ps_file; g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL); g_return_val_if_fail(filename != nullptr, NULL); g_return_val_if_fail(n_pages > 0, NULL); ps_file = (PopplerPSFile *)g_object_new(POPPLER_TYPE_PS_FILE, nullptr); ps_file->document = (PopplerDocument *)g_object_ref(document); ps_file->filename = g_strdup(filename); ps_file->first_page = first_page + 1; ps_file->last_page = first_page + 1 + n_pages - 1; return ps_file; } #ifndef G_OS_WIN32 /** * poppler_ps_file_new_fd: * @document: a #PopplerDocument * @fd: a valid file descriptor open for writing * @first_page: the first page to print * @n_pages: the number of pages to print * * Create a new postscript file to render to. * Note that this function takes ownership of @fd; you must not operate on it * again, nor close it. * * Return value: (transfer full): a #PopplerPSFile * * Since: 21.12.0 **/ PopplerPSFile *poppler_ps_file_new_fd(PopplerDocument *document, int fd, int first_page, int n_pages) { PopplerPSFile *ps_file; g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), nullptr); g_return_val_if_fail(fd != -1, nullptr); g_return_val_if_fail(n_pages > 0, nullptr); ps_file = (PopplerPSFile *)g_object_new(POPPLER_TYPE_PS_FILE, nullptr); ps_file->document = (PopplerDocument *)g_object_ref(document); ps_file->fd = fd; ps_file->first_page = first_page + 1; ps_file->last_page = first_page + 1 + n_pages - 1; return ps_file; } #endif /* !G_OS_WIN32 */ /** * poppler_ps_file_set_paper_size: * @ps_file: a PopplerPSFile which was not yet printed to. * @width: the paper width in 1/72 inch * @height: the paper height in 1/72 inch * * Set the output paper size. These values will end up in the * DocumentMedia, the BoundingBox DSC comments and other places in the * generated PostScript. * **/ void poppler_ps_file_set_paper_size(PopplerPSFile *ps_file, double width, double height) { g_return_if_fail(ps_file->out == nullptr); ps_file->paper_width = width; ps_file->paper_height = height; } /** * poppler_ps_file_set_duplex: * @ps_file: a PopplerPSFile which was not yet printed to * @duplex: whether to force duplex printing (on printers which support this) * * Enable or disable Duplex printing. * **/ void poppler_ps_file_set_duplex(PopplerPSFile *ps_file, gboolean duplex) { g_return_if_fail(ps_file->out == nullptr); ps_file->duplex = duplex; } /** * poppler_ps_file_free: * @ps_file: a PopplerPSFile * * Frees @ps_file * **/ void poppler_ps_file_free(PopplerPSFile *ps_file) { g_return_if_fail(ps_file != nullptr); g_object_unref(ps_file); } /** * poppler_document_get_form_field: * @document: a #PopplerDocument * @id: an id of a #PopplerFormField * * Returns the #PopplerFormField for the given @id. It must be freed with * g_object_unref() * * Return value: (transfer full): a new #PopplerFormField or %NULL if * not found **/ PopplerFormField *poppler_document_get_form_field(PopplerDocument *document, gint id) { Page *page; unsigned pageNum; unsigned fieldNum; FormWidget *field; FormWidget::decodeID(id, &pageNum, &fieldNum); page = document->doc->getPage(pageNum); if (!page) { return nullptr; } const std::unique_ptr widgets = page->getFormWidgets(); if (!widgets) { return nullptr; } field = widgets->getWidget(fieldNum); if (field) { return _poppler_form_field_new(document, field); } return nullptr; } gboolean _poppler_convert_pdf_date_to_gtime(const GooString *date, time_t *gdate) { gchar *date_string; gboolean retval; if (date->hasUnicodeMarker()) { 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()); } retval = poppler_date_parse(date_string, gdate); g_free(date_string); return retval; } /** * _poppler_convert_pdf_date_to_date_time: * @date: a PDF date * * Converts the PDF date in @date to a #GDateTime. * * Returns: The converted date, or %NULL on error. **/ GDateTime *_poppler_convert_pdf_date_to_date_time(const GooString *date) { GDateTime *date_time = nullptr; GTimeZone *time_zone = nullptr; int year, mon, day, hour, min, sec, tzHours, tzMins; char tz; if (parseDateString(date, &year, &mon, &day, &hour, &min, &sec, &tz, &tzHours, &tzMins)) { if (tz == '+' || tz == '-') { 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(); } else { g_warning("unexpected tz val '%c'", tz); time_zone = g_time_zone_new_utc(); } date_time = g_date_time_new(time_zone, year, mon, day, hour, min, sec); g_time_zone_unref(time_zone); } return date_time; } /** * _poppler_convert_date_time_to_pdf_date: * @datetime: a #GDateTime * * Converts a #GDateTime to a PDF date. * * Returns: The converted date **/ GooString *_poppler_convert_date_time_to_pdf_date(GDateTime *datetime) { int offset_min; gchar *date_str; std::unique_ptr 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"); if (offset_min == 0) { out_str = GooString::format("{0:s}Z", date_str); } else { char tz = offset_min > 0 ? '+' : '-'; out_str = GooString::format("{0:s}{1:c}{2:02d}'{3:02d}'", date_str, tz, offset_min / 60, offset_min % 60); } g_free(date_str); return out_str.release(); } 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))); const auto field_partial_name = new GooString(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) : "", 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), std::make_unique(font_color->red, font_color->green, font_color->blue), poppler_signing_data_get_border_width(signing_data), std::make_unique(border_color->red, border_color->green, border_color->blue), std::make_unique(background_color->red, background_color->green, background_color->blue), reason.get(), location.get(), poppler_signing_data_get_image_path(signing_data) ? poppler_signing_data_get_image_path(signing_data) : "", owner_pwd, user_pwd); 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); } poppler-24.02.0/glib/poppler-document.h000066400000000000000000000530271455701731300177400ustar00rootroot00000000000000/* poppler-document.h: glib interface to poppler * Copyright (C) 2004, Red Hat, Inc. * * Copyright (C) 2016 Jakub Alba * Copyright (C) 2018, 2019, 2021, 2022 Marek Kasik * Copyright (C) 2019 Masamichi Hosoda * Copyright (C) 2021 André Guerreiro * * 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_DOCUMENT_H__ #define __POPPLER_DOCUMENT_H__ #include #include #include "poppler.h" G_BEGIN_DECLS #define POPPLER_TYPE_DOCUMENT (poppler_document_get_type()) #define POPPLER_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), POPPLER_TYPE_DOCUMENT, PopplerDocument)) #define POPPLER_IS_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), POPPLER_TYPE_DOCUMENT)) /** * PopplerPageLayout: * @POPPLER_PAGE_LAYOUT_UNSET: no specific layout set * @POPPLER_PAGE_LAYOUT_SINGLE_PAGE: one page at a time * @POPPLER_PAGE_LAYOUT_ONE_COLUMN: pages in one column * @POPPLER_PAGE_LAYOUT_TWO_COLUMN_LEFT: pages in two columns with odd numbered pages on the left * @POPPLER_PAGE_LAYOUT_TWO_COLUMN_RIGHT: pages in two columns with odd numbered pages on the right * @POPPLER_PAGE_LAYOUT_TWO_PAGE_LEFT: two pages at a time with odd numbered pages on the left * @POPPLER_PAGE_LAYOUT_TWO_PAGE_RIGHT: two pages at a time with odd numbered pages on the right * * Page layout types */ typedef enum { POPPLER_PAGE_LAYOUT_UNSET, POPPLER_PAGE_LAYOUT_SINGLE_PAGE, POPPLER_PAGE_LAYOUT_ONE_COLUMN, POPPLER_PAGE_LAYOUT_TWO_COLUMN_LEFT, POPPLER_PAGE_LAYOUT_TWO_COLUMN_RIGHT, POPPLER_PAGE_LAYOUT_TWO_PAGE_LEFT, POPPLER_PAGE_LAYOUT_TWO_PAGE_RIGHT } PopplerPageLayout; /** * PopplerPageMode: * @POPPLER_PAGE_MODE_UNSET: no specific mode set * @POPPLER_PAGE_MODE_NONE: neither document outline nor thumbnails visible * @POPPLER_PAGE_MODE_USE_OUTLINES: document outline visible * @POPPLER_PAGE_MODE_USE_THUMBS: thumbnails visible * @POPPLER_PAGE_MODE_FULL_SCREEN: full-screen mode * @POPPLER_PAGE_MODE_USE_OC: layers panel visible * @POPPLER_PAGE_MODE_USE_ATTACHMENTS: attachments panel visible * * Page modes */ typedef enum { POPPLER_PAGE_MODE_UNSET, POPPLER_PAGE_MODE_NONE, POPPLER_PAGE_MODE_USE_OUTLINES, POPPLER_PAGE_MODE_USE_THUMBS, POPPLER_PAGE_MODE_FULL_SCREEN, POPPLER_PAGE_MODE_USE_OC, POPPLER_PAGE_MODE_USE_ATTACHMENTS } PopplerPageMode; /** * PopplerFontType: * @POPPLER_FONT_TYPE_UNKNOWN: unknown font type * @POPPLER_FONT_TYPE_TYPE1: Type 1 font type * @POPPLER_FONT_TYPE_TYPE1C: Type 1 font type embedded in Compact Font Format (CFF) font program * @POPPLER_FONT_TYPE_TYPE1COT: Type 1 font type embedded in OpenType font program * @POPPLER_FONT_TYPE_TYPE3: A font type that is defined with PDF graphics operators * @POPPLER_FONT_TYPE_TRUETYPE: TrueType font type * @POPPLER_FONT_TYPE_TRUETYPEOT: TrueType font type embedded in OpenType font program * @POPPLER_FONT_TYPE_CID_TYPE0: CIDFont type based on Type 1 font technology * @POPPLER_FONT_TYPE_CID_TYPE0C: CIDFont type based on Type 1 font technology embedded in CFF font program * @POPPLER_FONT_TYPE_CID_TYPE0COT: CIDFont type based on Type 1 font technology embedded in OpenType font program * @POPPLER_FONT_TYPE_CID_TYPE2: CIDFont type based on TrueType font technology * @POPPLER_FONT_TYPE_CID_TYPE2OT: CIDFont type based on TrueType font technology embedded in OpenType font program * * Font types */ typedef enum { POPPLER_FONT_TYPE_UNKNOWN, POPPLER_FONT_TYPE_TYPE1, POPPLER_FONT_TYPE_TYPE1C, POPPLER_FONT_TYPE_TYPE1COT, POPPLER_FONT_TYPE_TYPE3, POPPLER_FONT_TYPE_TRUETYPE, POPPLER_FONT_TYPE_TRUETYPEOT, POPPLER_FONT_TYPE_CID_TYPE0, POPPLER_FONT_TYPE_CID_TYPE0C, POPPLER_FONT_TYPE_CID_TYPE0COT, POPPLER_FONT_TYPE_CID_TYPE2, POPPLER_FONT_TYPE_CID_TYPE2OT } PopplerFontType; /** * PopplerViewerPreferences: * @POPPLER_VIEWER_PREFERENCES_UNSET: no preferences set * @POPPLER_VIEWER_PREFERENCES_HIDE_TOOLBAR: hider toolbars when document is active * @POPPLER_VIEWER_PREFERENCES_HIDE_MENUBAR: hide menu bar when document is active * @POPPLER_VIEWER_PREFERENCES_HIDE_WINDOWUI: hide UI elements in document's window * @POPPLER_VIEWER_PREFERENCES_FIT_WINDOW: resize document's window to fit the size of the first displayed page * @POPPLER_VIEWER_PREFERENCES_CENTER_WINDOW: position the document's window in the center of the screen * @POPPLER_VIEWER_PREFERENCES_DISPLAY_DOC_TITLE: display document title in window's title bar * @POPPLER_VIEWER_PREFERENCES_DIRECTION_RTL: the predominant reading order for text is right to left * * Viewer preferences */ typedef enum /*< flags >*/ { POPPLER_VIEWER_PREFERENCES_UNSET = 0, POPPLER_VIEWER_PREFERENCES_HIDE_TOOLBAR = 1 << 0, POPPLER_VIEWER_PREFERENCES_HIDE_MENUBAR = 1 << 1, POPPLER_VIEWER_PREFERENCES_HIDE_WINDOWUI = 1 << 2, POPPLER_VIEWER_PREFERENCES_FIT_WINDOW = 1 << 3, POPPLER_VIEWER_PREFERENCES_CENTER_WINDOW = 1 << 4, POPPLER_VIEWER_PREFERENCES_DISPLAY_DOC_TITLE = 1 << 5, POPPLER_VIEWER_PREFERENCES_DIRECTION_RTL = 1 << 6 } PopplerViewerPreferences; /** * PopplerPrintScaling: * @POPPLER_PRINT_SCALING_APP_DEFAULT: application's default page scaling * @POPPLER_PRINT_SCALING_NONE: no page scaling * * PrintScaling viewer preference * * Since: 0.73 */ typedef enum { POPPLER_PRINT_SCALING_APP_DEFAULT, POPPLER_PRINT_SCALING_NONE } PopplerPrintScaling; /** * PopplerPrintDuplex: * @POPPLER_PRINT_DUPLEX_NONE: No preference on duplex printing * @POPPLER_PRINT_DUPLEX_SIMPLEX: Print single-sided * @POPPLER_PRINT_DUPLEX_DUPLEX_FLIP_SHORT_EDGE: Duplex and flip on the short edge of the sheet * @POPPLER_PRINT_DUPLEX_DUPLEX_FLIP_LONG_EDGE: Duplex and flip on the long edge of the sheet * * Duplex viewer preference * * Since: 0.80 */ typedef enum { POPPLER_PRINT_DUPLEX_NONE, POPPLER_PRINT_DUPLEX_SIMPLEX, POPPLER_PRINT_DUPLEX_DUPLEX_FLIP_SHORT_EDGE, POPPLER_PRINT_DUPLEX_DUPLEX_FLIP_LONG_EDGE } PopplerPrintDuplex; /** * PopplerPermissions: * @POPPLER_PERMISSIONS_OK_TO_PRINT: document can be printer * @POPPLER_PERMISSIONS_OK_TO_MODIFY: document contents can be modified * @POPPLER_PERMISSIONS_OK_TO_COPY: document can be copied * @POPPLER_PERMISSIONS_OK_TO_ADD_NOTES: annotations can added to the document * @POPPLER_PERMISSIONS_OK_TO_FILL_FORM: interactive form fields can be filled in * @POPPLER_PERMISSIONS_OK_TO_EXTRACT_CONTENTS: extract text and graphics * (in support of accessibility to users with disabilities or for other purposes). Since 0.18 * @POPPLER_PERMISSIONS_OK_TO_ASSEMBLE: assemble the document (insert, rotate, or delete pages and create * bookmarks or thumbnail images). Since 0.18 * @POPPLER_PERMISSIONS_OK_TO_PRINT_HIGH_RESOLUTION: document can be printer at high resolution. Since 0.18 * @POPPLER_PERMISSIONS_FULL: document permits all operations * * Permissions */ /* clang-format off */ typedef enum /*< flags >*/ { POPPLER_PERMISSIONS_OK_TO_PRINT = 1 << 0, POPPLER_PERMISSIONS_OK_TO_MODIFY = 1 << 1, POPPLER_PERMISSIONS_OK_TO_COPY = 1 << 2, POPPLER_PERMISSIONS_OK_TO_ADD_NOTES = 1 << 3, POPPLER_PERMISSIONS_OK_TO_FILL_FORM = 1 << 4, POPPLER_PERMISSIONS_OK_TO_EXTRACT_CONTENTS = 1 << 5, POPPLER_PERMISSIONS_OK_TO_ASSEMBLE = 1 << 6, POPPLER_PERMISSIONS_OK_TO_PRINT_HIGH_RESOLUTION = 1 << 7, POPPLER_PERMISSIONS_FULL = (POPPLER_PERMISSIONS_OK_TO_PRINT | POPPLER_PERMISSIONS_OK_TO_MODIFY | POPPLER_PERMISSIONS_OK_TO_COPY | POPPLER_PERMISSIONS_OK_TO_ADD_NOTES | POPPLER_PERMISSIONS_OK_TO_FILL_FORM | POPPLER_PERMISSIONS_OK_TO_EXTRACT_CONTENTS | POPPLER_PERMISSIONS_OK_TO_ASSEMBLE | POPPLER_PERMISSIONS_OK_TO_PRINT_HIGH_RESOLUTION) } PopplerPermissions; /* clang-format on */ /** * PopplerPDFSubtype: * @POPPLER_PDF_SUBTYPE_UNSET: Null * @POPPLER_PDF_SUBTYPE_PDF_A: ISO 19005 - Document management -- Electronic document file format for long-term preservation (PDF/A) * @POPPLER_PDF_SUBTYPE_PDF_E: ISO 24517 - Document management -- Engineering document format using PDF (PDF/E) * @POPPLER_PDF_SUBTYPE_PDF_UA: ISO 14289 - Document management applications -- Electronic document file format enhancement for accessibility (PDF/UA) * @POPPLER_PDF_SUBTYPE_PDF_VT: ISO 16612 - Graphic technology -- Variable data exchange (PDF/VT) * @POPPLER_PDF_SUBTYPE_PDF_X: ISO 15930 - Graphic technology -- Prepress digital data exchange (PDF/X) * @POPPLER_PDF_SUBTYPE_NONE: Not compliant with the above standards * * PDF Subtype * * Since: 0.70 */ typedef enum { POPPLER_PDF_SUBTYPE_UNSET, POPPLER_PDF_SUBTYPE_PDF_A, POPPLER_PDF_SUBTYPE_PDF_E, POPPLER_PDF_SUBTYPE_PDF_UA, POPPLER_PDF_SUBTYPE_PDF_VT, POPPLER_PDF_SUBTYPE_PDF_X, POPPLER_PDF_SUBTYPE_NONE } PopplerPDFSubtype; /** * PopplerPDFPart: * @POPPLER_PDF_SUBTYPE_PART_UNSET: Null * @POPPLER_PDF_SUBTYPE_PART_1: 1 * @POPPLER_PDF_SUBTYPE_PART_2: 2 * @POPPLER_PDF_SUBTYPE_PART_3: 3 * @POPPLER_PDF_SUBTYPE_PART_4: 4 * @POPPLER_PDF_SUBTYPE_PART_5: 5 * @POPPLER_PDF_SUBTYPE_PART_6: 6 * @POPPLER_PDF_SUBTYPE_PART_7: 7 * @POPPLER_PDF_SUBTYPE_PART_8: 8 * @POPPLER_PDF_SUBTYPE_PART_NONE: No part available * * PDF Subtype Part * * Since: 0.70 */ typedef enum { POPPLER_PDF_SUBTYPE_PART_UNSET, POPPLER_PDF_SUBTYPE_PART_1, POPPLER_PDF_SUBTYPE_PART_2, POPPLER_PDF_SUBTYPE_PART_3, POPPLER_PDF_SUBTYPE_PART_4, POPPLER_PDF_SUBTYPE_PART_5, POPPLER_PDF_SUBTYPE_PART_6, POPPLER_PDF_SUBTYPE_PART_7, POPPLER_PDF_SUBTYPE_PART_8, POPPLER_PDF_SUBTYPE_PART_NONE } PopplerPDFPart; /** * PopplerPDFConformance: * @POPPLER_PDF_SUBTYPE_CONF_UNSET: Null * @POPPLER_PDF_SUBTYPE_CONF_A: Level A (accessible) conformance (PDF/A) * @POPPLER_PDF_SUBTYPE_CONF_B: Level B (basic) conformance (PDF/A) * @POPPLER_PDF_SUBTYPE_CONF_G: Level G (external graphical content) (PDF/X) * @POPPLER_PDF_SUBTYPE_CONF_N: Level N (external ICC Profile) (PDF/X) * @POPPLER_PDF_SUBTYPE_CONF_P: Level P (ICC Profile) (PDF/X) * @POPPLER_PDF_SUBTYPE_CONF_PG: Level PG (conjunction of P and G) (PDF/X) * @POPPLER_PDF_SUBTYPE_CONF_U: Level U (Unicode) conformance (PDF/A) * @POPPLER_PDF_SUBTYPE_CONF_NONE: No conformance level available * * PDF Subtype Conformance * * Since: 0.70 */ typedef enum { POPPLER_PDF_SUBTYPE_CONF_UNSET, POPPLER_PDF_SUBTYPE_CONF_A, POPPLER_PDF_SUBTYPE_CONF_B, POPPLER_PDF_SUBTYPE_CONF_G, POPPLER_PDF_SUBTYPE_CONF_N, POPPLER_PDF_SUBTYPE_CONF_P, POPPLER_PDF_SUBTYPE_CONF_PG, POPPLER_PDF_SUBTYPE_CONF_U, POPPLER_PDF_SUBTYPE_CONF_NONE } PopplerPDFConformance; POPPLER_PUBLIC GType poppler_document_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerDocument *poppler_document_new_from_file(const char *uri, const char *password, GError **error); POPPLER_PUBLIC PopplerDocument *poppler_document_new_from_data(char *data, int length, const char *password, GError **error) G_GNUC_DEPRECATED_FOR(poppler_document_new_from_bytes); POPPLER_PUBLIC PopplerDocument *poppler_document_new_from_bytes(GBytes *bytes, const char *password, GError **error); POPPLER_PUBLIC PopplerDocument *poppler_document_new_from_stream(GInputStream *stream, goffset length, const char *password, GCancellable *cancellable, GError **error); POPPLER_PUBLIC PopplerDocument *poppler_document_new_from_gfile(GFile *file, const char *password, GCancellable *cancellable, GError **error); #ifndef G_OS_WIN32 POPPLER_PUBLIC PopplerDocument *poppler_document_new_from_fd(int fd, const char *password, GError **error); #endif POPPLER_PUBLIC gboolean poppler_document_save(PopplerDocument *document, const char *uri, GError **error); POPPLER_PUBLIC gboolean poppler_document_save_a_copy(PopplerDocument *document, const char *uri, GError **error); #ifndef G_OS_WIN32 POPPLER_PUBLIC gboolean poppler_document_save_to_fd(PopplerDocument *document, int fd, gboolean include_changes, GError **error); #endif POPPLER_PUBLIC gboolean poppler_document_get_id(PopplerDocument *document, gchar **permanent_id, gchar **update_id); POPPLER_PUBLIC int poppler_document_get_n_pages(PopplerDocument *document); POPPLER_PUBLIC PopplerPage *poppler_document_get_page(PopplerDocument *document, int index); POPPLER_PUBLIC PopplerPage *poppler_document_get_page_by_label(PopplerDocument *document, const char *label); POPPLER_PUBLIC gchar *poppler_document_get_pdf_version_string(PopplerDocument *document); POPPLER_PUBLIC void poppler_document_get_pdf_version(PopplerDocument *document, guint *major_version, guint *minor_version); POPPLER_PUBLIC gchar *poppler_document_get_title(PopplerDocument *document); POPPLER_PUBLIC void poppler_document_set_title(PopplerDocument *document, const gchar *title); POPPLER_PUBLIC gchar *poppler_document_get_author(PopplerDocument *document); POPPLER_PUBLIC void poppler_document_set_author(PopplerDocument *document, const gchar *author); POPPLER_PUBLIC gchar *poppler_document_get_subject(PopplerDocument *document); POPPLER_PUBLIC void poppler_document_set_subject(PopplerDocument *document, const gchar *subject); POPPLER_PUBLIC gchar *poppler_document_get_keywords(PopplerDocument *document); POPPLER_PUBLIC void poppler_document_set_keywords(PopplerDocument *document, const gchar *keywords); POPPLER_PUBLIC gchar *poppler_document_get_creator(PopplerDocument *document); POPPLER_PUBLIC void poppler_document_set_creator(PopplerDocument *document, const gchar *creator); POPPLER_PUBLIC gchar *poppler_document_get_producer(PopplerDocument *document); POPPLER_PUBLIC void poppler_document_set_producer(PopplerDocument *document, const gchar *producer); POPPLER_PUBLIC time_t poppler_document_get_creation_date(PopplerDocument *document); POPPLER_PUBLIC void poppler_document_set_creation_date(PopplerDocument *document, time_t creation_date); POPPLER_PUBLIC GDateTime *poppler_document_get_creation_date_time(PopplerDocument *document); POPPLER_PUBLIC void poppler_document_set_creation_date_time(PopplerDocument *document, GDateTime *creation_datetime); POPPLER_PUBLIC time_t poppler_document_get_modification_date(PopplerDocument *document); POPPLER_PUBLIC void poppler_document_set_modification_date(PopplerDocument *document, time_t modification_date); POPPLER_PUBLIC GDateTime *poppler_document_get_modification_date_time(PopplerDocument *document); POPPLER_PUBLIC void poppler_document_set_modification_date_time(PopplerDocument *document, GDateTime *modification_datetime); POPPLER_PUBLIC gboolean poppler_document_is_linearized(PopplerDocument *document); POPPLER_PUBLIC PopplerPageLayout poppler_document_get_page_layout(PopplerDocument *document); POPPLER_PUBLIC PopplerPageMode poppler_document_get_page_mode(PopplerDocument *document); POPPLER_PUBLIC PopplerPermissions poppler_document_get_permissions(PopplerDocument *document); POPPLER_PUBLIC gchar *poppler_document_get_pdf_subtype_string(PopplerDocument *document); POPPLER_PUBLIC PopplerPDFSubtype poppler_document_get_pdf_subtype(PopplerDocument *document); POPPLER_PUBLIC PopplerPDFPart poppler_document_get_pdf_part(PopplerDocument *document); POPPLER_PUBLIC PopplerPDFConformance poppler_document_get_pdf_conformance(PopplerDocument *document); POPPLER_PUBLIC gchar *poppler_document_get_metadata(PopplerDocument *document); POPPLER_PUBLIC PopplerPrintScaling poppler_document_get_print_scaling(PopplerDocument *document); POPPLER_PUBLIC PopplerPrintDuplex poppler_document_get_print_duplex(PopplerDocument *document); POPPLER_PUBLIC gint poppler_document_get_print_n_copies(PopplerDocument *document); POPPLER_PUBLIC PopplerPageRange *poppler_document_get_print_page_ranges(PopplerDocument *document, int *n_ranges) G_GNUC_MALLOC; /* Attachments */ POPPLER_PUBLIC guint poppler_document_get_n_attachments(PopplerDocument *document); POPPLER_PUBLIC gboolean poppler_document_has_attachments(PopplerDocument *document); POPPLER_PUBLIC GList *poppler_document_get_attachments(PopplerDocument *document); /* Links */ POPPLER_PUBLIC PopplerDest *poppler_document_find_dest(PopplerDocument *document, const gchar *link_name); POPPLER_PUBLIC GTree *poppler_document_create_dests_tree(PopplerDocument *document); /* Form */ POPPLER_PUBLIC PopplerFormField *poppler_document_get_form_field(PopplerDocument *document, gint id); POPPLER_PUBLIC void poppler_document_reset_form(PopplerDocument *document, GList *fields, gboolean exclude_fields); /* Javascript */ POPPLER_PUBLIC gboolean poppler_document_has_javascript(PopplerDocument *document); /* Signatures */ POPPLER_PUBLIC 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; POPPLER_PUBLIC PopplerIndexIter *poppler_index_iter_new(PopplerDocument *document); POPPLER_PUBLIC PopplerIndexIter *poppler_index_iter_copy(PopplerIndexIter *iter); POPPLER_PUBLIC void poppler_index_iter_free(PopplerIndexIter *iter); POPPLER_PUBLIC PopplerIndexIter *poppler_index_iter_get_child(PopplerIndexIter *parent); POPPLER_PUBLIC gboolean poppler_index_iter_is_open(PopplerIndexIter *iter); POPPLER_PUBLIC 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)) POPPLER_PUBLIC GType poppler_font_info_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerFontInfo *poppler_font_info_new(PopplerDocument *document); POPPLER_PUBLIC gboolean poppler_font_info_scan(PopplerFontInfo *font_info, int n_pages, PopplerFontsIter **iter); POPPLER_PUBLIC void poppler_font_info_free(PopplerFontInfo *font_info); #define POPPLER_TYPE_FONTS_ITER (poppler_fonts_iter_get_type()) POPPLER_PUBLIC GType poppler_fonts_iter_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerFontsIter *poppler_fonts_iter_copy(PopplerFontsIter *iter); POPPLER_PUBLIC void poppler_fonts_iter_free(PopplerFontsIter *iter); POPPLER_PUBLIC const char *poppler_fonts_iter_get_name(PopplerFontsIter *iter); POPPLER_PUBLIC const char *poppler_fonts_iter_get_full_name(PopplerFontsIter *iter); POPPLER_PUBLIC const char *poppler_fonts_iter_get_substitute_name(PopplerFontsIter *iter); POPPLER_PUBLIC const char *poppler_fonts_iter_get_file_name(PopplerFontsIter *iter); POPPLER_PUBLIC PopplerFontType poppler_fonts_iter_get_font_type(PopplerFontsIter *iter); POPPLER_PUBLIC const char *poppler_fonts_iter_get_encoding(PopplerFontsIter *iter); POPPLER_PUBLIC gboolean poppler_fonts_iter_is_embedded(PopplerFontsIter *iter); POPPLER_PUBLIC 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; POPPLER_PUBLIC PopplerLayersIter *poppler_layers_iter_new(PopplerDocument *document); POPPLER_PUBLIC PopplerLayersIter *poppler_layers_iter_copy(PopplerLayersIter *iter); POPPLER_PUBLIC void poppler_layers_iter_free(PopplerLayersIter *iter); POPPLER_PUBLIC PopplerLayersIter *poppler_layers_iter_get_child(PopplerLayersIter *parent); POPPLER_PUBLIC gchar *poppler_layers_iter_get_title(PopplerLayersIter *iter); POPPLER_PUBLIC 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)) POPPLER_PUBLIC GType poppler_ps_file_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerPSFile *poppler_ps_file_new(PopplerDocument *document, const char *filename, int first_page, int n_pages); #ifndef G_OS_WIN32 POPPLER_PUBLIC PopplerPSFile *poppler_ps_file_new_fd(PopplerDocument *document, int fd, int first_page, int n_pages); #endif POPPLER_PUBLIC void poppler_ps_file_set_paper_size(PopplerPSFile *ps_file, double width, double height); POPPLER_PUBLIC 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 * @end_page: last page in the range of pages * * A #PopplerPageRange is used to specify a range of pages. * * Since: 0.80 */ struct _PopplerPageRange { gint start_page; gint end_page; }; G_END_DECLS #endif /* __POPPLER_DOCUMENT_H__ */ poppler-24.02.0/glib/poppler-enums.c.template000066400000000000000000000016101455701731300210450ustar00rootroot00000000000000/*** BEGIN file-header ***/ #include #include "poppler-enums.h" /*** END file-header ***/ /*** BEGIN file-production ***/ /* enumerations from "@filename@" */ #include "@filename@" /*** END file-production ***/ /*** BEGIN value-header ***/ GType @enum_name@_get_type (void) { static volatile gsize g_define_type_id = 0; if (g_once_init_enter (&g_define_type_id)) { static const G@Type@Value values[] = { /*** END value-header ***/ /*** BEGIN value-production ***/ { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, /*** END value-production ***/ /*** BEGIN value-tail ***/ { 0, NULL, NULL } }; GType type = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values); g_once_init_leave (&g_define_type_id, type); } return g_define_type_id; } /*** END value-tail ***/ /*** BEGIN file-tail ***/ /*** END file-tail ***/ poppler-24.02.0/glib/poppler-enums.h.template000066400000000000000000000010341455701731300210520ustar00rootroot00000000000000/*** BEGIN file-header ***/ #ifndef POPPLER_ENUMS_H #define POPPLER_ENUMS_H #include #include "poppler.h" G_BEGIN_DECLS /*** END file-header ***/ /*** BEGIN file-production ***/ /* enumerations from "@filename@" */ /*** END file-production ***/ /*** BEGIN value-header ***/ POPPLER_PUBLIC GType @enum_name@_get_type (void) G_GNUC_CONST; #define POPPLER_TYPE_@ENUMSHORT@ (@enum_name@_get_type ()) /*** END value-header ***/ /*** BEGIN file-tail ***/ G_END_DECLS #endif /* !POPPLER_ENUMS_H */ /*** END file-tail ***/ poppler-24.02.0/glib/poppler-features.h.cmake000066400000000000000000000052761455701731300210220ustar00rootroot00000000000000/* poppler-features.h: glib interface to poppler * Copyright (C) 2006, Red Hat, Inc. * * 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_FEATURES_H__ #define __POPPLER_FEATURES_H__ /** * SECTION:poppler-features * @short_description: Variables and functions to check the poppler version and features * @Title: Version and Features Information * * Poppler provides version information, and information about features * enabled at compile time. This is primarily useful in configure checks * for builds that have a configure script, or for allowing code to optionally * depend but not require a specific poppler version. */ /** * POPPLER_HAS_CAIRO: * * Defined if poppler was compiled with cairo support. */ @CAIRO_FEATURE@ /** * POPPLER_MAJOR_VERSION: * * The major version number of the poppler header files (e.g. in poppler version * 0.1.2 this is 0.) * * Since: 0.12 */ #define POPPLER_MAJOR_VERSION (@POPPLER_MAJOR_VERSION@) /** * POPPLER_MINOR_VERSION: * * The major version number of the poppler header files (e.g. in poppler version * 0.1.2 this is 1.) * * Since: 0.12 */ #define POPPLER_MINOR_VERSION (@POPPLER_MINOR_VERSION@) /** * POPPLER_MICRO_VERSION: * * The micro version number of the poppler header files (e.g. in poppler version * 0.1.2 this is 2.) * * Since: 0.12 */ #define POPPLER_MICRO_VERSION (@POPPLER_MICRO_VERSION@) /** * POPPLER_CHECK_VERSION: * @major: major version (e.g. 0 for version 0.1.2) * @minor: minor version (e.g. 1 for version 0.1.2) * @micro: micro version (e.g. 2 for version 0.1.2) * * Checks the version fo the poppler library * * Returns: %TRUE if the version of the poppler header files is the same * as or newer than the passed-in version * * Since: 0.12 */ #define POPPLER_CHECK_VERSION(major,minor,micro) \ (POPPLER_MAJOR_VERSION > (major) || \ (POPPLER_MAJOR_VERSION == (major) && POPPLER_MINOR_VERSION > (minor)) || \ (POPPLER_MAJOR_VERSION == (major) && POPPLER_MINOR_VERSION == (minor) && POPPLER_MICRO_VERSION >= (micro))) #endif /* __POPPLER_FEATURES_H__ */ poppler-24.02.0/glib/poppler-form-field.cc000066400000000000000000002100431455701731300202750ustar00rootroot00000000000000/* poppler-form-field.cc: glib interface to poppler * * Copyright (C) 2007 Carlos Garcia Campos * Copyright (C) 2006 Julien Rebetez * Copyright (C) 2020 Oliver Sander * Copyright (C) 2021 André Guerreiro * Copyright (C) 2021, 2023 Marek Kasik * Copyright (C) 2023 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 * 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 "poppler.h" #include "poppler-private.h" #include #ifdef ENABLE_NSS3 # include #endif #include /** * SECTION:poppler-form-field * @short_description: Form Field * @title: PopplerFormField */ typedef struct _PopplerFormFieldClass PopplerFormFieldClass; struct _PopplerFormFieldClass { GObjectClass parent_class; }; G_DEFINE_TYPE(PopplerFormField, poppler_form_field, G_TYPE_OBJECT) static void poppler_form_field_finalize(GObject *object) { PopplerFormField *field = POPPLER_FORM_FIELD(object); if (field->document) { g_object_unref(field->document); field->document = nullptr; } if (field->action) { poppler_action_free(field->action); field->action = nullptr; } field->widget = nullptr; G_OBJECT_CLASS(poppler_form_field_parent_class)->finalize(object); } static void poppler_form_field_init(PopplerFormField *field) { } static void poppler_form_field_class_init(PopplerFormFieldClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS(klass); gobject_class->finalize = poppler_form_field_finalize; } PopplerFormField *_poppler_form_field_new(PopplerDocument *document, FormWidget *field) { PopplerFormField *poppler_field; g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL); g_return_val_if_fail(field != nullptr, NULL); poppler_field = POPPLER_FORM_FIELD(g_object_new(POPPLER_TYPE_FORM_FIELD, nullptr)); poppler_field->document = (PopplerDocument *)g_object_ref(document); poppler_field->widget = field; return poppler_field; } /* Public methods */ /** * poppler_form_field_get_field_type: * @field: a #PopplerFormField * * Gets the type of @field * * Return value: #PopplerFormFieldType of @field **/ PopplerFormFieldType poppler_form_field_get_field_type(PopplerFormField *field) { g_return_val_if_fail(POPPLER_IS_FORM_FIELD(field), POPPLER_FORM_FIELD_UNKNOWN); switch (field->widget->getType()) { case formButton: return POPPLER_FORM_FIELD_BUTTON; case formText: return POPPLER_FORM_FIELD_TEXT; case formChoice: return POPPLER_FORM_FIELD_CHOICE; case formSignature: return POPPLER_FORM_FIELD_SIGNATURE; default: g_warning("Unsupported Form Field Type"); } return POPPLER_FORM_FIELD_UNKNOWN; } /** * poppler_form_field_get_id: * @field: a #PopplerFormField * * Gets the id of @field * * Return value: the id of @field **/ gint poppler_form_field_get_id(PopplerFormField *field) { g_return_val_if_fail(POPPLER_IS_FORM_FIELD(field), -1); return field->widget->getID(); } /** * poppler_form_field_get_font_size: * @field: a #PopplerFormField * * 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 * * Return value: the font size of @field **/ gdouble poppler_form_field_get_font_size(PopplerFormField *field) { g_return_val_if_fail(POPPLER_IS_FORM_FIELD(field), 0); return 0; } /** * poppler_form_field_is_read_only: * @field: a #PopplerFormField * * Checks whether @field is read only * * Return value: %TRUE if @field is read only **/ gboolean poppler_form_field_is_read_only(PopplerFormField *field) { g_return_val_if_fail(POPPLER_IS_FORM_FIELD(field), FALSE); return field->widget->isReadOnly(); } /** * poppler_form_field_get_action: * @field: a #PopplerFormField * * Retrieves the action (#PopplerAction) that shall be * performed when @field is activated, or %NULL * * Return value: (transfer none): the action to perform. The returned * object is owned by @field and should not be freed * * Since: 0.18 */ PopplerAction *poppler_form_field_get_action(PopplerFormField *field) { LinkAction *action; if (field->action) { return field->action; } action = field->widget->getActivationAction(); if (!action) { return nullptr; } field->action = _poppler_action_new(field->document, action, nullptr); return field->action; } /** * poppler_form_field_get_additional_action: * @field: a #PopplerFormField * @type: the type of additional action * * Retrieves the action (#PopplerAction) that shall be performed when * an additional action is triggered on @field, or %NULL. * * Return value: (transfer none): the action to perform. The returned * object is owned by @field and should not be freed. * * * Since: 0.72 */ PopplerAction *poppler_form_field_get_additional_action(PopplerFormField *field, PopplerAdditionalActionType type) { Annot::FormAdditionalActionsType form_action; PopplerAction **action; switch (type) { case POPPLER_ADDITIONAL_ACTION_FIELD_MODIFIED: form_action = Annot::actionFieldModified; action = &field->field_modified_action; break; case POPPLER_ADDITIONAL_ACTION_FORMAT_FIELD: form_action = Annot::actionFormatField; action = &field->format_field_action; break; case POPPLER_ADDITIONAL_ACTION_VALIDATE_FIELD: form_action = Annot::actionValidateField; action = &field->validate_field_action; break; case POPPLER_ADDITIONAL_ACTION_CALCULATE_FIELD: form_action = Annot::actionCalculateField; action = &field->calculate_field_action; break; default: g_return_val_if_reached(nullptr); return nullptr; } if (*action) { return *action; } std::unique_ptr link_action = field->widget->getAdditionalAction(form_action); if (!link_action) { return nullptr; } *action = _poppler_action_new(nullptr, link_action.get(), nullptr); return *action; } /* Button Field */ /** * poppler_form_field_button_get_button_type: * @field: a #PopplerFormField * * Gets the button type of @field * * Return value: #PopplerFormButtonType of @field **/ PopplerFormButtonType poppler_form_field_button_get_button_type(PopplerFormField *field) { g_return_val_if_fail(field->widget->getType() == formButton, POPPLER_FORM_BUTTON_PUSH); switch (static_cast(field->widget)->getButtonType()) { case formButtonPush: return POPPLER_FORM_BUTTON_PUSH; case formButtonCheck: return POPPLER_FORM_BUTTON_CHECK; case formButtonRadio: return POPPLER_FORM_BUTTON_RADIO; default: g_assert_not_reached(); } } /** * poppler_form_field_button_get_state: * @field: a #PopplerFormField * * Queries a #PopplerFormField and returns its current state. Returns %TRUE if * @field is pressed in and %FALSE if it is raised. * * Return value: current state of @field **/ gboolean poppler_form_field_button_get_state(PopplerFormField *field) { g_return_val_if_fail(field->widget->getType() == formButton, FALSE); return static_cast(field->widget)->getState(); } /** * poppler_form_field_button_set_state: * @field: a #PopplerFormField * @state: %TRUE or %FALSE * * Sets the status of @field. Set to %TRUE if you want the #PopplerFormField * to be 'pressed in', and %FALSE to raise it. **/ void poppler_form_field_button_set_state(PopplerFormField *field, gboolean state) { g_return_if_fail(field->widget->getType() == formButton); static_cast(field->widget)->setState((bool)state); } /** * poppler_form_field_get_partial_name: * @field: a #PopplerFormField * * Gets the partial name of @field. * * Return value: a new allocated string. It must be freed with g_free() when done. * * Since: 0.16 **/ gchar *poppler_form_field_get_partial_name(PopplerFormField *field) { const GooString *tmp; g_return_val_if_fail(POPPLER_IS_FORM_FIELD(field), NULL); tmp = field->widget->getPartialName(); return tmp ? _poppler_goo_string_to_utf8(tmp) : nullptr; } /** * poppler_form_field_get_mapping_name: * @field: a #PopplerFormField * * Gets the mapping name of @field that is used when * exporting interactive form field data from the document * * Return value: a new allocated string. It must be freed with g_free() when done. * * Since: 0.16 **/ gchar *poppler_form_field_get_mapping_name(PopplerFormField *field) { const GooString *tmp; g_return_val_if_fail(POPPLER_IS_FORM_FIELD(field), NULL); tmp = field->widget->getMappingName(); return tmp ? _poppler_goo_string_to_utf8(tmp) : nullptr; } /** * poppler_form_field_get_name: * @field: a #PopplerFormField * * Gets the fully qualified name of @field. It's constructed by concatenating * the partial field names of the field and all of its ancestors. * * Return value: a new allocated string. It must be freed with g_free() when done. * * Since: 0.16 **/ 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(); return tmp ? _poppler_goo_string_to_utf8(tmp) : nullptr; } /** * poppler_form_field_get_alternate_ui_name: * @field: a #PopplerFormField * * 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). * * Return value: a new allocated string. It must be freed with g_free() when done. * * Since: 0.88 **/ gchar *poppler_form_field_get_alternate_ui_name(PopplerFormField *field) { const GooString *tmp; g_return_val_if_fail(POPPLER_IS_FORM_FIELD(field), NULL); tmp = field->widget->getAlternateUiName(); return tmp ? _poppler_goo_string_to_utf8(tmp) : nullptr; } /** * PopplerCertificateInfo: * * PopplerCertificateInfo contains detailed info about a signing certificate. * * 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: 21.12.0 **/ struct _PopplerSignatureInfo { PopplerSignatureStatus sig_status; 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) { 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"); return nullptr; } 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); poppler_sig_info = g_new0(PopplerSignatureInfo, 1); switch (sig_info->getSignatureValStatus()) { case SIGNATURE_VALID: poppler_sig_info->sig_status = POPPLER_SIGNATURE_VALID; break; case SIGNATURE_INVALID: poppler_sig_info->sig_status = POPPLER_SIGNATURE_INVALID; break; case SIGNATURE_DIGEST_MISMATCH: poppler_sig_info->sig_status = POPPLER_SIGNATURE_DIGEST_MISMATCH; break; case SIGNATURE_DECODING_ERROR: poppler_sig_info->sig_status = POPPLER_SIGNATURE_DECODING_ERROR; break; case SIGNATURE_GENERIC_ERROR: poppler_sig_info->sig_status = POPPLER_SIGNATURE_GENERIC_ERROR; break; case SIGNATURE_NOT_FOUND: poppler_sig_info->sig_status = POPPLER_SIGNATURE_NOT_FOUND; break; case SIGNATURE_NOT_VERIFIED: poppler_sig_info->sig_status = POPPLER_SIGNATURE_NOT_VERIFIED; break; } switch (sig_info->getCertificateValStatus()) { case CERTIFICATE_TRUSTED: poppler_sig_info->cert_status = POPPLER_CERTIFICATE_TRUSTED; break; case CERTIFICATE_UNTRUSTED_ISSUER: poppler_sig_info->cert_status = POPPLER_CERTIFICATE_UNTRUSTED_ISSUER; break; case CERTIFICATE_UNKNOWN_ISSUER: poppler_sig_info->cert_status = POPPLER_CERTIFICATE_UNKNOWN_ISSUER; break; case CERTIFICATE_REVOKED: poppler_sig_info->cert_status = POPPLER_CERTIFICATE_REVOKED; break; case CERTIFICATE_EXPIRED: poppler_sig_info->cert_status = POPPLER_CERTIFICATE_EXPIRED; break; case CERTIFICATE_GENERIC_ERROR: poppler_sig_info->cert_status = POPPLER_CERTIFICATE_GENERIC_ERROR; break; case CERTIFICATE_NOT_VERIFIED: poppler_sig_info->cert_status = POPPLER_CERTIFICATE_NOT_VERIFIED; break; } 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; } static void signature_validate_thread(GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable) { PopplerSignatureValidationFlags flags = (PopplerSignatureValidationFlags)GPOINTER_TO_INT(task_data); PopplerSignatureInfo *signature_info; PopplerFormField *field = (PopplerFormField *)source_object; GError *error = nullptr; signature_info = _poppler_form_field_signature_validate(field, flags, FALSE, &error); if (signature_info == nullptr && error != nullptr) { g_task_return_error(task, error); return; } if (g_task_set_return_on_cancel(task, FALSE)) { g_task_return_pointer(task, signature_info, (GDestroyNotify)poppler_signature_info_free); } } /** * poppler_form_field_signature_validate_sync: * @field: a #PopplerFormField that represents a signature annotation * @flags: #PopplerSignatureValidationFlags flags influencing process of validation of the field signature * @cancellable: (nullable): optional #GCancellable object * @error: a #GError * * Synchronously validates the cryptographic signature contained in @signature_field. * * Return value: (transfer full): a #PopplerSignatureInfo structure containing signature metadata and validation status * Free the returned structure with poppler_signature_info_free(). * * Since: 21.12.0 **/ PopplerSignatureInfo *poppler_form_field_signature_validate_sync(PopplerFormField *field, PopplerSignatureValidationFlags flags, GCancellable *cancellable, GError **error) { PopplerSignatureInfo *signature_info; GTask *task; g_return_val_if_fail(error == NULL || *error == NULL, NULL); task = g_task_new(field, cancellable, nullptr, nullptr); g_task_set_task_data(task, GINT_TO_POINTER(flags), nullptr); g_task_set_return_on_cancel(task, TRUE); g_task_run_in_thread_sync(task, signature_validate_thread); signature_info = (PopplerSignatureInfo *)g_task_propagate_pointer(task, error); g_object_unref(task); return signature_info; } /** * poppler_form_field_signature_validate_async: * @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 * * Asynchronously validates the cryptographic signature contained in @signature_field. * * Since: 21.12.0 **/ void poppler_form_field_signature_validate_async(PopplerFormField *field, PopplerSignatureValidationFlags flags, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new(field, cancellable, callback, user_data); g_task_set_task_data(task, GINT_TO_POINTER(flags), nullptr); g_task_set_return_on_cancel(task, TRUE); g_task_run_in_thread(task, signature_validate_thread); g_object_unref(task); } /** * poppler_form_field_signature_validate_finish: * @field: a #PopplerFormField that represents a signature annotation * @result: a #GAsyncResult * @error: a #GError * * Finishes validation of the cryptographic signature contained in @signature_field. * See poppler_form_field_signature_validate_async(). * * Return value: (transfer full): a #PopplerSignatureInfo structure containing signature metadata and validation status * Free the returned structure with poppler_signature_info_free(). * * Since: 21.12.0 **/ PopplerSignatureInfo *poppler_form_field_signature_validate_finish(PopplerFormField *field, GAsyncResult *result, GError **error) { g_return_val_if_fail(g_task_is_valid(result, field), NULL); return (PopplerSignatureInfo *)g_task_propagate_pointer(G_TASK(result), error); } G_DEFINE_BOXED_TYPE(PopplerSignatureInfo, poppler_signature_info, poppler_signature_info_copy, poppler_signature_info_free) /** * poppler_signature_info_copy: * @siginfo: a #PopplerSignatureInfo structure containing signature metadata and validation status * * Copies @siginfo, creating an identical #PopplerSignatureInfo. * * Return value: (transfer full): a new #PopplerSignatureInfo structure identical to @siginfo * * Since: 21.12.0 **/ PopplerSignatureInfo *poppler_signature_info_copy(const PopplerSignatureInfo *siginfo) { PopplerSignatureInfo *new_info; g_return_val_if_fail(siginfo != NULL, 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; } /** * poppler_signature_info_free: * @siginfo: a #PopplerSignatureInfo structure containing signature metadata and validation status * * Frees @siginfo * * Since: 21.12.0 **/ void poppler_signature_info_free(PopplerSignatureInfo *siginfo) { if (siginfo == nullptr) { return; } g_date_time_unref(siginfo->local_signing_time); g_free(siginfo->signer_name); poppler_certificate_info_free(siginfo->certificate_info); g_free(siginfo); } /** * poppler_signature_info_get_signature_status: * @siginfo: a #PopplerSignatureInfo * * Returns status of the signature for given PopplerSignatureInfo. * * Return value: signature status of the signature * * Since: 21.12.0 **/ PopplerSignatureStatus poppler_signature_info_get_signature_status(const PopplerSignatureInfo *siginfo) { g_return_val_if_fail(siginfo != NULL, 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 != NULL, NULL); return siginfo->certificate_info; } /** * poppler_signature_info_get_certificate_status: * @siginfo: a #PopplerSignatureInfo * * Returns status of the certificate for given PopplerSignatureInfo. * * Return value: certificate status of the signature * * Since: 21.12.0 **/ PopplerCertificateStatus poppler_signature_info_get_certificate_status(const PopplerSignatureInfo *siginfo) { g_return_val_if_fail(siginfo != NULL, POPPLER_CERTIFICATE_GENERIC_ERROR); return siginfo->cert_status; } /** * poppler_signature_info_get_signer_name: * @siginfo: a #PopplerSignatureInfo * * Returns name of signer for given PopplerSignatureInfo. * * Return value: (transfer none): A string. * * Since: 21.12.0 **/ const gchar *poppler_signature_info_get_signer_name(const PopplerSignatureInfo *siginfo) { g_return_val_if_fail(siginfo != NULL, NULL); return siginfo->signer_name; } /** * poppler_signature_info_get_local_signing_time: * @siginfo: a #PopplerSignatureInfo * * 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. * * Return value: (transfer none): GDateTime * * Since: 21.12.0 **/ GDateTime *poppler_signature_info_get_local_signing_time(const PopplerSignatureInfo *siginfo) { g_return_val_if_fail(siginfo != NULL, NULL); return siginfo->local_signing_time; } /* Text Field */ /** * poppler_form_field_text_get_text_type: * @field: a #PopplerFormField * * Gets the text type of @field. * * Return value: #PopplerFormTextType of @field **/ PopplerFormTextType poppler_form_field_text_get_text_type(PopplerFormField *field) { FormWidgetText *text_field; g_return_val_if_fail(field->widget->getType() == formText, POPPLER_FORM_TEXT_NORMAL); text_field = static_cast(field->widget); if (text_field->isMultiline()) { return POPPLER_FORM_TEXT_MULTILINE; } else if (text_field->isFileSelect()) { return POPPLER_FORM_TEXT_FILE_SELECT; } return POPPLER_FORM_TEXT_NORMAL; } /** * poppler_form_field_text_get_text: * @field: a #PopplerFormField * * Retrieves the contents of @field. * * Return value: a new allocated string. It must be freed with g_free() when done. **/ gchar *poppler_form_field_text_get_text(PopplerFormField *field) { FormWidgetText *text_field; const GooString *tmp; g_return_val_if_fail(field->widget->getType() == formText, NULL); text_field = static_cast(field->widget); tmp = text_field->getContent(); return tmp ? _poppler_goo_string_to_utf8(tmp) : nullptr; } /** * poppler_form_field_text_set_text: * @field: a #PopplerFormField * @text: the new text * * Sets the text in @field to the given value, replacing the current contents. **/ 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); g_free(tmp); static_cast(field->widget)->setContent(goo_tmp); delete goo_tmp; } /** * poppler_form_field_text_get_max_len: * @field: a #PopplerFormField * * Retrieves the maximum allowed length of the text in @field * * Return value: the maximum allowed number of characters in @field, or -1 if there is no maximum. **/ gint poppler_form_field_text_get_max_len(PopplerFormField *field) { g_return_val_if_fail(field->widget->getType() == formText, 0); return static_cast(field->widget)->getMaxLen(); } /** * poppler_form_field_text_do_spell_check: * @field: a #PopplerFormField * * Checks whether spell checking should be done for the contents of @field * * Return value: %TRUE if spell checking should be done for @field **/ gboolean poppler_form_field_text_do_spell_check(PopplerFormField *field) { g_return_val_if_fail(field->widget->getType() == formText, FALSE); return !static_cast(field->widget)->noSpellCheck(); } gboolean poppler_form_field_text_do_scroll(PopplerFormField *field) { g_return_val_if_fail(field->widget->getType() == formText, FALSE); return !static_cast(field->widget)->noScroll(); } /** * poppler_form_field_text_is_rich_text: * @field: a #PopplerFormField * * Checks whether the contents of @field are rich text * * Return value: %TRUE if the contents of @field are rich text **/ gboolean poppler_form_field_text_is_rich_text(PopplerFormField *field) { g_return_val_if_fail(field->widget->getType() == formText, FALSE); return static_cast(field->widget)->isRichText(); } /** * poppler_form_field_text_is_password: * @field: a #PopplerFormField * * Checks whether content of @field is a password and it must be hidden * * Return value: %TRUE if the content of @field is a password **/ gboolean poppler_form_field_text_is_password(PopplerFormField *field) { g_return_val_if_fail(field->widget->getType() == formText, FALSE); return static_cast(field->widget)->isPassword(); } /* Choice Field */ /** * poppler_form_field_choice_get_choice_type: * @field: a #PopplerFormField * * Gets the choice type of @field * * Return value: #PopplerFormChoiceType of @field **/ PopplerFormChoiceType poppler_form_field_choice_get_choice_type(PopplerFormField *field) { g_return_val_if_fail(field->widget->getType() == formChoice, POPPLER_FORM_CHOICE_COMBO); if (static_cast(field->widget)->isCombo()) { return POPPLER_FORM_CHOICE_COMBO; } else { return POPPLER_FORM_CHOICE_LIST; } } /** * poppler_form_field_choice_is_editable: * @field: a #PopplerFormField * * Checks whether @field is editable * * Return value: %TRUE if @field is editable **/ gboolean poppler_form_field_choice_is_editable(PopplerFormField *field) { g_return_val_if_fail(field->widget->getType() == formChoice, FALSE); return static_cast(field->widget)->hasEdit(); } /** * poppler_form_field_choice_can_select_multiple: * @field: a #PopplerFormField * * Checks whether @field allows multiple choices to be selected * * Return value: %TRUE if @field allows multiple choices to be selected **/ gboolean poppler_form_field_choice_can_select_multiple(PopplerFormField *field) { g_return_val_if_fail(field->widget->getType() == formChoice, FALSE); return static_cast(field->widget)->isMultiSelect(); } /** * poppler_form_field_choice_do_spell_check: * @field: a #PopplerFormField * * Checks whether spell checking should be done for the contents of @field * * Return value: %TRUE if spell checking should be done for @field **/ gboolean poppler_form_field_choice_do_spell_check(PopplerFormField *field) { g_return_val_if_fail(field->widget->getType() == formChoice, FALSE); return !static_cast(field->widget)->noSpellCheck(); } gboolean poppler_form_field_choice_commit_on_change(PopplerFormField *field) { g_return_val_if_fail(field->widget->getType() == formChoice, FALSE); return static_cast(field->widget)->commitOnSelChange(); } /** * poppler_form_field_choice_get_n_items: * @field: a #PopplerFormField * * Returns the number of items on @field * * Return value: the number of items on @field **/ gint poppler_form_field_choice_get_n_items(PopplerFormField *field) { g_return_val_if_fail(field->widget->getType() == formChoice, -1); return static_cast(field->widget)->getNumChoices(); } /** * poppler_form_field_choice_get_item: * @field: a #PopplerFormField * @index: the index of the item * * Returns the contents of the item on @field at the given index * * Return value: a new allocated string. It must be freed with g_free() when done. **/ gchar *poppler_form_field_choice_get_item(PopplerFormField *field, gint index) { const GooString *tmp; g_return_val_if_fail(field->widget->getType() == formChoice, NULL); g_return_val_if_fail(index >= 0 && index < poppler_form_field_choice_get_n_items(field), NULL); tmp = static_cast(field->widget)->getChoice(index); return tmp ? _poppler_goo_string_to_utf8(tmp) : nullptr; } /** * poppler_form_field_choice_is_item_selected: * @field: a #PopplerFormField * @index: the index of the item * * Checks whether the item at the given index on @field is currently selected * * Return value: %TRUE if item at @index is currently selected **/ gboolean poppler_form_field_choice_is_item_selected(PopplerFormField *field, gint index) { g_return_val_if_fail(field->widget->getType() == formChoice, FALSE); g_return_val_if_fail(index >= 0 && index < poppler_form_field_choice_get_n_items(field), FALSE); return static_cast(field->widget)->isSelected(index); } /** * poppler_form_field_choice_select_item: * @field: a #PopplerFormField * @index: the index of the item * * Selects the item at the given index on @field **/ void poppler_form_field_choice_select_item(PopplerFormField *field, gint index) { g_return_if_fail(field->widget->getType() == formChoice); g_return_if_fail(index >= 0 && index < poppler_form_field_choice_get_n_items(field)); static_cast(field->widget)->select(index); } /** * poppler_form_field_choice_unselect_all: * @field: a #PopplerFormField * * Unselects all the items on @field **/ void poppler_form_field_choice_unselect_all(PopplerFormField *field) { g_return_if_fail(field->widget->getType() == formChoice); static_cast(field->widget)->deselectAll(); } /** * poppler_form_field_choice_toggle_item: * @field: a #PopplerFormField * @index: the index of the item * * Changes the state of the item at the given index **/ void poppler_form_field_choice_toggle_item(PopplerFormField *field, gint index) { g_return_if_fail(field->widget->getType() == formChoice); g_return_if_fail(index >= 0 && index < poppler_form_field_choice_get_n_items(field)); static_cast(field->widget)->toggle(index); } /** * poppler_form_field_choice_set_text: * @field: a #PopplerFormField * @text: the new text * * Sets the text in @field to the given value, replacing the current contents **/ 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); g_free(tmp); static_cast(field->widget)->setEditChoice(goo_tmp); delete goo_tmp; } /** * poppler_form_field_choice_get_text: * @field: a #PopplerFormField * * Retrieves the contents of @field. * * Return value: a new allocated string. It must be freed with g_free() when done. **/ gchar *poppler_form_field_choice_get_text(PopplerFormField *field) { const GooString *tmp; g_return_val_if_fail(field->widget->getType() == formChoice, NULL); tmp = static_cast(field->widget)->getEditChoice(); return tmp ? _poppler_goo_string_to_utf8(tmp) : nullptr; } /* Signing Data */ 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 = 0xEF; background_color.green = 0xEF; background_color.blue = 0xEF; poppler_signing_data_set_background_color(data, &background_color); /* Red border color */ auto border_color = PopplerColor(); border_color.red = 0xFF; 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 = 0xFF; 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 } poppler-24.02.0/glib/poppler-form-field.h000066400000000000000000000412251455701731300201430ustar00rootroot00000000000000/* poppler-form-field.h: glib interface to poppler * * Copyright (C) 2007 Carlos Garcia Campos * Copyright (C) 2021 André Guerreiro * 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 * 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_FORM_FIELD_H__ #define __POPPLER_FORM_FIELD_H__ #include #include "poppler.h" G_BEGIN_DECLS #define POPPLER_TYPE_FORM_FIELD (poppler_form_field_get_type()) #define POPPLER_FORM_FIELD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), POPPLER_TYPE_FORM_FIELD, PopplerFormField)) #define POPPLER_IS_FORM_FIELD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), POPPLER_TYPE_FORM_FIELD)) /** * PopplerSignatureStatus * @POPPLER_SIGNATURE_VALID: signature is cryptographically valid * @POPPLER_SIGNATURE_INVALID: signature is cryptographically invalid * @POPPLER_SIGNATURE_DIGEST_MISMATCH: document content was changed after the signature was applied * @POPPLER_SIGNATURE_DECODING_ERROR: signature CMS/PKCS7 structure is malformed * @POPPLER_SIGNATURE_GENERIC_ERROR: failed to verify signature * @POPPLER_SIGNATURE_NOT_FOUND: requested signature is not present in the document * @POPPLER_SIGNATURE_NOT_VERIFIED: signature not yet verified * * Signature verification results * * Since: 21.12.0 */ typedef enum { POPPLER_SIGNATURE_VALID, POPPLER_SIGNATURE_INVALID, POPPLER_SIGNATURE_DIGEST_MISMATCH, POPPLER_SIGNATURE_DECODING_ERROR, POPPLER_SIGNATURE_GENERIC_ERROR, POPPLER_SIGNATURE_NOT_FOUND, POPPLER_SIGNATURE_NOT_VERIFIED } PopplerSignatureStatus; /** * PopplerCertificateStatus * @POPPLER_CERTIFICATE_TRUSTED: certificate is considered trusted * @POPPLER_CERTIFICATE_UNTRUSTED_ISSUER: the issuer of this certificate has been marked as untrusted by the user * @POPPLER_CERTIFICATE_UNKNOWN_ISSUER: this certificate trust chain has not finished in a trusted root certificate * @POPPLER_CERTIFICATE_REVOKED: certificate was revoked by the issuing certificate authority * @POPPLER_CERTIFICATE_EXPIRED: signing time is outside the validity bounds of this certificate * @POPPLER_CERTIFICATE_GENERIC_ERROR: failed to verify certificate * @POPPLER_CERTIFICATE_NOT_VERIFIED: certificate not yet verified * * Signature certificate verification results * * Since: 21.12.0 */ typedef enum { POPPLER_CERTIFICATE_TRUSTED, POPPLER_CERTIFICATE_UNTRUSTED_ISSUER, POPPLER_CERTIFICATE_UNKNOWN_ISSUER, POPPLER_CERTIFICATE_REVOKED, POPPLER_CERTIFICATE_EXPIRED, POPPLER_CERTIFICATE_GENERIC_ERROR, POPPLER_CERTIFICATE_NOT_VERIFIED } PopplerCertificateStatus; /** * PopplerSignatureValidationFlags * @POPPLER_SIGNATURE_VALIDATION_FLAG_VALIDATE_CERTIFICATE: Whether to validate also the certificate of the signature * @POPPLER_SIGNATURE_VALIDATION_FLAG_WITHOUT_OCSP_REVOCATION_CHECK: Whether to not do OCSP (Online Certificate Status Protocol) revocation check * @POPPLER_SIGNATURE_VALIDATION_FLAG_USE_AIA_CERTIFICATE_FETCH: Whether to use AIA (Authority Information Access) extension for certificate fetching * * Signature validation flags * * Since: 21.12.0 */ typedef enum /*< flags >*/ { POPPLER_SIGNATURE_VALIDATION_FLAG_VALIDATE_CERTIFICATE = 1 << 0, POPPLER_SIGNATURE_VALIDATION_FLAG_WITHOUT_OCSP_REVOCATION_CHECK = 1 << 1, POPPLER_SIGNATURE_VALIDATION_FLAG_USE_AIA_CERTIFICATE_FETCH = 1 << 2, } PopplerSignatureValidationFlags; typedef enum { POPPLER_FORM_FIELD_UNKNOWN, POPPLER_FORM_FIELD_BUTTON, POPPLER_FORM_FIELD_TEXT, POPPLER_FORM_FIELD_CHOICE, POPPLER_FORM_FIELD_SIGNATURE } PopplerFormFieldType; typedef enum { POPPLER_FORM_BUTTON_PUSH, POPPLER_FORM_BUTTON_CHECK, POPPLER_FORM_BUTTON_RADIO } PopplerFormButtonType; typedef enum { POPPLER_FORM_TEXT_NORMAL, POPPLER_FORM_TEXT_MULTILINE, POPPLER_FORM_TEXT_FILE_SELECT } PopplerFormTextType; typedef enum { POPPLER_FORM_CHOICE_COMBO, POPPLER_FORM_CHOICE_LIST } PopplerFormChoiceType; /** * PopplerAdditionalActionType: * @POPPLER_ADDITIONAL_ACTION_FIELD_MODIFIED: The action to be performed when the user modifies the field. * @POPPLER_ADDITIONAL_ACTION_FORMAT_FIELD: The action to be performed before the field is formatted to * display its value. * @POPPLER_ADDITIONAL_ACTION_VALIDATE_FIELD: The action to be performed when the field value changes. * @POPPLER_ADDITIONAL_ACTION_CALCULATE_FIELD: The action to be performed when the field needs to be * recalculated. * * Form field additional action types to be passed to @poppler_form_field_get_additional_action * * Since: 0.72 */ typedef enum { POPPLER_ADDITIONAL_ACTION_FIELD_MODIFIED, POPPLER_ADDITIONAL_ACTION_FORMAT_FIELD, POPPLER_ADDITIONAL_ACTION_VALIDATE_FIELD, POPPLER_ADDITIONAL_ACTION_CALCULATE_FIELD } PopplerAdditionalActionType; POPPLER_PUBLIC GType poppler_form_field_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerFormFieldType poppler_form_field_get_field_type(PopplerFormField *field); POPPLER_PUBLIC gint poppler_form_field_get_id(PopplerFormField *field); POPPLER_PUBLIC gdouble poppler_form_field_get_font_size(PopplerFormField *field); POPPLER_PUBLIC gboolean poppler_form_field_is_read_only(PopplerFormField *field); POPPLER_PUBLIC gchar *poppler_form_field_get_partial_name(PopplerFormField *field); POPPLER_PUBLIC gchar *poppler_form_field_get_mapping_name(PopplerFormField *field); POPPLER_PUBLIC gchar *poppler_form_field_get_name(PopplerFormField *field); POPPLER_PUBLIC PopplerAction *poppler_form_field_get_action(PopplerFormField *field); POPPLER_PUBLIC PopplerAction *poppler_form_field_get_additional_action(PopplerFormField *field, PopplerAdditionalActionType type); POPPLER_PUBLIC gchar *poppler_form_field_get_alternate_ui_name(PopplerFormField *field); /* Button Field */ POPPLER_PUBLIC PopplerFormButtonType poppler_form_field_button_get_button_type(PopplerFormField *field); POPPLER_PUBLIC gboolean poppler_form_field_button_get_state(PopplerFormField *field); POPPLER_PUBLIC void poppler_form_field_button_set_state(PopplerFormField *field, gboolean state); /* Text Field */ POPPLER_PUBLIC PopplerFormTextType poppler_form_field_text_get_text_type(PopplerFormField *field); POPPLER_PUBLIC gchar *poppler_form_field_text_get_text(PopplerFormField *field); POPPLER_PUBLIC void poppler_form_field_text_set_text(PopplerFormField *field, const gchar *text); POPPLER_PUBLIC gint poppler_form_field_text_get_max_len(PopplerFormField *field); POPPLER_PUBLIC gboolean poppler_form_field_text_do_spell_check(PopplerFormField *field); POPPLER_PUBLIC gboolean poppler_form_field_text_do_scroll(PopplerFormField *field); POPPLER_PUBLIC gboolean poppler_form_field_text_is_rich_text(PopplerFormField *field); POPPLER_PUBLIC gboolean poppler_form_field_text_is_password(PopplerFormField *field); /* Choice Field */ POPPLER_PUBLIC PopplerFormChoiceType poppler_form_field_choice_get_choice_type(PopplerFormField *field); POPPLER_PUBLIC gboolean poppler_form_field_choice_is_editable(PopplerFormField *field); POPPLER_PUBLIC gboolean poppler_form_field_choice_can_select_multiple(PopplerFormField *field); POPPLER_PUBLIC gboolean poppler_form_field_choice_do_spell_check(PopplerFormField *field); POPPLER_PUBLIC gboolean poppler_form_field_choice_commit_on_change(PopplerFormField *field); POPPLER_PUBLIC gint poppler_form_field_choice_get_n_items(PopplerFormField *field); POPPLER_PUBLIC gchar *poppler_form_field_choice_get_item(PopplerFormField *field, gint index); POPPLER_PUBLIC gboolean poppler_form_field_choice_is_item_selected(PopplerFormField *field, gint index); POPPLER_PUBLIC void poppler_form_field_choice_select_item(PopplerFormField *field, gint index); POPPLER_PUBLIC void poppler_form_field_choice_unselect_all(PopplerFormField *field); POPPLER_PUBLIC void poppler_form_field_choice_toggle_item(PopplerFormField *field, gint index); POPPLER_PUBLIC void poppler_form_field_choice_set_text(PopplerFormField *field, const gchar *text); POPPLER_PUBLIC gchar *poppler_form_field_choice_get_text(PopplerFormField *field); POPPLER_PUBLIC PopplerSignatureInfo *poppler_form_field_signature_validate_sync(PopplerFormField *field, PopplerSignatureValidationFlags flags, GCancellable *cancellable, GError **error); POPPLER_PUBLIC void poppler_form_field_signature_validate_async(PopplerFormField *field, PopplerSignatureValidationFlags flags, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); POPPLER_PUBLIC PopplerSignatureInfo *poppler_form_field_signature_validate_finish(PopplerFormField *field, GAsyncResult *result, GError **error); /* Signature Field */ #define POPPLER_TYPE_SIGNATURE_INFO (poppler_signature_info_get_type()) POPPLER_PUBLIC GType poppler_signature_info_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerSignatureInfo *poppler_signature_info_copy(const PopplerSignatureInfo *siginfo); POPPLER_PUBLIC void poppler_signature_info_free(PopplerSignatureInfo *siginfo); POPPLER_PUBLIC PopplerSignatureStatus poppler_signature_info_get_signature_status(const PopplerSignatureInfo *siginfo); 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 #endif /* __POPPLER_FORM_FIELD_H__ */ poppler-24.02.0/glib/poppler-input-stream.cc000066400000000000000000000043231455701731300207030ustar00rootroot00000000000000/* poppler-input-stream.cc: glib interface to poppler * * Copyright (C) 2012 Carlos Garcia Campos * * 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 "config.h" #include "poppler-input-stream.h" PopplerInputStream::PopplerInputStream(GInputStream *inputStreamA, GCancellable *cancellableA, Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA) : BaseSeekInputStream(startA, limitedA, lengthA, std::move(dictA)) { inputStream = (GInputStream *)g_object_ref(inputStreamA); cancellable = cancellableA ? (GCancellable *)g_object_ref(cancellableA) : nullptr; } PopplerInputStream::~PopplerInputStream() { close(); g_object_unref(inputStream); if (cancellable) { g_object_unref(cancellable); } } BaseStream *PopplerInputStream::copy() { return new PopplerInputStream(inputStream, cancellable, start, limited, length, dict.copy()); } Stream *PopplerInputStream::makeSubStream(Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA) { return new PopplerInputStream(inputStream, cancellable, startA, limitedA, lengthA, std::move(dictA)); } Goffset PopplerInputStream::currentPos() const { GSeekable *seekable = G_SEEKABLE(inputStream); return g_seekable_tell(seekable); } void PopplerInputStream::setCurrentPos(Goffset offset) { GSeekable *seekable = G_SEEKABLE(inputStream); g_seekable_seek(seekable, offset, G_SEEK_SET, cancellable, nullptr); } Goffset PopplerInputStream::read(char *buffer, Goffset count) { return g_input_stream_read(inputStream, buffer, count, cancellable, nullptr); } poppler-24.02.0/glib/poppler-input-stream.h000066400000000000000000000032661455701731300205520ustar00rootroot00000000000000/* poppler-input-stream.h: glib interface to poppler * * Copyright (C) 2012 Carlos Garcia Campos * Copyright (C) 2019 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 * 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_INPUT_STREAM_H__ #define __POPPLER_INPUT_STREAM_H__ #include #ifndef __GI_SCANNER__ # include # include class PopplerInputStream : public BaseSeekInputStream { public: PopplerInputStream(GInputStream *inputStream, GCancellable *cancellableA, Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA); ~PopplerInputStream() override; BaseStream *copy() override; Stream *makeSubStream(Goffset start, bool limited, Goffset lengthA, Object &&dictA) override; private: Goffset currentPos() const override; void setCurrentPos(Goffset offset) override; Goffset read(char *buffer, Goffset count) override; GInputStream *inputStream; GCancellable *cancellable; }; #endif /* __GI_SCANNER__ */ #endif /* __POPPLER_INPUT_STREAM_H__ */ poppler-24.02.0/glib/poppler-layer.cc000066400000000000000000000124611455701731300173710ustar00rootroot00000000000000/* poppler-layer.cc: glib interface to poppler * * Copyright (C) 2008 Carlos Garcia Campos * * 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 "poppler-layer.h" #include "poppler-private.h" /** * SECTION:poppler-layer * @short_description: Layers * @title: PopplerLayer */ typedef struct _PopplerLayerClass PopplerLayerClass; struct _PopplerLayerClass { GObjectClass parent_class; }; G_DEFINE_TYPE(PopplerLayer, poppler_layer, G_TYPE_OBJECT) static void poppler_layer_finalize(GObject *object) { PopplerLayer *poppler_layer = POPPLER_LAYER(object); if (poppler_layer->document) { g_object_unref(poppler_layer->document); poppler_layer->document = nullptr; } if (poppler_layer->title) { g_free(poppler_layer->title); poppler_layer->title = nullptr; } poppler_layer->layer = nullptr; poppler_layer->rbgroup = nullptr; G_OBJECT_CLASS(poppler_layer_parent_class)->finalize(object); } static void poppler_layer_init(PopplerLayer *layer) { } static void poppler_layer_class_init(PopplerLayerClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS(klass); gobject_class->finalize = poppler_layer_finalize; } PopplerLayer *_poppler_layer_new(PopplerDocument *document, Layer *layer, GList *rbgroup) { PopplerLayer *poppler_layer; const GooString *layer_name; g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL); g_return_val_if_fail(layer != nullptr, NULL); poppler_layer = POPPLER_LAYER(g_object_new(POPPLER_TYPE_LAYER, nullptr)); poppler_layer->document = (PopplerDocument *)g_object_ref(document); poppler_layer->layer = layer; poppler_layer->rbgroup = rbgroup; layer_name = layer->oc->getName(); poppler_layer->title = layer_name ? _poppler_goo_string_to_utf8(layer_name) : nullptr; return poppler_layer; } /** * poppler_layer_get_title: * @layer: a #PopplerLayer * * Returns the name of the layer suitable for * presentation as a title in a viewer's GUI * * Return value: a string containing the title of the layer * * Since: 0.12 **/ const gchar *poppler_layer_get_title(PopplerLayer *poppler_layer) { g_return_val_if_fail(POPPLER_IS_LAYER(poppler_layer), NULL); return poppler_layer->title; } /** * poppler_layer_is_visible: * @layer: a #PopplerLayer * * Returns whether @layer is visible * * Return value: %TRUE if @layer is visible * * Since: 0.12 **/ gboolean poppler_layer_is_visible(PopplerLayer *poppler_layer) { g_return_val_if_fail(POPPLER_IS_LAYER(poppler_layer), FALSE); return poppler_layer->layer->oc->getState() == OptionalContentGroup::On; } /** * poppler_layer_show: * @layer: a #PopplerLayer * * Shows @layer * * Since: 0.12 **/ void poppler_layer_show(PopplerLayer *poppler_layer) { GList *l; Layer *layer; g_return_if_fail(POPPLER_IS_LAYER(poppler_layer)); layer = poppler_layer->layer; if (layer->oc->getState() == OptionalContentGroup::On) { return; } layer->oc->setState(OptionalContentGroup::On); for (l = poppler_layer->rbgroup; l && l->data; l = g_list_next(l)) { OptionalContentGroup *oc = (OptionalContentGroup *)l->data; if (oc != layer->oc) { oc->setState(OptionalContentGroup::Off); } } } /** * poppler_layer_hide: * @layer: a #PopplerLayer * * Hides @layer. If @layer is the parent of other nested layers, * such layers will be also hidden and will be blocked until @layer * is shown again * * Since: 0.12 **/ void poppler_layer_hide(PopplerLayer *poppler_layer) { Layer *layer; g_return_if_fail(POPPLER_IS_LAYER(poppler_layer)); layer = poppler_layer->layer; if (layer->oc->getState() == OptionalContentGroup::Off) { return; } layer->oc->setState(OptionalContentGroup::Off); } /** * poppler_layer_is_parent: * @layer: a #PopplerLayer * * Returns whether @layer is parent of other nested layers. * * Return value: %TRUE if @layer is a parent layer * * Since: 0.12 **/ gboolean poppler_layer_is_parent(PopplerLayer *poppler_layer) { g_return_val_if_fail(POPPLER_IS_LAYER(poppler_layer), FALSE); return poppler_layer->layer->kids != nullptr; } /** * poppler_layer_get_radio_button_group_id: * @layer: a #PopplerLayer * * Returns the numeric ID the radio button group associated with @layer. * * Return value: the ID of the radio button group associated with @layer, * or 0 if the layer is not associated to any radio button group * * Since: 0.12 **/ gint poppler_layer_get_radio_button_group_id(PopplerLayer *poppler_layer) { g_return_val_if_fail(POPPLER_IS_LAYER(poppler_layer), FALSE); return GPOINTER_TO_INT(poppler_layer->rbgroup); } poppler-24.02.0/glib/poppler-layer.h000066400000000000000000000032611455701731300172310ustar00rootroot00000000000000/* poppler-layer.h: glib interface to poppler * * Copyright (C) 2008 Carlos Garcia Campos * * 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_LAYER_H__ #define __POPPLER_LAYER_H__ #include #include "poppler.h" G_BEGIN_DECLS #define POPPLER_TYPE_LAYER (poppler_layer_get_type()) #define POPPLER_LAYER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), POPPLER_TYPE_LAYER, PopplerLayer)) #define POPPLER_IS_LAYER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), POPPLER_TYPE_LAYER)) POPPLER_PUBLIC GType poppler_layer_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC const gchar *poppler_layer_get_title(PopplerLayer *layer); POPPLER_PUBLIC gboolean poppler_layer_is_visible(PopplerLayer *layer); POPPLER_PUBLIC void poppler_layer_show(PopplerLayer *layer); POPPLER_PUBLIC void poppler_layer_hide(PopplerLayer *layer); POPPLER_PUBLIC gboolean poppler_layer_is_parent(PopplerLayer *layer); POPPLER_PUBLIC gint poppler_layer_get_radio_button_group_id(PopplerLayer *layer); G_END_DECLS #endif /* __POPPLER_LAYER_H__ */ poppler-24.02.0/glib/poppler-media.cc000066400000000000000000000251241455701731300173340ustar00rootroot00000000000000/* poppler-media.cc: glib interface to MediaRendition * * Copyright (C) 2010 Carlos Garcia Campos * * 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 "config.h" #include #include #include "poppler-media.h" #include "poppler-private.h" /** * SECTION: poppler-media * @short_description: Media * @title: PopplerMedia */ typedef struct _PopplerMediaClass PopplerMediaClass; struct _PopplerMedia { GObject parent_instance; gchar *filename; gboolean auto_play; gboolean show_controls; gfloat repeat_count; gchar *mime_type; Object stream; }; struct _PopplerMediaClass { GObjectClass parent_class; }; G_DEFINE_TYPE(PopplerMedia, poppler_media, G_TYPE_OBJECT) static void poppler_media_finalize(GObject *object) { PopplerMedia *media = POPPLER_MEDIA(object); if (media->filename) { g_free(media->filename); media->filename = nullptr; } if (media->mime_type) { g_free(media->mime_type); media->mime_type = nullptr; } media->stream = Object(); G_OBJECT_CLASS(poppler_media_parent_class)->finalize(object); } static void poppler_media_class_init(PopplerMediaClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS(klass); gobject_class->finalize = poppler_media_finalize; } static void poppler_media_init(PopplerMedia *media) { } PopplerMedia *_poppler_media_new(const MediaRendition *poppler_media) { PopplerMedia *media; g_assert(poppler_media != nullptr); media = POPPLER_MEDIA(g_object_new(POPPLER_TYPE_MEDIA, nullptr)); if (poppler_media->getIsEmbedded()) { const GooString *mime_type; media->stream = poppler_media->getEmbbededStreamObject()->copy(); mime_type = poppler_media->getContentType(); if (mime_type) { media->mime_type = g_strdup(mime_type->c_str()); } } else { media->filename = g_strdup(poppler_media->getFileName()->c_str()); } const MediaParameters *mp = poppler_media->getBEParameters(); mp = mp ? mp : poppler_media->getMHParameters(); media->auto_play = mp ? mp->autoPlay : false; media->show_controls = mp ? mp->showControls : false; media->repeat_count = mp ? mp->repeatCount : 1.f; return media; } /** * poppler_media_get_filename: * @poppler_media: a #PopplerMedia * * Returns the media clip filename, in case of non-embedded media. filename might be * a local relative or absolute path or a URI * * Return value: a filename, return value is owned by #PopplerMedia and should not be freed * * Since: 0.14 */ const gchar *poppler_media_get_filename(PopplerMedia *poppler_media) { g_return_val_if_fail(POPPLER_IS_MEDIA(poppler_media), NULL); g_return_val_if_fail(!poppler_media->stream.isStream(), NULL); return poppler_media->filename; } /** * poppler_media_is_embedded: * @poppler_media: a #PopplerMedia * * 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 * poppler_media_get_filename() function. * * Return value: %TRUE if media clip is embedded, %FALSE otherwise * * Since: 0.14 */ gboolean poppler_media_is_embedded(PopplerMedia *poppler_media) { g_return_val_if_fail(POPPLER_IS_MEDIA(poppler_media), FALSE); return poppler_media->stream.isStream(); } /** * poppler_media_get_auto_play: * @poppler_media: a #PopplerMedia * * Returns the auto-play parameter. * * Return value: %TRUE if media should auto-play, %FALSE otherwise * * Since: 20.04.0 */ gboolean poppler_media_get_auto_play(PopplerMedia *poppler_media) { g_return_val_if_fail(POPPLER_IS_MEDIA(poppler_media), FALSE); return poppler_media->auto_play; } /** * poppler_media_get_show_controls: * @poppler_media: a #PopplerMedia * * Returns the show controls parameter. * * Return value: %TRUE if media should show controls, %FALSE otherwise * * Since: 20.04.0 */ gboolean poppler_media_get_show_controls(PopplerMedia *poppler_media) { g_return_val_if_fail(POPPLER_IS_MEDIA(poppler_media), FALSE); return poppler_media->show_controls; } /** * poppler_media_get_repeat_count: * @poppler_media: a #PopplerMedia * * Returns the repeat count parameter. * * Return value: Repeat count parameter (float) * * Since: 20.04.0 */ gfloat poppler_media_get_repeat_count(PopplerMedia *poppler_media) { g_return_val_if_fail(POPPLER_IS_MEDIA(poppler_media), FALSE); return poppler_media->repeat_count; } /** * poppler_media_get_mime_type: * @poppler_media: a #PopplerMedia * * Returns the media clip mime-type * * Return value: the mime-type, return value is owned by #PopplerMedia and should not be freed * * Since: 0.14 */ const gchar *poppler_media_get_mime_type(PopplerMedia *poppler_media) { g_return_val_if_fail(POPPLER_IS_MEDIA(poppler_media), NULL); return poppler_media->mime_type; } static gboolean save_helper(const gchar *buf, gsize count, gpointer data, GError **error) { FILE *f = (FILE *)data; gsize n; n = fwrite(buf, 1, count, f); if (n != count) { int errsv = errno; g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errsv), "Error writing to media file: %s", g_strerror(errsv)); return FALSE; } return TRUE; } /** * poppler_media_save: * @poppler_media: a #PopplerMedia * @filename: name of file to save * @error: (allow-none): return location for error, or %NULL. * * 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 * and whatever the save function generates. * * Return value: %TRUE, if the file successfully saved * * Since: 0.14 */ gboolean poppler_media_save(PopplerMedia *poppler_media, const char *filename, GError **error) { gboolean result; FILE *f; g_return_val_if_fail(POPPLER_IS_MEDIA(poppler_media), FALSE); g_return_val_if_fail(poppler_media->stream.isStream(), FALSE); f = openFile(filename, "wb"); if (f == nullptr) { gchar *display_name = g_filename_display_name(filename); g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errno), "Failed to open '%s' for writing: %s", display_name, g_strerror(errno)); g_free(display_name); return FALSE; } result = poppler_media_save_to_callback(poppler_media, save_helper, f, error); if (fclose(f) < 0) { gchar *display_name = g_filename_display_name(filename); g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errno), "Failed to close '%s', all data may not have been saved: %s", display_name, g_strerror(errno)); g_free(display_name); return FALSE; } return result; } #ifndef G_OS_WIN32 /** * poppler_media_save_to_fd: * @poppler_media: a #PopplerMedia * @fd: a valid file descriptor open for writing * @error: (allow-none): return location for error, or %NULL. * * 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 * and whatever the save function generates. * Note that this function takes ownership of @fd; you must not operate on it * again, nor close it. * * Return value: %TRUE, if the file successfully saved * * Since: 21.12.0 */ gboolean poppler_media_save_to_fd(PopplerMedia *poppler_media, int fd, GError **error) { gboolean result; FILE *f; g_return_val_if_fail(POPPLER_IS_MEDIA(poppler_media), FALSE); g_return_val_if_fail(poppler_media->stream.isStream(), FALSE); f = fdopen(fd, "wb"); if (f == nullptr) { int errsv = errno; g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errsv), "Failed to open FD %d for writing: %s", fd, g_strerror(errsv)); close(fd); return FALSE; } result = poppler_media_save_to_callback(poppler_media, save_helper, f, error); if (fclose(f) < 0) { int errsv = errno; g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errsv), "Failed to close FD %d, all data may not have been saved: %s", fd, g_strerror(errsv)); return FALSE; } return result; } #endif /* !G_OS_WIN32 */ #define BUF_SIZE 1024 /** * poppler_media_save_to_callback: * @poppler_media: a #PopplerMedia * @save_func: (scope call): a function that is called to save each block of data that the save routine generates. * @user_data: user data to pass to the save function. * @error: (allow-none): return location for error, or %NULL. * * 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 * whatever the save function generates. * * Return value: %TRUE, if the save successfully completed * * Since: 0.14 */ gboolean poppler_media_save_to_callback(PopplerMedia *poppler_media, PopplerMediaSaveFunc save_func, gpointer user_data, GError **error) { Stream *stream; gchar buf[BUF_SIZE]; int i; gboolean eof_reached = FALSE; g_return_val_if_fail(POPPLER_IS_MEDIA(poppler_media), FALSE); g_return_val_if_fail(poppler_media->stream.isStream(), FALSE); stream = poppler_media->stream.getStream(); stream->reset(); do { int data; for (i = 0; i < BUF_SIZE; i++) { data = stream->getChar(); if (data == EOF) { eof_reached = TRUE; break; } buf[i] = data; } if (i > 0) { if (!(save_func)(buf, i, user_data, error)) { stream->close(); return FALSE; } } } while (!eof_reached); stream->close(); return TRUE; } poppler-24.02.0/glib/poppler-media.h000066400000000000000000000060521455701731300171750ustar00rootroot00000000000000/* poppler-media.h: glib interface to MediaRendition * * Copyright (C) 2010 Carlos Garcia Campos * * 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_MEDIA_H__ #define __POPPLER_MEDIA_H__ #include #include "poppler.h" G_BEGIN_DECLS #define POPPLER_TYPE_MEDIA (poppler_media_get_type()) #define POPPLER_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), POPPLER_TYPE_MEDIA, PopplerMedia)) #define POPPLER_IS_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), POPPLER_TYPE_MEDIA)) /* FIXME: this should be generic (PopplerSaveToCallbackFunc) */ /** * PopplerMediaSaveFunc: * @buf: (array length=count) (element-type guint8): buffer containing * bytes to be written. * @count: number of bytes in @buf. * @data: (closure): user data passed to poppler_media_save_to_callback() * @error: GError to set on error, or %NULL * * 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 * @error and return %FALSE, in which case poppler_media_save_to_callback() * will fail with the same error. * * Returns: %TRUE if successful, %FALSE (with @error set) if failed. * * Since: 0.14 */ typedef gboolean (*PopplerMediaSaveFunc)(const gchar *buf, gsize count, gpointer data, GError **error); POPPLER_PUBLIC GType poppler_media_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC gboolean poppler_media_is_embedded(PopplerMedia *poppler_media); POPPLER_PUBLIC const gchar *poppler_media_get_filename(PopplerMedia *poppler_media); POPPLER_PUBLIC const gchar *poppler_media_get_mime_type(PopplerMedia *poppler_media); POPPLER_PUBLIC gboolean poppler_media_get_auto_play(PopplerMedia *poppler_media); POPPLER_PUBLIC gboolean poppler_media_get_show_controls(PopplerMedia *poppler_media); POPPLER_PUBLIC gfloat poppler_media_get_repeat_count(PopplerMedia *poppler_media); POPPLER_PUBLIC gboolean poppler_media_save(PopplerMedia *poppler_media, const char *filename, GError **error); #ifndef G_OS_WIN32 POPPLER_PUBLIC gboolean poppler_media_save_to_fd(PopplerMedia *poppler_media, int fd, GError **error); #endif POPPLER_PUBLIC gboolean poppler_media_save_to_callback(PopplerMedia *poppler_media, PopplerMediaSaveFunc save_func, gpointer user_data, GError **error); G_END_DECLS #endif /* __POPPLER_MEDIA_H__ */ poppler-24.02.0/glib/poppler-movie.cc000066400000000000000000000215461455701731300174000ustar00rootroot00000000000000/* poppler-movie.cc: glib interface to Movie * * Copyright (C) 2010 Carlos Garcia Campos * Copyright (C) 2008 Hugo Mercier * Copyright (C) 2017 Francesco Poli * * 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 "poppler-movie.h" #include "poppler-private.h" /** * SECTION: poppler-movie * @short_description: Movies * @title: PopplerMovie */ typedef struct _PopplerMovieClass PopplerMovieClass; struct _PopplerMovie { GObject parent_instance; gchar *filename; gboolean need_poster; gboolean show_controls; PopplerMoviePlayMode mode; gboolean synchronous_play; gdouble volume; gdouble rate; guint64 start; guint64 duration; gushort rotation_angle; gint width; gint height; }; struct _PopplerMovieClass { GObjectClass parent_class; }; G_DEFINE_TYPE(PopplerMovie, poppler_movie, G_TYPE_OBJECT) static void poppler_movie_finalize(GObject *object) { PopplerMovie *movie = POPPLER_MOVIE(object); if (movie->filename) { g_free(movie->filename); movie->filename = nullptr; } G_OBJECT_CLASS(poppler_movie_parent_class)->finalize(object); } static void poppler_movie_class_init(PopplerMovieClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS(klass); gobject_class->finalize = poppler_movie_finalize; } static void poppler_movie_init(PopplerMovie *movie) { } PopplerMovie *_poppler_movie_new(const Movie *poppler_movie) { PopplerMovie *movie; g_assert(poppler_movie != nullptr); movie = POPPLER_MOVIE(g_object_new(POPPLER_TYPE_MOVIE, nullptr)); movie->filename = g_strdup(poppler_movie->getFileName()->c_str()); if (poppler_movie->getShowPoster()) { Object tmp = poppler_movie->getPoster(); movie->need_poster = (!tmp.isRef() && !tmp.isStream()); } movie->show_controls = poppler_movie->getActivationParameters()->showControls; switch (poppler_movie->getActivationParameters()->repeatMode) { case MovieActivationParameters::repeatModeOnce: movie->mode = POPPLER_MOVIE_PLAY_MODE_ONCE; break; case MovieActivationParameters::repeatModeOpen: movie->mode = POPPLER_MOVIE_PLAY_MODE_OPEN; break; case MovieActivationParameters::repeatModeRepeat: movie->mode = POPPLER_MOVIE_PLAY_MODE_REPEAT; break; case MovieActivationParameters::repeatModePalindrome: movie->mode = POPPLER_MOVIE_PLAY_MODE_PALINDROME; break; } movie->synchronous_play = poppler_movie->getActivationParameters()->synchronousPlay; // map 0 - 100 to 0.0 - 1.0 movie->volume = poppler_movie->getActivationParameters()->volume / 100.0; movie->rate = poppler_movie->getActivationParameters()->rate; if (poppler_movie->getActivationParameters()->start.units_per_second > 0 && poppler_movie->getActivationParameters()->start.units <= G_MAXUINT64 / 1000000000) { movie->start = 1000000000L * poppler_movie->getActivationParameters()->start.units / poppler_movie->getActivationParameters()->start.units_per_second; } else { movie->start = 0L; } if (poppler_movie->getActivationParameters()->duration.units_per_second > 0 && poppler_movie->getActivationParameters()->duration.units <= G_MAXUINT64 / 1000000000) { movie->duration = 1000000000L * poppler_movie->getActivationParameters()->duration.units / poppler_movie->getActivationParameters()->duration.units_per_second; } else { movie->duration = 0L; } movie->rotation_angle = poppler_movie->getRotationAngle(); poppler_movie->getAspect(&movie->width, &movie->height); return movie; } /** * poppler_movie_get_filename: * @poppler_movie: a #PopplerMovie * * Returns the local filename identifying a self-describing movie file * * Return value: a local filename, return value is owned by #PopplerMovie and * should not be freed * * Since: 0.14 */ const gchar *poppler_movie_get_filename(PopplerMovie *poppler_movie) { g_return_val_if_fail(POPPLER_IS_MOVIE(poppler_movie), NULL); return poppler_movie->filename; } /** * poppler_movie_need_poster: * @poppler_movie: a #PopplerMovie * * Returns whether a poster image representing the Movie * shall be displayed. The poster image must be retrieved * from the movie file. * * Return value: %TRUE if move needs a poster image, %FALSE otherwise * * Since: 0.14 */ gboolean poppler_movie_need_poster(PopplerMovie *poppler_movie) { g_return_val_if_fail(POPPLER_IS_MOVIE(poppler_movie), FALSE); return poppler_movie->need_poster; } /** * poppler_movie_show_controls: * @poppler_movie: a #PopplerMovie * * Returns whether to display a movie controller bar while playing the movie * * Return value: %TRUE if controller bar should be displayed, %FALSE otherwise * * Since: 0.14 */ gboolean poppler_movie_show_controls(PopplerMovie *poppler_movie) { g_return_val_if_fail(POPPLER_IS_MOVIE(poppler_movie), FALSE); return poppler_movie->show_controls; } /** * poppler_movie_get_play_mode: * @poppler_movie: a #PopplerMovie * * Returns the play mode of @poppler_movie. * * Return value: a #PopplerMoviePlayMode. * * Since: 0.54 */ PopplerMoviePlayMode poppler_movie_get_play_mode(PopplerMovie *poppler_movie) { g_return_val_if_fail(POPPLER_IS_MOVIE(poppler_movie), POPPLER_MOVIE_PLAY_MODE_ONCE); return poppler_movie->mode; } /** * poppler_movie_is_synchronous: * @poppler_movie: a #PopplerMovie * * Returns whether the user must wait for the movie to be finished before * the PDF viewer accepts any interactive action * * Return value: %TRUE if yes, %FALSE otherwise * * Since: 0.80 */ gboolean poppler_movie_is_synchronous(PopplerMovie *poppler_movie) { g_return_val_if_fail(POPPLER_IS_MOVIE(poppler_movie), FALSE); return poppler_movie->synchronous_play; } /** * poppler_movie_get_volume: * @poppler_movie: a #PopplerMovie * * Returns the playback audio volume * * Return value: volume setting for the movie (0.0 - 1.0) * * Since: 0.80 */ gdouble poppler_movie_get_volume(PopplerMovie *poppler_movie) { g_return_val_if_fail(POPPLER_IS_MOVIE(poppler_movie), 0); return poppler_movie->volume; } /** * poppler_movie_get_rate: * @poppler_movie: a #PopplerMovie * * Returns the relative speed of the movie * * Return value: the relative speed of the movie (1 means no change) * * Since: 0.80 */ gdouble poppler_movie_get_rate(PopplerMovie *poppler_movie) { g_return_val_if_fail(POPPLER_IS_MOVIE(poppler_movie), 0); return poppler_movie->rate; } /** * poppler_movie_get_rotation_angle: * @poppler_movie: a #PopplerMovie * * Returns the rotation angle * * Return value: the number of degrees the movie should be rotated (positive, * multiples of 90: 0, 90, 180, 270) * * Since: 0.80 */ gushort poppler_movie_get_rotation_angle(PopplerMovie *poppler_movie) { g_return_val_if_fail(POPPLER_IS_MOVIE(poppler_movie), 0); return poppler_movie->rotation_angle; } /** * poppler_movie_get_start: * @poppler_movie: a #PopplerMovie * * Returns the start position of the movie playback * * Return value: the start position of the movie playback (in ns) * * Since: 0.80 */ guint64 poppler_movie_get_start(PopplerMovie *poppler_movie) { g_return_val_if_fail(POPPLER_IS_MOVIE(poppler_movie), 0L); return poppler_movie->start; } /** * poppler_movie_get_duration: * @poppler_movie: a #PopplerMovie * * Returns the duration of the movie playback * * Return value: the duration of the movie playback (in ns) * * Since: 0.80 */ guint64 poppler_movie_get_duration(PopplerMovie *poppler_movie) { g_return_val_if_fail(POPPLER_IS_MOVIE(poppler_movie), 0L); return poppler_movie->duration; } /** * poppler_movie_get_aspect: * @poppler_movie: a #PopplerMovie * @width: width of the movie's bounding box * @height: height of the movie's bounding box * * 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. * * Since: 0.89 */ void poppler_movie_get_aspect(PopplerMovie *poppler_movie, gint *width, gint *height) { g_return_if_fail(POPPLER_IS_MOVIE(poppler_movie)); *width = poppler_movie->width; *height = poppler_movie->height; } poppler-24.02.0/glib/poppler-movie.h000066400000000000000000000057651455701731300172470ustar00rootroot00000000000000/* poppler-movie.h: glib interface to Movie * * Copyright (C) 2010 Carlos Garcia Campos * Copyright (C) 2008 Hugo Mercier * Copyright (C) 2017 Francesco Poli * * 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_MOVIE_H__ #define __POPPLER_MOVIE_H__ #include #include "poppler.h" G_BEGIN_DECLS #define POPPLER_TYPE_MOVIE (poppler_movie_get_type()) #define POPPLER_MOVIE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), POPPLER_TYPE_MOVIE, PopplerMovie)) #define POPPLER_IS_MOVIE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), POPPLER_TYPE_MOVIE)) /** * PopplerMoviePlayMode: * @POPPLER_MOVIE_PLAY_MODE_ONCE: the movie should be played once and controls should be closed at the end. * @POPPLER_MOVIE_PLAY_MODE_OPEN: the movie should be played once, but controls should be left open. * @POPPLER_MOVIE_PLAY_MODE_REPEAT: the movie should be played in loop, until manually stopped. * @POPPLER_MOVIE_PLAY_MODE_PALINDROME: the movie should be played forward and backward, forward and backward, * and so forth, until manually stopped. * * Play mode enum values. * * Since: 0.54 */ typedef enum { POPPLER_MOVIE_PLAY_MODE_ONCE, POPPLER_MOVIE_PLAY_MODE_OPEN, POPPLER_MOVIE_PLAY_MODE_REPEAT, POPPLER_MOVIE_PLAY_MODE_PALINDROME } PopplerMoviePlayMode; POPPLER_PUBLIC GType poppler_movie_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC const gchar *poppler_movie_get_filename(PopplerMovie *poppler_movie); POPPLER_PUBLIC gboolean poppler_movie_need_poster(PopplerMovie *poppler_movie); POPPLER_PUBLIC gboolean poppler_movie_show_controls(PopplerMovie *poppler_movie); POPPLER_PUBLIC PopplerMoviePlayMode poppler_movie_get_play_mode(PopplerMovie *poppler_movie); POPPLER_PUBLIC gboolean poppler_movie_is_synchronous(PopplerMovie *poppler_movie); POPPLER_PUBLIC gdouble poppler_movie_get_volume(PopplerMovie *poppler_movie); POPPLER_PUBLIC gdouble poppler_movie_get_rate(PopplerMovie *poppler_movie); POPPLER_PUBLIC gushort poppler_movie_get_rotation_angle(PopplerMovie *poppler_movie); POPPLER_PUBLIC guint64 poppler_movie_get_start(PopplerMovie *poppler_movie); POPPLER_PUBLIC guint64 poppler_movie_get_duration(PopplerMovie *poppler_movie); POPPLER_PUBLIC void poppler_movie_get_aspect(PopplerMovie *poppler_movie, gint *width, gint *height); G_END_DECLS #endif /* __POPPLER_MOVIE_H__ */ poppler-24.02.0/glib/poppler-page.cc000066400000000000000000002321111455701731300171650ustar00rootroot00000000000000/* poppler-page.cc: glib wrapper for poppler * Copyright (C) 2005, Red Hat, Inc. * * 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 "config.h" #include #ifndef __GI_SCANNER__ # include # include # include # include # include # include # include # include #endif #include "poppler.h" #include "poppler-private.h" static void _page_unrotate_xy(Page *page, double *x, double *y); /** * SECTION:poppler-page * @short_description: Information about a page in a document * @title: PopplerPage */ enum { PROP_0, PROP_LABEL }; static PopplerRectangleExtended *poppler_rectangle_extended_new(); typedef struct _PopplerPageClass PopplerPageClass; struct _PopplerPageClass { GObjectClass parent_class; }; G_DEFINE_TYPE(PopplerPage, poppler_page, G_TYPE_OBJECT) PopplerPage *_poppler_page_new(PopplerDocument *document, Page *page, int index) { PopplerPage *poppler_page; g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL); poppler_page = (PopplerPage *)g_object_new(POPPLER_TYPE_PAGE, nullptr, NULL); poppler_page->document = (PopplerDocument *)g_object_ref(document); poppler_page->page = page; poppler_page->index = index; return poppler_page; } static void poppler_page_finalize(GObject *object) { PopplerPage *page = POPPLER_PAGE(object); g_object_unref(page->document); page->document = nullptr; if (page->text != nullptr) { page->text->decRefCnt(); } /* page->page is owned by the document */ G_OBJECT_CLASS(poppler_page_parent_class)->finalize(object); } /** * poppler_page_get_size: * @page: A #PopplerPage * @width: (out) (allow-none): return location for the width of @page * @height: (out) (allow-none): return location for the height of @page * * Gets the size of @page at the current scale and rotation. **/ void poppler_page_get_size(PopplerPage *page, double *width, double *height) { double page_width, page_height; int rotate; g_return_if_fail(POPPLER_IS_PAGE(page)); rotate = page->page->getRotate(); if (rotate == 90 || rotate == 270) { page_height = page->page->getCropWidth(); page_width = page->page->getCropHeight(); } else { page_width = page->page->getCropWidth(); page_height = page->page->getCropHeight(); } if (width != nullptr) { *width = page_width; } if (height != nullptr) { *height = page_height; } } /** * poppler_page_get_index: * @page: a #PopplerPage * * Returns the index of @page * * Return value: index value of @page **/ int poppler_page_get_index(PopplerPage *page) { g_return_val_if_fail(POPPLER_IS_PAGE(page), 0); return page->index; } /** * poppler_page_get_label: * @page: a #PopplerPage * * Returns the label of @page. Note that page labels * and page indices might not coincide. * * Return value: a new allocated string containing the label of @page, * or %NULL if @page doesn't have a label * * Since: 0.16 **/ gchar *poppler_page_get_label(PopplerPage *page) { GooString label; g_return_val_if_fail(POPPLER_IS_PAGE(page), NULL); page->document->doc->getCatalog()->indexToLabel(page->index, &label); return _poppler_goo_string_to_utf8(&label); } /** * poppler_page_get_duration: * @page: a #PopplerPage * * Returns the duration of @page * * Return value: duration in seconds of @page or -1. **/ double poppler_page_get_duration(PopplerPage *page) { g_return_val_if_fail(POPPLER_IS_PAGE(page), -1); return page->page->getDuration(); } /** * poppler_page_get_transition: * @page: a #PopplerPage * * Returns the transition effect of @page * * Return value: a #PopplerPageTransition or %NULL. **/ PopplerPageTransition *poppler_page_get_transition(PopplerPage *page) { PageTransition *trans; PopplerPageTransition *transition; g_return_val_if_fail(POPPLER_IS_PAGE(page), NULL); Object obj = page->page->getTrans(); trans = new PageTransition(&obj); if (!trans->isOk()) { delete trans; return nullptr; } transition = poppler_page_transition_new(); switch (trans->getType()) { case transitionReplace: transition->type = POPPLER_PAGE_TRANSITION_REPLACE; break; case transitionSplit: transition->type = POPPLER_PAGE_TRANSITION_SPLIT; break; case transitionBlinds: transition->type = POPPLER_PAGE_TRANSITION_BLINDS; break; case transitionBox: transition->type = POPPLER_PAGE_TRANSITION_BOX; break; case transitionWipe: transition->type = POPPLER_PAGE_TRANSITION_WIPE; break; case transitionDissolve: transition->type = POPPLER_PAGE_TRANSITION_DISSOLVE; break; case transitionGlitter: transition->type = POPPLER_PAGE_TRANSITION_GLITTER; break; case transitionFly: transition->type = POPPLER_PAGE_TRANSITION_FLY; break; case transitionPush: transition->type = POPPLER_PAGE_TRANSITION_PUSH; break; case transitionCover: transition->type = POPPLER_PAGE_TRANSITION_COVER; break; case transitionUncover: transition->type = POPPLER_PAGE_TRANSITION_UNCOVER; break; case transitionFade: transition->type = POPPLER_PAGE_TRANSITION_FADE; break; default: g_assert_not_reached(); } transition->alignment = (trans->getAlignment() == transitionHorizontal) ? POPPLER_PAGE_TRANSITION_HORIZONTAL : POPPLER_PAGE_TRANSITION_VERTICAL; transition->direction = (trans->getDirection() == transitionInward) ? POPPLER_PAGE_TRANSITION_INWARD : POPPLER_PAGE_TRANSITION_OUTWARD; transition->duration = trans->getDuration(); transition->duration_real = trans->getDuration(); transition->angle = trans->getAngle(); transition->scale = trans->getScale(); transition->rectangular = trans->isRectangular(); delete trans; return transition; } 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); text_dev->endPage(); page->text = text_dev->takeText(); delete gfx; delete text_dev; } return page->text; } static gboolean annot_is_markup(Annot *annot) { 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); } if (user_print_flags & POPPLER_PRINT_MARKUP_ANNOTS) { return annot_is_markup(annot) ? (annot->getFlags() & Annot::flagPrint) : (annot->getType() == Annot::typeWidget); } /* Print document only, form fields are always printed */ return (annot->getType() == Annot::typeWidget); } static void _poppler_page_render(PopplerPage *page, cairo_t *cairo, bool printing, PopplerPrintFlags print_flags) { CairoOutputDev *output_dev; g_return_if_fail(POPPLER_IS_PAGE(page)); output_dev = page->document->output_dev; output_dev->setCairo(cairo); output_dev->setPrinting(printing); if (!printing && page->text == nullptr) { 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); cairo_restore(cairo); output_dev->setCairo(nullptr); output_dev->setTextPage(nullptr); } /** * poppler_page_render: * @page: the page to render from * @cairo: cairo context to render to * * 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 * poppler_page_render_for_printing() instead. Please see the documentation * for that function for the differences between rendering to the screen and * rendering to a printer. **/ 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_for_printing_with_options: * @page: the page to render from * @cairo: cairo context to render to * @options: print options * * 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 * differences between rendering to the screen and rendering to a printer. * * Since: 0.16 **/ void poppler_page_render_for_printing_with_options(PopplerPage *page, cairo_t *cairo, PopplerPrintFlags options) { g_return_if_fail(POPPLER_IS_PAGE(page)); _poppler_page_render(page, cairo, true, options); } /** * poppler_page_render_for_printing: * @page: the page to render from * @cairo: cairo context to render to * * 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(). * * The difference between poppler_page_render() and this function is that some * things get rendered differently between screens and printers: * * * * PDF annotations get rendered according to their #PopplerAnnotFlag value. * For example, #POPPLER_ANNOT_FLAG_PRINT refers to whether an annotation * is printed or not, whereas #POPPLER_ANNOT_FLAG_NO_VIEW refers to whether * an annotation is invisible when displaying to the screen. * * * PDF supports "hairlines" of width 0.0, which often get rendered as * having a width of 1 device pixel. When displaying on a screen, Cairo * may render such lines wide so that they are hard to see, and Poppler * makes use of PDF's Stroke Adjust graphics parameter to make the lines * easier to see. However, when printing, Poppler is able to directly use a * printer's pixel size instead. * * * Some advanced features in PDF may require an image to be rasterized * before sending off to a printer. This may produce raster images which * exceed Cairo's limits. The "printing" functions will detect this condition * and try to down-scale the intermediate surfaces as appropriate. * * * **/ 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); } static cairo_surface_t *create_surface_from_thumbnail_data(guchar *data, gint width, gint height, gint rowstride) { guchar *cairo_pixels; gint cairo_stride; cairo_surface_t *surface; int j; surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height); if (cairo_surface_status(surface)) { return nullptr; } cairo_pixels = cairo_image_surface_get_data(surface); cairo_stride = cairo_image_surface_get_stride(surface); for (j = height; j; j--) { guchar *p = data; guchar *q = cairo_pixels; guchar *end = p + 3 * width; while (p < end) { #if G_BYTE_ORDER == G_LITTLE_ENDIAN q[0] = p[2]; q[1] = p[1]; q[2] = p[0]; #else q[1] = p[0]; q[2] = p[1]; q[3] = p[2]; #endif p += 3; q += 4; } data += rowstride; cairo_pixels += cairo_stride; } return surface; } /** * poppler_page_get_thumbnail: * @page: the #PopplerPage to get the thumbnail for * * Get the embedded thumbnail for the specified page. If the document * doesn't have an embedded thumbnail for the page, this function * returns %NULL. * * Return value: the tumbnail as a cairo_surface_t or %NULL if the document * doesn't have a thumbnail for this page. **/ cairo_surface_t *poppler_page_get_thumbnail(PopplerPage *page) { unsigned char *data; int width, height, rowstride; cairo_surface_t *surface; g_return_val_if_fail(POPPLER_IS_PAGE(page), NULL); if (!page->page->loadThumb(&data, &width, &height, &rowstride)) { return nullptr; } surface = create_surface_from_thumbnail_data(data, width, height, rowstride); gfree(data); return surface; } /** * poppler_page_render_selection: * @page: the #PopplerPage for which to render selection * @cairo: cairo context to render to * @selection: start and end point of selection as a rectangle * @old_selection: previous selection * @style: a #PopplerSelectionStyle * @glyph_color: color to use for drawing glyphs * @background_color: color to use for the selection background * * Render the selection specified by @selection for @page to * the given cairo context. The selection will be rendered, using * @glyph_color for the glyphs and @background_color for the selection * background. * * If non-NULL, @old_selection specifies the selection that is already * rendered to @cairo, in which case this function will (some day) * only render the changed part of the selection. **/ void poppler_page_render_selection(PopplerPage *page, cairo_t *cairo, PopplerRectangle *selection, PopplerRectangle *old_selection, PopplerSelectionStyle style, PopplerColor *glyph_color, PopplerColor *background_color) { CairoOutputDev *output_dev; TextPage *text; SelectionStyle selection_style = selectionStyleGlyph; PDFRectangle pdf_selection(selection->x1, selection->y1, selection->x2, selection->y2); GfxColor gfx_background_color = { { background_color->red, background_color->green, background_color->blue } }; GfxColor gfx_glyph_color = { { glyph_color->red, glyph_color->green, glyph_color->blue } }; switch (style) { case POPPLER_SELECTION_GLYPH: selection_style = selectionStyleGlyph; break; case POPPLER_SELECTION_WORD: selection_style = selectionStyleWord; break; case POPPLER_SELECTION_LINE: selection_style = selectionStyleLine; break; } output_dev = page->document->output_dev; output_dev->setCairo(cairo); text = poppler_page_get_text_page(page); text->drawSelection(output_dev, 1.0, 0, &pdf_selection, selection_style, &gfx_glyph_color, &gfx_background_color); output_dev->setCairo(nullptr); } /** * poppler_page_get_thumbnail_size: * @page: A #PopplerPage * @width: (out): return location for width * @height: (out): return location for height * * Returns %TRUE if @page has a thumbnail associated with it. It also * fills in @width and @height with the width and height of the * thumbnail. The values of width and height are not changed if no * appropriate thumbnail exists. * * Return value: %TRUE, if @page has a thumbnail associated with it. **/ gboolean poppler_page_get_thumbnail_size(PopplerPage *page, int *width, int *height) { Dict *dict; gboolean retval = FALSE; g_return_val_if_fail(POPPLER_IS_PAGE(page), FALSE); g_return_val_if_fail(width != nullptr, FALSE); g_return_val_if_fail(height != nullptr, FALSE); Object thumb = page->page->getThumb(); if (!thumb.isStream()) { return FALSE; } dict = thumb.streamGetDict(); /* Theoretically, this could succeed and you would still fail when * loading the thumb */ if (dict->lookupInt("Width", "W", width) && dict->lookupInt("Height", "H", height)) { retval = TRUE; } return retval; } /** * poppler_page_get_selection_region: * @page: a #PopplerPage * @scale: scale specified as pixels per point * @style: a #PopplerSelectionStyle * @selection: start and end point of selection as a rectangle * * Returns a region containing the area that would be rendered by * poppler_page_render_selection() as a #GList of * #PopplerRectangle. The returned list must be freed with * poppler_page_selection_region_free(). * * Return value: (element-type PopplerRectangle) (transfer full): a #GList of #PopplerRectangle * * Deprecated: 0.16: Use poppler_page_get_selected_region() instead. **/ GList *poppler_page_get_selection_region(PopplerPage *page, gdouble scale, PopplerSelectionStyle style, PopplerRectangle *selection) { PDFRectangle poppler_selection; TextPage *text; SelectionStyle selection_style = selectionStyleGlyph; GList *region = nullptr; poppler_selection.x1 = selection->x1; poppler_selection.y1 = selection->y1; poppler_selection.x2 = selection->x2; poppler_selection.y2 = selection->y2; switch (style) { case POPPLER_SELECTION_GLYPH: selection_style = selectionStyleGlyph; break; case POPPLER_SELECTION_WORD: selection_style = selectionStyleWord; break; case POPPLER_SELECTION_LINE: selection_style = selectionStyleLine; break; } text = poppler_page_get_text_page(page); std::vector *list = text->getSelectionRegion(&poppler_selection, selection_style, scale); for (const PDFRectangle *selection_rect : *list) { PopplerRectangle *rect; rect = poppler_rectangle_new_from_pdf_rectangle(selection_rect); region = g_list_prepend(region, rect); delete selection_rect; } delete list; return g_list_reverse(region); } /** * poppler_page_selection_region_free: * @region: (element-type PopplerRectangle): a #GList of * #PopplerRectangle * * Frees @region * * Deprecated: 0.16: 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. */ void poppler_page_selection_region_free(GList *region) { if (G_UNLIKELY(!region)) { return; } g_list_free_full(region, (GDestroyNotify)poppler_rectangle_free); } /** * poppler_page_get_selected_region: * @page: a #PopplerPage * @scale: scale specified as pixels per point * @style: a #PopplerSelectionStyle * @selection: start and end point of selection as a rectangle * * 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() * * Return value: (transfer full): a cairo_region_t * * Since: 0.16 **/ cairo_region_t *poppler_page_get_selected_region(PopplerPage *page, gdouble scale, PopplerSelectionStyle style, PopplerRectangle *selection) { PDFRectangle poppler_selection; TextPage *text; SelectionStyle selection_style = selectionStyleGlyph; cairo_region_t *region; poppler_selection.x1 = selection->x1; poppler_selection.y1 = selection->y1; poppler_selection.x2 = selection->x2; poppler_selection.y2 = selection->y2; switch (style) { case POPPLER_SELECTION_GLYPH: selection_style = selectionStyleGlyph; break; case POPPLER_SELECTION_WORD: selection_style = selectionStyleWord; break; case POPPLER_SELECTION_LINE: selection_style = selectionStyleLine; break; } text = poppler_page_get_text_page(page); std::vector *list = text->getSelectionRegion(&poppler_selection, selection_style, 1.0); region = cairo_region_create(); for (const PDFRectangle *selection_rect : *list) { cairo_rectangle_int_t rect; rect.x = (gint)((selection_rect->x1 * scale) + 0.5); rect.y = (gint)((selection_rect->y1 * scale) + 0.5); rect.width = (gint)(((selection_rect->x2 - selection_rect->x1) * scale) + 0.5); rect.height = (gint)(((selection_rect->y2 - selection_rect->y1) * scale) + 0.5); cairo_region_union_rectangle(region, &rect); delete selection_rect; } delete list; return region; } /** * poppler_page_get_selected_text: * @page: a #PopplerPage * @style: a #PopplerSelectionStyle * @selection: the #PopplerRectangle including the text * * Retrieves the contents of the specified @selection as text. * * Return value: 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; PDFRectangle pdf_selection; g_return_val_if_fail(POPPLER_IS_PAGE(page), NULL); g_return_val_if_fail(selection != nullptr, NULL); pdf_selection.x1 = selection->x1; pdf_selection.y1 = selection->y1; pdf_selection.x2 = selection->x2; pdf_selection.y2 = selection->y2; switch (style) { case POPPLER_SELECTION_GLYPH: selection_style = selectionStyleGlyph; break; case POPPLER_SELECTION_WORD: selection_style = selectionStyleWord; break; case POPPLER_SELECTION_LINE: selection_style = selectionStyleLine; break; } 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; return result; } /** * poppler_page_get_text: * @page: a #PopplerPage * * Retrieves the text of @page. * * Return value: a pointer to the text of the @page * as a string * Since: 0.16 **/ char *poppler_page_get_text(PopplerPage *page) { PopplerRectangle rectangle = { 0, 0, 0, 0 }; g_return_val_if_fail(POPPLER_IS_PAGE(page), NULL); poppler_page_get_size(page, &rectangle.x2, &rectangle.y2); return poppler_page_get_selected_text(page, POPPLER_SELECTION_GLYPH, &rectangle); } /** * poppler_page_get_text_for_area: * @page: a #PopplerPage * @area: a #PopplerRectangle * * Retrieves the text of @page contained in @area. * * Return value: a pointer to the text as a string * * Since: 0.26 **/ char *poppler_page_get_text_for_area(PopplerPage *page, PopplerRectangle *area) { g_return_val_if_fail(POPPLER_IS_PAGE(page), NULL); g_return_val_if_fail(area != nullptr, NULL); return poppler_page_get_selected_text(page, POPPLER_SELECTION_GLYPH, area); } /** * poppler_page_find_text_with_options: * @page: a #PopplerPage * @text: the text to search for (UTF-8 encoded) * @options: find 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. * The coordinates are in PDF points. * * 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 * 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 * 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. * * Return value: (element-type PopplerRectangle) (transfer full): a newly allocated list * of newly allocated #PopplerRectangle. Free with g_list_free_full() using poppler_rectangle_free(). * * Since: 0.22 **/ GList *poppler_page_find_text_with_options(PopplerPage *page, const char *text, PopplerFindFlags options) { PopplerRectangleExtended *match; GList *matches; double xMin, yMin, xMax, yMax; PDFRectangle continueMatch; bool ignoredHyphen; gunichar *ucs4; glong ucs4_len; double height; TextPage *text_dev; gboolean backwards; gboolean start_at_last = FALSE; g_return_val_if_fail(POPPLER_IS_PAGE(page), NULL); g_return_val_if_fail(text != nullptr, NULL); text_dev = poppler_page_get_text_page(page); ucs4 = g_utf8_to_ucs4_fast(text, -1, &ucs4_len); poppler_page_get_size(page, nullptr, &height); const bool multiline = (options & POPPLER_FIND_MULTILINE); backwards = options & POPPLER_FIND_BACKWARDS; matches = nullptr; xMin = 0; yMin = backwards ? height : 0; continueMatch.x1 = std::numeric_limits::max(); // we use this to detect valid returned values while (text_dev->findText(ucs4, ucs4_len, false, true, // startAtTop, stopAtBottom start_at_last, false, // stopAtLast options & POPPLER_FIND_CASE_SENSITIVE, options & POPPLER_FIND_IGNORE_DIACRITICS, options & POPPLER_FIND_MULTILINE, backwards, options & POPPLER_FIND_WHOLE_WORDS_ONLY, &xMin, &yMin, &xMax, &yMax, &continueMatch, &ignoredHyphen)) { match = poppler_rectangle_extended_new(); match->x1 = xMin; match->y1 = height - yMax; match->x2 = xMax; match->y2 = height - yMin; match->match_continued = false; match->ignored_hyphen = false; matches = g_list_prepend(matches, match); start_at_last = TRUE; if (continueMatch.x1 != std::numeric_limits::max()) { // received rect for next-line part of a multi-line match, add it. if (multiline) { match->match_continued = true; match->ignored_hyphen = ignoredHyphen; match = poppler_rectangle_extended_new(); match->x1 = continueMatch.x1; match->y1 = height - continueMatch.y1; match->x2 = continueMatch.x2; match->y2 = height - continueMatch.y2; match->match_continued = false; match->ignored_hyphen = false; matches = g_list_prepend(matches, match); } continueMatch.x1 = std::numeric_limits::max(); } } g_free(ucs4); return g_list_reverse(matches); } /** * poppler_page_find_text: * @page: a #PopplerPage * @text: the text to search for (UTF-8 encoded) * * 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. * The coordinates are in PDF points. * * Return value: (element-type PopplerRectangle) (transfer full): a #GList of #PopplerRectangle, **/ GList *poppler_page_find_text(PopplerPage *page, const char *text) { return poppler_page_find_text_with_options(page, text, POPPLER_FIND_DEFAULT); } 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(); if (imgDrawDeviceCbk) { 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; return image_dev; } /** * poppler_page_get_image_mapping: * @page: A #PopplerPage * * Returns a list of #PopplerImageMapping items that map from a * location on @page to an image of the page. This list must be freed * with poppler_page_free_image_mapping() when done. * * Return value: (element-type PopplerImageMapping) (transfer full): A #GList of #PopplerImageMapping **/ GList *poppler_page_get_image_mapping(PopplerPage *page) { GList *map_list = nullptr; CairoImageOutputDev *out; gint i; g_return_val_if_fail(POPPLER_IS_PAGE(page), NULL); out = poppler_page_get_image_output_dev(page, nullptr, nullptr); for (i = 0; i < out->getNumImages(); i++) { PopplerImageMapping *mapping; CairoImage *image; image = out->getImage(i); /* Create the mapping */ mapping = poppler_image_mapping_new(); image->getRect(&(mapping->area.x1), &(mapping->area.y1), &(mapping->area.x2), &(mapping->area.y2)); mapping->image_id = i; mapping->area.x1 -= page->page->getCropBox()->x1; mapping->area.x2 -= page->page->getCropBox()->x1; mapping->area.y1 -= page->page->getCropBox()->y1; mapping->area.y2 -= page->page->getCropBox()->y1; map_list = g_list_prepend(map_list, mapping); } delete out; return map_list; } static bool image_draw_decide_cb(int image_id, void *data) { return (image_id == GPOINTER_TO_INT(data)); } /** * poppler_page_get_image: * @page: A #PopplerPage * @image_id: The image identifier * * Returns a cairo surface for the image of the @page * * Return value: A cairo surface for the image **/ cairo_surface_t *poppler_page_get_image(PopplerPage *page, gint image_id) { CairoImageOutputDev *out; cairo_surface_t *image; g_return_val_if_fail(POPPLER_IS_PAGE(page), NULL); out = poppler_page_get_image_output_dev(page, image_draw_decide_cb, GINT_TO_POINTER(image_id)); if (image_id >= out->getNumImages()) { delete out; return nullptr; } image = out->getImage(image_id)->getImage(); if (!image) { delete out; return nullptr; } cairo_surface_reference(image); delete out; return image; } /** * poppler_page_free_image_mapping: * @list: (element-type PopplerImageMapping): A list of * #PopplerImageMappings * * Frees a list of #PopplerImageMappings allocated by * poppler_page_get_image_mapping(). **/ void poppler_page_free_image_mapping(GList *list) { if (G_UNLIKELY(list == nullptr)) { return; } g_list_free_full(list, (GDestroyNotify)poppler_image_mapping_free); } /** * poppler_page_render_to_ps: * @page: a #PopplerPage * @ps_file: the PopplerPSFile to render to * * Render the page on a postscript file * **/ void poppler_page_render_to_ps(PopplerPage *page, PopplerPSFile *ps_file) { g_return_if_fail(POPPLER_IS_PAGE(page)); g_return_if_fail(ps_file != nullptr); if (!ps_file->out) { std::vector pages; for (int i = ps_file->first_page; i <= ps_file->last_page; ++i) { pages.push_back(i); } if (ps_file->fd != -1) { ps_file->out = new PSOutputDev(ps_file->fd, ps_file->document->doc, nullptr, pages, psModePS, (int)ps_file->paper_width, (int)ps_file->paper_height, false, ps_file->duplex, 0, 0, 0, 0, psRasterizeWhenNeeded, false, nullptr, nullptr); } else { ps_file->out = new PSOutputDev(ps_file->filename, ps_file->document->doc, nullptr, pages, psModePS, (int)ps_file->paper_width, (int)ps_file->paper_height, false, ps_file->duplex, 0, 0, 0, 0, psRasterizeWhenNeeded, false, nullptr, nullptr); } } ps_file->document->doc->displayPage(ps_file->out, page->index + 1, 72.0, 72.0, 0, false, true, false); } static void poppler_page_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { PopplerPage *page = POPPLER_PAGE(object); switch (prop_id) { case PROP_LABEL: g_value_take_string(value, poppler_page_get_label(page)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); } } static void poppler_page_class_init(PopplerPageClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS(klass); gobject_class->finalize = poppler_page_finalize; gobject_class->get_property = poppler_page_get_property; /** * PopplerPage:label: * * The label of the page or %NULL. See also poppler_page_get_label() */ g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_LABEL, g_param_spec_string("label", "Page Label", "The label of the page", nullptr, G_PARAM_READABLE)); } static void poppler_page_init(PopplerPage *page) { } /** * poppler_page_get_link_mapping: * @page: A #PopplerPage * * Returns a list of #PopplerLinkMapping items that map from a * location on @page to a #PopplerAction. This list must be freed * with poppler_page_free_link_mapping() when done. * * Return value: (element-type PopplerLinkMapping) (transfer full): A #GList of #PopplerLinkMapping **/ GList *poppler_page_get_link_mapping(PopplerPage *page) { GList *map_list = nullptr; Links *links; double width, height; g_return_val_if_fail(POPPLER_IS_PAGE(page), NULL); links = new Links(page->page->getAnnots()); if (links == nullptr) { return nullptr; } poppler_page_get_size(page, &width, &height); for (AnnotLink *link : links->getLinks()) { PopplerLinkMapping *mapping; PopplerRectangle rect; LinkAction *link_action; link_action = link->getAction(); /* Create the mapping */ mapping = poppler_link_mapping_new(); mapping->action = _poppler_action_new(page->document, link_action, nullptr); link->getRect(&rect.x1, &rect.y1, &rect.x2, &rect.y2); rect.x1 -= page->page->getCropBox()->x1; rect.x2 -= page->page->getCropBox()->x1; rect.y1 -= page->page->getCropBox()->y1; rect.y2 -= page->page->getCropBox()->y1; switch (page->page->getRotate()) { case 90: mapping->area.x1 = rect.y1; mapping->area.y1 = height - rect.x2; mapping->area.x2 = mapping->area.x1 + (rect.y2 - rect.y1); mapping->area.y2 = mapping->area.y1 + (rect.x2 - rect.x1); break; case 180: mapping->area.x1 = width - rect.x2; mapping->area.y1 = height - rect.y2; mapping->area.x2 = mapping->area.x1 + (rect.x2 - rect.x1); mapping->area.y2 = mapping->area.y1 + (rect.y2 - rect.y1); break; case 270: mapping->area.x1 = width - rect.y2; mapping->area.y1 = rect.x1; mapping->area.x2 = mapping->area.x1 + (rect.y2 - rect.y1); mapping->area.y2 = mapping->area.y1 + (rect.x2 - rect.x1); break; default: mapping->area.x1 = rect.x1; mapping->area.y1 = rect.y1; mapping->area.x2 = rect.x2; mapping->area.y2 = rect.y2; } map_list = g_list_prepend(map_list, mapping); } delete links; return map_list; } /** * poppler_page_free_link_mapping: * @list: (element-type PopplerLinkMapping): A list of * #PopplerLinkMappings * * 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 * copy them with poppler_action_copy(). **/ void poppler_page_free_link_mapping(GList *list) { if (G_UNLIKELY(list == nullptr)) { return; } g_list_free_full(list, (GDestroyNotify)poppler_link_mapping_free); } /** * poppler_page_get_form_field_mapping: * @page: A #PopplerPage * * Returns a list of #PopplerFormFieldMapping items that map from a * location on @page to a form field. This list must be freed * with poppler_page_free_form_field_mapping() when done. * * Return value: (element-type PopplerFormFieldMapping) (transfer full): A #GList of #PopplerFormFieldMapping **/ GList *poppler_page_get_form_field_mapping(PopplerPage *page) { GList *map_list = nullptr; gint i; g_return_val_if_fail(POPPLER_IS_PAGE(page), NULL); const std::unique_ptr forms = page->page->getFormWidgets(); if (forms == nullptr) { return nullptr; } for (i = 0; i < forms->getNumWidgets(); i++) { PopplerFormFieldMapping *mapping; FormWidget *field; mapping = poppler_form_field_mapping_new(); field = forms->getWidget(i); mapping->field = _poppler_form_field_new(page->document, field); field->getRect(&(mapping->area.x1), &(mapping->area.y1), &(mapping->area.x2), &(mapping->area.y2)); mapping->area.x1 -= page->page->getCropBox()->x1; mapping->area.x2 -= page->page->getCropBox()->x1; mapping->area.y1 -= page->page->getCropBox()->y1; mapping->area.y2 -= page->page->getCropBox()->y1; map_list = g_list_prepend(map_list, mapping); } return map_list; } /** * poppler_page_free_form_field_mapping: * @list: (element-type PopplerFormFieldMapping): A list of * #PopplerFormFieldMappings * * Frees a list of #PopplerFormFieldMappings allocated by * poppler_page_get_form_field_mapping(). **/ void poppler_page_free_form_field_mapping(GList *list) { if (G_UNLIKELY(list == nullptr)) { return; } g_list_free_full(list, (GDestroyNotify)poppler_form_field_mapping_free); } /** * poppler_page_get_annot_mapping: * @page: A #PopplerPage * * Returns a list of #PopplerAnnotMapping items that map from a location on * @page to a #PopplerAnnot. This list must be freed with * poppler_page_free_annot_mapping() when done. * * Return value: (element-type PopplerAnnotMapping) (transfer full): A #GList of #PopplerAnnotMapping **/ GList *poppler_page_get_annot_mapping(PopplerPage *page) { GList *map_list = nullptr; double width, height; Annots *annots; const PDFRectangle *crop_box; g_return_val_if_fail(POPPLER_IS_PAGE(page), NULL); annots = page->page->getAnnots(); if (!annots) { return nullptr; } poppler_page_get_size(page, &width, &height); crop_box = page->page->getCropBox(); for (Annot *annot : annots->getAnnots()) { PopplerAnnotMapping *mapping; PopplerRectangle rect; gboolean flag_no_rotate; gint rotation = 0; flag_no_rotate = annot->getFlags() & Annot::flagNoRotate; /* Create the mapping */ mapping = poppler_annot_mapping_new(); switch (annot->getType()) { case Annot::typeText: mapping->annot = _poppler_annot_text_new(annot); break; case Annot::typeFreeText: mapping->annot = _poppler_annot_free_text_new(annot); break; case Annot::typeFileAttachment: mapping->annot = _poppler_annot_file_attachment_new(annot); break; case Annot::typeMovie: mapping->annot = _poppler_annot_movie_new(annot); break; case Annot::typeScreen: mapping->annot = _poppler_annot_screen_new(page->document, annot); break; case Annot::typeLine: mapping->annot = _poppler_annot_line_new(annot); break; case Annot::typeSquare: mapping->annot = _poppler_annot_square_new(annot); break; case Annot::typeCircle: mapping->annot = _poppler_annot_circle_new(annot); break; case Annot::typeHighlight: case Annot::typeUnderline: case Annot::typeSquiggly: case Annot::typeStrikeOut: mapping->annot = _poppler_annot_text_markup_new(annot); break; case Annot::typeStamp: mapping->annot = _poppler_annot_stamp_new(annot); break; default: mapping->annot = _poppler_annot_new(annot); break; } const PDFRectangle &annot_rect = annot->getRect(); rect.x1 = annot_rect.x1 - crop_box->x1; rect.y1 = annot_rect.y1 - crop_box->y1; rect.x2 = annot_rect.x2 - crop_box->x1; rect.y2 = annot_rect.y2 - crop_box->y1; rotation = page->page->getRotate(); if (rotation == 0 || !SUPPORTED_ROTATION(rotation)) { /* zero or unknown rotation */ mapping->area.x1 = rect.x1; mapping->area.y1 = rect.y1; mapping->area.x2 = rect.x2; mapping->area.y2 = rect.y2; } else { gdouble annot_height = rect.y2 - rect.y1; gdouble annot_width = rect.x2 - rect.x1; if (flag_no_rotate) { if (rotation == 90) { mapping->area.x1 = rect.y2; mapping->area.y1 = height - (rect.x1 + annot_height); mapping->area.x2 = rect.y2 + annot_width; mapping->area.y2 = height - rect.x1; } else if (rotation == 180) { mapping->area.x1 = width - rect.x1; mapping->area.x2 = MIN(mapping->area.x1 + annot_width, width); mapping->area.y2 = height - rect.y2; mapping->area.y1 = MAX(0, mapping->area.y2 - annot_height); } else if (rotation == 270) { mapping->area.x1 = width - rect.y2; mapping->area.x2 = MIN(mapping->area.x1 + annot_width, width); mapping->area.y2 = rect.x1; mapping->area.y1 = MAX(0, mapping->area.y2 - annot_height); } } else { /* !flag_no_rotate */ if (rotation == 90) { mapping->area.x1 = rect.y1; mapping->area.y1 = height - rect.x2; mapping->area.x2 = mapping->area.x1 + annot_height; mapping->area.y2 = mapping->area.y1 + annot_width; } else if (rotation == 180) { mapping->area.x1 = width - rect.x2; mapping->area.y1 = height - rect.y2; mapping->area.x2 = mapping->area.x1 + annot_width; mapping->area.y2 = mapping->area.y1 + annot_height; } else if (rotation == 270) { mapping->area.x1 = width - rect.y2; mapping->area.y1 = rect.x1; mapping->area.x2 = mapping->area.x1 + annot_height; mapping->area.y2 = mapping->area.y1 + annot_width; } } } map_list = g_list_prepend(map_list, mapping); } return g_list_reverse(map_list); } /** * poppler_page_free_annot_mapping: * @list: (element-type PopplerAnnotMapping): A list of * #PopplerAnnotMappings * * 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(). **/ void poppler_page_free_annot_mapping(GList *list) { if (G_UNLIKELY(!list)) { return; } g_list_free_full(list, (GDestroyNotify)poppler_annot_mapping_free); } /* Adds or removes (according to @add parameter) the passed in @crop_box from the * passed in @quads and returns it as a new #AnnotQuadrilaterals object */ AnnotQuadrilaterals *new_quads_from_offset_cropbox(const PDFRectangle *crop_box, AnnotQuadrilaterals *quads, gboolean add) { int len = quads->getQuadrilateralsLength(); auto quads_array = std::make_unique(len); for (int i = 0; i < len; i++) { if (add) { quads_array[i] = AnnotQuadrilaterals::AnnotQuadrilateral(quads->getX1(i) + crop_box->x1, quads->getY1(i) + crop_box->y1, quads->getX2(i) + crop_box->x1, quads->getY2(i) + crop_box->y1, quads->getX3(i) + crop_box->x1, quads->getY3(i) + crop_box->y1, quads->getX4(i) + crop_box->x1, quads->getY4(i) + crop_box->y1); } else { quads_array[i] = AnnotQuadrilaterals::AnnotQuadrilateral(quads->getX1(i) - crop_box->x1, quads->getY1(i) - crop_box->y1, quads->getX2(i) - crop_box->x1, quads->getY2(i) - crop_box->y1, quads->getX3(i) - crop_box->x1, quads->getY3(i) - crop_box->y1, quads->getX4(i) - crop_box->x1, quads->getY4(i) - crop_box->y1); } } return new AnnotQuadrilaterals(std::move(quads_array), len); } /* This function undoes the rotation of @page in the passed-in @x @y point. * In other words, it moves the point to where it'll be located if @page * was put to zero rotation (unrotated) */ static void _page_unrotate_xy(Page *page, double *x, double *y) { double page_width, page_height, temp; gint rotation = page->getRotate(); if (rotation == 90 || rotation == 270) { page_height = page->getCropWidth(); page_width = page->getCropHeight(); } else { page_width = page->getCropWidth(); page_height = page->getCropHeight(); } if (rotation == 90) { temp = *x; *x = page_height - *y; *y = temp; } else if (rotation == 180) { *x = page_width - *x; *y = page_height - *y; } else if (rotation == 270) { temp = *x; *x = *y; *y = page_width - temp; } } AnnotQuadrilaterals *_page_new_quads_unrotated(Page *page, AnnotQuadrilaterals *quads) { double x1, y1, x2, y2, x3, y3, x4, y4; int len = quads->getQuadrilateralsLength(); auto quads_array = std::make_unique(len); for (int i = 0; i < len; i++) { x1 = quads->getX1(i); y1 = quads->getY1(i); x2 = quads->getX2(i); y2 = quads->getY2(i); x3 = quads->getX3(i); y3 = quads->getY3(i); x4 = quads->getX4(i); y4 = quads->getY4(i); _page_unrotate_xy(page, &x1, &y1); _page_unrotate_xy(page, &x2, &y2); _page_unrotate_xy(page, &x3, &y3); _page_unrotate_xy(page, &x4, &y4); quads_array[i] = AnnotQuadrilaterals::AnnotQuadrilateral(x1, y1, x2, y2, x3, y3, x4, y4); } return new AnnotQuadrilaterals(std::move(quads_array), len); } /* @x1 @y1 @x2 @y2 are both 'in' and 'out' parameters, representing * the diagonal of a rectangle which is the 'rect' area of @annot * which is inside @page. * * If @page is unrotated (i.e. has zero rotation) this function does * nothing, otherwise this function un-rotates the passed-in rect * coords according to @page rotation so as the returned coords are * those of the rect if page was put to zero rotation (unrotated). * This is mandated by PDF spec when saving annotation coords (see * 8.4.2 Annotation Flags) which also explains the special rotation * that needs to be done when @annot has the flagNoRotate set to * true, which this function follows. */ void _unrotate_rect_for_annot_and_page(Page *page, Annot *annot, double *x1, double *y1, double *x2, double *y2) { gboolean flag_no_rotate; if (!SUPPORTED_ROTATION(page->getRotate())) { return; } /* Normalize received rect diagonal to be from UpperLeft to BottomRight, * as our algorithm follows that */ if (*y2 > *y1) { double temp = *y1; *y1 = *y2; *y2 = temp; } if (G_UNLIKELY(*x1 > *x2)) { double temp = *x1; *x1 = *x2; *x2 = temp; } flag_no_rotate = annot->getFlags() & Annot::flagNoRotate; if (flag_no_rotate) { /* For this case rotating just the upperleft point is enough */ double annot_height = *y1 - *y2; double annot_width = *x2 - *x1; _page_unrotate_xy(page, x1, y1); *x2 = *x1 + annot_width; *y2 = *y1 - annot_height; } else { _page_unrotate_xy(page, x1, y1); _page_unrotate_xy(page, x2, y2); } } /** * poppler_page_add_annot: * @page: a #PopplerPage * @annot: a #PopplerAnnot to add * * Adds annotation @annot to @page. * * Since: 0.16 */ void poppler_page_add_annot(PopplerPage *page, PopplerAnnot *annot) { double x1, y1, x2, y2; gboolean page_is_rotated; const PDFRectangle *crop_box; const PDFRectangle *page_crop_box; g_return_if_fail(POPPLER_IS_PAGE(page)); g_return_if_fail(POPPLER_IS_ANNOT(annot)); /* Add the page's cropBox to the coordinates of rect field of annot */ page_crop_box = page->page->getCropBox(); annot->annot->getRect(&x1, &y1, &x2, &y2); page_is_rotated = SUPPORTED_ROTATION(page->page->getRotate()); if (page_is_rotated) { /* annot is inside a rotated page, as core poppler rect must be saved * un-rotated, let's proceed to un-rotate rect before saving */ _unrotate_rect_for_annot_and_page(page->page, annot->annot, &x1, &y1, &x2, &y2); } annot->annot->setRect(x1 + page_crop_box->x1, y1 + page_crop_box->y1, x2 + page_crop_box->x1, y2 + page_crop_box->y1); 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); } 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); } /* 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); } page->page->addAnnot(annot->annot); } /** * poppler_page_remove_annot: * @page: a #PopplerPage * @annot: a #PopplerAnnot to remove * * Removes annotation @annot from @page * * Since: 0.22 */ void poppler_page_remove_annot(PopplerPage *page, PopplerAnnot *annot) { g_return_if_fail(POPPLER_IS_PAGE(page)); g_return_if_fail(POPPLER_IS_ANNOT(annot)); page->page->removeAnnot(annot->annot); } /* PopplerRectangle type */ G_DEFINE_BOXED_TYPE(PopplerRectangle, poppler_rectangle, poppler_rectangle_copy, poppler_rectangle_free) static PopplerRectangleExtended *poppler_rectangle_extended_new() { return g_slice_new0(PopplerRectangleExtended); } PopplerRectangle *poppler_rectangle_new_from_pdf_rectangle(const PDFRectangle *rect) { auto r = poppler_rectangle_extended_new(); r->x1 = rect->x1; r->y1 = rect->y1; r->x2 = rect->x2; r->y2 = rect->y2; return reinterpret_cast(r); } /** * poppler_rectangle_new: * * Creates a new #PopplerRectangle * * Returns: a new #PopplerRectangle, use poppler_rectangle_free() to free it */ PopplerRectangle *poppler_rectangle_new(void) { return reinterpret_cast(poppler_rectangle_extended_new()); } /** * poppler_rectangle_copy: * @rectangle: a #PopplerRectangle to copy * * Creates a copy of @rectangle. * * Note that you must only use this function on an allocated PopplerRectangle, as * returned by poppler_rectangle_new(), poppler_rectangle_copy(), or the list elements * returned from poppler_page_find_text() or poppler_page_find_text_with_options(). * Returns: a new allocated copy of @rectangle */ PopplerRectangle *poppler_rectangle_copy(PopplerRectangle *rectangle) { g_return_val_if_fail(rectangle != nullptr, NULL); auto ext_rectangle = reinterpret_cast(rectangle); return reinterpret_cast(g_slice_dup(PopplerRectangleExtended, ext_rectangle)); } /** * poppler_rectangle_free: * @rectangle: a #PopplerRectangle * * Frees the given #PopplerRectangle. * * Note that you must only use this function on an allocated PopplerRectangle, as * returned by poppler_rectangle_new(), poppler_rectangle_copy(), or the list elements * returned from poppler_page_find_text() or poppler_page_find_text_with_options(). */ void poppler_rectangle_free(PopplerRectangle *rectangle) { auto ext_rectangle = reinterpret_cast(rectangle); g_slice_free(PopplerRectangleExtended, ext_rectangle); } /** * poppler_rectangle_find_get_match_continued: * @rectangle: a #PopplerRectangle * * When using poppler_page_find_text_with_options() with the * %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. * * 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(). * * Returns: whether there are more rectangles belonging to the same match * * Since: 21.05.0 */ gboolean poppler_rectangle_find_get_match_continued(const PopplerRectangle *rectangle) { g_return_val_if_fail(rectangle != nullptr, false); auto ext_rectangle = reinterpret_cast(rectangle); return ext_rectangle->match_continued; } /** * poppler_rectangle_find_get_ignored_hyphen: * @rectangle: a #PopplerRectangle * * When using poppler_page_find_text_with_options() with the * %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. * * 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(). * * Returns: whether a hyphen was ignored at the end of the line corresponding to @rectangle. * * Since: 21.05.0 */ gboolean poppler_rectangle_find_get_ignored_hyphen(const PopplerRectangle *rectangle) { g_return_val_if_fail(rectangle != nullptr, false); auto ext_rectangle = reinterpret_cast(rectangle); return ext_rectangle->ignored_hyphen; } G_DEFINE_BOXED_TYPE(PopplerPoint, poppler_point, poppler_point_copy, poppler_point_free) /** * poppler_point_new: * * Creates a new #PopplerPoint. It must be freed with poppler_point_free() after use. * * Returns: a new #PopplerPoint * * Since: 0.26 **/ PopplerPoint *poppler_point_new(void) { return g_slice_new0(PopplerPoint); } /** * poppler_point_copy: * @point: a #PopplerPoint to copy * * Creates a copy of @point. The copy must be freed with poppler_point_free() * after use. * * Returns: a new allocated copy of @point * * Since: 0.26 **/ PopplerPoint *poppler_point_copy(PopplerPoint *point) { g_return_val_if_fail(point != nullptr, NULL); return g_slice_dup(PopplerPoint, point); } /** * poppler_point_free: * @point: a #PopplerPoint * * Frees the memory used by @point * * Since: 0.26 **/ void poppler_point_free(PopplerPoint *point) { g_slice_free(PopplerPoint, point); } /* PopplerQuadrilateral type */ G_DEFINE_BOXED_TYPE(PopplerQuadrilateral, poppler_quadrilateral, poppler_quadrilateral_copy, poppler_quadrilateral_free) /** * poppler_quadrilateral_new: * * Creates a new #PopplerQuadrilateral. It must be freed with poppler_quadrilateral_free() after use. * * Returns: a new #PopplerQuadrilateral. * * Since: 0.26 **/ PopplerQuadrilateral *poppler_quadrilateral_new(void) { return g_slice_new0(PopplerQuadrilateral); } /** * poppler_quadrilateral_copy: * @quad: a #PopplerQuadrilateral to copy * * Creates a copy of @quad. The copy must be freed with poppler_quadrilateral_free() after use. * * Returns: a new allocated copy of @quad * * Since: 0.26 **/ PopplerQuadrilateral *poppler_quadrilateral_copy(PopplerQuadrilateral *quad) { g_return_val_if_fail(quad != nullptr, NULL); return g_slice_dup(PopplerQuadrilateral, quad); } /** * poppler_quadrilateral_free: * @quad: a #PopplerQuadrilateral * * Frees the memory used by @quad * * Since: 0.26 **/ void poppler_quadrilateral_free(PopplerQuadrilateral *quad) { g_slice_free(PopplerQuadrilateral, quad); } /* PopplerTextAttributes type */ G_DEFINE_BOXED_TYPE(PopplerTextAttributes, poppler_text_attributes, poppler_text_attributes_copy, poppler_text_attributes_free) /** * poppler_text_attributes_new: * * Creates a new #PopplerTextAttributes * * Returns: a new #PopplerTextAttributes, use poppler_text_attributes_free() to free it * * Since: 0.18 */ PopplerTextAttributes *poppler_text_attributes_new(void) { return (PopplerTextAttributes *)g_slice_new0(PopplerTextAttributes); } static gchar *get_font_name_from_word(const TextWord *word, gint word_i) { const GooString *font_name = word->getFontName(word_i); const gchar *name; gboolean subset; gint i; if (!font_name || font_name->getLength() == 0) { return g_strdup("Default"); } // check for a font subset name: capital letters followed by a '+' sign for (i = 0; i < font_name->getLength(); ++i) { if (font_name->getChar(i) < 'A' || font_name->getChar(i) > 'Z') { break; } } subset = i > 0 && i < font_name->getLength() && font_name->getChar(i) == '+'; name = font_name->c_str(); if (subset) { name += i + 1; } return g_strdup(name); } /* * Allocates a new PopplerTextAttributes with word attributes */ static PopplerTextAttributes *poppler_text_attributes_new_from_word(const TextWord *word, gint i) { PopplerTextAttributes *attrs = poppler_text_attributes_new(); gdouble r, g, b; attrs->font_name = get_font_name_from_word(word, i); attrs->font_size = word->getFontSize(); attrs->is_underlined = word->isUnderlined(); word->getColor(&r, &g, &b); attrs->color.red = (int)(r * 65535. + 0.5); attrs->color.green = (int)(g * 65535. + 0.5); attrs->color.blue = (int)(b * 65535. + 0.5); return attrs; } /** * poppler_text_attributes_copy: * @text_attrs: a #PopplerTextAttributes to copy * * Creates a copy of @text_attrs * * Returns: a new allocated copy of @text_attrs * * Since: 0.18 */ PopplerTextAttributes *poppler_text_attributes_copy(PopplerTextAttributes *text_attrs) { PopplerTextAttributes *attrs; attrs = g_slice_dup(PopplerTextAttributes, text_attrs); attrs->font_name = g_strdup(text_attrs->font_name); return attrs; } /** * poppler_text_attributes_free: * @text_attrs: a #PopplerTextAttributes * * Frees the given #PopplerTextAttributes * * Since: 0.18 */ void poppler_text_attributes_free(PopplerTextAttributes *text_attrs) { g_free(text_attrs->font_name); g_slice_free(PopplerTextAttributes, text_attrs); } /** * SECTION:poppler-color * @short_description: Colors * @title: PopplerColor */ /* PopplerColor type */ G_DEFINE_BOXED_TYPE(PopplerColor, poppler_color, poppler_color_copy, poppler_color_free) /** * poppler_color_new: * * Creates a new #PopplerColor * * Returns: a new #PopplerColor, use poppler_color_free() to free it */ PopplerColor *poppler_color_new(void) { return (PopplerColor *)g_new0(PopplerColor, 1); } /** * poppler_color_copy: * @color: a #PopplerColor to copy * * Creates a copy of @color * * Returns: a new allocated copy of @color */ PopplerColor *poppler_color_copy(PopplerColor *color) { PopplerColor *new_color; new_color = g_new(PopplerColor, 1); *new_color = *color; return new_color; } /** * poppler_color_free: * @color: a #PopplerColor * * Frees the given #PopplerColor */ void poppler_color_free(PopplerColor *color) { g_free(color); } /* PopplerLinkMapping type */ G_DEFINE_BOXED_TYPE(PopplerLinkMapping, poppler_link_mapping, poppler_link_mapping_copy, poppler_link_mapping_free) /** * poppler_link_mapping_new: * * Creates a new #PopplerLinkMapping * * Returns: a new #PopplerLinkMapping, use poppler_link_mapping_free() to free it */ PopplerLinkMapping *poppler_link_mapping_new(void) { return g_slice_new0(PopplerLinkMapping); } /** * poppler_link_mapping_copy: * @mapping: a #PopplerLinkMapping to copy * * Creates a copy of @mapping * * Returns: a new allocated copy of @mapping */ PopplerLinkMapping *poppler_link_mapping_copy(PopplerLinkMapping *mapping) { PopplerLinkMapping *new_mapping; new_mapping = g_slice_dup(PopplerLinkMapping, mapping); if (new_mapping->action) { new_mapping->action = poppler_action_copy(new_mapping->action); } return new_mapping; } /** * poppler_link_mapping_free: * @mapping: a #PopplerLinkMapping * * Frees the given #PopplerLinkMapping */ void poppler_link_mapping_free(PopplerLinkMapping *mapping) { if (G_UNLIKELY(!mapping)) { return; } if (mapping->action) { poppler_action_free(mapping->action); } g_slice_free(PopplerLinkMapping, mapping); } /* Poppler Image mapping type */ G_DEFINE_BOXED_TYPE(PopplerImageMapping, poppler_image_mapping, poppler_image_mapping_copy, poppler_image_mapping_free) /** * poppler_image_mapping_new: * * Creates a new #PopplerImageMapping * * Returns: a new #PopplerImageMapping, use poppler_image_mapping_free() to free it */ PopplerImageMapping *poppler_image_mapping_new(void) { return g_slice_new0(PopplerImageMapping); } /** * poppler_image_mapping_copy: * @mapping: a #PopplerImageMapping to copy * * Creates a copy of @mapping * * Returns: a new allocated copy of @mapping */ PopplerImageMapping *poppler_image_mapping_copy(PopplerImageMapping *mapping) { return g_slice_dup(PopplerImageMapping, mapping); } /** * poppler_image_mapping_free: * @mapping: a #PopplerImageMapping * * Frees the given #PopplerImageMapping */ void poppler_image_mapping_free(PopplerImageMapping *mapping) { g_slice_free(PopplerImageMapping, mapping); } /* Page Transition */ G_DEFINE_BOXED_TYPE(PopplerPageTransition, poppler_page_transition, poppler_page_transition_copy, poppler_page_transition_free) /** * poppler_page_transition_new: * * Creates a new #PopplerPageTransition * * Returns: a new #PopplerPageTransition, use poppler_page_transition_free() to free it */ PopplerPageTransition *poppler_page_transition_new(void) { return (PopplerPageTransition *)g_new0(PopplerPageTransition, 1); } /** * poppler_page_transition_copy: * @transition: a #PopplerPageTransition to copy * * Creates a copy of @transition * * Returns: a new allocated copy of @transition */ PopplerPageTransition *poppler_page_transition_copy(PopplerPageTransition *transition) { PopplerPageTransition *new_transition; new_transition = poppler_page_transition_new(); *new_transition = *transition; return new_transition; } /** * poppler_page_transition_free: * @transition: a #PopplerPageTransition * * Frees the given #PopplerPageTransition */ void poppler_page_transition_free(PopplerPageTransition *transition) { g_free(transition); } /* Form Field Mapping Type */ G_DEFINE_BOXED_TYPE(PopplerFormFieldMapping, poppler_form_field_mapping, poppler_form_field_mapping_copy, poppler_form_field_mapping_free) /** * poppler_form_field_mapping_new: * * Creates a new #PopplerFormFieldMapping * * Returns: a new #PopplerFormFieldMapping, use poppler_form_field_mapping_free() to free it */ PopplerFormFieldMapping *poppler_form_field_mapping_new(void) { return g_slice_new0(PopplerFormFieldMapping); } /** * poppler_form_field_mapping_copy: * @mapping: a #PopplerFormFieldMapping to copy * * Creates a copy of @mapping * * Returns: a new allocated copy of @mapping */ PopplerFormFieldMapping *poppler_form_field_mapping_copy(PopplerFormFieldMapping *mapping) { PopplerFormFieldMapping *new_mapping; new_mapping = g_slice_dup(PopplerFormFieldMapping, mapping); if (mapping->field) { new_mapping->field = (PopplerFormField *)g_object_ref(mapping->field); } return new_mapping; } /** * poppler_form_field_mapping_free: * @mapping: a #PopplerFormFieldMapping * * Frees the given #PopplerFormFieldMapping */ void poppler_form_field_mapping_free(PopplerFormFieldMapping *mapping) { if (G_UNLIKELY(!mapping)) { return; } if (mapping->field) { g_object_unref(mapping->field); } g_slice_free(PopplerFormFieldMapping, mapping); } /* PopplerAnnot Mapping Type */ G_DEFINE_BOXED_TYPE(PopplerAnnotMapping, poppler_annot_mapping, poppler_annot_mapping_copy, poppler_annot_mapping_free) /** * poppler_annot_mapping_new: * * Creates a new #PopplerAnnotMapping * * Returns: a new #PopplerAnnotMapping, use poppler_annot_mapping_free() to free it */ PopplerAnnotMapping *poppler_annot_mapping_new(void) { return g_slice_new0(PopplerAnnotMapping); } /** * poppler_annot_mapping_copy: * @mapping: a #PopplerAnnotMapping to copy * * Creates a copy of @mapping * * Returns: a new allocated copy of @mapping */ PopplerAnnotMapping *poppler_annot_mapping_copy(PopplerAnnotMapping *mapping) { PopplerAnnotMapping *new_mapping; new_mapping = g_slice_dup(PopplerAnnotMapping, mapping); if (mapping->annot) { new_mapping->annot = (PopplerAnnot *)g_object_ref(mapping->annot); } return new_mapping; } /** * poppler_annot_mapping_free: * @mapping: a #PopplerAnnotMapping * * Frees the given #PopplerAnnotMapping */ void poppler_annot_mapping_free(PopplerAnnotMapping *mapping) { if (G_UNLIKELY(!mapping)) { return; } if (mapping->annot) { g_object_unref(mapping->annot); } g_slice_free(PopplerAnnotMapping, mapping); } /** * poppler_page_get_crop_box: * @page: a #PopplerPage * @rect: (out): a #PopplerRectangle to fill * * Retrurns the crop box of @page */ void poppler_page_get_crop_box(PopplerPage *page, PopplerRectangle *rect) { const PDFRectangle *cropBox = page->page->getCropBox(); rect->x1 = cropBox->x1; rect->x2 = cropBox->x2; rect->y1 = cropBox->y1; rect->y2 = cropBox->y2; } /* * poppler_page_get_bounding_box: * @page: A #PopplerPage * @rect: (out) return the bounding box of the page * * Returns the bounding box of the page, a rectangle enclosing all text, vector * graphics (lines, rectangles and curves) and raster images in the page. * Includes invisible text but not (yet) annotations like highlights and form * elements. * * Return value: %TRUE if the page contains graphics, %FALSE otherwise * * Since: 0.88 */ gboolean poppler_page_get_bounding_box(PopplerPage *page, PopplerRectangle *rect) { BBoxOutputDev *bb_out; bool hasGraphics; g_return_val_if_fail(POPPLER_IS_PAGE(page), false); g_return_val_if_fail(rect != nullptr, false); bb_out = new BBoxOutputDev(); page->page->displaySlice(bb_out, 72.0, 72.0, 0, false, /* useMediaBox */ true, /* Crop */ -1, -1, -1, -1, false, /* printing */ nullptr, nullptr, nullptr, nullptr); hasGraphics = bb_out->getHasGraphics(); if (hasGraphics) { rect->x1 = bb_out->getX1(); rect->y1 = bb_out->getY1(); rect->x2 = bb_out->getX2(); rect->y2 = bb_out->getY2(); } delete bb_out; return hasGraphics; } /** * poppler_page_get_text_layout: * @page: A #PopplerPage * @rectangles: (out) (array length=n_rectangles) (transfer container): return location for an array of #PopplerRectangle * @n_rectangles: (out): length of returned array * * Obtains the layout of the text as a list of #PopplerRectangle * 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(). * * Return value: %TRUE if the page contains text, %FALSE otherwise * * Since: 0.16 **/ gboolean poppler_page_get_text_layout(PopplerPage *page, PopplerRectangle **rectangles, guint *n_rectangles) { PopplerRectangle selection = { 0, 0, 0, 0 }; g_return_val_if_fail(POPPLER_IS_PAGE(page), FALSE); poppler_page_get_size(page, &selection.x2, &selection.y2); return poppler_page_get_text_layout_for_area(page, &selection, rectangles, n_rectangles); } /** * poppler_page_get_text_layout_for_area: * @page: A #PopplerPage * @area: a #PopplerRectangle * @rectangles: (out) (array length=n_rectangles) (transfer container): return location for an array of #PopplerRectangle * @n_rectangles: (out): length of returned array * * Obtains the layout of the text contained in @area as a list of #PopplerRectangle * 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() * * Return value: %TRUE if the page contains text, %FALSE otherwise * * Since: 0.26 **/ gboolean poppler_page_get_text_layout_for_area(PopplerPage *page, PopplerRectangle *area, PopplerRectangle **rectangles, guint *n_rectangles) { TextPage *text; PopplerRectangle *rect; PDFRectangle selection; int i, k; guint offset = 0; guint n_rects = 0; gdouble x1, y1, x2, y2; 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); *n_rectangles = 0; selection.x1 = area->x1; selection.y1 = area->y1; selection.x2 = area->x2; selection.y2 = area->y2; text = poppler_page_get_text_page(page); std::vector **word_list = text->getSelectionWords(&selection, selectionStyleGlyph, &n_lines); if (!word_list) { 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_sel->getEnd() - word_sel->getBegin(); if (!word_sel->getWord()->hasSpaceAfter() && j < line_words->size() - 1) { n_rects--; } } } *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]; const TextWord *word = word_sel->getWord(); int end = word_sel->getEnd(); for (k = word_sel->getBegin(); k < end; k++) { rect = *rectangles + offset; word->getCharBBox(k, &(rect->x1), &(rect->y1), &(rect->x2), &(rect->y2)); offset++; } 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]; next_word_sel->getWord()->getBBox(&x3, &y3, &x4, &y4); // space is from one word to other and with the same height as // first word. rect->x1 = x2; rect->y1 = y1; rect->x2 = x3; rect->y2 = y2; offset++; } delete word_sel; } if (i < n_lines - 1 && offset > 0) { // end of line rect->x1 = x2; rect->y1 = y2; rect->x2 = x2; rect->y2 = y2; offset++; } delete line_words; } gfree(word_list); return TRUE; } /** * poppler_page_free_text_attributes: * @list: (element-type PopplerTextAttributes): A list of * #PopplerTextAttributess * * Frees a list of #PopplerTextAttributess allocated by * poppler_page_get_text_attributes(). * * Since: 0.18 **/ void poppler_page_free_text_attributes(GList *list) { if (G_UNLIKELY(list == nullptr)) { return; } g_list_free_full(list, (GDestroyNotify)poppler_text_attributes_free); } static gboolean word_text_attributes_equal(const TextWord *a, gint ai, const TextWord *b, gint bi) { double ar, ag, ab, br, bg, bb; if (!a->getFontInfo(ai)->matches(b->getFontInfo(bi))) { return FALSE; } if (a->getFontSize() != b->getFontSize()) { return FALSE; } if (a->isUnderlined() != b->isUnderlined()) { return FALSE; } a->getColor(&ar, &ag, &ab); b->getColor(&br, &bg, &bb); return (ar == br && ag == bg && ab == bb); } /** * poppler_page_get_text_attributes: * @page: A #PopplerPage * * 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()) * to which text attributes apply. * * See also poppler_page_get_text_attributes_for_area() * * Return value: (element-type PopplerTextAttributes) (transfer full): A #GList of #PopplerTextAttributes * * Since: 0.18 **/ GList *poppler_page_get_text_attributes(PopplerPage *page) { PopplerRectangle selection = { 0, 0, 0, 0 }; g_return_val_if_fail(POPPLER_IS_PAGE(page), NULL); poppler_page_get_size(page, &selection.x2, &selection.y2); return poppler_page_get_text_attributes_for_area(page, &selection); } /** * poppler_page_get_text_attributes_for_area: * @page: A #PopplerPage * @area: a #PopplerRectangle * * Obtains the attributes of the text in @area 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()) * to which text attributes apply. * * Return value: (element-type PopplerTextAttributes) (transfer full): A #GList of #PopplerTextAttributes * * Since: 0.26 **/ GList *poppler_page_get_text_attributes_for_area(PopplerPage *page, PopplerRectangle *area) { 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; g_return_val_if_fail(POPPLER_IS_PAGE(page), NULL); g_return_val_if_fail(area != nullptr, nullptr); selection.x1 = area->x1; selection.y1 = area->y1; selection.x2 = area->x2; selection.y2 = area->y2; text = poppler_page_get_text_page(page); std::vector **word_list = text->getSelectionWords(&selection, selectionStyleGlyph, &n_lines); if (!word_list) { 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]; int end = word_sel->getEnd(); word = word_sel->getWord(); for (word_i = word_sel->getBegin(); word_i < end; word_i++) { if (!prev_word || !word_text_attributes_equal(word, word_i, prev_word, prev_word_i)) { attrs = poppler_text_attributes_new_from_word(word, word_i); attrs->start_index = offset; attributes = g_list_prepend(attributes, attrs); } attrs->end_index = offset; offset++; prev_word = word; prev_word_i = word_i; } if (word->hasSpaceAfter() && j < line_words->size() - 1) { attrs->end_index = offset; offset++; } delete word_sel; } if (i < n_lines - 1) { attrs->end_index = offset; offset++; } delete line_words; } gfree(word_list); return g_list_reverse(attributes); } poppler-24.02.0/glib/poppler-page.h000066400000000000000000000340631455701731300170350ustar00rootroot00000000000000/* poppler-page.h: glib interface to poppler * Copyright (C) 2004, Red Hat, Inc. * * 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_PAGE_H__ #define __POPPLER_PAGE_H__ #include #include "poppler.h" #include G_BEGIN_DECLS #define POPPLER_TYPE_PAGE (poppler_page_get_type()) #define POPPLER_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), POPPLER_TYPE_PAGE, PopplerPage)) #define POPPLER_IS_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), POPPLER_TYPE_PAGE)) POPPLER_PUBLIC 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_for_printing(PopplerPage *page, cairo_t *cairo); POPPLER_PUBLIC void poppler_page_render_for_printing_with_options(PopplerPage *page, cairo_t *cairo, PopplerPrintFlags options); POPPLER_PUBLIC cairo_surface_t *poppler_page_get_thumbnail(PopplerPage *page); POPPLER_PUBLIC void poppler_page_render_selection(PopplerPage *page, cairo_t *cairo, PopplerRectangle *selection, PopplerRectangle *old_selection, PopplerSelectionStyle style, PopplerColor *glyph_color, PopplerColor *background_color); POPPLER_PUBLIC void poppler_page_get_size(PopplerPage *page, double *width, double *height); POPPLER_PUBLIC int poppler_page_get_index(PopplerPage *page); POPPLER_PUBLIC gchar *poppler_page_get_label(PopplerPage *page); POPPLER_PUBLIC double poppler_page_get_duration(PopplerPage *page); POPPLER_PUBLIC PopplerPageTransition *poppler_page_get_transition(PopplerPage *page); POPPLER_PUBLIC gboolean poppler_page_get_thumbnail_size(PopplerPage *page, int *width, int *height); POPPLER_PUBLIC GList *poppler_page_find_text_with_options(PopplerPage *page, const char *text, PopplerFindFlags options); POPPLER_PUBLIC GList *poppler_page_find_text(PopplerPage *page, const char *text); POPPLER_PUBLIC void poppler_page_render_to_ps(PopplerPage *page, PopplerPSFile *ps_file); POPPLER_PUBLIC char *poppler_page_get_text(PopplerPage *page); POPPLER_PUBLIC char *poppler_page_get_text_for_area(PopplerPage *page, PopplerRectangle *area); POPPLER_PUBLIC char *poppler_page_get_selected_text(PopplerPage *page, PopplerSelectionStyle style, PopplerRectangle *selection); POPPLER_PUBLIC cairo_region_t *poppler_page_get_selected_region(PopplerPage *page, gdouble scale, PopplerSelectionStyle style, PopplerRectangle *selection); POPPLER_PUBLIC GList *poppler_page_get_selection_region(PopplerPage *page, gdouble scale, PopplerSelectionStyle style, PopplerRectangle *selection) G_GNUC_DEPRECATED_FOR(poppler_page_get_selected_region); POPPLER_PUBLIC void poppler_page_selection_region_free(GList *region) G_GNUC_DEPRECATED_FOR(cairo_region_destroy); POPPLER_PUBLIC GList *poppler_page_get_link_mapping(PopplerPage *page); POPPLER_PUBLIC void poppler_page_free_link_mapping(GList *list); POPPLER_PUBLIC GList *poppler_page_get_image_mapping(PopplerPage *page); POPPLER_PUBLIC void poppler_page_free_image_mapping(GList *list); POPPLER_PUBLIC cairo_surface_t *poppler_page_get_image(PopplerPage *page, gint image_id); POPPLER_PUBLIC GList *poppler_page_get_form_field_mapping(PopplerPage *page); POPPLER_PUBLIC void poppler_page_free_form_field_mapping(GList *list); POPPLER_PUBLIC GList *poppler_page_get_annot_mapping(PopplerPage *page); POPPLER_PUBLIC void poppler_page_free_annot_mapping(GList *list); POPPLER_PUBLIC void poppler_page_add_annot(PopplerPage *page, PopplerAnnot *annot); POPPLER_PUBLIC void poppler_page_remove_annot(PopplerPage *page, PopplerAnnot *annot); POPPLER_PUBLIC void poppler_page_get_crop_box(PopplerPage *page, PopplerRectangle *rect); POPPLER_PUBLIC gboolean poppler_page_get_bounding_box(PopplerPage *page, PopplerRectangle *rect); POPPLER_PUBLIC gboolean poppler_page_get_text_layout(PopplerPage *page, PopplerRectangle **rectangles, guint *n_rectangles); POPPLER_PUBLIC gboolean poppler_page_get_text_layout_for_area(PopplerPage *page, PopplerRectangle *area, PopplerRectangle **rectangles, guint *n_rectangles); POPPLER_PUBLIC GList *poppler_page_get_text_attributes(PopplerPage *page); POPPLER_PUBLIC void poppler_page_free_text_attributes(GList *list); POPPLER_PUBLIC GList *poppler_page_get_text_attributes_for_area(PopplerPage *page, PopplerRectangle *area); /* A rectangle on a page, with coordinates in PDF points. */ #define POPPLER_TYPE_RECTANGLE (poppler_rectangle_get_type()) /** * PopplerRectangle: * @x1: x coordinate of lower left corner * @y1: y coordinate of lower left corner * @x2: x coordinate of upper right corner * @y2: y coordinate of upper right corner * * A #PopplerRectangle is used to describe * locations on a page and bounding boxes */ struct _PopplerRectangle { gdouble x1; gdouble y1; gdouble x2; gdouble y2; }; POPPLER_PUBLIC GType poppler_rectangle_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerRectangle *poppler_rectangle_new(void); POPPLER_PUBLIC PopplerRectangle *poppler_rectangle_copy(PopplerRectangle *rectangle); POPPLER_PUBLIC void poppler_rectangle_free(PopplerRectangle *rectangle); POPPLER_PUBLIC gboolean poppler_rectangle_find_get_match_continued(const PopplerRectangle *rectangle); POPPLER_PUBLIC gboolean poppler_rectangle_find_get_ignored_hyphen(const PopplerRectangle *rectangle); /* A point on a page, with coordinates in PDF points. */ #define POPPLER_TYPE_POINT (poppler_point_get_type()) /** * PopplerPoint: * @x: x coordinate * @y: y coordinate * * A #PopplerPoint is used to describe a location point on a page */ struct _PopplerPoint { gdouble x; gdouble y; }; POPPLER_PUBLIC GType poppler_point_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerPoint *poppler_point_new(void); POPPLER_PUBLIC PopplerPoint *poppler_point_copy(PopplerPoint *point); POPPLER_PUBLIC void poppler_point_free(PopplerPoint *point); /* PopplerQuadrilateral */ /* A quadrilateral encompasses a word or group of contiguous words in the * text underlying the annotation. The coordinates for each quadrilateral are * given in the order x1 y1 x2 y2 x3 y3 x4 y4 specifying the quadrilateral’s four * vertices in counterclockwise order */ #define POPPLER_TYPE_QUADRILATERAL (poppler_quadrilateral_get_type()) /** * PopplerQuadrilateral: * @p1: a #PopplerPoint with the first vertex coordinates * @p2: a #PopplerPoint with the second vertex coordinates * @p3: a #PopplerPoint with the third vertex coordinates * @p4: a #PopplerPoint with the fourth vertex coordinates * * A #PopplerQuadrilateral is used to describe rectangle-like polygon * with arbitrary inclination on a page. * * Since: 0.26 **/ struct _PopplerQuadrilateral { PopplerPoint p1; PopplerPoint p2; PopplerPoint p3; PopplerPoint p4; }; POPPLER_PUBLIC GType poppler_quadrilateral_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerQuadrilateral *poppler_quadrilateral_new(void); POPPLER_PUBLIC PopplerQuadrilateral *poppler_quadrilateral_copy(PopplerQuadrilateral *quad); POPPLER_PUBLIC void poppler_quadrilateral_free(PopplerQuadrilateral *quad); /* A color in RGB */ #define POPPLER_TYPE_COLOR (poppler_color_get_type()) /** * PopplerColor: * @red: the red component of color * @green: the green component of color * @blue: the blue component of color * * A #PopplerColor describes a RGB color. Color components * are values between 0 and 65535 */ struct _PopplerColor { guint16 red; guint16 green; guint16 blue; }; POPPLER_PUBLIC GType poppler_color_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerColor *poppler_color_new(void); POPPLER_PUBLIC PopplerColor *poppler_color_copy(PopplerColor *color); POPPLER_PUBLIC void poppler_color_free(PopplerColor *color); /* Text attributes. */ #define POPPLER_TYPE_TEXT_ATTRIBUTES (poppler_text_attributes_get_type()) /** * PopplerTextAttributes: * @font_name: font name * @font_size: font size * @is_underlined: if text is underlined * @color: a #PopplerColor, the foreground color * @start_index: start position this text attributes apply * @end_index: end position this text attributes apply * * A #PopplerTextAttributes is used to describe text attributes of a range of text * * Since: 0.18 */ struct _PopplerTextAttributes { gchar *font_name; gdouble font_size; gboolean is_underlined; PopplerColor color; gint start_index; gint end_index; }; POPPLER_PUBLIC GType poppler_text_attributes_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerTextAttributes *poppler_text_attributes_new(void); POPPLER_PUBLIC PopplerTextAttributes *poppler_text_attributes_copy(PopplerTextAttributes *text_attrs); POPPLER_PUBLIC void poppler_text_attributes_free(PopplerTextAttributes *text_attrs); /* Mapping between areas on the current page and PopplerActions */ #define POPPLER_TYPE_LINK_MAPPING (poppler_link_mapping_get_type()) /** * PopplerLinkMapping: * @area: a #PopplerRectangle representing an area of the page * @action: a #PopplerAction * * A #PopplerLinkMapping structure represents the location * of @action on the page */ struct _PopplerLinkMapping { PopplerRectangle area; PopplerAction *action; }; POPPLER_PUBLIC GType poppler_link_mapping_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerLinkMapping *poppler_link_mapping_new(void); POPPLER_PUBLIC PopplerLinkMapping *poppler_link_mapping_copy(PopplerLinkMapping *mapping); POPPLER_PUBLIC void poppler_link_mapping_free(PopplerLinkMapping *mapping); /* Page Transition */ #define POPPLER_TYPE_PAGE_TRANSITION (poppler_page_transition_get_type()) /** * PopplerPageTransition: * @type: the type of transtition * @alignment: the dimension in which the transition effect shall occur. * Only for #POPPLER_PAGE_TRANSITION_SPLIT and #POPPLER_PAGE_TRANSITION_BLINDS transition types * @direction: the direction of motion for the transition effect. * Only for #POPPLER_PAGE_TRANSITION_SPLIT, #POPPLER_PAGE_TRANSITION_BOX and #POPPLER_PAGE_TRANSITION_FLY * transition types * @duration: the duration of the transition effect * @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, * #POPPLER_PAGE_TRANSITION_COVER, #POPPLER_PAGE_TRANSITION_UNCOVER and #POPPLER_PAGE_TRANSITION_PUSH * transition types * @scale: the starting or ending scale at which the changes shall be drawn. * Only for #POPPLER_PAGE_TRANSITION_FLY transition type * @rectangular: whether the area that will be flown is rectangular and opaque. * Only for #POPPLER_PAGE_TRANSITION_FLY transition type * * A #PopplerPageTransition structures describes a visual transition * to use when moving between pages during a presentation */ struct _PopplerPageTransition { PopplerPageTransitionType type; PopplerPageTransitionAlignment alignment; PopplerPageTransitionDirection direction; gint duration; gint angle; gdouble scale; gboolean rectangular; gdouble duration_real; }; POPPLER_PUBLIC GType poppler_page_transition_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerPageTransition *poppler_page_transition_new(void); POPPLER_PUBLIC PopplerPageTransition *poppler_page_transition_copy(PopplerPageTransition *transition); POPPLER_PUBLIC void poppler_page_transition_free(PopplerPageTransition *transition); /* Mapping between areas on the current page and images */ #define POPPLER_TYPE_IMAGE_MAPPING (poppler_image_mapping_get_type()) /** * PopplerImageMapping: * @area: a #PopplerRectangle representing an area of the page * @image_id: an image identifier * * A #PopplerImageMapping structure represents the location * of an image on the page */ struct _PopplerImageMapping { PopplerRectangle area; gint image_id; }; POPPLER_PUBLIC GType poppler_image_mapping_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerImageMapping *poppler_image_mapping_new(void); POPPLER_PUBLIC PopplerImageMapping *poppler_image_mapping_copy(PopplerImageMapping *mapping); POPPLER_PUBLIC void poppler_image_mapping_free(PopplerImageMapping *mapping); /* Mapping between areas on the current page and form fields */ #define POPPLER_TYPE_FORM_FIELD_MAPPING (poppler_form_field_mapping_get_type()) /** * PopplerFormFieldMapping: * @area: a #PopplerRectangle representing an area of the page * @field: a #PopplerFormField * * A #PopplerFormFieldMapping structure represents the location * of @field on the page */ struct _PopplerFormFieldMapping { PopplerRectangle area; PopplerFormField *field; }; POPPLER_PUBLIC GType poppler_form_field_mapping_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerFormFieldMapping *poppler_form_field_mapping_new(void); POPPLER_PUBLIC PopplerFormFieldMapping *poppler_form_field_mapping_copy(PopplerFormFieldMapping *mapping); POPPLER_PUBLIC void poppler_form_field_mapping_free(PopplerFormFieldMapping *mapping); /* Mapping between areas on the current page and annots */ #define POPPLER_TYPE_ANNOT_MAPPING (poppler_annot_mapping_get_type()) /** * PopplerAnnotMapping: * @area: a #PopplerRectangle representing an area of the page * @annot: a #PopplerAnnot * * A #PopplerAnnotMapping structure represents the location * of @annot on the page */ struct _PopplerAnnotMapping { PopplerRectangle area; PopplerAnnot *annot; }; POPPLER_PUBLIC GType poppler_annot_mapping_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerAnnotMapping *poppler_annot_mapping_new(void); POPPLER_PUBLIC PopplerAnnotMapping *poppler_annot_mapping_copy(PopplerAnnotMapping *mapping); POPPLER_PUBLIC void poppler_annot_mapping_free(PopplerAnnotMapping *mapping); G_END_DECLS #endif /* __POPPLER_PAGE_H__ */ poppler-24.02.0/glib/poppler-private.h000066400000000000000000000116341455701731300175720ustar00rootroot00000000000000#ifndef __POPPLER_PRIVATE_H__ #define __POPPLER_PRIVATE_H__ #include #ifndef __GI_SCANNER__ # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include #endif #define SUPPORTED_ROTATION(r) (r == 90 || r == 180 || r == 270) struct _PopplerDocument { /*< private >*/ GObject parent_instance; std::unique_ptr initer; PDFDoc *doc; GList *layers; GList *layers_rbgroups; CairoOutputDev *output_dev; }; struct _PopplerPSFile { /*< private >*/ GObject parent_instance; PopplerDocument *document; PSOutputDev *out; int fd; char *filename; int first_page; int last_page; double paper_width; double paper_height; gboolean duplex; }; struct _PopplerFontInfo { /*< private >*/ GObject parent_instance; PopplerDocument *document; FontInfoScanner *scanner; }; struct _PopplerPage { /*< private >*/ GObject parent_instance; PopplerDocument *document; Page *page; int index; TextPage *text; }; struct _PopplerFormField { /*< private >*/ GObject parent_instance; PopplerDocument *document; FormWidget *widget; PopplerAction *action; PopplerAction *field_modified_action; PopplerAction *format_field_action; PopplerAction *validate_field_action; PopplerAction *calculate_field_action; }; struct _PopplerAnnot { GObject parent_instance; Annot *annot; }; typedef struct _Layer { /*< private >*/ GList *kids; gchar *label; OptionalContentGroup *oc; } Layer; struct _PopplerLayer { /*< private >*/ GObject parent_instance; PopplerDocument *document; Layer *layer; GList *rbgroup; gchar *title; }; struct _PopplerStructureElement { /*< private >*/ GObject parent_instance; PopplerDocument *document; const StructElement *elem; }; /* * PopplerRectangleExtended: * * The real type behind the public PopplerRectangle. * Must be ABI compatible to it! */ typedef struct { /*< private >*/ double x1; double y1; double x2; double y2; bool match_continued; /* Described in poppler_rectangle_find_get_match_continued() */ bool ignored_hyphen; /* Described in poppler_rectangle_find_get_ignored_hyphen() */ } PopplerRectangleExtended; PopplerRectangle *poppler_rectangle_new_from_pdf_rectangle(const PDFRectangle *rect); GList *_poppler_document_get_layers(PopplerDocument *document); GList *_poppler_document_get_layer_rbgroup(PopplerDocument *document, Layer *layer); PopplerPage *_poppler_page_new(PopplerDocument *document, Page *page, int index); void _unrotate_rect_for_annot_and_page(Page *page, Annot *annot, double *x1, double *y1, double *x2, double *y2); AnnotQuadrilaterals *_page_new_quads_unrotated(Page *page, AnnotQuadrilaterals *quads); AnnotQuadrilaterals *new_quads_from_offset_cropbox(const PDFRectangle *crop_box, AnnotQuadrilaterals *quads, gboolean add); PopplerAction *_poppler_action_new(PopplerDocument *document, const LinkAction *link, const gchar *title); PopplerLayer *_poppler_layer_new(PopplerDocument *document, Layer *layer, GList *rbgroup); PopplerDest *_poppler_dest_new_goto(PopplerDocument *document, LinkDest *link_dest); PopplerFormField *_poppler_form_field_new(PopplerDocument *document, FormWidget *field); PopplerAttachment *_poppler_attachment_new(FileSpec *file); PopplerMovie *_poppler_movie_new(const Movie *movie); PopplerMedia *_poppler_media_new(const MediaRendition *media); PopplerAnnot *_poppler_annot_new(Annot *annot); PopplerAnnot *_poppler_annot_text_new(Annot *annot); PopplerAnnot *_poppler_annot_free_text_new(Annot *annot); PopplerAnnot *_poppler_annot_text_markup_new(Annot *annot); PopplerAnnot *_poppler_annot_file_attachment_new(Annot *annot); PopplerAnnot *_poppler_annot_movie_new(Annot *annot); PopplerAnnot *_poppler_annot_screen_new(PopplerDocument *doc, Annot *annot); PopplerAnnot *_poppler_annot_line_new(Annot *annot); PopplerAnnot *_poppler_annot_circle_new(Annot *annot); PopplerAnnot *_poppler_annot_square_new(Annot *annot); PopplerAnnot *_poppler_annot_stamp_new(Annot *annot); 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); AnnotStampImageHelper *_poppler_convert_cairo_image_to_stamp_image_helper(const cairo_surface_t *image); void _poppler_error_cb(ErrorCategory category, Goffset pos, const char *message); #endif poppler-24.02.0/glib/poppler-structure-element.cc000066400000000000000000002221241455701731300217430ustar00rootroot00000000000000/* poppler-structure.cc: glib interface to poppler * * Copyright (C) 2013 Igalia S.L. * Copyright (C) 2018 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 * 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 "config.h" #ifndef __GI_SCANNER__ # include # include # include # include # include #endif /* !__GI_SCANNER__ */ #include "poppler.h" #include "poppler-private.h" #include "poppler-structure-element.h" /** * SECTION:poppler-structure-element * @short_description: Document structure element. * @title: PopplerStructureElement * * Instances of #PopplerStructureElement are used to describe the structure * of a #PopplerDocument. To access the elements in the structure of the * document, use poppler_structure_element_iter_new() to obtain an iterator * for the top-level #PopplerStructureElement, and then use the * #PopplerStructureElementIter methods to traverse the structure tree. */ typedef struct _PopplerStructureElementClass { GObjectClass parent_class; } PopplerStructureElementClass; G_DEFINE_TYPE(PopplerStructureElement, poppler_structure_element, G_TYPE_OBJECT) static PopplerStructureElement *_poppler_structure_element_new(PopplerDocument *document, const StructElement *element) { PopplerStructureElement *poppler_structure_element; g_assert(POPPLER_IS_DOCUMENT(document)); g_assert(element); poppler_structure_element = (PopplerStructureElement *)g_object_new(POPPLER_TYPE_STRUCTURE_ELEMENT, nullptr, NULL); poppler_structure_element->document = (PopplerDocument *)g_object_ref(document); poppler_structure_element->elem = element; return poppler_structure_element; } static void poppler_structure_element_init(PopplerStructureElement *poppler_structure_element) { } static void poppler_structure_element_finalize(GObject *object) { PopplerStructureElement *poppler_structure_element = POPPLER_STRUCTURE_ELEMENT(object); /* poppler_structure_element->elem is owned by the StructTreeRoot */ g_object_unref(poppler_structure_element->document); G_OBJECT_CLASS(poppler_structure_element_parent_class)->finalize(object); } static void poppler_structure_element_class_init(PopplerStructureElementClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS(klass); gobject_class->finalize = poppler_structure_element_finalize; } /** * poppler_structure_element_get_kind: * @poppler_structure_element: A #PopplerStructureElement * * Return value: A #PopplerStructureElementKind value. * * Since: 0.26 */ PopplerStructureElementKind poppler_structure_element_get_kind(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(POPPLER_IS_STRUCTURE_ELEMENT(poppler_structure_element), POPPLER_STRUCTURE_ELEMENT_CONTENT); g_return_val_if_fail(poppler_structure_element->elem != nullptr, POPPLER_STRUCTURE_ELEMENT_CONTENT); switch (poppler_structure_element->elem->getType()) { case StructElement::MCID: return POPPLER_STRUCTURE_ELEMENT_CONTENT; case StructElement::OBJR: return POPPLER_STRUCTURE_ELEMENT_OBJECT_REFERENCE; case StructElement::Document: return POPPLER_STRUCTURE_ELEMENT_DOCUMENT; case StructElement::Part: return POPPLER_STRUCTURE_ELEMENT_PART; case StructElement::Art: return POPPLER_STRUCTURE_ELEMENT_ARTICLE; case StructElement::Sect: return POPPLER_STRUCTURE_ELEMENT_SECTION; case StructElement::Div: return POPPLER_STRUCTURE_ELEMENT_DIV; case StructElement::Span: return POPPLER_STRUCTURE_ELEMENT_SPAN; case StructElement::Quote: return POPPLER_STRUCTURE_ELEMENT_QUOTE; case StructElement::Note: return POPPLER_STRUCTURE_ELEMENT_NOTE; case StructElement::Reference: return POPPLER_STRUCTURE_ELEMENT_REFERENCE; case StructElement::BibEntry: return POPPLER_STRUCTURE_ELEMENT_BIBENTRY; case StructElement::Code: return POPPLER_STRUCTURE_ELEMENT_CODE; case StructElement::Link: return POPPLER_STRUCTURE_ELEMENT_LINK; case StructElement::Annot: return POPPLER_STRUCTURE_ELEMENT_ANNOT; case StructElement::BlockQuote: return POPPLER_STRUCTURE_ELEMENT_BLOCKQUOTE; case StructElement::Caption: return POPPLER_STRUCTURE_ELEMENT_CAPTION; case StructElement::NonStruct: return POPPLER_STRUCTURE_ELEMENT_NONSTRUCT; case StructElement::TOC: return POPPLER_STRUCTURE_ELEMENT_TOC; case StructElement::TOCI: return POPPLER_STRUCTURE_ELEMENT_TOC_ITEM; case StructElement::Index: return POPPLER_STRUCTURE_ELEMENT_INDEX; case StructElement::Private: return POPPLER_STRUCTURE_ELEMENT_PRIVATE; case StructElement::P: return POPPLER_STRUCTURE_ELEMENT_PARAGRAPH; case StructElement::H: return POPPLER_STRUCTURE_ELEMENT_HEADING; case StructElement::H1: return POPPLER_STRUCTURE_ELEMENT_HEADING_1; case StructElement::H2: return POPPLER_STRUCTURE_ELEMENT_HEADING_2; case StructElement::H3: return POPPLER_STRUCTURE_ELEMENT_HEADING_3; case StructElement::H4: return POPPLER_STRUCTURE_ELEMENT_HEADING_4; case StructElement::H5: return POPPLER_STRUCTURE_ELEMENT_HEADING_5; case StructElement::H6: return POPPLER_STRUCTURE_ELEMENT_HEADING_6; case StructElement::L: return POPPLER_STRUCTURE_ELEMENT_LIST; case StructElement::LI: return POPPLER_STRUCTURE_ELEMENT_LIST_ITEM; case StructElement::Lbl: return POPPLER_STRUCTURE_ELEMENT_LIST_LABEL; case StructElement::LBody: return POPPLER_STRUCTURE_ELEMENT_LIST_BODY; case StructElement::Table: return POPPLER_STRUCTURE_ELEMENT_TABLE; case StructElement::TR: return POPPLER_STRUCTURE_ELEMENT_TABLE_ROW; case StructElement::TH: return POPPLER_STRUCTURE_ELEMENT_TABLE_HEADING; case StructElement::TD: return POPPLER_STRUCTURE_ELEMENT_TABLE_DATA; case StructElement::THead: return POPPLER_STRUCTURE_ELEMENT_TABLE_HEADER; case StructElement::TFoot: return POPPLER_STRUCTURE_ELEMENT_TABLE_FOOTER; case StructElement::TBody: return POPPLER_STRUCTURE_ELEMENT_TABLE_BODY; case StructElement::Ruby: return POPPLER_STRUCTURE_ELEMENT_RUBY; case StructElement::RB: return POPPLER_STRUCTURE_ELEMENT_RUBY_BASE_TEXT; case StructElement::RT: return POPPLER_STRUCTURE_ELEMENT_RUBY_ANNOT_TEXT; case StructElement::RP: return POPPLER_STRUCTURE_ELEMENT_RUBY_PUNCTUATION; case StructElement::Warichu: return POPPLER_STRUCTURE_ELEMENT_WARICHU; case StructElement::WT: return POPPLER_STRUCTURE_ELEMENT_WARICHU_TEXT; case StructElement::WP: return POPPLER_STRUCTURE_ELEMENT_WARICHU_PUNCTUATION; case StructElement::Figure: return POPPLER_STRUCTURE_ELEMENT_FIGURE; case StructElement::Formula: return POPPLER_STRUCTURE_ELEMENT_FORMULA; case StructElement::Form: return POPPLER_STRUCTURE_ELEMENT_FORM; /* There should never be elements of type StructElement::Unknown */ case StructElement::Unknown: g_assert_not_reached(); } g_assert_not_reached(); return POPPLER_STRUCTURE_ELEMENT_CONTENT; } template struct EnumNameValue { const gchar *name; EnumType value; static const EnumNameValue values[]; static const Attribute::Type attribute_type; }; #define ENUM_VALUES(E, A) \ template<> \ const Attribute::Type EnumNameValue::attribute_type = Attribute::A; \ template<> \ const EnumNameValue EnumNameValue::values[] = ENUM_VALUES(PopplerStructurePlacement, Placement) { { "Block", POPPLER_STRUCTURE_PLACEMENT_BLOCK }, { "Inline", POPPLER_STRUCTURE_PLACEMENT_INLINE }, { "Before", POPPLER_STRUCTURE_PLACEMENT_BEFORE }, { "Start", POPPLER_STRUCTURE_PLACEMENT_START }, { "End", POPPLER_STRUCTURE_PLACEMENT_END }, {} }; ENUM_VALUES(PopplerStructureWritingMode, WritingMode) { { "LrTb", POPPLER_STRUCTURE_WRITING_MODE_LR_TB }, { "RlTb", POPPLER_STRUCTURE_WRITING_MODE_RL_TB }, { "TbRl", POPPLER_STRUCTURE_WRITING_MODE_TB_RL }, {} }; ENUM_VALUES(PopplerStructureBorderStyle, BorderStyle) { { "None", POPPLER_STRUCTURE_BORDER_STYLE_NONE }, { "Hidden", POPPLER_STRUCTURE_BORDER_STYLE_HIDDEN }, { "Dotted", POPPLER_STRUCTURE_BORDER_STYLE_DOTTED }, { "Dashed", POPPLER_STRUCTURE_BORDER_STYLE_DASHED }, { "Solid", POPPLER_STRUCTURE_BORDER_STYLE_SOLID }, { "Double", POPPLER_STRUCTURE_BORDER_STYLE_DOUBLE }, { "Groove", POPPLER_STRUCTURE_BORDER_STYLE_GROOVE }, { "Inset", POPPLER_STRUCTURE_BORDER_STYLE_INSET }, { "Outset", POPPLER_STRUCTURE_BORDER_STYLE_OUTSET }, {} }; ENUM_VALUES(PopplerStructureTextAlign, TextAlign) { { "Start", POPPLER_STRUCTURE_TEXT_ALIGN_START }, { "Center", POPPLER_STRUCTURE_TEXT_ALIGN_CENTER }, { "End", POPPLER_STRUCTURE_TEXT_ALIGN_END }, { "Justify", POPPLER_STRUCTURE_TEXT_ALIGN_JUSTIFY }, {} }; ENUM_VALUES(PopplerStructureBlockAlign, BlockAlign) { { "Before", POPPLER_STRUCTURE_BLOCK_ALIGN_BEFORE }, { "Middle", POPPLER_STRUCTURE_BLOCK_ALIGN_MIDDLE }, { "After", POPPLER_STRUCTURE_BLOCK_ALIGN_AFTER }, { "Justify", POPPLER_STRUCTURE_BLOCK_ALIGN_JUSTIFY }, {} }; ENUM_VALUES(PopplerStructureInlineAlign, InlineAlign) { { "Start", POPPLER_STRUCTURE_INLINE_ALIGN_START }, { "Center", POPPLER_STRUCTURE_INLINE_ALIGN_CENTER }, { "End", POPPLER_STRUCTURE_INLINE_ALIGN_END }, {} }; ENUM_VALUES(PopplerStructureTextDecoration, TextDecorationType) { { "None", POPPLER_STRUCTURE_TEXT_DECORATION_NONE }, { "Underline", POPPLER_STRUCTURE_TEXT_DECORATION_UNDERLINE }, { "Overline", POPPLER_STRUCTURE_TEXT_DECORATION_OVERLINE }, { "LineThrough", POPPLER_STRUCTURE_TEXT_DECORATION_LINETHROUGH }, {} }; ENUM_VALUES(PopplerStructureRubyAlign, RubyAlign) { { "Start", POPPLER_STRUCTURE_RUBY_ALIGN_START }, { "Center", POPPLER_STRUCTURE_RUBY_ALIGN_CENTER }, { "End", POPPLER_STRUCTURE_RUBY_ALIGN_END }, { "Justify", POPPLER_STRUCTURE_RUBY_ALIGN_JUSTIFY }, { "Distribute", POPPLER_STRUCTURE_RUBY_ALIGN_DISTRIBUTE }, {} }; ENUM_VALUES(PopplerStructureRubyPosition, RubyPosition) { { "Before", POPPLER_STRUCTURE_RUBY_POSITION_BEFORE }, { "After", POPPLER_STRUCTURE_RUBY_POSITION_AFTER }, { "Warichu", POPPLER_STRUCTURE_RUBY_POSITION_WARICHU }, { "Inline", POPPLER_STRUCTURE_RUBY_POSITION_INLINE }, {} }; ENUM_VALUES(PopplerStructureGlyphOrientation, GlyphOrientationVertical) { { "Auto", POPPLER_STRUCTURE_GLYPH_ORIENTATION_AUTO }, { "90", POPPLER_STRUCTURE_GLYPH_ORIENTATION_90 }, { "180", POPPLER_STRUCTURE_GLYPH_ORIENTATION_180 }, { "270", POPPLER_STRUCTURE_GLYPH_ORIENTATION_270 }, { "360", POPPLER_STRUCTURE_GLYPH_ORIENTATION_0 }, { "-90", POPPLER_STRUCTURE_GLYPH_ORIENTATION_270 }, { "-180", POPPLER_STRUCTURE_GLYPH_ORIENTATION_180 }, {} }; ENUM_VALUES(PopplerStructureListNumbering, ListNumbering) { { "None", POPPLER_STRUCTURE_LIST_NUMBERING_NONE }, { "Disc", POPPLER_STRUCTURE_LIST_NUMBERING_DISC }, { "Circle", POPPLER_STRUCTURE_LIST_NUMBERING_CIRCLE }, { "Square", POPPLER_STRUCTURE_LIST_NUMBERING_SQUARE }, { "Decimal", POPPLER_STRUCTURE_LIST_NUMBERING_DECIMAL }, { "UpperRoman", POPPLER_STRUCTURE_LIST_NUMBERING_UPPER_ROMAN }, { "LowerRoman", POPPLER_STRUCTURE_LIST_NUMBERING_LOWER_ROMAN }, { "UpperAlpha", POPPLER_STRUCTURE_LIST_NUMBERING_UPPER_ALPHA }, { "LowerAlpha", POPPLER_STRUCTURE_LIST_NUMBERING_LOWER_ALPHA }, {} }; ENUM_VALUES(PopplerStructureFormRole, Role) { { "rb", POPPLER_STRUCTURE_FORM_ROLE_RADIO_BUTTON }, { "cb", POPPLER_STRUCTURE_FORM_ROLE_CHECKBOX }, { "pb", POPPLER_STRUCTURE_FORM_ROLE_PUSH_BUTTON }, { "tv", POPPLER_STRUCTURE_FORM_ROLE_TEXT_VALUE }, {} }; ENUM_VALUES(PopplerStructureFormState, checked) { { "on", POPPLER_STRUCTURE_FORM_STATE_ON }, { "off", POPPLER_STRUCTURE_FORM_STATE_OFF }, { "neutral", POPPLER_STRUCTURE_FORM_STATE_NEUTRAL }, {} }; ENUM_VALUES(PopplerStructureTableScope, Scope) { { "Row", POPPLER_STRUCTURE_TABLE_SCOPE_ROW }, { "Column", POPPLER_STRUCTURE_TABLE_SCOPE_COLUMN }, { "Both", POPPLER_STRUCTURE_TABLE_SCOPE_BOTH }, {} }; #undef ENUM_VALUES template static EnumType name_to_enum(const Object *name_value) { /* * Non-NULL names must always be valid because Poppler * discards the invalid attributes when parsing them. */ g_assert(name_value != nullptr); for (const EnumNameValue *item = EnumNameValue::values; item->name; item++) { if (name_value->isName(item->name)) { return item->value; } } g_assert_not_reached(); return static_cast(-1); } template static EnumType attr_to_enum(PopplerStructureElement *poppler_structure_element) { const Attribute *attr = poppler_structure_element->elem->findAttribute(EnumNameValue::attribute_type, true); return name_to_enum((attr != nullptr) ? attr->getValue() : Attribute::getDefaultValue(EnumNameValue::attribute_type)); } static inline const Object *attr_value_or_default(PopplerStructureElement *poppler_structure_element, Attribute::Type attribute_type) { const Attribute *attr = poppler_structure_element->elem->findAttribute(attribute_type, true); return attr ? attr->getValue() : Attribute::getDefaultValue(attribute_type); } /** * poppler_structure_element_get_page: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the page number in which the element is contained. * * Return value: Number of the page that contains the element, of * -1 if not defined. * * Since: 0.26 */ gint poppler_structure_element_get_page(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(POPPLER_IS_STRUCTURE_ELEMENT(poppler_structure_element), -1); g_return_val_if_fail(poppler_structure_element->elem != nullptr, -1); Ref ref; if (poppler_structure_element->elem->getPageRef(ref)) { return poppler_structure_element->document->doc->findPage(ref) - 1; } return -1; } /** * poppler_structure_element_is_content: * @poppler_structure_element: A #PopplerStructureElement * * Checks whether an element is actual document content. * * Return value: %TRUE if the element is content, or %FALSE otherwise. * * Since: 0.26 */ gboolean poppler_structure_element_is_content(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(POPPLER_IS_STRUCTURE_ELEMENT(poppler_structure_element), FALSE); g_return_val_if_fail(poppler_structure_element->elem != nullptr, FALSE); return poppler_structure_element->elem->isContent(); } /** * poppler_structure_element_is_inline: * @poppler_structure_element: A #PopplerStructureElement * * Checks whether an element is an inline element. * * Return value: %TRUE if the element is an inline element, or %FALSE otherwise. * * Since: 0.26 */ gboolean poppler_structure_element_is_inline(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(POPPLER_IS_STRUCTURE_ELEMENT(poppler_structure_element), FALSE); g_return_val_if_fail(poppler_structure_element->elem != nullptr, FALSE); return poppler_structure_element->elem->isInline(); } /** * poppler_structure_element_is_block: * @poppler_structure_element: A #PopplerStructureElement * * Checks whether an element is a block element. * * Return value: %TRUE if the element is a block element, or %FALSE otherwise. * * Since: 0.26 */ gboolean poppler_structure_element_is_block(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(POPPLER_IS_STRUCTURE_ELEMENT(poppler_structure_element), FALSE); g_return_val_if_fail(poppler_structure_element->elem != nullptr, FALSE); return poppler_structure_element->elem->isBlock(); } /** * poppler_structure_element_is_grouping: * @poppler_structure_element: A #PopplerStructureElement * * Checks whether an element is a grouping element. * * Return value: %TRUE if the element is a grouping element, %FALSE * otherwise. * * Since: 0.26 */ gboolean poppler_structure_element_is_grouping(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(POPPLER_IS_STRUCTURE_ELEMENT(poppler_structure_element), FALSE); g_return_val_if_fail(poppler_structure_element->elem != nullptr, FALSE); return poppler_structure_element->elem->isGrouping(); } /** * poppler_structure_element_get_id: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the identifier of an element. * * Return value: (transfer full): The identifier of the element (if * defined), or %NULL. * * Since: 0.26 */ gchar *poppler_structure_element_get_id(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(POPPLER_IS_STRUCTURE_ELEMENT(poppler_structure_element), NULL); g_return_val_if_fail(poppler_structure_element->elem != nullptr, NULL); const GooString *string = poppler_structure_element->elem->getID(); return string ? _poppler_goo_string_to_utf8(string) : nullptr; } /** * poppler_structure_element_get_title: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the title of an element. * * Return value: (transfer full): The title of the element, or %NULL. * * Since: 0.26 */ gchar *poppler_structure_element_get_title(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(POPPLER_IS_STRUCTURE_ELEMENT(poppler_structure_element), NULL); g_return_val_if_fail(poppler_structure_element->elem != nullptr, NULL); const GooString *string = poppler_structure_element->elem->getTitle(); return string ? _poppler_goo_string_to_utf8(string) : nullptr; } /** * poppler_structure_element_get_abbreviation: * @poppler_structure_element: A #PopplerStructureElement * * Acronyms and abbreviations contained in elements of type * #POPPLER_STRUCTURE_ELEMENT_SPAN may have an associated expanded * text form, which can be retrieved using this function. * * Return value: (transfer full): Text of the expanded abbreviation if the * element text is an abbreviation or acrony, %NULL if not. * * Since: 0.26 */ gchar *poppler_structure_element_get_abbreviation(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(POPPLER_IS_STRUCTURE_ELEMENT(poppler_structure_element), NULL); g_return_val_if_fail(poppler_structure_element->elem != nullptr, NULL); if (poppler_structure_element->elem->getType() != StructElement::Span) { return nullptr; } const GooString *string = poppler_structure_element->elem->getExpandedAbbr(); return string ? _poppler_goo_string_to_utf8(string) : nullptr; } /** * poppler_structure_element_get_language: * @poppler_structure_element: A #PopplerStructureElement * * 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 * defined. * * Return value: (transfer full): language and country code, or %NULL. * * Since: 0.26 */ gchar *poppler_structure_element_get_language(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(POPPLER_IS_STRUCTURE_ELEMENT(poppler_structure_element), NULL); g_return_val_if_fail(poppler_structure_element->elem != nullptr, NULL); const GooString *string = poppler_structure_element->elem->getLanguage(); return string ? _poppler_goo_string_to_utf8(string) : nullptr; } /** * poppler_structure_element_get_alt_text: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the “alternate” text representation of the element (and its child * elements). This is mostly used for non-text elements like images and * figures, to specify a textual description of the element. * * Note that for elements containing proper text, the function * poppler_structure_element_get_text() must be used instead. * * Return value: (transfer full): The alternate text representation for the * element, or %NULL if not defined. * * Since: 0.26 */ gchar *poppler_structure_element_get_alt_text(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(POPPLER_IS_STRUCTURE_ELEMENT(poppler_structure_element), NULL); g_return_val_if_fail(poppler_structure_element->elem != nullptr, NULL); const GooString *string = poppler_structure_element->elem->getAltText(); return string ? _poppler_goo_string_to_utf8(string) : nullptr; } /** * poppler_structure_element_get_actual_text: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the actual text enclosed by the element (and its child elements). * The actual text is mostly used for non-text elements like images and * figures which do have the graphical appearance of text, like * a logo. For those the actual text is the equivalent text to those * graphical elements which look like text when rendered. * * Note that for elements containing proper text, the function * poppler_structure_element_get_text() must be used instead. * * Return value: (transfer full): The actual text for the element, or %NULL * if not defined. * * Since: 0.26 */ gchar *poppler_structure_element_get_actual_text(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(POPPLER_IS_STRUCTURE_ELEMENT(poppler_structure_element), NULL); g_return_val_if_fail(poppler_structure_element->elem != nullptr, NULL); const GooString *string = poppler_structure_element->elem->getActualText(); return string ? _poppler_goo_string_to_utf8(string) : nullptr; } /** * poppler_structure_element_get_text: * @poppler_structure_element: A #PopplerStructureElement * @flags: A #PopplerStructureGetTextFlags value, or * %POPPLER_STRUCTURE_GET_TEXT_NONE to disable all the flags. * * Obtains the text enclosed by an element, or the text enclosed by the * elements in the subtree (including the element itself). * * Return value: (transfer full): A string. * * Since: 0.26 */ gchar *poppler_structure_element_get_text(PopplerStructureElement *poppler_structure_element, PopplerStructureGetTextFlags flags) { g_return_val_if_fail(POPPLER_IS_STRUCTURE_ELEMENT(poppler_structure_element), NULL); g_return_val_if_fail(poppler_structure_element->elem != nullptr, NULL); GooString *string = poppler_structure_element->elem->getText(flags & POPPLER_STRUCTURE_GET_TEXT_RECURSIVE); gchar *result = string ? _poppler_goo_string_to_utf8(string) : nullptr; delete string; return result; } struct _PopplerStructureElementIter { PopplerDocument *document; union { const StructElement *elem; const StructTreeRoot *root; }; gboolean is_root; unsigned index; }; G_DEFINE_BOXED_TYPE(PopplerStructureElementIter, poppler_structure_element_iter, poppler_structure_element_iter_copy, poppler_structure_element_iter_free) /** * poppler_structure_element_iter_copy: * @iter: a #PopplerStructureElementIter * * Creates a new #PopplerStructureElementIter as a copy of @iter. The * returned value must be freed with poppler_structure_element_iter_free(). * * Return value: (transfer full): a new #PopplerStructureElementIter * * Since: 0.26 */ PopplerStructureElementIter *poppler_structure_element_iter_copy(PopplerStructureElementIter *iter) { PopplerStructureElementIter *new_iter; g_return_val_if_fail(iter != nullptr, NULL); new_iter = g_slice_dup(PopplerStructureElementIter, iter); new_iter->document = (PopplerDocument *)g_object_ref(new_iter->document); return new_iter; } /** * poppler_structure_element_iter_free: * @iter: a #PopplerStructureElementIter * * Frees @iter. * * Since: 0.26 */ void poppler_structure_element_iter_free(PopplerStructureElementIter *iter) { if (G_UNLIKELY(iter == nullptr)) { return; } g_object_unref(iter->document); g_slice_free(PopplerStructureElementIter, iter); } /** * poppler_structure_element_iter_new: * @poppler_document: a #PopplerDocument. * * Returns the root #PopplerStructureElementIter for @document, 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 * the document structure and its contents. Each node in the tree contains * a #PopplerStructureElement. * * Here is a simple example that walks the whole tree: * * * 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); * } * * * Return value: (transfer full): a new #PopplerStructureElementIter, or %NULL if document * doesn't have structure tree. * * Since: 0.26 */ PopplerStructureElementIter *poppler_structure_element_iter_new(PopplerDocument *poppler_document) { PopplerStructureElementIter *iter; g_return_val_if_fail(POPPLER_IS_DOCUMENT(poppler_document), NULL); const StructTreeRoot *root = poppler_document->doc->getStructTreeRoot(); if (root == nullptr) { return nullptr; } if (root->getNumChildren() == 0) { return nullptr; } iter = g_slice_new0(PopplerStructureElementIter); iter->document = (PopplerDocument *)g_object_ref(poppler_document); iter->is_root = TRUE; iter->root = root; return iter; } /** * poppler_structure_element_iter_next: * @iter: a #PopplerStructureElementIter * * Sets @iter to point to the next structure element at the current level * of the tree, if valid. See poppler_structure_element_iter_new() for more * information. * * Return value: %TRUE, if @iter was set to the next structure element * * Since: 0.26 */ gboolean poppler_structure_element_iter_next(PopplerStructureElementIter *iter) { unsigned elements; g_return_val_if_fail(iter != nullptr, FALSE); elements = iter->is_root ? iter->root->getNumChildren() : iter->elem->getNumChildren(); return ++iter->index < elements; } /** * poppler_structure_element_iter_get_element: * @iter: a #PopplerStructureElementIter * * Returns the #PopplerStructureElementIter associated with @iter. * * Return value: (transfer full): a new #PopplerStructureElementIter * * Since: 0.26 */ PopplerStructureElement *poppler_structure_element_iter_get_element(PopplerStructureElementIter *iter) { g_return_val_if_fail(iter != nullptr, NULL); const StructElement *elem = iter->is_root ? iter->root->getChild(iter->index) : iter->elem->getChild(iter->index); return _poppler_structure_element_new(iter->document, elem); } /** * poppler_structure_element_iter_get_child: * @parent: a #PopplerStructureElementIter * * Returns a new iterator to the children elements of the * #PopplerStructureElement associated with @iter. The returned value must * be freed with poppler_structure_element_iter_free(). * * Return value: a new #PopplerStructureElementIter * * Since: 0.26 */ PopplerStructureElementIter *poppler_structure_element_iter_get_child(PopplerStructureElementIter *parent) { const StructElement *elem; g_return_val_if_fail(parent != nullptr, NULL); elem = parent->is_root ? parent->root->getChild(parent->index) : parent->elem->getChild(parent->index); if (elem->getNumChildren() > 0) { PopplerStructureElementIter *child = g_slice_new0(PopplerStructureElementIter); child->document = (PopplerDocument *)g_object_ref(parent->document); child->elem = elem; return child; } return nullptr; } struct _PopplerTextSpan { gchar *text; gchar *font_name; guint flags; PopplerColor color; }; G_DEFINE_BOXED_TYPE(PopplerTextSpan, poppler_text_span, poppler_text_span_copy, poppler_text_span_free) enum { POPPLER_TEXT_SPAN_FIXED_WIDTH = (1 << 0), POPPLER_TEXT_SPAN_SERIF = (1 << 1), POPPLER_TEXT_SPAN_ITALIC = (1 << 2), POPPLER_TEXT_SPAN_BOLD = (1 << 3), }; static PopplerTextSpan *text_span_poppler_text_span(const TextSpan &span) { PopplerTextSpan *new_span = g_slice_new0(PopplerTextSpan); if (GooString *text = span.getText()) { new_span->text = _poppler_goo_string_to_utf8(text); } new_span->color.red = colToDbl(span.getColor().r) * 65535; new_span->color.green = colToDbl(span.getColor().g) * 65535; new_span->color.blue = colToDbl(span.getColor().b) * 65535; if (span.getFont()) { // GfxFont sometimes does not have a family name but there // is always a font name that can be used as fallback. 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; } if (span.getFont()->isFixedWidth()) { new_span->flags |= POPPLER_TEXT_SPAN_FIXED_WIDTH; } if (span.getFont()->isSerif()) { new_span->flags |= POPPLER_TEXT_SPAN_SERIF; } if (span.getFont()->isItalic()) { new_span->flags |= POPPLER_TEXT_SPAN_ITALIC; } if (span.getFont()->isBold()) { new_span->flags |= POPPLER_TEXT_SPAN_BOLD; } /* isBold() can return false for some fonts whose weight is heavy */ switch (span.getFont()->getWeight()) { case GfxFont::W500: case GfxFont::W600: case GfxFont::W700: case GfxFont::W800: case GfxFont::W900: new_span->flags |= POPPLER_TEXT_SPAN_BOLD; default: break; } } return new_span; } /** * poppler_text_span_copy: * @poppler_text_span: a #PopplerTextSpan * * Makes a copy of a text span. * * Return value: (transfer full): A new #PopplerTextSpan * * Since: 0.26 */ PopplerTextSpan *poppler_text_span_copy(PopplerTextSpan *poppler_text_span) { PopplerTextSpan *new_span; g_return_val_if_fail(poppler_text_span != nullptr, NULL); new_span = g_slice_dup(PopplerTextSpan, poppler_text_span); new_span->text = g_strdup(poppler_text_span->text); if (poppler_text_span->font_name) { new_span->font_name = g_strdup(poppler_text_span->font_name); } return new_span; } /** * poppler_text_span_free: * @poppler_text_span: A #PopplerTextSpan * * Frees a text span. * * Since: 0.26 */ void poppler_text_span_free(PopplerTextSpan *poppler_text_span) { if (G_UNLIKELY(poppler_text_span == nullptr)) { return; } g_free(poppler_text_span->text); g_free(poppler_text_span->font_name); g_slice_free(PopplerTextSpan, poppler_text_span); } /** * poppler_text_span_is_fixed_width_font: * @poppler_text_span: a #PopplerTextSpan * * Check wether a text span is meant to be rendered using a fixed-width font. * * Return value: Whether the span uses a fixed-width font. * * Since: 0.26 */ gboolean poppler_text_span_is_fixed_width_font(PopplerTextSpan *poppler_text_span) { g_return_val_if_fail(poppler_text_span != nullptr, FALSE); return (poppler_text_span->flags & POPPLER_TEXT_SPAN_FIXED_WIDTH); } /** * poppler_text_span_is_serif_font: * @poppler_text_span: a #PopplerTextSpan * * Check whether a text span is meant to be rendered using a serif font. * * Return value: Whether the span uses a serif font. * * Since: 0.26 */ gboolean poppler_text_span_is_serif_font(PopplerTextSpan *poppler_text_span) { g_return_val_if_fail(poppler_text_span != nullptr, FALSE); return (poppler_text_span->flags & POPPLER_TEXT_SPAN_SERIF); } /** * poppler_text_span_is_bold_font: * @poppler_text_span: a #PopplerTextSpan * * Check whether a text span is meant to be rendered using a bold font. * * Return value: Whether the span uses bold font. * * Since: 0.26 */ gboolean poppler_text_span_is_bold_font(PopplerTextSpan *poppler_text_span) { g_return_val_if_fail(poppler_text_span != nullptr, FALSE); return (poppler_text_span->flags & POPPLER_TEXT_SPAN_BOLD); } /** * poppler_text_span_get_color: * @poppler_text_span: a #PopplerTextSpan * @color: (out): a return location for a #PopplerColor * * Obtains the color in which the text is to be rendered. * * Since: 0.26 */ void poppler_text_span_get_color(PopplerTextSpan *poppler_text_span, PopplerColor *color) { g_return_if_fail(poppler_text_span != nullptr); g_return_if_fail(color != nullptr); *color = poppler_text_span->color; } /** * poppler_text_span_get_text: * @poppler_text_span: a #PopplerTextSpan * * Obtains the text contained in the span. * * Return value: (transfer none): A string. * * Since: 0.26 */ const gchar *poppler_text_span_get_text(PopplerTextSpan *poppler_text_span) { g_return_val_if_fail(poppler_text_span != nullptr, NULL); return poppler_text_span->text; } /** * poppler_text_span_get_font_name: * @poppler_text_span: a #PopplerTextSpan * * Obtains the name of the font in which the span is to be rendered. * * Return value: (transfer none): A string containing the font name, or * %NULL if a font is not defined. * * Since: 0.26 */ const gchar *poppler_text_span_get_font_name(PopplerTextSpan *poppler_text_span) { g_return_val_if_fail(poppler_text_span != nullptr, NULL); return poppler_text_span->font_name; } /** * poppler_structure_element_get_text_spans: * @poppler_structure_element: A #PopplerStructureElement * @n_text_spans: (out): A pointer to the location where the number of elements in * the returned array will be stored. * * 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 * obtain and free the text spans of an element: * * * 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); * * * Return value: (transfer full) (array length=n_text_spans) (element-type PopplerTextSpan): * An array of #PopplerTextSpan elements. * * Since: 0.26 */ PopplerTextSpan **poppler_structure_element_get_text_spans(PopplerStructureElement *poppler_structure_element, guint *n_text_spans) { g_return_val_if_fail(POPPLER_IS_STRUCTURE_ELEMENT(poppler_structure_element), NULL); g_return_val_if_fail(n_text_spans != nullptr, NULL); g_return_val_if_fail(poppler_structure_element->elem != nullptr, NULL); if (!poppler_structure_element->elem->isContent()) { return nullptr; } const TextSpanArray spans(poppler_structure_element->elem->getTextSpans()); PopplerTextSpan **text_spans = g_new0(PopplerTextSpan *, spans.size()); size_t i = 0; for (const TextSpan &s : spans) { text_spans[i++] = text_span_poppler_text_span(s); } *n_text_spans = spans.size(); return text_spans; } /* General Layout Attributes */ /** * poppler_structure_element_get_placement: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the placement type of the structure element. * * Return value: A #PopplerStructurePlacement value. * * Since: 0.26 */ PopplerStructurePlacement poppler_structure_element_get_placement(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(POPPLER_IS_STRUCTURE_ELEMENT(poppler_structure_element), EnumNameValue::values[0].value); return attr_to_enum(poppler_structure_element); } /** * poppler_structure_element_get_writing_mode: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the writing mode (writing direction) of the content associated * with a structure element. * * Return value: A #PopplerStructureWritingMode value. * * Since: 0.26 */ PopplerStructureWritingMode poppler_structure_element_get_writing_mode(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(POPPLER_IS_STRUCTURE_ELEMENT(poppler_structure_element), EnumNameValue::values[0].value); return attr_to_enum(poppler_structure_element); } static void convert_border_style(const Object *object, PopplerStructureBorderStyle *values) { g_assert(object != nullptr); g_assert(values != nullptr); if (object->isArray()) { g_assert(object->arrayGetLength() == 4); for (guint i = 0; i < 4; i++) { Object item = object->arrayGet(i); values[i] = name_to_enum(&item); } } else { values[0] = values[1] = values[2] = values[3] = name_to_enum(object); } } /** * poppler_structure_element_get_border_style: * @poppler_structure_element: A #PopplerStructureElement * @border_styles: (out) (array fixed-size=4) (element-type PopplerStructureBorderStyle): * An array of four #PopplerStructureBorderStyle elements. * * Obtains the border style of a structure element. The result values * are in before-after-start-end ordering. For example, using Western * left-to-right writing, that is top-bottom-left-right. * * Since: 0.26 */ void poppler_structure_element_get_border_style(PopplerStructureElement *poppler_structure_element, PopplerStructureBorderStyle *border_styles) { g_return_if_fail(POPPLER_IS_STRUCTURE_ELEMENT(poppler_structure_element)); g_return_if_fail(border_styles != nullptr); convert_border_style(attr_value_or_default(poppler_structure_element, Attribute::BorderStyle), border_styles); } static inline void convert_doubles_array(const Object *object, gdouble **values, guint *n_values) { g_assert(object->isArray()); g_assert(n_values != nullptr); g_assert(values != nullptr); *n_values = object->arrayGetLength(); gdouble *doubles = g_new(gdouble, *n_values); for (guint i = 0; i < *n_values; i++) { doubles[i] = object->arrayGet(i).getNum(); } values = &doubles; } static inline void convert_color(const Object *object, PopplerColor *color) { g_assert(color != nullptr); g_assert(object->isArray() && object->arrayGetLength() != 3); color->red = object->arrayGet(0).getNum() * 65535; color->green = object->arrayGet(1).getNum() * 65535; color->blue = object->arrayGet(2).getNum() * 65535; } /** * poppler_structure_element_get_color: * @poppler_structure_element: A #PopplerStructureElement * @color: (out): A #PopplerColor. * * Obtains the color of the content contained in the element. * If this attribute is not specified, the color for this element shall * be the current text fill color in effect at the start of its associated content. * * Return value: %TRUE if a color is defined for the element, * %FALSE otherwise. * * Since: 0.26 */ gboolean poppler_structure_element_get_color(PopplerStructureElement *poppler_structure_element, PopplerColor *color) { g_return_val_if_fail(POPPLER_IS_STRUCTURE_ELEMENT(poppler_structure_element), FALSE); g_return_val_if_fail(color != nullptr, FALSE); const Object *value = attr_value_or_default(poppler_structure_element, Attribute::Color); if (value == nullptr) { return FALSE; } convert_color(value, color); return TRUE; } /** * poppler_structure_element_get_background_color: * @poppler_structure_element: A #PopplerStructureElement * @color: (out): A #PopplerColor. * * Obtains the background color of the element. If this attribute is * not specified, the element shall be treated as if it were transparent. * * Return value: %TRUE if a color is defined for the element, * %FALSE otherwise. * * Since: 0.26 */ gboolean poppler_structure_element_get_background_color(PopplerStructureElement *poppler_structure_element, PopplerColor *color) { g_return_val_if_fail(POPPLER_IS_STRUCTURE_ELEMENT(poppler_structure_element), FALSE); g_return_val_if_fail(color != nullptr, FALSE); const Object *value = attr_value_or_default(poppler_structure_element, Attribute::BackgroundColor); if (value == nullptr) { return FALSE; } convert_color(value, color); return TRUE; } /** * poppler_structure_element_get_border_color: * @poppler_structure_element: A #PopplerStructureElement * @colors: (out) (array fixed-size=4) (element-type PopplerColor): An array * of four #PopplerColor. * * Obtains the color of border around the 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). * If this attribute is not specified, the border color for this element shall * be the current text fill color in effect at the start of its associated * content. * * Return value: %TRUE if a color is defined for the element, * %FALSE otherwise. * * Since: 0.26 */ gboolean poppler_structure_element_get_border_color(PopplerStructureElement *poppler_structure_element, PopplerColor *colors) { g_return_val_if_fail(POPPLER_IS_STRUCTURE_ELEMENT(poppler_structure_element), FALSE); g_return_val_if_fail(colors != nullptr, FALSE); const Object *value = attr_value_or_default(poppler_structure_element, Attribute::BorderColor); if (value == nullptr) { return FALSE; } g_assert(value->isArray()); if (value->arrayGetLength() == 4) { // One color per side. for (guint i = 0; i < 4; i++) { Object item = value->arrayGet(i); convert_color(&item, &colors[i]); } } else { // Same color in all sides. g_assert(value->arrayGetLength() == 3); convert_color(value, &colors[0]); colors[1] = colors[2] = colors[3] = colors[0]; } return TRUE; } static inline void convert_double_or_4_doubles(const Object *object, gdouble *value) { g_assert(object != nullptr); if (object->isArray()) { g_assert(object->arrayGetLength() == 4); for (guint i = 0; i < 4; i++) { value[i] = object->arrayGet(i).getNum(); } } else { g_assert(object->isNum()); value[0] = value[1] = value[2] = value[3] = object->getNum(); } } /** * poppler_structure_element_get_border_thickness: * @poppler_structure_element: A #PopplerStructureElement * @border_thicknesses: (out) (array fixed-size=4) (element-type gdouble): * Array with the four values of 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). * A value of 0 indicates that the border shall not be drawn. * * Return value: %TRUE if the border thickness attribute is defined for * the element, %FALSE otherwise. * * Since: 0.26 */ gboolean poppler_structure_element_get_border_thickness(PopplerStructureElement *poppler_structure_element, gdouble *border_thicknesses) { g_return_val_if_fail(POPPLER_IS_STRUCTURE_ELEMENT(poppler_structure_element), FALSE); g_return_val_if_fail(border_thicknesses != nullptr, FALSE); const Object *value = attr_value_or_default(poppler_structure_element, Attribute::BorderThickness); if (value == nullptr) { return FALSE; } convert_double_or_4_doubles(value, border_thicknesses); return TRUE; } /** * poppler_structure_element_get_padding: * @poppler_structure_element: A #PopplerStructureElement * @paddings: (out) (array fixed-size=4) (element-type gdouble): * Padding for the four sides of the element. * * 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. * * Since: 0.26 */ void poppler_structure_element_get_padding(PopplerStructureElement *poppler_structure_element, gdouble *paddings) { g_return_if_fail(POPPLER_IS_STRUCTURE_ELEMENT(poppler_structure_element)); g_return_if_fail(paddings != nullptr); convert_double_or_4_doubles(attr_value_or_default(poppler_structure_element, Attribute::Padding), paddings); } /* Layout Attributes for block-level structure elements */ /** * poppler_structure_element_get_space_before: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the amount of empty space before the block-level structure element. * * Return value: A positive value. * * Since: 0.26 */ gdouble poppler_structure_element_get_space_before(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(poppler_structure_element_is_block(poppler_structure_element), NAN); return attr_value_or_default(poppler_structure_element, Attribute::SpaceBefore)->getNum(); } /** * poppler_structure_element_get_space_after: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the amount of empty space after the block-level structure element. * * Return value: A positive value. * * Since: 0.26 */ gdouble poppler_structure_element_get_space_after(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(poppler_structure_element_is_block(poppler_structure_element), NAN); return attr_value_or_default(poppler_structure_element, Attribute::SpaceAfter)->getNum(); } /** * poppler_structure_element_get_start_indent: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the amount of indentation at the beginning of the block-level structure element. * * Return value: A numeric value. * * Since: 0.26 */ gdouble poppler_structure_element_get_start_indent(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(poppler_structure_element_is_block(poppler_structure_element), NAN); return attr_value_or_default(poppler_structure_element, Attribute::StartIndent)->getNum(); } /** * poppler_structure_element_get_end_indent: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the amount of indentation at the end of the block-level structure element. * * Return value: A numeric value. * * Since: 0.26 */ gdouble poppler_structure_element_get_end_indent(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(poppler_structure_element_is_block(poppler_structure_element), NAN); return attr_value_or_default(poppler_structure_element, Attribute::EndIndent)->getNum(); } /** * poppler_structure_element_get_text_indent: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the amount of indentation of the text contained in the block-level structure element. * * Return value: A numeric value. * * Since: 0.26 */ gdouble poppler_structure_element_get_text_indent(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(poppler_structure_element_is_block(poppler_structure_element), NAN); return attr_value_or_default(poppler_structure_element, Attribute::TextIndent)->getNum(); } /** * poppler_structure_element_get_text_align: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the text alignment mode of the text contained into a * block-level structure element. * * Return value: A #PopplerStructureTextAlign value. * * Since: 0.26 */ PopplerStructureTextAlign poppler_structure_element_get_text_align(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(poppler_structure_element_is_block(poppler_structure_element), EnumNameValue::values[0].value); return attr_to_enum(poppler_structure_element); } /** * poppler_structure_element_get_bounding_box: * @poppler_structure_element: A #PopplerStructureElement * @bounding_box: (out): A #PopplerRectangle. * * Obtains the size of the bounding box of a block-level structure element. * * Return value: %TRUE if a bounding box is defined for the element, * %FALSE otherwise. * * Since: 0.26 */ gboolean poppler_structure_element_get_bounding_box(PopplerStructureElement *poppler_structure_element, PopplerRectangle *bounding_box) { g_return_val_if_fail(poppler_structure_element_is_block(poppler_structure_element), FALSE); g_return_val_if_fail(bounding_box != nullptr, FALSE); const Object *value = attr_value_or_default(poppler_structure_element, Attribute::BBox); if (value == nullptr) { return FALSE; } gdouble dimensions[4]; convert_double_or_4_doubles(value, dimensions); bounding_box->x1 = dimensions[0]; bounding_box->y1 = dimensions[1]; bounding_box->x2 = dimensions[2]; bounding_box->y2 = dimensions[3]; return TRUE; } /** * poppler_structure_element_get_width: * @poppler_structure_element: A #PopplerStructureElement * * 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. * * Return value: A positive value if a width is defined, or -1 * if the width is to be calculated automatically. * * Since: 0.26 */ gdouble poppler_structure_element_get_width(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(poppler_structure_element_is_block(poppler_structure_element), NAN); const Object *value = attr_value_or_default(poppler_structure_element, Attribute::Width); return value->isName("Auto") ? -1.0 : value->getNum(); } /** * poppler_structure_element_get_height: * @poppler_structure_element: A #PopplerStructureElement * * 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. * * Return value: A positive value if a width is defined, or -1 * if the height is to be calculated automatically. * * Since: 0.26 */ gdouble poppler_structure_element_get_height(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(poppler_structure_element_is_block(poppler_structure_element), NAN); const Object *value = attr_value_or_default(poppler_structure_element, Attribute::Height); return value->isName("Auto") ? -1.0 : value->getNum(); } /** * poppler_structure_element_get_block_align: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the block-alignment mode of the block-level structure element. * * Return value: A #PopplerStructureBlockAlign value. * * Since: 0.26 */ PopplerStructureBlockAlign poppler_structure_element_get_block_align(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(poppler_structure_element_is_block(poppler_structure_element), EnumNameValue::values[0].value); return attr_to_enum(poppler_structure_element); } /** * poppler_structure_element_get_inline_align: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the inline-alignment mode of the block-level structure element. * * Return value: A #PopplerStructureInlineAlign value. * * Since: 0.26 */ PopplerStructureInlineAlign poppler_structure_element_get_inline_align(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(poppler_structure_element_is_block(poppler_structure_element), EnumNameValue::values[0].value); return attr_to_enum(poppler_structure_element); } /** * poppler_structure_element_get_table_border_style: * @poppler_structure_element: A #PopplerStructureElement * @border_styles: (out) (array fixed-size=4) (element-type PopplerStructureBorderStyle): * An array of four #PopplerStructureBorderStyle elements. * * Obtains the table cell border style of a block-level structure element. The result values * are in before-after-start-end ordering. For example, using Western * left-to-right writing, that is top-bottom-left-right. * * Since: 0.26 */ void poppler_structure_element_get_table_border_style(PopplerStructureElement *poppler_structure_element, PopplerStructureBorderStyle *border_styles) { g_return_if_fail(poppler_structure_element_is_block(poppler_structure_element)); g_return_if_fail(border_styles != nullptr); convert_border_style(attr_value_or_default(poppler_structure_element, Attribute::TBorderStyle), border_styles); } /** * poppler_structure_element_get_table_padding: * @poppler_structure_element: A #PopplerStructureElement * @paddings: (out) (array fixed-size=4) (element-type gdouble): * Padding for the four sides of the element. * * 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 * Western left-to-right writing, that is top-bottom-left-right). * * Since: 0.26 */ void poppler_structure_element_get_table_padding(PopplerStructureElement *poppler_structure_element, gdouble *paddings) { g_return_if_fail(poppler_structure_element_is_block(poppler_structure_element)); g_return_if_fail(paddings != nullptr); convert_double_or_4_doubles(attr_value_or_default(poppler_structure_element, Attribute::TPadding), paddings); } /* Layout Attributes for inline-level structure elements */ /** * poppler_structure_element_get_baseline_shift: * @poppler_structure_element: A #PopplerStructureElement * * Obtains how much the text contained in the inline-level structure element should be shifted, * measuring from the baseline of the glyphs. * * Return value: A numeric value. * * Since: 0.26 */ gdouble poppler_structure_element_get_baseline_shift(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(poppler_structure_element_is_inline(poppler_structure_element), NAN); return attr_value_or_default(poppler_structure_element, Attribute::BaselineShift)->getNum(); } /** * poppler_structure_element_get_line_height: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the line height for the text contained in the inline-level structure element. * Note that for elements which do not specify a line height, it has to be calculated, * and in this case -1 is returned. * * Return value: A positive value if a line height is defined, or -1 * if the height is to be calculated automatically. * * Since: 0.26 */ gdouble poppler_structure_element_get_line_height(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(poppler_structure_element_is_inline(poppler_structure_element), NAN); const Object *value = attr_value_or_default(poppler_structure_element, Attribute::LineHeight); return (value->isName("Normal") || value->isName("Auto")) ? -1.0 : value->getNum(); } /** * poppler_structure_element_get_text_decoration_color: * @poppler_structure_element: A #PopplerStructureElement * @color: (out): A #PopplerColor. * * Obtains the color of the text decoration for the text contained * in the inline-level structure element. * If this attribute is not specified, the color for this element shall be the current fill * color in effect at the start of its associated content. * * Return value: %TRUE if a color is defined for the element, * %FALSE otherwise. * * Since: 0.26 */ gboolean poppler_structure_element_get_text_decoration_color(PopplerStructureElement *poppler_structure_element, PopplerColor *color) { g_return_val_if_fail(poppler_structure_element_is_inline(poppler_structure_element), FALSE); g_return_val_if_fail(color != nullptr, FALSE); const Object *value = attr_value_or_default(poppler_structure_element, Attribute::TextDecorationColor); if (value == nullptr) { return FALSE; } convert_color(value, color); return FALSE; } /** * poppler_structure_element_get_text_decoration_thickness: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the thickness of the text decoration for the text contained * in the inline-level structure element. * If this attribute is not specified, it shall be derived from the current * stroke thickness in effect at the start of the element’s associated content. * * Return value: Thickness of the text decoration, or NAN if not defined. * * Since: 0.26 */ gdouble poppler_structure_element_get_text_decoration_thickness(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(poppler_structure_element_is_inline(poppler_structure_element), NAN); const Object *value = attr_value_or_default(poppler_structure_element, Attribute::TextDecorationThickness); return (value == nullptr) ? NAN : value->getNum(); } /** * poppler_structure_element_get_text_decoration_type: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the text decoration type of the text contained in the * inline-level structure element. * * Return value: A #PopplerStructureTextDecoration value. * * Since: 0.26 */ PopplerStructureTextDecoration poppler_structure_element_get_text_decoration_type(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(poppler_structure_element_is_inline(poppler_structure_element), EnumNameValue::values[0].value); return attr_to_enum(poppler_structure_element); } /** * poppler_structure_element_get_ruby_align: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the alignment for the ruby text contained in a * inline-level structure element. * * Return value: A #PopplerStructureRubyAlign value. * * Since: 0.26 */ PopplerStructureRubyAlign poppler_structure_element_get_ruby_align(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(poppler_structure_element_is_inline(poppler_structure_element), EnumNameValue::values[0].value); return attr_to_enum(poppler_structure_element); } /** * poppler_structure_element_get_ruby_position: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the position for the ruby text contained in a * inline-level structure element. * * Return value: A #PopplerStructureRubyPosition value. * * Since: 0.26 */ PopplerStructureRubyPosition poppler_structure_element_get_ruby_position(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(poppler_structure_element_is_inline(poppler_structure_element), EnumNameValue::values[0].value); return attr_to_enum(poppler_structure_element); } /** * poppler_structure_element_get_glyph_orientation: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the glyph orientation for the text contained in a * inline-level structure element. * * Return value: A #PopplerStructureGlyphOrientation value. * * Since: 0.26 */ PopplerStructureGlyphOrientation poppler_structure_element_get_glyph_orientation(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(poppler_structure_element_is_inline(poppler_structure_element), EnumNameValue::values[0].value); return attr_to_enum(poppler_structure_element); } /* Column Attributes */ /** * poppler_structure_element_get_column_count: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the number of columns used to lay out the content contained * in the grouping element. * * Return value: Number of columns. * * Since: 0.26 */ guint poppler_structure_element_get_column_count(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(poppler_structure_element_is_grouping(poppler_structure_element), 0); return static_cast(attr_value_or_default(poppler_structure_element, Attribute::ColumnCount)->getInt()); } /** * poppler_structure_element_get_column_gaps: * @poppler_structure_element: A #PopplerStructureElement * @n_values: (out): Size of the returned array. * * 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 * is set to zero. * * 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. * * Return value: (transfer full) (array length=n_values) (element-type gdouble): * Array containing the values for the column gaps, or %NULL if the * array is empty or the attribute is not defined. * * Since: 0.26 */ gdouble *poppler_structure_element_get_column_gaps(PopplerStructureElement *poppler_structure_element, guint *n_values) { g_return_val_if_fail(poppler_structure_element_is_grouping(poppler_structure_element), NULL); g_return_val_if_fail(n_values != nullptr, NULL); const Object *value = attr_value_or_default(poppler_structure_element, Attribute::ColumnGap); if (value == nullptr) { *n_values = static_cast(-1); return nullptr; } gdouble *result = nullptr; convert_doubles_array(value, &result, n_values); return result; } /** * poppler_structure_element_get_column_widths: * @poppler_structure_element: A #PopplerStructureElement * @n_values: (out): Size of the returned array. * * 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. * * Return value: (transfer full) (array length=n_values) (element-type gdouble): * Array containing widths of the columns, or %NULL if the attribute * is not defined. * * Since: 0.26 */ gdouble *poppler_structure_element_get_column_widths(PopplerStructureElement *poppler_structure_element, guint *n_values) { g_return_val_if_fail(poppler_structure_element_is_grouping(poppler_structure_element), NULL); g_return_val_if_fail(n_values != nullptr, NULL); const Object *value = attr_value_or_default(poppler_structure_element, Attribute::ColumnWidths); if (value == nullptr) { return nullptr; } gdouble *result = nullptr; convert_doubles_array(value, &result, n_values); return result; } /* List Attribute */ /** * poppler_structure_element_get_list_numbering: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the list numbering style for list items. * * Return value: A #PopplerStructureListNumbering value. * * Since: 0.26 */ PopplerStructureListNumbering poppler_structure_element_get_list_numbering(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(poppler_structure_element_get_kind(poppler_structure_element) == POPPLER_STRUCTURE_ELEMENT_LIST_ITEM, EnumNameValue::values[0].value); return attr_to_enum(poppler_structure_element); } /* PrintField Attributes */ /** * poppler_structure_element_get_form_role: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the role of a form structure element that is part of a form, or is * a form field. This hints how the control for the element is intended * to be rendered. * * Return value: A #PopplerStructureFormRole value. * * Since: 0.26 */ PopplerStructureFormRole poppler_structure_element_get_form_role(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(poppler_structure_element_get_kind(poppler_structure_element) == POPPLER_STRUCTURE_ELEMENT_FORM, EnumNameValue::values[0].value); /* * The Role attribute can actually be undefined. */ const Object *value = attr_value_or_default(poppler_structure_element, Attribute::Role); if (value == nullptr) { return POPPLER_STRUCTURE_FORM_ROLE_UNDEFINED; } return name_to_enum(value); } /** * poppler_structure_element_get_form_state: * @poppler_structure_element: A #PopplerStructureElement * * For a structure element that is a form field, obtains in which state * the associated control is expected to be rendered. * * Return value: A #PopplerStructureFormState value. * * Since: 0.26 */ PopplerStructureFormState poppler_structure_element_get_form_state(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(poppler_structure_element_get_kind(poppler_structure_element) == POPPLER_STRUCTURE_ELEMENT_FORM, EnumNameValue::values[0].value); return attr_to_enum(poppler_structure_element); } /** * poppler_structure_element_get_form_description: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the textual description of the form element. Note that the * description is for informative purposes, and it is not intended * to be rendered. For example, assistive technologies may use the * description field to provide an alternate way of presenting an * element to the user. * * The returned string is allocated by the function. When it is * not needed anymore, be sure to call g_free() on it. * * Return value: (transfer full): A string, or %NULL if the attribute * is not defined. * * Since: 0.26 */ gchar *poppler_structure_element_get_form_description(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(poppler_structure_element_get_kind(poppler_structure_element) == POPPLER_STRUCTURE_ELEMENT_FORM, NULL); const Object *value = attr_value_or_default(poppler_structure_element, Attribute::Desc); if (value == nullptr) { return nullptr; } if (value->isString()) { return _poppler_goo_string_to_utf8(value->getString()); } if (value->isName()) { return g_strdup(value->getName()); } g_assert_not_reached(); return nullptr; } /* Table Attributes */ /** * poppler_structure_element_get_table_row_span: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the number of rows the table element spans to. * * Return value: A positive, non-zero value. * * Since: 0.26 */ guint poppler_structure_element_get_table_row_span(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(poppler_structure_element_get_kind(poppler_structure_element) == POPPLER_STRUCTURE_ELEMENT_TABLE, 0); return static_cast(attr_value_or_default(poppler_structure_element, Attribute::RowSpan)->getInt()); } /** * poppler_structure_element_get_table_column_span: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the number of columns the table element spans to. * * Return value: A positive, non-zero value. * * Since: 0.26 */ guint poppler_structure_element_get_table_column_span(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(poppler_structure_element_get_kind(poppler_structure_element) == POPPLER_STRUCTURE_ELEMENT_TABLE, 0); return static_cast(attr_value_or_default(poppler_structure_element, Attribute::ColSpan)->getInt()); } /** * poppler_structure_element_get_table_headers: * @poppler_structure_element: A #PopplerStructureElement * * 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(). * * Return value: (transfer full) (array zero-terminated=1) (element-type gchar*): * Zero-terminated array of strings with the table header names, * or %NULL if the attribute is not defined. * * Since: 0.26 */ gchar **poppler_structure_element_get_table_headers(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(poppler_structure_element_get_kind(poppler_structure_element) == POPPLER_STRUCTURE_ELEMENT_TABLE, NULL); const Object *value = attr_value_or_default(poppler_structure_element, Attribute::Headers); if (value == nullptr) { return nullptr; } g_assert(value->isArray()); const guint n_values = value->arrayGetLength(); gchar **result = g_new0(gchar *, n_values + 1); for (guint i = 0; i < n_values; i++) { Object item = value->arrayGet(i); if (item.isString()) { result[i] = _poppler_goo_string_to_utf8(item.getString()); } else if (item.isName()) { result[i] = g_strdup(item.getName()); } else { g_assert_not_reached(); } } return result; } /** * poppler_structure_element_get_table_scope: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the scope of a table structure element. * * Return value: A #PopplerStructureTableScope value. * * Since: 0.26 */ PopplerStructureTableScope poppler_structure_element_get_table_scope(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(poppler_structure_element_get_kind(poppler_structure_element) == POPPLER_STRUCTURE_ELEMENT_TABLE, EnumNameValue::values[0].value); return attr_to_enum(poppler_structure_element); } /** * poppler_structure_element_get_table_summary: * @poppler_structure_element: A #PopplerStructureElement * * Obtains the textual summary of the contents of the table element. Note that * the summary is meant for informative purposes, and it is not intended * to be rendered. For example, assistive technologies may use the * description field to provide an alternate way of presenting an element * to the user, or a document indexer may want to scan it for additional * keywords. * * The returned string is allocated by the function. When it is * not needed anymore, be sure to call g_free() on it. * * Return value: (transfer full): A string, or %NULL if the attribute * is not defined. * * Since: 0.26 */ gchar *poppler_structure_element_get_table_summary(PopplerStructureElement *poppler_structure_element) { g_return_val_if_fail(POPPLER_IS_STRUCTURE_ELEMENT(poppler_structure_element), NULL); const Object *value = attr_value_or_default(poppler_structure_element, Attribute::Summary); if (value == nullptr) { return nullptr; } if (value->isString()) { return _poppler_goo_string_to_utf8(value->getString()); } if (value->isName()) { return g_strdup(value->getName()); } g_assert_not_reached(); return nullptr; } poppler-24.02.0/glib/poppler-structure-element.h000066400000000000000000000407561455701731300216160ustar00rootroot00000000000000/* poppler-structure-element.h: glib interface to poppler * * Copyright (C) 2013 Igalia S.L. * * 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_STRUCTURE_ELEMENT_H__ #define __POPPLER_STRUCTURE_ELEMENT_H__ #include #include "poppler.h" G_BEGIN_DECLS #define POPPLER_TYPE_STRUCTURE_ELEMENT (poppler_structure_element_get_type()) #define POPPLER_STRUCTURE_ELEMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), POPPLER_TYPE_STRUCTURE_ELEMENT, PopplerStructureElement)) #define POPPLER_IS_STRUCTURE_ELEMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), POPPLER_TYPE_STRUCTURE_ELEMENT)) /** * PopplerStructureElementKind: */ typedef enum { POPPLER_STRUCTURE_ELEMENT_CONTENT, POPPLER_STRUCTURE_ELEMENT_OBJECT_REFERENCE, POPPLER_STRUCTURE_ELEMENT_DOCUMENT, POPPLER_STRUCTURE_ELEMENT_PART, POPPLER_STRUCTURE_ELEMENT_ARTICLE, POPPLER_STRUCTURE_ELEMENT_SECTION, POPPLER_STRUCTURE_ELEMENT_DIV, POPPLER_STRUCTURE_ELEMENT_SPAN, POPPLER_STRUCTURE_ELEMENT_QUOTE, POPPLER_STRUCTURE_ELEMENT_NOTE, POPPLER_STRUCTURE_ELEMENT_REFERENCE, POPPLER_STRUCTURE_ELEMENT_BIBENTRY, POPPLER_STRUCTURE_ELEMENT_CODE, POPPLER_STRUCTURE_ELEMENT_LINK, POPPLER_STRUCTURE_ELEMENT_ANNOT, POPPLER_STRUCTURE_ELEMENT_BLOCKQUOTE, POPPLER_STRUCTURE_ELEMENT_CAPTION, POPPLER_STRUCTURE_ELEMENT_NONSTRUCT, POPPLER_STRUCTURE_ELEMENT_TOC, POPPLER_STRUCTURE_ELEMENT_TOC_ITEM, POPPLER_STRUCTURE_ELEMENT_INDEX, POPPLER_STRUCTURE_ELEMENT_PRIVATE, POPPLER_STRUCTURE_ELEMENT_PARAGRAPH, POPPLER_STRUCTURE_ELEMENT_HEADING, POPPLER_STRUCTURE_ELEMENT_HEADING_1, POPPLER_STRUCTURE_ELEMENT_HEADING_2, POPPLER_STRUCTURE_ELEMENT_HEADING_3, POPPLER_STRUCTURE_ELEMENT_HEADING_4, POPPLER_STRUCTURE_ELEMENT_HEADING_5, POPPLER_STRUCTURE_ELEMENT_HEADING_6, POPPLER_STRUCTURE_ELEMENT_LIST, POPPLER_STRUCTURE_ELEMENT_LIST_ITEM, POPPLER_STRUCTURE_ELEMENT_LIST_LABEL, POPPLER_STRUCTURE_ELEMENT_LIST_BODY, POPPLER_STRUCTURE_ELEMENT_TABLE, POPPLER_STRUCTURE_ELEMENT_TABLE_ROW, POPPLER_STRUCTURE_ELEMENT_TABLE_HEADING, POPPLER_STRUCTURE_ELEMENT_TABLE_DATA, POPPLER_STRUCTURE_ELEMENT_TABLE_HEADER, POPPLER_STRUCTURE_ELEMENT_TABLE_FOOTER, POPPLER_STRUCTURE_ELEMENT_TABLE_BODY, POPPLER_STRUCTURE_ELEMENT_RUBY, POPPLER_STRUCTURE_ELEMENT_RUBY_BASE_TEXT, POPPLER_STRUCTURE_ELEMENT_RUBY_ANNOT_TEXT, POPPLER_STRUCTURE_ELEMENT_RUBY_PUNCTUATION, POPPLER_STRUCTURE_ELEMENT_WARICHU, POPPLER_STRUCTURE_ELEMENT_WARICHU_TEXT, POPPLER_STRUCTURE_ELEMENT_WARICHU_PUNCTUATION, POPPLER_STRUCTURE_ELEMENT_FIGURE, POPPLER_STRUCTURE_ELEMENT_FORMULA, POPPLER_STRUCTURE_ELEMENT_FORM, } PopplerStructureElementKind; /** * PopplerStructureGetTextFlags: * @POPPLER_STRUCTURE_GET_TEXT_NONE: No flags. * @POPPLER_STRUCTURE_GET_TEXT_RECURSIVE: For non-leaf, non-content * elements, recursively obtain the text from all the elements * enclosed in the subtree. */ typedef enum { POPPLER_STRUCTURE_GET_TEXT_NONE = 0, POPPLER_STRUCTURE_GET_TEXT_RECURSIVE = (1 << 0), } PopplerStructureGetTextFlags; /** * PopplerStructurePlacement: */ typedef enum { POPPLER_STRUCTURE_PLACEMENT_BLOCK, POPPLER_STRUCTURE_PLACEMENT_INLINE, POPPLER_STRUCTURE_PLACEMENT_BEFORE, POPPLER_STRUCTURE_PLACEMENT_START, POPPLER_STRUCTURE_PLACEMENT_END, } PopplerStructurePlacement; /** * PopplerStructureWritingMode: */ typedef enum { POPPLER_STRUCTURE_WRITING_MODE_LR_TB, POPPLER_STRUCTURE_WRITING_MODE_RL_TB, POPPLER_STRUCTURE_WRITING_MODE_TB_RL, } PopplerStructureWritingMode; /** * PopplerStructureBorderStyle: */ typedef enum { POPPLER_STRUCTURE_BORDER_STYLE_NONE, POPPLER_STRUCTURE_BORDER_STYLE_HIDDEN, POPPLER_STRUCTURE_BORDER_STYLE_DOTTED, POPPLER_STRUCTURE_BORDER_STYLE_DASHED, POPPLER_STRUCTURE_BORDER_STYLE_SOLID, POPPLER_STRUCTURE_BORDER_STYLE_DOUBLE, POPPLER_STRUCTURE_BORDER_STYLE_GROOVE, POPPLER_STRUCTURE_BORDER_STYLE_INSET, POPPLER_STRUCTURE_BORDER_STYLE_OUTSET, } PopplerStructureBorderStyle; /** * PopplerStructureTextAlign: */ typedef enum { POPPLER_STRUCTURE_TEXT_ALIGN_START, POPPLER_STRUCTURE_TEXT_ALIGN_CENTER, POPPLER_STRUCTURE_TEXT_ALIGN_END, POPPLER_STRUCTURE_TEXT_ALIGN_JUSTIFY, } PopplerStructureTextAlign; /** * PopplerStructureBlockAlign: */ typedef enum { POPPLER_STRUCTURE_BLOCK_ALIGN_BEFORE, POPPLER_STRUCTURE_BLOCK_ALIGN_MIDDLE, POPPLER_STRUCTURE_BLOCK_ALIGN_AFTER, POPPLER_STRUCTURE_BLOCK_ALIGN_JUSTIFY, } PopplerStructureBlockAlign; /** * PopplerStructureInlineAlign: */ typedef enum { POPPLER_STRUCTURE_INLINE_ALIGN_START, POPPLER_STRUCTURE_INLINE_ALIGN_CENTER, POPPLER_STRUCTURE_INLINE_ALIGN_END, } PopplerStructureInlineAlign; /** * PopplerStructureTextDecoration: */ typedef enum { POPPLER_STRUCTURE_TEXT_DECORATION_NONE, POPPLER_STRUCTURE_TEXT_DECORATION_UNDERLINE, POPPLER_STRUCTURE_TEXT_DECORATION_OVERLINE, POPPLER_STRUCTURE_TEXT_DECORATION_LINETHROUGH, } PopplerStructureTextDecoration; /** * PopplerStructureRubyAlign: */ typedef enum { POPPLER_STRUCTURE_RUBY_ALIGN_START, POPPLER_STRUCTURE_RUBY_ALIGN_CENTER, POPPLER_STRUCTURE_RUBY_ALIGN_END, POPPLER_STRUCTURE_RUBY_ALIGN_JUSTIFY, POPPLER_STRUCTURE_RUBY_ALIGN_DISTRIBUTE, } PopplerStructureRubyAlign; /** * PopplerStructureRubyPosition: */ typedef enum { POPPLER_STRUCTURE_RUBY_POSITION_BEFORE, POPPLER_STRUCTURE_RUBY_POSITION_AFTER, POPPLER_STRUCTURE_RUBY_POSITION_WARICHU, POPPLER_STRUCTURE_RUBY_POSITION_INLINE, } PopplerStructureRubyPosition; /** * PopplerStructureGlyphOrientation: */ typedef enum { POPPLER_STRUCTURE_GLYPH_ORIENTATION_AUTO, POPPLER_STRUCTURE_GLYPH_ORIENTATION_0 = POPPLER_STRUCTURE_GLYPH_ORIENTATION_AUTO, POPPLER_STRUCTURE_GLYPH_ORIENTATION_90, POPPLER_STRUCTURE_GLYPH_ORIENTATION_180, POPPLER_STRUCTURE_GLYPH_ORIENTATION_270, } PopplerStructureGlyphOrientation; /** * PopplerStructureListNumbering: */ typedef enum { POPPLER_STRUCTURE_LIST_NUMBERING_NONE, POPPLER_STRUCTURE_LIST_NUMBERING_DISC, POPPLER_STRUCTURE_LIST_NUMBERING_CIRCLE, POPPLER_STRUCTURE_LIST_NUMBERING_SQUARE, POPPLER_STRUCTURE_LIST_NUMBERING_DECIMAL, POPPLER_STRUCTURE_LIST_NUMBERING_UPPER_ROMAN, POPPLER_STRUCTURE_LIST_NUMBERING_LOWER_ROMAN, POPPLER_STRUCTURE_LIST_NUMBERING_UPPER_ALPHA, POPPLER_STRUCTURE_LIST_NUMBERING_LOWER_ALPHA, } PopplerStructureListNumbering; /** * PopplerStructureFormRole: */ typedef enum { POPPLER_STRUCTURE_FORM_ROLE_UNDEFINED, POPPLER_STRUCTURE_FORM_ROLE_RADIO_BUTTON, POPPLER_STRUCTURE_FORM_ROLE_PUSH_BUTTON, POPPLER_STRUCTURE_FORM_ROLE_TEXT_VALUE, POPPLER_STRUCTURE_FORM_ROLE_CHECKBOX, } PopplerStructureFormRole; /** * PopplerStructureFormState: */ typedef enum { POPPLER_STRUCTURE_FORM_STATE_ON, POPPLER_STRUCTURE_FORM_STATE_OFF, POPPLER_STRUCTURE_FORM_STATE_NEUTRAL, } PopplerStructureFormState; /** * PopplerStructureTableScope: */ typedef enum { POPPLER_STRUCTURE_TABLE_SCOPE_ROW, POPPLER_STRUCTURE_TABLE_SCOPE_COLUMN, POPPLER_STRUCTURE_TABLE_SCOPE_BOTH, } PopplerStructureTableScope; POPPLER_PUBLIC GType poppler_structure_element_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerStructureElementKind poppler_structure_element_get_kind(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC gint poppler_structure_element_get_page(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC gboolean poppler_structure_element_is_content(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC gboolean poppler_structure_element_is_inline(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC gboolean poppler_structure_element_is_block(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC gboolean poppler_structure_element_is_grouping(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC gchar *poppler_structure_element_get_id(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC gchar *poppler_structure_element_get_title(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC gchar *poppler_structure_element_get_abbreviation(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC gchar *poppler_structure_element_get_language(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC gchar *poppler_structure_element_get_text(PopplerStructureElement *poppler_structure_element, PopplerStructureGetTextFlags flags); POPPLER_PUBLIC gchar *poppler_structure_element_get_alt_text(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC gchar *poppler_structure_element_get_actual_text(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC PopplerTextSpan **poppler_structure_element_get_text_spans(PopplerStructureElement *poppler_structure_element, guint *n_text_spans); POPPLER_PUBLIC PopplerStructurePlacement poppler_structure_element_get_placement(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC PopplerStructureWritingMode poppler_structure_element_get_writing_mode(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC gboolean poppler_structure_element_get_background_color(PopplerStructureElement *poppler_structure_element, PopplerColor *color); POPPLER_PUBLIC gboolean poppler_structure_element_get_border_color(PopplerStructureElement *poppler_structure_element, PopplerColor *colors); POPPLER_PUBLIC void poppler_structure_element_get_border_style(PopplerStructureElement *poppler_structure_element, PopplerStructureBorderStyle *border_styles); POPPLER_PUBLIC gboolean poppler_structure_element_get_border_thickness(PopplerStructureElement *poppler_structure_element, gdouble *border_thicknesses); POPPLER_PUBLIC void poppler_structure_element_get_padding(PopplerStructureElement *poppler_structure_element, gdouble *paddings); POPPLER_PUBLIC gboolean poppler_structure_element_get_color(PopplerStructureElement *poppler_structure_element, PopplerColor *color); POPPLER_PUBLIC gdouble poppler_structure_element_get_space_before(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC gdouble poppler_structure_element_get_space_after(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC gdouble poppler_structure_element_get_start_indent(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC gdouble poppler_structure_element_get_end_indent(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC gdouble poppler_structure_element_get_text_indent(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC PopplerStructureTextAlign poppler_structure_element_get_text_align(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC gboolean poppler_structure_element_get_bounding_box(PopplerStructureElement *poppler_structure_element, PopplerRectangle *bounding_box); POPPLER_PUBLIC gdouble poppler_structure_element_get_width(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC gdouble poppler_structure_element_get_height(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC PopplerStructureBlockAlign poppler_structure_element_get_block_align(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC PopplerStructureInlineAlign poppler_structure_element_get_inline_align(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC void poppler_structure_element_get_table_border_style(PopplerStructureElement *poppler_structure_element, PopplerStructureBorderStyle *border_styles); POPPLER_PUBLIC void poppler_structure_element_get_table_padding(PopplerStructureElement *poppler_structure_element, gdouble *paddings); POPPLER_PUBLIC gdouble poppler_structure_element_get_baseline_shift(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC gdouble poppler_structure_element_get_line_height(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC gboolean poppler_structure_element_get_text_decoration_color(PopplerStructureElement *poppler_structure_element, PopplerColor *color); POPPLER_PUBLIC gdouble poppler_structure_element_get_text_decoration_thickness(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC PopplerStructureTextDecoration poppler_structure_element_get_text_decoration_type(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC PopplerStructureRubyAlign poppler_structure_element_get_ruby_align(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC PopplerStructureRubyPosition poppler_structure_element_get_ruby_position(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC PopplerStructureGlyphOrientation poppler_structure_element_get_glyph_orientation(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC guint poppler_structure_element_get_column_count(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC gdouble *poppler_structure_element_get_column_gaps(PopplerStructureElement *poppler_structure_element, guint *n_values); POPPLER_PUBLIC gdouble *poppler_structure_element_get_column_widths(PopplerStructureElement *poppler_structure_element, guint *n_values); POPPLER_PUBLIC PopplerStructureListNumbering poppler_structure_element_get_list_numbering(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC PopplerStructureFormRole poppler_structure_element_get_form_role(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC PopplerStructureFormState poppler_structure_element_get_form_state(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC gchar *poppler_structure_element_get_form_description(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC guint poppler_structure_element_get_table_row_span(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC guint poppler_structure_element_get_table_column_span(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC gchar **poppler_structure_element_get_table_headers(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC PopplerStructureTableScope poppler_structure_element_get_table_scope(PopplerStructureElement *poppler_structure_element); POPPLER_PUBLIC gchar *poppler_structure_element_get_table_summary(PopplerStructureElement *poppler_structure_element); #define POPPLER_TYPE_STRUCTURE_ELEMENT_ITER (poppler_structure_element_iter_get_type()) POPPLER_PUBLIC GType poppler_structure_element_iter_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerStructureElementIter *poppler_structure_element_iter_new(PopplerDocument *poppler_document); POPPLER_PUBLIC PopplerStructureElementIter *poppler_structure_element_iter_get_child(PopplerStructureElementIter *parent); POPPLER_PUBLIC PopplerStructureElementIter *poppler_structure_element_iter_copy(PopplerStructureElementIter *iter); POPPLER_PUBLIC PopplerStructureElement *poppler_structure_element_iter_get_element(PopplerStructureElementIter *iter); POPPLER_PUBLIC gboolean poppler_structure_element_iter_next(PopplerStructureElementIter *iter); POPPLER_PUBLIC void poppler_structure_element_iter_free(PopplerStructureElementIter *iter); #define POPPLER_TYPE_TEXT_SPAN (poppler_text_span_get_type()) POPPLER_PUBLIC GType poppler_text_span_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC PopplerTextSpan *poppler_text_span_copy(PopplerTextSpan *poppler_text_span); POPPLER_PUBLIC void poppler_text_span_free(PopplerTextSpan *poppler_text_span); POPPLER_PUBLIC gboolean poppler_text_span_is_fixed_width_font(PopplerTextSpan *poppler_text_span); POPPLER_PUBLIC gboolean poppler_text_span_is_serif_font(PopplerTextSpan *poppler_text_span); POPPLER_PUBLIC gboolean poppler_text_span_is_bold_font(PopplerTextSpan *poppler_text_span); POPPLER_PUBLIC void poppler_text_span_get_color(PopplerTextSpan *poppler_text_span, PopplerColor *color); POPPLER_PUBLIC const gchar *poppler_text_span_get_text(PopplerTextSpan *poppler_text_span); POPPLER_PUBLIC const gchar *poppler_text_span_get_font_name(PopplerTextSpan *poppler_text_span); G_END_DECLS #endif /* !__POPPLER_STRUCTURE_ELEMENT_H__ */ poppler-24.02.0/glib/poppler.cc000066400000000000000000000050441455701731300162560ustar00rootroot00000000000000/* poppler.cc: glib wrapper for poppler * Copyright (C) 2005, Red Hat, Inc. * * 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 "poppler.h" #ifndef __GI_SCANNER__ # include #endif #include "poppler-private.h" /** * SECTION: poppler-errors * @title: Error handling * @short_description: Error domain and codes * */ /** * POPPLER_ERROR: * * Error domain for poppler operations. Errors in this domain will * be from the #PopplerError enumeration. See #GError for information * on error domains. */ GQuark poppler_error_quark(void) { static GQuark q = 0; if (q == 0) { q = g_quark_from_static_string("poppler-quark"); } return q; } /** * poppler_get_backend: * * Returns the backend compiled into the poppler library. * * Return value: The backend used by poppler **/ PopplerBackend poppler_get_backend(void) { return POPPLER_BACKEND_CAIRO; } static const char poppler_version[] = PACKAGE_VERSION; /** * poppler_get_version: * * Returns the version of poppler in use. This result is not to be freed. * * Return value: the version of poppler. **/ const char *poppler_get_version(void) { return poppler_version; } /* We want to install an error callback so that PDF syntax warnings etc * can be redirected through the GLib logging API instead of always just * going to stderr. */ void _poppler_error_cb(ErrorCategory category, Goffset pos, const char *message) { static const char *const cat_str[] = { "Syntax warning", "Syntax error", nullptr, nullptr, "IO error", nullptr, "Unimplemented feature", "Internal error" }; /* The following will never occur in poppler-glib */ if (category == errConfig || category == errCommandLine || category == errNotAllowed) { return; } g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "%s at position %" G_GOFFSET_FORMAT ": %s", cat_str[category], (goffset)pos, message); } poppler-24.02.0/glib/poppler.h000066400000000000000000000230731455701731300161220ustar00rootroot00000000000000/* poppler.h: glib interface to poppler * Copyright (C) 2004, Red Hat, Inc. * Copyright (C) 2021 André Guerreiro * * 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_GLIB_H__ #define __POPPLER_GLIB_H__ #include #include "poppler-macros.h" G_BEGIN_DECLS POPPLER_PUBLIC GQuark poppler_error_quark(void); #define POPPLER_ERROR poppler_error_quark() /** * PopplerError: * @POPPLER_ERROR_INVALID: Generic error when a document operation fails * @POPPLER_ERROR_ENCRYPTED: Document is encrypted * @POPPLER_ERROR_OPEN_FILE: File could not be opened for writing when saving document * @POPPLER_ERROR_BAD_CATALOG: Failed to read the document catalog * @POPPLER_ERROR_DAMAGED: Document is damaged * * Error codes returned by #PopplerDocument */ typedef enum { POPPLER_ERROR_INVALID, POPPLER_ERROR_ENCRYPTED, POPPLER_ERROR_OPEN_FILE, POPPLER_ERROR_BAD_CATALOG, POPPLER_ERROR_DAMAGED, POPPLER_ERROR_SIGNING } PopplerError; /** * PopplerPageTransitionType: * @POPPLER_PAGE_TRANSITION_REPLACE: the new page replace the old one * @POPPLER_PAGE_TRANSITION_SPLIT: two lines sweep across the screen, revealing the new page * @POPPLER_PAGE_TRANSITION_BLINDS: multiple lines, evenly spaced across the screen, synchronously * sweep in the same direction to reveal the new page * @POPPLER_PAGE_TRANSITION_BOX: a rectangular box sweeps inward from the edges of the page or * outward from the center revealing the new page * @POPPLER_PAGE_TRANSITION_WIPE: a single line sweeps across the screen from one edge to the other * revealing the new page * @POPPLER_PAGE_TRANSITION_DISSOLVE: the old page dissolves gradually to reveal the new one * @POPPLER_PAGE_TRANSITION_GLITTER: similar to #POPPLER_PAGE_TRANSITION_DISSOLVE, except that the effect * sweeps across the page in a wide band moving from one side of the screen to the other * @POPPLER_PAGE_TRANSITION_FLY: changes are flown out or in to or from a location that is offscreen * @POPPLER_PAGE_TRANSITION_PUSH: the old page slides off the screen while the new page slides in * @POPPLER_PAGE_TRANSITION_COVER: the new page slides on to the screen covering the old page * @POPPLER_PAGE_TRANSITION_UNCOVER: the old page slides off the screen uncovering the new page * @POPPLER_PAGE_TRANSITION_FADE: the new page gradually becomes visible through the old one * * Page transition types */ typedef enum { POPPLER_PAGE_TRANSITION_REPLACE, POPPLER_PAGE_TRANSITION_SPLIT, POPPLER_PAGE_TRANSITION_BLINDS, POPPLER_PAGE_TRANSITION_BOX, POPPLER_PAGE_TRANSITION_WIPE, POPPLER_PAGE_TRANSITION_DISSOLVE, POPPLER_PAGE_TRANSITION_GLITTER, POPPLER_PAGE_TRANSITION_FLY, POPPLER_PAGE_TRANSITION_PUSH, POPPLER_PAGE_TRANSITION_COVER, POPPLER_PAGE_TRANSITION_UNCOVER, POPPLER_PAGE_TRANSITION_FADE } PopplerPageTransitionType; /** * PopplerPageTransitionAlignment: * @POPPLER_PAGE_TRANSITION_HORIZONTAL: horizontal dimension * @POPPLER_PAGE_TRANSITION_VERTICAL: vertical dimension * * Page transition alignment types for #POPPLER_PAGE_TRANSITION_SPLIT * and #POPPLER_PAGE_TRANSITION_BLINDS transition types */ typedef enum { POPPLER_PAGE_TRANSITION_HORIZONTAL, POPPLER_PAGE_TRANSITION_VERTICAL } PopplerPageTransitionAlignment; /** * PopplerPageTransitionDirection: * @POPPLER_PAGE_TRANSITION_INWARD: inward from the edges of the page * @POPPLER_PAGE_TRANSITION_OUTWARD: outward from the center of the page * * Page transition direction types for #POPPLER_PAGE_TRANSITION_SPLIT, * #POPPLER_PAGE_TRANSITION_BOX and #POPPLER_PAGE_TRANSITION_FLY transition types */ typedef enum { POPPLER_PAGE_TRANSITION_INWARD, POPPLER_PAGE_TRANSITION_OUTWARD } PopplerPageTransitionDirection; /** * PopplerSelectionStyle: * @POPPLER_SELECTION_GLYPH: glyph is the minimum unit for selection * @POPPLER_SELECTION_WORD: word is the minimum unit for selection * @POPPLER_SELECTION_LINE: line is the minimum unit for selection * * Selection styles */ typedef enum { POPPLER_SELECTION_GLYPH, POPPLER_SELECTION_WORD, POPPLER_SELECTION_LINE } PopplerSelectionStyle; /** * PopplerPrintFlags: * @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 * * Printing flags * * Since: 0.16 */ typedef enum /*< flags >*/ { POPPLER_PRINT_DOCUMENT = 0, POPPLER_PRINT_MARKUP_ANNOTS = 1 << 0, POPPLER_PRINT_STAMP_ANNOTS_ONLY = 1 << 1, POPPLER_PRINT_ALL = POPPLER_PRINT_MARKUP_ANNOTS } PopplerPrintFlags; /** * PopplerFindFlags: * @POPPLER_FIND_DEFAULT: use default search settings * @POPPLER_FIND_CASE_SENSITIVE: do case sensitive search * @POPPLER_FIND_BACKWARDS: search backwards * @POPPLER_FIND_WHOLE_WORDS_ONLY: search only whole words * @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_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. * * Flags using while searching text in a page * * Since: 0.22 */ typedef enum /*< flags >*/ { POPPLER_FIND_DEFAULT = 0, POPPLER_FIND_CASE_SENSITIVE = 1 << 0, POPPLER_FIND_BACKWARDS = 1 << 1, POPPLER_FIND_WHOLE_WORDS_ONLY = 1 << 2, POPPLER_FIND_IGNORE_DIACRITICS = 1 << 3, POPPLER_FIND_MULTILINE = 1 << 4 } PopplerFindFlags; typedef struct _PopplerDocument PopplerDocument; typedef struct _PopplerIndexIter PopplerIndexIter; typedef struct _PopplerFontsIter PopplerFontsIter; typedef struct _PopplerLayersIter PopplerLayersIter; typedef struct _PopplerPoint PopplerPoint; typedef struct _PopplerRectangle PopplerRectangle; typedef struct _PopplerTextAttributes PopplerTextAttributes; typedef struct _PopplerColor PopplerColor; typedef struct _PopplerLinkMapping PopplerLinkMapping; typedef struct _PopplerPageTransition PopplerPageTransition; typedef struct _PopplerImageMapping PopplerImageMapping; typedef struct _PopplerFormFieldMapping PopplerFormFieldMapping; typedef struct _PopplerAnnotMapping PopplerAnnotMapping; typedef struct _PopplerPage PopplerPage; typedef struct _PopplerFontInfo PopplerFontInfo; typedef struct _PopplerLayer PopplerLayer; typedef struct _PopplerPSFile PopplerPSFile; typedef union _PopplerAction PopplerAction; typedef struct _PopplerDest PopplerDest; typedef struct _PopplerActionLayer PopplerActionLayer; typedef struct _PopplerFormField PopplerFormField; typedef struct _PopplerAttachment PopplerAttachment; typedef struct _PopplerMovie PopplerMovie; typedef struct _PopplerMedia PopplerMedia; typedef struct _PopplerAnnot PopplerAnnot; typedef struct _PopplerAnnotMarkup PopplerAnnotMarkup; typedef struct _PopplerAnnotText PopplerAnnotText; typedef struct _PopplerAnnotTextMarkup PopplerAnnotTextMarkup; typedef struct _PopplerAnnotFreeText PopplerAnnotFreeText; typedef struct _PopplerAnnotFileAttachment PopplerAnnotFileAttachment; typedef struct _PopplerAnnotMovie PopplerAnnotMovie; typedef struct _PopplerAnnotScreen PopplerAnnotScreen; typedef struct _PopplerAnnotCalloutLine PopplerAnnotCalloutLine; typedef struct _PopplerAnnotLine PopplerAnnotLine; typedef struct _PopplerAnnotCircle PopplerAnnotCircle; typedef struct _PopplerAnnotSquare PopplerAnnotSquare; typedef struct _PopplerQuadrilateral PopplerQuadrilateral; typedef struct _PopplerStructureElement PopplerStructureElement; typedef struct _PopplerStructureElementIter PopplerStructureElementIter; 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: * @POPPLER_BACKEND_UNKNOWN: Unknown backend * @POPPLER_BACKEND_SPLASH: Splash backend * @POPPLER_BACKEND_CAIRO: Cairo backend * * Backend codes returned by poppler_get_backend(). */ typedef enum { POPPLER_BACKEND_UNKNOWN, POPPLER_BACKEND_SPLASH, POPPLER_BACKEND_CAIRO } PopplerBackend; POPPLER_PUBLIC PopplerBackend poppler_get_backend(void); POPPLER_PUBLIC const char *poppler_get_version(void); G_END_DECLS #include "poppler-features.h" #include "poppler-document.h" #include "poppler-page.h" #include "poppler-layer.h" #include "poppler-action.h" #include "poppler-form-field.h" #include "poppler-enums.h" #include "poppler-attachment.h" #include "poppler-annot.h" #include "poppler-date.h" #include "poppler-movie.h" #include "poppler-media.h" #include "poppler-structure-element.h" #endif /* __POPPLER_GLIB_H__ */ poppler-24.02.0/glib/reference/000077500000000000000000000000001455701731300162215ustar00rootroot00000000000000poppler-24.02.0/glib/reference/.gitignore000066400000000000000000000004121455701731300202060ustar00rootroot00000000000000Makefile Makefile.in poppler-decl-list.txt poppler-decl.txt poppler-undeclared.txt poppler-undocumented.txt poppler-unused.txt poppler.args poppler.hierarchy poppler.interfaces poppler.prerequisites poppler.signals deprecated html xml *.stamp *.lo .libs version.xml poppler-24.02.0/glib/reference/CMakeLists.txt000066400000000000000000000007521455701731300207650ustar00rootroot00000000000000add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/glib-docs-build.stamp DEPENDS poppler-glib COMMAND ${CMAKE_SOURCE_DIR}/make-glib-api-docs --src-dir=${CMAKE_SOURCE_DIR} --build-dir=${CMAKE_BINARY_DIR} COMMAND touch ${CMAKE_CURRENT_BINARY_DIR}/glib-docs-build.stamp ) add_custom_target(glib-docs ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/glib-docs-build.stamp) install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html/ DESTINATION "${CMAKE_INSTALL_DATADIR}/gtk-doc/html/poppler" ) poppler-24.02.0/glib/reference/poppler-docs.sgml000066400000000000000000000125131455701731300215160ustar00rootroot00000000000000 ]> Poppler Reference Manual for Poppler &version; Poppler Index of all symbols Index of deprecated symbols Index of new symbols in 0.12 Index of new symbols in 0.14 Index of new symbols in 0.16 Index of new symbols in 0.18 Index of new symbols in 0.20 Index of new symbols in 0.22 Index of new symbols in 0.26 Index of new symbols in 0.33 Index of new symbols in 0.46 Index of new symbols in 0.54 Index of new symbols in 0.70 Index of new symbols in 0.72 Index of new symbols in 0.73 Index of new symbols in 0.78 Index of new symbols in 0.80 Index of new symbols in 0.82 Index of new symbols in 0.88 Index of new symbols in 0.89 Index of new symbols in 0.90 Index of new symbols in 20.04.0 Index of new symbols in 20.09.0 Index of new symbols in 21.05.0 Index of new symbols in 21.12.0 poppler-24.02.0/glib/reference/poppler-overrides.txt000066400000000000000000000000001455701731300224310ustar00rootroot00000000000000poppler-24.02.0/glib/reference/poppler-sections.txt000066400000000000000000000622251455701731300222770ustar00rootroot00000000000000poppler.h
poppler-color Poppler Color POPPLER_TYPE_COLOR poppler_color_get_type
poppler-page Poppler Page PopplerAnnotMapping PopplerFindFlags PopplerFormFieldMapping PopplerImageMapping PopplerLinkMapping PopplerPage PopplerPageTransition PopplerPageTransitionAlignment PopplerPageTransitionDirection PopplerPageTransitionType PopplerPoint PopplerPrintFlags PopplerQuadrilateral PopplerRectangle PopplerSelectionStyle PopplerTextAttributes poppler_annot_mapping_copy poppler_annot_mapping_free poppler_annot_mapping_new poppler_form_field_mapping_copy poppler_form_field_mapping_free poppler_form_field_mapping_new poppler_image_mapping_copy poppler_image_mapping_free poppler_image_mapping_new poppler_link_mapping_copy poppler_link_mapping_free poppler_link_mapping_new poppler_page_add_annot poppler_page_find_text poppler_page_find_text_with_options poppler_page_free_annot_mapping poppler_page_free_form_field_mapping poppler_page_free_image_mapping poppler_page_free_link_mapping poppler_page_free_text_attributes poppler_page_get_annot_mapping poppler_page_get_bounding_box poppler_page_get_crop_box poppler_page_get_duration poppler_page_get_form_field_mapping poppler_page_get_image poppler_page_get_image_mapping poppler_page_get_index poppler_page_get_label poppler_page_get_link_mapping poppler_page_get_selected_region poppler_page_get_selected_text poppler_page_get_selection_region poppler_page_get_size poppler_page_get_text poppler_page_get_text_attributes poppler_page_get_text_attributes_for_area poppler_page_get_text_for_area poppler_page_get_text_layout poppler_page_get_text_layout_for_area poppler_page_get_thumbnail poppler_page_get_thumbnail_size poppler_page_get_transition poppler_page_remove_annot poppler_page_render poppler_page_render_for_printing poppler_page_render_for_printing_with_options poppler_page_render_selection poppler_page_render_to_ps poppler_page_selection_region_free poppler_page_transition_copy poppler_page_transition_free poppler_page_transition_new poppler_point_copy poppler_point_free poppler_point_new poppler_quadrilateral_copy poppler_quadrilateral_free poppler_quadrilateral_new poppler_rectangle_copy poppler_rectangle_find_get_match_continued poppler_rectangle_find_get_ignored_hyphen poppler_rectangle_free poppler_rectangle_new poppler_text_attributes_copy poppler_text_attributes_free poppler_text_attributes_new POPPLER_PAGE POPPLER_IS_PAGE POPPLER_TYPE_ANNOT_MAPPING POPPLER_TYPE_FIND_FLAGS POPPLER_TYPE_FORM_FIELD_MAPPING POPPLER_TYPE_IMAGE_MAPPING POPPLER_TYPE_LINK_MAPPING POPPLER_TYPE_PAGE POPPLER_TYPE_PAGE_TRANSITION POPPLER_TYPE_PAGE_TRANSITION_ALIGNMENT POPPLER_TYPE_PAGE_TRANSITION_DIRECTION POPPLER_TYPE_PAGE_TRANSITION_TYPE POPPLER_TYPE_POINT POPPLER_TYPE_PRINT_FLAGS POPPLER_TYPE_QUADRILATERAL POPPLER_TYPE_RECTANGLE POPPLER_TYPE_SELECTION_STYLE POPPLER_TYPE_TEXT_ATTRIBUTES poppler_annot_mapping_get_type poppler_find_flags_get_type poppler_form_field_mapping_get_type poppler_image_mapping_get_type poppler_link_mapping_get_type poppler_page_get_type poppler_page_transition_alignment_get_type poppler_page_transition_direction_get_type poppler_page_transition_get_type poppler_page_transition_type_get_type poppler_point_get_type poppler_print_flags_get_type poppler_quadrilateral_get_type poppler_rectangle_get_type poppler_selection_style_get_type poppler_signature_info_get_type poppler_text_attributes_get_type
poppler-document PopplerDocument PopplerDocument PopplerFontInfo PopplerFontType PopplerFontsIter PopplerIndexIter PopplerLayersIter PopplerPDFConformance PopplerPDFPart PopplerPDFSubtype PopplerPSFile PopplerPageLayout PopplerPageMode PopplerPageRange PopplerPermissions PopplerPrintDuplex PopplerPrintScaling PopplerViewerPreferences poppler_document_create_dests_tree poppler_document_find_dest poppler_document_get_attachments poppler_document_get_author poppler_document_get_creation_date poppler_document_get_creation_date_time poppler_document_get_creator poppler_document_get_form_field poppler_document_get_id poppler_document_get_keywords poppler_document_get_metadata poppler_document_get_modification_date poppler_document_get_modification_date_time poppler_document_get_n_attachments poppler_document_get_n_pages poppler_document_get_n_signatures poppler_document_get_page poppler_document_get_page_by_label poppler_document_get_page_layout poppler_document_get_page_mode poppler_document_get_pdf_conformance poppler_document_get_pdf_part poppler_document_get_pdf_subtype poppler_document_get_pdf_subtype_string poppler_document_get_pdf_version poppler_document_get_pdf_version_string poppler_document_get_permissions poppler_document_get_print_duplex poppler_document_get_print_n_copies poppler_document_get_print_page_ranges poppler_document_get_print_scaling poppler_document_get_producer poppler_document_get_signature_fields poppler_document_get_subject poppler_document_get_title poppler_document_has_attachments poppler_document_has_javascript poppler_document_is_linearized poppler_document_new_from_bytes poppler_document_new_from_data poppler_document_new_from_fd poppler_document_new_from_file poppler_document_new_from_gfile poppler_document_new_from_stream poppler_document_reset_form poppler_document_save poppler_document_save_a_copy poppler_document_save_to_fd poppler_document_set_author poppler_document_set_creation_date poppler_document_set_creation_date_time poppler_document_set_creator poppler_document_set_keywords poppler_document_set_modification_date poppler_document_set_modification_date_time poppler_document_set_producer poppler_document_set_subject poppler_document_set_title poppler_font_info_free poppler_font_info_new poppler_font_info_scan poppler_fonts_iter_copy poppler_fonts_iter_free poppler_fonts_iter_get_encoding poppler_fonts_iter_get_file_name poppler_fonts_iter_get_font_type poppler_fonts_iter_get_full_name poppler_fonts_iter_get_name poppler_fonts_iter_get_substitute_name poppler_fonts_iter_is_embedded poppler_fonts_iter_is_subset poppler_fonts_iter_next poppler_index_iter_copy poppler_index_iter_free poppler_index_iter_get_action poppler_index_iter_get_child poppler_index_iter_is_open poppler_index_iter_new poppler_index_iter_next poppler_layers_iter_copy poppler_layers_iter_free poppler_layers_iter_get_child poppler_layers_iter_get_layer poppler_layers_iter_get_title poppler_layers_iter_new poppler_layers_iter_next poppler_ps_file_free poppler_ps_file_new poppler_ps_file_new_fd poppler_ps_file_set_duplex poppler_ps_file_set_paper_size POPPLER_DOCUMENT POPPLER_FONT_INFO POPPLER_PS_FILE POPPLER_IS_DOCUMENT POPPLER_IS_FONT_INFO POPPLER_IS_PS_FILE POPPLER_TYPE_DOCUMENT POPPLER_TYPE_FONTS_ITER POPPLER_TYPE_FONT_INFO POPPLER_TYPE_FONT_TYPE POPPLER_TYPE_INDEX_ITER POPPLER_TYPE_LAYER POPPLER_TYPE_LAYERS_ITER POPPLER_TYPE_PAGE_LAYOUT POPPLER_TYPE_PAGE_MODE POPPLER_TYPE_PDF_CONFORMANCE POPPLER_TYPE_PDF_PART POPPLER_TYPE_PDF_SUBTYPE POPPLER_TYPE_PERMISSIONS POPPLER_TYPE_PRINT_DUPLEX POPPLER_TYPE_PRINT_SCALING POPPLER_TYPE_PS_FILE POPPLER_TYPE_VIEWER_PREFERENCES poppler_document_get_type poppler_font_info_get_type poppler_font_type_get_type poppler_fonts_iter_get_type poppler_index_iter_get_type poppler_layers_iter_get_type poppler_page_layout_get_type poppler_page_mode_get_type poppler_pdf_conformance_get_type poppler_pdf_part_get_type poppler_pdf_subtype_get_type poppler_permissions_get_type poppler_print_duplex_get_type poppler_print_scaling_get_type poppler_ps_file_get_type poppler_viewer_preferences_get_type
poppler-action PopplerAction PopplerAction PopplerDest PopplerActionAny PopplerActionGotoDest PopplerActionGotoRemote PopplerActionLaunch PopplerActionUri PopplerActionNamed PopplerActionMovie PopplerActionRendition PopplerActionResetForm PopplerActionOCGState PopplerActionJavascript PopplerActionType PopplerDestType PopplerActionMovieOperation PopplerActionLayer PopplerActionLayerAction poppler_action_copy poppler_action_free poppler_dest_copy poppler_dest_free POPPLER_ACTION POPPLER_TYPE_ACTION POPPLER_TYPE_ACTION_TYPE POPPLER_TYPE_DEST POPPLER_TYPE_DEST_TYPE POPPLER_TYPE_ACTION_LAYER_ACTION POPPLER_TYPE_ACTION_MOVIE_OPERATION poppler_action_get_type poppler_dest_get_type poppler_action_type_get_type poppler_dest_type_get_type poppler_action_layer_action_get_type poppler_action_movie_operation_get_type
poppler-attachment PopplerAttachment PopplerAttachment PopplerAttachmentSaveFunc poppler_attachment_get_checksum poppler_attachment_get_ctime poppler_attachment_get_description poppler_attachment_get_mtime poppler_attachment_get_name poppler_attachment_get_size poppler_attachment_save poppler_attachment_save_to_fd poppler_attachment_save_to_callback POPPLER_ATTACHMENT POPPLER_IS_ATTACHMENT POPPLER_TYPE_ATTACHMENT poppler_attachment_get_type
poppler-form-field PopplerFormField PopplerFormField PopplerAdditionalActionType PopplerCertificateInfo PopplerCertificateStatus PopplerFormFieldType PopplerFormButtonType PopplerFormChoiceType 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 poppler_form_field_choice_can_select_multiple poppler_form_field_choice_commit_on_change poppler_form_field_choice_do_spell_check poppler_form_field_choice_get_choice_type poppler_form_field_choice_get_item poppler_form_field_choice_get_n_items poppler_form_field_choice_get_text poppler_form_field_choice_is_editable poppler_form_field_choice_is_item_selected poppler_form_field_choice_select_item poppler_form_field_choice_set_text poppler_form_field_choice_toggle_item poppler_form_field_choice_unselect_all poppler_form_field_get_action poppler_form_field_get_additional_action poppler_form_field_get_alternate_ui_name poppler_form_field_get_field_type poppler_form_field_get_font_size poppler_form_field_get_id poppler_form_field_get_mapping_name poppler_form_field_get_name poppler_form_field_get_partial_name poppler_form_field_is_read_only poppler_form_field_signature_validate_async poppler_form_field_signature_validate_finish poppler_form_field_signature_validate_sync poppler_form_field_text_do_scroll poppler_form_field_text_do_spell_check poppler_form_field_text_get_max_len poppler_form_field_text_get_text poppler_form_field_text_get_text_type poppler_form_field_text_is_password 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 POPPLER_IS_FORM_FIELD POPPLER_TYPE_ADDITIONAL_ACTION_TYPE POPPLER_TYPE_CERTIFICATE_STATUS POPPLER_TYPE_FORM_BUTTON_TYPE POPPLER_TYPE_FORM_CHOICE_TYPE POPPLER_TYPE_FORM_FIELD POPPLER_TYPE_FORM_FIELD_TYPE POPPLER_TYPE_FORM_TEXT_TYPE POPPLER_TYPE_SIGNATURE_INFO POPPLER_TYPE_SIGNATURE_STATUS poppler_additional_action_type_get_type poppler_certificate_status_get_type poppler_form_button_type_get_type poppler_form_choice_type_get_type poppler_form_field_get_type poppler_form_field_type_get_type poppler_form_text_type_get_type poppler_signature_info_get_type poppler_signature_status_get_type poppler_signature_validation_flags_get_type
poppler-annot Poppler Annotation PopplerAnnotCalloutLine PopplerAnnot PopplerAnnotCircle PopplerAnnotFileAttachment PopplerAnnotFreeText PopplerAnnotLine PopplerAnnotMarkup PopplerAnnotMovie PopplerAnnotScreen PopplerAnnotSquare PopplerAnnotStamp PopplerAnnotText PopplerAnnotTextMarkup PopplerAnnotExternalDataType PopplerAnnotFlag PopplerAnnotFreeTextQuadding PopplerAnnotMarkupReplyType PopplerAnnotTextState PopplerAnnotStampIcon PopplerAnnotType poppler_annot_get_annot_type POPPLER_ANNOT_TEXT_ICON_CIRCLE POPPLER_ANNOT_TEXT_ICON_COMMENT POPPLER_ANNOT_TEXT_ICON_CROSS POPPLER_ANNOT_TEXT_ICON_HELP POPPLER_ANNOT_TEXT_ICON_INSERT POPPLER_ANNOT_TEXT_ICON_KEY POPPLER_ANNOT_TEXT_ICON_NEW_PARAGRAPH POPPLER_ANNOT_TEXT_ICON_NOTE POPPLER_ANNOT_TEXT_ICON_PARAGRAPH poppler_annot_callout_line_copy poppler_annot_callout_line_free poppler_annot_callout_line_new poppler_annot_circle_get_interior_color poppler_annot_circle_new 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_quadding poppler_annot_get_color poppler_annot_get_contents poppler_annot_get_flags poppler_annot_get_modified poppler_annot_get_name poppler_annot_get_page_index poppler_annot_get_rectangle poppler_annot_line_new poppler_annot_line_set_vertices poppler_annot_markup_get_date poppler_annot_markup_get_external_data poppler_annot_markup_get_label poppler_annot_markup_get_opacity poppler_annot_markup_get_popup_is_open poppler_annot_markup_get_popup_rectangle poppler_annot_markup_get_reply_to poppler_annot_markup_get_subject poppler_annot_markup_has_popup poppler_annot_markup_set_label poppler_annot_markup_set_opacity poppler_annot_markup_set_popup poppler_annot_markup_set_popup_is_open poppler_annot_markup_set_popup_rectangle poppler_annot_movie_get_movie poppler_annot_movie_get_title poppler_annot_screen_get_action poppler_annot_set_color poppler_annot_set_contents poppler_annot_set_flags poppler_annot_set_rectangle poppler_annot_square_get_interior_color poppler_annot_square_new poppler_annot_square_set_interior_color poppler_annot_stamp_get_icon poppler_annot_stamp_new poppler_annot_stamp_set_custom_image poppler_annot_stamp_set_icon poppler_annot_text_get_icon poppler_annot_text_get_is_open poppler_annot_text_get_state poppler_annot_text_markup_get_quadrilaterals poppler_annot_text_markup_new_highlight poppler_annot_text_markup_new_squiggly poppler_annot_text_markup_new_strikeout poppler_annot_text_markup_new_underline poppler_annot_text_markup_set_quadrilaterals poppler_annot_text_new poppler_annot_text_set_icon poppler_annot_text_set_is_open POPPLER_ANNOT POPPLER_ANNOT_CIRCLE POPPLER_ANNOT_FILE_ATTACHMENT POPPLER_ANNOT_FREE_TEXT POPPLER_ANNOT_LINE POPPLER_ANNOT_MARKUP POPPLER_ANNOT_MOVIE POPPLER_ANNOT_SCREEN POPPLER_ANNOT_SQUARE POPPLER_ANNOT_STAMP POPPLER_ANNOT_TEXT POPPLER_ANNOT_TEXT_MARKUP POPPLER_IS_ANNOT POPPLER_IS_ANNOT_CIRCLE POPPLER_IS_ANNOT_FILE_ATTACHMENT POPPLER_IS_ANNOT_FREE_TEXT POPPLER_IS_ANNOT_LINE POPPLER_IS_ANNOT_MARKUP POPPLER_IS_ANNOT_MOVIE POPPLER_IS_ANNOT_SCREEN POPPLER_IS_ANNOT_SQUARE POPPLER_IS_ANNOT_STAMP POPPLER_IS_ANNOT_TEXT POPPLER_IS_ANNOT_TEXT_MARKUP POPPLER_TYPE_ANNOT POPPLER_TYPE_ANNOT_CALLOUT_LINE POPPLER_TYPE_ANNOT_CIRCLE POPPLER_TYPE_ANNOT_EXTERNAL_DATA_TYPE POPPLER_TYPE_ANNOT_FILE_ATTACHMENT POPPLER_TYPE_ANNOT_FLAG POPPLER_TYPE_ANNOT_FREE_TEXT POPPLER_TYPE_ANNOT_FREE_TEXT_QUADDING POPPLER_TYPE_ANNOT_LINE POPPLER_TYPE_ANNOT_MARKUP POPPLER_TYPE_ANNOT_MARKUP_REPLY_TYPE POPPLER_TYPE_ANNOT_MOVIE POPPLER_TYPE_ANNOT_SCREEN POPPLER_TYPE_ANNOT_SQUARE POPPLER_TYPE_ANNOT_STAMP POPPLER_TYPE_ANNOT_TEXT POPPLER_TYPE_ANNOT_TEXT_MARKUP POPPLER_TYPE_ANNOT_TEXT_STATE POPPLER_TYPE_ANNOT_TYPE poppler_annot_callout_line_get_type poppler_annot_circle_get_type poppler_annot_external_data_type_get_type poppler_annot_file_attachment_get_type poppler_annot_flag_get_type poppler_annot_free_text_get_type poppler_annot_free_text_quadding_get_type poppler_annot_get_type poppler_annot_line_get_type poppler_annot_markup_get_type poppler_annot_markup_reply_type_get_type poppler_annot_movie_get_type poppler_annot_screen_get_type poppler_annot_square_get_type poppler_annot_stamp_get_type poppler_annot_text_get_type poppler_annot_text_markup_get_type poppler_annot_text_state_get_type poppler_annot_type_get_type
poppler-layer Poppler Layer PopplerLayer poppler_layer_get_radio_button_group_id poppler_layer_get_title poppler_layer_hide poppler_layer_is_parent poppler_layer_is_visible poppler_layer_show POPPLER_LAYER POPPLER_IS_LAYER POPPLER_TYPE_LAYER poppler_layer_get_type
poppler-media Poppler Media PopplerMedia PopplerMediaSaveFunc poppler_media_get_auto_play poppler_media_get_filename poppler_media_get_mime_type poppler_media_get_repeat_count poppler_media_get_show_controls poppler_media_is_embedded poppler_media_save poppler_media_save_to_fd poppler_media_save_to_callback POPPLER_MEDIA POPPLER_IS_MEDIA POPPLER_TYPE_MEDIA poppler_media_get_type
poppler-movie Poppler Movie PopplerMovie PopplerMoviePlayMode poppler_movie_get_aspect poppler_movie_get_duration poppler_movie_get_filename poppler_movie_get_play_mode poppler_movie_get_rate poppler_movie_get_rotation_angle poppler_movie_get_start poppler_movie_get_volume poppler_movie_is_synchronous poppler_movie_need_poster poppler_movie_show_controls POPPLER_MOVIE POPPLER_IS_MOVIE POPPLER_TYPE_MOVIE poppler_movie_get_type POPPLER_TYPE_MOVIE_PLAY_MODE poppler_movie_play_mode_get_type
poppler-structure-element PopplerStructureElement PopplerStructureBlockAlign PopplerStructureBorderStyle PopplerStructureElement PopplerStructureElementIter PopplerStructureElementKind PopplerStructureFormRole PopplerStructureFormState PopplerStructureGetTextFlags PopplerStructureGlyphOrientation PopplerStructureInlineAlign PopplerStructureListNumbering PopplerStructurePlacement PopplerStructureRubyAlign PopplerStructureRubyPosition PopplerStructureTableScope PopplerStructureTextAlign PopplerStructureTextDecoration PopplerStructureWritingMode poppler_structure_element_get_abbreviation poppler_structure_element_get_actual_text poppler_structure_element_get_alt_text poppler_structure_element_get_background_color poppler_structure_element_get_baseline_shift poppler_structure_element_get_block_align poppler_structure_element_get_border_color poppler_structure_element_get_border_style poppler_structure_element_get_border_thickness poppler_structure_element_get_bounding_box poppler_structure_element_get_color poppler_structure_element_get_column_count poppler_structure_element_get_column_gaps poppler_structure_element_get_column_widths poppler_structure_element_get_end_indent poppler_structure_element_get_form_description poppler_structure_element_get_form_role poppler_structure_element_get_form_state poppler_structure_element_get_glyph_orientation poppler_structure_element_get_height poppler_structure_element_get_id poppler_structure_element_get_inline_align poppler_structure_element_get_kind poppler_structure_element_get_language poppler_structure_element_get_line_height poppler_structure_element_get_list_numbering poppler_structure_element_get_padding poppler_structure_element_get_page poppler_structure_element_get_placement poppler_structure_element_get_ruby_align poppler_structure_element_get_ruby_position poppler_structure_element_get_space_after poppler_structure_element_get_space_before poppler_structure_element_get_start_indent poppler_structure_element_get_table_border_style poppler_structure_element_get_table_column_span poppler_structure_element_get_table_headers poppler_structure_element_get_table_padding poppler_structure_element_get_table_row_span poppler_structure_element_get_table_scope poppler_structure_element_get_table_summary poppler_structure_element_get_text poppler_structure_element_get_text_align poppler_structure_element_get_text_decoration_color poppler_structure_element_get_text_decoration_thickness poppler_structure_element_get_text_decoration_type poppler_structure_element_get_text_indent poppler_structure_element_get_text_spans poppler_structure_element_get_title poppler_structure_element_get_width poppler_structure_element_get_writing_mode poppler_structure_element_is_block poppler_structure_element_is_content poppler_structure_element_is_grouping poppler_structure_element_is_inline poppler_structure_element_iter_copy poppler_structure_element_iter_free poppler_structure_element_iter_get_child poppler_structure_element_iter_get_element poppler_structure_element_iter_new poppler_structure_element_iter_next POPPLER_STRUCTURE_ELEMENT POPPLER_IS_STRUCTURE_ELEMENT POPPLER_TYPE_STRUCTURE_BLOCK_ALIGN POPPLER_TYPE_STRUCTURE_BORDER_STYLE POPPLER_TYPE_STRUCTURE_ELEMENT POPPLER_TYPE_STRUCTURE_ELEMENT_ITER POPPLER_TYPE_STRUCTURE_ELEMENT_KIND POPPLER_TYPE_STRUCTURE_FORM_ROLE POPPLER_TYPE_STRUCTURE_FORM_STATE POPPLER_TYPE_STRUCTURE_GET_TEXT_FLAGS POPPLER_TYPE_STRUCTURE_GLYPH_ORIENTATION POPPLER_TYPE_STRUCTURE_INLINE_ALIGN POPPLER_TYPE_STRUCTURE_LIST_NUMBERING POPPLER_TYPE_STRUCTURE_PLACEMENT POPPLER_TYPE_STRUCTURE_REFERENCE POPPLER_TYPE_STRUCTURE_RUBY_ALIGN POPPLER_TYPE_STRUCTURE_RUBY_POSITION POPPLER_TYPE_STRUCTURE_TABLE_SCOPE POPPLER_TYPE_STRUCTURE_TEXT_ALIGN POPPLER_TYPE_STRUCTURE_TEXT_DECORATION POPPLER_TYPE_STRUCTURE_WRITING_MODE poppler_structure_block_align_get_type poppler_structure_border_style_get_type poppler_structure_element_get_type poppler_structure_element_iter_get_type poppler_structure_element_kind_get_type poppler_structure_form_role_get_type poppler_structure_form_state_get_type poppler_structure_get_text_flags_get_type poppler_structure_glyph_orientation_get_type poppler_structure_inline_align_get_type poppler_structure_list_numbering_get_type poppler_structure_placement_get_type poppler_structure_ruby_align_get_type poppler_structure_ruby_position_get_type poppler_structure_table_scope_get_type poppler_structure_text_align_get_type poppler_structure_text_decoration_get_type poppler_structure_writing_mode_get_type
poppler-text-span Poppler Text Span PopplerTextSpan poppler_text_span_copy poppler_text_span_free poppler_text_span_get_color poppler_text_span_get_font_name poppler_text_span_get_text poppler_text_span_is_bold_font poppler_text_span_is_fixed_width_font poppler_text_span_is_serif_font POPPLER_TYPE_TEXT_SPAN poppler_text_span_get_type
poppler-pdf-utility-functions PDF Utility functions poppler_date_parse poppler_named_dest_from_bytestring poppler_named_dest_to_bytestring
poppler-features Poppler Features POPPLER_HAS_CAIRO POPPLER_CHECK_VERSION POPPLER_MAJOR_VERSION POPPLER_MICRO_VERSION POPPLER_MINOR_VERSION PopplerBackend poppler_get_backend poppler_get_version POPPLER_TYPE_BACKEND poppler_backend_get_type POPPLER_PUBLIC
poppler-errors POPPLER_ERROR PopplerError POPPLER_TYPE_ERROR poppler_error_get_type poppler_error_quark
poppler-24.02.0/glib/reference/poppler.types000066400000000000000000000061171455701731300207750ustar00rootroot00000000000000poppler_action_get_type poppler_action_layer_action_get_type poppler_action_movie_operation_get_type poppler_action_type_get_type poppler_additional_action_type_get_type poppler_annot_callout_line_get_type poppler_annot_circle_get_type poppler_annot_external_data_type_get_type poppler_annot_file_attachment_get_type poppler_annot_flag_get_type poppler_annot_free_text_get_type poppler_annot_free_text_quadding_get_type poppler_annot_get_type poppler_annot_line_get_type poppler_annot_mapping_get_type poppler_annot_markup_get_type poppler_annot_markup_reply_type_get_type poppler_annot_movie_get_type poppler_annot_screen_get_type poppler_annot_square_get_type poppler_annot_stamp_get_type poppler_annot_text_get_type poppler_annot_text_markup_get_type 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 poppler_dest_type_get_type poppler_document_get_type poppler_error_get_type poppler_find_flags_get_type poppler_font_info_get_type poppler_font_type_get_type poppler_fonts_iter_get_type poppler_form_button_type_get_type poppler_form_choice_type_get_type poppler_form_field_get_type poppler_form_field_mapping_get_type poppler_form_field_type_get_type poppler_form_text_type_get_type poppler_image_mapping_get_type poppler_index_iter_get_type poppler_layer_get_type poppler_layers_iter_get_type poppler_link_mapping_get_type poppler_media_get_type poppler_movie_get_type poppler_movie_play_mode_get_type poppler_page_get_type poppler_page_layout_get_type poppler_page_mode_get_type poppler_page_transition_alignment_get_type poppler_page_transition_direction_get_type poppler_page_transition_get_type poppler_page_transition_type_get_type poppler_pdf_conformance_get_type poppler_pdf_part_get_type poppler_pdf_subtype_get_type poppler_permissions_get_type poppler_point_get_type poppler_print_duplex_get_type poppler_print_flags_get_type poppler_print_scaling_get_type poppler_ps_file_get_type poppler_quadrilateral_get_type poppler_rectangle_get_type poppler_selection_style_get_type poppler_signature_info_get_type poppler_signature_status_get_type poppler_signature_validation_flags_get_type poppler_structure_block_align_get_type poppler_structure_border_style_get_type poppler_structure_element_get_type poppler_structure_element_iter_get_type poppler_structure_element_kind_get_type poppler_structure_form_role_get_type poppler_structure_form_state_get_type poppler_structure_get_text_flags_get_type poppler_structure_glyph_orientation_get_type poppler_structure_inline_align_get_type poppler_structure_list_numbering_get_type poppler_structure_placement_get_type poppler_structure_ruby_align_get_type poppler_structure_ruby_position_get_type poppler_structure_table_scope_get_type poppler_structure_text_align_get_type poppler_structure_text_decoration_get_type 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_typepoppler-24.02.0/glib/tests/000077500000000000000000000000001455701731300154255ustar00rootroot00000000000000poppler-24.02.0/glib/tests/CMakeLists.txt000066400000000000000000000062731455701731300201750ustar00rootroot00000000000000macro(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 check_text.c ) poppler_add_test(poppler-check-text BUILD_GTK_TESTS ${poppler_check_text_SRCS}) add_test(poppler-check-text ${EXECUTABLE_OUTPUT_PATH}/poppler-check-text) set(poppler_check_bb_SRCS check_bb.c ) 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) add_executable(pdfdrawbb pdfdrawbb.c) target_link_libraries(pdfdrawbb poppler-glib) macro(GLIB_ADD_FUZZER exe) string(REPLACE "-" "" test_name ${exe}) set(${test_name}_SOURCES ${ARGN} ) poppler_add_test(${exe} BUILD_GTK_TESTS ${${test_name}_SOURCES}) target_link_libraries(${exe} poppler-glib PkgConfig::GTK3) endmacro(GLIB_ADD_FUZZER) if(ENABLE_FUZZER) glib_add_fuzzer(annot_fuzzer ./fuzzing/annot_fuzzer.cc) glib_add_fuzzer(doc_attr_fuzzer ./fuzzing/doc_attr_fuzzer.cc) glib_add_fuzzer(find_text_fuzzer ./fuzzing/find_text_fuzzer.cc) glib_add_fuzzer(util_fuzzer ./fuzzing/util_fuzzer.cc) glib_add_fuzzer(label_fuzzer ./fuzzing/label_fuzzer.cc) glib_add_fuzzer(pdf_draw_fuzzer ./fuzzing/pdf_draw_fuzzer.cc) endif() poppler-24.02.0/glib/tests/check_bb.c000066400000000000000000000045361455701731300173210ustar00rootroot00000000000000/* * testing program for the boundingbox function */ #include #include #include #include #include /* * compare floating-point coordinates */ int equal(double a, double b, double precision) { return fabs(a - b) < precision; } /* * main */ int main(int argc, char *argv[]) { GFile *infile; PopplerDocument *doc; PopplerPage *page; int npages, n; gboolean hg; PopplerRectangle bb, correct; GError *err = NULL; int argx; double precision = 0.01; /* open file */ g_print("file: %s\n", argv[1]); infile = g_file_new_for_path(argv[1]); if (!infile) { exit(EXIT_FAILURE); } doc = poppler_document_new_from_gfile(infile, NULL, NULL, &err); if (doc == NULL) { g_printerr("error opening pdf file: %s\n", err->message); g_error_free(err); exit(EXIT_FAILURE); } /* precision */ argx = 2; if (!strcmp(argv[argx], "-p")) { precision = atof(argv[argx + 1]); argx += 2; } /* pages */ npages = poppler_document_get_n_pages(doc); if (npages < 1) { g_printerr("no page in document\n"); exit(EXIT_FAILURE); } /* check the bounding box */ for (n = 0; n < poppler_document_get_n_pages(doc); n++) { g_print(" page: %d\n", n + 1); page = poppler_document_get_page(doc, n); hg = poppler_page_get_bounding_box(page, &bb); if (!hg) { g_printerr("no graphics in page\n"); exit(EXIT_FAILURE); } g_print(" bounding box: %g,%g - %g,%g\n", bb.x1, bb.y1, bb.x2, bb.y2); if (argc - argx < 4) { g_print("not enough arguments\n"); exit(EXIT_FAILURE); } correct.x1 = atof(argv[argx++]); correct.y1 = atof(argv[argx++]); correct.x2 = atof(argv[argx++]); correct.y2 = atof(argv[argx++]); g_print(" correct: %g,%g - %g,%g\n", correct.x1, correct.y1, correct.x2, correct.y2); if (!equal(bb.x1, correct.x1, precision) || !equal(bb.x2, correct.x2, precision) || !equal(bb.y1, correct.y1, precision) || !equal(bb.y2, correct.y2, precision)) { g_print("bounding box differs from expected\n"); exit(EXIT_FAILURE); } g_object_unref(page); } return EXIT_SUCCESS; } poppler-24.02.0/glib/tests/check_text.c000066400000000000000000000051641455701731300177200ustar00rootroot00000000000000/* * testing program for the get_text function */ #include #include #include #include /* * main */ int main(int argc, char *argv[]) { GFile *infile; PopplerDocument *doc; PopplerPage *page; PopplerRectangle *areas = NULL; guint n_glyph_areas, n_utf8_chars; int npages, n; char *text; GError *err = NULL; /* open file */ infile = g_file_new_for_path(TESTDATADIR "/unittestcases/WithActualText.pdf"); if (!infile) { exit(EXIT_FAILURE); } doc = poppler_document_new_from_gfile(infile, NULL, NULL, &err); if (doc == NULL) { g_printerr("error opening pdf file: %s\n", err->message); g_error_free(err); exit(EXIT_FAILURE); } /* pages */ npages = poppler_document_get_n_pages(doc); if (npages < 1) { g_printerr("no page in document\n"); exit(EXIT_FAILURE); } /* check text */ n = 0; page = poppler_document_get_page(doc, n); text = poppler_page_get_text(page); g_print("%s\n", text); g_assert_cmpstr(text, ==, "The slow brown fox jumps over the black dog."); /* Cleanup vars for next test */ g_clear_object(&page); g_clear_object(&doc); g_clear_object(&infile); g_clear_pointer(&text, g_free); /* Test for consistency between utf8 characters returned by poppler_page_get_text() * and glyph layout areas returned by poppler_page_get_text_layout(). Issue #1100 */ g_print("Consistency test between poppler_page_get_text() and poppler_page_get_text_layout()\n"); g_print("Issue #1100 \n"); infile = g_file_new_for_path(TESTDATADIR "/unittestcases/searchAcrossLines.pdf"); if (!infile) { exit(EXIT_FAILURE); } doc = poppler_document_new_from_gfile(infile, NULL, NULL, &err); if (doc == NULL) { g_printerr("error opening pdf file: %s\n", err->message); g_error_free(err); exit(EXIT_FAILURE); } page = poppler_document_get_page(doc, 0); if (page == NULL || !POPPLER_IS_PAGE(page)) { g_print("error opening pdf page\n"); exit(EXIT_FAILURE); } text = poppler_page_get_text(page); n_utf8_chars = (guint)g_utf8_strlen(text, -1); poppler_page_get_text_layout(page, &areas, &n_glyph_areas); g_assert_cmpuint(n_glyph_areas, ==, n_utf8_chars); g_print("Test: OK ('layout glyph areas' match amount of 'utf8 characters')\n"); /* Cleanup vars for next test */ g_clear_object(&page); g_clear_object(&doc); g_clear_object(&infile); g_clear_pointer(&areas, g_free); g_clear_pointer(&text, g_free); return EXIT_SUCCESS; } poppler-24.02.0/glib/tests/fuzzing/000077500000000000000000000000001455701731300171215ustar00rootroot00000000000000poppler-24.02.0/glib/tests/fuzzing/annot_fuzzer.cc000066400000000000000000000040041455701731300221520ustar00rootroot00000000000000#include #include #include #include #include "fuzzer_temp_file.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { GError *err = NULL; PopplerDocument *doc; PopplerPage *page; PopplerAnnot *annot; PopplerRectangle bb; gdouble width, height; gboolean hg; int npages; cairo_t *cr; cairo_surface_t *surface; cairo_status_t status; doc = poppler_document_new_from_data((char *)data, size, NULL, &err); if (doc == NULL) { g_error_free(err); return 0; } npages = poppler_document_get_n_pages(doc); if (npages < 1) { g_object_unref(doc); return 0; } char *tmpfile = fuzzer_get_tmpfile(data, size); surface = cairo_pdf_surface_create(tmpfile, 1.0, 1.0); status = cairo_surface_status(surface); if (status != CAIRO_STATUS_SUCCESS) { g_object_unref(doc); fuzzer_release_tmpfile(tmpfile); return 0; } for (int n = 0; n < npages; n++) { page = poppler_document_get_page(doc, n); if (!page) { continue; } poppler_page_get_size(page, &width, &height); cairo_pdf_surface_set_size(surface, width, height); hg = poppler_page_get_bounding_box(page, &bb); if (hg) { annot = poppler_annot_text_new(doc, &bb); if (annot != NULL) { g_object_unref(page); continue; } poppler_page_add_annot(page, annot); } cr = cairo_create(surface); status = cairo_status(cr); if (status != CAIRO_STATUS_SUCCESS) { cairo_destroy(cr); g_object_unref(page); continue; } poppler_page_render_for_printing(page, cr); cairo_surface_show_page(surface); cairo_destroy(cr); g_object_unref(page); } cairo_surface_destroy(surface); fuzzer_release_tmpfile(tmpfile); g_object_unref(doc); return 0; } poppler-24.02.0/glib/tests/fuzzing/doc_attr_fuzzer.cc000066400000000000000000000014521455701731300226360ustar00rootroot00000000000000#include #include #include #include extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { GError *err = NULL; PopplerDocument *doc; PopplerPage *page; char *buf; int npages, n; doc = poppler_document_new_from_data((char *)data, size, NULL, &err); if (doc == NULL) { g_error_free(err); return 0; } buf = (char *)calloc(size + 1, sizeof(char)); memcpy(buf, data, size); buf[size] = '\0'; poppler_document_set_author(doc, buf); poppler_document_set_creator(doc, buf); poppler_document_set_keywords(doc, buf); poppler_document_set_producer(doc, buf); poppler_document_set_subject(doc, buf); poppler_document_set_title(doc, buf); free(buf); return 0; } poppler-24.02.0/glib/tests/fuzzing/find_text_fuzzer.cc000066400000000000000000000015331455701731300230230ustar00rootroot00000000000000#include #include #include #include extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { GError *err = NULL; PopplerDocument *doc; PopplerPage *page; char *buf; int npages; doc = poppler_document_new_from_data((char *)data, size, NULL, &err); if (doc == NULL) { g_error_free(err); return 0; } npages = poppler_document_get_n_pages(doc); if (npages < 1) { return 0; } buf = (char *)calloc(size + 1, sizeof(char)); memcpy(buf, data, size); buf[size] = '\0'; for (int n = 0; n < npages; n++) { page = poppler_document_get_page(doc, n); if (!page) { continue; } poppler_page_find_text(page, buf); g_object_unref(page); } free(buf); return 0; } poppler-24.02.0/glib/tests/fuzzing/fuzzer_temp_file.h000066400000000000000000000045621455701731300226520ustar00rootroot00000000000000// Copyright 2018 Google Inc. // // 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. // Adapter utility from fuzzer input to a temporary file, for fuzzing APIs that // require a file instead of an input buffer. #ifndef FUZZER_TEMP_FILE_H_ #define FUZZER_TEMP_FILE_H_ #include #include #include #include #include // Pure-C interface for creating and cleaning up temporary files. static char *fuzzer_get_tmpfile(const uint8_t *data, size_t size) { char *filename_buffer = strdup("/tmp/generate_temporary_file.XXXXXX"); if (!filename_buffer) { perror("Failed to allocate file name buffer."); abort(); } const int file_descriptor = mkstemp(filename_buffer); if (file_descriptor < 0) { perror("Failed to make temporary file."); abort(); } FILE *file = fdopen(file_descriptor, "wb"); if (!file) { perror("Failed to open file descriptor."); close(file_descriptor); abort(); } const size_t bytes_written = fwrite(data, sizeof(uint8_t), size, file); if (bytes_written < size) { close(file_descriptor); fprintf(stderr, "Failed to write all bytes to file (%zu out of %zu)", bytes_written, size); abort(); } fclose(file); return filename_buffer; } static void fuzzer_release_tmpfile(char *filename) { if (unlink(filename) != 0) { perror("WARNING: Failed to delete temporary file."); } free(filename); } // C++ RAII object for creating temporary files. #ifdef __cplusplus class FuzzerTemporaryFile { public: FuzzerTemporaryFile(const uint8_t *data, size_t size) : filename_(fuzzer_get_tmpfile(data, size)) { } ~FuzzerTemporaryFile() { fuzzer_release_tmpfile(filename_); } const char *filename() const { return filename_; } private: char *filename_; }; #endif #endif // FUZZER_TEMP_FILE_H_ poppler-24.02.0/glib/tests/fuzzing/label_fuzzer.cc000066400000000000000000000014731455701731300221210ustar00rootroot00000000000000#include #include #include #include extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { GError *err = NULL; PopplerDocument *doc; PopplerPage *page; char *buf; int npages; doc = poppler_document_new_from_data((char *)data, size, NULL, &err); if (doc == NULL) { g_error_free(err); return 0; } npages = poppler_document_get_n_pages(doc); if (npages < 1) { return 0; } buf = (char *)calloc(size + 1, sizeof(char)); memcpy(buf, data, size); buf[size] = '\0'; for (int n = 0; n < npages; n++) { page = poppler_document_get_page_by_label(doc, buf); if (!page) { continue; } g_object_unref(page); } free(buf); return 0; } poppler-24.02.0/glib/tests/fuzzing/pdf_draw_fuzzer.cc000066400000000000000000000036301455701731300226250ustar00rootroot00000000000000#include #include #include #include #include "fuzzer_temp_file.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { GError *err = NULL; PopplerDocument *doc; PopplerPage *page; PopplerRectangle bb; gdouble width, height; gboolean hg; int npages; cairo_t *cr; cairo_surface_t *surface; cairo_status_t status; doc = poppler_document_new_from_data((char *)data, size, NULL, &err); if (doc == NULL) { g_error_free(err); return 0; } npages = poppler_document_get_n_pages(doc); if (npages < 1) { g_object_unref(doc); return 0; } char *tmpfile = fuzzer_get_tmpfile(data, size); surface = cairo_pdf_surface_create(tmpfile, 1.0, 1.0); status = cairo_surface_status(surface); if (status != CAIRO_STATUS_SUCCESS) { g_object_unref(doc); fuzzer_release_tmpfile(tmpfile); return 0; } for (int n = 0; n < npages; n++) { page = poppler_document_get_page(doc, n); if (!page) { continue; } poppler_page_get_size(page, &width, &height); cairo_pdf_surface_set_size(surface, width, height); hg = poppler_page_get_bounding_box(page, &bb); cr = cairo_create(surface); status = cairo_status(cr); if (status != CAIRO_STATUS_SUCCESS) { g_object_unref(page); continue; } if (hg) { cairo_set_source_rgb(cr, 0.6, 0.6, 1.0); cairo_rectangle(cr, bb.x1, bb.y1, bb.x2 - bb.x1, bb.y2 - bb.y1); cairo_stroke(cr); } poppler_page_render_for_printing(page, cr); cairo_surface_show_page(surface); cairo_destroy(cr); g_object_unref(page); } cairo_surface_destroy(surface); g_object_unref(doc); fuzzer_release_tmpfile(tmpfile); return 0; } poppler-24.02.0/glib/tests/fuzzing/util_fuzzer.cc000066400000000000000000000010411455701731300220060ustar00rootroot00000000000000#include #include #include #include extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { char *tmp_ch; char *buf; gsize length; guint8 *tmp_uint; buf = (char *)calloc(size + 1, sizeof(char)); memcpy(buf, data, size); buf[size] = '\0'; tmp_ch = poppler_named_dest_from_bytestring((const guint8 *)buf, size); tmp_uint = poppler_named_dest_to_bytestring(buf, &length); g_free(tmp_ch); g_free(tmp_uint); free(buf); return 0; } poppler-24.02.0/glib/tests/pdfdrawbb.c000066400000000000000000000063271455701731300175340ustar00rootroot00000000000000/* * pdfdrawbb.c * * draw the bounding box of each page */ #include #include #include #include #include #include #include /* * add suffix to a pdf filename */ char *pdfaddsuffix(char *infile, char *suffix) { char *basename; char *outfile; char *pos; basename = g_path_get_basename(infile); outfile = malloc(strlen(infile) + strlen(suffix) + 10); strcpy(outfile, basename); g_free(basename); pos = strrchr(outfile, '.'); if (pos != NULL && (!strcmp(pos, ".pdf") || !strcmp(pos, ".PDF"))) { *pos = '\0'; } strcat(outfile, "-"); strcat(outfile, suffix); strcat(outfile, ".pdf"); return outfile; } /* * main */ int main(int argc, char *argv[]) { int opt; gboolean usage = FALSE; char *infilename, *outfilename; GError *err = NULL; GFile *infile; PopplerDocument *doc; PopplerPage *page; int npages, n; PopplerRectangle bb; gboolean hg; gdouble width, height; cairo_surface_t *surface; cairo_t *cr; /* arguments */ while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': usage = TRUE; break; } } if (!usage && argc - 1 < optind) { g_print("input file name missing\n"); usage = TRUE; } if (usage) { g_print("usage:\n"); g_print("\tpdfdrawbb"); g_print("[-h] file.pdf\n"); g_print("\t\t-h\t\tthis help\n"); exit(EXIT_FAILURE); } infilename = argv[optind]; if (!infilename) { exit(EXIT_FAILURE); } outfilename = pdfaddsuffix(argv[optind], "bb"); /* open file */ infile = g_file_new_for_path(infilename); if (infile == NULL) { exit(EXIT_FAILURE); } doc = poppler_document_new_from_gfile(infile, NULL, NULL, &err); if (doc == NULL) { g_printerr("error opening pdf file: %s\n", err->message); g_error_free(err); exit(EXIT_FAILURE); } /* pages */ npages = poppler_document_get_n_pages(doc); if (npages < 1) { g_print("no page in document\n"); exit(EXIT_FAILURE); } /* copy to destination */ surface = cairo_pdf_surface_create(outfilename, 1.0, 1.0); g_print("infile: %s\n", infilename); g_print("outfile: %s\n", outfilename); for (n = 0; n < npages; n++) { g_print("page %d:\n", n); page = poppler_document_get_page(doc, n); poppler_page_get_size(page, &width, &height); cairo_pdf_surface_set_size(surface, width, height); hg = poppler_page_get_bounding_box(page, &bb); if (hg) { g_print("bounding box %g,%g - %g,%g", bb.x1, bb.y1, bb.x2, bb.y2); } g_print("\n"); cr = cairo_create(surface); poppler_page_render_for_printing(page, cr); if (hg) { cairo_set_source_rgb(cr, 0.6, 0.6, 1.0); cairo_rectangle(cr, bb.x1, bb.y1, bb.x2 - bb.x1, bb.y2 - bb.y1); cairo_stroke(cr); } cairo_destroy(cr); cairo_surface_show_page(surface); g_object_unref(page); } cairo_surface_destroy(surface); return EXIT_SUCCESS; } poppler-24.02.0/goo/000077500000000000000000000000001455701731300141325ustar00rootroot00000000000000poppler-24.02.0/goo/.gitignore000066400000000000000000000000741455701731300161230ustar00rootroot00000000000000.cvsignore .deps .libs Makefile Makefile.in *.la *.lo *.loT poppler-24.02.0/goo/GooCheckedOps.h000066400000000000000000000067301455701731300167660ustar00rootroot00000000000000//======================================================================== // // GooCheckedOps.h // // This file is licensed under the GPLv2 or later // // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019 LE GARREC Vincent // Copyright (C) 2019-2021 Albert Astals Cid // //======================================================================== #ifndef GOO_CHECKED_OPS_H #define GOO_CHECKED_OPS_H #include #include template inline bool checkedAssign(long long lz, T *z) { static_assert((std::numeric_limits::max)() > (std::numeric_limits::max)(), "The max of long long type must be larger to perform overflow checks."); static_assert((std::numeric_limits::min)() < (std::numeric_limits::min)(), "The min of long long type must be smaller to perform overflow checks."); if (lz > (std::numeric_limits::max)() || lz < (std::numeric_limits::min)()) { return true; } *z = static_cast(lz); return false; } #ifndef __has_builtin # define __has_builtin(x) 0 #endif template inline bool checkedAdd(T x, T y, T *z) { // The __GNUC__ checks can not be removed until we depend on GCC >= 10.1 // which is the first version that returns true for __has_builtin(__builtin_add_overflow) #if __GNUC__ >= 5 || __has_builtin(__builtin_add_overflow) return __builtin_add_overflow(x, y, z); #else const auto lz = static_cast(x) + static_cast(y); return checkedAssign(lz, z); #endif } template<> inline bool checkedAdd(long long x, long long y, long long *z) { #if __GNUC__ >= 5 || __has_builtin(__builtin_add_overflow) return __builtin_add_overflow(x, y, z); #else if (x > 0 && y > 0) { if (x > (std::numeric_limits::max)() - y) { return true; } } else if (x < 0 && y < 0) { if (x < (std::numeric_limits::min)() - y) { return true; } } *z = x + y; return false; #endif } template inline bool checkedSubtraction(T x, T y, T *z) { #if __GNUC__ >= 5 || __has_builtin(__builtin_sub_overflow) return __builtin_sub_overflow(x, y, z); #else const auto lz = static_cast(x) - static_cast(y); return checkedAssign(lz, z); #endif } template inline bool checkedMultiply(T x, T y, T *z) { #if __GNUC__ >= 5 || __has_builtin(__builtin_mul_overflow) return __builtin_mul_overflow(x, y, z); #else const auto lz = static_cast(x) * static_cast(y); return checkedAssign(lz, z); #endif } template<> inline bool checkedMultiply(long long x, long long y, long long *z) { #if __GNUC__ >= 5 || __has_builtin(__builtin_mul_overflow) return __builtin_mul_overflow(x, y, z); #else if (x != 0 && (std::numeric_limits::max)() / x < y) { return true; } *z = x * y; return false; #endif } template inline T safeAverage(T a, T b) { static_assert((std::numeric_limits::max)() > (std::numeric_limits::max)(), "The max of long long type must be larger to perform overflow checks."); static_assert((std::numeric_limits::min)() < (std::numeric_limits::min)(), "The min of long long type must be smaller to perform overflow checks."); return static_cast((static_cast(a) + static_cast(b)) / 2); } #endif // GOO_CHECKED_OPS_H poppler-24.02.0/goo/GooLikely.h000066400000000000000000000010541455701731300162010ustar00rootroot00000000000000//======================================================================== // // GooLikely.h // // This file is licensed under the GPLv2 or later // // Copyright (C) 2008 Kees Cook // //======================================================================== #ifndef GOOLIKELY_H #define GOOLIKELY_H #if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) # define likely(x) __builtin_expect((x), 1) # define unlikely(x) __builtin_expect((x), 0) #else # define likely(x) (x) # define unlikely(x) (x) #endif #endif poppler-24.02.0/goo/GooString.cc000066400000000000000000000511211455701731300163540ustar00rootroot00000000000000//======================================================================== // // GooString.cc // // Simple variable-length string type. // // 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) 2006 Krzysztof Kowalczyk // Copyright (C) 2007 Jeff Muizelaar // Copyright (C) 2008-2011, 2016-2018, 2022 Albert Astals Cid // Copyright (C) 2011 Kenji Uno // Copyright (C) 2012, 2013 Fabio D'Urso // Copyright (C) 2012, 2017 Adrian Johnson // Copyright (C) 2012 Pino Toscano // Copyright (C) 2013 Jason Crain // Copyright (C) 2015 William Bader // 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 Adam Reichold // Copyright (C) 2018 Greg Knight // Copyright (C) 2019, 2022, 2023 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 // //======================================================================== #include #include #include #include #include #include #include #include #include #include "gmem.h" #include "Error.h" #include "GooString.h" //------------------------------------------------------------------------ namespace { union GooStringFormatArg { int i; unsigned int ui; long l; unsigned long ul; long long ll; unsigned long long ull; double f; char c; char *s; GooString *gs; }; enum GooStringFormatType { fmtIntDecimal, fmtIntHex, fmtIntHexUpper, fmtIntOctal, fmtIntBinary, fmtUIntDecimal, fmtUIntHex, fmtUIntHexUpper, fmtUIntOctal, fmtUIntBinary, fmtLongDecimal, fmtLongHex, fmtLongHexUpper, fmtLongOctal, fmtLongBinary, fmtULongDecimal, fmtULongHex, fmtULongHexUpper, fmtULongOctal, fmtULongBinary, fmtLongLongDecimal, fmtLongLongHex, fmtLongLongHexUpper, fmtLongLongOctal, fmtLongLongBinary, fmtULongLongDecimal, fmtULongLongHex, fmtULongLongHexUpper, fmtULongLongOctal, fmtULongLongBinary, fmtDouble, fmtDoubleTrimSmallAware, fmtDoubleTrim, fmtChar, fmtString, fmtGooString, fmtSpace }; const char *const formatStrings[] = { "d", "x", "X", "o", "b", "ud", "ux", "uX", "uo", "ub", "ld", "lx", "lX", "lo", "lb", "uld", "ulx", "ulX", "ulo", "ulb", "lld", "llx", "llX", "llo", "llb", "ulld", "ullx", "ullX", "ullo", "ullb", "f", "gs", "g", "c", "s", "t", "w", nullptr }; void formatInt(long long x, char *buf, int bufSize, bool zeroFill, int width, int base, const char **p, int *len, bool upperCase = false); void formatUInt(unsigned long long x, char *buf, int bufSize, bool zeroFill, int width, int base, const char **p, int *len, bool upperCase = false); void formatDouble(double x, char *buf, int bufSize, int prec, bool trim, const char **p, int *len); void formatDoubleSmallAware(double x, char *buf, int bufSize, int prec, bool trim, const char **p, int *len); } //------------------------------------------------------------------------ std::unique_ptr GooString::format(const char *fmt, ...) { auto s = std::make_unique(); va_list argList; va_start(argList, fmt); s->appendfv(fmt, argList); va_end(argList); return s; } std::unique_ptr GooString::formatv(const char *fmt, va_list argList) { auto s = std::make_unique(); s->appendfv(fmt, argList); return s; } GooString *GooString::appendf(const char *fmt, ...) { va_list argList; va_start(argList, fmt); appendfv(fmt, argList); va_end(argList); return this; } GooString *GooString::appendfv(const char *fmt, va_list argList) { GooStringFormatArg *args; int argsLen, argsSize; GooStringFormatArg arg; int idx, width, prec; bool reverseAlign, zeroFill; GooStringFormatType ft; char buf[65]; int len, i; const char *p0, *p1; const char *str; GooStringFormatArg argsBuf[8]; argsLen = 0; argsSize = sizeof(argsBuf) / sizeof(argsBuf[0]); args = argsBuf; p0 = fmt; while (*p0) { if (*p0 == '{') { ++p0; if (*p0 == '{') { ++p0; append('{'); } else { // parse the format string if (!(*p0 >= '0' && *p0 <= '9')) { break; } idx = *p0 - '0'; for (++p0; *p0 >= '0' && *p0 <= '9'; ++p0) { idx = 10 * idx + (*p0 - '0'); } if (*p0 != ':') { break; } ++p0; if (*p0 == '-') { reverseAlign = true; ++p0; } else { reverseAlign = false; } width = 0; zeroFill = *p0 == '0'; for (; *p0 >= '0' && *p0 <= '9'; ++p0) { width = 10 * width + (*p0 - '0'); } if (width < 0) { width = 0; } if (*p0 == '.') { ++p0; prec = 0; for (; *p0 >= '0' && *p0 <= '9'; ++p0) { prec = 10 * prec + (*p0 - '0'); } } else { prec = 0; } for (ft = (GooStringFormatType)0; formatStrings[ft]; ft = (GooStringFormatType)(ft + 1)) { if (!strncmp(p0, formatStrings[ft], strlen(formatStrings[ft]))) { break; } } if (!formatStrings[ft]) { break; } p0 += strlen(formatStrings[ft]); if (*p0 != '}') { break; } ++p0; // fetch the argument if (idx > argsLen) { break; } if (idx == argsLen) { if (argsLen == argsSize) { argsSize *= 2; if (args == argsBuf) { args = (GooStringFormatArg *)gmallocn(argsSize, sizeof(GooStringFormatArg)); memcpy(args, argsBuf, argsLen * sizeof(GooStringFormatArg)); } else { args = (GooStringFormatArg *)greallocn(args, argsSize, sizeof(GooStringFormatArg)); } } switch (ft) { case fmtIntDecimal: case fmtIntHex: case fmtIntHexUpper: case fmtIntOctal: case fmtIntBinary: case fmtSpace: args[argsLen].i = va_arg(argList, int); break; case fmtUIntDecimal: case fmtUIntHex: case fmtUIntHexUpper: case fmtUIntOctal: case fmtUIntBinary: args[argsLen].ui = va_arg(argList, unsigned int); break; case fmtLongDecimal: case fmtLongHex: case fmtLongHexUpper: case fmtLongOctal: case fmtLongBinary: args[argsLen].l = va_arg(argList, long); break; case fmtULongDecimal: case fmtULongHex: case fmtULongHexUpper: case fmtULongOctal: case fmtULongBinary: args[argsLen].ul = va_arg(argList, unsigned long); break; case fmtLongLongDecimal: case fmtLongLongHex: case fmtLongLongHexUpper: case fmtLongLongOctal: case fmtLongLongBinary: args[argsLen].ll = va_arg(argList, long long); break; case fmtULongLongDecimal: case fmtULongLongHex: case fmtULongLongHexUpper: case fmtULongLongOctal: case fmtULongLongBinary: args[argsLen].ull = va_arg(argList, unsigned long long); break; case fmtDouble: case fmtDoubleTrim: case fmtDoubleTrimSmallAware: args[argsLen].f = va_arg(argList, double); break; case fmtChar: args[argsLen].c = (char)va_arg(argList, int); break; case fmtString: args[argsLen].s = va_arg(argList, char *); break; case fmtGooString: args[argsLen].gs = va_arg(argList, GooString *); break; } ++argsLen; } // format the argument arg = args[idx]; switch (ft) { case fmtIntDecimal: formatInt(arg.i, buf, sizeof(buf), zeroFill, width, 10, &str, &len); break; case fmtIntHex: formatInt(arg.i, buf, sizeof(buf), zeroFill, width, 16, &str, &len); break; case fmtIntHexUpper: formatInt(arg.i, buf, sizeof(buf), zeroFill, width, 16, &str, &len, true); break; case fmtIntOctal: formatInt(arg.i, buf, sizeof(buf), zeroFill, width, 8, &str, &len); break; case fmtIntBinary: formatInt(arg.i, buf, sizeof(buf), zeroFill, width, 2, &str, &len); break; case fmtUIntDecimal: formatUInt(arg.ui, buf, sizeof(buf), zeroFill, width, 10, &str, &len); break; case fmtUIntHex: formatUInt(arg.ui, buf, sizeof(buf), zeroFill, width, 16, &str, &len); break; case fmtUIntHexUpper: formatUInt(arg.ui, buf, sizeof(buf), zeroFill, width, 16, &str, &len, true); break; case fmtUIntOctal: formatUInt(arg.ui, buf, sizeof(buf), zeroFill, width, 8, &str, &len); break; case fmtUIntBinary: formatUInt(arg.ui, buf, sizeof(buf), zeroFill, width, 2, &str, &len); break; case fmtLongDecimal: formatInt(arg.l, buf, sizeof(buf), zeroFill, width, 10, &str, &len); break; case fmtLongHex: formatInt(arg.l, buf, sizeof(buf), zeroFill, width, 16, &str, &len); break; case fmtLongHexUpper: formatInt(arg.l, buf, sizeof(buf), zeroFill, width, 16, &str, &len, true); break; case fmtLongOctal: formatInt(arg.l, buf, sizeof(buf), zeroFill, width, 8, &str, &len); break; case fmtLongBinary: formatInt(arg.l, buf, sizeof(buf), zeroFill, width, 2, &str, &len); break; case fmtULongDecimal: formatUInt(arg.ul, buf, sizeof(buf), zeroFill, width, 10, &str, &len); break; case fmtULongHex: formatUInt(arg.ul, buf, sizeof(buf), zeroFill, width, 16, &str, &len); break; case fmtULongHexUpper: formatUInt(arg.ul, buf, sizeof(buf), zeroFill, width, 16, &str, &len, true); break; case fmtULongOctal: formatUInt(arg.ul, buf, sizeof(buf), zeroFill, width, 8, &str, &len); break; case fmtULongBinary: formatUInt(arg.ul, buf, sizeof(buf), zeroFill, width, 2, &str, &len); break; case fmtLongLongDecimal: formatInt(arg.ll, buf, sizeof(buf), zeroFill, width, 10, &str, &len); break; case fmtLongLongHex: formatInt(arg.ll, buf, sizeof(buf), zeroFill, width, 16, &str, &len); break; case fmtLongLongHexUpper: formatInt(arg.ll, buf, sizeof(buf), zeroFill, width, 16, &str, &len, true); break; case fmtLongLongOctal: formatInt(arg.ll, buf, sizeof(buf), zeroFill, width, 8, &str, &len); break; case fmtLongLongBinary: formatInt(arg.ll, buf, sizeof(buf), zeroFill, width, 2, &str, &len); break; case fmtULongLongDecimal: formatUInt(arg.ull, buf, sizeof(buf), zeroFill, width, 10, &str, &len); break; case fmtULongLongHex: formatUInt(arg.ull, buf, sizeof(buf), zeroFill, width, 16, &str, &len); break; case fmtULongLongHexUpper: formatUInt(arg.ull, buf, sizeof(buf), zeroFill, width, 16, &str, &len, true); break; case fmtULongLongOctal: formatUInt(arg.ull, buf, sizeof(buf), zeroFill, width, 8, &str, &len); break; case fmtULongLongBinary: formatUInt(arg.ull, buf, sizeof(buf), zeroFill, width, 2, &str, &len); break; case fmtDouble: formatDouble(arg.f, buf, sizeof(buf), prec, false, &str, &len); break; case fmtDoubleTrim: formatDouble(arg.f, buf, sizeof(buf), prec, true, &str, &len); break; case fmtDoubleTrimSmallAware: formatDoubleSmallAware(arg.f, buf, sizeof(buf), prec, true, &str, &len); break; case fmtChar: buf[0] = arg.c; str = buf; len = 1; reverseAlign = !reverseAlign; break; case fmtString: { str = arg.s; 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(); len = arg.gs->getLength(); } else { str = "(null)"; len = 6; } reverseAlign = !reverseAlign; break; case fmtSpace: str = buf; len = 0; width = arg.i; break; } // append the formatted arg, handling width and alignment if (!reverseAlign && len < width) { for (i = len; i < width; ++i) { append(' '); } } append(str, len); if (reverseAlign && len < width) { for (i = len; i < width; ++i) { append(' '); } } } } else if (*p0 == '}') { ++p0; if (*p0 == '}') { ++p0; } append('}'); } else { for (p1 = p0 + 1; *p1 && *p1 != '{' && *p1 != '}'; ++p1) { ; } append(p0, p1 - p0); p0 = p1; } } if (args != argsBuf) { gfree(args); } return this; } namespace { const char lowerCaseDigits[17] = "0123456789abcdef"; const char upperCaseDigits[17] = "0123456789ABCDEF"; void formatInt(long long x, char *buf, int bufSize, bool zeroFill, int width, int base, const char **p, int *len, bool upperCase) { const char *vals = upperCase ? upperCaseDigits : lowerCaseDigits; bool neg; int start, i, j; unsigned long long abs_x; i = bufSize; if ((neg = x < 0)) { abs_x = -x; } else { abs_x = x; } start = neg ? 1 : 0; if (abs_x == 0) { buf[--i] = '0'; } else { while (i > start && abs_x) { buf[--i] = vals[abs_x % base]; abs_x /= base; } } if (zeroFill) { for (j = bufSize - i; i > start && j < width - start; ++j) { buf[--i] = '0'; } } if (neg) { buf[--i] = '-'; } *p = buf + i; *len = bufSize - i; } void formatUInt(unsigned long long x, char *buf, int bufSize, bool zeroFill, int width, int base, const char **p, int *len, bool upperCase) { const char *vals = upperCase ? upperCaseDigits : lowerCaseDigits; int i, j; i = bufSize; if (x == 0) { buf[--i] = '0'; } else { while (i > 0 && x) { buf[--i] = vals[x % base]; x /= base; } } if (zeroFill) { for (j = bufSize - i; i > 0 && j < width; ++j) { buf[--i] = '0'; } } *p = buf + i; *len = bufSize - i; } void formatDouble(double x, char *buf, int bufSize, int prec, bool trim, const char **p, int *len) { bool neg, started; double x2; int d, i, j; if ((neg = x < 0)) { x = -x; } x = floor(x * pow(10.0, prec) + 0.5); i = bufSize; started = !trim; for (j = 0; j < prec && i > 1; ++j) { x2 = floor(0.1 * (x + 0.5)); d = (int)floor(x - 10 * x2 + 0.5); if (started || d != 0) { buf[--i] = '0' + d; started = true; } x = x2; } if (i > 1 && started) { buf[--i] = '.'; } if (i > 1) { do { x2 = floor(0.1 * (x + 0.5)); d = (int)floor(x - 10 * x2 + 0.5); buf[--i] = '0' + d; x = x2; } while (i > 1 && x); } if (neg) { buf[--i] = '-'; } *p = buf + i; *len = bufSize - i; } void formatDoubleSmallAware(double x, char *buf, int bufSize, int prec, bool trim, const char **p, int *len) { double absX = fabs(x); if (absX >= 0.1) { formatDouble(x, buf, bufSize, prec, trim, p, len); } else { while (absX < 0.1 && prec < 16) { absX = absX * 10; prec++; } formatDouble(x, buf, bufSize, prec, trim, p, len); } } } GooString *GooString::lowerCase() { lowerCase(*this); return this; } void GooString::lowerCase(std::string &s) { for (auto &c : s) { if (std::isupper(c)) { c = std::tolower(c); } } } std::string GooString::toLowerCase(const std::string &s) { std::string newString = 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); } poppler-24.02.0/goo/GooString.h000066400000000000000000000233601455701731300162220ustar00rootroot00000000000000//======================================================================== // // GooString.h // // Simple variable-length string type. // // 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) 2006 Krzysztof Kowalczyk // Copyright (C) 2008-2010, 2012, 2014, 2017-2022 Albert Astals Cid // Copyright (C) 2012-2014 Fabio D'Urso // Copyright (C) 2013 Jason Crain // Copyright (C) 2015, 2018 Adam Reichold // Copyright (C) 2016 Jakub Alba // 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) 2019 Christophe Fergeau // Copyright (C) 2019 Tomoyuki Kubota // Copyright (C) 2019, 2020, 2022, 2023 Oliver Sander // Copyright (C) 2019 Hans-Ulrich Jüttner // Copyright (C) 2020 Thorsten Behrens // Copyright (C) 2022 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 // //======================================================================== #ifndef GooString_H #define GooString_H #include "poppler_private_export.h" #include #include #include #ifdef __clang__ # define GOOSTRING_FORMAT __attribute__((__annotate__("gooformat"))) #else # define GOOSTRING_FORMAT #endif class GooString : private std::string { public: // Create an empty string. GooString() = default; // Destructor. ~GooString() = default; GooString(GooString &&other) = default; GooString &operator=(GooString &&other) = default; GooString(const GooString &other) = delete; GooString &operator=(const GooString &other) = delete; // Create a string from a C string. explicit GooString(const char *sA) : std::string(sA ? sA : "") { } // Zero-cost conversion from and to std::string explicit GooString(const std::string &str) : std::string(str) { } explicit GooString(std::string &&str) : std::string(std::move(str)) { } const std::string &toStr() const { return *this; } std::string &toNonConstStr() { return *this; } // Create a string from chars at . This string // can contain null characters. GooString(const char *sA, size_t lengthA) : std::string(sA ? sA : "", sA ? lengthA : 0) { } // Create a string from chars at in . GooString(const GooString *str, int idx, size_t lengthA) : std::string(*str, idx, lengthA) { } GooString(const std::string &str, int idx, size_t lengthA) : std::string(str, idx, lengthA) { } // Set content of a string to . GooString *Set(const GooString *newStr) { assign(newStr ? static_cast(*newStr) : std::string {}); return this; } GooString *Set(const char *newStr) { assign(newStr ? newStr : ""); return this; } GooString *Set(const char *newStr, int newLen) { assign(newStr ? newStr : "", newStr ? newLen : 0); return this; } // Copy a string. explicit GooString(const GooString *str) : std::string(str ? static_cast(*str) : std::string {}) { } GooString *copy() const { return new GooString(this); } // Concatenate two strings. GooString(const GooString *str1, const GooString *str2) { reserve(str1->size() + str2->size()); static_cast(*this).append(*str1); static_cast(*this).append(*str2); } // Create a formatted string. Similar to printf, but without the // string overflow issues. Formatting elements consist of: // {:[][.]} // where: // - is the argument number (arg 0 is the first argument // following the format string) -- NB: args must be first used in // order; they can be reused in any order // - is the field width -- negative to reverse the alignment; // starting with a leading zero to zero-fill (for integers) // - is the number of digits to the right of the decimal // point (for floating point numbers) // - is one of: // d, x, X, o, b -- int in decimal, lowercase hex, uppercase hex, octal, binary // ud, ux, uX, uo, ub -- unsigned int // ld, lx, lX, lo, lb, uld, ulx, ulX, ulo, ulb -- long, unsigned long // lld, llx, llX, llo, llb, ulld, ullx, ullX, ullo, ullb // -- long long, unsigned long long // f, g, gs -- floating point (float or double) // f -- always prints trailing zeros (eg 1.0 with .2f will print 1.00) // g -- omits trailing zeros and, if possible, the dot (eg 1.0 shows up as 1) // gs -- is like g, but treats as number of significant // digits to show (eg 0.0123 with .2gs will print 0.012) // c -- character (char, short or int) // s -- string (char *) // 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); // Get length. int getLength() const { return size(); } // Get C string. using std::string::c_str; // Get th character. char getChar(size_t i) const { return (*this)[i]; } // Change th character. void setChar(int i, char c) { (*this)[i] = c; } // Clear string to zero length. GooString *clear() { static_cast(*this).clear(); return this; } // Append a character or string. GooString *append(char c) { push_back(c); return this; } GooString *append(const GooString *str) { static_cast(*this).append(*str); return this; } GooString *append(const std::string &str) { static_cast(*this).append(str); return this; } GooString *append(const char *str) { static_cast(*this).append(str); return this; } GooString *append(const char *str, size_t lengthA) { static_cast(*this).append(str, lengthA); return this; } // Append a formatted string. POPPLER_PRIVATE_EXPORT GooString *appendf(const char *fmt, ...) GOOSTRING_FORMAT; POPPLER_PRIVATE_EXPORT GooString *appendfv(const char *fmt, va_list argList); // Insert a character or string. GooString *insert(int i, char c) { static_cast(*this).insert(i, 1, c); return this; } GooString *insert(int i, const GooString *str) { static_cast(*this).insert(i, *str); return this; } GooString *insert(int i, const std::string &str) { static_cast(*this).insert(i, str); return this; } GooString *insert(int i, const char *str) { static_cast(*this).insert(i, str); return this; } GooString *insert(int i, const char *str, int lengthA) { static_cast(*this).insert(i, str, lengthA); return this; } // Delete a character or range of characters. GooString *del(int i, int n = 1) { erase(i, n); return this; } // Convert string to all-lower case. POPPLER_PRIVATE_EXPORT GooString *lowerCase(); POPPLER_PRIVATE_EXPORT static void lowerCase(std::string &s); // Returns a new string converted to all-lower case. POPPLER_PRIVATE_EXPORT static std::string toLowerCase(const std::string &s); // Compare two strings: -1:< 0:= +1:> int cmp(const GooString *str) const { return compare(*str); } int cmp(const std::string &str) const { return compare(str); } int cmpN(GooString *str, int n) const { return compare(0, n, *str); } int cmp(const char *sA) const { return compare(sA); } 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; 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'; } POPPLER_PRIVATE_EXPORT void prependUnicodeMarker(); }; #endif poppler-24.02.0/goo/GooTimer.cc000066400000000000000000000040541455701731300161710ustar00rootroot00000000000000//======================================================================== // // GooTimer.cc // // This file is licensed under GPLv2 or later // // Copyright 2005 Jonathan Blandford // Copyright 2007 Krzysztof Kowalczyk // Copyright 2010 Hib Eris // Inspired by gtimer.c in glib, which is Copyright 2000 by the GLib Team // //======================================================================== #include #include "GooTimer.h" #include #define USEC_PER_SEC 1000000 //------------------------------------------------------------------------ // GooTimer //------------------------------------------------------------------------ GooTimer::GooTimer() { start(); } void GooTimer::start() { #ifdef HAVE_GETTIMEOFDAY gettimeofday(&start_time, nullptr); #elif defined(_WIN32) QueryPerformanceCounter(&start_time); #endif active = true; } void GooTimer::stop() { #ifdef HAVE_GETTIMEOFDAY gettimeofday(&end_time, nullptr); #elif defined(_WIN32) QueryPerformanceCounter(&end_time); #endif active = false; } #ifdef HAVE_GETTIMEOFDAY double GooTimer::getElapsed() { double total; struct timeval elapsed; if (active) { gettimeofday(&end_time, nullptr); } if (start_time.tv_usec > end_time.tv_usec) { end_time.tv_usec += USEC_PER_SEC; end_time.tv_sec--; } elapsed.tv_usec = end_time.tv_usec - start_time.tv_usec; elapsed.tv_sec = end_time.tv_sec - start_time.tv_sec; total = elapsed.tv_sec + ((double)elapsed.tv_usec / 1e6); if (total < 0) { total = 0; } return total; } #elif defined(_WIN32) double GooTimer::getElapsed() { LARGE_INTEGER freq; double time_in_secs; QueryPerformanceFrequency(&freq); if (active) QueryPerformanceCounter(&end_time); time_in_secs = (double)(end_time.QuadPart - start_time.QuadPart) / (double)freq.QuadPart; return time_in_secs * 1000.0; } #else double GooTimer::getElapsed() { # warning "no support for GooTimer" return 0; } #endif poppler-24.02.0/goo/GooTimer.h000066400000000000000000000026041455701731300160320ustar00rootroot00000000000000//======================================================================== // // GooTimer.cc // // This file is licensed under GPLv2 or later // // Copyright 2005 Jonathan Blandford // Copyright 2007 Krzysztof Kowalczyk // Copyright 2010 Hib Eris // Copyright 2011 Albert Astals cid // Copyright 2014 Bogdan Cristea // Copyright 2014 Peter Breitenlohner // Inspired by gtimer.c in glib, which is Copyright 2000 by the GLib Team // //======================================================================== #ifndef GOOTIMER_H #define GOOTIMER_H #include "poppler-config.h" #include "poppler_private_export.h" #ifdef HAVE_GETTIMEOFDAY # include #endif #ifdef _WIN32 # ifndef NOMINMAX # define NOMINMAX # endif # include #endif //------------------------------------------------------------------------ // GooTimer //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT GooTimer { public: // Create a new timer. GooTimer(); void start(); void stop(); double getElapsed(); private: #ifdef HAVE_GETTIMEOFDAY struct timeval start_time; struct timeval end_time; #elif defined(_WIN32) LARGE_INTEGER start_time; LARGE_INTEGER end_time; #endif bool active; }; #endif poppler-24.02.0/goo/ImgWriter.cc000066400000000000000000000005221455701731300163510ustar00rootroot00000000000000//======================================================================== // // ImgWriter.cpp // // This file is licensed under the GPLv2 or later // // Copyright (C) 2009 Albert Astals Cid // //======================================================================== #include "ImgWriter.h" ImgWriter::~ImgWriter() { } poppler-24.02.0/goo/ImgWriter.h000066400000000000000000000022021455701731300162100ustar00rootroot00000000000000//======================================================================== // // ImgWriter.h // // This file is licensed under the GPLv2 or later // // Copyright (C) 2009 Stefan Thomas // Copyright (C) 2009, 2011, 2018, 2022 Albert Astals Cid // Copyright (C) 2010 Adrian Johnson // Copyright (C) 2010 Brian Cameron // Copyright (C) 2011 Thomas Freitag // //======================================================================== #ifndef IMGWRITER_H #define IMGWRITER_H #include "poppler_private_export.h" #include class POPPLER_PRIVATE_EXPORT ImgWriter { public: ImgWriter() = default; ImgWriter(const ImgWriter &) = delete; ImgWriter &operator=(const ImgWriter &other) = delete; virtual ~ImgWriter(); virtual bool init(FILE *f, int width, int height, double hDPI, double vDPI) = 0; virtual bool writePointers(unsigned char **rowPointers, int rowCount) = 0; virtual bool writeRow(unsigned char **row) = 0; virtual bool close() = 0; virtual bool supportCMYK() { return false; } }; #endif poppler-24.02.0/goo/JpegWriter.cc000066400000000000000000000125561455701731300165340ustar00rootroot00000000000000//======================================================================== // // JpegWriter.cc // // This file is licensed under the GPLv2 or later // // Copyright (C) 2009 Stefan Thomas // Copyright (C) 2010, 2012, 2017 Adrian Johnson // Copyright (C) 2010 Harry Roberts // Copyright (C) 2011 Thomas Freitag // Copyright (C) 2013 Peter Breitenlohner // Copyright (C) 2017, 2018, 2022 Albert Astals Cid // Copyright (C) 2018 Martin Packman // Copyright (C) 2018 Ed Porras // Copyright (C) 2021 Peter Williams // Copyright (C) 2023 Jordan Abrahams-Whitehead // //======================================================================== #include "JpegWriter.h" #include #ifdef ENABLE_LIBJPEG # include "poppler/Error.h" # include extern "C" { # include } struct JpegWriterPrivate { bool progressive; bool optimize; int quality; JpegWriter::Format format; struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; }; static void outputMessage(j_common_ptr cinfo) { char buffer[JMSG_LENGTH_MAX]; // Create the message (*cinfo->err->format_message)(cinfo, buffer); // Send it to poppler's error handler error(errInternal, -1, "{0:s}", buffer); } JpegWriter::JpegWriter(int q, bool p, Format formatA) { priv = new JpegWriterPrivate; priv->progressive = p; priv->optimize = false; priv->quality = q; priv->format = formatA; } JpegWriter::JpegWriter(Format formatA) : JpegWriter(-1, false, formatA) { } JpegWriter::~JpegWriter() { // cleanup jpeg_destroy_compress(&priv->cinfo); delete priv; } void JpegWriter::setQuality(int quality) { priv->quality = quality; } void JpegWriter::setProgressive(bool progressive) { priv->progressive = progressive; } void JpegWriter::setOptimize(bool optimize) { priv->optimize = optimize; } bool JpegWriter::init(FILE *f, int width, int height, double hDPI, double vDPI) { if (hDPI < 0 || vDPI < 0 || hDPI > std::numeric_limits::max() || vDPI > std::numeric_limits::max()) { error(errInternal, -1, "JpegWriter::init: hDPI or vDPI values are invalid {0:f} {1:f}", hDPI, vDPI); return false; } // Setup error handler priv->cinfo.err = jpeg_std_error(&priv->jerr); priv->jerr.output_message = &outputMessage; // Initialize libjpeg jpeg_create_compress(&priv->cinfo); // First set colorspace and call jpeg_set_defaults() since // jpeg_set_defaults() sets default values for all fields in // cinfo based on the colorspace. switch (priv->format) { case RGB: priv->cinfo.in_color_space = JCS_RGB; break; case GRAY: priv->cinfo.in_color_space = JCS_GRAYSCALE; break; case CMYK: priv->cinfo.in_color_space = JCS_CMYK; break; default: return false; } jpeg_set_defaults(&priv->cinfo); // Set destination file jpeg_stdio_dest(&priv->cinfo, f); // Set libjpeg configuration priv->cinfo.image_width = width; priv->cinfo.image_height = height; priv->cinfo.density_unit = 1; // dots per inch priv->cinfo.X_density = static_cast(hDPI); priv->cinfo.Y_density = static_cast(vDPI); switch (priv->format) { case GRAY: priv->cinfo.input_components = 1; break; case RGB: priv->cinfo.input_components = 3; break; case CMYK: priv->cinfo.input_components = 4; jpeg_set_colorspace(&priv->cinfo, JCS_YCCK); priv->cinfo.write_JFIF_header = TRUE; break; default: return false; } // Set quality if (priv->quality >= 0 && priv->quality <= 100) { jpeg_set_quality(&priv->cinfo, priv->quality, TRUE); } // Use progressive mode if (priv->progressive) { jpeg_simple_progression(&priv->cinfo); } // Set whether to compute optimal Huffman coding tables priv->cinfo.optimize_coding = static_cast(priv->optimize); // Get ready for data jpeg_start_compress(&priv->cinfo, TRUE); return true; } bool JpegWriter::writePointers(unsigned char **rowPointers, int rowCount) { if (priv->format == CMYK) { for (int y = 0; y < rowCount; y++) { unsigned char *row = rowPointers[y]; for (unsigned int x = 0; x < priv->cinfo.image_width; x++) { for (int n = 0; n < 4; n++) { *row = 0xff - *row; row++; } } } } // Write all rows to the file jpeg_write_scanlines(&priv->cinfo, rowPointers, rowCount); return true; } bool JpegWriter::writeRow(unsigned char **rowPointer) { if (priv->format == CMYK) { unsigned char *row = rowPointer[0]; for (unsigned int x = 0; x < priv->cinfo.image_width; x++) { for (int n = 0; n < 4; n++) { *row = 0xff - *row; row++; } } } // Write the row to the file jpeg_write_scanlines(&priv->cinfo, rowPointer, 1); return true; } bool JpegWriter::close() { jpeg_finish_compress(&priv->cinfo); return true; } bool JpegWriter::supportCMYK() { return priv->format == CMYK; } #endif poppler-24.02.0/goo/JpegWriter.h000066400000000000000000000035751455701731300163770ustar00rootroot00000000000000//======================================================================== // // JpegWriter.h // // This file is licensed under the GPLv2 or later // // Copyright (C) 2009 Stefan Thomas // Copyright (C) 2010, 2012, 2017 Adrian Johnson // Copyright (C) 2010 Jürg Billeter // Copyright (C) 2010 Harry Roberts // Copyright (C) 2010 Brian Cameron // Copyright (C) 2011, 2021, 2022 Albert Astals Cid // Copyright (C) 2011 Thomas Freitag // Copyright (C) 2018 Martin Packman // //======================================================================== #ifndef JPEGWRITER_H #define JPEGWRITER_H #include "poppler-config.h" #include "poppler_private_export.h" #ifdef ENABLE_LIBJPEG # include # include "ImgWriter.h" struct JpegWriterPrivate; class POPPLER_PRIVATE_EXPORT JpegWriter : public ImgWriter { public: /* RGB - 3 bytes/pixel * GRAY - 1 byte/pixel * CMYK - 4 bytes/pixel */ enum Format { RGB, GRAY, CMYK }; JpegWriter(int quality, bool progressive, Format format = RGB); explicit JpegWriter(Format format = RGB); ~JpegWriter() override; JpegWriter(const JpegWriter &other) = delete; JpegWriter &operator=(const JpegWriter &other) = delete; void setQuality(int quality); void setProgressive(bool progressive); void setOptimize(bool optimize); bool init(FILE *f, int width, int height, double hDPI, double vDPI) override; bool writePointers(unsigned char **rowPointers, int rowCount) override; bool writeRow(unsigned char **row) override; bool close() override; bool supportCMYK() override; private: JpegWriterPrivate *priv; }; #endif #endif poppler-24.02.0/goo/NetPBMWriter.cc000066400000000000000000000050021455701731300167200ustar00rootroot00000000000000//======================================================================== // // NetPBMWriter.h // // Copyright 1998-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) 2005, 2007, 2011, 2022 Albert Astals Cid // Copyright (C) 2006 Rainer Keller // Copyright (C) 2008 Timothy Lee // Copyright (C) 2008 Vasile Gaburici // Copyright (C) 2009 Carlos Garcia Campos // Copyright (C) 2009 William Bader // Copyright (C) 2010 Jakob Voss // Copyright (C) 2012, 2013 Adrian Johnson // Copyright (C) 2013 Thomas Fischer // // 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 "poppler-config.h" #include "NetPBMWriter.h" // Writer for the NetPBM formats (PBM and PPM) // This format is documented at: // http://netpbm.sourceforge.net/doc/pbm.html // http://netpbm.sourceforge.net/doc/ppm.html NetPBMWriter::NetPBMWriter(Format formatA) : format(formatA) { } bool NetPBMWriter::init(FILE *f, int widthA, int heightA, double /*hDPI*/, double /*vDPI*/) { file = f; width = widthA; if (format == MONOCHROME) { fprintf(file, "P4\n"); fprintf(file, "%d %d\n", widthA, heightA); } else { fprintf(file, "P6\n"); fprintf(file, "%d %d\n", widthA, heightA); fprintf(file, "255\n"); } return true; } bool NetPBMWriter::writePointers(unsigned char **rowPointers, int rowCount) { for (int i = 0; i < rowCount; i++) { writeRow(&rowPointers[i]); } return true; } bool NetPBMWriter::writeRow(unsigned char **row) { if (format == MONOCHROME) { // PBM uses 0 = white, 1 = black so we need to invert the colors int size = (width + 7) / 8; for (int i = 0; i < size; i++) { fputc((*row)[i] ^ 0xff, file); } } else { fwrite(*row, 1, width * 3, file); } return true; } bool NetPBMWriter::close() { return true; } poppler-24.02.0/goo/NetPBMWriter.h000066400000000000000000000027101455701731300165650ustar00rootroot00000000000000//======================================================================== // // NetPBMWriter.h // // This file is licensed under the GPLv2 or later // // Copyright (C) 2009 Stefan Thomas // Copyright (C) 2009, 2011, 2021, 2022 Albert Astals Cid // Copyright (C) 2010, 2013 Adrian Johnson // Copyright (C) 2010 Brian Cameron // Copyright (C) 2011 Thomas Freitag // //======================================================================== #ifndef NETPBMWRITER_H #define NETPBMWRITER_H #include "poppler-config.h" #include "poppler_private_export.h" #include "ImgWriter.h" // Writer for the NetPBM formats (PBM and PPM) // This format is documented at: // http://netpbm.sourceforge.net/doc/pbm.html // http://netpbm.sourceforge.net/doc/ppm.html class POPPLER_PRIVATE_EXPORT NetPBMWriter : public ImgWriter { public: /* RGB - 3 bytes/pixel * MONOCHROME - 8 pixels/byte */ enum Format { RGB, MONOCHROME }; explicit NetPBMWriter(Format formatA = RGB); ~NetPBMWriter() override = default; bool init(FILE *f, int width, int height, double /*hDPI*/, double /*vDPI*/) override; bool writePointers(unsigned char **rowPointers, int rowCount) override; bool writeRow(unsigned char **row) override; bool close() override; private: FILE *file; Format format; int width; }; #endif poppler-24.02.0/goo/PNGWriter.cc000066400000000000000000000134561455701731300162730ustar00rootroot00000000000000//======================================================================== // // PNGWriter.cc // // This file is licensed under the GPLv2 or later // // Copyright (C) 2009 Warren Toomey // Copyright (C) 2009 Shen Liang // 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 // Copyright (C) 2012 Pino Toscano // //======================================================================== #include "PNGWriter.h" #ifdef ENABLE_LIBPNG # include # include # include # include "poppler/Error.h" # include "goo/gmem.h" # include struct PNGWriterPrivate { explicit PNGWriterPrivate(PNGWriter::Format f) : format(f) { } PNGWriter::Format format; 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(formatA); } PNGWriter::~PNGWriter() { /* cleanup heap allocation */ png_destroy_write_struct(&priv->png_ptr, &priv->info_ptr); if (priv->icc_data) { gfree(priv->icc_data); free(priv->icc_name); } delete priv; } void PNGWriter::setICCProfile(const char *name, unsigned char *data, int size) { priv->icc_data = (unsigned char *)gmalloc(size); memcpy(priv->icc_data, data, size); priv->icc_data_size = size; priv->icc_name = strdup(name); } void PNGWriter::setSRGBProfile() { priv->sRGB_profile = true; } bool PNGWriter::init(FILE *f, int width, int height, double hDPI, double vDPI) { /* libpng changed the png_set_iCCP() prototype in 1.5.0 */ # if PNG_LIBPNG_VER < 10500 png_charp icc_data_ptr = (png_charp)priv->icc_data; # else png_const_bytep icc_data_ptr = (png_const_bytep)priv->icc_data; # endif if (hDPI < 0 || vDPI < 0) { error(errInternal, -1, "PNGWriter::init: hDPI or vDPI values are invalid {0:f} {1:f}", hDPI, vDPI); return false; } const double png_res_x = hDPI / 0.0254; const double png_res_y = vDPI / 0.0254; if (png_res_x > std::numeric_limits::max() || png_res_y > std::numeric_limits::max()) { error(errInternal, -1, "PNGWriter::init: hDPI or vDPI values are invalid {0:f} {1:f}", hDPI, vDPI); return false; } /* initialize stuff */ priv->png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); if (!priv->png_ptr) { error(errInternal, -1, "png_create_write_struct failed"); return false; } priv->info_ptr = png_create_info_struct(priv->png_ptr); if (!priv->info_ptr) { error(errInternal, -1, "png_create_info_struct failed"); return false; } if (setjmp(png_jmpbuf(priv->png_ptr))) { error(errInternal, -1, "png_jmpbuf failed"); return false; } /* write header */ png_init_io(priv->png_ptr, f); if (setjmp(png_jmpbuf(priv->png_ptr))) { error(errInternal, -1, "Error during writing header"); return false; } // Set up the type of PNG image and the compression level png_set_compression_level(priv->png_ptr, Z_BEST_COMPRESSION); // Silence silly gcc png_byte bit_depth = -1; png_byte color_type = -1; switch (priv->format) { case RGB: bit_depth = 8; color_type = PNG_COLOR_TYPE_RGB; break; case RGB48: bit_depth = 16; color_type = PNG_COLOR_TYPE_RGB; break; case RGBA: bit_depth = 8; color_type = PNG_COLOR_TYPE_RGB_ALPHA; break; case GRAY: bit_depth = 8; color_type = PNG_COLOR_TYPE_GRAY; break; case MONOCHROME: bit_depth = 1; color_type = PNG_COLOR_TYPE_GRAY; break; } png_byte interlace_type = PNG_INTERLACE_NONE; png_set_IHDR(priv->png_ptr, priv->info_ptr, width, height, bit_depth, color_type, interlace_type, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_set_pHYs(priv->png_ptr, priv->info_ptr, static_cast(png_res_x), static_cast(png_res_y), PNG_RESOLUTION_METER); if (priv->icc_data) { png_set_iCCP(priv->png_ptr, priv->info_ptr, priv->icc_name, PNG_COMPRESSION_TYPE_BASE, icc_data_ptr, priv->icc_data_size); } else if (priv->sRGB_profile) { png_set_sRGB(priv->png_ptr, priv->info_ptr, PNG_sRGB_INTENT_RELATIVE); } png_write_info(priv->png_ptr, priv->info_ptr); if (setjmp(png_jmpbuf(priv->png_ptr))) { error(errInternal, -1, "error during writing png info bytes"); return false; } return true; } bool PNGWriter::writePointers(unsigned char **rowPointers, int rowCount) { png_write_image(priv->png_ptr, rowPointers); /* write bytes */ if (setjmp(png_jmpbuf(priv->png_ptr))) { error(errInternal, -1, "Error during writing bytes"); return false; } return true; } bool PNGWriter::writeRow(unsigned char **row) { // Write the row to the file png_write_rows(priv->png_ptr, row, 1); if (setjmp(png_jmpbuf(priv->png_ptr))) { error(errInternal, -1, "error during png row write"); return false; } return true; } bool PNGWriter::close() { /* end write */ png_write_end(priv->png_ptr, priv->info_ptr); if (setjmp(png_jmpbuf(priv->png_ptr))) { error(errInternal, -1, "Error during end of write"); return false; } return true; } #endif poppler-24.02.0/goo/PNGWriter.h000066400000000000000000000032511455701731300161250ustar00rootroot00000000000000//======================================================================== // // PNGWriter.h // // This file is licensed under the GPLv2 or later // // Copyright (C) 2009 Warren Toomey // Copyright (C) 2009 Shen Liang // Copyright (C) 2009, 2011-2013, 2021, 2022 Albert Astals Cid // Copyright (C) 2009 Stefan Thomas // Copyright (C) 2010, 2011, 2013, 2017 Adrian Johnson // Copyright (C) 2012 Pino Toscano // //======================================================================== #ifndef PNGWRITER_H #define PNGWRITER_H #include "poppler-config.h" #include "poppler_private_export.h" #ifdef ENABLE_LIBPNG # include "ImgWriter.h" struct PNGWriterPrivate; class POPPLER_PRIVATE_EXPORT PNGWriter : public ImgWriter { public: /* RGB - 3 bytes/pixel * RGBA - 4 bytes/pixel * GRAY - 1 byte/pixel * MONOCHROME - 8 pixels/byte * RGB48 - 6 bytes/pixel */ enum Format { RGB, RGBA, GRAY, MONOCHROME, RGB48 }; explicit PNGWriter(Format format = RGB); ~PNGWriter() override; PNGWriter(const PNGWriter &other) = delete; PNGWriter &operator=(const PNGWriter &other) = delete; void setICCProfile(const char *name, unsigned char *data, int size); void setSRGBProfile(); bool init(FILE *f, int width, int height, double hDPI, double vDPI) override; bool writePointers(unsigned char **rowPointers, int rowCount) override; bool writeRow(unsigned char **row) override; bool close() override; private: PNGWriterPrivate *priv; }; #endif #endif poppler-24.02.0/goo/TiffWriter.cc000066400000000000000000000170611455701731300165330ustar00rootroot00000000000000//======================================================================== // // TiffWriter.cc // // This file is licensed under the GPLv2 or later // // Copyright (C) 2010, 2012 William Bader // Copyright (C) 2012, 2021, 2022 Albert Astals Cid // Copyright (C) 2012, 2017 Adrian Johnson // Copyright (C) 2012 Pino Toscano // Copyright (C) 2014 Steven Lee // //======================================================================== #include "TiffWriter.h" #ifdef ENABLE_LIBTIFF # include # ifdef _WIN32 # include # endif extern "C" { # include } # include struct TiffWriterPrivate { TIFF *f; // LibTiff file context int numRows; // number of rows in the image int curRow; // number of rows written const char *compressionString; // compression type TiffWriter::Format format; // format of image data }; TiffWriter::~TiffWriter() { delete priv; } TiffWriter::TiffWriter(Format formatA) { priv = new TiffWriterPrivate; priv->f = nullptr; priv->numRows = 0; priv->curRow = 0; priv->compressionString = nullptr; priv->format = formatA; } // Set the compression type void TiffWriter::setCompressionString(const char *compressionStringArg) { priv->compressionString = compressionStringArg; } // Write a TIFF file. bool TiffWriter::init(FILE *openedFile, int width, int height, double hDPI, double vDPI) { unsigned int compression; uint16_t photometric = 0; uint32_t rowsperstrip = (uint32_t)-1; int bitspersample; uint16_t samplesperpixel = 0; const struct compression_name_tag { const char *compressionName; // name of the compression option from the command line unsigned int compressionCode; // internal libtiff code const char *compressionDescription; // descriptive name } compressionList[] = { { "none", COMPRESSION_NONE, "no compression" }, { "ccittrle", COMPRESSION_CCITTRLE, "CCITT modified Huffman RLE" }, { "ccittfax3", COMPRESSION_CCITTFAX3, "CCITT Group 3 fax encoding" }, { "ccittt4", COMPRESSION_CCITT_T4, "CCITT T.4 (TIFF 6 name)" }, { "ccittfax4", COMPRESSION_CCITTFAX4, "CCITT Group 4 fax encoding" }, { "ccittt6", COMPRESSION_CCITT_T6, "CCITT T.6 (TIFF 6 name)" }, { "lzw", COMPRESSION_LZW, "Lempel-Ziv & Welch" }, { "ojpeg", COMPRESSION_OJPEG, "!6.0 JPEG" }, { "jpeg", COMPRESSION_JPEG, "%JPEG DCT compression" }, { "next", COMPRESSION_NEXT, "NeXT 2-bit RLE" }, { "packbits", COMPRESSION_PACKBITS, "Macintosh RLE" }, { "ccittrlew", COMPRESSION_CCITTRLEW, "CCITT modified Huffman RLE w/ word alignment" }, { "deflate", COMPRESSION_DEFLATE, "Deflate compression" }, { "adeflate", COMPRESSION_ADOBE_DEFLATE, "Deflate compression, as recognized by Adobe" }, { "dcs", COMPRESSION_DCS, "Kodak DCS encoding" }, { "jbig", COMPRESSION_JBIG, "ISO JBIG" }, { "jp2000", COMPRESSION_JP2000, "Leadtools JPEG2000" }, { nullptr, 0, nullptr } }; // Initialize priv->f = nullptr; priv->curRow = 0; // Store the number of rows priv->numRows = height; // Set the compression compression = COMPRESSION_NONE; if (priv->compressionString == nullptr || strcmp(priv->compressionString, "") == 0) { compression = COMPRESSION_NONE; } else { int i; for (i = 0; compressionList[i].compressionName != nullptr; i++) { if (strcmp(priv->compressionString, compressionList[i].compressionName) == 0) { compression = compressionList[i].compressionCode; break; } } if (compressionList[i].compressionName == nullptr) { fprintf(stderr, "TiffWriter: Unknown compression type '%.10s', using 'none'.\n", priv->compressionString); fprintf(stderr, "Known compression types (the tiff library might not support every type)\n"); for (i = 0; compressionList[i].compressionName != nullptr; i++) { fprintf(stderr, "%10s %s\n", compressionList[i].compressionName, compressionList[i].compressionDescription); } } } // Set bits per sample, samples per pixel, and photometric type from format bitspersample = (priv->format == MONOCHROME ? 1 : 8); switch (priv->format) { case MONOCHROME: case GRAY: samplesperpixel = 1; photometric = PHOTOMETRIC_MINISBLACK; break; case RGB: samplesperpixel = 3; photometric = PHOTOMETRIC_RGB; break; case RGBA_PREMULTIPLIED: samplesperpixel = 4; photometric = PHOTOMETRIC_RGB; break; case CMYK: samplesperpixel = 4; photometric = PHOTOMETRIC_SEPARATED; break; case RGB48: samplesperpixel = 3; bitspersample = 16; photometric = PHOTOMETRIC_RGB; break; } // Open the file if (openedFile == nullptr) { fprintf(stderr, "TiffWriter: No output file given.\n"); return false; } # ifdef _WIN32 // Convert C Library handle to Win32 Handle priv->f = TIFFFdOpen(_get_osfhandle(fileno(openedFile)), "-", "w"); # else priv->f = TIFFFdOpen(fileno(openedFile), "-", "w"); # endif if (!priv->f) { return false; } // Set TIFF tags TIFFSetField(priv->f, TIFFTAG_IMAGEWIDTH, width); TIFFSetField(priv->f, TIFFTAG_IMAGELENGTH, height); TIFFSetField(priv->f, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); TIFFSetField(priv->f, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel); TIFFSetField(priv->f, TIFFTAG_BITSPERSAMPLE, bitspersample); TIFFSetField(priv->f, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); TIFFSetField(priv->f, TIFFTAG_PHOTOMETRIC, photometric); TIFFSetField(priv->f, TIFFTAG_COMPRESSION, (uint16_t)compression); TIFFSetField(priv->f, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(priv->f, rowsperstrip)); TIFFSetField(priv->f, TIFFTAG_XRESOLUTION, hDPI); TIFFSetField(priv->f, TIFFTAG_YRESOLUTION, vDPI); TIFFSetField(priv->f, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH); if (priv->format == RGBA_PREMULTIPLIED) { uint16_t extra = EXTRASAMPLE_ASSOCALPHA; TIFFSetField(priv->f, TIFFTAG_EXTRASAMPLES, 1, &extra); } if (priv->format == CMYK) { TIFFSetField(priv->f, TIFFTAG_INKSET, INKSET_CMYK); TIFFSetField(priv->f, TIFFTAG_NUMBEROFINKS, 4); } return true; } bool TiffWriter::writePointers(unsigned char **rowPointers, int rowCount) { // Write all rows to the file for (int row = 0; row < rowCount; row++) { if (TIFFWriteScanline(priv->f, rowPointers[row], row, 0) < 0) { fprintf(stderr, "TiffWriter: Error writing tiff row %d\n", row); return false; } } return true; } bool TiffWriter::writeRow(unsigned char **rowData) { // Add a single row if (TIFFWriteScanline(priv->f, *rowData, priv->curRow, 0) < 0) { fprintf(stderr, "TiffWriter: Error writing tiff row %d\n", priv->curRow); return false; } priv->curRow++; return true; } bool TiffWriter::close() { // Close the file TIFFClose(priv->f); return true; } #endif poppler-24.02.0/goo/TiffWriter.h000066400000000000000000000034111455701731300163670ustar00rootroot00000000000000//======================================================================== // // TiffWriter.h // // This file is licensed under the GPLv2 or later // // Copyright (C) 2010, 2012 William Bader // Copyright (C) 2011, 2012, 2021, 2022 Albert Astals Cid // Copyright (C) 2012, 2017 Adrian Johnson // Copyright (C) 2012 Pino Toscano // //======================================================================== #ifndef TIFFWRITER_H #define TIFFWRITER_H #include "poppler-config.h" #include "poppler_private_export.h" #ifdef ENABLE_LIBTIFF # include # include "ImgWriter.h" struct TiffWriterPrivate; class POPPLER_PRIVATE_EXPORT TiffWriter : public ImgWriter { public: /* RGB - 3 bytes/pixel * RGBA_PREMULTIPLIED - 4 bytes/pixel premultiplied by alpha * GRAY - 1 byte/pixel * MONOCHROME - 8 pixels/byte * CMYK - 4 bytes/pixel * RGB48 - 6 bytes/pixel */ enum Format { RGB, RGBA_PREMULTIPLIED, GRAY, MONOCHROME, CMYK, RGB48 }; explicit TiffWriter(Format format = RGB); ~TiffWriter() override; TiffWriter(const TiffWriter &other) = delete; TiffWriter &operator=(const TiffWriter &other) = delete; void setCompressionString(const char *compressionStringArg); bool init(FILE *openedFile, int width, int height, double hDPI, double vDPI) override; bool writePointers(unsigned char **rowPointers, int rowCount) override; bool writeRow(unsigned char **rowData) override; bool supportCMYK() override { return true; } bool close() override; private: TiffWriterPrivate *priv; }; #endif #endif poppler-24.02.0/goo/ft_utils.cc000066400000000000000000000035231455701731300162750ustar00rootroot00000000000000//======================================================================== // // ft_util.cc // // FreeType helper functions. // // This file is licensed under the GPLv2 or later // // Copyright (C) 2022 Adrian Johnson // //======================================================================== #include #include "ft_utils.h" #include "gfile.h" #ifdef _WIN32 static unsigned long ft_stream_read(FT_Stream stream, unsigned long offset, unsigned char *buffer, unsigned long count) { FILE *file = (FILE *)stream->descriptor.pointer; fseek(file, offset, SEEK_SET); return fread(buffer, 1, count, file); } static void ft_stream_close(FT_Stream stream) { FILE *file = (FILE *)stream->descriptor.pointer; fclose(file); delete stream; } #endif // Same as FT_New_Face() but handles UTF-8 filenames on Windows FT_Error ft_new_face_from_file(FT_Library library, const char *filename_utf8, FT_Long face_index, FT_Face *aface) { #ifdef _WIN32 FILE *file; long size; if (!filename_utf8) return FT_Err_Invalid_Argument; file = openFile(filename_utf8, "rb"); if (!file) return FT_Err_Cannot_Open_Resource; fseek(file, 0, SEEK_END); size = ftell(file); rewind(file); if (size <= 0) return FT_Err_Cannot_Open_Stream; FT_StreamRec *stream = new FT_StreamRec; *stream = {}; stream->size = size; stream->read = ft_stream_read; stream->close = ft_stream_close; stream->descriptor.pointer = file; FT_Open_Args args = {}; args.flags = FT_OPEN_STREAM; args.stream = stream; return FT_Open_Face(library, &args, face_index, aface); #else // On POSIX, FT_New_Face mmaps font files. If not Windows, prefer FT_New_Face over our stdio.h based FT_Open_Face. return FT_New_Face(library, filename_utf8, face_index, aface); #endif } poppler-24.02.0/goo/ft_utils.h000066400000000000000000000012551455701731300161370ustar00rootroot00000000000000//======================================================================== // // ft_util.h // // FreeType helper functions. // // This file is licensed under the GPLv2 or later // // Copyright (C) 2022 Adrian Johnson // //======================================================================== #ifndef FT_UTILS_H #define FT_UTILS_H #include "config.h" #include "poppler_private_export.h" #include #include FT_FREETYPE_H // Same as FT_New_Face() but handles UTF-8 filenames on Windows POPPLER_PRIVATE_EXPORT FT_Error ft_new_face_from_file(FT_Library library, const char *filename_utf8, FT_Long face_index, FT_Face *aface); #endif // FT_UTILS_H poppler-24.02.0/goo/gbase64.cc000066400000000000000000000032121455701731300156720ustar00rootroot00000000000000//======================================================================== // // gbase64.cc // // Implementation of a base64 encoder, because another one did not immediately // avail itself. // // This file is licensed under the GPLv2 or later // // Copyright (C) 2018 Greg Knight // //======================================================================== #include "gbase64.h" #include static void b64encodeTriplet(char output[4], unsigned char a, unsigned char b, unsigned char c) { static const char *base64table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 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 } std::string gbase64Encode(const void *input, size_t len) { char quad[4]; size_t pos = 0; std::stringstream buf; auto bytes = static_cast(input); for (; pos + 3 <= len; pos += 3) { b64encodeTriplet(quad, bytes[0], bytes[1], bytes[2]); buf.write(&quad[0], 4); bytes += 3; } switch (len - pos) { case 1: b64encodeTriplet(quad, bytes[0], 0, 0); quad[2] = quad[3] = '='; buf.write(&quad[0], 4); break; case 2: b64encodeTriplet(quad, bytes[0], bytes[1], 0); quad[3] = '='; buf.write(&quad[0], 4); break; } return buf.str(); } poppler-24.02.0/goo/gbase64.h000066400000000000000000000017231455701731300155410ustar00rootroot00000000000000//======================================================================== // // gbase64.h // // Implementation of a base64 encoder, because another one did not immediately // avail itself. // // This file is licensed under the GPLv2 or later // // Copyright (C) 2018 Greg Knight // Copyright (C) 2019 Albert Astals Cid // //======================================================================== #ifndef GOO_GBASE64_H #define GOO_GBASE64_H #include "poppler_private_export.h" #include #include std::string POPPLER_PRIVATE_EXPORT gbase64Encode(const void *input, size_t len); inline std::string gbase64Encode(const std::vector &input) { return input.empty() ? std::string() : gbase64Encode(&input[0], input.size()); } inline std::string gbase64Encode(const std::vector &input) { return input.empty() ? std::string() : gbase64Encode(&input[0], input.size()); } #endif // ndef GOO_GBASE64_H poppler-24.02.0/goo/gbasename.cc000066400000000000000000000044621455701731300163710ustar00rootroot00000000000000//======================================================================== // // gbasename.cc // // Wrapper for libgen's basename() call which returns a std::string. // This is a convenience method working around questionable behavior // in the copy of basename() provided by libgen.h. // // According to man 3 basename: // // Both dirname() and basename() may modify the contents of path, so it // may be desirable to pass a copy when calling one of these functions. // // ... // // These functions may return pointers to statically allocated memory // which may be overwritten by subsequent calls. Alternatively, they // may return a pointer to some part of path, so that the string // referred to by path should not be modified or freed until the pointer // returned by the function is no longer required. // // Because basename can modify filename (for some reason), we have to // duplicate our input into a mutable buffer before we can call it. // The return value might be part of this mutable temporary, but not // generally the front, so 'char *' cannot be used as our return value. // The return value might also be a statically allocated string, // rendering basename (and thus gbasename) non-thread-safe. Because // we don't know how basename()'s return value is lifecycled, we need // to duplicate it again into something whose lifecycle we can predict. // // This is how a method that should amount to finding the last slash // in a string ends up requiring two memory allocations while managing // not to be thread-safe. In a way, it's kind of impressive. // // This file is licensed under the GPLv2 or later // // Copyright (C) 2018, 2019 Greg Knight // Copyright (C) 2019 Albert Astals Cid // //======================================================================== #include "gbasename.h" #ifndef _MSC_VER # include #endif #include #include std::string gbasename(const char *filename) { #ifdef _MSC_VER char fname[_MAX_FNAME] = {}, fext[_MAX_EXT] = {}; errno_t z = _splitpath_s(filename, NULL, 0, NULL, 0, fname, _countof(fname), fext, _countof(fext)); return std::string(fname) + std::string(fext); #else char *mutabl = strdup(filename); std::string retu = basename(mutabl); free(mutabl); return retu; #endif } poppler-24.02.0/goo/gbasename.h000066400000000000000000000013211455701731300162220ustar00rootroot00000000000000//======================================================================== // // gbasename.h // // Wrapper for libgen's basename() call which returns a std::string. // This is a convenience method working around questionable behavior // in the copy of basename() provided by libgen.h. // // This file is licensed under the GPLv2 or later // // Copyright (C) 2018 Greg Knight // Copyright (C) 2019 Albert Astals Cid // //======================================================================== #ifndef GBASENAME_H #define GBASENAME_H #include #include "poppler_private_export.h" std::string POPPLER_PRIVATE_EXPORT gbasename(const char *filename); #endif // ndef GBASENAME_H poppler-24.02.0/goo/gdir.h000066400000000000000000000053331455701731300152340ustar00rootroot00000000000000//======================================================================== // // 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 poppler-24.02.0/goo/gfile.cc000066400000000000000000000325031455701731300155320ustar00rootroot00000000000000//======================================================================== // // gfile.cc // // 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 Takashi Iwai // 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 Kovid Goyal // Copyright (C) 2013, 2018 Adam Reichold // Copyright (C) 2013, 2017 Adrian Johnson // Copyright (C) 2013 Peter Breitenlohner // Copyright (C) 2013, 2017 Thomas Freitag // Copyright (C) 2017 Christoph Cullmann // Copyright (C) 2018 Mojca Miklavec // Copyright (C) 2019, 2021 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 // //======================================================================== #include #ifndef _WIN32 # include # include # include # include # include # include #endif // _WIN32 #include #include #include "GooString.h" #include "gfile.h" #include "gdir.h" // Some systems don't define this, so just make it something reasonably // large. #ifndef PATH_MAX # define PATH_MAX 1024 #endif #ifndef _WIN32 using namespace std::string_literals; namespace { template struct void_type { using type = void; }; template using void_t = typename void_type::type; template> struct StatMtim { static const struct timespec &value(const Stat &stbuf) { return stbuf.st_mtim; } }; // Mac OS X uses a different field name than POSIX and this detects it. template struct StatMtim> { static const struct timespec &value(const Stat &stbuf) { return stbuf.st_mtimespec; } }; inline const struct timespec &mtim(const struct stat &stbuf) { return StatMtim::value(stbuf); } } #endif //------------------------------------------------------------------------ GooString *appendToPath(GooString *path, const char *fileName) { #ifdef _WIN32 //---------- Win32 ---------- GooString *tmp; char buf[256]; char *fp; tmp = new GooString(path); tmp->append('/'); tmp->append(fileName); GetFullPathNameA(tmp->c_str(), sizeof(buf), buf, &fp); delete tmp; path->clear(); path->append(buf); return path; #else //---------- Unix ---------- int i; // appending "." does nothing if (!strcmp(fileName, ".")) { return path; } // appending ".." goes up one directory if (!strcmp(fileName, "..")) { for (i = path->getLength() - 2; i >= 0; --i) { if (path->getChar(i) == '/') { break; } } if (i <= 0) { if (path->getChar(0) == '/') { path->del(1, path->getLength() - 1); } else { path->clear(); path->append(".."); } } else { path->del(i, path->getLength() - i); } return path; } // otherwise, append "/" and new path component if (path->getLength() > 0 && path->getChar(path->getLength() - 1) != '/') { path->append('/'); } path->append(fileName); return path; #endif } #ifndef _WIN32 static bool makeFileDescriptorCloexec(int fd) { # ifdef FD_CLOEXEC int flags = fcntl(fd, F_GETFD); if (flags >= 0 && !(flags & FD_CLOEXEC)) { flags = fcntl(fd, F_SETFD, flags | FD_CLOEXEC); } return flags >= 0; # else return true; # endif } int openFileDescriptor(const char *path, int flags) { # ifdef O_CLOEXEC return open(path, flags | O_CLOEXEC); # else int fd = open(path, flags); if (fd == -1) return fd; if (!makeFileDescriptorCloexec(fd)) { close(fd); return -1; } return fd; # endif } #endif FILE *openFile(const char *path, const char *mode) { #ifdef _WIN32 OSVERSIONINFO version; wchar_t wPath[_MAX_PATH + 1]; char nPath[_MAX_PATH + 1]; wchar_t wMode[8]; const char *p; size_t i; // NB: _wfopen is only available in NT version.dwOSVersionInfoSize = sizeof(version); GetVersionEx(&version); if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) { for (p = path, i = 0; *p && i < _MAX_PATH; ++i) { if ((p[0] & 0xe0) == 0xc0 && p[1] && (p[1] & 0xc0) == 0x80) { wPath[i] = (wchar_t)(((p[0] & 0x1f) << 6) | (p[1] & 0x3f)); p += 2; } else if ((p[0] & 0xf0) == 0xe0 && p[1] && (p[1] & 0xc0) == 0x80 && p[2] && (p[2] & 0xc0) == 0x80) { wPath[i] = (wchar_t)(((p[0] & 0x0f) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f)); p += 3; } else { wPath[i] = (wchar_t)(p[0] & 0xff); p += 1; } } wPath[i] = (wchar_t)0; for (i = 0; (i < sizeof(mode) - 1) && mode[i]; ++i) { wMode[i] = (wchar_t)(mode[i] & 0xff); } wMode[i] = (wchar_t)0; return _wfopen(wPath, wMode); } else { for (p = path, i = 0; *p && i < _MAX_PATH; ++i) { if ((p[0] & 0xe0) == 0xc0 && p[1] && (p[1] & 0xc0) == 0x80) { nPath[i] = (char)(((p[0] & 0x1f) << 6) | (p[1] & 0x3f)); p += 2; } else if ((p[0] & 0xf0) == 0xe0 && p[1] && (p[1] & 0xc0) == 0x80 && p[2] && (p[2] & 0xc0) == 0x80) { nPath[i] = (char)(((p[1] & 0x3f) << 6) | (p[2] & 0x3f)); p += 3; } else { nPath[i] = p[0]; p += 1; } } nPath[i] = '\0'; return fopen(nPath, mode); } #else // First try to atomically open the file with CLOEXEC const std::string modeStr = mode + "e"s; FILE *file = fopen(path, modeStr.c_str()); if (file != nullptr) { return file; } // Fall back to the provided mode and apply CLOEXEC afterwards file = fopen(path, mode); if (file == nullptr) { return nullptr; } if (!makeFileDescriptorCloexec(fileno(file))) { fclose(file); return nullptr; } return file; #endif } char *getLine(char *buf, int size, FILE *f) { int c, i; i = 0; while (i < size - 1) { if ((c = fgetc(f)) == EOF) { break; } buf[i++] = (char)c; if (c == '\x0a') { break; } if (c == '\x0d') { c = fgetc(f); if (c == '\x0a' && i < size - 1) { buf[i++] = (char)c; } else if (c != EOF) { ungetc(c, f); } break; } } buf[i] = '\0'; if (i == 0) { return nullptr; } return buf; } int Gfseek(FILE *f, Goffset offset, int whence) { #if defined(HAVE_FSEEKO) return fseeko(f, offset, whence); #elif defined(HAVE_FSEEK64) return fseek64(f, offset, whence); #elif defined(__MINGW32__) return fseeko64(f, offset, whence); #elif defined(_WIN32) return _fseeki64(f, offset, whence); #else return fseek(f, offset, whence); #endif } Goffset Gftell(FILE *f) { #if defined(HAVE_FSEEKO) return ftello(f); #elif defined(HAVE_FSEEK64) return ftell64(f); #elif defined(__MINGW32__) return ftello64(f); #elif defined(_WIN32) return _ftelli64(f); #else return ftell(f); #endif } Goffset GoffsetMax() { #if defined(HAVE_FSEEKO) return (std::numeric_limits::max)(); #elif defined(HAVE_FSEEK64) || defined(__MINGW32__) return (std::numeric_limits::max)(); #elif defined(_WIN32) return (std::numeric_limits<__int64>::max)(); #else return (std::numeric_limits::max)(); #endif } //------------------------------------------------------------------------ // GooFile //------------------------------------------------------------------------ #ifdef _WIN32 GooFile::GooFile(HANDLE handleA) : handle(handleA) { GetFileTime(handleA, nullptr, nullptr, &modifiedTimeOnOpen); } int GooFile::read(char *buf, int n, Goffset offset) const { DWORD m; LARGE_INTEGER largeInteger = {}; largeInteger.QuadPart = offset; OVERLAPPED overlapped = {}; overlapped.Offset = largeInteger.LowPart; overlapped.OffsetHigh = largeInteger.HighPart; return FALSE == ReadFile(handle, buf, n, &m, &overlapped) ? -1 : m; } Goffset GooFile::size() const { LARGE_INTEGER size = { (DWORD)-1, -1 }; GetFileSizeEx(handle, &size); return size.QuadPart; } std::unique_ptr GooFile::open(const std::string &fileName) { HANDLE handle = CreateFileA(fileName.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); return handle == INVALID_HANDLE_VALUE ? std::unique_ptr() : std::unique_ptr(new GooFile(handle)); } std::unique_ptr GooFile::open(const wchar_t *fileName) { HANDLE handle = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); return handle == INVALID_HANDLE_VALUE ? std::unique_ptr() : std::unique_ptr(new GooFile(handle)); } bool GooFile::modificationTimeChangedSinceOpen() const { struct _FILETIME lastModified; GetFileTime(handle, nullptr, nullptr, &lastModified); return modifiedTimeOnOpen.dwHighDateTime != lastModified.dwHighDateTime || modifiedTimeOnOpen.dwLowDateTime != lastModified.dwLowDateTime; } #else int GooFile::read(char *buf, int n, Goffset offset) const { # ifdef HAVE_PREAD64 return pread64(fd, buf, n, offset); # else return pread(fd, buf, n, offset); # endif } Goffset GooFile::size() const { # ifdef HAVE_LSEEK64 return lseek64(fd, 0, SEEK_END); # else return lseek(fd, 0, SEEK_END); # endif } std::unique_ptr GooFile::open(const std::string &fileName) { int fd = openFileDescriptor(fileName.c_str(), O_RDONLY); return GooFile::open(fd); } std::unique_ptr GooFile::open(int fdA) { return fdA < 0 ? std::unique_ptr() : std::unique_ptr(new GooFile(fdA)); } GooFile::GooFile(int fdA) : fd(fdA) { struct stat statbuf; fstat(fd, &statbuf); modifiedTimeOnOpen = mtim(statbuf); } bool GooFile::modificationTimeChangedSinceOpen() const { struct stat statbuf; fstat(fd, &statbuf); return modifiedTimeOnOpen.tv_sec != mtim(statbuf).tv_sec || modifiedTimeOnOpen.tv_nsec != mtim(statbuf).tv_nsec; } #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 } poppler-24.02.0/goo/gfile.h000066400000000000000000000112761455701731300154000ustar00rootroot00000000000000//======================================================================== // // 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, 2021 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 GFILE_H #define GFILE_H #include "poppler-config.h" #include "poppler_private_export.h" #include #include #include #include #include extern "C" { #if defined(_WIN32) # include # ifdef FPTEX # include # else # ifndef NOMINMAX # define NOMINMAX # endif # include # endif #else # include # include # if defined(HAVE_DIRENT_H) # include # define NAMLEN(d) strlen((d)->d_name) # else # define dirent direct # define NAMLEN(d) (d)->d_namlen # ifdef HAVE_SYS_NDIR_H # include # endif # ifdef HAVE_SYS_DIR_H # include # endif # ifdef HAVE_NDIR_H # include # endif # endif #endif } #include class GooString; /* Integer type for all file offsets and file sizes */ typedef long long Goffset; //------------------------------------------------------------------------ // Append a file name to a path string. may be an empty // string, denoting the current directory). Returns . extern GooString POPPLER_PRIVATE_EXPORT *appendToPath(GooString *path, const char *fileName); #ifndef _WIN32 // Open a file descriptor // Could be implemented on WIN32 too, but the only external caller of // this function is not used on WIN32 extern int POPPLER_PRIVATE_EXPORT openFileDescriptor(const char *path, int flags); #endif // Open a file. On Windows, this converts the path from UTF-8 to // UCS-2 and calls _wfopen (if available). On other OSes, this simply // calls fopen. extern FILE POPPLER_PRIVATE_EXPORT *openFile(const char *path, const char *mode); // Just like fgets, but handles Unix, Mac, and/or DOS end-of-line // conventions. extern char POPPLER_PRIVATE_EXPORT *getLine(char *buf, int size, FILE *f); // Like fseek/ftell but uses platform specific variants that support large files extern int POPPLER_PRIVATE_EXPORT Gfseek(FILE *f, Goffset offset, int whence); extern Goffset POPPLER_PRIVATE_EXPORT Gftell(FILE *f); // Largest offset supported by Gfseek/Gftell extern Goffset GoffsetMax(); //------------------------------------------------------------------------ // GooFile //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT GooFile { public: GooFile(const GooFile &) = delete; GooFile &operator=(const GooFile &other) = delete; int read(char *buf, int n, Goffset offset) const; Goffset size() const; static std::unique_ptr open(const std::string &fileName); #ifndef _WIN32 static std::unique_ptr open(int fdA); #endif #ifdef _WIN32 static std::unique_ptr open(const wchar_t *fileName); ~GooFile() { CloseHandle(handle); } // Asuming than on windows you can't change files that are already open bool modificationTimeChangedSinceOpen() const; private: GooFile(HANDLE handleA); HANDLE handle; struct _FILETIME modifiedTimeOnOpen; #else ~GooFile() { close(fd); } bool modificationTimeChangedSinceOpen() const; private: explicit GooFile(int fdA); int fd; struct timespec modifiedTimeOnOpen; #endif // _WIN32 }; #endif poppler-24.02.0/goo/glibc.cc000066400000000000000000000023471455701731300155270ustar00rootroot00000000000000//======================================================================== // // glibc.h // // Emulate various non-portable glibc functions. // // This file is licensed under the GPLv2 or later // // Copyright (C) 2016 Adrian Johnson // Copyright (C) 2022 Albert Astals Cid // //======================================================================== #include "glibc.h" #ifndef HAVE_GMTIME_R struct tm *gmtime_r(const time_t *timep, struct tm *result) { struct tm *gt; gt = gmtime(timep); if (gt) *result = *gt; return gt; } #endif #ifndef HAVE_LOCALTIME_R struct tm *localtime_r(const time_t *timep, struct tm *result) { struct tm *lt; lt = localtime(timep); *result = *lt; return lt; } #endif #ifndef HAVE_TIMEGM // Get offset of local time from UTC in seconds. DST is ignored. static time_t getLocalTimeZoneOffset() { time_t utc, local; struct tm tm_utc; time(&utc); gmtime_r(&utc, &tm_utc); local = mktime(&tm_utc); return static_cast(difftime(utc, local)); } time_t timegm(struct tm *tm) { tm->tm_isdst = 0; time_t t = mktime(tm); if (t == -1) return t; t += getLocalTimeZoneOffset(); return t; } #endif poppler-24.02.0/goo/glibc.h000066400000000000000000000016421455701731300153660ustar00rootroot00000000000000//======================================================================== // // glibc.h // // Emulate various non-portable glibc functions. // // This file is licensed under the GPLv2 or later // // Copyright (C) 2016, 2017 Adrian Johnson // Copyright (C) 2017 Albert Astals Cid // //======================================================================== #ifndef GLIBC_H #define GLIBC_H #include "config.h" #include "poppler_private_export.h" #include #ifndef HAVE_GMTIME_R struct tm POPPLER_PRIVATE_EXPORT *gmtime_r(const time_t *timep, struct tm *result); #endif #ifndef HAVE_LOCALTIME_R struct tm POPPLER_PRIVATE_EXPORT *localtime_r(const time_t *timep, struct tm *result); #endif #ifndef HAVE_TIMEGM time_t POPPLER_PRIVATE_EXPORT timegm(struct tm *tm); #endif #ifndef HAVE_STRTOK_R char *strtok_r(char *s, const char *delim, char **save_ptr); #endif #endif // GLIBC_H poppler-24.02.0/goo/glibc_strtok_r.cc000066400000000000000000000067201455701731300174550ustar00rootroot00000000000000/* Reentrant string tokenizer. Generic version. Copyright (C) 1991,1996-1999,2001,2004 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ /* Copyright (C) 1991,93,96,97,99,2000,2002 Free Software Foundation, Inc. This file is part of the GNU C Library. Based on strlen implementation by Torbjorn Granlund (tege@sics.se), with help from Dan Sahlin (dan@sics.se) and commentary by Jim Blandy (jimb@ai.mit.edu); adaptation to memchr suggested by Dick Karpinski (dick@cca.ucsf.edu), and implemented by Roland McGrath (roland@ai.mit.edu). The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ //======================================================================== // // 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) 2012 Alexey Pavlov // Copyright (C) 2012 Albert Astals Cid // Copyright (C) 2017 Adrian Johnson // Copyright (C) 2017 Pekka 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 // //======================================================================== #include "glibc.h" #ifndef HAVE_STRTOK_R # include # define __rawmemchr strchr char *strtok_r(char *s, const char *delim, char **save_ptr) { char *token; if (s == NULL) s = *save_ptr; /* Scan leading delimiters. */ s += strspn(s, delim); if (*s == '\0') { *save_ptr = s; return NULL; } /* Find the end of the token. */ token = s; s = strpbrk(token, delim); if (s == NULL) /* This token finishes the string. */ *save_ptr = __rawmemchr(token, '\0'); else { /* Terminate the token and make *SAVE_PTR point past it. */ *s = '\0'; *save_ptr = s + 1; } return token; } #endif poppler-24.02.0/goo/gmem.h000066400000000000000000000113311455701731300152270ustar00rootroot00000000000000/* * gmem.h * * Memory routines with out-of-memory checking. * * 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) 2005 Takashi Iwai // Copyright (C) 2007-2010, 2017, 2019, 2022 Albert Astals Cid // Copyright (C) 2008 Jonathan Kew // Copyright (C) 2018 Adam Reichold // Copyright (C) 2021 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 // //======================================================================== #ifndef GMEM_H #define GMEM_H #include #include #include #include #include "GooCheckedOps.h" /// Same as malloc, but prints error message and exits if malloc() returns NULL. inline void *gmalloc(size_t size, bool checkoverflow = false) { if (size == 0) { return nullptr; } if (void *p = std::malloc(size)) { return p; } std::fputs("Out of memory\n", stderr); if (checkoverflow) { return nullptr; } std::abort(); } inline void *gmalloc_checkoverflow(size_t size) { return gmalloc(size, true); } /// Same as free inline void gfree(void *p) { std::free(p); } /// Same as realloc, but prints error message and exits if realloc() returns NULL. /// If

is NULL, calls malloc() instead of realloc(). inline void *grealloc(void *p, size_t size, bool checkoverflow = false) { if (size == 0) { gfree(p); return nullptr; } if (void *q = p ? std::realloc(p, size) : std::malloc(size)) { return q; } std::fputs("Out of memory\n", stderr); if (checkoverflow) { return nullptr; } std::abort(); } inline void *grealloc_checkoverflow(void *p, size_t size) { return grealloc(p, size, true); } /* * These are similar to gmalloc and grealloc, but take an object count * and size. The result is similar to allocating * * bytes, but there is an additional error check that the total size * doesn't overflow an int. * The gmallocn_checkoverflow variant returns NULL instead of exiting * the application if a overflow is detected. */ inline void *gmallocn(int count, int size, bool checkoverflow = false) { if (count == 0) { return nullptr; } int bytes; if (count < 0 || size <= 0 || checkedMultiply(count, size, &bytes)) { std::fputs("Bogus memory allocation size\n", stderr); if (checkoverflow) { return nullptr; } std::abort(); } return gmalloc(bytes, checkoverflow); } inline void *gmallocn_checkoverflow(int count, int size) { return gmallocn(count, size, true); } inline void *gmallocn3(int width, int height, int size, bool checkoverflow = false) { if (width == 0 || height == 0) { return nullptr; } int count; int bytes; if (width < 0 || height < 0 || size <= 0 || checkedMultiply(width, height, &count) || checkedMultiply(count, size, &bytes)) { std::fputs("Bogus memory allocation size\n", stderr); if (checkoverflow) { return nullptr; } std::abort(); } return gmalloc(bytes, checkoverflow); } inline void *greallocn(void *p, int count, int size, bool checkoverflow = false, bool free_p = true) { if (count == 0) { if (free_p) { gfree(p); } return nullptr; } int bytes; if (count < 0 || size <= 0 || checkedMultiply(count, size, &bytes)) { std::fputs("Bogus memory allocation size\n", stderr); if (checkoverflow) { if (free_p) { gfree(p); } return nullptr; } std::abort(); } assert(bytes > 0); if (void *q = grealloc(p, bytes, checkoverflow)) { return q; } if (free_p) { gfree(p); } return nullptr; } inline void *greallocn_checkoverflow(void *p, int count, int size) { return greallocn(p, count, size, true); } /// Allocate memory and copy a string into it. inline char *copyString(const char *s) { char *r = static_cast(gmalloc(std::strlen(s) + 1, false)); return std::strcpy(r, s); } /// Allocate memory and copy a limited-length string to it. inline char *copyString(const char *s, size_t n) { char *r = static_cast(gmalloc(n + 1, false)); r[n] = '\0'; return std::strncpy(r, s, n); } #endif // GMEM_H poppler-24.02.0/goo/grandom.cc000066400000000000000000000017631455701731300160770ustar00rootroot00000000000000/* * grandom.cc * * This file is licensed under the GPLv2 or later * * Pseudo-random number generation * * Copyright (C) 2012 Fabio D'Urso * Copyright (C) 2018 Adam Reichold * Copyright (C) 2022 Albert Astals Cid */ #include "grandom.h" #include namespace { auto &grandom_engine() { static thread_local std::default_random_engine engine { std::random_device {}() }; return engine; } } void grandom_fill(unsigned char *buff, int size) { auto &engine = grandom_engine(); std::uniform_int_distribution distribution { std::numeric_limits::min(), std::numeric_limits::max() }; for (int index = 0; index < size; ++index) { buff[index] = static_cast(distribution(engine)); } } double grandom_double() { auto &engine = grandom_engine(); return std::generate_canonical::digits>(engine); } poppler-24.02.0/goo/grandom.h000066400000000000000000000006711455701731300157360ustar00rootroot00000000000000/* * grandom.h * * This file is licensed under the GPLv2 or later * * Pseudo-random number generation * * Copyright (C) 2012 Fabio D'Urso * Copyright (C) 2018 Adam Reichold */ #ifndef GRANDOM_H #define GRANDOM_H /// Fills the given buffer with random bytes void grandom_fill(unsigned char *buff, int size); /// Returns a random number in [0,1) double grandom_double(); #endif poppler-24.02.0/goo/gstrtod.cc000066400000000000000000000100761455701731300161330ustar00rootroot00000000000000/* This file is part of Libspectre. * * Copyright (C) 2007, 2012 Albert Astals Cid * Copyright (C) 2007 Carlos Garcia Campos * * Libspectre 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. * * Libspectre 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. */ /* This function comes from spectre-utils from libspectre */ #include "gstrtod.h" #include #include #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') double gatof(const char *nptr) { return gstrtod(nptr, nullptr); } double gstrtod(const char *nptr, char **endptr) { char *fail_pos; double val; struct lconv *locale_data; const char *decimal_point; int decimal_point_len; const char *p, *decimal_point_pos; const char *end = nullptr; /* Silence gcc */ int strtod_errno; fail_pos = nullptr; locale_data = localeconv(); decimal_point = locale_data->decimal_point; decimal_point_len = strlen(decimal_point); decimal_point_pos = nullptr; end = nullptr; if (decimal_point[0] != '.' || decimal_point[1] != 0) { p = nptr; /* Skip leading space */ while (ascii_isspace(*p)) { p++; } /* Skip leading optional sign */ if (*p == '+' || *p == '-') { p++; } if (ascii_isdigit(*p) || *p == '.') { while (ascii_isdigit(*p)) { p++; } if (*p == '.') { decimal_point_pos = p++; } while (ascii_isdigit(*p)) { p++; } if (*p == 'e' || *p == 'E') { p++; } if (*p == '+' || *p == '-') { p++; } while (ascii_isdigit(*p)) { p++; } end = p; } /* For the other cases, we need not convert the decimal point */ } if (decimal_point_pos) { char *copy, *c; /* We need to convert the '.' to the locale specific decimal point */ copy = (char *)malloc(end - nptr + 1 + decimal_point_len); c = copy; memcpy(c, nptr, decimal_point_pos - nptr); c += decimal_point_pos - nptr; memcpy(c, decimal_point, decimal_point_len); c += decimal_point_len; memcpy(c, decimal_point_pos + 1, end - (decimal_point_pos + 1)); c += end - (decimal_point_pos + 1); *c = 0; errno = 0; val = strtod(copy, &fail_pos); strtod_errno = errno; if (fail_pos) { if (fail_pos - copy > decimal_point_pos - nptr) { fail_pos = (char *)nptr + (fail_pos - copy) - (decimal_point_len - 1); } else { fail_pos = (char *)nptr + (fail_pos - copy); } } free(copy); } else if (end) { char *copy; copy = (char *)malloc(end - (char *)nptr + 1); memcpy(copy, nptr, end - nptr); *(copy + (end - (char *)nptr)) = 0; errno = 0; val = strtod(copy, &fail_pos); strtod_errno = errno; if (fail_pos) { fail_pos = (char *)nptr + (fail_pos - copy); } free(copy); } else { errno = 0; val = strtod(nptr, &fail_pos); strtod_errno = errno; } if (endptr) { *endptr = fail_pos; } errno = strtod_errno; return val; } poppler-24.02.0/goo/gstrtod.h000066400000000000000000000027411455701731300157750ustar00rootroot00000000000000/* This file is part of Libspectre. * * Copyright (C) 2007 Albert Astals Cid * Copyright (C) 2007 Carlos Garcia Campos * * Libspectre 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. * * Libspectre 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. */ /* This function comes from spectre-utils from libspectre */ #ifndef GSTRTOD_H #define GSTRTOD_H #include "poppler_private_export.h" #ifdef __cplusplus extern "C" { #endif /* This function behaves like the standard atof()/(strtod() function * does in the C locale. It does this without actually changing * the current locale, since that would not be thread-safe. * A limitation of the implementation is that this function * will still accept localized versions of infinities and NANs. */ double POPPLER_PRIVATE_EXPORT gatof(const char *nptr); double gstrtod(const char *nptr, char **endptr); #ifdef __cplusplus } #endif #endif poppler-24.02.0/gtkdoc.py000066400000000000000000000456611455701731300152070ustar00rootroot00000000000000# Copyright (C) 2011 Igalia S.L. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library 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 # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from __future__ import absolute_import, division, print_function import errno import logging import os import os.path import subprocess import sys PY2 = sys.version_info[0] == 2 if PY2: input = raw_input class GTKDoc(object): """Class that controls a gtkdoc run. Each instance of this class represents one gtkdoc configuration and set of documentation. The gtkdoc package is a series of tools run consecutively which converts inline C/C++ documentation into docbook files and then into HTML. This class is suitable for generating documentation or simply verifying correctness. Keyword arguments: output_dir -- The path where gtkdoc output should be placed. Generation may overwrite file in this directory. Required. module_name -- The name of the documentation module. For libraries this is typically the library name. Required if not library path is given. source_dirs -- A list of paths to directories of source code to be scanned. Required if headers is not specified. ignored_files -- A list of filenames to ignore in the source directory. It is only necessary to provide the basenames of these files. Typically it is important to provide an updated list of ignored files to prevent warnings about undocumented symbols. headers -- A list of paths to headers to be scanned. Required if source_dirs is not specified. namespace -- The library namespace. decorator -- If a decorator is used to unhide certain symbols in header files this parameter is required for successful scanning. (default '') deprecation_guard -- gtkdoc tries to ensure that symbols marked as deprecated are encased in this C preprocessor define. This is required to avoid gtkdoc warnings. (default '') cflags -- This parameter specifies any preprocessor flags necessary for building the scanner binary during gtkdoc-scanobj. Typically this includes all absolute include paths necessary to resolve all header dependencies. (default '') ldflags -- This parameter specifies any linker flags necessary for building the scanner binary during gtkdoc-scanobj. Typically this includes "-lyourlibraryname". (default '') library_path -- This parameter specifies the path to the directory where you library resides used for building the scanner binary during gtkdoc-scanobj. (default '') doc_dir -- The path to other documentation files necessary to build the documentation. This files in this directory as well as the files in the 'html' subdirectory will be copied recursively into the output directory. (default '') main_sgml_file -- The path or name (if a doc_dir is given) of the SGML file that is the considered the main page of your documentation. (default: -docs.sgml) version -- The version number of the module. If this is provided, a version.xml file containing the version will be created in the output directory during documentation generation. interactive -- Whether or not errors or warnings should prompt the user to continue or not. When this value is false, generation will continue despite warnings. (default False) virtual_root -- A temporary installation directory which is used as the root where the actual installation prefix lives; this is mostly useful for packagers, and should be set to what is given to make install as DESTDIR. """ def __init__(self, args): # Parameters specific to scanning. self.module_name = '' self.source_dirs = [] self.headers = [] self.ignored_files = [] self.namespace = '' self.decorator = '' self.deprecation_guard = '' # Parameters specific to gtkdoc-scanobj. self.cflags = '' self.ldflags = '' self.library_path = '' # Parameters specific to generation. self.output_dir = '' self.doc_dir = '' self.main_sgml_file = '' # Parameters specific to gtkdoc-fixxref. self.cross_reference_deps = [] self.interactive = False self.logger = logging.getLogger('gtkdoc') for key, value in iter(args.items()): setattr(self, key, value) if not getattr(self, 'output_dir'): raise Exception('output_dir not specified.') if not getattr(self, 'module_name'): raise Exception('module_name not specified.') if not getattr(self, 'source_dirs') and not getattr(self, 'headers'): raise Exception('Neither source_dirs nor headers specified.' % key) # Make all paths absolute in case we were passed relative paths, since # we change the current working directory when executing subcommands. self.output_dir = os.path.abspath(self.output_dir) self.source_dirs = [os.path.abspath(x) for x in self.source_dirs] self.headers = [os.path.abspath(x) for x in self.headers] if self.library_path: self.library_path = os.path.abspath(self.library_path) if not self.main_sgml_file: self.main_sgml_file = self.module_name + "-docs.sgml" def generate(self, html=True): self.saw_warnings = False self._copy_doc_files_to_output_dir(html) self._write_version_xml() self._run_gtkdoc_scan() self._run_gtkdoc_scangobj() self._run_gtkdoc_mkdb() if not html: return self._run_gtkdoc_mkhtml() self._run_gtkdoc_fixxref() def _delete_file_if_exists(self, path): if not os.access(path, os.F_OK | os.R_OK): return self.logger.debug('deleting %s', path) os.unlink(path) def _create_directory_if_nonexistent(self, path): try: os.makedirs(path) except OSError as error: if error.errno != errno.EEXIST: raise def _raise_exception_if_file_inaccessible(self, path): if not os.path.exists(path) or not os.access(path, os.R_OK): raise Exception("Could not access file at: %s" % path) def _output_has_warnings(self, outputs): for output in outputs: if output and output.find('warning'): return True return False def _ask_yes_or_no_question(self, question): if not self.interactive: return True question += ' [y/N] ' answer = None while answer != 'y' and answer != 'n' and answer != '': answer = input(question).lower() return answer == 'y' def _run_command(self, args, env=None, cwd=None, print_output=True, ignore_warnings=False): if print_output: self.logger.debug("Running %s", args[0]) self.logger.debug("Full command args: %s", str(args)) process = subprocess.Popen(args, env=env, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = [b.decode("utf-8") for b in process.communicate()] if print_output: if stdout: if PY2: try: sys.stdout.write(stdout.encode("utf-8")) except UnicodeDecodeError: sys.stdout.write(stdout) else: sys.stdout.write(stdout) if stderr: if PY2: try: sys.stderr.write(stderr.encode("utf-8")) except UnicodeDecodeError: sys.stderr.write(stderr) else: sys.stderr.write(stderr) if process.returncode != 0: raise Exception('%s produced a non-zero return code %i' % (args[0], process.returncode)) if not ignore_warnings and ('warning' in stderr or 'warning' in stdout): self.saw_warnings = True if not self._ask_yes_or_no_question('%s produced warnings, ' 'try to continue?' % args[0]): raise Exception('%s step failed' % args[0]) return stdout.strip() def _copy_doc_files_to_output_dir(self, html=True): if not self.doc_dir: self.logger.info('Not copying any files from doc directory,' ' because no doc directory given.') return def copy_file_replacing_existing(src, dest): if os.path.isdir(src): self.logger.debug('skipped directory %s', src) return if not os.access(src, os.F_OK | os.R_OK): self.logger.debug('skipped unreadable %s', src) return self._delete_file_if_exists(dest) self.logger.debug('created %s', dest) try: os.link(src, dest) except OSError: os.symlink(src, dest) def copy_all_files_in_directory(src, dest): for path in os.listdir(src): copy_file_replacing_existing(os.path.join(src, path), os.path.join(dest, path)) self.logger.debug('Copying template files to output directory...') self._create_directory_if_nonexistent(self.output_dir) copy_all_files_in_directory(self.doc_dir, self.output_dir) if not html: return self.logger.debug('Copying HTML files to output directory...') html_src_dir = os.path.join(self.doc_dir, 'html') html_dest_dir = os.path.join(self.output_dir, 'html') self._create_directory_if_nonexistent(html_dest_dir) if os.path.exists(html_src_dir): copy_all_files_in_directory(html_src_dir, html_dest_dir) def _write_version_xml(self): if not self.version: self.logger.info('No version specified, so not writing version.xml') return version_xml_path = os.path.join(self.output_dir, 'version.xml') src_version_xml_path = os.path.join(self.doc_dir, 'version.xml') # Don't overwrite version.xml if it was in the doc directory. if os.path.exists(version_xml_path) and \ os.path.exists(src_version_xml_path): return output_file = open(version_xml_path, 'w') output_file.write(self.version) output_file.close() def _ignored_files_basenames(self): return ' '.join([os.path.basename(x) for x in self.ignored_files]) def _run_gtkdoc_scan(self): args = ['gtkdoc-scan', '--module=%s' % self.module_name, '--rebuild-types'] if not self.headers: # Each source directory should be have its own "--source-dir=" prefix. args.extend(['--source-dir=%s' % path for path in self.source_dirs]) if self.decorator: args.append('--ignore-decorators=%s' % self.decorator) if self.deprecation_guard: args.append('--deprecated-guards=%s' % self.deprecation_guard) if self.output_dir: args.append('--output-dir=%s' % self.output_dir) # We only need to pass the list of ignored files if the we are not using an explicit list of headers. if not self.headers: # gtkdoc-scan wants the basenames of ignored headers, so strip the # dirname. Different from "--source-dir", the headers should be # specified as one long string. ignored_files_basenames = self._ignored_files_basenames() if ignored_files_basenames: args.append('--ignore-headers=%s' % ignored_files_basenames) if self.headers: args.extend(self.headers) self._run_command(args) def _run_gtkdoc_scangobj(self): env = os.environ ldflags = self.ldflags if self.library_path: additional_ldflags = '' for arg in env.get('LDFLAGS', '').split(' '): if arg.startswith('-L'): additional_ldflags = '%s %s' % (additional_ldflags, arg) ldflags = ' "-L%s" %s ' % (self.library_path, additional_ldflags) + ldflags current_ld_library_path = env.get('LD_LIBRARY_PATH') if current_ld_library_path: env['LD_LIBRARY_PATH'] = '%s:%s' % (self.library_path, current_ld_library_path) else: env['LD_LIBRARY_PATH'] = self.library_path if ldflags: env['LDFLAGS'] = '%s %s' % (ldflags, env.get('LDFLAGS', '')) if self.cflags: env['CFLAGS'] = '%s %s' % (self.cflags, env.get('CFLAGS', '')) if 'CFLAGS' in env: self.logger.debug('CFLAGS=%s', env['CFLAGS']) if 'LDFLAGS' in env: self.logger.debug('LDFLAGS %s', env['LDFLAGS']) if 'RUN' in env: self.logger.debug('RUN=%s', env['RUN']) self._run_command(['gtkdoc-scangobj', '--module=%s' % self.module_name], env=env, cwd=self.output_dir) def _run_gtkdoc_mkdb(self): sgml_file = os.path.join(self.output_dir, self.main_sgml_file) self._raise_exception_if_file_inaccessible(sgml_file) args = ['gtkdoc-mkdb', '--module=%s' % self.module_name, '--main-sgml-file=%s' % sgml_file, '--source-suffixes=h,c,cpp,cc', '--output-format=xml', '--sgml-mode'] if self.namespace: args.append('--name-space=%s' % self.namespace) ignored_files_basenames = self._ignored_files_basenames() if ignored_files_basenames: args.append('--ignore-files=%s' % ignored_files_basenames) # Each directory should be have its own "--source-dir=" prefix. args.extend(['--source-dir=%s' % path for path in self.source_dirs]) self._run_command(args, cwd=self.output_dir) def _run_gtkdoc_mkhtml(self): html_dest_dir = os.path.join(self.output_dir, 'html') if not os.path.isdir(html_dest_dir): raise Exception("%s is not a directory, could not generate HTML" % html_dest_dir) elif not os.access(html_dest_dir, os.X_OK | os.R_OK | os.W_OK): raise Exception("Could not access %s to generate HTML" % html_dest_dir) # gtkdoc-mkhtml expects the SGML path to be absolute. sgml_file = os.path.join(os.path.abspath(self.output_dir), self.main_sgml_file) self._raise_exception_if_file_inaccessible(sgml_file) self._run_command(['gtkdoc-mkhtml', self.module_name, sgml_file], cwd=html_dest_dir) def _run_gtkdoc_fixxref(self): args = ['gtkdoc-fixxref', '--module=%s' % self.module_name, '--module-dir=html', '--html-dir=html'] args.extend(['--extra-dir=%s' % extra_dir for extra_dir in self.cross_reference_deps]) self._run_command(args, cwd=self.output_dir, ignore_warnings=True) def rebase_installed_docs(self): if not os.path.isdir(self.output_dir): raise Exception("Tried to rebase documentation before generating it.") html_dir = os.path.join(self.virtual_root + self.prefix, 'share', 'gtk-doc', 'html', self.module_name) if not os.path.isdir(html_dir): return args = ['gtkdoc-rebase', '--relative', '--html-dir=%s' % html_dir] args.extend(['--other-dir=%s' % extra_dir for extra_dir in self.cross_reference_deps]) if self.virtual_root: args.extend(['--dest-dir=%s' % self.virtual_root]) self._run_command(args, cwd=self.output_dir) def api_missing_documentation(self): unused_doc_file = os.path.join(self.output_dir, self.module_name + "-unused.txt") if not os.path.exists(unused_doc_file) or not os.access(unused_doc_file, os.R_OK): return [] return open(unused_doc_file).read().splitlines() class PkgConfigGTKDoc(GTKDoc): """Class reads a library's pkgconfig file to guess gtkdoc parameters. Some gtkdoc parameters can be guessed by reading a library's pkgconfig file, including the cflags, ldflags and version parameters. If you provide these parameters as well, they will be appended to the ones guessed via the pkgconfig file. Keyword arguments: pkg_config_path -- Path to the pkgconfig file for the library. Required. """ def __init__(self, pkg_config_path, args): super(PkgConfigGTKDoc, self).__init__(args) pkg_config = os.environ.get('PKG_CONFIG', 'pkg-config') if not os.path.exists(pkg_config_path): raise Exception('Could not find pkg-config file at: %s' % pkg_config_path) self.cflags += " " + self._run_command([pkg_config, pkg_config_path, '--cflags'], print_output=False) self.ldflags += " " + self._run_command([pkg_config, pkg_config_path, '--libs'], print_output=False) self.version = self._run_command([pkg_config, pkg_config_path, '--modversion'], print_output=False) self.prefix = self._run_command([pkg_config, pkg_config_path, '--variable=prefix'], print_output=False) poppler-24.02.0/hooks/000077500000000000000000000000001455701731300144715ustar00rootroot00000000000000poppler-24.02.0/hooks/pre-commit000077500000000000000000000005411455701731300164730ustar00rootroot00000000000000#!/usr/bin/env bash readonly output=$(git clang-format -v --diff) if [[ "$output" == *"no modified files to format"* ]]; then exit 0; fi if [[ "$output" == *"clang-format did not modify any files"* ]]; then exit 0; fi echo "ERROR: you need to run git clang-format on your commit" echo " git clang-format -f is potentially what you want" exit 1 poppler-24.02.0/make-glib-api-docs000077500000000000000000000055431455701731300166300ustar00rootroot00000000000000#!/usr/bin/env python3 # # Copyright (C) 2017 Carlos Garcia Campos # Copyright (C) 2019 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 # the Free Software Foundation; either version 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import argparse import logging import os from gtkdoc import PkgConfigGTKDoc def configure_logging(verbose): level = logging.DEBUG if verbose else logging.INFO logger = logging.getLogger('gtkdoc') logger.setLevel(level) handler = logging.StreamHandler() handler.setLevel(level) logger.addHandler(handler) if level == logging.DEBUG: handler.setFormatter(logging.Formatter('[%(asctime)s] %(message)s')) else: handler.setFormatter(logging.Formatter('%(message)s')) parser = argparse.ArgumentParser(description='Make poppler GLib API documentation.') parser.add_argument('-v', '--verbose', action='store_true', default = False, help='Whether or not to run in verbose mode.') parser.add_argument('--skip-html', action='store_true', help='Whether or not to skip HTML generation, which can be slow.') parser.add_argument('-s', '--src-dir', action='store', default='.', dest='src_dir', help='The source directory') parser.add_argument('-b', '--build-dir', action='store', default='build', dest='build_dir', help='The build directory') args = parser.parse_args() configure_logging(args.verbose) pkgconfig_file = os.path.join(args.build_dir, 'poppler-glib.pc') pkgconfig_path = os.environ.get("PKG_CONFIG_PATH") os.environ['PKG_CONFIG_PATH'] = args.build_dir if pkgconfig_path: os.environ['PKG_CONFIG_PATH'] += ':' + pkgconfig_path gtkdoc = PkgConfigGTKDoc(pkgconfig_file, { 'library_path': os.path.join(args.build_dir, 'glib'), 'module_name': 'poppler', 'doc_dir': os.path.join(args.src_dir, 'glib', 'reference'), 'output_dir': os.path.join(args.build_dir, 'glib', 'reference'), 'main_sgml_file': 'poppler-docs.sgml', 'source_dirs': [os.path.join(args.src_dir, 'glib'), os.path.join(args.build_dir, 'glib')], 'cflags': '-I%s' % os.path.join(args.src_dir, 'glib'), 'ignored_files': ['poppler-private.h', 'poppler-input-stream.h', 'poppler-cached-file-loader.h', 'demo'] }) gtkdoc.generate(not args.skip_html) poppler-24.02.0/poppler-cpp.pc.cmake000066400000000000000000000005101455701731300172060ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ libdir=@CMAKE_INSTALL_FULL_LIBDIR@ includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ Name: poppler-cpp Description: cpp backend for Poppler PDF rendering library Version: @POPPLER_VERSION@ Requires: @PC_REQUIRES@ @PC_REQUIRES_PRIVATE@ Libs: -L${libdir} -lpoppler-cpp Cflags: -I${includedir}/poppler/cpp poppler-24.02.0/poppler-glib.pc.cmake000066400000000000000000000006121455701731300173440ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ libdir=@CMAKE_INSTALL_FULL_LIBDIR@ includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ Name: poppler-glib Description: GLib wrapper for poppler Version: @POPPLER_VERSION@ Requires: glib-2.0 >= @GLIB_REQUIRED@ gobject-2.0 >= @GLIB_REQUIRED@ cairo >= @CAIRO_VERSION@ @PC_REQUIRES@ @PC_REQUIRES_PRIVATE@ Libs: -L${libdir} -lpoppler-glib Cflags: -I${includedir}/poppler/glib poppler-24.02.0/poppler-qt5.pc.cmake000066400000000000000000000004631455701731300171440ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ libdir=@CMAKE_INSTALL_FULL_LIBDIR@ includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ Name: poppler-qt5 Description: Qt5 bindings for poppler Version: @POPPLER_VERSION@ Requires: @PC_REQUIRES@ @PC_REQUIRES_PRIVATE@ Libs: -L${libdir} -lpoppler-qt5 Cflags: -I${includedir}/poppler/qt5 poppler-24.02.0/poppler-qt6.pc.cmake000066400000000000000000000004631455701731300171450ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ libdir=@CMAKE_INSTALL_FULL_LIBDIR@ includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ Name: poppler-qt6 Description: Qt6 bindings for poppler Version: @POPPLER_VERSION@ Requires: @PC_REQUIRES@ @PC_REQUIRES_PRIVATE@ Libs: -L${libdir} -lpoppler-qt6 Cflags: -I${includedir}/poppler/qt6 poppler-24.02.0/poppler.pc.cmake000066400000000000000000000003661455701731300164370ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ libdir=@CMAKE_INSTALL_FULL_LIBDIR@ includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ Name: poppler Description: PDF rendering library Version: @POPPLER_VERSION@ Libs: -L${libdir} -lpoppler Cflags: -I${includedir}/poppler poppler-24.02.0/poppler/000077500000000000000000000000001455701731300150275ustar00rootroot00000000000000poppler-24.02.0/poppler/.gitignore000066400000000000000000000001261455701731300170160ustar00rootroot00000000000000.cvsignore .deps .libs Makefile Makefile.in *.la *.lo *.loT poppler-config.h stamp-h2 poppler-24.02.0/poppler/Annot.cc000066400000000000000000012141731455701731300164260ustar00rootroot00000000000000//======================================================================== // // Annot.cc // // Copyright 2000-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 Scott Turner // Copyright (C) 2007, 2008 Julien Rebetez // 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 // Copyright (C) 2008, 2011 Pino Toscano // Copyright (C) 2008 Michael Vrable // Copyright (C) 2008 Hugo Mercier // Copyright (C) 2009 Ilya Gorenbein // Copyright (C) 2011, 2013, 2019 José Aliste // Copyright (C) 2012, 2013 Fabio D'Urso // Copyright (C) 2012, 2013 Thomas Freitag // Copyright (C) 2012, 2015 Tobias Koenig // Copyright (C) 2013 Peter Breitenlohner // Copyright (C) 2013, 2017 Adrian Johnson // Copyright (C) 2014, 2015 Marek Kasik // Copyright (C) 2014 Jiri Slaby // Copyright (C) 2014 Anuj Khare // Copyright (C) 2015 Petr Gajdos // Copyright (C) 2015 Philipp Reinkemeier // Copyright (C) 2015 Tamas Szekeres // Copyright (C) 2017 Hans-Ulrich Jüttner // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright 2018 Andre Heinecke // 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) 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 Katarina Behrens // Copyright (C) 2020 Thorsten Behrens // Copyright (C) 2020 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) 2023 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 // //======================================================================== #include #include #include #include #include "goo/gmem.h" #include "goo/gstrtod.h" #include "Error.h" #include "Object.h" #include "Catalog.h" #include "Gfx.h" #include "Lexer.h" #include "PDFDoc.h" #include "Page.h" #include "Annot.h" #include "GfxFont.h" #include "CharCodeToUnicode.h" #include "PDFDocEncoding.h" #include "Form.h" #include "Error.h" #include "XRef.h" #include "Movie.h" #include "OptionalContent.h" #include "Sound.h" #include "FileSpec.h" #include "DateInfo.h" #include "Link.h" #include #include #include "annot_stamp_approved.h" #include "annot_stamp_as_is.h" #include "annot_stamp_confidential.h" #include "annot_stamp_departmental.h" #include "annot_stamp_final.h" #include "annot_stamp_for_comment.h" #include "annot_stamp_experimental.h" #include "annot_stamp_expired.h" #include "annot_stamp_not_approved.h" #include "annot_stamp_not_for_public_release.h" #include "annot_stamp_sold.h" #include "annot_stamp_top_secret.h" #include "annot_stamp_for_public_release.h" #include "annot_stamp_draft.h" #ifndef M_PI # define M_PI 3.14159265358979323846 #endif #define fieldFlagReadOnly 0x00000001 #define fieldFlagRequired 0x00000002 #define fieldFlagNoExport 0x00000004 #define fieldFlagMultiline 0x00001000 #define fieldFlagPassword 0x00002000 #define fieldFlagNoToggleToOff 0x00004000 #define fieldFlagRadio 0x00008000 #define fieldFlagPushbutton 0x00010000 #define fieldFlagCombo 0x00020000 #define fieldFlagEdit 0x00040000 #define fieldFlagSort 0x00080000 #define fieldFlagFileSelect 0x00100000 #define fieldFlagMultiSelect 0x00200000 #define fieldFlagDoNotSpellCheck 0x00400000 #define fieldFlagDoNotScroll 0x00800000 #define fieldFlagComb 0x01000000 #define fieldFlagRichText 0x02000000 #define fieldFlagRadiosInUnison 0x02000000 #define fieldFlagCommitOnSelChange 0x04000000 // distance of Bezier control point from center for circle approximation // = (4 * (sqrt(2) - 1) / 3) * r #define bezierCircle 0.55228475 static AnnotLineEndingStyle parseAnnotLineEndingStyle(const GooString *string) { if (string != nullptr) { if (!string->cmp("Square")) { return annotLineEndingSquare; } else if (!string->cmp("Circle")) { return annotLineEndingCircle; } else if (!string->cmp("Diamond")) { return annotLineEndingDiamond; } else if (!string->cmp("OpenArrow")) { return annotLineEndingOpenArrow; } else if (!string->cmp("ClosedArrow")) { return annotLineEndingClosedArrow; } else if (!string->cmp("Butt")) { return annotLineEndingButt; } else if (!string->cmp("ROpenArrow")) { return annotLineEndingROpenArrow; } else if (!string->cmp("RClosedArrow")) { return annotLineEndingRClosedArrow; } else if (!string->cmp("Slash")) { return annotLineEndingSlash; } else { return annotLineEndingNone; } } else { return annotLineEndingNone; } } static const char *convertAnnotLineEndingStyle(AnnotLineEndingStyle style) { switch (style) { case annotLineEndingSquare: return "Square"; case annotLineEndingCircle: return "Circle"; case annotLineEndingDiamond: return "Diamond"; case annotLineEndingOpenArrow: return "OpenArrow"; case annotLineEndingClosedArrow: return "ClosedArrow"; case annotLineEndingButt: return "Butt"; case annotLineEndingROpenArrow: return "ROpenArrow"; case annotLineEndingRClosedArrow: return "RClosedArrow"; case annotLineEndingSlash: return "Slash"; default: return "None"; } } static AnnotExternalDataType parseAnnotExternalData(Dict *dict) { AnnotExternalDataType type; Object obj1 = dict->lookup("Subtype"); if (obj1.isName()) { const char *typeName = obj1.getName(); if (!strcmp(typeName, "Markup3D")) { type = annotExternalDataMarkup3D; } else { type = annotExternalDataMarkupUnknown; } } else { type = annotExternalDataMarkupUnknown; } return type; } static std::unique_ptr parseDiffRectangle(Array *array, PDFRectangle *rect) { if (array->getLength() == 4) { // deltas const double dx1 = array->get(0).getNumWithDefaultValue(0); const double dy1 = array->get(1).getNumWithDefaultValue(0); const double dx2 = array->get(2).getNumWithDefaultValue(0); const double dy2 = array->get(3).getNumWithDefaultValue(0); // checking that the numbers are valid (i.e. >= 0), // and that applying the differences still give us a valid rect if (dx1 >= 0 && dy1 >= 0 && dx2 >= 0 && dy2 && (rect->x2 - rect->x1 - dx1 - dx2) >= 0 && (rect->y2 - rect->y1 - dy1 - dy2) >= 0) { auto newRect = std::make_unique(); newRect->x1 = rect->x1 + dx1; newRect->y1 = rect->y1 + dy1; newRect->x2 = rect->x2 - dx2; newRect->y2 = rect->y2 - dy2; return newRect; } } return nullptr; } static std::unique_ptr getAdditionalAction(Annot::AdditionalActionsType type, Object *additionalActions, PDFDoc *doc) { Object additionalActionsObject = additionalActions->fetch(doc->getXRef()); if (additionalActionsObject.isDict()) { const char *key = (type == Annot::actionCursorEntering ? "E" : type == Annot::actionCursorLeaving ? "X" : type == Annot::actionMousePressed ? "D" : type == Annot::actionMouseReleased ? "U" : type == Annot::actionFocusIn ? "Fo" : type == Annot::actionFocusOut ? "Bl" : type == Annot::actionPageOpening ? "PO" : type == Annot::actionPageClosing ? "PC" : type == Annot::actionPageVisible ? "PV" : type == Annot::actionPageInvisible ? "PI" : nullptr); Object actionObject = additionalActionsObject.dictLookup(key); if (actionObject.isDict()) { return LinkAction::parseAction(&actionObject, doc->getCatalog()->getBaseURI()); } } return nullptr; } static const char *getFormAdditionalActionKey(Annot::FormAdditionalActionsType type) { return (type == Annot::actionFieldModified ? "K" : type == Annot::actionFormatField ? "F" : type == Annot::actionValidateField ? "V" : type == Annot::actionCalculateField ? "C" : nullptr); } static const char *determineFallbackFont(const std::string &tok, const char *defaultFallback) { // TODO: adjust these based on other example PDFs. if (tok == "/ZaDb") { return "ZapfDingbats"; } else if (tok == "/Cour") { return "Courier"; } else if (tok == "/TiRo") { return "TimesNewRoman"; } else if (tok == "/Helvetica-Bold") { return "Helvetica-Bold"; } return defaultFallback; } //------------------------------------------------------------------------ // AnnotBorderEffect //------------------------------------------------------------------------ AnnotBorderEffect::AnnotBorderEffect(Dict *dict) { Object obj1; obj1 = dict->lookup("S"); if (obj1.isName()) { const char *effectName = obj1.getName(); if (!strcmp(effectName, "C")) { effectType = borderEffectCloudy; } else { effectType = borderEffectNoEffect; } } else { effectType = borderEffectNoEffect; } if (effectType == borderEffectCloudy) { intensity = dict->lookup("I").getNumWithDefaultValue(0); } else { intensity = 0; } } //------------------------------------------------------------------------ // AnnotPath //------------------------------------------------------------------------ AnnotPath::AnnotPath() = default; AnnotPath::AnnotPath(Array *array) { parsePathArray(array); } AnnotPath::AnnotPath(std::vector &&coordsA) { coords = std::move(coordsA); } AnnotPath::~AnnotPath() = default; double AnnotPath::getX(int coord) const { if (coord >= 0 && coord < getCoordsLength()) { return coords[coord].getX(); } return 0; } double AnnotPath::getY(int coord) const { if (coord >= 0 && coord < getCoordsLength()) { return coords[coord].getY(); } 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) { error(errSyntaxError, -1, "Bad Annot Path"); return; } const auto tempLength = array->getLength() / 2; std::vector tempCoords; tempCoords.reserve(tempLength); for (int i = 0; i < tempLength; i++) { double x = 0, y = 0; Object obj1 = array->get(i * 2); if (obj1.isNum()) { x = obj1.getNum(); } else { return; } obj1 = array->get((i * 2) + 1); if (obj1.isNum()) { y = obj1.getNum(); } else { return; } tempCoords.emplace_back(x, y); } coords = std::move(tempCoords); } //------------------------------------------------------------------------ // AnnotCalloutLine //------------------------------------------------------------------------ AnnotCalloutLine::AnnotCalloutLine(double x1, double y1, double x2, double y2) : coord1(x1, y1), coord2(x2, y2) { } AnnotCalloutLine::~AnnotCalloutLine() = default; //------------------------------------------------------------------------ // AnnotCalloutMultiLine //------------------------------------------------------------------------ AnnotCalloutMultiLine::AnnotCalloutMultiLine(double x1, double y1, double x2, double y2, double x3, double y3) : AnnotCalloutLine(x1, y1, x2, y2), coord3(x3, y3) { } AnnotCalloutMultiLine::~AnnotCalloutMultiLine() = default; //------------------------------------------------------------------------ // AnnotQuadrilateral //------------------------------------------------------------------------ AnnotQuadrilaterals::AnnotQuadrilaterals(Array *array, PDFRectangle *rect) { int arrayLength = array->getLength(); int quadsLength = 0; double quadArray[8]; // default values quadrilateralsLength = 0; if ((arrayLength % 8) == 0) { int i; quadsLength = arrayLength / 8; auto quads = std::make_unique(quadsLength); for (i = 0; i < quadsLength; i++) { for (int j = 0; j < 8; j++) { Object obj = array->get(i * 8 + j); if (obj.isNum()) { quadArray[j] = obj.getNum(); } else { error(errSyntaxError, -1, "Invalid QuadPoint in annot"); return; } } quads[i] = AnnotQuadrilateral(quadArray[0], quadArray[1], quadArray[2], quadArray[3], quadArray[4], quadArray[5], quadArray[6], quadArray[7]); } quadrilateralsLength = quadsLength; quadrilaterals = std::move(quads); } } AnnotQuadrilaterals::AnnotQuadrilaterals(std::unique_ptr &&quads, int quadsLength) { quadrilaterals = std::move(quads); quadrilateralsLength = quadsLength; } AnnotQuadrilaterals::~AnnotQuadrilaterals() = default; double AnnotQuadrilaterals::getX1(int quadrilateral) { if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength) { return quadrilaterals[quadrilateral].coord1.getX(); } return 0; } double AnnotQuadrilaterals::getY1(int quadrilateral) { if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength) { return quadrilaterals[quadrilateral].coord1.getY(); } return 0; } double AnnotQuadrilaterals::getX2(int quadrilateral) { if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength) { return quadrilaterals[quadrilateral].coord2.getX(); } return 0; } double AnnotQuadrilaterals::getY2(int quadrilateral) { if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength) { return quadrilaterals[quadrilateral].coord2.getY(); } return 0; } double AnnotQuadrilaterals::getX3(int quadrilateral) { if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength) { return quadrilaterals[quadrilateral].coord3.getX(); } return 0; } double AnnotQuadrilaterals::getY3(int quadrilateral) { if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength) { return quadrilaterals[quadrilateral].coord3.getY(); } return 0; } double AnnotQuadrilaterals::getX4(int quadrilateral) { if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength) { return quadrilaterals[quadrilateral].coord4.getX(); } return 0; } double AnnotQuadrilaterals::getY4(int quadrilateral) { if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength) { return quadrilaterals[quadrilateral].coord4.getY(); } return 0; } AnnotQuadrilaterals::AnnotQuadrilateral::AnnotQuadrilateral() = default; AnnotQuadrilaterals::AnnotQuadrilateral::AnnotQuadrilateral(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) : coord1(x1, y1), coord2(x2, y2), coord3(x3, y3), coord4(x4, y4) { } //------------------------------------------------------------------------ // AnnotBorder //------------------------------------------------------------------------ AnnotBorder::AnnotBorder() { width = 1; style = borderSolid; } bool AnnotBorder::parseDashArray(Object *dashObj) { bool correct = true; const int tempLength = dashObj->arrayGetLength(); std::vector tempDash(tempLength); // TODO: check not all zero (Line Dash Pattern Page 217 PDF 8.1) for (int i = 0; i < tempLength && i < DASH_LIMIT && correct; i++) { const Object obj1 = dashObj->arrayGet(i); if (obj1.isNum()) { tempDash[i] = obj1.getNum(); correct = tempDash[i] >= 0; } else { correct = false; } } if (correct) { dash = std::move(tempDash); style = borderDashed; } return correct; } AnnotBorder::~AnnotBorder() = default; //------------------------------------------------------------------------ // AnnotBorderArray //------------------------------------------------------------------------ AnnotBorderArray::AnnotBorderArray() { horizontalCorner = 0; verticalCorner = 0; } AnnotBorderArray::AnnotBorderArray(Array *array) { Object obj1; int arrayLength = array->getLength(); bool correct = true; if (arrayLength == 3 || arrayLength == 4) { // implementation note 81 in Appendix H. obj1 = array->get(0); if (obj1.isNum()) { horizontalCorner = obj1.getNum(); } else { correct = false; } obj1 = array->get(1); if (obj1.isNum()) { verticalCorner = obj1.getNum(); } else { correct = false; } obj1 = array->get(2); if (obj1.isNum()) { width = obj1.getNum(); } else { correct = false; } if (arrayLength == 4) { obj1 = array->get(3); if (obj1.isArray()) { correct = parseDashArray(&obj1); } else { correct = false; } } } else { correct = false; } if (!correct) { width = 0; } } std::unique_ptr AnnotBorderArray::copy() const { AnnotBorderArray *res = new AnnotBorderArray(); res->type = type; res->width = width; res->dash = dash; res->style = style; res->horizontalCorner = horizontalCorner; res->verticalCorner = verticalCorner; return std::unique_ptr(res); } Object AnnotBorderArray::writeToObject(XRef *xref) const { Array *borderArray = new Array(xref); borderArray->add(Object(horizontalCorner)); borderArray->add(Object(verticalCorner)); borderArray->add(Object(width)); if (dash.size() > 0) { Array *a = new Array(xref); for (double d : dash) { a->add(Object(d)); } borderArray->add(Object(a)); } return Object(borderArray); } //------------------------------------------------------------------------ // AnnotBorderBS //------------------------------------------------------------------------ AnnotBorderBS::AnnotBorderBS() { } AnnotBorderBS::AnnotBorderBS(Dict *dict) { // Border width (in points) Object obj1 = dict->lookup("W"); width = obj1.getNumWithDefaultValue(1.0); // Border style obj1 = dict->lookup("S"); if (obj1.isName()) { const char *styleName = obj1.getName(); if (!strcmp(styleName, "S")) { style = borderSolid; } else if (!strcmp(styleName, "D")) { style = borderDashed; } else if (!strcmp(styleName, "B")) { style = borderBeveled; } else if (!strcmp(styleName, "I")) { style = borderInset; } else if (!strcmp(styleName, "U")) { style = borderUnderlined; } else { style = borderSolid; } } else { style = borderSolid; } // Border dash style if (style == borderDashed) { obj1 = dict->lookup("D"); if (!obj1.isArray() || !parseDashArray(&obj1)) { dash = { 3 }; } } } const char *AnnotBorderBS::getStyleName() const { switch (style) { case borderSolid: return "S"; case borderDashed: return "D"; case borderBeveled: return "B"; case borderInset: return "I"; case borderUnderlined: return "U"; } return "S"; } std::unique_ptr AnnotBorderBS::copy() const { AnnotBorderBS *res = new AnnotBorderBS(); res->type = type; res->width = width; res->dash = dash; res->style = style; return std::unique_ptr(res); } 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) { Array *a = new Array(xref); for (double d : dash) { a->add(Object(d)); } dict->set("D", Object(a)); } return Object(dict); } //------------------------------------------------------------------------ // AnnotColor //------------------------------------------------------------------------ AnnotColor::AnnotColor() { length = 0; } AnnotColor::AnnotColor(double gray) { length = 1; values[0] = gray; } AnnotColor::AnnotColor(double r, double g, double b) { length = 3; values[0] = r; values[1] = g; values[2] = b; } AnnotColor::AnnotColor(double c, double m, double y, double k) { length = 4; values[0] = c; values[1] = m; values[2] = y; values[3] = k; } // If is +1, color is brightened; // if is -1, color is darkened; // otherwise color is not modified. AnnotColor::AnnotColor(Array *array, int adjust) { int i; length = array->getLength(); if (length > 4) { length = 4; } for (i = 0; i < length; i++) { Object obj1 = array->get(i); if (obj1.isNum()) { values[i] = obj1.getNum(); if (values[i] < 0 || values[i] > 1) { values[i] = 0; } } else { values[i] = 0; } } if (adjust != 0) { adjustColor(adjust); } } void AnnotColor::adjustColor(int adjust) { int i; if (length == 4) { adjust = -adjust; } if (adjust > 0) { for (i = 0; i < length; ++i) { values[i] = 0.5 * values[i] + 0.5; } } else if (adjust < 0) { for (i = 0; i < length; ++i) { values[i] = 0.5 * values[i]; } } } Object AnnotColor::writeToObject(XRef *xref) const { if (length == 0) { return Object(objNull); // Transparent (no color) } else { Array *a = new Array(xref); for (int i = 0; i < length; ++i) { a->add(Object(values[i])); } return Object(a); } } //------------------------------------------------------------------------ // DefaultAppearance //------------------------------------------------------------------------ DefaultAppearance::DefaultAppearance(Object &&fontNameA, double fontPtSizeA, std::unique_ptr &&fontColorA) : fontName(std::move(fontNameA)), fontPtSize(fontPtSizeA), fontColor(std::move(fontColorA)) { } DefaultAppearance::DefaultAppearance(const GooString *da) { fontPtSize = -1; if (da) { std::vector daToks; int i = FormFieldText::tokenizeDA(da->toStr(), &daToks, "Tf"); if (i >= 1) { fontPtSize = gatof(daToks[i - 1].c_str()); } if (i >= 2) { // We are expecting a name, therefore the first letter should be '/'. const std::string &fontToken = daToks[i - 2]; if (fontToken.size() > 1 && fontToken[0] == '/') { // The +1 is here to skip the leading '/'. fontName = Object(objName, fontToken.c_str() + 1); } } // Scan backwards: we are looking for the last set value for (i = daToks.size() - 1; i >= 0; --i) { if (!fontColor) { if (daToks[i] == "g" && i >= 1) { fontColor = std::make_unique(gatof(daToks[i - 1].c_str())); } else if (daToks[i] == "rg" && i >= 3) { fontColor = std::make_unique(gatof(daToks[i - 3].c_str()), gatof(daToks[i - 2].c_str()), gatof(daToks[i - 1].c_str())); } else if (daToks[i] == "k" && i >= 4) { fontColor = std::make_unique(gatof(daToks[i - 4].c_str()), gatof(daToks[i - 3].c_str()), gatof(daToks[i - 2].c_str()), gatof(daToks[i - 1].c_str())); } } } } } void DefaultAppearance::setFontName(Object &&fontNameA) { fontName = std::move(fontNameA); } void DefaultAppearance::setFontPtSize(double fontPtSizeA) { fontPtSize = fontPtSizeA; } void DefaultAppearance::setFontColor(std::unique_ptr fontColorA) { fontColor = std::move(fontColorA); } std::string DefaultAppearance::toAppearanceString() const { AnnotAppearanceBuilder appearBuilder; if (fontColor) { appearBuilder.setDrawColor(fontColor.get(), true); } appearBuilder.setTextFont(fontName, fontPtSize); return appearBuilder.buffer()->toStr(); } //------------------------------------------------------------------------ // AnnotIconFit //------------------------------------------------------------------------ AnnotIconFit::AnnotIconFit(Dict *dict) { Object obj1; obj1 = dict->lookup("SW"); if (obj1.isName()) { const char *scaleName = obj1.getName(); if (!strcmp(scaleName, "B")) { scaleWhen = scaleBigger; } else if (!strcmp(scaleName, "S")) { scaleWhen = scaleSmaller; } else if (!strcmp(scaleName, "N")) { scaleWhen = scaleNever; } else { scaleWhen = scaleAlways; } } else { scaleWhen = scaleAlways; } obj1 = dict->lookup("S"); if (obj1.isName()) { const char *scaleName = obj1.getName(); if (!strcmp(scaleName, "A")) { scale = scaleAnamorphic; } else { scale = scaleProportional; } } else { scale = scaleProportional; } obj1 = dict->lookup("A"); if (obj1.isArray() && obj1.arrayGetLength() == 2) { left = obj1.arrayGet(0).getNumWithDefaultValue(0); bottom = obj1.arrayGet(1).getNumWithDefaultValue(0); if (left < 0 || left > 1) { left = 0.5; } if (bottom < 0 || bottom > 1) { bottom = 0.5; } } else { left = bottom = 0.5; } fullyBounds = dict->lookup("FB").getBoolWithDefaultValue(false); } //------------------------------------------------------------------------ // AnnotAppearance //------------------------------------------------------------------------ AnnotAppearance::AnnotAppearance(PDFDoc *docA, Object *dict) { assert(dict->isDict()); doc = docA; appearDict = dict->copy(); } AnnotAppearance::~AnnotAppearance() { } Object AnnotAppearance::getAppearanceStream(AnnotAppearanceType type, const char *state) { Object apData; // Obtain dictionary or stream associated to appearance type switch (type) { case appearRollover: apData = appearDict.dictLookupNF("R").copy(); if (apData.isNull()) { apData = appearDict.dictLookupNF("N").copy(); } break; case appearDown: apData = appearDict.dictLookupNF("D").copy(); if (apData.isNull()) { apData = appearDict.dictLookupNF("N").copy(); } break; case appearNormal: apData = appearDict.dictLookupNF("N").copy(); break; } if (apData.isDict() && state) { return apData.dictLookupNF(state).copy(); } else if (apData.isRef()) { return apData; } return Object(); } std::unique_ptr AnnotAppearance::getStateKey(int i) { const Object &obj1 = appearDict.dictLookupNF("N"); if (obj1.isDict()) { return std::make_unique(obj1.dictGetKey(i)); } return nullptr; } int AnnotAppearance::getNumStates() { int res = 0; const Object &obj1 = appearDict.dictLookupNF("N"); if (obj1.isDict()) { res = obj1.dictGetLength(); } return res; } // Test if stateObj (a Ref or a Dict) points to the specified stream bool AnnotAppearance::referencesStream(const Object *stateObj, Ref refToStream) { if (stateObj->isRef()) { const Ref r = stateObj->getRef(); if (r == refToStream) { return true; } } else if (stateObj->isDict()) { // Test each value const int size = stateObj->dictGetLength(); for (int i = 0; i < size; ++i) { const Object &obj1 = stateObj->dictGetValNF(i); if (obj1.isRef()) { const Ref r = obj1.getRef(); if (r == refToStream) { return true; } } } } return false; // Not found } // Test if this AnnotAppearance references the specified stream bool AnnotAppearance::referencesStream(Ref refToStream) { bool found; // Scan each state's ref/subdictionary const Object &objN = appearDict.dictLookupNF("N"); found = referencesStream(&objN, refToStream); if (found) { return true; } const Object &objR = appearDict.dictLookupNF("R"); found = referencesStream(&objR, refToStream); if (found) { return true; } const Object &objD = appearDict.dictLookupNF("D"); found = referencesStream(&objD, refToStream); return found; } // If this is the only annotation in the document that references the // specified appearance stream, remove the appearance stream void AnnotAppearance::removeStream(Ref refToStream) { const int lastpage = doc->getNumPages(); for (int pg = 1; pg <= lastpage; ++pg) { // Scan all annotations in the document Page *page = doc->getPage(pg); if (!page) { error(errSyntaxError, -1, "Failed check for shared annotation stream at page {0:d}", pg); continue; } Annots *annots = page->getAnnots(); for (Annot *annot : annots->getAnnots()) { AnnotAppearance *annotAp = annot->getAppearStreams(); if (annotAp && annotAp != this && annotAp->referencesStream(refToStream)) { return; // Another annotation points to the stream -> Don't delete it } } } // TODO: stream resources (e.g. font), AP name tree doc->getXRef()->removeIndirectObject(refToStream); } // Removes stream if obj is a Ref, or removes pointed streams if obj is a Dict void AnnotAppearance::removeStateStreams(const Object *state) { if (state->isRef()) { removeStream(state->getRef()); } else if (state->isDict()) { const int size = state->dictGetLength(); for (int i = 0; i < size; ++i) { const Object &obj2 = state->dictGetValNF(i); if (obj2.isRef()) { removeStream(obj2.getRef()); } } } } void AnnotAppearance::removeAllStreams() { const Object &objN = appearDict.dictLookupNF("N"); removeStateStreams(&objN); const Object &objR = appearDict.dictLookupNF("R"); removeStateStreams(&objR); const Object &objD = appearDict.dictLookupNF("D"); removeStateStreams(&objD); } //------------------------------------------------------------------------ // AnnotAppearanceCharacs //------------------------------------------------------------------------ AnnotAppearanceCharacs::AnnotAppearanceCharacs(Dict *dict) { Object obj1; if (!dict) { rotation = 0; position = captionNoIcon; return; } obj1 = dict->lookup("R"); if (obj1.isInt()) { rotation = obj1.getInt(); } else { rotation = 0; } obj1 = dict->lookup("BC"); if (obj1.isArray()) { Array *colorComponents = obj1.getArray(); if (colorComponents->getLength() > 0) { borderColor = std::make_unique(colorComponents); } } obj1 = dict->lookup("BG"); if (obj1.isArray()) { Array *colorComponents = obj1.getArray(); if (colorComponents->getLength() > 0) { backColor = std::make_unique(colorComponents); } } obj1 = dict->lookup("CA"); if (obj1.isString()) { normalCaption = std::make_unique(obj1.getString()); } obj1 = dict->lookup("RC"); if (obj1.isString()) { rolloverCaption = std::make_unique(obj1.getString()); } obj1 = dict->lookup("AC"); if (obj1.isString()) { alternateCaption = std::make_unique(obj1.getString()); } obj1 = dict->lookup("IF"); if (obj1.isDict()) { iconFit = std::make_unique(obj1.getDict()); } obj1 = dict->lookup("TP"); if (obj1.isInt()) { position = (AnnotAppearanceCharacsTextPos)obj1.getInt(); } else { position = captionNoIcon; } } AnnotAppearanceCharacs::~AnnotAppearanceCharacs() = default; std::unique_ptr AnnotAppearanceCharacs::copy() const { AnnotAppearanceCharacs *res = new AnnotAppearanceCharacs(nullptr); res->rotation = rotation; if (borderColor) { res->borderColor = std::make_unique(*borderColor); } if (backColor) { res->backColor = std::make_unique(*backColor); } if (normalCaption) { res->normalCaption = std::unique_ptr(normalCaption->copy()); } if (rolloverCaption) { res->rolloverCaption = std::unique_ptr(rolloverCaption->copy()); } if (alternateCaption) { res->alternateCaption = std::unique_ptr(alternateCaption->copy()); } if (iconFit) { res->iconFit = std::make_unique(*iconFit); } res->position = position; return std::unique_ptr(res); } //------------------------------------------------------------------------ // AnnotAppearanceBBox //------------------------------------------------------------------------ AnnotAppearanceBBox::AnnotAppearanceBBox(PDFRectangle *rect) { origX = rect->x1; origY = rect->y1; borderWidth = 0; // Initially set the same size as rect minX = 0; minY = 0; maxX = rect->x2 - rect->x1; maxY = rect->y2 - rect->y1; } void AnnotAppearanceBBox::extendTo(double x, double y) { if (x < minX) { minX = x; } else if (x > maxX) { maxX = x; } if (y < minY) { minY = y; } else if (y > maxY) { maxY = y; } } void AnnotAppearanceBBox::getBBoxRect(double bbox[4]) const { bbox[0] = minX - borderWidth; bbox[1] = minY - borderWidth; bbox[2] = maxX + borderWidth; bbox[3] = maxY + borderWidth; } double AnnotAppearanceBBox::getPageXMin() const { return origX + minX - borderWidth; } double AnnotAppearanceBBox::getPageYMin() const { return origY + minY - borderWidth; } double AnnotAppearanceBBox::getPageXMax() const { return origX + maxX + borderWidth; } double AnnotAppearanceBBox::getPageYMax() const { return origY + maxY + borderWidth; } //------------------------------------------------------------------------ // Annot //------------------------------------------------------------------------ #define annotLocker() const std::scoped_lock locker(mutex) Annot::Annot(PDFDoc *docA, PDFRectangle *rectA) { refCnt = 1; flags = flagUnknown; type = typeUnknown; Array *a = new Array(docA->getXRef()); a->add(Object(rectA->x1)); a->add(Object(rectA->y1)); a->add(Object(rectA->x2)); a->add(Object(rectA->y2)); annotObj = Object(new Dict(docA->getXRef())); annotObj.dictSet("Type", Object(objName, "Annot")); annotObj.dictSet("Rect", Object(a)); ref = docA->getXRef()->addIndirectObject(annotObj); initialize(docA, annotObj.getDict()); } Annot::Annot(PDFDoc *docA, Object &&dictObject) { refCnt = 1; hasRef = false; flags = flagUnknown; type = typeUnknown; annotObj = std::move(dictObject); initialize(docA, annotObj.getDict()); } Annot::Annot(PDFDoc *docA, Object &&dictObject, const Object *obj) { refCnt = 1; if (obj->isRef()) { hasRef = true; ref = obj->getRef(); } else { hasRef = false; } flags = flagUnknown; type = typeUnknown; annotObj = std::move(dictObject); initialize(docA, annotObj.getDict()); } void Annot::initialize(PDFDoc *docA, Dict *dict) { Object apObj, asObj, obj1; ok = true; doc = docA; appearance.setToNull(); //----- parse the rectangle rect = std::make_unique(); obj1 = dict->lookup("Rect"); if (obj1.isArray() && obj1.arrayGetLength() == 4) { rect->x1 = obj1.arrayGet(0).getNumWithDefaultValue(0); rect->y1 = obj1.arrayGet(1).getNumWithDefaultValue(0); rect->x2 = obj1.arrayGet(2).getNumWithDefaultValue(1); rect->y2 = obj1.arrayGet(3).getNumWithDefaultValue(1); if (rect->x1 > rect->x2) { double t = rect->x1; rect->x1 = rect->x2; rect->x2 = t; } if (rect->y1 > rect->y2) { double t = rect->y1; rect->y1 = rect->y2; rect->y2 = t; } } else { rect->x1 = rect->y1 = 0; rect->x2 = rect->y2 = 1; error(errSyntaxError, -1, "Bad bounding box for annotation"); ok = false; } obj1 = dict->lookup("Contents"); if (obj1.isString()) { contents.reset(obj1.getString()->copy()); } else { contents = std::make_unique(); } // Note: This value is overwritten by Annots ctor const Object &pObj = dict->lookupNF("P"); if (pObj.isRef()) { const Ref pRef = pObj.getRef(); page = doc->getCatalog()->findPage(pRef); } else { page = 0; } obj1 = dict->lookup("NM"); if (obj1.isString()) { name.reset(obj1.getString()->copy()); } obj1 = dict->lookup("M"); if (obj1.isString()) { modified.reset(obj1.getString()->copy()); } //----- get the flags obj1 = dict->lookup("F"); if (obj1.isInt()) { flags |= obj1.getInt(); } else { flags = flagUnknown; } //----- get the annotation appearance dictionary apObj = dict->lookup("AP"); if (apObj.isDict()) { appearStreams = std::make_unique(doc, &apObj); } //----- get the appearance state asObj = dict->lookup("AS"); if (asObj.isName()) { appearState = std::make_unique(asObj.getName()); } else if (appearStreams && appearStreams->getNumStates() != 0) { error(errSyntaxError, -1, "Invalid or missing AS value in annotation containing one or more appearance subdictionaries"); // AS value is required in this case, but if the // N dictionary contains only one entry // take it as default appearance. if (appearStreams->getNumStates() == 1) { appearState = appearStreams->getStateKey(0); } } if (!appearState) { appearState = std::make_unique("Off"); } //----- get the annotation appearance if (appearStreams) { appearance = appearStreams->getAppearanceStream(AnnotAppearance::appearNormal, appearState->c_str()); } //----- parse the border style // According to the spec if neither the Border nor the BS entry is present, // the border shall be drawn as a solid line with a width of 1 point. But acroread // seems to ignore the Border entry for annots that can't have a BS entry. So, we only // follow this rule for annots tha can have a BS entry. obj1 = dict->lookup("Border"); if (obj1.isArray()) { border = std::make_unique(obj1.getArray()); } obj1 = dict->lookup("C"); if (obj1.isArray()) { color = std::make_unique(obj1.getArray()); } obj1 = dict->lookup("StructParent"); if (obj1.isInt()) { treeKey = obj1.getInt(); } else { treeKey = 0; } oc = dict->lookupNF("OC").copy(); } void Annot::getRect(double *x1, double *y1, double *x2, double *y2) const { *x1 = rect->x1; *y1 = rect->y1; *x2 = rect->x2; *y2 = rect->y2; } void Annot::setRect(const PDFRectangle *rectA) { setRect(rectA->x1, rectA->y1, rectA->x2, rectA->y2); } void Annot::setRect(double x1, double y1, double x2, double y2) { if (x1 < x2) { rect->x1 = x1; rect->x2 = x2; } else { rect->x1 = x2; rect->x2 = x1; } if (y1 < y2) { rect->y1 = y1; rect->y2 = y2; } else { rect->y1 = y2; rect->y2 = y1; } Array *a = new Array(doc->getXRef()); a->add(Object(rect->x1)); a->add(Object(rect->y1)); a->add(Object(rect->x2)); a->add(Object(rect->y2)); update("Rect", Object(a)); invalidateAppearance(); } bool Annot::inRect(double x, double y) const { return rect->contains(x, y); } 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)); annotObj.dictSet("M", Object(modified->copy())); } annotObj.dictSet(const_cast(key), std::move(value)); doc->getXRef()->setModifiedObject(&annotObj, ref); hasBeenUpdated = true; } void Annot::setContents(std::unique_ptr &&new_content) { annotLocker(); if (new_content) { contents = std::move(new_content); // append the unicode marker if needed if (!contents->hasUnicodeMarker()) { contents->prependUnicodeMarker(); } } else { contents = std::make_unique(); } update("Contents", Object(contents->copy())); } void Annot::setName(GooString *new_name) { annotLocker(); if (new_name) { name = std::make_unique(new_name); } else { name = std::make_unique(); } update("NM", Object(name->copy())); } void Annot::setModified(GooString *new_modified) { annotLocker(); if (new_modified) { modified = std::make_unique(new_modified); update("M", Object(modified->copy())); } else { modified.reset(nullptr); update("M", Object(objNull)); } } void Annot::setFlags(unsigned int new_flags) { annotLocker(); flags = new_flags; update("F", Object(int(flags))); } void Annot::setBorder(std::unique_ptr &&new_border) { annotLocker(); if (new_border) { Object obj1 = new_border->writeToObject(doc->getXRef()); update(new_border->getType() == AnnotBorder::typeArray ? "Border" : "BS", std::move(obj1)); border = std::move(new_border); } else { border = nullptr; } invalidateAppearance(); } void Annot::setColor(std::unique_ptr &&new_color) { annotLocker(); if (new_color) { Object obj1 = new_color->writeToObject(doc->getXRef()); update("C", std::move(obj1)); color = std::move(new_color); } else { color = nullptr; } invalidateAppearance(); } void Annot::setPage(int pageIndex, bool updateP) { annotLocker(); Page *pageobj = doc->getPage(pageIndex); Object obj1(objNull); if (pageobj) { const Ref pageRef = pageobj->getRef(); obj1 = Object(pageRef); page = pageIndex; } else { page = 0; } if (updateP) { update("P", std::move(obj1)); } } void Annot::setAppearanceState(const char *state) { annotLocker(); if (!state) { return; } appearState = std::make_unique(state); appearBBox = nullptr; update("AS", Object(objName, state)); // The appearance state determines the current appearance stream if (appearStreams) { appearance = appearStreams->getAppearanceStream(AnnotAppearance::appearNormal, appearState->c_str()); } else { appearance.setToNull(); } } void Annot::invalidateAppearance() { annotLocker(); if (appearStreams) { // Remove existing appearance streams appearStreams->removeAllStreams(); } appearStreams = nullptr; appearState = nullptr; appearBBox = nullptr; appearance.setToNull(); Object obj2 = annotObj.dictLookup("AP"); if (!obj2.isNull()) { update("AP", Object(objNull)); // Remove AP } obj2 = annotObj.dictLookup("AS"); if (!obj2.isNull()) { update("AS", Object(objNull)); // Remove AS } } double Annot::getXMin() { return rect->x1; } double Annot::getYMin() { return rect->y1; } double Annot::getXMax() { return rect->x2; } double Annot::getYMax() { return rect->y2; } void Annot::readArrayNum(Object *pdfArray, int key, double *value) { Object valueObject = pdfArray->arrayGet(key); if (valueObject.isNum()) { *value = valueObject.getNum(); } else { *value = 0; ok = false; } } void Annot::removeReferencedObjects() { // Remove appearance streams (if any) invalidateAppearance(); } void Annot::incRefCnt() { refCnt++; } void Annot::decRefCnt() { if (--refCnt == 0) { delete this; } } Annot::~Annot() { } void AnnotAppearanceBuilder::setDrawColor(const AnnotColor *drawColor, bool fill) { const double *values = drawColor->getValues(); 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; case AnnotColor::colorRGB: appearBuf->appendf("{0:.5f} {1:.5f} {2:.5f} {3:s}\n", values[0], values[1], values[2], fill ? "rg" : "RG"); break; case AnnotColor::colorGray: appearBuf->appendf("{0:.5f} {1:c}\n", values[0], fill ? 'g' : 'G'); break; case AnnotColor::colorTransparent: default: break; } } void AnnotAppearanceBuilder::setTextFont(const Object &fontName, double fontSize) { if (fontName.isName() && strlen(fontName.getName()) > 0) { appearBuf->appendf("/{0:s} {1:.2f} Tf\n", fontName.getName(), fontSize); } } void AnnotAppearanceBuilder::setLineStyleForBorder(const AnnotBorder *border) { switch (border->getStyle()) { case AnnotBorder::borderDashed: appearBuf->append("["); for (double dash : border->getDash()) { appearBuf->appendf(" {0:.2f}", dash); } appearBuf->append(" ] 0 d\n"); break; default: appearBuf->append("[] 0 d\n"); break; } appearBuf->appendf("{0:.2f} w\n", border->getWidth()); } // Draw an (approximate) circle of radius centered at (, ). // If is true, the circle is filled; otherwise it is stroked. void AnnotAppearanceBuilder::drawCircle(double cx, double cy, double r, bool fill) { if (fill) { drawEllipse(cx, cy, r, r, true, false); } else { drawEllipse(cx, cy, r, r, false, true); } } // Draw an (approximate) ellipse of radius on x-axis and on y-axis, centered at (, ). // If is true, the ellipse is filled with current color for non-stroking operations. // If is true, the ellipse path ist stroked with current color and color space for stroking operations. // Path will be closed if either fill or stroke is true; otherwise it's left open. void AnnotAppearanceBuilder::drawEllipse(double cx, double cy, double rx, double ry, bool fill, bool stroke) { appearBuf->appendf("{0:.2f} {1:.2f} m\n", cx + rx, cy); appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", cx + rx, cy + bezierCircle * ry, cx + bezierCircle * rx, cy + ry, cx, cy + ry); appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", cx - bezierCircle * rx, cy + ry, cx - rx, cy + bezierCircle * ry, cx - rx, cy); appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", cx - rx, cy - bezierCircle * ry, cx - bezierCircle * rx, cy - ry, cx, cy - ry); appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", cx + bezierCircle * rx, cy - ry, cx + rx, cy - bezierCircle * ry, cx + rx, cy); if (!fill && stroke) { appearBuf->append("s\n"); } else if (fill && !stroke) { appearBuf->append("f\n"); } else if (fill && stroke) { appearBuf->append("b\n"); } } // Draw the top-left half of an (approximate) circle of radius // centered at (, ). void AnnotAppearanceBuilder::drawCircleTopLeft(double cx, double cy, double r) { double r2; r2 = r / sqrt(2.0); 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); appearBuf->append("S\n"); } // Draw the bottom-right half of an (approximate) circle of radius // centered at (, ). void AnnotAppearanceBuilder::drawCircleBottomRight(double cx, double cy, double r) { double r2; r2 = r / sqrt(2.0); 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); appearBuf->append("S\n"); } void AnnotAppearanceBuilder::drawLineEndSquare(double x, double y, double size, bool fill, const Matrix &m) { const double halfSize { size / 2. }; const double x1[3] { x - size, x - size, x }; const double y1[3] { y + halfSize, y - halfSize, y - halfSize }; double tx, ty; m.transform(x, y + halfSize, &tx, &ty); appendf("{0:.2f} {1:.2f} m\n", tx, ty); for (int i = 0; i < 3; i++) { m.transform(x1[i], y1[i], &tx, &ty); appendf("{0:.2f} {1:.2f} l\n", tx, ty); } appearBuf->append(fill ? "b\n" : "s\n"); } void AnnotAppearanceBuilder::drawLineEndCircle(double x, double y, double size, bool fill, const Matrix &m) { const double halfSize { size / 2. }; const double x1[4] { x, x - halfSize - bezierCircle * halfSize, x - size, x - halfSize + bezierCircle * halfSize }; const double x2[4] { x - halfSize + bezierCircle * halfSize, x - size, x - halfSize - bezierCircle * halfSize, x }; const double x3[4] { x - halfSize, x - size, x - halfSize, x }; const double y1[4] { y + bezierCircle * halfSize, y + halfSize, y - bezierCircle * halfSize, y - halfSize }; const double y2[4] { y + halfSize, y + bezierCircle * halfSize, y - halfSize, y - bezierCircle * halfSize }; const double y3[4] { y + halfSize, y, y - halfSize, y }; double tx[3]; double ty[3]; m.transform(x, y, &tx[0], &ty[0]); appearBuf->appendf("{0:.2f} {1:.2f} m\n", tx[0], ty[0]); for (int i = 0; i < 4; i++) { m.transform(x1[i], y1[i], &tx[0], &ty[0]); m.transform(x2[i], y2[i], &tx[1], &ty[1]); m.transform(x3[i], y3[i], &tx[2], &ty[2]); appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", tx[0], ty[0], tx[1], ty[1], tx[2], ty[2]); } appearBuf->append(fill ? "b\n" : "s\n"); } void AnnotAppearanceBuilder::drawLineEndDiamond(double x, double y, double size, bool fill, const Matrix &m) { const double halfSize { size / 2. }; const double x1[3] { x - halfSize, x - size, x - halfSize }; const double y1[3] { y + halfSize, y, y - halfSize }; double tx, ty; m.transform(x, y, &tx, &ty); appendf("{0:.2f} {1:.2f} m\n", tx, ty); for (int i = 0; i < 3; i++) { m.transform(x1[i], y1[i], &tx, &ty); appendf("{0:.2f} {1:.2f} l\n", tx, ty); } appearBuf->append(fill ? "b\n" : "s\n"); } void AnnotAppearanceBuilder::drawLineEndArrow(double x, double y, double size, int orientation, bool isOpen, bool fill, const Matrix &m) { const double alpha { M_PI / 6. }; const double xOffs { orientation * size }; const double yOffs { tan(alpha) * size }; double tx, ty; m.transform(x - xOffs, y + yOffs, &tx, &ty); appendf("{0:.2f} {1:.2f} m\n", tx, ty); m.transform(x, y, &tx, &ty); appendf("{0:.2f} {1:.2f} l\n", tx, ty); m.transform(x - xOffs, y - yOffs, &tx, &ty); appendf("{0:.2f} {1:.2f} l\n", tx, ty); if (isOpen) { appearBuf->append("S\n"); } else { appearBuf->append(fill ? "b\n" : "s\n"); } } void AnnotAppearanceBuilder::drawLineEndSlash(double x, double y, double size, const Matrix &m) { const double halfSize { size / 2. }; const double xOffs { cos(M_PI / 3.) * halfSize }; double tx, ty; m.transform(x - xOffs, y - halfSize, &tx, &ty); appendf("{0:.2f} {1:.2f} m\n", tx, ty); m.transform(x + xOffs, y + halfSize, &tx, &ty); appendf("{0:.2f} {1:.2f} l\n", tx, ty); appearBuf->append("S\n"); } void AnnotAppearanceBuilder::drawLineEnding(AnnotLineEndingStyle endingStyle, double x, double y, double size, bool fill, const Matrix &m) { switch (endingStyle) { case annotLineEndingSquare: drawLineEndSquare(x, y, size, fill, m); break; case annotLineEndingCircle: drawLineEndCircle(x, y, size, fill, m); break; case annotLineEndingDiamond: drawLineEndDiamond(x, y, size, fill, m); break; case annotLineEndingOpenArrow: drawLineEndArrow(x, y, size, 1, true, fill, m); break; case annotLineEndingClosedArrow: drawLineEndArrow(x, y, size, 1, false, fill, m); break; case annotLineEndingButt: { const double halfSize { size / 2. }; double tx, ty; m.transform(x, y + halfSize, &tx, &ty); appendf("{0:.2f} {1:.2f} m\n", tx, ty); m.transform(x, y - halfSize, &tx, &ty); appendf("{0:.2f} {1:.2f} l S\n", tx, ty); } break; case annotLineEndingROpenArrow: drawLineEndArrow(x, y, size, -1, true, fill, m); break; case annotLineEndingRClosedArrow: drawLineEndArrow(x, y, size, -1, false, fill, m); break; case annotLineEndingSlash: drawLineEndSlash(x, y, size, m); break; default: break; } } double AnnotAppearanceBuilder::lineEndingXShorten(AnnotLineEndingStyle endingStyle, double size) { switch (endingStyle) { case annotLineEndingCircle: case annotLineEndingClosedArrow: case annotLineEndingDiamond: case annotLineEndingSquare: return size; default: break; } return 0; } double AnnotAppearanceBuilder::lineEndingXExtendBBox(AnnotLineEndingStyle endingStyle, double size) { switch (endingStyle) { case annotLineEndingRClosedArrow: case annotLineEndingROpenArrow: return size; case annotLineEndingSlash: return cos(M_PI / 3.) * size / 2.; default: break; } return 0; } Object Annot::createForm(const GooString *appearBuf, const double *bbox, bool transparencyGroup, Dict *resDict) { return createForm(appearBuf, bbox, transparencyGroup, resDict ? Object(resDict) : Object()); } Object Annot::createForm(const GooString *appearBuf, const double *bbox, bool transparencyGroup, Object &&resDictObject) { Dict *appearDict = new Dict(doc->getXRef()); appearDict->set("Length", Object(appearBuf->getLength())); appearDict->set("Subtype", Object(objName, "Form")); Array *a = new Array(doc->getXRef()); a->add(Object(bbox[0])); a->add(Object(bbox[1])); a->add(Object(bbox[2])); a->add(Object(bbox[3])); appearDict->set("BBox", Object(a)); if (transparencyGroup) { Dict *d = new Dict(doc->getXRef()); d->set("S", Object(objName, "Transparency")); appearDict->set("Group", Object(d)); } if (resDictObject.isDict()) { appearDict->set("Resources", std::move(resDictObject)); } Stream *mStream = new AutoFreeMemStream(copyString(appearBuf->c_str()), 0, appearBuf->getLength(), Object(appearDict)); return Object(mStream); } Dict *Annot::createResourcesDict(const char *formName, Object &&formStream, const char *stateName, double opacity, const char *blendMode) { Dict *gsDict = new Dict(doc->getXRef()); if (opacity != 1) { gsDict->set("CA", Object(opacity)); gsDict->set("ca", Object(opacity)); } if (blendMode) { gsDict->set("BM", Object(objName, blendMode)); } Dict *stateDict = new Dict(doc->getXRef()); stateDict->set(stateName, Object(gsDict)); Dict *formDict = new Dict(doc->getXRef()); formDict->set(formName, std::move(formStream)); Dict *resDict = new Dict(doc->getXRef()); resDict->set("ExtGState", Object(stateDict)); resDict->set("XObject", Object(formDict)); return resDict; } Object Annot::getAppearanceResDict() { Object obj1, obj2; // Fetch appearance's resource dict (if any) obj1 = appearance.fetch(doc->getXRef()); if (obj1.isStream()) { obj2 = obj1.streamGetDict()->lookup("Resources"); if (obj2.isDict()) { return obj2; } } return Object(objNull); } bool Annot::isVisible(bool printing) { // check the flags if ((flags & flagHidden) || (printing && !(flags & flagPrint)) || (!printing && (flags & flagNoView))) { return false; } // check the OC OCGs *optContentConfig = doc->getCatalog()->getOptContentConfig(); if (optContentConfig) { if (!optContentConfig->optContentIsVisible(&oc)) { return false; } } return true; } int Annot::getRotation() const { Page *pageobj = doc->getPage(page); assert(pageobj != nullptr); if (flags & flagNoRotate) { return (360 - pageobj->getRotate()) % 360; } else { return 0; } } void Annot::draw(Gfx *gfx, bool printing) { annotLocker(); if (!isVisible(printing)) { return; } // draw the appearance stream Object obj = appearance.fetch(gfx->getXRef()); gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation()); } void Annot::setNewAppearance(Object &&newAppearance) { if (newAppearance.isNull()) { return; } annotLocker(); if (newAppearance.getType() == ObjType::objStream) { invalidateAppearance(); appearance = std::move(newAppearance); Ref updatedAppearanceStream = doc->getXRef()->addIndirectObject(appearance); 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); } else { appearStreams = std::make_unique(doc, &newAppearance); update("AP", std::move(newAppearance)); if (appearStreams) { appearance = appearStreams->getAppearanceStream(AnnotAppearance::appearNormal, appearState->c_str()); } } } Object Annot::getAppearance() const { return appearance.fetch(doc->getXRef()); } //------------------------------------------------------------------------ // AnnotPopup //------------------------------------------------------------------------ AnnotPopup::AnnotPopup(PDFDoc *docA, PDFRectangle *rectA) : Annot(docA, rectA) { type = typePopup; annotObj.dictSet("Subtype", Object(objName, "Popup")); initialize(docA, annotObj.getDict()); } AnnotPopup::AnnotPopup(PDFDoc *docA, Object &&dictObject, const Object *obj) : Annot(docA, std::move(dictObject), obj) { type = typePopup; initialize(docA, annotObj.getDict()); } AnnotPopup::~AnnotPopup() { } void AnnotPopup::initialize(PDFDoc *docA, Dict *dict) { const Object &parentObj = dict->lookupNF("Parent"); if (parentObj.isRef()) { parentRef = parentObj.getRef(); } else { parentRef = Ref::INVALID(); } open = dict->lookup("Open").getBoolWithDefaultValue(false); } void AnnotPopup::setParent(Annot *parentA) { parentRef = parentA->getRef(); update("Parent", Object(parentRef)); } void AnnotPopup::setOpen(bool openA) { open = openA; update("Open", Object(open)); } //------------------------------------------------------------------------ // AnnotMarkup //------------------------------------------------------------------------ AnnotMarkup::AnnotMarkup(PDFDoc *docA, PDFRectangle *rectA) : Annot(docA, rectA) { initialize(docA, annotObj.getDict()); } AnnotMarkup::AnnotMarkup(PDFDoc *docA, Object &&dictObject, const Object *obj) : Annot(docA, std::move(dictObject), obj) { initialize(docA, annotObj.getDict()); } AnnotMarkup::~AnnotMarkup() = default; void AnnotMarkup::initialize(PDFDoc *docA, Dict *dict) { Object obj1; obj1 = dict->lookup("T"); if (obj1.isString()) { label.reset(obj1.getString()->copy()); } Object popupObj = dict->lookup("Popup"); const Object &obj2 = dict->lookupNF("Popup"); if (popupObj.isDict() && obj2.isRef()) { popup = std::make_unique(docA, std::move(popupObj), &obj2); } opacity = dict->lookup("CA").getNumWithDefaultValue(1.0); obj1 = dict->lookup("CreationDate"); if (obj1.isString()) { date.reset(obj1.getString()->copy()); } const Object &irtObj = dict->lookupNF("IRT"); if (irtObj.isRef()) { inReplyTo = irtObj.getRef(); } else { inReplyTo = Ref::INVALID(); } obj1 = dict->lookup("Subj"); if (obj1.isString()) { subject.reset(obj1.getString()->copy()); } obj1 = dict->lookup("RT"); if (obj1.isName()) { const char *replyName = obj1.getName(); if (!strcmp(replyName, "R")) { replyTo = replyTypeR; } else if (!strcmp(replyName, "Group")) { replyTo = replyTypeGroup; } else { replyTo = replyTypeR; } } else { replyTo = replyTypeR; } obj1 = dict->lookup("ExData"); if (obj1.isDict()) { exData = parseAnnotExternalData(obj1.getDict()); } else { exData = annotExternalDataMarkupUnknown; } } 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(); } } else { label = std::make_unique(); } update("T", Object(label->copy())); } void AnnotMarkup::setPopup(std::unique_ptr &&new_popup) { // If there exists an old popup annotation that is already // associated with a page, then we need to remove that // popup annotation from the page. Otherwise we would have // dangling references to it. if (popup && popup->getPageNum() != 0) { Page *pageobj = doc->getPage(popup->getPageNum()); if (pageobj) { pageobj->removeAnnot(popup.get()); } } if (new_popup) { const Ref popupRef = new_popup->getRef(); update("Popup", Object(popupRef)); new_popup->setParent(this); popup = std::move(new_popup); // If this annotation is already added to a page, then we // add the new popup annotation to the same page. if (page != 0) { Page *pageobj = doc->getPage(page); assert(pageobj != nullptr); // pageobj should exist in doc (see setPage()) pageobj->addAnnot(popup.get()); } } else { popup = nullptr; } } void AnnotMarkup::setOpacity(double opacityA) { opacity = opacityA; update("CA", Object(opacity)); invalidateAppearance(); } void AnnotMarkup::setDate(GooString *new_date) { if (new_date) { date = std::make_unique(new_date); update("CreationDate", Object(date->copy())); } else { date.reset(nullptr); update("CreationDate", Object(objNull)); } } void AnnotMarkup::removeReferencedObjects() { Page *pageobj = doc->getPage(page); assert(pageobj != nullptr); // We're called when removing an annot from a page // Remove popup if (popup) { pageobj->removeAnnot(popup.get()); } Annot::removeReferencedObjects(); } //------------------------------------------------------------------------ // AnnotText //------------------------------------------------------------------------ AnnotText::AnnotText(PDFDoc *docA, PDFRectangle *rectA) : AnnotMarkup(docA, rectA) { type = typeText; flags |= flagNoZoom | flagNoRotate; annotObj.dictSet("Subtype", Object(objName, "Text")); initialize(docA, annotObj.getDict()); } AnnotText::AnnotText(PDFDoc *docA, Object &&dictObject, const Object *obj) : AnnotMarkup(docA, std::move(dictObject), obj) { type = typeText; flags |= flagNoZoom | flagNoRotate; initialize(docA, annotObj.getDict()); } AnnotText::~AnnotText() = default; void AnnotText::initialize(PDFDoc *docA, Dict *dict) { Object obj1; open = dict->lookup("Open").getBoolWithDefaultValue(false); obj1 = dict->lookup("Name"); if (obj1.isName()) { icon = std::make_unique(obj1.getName()); } else { icon = std::make_unique("Note"); } obj1 = dict->lookup("StateModel"); if (obj1.isString()) { const GooString *modelName = obj1.getString(); Object obj2 = dict->lookup("State"); if (obj2.isString()) { const GooString *stateName = obj2.getString(); if (!stateName->cmp("Marked")) { state = stateMarked; } else if (!stateName->cmp("Unmarked")) { state = stateUnmarked; } else if (!stateName->cmp("Accepted")) { state = stateAccepted; } else if (!stateName->cmp("Rejected")) { state = stateRejected; } else if (!stateName->cmp("Cancelled")) { state = stateCancelled; } else if (!stateName->cmp("Completed")) { state = stateCompleted; } else if (!stateName->cmp("None")) { state = stateNone; } else { state = stateUnknown; } } else { state = stateUnknown; } if (!modelName->cmp("Marked")) { switch (state) { case stateUnknown: state = stateMarked; break; case stateAccepted: case stateRejected: case stateCancelled: case stateCompleted: case stateNone: state = stateUnknown; break; default: break; } } else if (!modelName->cmp("Review")) { switch (state) { case stateUnknown: state = stateNone; break; case stateMarked: case stateUnmarked: state = stateUnknown; break; default: break; } } else { state = stateUnknown; } } else { state = stateUnknown; } } void AnnotText::setOpen(bool openA) { open = openA; update("Open", Object(open)); } void AnnotText::setIcon(GooString *new_icon) { if (new_icon && icon->cmp(new_icon) == 0) { return; } if (new_icon) { icon = std::make_unique(new_icon); } else { icon = std::make_unique("Note"); } update("Name", Object(objName, icon->c_str())); invalidateAppearance(); } #define ANNOT_TEXT_AP_NOTE \ "3.602 24 m 20.398 24 l 22.387 24 24 22.387 24 20.398 c 24 3.602 l 24\n" \ "1.613 22.387 0 20.398 0 c 3.602 0 l 1.613 0 0 1.613 0 3.602 c 0 20.398\n" \ "l 0 22.387 1.613 24 3.602 24 c h\n" \ "3.602 24 m f\n" \ "0.533333 0.541176 0.521569 RG 2 w\n" \ "1 J\n" \ "1 j\n" \ "[] 0.0 d\n" \ "4 M 9 18 m 4 18 l 4 7 4 4 6 3 c 20 3 l 18 4 18 7 18 18 c 17 18 l S\n" \ "1.5 w\n" \ "0 j\n" \ "10 16 m 14 21 l S\n" \ "1.85625 w\n" \ "1 j\n" \ "15.07 20.523 m 15.07 19.672 14.379 18.977 13.523 18.977 c 12.672 18.977\n" \ "11.977 19.672 11.977 20.523 c 11.977 21.379 12.672 22.07 13.523 22.07 c\n" \ "14.379 22.07 15.07 21.379 15.07 20.523 c h\n" \ "15.07 20.523 m S\n" \ "1 w\n" \ "0 j\n" \ "6.5 13.5 m 15.5 13.5 l S\n" \ "6.5 10.5 m 13.5 10.5 l S\n" \ "6.801 7.5 m 15.5 7.5 l S\n" \ "0.729412 0.741176 0.713725 RG 2 w\n" \ "1 j\n" \ "9 19 m 4 19 l 4 8 4 5 6 4 c 20 4 l 18 5 18 8 18 19 c 17 19 l S\n" \ "1.5 w\n" \ "0 j\n" \ "10 17 m 14 22 l S\n" \ "1.85625 w\n" \ "1 j\n" \ "15.07 21.523 m 15.07 20.672 14.379 19.977 13.523 19.977 c 12.672 19.977\n" \ "11.977 20.672 11.977 21.523 c 11.977 22.379 12.672 23.07 13.523 23.07 c\n" \ "14.379 23.07 15.07 22.379 15.07 21.523 c h\n" \ "15.07 21.523 m S\n" \ "1 w\n" \ "0 j\n" \ "6.5 14.5 m 15.5 14.5 l S\n" \ "6.5 11.5 m 13.5 11.5 l S\n" \ "6.801 8.5 m 15.5 8.5 l S\n" #define ANNOT_TEXT_AP_COMMENT \ "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \ "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n" \ "l 1 21.523 2.477 23 4.301 23 c h\n" \ "4.301 23 m f\n" \ "0.533333 0.541176 0.521569 RG 2 w\n" \ "0 J\n" \ "1 j\n" \ "[] 0.0 d\n" \ "4 M 8 20 m 16 20 l 18.363 20 20 18.215 20 16 c 20 13 l 20 10.785 18.363 9\n" \ "16 9 c 13 9 l 8 3 l 8 9 l 8 9 l 5.637 9 4 10.785 4 13 c 4 16 l 4 18.215\n" \ "5.637 20 8 20 c h\n" \ "8 20 m S\n" \ "0.729412 0.741176 0.713725 RG 8 21 m 16 21 l 18.363 21 20 19.215 20 17\n" \ "c 20 14 l 20 11.785 18.363 10\n" \ "16 10 c 13 10 l 8 4 l 8 10 l 8 10 l 5.637 10 4 11.785 4 14 c 4 17 l 4\n" \ "19.215 5.637 21 8 21 c h\n" \ "8 21 m S\n" #define ANNOT_TEXT_AP_KEY \ "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \ "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n" \ "l 1 21.523 2.477 23 4.301 23 c h\n" \ "4.301 23 m f\n" \ "0.533333 0.541176 0.521569 RG 2 w\n" \ "1 J\n" \ "0 j\n" \ "[] 0.0 d\n" \ "4 M 11.895 18.754 m 13.926 20.625 17.09 20.496 18.961 18.465 c 20.832\n" \ "16.434 20.699 13.27 18.668 11.398 c 17.164 10.016 15.043 9.746 13.281\n" \ "10.516 c 12.473 9.324 l 11.281 10.078 l 9.547 8.664 l 9.008 6.496 l\n" \ "7.059 6.059 l 6.34 4.121 l 5.543 3.668 l 3.375 4.207 l 2.938 6.156 l\n" \ "10.57 13.457 l 9.949 15.277 10.391 17.367 11.895 18.754 c h\n" \ "11.895 18.754 m S\n" \ "1.5 w\n" \ "16.059 15.586 m 16.523 15.078 17.316 15.043 17.824 15.512 c 18.332\n" \ "15.98 18.363 16.77 17.895 17.277 c 17.43 17.785 16.637 17.816 16.129\n" \ "17.352 c 15.621 16.883 15.59 16.094 16.059 15.586 c h\n" \ "16.059 15.586 m S\n" \ "0.729412 0.741176 0.713725 RG 2 w\n" \ "11.895 19.754 m 13.926 21.625 17.09 21.496 18.961 19.465 c 20.832\n" \ "17.434 20.699 14.27 18.668 12.398 c 17.164 11.016 15.043 10.746 13.281\n" \ "11.516 c 12.473 10.324 l 11.281 11.078 l 9.547 9.664 l 9.008 7.496 l\n" \ "7.059 7.059 l 6.34 5.121 l 5.543 4.668 l 3.375 5.207 l 2.938 7.156 l\n" \ "10.57 14.457 l 9.949 16.277 10.391 18.367 11.895 19.754 c h\n" \ "11.895 19.754 m S\n" \ "1.5 w\n" \ "16.059 16.586 m 16.523 16.078 17.316 16.043 17.824 16.512 c 18.332\n" \ "16.98 18.363 17.77 17.895 18.277 c 17.43 18.785 16.637 18.816 16.129\n" \ "18.352 c 15.621 17.883 15.59 17.094 16.059 16.586 c h\n" \ "16.059 16.586 m S\n" #define ANNOT_TEXT_AP_HELP \ "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \ "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n" \ "l 1 21.523 2.477 23 4.301 23 c h\n" \ "4.301 23 m f\n" \ "0.533333 0.541176 0.521569 RG 2.5 w\n" \ "1 J\n" \ "1 j\n" \ "[] 0.0 d\n" \ "4 M 8.289 16.488 m 8.824 17.828 10.043 18.773 11.473 18.965 c 12.902 19.156\n" \ "14.328 18.559 15.195 17.406 c 16.062 16.254 16.242 14.723 15.664 13.398\n" \ "c S\n" \ "0 j\n" \ "12 8 m 12 12 16 11 16 15 c S\n" \ "1.539286 w\n" \ "1 j\n" \ "q 1 0 0 -0.999991 0 24 cm\n" \ "12.684 20.891 m 12.473 21.258 12.004 21.395 11.629 21.196 c 11.254\n" \ "20.992 11.105 20.531 11.297 20.149 c 11.488 19.77 11.945 19.61 12.332\n" \ "19.789 c 12.719 19.969 12.891 20.426 12.719 20.817 c S Q\n" \ "0.729412 0.741176 0.713725 RG 2.5 w\n" \ "8.289 17.488 m 9.109 19.539 11.438 20.535 13.488 19.711 c 15.539 18.891\n" \ "16.535 16.562 15.711 14.512 c 15.699 14.473 15.684 14.438 15.664 14.398\n" \ "c S\n" \ "0 j\n" \ "12 9 m 12 13 16 12 16 16 c S\n" \ "1.539286 w\n" \ "1 j\n" \ "q 1 0 0 -0.999991 0 24 cm\n" \ "12.684 19.891 m 12.473 20.258 12.004 20.395 11.629 20.195 c 11.254\n" \ "19.992 11.105 19.531 11.297 19.149 c 11.488 18.77 11.945 18.61 12.332\n" \ "18.789 c 12.719 18.969 12.891 19.426 12.719 19.817 c S Q\n" #define ANNOT_TEXT_AP_NEW_PARAGRAPH \ "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \ "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n" \ "l 1 21.523 2.477 23 4.301 23 c h\n" \ "4.301 23 m f\n" \ "0.533333 0.541176 0.521569 RG 4 w\n" \ "0 J\n" \ "2 j\n" \ "[] 0.0 d\n" \ "4 M q 1 0 0 -1 0 24 cm\n" \ "9.211 11.988 m 8.449 12.07 7.711 11.707 7.305 11.059 c 6.898 10.41\n" \ "6.898 9.59 7.305 8.941 c 7.711 8.293 8.449 7.93 9.211 8.012 c S Q\n" \ "1.004413 w\n" \ "1 J\n" \ "1 j\n" \ "q 1 0 0 -0.991232 0 24 cm\n" \ "18.07 11.511 m 15.113 10.014 l 12.199 11.602 l 12.711 8.323 l 10.301\n" \ "6.045 l 13.574 5.517 l 14.996 2.522 l 16.512 5.474 l 19.801 5.899 l\n" \ "17.461 8.252 l 18.07 11.511 l h\n" \ "18.07 11.511 m S Q\n" \ "2 w\n" \ "0 j\n" \ "11 17 m 10 17 l 10 3 l S\n" \ "14 3 m 14 13 l S\n" \ "0.729412 0.741176 0.713725 RG 4 w\n" \ "0 J\n" \ "2 j\n" \ "q 1 0 0 -1 0 24 cm\n" \ "9.211 10.988 m 8.109 11.105 7.125 10.309 7.012 9.211 c 6.895 8.109\n" \ "7.691 7.125 8.789 7.012 c 8.93 6.996 9.07 6.996 9.211 7.012 c S Q\n" \ "1.004413 w\n" \ "1 J\n" \ "1 j\n" \ "q 1 0 0 -0.991232 0 24 cm\n" \ "18.07 10.502 m 15.113 9.005 l 12.199 10.593 l 12.711 7.314 l 10.301\n" \ "5.036 l 13.574 4.508 l 14.996 1.513 l 16.512 4.465 l 19.801 4.891 l\n" \ "17.461 7.243 l 18.07 10.502 l h\n" \ "18.07 10.502 m S Q\n" \ "2 w\n" \ "0 j\n" \ "11 18 m 10 18 l 10 4 l S\n" \ "14 4 m 14 14 l S\n" #define ANNOT_TEXT_AP_PARAGRAPH \ "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \ "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n" \ "l 1 21.523 2.477 23 4.301 23 c h\n" \ "4.301 23 m f\n" \ "0.533333 0.541176 0.521569 RG 2 w\n" \ "1 J\n" \ "1 j\n" \ "[] 0.0 d\n" \ "4 M 15 3 m 15 18 l 11 18 l 11 3 l S\n" \ "4 w\n" \ "q 1 0 0 -1 0 24 cm\n" \ "9.777 10.988 m 8.746 10.871 7.973 9.988 8 8.949 c 8.027 7.91 8.844\n" \ "7.066 9.879 7.004 c S Q\n" \ "0.729412 0.741176 0.713725 RG 2 w\n" \ "15 4 m 15 19 l 11 19 l 11 4 l S\n" \ "4 w\n" \ "q 1 0 0 -1 0 24 cm\n" \ "9.777 9.988 m 8.746 9.871 7.973 8.988 8 7.949 c 8.027 6.91 8.844 6.066\n" \ "9.879 6.004 c S Q\n" #define ANNOT_TEXT_AP_INSERT \ "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \ "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n" \ "l 1 21.523 2.477 23 4.301 23 c h\n" \ "4.301 23 m f\n" \ "0.533333 0.541176 0.521569 RG 2 w\n" \ "1 J\n" \ "0 j\n" \ "[] 0.0 d\n" \ "4 M 12 18.012 m 20 18 l S\n" \ "9 10 m 17 10 l S\n" \ "12 14.012 m 20 14 l S\n" \ "12 6.012 m 20 6.012 l S\n" \ "4 12 m 6 10 l 4 8 l S\n" \ "4 12 m 4 8 l S\n" \ "0.729412 0.741176 0.713725 RG 12 19.012 m 20 19 l S\n" \ "9 11 m 17 11 l S\n" \ "12 15.012 m 20 15 l S\n" \ "12 7.012 m 20 7.012 l S\n" \ "4 13 m 6 11 l 4 9 l S\n" \ "4 13 m 4 9 l S\n" #define ANNOT_TEXT_AP_CROSS \ "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \ "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n" \ "l 1 21.523 2.477 23 4.301 23 c h\n" \ "4.301 23 m f\n" \ "0.533333 0.541176 0.521569 RG 2.5 w\n" \ "1 J\n" \ "0 j\n" \ "[] 0.0 d\n" \ "4 M 18 5 m 6 17 l S\n" \ "6 5 m 18 17 l S\n" \ "0.729412 0.741176 0.713725 RG 18 6 m 6 18 l S\n" \ "6 6 m 18 18 l S\n" #define ANNOT_TEXT_AP_CIRCLE \ "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \ "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n" \ "l 1 21.523 2.477 23 4.301 23 c h\n" \ "4.301 23 m f\n" \ "0.533333 0.541176 0.521569 RG 2.5 w\n" \ "1 J\n" \ "1 j\n" \ "[] 0.0 d\n" \ "4 M 19.5 11.5 m 19.5 7.359 16.141 4 12 4 c 7.859 4 4.5 7.359 4.5 11.5 c 4.5\n" \ "15.641 7.859 19 12 19 c 16.141 19 19.5 15.641 19.5 11.5 c h\n" \ "19.5 11.5 m S\n" \ "0.729412 0.741176 0.713725 RG 19.5 12.5 m 19.5 8.359 16.141 5 12 5 c\n" \ "7.859 5 4.5 8.359 4.5 12.5 c 4.5\n" \ "16.641 7.859 20 12 20 c 16.141 20 19.5 16.641 19.5 12.5 c h\n" \ "19.5 12.5 m S\n" void AnnotText::draw(Gfx *gfx, bool printing) { double ca = 1; if (!isVisible(printing)) { return; } annotLocker(); if (appearance.isNull()) { ca = opacity; AnnotAppearanceBuilder appearBuilder; appearBuilder.append("q\n"); if (color) { appearBuilder.setDrawColor(color.get(), true); } else { appearBuilder.append("1 1 1 rg\n"); } if (!icon->cmp("Note")) { appearBuilder.append(ANNOT_TEXT_AP_NOTE); } else if (!icon->cmp("Comment")) { appearBuilder.append(ANNOT_TEXT_AP_COMMENT); } else if (!icon->cmp("Key")) { appearBuilder.append(ANNOT_TEXT_AP_KEY); } else if (!icon->cmp("Help")) { appearBuilder.append(ANNOT_TEXT_AP_HELP); } else if (!icon->cmp("NewParagraph")) { appearBuilder.append(ANNOT_TEXT_AP_NEW_PARAGRAPH); } else if (!icon->cmp("Paragraph")) { appearBuilder.append(ANNOT_TEXT_AP_PARAGRAPH); } else if (!icon->cmp("Insert")) { appearBuilder.append(ANNOT_TEXT_AP_INSERT); } else if (!icon->cmp("Cross")) { appearBuilder.append(ANNOT_TEXT_AP_CROSS); } else if (!icon->cmp("Circle")) { appearBuilder.append(ANNOT_TEXT_AP_CIRCLE); } appearBuilder.append("Q\n"); // Force 24x24 rectangle PDFRectangle fixedRect(rect->x1, rect->y2 - 24, rect->x1 + 24, rect->y2); appearBBox = std::make_unique(&fixedRect); double bbox[4]; appearBBox->getBBoxRect(bbox); if (ca == 1) { appearance = createForm(appearBuilder.buffer(), bbox, false, nullptr); } else { Object aStream = createForm(appearBuilder.buffer(), bbox, true, nullptr); GooString appearBuf("/GS0 gs\n/Fm0 Do"); Dict *resDict = createResourcesDict("Fm0", std::move(aStream), "GS0", ca, nullptr); appearance = createForm(&appearBuf, bbox, false, resDict); } } // draw the appearance stream Object obj = appearance.fetch(gfx->getXRef()); if (appearBBox) { gfx->drawAnnot(&obj, nullptr, color.get(), appearBBox->getPageXMin(), appearBBox->getPageYMin(), appearBBox->getPageXMax(), appearBBox->getPageYMax(), getRotation()); } else { gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation()); } } //------------------------------------------------------------------------ // AnnotLink //------------------------------------------------------------------------ AnnotLink::AnnotLink(PDFDoc *docA, PDFRectangle *rectA) : Annot(docA, rectA) { type = typeLink; annotObj.dictSet("Subtype", Object(objName, "Link")); initialize(docA, annotObj.getDict()); } AnnotLink::AnnotLink(PDFDoc *docA, Object &&dictObject, const Object *obj) : Annot(docA, std::move(dictObject), obj) { type = typeLink; initialize(docA, annotObj.getDict()); } AnnotLink::~AnnotLink() = default; void AnnotLink::initialize(PDFDoc *docA, Dict *dict) { Object obj1; // look for destination obj1 = dict->lookup("Dest"); if (!obj1.isNull()) { action = LinkAction::parseDest(&obj1); // look for action } else { obj1 = dict->lookup("A"); if (obj1.isDict()) { action = LinkAction::parseAction(&obj1, doc->getCatalog()->getBaseURI()); } } obj1 = dict->lookup("H"); if (obj1.isName()) { const char *effect = obj1.getName(); if (!strcmp(effect, "N")) { linkEffect = effectNone; } else if (!strcmp(effect, "I")) { linkEffect = effectInvert; } else if (!strcmp(effect, "O")) { linkEffect = effectOutline; } else if (!strcmp(effect, "P")) { linkEffect = effectPush; } else { linkEffect = effectInvert; } } else { linkEffect = effectInvert; } /* obj1 = dict->lookup("PA"); if (obj1.isDict()) { uriAction = NULL; } else { uriAction = NULL; } obj1.free(); */ obj1 = dict->lookup("QuadPoints"); if (obj1.isArray()) { quadrilaterals = std::make_unique(obj1.getArray(), rect.get()); } obj1 = dict->lookup("BS"); if (obj1.isDict()) { border = std::make_unique(obj1.getDict()); } else if (!border) { border = std::make_unique(); } } void AnnotLink::draw(Gfx *gfx, bool printing) { if (!isVisible(printing)) { return; } annotLocker(); // draw the appearance stream Object obj = appearance.fetch(gfx->getXRef()); gfx->drawAnnot(&obj, border.get(), color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation()); } //------------------------------------------------------------------------ // AnnotFreeText //------------------------------------------------------------------------ const double AnnotFreeText::undefinedFontPtSize = 10.; AnnotFreeText::AnnotFreeText(PDFDoc *docA, PDFRectangle *rectA) : AnnotMarkup(docA, rectA) { type = typeFreeText; annotObj.dictSet("Subtype", Object(objName, "FreeText")); annotObj.dictSet("DA", Object(new GooString())); initialize(docA, annotObj.getDict()); } AnnotFreeText::AnnotFreeText(PDFDoc *docA, Object &&dictObject, const Object *obj) : AnnotMarkup(docA, std::move(dictObject), obj) { type = typeFreeText; initialize(docA, annotObj.getDict()); } AnnotFreeText::~AnnotFreeText() = default; void AnnotFreeText::initialize(PDFDoc *docA, Dict *dict) { Object obj1; obj1 = dict->lookup("DA"); if (obj1.isString()) { appearanceString.reset(obj1.getString()->copy()); } else { appearanceString = std::make_unique(); error(errSyntaxWarning, -1, "Bad appearance for annotation"); } obj1 = dict->lookup("Q"); if (obj1.isInt()) { quadding = (VariableTextQuadding)obj1.getInt(); } else { quadding = VariableTextQuadding::leftJustified; } obj1 = dict->lookup("DS"); if (obj1.isString()) { styleString.reset(obj1.getString()->copy()); } obj1 = dict->lookup("CL"); if (obj1.isArray() && obj1.arrayGetLength() >= 4) { const double x1 = obj1.arrayGet(0).getNumWithDefaultValue(0); const double y1 = obj1.arrayGet(1).getNumWithDefaultValue(0); const double x2 = obj1.arrayGet(2).getNumWithDefaultValue(0); const double y2 = obj1.arrayGet(3).getNumWithDefaultValue(0); if (obj1.arrayGetLength() == 6) { const double x3 = obj1.arrayGet(4).getNumWithDefaultValue(0); const double y3 = obj1.arrayGet(5).getNumWithDefaultValue(0); calloutLine = std::make_unique(x1, y1, x2, y2, x3, y3); } else { calloutLine = std::make_unique(x1, y1, x2, y2); } } obj1 = dict->lookup("IT"); if (obj1.isName()) { const char *intentName = obj1.getName(); if (!strcmp(intentName, "FreeText")) { intent = intentFreeText; } else if (!strcmp(intentName, "FreeTextCallout")) { intent = intentFreeTextCallout; } else if (!strcmp(intentName, "FreeTextTypeWriter")) { intent = intentFreeTextTypeWriter; } else { intent = intentFreeText; } } else { intent = intentFreeText; } obj1 = dict->lookup("BS"); if (obj1.isDict()) { border = std::make_unique(obj1.getDict()); } else if (!border) { border = std::make_unique(); } obj1 = dict->lookup("BE"); if (obj1.isDict()) { borderEffect = std::make_unique(obj1.getDict()); } obj1 = dict->lookup("RD"); if (obj1.isArray()) { rectangle = parseDiffRectangle(obj1.getArray(), rect.get()); } obj1 = dict->lookup("LE"); if (obj1.isName()) { GooString styleName(obj1.getName()); endStyle = parseAnnotLineEndingStyle(&styleName); } else { endStyle = annotLineEndingNone; } } void AnnotFreeText::setContents(std::unique_ptr &&new_content) { Annot::setContents(std::move(new_content)); invalidateAppearance(); } void AnnotFreeText::setDefaultAppearance(const DefaultAppearance &da) { appearanceString = std::make_unique(da.toAppearanceString()); update("DA", Object(appearanceString->copy())); invalidateAppearance(); } void AnnotFreeText::setQuadding(VariableTextQuadding new_quadding) { quadding = new_quadding; update("Q", Object((int)quadding)); invalidateAppearance(); } 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(); } } else { styleString = std::make_unique(); } update("DS", Object(styleString->copy())); } void AnnotFreeText::setCalloutLine(AnnotCalloutLine *line) { Object obj1; if (line == nullptr) { obj1.setToNull(); calloutLine = nullptr; } else { double x1 = line->getX1(), y1 = line->getY1(); double x2 = line->getX2(), y2 = line->getY2(); obj1 = Object(new Array(doc->getXRef())); obj1.arrayAdd(Object(x1)); obj1.arrayAdd(Object(y1)); obj1.arrayAdd(Object(x2)); obj1.arrayAdd(Object(y2)); AnnotCalloutMultiLine *mline = dynamic_cast(line); if (mline) { double x3 = mline->getX3(), y3 = mline->getY3(); obj1.arrayAdd(Object(x3)); obj1.arrayAdd(Object(y3)); calloutLine = std::make_unique(x1, y1, x2, y2, x3, y3); } else { calloutLine = std::make_unique(x1, y1, x2, y2); } } update("CL", std::move(obj1)); invalidateAppearance(); } void AnnotFreeText::setIntent(AnnotFreeTextIntent new_intent) { const char *intentName; intent = new_intent; if (new_intent == intentFreeText) { intentName = "FreeText"; } else if (new_intent == intentFreeTextCallout) { intentName = "FreeTextCallout"; } else { // intentFreeTextTypeWriter intentName = "FreeTextTypeWriter"; } update("IT", Object(objName, intentName)); } std::unique_ptr AnnotFreeText::getDefaultAppearance() const { return std::make_unique(appearanceString.get()); } static std::unique_ptr createAnnotDrawFont(XRef *xref, Dict *fontParentDict, const char *resourceName = "AnnotDrawFont", const char *fontname = "Helvetica") { const Ref dummyRef = { -1, -1 }; Dict *fontDict = new Dict(xref); fontDict->add("BaseFont", Object(objName, fontname)); fontDict->add("Subtype", Object(objName, "Type1")); if (strcmp(fontname, "ZapfDingbats") && strcmp(fontname, "Symbol")) { fontDict->add("Encoding", Object(objName, "WinAnsiEncoding")); } Object fontsDictObj = fontParentDict->lookup("Font"); if (!fontsDictObj.isDict()) { fontsDictObj = Object(new Dict(xref)); fontParentDict->add("Font", fontsDictObj.copy()); // This is not a copy it's a ref } fontsDictObj.dictSet(resourceName, Object(fontDict)); return GfxFont::makeFont(xref, resourceName, dummyRef, fontDict); } class HorizontalTextLayouter { public: HorizontalTextLayouter() = default; HorizontalTextLayouter(const GooString *text, const Form *form, const GfxFont *font, std::optional availableWidth, const bool noReencode) { int i = 0; double blockWidth; bool newFontNeeded = false; GooString outputText; const bool isUnicode = text->hasUnicodeMarker(); int charCount; Annot::layoutText(text, &outputText, &i, *font, &blockWidth, availableWidth ? *availableWidth : 0.0, &charCount, noReencode, !noReencode ? &newFontNeeded : nullptr); data.emplace_back(outputText.toStr(), std::string(), blockWidth, charCount); if (availableWidth) { *availableWidth -= blockWidth; } 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; error(errSyntaxError, -1, "HorizontalTextLayouter, found character that the font can't represent"); newFontNeeded = false; } else { Unicode uChar; if (isUnicode) { uChar = (unsigned char)(text->getChar(i)) << 8; uChar += (unsigned char)(text->getChar(i + 1)); } else { uChar = pdfDocEncoding[text->getChar(i) & 0xff]; } const std::string auxFontName = form->getFallbackFontForChar(uChar, *font); if (!auxFontName.empty()) { std::shared_ptr auxFont = form->getDefaultResources()->lookupFont(auxFontName.c_str()); // 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(); } int auxI = 0; Annot::layoutText(&auxContents, &outputText, &auxI, *auxFont, &blockWidth, availableWidth ? *availableWidth : 0.0, &charCount, false, &newFontNeeded); assert(!newFontNeeded); if (availableWidth) { *availableWidth -= blockWidth; } // 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) // 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); } } else { error(errSyntaxError, -1, "HorizontalTextLayouter, couldn't find a font for character U+{0:04uX}", uChar); newFontNeeded = false; i += isUnicode ? 2 : 1; } } // Now layout the rest of the text with the original font if (!availableWidth || *availableWidth > 0) { Annot::layoutText(text, &outputText, &i, *font, &blockWidth, availableWidth ? *availableWidth : 0.0, &charCount, false, &newFontNeeded); if (availableWidth) { *availableWidth -= blockWidth; } // 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) { data.emplace_back(outputText.toStr(), std::string(), blockWidth, charCount); } else { i -= isUnicode ? 2 : 1; } } } consumedText = i; } HorizontalTextLayouter(const HorizontalTextLayouter &) = delete; HorizontalTextLayouter &operator=(const HorizontalTextLayouter &) = delete; double totalWidth() const { double totalWidth = 0; for (const Data &d : data) { totalWidth += d.width; } return totalWidth; } int totalCharCount() const { int total = 0; for (const Data &d : data) { total += d.charCount; } return total; } struct Data { Data(const std::string &t, const std::string &fName, double w, int cc) : text(t), fontName(fName), width(w), charCount(cc) { } const std::string text; const std::string fontName; const double width; const int charCount; }; std::vector data; int consumedText; }; struct DrawMultiLineTextResult { std::string text; int nLines = 0; }; // if fontName is empty it is assumed it is sent from the outside // so for text that is in font no Tf is added and for text that is in the aux fonts // a pair of q/Q is added static DrawMultiLineTextResult drawMultiLineText(const GooString &text, double availableWidth, const Form *form, const GfxFont &font, const std::string &fontName, double fontSize, VariableTextQuadding quadding, double borderWidth) { DrawMultiLineTextResult result; int i = 0; double xPosPrev = 0; const double availableTextWidthInFontPtSize = availableWidth / fontSize; while (i < text.getLength()) { GooString lineText(text.toStr().substr(i)); if (!lineText.hasUnicodeMarker() && text.hasUnicodeMarker()) { lineText.prependUnicodeMarker(); } const HorizontalTextLayouter textLayouter(&lineText, form, &font, availableTextWidthInFontPtSize, false); const double totalWidth = textLayouter.totalWidth() * fontSize; auto calculateX = [quadding, availableWidth, totalWidth, borderWidth] { switch (quadding) { case VariableTextQuadding::centered: return (availableWidth - totalWidth) / 2; break; case VariableTextQuadding::rightJustified: return availableWidth - totalWidth - borderWidth; break; default: // VariableTextQuadding::lLeftJustified: return borderWidth; break; } }; const double xPos = calculateX(); AnnotAppearanceBuilder builder; bool first = true; double prevBlockWidth = 0; for (const HorizontalTextLayouter::Data &d : textLayouter.data) { const std::string &fName = d.fontName.empty() ? fontName : d.fontName; if (!fName.empty()) { if (fontName.empty()) { builder.append(" q\n"); } builder.appendf("/{0:s} {1:.2f} Tf\n", fName.c_str(), fontSize); } const double yDiff = first ? -fontSize : 0; const double xDiff = first ? xPos - xPosPrev : prevBlockWidth; builder.appendf("{0:.2f} {1:.2f} Td\n", xDiff, yDiff); builder.writeString(d.text); builder.append(" Tj\n"); first = false; prevBlockWidth = d.width * fontSize; if (!fName.empty() && fontName.empty()) { builder.append(" Q\n"); } } xPosPrev = xPos + totalWidth - prevBlockWidth; result.text += builder.buffer()->toStr(); result.nLines += 1; if (i == 0) { i += textLayouter.consumedText; } else { i += textLayouter.consumedText - (text.hasUnicodeMarker() ? 2 : 0); } } return result; } void AnnotFreeText::generateFreeTextAppearance() { double borderWidth, ca = opacity; AnnotAppearanceBuilder appearBuilder; appearBuilder.append("q\n"); borderWidth = border->getWidth(); if (borderWidth > 0) { appearBuilder.setLineStyleForBorder(border.get()); } // Box size const double width = rect->x2 - rect->x1; const double height = rect->y2 - rect->y1; // Parse some properties from the appearance string DefaultAppearance da { appearanceString.get() }; // Default values if (!da.getFontName().isName()) { da.setFontName(Object(objName, "AnnotDrawFont")); } if (da.getFontPtSize() <= 0) { da.setFontPtSize(undefinedFontPtSize); } if (!da.getFontColor()) { da.setFontColor(std::make_unique(0, 0, 0)); } if (!contents) { contents = std::make_unique(); } // Draw box bool doFill = (color && color->getSpace() != AnnotColor::colorTransparent); bool doStroke = (borderWidth != 0); if (doFill || doStroke) { if (doStroke) { 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.append(doStroke ? "B\n" : "f\n"); } else { appearBuilder.append("S\n"); } } // Setup text clipping const double textmargin = borderWidth * 2; const double textwidth = width - 2 * textmargin; appearBuilder.appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re W n\n", textmargin, textwidth, height - 2 * textmargin); std::unique_ptr font = nullptr; // look for font name in the default resources Form *form = doc->getCatalog()->getForm(); // form is owned by catalog, no need to clean it up Object resourceObj; if (form && form->getDefaultResourcesObj() && form->getDefaultResourcesObj()->isDict()) { resourceObj = form->getDefaultResourcesObj()->copy(); // No real copy, but increment refcount of /DR Dict Dict *resDict = resourceObj.getDict(); Object fontResources = resDict->lookup("Font"); // The 'Font' subdictionary if (!fontResources.isDict()) { error(errSyntaxWarning, -1, "Font subdictionary is not a dictionary"); } else { // Get the font dictionary for the actual requested font Ref fontReference; Object fontDictionary = fontResources.getDict()->lookup(da.getFontName().getName(), &fontReference); if (fontDictionary.isDict()) { font = GfxFont::makeFont(doc->getXRef(), da.getFontName().getName(), fontReference, fontDictionary.getDict()); } else { error(errSyntaxWarning, -1, "Font dictionary is not a dictionary"); } } } // if fontname is not in the default resources, create a Helvetica fake font if (!font) { Dict *fontResDict = new Dict(doc->getXRef()); resourceObj = Object(fontResDict); font = createAnnotDrawFont(doc->getXRef(), fontResDict, da.getFontName().getName()); } // Set font state 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()); appearBuilder.append("ET Q\n"); double bbox[4]; bbox[0] = bbox[1] = 0; bbox[2] = rect->x2 - rect->x1; bbox[3] = rect->y2 - rect->y1; Object newAppearance; if (ca == 1) { newAppearance = createForm(appearBuilder.buffer(), bbox, false, std::move(resourceObj)); } else { Object aStream = createForm(appearBuilder.buffer(), bbox, true, std::move(resourceObj)); GooString appearBuf("/GS0 gs\n/Fm0 Do"); Dict *resDict = createResourcesDict("Fm0", std::move(aStream), "GS0", ca, nullptr); newAppearance = createForm(&appearBuf, bbox, false, resDict); } if (hasBeenUpdated) { // We should technically do this for all annots but AnnotFreeText // is 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(std::move(newAppearance)); } else { appearance = std::move(newAppearance); } } void AnnotFreeText::draw(Gfx *gfx, bool printing) { if (!isVisible(printing)) { return; } annotLocker(); if (appearance.isNull()) { generateFreeTextAppearance(); } // draw the appearance stream Object obj = appearance.fetch(gfx->getXRef()); gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation()); } // Before retrieving the res dict, regenerate the appearance stream if needed, // because AnnotFreeText::draw needs to store font info in the res dict Object AnnotFreeText::getAppearanceResDict() { if (appearance.isNull()) { generateFreeTextAppearance(); } return Annot::getAppearanceResDict(); } //------------------------------------------------------------------------ // AnnotLine //------------------------------------------------------------------------ AnnotLine::AnnotLine(PDFDoc *docA, PDFRectangle *rectA) : AnnotMarkup(docA, rectA) { type = typeLine; annotObj.dictSet("Subtype", Object(objName, "Line")); initialize(docA, annotObj.getDict()); } AnnotLine::AnnotLine(PDFDoc *docA, Object &&dictObject, const Object *obj) : AnnotMarkup(docA, std::move(dictObject), obj) { type = typeLine; initialize(docA, annotObj.getDict()); } AnnotLine::~AnnotLine() = default; void AnnotLine::initialize(PDFDoc *docA, Dict *dict) { Object obj1; obj1 = dict->lookup("L"); if (obj1.isArray() && obj1.arrayGetLength() == 4) { const double x1 = obj1.arrayGet(0).getNumWithDefaultValue(0); const double y1 = obj1.arrayGet(1).getNumWithDefaultValue(0); const double x2 = obj1.arrayGet(2).getNumWithDefaultValue(0); const double y2 = obj1.arrayGet(3).getNumWithDefaultValue(0); coord1 = std::make_unique(x1, y1); coord2 = std::make_unique(x2, y2); } else { coord1 = std::make_unique(); coord2 = std::make_unique(); } obj1 = dict->lookup("LE"); if (obj1.isArray() && obj1.arrayGetLength() == 2) { Object obj2; obj2 = obj1.arrayGet(0); if (obj2.isName()) { GooString leName(obj2.getName()); startStyle = parseAnnotLineEndingStyle(&leName); } else { startStyle = annotLineEndingNone; } obj2 = obj1.arrayGet(1); if (obj2.isName()) { GooString leName(obj2.getName()); endStyle = parseAnnotLineEndingStyle(&leName); } else { endStyle = annotLineEndingNone; } } else { startStyle = endStyle = annotLineEndingNone; } obj1 = dict->lookup("IC"); if (obj1.isArray()) { interiorColor = std::make_unique(obj1.getArray()); } leaderLineLength = dict->lookup("LL").getNumWithDefaultValue(0); leaderLineExtension = dict->lookup("LLE").getNumWithDefaultValue(0); if (leaderLineExtension < 0) { leaderLineExtension = 0; } caption = dict->lookup("Cap").getBoolWithDefaultValue(false); obj1 = dict->lookup("IT"); if (obj1.isName()) { const char *intentName = obj1.getName(); if (!strcmp(intentName, "LineArrow")) { intent = intentLineArrow; } else if (!strcmp(intentName, "LineDimension")) { intent = intentLineDimension; } else { intent = intentLineArrow; } } else { intent = intentLineArrow; } leaderLineOffset = dict->lookup("LLO").getNumWithDefaultValue(0); if (leaderLineOffset < 0) { leaderLineOffset = 0; } obj1 = dict->lookup("CP"); if (obj1.isName()) { const char *captionName = obj1.getName(); if (!strcmp(captionName, "Inline")) { captionPos = captionPosInline; } else if (!strcmp(captionName, "Top")) { captionPos = captionPosTop; } else { captionPos = captionPosInline; } } else { captionPos = captionPosInline; } obj1 = dict->lookup("Measure"); if (obj1.isDict()) { measure = nullptr; } else { measure = nullptr; } obj1 = dict->lookup("CO"); if (obj1.isArray() && (obj1.arrayGetLength() == 2)) { captionTextHorizontal = obj1.arrayGet(0).getNumWithDefaultValue(0); captionTextVertical = obj1.arrayGet(1).getNumWithDefaultValue(0); } else { captionTextHorizontal = captionTextVertical = 0; } obj1 = dict->lookup("BS"); if (obj1.isDict()) { border = std::make_unique(obj1.getDict()); } else if (!border) { border = std::make_unique(); } } void AnnotLine::setContents(std::unique_ptr &&new_content) { Annot::setContents(std::move(new_content)); if (caption) { invalidateAppearance(); } } void AnnotLine::setVertices(double x1, double y1, double x2, double y2) { coord1 = std::make_unique(x1, y1); coord2 = std::make_unique(x2, y2); Array *lArray = new Array(doc->getXRef()); lArray->add(Object(x1)); lArray->add(Object(y1)); lArray->add(Object(x2)); lArray->add(Object(y2)); update("L", Object(lArray)); invalidateAppearance(); } void AnnotLine::setStartEndStyle(AnnotLineEndingStyle start, AnnotLineEndingStyle end) { startStyle = start; endStyle = end; Array *leArray = new Array(doc->getXRef()); leArray->add(Object(objName, convertAnnotLineEndingStyle(startStyle))); leArray->add(Object(objName, convertAnnotLineEndingStyle(endStyle))); update("LE", Object(leArray)); invalidateAppearance(); } void AnnotLine::setInteriorColor(std::unique_ptr &&new_color) { if (new_color) { Object obj1 = new_color->writeToObject(doc->getXRef()); update("IC", std::move(obj1)); interiorColor = std::move(new_color); } else { interiorColor = nullptr; } invalidateAppearance(); } void AnnotLine::setLeaderLineLength(double len) { leaderLineLength = len; update("LL", Object(len)); invalidateAppearance(); } void AnnotLine::setLeaderLineExtension(double len) { leaderLineExtension = len; update("LLE", Object(len)); // LL is required if LLE is present update("LL", Object(leaderLineLength)); invalidateAppearance(); } void AnnotLine::setCaption(bool new_cap) { caption = new_cap; update("Cap", Object(new_cap)); invalidateAppearance(); } void AnnotLine::setIntent(AnnotLineIntent new_intent) { const char *intentName; intent = new_intent; if (new_intent == intentLineArrow) { intentName = "LineArrow"; } else { // intentLineDimension intentName = "LineDimension"; } update("IT", Object(objName, intentName)); } void AnnotLine::generateLineAppearance() { double borderWidth, ca = opacity; bool fill = false; appearBBox = std::make_unique(rect.get()); AnnotAppearanceBuilder appearBuilder; appearBuilder.append("q\n"); if (color) { appearBuilder.setDrawColor(color.get(), false); } if (interiorColor) { appearBuilder.setDrawColor(interiorColor.get(), true); fill = true; } appearBuilder.setLineStyleForBorder(border.get()); borderWidth = border->getWidth(); appearBBox->setBorderWidth(std::max(1., borderWidth)); const double x1 = coord1->getX(); const double y1 = coord1->getY(); const double x2 = coord2->getX(); const double y2 = coord2->getY(); // Main segment length const double main_len = sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); // Main segment becomes positive x direction, coord1 becomes (0,0) Matrix matr; const double angle = atan2(y2 - y1, x2 - x1); matr.m[0] = matr.m[3] = cos(angle); matr.m[1] = sin(angle); matr.m[2] = -matr.m[1]; matr.m[4] = x1 - rect->x1; matr.m[5] = y1 - rect->y1; double tx, ty, captionwidth = 0, captionheight = 0; AnnotLineCaptionPos actualCaptionPos = captionPos; const double fontsize = 9; const double captionhmargin = 2; // Left and right margin (inline caption only) const double captionmaxwidth = main_len - 2 * captionhmargin; const double lineendingSize = std::min(6. * borderWidth, main_len / 2); Dict *fontResDict; std::unique_ptr font; // Calculate caption width and height if (caption) { fontResDict = new Dict(doc->getXRef()); font = createAnnotDrawFont(doc->getXRef(), fontResDict); int lines = 0; int i = 0; while (i < contents->getLength()) { GooString out; double linewidth; layoutText(contents.get(), &out, &i, *font, &linewidth, 0, nullptr, false); linewidth *= fontsize; if (linewidth > captionwidth) { captionwidth = linewidth; } ++lines; } captionheight = lines * fontsize; // If text is longer than available space, turn into captionPosTop if (captionwidth > captionmaxwidth) { actualCaptionPos = captionPosTop; } } else { fontResDict = nullptr; font = nullptr; } // Draw main segment matr.transform(AnnotAppearanceBuilder::lineEndingXShorten(startStyle, lineendingSize), leaderLineLength, &tx, &ty); appearBuilder.appendf("{0:.2f} {1:.2f} m\n", tx, ty); appearBBox->extendTo(tx, ty); if (captionwidth != 0 && actualCaptionPos == captionPosInline) { // Break in the middle matr.transform((main_len - captionwidth) / 2 - captionhmargin, leaderLineLength, &tx, &ty); appearBuilder.appendf("{0:.2f} {1:.2f} l S\n", tx, ty); matr.transform((main_len + captionwidth) / 2 + captionhmargin, leaderLineLength, &tx, &ty); appearBuilder.appendf("{0:.2f} {1:.2f} m\n", tx, ty); } matr.transform(main_len - AnnotAppearanceBuilder::lineEndingXShorten(endStyle, lineendingSize), leaderLineLength, &tx, &ty); appearBuilder.appendf("{0:.2f} {1:.2f} l S\n", tx, ty); appearBBox->extendTo(tx, ty); if (startStyle != annotLineEndingNone) { const double extendX { -AnnotAppearanceBuilder::lineEndingXExtendBBox(startStyle, lineendingSize) }; appearBuilder.drawLineEnding(startStyle, 0, leaderLineLength, -lineendingSize, fill, matr); matr.transform(extendX, leaderLineLength + lineendingSize / 2., &tx, &ty); appearBBox->extendTo(tx, ty); matr.transform(extendX, leaderLineLength - lineendingSize / 2., &tx, &ty); appearBBox->extendTo(tx, ty); } if (endStyle != annotLineEndingNone) { const double extendX { AnnotAppearanceBuilder::lineEndingXExtendBBox(endStyle, lineendingSize) }; appearBuilder.drawLineEnding(endStyle, main_len, leaderLineLength, lineendingSize, fill, matr); matr.transform(main_len + extendX, leaderLineLength + lineendingSize / 2., &tx, &ty); appearBBox->extendTo(tx, ty); matr.transform(main_len + extendX, leaderLineLength - lineendingSize / 2., &tx, &ty); appearBBox->extendTo(tx, ty); } // Draw caption text if (caption) { double tlx = (main_len - captionwidth) / 2, tly; // Top-left coords if (actualCaptionPos == captionPosInline) { tly = leaderLineLength + captionheight / 2; } else { tly = leaderLineLength + captionheight + 2 * borderWidth; } tlx += captionTextHorizontal; tly += captionTextVertical; // Adjust bounding box matr.transform(tlx, tly - captionheight, &tx, &ty); appearBBox->extendTo(tx, ty); matr.transform(tlx + captionwidth, tly - captionheight, &tx, &ty); appearBBox->extendTo(tx, ty); matr.transform(tlx + captionwidth, tly, &tx, &ty); appearBBox->extendTo(tx, ty); matr.transform(tlx, tly, &tx, &ty); appearBBox->extendTo(tx, ty); // Setup text state (reusing transformed top-left coord) appearBuilder.appendf("0 g BT /AnnotDrawFont {0:.2f} Tf\n", fontsize); // Font color: black appearBuilder.appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} Tm\n", matr.m[0], matr.m[1], matr.m[2], matr.m[3], tx, ty); appearBuilder.appendf("0 {0:.2f} Td\n", -fontsize * font->getDescent()); // Draw text int i = 0; double xposPrev = 0; while (i < contents->getLength()) { GooString out; double linewidth, xpos; layoutText(contents.get(), &out, &i, *font, &linewidth, 0, nullptr, false); linewidth *= fontsize; xpos = (captionwidth - linewidth) / 2; appearBuilder.appendf("{0:.2f} {1:.2f} Td\n", xpos - xposPrev, -fontsize); appearBuilder.writeString(out.toStr()); appearBuilder.append("Tj\n"); xposPrev = xpos; } appearBuilder.append("ET\n"); } // Draw leader lines double ll_len = fabs(leaderLineLength) + leaderLineExtension; double sign = leaderLineLength >= 0 ? 1 : -1; if (ll_len != 0) { matr.transform(0, 0, &tx, &ty); appearBuilder.appendf("{0:.2f} {1:.2f} m\n", tx, ty); appearBBox->extendTo(tx, ty); matr.transform(0, sign * ll_len, &tx, &ty); appearBuilder.appendf("{0:.2f} {1:.2f} l S\n", tx, ty); appearBBox->extendTo(tx, ty); matr.transform(main_len, 0, &tx, &ty); appearBuilder.appendf("{0:.2f} {1:.2f} m\n", tx, ty); appearBBox->extendTo(tx, ty); matr.transform(main_len, sign * ll_len, &tx, &ty); appearBuilder.appendf("{0:.2f} {1:.2f} l S\n", tx, ty); appearBBox->extendTo(tx, ty); } appearBuilder.append("Q\n"); double bbox[4]; appearBBox->getBBoxRect(bbox); if (ca == 1) { appearance = createForm(appearBuilder.buffer(), bbox, false, fontResDict); } else { Object aStream = createForm(appearBuilder.buffer(), bbox, true, fontResDict); GooString appearBuf("/GS0 gs\n/Fm0 Do"); Dict *resDict = createResourcesDict("Fm0", std::move(aStream), "GS0", ca, nullptr); appearance = createForm(&appearBuf, bbox, false, resDict); } } void AnnotLine::draw(Gfx *gfx, bool printing) { if (!isVisible(printing)) { return; } annotLocker(); if (appearance.isNull()) { generateLineAppearance(); } // draw the appearance stream Object obj = appearance.fetch(gfx->getXRef()); if (appearBBox) { gfx->drawAnnot(&obj, nullptr, color.get(), appearBBox->getPageXMin(), appearBBox->getPageYMin(), appearBBox->getPageXMax(), appearBBox->getPageYMax(), getRotation()); } else { gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation()); } } // Before retrieving the res dict, regenerate the appearance stream if needed, // because AnnotLine::draw may need to store font info in the res dict Object AnnotLine::getAppearanceResDict() { if (appearance.isNull()) { generateLineAppearance(); } return Annot::getAppearanceResDict(); } //------------------------------------------------------------------------ // AnnotTextMarkup //------------------------------------------------------------------------ AnnotTextMarkup::AnnotTextMarkup(PDFDoc *docA, PDFRectangle *rectA, AnnotSubtype subType) : AnnotMarkup(docA, rectA) { switch (subType) { case typeHighlight: annotObj.dictSet("Subtype", Object(objName, "Highlight")); break; case typeUnderline: annotObj.dictSet("Subtype", Object(objName, "Underline")); break; case typeSquiggly: annotObj.dictSet("Subtype", Object(objName, "Squiggly")); break; case typeStrikeOut: annotObj.dictSet("Subtype", Object(objName, "StrikeOut")); break; default: assert(0 && "Invalid subtype for AnnotTextMarkup\n"); } // Store dummy quadrilateral with null coordinates Array *quadPoints = new Array(doc->getXRef()); for (int i = 0; i < 4 * 2; ++i) { quadPoints->add(Object(0.)); } annotObj.dictSet("QuadPoints", Object(quadPoints)); initialize(docA, annotObj.getDict()); } AnnotTextMarkup::AnnotTextMarkup(PDFDoc *docA, Object &&dictObject, const Object *obj) : AnnotMarkup(docA, std::move(dictObject), obj) { // the real type will be read in initialize() type = typeHighlight; initialize(docA, annotObj.getDict()); } AnnotTextMarkup::~AnnotTextMarkup() = default; void AnnotTextMarkup::initialize(PDFDoc *docA, Dict *dict) { Object obj1; obj1 = dict->lookup("Subtype"); if (obj1.isName()) { GooString typeName(obj1.getName()); if (!typeName.cmp("Highlight")) { type = typeHighlight; } else if (!typeName.cmp("Underline")) { type = typeUnderline; } else if (!typeName.cmp("Squiggly")) { type = typeSquiggly; } else if (!typeName.cmp("StrikeOut")) { type = typeStrikeOut; } } obj1 = dict->lookup("QuadPoints"); if (obj1.isArray()) { quadrilaterals = std::make_unique(obj1.getArray(), rect.get()); } else { error(errSyntaxError, -1, "Bad Annot Text Markup QuadPoints"); ok = false; } } void AnnotTextMarkup::setType(AnnotSubtype new_type) { const char *typeName = nullptr; /* squelch bogus compiler warning */ switch (new_type) { case typeHighlight: typeName = "Highlight"; break; case typeUnderline: typeName = "Underline"; break; case typeSquiggly: typeName = "Squiggly"; break; case typeStrikeOut: typeName = "StrikeOut"; break; default: assert(!"Invalid subtype"); } type = new_type; update("Subtype", Object(objName, typeName)); invalidateAppearance(); } void AnnotTextMarkup::setQuadrilaterals(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))); } quadrilaterals = std::make_unique(a, rect.get()); annotObj.dictSet("QuadPoints", Object(a)); invalidateAppearance(); } bool AnnotTextMarkup::shouldCreateApperance(Gfx *gfx) const { if (appearance.isNull()) { return true; } // Adobe Reader seems to have a complex condition for when to use the // appearance stream of typeHighlight, which is "use it if it has a Resources dictionary with ExtGState" // this is reverse engineering of me editing a file by hand and checking what it does so the real // condition may be more or less complex if (type == typeHighlight) { XRef *xref = gfx->getXRef(); const Object fetchedApperance = appearance.fetch(xref); if (fetchedApperance.isStream()) { const Object resources = fetchedApperance.streamGetDict()->lookup("Resources"); if (resources.isDict()) { if (resources.dictLookup("ExtGState").isDict()) { return false; } } } return true; } return false; } void AnnotTextMarkup::draw(Gfx *gfx, bool printing) { double ca = 1; int i; if (!isVisible(printing)) { return; } annotLocker(); if (shouldCreateApperance(gfx)) { bool blendMultiply = true; ca = opacity; AnnotAppearanceBuilder appearBuilder; appearBuilder.append("q\n"); /* Adjust BBox */ appearBBox = std::make_unique(rect.get()); for (i = 0; i < quadrilaterals->getQuadrilateralsLength(); ++i) { appearBBox->extendTo(quadrilaterals->getX1(i) - rect->x1, quadrilaterals->getY1(i) - rect->y1); appearBBox->extendTo(quadrilaterals->getX2(i) - rect->x1, quadrilaterals->getY2(i) - rect->y1); appearBBox->extendTo(quadrilaterals->getX3(i) - rect->x1, quadrilaterals->getY3(i) - rect->y1); appearBBox->extendTo(quadrilaterals->getX4(i) - rect->x1, quadrilaterals->getY4(i) - rect->y1); } switch (type) { case typeUnderline: if (color) { appearBuilder.setDrawColor(color.get(), false); } appearBuilder.append("[] 0 d 1 w\n"); // use a borderwidth, which is consistent with the line width appearBBox->setBorderWidth(1.0); for (i = 0; i < quadrilaterals->getQuadrilateralsLength(); ++i) { double x3, y3, x4, y4; x3 = quadrilaterals->getX3(i); y3 = quadrilaterals->getY3(i); x4 = quadrilaterals->getX4(i); y4 = quadrilaterals->getY4(i); appearBuilder.appendf("{0:.2f} {1:.2f} m\n", x3, y3); appearBuilder.appendf("{0:.2f} {1:.2f} l\n", x4, y4); appearBuilder.append("S\n"); } break; case typeStrikeOut: if (color) { appearBuilder.setDrawColor(color.get(), false); } blendMultiply = false; appearBuilder.append("[] 0 d 1 w\n"); for (i = 0; i < quadrilaterals->getQuadrilateralsLength(); ++i) { double x1, y1, x2, y2; double x3, y3, x4, y4; x1 = quadrilaterals->getX1(i); y1 = quadrilaterals->getY1(i); x2 = quadrilaterals->getX2(i); y2 = quadrilaterals->getY2(i); x3 = quadrilaterals->getX3(i); y3 = quadrilaterals->getY3(i); x4 = quadrilaterals->getX4(i); y4 = quadrilaterals->getY4(i); appearBuilder.appendf("{0:.2f} {1:.2f} m\n", (x1 + x3) / 2., (y1 + y3) / 2.); appearBuilder.appendf("{0:.2f} {1:.2f} l\n", (x2 + x4) / 2., (y2 + y4) / 2.); appearBuilder.append("S\n"); } break; case typeSquiggly: if (color) { appearBuilder.setDrawColor(color.get(), false); } appearBuilder.append("[] 0 d 1 w\n"); for (i = 0; i < quadrilaterals->getQuadrilateralsLength(); ++i) { double x1, y1, x2, y3; double h6; x1 = quadrilaterals->getX1(i); y1 = quadrilaterals->getY1(i); x2 = quadrilaterals->getX2(i); y3 = quadrilaterals->getY3(i); h6 = (y1 - y3) / 6.0; appearBuilder.appendf("{0:.2f} {1:.2f} m\n", x1, y3 + h6); bool down = false; do { down = !down; // Zigzag line x1 += 2; appearBuilder.appendf("{0:.2f} {1:.2f} l\n", x1, y3 + (down ? 0 : h6)); } while (x1 < x2); appearBuilder.append("S\n"); } break; default: case typeHighlight: if (color) { appearBuilder.setDrawColor(color.get(), true); } double biggestBorder = 0; for (i = 0; i < quadrilaterals->getQuadrilateralsLength(); ++i) { double x1, y1, x2, y2, x3, y3, x4, y4; double h4; x1 = quadrilaterals->getX1(i); y1 = quadrilaterals->getY1(i); x2 = quadrilaterals->getX2(i); y2 = quadrilaterals->getY2(i); x3 = quadrilaterals->getX3(i); y3 = quadrilaterals->getY3(i); x4 = quadrilaterals->getX4(i); y4 = quadrilaterals->getY4(i); h4 = fabs(y1 - y3) / 4.0; if (h4 > biggestBorder) { biggestBorder = h4; } appearBuilder.appendf("{0:.2f} {1:.2f} m\n", x3, y3); appearBuilder.appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", x3 - h4, y3 + h4, x1 - h4, y1 - h4, x1, y1); appearBuilder.appendf("{0:.2f} {1:.2f} l\n", x2, y2); appearBuilder.appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", x2 + h4, y2 - h4, x4 + h4, y4 + h4, x4, y4); appearBuilder.append("f\n"); } appearBBox->setBorderWidth(biggestBorder); break; } appearBuilder.append("Q\n"); double bbox[4]; bbox[0] = appearBBox->getPageXMin(); bbox[1] = appearBBox->getPageYMin(); bbox[2] = appearBBox->getPageXMax(); bbox[3] = appearBBox->getPageYMax(); Object aStream = createForm(appearBuilder.buffer(), bbox, true, nullptr); GooString appearBuf("/GS0 gs\n/Fm0 Do"); Dict *resDict = createResourcesDict("Fm0", std::move(aStream), "GS0", 1, blendMultiply ? "Multiply" : nullptr); if (ca == 1) { appearance = createForm(&appearBuf, bbox, false, resDict); } else { aStream = createForm(&appearBuf, bbox, true, resDict); Dict *resDict2 = createResourcesDict("Fm0", std::move(aStream), "GS0", ca, nullptr); appearance = createForm(&appearBuf, bbox, false, resDict2); } } // draw the appearance stream Object obj = appearance.fetch(gfx->getXRef()); if (appearBBox) { gfx->drawAnnot(&obj, nullptr, color.get(), appearBBox->getPageXMin(), appearBBox->getPageYMin(), appearBBox->getPageXMax(), appearBBox->getPageYMax(), getRotation()); } else { gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation()); } } //------------------------------------------------------------------------ // AnnotWidget //------------------------------------------------------------------------ AnnotWidget::AnnotWidget(PDFDoc *docA, Object &&dictObject, const Object *obj) : Annot(docA, std::move(dictObject), obj) { type = typeWidget; field = nullptr; initialize(docA, annotObj.getDict()); } AnnotWidget::AnnotWidget(PDFDoc *docA, Object *dictObject, Object *obj, FormField *fieldA) : Annot(docA, dictObject->copy(), obj) { type = typeWidget; field = fieldA; initialize(docA, dictObject->getDict()); } AnnotWidget::~AnnotWidget() = default; void AnnotWidget::initialize(PDFDoc *docA, Dict *dict) { Object obj1; form = doc->getCatalog()->getForm(); obj1 = dict->lookup("H"); if (obj1.isName()) { const char *modeName = obj1.getName(); if (!strcmp(modeName, "N")) { mode = highlightModeNone; } else if (!strcmp(modeName, "O")) { mode = highlightModeOutline; } else if (!strcmp(modeName, "P") || !strcmp(modeName, "T")) { mode = highlightModePush; } else { mode = highlightModeInvert; } } else { mode = highlightModeInvert; } obj1 = dict->lookup("MK"); if (obj1.isDict()) { appearCharacs = std::make_unique(obj1.getDict()); } obj1 = dict->lookup("A"); if (obj1.isDict()) { action = LinkAction::parseAction(&obj1, doc->getCatalog()->getBaseURI()); } additionalActions = dict->lookupNF("AA").copy(); obj1 = dict->lookup("Parent"); if (obj1.isDict()) { parent = nullptr; } else { parent = nullptr; } obj1 = dict->lookup("BS"); if (obj1.isDict()) { border = std::make_unique(obj1.getDict()); } updatedAppearanceStream = Ref::INVALID(); } std::unique_ptr AnnotWidget::getAdditionalAction(AdditionalActionsType additionalActionType) { return ::getAdditionalAction(additionalActionType, &additionalActions, doc); } std::unique_ptr AnnotWidget::getFormAdditionalAction(FormAdditionalActionsType formAdditionalActionType) { Object additionalActionsObject = additionalActions.fetch(doc->getXRef()); if (additionalActionsObject.isDict()) { const char *key = getFormAdditionalActionKey(formAdditionalActionType); Object actionObject = additionalActionsObject.dictLookup(key); if (actionObject.isDict()) { return LinkAction::parseAction(&actionObject, doc->getCatalog()->getBaseURI()); } } return nullptr; } bool AnnotWidget::setFormAdditionalAction(FormAdditionalActionsType formAdditionalActionType, const std::string &js) { Object additionalActionsObject = additionalActions.fetch(doc->getXRef()); if (!additionalActionsObject.isDict()) { additionalActionsObject = Object(new Dict(doc->getXRef())); annotObj.dictSet("AA", additionalActionsObject.copy()); } additionalActionsObject.dictSet(getFormAdditionalActionKey(formAdditionalActionType), LinkJavaScript::createObject(doc->getXRef(), js)); if (additionalActions.isRef()) { doc->getXRef()->setModifiedObject(&additionalActionsObject, additionalActions.getRef()); } else if (hasRef) { doc->getXRef()->setModifiedObject(&annotObj, ref); } else { error(errInternal, -1, "AnnotWidget::setFormAdditionalAction, where neither additionalActions is ref nor annotobj itself is ref"); return false; } return true; } // Grand unified handler for preparing text strings to be drawn into form // fields. Takes as input a text string (in PDFDocEncoding or UTF-16). // Converts some or all of this string to the appropriate encoding for the // specified font, and computes the width of the text. Can optionally stop // converting when a specified width has been reached, to perform line-breaking // for multi-line fields. // // Parameters: // text: input text string to convert // outBuf: buffer for writing re-encoded string // i: index at which to start converting; will be updated to point just after // last character processed // font: the font which will be used for display // width: computed width (unscaled by font size) will be stored here // widthLimit: if non-zero, stop converting to keep width under this value // (should be scaled down by font size) // charCount: count of number of characters will be stored here // noReencode: if set, do not try to translate the character encoding // (useful for Zapf Dingbats or other unusual encodings) // can only be used with simple fonts, not CID-keyed fonts // // TODO: Handle surrogate pairs in UTF-16. // Should be able to generate output for any CID-keyed font. // Doesn't handle vertical fonts--should it? void Annot::layoutText(const GooString *text, GooString *outBuf, int *i, const GfxFont &font, double *width, double widthLimit, int *charCount, bool noReencode, bool *newFontNeeded) { CharCode c; Unicode uChar; const Unicode *uAux; double w = 0.0; int uLen, n; double dx, dy, ox, oy; if (newFontNeeded) { *newFontNeeded = false; } if (width != nullptr) { *width = 0.0; } if (charCount != nullptr) { *charCount = 0; } if (!text) { return; } bool unicode = text->hasUnicodeMarker(); bool spacePrev; // previous character was a space // State for backtracking when more text has been processed than fits within // widthLimit. We track for both input (text) and output (outBuf) the offset // to the first character to discard. // // We keep track of several points: // 1 - end of previous completed word which fits // 2 - previous character which fit int last_i1, last_i2, last_o1, last_o2; if (unicode && text->getLength() % 2 != 0) { error(errSyntaxError, -1, "AnnotWidget::layoutText, bad unicode string"); return; } // skip Unicode marker on string if needed if (unicode && *i == 0) { *i = 2; } // Start decoding and copying characters, until either: // we reach the end of the string // we reach the maximum width // we reach a newline character // As we copy characters, keep track of the last full word to fit, so that we // can backtrack if we exceed the maximum width. last_i1 = last_i2 = *i; last_o1 = last_o2 = 0; spacePrev = false; outBuf->clear(); while (*i < text->getLength()) { last_i2 = *i; last_o2 = outBuf->getLength(); if (unicode) { uChar = (unsigned char)(text->getChar(*i)) << 8; uChar += (unsigned char)(text->getChar(*i + 1)); *i += 2; } else { if (noReencode) { uChar = text->getChar(*i) & 0xff; } else { uChar = pdfDocEncoding[text->getChar(*i) & 0xff]; } *i += 1; } // Explicit line break? if (uChar == '\r' || uChar == '\n') { // Treat a sequence as a single line break if (uChar == '\r' && *i < text->getLength()) { if (unicode && text->getChar(*i) == '\0' && text->getChar(*i + 1) == '\n') { *i += 2; } else if (!unicode && text->getChar(*i) == '\n') { *i += 1; } } break; } if (noReencode) { outBuf->append(uChar); } else { const CharCodeToUnicode *ccToUnicode = font.getToUnicode(); if (!ccToUnicode) { // This assumes an identity CMap. outBuf->append((uChar >> 8) & 0xff); outBuf->append(uChar & 0xff); } else if (ccToUnicode->mapToCharCode(&uChar, &c, 1)) { if (font.isCIDFont()) { auto cidFont = static_cast(&font); if (c < cidFont->getCIDToGIDLen()) { const int glyph = cidFont->getCIDToGID()[c]; if (glyph > 0 || c == 0) { outBuf->append((c >> 8) & 0xff); outBuf->append(c & 0xff); } else { if (newFontNeeded) { *newFontNeeded = true; *i -= unicode ? 2 : 1; break; } outBuf->append((c >> 8) & 0xff); outBuf->append(c & 0xff); error(errSyntaxError, -1, "AnnotWidget::layoutText, font doesn't have glyph for charcode U+{0:04uX}", c); } } else { // TODO: This assumes an identity CMap. It should be extended to // handle the general case. outBuf->append((c >> 8) & 0xff); outBuf->append(c & 0xff); } } else { // 8-bit font outBuf->append(c); } } else { if (newFontNeeded) { *newFontNeeded = true; *i -= unicode ? 2 : 1; break; } else { error(errSyntaxError, -1, "AnnotWidget::layoutText, cannot convert U+{0:04uX}", uChar); } } } // If we see a space, then we have a linebreak opportunity. if (uChar == ' ') { last_i1 = *i; if (!spacePrev) { last_o1 = last_o2; } spacePrev = true; } else { spacePrev = false; } // Compute width of character just output if (outBuf->getLength() > last_o2) { dx = 0.0; font.getNextChar(outBuf->c_str() + last_o2, outBuf->getLength() - last_o2, &c, &uAux, &uLen, &dx, &dy, &ox, &oy); w += dx; } // Current line over-full now? if (widthLimit > 0.0 && w > widthLimit) { if (last_o1 > 0) { // Back up to the previous word which fit, if there was a previous // word. *i = last_i1; outBuf->del(last_o1, outBuf->getLength() - last_o1); } else if (last_o2 > 0) { // Otherwise, back up to the previous character (in the only word on // this line) *i = last_i2; outBuf->del(last_o2, outBuf->getLength() - last_o2); } else { // Otherwise, we were trying to fit the first character; include it // anyway even if it overflows the space--no updates to make. } break; } } // If splitting input into lines because we reached the width limit, then // consume any remaining trailing spaces that would go on this line from the // input. If in doing so we reach a newline, consume that also. This code // won't run if we stopped at a newline above, since in that case w <= // widthLimit still. if (widthLimit > 0.0 && w > widthLimit) { if (unicode) { while (*i < text->getLength() && text->getChar(*i) == '\0' && text->getChar(*i + 1) == ' ') { *i += 2; } if (*i < text->getLength() && text->getChar(*i) == '\0' && text->getChar(*i + 1) == '\r') { *i += 2; } if (*i < text->getLength() && text->getChar(*i) == '\0' && text->getChar(*i + 1) == '\n') { *i += 2; } } else { while (*i < text->getLength() && text->getChar(*i) == ' ') { *i += 1; } if (*i < text->getLength() && text->getChar(*i) == '\r') { *i += 1; } if (*i < text->getLength() && text->getChar(*i) == '\n') { *i += 1; } } } // Compute the actual width and character count of the final string, based on // breakpoint, if this information is requested by the caller. if (width != nullptr || charCount != nullptr) { const char *s = outBuf->c_str(); int len = outBuf->getLength(); while (len > 0) { dx = 0.0; n = font.getNextChar(s, len, &c, &uAux, &uLen, &dx, &dy, &ox, &oy); if (n == 0) { break; } if (width != nullptr) { *width += dx; } if (charCount != nullptr) { *charCount += 1; } s += n; len -= n; } } } // Copy the given string to appearBuf, adding parentheses around it and // escaping characters as appropriate. void AnnotAppearanceBuilder::writeString(const std::string &str) { appearBuf->append('('); for (const char c : str) { if (c == '(' || c == ')' || c == '\\') { appearBuf->append('\\'); appearBuf->append(c); } else if (c < 0x20) { appearBuf->appendf("\\{0:03o}", (unsigned char)c); } else { appearBuf->append(c); } } appearBuf->append(')'); } // Draw the variable text or caption for a field. bool AnnotAppearanceBuilder::drawText(const GooString *text, const Form *form, const GooString *da, const GfxResources *resources, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect, const VariableTextQuadding quadding, XRef *xref, Dict *resourcesDict, const int flags, const int nCombs) { const bool forceZapfDingbats = flags & ForceZapfDingbatsDrawTextFlag; std::vector daToks; const GfxFont *font; double fontSize; int tfPos, tmPos; bool freeText = false; // true if text should be freed before return std::unique_ptr fontToFree = nullptr; //~ if there is no MK entry, this should use the existing content stream, //~ and only replace the marked content portion of it //~ (this is only relevant for Tx fields) // parse the default appearance string tfPos = tmPos = -1; if (da) { FormFieldText::tokenizeDA(da->toStr(), &daToks, nullptr /*searchTok*/); for (int i = 2; i < (int)daToks.size(); ++i) { if (i >= 2 && daToks[i] == "Tf") { tfPos = i - 2; } else if (i >= 6 && daToks[i] == "Tm") { tmPos = i - 6; } } } // get the font and font size font = nullptr; fontSize = 0; if (tfPos >= 0) { std::string &tok = daToks[tfPos]; if (forceZapfDingbats) { assert(xref != nullptr); if (tok != "/ZaDb") { tok = "/ZaDb"; } } if (tok.size() >= 1 && 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"); // The font variable sometimes points to an object that needs to be deleted // and sometimes not, depending on whether the call to lookupFont above fails. // When the code path right here is taken, the destructor of fontToFree // (which is a std::unique_ptr) will delete the font object at the end of this method. fontToFree = createAnnotDrawFont(xref, resourcesDict, tok.c_str() + 1, fallback); font = fontToFree.get(); } else { error(errSyntaxError, -1, "Unknown font in field's DA string"); } } } else { error(errSyntaxError, -1, "Invalid font name in 'Tf' operator in field's DA string"); } fontSize = gatof(daToks[tfPos + 1].c_str()); } else { error(errSyntaxError, -1, "Missing 'Tf' operator in field's DA string"); } if (!font) { return false; } if (tmPos < 0) { // Add fake Tm to the DA tokens tmPos = daToks.size(); daToks.insert(daToks.end(), { "1", "0", "0", "1", "0", "0", "Tm" }); } // get the border width const double borderWidth = border ? border->getWidth() : 0; // for a password field, replace all characters with asterisks if (flags & TurnTextToStarsDrawTextFlag) { int len; if (text->hasUnicodeMarker()) { len = (text->getLength() - 2) / 2; } else { len = text->getLength(); } GooString *newText = new GooString; for (int i = 0; i < len; ++i) { newText->append('*'); } text = newText; freeText = true; } // setup if (flags & EmitMarkedContentDrawTextFlag) { appearBuf->append("/Tx BMC\n"); } appearBuf->append("q\n"); auto calculateDxDy = [this, appearCharacs, rect]() -> std::tuple { const int rot = appearCharacs ? appearCharacs->getRotation() : 0; switch (rot) { case 90: appearBuf->appendf("0 1 -1 0 {0:.2f} 0 cm\n", rect->x2 - rect->x1); return { rect->y2 - rect->y1, rect->x2 - rect->x1 }; case 180: appearBuf->appendf("-1 0 0 -1 {0:.2f} {1:.2f} cm\n", rect->x2 - rect->x1, rect->y2 - rect->y1); return { rect->x2 - rect->y2, rect->y2 - rect->y1 }; case 270: appearBuf->appendf("0 -1 1 0 0 {0:.2f} cm\n", rect->y2 - rect->y1); return { rect->y2 - rect->y1, rect->x2 - rect->x1 }; default: // assume rot == 0 return { rect->x2 - rect->x1, rect->y2 - rect->y1 }; } }; const auto dxdy = calculateDxDy(); const double dx = std::get<0>(dxdy); const double dy = std::get<1>(dxdy); appearBuf->append("BT\n"); // multi-line text if (flags & MultilineDrawTextFlag) { // 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; } } daToks[tfPos + 1] = GooString().appendf("{0:.2f}", fontSize)->toStr(); } // starting y coordinate // (note: each line of text starts with a Td operator that moves // down a line) const double y = dy - 3; // set the font matrix daToks[tmPos + 4] = "0"; daToks[tmPos + 5] = GooString().appendf("{0:.2f}", y)->toStr(); // write the DA string for (const std::string &daTok : daToks) { appearBuf->append(daTok)->append(' '); } const DrawMultiLineTextResult textCommands = drawMultiLineText(*text, dx, form, *font, std::string(), fontSize, quadding, borderWidth + 2); appearBuf->append(textCommands.text); // single-line text } else { //~ replace newlines with spaces? - what does Acrobat do? // comb formatting if (nCombs > 0) { // compute comb spacing const double w = (dx - 2 * borderWidth) / nCombs; // compute font autosize if (fontSize == 0) { fontSize = dy - 2 * borderWidth; if (w < fontSize) { fontSize = w; } fontSize = floor(fontSize); daToks[tfPos + 1] = GooString().appendf("{0:.2f}", fontSize)->toStr(); } const HorizontalTextLayouter textLayouter(text, form, font, {}, forceZapfDingbats); const int charCount = std::min(textLayouter.totalCharCount(), nCombs); // compute starting text cell auto calculateX = [quadding, borderWidth, nCombs, charCount, w] { switch (quadding) { case VariableTextQuadding::leftJustified: default: return borderWidth; case VariableTextQuadding::centered: return borderWidth + (nCombs - charCount) / 2.0 * w; case VariableTextQuadding::rightJustified: return borderWidth + (nCombs - charCount) * w; } }; const double x = calculateX(); const double y = 0.5 * dy - 0.4 * fontSize; // set the font matrix daToks[tmPos + 4] = GooString().appendf("{0:.2f}", x)->toStr(); daToks[tmPos + 5] = GooString().appendf("{0:.2f}", y)->toStr(); // write the DA string for (const std::string &daTok : daToks) { appearBuf->append(daTok)->append(' '); } // write the text string int i = 0; double xPrev = w; // so that first character is placed properly for (const HorizontalTextLayouter::Data &d : textLayouter.data) { const char *s = d.text.c_str(); int len = d.text.size(); while (i < nCombs && len > 0) { CharCode code; const Unicode *uAux; int uLen, n; double char_dx, char_dy, ox, oy; const GfxFont *currentFont = font; if (!d.fontName.empty()) { appearBuf->append(" q\n"); appearBuf->appendf("/{0:s} {1:.2f} Tf\n", d.fontName.c_str(), fontSize); currentFont = form->getDefaultResources()->lookupFont(d.fontName.c_str()).get(); } char_dx = 0.0; n = currentFont->getNextChar(s, len, &code, &uAux, &uLen, &char_dx, &char_dy, &ox, &oy); char_dx *= fontSize; // center each character within its cell, by advancing the text // position the appropriate amount relative to the start of the // previous character const double combX = 0.5 * (w - char_dx); appearBuf->appendf("{0:.2f} 0 Td\n", combX - xPrev + w); GooString charBuf(s, n); writeString(charBuf.toStr()); appearBuf->append(" Tj\n"); if (!d.fontName.empty()) { appearBuf->append(" Q\n"); } i++; s += n; len -= n; xPrev = combX; } } // regular (non-comb) formatting } else { const HorizontalTextLayouter textLayouter(text, form, font, {}, forceZapfDingbats); const double usedWidthUnscaled = textLayouter.totalWidth(); // compute font autosize if (fontSize == 0) { fontSize = dy - 2 * borderWidth; if (usedWidthUnscaled > 0) { const double fontSize2 = (dx - 4 - 2 * borderWidth) / usedWidthUnscaled; if (fontSize2 < fontSize) { fontSize = fontSize2; } } fontSize = floor(fontSize); daToks[tfPos + 1] = GooString().appendf("{0:.2f}", fontSize)->toStr(); } // compute text start position const double usedWidth = usedWidthUnscaled * fontSize; auto calculateX = [quadding, borderWidth, dx, usedWidth] { switch (quadding) { case VariableTextQuadding::leftJustified: default: return borderWidth + 2; case VariableTextQuadding::centered: return (dx - usedWidth) / 2; case VariableTextQuadding::rightJustified: return dx - borderWidth - 2 - usedWidth; } }; const double x = calculateX(); const double y = 0.5 * dy - 0.4 * fontSize; // set the font matrix daToks[tmPos + 4] = GooString().appendf("{0:.2f}", x)->toStr(); daToks[tmPos + 5] = GooString().appendf("{0:.2f}", y)->toStr(); // write the DA string for (const std::string &daTok : daToks) { appearBuf->append(daTok)->append(' '); } // This newline is not neeed at all but it makes for easier reading // and our auto tests "wrongly" assume it will be there, so add it anyway appearBuf->append("\n"); // write the text strings for (const HorizontalTextLayouter::Data &d : textLayouter.data) { if (!d.fontName.empty()) { appearBuf->append(" q\n"); appearBuf->appendf("/{0:s} {1:.2f} Tf\n", d.fontName.c_str(), fontSize); } writeString(d.text); appearBuf->append(" Tj\n"); if (!d.fontName.empty()) { appearBuf->append(" Q\n"); } } } } // cleanup appearBuf->append("ET\n"); appearBuf->append("Q\n"); if (flags & EmitMarkedContentDrawTextFlag) { appearBuf->append("EMC\n"); } if (freeText) { delete text; } return true; } // Draw the variable text or caption for a field. 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; GooString *tok; GooString convertedText; const GfxFont *font; double fontSize, borderWidth, x, y; int tfPos, tmPos, i, j; std::unique_ptr fontToFree; //~ if there is no MK entry, this should use the existing content stream, //~ and only replace the marked content portion of it //~ (this is only relevant for Tx fields) // parse the default appearance string tfPos = tmPos = -1; if (da) { i = 0; while (i < da->getLength()) { while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) { ++i; } if (i < da->getLength()) { for (j = i + 1; j < da->getLength() && !Lexer::isSpace(da->getChar(j)); ++j) { ; } daToks.push_back(new GooString(da, i, j - i)); i = j; } } for (std::size_t k = 2; k < daToks.size(); ++k) { if (k >= 2 && !(daToks[k])->cmp("Tf")) { tfPos = k - 2; } else if (k >= 6 && !(daToks[k])->cmp("Tm")) { tmPos = k - 6; } } } // get the font and font size font = nullptr; fontSize = 0; if (tfPos >= 0) { tok = daToks[tfPos]; if (tok->getLength() >= 1 && tok->getChar(0) == '/') { if (!resources || !(font = resources->lookupFont(tok->c_str() + 1).get())) { if (xref != nullptr && resourcesDict != nullptr) { const char *fallback = determineFallbackFont(tok->toStr(), "Helvetica"); // The font variable sometimes points to an object that needs to be deleted // and sometimes not, depending on whether the call to lookupFont above fails. // When the code path right here is taken, the destructor of fontToFree // (which is a std::unique_ptr) will delete the font object at the end of this method. fontToFree = createAnnotDrawFont(xref, resourcesDict, tok->c_str() + 1, fallback); font = fontToFree.get(); } else { error(errSyntaxError, -1, "Unknown font in field's DA string"); } } } else { error(errSyntaxError, -1, "Invalid font name in 'Tf' operator in field's DA string"); } tok = daToks[tfPos + 1]; 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; } // get the border width borderWidth = border ? border->getWidth() : 0; // compute font autosize if (fontSize == 0) { double wMax = 0; for (i = 0; i < fieldChoice->getNumChoices(); ++i) { j = 0; if (fieldChoice->getChoice(i) == nullptr) { error(errSyntaxError, -1, "Invalid annotation listbox"); for (auto entry : daToks) { delete entry; } return false; } double w; Annot::layoutText(fieldChoice->getChoice(i), &convertedText, &j, *font, &w, 0.0, nullptr, false); if (w > wMax) { wMax = w; } } fontSize = rect->y2 - rect->y1 - 2 * borderWidth; const double fontSize2 = (rect->x2 - rect->x1 - 4 - 2 * borderWidth) / wMax; if (fontSize2 < fontSize) { fontSize = fontSize2; } fontSize = floor(fontSize); if (tfPos >= 0) { tok = daToks[tfPos + 1]; tok->clear(); tok->appendf("{0:.2f}", fontSize); } } // draw the text y = rect->y2 - rect->y1 - 1.1 * fontSize; for (i = fieldChoice->getTopIndex(); i < fieldChoice->getNumChoices(); ++i) { // setup appearBuf->append("q\n"); // draw the background if selected if (fieldChoice->isSelected(i)) { appearBuf->append("0 g f\n"); appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} re f\n", borderWidth, y - 0.2 * fontSize, rect->x2 - rect->x1 - 2 * borderWidth, 1.1 * fontSize); } // setup appearBuf->append("BT\n"); // compute text width and start position j = 0; double w; Annot::layoutText(fieldChoice->getChoice(i), &convertedText, &j, *font, &w, 0.0, nullptr, false); w *= fontSize; switch (quadding) { case VariableTextQuadding::leftJustified: default: x = borderWidth + 2; break; case VariableTextQuadding::centered: x = (rect->x2 - rect->x1 - w) / 2; break; case VariableTextQuadding::rightJustified: x = rect->x2 - rect->x1 - borderWidth - 2 - w; break; } // set the font matrix if (tmPos >= 0) { tok = daToks[tmPos + 4]; tok->clear(); tok->appendf("{0:.2f}", x); tok = daToks[tmPos + 5]; tok->clear(); tok->appendf("{0:.2f}", y); } // write the DA string for (const GooString *daTok : daToks) { appearBuf->append(daTok)->append(' '); } // write the font matrix (if not part of the DA string) if (tmPos < 0) { appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y); } // change the text color if selected if (fieldChoice->isSelected(i)) { appearBuf->append("1 g\n"); } // write the text string writeString(convertedText.toStr()); appearBuf->append(" Tj\n"); // cleanup appearBuf->append("ET\n"); appearBuf->append("Q\n"); // next line y -= 1.1 * fontSize; } for (auto entry : daToks) { delete entry; } return true; } void AnnotAppearanceBuilder::drawFieldBorder(const FormField *field, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect) { AnnotColor adjustedColor; const double w = border->getWidth(); const AnnotColor *aColor = appearCharacs->getBorderColor(); if (!aColor) { aColor = appearCharacs->getBackColor(); } if (!aColor) { return; } const double dx = rect->x2 - rect->x1; const double dy = rect->y2 - rect->y1; // radio buttons with no caption have a round border const bool hasCaption = appearCharacs->getNormalCaption() != nullptr; if (field->getType() == formButton && static_cast(field)->getButtonType() == formButtonRadio && !hasCaption) { double r = 0.5 * (dx < dy ? dx : dy); switch (border->getStyle()) { case AnnotBorder::borderDashed: appearBuf->append("["); for (double dash : border->getDash()) { appearBuf->appendf(" {0:.2f}", dash); } appearBuf->append("] 0 d\n"); // fallthrough case AnnotBorder::borderSolid: case AnnotBorder::borderUnderlined: appearBuf->appendf("{0:.2f} w\n", w); 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); 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); 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); drawCircleBottomRight(0.5 * dx, 0.5 * dy, r - 0.75 * w); break; } } else { switch (border->getStyle()) { case AnnotBorder::borderDashed: appearBuf->append("["); for (double dash : border->getDash()) { appearBuf->appendf(" {0:.2f}", dash); } appearBuf->append("] 0 d\n"); // fallthrough case AnnotBorder::borderSolid: appearBuf->appendf("{0:.2f} w\n", w); 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); appearBuf->append("0 0 m\n"); appearBuf->appendf("0 {0:.2f} l\n", dy); appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx, dy); appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, dy - w); appearBuf->appendf("{0:.2f} {1:.2f} l\n", w, dy - w); appearBuf->appendf("{0:.2f} {0:.2f} l\n", w); appearBuf->append("f\n"); adjustedColor = AnnotColor(*aColor); adjustedColor.adjustColor(border->getStyle() == AnnotBorder::borderBeveled ? -1 : 1); 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); appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, dy - w); appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, w); appearBuf->appendf("{0:.2f} {0:.2f} l\n", w); appearBuf->append("f\n"); break; case AnnotBorder::borderUnderlined: appearBuf->appendf("{0:.2f} w\n", w); setDrawColor(aColor, false); appearBuf->appendf("0 0 m {0:.2f} 0 l s\n", dx); break; } // clip to the inside of the border appearBuf->appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re W n\n", w, dx - 2 * w, dy - 2 * w); } } bool AnnotAppearanceBuilder::drawFormField(const FormField *field, const Form *form, const GfxResources *resources, const GooString *da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect, const GooString *appearState, XRef *xref, Dict *resourcesDict) { // draw the field contents switch (field->getType()) { case formButton: return drawFormFieldButton(static_cast(field), form, resources, da, border, appearCharacs, rect, appearState, xref, resourcesDict); break; case formText: return drawFormFieldText(static_cast(field), form, resources, da, border, appearCharacs, rect, xref, resourcesDict); case formChoice: return drawFormFieldChoice(static_cast(field), form, resources, da, border, appearCharacs, rect, xref, resourcesDict); break; case formSignature: return drawSignatureFieldText(static_cast(field), form, resources, da, border, appearCharacs, rect, xref, resourcesDict); break; case formUndef: default: error(errSyntaxError, -1, "Unknown field type"); } return false; } bool AnnotAppearanceBuilder::drawFormFieldButton(const FormFieldButton *field, const Form *form, const GfxResources *resources, const GooString *da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect, const GooString *appearState, XRef *xref, Dict *resourcesDict) { const GooString *caption = nullptr; if (appearCharacs) { caption = appearCharacs->getNormalCaption(); } switch (field->getButtonType()) { case formButtonRadio: { //~ Acrobat doesn't draw a caption if there is no AP dict (?) if (appearState && appearState->cmp("Off") != 0 && field->getState(appearState->c_str())) { if (caption) { return drawText(caption, form, da, resources, border, appearCharacs, rect, VariableTextQuadding::centered, xref, resourcesDict, ForceZapfDingbatsDrawTextFlag); } else if (appearCharacs) { const AnnotColor *aColor = appearCharacs->getBorderColor(); if (aColor) { const double dx = rect->x2 - rect->x1; const double dy = rect->y2 - rect->y1; setDrawColor(aColor, true); drawCircle(0.5 * dx, 0.5 * dy, 0.2 * (dx < dy ? dx : dy), true); } return true; } } } break; case formButtonPush: if (caption) { return drawText(caption, form, da, resources, border, appearCharacs, rect, VariableTextQuadding::centered, xref, resourcesDict); } break; case formButtonCheck: if (appearState && appearState->cmp("Off") != 0) { if (!caption) { GooString checkMark("3"); return drawText(&checkMark, form, da, resources, border, appearCharacs, rect, VariableTextQuadding::centered, xref, resourcesDict, ForceZapfDingbatsDrawTextFlag); } else { return drawText(caption, form, da, resources, border, appearCharacs, rect, VariableTextQuadding::centered, xref, resourcesDict, ForceZapfDingbatsDrawTextFlag); } } break; } return true; } bool AnnotAppearanceBuilder::drawFormFieldText(const FormFieldText *fieldText, const Form *form, const GfxResources *resources, const GooString *da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect, XRef *xref, Dict *resourcesDict) { VariableTextQuadding quadding; const GooString *contents; contents = fieldText->getAppearanceContent(); if (contents) { if (fieldText->hasTextQuadding()) { quadding = fieldText->getTextQuadding(); } else if (form) { quadding = form->getTextQuadding(); } else { quadding = VariableTextQuadding::leftJustified; } const int nCombs = fieldText->isComb() ? fieldText->getMaxLen() : 0; int flags = EmitMarkedContentDrawTextFlag; if (fieldText->isMultiline()) { flags = flags | MultilineDrawTextFlag; } if (fieldText->isPassword()) { flags = flags | TurnTextToStarsDrawTextFlag; } return drawText(contents, form, da, resources, border, appearCharacs, rect, quadding, xref, resourcesDict, flags, nCombs); } return true; } static void setChildDictEntryValue(Dict *parentDict, const char *childDictName, const char *childDictEntryName, const Ref childDictEntryValue, XRef *xref) { Object childDictionaryObj = parentDict->lookup(childDictName); if (!childDictionaryObj.isDict()) { childDictionaryObj = Object(new Dict(xref)); parentDict->set(childDictName, childDictionaryObj.copy()); } childDictionaryObj.dictSet(childDictEntryName, Object(childDictEntryValue)); } bool AnnotAppearanceBuilder::drawSignatureFieldText(const FormFieldSignature *field, const Form *form, const GfxResources *resources, const GooString *_da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect, XRef *xref, Dict *resourcesDict) { const GooString &contents = field->getCustomAppearanceContent(); if (contents.toStr().empty()) { return false; } if (field->getImageResource() != Ref::INVALID()) { const double width = rect->x2 - rect->x1; const double height = rect->y2 - rect->y1; static const char *imageResourceId = "SigImg"; setChildDictEntryValue(resourcesDict, "XObject", imageResourceId, field->getImageResource(), xref); 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 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; 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 */); 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 */); } return true; } void AnnotAppearanceBuilder::drawSignatureFieldText(const GooString &text, const Form *form, const DefaultAppearance &da, const AnnotBorder *border, const PDFRectangle *rect, XRef *xref, Dict *resourcesDict, double leftMargin, bool centerVertically, bool centerHorizontally) { double borderWidth = 0; append("q\n"); if (border) { borderWidth = border->getWidth(); if (borderWidth > 0) { setLineStyleForBorder(border); } } // Box size const double width = rect->x2 - rect->x1; const double height = rect->y2 - rect->y1; const double textmargin = borderWidth * 2; const double textwidth = width - 2 * textmargin; // create a Helvetica fake font std::shared_ptr font = form ? form->getDefaultResources()->lookupFont(da.getFontName().getName()) : nullptr; if (!font) { font = createAnnotDrawFont(xref, resourcesDict, da.getFontName().getName()); } // 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); const DrawMultiLineTextResult textCommands = drawMultiLineText(text, textwidth, form, *font, da.getFontName().getName(), da.getFontPtSize(), centerHorizontally ? VariableTextQuadding::centered : VariableTextQuadding::leftJustified, 0 /*borderWidth*/); double yDelta = height - textmargin; if (centerVertically) { const double outTextHeight = textCommands.nLines * da.getFontPtSize(); if (outTextHeight < height) { yDelta -= (height - outTextHeight) / 2; } } appendf("BT 1 0 0 1 {0:.2f} {1:.2f} Tm\n", leftMargin + textmargin, yDelta); append(textCommands.text.c_str()); append("ET Q\n"); } bool AnnotAppearanceBuilder::drawFormFieldChoice(const FormFieldChoice *fieldChoice, const Form *form, const GfxResources *resources, const GooString *da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect, XRef *xref, Dict *resourcesDict) { const GooString *selected; VariableTextQuadding quadding; if (fieldChoice->hasTextQuadding()) { quadding = fieldChoice->getTextQuadding(); } else if (form) { quadding = form->getTextQuadding(); } else { quadding = VariableTextQuadding::leftJustified; } if (fieldChoice->isCombo()) { selected = fieldChoice->getSelectedChoice(); if (selected) { return drawText(selected, form, da, resources, border, appearCharacs, rect, quadding, xref, resourcesDict, EmitMarkedContentDrawTextFlag); //~ Acrobat draws a popup icon on the right side } // list box } else { return drawListBox(fieldChoice, border, rect, da, resources, quadding, xref, resourcesDict); } return true; } // Should we also merge Arrays? static void recursiveMergeDicts(Dict *primary, const Dict *secondary, RefRecursionChecker *alreadySeenDicts) { for (int i = 0; i < secondary->getLength(); ++i) { const char *key = secondary->getKey(i); if (!primary->hasKey(key)) { primary->add(key, secondary->lookup(key).deepCopy()); } else { Ref primaryRef; Object primaryObj = primary->lookup(key, &primaryRef); if (primaryObj.isDict()) { Ref secondaryRef; Object secondaryObj = secondary->lookup(key, &secondaryRef); if (secondaryObj.isDict()) { if (!alreadySeenDicts->insert(primaryRef) || !alreadySeenDicts->insert(secondaryRef)) { // bad PDF return; } recursiveMergeDicts(primaryObj.getDict(), secondaryObj.getDict(), alreadySeenDicts); } } } } } static void recursiveMergeDicts(Dict *primary, const Dict *secondary) { RefRecursionChecker alreadySeenDicts; recursiveMergeDicts(primary, secondary, &alreadySeenDicts); } void AnnotWidget::generateFieldAppearance() { const GooString *da; AnnotAppearanceBuilder appearBuilder; // draw the background if (appearCharacs) { const AnnotColor *aColor = appearCharacs->getBackColor(); if (aColor) { appearBuilder.setDrawColor(aColor, true); appearBuilder.appendf("0 0 {0:.2f} {1:.2f} re f\n", rect->x2 - rect->x1, rect->y2 - rect->y1); } } // draw the border if (appearCharacs && border && border->getWidth() > 0) { appearBuilder.drawFieldBorder(field, border.get(), appearCharacs.get(), rect.get()); } da = field->getDefaultAppearance(); if (!da && form) { da = form->getDefaultAppearance(); } Dict *appearDict = new Dict(doc->getXRef()); // Let's init resourcesDictObj and resources. // In PDF 1.2, an additional entry in the field dictionary, DR, was defined. // Beginning with PDF 1.5, this entry is obsolete. // And yet Acrobat Reader seems to be taking a field's DR into account. Object resourcesDictObj; const GfxResources *resources = nullptr; GfxResources *resourcesToFree = nullptr; if (field->getObj() && field->getObj()->isDict()) { // Let's use a field's resource dictionary. resourcesDictObj = field->getObj()->dictLookup("DR"); if (resourcesDictObj.isDict()) { if (form && form->getDefaultResourcesObj()->isDict()) { resourcesDictObj = resourcesDictObj.deepCopy(); recursiveMergeDicts(resourcesDictObj.getDict(), form->getDefaultResourcesObj()->getDict()); } resourcesToFree = new GfxResources(doc->getXRef(), resourcesDictObj.getDict(), nullptr); resources = resourcesToFree; } } if (!resourcesDictObj.isDict()) { // No luck with a field's resource dictionary. Let's use an AcroForm's resource dictionary. if (form && form->getDefaultResourcesObj()->isDict()) { resourcesDictObj = form->getDefaultResourcesObj()->deepCopy(); resources = form->getDefaultResources(); } } if (!resourcesDictObj.isDict()) { resourcesDictObj = Object(new Dict(doc->getXRef())); } const bool success = appearBuilder.drawFormField(field, form, resources, da, border.get(), appearCharacs.get(), rect.get(), appearState.get(), doc->getXRef(), resourcesDictObj.getDict()); if (!success && form && da != form->getDefaultAppearance()) { da = form->getDefaultAppearance(); appearBuilder.drawFormField(field, form, resources, da, border.get(), appearCharacs.get(), rect.get(), appearState.get(), doc->getXRef(), resourcesDictObj.getDict()); } const GooString *appearBuf = appearBuilder.buffer(); // fill the appearance stream dictionary appearDict->add("Length", Object(appearBuf->getLength())); appearDict->add("Subtype", Object(objName, "Form")); Array *bbox = new Array(doc->getXRef()); bbox->add(Object(0)); bbox->add(Object(0)); bbox->add(Object(rect->x2 - rect->x1)); bbox->add(Object(rect->y2 - rect->y1)); appearDict->add("BBox", Object(bbox)); // set the resource dictionary if (resourcesDictObj.getDict()->getLength() > 0) { appearDict->set("Resources", std::move(resourcesDictObj)); } // build the appearance stream Stream *appearStream = new AutoFreeMemStream(copyString(appearBuf->c_str()), 0, appearBuf->getLength(), Object(appearDict)); if (hasBeenUpdated) { // 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)); } else { appearance = Object(appearStream); } if (resourcesToFree) { delete resourcesToFree; } } void AnnotWidget::updateAppearanceStream() { // If this the first time updateAppearanceStream() is called on this widget, // destroy the AP dictionary because we are going to create a new one. if (updatedAppearanceStream == Ref::INVALID()) { invalidateAppearance(); // Delete AP dictionary and all referenced streams } // 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()) { if (field->getType() != FormFieldType::formSignature) { return; } } // Create the new appearance generateFieldAppearance(); // Fetch the appearance stream we've just created Object obj1 = appearance.fetch(doc->getXRef()); // If this the first time updateAppearanceStream() is called on this widget, // create a new AP dictionary containing the new appearance stream. // Otherwise, just update the stream we had created previously. if (updatedAppearanceStream == Ref::INVALID()) { // Write the appearance stream updatedAppearanceStream = doc->getXRef()->addIndirectObject(obj1); // Write the AP dictionary obj1 = Object(new Dict(doc->getXRef())); obj1.dictAdd("N", Object(updatedAppearanceStream)); // Update our internal pointers to the appearance dictionary appearStreams = std::make_unique(doc, &obj1); update("AP", std::move(obj1)); } else { // Replace the existing appearance stream doc->getXRef()->setModifiedObject(&obj1, updatedAppearanceStream); } } void AnnotWidget::draw(Gfx *gfx, bool printing) { if (!isVisible(printing)) { return; } annotLocker(); // Only construct the appearance stream when // - annot doesn't have an AP or // - 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() || (field->getType() != FormFieldType::formSignature && form && form->getNeedAppearances())) { generateFieldAppearance(); } } // draw the appearance stream Object obj = appearance.fetch(gfx->getXRef()); gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation()); } void AnnotWidget::invalidateAppearance() { updatedAppearanceStream = Ref::INVALID(); Annot::invalidateAppearance(); } //------------------------------------------------------------------------ // AnnotMovie //------------------------------------------------------------------------ AnnotMovie::AnnotMovie(PDFDoc *docA, PDFRectangle *rectA, Movie *movieA) : Annot(docA, rectA) { type = typeMovie; annotObj.dictSet("Subtype", Object(objName, "Movie")); movie = movieA->copy(); // TODO: create movie dict from movieA initialize(docA, annotObj.getDict()); } AnnotMovie::AnnotMovie(PDFDoc *docA, Object &&dictObject, const Object *obj) : Annot(docA, std::move(dictObject), obj) { type = typeMovie; initialize(docA, annotObj.getDict()); } AnnotMovie::~AnnotMovie() = default; void AnnotMovie::initialize(PDFDoc *docA, Dict *dict) { Object obj1; obj1 = dict->lookup("T"); if (obj1.isString()) { title.reset(obj1.getString()->copy()); } Object movieDict = dict->lookup("Movie"); if (movieDict.isDict()) { Object obj2 = dict->lookup("A"); if (obj2.isDict()) { movie = std::make_unique(&movieDict, &obj2); } else { movie = std::make_unique(&movieDict); } if (!movie->isOk()) { movie = nullptr; ok = false; } } else { error(errSyntaxError, -1, "Bad Annot Movie"); ok = false; } } void AnnotMovie::draw(Gfx *gfx, bool printing) { if (!isVisible(printing)) { return; } annotLocker(); if (appearance.isNull() && movie->getShowPoster()) { int width, height; Object poster = movie->getPoster(); movie->getAspect(&width, &height); if (width != -1 && height != -1 && !poster.isNone()) { auto appearBuf = std::make_unique(); appearBuf->append("q\n"); appearBuf->appendf("{0:d} 0 0 {1:d} 0 0 cm\n", width, height); appearBuf->append("/MImg Do\n"); appearBuf->append("Q\n"); Dict *imgDict = new Dict(gfx->getXRef()); imgDict->set("MImg", std::move(poster)); Dict *resDict = new Dict(gfx->getXRef()); resDict->set("XObject", Object(imgDict)); Dict *formDict = new Dict(gfx->getXRef()); formDict->set("Length", Object(appearBuf->getLength())); formDict->set("Subtype", Object(objName, "Form")); formDict->set("Name", Object(objName, "FRM")); Array *bboxArray = new Array(gfx->getXRef()); bboxArray->add(Object(0)); bboxArray->add(Object(0)); bboxArray->add(Object(width)); bboxArray->add(Object(height)); formDict->set("BBox", Object(bboxArray)); Array *matrix = new Array(gfx->getXRef()); matrix->add(Object(1)); matrix->add(Object(0)); matrix->add(Object(0)); matrix->add(Object(1)); matrix->add(Object(-width / 2)); matrix->add(Object(-height / 2)); formDict->set("Matrix", Object(matrix)); formDict->set("Resources", Object(resDict)); Stream *mStream = new AutoFreeMemStream(copyString(appearBuf->c_str()), 0, appearBuf->getLength(), Object(formDict)); Dict *dict = new Dict(gfx->getXRef()); dict->set("FRM", Object(mStream)); Dict *resDict2 = new Dict(gfx->getXRef()); resDict2->set("XObject", Object(dict)); appearBuf = std::make_unique(); appearBuf->append("q\n"); appearBuf->appendf("0 0 {0:d} {1:d} re W n\n", width, height); appearBuf->append("q\n"); appearBuf->appendf("0 0 {0:d} {1:d} re W n\n", width, height); appearBuf->appendf("1 0 0 1 {0:d} {1:d} cm\n", width / 2, height / 2); appearBuf->append("/FRM Do\n"); appearBuf->append("Q\n"); appearBuf->append("Q\n"); double bbox[4]; bbox[0] = bbox[1] = 0; bbox[2] = width; bbox[3] = height; appearance = createForm(appearBuf.get(), bbox, false, resDict2); } } // draw the appearance stream Object obj = appearance.fetch(gfx->getXRef()); gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation()); } //------------------------------------------------------------------------ // AnnotScreen //------------------------------------------------------------------------ AnnotScreen::AnnotScreen(PDFDoc *docA, PDFRectangle *rectA) : Annot(docA, rectA) { type = typeScreen; annotObj.dictSet("Subtype", Object(objName, "Screen")); initialize(docA, annotObj.getDict()); } AnnotScreen::AnnotScreen(PDFDoc *docA, Object &&dictObject, const Object *obj) : Annot(docA, std::move(dictObject), obj) { type = typeScreen; initialize(docA, annotObj.getDict()); } AnnotScreen::~AnnotScreen() = default; void AnnotScreen::initialize(PDFDoc *docA, Dict *dict) { Object obj1; obj1 = dict->lookup("T"); if (obj1.isString()) { title.reset(obj1.getString()->copy()); } obj1 = dict->lookup("A"); if (obj1.isDict()) { action = LinkAction::parseAction(&obj1, doc->getCatalog()->getBaseURI()); if (action && action->getKind() == actionRendition && page == 0) { error(errSyntaxError, -1, "Invalid Rendition action: associated screen annotation without P"); action = nullptr; ok = false; } } additionalActions = dict->lookupNF("AA").copy(); obj1 = dict->lookup("MK"); if (obj1.isDict()) { appearCharacs = std::make_unique(obj1.getDict()); } } std::unique_ptr AnnotScreen::getAdditionalAction(AdditionalActionsType additionalActionType) { if (additionalActionType == actionFocusIn || additionalActionType == actionFocusOut) { // not defined for screen annotation return nullptr; } return ::getAdditionalAction(additionalActionType, &additionalActions, doc); } //------------------------------------------------------------------------ // AnnotStamp //------------------------------------------------------------------------ AnnotStamp::AnnotStamp(PDFDoc *docA, PDFRectangle *rectA) : AnnotMarkup(docA, rectA) { type = typeStamp; annotObj.dictSet("Subtype", Object(objName, "Stamp")); initialize(docA, annotObj.getDict()); } AnnotStamp::AnnotStamp(PDFDoc *docA, Object &&dictObject, const Object *obj) : AnnotMarkup(docA, std::move(dictObject), obj) { type = typeStamp; initialize(docA, annotObj.getDict()); } AnnotStamp::~AnnotStamp() { delete stampImageHelper; } void AnnotStamp::initialize(PDFDoc *docA, Dict *dict) { Object obj1 = dict->lookup("Name"); if (obj1.isName()) { icon = std::make_unique(obj1.getName()); } else { icon = std::make_unique("Draft"); } stampImageHelper = nullptr; updatedAppearanceStream = Ref::INVALID(); } void AnnotStamp::generateStampCustomAppearance() { Ref imgRef = stampImageHelper->getRef(); const std::string imgStrName = "X" + std::to_string(imgRef.num); AnnotAppearanceBuilder appearBuilder; appearBuilder.append("q\n"); appearBuilder.append("/GS0 gs\n"); appearBuilder.appendf("{0:.3f} 0 0 {1:.3f} 0 0 cm\n", rect->x2 - rect->x1, rect->y2 - rect->y1); appearBuilder.append("/"); appearBuilder.append(imgStrName.c_str()); appearBuilder.append(" Do\n"); appearBuilder.append("Q\n"); Dict *resDict = createResourcesDict(imgStrName.c_str(), Object(imgRef), "GS0", opacity, nullptr); 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); } void AnnotStamp::generateStampDefaultAppearance() { Dict *extGStateDict = nullptr; AnnotAppearanceBuilder defaultAppearanceBuilder; double stampUnscaledWidth; double stampUnscaledHeight; const char *stampCode; if (!icon->cmp("Approved")) { stampUnscaledWidth = ANNOT_STAMP_APPROVED_WIDTH; stampUnscaledHeight = ANNOT_STAMP_APPROVED_HEIGHT; stampCode = ANNOT_STAMP_APPROVED; extGStateDict = getApprovedStampExtGStateDict(doc); } else if (!icon->cmp("AsIs")) { stampUnscaledWidth = ANNOT_STAMP_AS_IS_WIDTH; stampUnscaledHeight = ANNOT_STAMP_AS_IS_HEIGHT; stampCode = ANNOT_STAMP_AS_IS; extGStateDict = getAsIsStampExtGStateDict(doc); } else if (!icon->cmp("Confidential")) { stampUnscaledWidth = ANNOT_STAMP_CONFIDENTIAL_WIDTH; stampUnscaledHeight = ANNOT_STAMP_CONFIDENTIAL_HEIGHT; stampCode = ANNOT_STAMP_CONFIDENTIAL; extGStateDict = getConfidentialStampExtGStateDict(doc); } else if (!icon->cmp("Final")) { stampUnscaledWidth = ANNOT_STAMP_FINAL_WIDTH; stampUnscaledHeight = ANNOT_STAMP_FINAL_HEIGHT; stampCode = ANNOT_STAMP_FINAL; extGStateDict = getFinalStampExtGStateDict(doc); } else if (!icon->cmp("Experimental")) { stampUnscaledWidth = ANNOT_STAMP_EXPERIMENTAL_WIDTH; stampUnscaledHeight = ANNOT_STAMP_EXPERIMENTAL_HEIGHT; stampCode = ANNOT_STAMP_EXPERIMENTAL; extGStateDict = getExperimentalStampExtGStateDict(doc); } else if (!icon->cmp("Expired")) { stampUnscaledWidth = ANNOT_STAMP_EXPIRED_WIDTH; stampUnscaledHeight = ANNOT_STAMP_EXPIRED_HEIGHT; stampCode = ANNOT_STAMP_EXPIRED; extGStateDict = getExpiredStampExtGStateDict(doc); } else if (!icon->cmp("NotApproved")) { stampUnscaledWidth = ANNOT_STAMP_NOT_APPROVED_WIDTH; stampUnscaledHeight = ANNOT_STAMP_NOT_APPROVED_HEIGHT; stampCode = ANNOT_STAMP_NOT_APPROVED; extGStateDict = getNotApprovedStampExtGStateDict(doc); } else if (!icon->cmp("NotForPublicRelease")) { stampUnscaledWidth = ANNOT_STAMP_NOT_FOR_PUBLIC_RELEASE_WIDTH; stampUnscaledHeight = ANNOT_STAMP_NOT_FOR_PUBLIC_RELEASE_HEIGHT; stampCode = ANNOT_STAMP_NOT_FOR_PUBLIC_RELEASE; extGStateDict = getNotForPublicReleaseStampExtGStateDict(doc); } else if (!icon->cmp("Sold")) { stampUnscaledWidth = ANNOT_STAMP_SOLD_WIDTH; stampUnscaledHeight = ANNOT_STAMP_SOLD_HEIGHT; stampCode = ANNOT_STAMP_SOLD; extGStateDict = getSoldStampExtGStateDict(doc); } else if (!icon->cmp("Departmental")) { stampUnscaledWidth = ANNOT_STAMP_DEPARTMENTAL_WIDTH; stampUnscaledHeight = ANNOT_STAMP_DEPARTMENTAL_HEIGHT; stampCode = ANNOT_STAMP_DEPARTMENTAL; extGStateDict = getDepartmentalStampExtGStateDict(doc); } else if (!icon->cmp("ForComment")) { stampUnscaledWidth = ANNOT_STAMP_FOR_COMMENT_WIDTH; stampUnscaledHeight = ANNOT_STAMP_FOR_COMMENT_HEIGHT; stampCode = ANNOT_STAMP_FOR_COMMENT; extGStateDict = getForCommentStampExtGStateDict(doc); } else if (!icon->cmp("ForPublicRelease")) { stampUnscaledWidth = ANNOT_STAMP_FOR_PUBLIC_RELEASE_WIDTH; stampUnscaledHeight = ANNOT_STAMP_FOR_PUBLIC_RELEASE_HEIGHT; stampCode = ANNOT_STAMP_FOR_PUBLIC_RELEASE; extGStateDict = getForPublicReleaseStampExtGStateDict(doc); } else if (!icon->cmp("TopSecret")) { stampUnscaledWidth = ANNOT_STAMP_TOP_SECRET_WIDTH; stampUnscaledHeight = ANNOT_STAMP_TOP_SECRET_HEIGHT; stampCode = ANNOT_STAMP_TOP_SECRET; extGStateDict = getTopSecretStampExtGStateDict(doc); } else { stampUnscaledWidth = ANNOT_STAMP_DRAFT_WIDTH; stampUnscaledHeight = ANNOT_STAMP_DRAFT_HEIGHT; stampCode = ANNOT_STAMP_DRAFT; extGStateDict = getDraftStampExtGStateDict(doc); } 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()); defaultAppearanceBuilder.append(stampCode); defaultAppearanceBuilder.append("Q\n"); Dict *resDict = new Dict(doc->getXRef()); resDict->add("ExtGState", Object(extGStateDict)); Object aStream = createForm(defaultAppearanceBuilder.buffer(), bboxArray, true, resDict); AnnotAppearanceBuilder appearanceBuilder; appearanceBuilder.append("/GS0 gs\n/Fm0 Do"); resDict = createResourcesDict("Fm0", std::move(aStream), "GS0", opacity, nullptr); appearance = createForm(appearanceBuilder.buffer(), bboxArray, false, resDict); } void AnnotStamp::draw(Gfx *gfx, bool printing) { if (!isVisible(printing)) { return; } annotLocker(); if (appearance.isNull()) { if (stampImageHelper != nullptr) { generateStampCustomAppearance(); } else { generateStampDefaultAppearance(); } } // draw the appearance stream Object obj = appearance.fetch(gfx->getXRef()); if (appearBBox) { gfx->drawAnnot(&obj, nullptr, color.get(), appearBBox->getPageXMin(), appearBBox->getPageYMin(), appearBBox->getPageXMax(), appearBBox->getPageYMax(), getRotation()); } else { gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation()); } } void AnnotStamp::setIcon(GooString *new_icon) { if (new_icon) { icon = std::make_unique(new_icon); } else { icon = std::make_unique(); } update("Name", Object(objName, icon->c_str())); invalidateAppearance(); } void AnnotStamp::setCustomImage(AnnotStampImageHelper *stampImageHelperA) { if (!stampImageHelperA) { return; } annotLocker(); 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)); } void AnnotStamp::clearCustomImage() { if (stampImageHelper != nullptr) { stampImageHelper->removeAnnotStampImageObject(); delete stampImageHelper; stampImageHelper = nullptr; invalidateAppearance(); } } //------------------------------------------------------------------------ // AnnotGeometry //------------------------------------------------------------------------ AnnotGeometry::AnnotGeometry(PDFDoc *docA, PDFRectangle *rectA, AnnotSubtype subType) : AnnotMarkup(docA, rectA) { switch (subType) { case typeSquare: annotObj.dictSet("Subtype", Object(objName, "Square")); break; case typeCircle: annotObj.dictSet("Subtype", Object(objName, "Circle")); break; default: assert(0 && "Invalid subtype for AnnotGeometry\n"); } initialize(docA, annotObj.getDict()); } AnnotGeometry::AnnotGeometry(PDFDoc *docA, Object &&dictObject, const Object *obj) : AnnotMarkup(docA, std::move(dictObject), obj) { // the real type will be read in initialize() type = typeSquare; initialize(docA, annotObj.getDict()); } AnnotGeometry::~AnnotGeometry() = default; void AnnotGeometry::initialize(PDFDoc *docA, Dict *dict) { Object obj1; obj1 = dict->lookup("Subtype"); if (obj1.isName()) { GooString typeName(obj1.getName()); if (!typeName.cmp("Square")) { type = typeSquare; } else if (!typeName.cmp("Circle")) { type = typeCircle; } } obj1 = dict->lookup("IC"); if (obj1.isArray()) { interiorColor = std::make_unique(obj1.getArray()); } obj1 = dict->lookup("BS"); if (obj1.isDict()) { border = std::make_unique(obj1.getDict()); } else if (!border) { border = std::make_unique(); } obj1 = dict->lookup("BE"); if (obj1.isDict()) { borderEffect = std::make_unique(obj1.getDict()); } obj1 = dict->lookup("RD"); if (obj1.isArray()) { geometryRect = parseDiffRectangle(obj1.getArray(), rect.get()); } } void AnnotGeometry::setType(AnnotSubtype new_type) { const char *typeName = nullptr; /* squelch bogus compiler warning */ switch (new_type) { case typeSquare: typeName = "Square"; break; case typeCircle: typeName = "Circle"; break; default: assert(!"Invalid subtype"); } type = new_type; update("Subtype", Object(objName, typeName)); invalidateAppearance(); } void AnnotGeometry::setInteriorColor(std::unique_ptr &&new_color) { if (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(); } void AnnotGeometry::draw(Gfx *gfx, bool printing) { double ca = 1; if (!isVisible(printing)) { return; } annotLocker(); if (appearance.isNull()) { const bool fill = interiorColor && interiorColor->getSpace() != AnnotColor::colorTransparent; ca = opacity; AnnotAppearanceBuilder appearBuilder; appearBuilder.append("q\n"); if (color) { appearBuilder.setDrawColor(color.get(), false); } double borderWidth = border->getWidth(); appearBuilder.setLineStyleForBorder(border.get()); if (interiorColor) { appearBuilder.setDrawColor(interiorColor.get(), true); } if (type == typeSquare) { appearBuilder.appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} re\n", borderWidth / 2.0, borderWidth / 2.0, (rect->x2 - rect->x1) - borderWidth, (rect->y2 - rect->y1) - borderWidth); if (fill) { if (borderWidth > 0) { appearBuilder.append("b\n"); } else { appearBuilder.append("f\n"); } } else if (borderWidth > 0) { appearBuilder.append("S\n"); } } else { const double rx { (rect->x2 - rect->x1) / 2. }; const double ry { (rect->y2 - rect->y1) / 2. }; const double bwHalf { borderWidth / 2.0 }; appearBuilder.drawEllipse(rx, ry, rx - bwHalf, ry - bwHalf, fill, borderWidth > 0); } appearBuilder.append("Q\n"); double bbox[4]; bbox[0] = bbox[1] = 0; bbox[2] = rect->x2 - rect->x1; bbox[3] = rect->y2 - rect->y1; if (ca == 1) { appearance = createForm(appearBuilder.buffer(), bbox, false, nullptr); } else { Object aStream = createForm(appearBuilder.buffer(), bbox, true, nullptr); GooString appearBuf("/GS0 gs\n/Fm0 Do"); Dict *resDict = createResourcesDict("Fm0", std::move(aStream), "GS0", ca, nullptr); appearance = createForm(&appearBuf, bbox, false, resDict); } } // draw the appearance stream Object obj = appearance.fetch(gfx->getXRef()); gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation()); } //------------------------------------------------------------------------ // AnnotPolygon //------------------------------------------------------------------------ AnnotPolygon::AnnotPolygon(PDFDoc *docA, PDFRectangle *rectA, AnnotSubtype subType) : AnnotMarkup(docA, rectA) { switch (subType) { case typePolygon: annotObj.dictSet("Subtype", Object(objName, "Polygon")); break; case typePolyLine: annotObj.dictSet("Subtype", Object(objName, "PolyLine")); break; default: assert(0 && "Invalid subtype for AnnotGeometry\n"); } // Store dummy path with one null vertex only Array *a = new Array(doc->getXRef()); a->add(Object(0.)); a->add(Object(0.)); annotObj.dictSet("Vertices", Object(a)); initialize(docA, annotObj.getDict()); } AnnotPolygon::AnnotPolygon(PDFDoc *docA, Object &&dictObject, const Object *obj) : AnnotMarkup(docA, std::move(dictObject), obj) { // the real type will be read in initialize() type = typePolygon; initialize(docA, annotObj.getDict()); } AnnotPolygon::~AnnotPolygon() = default; void AnnotPolygon::initialize(PDFDoc *docA, Dict *dict) { Object obj1; obj1 = dict->lookup("Subtype"); if (obj1.isName()) { GooString typeName(obj1.getName()); if (!typeName.cmp("Polygon")) { type = typePolygon; } else if (!typeName.cmp("PolyLine")) { type = typePolyLine; } } obj1 = dict->lookup("Vertices"); if (obj1.isArray()) { vertices = std::make_unique(obj1.getArray()); } else { vertices = std::make_unique(); error(errSyntaxError, -1, "Bad Annot Polygon Vertices"); ok = false; } obj1 = dict->lookup("LE"); if (obj1.isArray() && obj1.arrayGetLength() == 2) { Object obj2 = obj1.arrayGet(0); if (obj2.isName()) { const GooString leName(obj2.getName()); startStyle = parseAnnotLineEndingStyle(&leName); } else { startStyle = annotLineEndingNone; } obj2 = obj1.arrayGet(1); if (obj2.isName()) { const GooString leName(obj2.getName()); endStyle = parseAnnotLineEndingStyle(&leName); } else { endStyle = annotLineEndingNone; } } else { startStyle = endStyle = annotLineEndingNone; } obj1 = dict->lookup("IC"); if (obj1.isArray()) { interiorColor = std::make_unique(obj1.getArray()); } obj1 = dict->lookup("BS"); if (obj1.isDict()) { border = std::make_unique(obj1.getDict()); } else if (!border) { border = std::make_unique(); } obj1 = dict->lookup("BE"); if (obj1.isDict()) { borderEffect = std::make_unique(obj1.getDict()); } obj1 = dict->lookup("IT"); if (obj1.isName()) { const char *intentName = obj1.getName(); if (!strcmp(intentName, "PolygonCloud")) { intent = polygonCloud; } else if (!strcmp(intentName, "PolyLineDimension")) { intent = polylineDimension; } else { intent = polygonDimension; } } else { intent = polygonCloud; } } void AnnotPolygon::setType(AnnotSubtype new_type) { const char *typeName = nullptr; /* squelch bogus compiler warning */ switch (new_type) { case typePolygon: typeName = "Polygon"; break; case typePolyLine: typeName = "PolyLine"; break; default: assert(!"Invalid subtype"); } type = new_type; update("Subtype", Object(objName, typeName)); invalidateAppearance(); } void AnnotPolygon::setVertices(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))); } vertices = std::make_unique(a); update("Vertices", Object(a)); invalidateAppearance(); } void AnnotPolygon::setStartEndStyle(AnnotLineEndingStyle start, AnnotLineEndingStyle end) { startStyle = start; endStyle = end; Array *a = new Array(doc->getXRef()); a->add(Object(objName, convertAnnotLineEndingStyle(startStyle))); a->add(Object(objName, convertAnnotLineEndingStyle(endStyle))); update("LE", Object(a)); invalidateAppearance(); } void AnnotPolygon::setInteriorColor(std::unique_ptr &&new_color) { if (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(); } void AnnotPolygon::setIntent(AnnotPolygonIntent new_intent) { const char *intentName; intent = new_intent; if (new_intent == polygonCloud) { intentName = "PolygonCloud"; } else if (new_intent == polylineDimension) { intentName = "PolyLineDimension"; } else { // polygonDimension intentName = "PolygonDimension"; } update("IT", Object(objName, intentName)); } void AnnotPolygon::generatePolyLineAppearance(AnnotAppearanceBuilder *appearBuilder) { const bool fill = (bool)interiorColor; const double x1 = vertices->getX(0); const double y1 = vertices->getY(0); const double x2 = vertices->getX(1); const double y2 = vertices->getY(1); const double x3 = vertices->getX(vertices->getCoordsLength() - 2); const double y3 = vertices->getY(vertices->getCoordsLength() - 2); const double x4 = vertices->getX(vertices->getCoordsLength() - 1); const double y4 = vertices->getY(vertices->getCoordsLength() - 1); const double len_1 = sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); // length of last segment const double len_2 = sqrt((x4 - x3) * (x4 - x3) + (y4 - y3) * (y4 - y3)); // segments become positive x direction, coord1 becomes (0,0). Matrix matr1, matr2; const double angle1 = atan2(y2 - y1, x2 - x1); const double angle2 = atan2(y4 - y3, x4 - x3); matr1.m[0] = matr1.m[3] = cos(angle1); matr1.m[1] = sin(angle1); matr1.m[2] = -matr1.m[1]; matr1.m[4] = x1 - rect->x1; matr1.m[5] = y1 - rect->y1; matr2.m[0] = matr2.m[3] = cos(angle2); matr2.m[1] = sin(angle2); matr2.m[2] = -matr2.m[1]; matr2.m[4] = x3 - rect->x1; matr2.m[5] = y3 - rect->y1; const double lineEndingSize1 { std::min(6. * border->getWidth(), len_1 / 2) }; const double lineEndingSize2 { std::min(6. * border->getWidth(), len_2 / 2) }; if (vertices->getCoordsLength() != 0) { double tx, ty; matr1.transform(AnnotAppearanceBuilder::lineEndingXShorten(startStyle, lineEndingSize1), 0, &tx, &ty); appearBuilder->appendf("{0:.2f} {1:.2f} m\n", tx, ty); appearBBox->extendTo(tx, ty); for (int i = 1; i < vertices->getCoordsLength() - 1; ++i) { appearBuilder->appendf("{0:.2f} {1:.2f} l\n", vertices->getX(i) - rect->x1, vertices->getY(i) - rect->y1); appearBBox->extendTo(vertices->getX(i) - rect->x1, vertices->getY(i) - rect->y1); } if (vertices->getCoordsLength() > 1) { matr2.transform(len_2 - AnnotAppearanceBuilder::lineEndingXShorten(endStyle, lineEndingSize2), 0, &tx, &ty); appearBuilder->appendf("{0:.2f} {1:.2f} l S\n", tx, ty); appearBBox->extendTo(tx, ty); } } if (startStyle != annotLineEndingNone) { const double extendX { -AnnotAppearanceBuilder::lineEndingXExtendBBox(startStyle, lineEndingSize1) }; double tx, ty; appearBuilder->drawLineEnding(startStyle, 0, 0, -lineEndingSize1, fill, matr1); matr1.transform(extendX, lineEndingSize1 / 2., &tx, &ty); appearBBox->extendTo(tx, ty); matr1.transform(extendX, -lineEndingSize1 / 2., &tx, &ty); appearBBox->extendTo(tx, ty); } if (endStyle != annotLineEndingNone) { const double extendX { AnnotAppearanceBuilder::lineEndingXExtendBBox(endStyle, lineEndingSize2) }; double tx, ty; appearBuilder->drawLineEnding(endStyle, len_2, 0, lineEndingSize2, fill, matr2); matr2.transform(len_2 + extendX, lineEndingSize2 / 2., &tx, &ty); appearBBox->extendTo(tx, ty); matr2.transform(len_2 + extendX, -lineEndingSize2 / 2., &tx, &ty); appearBBox->extendTo(tx, ty); } } void AnnotPolygon::draw(Gfx *gfx, bool printing) { double ca = 1; if (!isVisible(printing)) { return; } annotLocker(); if (appearance.isNull()) { appearBBox = std::make_unique(rect.get()); ca = opacity; AnnotAppearanceBuilder appearBuilder; appearBuilder.append("q\n"); if (color) { appearBuilder.setDrawColor(color.get(), false); } appearBuilder.setLineStyleForBorder(border.get()); appearBBox->setBorderWidth(std::max(1., border->getWidth())); if (interiorColor) { appearBuilder.setDrawColor(interiorColor.get(), true); } if (type == typePolyLine) { generatePolyLineAppearance(&appearBuilder); } else { if (vertices->getCoordsLength() != 0) { appearBuilder.appendf("{0:.2f} {1:.2f} m\n", vertices->getX(0) - rect->x1, vertices->getY(0) - rect->y1); appearBBox->extendTo(vertices->getX(0) - rect->x1, vertices->getY(0) - rect->y1); for (int i = 1; i < vertices->getCoordsLength(); ++i) { appearBuilder.appendf("{0:.2f} {1:.2f} l\n", vertices->getX(i) - rect->x1, vertices->getY(i) - rect->y1); appearBBox->extendTo(vertices->getX(i) - rect->x1, vertices->getY(i) - rect->y1); } const double borderWidth = border->getWidth(); if (interiorColor && interiorColor->getSpace() != AnnotColor::colorTransparent) { if (borderWidth > 0) { appearBuilder.append("b\n"); } else { appearBuilder.append("f\n"); } } else if (borderWidth > 0) { appearBuilder.append("s\n"); } } } appearBuilder.append("Q\n"); double bbox[4]; appearBBox->getBBoxRect(bbox); if (ca == 1) { appearance = createForm(appearBuilder.buffer(), bbox, false, nullptr); } else { Object aStream = createForm(appearBuilder.buffer(), bbox, true, nullptr); GooString appearBuf("/GS0 gs\n/Fm0 Do"); Dict *resDict = createResourcesDict("Fm0", std::move(aStream), "GS0", ca, nullptr); appearance = createForm(&appearBuf, bbox, false, resDict); } } // draw the appearance stream Object obj = appearance.fetch(gfx->getXRef()); if (appearBBox) { gfx->drawAnnot(&obj, nullptr, color.get(), appearBBox->getPageXMin(), appearBBox->getPageYMin(), appearBBox->getPageXMax(), appearBBox->getPageYMax(), getRotation()); } else { gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation()); } } //------------------------------------------------------------------------ // AnnotCaret //------------------------------------------------------------------------ AnnotCaret::AnnotCaret(PDFDoc *docA, PDFRectangle *rectA) : AnnotMarkup(docA, rectA) { type = typeCaret; annotObj.dictSet("Subtype", Object(objName, "Caret")); initialize(docA, annotObj.getDict()); } AnnotCaret::AnnotCaret(PDFDoc *docA, Object &&dictObject, const Object *obj) : AnnotMarkup(docA, std::move(dictObject), obj) { type = typeCaret; initialize(docA, annotObj.getDict()); } AnnotCaret::~AnnotCaret() = default; void AnnotCaret::initialize(PDFDoc *docA, Dict *dict) { Object obj1; symbol = symbolNone; obj1 = dict->lookup("Sy"); if (obj1.isName()) { GooString typeName(obj1.getName()); if (!typeName.cmp("P")) { symbol = symbolP; } else if (!typeName.cmp("None")) { symbol = symbolNone; } } obj1 = dict->lookup("RD"); if (obj1.isArray()) { caretRect = parseDiffRectangle(obj1.getArray(), rect.get()); } } void AnnotCaret::setSymbol(AnnotCaretSymbol new_symbol) { symbol = new_symbol; update("Sy", Object(objName, new_symbol == symbolP ? "P" : "None")); invalidateAppearance(); } //------------------------------------------------------------------------ // AnnotInk //------------------------------------------------------------------------ AnnotInk::AnnotInk(PDFDoc *docA, PDFRectangle *rectA) : AnnotMarkup(docA, rectA) { type = typeInk; annotObj.dictSet("Subtype", Object(objName, "Ink")); // Store dummy path with one null vertex only Array *inkListArray = new Array(doc->getXRef()); Array *vList = new Array(doc->getXRef()); vList->add(Object(0.)); vList->add(Object(0.)); inkListArray->add(Object(vList)); annotObj.dictSet("InkList", Object(inkListArray)); initialize(docA, annotObj.getDict()); } AnnotInk::AnnotInk(PDFDoc *docA, Object &&dictObject, const Object *obj) : AnnotMarkup(docA, std::move(dictObject), obj) { type = typeInk; initialize(docA, annotObj.getDict()); } AnnotInk::~AnnotInk() { freeInkList(); } void AnnotInk::initialize(PDFDoc *docA, Dict *dict) { Object obj1; obj1 = dict->lookup("InkList"); if (obj1.isArray()) { parseInkList(obj1.getArray()); } else { inkListLength = 0; inkList = nullptr; error(errSyntaxError, -1, "Bad Annot Ink List"); obj1 = dict->lookup("AP"); // Although InkList is required, it should be ignored // when there is an AP entry in the Annot, so do not fail // when that happens if (!obj1.isDict()) { ok = false; } } obj1 = dict->lookup("BS"); if (obj1.isDict()) { border = std::make_unique(obj1.getDict()); } else if (!border) { border = std::make_unique(); } } void AnnotInk::writeInkList(AnnotPath **paths, int n_paths, Array *dest_array) { for (int i = 0; i < n_paths; ++i) { AnnotPath *path = paths[i]; Array *a = new Array(doc->getXRef()); for (int j = 0; j < path->getCoordsLength(); ++j) { a->add(Object(path->getX(j))); a->add(Object(path->getY(j))); } dest_array->add(Object(a)); } } void AnnotInk::parseInkList(Array *array) { inkListLength = array->getLength(); inkList = (AnnotPath **)gmallocn((inkListLength), sizeof(AnnotPath *)); memset(inkList, 0, inkListLength * sizeof(AnnotPath *)); 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]; } gfree(inkList); } } void AnnotInk::setInkList(AnnotPath **paths, int n_paths) { freeInkList(); Array *a = new Array(doc->getXRef()); writeInkList(paths, n_paths, a); parseInkList(a); annotObj.dictSet("InkList", Object(a)); invalidateAppearance(); } void AnnotInk::draw(Gfx *gfx, bool printing) { double ca = 1; if (!isVisible(printing)) { return; } annotLocker(); if (appearance.isNull()) { appearBBox = std::make_unique(rect.get()); ca = opacity; AnnotAppearanceBuilder appearBuilder; appearBuilder.append("q\n"); if (color) { appearBuilder.setDrawColor(color.get(), false); } appearBuilder.setLineStyleForBorder(border.get()); appearBBox->setBorderWidth(std::max(1., border->getWidth())); for (int i = 0; i < inkListLength; ++i) { const AnnotPath *path = inkList[i]; 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); for (int j = 1; j < path->getCoordsLength(); ++j) { appearBuilder.appendf("{0:.2f} {1:.2f} l\n", path->getX(j) - rect->x1, path->getY(j) - rect->y1); appearBBox->extendTo(path->getX(j) - rect->x1, path->getY(j) - rect->y1); } appearBuilder.append("S\n"); } } appearBuilder.append("Q\n"); double bbox[4]; appearBBox->getBBoxRect(bbox); if (ca == 1) { appearance = createForm(appearBuilder.buffer(), bbox, false, nullptr); } else { Object aStream = createForm(appearBuilder.buffer(), bbox, true, nullptr); GooString appearBuf("/GS0 gs\n/Fm0 Do"); Dict *resDict = createResourcesDict("Fm0", std::move(aStream), "GS0", ca, nullptr); appearance = createForm(&appearBuf, bbox, false, resDict); } } // draw the appearance stream Object obj = appearance.fetch(gfx->getXRef()); if (appearBBox) { gfx->drawAnnot(&obj, nullptr, color.get(), appearBBox->getPageXMin(), appearBBox->getPageYMin(), appearBBox->getPageXMax(), appearBBox->getPageYMax(), getRotation()); } else { gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation()); } } //------------------------------------------------------------------------ // AnnotFileAttachment //------------------------------------------------------------------------ AnnotFileAttachment::AnnotFileAttachment(PDFDoc *docA, PDFRectangle *rectA, GooString *filename) : AnnotMarkup(docA, rectA) { type = typeFileAttachment; annotObj.dictSet("Subtype", Object(objName, "FileAttachment")); annotObj.dictSet("FS", Object(filename->copy())); initialize(docA, annotObj.getDict()); } AnnotFileAttachment::AnnotFileAttachment(PDFDoc *docA, Object &&dictObject, const Object *obj) : AnnotMarkup(docA, std::move(dictObject), obj) { type = typeFileAttachment; initialize(docA, annotObj.getDict()); } AnnotFileAttachment::~AnnotFileAttachment() = default; void AnnotFileAttachment::initialize(PDFDoc *docA, Dict *dict) { Object objFS = dict->lookup("FS"); if (objFS.isDict() || objFS.isString()) { file = std::move(objFS); } else { error(errSyntaxError, -1, "Bad Annot File Attachment"); ok = false; } Object objName = dict->lookup("Name"); if (objName.isName()) { name = std::make_unique(objName.getName()); } else { name = std::make_unique("PushPin"); } } #define ANNOT_FILE_ATTACHMENT_AP_PUSHPIN \ "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \ "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n" \ "l 1 21.523 2.477 23 4.301 23 c h\n" \ "4.301 23 m f\n" \ "0.533333 0.541176 0.521569 RG 2 w\n" \ "1 J\n" \ "1 j\n" \ "[] 0.0 d\n" \ "4 M 5 4 m 6 5 l S\n" \ "2 w\n" \ "11 14 m 9 12 l 6 12 l 13 5 l 13 8 l 15 10 l 18 11 l 20 11 l 12 19 l 12\n" \ "17 l 11 14 l h\n" \ "11 14 m S\n" \ "3 w\n" \ "6 5 m 9 8 l S\n" \ "0.729412 0.741176 0.713725 RG 2 w\n" \ "5 5 m 6 6 l S\n" \ "2 w\n" \ "11 15 m 9 13 l 6 13 l 13 6 l 13 9 l 15 11 l 18 12 l 20 12 l 12 20 l 12\n" \ "18 l 11 15 l h\n" \ "11 15 m S\n" \ "3 w\n" \ "6 6 m 9 9 l S\n" #define ANNOT_FILE_ATTACHMENT_AP_PAPERCLIP \ "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \ "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n" \ "l 1 21.523 2.477 23 4.301 23 c h\n" \ "4.301 23 m f\n" \ "0.533333 0.541176 0.521569 RG 2 w\n" \ "1 J\n" \ "1 j\n" \ "[] 0.0 d\n" \ "4 M 16.645 12.035 m 12.418 7.707 l 10.902 6.559 6.402 11.203 8.09 12.562 c\n" \ "14.133 18.578 l 14.949 19.387 16.867 19.184 17.539 18.465 c 20.551\n" \ "15.23 l 21.191 14.66 21.336 12.887 20.426 12.102 c 13.18 4.824 l 12.18\n" \ "3.82 6.25 2.566 4.324 4.461 c 3 6.395 3.383 11.438 4.711 12.801 c 9.648\n" \ "17.887 l S\n" \ "0.729412 0.741176 0.713725 RG 16.645 13.035 m 12.418 8.707 l\n" \ "10.902 7.559 6.402 12.203 8.09 13.562 c\n" \ "14.133 19.578 l 14.949 20.387 16.867 20.184 17.539 19.465 c 20.551\n" \ "16.23 l 21.191 15.66 21.336 13.887 20.426 13.102 c 13.18 5.824 l 12.18\n" \ "4.82 6.25 3.566 4.324 5.461 c 3 7.395 3.383 12.438 4.711 13.801 c 9.648\n" \ "18.887 l S\n" #define ANNOT_FILE_ATTACHMENT_AP_GRAPH \ "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \ "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n" \ "l 1 21.523 2.477 23 4.301 23 c h\n" \ "4.301 23 m f\n" \ "0.533333 0.541176 0.521569 RG 1 w\n" \ "1 J\n" \ "0 j\n" \ "[] 0.0 d\n" \ "4 M 18.5 15.5 m 18.5 13.086 l 16.086 15.5 l 18.5 15.5 l h\n" \ "18.5 15.5 m S\n" \ "7 7 m 10 11 l 13 9 l 18 15 l S\n" \ "0.729412 0.741176 0.713725 RG 7 8 m 10 12 l 13 10 l 18 16 l S\n" \ "18.5 16.5 m 18.5 14.086 l 16.086 16.5 l 18.5 16.5 l h\n" \ "18.5 16.5 m S\n" \ "0.533333 0.541176 0.521569 RG 2 w\n" \ "1 j\n" \ "3 19 m 3 3 l 21 3 l S\n" \ "0.729412 0.741176 0.713725 RG 3 20 m 3 4 l 21 4 l S\n" #define ANNOT_FILE_ATTACHMENT_AP_TAG \ "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \ "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n" \ "l 1 21.523 2.477 23 4.301 23 c h\n" \ "4.301 23 m f\n" \ "0.533333 0.541176 0.521569 RG 0.999781 w\n" \ "1 J\n" \ "1 j\n" \ "[] 0.0 d\n" \ "4 M q 1 0 0 -1 0 24 cm\n" \ "8.492 8.707 m 8.492 9.535 7.82 10.207 6.992 10.207 c 6.164 10.207 5.492\n" \ "9.535 5.492 8.707 c 5.492 7.879 6.164 7.207 6.992 7.207 c 7.82 7.207\n" \ "8.492 7.879 8.492 8.707 c h\n" \ "8.492 8.707 m S Q\n" \ "2 w\n" \ "20.078 11.414 m 20.891 10.602 20.785 9.293 20.078 8.586 c 14.422 2.93 l\n" \ "13.715 2.223 12.301 2.223 11.594 2.93 c 3.816 10.707 l 3.109 11.414\n" \ "2.402 17.781 3.816 19.195 c 5.23 20.609 11.594 19.902 12.301 19.195 c\n" \ "20.078 11.414 l h\n" \ "20.078 11.414 m S\n" \ "0.729412 0.741176 0.713725 RG 20.078 12.414 m\n" \ "20.891 11.605 20.785 10.293 20.078 9.586 c 14.422 3.93 l\n" \ "13.715 3.223 12.301 3.223 11.594 3.93 c 3.816 11.707 l 3.109 12.414\n" \ "2.402 18.781 3.816 20.195 c 5.23 21.609 11.594 20.902 12.301 20.195 c\n" \ "20.078 12.414 l h\n" \ "20.078 12.414 m S\n" \ "0.533333 0.541176 0.521569 RG 1 w\n" \ "0 j\n" \ "11.949 13.184 m 16.191 8.941 l S\n" \ "0.729412 0.741176 0.713725 RG 11.949 14.184 m 16.191 9.941 l S\n" \ "0.533333 0.541176 0.521569 RG 14.07 6.82 m 9.828 11.062 l S\n" \ "0.729412 0.741176 0.713725 RG 14.07 7.82 m 9.828 12.062 l S\n" \ "0.533333 0.541176 0.521569 RG 6.93 15.141 m 8 20 14.27 20.5 16 20.5 c\n" \ "18.094 20.504 19.5 20 19.5 18 c 19.5 16.699 20.91 16.418 22.5 16.5 c S\n" \ "0.729412 0.741176 0.713725 RG 0.999781 w\n" \ "1 j\n" \ "q 1 0 0 -1 0 24 cm\n" \ "8.492 7.707 m 8.492 8.535 7.82 9.207 6.992 9.207 c 6.164 9.207 5.492\n" \ "8.535 5.492 7.707 c 5.492 6.879 6.164 6.207 6.992 6.207 c 7.82 6.207\n" \ "8.492 6.879 8.492 7.707 c h\n" \ "8.492 7.707 m S Q\n" \ "1 w\n" \ "0 j\n" \ "6.93 16.141 m 8 21 14.27 21.5 16 21.5 c 18.094 21.504 19.5 21 19.5 19 c\n" \ "19.5 17.699 20.91 17.418 22.5 17.5 c S\n" void AnnotFileAttachment::draw(Gfx *gfx, bool printing) { double ca = 1; if (!isVisible(printing)) { return; } annotLocker(); if (appearance.isNull()) { ca = opacity; AnnotAppearanceBuilder appearBuilder; appearBuilder.append("q\n"); if (color) { appearBuilder.setDrawColor(color.get(), true); } else { appearBuilder.append("1 1 1 rg\n"); } if (!name->cmp("PushPin")) { appearBuilder.append(ANNOT_FILE_ATTACHMENT_AP_PUSHPIN); } else if (!name->cmp("Paperclip")) { appearBuilder.append(ANNOT_FILE_ATTACHMENT_AP_PAPERCLIP); } else if (!name->cmp("Graph")) { appearBuilder.append(ANNOT_FILE_ATTACHMENT_AP_GRAPH); } else if (!name->cmp("Tag")) { appearBuilder.append(ANNOT_FILE_ATTACHMENT_AP_TAG); } appearBuilder.append("Q\n"); double bbox[4]; bbox[0] = bbox[1] = 0; bbox[2] = bbox[3] = 24; if (ca == 1) { appearance = createForm(appearBuilder.buffer(), bbox, false, nullptr); } else { Object aStream = createForm(appearBuilder.buffer(), bbox, true, nullptr); GooString appearBuf("/GS0 gs\n/Fm0 Do"); Dict *resDict = createResourcesDict("Fm0", std::move(aStream), "GS0", ca, nullptr); appearance = createForm(&appearBuf, bbox, false, resDict); } } // draw the appearance stream Object obj = appearance.fetch(gfx->getXRef()); gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation()); } //------------------------------------------------------------------------ // AnnotSound //------------------------------------------------------------------------ AnnotSound::AnnotSound(PDFDoc *docA, PDFRectangle *rectA, Sound *soundA) : AnnotMarkup(docA, rectA) { type = typeSound; annotObj.dictSet("Subtype", Object(objName, "Sound")); annotObj.dictSet("Sound", soundA->getObject()->copy()); initialize(docA, annotObj.getDict()); } AnnotSound::AnnotSound(PDFDoc *docA, Object &&dictObject, const Object *obj) : AnnotMarkup(docA, std::move(dictObject), obj) { type = typeSound; initialize(docA, annotObj.getDict()); } AnnotSound::~AnnotSound() = default; void AnnotSound::initialize(PDFDoc *docA, Dict *dict) { Object obj1 = dict->lookup("Sound"); sound = Sound::parseSound(&obj1); if (!sound) { error(errSyntaxError, -1, "Bad Annot Sound"); ok = false; } obj1 = dict->lookup("Name"); if (obj1.isName()) { name = std::make_unique(obj1.getName()); } else { name = std::make_unique("Speaker"); } } #define ANNOT_SOUND_AP_SPEAKER \ "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \ "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n" \ "l 1 21.523 2.477 23 4.301 23 c h\n" \ "4.301 23 m f\n" \ "0.533333 0.541176 0.521569 RG 2 w\n" \ "0 J\n" \ "1 j\n" \ "[] 0.0 d\n" \ "4 M 4 14 m 4.086 8.043 l 7 8 l 11 4 l 11 18 l 7 14 l 4 14 l h\n" \ "4 14 m S\n" \ "1 w\n" \ "1 J\n" \ "0 j\n" \ "13.699 15.398 m 14.699 13.398 14.699 9.398 13.699 7.398 c S\n" \ "18.199 19.398 m 21.199 17.398 21.199 5.398 18.199 3.398 c S\n" \ "16 17.398 m 18 16.398 18 7.398 16 5.398 c S\n" \ "0.729412 0.741176 0.713725 RG 2 w\n" \ "0 J\n" \ "1 j\n" \ "4 15 m 4.086 9.043 l 7 9 l 11 5 l 11 19 l 7 15 l 4 15 l h\n" \ "4 15 m S\n" \ "1 w\n" \ "1 J\n" \ "0 j\n" \ "13.699 16 m 14.699 14 14.699 10 13.699 8 c S\n" \ "18.199 20 m 21.199 18 21.199 6 18.199 4 c S\n" \ "16 18 m 18 17 18 8 16 6 c S\n" #define ANNOT_SOUND_AP_MIC \ "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n" \ "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n" \ "l 1 21.523 2.477 23 4.301 23 c h\n" \ "4.301 23 m f\n" \ "0.533333 0.541176 0.521569 RG 2 w\n" \ "1 J\n" \ "0 j\n" \ "[] 0.0 d\n" \ "4 M 12 20 m 12 20 l 13.656 20 15 18.656 15 17 c 15 13 l 15 11.344 13.656 10\n" \ "12 10 c 12 10 l 10.344 10 9 11.344 9 13 c 9 17 l 9 18.656 10.344 20 12\n" \ "20 c h\n" \ "12 20 m S\n" \ "1 w\n" \ "17.5 14.5 m 17.5 11.973 l 17.5 8.941 15.047 6.5 12 6.5 c 8.953 6.5 6.5\n" \ "8.941 6.5 11.973 c 6.5 14.5 l S\n" \ "2 w\n" \ "0 J\n" \ "12 6.52 m 12 3 l S\n" \ "1 J\n" \ "8 3 m 16 3 l S\n" \ "0.729412 0.741176 0.713725 RG 12 21 m 12 21 l 13.656 21 15 19.656 15 18 c\n" \ "15 14 l 15 12.344 13.656 11 12 11 c 12 11 l 10.344 11 9 12.344 9 14 c\n" \ "9 18 l 9 19.656 10.344 21 12 21 c h\n" \ "12 21 m S\n" \ "1 w\n" \ "17.5 15.5 m 17.5 12.973 l 17.5 9.941 15.047 7.5 12 7.5 c 8.953 7.5 6.5\n" \ "9.941 6.5 12.973 c 6.5 15.5 l S\n" \ "2 w\n" \ "0 J\n" \ "12 7.52 m 12 4 l S\n" \ "1 J\n" \ "8 4 m 16 4 l S\n" void AnnotSound::draw(Gfx *gfx, bool printing) { Object obj; double ca = 1; if (!isVisible(printing)) { return; } annotLocker(); if (appearance.isNull()) { ca = opacity; AnnotAppearanceBuilder appearBuilder; appearBuilder.append("q\n"); if (color) { appearBuilder.setDrawColor(color.get(), true); } else { appearBuilder.append("1 1 1 rg\n"); } if (!name->cmp("Speaker")) { appearBuilder.append(ANNOT_SOUND_AP_SPEAKER); } else if (!name->cmp("Mic")) { appearBuilder.append(ANNOT_SOUND_AP_MIC); } appearBuilder.append("Q\n"); double bbox[4]; bbox[0] = bbox[1] = 0; bbox[2] = bbox[3] = 24; if (ca == 1) { appearance = createForm(appearBuilder.buffer(), bbox, false, nullptr); } else { Object aStream = createForm(appearBuilder.buffer(), bbox, true, nullptr); GooString appearBuf("/GS0 gs\n/Fm0 Do"); Dict *resDict = createResourcesDict("Fm0", std::move(aStream), "GS0", ca, nullptr); appearance = createForm(&appearBuf, bbox, false, resDict); } } // draw the appearance stream obj = appearance.fetch(gfx->getXRef()); gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation()); } //------------------------------------------------------------------------ // Annot3D //------------------------------------------------------------------------ Annot3D::Annot3D(PDFDoc *docA, PDFRectangle *rectA) : Annot(docA, rectA) { type = type3D; annotObj.dictSet("Subtype", Object(objName, "3D")); initialize(docA, annotObj.getDict()); } Annot3D::Annot3D(PDFDoc *docA, Object &&dictObject, const Object *obj) : Annot(docA, std::move(dictObject), obj) { type = type3D; initialize(docA, annotObj.getDict()); } Annot3D::~Annot3D() = default; void Annot3D::initialize(PDFDoc *docA, Dict *dict) { Object obj1 = dict->lookup("3DA"); if (obj1.isDict()) { activation = std::make_unique(obj1.getDict()); } } Annot3D::Activation::Activation(Dict *dict) { Object obj1; obj1 = dict->lookup("A"); if (obj1.isName()) { const char *name = obj1.getName(); if (!strcmp(name, "PO")) { aTrigger = aTriggerPageOpened; } else if (!strcmp(name, "PV")) { aTrigger = aTriggerPageVisible; } else if (!strcmp(name, "XA")) { aTrigger = aTriggerUserAction; } else { aTrigger = aTriggerUnknown; } } else { aTrigger = aTriggerUnknown; } obj1 = dict->lookup("AIS"); if (obj1.isName()) { const char *name = obj1.getName(); if (!strcmp(name, "I")) { aState = aStateEnabled; } else if (!strcmp(name, "L")) { aState = aStateDisabled; } else { aState = aStateUnknown; } } else { aState = aStateUnknown; } obj1 = dict->lookup("D"); if (obj1.isName()) { const char *name = obj1.getName(); if (!strcmp(name, "PC")) { dTrigger = dTriggerPageClosed; } else if (!strcmp(name, "PI")) { dTrigger = dTriggerPageInvisible; } else if (!strcmp(name, "XD")) { dTrigger = dTriggerUserAction; } else { dTrigger = dTriggerUnknown; } } else { dTrigger = dTriggerUnknown; } obj1 = dict->lookup("DIS"); if (obj1.isName()) { const char *name = obj1.getName(); if (!strcmp(name, "U")) { dState = dStateUninstantiaded; } else if (!strcmp(name, "I")) { dState = dStateInstantiated; } else if (!strcmp(name, "L")) { dState = dStateLive; } else { dState = dStateUnknown; } } else { dState = dStateUnknown; } displayToolbar = dict->lookup("TB").getBoolWithDefaultValue(true); displayNavigation = dict->lookup("NP").getBoolWithDefaultValue(false); } //------------------------------------------------------------------------ // AnnotRichMedia //------------------------------------------------------------------------ AnnotRichMedia::AnnotRichMedia(PDFDoc *docA, PDFRectangle *rectA) : Annot(docA, rectA) { type = typeRichMedia; annotObj.dictSet("Subtype", Object(objName, "RichMedia")); initialize(docA, annotObj.getDict()); } AnnotRichMedia::AnnotRichMedia(PDFDoc *docA, Object &&dictObject, const Object *obj) : Annot(docA, std::move(dictObject), obj) { type = typeRichMedia; initialize(docA, annotObj.getDict()); } AnnotRichMedia::~AnnotRichMedia() = default; void AnnotRichMedia::initialize(PDFDoc *docA, Dict *dict) { Object obj1 = dict->lookup("RichMediaContent"); if (obj1.isDict()) { content = std::make_unique(obj1.getDict()); } obj1 = dict->lookup("RichMediaSettings"); if (obj1.isDict()) { settings = std::make_unique(obj1.getDict()); } } AnnotRichMedia::Content *AnnotRichMedia::getContent() const { return content.get(); } AnnotRichMedia::Settings *AnnotRichMedia::getSettings() const { return settings.get(); } AnnotRichMedia::Settings::Settings(Dict *dict) { Object obj1 = dict->lookup("Activation"); if (obj1.isDict()) { activation = std::make_unique(obj1.getDict()); } obj1 = dict->lookup("Deactivation"); if (obj1.isDict()) { deactivation = std::make_unique(obj1.getDict()); } } AnnotRichMedia::Settings::~Settings() = default; AnnotRichMedia::Activation *AnnotRichMedia::Settings::getActivation() const { return activation.get(); } AnnotRichMedia::Deactivation *AnnotRichMedia::Settings::getDeactivation() const { return deactivation.get(); } AnnotRichMedia::Activation::Activation(Dict *dict) { Object obj1 = dict->lookup("Condition"); if (obj1.isName()) { const char *name = obj1.getName(); if (!strcmp(name, "PO")) { condition = conditionPageOpened; } else if (!strcmp(name, "PV")) { condition = conditionPageVisible; } else if (!strcmp(name, "XA")) { condition = conditionUserAction; } else { condition = conditionUserAction; } } else { condition = conditionUserAction; } } AnnotRichMedia::Activation::Condition AnnotRichMedia::Activation::getCondition() const { return condition; } AnnotRichMedia::Deactivation::Deactivation(Dict *dict) { Object obj1 = dict->lookup("Condition"); if (obj1.isName()) { const char *name = obj1.getName(); if (!strcmp(name, "PC")) { condition = conditionPageClosed; } else if (!strcmp(name, "PI")) { condition = conditionPageInvisible; } else if (!strcmp(name, "XD")) { condition = conditionUserAction; } else { condition = conditionUserAction; } } else { condition = conditionUserAction; } } AnnotRichMedia::Deactivation::Condition AnnotRichMedia::Deactivation::getCondition() const { return condition; } AnnotRichMedia::Content::Content(Dict *dict) { Object obj1 = dict->lookup("Configurations"); if (obj1.isArray()) { nConfigurations = obj1.arrayGetLength(); configurations = (Configuration **)gmallocn(nConfigurations, sizeof(Configuration *)); for (int i = 0; i < nConfigurations; ++i) { Object obj2 = obj1.arrayGet(i); if (obj2.isDict()) { configurations[i] = new AnnotRichMedia::Configuration(obj2.getDict()); } else { configurations[i] = nullptr; } } } 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 = (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); if (!objKey.isString() || objVal.isNull()) { error(errSyntaxError, -1, "Bad Annot Asset"); continue; } assets[nAssets] = new AnnotRichMedia::Asset; assets[nAssets]->name = std::make_unique(objKey.getString()); assets[nAssets]->fileSpec = std::move(objVal); ++nAssets; } } } } 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); } } int AnnotRichMedia::Content::getConfigurationsCount() const { return nConfigurations; } AnnotRichMedia::Configuration *AnnotRichMedia::Content::getConfiguration(int index) const { if (index < 0 || index >= nConfigurations) { return nullptr; } return configurations[index]; } int AnnotRichMedia::Content::getAssetsCount() const { return nAssets; } AnnotRichMedia::Asset *AnnotRichMedia::Content::getAsset(int index) const { if (index < 0 || index >= nAssets) { return nullptr; } return assets[index]; } AnnotRichMedia::Asset::Asset() = default; AnnotRichMedia::Asset::~Asset() = default; const GooString *AnnotRichMedia::Asset::getName() const { return name.get(); } Object *AnnotRichMedia::Asset::getFileSpec() const { return const_cast(&fileSpec); } AnnotRichMedia::Configuration::Configuration(Dict *dict) { Object obj1 = dict->lookup("Instances"); if (obj1.isArray()) { nInstances = obj1.arrayGetLength(); instances = (Instance **)gmallocn(nInstances, sizeof(Instance *)); for (int i = 0; i < nInstances; ++i) { Object obj2 = obj1.arrayGet(i); if (obj2.isDict()) { instances[i] = new AnnotRichMedia::Instance(obj2.getDict()); } else { instances[i] = nullptr; } } } else { instances = nullptr; } obj1 = dict->lookup("Name"); if (obj1.isString()) { name = std::make_unique(obj1.getString()); } obj1 = dict->lookup("Subtype"); if (obj1.isName()) { const char *subtypeName = obj1.getName(); if (!strcmp(subtypeName, "3D")) { type = type3D; } else if (!strcmp(subtypeName, "Flash")) { type = typeFlash; } else if (!strcmp(subtypeName, "Sound")) { type = typeSound; } else if (!strcmp(subtypeName, "Video")) { type = typeVideo; } 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 break; } } } } } } AnnotRichMedia::Configuration::~Configuration() { if (instances) { for (int i = 0; i < nInstances; ++i) { delete instances[i]; } gfree(instances); } } int AnnotRichMedia::Configuration::getInstancesCount() const { return nInstances; } AnnotRichMedia::Instance *AnnotRichMedia::Configuration::getInstance(int index) const { if (index < 0 || index >= nInstances) { return nullptr; } return instances[index]; } const GooString *AnnotRichMedia::Configuration::getName() const { return name.get(); } AnnotRichMedia::Configuration::Type AnnotRichMedia::Configuration::getType() const { return type; } AnnotRichMedia::Instance::Instance(Dict *dict) { Object obj1 = dict->lookup("Subtype"); const char *name = obj1.isName() ? obj1.getName() : ""; if (!strcmp(name, "3D")) { type = type3D; } else if (!strcmp(name, "Flash")) { type = typeFlash; } else if (!strcmp(name, "Sound")) { type = typeSound; } else if (!strcmp(name, "Video")) { type = typeVideo; } else { type = typeFlash; } obj1 = dict->lookup("Params"); if (obj1.isDict()) { params = std::make_unique(obj1.getDict()); } } AnnotRichMedia::Instance::~Instance() = default; AnnotRichMedia::Instance::Type AnnotRichMedia::Instance::getType() const { return type; } AnnotRichMedia::Params *AnnotRichMedia::Instance::getParams() const { return params.get(); } AnnotRichMedia::Params::Params(Dict *dict) { Object obj1 = dict->lookup("FlashVars"); if (obj1.isString()) { flashVars = std::make_unique(obj1.getString()); } } AnnotRichMedia::Params::~Params() = default; const GooString *AnnotRichMedia::Params::getFlashVars() const { return flashVars.get(); } //------------------------------------------------------------------------ // Annots //------------------------------------------------------------------------ Annots::Annots(PDFDoc *docA, int page, Object *annotsObj) { Annot *annot; int i; doc = docA; if (annotsObj->isArray()) { for (i = 0; i < annotsObj->arrayGetLength(); ++i) { // get the Ref to this annot and pass it to Annot constructor // this way, it'll be possible for the annot to retrieve the corresponding // form widget Object obj1 = annotsObj->arrayGet(i); if (obj1.isDict()) { const Object &obj2 = annotsObj->arrayGetNF(i); annot = createAnnot(std::move(obj1), &obj2); if (annot) { if (annot->isOk()) { annot->setPage(page, false); // Don't change /P appendAnnot(annot); } annot->decRefCnt(); } } } } } void Annots::appendAnnot(Annot *annot) { if (annot && annot->isOk()) { annots.push_back(annot); annot->incRefCnt(); } } bool Annots::removeAnnot(Annot *annot) { auto idx = std::find(annots.begin(), annots.end(), annot); if (idx == annots.end()) { return false; } else { annot->decRefCnt(); annots.erase(idx); return true; } } Annot *Annots::createAnnot(Object &&dictObject, const Object *obj) { Annot *annot = nullptr; Object obj1 = dictObject.dictLookup("Subtype"); if (obj1.isName()) { const char *typeName = obj1.getName(); if (!strcmp(typeName, "Text")) { annot = new AnnotText(doc, std::move(dictObject), obj); } else if (!strcmp(typeName, "Link")) { annot = new AnnotLink(doc, std::move(dictObject), obj); } else if (!strcmp(typeName, "FreeText")) { annot = new AnnotFreeText(doc, std::move(dictObject), obj); } else if (!strcmp(typeName, "Line")) { annot = new AnnotLine(doc, std::move(dictObject), obj); } else if (!strcmp(typeName, "Square")) { annot = new AnnotGeometry(doc, std::move(dictObject), obj); } else if (!strcmp(typeName, "Circle")) { annot = new AnnotGeometry(doc, std::move(dictObject), obj); } else if (!strcmp(typeName, "Polygon")) { annot = new AnnotPolygon(doc, std::move(dictObject), obj); } else if (!strcmp(typeName, "PolyLine")) { annot = new AnnotPolygon(doc, std::move(dictObject), obj); } else if (!strcmp(typeName, "Highlight")) { annot = new AnnotTextMarkup(doc, std::move(dictObject), obj); } else if (!strcmp(typeName, "Underline")) { annot = new AnnotTextMarkup(doc, std::move(dictObject), obj); } else if (!strcmp(typeName, "Squiggly")) { annot = new AnnotTextMarkup(doc, std::move(dictObject), obj); } else if (!strcmp(typeName, "StrikeOut")) { annot = new AnnotTextMarkup(doc, std::move(dictObject), obj); } else if (!strcmp(typeName, "Stamp")) { annot = new AnnotStamp(doc, std::move(dictObject), obj); } else if (!strcmp(typeName, "Caret")) { annot = new AnnotCaret(doc, std::move(dictObject), obj); } else if (!strcmp(typeName, "Ink")) { annot = new AnnotInk(doc, std::move(dictObject), obj); } else if (!strcmp(typeName, "FileAttachment")) { annot = new AnnotFileAttachment(doc, std::move(dictObject), obj); } else if (!strcmp(typeName, "Sound")) { annot = new AnnotSound(doc, std::move(dictObject), obj); } else if (!strcmp(typeName, "Movie")) { annot = new AnnotMovie(doc, std::move(dictObject), obj); } else if (!strcmp(typeName, "Widget")) { // Find the annot in forms if (obj->isRef()) { Form *form = doc->getCatalog()->getForm(); if (form) { FormWidget *widget = form->findWidgetByRef(obj->getRef()); if (widget) { annot = widget->getWidgetAnnotation(); annot->incRefCnt(); } } } if (!annot) { annot = new AnnotWidget(doc, std::move(dictObject), obj); } } else if (!strcmp(typeName, "Screen")) { annot = new AnnotScreen(doc, std::move(dictObject), obj); } else if (!strcmp(typeName, "PrinterMark")) { annot = new Annot(doc, std::move(dictObject), obj); } else if (!strcmp(typeName, "TrapNet")) { annot = new Annot(doc, std::move(dictObject), obj); } else if (!strcmp(typeName, "Watermark")) { annot = new Annot(doc, std::move(dictObject), obj); } else if (!strcmp(typeName, "3D")) { annot = new Annot3D(doc, std::move(dictObject), obj); } else if (!strcmp(typeName, "RichMedia")) { annot = new AnnotRichMedia(doc, std::move(dictObject), obj); } else if (!strcmp(typeName, "Popup")) { /* Popup annots are already handled by markup annots * Here we only care about popup annots without a * markup annotation associated */ Object obj2 = dictObject.dictLookup("Parent"); if (obj2.isNull()) { annot = new AnnotPopup(doc, std::move(dictObject), obj); } else { annot = nullptr; } } else { annot = new Annot(doc, std::move(dictObject), obj); } } return annot; } Annot *Annots::findAnnot(Ref *ref) { for (auto *annot : annots) { if (annot->match(ref)) { return annot; } } return nullptr; } Annots::~Annots() { for (auto *annot : annots) { annot->decRefCnt(); } } //------------------------------------------------------------------------ // AnnotAppearanceBuilder //------------------------------------------------------------------------ AnnotAppearanceBuilder::AnnotAppearanceBuilder() : appearBuf(new GooString()) { } AnnotAppearanceBuilder::~AnnotAppearanceBuilder() { delete appearBuf; } void AnnotAppearanceBuilder::append(const char *text) { appearBuf->append(text); } void AnnotAppearanceBuilder::appendf(const char *fmt, ...) GOOSTRING_FORMAT { va_list argList; va_start(argList, fmt); appearBuf->appendfv(fmt, argList); va_end(argList); } const GooString *AnnotAppearanceBuilder::buffer() const { return appearBuf; } poppler-24.02.0/poppler/Annot.h000066400000000000000000001650731455701731300162730ustar00rootroot00000000000000//======================================================================== // // Annot.h // // Copyright 2000-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 Scott Turner // Copyright (C) 2007, 2008 Julien Rebetez // Copyright (C) 2007-2011, 2013, 2015, 2018 Carlos Garcia Campos // Copyright (C) 2007, 2008 Iñigo Martínez // Copyright (C) 2008 Michael Vrable // Copyright (C) 2008 Hugo Mercier // Copyright (C) 2008 Pino Toscano // Copyright (C) 2008 Tomas Are Haavet // Copyright (C) 2009-2011, 2013, 2016-2023 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, 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 // Copyright (C) 2018, 2020, 2022 Oliver Sander // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019 Umang Malik // Copyright (C) 2019 João Netto // Copyright (C) 2020 Nelson Benítez León // Copyright (C) 2020 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) 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 // // 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 ANNOT_H #define ANNOT_H #include #include #include #include #include "AnnotStampImageHelper.h" #include "Object.h" #include "poppler_private_export.h" class XRef; class Gfx; class CharCodeToUnicode; class GfxFont; class GfxResources; class Page; class PDFDoc; class Form; class FormWidget; class FormField; class FormFieldButton; class FormFieldText; class FormFieldChoice; class FormFieldSignature; class PDFRectangle; class Movie; class LinkAction; class Sound; class FileSpec; enum AnnotLineEndingStyle { annotLineEndingSquare, // Square annotLineEndingCircle, // Circle annotLineEndingDiamond, // Diamond annotLineEndingOpenArrow, // OpenArrow annotLineEndingClosedArrow, // ClosedArrow annotLineEndingNone, // None annotLineEndingButt, // Butt annotLineEndingROpenArrow, // ROpenArrow annotLineEndingRClosedArrow, // RClosedArrow annotLineEndingSlash // Slash }; enum AnnotExternalDataType { annotExternalDataMarkupUnknown, annotExternalDataMarkup3D // Markup3D }; enum class VariableTextQuadding { leftJustified, centered, rightJustified }; //------------------------------------------------------------------------ // AnnotCoord //------------------------------------------------------------------------ class AnnotCoord { public: AnnotCoord() : x(0), y(0) { } AnnotCoord(double _x, double _y) : x(_x), y(_y) { } double getX() const { return x; } double getY() const { return y; } protected: double x, y; }; //------------------------------------------------------------------------ // AnnotPath //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT AnnotPath { public: AnnotPath(); explicit AnnotPath(Array *array); explicit AnnotPath(std::vector &&coords); ~AnnotPath(); AnnotPath(const AnnotPath &) = delete; AnnotPath &operator=(const AnnotPath &other) = delete; double getX(int coord) const; double getY(int coord) const; AnnotCoord *getCoord(int coord); int getCoordsLength() const { return coords.size(); } protected: std::vector coords; void parsePathArray(Array *array); }; //------------------------------------------------------------------------ // AnnotCalloutLine //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT AnnotCalloutLine { public: AnnotCalloutLine(double x1, double y1, double x2, double y2); virtual ~AnnotCalloutLine(); AnnotCalloutLine(const AnnotCalloutLine &) = delete; AnnotCalloutLine &operator=(const AnnotCalloutLine &other) = delete; double getX1() const { return coord1.getX(); } double getY1() const { return coord1.getY(); } double getX2() const { return coord2.getX(); } double getY2() const { return coord2.getY(); } protected: AnnotCoord coord1, coord2; }; //------------------------------------------------------------------------ // AnnotCalloutMultiLine //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT AnnotCalloutMultiLine : public AnnotCalloutLine { public: AnnotCalloutMultiLine(double x1, double y1, double x2, double y2, double x3, double y3); ~AnnotCalloutMultiLine() override; double getX3() const { return coord3.getX(); } double getY3() const { return coord3.getY(); } protected: AnnotCoord coord3; }; //------------------------------------------------------------------------ // AnnotBorderEffect //------------------------------------------------------------------------ class AnnotBorderEffect { public: enum AnnotBorderEffectType { borderEffectNoEffect, // S borderEffectCloudy // C }; explicit AnnotBorderEffect(Dict *dict); AnnotBorderEffectType getEffectType() const { return effectType; } double getIntensity() const { return intensity; } private: AnnotBorderEffectType effectType; // S (Default S) double intensity; // I (Default 0) }; //------------------------------------------------------------------------ // AnnotQuadrilateral //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT AnnotQuadrilaterals { public: class POPPLER_PRIVATE_EXPORT AnnotQuadrilateral { public: AnnotQuadrilateral(); AnnotQuadrilateral(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4); AnnotCoord coord1, coord2, coord3, coord4; }; AnnotQuadrilaterals(Array *array, PDFRectangle *rect); AnnotQuadrilaterals(std::unique_ptr &&quads, int quadsLength); ~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); int getQuadrilateralsLength() const { return quadrilateralsLength; } protected: std::unique_ptr quadrilaterals; int quadrilateralsLength; }; //------------------------------------------------------------------------ // AnnotBorder //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT AnnotBorder { public: enum AnnotBorderType { typeArray, typeBS }; enum AnnotBorderStyle { borderSolid, // Solid borderDashed, // Dashed borderBeveled, // Beveled borderInset, // Inset borderUnderlined // Underlined }; virtual ~AnnotBorder(); AnnotBorder(const AnnotBorder &) = delete; AnnotBorder &operator=(const AnnotBorder &other) = delete; virtual void setWidth(double new_width) { width = new_width; } virtual AnnotBorderType getType() const = 0; virtual double getWidth() const { return width; } virtual const std::vector &getDash() const { return dash; } virtual AnnotBorderStyle getStyle() const { return style; } virtual Object writeToObject(XRef *xref) const = 0; virtual std::unique_ptr copy() const = 0; protected: AnnotBorder(); bool parseDashArray(Object *dashObj); AnnotBorderType type; double width; static const int DASH_LIMIT = 10; // implementation note 82 in Appendix H. std::vector dash; AnnotBorderStyle style; }; //------------------------------------------------------------------------ // AnnotBorderArray //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT AnnotBorderArray : public AnnotBorder { public: AnnotBorderArray(); explicit AnnotBorderArray(Array *array); void setHorizontalCorner(double hc) { horizontalCorner = hc; } void setVerticalCorner(double vc) { verticalCorner = vc; } double getHorizontalCorner() const { return horizontalCorner; } double getVerticalCorner() const { return verticalCorner; } std::unique_ptr copy() const override; private: AnnotBorderType getType() const override { return typeArray; } Object writeToObject(XRef *xref) const override; double horizontalCorner; // (Default 0) double verticalCorner; // (Default 0) // double width; // (Default 1) (inherited from AnnotBorder) }; //------------------------------------------------------------------------ // AnnotBorderBS //------------------------------------------------------------------------ class AnnotBorderBS : public AnnotBorder { public: AnnotBorderBS(); explicit AnnotBorderBS(Dict *dict); private: AnnotBorderType getType() const override { return typeBS; } Object writeToObject(XRef *xref) const override; const char *getStyleName() const; std::unique_ptr copy() const override; // double width; // W (Default 1) (inherited from AnnotBorder) // AnnotBorderStyle style; // S (Default S) (inherited from AnnotBorder) // double *dash; // D (Default [3]) (inherited from AnnotBorder) }; //------------------------------------------------------------------------ // AnnotColor //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT AnnotColor { public: enum AnnotColorSpace { colorTransparent = 0, colorGray = 1, colorRGB = 3, colorCMYK = 4 }; AnnotColor(); explicit AnnotColor(double gray); AnnotColor(double r, double g, double b); AnnotColor(double c, double m, double y, double k); explicit AnnotColor(Array *array, int adjust = 0); void adjustColor(int adjust); AnnotColorSpace getSpace() const { return (AnnotColorSpace)length; } const double *getValues() const { return values; } Object writeToObject(XRef *xref) const; private: double values[4]; int length; }; //------------------------------------------------------------------------ // DefaultAppearance //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT DefaultAppearance { public: DefaultAppearance(Object &&fontNameA, double fontPtSizeA, std::unique_ptr &&fontColorA); explicit DefaultAppearance(const GooString *da); void setFontName(Object &&fontNameA); const Object &getFontName() const { return fontName; } void setFontPtSize(double fontPtSizeA); double getFontPtSize() const { return fontPtSize; } void setFontColor(std::unique_ptr fontColorA); const AnnotColor *getFontColor() const { return fontColor.get(); } std::string toAppearanceString() const; DefaultAppearance(const DefaultAppearance &) = delete; DefaultAppearance &operator=(const DefaultAppearance &) = delete; private: Object fontName; double fontPtSize; std::unique_ptr fontColor; }; //------------------------------------------------------------------------ // AnnotIconFit //------------------------------------------------------------------------ class AnnotIconFit { public: enum AnnotIconFitScaleWhen { scaleAlways, // A scaleBigger, // B scaleSmaller, // S scaleNever // N }; enum AnnotIconFitScale { scaleAnamorphic, // A scaleProportional // P }; explicit AnnotIconFit(Dict *dict); AnnotIconFitScaleWhen getScaleWhen() { return scaleWhen; } AnnotIconFitScale getScale() { return scale; } double getLeft() { return left; } double getBottom() { return bottom; } bool getFullyBounds() { return fullyBounds; } protected: AnnotIconFitScaleWhen scaleWhen; // SW (Default A) AnnotIconFitScale scale; // S (Default P) double left; // A (Default [0.5 0.5] double bottom; // Only if scale is P bool fullyBounds; // FB (Default false) }; //------------------------------------------------------------------------ // AnnotAppearance //------------------------------------------------------------------------ class AnnotAppearance { public: enum AnnotAppearanceType { appearNormal, appearRollover, appearDown }; AnnotAppearance(PDFDoc *docA, Object *dict); ~AnnotAppearance(); // State is ignored if no subdictionary is present Object getAppearanceStream(AnnotAppearanceType type, const char *state); // Access keys in normal appearance subdictionary (N) std::unique_ptr getStateKey(int i); int getNumStates(); // Removes all associated streams in the xref table. Caller is required to // reset parent annotation's AP and AS after this call. void removeAllStreams(); // Test if this AnnotAppearance references the specified stream bool referencesStream(Ref refToStream); private: static bool referencesStream(const Object *stateObj, Ref refToStream); void removeStream(Ref refToStream); void removeStateStreams(const Object *state); protected: PDFDoc *doc; Object appearDict; // Annotation's AP }; //------------------------------------------------------------------------ // AnnotAppearanceCharacs //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT AnnotAppearanceCharacs { public: enum AnnotAppearanceCharacsTextPos { captionNoIcon, // 0 captionNoCaption, // 1 captionBelow, // 2 captionAbove, // 3 captionRight, // 4 captionLeft, // 5 captionOverlaid // 6 }; explicit AnnotAppearanceCharacs(Dict *dict); ~AnnotAppearanceCharacs(); AnnotAppearanceCharacs(const AnnotAppearanceCharacs &) = delete; AnnotAppearanceCharacs &operator=(const AnnotAppearanceCharacs &) = delete; int getRotation() const { return rotation; } const AnnotColor *getBorderColor() const { return borderColor.get(); } void setBorderColor(std::unique_ptr &&color) { borderColor = std::move(color); } const AnnotColor *getBackColor() const { return backColor.get(); } void setBackColor(std::unique_ptr &&color) { backColor = std::move(color); } const GooString *getNormalCaption() const { return normalCaption.get(); } const GooString *getRolloverCaption() { return rolloverCaption.get(); } const GooString *getAlternateCaption() { return alternateCaption.get(); } const AnnotIconFit *getIconFit() { return iconFit.get(); } AnnotAppearanceCharacsTextPos getPosition() const { return position; } std::unique_ptr copy() const; protected: int rotation; // R (Default 0) std::unique_ptr borderColor; // BC std::unique_ptr backColor; // BG std::unique_ptr normalCaption; // CA std::unique_ptr rolloverCaption; // RC std::unique_ptr alternateCaption; // AC // I // RI // IX std::unique_ptr iconFit; // IF AnnotAppearanceCharacsTextPos position; // TP (Default 0) }; //------------------------------------------------------------------------ // AnnotAppearanceBBox //------------------------------------------------------------------------ class AnnotAppearanceBBox { public: explicit AnnotAppearanceBBox(PDFRectangle *rect); void setBorderWidth(double w) { borderWidth = w; } // The following functions operate on coords relative to [origX origY] void extendTo(double x, double y); void getBBoxRect(double bbox[4]) const; // Get boundaries in page coordinates double getPageXMin() const; double getPageYMin() const; double getPageXMax() const; double getPageYMax() const; private: double origX, origY, borderWidth; double minX, minY, maxX, maxY; }; //------------------------------------------------------------------------ // AnnotAppearanceBuilder //------------------------------------------------------------------------ class Matrix; class AnnotAppearanceBuilder { public: AnnotAppearanceBuilder(); ~AnnotAppearanceBuilder(); AnnotAppearanceBuilder(const AnnotAppearanceBuilder &) = delete; AnnotAppearanceBuilder &operator=(const AnnotAppearanceBuilder &) = delete; 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); void drawCircleTopLeft(double cx, double cy, double r); void drawCircleBottomRight(double cx, double cy, double r); void drawLineEnding(AnnotLineEndingStyle endingStyle, double x, double y, double size, bool fill, const Matrix &m); void drawLineEndSquare(double x, double y, double size, bool fill, const Matrix &m); void drawLineEndCircle(double x, double y, double size, bool fill, const Matrix &m); void drawLineEndDiamond(double x, double y, double size, bool fill, const Matrix &m); void drawLineEndArrow(double x, double y, double size, int orientation, bool isOpen, bool fill, const Matrix &m); void drawLineEndSlash(double x, double y, double size, const Matrix &m); void drawFieldBorder(const FormField *field, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect); bool drawFormField(const FormField *field, const Form *form, const GfxResources *resources, const GooString *da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect, const GooString *appearState, XRef *xref, Dict *resourcesDict); static double lineEndingXShorten(AnnotLineEndingStyle endingStyle, double size); static double lineEndingXExtendBBox(AnnotLineEndingStyle endingStyle, double size); void writeString(const std::string &str); void append(const char *text); void appendf(const char *fmt, ...) GOOSTRING_FORMAT; const GooString *buffer() const; private: enum DrawTextFlags { NoDrawTextFlags = 0, MultilineDrawTextFlag = 1, EmitMarkedContentDrawTextFlag = 2, ForceZapfDingbatsDrawTextFlag = 4, TurnTextToStarsDrawTextFlag = 8 }; bool drawListBox(const FormFieldChoice *fieldChoice, const AnnotBorder *border, const PDFRectangle *rect, const GooString *da, const GfxResources *resources, VariableTextQuadding quadding, XRef *xref, Dict *resourcesDict); bool drawFormFieldButton(const FormFieldButton *field, const Form *form, const GfxResources *resources, const GooString *da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect, const GooString *appearState, XRef *xref, Dict *resourcesDict); bool drawFormFieldText(const FormFieldText *fieldText, const Form *form, const GfxResources *resources, const GooString *da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect, XRef *xref, Dict *resourcesDict); bool drawFormFieldChoice(const FormFieldChoice *fieldChoice, const Form *form, const GfxResources *resources, const GooString *da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect, XRef *xref, Dict *resourcesDict); bool drawSignatureFieldText(const FormFieldSignature *field, const Form *form, const GfxResources *resources, const GooString *da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect, XRef *xref, Dict *resourcesDict); void drawSignatureFieldText(const GooString &text, const Form *form, const DefaultAppearance &da, const AnnotBorder *border, const PDFRectangle *rect, XRef *xref, Dict *resourcesDict, double leftMargin, bool centerVertically, bool centerHorizontally); bool drawText(const GooString *text, const Form *form, const GooString *da, const GfxResources *resources, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect, 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; }; //------------------------------------------------------------------------ // Annot //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT Annot { friend class Annots; friend class Page; public: enum AnnotFlag { flagUnknown = 0x0000, flagInvisible = 0x0001, flagHidden = 0x0002, flagPrint = 0x0004, flagNoZoom = 0x0008, flagNoRotate = 0x0010, flagNoView = 0x0020, flagReadOnly = 0x0040, flagLocked = 0x0080, flagToggleNoView = 0x0100, flagLockedContents = 0x0200 }; enum AnnotSubtype { typeUnknown, // 0 typeText, // Text 1 typeLink, // Link 2 typeFreeText, // FreeText 3 typeLine, // Line 4 typeSquare, // Square 5 typeCircle, // Circle 6 typePolygon, // Polygon 7 typePolyLine, // PolyLine 8 typeHighlight, // Highlight 9 typeUnderline, // Underline 10 typeSquiggly, // Squiggly 11 typeStrikeOut, // StrikeOut 12 typeStamp, // Stamp 13 typeCaret, // Caret 14 typeInk, // Ink 15 typePopup, // Popup 16 typeFileAttachment, // FileAttachment 17 typeSound, // Sound 18 typeMovie, // Movie 19 typeWidget, // Widget 20 typeScreen, // Screen 21 typePrinterMark, // PrinterMark 22 typeTrapNet, // TrapNet 23 typeWatermark, // Watermark 24 type3D, // 3D 25 typeRichMedia // RichMedia 26 }; /** * Describes the additional actions of a screen or widget annotation. */ enum AdditionalActionsType { actionCursorEntering, ///< Performed when the cursor enters the annotation's active area actionCursorLeaving, ///< Performed when the cursor exists the annotation's active area actionMousePressed, ///< Performed when the mouse button is pressed inside the annotation's active area actionMouseReleased, ///< Performed when the mouse button is released inside the annotation's active area actionFocusIn, ///< Performed when the annotation receives the input focus actionFocusOut, ///< Performed when the annotation loses the input focus actionPageOpening, ///< Performed when the page containing the annotation is opened actionPageClosing, ///< Performed when the page containing the annotation is closed actionPageVisible, ///< Performed when the page containing the annotation becomes visible actionPageInvisible ///< Performed when the page containing the annotation becomes invisible }; enum FormAdditionalActionsType { actionFieldModified, ///< Performed when the when the user modifies the field actionFormatField, ///< Performed before the field is formatted to display its value actionValidateField, ///< Performed when the field value changes actionCalculateField, ///< Performed when the field needs to be recalculated }; Annot(PDFDoc *docA, PDFRectangle *rectA); Annot(PDFDoc *docA, Object &&dictObject); Annot(PDFDoc *docA, Object &&dictObject, const Object *obj); bool isOk() { return ok; } void incRefCnt(); void decRefCnt(); virtual void draw(Gfx *gfx, bool printing); // Get the resource dict of the appearance stream virtual Object getAppearanceResDict(); bool match(const Ref *refA) const { return ref == *refA; } double getXMin(); double getYMin(); double getXMax(); double getYMax(); 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 setFlags(unsigned int new_flags); void setBorder(std::unique_ptr &&new_border); void setColor(std::unique_ptr &&new_color); void setAppearanceState(const char *state); // getters PDFDoc *getDoc() const { return doc; } bool getHasRef() const { return hasRef; } Ref getRef() const { return ref; } const Object &getAnnotObj() const { return annotObj; } AnnotSubtype getType() const { return type; } const PDFRectangle &getRect() const { return *rect; } void getRect(double *x1, double *y1, double *x2, double *y2) const; const GooString *getContents() const { return contents.get(); } int getPageNum() const { return page; } const GooString *getName() const { return name.get(); } const GooString *getModified() const { return modified.get(); } unsigned int getFlags() const { return flags; } Object getAppearance() const; void setNewAppearance(Object &&newAppearance); AnnotAppearance *getAppearStreams() const { return appearStreams.get(); } const GooString *getAppearState() const { return appearState.get(); } AnnotBorder *getBorder() const { return border.get(); } AnnotColor *getColor() const { return color.get(); } int getTreeKey() const { return treeKey; } int getId() { return ref.num; } // Check if point is inside the annot rectangle. bool inRect(double x, double y) const; // If newFontNeeded is not null, it will contain whether the given font has glyphs to represent the needed text static void layoutText(const GooString *text, GooString *outBuf, int *i, const GfxFont &font, double *width, double widthLimit, int *charCount, bool noReencode, bool *newFontNeeded = nullptr); private: void readArrayNum(Object *pdfArray, int key, double *value); // write vStr[i:j[ in appearBuf void initialize(PDFDoc *docA, Dict *dict); void setPage(int pageIndex, bool updateP); // Called by Page::addAnnot and Annots ctor protected: virtual ~Annot(); virtual void removeReferencedObjects(); // Called by Page::removeAnnot Object createForm(const GooString *appearBuf, const double *bbox, bool transparencyGroup, Dict *resDict); Object createForm(const GooString *appearBuf, const double *bbox, bool transparencyGroup, Object &&resDictObject); // overload to support incRef/decRef Dict *createResourcesDict(const char *formName, Object &&formStream, const char *stateName, double opacity, const char *blendMode); bool isVisible(bool printing); int getRotation() const; // Updates the field key of the annotation dictionary // and sets M to the current time void update(const char *key, Object &&value); // Delete appearance streams and reset appearance state virtual void invalidateAppearance(); Object annotObj; std::atomic_int refCnt; // required data AnnotSubtype type; // Annotation type std::unique_ptr rect; // Rect // optional data std::unique_ptr contents; // Contents std::unique_ptr name; // NM std::unique_ptr modified; // M int page; // P unsigned int flags; // F (must be a 32 bit unsigned int) std::unique_ptr appearStreams; // AP Object appearance; // a reference to the Form XObject stream // for the normal appearance std::unique_ptr appearBBox; // BBox of generated appearance std::unique_ptr appearState; // AS int treeKey; // Struct Parent; Object oc; // OC PDFDoc *doc; Ref ref; // object ref identifying this annotation std::unique_ptr border; // Border, BS std::unique_ptr color; // C bool ok; bool hasRef; mutable std::recursive_mutex mutex; bool hasBeenUpdated = false; }; //------------------------------------------------------------------------ // AnnotPopup //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT AnnotPopup : public Annot { public: AnnotPopup(PDFDoc *docA, PDFRectangle *rect); AnnotPopup(PDFDoc *docA, Object &&dictObject, const Object *obj); ~AnnotPopup() override; bool hasParent() const { return parentRef != Ref::INVALID(); } void setParent(Annot *parentA); bool getOpen() const { return open; } void setOpen(bool openA); protected: void initialize(PDFDoc *docA, Dict *dict); Ref parentRef; // Parent bool open; // Open }; //------------------------------------------------------------------------ // AnnotMarkup //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT AnnotMarkup : public Annot { public: enum AnnotMarkupReplyType { replyTypeR, // R replyTypeGroup // Group }; AnnotMarkup(PDFDoc *docA, PDFRectangle *rect); AnnotMarkup(PDFDoc *docA, Object &&dictObject, const Object *obj); ~AnnotMarkup() override; // getters const GooString *getLabel() const { return label.get(); } AnnotPopup *getPopup() const { return popup.get(); } double getOpacity() const { return opacity; } // getRC const GooString *getDate() const { return date.get(); } bool isInReplyTo() const { return inReplyTo != Ref::INVALID(); } int getInReplyToID() const { return inReplyTo.num; } const GooString *getSubject() const { return subject.get(); } AnnotMarkupReplyType getReplyTo() const { return replyTo; } AnnotExternalDataType getExData() const { return exData; } // The annotation takes the ownership of new_popup void setPopup(std::unique_ptr &&new_popup); void setLabel(std::unique_ptr &&new_label); void setOpacity(double opacityA); void setDate(GooString *new_date); protected: void removeReferencedObjects() override; std::unique_ptr label; // T (Default author) std::unique_ptr popup; // Popup double opacity; // CA (Default 1.0) // RC std::unique_ptr date; // CreationDate Ref inReplyTo; // IRT std::unique_ptr subject; // Subj AnnotMarkupReplyType replyTo; // RT (Default R) // this object is overridden by the custom intent fields defined in some // annotation types. // GooString *intent; // IT AnnotExternalDataType exData; // ExData private: void initialize(PDFDoc *docA, Dict *dict); }; //------------------------------------------------------------------------ // AnnotText //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT AnnotText : public AnnotMarkup { public: enum AnnotTextState { stateUnknown, // Marked state model stateMarked, // Marked stateUnmarked, // Unmarked // Review state model stateAccepted, // Accepted stateRejected, // Rejected stateCancelled, // Cancelled stateCompleted, // Completed stateNone // None }; AnnotText(PDFDoc *docA, PDFRectangle *rect); AnnotText(PDFDoc *docA, Object &&dictObject, const Object *obj); ~AnnotText() override; void draw(Gfx *gfx, bool printing) override; // getters bool getOpen() const { return open; } const GooString *getIcon() const { return icon.get(); } AnnotTextState getState() const { return state; } void setOpen(bool openA); void setIcon(GooString *new_icon); private: void initialize(PDFDoc *docA, Dict *dict); bool open; // Open (Default false) std::unique_ptr icon; // Name (Default Note) AnnotTextState state; // State (Default Umarked if // StateModel Marked // None if StareModel Review) }; //------------------------------------------------------------------------ // AnnotMovie //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT AnnotMovie : public Annot { public: AnnotMovie(PDFDoc *docA, PDFRectangle *rect, Movie *movieA); AnnotMovie(PDFDoc *docA, Object &&dictObject, const Object *obj); ~AnnotMovie() override; void draw(Gfx *gfx, bool printing) override; const GooString *getTitle() const { return title.get(); } Movie *getMovie() { return movie.get(); } private: void initialize(PDFDoc *docA, Dict *dict); std::unique_ptr title; // T std::unique_ptr movie; // Movie + A }; //------------------------------------------------------------------------ // AnnotScreen //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT AnnotScreen : public Annot { public: AnnotScreen(PDFDoc *docA, PDFRectangle *rect); AnnotScreen(PDFDoc *docA, Object &&dictObject, const Object *obj); ~AnnotScreen() override; const GooString *getTitle() const { return title.get(); } AnnotAppearanceCharacs *getAppearCharacs() { return appearCharacs.get(); } LinkAction *getAction() { return action.get(); } // The caller should not delete the result std::unique_ptr getAdditionalAction(AdditionalActionsType type); private: void initialize(PDFDoc *docA, Dict *dict); std::unique_ptr title; // T std::unique_ptr appearCharacs; // MK std::unique_ptr action; // A Object additionalActions; // AA }; //------------------------------------------------------------------------ // AnnotLink //------------------------------------------------------------------------ class AnnotLink : public Annot { public: enum AnnotLinkEffect { effectNone, // N effectInvert, // I effectOutline, // O effectPush // P }; AnnotLink(PDFDoc *docA, PDFRectangle *rect); AnnotLink(PDFDoc *docA, Object &&dictObject, const Object *obj); ~AnnotLink() override; void draw(Gfx *gfx, bool printing) override; // getters LinkAction *getAction() const { return action.get(); } AnnotLinkEffect getLinkEffect() const { return linkEffect; } AnnotQuadrilaterals *getQuadrilaterals() const { return quadrilaterals.get(); } protected: void initialize(PDFDoc *docA, Dict *dict); std::unique_ptr action; // A, Dest AnnotLinkEffect linkEffect; // H (Default I) // Dict *uriAction; // PA std::unique_ptr quadrilaterals; // QuadPoints }; //------------------------------------------------------------------------ // AnnotFreeText //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT AnnotFreeText : public AnnotMarkup { public: enum AnnotFreeTextIntent { intentFreeText, // FreeText intentFreeTextCallout, // FreeTextCallout intentFreeTextTypeWriter // FreeTextTypeWriter }; static const double undefinedFontPtSize; AnnotFreeText(PDFDoc *docA, PDFRectangle *rect); AnnotFreeText(PDFDoc *docA, Object &&dictObject, const Object *obj); ~AnnotFreeText() override; void draw(Gfx *gfx, bool printing) override; Object getAppearanceResDict() override; void setContents(std::unique_ptr &&new_content) override; void setDefaultAppearance(const DefaultAppearance &da); void setQuadding(VariableTextQuadding new_quadding); void setStyleString(GooString *new_string); void setCalloutLine(AnnotCalloutLine *line); void setIntent(AnnotFreeTextIntent new_intent); // getters std::unique_ptr getDefaultAppearance() const; VariableTextQuadding getQuadding() const { return quadding; } // return rc const GooString *getStyleString() const { return styleString.get(); } AnnotCalloutLine *getCalloutLine() const { return calloutLine.get(); } AnnotFreeTextIntent getIntent() const { return intent; } AnnotBorderEffect *getBorderEffect() const { return borderEffect.get(); } PDFRectangle *getRectangle() const { return rectangle.get(); } AnnotLineEndingStyle getEndStyle() const { return endStyle; } protected: void initialize(PDFDoc *docA, Dict *dict); void generateFreeTextAppearance(); // required std::unique_ptr appearanceString; // DA // optional VariableTextQuadding quadding; // Q (Default 0) // RC std::unique_ptr styleString; // DS std::unique_ptr calloutLine; // CL AnnotFreeTextIntent intent; // IT std::unique_ptr borderEffect; // BE std::unique_ptr rectangle; // RD // inherited from Annot // AnnotBorderBS border; // BS AnnotLineEndingStyle endStyle; // LE (Default None) }; //------------------------------------------------------------------------ // AnnotLine //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT AnnotLine : public AnnotMarkup { public: enum AnnotLineIntent { intentLineArrow, // LineArrow intentLineDimension // LineDimension }; enum AnnotLineCaptionPos { captionPosInline, // Inline captionPosTop // Top }; AnnotLine(PDFDoc *docA, PDFRectangle *rect); AnnotLine(PDFDoc *docA, Object &&dictObject, const Object *obj); ~AnnotLine() override; void draw(Gfx *gfx, bool printing) override; Object getAppearanceResDict() override; void setContents(std::unique_ptr &&new_content) override; void setVertices(double x1, double y1, double x2, double y2); void setStartEndStyle(AnnotLineEndingStyle start, AnnotLineEndingStyle end); void setInteriorColor(std::unique_ptr &&new_color); void setLeaderLineLength(double len); void setLeaderLineExtension(double len); void setCaption(bool new_cap); void setIntent(AnnotLineIntent new_intent); // getters AnnotLineEndingStyle getStartStyle() const { return startStyle; } AnnotLineEndingStyle getEndStyle() const { return endStyle; } AnnotColor *getInteriorColor() const { return interiorColor.get(); } double getLeaderLineLength() const { return leaderLineLength; } double getLeaderLineExtension() const { return leaderLineExtension; } bool getCaption() const { return caption; } AnnotLineIntent getIntent() const { return intent; } double getLeaderLineOffset() const { return leaderLineOffset; } AnnotLineCaptionPos getCaptionPos() const { return captionPos; } Dict *getMeasure() const { return measure; } double getCaptionTextHorizontal() const { return captionTextHorizontal; } double getCaptionTextVertical() const { return captionTextVertical; } double getX1() const { return coord1->getX(); } double getY1() const { return coord1->getY(); } double getX2() const { return coord2->getX(); } double getY2() const { return coord2->getY(); } protected: void initialize(PDFDoc *docA, Dict *dict); void generateLineAppearance(); // required std::unique_ptr coord1; std::unique_ptr coord2; // optional // inherited from Annot // AnnotBorderBS border; // BS AnnotLineEndingStyle startStyle; // LE (Default [/None /None]) AnnotLineEndingStyle endStyle; // std::unique_ptr interiorColor; // IC double leaderLineLength; // LL (Default 0) double leaderLineExtension; // LLE (Default 0) bool caption; // Cap (Default false) AnnotLineIntent intent; // IT double leaderLineOffset; // LLO AnnotLineCaptionPos captionPos; // CP (Default Inline) Dict *measure; // Measure double captionTextHorizontal; // CO (Default [0, 0]) double captionTextVertical; // }; //------------------------------------------------------------------------ // AnnotTextMarkup //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT AnnotTextMarkup : public AnnotMarkup { public: AnnotTextMarkup(PDFDoc *docA, PDFRectangle *rect, AnnotSubtype subType); AnnotTextMarkup(PDFDoc *docA, Object &&dictObject, const Object *obj); ~AnnotTextMarkup() override; void draw(Gfx *gfx, bool printing) override; // typeHighlight, typeUnderline, typeSquiggly or typeStrikeOut void setType(AnnotSubtype new_type); void setQuadrilaterals(AnnotQuadrilaterals *quadPoints); AnnotQuadrilaterals *getQuadrilaterals() const { return quadrilaterals.get(); } protected: void initialize(PDFDoc *docA, Dict *dict); std::unique_ptr quadrilaterals; // QuadPoints private: bool shouldCreateApperance(Gfx *gfx) const; }; //------------------------------------------------------------------------ // AnnotStamp //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT AnnotStamp : public AnnotMarkup { public: AnnotStamp(PDFDoc *docA, PDFRectangle *rect); AnnotStamp(PDFDoc *docA, Object &&dictObject, const Object *obj); ~AnnotStamp() override; void draw(Gfx *gfx, bool printing) override; void setIcon(GooString *new_icon); void setCustomImage(AnnotStampImageHelper *stampImageHelperA); void clearCustomImage(); // getters const GooString *getIcon() const { return icon.get(); } private: void initialize(PDFDoc *docA, Dict *dict); void generateStampDefaultAppearance(); void generateStampCustomAppearance(); std::unique_ptr icon; // Name (Default Draft) AnnotStampImageHelper *stampImageHelper; Ref updatedAppearanceStream; }; //------------------------------------------------------------------------ // AnnotGeometry //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT AnnotGeometry : public AnnotMarkup { public: AnnotGeometry(PDFDoc *docA, PDFRectangle *rect, AnnotSubtype subType); AnnotGeometry(PDFDoc *docA, Object &&dictObject, const Object *obj); ~AnnotGeometry() override; void draw(Gfx *gfx, bool printing) override; void setType(AnnotSubtype new_type); // typeSquare or typeCircle void setInteriorColor(std::unique_ptr &&new_color); // getters AnnotColor *getInteriorColor() const { return interiorColor.get(); } AnnotBorderEffect *getBorderEffect() const { return borderEffect.get(); } PDFRectangle *getGeometryRect() const { return geometryRect.get(); } private: void initialize(PDFDoc *docA, Dict *dict); std::unique_ptr interiorColor; // IC std::unique_ptr borderEffect; // BE std::unique_ptr geometryRect; // RD (combined with Rect) }; //------------------------------------------------------------------------ // AnnotPolygon //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT AnnotPolygon : public AnnotMarkup { public: enum AnnotPolygonIntent { polygonCloud, // PolygonCloud polylineDimension, // PolyLineDimension polygonDimension // PolygonDimension }; AnnotPolygon(PDFDoc *docA, PDFRectangle *rect, AnnotSubtype subType); AnnotPolygon(PDFDoc *docA, Object &&dictObject, const Object *obj); ~AnnotPolygon() override; void draw(Gfx *gfx, bool printing) override; void generatePolyLineAppearance(AnnotAppearanceBuilder *appearBuilder); void setType(AnnotSubtype new_type); // typePolygon or typePolyLine void setVertices(AnnotPath *path); void setStartEndStyle(AnnotLineEndingStyle start, AnnotLineEndingStyle end); void setInteriorColor(std::unique_ptr &&new_color); void setIntent(AnnotPolygonIntent new_intent); // getters AnnotPath *getVertices() const { return vertices.get(); } AnnotLineEndingStyle getStartStyle() const { return startStyle; } AnnotLineEndingStyle getEndStyle() const { return endStyle; } AnnotColor *getInteriorColor() const { return interiorColor.get(); } AnnotBorderEffect *getBorderEffect() const { return borderEffect.get(); } AnnotPolygonIntent getIntent() const { return intent; } private: void initialize(PDFDoc *docA, Dict *dict); // required std::unique_ptr vertices; // Vertices // optional AnnotLineEndingStyle startStyle; // LE (Default [/None /None]) AnnotLineEndingStyle endStyle; // // inherited from Annot // AnnotBorderBS border; // BS std::unique_ptr interiorColor; // IC std::unique_ptr borderEffect; // BE AnnotPolygonIntent intent; // IT // Measure }; //------------------------------------------------------------------------ // AnnotCaret //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT AnnotCaret : public AnnotMarkup { public: enum AnnotCaretSymbol { symbolNone, // None symbolP // P }; AnnotCaret(PDFDoc *docA, PDFRectangle *rect); AnnotCaret(PDFDoc *docA, Object &&dictObject, const Object *obj); ~AnnotCaret() override; void setSymbol(AnnotCaretSymbol new_symbol); // getters AnnotCaretSymbol getSymbol() const { return symbol; } PDFRectangle *getCaretRect() const { return caretRect.get(); } private: void initialize(PDFDoc *docA, Dict *dict); AnnotCaretSymbol symbol; // Sy (Default None) std::unique_ptr caretRect; // RD (combined with Rect) }; //------------------------------------------------------------------------ // AnnotInk //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT AnnotInk : public AnnotMarkup { public: AnnotInk(PDFDoc *docA, PDFRectangle *rect); AnnotInk(PDFDoc *docA, Object &&dictObject, const Object *obj); ~AnnotInk() override; void draw(Gfx *gfx, bool printing) override; void setInkList(AnnotPath **paths, int n_paths); // getters AnnotPath **getInkList() const { return inkList; } int getInkListLength() const { return inkListLength; } private: void initialize(PDFDoc *docA, Dict *dict); void writeInkList(AnnotPath **paths, int n_paths, Array *dest_array); void parseInkList(Array *src_array); void freeInkList(); // required AnnotPath **inkList; // InkList int inkListLength; // optional // inherited from Annot // AnnotBorderBS border; // BS }; //------------------------------------------------------------------------ // AnnotFileAttachment //------------------------------------------------------------------------ class AnnotFileAttachment : public AnnotMarkup { public: AnnotFileAttachment(PDFDoc *docA, PDFRectangle *rect, GooString *filename); AnnotFileAttachment(PDFDoc *docA, Object &&dictObject, const Object *obj); ~AnnotFileAttachment() override; void draw(Gfx *gfx, bool printing) override; // getters Object *getFile() { return &file; } const GooString *getName() const { return name.get(); } private: void initialize(PDFDoc *docA, Dict *dict); // required Object file; // FS // optional std::unique_ptr name; // Name }; //------------------------------------------------------------------------ // AnnotSound //------------------------------------------------------------------------ class AnnotSound : public AnnotMarkup { public: AnnotSound(PDFDoc *docA, PDFRectangle *rect, Sound *soundA); AnnotSound(PDFDoc *docA, Object &&dictObject, const Object *obj); ~AnnotSound() override; void draw(Gfx *gfx, bool printing) override; // getters Sound *getSound() { return sound.get(); } const GooString *getName() const { return name.get(); } private: void initialize(PDFDoc *docA, Dict *dict); // required std::unique_ptr sound; // Sound // optional std::unique_ptr name; // Name }; //------------------------------------------------------------------------ // AnnotWidget //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT AnnotWidget : public Annot { public: enum AnnotWidgetHighlightMode { highlightModeNone, // N highlightModeInvert, // I highlightModeOutline, // O highlightModePush // P,T }; AnnotWidget(PDFDoc *docA, Object &&dictObject, const Object *obj); AnnotWidget(PDFDoc *docA, Object *dictObject, Object *obj, FormField *fieldA); ~AnnotWidget() override; void draw(Gfx *gfx, bool printing) override; void invalidateAppearance() override; void generateFieldAppearance(); void updateAppearanceStream(); AnnotWidgetHighlightMode getMode() { return mode; } AnnotAppearanceCharacs *getAppearCharacs() { return appearCharacs.get(); } void setAppearCharacs(std::unique_ptr &&appearCharacsA) { appearCharacs = std::move(appearCharacsA); } LinkAction *getAction() { return action.get(); } // The caller should not delete the result std::unique_ptr getAdditionalAction(AdditionalActionsType type); std::unique_ptr getFormAdditionalAction(FormAdditionalActionsType type); Dict *getParent() { return parent; } bool setFormAdditionalAction(FormAdditionalActionsType type, const std::string &js); void setField(FormField *f) { field = f; }; private: void initialize(PDFDoc *docA, Dict *dict); Form *form; FormField *field; // FormField object for this annotation AnnotWidgetHighlightMode mode; // H (Default I) std::unique_ptr appearCharacs; // MK std::unique_ptr action; // A Object additionalActions; // AA // inherited from Annot // AnnotBorderBS border; // BS Dict *parent; // Parent Ref updatedAppearanceStream; // {-1,-1} if updateAppearanceStream has never been called }; //------------------------------------------------------------------------ // Annot3D //------------------------------------------------------------------------ class Annot3D : public Annot { class Activation { public: enum ActivationATrigger { aTriggerUnknown, aTriggerPageOpened, // PO aTriggerPageVisible, // PV aTriggerUserAction // XA }; enum ActivationAState { aStateUnknown, aStateEnabled, // I aStateDisabled // L }; enum ActivationDTrigger { dTriggerUnknown, dTriggerPageClosed, // PC dTriggerPageInvisible, // PI dTriggerUserAction // XD }; enum ActivationDState { dStateUnknown, dStateUninstantiaded, // U dStateInstantiated, // I dStateLive // L }; explicit Activation(Dict *dict); private: ActivationATrigger aTrigger; // A (Default XA) ActivationAState aState; // AIS (Default L) ActivationDTrigger dTrigger; // D (Default PI) ActivationDState dState; // DIS (Default U) bool displayToolbar; // TB (Default true) bool displayNavigation; // NP (Default false); }; public: Annot3D(PDFDoc *docA, PDFRectangle *rect); Annot3D(PDFDoc *docA, Object &&dictObject, const Object *obj); ~Annot3D() override; // getters private: void initialize(PDFDoc *docA, Dict *dict); std::unique_ptr activation; // 3DA }; //------------------------------------------------------------------------ // AnnotRichMedia //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT AnnotRichMedia : public Annot { public: class POPPLER_PRIVATE_EXPORT Params { public: explicit Params(Dict *dict); ~Params(); Params(const Params &) = delete; Params &operator=(const Params &) = delete; const GooString *getFlashVars() const; private: // optional std::unique_ptr flashVars; // FlashVars }; class POPPLER_PRIVATE_EXPORT Instance { public: enum Type { type3D, // 3D typeFlash, // Flash typeSound, // Sound typeVideo // Video }; explicit Instance(Dict *dict); ~Instance(); Instance(const Instance &) = delete; Instance &operator=(const Instance &) = delete; Type getType() const; Params *getParams() const; private: // optional Type type; // Subtype std::unique_ptr params; // Params }; class POPPLER_PRIVATE_EXPORT Configuration { public: enum Type { type3D, // 3D typeFlash, // Flash typeSound, // Sound typeVideo // Video }; explicit Configuration(Dict *dict); ~Configuration(); Configuration(const Configuration &) = delete; Configuration &operator=(const Configuration &) = delete; Type getType() const; const GooString *getName() const; int getInstancesCount() const; Instance *getInstance(int index) const; private: // optional Type type; // Subtype std::unique_ptr name; // Name Instance **instances; // Instances int nInstances; }; class Content; class POPPLER_PRIVATE_EXPORT Asset { public: Asset(); ~Asset(); Asset(const Asset &) = delete; Asset &operator=(const Asset &) = delete; const GooString *getName() const; Object *getFileSpec() const; private: friend class AnnotRichMedia::Content; std::unique_ptr name; Object fileSpec; }; class POPPLER_PRIVATE_EXPORT Content { public: explicit Content(Dict *dict); ~Content(); Content(const Content &) = delete; Content &operator=(const Content &) = delete; int getConfigurationsCount() const; Configuration *getConfiguration(int index) const; int getAssetsCount() const; Asset *getAsset(int index) const; private: // optional Configuration **configurations; // Configurations int nConfigurations; Asset **assets; // Assets int nAssets; }; class POPPLER_PRIVATE_EXPORT Activation { public: enum Condition { conditionPageOpened, // PO conditionPageVisible, // PV conditionUserAction // XA }; explicit Activation(Dict *dict); Condition getCondition() const; private: // optional Condition condition; }; class POPPLER_PRIVATE_EXPORT Deactivation { public: enum Condition { conditionPageClosed, // PC conditionPageInvisible, // PI conditionUserAction // XD }; explicit Deactivation(Dict *dict); Condition getCondition() const; private: // optional Condition condition; }; class POPPLER_PRIVATE_EXPORT Settings { public: explicit Settings(Dict *dict); ~Settings(); Settings(const Settings &) = delete; Settings &operator=(const Settings &) = delete; Activation *getActivation() const; Deactivation *getDeactivation() const; private: // optional std::unique_ptr activation; std::unique_ptr deactivation; }; AnnotRichMedia(PDFDoc *docA, PDFRectangle *rect); AnnotRichMedia(PDFDoc *docA, Object &&dictObject, const Object *obj); ~AnnotRichMedia() override; Content *getContent() const; Settings *getSettings() const; private: void initialize(PDFDoc *docA, Dict *dict); // required std::unique_ptr content; // RichMediaContent // optional std::unique_ptr settings; // RichMediaSettings }; //------------------------------------------------------------------------ // Annots //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT Annots { public: // Build a list of Annot objects and call setPage on them Annots(PDFDoc *docA, int page, Object *annotsObj); ~Annots(); Annots(const Annots &) = delete; Annots &operator=(const Annots &) = delete; const std::vector &getAnnots() { return annots; } void appendAnnot(Annot *annot); bool removeAnnot(Annot *annot); private: Annot *createAnnot(Object &&dictObject, const Object *obj); Annot *findAnnot(Ref *ref); PDFDoc *doc; std::vector annots; }; #endif poppler-24.02.0/poppler/AnnotStampImageHelper.cc000066400000000000000000000051111455701731300215230ustar00rootroot00000000000000//======================================================================== // // AnnotStampImageHelper.cc // // Copyright (C) 2021 Mahmoud Ahmed Khalil // Copyright (C) 2021 Albert Astals Cid // // Licensed under GPLv2 or later // //======================================================================== #include "AnnotStampImageHelper.h" #include "goo/gmem.h" #include "goo/gstrtod.h" #include "PDFDoc.h" #include "Stream.h" #include "Dict.h" #include AnnotStampImageHelper::AnnotStampImageHelper(PDFDoc *docA, int widthA, int heightA, ColorSpace colorSpace, int bitsPerComponent, char *data, int dataLength) { initialize(docA, widthA, heightA, colorSpace, bitsPerComponent, data, dataLength); } AnnotStampImageHelper::AnnotStampImageHelper(PDFDoc *docA, int widthA, int heightA, ColorSpace colorSpace, int bitsPerComponent, char *data, int dataLength, Ref softMaskRef) { initialize(docA, widthA, heightA, colorSpace, bitsPerComponent, data, dataLength); sMaskRef = softMaskRef; Dict *dict = imgObj.streamGetDict(); dict->add("SMask", Object(sMaskRef)); } void AnnotStampImageHelper::initialize(PDFDoc *docA, int widthA, int heightA, ColorSpace colorSpace, int bitsPerComponent, char *data, int dataLength) { doc = docA; width = widthA; height = heightA; sMaskRef = Ref::INVALID(); Dict *dict = new Dict(docA->getXRef()); dict->add("Type", Object(objName, "XObject")); dict->add("Subtype", Object(objName, "Image")); dict->add("Width", Object(width)); dict->add("Height", Object(height)); dict->add("ImageMask", Object(false)); dict->add("BitsPerComponent", Object(bitsPerComponent)); dict->add("Length", Object(dataLength)); switch (colorSpace) { case ColorSpace::DeviceGray: dict->add("ColorSpace", Object(objName, "DeviceGray")); break; case ColorSpace::DeviceRGB: dict->add("ColorSpace", Object(objName, "DeviceRGB")); break; case ColorSpace::DeviceCMYK: dict->add("ColorSpace", Object(objName, "DeviceCMYK")); break; } char *dataCopied = (char *)gmalloc(sizeof(char) * (dataLength)); std::memcpy(dataCopied, data, dataLength); Stream *dataStream = new AutoFreeMemStream(dataCopied, 0, dataLength, Object(dict)); imgObj = Object(dataStream); ref = doc->getXRef()->addIndirectObject(imgObj); } void AnnotStampImageHelper::removeAnnotStampImageObject() { if (sMaskRef != Ref::INVALID()) { doc->getXRef()->removeIndirectObject(sMaskRef); } doc->getXRef()->removeIndirectObject(ref); } poppler-24.02.0/poppler/AnnotStampImageHelper.h000066400000000000000000000041721455701731300213730ustar00rootroot00000000000000//======================================================================== // // AnnotStampImageHelper.h // // Copyright (C) 2021 Mahmoud Ahmed Khalil // Copyright (C) 2021 Albert Astals Cid // // Licensed under GPLv2 or later // //======================================================================== #ifndef ANNOTSTAMPIMAGEHELPER_H #define ANNOTSTAMPIMAGEHELPER_H #include "Object.h" class PDFDoc; enum ColorSpace { DeviceGray, DeviceRGB, DeviceCMYK }; /** * This class is used only to load Image XObjects into stamp annotations. It takes in * the image parameters in its constructors and creates a new Image XObject that gets * added to the XRef table, so that the annotations that would like to use it be able * to get its ref number. * * To have transparency in the image, you should first try to create the soft * mask of the image, by creating a AnnotStampImageHelper object giving it the soft * image data normally. You would then need to pass in the created soft mask Image XObject * ref to the actual image you'd like to be created by this helper class. */ 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() { } // Returns the ref to the created Image XObject Ref getRef() const { return ref; } // Returns the width of the image int getWidth() const { return width; } // Returns the height of the image int getHeight() const { return height; } // Removes the created Image XObject as well as its soft mask from the XRef Table void removeAnnotStampImageObject(); private: void initialize(PDFDoc *docA, int widthA, int heightA, ColorSpace colorSpace, int bitsPerComponent, char *data, int dataLength); PDFDoc *doc; Object imgObj; Ref ref; Ref sMaskRef; int width; int height; }; #endif poppler-24.02.0/poppler/Array.cc000066400000000000000000000062311455701731300164160ustar00rootroot00000000000000//======================================================================== // // Array.cc // // 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) 2005 Kristian Høgsberg // Copyright (C) 2012 Fabio D'Urso // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2013, 2017, 2019, 2022 Albert Astals Cid // Copyright (C) 2017 Adrian Johnson // Copyright (C) 2018, 2019 Adam Reichold // // 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 #include #include "Object.h" #include "Array.h" //------------------------------------------------------------------------ // Array //------------------------------------------------------------------------ #define arrayLocker() const std::scoped_lock locker(mutex) Array::Array(XRef *xrefA) { xref = xrefA; ref = 1; } Array::~Array() { } Array *Array::copy(XRef *xrefA) const { arrayLocker(); Array *a = new Array(xrefA); a->elems.reserve(elems.size()); for (const auto &elem : elems) { a->elems.push_back(elem.copy()); } return a; } Array *Array::deepCopy() const { arrayLocker(); Array *a = new Array(xref); a->elems.reserve(elems.size()); for (const auto &elem : elems) { a->elems.push_back(elem.deepCopy()); } return a; } void Array::add(Object &&elem) { arrayLocker(); elems.push_back(std::move(elem)); } void Array::remove(int i) { arrayLocker(); if (i < 0 || std::size_t(i) >= elems.size()) { assert(i >= 0 && std::size_t(i) < elems.size()); return; } elems.erase(elems.begin() + i); } Object Array::get(int i, int recursion) const { if (i < 0 || std::size_t(i) >= elems.size()) { return Object(objNull); } return elems[i].fetch(xref, recursion); } Object Array::get(int i, Ref *returnRef, int recursion) const { if (i < 0 || std::size_t(i) >= elems.size()) { *returnRef = Ref::INVALID(); return Object(objNull); } if (elems[i].getType() == objRef) { *returnRef = elems[i].getRef(); } else { *returnRef = Ref::INVALID(); } return elems[i].fetch(xref, recursion); } const Object &Array::getNF(int i) const { if (i < 0 || std::size_t(i) >= elems.size()) { static Object nullObj(objNull); return nullObj; } return elems[i]; } bool Array::getString(int i, GooString *string) const { const Object &obj = getNF(i); if (obj.isString()) { string->clear(); string->append(obj.getString()); return true; } else { return false; } } poppler-24.02.0/poppler/Array.h000066400000000000000000000052061455701731300162610ustar00rootroot00000000000000//======================================================================== // // Array.h // // 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) 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 Adrian Johnson // Copyright (C) 2018, 2019 Adam Reichold // // 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 ARRAY_H #define ARRAY_H #include #include #include #include "poppler-config.h" #include "poppler_private_export.h" #include "Object.h" class XRef; //------------------------------------------------------------------------ // Array //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT Array { public: // Constructor. explicit Array(XRef *xrefA); // Destructor. ~Array(); Array(const Array &) = delete; Array &operator=(const Array &) = delete; // Get number of elements. int getLength() const { return elems.size(); } // Copy array with new xref Array *copy(XRef *xrefA) const; Array *deepCopy() const; // Add an element // elem becomes a dead object after this call void add(Object &&elem); // Remove an element by position void remove(int i); // Accessors. Object get(int i, int recursion = 0) const; // Same as above but if the returned object is a fetched Ref returns such Ref in returnRef, otherwise returnRef is Ref::INVALID() Object get(int i, Ref *returnRef, int recursion = 0) const; const Object &getNF(int i) const; bool getString(int i, GooString *string) const; private: friend class Object; // for incRef/decRef // Reference counting. int incRef() { return ++ref; } int decRef() { return --ref; } XRef *xref; // the xref table for this PDF file std::vector elems; // array of elements std::atomic_int ref; // reference count mutable std::recursive_mutex mutex; }; #endif poppler-24.02.0/poppler/BBoxOutputDev.cc000066400000000000000000000137621455701731300200610ustar00rootroot00000000000000//======================================================================== // // BBoxOutputDev.cc // // This file is licensed under the GPLv2 or later // // Copyright 2020 sgerwk // Copyright 2022 Oliver Sander // //======================================================================== #include #include #include #define writingModeHorizontal 0 #define writingModeVertical 1 BBoxOutputDev::BBoxOutputDev() : BBoxOutputDev(true, true, true) { } BBoxOutputDev::BBoxOutputDev(bool textA, bool vectorA, bool rasterA) : BBoxOutputDev(textA, vectorA, rasterA, true) { } BBoxOutputDev::BBoxOutputDev(bool textA, bool vectorA, bool rasterA, bool lwidthA) { hasGraphics = false; text = textA; vector = vectorA; raster = rasterA; lwidth = lwidthA; } double BBoxOutputDev::getX1() const { return bb.x1; } double BBoxOutputDev::getY1() const { return bb.y1; } double BBoxOutputDev::getX2() const { return bb.x2; } double BBoxOutputDev::getY2() const { return bb.y2; } double BBoxOutputDev::getHasGraphics() const { return hasGraphics; } void BBoxOutputDev::endPage() { } void BBoxOutputDev::stroke(GfxState *state) { updatePath(&bb, state->getPath(), state); } void BBoxOutputDev::fill(GfxState *state) { updatePath(&bb, state->getPath(), state); } void BBoxOutputDev::eoFill(GfxState *state) { updatePath(&bb, state->getPath(), state); } void BBoxOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg) { updateImage(&bb, state); } void BBoxOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) { updateImage(&bb, state); } void BBoxOutputDev::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) { updateImage(&bb, state); } void BBoxOutputDev::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) { updateImage(&bb, state); } void BBoxOutputDev::drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, const Unicode *u, int uLen) { double leftent, rightent, ascent, descent; const double *fm, *fb; double fontSize, w, adjust; double fx, fy; if (!text) { return; } const GfxFont *const font = state->getFont().get(); if (!font) { return; } if (code == (CharCode)0x20) { return; } fontSize = state->getFontSize(); fb = font->getFontBBox(); if (font->getWMode() == writingModeHorizontal) { leftent = 0; rightent = 0; ascent = font->getAscent(); descent = font->getDescent(); } else { if (fb[0] == 0 && fb[1] == 0 && fb[2] == 0 && fb[3] == 0) { leftent = -0.5; rightent = 0.5; } else { leftent = fb[1]; rightent = fb[3]; } ascent = 0; descent = 0; } if (font->getType() != fontType3) { adjust = 1; } else { // adjust font size for type3 fonts, // similar to TextPage::updateFont() w = ((Gfx8BitFont *)font)->getWidth(code); adjust = w / 0.5; fm = font->getFontMatrix(); if (fm[0] != 0) { adjust *= fabs(fm[3] / fm[0]); } } ascent *= adjust * fontSize; descent *= adjust * fontSize; leftent *= adjust * fontSize; rightent *= adjust * fontSize; state->textTransformDelta(leftent, descent, &fx, &fy); updatePoint(&bb, fx + x, fy + y, state); state->textTransformDelta(rightent, ascent, &fx, &fy); updatePoint(&bb, fx + x, fy + y, state); state->textTransformDelta(leftent, descent, &fx, &fy); updatePoint(&bb, fx + x + dx, fy + y + dy, state); state->textTransformDelta(rightent, ascent, &fx, &fy); updatePoint(&bb, fx + x + dx, fy + y + dy, state); } /* update the bounding box with a new point */ void BBoxOutputDev::updatePoint(PDFRectangle *bbA, double x, double y, const GfxState *state) { Matrix o = { 1, 0, 0, 1, 0, 0 }; double tx, ty; double xMin, yMin, xMax, yMax; state->getClipBBox(&xMin, &yMin, &xMax, &yMax); o.scale(1, -1); o.translate(0, -state->getPageHeight()); state->transform(x, y, &tx, &ty); tx = tx < xMin ? xMin : tx > xMax ? xMax : tx; ty = ty < yMin ? yMin : ty > yMax ? yMax : ty; o.transform(tx, ty, &x, &y); if (!hasGraphics || bbA->x1 > x) { bbA->x1 = x; } if (!hasGraphics || bbA->y1 > y) { bbA->y1 = y; } if (!hasGraphics || bbA->x2 < x) { bbA->x2 = x; } if (!hasGraphics || bbA->y2 < y) { bbA->y2 = y; } hasGraphics = true; } /* update the bounding box with a new path */ void BBoxOutputDev::updatePath(PDFRectangle *bbA, const GfxPath *path, const GfxState *state) { int i, j; const GfxSubpath *subpath; double x, y; double w; if (!vector) { return; } w = lwidth ? state->getLineWidth() : 0; for (i = 0; i < path->getNumSubpaths(); i++) { subpath = path->getSubpath(i); for (j = 0; j < subpath->getNumPoints(); j++) { x = subpath->getX(j); y = subpath->getY(j); updatePoint(bbA, x - w / 2, y - w / 2, state); updatePoint(bbA, x + w / 2, y + w / 2, state); } } } /* update the bounding box with a new image */ void BBoxOutputDev::updateImage(PDFRectangle *bbA, const GfxState *state) { if (!raster) { return; } updatePoint(bbA, 0, 1, state); updatePoint(bbA, 1, 0, state); } poppler-24.02.0/poppler/BBoxOutputDev.h000066400000000000000000000045201455701731300177130ustar00rootroot00000000000000//======================================================================== // // BBoxOutputDev.cc // // This file is licensed under the GPLv2 or later // // Copyright 2020 sgerwk // //======================================================================== #include #include #include class POPPLER_PRIVATE_EXPORT BBoxOutputDev : public OutputDev { public: bool upsideDown() override { return false; } bool useDrawChar() override { return true; } bool interpretType3Chars() override { return false; } BBoxOutputDev(); BBoxOutputDev(bool text, bool vector, bool raster); BBoxOutputDev(bool text, bool vector, bool raster, bool lwidth); void endPage() override; void stroke(GfxState *state) override; void fill(GfxState *state) override; void eoFill(GfxState *state) override; void drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, const Unicode *u, int uLen) override; void drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg) override; void drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) override; 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; void 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) override; double getX1() const; double getY1() const; double getX2() const; double getY2() const; double getHasGraphics() const; private: PDFRectangle bb; bool hasGraphics; bool text; bool vector; bool raster; bool lwidth; void updatePoint(PDFRectangle *bbA, double x, double y, const GfxState *state); void updatePath(PDFRectangle *bbA, const GfxPath *path, const GfxState *state); void updateImage(PDFRectangle *bbA, const GfxState *state); }; poppler-24.02.0/poppler/BuiltinFont.h000066400000000000000000000121511455701731300174350ustar00rootroot00000000000000//======================================================================== // // BuiltinFont.h // // Copyright 2001-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) 2018, 2020 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 // //======================================================================== #ifndef BUILTINFONT_H #define BUILTINFONT_H #include "BuiltinFontWidth.h" //------------------------------------------------------------------------ using GetWidthFunction = const BuiltinFontWidth *(*)(const char *str, size_t len); struct BuiltinFont { const char *name; const char **defaultBaseEnc; short ascent; short descent; short bbox[4]; GetWidthFunction f; bool getWidth(const char *n, unsigned short *w) const { const struct BuiltinFontWidth *bfw = f(n, strlen(n)); if (!bfw) { return false; } *w = bfw->width; return true; } }; //------------------------------------------------------------------------ extern "C" { // define the gperf generated Lookup functions const struct BuiltinFontWidth *CourierWidthsLookup(const char *str, size_t len); const struct BuiltinFontWidth *CourierBoldWidthsLookup(const char *str, size_t len); const struct BuiltinFontWidth *CourierBoldObliqueWidthsLookup(const char *str, size_t len); const struct BuiltinFontWidth *CourierObliqueWidthsLookup(const char *str, size_t len); const struct BuiltinFontWidth *HelveticaWidthsLookup(const char *str, size_t len); const struct BuiltinFontWidth *HelveticaBoldWidthsLookup(const char *str, size_t len); const struct BuiltinFontWidth *HelveticaBoldObliqueWidthsLookup(const char *str, size_t len); const struct BuiltinFontWidth *HelveticaObliqueWidthsLookup(const char *str, size_t len); const struct BuiltinFontWidth *SymbolWidthsLookup(const char *str, size_t len); const struct BuiltinFontWidth *TimesBoldWidthsLookup(const char *str, size_t len); const struct BuiltinFontWidth *TimesBoldItalicWidthsLookup(const char *str, size_t len); const struct BuiltinFontWidth *TimesItalicWidthsLookup(const char *str, size_t len); const struct BuiltinFontWidth *TimesRomanWidthsLookup(const char *str, size_t len); const struct BuiltinFontWidth *ZapfDingbatsWidthsLookup(const char *str, size_t len); } static const BuiltinFont builtinFonts[] = { { "Courier", standardEncoding, 629, -157, { -23, -250, 715, 805 }, &CourierWidthsLookup }, { "Courier-Bold", standardEncoding, 629, -157, { -113, -250, 749, 801 }, &CourierBoldWidthsLookup }, { "Courier-BoldOblique", standardEncoding, 629, -157, { -57, -250, 869, 801 }, &CourierBoldObliqueWidthsLookup }, { "Courier-Oblique", standardEncoding, 629, -157, { -27, -250, 849, 805 }, &CourierObliqueWidthsLookup }, { "Helvetica", standardEncoding, 718, -207, { -166, -225, 1000, 931 }, &HelveticaWidthsLookup }, { "Helvetica-Bold", standardEncoding, 718, -207, { -170, -228, 1003, 962 }, &HelveticaBoldWidthsLookup }, { "Helvetica-BoldOblique", standardEncoding, 718, -207, { -174, -228, 1114, 962 }, &HelveticaBoldObliqueWidthsLookup }, { "Helvetica-Oblique", standardEncoding, 718, -207, { -170, -225, 1116, 931 }, &HelveticaObliqueWidthsLookup }, { "Symbol", symbolEncoding, 1010, -293, { -180, -293, 1090, 1010 }, &SymbolWidthsLookup }, { "Times-Bold", standardEncoding, 683, -217, { -168, -218, 1000, 935 }, &TimesBoldWidthsLookup }, { "Times-BoldItalic", standardEncoding, 683, -217, { -200, -218, 996, 921 }, &TimesBoldItalicWidthsLookup }, { "Times-Italic", standardEncoding, 683, -217, { -169, -217, 1010, 883 }, &TimesItalicWidthsLookup }, { "Times-Roman", standardEncoding, 683, -217, { -168, -218, 1000, 898 }, &TimesRomanWidthsLookup }, { "ZapfDingbats", zapfDingbatsEncoding, 820, -143, { -1, -143, 981, 820 }, &ZapfDingbatsWidthsLookup } }; static const BuiltinFont *builtinFontSubst[] = { &builtinFonts[0], &builtinFonts[3], &builtinFonts[1], &builtinFonts[2], &builtinFonts[4], &builtinFonts[7], &builtinFonts[5], &builtinFonts[6], &builtinFonts[12], &builtinFonts[11], &builtinFonts[9], &builtinFonts[10] }; //------------------------------------------------------------------------ #endif poppler-24.02.0/poppler/BuiltinFontWidth.h000066400000000000000000000016171455701731300204420ustar00rootroot00000000000000//======================================================================== // // BuiltinFontWidth.h // // Copyright 2001-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) 2020 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 // //======================================================================== #ifndef BUILTINFONTWIDTH_H #define BUILTINFONTWIDTH_H struct BuiltinFontWidth { const char *name; unsigned short width; }; #endif poppler-24.02.0/poppler/CIDFontsWidthsBuilder.h000066400000000000000000000145661455701731300213170ustar00rootroot00000000000000//======================================================================== // // CIDFontsWidthsBuilder.h // // This file is licensed under the GPLv2 or later // // Copyright 2023 g10 Code GmbH, Author: Sune Stolborg Vuorela //======================================================================== #ifndef CIDFontsWidthsBuilder_H #define CIDFontsWidthsBuilder_H #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.size()) { segmentDone(); } m_currentSegment = {}; } class SegmentBuilder { // How many elements at the end has this int uniqueElementsFromEnd(int value) { auto lastDifferent = std::find_if(m_values.rbegin(), m_values.rend(), [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::find_if(m_values.rbegin(), m_values.rend(), [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.size() && m_values.back() == savedValues.back()) { savedValues.push_back(m_values.back()); m_values.pop_back(); } } } ListSegment segment { m_firstIndex.value(), std::move(m_values) }; if (!savedValues.empty()) { m_firstIndex = m_lastIndex.value() - savedValues.size() + 1; } else { m_firstIndex = {}; m_lastIndex = {}; } m_values = std::move(savedValues); differentValues = false; return segment; } else { 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 poppler-24.02.0/poppler/CMap.cc000066400000000000000000000321671455701731300161670ustar00rootroot00000000000000//======================================================================== // // CMap.cc // // Copyright 2001-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) 2008 Koji Otani // Copyright (C) 2008, 2009, 2017-2021 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 // // 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 #include #include #include #include #include "goo/gmem.h" #include "goo/gfile.h" #include "goo/GooString.h" #include "Error.h" #include "GlobalParams.h" #include "PSTokenizer.h" #include "CMap.h" #include "Object.h" //------------------------------------------------------------------------ struct CMapVectorEntry { bool isVector; union { CMapVectorEntry *vector; CID cid; }; }; //------------------------------------------------------------------------ static int getCharFromFile(void *data) { return fgetc((FILE *)data); } static int getCharFromStream(void *data) { return ((Stream *)data)->getChar(); } //------------------------------------------------------------------------ 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()); if (!(cMap = globalParams->getCMap(collectionA, cMapNameA))) { 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"); } } else { error(errSyntaxError, -1, "Invalid Encoding in Type 0 font"); return {}; } return cMap; } 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-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); return {}; } auto cMap = std::shared_ptr(new CMap(collectionA->copy(), cMapNameA->copy())); cMap->parse2(cache, &getCharFromFile, f); fclose(f); return cMap; } std::shared_ptr CMap::parse(CMapCache *cache, const GooString *collectionA, Stream *str) { 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); str->close(); return cMap; } void CMap::parse2(CMapCache *cache, int (*getCharFunc)(void *), void *data) { PSTokenizer *pst; char tok1[256], tok2[256], tok3[256]; int n1, n2, n3; unsigned int start = 0, end = 0, code; pst = new PSTokenizer(getCharFunc, data); pst->getToken(tok1, sizeof(tok1), &n1); while (pst->getToken(tok2, sizeof(tok2), &n2)) { if (!strcmp(tok2, "usecmap")) { if (tok1[0] == '/') { useCMap(cache, tok1 + 1); } pst->getToken(tok1, sizeof(tok1), &n1); } else if (!strcmp(tok1, "/WMode")) { wMode = atoi(tok2); pst->getToken(tok1, sizeof(tok1), &n1); } else if (!strcmp(tok2, "begincidchar")) { while (pst->getToken(tok1, sizeof(tok1), &n1)) { if (!strcmp(tok1, "endcidchar")) { break; } if (!pst->getToken(tok2, sizeof(tok2), &n2) || !strcmp(tok2, "endcidchar")) { error(errSyntaxError, -1, "Illegal entry in cidchar block in CMap"); break; } if (!(tok1[0] == '<' && tok1[n1 - 1] == '>' && n1 >= 4 && (n1 & 1) == 0)) { error(errSyntaxError, -1, "Illegal entry in cidchar block in CMap"); continue; } tok1[n1 - 1] = '\0'; if (sscanf(tok1 + 1, "%x", &code) != 1) { error(errSyntaxError, -1, "Illegal entry in cidchar block in CMap"); continue; } n1 = (n1 - 2) / 2; addCIDs(code, code, n1, (CID)atoi(tok2)); } pst->getToken(tok1, sizeof(tok1), &n1); } else if (!strcmp(tok2, "begincidrange")) { while (pst->getToken(tok1, sizeof(tok1), &n1)) { if (!strcmp(tok1, "endcidrange")) { break; } if (!pst->getToken(tok2, sizeof(tok2), &n2) || !strcmp(tok2, "endcidrange") || !pst->getToken(tok3, sizeof(tok3), &n3) || !strcmp(tok3, "endcidrange")) { error(errSyntaxError, -1, "Illegal entry in cidrange block in CMap"); break; } if (tok1[0] == '<' && tok2[0] == '<' && n1 == n2 && n1 >= 4 && (n1 & 1) == 0) { tok1[n1 - 1] = tok2[n1 - 1] = '\0'; sscanf(tok1 + 1, "%x", &start); sscanf(tok2 + 1, "%x", &end); n1 = (n1 - 2) / 2; addCIDs(start, end, n1, (CID)atoi(tok3)); } } pst->getToken(tok1, sizeof(tok1), &n1); } else { strcpy(tok1, tok2); } } delete pst; } CMap::CMap(GooString *collectionA, GooString *cMapNameA) { int i; collection = collectionA; cMapName = cMapNameA; isIdent = false; wMode = 0; vector = (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry)); for (i = 0; i < 256; ++i) { vector[i].isVector = false; vector[i].cid = 0; } } CMap::CMap(GooString *collectionA, GooString *cMapNameA, int wModeA) { collection = collectionA; cMapName = cMapNameA; isIdent = true; wMode = wModeA; vector = nullptr; } void CMap::useCMap(CMapCache *cache, const char *useName) { GooString *useNameStr; std::shared_ptr subCMap; useNameStr = new GooString(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); } else { subCMap = globalParams->getCMap(collection, useNameStr); } delete useNameStr; if (!subCMap) { return; } isIdent = subCMap->isIdent; if (subCMap->vector) { copyVector(vector, subCMap->vector); } } void CMap::useCMap(CMapCache *cache, Object *obj) { std::shared_ptr subCMap = CMap::parse(cache, collection, obj); if (!subCMap) { return; } isIdent = subCMap->isIdent; if (subCMap->vector) { copyVector(vector, subCMap->vector); } } void CMap::copyVector(CMapVectorEntry *dest, CMapVectorEntry *src) { int i, j; for (i = 0; i < 256; ++i) { if (src[i].isVector) { if (!dest[i].isVector) { dest[i].isVector = true; dest[i].vector = (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry)); for (j = 0; j < 256; ++j) { dest[i].vector[j].isVector = false; dest[i].vector[j].cid = 0; } } copyVector(dest[i].vector, src[i].vector); } else { if (dest[i].isVector) { error(errSyntaxError, -1, "Collision in usecmap"); } else { dest[i].cid = src[i].cid; } } } } void CMap::addCIDs(unsigned int start, unsigned int end, unsigned int nBytes, CID firstCID) { if (nBytes > 4) { error(errSyntaxError, -1, "Illegal entry in cidchar block in CMap"); return; } const unsigned int start1 = start & 0xffffff00; const unsigned int end1 = end & 0xffffff00; for (unsigned int i = start1; i <= end1; i += 0x100) { CMapVectorEntry *vec = vector; for (unsigned int j = nBytes - 1; j >= 1; --j) { const int byte = (i >> (8 * j)) & 0xff; if (!vec[byte].isVector) { vec[byte].isVector = true; vec[byte].vector = (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry)); for (unsigned int k = 0; k < 256; ++k) { vec[byte].vector[k].isVector = false; vec[byte].vector[k].cid = 0; } } vec = vec[byte].vector; } const int byte0 = (i < start) ? (start & 0xff) : 0; const int byte1 = (i + 0xff > end) ? (end & 0xff) : 0xff; for (int byte = byte0; byte <= byte1; ++byte) { if (vec[byte].isVector) { error(errSyntaxError, -1, "Invalid CID ({0:ux} [{1:ud} bytes]) in CMap", i, nBytes); } else { vec[byte].cid = firstCID + ((i + byte) - start); } } } } CMap::~CMap() { delete collection; delete cMapName; if (vector) { freeCMapVector(vector); } } void CMap::freeCMapVector(CMapVectorEntry *vec) { int i; for (i = 0; i < 256; ++i) { if (vec[i].isVector) { freeCMapVector(vec[i].vector); } } gfree(vec); } bool CMap::match(const GooString *collectionA, const GooString *cMapNameA) { return !collection->cmp(collectionA) && !cMapName->cmp(cMapNameA); } CID CMap::getCID(const char *s, int len, CharCode *c, int *nUsed) { CMapVectorEntry *vec; CharCode cc; int n, i; vec = vector; cc = 0; n = 0; while (vec && n < len) { i = s[n++] & 0xff; cc = (cc << 8) | i; if (!vec[i].isVector) { *c = cc; *nUsed = n; return vec[i].cid; } vec = vec[i].vector; } if (isIdent && len >= 2) { // identity CMap *nUsed = 2; *c = cc = ((s[0] & 0xff) << 8) + (s[1] & 0xff); return cc; } *nUsed = 1; *c = s[0] & 0xff; return 0; } void CMap::setReverseMapVector(unsigned int startCode, CMapVectorEntry *vec, unsigned int *rmap, unsigned int rmapSize, unsigned int ncand) { int i; if (vec == nullptr) { return; } for (i = 0; i < 256; i++) { if (vec[i].isVector) { setReverseMapVector((startCode + i) << 8, vec[i].vector, rmap, rmapSize, ncand); } else { unsigned int cid = vec[i].cid; if (cid < rmapSize) { unsigned int cand; for (cand = 0; cand < ncand; cand++) { unsigned int code = startCode + i; unsigned int idx = cid * ncand + cand; if (rmap[idx] == 0) { rmap[idx] = code; break; } else if (rmap[idx] == code) { break; } } } } } } void CMap::setReverseMap(unsigned int *rmap, unsigned int rmapSize, unsigned int ncand) { setReverseMapVector(0, vector, rmap, rmapSize, ncand); } //------------------------------------------------------------------------ CMapCache::CMapCache() { } std::shared_ptr CMapCache::getCMap(const GooString *collection, const GooString *cMapName) { int i, j; if (cache[0] && cache[0]->match(collection, cMapName)) { return cache[0]; } for (i = 1; i < cMapCacheSize; ++i) { if (cache[i] && cache[i]->match(collection, cMapName)) { std::shared_ptr cmap = cache[i]; for (j = i; j >= 1; --j) { cache[j] = cache[j - 1]; } cache[0] = cmap; return cmap; } } std::shared_ptr cmap = CMap::parse(this, collection, cMapName); if (cmap) { for (j = cMapCacheSize - 1; j >= 1; --j) { cache[j] = cache[j - 1]; } cache[0] = cmap; return cmap; } return {}; } poppler-24.02.0/poppler/CMap.h000066400000000000000000000111161455701731300160200ustar00rootroot00000000000000//======================================================================== // // CMap.h // // Copyright 2001-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) 2008 Koji Otani // Copyright (C) 2009, 2018-2020, 2022 Albert Astals Cid // Copyright (C) 2012, 2017 Adrian Johnson // Copyright (C) 2018 Adam Reichold // // 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 CMAP_H #define CMAP_H #include #include #include #include "poppler-config.h" #include "CharTypes.h" class GooString; class Object; struct CMapVectorEntry; class CMapCache; class Stream; //------------------------------------------------------------------------ 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); // 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); // 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); ~CMap(); CMap(const CMap &) = delete; CMap &operator=(const CMap &) = delete; // Return collection name (-). const GooString *getCollection() const { return collection; } const GooString *getCMapName() const { return cMapName; } // Return true if this CMap matches the specified , and // . 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 // * to the number of bytes used by the char code. CID getCID(const char *s, int len, CharCode *c, int *nUsed); // Return the writing mode (0=horizontal, 1=vertical). int getWMode() const { return wMode; } void setReverseMap(unsigned int *rmap, unsigned int rmapSize, unsigned int ncand); private: void parse2(CMapCache *cache, int (*getCharFunc)(void *), void *data); CMap(GooString *collectionA, GooString *cMapNameA); CMap(GooString *collectionA, GooString *cMapNameA, int wModeA); void useCMap(CMapCache *cache, const char *useName); void useCMap(CMapCache *cache, Object *obj); void copyVector(CMapVectorEntry *dest, CMapVectorEntry *src); void addCIDs(unsigned int start, unsigned int end, unsigned int nBytes, CID firstCID); void freeCMapVector(CMapVectorEntry *vec); void setReverseMapVector(unsigned int startCode, CMapVectorEntry *vec, unsigned int *rmap, unsigned int rmapSize, unsigned int ncand); GooString *collection; GooString *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) CMapVectorEntry *vector; // vector for first byte (NULL for // identity CMap) }; //------------------------------------------------------------------------ #define cMapCacheSize 4 class CMapCache { public: CMapCache(); ~CMapCache() = default; CMapCache(const CMapCache &) = delete; CMapCache &operator=(const CMapCache &) = delete; // Get the CMap for the specified character collection. // Increments its reference count; there will be one reference for // the cache plus one for the caller of this function. // 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); private: std::array, cMapCacheSize> cache; }; #endif poppler-24.02.0/poppler/CachedFile.cc000066400000000000000000000150271455701731300173120ustar00rootroot00000000000000//======================================================================== // // CachedFile.cc // // This file is licensed under the GPLv2 or later // // Copyright 2009 Stefan Thomas // Copyright 2010, 2011 Hib Eris // Copyright 2010, 2018-2020, 2022 Albert Astals Cid // Copyright (C) 2013 Julien Nabet // //======================================================================== #include #include "CachedFile.h" //------------------------------------------------------------------------ // CachedFile //------------------------------------------------------------------------ CachedFile::CachedFile(CachedFileLoader *cacheLoader) { loader = cacheLoader; streamPos = 0; chunks = new std::vector(); length = 0; length = loader->init(this); refCnt = 1; if (length != ((size_t)-1)) { chunks->resize(length / CachedFileChunkSize + 1); } else { error(errInternal, -1, "Failed to initialize file cache."); chunks->resize(0); } } CachedFile::~CachedFile() { delete loader; delete chunks; } void CachedFile::incRefCnt() { refCnt++; } void CachedFile::decRefCnt() { if (--refCnt == 0) { delete this; } } long int CachedFile::tell() { return streamPos; } int CachedFile::seek(long int offset, int origin) { if (origin == SEEK_SET) { streamPos = offset; } else if (origin == SEEK_CUR) { streamPos += offset; } else { streamPos = length + offset; } if (streamPos > length) { streamPos = 0; return 1; } return 0; } int CachedFile::cache(const std::vector &origRanges) { std::vector loadChunks; int numChunks = length / CachedFileChunkSize + 1; std::vector chunkNeeded(numChunks); int startChunk, endChunk; std::vector chunk_ranges, all; ByteRange range; const std::vector *ranges = &origRanges; if (ranges->empty()) { range.offset = 0; range.length = length; all.push_back(range); ranges = &all; } for (int i = 0; i < numChunks; ++i) { chunkNeeded[i] = false; } for (const ByteRange &r : *ranges) { if (r.length == 0) { continue; } if (r.offset >= length) { continue; } const size_t start = r.offset; size_t end = start + r.length - 1; if (end >= length) { end = length - 1; } startChunk = start / CachedFileChunkSize; endChunk = end / CachedFileChunkSize; for (int chunk = startChunk; chunk <= endChunk; chunk++) { if ((*chunks)[chunk].state == chunkStateNew) { chunkNeeded[chunk] = true; } } } int chunk = 0; while (chunk < numChunks) { while (!chunkNeeded[chunk] && (++chunk != numChunks)) { ; } if (chunk == numChunks) { break; } startChunk = chunk; loadChunks.push_back(chunk); while ((++chunk != numChunks) && chunkNeeded[chunk]) { loadChunks.push_back(chunk); } endChunk = chunk - 1; range.offset = startChunk * CachedFileChunkSize; range.length = (endChunk - startChunk + 1) * CachedFileChunkSize; chunk_ranges.push_back(range); } if (chunk_ranges.size() > 0) { CachedFileWriter writer = CachedFileWriter(this, &loadChunks); return loader->load(chunk_ranges, &writer); } return 0; } size_t CachedFile::read(void *ptr, size_t unitsize, size_t count) { size_t bytes = unitsize * count; if (length < (streamPos + bytes)) { bytes = length - streamPos; } if (bytes == 0) { return 0; } // Load data if (cache(streamPos, bytes) != 0) { return 0; } // Copy data to buffer size_t toCopy = bytes; while (toCopy) { int chunk = streamPos / CachedFileChunkSize; int offset = streamPos % CachedFileChunkSize; size_t len = CachedFileChunkSize - offset; if (len > toCopy) { len = toCopy; } memcpy(ptr, (*chunks)[chunk].data + offset, len); streamPos += len; toCopy -= len; ptr = (char *)ptr + len; } return bytes; } int CachedFile::cache(size_t rangeOffset, size_t rangeLength) { std::vector r; ByteRange range; range.offset = rangeOffset; range.length = rangeLength; r.push_back(range); return cache(r); } //------------------------------------------------------------------------ // CachedFileWriter //------------------------------------------------------------------------ CachedFileWriter::CachedFileWriter(CachedFile *cachedFileA, std::vector *chunksA) { cachedFile = cachedFileA; chunks = chunksA; if (chunks) { offset = 0; it = (*chunks).begin(); } } CachedFileWriter::~CachedFileWriter() { } size_t CachedFileWriter::write(const char *ptr, size_t size) { const char *cp = ptr; size_t len = size; size_t nfree, ncopy; size_t written = 0; size_t chunk; if (!len) { return 0; } while (len) { if (chunks) { if (offset == CachedFileChunkSize) { ++it; if (it == (*chunks).end()) { return written; } offset = 0; } chunk = *it; } else { offset = cachedFile->length % CachedFileChunkSize; chunk = cachedFile->length / CachedFileChunkSize; } if (chunk >= cachedFile->chunks->size()) { cachedFile->chunks->resize(chunk + 1); } nfree = CachedFileChunkSize - offset; ncopy = (len >= nfree) ? nfree : len; memcpy(&((*cachedFile->chunks)[chunk].data[offset]), cp, ncopy); len -= ncopy; cp += ncopy; offset += ncopy; written += ncopy; if (!chunks) { cachedFile->length += ncopy; } if (offset == CachedFileChunkSize) { (*cachedFile->chunks)[chunk].state = CachedFile::chunkStateLoaded; } } if ((chunk == (cachedFile->length / CachedFileChunkSize)) && (offset == (cachedFile->length % CachedFileChunkSize))) { (*cachedFile->chunks)[chunk].state = CachedFile::chunkStateLoaded; } return written; } CachedFileLoader::~CachedFileLoader() = default; //------------------------------------------------------------------------ poppler-24.02.0/poppler/CachedFile.h000066400000000000000000000101631455701731300171500ustar00rootroot00000000000000//======================================================================== // // CachedFile.h // // Caching files support. // // This file is licensed under the GPLv2 or later // // Copyright 2009 Stefan Thomas // Copyright 2010 Hib Eris // Copyright 2010, 2018-2020, 2022 Albert Astals Cid // //======================================================================== #ifndef CACHEDFILE_H #define CACHEDFILE_H #include "poppler-config.h" #include "poppler_private_export.h" #include "Object.h" #include "Stream.h" #include //------------------------------------------------------------------------ #define CachedFileChunkSize 8192 // This should be a multiple of cachedStreamBufSize class GooString; class CachedFileLoader; //------------------------------------------------------------------------ // CachedFile // // CachedFile gives FILE-like access to a document at a specified URI. // In the constructor, you specify a CachedFileLoader that handles loading // the data from the document. The CachedFile requests no more data then it // needs from the CachedFileLoader. //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT CachedFile { friend class CachedFileWriter; public: explicit CachedFile(CachedFileLoader *cacheLoader); CachedFile(const CachedFile &) = delete; CachedFile &operator=(const CachedFile &) = delete; unsigned int getLength() const { return length; } long int tell(); int seek(long int offset, int origin); size_t read(void *ptr, size_t unitsize, size_t count); size_t write(const char *ptr, size_t size, size_t fromByte); int cache(const std::vector &ranges); // Reference counting. void incRefCnt(); void decRefCnt(); private: ~CachedFile(); enum ChunkState { chunkStateNew = 0, chunkStateLoaded }; typedef struct { ChunkState state; char data[CachedFileChunkSize]; } Chunk; int cache(size_t offset, size_t length); CachedFileLoader *loader; size_t length; size_t streamPos; std::vector *chunks; int refCnt; // reference count }; //------------------------------------------------------------------------ // CachedFileWriter // // CachedFileWriter handles sequential writes to a CachedFile. // On construction, you specify the CachedFile and the chunks of it to which data // should be written. //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT CachedFileWriter { public: // Construct a CachedFile Writer. // The caller is responsible for deleting the cachedFile and chunksA. CachedFileWriter(CachedFile *cachedFile, std::vector *chunksA); ~CachedFileWriter(); // Writes size bytes from ptr to cachedFile, returns number of bytes written. size_t write(const char *ptr, size_t size); private: CachedFile *cachedFile; std::vector *chunks; std::vector::iterator it; size_t offset; }; //------------------------------------------------------------------------ // CachedFileLoader // // CachedFileLoader is an abstact class that specifies the interface for // loadng data from an URI into a CachedFile. //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT CachedFileLoader { public: CachedFileLoader() = default; virtual ~CachedFileLoader(); CachedFileLoader(const CachedFileLoader &) = delete; CachedFileLoader &operator=(const CachedFileLoader &) = delete; // Initializes the file load. // Returns the length of the file. // The caller is responsible for deleting cachedFile. virtual size_t init(CachedFile *cachedFile) = 0; // Loads specified byte ranges and passes it to the writer to store them. // Returns 0 on success, Anything but 0 on failure. // The caller is responsible for deleting the writer. virtual int load(const std::vector &ranges, CachedFileWriter *writer) = 0; }; //------------------------------------------------------------------------ #endif poppler-24.02.0/poppler/CairoFontEngine.cc000066400000000000000000000604061455701731300203560ustar00rootroot00000000000000//======================================================================== // // CairoFontEngine.cc // // Copyright 2003 Glyph & Cog, LLC // Copyright 2004 Red Hat, Inc // //======================================================================== //======================================================================== // // 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) 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) 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, 2023 Adrian Johnson // Copyright (C) 2009 Darren Kenny // Copyright (C) 2010 Suzuki Toshiya // Copyright (C) 2010 Jan Kümmel // Copyright (C) 2012 Hib Eris // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2015, 2016 Jason Crain // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019 Christian Persch // 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 // // 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 #include #include #include "CairoFontEngine.h" #include "CairoOutputDev.h" #include "GlobalParams.h" #include #include #include "goo/ft_utils.h" #include "goo/gfile.h" #include "Error.h" #include "XRef.h" #include "Gfx.h" #include "Page.h" //------------------------------------------------------------------------ // CairoFont //------------------------------------------------------------------------ CairoFont::CairoFont(Ref refA, cairo_font_face_t *cairo_font_faceA, std::vector &&codeToGIDA, bool substituteA, bool printingA) : ref(refA), cairo_font_face(cairo_font_faceA), substitute(substituteA), printing(printingA) { codeToGID = std::move(codeToGIDA); } CairoFont::~CairoFont() { cairo_font_face_destroy(cairo_font_face); } bool CairoFont::matches(Ref &other, bool printingA) { return (other == ref); } cairo_font_face_t *CairoFont::getFontFace() { return cairo_font_face; } unsigned long CairoFont::getGlyph(CharCode code, const Unicode *u, int uLen) { FT_UInt gid; if (code < codeToGID.size()) { gid = (FT_UInt)codeToGID[code]; } else { gid = (FT_UInt)code; } return gid; } double CairoFont::getSubstitutionCorrection(const std::shared_ptr &gfxFont) { double w1, w2, w3; CharCode code; const char *name; // for substituted fonts: adjust the font matrix -- compare the // width of 'm' in the original font and the substituted font if (isSubstitute() && !gfxFont->isCIDFont()) { for (code = 0; code < 256; ++code) { if ((name = std::static_pointer_cast(gfxFont)->getCharName(code)) && name[0] == 'm' && name[1] == '\0') { break; } } if (code < 256) { w1 = std::static_pointer_cast(gfxFont)->getWidth(code); { cairo_matrix_t m; cairo_matrix_init_identity(&m); cairo_font_options_t *options = cairo_font_options_create(); cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_NONE); cairo_font_options_set_hint_metrics(options, CAIRO_HINT_METRICS_OFF); cairo_scaled_font_t *scaled_font = cairo_scaled_font_create(cairo_font_face, &m, &m, options); cairo_text_extents_t extents; cairo_scaled_font_text_extents(scaled_font, "m", &extents); cairo_scaled_font_destroy(scaled_font); cairo_font_options_destroy(options); w2 = extents.x_advance; } w3 = std::static_pointer_cast(gfxFont)->getWidth(0); if (!gfxFont->isSymbolic() && w2 > 0 && w1 > w3) { // if real font is substantially narrower than substituted // font, reduce the font size accordingly if (w1 > 0.01 && w1 < 0.9 * w2) { w1 /= w2; return w1; } } } } return 1.0; } //------------------------------------------------------------------------ // CairoFreeTypeFont //------------------------------------------------------------------------ static cairo_user_data_key_t ft_cairo_key; // Font resources to be freed when cairo_font_face_t is destroyed struct FreeTypeFontResource { FT_Face face; std::vector font_data; }; // cairo callback for when cairo_font_face_t is destroyed static void _ft_done_face(void *closure) { FreeTypeFontResource *resource = (FreeTypeFontResource *)closure; FT_Done_Face(resource->face); delete resource; } 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() { } // 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) { FreeTypeFontResource *resource = new FreeTypeFontResource; FreeTypeFontFace font_face; if (font_data.empty()) { FT_Error err = ft_new_face_from_file(lib, filename.c_str(), 0, &resource->face); if (err) { delete resource; return {}; } } else { resource->font_data = std::move(font_data); FT_Error err = FT_New_Memory_Face(lib, (FT_Byte *)resource->font_data.data(), resource->font_data.size(), 0, &resource->face); if (err) { delete resource; return {}; } } font_face.cairo_font_face = cairo_ft_font_face_create_for_ft_face(resource->face, FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP); if (cairo_font_face_set_user_data(font_face.cairo_font_face, &ft_cairo_key, resource, _ft_done_face)) { cairo_font_face_destroy(font_face.cairo_font_face); _ft_done_face(resource); return {}; } font_face.face = resource->face; 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; std::vector font_data; int i, n; std::optional fontLoc; char **enc; const char *name; FoFiType1C *ff1c; std::optional font_face; std::vector codeToGID; bool substitute = false; Ref ref = *gfxFont->getID(); Ref embFontID = Ref::INVALID(); gfxFont->getEmbeddedFontID(&embFontID); 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)"); goto err2; } // embedded font if (fontLoc->locType == gfxFontLocEmbedded) { auto fd = gfxFont->readEmbFontFile(xref); if (!fd || fd->empty()) { goto err2; } font_data = std::move(fd.value()); // external font } else { // gfxFontLocExternal fileName = fontLoc->path; fontType = fontLoc->fontType; substitute = true; } switch (fontType) { case fontType1: case fontType1C: case fontType1COT: font_face = getFreeTypeFontFace(fontEngine, lib, fileName, std::move(font_data)); if (!font_face) { error(errSyntaxError, -1, "could not create type1 face"); goto err2; } enc = std::static_pointer_cast(gfxFont)->getEncoding(); codeToGID.resize(256); for (i = 0; i < 256; ++i) { codeToGID[i] = 0; if ((name = enc[i])) { codeToGID[i] = FT_Get_Name_Index(font_face->face, (char *)name); if (codeToGID[i] == 0) { Unicode u; u = globalParams->mapNameToUnicodeText(name); codeToGID[i] = FT_Get_Char_Index(font_face->face, u); } if (codeToGID[i] == 0) { name = GfxFont::getAlternateName(name); if (name) { codeToGID[i] = FT_Get_Name_Index(font_face->face, (char *)name); } } } } 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); } } else { std::unique_ptr ff; if (!font_data.empty()) { ff = FoFiTrueType::make(font_data.data(), font_data.size()); } else { ff = FoFiTrueType::load(fileName.c_str()); } 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); } /* Fall through */ case fontTrueType: case fontTrueTypeOT: { std::unique_ptr ff; if (!font_data.empty()) { ff = FoFiTrueType::make(font_data.data(), font_data.size()); } else { ff = FoFiTrueType::load(fileName.c_str()); } if (!ff) { error(errSyntaxError, -1, "failed to load truetype font\n"); 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); } font_face = getFreeTypeFontFace(fontEngine, lib, fileName, std::move(font_data)); if (!font_face) { error(errSyntaxError, -1, "could not create truetype face\n"); goto err2; } break; } case fontCIDType0: case fontCIDType0C: if (!useCIDs) { if (!font_data.empty()) { ff1c = FoFiType1C::make(font_data.data(), font_data.size()); } else { 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); delete ff1c; } } font_face = getFreeTypeFontFace(fontEngine, lib, fileName, std::move(font_data)); if (!font_face) { error(errSyntaxError, -1, "could not create cid face\n"); 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 (codeToGID.empty()) { if (!useCIDs) { std::unique_ptr ff; if (!font_data.empty()) { ff = FoFiTrueType::make(font_data.data(), font_data.size()); } else { ff = FoFiTrueType::load(fileName.c_str()); } if (ff) { if (ff->isOpenTypeCFF()) { int *src = ff->getCIDToGIDMap(&n); codeToGID.reserve(n); codeToGID.insert(codeToGID.begin(), src, src + n); gfree(src); } } } } font_face = getFreeTypeFontFace(fontEngine, lib, fileName, std::move(font_data)); if (!font_face) { error(errSyntaxError, -1, "could not create cid (OT) face\n"); goto err2; } break; default: fprintf(stderr, "font type %d not handled\n", (int)fontType); goto err2; break; } return new CairoFreeTypeFont(ref, font_face->cairo_font_face, std::move(codeToGID), substitute); err2: fprintf(stderr, "some font thing failed\n"); return nullptr; } //------------------------------------------------------------------------ // CairoType3Font //------------------------------------------------------------------------ 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, CairoOutputDev *outputDevA, Gfx *gfxA) : font(fontA), doc(docA), fontEngine(fontEngineA), outputDev(outputDevA), gfx(gfxA) { } std::shared_ptr font; PDFDoc *doc; CairoFontEngine *fontEngine; 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; } static cairo_status_t _init_type3_glyph(cairo_scaled_font_t *scaled_font, cairo_t *cr, cairo_font_extents_t *extents) { type3_font_info_t *info; info = (type3_font_info_t *)cairo_font_face_get_user_data(cairo_scaled_font_get_font_face(scaled_font), &type3_font_key); const double *mat = info->font->getFontBBox(); extents->ascent = mat[3]; /* y2 */ extents->descent = -mat[3]; /* -y1 */ extents->height = extents->ascent + extents->descent; extents->max_x_advance = mat[2] - mat[1]; /* x2 - x1 */ extents->max_y_advance = 0; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _render_type3_glyph(cairo_scaled_font_t *scaled_font, unsigned long glyph, cairo_t *cr, cairo_text_extents_t *metrics, bool color) { Dict *charProcs; Object charProc; cairo_matrix_t matrix, invert_y_axis; const double *mat; double wx, wy; 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); charProcs = std::static_pointer_cast(info->font)->getCharProcs(); if (!charProcs) { return CAIRO_STATUS_USER_FONT_ERROR; } if ((int)glyph >= charProcs->getLength()) { return CAIRO_STATUS_USER_FONT_ERROR; } mat = info->font->getFontMatrix(); matrix.xx = mat[0]; matrix.yx = mat[1]; matrix.xy = mat[2]; matrix.yy = mat[3]; matrix.x0 = mat[4]; matrix.y0 = mat[5]; cairo_matrix_init_scale(&invert_y_axis, 1, -1); cairo_matrix_multiply(&matrix, &matrix, &invert_y_axis); cairo_transform(cr, &matrix); #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); gfx = info->gfx; gfx->saveState(); output_dev->startDoc(info->doc, info->fontEngine); output_dev->startType3Render(gfx->getState(), gfx->getXRef()); output_dev->setType3RenderType(color ? CairoOutputDev::Type3RenderColor : CairoOutputDev::Type3RenderMask); charProc = charProcs->getVal(glyph); if (!charProc.isStream()) { return CAIRO_STATUS_USER_FONT_ERROR; } Object charProcResObject = charProc.streamGetDict()->lookup("Resources"); if (charProcResObject.isDict()) { gfx->pushResources(charProcResObject.getDict()); } gfx->display(&charProc); if (charProcResObject.isDict()) { gfx->popResources(); } output_dev->getType3GlyphWidth(&wx, &wy); cairo_matrix_transform_distance(&matrix, &wx, &wy); metrics->x_advance = wx; metrics->y_advance = wy; if (output_dev->hasType3GlyphBBox()) { double *bbox = output_dev->getType3GlyphBBox(); cairo_matrix_transform_point(&matrix, &bbox[0], &bbox[1]); cairo_matrix_transform_point(&matrix, &bbox[2], &bbox[3]); metrics->x_bearing = bbox[0]; metrics->y_bearing = bbox[1]; metrics->width = bbox[2] - bbox[0]; metrics->height = bbox[3] - bbox[1]; } status = CAIRO_STATUS_SUCCESS; // If this is a render color glyph callback but the Type 3 glyph // specified non-color, return NOT_IMPLEMENTED. Cairo will then // call the render non-color glyph callback. if (color && !output_dev->type3GlyphHasColor()) { status = CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED; } return status; } #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); } #endif static cairo_status_t _render_type3_noncolor_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, false); } CairoType3Font *CairoType3Font::create(const std::shared_ptr &gfxFont, PDFDoc *doc, CairoFontEngine *fontEngine, bool printing, XRef *xref) { 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, 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); 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; if (charProcs && (name = enc[i])) { for (int j = 0; j < charProcs->getLength(); j++) { if (strcmp(name, charProcs->getKey(j)) == 0) { codeToGID[i] = j; } } } } return new CairoType3Font(ref, font_face, std::move(codeToGID), printing, xref); } 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() { } bool CairoType3Font::matches(Ref &other, bool printingA) { return (other == ref && printing == printingA); } //------------------------------------------------------------------------ // CairoFontEngine //------------------------------------------------------------------------ std::unordered_map CairoFontEngine::fontFileCache; std::recursive_mutex CairoFontEngine::fontFileCacheMutex; CairoFontEngine::CairoFontEngine(FT_Library libA) { lib = libA; fontCache.reserve(cairoFontCacheSize); FT_Int major, minor, patch; // as of FT 2.1.8, CID fonts are indexed by CID instead of GID FT_Library_Version(lib, &major, &minor, &patch); useCIDs = major > 2 || (major == 2 && (minor > 1 || (minor == 1 && patch > 7))); } CairoFontEngine::~CairoFontEngine() { } std::shared_ptr CairoFontEngine::getFont(const std::shared_ptr &gfxFont, PDFDoc *doc, bool printing, XRef *xref) { std::scoped_lock lock(mutex); Ref ref = *gfxFont->getID(); std::shared_ptr font; // Check if font is in the MRU cache, and move it to the end if it is. for (auto it = fontCache.rbegin(); it != fontCache.rend(); ++it) { if ((*it)->matches(ref, printing)) { font = *it; // move it to the end if (it != fontCache.rbegin()) { // https://stackoverflow.com/questions/1830158/how-to-call-erase-with-a-reverse-iterator fontCache.erase(std::next(it).base()); fontCache.push_back(font); } return font; } } GfxFontType fontType = gfxFont->getType(); if (fontType == fontType3) { font = std::shared_ptr(CairoType3Font::create(gfxFont, doc, this, printing, xref)); } else { font = std::shared_ptr(CairoFreeTypeFont::create(gfxFont, xref, lib, this, useCIDs)); } if (font) { if (fontCache.size() == cairoFontCacheSize) { fontCache.erase(fontCache.begin()); } fontCache.push_back(font); } return font; } std::optional 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; } poppler-24.02.0/poppler/CairoFontEngine.h000066400000000000000000000112231455701731300202110ustar00rootroot00000000000000//======================================================================== // // CairoFontEngine.h // // Copyright 2003 Glyph & Cog, LLC // Copyright 2004 Red Hat, Inc // //======================================================================== //======================================================================== // // 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) 2005, 2006 Kristian Høgsberg // 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) 2013 Thomas Freitag // Copyright (C) 2018 Adam Reichold // Copyright (C) 2022 Oliver Sander // Copyright (C) 2022 Marek Kasik // // 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 CAIROFONTENGINE_H #define CAIROFONTENGINE_H #include #include #include #include #include "poppler-config.h" #include #include "GfxFont.h" #include "PDFDoc.h" class CairoFontEngine; class CairoFont { public: CairoFont(Ref refA, cairo_font_face_t *cairo_font_faceA, std::vector &&codeToGIDA, bool substituteA, bool printingA); virtual ~CairoFont(); CairoFont(const CairoFont &) = delete; CairoFont &operator=(const CairoFont &other) = delete; virtual bool matches(Ref &other, bool printing); cairo_font_face_t *getFontFace(); unsigned long getGlyph(CharCode code, const Unicode *u, int uLen); double getSubstitutionCorrection(const std::shared_ptr &gfxFont); bool isSubstitute() { return substitute; } Ref getRef() { return ref; } protected: Ref ref; cairo_font_face_t *cairo_font_face; std::vector codeToGID; bool substitute; bool printing; }; //------------------------------------------------------------------------ struct FreeTypeFontFace { FT_Face face; cairo_font_face_t *cairo_font_face; }; class CairoFreeTypeFont : public CairoFont { public: static CairoFreeTypeFont *create(const std::shared_ptr &gfxFont, XRef *xref, FT_Library lib, CairoFontEngine *fontEngine, bool useCIDs); ~CairoFreeTypeFont() override; 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); }; //------------------------------------------------------------------------ class CairoType3Font : public CairoFont { public: static CairoType3Font *create(const std::shared_ptr &gfxFont, PDFDoc *doc, CairoFontEngine *fontEngine, bool printing, XRef *xref); ~CairoType3Font() override; bool matches(Ref &other, bool printing) override; private: CairoType3Font(Ref ref, cairo_font_face_t *cairo_font_face, std::vector &&codeToGIDA, bool printing, XRef *xref); }; //------------------------------------------------------------------------ //------------------------------------------------------------------------ // CairoFontEngine //------------------------------------------------------------------------ class CairoFontEngine { public: // Create a font engine. explicit CairoFontEngine(FT_Library libA); ~CairoFontEngine(); CairoFontEngine(const CairoFontEngine &) = delete; CairoFontEngine &operator=(const CairoFontEngine &other) = delete; 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; mutable std::mutex mutex; // Cache of CairoFont for current document // 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 poppler-24.02.0/poppler/CairoOutputDev.cc000066400000000000000000004044231455701731300202620ustar00rootroot00000000000000//======================================================================== // // CairoOutputDev.cc // // Copyright 2003 Glyph & Cog, LLC // Copyright 2004 Red Hat, Inc // //======================================================================== //======================================================================== // // 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) 2005-2008 Jeff Muizelaar // Copyright (C) 2005, 2006 Kristian Høgsberg // Copyright (C) 2005, 2009, 2012, 2017-2021, 2023 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-2023 Adrian Johnson // Copyright (C) 2008 Michael Vrable // Copyright (C) 2008, 2009 Chris Wilson // Copyright (C) 2008, 2012 Hib Eris // Copyright (C) 2009, 2010 David Benjamin // Copyright (C) 2011-2014 Thomas Freitag // Copyright (C) 2012 Patrick Pfeifer // Copyright (C) 2012, 2015, 2016 Jason Crain // Copyright (C) 2015 Suzuki Toshiya // 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, 2022 Marek Kasik // Copyright (C) 2020 Michal // 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 // // 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 #include #include #include #include #include #include "goo/gfile.h" #include "GlobalParams.h" #include "Error.h" #include "Object.h" #include "Gfx.h" #include "GfxState.h" #include "GfxFont.h" #include "Page.h" #include "Link.h" #include "FontEncodingTables.h" #include "PDFDocEncoding.h" #include #include #include "CairoOutputDev.h" #include "CairoFontEngine.h" #include "CairoRescaleBox.h" #include "UnicodeMap.h" #include "UTF.h" #include "JBIG2Stream.h" //------------------------------------------------------------------------ // #define LOG_CAIRO // To limit memory usage and improve performance when printing, limit // 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) #else # define LOG(x) #endif #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MAX(a, b) (((a) > (b)) ? (a) : (b)) //------------------------------------------------------------------------ // CairoImage //------------------------------------------------------------------------ CairoImage::CairoImage(double x1A, double y1A, double x2A, double y2A) { image = nullptr; x1 = x1A; y1 = y1A; x2 = x2A; y2 = y2A; } CairoImage::~CairoImage() { if (image) { cairo_surface_destroy(image); } } void CairoImage::setImage(cairo_surface_t *i) { if (image) { cairo_surface_destroy(image); } image = cairo_surface_reference(i); } //------------------------------------------------------------------------ // CairoOutputDev //------------------------------------------------------------------------ // We cannot tie the lifetime of an FT_Library object to that of // CairoOutputDev, since any FT_Faces created with it may end up with a // reference by Cairo which can be held long after the CairoOutputDev is // deleted. The simplest way to avoid problems is to never tear down the // FT_Library instance; to avoid leaks, just use a single global instance // initialized the first time it is needed. FT_Library CairoOutputDev::ft_lib; std::once_flag CairoOutputDev::ft_lib_once_flag; CairoOutputDev::CairoOutputDev() { doc = nullptr; std::call_once(ft_lib_once_flag, FT_Init_FreeType, &ft_lib); fontEngine = nullptr; fontEngine_owner = false; glyphs = nullptr; fill_pattern = nullptr; fill_color = {}; stroke_pattern = nullptr; stroke_color = {}; stroke_opacity = 1.0; fill_opacity = 1.0; textClipPath = nullptr; 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; t3_render_state = Type3RenderNone; t3_glyph_has_bbox = false; t3_glyph_has_color = false; text_matrix_valid = true; groupColorSpaceStack = nullptr; group = nullptr; mask = nullptr; shape = nullptr; cairo_shape = nullptr; knockoutCount = 0; textPage = nullptr; actualText = nullptr; logicalStruct = false; pdfPageNum = 0; cairoPageNum = 0; // the SA parameter supposedly defaults to false, but Acrobat // apparently hardwires it to true stroke_adjust = true; align_stroke_coords = false; adjusted_stroke_width = false; xref = nullptr; currentStructParents = -1; } CairoOutputDev::~CairoOutputDev() { if (fontEngine_owner && fontEngine) { delete fontEngine; } if (textClipPath) { cairo_path_destroy(textClipPath); textClipPath = nullptr; } if (cairo) { cairo_destroy(cairo); } cairo_pattern_destroy(stroke_pattern); cairo_pattern_destroy(fill_pattern); if (group) { cairo_pattern_destroy(group); } if (mask) { cairo_pattern_destroy(mask); } if (shape) { cairo_pattern_destroy(shape); } if (textPage) { textPage->decRefCnt(); } if (actualText) { delete actualText; } } 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)); } cairo_destroy(cairo); assert(!cairo_shape); } if (c != nullptr) { cairo = cairo_reference(c); /* save the initial matrix so that we can use it for type3 fonts. */ // XXX: is this sufficient? could we miss changes to the matrix somehow? cairo_get_matrix(cairo, &orig_matrix); } else { cairo = nullptr; cairo_shape = nullptr; } } 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; } if (text) { textPage = text; textPage->incRefCnt(); actualText = new ActualText(text); } else { textPage = nullptr; actualText = nullptr; } } void CairoOutputDev::copyAntialias(cairo_t *cr, cairo_t *source_cr) { cairo_set_antialias(cr, cairo_get_antialias(source_cr)); cairo_font_options_t *font_options = cairo_font_options_create(); cairo_get_font_options(source_cr, font_options); cairo_set_font_options(cr, font_options); cairo_font_options_destroy(font_options); } void CairoOutputDev::startDoc(PDFDoc *docA, CairoFontEngine *parentFontEngine) { doc = docA; if (parentFontEngine) { fontEngine = parentFontEngine; } else { if (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); fill_pattern = cairo_pattern_create_rgb(0., 0., 0.); fill_color = { 0, 0, 0 }; stroke_pattern = cairo_pattern_reference(fill_pattern); stroke_color = { 0, 0, 0 }; if (textPage) { textPage->startPage(state); } 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(); } } void CairoOutputDev::endPage() { if (textPage) { textPage->endPage(); textPage->coalesce(true, 0, false); } } 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.count(name.toStr()) == 0) { 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.count(name.toStr()) == 0) { 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.count(std::pair(structParents, mcid)) > 0) { 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.count(element) == 0) { 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 * source set on the provided cairo_t must be used, except in the * case of a color user font explicitly setting a color. * * As startPage() resets the source to solid black, this function * is used instead to initialise the CairoOutputDev when rendering * a user font glyph. * * As noted in the Cairo documentation, the default source of a * render callback contains an internal marker denoting the * foreground color is to be used when the glyph is rendered, even * though querying the default source will reveal solid black. * For this reason, fill_color and stroke_color are set to nullopt * to ensure updateFillColor()/updateStrokeColor() will update the * color even if the new color is black. * * The saveState()/restoreState() functions also ensure the * default source is saved and restored, and the fill_color and * stroke_color is reset to nullopt for the same reason. */ /* Initialise fill and stroke pattern to the current source pattern */ fill_pattern = cairo_pattern_reference(cairo_get_source(cairo)); stroke_pattern = cairo_pattern_reference(cairo_get_source(cairo)); fill_color = {}; stroke_color = {}; t3_glyph_has_bbox = false; t3_glyph_has_color = false; if (xrefA != nullptr) { xref = xrefA; } } void CairoOutputDev::saveState(GfxState *state) { LOG(printf("save\n")); cairo_save(cairo); if (cairo_shape) { cairo_save(cairo_shape); } /* To ensure the current source, potentially containing the hidden * foreground color maker, is saved and restored as required by * _render_type3_glyph, we avoid using the update color and * opacity functions in restoreState() and instead be careful to * save all the color related variables that have been set by the * update functions on the stack. */ SaveStateElement elem; elem.fill_pattern = cairo_pattern_reference(fill_pattern); elem.fill_opacity = fill_opacity; elem.stroke_pattern = cairo_pattern_reference(stroke_pattern); elem.stroke_opacity = stroke_opacity; elem.mask = mask ? cairo_pattern_reference(mask) : nullptr; elem.mask_matrix = mask_matrix; elem.fontRef = currentFont ? currentFont->getRef() : Ref::INVALID(); saveStateStack.push_back(elem); if (strokePathClip) { strokePathClip->ref_count++; } } void CairoOutputDev::restoreState(GfxState *state) { LOG(printf("restore\n")); cairo_restore(cairo); if (cairo_shape) { cairo_restore(cairo_shape); } text_matrix_valid = true; cairo_pattern_destroy(fill_pattern); fill_pattern = saveStateStack.back().fill_pattern; fill_color = {}; fill_opacity = saveStateStack.back().fill_opacity; cairo_pattern_destroy(stroke_pattern); stroke_pattern = saveStateStack.back().stroke_pattern; stroke_color = {}; stroke_opacity = saveStateStack.back().stroke_opacity; if (saveStateStack.back().fontRef != (currentFont ? currentFont->getRef() : Ref::INVALID())) { needFontUpdate = true; } /* This isn't restored by cairo_restore() since we keep it in the * output device. */ updateBlendMode(state); if (mask) { cairo_pattern_destroy(mask); } mask = saveStateStack.back().mask; mask_matrix = saveStateStack.back().mask_matrix; saveStateStack.pop_back(); if (strokePathClip && --strokePathClip->ref_count == 0) { delete strokePathClip->path; if (strokePathClip->dashes) { gfree(strokePathClip->dashes); } gfree(strokePathClip); strokePathClip = nullptr; } } void CairoOutputDev::updateAll(GfxState *state) { updateLineDash(state); updateLineJoin(state); updateLineCap(state); updateLineWidth(state); updateFlatness(state); updateMiterLimit(state); updateFillColor(state); updateStrokeColor(state); updateFillOpacity(state); updateStrokeOpacity(state); updateBlendMode(state); needFontUpdate = true; if (textPage) { textPage->updateFont(state); } } void CairoOutputDev::setDefaultCTM(const double *ctm) { cairo_matrix_t matrix; matrix.xx = ctm[0]; matrix.yx = ctm[1]; matrix.xy = ctm[2]; matrix.yy = ctm[3]; matrix.x0 = ctm[4]; matrix.y0 = ctm[5]; cairo_transform(cairo, &matrix); if (cairo_shape) { cairo_transform(cairo_shape, &matrix); } OutputDev::setDefaultCTM(ctm); } void CairoOutputDev::updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32) { cairo_matrix_t matrix, invert_matrix; matrix.xx = m11; matrix.yx = m12; matrix.xy = m21; matrix.yy = m22; matrix.x0 = m31; matrix.y0 = m32; /* Make sure the matrix is invertible before setting it. * cairo will blow up if we give it a matrix that's not * invertible, so we need to check before passing it * to cairo_transform. Ignoring it is likely to give better * results than not rendering anything at all. See #14398 * * Ideally, we could do the cairo_transform * and then check if anything went wrong and fix it then * instead of having to invert the matrix. */ invert_matrix = matrix; if (cairo_matrix_invert(&invert_matrix)) { error(errSyntaxWarning, -1, "matrix not invertible\n"); return; } cairo_transform(cairo, &matrix); if (cairo_shape) { cairo_transform(cairo_shape, &matrix); } updateLineDash(state); updateLineJoin(state); updateLineCap(state); updateLineWidth(state); } void CairoOutputDev::updateLineDash(GfxState *state) { double dashStart; const std::vector &dashPattern = state->getLineDash(&dashStart); cairo_set_dash(cairo, dashPattern.data(), dashPattern.size(), dashStart); if (cairo_shape) { cairo_set_dash(cairo_shape, dashPattern.data(), dashPattern.size(), dashStart); } } void CairoOutputDev::updateFlatness(GfxState *state) { // cairo_set_tolerance (cairo, state->getFlatness()); } void CairoOutputDev::updateLineJoin(GfxState *state) { switch (state->getLineJoin()) { case 0: cairo_set_line_join(cairo, CAIRO_LINE_JOIN_MITER); break; case 1: cairo_set_line_join(cairo, CAIRO_LINE_JOIN_ROUND); break; case 2: cairo_set_line_join(cairo, CAIRO_LINE_JOIN_BEVEL); break; } if (cairo_shape) { cairo_set_line_join(cairo_shape, cairo_get_line_join(cairo)); } } void CairoOutputDev::updateLineCap(GfxState *state) { switch (state->getLineCap()) { case 0: cairo_set_line_cap(cairo, CAIRO_LINE_CAP_BUTT); break; case 1: cairo_set_line_cap(cairo, CAIRO_LINE_CAP_ROUND); break; case 2: cairo_set_line_cap(cairo, CAIRO_LINE_CAP_SQUARE); break; } if (cairo_shape) { cairo_set_line_cap(cairo_shape, cairo_get_line_cap(cairo)); } } void CairoOutputDev::updateMiterLimit(GfxState *state) { cairo_set_miter_limit(cairo, state->getMiterLimit()); if (cairo_shape) { cairo_set_miter_limit(cairo_shape, state->getMiterLimit()); } } void CairoOutputDev::updateLineWidth(GfxState *state) { LOG(printf("line width: %f\n", state->getLineWidth())); adjusted_stroke_width = false; double width = state->getLineWidth(); if (stroke_adjust && !printing) { double x, y; x = y = width; /* find out line width in device units */ cairo_user_to_device_distance(cairo, &x, &y); if (fabs(x) <= 1.0 && fabs(y) <= 1.0) { /* adjust width to at least one device pixel */ x = y = 1.0; cairo_device_to_user_distance(cairo, &x, &y); width = MIN(fabs(x), fabs(y)); adjusted_stroke_width = true; } } else if (width == 0.0) { /* Cairo does not support 0 line width == 1 device pixel. Find out * how big pixels (device unit) are in the x and y * directions. Choose the smaller of the two as our line width. */ double x = 1.0, y = 1.0; if (printing) { // assume printer pixel size is 1/600 inch x = 72.0 / 600; y = 72.0 / 600; } cairo_device_to_user_distance(cairo, &x, &y); width = MIN(fabs(x), fabs(y)); } cairo_set_line_width(cairo, width); if (cairo_shape) { cairo_set_line_width(cairo_shape, cairo_get_line_width(cairo)); } } void CairoOutputDev::updateFillColor(GfxState *state) { if (inUncoloredPattern) { return; } GfxRGB new_color; state->getFillRGB(&new_color); bool color_match = fill_color && *fill_color == new_color; if (cairo_pattern_get_type(fill_pattern) != CAIRO_PATTERN_TYPE_SOLID || !color_match) { cairo_pattern_destroy(fill_pattern); fill_pattern = cairo_pattern_create_rgba(colToDbl(new_color.r), colToDbl(new_color.g), colToDbl(new_color.b), fill_opacity); fill_color = new_color; LOG(printf("fill color: %d %d %d\n", fill_color->r, fill_color->g, fill_color->b)); } } void CairoOutputDev::updateStrokeColor(GfxState *state) { if (inUncoloredPattern) { return; } GfxRGB new_color; state->getStrokeRGB(&new_color); bool color_match = stroke_color && *stroke_color == new_color; if (cairo_pattern_get_type(fill_pattern) != CAIRO_PATTERN_TYPE_SOLID || !color_match) { cairo_pattern_destroy(stroke_pattern); stroke_pattern = cairo_pattern_create_rgba(colToDbl(new_color.r), colToDbl(new_color.g), colToDbl(new_color.b), stroke_opacity); stroke_color = new_color; LOG(printf("stroke color: %d %d %d\n", stroke_color->r, stroke_color->g, stroke_color->b)); } } void CairoOutputDev::updateFillOpacity(GfxState *state) { double opacity = fill_opacity; if (inUncoloredPattern) { return; } fill_opacity = state->getFillOpacity(); if (opacity != fill_opacity) { if (!fill_color) { GfxRGB color; state->getFillRGB(&color); fill_color = color; } cairo_pattern_destroy(fill_pattern); fill_pattern = cairo_pattern_create_rgba(colToDbl(fill_color->r), colToDbl(fill_color->g), colToDbl(fill_color->b), fill_opacity); LOG(printf("fill opacity: %f\n", fill_opacity)); } } void CairoOutputDev::updateStrokeOpacity(GfxState *state) { double opacity = stroke_opacity; if (inUncoloredPattern) { return; } stroke_opacity = state->getStrokeOpacity(); if (opacity != stroke_opacity) { if (!stroke_color) { GfxRGB color; state->getStrokeRGB(&color); stroke_color = color; } cairo_pattern_destroy(stroke_pattern); stroke_pattern = cairo_pattern_create_rgba(colToDbl(stroke_color->r), colToDbl(stroke_color->g), colToDbl(stroke_color->b), stroke_opacity); LOG(printf("stroke opacity: %f\n", stroke_opacity)); } } void CairoOutputDev::updateFillColorStop(GfxState *state, double offset) { if (inUncoloredPattern) { return; } GfxRGB color; state->getFillRGB(&color); // If stroke pattern is set then the current fill is clipped // to a stroke path. In that case, the stroke opacity has to be used // rather than the fill opacity. // See https://gitlab.freedesktop.org/poppler/poppler/issues/178 auto opacity = (state->getStrokePattern()) ? state->getStrokeOpacity() : state->getFillOpacity(); cairo_pattern_add_color_stop_rgba(fill_pattern, offset, colToDbl(color.r), colToDbl(color.g), colToDbl(color.b), opacity); LOG(printf("fill color stop: %f (%d, %d, %d, %d)\n", offset, color.r, color.g, color.b, dblToCol(opacity))); } void CairoOutputDev::updateBlendMode(GfxState *state) { switch (state->getBlendMode()) { default: case gfxBlendNormal: cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); break; case gfxBlendMultiply: cairo_set_operator(cairo, CAIRO_OPERATOR_MULTIPLY); break; case gfxBlendScreen: cairo_set_operator(cairo, CAIRO_OPERATOR_SCREEN); break; case gfxBlendOverlay: cairo_set_operator(cairo, CAIRO_OPERATOR_OVERLAY); break; case gfxBlendDarken: cairo_set_operator(cairo, CAIRO_OPERATOR_DARKEN); break; case gfxBlendLighten: cairo_set_operator(cairo, CAIRO_OPERATOR_LIGHTEN); break; case gfxBlendColorDodge: cairo_set_operator(cairo, CAIRO_OPERATOR_COLOR_DODGE); break; case gfxBlendColorBurn: cairo_set_operator(cairo, CAIRO_OPERATOR_COLOR_BURN); break; case gfxBlendHardLight: cairo_set_operator(cairo, CAIRO_OPERATOR_HARD_LIGHT); break; case gfxBlendSoftLight: cairo_set_operator(cairo, CAIRO_OPERATOR_SOFT_LIGHT); break; case gfxBlendDifference: cairo_set_operator(cairo, CAIRO_OPERATOR_DIFFERENCE); break; case gfxBlendExclusion: cairo_set_operator(cairo, CAIRO_OPERATOR_EXCLUSION); break; case gfxBlendHue: cairo_set_operator(cairo, CAIRO_OPERATOR_HSL_HUE); break; case gfxBlendSaturation: cairo_set_operator(cairo, CAIRO_OPERATOR_HSL_SATURATION); break; case gfxBlendColor: cairo_set_operator(cairo, CAIRO_OPERATOR_HSL_COLOR); break; case gfxBlendLuminosity: cairo_set_operator(cairo, CAIRO_OPERATOR_HSL_LUMINOSITY); break; } LOG(printf("blend mode: %d\n", (int)state->getBlendMode())); } void CairoOutputDev::updateFont(GfxState *state) { cairo_font_face_t *font_face; cairo_matrix_t matrix, invert_matrix; LOG(printf("updateFont() font=%s\n", state->getFont()->getName()->c_str())); needFontUpdate = false; // FIXME: use cairo font engine? if (textPage) { textPage->updateFont(state); } currentFont = fontEngine->getFont(state->getFont(), doc, printing, xref); if (!currentFont) { return; } font_face = currentFont->getFontFace(); cairo_set_font_face(cairo, font_face); use_show_text_glyphs = state->getFont()->hasToUnicodeCMap() && cairo_surface_has_show_text_glyphs(cairo_get_target(cairo)); double fontSize = state->getFontSize(); const double *m = state->getTextMat(); /* NOTE: adjusting by a constant is hack. The correct solution * is probably to use user-fonts and compute the scale on a per * glyph basis instead of for the entire font */ double w = currentFont->getSubstitutionCorrection(state->getFont()); matrix.xx = m[0] * fontSize * state->getHorizScaling() * w; matrix.yx = m[1] * fontSize * state->getHorizScaling() * w; matrix.xy = -m[2] * fontSize; matrix.yy = -m[3] * fontSize; matrix.x0 = 0; matrix.y0 = 0; LOG(printf("font matrix: %f %f %f %f\n", matrix.xx, matrix.yx, matrix.xy, matrix.yy)); /* Make sure the font matrix is invertible before setting it. cairo * will blow up if we give it a matrix that's not invertible, so we * need to check before passing it to cairo_set_font_matrix. Ignoring it * is likely to give better results than not rendering anything at * all. See #18254. */ invert_matrix = matrix; if (cairo_matrix_invert(&invert_matrix)) { error(errSyntaxWarning, -1, "font matrix not invertible"); text_matrix_valid = false; return; } cairo_set_font_matrix(cairo, &matrix); text_matrix_valid = true; } /* Tolerance in pixels for checking if strokes are horizontal or vertical * lines in device space */ #define STROKE_COORD_TOLERANCE 0.5 /* Align stroke coordinate i if the point is the start or end of a * horizontal or vertical line */ void CairoOutputDev::alignStrokeCoords(const GfxSubpath *subpath, int i, double *x, double *y) { double x1, y1, x2, y2; bool align = false; x1 = subpath->getX(i); y1 = subpath->getY(i); cairo_user_to_device(cairo, &x1, &y1); // Does the current coord and prev coord form a horiz or vert line? if (i > 0 && !subpath->getCurve(i - 1)) { x2 = subpath->getX(i - 1); y2 = subpath->getY(i - 1); cairo_user_to_device(cairo, &x2, &y2); if (fabs(x2 - x1) < STROKE_COORD_TOLERANCE || fabs(y2 - y1) < STROKE_COORD_TOLERANCE) { align = true; } } // Does the current coord and next coord form a horiz or vert line? if (i < subpath->getNumPoints() - 1 && !subpath->getCurve(i + 1)) { x2 = subpath->getX(i + 1); y2 = subpath->getY(i + 1); cairo_user_to_device(cairo, &x2, &y2); if (fabs(x2 - x1) < STROKE_COORD_TOLERANCE || fabs(y2 - y1) < STROKE_COORD_TOLERANCE) { align = true; } } *x = subpath->getX(i); *y = subpath->getY(i); if (align) { /* see http://www.cairographics.org/FAQ/#sharp_lines */ cairo_user_to_device(cairo, x, y); *x = floor(*x) + 0.5; *y = floor(*y) + 0.5; cairo_device_to_user(cairo, x, y); } } #undef STROKE_COORD_TOLERANCE void CairoOutputDev::doPath(cairo_t *c, GfxState *state, const GfxPath *path) { int i, j; double x, y; cairo_new_path(c); for (i = 0; i < path->getNumSubpaths(); ++i) { const GfxSubpath *subpath = path->getSubpath(i); if (subpath->getNumPoints() > 0) { if (align_stroke_coords) { alignStrokeCoords(subpath, 0, &x, &y); } else { x = subpath->getX(0); y = subpath->getY(0); } cairo_move_to(c, x, y); j = 1; while (j < subpath->getNumPoints()) { if (subpath->getCurve(j)) { if (align_stroke_coords) { alignStrokeCoords(subpath, j + 2, &x, &y); } else { x = subpath->getX(j + 2); y = subpath->getY(j + 2); } cairo_curve_to(c, subpath->getX(j), subpath->getY(j), subpath->getX(j + 1), subpath->getY(j + 1), x, y); j += 3; } else { if (align_stroke_coords) { alignStrokeCoords(subpath, j, &x, &y); } else { x = subpath->getX(j); y = subpath->getY(j); } cairo_line_to(c, x, y); ++j; } } if (subpath->isClosed()) { LOG(printf("close\n")); cairo_close_path(c); } } } } void CairoOutputDev::stroke(GfxState *state) { if (t3_render_state == Type3RenderMask) { GfxGray gray; state->getFillGray(&gray); if (colToDbl(gray) > 0.5) { return; } } if (adjusted_stroke_width) { align_stroke_coords = true; } doPath(cairo, state, state->getPath()); align_stroke_coords = false; cairo_set_source(cairo, stroke_pattern); LOG(printf("stroke\n")); if (strokePathClip) { cairo_push_group(cairo); cairo_stroke(cairo); cairo_pop_group_to_source(cairo); fillToStrokePathClip(state); } else { cairo_stroke(cairo); } if (cairo_shape) { doPath(cairo_shape, state, state->getPath()); cairo_stroke(cairo_shape); } } void CairoOutputDev::fill(GfxState *state) { if (t3_render_state == Type3RenderMask) { GfxGray gray; state->getFillGray(&gray); if (colToDbl(gray) > 0.5) { return; } } doPath(cairo, state, state->getPath()); cairo_set_fill_rule(cairo, CAIRO_FILL_RULE_WINDING); cairo_set_source(cairo, fill_pattern); LOG(printf("fill\n")); // XXX: how do we get the path if (mask) { cairo_save(cairo); cairo_clip(cairo); if (strokePathClip) { cairo_push_group(cairo); fillToStrokePathClip(state); cairo_pop_group_to_source(cairo); } cairo_set_matrix(cairo, &mask_matrix); cairo_mask(cairo, mask); cairo_restore(cairo); } else if (strokePathClip) { fillToStrokePathClip(state); } else { cairo_fill(cairo); } if (cairo_shape) { cairo_set_fill_rule(cairo_shape, CAIRO_FILL_RULE_WINDING); doPath(cairo_shape, state, state->getPath()); cairo_fill(cairo_shape); } } void CairoOutputDev::eoFill(GfxState *state) { doPath(cairo, state, state->getPath()); cairo_set_fill_rule(cairo, CAIRO_FILL_RULE_EVEN_ODD); cairo_set_source(cairo, fill_pattern); LOG(printf("fill-eo\n")); if (mask) { cairo_save(cairo); cairo_clip(cairo); cairo_set_matrix(cairo, &mask_matrix); cairo_mask(cairo, mask); cairo_restore(cairo); } else { cairo_fill(cairo); } if (cairo_shape) { cairo_set_fill_rule(cairo_shape, CAIRO_FILL_RULE_EVEN_ODD); doPath(cairo_shape, state, state->getPath()); cairo_fill(cairo_shape); } } bool CairoOutputDev::tilingPatternFill(GfxState *state, Gfx *gfxA, Catalog *cat, GfxTilingPattern *tPat, const double *mat, int x0, int y0, int x1, int y1, double xStep, double yStep) { PDFRectangle box; Gfx *gfx; cairo_pattern_t *pattern; cairo_surface_t *surface; cairo_matrix_t matrix; cairo_matrix_t pattern_matrix; cairo_t *old_cairo; double xMin, yMin, xMax, yMax; double width, height; double scaleX, scaleY; int surface_width, surface_height; StrokePathClip *strokePathTmp; bool adjusted_stroke_width_tmp; cairo_pattern_t *maskTmp; const double *bbox = tPat->getBBox(); const double *pmat = tPat->getMatrix(); const int paintType = tPat->getPaintType(); Dict *resDict = tPat->getResDict(); Object *str = tPat->getContentStream(); width = bbox[2] - bbox[0]; height = bbox[3] - bbox[1]; if (xStep != width || yStep != height) { return false; } /* TODO: implement the other cases here too */ // Find the width and height of the transformed pattern cairo_get_matrix(cairo, &matrix); cairo_matrix_init(&pattern_matrix, mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); cairo_matrix_multiply(&matrix, &matrix, &pattern_matrix); double widthX = width, widthY = 0; cairo_matrix_transform_distance(&matrix, &widthX, &widthY); surface_width = ceil(sqrt(widthX * widthX + widthY * widthY)); double heightX = 0, heightY = height; cairo_matrix_transform_distance(&matrix, &heightX, &heightY); surface_height = ceil(sqrt(heightX * heightX + heightY * heightY)); scaleX = surface_width / width; scaleY = surface_height / height; surface = cairo_surface_create_similar(cairo_get_target(cairo), CAIRO_CONTENT_COLOR_ALPHA, surface_width, surface_height); if (cairo_surface_status(surface)) { return false; } old_cairo = cairo; cairo = cairo_create(surface); cairo_surface_destroy(surface); copyAntialias(cairo, old_cairo); box.x1 = bbox[0]; box.y1 = bbox[1]; box.x2 = bbox[2]; box.y2 = bbox[3]; cairo_scale(cairo, scaleX, scaleY); cairo_translate(cairo, -box.x1, -box.y1); strokePathTmp = strokePathClip; strokePathClip = nullptr; adjusted_stroke_width_tmp = adjusted_stroke_width; maskTmp = mask; mask = nullptr; gfx = new Gfx(doc, this, resDict, &box, nullptr, nullptr, nullptr, gfxA); if (paintType == 2) { inUncoloredPattern = true; } gfx->display(str); if (paintType == 2) { inUncoloredPattern = false; } delete gfx; strokePathClip = strokePathTmp; adjusted_stroke_width = adjusted_stroke_width_tmp; mask = maskTmp; pattern = cairo_pattern_create_for_surface(cairo_get_target(cairo)); cairo_destroy(cairo); cairo = old_cairo; if (cairo_pattern_status(pattern)) { return false; } // Cairo can fail if the pattern translation is too large. Fix by making the // translation smaller. const double det = pmat[0] * pmat[3] - pmat[1] * pmat[2]; // Find the number of repetitions of pattern we need to shift by. Transform // the translation component of pmat (pmat[4] and pmat[5]) into the pattern's // coordinate system by multiplying by inverse of pmat, then divide by // pattern size (xStep and yStep). const double xoffset = round((pmat[3] * pmat[4] - pmat[2] * pmat[5]) / (xStep * det)); const double yoffset = -round((pmat[1] * pmat[4] - pmat[0] * pmat[5]) / (yStep * det)); if (!std::isfinite(xoffset) || !std::isfinite(yoffset)) { error(errSyntaxWarning, -1, "CairoOutputDev: Singular matrix in tilingPatternFill"); return false; } // Shift pattern_matrix by multiples of the pattern size. pattern_matrix.x0 -= xoffset * pattern_matrix.xx * xStep + yoffset * pattern_matrix.xy * yStep; pattern_matrix.y0 -= xoffset * pattern_matrix.yx * xStep + yoffset * pattern_matrix.yy * yStep; state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); cairo_rectangle(cairo, xMin, yMin, xMax - xMin, yMax - yMin); cairo_matrix_init_scale(&matrix, scaleX, scaleY); cairo_matrix_translate(&matrix, -box.x1, -box.y1); cairo_pattern_set_matrix(pattern, &matrix); cairo_transform(cairo, &pattern_matrix); cairo_set_source(cairo, pattern); cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); if (strokePathClip) { fillToStrokePathClip(state); } else { cairo_fill(cairo); } cairo_pattern_destroy(pattern); 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 // following size in device space. Note when printing this size is // in points. const int subdivide_pixels = 10; double x_begin, x_end, x1, x2; double y_begin, y_end, y1, y2; double x_step; double y_step; GfxColor color; GfxRGB rgb; cairo_matrix_t mat; const double *matrix = shading->getMatrix(); mat.xx = matrix[0]; mat.yx = matrix[1]; mat.xy = matrix[2]; mat.yy = matrix[3]; mat.x0 = matrix[4]; mat.y0 = matrix[5]; if (cairo_matrix_invert(&mat)) { error(errSyntaxWarning, -1, "matrix not invertible\n"); return false; } // get cell size in pattern space x_step = y_step = subdivide_pixels; cairo_matrix_transform_distance(&mat, &x_step, &y_step); cairo_pattern_destroy(fill_pattern); fill_pattern = cairo_pattern_create_mesh(); cairo_pattern_set_matrix(fill_pattern, &mat); shading->getDomain(&x_begin, &y_begin, &x_end, &y_end); for (x1 = x_begin; x1 < x_end; x1 += x_step) { x2 = x1 + x_step; if (x2 > x_end) { x2 = x_end; } for (y1 = y_begin; y1 < y_end; y1 += y_step) { y2 = y1 + y_step; if (y2 > y_end) { y2 = y_end; } cairo_mesh_pattern_begin_patch(fill_pattern); cairo_mesh_pattern_move_to(fill_pattern, x1, y1); cairo_mesh_pattern_line_to(fill_pattern, x2, y1); cairo_mesh_pattern_line_to(fill_pattern, x2, y2); cairo_mesh_pattern_line_to(fill_pattern, x1, y2); shading->getColor(x1, y1, &color); shading->getColorSpace()->getRGB(&color, &rgb); cairo_mesh_pattern_set_corner_color_rgb(fill_pattern, 0, colToDbl(rgb.r), colToDbl(rgb.g), colToDbl(rgb.b)); shading->getColor(x2, y1, &color); shading->getColorSpace()->getRGB(&color, &rgb); cairo_mesh_pattern_set_corner_color_rgb(fill_pattern, 1, colToDbl(rgb.r), colToDbl(rgb.g), colToDbl(rgb.b)); shading->getColor(x2, y2, &color); shading->getColorSpace()->getRGB(&color, &rgb); cairo_mesh_pattern_set_corner_color_rgb(fill_pattern, 2, colToDbl(rgb.r), colToDbl(rgb.g), colToDbl(rgb.b)); shading->getColor(x1, y2, &color); shading->getColorSpace()->getRGB(&color, &rgb); cairo_mesh_pattern_set_corner_color_rgb(fill_pattern, 3, colToDbl(rgb.r), colToDbl(rgb.g), colToDbl(rgb.b)); cairo_mesh_pattern_end_patch(fill_pattern); } } double xMin, yMin, xMax, yMax; // get the clip region bbox state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); state->moveTo(xMin, yMin); state->lineTo(xMin, yMax); state->lineTo(xMax, yMax); state->lineTo(xMax, yMin); state->closePath(); fill(state); state->clearPath(); return true; } #endif /* CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0) */ bool CairoOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) { double x0, y0, x1, y1; double dx, dy; shading->getCoords(&x0, &y0, &x1, &y1); dx = x1 - x0; dy = y1 - y0; cairo_pattern_destroy(fill_pattern); fill_pattern = cairo_pattern_create_linear(x0 + tMin * dx, y0 + tMin * dy, x0 + tMax * dx, y0 + tMax * dy); if (!shading->getExtend0() && !shading->getExtend1()) { cairo_pattern_set_extend(fill_pattern, CAIRO_EXTEND_NONE); } else { cairo_pattern_set_extend(fill_pattern, CAIRO_EXTEND_PAD); } LOG(printf("axial-sh\n")); // TODO: use the actual stops in the shading in the case // of linear interpolation (Type 2 Exponential functions with N=1) return false; } bool CairoOutputDev::axialShadedSupportExtend(GfxState *state, GfxAxialShading *shading) { return (shading->getExtend0() == shading->getExtend1()); } bool CairoOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading, double sMin, double sMax) { double x0, y0, r0, x1, y1, r1; double dx, dy, dr; cairo_matrix_t matrix; double scale; shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1); dx = x1 - x0; dy = y1 - y0; dr = r1 - r0; // Cairo/pixman do not work well with a very large or small scaled // matrix. See cairo bug #81657. // // As a workaround, scale the pattern by the average of the vertical // and horizontal scaling of the current transformation matrix. cairo_get_matrix(cairo, &matrix); scale = (sqrt(matrix.xx * matrix.xx + matrix.yx * matrix.yx) + sqrt(matrix.xy * matrix.xy + matrix.yy * matrix.yy)) / 2; cairo_matrix_init_scale(&matrix, scale, scale); cairo_pattern_destroy(fill_pattern); fill_pattern = cairo_pattern_create_radial((x0 + sMin * dx) * scale, (y0 + sMin * dy) * scale, (r0 + sMin * dr) * scale, (x0 + sMax * dx) * scale, (y0 + sMax * dy) * scale, (r0 + sMax * dr) * scale); cairo_pattern_set_matrix(fill_pattern, &matrix); if (shading->getExtend0() && shading->getExtend1()) { cairo_pattern_set_extend(fill_pattern, CAIRO_EXTEND_PAD); } else { cairo_pattern_set_extend(fill_pattern, CAIRO_EXTEND_NONE); } LOG(printf("radial-sh\n")); return false; } bool CairoOutputDev::radialShadedSupportExtend(GfxState *state, GfxRadialShading *shading) { 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; GfxColor color[3]; int i, j; GfxRGB rgb; cairo_pattern_destroy(fill_pattern); fill_pattern = cairo_pattern_create_mesh(); for (i = 0; i < shading->getNTriangles(); i++) { if (shading->isParameterized()) { double color0, color1, color2; shading->getTriangle(i, &x0, &y0, &color0, &x1, &y1, &color1, &x2, &y2, &color2); shading->getParameterizedColor(color0, &color[0]); shading->getParameterizedColor(color1, &color[1]); shading->getParameterizedColor(color2, &color[2]); } else { shading->getTriangle(i, &x0, &y0, &color[0], &x1, &y1, &color[1], &x2, &y2, &color[2]); } cairo_mesh_pattern_begin_patch(fill_pattern); cairo_mesh_pattern_move_to(fill_pattern, x0, y0); cairo_mesh_pattern_line_to(fill_pattern, x1, y1); cairo_mesh_pattern_line_to(fill_pattern, x2, y2); for (j = 0; j < 3; j++) { shading->getColorSpace()->getRGB(&color[j], &rgb); cairo_mesh_pattern_set_corner_color_rgb(fill_pattern, j, colToDbl(rgb.r), colToDbl(rgb.g), colToDbl(rgb.b)); } cairo_mesh_pattern_end_patch(fill_pattern); } double xMin, yMin, xMax, yMax; // get the clip region bbox state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); state->moveTo(xMin, yMin); state->lineTo(xMin, yMax); state->lineTo(xMax, yMax); state->lineTo(xMax, yMin); state->closePath(); fill(state); state->clearPath(); return true; } bool CairoOutputDev::patchMeshShadedFill(GfxState *state, GfxPatchMeshShading *shading) { int i, j, k; cairo_pattern_destroy(fill_pattern); fill_pattern = cairo_pattern_create_mesh(); for (i = 0; i < shading->getNPatches(); i++) { const GfxPatch *patch = shading->getPatch(i); GfxColor color; GfxRGB rgb; cairo_mesh_pattern_begin_patch(fill_pattern); cairo_mesh_pattern_move_to(fill_pattern, patch->x[0][0], patch->y[0][0]); cairo_mesh_pattern_curve_to(fill_pattern, patch->x[0][1], patch->y[0][1], patch->x[0][2], patch->y[0][2], patch->x[0][3], patch->y[0][3]); cairo_mesh_pattern_curve_to(fill_pattern, patch->x[1][3], patch->y[1][3], patch->x[2][3], patch->y[2][3], patch->x[3][3], patch->y[3][3]); cairo_mesh_pattern_curve_to(fill_pattern, patch->x[3][2], patch->y[3][2], patch->x[3][1], patch->y[3][1], patch->x[3][0], patch->y[3][0]); cairo_mesh_pattern_curve_to(fill_pattern, patch->x[2][0], patch->y[2][0], patch->x[1][0], patch->y[1][0], patch->x[0][0], patch->y[0][0]); cairo_mesh_pattern_set_control_point(fill_pattern, 0, patch->x[1][1], patch->y[1][1]); cairo_mesh_pattern_set_control_point(fill_pattern, 1, patch->x[1][2], patch->y[1][2]); cairo_mesh_pattern_set_control_point(fill_pattern, 2, patch->x[2][2], patch->y[2][2]); cairo_mesh_pattern_set_control_point(fill_pattern, 3, patch->x[2][1], patch->y[2][1]); for (j = 0; j < 4; j++) { int u, v; switch (j) { case 0: u = 0; v = 0; break; case 1: u = 0; v = 1; break; case 2: u = 1; v = 1; break; case 3: u = 1; v = 0; break; } if (shading->isParameterized()) { shading->getParameterizedColor(patch->color[u][v].c[0], &color); } else { for (k = 0; k < shading->getColorSpace()->getNComps(); k++) { // simply cast to the desired type; that's all what is needed. color.c[k] = GfxColorComp(patch->color[u][v].c[k]); } } shading->getColorSpace()->getRGB(&color, &rgb); cairo_mesh_pattern_set_corner_color_rgb(fill_pattern, j, colToDbl(rgb.r), colToDbl(rgb.g), colToDbl(rgb.b)); } cairo_mesh_pattern_end_patch(fill_pattern); } double xMin, yMin, xMax, yMax; // get the clip region bbox state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); state->moveTo(xMin, yMin); state->lineTo(xMin, yMax); state->lineTo(xMax, yMax); state->lineTo(xMax, yMin); state->closePath(); fill(state); state->clearPath(); return true; } #endif /* CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0) */ void CairoOutputDev::clip(GfxState *state) { doPath(cairo, state, state->getPath()); cairo_set_fill_rule(cairo, CAIRO_FILL_RULE_WINDING); cairo_clip(cairo); LOG(printf("clip\n")); if (cairo_shape) { doPath(cairo_shape, state, state->getPath()); cairo_set_fill_rule(cairo_shape, CAIRO_FILL_RULE_WINDING); cairo_clip(cairo_shape); } } void CairoOutputDev::eoClip(GfxState *state) { doPath(cairo, state, state->getPath()); cairo_set_fill_rule(cairo, CAIRO_FILL_RULE_EVEN_ODD); cairo_clip(cairo); LOG(printf("clip-eo\n")); if (cairo_shape) { doPath(cairo_shape, state, state->getPath()); cairo_set_fill_rule(cairo_shape, CAIRO_FILL_RULE_EVEN_ODD); cairo_clip(cairo_shape); } } void CairoOutputDev::clipToStrokePath(GfxState *state) { LOG(printf("clip-to-stroke-path\n")); strokePathClip = (StrokePathClip *)gmalloc(sizeof(*strokePathClip)); strokePathClip->path = state->getPath()->copy(); cairo_get_matrix(cairo, &strokePathClip->ctm); strokePathClip->line_width = cairo_get_line_width(cairo); strokePathClip->dash_count = cairo_get_dash_count(cairo); if (strokePathClip->dash_count) { strokePathClip->dashes = (double *)gmallocn(sizeof(double), strokePathClip->dash_count); cairo_get_dash(cairo, strokePathClip->dashes, &strokePathClip->dash_offset); } else { strokePathClip->dashes = nullptr; } strokePathClip->cap = cairo_get_line_cap(cairo); strokePathClip->join = cairo_get_line_join(cairo); strokePathClip->miter = cairo_get_miter_limit(cairo); strokePathClip->ref_count = 1; } void CairoOutputDev::fillToStrokePathClip(GfxState *state) { cairo_save(cairo); cairo_set_matrix(cairo, &strokePathClip->ctm); cairo_set_line_width(cairo, strokePathClip->line_width); cairo_set_dash(cairo, strokePathClip->dashes, strokePathClip->dash_count, strokePathClip->dash_offset); cairo_set_line_cap(cairo, strokePathClip->cap); cairo_set_line_join(cairo, strokePathClip->join); cairo_set_miter_limit(cairo, strokePathClip->miter); doPath(cairo, state, strokePathClip->path); cairo_stroke(cairo); cairo_restore(cairo); } void CairoOutputDev::beginString(GfxState *state, const GooString *s) { int len = s->getLength(); if (needFontUpdate) { updateFont(state); } if (!currentFont) { return; } glyphs = (cairo_glyph_t *)gmallocn(len, sizeof(cairo_glyph_t)); glyphCount = 0; if (use_show_text_glyphs) { clusters = (cairo_text_cluster_t *)gmallocn(len, sizeof(cairo_text_cluster_t)); clusterCount = 0; utf8Max = len * 2; // start with twice the number of glyphs. we will realloc if we need more. utf8 = (char *)gmalloc(utf8Max); utf8Count = 0; } } 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) { if (currentFont) { glyphs[glyphCount].index = currentFont->getGlyph(code, u, uLen); 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) { // utf8 encoded characters can be up to 6 bytes if (utf8Max > uLen * 6) { utf8Max *= 2; } else { utf8Max += 2 * uLen * 6; } utf8 = (char *)grealloc(utf8, utf8Max); } clusters[clusterCount].num_bytes = 0; for (int i = 0; i < uLen; i++) { int size = utf8Map->mapUnicode(u[i], utf8 + utf8Count, utf8Max - utf8Count); utf8Count += size; clusters[clusterCount].num_bytes += size; } clusters[clusterCount].num_glyphs = 1; clusterCount++; } } if (!textPage) { return; } actualText->addChar(state, x, y, dx, dy, code, nBytes, u, uLen); } void CairoOutputDev::endString(GfxState *state) { int render; if (!currentFont) { return; } // endString can be called without a corresponding beginString. If this // happens glyphs will be null so don't draw anything, just return. // XXX: OutputDevs should probably not have to deal with this... if (!glyphs) { return; } // ignore empty strings and invisible text -- this is used by // Acrobat Capture render = state->getRender(); if (render == 3 || glyphCount == 0 || !text_matrix_valid) { 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); if (use_show_text_glyphs) { cairo_show_text_glyphs(cairo, utf8, utf8Count, glyphs, glyphCount, clusters, clusterCount, (cairo_text_cluster_flags_t)0); } else { cairo_show_glyphs(cairo, glyphs, glyphCount); } if (cairo_shape) { cairo_show_glyphs(cairo_shape, glyphs, glyphCount); } } // stroke if ((render & 3) == 1 || (render & 3) == 2) { LOG(printf("stroke string\n")); cairo_set_source(cairo, stroke_pattern); cairo_glyph_path(cairo, glyphs, glyphCount); cairo_stroke(cairo); if (cairo_shape) { cairo_glyph_path(cairo_shape, glyphs, glyphCount); cairo_stroke(cairo_shape); } } // clip if ((render & 4)) { LOG(printf("clip string\n")); // append the glyph path to textClipPath. // set textClipPath as the currentPath if (textClipPath) { cairo_append_path(cairo, textClipPath); if (cairo_shape) { cairo_append_path(cairo_shape, textClipPath); } cairo_path_destroy(textClipPath); } // append the glyph path cairo_glyph_path(cairo, glyphs, glyphCount); // move the path back into textClipPath // and clear the current path textClipPath = cairo_copy_path(cairo); cairo_new_path(cairo); if (cairo_shape) { cairo_new_path(cairo_shape); } } finish: gfree(glyphs); glyphs = nullptr; if (use_show_text_glyphs) { gfree(clusters); clusters = nullptr; gfree(utf8); utf8 = nullptr; } } bool CairoOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, const Unicode *u, int uLen) { cairo_save(cairo); cairo_matrix_t matrix; const double *ctm = state->getCTM(); matrix.xx = ctm[0]; matrix.yx = ctm[1]; matrix.xy = ctm[2]; matrix.yy = ctm[3]; matrix.x0 = ctm[4]; matrix.y0 = ctm[5]; /* Restore the original matrix and then transform to matrix needed for the * type3 font. This is ugly but seems to work. Perhaps there is a better way to do it?*/ cairo_set_matrix(cairo, &orig_matrix); cairo_transform(cairo, &matrix); if (cairo_shape) { cairo_save(cairo_shape); cairo_set_matrix(cairo_shape, &orig_matrix); cairo_transform(cairo_shape, &matrix); } cairo_pattern_destroy(stroke_pattern); cairo_pattern_reference(fill_pattern); stroke_pattern = fill_pattern; return false; } void CairoOutputDev::endType3Char(GfxState *state) { cairo_restore(cairo); if (cairo_shape) { cairo_restore(cairo_shape); } } void CairoOutputDev::type3D0(GfxState *state, double wx, double wy) { t3_glyph_wx = wx; t3_glyph_wy = wy; t3_glyph_has_color = true; } void CairoOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) { t3_glyph_wx = wx; t3_glyph_wy = wy; t3_glyph_bbox[0] = llx; t3_glyph_bbox[1] = lly; t3_glyph_bbox[2] = urx; t3_glyph_bbox[3] = ury; t3_glyph_has_bbox = true; t3_glyph_has_color = false; } void CairoOutputDev::beginTextObject(GfxState *state) { } void CairoOutputDev::endTextObject(GfxState *state) { if (textClipPath) { // clip the accumulated text path cairo_append_path(cairo, textClipPath); cairo_clip(cairo); if (cairo_shape) { cairo_append_path(cairo_shape, textClipPath); cairo_clip(cairo_shape); } cairo_path_destroy(textClipPath); textClipPath = nullptr; } } void CairoOutputDev::beginActualText(GfxState *state, const GooString *text) { if (textPage) { actualText->begin(state, text); } } void CairoOutputDev::endActualText(GfxState *state) { if (textPage) { actualText->end(state); } } static inline int splashRound(SplashCoord x) { return (int)floor(x + 0.5); } static inline int splashCeil(SplashCoord x) { return (int)ceil(x); } static inline int splashFloor(SplashCoord x) { return (int)floor(x); } static cairo_surface_t *cairo_surface_create_similar_clip(cairo_t *cairo, cairo_content_t content) { cairo_pattern_t *pattern; cairo_surface_t *surface = nullptr; cairo_push_group_with_content(cairo, content); pattern = cairo_pop_group(cairo); cairo_pattern_get_surface(pattern, &surface); cairo_surface_reference(surface); cairo_pattern_destroy(pattern); return surface; } void CairoOutputDev::beginTransparencyGroup(GfxState * /*state*/, const double * /*bbox*/, GfxColorSpace *blendingColorSpace, bool /*isolated*/, bool knockout, bool forSoftMask) { /* push color space */ ColorSpaceStack *css = new ColorSpaceStack; css->cs = blendingColorSpace; css->knockout = knockout; cairo_get_matrix(cairo, &css->group_matrix); css->next = groupColorSpaceStack; groupColorSpaceStack = css; LOG(printf("begin transparency group. knockout: %s\n", knockout ? "yes" : "no")); if (knockout) { knockoutCount++; if (!cairo_shape) { /* create a surface for tracking the shape */ cairo_surface_t *cairo_shape_surface = cairo_surface_create_similar_clip(cairo, CAIRO_CONTENT_ALPHA); cairo_shape = cairo_create(cairo_shape_surface); cairo_surface_destroy(cairo_shape_surface); copyAntialias(cairo_shape, cairo); /* the color doesn't matter as long as it is opaque */ cairo_set_source_rgb(cairo_shape, 0, 0, 0); cairo_matrix_t matrix; cairo_get_matrix(cairo, &matrix); cairo_set_matrix(cairo_shape, &matrix); } } if (groupColorSpaceStack->next && groupColorSpaceStack->next->knockout) { /* we need to track the shape */ cairo_push_group(cairo_shape); } if (false && forSoftMask) { cairo_push_group_with_content(cairo, CAIRO_CONTENT_ALPHA); } else { cairo_push_group(cairo); } /* push_group has an implicit cairo_save() */ if (knockout) { /*XXX: let's hope this matches the semantics needed */ cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); } else { cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); } } void CairoOutputDev::endTransparencyGroup(GfxState * /*state*/) { if (group) { cairo_pattern_destroy(group); } group = cairo_pop_group(cairo); LOG(printf("end transparency group\n")); if (groupColorSpaceStack->next && groupColorSpaceStack->next->knockout) { if (shape) { cairo_pattern_destroy(shape); } shape = cairo_pop_group(cairo_shape); } } void CairoOutputDev::paintTransparencyGroup(GfxState * /*state*/, const double * /*bbox*/) { LOG(printf("paint transparency group\n")); cairo_save(cairo); cairo_set_matrix(cairo, &groupColorSpaceStack->group_matrix); if (shape) { /* OPERATOR_SOURCE w/ a mask is defined as (src IN mask) ADD (dest OUT mask) * however our source has already been clipped to mask so we only need to * do ADD and OUT */ /* clear the shape mask */ cairo_set_source(cairo, shape); cairo_set_operator(cairo, CAIRO_OPERATOR_DEST_OUT); cairo_paint(cairo); cairo_set_operator(cairo, CAIRO_OPERATOR_ADD); } cairo_set_source(cairo, group); if (!mask) { cairo_paint_with_alpha(cairo, fill_opacity); cairo_status_t status = cairo_status(cairo); if (status) { printf("BAD status: %s\n", cairo_status_to_string(status)); } } else { if (fill_opacity < 1.0) { cairo_push_group(cairo); } cairo_save(cairo); cairo_set_matrix(cairo, &mask_matrix); cairo_mask(cairo, mask); cairo_restore(cairo); if (fill_opacity < 1.0) { cairo_pop_group_to_source(cairo); cairo_paint_with_alpha(cairo, fill_opacity); } cairo_pattern_destroy(mask); mask = nullptr; } if (shape) { if (cairo_shape) { cairo_set_source(cairo_shape, shape); cairo_paint(cairo_shape); cairo_set_source_rgb(cairo_shape, 0, 0, 0); } cairo_pattern_destroy(shape); shape = nullptr; } popTransparencyGroup(); cairo_restore(cairo); } static int luminocity(uint32_t x) { int r = (x >> 16) & 0xff; int g = (x >> 8) & 0xff; int b = (x >> 0) & 0xff; // an arbitrary integer approximation of .3*r + .59*g + .11*b int y = (r * 19661 + g * 38666 + b * 7209 + 32829) >> 16; return y; } /* XXX: do we need to deal with shape here? */ void CairoOutputDev::setSoftMask(GfxState *state, const double *bbox, bool alpha, Function *transferFunc, GfxColor *backdropColor) { cairo_pattern_destroy(mask); LOG(printf("set softMask\n")); if (!alpha || transferFunc) { /* We need to mask according to the luminocity of the group. * So we paint the group to an image surface convert it to a luminocity map * and then use that as the mask. */ /* Get clip extents in device space */ double x1, y1, x2, y2, x_min, y_min, x_max, y_max; cairo_clip_extents(cairo, &x1, &y1, &x2, &y2); cairo_user_to_device(cairo, &x1, &y1); cairo_user_to_device(cairo, &x2, &y2); x_min = MIN(x1, x2); y_min = MIN(y1, y2); x_max = MAX(x1, x2); y_max = MAX(y1, y2); cairo_clip_extents(cairo, &x1, &y1, &x2, &y2); cairo_user_to_device(cairo, &x1, &y2); cairo_user_to_device(cairo, &x2, &y1); x_min = MIN(x_min, MIN(x1, x2)); y_min = MIN(y_min, MIN(y1, y2)); x_max = MAX(x_max, MAX(x1, x2)); y_max = MAX(y_max, MAX(y1, y2)); int width = (int)(ceil(x_max) - floor(x_min)); int height = (int)(ceil(y_max) - floor(y_min)); /* Get group device offset */ double x_offset, y_offset; if (cairo_get_group_target(cairo) == cairo_get_target(cairo)) { cairo_surface_get_device_offset(cairo_get_group_target(cairo), &x_offset, &y_offset); } else { cairo_surface_t *pats; cairo_pattern_get_surface(group, &pats); cairo_surface_get_device_offset(pats, &x_offset, &y_offset); } /* Adjust extents by group offset */ x_min += x_offset; y_min += y_offset; cairo_surface_t *source = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); cairo_t *maskCtx = cairo_create(source); copyAntialias(maskCtx, cairo); // XXX: hopefully this uses the correct color space */ if (!alpha && groupColorSpaceStack->cs) { GfxRGB backdropColorRGB; groupColorSpaceStack->cs->getRGB(backdropColor, &backdropColorRGB); /* paint the backdrop */ cairo_set_source_rgb(maskCtx, colToDbl(backdropColorRGB.r), colToDbl(backdropColorRGB.g), colToDbl(backdropColorRGB.b)); } cairo_paint(maskCtx); /* Copy source ctm to mask ctm and translate origin so that the * mask appears it the same location on the source surface. */ cairo_matrix_t mat, tmat; cairo_matrix_init_translate(&tmat, -x_min, -y_min); cairo_get_matrix(cairo, &mat); cairo_matrix_multiply(&mat, &mat, &tmat); cairo_set_matrix(maskCtx, &mat); /* make the device offset of the new mask match that of the group */ cairo_surface_set_device_offset(source, x_offset, y_offset); /* paint the group */ cairo_set_source(maskCtx, group); cairo_paint(maskCtx); /* XXX status = cairo_status(maskCtx); */ cairo_destroy(maskCtx); /* convert to a luminocity map */ uint32_t *source_data = reinterpret_cast(cairo_image_surface_get_data(source)); 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; } } cairo_surface_mark_dirty(source); } /* setup the new mask pattern */ mask = cairo_pattern_create_for_surface(source); cairo_get_matrix(cairo, &mask_matrix); if (cairo_get_group_target(cairo) == cairo_get_target(cairo)) { cairo_pattern_set_matrix(mask, &mat); } else { cairo_matrix_t patMatrix; cairo_pattern_get_matrix(group, &patMatrix); /* Apply x_min, y_min offset to it appears in the same location as source. */ cairo_matrix_multiply(&patMatrix, &patMatrix, &tmat); cairo_pattern_set_matrix(mask, &patMatrix); } cairo_surface_destroy(source); } else if (alpha) { mask = cairo_pattern_reference(group); cairo_get_matrix(cairo, &mask_matrix); } popTransparencyGroup(); } void CairoOutputDev::popTransparencyGroup() { /* pop color space */ ColorSpaceStack *css = groupColorSpaceStack; if (css->knockout) { knockoutCount--; if (!knockoutCount) { /* we don't need to track the shape anymore because * we are not above any knockout groups */ cairo_destroy(cairo_shape); cairo_shape = nullptr; } } groupColorSpaceStack = css->next; delete css; } void CairoOutputDev::clearSoftMask(GfxState * /*state*/) { if (mask) { cairo_pattern_destroy(mask); } mask = nullptr; } /* Taken from cairo/doc/tutorial/src/singular.c */ static void get_singular_values(const cairo_matrix_t *matrix, double *major, double *minor) { double xx = matrix->xx, xy = matrix->xy; double yx = matrix->yx, yy = matrix->yy; double a = xx * xx + yx * yx; double b = xy * xy + yy * yy; double k = xx * xy + yx * yy; double f = (a + b) * .5; double g = (a - b) * .5; double delta = sqrt(g * g + k * k); if (major) { *major = sqrt(f + delta); } if (minor) { *minor = sqrt(f - delta); } } void CairoOutputDev::getScaledSize(const cairo_matrix_t *matrix, int orig_width, int orig_height, int *scaledWidth, int *scaledHeight) { double xScale; double yScale; if (orig_width > orig_height) { get_singular_values(matrix, &xScale, &yScale); } else { get_singular_values(matrix, &yScale, &xScale); } int tx, tx2, ty, ty2; /* the integer co-ordinates of the resulting image */ 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; } } cairo_filter_t CairoOutputDev::getFilterForSurface(cairo_surface_t *image, bool interpolate) { if (interpolate) { return CAIRO_FILTER_GOOD; } int orig_width = cairo_image_surface_get_width(image); int orig_height = cairo_image_surface_get_height(image); if (orig_width == 0 || orig_height == 0) { return CAIRO_FILTER_NEAREST; } /* When printing, don't change the interpolation. */ if (printing) { return CAIRO_FILTER_NEAREST; } cairo_matrix_t matrix; cairo_get_matrix(cairo, &matrix); int scaled_width, scaled_height; getScaledSize(&matrix, orig_width, orig_height, &scaled_width, &scaled_height); /* When scale factor is >= 400% we don't interpolate. See bugs #25268, #9860 */ if (scaled_width / orig_width >= 4 || scaled_height / orig_height >= 4) { return CAIRO_FILTER_NEAREST; } return CAIRO_FILTER_GOOD; } void CairoOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg) { /* FIXME: Doesn't the image mask support any colorspace? */ cairo_set_source(cairo, fill_pattern); /* 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; invert_bit = invert ? 1 : 0; if (pix ^ invert_bit) { return; } cairo_save(cairo); cairo_rectangle(cairo, 0., 0., width, height); cairo_fill(cairo); cairo_restore(cairo); if (cairo_shape) { cairo_save(cairo_shape); cairo_rectangle(cairo_shape, 0., 0., width, height); cairo_fill(cairo_shape); cairo_restore(cairo_shape); } return; } /* shape is 1.0 for painted areas, 0.0 for unpainted ones */ cairo_matrix_t matrix; 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); } } void CairoOutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool inlineImg, double *baseMatrix) { /* FIXME: Doesn't the image mask support any colorspace? */ cairo_set_source(cairo, fill_pattern); /* 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; invert_bit = invert ? 1 : 0; if (!(pix ^ invert_bit)) { cairo_save(cairo); cairo_rectangle(cairo, 0., 0., width, height); cairo_fill(cairo); cairo_restore(cairo); if (cairo_shape) { cairo_save(cairo_shape); cairo_rectangle(cairo_shape, 0., 0., width, height); cairo_fill(cairo_shape); cairo_restore(cairo_shape); } } } else { cairo_push_group_with_content(cairo, CAIRO_CONTENT_ALPHA); /* shape is 1.0 for painted areas, 0.0 for unpainted ones */ cairo_matrix_t matrix; 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); } if (state->getFillColorSpace()->getMode() == csPattern) { cairo_set_source_rgb(cairo, 1, 1, 1); cairo_set_matrix(cairo, &mask_matrix); cairo_mask(cairo, mask); } if (mask) { cairo_pattern_destroy(mask); } mask = cairo_pop_group(cairo); } saveState(state); double bbox[4] = { 0, 0, 1, 1 }; // dummy beginTransparencyGroup(state, bbox, state->getFillColorSpace(), true, false, false); } void CairoOutputDev::unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix) { double bbox[4] = { 0, 0, 1, 1 }; // dummy endTransparencyGroup(state); restoreState(state); paintTransparencyGroup(state, bbox); clearSoftMask(state); } void CairoOutputDev::drawImageMaskRegular(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg) { unsigned char *buffer; unsigned char *dest; 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; ptrdiff_t row_stride; cairo_filter_t filter; /* TODO: Do we want to cache these? */ imgStr = new ImageStream(str, width, 1, 1); imgStr->reset(); image = cairo_image_surface_create(CAIRO_FORMAT_A1, width, height); if (cairo_surface_status(image)) { goto cleanup; } buffer = cairo_image_surface_get_data(image); row_stride = cairo_image_surface_get_stride(image); invert_bit = invert ? 1 : 0; for (y = 0; y < height; y++) { pix = imgStr->getLine(); dest = buffer + y * row_stride; i = 0; bit = 0; for (x = 0; x < width; x++) { if (bit == 0) { dest[i] = 0; } if (!(pix[x] ^ invert_bit)) { #ifdef WORDS_BIGENDIAN dest[i] |= (1 << (7 - bit)); #else dest[i] |= (1 << bit); #endif } bit++; if (bit > 7) { bit = 0; i++; } } } filter = getFilterForSurface(image, interpolate); cairo_surface_mark_dirty(image); pattern = cairo_pattern_create_for_surface(image); cairo_surface_destroy(image); if (cairo_pattern_status(pattern)) { goto cleanup; } LOG(printf("drawImageMask %dx%d\n", width, height)); cairo_pattern_set_filter(pattern, filter); cairo_matrix_init_translate(&matrix, 0, height); cairo_matrix_scale(&matrix, width, -height); cairo_pattern_set_matrix(pattern, &matrix); if (cairo_pattern_status(pattern)) { cairo_pattern_destroy(pattern); goto cleanup; } if (state->getFillColorSpace()->getMode() == csPattern) { mask = cairo_pattern_reference(pattern); cairo_get_matrix(cairo, &mask_matrix); } else if (!printing) { cairo_save(cairo); cairo_rectangle(cairo, 0., 0., 1., 1.); cairo_clip(cairo); if (strokePathClip) { cairo_push_group(cairo); fillToStrokePathClip(state); cairo_pop_group_to_source(cairo); } cairo_mask(cairo, pattern); cairo_restore(cairo); } else { cairo_mask(cairo, pattern); } if (cairo_shape) { cairo_save(cairo_shape); cairo_set_source(cairo_shape, pattern); if (!printing) { cairo_rectangle(cairo_shape, 0., 0., 1., 1.); cairo_fill(cairo_shape); } else { cairo_mask(cairo_shape, pattern); } cairo_restore(cairo_shape); } 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; } 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; unsigned int *dest; cairo_surface_t *maskImage, *image; cairo_pattern_t *maskPattern, *pattern; cairo_matrix_t matrix; cairo_matrix_t maskMatrix; unsigned char *pix; int x, y; int invert_bit; cairo_filter_t filter; cairo_filter_t maskFilter; maskImgStr = new ImageStream(maskStr, maskWidth, 1, 1); maskImgStr->reset(); maskImage = cairo_image_surface_create(CAIRO_FORMAT_A8, maskWidth, maskHeight); if (cairo_surface_status(maskImage)) { maskImgStr->close(); delete maskImgStr; return; } maskBuffer = cairo_image_surface_get_data(maskImage); row_stride = cairo_image_surface_get_stride(maskImage); invert_bit = maskInvert ? 1 : 0; for (y = 0; y < maskHeight; y++) { pix = maskImgStr->getLine(); maskDest = maskBuffer + y * row_stride; for (x = 0; x < maskWidth; x++) { if (pix[x] ^ invert_bit) { *maskDest++ = 0; } else { *maskDest++ = 255; } } } maskImgStr->close(); delete maskImgStr; maskFilter = getFilterForSurface(maskImage, maskInterpolate); cairo_surface_mark_dirty(maskImage); maskPattern = cairo_pattern_create_for_surface(maskImage); cairo_surface_destroy(maskImage); if (cairo_pattern_status(maskPattern)) { return; } #if 0 /* ICCBased color space doesn't do any color correction * so check its underlying color space as well */ int is_identity_transform; is_identity_transform = colorMap->getColorSpace()->getMode() == csDeviceRGB || (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(); image = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height); if (cairo_surface_status(image)) { goto cleanup; } buffer = cairo_image_surface_get_data(image); 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); } filter = getFilterForSurface(image, interpolate); cairo_surface_mark_dirty(image); pattern = cairo_pattern_create_for_surface(image); cairo_surface_destroy(image); if (cairo_pattern_status(pattern)) { goto cleanup; } LOG(printf("drawMaskedImage %dx%d\n", width, height)); cairo_pattern_set_filter(pattern, filter); cairo_pattern_set_filter(maskPattern, maskFilter); if (!printing) { cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD); cairo_pattern_set_extend(maskPattern, CAIRO_EXTEND_PAD); } cairo_matrix_init_translate(&matrix, 0, height); cairo_matrix_scale(&matrix, width, -height); cairo_pattern_set_matrix(pattern, &matrix); if (cairo_pattern_status(pattern)) { cairo_pattern_destroy(pattern); cairo_pattern_destroy(maskPattern); goto cleanup; } cairo_matrix_init_translate(&maskMatrix, 0, maskHeight); cairo_matrix_scale(&maskMatrix, maskWidth, -maskHeight); cairo_pattern_set_matrix(maskPattern, &maskMatrix); if (cairo_pattern_status(maskPattern)) { cairo_pattern_destroy(maskPattern); cairo_pattern_destroy(pattern); goto cleanup; } if (!printing) { cairo_save(cairo); cairo_set_source(cairo, pattern); cairo_rectangle(cairo, 0., 0., 1., 1.); cairo_clip(cairo); cairo_mask(cairo, maskPattern); cairo_restore(cairo); } else { cairo_set_source(cairo, pattern); cairo_mask(cairo, maskPattern); } if (cairo_shape) { cairo_save(cairo_shape); cairo_set_source(cairo_shape, pattern); if (!printing) { cairo_rectangle(cairo_shape, 0., 0., 1., 1.); cairo_fill(cairo_shape); } else { cairo_mask(cairo_shape, pattern); } cairo_restore(cairo_shape); } cairo_pattern_destroy(maskPattern); cairo_pattern_destroy(pattern); cleanup: imgStr->close(); delete imgStr; } 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, mask_row_stride; unsigned char *maskBuffer, *buffer; unsigned char *maskDest; unsigned int *dest; cairo_surface_t *maskImage, *image; cairo_pattern_t *maskPattern, *pattern; cairo_matrix_t maskMatrix, matrix; unsigned char *pix; int y; cairo_filter_t filter; cairo_filter_t maskFilter; GfxRGB matteColorRgb; const GfxColor *matteColor = maskColorMap->getMatteColor(); if (matteColor != nullptr) { getMatteColorRgb(colorMap, matteColor, &matteColorRgb); } maskImgStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits()); maskImgStr->reset(); maskImage = cairo_image_surface_create(CAIRO_FORMAT_A8, maskWidth, maskHeight); if (cairo_surface_status(maskImage)) { maskImgStr->close(); delete maskImgStr; return; } maskBuffer = cairo_image_surface_get_data(maskImage); mask_row_stride = cairo_image_surface_get_stride(maskImage); for (y = 0; y < maskHeight; y++) { maskDest = (unsigned char *)(maskBuffer + y * mask_row_stride); pix = maskImgStr->getLine(); if (likely(pix != nullptr)) { maskColorMap->getGrayLine(pix, maskDest, maskWidth); } } maskImgStr->close(); delete maskImgStr; maskFilter = getFilterForSurface(maskImage, maskInterpolate); cairo_surface_mark_dirty(maskImage); maskPattern = cairo_pattern_create_for_surface(maskImage); cairo_surface_destroy(maskImage); if (cairo_pattern_status(maskPattern)) { return; } #if 0 /* ICCBased color space doesn't do any color correction * so check its underlying color space as well */ int is_identity_transform; is_identity_transform = colorMap->getColorSpace()->getMode() == csDeviceRGB || (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(); image = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height); if (cairo_surface_status(image)) { goto cleanup; } buffer = cairo_image_surface_get_data(image); row_stride = cairo_image_surface_get_stride(image); for (y = 0; y < height; y++) { dest = reinterpret_cast(buffer + y * row_stride); 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); if (matteColor == nullptr) { setMimeData(state, str, ref, colorMap, image, height); } pattern = cairo_pattern_create_for_surface(image); cairo_surface_destroy(image); if (cairo_pattern_status(pattern)) { goto cleanup; } LOG(printf("drawSoftMaskedImage %dx%d\n", width, height)); cairo_pattern_set_filter(pattern, filter); cairo_pattern_set_filter(maskPattern, maskFilter); if (!printing) { cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD); cairo_pattern_set_extend(maskPattern, CAIRO_EXTEND_PAD); } cairo_matrix_init_translate(&matrix, 0, height); cairo_matrix_scale(&matrix, width, -height); cairo_pattern_set_matrix(pattern, &matrix); if (cairo_pattern_status(pattern)) { cairo_pattern_destroy(pattern); cairo_pattern_destroy(maskPattern); goto cleanup; } cairo_matrix_init_translate(&maskMatrix, 0, maskHeight); cairo_matrix_scale(&maskMatrix, maskWidth, -maskHeight); cairo_pattern_set_matrix(maskPattern, &maskMatrix); if (cairo_pattern_status(maskPattern)) { cairo_pattern_destroy(maskPattern); cairo_pattern_destroy(pattern); goto cleanup; } if (fill_opacity != 1.0) { cairo_push_group(cairo); } else { cairo_save(cairo); } cairo_set_source(cairo, pattern); if (!printing) { cairo_rectangle(cairo, 0., 0., 1., 1.); cairo_clip(cairo); } cairo_mask(cairo, maskPattern); if (fill_opacity != 1.0) { cairo_pop_group_to_source(cairo); cairo_save(cairo); if (!printing) { cairo_rectangle(cairo, 0., 0., 1., 1.); cairo_clip(cairo); } cairo_paint_with_alpha(cairo, fill_opacity); } cairo_restore(cairo); if (cairo_shape) { cairo_save(cairo_shape); cairo_set_source(cairo_shape, pattern); if (!printing) { cairo_rectangle(cairo_shape, 0., 0., 1., 1.); cairo_fill(cairo_shape); } else { cairo_mask(cairo_shape, pattern); } cairo_restore(cairo_shape); } cairo_pattern_destroy(maskPattern); cairo_pattern_destroy(pattern); cleanup: imgStr->close(); delete imgStr; } bool CairoOutputDev::getStreamData(Stream *str, char **buffer, int *length) { int len, i; char *strBuffer; len = 0; str->close(); str->reset(); while (str->getChar() != EOF) { len++; } if (len == 0) { return false; } strBuffer = (char *)gmalloc(len); str->close(); str->reset(); for (i = 0; i < len; ++i) { strBuffer[i] = str->getChar(); } *buffer = strBuffer; *length = len; return true; } static bool colorMapHasIdentityDecodeMap(GfxImageColorMap *colorMap) { for (int i = 0; i < colorMap->getNumPixelComps(); i++) { if (colorMap->getDecodeLow(i) != 0.0 || colorMap->getDecodeHigh(i) != 1.0) { return false; } } 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; char *idBuffer; cairo_status_t status; mime_id = new GooString; if (mime_id_prefix) { mime_id->append(mime_id_prefix); } 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; 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); Object *globalsStr = jb2Str->getGlobalsStream(); char *globalsBuffer; int globalsLength; // nothing to do for JBIG2 stream without Globals if (!globalsStr->isStream()) { return true; } if (setMimeIdFromRef(image, CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID, nullptr, jb2Str->getGlobalsStreamRef())) { return false; } if (!getStreamData(globalsStr->getStream(), &globalsBuffer, &globalsLength)) { return false; } if (cairo_surface_set_mime_data(image, CAIRO_MIME_TYPE_JBIG2_GLOBAL, (const unsigned char *)globalsBuffer, globalsLength, gfree, (void *)globalsBuffer)) { gfree(globalsBuffer); return false; } 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); GooString params; params.appendf("Columns={0:d}", ccittStr->getColumns()); params.appendf(" Rows={0:d}", height); params.appendf(" K={0:d}", ccittStr->getEncoding()); params.appendf(" EndOfLine={0:d}", ccittStr->getEndOfLine() ? 1 : 0); params.appendf(" EncodedByteAlign={0:d}", ccittStr->getEncodedByteAlign() ? 1 : 0); params.appendf(" EndOfBlock={0:d}", ccittStr->getEndOfBlock() ? 1 : 0); params.appendf(" BlackIs1={0:d}", ccittStr->getBlackIs1() ? 1 : 0); params.appendf(" DamagedRowsBeforeError={0:d}", ccittStr->getDamagedRowsBeforeError()); char *p = strdup(params.c_str()); if (cairo_surface_set_mime_data(image, CAIRO_MIME_TYPE_CCITT_FAX_PARAMS, (const unsigned char *)p, params.getLength(), gfree, (void *)p)) { gfree(p); return false; } 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; if (!printing) { 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 // 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) { if (ref && ref->isRef()) { status = setMimeIdFromRef(image, CAIRO_MIME_TYPE_UNIQUE_ID, "poppler-surface-", ref->getRef()); if (status) { return; } } } #endif switch (strKind) { case strDCT: mime_type = CAIRO_MIME_TYPE_JPEG; break; 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); // colorspace in stream dict may be different from colorspace in jpx // data if (strKind == strJPX && colorSpace) { return; } // 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 csCalRGB: case csDeviceCMYK: case csICCBased: break; case csLab: case csIndexed: case csSeparation: case csDeviceN: case csPattern: return; } } if (!colorMapHasIdentityDecodeMap(colorMap)) { 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)) { status = cairo_surface_set_mime_data(image, mime_type, (const unsigned char *)strBuffer, len, gfree, strBuffer); } if (status) { gfree(strBuffer); } } } class RescaleDrawImage : public CairoRescaleBox { private: ImageStream *imgStr; GfxRGB *lookup; int width; GfxImageColorMap *colorMap; const int *maskColors; int current_row; bool imageError; public: ~RescaleDrawImage() override; cairo_surface_t *getSourceImage(Stream *str, int widthA, int height, int scaledWidth, int scaledHeight, bool printing, GfxImageColorMap *colorMapA, const int *maskColorsA) { cairo_surface_t *image = nullptr; int i; lookup = nullptr; colorMap = colorMapA; maskColors = maskColorsA; width = widthA; current_row = -1; imageError = false; /* TODO: Do we want to cache these? */ imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgStr->reset(); #if 0 /* ICCBased color space doesn't do any color correction * so check its underlying color space as well */ int is_identity_transform; is_identity_transform = colorMap->getColorSpace()->getMode() == csDeviceRGB || (colorMap->getColorSpace()->getMode() == csICCBased && ((GfxICCBasedColorSpace*)colorMap->getColorSpace())->getAlt()->getMode() == csDeviceRGB); #endif // special case for one-channel (monochrome/gray/separation) images: // build a lookup table here if (colorMap->getNumPixelComps() == 1) { int n; unsigned char pix; n = 1 << colorMap->getBits(); lookup = (GfxRGB *)gmallocn(n, sizeof(GfxRGB)); for (i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getRGB(&pix, &lookup[i]); } } 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) { if (width > height) { scaledWidth = MAX_PRINT_IMAGE_SIZE; scaledHeight = MAX_PRINT_IMAGE_SIZE * (double)height / width; } else { scaledHeight = MAX_PRINT_IMAGE_SIZE; scaledWidth = MAX_PRINT_IMAGE_SIZE * (double)width / height; } needsCustomDownscaling = true; if (scaledWidth == 0) { scaledWidth = 1; } if (scaledHeight == 0) { scaledHeight = 1; } } } if (!needsCustomDownscaling || scaledWidth >= width || scaledHeight >= height) { // No downscaling. Create cairo image containing the source image data. unsigned char *buffer; ptrdiff_t stride; image = cairo_image_surface_create(maskColors ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24, width, height); if (cairo_surface_status(image)) { goto cleanup; } buffer = cairo_image_surface_get_data(image); stride = cairo_image_surface_get_stride(image); for (int y = 0; y < height; y++) { uint32_t *dest = reinterpret_cast(buffer + y * stride); getRow(y, dest); } } else { // 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 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); if (cairo_surface_status(image)) { goto cleanup; } downScaleImage(width, height, scaledWidth, scaledHeight, 0, 0, scaledWidth, scaledHeight, image); } cairo_surface_mark_dirty(image); cleanup: gfree(lookup); imgStr->close(); delete imgStr; return image; } void getRow(int row_num, uint32_t *row_data) override { unsigned char *pix; if (row_num <= current_row) { return; } while (current_row < row_num) { pix = imgStr->getLine(); current_row++; } if (unlikely(pix == nullptr)) { memset(row_data, 0, width * 4); if (!imageError) { error(errInternal, -1, "Bad image stream"); imageError = true; } } else if (lookup) { unsigned char *p = pix; GfxRGB rgb; for (int i = 0; i < width; i++) { rgb = lookup[*p]; row_data[i] = ((int)colToByte(rgb.r) << 16) | ((int)colToByte(rgb.g) << 8) | ((int)colToByte(rgb.b) << 0); p++; } } else { colorMap->getRGBLine(pix, row_data, width); } if (maskColors) { for (int x = 0; x < width; x++) { bool is_opaque = false; for (int i = 0; i < colorMap->getNumPixelComps(); ++i) { if (pix[i] < maskColors[2 * i] || pix[i] > maskColors[2 * i + 1]) { is_opaque = true; break; } } if (is_opaque) { *row_data |= 0xff000000; } else { *row_data = 0; } row_data++; pix += colorMap->getNumPixelComps(); } } } }; RescaleDrawImage::~RescaleDrawImage() = default; void CairoOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int widthA, int heightA, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) { cairo_surface_t *image; cairo_pattern_t *pattern, *maskPattern; cairo_matrix_t matrix; int width, height; int scaledWidth, scaledHeight; cairo_filter_t filter = CAIRO_FILTER_GOOD; RescaleDrawImage rescale; LOG(printf("drawImage %dx%d\n", widthA, heightA)); cairo_get_matrix(cairo, &matrix); getScaledSize(&matrix, widthA, heightA, &scaledWidth, &scaledHeight); image = rescale.getSourceImage(str, widthA, heightA, scaledWidth, scaledHeight, printing, colorMap, maskColors); if (!image) { return; } width = cairo_image_surface_get_width(image); height = cairo_image_surface_get_height(image); if (width == widthA && height == heightA) { filter = getFilterForSurface(image, interpolate); } 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); } } pattern = cairo_pattern_create_for_surface(image); cairo_surface_destroy(image); if (cairo_pattern_status(pattern)) { return; } cairo_pattern_set_filter(pattern, filter); if (!printing) { cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD); } cairo_matrix_init_translate(&matrix, 0, height); cairo_matrix_scale(&matrix, width, -height); cairo_pattern_set_matrix(pattern, &matrix); if (cairo_pattern_status(pattern)) { cairo_pattern_destroy(pattern); return; } if (!mask && fill_opacity != 1.0) { maskPattern = cairo_pattern_create_rgba(1., 1., 1., fill_opacity); } else if (mask) { maskPattern = cairo_pattern_reference(mask); } else { maskPattern = nullptr; } cairo_save(cairo); cairo_set_source(cairo, pattern); if (!printing) { cairo_rectangle(cairo, 0., 0., 1., 1.); } if (maskPattern) { if (!printing) { cairo_clip(cairo); } if (mask) { cairo_set_matrix(cairo, &mask_matrix); } cairo_mask(cairo, maskPattern); } else { if (printing) { cairo_paint(cairo); } else { cairo_fill(cairo); } } cairo_restore(cairo); cairo_pattern_destroy(maskPattern); if (cairo_shape) { cairo_save(cairo_shape); cairo_set_source(cairo_shape, pattern); if (printing) { cairo_paint(cairo_shape); } else { cairo_rectangle(cairo_shape, 0., 0., 1., 1.); cairo_fill(cairo_shape); } cairo_restore(cairo_shape); } 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.size() == 0) { return; } #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 18, 0) cairo_tag_end(cairo, markedContentStack.back().c_str()); #endif markedContentStack.pop_back(); } //------------------------------------------------------------------------ // ImageOutputDev //------------------------------------------------------------------------ CairoImageOutputDev::CairoImageOutputDev() { images = nullptr; numImages = 0; size = 0; imgDrawCbk = nullptr; imgDrawCbkData = nullptr; } CairoImageOutputDev::~CairoImageOutputDev() { int i; for (i = 0; i < numImages; i++) { delete images[i]; } gfree(images); } void CairoImageOutputDev::saveImage(CairoImage *image) { if (numImages >= size) { size += 16; images = (CairoImage **)greallocn(images, size, sizeof(CairoImage *)); } images[numImages++] = image; } void CairoImageOutputDev::getBBox(GfxState *state, int width, int height, double *x1, double *y1, double *x2, double *y2) { const double *ctm = state->getCTM(); cairo_matrix_t matrix; cairo_matrix_init(&matrix, ctm[0], ctm[1], -ctm[2], -ctm[3], ctm[2] + ctm[4], ctm[3] + ctm[5]); int scaledWidth, scaledHeight; getScaledSize(&matrix, width, height, &scaledWidth, &scaledHeight); if (matrix.xx >= 0) { *x1 = matrix.x0; } else { *x1 = matrix.x0 - scaledWidth; } *x2 = *x1 + scaledWidth; if (matrix.yy >= 0) { *y1 = matrix.y0; } else { *y1 = matrix.y0 - scaledHeight; } *y2 = *y1 + scaledHeight; } void CairoImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg) { cairo_t *cr; cairo_surface_t *surface; double x1, y1, x2, y2; CairoImage *image; getBBox(state, width, height, &x1, &y1, &x2, &y2); image = new CairoImage(x1, y1, x2, y2); saveImage(image); if (imgDrawCbk && imgDrawCbk(numImages - 1, imgDrawCbkData)) { surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); cr = cairo_create(surface); setCairo(cr); cairo_translate(cr, 0, height); cairo_scale(cr, width, -height); CairoOutputDev::drawImageMask(state, ref, str, width, height, invert, interpolate, inlineImg); image->setImage(surface); setCairo(nullptr); cairo_surface_destroy(surface); cairo_destroy(cr); } } void CairoImageOutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool inlineImg, double *baseMatrix) { cairo_t *cr; cairo_surface_t *surface; double x1, y1, x2, y2; CairoImage *image; getBBox(state, width, height, &x1, &y1, &x2, &y2); image = new CairoImage(x1, y1, x2, y2); saveImage(image); if (imgDrawCbk && imgDrawCbk(numImages - 1, imgDrawCbkData)) { surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); cr = cairo_create(surface); setCairo(cr); cairo_translate(cr, 0, height); cairo_scale(cr, width, -height); CairoOutputDev::drawImageMask(state, ref, str, width, height, invert, inlineImg, false); if (state->getFillColorSpace()->getMode() == csPattern) { cairo_mask(cairo, mask); } image->setImage(surface); setCairo(nullptr); cairo_surface_destroy(surface); cairo_destroy(cr); } } void CairoImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) { cairo_t *cr; cairo_surface_t *surface; double x1, y1, x2, y2; CairoImage *image; getBBox(state, width, height, &x1, &y1, &x2, &y2); image = new CairoImage(x1, y1, x2, y2); saveImage(image); if (imgDrawCbk && imgDrawCbk(numImages - 1, imgDrawCbkData)) { surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); cr = cairo_create(surface); setCairo(cr); cairo_translate(cr, 0, height); cairo_scale(cr, width, -height); CairoOutputDev::drawImage(state, ref, str, width, height, colorMap, interpolate, maskColors, inlineImg); image->setImage(surface); setCairo(nullptr); cairo_surface_destroy(surface); cairo_destroy(cr); } } void CairoImageOutputDev::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) { cairo_t *cr; cairo_surface_t *surface; double x1, y1, x2, y2; CairoImage *image; getBBox(state, width, height, &x1, &y1, &x2, &y2); image = new CairoImage(x1, y1, x2, y2); saveImage(image); if (imgDrawCbk && imgDrawCbk(numImages - 1, imgDrawCbkData)) { surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); cr = cairo_create(surface); setCairo(cr); cairo_translate(cr, 0, height); cairo_scale(cr, width, -height); CairoOutputDev::drawSoftMaskedImage(state, ref, str, width, height, colorMap, interpolate, maskStr, maskWidth, maskHeight, maskColorMap, maskInterpolate); image->setImage(surface); setCairo(nullptr); cairo_surface_destroy(surface); cairo_destroy(cr); } } void CairoImageOutputDev::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) { cairo_t *cr; cairo_surface_t *surface; double x1, y1, x2, y2; CairoImage *image; getBBox(state, width, height, &x1, &y1, &x2, &y2); image = new CairoImage(x1, y1, x2, y2); saveImage(image); if (imgDrawCbk && imgDrawCbk(numImages - 1, imgDrawCbkData)) { surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); cr = cairo_create(surface); setCairo(cr); cairo_translate(cr, 0, height); cairo_scale(cr, width, -height); CairoOutputDev::drawMaskedImage(state, ref, str, width, height, colorMap, interpolate, maskStr, maskWidth, maskHeight, maskInvert, maskInterpolate); image->setImage(surface); setCairo(nullptr); cairo_surface_destroy(surface); cairo_destroy(cr); } } poppler-24.02.0/poppler/CairoOutputDev.h000066400000000000000000000543511455701731300201250ustar00rootroot00000000000000//======================================================================== // // CairoOutputDev.h // // Copyright 2003 Glyph & Cog, LLC // Copyright 2004 Red Hat, INC // //======================================================================== //======================================================================== // // 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) 2005-2008 Jeff Muizelaar // 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, 2023 Adrian Johnson // Copyright (C) 2008 Michael Vrable // Copyright (C) 2010-2013 Thomas Freitag // Copyright (C) 2015 Suzuki Toshiya // Copyright (C) 2016 Jason Crain // Copyright (C) 2018, 2019, 2021 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 Michal // Copyright (C) 2021 Christian Persch // Copyright (C) 2022 Marek Kasik // // 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 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; class GfxPath; class Gfx8BitFont; struct GfxRGB; class CairoFontEngine; class CairoFont; //------------------------------------------------------------------------ //------------------------------------------------------------------------ // CairoImage //------------------------------------------------------------------------ class CairoImage { public: // Constructor. CairoImage(double x1, double y1, double x2, double y2); // Destructor. ~CairoImage(); CairoImage(const CairoImage &) = delete; CairoImage &operator=(const CairoImage &) = delete; // Set the image cairo surface void setImage(cairo_surface_t *image); // Get the image cairo surface cairo_surface_t *getImage() const { return image; } // Get the image rectangle void getRect(double *xa1, double *ya1, double *xa2, double *ya2) { *xa1 = x1; *ya1 = y1; *xa2 = x2; *ya2 = y2; } private: cairo_surface_t *image; // image cairo surface double x1, y1; // upper left corner double x2, y2; // lower right corner }; //------------------------------------------------------------------------ // CairoOutputDev //------------------------------------------------------------------------ class CairoOutputDev : public OutputDev { public: // Constructor. CairoOutputDev(); // Destructor. ~CairoOutputDev() override; //----- get info about output device // Does this device use upside-down coordinates? // (Upside-down means (0,0) is the top left corner of the page.) bool upsideDown() override { return true; } // Does this device use drawChar() or drawString()? bool useDrawChar() override { return true; } // Does this device use tilingPatternFill()? If this returns false, // tiling pattern fills will be reduced to a series of other drawing // operations. bool useTilingPatternFill() override { return true; } // 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 // Does this device use FillColorStop()? 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; } // Does this device need to clip pages to the crop box even when the // box is the crop box? bool needClipToCropBox() override { return true; } //----- initialization and control // Start a page. void startPage(int pageNum, GfxState *state, XRef *xref) override; // 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; //----- update graphics state void updateAll(GfxState *state) override; void setDefaultCTM(const double *ctm) override; void updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32) override; void updateLineDash(GfxState *state) override; void updateFlatness(GfxState *state) override; void updateLineJoin(GfxState *state) override; void updateLineCap(GfxState *state) override; void updateMiterLimit(GfxState *state) override; void updateLineWidth(GfxState *state) override; void updateFillColor(GfxState *state) override; void updateStrokeColor(GfxState *state) override; void updateFillOpacity(GfxState *state) override; void updateStrokeOpacity(GfxState *state) override; void updateFillColorStop(GfxState *state, double offset) override; void updateBlendMode(GfxState *state) override; //----- update text state void updateFont(GfxState *state) override; //----- path painting void stroke(GfxState *state) override; 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; #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; void eoClip(GfxState *state) override; //----- text drawing void beginString(GfxState *state, const GooString *s) override; void endString(GfxState *state) override; void drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, const Unicode *u, int uLen) override; void beginActualText(GfxState *state, const GooString *text) override; void endActualText(GfxState *state) override; bool beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, const Unicode *u, int uLen) override; void endType3Char(GfxState *state) override; 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; void 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) override; 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; //----- 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; void popTransparencyGroup(); void paintTransparencyGroup(GfxState * /*state*/, const double * /*bbox*/) override; void setSoftMask(GfxState * /*state*/, const double * /*bbox*/, bool /*alpha*/, Function * /*transferFunc*/, GfxColor * /*backdropColor*/) override; void clearSoftMask(GfxState * /*state*/) override; //----- Type 3 font operators void type3D0(GfxState *state, double wx, double wy) override; void type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) override; //----- special access // Called to indicate that a new PDF document has been loaded. void startDoc(PDFDoc *docA, CairoFontEngine *fontEngine = nullptr); // Called to prepare this output dev for rendering CairoType3Font. void startType3Render(GfxState *state, XRef *xref); bool isReverseVideo() { return false; } void setCairo(cairo_t *cr); void setTextPage(TextPage *text); void setPrinting(bool printingA) { printing = printingA; needFontUpdate = true; } void copyAntialias(cairo_t *cr, cairo_t *source_cr); void setLogicalStructure(bool logStruct) { this->logicalStruct = logStruct; } enum Type3RenderType { 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; } protected: void doPath(cairo_t *cairo, GfxState *state, const GfxPath *path); cairo_surface_t *downscaleSurface(cairo_surface_t *orig_surface); void getScaledSize(const cairo_matrix_t *matrix, int orig_width, int orig_height, int *scaledWidth, int *scaledHeight); cairo_filter_t getFilterForSurface(cairo_surface_t *image, bool interpolate); bool getStreamData(Stream *str, char **buffer, int *length); 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); 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); #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0) 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; double fill_opacity; double stroke_opacity; bool stroke_adjust; bool adjusted_stroke_width; bool align_stroke_coords; std::shared_ptr currentFont; XRef *xref; struct StrokePathClip { GfxPath *path; cairo_matrix_t ctm; double line_width; double *dashes; int dash_count; double dash_offset; cairo_line_cap_t cap; cairo_line_join_t join; double miter; int ref_count; } *strokePathClip; PDFDoc *doc; // the current document static FT_Library ft_lib; static std::once_flag ft_lib_once_flag; CairoFontEngine *fontEngine; bool fontEngine_owner; cairo_t *cairo; cairo_matrix_t orig_matrix; bool needFontUpdate; // set when the font needs to be updated bool printing; bool use_show_text_glyphs; bool text_matrix_valid; cairo_glyph_t *glyphs; int glyphCount; cairo_text_cluster_t *clusters; int clusterCount; char *utf8; int utf8Count; int utf8Max; cairo_path_t *textClipPath; bool inUncoloredPattern; // inside a uncolored pattern (PaintType = 2) 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; cairo_pattern_t *group; cairo_pattern_t *shape; cairo_pattern_t *mask; cairo_matrix_t mask_matrix; cairo_t *cairo_shape; int knockoutCount; struct ColorSpaceStack { bool knockout; GfxColorSpace *cs; cairo_matrix_t group_matrix; struct ColorSpaceStack *next; } *groupColorSpaceStack; struct SaveStateElement { // These patterns hold a reference cairo_pattern_t *fill_pattern; cairo_pattern_t *stroke_pattern; double fill_opacity; double stroke_opacity; cairo_pattern_t *mask; // can be null cairo_matrix_t mask_matrix; 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; }; //------------------------------------------------------------------------ // CairoImageOutputDev //------------------------------------------------------------------------ // XXX: this should ideally not inherit from CairoOutputDev but use it instead perhaps class CairoImageOutputDev : public CairoOutputDev { public: // Constructor. CairoImageOutputDev(); // Destructor. ~CairoImageOutputDev() override; //----- get info about output device // Does this device use upside-down coordinates? // (Upside-down means (0,0) is the top left corner of the page.) bool upsideDown() override { return true; } // Does this device use drawChar() or drawString()? bool useDrawChar() override { return false; } // Does this device use tilingPatternFill()? If this returns false, // tiling pattern fills will be reduced to a series of other drawing // operations. bool useTilingPatternFill() override { return true; } // 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 // Does this device use FillColorStop()? 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; } // Does this device need non-text content? bool needNonText() override { return true; } //----- save/restore graphics state void saveState(GfxState *state) override { } void restoreState(GfxState *state) override { } //----- update graphics state void updateAll(GfxState *state) override { } void setDefaultCTM(const double *ctm) override { } void updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32) override { } void updateLineDash(GfxState *state) override { } void updateFlatness(GfxState *state) override { } void updateLineJoin(GfxState *state) override { } void updateLineCap(GfxState *state) override { } void updateMiterLimit(GfxState *state) override { } void updateLineWidth(GfxState *state) override { } void updateFillColor(GfxState *state) override { } void updateStrokeColor(GfxState *state) override { } void updateFillOpacity(GfxState *state) override { } void updateStrokeOpacity(GfxState *state) override { } void updateBlendMode(GfxState *state) override { } //----- update text state void updateFont(GfxState *state) override { } //----- path painting void stroke(GfxState *state) override { } 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; } //----- path clipping void clip(GfxState *state) override { } void eoClip(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 drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) override; void 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) override; 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; 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 { } //----- 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 { } void paintTransparencyGroup(GfxState * /*state*/, const double * /*bbox*/) override { } void setSoftMask(GfxState * /*state*/, const double * /*bbox*/, bool /*alpha*/, Function * /*transferFunc*/, GfxColor * /*backdropColor*/) override { } void clearSoftMask(GfxState * /*state*/) override { } //----- Image list // By default images are not rendred void setImageDrawDecideCbk(bool (*cbk)(int img_id, void *data), void *data) { imgDrawCbk = cbk; imgDrawCbkData = data; } // Iterate through list of images. int getNumImages() const { return numImages; } CairoImage *getImage(int i) const { return images[i]; } private: void saveImage(CairoImage *image); void getBBox(GfxState *state, int width, int height, double *x1, double *y1, double *x2, double *y2); CairoImage **images; int numImages; int size; bool (*imgDrawCbk)(int img_id, void *data); void *imgDrawCbkData; }; #endif poppler-24.02.0/poppler/CairoRescaleBox.cc000066400000000000000000000277741455701731300203640ustar00rootroot00000000000000/* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */ /* * Copyright © 2009 Mozilla Corporation * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Mozilla Corporation not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Mozilla Corporation makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * MOZILLA CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT * SHALL MOZILLA CORPORATION BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. * * Author: Jeff Muizelaar, Mozilla Corp. */ //======================================================================== // // 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) 2012 Hib Eris // Copyright (C) 2012, 2017 Adrian Johnson // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019 Albert Astals Cid // Copyright (C) 2019 Marek Kasik // // 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 // //======================================================================== /* This implements a box filter that supports non-integer box sizes */ #include #include #include #include #include #include #include #include "goo/gmem.h" #include "CairoRescaleBox.h" /* we work in fixed point where 1. == 1 << 24 */ #define FIXED_SHIFT 24 static void downsample_row_box_filter(int start, int width, uint32_t *src, const uint32_t *src_limit, uint32_t *dest, const int coverage[], int pixel_coverage) { /* we need an array of the pixel contribution of each destination pixel on the boundaries. * we invert the value to get the value on the other size of the box */ /* value = a * contribution * 1/box_size value += a * 1/box_size value += a * 1/box_size value += a * 1/box_size value += a * (1 - contribution) * 1/box_size a * (1/box_size - contribution * 1/box_size) box size is constant value = a * contribution_a * 1/box_size + b * contribution_b * 1/box_size contribution_b = (1 - contribution_a) = (1 - contribution_a_next) */ /* box size = ceil(src_width/dest_width) */ int x = 0; /* skip to start */ /* XXX: it might be possible to do this directly instead of iteratively, however * the iterative solution is simple */ while (x < start && src < src_limit) { int box = 1 << FIXED_SHIFT; int start_coverage = coverage[x]; box -= start_coverage; src++; while (box >= pixel_coverage && src < src_limit) { src++; box -= pixel_coverage; } x++; } while (x < start + width && src < src_limit) { uint32_t a = 0; uint32_t r = 0; uint32_t g = 0; uint32_t b = 0; int box = 1 << FIXED_SHIFT; int start_coverage = coverage[x]; a = ((*src >> 24) & 0xff) * start_coverage; r = ((*src >> 16) & 0xff) * start_coverage; g = ((*src >> 8) & 0xff) * start_coverage; b = ((*src >> 0) & 0xff) * start_coverage; src++; x++; box -= start_coverage; while (box >= pixel_coverage && src < src_limit) { a += ((*src >> 24) & 0xff) * pixel_coverage; r += ((*src >> 16) & 0xff) * pixel_coverage; g += ((*src >> 8) & 0xff) * pixel_coverage; b += ((*src >> 0) & 0xff) * pixel_coverage; src++; box -= pixel_coverage; } /* multiply by whatever is leftover * this ensures that we don't bias down. * i.e. start_coverage + n*pixel_coverage + box == 1 << 24 */ if (box > 0 && src < src_limit) { a += ((*src >> 24) & 0xff) * box; r += ((*src >> 16) & 0xff) * box; g += ((*src >> 8) & 0xff) * box; b += ((*src >> 0) & 0xff) * box; } a >>= FIXED_SHIFT; r >>= FIXED_SHIFT; g >>= FIXED_SHIFT; b >>= FIXED_SHIFT; *dest = (a << 24) | (r << 16) | (g << 8) | b; dest++; } } static void downsample_columns_box_filter(int n, int start_coverage, int pixel_coverage, uint32_t *src, uint32_t *dest) { int stride = n; while (n--) { uint32_t a = 0; uint32_t r = 0; uint32_t g = 0; uint32_t b = 0; uint32_t *column_src = src; int box = 1 << FIXED_SHIFT; a = ((*column_src >> 24) & 0xff) * start_coverage; r = ((*column_src >> 16) & 0xff) * start_coverage; g = ((*column_src >> 8) & 0xff) * start_coverage; b = ((*column_src >> 0) & 0xff) * start_coverage; column_src += stride; box -= start_coverage; while (box >= pixel_coverage) { a += ((*column_src >> 24) & 0xff) * pixel_coverage; r += ((*column_src >> 16) & 0xff) * pixel_coverage; g += ((*column_src >> 8) & 0xff) * pixel_coverage; b += ((*column_src >> 0) & 0xff) * pixel_coverage; column_src += stride; box -= pixel_coverage; } if (box > 0) { a += ((*column_src >> 24) & 0xff) * box; r += ((*column_src >> 16) & 0xff) * box; g += ((*column_src >> 8) & 0xff) * box; b += ((*column_src >> 0) & 0xff) * box; } a >>= FIXED_SHIFT; r >>= FIXED_SHIFT; g >>= FIXED_SHIFT; b >>= FIXED_SHIFT; *dest = (a << 24) | (r << 16) | (g << 8) | b; dest++; src++; } } static int compute_coverage(int coverage[], int src_length, int dest_length) { int i; /* num = src_length/dest_length total = sum(pixel) / num pixel * 1/num == pixel * dest_length / src_length */ /* the average contribution of each source pixel */ int ratio = ((1 << 24) * (long long int)dest_length) / src_length; /* because ((1 << 24)*(long long int)dest_length) won't always be divisible by src_length * we'll need someplace to put the other bits. * * We want to ensure a + n*ratio < 1<<24 * * 1<<24 * */ double scale = (double)src_length / dest_length; /* for each destination pixel compute the coverage of the left most pixel included in the box */ /* I have a proof of this, which this margin is too narrow to contain */ for (i = 0; i < dest_length; i++) { double left_side = i * scale; double right_side = (i + 1) * scale; double right_fract = right_side - floor(right_side); double left_fract = ceil(left_side) - left_side; int overage; /* find out how many source pixels will be used to fill the box */ int count = floor(right_side) - ceil(left_side); /* what's the maximum value this expression can become? floor((i+1)*scale) - ceil(i*scale) (i+1)*scale - i*scale == scale since floor((i+1)*scale) <= (i+1)*scale and ceil(i*scale) >= i*scale floor((i+1)*scale) - ceil(i*scale) <= scale further since: floor((i+1)*scale) - ceil(i*scale) is an integer therefore: floor((i+1)*scale) - ceil(i*scale) <= floor(scale) */ if (left_fract == 0.) { count--; } /* compute how much the right-most pixel contributes */ overage = ratio * (right_fract); /* the remainder is the amount that the left-most pixel * contributes */ coverage[i] = (1 << 24) - (count * ratio + overage); } return ratio; } bool CairoRescaleBox::downScaleImage(unsigned orig_width, unsigned orig_height, signed scaled_width, signed scaled_height, unsigned short int start_column, unsigned short int start_row, unsigned short int width, unsigned short int height, cairo_surface_t *dest_surface) { int pixel_coverage_x, pixel_coverage_y; int dest_y; int src_y = 0; uint32_t *scanline; int *x_coverage = nullptr; int *y_coverage = nullptr; uint32_t *temp_buf = nullptr; bool retval = false; unsigned int *dest; int dst_stride; dest = reinterpret_cast(cairo_image_surface_get_data(dest_surface)); dst_stride = cairo_image_surface_get_stride(dest_surface); scanline = (uint32_t *)gmallocn(orig_width, sizeof(int)); x_coverage = (int *)gmallocn(orig_width, sizeof(int)); y_coverage = (int *)gmallocn(orig_height, sizeof(int)); /* we need to allocate enough room for ceil(src_height/dest_height)+1 Example: src_height = 140 dest_height = 50 src_height/dest_height = 2.8 |-------------| 2.8 pixels |----|----|----|----| 4 pixels need to sample 3 pixels |-------------| 2.8 pixels |----|----|----|----| 4 pixels need to sample 4 pixels */ temp_buf = (uint32_t *)gmallocn3((orig_height + scaled_height - 1) / scaled_height + 1, scaled_width, sizeof(uint32_t)); if (!x_coverage || !y_coverage || !scanline || !temp_buf) { goto cleanup; } pixel_coverage_x = compute_coverage(x_coverage, orig_width, scaled_width); pixel_coverage_y = compute_coverage(y_coverage, orig_height, scaled_height); assert(width + start_column <= scaled_width); /* skip the rows at the beginning */ for (dest_y = 0; dest_y < start_row; dest_y++) { int box = 1 << FIXED_SHIFT; int start_coverage_y = y_coverage[dest_y]; box -= start_coverage_y; src_y++; while (box >= pixel_coverage_y) { box -= pixel_coverage_y; src_y++; } } for (; dest_y < start_row + height; dest_y++) { int columns = 0; int box = 1 << FIXED_SHIFT; int start_coverage_y = y_coverage[dest_y]; getRow(src_y, scanline); downsample_row_box_filter(start_column, width, scanline, scanline + orig_width, temp_buf + width * columns, x_coverage, pixel_coverage_x); columns++; src_y++; box -= start_coverage_y; while (box >= pixel_coverage_y) { getRow(src_y, scanline); downsample_row_box_filter(start_column, width, scanline, scanline + orig_width, temp_buf + width * columns, x_coverage, pixel_coverage_x); columns++; src_y++; box -= pixel_coverage_y; } /* downsample any leftovers */ if (box > 0) { getRow(src_y, scanline); downsample_row_box_filter(start_column, width, scanline, scanline + orig_width, temp_buf + width * columns, x_coverage, pixel_coverage_x); columns++; } /* now scale the rows we just downsampled in the y direction */ downsample_columns_box_filter(width, start_coverage_y, pixel_coverage_y, temp_buf, dest); dest += dst_stride / 4; // assert(width*columns <= ((orig_height + scaled_height-1)/scaled_height+1) * width); } // assert (src_y<=orig_height); retval = true; cleanup: free(x_coverage); free(y_coverage); free(temp_buf); free(scanline); return retval; } poppler-24.02.0/poppler/CairoRescaleBox.h000066400000000000000000000047471455701731300202210ustar00rootroot00000000000000/* * Copyright © 2009 Mozilla Corporation * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Mozilla Corporation not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Mozilla Corporation makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * MOZILLA CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT * SHALL MOZILLA CORPORATION BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. * * Author: Jeff Muizelaar, Mozilla Corp. */ //======================================================================== // // 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) 2012 Adrian Johnson // Copyright (C) 2018 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 // //======================================================================== #ifndef CAIRO_RESCALE_BOX_H #define CAIRO_RESCALE_BOX_H #include class CairoRescaleBox { public: CairoRescaleBox() {}; virtual ~CairoRescaleBox() {}; CairoRescaleBox(const CairoRescaleBox &) = delete; CairoRescaleBox &operator=(const CairoRescaleBox &) = delete; virtual bool downScaleImage(unsigned orig_width, unsigned orig_height, signed scaled_width, signed scaled_height, unsigned short int start_column, unsigned short int start_row, unsigned short int width, unsigned short int height, cairo_surface_t *dest_surface); virtual void getRow(int row_num, uint32_t *row_data) = 0; }; #endif /* CAIRO_RESCALE_BOX_H */ poppler-24.02.0/poppler/Catalog.cc000066400000000000000000001073051455701731300167160ustar00rootroot00000000000000//======================================================================== // // Catalog.cc // // Copyright 1996-2007 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) 2005 Kristian Høgsberg // Copyright (C) 2005-2013, 2015, 2017-2023 Albert Astals Cid // Copyright (C) 2005 Jeff Muizelaar // Copyright (C) 2005 Jonathan Blandford // Copyright (C) 2005 Marco Pesenti Gritti // Copyright (C) 2005, 2006, 2008 Brad Hards // Copyright (C) 2006, 2008, 2011 Carlos Garcia Campos // Copyright (C) 2007 Julien Rebetez // Copyright (C) 2008, 2011 Pino Toscano // Copyright (C) 2009 Ilya Gorenbein // Copyright (C) 2010 Hib Eris // Copyright (C) 2012 Fabio D'Urso // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2013 Julien Nabet // Copyright (C) 2013 Adrian Perez de Castro // Copyright (C) 2013, 2017 Adrian Johnson // Copyright (C) 2013 José Aliste // Copyright (C) 2014 Ed Porras // Copyright (C) 2015 Even Rouault // Copyright (C) 2016 Masamichi Hosoda // 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 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) 2021 RM // Copyright (C) 2023 Ilaï Deutel // // 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 #include #include #include "goo/gmem.h" #include "Object.h" #include "PDFDoc.h" #include "XRef.h" #include "Array.h" #include "Dict.h" #include "Page.h" #include "Error.h" #include "Link.h" #include "PageLabelInfo.h" #include "Catalog.h" #include "Form.h" #include "OptionalContent.h" #include "ViewerPreferences.h" #include "FileSpec.h" #include "StructTreeRoot.h" //------------------------------------------------------------------------ // Catalog //------------------------------------------------------------------------ #define catalogLocker() const std::scoped_lock locker(mutex) Catalog::Catalog(PDFDoc *docA) { ok = true; doc = docA; xref = doc->getXRef(); numPages = -1; pageLabelInfo = nullptr; form = nullptr; optContent = nullptr; pageMode = pageModeNull; pageLayout = pageLayoutNull; destNameTree = nullptr; embeddedFileNameTree = nullptr; jsNameTree = nullptr; viewerPrefs = nullptr; structTreeRoot = nullptr; pagesList = nullptr; pagesRefList = nullptr; attrsList = nullptr; kidsIdxList = nullptr; markInfo = markInfoNull; Object catDict = xref->getCatalog(); if (!catDict.isDict()) { error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})", catDict.getTypeName()); ok = false; return; } // get the AcroForm dictionary acroForm = catDict.getDict()->lookup("AcroForm"); // read base URI Object obj = catDict.getDict()->lookupEnsureEncryptedIfNeeded("URI"); if (obj.isDict()) { Object obj2 = obj.getDict()->lookupEnsureEncryptedIfNeeded("Base"); if (obj2.isString()) { baseURI = obj2.getString()->toStr(); } } // get the Optional Content dictionary Object optContentProps = catDict.dictLookup("OCProperties"); if (optContentProps.isDict()) { optContent = new OCGs(&optContentProps, xref); if (!optContent->isOk()) { delete optContent; optContent = nullptr; } } // actions additionalActions = catDict.dictLookupNF("AA").copy(); // get the ViewerPreferences dictionary viewerPreferences = catDict.dictLookup("ViewerPreferences"); const Object version = catDict.dictLookup("Version"); if (version.isName()) { const int res = sscanf(version.getName(), "%d.%d", &catalogPdfMajorVersion, &catalogPdfMinorVersion); if (res != 2) { catalogPdfMajorVersion = -1; catalogPdfMinorVersion = -1; } } } 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; delete embeddedFileNameTree; delete jsNameTree; delete pageLabelInfo; delete form; delete optContent; delete viewerPrefs; delete structTreeRoot; } std::unique_ptr Catalog::readMetadata() { catalogLocker(); if (metadata.isNone()) { Object catDict = xref->getCatalog(); if (catDict.isDict()) { metadata = catDict.dictLookup("Metadata"); } else { error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})", catDict.getTypeName()); metadata.setToNull(); } } if (!metadata.isStream()) { return {}; } Object obj = metadata.streamGetDict()->lookup("Subtype"); if (!obj.isName("XML")) { error(errSyntaxWarning, -1, "Unknown Metadata type: '{0:s}'", obj.isName() ? obj.getName() : "???"); } std::unique_ptr s = std::make_unique(); metadata.getStream()->fillGooString(s.get()); metadata.streamClose(); return s; } Page *Catalog::getPage(int i) { if (i < 1) { return nullptr; } catalogLocker(); if (std::size_t(i) > pages.size()) { bool cached = cachePageTree(i); if (cached == false) { return nullptr; } } return pages[i - 1].first.get(); } Ref *Catalog::getPageRef(int i) { if (i < 1) { return nullptr; } catalogLocker(); if (std::size_t(i) > pages.size()) { bool cached = cachePageTree(i); if (cached == false) { return nullptr; } } return &pages[i - 1].second; } bool Catalog::cachePageTree(int page) { if (pagesList == nullptr) { Ref pagesRef; 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; } } 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; } 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); } while (true) { if (std::size_t(page) <= pages.size()) { return true; } if (pagesList->empty()) { 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; } 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; } 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()++; continue; } 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; } if (pages.size() >= std::size_t(numPages)) { error(errSyntaxError, -1, "Page count in top-level pages object is incorrect"); return false; } pages.emplace_back(std::move(p), kidRef.getRef()); 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(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()++; } } return false; } int Catalog::findPage(const Ref pageRef) { int i; for (i = 0; i < getNumPages(); ++i) { Ref *ref = getPageRef(i + 1); if (ref != nullptr && *ref == pageRef) { return i + 1; } } return 0; } std::unique_ptr Catalog::findDest(const GooString *name) { // try named destination dictionary then name tree if (getDests()->isDict()) { Object obj1 = getDests()->dictLookup(name->c_str()); return createLinkDest(&obj1); } catalogLocker(); Object obj2 = getDestNameTree()->lookup(name); return createLinkDest(&obj2); } std::unique_ptr Catalog::createLinkDest(Object *obj) { std::unique_ptr dest; if (obj->isArray()) { dest = std::make_unique(obj->getArray()); } else if (obj->isDict()) { Object obj2 = obj->dictLookup("D"); if (obj2.isArray()) { dest = std::make_unique(obj2.getArray()); } else { error(errSyntaxWarning, -1, "Bad named destination value"); } } else { error(errSyntaxWarning, -1, "Bad named destination value"); } if (dest && !dest->isOk()) { dest.reset(); } return dest; } int Catalog::numDests() { Object *obj; obj = getDests(); if (!obj->isDict()) { return 0; } return obj->dictGetLength(); } const char *Catalog::getDestsName(int i) { Object *obj; obj = getDests(); if (!obj->isDict()) { return nullptr; } return obj->dictGetKey(i); } std::unique_ptr Catalog::getDestsDest(int i) { Object *obj = getDests(); if (!obj->isDict()) { return nullptr; } Object obj1 = obj->dictGetVal(i); return createLinkDest(&obj1); } std::unique_ptr Catalog::getDestNameTreeDest(int i) { Object obj; catalogLocker(); Object *aux = getDestNameTree()->getValue(i); if (aux) { obj = aux->fetch(xref); } return createLinkDest(&obj); } std::unique_ptr Catalog::embeddedFile(int i) { catalogLocker(); Object *obj = getEmbeddedFileNameTree()->getValue(i); if (obj->isRef()) { Object fsDict = obj->fetch(xref); return std::make_unique(&fsDict); } else if (obj->isDict()) { return std::make_unique(obj); } else { Object null; return std::make_unique(&null); } } bool Catalog::hasEmbeddedFile(const std::string &fileName) { NameTree *ef = getEmbeddedFileNameTree(); for (int i = 0; i < ef->numEntries(); ++i) { if (fileName == ef->getName(i)->toStr()) { return true; } } return false; } void Catalog::addEmbeddedFile(GooFile *file, const std::string &fileName) { catalogLocker(); const Ref fileSpecRef = xref->addIndirectObject(FileSpec::newFileSpecObject(xref, file, fileName)); Object catDict = xref->getCatalog(); Ref namesObjRef; Object namesObj = catDict.getDict()->lookup("Names", &namesObjRef); if (!namesObj.isDict()) { // Need to create the names Dict catDict.dictSet("Names", Object(new Dict(xref))); namesObj = catDict.getDict()->lookup("Names"); // Trigger getting the names dict again when needed names = Object(); } Dict *namesDict = namesObj.getDict(); // We create a new EmbeddedFiles nametree, this replaces the existing one (if any), but it's not a problem Object embeddedFilesObj = Object(new Dict(xref)); const Ref embeddedFilesRef = xref->addIndirectObject(embeddedFilesObj); Array *embeddedFilesNamesArray = new Array(xref); // This flattens out the existing EmbeddedFiles nametree (if any), should not be a problem NameTree *ef = getEmbeddedFileNameTree(); bool fileAlreadyAdded = false; for (int i = 0; i < ef->numEntries(); ++i) { const GooString *efNameI = ef->getName(i); // we need to add the file if it has not been added yet and the name is smaller or equal lexicographically // than the current item const bool sameFileName = fileName == efNameI->toStr(); 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(fileSpecRef)); fileAlreadyAdded = true; } if (sameFileName) { // If the new name is the same lexicographically than an existing file then don't add the existing file (i.e. replace) continue; } embeddedFilesNamesArray->add(Object(efNameI->copy())); embeddedFilesNamesArray->add(ef->getValue(i)->copy()); } if (!fileAlreadyAdded) { // The new file is bigger lexicographically than the existing ones embeddedFilesNamesArray->add(Object(new GooString(fileName))); embeddedFilesNamesArray->add(Object(fileSpecRef)); } embeddedFilesObj.dictSet("Names", Object(embeddedFilesNamesArray)); namesDict->set("EmbeddedFiles", Object(embeddedFilesRef)); if (namesObjRef != Ref::INVALID()) { xref->setModifiedObject(&namesObj, namesObjRef); } else { xref->setModifiedObject(&catDict, { xref->getRootNum(), xref->getRootGen() }); } // recreate Nametree on next call that uses it delete embeddedFileNameTree; embeddedFileNameTree = nullptr; } GooString *Catalog::getJS(int i) { Object obj; // getJSNameTree()->getValue(i) returns a shallow copy of the object so we // do not need to free it catalogLocker(); Object *aux = getJSNameTree()->getValue(i); if (aux) { obj = aux->fetch(xref); } if (!obj.isDict()) { return nullptr; } Object obj2 = obj.dictLookup("S"); if (!obj2.isName()) { return nullptr; } if (strcmp(obj2.getName(), "JavaScript")) { return nullptr; } obj2 = obj.dictLookup("JS"); GooString *js = nullptr; if (obj2.isString()) { js = new GooString(obj2.getString()); } else if (obj2.isStream()) { Stream *stream = obj2.getStream(); js = new GooString(); stream->fillGooString(js); } return js; } Catalog::PageMode Catalog::getPageMode() { catalogLocker(); if (pageMode == pageModeNull) { pageMode = pageModeNone; Object catDict = xref->getCatalog(); if (!catDict.isDict()) { error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})", catDict.getTypeName()); return pageMode; } Object obj = catDict.dictLookup("PageMode"); if (obj.isName()) { if (obj.isName("UseNone")) { pageMode = pageModeNone; } else if (obj.isName("UseOutlines")) { pageMode = pageModeOutlines; } else if (obj.isName("UseThumbs")) { pageMode = pageModeThumbs; } else if (obj.isName("FullScreen")) { pageMode = pageModeFullScreen; } else if (obj.isName("UseOC")) { pageMode = pageModeOC; } else if (obj.isName("UseAttachments")) { pageMode = pageModeAttach; } } } return pageMode; } Catalog::PageLayout Catalog::getPageLayout() { catalogLocker(); if (pageLayout == pageLayoutNull) { pageLayout = pageLayoutNone; Object catDict = xref->getCatalog(); if (!catDict.isDict()) { error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})", catDict.getTypeName()); return pageLayout; } pageLayout = pageLayoutNone; Object obj = catDict.dictLookup("PageLayout"); if (obj.isName()) { if (obj.isName("SinglePage")) { pageLayout = pageLayoutSinglePage; } if (obj.isName("OneColumn")) { pageLayout = pageLayoutOneColumn; } if (obj.isName("TwoColumnLeft")) { pageLayout = pageLayoutTwoColumnLeft; } if (obj.isName("TwoColumnRight")) { pageLayout = pageLayoutTwoColumnRight; } if (obj.isName("TwoPageLeft")) { pageLayout = pageLayoutTwoPageLeft; } if (obj.isName("TwoPageRight")) { pageLayout = pageLayoutTwoPageRight; } } } 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::Entry::Entry(Array *array, int index) { if (!array->getString(index, &name)) { Object aux = array->get(index); if (aux.isString()) { name.append(aux.getString()); } else { error(errSyntaxError, -1, "Invalid page tree"); } } 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); } void NameTree::init(XRef *xrefA, Object *tree) { xref = xrefA; RefRecursionChecker seen; parse(tree, seen); if (entries && length > 0) { qsort(entries, length, sizeof(Entry *), Entry::cmpEntry); } } void NameTree::parse(const Object *tree, RefRecursionChecker &seen) { if (!tree->isDict()) { return; } // leaf node 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); } } // root or intermediate node Ref ref; const Object kids = tree->getDict()->lookup("Kids", &ref); 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 (!seen.insert(ref)) { error(errSyntaxError, -1, "loop in NameTree (numObj: {0:d})", ref.num); continue; } if (kid.isDict()) { parse(&kid, seen); } } } } int NameTree::Entry::cmp(const void *voidKey, const void *voidEntry) { GooString *key = (GooString *)voidKey; Entry *entry = *(NameTree::Entry **)voidEntry; return key->cmp(&entry->name); } Object NameTree::lookup(const GooString *name) { Entry **entry; entry = (Entry **)bsearch(name, entries, length, sizeof(Entry *), Entry::cmp); if (entry != nullptr) { return (*entry)->value.fetch(xref); } else { error(errSyntaxError, -1, "failed to look up ({0:s})", name->c_str()); return Object(objNull); } } Object *NameTree::getValue(int index) { if (index < length) { return &entries[index]->value; } else { return nullptr; } } const GooString *NameTree::getName(int index) const { if (index < length) { return &entries[index]->name; } else { return nullptr; } } bool Catalog::labelToIndex(GooString *label, int *index) { char *end; PageLabelInfo *pli = getPageLabelInfo(); if (pli != nullptr) { if (!pli->labelToIndex(label, index)) { return false; } } else { *index = strtol(label->c_str(), &end, 10) - 1; if (*end != '\0') { return false; } } if (*index < 0 || *index >= getNumPages()) { return false; } return true; } bool Catalog::indexToLabel(int index, GooString *label) { char buffer[32]; if (index < 0 || index >= getNumPages()) { return false; } PageLabelInfo *pli = getPageLabelInfo(); if (pli != nullptr) { return pli->indexToLabel(index, label); } else { snprintf(buffer, sizeof(buffer), "%d", index + 1); label->append(buffer); return true; } } int Catalog::getNumPages() { catalogLocker(); if (numPages == -1) { Object catDict = xref->getCatalog(); if (!catDict.isDict()) { error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})", catDict.getTypeName()); return 0; } Object pagesDict = 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 (!pagesDict.isDict()) { error(errSyntaxError, -1, "Top-level pages object is wrong type ({0:s})", pagesDict.getTypeName()); return 0; } Object obj = pagesDict.dictLookup("Count"); // some PDF files actually use real numbers here ("/Count 9.0") if (!obj.isNum()) { if (pagesDict.dictIs("Page")) { const Object &pageRootRef = catDict.dictLookupNF("Pages"); error(errSyntaxError, -1, "Pages top-level is a single Page. The document is malformed, trying to recover..."); 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); if (p->isOk()) { pages.emplace_back(std::move(p), pageRef); numPages = 1; } else { numPages = 0; } } else { numPages = 0; } } else { error(errSyntaxError, -1, "Page count in top-level pages object is wrong type ({0:s})", obj.getTypeName()); numPages = 0; } } else { 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; } else if (numPages > xref->getNumObjects()) { error(errSyntaxError, -1, "Page count ({0:d}) larger than number of objects ({1:d})", numPages, xref->getNumObjects()); numPages = 0; } } } return numPages; } PageLabelInfo *Catalog::getPageLabelInfo() { catalogLocker(); if (!pageLabelInfo) { Object catDict = xref->getCatalog(); if (!catDict.isDict()) { error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})", catDict.getTypeName()); return nullptr; } Object obj = catDict.dictLookup("PageLabels"); if (obj.isDict()) { pageLabelInfo = new PageLabelInfo(&obj, getNumPages()); } } return pageLabelInfo; } StructTreeRoot *Catalog::getStructTreeRoot() { catalogLocker(); if (!structTreeRoot) { Object catalog = xref->getCatalog(); if (!catalog.isDict()) { error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})", catalog.getTypeName()); return nullptr; } Object root = catalog.dictLookup("StructTreeRoot"); if (root.isDict("StructTreeRoot")) { structTreeRoot = new StructTreeRoot(doc, root.getDict()); } } return structTreeRoot; } unsigned int Catalog::getMarkInfo() { if (markInfo == markInfoNull) { markInfo = 0; catalogLocker(); Object catDict = xref->getCatalog(); if (catDict.isDict()) { Object markInfoDict = catDict.dictLookup("MarkInfo"); if (markInfoDict.isDict()) { Object value = markInfoDict.dictLookup("Marked"); if (value.isBool()) { if (value.getBool()) { markInfo |= markInfoMarked; } } else if (!value.isNull()) { error(errSyntaxError, -1, "Marked object is wrong type ({0:s})", value.getTypeName()); } value = markInfoDict.dictLookup("Suspects"); if (value.isBool() && value.getBool()) { markInfo |= markInfoSuspects; } else if (!value.isNull()) { error(errSyntaxError, -1, "Suspects object is wrong type ({0:s})", value.getTypeName()); } value = markInfoDict.dictLookup("UserProperties"); if (value.isBool() && value.getBool()) { markInfo |= markInfoUserProperties; } else if (!value.isNull()) { error(errSyntaxError, -1, "UserProperties object is wrong type ({0:s})", value.getTypeName()); } } else if (!markInfoDict.isNull()) { error(errSyntaxError, -1, "MarkInfo object is wrong type ({0:s})", markInfoDict.getTypeName()); } } else { error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})", catDict.getTypeName()); } } return markInfo; } Object *Catalog::getCreateOutline() { catalogLocker(); Object catDict = xref->getCatalog(); // If there is no Object in the outline variable, // check if there is an Outline dict in the catalog if (outline.isNone()) { if (catDict.isDict()) { Object outline_obj = catDict.dictLookup("Outlines"); if (outline_obj.isDict()) { return &outline; } } else { // catalog is not a dict, give up? return &outline; } } // If there is an Object in variable, make sure it's a dict if (outline.isDict()) { return &outline; } // setup an empty outline dict outline = Object(new Dict(doc->getXRef())); outline.dictSet("Type", Object(objName, "Outlines")); outline.dictSet("Count", Object(0)); const Ref outlineRef = doc->getXRef()->addIndirectObject(outline); catDict.dictAdd("Outlines", Object(outlineRef)); xref->setModifiedObject(&catDict, { xref->getRootNum(), xref->getRootGen() }); return &outline; } Object *Catalog::getOutline() { catalogLocker(); if (outline.isNone()) { Object catDict = xref->getCatalog(); if (catDict.isDict()) { outline = catDict.dictLookup("Outlines"); } else { error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})", catDict.getTypeName()); outline.setToNull(); } } return &outline; } Object *Catalog::getDests() { catalogLocker(); if (dests.isNone()) { Object catDict = xref->getCatalog(); if (catDict.isDict()) { dests = catDict.dictLookup("Dests"); } else { error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})", catDict.getTypeName()); dests.setToNull(); } } return &dests; } Catalog::FormType Catalog::getFormType() { Object xfa; FormType res = NoForm; if (acroForm.isDict()) { xfa = acroForm.dictLookup("XFA"); if (xfa.isStream() || xfa.isArray()) { res = XfaForm; } else { res = AcroForm; } } return res; } Form *Catalog::getCreateForm() { catalogLocker(); if (!form) { Object catDict = xref->getCatalog(); if (!catDict.isDict()) { error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})", catDict.getTypeName()); return nullptr; } if (!acroForm.isDict()) { acroForm = Object(new Dict(xref)); acroForm.dictSet("Fields", Object(new Array(xref))); const Ref newFormRef = xref->addIndirectObject(acroForm); catDict.dictSet("AcroForm", Object(newFormRef)); xref->setModifiedObject(&catDict, { xref->getRootNum(), xref->getRootGen() }); } } return getForm(); } Form *Catalog::getForm() { catalogLocker(); if (!form) { if (acroForm.isDict()) { form = new Form(doc); // perform form-related loading after all widgets have been loaded form->postWidgetsLoad(); } } return form; } void Catalog::addFormToAcroForm(const Ref formRef) { catalogLocker(); if (!acroForm.isDict()) { getCreateForm(); } // append to field array Ref fieldRef; Object fieldArray = acroForm.getDict()->lookup("Fields", &fieldRef); fieldArray.getArray()->add(Object(formRef)); setAcroFormModified(); } void Catalog::setAcroFormModified() { Object catDict = xref->getCatalog(); Ref acroFormRef; catDict.getDict()->lookup("AcroForm", &acroFormRef); if (acroFormRef != Ref::INVALID()) { xref->setModifiedObject(&acroForm, acroFormRef); } else { xref->setModifiedObject(&catDict, { xref->getRootNum(), xref->getRootGen() }); } } void Catalog::removeFormFromAcroForm(const Ref formRef) { catalogLocker(); Object catDict = xref->getCatalog(); if (acroForm.isDict()) { // remove from field array Ref fieldRef; Object fieldArrayO = acroForm.getDict()->lookup("Fields", &fieldRef); Array *fieldArray = fieldArrayO.getArray(); for (int i = 0; i < fieldArray->getLength(); ++i) { const Object &o = fieldArray->getNF(i); if (o.isRef() && o.getRef() == formRef) { fieldArray->remove(i); break; } } setAcroFormModified(); } } ViewerPreferences *Catalog::getViewerPreferences() { catalogLocker(); if (!viewerPrefs) { if (viewerPreferences.isDict()) { viewerPrefs = new ViewerPreferences(viewerPreferences.getDict()); } } return viewerPrefs; } Object *Catalog::getNames() { if (names.isNone()) { Object catDict = xref->getCatalog(); if (catDict.isDict()) { names = catDict.dictLookup("Names"); } else { error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})", catDict.getTypeName()); names.setToNull(); } } return &names; } NameTree *Catalog::getDestNameTree() { if (!destNameTree) { destNameTree = new NameTree(); if (getNames()->isDict()) { Object obj = getNames()->dictLookup("Dests"); destNameTree->init(xref, &obj); } } return destNameTree; } NameTree *Catalog::getEmbeddedFileNameTree() { if (!embeddedFileNameTree) { embeddedFileNameTree = new NameTree(); if (getNames()->isDict()) { Object obj = getNames()->dictLookup("EmbeddedFiles"); embeddedFileNameTree->init(xref, &obj); } } return embeddedFileNameTree; } NameTree *Catalog::getJSNameTree() { if (!jsNameTree) { jsNameTree = new NameTree(); if (getNames()->isDict()) { Object obj = getNames()->dictLookup("JavaScript"); jsNameTree->init(xref, &obj); } } return jsNameTree; } std::unique_ptr Catalog::getAdditionalAction(DocumentAdditionalActionsType type) { Object additionalActionsObject = additionalActions.fetch(doc->getXRef()); if (additionalActionsObject.isDict()) { const char *key = (type == actionCloseDocument ? "WC" : type == actionSaveDocumentStart ? "WS" : type == actionSaveDocumentFinish ? "DS" : type == actionPrintDocumentStart ? "WP" : type == actionPrintDocumentFinish ? "DP" : nullptr); Object actionObject = additionalActionsObject.dictLookup(key); if (actionObject.isDict()) { return LinkAction::parseAction(&actionObject, doc->getCatalog()->getBaseURI()); } } return nullptr; } poppler-24.02.0/poppler/Catalog.h000066400000000000000000000246701455701731300165630ustar00rootroot00000000000000//======================================================================== // // Catalog.h // // Copyright 1996-2007 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) 2005 Kristian Høgsberg // Copyright (C) 2005, 2007, 2009-2011, 2013, 2017-2023 Albert Astals Cid // Copyright (C) 2005 Jonathan Blandford // Copyright (C) 2005, 2006, 2008 Brad Hards // Copyright (C) 2007 Julien Rebetez // Copyright (C) 2008, 2011 Pino Toscano // Copyright (C) 2010 Hib Eris // Copyright (C) 2012 Fabio D'Urso // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2013 Adrian Perez de Castro // Copyright (C) 2013, 2017 Adrian Johnson // Copyright (C) 2013 José Aliste // Copyright (C) 2016 Masamichi Hosoda // 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 Oliver Sander // 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 // // 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 CATALOG_H #define CATALOG_H #include "poppler-config.h" #include "poppler_private_export.h" #include "Object.h" #include "Link.h" #include #include #include class PDFDoc; class XRef; class Object; class Page; class PageAttrs; struct Ref; class PageLabelInfo; class Form; class OCGs; class ViewerPreferences; class FileSpec; class StructTreeRoot; //------------------------------------------------------------------------ // NameTree //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT NameTree { public: NameTree(); ~NameTree(); NameTree(const NameTree &) = delete; NameTree &operator=(const NameTree &) = delete; void init(XRef *xref, Object *tree); Object lookup(const GooString *name); int numEntries() { return length; }; // 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; private: struct Entry { Entry(Array *array, int index); ~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, RefRecursionChecker &seen); void addEntry(Entry *entry); 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 }; //------------------------------------------------------------------------ // Catalog //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT Catalog { public: // Constructor. explicit Catalog(PDFDoc *docA); // Destructor. ~Catalog(); Catalog(const Catalog &) = delete; Catalog &operator=(const Catalog &) = delete; // Is catalog valid? bool isOk() { return ok; } // Get number of pages. int getNumPages(); // Get a page. Page *getPage(int i); // Get the reference for a page object. Ref *getPageRef(int i); // Return base URI, or NULL if none. const std::optional &getBaseURI() const { return baseURI; } // Return the contents of the metadata stream, or NULL if there is // no metadata. std::unique_ptr readMetadata(); // Return the structure tree root object. StructTreeRoot *getStructTreeRoot(); // Return values from the MarkInfo dictionary as flags in a bitfield. enum MarkInfoFlags { markInfoNull = 1 << 0, markInfoMarked = 1 << 1, markInfoUserProperties = 1 << 2, markInfoSuspects = 1 << 3, }; unsigned int getMarkInfo(); // Find a page, given its object ID. Returns page number, or 0 if // not found. int findPage(const Ref pageRef); // Find a named destination. Returns the link destination, or // NULL if is not a destination. std::unique_ptr findDest(const GooString *name); Object *getDests(); // Get the number of named destinations in name-dict int numDests(); // Get the i'th named destination name in name-dict const char *getDestsName(int i); // Get the i'th named destination link destination in name-dict std::unique_ptr getDestsDest(int i); // Get the number of named destinations in name-tree int numDestNameTree() { return getDestNameTree()->numEntries(); } // Get the i'th named destination name in name-tree const GooString *getDestNameTreeName(int i) { return getDestNameTree()->getName(i); } // Get the i'th named destination link destination in name-tree std::unique_ptr getDestNameTreeDest(int i); // Get the number of embedded files int numEmbeddedFiles() { return getEmbeddedFileNameTree()->numEntries(); } // Get the i'th file embedded (at the Document level) in the document std::unique_ptr embeddedFile(int i); // Is there an embedded file with the given name? bool hasEmbeddedFile(const std::string &fileName); // Adds and embeddedFile // If there is already an existing embedded file with the given fileName // it gets replaced, if that's not what you want check hasEmbeddedFile first void addEmbeddedFile(GooFile *file, const std::string &fileName); // Get the number of javascript scripts int numJS() { return getJSNameTree()->numEntries(); } 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); // Convert between page indices and page labels. bool labelToIndex(GooString *label, int *index); bool indexToLabel(int index, GooString *label); Object *getOutline(); // returns the existing outline or new one if it doesn't exist Object *getCreateOutline(); Object *getAcroForm() { return &acroForm; } void addFormToAcroForm(const Ref formRef); void removeFormFromAcroForm(const Ref formRef); void setAcroFormModified(); OCGs *getOptContentConfig() { return optContent; } int getPDFMajorVersion() const { return catalogPdfMajorVersion; } int getPDFMinorVersion() const { return catalogPdfMinorVersion; } enum FormType { NoForm, AcroForm, XfaForm }; FormType getFormType(); // This can return nullptr if the document is in a very damaged state Form *getCreateForm(); Form *getForm(); ViewerPreferences *getViewerPreferences(); enum PageMode { pageModeNone, pageModeOutlines, pageModeThumbs, pageModeFullScreen, pageModeOC, pageModeAttach, pageModeNull }; enum PageLayout { pageLayoutNone, pageLayoutSinglePage, pageLayoutOneColumn, pageLayoutTwoColumnLeft, pageLayoutTwoColumnRight, pageLayoutTwoPageLeft, pageLayoutTwoPageRight, pageLayoutNull }; // Returns the page mode. PageMode getPageMode(); PageLayout getPageLayout(); enum DocumentAdditionalActionsType { actionCloseDocument, ///< Performed before closing the document actionSaveDocumentStart, ///< Performed before saving the document actionSaveDocumentFinish, ///< Performed after saving the document actionPrintDocumentStart, ///< Performed before printing the document actionPrintDocumentFinish, ///< Performed after printing the document }; std::unique_ptr getAdditionalAction(DocumentAdditionalActionsType type); private: // Get page label info. PageLabelInfo *getPageLabelInfo(); PDFDoc *doc; XRef *xref; // the xref table for this PDF file std::vector, Ref>> pages; std::vector *pagesList; std::vector *pagesRefList; std::vector *attrsList; std::vector *kidsIdxList; Form *form; ViewerPreferences *viewerPrefs; int numPages; // number of pages Object dests; // named destination dictionary Object names; // named names dictionary NameTree *destNameTree; // named destination name-tree NameTree *embeddedFileNameTree; // embedded file name-tree NameTree *jsNameTree; // Java Script name-tree std::optional baseURI; // base URI for URI-type links Object metadata; // metadata stream StructTreeRoot *structTreeRoot; // structure tree root unsigned int markInfo; // Flags from MarkInfo dictionary Object outline; // outline dictionary Object acroForm; // AcroForm dictionary Object viewerPreferences; // ViewerPreference dictionary OCGs *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 cachePageTree(int page); // Cache first pages. Object *findDestInTree(Object *tree, GooString *name, Object *obj); Object *getNames(); NameTree *getDestNameTree(); NameTree *getEmbeddedFileNameTree(); NameTree *getJSNameTree(); std::unique_ptr createLinkDest(Object *obj); int catalogPdfMajorVersion = -1; int catalogPdfMinorVersion = -1; mutable std::recursive_mutex mutex; }; #endif poppler-24.02.0/poppler/CertificateInfo.cc000066400000000000000000000057301455701731300204010ustar00rootroot00000000000000//======================================================================== // // CertificateInfo.cc // // This file is licensed under the GPLv2 or later // // Copyright 2018 Chinmoy Ranjan Pradhan // Copyright 2018, 2019, 2022 Albert Astals Cid // Copyright 2018 Oliver Sander // Copyright 2020 Thorsten Behrens // Copyright 2023 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== #include "CertificateInfo.h" #include #include X509CertificateInfo::X509CertificateInfo() : ku_extensions(KU_NONE), cert_version(-1), is_self_signed(false), keyLocation(KeyLocation::Unknown) { } X509CertificateInfo::~X509CertificateInfo() = default; int X509CertificateInfo::getVersion() const { return cert_version; } const GooString &X509CertificateInfo::getSerialNumber() const { return cert_serial; } const GooString &X509CertificateInfo::getNickName() const { return cert_nick; } const X509CertificateInfo::EntityInfo &X509CertificateInfo::getIssuerInfo() const { return issuer_info; } const X509CertificateInfo::Validity &X509CertificateInfo::getValidity() const { return cert_validity; } const X509CertificateInfo::EntityInfo &X509CertificateInfo::getSubjectInfo() const { return subject_info; } const X509CertificateInfo::PublicKeyInfo &X509CertificateInfo::getPublicKeyInfo() const { return public_key_info; } unsigned int X509CertificateInfo::getKeyUsageExtensions() const { return ku_extensions; } const GooString &X509CertificateInfo::getCertificateDER() const { return cert_der; } bool X509CertificateInfo::getIsSelfSigned() const { return is_self_signed; } void X509CertificateInfo::setVersion(int version) { cert_version = version; } void X509CertificateInfo::setSerialNumber(const GooString &serialNumber) { cert_serial.Set(&serialNumber); } void X509CertificateInfo::setNickName(const GooString &nickName) { cert_nick.Set(&nickName); } void X509CertificateInfo::setIssuerInfo(EntityInfo &&issuerInfo) { issuer_info = std::move(issuerInfo); } void X509CertificateInfo::setValidity(Validity validity) { cert_validity = validity; } void X509CertificateInfo::setSubjectInfo(EntityInfo &&subjectInfo) { subject_info = std::move(subjectInfo); } void X509CertificateInfo::setPublicKeyInfo(PublicKeyInfo &&pkInfo) { public_key_info = std::move(pkInfo); } void X509CertificateInfo::setKeyUsageExtensions(unsigned int keyUsages) { ku_extensions = keyUsages; } void X509CertificateInfo::setCertificateDER(const GooString &certDer) { cert_der.Set(&certDer); } void X509CertificateInfo::setIsSelfSigned(bool isSelfSigned) { is_self_signed = isSelfSigned; } KeyLocation X509CertificateInfo::getKeyLocation() const { return keyLocation; } void X509CertificateInfo::setKeyLocation(KeyLocation location) { keyLocation = location; } poppler-24.02.0/poppler/CertificateInfo.h000066400000000000000000000103641455701731300202420ustar00rootroot00000000000000//======================================================================== // // CertificateInfo.h // // This file is licensed under the GPLv2 or later // // Copyright 2018 Chinmoy Ranjan Pradhan // Copyright 2018, 2019 Albert Astals Cid // Copyright 2018 Oliver Sander // Copyright 2020 Thorsten Behrens // Copyright 2023 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== #ifndef CERTIFICATEINFO_H #define CERTIFICATEINFO_H #include #include #include "goo/GooString.h" #include "poppler_private_export.h" enum CertificateKeyUsageExtension { KU_DIGITAL_SIGNATURE = 0x80, KU_NON_REPUDIATION = 0x40, KU_KEY_ENCIPHERMENT = 0x20, KU_DATA_ENCIPHERMENT = 0x10, KU_KEY_AGREEMENT = 0x08, KU_KEY_CERT_SIGN = 0x04, KU_CRL_SIGN = 0x02, KU_ENCIPHER_ONLY = 0x01, KU_NONE = 0x00 }; enum PublicKeyType { RSAKEY, DSAKEY, ECKEY, OTHERKEY }; /** 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) */ }; class POPPLER_PRIVATE_EXPORT X509CertificateInfo { public: X509CertificateInfo(); ~X509CertificateInfo(); X509CertificateInfo(const X509CertificateInfo &) = delete; X509CertificateInfo &operator=(const X509CertificateInfo &) = delete; struct PublicKeyInfo { PublicKeyInfo() = default; PublicKeyInfo(PublicKeyInfo &&) noexcept = default; PublicKeyInfo &operator=(PublicKeyInfo &&) noexcept = default; PublicKeyInfo(const PublicKeyInfo &) = delete; PublicKeyInfo &operator=(const PublicKeyInfo &) = delete; GooString publicKey; PublicKeyType publicKeyType = OTHERKEY; unsigned int publicKeyStrength = 0; // in bits }; struct EntityInfo { EntityInfo() = default; ~EntityInfo() = default; EntityInfo(EntityInfo &&) noexcept = default; EntityInfo &operator=(EntityInfo &&) noexcept = default; EntityInfo(const EntityInfo &) = delete; EntityInfo &operator=(const EntityInfo &) = delete; std::string commonName; std::string distinguishedName; std::string email; std::string organization; }; struct Validity { Validity() : notBefore(0), notAfter(0) { } time_t notBefore; time_t notAfter; }; /* GETTERS */ int getVersion() const; const GooString &getSerialNumber() const; const GooString &getNickName() const; const EntityInfo &getIssuerInfo() const; const Validity &getValidity() const; const EntityInfo &getSubjectInfo() const; const PublicKeyInfo &getPublicKeyInfo() const; unsigned int getKeyUsageExtensions() const; const GooString &getCertificateDER() const; bool getIsSelfSigned() const; KeyLocation getKeyLocation() const; /* SETTERS */ void setVersion(int); void setSerialNumber(const GooString &); void setNickName(const GooString &); void setIssuerInfo(EntityInfo &&); void setValidity(Validity); void setSubjectInfo(EntityInfo &&); void setPublicKeyInfo(PublicKeyInfo &&); void setKeyUsageExtensions(unsigned int); void setCertificateDER(const GooString &); void setIsSelfSigned(bool); void setKeyLocation(KeyLocation location); private: EntityInfo issuer_info; EntityInfo subject_info; PublicKeyInfo public_key_info; Validity cert_validity; GooString cert_serial; GooString cert_der; GooString cert_nick; unsigned int ku_extensions; int cert_version; bool is_self_signed; KeyLocation keyLocation; }; #endif poppler-24.02.0/poppler/CharCodeToUnicode.cc000066400000000000000000000607701455701731300206320ustar00rootroot00000000000000//======================================================================== // // CharCodeToUnicode.cc // // Copyright 2001-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, 2008-2010, 2012, 2018-2022 Albert Astals Cid // Copyright (C) 2007 Julien Rebetez // Copyright (C) 2007 Koji Otani // Copyright (C) 2008 Michael Vrable // Copyright (C) 2008 Vasile Gaburici // Copyright (C) 2010 William Bader // Copyright (C) 2010 Jakub Wilk // Copyright (C) 2012 Thomas Freitag // Copyright (C) 2012, 2017 Adrian Johnson // Copyright (C) 2014 Jiri Slaby // Copyright (C) 2015 Marek Kasik // Copyright (C) 2017 Jean Ghali // 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 // //======================================================================== #include #include #include #include #include "goo/glibc.h" #include "goo/gmem.h" #include "goo/gfile.h" #include "goo/GooLikely.h" #include "goo/GooString.h" #include "Error.h" #include "GlobalParams.h" #include "PSTokenizer.h" #include "CharCodeToUnicode.h" #include "UTF.h" //------------------------------------------------------------------------ //------------------------------------------------------------------------ static int getCharFromString(void *data) { unsigned char *p; int c; p = *(unsigned char **)data; if (*p) { c = *p++; *(unsigned char **)data = p; } else { c = EOF; } return c; } static int getCharFromFile(void *data) { return fgetc((FILE *)data); } //------------------------------------------------------------------------ static const int hexCharVals[256] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 1x -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 2x 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 3x -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 4x -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 5x -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 6x -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 7x -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 8x -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 9x -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Ax -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Bx -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Cx -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Dx -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Ex -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // Fx }; // Parse a -byte hex string into *. Returns false on // error. static bool parseHex(const char *s, int len, unsigned int *val) { int i, x; *val = 0; for (i = 0; i < len; ++i) { x = hexCharVals[s[i] & 0xff]; if (x < 0) { return false; } *val = (*val << 4) + x; } return true; } //------------------------------------------------------------------------ CharCodeToUnicode *CharCodeToUnicode::makeIdentityMapping() { CharCodeToUnicode *ctu = new CharCodeToUnicode(); ctu->isIdentity = true; ctu->mapLen = 1; ctu->map = (Unicode *)gmallocn(ctu->mapLen, sizeof(Unicode)); return ctu; } CharCodeToUnicode *CharCodeToUnicode::parseCIDToUnicode(const char *fileName, const GooString *collection) { FILE *f; Unicode *mapA; CharCode size, mapLenA; char buf[64]; Unicode u; CharCodeToUnicode *ctu; if (!(f = openFile(fileName, "r"))) { error(errIO, -1, "Couldn't open cidToUnicode file '{0:s}'", fileName); return nullptr; } size = 32768; mapA = (Unicode *)gmallocn(size, sizeof(Unicode)); mapLenA = 0; while (getLine(buf, sizeof(buf), f)) { if (mapLenA == size) { size *= 2; mapA = (Unicode *)greallocn(mapA, size, sizeof(Unicode)); } if (sscanf(buf, "%x", &u) == 1) { mapA[mapLenA] = u; } else { error(errSyntaxWarning, -1, "Bad line ({0:d}) in cidToUnicode file '{1:s}'", (int)(mapLenA + 1), fileName); mapA[mapLenA] = 0; } ++mapLenA; } fclose(f); ctu = new CharCodeToUnicode(collection->toStr(), mapA, mapLenA, true, {}); gfree(mapA); return ctu; } CharCodeToUnicode *CharCodeToUnicode::parseUnicodeToUnicode(const GooString *fileName) { FILE *f; Unicode *mapA; CharCode size, oldSize, len; 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; std::vector sMapA; 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; std::vector u; u.reserve(n); for (i = 0; i < n; ++i) { u.push_back(uBuf[i]); } sMapA.push_back({ u0, std::move(u) }); } if (u0 >= len) { len = u0 + 1; } } fclose(f); ctu = new CharCodeToUnicode(fileName->toStr(), mapA, len, true, std::move(sMapA)); gfree(mapA); gfree(uBuf); return ctu; } CharCodeToUnicode *CharCodeToUnicode::make8BitToUnicode(Unicode *toUnicode) { return new CharCodeToUnicode({}, toUnicode, 256, true, {}); } CharCodeToUnicode *CharCodeToUnicode::parseCMap(const GooString *buf, int nBits) { CharCodeToUnicode *ctu; ctu = new CharCodeToUnicode(std::optional()); const char *p = buf->c_str(); if (!ctu->parseCMap1(&getCharFromString, &p, nBits)) { delete ctu; return nullptr; } return ctu; } CharCodeToUnicode *CharCodeToUnicode::parseCMapFromFile(const GooString *fileName, int nBits) { CharCodeToUnicode *ctu; FILE *f; ctu = new CharCodeToUnicode(std::optional()); if ((f = globalParams->findToUnicodeFile(fileName))) { if (!ctu->parseCMap1(&getCharFromFile, f, nBits)) { delete ctu; fclose(f); return nullptr; } } else { error(errSyntaxError, -1, "Couldn't find ToUnicode CMap file for '{0:t}'", fileName); } return ctu; } void CharCodeToUnicode::mergeCMap(const GooString *buf, int nBits) { const char *p = buf->c_str(); parseCMap1(&getCharFromString, &p, nBits); } bool CharCodeToUnicode::parseCMap1(int (*getCharFunc)(void *), void *data, int nBits) { PSTokenizer *pst; char tok1[256], tok2[256], tok3[256]; int n1, n2, n3; CharCode i; CharCode maxCode, code1, code2; GooString *name; FILE *f; bool ok = false; maxCode = (nBits == 8) ? 0xff : (nBits == 16) ? 0xffff : 0xffffffff; pst = new PSTokenizer(getCharFunc, data); pst->getToken(tok1, sizeof(tok1), &n1); while (pst->getToken(tok2, sizeof(tok2), &n2)) { if (!strcmp(tok2, "usecmap")) { if (tok1[0] == '/') { name = new GooString(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); } delete name; } pst->getToken(tok1, sizeof(tok1), &n1); } else if (!strcmp(tok2, "beginbfchar")) { while (pst->getToken(tok1, sizeof(tok1), &n1)) { if (!strcmp(tok1, "endbfchar")) { break; } if (!pst->getToken(tok2, sizeof(tok2), &n2) || !strcmp(tok2, "endbfchar")) { error(errSyntaxWarning, -1, "Illegal entry in bfchar block in ToUnicode CMap"); break; } if (!(tok1[0] == '<' && tok1[n1 - 1] == '>' && tok2[0] == '<' && tok2[n2 - 1] == '>')) { error(errSyntaxWarning, -1, "Illegal entry in bfchar block in ToUnicode CMap"); continue; } tok1[n1 - 1] = tok2[n2 - 1] = '\0'; if (!parseHex(tok1 + 1, n1 - 2, &code1)) { error(errSyntaxWarning, -1, "Illegal entry in bfchar block in ToUnicode CMap"); continue; } if (code1 > maxCode) { error(errSyntaxWarning, -1, "Invalid entry in bfchar block in ToUnicode CMap"); } addMapping(code1, tok2 + 1, n2 - 2, 0); ok = true; } pst->getToken(tok1, sizeof(tok1), &n1); } else if (!strcmp(tok2, "beginbfrange")) { while (pst->getToken(tok1, sizeof(tok1), &n1)) { if (!strcmp(tok1, "endbfrange")) { break; } if (!pst->getToken(tok2, sizeof(tok2), &n2) || !strcmp(tok2, "endbfrange") || !pst->getToken(tok3, sizeof(tok3), &n3) || !strcmp(tok3, "endbfrange")) { error(errSyntaxWarning, -1, "Illegal entry in bfrange block in ToUnicode CMap"); break; } if (!(tok1[0] == '<' && tok1[n1 - 1] == '>' && tok2[0] == '<' && tok2[n2 - 1] == '>')) { error(errSyntaxWarning, -1, "Illegal entry in bfrange block in ToUnicode CMap"); continue; } tok1[n1 - 1] = tok2[n2 - 1] = '\0'; if (!parseHex(tok1 + 1, n1 - 2, &code1) || !parseHex(tok2 + 1, n2 - 2, &code2)) { error(errSyntaxWarning, -1, "Illegal entry in bfrange block in ToUnicode CMap"); continue; } if (code1 > maxCode || code2 > maxCode) { error(errSyntaxWarning, -1, "Invalid entry in bfrange block in ToUnicode CMap"); if (code1 > maxCode) { code1 = maxCode; } if (code2 > maxCode) { code2 = maxCode; } } if (!strcmp(tok3, "[")) { i = 0; while (pst->getToken(tok1, sizeof(tok1), &n1) && code1 + i <= code2) { if (!strcmp(tok1, "]")) { break; } if (tok1[0] == '<' && tok1[n1 - 1] == '>') { tok1[n1 - 1] = '\0'; addMapping(code1 + i, tok1 + 1, n1 - 2, 0); ok = true; } else { error(errSyntaxWarning, -1, "Illegal entry in bfrange block in ToUnicode CMap"); } ++i; } } else if (tok3[0] == '<' && tok3[n3 - 1] == '>') { tok3[n3 - 1] = '\0'; for (i = 0; code1 <= code2; ++code1, ++i) { addMapping(code1, tok3 + 1, n3 - 2, i); ok = true; } } else { error(errSyntaxWarning, -1, "Illegal entry in bfrange block in ToUnicode CMap"); } } pst->getToken(tok1, sizeof(tok1), &n1); } else if (!strcmp(tok2, "begincidchar")) { // the begincidchar operator is not allowed in ToUnicode CMaps, // but some buggy PDF generators incorrectly use // code-to-CID-type CMaps here error(errSyntaxWarning, -1, "Invalid 'begincidchar' operator in ToUnicode CMap"); while (pst->getToken(tok1, sizeof(tok1), &n1)) { if (!strcmp(tok1, "endcidchar")) { break; } if (!pst->getToken(tok2, sizeof(tok2), &n2) || !strcmp(tok2, "endcidchar")) { error(errSyntaxWarning, -1, "Illegal entry in cidchar block in ToUnicode CMap"); break; } if (!(tok1[0] == '<' && tok1[n1 - 1] == '>')) { error(errSyntaxWarning, -1, "Illegal entry in cidchar block in ToUnicode CMap"); continue; } tok1[n1 - 1] = '\0'; if (!parseHex(tok1 + 1, n1 - 2, &code1)) { error(errSyntaxWarning, -1, "Illegal entry in cidchar block in ToUnicode CMap"); continue; } if (code1 > maxCode) { error(errSyntaxWarning, -1, "Invalid entry in cidchar block in ToUnicode CMap"); } addMappingInt(code1, atoi(tok2)); ok = true; } pst->getToken(tok1, sizeof(tok1), &n1); } else if (!strcmp(tok2, "begincidrange")) { // the begincidrange operator is not allowed in ToUnicode CMaps, // but some buggy PDF generators incorrectly use // code-to-CID-type CMaps here error(errSyntaxWarning, -1, "Invalid 'begincidrange' operator in ToUnicode CMap"); while (pst->getToken(tok1, sizeof(tok1), &n1)) { if (!strcmp(tok1, "endcidrange")) { break; } if (!pst->getToken(tok2, sizeof(tok2), &n2) || !strcmp(tok2, "endcidrange") || !pst->getToken(tok3, sizeof(tok3), &n3) || !strcmp(tok3, "endcidrange")) { error(errSyntaxWarning, -1, "Illegal entry in cidrange block in ToUnicode CMap"); break; } if (!(tok1[0] == '<' && tok1[n1 - 1] == '>' && tok2[0] == '<' && tok2[n2 - 1] == '>')) { error(errSyntaxWarning, -1, "Illegal entry in cidrange block in ToUnicode CMap"); continue; } tok1[n1 - 1] = tok2[n2 - 1] = '\0'; if (!parseHex(tok1 + 1, n1 - 2, &code1) || !parseHex(tok2 + 1, n2 - 2, &code2)) { error(errSyntaxWarning, -1, "Illegal entry in cidrange block in ToUnicode CMap"); continue; } if (code1 > maxCode || code2 > maxCode) { error(errSyntaxWarning, -1, "Invalid entry in cidrange block in ToUnicode CMap"); if (code2 > maxCode) { code2 = maxCode; } } for (i = atoi(tok3); code1 <= code2; ++code1, ++i) { addMappingInt(code1, i); ok = true; } } pst->getToken(tok1, sizeof(tok1), &n1); } else { strcpy(tok1, tok2); } } delete pst; return ok; } void CharCodeToUnicode::addMapping(CharCode code, char *uStr, int n, int offset) { CharCode oldLen, i; Unicode u; int j; 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; } if (unlikely(code >= mapLen)) { 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; } } } if (n <= 4) { if (!parseHex(uStr, n, &u)) { error(errSyntaxWarning, -1, "Illegal entry in ToUnicode CMap"); return; } map[code] = u + offset; if (!UnicodeIsValid(map[code])) { map[code] = 0xfffd; } } else { map[code] = 0; int utf16Len = n / 4; std::vector utf16(utf16Len); utf16.resize(utf16Len); for (j = 0; j < utf16Len; ++j) { if (!parseHex(uStr + j * 4, 4, &utf16[j])) { error(errSyntaxWarning, -1, "Illegal entry in ToUnicode CMap"); return; } } utf16[utf16Len - 1] += offset; sMap.push_back({ code, UTF16toUCS4(utf16.data(), utf16.size()) }); } } 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; } } map[code] = u; } CharCodeToUnicode::CharCodeToUnicode() { map = nullptr; mapLen = 0; refCnt = 1; isIdentity = false; } CharCodeToUnicode::CharCodeToUnicode(const std::optional &tagA) : tag(tagA) { CharCode i; mapLen = 256; map = (Unicode *)gmallocn(mapLen, sizeof(Unicode)); for (i = 0; i < mapLen; ++i) { map[i] = 0; } refCnt = 1; isIdentity = false; } CharCodeToUnicode::CharCodeToUnicode(const std::optional &tagA, Unicode *mapA, CharCode mapLenA, bool copyMap, std::vector &&sMapA) : tag(tagA) { mapLen = mapLenA; if (copyMap) { map = (Unicode *)gmallocn(mapLen, sizeof(Unicode)); memcpy(map, mapA, mapLen * sizeof(Unicode)); } else { map = mapA; } sMap = std::move(sMapA); refCnt = 1; isIdentity = false; } CharCodeToUnicode::~CharCodeToUnicode() { gfree(map); } void CharCodeToUnicode::incRefCnt() { ++refCnt; } void CharCodeToUnicode::decRefCnt() { if (--refCnt == 0) { delete this; } } bool CharCodeToUnicode::match(const GooString *tagA) { return tag && tag == tagA->toStr(); } void CharCodeToUnicode::setMapping(CharCode c, Unicode *u, int len) { size_t i; int j; if (!map || isIdentity) { return; } if (len == 1) { map[c] = u[0]; } else { std::optional> element; for (i = 0; i < sMap.size(); ++i) { if (sMap[i].c == c) { sMap[i].u.clear(); element = std::ref(sMap[i]); break; } } if (!element) { sMap.emplace_back(CharCodeToUnicodeString { c, {} }); element = std::ref(sMap.back()); } map[c] = 0; element->get().c = c; element->get().u.reserve(len); for (j = 0; j < len; ++j) { if (UnicodeIsValid(u[j])) { element->get().u.push_back(u[j]); } else { element->get().u.push_back(0xfffd); } } } } int CharCodeToUnicode::mapToUnicode(CharCode c, Unicode const **u) const { if (isIdentity) { map[0] = (Unicode)c; *u = map; return 1; } if (c >= mapLen) { return 0; } if (map[c]) { *u = &map[c]; return 1; } 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; } int CharCodeToUnicode::mapToCharCode(const Unicode *u, CharCode *c, int usize) const { // look for charcode in map if (usize == 1 || (usize > 1 && !(*u & ~0xff))) { if (isIdentity) { *c = (CharCode)*u; return 1; } for (CharCode i = 0; i < mapLen; i++) { if (map[i] == *u) { *c = i; return 1; } } *c = 'x'; } else { size_t j; // for each entry in the sMap for (const auto &element : sMap) { // if the entry's unicode length isn't the same are usize, the strings // are obviously different if (element.u.size() != size_t(usize)) { continue; } // compare the string char by char for (j = 0; j < element.u.size(); j++) { if (element.u[j] != u[j]) { break; } } // we have the same strings if (j == element.u.size()) { *c = element.c; return 1; } } } return 0; } //------------------------------------------------------------------------ CharCodeToUnicodeCache::CharCodeToUnicodeCache(int sizeA) { int i; size = sizeA; cache = (CharCodeToUnicode **)gmallocn(size, sizeof(CharCodeToUnicode *)); for (i = 0; i < size; ++i) { cache[i] = nullptr; } } CharCodeToUnicodeCache::~CharCodeToUnicodeCache() { 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]; } cache[0] = ctu; ctu->incRefCnt(); return ctu; } } return nullptr; } void CharCodeToUnicodeCache::add(CharCodeToUnicode *ctu) { int i; if (cache[size - 1]) { cache[size - 1]->decRefCnt(); } for (i = size - 1; i >= 1; --i) { cache[i] = cache[i - 1]; } cache[0] = ctu; ctu->incRefCnt(); } poppler-24.02.0/poppler/CharCodeToUnicode.h000066400000000000000000000124001455701731300204570ustar00rootroot00000000000000//======================================================================== // // CharCodeToUnicode.h // // Mapping from character codes to Unicode. // // Copyright 2001-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) 2007 Julien Rebetez // Copyright (C) 2007 Koji Otani // Copyright (C) 2008, 2011, 2012, 2018, 2019, 2021, 2022 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 // //======================================================================== #ifndef CHARCODETOUNICODE_H #define CHARCODETOUNICODE_H #include #include #include #include "poppler-config.h" #include "CharTypes.h" class GooString; //------------------------------------------------------------------------ class CharCodeToUnicode { friend class UnicodeToCharCode; public: // Create an identity mapping (Unicode = CharCode). static CharCodeToUnicode *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); // 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); // 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); // Parse a ToUnicode CMap for an 8- or 16-bit font, merging it into // . void mergeCMap(const GooString *buf, int nBits); ~CharCodeToUnicode(); 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); // Set the mapping for . void setMapping(CharCode c, Unicode *u, int len); // Map a CharCode to Unicode. Returns a pointer in u to internal storage // so never store the pointers it returns, just the data, otherwise // your pointed values might get changed by future calls int mapToUnicode(CharCode c, Unicode const **u) const; // 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; } private: struct CharCodeToUnicodeString { CharCode c; std::vector u; }; 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, std::vector &&sMapA); const std::optional tag; Unicode *map; CharCode mapLen; std::vector sMap; std::atomic_int refCnt; bool isIdentity; }; //------------------------------------------------------------------------ class CharCodeToUnicodeCache { public: explicit CharCodeToUnicodeCache(int sizeA); ~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); // Insert into the cache, in the most-recently-used position. void add(CharCodeToUnicode *ctu); private: CharCodeToUnicode **cache; int size; }; #endif poppler-24.02.0/poppler/CharTypes.h000066400000000000000000000010161455701731300171000ustar00rootroot00000000000000//======================================================================== // // CharTypes.h // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #ifndef CHARTYPES_H #define CHARTYPES_H // Unicode character. typedef unsigned int Unicode; // Character ID for CID character collections. typedef unsigned int CID; // This is large enough to hold any of the following: // - 8-bit char code // - 16-bit CID // - Unicode typedef unsigned int CharCode; #endif poppler-24.02.0/poppler/CourierBoldObliqueWidths.gperf000066400000000000000000000101471455701731300227740ustar00rootroot00000000000000%{ #include #include "BuiltinFontWidth.h" %} %language=ANSI-C %define initializer-suffix ,0 %define lookup-function-name CourierBoldObliqueWidthsLookup %struct-type %omit-struct-type %readonly-tables struct BuiltinFontWidth %% #### Ntilde, 600 rcaron, 600 kcommaaccent, 600 Ncommaaccent, 600 Zacute, 600 comma, 600 cedilla, 600 plusminus, 600 circumflex, 600 dotaccent, 600 edotaccent, 600 asciitilde, 600 colon, 600 onehalf, 600 dollar, 600 Lcaron, 600 ntilde, 600 Aogonek, 600 ncommaaccent, 600 minus, 600 Iogonek, 600 zacute, 600 yen, 600 space, 600 Omacron, 600 questiondown, 600 emdash, 600 Agrave, 600 three, 600 numbersign, 600 lcaron, 600 A, 600 B, 600 C, 600 aogonek, 600 D, 600 E, 600 onequarter, 600 F, 600 G, 600 H, 600 I, 600 J, 600 K, 600 iogonek, 600 backslash, 600 L, 600 periodcentered, 600 M, 600 N, 600 omacron, 600 Tcommaaccent, 600 O, 600 P, 600 Q, 600 Uhungarumlaut, 600 R, 600 Aacute, 600 caron, 600 S, 600 T, 600 U, 600 agrave, 600 V, 600 W, 600 X, 600 question, 600 equal, 600 Y, 600 Z, 600 four, 600 a, 600 Gcommaaccent, 600 b, 600 c, 600 d, 600 e, 600 f, 600 g, 600 bullet, 600 h, 600 i, 600 Oslash, 600 dagger, 600 j, 600 k, 600 l, 600 m, 600 n, 600 tcommaaccent, 600 o, 600 ordfeminine, 600 ring, 600 p, 600 q, 600 uhungarumlaut, 600 r, 600 twosuperior, 600 aacute, 600 s, 600 OE, 600 t, 600 divide, 600 u, 600 Ccaron, 600 v, 600 w, 600 x, 600 y, 600 z, 600 Gbreve, 600 commaaccent, 600 hungarumlaut, 600 Idotaccent, 600 Nacute, 600 quotedbl, 600 gcommaaccent, 600 mu, 600 greaterequal, 600 Scaron, 600 Lslash, 600 semicolon, 600 oslash, 600 lessequal, 600 lozenge, 600 parenright, 600 ccaron, 600 Ecircumflex, 600 gbreve, 600 trademark, 600 daggerdbl, 600 nacute, 600 macron, 600 Otilde, 600 Emacron, 600 ellipsis, 600 scaron, 600 AE, 600 Ucircumflex, 600 lslash, 600 quotedblleft, 600 guilsinglright, 600 hyphen, 600 quotesingle, 600 eight, 600 exclamdown, 600 endash, 600 oe, 600 Abreve, 600 Umacron, 600 ecircumflex, 600 Adieresis, 600 copyright, 600 Egrave, 600 slash, 600 Edieresis, 600 otilde, 600 Idieresis, 600 parenleft, 600 one, 600 emacron, 600 Odieresis, 600 ucircumflex, 600 bracketleft, 600 Ugrave, 600 quoteright, 600 Udieresis, 600 perthousand, 600 Ydieresis, 600 umacron, 600 abreve, 600 Eacute, 600 adieresis, 600 egrave, 600 edieresis, 600 idieresis, 600 Eth, 600 ae, 600 asterisk, 600 odieresis, 600 Uacute, 600 ugrave, 600 nine, 600 five, 600 udieresis, 600 Zcaron, 600 Scommaaccent, 600 threequarters, 600 guillemotright, 600 Ccedilla, 600 ydieresis, 600 tilde, 600 at, 600 eacute, 600 underscore, 600 Euro, 600 Dcroat, 600 multiply, 600 zero, 600 eth, 600 Scedilla, 600 Ograve, 600 Racute, 600 partialdiff, 600 uacute, 600 braceleft, 600 Thorn, 600 zcaron, 600 scommaaccent, 600 ccedilla, 600 Dcaron, 600 dcroat, 600 Ocircumflex, 600 Oacute, 600 scedilla, 600 ogonek, 600 ograve, 600 racute, 600 Tcaron, 600 Eogonek, 600 thorn, 600 degree, 600 registered, 600 radical, 600 Aring, 600 percent, 600 six, 600 paragraph, 600 dcaron, 600 Uogonek, 600 two, 600 summation, 600 Igrave, 600 Lacute, 600 ocircumflex, 600 oacute, 600 Uring, 600 Lcommaaccent, 600 tcaron, 600 eogonek, 600 Delta, 600 Ohungarumlaut, 600 asciicircum, 600 aring, 600 grave, 600 uogonek, 600 bracketright, 600 Iacute, 600 ampersand, 600 igrave, 600 lacute, 600 Ncaron, 600 plus, 600 uring, 600 quotesinglbase, 600 lcommaaccent, 600 Yacute, 600 ohungarumlaut, 600 threesuperior, 600 acute, 600 section, 600 dieresis, 600 iacute, 600 quotedblbase, 600 ncaron, 600 florin, 600 yacute, 600 Rcommaaccent, 600 fi, 600 fl, 600 Acircumflex, 600 Cacute, 600 Icircumflex, 600 guillemotleft, 600 germandbls, 600 Amacron, 600 seven, 600 Sacute, 600 ordmasculine, 600 dotlessi, 600 sterling, 600 notequal, 600 Imacron, 600 rcommaaccent, 600 Zdotaccent, 600 acircumflex, 600 cacute, 600 Ecaron, 600 icircumflex, 600 braceright, 600 quotedblright, 600 amacron, 600 sacute, 600 imacron, 600 cent, 600 currency, 600 logicalnot, 600 zdotaccent, 600 Atilde, 600 breve, 600 bar, 600 fraction, 600 less, 600 ecaron, 600 guilsinglleft, 600 exclam, 600 period, 600 Rcaron, 600 Kcommaaccent, 600 greater, 600 atilde, 600 brokenbar, 600 quoteleft, 600 Edotaccent, 600 onesuperior, 600 #### %% poppler-24.02.0/poppler/CourierBoldObliqueWidths.pregenerated.c000066400000000000000000002667461455701731300246010ustar00rootroot00000000000000/* ANSI-C code produced by gperf version 3.1 */ /* Command-line: gperf poppler/CourierBoldObliqueWidths.gperf */ /* Computed positions: -k'1-2,5,$' */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) && ('-' == 45) && ('.' == 46) && ('/' == 47) \ && ('0' == 48) && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) && ('=' == 61) && ('>' == 62) \ && ('?' == 63) && ('A' == 65) && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) && ('N' == 78) \ && ('O' == 79) && ('P' == 80) && ('Q' == 81) && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) && ('k' == 107) && ('l' == 108) \ && ('m' == 109) && ('n' == 110) && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) /* The character set is not based on ISO-646. */ # error "gperf generated tables don't work with this execution character set. Please report a bug to ." #endif #line 1 "poppler/CourierBoldObliqueWidths.gperf" #include #include "BuiltinFontWidth.h" #define TOTAL_KEYWORDS 315 #define MIN_WORD_LENGTH 1 #define MAX_WORD_LENGTH 14 #define MIN_HASH_VALUE 1 #define MAX_HASH_VALUE 1041 /* maximum key range = 1041, duplicates = 0 */ #ifdef __GNUC__ __inline #else # ifdef __cplusplus inline # endif #endif static unsigned int hash(register const char *str, register size_t len) { static const unsigned short asso_values[] = { 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 270, 415, 28, 8, 150, 390, 290, 375, 370, 335, 5, 455, 330, 405, 355, 325, 310, 3, 320, 160, 240, 225, 145, 70, 410, 460, 1042, 1042, 1042, 1042, 1042, 1042, 20, 345, 30, 115, 0, 395, 140, 165, 135, 35, 380, 170, 130, 15, 45, 215, 260, 100, 65, 10, 155, 400, 300, 305, 280, 315, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042 }; register unsigned int hval = len; switch (hval) { default: hval += asso_values[(unsigned char)str[4]]; /*FALLTHROUGH*/ case 4: case 3: case 2: hval += asso_values[(unsigned char)str[1]]; /*FALLTHROUGH*/ case 1: hval += asso_values[(unsigned char)str[0]]; break; } return hval + asso_values[(unsigned char)str[len - 1]]; } const struct BuiltinFontWidth *CourierBoldObliqueWidthsLookup(register const char *str, register size_t len) { static const struct BuiltinFontWidth wordlist[] = { { "", 0 }, #line 90 "poppler/CourierBoldObliqueWidths.gperf" { "e", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 70 "poppler/CourierBoldObliqueWidths.gperf" { "R", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 57 "poppler/CourierBoldObliqueWidths.gperf" { "K", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 49 "poppler/CourierBoldObliqueWidths.gperf" { "D", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 115 "poppler/CourierBoldObliqueWidths.gperf" { "t", 600 }, #line 191 "poppler/CourierBoldObliqueWidths.gperf" { "ae", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 102 "poppler/CourierBoldObliqueWidths.gperf" { "n", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 207 "poppler/CourierBoldObliqueWidths.gperf" { "eacute", 600 }, { "", 0 }, { "", 0 }, #line 216 "poppler/CourierBoldObliqueWidths.gperf" { "Racute", 600 }, { "", 0 }, #line 85 "poppler/CourierBoldObliqueWidths.gperf" { "a", 600 }, #line 206 "poppler/CourierBoldObliqueWidths.gperf" { "at", 600 }, { "", 0 }, #line 308 "poppler/CourierBoldObliqueWidths.gperf" { "cent", 600 }, { "", 0 }, { "", 0 }, #line 161 "poppler/CourierBoldObliqueWidths.gperf" { "oe", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 145 "poppler/CourierBoldObliqueWidths.gperf" { "nacute", 600 }, { "", 0 }, #line 254 "poppler/CourierBoldObliqueWidths.gperf" { "Delta", 600 }, { "", 0 }, #line 273 "poppler/CourierBoldObliqueWidths.gperf" { "acute", 600 }, #line 112 "poppler/CourierBoldObliqueWidths.gperf" { "aacute", 600 }, #line 47 "poppler/CourierBoldObliqueWidths.gperf" { "C", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 88 "poppler/CourierBoldObliqueWidths.gperf" { "c", 600 }, { "", 0 }, #line 173 "poppler/CourierBoldObliqueWidths.gperf" { "one", 600 }, #line 285 "poppler/CourierBoldObliqueWidths.gperf" { "Cacute", 600 }, { "", 0 }, #line 300 "poppler/CourierBoldObliqueWidths.gperf" { "cacute", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 98 "poppler/CourierBoldObliqueWidths.gperf" { "j", 600 }, { "", 0 }, { "", 0 }, #line 210 "poppler/CourierBoldObliqueWidths.gperf" { "Dcroat", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 249 "poppler/CourierBoldObliqueWidths.gperf" { "oacute", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 72 "poppler/CourierBoldObliqueWidths.gperf" { "caron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 104 "poppler/CourierBoldObliqueWidths.gperf" { "o", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 317 "poppler/CourierBoldObliqueWidths.gperf" { "ecaron", 600 }, { "", 0 }, { "", 0 }, #line 321 "poppler/CourierBoldObliqueWidths.gperf" { "Rcaron", 600 }, #line 290 "poppler/CourierBoldObliqueWidths.gperf" { "seven", 600 }, #line 306 "poppler/CourierBoldObliqueWidths.gperf" { "sacute", 600 }, { "", 0 }, { "", 0 }, #line 224 "poppler/CourierBoldObliqueWidths.gperf" { "Dcaron", 600 }, { "", 0 }, #line 252 "poppler/CourierBoldObliqueWidths.gperf" { "tcaron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 26 "poppler/CourierBoldObliqueWidths.gperf" { "colon", 600 }, #line 278 "poppler/CourierBoldObliqueWidths.gperf" { "ncaron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 125 "poppler/CourierBoldObliqueWidths.gperf" { "commaaccent", 600 }, { "", 0 }, { "", 0 }, #line 135 "poppler/CourierBoldObliqueWidths.gperf" { "semicolon", 600 }, #line 19 "poppler/CourierBoldObliqueWidths.gperf" { "comma", 600 }, #line 235 "poppler/CourierBoldObliqueWidths.gperf" { "degree", 600 }, { "", 0 }, { "", 0 }, #line 118 "poppler/CourierBoldObliqueWidths.gperf" { "Ccaron", 600 }, { "", 0 }, #line 140 "poppler/CourierBoldObliqueWidths.gperf" { "ccaron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 113 "poppler/CourierBoldObliqueWidths.gperf" { "s", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 231 "poppler/CourierBoldObliqueWidths.gperf" { "racute", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 79 "poppler/CourierBoldObliqueWidths.gperf" { "X", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 30 "poppler/CourierBoldObliqueWidths.gperf" { "ntilde", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 205 "poppler/CourierBoldObliqueWidths.gperf" { "tilde", 600 }, #line 324 "poppler/CourierBoldObliqueWidths.gperf" { "atilde", 600 }, { "", 0 }, { "", 0 }, #line 196 "poppler/CourierBoldObliqueWidths.gperf" { "nine", 600 }, #line 24 "poppler/CourierBoldObliqueWidths.gperf" { "edotaccent", 600 }, #line 105 "poppler/CourierBoldObliqueWidths.gperf" { "ordfeminine", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 158 "poppler/CourierBoldObliqueWidths.gperf" { "eight", 600 }, #line 150 "poppler/CourierBoldObliqueWidths.gperf" { "scaron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 276 "poppler/CourierBoldObliqueWidths.gperf" { "iacute", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 170 "poppler/CourierBoldObliqueWidths.gperf" { "otilde", 600 }, #line 292 "poppler/CourierBoldObliqueWidths.gperf" { "ordmasculine", 600 }, #line 213 "poppler/CourierBoldObliqueWidths.gperf" { "eth", 600 }, { "", 0 }, #line 42 "poppler/CourierBoldObliqueWidths.gperf" { "three", 600 }, #line 225 "poppler/CourierBoldObliqueWidths.gperf" { "dcroat", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 281 "poppler/CourierBoldObliqueWidths.gperf" { "Rcommaaccent", 600 }, #line 185 "poppler/CourierBoldObliqueWidths.gperf" { "Eacute", 600 }, #line 322 "poppler/CourierBoldObliqueWidths.gperf" { "Kcommaaccent", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 218 "poppler/CourierBoldObliqueWidths.gperf" { "uacute", 600 }, #line 103 "poppler/CourierBoldObliqueWidths.gperf" { "tcommaaccent", 600 }, { "", 0 }, #line 166 "poppler/CourierBoldObliqueWidths.gperf" { "copyright", 600 }, #line 43 "poppler/CourierBoldObliqueWidths.gperf" { "numbersign", 600 }, #line 15 "poppler/CourierBoldObliqueWidths.gperf" { "rcaron", 600 }, #line 32 "poppler/CourierBoldObliqueWidths.gperf" { "ncommaaccent", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 110 "poppler/CourierBoldObliqueWidths.gperf" { "r", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 264 "poppler/CourierBoldObliqueWidths.gperf" { "lacute", 600 }, { "", 0 }, { "", 0 }, #line 23 "poppler/CourierBoldObliqueWidths.gperf" { "dotaccent", 600 }, #line 234 "poppler/CourierBoldObliqueWidths.gperf" { "thorn", 600 }, #line 242 "poppler/CourierBoldObliqueWidths.gperf" { "dcaron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 146 "poppler/CourierBoldObliqueWidths.gperf" { "macron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 203 "poppler/CourierBoldObliqueWidths.gperf" { "Ccedilla", 600 }, #line 274 "poppler/CourierBoldObliqueWidths.gperf" { "section", 600 }, #line 223 "poppler/CourierBoldObliqueWidths.gperf" { "ccedilla", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 20 "poppler/CourierBoldObliqueWidths.gperf" { "cedilla", 600 }, { "", 0 }, { "", 0 }, #line 25 "poppler/CourierBoldObliqueWidths.gperf" { "asciitilde", 600 }, #line 89 "poppler/CourierBoldObliqueWidths.gperf" { "d", 600 }, #line 239 "poppler/CourierBoldObliqueWidths.gperf" { "percent", 600 }, { "", 0 }, { "", 0 }, #line 288 "poppler/CourierBoldObliqueWidths.gperf" { "germandbls", 600 }, { "", 0 }, #line 138 "poppler/CourierBoldObliqueWidths.gperf" { "lozenge", 600 }, { "", 0 }, #line 316 "poppler/CourierBoldObliqueWidths.gperf" { "less", 600 }, { "", 0 }, #line 97 "poppler/CourierBoldObliqueWidths.gperf" { "dagger", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 258 "poppler/CourierBoldObliqueWidths.gperf" { "grave", 600 }, #line 301 "poppler/CourierBoldObliqueWidths.gperf" { "Ecaron", 600 }, #line 222 "poppler/CourierBoldObliqueWidths.gperf" { "scommaaccent", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 160 "poppler/CourierBoldObliqueWidths.gperf" { "endash", 600 }, #line 174 "poppler/CourierBoldObliqueWidths.gperf" { "emacron", 600 }, #line 201 "poppler/CourierBoldObliqueWidths.gperf" { "threequarters", 600 }, { "", 0 }, { "", 0 }, #line 232 "poppler/CourierBoldObliqueWidths.gperf" { "Tcaron", 600 }, { "", 0 }, #line 228 "poppler/CourierBoldObliqueWidths.gperf" { "scedilla", 600 }, { "", 0 }, { "", 0 }, #line 101 "poppler/CourierBoldObliqueWidths.gperf" { "m", 600 }, { "", 0 }, { "", 0 }, #line 245 "poppler/CourierBoldObliqueWidths.gperf" { "summation", 600 }, #line 310 "poppler/CourierBoldObliqueWidths.gperf" { "logicalnot", 600 }, #line 44 "poppler/CourierBoldObliqueWidths.gperf" { "lcaron", 600 }, { "", 0 }, { "", 0 }, #line 172 "poppler/CourierBoldObliqueWidths.gperf" { "parenleft", 600 }, #line 139 "poppler/CourierBoldObliqueWidths.gperf" { "parenright", 600 }, #line 95 "poppler/CourierBoldObliqueWidths.gperf" { "i", 600 }, #line 305 "poppler/CourierBoldObliqueWidths.gperf" { "amacron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 194 "poppler/CourierBoldObliqueWidths.gperf" { "Uacute", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 208 "poppler/CourierBoldObliqueWidths.gperf" { "underscore", 600 }, #line 92 "poppler/CourierBoldObliqueWidths.gperf" { "g", 600 }, #line 297 "poppler/CourierBoldObliqueWidths.gperf" { "rcommaaccent", 600 }, { "", 0 }, { "", 0 }, #line 37 "poppler/CourierBoldObliqueWidths.gperf" { "space", 600 }, #line 28 "poppler/CourierBoldObliqueWidths.gperf" { "dollar", 600 }, { "", 0 }, #line 272 "poppler/CourierBoldObliqueWidths.gperf" { "threesuperior", 600 }, #line 188 "poppler/CourierBoldObliqueWidths.gperf" { "edieresis", 600 }, #line 236 "poppler/CourierBoldObliqueWidths.gperf" { "registered", 600 }, #line 78 "poppler/CourierBoldObliqueWidths.gperf" { "W", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 64 "poppler/CourierBoldObliqueWidths.gperf" { "omacron", 600 }, #line 36 "poppler/CourierBoldObliqueWidths.gperf" { "yen", 600 }, { "", 0 }, { "", 0 }, #line 50 "poppler/CourierBoldObliqueWidths.gperf" { "E", 600 }, { "", 0 }, #line 293 "poppler/CourierBoldObliqueWidths.gperf" { "dotlessi", 600 }, { "", 0 }, #line 327 "poppler/CourierBoldObliqueWidths.gperf" { "Edotaccent", 600 }, #line 71 "poppler/CourierBoldObliqueWidths.gperf" { "Aacute", 600 }, { "", 0 }, { "", 0 }, #line 186 "poppler/CourierBoldObliqueWidths.gperf" { "adieresis", 600 }, { "", 0 }, #line 117 "poppler/CourierBoldObliqueWidths.gperf" { "u", 600 }, { "", 0 }, { "", 0 }, #line 144 "poppler/CourierBoldObliqueWidths.gperf" { "daggerdbl", 600 }, { "", 0 }, #line 280 "poppler/CourierBoldObliqueWidths.gperf" { "yacute", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 74 "poppler/CourierBoldObliqueWidths.gperf" { "T", 600 }, #line 130 "poppler/CourierBoldObliqueWidths.gperf" { "gcommaaccent", 600 }, #line 275 "poppler/CourierBoldObliqueWidths.gperf" { "dieresis", 600 }, { "", 0 }, #line 51 "poppler/CourierBoldObliqueWidths.gperf" { "onequarter", 600 }, #line 328 "poppler/CourierBoldObliqueWidths.gperf" { "onesuperior", 600 }, #line 237 "poppler/CourierBoldObliqueWidths.gperf" { "radical", 600 }, #line 190 "poppler/CourierBoldObliqueWidths.gperf" { "Eth", 600 }, { "", 0 }, { "", 0 }, #line 94 "poppler/CourierBoldObliqueWidths.gperf" { "h", 600 }, { "", 0 }, { "", 0 }, #line 193 "poppler/CourierBoldObliqueWidths.gperf" { "odieresis", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 100 "poppler/CourierBoldObliqueWidths.gperf" { "l", 600 }, #line 65 "poppler/CourierBoldObliqueWidths.gperf" { "Tcommaaccent", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 136 "poppler/CourierBoldObliqueWidths.gperf" { "oslash", 600 }, { "", 0 }, { "", 0 }, #line 137 "poppler/CourierBoldObliqueWidths.gperf" { "lessequal", 600 }, #line 159 "poppler/CourierBoldObliqueWidths.gperf" { "exclamdown", 600 }, #line 35 "poppler/CourierBoldObliqueWidths.gperf" { "zacute", 600 }, #line 269 "poppler/CourierBoldObliqueWidths.gperf" { "lcommaaccent", 600 }, { "", 0 }, #line 209 "poppler/CourierBoldObliqueWidths.gperf" { "Euro", 600 }, { "", 0 }, #line 291 "poppler/CourierBoldObliqueWidths.gperf" { "Sacute", 600 }, #line 323 "poppler/CourierBoldObliqueWidths.gperf" { "greater", 600 }, #line 244 "poppler/CourierBoldObliqueWidths.gperf" { "two", 600 }, { "", 0 }, #line 220 "poppler/CourierBoldObliqueWidths.gperf" { "Thorn", 600 }, #line 256 "poppler/CourierBoldObliqueWidths.gperf" { "asciicircum", 600 }, #line 126 "poppler/CourierBoldObliqueWidths.gperf" { "hungarumlaut", 600 }, { "", 0 }, #line 212 "poppler/CourierBoldObliqueWidths.gperf" { "zero", 600 }, { "", 0 }, #line 40 "poppler/CourierBoldObliqueWidths.gperf" { "emdash", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 116 "poppler/CourierBoldObliqueWidths.gperf" { "divide", 600 }, { "", 0 }, #line 271 "poppler/CourierBoldObliqueWidths.gperf" { "ohungarumlaut", 600 }, #line 262 "poppler/CourierBoldObliqueWidths.gperf" { "ampersand", 600 }, { "", 0 }, #line 164 "poppler/CourierBoldObliqueWidths.gperf" { "ecircumflex", 600 }, { "", 0 }, { "", 0 }, #line 106 "poppler/CourierBoldObliqueWidths.gperf" { "ring", 600 }, { "", 0 }, #line 320 "poppler/CourierBoldObliqueWidths.gperf" { "period", 600 }, { "", 0 }, #line 318 "poppler/CourierBoldObliqueWidths.gperf" { "guilsinglleft", 600 }, #line 155 "poppler/CourierBoldObliqueWidths.gperf" { "guilsinglright", 600 }, { "", 0 }, { "", 0 }, #line 307 "poppler/CourierBoldObliqueWidths.gperf" { "imacron", 600 }, { "", 0 }, #line 61 "poppler/CourierBoldObliqueWidths.gperf" { "periodcentered", 600 }, { "", 0 }, #line 227 "poppler/CourierBoldObliqueWidths.gperf" { "Oacute", 600 }, { "", 0 }, #line 294 "poppler/CourierBoldObliqueWidths.gperf" { "sterling", 600 }, { "", 0 }, { "", 0 }, #line 299 "poppler/CourierBoldObliqueWidths.gperf" { "acircumflex", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 33 "poppler/CourierBoldObliqueWidths.gperf" { "minus", 600 }, #line 312 "poppler/CourierBoldObliqueWidths.gperf" { "Atilde", 600 }, #line 148 "poppler/CourierBoldObliqueWidths.gperf" { "Emacron", 600 }, { "", 0 }, { "", 0 }, #line 257 "poppler/CourierBoldObliqueWidths.gperf" { "aring", 600 }, #line 261 "poppler/CourierBoldObliqueWidths.gperf" { "Iacute", 600 }, #line 183 "poppler/CourierBoldObliqueWidths.gperf" { "umacron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 221 "poppler/CourierBoldObliqueWidths.gperf" { "zcaron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 133 "poppler/CourierBoldObliqueWidths.gperf" { "Scaron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 248 "poppler/CourierBoldObliqueWidths.gperf" { "ocircumflex", 600 }, { "", 0 }, { "", 0 }, #line 189 "poppler/CourierBoldObliqueWidths.gperf" { "idieresis", 600 }, { "", 0 }, #line 157 "poppler/CourierBoldObliqueWidths.gperf" { "quotesingle", 600 }, #line 277 "poppler/CourierBoldObliqueWidths.gperf" { "quotedblbase", 600 }, { "", 0 }, #line 268 "poppler/CourierBoldObliqueWidths.gperf" { "quotesinglbase", 600 }, { "", 0 }, #line 107 "poppler/CourierBoldObliqueWidths.gperf" { "p", 600 }, #line 132 "poppler/CourierBoldObliqueWidths.gperf" { "greaterequal", 600 }, { "", 0 }, #line 326 "poppler/CourierBoldObliqueWidths.gperf" { "quoteleft", 600 }, #line 179 "poppler/CourierBoldObliqueWidths.gperf" { "quoteright", 600 }, { "", 0 }, #line 154 "poppler/CourierBoldObliqueWidths.gperf" { "quotedblleft", 600 }, #line 304 "poppler/CourierBoldObliqueWidths.gperf" { "quotedblright", 600 }, #line 169 "poppler/CourierBoldObliqueWidths.gperf" { "Edieresis", 600 }, { "", 0 }, #line 128 "poppler/CourierBoldObliqueWidths.gperf" { "Nacute", 600 }, #line 131 "poppler/CourierBoldObliqueWidths.gperf" { "mu", 600 }, { "", 0 }, #line 198 "poppler/CourierBoldObliqueWidths.gperf" { "udieresis", 600 }, { "", 0 }, #line 270 "poppler/CourierBoldObliqueWidths.gperf" { "Yacute", 600 }, #line 253 "poppler/CourierBoldObliqueWidths.gperf" { "eogonek", 600 }, #line 80 "poppler/CourierBoldObliqueWidths.gperf" { "question", 600 }, { "", 0 }, #line 313 "poppler/CourierBoldObliqueWidths.gperf" { "breve", 600 }, #line 77 "poppler/CourierBoldObliqueWidths.gperf" { "V", 600 }, #line 39 "poppler/CourierBoldObliqueWidths.gperf" { "questiondown", 600 }, { "", 0 }, #line 266 "poppler/CourierBoldObliqueWidths.gperf" { "plus", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 149 "poppler/CourierBoldObliqueWidths.gperf" { "ellipsis", 600 }, { "", 0 }, { "", 0 }, #line 319 "poppler/CourierBoldObliqueWidths.gperf" { "exclam", 600 }, { "", 0 }, { "", 0 }, #line 219 "poppler/CourierBoldObliqueWidths.gperf" { "braceleft", 600 }, #line 303 "poppler/CourierBoldObliqueWidths.gperf" { "braceright", 600 }, #line 156 "poppler/CourierBoldObliqueWidths.gperf" { "hyphen", 600 }, #line 48 "poppler/CourierBoldObliqueWidths.gperf" { "aogonek", 600 }, #line 314 "poppler/CourierBoldObliqueWidths.gperf" { "bar", 600 }, { "", 0 }, #line 311 "poppler/CourierBoldObliqueWidths.gperf" { "zdotaccent", 600 }, #line 153 "poppler/CourierBoldObliqueWidths.gperf" { "lslash", 600 }, #line 86 "poppler/CourierBoldObliqueWidths.gperf" { "Gcommaaccent", 600 }, #line 309 "poppler/CourierBoldObliqueWidths.gperf" { "currency", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 75 "poppler/CourierBoldObliqueWidths.gperf" { "U", 600 }, #line 27 "poppler/CourierBoldObliqueWidths.gperf" { "onehalf", 600 }, #line 109 "poppler/CourierBoldObliqueWidths.gperf" { "uhungarumlaut", 600 }, { "", 0 }, { "", 0 }, #line 147 "poppler/CourierBoldObliqueWidths.gperf" { "Otilde", 600 }, { "", 0 }, #line 287 "poppler/CourierBoldObliqueWidths.gperf" { "guillemotleft", 600 }, #line 202 "poppler/CourierBoldObliqueWidths.gperf" { "guillemotright", 600 }, { "", 0 }, #line 247 "poppler/CourierBoldObliqueWidths.gperf" { "Lacute", 600 }, #line 163 "poppler/CourierBoldObliqueWidths.gperf" { "Umacron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 18 "poppler/CourierBoldObliqueWidths.gperf" { "Zacute", 600 }, { "", 0 }, #line 295 "poppler/CourierBoldObliqueWidths.gperf" { "notequal", 600 }, #line 143 "poppler/CourierBoldObliqueWidths.gperf" { "trademark", 600 }, { "", 0 }, #line 265 "poppler/CourierBoldObliqueWidths.gperf" { "Ncaron", 600 }, #line 200 "poppler/CourierBoldObliqueWidths.gperf" { "Scommaaccent", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 181 "poppler/CourierBoldObliqueWidths.gperf" { "perthousand", 600 }, { "", 0 }, #line 240 "poppler/CourierBoldObliqueWidths.gperf" { "six", 600 }, { "", 0 }, { "", 0 }, #line 302 "poppler/CourierBoldObliqueWidths.gperf" { "icircumflex", 600 }, { "", 0 }, #line 214 "poppler/CourierBoldObliqueWidths.gperf" { "Scedilla", 600 }, { "", 0 }, { "", 0 }, #line 93 "poppler/CourierBoldObliqueWidths.gperf" { "bullet", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 108 "poppler/CourierBoldObliqueWidths.gperf" { "q", 600 }, #line 289 "poppler/CourierBoldObliqueWidths.gperf" { "Amacron", 600 }, { "", 0 }, { "", 0 }, #line 127 "poppler/CourierBoldObliqueWidths.gperf" { "Idotaccent", 600 }, #line 141 "poppler/CourierBoldObliqueWidths.gperf" { "Ecircumflex", 600 }, { "", 0 }, #line 315 "poppler/CourierBoldObliqueWidths.gperf" { "fraction", 600 }, #line 180 "poppler/CourierBoldObliqueWidths.gperf" { "Udieresis", 600 }, { "", 0 }, #line 176 "poppler/CourierBoldObliqueWidths.gperf" { "ucircumflex", 600 }, { "", 0 }, { "", 0 }, #line 197 "poppler/CourierBoldObliqueWidths.gperf" { "five", 600 }, { "", 0 }, #line 14 "poppler/CourierBoldObliqueWidths.gperf" { "Ntilde", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 267 "poppler/CourierBoldObliqueWidths.gperf" { "uring", 600 }, #line 45 "poppler/CourierBoldObliqueWidths.gperf" { "A", 600 }, { "", 0 }, { "", 0 }, #line 84 "poppler/CourierBoldObliqueWidths.gperf" { "four", 600 }, { "", 0 }, #line 187 "poppler/CourierBoldObliqueWidths.gperf" { "egrave", 600 }, { "", 0 }, { "", 0 }, #line 241 "poppler/CourierBoldObliqueWidths.gperf" { "paragraph", 600 }, { "", 0 }, #line 29 "poppler/CourierBoldObliqueWidths.gperf" { "Lcaron", 600 }, { "", 0 }, { "", 0 }, #line 325 "poppler/CourierBoldObliqueWidths.gperf" { "brokenbar", 600 }, { "", 0 }, #line 199 "poppler/CourierBoldObliqueWidths.gperf" { "Zcaron", 600 }, { "", 0 }, { "", 0 }, #line 165 "poppler/CourierBoldObliqueWidths.gperf" { "Adieresis", 600 }, { "", 0 }, #line 122 "poppler/CourierBoldObliqueWidths.gperf" { "y", 600 }, #line 16 "poppler/CourierBoldObliqueWidths.gperf" { "kcommaaccent", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 76 "poppler/CourierBoldObliqueWidths.gperf" { "agrave", 600 }, { "", 0 }, #line 69 "poppler/CourierBoldObliqueWidths.gperf" { "Uhungarumlaut", 600 }, #line 204 "poppler/CourierBoldObliqueWidths.gperf" { "ydieresis", 600 }, #line 168 "poppler/CourierBoldObliqueWidths.gperf" { "slash", 600 }, #line 229 "poppler/CourierBoldObliqueWidths.gperf" { "ogonek", 600 }, #line 151 "poppler/CourierBoldObliqueWidths.gperf" { "AE", 600 }, #line 192 "poppler/CourierBoldObliqueWidths.gperf" { "asterisk", 600 }, { "", 0 }, { "", 0 }, #line 111 "poppler/CourierBoldObliqueWidths.gperf" { "twosuperior", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 53 "poppler/CourierBoldObliqueWidths.gperf" { "G", 600 }, #line 58 "poppler/CourierBoldObliqueWidths.gperf" { "iogonek", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 17 "poppler/CourierBoldObliqueWidths.gperf" { "Ncommaaccent", 600 }, { "", 0 }, #line 21 "poppler/CourierBoldObliqueWidths.gperf" { "plusminus", 600 }, { "", 0 }, #line 230 "poppler/CourierBoldObliqueWidths.gperf" { "ograve", 600 }, { "", 0 }, #line 129 "poppler/CourierBoldObliqueWidths.gperf" { "quotedbl", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 233 "poppler/CourierBoldObliqueWidths.gperf" { "Eogonek", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 120 "poppler/CourierBoldObliqueWidths.gperf" { "w", 600 }, #line 259 "poppler/CourierBoldObliqueWidths.gperf" { "uogonek", 600 }, { "", 0 }, #line 59 "poppler/CourierBoldObliqueWidths.gperf" { "backslash", 600 }, #line 81 "poppler/CourierBoldObliqueWidths.gperf" { "equal", 600 }, { "", 0 }, #line 38 "poppler/CourierBoldObliqueWidths.gperf" { "Omacron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 121 "poppler/CourierBoldObliqueWidths.gperf" { "x", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 298 "poppler/CourierBoldObliqueWidths.gperf" { "Zdotaccent", 600 }, #line 152 "poppler/CourierBoldObliqueWidths.gperf" { "Ucircumflex", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 68 "poppler/CourierBoldObliqueWidths.gperf" { "Q", 600 }, #line 296 "poppler/CourierBoldObliqueWidths.gperf" { "Imacron", 600 }, { "", 0 }, { "", 0 }, #line 250 "poppler/CourierBoldObliqueWidths.gperf" { "Uring", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 123 "poppler/CourierBoldObliqueWidths.gperf" { "z", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 22 "poppler/CourierBoldObliqueWidths.gperf" { "circumflex", 600 }, { "", 0 }, #line 251 "poppler/CourierBoldObliqueWidths.gperf" { "Lcommaaccent", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 73 "poppler/CourierBoldObliqueWidths.gperf" { "S", 600 }, { "", 0 }, { "", 0 }, #line 175 "poppler/CourierBoldObliqueWidths.gperf" { "Odieresis", 600 }, { "", 0 }, #line 284 "poppler/CourierBoldObliqueWidths.gperf" { "Acircumflex", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 67 "poppler/CourierBoldObliqueWidths.gperf" { "P", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 238 "poppler/CourierBoldObliqueWidths.gperf" { "Aring", 600 }, #line 96 "poppler/CourierBoldObliqueWidths.gperf" { "Oslash", 600 }, #line 114 "poppler/CourierBoldObliqueWidths.gperf" { "OE", 600 }, { "", 0 }, #line 171 "poppler/CourierBoldObliqueWidths.gperf" { "Idieresis", 600 }, { "", 0 }, #line 62 "poppler/CourierBoldObliqueWidths.gperf" { "M", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 282 "poppler/CourierBoldObliqueWidths.gperf" { "fi", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 56 "poppler/CourierBoldObliqueWidths.gperf" { "J", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 263 "poppler/CourierBoldObliqueWidths.gperf" { "igrave", 600 }, { "", 0 }, #line 255 "poppler/CourierBoldObliqueWidths.gperf" { "Ohungarumlaut", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 243 "poppler/CourierBoldObliqueWidths.gperf" { "Uogonek", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 87 "poppler/CourierBoldObliqueWidths.gperf" { "b", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 167 "poppler/CourierBoldObliqueWidths.gperf" { "Egrave", 600 }, { "", 0 }, { "", 0 }, #line 182 "poppler/CourierBoldObliqueWidths.gperf" { "Ydieresis", 600 }, { "", 0 }, #line 195 "poppler/CourierBoldObliqueWidths.gperf" { "ugrave", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 211 "poppler/CourierBoldObliqueWidths.gperf" { "multiply", 600 }, { "", 0 }, { "", 0 }, #line 66 "poppler/CourierBoldObliqueWidths.gperf" { "O", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 31 "poppler/CourierBoldObliqueWidths.gperf" { "Aogonek", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 279 "poppler/CourierBoldObliqueWidths.gperf" { "florin", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 226 "poppler/CourierBoldObliqueWidths.gperf" { "Ocircumflex", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 283 "poppler/CourierBoldObliqueWidths.gperf" { "fl", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 55 "poppler/CourierBoldObliqueWidths.gperf" { "I", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 286 "poppler/CourierBoldObliqueWidths.gperf" { "Icircumflex", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 54 "poppler/CourierBoldObliqueWidths.gperf" { "H", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 134 "poppler/CourierBoldObliqueWidths.gperf" { "Lslash", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 99 "poppler/CourierBoldObliqueWidths.gperf" { "k", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 184 "poppler/CourierBoldObliqueWidths.gperf" { "abreve", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 217 "poppler/CourierBoldObliqueWidths.gperf" { "partialdiff", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 52 "poppler/CourierBoldObliqueWidths.gperf" { "F", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 178 "poppler/CourierBoldObliqueWidths.gperf" { "Ugrave", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 91 "poppler/CourierBoldObliqueWidths.gperf" { "f", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 119 "poppler/CourierBoldObliqueWidths.gperf" { "v", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 63 "poppler/CourierBoldObliqueWidths.gperf" { "N", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 41 "poppler/CourierBoldObliqueWidths.gperf" { "Agrave", 600 }, #line 34 "poppler/CourierBoldObliqueWidths.gperf" { "Iogonek", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 82 "poppler/CourierBoldObliqueWidths.gperf" { "Y", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 46 "poppler/CourierBoldObliqueWidths.gperf" { "B", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 177 "poppler/CourierBoldObliqueWidths.gperf" { "bracketleft", 600 }, #line 260 "poppler/CourierBoldObliqueWidths.gperf" { "bracketright", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 142 "poppler/CourierBoldObliqueWidths.gperf" { "gbreve", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 215 "poppler/CourierBoldObliqueWidths.gperf" { "Ograve", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 60 "poppler/CourierBoldObliqueWidths.gperf" { "L", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 246 "poppler/CourierBoldObliqueWidths.gperf" { "Igrave", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 83 "poppler/CourierBoldObliqueWidths.gperf" { "Z", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 162 "poppler/CourierBoldObliqueWidths.gperf" { "Abreve", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 124 "poppler/CourierBoldObliqueWidths.gperf" { "Gbreve", 600 } }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) { register unsigned int key = hash(str, len); if (key <= MAX_HASH_VALUE) { register const char *s = wordlist[key].name; if (*str == *s && !strcmp(str + 1, s + 1)) return &wordlist[key]; } } return 0; } #line 330 "poppler/CourierBoldObliqueWidths.gperf" poppler-24.02.0/poppler/CourierBoldWidths.gperf000066400000000000000000000101401455701731300214440ustar00rootroot00000000000000%{ #include #include "BuiltinFontWidth.h" %} %language=ANSI-C %define initializer-suffix ,0 %define lookup-function-name CourierBoldWidthsLookup %struct-type %omit-struct-type %readonly-tables struct BuiltinFontWidth %% #### Ntilde, 600 rcaron, 600 kcommaaccent, 600 Ncommaaccent, 600 Zacute, 600 comma, 600 cedilla, 600 plusminus, 600 circumflex, 600 dotaccent, 600 edotaccent, 600 asciitilde, 600 colon, 600 onehalf, 600 dollar, 600 Lcaron, 600 ntilde, 600 Aogonek, 600 ncommaaccent, 600 minus, 600 Iogonek, 600 zacute, 600 yen, 600 space, 600 Omacron, 600 questiondown, 600 emdash, 600 Agrave, 600 three, 600 numbersign, 600 lcaron, 600 A, 600 B, 600 C, 600 aogonek, 600 D, 600 E, 600 onequarter, 600 F, 600 G, 600 H, 600 I, 600 J, 600 K, 600 iogonek, 600 backslash, 600 L, 600 periodcentered, 600 M, 600 N, 600 omacron, 600 Tcommaaccent, 600 O, 600 P, 600 Q, 600 Uhungarumlaut, 600 R, 600 Aacute, 600 caron, 600 S, 600 T, 600 U, 600 agrave, 600 V, 600 W, 600 X, 600 question, 600 equal, 600 Y, 600 Z, 600 four, 600 a, 600 Gcommaaccent, 600 b, 600 c, 600 d, 600 e, 600 f, 600 g, 600 bullet, 600 h, 600 i, 600 Oslash, 600 dagger, 600 j, 600 k, 600 l, 600 m, 600 n, 600 tcommaaccent, 600 o, 600 ordfeminine, 600 ring, 600 p, 600 q, 600 uhungarumlaut, 600 r, 600 twosuperior, 600 aacute, 600 s, 600 OE, 600 t, 600 divide, 600 u, 600 Ccaron, 600 v, 600 w, 600 x, 600 y, 600 z, 600 Gbreve, 600 commaaccent, 600 hungarumlaut, 600 Idotaccent, 600 Nacute, 600 quotedbl, 600 gcommaaccent, 600 mu, 600 greaterequal, 600 Scaron, 600 Lslash, 600 semicolon, 600 oslash, 600 lessequal, 600 lozenge, 600 parenright, 600 ccaron, 600 Ecircumflex, 600 gbreve, 600 trademark, 600 daggerdbl, 600 nacute, 600 macron, 600 Otilde, 600 Emacron, 600 ellipsis, 600 scaron, 600 AE, 600 Ucircumflex, 600 lslash, 600 quotedblleft, 600 guilsinglright, 600 hyphen, 600 quotesingle, 600 eight, 600 exclamdown, 600 endash, 600 oe, 600 Abreve, 600 Umacron, 600 ecircumflex, 600 Adieresis, 600 copyright, 600 Egrave, 600 slash, 600 Edieresis, 600 otilde, 600 Idieresis, 600 parenleft, 600 one, 600 emacron, 600 Odieresis, 600 ucircumflex, 600 bracketleft, 600 Ugrave, 600 quoteright, 600 Udieresis, 600 perthousand, 600 Ydieresis, 600 umacron, 600 abreve, 600 Eacute, 600 adieresis, 600 egrave, 600 edieresis, 600 idieresis, 600 Eth, 600 ae, 600 asterisk, 600 odieresis, 600 Uacute, 600 ugrave, 600 nine, 600 five, 600 udieresis, 600 Zcaron, 600 Scommaaccent, 600 threequarters, 600 guillemotright, 600 Ccedilla, 600 ydieresis, 600 tilde, 600 at, 600 eacute, 600 underscore, 600 Euro, 600 Dcroat, 600 multiply, 600 zero, 600 eth, 600 Scedilla, 600 Ograve, 600 Racute, 600 partialdiff, 600 uacute, 600 braceleft, 600 Thorn, 600 zcaron, 600 scommaaccent, 600 ccedilla, 600 Dcaron, 600 dcroat, 600 Ocircumflex, 600 Oacute, 600 scedilla, 600 ogonek, 600 ograve, 600 racute, 600 Tcaron, 600 Eogonek, 600 thorn, 600 degree, 600 registered, 600 radical, 600 Aring, 600 percent, 600 six, 600 paragraph, 600 dcaron, 600 Uogonek, 600 two, 600 summation, 600 Igrave, 600 Lacute, 600 ocircumflex, 600 oacute, 600 Uring, 600 Lcommaaccent, 600 tcaron, 600 eogonek, 600 Delta, 600 Ohungarumlaut, 600 asciicircum, 600 aring, 600 grave, 600 uogonek, 600 bracketright, 600 Iacute, 600 ampersand, 600 igrave, 600 lacute, 600 Ncaron, 600 plus, 600 uring, 600 quotesinglbase, 600 lcommaaccent, 600 Yacute, 600 ohungarumlaut, 600 threesuperior, 600 acute, 600 section, 600 dieresis, 600 iacute, 600 quotedblbase, 600 ncaron, 600 florin, 600 yacute, 600 Rcommaaccent, 600 fi, 600 fl, 600 Acircumflex, 600 Cacute, 600 Icircumflex, 600 guillemotleft, 600 germandbls, 600 Amacron, 600 seven, 600 Sacute, 600 ordmasculine, 600 dotlessi, 600 sterling, 600 notequal, 600 Imacron, 600 rcommaaccent, 600 Zdotaccent, 600 acircumflex, 600 cacute, 600 Ecaron, 600 icircumflex, 600 braceright, 600 quotedblright, 600 amacron, 600 sacute, 600 imacron, 600 cent, 600 currency, 600 logicalnot, 600 zdotaccent, 600 Atilde, 600 breve, 600 bar, 600 fraction, 600 less, 600 ecaron, 600 guilsinglleft, 600 exclam, 600 period, 600 Rcaron, 600 Kcommaaccent, 600 greater, 600 atilde, 600 brokenbar, 600 quoteleft, 600 Edotaccent, 600 onesuperior, 600 #### %% poppler-24.02.0/poppler/CourierBoldWidths.pregenerated.c000066400000000000000000002624551455701731300232510ustar00rootroot00000000000000/* ANSI-C code produced by gperf version 3.1 */ /* Command-line: gperf poppler/CourierBoldWidths.gperf */ /* Computed positions: -k'1-2,5,$' */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) && ('-' == 45) && ('.' == 46) && ('/' == 47) \ && ('0' == 48) && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) && ('=' == 61) && ('>' == 62) \ && ('?' == 63) && ('A' == 65) && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) && ('N' == 78) \ && ('O' == 79) && ('P' == 80) && ('Q' == 81) && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) && ('k' == 107) && ('l' == 108) \ && ('m' == 109) && ('n' == 110) && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) /* The character set is not based on ISO-646. */ # error "gperf generated tables don't work with this execution character set. Please report a bug to ." #endif #line 1 "poppler/CourierBoldWidths.gperf" #include #include "BuiltinFontWidth.h" #define TOTAL_KEYWORDS 315 #define MIN_WORD_LENGTH 1 #define MAX_WORD_LENGTH 14 #define MIN_HASH_VALUE 1 #define MAX_HASH_VALUE 1041 /* maximum key range = 1041, duplicates = 0 */ #ifdef __GNUC__ __inline #else # ifdef __cplusplus inline # endif #endif static unsigned int hash(register const char *str, register size_t len) { static const unsigned short asso_values[] = { 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 270, 415, 28, 8, 150, 390, 290, 375, 370, 335, 5, 455, 330, 405, 355, 325, 310, 3, 320, 160, 240, 225, 145, 70, 410, 460, 1042, 1042, 1042, 1042, 1042, 1042, 20, 345, 30, 115, 0, 395, 140, 165, 135, 35, 380, 170, 130, 15, 45, 215, 260, 100, 65, 10, 155, 400, 300, 305, 280, 315, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042 }; register unsigned int hval = len; switch (hval) { default: hval += asso_values[(unsigned char)str[4]]; /*FALLTHROUGH*/ case 4: case 3: case 2: hval += asso_values[(unsigned char)str[1]]; /*FALLTHROUGH*/ case 1: hval += asso_values[(unsigned char)str[0]]; break; } return hval + asso_values[(unsigned char)str[len - 1]]; } const struct BuiltinFontWidth *CourierBoldWidthsLookup(register const char *str, register size_t len) { static const struct BuiltinFontWidth wordlist[] = { { "", 0 }, #line 90 "poppler/CourierBoldWidths.gperf" { "e", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 70 "poppler/CourierBoldWidths.gperf" { "R", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 57 "poppler/CourierBoldWidths.gperf" { "K", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 49 "poppler/CourierBoldWidths.gperf" { "D", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 115 "poppler/CourierBoldWidths.gperf" { "t", 600 }, #line 191 "poppler/CourierBoldWidths.gperf" { "ae", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 102 "poppler/CourierBoldWidths.gperf" { "n", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 207 "poppler/CourierBoldWidths.gperf" { "eacute", 600 }, { "", 0 }, { "", 0 }, #line 216 "poppler/CourierBoldWidths.gperf" { "Racute", 600 }, { "", 0 }, #line 85 "poppler/CourierBoldWidths.gperf" { "a", 600 }, #line 206 "poppler/CourierBoldWidths.gperf" { "at", 600 }, { "", 0 }, #line 308 "poppler/CourierBoldWidths.gperf" { "cent", 600 }, { "", 0 }, { "", 0 }, #line 161 "poppler/CourierBoldWidths.gperf" { "oe", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 145 "poppler/CourierBoldWidths.gperf" { "nacute", 600 }, { "", 0 }, #line 254 "poppler/CourierBoldWidths.gperf" { "Delta", 600 }, { "", 0 }, #line 273 "poppler/CourierBoldWidths.gperf" { "acute", 600 }, #line 112 "poppler/CourierBoldWidths.gperf" { "aacute", 600 }, #line 47 "poppler/CourierBoldWidths.gperf" { "C", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 88 "poppler/CourierBoldWidths.gperf" { "c", 600 }, { "", 0 }, #line 173 "poppler/CourierBoldWidths.gperf" { "one", 600 }, #line 285 "poppler/CourierBoldWidths.gperf" { "Cacute", 600 }, { "", 0 }, #line 300 "poppler/CourierBoldWidths.gperf" { "cacute", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 98 "poppler/CourierBoldWidths.gperf" { "j", 600 }, { "", 0 }, { "", 0 }, #line 210 "poppler/CourierBoldWidths.gperf" { "Dcroat", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 249 "poppler/CourierBoldWidths.gperf" { "oacute", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 72 "poppler/CourierBoldWidths.gperf" { "caron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 104 "poppler/CourierBoldWidths.gperf" { "o", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 317 "poppler/CourierBoldWidths.gperf" { "ecaron", 600 }, { "", 0 }, { "", 0 }, #line 321 "poppler/CourierBoldWidths.gperf" { "Rcaron", 600 }, #line 290 "poppler/CourierBoldWidths.gperf" { "seven", 600 }, #line 306 "poppler/CourierBoldWidths.gperf" { "sacute", 600 }, { "", 0 }, { "", 0 }, #line 224 "poppler/CourierBoldWidths.gperf" { "Dcaron", 600 }, { "", 0 }, #line 252 "poppler/CourierBoldWidths.gperf" { "tcaron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 26 "poppler/CourierBoldWidths.gperf" { "colon", 600 }, #line 278 "poppler/CourierBoldWidths.gperf" { "ncaron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 125 "poppler/CourierBoldWidths.gperf" { "commaaccent", 600 }, { "", 0 }, { "", 0 }, #line 135 "poppler/CourierBoldWidths.gperf" { "semicolon", 600 }, #line 19 "poppler/CourierBoldWidths.gperf" { "comma", 600 }, #line 235 "poppler/CourierBoldWidths.gperf" { "degree", 600 }, { "", 0 }, { "", 0 }, #line 118 "poppler/CourierBoldWidths.gperf" { "Ccaron", 600 }, { "", 0 }, #line 140 "poppler/CourierBoldWidths.gperf" { "ccaron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 113 "poppler/CourierBoldWidths.gperf" { "s", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 231 "poppler/CourierBoldWidths.gperf" { "racute", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 79 "poppler/CourierBoldWidths.gperf" { "X", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 30 "poppler/CourierBoldWidths.gperf" { "ntilde", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 205 "poppler/CourierBoldWidths.gperf" { "tilde", 600 }, #line 324 "poppler/CourierBoldWidths.gperf" { "atilde", 600 }, { "", 0 }, { "", 0 }, #line 196 "poppler/CourierBoldWidths.gperf" { "nine", 600 }, #line 24 "poppler/CourierBoldWidths.gperf" { "edotaccent", 600 }, #line 105 "poppler/CourierBoldWidths.gperf" { "ordfeminine", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 158 "poppler/CourierBoldWidths.gperf" { "eight", 600 }, #line 150 "poppler/CourierBoldWidths.gperf" { "scaron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 276 "poppler/CourierBoldWidths.gperf" { "iacute", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 170 "poppler/CourierBoldWidths.gperf" { "otilde", 600 }, #line 292 "poppler/CourierBoldWidths.gperf" { "ordmasculine", 600 }, #line 213 "poppler/CourierBoldWidths.gperf" { "eth", 600 }, { "", 0 }, #line 42 "poppler/CourierBoldWidths.gperf" { "three", 600 }, #line 225 "poppler/CourierBoldWidths.gperf" { "dcroat", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 281 "poppler/CourierBoldWidths.gperf" { "Rcommaaccent", 600 }, #line 185 "poppler/CourierBoldWidths.gperf" { "Eacute", 600 }, #line 322 "poppler/CourierBoldWidths.gperf" { "Kcommaaccent", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 218 "poppler/CourierBoldWidths.gperf" { "uacute", 600 }, #line 103 "poppler/CourierBoldWidths.gperf" { "tcommaaccent", 600 }, { "", 0 }, #line 166 "poppler/CourierBoldWidths.gperf" { "copyright", 600 }, #line 43 "poppler/CourierBoldWidths.gperf" { "numbersign", 600 }, #line 15 "poppler/CourierBoldWidths.gperf" { "rcaron", 600 }, #line 32 "poppler/CourierBoldWidths.gperf" { "ncommaaccent", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 110 "poppler/CourierBoldWidths.gperf" { "r", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 264 "poppler/CourierBoldWidths.gperf" { "lacute", 600 }, { "", 0 }, { "", 0 }, #line 23 "poppler/CourierBoldWidths.gperf" { "dotaccent", 600 }, #line 234 "poppler/CourierBoldWidths.gperf" { "thorn", 600 }, #line 242 "poppler/CourierBoldWidths.gperf" { "dcaron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 146 "poppler/CourierBoldWidths.gperf" { "macron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 203 "poppler/CourierBoldWidths.gperf" { "Ccedilla", 600 }, #line 274 "poppler/CourierBoldWidths.gperf" { "section", 600 }, #line 223 "poppler/CourierBoldWidths.gperf" { "ccedilla", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 20 "poppler/CourierBoldWidths.gperf" { "cedilla", 600 }, { "", 0 }, { "", 0 }, #line 25 "poppler/CourierBoldWidths.gperf" { "asciitilde", 600 }, #line 89 "poppler/CourierBoldWidths.gperf" { "d", 600 }, #line 239 "poppler/CourierBoldWidths.gperf" { "percent", 600 }, { "", 0 }, { "", 0 }, #line 288 "poppler/CourierBoldWidths.gperf" { "germandbls", 600 }, { "", 0 }, #line 138 "poppler/CourierBoldWidths.gperf" { "lozenge", 600 }, { "", 0 }, #line 316 "poppler/CourierBoldWidths.gperf" { "less", 600 }, { "", 0 }, #line 97 "poppler/CourierBoldWidths.gperf" { "dagger", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 258 "poppler/CourierBoldWidths.gperf" { "grave", 600 }, #line 301 "poppler/CourierBoldWidths.gperf" { "Ecaron", 600 }, #line 222 "poppler/CourierBoldWidths.gperf" { "scommaaccent", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 160 "poppler/CourierBoldWidths.gperf" { "endash", 600 }, #line 174 "poppler/CourierBoldWidths.gperf" { "emacron", 600 }, #line 201 "poppler/CourierBoldWidths.gperf" { "threequarters", 600 }, { "", 0 }, { "", 0 }, #line 232 "poppler/CourierBoldWidths.gperf" { "Tcaron", 600 }, { "", 0 }, #line 228 "poppler/CourierBoldWidths.gperf" { "scedilla", 600 }, { "", 0 }, { "", 0 }, #line 101 "poppler/CourierBoldWidths.gperf" { "m", 600 }, { "", 0 }, { "", 0 }, #line 245 "poppler/CourierBoldWidths.gperf" { "summation", 600 }, #line 310 "poppler/CourierBoldWidths.gperf" { "logicalnot", 600 }, #line 44 "poppler/CourierBoldWidths.gperf" { "lcaron", 600 }, { "", 0 }, { "", 0 }, #line 172 "poppler/CourierBoldWidths.gperf" { "parenleft", 600 }, #line 139 "poppler/CourierBoldWidths.gperf" { "parenright", 600 }, #line 95 "poppler/CourierBoldWidths.gperf" { "i", 600 }, #line 305 "poppler/CourierBoldWidths.gperf" { "amacron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 194 "poppler/CourierBoldWidths.gperf" { "Uacute", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 208 "poppler/CourierBoldWidths.gperf" { "underscore", 600 }, #line 92 "poppler/CourierBoldWidths.gperf" { "g", 600 }, #line 297 "poppler/CourierBoldWidths.gperf" { "rcommaaccent", 600 }, { "", 0 }, { "", 0 }, #line 37 "poppler/CourierBoldWidths.gperf" { "space", 600 }, #line 28 "poppler/CourierBoldWidths.gperf" { "dollar", 600 }, { "", 0 }, #line 272 "poppler/CourierBoldWidths.gperf" { "threesuperior", 600 }, #line 188 "poppler/CourierBoldWidths.gperf" { "edieresis", 600 }, #line 236 "poppler/CourierBoldWidths.gperf" { "registered", 600 }, #line 78 "poppler/CourierBoldWidths.gperf" { "W", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 64 "poppler/CourierBoldWidths.gperf" { "omacron", 600 }, #line 36 "poppler/CourierBoldWidths.gperf" { "yen", 600 }, { "", 0 }, { "", 0 }, #line 50 "poppler/CourierBoldWidths.gperf" { "E", 600 }, { "", 0 }, #line 293 "poppler/CourierBoldWidths.gperf" { "dotlessi", 600 }, { "", 0 }, #line 327 "poppler/CourierBoldWidths.gperf" { "Edotaccent", 600 }, #line 71 "poppler/CourierBoldWidths.gperf" { "Aacute", 600 }, { "", 0 }, { "", 0 }, #line 186 "poppler/CourierBoldWidths.gperf" { "adieresis", 600 }, { "", 0 }, #line 117 "poppler/CourierBoldWidths.gperf" { "u", 600 }, { "", 0 }, { "", 0 }, #line 144 "poppler/CourierBoldWidths.gperf" { "daggerdbl", 600 }, { "", 0 }, #line 280 "poppler/CourierBoldWidths.gperf" { "yacute", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 74 "poppler/CourierBoldWidths.gperf" { "T", 600 }, #line 130 "poppler/CourierBoldWidths.gperf" { "gcommaaccent", 600 }, #line 275 "poppler/CourierBoldWidths.gperf" { "dieresis", 600 }, { "", 0 }, #line 51 "poppler/CourierBoldWidths.gperf" { "onequarter", 600 }, #line 328 "poppler/CourierBoldWidths.gperf" { "onesuperior", 600 }, #line 237 "poppler/CourierBoldWidths.gperf" { "radical", 600 }, #line 190 "poppler/CourierBoldWidths.gperf" { "Eth", 600 }, { "", 0 }, { "", 0 }, #line 94 "poppler/CourierBoldWidths.gperf" { "h", 600 }, { "", 0 }, { "", 0 }, #line 193 "poppler/CourierBoldWidths.gperf" { "odieresis", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 100 "poppler/CourierBoldWidths.gperf" { "l", 600 }, #line 65 "poppler/CourierBoldWidths.gperf" { "Tcommaaccent", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 136 "poppler/CourierBoldWidths.gperf" { "oslash", 600 }, { "", 0 }, { "", 0 }, #line 137 "poppler/CourierBoldWidths.gperf" { "lessequal", 600 }, #line 159 "poppler/CourierBoldWidths.gperf" { "exclamdown", 600 }, #line 35 "poppler/CourierBoldWidths.gperf" { "zacute", 600 }, #line 269 "poppler/CourierBoldWidths.gperf" { "lcommaaccent", 600 }, { "", 0 }, #line 209 "poppler/CourierBoldWidths.gperf" { "Euro", 600 }, { "", 0 }, #line 291 "poppler/CourierBoldWidths.gperf" { "Sacute", 600 }, #line 323 "poppler/CourierBoldWidths.gperf" { "greater", 600 }, #line 244 "poppler/CourierBoldWidths.gperf" { "two", 600 }, { "", 0 }, #line 220 "poppler/CourierBoldWidths.gperf" { "Thorn", 600 }, #line 256 "poppler/CourierBoldWidths.gperf" { "asciicircum", 600 }, #line 126 "poppler/CourierBoldWidths.gperf" { "hungarumlaut", 600 }, { "", 0 }, #line 212 "poppler/CourierBoldWidths.gperf" { "zero", 600 }, { "", 0 }, #line 40 "poppler/CourierBoldWidths.gperf" { "emdash", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 116 "poppler/CourierBoldWidths.gperf" { "divide", 600 }, { "", 0 }, #line 271 "poppler/CourierBoldWidths.gperf" { "ohungarumlaut", 600 }, #line 262 "poppler/CourierBoldWidths.gperf" { "ampersand", 600 }, { "", 0 }, #line 164 "poppler/CourierBoldWidths.gperf" { "ecircumflex", 600 }, { "", 0 }, { "", 0 }, #line 106 "poppler/CourierBoldWidths.gperf" { "ring", 600 }, { "", 0 }, #line 320 "poppler/CourierBoldWidths.gperf" { "period", 600 }, { "", 0 }, #line 318 "poppler/CourierBoldWidths.gperf" { "guilsinglleft", 600 }, #line 155 "poppler/CourierBoldWidths.gperf" { "guilsinglright", 600 }, { "", 0 }, { "", 0 }, #line 307 "poppler/CourierBoldWidths.gperf" { "imacron", 600 }, { "", 0 }, #line 61 "poppler/CourierBoldWidths.gperf" { "periodcentered", 600 }, { "", 0 }, #line 227 "poppler/CourierBoldWidths.gperf" { "Oacute", 600 }, { "", 0 }, #line 294 "poppler/CourierBoldWidths.gperf" { "sterling", 600 }, { "", 0 }, { "", 0 }, #line 299 "poppler/CourierBoldWidths.gperf" { "acircumflex", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 33 "poppler/CourierBoldWidths.gperf" { "minus", 600 }, #line 312 "poppler/CourierBoldWidths.gperf" { "Atilde", 600 }, #line 148 "poppler/CourierBoldWidths.gperf" { "Emacron", 600 }, { "", 0 }, { "", 0 }, #line 257 "poppler/CourierBoldWidths.gperf" { "aring", 600 }, #line 261 "poppler/CourierBoldWidths.gperf" { "Iacute", 600 }, #line 183 "poppler/CourierBoldWidths.gperf" { "umacron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 221 "poppler/CourierBoldWidths.gperf" { "zcaron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 133 "poppler/CourierBoldWidths.gperf" { "Scaron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 248 "poppler/CourierBoldWidths.gperf" { "ocircumflex", 600 }, { "", 0 }, { "", 0 }, #line 189 "poppler/CourierBoldWidths.gperf" { "idieresis", 600 }, { "", 0 }, #line 157 "poppler/CourierBoldWidths.gperf" { "quotesingle", 600 }, #line 277 "poppler/CourierBoldWidths.gperf" { "quotedblbase", 600 }, { "", 0 }, #line 268 "poppler/CourierBoldWidths.gperf" { "quotesinglbase", 600 }, { "", 0 }, #line 107 "poppler/CourierBoldWidths.gperf" { "p", 600 }, #line 132 "poppler/CourierBoldWidths.gperf" { "greaterequal", 600 }, { "", 0 }, #line 326 "poppler/CourierBoldWidths.gperf" { "quoteleft", 600 }, #line 179 "poppler/CourierBoldWidths.gperf" { "quoteright", 600 }, { "", 0 }, #line 154 "poppler/CourierBoldWidths.gperf" { "quotedblleft", 600 }, #line 304 "poppler/CourierBoldWidths.gperf" { "quotedblright", 600 }, #line 169 "poppler/CourierBoldWidths.gperf" { "Edieresis", 600 }, { "", 0 }, #line 128 "poppler/CourierBoldWidths.gperf" { "Nacute", 600 }, #line 131 "poppler/CourierBoldWidths.gperf" { "mu", 600 }, { "", 0 }, #line 198 "poppler/CourierBoldWidths.gperf" { "udieresis", 600 }, { "", 0 }, #line 270 "poppler/CourierBoldWidths.gperf" { "Yacute", 600 }, #line 253 "poppler/CourierBoldWidths.gperf" { "eogonek", 600 }, #line 80 "poppler/CourierBoldWidths.gperf" { "question", 600 }, { "", 0 }, #line 313 "poppler/CourierBoldWidths.gperf" { "breve", 600 }, #line 77 "poppler/CourierBoldWidths.gperf" { "V", 600 }, #line 39 "poppler/CourierBoldWidths.gperf" { "questiondown", 600 }, { "", 0 }, #line 266 "poppler/CourierBoldWidths.gperf" { "plus", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 149 "poppler/CourierBoldWidths.gperf" { "ellipsis", 600 }, { "", 0 }, { "", 0 }, #line 319 "poppler/CourierBoldWidths.gperf" { "exclam", 600 }, { "", 0 }, { "", 0 }, #line 219 "poppler/CourierBoldWidths.gperf" { "braceleft", 600 }, #line 303 "poppler/CourierBoldWidths.gperf" { "braceright", 600 }, #line 156 "poppler/CourierBoldWidths.gperf" { "hyphen", 600 }, #line 48 "poppler/CourierBoldWidths.gperf" { "aogonek", 600 }, #line 314 "poppler/CourierBoldWidths.gperf" { "bar", 600 }, { "", 0 }, #line 311 "poppler/CourierBoldWidths.gperf" { "zdotaccent", 600 }, #line 153 "poppler/CourierBoldWidths.gperf" { "lslash", 600 }, #line 86 "poppler/CourierBoldWidths.gperf" { "Gcommaaccent", 600 }, #line 309 "poppler/CourierBoldWidths.gperf" { "currency", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 75 "poppler/CourierBoldWidths.gperf" { "U", 600 }, #line 27 "poppler/CourierBoldWidths.gperf" { "onehalf", 600 }, #line 109 "poppler/CourierBoldWidths.gperf" { "uhungarumlaut", 600 }, { "", 0 }, { "", 0 }, #line 147 "poppler/CourierBoldWidths.gperf" { "Otilde", 600 }, { "", 0 }, #line 287 "poppler/CourierBoldWidths.gperf" { "guillemotleft", 600 }, #line 202 "poppler/CourierBoldWidths.gperf" { "guillemotright", 600 }, { "", 0 }, #line 247 "poppler/CourierBoldWidths.gperf" { "Lacute", 600 }, #line 163 "poppler/CourierBoldWidths.gperf" { "Umacron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 18 "poppler/CourierBoldWidths.gperf" { "Zacute", 600 }, { "", 0 }, #line 295 "poppler/CourierBoldWidths.gperf" { "notequal", 600 }, #line 143 "poppler/CourierBoldWidths.gperf" { "trademark", 600 }, { "", 0 }, #line 265 "poppler/CourierBoldWidths.gperf" { "Ncaron", 600 }, #line 200 "poppler/CourierBoldWidths.gperf" { "Scommaaccent", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 181 "poppler/CourierBoldWidths.gperf" { "perthousand", 600 }, { "", 0 }, #line 240 "poppler/CourierBoldWidths.gperf" { "six", 600 }, { "", 0 }, { "", 0 }, #line 302 "poppler/CourierBoldWidths.gperf" { "icircumflex", 600 }, { "", 0 }, #line 214 "poppler/CourierBoldWidths.gperf" { "Scedilla", 600 }, { "", 0 }, { "", 0 }, #line 93 "poppler/CourierBoldWidths.gperf" { "bullet", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 108 "poppler/CourierBoldWidths.gperf" { "q", 600 }, #line 289 "poppler/CourierBoldWidths.gperf" { "Amacron", 600 }, { "", 0 }, { "", 0 }, #line 127 "poppler/CourierBoldWidths.gperf" { "Idotaccent", 600 }, #line 141 "poppler/CourierBoldWidths.gperf" { "Ecircumflex", 600 }, { "", 0 }, #line 315 "poppler/CourierBoldWidths.gperf" { "fraction", 600 }, #line 180 "poppler/CourierBoldWidths.gperf" { "Udieresis", 600 }, { "", 0 }, #line 176 "poppler/CourierBoldWidths.gperf" { "ucircumflex", 600 }, { "", 0 }, { "", 0 }, #line 197 "poppler/CourierBoldWidths.gperf" { "five", 600 }, { "", 0 }, #line 14 "poppler/CourierBoldWidths.gperf" { "Ntilde", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 267 "poppler/CourierBoldWidths.gperf" { "uring", 600 }, #line 45 "poppler/CourierBoldWidths.gperf" { "A", 600 }, { "", 0 }, { "", 0 }, #line 84 "poppler/CourierBoldWidths.gperf" { "four", 600 }, { "", 0 }, #line 187 "poppler/CourierBoldWidths.gperf" { "egrave", 600 }, { "", 0 }, { "", 0 }, #line 241 "poppler/CourierBoldWidths.gperf" { "paragraph", 600 }, { "", 0 }, #line 29 "poppler/CourierBoldWidths.gperf" { "Lcaron", 600 }, { "", 0 }, { "", 0 }, #line 325 "poppler/CourierBoldWidths.gperf" { "brokenbar", 600 }, { "", 0 }, #line 199 "poppler/CourierBoldWidths.gperf" { "Zcaron", 600 }, { "", 0 }, { "", 0 }, #line 165 "poppler/CourierBoldWidths.gperf" { "Adieresis", 600 }, { "", 0 }, #line 122 "poppler/CourierBoldWidths.gperf" { "y", 600 }, #line 16 "poppler/CourierBoldWidths.gperf" { "kcommaaccent", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 76 "poppler/CourierBoldWidths.gperf" { "agrave", 600 }, { "", 0 }, #line 69 "poppler/CourierBoldWidths.gperf" { "Uhungarumlaut", 600 }, #line 204 "poppler/CourierBoldWidths.gperf" { "ydieresis", 600 }, #line 168 "poppler/CourierBoldWidths.gperf" { "slash", 600 }, #line 229 "poppler/CourierBoldWidths.gperf" { "ogonek", 600 }, #line 151 "poppler/CourierBoldWidths.gperf" { "AE", 600 }, #line 192 "poppler/CourierBoldWidths.gperf" { "asterisk", 600 }, { "", 0 }, { "", 0 }, #line 111 "poppler/CourierBoldWidths.gperf" { "twosuperior", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 53 "poppler/CourierBoldWidths.gperf" { "G", 600 }, #line 58 "poppler/CourierBoldWidths.gperf" { "iogonek", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 17 "poppler/CourierBoldWidths.gperf" { "Ncommaaccent", 600 }, { "", 0 }, #line 21 "poppler/CourierBoldWidths.gperf" { "plusminus", 600 }, { "", 0 }, #line 230 "poppler/CourierBoldWidths.gperf" { "ograve", 600 }, { "", 0 }, #line 129 "poppler/CourierBoldWidths.gperf" { "quotedbl", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 233 "poppler/CourierBoldWidths.gperf" { "Eogonek", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 120 "poppler/CourierBoldWidths.gperf" { "w", 600 }, #line 259 "poppler/CourierBoldWidths.gperf" { "uogonek", 600 }, { "", 0 }, #line 59 "poppler/CourierBoldWidths.gperf" { "backslash", 600 }, #line 81 "poppler/CourierBoldWidths.gperf" { "equal", 600 }, { "", 0 }, #line 38 "poppler/CourierBoldWidths.gperf" { "Omacron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 121 "poppler/CourierBoldWidths.gperf" { "x", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 298 "poppler/CourierBoldWidths.gperf" { "Zdotaccent", 600 }, #line 152 "poppler/CourierBoldWidths.gperf" { "Ucircumflex", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 68 "poppler/CourierBoldWidths.gperf" { "Q", 600 }, #line 296 "poppler/CourierBoldWidths.gperf" { "Imacron", 600 }, { "", 0 }, { "", 0 }, #line 250 "poppler/CourierBoldWidths.gperf" { "Uring", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 123 "poppler/CourierBoldWidths.gperf" { "z", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 22 "poppler/CourierBoldWidths.gperf" { "circumflex", 600 }, { "", 0 }, #line 251 "poppler/CourierBoldWidths.gperf" { "Lcommaaccent", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 73 "poppler/CourierBoldWidths.gperf" { "S", 600 }, { "", 0 }, { "", 0 }, #line 175 "poppler/CourierBoldWidths.gperf" { "Odieresis", 600 }, { "", 0 }, #line 284 "poppler/CourierBoldWidths.gperf" { "Acircumflex", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 67 "poppler/CourierBoldWidths.gperf" { "P", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 238 "poppler/CourierBoldWidths.gperf" { "Aring", 600 }, #line 96 "poppler/CourierBoldWidths.gperf" { "Oslash", 600 }, #line 114 "poppler/CourierBoldWidths.gperf" { "OE", 600 }, { "", 0 }, #line 171 "poppler/CourierBoldWidths.gperf" { "Idieresis", 600 }, { "", 0 }, #line 62 "poppler/CourierBoldWidths.gperf" { "M", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 282 "poppler/CourierBoldWidths.gperf" { "fi", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 56 "poppler/CourierBoldWidths.gperf" { "J", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 263 "poppler/CourierBoldWidths.gperf" { "igrave", 600 }, { "", 0 }, #line 255 "poppler/CourierBoldWidths.gperf" { "Ohungarumlaut", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 243 "poppler/CourierBoldWidths.gperf" { "Uogonek", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 87 "poppler/CourierBoldWidths.gperf" { "b", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 167 "poppler/CourierBoldWidths.gperf" { "Egrave", 600 }, { "", 0 }, { "", 0 }, #line 182 "poppler/CourierBoldWidths.gperf" { "Ydieresis", 600 }, { "", 0 }, #line 195 "poppler/CourierBoldWidths.gperf" { "ugrave", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 211 "poppler/CourierBoldWidths.gperf" { "multiply", 600 }, { "", 0 }, { "", 0 }, #line 66 "poppler/CourierBoldWidths.gperf" { "O", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 31 "poppler/CourierBoldWidths.gperf" { "Aogonek", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 279 "poppler/CourierBoldWidths.gperf" { "florin", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 226 "poppler/CourierBoldWidths.gperf" { "Ocircumflex", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 283 "poppler/CourierBoldWidths.gperf" { "fl", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 55 "poppler/CourierBoldWidths.gperf" { "I", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 286 "poppler/CourierBoldWidths.gperf" { "Icircumflex", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 54 "poppler/CourierBoldWidths.gperf" { "H", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 134 "poppler/CourierBoldWidths.gperf" { "Lslash", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 99 "poppler/CourierBoldWidths.gperf" { "k", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 184 "poppler/CourierBoldWidths.gperf" { "abreve", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 217 "poppler/CourierBoldWidths.gperf" { "partialdiff", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 52 "poppler/CourierBoldWidths.gperf" { "F", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 178 "poppler/CourierBoldWidths.gperf" { "Ugrave", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 91 "poppler/CourierBoldWidths.gperf" { "f", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 119 "poppler/CourierBoldWidths.gperf" { "v", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 63 "poppler/CourierBoldWidths.gperf" { "N", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 41 "poppler/CourierBoldWidths.gperf" { "Agrave", 600 }, #line 34 "poppler/CourierBoldWidths.gperf" { "Iogonek", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 82 "poppler/CourierBoldWidths.gperf" { "Y", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 46 "poppler/CourierBoldWidths.gperf" { "B", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 177 "poppler/CourierBoldWidths.gperf" { "bracketleft", 600 }, #line 260 "poppler/CourierBoldWidths.gperf" { "bracketright", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 142 "poppler/CourierBoldWidths.gperf" { "gbreve", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 215 "poppler/CourierBoldWidths.gperf" { "Ograve", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 60 "poppler/CourierBoldWidths.gperf" { "L", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 246 "poppler/CourierBoldWidths.gperf" { "Igrave", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 83 "poppler/CourierBoldWidths.gperf" { "Z", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 162 "poppler/CourierBoldWidths.gperf" { "Abreve", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 124 "poppler/CourierBoldWidths.gperf" { "Gbreve", 600 } }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) { register unsigned int key = hash(str, len); if (key <= MAX_HASH_VALUE) { register const char *s = wordlist[key].name; if (*str == *s && !strcmp(str + 1, s + 1)) return &wordlist[key]; } } return 0; } #line 330 "poppler/CourierBoldWidths.gperf" poppler-24.02.0/poppler/CourierObliqueWidths.gperf000066400000000000000000000101431455701731300221670ustar00rootroot00000000000000%{ #include #include "BuiltinFontWidth.h" %} %language=ANSI-C %define initializer-suffix ,0 %define lookup-function-name CourierObliqueWidthsLookup %struct-type %omit-struct-type %readonly-tables struct BuiltinFontWidth %% #### Ntilde, 600 rcaron, 600 kcommaaccent, 600 Ncommaaccent, 600 Zacute, 600 comma, 600 cedilla, 600 plusminus, 600 circumflex, 600 dotaccent, 600 edotaccent, 600 asciitilde, 600 colon, 600 onehalf, 600 dollar, 600 Lcaron, 600 ntilde, 600 Aogonek, 600 ncommaaccent, 600 minus, 600 Iogonek, 600 zacute, 600 yen, 600 space, 600 Omacron, 600 questiondown, 600 emdash, 600 Agrave, 600 three, 600 numbersign, 600 lcaron, 600 A, 600 B, 600 C, 600 aogonek, 600 D, 600 E, 600 onequarter, 600 F, 600 G, 600 H, 600 I, 600 J, 600 K, 600 iogonek, 600 backslash, 600 L, 600 periodcentered, 600 M, 600 N, 600 omacron, 600 Tcommaaccent, 600 O, 600 P, 600 Q, 600 Uhungarumlaut, 600 R, 600 Aacute, 600 caron, 600 S, 600 T, 600 U, 600 agrave, 600 V, 600 W, 600 X, 600 question, 600 equal, 600 Y, 600 Z, 600 four, 600 a, 600 Gcommaaccent, 600 b, 600 c, 600 d, 600 e, 600 f, 600 g, 600 bullet, 600 h, 600 i, 600 Oslash, 600 dagger, 600 j, 600 k, 600 l, 600 m, 600 n, 600 tcommaaccent, 600 o, 600 ordfeminine, 600 ring, 600 p, 600 q, 600 uhungarumlaut, 600 r, 600 twosuperior, 600 aacute, 600 s, 600 OE, 600 t, 600 divide, 600 u, 600 Ccaron, 600 v, 600 w, 600 x, 600 y, 600 z, 600 Gbreve, 600 commaaccent, 600 hungarumlaut, 600 Idotaccent, 600 Nacute, 600 quotedbl, 600 gcommaaccent, 600 mu, 600 greaterequal, 600 Scaron, 600 Lslash, 600 semicolon, 600 oslash, 600 lessequal, 600 lozenge, 600 parenright, 600 ccaron, 600 Ecircumflex, 600 gbreve, 600 trademark, 600 daggerdbl, 600 nacute, 600 macron, 600 Otilde, 600 Emacron, 600 ellipsis, 600 scaron, 600 AE, 600 Ucircumflex, 600 lslash, 600 quotedblleft, 600 guilsinglright, 600 hyphen, 600 quotesingle, 600 eight, 600 exclamdown, 600 endash, 600 oe, 600 Abreve, 600 Umacron, 600 ecircumflex, 600 Adieresis, 600 copyright, 600 Egrave, 600 slash, 600 Edieresis, 600 otilde, 600 Idieresis, 600 parenleft, 600 one, 600 emacron, 600 Odieresis, 600 ucircumflex, 600 bracketleft, 600 Ugrave, 600 quoteright, 600 Udieresis, 600 perthousand, 600 Ydieresis, 600 umacron, 600 abreve, 600 Eacute, 600 adieresis, 600 egrave, 600 edieresis, 600 idieresis, 600 Eth, 600 ae, 600 asterisk, 600 odieresis, 600 Uacute, 600 ugrave, 600 nine, 600 five, 600 udieresis, 600 Zcaron, 600 Scommaaccent, 600 threequarters, 600 guillemotright, 600 Ccedilla, 600 ydieresis, 600 tilde, 600 at, 600 eacute, 600 underscore, 600 Euro, 600 Dcroat, 600 multiply, 600 zero, 600 eth, 600 Scedilla, 600 Ograve, 600 Racute, 600 partialdiff, 600 uacute, 600 braceleft, 600 Thorn, 600 zcaron, 600 scommaaccent, 600 ccedilla, 600 Dcaron, 600 dcroat, 600 Ocircumflex, 600 Oacute, 600 scedilla, 600 ogonek, 600 ograve, 600 racute, 600 Tcaron, 600 Eogonek, 600 thorn, 600 degree, 600 registered, 600 radical, 600 Aring, 600 percent, 600 six, 600 paragraph, 600 dcaron, 600 Uogonek, 600 two, 600 summation, 600 Igrave, 600 Lacute, 600 ocircumflex, 600 oacute, 600 Uring, 600 Lcommaaccent, 600 tcaron, 600 eogonek, 600 Delta, 600 Ohungarumlaut, 600 asciicircum, 600 aring, 600 grave, 600 uogonek, 600 bracketright, 600 Iacute, 600 ampersand, 600 igrave, 600 lacute, 600 Ncaron, 600 plus, 600 uring, 600 quotesinglbase, 600 lcommaaccent, 600 Yacute, 600 ohungarumlaut, 600 threesuperior, 600 acute, 600 section, 600 dieresis, 600 iacute, 600 quotedblbase, 600 ncaron, 600 florin, 600 yacute, 600 Rcommaaccent, 600 fi, 600 fl, 600 Acircumflex, 600 Cacute, 600 Icircumflex, 600 guillemotleft, 600 germandbls, 600 Amacron, 600 seven, 600 Sacute, 600 ordmasculine, 600 dotlessi, 600 sterling, 600 notequal, 600 Imacron, 600 rcommaaccent, 600 Zdotaccent, 600 acircumflex, 600 cacute, 600 Ecaron, 600 icircumflex, 600 braceright, 600 quotedblright, 600 amacron, 600 sacute, 600 imacron, 600 cent, 600 currency, 600 logicalnot, 600 zdotaccent, 600 Atilde, 600 breve, 600 bar, 600 fraction, 600 less, 600 ecaron, 600 guilsinglleft, 600 exclam, 600 period, 600 Rcaron, 600 Kcommaaccent, 600 greater, 600 atilde, 600 brokenbar, 600 quoteleft, 600 Edotaccent, 600 onesuperior, 600 #### %% poppler-24.02.0/poppler/CourierObliqueWidths.pregenerated.c000066400000000000000000002643521455701731300237670ustar00rootroot00000000000000/* ANSI-C code produced by gperf version 3.1 */ /* Command-line: gperf poppler/CourierObliqueWidths.gperf */ /* Computed positions: -k'1-2,5,$' */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) && ('-' == 45) && ('.' == 46) && ('/' == 47) \ && ('0' == 48) && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) && ('=' == 61) && ('>' == 62) \ && ('?' == 63) && ('A' == 65) && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) && ('N' == 78) \ && ('O' == 79) && ('P' == 80) && ('Q' == 81) && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) && ('k' == 107) && ('l' == 108) \ && ('m' == 109) && ('n' == 110) && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) /* The character set is not based on ISO-646. */ # error "gperf generated tables don't work with this execution character set. Please report a bug to ." #endif #line 1 "poppler/CourierObliqueWidths.gperf" #include #include "BuiltinFontWidth.h" #define TOTAL_KEYWORDS 315 #define MIN_WORD_LENGTH 1 #define MAX_WORD_LENGTH 14 #define MIN_HASH_VALUE 1 #define MAX_HASH_VALUE 1041 /* maximum key range = 1041, duplicates = 0 */ #ifdef __GNUC__ __inline #else # ifdef __cplusplus inline # endif #endif static unsigned int hash(register const char *str, register size_t len) { static const unsigned short asso_values[] = { 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 270, 415, 28, 8, 150, 390, 290, 375, 370, 335, 5, 455, 330, 405, 355, 325, 310, 3, 320, 160, 240, 225, 145, 70, 410, 460, 1042, 1042, 1042, 1042, 1042, 1042, 20, 345, 30, 115, 0, 395, 140, 165, 135, 35, 380, 170, 130, 15, 45, 215, 260, 100, 65, 10, 155, 400, 300, 305, 280, 315, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042 }; register unsigned int hval = len; switch (hval) { default: hval += asso_values[(unsigned char)str[4]]; /*FALLTHROUGH*/ case 4: case 3: case 2: hval += asso_values[(unsigned char)str[1]]; /*FALLTHROUGH*/ case 1: hval += asso_values[(unsigned char)str[0]]; break; } return hval + asso_values[(unsigned char)str[len - 1]]; } const struct BuiltinFontWidth *CourierObliqueWidthsLookup(register const char *str, register size_t len) { static const struct BuiltinFontWidth wordlist[] = { { "", 0 }, #line 90 "poppler/CourierObliqueWidths.gperf" { "e", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 70 "poppler/CourierObliqueWidths.gperf" { "R", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 57 "poppler/CourierObliqueWidths.gperf" { "K", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 49 "poppler/CourierObliqueWidths.gperf" { "D", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 115 "poppler/CourierObliqueWidths.gperf" { "t", 600 }, #line 191 "poppler/CourierObliqueWidths.gperf" { "ae", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 102 "poppler/CourierObliqueWidths.gperf" { "n", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 207 "poppler/CourierObliqueWidths.gperf" { "eacute", 600 }, { "", 0 }, { "", 0 }, #line 216 "poppler/CourierObliqueWidths.gperf" { "Racute", 600 }, { "", 0 }, #line 85 "poppler/CourierObliqueWidths.gperf" { "a", 600 }, #line 206 "poppler/CourierObliqueWidths.gperf" { "at", 600 }, { "", 0 }, #line 308 "poppler/CourierObliqueWidths.gperf" { "cent", 600 }, { "", 0 }, { "", 0 }, #line 161 "poppler/CourierObliqueWidths.gperf" { "oe", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 145 "poppler/CourierObliqueWidths.gperf" { "nacute", 600 }, { "", 0 }, #line 254 "poppler/CourierObliqueWidths.gperf" { "Delta", 600 }, { "", 0 }, #line 273 "poppler/CourierObliqueWidths.gperf" { "acute", 600 }, #line 112 "poppler/CourierObliqueWidths.gperf" { "aacute", 600 }, #line 47 "poppler/CourierObliqueWidths.gperf" { "C", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 88 "poppler/CourierObliqueWidths.gperf" { "c", 600 }, { "", 0 }, #line 173 "poppler/CourierObliqueWidths.gperf" { "one", 600 }, #line 285 "poppler/CourierObliqueWidths.gperf" { "Cacute", 600 }, { "", 0 }, #line 300 "poppler/CourierObliqueWidths.gperf" { "cacute", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 98 "poppler/CourierObliqueWidths.gperf" { "j", 600 }, { "", 0 }, { "", 0 }, #line 210 "poppler/CourierObliqueWidths.gperf" { "Dcroat", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 249 "poppler/CourierObliqueWidths.gperf" { "oacute", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 72 "poppler/CourierObliqueWidths.gperf" { "caron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 104 "poppler/CourierObliqueWidths.gperf" { "o", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 317 "poppler/CourierObliqueWidths.gperf" { "ecaron", 600 }, { "", 0 }, { "", 0 }, #line 321 "poppler/CourierObliqueWidths.gperf" { "Rcaron", 600 }, #line 290 "poppler/CourierObliqueWidths.gperf" { "seven", 600 }, #line 306 "poppler/CourierObliqueWidths.gperf" { "sacute", 600 }, { "", 0 }, { "", 0 }, #line 224 "poppler/CourierObliqueWidths.gperf" { "Dcaron", 600 }, { "", 0 }, #line 252 "poppler/CourierObliqueWidths.gperf" { "tcaron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 26 "poppler/CourierObliqueWidths.gperf" { "colon", 600 }, #line 278 "poppler/CourierObliqueWidths.gperf" { "ncaron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 125 "poppler/CourierObliqueWidths.gperf" { "commaaccent", 600 }, { "", 0 }, { "", 0 }, #line 135 "poppler/CourierObliqueWidths.gperf" { "semicolon", 600 }, #line 19 "poppler/CourierObliqueWidths.gperf" { "comma", 600 }, #line 235 "poppler/CourierObliqueWidths.gperf" { "degree", 600 }, { "", 0 }, { "", 0 }, #line 118 "poppler/CourierObliqueWidths.gperf" { "Ccaron", 600 }, { "", 0 }, #line 140 "poppler/CourierObliqueWidths.gperf" { "ccaron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 113 "poppler/CourierObliqueWidths.gperf" { "s", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 231 "poppler/CourierObliqueWidths.gperf" { "racute", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 79 "poppler/CourierObliqueWidths.gperf" { "X", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 30 "poppler/CourierObliqueWidths.gperf" { "ntilde", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 205 "poppler/CourierObliqueWidths.gperf" { "tilde", 600 }, #line 324 "poppler/CourierObliqueWidths.gperf" { "atilde", 600 }, { "", 0 }, { "", 0 }, #line 196 "poppler/CourierObliqueWidths.gperf" { "nine", 600 }, #line 24 "poppler/CourierObliqueWidths.gperf" { "edotaccent", 600 }, #line 105 "poppler/CourierObliqueWidths.gperf" { "ordfeminine", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 158 "poppler/CourierObliqueWidths.gperf" { "eight", 600 }, #line 150 "poppler/CourierObliqueWidths.gperf" { "scaron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 276 "poppler/CourierObliqueWidths.gperf" { "iacute", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 170 "poppler/CourierObliqueWidths.gperf" { "otilde", 600 }, #line 292 "poppler/CourierObliqueWidths.gperf" { "ordmasculine", 600 }, #line 213 "poppler/CourierObliqueWidths.gperf" { "eth", 600 }, { "", 0 }, #line 42 "poppler/CourierObliqueWidths.gperf" { "three", 600 }, #line 225 "poppler/CourierObliqueWidths.gperf" { "dcroat", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 281 "poppler/CourierObliqueWidths.gperf" { "Rcommaaccent", 600 }, #line 185 "poppler/CourierObliqueWidths.gperf" { "Eacute", 600 }, #line 322 "poppler/CourierObliqueWidths.gperf" { "Kcommaaccent", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 218 "poppler/CourierObliqueWidths.gperf" { "uacute", 600 }, #line 103 "poppler/CourierObliqueWidths.gperf" { "tcommaaccent", 600 }, { "", 0 }, #line 166 "poppler/CourierObliqueWidths.gperf" { "copyright", 600 }, #line 43 "poppler/CourierObliqueWidths.gperf" { "numbersign", 600 }, #line 15 "poppler/CourierObliqueWidths.gperf" { "rcaron", 600 }, #line 32 "poppler/CourierObliqueWidths.gperf" { "ncommaaccent", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 110 "poppler/CourierObliqueWidths.gperf" { "r", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 264 "poppler/CourierObliqueWidths.gperf" { "lacute", 600 }, { "", 0 }, { "", 0 }, #line 23 "poppler/CourierObliqueWidths.gperf" { "dotaccent", 600 }, #line 234 "poppler/CourierObliqueWidths.gperf" { "thorn", 600 }, #line 242 "poppler/CourierObliqueWidths.gperf" { "dcaron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 146 "poppler/CourierObliqueWidths.gperf" { "macron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 203 "poppler/CourierObliqueWidths.gperf" { "Ccedilla", 600 }, #line 274 "poppler/CourierObliqueWidths.gperf" { "section", 600 }, #line 223 "poppler/CourierObliqueWidths.gperf" { "ccedilla", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 20 "poppler/CourierObliqueWidths.gperf" { "cedilla", 600 }, { "", 0 }, { "", 0 }, #line 25 "poppler/CourierObliqueWidths.gperf" { "asciitilde", 600 }, #line 89 "poppler/CourierObliqueWidths.gperf" { "d", 600 }, #line 239 "poppler/CourierObliqueWidths.gperf" { "percent", 600 }, { "", 0 }, { "", 0 }, #line 288 "poppler/CourierObliqueWidths.gperf" { "germandbls", 600 }, { "", 0 }, #line 138 "poppler/CourierObliqueWidths.gperf" { "lozenge", 600 }, { "", 0 }, #line 316 "poppler/CourierObliqueWidths.gperf" { "less", 600 }, { "", 0 }, #line 97 "poppler/CourierObliqueWidths.gperf" { "dagger", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 258 "poppler/CourierObliqueWidths.gperf" { "grave", 600 }, #line 301 "poppler/CourierObliqueWidths.gperf" { "Ecaron", 600 }, #line 222 "poppler/CourierObliqueWidths.gperf" { "scommaaccent", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 160 "poppler/CourierObliqueWidths.gperf" { "endash", 600 }, #line 174 "poppler/CourierObliqueWidths.gperf" { "emacron", 600 }, #line 201 "poppler/CourierObliqueWidths.gperf" { "threequarters", 600 }, { "", 0 }, { "", 0 }, #line 232 "poppler/CourierObliqueWidths.gperf" { "Tcaron", 600 }, { "", 0 }, #line 228 "poppler/CourierObliqueWidths.gperf" { "scedilla", 600 }, { "", 0 }, { "", 0 }, #line 101 "poppler/CourierObliqueWidths.gperf" { "m", 600 }, { "", 0 }, { "", 0 }, #line 245 "poppler/CourierObliqueWidths.gperf" { "summation", 600 }, #line 310 "poppler/CourierObliqueWidths.gperf" { "logicalnot", 600 }, #line 44 "poppler/CourierObliqueWidths.gperf" { "lcaron", 600 }, { "", 0 }, { "", 0 }, #line 172 "poppler/CourierObliqueWidths.gperf" { "parenleft", 600 }, #line 139 "poppler/CourierObliqueWidths.gperf" { "parenright", 600 }, #line 95 "poppler/CourierObliqueWidths.gperf" { "i", 600 }, #line 305 "poppler/CourierObliqueWidths.gperf" { "amacron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 194 "poppler/CourierObliqueWidths.gperf" { "Uacute", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 208 "poppler/CourierObliqueWidths.gperf" { "underscore", 600 }, #line 92 "poppler/CourierObliqueWidths.gperf" { "g", 600 }, #line 297 "poppler/CourierObliqueWidths.gperf" { "rcommaaccent", 600 }, { "", 0 }, { "", 0 }, #line 37 "poppler/CourierObliqueWidths.gperf" { "space", 600 }, #line 28 "poppler/CourierObliqueWidths.gperf" { "dollar", 600 }, { "", 0 }, #line 272 "poppler/CourierObliqueWidths.gperf" { "threesuperior", 600 }, #line 188 "poppler/CourierObliqueWidths.gperf" { "edieresis", 600 }, #line 236 "poppler/CourierObliqueWidths.gperf" { "registered", 600 }, #line 78 "poppler/CourierObliqueWidths.gperf" { "W", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 64 "poppler/CourierObliqueWidths.gperf" { "omacron", 600 }, #line 36 "poppler/CourierObliqueWidths.gperf" { "yen", 600 }, { "", 0 }, { "", 0 }, #line 50 "poppler/CourierObliqueWidths.gperf" { "E", 600 }, { "", 0 }, #line 293 "poppler/CourierObliqueWidths.gperf" { "dotlessi", 600 }, { "", 0 }, #line 327 "poppler/CourierObliqueWidths.gperf" { "Edotaccent", 600 }, #line 71 "poppler/CourierObliqueWidths.gperf" { "Aacute", 600 }, { "", 0 }, { "", 0 }, #line 186 "poppler/CourierObliqueWidths.gperf" { "adieresis", 600 }, { "", 0 }, #line 117 "poppler/CourierObliqueWidths.gperf" { "u", 600 }, { "", 0 }, { "", 0 }, #line 144 "poppler/CourierObliqueWidths.gperf" { "daggerdbl", 600 }, { "", 0 }, #line 280 "poppler/CourierObliqueWidths.gperf" { "yacute", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 74 "poppler/CourierObliqueWidths.gperf" { "T", 600 }, #line 130 "poppler/CourierObliqueWidths.gperf" { "gcommaaccent", 600 }, #line 275 "poppler/CourierObliqueWidths.gperf" { "dieresis", 600 }, { "", 0 }, #line 51 "poppler/CourierObliqueWidths.gperf" { "onequarter", 600 }, #line 328 "poppler/CourierObliqueWidths.gperf" { "onesuperior", 600 }, #line 237 "poppler/CourierObliqueWidths.gperf" { "radical", 600 }, #line 190 "poppler/CourierObliqueWidths.gperf" { "Eth", 600 }, { "", 0 }, { "", 0 }, #line 94 "poppler/CourierObliqueWidths.gperf" { "h", 600 }, { "", 0 }, { "", 0 }, #line 193 "poppler/CourierObliqueWidths.gperf" { "odieresis", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 100 "poppler/CourierObliqueWidths.gperf" { "l", 600 }, #line 65 "poppler/CourierObliqueWidths.gperf" { "Tcommaaccent", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 136 "poppler/CourierObliqueWidths.gperf" { "oslash", 600 }, { "", 0 }, { "", 0 }, #line 137 "poppler/CourierObliqueWidths.gperf" { "lessequal", 600 }, #line 159 "poppler/CourierObliqueWidths.gperf" { "exclamdown", 600 }, #line 35 "poppler/CourierObliqueWidths.gperf" { "zacute", 600 }, #line 269 "poppler/CourierObliqueWidths.gperf" { "lcommaaccent", 600 }, { "", 0 }, #line 209 "poppler/CourierObliqueWidths.gperf" { "Euro", 600 }, { "", 0 }, #line 291 "poppler/CourierObliqueWidths.gperf" { "Sacute", 600 }, #line 323 "poppler/CourierObliqueWidths.gperf" { "greater", 600 }, #line 244 "poppler/CourierObliqueWidths.gperf" { "two", 600 }, { "", 0 }, #line 220 "poppler/CourierObliqueWidths.gperf" { "Thorn", 600 }, #line 256 "poppler/CourierObliqueWidths.gperf" { "asciicircum", 600 }, #line 126 "poppler/CourierObliqueWidths.gperf" { "hungarumlaut", 600 }, { "", 0 }, #line 212 "poppler/CourierObliqueWidths.gperf" { "zero", 600 }, { "", 0 }, #line 40 "poppler/CourierObliqueWidths.gperf" { "emdash", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 116 "poppler/CourierObliqueWidths.gperf" { "divide", 600 }, { "", 0 }, #line 271 "poppler/CourierObliqueWidths.gperf" { "ohungarumlaut", 600 }, #line 262 "poppler/CourierObliqueWidths.gperf" { "ampersand", 600 }, { "", 0 }, #line 164 "poppler/CourierObliqueWidths.gperf" { "ecircumflex", 600 }, { "", 0 }, { "", 0 }, #line 106 "poppler/CourierObliqueWidths.gperf" { "ring", 600 }, { "", 0 }, #line 320 "poppler/CourierObliqueWidths.gperf" { "period", 600 }, { "", 0 }, #line 318 "poppler/CourierObliqueWidths.gperf" { "guilsinglleft", 600 }, #line 155 "poppler/CourierObliqueWidths.gperf" { "guilsinglright", 600 }, { "", 0 }, { "", 0 }, #line 307 "poppler/CourierObliqueWidths.gperf" { "imacron", 600 }, { "", 0 }, #line 61 "poppler/CourierObliqueWidths.gperf" { "periodcentered", 600 }, { "", 0 }, #line 227 "poppler/CourierObliqueWidths.gperf" { "Oacute", 600 }, { "", 0 }, #line 294 "poppler/CourierObliqueWidths.gperf" { "sterling", 600 }, { "", 0 }, { "", 0 }, #line 299 "poppler/CourierObliqueWidths.gperf" { "acircumflex", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 33 "poppler/CourierObliqueWidths.gperf" { "minus", 600 }, #line 312 "poppler/CourierObliqueWidths.gperf" { "Atilde", 600 }, #line 148 "poppler/CourierObliqueWidths.gperf" { "Emacron", 600 }, { "", 0 }, { "", 0 }, #line 257 "poppler/CourierObliqueWidths.gperf" { "aring", 600 }, #line 261 "poppler/CourierObliqueWidths.gperf" { "Iacute", 600 }, #line 183 "poppler/CourierObliqueWidths.gperf" { "umacron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 221 "poppler/CourierObliqueWidths.gperf" { "zcaron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 133 "poppler/CourierObliqueWidths.gperf" { "Scaron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 248 "poppler/CourierObliqueWidths.gperf" { "ocircumflex", 600 }, { "", 0 }, { "", 0 }, #line 189 "poppler/CourierObliqueWidths.gperf" { "idieresis", 600 }, { "", 0 }, #line 157 "poppler/CourierObliqueWidths.gperf" { "quotesingle", 600 }, #line 277 "poppler/CourierObliqueWidths.gperf" { "quotedblbase", 600 }, { "", 0 }, #line 268 "poppler/CourierObliqueWidths.gperf" { "quotesinglbase", 600 }, { "", 0 }, #line 107 "poppler/CourierObliqueWidths.gperf" { "p", 600 }, #line 132 "poppler/CourierObliqueWidths.gperf" { "greaterequal", 600 }, { "", 0 }, #line 326 "poppler/CourierObliqueWidths.gperf" { "quoteleft", 600 }, #line 179 "poppler/CourierObliqueWidths.gperf" { "quoteright", 600 }, { "", 0 }, #line 154 "poppler/CourierObliqueWidths.gperf" { "quotedblleft", 600 }, #line 304 "poppler/CourierObliqueWidths.gperf" { "quotedblright", 600 }, #line 169 "poppler/CourierObliqueWidths.gperf" { "Edieresis", 600 }, { "", 0 }, #line 128 "poppler/CourierObliqueWidths.gperf" { "Nacute", 600 }, #line 131 "poppler/CourierObliqueWidths.gperf" { "mu", 600 }, { "", 0 }, #line 198 "poppler/CourierObliqueWidths.gperf" { "udieresis", 600 }, { "", 0 }, #line 270 "poppler/CourierObliqueWidths.gperf" { "Yacute", 600 }, #line 253 "poppler/CourierObliqueWidths.gperf" { "eogonek", 600 }, #line 80 "poppler/CourierObliqueWidths.gperf" { "question", 600 }, { "", 0 }, #line 313 "poppler/CourierObliqueWidths.gperf" { "breve", 600 }, #line 77 "poppler/CourierObliqueWidths.gperf" { "V", 600 }, #line 39 "poppler/CourierObliqueWidths.gperf" { "questiondown", 600 }, { "", 0 }, #line 266 "poppler/CourierObliqueWidths.gperf" { "plus", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 149 "poppler/CourierObliqueWidths.gperf" { "ellipsis", 600 }, { "", 0 }, { "", 0 }, #line 319 "poppler/CourierObliqueWidths.gperf" { "exclam", 600 }, { "", 0 }, { "", 0 }, #line 219 "poppler/CourierObliqueWidths.gperf" { "braceleft", 600 }, #line 303 "poppler/CourierObliqueWidths.gperf" { "braceright", 600 }, #line 156 "poppler/CourierObliqueWidths.gperf" { "hyphen", 600 }, #line 48 "poppler/CourierObliqueWidths.gperf" { "aogonek", 600 }, #line 314 "poppler/CourierObliqueWidths.gperf" { "bar", 600 }, { "", 0 }, #line 311 "poppler/CourierObliqueWidths.gperf" { "zdotaccent", 600 }, #line 153 "poppler/CourierObliqueWidths.gperf" { "lslash", 600 }, #line 86 "poppler/CourierObliqueWidths.gperf" { "Gcommaaccent", 600 }, #line 309 "poppler/CourierObliqueWidths.gperf" { "currency", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 75 "poppler/CourierObliqueWidths.gperf" { "U", 600 }, #line 27 "poppler/CourierObliqueWidths.gperf" { "onehalf", 600 }, #line 109 "poppler/CourierObliqueWidths.gperf" { "uhungarumlaut", 600 }, { "", 0 }, { "", 0 }, #line 147 "poppler/CourierObliqueWidths.gperf" { "Otilde", 600 }, { "", 0 }, #line 287 "poppler/CourierObliqueWidths.gperf" { "guillemotleft", 600 }, #line 202 "poppler/CourierObliqueWidths.gperf" { "guillemotright", 600 }, { "", 0 }, #line 247 "poppler/CourierObliqueWidths.gperf" { "Lacute", 600 }, #line 163 "poppler/CourierObliqueWidths.gperf" { "Umacron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 18 "poppler/CourierObliqueWidths.gperf" { "Zacute", 600 }, { "", 0 }, #line 295 "poppler/CourierObliqueWidths.gperf" { "notequal", 600 }, #line 143 "poppler/CourierObliqueWidths.gperf" { "trademark", 600 }, { "", 0 }, #line 265 "poppler/CourierObliqueWidths.gperf" { "Ncaron", 600 }, #line 200 "poppler/CourierObliqueWidths.gperf" { "Scommaaccent", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 181 "poppler/CourierObliqueWidths.gperf" { "perthousand", 600 }, { "", 0 }, #line 240 "poppler/CourierObliqueWidths.gperf" { "six", 600 }, { "", 0 }, { "", 0 }, #line 302 "poppler/CourierObliqueWidths.gperf" { "icircumflex", 600 }, { "", 0 }, #line 214 "poppler/CourierObliqueWidths.gperf" { "Scedilla", 600 }, { "", 0 }, { "", 0 }, #line 93 "poppler/CourierObliqueWidths.gperf" { "bullet", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 108 "poppler/CourierObliqueWidths.gperf" { "q", 600 }, #line 289 "poppler/CourierObliqueWidths.gperf" { "Amacron", 600 }, { "", 0 }, { "", 0 }, #line 127 "poppler/CourierObliqueWidths.gperf" { "Idotaccent", 600 }, #line 141 "poppler/CourierObliqueWidths.gperf" { "Ecircumflex", 600 }, { "", 0 }, #line 315 "poppler/CourierObliqueWidths.gperf" { "fraction", 600 }, #line 180 "poppler/CourierObliqueWidths.gperf" { "Udieresis", 600 }, { "", 0 }, #line 176 "poppler/CourierObliqueWidths.gperf" { "ucircumflex", 600 }, { "", 0 }, { "", 0 }, #line 197 "poppler/CourierObliqueWidths.gperf" { "five", 600 }, { "", 0 }, #line 14 "poppler/CourierObliqueWidths.gperf" { "Ntilde", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 267 "poppler/CourierObliqueWidths.gperf" { "uring", 600 }, #line 45 "poppler/CourierObliqueWidths.gperf" { "A", 600 }, { "", 0 }, { "", 0 }, #line 84 "poppler/CourierObliqueWidths.gperf" { "four", 600 }, { "", 0 }, #line 187 "poppler/CourierObliqueWidths.gperf" { "egrave", 600 }, { "", 0 }, { "", 0 }, #line 241 "poppler/CourierObliqueWidths.gperf" { "paragraph", 600 }, { "", 0 }, #line 29 "poppler/CourierObliqueWidths.gperf" { "Lcaron", 600 }, { "", 0 }, { "", 0 }, #line 325 "poppler/CourierObliqueWidths.gperf" { "brokenbar", 600 }, { "", 0 }, #line 199 "poppler/CourierObliqueWidths.gperf" { "Zcaron", 600 }, { "", 0 }, { "", 0 }, #line 165 "poppler/CourierObliqueWidths.gperf" { "Adieresis", 600 }, { "", 0 }, #line 122 "poppler/CourierObliqueWidths.gperf" { "y", 600 }, #line 16 "poppler/CourierObliqueWidths.gperf" { "kcommaaccent", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 76 "poppler/CourierObliqueWidths.gperf" { "agrave", 600 }, { "", 0 }, #line 69 "poppler/CourierObliqueWidths.gperf" { "Uhungarumlaut", 600 }, #line 204 "poppler/CourierObliqueWidths.gperf" { "ydieresis", 600 }, #line 168 "poppler/CourierObliqueWidths.gperf" { "slash", 600 }, #line 229 "poppler/CourierObliqueWidths.gperf" { "ogonek", 600 }, #line 151 "poppler/CourierObliqueWidths.gperf" { "AE", 600 }, #line 192 "poppler/CourierObliqueWidths.gperf" { "asterisk", 600 }, { "", 0 }, { "", 0 }, #line 111 "poppler/CourierObliqueWidths.gperf" { "twosuperior", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 53 "poppler/CourierObliqueWidths.gperf" { "G", 600 }, #line 58 "poppler/CourierObliqueWidths.gperf" { "iogonek", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 17 "poppler/CourierObliqueWidths.gperf" { "Ncommaaccent", 600 }, { "", 0 }, #line 21 "poppler/CourierObliqueWidths.gperf" { "plusminus", 600 }, { "", 0 }, #line 230 "poppler/CourierObliqueWidths.gperf" { "ograve", 600 }, { "", 0 }, #line 129 "poppler/CourierObliqueWidths.gperf" { "quotedbl", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 233 "poppler/CourierObliqueWidths.gperf" { "Eogonek", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 120 "poppler/CourierObliqueWidths.gperf" { "w", 600 }, #line 259 "poppler/CourierObliqueWidths.gperf" { "uogonek", 600 }, { "", 0 }, #line 59 "poppler/CourierObliqueWidths.gperf" { "backslash", 600 }, #line 81 "poppler/CourierObliqueWidths.gperf" { "equal", 600 }, { "", 0 }, #line 38 "poppler/CourierObliqueWidths.gperf" { "Omacron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 121 "poppler/CourierObliqueWidths.gperf" { "x", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 298 "poppler/CourierObliqueWidths.gperf" { "Zdotaccent", 600 }, #line 152 "poppler/CourierObliqueWidths.gperf" { "Ucircumflex", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 68 "poppler/CourierObliqueWidths.gperf" { "Q", 600 }, #line 296 "poppler/CourierObliqueWidths.gperf" { "Imacron", 600 }, { "", 0 }, { "", 0 }, #line 250 "poppler/CourierObliqueWidths.gperf" { "Uring", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 123 "poppler/CourierObliqueWidths.gperf" { "z", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 22 "poppler/CourierObliqueWidths.gperf" { "circumflex", 600 }, { "", 0 }, #line 251 "poppler/CourierObliqueWidths.gperf" { "Lcommaaccent", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 73 "poppler/CourierObliqueWidths.gperf" { "S", 600 }, { "", 0 }, { "", 0 }, #line 175 "poppler/CourierObliqueWidths.gperf" { "Odieresis", 600 }, { "", 0 }, #line 284 "poppler/CourierObliqueWidths.gperf" { "Acircumflex", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 67 "poppler/CourierObliqueWidths.gperf" { "P", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 238 "poppler/CourierObliqueWidths.gperf" { "Aring", 600 }, #line 96 "poppler/CourierObliqueWidths.gperf" { "Oslash", 600 }, #line 114 "poppler/CourierObliqueWidths.gperf" { "OE", 600 }, { "", 0 }, #line 171 "poppler/CourierObliqueWidths.gperf" { "Idieresis", 600 }, { "", 0 }, #line 62 "poppler/CourierObliqueWidths.gperf" { "M", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 282 "poppler/CourierObliqueWidths.gperf" { "fi", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 56 "poppler/CourierObliqueWidths.gperf" { "J", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 263 "poppler/CourierObliqueWidths.gperf" { "igrave", 600 }, { "", 0 }, #line 255 "poppler/CourierObliqueWidths.gperf" { "Ohungarumlaut", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 243 "poppler/CourierObliqueWidths.gperf" { "Uogonek", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 87 "poppler/CourierObliqueWidths.gperf" { "b", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 167 "poppler/CourierObliqueWidths.gperf" { "Egrave", 600 }, { "", 0 }, { "", 0 }, #line 182 "poppler/CourierObliqueWidths.gperf" { "Ydieresis", 600 }, { "", 0 }, #line 195 "poppler/CourierObliqueWidths.gperf" { "ugrave", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 211 "poppler/CourierObliqueWidths.gperf" { "multiply", 600 }, { "", 0 }, { "", 0 }, #line 66 "poppler/CourierObliqueWidths.gperf" { "O", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 31 "poppler/CourierObliqueWidths.gperf" { "Aogonek", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 279 "poppler/CourierObliqueWidths.gperf" { "florin", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 226 "poppler/CourierObliqueWidths.gperf" { "Ocircumflex", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 283 "poppler/CourierObliqueWidths.gperf" { "fl", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 55 "poppler/CourierObliqueWidths.gperf" { "I", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 286 "poppler/CourierObliqueWidths.gperf" { "Icircumflex", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 54 "poppler/CourierObliqueWidths.gperf" { "H", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 134 "poppler/CourierObliqueWidths.gperf" { "Lslash", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 99 "poppler/CourierObliqueWidths.gperf" { "k", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 184 "poppler/CourierObliqueWidths.gperf" { "abreve", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 217 "poppler/CourierObliqueWidths.gperf" { "partialdiff", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 52 "poppler/CourierObliqueWidths.gperf" { "F", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 178 "poppler/CourierObliqueWidths.gperf" { "Ugrave", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 91 "poppler/CourierObliqueWidths.gperf" { "f", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 119 "poppler/CourierObliqueWidths.gperf" { "v", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 63 "poppler/CourierObliqueWidths.gperf" { "N", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 41 "poppler/CourierObliqueWidths.gperf" { "Agrave", 600 }, #line 34 "poppler/CourierObliqueWidths.gperf" { "Iogonek", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 82 "poppler/CourierObliqueWidths.gperf" { "Y", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 46 "poppler/CourierObliqueWidths.gperf" { "B", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 177 "poppler/CourierObliqueWidths.gperf" { "bracketleft", 600 }, #line 260 "poppler/CourierObliqueWidths.gperf" { "bracketright", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 142 "poppler/CourierObliqueWidths.gperf" { "gbreve", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 215 "poppler/CourierObliqueWidths.gperf" { "Ograve", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 60 "poppler/CourierObliqueWidths.gperf" { "L", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 246 "poppler/CourierObliqueWidths.gperf" { "Igrave", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 83 "poppler/CourierObliqueWidths.gperf" { "Z", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 162 "poppler/CourierObliqueWidths.gperf" { "Abreve", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 124 "poppler/CourierObliqueWidths.gperf" { "Gbreve", 600 } }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) { register unsigned int key = hash(str, len); if (key <= MAX_HASH_VALUE) { register const char *s = wordlist[key].name; if (*str == *s && !strcmp(str + 1, s + 1)) return &wordlist[key]; } } return 0; } #line 330 "poppler/CourierObliqueWidths.gperf" poppler-24.02.0/poppler/CourierWidths.gperf000066400000000000000000000101341455701731300206460ustar00rootroot00000000000000%{ #include #include "BuiltinFontWidth.h" %} %language=ANSI-C %define initializer-suffix ,0 %define lookup-function-name CourierWidthsLookup %struct-type %omit-struct-type %readonly-tables struct BuiltinFontWidth %% #### Ntilde, 600 rcaron, 600 kcommaaccent, 600 Ncommaaccent, 600 Zacute, 600 comma, 600 cedilla, 600 plusminus, 603 circumflex, 600 dotaccent, 600 edotaccent, 600 asciitilde, 600 colon, 600 onehalf, 600 dollar, 600 Lcaron, 600 ntilde, 600 Aogonek, 600 ncommaaccent, 600 minus, 600 Iogonek, 600 zacute, 600 yen, 600 space, 600 Omacron, 600 questiondown, 600 emdash, 600 Agrave, 600 three, 600 numbersign, 600 lcaron, 600 A, 600 B, 600 C, 600 aogonek, 600 D, 600 E, 600 onequarter, 600 F, 600 G, 600 H, 600 I, 600 J, 600 K, 600 iogonek, 600 L, 600 backslash, 600 periodcentered, 600 M, 600 N, 600 omacron, 600 Tcommaaccent, 600 O, 600 P, 600 Q, 600 Uhungarumlaut, 600 R, 600 Aacute, 600 caron, 600 S, 600 T, 600 U, 600 agrave, 600 V, 600 W, 600 equal, 600 question, 600 X, 600 Y, 600 Z, 600 four, 600 a, 600 Gcommaaccent, 600 b, 600 c, 600 d, 600 e, 600 f, 600 g, 600 bullet, 600 h, 600 i, 600 Oslash, 600 dagger, 600 j, 600 k, 600 l, 600 m, 600 n, 600 tcommaaccent, 600 o, 600 ordfeminine, 600 ring, 600 p, 600 q, 600 uhungarumlaut, 600 r, 600 twosuperior, 600 aacute, 600 s, 600 OE, 600 t, 600 divide, 600 u, 600 Ccaron, 600 v, 600 w, 600 x, 600 y, 600 z, 600 Gbreve, 600 commaaccent, 600 hungarumlaut, 600 Idotaccent, 600 Nacute, 600 quotedbl, 600 gcommaaccent, 600 mu, 600 greaterequal, 600 Scaron, 600 Lslash, 600 semicolon, 600 oslash, 600 lessequal, 600 lozenge, 600 parenright, 600 ccaron, 600 Ecircumflex, 600 gbreve, 600 trademark, 600 daggerdbl, 600 nacute, 600 macron, 600 Otilde, 600 Emacron, 600 ellipsis, 600 scaron, 600 AE, 600 Ucircumflex, 600 lslash, 600 quotedblleft, 600 hyphen, 600 guilsinglright, 600 quotesingle, 600 eight, 600 exclamdown, 600 endash, 600 oe, 600 Abreve, 600 Umacron, 600 ecircumflex, 600 Adieresis, 600 copyright, 600 Egrave, 600 slash, 600 Edieresis, 600 otilde, 600 Idieresis, 600 parenleft, 600 one, 600 emacron, 600 Odieresis, 600 ucircumflex, 600 bracketleft, 600 Ugrave, 600 quoteright, 600 Udieresis, 600 perthousand, 600 Ydieresis, 600 umacron, 600 abreve, 600 Eacute, 600 adieresis, 600 egrave, 600 edieresis, 600 idieresis, 600 Eth, 600 ae, 600 asterisk, 600 odieresis, 600 Uacute, 600 ugrave, 600 five, 600 nine, 600 udieresis, 600 Zcaron, 600 Scommaaccent, 600 threequarters, 600 guillemotright, 600 Ccedilla, 600 ydieresis, 600 tilde, 600 at, 600 eacute, 600 underscore, 600 Euro, 600 Dcroat, 600 zero, 600 multiply, 600 eth, 600 Scedilla, 600 Racute, 600 Ograve, 600 partialdiff, 600 uacute, 600 braceleft, 600 Thorn, 600 zcaron, 600 scommaaccent, 600 ccedilla, 600 Dcaron, 600 dcroat, 600 scedilla, 600 Oacute, 600 Ocircumflex, 600 ogonek, 600 ograve, 600 racute, 600 Tcaron, 600 Eogonek, 600 thorn, 600 degree, 600 registered, 600 radical, 600 Aring, 600 percent, 600 six, 600 paragraph, 600 dcaron, 600 Uogonek, 600 two, 600 summation, 600 Igrave, 600 Lacute, 600 ocircumflex, 600 oacute, 600 Uring, 600 Lcommaaccent, 600 tcaron, 600 eogonek, 600 Delta, 600 Ohungarumlaut, 600 asciicircum, 600 aring, 600 grave, 600 uogonek, 600 bracketright, 600 ampersand, 600 Iacute, 600 lacute, 600 igrave, 600 Ncaron, 600 plus, 600 uring, 600 quotesinglbase, 600 lcommaaccent, 600 Yacute, 600 ohungarumlaut, 600 threesuperior, 600 acute, 600 section, 600 dieresis, 600 quotedblbase, 600 iacute, 600 ncaron, 600 florin, 600 yacute, 600 Rcommaaccent, 600 fi, 600 fl, 600 Acircumflex, 600 Cacute, 600 Icircumflex, 600 guillemotleft, 600 germandbls, 600 seven, 600 Amacron, 600 Sacute, 600 ordmasculine, 600 dotlessi, 600 sterling, 600 notequal, 600 Imacron, 600 rcommaaccent, 600 Zdotaccent, 600 acircumflex, 600 cacute, 600 Ecaron, 600 braceright, 600 icircumflex, 600 quotedblright, 600 amacron, 600 sacute, 600 imacron, 600 cent, 600 currency, 600 logicalnot, 600 zdotaccent, 600 Atilde, 600 breve, 600 bar, 600 fraction, 600 less, 600 ecaron, 600 guilsinglleft, 600 exclam, 600 period, 600 Rcaron, 600 Kcommaaccent, 600 greater, 600 atilde, 600 brokenbar, 600 quoteleft, 600 Edotaccent, 600 onesuperior, 600 #### %% poppler-24.02.0/poppler/CourierWidths.pregenerated.c000066400000000000000000002600611455701731300224370ustar00rootroot00000000000000/* ANSI-C code produced by gperf version 3.1 */ /* Command-line: gperf poppler/CourierWidths.gperf */ /* Computed positions: -k'1-2,5,$' */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) && ('-' == 45) && ('.' == 46) && ('/' == 47) \ && ('0' == 48) && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) && ('=' == 61) && ('>' == 62) \ && ('?' == 63) && ('A' == 65) && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) && ('N' == 78) \ && ('O' == 79) && ('P' == 80) && ('Q' == 81) && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) && ('k' == 107) && ('l' == 108) \ && ('m' == 109) && ('n' == 110) && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) /* The character set is not based on ISO-646. */ # error "gperf generated tables don't work with this execution character set. Please report a bug to ." #endif #line 1 "poppler/CourierWidths.gperf" #include #include "BuiltinFontWidth.h" #define TOTAL_KEYWORDS 315 #define MIN_WORD_LENGTH 1 #define MAX_WORD_LENGTH 14 #define MIN_HASH_VALUE 1 #define MAX_HASH_VALUE 1041 /* maximum key range = 1041, duplicates = 0 */ #ifdef __GNUC__ __inline #else # ifdef __cplusplus inline # endif #endif static unsigned int hash(register const char *str, register size_t len) { static const unsigned short asso_values[] = { 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 270, 415, 28, 8, 150, 390, 290, 375, 370, 335, 5, 455, 330, 405, 355, 325, 310, 3, 320, 160, 240, 225, 145, 70, 410, 460, 1042, 1042, 1042, 1042, 1042, 1042, 20, 345, 30, 115, 0, 395, 140, 165, 135, 35, 380, 170, 130, 15, 45, 215, 260, 100, 65, 10, 155, 400, 300, 305, 280, 315, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042 }; register unsigned int hval = len; switch (hval) { default: hval += asso_values[(unsigned char)str[4]]; /*FALLTHROUGH*/ case 4: case 3: case 2: hval += asso_values[(unsigned char)str[1]]; /*FALLTHROUGH*/ case 1: hval += asso_values[(unsigned char)str[0]]; break; } return hval + asso_values[(unsigned char)str[len - 1]]; } const struct BuiltinFontWidth *CourierWidthsLookup(register const char *str, register size_t len) { static const struct BuiltinFontWidth wordlist[] = { { "", 0 }, #line 90 "poppler/CourierWidths.gperf" { "e", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 70 "poppler/CourierWidths.gperf" { "R", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 57 "poppler/CourierWidths.gperf" { "K", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 49 "poppler/CourierWidths.gperf" { "D", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 115 "poppler/CourierWidths.gperf" { "t", 600 }, #line 191 "poppler/CourierWidths.gperf" { "ae", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 102 "poppler/CourierWidths.gperf" { "n", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 207 "poppler/CourierWidths.gperf" { "eacute", 600 }, { "", 0 }, { "", 0 }, #line 215 "poppler/CourierWidths.gperf" { "Racute", 600 }, { "", 0 }, #line 85 "poppler/CourierWidths.gperf" { "a", 600 }, #line 206 "poppler/CourierWidths.gperf" { "at", 600 }, { "", 0 }, #line 308 "poppler/CourierWidths.gperf" { "cent", 600 }, { "", 0 }, { "", 0 }, #line 161 "poppler/CourierWidths.gperf" { "oe", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 145 "poppler/CourierWidths.gperf" { "nacute", 600 }, { "", 0 }, #line 254 "poppler/CourierWidths.gperf" { "Delta", 600 }, { "", 0 }, #line 273 "poppler/CourierWidths.gperf" { "acute", 600 }, #line 112 "poppler/CourierWidths.gperf" { "aacute", 600 }, #line 47 "poppler/CourierWidths.gperf" { "C", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 88 "poppler/CourierWidths.gperf" { "c", 600 }, { "", 0 }, #line 173 "poppler/CourierWidths.gperf" { "one", 600 }, #line 285 "poppler/CourierWidths.gperf" { "Cacute", 600 }, { "", 0 }, #line 300 "poppler/CourierWidths.gperf" { "cacute", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 98 "poppler/CourierWidths.gperf" { "j", 600 }, { "", 0 }, { "", 0 }, #line 210 "poppler/CourierWidths.gperf" { "Dcroat", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 249 "poppler/CourierWidths.gperf" { "oacute", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 72 "poppler/CourierWidths.gperf" { "caron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 104 "poppler/CourierWidths.gperf" { "o", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 317 "poppler/CourierWidths.gperf" { "ecaron", 600 }, { "", 0 }, { "", 0 }, #line 321 "poppler/CourierWidths.gperf" { "Rcaron", 600 }, #line 289 "poppler/CourierWidths.gperf" { "seven", 600 }, #line 306 "poppler/CourierWidths.gperf" { "sacute", 600 }, { "", 0 }, { "", 0 }, #line 224 "poppler/CourierWidths.gperf" { "Dcaron", 600 }, { "", 0 }, #line 252 "poppler/CourierWidths.gperf" { "tcaron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 26 "poppler/CourierWidths.gperf" { "colon", 600 }, #line 278 "poppler/CourierWidths.gperf" { "ncaron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 125 "poppler/CourierWidths.gperf" { "commaaccent", 600 }, { "", 0 }, { "", 0 }, #line 135 "poppler/CourierWidths.gperf" { "semicolon", 600 }, #line 19 "poppler/CourierWidths.gperf" { "comma", 600 }, #line 235 "poppler/CourierWidths.gperf" { "degree", 600 }, { "", 0 }, { "", 0 }, #line 118 "poppler/CourierWidths.gperf" { "Ccaron", 600 }, { "", 0 }, #line 140 "poppler/CourierWidths.gperf" { "ccaron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 113 "poppler/CourierWidths.gperf" { "s", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 231 "poppler/CourierWidths.gperf" { "racute", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 81 "poppler/CourierWidths.gperf" { "X", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 30 "poppler/CourierWidths.gperf" { "ntilde", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 205 "poppler/CourierWidths.gperf" { "tilde", 600 }, #line 324 "poppler/CourierWidths.gperf" { "atilde", 600 }, { "", 0 }, { "", 0 }, #line 197 "poppler/CourierWidths.gperf" { "nine", 600 }, #line 24 "poppler/CourierWidths.gperf" { "edotaccent", 600 }, #line 105 "poppler/CourierWidths.gperf" { "ordfeminine", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 158 "poppler/CourierWidths.gperf" { "eight", 600 }, #line 150 "poppler/CourierWidths.gperf" { "scaron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 277 "poppler/CourierWidths.gperf" { "iacute", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 170 "poppler/CourierWidths.gperf" { "otilde", 600 }, #line 292 "poppler/CourierWidths.gperf" { "ordmasculine", 600 }, #line 213 "poppler/CourierWidths.gperf" { "eth", 600 }, { "", 0 }, #line 42 "poppler/CourierWidths.gperf" { "three", 600 }, #line 225 "poppler/CourierWidths.gperf" { "dcroat", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 281 "poppler/CourierWidths.gperf" { "Rcommaaccent", 600 }, #line 185 "poppler/CourierWidths.gperf" { "Eacute", 600 }, #line 322 "poppler/CourierWidths.gperf" { "Kcommaaccent", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 218 "poppler/CourierWidths.gperf" { "uacute", 600 }, #line 103 "poppler/CourierWidths.gperf" { "tcommaaccent", 600 }, { "", 0 }, #line 166 "poppler/CourierWidths.gperf" { "copyright", 600 }, #line 43 "poppler/CourierWidths.gperf" { "numbersign", 600 }, #line 15 "poppler/CourierWidths.gperf" { "rcaron", 600 }, #line 32 "poppler/CourierWidths.gperf" { "ncommaaccent", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 110 "poppler/CourierWidths.gperf" { "r", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 263 "poppler/CourierWidths.gperf" { "lacute", 600 }, { "", 0 }, { "", 0 }, #line 23 "poppler/CourierWidths.gperf" { "dotaccent", 600 }, #line 234 "poppler/CourierWidths.gperf" { "thorn", 600 }, #line 242 "poppler/CourierWidths.gperf" { "dcaron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 146 "poppler/CourierWidths.gperf" { "macron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 203 "poppler/CourierWidths.gperf" { "Ccedilla", 600 }, #line 274 "poppler/CourierWidths.gperf" { "section", 600 }, #line 223 "poppler/CourierWidths.gperf" { "ccedilla", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 20 "poppler/CourierWidths.gperf" { "cedilla", 600 }, { "", 0 }, { "", 0 }, #line 25 "poppler/CourierWidths.gperf" { "asciitilde", 600 }, #line 89 "poppler/CourierWidths.gperf" { "d", 600 }, #line 239 "poppler/CourierWidths.gperf" { "percent", 600 }, { "", 0 }, { "", 0 }, #line 288 "poppler/CourierWidths.gperf" { "germandbls", 600 }, { "", 0 }, #line 138 "poppler/CourierWidths.gperf" { "lozenge", 600 }, { "", 0 }, #line 316 "poppler/CourierWidths.gperf" { "less", 600 }, { "", 0 }, #line 97 "poppler/CourierWidths.gperf" { "dagger", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 258 "poppler/CourierWidths.gperf" { "grave", 600 }, #line 301 "poppler/CourierWidths.gperf" { "Ecaron", 600 }, #line 222 "poppler/CourierWidths.gperf" { "scommaaccent", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 160 "poppler/CourierWidths.gperf" { "endash", 600 }, #line 174 "poppler/CourierWidths.gperf" { "emacron", 600 }, #line 201 "poppler/CourierWidths.gperf" { "threequarters", 600 }, { "", 0 }, { "", 0 }, #line 232 "poppler/CourierWidths.gperf" { "Tcaron", 600 }, { "", 0 }, #line 226 "poppler/CourierWidths.gperf" { "scedilla", 600 }, { "", 0 }, { "", 0 }, #line 101 "poppler/CourierWidths.gperf" { "m", 600 }, { "", 0 }, { "", 0 }, #line 245 "poppler/CourierWidths.gperf" { "summation", 600 }, #line 310 "poppler/CourierWidths.gperf" { "logicalnot", 600 }, #line 44 "poppler/CourierWidths.gperf" { "lcaron", 600 }, { "", 0 }, { "", 0 }, #line 172 "poppler/CourierWidths.gperf" { "parenleft", 600 }, #line 139 "poppler/CourierWidths.gperf" { "parenright", 600 }, #line 95 "poppler/CourierWidths.gperf" { "i", 600 }, #line 305 "poppler/CourierWidths.gperf" { "amacron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 194 "poppler/CourierWidths.gperf" { "Uacute", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 208 "poppler/CourierWidths.gperf" { "underscore", 600 }, #line 92 "poppler/CourierWidths.gperf" { "g", 600 }, #line 297 "poppler/CourierWidths.gperf" { "rcommaaccent", 600 }, { "", 0 }, { "", 0 }, #line 37 "poppler/CourierWidths.gperf" { "space", 600 }, #line 28 "poppler/CourierWidths.gperf" { "dollar", 600 }, { "", 0 }, #line 272 "poppler/CourierWidths.gperf" { "threesuperior", 600 }, #line 188 "poppler/CourierWidths.gperf" { "edieresis", 600 }, #line 236 "poppler/CourierWidths.gperf" { "registered", 600 }, #line 78 "poppler/CourierWidths.gperf" { "W", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 64 "poppler/CourierWidths.gperf" { "omacron", 600 }, #line 36 "poppler/CourierWidths.gperf" { "yen", 600 }, { "", 0 }, { "", 0 }, #line 50 "poppler/CourierWidths.gperf" { "E", 600 }, { "", 0 }, #line 293 "poppler/CourierWidths.gperf" { "dotlessi", 600 }, { "", 0 }, #line 327 "poppler/CourierWidths.gperf" { "Edotaccent", 600 }, #line 71 "poppler/CourierWidths.gperf" { "Aacute", 600 }, { "", 0 }, { "", 0 }, #line 186 "poppler/CourierWidths.gperf" { "adieresis", 600 }, { "", 0 }, #line 117 "poppler/CourierWidths.gperf" { "u", 600 }, { "", 0 }, { "", 0 }, #line 144 "poppler/CourierWidths.gperf" { "daggerdbl", 600 }, { "", 0 }, #line 280 "poppler/CourierWidths.gperf" { "yacute", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 74 "poppler/CourierWidths.gperf" { "T", 600 }, #line 130 "poppler/CourierWidths.gperf" { "gcommaaccent", 600 }, #line 275 "poppler/CourierWidths.gperf" { "dieresis", 600 }, { "", 0 }, #line 51 "poppler/CourierWidths.gperf" { "onequarter", 600 }, #line 328 "poppler/CourierWidths.gperf" { "onesuperior", 600 }, #line 237 "poppler/CourierWidths.gperf" { "radical", 600 }, #line 190 "poppler/CourierWidths.gperf" { "Eth", 600 }, { "", 0 }, { "", 0 }, #line 94 "poppler/CourierWidths.gperf" { "h", 600 }, { "", 0 }, { "", 0 }, #line 193 "poppler/CourierWidths.gperf" { "odieresis", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 100 "poppler/CourierWidths.gperf" { "l", 600 }, #line 65 "poppler/CourierWidths.gperf" { "Tcommaaccent", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 136 "poppler/CourierWidths.gperf" { "oslash", 600 }, { "", 0 }, { "", 0 }, #line 137 "poppler/CourierWidths.gperf" { "lessequal", 600 }, #line 159 "poppler/CourierWidths.gperf" { "exclamdown", 600 }, #line 35 "poppler/CourierWidths.gperf" { "zacute", 600 }, #line 269 "poppler/CourierWidths.gperf" { "lcommaaccent", 600 }, { "", 0 }, #line 209 "poppler/CourierWidths.gperf" { "Euro", 600 }, { "", 0 }, #line 291 "poppler/CourierWidths.gperf" { "Sacute", 600 }, #line 323 "poppler/CourierWidths.gperf" { "greater", 600 }, #line 244 "poppler/CourierWidths.gperf" { "two", 600 }, { "", 0 }, #line 220 "poppler/CourierWidths.gperf" { "Thorn", 600 }, #line 256 "poppler/CourierWidths.gperf" { "asciicircum", 600 }, #line 126 "poppler/CourierWidths.gperf" { "hungarumlaut", 600 }, { "", 0 }, #line 211 "poppler/CourierWidths.gperf" { "zero", 600 }, { "", 0 }, #line 40 "poppler/CourierWidths.gperf" { "emdash", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 116 "poppler/CourierWidths.gperf" { "divide", 600 }, { "", 0 }, #line 271 "poppler/CourierWidths.gperf" { "ohungarumlaut", 600 }, #line 261 "poppler/CourierWidths.gperf" { "ampersand", 600 }, { "", 0 }, #line 164 "poppler/CourierWidths.gperf" { "ecircumflex", 600 }, { "", 0 }, { "", 0 }, #line 106 "poppler/CourierWidths.gperf" { "ring", 600 }, { "", 0 }, #line 320 "poppler/CourierWidths.gperf" { "period", 600 }, { "", 0 }, #line 318 "poppler/CourierWidths.gperf" { "guilsinglleft", 600 }, #line 156 "poppler/CourierWidths.gperf" { "guilsinglright", 600 }, { "", 0 }, { "", 0 }, #line 307 "poppler/CourierWidths.gperf" { "imacron", 600 }, { "", 0 }, #line 61 "poppler/CourierWidths.gperf" { "periodcentered", 600 }, { "", 0 }, #line 227 "poppler/CourierWidths.gperf" { "Oacute", 600 }, { "", 0 }, #line 294 "poppler/CourierWidths.gperf" { "sterling", 600 }, { "", 0 }, { "", 0 }, #line 299 "poppler/CourierWidths.gperf" { "acircumflex", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 33 "poppler/CourierWidths.gperf" { "minus", 600 }, #line 312 "poppler/CourierWidths.gperf" { "Atilde", 600 }, #line 148 "poppler/CourierWidths.gperf" { "Emacron", 600 }, { "", 0 }, { "", 0 }, #line 257 "poppler/CourierWidths.gperf" { "aring", 600 }, #line 262 "poppler/CourierWidths.gperf" { "Iacute", 600 }, #line 183 "poppler/CourierWidths.gperf" { "umacron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 221 "poppler/CourierWidths.gperf" { "zcaron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 133 "poppler/CourierWidths.gperf" { "Scaron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 248 "poppler/CourierWidths.gperf" { "ocircumflex", 600 }, { "", 0 }, { "", 0 }, #line 189 "poppler/CourierWidths.gperf" { "idieresis", 600 }, { "", 0 }, #line 157 "poppler/CourierWidths.gperf" { "quotesingle", 600 }, #line 276 "poppler/CourierWidths.gperf" { "quotedblbase", 600 }, { "", 0 }, #line 268 "poppler/CourierWidths.gperf" { "quotesinglbase", 600 }, { "", 0 }, #line 107 "poppler/CourierWidths.gperf" { "p", 600 }, #line 132 "poppler/CourierWidths.gperf" { "greaterequal", 600 }, { "", 0 }, #line 326 "poppler/CourierWidths.gperf" { "quoteleft", 600 }, #line 179 "poppler/CourierWidths.gperf" { "quoteright", 600 }, { "", 0 }, #line 154 "poppler/CourierWidths.gperf" { "quotedblleft", 600 }, #line 304 "poppler/CourierWidths.gperf" { "quotedblright", 600 }, #line 169 "poppler/CourierWidths.gperf" { "Edieresis", 600 }, { "", 0 }, #line 128 "poppler/CourierWidths.gperf" { "Nacute", 600 }, #line 131 "poppler/CourierWidths.gperf" { "mu", 600 }, { "", 0 }, #line 198 "poppler/CourierWidths.gperf" { "udieresis", 600 }, { "", 0 }, #line 270 "poppler/CourierWidths.gperf" { "Yacute", 600 }, #line 253 "poppler/CourierWidths.gperf" { "eogonek", 600 }, #line 80 "poppler/CourierWidths.gperf" { "question", 600 }, { "", 0 }, #line 313 "poppler/CourierWidths.gperf" { "breve", 600 }, #line 77 "poppler/CourierWidths.gperf" { "V", 600 }, #line 39 "poppler/CourierWidths.gperf" { "questiondown", 600 }, { "", 0 }, #line 266 "poppler/CourierWidths.gperf" { "plus", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 149 "poppler/CourierWidths.gperf" { "ellipsis", 600 }, { "", 0 }, { "", 0 }, #line 319 "poppler/CourierWidths.gperf" { "exclam", 600 }, { "", 0 }, { "", 0 }, #line 219 "poppler/CourierWidths.gperf" { "braceleft", 600 }, #line 302 "poppler/CourierWidths.gperf" { "braceright", 600 }, #line 155 "poppler/CourierWidths.gperf" { "hyphen", 600 }, #line 48 "poppler/CourierWidths.gperf" { "aogonek", 600 }, #line 314 "poppler/CourierWidths.gperf" { "bar", 600 }, { "", 0 }, #line 311 "poppler/CourierWidths.gperf" { "zdotaccent", 600 }, #line 153 "poppler/CourierWidths.gperf" { "lslash", 600 }, #line 86 "poppler/CourierWidths.gperf" { "Gcommaaccent", 600 }, #line 309 "poppler/CourierWidths.gperf" { "currency", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 75 "poppler/CourierWidths.gperf" { "U", 600 }, #line 27 "poppler/CourierWidths.gperf" { "onehalf", 600 }, #line 109 "poppler/CourierWidths.gperf" { "uhungarumlaut", 600 }, { "", 0 }, { "", 0 }, #line 147 "poppler/CourierWidths.gperf" { "Otilde", 600 }, { "", 0 }, #line 287 "poppler/CourierWidths.gperf" { "guillemotleft", 600 }, #line 202 "poppler/CourierWidths.gperf" { "guillemotright", 600 }, { "", 0 }, #line 247 "poppler/CourierWidths.gperf" { "Lacute", 600 }, #line 163 "poppler/CourierWidths.gperf" { "Umacron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 18 "poppler/CourierWidths.gperf" { "Zacute", 600 }, { "", 0 }, #line 295 "poppler/CourierWidths.gperf" { "notequal", 600 }, #line 143 "poppler/CourierWidths.gperf" { "trademark", 600 }, { "", 0 }, #line 265 "poppler/CourierWidths.gperf" { "Ncaron", 600 }, #line 200 "poppler/CourierWidths.gperf" { "Scommaaccent", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 181 "poppler/CourierWidths.gperf" { "perthousand", 600 }, { "", 0 }, #line 240 "poppler/CourierWidths.gperf" { "six", 600 }, { "", 0 }, { "", 0 }, #line 303 "poppler/CourierWidths.gperf" { "icircumflex", 600 }, { "", 0 }, #line 214 "poppler/CourierWidths.gperf" { "Scedilla", 600 }, { "", 0 }, { "", 0 }, #line 93 "poppler/CourierWidths.gperf" { "bullet", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 108 "poppler/CourierWidths.gperf" { "q", 600 }, #line 290 "poppler/CourierWidths.gperf" { "Amacron", 600 }, { "", 0 }, { "", 0 }, #line 127 "poppler/CourierWidths.gperf" { "Idotaccent", 600 }, #line 141 "poppler/CourierWidths.gperf" { "Ecircumflex", 600 }, { "", 0 }, #line 315 "poppler/CourierWidths.gperf" { "fraction", 600 }, #line 180 "poppler/CourierWidths.gperf" { "Udieresis", 600 }, { "", 0 }, #line 176 "poppler/CourierWidths.gperf" { "ucircumflex", 600 }, { "", 0 }, { "", 0 }, #line 196 "poppler/CourierWidths.gperf" { "five", 600 }, { "", 0 }, #line 14 "poppler/CourierWidths.gperf" { "Ntilde", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 267 "poppler/CourierWidths.gperf" { "uring", 600 }, #line 45 "poppler/CourierWidths.gperf" { "A", 600 }, { "", 0 }, { "", 0 }, #line 84 "poppler/CourierWidths.gperf" { "four", 600 }, { "", 0 }, #line 187 "poppler/CourierWidths.gperf" { "egrave", 600 }, { "", 0 }, { "", 0 }, #line 241 "poppler/CourierWidths.gperf" { "paragraph", 600 }, { "", 0 }, #line 29 "poppler/CourierWidths.gperf" { "Lcaron", 600 }, { "", 0 }, { "", 0 }, #line 325 "poppler/CourierWidths.gperf" { "brokenbar", 600 }, { "", 0 }, #line 199 "poppler/CourierWidths.gperf" { "Zcaron", 600 }, { "", 0 }, { "", 0 }, #line 165 "poppler/CourierWidths.gperf" { "Adieresis", 600 }, { "", 0 }, #line 122 "poppler/CourierWidths.gperf" { "y", 600 }, #line 16 "poppler/CourierWidths.gperf" { "kcommaaccent", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 76 "poppler/CourierWidths.gperf" { "agrave", 600 }, { "", 0 }, #line 69 "poppler/CourierWidths.gperf" { "Uhungarumlaut", 600 }, #line 204 "poppler/CourierWidths.gperf" { "ydieresis", 600 }, #line 168 "poppler/CourierWidths.gperf" { "slash", 600 }, #line 229 "poppler/CourierWidths.gperf" { "ogonek", 600 }, #line 151 "poppler/CourierWidths.gperf" { "AE", 600 }, #line 192 "poppler/CourierWidths.gperf" { "asterisk", 600 }, { "", 0 }, { "", 0 }, #line 111 "poppler/CourierWidths.gperf" { "twosuperior", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 53 "poppler/CourierWidths.gperf" { "G", 600 }, #line 58 "poppler/CourierWidths.gperf" { "iogonek", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 17 "poppler/CourierWidths.gperf" { "Ncommaaccent", 600 }, { "", 0 }, #line 21 "poppler/CourierWidths.gperf" { "plusminus", 603 }, { "", 0 }, #line 230 "poppler/CourierWidths.gperf" { "ograve", 600 }, { "", 0 }, #line 129 "poppler/CourierWidths.gperf" { "quotedbl", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 233 "poppler/CourierWidths.gperf" { "Eogonek", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 120 "poppler/CourierWidths.gperf" { "w", 600 }, #line 259 "poppler/CourierWidths.gperf" { "uogonek", 600 }, { "", 0 }, #line 60 "poppler/CourierWidths.gperf" { "backslash", 600 }, #line 79 "poppler/CourierWidths.gperf" { "equal", 600 }, { "", 0 }, #line 38 "poppler/CourierWidths.gperf" { "Omacron", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 121 "poppler/CourierWidths.gperf" { "x", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 298 "poppler/CourierWidths.gperf" { "Zdotaccent", 600 }, #line 152 "poppler/CourierWidths.gperf" { "Ucircumflex", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 68 "poppler/CourierWidths.gperf" { "Q", 600 }, #line 296 "poppler/CourierWidths.gperf" { "Imacron", 600 }, { "", 0 }, { "", 0 }, #line 250 "poppler/CourierWidths.gperf" { "Uring", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 123 "poppler/CourierWidths.gperf" { "z", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 22 "poppler/CourierWidths.gperf" { "circumflex", 600 }, { "", 0 }, #line 251 "poppler/CourierWidths.gperf" { "Lcommaaccent", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 73 "poppler/CourierWidths.gperf" { "S", 600 }, { "", 0 }, { "", 0 }, #line 175 "poppler/CourierWidths.gperf" { "Odieresis", 600 }, { "", 0 }, #line 284 "poppler/CourierWidths.gperf" { "Acircumflex", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 67 "poppler/CourierWidths.gperf" { "P", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 238 "poppler/CourierWidths.gperf" { "Aring", 600 }, #line 96 "poppler/CourierWidths.gperf" { "Oslash", 600 }, #line 114 "poppler/CourierWidths.gperf" { "OE", 600 }, { "", 0 }, #line 171 "poppler/CourierWidths.gperf" { "Idieresis", 600 }, { "", 0 }, #line 62 "poppler/CourierWidths.gperf" { "M", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 282 "poppler/CourierWidths.gperf" { "fi", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 56 "poppler/CourierWidths.gperf" { "J", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 264 "poppler/CourierWidths.gperf" { "igrave", 600 }, { "", 0 }, #line 255 "poppler/CourierWidths.gperf" { "Ohungarumlaut", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 243 "poppler/CourierWidths.gperf" { "Uogonek", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 87 "poppler/CourierWidths.gperf" { "b", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 167 "poppler/CourierWidths.gperf" { "Egrave", 600 }, { "", 0 }, { "", 0 }, #line 182 "poppler/CourierWidths.gperf" { "Ydieresis", 600 }, { "", 0 }, #line 195 "poppler/CourierWidths.gperf" { "ugrave", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 212 "poppler/CourierWidths.gperf" { "multiply", 600 }, { "", 0 }, { "", 0 }, #line 66 "poppler/CourierWidths.gperf" { "O", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 31 "poppler/CourierWidths.gperf" { "Aogonek", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 279 "poppler/CourierWidths.gperf" { "florin", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 228 "poppler/CourierWidths.gperf" { "Ocircumflex", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 283 "poppler/CourierWidths.gperf" { "fl", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 55 "poppler/CourierWidths.gperf" { "I", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 286 "poppler/CourierWidths.gperf" { "Icircumflex", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 54 "poppler/CourierWidths.gperf" { "H", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 134 "poppler/CourierWidths.gperf" { "Lslash", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 99 "poppler/CourierWidths.gperf" { "k", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 184 "poppler/CourierWidths.gperf" { "abreve", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 217 "poppler/CourierWidths.gperf" { "partialdiff", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 52 "poppler/CourierWidths.gperf" { "F", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 178 "poppler/CourierWidths.gperf" { "Ugrave", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 91 "poppler/CourierWidths.gperf" { "f", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 119 "poppler/CourierWidths.gperf" { "v", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 63 "poppler/CourierWidths.gperf" { "N", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 41 "poppler/CourierWidths.gperf" { "Agrave", 600 }, #line 34 "poppler/CourierWidths.gperf" { "Iogonek", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 82 "poppler/CourierWidths.gperf" { "Y", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 46 "poppler/CourierWidths.gperf" { "B", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 177 "poppler/CourierWidths.gperf" { "bracketleft", 600 }, #line 260 "poppler/CourierWidths.gperf" { "bracketright", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 142 "poppler/CourierWidths.gperf" { "gbreve", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 216 "poppler/CourierWidths.gperf" { "Ograve", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 59 "poppler/CourierWidths.gperf" { "L", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 246 "poppler/CourierWidths.gperf" { "Igrave", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 83 "poppler/CourierWidths.gperf" { "Z", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 162 "poppler/CourierWidths.gperf" { "Abreve", 600 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 124 "poppler/CourierWidths.gperf" { "Gbreve", 600 } }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) { register unsigned int key = hash(str, len); if (key <= MAX_HASH_VALUE) { register const char *s = wordlist[key].name; if (*str == *s && !strcmp(str + 1, s + 1)) return &wordlist[key]; } } return 0; } #line 330 "poppler/CourierWidths.gperf" poppler-24.02.0/poppler/CryptoSignBackend.cc000066400000000000000000000057641455701731300207230ustar00rootroot00000000000000//======================================================================== // // CryptoSignBackend.cc // // This file is licensed under the GPLv2 or later // // Copyright 2023 g10 Code GmbH, Author: Sune Stolborg Vuorela //======================================================================== #include "CryptoSignBackend.h" #include "config.h" #ifdef ENABLE_GPGME # include "GPGMECryptoSignBackend.h" #endif #ifdef ENABLE_NSS3 # include "NSSCryptoSignBackend.h" #endif namespace CryptoSign { 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; poppler-24.02.0/poppler/CryptoSignBackend.h000066400000000000000000000070531455701731300205560ustar00rootroot00000000000000//======================================================================== // // CryptoSignBackend.h // // This file is licensed under the GPLv2 or later // // Copyright 2023 g10 Code GmbH, Author: Sune Stolborg Vuorela //======================================================================== #ifndef SIGNATUREBACKEND_H #define SIGNATUREBACKEND_H #include #include #include #include #include "HashAlgorithm.h" #include "CertificateInfo.h" #include "SignatureInfo.h" #include "goo/GooString.h" #include "poppler_private_export.h" namespace CryptoSign { // 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; // 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; virtual CertificateValidationStatus validateCertificate(std::chrono::system_clock::time_point validation_time, bool ocspRevocationCheck, bool useAIACertFetch) = 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 std::unique_ptr getCertificateInfo() const = 0; virtual std::optional 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) = 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 poppler-24.02.0/poppler/CurlCachedFile.cc000066400000000000000000000052351455701731300201400ustar00rootroot00000000000000//======================================================================== // // CurlCachedFile.cc // // This file is licensed under the GPLv2 or later // // Copyright 2009 Stefan Thomas // Copyright 2010, 2011 Hib Eris // Copyright 2010, 2019, 2021, 2022 Albert Astals Cid // //======================================================================== #include #include "CurlCachedFile.h" #include "goo/GooString.h" //------------------------------------------------------------------------ CurlCachedFileLoader::CurlCachedFileLoader(const std::string &urlA) : url(urlA) { cachedFile = nullptr; curl = nullptr; } CurlCachedFileLoader::~CurlCachedFileLoader() { curl_easy_cleanup(curl); } static size_t noop_cb(char *ptr, size_t size, size_t nmemb, void *ptr2) { return size * nmemb; } size_t CurlCachedFileLoader::init(CachedFile *cachedFileA) { curl_off_t contentLength = -1; long code = 0; size_t size; cachedFile = cachedFileA; curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_HEADER, 1); curl_easy_setopt(curl, CURLOPT_NOBODY, 1); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &noop_cb); curl_easy_perform(curl); curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); if (code) { curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &contentLength); size = contentLength; } else { error(errInternal, -1, "Failed to get size of '{0:s}'.", url.c_str()); size = -1; } curl_easy_reset(curl); return size; } static size_t load_cb(const char *ptr, size_t size, size_t nmemb, void *data) { CachedFileWriter *writer = (CachedFileWriter *)data; return (writer->write)(ptr, size * nmemb); } int CurlCachedFileLoader::load(const std::vector &ranges, CachedFileWriter *writer) { CURLcode r = CURLE_OK; unsigned long long fromByte, toByte; for (const ByteRange &bRange : ranges) { fromByte = bRange.offset; toByte = fromByte + bRange.length - 1; const std::unique_ptr 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()); r = curl_easy_perform(curl); curl_easy_reset(curl); if (r != CURLE_OK) { break; } } return r; } //------------------------------------------------------------------------ poppler-24.02.0/poppler/CurlCachedFile.h000066400000000000000000000016441455701731300200020ustar00rootroot00000000000000//======================================================================== // // CurlCachedFile.h // // This file is licensed under the GPLv2 or later // // Copyright 2010 Hib Eris // Copyright 2010, 2022 Albert Astals Cid // //======================================================================== #ifndef CURLCACHELOADER_H #define CURLCACHELOADER_H #include "poppler-config.h" #include "CachedFile.h" #include //------------------------------------------------------------------------ class CurlCachedFileLoader : public CachedFileLoader { public: explicit CurlCachedFileLoader(const std::string &urlA); ~CurlCachedFileLoader() override; size_t init(CachedFile *cachedFile) override; int load(const std::vector &ranges, CachedFileWriter *writer) override; private: const std::string url; CachedFile *cachedFile; CURL *curl; }; #endif poppler-24.02.0/poppler/CurlPDFDocBuilder.cc000066400000000000000000000030211455701731300205260ustar00rootroot00000000000000//======================================================================== // // CurlPDFDocBuilder.cc // // This file is licensed under the GPLv2 or later // // Copyright 2010 Hib Eris // Copyright 2010, 2017, 2022 Albert Astals Cid // Copyright 2021 Oliver Sander // //======================================================================== #include #include "CurlPDFDocBuilder.h" #include "CachedFile.h" #include "CurlCachedFile.h" #include "ErrorCodes.h" //------------------------------------------------------------------------ // CurlPDFDocBuilder //------------------------------------------------------------------------ std::unique_ptr CurlPDFDocBuilder::buildPDFDoc(const GooString &uri, const std::optional &ownerPassword, const std::optional &userPassword, void *guiDataA) { 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())); } BaseStream *str = new CachedFileStream(cachedFile, 0, false, cachedFile->getLength(), Object(objNull)); return std::make_unique(str, ownerPassword, userPassword, guiDataA); } bool CurlPDFDocBuilder::supports(const GooString &uri) { if (uri.cmpN("http://", 7) == 0 || uri.cmpN("https://", 8) == 0) { return true; } else { return false; } } poppler-24.02.0/poppler/CurlPDFDocBuilder.h000066400000000000000000000020721455701731300203750ustar00rootroot00000000000000//======================================================================== // // CurlPDFDocBuilder.h // // This file is licensed under the GPLv2 or later // // Copyright 2010 Hib Eris // Copyright 2010, 2018, 2022 Albert Astals Cid // Copyright 2021 Oliver Sander // //======================================================================== #ifndef CURLPDFDOCBUILDER_H #define CURLPDFDOCBUILDER_H #include "PDFDocBuilder.h" //------------------------------------------------------------------------ // CurlPDFDocBuilder // // The CurlPDFDocBuilder implements a PDFDocBuilder for 'http(s)://'. //------------------------------------------------------------------------ class CurlPDFDocBuilder : public PDFDocBuilder { public: std::unique_ptr buildPDFDoc(const GooString &uri, const std::optional &ownerPassword = {}, const std::optional &userPassword = {}, void *guiDataA = nullptr) override; bool supports(const GooString &uri) override; }; #endif /* CURLPDFDOCBUILDER_H */ poppler-24.02.0/poppler/DCTStream.cc000066400000000000000000000165351455701731300171360ustar00rootroot00000000000000//======================================================================== // // DCTStream.cc // // This file is licensed under the GPLv2 or later // // Copyright 2005 Jeff Muizelaar // Copyright 2005-2010, 2012, 2017, 2020-2023 Albert Astals Cid // Copyright 2009 Ryszard Trojnacki // Copyright 2010 Carlos Garcia Campos // Copyright 2011 Daiki Ueno // Copyright 2011 Tomas Hoger // Copyright 2012, 2013 Thomas Freitag // Copyright 2017 Adrian Johnson // Copyright 2020 Lluís Batlle i Rossell // //======================================================================== #include "DCTStream.h" static void str_init_source(j_decompress_ptr cinfo) { } static boolean str_fill_input_buffer(j_decompress_ptr cinfo) { int c; struct str_src_mgr *src = (struct str_src_mgr *)cinfo->src; if (src->index == 0) { c = 0xFF; src->index++; } else if (src->index == 1) { c = 0xD8; src->index++; } else { c = src->str->getChar(); } src->buffer = c; src->pub.next_input_byte = &src->buffer; src->pub.bytes_in_buffer = 1; if (c != EOF) { return TRUE; } else { return FALSE; } } static void str_skip_input_data(j_decompress_ptr cinfo, long num_bytes) { struct str_src_mgr *src = (struct str_src_mgr *)cinfo->src; if (num_bytes > 0) { while (num_bytes > (long)src->pub.bytes_in_buffer) { num_bytes -= (long)src->pub.bytes_in_buffer; str_fill_input_buffer(cinfo); } src->pub.next_input_byte += (size_t)num_bytes; src->pub.bytes_in_buffer -= (size_t)num_bytes; } } static void str_term_source(j_decompress_ptr cinfo) { } DCTStream::DCTStream(Stream *strA, int colorXformA, Dict *dict, int recursion) : FilterStream(strA) { colorXform = colorXformA; if (dict != nullptr) { Object obj = dict->lookup("Width", recursion); err.width = (obj.isInt() && obj.getInt() <= JPEG_MAX_DIMENSION) ? obj.getInt() : 0; obj = dict->lookup("Height", recursion); err.height = (obj.isInt() && obj.getInt() <= JPEG_MAX_DIMENSION) ? obj.getInt() : 0; } else { err.height = err.width = 0; } init(); } DCTStream::~DCTStream() { jpeg_destroy_decompress(&cinfo); delete str; } static void exitErrorHandler(jpeg_common_struct *error) { j_decompress_ptr cinfo = (j_decompress_ptr)error; str_error_mgr *err = (struct str_error_mgr *)cinfo->err; if (cinfo->err->msg_code == JERR_IMAGE_TOO_BIG && err->width != 0 && err->height != 0) { cinfo->image_height = err->height; cinfo->image_width = err->width; } else { longjmp(err->setjmp_buffer, 1); } } void DCTStream::init() { jpeg_std_error(&err.pub); err.pub.error_exit = &exitErrorHandler; src.pub.init_source = str_init_source; src.pub.fill_input_buffer = str_fill_input_buffer; src.pub.skip_input_data = str_skip_input_data; src.pub.resync_to_restart = jpeg_resync_to_restart; src.pub.term_source = str_term_source; src.pub.bytes_in_buffer = 0; src.pub.next_input_byte = nullptr; src.str = str; src.index = 0; current = nullptr; limit = nullptr; cinfo.err = &err.pub; if (!setjmp(err.setjmp_buffer)) { jpeg_create_decompress(&cinfo); cinfo.src = (jpeg_source_mgr *)&src; } row_buffer = nullptr; } void DCTStream::reset() { int row_stride; str->reset(); if (row_buffer) { jpeg_destroy_decompress(&cinfo); init(); } // JPEG data has to start with 0xFF 0xD8 // but some pdf like the one on // https://bugs.freedesktop.org/show_bug.cgi?id=3299 // does have some garbage before that this seeks for // the start marker... bool startFound = false; int c = 0, c2 = 0; while (!startFound) { if (!c) { c = str->getChar(); if (c == -1) { error(errSyntaxError, -1, "Could not find start of jpeg data"); return; } if (c != 0xFF) { c = 0; } } else { c2 = str->getChar(); if (c2 != 0xD8) { c = 0; c2 = 0; } else { startFound = true; } } } if (!setjmp(err.setjmp_buffer)) { if (jpeg_read_header(&cinfo, TRUE) != JPEG_SUSPENDED) { // figure out color transform if (colorXform == -1 && !cinfo.saw_Adobe_marker) { if (cinfo.num_components == 3) { if (cinfo.saw_JFIF_marker) { colorXform = 1; } else if (cinfo.cur_comp_info[0]->component_id == 82 && cinfo.cur_comp_info[1]->component_id == 71 && cinfo.cur_comp_info[2]->component_id == 66) { // ASCII "RGB" colorXform = 0; } else { colorXform = 1; } } else { colorXform = 0; } } else if (cinfo.saw_Adobe_marker) { colorXform = cinfo.Adobe_transform; } switch (cinfo.num_components) { case 3: cinfo.jpeg_color_space = colorXform ? JCS_YCbCr : JCS_RGB; break; case 4: cinfo.jpeg_color_space = colorXform ? JCS_YCCK : JCS_CMYK; break; } jpeg_start_decompress(&cinfo); row_stride = cinfo.output_width * cinfo.output_components; row_buffer = cinfo.mem->alloc_sarray((j_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, 1); } } } bool DCTStream::readLine() { if (cinfo.output_scanline < cinfo.output_height) { if (!setjmp(err.setjmp_buffer)) { if (!jpeg_read_scanlines(&cinfo, row_buffer, 1)) { return false; } else { current = &row_buffer[0][0]; limit = &row_buffer[0][(cinfo.output_width - 1) * cinfo.output_components] + cinfo.output_components; return true; } } else { return false; } } else { return false; } } int DCTStream::getChar() { if (current == limit) { if (!readLine()) { return EOF; } } return *current++; } int DCTStream::getChars(int nChars, unsigned char *buffer) { for (int i = 0; i < nChars;) { if (current == limit) { if (!readLine()) { return i; } } intptr_t left = limit - current; if (left + i > nChars) { left = nChars - i; } memcpy(buffer + i, current, left); current += left; i += static_cast(left); } return nChars; } int DCTStream::lookChar() { if (unlikely(current == nullptr)) { return EOF; } return *current; } GooString *DCTStream::getPSFilter(int psLevel, const char *indent) { GooString *s; if (psLevel < 2) { return nullptr; } if (!(s = str->getPSFilter(psLevel, indent))) { return nullptr; } s->append(indent)->append("<< >> /DCTDecode filter\n"); return s; } bool DCTStream::isBinary(bool last) const { return str->isBinary(true); } poppler-24.02.0/poppler/DCTStream.h000066400000000000000000000040211455701731300167630ustar00rootroot00000000000000//======================================================================== // // DCTStream.h // // This file is licensed under the GPLv2 or later // // Copyright 2005 Jeff Muizelaar // Copyright 2005 Martin Kretzschmar // Copyright 2005-2007, 2009-2011, 2017, 2019, 2021 Albert Astals Cid // Copyright 2010 Carlos Garcia Campos // Copyright 2011 Daiki Ueno // Copyright 2013 Thomas Freitag // Copyright 2020 Lluís Batlle i Rossell // //======================================================================== #ifndef DCTSTREAM_H #define DCTSTREAM_H #include "poppler-config.h" #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include "goo/gmem.h" #include "goo/gfile.h" #include "Error.h" #include "Object.h" #include "Decrypt.h" #include "Stream.h" extern "C" { #include #include } struct str_src_mgr { struct jpeg_source_mgr pub; JOCTET buffer; Stream *str; int index; }; struct str_error_mgr { struct jpeg_error_mgr pub; jmp_buf setjmp_buffer; int width; int height; }; class DCTStream : public FilterStream { public: DCTStream(Stream *strA, int colorXformA, Dict *dict, int recursion); ~DCTStream() override; StreamKind getKind() const override { return strDCT; } void reset() override; int getChar() override; int lookChar() override; GooString *getPSFilter(int psLevel, const char *indent) override; bool isBinary(bool last = true) const override; private: void init(); bool hasGetChars() override { return true; } bool readLine(); int getChars(int nChars, unsigned char *buffer) override; int colorXform; JSAMPLE *current; JSAMPLE *limit; struct jpeg_decompress_struct cinfo; struct str_error_mgr err; struct str_src_mgr src; JSAMPARRAY row_buffer; }; #endif poppler-24.02.0/poppler/DateInfo.cc000066400000000000000000000110451455701731300170300ustar00rootroot00000000000000//======================================================================== // // DateInfo.cc // // Copyright (C) 2008, 2018, 2019, 2021, 2022 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 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 // //======================================================================== //======================================================================== // // Based on code from pdfinfo.cc // // Copyright 1998-2003 Glyph & Cog, LLC // //======================================================================== #include #include "glibc.h" #include "gmem.h" #include "DateInfo.h" #include "UTF.h" #include #include /* 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) { std::vector u = TextStringToUCS4(date->toStr()); GooString s; for (auto &c : u) { // Ignore any non ASCII characters if (c < 128) { s.append(c); } } const char *dateString = s.c_str(); if (strlen(dateString) < 2) { return false; } if (dateString[0] == 'D' && dateString[1] == ':') { dateString += 2; } *month = 1; *day = 1; *hour = 0; *minute = 0; *second = 0; *tz = 0x00; *tzHour = 0; *tzMinute = 0; if (sscanf(dateString, "%4d%2d%2d%2d%2d%2d%c%2d%*c%2d", year, month, day, hour, minute, second, tz, tzHour, tzMinute) > 0) { /* Workaround for y2k bug in Distiller 3 stolen from gpdf, hoping that it won't * be used after y2.2k */ if (*year < 1930 && strlen(dateString) > 14) { int century, years_since_1900; if (sscanf(dateString, "%2d%3d%2d%2d%2d%2d%2d", ¢ury, &years_since_1900, month, day, hour, minute, second) == 7) { *year = century * 100 + years_since_1900; } else { return false; } } if (*year <= 0) { return false; } return true; } return false; } GooString *timeToDateString(const time_t *timeA) { 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); // 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 // time. 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); } else if (offset < 0) { dateString->appendf("-{0:02d}'{1:02d}'", -offset / 3600, (-offset % 3600) / 60); } else { dateString->append("Z"); } return dateString; } // Convert PDF date string to time. Returns -1 if conversion fails. time_t dateStringToTime(const GooString *dateString) { int year, mon, day, hour, min, sec, tz_hour, tz_minute; char tz; struct tm tm; time_t time; if (!parseDateString(dateString, &year, &mon, &day, &hour, &min, &sec, &tz, &tz_hour, &tz_minute)) { return -1; } tm.tm_year = year - 1900; tm.tm_mon = mon - 1; tm.tm_mday = day; tm.tm_hour = hour; tm.tm_min = min; tm.tm_sec = sec; tm.tm_wday = -1; tm.tm_yday = -1; tm.tm_isdst = -1; /* 0 = DST off, 1 = DST on, -1 = don't know */ /* compute tm_wday and tm_yday and check date */ time = timegm(&tm); if (time == (time_t)-1) { return time; } time_t offset = (tz_hour * 60 + tz_minute) * 60; if (tz == '-') { offset *= -1; } time -= offset; return time; } poppler-24.02.0/poppler/DateInfo.h000066400000000000000000000030321455701731300166670ustar00rootroot00000000000000//======================================================================== // // DateInfo.h // // Copyright (C) 2008, 2018, 2019 Albert Astals Cid // Copyright (C) 2009 Carlos Garcia Campos // Copyright (C) 2015 André Guerreiro // Copyright (C) 2015 André Esser // Copyright (C) 2016, 2021 Adrian Johnson // // 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 // //======================================================================== //======================================================================== // // Based on code from pdfinfo.cc // // Copyright 1998-2003 Glyph & Cog, LLC // //======================================================================== #ifndef DATE_INFO_H #define DATE_INFO_H #include "goo/GooString.h" #include "poppler_private_export.h" #include bool POPPLER_PRIVATE_EXPORT parseDateString(const GooString *date, int *year, int *month, int *day, int *hour, int *minute, int *second, char *tz, int *tzHour, int *tzMinute); /* Converts the time_t into a PDF Date format string. * If timeA is NULL, current time is used. * Returns new GooString. Free with delete. */ GooString POPPLER_PRIVATE_EXPORT *timeToDateString(const time_t *timeA); /* Convert PDF date string to time. * Returns -1 if conversion fails. */ time_t POPPLER_PRIVATE_EXPORT dateStringToTime(const GooString *dateString); #endif poppler-24.02.0/poppler/Decrypt.cc000066400000000000000000001717721455701731300167670ustar00rootroot00000000000000//======================================================================== // // Decrypt.cc // // 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) 2008 Julien Rebetez // Copyright (C) 2008, 2010, 2016-2021 Albert Astals Cid // Copyright (C) 2009 Matthias Franz // Copyright (C) 2009 David Benjamin // Copyright (C) 2012 Fabio D'Urso // Copyright (C) 2013, 2017 Adrian Johnson // Copyright (C) 2016 Alok Anand // Copyright (C) 2016 Thomas Freitag // Copyright (C) 2018 Adam Reichold // // 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 #include #include #include "goo/gmem.h" #include "goo/grandom.h" #include "Decrypt.h" #include "Error.h" static void rc4InitKey(const unsigned char *key, int keyLen, unsigned char *state); static unsigned char rc4DecryptByte(unsigned char *state, unsigned char *x, unsigned char *y, unsigned char c); static bool aesReadBlock(Stream *str, unsigned char *in, bool addPadding); static void aesKeyExpansion(DecryptAESState *s, const unsigned char *objKey, int objKeyLen, bool decrypt); static void aesEncryptBlock(DecryptAESState *s, const unsigned char *in); static void aesDecryptBlock(DecryptAESState *s, const unsigned char *in, bool last); static void aes256KeyExpansion(DecryptAES256State *s, const unsigned char *objKey, int objKeyLen, bool decrypt); static void aes256EncryptBlock(DecryptAES256State *s, const unsigned char *in); static void aes256DecryptBlock(DecryptAES256State *s, const unsigned char *in, bool last); static void sha256(unsigned char *msg, int msgLen, unsigned char *hash); static void sha384(unsigned char *msg, int msgLen, unsigned char *hash); static void sha512(unsigned char *msg, int msgLen, unsigned char *hash); static void revision6Hash(const GooString *inputPassword, unsigned char *K, const char *userKey); static const unsigned char passwordPad[32] = { 0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41, 0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa, 0x01, 0x08, 0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80, 0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a }; //------------------------------------------------------------------------ // Decrypt //------------------------------------------------------------------------ bool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength, const GooString *ownerKey, const GooString *userKey, const GooString *ownerEnc, const GooString *userEnc, int permissions, const GooString *fileID, const GooString *ownerPassword, const GooString *userPassword, unsigned char *fileKey, bool encryptMetadata, bool *ownerPasswordOk) { DecryptAES256State state; unsigned char test[127 + 56], test2[32]; GooString *userPassword2; unsigned char fState[256]; unsigned char tmpKey[16]; unsigned char fx, fy; int len, i, j; *ownerPasswordOk = false; if (encRevision == 5 || encRevision == 6) { // check the owner password if (ownerPassword) { //~ this is supposed to convert the password to UTF-8 using "SASLprep" len = ownerPassword->getLength(); if (len > 127) { len = 127; } memcpy(test, ownerPassword->c_str(), len); memcpy(test + len, ownerKey->c_str() + 32, 8); memcpy(test + len + 8, userKey->c_str(), 48); sha256(test, len + 56, test); if (encRevision == 6) { // test contains the initial SHA-256 hash as input K. revision6Hash(ownerPassword, test, userKey->c_str()); } if (!memcmp(test, ownerKey->c_str(), 32)) { // compute the file key from the owner password memcpy(test, ownerPassword->c_str(), len); memcpy(test + len, ownerKey->c_str() + 40, 8); memcpy(test + len + 8, userKey->c_str(), 48); sha256(test, len + 56, test); if (encRevision == 6) { // test contains the initial SHA-256 hash input K. revision6Hash(ownerPassword, test, userKey->c_str()); } aes256KeyExpansion(&state, test, 32, true); for (i = 0; i < 16; ++i) { state.cbc[i] = 0; } aes256DecryptBlock(&state, (unsigned char *)ownerEnc->c_str(), false); memcpy(fileKey, state.buf, 16); aes256DecryptBlock(&state, (unsigned char *)ownerEnc->c_str() + 16, false); memcpy(fileKey + 16, state.buf, 16); *ownerPasswordOk = true; return true; } } // check the user password if (userPassword) { //~ this is supposed to convert the password to UTF-8 using "SASLprep" len = userPassword->getLength(); if (len > 127) { len = 127; } memcpy(test, userPassword->c_str(), len); memcpy(test + len, userKey->c_str() + 32, 8); sha256(test, len + 8, test); if (encRevision == 6) { // test contains the initial SHA-256 hash input K. // user key is not used in checking user password. revision6Hash(userPassword, test, nullptr); } if (!memcmp(test, userKey->c_str(), 32)) { // compute the file key from the user password memcpy(test, userPassword->c_str(), len); memcpy(test + len, userKey->c_str() + 40, 8); sha256(test, len + 8, test); if (encRevision == 6) { // test contains the initial SHA-256 hash input K. // user key is not used in computing intermediate user key. revision6Hash(userPassword, test, nullptr); } aes256KeyExpansion(&state, test, 32, true); for (i = 0; i < 16; ++i) { state.cbc[i] = 0; } aes256DecryptBlock(&state, (unsigned char *)userEnc->c_str(), false); memcpy(fileKey, state.buf, 16); aes256DecryptBlock(&state, (unsigned char *)userEnc->c_str() + 16, false); memcpy(fileKey + 16, state.buf, 16); return true; } } return false; } else { // try using the supplied owner password to generate the user password if (ownerPassword) { len = ownerPassword->getLength(); if (len < 32) { memcpy(test, ownerPassword->c_str(), len); memcpy(test + len, passwordPad, 32 - len); } else { memcpy(test, ownerPassword->c_str(), 32); } md5(test, 32, test); if (encRevision == 3) { for (i = 0; i < 50; ++i) { md5(test, keyLength, test); } } if (encRevision == 2) { rc4InitKey(test, keyLength, fState); fx = fy = 0; for (i = 0; i < 32; ++i) { test2[i] = rc4DecryptByte(fState, &fx, &fy, ownerKey->getChar(i)); } } else { memcpy(test2, ownerKey->c_str(), 32); for (i = 19; i >= 0; --i) { for (j = 0; j < keyLength; ++j) { tmpKey[j] = test[j] ^ i; } rc4InitKey(tmpKey, keyLength, fState); fx = fy = 0; for (j = 0; j < 32; ++j) { test2[j] = rc4DecryptByte(fState, &fx, &fy, test2[j]); } } } userPassword2 = new GooString((char *)test2, 32); if (makeFileKey2(encVersion, encRevision, keyLength, ownerKey, userKey, permissions, fileID, userPassword2, fileKey, encryptMetadata)) { *ownerPasswordOk = true; delete userPassword2; return true; } delete userPassword2; } // try using the supplied user password return makeFileKey2(encVersion, encRevision, keyLength, ownerKey, userKey, permissions, fileID, userPassword, fileKey, encryptMetadata); } } bool Decrypt::makeFileKey2(int encVersion, int encRevision, int keyLength, const GooString *ownerKey, const GooString *userKey, int permissions, const GooString *fileID, const GooString *userPassword, unsigned char *fileKey, bool encryptMetadata) { unsigned char *buf; unsigned char test[32]; unsigned char fState[256]; unsigned char tmpKey[16]; unsigned char fx, fy; int len, i, j; bool ok; // generate file key buf = (unsigned char *)gmalloc(72 + fileID->getLength()); if (userPassword) { len = userPassword->getLength(); if (len < 32) { memcpy(buf, userPassword->c_str(), len); memcpy(buf + len, passwordPad, 32 - len); } else { memcpy(buf, userPassword->c_str(), 32); } } else { memcpy(buf, passwordPad, 32); } memcpy(buf + 32, ownerKey->c_str(), 32); buf[64] = permissions & 0xff; buf[65] = (permissions >> 8) & 0xff; buf[66] = (permissions >> 16) & 0xff; buf[67] = (permissions >> 24) & 0xff; memcpy(buf + 68, fileID->c_str(), fileID->getLength()); len = 68 + fileID->getLength(); if (!encryptMetadata) { buf[len++] = 0xff; buf[len++] = 0xff; buf[len++] = 0xff; buf[len++] = 0xff; } md5(buf, len, fileKey); if (encRevision == 3) { for (i = 0; i < 50; ++i) { md5(fileKey, keyLength, fileKey); } } // test user password if (encRevision == 2) { rc4InitKey(fileKey, keyLength, fState); fx = fy = 0; for (i = 0; i < 32; ++i) { test[i] = rc4DecryptByte(fState, &fx, &fy, userKey->getChar(i)); } ok = memcmp(test, passwordPad, 32) == 0; } else if (encRevision == 3) { memcpy(test, userKey->c_str(), 32); for (i = 19; i >= 0; --i) { for (j = 0; j < keyLength; ++j) { tmpKey[j] = fileKey[j] ^ i; } rc4InitKey(tmpKey, keyLength, fState); fx = fy = 0; for (j = 0; j < 32; ++j) { test[j] = rc4DecryptByte(fState, &fx, &fy, test[j]); } } memcpy(buf, passwordPad, 32); memcpy(buf + 32, fileID->c_str(), fileID->getLength()); md5(buf, 32 + fileID->getLength(), buf); ok = memcmp(test, buf, 16) == 0; } else { ok = false; } gfree(buf); return ok; } //------------------------------------------------------------------------ // BaseCryptStream //------------------------------------------------------------------------ BaseCryptStream::BaseCryptStream(Stream *strA, const unsigned char *fileKey, CryptAlgorithm algoA, int keyLength, Ref refA) : FilterStream(strA) { algo = algoA; // construct object key for (int i = 0; i < keyLength; ++i) { objKey[i] = fileKey[i]; } for (std::size_t i = keyLength; i < sizeof(objKey); ++i) { objKey[i] = 0; } switch (algo) { case cryptRC4: if (likely(keyLength < static_cast(sizeof(objKey) - 4))) { objKey[keyLength] = refA.num & 0xff; objKey[keyLength + 1] = (refA.num >> 8) & 0xff; objKey[keyLength + 2] = (refA.num >> 16) & 0xff; objKey[keyLength + 3] = refA.gen & 0xff; objKey[keyLength + 4] = (refA.gen >> 8) & 0xff; md5(objKey, keyLength + 5, objKey); } if ((objKeyLength = keyLength + 5) > 16) { objKeyLength = 16; } break; case cryptAES: objKey[keyLength] = refA.num & 0xff; objKey[keyLength + 1] = (refA.num >> 8) & 0xff; objKey[keyLength + 2] = (refA.num >> 16) & 0xff; objKey[keyLength + 3] = refA.gen & 0xff; objKey[keyLength + 4] = (refA.gen >> 8) & 0xff; objKey[keyLength + 5] = 0x73; // 's' objKey[keyLength + 6] = 0x41; // 'A' objKey[keyLength + 7] = 0x6c; // 'l' objKey[keyLength + 8] = 0x54; // 'T' md5(objKey, keyLength + 9, objKey); if ((objKeyLength = keyLength + 5) > 16) { objKeyLength = 16; } break; case cryptAES256: objKeyLength = keyLength; break; case cryptNone: break; } charactersRead = 0; nextCharBuff = EOF; autoDelete = true; } BaseCryptStream::~BaseCryptStream() { if (autoDelete) { delete str; } } void BaseCryptStream::reset() { charactersRead = 0; nextCharBuff = EOF; str->reset(); } Goffset BaseCryptStream::getPos() { return charactersRead; } int BaseCryptStream::getChar() { // Read next character and empty the buffer, so that a new character will be read next time int c = lookChar(); nextCharBuff = EOF; if (c != EOF) { charactersRead++; } return c; } bool BaseCryptStream::isBinary(bool last) const { return str->isBinary(last); } void BaseCryptStream::setAutoDelete(bool val) { autoDelete = val; } //------------------------------------------------------------------------ // EncryptStream //------------------------------------------------------------------------ EncryptStream::EncryptStream(Stream *strA, const unsigned char *fileKey, CryptAlgorithm algoA, int keyLength, Ref refA) : BaseCryptStream(strA, fileKey, algoA, keyLength, refA) { // Fill the CBC initialization vector for AES and AES-256 switch (algo) { case cryptAES: grandom_fill(state.aes.cbc, 16); break; case cryptAES256: grandom_fill(state.aes256.cbc, 16); break; default: break; } } EncryptStream::~EncryptStream() { } void EncryptStream::reset() { BaseCryptStream::reset(); switch (algo) { case cryptRC4: state.rc4.x = state.rc4.y = 0; rc4InitKey(objKey, objKeyLength, state.rc4.state); break; case cryptAES: aesKeyExpansion(&state.aes, objKey, objKeyLength, false); memcpy(state.aes.buf, state.aes.cbc, 16); // Copy CBC IV to buf state.aes.bufIdx = 0; state.aes.paddingReached = false; break; case cryptAES256: aes256KeyExpansion(&state.aes256, objKey, objKeyLength, false); memcpy(state.aes256.buf, state.aes256.cbc, 16); // Copy CBC IV to buf state.aes256.bufIdx = 0; state.aes256.paddingReached = false; break; case cryptNone: break; } } int EncryptStream::lookChar() { unsigned char in[16]; int c; if (nextCharBuff != EOF) { return nextCharBuff; } c = EOF; // make gcc happy switch (algo) { case cryptRC4: if ((c = str->getChar()) != EOF) { // RC4 is XOR-based: the decryption algorithm works for encryption too c = rc4DecryptByte(state.rc4.state, &state.rc4.x, &state.rc4.y, (unsigned char)c); } break; case cryptAES: if (state.aes.bufIdx == 16 && !state.aes.paddingReached) { state.aes.paddingReached = !aesReadBlock(str, in, true); aesEncryptBlock(&state.aes, in); } if (state.aes.bufIdx == 16) { c = EOF; } else { c = state.aes.buf[state.aes.bufIdx++]; } break; case cryptAES256: if (state.aes256.bufIdx == 16 && !state.aes256.paddingReached) { state.aes256.paddingReached = !aesReadBlock(str, in, true); aes256EncryptBlock(&state.aes256, in); } if (state.aes256.bufIdx == 16) { c = EOF; } else { c = state.aes256.buf[state.aes256.bufIdx++]; } break; case cryptNone: break; } return (nextCharBuff = c); } //------------------------------------------------------------------------ // DecryptStream //------------------------------------------------------------------------ DecryptStream::DecryptStream(Stream *strA, const unsigned char *fileKey, CryptAlgorithm algoA, int keyLength, Ref refA) : BaseCryptStream(strA, fileKey, algoA, keyLength, refA) { } DecryptStream::~DecryptStream() { } void DecryptStream::reset() { int i; BaseCryptStream::reset(); switch (algo) { case cryptRC4: state.rc4.x = state.rc4.y = 0; rc4InitKey(objKey, objKeyLength, state.rc4.state); break; case cryptAES: aesKeyExpansion(&state.aes, objKey, objKeyLength, true); for (i = 0; i < 16; ++i) { state.aes.cbc[i] = str->getChar(); } state.aes.bufIdx = 16; break; case cryptAES256: aes256KeyExpansion(&state.aes256, objKey, objKeyLength, true); for (i = 0; i < 16; ++i) { state.aes256.cbc[i] = str->getChar(); } state.aes256.bufIdx = 16; break; case cryptNone: break; } } int DecryptStream::lookChar() { unsigned char in[16]; int c; if (nextCharBuff != EOF) { return nextCharBuff; } c = EOF; // make gcc happy switch (algo) { case cryptRC4: if ((c = str->getChar()) != EOF) { c = rc4DecryptByte(state.rc4.state, &state.rc4.x, &state.rc4.y, (unsigned char)c); } break; case cryptAES: if (state.aes.bufIdx == 16) { if (aesReadBlock(str, in, false)) { aesDecryptBlock(&state.aes, in, str->lookChar() == EOF); } } if (state.aes.bufIdx == 16) { c = EOF; } else { c = state.aes.buf[state.aes.bufIdx++]; } break; case cryptAES256: if (state.aes256.bufIdx == 16) { if (aesReadBlock(str, in, false)) { aes256DecryptBlock(&state.aes256, in, str->lookChar() == EOF); } } if (state.aes256.bufIdx == 16) { c = EOF; } else { c = state.aes256.buf[state.aes256.bufIdx++]; } break; case cryptNone: break; } return (nextCharBuff = c); } //------------------------------------------------------------------------ // RC4-compatible decryption //------------------------------------------------------------------------ static void rc4InitKey(const unsigned char *key, int keyLen, unsigned char *state) { unsigned char index1, index2; unsigned char t; int i; for (i = 0; i < 256; ++i) { state[i] = i; } if (unlikely(keyLen == 0)) { return; } index1 = index2 = 0; for (i = 0; i < 256; ++i) { index2 = (key[index1] + state[i] + index2) % 256; t = state[i]; state[i] = state[index2]; state[index2] = t; index1 = (index1 + 1) % keyLen; } } static unsigned char rc4DecryptByte(unsigned char *state, unsigned char *x, unsigned char *y, unsigned char c) { unsigned char x1, y1, tx, ty; x1 = *x = (*x + 1) % 256; y1 = *y = (state[*x] + *y) % 256; tx = state[x1]; ty = state[y1]; state[x1] = ty; state[y1] = tx; return c ^ state[(tx + ty) % 256]; } //------------------------------------------------------------------------ // AES decryption //------------------------------------------------------------------------ // Returns false if EOF was reached, true otherwise static bool aesReadBlock(Stream *str, unsigned char *in, bool addPadding) { int c, i; for (i = 0; i < 16; ++i) { if ((c = str->getChar()) != EOF) { in[i] = (unsigned char)c; } else { break; } } if (i == 16) { return true; } else { if (addPadding) { c = 16 - i; while (i < 16) { in[i++] = (unsigned char)c; } } return false; } } static const unsigned char sbox[256] = { 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; static const unsigned char invSbox[256] = { 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d }; static const unsigned int rcon[11] = { 0x00000000, // unused 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1b000000, 0x36000000 }; static inline unsigned int subWord(unsigned int x) { return (sbox[x >> 24] << 24) | (sbox[(x >> 16) & 0xff] << 16) | (sbox[(x >> 8) & 0xff] << 8) | sbox[x & 0xff]; } static inline unsigned int rotWord(unsigned int x) { return ((x << 8) & 0xffffffff) | (x >> 24); } static inline void subBytes(unsigned char *state) { int i; for (i = 0; i < 16; ++i) { state[i] = sbox[state[i]]; } } static inline void invSubBytes(unsigned char *state) { int i; for (i = 0; i < 16; ++i) { state[i] = invSbox[state[i]]; } } static inline void shiftRows(unsigned char *state) { unsigned char t; t = state[4]; state[4] = state[5]; state[5] = state[6]; state[6] = state[7]; state[7] = t; t = state[8]; state[8] = state[10]; state[10] = t; t = state[9]; state[9] = state[11]; state[11] = t; t = state[15]; state[15] = state[14]; state[14] = state[13]; state[13] = state[12]; state[12] = t; } static inline void invShiftRows(unsigned char *state) { unsigned char t; t = state[7]; state[7] = state[6]; state[6] = state[5]; state[5] = state[4]; state[4] = t; t = state[8]; state[8] = state[10]; state[10] = t; t = state[9]; state[9] = state[11]; state[11] = t; t = state[12]; state[12] = state[13]; state[13] = state[14]; state[14] = state[15]; state[15] = t; } // {02} \cdot s struct Mul02Table { constexpr Mul02Table() : values() { for (int s = 0; s < 256; s++) { values[s] = (s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1); } } constexpr unsigned char operator()(uint8_t i) const { return values[i]; } unsigned char values[256]; }; static constexpr Mul02Table mul02; // {03} \cdot s struct Mul03Table { constexpr Mul03Table() : values() { for (int s = 0; s < 256; s++) { const unsigned char s2 = (s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1); values[s] = s ^ s2; } } constexpr unsigned char operator()(uint8_t i) const { return values[i]; } unsigned char values[256]; }; static constexpr Mul03Table mul03; // {09} \cdot s struct Mul09Table { constexpr Mul09Table() : values() { for (int s = 0; s < 256; s++) { const unsigned char s2 = (s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1); const unsigned char s4 = (s2 & 0x80) ? ((s2 << 1) ^ 0x1b) : (s2 << 1); const unsigned char s8 = (s4 & 0x80) ? ((s4 << 1) ^ 0x1b) : (s4 << 1); values[s] = s ^ s8; } } constexpr unsigned char operator()(uint8_t i) const { return values[i]; } unsigned char values[256]; }; static constexpr Mul09Table mul09; // {0b} \cdot s struct Mul0bTable { constexpr Mul0bTable() : values() { for (int s = 0; s < 256; s++) { const unsigned char s2 = (s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1); const unsigned char s4 = (s2 & 0x80) ? ((s2 << 1) ^ 0x1b) : (s2 << 1); const unsigned char s8 = (s4 & 0x80) ? ((s4 << 1) ^ 0x1b) : (s4 << 1); values[s] = s ^ s2 ^ s8; } } constexpr unsigned char operator()(uint8_t i) const { return values[i]; } unsigned char values[256]; }; static constexpr Mul0bTable mul0b; // {0d} \cdot s struct Mul0dTable { constexpr Mul0dTable() : values() { for (int s = 0; s < 256; s++) { const unsigned char s2 = (s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1); const unsigned char s4 = (s2 & 0x80) ? ((s2 << 1) ^ 0x1b) : (s2 << 1); const unsigned char s8 = (s4 & 0x80) ? ((s4 << 1) ^ 0x1b) : (s4 << 1); values[s] = s ^ s4 ^ s8; } } constexpr unsigned char operator()(uint8_t i) const { return values[i]; } unsigned char values[256]; }; static constexpr Mul0dTable mul0d; // {0e} \cdot s struct Mul0eTable { constexpr Mul0eTable() : values() { for (int s = 0; s < 256; s++) { const unsigned char s2 = (s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1); const unsigned char s4 = (s2 & 0x80) ? ((s2 << 1) ^ 0x1b) : (s2 << 1); const unsigned char s8 = (s4 & 0x80) ? ((s4 << 1) ^ 0x1b) : (s4 << 1); values[s] = s2 ^ s4 ^ s8; } } constexpr unsigned char operator()(uint8_t i) const { return values[i]; } unsigned char values[256]; }; static constexpr Mul0eTable mul0e; static inline void mixColumns(unsigned char *state) { int c; unsigned char s0, s1, s2, s3; for (c = 0; c < 4; ++c) { s0 = state[c]; s1 = state[4 + c]; s2 = state[8 + c]; s3 = state[12 + c]; state[c] = mul02(s0) ^ mul03(s1) ^ s2 ^ s3; state[4 + c] = s0 ^ mul02(s1) ^ mul03(s2) ^ s3; state[8 + c] = s0 ^ s1 ^ mul02(s2) ^ mul03(s3); state[12 + c] = mul03(s0) ^ s1 ^ s2 ^ mul02(s3); } } static inline void invMixColumns(unsigned char *state) { int c; unsigned char s0, s1, s2, s3; for (c = 0; c < 4; ++c) { s0 = state[c]; s1 = state[4 + c]; s2 = state[8 + c]; s3 = state[12 + c]; state[c] = mul0e(s0) ^ mul0b(s1) ^ mul0d(s2) ^ mul09(s3); state[4 + c] = mul09(s0) ^ mul0e(s1) ^ mul0b(s2) ^ mul0d(s3); state[8 + c] = mul0d(s0) ^ mul09(s1) ^ mul0e(s2) ^ mul0b(s3); state[12 + c] = mul0b(s0) ^ mul0d(s1) ^ mul09(s2) ^ mul0e(s3); } } static inline void invMixColumnsW(unsigned int *w) { int c; unsigned char s0, s1, s2, s3; for (c = 0; c < 4; ++c) { s0 = w[c] >> 24; s1 = w[c] >> 16; s2 = w[c] >> 8; s3 = w[c]; w[c] = ((mul0e(s0) ^ mul0b(s1) ^ mul0d(s2) ^ mul09(s3)) << 24) | ((mul09(s0) ^ mul0e(s1) ^ mul0b(s2) ^ mul0d(s3)) << 16) | ((mul0d(s0) ^ mul09(s1) ^ mul0e(s2) ^ mul0b(s3)) << 8) | (mul0b(s0) ^ mul0d(s1) ^ mul09(s2) ^ mul0e(s3)); } } static inline void addRoundKey(unsigned char *state, const unsigned int *w) { int c; for (c = 0; c < 4; ++c) { state[c] ^= w[c] >> 24; state[4 + c] ^= w[c] >> 16; state[8 + c] ^= w[c] >> 8; state[12 + c] ^= w[c]; } } static void aesKeyExpansion(DecryptAESState *s, const unsigned char *objKey, int /*objKeyLen*/, bool decrypt) { unsigned int temp; int i, round; //~ this assumes objKeyLen == 16 for (i = 0; i < 4; ++i) { s->w[i] = (objKey[4 * i] << 24) + (objKey[4 * i + 1] << 16) + (objKey[4 * i + 2] << 8) + objKey[4 * i + 3]; } for (i = 4; i < 44; ++i) { temp = s->w[i - 1]; if (!(i & 3)) { temp = subWord(rotWord(temp)) ^ rcon[i / 4]; } s->w[i] = s->w[i - 4] ^ temp; } /* In case of decryption, adjust the key schedule for the equivalent inverse cipher */ if (decrypt) { for (round = 1; round <= 9; ++round) { invMixColumnsW(&s->w[round * 4]); } } } static void aesEncryptBlock(DecryptAESState *s, const unsigned char *in) { int c, round; // initial state (input is xor'd with previous output because of CBC) for (c = 0; c < 4; ++c) { s->state[c] = in[4 * c] ^ s->buf[4 * c]; s->state[4 + c] = in[4 * c + 1] ^ s->buf[4 * c + 1]; s->state[8 + c] = in[4 * c + 2] ^ s->buf[4 * c + 2]; s->state[12 + c] = in[4 * c + 3] ^ s->buf[4 * c + 3]; } // round 0 addRoundKey(s->state, &s->w[0]); // rounds 1-9 for (round = 1; round <= 9; ++round) { subBytes(s->state); shiftRows(s->state); mixColumns(s->state); addRoundKey(s->state, &s->w[round * 4]); } // round 10 subBytes(s->state); shiftRows(s->state); addRoundKey(s->state, &s->w[10 * 4]); for (c = 0; c < 4; ++c) { s->buf[4 * c] = s->state[c]; s->buf[4 * c + 1] = s->state[4 + c]; s->buf[4 * c + 2] = s->state[8 + c]; s->buf[4 * c + 3] = s->state[12 + c]; } s->bufIdx = 0; } static void aesDecryptBlock(DecryptAESState *s, const unsigned char *in, bool last) { int c, round, n, i; // initial state for (c = 0; c < 4; ++c) { s->state[c] = in[4 * c]; s->state[4 + c] = in[4 * c + 1]; s->state[8 + c] = in[4 * c + 2]; s->state[12 + c] = in[4 * c + 3]; } // round 0 addRoundKey(s->state, &s->w[10 * 4]); // rounds 1-9 for (round = 9; round >= 1; --round) { invSubBytes(s->state); invShiftRows(s->state); invMixColumns(s->state); addRoundKey(s->state, &s->w[round * 4]); } // round 10 invSubBytes(s->state); invShiftRows(s->state); addRoundKey(s->state, &s->w[0]); // CBC for (c = 0; c < 4; ++c) { s->buf[4 * c] = s->state[c] ^ s->cbc[4 * c]; s->buf[4 * c + 1] = s->state[4 + c] ^ s->cbc[4 * c + 1]; s->buf[4 * c + 2] = s->state[8 + c] ^ s->cbc[4 * c + 2]; s->buf[4 * c + 3] = s->state[12 + c] ^ s->cbc[4 * c + 3]; } // save the input block for the next CBC for (i = 0; i < 16; ++i) { s->cbc[i] = in[i]; } // remove padding s->bufIdx = 0; if (last) { n = s->buf[15]; if (n < 1 || n > 16) { // this should never happen n = 16; } for (i = 15; i >= n; --i) { s->buf[i] = s->buf[i - n]; } s->bufIdx = n; } } //------------------------------------------------------------------------ // AES-256 decryption //------------------------------------------------------------------------ static void aes256KeyExpansion(DecryptAES256State *s, const unsigned char *objKey, int objKeyLen, bool decrypt) { unsigned int temp; int i, round; //~ this assumes objKeyLen == 32 for (i = 0; i < 8; ++i) { s->w[i] = (objKey[4 * i] << 24) + (objKey[4 * i + 1] << 16) + (objKey[4 * i + 2] << 8) + objKey[4 * i + 3]; } for (i = 8; i < 60; ++i) { temp = s->w[i - 1]; if ((i & 7) == 0) { temp = subWord(rotWord(temp)) ^ rcon[i / 8]; } else if ((i & 7) == 4) { temp = subWord(temp); } s->w[i] = s->w[i - 8] ^ temp; } /* In case of decryption, adjust the key schedule for the equivalent inverse cipher */ if (decrypt) { for (round = 1; round <= 13; ++round) { invMixColumnsW(&s->w[round * 4]); } } } static void aes256EncryptBlock(DecryptAES256State *s, const unsigned char *in) { int c, round; // initial state (input is xor'd with previous output because of CBC) for (c = 0; c < 4; ++c) { s->state[c] = in[4 * c] ^ s->buf[4 * c]; s->state[4 + c] = in[4 * c + 1] ^ s->buf[4 * c + 1]; s->state[8 + c] = in[4 * c + 2] ^ s->buf[4 * c + 2]; s->state[12 + c] = in[4 * c + 3] ^ s->buf[4 * c + 3]; } // round 0 addRoundKey(s->state, &s->w[0]); // rounds 1-13 for (round = 1; round <= 13; ++round) { subBytes(s->state); shiftRows(s->state); mixColumns(s->state); addRoundKey(s->state, &s->w[round * 4]); } // round 14 subBytes(s->state); shiftRows(s->state); addRoundKey(s->state, &s->w[14 * 4]); for (c = 0; c < 4; ++c) { s->buf[4 * c] = s->state[c]; s->buf[4 * c + 1] = s->state[4 + c]; s->buf[4 * c + 2] = s->state[8 + c]; s->buf[4 * c + 3] = s->state[12 + c]; } s->bufIdx = 0; } static void aes256DecryptBlock(DecryptAES256State *s, const unsigned char *in, bool last) { int c, round, n, i; // initial state for (c = 0; c < 4; ++c) { s->state[c] = in[4 * c]; s->state[4 + c] = in[4 * c + 1]; s->state[8 + c] = in[4 * c + 2]; s->state[12 + c] = in[4 * c + 3]; } // round 0 addRoundKey(s->state, &s->w[14 * 4]); // rounds 13-1 for (round = 13; round >= 1; --round) { invSubBytes(s->state); invShiftRows(s->state); invMixColumns(s->state); addRoundKey(s->state, &s->w[round * 4]); } // round 14 invSubBytes(s->state); invShiftRows(s->state); addRoundKey(s->state, &s->w[0]); // CBC for (c = 0; c < 4; ++c) { s->buf[4 * c] = s->state[c] ^ s->cbc[4 * c]; s->buf[4 * c + 1] = s->state[4 + c] ^ s->cbc[4 * c + 1]; s->buf[4 * c + 2] = s->state[8 + c] ^ s->cbc[4 * c + 2]; s->buf[4 * c + 3] = s->state[12 + c] ^ s->cbc[4 * c + 3]; } // save the input block for the next CBC for (i = 0; i < 16; ++i) { s->cbc[i] = in[i]; } // remove padding s->bufIdx = 0; if (last) { n = s->buf[15]; if (n < 1 || n > 16) { // this should never happen n = 16; } for (i = 15; i >= n; --i) { s->buf[i] = s->buf[i - n]; } s->bufIdx = n; if (n > 16) { error(errSyntaxError, -1, "Reducing bufIdx from {0:d} to 16 to not crash", n); s->bufIdx = 16; } } } //------------------------------------------------------------------------ // MD5 message digest //------------------------------------------------------------------------ // this works around a bug in older Sun compilers static inline unsigned long rotateLeft(unsigned long x, int r) { x &= 0xffffffff; return ((x << r) | (x >> (32 - r))) & 0xffffffff; } static inline unsigned long md5Round1(unsigned long a, unsigned long b, unsigned long c, unsigned long d, unsigned long Xk, unsigned long s, unsigned long Ti) { return b + rotateLeft((a + ((b & c) | (~b & d)) + Xk + Ti), s); } static inline unsigned long md5Round2(unsigned long a, unsigned long b, unsigned long c, unsigned long d, unsigned long Xk, unsigned long s, unsigned long Ti) { return b + rotateLeft((a + ((b & d) | (c & ~d)) + Xk + Ti), s); } static inline unsigned long md5Round3(unsigned long a, unsigned long b, unsigned long c, unsigned long d, unsigned long Xk, unsigned long s, unsigned long Ti) { return b + rotateLeft((a + (b ^ c ^ d) + Xk + Ti), s); } static inline unsigned long md5Round4(unsigned long a, unsigned long b, unsigned long c, unsigned long d, unsigned long Xk, unsigned long s, unsigned long Ti) { return b + rotateLeft((a + (c ^ (b | ~d)) + Xk + Ti), s); } struct MD5State { unsigned long a, b, c, d; unsigned char buf[64]; int bufLen; int msgLen; unsigned char digest[16]; }; static void md5Start(MD5State *state) { state->a = 0x67452301; state->b = 0xefcdab89; state->c = 0x98badcfe; state->d = 0x10325476; state->bufLen = 0; state->msgLen = 0; } static void md5ProcessBlock(MD5State *state) { unsigned long x[16]; for (int i = 0; i < 16; ++i) { x[i] = state->buf[4 * i] | (state->buf[4 * i + 1] << 8) | (state->buf[4 * i + 2] << 16) | (state->buf[4 * i + 3] << 24); } unsigned long a = state->a; unsigned long b = state->b; unsigned long c = state->c; unsigned long d = state->d; // round 1 a = md5Round1(a, b, c, d, x[0], 7, 0xd76aa478); d = md5Round1(d, a, b, c, x[1], 12, 0xe8c7b756); c = md5Round1(c, d, a, b, x[2], 17, 0x242070db); b = md5Round1(b, c, d, a, x[3], 22, 0xc1bdceee); a = md5Round1(a, b, c, d, x[4], 7, 0xf57c0faf); d = md5Round1(d, a, b, c, x[5], 12, 0x4787c62a); c = md5Round1(c, d, a, b, x[6], 17, 0xa8304613); b = md5Round1(b, c, d, a, x[7], 22, 0xfd469501); a = md5Round1(a, b, c, d, x[8], 7, 0x698098d8); d = md5Round1(d, a, b, c, x[9], 12, 0x8b44f7af); c = md5Round1(c, d, a, b, x[10], 17, 0xffff5bb1); b = md5Round1(b, c, d, a, x[11], 22, 0x895cd7be); a = md5Round1(a, b, c, d, x[12], 7, 0x6b901122); d = md5Round1(d, a, b, c, x[13], 12, 0xfd987193); c = md5Round1(c, d, a, b, x[14], 17, 0xa679438e); b = md5Round1(b, c, d, a, x[15], 22, 0x49b40821); // round 2 a = md5Round2(a, b, c, d, x[1], 5, 0xf61e2562); d = md5Round2(d, a, b, c, x[6], 9, 0xc040b340); c = md5Round2(c, d, a, b, x[11], 14, 0x265e5a51); b = md5Round2(b, c, d, a, x[0], 20, 0xe9b6c7aa); a = md5Round2(a, b, c, d, x[5], 5, 0xd62f105d); d = md5Round2(d, a, b, c, x[10], 9, 0x02441453); c = md5Round2(c, d, a, b, x[15], 14, 0xd8a1e681); b = md5Round2(b, c, d, a, x[4], 20, 0xe7d3fbc8); a = md5Round2(a, b, c, d, x[9], 5, 0x21e1cde6); d = md5Round2(d, a, b, c, x[14], 9, 0xc33707d6); c = md5Round2(c, d, a, b, x[3], 14, 0xf4d50d87); b = md5Round2(b, c, d, a, x[8], 20, 0x455a14ed); a = md5Round2(a, b, c, d, x[13], 5, 0xa9e3e905); d = md5Round2(d, a, b, c, x[2], 9, 0xfcefa3f8); c = md5Round2(c, d, a, b, x[7], 14, 0x676f02d9); b = md5Round2(b, c, d, a, x[12], 20, 0x8d2a4c8a); // round 3 a = md5Round3(a, b, c, d, x[5], 4, 0xfffa3942); d = md5Round3(d, a, b, c, x[8], 11, 0x8771f681); c = md5Round3(c, d, a, b, x[11], 16, 0x6d9d6122); b = md5Round3(b, c, d, a, x[14], 23, 0xfde5380c); a = md5Round3(a, b, c, d, x[1], 4, 0xa4beea44); d = md5Round3(d, a, b, c, x[4], 11, 0x4bdecfa9); c = md5Round3(c, d, a, b, x[7], 16, 0xf6bb4b60); b = md5Round3(b, c, d, a, x[10], 23, 0xbebfbc70); a = md5Round3(a, b, c, d, x[13], 4, 0x289b7ec6); d = md5Round3(d, a, b, c, x[0], 11, 0xeaa127fa); c = md5Round3(c, d, a, b, x[3], 16, 0xd4ef3085); b = md5Round3(b, c, d, a, x[6], 23, 0x04881d05); a = md5Round3(a, b, c, d, x[9], 4, 0xd9d4d039); d = md5Round3(d, a, b, c, x[12], 11, 0xe6db99e5); c = md5Round3(c, d, a, b, x[15], 16, 0x1fa27cf8); b = md5Round3(b, c, d, a, x[2], 23, 0xc4ac5665); // round 4 a = md5Round4(a, b, c, d, x[0], 6, 0xf4292244); d = md5Round4(d, a, b, c, x[7], 10, 0x432aff97); c = md5Round4(c, d, a, b, x[14], 15, 0xab9423a7); b = md5Round4(b, c, d, a, x[5], 21, 0xfc93a039); a = md5Round4(a, b, c, d, x[12], 6, 0x655b59c3); d = md5Round4(d, a, b, c, x[3], 10, 0x8f0ccc92); c = md5Round4(c, d, a, b, x[10], 15, 0xffeff47d); b = md5Round4(b, c, d, a, x[1], 21, 0x85845dd1); a = md5Round4(a, b, c, d, x[8], 6, 0x6fa87e4f); d = md5Round4(d, a, b, c, x[15], 10, 0xfe2ce6e0); c = md5Round4(c, d, a, b, x[6], 15, 0xa3014314); b = md5Round4(b, c, d, a, x[13], 21, 0x4e0811a1); a = md5Round4(a, b, c, d, x[4], 6, 0xf7537e82); d = md5Round4(d, a, b, c, x[11], 10, 0xbd3af235); c = md5Round4(c, d, a, b, x[2], 15, 0x2ad7d2bb); b = md5Round4(b, c, d, a, x[9], 21, 0xeb86d391); // increment a, b, c, d state->a += a; state->b += b; state->c += c; state->d += d; state->bufLen = 0; } static void md5Append(MD5State *state, const unsigned char *data, int dataLen) { const unsigned char *p = data; int remain = dataLen; while (state->bufLen + remain >= 64) { const int k = 64 - state->bufLen; memcpy(state->buf + state->bufLen, p, k); state->bufLen = 64; md5ProcessBlock(state); p += k; remain -= k; } if (remain > 0) { memcpy(state->buf + state->bufLen, p, remain); state->bufLen += remain; } state->msgLen += dataLen; } static void md5Finish(MD5State *state) { // padding and length state->buf[state->bufLen++] = 0x80; if (state->bufLen > 56) { while (state->bufLen < 64) { state->buf[state->bufLen++] = 0x00; } md5ProcessBlock(state); } while (state->bufLen < 56) { state->buf[state->bufLen++] = 0x00; } state->buf[56] = (unsigned char)(state->msgLen << 3); state->buf[57] = (unsigned char)(state->msgLen >> 5); state->buf[58] = (unsigned char)(state->msgLen >> 13); state->buf[59] = (unsigned char)(state->msgLen >> 21); state->buf[60] = (unsigned char)(state->msgLen >> 29); state->buf[61] = (unsigned char)0; state->buf[62] = (unsigned char)0; state->buf[63] = (unsigned char)0; state->bufLen = 64; md5ProcessBlock(state); // break digest into bytes state->digest[0] = (unsigned char)state->a; state->digest[1] = (unsigned char)(state->a >> 8); state->digest[2] = (unsigned char)(state->a >> 16); state->digest[3] = (unsigned char)(state->a >> 24); state->digest[4] = (unsigned char)state->b; state->digest[5] = (unsigned char)(state->b >> 8); state->digest[6] = (unsigned char)(state->b >> 16); state->digest[7] = (unsigned char)(state->b >> 24); state->digest[8] = (unsigned char)state->c; state->digest[9] = (unsigned char)(state->c >> 8); state->digest[10] = (unsigned char)(state->c >> 16); state->digest[11] = (unsigned char)(state->c >> 24); state->digest[12] = (unsigned char)state->d; state->digest[13] = (unsigned char)(state->d >> 8); state->digest[14] = (unsigned char)(state->d >> 16); state->digest[15] = (unsigned char)(state->d >> 24); } void md5(const unsigned char *msg, int msgLen, unsigned char *digest) { if (msgLen < 0) { return; } MD5State state; md5Start(&state); md5Append(&state, msg, msgLen); md5Finish(&state); for (int i = 0; i < 16; ++i) { digest[i] = state.digest[i]; } } //------------------------------------------------------------------------ // SHA-256 hash //------------------------------------------------------------------------ static const unsigned int sha256K[64] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; static inline unsigned int rotr(unsigned int x, unsigned int n) { return (x >> n) | (x << (32 - n)); } static inline unsigned int sha256Ch(unsigned int x, unsigned int y, unsigned int z) { return (x & y) ^ (~x & z); } static inline unsigned int sha256Maj(unsigned int x, unsigned int y, unsigned int z) { return (x & y) ^ (x & z) ^ (y & z); } static inline unsigned int sha256Sigma0(unsigned int x) { return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22); } static inline unsigned int sha256Sigma1(unsigned int x) { return rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25); } static inline unsigned int sha256sigma0(unsigned int x) { return rotr(x, 7) ^ rotr(x, 18) ^ (x >> 3); } static inline unsigned int sha256sigma1(unsigned int x) { return rotr(x, 17) ^ rotr(x, 19) ^ (x >> 10); } static void sha256HashBlock(const unsigned char *blk, unsigned int *H) { unsigned int W[64]; unsigned int a, b, c, d, e, f, g, h; unsigned int T1, T2; unsigned int t; // 1. prepare the message schedule for (t = 0; t < 16; ++t) { W[t] = (blk[t * 4] << 24) | (blk[t * 4 + 1] << 16) | (blk[t * 4 + 2] << 8) | blk[t * 4 + 3]; } for (t = 16; t < 64; ++t) { W[t] = sha256sigma1(W[t - 2]) + W[t - 7] + sha256sigma0(W[t - 15]) + W[t - 16]; } // 2. initialize the eight working variables a = H[0]; b = H[1]; c = H[2]; d = H[3]; e = H[4]; f = H[5]; g = H[6]; h = H[7]; // 3. for (t = 0; t < 64; ++t) { T1 = h + sha256Sigma1(e) + sha256Ch(e, f, g) + sha256K[t] + W[t]; T2 = sha256Sigma0(a) + sha256Maj(a, b, c); h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a = T1 + T2; } // 4. compute the intermediate hash value H[0] += a; H[1] += b; H[2] += c; H[3] += d; H[4] += e; H[5] += f; H[6] += g; H[7] += h; } static void sha256(unsigned char *msg, int msgLen, unsigned char *hash) { unsigned char blk[64]; unsigned int H[8]; int blkLen, i; H[0] = 0x6a09e667; H[1] = 0xbb67ae85; H[2] = 0x3c6ef372; H[3] = 0xa54ff53a; H[4] = 0x510e527f; H[5] = 0x9b05688c; H[6] = 0x1f83d9ab; H[7] = 0x5be0cd19; blkLen = 0; for (i = 0; i + 64 <= msgLen; i += 64) { sha256HashBlock(msg + i, H); } blkLen = msgLen - i; if (blkLen > 0) { memcpy(blk, msg + i, blkLen); } // pad the message blk[blkLen++] = 0x80; if (blkLen > 56) { while (blkLen < 64) { blk[blkLen++] = 0; } sha256HashBlock(blk, H); blkLen = 0; } while (blkLen < 56) { blk[blkLen++] = 0; } blk[56] = 0; blk[57] = 0; blk[58] = 0; blk[59] = 0; blk[60] = (unsigned char)(msgLen >> 21); blk[61] = (unsigned char)(msgLen >> 13); blk[62] = (unsigned char)(msgLen >> 5); blk[63] = (unsigned char)(msgLen << 3); sha256HashBlock(blk, H); // copy the output into the buffer (convert words to bytes) for (i = 0; i < 8; ++i) { hash[i * 4] = (unsigned char)(H[i] >> 24); hash[i * 4 + 1] = (unsigned char)(H[i] >> 16); hash[i * 4 + 2] = (unsigned char)(H[i] >> 8); hash[i * 4 + 3] = (unsigned char)H[i]; } } //------------------------------------------------------------------------ // SHA-512 hash (see FIPS 180-4) //------------------------------------------------------------------------ // SHA 384 and SHA 512 use the same sequence of eighty constant 64 bit words. static const uint64_t shaK[80] = { 0x428a2f98d728ae22ull, 0x7137449123ef65cdull, 0xb5c0fbcfec4d3b2full, 0xe9b5dba58189dbbcull, 0x3956c25bf348b538ull, 0x59f111f1b605d019ull, 0x923f82a4af194f9bull, 0xab1c5ed5da6d8118ull, 0xd807aa98a3030242ull, 0x12835b0145706fbeull, 0x243185be4ee4b28cull, 0x550c7dc3d5ffb4e2ull, 0x72be5d74f27b896full, 0x80deb1fe3b1696b1ull, 0x9bdc06a725c71235ull, 0xc19bf174cf692694ull, 0xe49b69c19ef14ad2ull, 0xefbe4786384f25e3ull, 0x0fc19dc68b8cd5b5ull, 0x240ca1cc77ac9c65ull, 0x2de92c6f592b0275ull, 0x4a7484aa6ea6e483ull, 0x5cb0a9dcbd41fbd4ull, 0x76f988da831153b5ull, 0x983e5152ee66dfabull, 0xa831c66d2db43210ull, 0xb00327c898fb213full, 0xbf597fc7beef0ee4ull, 0xc6e00bf33da88fc2ull, 0xd5a79147930aa725ull, 0x06ca6351e003826full, 0x142929670a0e6e70ull, 0x27b70a8546d22ffcull, 0x2e1b21385c26c926ull, 0x4d2c6dfc5ac42aedull, 0x53380d139d95b3dfull, 0x650a73548baf63deull, 0x766a0abb3c77b2a8ull, 0x81c2c92e47edaee6ull, 0x92722c851482353bull, 0xa2bfe8a14cf10364ull, 0xa81a664bbc423001ull, 0xc24b8b70d0f89791ull, 0xc76c51a30654be30ull, 0xd192e819d6ef5218ull, 0xd69906245565a910ull, 0xf40e35855771202aull, 0x106aa07032bbd1b8ull, 0x19a4c116b8d2d0c8ull, 0x1e376c085141ab53ull, 0x2748774cdf8eeb99ull, 0x34b0bcb5e19b48a8ull, 0x391c0cb3c5c95a63ull, 0x4ed8aa4ae3418acbull, 0x5b9cca4f7763e373ull, 0x682e6ff3d6b2b8a3ull, 0x748f82ee5defb2fcull, 0x78a5636f43172f60ull, 0x84c87814a1f0ab72ull, 0x8cc702081a6439ecull, 0x90befffa23631e28ull, 0xa4506cebde82bde9ull, 0xbef9a3f7b2c67915ull, 0xc67178f2e372532bull, 0xca273eceea26619cull, 0xd186b8c721c0c207ull, 0xeada7dd6cde0eb1eull, 0xf57d4f7fee6ed178ull, 0x06f067aa72176fbaull, 0x0a637dc5a2c898a6ull, 0x113f9804bef90daeull, 0x1b710b35131c471bull, 0x28db77f523047d84ull, 0x32caab7b40c72493ull, 0x3c9ebe0a15c9bebcull, 0x431d67c49c100d4cull, 0x4cc5d4becb3e42b6ull, 0x597f299cfc657e2aull, 0x5fcb6fab3ad6faecull, 0x6c44198c4a475817ull }; static inline uint64_t rotr(uint64_t x, uint64_t n) { return (x >> n) | (x << (64 - n)); } static inline uint64_t sha512Ch(uint64_t x, uint64_t y, uint64_t z) { return (x & y) ^ (~x & z); } static inline uint64_t sha512Maj(uint64_t x, uint64_t y, uint64_t z) { return (x & y) ^ (x & z) ^ (y & z); } static inline uint64_t sha512Sigma0(uint64_t x) { return rotr(x, 28) ^ rotr(x, 34) ^ rotr(x, 39); } static inline uint64_t sha512Sigma1(uint64_t x) { return rotr(x, 14) ^ rotr(x, 18) ^ rotr(x, 41); } static inline uint64_t sha512sigma0(uint64_t x) { return rotr(x, 1) ^ rotr(x, 8) ^ (x >> 7); } static inline uint64_t sha512sigma1(uint64_t x) { return rotr(x, 19) ^ rotr(x, 61) ^ (x >> 6); } static void sha512HashBlock(const unsigned char *blk, uint64_t *H) { uint64_t W[80]; uint64_t a, b, c, d, e, f, g, h; uint64_t T1, T2; unsigned int t; // 1. prepare the message schedule for (t = 0; t < 16; ++t) { W[t] = (((uint64_t)blk[t * 8] << 56) | ((uint64_t)blk[t * 8 + 1] << 48) | ((uint64_t)blk[t * 8 + 2] << 40) | ((uint64_t)blk[t * 8 + 3] << 32) | ((uint64_t)blk[t * 8 + 4] << 24) | ((uint64_t)blk[t * 8 + 5] << 16) | ((uint64_t)blk[t * 8 + 6] << 8) | ((uint64_t)blk[t * 8 + 7])); } for (t = 16; t < 80; ++t) { W[t] = sha512sigma1(W[t - 2]) + W[t - 7] + sha512sigma0(W[t - 15]) + W[t - 16]; } // 2. initialize the eight working variables a = H[0]; b = H[1]; c = H[2]; d = H[3]; e = H[4]; f = H[5]; g = H[6]; h = H[7]; // 3. for (t = 0; t < 80; ++t) { T1 = h + sha512Sigma1(e) + sha512Ch(e, f, g) + shaK[t] + W[t]; T2 = sha512Sigma0(a) + sha512Maj(a, b, c); h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a = T1 + T2; } // 4. compute the intermediate hash value H[0] += a; H[1] += b; H[2] += c; H[3] += d; H[4] += e; H[5] += f; H[6] += g; H[7] += h; } static void sha512(unsigned char *msg, int msgLen, unsigned char *hash) { unsigned char blk[128]; uint64_t H[8]; int blkLen = 0, i; // setting the initial hash value. H[0] = 0x6a09e667f3bcc908ull; H[1] = 0xbb67ae8584caa73bull; H[2] = 0x3c6ef372fe94f82bull; H[3] = 0xa54ff53a5f1d36f1ull; H[4] = 0x510e527fade682d1ull; H[5] = 0x9b05688c2b3e6c1full; H[6] = 0x1f83d9abfb41bd6bull; H[7] = 0x5be0cd19137e2179ull; for (i = 0; i + 128 <= msgLen; i += 128) { sha512HashBlock(msg + i, H); } blkLen = msgLen - i; if (blkLen > 0) { memcpy(blk, msg + i, blkLen); } // pad the message blk[blkLen++] = 0x80; if (blkLen > 112) { while (blkLen < 128) { blk[blkLen++] = 0; } sha512HashBlock(blk, H); blkLen = 0; } while (blkLen < 112) { blk[blkLen++] = 0; } blk[112] = 0; blk[113] = 0; blk[114] = 0; blk[115] = 0; blk[116] = 0; blk[117] = 0; blk[118] = 0; blk[119] = 0; blk[120] = 0; blk[121] = 0; blk[122] = 0; blk[123] = 0; blk[124] = (unsigned char)(msgLen >> 21); blk[125] = (unsigned char)(msgLen >> 13); blk[126] = (unsigned char)(msgLen >> 5); blk[127] = (unsigned char)(msgLen << 3); sha512HashBlock(blk, H); // copy the output into the buffer (convert words to bytes) for (i = 0; i < 8; ++i) { hash[i * 8] = (unsigned char)(H[i] >> 56); hash[i * 8 + 1] = (unsigned char)(H[i] >> 48); hash[i * 8 + 2] = (unsigned char)(H[i] >> 40); hash[i * 8 + 3] = (unsigned char)(H[i] >> 32); hash[i * 8 + 4] = (unsigned char)(H[i] >> 24); hash[i * 8 + 5] = (unsigned char)(H[i] >> 16); hash[i * 8 + 6] = (unsigned char)(H[i] >> 8); hash[i * 8 + 7] = (unsigned char)H[i]; } } //------------------------------------------------------------------------ // SHA-384 (see FIPS 180-4) //------------------------------------------------------------------------ // The algorithm is defined in the exact same manner as SHA 512 with 2 exceptions // 1.Initial hash value is different. // 2.A 384 bit message digest is obtained by truncating the final hash value. static void sha384(unsigned char *msg, int msgLen, unsigned char *hash) { unsigned char blk[128]; uint64_t H[8]; int blkLen, i; // setting initial hash values H[0] = 0xcbbb9d5dc1059ed8ull; H[1] = 0x629a292a367cd507ull; H[2] = 0x9159015a3070dd17ull; H[3] = 0x152fecd8f70e5939ull; H[4] = 0x67332667ffc00b31ull; H[5] = 0x8eb44a8768581511ull; H[6] = 0xdb0c2e0d64f98fa7ull; H[7] = 0x47b5481dbefa4fa4ull; // SHA 384 will use the same sha512HashBlock function. blkLen = 0; for (i = 0; i + 128 <= msgLen; i += 128) { sha512HashBlock(msg + i, H); } blkLen = msgLen - i; if (blkLen > 0) { memcpy(blk, msg + i, blkLen); } // pad the message blk[blkLen++] = 0x80; if (blkLen > 112) { while (blkLen < 128) { blk[blkLen++] = 0; } sha512HashBlock(blk, H); blkLen = 0; } while (blkLen < 112) { blk[blkLen++] = 0; } blk[112] = 0; blk[113] = 0; blk[114] = 0; blk[115] = 0; blk[116] = 0; blk[117] = 0; blk[118] = 0; blk[119] = 0; blk[120] = 0; blk[121] = 0; blk[122] = 0; blk[123] = 0; blk[124] = (unsigned char)(msgLen >> 21); blk[125] = (unsigned char)(msgLen >> 13); blk[126] = (unsigned char)(msgLen >> 5); blk[127] = (unsigned char)(msgLen << 3); sha512HashBlock(blk, H); // copy the output into the buffer (convert words to bytes) // hash is truncated to 384 bits. for (i = 0; i < 6; ++i) { hash[i * 8] = (unsigned char)(H[i] >> 56); hash[i * 8 + 1] = (unsigned char)(H[i] >> 48); hash[i * 8 + 2] = (unsigned char)(H[i] >> 40); hash[i * 8 + 3] = (unsigned char)(H[i] >> 32); hash[i * 8 + 4] = (unsigned char)(H[i] >> 24); hash[i * 8 + 5] = (unsigned char)(H[i] >> 16); hash[i * 8 + 6] = (unsigned char)(H[i] >> 8); hash[i * 8 + 7] = (unsigned char)H[i]; } } //------------------------------------------------------------------------ // Section 7.6.3.3 (Encryption Key algorithm) of ISO/DIS 32000-2 // Algorithm 2.B:Computing a hash (for revision 6). //------------------------------------------------------------------------ static void revision6Hash(const GooString *inputPassword, unsigned char *K, const char *userKey) { unsigned char K1[64 * (127 + 64 + 48)]; unsigned char E[64 * (127 + 64 + 48)]; DecryptAESState state; unsigned char aesKey[16]; unsigned char BE16byteNumber[16]; int inputPasswordLength = inputPassword->getLength(); int KLength = 32; const int userKeyLength = userKey ? 48 : 0; int sequenceLength; int totalLength; int rounds = 0; while (rounds < 64 || rounds < E[totalLength - 1] + 32) { sequenceLength = inputPasswordLength + KLength + userKeyLength; totalLength = 64 * sequenceLength; // a.make the string K1 memcpy(K1, inputPassword->c_str(), inputPasswordLength); memcpy(K1 + inputPasswordLength, K, KLength); if (userKey) { memcpy(K1 + inputPasswordLength + KLength, userKey, userKeyLength); } for (int i = 1; i < 64; ++i) { memcpy(K1 + (i * sequenceLength), K1, sequenceLength); } // b.Encrypt K1 memcpy(aesKey, K, 16); memcpy(state.cbc, K + 16, 16); memcpy(state.buf, state.cbc, 16); // Copy CBC IV to buf state.bufIdx = 0; state.paddingReached = false; aesKeyExpansion(&state, aesKey, 16, false); for (int i = 0; i < (4 * sequenceLength); i++) { aesEncryptBlock(&state, K1 + (16 * i)); memcpy(E + (16 * i), state.buf, 16); } memcpy(BE16byteNumber, E, 16); // c.Taking the first 16 Bytes of E as unsigned big-endian integer, // compute the remainder,modulo 3. uint64_t N1 = 0, N2 = 0, N3 = 0; // N1 contains first 8 bytes of BE16byteNumber N1 = ((uint64_t)BE16byteNumber[0] << 56 | (uint64_t)BE16byteNumber[1] << 48 | (uint64_t)BE16byteNumber[2] << 40 | (uint64_t)BE16byteNumber[3] << 32 | (uint64_t)BE16byteNumber[4] << 24 | (uint64_t)BE16byteNumber[5] << 16 | (uint64_t)BE16byteNumber[6] << 8 | (uint64_t)BE16byteNumber[7]); uint64_t rem = N1 % 3; // N2 contains 0s in higher 4 bytes and 9th to 12 th bytes of BE16byteNumber in lower 4 bytes. N2 = ((uint64_t)BE16byteNumber[8] << 24 | (uint64_t)BE16byteNumber[9] << 16 | (uint64_t)BE16byteNumber[10] << 8 | (uint64_t)BE16byteNumber[11]); rem = ((rem << 32) | N2) % 3; // N3 contains 0s in higher 4 bytes and 13th to 16th bytes of BE16byteNumber in lower 4 bytes. N3 = ((uint64_t)BE16byteNumber[12] << 24 | (uint64_t)BE16byteNumber[13] << 16 | (uint64_t)BE16byteNumber[14] << 8 | (uint64_t)BE16byteNumber[15]); rem = ((rem << 32) | N3) % 3; // d.If remainder is 0 perform SHA-256 if (rem == 0) { KLength = 32; sha256(E, totalLength, K); } // remainder is 1 perform SHA-384 else if (rem == 1) { KLength = 48; sha384(E, totalLength, K); } // remainder is 2 perform SHA-512 else if (rem == 2) { KLength = 64; sha512(E, totalLength, K); } rounds++; } // the first 32 bytes of the final K are the output of the function. } poppler-24.02.0/poppler/Decrypt.h000066400000000000000000000121551455701731300166160ustar00rootroot00000000000000//======================================================================== // // Decrypt.h // // 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) 2008 Julien Rebetez // Copyright (C) 2009 David Benjamin // Copyright (C) 2012 Fabio D'Urso // Copyright (C) 2013 Adrian Johnson // Copyright (C) 2013, 2018, 2019, 2021 Albert Astals Cid // Copyright (C) 2013 Thomas Freitag // // 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 DECRYPT_H #define DECRYPT_H #include "goo/GooString.h" #include "Object.h" #include "Stream.h" //------------------------------------------------------------------------ // Decrypt //------------------------------------------------------------------------ class Decrypt { public: // Generate a file key. The buffer must have space for at // least 16 bytes. Checks and then // and returns true if either is correct. Sets if // the owner password was correct. Either or both of the passwords // may be NULL, which is treated as an empty string. static bool makeFileKey(int encVersion, int encRevision, int keyLength, const GooString *ownerKey, const GooString *userKey, const GooString *ownerEnc, const GooString *userEnc, int permissions, const GooString *fileID, const GooString *ownerPassword, const GooString *userPassword, unsigned char *fileKey, bool encryptMetadata, bool *ownerPasswordOk); private: static bool makeFileKey2(int encVersion, int encRevision, int keyLength, const GooString *ownerKey, const GooString *userKey, int permissions, const GooString *fileID, const GooString *userPassword, unsigned char *fileKey, bool encryptMetadata); }; //------------------------------------------------------------------------ // Helper classes //------------------------------------------------------------------------ /* DecryptRC4State, DecryptAESState, DecryptAES256State are named like this for * historical reasons, but they're used for encryption too. * In case of decryption, the cbc field in AES and AES-256 contains the previous * input block or the CBC initialization vector (IV) if the stream has just been * reset). In case of encryption, it always contains the IV, whereas the * previous output is kept in buf. The paddingReached field is only used in * case of encryption. */ struct DecryptRC4State { unsigned char state[256]; unsigned char x, y; }; struct DecryptAESState { unsigned int w[44]; unsigned char state[16]; unsigned char cbc[16]; unsigned char buf[16]; bool paddingReached; // encryption only int bufIdx; }; struct DecryptAES256State { unsigned int w[60]; unsigned char state[16]; unsigned char cbc[16]; unsigned char buf[16]; bool paddingReached; // encryption only int bufIdx; }; class BaseCryptStream : public FilterStream { public: BaseCryptStream(Stream *strA, const unsigned char *fileKey, CryptAlgorithm algoA, int keyLength, Ref ref); ~BaseCryptStream() override; StreamKind getKind() const override { return strCrypt; } void reset() override; int getChar() override; int lookChar() override = 0; Goffset getPos() override; bool isBinary(bool last) const override; Stream *getUndecodedStream() override { return this; } void setAutoDelete(bool val); protected: CryptAlgorithm algo; int objKeyLength; unsigned char objKey[32]; Goffset charactersRead; // so that getPos() can be correct int nextCharBuff; // EOF means not read yet bool autoDelete; union { DecryptRC4State rc4; DecryptAESState aes; DecryptAES256State aes256; } state; }; //------------------------------------------------------------------------ // EncryptStream / DecryptStream //------------------------------------------------------------------------ class EncryptStream : public BaseCryptStream { public: EncryptStream(Stream *strA, const unsigned char *fileKey, CryptAlgorithm algoA, int keyLength, Ref ref); ~EncryptStream() override; void reset() override; int lookChar() override; }; class DecryptStream : public BaseCryptStream { public: DecryptStream(Stream *strA, const unsigned char *fileKey, CryptAlgorithm algoA, int keyLength, Ref ref); ~DecryptStream() override; void reset() override; int lookChar() override; }; //------------------------------------------------------------------------ extern void md5(const unsigned char *msg, int msgLen, unsigned char *digest); #endif poppler-24.02.0/poppler/Dict.cc000066400000000000000000000155111455701731300162240ustar00rootroot00000000000000//======================================================================== // // Dict.cc // // 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) 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) 2010 Paweł Wiejacha // Copyright (C) 2012 Fabio D'Urso // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2014 Scott West // Copyright (C) 2017 Adrian Johnson // Copyright (C) 2018 Adam Reichold // // 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 #include #include "XRef.h" #include "Dict.h" //------------------------------------------------------------------------ // Dict //------------------------------------------------------------------------ #define dictLocker() const std::scoped_lock locker(mutex) constexpr int SORT_LENGTH_LOWER_LIMIT = 32; struct Dict::CmpDictEntry { bool operator()(const DictEntry &lhs, const DictEntry &rhs) const { return lhs.first < rhs.first; } bool operator()(const DictEntry &lhs, const char *rhs) const { return lhs.first < rhs; } bool operator()(const char *lhs, const DictEntry &rhs) const { return lhs < rhs.first; } }; Dict::Dict(XRef *xrefA) { xref = xrefA; ref = 1; sorted = false; } Dict::Dict(const Dict *dictA) { xref = dictA->xref; ref = 1; entries.reserve(dictA->entries.size()); for (const auto &entry : dictA->entries) { entries.emplace_back(entry.first, entry.second.copy()); } sorted = dictA->sorted.load(); } Dict *Dict::copy(XRef *xrefA) const { dictLocker(); Dict *dictA = new Dict(this); dictA->xref = xrefA; for (auto &entry : dictA->entries) { if (entry.second.getType() == objDict) { entry.second = Object(entry.second.getDict()->copy(xrefA)); } } return dictA; } Dict *Dict::deepCopy() const { dictLocker(); Dict *dictA = new Dict(xref); dictA->entries.reserve(entries.size()); for (auto &entry : entries) { dictA->entries.emplace_back(entry.first, entry.second.deepCopy()); } return dictA; } void Dict::add(const char *key, Object &&val) { dictLocker(); entries.emplace_back(key, std::move(val)); sorted = false; } inline const Dict::DictEntry *Dict::find(const char *key) const { if (entries.size() >= SORT_LENGTH_LOWER_LIMIT) { if (!sorted) { dictLocker(); if (!sorted) { Dict *that = const_cast(this); std::sort(that->entries.begin(), that->entries.end(), CmpDictEntry {}); that->sorted = true; } } } if (sorted) { const auto pos = std::lower_bound(entries.begin(), entries.end(), key, CmpDictEntry {}); 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; }); if (pos != entries.rend()) { return &*pos; } } return nullptr; } inline Dict::DictEntry *Dict::find(const char *key) { return const_cast(const_cast(this)->find(key)); } void Dict::remove(const char *key) { dictLocker(); if (auto *entry = find(key)) { if (sorted) { const auto index = entry - &entries.front(); entries.erase(entries.begin() + index); } else { swap(*entry, entries.back()); entries.pop_back(); } } } void Dict::set(const char *key, Object &&val) { if (val.isNull()) { remove(key); return; } dictLocker(); if (auto *entry = find(key)) { entry->second = std::move(val); } else { add(key, std::move(val)); } } bool Dict::is(const char *type) const { if (const auto *entry = find("Type")) { return entry->second.isName(type); } return false; } Object Dict::lookup(const char *key, int recursion) const { if (const auto *entry = find(key)) { return entry->second.fetch(xref, recursion); } return Object(objNull); } Object Dict::lookup(const char *key, Ref *returnRef, int recursion) const { if (const auto *entry = find(key)) { if (entry->second.getType() == objRef) { *returnRef = entry->second.getRef(); } else { *returnRef = Ref::INVALID(); } return entry->second.fetch(xref, recursion); } *returnRef = Ref::INVALID(); return Object(objNull); } Object Dict::lookupEnsureEncryptedIfNeeded(const char *key) const { const auto *entry = find(key); if (!entry) { return Object(objNull); } if (entry->second.getType() == objRef && xref->isEncrypted() && !xref->isRefEncrypted(entry->second.getRef())) { error(errSyntaxError, -1, "{0:s} is not encrypted and the document is. This may be a hacking attempt", key); return Object(objNull); } return entry->second.fetch(xref); } const Object &Dict::lookupNF(const char *key) const { if (const auto *entry = find(key)) { return entry->second; } static Object nullObj(objNull); return nullObj; } bool Dict::lookupInt(const char *key, const char *alt_key, int *value) const { auto obj1 = lookup(key); if (obj1.isNull() && alt_key != nullptr) { obj1 = lookup(alt_key); } if (obj1.isInt()) { *value = obj1.getInt(); return true; } return false; } Object Dict::getVal(int i, Ref *returnRef) const { const DictEntry &entry = entries[i]; if (entry.second.getType() == objRef) { *returnRef = entry.second.getRef(); } else { *returnRef = Ref::INVALID(); } return entry.second.fetch(xref); } bool Dict::hasKey(const char *key) const { return find(key) != nullptr; } std::string Dict::findAvailableKey(const std::string &suggestedKey) { int i = 0; std::string res = suggestedKey; while (find(res.c_str())) { res = suggestedKey + std::to_string(i++); } return res; } poppler-24.02.0/poppler/Dict.h000066400000000000000000000113071455701731300160650ustar00rootroot00000000000000//======================================================================== // // Dict.h // // 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) 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 Paweł Wiejacha // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2017 Adrian Johnson // Copyright (C) 2018 Adam Reichold // // 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 DICT_H #define DICT_H #include #include #include #include #include #include "poppler-config.h" #include "poppler_private_export.h" #include "Object.h" //------------------------------------------------------------------------ // Dict //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT Dict { public: // Constructor. explicit Dict(XRef *xrefA); explicit Dict(const Dict *dictA); Dict *copy(XRef *xrefA) const; Dict *deepCopy() const; Dict(const Dict &) = delete; Dict &operator=(const Dict &) = delete; // Get number of entries. int getLength() const { return static_cast(entries.size()); } // Add an entry. (Copies key into Dict.) // val becomes a dead object after the call void add(const char *key, Object &&val); // Add an entry. (Takes ownership of key.) void add(char *key, Object &&val) = delete; // Update the value of an existing entry, otherwise create it // val becomes a dead object after the call void set(const char *key, Object &&val); // Remove an entry. This invalidate indexes void remove(const char *key); // Check if dictionary is of specified type. bool is(const char *type) const; // Look up an entry and return the value. Returns a null object // if is not in the dictionary. Object lookup(const char *key, int recursion = 0) const; // Same as above but if the returned object is a fetched Ref returns such Ref in returnRef, otherwise returnRef is Ref::INVALID() Object lookup(const char *key, Ref *returnRef, int recursion = 0) const; // Look up an entry and return the value. Returns a null object // if is not in the dictionary or if it is a ref to a non encrypted object in a partially encrypted document Object lookupEnsureEncryptedIfNeeded(const char *key) const; const Object &lookupNF(const char *key) const; bool lookupInt(const char *key, const char *alt_key, int *value) const; // Iterative accessors. const char *getKey(int i) const { return entries[i].first.c_str(); } Object getVal(int i) const { return entries[i].second.fetch(xref); } // Same as above but if the returned object is a fetched Ref returns such Ref in returnRef, otherwise returnRef is Ref::INVALID() Object getVal(int i, Ref *returnRef) const; const Object &getValNF(int i) const { return entries[i].second; } // Set the xref pointer. This is only used in one special case: the // trailer dictionary, which is read before the xref table is // parsed. void setXRef(XRef *xrefA) { xref = xrefA; } XRef *getXRef() const { return xref; } bool hasKey(const char *key) const; // Returns a key name that is not in the dictionary // It will be suggestedKey itself if available // otherwise it will start adding 0, 1, 2, 3, etc. to suggestedKey until there's one available std::string findAvailableKey(const std::string &suggestedKey); private: friend class Object; // for incRef/decRef // Reference counting. int incRef() { return ++ref; } int decRef() { return --ref; } using DictEntry = std::pair; struct CmpDictEntry; XRef *xref; // the xref table for this PDF file std::vector entries; std::atomic_int ref; // reference count std::atomic_bool sorted; mutable std::recursive_mutex mutex; const DictEntry *find(const char *key) const; DictEntry *find(const char *key); }; #endif poppler-24.02.0/poppler/DistinguishedNameParser.h000066400000000000000000000234661455701731300217740ustar00rootroot00000000000000//======================================================================== // // 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 // // 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.size() && dnPair.second.size()) { 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::find_if(dn.begin(), dn.end(), [&key](const auto &it) { return it.first == key; }); if (first == dn.end()) { return {}; } return first->second; } } // namespace DN #endif // DISTINGUISHEDNAMEPARSER_H poppler-24.02.0/poppler/Error.cc000066400000000000000000000050371455701731300164340ustar00rootroot00000000000000//======================================================================== // // Error.cc // // 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) 2005, 2007 Jeff Muizelaar // Copyright (C) 2005, 2018, 2022 Albert Astals Cid // Copyright (C) 2007 Krzysztof Kowalczyk // Copyright (C) 2012 Marek Kasik // Copyright (C) 2013, 2017 Adrian Johnson // Copyright (C) 2020 Adam Reichold // // 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 #include #include #include #include #include "GooString.h" #include "GlobalParams.h" #include "Error.h" static const char *errorCategoryNames[] = { "Syntax Warning", "Syntax Error", "Config Error", "Command Line Error", "I/O Error", "Permission Error", "Unimplemented Feature", "Internal Error" }; static ErrorCallback errorCbk = nullptr; void setErrorCallback(ErrorCallback cbk) { errorCbk = cbk; } void CDECL error(ErrorCategory category, Goffset pos, const char *msg, ...) { va_list args; // NB: this can be called before the globalParams object is created if (!errorCbk && globalParams && globalParams->getErrQuiet()) { return; } va_start(args, msg); const std::unique_ptr s = GooString::formatv(msg, args); va_end(args); GooString sanitized; for (int i = 0; i < s->getLength(); ++i) { const char c = s->getChar(i); if (c < (char)0x20 || c >= (char)0x7f) { sanitized.appendf("<{0:02x}>", c & 0xff); } else { sanitized.append(c); } } if (errorCbk) { (*errorCbk)(category, pos, sanitized.c_str()); } else { if (pos >= 0) { fprintf(stderr, "%s (%lld): %s\n", errorCategoryNames[category], (long long)pos, sanitized.c_str()); } else { fprintf(stderr, "%s: %s\n", errorCategoryNames[category], sanitized.c_str()); } fflush(stderr); } } poppler-24.02.0/poppler/Error.h000066400000000000000000000043571455701731300163020ustar00rootroot00000000000000//======================================================================== // // Error.h // // 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) 2005, 2007 Jeff Muizelaar // Copyright (C) 2005, 2018 Albert Astals Cid // Copyright (C) 2005 Kristian Høgsberg // Copyright (C) 2013 Adrian Johnson // Copyright (C) 2014 Fabio D'Urso // Copyright (C) 2020 Adam Reichold // // 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 ERROR_H #define ERROR_H #include #include "poppler-config.h" #include "poppler_private_export.h" #include "goo/gfile.h" #include "goo/GooString.h" enum ErrorCategory { errSyntaxWarning, // PDF syntax error which can be worked around; // output will probably be correct errSyntaxError, // PDF syntax error which can be worked around; // output will probably be incorrect errConfig, // error in Xpdf config info (xpdfrc file, etc.) errCommandLine, // error in user-supplied parameters, action not // allowed, etc. (only used by command-line tools) errIO, // error in file I/O errNotAllowed, // action not allowed by PDF permission bits errUnimplemented, // unimplemented PDF feature - display will be // incorrect errInternal // internal error - malfunction within the Xpdf code }; using ErrorCallback = void (*)(ErrorCategory category, Goffset pos, const char *msg); extern void POPPLER_PRIVATE_EXPORT setErrorCallback(ErrorCallback cbk); extern void CDECL POPPLER_PRIVATE_EXPORT error(ErrorCategory category, Goffset pos, const char *msg, ...) GOOSTRING_FORMAT; #endif poppler-24.02.0/poppler/ErrorCodes.h000066400000000000000000000037121455701731300172520ustar00rootroot00000000000000//======================================================================== // // ErrorCodes.h // // Copyright 2002-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) 2017 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 // //======================================================================== #ifndef ERRORCODES_H #define ERRORCODES_H #define errNone 0 // no error #define errOpenFile 1 // couldn't open the PDF file #define errBadCatalog 2 // couldn't read the page catalog #define errDamaged \ 3 // PDF file was damaged and couldn't be // repaired #define errEncrypted \ 4 // file was encrypted and password was // incorrect or not supplied #define errHighlightFile 5 // nonexistent or invalid highlight file #define errBadPrinter 6 // invalid printer #define errPrinting 7 // error during printing #define errPermission 8 // PDF file doesn't allow that operation #define errBadPageNum 9 // invalid page number #define errFileIO 10 // file I/O error #define errFileChangedSinceOpen 11 // file has changed since opening and save can't be done #endif poppler-24.02.0/poppler/FDPDFDocBuilder.cc000066400000000000000000000033521455701731300201210ustar00rootroot00000000000000//======================================================================== // // FileDescriptorPDFDocBuilder.cc // // This file is licensed under the GPLv2 or later // // Copyright 2010 Hib Eris // Copyright 2010, 2017, 2021, 2022 Albert Astals Cid // Copyright 2021 Oliver Sander // Copyright 2021 Christian Persch // //======================================================================== #include #include #include "FDPDFDocBuilder.h" #include "FILECacheLoader.h" #include "CachedFile.h" //------------------------------------------------------------------------ // FileDescriptorPDFDocBuilder //------------------------------------------------------------------------ int FileDescriptorPDFDocBuilder::parseFdFromUri(const GooString &uri) { int fd = -1; char c; if (sscanf(uri.c_str(), "fd://%d%c", &fd, &c) != 1) { return -1; } return fd; } std::unique_ptr FileDescriptorPDFDocBuilder::buildPDFDoc(const GooString &uri, const std::optional &ownerPassword, const std::optional &userPassword, void *guiDataA) { const auto fd = parseFdFromUri(uri); if (fd == -1) { return {}; } FILE *file; if (fd == fileno(stdin)) { file = stdin; } else { file = fdopen(fd, "rb"); } if (!file) { return {}; } CachedFile *cachedFile = new CachedFile(new FILECacheLoader(file)); return std::make_unique(new CachedFileStream(cachedFile, 0, false, cachedFile->getLength(), Object(objNull)), ownerPassword, userPassword); } bool FileDescriptorPDFDocBuilder::supports(const GooString &uri) { return parseFdFromUri(uri) != -1; } poppler-24.02.0/poppler/FDPDFDocBuilder.h000066400000000000000000000023341455701731300177620ustar00rootroot00000000000000//======================================================================== // // FileDescriptorPDFDocBuilder.h // // This file is licensed under the GPLv2 or later // // Copyright 2010 Hib Eris // Copyright 2010, 2018, 2022 Albert Astals Cid // Copyright 2021 Oliver Sander // Copyright 2021 Christian Persch // //======================================================================== #ifndef FDPDFDOCBUILDER_H #define FDPDFDOCBUILDER_H #include "PDFDocBuilder.h" //------------------------------------------------------------------------ // FileDescriptorPDFDocBuilder // // The FileDescriptorPDFDocBuilder implements a PDFDocBuilder that read from a file descriptor. //------------------------------------------------------------------------ class FileDescriptorPDFDocBuilder : public PDFDocBuilder { public: std::unique_ptr buildPDFDoc(const GooString &uri, const std::optional &ownerPassword = {}, const std::optional &userPassword = {}, void *guiDataA = nullptr) override; bool supports(const GooString &uri) override; private: int parseFdFromUri(const GooString &uri); }; #endif /* FDPDFDOCBUILDER_H */ poppler-24.02.0/poppler/FILECacheLoader.cc000066400000000000000000000025071455701731300201340ustar00rootroot00000000000000//======================================================================== // // FILECacheLoader.cc // // This file is licensed under the GPLv2 or later // // Copyright 2010 Hib Eris // Copyright 2010, 2022 Albert Astals Cid // Copyright 2010 Jonathan Liu // Copyright 2021 Peter Williams // Copyright 2021 Christian Persch // //======================================================================== #include #include "FILECacheLoader.h" #if defined(_WIN32) || defined(__CYGWIN__) # include // for O_BINARY # include // for _setmode #endif FILECacheLoader::~FILECacheLoader() { if (file != stdin) { fclose(file); } } size_t FILECacheLoader::init(CachedFile *cachedFile) { size_t read, size = 0; char buf[CachedFileChunkSize]; #if defined(_WIN32) || defined(__CYGWIN__) _setmode(fileno(file), O_BINARY); #endif CachedFileWriter writer = CachedFileWriter(cachedFile, nullptr); do { read = fread(buf, 1, CachedFileChunkSize, file); (writer.write)(buf, CachedFileChunkSize); size += read; } while (read == CachedFileChunkSize); return size; } int FILECacheLoader::load(const std::vector &ranges, CachedFileWriter *writer) { return 0; } poppler-24.02.0/poppler/FILECacheLoader.h000066400000000000000000000015471455701731300200010ustar00rootroot00000000000000//======================================================================== // // FILECacheLoader.h // // This file is licensed under the GPLv2 or later // // Copyright 2010 Hib Eris // Copyright 2010, 2022 Albert Astals Cid // Copyright 2021 Christian Persch // //======================================================================== #ifndef FILECACHELOADER_H #define FILECACHELOADER_H #include "CachedFile.h" #include class POPPLER_PRIVATE_EXPORT FILECacheLoader : public CachedFileLoader { FILE *file = stdin; public: FILECacheLoader() = default; ~FILECacheLoader() override; explicit FILECacheLoader(FILE *fileA) : file(fileA) { } size_t init(CachedFile *cachedFile) override; int load(const std::vector &ranges, CachedFileWriter *writer) override; }; #endif poppler-24.02.0/poppler/FileSpec.cc000066400000000000000000000214771455701731300170430ustar00rootroot00000000000000//======================================================================== // // FileSpec.cc // // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // // Copyright (C) 2008-2009 Carlos Garcia Campos // Copyright (C) 2009 Kovid Goyal // Copyright (C) 2012, 2017-2021 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 // // 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 // //======================================================================== //======================================================================== // // Most of the code from Link.cc and PSOutputDev.cc // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #include #include "FileSpec.h" #include "XRef.h" #include "goo/gfile.h" EmbFile::EmbFile(Object &&efStream) { m_size = -1; m_createDate = nullptr; m_modDate = nullptr; m_checksum = nullptr; m_mimetype = nullptr; m_objStr = std::move(efStream); if (m_objStr.isStream()) { // dataDict corresponds to Table 3.41 in the PDF1.6 spec. Dict *dataDict = m_objStr.streamGetDict(); // subtype is normally the mimetype Object subtypeName = dataDict->lookup("Subtype"); if (subtypeName.isName()) { m_mimetype = new GooString(subtypeName.getName()); } // paramDict corresponds to Table 3.42 in the PDF1.6 spec Object paramDict = dataDict->lookup("Params"); if (paramDict.isDict()) { Object paramObj = paramDict.dictLookup("ModDate"); if (paramObj.isString()) { m_modDate = new GooString(paramObj.getString()); } paramObj = paramDict.dictLookup("CreationDate"); if (paramObj.isString()) { m_createDate = new GooString(paramObj.getString()); } paramObj = paramDict.dictLookup("Size"); if (paramObj.isInt()) { m_size = paramObj.getInt(); } paramObj = paramDict.dictLookup("CheckSum"); if (paramObj.isString()) { m_checksum = new GooString(paramObj.getString()); } } } } EmbFile::~EmbFile() { delete m_createDate; delete m_modDate; delete m_checksum; delete m_mimetype; } bool EmbFile::save(const char *path) { FILE *f; bool ret; if (!(f = openFile(path, "wb"))) { return false; } ret = save2(f); fclose(f); return ret; } bool EmbFile::save2(FILE *f) { int c; if (unlikely(!m_objStr.isStream())) { return false; } m_objStr.streamReset(); while ((c = m_objStr.streamGetChar()) != EOF) { fputc(c, f); } return true; } FileSpec::FileSpec(const Object *fileSpecA) { ok = true; fileName = nullptr; platformFileName = nullptr; embFile = nullptr; desc = nullptr; fileSpec = fileSpecA->copy(); Object obj1 = getFileSpecName(fileSpecA); if (!obj1.isString()) { ok = false; error(errSyntaxError, -1, "Invalid FileSpec"); return; } fileName = obj1.getString()->copy(); if (fileSpec.isDict()) { obj1 = fileSpec.dictLookup("EF"); if (obj1.isDict()) { fileStream = obj1.dictLookupNF("F").copy(); if (!fileStream.isRef()) { ok = false; fileStream.setToNull(); error(errSyntaxError, -1, "Invalid FileSpec: Embedded file stream is not an indirect reference"); return; } } obj1 = fileSpec.dictLookup("Desc"); if (obj1.isString()) { desc = obj1.getString()->copy(); } } } FileSpec::~FileSpec() { delete fileName; delete platformFileName; delete embFile; delete desc; } EmbFile *FileSpec::getEmbeddedFile() { if (!ok || !fileSpec.isDict()) { return nullptr; } if (embFile) { return embFile; } XRef *xref = fileSpec.getDict()->getXRef(); embFile = new EmbFile(fileStream.fetch(xref)); return embFile; } Object FileSpec::newFileSpecObject(XRef *xref, GooFile *file, const std::string &fileName) { Object paramsDict = Object(new Dict(xref)); paramsDict.dictSet("Size", Object(file->size())); // No Subtype in the embedded file stream dictionary for now Object streamDict = Object(new Dict(xref)); streamDict.dictSet("Length", Object(file->size())); streamDict.dictSet("Params", std::move(paramsDict)); FileStream *fStream = new FileStream(file, 0, false, file->size(), std::move(streamDict)); fStream->setNeedsEncryptionOnSave(true); Stream *stream = fStream; const Ref streamRef = xref->addIndirectObject(Object(stream)); Dict *efDict = new Dict(xref); efDict->set("F", Object(streamRef)); Dict *fsDict = new Dict(xref); fsDict->set("Type", Object(objName, "Filespec")); fsDict->set("UF", Object(new GooString(fileName))); fsDict->set("EF", Object(efDict)); return Object(fsDict); } GooString *FileSpec::getFileNameForPlatform() { if (platformFileName) { return platformFileName; } Object obj1 = getFileSpecNameForPlatform(&fileSpec); if (obj1.isString()) { platformFileName = obj1.getString()->copy(); } return platformFileName; } Object getFileSpecName(const Object *fileSpec) { if (fileSpec->isString()) { return fileSpec->copy(); } if (fileSpec->isDict()) { Object fileName = fileSpec->dictLookup("UF"); if (fileName.isString()) { return fileName; } fileName = fileSpec->dictLookup("F"); if (fileName.isString()) { return fileName; } fileName = fileSpec->dictLookup("DOS"); if (fileName.isString()) { return fileName; } fileName = fileSpec->dictLookup("Mac"); if (fileName.isString()) { return fileName; } fileName = fileSpec->dictLookup("Unix"); if (fileName.isString()) { return fileName; } } return Object(); } Object getFileSpecNameForPlatform(const Object *fileSpec) { if (fileSpec->isString()) { return fileSpec->copy(); } Object fileName; if (fileSpec->isDict()) { fileName = fileSpec->dictLookup("UF"); if (!fileName.isString()) { fileName = fileSpec->dictLookup("F"); if (!fileName.isString()) { #ifdef _WIN32 const char *platform = "DOS"; #else const char *platform = "Unix"; #endif fileName = fileSpec->dictLookup(platform); if (!fileName.isString()) { error(errSyntaxError, -1, "Illegal file spec"); return Object(); } } } } else { error(errSyntaxError, -1, "Illegal file spec"); return Object(); } // system-dependent path manipulation #ifdef _WIN32 int i, j; GooString *name = fileName.getString()->copy(); // "//...." --> "\...." // "/x/...." --> "x:\...." // "/server/share/...." --> "\\server\share\...." // convert escaped slashes to slashes and unescaped slashes to backslashes i = 0; if (name->getChar(0) == '/') { if (name->getLength() >= 2 && name->getChar(1) == '/') { name->del(0); i = 0; } else if (name->getLength() >= 2 && ((name->getChar(1) >= 'a' && name->getChar(1) <= 'z') || (name->getChar(1) >= 'A' && name->getChar(1) <= 'Z')) && (name->getLength() == 2 || name->getChar(2) == '/')) { name->setChar(0, name->getChar(1)); name->setChar(1, ':'); i = 2; } else { for (j = 2; j < name->getLength(); ++j) { if (name->getChar(j - 1) != '\\' && name->getChar(j) == '/') { break; } } if (j < name->getLength()) { name->setChar(0, '\\'); name->insert(0, '\\'); i = 2; } } } for (; i < name->getLength(); ++i) { if (name->getChar(i) == '/') { name->setChar(i, '\\'); } else if (name->getChar(i) == '\\' && i + 1 < name->getLength() && name->getChar(i + 1) == '/') { name->del(i); } } fileName = Object(name); #endif /* _WIN32 */ return fileName; } poppler-24.02.0/poppler/FileSpec.h000066400000000000000000000046371455701731300167040ustar00rootroot00000000000000//======================================================================== // // FileSpec.h // // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // // Copyright (C) 2008 Carlos Garcia Campos // Copyright (C) 2017-2019, 2021 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 // //======================================================================== #ifndef FILE_SPEC_H #define FILE_SPEC_H #include "Object.h" #include "poppler_private_export.h" class POPPLER_PRIVATE_EXPORT EmbFile { public: explicit EmbFile(Object &&efStream); ~EmbFile(); EmbFile(const EmbFile &) = delete; 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; } 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); private: bool save2(FILE *f); int m_size; GooString *m_createDate; GooString *m_modDate; GooString *m_checksum; GooString *m_mimetype; Object m_objStr; }; class POPPLER_PRIVATE_EXPORT FileSpec { public: explicit FileSpec(const Object *fileSpec); ~FileSpec(); FileSpec(const FileSpec &) = delete; FileSpec &operator=(const FileSpec &) = delete; bool isOk() const { return ok; } const GooString *getFileName() const { return fileName; } GooString *getFileNameForPlatform(); const GooString *getDescription() const { return desc; } EmbFile *getEmbeddedFile(); static Object newFileSpecObject(XRef *xref, GooFile *file, const std::string &fileName); private: bool ok; Object fileSpec; GooString *fileName; // F, UF, DOS, Mac, Unix GooString *platformFileName; Object fileStream; // Ref to F entry in UF EmbFile *embFile; GooString *desc; // Desc }; Object getFileSpecName(const Object *fileSpec); Object POPPLER_PRIVATE_EXPORT getFileSpecNameForPlatform(const Object *fileSpec); #endif /* FILE_SPEC_H */ poppler-24.02.0/poppler/FlateEncoder.cc000066400000000000000000000103151455701731300176710ustar00rootroot00000000000000//======================================================================== // // FlateEncoder.cc // // Copyright (C) 2016, William Bader // Copyright (C) 2017 Adrian Johnson // Copyright (C) 2021 Even Rouault // Copyright (C) 2022 Albert Astals Cid // // This file is under the GPLv2 or later license // //======================================================================== #include #include "FlateEncoder.h" //------------------------------------------------------------------------ // FlateEncoder //------------------------------------------------------------------------ FlateEncoder::FlateEncoder(Stream *strA) : FilterStream(strA) { int zlib_status; outBufPtr = outBufEnd = outBuf; inBufEof = outBufEof = false; // We used to assign Z_NULL to the 3 following members of zlib_stream, // but as Z_NULL is a #define to 0, using it triggers the // -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); zlib_stream.zalloc = nullptr; zlib_stream.zfree = nullptr; zlib_stream.opaque = nullptr; zlib_status = deflateInit(&zlib_stream, Z_DEFAULT_COMPRESSION); if (zlib_status != Z_OK) { inBufEof = outBufEof = true; error(errInternal, -1, "Internal: deflateInit() failed in FlateEncoder::FlateEncoder()"); } zlib_stream.next_out = outBufEnd; zlib_stream.avail_out = 1; /* anything but 0 to trigger a read */ } FlateEncoder::~FlateEncoder() { deflateEnd(&zlib_stream); if (str->isEncoder()) { delete str; } } void FlateEncoder::reset() { int zlib_status; str->reset(); outBufPtr = outBufEnd = outBuf; inBufEof = outBufEof = false; deflateEnd(&zlib_stream); zlib_status = deflateInit(&zlib_stream, Z_DEFAULT_COMPRESSION); if (zlib_status != Z_OK) { inBufEof = outBufEof = true; error(errInternal, -1, "Internal: deflateInit() failed in FlateEncoder::reset()"); } zlib_stream.next_out = outBufEnd; zlib_stream.avail_out = 1; /* anything but 0 to trigger a read */ } bool FlateEncoder::fillBuf() { unsigned int starting_avail_out; int zlib_status; /* If the output is done, don't try to read more. */ if (outBufEof) { return false; } /* The output buffer should be empty. */ /* If it is not empty, push any processed data to the start. */ if (outBufPtr > outBuf && outBufPtr < outBufEnd) { const ptrdiff_t n = outBufEnd - outBufPtr; memmove(outBuf, outBufPtr, n); outBufEnd = &outBuf[n]; } else { outBufEnd = outBuf; } outBufPtr = outBuf; /* Keep feeding zlib until we get output. */ /* zlib might consume a few input buffers */ /* before it starts producing output. */ do { /* avail_out > 0 means that zlib has depleted its input */ /* and needs a new chunk of input in order to generate */ /* more output. */ if (zlib_stream.avail_out != 0) { /* Fill the input buffer */ const int n = (inBufEof ? 0 : str->doGetChars(inBufSize, inBuf)); if (n == 0) { inBufEof = true; } zlib_stream.next_in = inBuf; zlib_stream.avail_in = n; } /* Ask zlib for output. */ zlib_stream.next_out = outBufEnd; starting_avail_out = static_cast(&outBuf[outBufSize] - outBufEnd); zlib_stream.avail_out = starting_avail_out; zlib_status = deflate(&zlib_stream, (inBufEof ? Z_FINISH : Z_NO_FLUSH)); if (zlib_status == Z_STREAM_ERROR || zlib_stream.avail_out > starting_avail_out) { /* Unrecoverable error */ inBufEof = outBufEof = true; error(errInternal, -1, "Internal: deflate() failed in FlateEncoder::fillBuf()"); return false; } } while (zlib_stream.avail_out == outBufSize && !inBufEof); outBufEnd = &outBuf[outBufSize] - zlib_stream.avail_out; if (inBufEof && zlib_stream.avail_out != 0) { outBufEof = true; } return outBufPtr < outBufEnd; } poppler-24.02.0/poppler/FlateEncoder.h000066400000000000000000000035151455701731300175370ustar00rootroot00000000000000//======================================================================== // // FlateEncoder.h // // Copyright (C) 2016, William Bader // Copyright (C) 2018, 2019, 2021 Albert Astals Cid // // This file is under the GPLv2 or later license // //======================================================================== #ifndef FLATEENCODE_H #define FLATEENCODE_H #include "poppler-config.h" #include #include #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include "goo/gmem.h" #include "goo/gfile.h" #include "Error.h" #include "Object.h" #include "Decrypt.h" #include "Stream.h" extern "C" { #include } //------------------------------------------------------------------------ // FlateEncoder //------------------------------------------------------------------------ class FlateEncoder : public FilterStream { public: explicit FlateEncoder(Stream *strA); ~FlateEncoder() override; StreamKind getKind() const override { return strWeird; } void 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; } bool isBinary(bool last = true) const override { return true; } bool isEncoder() const override { return true; } private: static const int inBufSize = 16384; static const int outBufSize = inBufSize; unsigned char inBuf[inBufSize]; unsigned char outBuf[outBufSize]; unsigned char *outBufPtr; unsigned char *outBufEnd; bool inBufEof; bool outBufEof; z_stream zlib_stream; bool fillBuf(); }; #endif poppler-24.02.0/poppler/FlateStream.cc000066400000000000000000000070721455701731300175530ustar00rootroot00000000000000//======================================================================== // // FlateStream.cc // // Copyright (C) 2005, Jeff Muizelaar // Copyright (C) 2010, 2021, Albert Astals Cid // Copyright (C) 2016, William Bader // Copyright (C) 2017, Adrian Johnson // // This file is under the GPLv2 or later license // //======================================================================== #include #include "poppler-config.h" #ifdef ENABLE_ZLIB_UNCOMPRESS # include "FlateStream.h" FlateStream::FlateStream(Stream *strA, int predictor, int columns, int colors, int bits) : FilterStream(strA) { if (predictor != 1) { pred = new StreamPredictor(this, predictor, columns, colors, bits); if (!pred->isOk()) { delete pred; pred = nullptr; } } else { pred = NULL; } out_pos = 0; memset(&d_stream, 0, sizeof(d_stream)); inflateInit(&d_stream); } FlateStream::~FlateStream() { inflateEnd(&d_stream); delete pred; delete str; } void FlateStream::reset() { // FIXME: what are the semantics of reset? // i.e. how much initialization has to happen in the constructor? /* reinitialize zlib */ inflateEnd(&d_stream); memset(&d_stream, 0, sizeof(d_stream)); inflateInit(&d_stream); str->reset(); d_stream.avail_in = 0; status = Z_OK; out_pos = 0; out_buf_len = 0; } int FlateStream::getRawChar() { return doGetRawChar(); } void FlateStream::getRawChars(int nChars, int *buffer) { for (int i = 0; i < nChars; ++i) buffer[i] = doGetRawChar(); } int FlateStream::getChar() { if (pred) return pred->getChar(); else return getRawChar(); } int FlateStream::lookChar() { if (pred) return pred->lookChar(); if (fill_buffer()) return EOF; return out_buf[out_pos]; } int FlateStream::fill_buffer() { /* only fill the buffer if it has all been used */ if (out_pos >= out_buf_len) { /* check if the flatestream has been exhausted */ if (status == Z_STREAM_END) { return -1; } /* set to the beginning of out_buf */ d_stream.avail_out = sizeof(out_buf); d_stream.next_out = out_buf; out_pos = 0; while (1) { /* buffer is empty so we need to fill it */ if (d_stream.avail_in == 0) { int c; /* read from the source stream */ while (d_stream.avail_in < sizeof(in_buf) && (c = str->getChar()) != EOF) { in_buf[d_stream.avail_in++] = c; } d_stream.next_in = in_buf; } /* keep decompressing until we can't anymore */ if (d_stream.avail_out == 0 || d_stream.avail_in == 0 || (status != Z_OK && status != Z_BUF_ERROR)) break; status = inflate(&d_stream, Z_SYNC_FLUSH); } out_buf_len = sizeof(out_buf) - d_stream.avail_out; if (status != Z_OK && status != Z_STREAM_END) return -1; if (!out_buf_len) return -1; } return 0; } GooString *FlateStream::getPSFilter(int psLevel, const char *indent) { GooString *s; if (psLevel < 3 || pred) { return NULL; } if (!(s = str->getPSFilter(psLevel, indent))) { return NULL; } s->append(indent)->append("<< >> /FlateDecode filter\n"); return s; } bool FlateStream::isBinary(bool last) const { return str->isBinary(true); } #endif poppler-24.02.0/poppler/FlateStream.h000066400000000000000000000032101455701731300174030ustar00rootroot00000000000000//======================================================================== // // FlateStream.h // // Copyright (C) 2005, Jeff Muizelaar // Copyright (C) 2010, 2011, 2019, 2021, Albert Astals Cid // // This file is under the GPLv2 or later license // //======================================================================== #ifndef FLATESTREAM_H #define FLATESTREAM_H #include "poppler-config.h" #include #include #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include "goo/gmem.h" #include "goo/gfile.h" #include "Error.h" #include "Object.h" #include "Decrypt.h" #include "Stream.h" extern "C" { #include } class FlateStream : public FilterStream { public: FlateStream(Stream *strA, int predictor, int columns, int colors, int bits); virtual ~FlateStream(); StreamKind getKind() const override { return strFlate; } void 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; bool isBinary(bool last = true) const override; private: inline int doGetRawChar() { if (fill_buffer()) return EOF; return out_buf[out_pos++]; } int fill_buffer(void); z_stream d_stream; StreamPredictor *pred; int status; /* in_buf currently needs to be 1 or we over read from EmbedStreams */ unsigned char in_buf[1]; unsigned char out_buf[4096]; int out_pos; int out_buf_len; }; #endif poppler-24.02.0/poppler/FontEncodingTables.cc000066400000000000000000002263461455701731300210630ustar00rootroot00000000000000//======================================================================== // // FontEncodingTables.cc // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #include #include #include "FontEncodingTables.h" const char *macRomanEncoding[256] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quotesingle", "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", "grave", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", nullptr, "Adieresis", "Aring", "Ccedilla", "Eacute", "Ntilde", "Odieresis", "Udieresis", "aacute", "agrave", "acircumflex", "adieresis", "atilde", "aring", "ccedilla", "eacute", "egrave", "ecircumflex", "edieresis", "iacute", "igrave", "icircumflex", "idieresis", "ntilde", "oacute", "ograve", "ocircumflex", "odieresis", "otilde", "uacute", "ugrave", "ucircumflex", "udieresis", "dagger", "degree", "cent", "sterling", "section", "bullet", "paragraph", "germandbls", "registered", "copyright", "trademark", "acute", "dieresis", "notequal", "AE", "Oslash", "infinity", "plusminus", "lessequal", "greaterequal", "yen", "mu", "partialdiff", "summation", "product", "pi", "integral", "ordfeminine", "ordmasculine", "Omega", "ae", "oslash", "questiondown", "exclamdown", "logicalnot", "radical", "florin", "approxequal", "Delta", "guillemotleft", "guillemotright", "ellipsis", "space", "Agrave", "Atilde", "Otilde", "OE", "oe", "endash", "emdash", "quotedblleft", "quotedblright", "quoteleft", "quoteright", "divide", "lozenge", "ydieresis", "Ydieresis", "fraction", "currency", "guilsinglleft", "guilsinglright", "fi", "fl", "daggerdbl", "periodcentered", "quotesinglbase", "quotedblbase", "perthousand", "Acircumflex", "Ecircumflex", "Aacute", "Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis", "Igrave", "Oacute", "Ocircumflex", "apple", "Ograve", "Uacute", "Ucircumflex", "Ugrave", "dotlessi", "circumflex", "tilde", "macron", "breve", "dotaccent", "ring", "cedilla", "hungarumlaut", "ogonek", "caron" }; const char *macExpertEncoding[256] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "space", "exclamsmall", "Hungarumlautsmall", "centoldstyle", "dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall", "parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader", "comma", "hyphen", "period", "fraction", "zerooldstyle", "oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon", "semicolon", nullptr, "threequartersemdash", nullptr, "questionsmall", nullptr, nullptr, nullptr, nullptr, "Ethsmall", nullptr, nullptr, "onequarter", "onehalf", "threequarters", "oneeighth", "threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior", nullptr, "parenrightinferior", "Circumflexsmall", "hypheninferior", "Gravesmall", "Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall", "Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall", "Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall", "Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall", nullptr, nullptr, "asuperior", "centsuperior", nullptr, nullptr, nullptr, nullptr, "Aacutesmall", "Agravesmall", "Acircumflexsmall", "Adieresissmall", "Atildesmall", "Aringsmall", "Ccedillasmall", "Eacutesmall", "Egravesmall", "Ecircumflexsmall", "Edieresissmall", "Iacutesmall", "Igravesmall", "Icircumflexsmall", "Idieresissmall", "Ntildesmall", "Oacutesmall", "Ogravesmall", "Ocircumflexsmall", "Odieresissmall", "Otildesmall", "Uacutesmall", "Ugravesmall", "Ucircumflexsmall", "Udieresissmall", nullptr, "eightsuperior", "fourinferior", "threeinferior", "sixinferior", "eightinferior", "seveninferior", "Scaronsmall", nullptr, "centinferior", "twoinferior", nullptr, "Dieresissmall", nullptr, "Caronsmall", "osuperior", "fiveinferior", nullptr, "commainferior", "periodinferior", "Yacutesmall", nullptr, "dollarinferior", nullptr, nullptr, "Thornsmall", nullptr, "nineinferior", "zeroinferior", "Zcaronsmall", "AEsmall", "Oslashsmall", "questiondownsmall", "oneinferior", "Lslashsmall", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "Cedillasmall", nullptr, nullptr, nullptr, nullptr, nullptr, "OEsmall", "figuredash", "hyphensuperior", nullptr, nullptr, nullptr, nullptr, "exclamdownsmall", nullptr, "Ydieresissmall", nullptr, "onesuperior", "twosuperior", "threesuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior", "ninesuperior", "zerosuperior", nullptr, "esuperior", "rsuperior", "tsuperior", nullptr, nullptr, "isuperior", "ssuperior", "dsuperior", nullptr, nullptr, nullptr, nullptr, nullptr, "lsuperior", "Ogoneksmall", "Brevesmall", "Macronsmall", "bsuperior", "nsuperior", "msuperior", "commasuperior", "periodsuperior", "Dotaccentsmall", "Ringsmall", nullptr, nullptr, nullptr, nullptr }; const char *winAnsiEncoding[256] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quotesingle", "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", "grave", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", "bullet", "Euro", "bullet", "quotesinglbase", "florin", "quotedblbase", "ellipsis", "dagger", "daggerdbl", "circumflex", "perthousand", "Scaron", "guilsinglleft", "OE", "bullet", "Zcaron", "bullet", "bullet", "quoteleft", "quoteright", "quotedblleft", "quotedblright", "bullet", "endash", "emdash", "tilde", "trademark", "scaron", "guilsinglright", "oe", "bullet", "zcaron", "Ydieresis", "space", "exclamdown", "cent", "sterling", "currency", "yen", "brokenbar", "section", "dieresis", "copyright", "ordfeminine", "guillemotleft", "logicalnot", "hyphen", "registered", "macron", "degree", "plusminus", "twosuperior", "threesuperior", "acute", "mu", "paragraph", "periodcentered", "cedilla", "onesuperior", "ordmasculine", "guillemotright", "onequarter", "onehalf", "threequarters", "questiondown", "Agrave", "Aacute", "Acircumflex", "Atilde", "Adieresis", "Aring", "AE", "Ccedilla", "Egrave", "Eacute", "Ecircumflex", "Edieresis", "Igrave", "Iacute", "Icircumflex", "Idieresis", "Eth", "Ntilde", "Ograve", "Oacute", "Ocircumflex", "Otilde", "Odieresis", "multiply", "Oslash", "Ugrave", "Uacute", "Ucircumflex", "Udieresis", "Yacute", "Thorn", "germandbls", "agrave", "aacute", "acircumflex", "atilde", "adieresis", "aring", "ae", "ccedilla", "egrave", "eacute", "ecircumflex", "edieresis", "igrave", "iacute", "icircumflex", "idieresis", "eth", "ntilde", "ograve", "oacute", "ocircumflex", "otilde", "odieresis", "divide", "oslash", "ugrave", "uacute", "ucircumflex", "udieresis", "yacute", "thorn", "ydieresis" }; const char *standardEncoding[256] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "exclamdown", "cent", "sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle", "quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl", nullptr, "endash", "dagger", "daggerdbl", "periodcentered", nullptr, "paragraph", "bullet", "quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", "ellipsis", "perthousand", nullptr, "questiondown", nullptr, "grave", "acute", "circumflex", "tilde", "macron", "breve", "dotaccent", "dieresis", nullptr, "ring", "cedilla", nullptr, "hungarumlaut", "ogonek", "caron", "emdash", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "AE", nullptr, "ordfeminine", nullptr, nullptr, nullptr, nullptr, "Lslash", "Oslash", "OE", "ordmasculine", nullptr, nullptr, nullptr, nullptr, nullptr, "ae", nullptr, nullptr, nullptr, "dotlessi", nullptr, nullptr, "lslash", "oslash", "oe", "germandbls", nullptr, nullptr, nullptr, nullptr }; const char *expertEncoding[256] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "space", "exclamsmall", "Hungarumlautsmall", nullptr, "dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall", "parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader", "comma", "hyphen", "period", "fraction", "zerooldstyle", "oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon", "semicolon", "commasuperior", "threequartersemdash", "periodsuperior", "questionsmall", nullptr, "asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", nullptr, nullptr, nullptr, "isuperior", nullptr, nullptr, "lsuperior", "msuperior", "nsuperior", "osuperior", nullptr, nullptr, "rsuperior", "ssuperior", "tsuperior", nullptr, "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior", nullptr, "parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall", "Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall", "Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall", "Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall", "Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "exclamdownsmall", "centoldstyle", "Lslashsmall", nullptr, nullptr, "Scaronsmall", "Zcaronsmall", "Dieresissmall", "Brevesmall", "Caronsmall", nullptr, "Dotaccentsmall", nullptr, nullptr, "Macronsmall", nullptr, nullptr, "figuredash", "hypheninferior", nullptr, nullptr, "Ogoneksmall", "Ringsmall", "Cedillasmall", nullptr, nullptr, nullptr, "onequarter", "onehalf", "threequarters", "questiondownsmall", "oneeighth", "threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds", nullptr, nullptr, "zerosuperior", "onesuperior", "twosuperior", "threesuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior", "centinferior", "dollarinferior", "periodinferior", "commainferior", "Agravesmall", "Aacutesmall", "Acircumflexsmall", "Atildesmall", "Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall", "Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall", "Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall", "Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall", "Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall", "Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall", "Ydieresissmall" }; const char *symbolEncoding[256] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "space", "exclam", "universal", "numbersign", "existential", "percent", "ampersand", "suchthat", "parenleft", "parenright", "asteriskmath", "plus", "comma", "minus", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "congruent", "Alpha", "Beta", "Chi", "Delta", "Epsilon", "Phi", "Gamma", "Eta", "Iota", "theta1", "Kappa", "Lambda", "Mu", "Nu", "Omicron", "Pi", "Theta", "Rho", "Sigma", "Tau", "Upsilon", "sigma1", "Omega", "Xi", "Psi", "Zeta", "bracketleft", "therefore", "bracketright", "perpendicular", "underscore", "radicalex", "alpha", "beta", "chi", "delta", "epsilon", "phi", "gamma", "eta", "iota", "phi1", "kappa", "lambda", "mu", "nu", "omicron", "pi", "theta", "rho", "sigma", "tau", "upsilon", "omega1", "omega", "xi", "psi", "zeta", "braceleft", "bar", "braceright", "similar", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "Upsilon1", "minute", "lessequal", "fraction", "infinity", "florin", "club", "diamond", "heart", "spade", "arrowboth", "arrowleft", "arrowup", "arrowright", "arrowdown", "degree", "plusminus", "second", "greaterequal", "multiply", "proportional", "partialdiff", "bullet", "divide", "notequal", "equivalence", "approxequal", "ellipsis", "arrowvertex", "arrowhorizex", "carriagereturn", "aleph", "Ifraktur", "Rfraktur", "weierstrass", "circlemultiply", "circleplus", "emptyset", "intersection", "union", "propersuperset", "reflexsuperset", "notsubset", "propersubset", "reflexsubset", "element", "notelement", "angle", "gradient", "registerserif", "copyrightserif", "trademarkserif", "product", "radical", "dotmath", "logicalnot", "logicaland", "logicalor", "arrowdblboth", "arrowdblleft", "arrowdblup", "arrowdblright", "arrowdbldown", "lozenge", "angleleft", "registersans", "copyrightsans", "trademarksans", "summation", "parenlefttp", "parenleftex", "parenleftbt", "bracketlefttp", "bracketleftex", "bracketleftbt", "bracelefttp", "braceleftmid", "braceleftbt", "braceex", nullptr, "angleright", "integral", "integraltp", "integralex", "integralbt", "parenrighttp", "parenrightex", "parenrightbt", "bracketrighttp", "bracketrightex", "bracketrightbt", "bracerighttp", "bracerightmid", "bracerightbt", nullptr }; const char *zapfDingbatsEncoding[256] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "space", "a1", "a2", "a202", "a3", "a4", "a5", "a119", "a118", "a117", "a11", "a12", "a13", "a14", "a15", "a16", "a105", "a17", "a18", "a19", "a20", "a21", "a22", "a23", "a24", "a25", "a26", "a27", "a28", "a6", "a7", "a8", "a9", "a10", "a29", "a30", "a31", "a32", "a33", "a34", "a35", "a36", "a37", "a38", "a39", "a40", "a41", "a42", "a43", "a44", "a45", "a46", "a47", "a48", "a49", "a50", "a51", "a52", "a53", "a54", "a55", "a56", "a57", "a58", "a59", "a60", "a61", "a62", "a63", "a64", "a65", "a66", "a67", "a68", "a69", "a70", "a71", "a72", "a73", "a74", "a203", "a75", "a204", "a76", "a77", "a78", "a79", "a81", "a82", "a83", "a84", "a97", "a98", "a99", "a100", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "a101", "a102", "a103", "a104", "a106", "a107", "a108", "a112", "a111", "a110", "a109", "a120", "a121", "a122", "a123", "a124", "a125", "a126", "a127", "a128", "a129", "a130", "a131", "a132", "a133", "a134", "a135", "a136", "a137", "a138", "a139", "a140", "a141", "a142", "a143", "a144", "a145", "a146", "a147", "a148", "a149", "a150", "a151", "a152", "a153", "a154", "a155", "a156", "a157", "a158", "a159", "a160", "a161", "a163", "a164", "a196", "a165", "a192", "a166", "a167", "a168", "a169", "a170", "a171", "a172", "a173", "a162", "a174", "a175", "a176", "a177", "a178", "a179", "a193", "a180", "a199", "a181", "a200", "a182", nullptr, "a201", "a183", "a184", "a197", "a185", "a194", "a198", "a186", "a195", "a187", "a188", "a189", "a190", "a191", nullptr }; poppler-24.02.0/poppler/FontEncodingTables.h000066400000000000000000000010641455701731300207110ustar00rootroot00000000000000//======================================================================== // // FontEncodingTables.h // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #ifndef FONTENCODINGTABLES_H #define FONTENCODINGTABLES_H extern const char *macRomanEncoding[]; extern const char *macExpertEncoding[]; extern const char *winAnsiEncoding[]; extern const char *standardEncoding[]; extern const char *expertEncoding[]; extern const char *symbolEncoding[]; extern const char *zapfDingbatsEncoding[]; #endif poppler-24.02.0/poppler/FontInfo.cc000066400000000000000000000146611455701731300170700ustar00rootroot00000000000000//======================================================================== // // FontInfo.cc // // Copyright (C) 2005, 2006 Kristian Høgsberg // Copyright (C) 2005-2008, 2010, 2017-2020, 2023 Albert Astals Cid // Copyright (C) 2005 Brad Hards // Copyright (C) 2006 Kouhei Sutou // Copyright (C) 2009 Pino Toscano // Copyright (C) 2010 Hib Eris // Copyright (C) 2010, 2012 Adrian Johnson // Copyright (C) 2010, 2013 Thomas Freitag // Copyright (C) 2011 Carlos Garcia Campos // Copyright (C) 2012 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, 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 // //======================================================================== //======================================================================== // // Based on code from pdffonts.cc // // Copyright 2001-2007 Glyph & Cog, LLC // //======================================================================== #include "config.h" #include #include #include #include #include #include "GlobalParams.h" #include "Error.h" #include "Object.h" #include "Dict.h" #include "GfxFont.h" #include "Annot.h" #include "PDFDoc.h" #include "FontInfo.h" FontInfoScanner::FontInfoScanner(PDFDoc *docA, int firstPage) { doc = docA; currentPage = firstPage + 1; } FontInfoScanner::~FontInfoScanner() { } std::vector FontInfoScanner::scan(int nPages) { Page *page; Dict *resDict; Annots *annots; int lastPage; std::vector result; if (currentPage > doc->getNumPages()) { return result; } lastPage = currentPage + nPages; if (lastPage > doc->getNumPages() + 1) { lastPage = doc->getNumPages() + 1; } std::unique_ptr xrefA(doc->getXRef()->copy()); for (int pg = currentPage; pg < lastPage; ++pg) { page = doc->getPage(pg); if (!page) { continue; } if ((resDict = page->getResourceDictCopy(xrefA.get()))) { scanFonts(xrefA.get(), resDict, &result); delete resDict; } annots = page->getAnnots(); for (Annot *annot : annots->getAnnots()) { Object obj1 = annot->getAppearanceResDict(); if (obj1.isDict()) { scanFonts(xrefA.get(), obj1.getDict(), &result); } } } currentPage = lastPage; return result; } 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 fontRef = *font->getID(); // add this font to the list if not already found if (fonts.insert(fontRef.num).second) { fontsList->push_back(new FontInfo(font.get(), xrefA)); } } } delete gfxFontDict; } // recursively scan any resource dictionaries in objects in this // resource dictionary const char *resTypes[] = { "XObject", "Pattern" }; for (const char *resType : resTypes) { 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); // 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 (!visitedObjects.insert(resourcesRef)) { continue; } if (resObj.isDict() && resObj.getDict() != resDict) { scanFonts(xrefA, resObj.getDict(), fontsList); } } } } } } FontInfo::FontInfo(GfxFont *font, XRef *xref) { fontRef = *font->getID(); // font name const std::optional &origName = font->getName(); if (origName) { name = *font->getName(); } // font type type = (FontInfo::Type)font->getType(); // check for an embedded font if (font->getType() == fontType3) { emb = true; embRef = Ref::INVALID(); } else { emb = font->getEmbeddedFontID(&embRef); } if (!emb) { GooString substituteNameAux; const std::optional fontLoc = font->locateFont(xref, nullptr, &substituteNameAux); if (fontLoc && fontLoc->locType == gfxFontLocExternal) { file = fontLoc->path; } if (substituteNameAux.getLength() > 0) { substituteName = substituteNameAux.toStr(); } } encoding = font->getEncodingName(); // look for a ToUnicode map hasToUnicode = false; Object fontObj = xref->fetch(fontRef); if (fontObj.isDict()) { hasToUnicode = fontObj.dictLookup("ToUnicode").isStream(); } // check for a font subset name: capital letters followed by a '+' // sign subset = font->isSubset(); } poppler-24.02.0/poppler/FontInfo.h000066400000000000000000000057531455701731300167340ustar00rootroot00000000000000//======================================================================== // // FontInfo.h // // Copyright (C) 2005 Kristian Høgsberg // 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 // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2019, 2021, 2022 Oliver Sander // Copyright (C) 2019 Adam Reichold // // 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 // //======================================================================== //======================================================================== // // Based on code from pdffonts.cc // // Copyright 2001-2007 Glyph & Cog, LLC // //======================================================================== #ifndef FONT_INFO_H #define FONT_INFO_H #include "Object.h" #include "poppler_private_export.h" #include #include #include class GfxFont; class PDFDoc; class POPPLER_PRIVATE_EXPORT FontInfo { public: enum Type { unknown, Type1, Type1C, Type1COT, Type3, TrueType, TrueTypeOT, CIDType0, CIDType0C, CIDType0COT, CIDTrueType, CIDTrueTypeOT }; // Constructor. FontInfo(GfxFont *fontA, XRef *xrefA); // Copy constructor FontInfo(const FontInfo &f) = default; FontInfo &operator=(const FontInfo &) = delete; const std::optional &getName() const { return name; }; const std::optional &getSubstituteName() const { return substituteName; }; const std::optional &getFile() const { return file; }; const std::string &getEncoding() const { return encoding; }; Type getType() const { return type; }; bool getEmbedded() const { return emb; }; bool getSubset() const { return subset; }; bool getToUnicode() const { return hasToUnicode; }; Ref getRef() const { return fontRef; }; Ref getEmbRef() const { return embRef; }; private: std::optional name; std::optional substituteName; std::optional file; std::string encoding; Type type; bool emb; bool subset; bool hasToUnicode; Ref fontRef; Ref embRef; }; class POPPLER_PRIVATE_EXPORT FontInfoScanner { public: // Constructor. explicit FontInfoScanner(PDFDoc *doc, int firstPage = 0); // Destructor. ~FontInfoScanner(); std::vector scan(int nPages); private: PDFDoc *doc; int currentPage; std::unordered_set fonts; RefRecursionChecker visitedObjects; void scanFonts(XRef *xrefA, Dict *resDict, std::vector *fontsList); }; #endif poppler-24.02.0/poppler/Form.cc000066400000000000000000003235671455701731300162610ustar00rootroot00000000000000//======================================================================== // // Form.cc // // This file is licensed under the GPLv2 or later // // Copyright 2006-2008 Julien Rebetez // Copyright 2007-2012, 2015-2023 Albert Astals Cid // Copyright 2007-2008, 2011 Carlos Garcia Campos // Copyright 2007, 2013, 2016, 2019, 2022 Adrian Johnson // Copyright 2007 Iñigo Martínez // Copyright 2008, 2011 Pino Toscano // Copyright 2008 Michael Vrable // Copyright 2009 Matthias Drochner // Copyright 2009 KDAB via Guillermo Amaral // Copyright 2010, 2012 Mark Riedesel // Copyright 2012 Fabio D'Urso // Copyright 2015 André Guerreiro // Copyright 2015 André Esser // Copyright 2017 Hans-Ulrich Jüttner // Copyright 2017 Bernd Kuhls // Copyright 2018 Andre Heinecke // Copyright 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright 2018 Chinmoy Ranjan Pradhan // Copyright 2018 Adam Reichold // Copyright 2018-2022 Nelson Benítez León // Copyright 2019, 2020 Oliver Sander // Copyright 2019 Tomoyuki Kubota // Copyright 2019 João Netto // Copyright 2020-2022 Marek Kasik // Copyright 2020 Thorsten Behrens // Copyright 2020, 2023 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 2023 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== #include #include #include #include #include #include #include #include #include "goo/ft_utils.h" #include "goo/gmem.h" #include "goo/gfile.h" #include "goo/GooString.h" #include "Error.h" #include "ErrorCodes.h" #include "CharCodeToUnicode.h" #include "Object.h" #include "Array.h" #include "Dict.h" #include "Gfx.h" #include "GfxFont.h" #include "GlobalParams.h" #include "Form.h" #include "PDFDoc.h" #include "DateInfo.h" #include "CryptoSignBackend.h" #include "SignatureInfo.h" #include "CertificateInfo.h" #include "XRef.h" #include "PDFDocEncoding.h" #include "Annot.h" #include "Link.h" #include "Lexer.h" #include "Parser.h" #include "CIDFontsWidthsBuilder.h" #include "fofi/FoFiTrueType.h" #include "fofi/FoFiIdentifier.h" #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) { // 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(); // unicode marker result[0] = '\xfe'; result[1] = '\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; } return result; } static GooString *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; } FormWidget::FormWidget(PDFDoc *docA, Object *aobj, unsigned num, Ref aref, FormField *fieldA) { ref = aref; ID = 0; childNum = num; doc = docA; xref = doc->getXRef(); obj = aobj->copy(); type = formUndef; field = fieldA; widget = nullptr; } FormWidget::~FormWidget() { if (widget) { widget->decRefCnt(); } } void FormWidget::print(int indent) { printf("%*s+ (%d %d): [widget]\n", indent, "", ref.num, ref.gen); } void FormWidget::createWidgetAnnotation() { if (widget) { return; } Object obj1(ref); widget = new AnnotWidget(doc, &obj, &obj1, field); } bool FormWidget::inRect(double x, double y) const { return widget ? widget->inRect(x, y) : false; } void FormWidget::getRect(double *x1, double *y1, double *x2, double *y2) const { if (widget) { widget->getRect(x1, y1, x2, y2); } } bool FormWidget::isReadOnly() const { return field->isReadOnly(); } void FormWidget::setReadOnly(bool value) { field->setReadOnly(value); } int FormWidget::encodeID(unsigned pageNum, unsigned fieldNum) { return (pageNum << 4 * sizeof(unsigned)) + fieldNum; } void FormWidget::decodeID(unsigned id, unsigned *pageNum, unsigned *fieldNum) { *pageNum = id >> 4 * sizeof(unsigned); *fieldNum = (id << 4 * sizeof(unsigned)) >> 4 * sizeof(unsigned); } const GooString *FormWidget::getPartialName() const { return field->getPartialName(); } void FormWidget::setPartialName(const GooString &name) { field->setPartialName(name); } const GooString *FormWidget::getAlternateUiName() const { return field->getAlternateUiName(); } const GooString *FormWidget::getMappingName() const { return field->getMappingName(); } GooString *FormWidget::getFullyQualifiedName() { return field->getFullyQualifiedName(); } LinkAction *FormWidget::getActivationAction() { return widget ? widget->getAction() : nullptr; } std::unique_ptr FormWidget::getAdditionalAction(Annot::FormAdditionalActionsType t) { return widget ? widget->getFormAdditionalAction(t) : nullptr; } bool FormWidget::setAdditionalAction(Annot::FormAdditionalActionsType t, const std::string &js) { if (!widget) { return false; } return widget->setFormAdditionalAction(t, js); } 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 // The "on" state may be stored under any other name Object obj1 = obj.dictLookup("AP"); if (obj1.isDict()) { Object obj2 = obj1.dictLookup("N"); if (obj2.isDict()) { for (int i = 0; i < obj2.dictGetLength(); i++) { const char *key = obj2.dictGetKey(i); if (strcmp(key, "Off") != 0) { onStr = new GooString(key); break; } } } } } const char *FormWidgetButton::getOnStr() const { if (onStr) { return onStr->c_str(); } // 12.7.4.2.3 Check Boxes // Yes should be used as the name for the on state return parent()->getButtonType() == formButtonCheck ? "Yes" : nullptr; } FormWidgetButton::~FormWidgetButton() { delete onStr; } FormButtonType FormWidgetButton::getButtonType() const { return parent()->getButtonType(); } void FormWidgetButton::setAppearanceState(const char *state) { if (!widget) { return; } widget->setAppearanceState(state); } void FormWidgetButton::updateWidgetAppearance() { // The appearance stream must NOT be regenerated for this widget type } void FormWidgetButton::setState(bool astate) { // pushButtons don't have state if (parent()->getButtonType() == formButtonPush) { return; } // Silently return if can't set ON state if (astate && !getOnStr()) { return; } parent()->setState(astate ? getOnStr() : (char *)"Off"); // Parent will call setAppearanceState() // Now handle standAlone fields which are related to this one by having the same // fully qualified name. This is *partially* by spec, as seen in "Field names" // section inside "8.6.2 Field Dictionaries" in 1.7 PDF spec. Issue #1034 if (!astate) { // We're only interested when this field is being set to ON, return; // to check if it has related fields and then set them OFF } unsigned this_page_num, this_field_num; decodeID(getID(), &this_page_num, &this_field_num); Page *this_page = doc->getCatalog()->getPage(this_page_num); const FormField *this_field = getField(); if (!this_page->hasStandaloneFields() || this_field == nullptr) { return; } auto this_page_widgets = this_page->getFormWidgets(); const FormButtonType this_button_type = getButtonType(); const int tot = this_page_widgets->getNumWidgets(); for (int i = 0; i < tot; i++) { bool found_related = false; FormWidget *wid = this_page_widgets->getWidget(i); const bool same_fqn = wid->getFullyQualifiedName()->cmp(getFullyQualifiedName()) == 0; const bool same_button_type = wid->getType() == formButton && static_cast(wid)->getButtonType() == this_button_type; if (same_fqn && same_button_type) { if (this_field->isStandAlone()) { //'this_field' is standAlone, so we need to search in both standAlone fields and normal fields if (this_field != wid->getField()) { // so take care to not choose our same field found_related = true; } } else { //'this_field' is not standAlone, so we just need to search in standAlone fields if (wid->getField()->isStandAlone()) { found_related = true; } } } if (found_related) { FormFieldButton *ffb = static_cast(wid->getField()); if (ffb == nullptr) { error(errInternal, -1, "FormWidgetButton::setState : FormFieldButton expected\n"); continue; } ffb->setState((char *)"Off", true); } } } bool FormWidgetButton::getState() const { return getOnStr() ? parent()->getState(getOnStr()) : false; } FormFieldButton *FormWidgetButton::parent() const { return static_cast(field); } FormWidgetText::FormWidgetText(PDFDoc *docA, Object *dictObj, unsigned num, Ref refA, FormField *p) : FormWidget(docA, dictObj, num, refA, p) { type = formText; } const GooString *FormWidgetText::getContent() const { return parent()->getContent(); } void FormWidgetText::updateWidgetAppearance() { if (widget) { widget->updateAppearanceStream(); } } bool FormWidgetText::isMultiline() const { return parent()->isMultiline(); } bool FormWidgetText::isPassword() const { return parent()->isPassword(); } bool FormWidgetText::isFileSelect() const { return parent()->isFileSelect(); } bool FormWidgetText::noSpellCheck() const { return parent()->noSpellCheck(); } bool FormWidgetText::noScroll() const { return parent()->noScroll(); } bool FormWidgetText::isComb() const { return parent()->isComb(); } bool FormWidgetText::isRichText() const { return parent()->isRichText(); } int FormWidgetText::getMaxLen() const { return parent()->getMaxLen(); } double FormWidgetText::getTextFontSize() { return parent()->getTextFontSize(); } void FormWidgetText::setTextFontSize(int fontSize) { parent()->setTextFontSize(fontSize); } void FormWidgetText::setContent(const GooString *new_content) { parent()->setContentCopy(new_content); } void FormWidgetText::setAppearanceContent(const GooString *new_content) { parent()->setAppearanceContentCopy(new_content); } FormFieldText *FormWidgetText::parent() const { return static_cast(field); } FormWidgetChoice::FormWidgetChoice(PDFDoc *docA, Object *dictObj, unsigned num, Ref refA, FormField *p) : FormWidget(docA, dictObj, num, refA, p) { type = formChoice; } FormWidgetChoice::~FormWidgetChoice() { } bool FormWidgetChoice::_checkRange(int i) const { if (i < 0 || i >= parent()->getNumChoices()) { error(errInternal, -1, "FormWidgetChoice::_checkRange i out of range : {0:d}", i); return false; } return true; } void FormWidgetChoice::select(int i) { if (!_checkRange(i)) { return; } parent()->select(i); } void FormWidgetChoice::toggle(int i) { if (!_checkRange(i)) { return; } parent()->toggle(i); } void FormWidgetChoice::deselectAll() { parent()->deselectAll(); } const GooString *FormWidgetChoice::getEditChoice() const { if (!hasEdit()) { error(errInternal, -1, "FormFieldChoice::getEditChoice called on a non-editable choice\n"); return nullptr; } return parent()->getEditChoice(); } void FormWidgetChoice::updateWidgetAppearance() { if (widget) { widget->updateAppearanceStream(); } } bool FormWidgetChoice::isSelected(int i) const { if (!_checkRange(i)) { return false; } return parent()->isSelected(i); } void FormWidgetChoice::setEditChoice(const GooString *new_content) { if (!hasEdit()) { error(errInternal, -1, "FormFieldChoice::setEditChoice : trying to edit an non-editable choice\n"); return; } parent()->setEditChoice(new_content); } int FormWidgetChoice::getNumChoices() const { return parent()->getNumChoices(); } const GooString *FormWidgetChoice::getChoice(int i) const { return parent()->getChoice(i); } const GooString *FormWidgetChoice::getExportVal(int i) const { return parent()->getExportVal(i); } bool FormWidgetChoice::isCombo() const { return parent()->isCombo(); } bool FormWidgetChoice::hasEdit() const { return parent()->hasEdit(); } bool FormWidgetChoice::isMultiSelect() const { return parent()->isMultiSelect(); } bool FormWidgetChoice::noSpellCheck() const { return parent()->noSpellCheck(); } bool FormWidgetChoice::commitOnSelChange() const { return parent()->commitOnSelChange(); } bool FormWidgetChoice::isListBox() const { return parent()->isListBox(); } FormFieldChoice *FormWidgetChoice::parent() const { return static_cast(field); } FormWidgetSignature::FormWidgetSignature(PDFDoc *docA, Object *dictObj, unsigned num, Ref refA, FormField *p) : FormWidget(docA, dictObj, num, refA, p) { type = formSignature; } const GooString *FormWidgetSignature::getSignature() const { return static_cast(field)->getSignature(); } SignatureInfo *FormWidgetSignature::validateSignature(bool doVerifyCert, bool forceRevalidation, time_t validationTime, bool ocspRevocationCheck, bool enableAIA) { return static_cast(field)->validateSignature(doVerifyCert, forceRevalidation, validationTime, ocspRevocationCheck, enableAIA); } // update hash with the specified range of data from the file 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]; while (start < end) { if (Gfseek(f, start, SEEK_SET) != 0) { delete[] buf; return false; } int len = BUF_SIZE; if (end - start < len) { len = static_cast(end - start); } if (fread(buf, 1, len, f) != static_cast(len)) { delete[] buf; return false; } handler->addData(buf, len); start += len; } delete[] buf; return true; } bool 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) { auto backend = CryptoSign::Factory::createActive(); if (!backend) { return false; } if (certNickname.empty()) { fprintf(stderr, "signDocument: Empty nickname\n"); return false; } auto sigHandler = backend->createSigningHandler(certNickname, HashAlgorithm::Sha256); FormFieldSignature *signatureField = static_cast(field); std::unique_ptr certInfo = sigHandler->getCertificateInfo(); if (!certInfo) { fprintf(stderr, "signDocument: error getting signature info\n"); return false; } 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), CryptoSign::maxSupportedSignatureSize, reason, location)) { return false; } // 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.c_str()); return false; } // 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 false; } // Update byte range of signature in the saved PDF Goffset sigStart, sigEnd, fileSize; 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; } // compute hash of byte ranges if (!hashFileRange(file, sigHandler.get(), 0LL, sigStart)) { fclose(file); return false; } if (!hashFileRange(file, sigHandler.get(), sigEnd, fileSize)) { fclose(file); return false; } // and sign it auto signature = sigHandler->signDetached(password); if (!signature) { fclose(file); return false; } if (signature->getLength() > CryptoSign::maxSupportedSignatureSize) { fclose(file); return false; } // pad with zeroes to placeholder length auto length = signature->getLength(); signature->append(std::string(CryptoSign::maxSupportedSignatureSize - length, '\0')); // write signature to saved file if (!updateSignature(file, sigStart, sigEnd, signature.value())) { fprintf(stderr, "signDocument: unable update signature\n"); fclose(file); return false; } signatureField->setSignature(*signature); fclose(file); return true; } bool 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(); const std::string pdfFontName = form->findPdfFontNameToUseForSigning(); if (pdfFontName.empty()) { return false; } const DefaultAppearance da { { objName, pdfFontName.c_str() }, fontSize, std::move(fontColor) }; getField()->setDefaultAppearance(da.toAppearanceString()); 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)); std::unique_ptr origBorderCopy = getWidgetAnnotation()->getBorder() ? getWidgetAnnotation()->getBorder()->copy() : nullptr; std::unique_ptr border(new AnnotBorderArray()); border->setWidth(borderWidth); getWidgetAnnotation()->setBorder(std::move(border)); 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); // say that there a now signatures and that we should append only doc->getCatalog()->getAcroForm()->dictSet("SigFlags", Object(3)); const bool success = signDocument(saveFilename, certNickname, password, reason, location, ownerPassword, userPassword); // Now bring back the annotation appearance back to what it was ffs->setDefaultAppearance(originalDefaultAppearance); ffs->setCustomAppearanceContent({}); ffs->setCustomAppearanceLeftContent({}); getWidgetAnnotation()->setAppearCharacs(std::move(origAppearCharacs)); getWidgetAnnotation()->setBorder(std::move(origBorderCopy)); getWidgetAnnotation()->generateFieldAppearance(); getWidgetAnnotation()->updateAppearanceStream(); return success; } // 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); if (!newDoc.isOk()) { return false; } XRef *newXref = newDoc.getXRef(); XRefEntry *entry = newXref->getEntry(objNum); if (entry->type != xrefEntryUncompressed) { return false; } *objStart = entry->offset; newXref->fetch(objNum, entry->gen, 0, objEnd); return true; } // find next offset containing the dummy offset '9999999999' and overwrite with offset static char *setNextOffset(char *start, Goffset offset) { char buf[50]; sprintf(buf, "%lld", offset); strcat(buf, " "); // add some padding char *p = strstr(start, "9999999999"); if (p) { memcpy(p, buf, 10); // overwrite exact size. p += 10; } else { return nullptr; } return p; } // Updates the ByteRange array of the signature object in the file. // Returns start/end of signature string and file size. bool FormWidgetSignature::updateOffsets(FILE *f, Goffset objStart, Goffset objEnd, Goffset *sigStart, Goffset *sigEnd, Goffset *fileSize) { if (Gfseek(f, 0, SEEK_END) != 0) { return false; } *fileSize = Gftell(f); if (objEnd > *fileSize) { objEnd = *fileSize; } // sanity check object offsets if (objEnd <= objStart || (objEnd - objStart >= INT_MAX)) { return false; } const size_t bufSize = static_cast(objEnd - objStart); if (Gfseek(f, objStart, SEEK_SET) != 0) { return false; } std::vector buf(bufSize + 1); if (fread(buf.data(), 1, bufSize, f) != bufSize) { return false; } buf[bufSize] = 0; // prevent string functions from searching past the end // 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 <000", 14) == 0) { *sigStart = objStart + i + 10; char *p = strchr(&buf[i], '>'); if (p) { *sigEnd = objStart + (p - buf.data()) + 1; } break; } } if (*sigStart == -1 || *sigEnd == -1) { return false; } // Search for ByteRange array and update offsets for (size_t i = 0; i < bufSize - 10; i++) { if (buf[i] == '/' && strncmp(&buf[i], "/ByteRange", 10) == 0) { // update range char *p = setNextOffset(&buf[i], *sigStart); if (!p) { return false; } p = setNextOffset(p, *sigEnd); if (!p) { return false; } p = setNextOffset(p, *fileSize - *sigEnd); if (!p) { return false; } break; } } // write buffer back to disk if (Gfseek(f, objStart, SEEK_SET) != 0) { return false; } fwrite(buf.data(), bufSize, 1, f); return true; } // Overwrite signature string in the file with new signature bool FormWidgetSignature::updateSignature(FILE *f, Goffset sigStart, Goffset sigEnd, const GooString &signature) { if (signature.getLength() * 2 + 2 != 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; fprintf(f, "%2.2x", value); } fprintf(f, "> "); return true; } bool FormWidgetSignature::createSignature(Object &vObj, Ref vRef, const GooString &name, int placeholderLength, const GooString *reason, const GooString *location) { vObj.dictAdd("Type", Object(objName, "Sig")); vObj.dictAdd("Filter", Object(objName, "Adobe.PPKLite")); vObj.dictAdd("SubFilter", Object(objName, "adbe.pkcs7.detached")); vObj.dictAdd("Name", Object(name.copy())); GooString *date = timeToDateString(nullptr); vObj.dictAdd("M", Object(date)); if (reason && (reason->getLength() > 0)) { vObj.dictAdd("Reason", Object(reason->copy())); } if (location && (location->getLength() > 0)) { vObj.dictAdd("Location", Object(location->copy())); } vObj.dictAdd("Contents", Object(objHexString, new GooString(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))); bObj.arrayAdd(Object(static_cast(9999999999LL))); bObj.arrayAdd(Object(static_cast(9999999999LL))); bObj.arrayAdd(Object(static_cast(9999999999LL))); vObj.dictAdd("ByteRange", bObj.copy()); obj.dictSet("V", Object(vRef)); xref->setModifiedObject(&obj, ref); return true; } std::vector FormWidgetSignature::getSignedRangeBounds() const { return static_cast(field)->getSignedRangeBounds(); } std::optional FormWidgetSignature::getCheckedSignature(Goffset *checkedFileSize) { return static_cast(field)->getCheckedSignature(checkedFileSize); } void FormWidgetSignature::updateWidgetAppearance() { if (widget) { widget->updateAppearanceStream(); } } //======================================================================== // FormField //======================================================================== FormField::FormField(PDFDoc *docA, Object &&aobj, const Ref aref, FormField *parentA, std::set *usedParents, FormFieldType ty) { doc = docA; xref = doc->getXRef(); obj = std::move(aobj); Dict *dict = obj.getDict(); 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; // childs Object obj1 = dict->lookup("Kids"); if (obj1.isArray()) { // Load children for (int i = 0; i < obj1.arrayGetLength(); i++) { Ref childRef; Object childObj = obj1.getArray()->get(i, &childRef); if (childRef == Ref::INVALID()) { error(errSyntaxError, -1, "Invalid form field renference"); continue; } if (!childObj.isDict()) { error(errSyntaxError, -1, "Form field child is not a dictionary"); continue; } if (usedParents->find(childRef.num) == usedParents->end()) { // Field child: it could be a form field or a widget or composed dict const Object &objParent = childObj.dictLookupNF("Parent"); Object obj3 = childObj.dictLookup("Parent"); if (objParent.isRef() || obj3.isDict()) { // Child is a form field or composed dict // We create the field, if it's composed // it will create the widget as a child std::set usedParentsAux = *usedParents; usedParentsAux.insert(childRef.num); if (terminal) { error(errSyntaxWarning, -1, "Field can't have both Widget AND Field as kids\n"); continue; } numChildren++; children = (FormField **)greallocn(children, numChildren, sizeof(FormField *)); children[numChildren - 1] = 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"); continue; } _createWidget(&childObj, childRef); } } } } } else { // No children, if it's a composed dict, create the child widget obj1 = dict->lookup("Subtype"); if (obj1.isName("Widget")) { _createWidget(&obj, ref); } } // flags obj1 = Form::fieldLookup(dict, "Ff"); if (obj1.isInt()) { int flags = obj1.getInt(); if (flags & 0x1) { // 1 -> ReadOnly readOnly = true; } if (flags & 0x2) { // 2 -> Required // TODO } if (flags & 0x4) { // 3 -> NoExport // TODO } } // Variable Text obj1 = Form::fieldLookup(dict, "DA"); if (obj1.isString()) { defaultAppearance = obj1.getString()->copy(); } obj1 = Form::fieldLookup(dict, "Q"); if (obj1.isInt()) { const VariableTextQuadding aux = static_cast(obj1.getInt()); hasQuadding = aux == VariableTextQuadding::leftJustified || aux == VariableTextQuadding::centered || aux == VariableTextQuadding::rightJustified; if (likely(hasQuadding)) { quadding = static_cast(aux); } } 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; } } void FormField::setDefaultAppearance(const std::string &appearance) { delete defaultAppearance; defaultAppearance = new GooString(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; } void FormField::print(int indent) { printf("%*s- (%d %d): [container] terminal: %s children: %d\n", indent, "", ref.num, ref.gen, terminal ? "Yes" : "No", numChildren); } void FormField::printTree(int indent) { print(indent); if (terminal) { for (int i = 0; i < numChildren; i++) { widgets[i]->print(indent + 4); } } else { for (int i = 0; i < numChildren; i++) { children[i]->printTree(indent + 4); } } } void FormField::fillChildrenSiblingsID() { if (terminal) { return; } for (int i = 0; i < numChildren; i++) { children[i]->fillChildrenSiblingsID(); } } void FormField::createWidgetAnnotations() { if (terminal) { for (int i = 0; i < numChildren; i++) { widgets[i]->createWidgetAnnotation(); } } else { for (int i = 0; i < numChildren; i++) { children[i]->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); break; case formText: widgets[numChildren - 1] = new FormWidgetText(doc, objA, numChildren - 1, aref, this); break; case formChoice: widgets[numChildren - 1] = new FormWidgetChoice(doc, objA, numChildren - 1, aref, this); break; case formSignature: widgets[numChildren - 1] = new FormWidgetSignature(doc, objA, numChildren - 1, 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]; } } } else { for (int i = 0; i < numChildren; i++) { FormWidget *result = children[i]->findWidgetByRef(aref); if (result) { return result; } } } return nullptr; } GooString *FormField::getFullyQualifiedName() { Object parentObj; const GooString *parent_name; bool unicode_encoded = false; if (fullyQualifiedName) { return fullyQualifiedName; } fullyQualifiedName = new GooString(); 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) { fullyQualifiedName->insert(0, "\0.", 2); // 2-byte unicode period if (parent_name->hasUnicodeMarker()) { 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); fullyQualifiedName->insert(0, tmp_str + 2, tmp_length - 2); // Remove the unicode BOM delete[] tmp_str; } } else { fullyQualifiedName->insert(0, '.'); // 1-byte ascii period if (parent_name->hasUnicodeMarker()) { unicode_encoded = true; fullyQualifiedName = convertToUtf16(fullyQualifiedName); fullyQualifiedName->insert(0, parent_name->c_str() + 2, parent_name->getLength() - 2); // Remove the unicode BOM } else { fullyQualifiedName->insert(0, parent_name); } } } 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\n"); return fullyQualifiedName; } } if (partialName) { if (unicode_encoded) { if (partialName->hasUnicodeMarker()) { 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); fullyQualifiedName->append(tmp_str + 2, tmp_length - 2); // Remove the unicode BOM delete[] tmp_str; } } else { if (partialName->hasUnicodeMarker()) { unicode_encoded = true; fullyQualifiedName = convertToUtf16(fullyQualifiedName); fullyQualifiedName->append(partialName->c_str() + 2, partialName->getLength() - 2); // Remove the unicode BOM } else { fullyQualifiedName->append(partialName); } } } else { int len = fullyQualifiedName->getLength(); // Remove the last period if (unicode_encoded) { if (len > 1) { fullyQualifiedName->del(len - 2, 2); } } else { if (len > 0) { fullyQualifiedName->del(len - 1, 1); } } } if (unicode_encoded) { fullyQualifiedName->prependUnicodeMarker(); } return fullyQualifiedName; } void FormField::updateChildrenAppearance() { // Recursively update each child's appearance if (terminal) { for (int i = 0; i < numChildren; i++) { widgets[i]->updateWidgetAppearance(); } } else { for (int i = 0; i < numChildren; i++) { children[i]->updateChildrenAppearance(); } } } void FormField::setReadOnly(bool value) { if (value == readOnly) { return; } readOnly = value; Dict *dict = obj.getDict(); const Object obj1 = Form::fieldLookup(dict, "Ff"); int flags = 0; if (obj1.isInt()) { flags = obj1.getInt(); } if (value) { flags |= 1; } else { flags &= ~1; } dict->set("Ff", Object(flags)); xref->setModifiedObject(&obj, ref); updateChildrenAppearance(); } void FormField::reset(const std::vector &excludedFields) { resetChildren(excludedFields); } void FormField::resetChildren(const std::vector &excludedFields) { if (!terminal) { for (int i = 0; i < numChildren; i++) { children[i]->reset(excludedFields); } } } bool FormField::isAmongExcludedFields(const std::vector &excludedFields) { Ref fieldRef; for (const std::string &field : excludedFields) { if (field.compare(field.size() - 2, 2, " R") == 0) { if (sscanf(field.c_str(), "%d %d R", &fieldRef.num, &fieldRef.gen) == 2 && fieldRef == getRef()) { return true; } } else { if (field == getFullyQualifiedName()->toStr()) { return true; } } } return false; } FormField *FormField::findFieldByRef(Ref aref) { if (terminal) { if (this->getRef() == aref) { return this; } } else { for (int i = 0; i < numChildren; i++) { FormField *result = children[i]->findFieldByRef(aref); if (result) { return result; } } } return nullptr; } FormField *FormField::findFieldByFullyQualifiedName(const std::string &name) { if (terminal) { if (getFullyQualifiedName()->cmp(name.c_str()) == 0) { return this; } } else { for (int i = 0; i < numChildren; i++) { FormField *result = children[i]->findFieldByFullyQualifiedName(name); if (result) { return result; } } } return nullptr; } //------------------------------------------------------------------------ // FormFieldButton //------------------------------------------------------------------------ FormFieldButton::FormFieldButton(PDFDoc *docA, Object &&dictObj, const Ref refA, FormField *parentA, std::set *usedParents) : FormField(docA, std::move(dictObj), refA, parentA, usedParents, formButton) { Dict *dict = obj.getDict(); active_child = -1; noAllOff = false; siblings = nullptr; numSiblings = 0; appearanceState.setToNull(); defaultAppearanceState.setToNull(); btype = formButtonCheck; Object obj1 = Form::fieldLookup(dict, "Ff"); if (obj1.isInt()) { int flags = obj1.getInt(); if (flags & 0x10000) { // 17 -> push button btype = formButtonPush; } else if (flags & 0x8000) { // 16 -> radio button btype = formButtonRadio; if (flags & 0x4000) { // 15 -> noToggleToOff noAllOff = true; } } if (flags & 0x1000000) { // 26 -> radiosInUnison error(errUnimplemented, -1, "FormFieldButton:: radiosInUnison flag unimplemented, please report a bug with a testcase\n"); } } bool isChildRadiobutton = btype == formButtonRadio && terminal && parent && parent->getType() == formButton; // Ignore "V" for child radiobuttons, so FormFieldButton::getState() does not use it and instead uses the // "V" of the parent, which is the real value indicating the active field in the radio group. Issue #159 if (btype != formButtonPush && !isChildRadiobutton) { // Even though V is inheritable we are interested in the value of this // field, if not present it's probably because it's a button in a set. appearanceState = dict->lookup("V"); defaultAppearanceState = Form::fieldLookup(dict, "DV"); } } static const char *_getButtonType(FormButtonType type) { switch (type) { case formButtonPush: return "push"; case formButtonCheck: return "check"; case formButtonRadio: return "radio"; default: break; } return "unknown"; } 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); } void FormFieldButton::setNumSiblings(int num) { numSiblings = num; siblings = (FormFieldButton **)greallocn(siblings, numSiblings, sizeof(FormFieldButton *)); } void FormFieldButton::fillChildrenSiblingsID() { if (!terminal) { for (int i = 0; i < numChildren; i++) { FormFieldButton *child = dynamic_cast(children[i]); 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]); if (i == j) { continue; } if (child == otherChild) { continue; } child->setSibling(counter, otherChild); counter++; } // now call ourselves on the child // to fill its children data child->fillChildrenSiblingsID(); } } } } bool FormFieldButton::setState(const char *state, bool ignoreToggleOff) { // A check button could behave as a radio button // when it's in a set of more than 1 buttons if (btype != formButtonRadio && btype != formButtonCheck) { return false; } if (terminal && parent && parent->getType() == formButton && appearanceState.isNull()) { // It's button in a set, set state on parent if (static_cast(parent)->setState(state)) { return true; } return false; } bool isOn = strcmp(state, "Off") != 0; if (!isOn && noAllOff && !ignoreToggleOff) { return false; // Don't allow to set all radio to off } const char *current = getAppearanceState(); bool currentFound = false, newFound = false; for (int i = 0; i < numChildren; 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]); } else { widget = static_cast(children[i]->getWidget(0)); } if (!widget->getOnStr()) { continue; } const char *onStr = widget->getOnStr(); if (current && strcmp(current, onStr) == 0) { widget->setAppearanceState("Off"); if (!isOn) { break; } currentFound = true; } if (isOn && strcmp(state, onStr) == 0) { widget->setAppearanceState(state); newFound = true; } if (currentFound && newFound) { break; } } updateState(state); return true; } bool FormFieldButton::getState(const char *state) const { if (appearanceState.isName(state)) { return true; } return (parent && parent->getType() == formButton) ? static_cast(parent)->getState(state) : false; } void FormFieldButton::updateState(const char *state) { appearanceState = Object(objName, state); obj.getDict()->set("V", appearanceState.copy()); xref->setModifiedObject(&obj, ref); } FormFieldButton::~FormFieldButton() { if (siblings) { gfree(siblings); } } void FormFieldButton::reset(const std::vector &excludedFields) { if (!isAmongExcludedFields(excludedFields)) { if (getDefaultAppearanceState()) { setState(getDefaultAppearanceState()); } else { obj.getDict()->remove("V"); // Clear check button if it doesn't have default value. // This behaviour is what Adobe Reader does, it is not written in specification. if (btype == formButtonCheck) { setState("Off"); } } } resetChildren(excludedFields); } //------------------------------------------------------------------------ // FormFieldText //------------------------------------------------------------------------ FormFieldText::FormFieldText(PDFDoc *docA, Object &&dictObj, const Ref refA, FormField *parentA, std::set *usedParents) : FormField(docA, std::move(dictObj), refA, parentA, usedParents, formText) { Dict *dict = obj.getDict(); Object obj1; content = nullptr; internalContent = nullptr; defaultContent = nullptr; multiline = password = fileSelect = doNotSpellCheck = doNotScroll = comb = richText = false; maxLen = 0; obj1 = Form::fieldLookup(dict, "Ff"); if (obj1.isInt()) { int flags = obj1.getInt(); if (flags & 0x1000) { // 13 -> Multiline multiline = true; } if (flags & 0x2000) { // 14 -> Password password = true; } if (flags & 0x100000) { // 21 -> FileSelect fileSelect = true; } if (flags & 0x400000) { // 23 -> DoNotSpellCheck doNotSpellCheck = true; } if (flags & 0x800000) { // 24 -> DoNotScroll doNotScroll = true; } if (flags & 0x1000000) { // 25 -> Comb comb = true; } if (flags & 0x2000000) { // 26 -> RichText richText = true; } } obj1 = Form::fieldLookup(dict, "MaxLen"); if (obj1.isInt()) { maxLen = obj1.getInt(); } fillContent(fillDefaultValue); fillContent(fillValue); } void FormFieldText::fillContent(FillValueType fillType) { Dict *dict = obj.getDict(); Object obj1; obj1 = Form::fieldLookup(dict, fillType == fillDefaultValue ? "DV" : "V"); if (obj1.isString()) { if (obj1.getString()->hasUnicodeMarker()) { if (obj1.getString()->getLength() > 2) { if (fillType == fillDefaultValue) { defaultContent = obj1.getString()->copy(); } else { content = obj1.getString()->copy(); } } } 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); if (fillType == fillDefaultValue) { defaultContent = new GooString(tmp_str, tmp_length); } else { content = new GooString(tmp_str, tmp_length); } 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); } void FormFieldText::setContentCopy(const GooString *new_content) { delete content; content = nullptr; if (new_content) { content = new_content->copy(); // append the unicode marker if needed if (!content->hasUnicodeMarker()) { content->prependUnicodeMarker(); } Form *form = doc->getCatalog()->getForm(); if (form) { DefaultAppearance da(defaultAppearance); if (da.getFontName().isName()) { const std::string fontName = da.getFontName().getName(); if (!fontName.empty()) { // Use the field resource dictionary if it exists 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); // 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); } } } else { // This is wrong, there has to be a Tf in DA } } } obj.getDict()->set("V", Object(content ? content->copy() : new GooString(""))); xref->setModifiedObject(&obj, ref); updateChildrenAppearance(); } void FormFieldText::setAppearanceContentCopy(const GooString *new_content) { delete internalContent; internalContent = nullptr; if (new_content) { internalContent = new_content->copy(); } updateChildrenAppearance(); } FormFieldText::~FormFieldText() { delete content; delete internalContent; delete defaultContent; } void FormFieldText::reset(const std::vector &excludedFields) { if (!isAmongExcludedFields(excludedFields)) { setContentCopy(defaultContent); if (defaultContent == nullptr) { obj.getDict()->remove("V"); } } resetChildren(excludedFields); } double FormFieldText::getTextFontSize() { std::vector daToks; int idx = parseDA(&daToks); double fontSize = -1; if (idx >= 0) { char *p = nullptr; fontSize = strtod(daToks[idx].c_str(), &p); if (!p || *p) { fontSize = -1; } } return fontSize; } void FormFieldText::setTextFontSize(int fontSize) { if (fontSize > 0 && obj.isDict()) { std::vector daToks; int idx = parseDA(&daToks); if (idx == -1) { error(errSyntaxError, -1, "FormFieldText:: invalid DA object\n"); return; } if (defaultAppearance) { delete defaultAppearance; } defaultAppearance = new GooString; for (std::size_t i = 0; i < daToks.size(); ++i) { if (i > 0) { defaultAppearance->append(' '); } if (i == (std::size_t)idx) { defaultAppearance->appendf("{0:d}", fontSize); } else { defaultAppearance->append(daToks[i]); } } obj.dictSet("DA", Object(defaultAppearance->copy())); xref->setModifiedObject(&obj, ref); updateChildrenAppearance(); } } int FormFieldText::tokenizeDA(const std::string &da, std::vector *daToks, const char *searchTok) { int idx = -1; if (daToks) { size_t i = 0; size_t j = 0; while (i < da.size()) { while (i < da.size() && Lexer::isSpace(da[i])) { ++i; } if (i < da.size()) { for (j = i + 1; j < da.size() && !Lexer::isSpace(da[j]); ++j) { } std::string tok(da, i, j - i); if (searchTok && tok == searchTok) { idx = daToks->size(); } daToks->emplace_back(std::move(tok)); i = j; } } } return idx; } int FormFieldText::parseDA(std::vector *daToks) { int idx = -1; if (obj.isDict()) { Object objDA(obj.dictLookup("DA")); if (objDA.isString()) { const GooString *da = objDA.getString(); idx = tokenizeDA(da->toStr(), daToks, "Tf") - 1; } } return idx; } //------------------------------------------------------------------------ // FormFieldChoice //------------------------------------------------------------------------ FormFieldChoice::FormFieldChoice(PDFDoc *docA, Object &&aobj, const Ref refA, FormField *parentA, std::set *usedParents) : FormField(docA, std::move(aobj), refA, parentA, usedParents, formChoice) { numChoices = 0; choices = nullptr; defaultChoices = nullptr; editedChoice = nullptr; topIdx = 0; Dict *dict = obj.getDict(); Object obj1; combo = edit = multiselect = doNotSpellCheck = doCommitOnSelChange = false; obj1 = Form::fieldLookup(dict, "Ff"); if (obj1.isInt()) { int flags = obj1.getInt(); if (flags & 0x20000) { // 18 -> Combo combo = true; } if (flags & 0x40000) { // 19 -> Edit edit = true; } if (flags & 0x200000) { // 22 -> MultiSelect multiselect = true; } if (flags & 0x400000) { // 23 -> DoNotSpellCheck doNotSpellCheck = true; } if (flags & 0x4000000) { // 27 -> CommitOnSelChange doCommitOnSelChange = true; } } obj1 = dict->lookup("TI"); if (obj1.isInt()) { topIdx = obj1.getInt(); if (topIdx < 0) { error(errSyntaxError, -1, "FormFieldChoice:: invalid topIdx entry\n"); topIdx = 0; } } obj1 = Form::fieldLookup(dict, "Opt"); 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); if (obj2.isString()) { 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"); 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"); } 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"); } } else { error(errSyntaxError, -1, "FormWidgetChoice:: invalid {0:d} Opt entry\n", i); } } } else { // empty choice } // Find selected items // Note: PDF specs say that /V has precedence over /I, but acroread seems to // do the opposite. We do the same. obj1 = Form::fieldLookup(dict, "I"); if (obj1.isArray()) { for (int i = 0; i < obj1.arrayGetLength(); i++) { Object obj2 = obj1.arrayGet(i); if (obj2.isInt() && obj2.getInt() >= 0 && obj2.getInt() < numChoices) { choices[obj2.getInt()].selected = true; } } } else { // Note: According to PDF specs, /V should *never* contain the exportVal. // However, if /Opt is an array of (exportVal,optionName) pairs, acroread // seems to expect the exportVal instead of the optionName and so we do too. fillChoices(fillValue); } fillChoices(fillDefaultValue); } void FormFieldChoice::fillChoices(FillValueType fillType) { const char *key = fillType == fillDefaultValue ? "DV" : "V"; Dict *dict = obj.getDict(); Object obj1; obj1 = Form::fieldLookup(dict, key); if (obj1.isString() || obj1.isArray()) { if (fillType == fillDefaultValue) { defaultChoices = new bool[numChoices]; memset(defaultChoices, 0, sizeof(bool) * numChoices); } if (obj1.isString()) { bool optionFound = false; for (int i = 0; i < numChoices; i++) { if (choices[i].exportVal) { if (choices[i].exportVal->cmp(obj1.getString()) == 0) { optionFound = true; } } else if (choices[i].optionName) { if (choices[i].optionName->cmp(obj1.getString()) == 0) { optionFound = true; } } if (optionFound) { if (fillType == fillDefaultValue) { defaultChoices[i] = true; } else { choices[i].selected = true; } break; // We've determined that this option is selected. No need to keep on scanning } } // Set custom value if /V doesn't refer to any predefined option and the field is user-editable if (fillType == fillValue && !optionFound && edit) { editedChoice = obj1.getString()->copy(); } } else if (obj1.isArray()) { for (int i = 0; i < numChoices; i++) { for (int j = 0; j < obj1.arrayGetLength(); j++) { const Object obj2 = obj1.arrayGet(j); if (!obj2.isString()) { error(errSyntaxError, -1, "FormWidgetChoice:: {0:s} array contains a non string object", key); continue; } bool matches = false; if (choices[i].exportVal) { if (choices[i].exportVal->cmp(obj2.getString()) == 0) { matches = true; } } else if (choices[i].optionName) { if (choices[i].optionName->cmp(obj2.getString()) == 0) { matches = true; } } if (matches) { if (fillType == fillDefaultValue) { defaultChoices[i] = true; } else { choices[i].selected = true; } break; // We've determined that this option is selected. No need to keep on scanning } } } } } } 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); } void FormFieldChoice::updateSelection() { Object objV; Object objI(objNull); if (edit && editedChoice) { // This is an editable combo-box with user-entered text objV = Object(editedChoice->copy()); } else { const int numSelected = getNumSelected(); // Create /I array only if multiple selection is allowed (as per PDF spec) if (multiselect) { objI = Object(new Array(xref)); } if (numSelected == 0) { // No options are selected objV = Object(new GooString("")); } else if (numSelected == 1) { // Only one option is selected for (int i = 0; i < numChoices; i++) { if (choices[i].selected) { if (multiselect) { objI.arrayAdd(Object(i)); } if (choices[i].exportVal) { objV = Object(choices[i].exportVal->copy()); } else if (choices[i].optionName) { objV = Object(choices[i].optionName->copy()); } break; // We've just written the selected option. No need to keep on scanning } } } else { // More than one option is selected objV = Object(new Array(xref)); for (int i = 0; i < numChoices; i++) { if (choices[i].selected) { if (multiselect) { objI.arrayAdd(Object(i)); } if (choices[i].exportVal) { objV.arrayAdd(Object(choices[i].exportVal->copy())); } else if (choices[i].optionName) { objV.arrayAdd(Object(choices[i].optionName->copy())); } } } } } obj.getDict()->set("V", std::move(objV)); obj.getDict()->set("I", std::move(objI)); xref->setModifiedObject(&obj, ref); updateChildrenAppearance(); } void FormFieldChoice::unselectAll() { for (int i = 0; i < numChoices; i++) { choices[i].selected = false; } } void FormFieldChoice::deselectAll() { delete editedChoice; editedChoice = nullptr; unselectAll(); updateSelection(); } void FormFieldChoice::toggle(int i) { delete editedChoice; editedChoice = nullptr; choices[i].selected = !choices[i].selected; updateSelection(); } void FormFieldChoice::select(int i) { delete editedChoice; editedChoice = nullptr; if (!multiselect) { unselectAll(); } choices[i].selected = true; updateSelection(); } void FormFieldChoice::setEditChoice(const GooString *new_content) { delete editedChoice; editedChoice = nullptr; unselectAll(); if (new_content) { editedChoice = new_content->copy(); // append the unicode marker if needed if (!editedChoice->hasUnicodeMarker()) { editedChoice->prependUnicodeMarker(); } } updateSelection(); } const GooString *FormFieldChoice::getEditChoice() const { return editedChoice; } int FormFieldChoice::getNumSelected() { int cnt = 0; for (int i = 0; i < numChoices; i++) { if (choices[i].selected) { cnt++; } } return cnt; } const GooString *FormFieldChoice::getSelectedChoice() const { if (edit && editedChoice) { return editedChoice; } for (int i = 0; i < numChoices; i++) { if (choices[i].optionName && choices[i].selected) { return choices[i].optionName; } } return nullptr; } void FormFieldChoice::reset(const std::vector &excludedFields) { if (!isAmongExcludedFields(excludedFields)) { delete editedChoice; editedChoice = nullptr; if (defaultChoices) { for (int i = 0; i < numChoices; i++) { choices[i].selected = defaultChoices[i]; } } else { unselectAll(); } } resetChildren(excludedFields); updateSelection(); } //------------------------------------------------------------------------ // 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) { signature_info = new SignatureInfo(); parseInfo(); } FormFieldSignature::~FormFieldSignature() { delete signature_info; delete signature; } void FormFieldSignature::setSignature(const GooString &sig) { delete signature; signature = sig.copy(); } const GooString &FormFieldSignature::getCustomAppearanceContent() const { return customAppearanceContent; } void FormFieldSignature::setCustomAppearanceContent(const GooString &s) { customAppearanceContent = GooString(s.toStr()); } const GooString &FormFieldSignature::getCustomAppearanceLeftContent() const { return customAppearanceLeftContent; } void FormFieldSignature::setCustomAppearanceLeftContent(const GooString &s) { customAppearanceLeftContent = GooString(s.toStr()); } double FormFieldSignature::getCustomAppearanceLeftFontSize() const { return customAppearanceLeftFontSize; } void FormFieldSignature::setCustomAppearanceLeftFontSize(double size) { customAppearanceLeftFontSize = size; } Ref FormFieldSignature::getImageResource() const { return imageResource; } void FormFieldSignature::setImageResource(const Ref imageResourceA) { imageResource = imageResourceA; } void FormFieldSignature::setCertificateInfo(std::unique_ptr &certInfo) { certificate_info.swap(certInfo); } FormWidget *FormFieldSignature::getCreateWidget() { ::FormWidget *fw = getWidget(0); if (!fw) { error(errSyntaxError, 0, "FormFieldSignature: was asked for widget and didn't had one, creating it"); _createWidget(&obj, ref); fw = getWidget(0); fw->createWidgetAnnotation(); } return fw; } void FormFieldSignature::parseInfo() { if (!obj.isDict()) { return; } // retrieve PKCS#7 Object sig_dict = obj.dictLookup("V"); if (!sig_dict.isDict()) { return; } Object contents_obj = sig_dict.dictLookup("Contents"); if (contents_obj.isString()) { signature = contents_obj.getString()->copy(); } byte_range = sig_dict.dictLookup("ByteRange"); const Object location_obj = sig_dict.dictLookup("Location"); if (location_obj.isString()) { signature_info->setLocation(location_obj.getString()); } const Object reason_obj = sig_dict.dictLookup("Reason"); if (reason_obj.isString()) { signature_info->setReason(reason_obj.getString()); } // retrieve SigningTime Object time_of_signing = sig_dict.dictLookup("M"); if (time_of_signing.isString()) { const GooString *time_str = time_of_signing.getString(); signature_info->setSigningTime(dateStringToTime(time_str)); // Put this information directly in SignatureInfo object } // 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; } } void FormFieldSignature::hashSignedDataBlock(CryptoSign::VerificationInterface *handler, Goffset block_len) { if (!handler) { return; } const int BLOCK_SIZE = 4096; unsigned char signed_data_buffer[BLOCK_SIZE]; Goffset i = 0; while (i < block_len) { Goffset bytes_left = block_len - i; if (bytes_left < BLOCK_SIZE) { doc->getBaseStream()->doGetChars(static_cast(bytes_left), signed_data_buffer); handler->addData(signed_data_buffer, static_cast(bytes_left)); i = block_len; } else { doc->getBaseStream()->doGetChars(BLOCK_SIZE, signed_data_buffer); handler->addData(signed_data_buffer, BLOCK_SIZE); i += BLOCK_SIZE; } } } FormSignatureType FormWidgetSignature::signatureType() const { return static_cast(field)->getSignatureType(); } void FormWidgetSignature::setSignatureType(FormSignatureType fst) { static_cast(field)->setSignatureType(fst); } SignatureInfo *FormFieldSignature::validateSignature(bool doVerifyCert, bool forceRevalidation, time_t validationTime, bool ocspRevocationCheck, bool enableAIA) { auto backend = CryptoSign::Factory::createActive(); if (!backend) { return signature_info; } if (signature_info->getSignatureValStatus() != SIGNATURE_NOT_VERIFIED && !forceRevalidation) { return signature_info; } if (signature == nullptr) { error(errSyntaxError, 0, "Invalid or missing Signature string"); return signature_info; } if (!byte_range.isArray()) { error(errSyntaxError, 0, "Invalid or missing ByteRange array"); return signature_info; } int arrayLen = byte_range.arrayGetLength(); if (arrayLen < 2) { error(errSyntaxError, 0, "Too few elements in ByteRange array"); return signature_info; } const int signature_len = signature->getLength(); std::vector signatureData(signature_len); memcpy(signatureData.data(), signature->c_str(), signature_len); auto signature_handler = backend->createVerificationHandler(std::move(signatureData)); Goffset fileLength = doc->getBaseStream()->getLength(); for (int i = 0; i < arrayLen / 2; i++) { Object offsetObj = byte_range.arrayGet(i * 2); Object lenObj = byte_range.arrayGet(i * 2 + 1); if (!offsetObj.isIntOrInt64() || !lenObj.isIntOrInt64()) { error(errSyntaxError, 0, "Illegal values in ByteRange array"); return signature_info; } Goffset offset = offsetObj.getIntOrInt64(); Goffset len = lenObj.getIntOrInt64(); if (offset < 0 || offset >= fileLength || len < 0 || len > fileLength || offset + len > fileLength) { error(errSyntaxError, 0, "Illegal values in ByteRange array"); return signature_info; } doc->getBaseStream()->setPos(offset); hashSignedDataBlock(signature_handler.get(), len); } if (!signature_info->isSubfilterSupported()) { error(errUnimplemented, 0, "Unable to validate this type of signature"); return signature_info; } 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() != 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) { return signature_info; } const CertificateValidationStatus cert_val_state = signature_handler->validateCertificate(std::chrono::system_clock::from_time_t(validationTime), ocspRevocationCheck, enableAIA); signature_info->setCertificateValStatus(cert_val_state); return signature_info; } std::vector FormFieldSignature::getSignedRangeBounds() const { std::vector range_vec; if (byte_range.isArray()) { if (byte_range.arrayGetLength() == 4) { for (int i = 0; i < 2; ++i) { const Object offsetObj(byte_range.arrayGet(2 * i)); const Object lenObj(byte_range.arrayGet(2 * i + 1)); if (offsetObj.isIntOrInt64() && lenObj.isIntOrInt64()) { const Goffset offset = offsetObj.getIntOrInt64(); const Goffset len = lenObj.getIntOrInt64(); range_vec.push_back(offset); range_vec.push_back(offset + len); } } } } return range_vec; } std::optional FormFieldSignature::getCheckedSignature(Goffset *checkedFileSize) { Goffset start = 0; Goffset end = 0; const std::vector ranges = getSignedRangeBounds(); if (ranges.size() == 4) { start = ranges[1]; end = ranges[2]; } if (end >= start + 6) { BaseStream *stream = doc->getBaseStream(); *checkedFileSize = stream->getLength(); Goffset len = end - start; stream->setPos(end - 1); int c2 = stream->lookChar(); stream->setPos(start); int c1 = stream->getChar(); // PDF signatures are first ASN1 DER, then hex encoded PKCS#7 structures, // possibly padded with 0 characters and enclosed in '<' and '>'. // The ASN1 DER encoding of a PKCS#7 structure must start with the tag 0x30 // for SEQUENCE. The next byte must be 0x80 for ASN1 DER indefinite length // encoding or (0x80 + n) for ASN1 DER definite length encoding // where n is the number of subsequent "length bytes" which big-endian // encode the length of the content of the SEQUENCE following them. if (len <= std::numeric_limits::max() && *checkedFileSize > end && c1 == '<' && c2 == '>') { GooString gstr; ++start; --end; len = end - start; Goffset pos = 0; do { c1 = stream->getChar(); if (c1 == EOF) { return {}; } 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 (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; } } if (len > 0) { return GooString(&gstr, 0, len); } } } } return {}; } void FormFieldSignature::print(int indent) { printf("%*s- (%d %d): [signature] terminal: %s children: %d\n", indent, "", ref.num, ref.gen, terminal ? "Yes" : "No", numChildren); } //------------------------------------------------------------------------ // Form //------------------------------------------------------------------------ Form::Form(PDFDoc *docA) : doc(docA) { Object obj1; XRef *xref = doc->getXRef(); size = 0; numFields = 0; rootFields = nullptr; quadding = VariableTextQuadding::leftJustified; defaultAppearance = nullptr; defaultResources = nullptr; Object *acroForm = doc->getCatalog()->getAcroForm(); needAppearances = acroForm->dictLookup("NeedAppearances").getBoolWithDefaultValue(false); obj1 = acroForm->dictLookup("DA"); if (obj1.isString()) { defaultAppearance = obj1.getString()->copy(); } obj1 = acroForm->dictLookup("Q"); if (obj1.isInt()) { const VariableTextQuadding aux = static_cast(obj1.getInt()); if (aux == VariableTextQuadding::leftJustified || aux == VariableTextQuadding::centered || aux == VariableTextQuadding::rightJustified) { quadding = static_cast(aux); } } resDict = acroForm->dictLookup("DR"); if (resDict.isDict()) { // At a minimum, this dictionary shall contain a Font entry obj1 = resDict.dictLookup("Font"); if (obj1.isDict()) { defaultResources = new GfxResources(xref, resDict.getDict(), nullptr); } } if (!defaultResources) { resDict.setToNull(); } obj1 = acroForm->dictLookup("Fields"); if (obj1.isArray()) { Array *array = obj1.getArray(); std::set alreadyReadRefs; for (int i = 0; i < array->getLength(); i++) { Object obj2 = array->get(i); const Object &oref = array->getNF(i); if (!oref.isRef()) { error(errSyntaxWarning, -1, "Direct object in rootFields"); continue; } if (!obj2.isDict()) { error(errSyntaxWarning, -1, "Reference in Fields array to an invalid or non existent object"); continue; } if (alreadyReadRefs.find(oref.getRef()) != alreadyReadRefs.end()) { continue; } 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); } } else { error(errSyntaxError, -1, "Can't get Fields array\n"); } obj1 = acroForm->dictLookup("CO"); if (obj1.isArray()) { Array *array = obj1.getArray(); calculateOrder.reserve(array->getLength()); for (int i = 0; i < array->getLength(); i++) { const Object &oref = array->getNF(i); if (!oref.isRef()) { error(errSyntaxWarning, -1, "Direct object in CO"); continue; } calculateOrder.push_back(oref.getRef()); } } // for (int i = 0; i < numFields; i++) // rootFields[i]->printTree(); } Form::~Form() { int i; for (i = 0; i < numFields; ++i) { delete rootFields[i]; } gfree(rootFields); delete defaultAppearance; delete defaultResources; } // Look up an inheritable field dictionary entry. static Object fieldLookup(Dict *field, const char *key, std::set *usedParents) { Dict *dict = field; Object obj = dict->lookup(key); if (!obj.isNull()) { return obj; } const Object &parent = dict->lookupNF("Parent"); if (parent.isRef()) { const Ref ref = parent.getRef(); if (usedParents->find(ref.num) == usedParents->end()) { usedParents->insert(ref.num); Object obj2 = parent.fetch(dict->getXRef()); if (obj2.isDict()) { return fieldLookup(obj2.getDict(), key, usedParents); } } } else if (parent.isDict()) { return fieldLookup(parent.getDict(), key, usedParents); } return Object(objNull); } Object Form::fieldLookup(Dict *field, const char *key) { std::set usedParents; return ::fieldLookup(field, key, &usedParents); } FormField *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); } else if (obj2.isName("Tx")) { field = new FormFieldText(docA, std::move(obj), aref, parent, usedParents); } else if (obj2.isName("Ch")) { field = new FormFieldChoice(docA, std::move(obj), aref, parent, usedParents); } else if (obj2.isName("Sig")) { field = new FormFieldSignature(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 field; } static const std::string kOurDictFontNamePrefix = "popplerfont"; std::string Form::findFontInDefaultResources(const std::string &fontFamily, const std::string &fontStyle) const { if (!resDict.isDict()) { return {}; } const std::string fontFamilyAndStyle = fontStyle.empty() ? fontFamily : fontFamily + " " + fontStyle; Object fontDictObj = resDict.dictLookup("Font"); assert(fontDictObj.isDict()); const Dict *fontDict = fontDictObj.getDict(); for (int i = 0; i < fontDict->getLength(); ++i) { const char *key = fontDict->getKey(i); if (GooString::startsWith(key, kOurDictFontNamePrefix)) { const Object fontObj = fontDict->getVal(i); if (fontObj.isDict() && fontObj.dictIs("Font")) { const Object fontBaseFontObj = fontObj.dictLookup("BaseFont"); if (fontBaseFontObj.isName(fontFamilyAndStyle.c_str())) { return key; } } } } return {}; } 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, forceName); if (!addFontRes.fontName.empty()) { return addFontRes; } filesToIgnore.emplace_back(findFontRes.filepath); findFontRes = globalParams->findSystemFontFileForFamilyAndStyle(fontFamily, fontStyle, filesToIgnore); } return {}; } 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")) { 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 {}; } const FoFiIdentifierType fontFoFiType = FoFiIdentifier::identifyFile(filepath.c_str()); if (fontFoFiType != fofiIdTrueType && fontFoFiType != fofiIdTrueTypeCollection && fontFoFiType != fofiIdOpenTypeCFF8Bit && fontFoFiType != fofiIdOpenTypeCFFCID) { error(errIO, -1, "We only support embedding ttf/ttc/otf fonts for now. The font file for {0:s} {1:s} was {2:s} of type {3:d}", fontFamily.c_str(), fontStyle.c_str(), filepath.c_str(), fontFoFiType); return {}; } 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)); fontDict.dictSet("Type", Object(objName, "Font")); fontDict.dictSet("Subtype", Object(objName, "Type0")); fontDict.dictSet("BaseFont", Object(objName, fontFamilyAndStyle.c_str())); fontDict.dictSet("Encoding", Object(objName, "Identity-H")); { std::unique_ptr descendantFonts = std::make_unique(xref); const bool isTrueType = (fontFoFiType == fofiIdTrueType || fontFoFiType == fofiIdTrueTypeCollection); std::unique_ptr descendantFont = std::make_unique(xref); descendantFont->set("Type", Object(objName, "Font")); descendantFont->set("Subtype", Object(objName, isTrueType ? "CIDFontType2" : "CIDFontType0")); descendantFont->set("BaseFont", Object(objName, fontFamilyAndStyle.c_str())); { // 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("Supplement", Object(0)); descendantFont->set("CIDSystemInfo", Object(cidSystemInfo)); } FT_Library freetypeLib; if (FT_Init_FreeType(&freetypeLib)) { error(errIO, -1, "FT_Init_FreeType failed"); return {}; } const std::unique_ptr freetypeLibDeleter(&freetypeLib, [](FT_Library *l) { FT_Done_FreeType(*l); }); FT_Face face; if (ft_new_face_from_file(freetypeLib, filepath.c_str(), faceIndex, &face)) { error(errIO, -1, "ft_new_face_from_file failed for {0:s}", filepath.c_str()); return {}; } const std::unique_ptr faceDeleter(&face, [](FT_Face *f) { FT_Done_Face(*f); }); if (FT_Set_Char_Size(face, 1000, 1000, 0, 0)) { error(errIO, -1, "FT_Set_Char_Size failed for {0:s}", filepath.c_str()); return {}; } { std::unique_ptr fontDescriptor = std::make_unique(xref); fontDescriptor->set("Type", Object(objName, "FontDescriptor")); fontDescriptor->set("FontName", Object(objName, fontFamilyAndStyle.c_str())); // a bit arbirary but the Flags field is mandatory... const std::string lowerCaseFontFamily = GooString::toLowerCase(fontFamily); if (lowerCaseFontFamily.find("serif") != std::string::npos && lowerCaseFontFamily.find("sans") == std::string::npos) { fontDescriptor->set("Flags", Object(2)); // Serif } else { fontDescriptor->set("Flags", Object(0)); // Sans Serif } Array *fontBBox = new Array(xref); fontBBox->add(Object(static_cast(face->bbox.xMin))); fontBBox->add(Object(static_cast(face->bbox.yMin))); fontBBox->add(Object(static_cast(face->bbox.xMax))); fontBBox->add(Object(static_cast(face->bbox.yMax))); fontDescriptor->set("FontBBox", Object(fontBBox)); fontDescriptor->set("Ascent", Object(static_cast(face->ascender))); fontDescriptor->set("Descent", Object(static_cast(face->descender))); { const std::unique_ptr file(GooFile::open(filepath)); if (!file) { error(errIO, -1, "Failed to open {0:s}", filepath.c_str()); return {}; } const Goffset fileSize = file->size(); if (fileSize < 0) { error(errIO, -1, "Failed to get file size for {0:s}", filepath.c_str()); return {}; } // GooFile::read only takes an integer so for now we don't support huge fonts if (fileSize > std::numeric_limits::max()) { error(errIO, -1, "Font size is too big {0:s}", filepath.c_str()); return {}; } char *dataPtr = static_cast(gmalloc(fileSize)); const Goffset bytesRead = file->read(dataPtr, static_cast(fileSize), 0); if (bytesRead != fileSize) { error(errIO, -1, "Failed to read contents of {0:s}", filepath.c_str()); gfree(dataPtr); return {}; } if (isTrueType) { 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, StreamCompression::Compress); fontDescriptor->set("FontFile3", Object(fontFile3Ref)); } } const Ref fontDescriptorRef = xref->addIndirectObject(Object(fontDescriptor.release())); descendantFont->set("FontDescriptor", Object(fontDescriptorRef)); } static const int basicMultilingualMaxCode = 65535; const std::unique_ptr fft = FoFiTrueType::load(filepath.c_str()); if (fft) { // Look for the Unicode BMP cmaps, which are 0/3 or 3/1 int unicodeBMPCMap = fft->findCmap(0, 3); if (unicodeBMPCMap < 0) { unicodeBMPCMap = fft->findCmap(3, 1); } if (unicodeBMPCMap < 0) { error(errIO, -1, "Font does not have an unicode BMP cmap {0:s}", filepath.c_str()); return {}; } 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)) { fontsWidths.addWidth(code, 0); } else { fontsWidths.addWidth(code, static_cast(face->glyph->metrics.horiAdvance)); } } Array *widths = new Array(xref); 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))); int i = 0; for (int code = 0; code <= basicMultilingualMaxCode; ++code) { const int glyph = fft->mapCodeToGID(unicodeBMPCMap, code); dataPtr[i++] = (unsigned char)(glyph >> 8); dataPtr[i++] = (unsigned char)(glyph & 0xff); } const Ref cidToGidMapStream = xref->addStreamObject(new Dict(xref), dataPtr, basicMultilingualMaxCode * 2, StreamCompression::Compress); descendantFont->set("CIDToGIDMap", Object(cidToGidMapStream)); } descendantFonts->add(Object(descendantFont.release())); fontDict.dictSet("DescendantFonts", Object(descendantFonts.release())); } const Ref fontDictRef = xref->addIndirectObject(fontDict); std::string dictFontName = forceName ? fontFamilyAndStyle : kOurDictFontNamePrefix; Object *acroForm = doc->getCatalog()->getAcroForm(); if (resDict.isDict()) { Ref fontDictObjRef; Object fontDictObj = resDict.getDict()->lookup("Font", &fontDictObjRef); assert(fontDictObj.isDict()); dictFontName = fontDictObj.getDict()->findAvailableKey(dictFontName); fontDictObj.dictSet(dictFontName.c_str(), Object(fontDictRef)); if (fontDictObjRef != Ref::INVALID()) { xref->setModifiedObject(&fontDictObj, fontDictObjRef); } else { Ref resDictRef; acroForm->getDict()->lookup("DR", &resDictRef); if (resDictRef != Ref::INVALID()) { xref->setModifiedObject(&resDict, resDictRef); } else { doc->getCatalog()->setAcroFormModified(); } } // maybe we can do something to reuse the existing data instead of recreating from scratch? delete defaultResources; defaultResources = new GfxResources(xref, resDict.getDict(), nullptr); } else { Dict *fontsDict = new Dict(xref); fontsDict->set(dictFontName.c_str(), Object(fontDictRef)); Dict *defaultResourcesDict = new Dict(xref); defaultResourcesDict->set("Font", Object(fontsDict)); assert(!defaultResources); defaultResources = new GfxResources(xref, defaultResourcesDict, nullptr); resDict = Object(defaultResourcesDict); acroForm->dictSet("DR", resDict.copy()); doc->getCatalog()->setAcroFormModified(); } return { dictFontName, fontDictRef }; } std::string Form::getFallbackFontForChar(Unicode uChar, const GfxFont &fontToEmulate) const { const UCharFontSearchResult res = globalParams->findSystemFontFileForUChar(uChar, fontToEmulate); return findFontInDefaultResources(res.family, res.style); } std::vector Form::ensureFontsForAllCharacters(const GooString *unicodeText, const std::string &pdfFontNameToEmulate, GfxResources *fieldResources) { GfxResources *resources = fieldResources ? fieldResources : defaultResources; 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"); 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)) { if (f->isCIDFont()) { auto cidFont = static_cast(f.get()); if (c < cidFont->getCIDToGIDLen() && c != 0 && c != '\r' && c != '\n') { const int glyph = cidFont->getCIDToGID()[c]; if (glyph == 0) { addFont = true; } } } } else { addFont = true; } if (addFont) { Form::AddFontResult res = doGetAddFontToDefaultResources(uChar, *f); if (res.ref != Ref::INVALID()) { newFonts.emplace_back(res); } } } return newFonts; } Form::AddFontResult Form::doGetAddFontToDefaultResources(Unicode uChar, const GfxFont &fontToEmulate) { const UCharFontSearchResult res = globalParams->findSystemFontFileForUChar(uChar, fontToEmulate); std::string pdfFontName = findFontInDefaultResources(res.family, res.style); if (pdfFontName.empty()) { return addFontToDefaultResources(res.filepath, res.faceIndex, res.family, res.style); } return { pdfFontName, Ref::INVALID() }; } void Form::postWidgetsLoad() { // We create the widget annotations associated to // 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(); } } FormWidget *Form::findWidgetByRef(Ref aref) { for (int i = 0; i < numFields; i++) { FormWidget *result = rootFields[i]->findWidgetByRef(aref); if (result) { return result; } } return nullptr; } FormField *Form::findFieldByRef(Ref aref) const { for (int i = 0; i < numFields; i++) { FormField *result = rootFields[i]->findFieldByRef(aref); if (result) { return result; } } return nullptr; } FormField *Form::findFieldByFullyQualifiedName(const std::string &name) const { for (int i = 0; i < numFields; i++) { FormField *result = rootFields[i]->findFieldByFullyQualifiedName(name); if (result) { return result; } } return nullptr; } 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()); } } else { if (!excludeFields) { for (const std::string &field : fields) { 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) { foundField = findFieldByRef(fieldRef); } else { foundField = findFieldByFullyQualifiedName(field); } if (foundField) { foundField->reset(std::vector()); } } } else { for (int i = 0; i < numFields; i++) { rootFields[i]->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'\n"); 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 */ for (Annot *annot : annots->getAnnots()) { if (annot->getType() != Annot::typeWidget) { continue; } if (!annot->getHasRef()) { /* Since all entry in a form field's kid dict needs to be indirect references, if this annot isn't indirect, it isn't related to a form field */ continue; } Ref r = annot->getRef(); /* Try to find a form field which either has this Annot in its Kids entry or is merged with this Annot */ 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; } } } } 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) { FormWidget *frmWidget = frmField->getWidget(0); frmWidget->setID(FormWidget::encodeID(page, numWidgets)); widgets[numWidgets++] = frmWidget; } } FormPageWidgets::~FormPageWidgets() { gfree(widgets); } poppler-24.02.0/poppler/Form.h000066400000000000000000000700221455701731300161040ustar00rootroot00000000000000//======================================================================== // // Form.h // // This file is licensed under the GPLv2 or later // // Copyright 2006 Julien Rebetez // Copyright 2007, 2008, 2011 Carlos Garcia Campos // Copyright 2007-2010, 2012, 2015-2023 Albert Astals Cid // Copyright 2010 Mark Riedesel // Copyright 2011 Pino Toscano // Copyright 2012 Fabio D'Urso // Copyright 2013 Adrian Johnson // Copyright 2015 André Guerreiro // Copyright 2015 André Esser // Copyright 2017 Roland Hieber // Copyright 2017 Hans-Ulrich Jüttner // Copyright 2018 Andre Heinecke // Copyright 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright 2018 Chinmoy Ranjan Pradhan // Copyright 2019, 2020 Oliver Sander // Copyright 2019 João Netto // Copyright 2020, 2021 Nelson Benítez León // Copyright 2020 Marek Kasik // Copyright 2020 Thorsten Behrens // Copyright 2020 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 2022 Alexander Sulfrian // Copyright 2023 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== #ifndef FORM_H #define FORM_H #include "Annot.h" #include "CharTypes.h" #include "Object.h" #include "poppler_private_export.h" #include #include #include #include class GooString; class Array; class Dict; class Annot; class AnnotWidget; class Annots; class LinkAction; class GfxResources; class PDFDoc; class SignatureInfo; class X509CertificateInfo; namespace CryptoSign { class VerificationInterface; } enum FormFieldType { formButton, formText, formChoice, formSignature, formUndef }; enum FormButtonType { formButtonCheck, formButtonPush, formButtonRadio }; enum FormSignatureType { adbe_pkcs7_sha1, adbe_pkcs7_detached, ETSI_CAdES_detached, unknown_signature_type, unsigned_signature_field }; enum FillValueType { fillValue, fillDefaultValue }; class Form; class FormField; class FormFieldButton; class FormFieldText; class FormFieldSignature; class FormFieldChoice; //------------------------------------------------------------------------ // FormWidget // A FormWidget represents the graphical part of a field and is "attached" // to a page. //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT FormWidget { public: virtual ~FormWidget(); // Check if point is inside the field bounding rect bool inRect(double x, double y) const; // Get the field bounding rect void getRect(double *x1, double *y1, double *x2, double *y2) const; unsigned getID() { return ID; } void setID(unsigned int i) { ID = i; } FormField *getField() { return field; } FormFieldType getType() { return type; } Object *getObj() { return &obj; } Ref getRef() { return ref; } void setChildNum(unsigned i) { childNum = i; } unsigned getChildNum() { return childNum; } const GooString *getPartialName() const; void setPartialName(const GooString &name); const GooString *getAlternateUiName() const; const GooString *getMappingName() const; GooString *getFullyQualifiedName(); bool isModified() const; bool isReadOnly() const; void setReadOnly(bool value); LinkAction *getActivationAction(); // The caller should not delete the result std::unique_ptr getAdditionalAction(Annot::FormAdditionalActionsType type); bool setAdditionalAction(Annot::FormAdditionalActionsType t, const std::string &js); // return the unique ID corresponding to pageNum/fieldNum static int encodeID(unsigned pageNum, unsigned fieldNum); // decode id and retrieve pageNum and fieldNum static void decodeID(unsigned id, unsigned *pageNum, unsigned *fieldNum); void createWidgetAnnotation(); AnnotWidget *getWidgetAnnotation() const { return widget; } void setWidgetAnnotation(AnnotWidget *_widget) { widget = _widget; } virtual void updateWidgetAppearance() = 0; void print(int indent = 0); protected: FormWidget(PDFDoc *docA, Object *aobj, unsigned num, Ref aref, FormField *fieldA); AnnotWidget *widget; FormField *field; FormFieldType type; Object obj; Ref ref; PDFDoc *doc; XRef *xref; // index of this field in the parent's child list unsigned childNum; /* Field ID is an (unsigned) integer, calculated as follow : the first sizeof/2 bits are the field number, relative to the page the last sizeof/2 bits are the page number [page number | field number] (encoding) id = (pageNum << 4*sizeof(unsigned)) + fieldNum; (decoding) pageNum = id >> 4*sizeof(unsigned); fieldNum = (id << 4*sizeof(unsigned)) >> 4*sizeof(unsigned); */ unsigned ID; }; //------------------------------------------------------------------------ // FormWidgetButton //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT FormWidgetButton : public FormWidget { public: FormWidgetButton(PDFDoc *docA, Object *dictObj, unsigned num, Ref ref, FormField *p); ~FormWidgetButton() override; FormButtonType getButtonType() const; void setState(bool state); bool getState() const; const char *getOnStr() const; void setAppearanceState(const char *state); void updateWidgetAppearance() override; protected: FormFieldButton *parent() const; GooString *onStr; }; //------------------------------------------------------------------------ // FormWidgetText //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT FormWidgetText : public FormWidget { public: FormWidgetText(PDFDoc *docA, Object *dictObj, unsigned num, Ref ref, FormField *p); // return the field's content (UTF16BE) const GooString *getContent() const; // expects a UTF16BE string void setContent(const GooString *new_content); // sets the text inside the field appearance stream void setAppearanceContent(const GooString *new_content); void updateWidgetAppearance() override; bool isMultiline() const; bool isPassword() const; bool isFileSelect() const; bool noSpellCheck() const; bool noScroll() const; bool isComb() const; bool isRichText() const; int getMaxLen() const; // return the font size of the field's text double getTextFontSize(); // set the font size of the field's text (currently only integer values) void setTextFontSize(int fontSize); protected: FormFieldText *parent() const; }; //------------------------------------------------------------------------ // FormWidgetChoice //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT FormWidgetChoice : public FormWidget { public: FormWidgetChoice(PDFDoc *docA, Object *dictObj, unsigned num, Ref ref, FormField *p); ~FormWidgetChoice() override; int getNumChoices() const; // return the display name of the i-th choice (UTF16BE) const GooString *getChoice(int i) const; const GooString *getExportVal(int i) const; // select the i-th choice void select(int i); // toggle selection of the i-th choice void toggle(int i); // deselect everything void deselectAll(); // 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); const GooString *getEditChoice() const; void updateWidgetAppearance() override; bool isSelected(int i) const; bool isCombo() const; bool hasEdit() const; bool isMultiSelect() const; bool noSpellCheck() const; bool commitOnSelChange() const; bool isListBox() const; protected: bool _checkRange(int i) const; FormFieldChoice *parent() const; }; //------------------------------------------------------------------------ // FormWidgetSignature //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT FormWidgetSignature : public FormWidget { public: FormWidgetSignature(PDFDoc *docA, Object *dictObj, unsigned num, Ref ref, FormField *p); void updateWidgetAppearance() override; FormSignatureType signatureType() const; void setSignatureType(FormSignatureType fst); // Use -1 for now as validationTime SignatureInfo *validateSignature(bool doVerifyCert, bool forceRevalidation, time_t validationTime, bool ocspRevocationCheck, bool enableAIA); // returns a list with the boundaries of the signed ranges // the elements of the list are of type Goffset std::vector getSignedRangeBounds() const; // Creates or replaces the dictionary name "V" in the signature dictionary and // fills it with the fields of the signature; the field "Contents" is the signature // in PKCS#7 format, which is calculated over the byte range encompassing the whole // document except for the signature itself; this byte range is specified in the // 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 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 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; private: bool createSignature(Object &vObj, Ref vRef, const GooString &name, int placeholderLength, const GooString *reason = nullptr, const GooString *location = nullptr); 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); }; //------------------------------------------------------------------------ // FormField // A FormField implements the logical side of a field and is "attached" to // the Catalog. This is an internal class and client applications should // only interact with FormWidgets. //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT FormField { public: FormField(PDFDoc *docA, Object &&aobj, const Ref aref, FormField *parent, std::set *usedParents, FormFieldType t = formUndef); virtual ~FormField(); // Accessors. FormFieldType getType() const { return type; } Object *getObj() { return &obj; } Ref getRef() { return ref; } void setReadOnly(bool value); bool isReadOnly() const { return readOnly; } void setStandAlone(bool value) { standAlone = value; } bool isStandAlone() const { return standAlone; } GooString *getDefaultAppearance() const { return defaultAppearance; } void setDefaultAppearance(const std::string &appearance); bool hasTextQuadding() const { return hasQuadding; } VariableTextQuadding getTextQuadding() const { return quadding; } const GooString *getPartialName() const { return partialName; } void setPartialName(const GooString &name); const GooString *getAlternateUiName() const { return alternateUiName; } const GooString *getMappingName() const { return mappingName; } GooString *getFullyQualifiedName(); 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]; } // only implemented in FormFieldButton virtual void fillChildrenSiblingsID(); void createWidgetAnnotations(); void printTree(int indent = 0); virtual void print(int indent = 0); virtual void reset(const std::vector &excludedFields); void resetChildren(const std::vector &excludedFields); FormField *findFieldByRef(Ref aref); FormField *findFieldByFullyQualifiedName(const std::string &name); protected: void _createWidget(Object *obj, Ref aref); void createChildren(std::set *usedParents); void updateChildrenAppearance(); bool isAmongExcludedFields(const std::vector &excludedFields); FormFieldType type; // field type Ref ref; bool terminal; Object obj; PDFDoc *doc; XRef *xref; FormField **children; FormField *parent; int numChildren; FormWidget **widgets; bool readOnly; GooString *partialName; // T field GooString *alternateUiName; // TU field GooString *mappingName; // TM field GooString *fullyQualifiedName; // Variable Text GooString *defaultAppearance; bool hasQuadding; VariableTextQuadding quadding; // True when FormField is not part of Catalog's Field array (or there isn't one). bool standAlone; private: FormField() { } }; //------------------------------------------------------------------------ // FormFieldButton //------------------------------------------------------------------------ class FormFieldButton : public FormField { public: FormFieldButton(PDFDoc *docA, Object &&dict, const Ref ref, FormField *parent, std::set *usedParents); FormButtonType getButtonType() const { return btype; } bool noToggleToOff() const { return noAllOff; } // returns true if the state modification is accepted bool setState(const char *state, bool ignoreToggleOff = false); bool getState(const char *state) const; const char *getAppearanceState() const { return appearanceState.isName() ? appearanceState.getName() : nullptr; } const char *getDefaultAppearanceState() const { return defaultAppearanceState.isName() ? defaultAppearanceState.getName() : nullptr; } void fillChildrenSiblingsID() override; void setNumSiblings(int num); void setSibling(int i, FormFieldButton *id) { siblings[i] = id; } // 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; } void print(int indent) override; void reset(const std::vector &excludedFields) override; ~FormFieldButton() override; 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; FormButtonType btype; int size; int active_child; // only used for combo box bool noAllOff; Object appearanceState; // V Object defaultAppearanceState; // DV }; //------------------------------------------------------------------------ // FormFieldText //------------------------------------------------------------------------ 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); ~FormFieldText() override; bool isMultiline() const { return multiline; } bool isPassword() const { return password; } bool isFileSelect() const { return fileSelect; } bool noSpellCheck() const { return doNotSpellCheck; } bool noScroll() const { return doNotScroll; } bool isComb() const { return comb; } bool isRichText() const { return richText; } int getMaxLen() const { return maxLen; } // return the font size of the field's text double getTextFontSize(); // set the font size of the field's text (currently only integer values) void setTextFontSize(int fontSize); void print(int indent) override; void reset(const std::vector &excludedFields) override; static int tokenizeDA(const std::string &daString, std::vector *daToks, const char *searchTok); protected: int parseDA(std::vector *daToks); void fillContent(FillValueType fillType); GooString *content; GooString *internalContent; GooString *defaultContent; bool multiline; bool password; bool fileSelect; bool doNotSpellCheck; bool doNotScroll; bool comb; bool richText; int maxLen; }; //------------------------------------------------------------------------ // FormFieldChoice //------------------------------------------------------------------------ class FormFieldChoice : public FormField { public: FormFieldChoice(PDFDoc *docA, Object &&aobj, const Ref ref, FormField *parent, std::set *usedParents); ~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; } // For multi-select choices it returns the first one const GooString *getSelectedChoice() const; // select the i-th choice void select(int i); // toggle selection of the i-th choice void toggle(int i); // deselect everything void deselectAll(); // only work for editable combo box, set the user-entered text as the current choice void setEditChoice(const GooString *new_content); const GooString *getEditChoice() const; bool isSelected(int i) const { return choices[i].selected; } int getNumSelected(); bool isCombo() const { return combo; } bool hasEdit() const { return edit; } bool isMultiSelect() const { return multiselect; } bool noSpellCheck() const { return doNotSpellCheck; } bool commitOnSelChange() const { return doCommitOnSelChange; } bool isListBox() const { return !combo; } int getTopIndex() const { return topIdx; } void print(int indent) override; void reset(const std::vector &excludedFields) override; protected: void unselectAll(); void updateSelection(); void fillChoices(FillValueType fillType); bool combo; bool edit; bool multiselect; bool doNotSpellCheck; bool doCommitOnSelChange; struct ChoiceOpt { GooString *exportVal; // the export value ("internal" name) GooString *optionName; // displayed name bool selected; // if this choice is selected }; int numChoices; ChoiceOpt *choices; bool *defaultChoices; GooString *editedChoice; int topIdx; // TI }; //------------------------------------------------------------------------ // FormFieldSignature //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT FormFieldSignature : public FormField { public: 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); // returns a list with the boundaries of the signed ranges // the elements of the list are of type Goffset std::vector getSignedRangeBounds() const; // 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); ~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 GooString &getCustomAppearanceContent() const; void setCustomAppearanceContent(const GooString &s); const GooString &getCustomAppearanceLeftContent() const; void setCustomAppearanceLeftContent(const GooString &s); double getCustomAppearanceLeftFontSize() const; void setCustomAppearanceLeftFontSize(double size); // Background image (ref to an object of type XObject). Invalid ref if not required. Ref getImageResource() const; void setImageResource(const Ref imageResourceA); void setCertificateInfo(std::unique_ptr &); FormWidget *getCreateWidget(); private: void parseInfo(); void hashSignedDataBlock(CryptoSign::VerificationInterface *handler, Goffset block_len); FormSignatureType signature_type; Object byte_range; GooString *signature; SignatureInfo *signature_info; GooString customAppearanceContent; GooString customAppearanceLeftContent; double customAppearanceLeftFontSize = 20; Ref imageResource = Ref::INVALID(); std::unique_ptr certificate_info; void print(int indent) override; }; //------------------------------------------------------------------------ // Form // This class handle the document-wide part of Form (things in the acroForm // Catalog entry). //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT Form { public: explicit Form(PDFDoc *doc); ~Form(); Form(const Form &) = delete; Form &operator=(const Form &) = delete; // Look up an inheritable field dictionary entry. static Object fieldLookup(Dict *field, const char *key); /* 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); // 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; Ref ref; }; // 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 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 std::string getFallbackFontForChar(Unicode uChar, const GfxFont &fontToEmulate) const; // Makes sure the default resources has fonts to draw all the given chars and as close as possible to the given pdfFontNameToEmulate // If needed adds fonts to the default resources dictionary, font names will be popplerfontXXX // If fieldResources is not nullptr, it is used instead of the to query the font to emulate instead of the default resources // Returns a list of all the added fonts (if any) 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; } VariableTextQuadding getTextQuadding() const { return quadding; } GfxResources *getDefaultResources() const { return defaultResources; } Object *getDefaultResourcesObj() { return &resDict; } FormWidget *findWidgetByRef(Ref aref); FormField *findFieldByRef(Ref aref) const; FormField *findFieldByFullyQualifiedName(const std::string &name) const; void postWidgetsLoad(); const std::vector &getCalculateOrder() const { return calculateOrder; } void reset(const std::vector &fields, bool excludeFields); 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 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; PDFDoc *const doc; bool needAppearances; GfxResources *defaultResources; Object resDict; std::vector calculateOrder; // Variable Text GooString *defaultAppearance; VariableTextQuadding quadding; }; //------------------------------------------------------------------------ // FormPageWidgets //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT FormPageWidgets { public: FormPageWidgets(Annots *annots, unsigned int page, Form *form); ~FormPageWidgets(); FormPageWidgets(const FormPageWidgets &) = delete; FormPageWidgets &operator=(const FormPageWidgets &) = delete; int getNumWidgets() const { return numWidgets; } FormWidget *getWidget(int i) const { return widgets[i]; } void addWidgets(const std::vector &addedWidgets, unsigned int page); private: FormWidget **widgets; int numWidgets; int size; }; #endif poppler-24.02.0/poppler/Function.cc000066400000000000000000001410051455701731300171240ustar00rootroot00000000000000//======================================================================== // // Function.cc // // Copyright 2001-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, 2008-2010, 2013-2015, 2017-2020, 2022, 2023 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 // // 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 #include #include #include #include #include "goo/gmem.h" #include "goo/gstrtod.h" #include "Object.h" #include "Dict.h" #include "Stream.h" #include "Error.h" #include "Function.h" #ifndef M_PI # define M_PI 3.14159265358979323846 #endif //------------------------------------------------------------------------ // Function //------------------------------------------------------------------------ Function::Function() : domain {} { } Function::~Function() { } Function *Function::parse(Object *funcObj) { std::set usedParents; return parse(funcObj, &usedParents); } Function *Function::parse(Object *funcObj, std::set *usedParents) { Function *func; Dict *dict; int funcType; if (funcObj->isStream()) { dict = funcObj->streamGetDict(); } else if (funcObj->isDict()) { dict = funcObj->getDict(); } else if (funcObj->isName("Identity")) { return new IdentityFunction(); } else { error(errSyntaxError, -1, "Expected function dictionary or stream"); return nullptr; } Object obj1 = dict->lookup("FunctionType"); if (!obj1.isInt()) { error(errSyntaxError, -1, "Function type is missing or wrong type"); return nullptr; } funcType = obj1.getInt(); if (funcType == 0) { func = new SampledFunction(funcObj, dict); } else if (funcType == 2) { func = new ExponentialFunction(funcObj, dict); } else if (funcType == 3) { func = new StitchingFunction(funcObj, dict, usedParents); } else if (funcType == 4) { func = new PostScriptFunction(funcObj, dict); } else { error(errSyntaxError, -1, "Unimplemented function type ({0:d})", funcType); return nullptr; } if (!func->isOk()) { delete func; return nullptr; } return func; } Function::Function(const Function *func) { m = func->m; n = func->n; memcpy(domain, func->domain, funcMaxInputs * 2 * sizeof(double)); memcpy(range, func->range, funcMaxOutputs * 2 * sizeof(double)); hasRange = func->hasRange; } bool Function::init(Dict *dict) { Object obj1; int i; //----- Domain obj1 = dict->lookup("Domain"); if (!obj1.isArray()) { error(errSyntaxError, -1, "Function is missing domain"); return false; } m = obj1.arrayGetLength() / 2; if (m > funcMaxInputs) { error(errSyntaxError, -1, "Functions with more than {0:d} inputs are unsupported", funcMaxInputs); return false; } for (i = 0; i < m; ++i) { Object obj2 = obj1.arrayGet(2 * i); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function domain array"); return false; } domain[i][0] = obj2.getNum(); obj2 = obj1.arrayGet(2 * i + 1); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function domain array"); return false; } domain[i][1] = obj2.getNum(); } //----- Range hasRange = false; n = 0; obj1 = dict->lookup("Range"); if (obj1.isArray()) { hasRange = true; n = obj1.arrayGetLength() / 2; if (n > funcMaxOutputs) { error(errSyntaxError, -1, "Functions with more than {0:d} outputs are unsupported", funcMaxOutputs); return false; } for (i = 0; i < n; ++i) { Object obj2 = obj1.arrayGet(2 * i); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function range array"); return false; } range[i][0] = obj2.getNum(); obj2 = obj1.arrayGet(2 * i + 1); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function range array"); return false; } range[i][1] = obj2.getNum(); } } return true; } //------------------------------------------------------------------------ // IdentityFunction //------------------------------------------------------------------------ IdentityFunction::IdentityFunction() { int i; // fill these in with arbitrary values just in case they get used // somewhere m = funcMaxInputs; n = funcMaxOutputs; for (i = 0; i < funcMaxInputs; ++i) { domain[i][0] = 0; domain[i][1] = 1; } hasRange = false; } IdentityFunction::~IdentityFunction() { } void IdentityFunction::transform(const double *in, double *out) const { int i; for (i = 0; i < funcMaxOutputs; ++i) { out[i] = in[i]; } } //------------------------------------------------------------------------ // SampledFunction //------------------------------------------------------------------------ SampledFunction::SampledFunction(Object *funcObj, Dict *dict) : cacheOut {} { Stream *str; int sampleBits; double sampleMul; Object obj1; unsigned int buf, bitMask; int bits; unsigned int s; double in[funcMaxInputs]; int i, j, t, bit, idx; idxOffset = nullptr; samples = nullptr; sBuf = nullptr; ok = false; //----- initialize the generic stuff if (!init(dict)) { return; } if (!hasRange) { error(errSyntaxError, -1, "Type 0 function is missing range"); return; } if (m > sampledFuncMaxInputs) { error(errSyntaxError, -1, "Sampled functions with more than {0:d} inputs are unsupported", sampledFuncMaxInputs); return; } //----- buffer sBuf = (double *)gmallocn(1 << m, sizeof(double)); //----- get the stream if (!funcObj->isStream()) { error(errSyntaxError, -1, "Type 0 function isn't a stream"); return; } str = funcObj->getStream(); //----- Size obj1 = dict->lookup("Size"); if (!obj1.isArray() || obj1.arrayGetLength() != m) { error(errSyntaxError, -1, "Function has missing or invalid size array"); return; } for (i = 0; i < m; ++i) { Object obj2 = obj1.arrayGet(i); if (!obj2.isInt()) { error(errSyntaxError, -1, "Illegal value in function size array"); return; } sampleSize[i] = obj2.getInt(); if (sampleSize[i] <= 0) { error(errSyntaxError, -1, "Illegal non-positive value in function size array"); return; } } idxOffset = (int *)gmallocn(1 << m, sizeof(int)); for (i = 0; i < (1 << m); ++i) { idx = 0; for (j = m - 1, t = i; j >= 1; --j, t <<= 1) { if (sampleSize[j] == 1) { bit = 0; } else { bit = (t >> (m - 1)) & 1; } idx = (idx + bit) * sampleSize[j - 1]; } if (m > 0 && sampleSize[0] == 1) { bit = 0; } else { bit = (t >> (m - 1)) & 1; } idxOffset[i] = (idx + bit) * n; } //----- BitsPerSample obj1 = dict->lookup("BitsPerSample"); if (!obj1.isInt()) { error(errSyntaxError, -1, "Function has missing or invalid BitsPerSample"); return; } sampleBits = obj1.getInt(); if (unlikely(sampleBits < 1 || sampleBits > 32)) { error(errSyntaxError, -1, "Function invalid BitsPerSample"); return; } sampleMul = 1.0 / (pow(2.0, (double)sampleBits) - 1); //----- Encode obj1 = dict->lookup("Encode"); if (obj1.isArray() && obj1.arrayGetLength() == 2 * m) { for (i = 0; i < m; ++i) { Object obj2 = obj1.arrayGet(2 * i); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function encode array"); return; } encode[i][0] = obj2.getNum(); obj2 = obj1.arrayGet(2 * i + 1); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function encode array"); return; } encode[i][1] = obj2.getNum(); } } else { for (i = 0; i < m; ++i) { encode[i][0] = 0; encode[i][1] = sampleSize[i] - 1; } } for (i = 0; i < m; ++i) { if (unlikely((domain[i][1] - domain[i][0]) == 0)) { error(errSyntaxError, -1, "Illegal value in function domain array"); return; } inputMul[i] = (encode[i][1] - encode[i][0]) / (domain[i][1] - domain[i][0]); } //----- Decode obj1 = dict->lookup("Decode"); if (obj1.isArray() && obj1.arrayGetLength() == 2 * n) { for (i = 0; i < n; ++i) { Object obj2 = obj1.arrayGet(2 * i); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function decode array"); return; } decode[i][0] = obj2.getNum(); obj2 = obj1.arrayGet(2 * i + 1); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function decode array"); return; } decode[i][1] = obj2.getNum(); } } else { for (i = 0; i < n; ++i) { decode[i][0] = range[i][0]; decode[i][1] = range[i][1]; } } //----- samples nSamples = n; for (i = 0; i < m; ++i) { nSamples *= sampleSize[i]; } samples = (double *)gmallocn_checkoverflow(nSamples, sizeof(double)); if (!samples) { error(errSyntaxError, -1, "Function has invalid number of samples"); return; } buf = 0; bits = 0; bitMask = (1 << sampleBits) - 1; str->reset(); for (i = 0; i < nSamples; ++i) { if (sampleBits == 8) { s = str->getChar(); } else if (sampleBits == 16) { s = str->getChar(); s = (s << 8) + str->getChar(); } else if (sampleBits == 32) { s = str->getChar(); s = (s << 8) + str->getChar(); s = (s << 8) + str->getChar(); s = (s << 8) + str->getChar(); } else { while (bits < sampleBits) { buf = (buf << 8) | (str->getChar() & 0xff); bits += 8; } s = (buf >> (bits - sampleBits)) & bitMask; bits -= sampleBits; } samples[i] = (double)s * sampleMul; } str->close(); // set up the cache for (i = 0; i < m; ++i) { in[i] = domain[i][0]; cacheIn[i] = in[i] - 1; } transform(in, cacheOut); ok = true; } SampledFunction::~SampledFunction() { if (idxOffset) { gfree(idxOffset); } if (samples) { gfree(samples); } if (sBuf) { gfree(sBuf); } } SampledFunction::SampledFunction(const SampledFunction *func) : Function(func) { memcpy(sampleSize, func->sampleSize, funcMaxInputs * sizeof(int)); memcpy(encode, func->encode, funcMaxInputs * 2 * sizeof(double)); memcpy(decode, func->decode, funcMaxOutputs * 2 * sizeof(double)); memcpy(inputMul, func->inputMul, funcMaxInputs * sizeof(double)); nSamples = func->nSamples; idxOffset = (int *)gmallocn(1 << m, sizeof(int)); memcpy(idxOffset, func->idxOffset, (1 << m) * (int)sizeof(int)); samples = (double *)gmallocn(nSamples, sizeof(double)); memcpy(samples, func->samples, nSamples * sizeof(double)); sBuf = (double *)gmallocn(1 << m, sizeof(double)); memcpy(cacheIn, func->cacheIn, funcMaxInputs * sizeof(double)); memcpy(cacheOut, func->cacheOut, funcMaxOutputs * sizeof(double)); ok = func->ok; } void SampledFunction::transform(const double *in, double *out) const { double x; int e[funcMaxInputs]; double efrac0[funcMaxInputs]; double efrac1[funcMaxInputs]; // check the cache bool inCache = true; for (int i = 0; i < m; ++i) { if (in[i] != cacheIn[i]) { inCache = false; break; } } if (inCache) { for (int i = 0; i < n; ++i) { out[i] = cacheOut[i]; } return; } // map input values into sample array for (int i = 0; i < m; ++i) { x = (in[i] - domain[i][0]) * inputMul[i] + encode[i][0]; if (x < 0 || std::isnan(x)) { x = 0; } else if (x > sampleSize[i] - 1) { x = sampleSize[i] - 1; } e[i] = (int)x; if (e[i] == sampleSize[i] - 1 && sampleSize[i] > 1) { // this happens if in[i] = domain[i][1] e[i] = sampleSize[i] - 2; } efrac1[i] = x - e[i]; efrac0[i] = 1 - efrac1[i]; } // compute index for the first sample to be used int idx0 = 0; for (int k = m - 1; k >= 1; --k) { idx0 = (idx0 + e[k]) * sampleSize[k - 1]; } idx0 = (idx0 + e[0]) * n; // for each output, do m-linear interpolation for (int i = 0; i < n; ++i) { // pull 2^m values out of the sample array for (int j = 0; j < (1 << m); ++j) { int idx = idx0 + idxOffset[j] + i; if (likely(idx >= 0 && idx < nSamples)) { sBuf[j] = samples[idx]; } else { sBuf[j] = 0; // TODO Investigate if this is what Adobe does } } // do m sets of interpolations for (int j = 0, t = (1 << m); j < m; ++j, t >>= 1) { for (int k = 0; k < t; k += 2) { sBuf[k >> 1] = efrac0[j] * sBuf[k] + efrac1[j] * sBuf[k + 1]; } } // map output value to range out[i] = sBuf[0] * (decode[i][1] - decode[i][0]) + decode[i][0]; if (out[i] < range[i][0]) { out[i] = range[i][0]; } else if (out[i] > range[i][1]) { out[i] = range[i][1]; } } // save current result in the cache for (int i = 0; i < m; ++i) { cacheIn[i] = in[i]; } for (int i = 0; i < n; ++i) { cacheOut[i] = out[i]; } } bool SampledFunction::hasDifferentResultSet(const Function *func) const { if (func->getType() == 0) { SampledFunction *compTo = (SampledFunction *)func; if (compTo->getSampleNumber() != nSamples) { return true; } const double *compSamples = compTo->getSamples(); for (int i = 0; i < nSamples; i++) { if (samples[i] != compSamples[i]) { return true; } } } return false; } //------------------------------------------------------------------------ // ExponentialFunction //------------------------------------------------------------------------ ExponentialFunction::ExponentialFunction(Object *funcObj, Dict *dict) { Object obj1; ok = false; //----- initialize the generic stuff if (!init(dict)) { return; } if (m != 1) { error(errSyntaxError, -1, "Exponential function with more than one input"); return; } //----- C0 obj1 = dict->lookup("C0"); if (obj1.isArray()) { if (hasRange && obj1.arrayGetLength() != n) { error(errSyntaxError, -1, "Function's C0 array is wrong length"); return; } n = obj1.arrayGetLength(); if (unlikely(n > funcMaxOutputs)) { error(errSyntaxError, -1, "Function's C0 array is wrong length"); n = funcMaxOutputs; } for (int i = 0; i < n; ++i) { Object obj2 = obj1.arrayGet(i); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function C0 array"); return; } c0[i] = obj2.getNum(); } } else { if (hasRange && n != 1) { error(errSyntaxError, -1, "Function's C0 array is wrong length"); return; } n = 1; c0[0] = 0; } //----- C1 obj1 = dict->lookup("C1"); if (obj1.isArray()) { if (obj1.arrayGetLength() != n) { error(errSyntaxError, -1, "Function's C1 array is wrong length"); return; } for (int i = 0; i < n; ++i) { Object obj2 = obj1.arrayGet(i); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function C1 array"); return; } c1[i] = obj2.getNum(); } } else { if (n != 1) { error(errSyntaxError, -1, "Function's C1 array is wrong length"); return; } c1[0] = 1; } //----- N (exponent) obj1 = dict->lookup("N"); if (!obj1.isNum()) { error(errSyntaxError, -1, "Function has missing or invalid N"); return; } e = obj1.getNum(); isLinear = fabs(e - 1.) < 1e-10; ok = true; } ExponentialFunction::~ExponentialFunction() { } ExponentialFunction::ExponentialFunction(const ExponentialFunction *func) : Function(func) { memcpy(c0, func->c0, funcMaxOutputs * sizeof(double)); memcpy(c1, func->c1, funcMaxOutputs * sizeof(double)); e = func->e; isLinear = func->isLinear; ok = func->ok; } void ExponentialFunction::transform(const double *in, double *out) const { double x; int i; if (in[0] < domain[0][0]) { x = domain[0][0]; } else if (in[0] > domain[0][1]) { x = domain[0][1]; } else { x = in[0]; } for (i = 0; i < n; ++i) { out[i] = c0[i] + (isLinear ? x : pow(x, e)) * (c1[i] - c0[i]); if (hasRange) { if (out[i] < range[i][0]) { out[i] = range[i][0]; } else if (out[i] > range[i][1]) { out[i] = range[i][1]; } } } return; } //------------------------------------------------------------------------ // StitchingFunction //------------------------------------------------------------------------ StitchingFunction::StitchingFunction(Object *funcObj, Dict *dict, std::set *usedParents) { Object obj1; int i; ok = false; funcs = nullptr; bounds = nullptr; encode = nullptr; scale = nullptr; //----- initialize the generic stuff if (!init(dict)) { return; } if (m != 1) { error(errSyntaxError, -1, "Stitching function with more than one input"); return; } //----- Functions obj1 = dict->lookup("Functions"); if (!obj1.isArray()) { error(errSyntaxError, -1, "Missing 'Functions' entry in stitching function"); return; } k = obj1.arrayGetLength(); funcs = (Function **)gmallocn(k, sizeof(Function *)); bounds = (double *)gmallocn(k + 1, sizeof(double)); encode = (double *)gmallocn(2 * k, sizeof(double)); scale = (double *)gmallocn(k, sizeof(double)); for (i = 0; i < k; ++i) { funcs[i] = nullptr; } for (i = 0; i < k; ++i) { std::set usedParentsAux = *usedParents; Ref ref; Object obj2 = obj1.getArray()->get(i, &ref); if (ref != Ref::INVALID()) { if (usedParentsAux.find(ref.num) == usedParentsAux.end()) { usedParentsAux.insert(ref.num); } else { return; } } if (!(funcs[i] = Function::parse(&obj2, &usedParentsAux))) { return; } if (funcs[i]->getInputSize() != 1 || (i > 0 && funcs[i]->getOutputSize() != funcs[0]->getOutputSize())) { error(errSyntaxError, -1, "Incompatible subfunctions in stitching function"); return; } } //----- Bounds obj1 = dict->lookup("Bounds"); if (!obj1.isArray() || obj1.arrayGetLength() != k - 1) { error(errSyntaxError, -1, "Missing or invalid 'Bounds' entry in stitching function"); return; } bounds[0] = domain[0][0]; for (i = 1; i < k; ++i) { Object obj2 = obj1.arrayGet(i - 1); if (!obj2.isNum()) { error(errSyntaxError, -1, "Invalid type in 'Bounds' array in stitching function"); return; } bounds[i] = obj2.getNum(); } bounds[k] = domain[0][1]; //----- Encode obj1 = dict->lookup("Encode"); if (!obj1.isArray() || obj1.arrayGetLength() != 2 * k) { error(errSyntaxError, -1, "Missing or invalid 'Encode' entry in stitching function"); return; } for (i = 0; i < 2 * k; ++i) { Object obj2 = obj1.arrayGet(i); if (!obj2.isNum()) { error(errSyntaxError, -1, "Invalid type in 'Encode' array in stitching function"); return; } encode[i] = obj2.getNum(); } //----- pre-compute the scale factors for (i = 0; i < k; ++i) { if (bounds[i] == bounds[i + 1]) { // avoid a divide-by-zero -- in this situation, function i will // never be used anyway scale[i] = 0; } else { scale[i] = (encode[2 * i + 1] - encode[2 * i]) / (bounds[i + 1] - bounds[i]); } } n = funcs[0]->getOutputSize(); ok = true; return; } StitchingFunction::StitchingFunction(const StitchingFunction *func) : Function(func) { k = func->k; funcs = (Function **)gmallocn(k, sizeof(Function *)); for (int i = 0; i < k; ++i) { funcs[i] = func->funcs[i]->copy(); } bounds = (double *)gmallocn(k + 1, sizeof(double)); memcpy(bounds, func->bounds, (k + 1) * sizeof(double)); encode = (double *)gmallocn(2 * k, sizeof(double)); memcpy(encode, func->encode, 2 * k * sizeof(double)); scale = (double *)gmallocn(k, sizeof(double)); memcpy(scale, func->scale, k * sizeof(double)); ok = func->ok; } StitchingFunction::~StitchingFunction() { int i; if (funcs) { for (i = 0; i < k; ++i) { if (funcs[i]) { delete funcs[i]; } } } gfree(funcs); gfree(bounds); gfree(encode); gfree(scale); } void StitchingFunction::transform(const double *in, double *out) const { double x; int i; if (in[0] < domain[0][0]) { x = domain[0][0]; } else if (in[0] > domain[0][1]) { x = domain[0][1]; } else { x = in[0]; } for (i = 0; i < k - 1; ++i) { if (x < bounds[i + 1]) { break; } } x = encode[2 * i] + (x - bounds[i]) * scale[i]; funcs[i]->transform(&x, out); } //------------------------------------------------------------------------ // PostScriptFunction //------------------------------------------------------------------------ enum PSOp { psOpAbs, psOpAdd, psOpAnd, psOpAtan, psOpBitshift, psOpCeiling, psOpCopy, psOpCos, psOpCvi, psOpCvr, psOpDiv, psOpDup, psOpEq, psOpExch, psOpExp, psOpFalse, psOpFloor, psOpGe, psOpGt, psOpIdiv, psOpIndex, psOpLe, psOpLn, psOpLog, psOpLt, psOpMod, psOpMul, psOpNe, psOpNeg, psOpNot, psOpOr, psOpPop, psOpRoll, psOpRound, psOpSin, psOpSqrt, psOpSub, psOpTrue, psOpTruncate, psOpXor, psOpIf, psOpIfelse, psOpReturn }; // Note: 'if' and 'ifelse' are parsed separately. // The rest are listed here in alphabetical order. // The index in this table is equivalent to the entry in PSOp. static const char *psOpNames[] = { "abs", "add", "and", "atan", "bitshift", "ceiling", "copy", "cos", "cvi", "cvr", "div", "dup", "eq", "exch", "exp", "false", "floor", "ge", "gt", "idiv", "index", "le", "ln", "log", "lt", "mod", "mul", "ne", "neg", "not", "or", "pop", "roll", "round", "sin", "sqrt", "sub", "true", "truncate", "xor" }; #define nPSOps (sizeof(psOpNames) / sizeof(char *)) enum PSObjectType { psBool, psInt, psReal, psOperator, psBlock }; // In the code array, 'if'/'ifelse' operators take up three slots // plus space for the code in the subclause(s). // // +---------------------------------+ // | psOperator: psOpIf / psOpIfelse | // +---------------------------------+ // | psBlock: ptr= | // +---------------------------------+ // | psBlock: ptr= | // +---------------------------------+ // | if clause | // | ... | // | psOperator: psOpReturn | // +---------------------------------+ // | else clause | // | ... | // | psOperator: psOpReturn | // +---------------------------------+ // | ... | // // For 'if', pointer is present in the code stream but unused. struct PSObject { PSObjectType type; union { bool booln; // boolean (stack only) int intg; // integer (stack and code) double real; // real (stack and code) PSOp op; // operator (code only) int blk; // if/ifelse block pointer (code only) }; }; #define psStackSize 100 class PSStack { public: PSStack() { sp = psStackSize; } void clear() { sp = psStackSize; } void pushBool(bool booln) { if (checkOverflow()) { stack[--sp].type = psBool; stack[sp].booln = booln; } } void pushInt(int intg) { if (checkOverflow()) { stack[--sp].type = psInt; stack[sp].intg = intg; } } void pushReal(double real) { if (checkOverflow()) { stack[--sp].type = psReal; stack[sp].real = real; } } bool popBool() { if (checkUnderflow() && checkType(psBool, psBool)) { return stack[sp++].booln; } return false; } int popInt() { if (checkUnderflow() && checkType(psInt, psInt)) { return stack[sp++].intg; } return 0; } double popNum() { double ret; if (checkUnderflow() && checkType(psInt, psReal)) { ret = (stack[sp].type == psInt) ? (double)stack[sp].intg : stack[sp].real; ++sp; return ret; } return 0; } bool empty() { return sp == psStackSize; } bool topIsInt() { return sp < psStackSize && stack[sp].type == psInt; } bool topTwoAreInts() { return sp < psStackSize - 1 && stack[sp].type == psInt && stack[sp + 1].type == psInt; } bool topIsReal() { return sp < psStackSize && stack[sp].type == psReal; } bool topTwoAreNums() { return sp < psStackSize - 1 && (stack[sp].type == psInt || stack[sp].type == psReal) && (stack[sp + 1].type == psInt || stack[sp + 1].type == psReal); } void copy(int n); void roll(int n, int j); void index(int i) { if (!checkOverflow()) { return; } --sp; if (unlikely(sp + i + 1 >= psStackSize)) { error(errSyntaxError, -1, "Stack underflow in PostScript function"); return; } if (unlikely(sp + i + 1 < 0)) { error(errSyntaxError, -1, "Stack overflow in PostScript function"); return; } stack[sp] = stack[sp + 1 + i]; } void pop() { if (!checkUnderflow()) { return; } ++sp; } private: bool checkOverflow(int n = 1) { if (sp - n < 0) { error(errSyntaxError, -1, "Stack overflow in PostScript function"); return false; } return true; } bool checkUnderflow() { if (sp == psStackSize) { error(errSyntaxError, -1, "Stack underflow in PostScript function"); return false; } return true; } bool checkType(PSObjectType t1, PSObjectType t2) { if (stack[sp].type != t1 && stack[sp].type != t2) { error(errSyntaxError, -1, "Type mismatch in PostScript function"); return false; } return true; } PSObject stack[psStackSize]; int sp; }; void PSStack::copy(int n) { int i; int aux; if (unlikely(checkedAdd(sp, n, &aux) || aux > psStackSize)) { error(errSyntaxError, -1, "Stack underflow in PostScript function"); return; } if (unlikely(checkedSubtraction(sp, n, &aux) || aux > psStackSize)) { error(errSyntaxError, -1, "Stack underflow in PostScript function"); return; } if (!checkOverflow(n)) { return; } for (i = sp + n - 1; i >= sp; --i) { stack[i - n] = stack[i]; } sp -= n; } void PSStack::roll(int n, int j) { PSObject obj; int i, k; if (unlikely(n == 0)) { return; } if (j >= 0) { j %= n; } else { j = -j % n; if (j != 0) { j = n - j; } } if (n <= 0 || j == 0 || n > psStackSize || sp + n > psStackSize) { return; } if (j <= n / 2) { for (i = 0; i < j; ++i) { obj = stack[sp]; for (k = sp; k < sp + n - 1; ++k) { stack[k] = stack[k + 1]; } stack[sp + n - 1] = obj; } } else { j = n - j; for (i = 0; i < j; ++i) { obj = stack[sp + n - 1]; for (k = sp + n - 1; k > sp; --k) { stack[k] = stack[k - 1]; } stack[sp] = obj; } } } PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) { Stream *str; int codePtr; double in[funcMaxInputs]; int i; code = nullptr; codeString = nullptr; codeSize = 0; ok = false; //----- initialize the generic stuff if (!init(dict)) { goto err1; } if (!hasRange) { error(errSyntaxError, -1, "Type 4 function is missing range"); goto err1; } //----- get the stream if (!funcObj->isStream()) { error(errSyntaxError, -1, "Type 4 function isn't a stream"); goto err1; } str = funcObj->getStream(); //----- parse the function codeString = new GooString(); str->reset(); if (getToken(str)->cmp("{") != 0) { error(errSyntaxError, -1, "Expected '{{' at start of PostScript function"); goto err1; } codePtr = 0; if (!parseCode(str, &codePtr)) { goto err2; } str->close(); //----- set up the cache for (i = 0; i < m; ++i) { in[i] = domain[i][0]; cacheIn[i] = in[i] - 1; } transform(in, cacheOut); ok = true; err2: str->close(); err1: return; } PostScriptFunction::PostScriptFunction(const PostScriptFunction *func) : Function(func) { codeSize = func->codeSize; code = (PSObject *)gmallocn(codeSize, sizeof(PSObject)); memcpy(code, func->code, codeSize * sizeof(PSObject)); codeString = func->codeString->copy(); memcpy(cacheIn, func->cacheIn, funcMaxInputs * sizeof(double)); memcpy(cacheOut, func->cacheOut, funcMaxOutputs * sizeof(double)); ok = func->ok; } PostScriptFunction::~PostScriptFunction() { gfree(code); delete codeString; } void PostScriptFunction::transform(const double *in, double *out) const { PSStack stack; int i; // check the cache for (i = 0; i < m; ++i) { if (in[i] != cacheIn[i]) { break; } } if (i == m) { for (i = 0; i < n; ++i) { out[i] = cacheOut[i]; } return; } for (i = 0; i < m; ++i) { //~ may need to check for integers here stack.pushReal(in[i]); } exec(&stack, 0); for (i = n - 1; i >= 0; --i) { out[i] = stack.popNum(); if (out[i] < range[i][0]) { out[i] = range[i][0]; } else if (out[i] > range[i][1]) { out[i] = range[i][1]; } } stack.clear(); // if (!stack->empty()) { // error(errSyntaxWarning, -1, // "Extra values on stack at end of PostScript function"); // } // save current result in the cache for (i = 0; i < m; ++i) { cacheIn[i] = in[i]; } for (i = 0; i < n; ++i) { cacheOut[i] = out[i]; } } bool PostScriptFunction::parseCode(Stream *str, int *codePtr) { bool isReal; int opPtr, elsePtr; int a, b, mid, cmp; while (true) { // 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) { if (*p == '.') { isReal = true; break; } } resizeCode(*codePtr); if (isReal) { code[*codePtr].type = psReal; code[*codePtr].real = gatof(tok->c_str()); } else { code[*codePtr].type = psInt; code[*codePtr].intg = atoi(tok->c_str()); } ++*codePtr; } else if (!tok->cmp("{")) { opPtr = *codePtr; *codePtr += 3; resizeCode(opPtr + 2); if (!parseCode(str, codePtr)) { return false; } tok = getToken(str); if (!tok->cmp("{")) { elsePtr = *codePtr; if (!parseCode(str, codePtr)) { return false; } tok = getToken(str); } else { elsePtr = -1; } if (!tok->cmp("if")) { if (elsePtr >= 0) { error(errSyntaxError, -1, "Got 'if' operator with two blocks in PostScript function"); return false; } code[opPtr].type = psOperator; code[opPtr].op = psOpIf; code[opPtr + 2].type = psBlock; code[opPtr + 2].blk = *codePtr; } else if (!tok->cmp("ifelse")) { if (elsePtr < 0) { error(errSyntaxError, -1, "Got 'ifelse' operator with one block in PostScript function"); return false; } code[opPtr].type = psOperator; code[opPtr].op = psOpIfelse; code[opPtr + 1].type = psBlock; code[opPtr + 1].blk = elsePtr; code[opPtr + 2].type = psBlock; code[opPtr + 2].blk = *codePtr; } else { error(errSyntaxError, -1, "Expected if/ifelse operator in PostScript function"); return false; } } else if (!tok->cmp("}")) { resizeCode(*codePtr); code[*codePtr].type = psOperator; code[*codePtr].op = psOpReturn; ++*codePtr; break; } else { a = -1; b = nPSOps; cmp = 0; // make gcc happy // invariant: psOpNames[a] < tok < psOpNames[b] while (b - a > 1) { mid = (a + b) / 2; cmp = tok->cmp(psOpNames[mid]); if (cmp > 0) { a = mid; } else if (cmp < 0) { b = mid; } else { a = b = mid; } } if (cmp != 0) { error(errSyntaxError, -1, "Unknown operator '{0:t}' in PostScript function", tok.get()); return false; } resizeCode(*codePtr); code[*codePtr].type = psOperator; code[*codePtr].op = (PSOp)a; ++*codePtr; } } return true; } std::unique_ptr PostScriptFunction::getToken(Stream *str) { int c; bool comment; std::string s; comment = false; while (true) { if ((c = str->getChar()) == EOF) { break; } codeString->append(c); if (comment) { if (c == '\x0a' || c == '\x0d') { comment = false; } } else if (c == '%') { comment = true; } else if (!isspace(c)) { break; } } if (c == '{' || c == '}') { s.push_back((char)c); } else if (isdigit(c) || c == '.' || c == '-') { while (true) { s.push_back((char)c); c = str->lookChar(); if (c == EOF || !(isdigit(c) || c == '.' || c == '-')) { break; } str->getChar(); codeString->append(c); } } else { while (true) { s.push_back((char)c); c = str->lookChar(); if (c == EOF || !isalnum(c)) { break; } str->getChar(); codeString->append(c); } } return std::make_unique(s); } void PostScriptFunction::resizeCode(int newSize) { if (newSize >= codeSize) { codeSize += 64; code = (PSObject *)greallocn(code, codeSize, sizeof(PSObject)); } } void PostScriptFunction::exec(PSStack *stack, int codePtr) const { int i1, i2; double r1, r2, result; bool b1, b2; while (true) { switch (code[codePtr].type) { case psInt: stack->pushInt(code[codePtr++].intg); break; case psReal: stack->pushReal(code[codePtr++].real); break; case psOperator: switch (code[codePtr++].op) { case psOpAbs: if (stack->topIsInt()) { stack->pushInt(abs(stack->popInt())); } else { stack->pushReal(fabs(stack->popNum())); } break; case psOpAdd: if (stack->topTwoAreInts()) { i2 = stack->popInt(); i1 = stack->popInt(); stack->pushInt(i1 + i2); } else { r2 = stack->popNum(); r1 = stack->popNum(); stack->pushReal(r1 + r2); } break; case psOpAnd: if (stack->topTwoAreInts()) { i2 = stack->popInt(); i1 = stack->popInt(); stack->pushInt(i1 & i2); } else { b2 = stack->popBool(); b1 = stack->popBool(); stack->pushBool(b1 && b2); } break; case psOpAtan: r2 = stack->popNum(); r1 = stack->popNum(); result = atan2(r1, r2) * 180.0 / M_PI; if (result < 0) { result += 360.0; } stack->pushReal(result); break; case psOpBitshift: i2 = stack->popInt(); i1 = stack->popInt(); if (i2 > 0) { stack->pushInt(i1 << i2); } else if (i2 < 0) { stack->pushInt((int)((unsigned int)i1 >> -i2)); } else { stack->pushInt(i1); } break; case psOpCeiling: if (!stack->topIsInt()) { stack->pushReal(ceil(stack->popNum())); } break; case psOpCopy: stack->copy(stack->popInt()); break; case psOpCos: stack->pushReal(cos(stack->popNum() * M_PI / 180.0)); break; case psOpCvi: if (!stack->topIsInt()) { stack->pushInt((int)stack->popNum()); } break; case psOpCvr: if (!stack->topIsReal()) { stack->pushReal(stack->popNum()); } break; case psOpDiv: r2 = stack->popNum(); r1 = stack->popNum(); stack->pushReal(r1 / r2); break; case psOpDup: stack->copy(1); break; case psOpEq: if (stack->topTwoAreInts()) { i2 = stack->popInt(); i1 = stack->popInt(); stack->pushBool(i1 == i2); } else if (stack->topTwoAreNums()) { r2 = stack->popNum(); r1 = stack->popNum(); stack->pushBool(r1 == r2); } else { b2 = stack->popBool(); b1 = stack->popBool(); stack->pushBool(b1 == b2); } break; case psOpExch: stack->roll(2, 1); break; case psOpExp: r2 = stack->popNum(); r1 = stack->popNum(); stack->pushReal(pow(r1, r2)); break; case psOpFalse: stack->pushBool(false); break; case psOpFloor: if (!stack->topIsInt()) { stack->pushReal(floor(stack->popNum())); } break; case psOpGe: if (stack->topTwoAreInts()) { i2 = stack->popInt(); i1 = stack->popInt(); stack->pushBool(i1 >= i2); } else { r2 = stack->popNum(); r1 = stack->popNum(); stack->pushBool(r1 >= r2); } break; case psOpGt: if (stack->topTwoAreInts()) { i2 = stack->popInt(); i1 = stack->popInt(); stack->pushBool(i1 > i2); } else { r2 = stack->popNum(); r1 = stack->popNum(); stack->pushBool(r1 > r2); } break; case psOpIdiv: i2 = stack->popInt(); i1 = stack->popInt(); if (likely((i2 != 0) && !(i2 == -1 && i1 == INT_MIN))) { stack->pushInt(i1 / i2); } break; case psOpIndex: stack->index(stack->popInt()); break; case psOpLe: if (stack->topTwoAreInts()) { i2 = stack->popInt(); i1 = stack->popInt(); stack->pushBool(i1 <= i2); } else { r2 = stack->popNum(); r1 = stack->popNum(); stack->pushBool(r1 <= r2); } break; case psOpLn: stack->pushReal(log(stack->popNum())); break; case psOpLog: stack->pushReal(log10(stack->popNum())); break; case psOpLt: if (stack->topTwoAreInts()) { i2 = stack->popInt(); i1 = stack->popInt(); stack->pushBool(i1 < i2); } else { r2 = stack->popNum(); r1 = stack->popNum(); stack->pushBool(r1 < r2); } break; case psOpMod: i2 = stack->popInt(); i1 = stack->popInt(); if (likely(i2 != 0)) { stack->pushInt(i1 % i2); } break; case psOpMul: if (stack->topTwoAreInts()) { i2 = stack->popInt(); i1 = stack->popInt(); //~ should check for out-of-range, and push a real instead stack->pushInt(i1 * i2); } else { r2 = stack->popNum(); r1 = stack->popNum(); stack->pushReal(r1 * r2); } break; case psOpNe: if (stack->topTwoAreInts()) { i2 = stack->popInt(); i1 = stack->popInt(); stack->pushBool(i1 != i2); } else if (stack->topTwoAreNums()) { r2 = stack->popNum(); r1 = stack->popNum(); stack->pushBool(r1 != r2); } else { b2 = stack->popBool(); b1 = stack->popBool(); stack->pushBool(b1 != b2); } break; case psOpNeg: if (stack->topIsInt()) { stack->pushInt(-stack->popInt()); } else { stack->pushReal(-stack->popNum()); } break; case psOpNot: if (stack->topIsInt()) { stack->pushInt(~stack->popInt()); } else { stack->pushBool(!stack->popBool()); } break; case psOpOr: if (stack->topTwoAreInts()) { i2 = stack->popInt(); i1 = stack->popInt(); stack->pushInt(i1 | i2); } else { b2 = stack->popBool(); b1 = stack->popBool(); stack->pushBool(b1 || b2); } break; case psOpPop: stack->pop(); break; case psOpRoll: i2 = stack->popInt(); i1 = stack->popInt(); stack->roll(i1, i2); break; case psOpRound: if (!stack->topIsInt()) { r1 = stack->popNum(); stack->pushReal((r1 >= 0) ? floor(r1 + 0.5) : ceil(r1 - 0.5)); } break; case psOpSin: stack->pushReal(sin(stack->popNum() * M_PI / 180.0)); break; case psOpSqrt: stack->pushReal(sqrt(stack->popNum())); break; case psOpSub: if (stack->topTwoAreInts()) { i2 = stack->popInt(); i1 = stack->popInt(); stack->pushInt(i1 - i2); } else { r2 = stack->popNum(); r1 = stack->popNum(); stack->pushReal(r1 - r2); } break; case psOpTrue: stack->pushBool(true); break; case psOpTruncate: if (!stack->topIsInt()) { r1 = stack->popNum(); stack->pushReal((r1 >= 0) ? floor(r1) : ceil(r1)); } break; case psOpXor: if (stack->topTwoAreInts()) { i2 = stack->popInt(); i1 = stack->popInt(); stack->pushInt(i1 ^ i2); } else { b2 = stack->popBool(); b1 = stack->popBool(); stack->pushBool(b1 ^ b2); } break; case psOpIf: b1 = stack->popBool(); if (b1) { exec(stack, codePtr + 2); } codePtr = code[codePtr + 1].blk; break; case psOpIfelse: b1 = stack->popBool(); if (b1) { exec(stack, codePtr + 2); } else { exec(stack, code[codePtr].blk); } codePtr = code[codePtr + 1].blk; break; case psOpReturn: return; } break; default: error(errSyntaxError, -1, "Internal: bad object in PostScript function code"); break; } } } poppler-24.02.0/poppler/Function.h000066400000000000000000000201731455701731300167700ustar00rootroot00000000000000//======================================================================== // // Function.h // // Copyright 2001-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) 2009, 2010, 2018, 2019, 2021 Albert Astals Cid // Copyright (C) 2010 Christian Feuersänger // Copyright (C) 2011 Andrea Canciani // Copyright (C) 2012 Thomas Freitag // Copyright (C) 2012 Adam Reichold // // 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 FUNCTION_H #define FUNCTION_H #include "Object.h" #include class Dict; class Stream; struct PSObject; class PSStack; //------------------------------------------------------------------------ // Function //------------------------------------------------------------------------ #define funcMaxInputs 32 #define funcMaxOutputs 32 #define sampledFuncMaxInputs 16 class POPPLER_PRIVATE_EXPORT Function { public: Function(); virtual ~Function(); Function(const Function &) = delete; Function &operator=(const Function &other) = delete; // Construct a function. Returns NULL if unsuccessful. static Function *parse(Object *funcObj); // Initialize the entries common to all function types. bool init(Dict *dict); virtual Function *copy() const = 0; // Return the function type: // -1 : identity // 0 : sampled // 2 : exponential // 3 : stitching // 4 : PostScript virtual int getType() const = 0; // Return size of input and output tuples. int getInputSize() const { return m; } int getOutputSize() const { return n; } double getDomainMin(int i) const { return domain[i][0]; } double getDomainMax(int i) const { return domain[i][1]; } double getRangeMin(int i) const { return range[i][0]; } double getRangeMax(int i) const { return range[i][1]; } bool getHasRange() const { return hasRange; } virtual bool hasDifferentResultSet(const Function *func) const { return false; } // Transform an input tuple into an output tuple. virtual void transform(const double *in, double *out) const = 0; virtual bool isOk() const = 0; protected: static Function *parse(Object *funcObj, std::set *usedParents); explicit Function(const Function *func); int m, n; // size of input and output tuples double // min and max values for function domain domain[funcMaxInputs][2]; double // min and max values for function range range[funcMaxOutputs][2]; bool hasRange; // set if range is defined }; //------------------------------------------------------------------------ // IdentityFunction //------------------------------------------------------------------------ class IdentityFunction : public Function { public: IdentityFunction(); ~IdentityFunction() override; Function *copy() const override { return new IdentityFunction(); } int getType() const override { return -1; } void transform(const double *in, double *out) const override; bool isOk() const override { return true; } private: }; //------------------------------------------------------------------------ // SampledFunction //------------------------------------------------------------------------ class SampledFunction : public Function { public: SampledFunction(Object *funcObj, Dict *dict); ~SampledFunction() override; Function *copy() const override { return new SampledFunction(this); } int getType() const override { return 0; } void transform(const double *in, double *out) const override; bool isOk() const override { return ok; } bool hasDifferentResultSet(const Function *func) const override; int getSampleSize(int i) const { return sampleSize[i]; } double getEncodeMin(int i) const { return encode[i][0]; } double getEncodeMax(int i) const { return encode[i][1]; } double getDecodeMin(int i) const { return decode[i][0]; } double getDecodeMax(int i) const { return decode[i][1]; } const double *getSamples() const { return samples; } int getSampleNumber() const { return nSamples; } private: explicit SampledFunction(const SampledFunction *func); int // number of samples for each domain element sampleSize[funcMaxInputs]; double // min and max values for domain encoder encode[funcMaxInputs][2]; double // min and max values for range decoder decode[funcMaxOutputs][2]; double // input multipliers inputMul[funcMaxInputs]; int *idxOffset; double *samples; // the samples int nSamples; // size of the samples array double *sBuf; // buffer for the transform function mutable double cacheIn[funcMaxInputs]; mutable double cacheOut[funcMaxOutputs]; bool ok; }; //------------------------------------------------------------------------ // ExponentialFunction //------------------------------------------------------------------------ class ExponentialFunction : public Function { public: ExponentialFunction(Object *funcObj, Dict *dict); ~ExponentialFunction() override; Function *copy() const override { return new ExponentialFunction(this); } int getType() const override { return 2; } void transform(const double *in, double *out) const override; bool isOk() const override { return ok; } const double *getC0() const { return c0; } const double *getC1() const { return c1; } double getE() const { return e; } private: explicit ExponentialFunction(const ExponentialFunction *func); double c0[funcMaxOutputs]; double c1[funcMaxOutputs]; double e; bool isLinear; bool ok; }; //------------------------------------------------------------------------ // StitchingFunction //------------------------------------------------------------------------ class StitchingFunction : public Function { public: StitchingFunction(Object *funcObj, Dict *dict, std::set *usedParents); ~StitchingFunction() override; Function *copy() const override { return new StitchingFunction(this); } int getType() const override { return 3; } void transform(const double *in, double *out) const override; bool isOk() const override { return ok; } int getNumFuncs() const { return k; } const Function *getFunc(int i) const { return funcs[i]; } const double *getBounds() const { return bounds; } const double *getEncode() const { return encode; } const double *getScale() const { return scale; } private: explicit StitchingFunction(const StitchingFunction *func); int k; Function **funcs; double *bounds; double *encode; double *scale; bool ok; }; //------------------------------------------------------------------------ // PostScriptFunction //------------------------------------------------------------------------ class PostScriptFunction : public Function { public: PostScriptFunction(Object *funcObj, Dict *dict); ~PostScriptFunction() override; Function *copy() const override { return new PostScriptFunction(this); } int getType() const override { return 4; } void transform(const double *in, double *out) const override; bool isOk() const override { return ok; } const GooString *getCodeString() const { return codeString; } private: explicit PostScriptFunction(const PostScriptFunction *func); bool parseCode(Stream *str, int *codePtr); std::unique_ptr getToken(Stream *str); void resizeCode(int newSize); void exec(PSStack *stack, int codePtr) const; GooString *codeString; PSObject *code; int codeSize; mutable double cacheIn[funcMaxInputs]; mutable double cacheOut[funcMaxOutputs]; bool ok; }; #endif poppler-24.02.0/poppler/GPGMECryptoSignBackend.cc000066400000000000000000000307111455701731300214710ustar00rootroot00000000000000//======================================================================== // // GPGMECryptoSignBackend.cc // // This file is licensed under the GPLv2 or later // // Copyright 2023 g10 Code GmbH, Author: Sune Stolborg Vuorela //======================================================================== #include "config.h" #include "GPGMECryptoSignBackend.h" #include "DistinguishedNameParser.h" #include #include #include #include #include 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(); 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) { auto certificateInfo = std::make_unique(); certificateInfo->setIssuerInfo(getEntityInfoFromKey(fromCharPtr(key.issuerName()))); certificateInfo->setSerialNumber(GooString { DN::detail::parseHexString(fromCharPtr(key.issuerSerial())).value_or("") }); auto subjectInfo = getEntityInfoFromKey(fromCharPtr(key.userID(0).id())); if (subjectInfo.email.empty()) { subjectInfo.email = fromCharPtr(key.userID(1).email()); } certificateInfo->setSubjectInfo(std::move(subjectInfo)); certificateInfo->setValidity(getValidityFromSubkey(key.subkey(0))); 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: pkInfo.publicKeyType = OTHERKEY; } { auto ctx = GpgME::Context::create(GpgME::CMS); 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); } 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) { return std::make_unique(std::move(pkcs7)); } std::vector> GpgSignatureBackend::getAvailableSigningCertificates() { std::vector> certificates; const auto context = GpgME::Context::create(GpgME::CMS); 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)); } else { break; } } return certificates; } GpgSignatureCreation::GpgSignatureCreation(const std::string &certId) : gpgContext { GpgME::Context::create(GpgME::CMS) } { GpgME::Error error; const auto signingKey = gpgContext->key(certId.c_str(), error, true); if (isSuccess(error)) { gpgContext->addSigningKey(signingKey); key = signingKey; } } void GpgSignatureCreation::addData(unsigned char *dataBlock, int dataLen) { gpgData.write(dataBlock, dataLen); } std::optional GpgSignatureCreation::signDetached(const std::string &password) { if (!key) { return {}; } gpgData.rewind(); GpgME::Data signatureData; const auto signingResult = gpgContext->sign(gpgData, signatureData, GpgME::SignatureMode::Detached); if (!isValidResult(signingResult)) { return {}; } const auto signatureString = signatureData.toString(); return GooString(std::move(signatureString)); } std::unique_ptr GpgSignatureCreation::getCertificateInfo() const { if (!key) { return nullptr; } return getCertificateInfoFromKey(*key); } GpgSignatureVerification::GpgSignatureVerification(const std::vector &p7data) : gpgContext { GpgME::Context::create(GpgME::CMS) }, signatureData(reinterpret_cast(p7data.data()), p7data.size()) { gpgContext->setOffline(true); signatureData.setEncoding(GpgME::Data::BinaryEncoding); } void GpgSignatureVerification::addData(unsigned char *dataBlock, int dataLen) { signedData.write(dataBlock, dataLen); } 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)); 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 {}; } const auto dn = DN::parseString(fromCharPtr(signature->key(true, false).userID(0).id())); return DN::FindFirstValue(dn, "CN").value_or(""); } 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()); } CertificateValidationStatus GpgSignatureVerification::validateCertificate(std::chrono::system_clock::time_point validation_time, bool ocspRevocationCheck, bool useAIACertFetch) { if (!gpgResult) { return CERTIFICATE_NOT_VERIFIED; } if (gpgResult->error()) { return CERTIFICATE_GENERIC_ERROR; } const auto signature = getSignature(gpgResult.value(), 0); if (!signature) { return CERTIFICATE_GENERIC_ERROR; } const auto offline = gpgContext->offline(); gpgContext->setOffline((!ocspRevocationCheck) || useAIACertFetch); const auto key = signature->key(true, true); gpgContext->setOffline(offline); if (key.isExpired()) { return CERTIFICATE_EXPIRED; } if (key.isRevoked()) { return CERTIFICATE_REVOKED; } if (key.isBad()) { return CERTIFICATE_NOT_VERIFIED; } return CERTIFICATE_TRUSTED; } 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; } // Ensure key is actually available signature->key(true, true); const auto summary = signature->summary(); using Summary = GpgME::Signature::Summary; if (summary & Summary::Red) { return SIGNATURE_INVALID; } if (summary & Summary::Green || summary & Summary::Valid) { return SIGNATURE_VALID; } return SIGNATURE_GENERIC_ERROR; } poppler-24.02.0/poppler/GPGMECryptoSignBackend.h000066400000000000000000000043721455701731300213370ustar00rootroot00000000000000//======================================================================== // // GPGMECryptoSignBackend.h // // This file is licensed under the GPLv2 or later // // Copyright 2023 g10 Code GmbH, Author: Sune Stolborg Vuorela //======================================================================== #include "CryptoSignBackend.h" #include #include #include class GpgSignatureBackend : public CryptoSign::Backend { public: GpgSignatureBackend(); std::unique_ptr createVerificationHandler(std::vector &&pkcs7) 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::optional signDetached(const std::string &password) final; private: std::unique_ptr gpgContext; GpgME::Data gpgData; std::optional key; }; class GpgSignatureVerification : public CryptoSign::VerificationInterface { public: explicit GpgSignatureVerification(const std::vector &pkcs7data); 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 validateCertificate(std::chrono::system_clock::time_point validation_time, bool ocspRevocationCheck, bool useAIACertFetch) final; std::unique_ptr getCertificateInfo() const final; private: std::unique_ptr gpgContext; GpgME::Data signatureData; GpgME::Data signedData; std::optional gpgResult; }; poppler-24.02.0/poppler/Gfx.cc000066400000000000000000005274701455701731300161010ustar00rootroot00000000000000//======================================================================== // // Gfx.cc // // Copyright 1996-2013 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) 2005 Jonathan Blandford // Copyright (C) 2005-2013, 2015-2022 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, 2023 Adrian Johnson // Copyright (C) 2007, 2008 Iñigo Martínez // Copyright (C) 2007 Koji Otani // Copyright (C) 2007 Krzysztof Kowalczyk // Copyright (C) 2008 Pino Toscano // Copyright (C) 2008 Michael Vrable // Copyright (C) 2008 Hib Eris // Copyright (C) 2009 M Joonas Pihlaja // Copyright (C) 2009-2016, 2020 Thomas Freitag // Copyright (C) 2009 William Bader // Copyright (C) 2009, 2010 David Benjamin // 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, 2013 Fabio D'Urso // Copyright (C) 2012 Lu Wang // Copyright (C) 2014 Jason Crain // 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, 2019 Adam Reichold // Copyright (C) 2018 Denis Onishchenko // Copyright (C) 2019 LE GARREC Vincent // Copyright (C) 2019-2022 Oliver Sander // Copyright (C) 2019 Volker Krause // Copyright (C) 2020 Philipp Knechtges // Copyright (C) 2021 Steve Rosenhamer // 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 // //======================================================================== #include #include #include #include #include #include #include #include "goo/gmem.h" #include "goo/GooTimer.h" #include "GlobalParams.h" #include "CharTypes.h" #include "Object.h" #include "PDFDoc.h" #include "Array.h" #include "Annot.h" #include "Dict.h" #include "Stream.h" #include "Lexer.h" #include "Parser.h" #include "GfxFont.h" #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" // the MSVC math.h doesn't define this #ifndef M_PI # define M_PI 3.14159265358979323846 #endif //------------------------------------------------------------------------ // constants //------------------------------------------------------------------------ // Max recursive depth for a function shading fill. #define functionMaxDepth 6 // Max delta allowed in any color component for a function shading fill. #define functionColorDelta (dblToCol(1 / 256.0)) // Max number of splits along the t axis for an axial shading fill. #define axialMaxSplits 256 // Max delta allowed in any color component for an axial shading fill. #define axialColorDelta (dblToCol(1 / 256.0)) // Max number of splits along the t axis for a radial shading fill. #define radialMaxSplits 256 // Max delta allowed in any color component for a radial shading fill. #define radialColorDelta (dblToCol(1 / 256.0)) // Max recursive depth for a Gouraud triangle shading fill. // // Triangles will be split at most gouraudMaxDepth times (each time into 4 // smaller ones). That makes pow(4,gouraudMaxDepth) many triangles for // every triangle. #define gouraudMaxDepth 6 // Max delta allowed in any color component for a Gouraud triangle // shading fill. #define gouraudColorDelta (dblToCol(3. / 256.0)) // Gouraud triangle: if the three color parameters differ by at more than this percend of // the total color parameter range, the triangle will be refined #define gouraudParameterizedColorDelta 5e-3 // Max recursive depth for a patch mesh shading fill. #define patchMaxDepth 6 // Max delta allowed in any color component for a patch mesh shading // fill. #define patchColorDelta (dblToCol((3. / 256.0))) //------------------------------------------------------------------------ // Operator table //------------------------------------------------------------------------ const Operator Gfx::opTab[] = { { "\"", 3, { tchkNum, tchkNum, tchkString }, &Gfx::opMoveSetShowText }, { "'", 1, { tchkString }, &Gfx::opMoveShowText }, { "B", 0, { tchkNone }, &Gfx::opFillStroke }, { "B*", 0, { tchkNone }, &Gfx::opEOFillStroke }, { "BDC", 2, { tchkName, tchkProps }, &Gfx::opBeginMarkedContent }, { "BI", 0, { tchkNone }, &Gfx::opBeginImage }, { "BMC", 1, { tchkName }, &Gfx::opBeginMarkedContent }, { "BT", 0, { tchkNone }, &Gfx::opBeginText }, { "BX", 0, { tchkNone }, &Gfx::opBeginIgnoreUndef }, { "CS", 1, { tchkName }, &Gfx::opSetStrokeColorSpace }, { "DP", 2, { tchkName, tchkProps }, &Gfx::opMarkPoint }, { "Do", 1, { tchkName }, &Gfx::opXObject }, { "EI", 0, { tchkNone }, &Gfx::opEndImage }, { "EMC", 0, { tchkNone }, &Gfx::opEndMarkedContent }, { "ET", 0, { tchkNone }, &Gfx::opEndText }, { "EX", 0, { tchkNone }, &Gfx::opEndIgnoreUndef }, { "F", 0, { tchkNone }, &Gfx::opFill }, { "G", 1, { tchkNum }, &Gfx::opSetStrokeGray }, { "ID", 0, { tchkNone }, &Gfx::opImageData }, { "J", 1, { tchkInt }, &Gfx::opSetLineCap }, { "K", 4, { tchkNum, tchkNum, tchkNum, tchkNum }, &Gfx::opSetStrokeCMYKColor }, { "M", 1, { tchkNum }, &Gfx::opSetMiterLimit }, { "MP", 1, { tchkName }, &Gfx::opMarkPoint }, { "Q", 0, { tchkNone }, &Gfx::opRestore }, { "RG", 3, { tchkNum, tchkNum, tchkNum }, &Gfx::opSetStrokeRGBColor }, { "S", 0, { tchkNone }, &Gfx::opStroke }, { "SC", -4, { tchkNum, tchkNum, tchkNum, tchkNum }, &Gfx::opSetStrokeColor }, { "SCN", -33, { tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN }, &Gfx::opSetStrokeColorN }, { "T*", 0, { tchkNone }, &Gfx::opTextNextLine }, { "TD", 2, { tchkNum, tchkNum }, &Gfx::opTextMoveSet }, { "TJ", 1, { tchkArray }, &Gfx::opShowSpaceText }, { "TL", 1, { tchkNum }, &Gfx::opSetTextLeading }, { "Tc", 1, { tchkNum }, &Gfx::opSetCharSpacing }, { "Td", 2, { tchkNum, tchkNum }, &Gfx::opTextMove }, { "Tf", 2, { tchkName, tchkNum }, &Gfx::opSetFont }, { "Tj", 1, { tchkString }, &Gfx::opShowText }, { "Tm", 6, { tchkNum, tchkNum, tchkNum, tchkNum, tchkNum, tchkNum }, &Gfx::opSetTextMatrix }, { "Tr", 1, { tchkInt }, &Gfx::opSetTextRender }, { "Ts", 1, { tchkNum }, &Gfx::opSetTextRise }, { "Tw", 1, { tchkNum }, &Gfx::opSetWordSpacing }, { "Tz", 1, { tchkNum }, &Gfx::opSetHorizScaling }, { "W", 0, { tchkNone }, &Gfx::opClip }, { "W*", 0, { tchkNone }, &Gfx::opEOClip }, { "b", 0, { tchkNone }, &Gfx::opCloseFillStroke }, { "b*", 0, { tchkNone }, &Gfx::opCloseEOFillStroke }, { "c", 6, { tchkNum, tchkNum, tchkNum, tchkNum, tchkNum, tchkNum }, &Gfx::opCurveTo }, { "cm", 6, { tchkNum, tchkNum, tchkNum, tchkNum, tchkNum, tchkNum }, &Gfx::opConcat }, { "cs", 1, { tchkName }, &Gfx::opSetFillColorSpace }, { "d", 2, { tchkArray, tchkNum }, &Gfx::opSetDash }, { "d0", 2, { tchkNum, tchkNum }, &Gfx::opSetCharWidth }, { "d1", 6, { tchkNum, tchkNum, tchkNum, tchkNum, tchkNum, tchkNum }, &Gfx::opSetCacheDevice }, { "f", 0, { tchkNone }, &Gfx::opFill }, { "f*", 0, { tchkNone }, &Gfx::opEOFill }, { "g", 1, { tchkNum }, &Gfx::opSetFillGray }, { "gs", 1, { tchkName }, &Gfx::opSetExtGState }, { "h", 0, { tchkNone }, &Gfx::opClosePath }, { "i", 1, { tchkNum }, &Gfx::opSetFlat }, { "j", 1, { tchkInt }, &Gfx::opSetLineJoin }, { "k", 4, { tchkNum, tchkNum, tchkNum, tchkNum }, &Gfx::opSetFillCMYKColor }, { "l", 2, { tchkNum, tchkNum }, &Gfx::opLineTo }, { "m", 2, { tchkNum, tchkNum }, &Gfx::opMoveTo }, { "n", 0, { tchkNone }, &Gfx::opEndPath }, { "q", 0, { tchkNone }, &Gfx::opSave }, { "re", 4, { tchkNum, tchkNum, tchkNum, tchkNum }, &Gfx::opRectangle }, { "rg", 3, { tchkNum, tchkNum, tchkNum }, &Gfx::opSetFillRGBColor }, { "ri", 1, { tchkName }, &Gfx::opSetRenderingIntent }, { "s", 0, { tchkNone }, &Gfx::opCloseStroke }, { "sc", -4, { tchkNum, tchkNum, tchkNum, tchkNum }, &Gfx::opSetFillColor }, { "scn", -33, { tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN, tchkSCN }, &Gfx::opSetFillColorN }, { "sh", 1, { tchkName }, &Gfx::opShFill }, { "v", 4, { tchkNum, tchkNum, tchkNum, tchkNum }, &Gfx::opCurveTo1 }, { "w", 1, { tchkNum }, &Gfx::opSetLineWidth }, { "y", 4, { tchkNum, tchkNum, tchkNum, tchkNum }, &Gfx::opCurveTo2 }, }; #define numOps (sizeof(opTab) / sizeof(Operator)) static inline bool isSameGfxColor(const GfxColor &colorA, const GfxColor &colorB, unsigned int nComps, double delta) { for (unsigned int k = 0; k < nComps; ++k) { if (abs(colorA.c[k] - colorB.c[k]) > delta) { return false; } } return true; } //------------------------------------------------------------------------ // GfxResources //------------------------------------------------------------------------ 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()); } // get XObject dictionary xObjDict = resDict->lookup("XObject"); // get color space dictionary colorSpaceDict = resDict->lookup("ColorSpace"); // get pattern dictionary patternDict = resDict->lookup("Pattern"); // get shading dictionary shadingDict = resDict->lookup("Shading"); // get graphics state parameter dictionary gStateDict = resDict->lookup("ExtGState"); // get properties dictionary propertiesDict = resDict->lookup("Properties"); delete resDict; } else { fonts = nullptr; xObjDict.setToNull(); colorSpaceDict.setToNull(); patternDict.setToNull(); shadingDict.setToNull(); gStateDict.setToNull(); propertiesDict.setToNull(); } next = nextA; } GfxResources::~GfxResources() { delete fonts; } std::shared_ptr GfxResources::doLookupFont(const char *name) const { const GfxResources *resPtr; for (resPtr = this; resPtr; resPtr = resPtr->next) { if (resPtr->fonts) { if (std::shared_ptr font = resPtr->fonts->lookup(name)) { return font; } } } error(errSyntaxError, -1, "Unknown font tag '{0:s}'", name); return nullptr; } std::shared_ptr GfxResources::lookupFont(const char *name) { return doLookupFont(name); } std::shared_ptr GfxResources::lookupFont(const char *name) const { return doLookupFont(name); } Object GfxResources::lookupXObject(const char *name) { GfxResources *resPtr; for (resPtr = this; resPtr; resPtr = resPtr->next) { if (resPtr->xObjDict.isDict()) { Object obj = resPtr->xObjDict.dictLookup(name); if (!obj.isNull()) { return obj; } } } error(errSyntaxError, -1, "XObject '{0:s}' is unknown", name); return Object(objNull); } Object GfxResources::lookupXObjectNF(const char *name) { GfxResources *resPtr; for (resPtr = this; resPtr; resPtr = resPtr->next) { if (resPtr->xObjDict.isDict()) { Object obj = resPtr->xObjDict.dictLookupNF(name).copy(); if (!obj.isNull()) { return obj; } } } error(errSyntaxError, -1, "XObject '{0:s}' is unknown", name); return Object(objNull); } Object GfxResources::lookupMarkedContentNF(const char *name) { GfxResources *resPtr; for (resPtr = this; resPtr; resPtr = resPtr->next) { if (resPtr->propertiesDict.isDict()) { Object obj = resPtr->propertiesDict.dictLookupNF(name).copy(); if (!obj.isNull()) { return obj; } } } error(errSyntaxError, -1, "Marked Content '{0:s}' is unknown", name); return Object(objNull); } Object GfxResources::lookupColorSpace(const char *name) { GfxResources *resPtr; for (resPtr = this; resPtr; resPtr = resPtr->next) { if (resPtr->colorSpaceDict.isDict()) { Object obj = resPtr->colorSpaceDict.dictLookup(name); if (!obj.isNull()) { return obj; } } } return Object(objNull); } GfxPattern *GfxResources::lookupPattern(const char *name, OutputDev *out, GfxState *state) { GfxResources *resPtr; for (resPtr = this; resPtr; resPtr = resPtr->next) { if (resPtr->patternDict.isDict()) { Ref patternRef = Ref::INVALID(); Object obj = resPtr->patternDict.getDict()->lookup(name, &patternRef); if (!obj.isNull()) { return GfxPattern::parse(resPtr, &obj, out, state, patternRef.num); } } } error(errSyntaxError, -1, "Unknown pattern '{0:s}'", name); return nullptr; } GfxShading *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; } } } error(errSyntaxError, -1, "ExtGState '{0:s}' is unknown", name); return nullptr; } Object GfxResources::lookupGState(const char *name) { Object obj = lookupGStateNF(name); if (obj.isNull()) { return Object(objNull); } if (!obj.isRef()) { return obj; } const Ref ref = obj.getRef(); if (auto *item = gStateCache.lookup(ref)) { return item->copy(); } auto *item = new Object { xref->fetch(ref) }; gStateCache.put(ref, item); return item->copy(); } Object GfxResources::lookupGStateNF(const char *name) { GfxResources *resPtr; for (resPtr = this; resPtr; resPtr = resPtr->next) { if (resPtr->gStateDict.isDict()) { Object obj = resPtr->gStateDict.dictLookupNF(name).copy(); if (!obj.isNull()) { return obj; } } } error(errSyntaxError, -1, "ExtGState '{0:s}' is unknown", name); return Object(objNull); } //------------------------------------------------------------------------ // Gfx //------------------------------------------------------------------------ Gfx::Gfx(PDFDoc *docA, OutputDev *outA, int pageNum, Dict *resDict, double hDPI, double vDPI, const PDFRectangle *box, const PDFRectangle *cropBox, int rotate, bool (*abortCheckCbkA)(void *data), void *abortCheckCbkDataA, XRef *xrefA) : printCommands(globalParams->getPrintCommands()), profileCommands(globalParams->getProfileCommands()) { int i; doc = docA; xref = (xrefA == nullptr) ? doc->getXRef() : xrefA; catalog = doc->getCatalog(); subPage = false; mcStack = nullptr; parser = nullptr; // start the resource stack res = new GfxResources(xref, resDict, nullptr); // initialize out = outA; state = new GfxState(hDPI, vDPI, box, rotate, out->upsideDown()); out->initGfxState(state); stackHeight = 1; pushStateGuard(); fontChanged = false; clip = clipNone; ignoreUndef = 0; out->startPage(pageNum, state, xref); out->setDefaultCTM(state->getCTM()); out->updateAll(state); for (i = 0; i < 6; ++i) { baseMatrix[i] = state->getCTM()[i]; } displayDepth = 0; ocState = true; parser = nullptr; abortCheckCbk = abortCheckCbkA; abortCheckCbkData = abortCheckCbkDataA; // set crop box if (cropBox) { state->moveTo(cropBox->x1, cropBox->y1); state->lineTo(cropBox->x2, cropBox->y1); state->lineTo(cropBox->x2, cropBox->y2); state->lineTo(cropBox->x1, cropBox->y2); state->closePath(); state->clip(); out->clip(state); state->clearPath(); } #ifdef USE_CMS initDisplayProfile(); #endif } Gfx::Gfx(PDFDoc *docA, OutputDev *outA, Dict *resDict, const PDFRectangle *box, const PDFRectangle *cropBox, bool (*abortCheckCbkA)(void *data), void *abortCheckCbkDataA, Gfx *gfxA) : printCommands(globalParams->getPrintCommands()), profileCommands(globalParams->getProfileCommands()) { int i; doc = docA; if (gfxA) { xref = gfxA->getXRef(); formsDrawing = gfxA->formsDrawing; charProcDrawing = gfxA->charProcDrawing; } else { xref = doc->getXRef(); } catalog = doc->getCatalog(); subPage = true; mcStack = nullptr; parser = nullptr; // start the resource stack res = new GfxResources(xref, resDict, nullptr); // initialize out = outA; double hDPI = 72; double vDPI = 72; if (gfxA) { hDPI = gfxA->getState()->getHDPI(); vDPI = gfxA->getState()->getVDPI(); } state = new GfxState(hDPI, vDPI, box, 0, false); stackHeight = 1; pushStateGuard(); fontChanged = false; clip = clipNone; ignoreUndef = 0; for (i = 0; i < 6; ++i) { baseMatrix[i] = state->getCTM()[i]; } displayDepth = 0; ocState = true; parser = nullptr; abortCheckCbk = abortCheckCbkA; abortCheckCbkData = abortCheckCbkDataA; // set crop box if (cropBox) { state->moveTo(cropBox->x1, cropBox->y1); state->lineTo(cropBox->x2, cropBox->y1); state->lineTo(cropBox->x2, cropBox->y2); state->lineTo(cropBox->x1, cropBox->y2); state->closePath(); state->clip(); out->clip(state); state->clearPath(); } #ifdef USE_CMS initDisplayProfile(); #endif } #ifdef USE_CMS # include void Gfx::initDisplayProfile() { Object catDict = xref->getCatalog(); if (catDict.isDict()) { Object outputIntents = catDict.dictLookup("OutputIntents"); if (outputIntents.isArray() && outputIntents.arrayGetLength() == 1) { Object firstElement = outputIntents.arrayGet(0); if (firstElement.isDict()) { Object profile = firstElement.dictLookup("DestOutputProfile"); if (profile.isStream()) { Stream *iccStream = profile.getStream(); const std::vector profBuf = iccStream->toUnsignedChars(65536, 65536); auto hp = make_GfxLCMSProfilePtr(cmsOpenProfileFromMem(profBuf.data(), profBuf.size())); if (!hp) { error(errSyntaxWarning, -1, "read ICCBased color space profile error"); } else { state->setDisplayProfile(hp); } } } } } } #endif Gfx::~Gfx() { while (stateGuards.size()) { popStateGuard(); } if (!subPage) { out->endPage(); } // There shouldn't be more saves, but pop them if there were any while (state->hasSaves()) { error(errSyntaxError, -1, "Found state under last state guard. Popping."); restoreState(); } delete state; while (res) { popResources(); } while (mcStack) { popMarkedContent(); } } void Gfx::display(Object *obj, bool topLevel) { // check for excessive recursion if (displayDepth > 100) { return; } if (obj->isArray()) { for (int i = 0; i < obj->arrayGetLength(); ++i) { Object obj2 = obj->arrayGet(i); if (!obj2.isStream()) { error(errSyntaxError, -1, "Weird page contents"); return; } } } else if (!obj->isStream()) { error(errSyntaxError, -1, "Weird page contents"); return; } parser = new Parser(xref, obj, false); go(topLevel); delete parser; parser = nullptr; } void Gfx::go(bool topLevel) { Object obj; Object args[maxArgs]; int numArgs, i; int lastAbortCheck; // scan a sequence of objects pushStateGuard(); updateLevel = 1; // make sure even empty pages trigger a call to dump() lastAbortCheck = 0; numArgs = 0; obj = parser->getObj(); while (!obj.isEOF()) { commandAborted = false; // got a command - execute it if (obj.isCmd()) { if (printCommands) { obj.print(stdout); for (i = 0; i < numArgs; ++i) { printf(" "); args[i].print(stdout); } printf("\n"); fflush(stdout); } GooTimer *timer = nullptr; if (unlikely(profileCommands)) { timer = new GooTimer(); } // Run the operation execOp(&obj, args, numArgs); // Update the profile information if (unlikely(profileCommands)) { if (auto *const hash = out->getProfileHash()) { auto &data = (*hash)[obj.getCmd()]; data.addElement(timer->getElapsed()); } delete timer; } for (i = 0; i < numArgs; ++i) { args[i].setToNull(); // Free memory early } numArgs = 0; // periodically update display if (++updateLevel >= 20000) { out->dump(); updateLevel = 0; lastAbortCheck = 0; } // did the command throw an exception if (commandAborted) { // don't propogate; recursive drawing comes from Form XObjects which // should probably be drawn in a separate context anyway for caching commandAborted = false; break; } // check for an abort if (abortCheckCbk) { if (updateLevel - lastAbortCheck > 10) { if ((*abortCheckCbk)(abortCheckCbkData)) { break; } lastAbortCheck = updateLevel; } } // got an argument - save it } else if (numArgs < maxArgs) { args[numArgs++] = std::move(obj); // too many arguments - something is wrong } else { error(errSyntaxError, getPos(), "Too many args in content stream"); if (printCommands) { printf("throwing away arg: "); obj.print(stdout); printf("\n"); fflush(stdout); } } // grab the next object obj = parser->getObj(); } // args at end with no command if (numArgs > 0) { error(errSyntaxError, getPos(), "Leftover args in content stream"); if (printCommands) { printf("%d leftovers:", numArgs); for (i = 0; i < numArgs; ++i) { printf(" "); args[i].print(stdout); } printf("\n"); fflush(stdout); } } popStateGuard(); // update display if (topLevel && updateLevel > 0) { out->dump(); } } void Gfx::execOp(Object *cmd, Object args[], int numArgs) { const Operator *op; Object *argPtr; int i; // find operator const char *name = cmd->getCmd(); if (!(op = findOp(name))) { if (ignoreUndef == 0) { error(errSyntaxError, getPos(), "Unknown operator '{0:s}'", name); } return; } // type check args argPtr = args; if (op->numArgs >= 0) { if (numArgs < op->numArgs) { error(errSyntaxError, getPos(), "Too few ({0:d}) args to '{1:s}' operator", numArgs, name); commandAborted = true; return; } if (numArgs > op->numArgs) { #if 0 error(errSyntaxWarning, getPos(), "Too many ({0:d}) args to '{1:s}' operator", numArgs, name); #endif argPtr += numArgs - op->numArgs; numArgs = op->numArgs; } } else { if (numArgs > -op->numArgs) { error(errSyntaxError, getPos(), "Too many ({0:d}) args to '{1:s}' operator", numArgs, name); return; } } for (i = 0; i < numArgs; ++i) { if (!checkArg(&argPtr[i], op->tchk[i])) { error(errSyntaxError, getPos(), "Arg #{0:d} to '{1:s}' operator is wrong type ({2:s})", i, name, argPtr[i].getTypeName()); return; } } // do it (this->*op->func)(argPtr, numArgs); } const Operator *Gfx::findOp(const char *name) { int a, b, m, cmp; a = -1; b = numOps; cmp = 0; // make gcc happy // invariant: opTab[a] < name < opTab[b] while (b - a > 1) { m = (a + b) / 2; cmp = strcmp(opTab[m].name, name); if (cmp < 0) { a = m; } else if (cmp > 0) { b = m; } else { a = b = m; } } if (cmp != 0) { return nullptr; } return &opTab[a]; } bool Gfx::checkArg(Object *arg, TchkType type) { switch (type) { case tchkBool: return arg->isBool(); case tchkInt: return arg->isInt(); case tchkNum: return arg->isNum(); case tchkString: return arg->isString(); case tchkName: return arg->isName(); case tchkArray: return arg->isArray(); case tchkProps: return arg->isDict() || arg->isName(); case tchkSCN: return arg->isNum() || arg->isName(); case tchkNone: return false; } return false; } Goffset Gfx::getPos() { return parser ? parser->getPos() : -1; } //------------------------------------------------------------------------ // graphics state operators //------------------------------------------------------------------------ void Gfx::opSave(Object args[], int numArgs) { saveState(); } void Gfx::opRestore(Object args[], int numArgs) { restoreState(); } void Gfx::opConcat(Object args[], int numArgs) { state->concatCTM(args[0].getNum(), args[1].getNum(), args[2].getNum(), args[3].getNum(), args[4].getNum(), args[5].getNum()); out->updateCTM(state, args[0].getNum(), args[1].getNum(), args[2].getNum(), args[3].getNum(), args[4].getNum(), args[5].getNum()); fontChanged = true; } void Gfx::opSetDash(Object args[], int numArgs) { const Array *a = args[0].getArray(); int length = a->getLength(); std::vector dash(length); for (int i = 0; i < length; ++i) { dash[i] = a->get(i).getNumWithDefaultValue(0); } state->setLineDash(std::move(dash), args[1].getNum()); out->updateLineDash(state); } void Gfx::opSetFlat(Object args[], int numArgs) { state->setFlatness((int)args[0].getNum()); out->updateFlatness(state); } void Gfx::opSetLineJoin(Object args[], int numArgs) { state->setLineJoin(args[0].getInt()); out->updateLineJoin(state); } void Gfx::opSetLineCap(Object args[], int numArgs) { state->setLineCap(args[0].getInt()); out->updateLineCap(state); } void Gfx::opSetMiterLimit(Object args[], int numArgs) { state->setMiterLimit(args[0].getNum()); out->updateMiterLimit(state); } void Gfx::opSetLineWidth(Object args[], int numArgs) { state->setLineWidth(args[0].getNum()); out->updateLineWidth(state); } void Gfx::opSetExtGState(Object args[], int numArgs) { Object obj1, obj2; GfxBlendMode mode; bool haveFillOP; GfxColor backdropColor; bool haveBackdropColor; bool alpha; double opac; obj1 = res->lookupGState(args[0].getName()); if (obj1.isNull()) { return; } if (!obj1.isDict()) { error(errSyntaxError, getPos(), "ExtGState '{0:s}' is wrong type", args[0].getName()); return; } if (printCommands) { printf(" gfx state dict: "); obj1.print(); printf("\n"); } // parameters that are also set by individual PDF operators obj2 = obj1.dictLookup("LW"); if (obj2.isNum()) { opSetLineWidth(&obj2, 1); } obj2 = obj1.dictLookup("LC"); if (obj2.isInt()) { opSetLineCap(&obj2, 1); } obj2 = obj1.dictLookup("LJ"); if (obj2.isInt()) { opSetLineJoin(&obj2, 1); } obj2 = obj1.dictLookup("ML"); if (obj2.isNum()) { opSetMiterLimit(&obj2, 1); } obj2 = obj1.dictLookup("D"); if (obj2.isArray() && obj2.arrayGetLength() == 2) { Object args2[2]; args2[0] = obj2.arrayGet(0); args2[1] = obj2.arrayGet(1); if (args2[0].isArray() && args2[1].isNum()) { opSetDash(args2, 2); } } #if 0 //~ need to add a new version of GfxResources::lookupFont() that //~ takes an indirect ref instead of a name if (obj1.dictLookup("Font", &obj2)->isArray() && obj2.arrayGetLength() == 2) { obj2.arrayGet(0, &args2[0]); obj2.arrayGet(1, &args2[1]); if (args2[0].isDict() && args2[1].isNum()) { opSetFont(args2, 2); } args2[0].free(); args2[1].free(); } obj2.free(); #endif obj2 = obj1.dictLookup("FL"); if (obj2.isNum()) { opSetFlat(&obj2, 1); } // transparency support: blend mode, fill/stroke opacity obj2 = obj1.dictLookup("BM"); if (!obj2.isNull()) { if (state->parseBlendMode(&obj2, &mode)) { state->setBlendMode(mode); out->updateBlendMode(state); } else { error(errSyntaxError, getPos(), "Invalid blend mode in ExtGState"); } } obj2 = obj1.dictLookup("ca"); if (obj2.isNum()) { opac = obj2.getNum(); state->setFillOpacity(opac < 0 ? 0 : opac > 1 ? 1 : opac); out->updateFillOpacity(state); } obj2 = obj1.dictLookup("CA"); if (obj2.isNum()) { opac = obj2.getNum(); state->setStrokeOpacity(opac < 0 ? 0 : opac > 1 ? 1 : opac); out->updateStrokeOpacity(state); } // fill/stroke overprint, overprint mode obj2 = obj1.dictLookup("op"); if ((haveFillOP = obj2.isBool())) { state->setFillOverprint(obj2.getBool()); out->updateFillOverprint(state); } obj2 = obj1.dictLookup("OP"); if (obj2.isBool()) { state->setStrokeOverprint(obj2.getBool()); out->updateStrokeOverprint(state); if (!haveFillOP) { state->setFillOverprint(obj2.getBool()); out->updateFillOverprint(state); } } obj2 = obj1.dictLookup("OPM"); if (obj2.isInt()) { state->setOverprintMode(obj2.getInt()); out->updateOverprintMode(state); } // stroke adjust obj2 = obj1.dictLookup("SA"); if (obj2.isBool()) { state->setStrokeAdjust(obj2.getBool()); out->updateStrokeAdjust(state); } // transfer function obj2 = obj1.dictLookup("TR2"); if (obj2.isNull()) { obj2 = obj1.dictLookup("TR"); } if (obj2.isName("Default") || obj2.isName("Identity")) { Function *funcs[4] = { nullptr, nullptr, nullptr, nullptr }; state->setTransfer(funcs); out->updateTransfer(state); } else if (obj2.isArray() && obj2.arrayGetLength() == 4) { Function *funcs[4] = { nullptr, nullptr, nullptr, nullptr }; for (int i = 0; i < 4; ++i) { Object obj3 = obj2.arrayGet(i); funcs[i] = Function::parse(&obj3); if (!funcs[i]) { break; } } if (funcs[0] && funcs[1] && funcs[2] && funcs[3]) { state->setTransfer(funcs); out->updateTransfer(state); } else { for (Function *f : funcs) { delete f; } } } else if (obj2.isName() || obj2.isDict() || obj2.isStream()) { Function *funcs[4]; if ((funcs[0] = Function::parse(&obj2))) { funcs[1] = funcs[2] = funcs[3] = nullptr; state->setTransfer(funcs); out->updateTransfer(state); } } else if (!obj2.isNull()) { error(errSyntaxError, getPos(), "Invalid transfer function in ExtGState"); } // alpha is shape obj2 = obj1.dictLookup("AIS"); if (obj2.isBool()) { state->setAlphaIsShape(obj2.getBool()); out->updateAlphaIsShape(state); } // text knockout obj2 = obj1.dictLookup("TK"); if (obj2.isBool()) { state->setTextKnockout(obj2.getBool()); out->updateTextKnockout(state); } // soft mask obj2 = obj1.dictLookup("SMask"); if (!obj2.isNull()) { if (obj2.isName("None")) { out->clearSoftMask(state); } else if (obj2.isDict()) { Object obj3 = obj2.dictLookup("S"); if (obj3.isName("Alpha")) { alpha = true; } else { // "Luminosity" alpha = false; } Function *softMaskTransferFunc = nullptr; obj3 = obj2.dictLookup("TR"); if (!obj3.isNull()) { if (obj3.isName("Default") || obj3.isName("Identity")) { // nothing } else { softMaskTransferFunc = Function::parse(&obj3); if (softMaskTransferFunc == nullptr || softMaskTransferFunc->getInputSize() != 1 || softMaskTransferFunc->getOutputSize() != 1) { error(errSyntaxError, getPos(), "Invalid transfer function in soft mask in ExtGState"); delete softMaskTransferFunc; softMaskTransferFunc = nullptr; } } } obj3 = obj2.dictLookup("BC"); if ((haveBackdropColor = obj3.isArray())) { for (int &c : backdropColor.c) { c = 0; } for (int i = 0; i < obj3.arrayGetLength() && i < gfxColorMaxComps; ++i) { Object obj4 = obj3.arrayGet(i); if (obj4.isNum()) { backdropColor.c[i] = dblToCol(obj4.getNum()); } } } obj3 = obj2.dictLookup("G"); if (obj3.isStream()) { Object obj4 = obj3.streamGetDict()->lookup("Group"); if (obj4.isDict()) { GfxColorSpace *blendingColorSpace = nullptr; Object obj5 = obj4.dictLookup("CS"); if (!obj5.isNull()) { blendingColorSpace = GfxColorSpace::parse(res, &obj5, out, state); } const bool isolated = obj4.dictLookup("I").getBoolWithDefaultValue(false); const bool knockout = obj4.dictLookup("K").getBoolWithDefaultValue(false); if (!haveBackdropColor) { if (blendingColorSpace) { blendingColorSpace->getDefaultColor(&backdropColor); } else { //~ need to get the parent or default color space (?) for (int &c : backdropColor.c) { c = 0; } } } doSoftMask(&obj3, alpha, blendingColorSpace, isolated, knockout, softMaskTransferFunc, &backdropColor); delete blendingColorSpace; } else { error(errSyntaxError, getPos(), "Invalid soft mask in ExtGState - missing group"); } } else { error(errSyntaxError, getPos(), "Invalid soft mask in ExtGState - missing group"); } delete softMaskTransferFunc; } else if (!obj2.isNull()) { error(errSyntaxError, getPos(), "Invalid soft mask in ExtGState"); } } obj2 = obj1.dictLookup("Font"); if (obj2.isArray()) { if (obj2.arrayGetLength() == 2) { const Object &fargs0 = obj2.arrayGetNF(0); Object fargs1 = obj2.arrayGet(1); if (fargs0.isRef() && fargs1.isNum()) { Object fobj = fargs0.fetch(xref); if (fobj.isDict()) { Ref r = fargs0.getRef(); std::shared_ptr font = GfxFont::makeFont(xref, args[0].getName(), r, fobj.getDict()); state->setFont(font, fargs1.getNum()); fontChanged = true; } } } else { error(errSyntaxError, getPos(), "Number of args mismatch for /Font in ExtGState"); } } obj2 = obj1.dictLookup("LW"); if (obj2.isNum()) { opSetLineWidth(&obj2, 1); } obj2 = obj1.dictLookup("LC"); if (obj2.isInt()) { opSetLineCap(&obj2, 1); } obj2 = obj1.dictLookup("LJ"); if (obj2.isInt()) { opSetLineJoin(&obj2, 1); } obj2 = obj1.dictLookup("ML"); if (obj2.isNum()) { opSetMiterLimit(&obj2, 1); } obj2 = obj1.dictLookup("D"); if (obj2.isArray()) { if (obj2.arrayGetLength() == 2) { Object dargs[2]; dargs[0] = obj2.arrayGetNF(0).copy(); dargs[1] = obj2.arrayGet(1); if (dargs[0].isArray() && dargs[1].isInt()) { opSetDash(dargs, 2); } } else { error(errSyntaxError, getPos(), "Number of args mismatch for /D in ExtGState"); } } obj2 = obj1.dictLookup("RI"); if (obj2.isName()) { opSetRenderingIntent(&obj2, 1); } obj2 = obj1.dictLookup("FL"); if (obj2.isNum()) { opSetFlat(&obj2, 1); } } void Gfx::doSoftMask(Object *str, bool alpha, GfxColorSpace *blendingColorSpace, bool isolated, bool knockout, Function *transferFunc, GfxColor *backdropColor) { Dict *dict, *resDict; double m[6], bbox[4]; Object obj1; int i; // get stream dict dict = str->streamGetDict(); // check form type obj1 = dict->lookup("FormType"); if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) { error(errSyntaxError, getPos(), "Unknown form type"); } // get bounding box obj1 = dict->lookup("BBox"); if (!obj1.isArray()) { error(errSyntaxError, getPos(), "Bad form bounding box"); return; } for (i = 0; i < 4; ++i) { Object obj2 = obj1.arrayGet(i); if (likely(obj2.isNum())) { bbox[i] = obj2.getNum(); } else { error(errSyntaxError, getPos(), "Bad form bounding box (non number)"); return; } } // get matrix obj1 = dict->lookup("Matrix"); if (obj1.isArray()) { for (i = 0; i < 6; ++i) { Object obj2 = obj1.arrayGet(i); if (likely(obj2.isNum())) { m[i] = obj2.getNum(); } else { m[i] = 0; } } } else { m[0] = 1; m[1] = 0; m[2] = 0; m[3] = 1; m[4] = 0; m[5] = 0; } // get resources obj1 = dict->lookup("Resources"); resDict = obj1.isDict() ? obj1.getDict() : nullptr; // draw it drawForm(str, resDict, m, bbox, true, true, blendingColorSpace, isolated, knockout, alpha, transferFunc, backdropColor); } void Gfx::opSetRenderingIntent(Object args[], int numArgs) { state->setRenderingIntent(args[0].getName()); } //------------------------------------------------------------------------ // color operators //------------------------------------------------------------------------ void Gfx::opSetFillGray(Object args[], int numArgs) { GfxColor color; GfxColorSpace *colorSpace = nullptr; 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; colorSpace = state->copyDefaultGrayColorSpace(); } state->setFillColorSpace(colorSpace); out->updateFillColorSpace(state); color.c[0] = dblToCol(args[0].getNum()); state->setFillColor(&color); out->updateFillColor(state); } void Gfx::opSetStrokeGray(Object args[], int numArgs) { GfxColor color; GfxColorSpace *colorSpace = nullptr; state->setStrokePattern(nullptr); Object obj = res->lookupColorSpace("DefaultGray"); if (!obj.isNull()) { colorSpace = GfxColorSpace::parse(res, &obj, out, state); } if (colorSpace == nullptr) { colorSpace = state->copyDefaultGrayColorSpace(); } state->setStrokeColorSpace(colorSpace); out->updateStrokeColorSpace(state); color.c[0] = dblToCol(args[0].getNum()); state->setStrokeColor(&color); out->updateStrokeColor(state); } void Gfx::opSetFillCMYKColor(Object args[], int numArgs) { GfxColor color; GfxColorSpace *colorSpace = nullptr; int i; Object obj = res->lookupColorSpace("DefaultCMYK"); if (!obj.isNull()) { colorSpace = GfxColorSpace::parse(res, &obj, out, state); } if (colorSpace == nullptr) { colorSpace = state->copyDefaultCMYKColorSpace(); } state->setFillPattern(nullptr); state->setFillColorSpace(colorSpace); out->updateFillColorSpace(state); for (i = 0; i < 4; ++i) { color.c[i] = dblToCol(args[i].getNum()); } state->setFillColor(&color); out->updateFillColor(state); } void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) { GfxColor color; GfxColorSpace *colorSpace = nullptr; int i; state->setStrokePattern(nullptr); Object obj = res->lookupColorSpace("DefaultCMYK"); if (!obj.isNull()) { colorSpace = GfxColorSpace::parse(res, &obj, out, state); } if (colorSpace == nullptr) { colorSpace = state->copyDefaultCMYKColorSpace(); } state->setStrokeColorSpace(colorSpace); out->updateStrokeColorSpace(state); for (i = 0; i < 4; ++i) { color.c[i] = dblToCol(args[i].getNum()); } state->setStrokeColor(&color); out->updateStrokeColor(state); } void Gfx::opSetFillRGBColor(Object args[], int numArgs) { GfxColorSpace *colorSpace = nullptr; GfxColor color; int i; state->setFillPattern(nullptr); Object obj = res->lookupColorSpace("DefaultRGB"); if (!obj.isNull()) { colorSpace = GfxColorSpace::parse(res, &obj, out, state); } if (colorSpace == nullptr || colorSpace->getNComps() > 3) { delete colorSpace; colorSpace = state->copyDefaultRGBColorSpace(); } state->setFillColorSpace(colorSpace); out->updateFillColorSpace(state); for (i = 0; i < 3; ++i) { color.c[i] = dblToCol(args[i].getNum()); } state->setFillColor(&color); out->updateFillColor(state); } void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) { GfxColorSpace *colorSpace = nullptr; GfxColor color; int i; state->setStrokePattern(nullptr); Object obj = res->lookupColorSpace("DefaultRGB"); if (!obj.isNull()) { colorSpace = GfxColorSpace::parse(res, &obj, out, state); } if (colorSpace == nullptr) { colorSpace = state->copyDefaultRGBColorSpace(); } state->setStrokeColorSpace(colorSpace); out->updateStrokeColorSpace(state); for (i = 0; i < 3; ++i) { color.c[i] = dblToCol(args[i].getNum()); } state->setStrokeColor(&color); out->updateStrokeColor(state); } void Gfx::opSetFillColorSpace(Object args[], int numArgs) { GfxColorSpace *colorSpace; GfxColor color; Object obj = res->lookupColorSpace(args[0].getName()); if (obj.isNull()) { colorSpace = GfxColorSpace::parse(res, &args[0], out, state); } else { colorSpace = GfxColorSpace::parse(res, &obj, out, state); } if (colorSpace) { state->setFillPattern(nullptr); state->setFillColorSpace(colorSpace); out->updateFillColorSpace(state); colorSpace->getDefaultColor(&color); state->setFillColor(&color); out->updateFillColor(state); } else { error(errSyntaxError, getPos(), "Bad color space (fill)"); } } void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) { GfxColorSpace *colorSpace; GfxColor color; state->setStrokePattern(nullptr); Object obj = res->lookupColorSpace(args[0].getName()); if (obj.isNull()) { colorSpace = GfxColorSpace::parse(res, &args[0], out, state); } else { colorSpace = GfxColorSpace::parse(res, &obj, out, state); } if (colorSpace) { state->setStrokeColorSpace(colorSpace); out->updateStrokeColorSpace(state); colorSpace->getDefaultColor(&color); state->setStrokeColor(&color); out->updateStrokeColor(state); } else { error(errSyntaxError, getPos(), "Bad color space (stroke)"); } } void Gfx::opSetFillColor(Object args[], int numArgs) { GfxColor color; int i; if (numArgs != state->getFillColorSpace()->getNComps()) { error(errSyntaxError, getPos(), "Incorrect number of arguments in 'sc' command"); return; } state->setFillPattern(nullptr); for (i = 0; i < numArgs; ++i) { color.c[i] = dblToCol(args[i].getNum()); } state->setFillColor(&color); out->updateFillColor(state); } void Gfx::opSetStrokeColor(Object args[], int numArgs) { GfxColor color; int i; if (numArgs != state->getStrokeColorSpace()->getNComps()) { error(errSyntaxError, getPos(), "Incorrect number of arguments in 'SC' command"); return; } state->setStrokePattern(nullptr); for (i = 0; i < numArgs; ++i) { color.c[i] = dblToCol(args[i].getNum()); } state->setStrokeColor(&color); out->updateStrokeColor(state); } void Gfx::opSetFillColorN(Object args[], int numArgs) { GfxColor color; GfxPattern *pattern; int i; if (state->getFillColorSpace()->getMode() == csPattern) { if (numArgs > 1) { if (!((GfxPatternColorSpace *)state->getFillColorSpace())->getUnder() || numArgs - 1 != ((GfxPatternColorSpace *)state->getFillColorSpace())->getUnder()->getNComps()) { error(errSyntaxError, getPos(), "Incorrect number of arguments in 'scn' command"); return; } for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) { if (args[i].isNum()) { color.c[i] = dblToCol(args[i].getNum()); } else { color.c[i] = 0; // TODO Investigate if this is what Adobe does } } state->setFillColor(&color); out->updateFillColor(state); } if (numArgs > 0) { if (args[numArgs - 1].isName() && (pattern = res->lookupPattern(args[numArgs - 1].getName(), out, state))) { state->setFillPattern(pattern); } } } else { if (numArgs != state->getFillColorSpace()->getNComps()) { error(errSyntaxError, getPos(), "Incorrect number of arguments in 'scn' command"); return; } state->setFillPattern(nullptr); for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) { if (args[i].isNum()) { color.c[i] = dblToCol(args[i].getNum()); } else { color.c[i] = 0; // TODO Investigate if this is what Adobe does } } state->setFillColor(&color); out->updateFillColor(state); } } void Gfx::opSetStrokeColorN(Object args[], int numArgs) { GfxColor color; GfxPattern *pattern; int i; if (state->getStrokeColorSpace()->getMode() == csPattern) { if (numArgs > 1) { if (!((GfxPatternColorSpace *)state->getStrokeColorSpace())->getUnder() || numArgs - 1 != ((GfxPatternColorSpace *)state->getStrokeColorSpace())->getUnder()->getNComps()) { error(errSyntaxError, getPos(), "Incorrect number of arguments in 'SCN' command"); return; } for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) { if (args[i].isNum()) { color.c[i] = dblToCol(args[i].getNum()); } else { color.c[i] = 0; // TODO Investigate if this is what Adobe does } } state->setStrokeColor(&color); out->updateStrokeColor(state); } if (unlikely(numArgs <= 0)) { error(errSyntaxError, getPos(), "Incorrect number of arguments in 'SCN' command"); return; } if (args[numArgs - 1].isName() && (pattern = res->lookupPattern(args[numArgs - 1].getName(), out, state))) { state->setStrokePattern(pattern); } } else { if (numArgs != state->getStrokeColorSpace()->getNComps()) { error(errSyntaxError, getPos(), "Incorrect number of arguments in 'SCN' command"); return; } state->setStrokePattern(nullptr); for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) { if (args[i].isNum()) { color.c[i] = dblToCol(args[i].getNum()); } else { color.c[i] = 0; // TODO Investigate if this is what Adobe does } } state->setStrokeColor(&color); out->updateStrokeColor(state); } } //------------------------------------------------------------------------ // path segment operators //------------------------------------------------------------------------ void Gfx::opMoveTo(Object args[], int numArgs) { state->moveTo(args[0].getNum(), args[1].getNum()); } void Gfx::opLineTo(Object args[], int numArgs) { if (!state->isCurPt()) { error(errSyntaxError, getPos(), "No current point in lineto"); return; } state->lineTo(args[0].getNum(), args[1].getNum()); } void Gfx::opCurveTo(Object args[], int numArgs) { double x1, y1, x2, y2, x3, y3; if (!state->isCurPt()) { error(errSyntaxError, getPos(), "No current point in curveto"); return; } x1 = args[0].getNum(); y1 = args[1].getNum(); x2 = args[2].getNum(); y2 = args[3].getNum(); x3 = args[4].getNum(); y3 = args[5].getNum(); state->curveTo(x1, y1, x2, y2, x3, y3); } void Gfx::opCurveTo1(Object args[], int numArgs) { double x1, y1, x2, y2, x3, y3; if (!state->isCurPt()) { error(errSyntaxError, getPos(), "No current point in curveto1"); return; } x1 = state->getCurX(); y1 = state->getCurY(); x2 = args[0].getNum(); y2 = args[1].getNum(); x3 = args[2].getNum(); y3 = args[3].getNum(); state->curveTo(x1, y1, x2, y2, x3, y3); } void Gfx::opCurveTo2(Object args[], int numArgs) { double x1, y1, x2, y2, x3, y3; if (!state->isCurPt()) { error(errSyntaxError, getPos(), "No current point in curveto2"); return; } x1 = args[0].getNum(); y1 = args[1].getNum(); x2 = args[2].getNum(); y2 = args[3].getNum(); x3 = x2; y3 = y2; state->curveTo(x1, y1, x2, y2, x3, y3); } void Gfx::opRectangle(Object args[], int numArgs) { double x, y, w, h; x = args[0].getNum(); y = args[1].getNum(); w = args[2].getNum(); h = args[3].getNum(); state->moveTo(x, y); state->lineTo(x + w, y); state->lineTo(x + w, y + h); state->lineTo(x, y + h); state->closePath(); } void Gfx::opClosePath(Object args[], int numArgs) { if (!state->isCurPt()) { error(errSyntaxError, getPos(), "No current point in closepath"); return; } state->closePath(); } //------------------------------------------------------------------------ // path painting operators //------------------------------------------------------------------------ void Gfx::opEndPath(Object args[], int numArgs) { doEndPath(); } void Gfx::opStroke(Object args[], int numArgs) { if (!state->isCurPt()) { // error(errSyntaxError, getPos(), "No path in stroke"); return; } if (state->isPath()) { if (ocState) { if (state->getStrokeColorSpace()->getMode() == csPattern) { doPatternStroke(); } else { out->stroke(state); } } } doEndPath(); } void Gfx::opCloseStroke(Object * /*args[]*/, int /*numArgs*/) { if (!state->isCurPt()) { // error(errSyntaxError, getPos(), "No path in closepath/stroke"); return; } if (state->isPath()) { state->closePath(); if (ocState) { if (state->getStrokeColorSpace()->getMode() == csPattern) { doPatternStroke(); } else { out->stroke(state); } } } doEndPath(); } void Gfx::opFill(Object args[], int numArgs) { if (!state->isCurPt()) { // error(errSyntaxError, getPos(), "No path in fill"); return; } if (state->isPath()) { if (ocState) { if (state->getFillColorSpace()->getMode() == csPattern) { doPatternFill(false); } else { out->fill(state); } } } doEndPath(); } void Gfx::opEOFill(Object args[], int numArgs) { if (!state->isCurPt()) { // error(errSyntaxError, getPos(), "No path in eofill"); return; } if (state->isPath()) { if (ocState) { if (state->getFillColorSpace()->getMode() == csPattern) { doPatternFill(true); } else { out->eoFill(state); } } } doEndPath(); } void Gfx::opFillStroke(Object args[], int numArgs) { if (!state->isCurPt()) { // error(errSyntaxError, getPos(), "No path in fill/stroke"); return; } if (state->isPath()) { if (ocState) { if (state->getFillColorSpace()->getMode() == csPattern) { doPatternFill(false); } else { out->fill(state); } if (state->getStrokeColorSpace()->getMode() == csPattern) { doPatternStroke(); } else { out->stroke(state); } } } doEndPath(); } void Gfx::opCloseFillStroke(Object args[], int numArgs) { if (!state->isCurPt()) { // error(errSyntaxError, getPos(), "No path in closepath/fill/stroke"); return; } if (state->isPath()) { state->closePath(); if (ocState) { if (state->getFillColorSpace()->getMode() == csPattern) { doPatternFill(false); } else { out->fill(state); } if (state->getStrokeColorSpace()->getMode() == csPattern) { doPatternStroke(); } else { out->stroke(state); } } } doEndPath(); } void Gfx::opEOFillStroke(Object args[], int numArgs) { if (!state->isCurPt()) { // error(errSyntaxError, getPos(), "No path in eofill/stroke"); return; } if (state->isPath()) { if (ocState) { if (state->getFillColorSpace()->getMode() == csPattern) { doPatternFill(true); } else { out->eoFill(state); } if (state->getStrokeColorSpace()->getMode() == csPattern) { doPatternStroke(); } else { out->stroke(state); } } } doEndPath(); } void Gfx::opCloseEOFillStroke(Object args[], int numArgs) { if (!state->isCurPt()) { // error(errSyntaxError, getPos(), "No path in closepath/eofill/stroke"); return; } if (state->isPath()) { state->closePath(); if (ocState) { if (state->getFillColorSpace()->getMode() == csPattern) { doPatternFill(true); } else { out->eoFill(state); } if (state->getStrokeColorSpace()->getMode() == csPattern) { doPatternStroke(); } else { out->stroke(state); } } } doEndPath(); } void Gfx::doPatternFill(bool eoFill) { GfxPattern *pattern; // this is a bit of a kludge -- patterns can be really slow, so we // skip them if we're only doing text extraction, since they almost // certainly don't contain any text if (!out->needNonText()) { return; } if (!(pattern = state->getFillPattern())) { return; } switch (pattern->getType()) { case 1: doTilingPatternFill((GfxTilingPattern *)pattern, false, eoFill, false); break; case 2: doShadingPatternFill((GfxShadingPattern *)pattern, false, eoFill, false); break; default: error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in fill", pattern->getType()); break; } } void Gfx::doPatternStroke() { GfxPattern *pattern; // this is a bit of a kludge -- patterns can be really slow, so we // skip them if we're only doing text extraction, since they almost // certainly don't contain any text if (!out->needNonText()) { return; } if (!(pattern = state->getStrokePattern())) { return; } switch (pattern->getType()) { case 1: doTilingPatternFill((GfxTilingPattern *)pattern, true, false, false); break; case 2: doShadingPatternFill((GfxShadingPattern *)pattern, true, false, false); break; default: error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in stroke", pattern->getType()); break; } } void Gfx::doPatternText() { GfxPattern *pattern; // this is a bit of a kludge -- patterns can be really slow, so we // skip them if we're only doing text extraction, since they almost // certainly don't contain any text if (!out->needNonText()) { return; } if (!(pattern = state->getFillPattern())) { return; } switch (pattern->getType()) { case 1: doTilingPatternFill((GfxTilingPattern *)pattern, false, false, true); break; case 2: doShadingPatternFill((GfxShadingPattern *)pattern, false, false, true); break; default: error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in fill", pattern->getType()); break; } } void Gfx::doPatternImageMask(Object *ref, Stream *str, int width, int height, bool invert, bool inlineImg) { saveState(); out->setSoftMaskFromImageMask(state, ref, str, width, height, invert, inlineImg, baseMatrix); state->clearPath(); state->moveTo(0, 0); state->lineTo(1, 0); state->lineTo(1, 1); state->lineTo(0, 1); state->closePath(); doPatternText(); out->unsetSoftMaskFromImageMask(state, baseMatrix); restoreState(); } 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; double cxMin, cyMin, cxMax, cyMax; int xi0, yi0, xi1, yi1, xi, yi; const double *ctm, *btm, *ptm; double m[6], ictm[6], m1[6], imb[6]; double det; double xstep, ystep; int i; // get color space patCS = (GfxPatternColorSpace *)(stroke ? state->getStrokeColorSpace() : state->getFillColorSpace()); // construct a (pattern space) -> (current space) transform matrix ctm = state->getCTM(); btm = baseMatrix; ptm = tPat->getMatrix(); // iCTM = invert CTM det = ctm[0] * ctm[3] - ctm[1] * ctm[2]; if (fabs(det) < 0.000001) { error(errSyntaxError, getPos(), "Singular matrix in tiling pattern fill"); return; } det = 1 / det; ictm[0] = ctm[3] * det; ictm[1] = -ctm[1] * det; ictm[2] = -ctm[2] * det; ictm[3] = ctm[0] * det; ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det; ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det; // m1 = PTM * BTM = PTM * base transform matrix m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2]; m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3]; m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2]; m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3]; m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4]; m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5]; // m = m1 * iCTM = (PTM * BTM) * (iCTM) m[0] = m1[0] * ictm[0] + m1[1] * ictm[2]; m[1] = m1[0] * ictm[1] + m1[1] * ictm[3]; m[2] = m1[2] * ictm[0] + m1[3] * ictm[2]; m[3] = m1[2] * ictm[1] + m1[3] * ictm[3]; m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4]; m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5]; // construct a (device space) -> (pattern space) transform matrix det = m1[0] * m1[3] - m1[1] * m1[2]; det = 1 / det; if (!std::isfinite(det)) { error(errSyntaxError, getPos(), "Singular matrix in tiling pattern fill"); return; } imb[0] = m1[3] * det; imb[1] = -m1[1] * det; imb[2] = -m1[2] * det; imb[3] = m1[0] * det; imb[4] = (m1[2] * m1[5] - m1[3] * m1[4]) * det; imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det; // save current graphics state savedState = saveStateStack(); // set underlying color space (for uncolored tiling patterns); set // various other parameters (stroke color, line width) to match // Adobe's behavior state->setFillPattern(nullptr); state->setStrokePattern(nullptr); if (tPat->getPaintType() == 2 && (cs = patCS->getUnder())) { state->setFillColorSpace(cs->copy()); out->updateFillColorSpace(state); state->setStrokeColorSpace(cs->copy()); out->updateStrokeColorSpace(state); if (stroke) { state->setFillColor(state->getStrokeColor()); } else { state->setStrokeColor(state->getFillColor()); } out->updateFillColor(state); out->updateStrokeColor(state); } else { cs = new GfxDeviceGrayColorSpace(); state->setFillColorSpace(cs); cs->getDefaultColor(&color); state->setFillColor(&color); out->updateFillColorSpace(state); state->setStrokeColorSpace(new GfxDeviceGrayColorSpace()); state->setStrokeColor(&color); out->updateStrokeColorSpace(state); } if (!stroke) { state->setLineWidth(0); out->updateLineWidth(state); } // clip to current path if (stroke) { state->clipToStrokePath(); out->clipToStrokePath(state); } else if (!text) { state->clip(); if (eoFill) { out->eoClip(state); } else { out->clip(state); } } state->clearPath(); // get the clip region, check for empty state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax); if (cxMin > cxMax || cyMin > cyMax) { goto restore; } // transform clip region bbox to pattern space xMin = xMax = cxMin * imb[0] + cyMin * imb[2] + imb[4]; yMin = yMax = cxMin * imb[1] + cyMin * imb[3] + imb[5]; x1 = cxMin * imb[0] + cyMax * imb[2] + imb[4]; y1 = cxMin * imb[1] + cyMax * imb[3] + imb[5]; if (x1 < xMin) { xMin = x1; } else if (x1 > xMax) { xMax = x1; } if (y1 < yMin) { yMin = y1; } else if (y1 > yMax) { yMax = y1; } x1 = cxMax * imb[0] + cyMin * imb[2] + imb[4]; y1 = cxMax * imb[1] + cyMin * imb[3] + imb[5]; if (x1 < xMin) { xMin = x1; } else if (x1 > xMax) { xMax = x1; } if (y1 < yMin) { yMin = y1; } else if (y1 > yMax) { yMax = y1; } x1 = cxMax * imb[0] + cyMax * imb[2] + imb[4]; y1 = cxMax * imb[1] + cyMax * imb[3] + imb[5]; if (x1 < xMin) { xMin = x1; } else if (x1 > xMax) { xMax = x1; } if (y1 < yMin) { yMin = y1; } else if (y1 > yMax) { yMax = y1; } // draw the pattern //~ this should treat negative steps differently -- start at right/top //~ edge instead of left/bottom (?) xstep = fabs(tPat->getXStep()); ystep = fabs(tPat->getYStep()); if (unlikely(xstep == 0 || ystep == 0)) { goto restore; } if (tPat->getBBox()[0] < tPat->getBBox()[2]) { xi0 = (int)ceil((xMin - tPat->getBBox()[2]) / xstep); xi1 = (int)floor((xMax - tPat->getBBox()[0]) / xstep) + 1; } else { xi0 = (int)ceil((xMin - tPat->getBBox()[0]) / xstep); xi1 = (int)floor((xMax - tPat->getBBox()[2]) / xstep) + 1; } if (tPat->getBBox()[1] < tPat->getBBox()[3]) { yi0 = (int)ceil((yMin - tPat->getBBox()[3]) / ystep); yi1 = (int)floor((yMax - tPat->getBBox()[1]) / ystep) + 1; } else { yi0 = (int)ceil((yMin - tPat->getBBox()[1]) / ystep); yi1 = (int)floor((yMax - tPat->getBBox()[3]) / ystep) + 1; } for (i = 0; i < 4; ++i) { m1[i] = m[i]; } m1[4] = m[4]; m1[5] = m[5]; { bool shouldDrawPattern = true; std::set::iterator patternRefIt; const int patternRefNum = tPat->getPatternRefNum(); if (patternRefNum != -1) { if (formsDrawing.find(patternRefNum) == formsDrawing.end()) { patternRefIt = formsDrawing.insert(patternRefNum).first; } else { shouldDrawPattern = false; } } if (shouldDrawPattern) { if (out->useTilingPatternFill() && out->tilingPatternFill(state, this, catalog, tPat, m1, xi0, yi0, xi1, yi1, xstep, ystep)) { // do nothing } else { out->updatePatternOpacity(state); for (yi = yi0; yi < yi1; ++yi) { for (xi = xi0; xi < xi1; ++xi) { x = xi * xstep; y = yi * ystep; m1[4] = x * m[0] + y * m[2] + m[4]; m1[5] = x * m[1] + y * m[3] + m[5]; drawForm(tPat->getContentStream(), tPat->getResDict(), m1, tPat->getBBox()); } } out->clearPatternOpacity(state); } if (patternRefNum != -1) { formsDrawing.erase(patternRefIt); } } } // restore graphics state restore: restoreStateStack(savedState); } void Gfx::doShadingPatternFill(GfxShadingPattern *sPat, bool stroke, bool eoFill, bool text) { GfxShading *shading; GfxState *savedState; const double *ctm, *btm, *ptm; double m[6], ictm[6], m1[6]; double xMin, yMin, xMax, yMax; double det; shading = sPat->getShading(); // save current graphics state savedState = saveStateStack(); // clip to current path if (stroke) { state->clipToStrokePath(); out->clipToStrokePath(state); } else if (!text) { state->clip(); if (eoFill) { out->eoClip(state); } else { out->clip(state); } } state->clearPath(); // construct a (pattern space) -> (current space) transform matrix ctm = state->getCTM(); btm = baseMatrix; ptm = sPat->getMatrix(); // iCTM = invert CTM det = ctm[0] * ctm[3] - ctm[1] * ctm[2]; if (fabs(det) < 0.000001) { error(errSyntaxError, getPos(), "Singular matrix in shading pattern fill"); restoreStateStack(savedState); return; } det = 1 / det; ictm[0] = ctm[3] * det; ictm[1] = -ctm[1] * det; ictm[2] = -ctm[2] * det; ictm[3] = ctm[0] * det; ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det; ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det; // m1 = PTM * BTM = PTM * base transform matrix m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2]; m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3]; m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2]; m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3]; m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4]; m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5]; // m = m1 * iCTM = (PTM * BTM) * (iCTM) m[0] = m1[0] * ictm[0] + m1[1] * ictm[2]; m[1] = m1[0] * ictm[1] + m1[1] * ictm[3]; m[2] = m1[2] * ictm[0] + m1[3] * ictm[2]; m[3] = m1[2] * ictm[1] + m1[3] * ictm[3]; m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4]; m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5]; // set the new matrix state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]); out->updateCTM(state, m[0], m[1], m[2], m[3], m[4], m[5]); // clip to bbox if (shading->getHasBBox()) { shading->getBBox(&xMin, &yMin, &xMax, &yMax); state->moveTo(xMin, yMin); state->lineTo(xMax, yMin); state->lineTo(xMax, yMax); state->lineTo(xMin, yMax); state->closePath(); state->clip(); out->clip(state); state->clearPath(); } // set the color space state->setFillColorSpace(shading->getColorSpace()->copy()); out->updateFillColorSpace(state); // background color fill if (shading->getHasBackground()) { state->setFillColor(shading->getBackground()); out->updateFillColor(state); state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); state->moveTo(xMin, yMin); state->lineTo(xMax, yMin); state->lineTo(xMax, yMax); state->lineTo(xMin, yMax); state->closePath(); out->fill(state); state->clearPath(); } #if 1 //~tmp: turn off anti-aliasing temporarily bool vaa = out->getVectorAntialias(); if (vaa) { out->setVectorAntialias(false); } #endif // do shading type-specific operations switch (shading->getType()) { case 1: doFunctionShFill((GfxFunctionShading *)shading); break; case 2: doAxialShFill((GfxAxialShading *)shading); break; case 3: doRadialShFill((GfxRadialShading *)shading); break; case 4: case 5: doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading); break; case 6: case 7: doPatchMeshShFill((GfxPatchMeshShading *)shading); break; } #if 1 //~tmp: turn off anti-aliasing temporarily if (vaa) { out->setVectorAntialias(true); } #endif // restore graphics state restoreStateStack(savedState); } void Gfx::opShFill(Object args[], int numArgs) { GfxShading *shading; GfxState *savedState; double xMin, yMin, xMax, yMax; if (!ocState) { return; } if (!(shading = res->lookupShading(args[0].getName(), out, state))) { return; } // save current graphics state savedState = saveStateStack(); // clip to bbox if (shading->getHasBBox()) { shading->getBBox(&xMin, &yMin, &xMax, &yMax); state->moveTo(xMin, yMin); state->lineTo(xMax, yMin); state->lineTo(xMax, yMax); state->lineTo(xMin, yMax); state->closePath(); state->clip(); out->clip(state); state->clearPath(); } // set the color space state->setFillColorSpace(shading->getColorSpace()->copy()); out->updateFillColorSpace(state); #if 1 //~tmp: turn off anti-aliasing temporarily bool vaa = out->getVectorAntialias(); if (vaa) { out->setVectorAntialias(false); } #endif // do shading type-specific operations switch (shading->getType()) { case 1: doFunctionShFill((GfxFunctionShading *)shading); break; case 2: doAxialShFill((GfxAxialShading *)shading); break; case 3: doRadialShFill((GfxRadialShading *)shading); break; case 4: case 5: doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading); break; case 6: case 7: doPatchMeshShFill((GfxPatchMeshShading *)shading); break; } #if 1 //~tmp: turn off anti-aliasing temporarily if (vaa) { out->setVectorAntialias(true); } #endif // restore graphics state restoreStateStack(savedState); delete shading; } void Gfx::doFunctionShFill(GfxFunctionShading *shading) { double x0, y0, x1, y1; GfxColor colors[4]; if (out->useShadedFills(shading->getType()) && out->functionShadedFill(state, shading)) { return; } shading->getDomain(&x0, &y0, &x1, &y1); shading->getColor(x0, y0, &colors[0]); shading->getColor(x0, y1, &colors[1]); shading->getColor(x1, y0, &colors[2]); shading->getColor(x1, y1, &colors[3]); doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0); } void Gfx::doFunctionShFill1(GfxFunctionShading *shading, double x0, double y0, double x1, double y1, GfxColor *colors, int depth) { GfxColor fillColor; GfxColor color0M, color1M, colorM0, colorM1, colorMM; GfxColor colors2[4]; double xM, yM; int nComps, i, j; nComps = shading->getColorSpace()->getNComps(); const double *matrix = shading->getMatrix(); // compare the four corner colors for (i = 0; i < 4; ++i) { for (j = 0; j < nComps; ++j) { if (abs(colors[i].c[j] - colors[(i + 1) & 3].c[j]) > functionColorDelta) { break; } } if (j < nComps) { break; } } // center of the rectangle xM = 0.5 * (x0 + x1); yM = 0.5 * (y0 + y1); // the four corner colors are close (or we hit the recursive limit) // -- fill the rectangle; but require at least one subdivision // (depth==0) to avoid problems when the four outer corners of the // shaded region are the same color if ((i == 4 && depth > 0) || depth == functionMaxDepth) { // use the center color shading->getColor(xM, yM, &fillColor); state->setFillColor(&fillColor); out->updateFillColor(state); // fill the rectangle state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4], x0 * matrix[1] + y0 * matrix[3] + matrix[5]); state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4], x1 * matrix[1] + y0 * matrix[3] + matrix[5]); state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4], x1 * matrix[1] + y1 * matrix[3] + matrix[5]); state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4], x0 * matrix[1] + y1 * matrix[3] + matrix[5]); state->closePath(); out->fill(state); state->clearPath(); // the four corner colors are not close enough -- subdivide the // rectangle } else { // colors[0] colorM0 colors[2] // (x0,y0) (xM,y0) (x1,y0) // +----------+----------+ // | | | // | UL | UR | // color0M | colorMM | color1M // (x0,yM) +----------+----------+ (x1,yM) // | (xM,yM) | // | LL | LR | // | | | // +----------+----------+ // colors[1] colorM1 colors[3] // (x0,y1) (xM,y1) (x1,y1) shading->getColor(x0, yM, &color0M); shading->getColor(x1, yM, &color1M); shading->getColor(xM, y0, &colorM0); shading->getColor(xM, y1, &colorM1); shading->getColor(xM, yM, &colorMM); // upper-left sub-rectangle colors2[0] = colors[0]; colors2[1] = color0M; colors2[2] = colorM0; colors2[3] = colorMM; doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1); // lower-left sub-rectangle colors2[0] = color0M; colors2[1] = colors[1]; colors2[2] = colorMM; colors2[3] = colorM1; doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1); // upper-right sub-rectangle colors2[0] = colorM0; colors2[1] = colorMM; colors2[2] = colors[2]; colors2[3] = color1M; doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1); // lower-right sub-rectangle colors2[0] = colorMM; colors2[1] = colorM1; colors2[2] = color1M; colors2[3] = colors[3]; doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1); } } void Gfx::doAxialShFill(GfxAxialShading *shading) { double xMin, yMin, xMax, yMax; double x0, y0, x1, y1; double dx, dy, mul; bool dxZero, dyZero; double bboxIntersections[4]; double tMin, tMax, tx, ty; double s[4], sMin, sMax, tmp; double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1; double t0, t1, tt; double ta[axialMaxSplits + 1]; int next[axialMaxSplits + 1]; GfxColor color0 = {}, color1 = {}; int nComps; int i, j, k; bool needExtend = true; // get the clip region bbox state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); // compute min and max t values, based on the four corners of the // clip region bbox shading->getCoords(&x0, &y0, &x1, &y1); dx = x1 - x0; dy = y1 - y0; dxZero = fabs(dx) < 0.01; dyZero = fabs(dy) < 0.01; if (dxZero && dyZero) { tMin = tMax = 0; } else { mul = 1 / (dx * dx + dy * dy); bboxIntersections[0] = ((xMin - x0) * dx + (yMin - y0) * dy) * mul; 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)); tMin = bboxIntersections[0]; tMax = bboxIntersections[3]; if (tMin < 0 && !shading->getExtend0()) { tMin = 0; } if (tMax > 1 && !shading->getExtend1()) { tMax = 1; } } if (out->useShadedFills(shading->getType()) && out->axialShadedFill(state, shading, tMin, tMax)) { return; } // get the function domain t0 = shading->getDomain0(); t1 = shading->getDomain1(); // Traverse the t axis and do the shading. // // For each point (tx, ty) on the t axis, consider a line through // that point perpendicular to the t axis: // // x(s) = tx + s * -dy --> s = (x - tx) / -dy // y(s) = ty + s * dx --> s = (y - ty) / dx // // Then look at the intersection of this line with the bounding box // (xMin, yMin, xMax, yMax). In the general case, there are four // intersection points: // // s0 = (xMin - tx) / -dy // s1 = (xMax - tx) / -dy // s2 = (yMin - ty) / dx // s3 = (yMax - ty) / dx // // and we want the middle two s values. // // In the case where dx = 0, take s0 and s1; in the case where dy = // 0, take s2 and s3. // // Each filled polygon is bounded by two of these line segments // perpdendicular to the t axis. // // The t axis is bisected into smaller regions until the color // difference across a region is small enough, and then the region // is painted with a single color. // set up: require at least one split to avoid problems when the two // ends of the t axis have the same color nComps = shading->getColorSpace()->getNComps(); ta[0] = tMin; next[0] = axialMaxSplits / 2; ta[axialMaxSplits / 2] = 0.5 * (tMin + tMax); next[axialMaxSplits / 2] = axialMaxSplits; ta[axialMaxSplits] = tMax; // compute the color at t = tMin if (tMin < 0) { tt = t0; } else if (tMin > 1) { tt = t1; } else { tt = t0 + (t1 - t0) * tMin; } shading->getColor(tt, &color0); if (out->useFillColorStop()) { // make sure we add stop color when t = tMin state->setFillColor(&color0); out->updateFillColorStop(state, 0); } // compute the coordinates of the point on the t axis at t = tMin; // then compute the intersection of the perpendicular line with the // bounding box tx = x0 + tMin * dx; ty = y0 + tMin * dy; if (dxZero && dyZero) { sMin = sMax = 0; } else if (dxZero) { sMin = (xMin - tx) / -dy; sMax = (xMax - tx) / -dy; if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; } } else if (dyZero) { sMin = (yMin - ty) / dx; sMax = (yMax - ty) / dx; if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; } } else { s[0] = (yMin - ty) / dx; s[1] = (yMax - ty) / dx; s[2] = (xMin - tx) / -dy; s[3] = (xMax - tx) / -dy; std::sort(std::begin(s), std::end(s)); sMin = s[1]; sMax = s[2]; } ux0 = tx - sMin * dy; uy0 = ty + sMin * dx; vx0 = tx - sMax * dy; vy0 = ty + sMax * dx; i = 0; bool doneBBox1, doneBBox2; if (dxZero && dyZero) { doneBBox1 = doneBBox2 = true; } else { doneBBox1 = bboxIntersections[1] < tMin; doneBBox2 = bboxIntersections[2] > tMax; } // If output device doesn't support the extended mode required // we have to do it here needExtend = !out->axialShadedSupportExtend(state, shading); while (i < axialMaxSplits) { // bisect until color difference is small enough or we hit the // bisection limit const double previousStop = tt; j = next[i]; while (j > i + 1) { if (ta[j] < 0) { tt = t0; } else if (ta[j] > 1) { tt = t1; } else { tt = t0 + (t1 - t0) * ta[j]; } // Try to determine whether the color map is constant between ta[i] and ta[j]. // In the strict sense this question cannot be answered by sampling alone. // We try an educated guess in form of 2 samples. // See https://gitlab.freedesktop.org/poppler/poppler/issues/938 for a file where one sample was not enough. // The first test sample at 1.0 (i.e., ta[j]) is coded separately, because we may // want to reuse the color later shading->getColor(tt, &color1); bool isPatchOfConstantColor = isSameGfxColor(color1, color0, nComps, axialColorDelta); if (isPatchOfConstantColor) { // Add more sample locations here if required for (double l : { 0.5 }) { GfxColor tmpColor; double x = previousStop + l * (tt - previousStop); shading->getColor(x, &tmpColor); if (!isSameGfxColor(tmpColor, color0, nComps, axialColorDelta)) { isPatchOfConstantColor = false; break; } } } if (isPatchOfConstantColor) { // in these two if what we guarantee is that if we are skipping lots of // positions because the colors are the same, we still create a region // with vertexs passing by bboxIntersections[1] and bboxIntersections[2] // otherwise we can have empty regions that should really be painted // like happened in bug 19896 // What we do to ensure that we pass a line through this points // is making sure use the exact bboxIntersections[] value as one of the used ta[] values if (!doneBBox1 && ta[i] < bboxIntersections[1] && ta[j] > bboxIntersections[1]) { int teoricalj = (int)((bboxIntersections[1] - tMin) * axialMaxSplits / (tMax - tMin)); if (teoricalj <= i) { teoricalj = i + 1; } if (teoricalj < j) { next[i] = teoricalj; next[teoricalj] = j; } else { teoricalj = j; } ta[teoricalj] = bboxIntersections[1]; j = teoricalj; doneBBox1 = true; } if (!doneBBox2 && ta[i] < bboxIntersections[2] && ta[j] > bboxIntersections[2]) { int teoricalj = (int)((bboxIntersections[2] - tMin) * axialMaxSplits / (tMax - tMin)); if (teoricalj <= i) { teoricalj = i + 1; } if (teoricalj < j) { next[i] = teoricalj; next[teoricalj] = j; } else { teoricalj = j; } ta[teoricalj] = bboxIntersections[2]; j = teoricalj; doneBBox2 = true; } break; } k = (i + j) / 2; ta[k] = 0.5 * (ta[i] + ta[j]); next[i] = k; next[k] = j; j = k; } // use the average of the colors of the two sides of the region for (k = 0; k < nComps; ++k) { color0.c[k] = safeAverage(color0.c[k], color1.c[k]); } // compute the coordinates of the point on the t axis; then // compute the intersection of the perpendicular line with the // bounding box tx = x0 + ta[j] * dx; ty = y0 + ta[j] * dy; if (dxZero && dyZero) { sMin = sMax = 0; } else if (dxZero) { sMin = (xMin - tx) / -dy; sMax = (xMax - tx) / -dy; if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; } } else if (dyZero) { sMin = (yMin - ty) / dx; sMax = (yMax - ty) / dx; if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; } } else { s[0] = (yMin - ty) / dx; s[1] = (yMax - ty) / dx; s[2] = (xMin - tx) / -dy; s[3] = (xMax - tx) / -dy; std::sort(std::begin(s), std::end(s)); sMin = s[1]; sMax = s[2]; } ux1 = tx - sMin * dy; uy1 = ty + sMin * dx; vx1 = tx - sMax * dy; vy1 = ty + sMax * dx; // set the color state->setFillColor(&color0); if (out->useFillColorStop()) { out->updateFillColorStop(state, (ta[j] - tMin) / (tMax - tMin)); } else { out->updateFillColor(state); } if (needExtend) { // fill the region state->moveTo(ux0, uy0); state->lineTo(vx0, vy0); state->lineTo(vx1, vy1); state->lineTo(ux1, uy1); state->closePath(); } if (!out->useFillColorStop()) { out->fill(state); state->clearPath(); } // set up for next region ux0 = ux1; uy0 = uy1; vx0 = vx1; vy0 = vy1; color0 = color1; i = next[i]; } if (out->useFillColorStop()) { if (!needExtend) { state->moveTo(xMin, yMin); state->lineTo(xMin, yMax); state->lineTo(xMax, yMax); state->lineTo(xMax, yMin); state->closePath(); } out->fill(state); state->clearPath(); } } static inline void getShadingColorRadialHelper(double t0, double t1, double t, GfxRadialShading *shading, GfxColor *color) { if (t0 < t1) { if (t < t0) { shading->getColor(t0, color); } else if (t > t1) { shading->getColor(t1, color); } else { shading->getColor(t, color); } } else { if (t > t0) { shading->getColor(t0, color); } else if (t < t1) { shading->getColor(t1, color); } else { shading->getColor(t, color); } } } void Gfx::doRadialShFill(GfxRadialShading *shading) { double xMin, yMin, xMax, yMax; double x0, y0, r0, x1, y1, r1, t0, t1; int nComps; GfxColor colorA = {}, colorB = {}, colorC = {}; double xa, ya, xb, yb, ra, rb; double ta, tb, sa, sb; double sz, xz, yz, sMin, sMax; bool enclosed; int ia, ib, k, n; double theta, alpha, angle, t; bool needExtend = true; // get the shading info shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1); t0 = shading->getDomain0(); t1 = shading->getDomain1(); nComps = shading->getColorSpace()->getNComps(); // Compute the point at which r(s) = 0; check for the enclosed // circles case; and compute the angles for the tangent lines. if (x0 == x1 && y0 == y1) { enclosed = true; theta = 0; // make gcc happy sz = 0; // make gcc happy } else if (r0 == r1) { enclosed = false; theta = 0; sz = 0; // make gcc happy } else { sz = (r1 > r0) ? -r0 / (r1 - r0) : -r1 / (r0 - r1); xz = x0 + sz * (x1 - x0); yz = y0 + sz * (y1 - y0); enclosed = (xz - x0) * (xz - x0) + (yz - y0) * (yz - y0) <= r0 * r0; const double theta_aux = sqrt((x0 - xz) * (x0 - xz) + (y0 - yz) * (y0 - yz)); if (likely(theta_aux != 0)) { theta = asin(r0 / theta_aux); } else { theta = 0; } if (r0 > r1) { theta = -theta; } } if (enclosed) { alpha = 0; } else { alpha = atan2(y1 - y0, x1 - x0); } // compute the (possibly extended) s range state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); if (enclosed) { sMin = 0; sMax = 1; } else { sMin = 1; sMax = 0; // solve for x(s) + r(s) = xMin if ((x1 + r1) - (x0 + r0) != 0) { sa = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0)); if (sa < sMin) { sMin = sa; } else if (sa > sMax) { sMax = sa; } } // solve for x(s) - r(s) = xMax if ((x1 - r1) - (x0 - r0) != 0) { sa = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0)); if (sa < sMin) { sMin = sa; } else if (sa > sMax) { sMax = sa; } } // solve for y(s) + r(s) = yMin if ((y1 + r1) - (y0 + r0) != 0) { sa = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0)); if (sa < sMin) { sMin = sa; } else if (sa > sMax) { sMax = sa; } } // solve for y(s) - r(s) = yMax if ((y1 - r1) - (y0 - r0) != 0) { sa = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0)); if (sa < sMin) { sMin = sa; } else if (sa > sMax) { sMax = sa; } } // check against sz if (r0 < r1) { if (sMin < sz) { sMin = sz; } } else if (r0 > r1) { if (sMax > sz) { sMax = sz; } } // check the 'extend' flags if (!shading->getExtend0() && sMin < 0) { sMin = 0; } if (!shading->getExtend1() && sMax > 1) { sMax = 1; } } if (out->useShadedFills(shading->getType()) && out->radialShadedFill(state, shading, sMin, sMax)) { return; } // compute the number of steps into which circles must be divided to // achieve a curve flatness of 0.1 pixel in device space for the // largest circle (note that "device space" is 72 dpi when generating // PostScript, hence the relatively small 0.1 pixel accuracy) const double *ctm = state->getCTM(); t = fabs(ctm[0]); if (fabs(ctm[1]) > t) { t = fabs(ctm[1]); } if (fabs(ctm[2]) > t) { t = fabs(ctm[2]); } if (fabs(ctm[3]) > t) { t = fabs(ctm[3]); } if (r0 > r1) { t *= r0; } else { t *= r1; } if (t < 1) { n = 3; } else { const double tmp = 1 - 0.1 / t; if (unlikely(tmp == 1)) { n = 200; } else { n = (int)(M_PI / acos(tmp)); } if (n < 3) { n = 3; } else if (n > 200) { n = 200; } } // setup for the start circle ia = 0; sa = sMin; ta = t0 + sa * (t1 - t0); xa = x0 + sa * (x1 - x0); ya = y0 + sa * (y1 - y0); ra = r0 + sa * (r1 - r0); getShadingColorRadialHelper(t0, t1, ta, shading, &colorA); needExtend = !out->radialShadedSupportExtend(state, shading); // fill the circles while (ia < radialMaxSplits) { // go as far along the t axis (toward t1) as we can, such that the // color difference is within the tolerance (radialColorDelta) -- // this uses bisection (between the current value, t, and t1), // limited to radialMaxSplits points along the t axis; require at // least one split to avoid problems when the innermost and // outermost colors are the same ib = radialMaxSplits; sb = sMax; tb = t0 + sb * (t1 - t0); getShadingColorRadialHelper(t0, t1, tb, shading, &colorB); while (ib - ia > 1) { if (isSameGfxColor(colorB, colorA, nComps, radialColorDelta)) { // The shading is not necessarily lineal so having two points with the // same color does not mean all the areas in between have the same color too int ic = ia + 1; for (; ic <= ib; ic++) { const double sc = sMin + ((double)ic / (double)radialMaxSplits) * (sMax - sMin); const double tc = t0 + sc * (t1 - t0); getShadingColorRadialHelper(t0, t1, tc, shading, &colorC); if (!isSameGfxColor(colorC, colorA, nComps, radialColorDelta)) { break; } } ib = (ic > ia + 1) ? ic - 1 : ia + 1; sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin); tb = t0 + sb * (t1 - t0); getShadingColorRadialHelper(t0, t1, tb, shading, &colorB); break; } ib = (ia + ib) / 2; sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin); tb = t0 + sb * (t1 - t0); getShadingColorRadialHelper(t0, t1, tb, shading, &colorB); } // compute center and radius of the circle xb = x0 + sb * (x1 - x0); yb = y0 + sb * (y1 - y0); rb = r0 + sb * (r1 - r0); // use the average of the colors at the two circles for (k = 0; k < nComps; ++k) { colorA.c[k] = safeAverage(colorA.c[k], colorB.c[k]); } state->setFillColor(&colorA); if (out->useFillColorStop()) { out->updateFillColorStop(state, (sa - sMin) / (sMax - sMin)); } else { out->updateFillColor(state); } if (needExtend) { if (enclosed) { // construct path for first circle (counterclockwise) state->moveTo(xa + ra, ya); for (k = 1; k < n; ++k) { angle = ((double)k / (double)n) * 2 * M_PI; state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle)); } state->closePath(); // construct and append path for second circle (clockwise) state->moveTo(xb + rb, yb); for (k = 1; k < n; ++k) { angle = -((double)k / (double)n) * 2 * M_PI; state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle)); } state->closePath(); } else { // construct the first subpath (clockwise) state->moveTo(xa + ra * cos(alpha + theta + 0.5 * M_PI), ya + ra * sin(alpha + theta + 0.5 * M_PI)); for (k = 0; k < n; ++k) { angle = alpha + theta + 0.5 * M_PI - ((double)k / (double)n) * (2 * theta + M_PI); state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle)); } for (k = 0; k < n; ++k) { angle = alpha - theta - 0.5 * M_PI + ((double)k / (double)n) * (2 * theta - M_PI); state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle)); } state->closePath(); // construct the second subpath (counterclockwise) state->moveTo(xa + ra * cos(alpha + theta + 0.5 * M_PI), ya + ra * sin(alpha + theta + 0.5 * M_PI)); for (k = 0; k < n; ++k) { angle = alpha + theta + 0.5 * M_PI + ((double)k / (double)n) * (-2 * theta + M_PI); state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle)); } for (k = 0; k < n; ++k) { angle = alpha - theta - 0.5 * M_PI + ((double)k / (double)n) * (2 * theta + M_PI); state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle)); } state->closePath(); } } if (!out->useFillColorStop()) { // fill the path out->fill(state); state->clearPath(); } // step to the next value of t ia = ib; sa = sb; ta = tb; xa = xb; ya = yb; ra = rb; colorA = colorB; } if (out->useFillColorStop()) { // make sure we add stop color when sb = sMax state->setFillColor(&colorA); out->updateFillColorStop(state, (sb - sMin) / (sMax - sMin)); // fill the path state->moveTo(xMin, yMin); state->lineTo(xMin, yMax); state->lineTo(xMax, yMax); state->lineTo(xMax, yMin); state->closePath(); out->fill(state); state->clearPath(); } if (!needExtend) { return; } if (enclosed) { // extend the smaller circle if ((shading->getExtend0() && r0 <= r1) || (shading->getExtend1() && r1 < r0)) { if (r0 <= r1) { ta = t0; ra = r0; xa = x0; ya = y0; } else { ta = t1; ra = r1; xa = x1; ya = y1; } shading->getColor(ta, &colorA); state->setFillColor(&colorA); out->updateFillColor(state); state->moveTo(xa + ra, ya); for (k = 1; k < n; ++k) { angle = ((double)k / (double)n) * 2 * M_PI; state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle)); } state->closePath(); out->fill(state); state->clearPath(); } // extend the larger circle if ((shading->getExtend0() && r0 > r1) || (shading->getExtend1() && r1 >= r0)) { if (r0 > r1) { ta = t0; ra = r0; xa = x0; ya = y0; } else { ta = t1; ra = r1; xa = x1; ya = y1; } shading->getColor(ta, &colorA); state->setFillColor(&colorA); out->updateFillColor(state); state->moveTo(xMin, yMin); state->lineTo(xMin, yMax); state->lineTo(xMax, yMax); state->lineTo(xMax, yMin); state->closePath(); state->moveTo(xa + ra, ya); for (k = 1; k < n; ++k) { angle = ((double)k / (double)n) * 2 * M_PI; state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle)); } state->closePath(); out->fill(state); state->clearPath(); } } } void Gfx::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) { double x0, y0, x1, y1, x2, y2; int i; if (out->useShadedFills(shading->getType())) { if (out->gouraudTriangleShadedFill(state, shading)) { return; } } // preallocate a path (speed improvements) state->moveTo(0., 0.); state->lineTo(1., 0.); state->lineTo(0., 1.); state->closePath(); GfxState::ReusablePathIterator *reusablePath = state->getReusablePath(); if (shading->isParameterized()) { // work with parameterized values: double color0, color1, color2; // a relative threshold: 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); } } else { // this always produces output -- even for parameterized ranges. // But it ignores the parameterized color map (the function). // // Note that using this code in for parameterized shadings might be // correct in circumstances (namely if the function is linear in the actual // triangle), but in general, it will simply be wrong. 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); } } delete reusablePath; } static inline void checkTrue(bool b, const char *message) { if (unlikely(!b)) { error(errSyntaxError, -1, message); } } void Gfx::gouraudFillTriangle(double x0, double y0, GfxColor *color0, double x1, double y1, GfxColor *color1, double x2, double y2, GfxColor *color2, int nComps, int depth, GfxState::ReusablePathIterator *path) { double x01, y01, x12, y12, x20, y20; GfxColor color01, color12, color20; int i; for (i = 0; i < nComps; ++i) { if (abs(color0->c[i] - color1->c[i]) > gouraudColorDelta || abs(color1->c[i] - color2->c[i]) > gouraudColorDelta) { break; } } if (i == nComps || depth == gouraudMaxDepth) { state->setFillColor(color0); out->updateFillColor(state); path->reset(); checkTrue(!path->isEnd(), "Path should not be at end"); path->setCoord(x0, y0); path->next(); checkTrue(!path->isEnd(), "Path should not be at end"); path->setCoord(x1, y1); path->next(); checkTrue(!path->isEnd(), "Path should not be at end"); path->setCoord(x2, y2); path->next(); checkTrue(!path->isEnd(), "Path should not be at end"); path->setCoord(x0, y0); path->next(); checkTrue(path->isEnd(), "Path should be at end"); out->fill(state); } else { x01 = 0.5 * (x0 + x1); y01 = 0.5 * (y0 + y1); x12 = 0.5 * (x1 + x2); y12 = 0.5 * (y1 + y2); x20 = 0.5 * (x2 + x0); y20 = 0.5 * (y2 + y0); for (i = 0; i < nComps; ++i) { color01.c[i] = safeAverage(color0->c[i], color1->c[i]); color12.c[i] = safeAverage(color1->c[i], color2->c[i]); color20.c[i] = safeAverage(color2->c[i], color0->c[i]); } gouraudFillTriangle(x0, y0, color0, x01, y01, &color01, x20, y20, &color20, nComps, depth + 1, path); gouraudFillTriangle(x01, y01, &color01, x1, y1, color1, x12, y12, &color12, nComps, depth + 1, path); gouraudFillTriangle(x01, y01, &color01, x12, y12, &color12, x20, y20, &color20, nComps, depth + 1, path); gouraudFillTriangle(x20, y20, &color20, x12, y12, &color12, x2, y2, color2, nComps, depth + 1, path); } } void Gfx::gouraudFillTriangle(double x0, double y0, double color0, double x1, double y1, double color1, double x2, double y2, double color2, double refineColorThreshold, int depth, GfxGouraudTriangleShading *shading, GfxState::ReusablePathIterator *path) { const double meanColor = (color0 + color1 + color2) / 3; const bool isFineEnough = fabs(color0 - meanColor) < refineColorThreshold && fabs(color1 - meanColor) < refineColorThreshold && fabs(color2 - meanColor) < refineColorThreshold; if (isFineEnough || depth == gouraudMaxDepth) { GfxColor color; shading->getParameterizedColor(meanColor, &color); state->setFillColor(&color); out->updateFillColor(state); path->reset(); checkTrue(!path->isEnd(), "Path should not be at end"); path->setCoord(x0, y0); path->next(); checkTrue(!path->isEnd(), "Path should not be at end"); path->setCoord(x1, y1); path->next(); checkTrue(!path->isEnd(), "Path should not be at end"); path->setCoord(x2, y2); path->next(); checkTrue(!path->isEnd(), "Path should not be at end"); path->setCoord(x0, y0); path->next(); checkTrue(path->isEnd(), "Path should be at end"); out->fill(state); } else { const double x01 = 0.5 * (x0 + x1); const double y01 = 0.5 * (y0 + y1); const double x12 = 0.5 * (x1 + x2); const double y12 = 0.5 * (y1 + y2); const double x20 = 0.5 * (x2 + x0); const double y20 = 0.5 * (y2 + y0); const double color01 = (color0 + color1) / 2.; const double color12 = (color1 + color2) / 2.; const double color20 = (color2 + color0) / 2.; ++depth; gouraudFillTriangle(x0, y0, color0, x01, y01, color01, x20, y20, color20, refineColorThreshold, depth, shading, path); gouraudFillTriangle(x01, y01, color01, x1, y1, color1, x12, y12, color12, refineColorThreshold, depth, shading, path); gouraudFillTriangle(x01, y01, color01, x12, y12, color12, x20, y20, color20, refineColorThreshold, depth, shading, path); gouraudFillTriangle(x20, y20, color20, x12, y12, color12, x2, y2, color2, refineColorThreshold, depth, shading, path); } } void Gfx::doPatchMeshShFill(GfxPatchMeshShading *shading) { int start, i; if (out->useShadedFills(shading->getType())) { if (out->patchMeshShadedFill(state, shading)) { return; } } if (shading->getNPatches() > 128) { start = 3; } else if (shading->getNPatches() > 64) { start = 2; } else if (shading->getNPatches() > 16) { start = 1; } else { start = 0; } /* * Parameterized shadings take one parameter [t_0,t_e] * and map it into the color space. * * Consequently, all color values are stored as doubles. * * These color values are interpreted as parameters for parameterized * shadings and as colorspace entities otherwise. * * The only difference is that color space entities are stored into * DOUBLE arrays, not into arrays of type GfxColorComp. */ const int colorComps = shading->getColorSpace()->getNComps(); double refineColorThreshold; if (shading->isParameterized()) { refineColorThreshold = gouraudParameterizedColorDelta * (shading->getParameterDomainMax() - shading->getParameterDomainMin()); } else { refineColorThreshold = patchColorDelta; } for (i = 0; i < shading->getNPatches(); ++i) { fillPatch(shading->getPatch(i), colorComps, shading->isParameterized() ? 1 : colorComps, refineColorThreshold, start, shading); } } void Gfx::fillPatch(const GfxPatch *patch, int colorComps, int patchColorComps, double refineColorThreshold, int depth, const GfxPatchMeshShading *shading) { GfxPatch patch00, patch01, patch10, patch11; double xx[4][8], yy[4][8]; double xxm, yym; int i; for (i = 0; i < patchColorComps; ++i) { // these comparisons are done in double arithmetics. // // For non-parameterized shadings, they are done in color space // components. if (fabs(patch->color[0][0].c[i] - patch->color[0][1].c[i]) > refineColorThreshold || fabs(patch->color[0][1].c[i] - patch->color[1][1].c[i]) > refineColorThreshold || fabs(patch->color[1][1].c[i] - patch->color[1][0].c[i]) > refineColorThreshold || fabs(patch->color[1][0].c[i] - patch->color[0][0].c[i]) > refineColorThreshold) { break; } } if (i == patchColorComps || depth == patchMaxDepth) { GfxColor flatColor; if (shading->isParameterized()) { shading->getParameterizedColor(patch->color[0][0].c[0], &flatColor); } else { for (i = 0; i < colorComps; ++i) { // simply cast to the desired type; that's all what is needed. flatColor.c[i] = GfxColorComp(patch->color[0][0].c[i]); } } state->setFillColor(&flatColor); out->updateFillColor(state); state->moveTo(patch->x[0][0], patch->y[0][0]); state->curveTo(patch->x[0][1], patch->y[0][1], patch->x[0][2], patch->y[0][2], patch->x[0][3], patch->y[0][3]); state->curveTo(patch->x[1][3], patch->y[1][3], patch->x[2][3], patch->y[2][3], patch->x[3][3], patch->y[3][3]); state->curveTo(patch->x[3][2], patch->y[3][2], patch->x[3][1], patch->y[3][1], patch->x[3][0], patch->y[3][0]); state->curveTo(patch->x[2][0], patch->y[2][0], patch->x[1][0], patch->y[1][0], patch->x[0][0], patch->y[0][0]); state->closePath(); out->fill(state); state->clearPath(); } else { for (i = 0; i < 4; ++i) { xx[i][0] = patch->x[i][0]; yy[i][0] = patch->y[i][0]; xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]); yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]); xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]); yym = 0.5 * (patch->y[i][1] + patch->y[i][2]); xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]); yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]); xx[i][2] = 0.5 * (xx[i][1] + xxm); yy[i][2] = 0.5 * (yy[i][1] + yym); xx[i][5] = 0.5 * (xxm + xx[i][6]); yy[i][5] = 0.5 * (yym + yy[i][6]); xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]); yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]); xx[i][7] = patch->x[i][3]; yy[i][7] = patch->y[i][3]; } for (i = 0; i < 4; ++i) { patch00.x[0][i] = xx[0][i]; patch00.y[0][i] = yy[0][i]; patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]); patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]); xxm = 0.5 * (xx[1][i] + xx[2][i]); yym = 0.5 * (yy[1][i] + yy[2][i]); patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]); patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]); patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm); patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym); patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]); patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]); patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]); patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]); patch10.x[0][i] = patch00.x[3][i]; patch10.y[0][i] = patch00.y[3][i]; patch10.x[3][i] = xx[3][i]; patch10.y[3][i] = yy[3][i]; } for (i = 4; i < 8; ++i) { patch01.x[0][i - 4] = xx[0][i]; patch01.y[0][i - 4] = yy[0][i]; patch01.x[1][i - 4] = 0.5 * (xx[0][i] + xx[1][i]); patch01.y[1][i - 4] = 0.5 * (yy[0][i] + yy[1][i]); xxm = 0.5 * (xx[1][i] + xx[2][i]); yym = 0.5 * (yy[1][i] + yy[2][i]); patch11.x[2][i - 4] = 0.5 * (xx[2][i] + xx[3][i]); patch11.y[2][i - 4] = 0.5 * (yy[2][i] + yy[3][i]); patch01.x[2][i - 4] = 0.5 * (patch01.x[1][i - 4] + xxm); patch01.y[2][i - 4] = 0.5 * (patch01.y[1][i - 4] + yym); patch11.x[1][i - 4] = 0.5 * (xxm + patch11.x[2][i - 4]); patch11.y[1][i - 4] = 0.5 * (yym + patch11.y[2][i - 4]); patch01.x[3][i - 4] = 0.5 * (patch01.x[2][i - 4] + patch11.x[1][i - 4]); patch01.y[3][i - 4] = 0.5 * (patch01.y[2][i - 4] + patch11.y[1][i - 4]); patch11.x[0][i - 4] = patch01.x[3][i - 4]; patch11.y[0][i - 4] = patch01.y[3][i - 4]; patch11.x[3][i - 4] = xx[3][i]; patch11.y[3][i - 4] = yy[3][i]; } for (i = 0; i < patchColorComps; ++i) { patch00.color[0][0].c[i] = patch->color[0][0].c[i]; patch00.color[0][1].c[i] = (patch->color[0][0].c[i] + patch->color[0][1].c[i]) / 2.; patch01.color[0][0].c[i] = patch00.color[0][1].c[i]; patch01.color[0][1].c[i] = patch->color[0][1].c[i]; patch01.color[1][1].c[i] = (patch->color[0][1].c[i] + patch->color[1][1].c[i]) / 2.; patch11.color[0][1].c[i] = patch01.color[1][1].c[i]; patch11.color[1][1].c[i] = patch->color[1][1].c[i]; patch11.color[1][0].c[i] = (patch->color[1][1].c[i] + patch->color[1][0].c[i]) / 2.; patch10.color[1][1].c[i] = patch11.color[1][0].c[i]; patch10.color[1][0].c[i] = patch->color[1][0].c[i]; patch10.color[0][0].c[i] = (patch->color[1][0].c[i] + patch->color[0][0].c[i]) / 2.; patch00.color[1][0].c[i] = patch10.color[0][0].c[i]; patch00.color[1][1].c[i] = (patch00.color[1][0].c[i] + patch01.color[1][1].c[i]) / 2.; patch01.color[1][0].c[i] = patch00.color[1][1].c[i]; patch11.color[0][0].c[i] = patch00.color[1][1].c[i]; patch10.color[0][1].c[i] = patch00.color[1][1].c[i]; } fillPatch(&patch00, colorComps, patchColorComps, refineColorThreshold, depth + 1, shading); fillPatch(&patch10, colorComps, patchColorComps, refineColorThreshold, depth + 1, shading); fillPatch(&patch01, colorComps, patchColorComps, refineColorThreshold, depth + 1, shading); fillPatch(&patch11, colorComps, patchColorComps, refineColorThreshold, depth + 1, shading); } } void Gfx::doEndPath() { if (state->isCurPt() && clip != clipNone) { state->clip(); if (clip == clipNormal) { out->clip(state); } else { out->eoClip(state); } } clip = clipNone; state->clearPath(); } //------------------------------------------------------------------------ // path clipping operators //------------------------------------------------------------------------ void Gfx::opClip(Object args[], int numArgs) { clip = clipNormal; } void Gfx::opEOClip(Object args[], int numArgs) { clip = clipEO; } //------------------------------------------------------------------------ // text object operators //------------------------------------------------------------------------ void Gfx::opBeginText(Object args[], int numArgs) { out->beginTextObject(state); state->setTextMat(1, 0, 0, 1, 0, 0); state->textMoveTo(0, 0); out->updateTextMat(state); out->updateTextPos(state); fontChanged = true; } void Gfx::opEndText(Object args[], int numArgs) { out->endTextObject(state); } //------------------------------------------------------------------------ // text state operators //------------------------------------------------------------------------ void Gfx::opSetCharSpacing(Object args[], int numArgs) { state->setCharSpace(args[0].getNum()); out->updateCharSpace(state); } void Gfx::opSetFont(Object args[], int numArgs) { std::shared_ptr font; if (!(font = res->lookupFont(args[0].getName()))) { // unsetting the font (drawing no text) is better than using the // previous one and drawing random glyphs from it state->setFont(nullptr, args[1].getNum()); fontChanged = true; return; } if (printCommands) { printf(" font: tag=%s name='%s' %g\n", font->getTag().c_str(), font->getName() ? font->getName()->c_str() : "???", args[1].getNum()); fflush(stdout); } state->setFont(font, args[1].getNum()); fontChanged = true; } void Gfx::opSetTextLeading(Object args[], int numArgs) { state->setLeading(args[0].getNum()); } void Gfx::opSetTextRender(Object args[], int numArgs) { state->setRender(args[0].getInt()); out->updateRender(state); } void Gfx::opSetTextRise(Object args[], int numArgs) { state->setRise(args[0].getNum()); out->updateRise(state); } void Gfx::opSetWordSpacing(Object args[], int numArgs) { state->setWordSpace(args[0].getNum()); out->updateWordSpace(state); } void Gfx::opSetHorizScaling(Object args[], int numArgs) { state->setHorizScaling(args[0].getNum()); out->updateHorizScaling(state); fontChanged = true; } //------------------------------------------------------------------------ // text positioning operators //------------------------------------------------------------------------ void Gfx::opTextMove(Object args[], int numArgs) { double tx, ty; tx = state->getLineX() + args[0].getNum(); ty = state->getLineY() + args[1].getNum(); state->textMoveTo(tx, ty); out->updateTextPos(state); } void Gfx::opTextMoveSet(Object args[], int numArgs) { double tx, ty; tx = state->getLineX() + args[0].getNum(); ty = args[1].getNum(); state->setLeading(-ty); ty += state->getLineY(); state->textMoveTo(tx, ty); out->updateTextPos(state); } void Gfx::opSetTextMatrix(Object args[], int numArgs) { state->setTextMat(args[0].getNum(), args[1].getNum(), args[2].getNum(), args[3].getNum(), args[4].getNum(), args[5].getNum()); state->textMoveTo(0, 0); out->updateTextMat(state); out->updateTextPos(state); fontChanged = true; } void Gfx::opTextNextLine(Object args[], int numArgs) { double tx, ty; tx = state->getLineX(); ty = state->getLineY() - state->getLeading(); state->textMoveTo(tx, ty); out->updateTextPos(state); } //------------------------------------------------------------------------ // text string operators //------------------------------------------------------------------------ void Gfx::opShowText(Object args[], int numArgs) { if (!state->getFont()) { error(errSyntaxError, getPos(), "No font in show"); return; } if (fontChanged) { out->updateFont(state); fontChanged = false; } out->beginStringOp(state); doShowText(args[0].getString()); out->endStringOp(state); if (!ocState) { doIncCharCount(args[0].getString()); } } void Gfx::opMoveShowText(Object args[], int numArgs) { double tx, ty; if (!state->getFont()) { error(errSyntaxError, getPos(), "No font in move/show"); return; } if (fontChanged) { out->updateFont(state); fontChanged = false; } tx = state->getLineX(); ty = state->getLineY() - state->getLeading(); state->textMoveTo(tx, ty); out->updateTextPos(state); out->beginStringOp(state); doShowText(args[0].getString()); out->endStringOp(state); if (!ocState) { doIncCharCount(args[0].getString()); } } void Gfx::opMoveSetShowText(Object args[], int numArgs) { double tx, ty; if (!state->getFont()) { error(errSyntaxError, getPos(), "No font in move/set/show"); return; } if (fontChanged) { out->updateFont(state); fontChanged = false; } state->setWordSpace(args[0].getNum()); state->setCharSpace(args[1].getNum()); tx = state->getLineX(); ty = state->getLineY() - state->getLeading(); state->textMoveTo(tx, ty); out->updateWordSpace(state); out->updateCharSpace(state); out->updateTextPos(state); out->beginStringOp(state); doShowText(args[2].getString()); out->endStringOp(state); if (ocState) { doIncCharCount(args[2].getString()); } } void Gfx::opShowSpaceText(Object args[], int numArgs) { Array *a; int wMode; int i; if (!state->getFont()) { error(errSyntaxError, getPos(), "No font in show/space"); return; } if (fontChanged) { out->updateFont(state); fontChanged = false; } out->beginStringOp(state); wMode = state->getFont()->getWMode(); a = args[0].getArray(); for (i = 0; i < a->getLength(); ++i) { Object obj = a->get(i); if (obj.isNum()) { // this uses the absolute value of the font size to match // Acrobat's behavior if (wMode) { state->textShift(0, -obj.getNum() * 0.001 * state->getFontSize()); } else { state->textShift(-obj.getNum() * 0.001 * state->getFontSize() * state->getHorizScaling(), 0); } out->updateTextShift(state, obj.getNum()); } else if (obj.isString()) { doShowText(obj.getString()); } else { error(errSyntaxError, getPos(), "Element of show/space array must be number or string"); } } out->endStringOp(state); if (!ocState) { a = args[0].getArray(); for (i = 0; i < a->getLength(); ++i) { Object obj = a->get(i); if (obj.isString()) { doIncCharCount(obj.getString()); } } } } void Gfx::doShowText(const GooString *s) { int wMode; double riseX, riseY; CharCode code; const Unicode *u = nullptr; double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, ddx, ddy; double originX, originY, tOriginX, tOriginY; double x0, y0, x1, y1; double tmp[4], newCTM[6]; const double *oldCTM, *mat; Dict *resDict; Parser *oldParser; GfxState *savedState; const char *p; int render; bool patternFill; int len, n, uLen, nChars, nSpaces; GfxFont *const font = state->getFont().get(); wMode = font->getWMode(); if (out->useDrawChar()) { out->beginString(state, s); } // if we're doing a pattern fill, set up clipping render = state->getRender(); if (!(render & 1) && state->getFillColorSpace()->getMode() == csPattern) { patternFill = true; saveState(); // disable fill, enable clipping, leave stroke unchanged if ((render ^ (render >> 1)) & 1) { render = 5; } else { render = 7; } state->setRender(render); out->updateRender(state); } else { patternFill = false; } state->textTransformDelta(0, state->getRise(), &riseX, &riseY); x0 = state->getCurX() + riseX; y0 = state->getCurY() + riseY; // handle a Type 3 char if (font->getType() == fontType3 && out->interpretType3Chars()) { oldCTM = state->getCTM(); mat = state->getTextMat(); tmp[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2]; tmp[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3]; tmp[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2]; tmp[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3]; mat = font->getFontMatrix(); newCTM[0] = mat[0] * tmp[0] + mat[1] * tmp[2]; newCTM[1] = mat[0] * tmp[1] + mat[1] * tmp[3]; newCTM[2] = mat[2] * tmp[0] + mat[3] * tmp[2]; newCTM[3] = mat[2] * tmp[1] + mat[3] * tmp[3]; newCTM[0] *= state->getFontSize(); newCTM[1] *= state->getFontSize(); newCTM[2] *= state->getFontSize(); newCTM[3] *= state->getFontSize(); newCTM[0] *= state->getHorizScaling(); newCTM[1] *= state->getHorizScaling(); curX = state->getCurX(); curY = state->getCurY(); oldParser = parser; p = s->c_str(); len = s->getLength(); while (len > 0) { n = font->getNextChar(p, len, &code, &u, &uLen, &dx, &dy, &originX, &originY); dx = dx * state->getFontSize() + state->getCharSpace(); if (n == 1 && *p == ' ') { dx += state->getWordSpace(); } dx *= state->getHorizScaling(); dy *= state->getFontSize(); state->textTransformDelta(dx, dy, &tdx, &tdy); state->transform(curX + riseX, curY + riseY, &x, &y); savedState = saveStateStack(); state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y); //~ the CTM concat values here are wrong (but never used) out->updateCTM(state, 1, 0, 0, 1, 0, 0); state->transformDelta(dx, dy, &ddx, &ddy); if (!out->beginType3Char(state, curX + riseX, curY + riseY, ddx, ddy, code, u, uLen)) { Object charProc = ((Gfx8BitFont *)font)->getCharProcNF(code); int refNum = -1; if (charProc.isRef()) { refNum = charProc.getRef().num; charProc = charProc.fetch(((Gfx8BitFont *)font)->getCharProcs()->getXRef()); } if ((resDict = ((Gfx8BitFont *)font)->getResources())) { pushResources(resDict); } if (charProc.isStream()) { Object charProcResourcesObj = charProc.streamGetDict()->lookup("Resources"); if (charProcResourcesObj.isDict()) { pushResources(charProcResourcesObj.getDict()); } std::set::iterator charProcDrawingIt; bool displayCharProc = true; if (refNum != -1) { if (charProcDrawing.find(refNum) == charProcDrawing.end()) { charProcDrawingIt = charProcDrawing.insert(refNum).first; } else { displayCharProc = false; error(errSyntaxError, -1, "CharProc wants to draw a CharProc that is already being drawn"); } } if (displayCharProc) { ++displayDepth; display(&charProc, false); --displayDepth; if (refNum != -1) { charProcDrawing.erase(charProcDrawingIt); } } if (charProcResourcesObj.isDict()) { popResources(); } } else { error(errSyntaxError, getPos(), "Missing or bad Type3 CharProc entry"); } out->endType3Char(state); if (resDict) { popResources(); } } restoreStateStack(savedState); // GfxState::restore() does *not* restore the current position, // so we deal with it here using (curX, curY) and (lineX, lineY) curX += tdx; curY += tdy; state->moveTo(curX, curY); // Call updateCTM with the identity transformation. That way, the CTM is unchanged, // but any side effect that the method may have is triggered. This is the case, // in particular, for the Splash backend. out->updateCTM(state, 1, 0, 0, 1, 0, 0); p += n; len -= n; } parser = oldParser; } else if (out->useDrawChar()) { p = s->c_str(); len = s->getLength(); while (len > 0) { n = font->getNextChar(p, len, &code, &u, &uLen, &dx, &dy, &originX, &originY); if (wMode) { dx *= state->getFontSize(); dy = dy * state->getFontSize() + state->getCharSpace(); if (n == 1 && *p == ' ') { dy += state->getWordSpace(); } } else { dx = dx * state->getFontSize() + state->getCharSpace(); if (n == 1 && *p == ' ') { dx += state->getWordSpace(); } dx *= state->getHorizScaling(); dy *= state->getFontSize(); } state->textTransformDelta(dx, dy, &tdx, &tdy); originX *= state->getFontSize(); originY *= state->getFontSize(); state->textTransformDelta(originX, originY, &tOriginX, &tOriginY); if (ocState) { out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY, tdx, tdy, tOriginX, tOriginY, code, n, u, uLen); } state->shift(tdx, tdy); p += n; len -= n; } } else { dx = dy = 0; p = s->c_str(); len = s->getLength(); nChars = nSpaces = 0; while (len > 0) { n = font->getNextChar(p, len, &code, &u, &uLen, &dx2, &dy2, &originX, &originY); dx += dx2; dy += dy2; if (n == 1 && *p == ' ') { ++nSpaces; } ++nChars; p += n; len -= n; } if (wMode) { dx *= state->getFontSize(); dy = dy * state->getFontSize() + nChars * state->getCharSpace() + nSpaces * state->getWordSpace(); } else { dx = dx * state->getFontSize() + nChars * state->getCharSpace() + nSpaces * state->getWordSpace(); dx *= state->getHorizScaling(); dy *= state->getFontSize(); } state->textTransformDelta(dx, dy, &tdx, &tdy); if (ocState) { out->drawString(state, s); } state->shift(tdx, tdy); } if (out->useDrawChar()) { out->endString(state); } if (patternFill && ocState) { out->saveTextPos(state); // tell the OutputDev to do the clipping out->endTextObject(state); // set up a clipping bbox so doPatternText will work -- assume // that the text bounding box does not extend past the baseline in // any direction by more than twice the font size x1 = state->getCurX() + riseX; y1 = state->getCurY() + riseY; if (x0 > x1) { x = x0; x0 = x1; x1 = x; } if (y0 > y1) { y = y0; y0 = y1; y1 = y; } state->textTransformDelta(0, state->getFontSize(), &dx, &dy); state->textTransformDelta(state->getFontSize(), 0, &dx2, &dy2); dx = fabs(dx); dx2 = fabs(dx2); if (dx2 > dx) { dx = dx2; } dy = fabs(dy); dy2 = fabs(dy2); if (dy2 > dy) { dy = dy2; } state->clipToRect(x0 - 2 * dx, y0 - 2 * dy, x1 + 2 * dx, y1 + 2 * dy); // set render mode to fill-only state->setRender(0); out->updateRender(state); doPatternText(); restoreState(); out->restoreTextPos(state); } updateLevel += 10 * s->getLength(); } // NB: this is only called when ocState is false. void Gfx::doIncCharCount(const GooString *s) { if (out->needCharCount()) { out->incCharCount(s->getLength()); } } //------------------------------------------------------------------------ // XObject operators //------------------------------------------------------------------------ void Gfx::opXObject(Object args[], int numArgs) { const char *name; if (!ocState && !out->needCharCount()) { return; } name = args[0].getName(); Object obj1 = res->lookupXObject(name); if (obj1.isNull()) { return; } if (!obj1.isStream()) { error(errSyntaxError, getPos(), "XObject '{0:s}' is wrong type", name); return; } #ifdef OPI_SUPPORT Object opiDict = obj1.streamGetDict()->lookup("OPI"); if (opiDict.isDict()) { out->opiBegin(state, opiDict.getDict()); } #endif Object obj2 = obj1.streamGetDict()->lookup("Subtype"); if (obj2.isName("Image")) { if (out->needNonText()) { Object refObj = res->lookupXObjectNF(name); doImage(&refObj, obj1.getStream(), false); } } else if (obj2.isName("Form")) { Object refObj = res->lookupXObjectNF(name); bool shouldDoForm = true; std::set::iterator drawingFormIt; if (refObj.isRef()) { const int num = refObj.getRef().num; if (formsDrawing.find(num) == formsDrawing.end()) { drawingFormIt = formsDrawing.insert(num).first; } else { shouldDoForm = false; } } if (shouldDoForm) { if (out->useDrawForm() && refObj.isRef()) { out->drawForm(refObj.getRef()); } else { Ref ref = refObj.isRef() ? refObj.getRef() : Ref::INVALID(); out->beginForm(&obj1, ref); doForm(&obj1); out->endForm(&obj1, ref); } } if (refObj.isRef() && shouldDoForm) { formsDrawing.erase(drawingFormIt); } } else if (obj2.isName("PS")) { Object obj3 = obj1.streamGetDict()->lookup("Level1"); out->psXObject(obj1.getStream(), obj3.isStream() ? obj3.getStream() : nullptr); } else if (obj2.isName()) { error(errSyntaxError, getPos(), "Unknown XObject subtype '{0:s}'", obj2.getName()); } else { error(errSyntaxError, getPos(), "XObject subtype is missing or wrong type"); } #ifdef OPI_SUPPORT if (opiDict.isDict()) { out->opiEnd(state, opiDict.getDict()); } #endif } void Gfx::doImage(Object *ref, Stream *str, bool inlineImg) { Dict *dict, *maskDict; int width, height; int bits, maskBits; bool interpolate; StreamColorSpaceMode csMode; bool mask; bool invert; GfxColorSpace *colorSpace, *maskColorSpace; bool haveColorKeyMask, haveExplicitMask, haveSoftMask; int maskColors[2 * gfxColorMaxComps] = {}; int maskWidth, maskHeight; bool maskInvert; bool maskInterpolate; Stream *maskStr; int i, n; // get info from the stream bits = 0; csMode = streamCSNone; str->getImageParams(&bits, &csMode); // get stream dict dict = str->getDict(); // check for optional content key if (ref) { const Object &objOC = dict->lookupNF("OC"); if (catalog->getOptContentConfig() && !catalog->getOptContentConfig()->optContentIsVisible(&objOC)) { return; } } // get size Object obj1 = dict->lookup("Width"); if (obj1.isNull()) { obj1 = dict->lookup("W"); } if (obj1.isInt()) { width = obj1.getInt(); } else if (obj1.isReal()) { width = (int)obj1.getReal(); } else { goto err1; } obj1 = dict->lookup("Height"); if (obj1.isNull()) { obj1 = dict->lookup("H"); } if (obj1.isInt()) { height = obj1.getInt(); } else if (obj1.isReal()) { height = (int)obj1.getReal(); } else { goto err1; } if (width < 1 || height < 1) { goto err1; } // image interpolation obj1 = dict->lookup("Interpolate"); if (obj1.isNull()) { obj1 = dict->lookup("I"); } if (obj1.isBool()) { interpolate = obj1.getBool(); } else { interpolate = false; } maskInterpolate = false; // image or mask? obj1 = dict->lookup("ImageMask"); if (obj1.isNull()) { obj1 = dict->lookup("IM"); } mask = false; if (obj1.isBool()) { mask = obj1.getBool(); } else if (!obj1.isNull()) { goto err1; } // bit depth if (bits == 0) { obj1 = dict->lookup("BitsPerComponent"); if (obj1.isNull()) { obj1 = dict->lookup("BPC"); } if (obj1.isInt()) { bits = obj1.getInt(); } else if (mask) { bits = 1; } else { goto err1; } } // display a mask if (mask) { // check for inverted mask if (bits != 1) { goto err1; } invert = false; obj1 = dict->lookup("Decode"); if (obj1.isNull()) { obj1 = dict->lookup("D"); } if (obj1.isArray()) { Object obj2; obj2 = obj1.arrayGet(0); // Table 4.39 says /Decode must be [1 0] or [0 1]. Adobe // accepts [1.0 0.0] as well. if (obj2.isNum() && obj2.getNum() >= 0.9) { invert = true; } } else if (!obj1.isNull()) { goto err1; } // if drawing is disabled, skip over inline image data if (!ocState || !out->needNonText()) { str->reset(); n = height * ((width + 7) / 8); for (i = 0; i < n; ++i) { str->getChar(); } str->close(); // draw it } else { if (state->getFillColorSpace()->getMode() == csPattern) { doPatternImageMask(ref, str, width, height, invert, inlineImg); } else { out->drawImageMask(state, ref, str, width, height, invert, interpolate, inlineImg); } } } else { if (bits == 0) { goto err1; } // get color space and color map obj1 = dict->lookup("ColorSpace"); if (obj1.isNull()) { obj1 = dict->lookup("CS"); } if (obj1.isName() && inlineImg) { Object obj2 = res->lookupColorSpace(obj1.getName()); if (!obj2.isNull()) { obj1 = std::move(obj2); } } if (!obj1.isNull()) { char *tempIntent = nullptr; Object objIntent = dict->lookup("Intent"); if (objIntent.isName()) { const char *stateIntent = state->getRenderingIntent(); if (stateIntent != nullptr) { tempIntent = strdup(stateIntent); } state->setRenderingIntent(objIntent.getName()); } colorSpace = GfxColorSpace::parse(res, &obj1, out, state); if (objIntent.isName()) { state->setRenderingIntent(tempIntent); free(tempIntent); } } else if (csMode == streamCSDeviceGray) { Object objCS = res->lookupColorSpace("DefaultGray"); if (objCS.isNull()) { colorSpace = new GfxDeviceGrayColorSpace(); } else { colorSpace = GfxColorSpace::parse(res, &objCS, out, state); } } else if (csMode == streamCSDeviceRGB) { Object objCS = res->lookupColorSpace("DefaultRGB"); if (objCS.isNull()) { colorSpace = new GfxDeviceRGBColorSpace(); } else { colorSpace = GfxColorSpace::parse(res, &objCS, out, state); } } else if (csMode == streamCSDeviceCMYK) { Object objCS = res->lookupColorSpace("DefaultCMYK"); if (objCS.isNull()) { colorSpace = new GfxDeviceCMYKColorSpace(); } else { colorSpace = GfxColorSpace::parse(res, &objCS, out, state); } } else { colorSpace = nullptr; } if (!colorSpace) { goto err1; } obj1 = dict->lookup("Decode"); if (obj1.isNull()) { obj1 = dict->lookup("D"); } GfxImageColorMap colorMap(bits, &obj1, colorSpace); if (!colorMap.isOk()) { goto err1; } // get the mask bool haveMaskImage = false; haveColorKeyMask = haveExplicitMask = haveSoftMask = false; maskStr = nullptr; // make gcc happy maskWidth = maskHeight = 0; // make gcc happy maskInvert = false; // make gcc happy std::unique_ptr maskColorMap; Object maskObj = dict->lookup("Mask"); Object smaskObj = dict->lookup("SMask"); if (maskObj.isStream()) { maskStr = maskObj.getStream(); maskDict = maskObj.streamGetDict(); // if Type is XObject and Subtype is Image // then the way the softmask is drawn will draw // correctly, if it falls through to the explicit // mask code then you get an error and no image // drawn because it expects maskDict to have an entry // of Mask or IM that is boolean... Object tobj = maskDict->lookup("Type"); if (!tobj.isNull() && tobj.isName() && tobj.isName("XObject")) { Object sobj = maskDict->lookup("Subtype"); if (!sobj.isNull() && sobj.isName() && sobj.isName("Image")) { // ensure that this mask does not include an ImageMask entry // which signifies the explicit mask obj1 = maskDict->lookup("ImageMask"); if (obj1.isNull()) { obj1 = maskDict->lookup("IM"); } if (obj1.isNull() || !obj1.isBool()) { haveMaskImage = true; } } } } if (smaskObj.isStream() || haveMaskImage) { // soft mask if (inlineImg) { goto err1; } if (!haveMaskImage) { maskStr = smaskObj.getStream(); maskDict = smaskObj.streamGetDict(); } obj1 = maskDict->lookup("Width"); if (obj1.isNull()) { obj1 = maskDict->lookup("W"); } if (!obj1.isInt()) { goto err1; } maskWidth = obj1.getInt(); obj1 = maskDict->lookup("Height"); if (obj1.isNull()) { obj1 = maskDict->lookup("H"); } if (!obj1.isInt()) { goto err1; } maskHeight = obj1.getInt(); obj1 = maskDict->lookup("Interpolate"); if (obj1.isNull()) { obj1 = maskDict->lookup("I"); } if (obj1.isBool()) { maskInterpolate = obj1.getBool(); } else { maskInterpolate = false; } obj1 = maskDict->lookup("BitsPerComponent"); if (obj1.isNull()) { obj1 = maskDict->lookup("BPC"); } if (!obj1.isInt()) { goto err1; } maskBits = obj1.getInt(); obj1 = maskDict->lookup("ColorSpace"); if (obj1.isNull()) { obj1 = maskDict->lookup("CS"); } if (obj1.isName()) { Object obj2 = res->lookupColorSpace(obj1.getName()); if (!obj2.isNull()) { obj1 = std::move(obj2); } } // Here, we parse manually instead of using GfxColorSpace::parse, // since we explicitly need DeviceGray and not some DefaultGray color space 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); 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()); } 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++) { 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()); break; } matteColor.c[i] = dblToCol(obj2.getNum()); } if (i == colorSpace->getNComps()) { maskColorMap->setMatteColor(&matteColor); } } } haveSoftMask = true; } else if (maskObj.isArray()) { // color key mask for (i = 0; i < maskObj.arrayGetLength() && i < 2 * gfxColorMaxComps; ++i) { obj1 = maskObj.arrayGet(i); if (obj1.isInt()) { maskColors[i] = obj1.getInt(); } else if (obj1.isReal()) { error(errSyntaxError, -1, "Mask entry should be an integer but it's a real, trying to use it"); maskColors[i] = (int)obj1.getReal(); } else { error(errSyntaxError, -1, "Mask entry should be an integer but it's of type {0:d}", obj1.getType()); goto err1; } } haveColorKeyMask = true; } else if (maskObj.isStream()) { // explicit mask if (inlineImg) { goto err1; } if (maskStr == nullptr) { maskStr = maskObj.getStream(); maskDict = maskObj.streamGetDict(); } obj1 = maskDict->lookup("Width"); if (obj1.isNull()) { obj1 = maskDict->lookup("W"); } if (!obj1.isInt()) { goto err1; } maskWidth = obj1.getInt(); obj1 = maskDict->lookup("Height"); if (obj1.isNull()) { obj1 = maskDict->lookup("H"); } if (!obj1.isInt()) { goto err1; } maskHeight = obj1.getInt(); obj1 = maskDict->lookup("Interpolate"); if (obj1.isNull()) { obj1 = maskDict->lookup("I"); } if (obj1.isBool()) { maskInterpolate = obj1.getBool(); } else { maskInterpolate = false; } obj1 = maskDict->lookup("ImageMask"); if (obj1.isNull()) { obj1 = maskDict->lookup("IM"); } if (!haveMaskImage && (!obj1.isBool() || !obj1.getBool())) { goto err1; } maskInvert = false; obj1 = maskDict->lookup("Decode"); if (obj1.isNull()) { obj1 = maskDict->lookup("D"); } if (obj1.isArray()) { Object obj2 = obj1.arrayGet(0); // Table 4.39 says /Decode must be [1 0] or [0 1]. Adobe // accepts [1.0 0.0] as well. if (obj2.isNum() && obj2.getNum() >= 0.9) { maskInvert = true; } } else if (!obj1.isNull()) { goto err1; } haveExplicitMask = true; } // if drawing is disabled, skip over inline image data if (!ocState || !out->needNonText()) { str->reset(); n = height * ((width * colorMap.getNumPixelComps() * colorMap.getBits() + 7) / 8); for (i = 0; i < n; ++i) { str->getChar(); } str->close(); // draw it } else { if (haveSoftMask) { out->drawSoftMaskedImage(state, ref, str, width, height, &colorMap, interpolate, maskStr, maskWidth, maskHeight, maskColorMap.get(), maskInterpolate); } else if (haveExplicitMask) { out->drawMaskedImage(state, ref, str, width, height, &colorMap, interpolate, maskStr, maskWidth, maskHeight, maskInvert, maskInterpolate); } else { out->drawImage(state, ref, str, width, height, &colorMap, interpolate, haveColorKeyMask ? maskColors : nullptr, inlineImg); } } } if ((i = width * height) > 1000) { i = 1000; } updateLevel += i; return; err1: error(errSyntaxError, getPos(), "Bad image parameters"); } bool Gfx::checkTransparencyGroup(Dict *resDict) { // check the effect of compositing objects as a group: // look for ExtGState entries with ca != 1 or CA != 1 or BM != normal bool transpGroup = false; double opac; if (resDict == nullptr) { return false; } pushResources(resDict); Object extGStates = resDict->lookup("ExtGState"); if (extGStates.isDict()) { Dict *dict = extGStates.getDict(); for (int i = 0; i < dict->getLength() && !transpGroup; i++) { GfxBlendMode mode; Object obj1 = res->lookupGState(dict->getKey(i)); if (obj1.isDict()) { Object obj2 = obj1.dictLookup("BM"); if (!obj2.isNull()) { if (state->parseBlendMode(&obj2, &mode)) { if (mode != gfxBlendNormal) { transpGroup = true; } } else { error(errSyntaxError, getPos(), "Invalid blend mode in ExtGState"); } } obj2 = obj1.dictLookup("ca"); if (obj2.isNum()) { opac = obj2.getNum(); opac = opac < 0 ? 0 : opac > 1 ? 1 : opac; if (opac != 1) { transpGroup = true; } } obj2 = obj1.dictLookup("CA"); if (obj2.isNum()) { opac = obj2.getNum(); opac = opac < 0 ? 0 : opac > 1 ? 1 : opac; if (opac != 1) { transpGroup = true; } } // alpha is shape obj2 = obj1.dictLookup("AIS"); if (!transpGroup && obj2.isBool()) { transpGroup = obj2.getBool(); } // soft mask obj2 = obj1.dictLookup("SMask"); if (!transpGroup && !obj2.isNull()) { if (!obj2.isName("None")) { transpGroup = true; } } } } } popResources(); return transpGroup; } void Gfx::doForm(Object *str) { Dict *dict; bool transpGroup, isolated, knockout; GfxColorSpace *blendingColorSpace; double m[6], bbox[4]; Dict *resDict; bool ocSaved; Object obj1; int i; // get stream dict dict = str->streamGetDict(); // check form type obj1 = dict->lookup("FormType"); if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) { error(errSyntaxError, getPos(), "Unknown form type"); } // check for optional content key ocSaved = ocState; const Object &objOC = dict->lookupNF("OC"); if (catalog->getOptContentConfig() && !catalog->getOptContentConfig()->optContentIsVisible(&objOC)) { if (out->needCharCount()) { ocState = false; } else { return; } } // get bounding box Object bboxObj = dict->lookup("BBox"); if (!bboxObj.isArray()) { error(errSyntaxError, getPos(), "Bad form bounding box"); ocState = ocSaved; return; } for (i = 0; i < 4; ++i) { obj1 = bboxObj.arrayGet(i); if (likely(obj1.isNum())) { bbox[i] = obj1.getNum(); } else { error(errSyntaxError, getPos(), "Bad form bounding box value"); return; } } // get matrix Object matrixObj = dict->lookup("Matrix"); if (matrixObj.isArray()) { for (i = 0; i < 6; ++i) { obj1 = matrixObj.arrayGet(i); if (likely(obj1.isNum())) { m[i] = obj1.getNum(); } else { m[i] = 0; } } } else { m[0] = 1; m[1] = 0; m[2] = 0; m[3] = 1; m[4] = 0; m[5] = 0; } // get resources Object resObj = dict->lookup("Resources"); resDict = resObj.isDict() ? resObj.getDict() : nullptr; // check for a transparency group transpGroup = isolated = knockout = false; blendingColorSpace = nullptr; obj1 = dict->lookup("Group"); if (obj1.isDict()) { Object obj2 = obj1.dictLookup("S"); if (obj2.isName("Transparency")) { Object obj3 = obj1.dictLookup("CS"); if (!obj3.isNull()) { blendingColorSpace = GfxColorSpace::parse(res, &obj3, out, state); } obj3 = obj1.dictLookup("I"); if (obj3.isBool()) { isolated = obj3.getBool(); } obj3 = obj1.dictLookup("K"); if (obj3.isBool()) { knockout = obj3.getBool(); } transpGroup = isolated || out->checkTransparencyGroup(state, knockout) || checkTransparencyGroup(resDict); } } // draw it drawForm(str, resDict, m, bbox, transpGroup, false, blendingColorSpace, isolated, knockout); if (blendingColorSpace) { delete blendingColorSpace; } ocState = ocSaved; } void Gfx::drawForm(Object *str, Dict *resDict, const double *matrix, const double *bbox, bool transpGroup, bool softMask, GfxColorSpace *blendingColorSpace, bool isolated, bool knockout, bool alpha, Function *transferFunc, GfxColor *backdropColor) { Parser *oldParser; GfxState *savedState; double oldBaseMatrix[6]; int i; // push new resources on stack pushResources(resDict); // save current graphics state savedState = saveStateStack(); // kill any pre-existing path state->clearPath(); // save current parser oldParser = parser; // set form transformation matrix state->concatCTM(matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]); out->updateCTM(state, matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]); // set form bounding box state->moveTo(bbox[0], bbox[1]); state->lineTo(bbox[2], bbox[1]); state->lineTo(bbox[2], bbox[3]); state->lineTo(bbox[0], bbox[3]); state->closePath(); state->clip(); out->clip(state); state->clearPath(); if (softMask || transpGroup) { if (state->getBlendMode() != gfxBlendNormal) { state->setBlendMode(gfxBlendNormal); out->updateBlendMode(state); } if (state->getFillOpacity() != 1) { state->setFillOpacity(1); out->updateFillOpacity(state); } if (state->getStrokeOpacity() != 1) { state->setStrokeOpacity(1); out->updateStrokeOpacity(state); } out->clearSoftMask(state); out->beginTransparencyGroup(state, bbox, blendingColorSpace, isolated, knockout, softMask); } // set new base matrix for (i = 0; i < 6; ++i) { oldBaseMatrix[i] = baseMatrix[i]; baseMatrix[i] = state->getCTM()[i]; } GfxState *stateBefore = state; // draw the form ++displayDepth; display(str, false); --displayDepth; if (stateBefore != state) { if (state->isParentState(stateBefore)) { error(errSyntaxError, -1, "There's a form with more q than Q, trying to fix"); while (stateBefore != state) { restoreState(); } } else { error(errSyntaxError, -1, "There's a form with more Q than q"); } } if (softMask || transpGroup) { out->endTransparencyGroup(state); } // restore base matrix for (i = 0; i < 6; ++i) { baseMatrix[i] = oldBaseMatrix[i]; } // restore parser parser = oldParser; // restore graphics state restoreStateStack(savedState); // pop resource stack popResources(); if (softMask) { out->setSoftMask(state, bbox, alpha, transferFunc, backdropColor); } else if (transpGroup) { out->paintTransparencyGroup(state, bbox); } return; } //------------------------------------------------------------------------ // in-line image operators //------------------------------------------------------------------------ void Gfx::opBeginImage(Object args[], int numArgs) { Stream *str; int c1, c2; // NB: this function is run even if ocState is false -- doImage() is // responsible for skipping over the inline image data // build dict/stream str = buildImageStream(); // display the image if (str) { doImage(nullptr, str, true); // skip 'EI' tag c1 = str->getUndecodedStream()->getChar(); c2 = str->getUndecodedStream()->getChar(); while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) { c1 = c2; c2 = str->getUndecodedStream()->getChar(); } delete str; } } Stream *Gfx::buildImageStream() { Stream *str; // build dictionary Object dict(new Dict(xref)); Object obj = parser->getObj(); while (!obj.isCmd("ID") && !obj.isEOF()) { if (!obj.isName()) { error(errSyntaxError, getPos(), "Inline image dictionary key must be a name object"); } else { auto val = parser->getObj(); if (val.isEOF() || val.isError()) { break; } dict.dictAdd(obj.getName(), std::move(val)); } obj = parser->getObj(); } if (obj.isEOF()) { error(errSyntaxError, getPos(), "End of file in inline image"); return nullptr; } // make stream if (parser->getStream()) { str = new EmbedStream(parser->getStream(), std::move(dict), false, 0, true); str = str->addFilters(str->getDict()); } else { str = nullptr; } return str; } void Gfx::opImageData(Object args[], int numArgs) { error(errInternal, getPos(), "Got 'ID' operator"); } void Gfx::opEndImage(Object args[], int numArgs) { error(errInternal, getPos(), "Got 'EI' operator"); } //------------------------------------------------------------------------ // type 3 font operators //------------------------------------------------------------------------ void Gfx::opSetCharWidth(Object args[], int numArgs) { out->type3D0(state, args[0].getNum(), args[1].getNum()); } void Gfx::opSetCacheDevice(Object args[], int numArgs) { out->type3D1(state, args[0].getNum(), args[1].getNum(), args[2].getNum(), args[3].getNum(), args[4].getNum(), args[5].getNum()); } //------------------------------------------------------------------------ // compatibility operators //------------------------------------------------------------------------ void Gfx::opBeginIgnoreUndef(Object args[], int numArgs) { ++ignoreUndef; } void Gfx::opEndIgnoreUndef(Object args[], int numArgs) { if (ignoreUndef > 0) { --ignoreUndef; } } //------------------------------------------------------------------------ // marked content operators //------------------------------------------------------------------------ enum GfxMarkedContentKind { gfxMCOptionalContent, gfxMCActualText, gfxMCOther }; struct MarkedContentStack { GfxMarkedContentKind kind; bool ocSuppressed; // are we ignoring content based on OptionalContent? MarkedContentStack *next; // next object on stack }; void Gfx::popMarkedContent() { MarkedContentStack *mc = mcStack; mcStack = mc->next; delete mc; } void Gfx::pushMarkedContent() { MarkedContentStack *mc = new MarkedContentStack(); mc->ocSuppressed = false; mc->kind = gfxMCOther; mc->next = mcStack; mcStack = mc; } bool Gfx::contentIsHidden() { MarkedContentStack *mc = mcStack; bool hidden = mc && mc->ocSuppressed; while (!hidden && mc && mc->next) { mc = mc->next; hidden = mc->ocSuppressed; } return hidden; } void Gfx::opBeginMarkedContent(Object args[], int numArgs) { // push a new stack entry pushMarkedContent(); OCGs *contentConfig = catalog->getOptContentConfig(); const char *name0 = args[0].getName(); if (strncmp(name0, "OC", 2) == 0 && contentConfig) { if (numArgs >= 2) { if (args[1].isName()) { const char *name1 = args[1].getName(); MarkedContentStack *mc = mcStack; mc->kind = gfxMCOptionalContent; Object markedContent = res->lookupMarkedContentNF(name1); if (!markedContent.isNull()) { bool visible = contentConfig->optContentIsVisible(&markedContent); mc->ocSuppressed = !(visible); } else { error(errSyntaxError, getPos(), "DID NOT find {0:s}", name1); } } else { error(errSyntaxError, getPos(), "Unexpected MC Type: {0:d}", args[1].getType()); } } else { error(errSyntaxError, getPos(), "insufficient arguments for Marked Content"); } } 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; } } } if (printCommands) { printf(" marked content: %s ", args[0].getName()); if (numArgs == 2) { args[1].print(stdout); } printf("\n"); fflush(stdout); } ocState = !contentIsHidden(); if (numArgs == 2 && args[1].isDict()) { out->beginMarkedContent(args[0].getName(), args[1].getDict()); } else if (numArgs == 1) { out->beginMarkedContent(args[0].getName(), nullptr); } } void Gfx::opEndMarkedContent(Object args[], int numArgs) { if (!mcStack) { error(errSyntaxWarning, getPos(), "Mismatched EMC operator"); return; } MarkedContentStack *mc = mcStack; GfxMarkedContentKind mcKind = mc->kind; // pop the stack popMarkedContent(); if (mcKind == gfxMCActualText) { out->endActualText(state); } ocState = !contentIsHidden(); out->endMarkedContent(state); } void Gfx::opMarkPoint(Object args[], int numArgs) { if (printCommands) { printf(" mark point: %s ", args[0].getName()); if (numArgs == 2) { args[1].print(stdout); } printf("\n"); fflush(stdout); } if (numArgs == 2 && args[1].isDict()) { out->markPoint(args[0].getName(), args[1].getDict()); } else { out->markPoint(args[0].getName()); } } //------------------------------------------------------------------------ // misc //------------------------------------------------------------------------ struct GfxStackStateSaver { explicit GfxStackStateSaver(Gfx *gfxA) : gfx(gfxA) { gfx->saveState(); } ~GfxStackStateSaver() { gfx->restoreState(); } GfxStackStateSaver(const GfxStackStateSaver &) = delete; GfxStackStateSaver &operator=(const GfxStackStateSaver &) = delete; Gfx *const gfx; }; void Gfx::drawAnnot(Object *str, AnnotBorder *border, AnnotColor *aColor, double xMin, double yMin, double xMax, double yMax, int rotate) { Dict *dict, *resDict; double formXMin, formYMin, formXMax, formYMax; double x, y, sx, sy, tx, ty; double m[6], bbox[4]; GfxColor color; int i; // this function assumes that we are in the default user space, // i.e., baseMatrix = ctm // if the bounding box has zero width or height, don't draw anything // at all if (xMin == xMax || yMin == yMax) { return; } // saves gfx state and automatically restores it on return GfxStackStateSaver stackStateSaver(this); // Rotation around the topleft corner (for the NoRotate flag) if (rotate != 0) { const double angle_rad = rotate * M_PI / 180; const double c = cos(angle_rad); const double s = sin(angle_rad); // (xMin, yMax) is the pivot const double unrotateMTX[6] = { +c, -s, +s, +c, -c * xMin - s * yMax + xMin, -c * yMax + s * xMin + yMax }; state->concatCTM(unrotateMTX[0], unrotateMTX[1], unrotateMTX[2], unrotateMTX[3], unrotateMTX[4], unrotateMTX[5]); out->updateCTM(state, unrotateMTX[0], unrotateMTX[1], unrotateMTX[2], unrotateMTX[3], unrotateMTX[4], unrotateMTX[5]); } // draw the appearance stream (if there is one) if (str->isStream()) { // get stream dict dict = str->streamGetDict(); // get the form bounding box Object bboxObj = dict->lookup("BBox"); if (!bboxObj.isArray()) { error(errSyntaxError, getPos(), "Bad form bounding box"); return; } for (i = 0; i < 4; ++i) { Object obj1 = bboxObj.arrayGet(i); if (likely(obj1.isNum())) { bbox[i] = obj1.getNum(); } else { error(errSyntaxError, getPos(), "Bad form bounding box value"); return; } } // get the form matrix Object matrixObj = dict->lookup("Matrix"); if (matrixObj.isArray() && matrixObj.arrayGetLength() >= 6) { for (i = 0; i < 6; ++i) { Object obj1 = matrixObj.arrayGet(i); if (likely(obj1.isNum())) { m[i] = obj1.getNum(); } else { error(errSyntaxError, getPos(), "Bad form matrix"); return; } } } else { m[0] = 1; m[1] = 0; m[2] = 0; m[3] = 1; m[4] = 0; m[5] = 0; } // transform the four corners of the form bbox to default user // space, and construct the transformed bbox x = bbox[0] * m[0] + bbox[1] * m[2] + m[4]; y = bbox[0] * m[1] + bbox[1] * m[3] + m[5]; formXMin = formXMax = x; formYMin = formYMax = y; x = bbox[0] * m[0] + bbox[3] * m[2] + m[4]; y = bbox[0] * m[1] + bbox[3] * m[3] + m[5]; if (x < formXMin) { formXMin = x; } else if (x > formXMax) { formXMax = x; } if (y < formYMin) { formYMin = y; } else if (y > formYMax) { formYMax = y; } x = bbox[2] * m[0] + bbox[1] * m[2] + m[4]; y = bbox[2] * m[1] + bbox[1] * m[3] + m[5]; if (x < formXMin) { formXMin = x; } else if (x > formXMax) { formXMax = x; } if (y < formYMin) { formYMin = y; } else if (y > formYMax) { formYMax = y; } x = bbox[2] * m[0] + bbox[3] * m[2] + m[4]; y = bbox[2] * m[1] + bbox[3] * m[3] + m[5]; if (x < formXMin) { formXMin = x; } else if (x > formXMax) { formXMax = x; } if (y < formYMin) { formYMin = y; } else if (y > formYMax) { formYMax = y; } // construct a mapping matrix, [sx 0 0], which maps the transformed // [0 sy 0] // [tx ty 1] // bbox to the annotation rectangle if (formXMin == formXMax) { // this shouldn't happen sx = 1; } else { sx = (xMax - xMin) / (formXMax - formXMin); } if (formYMin == formYMax) { // this shouldn't happen sy = 1; } else { sy = (yMax - yMin) / (formYMax - formYMin); } tx = -formXMin * sx + xMin; ty = -formYMin * sy + yMin; // the final transform matrix is (form matrix) * (mapping matrix) m[0] *= sx; m[1] *= sy; m[2] *= sx; m[3] *= sy; m[4] = m[4] * sx + tx; m[5] = m[5] * sy + ty; // get the resources Object resObj = dict->lookup("Resources"); resDict = resObj.isDict() ? resObj.getDict() : nullptr; // draw it drawForm(str, resDict, m, bbox); } // draw the border if (border && border->getWidth() > 0 && (!aColor || aColor->getSpace() != AnnotColor::colorTransparent)) { if (state->getStrokeColorSpace()->getMode() != csDeviceRGB) { state->setStrokePattern(nullptr); state->setStrokeColorSpace(new GfxDeviceRGBColorSpace()); out->updateStrokeColorSpace(state); } double r, g, b; if (!aColor) { r = g = b = 0; } else if ((aColor->getSpace() == AnnotColor::colorRGB)) { const double *values = aColor->getValues(); r = values[0]; g = values[1]; b = values[2]; } else { error(errUnimplemented, -1, "AnnotColor different than RGB and Transparent not supported"); r = g = b = 0; }; color.c[0] = dblToCol(r); color.c[1] = dblToCol(g); color.c[2] = dblToCol(b); state->setStrokeColor(&color); out->updateStrokeColor(state); state->setLineWidth(border->getWidth()); out->updateLineWidth(state); const std::vector &dash = border->getDash(); if (border->getStyle() == AnnotBorder::borderDashed && dash.size() > 0) { std::vector dash2 = dash; state->setLineDash(std::move(dash2), 0); out->updateLineDash(state); } //~ this doesn't currently handle the beveled and engraved styles state->clearPath(); state->moveTo(xMin, yMin); state->lineTo(xMax, yMin); if (border->getStyle() != AnnotBorder::borderUnderlined) { state->lineTo(xMax, yMax); state->lineTo(xMin, yMax); state->closePath(); } out->stroke(state); } } int Gfx::bottomGuard() { return stateGuards[stateGuards.size() - 1]; } void Gfx::pushStateGuard() { stateGuards.push_back(stackHeight); } void Gfx::popStateGuard() { while (stackHeight > bottomGuard() && state->hasSaves()) { restoreState(); } stateGuards.pop_back(); } void Gfx::saveState() { out->saveState(state); state = state->save(); stackHeight++; } void Gfx::restoreState() { if (stackHeight <= bottomGuard() || !state->hasSaves()) { error(errSyntaxError, -1, "Restoring state when no valid states to pop"); return; } state = state->restore(); out->restoreState(state); stackHeight--; } // Create a new state stack, and initialize it with a copy of the // current state. GfxState *Gfx::saveStateStack() { GfxState *oldState; out->saveState(state); oldState = state; state = state->copy(true); return oldState; } // Switch back to the previous state stack. void Gfx::restoreStateStack(GfxState *oldState) { while (state->hasSaves()) { restoreState(); } delete state; state = oldState; out->restoreState(state); } void Gfx::pushResources(Dict *resDict) { res = new GfxResources(xref, resDict, res); } void Gfx::popResources() { GfxResources *resPtr; resPtr = res->getNext(); delete res; res = resPtr; } poppler-24.02.0/poppler/Gfx.h000066400000000000000000000336311455701731300157320ustar00rootroot00000000000000//======================================================================== // // Gfx.h // // 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) 2005 Jonathan Blandford // 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, 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 Volker Krause // // 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 GFX_H #define GFX_H #include "poppler-config.h" #include "poppler_private_export.h" #include "GfxState.h" #include "Object.h" #include "PopplerCache.h" #include class GooString; class PDFDoc; class XRef; class Array; class Stream; class Parser; class Dict; class Function; class OutputDev; class GfxFontDict; class GfxFont; class GfxPattern; class GfxTilingPattern; class GfxShadingPattern; class GfxShading; class GfxFunctionShading; class GfxAxialShading; class GfxRadialShading; class GfxGouraudTriangleShading; class GfxPatchMeshShading; struct GfxPatch; class GfxState; struct GfxColor; class GfxColorSpace; class Gfx; class PDFRectangle; class AnnotBorder; class AnnotColor; class Catalog; struct MarkedContentStack; //------------------------------------------------------------------------ enum GfxClipType { clipNone, clipNormal, clipEO }; enum TchkType { tchkBool, // boolean tchkInt, // integer tchkNum, // number (integer or real) tchkString, // string tchkName, // name tchkArray, // array tchkProps, // properties (dictionary or name) tchkSCN, // scn/SCN args (number of name) tchkNone // used to avoid empty initializer lists }; #define maxArgs 33 struct Operator { char name[4]; int numArgs; TchkType tchk[maxArgs]; void (Gfx::*func)(Object args[], int numArgs); }; //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT GfxResources { public: GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA); ~GfxResources(); GfxResources(const GfxResources &) = delete; GfxResources &operator=(const GfxResources &other) = delete; std::shared_ptr lookupFont(const char *name); std::shared_ptr lookupFont(const char *name) const; Object lookupXObject(const char *name); 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); Object lookupGState(const char *name); Object lookupGStateNF(const char *name); GfxResources *getNext() const { return next; } private: std::shared_ptr doLookupFont(const char *name) const; GfxFontDict *fonts; Object xObjDict; Object colorSpaceDict; Object patternDict; Object shadingDict; Object gStateDict; PopplerCache gStateCache; XRef *xref; Object propertiesDict; GfxResources *next; }; //------------------------------------------------------------------------ // Gfx //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT Gfx { public: // Constructor for regular output. Gfx(PDFDoc *docA, OutputDev *outA, int pageNum, Dict *resDict, double hDPI, double vDPI, const PDFRectangle *box, const PDFRectangle *cropBox, int rotate, bool (*abortCheckCbkA)(void *data) = nullptr, void *abortCheckCbkDataA = nullptr, XRef *xrefA = nullptr); // Constructor for a sub-page object. Gfx(PDFDoc *docA, OutputDev *outA, Dict *resDict, const PDFRectangle *box, const PDFRectangle *cropBox, bool (*abortCheckCbkA)(void *data) = nullptr, void *abortCheckCbkDataA = nullptr, Gfx *gfxA = nullptr); #ifdef USE_CMS void initDisplayProfile(); #endif ~Gfx(); Gfx(const Gfx &) = delete; Gfx &operator=(const Gfx &other) = delete; XRef *getXRef() { return xref; } // Interpret a stream or array of streams. void display(Object *obj, bool topLevel = true); // Display an annotation, given its appearance (a Form XObject), // border style, and bounding box (in default user space). void drawAnnot(Object *str, AnnotBorder *border, AnnotColor *aColor, double xMin, double yMin, double xMax, double yMax, int rotate); // Save graphics state. void saveState(); // Push a new state guard void pushStateGuard(); // Restore graphics state. void restoreState(); // Pop to state guard and pop guard void popStateGuard(); // Get the current graphics state object. GfxState *getState() { return state; } bool checkTransparencyGroup(Dict *resDict); void drawForm(Object *str, Dict *resDict, const double *matrix, const double *bbox, bool transpGroup = false, bool softMask = false, GfxColorSpace *blendingColorSpace = nullptr, bool isolated = false, bool knockout = false, bool alpha = false, Function *transferFunc = nullptr, GfxColor *backdropColor = nullptr); void pushResources(Dict *resDict); void popResources(); private: PDFDoc *doc; XRef *xref; // the xref table for this PDF file Catalog *catalog; // the Catalog for this PDF file OutputDev *out; // output device bool subPage; // is this a sub-page object? const bool printCommands; // print the drawing commands (for debugging) const bool profileCommands; // profile the drawing commands (for debugging) bool commandAborted; // did the previous command abort the drawing? GfxResources *res; // resource stack int updateLevel; GfxState *state; // current graphics state int stackHeight; // the height of the current graphics stack std::vector stateGuards; // a stack of state limits; to guard against unmatched pops bool fontChanged; // set if font or text matrix has changed GfxClipType clip; // do a clip? int ignoreUndef; // current BX/EX nesting level double baseMatrix[6]; // default matrix for most recent // page/form/pattern int displayDepth; bool ocState; // true if drawing is enabled, false if // disabled MarkedContentStack *mcStack; // current BMC/EMC stack Parser *parser; // parser for page content stream(s) std::set formsDrawing; // the forms/patterns that are being drawn std::set charProcDrawing; // the charProc that are being drawn bool // callback to check for an abort (*abortCheckCbk)(void *data); void *abortCheckCbkData; static const Operator opTab[]; // table of operators void go(bool topLevel); void execOp(Object *cmd, Object args[], int numArgs); const Operator *findOp(const char *name); bool checkArg(Object *arg, TchkType type); Goffset getPos(); int bottomGuard(); // graphics state operators void opSave(Object args[], int numArgs); void opRestore(Object args[], int numArgs); void opConcat(Object args[], int numArgs); void opSetDash(Object args[], int numArgs); void opSetFlat(Object args[], int numArgs); void opSetLineJoin(Object args[], int numArgs); void opSetLineCap(Object args[], int numArgs); void opSetMiterLimit(Object args[], int numArgs); void opSetLineWidth(Object args[], int numArgs); void opSetExtGState(Object args[], int numArgs); void doSoftMask(Object *str, bool alpha, GfxColorSpace *blendingColorSpace, bool isolated, bool knockout, Function *transferFunc, GfxColor *backdropColor); void opSetRenderingIntent(Object args[], int numArgs); // color operators void opSetFillGray(Object args[], int numArgs); void opSetStrokeGray(Object args[], int numArgs); void opSetFillCMYKColor(Object args[], int numArgs); void opSetStrokeCMYKColor(Object args[], int numArgs); void opSetFillRGBColor(Object args[], int numArgs); void opSetStrokeRGBColor(Object args[], int numArgs); void opSetFillColorSpace(Object args[], int numArgs); void opSetStrokeColorSpace(Object args[], int numArgs); void opSetFillColor(Object args[], int numArgs); void opSetStrokeColor(Object args[], int numArgs); void opSetFillColorN(Object args[], int numArgs); void opSetStrokeColorN(Object args[], int numArgs); // path segment operators void opMoveTo(Object args[], int numArgs); void opLineTo(Object args[], int numArgs); void opCurveTo(Object args[], int numArgs); void opCurveTo1(Object args[], int numArgs); void opCurveTo2(Object args[], int numArgs); void opRectangle(Object args[], int numArgs); void opClosePath(Object args[], int numArgs); // path painting operators void opEndPath(Object args[], int numArgs); void opStroke(Object args[], int numArgs); void opCloseStroke(Object args[], int numArgs); void opFill(Object args[], int numArgs); void opEOFill(Object args[], int numArgs); void opFillStroke(Object args[], int numArgs); void opCloseFillStroke(Object args[], int numArgs); void opEOFillStroke(Object args[], int numArgs); void opCloseEOFillStroke(Object args[], int numArgs); void doPatternFill(bool eoFill); void doPatternStroke(); void doPatternText(); void doPatternImageMask(Object *ref, Stream *str, int width, int height, bool invert, bool inlineImg); void doTilingPatternFill(GfxTilingPattern *tPat, bool stroke, bool eoFill, bool text); void doShadingPatternFill(GfxShadingPattern *sPat, bool stroke, bool eoFill, bool text); void opShFill(Object args[], int numArgs); void doFunctionShFill(GfxFunctionShading *shading); void doFunctionShFill1(GfxFunctionShading *shading, double x0, double y0, double x1, double y1, GfxColor *colors, int depth); void doAxialShFill(GfxAxialShading *shading); void doRadialShFill(GfxRadialShading *shading); void doGouraudTriangleShFill(GfxGouraudTriangleShading *shading); void gouraudFillTriangle(double x0, double y0, GfxColor *color0, double x1, double y1, GfxColor *color1, double x2, double y2, GfxColor *color2, int nComps, int depth, GfxState::ReusablePathIterator *path); void gouraudFillTriangle(double x0, double y0, double color0, double x1, double y1, double color1, double x2, double y2, double color2, double refineColorThreshold, int depth, GfxGouraudTriangleShading *shading, GfxState::ReusablePathIterator *path); void doPatchMeshShFill(GfxPatchMeshShading *shading); void fillPatch(const GfxPatch *patch, int colorComps, int patchColorComps, double refineColorThreshold, int depth, const GfxPatchMeshShading *shading); void doEndPath(); // path clipping operators void opClip(Object args[], int numArgs); void opEOClip(Object args[], int numArgs); // text object operators void opBeginText(Object args[], int numArgs); void opEndText(Object args[], int numArgs); // text state operators void opSetCharSpacing(Object args[], int numArgs); void opSetFont(Object args[], int numArgs); void opSetTextLeading(Object args[], int numArgs); void opSetTextRender(Object args[], int numArgs); void opSetTextRise(Object args[], int numArgs); void opSetWordSpacing(Object args[], int numArgs); void opSetHorizScaling(Object args[], int numArgs); // text positioning operators void opTextMove(Object args[], int numArgs); void opTextMoveSet(Object args[], int numArgs); void opSetTextMatrix(Object args[], int numArgs); void opTextNextLine(Object args[], int numArgs); // text string operators void opShowText(Object args[], int numArgs); void opMoveShowText(Object args[], int numArgs); void opMoveSetShowText(Object args[], int numArgs); void opShowSpaceText(Object args[], int numArgs); void doShowText(const GooString *s); void doIncCharCount(const GooString *s); // XObject operators void opXObject(Object args[], int numArgs); void doImage(Object *ref, Stream *str, bool inlineImg); void doForm(Object *str); // in-line image operators void opBeginImage(Object args[], int numArgs); Stream *buildImageStream(); void opImageData(Object args[], int numArgs); void opEndImage(Object args[], int numArgs); // type 3 font operators void opSetCharWidth(Object args[], int numArgs); void opSetCacheDevice(Object args[], int numArgs); // compatibility operators void opBeginIgnoreUndef(Object args[], int numArgs); void opEndIgnoreUndef(Object args[], int numArgs); // marked content operators void opBeginMarkedContent(Object args[], int numArgs); void opEndMarkedContent(Object args[], int numArgs); void opMarkPoint(Object args[], int numArgs); GfxState *saveStateStack(); void restoreStateStack(GfxState *oldState); bool contentIsHidden(); void pushMarkedContent(); void popMarkedContent(); }; #endif poppler-24.02.0/poppler/GfxFont.cc000066400000000000000000002563431455701731300167260ustar00rootroot00000000000000//======================================================================== // // GfxFont.cc // // 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) 2005, 2006, 2008-2010, 2012, 2014, 2015, 2017-2023 Albert Astals Cid // Copyright (C) 2005, 2006 Kristian Høgsberg // Copyright (C) 2006 Takashi Iwai // Copyright (C) 2007 Julien Rebetez // Copyright (C) 2007 Jeff Muizelaar // Copyright (C) 2007 Koji Otani // Copyright (C) 2007 Ed Catmur // Copyright (C) 2008 Jonathan Kew // Copyright (C) 2008 Ed Avis // Copyright (C) 2008, 2010 Hib Eris // Copyright (C) 2009 Peter Kerzum // Copyright (C) 2009, 2010 David Benjamin // Copyright (C) 2011 Axel Strübing // Copyright (C) 2011, 2012, 2014 Adrian Johnson // Copyright (C) 2012 Yi Yang // Copyright (C) 2012 Suzuki Toshiya // Copyright (C) 2012, 2017 Thomas Freitag // Copyright (C) 2013-2016, 2018 Jason Crain // Copyright (C) 2014 Olly Betts // 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) 2023 Khaled Hosny // // 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 #include #include #include #include #include #include #include #include "goo/gmem.h" #include "Error.h" #include "Object.h" #include "Dict.h" #include "GlobalParams.h" #include "CMap.h" #include "CharCodeToUnicode.h" #include "FontEncodingTables.h" #include "BuiltinFont.h" #include "UnicodeTypeTable.h" #include #include #include #include #include "GfxFont.h" #include "PSOutputDev.h" //------------------------------------------------------------------------ struct Base14FontMapEntry { const char *altName; const char *base14Name; }; static const Base14FontMapEntry base14FontMap[] = { { "Arial", "Helvetica" }, { "Arial,Bold", "Helvetica-Bold" }, { "Arial,BoldItalic", "Helvetica-BoldOblique" }, { "Arial,Italic", "Helvetica-Oblique" }, { "Arial-Bold", "Helvetica-Bold" }, { "Arial-BoldItalic", "Helvetica-BoldOblique" }, { "Arial-BoldItalicMT", "Helvetica-BoldOblique" }, { "Arial-BoldMT", "Helvetica-Bold" }, { "Arial-Italic", "Helvetica-Oblique" }, { "Arial-ItalicMT", "Helvetica-Oblique" }, { "ArialMT", "Helvetica" }, { "Courier", "Courier" }, { "Courier,Bold", "Courier-Bold" }, { "Courier,BoldItalic", "Courier-BoldOblique" }, { "Courier,Italic", "Courier-Oblique" }, { "Courier-Bold", "Courier-Bold" }, { "Courier-BoldOblique", "Courier-BoldOblique" }, { "Courier-Oblique", "Courier-Oblique" }, { "CourierNew", "Courier" }, { "CourierNew,Bold", "Courier-Bold" }, { "CourierNew,BoldItalic", "Courier-BoldOblique" }, { "CourierNew,Italic", "Courier-Oblique" }, { "CourierNew-Bold", "Courier-Bold" }, { "CourierNew-BoldItalic", "Courier-BoldOblique" }, { "CourierNew-Italic", "Courier-Oblique" }, { "CourierNewPS-BoldItalicMT", "Courier-BoldOblique" }, { "CourierNewPS-BoldMT", "Courier-Bold" }, { "CourierNewPS-ItalicMT", "Courier-Oblique" }, { "CourierNewPSMT", "Courier" }, { "Helvetica", "Helvetica" }, { "Helvetica,Bold", "Helvetica-Bold" }, { "Helvetica,BoldItalic", "Helvetica-BoldOblique" }, { "Helvetica,Italic", "Helvetica-Oblique" }, { "Helvetica-Bold", "Helvetica-Bold" }, { "Helvetica-BoldItalic", "Helvetica-BoldOblique" }, { "Helvetica-BoldOblique", "Helvetica-BoldOblique" }, { "Helvetica-Italic", "Helvetica-Oblique" }, { "Helvetica-Oblique", "Helvetica-Oblique" }, { "Symbol", "Symbol" }, { "Symbol,Bold", "Symbol" }, { "Symbol,BoldItalic", "Symbol" }, { "Symbol,Italic", "Symbol" }, { "SymbolMT", "Symbol" }, { "SymbolMT,Bold", "Symbol" }, { "SymbolMT,BoldItalic", "Symbol" }, { "SymbolMT,Italic", "Symbol" }, { "Times-Bold", "Times-Bold" }, { "Times-BoldItalic", "Times-BoldItalic" }, { "Times-Italic", "Times-Italic" }, { "Times-Roman", "Times-Roman" }, { "TimesNewRoman", "Times-Roman" }, { "TimesNewRoman,Bold", "Times-Bold" }, { "TimesNewRoman,BoldItalic", "Times-BoldItalic" }, { "TimesNewRoman,Italic", "Times-Italic" }, { "TimesNewRoman-Bold", "Times-Bold" }, { "TimesNewRoman-BoldItalic", "Times-BoldItalic" }, { "TimesNewRoman-Italic", "Times-Italic" }, { "TimesNewRomanPS", "Times-Roman" }, { "TimesNewRomanPS-Bold", "Times-Bold" }, { "TimesNewRomanPS-BoldItalic", "Times-BoldItalic" }, { "TimesNewRomanPS-BoldItalicMT", "Times-BoldItalic" }, { "TimesNewRomanPS-BoldMT", "Times-Bold" }, { "TimesNewRomanPS-Italic", "Times-Italic" }, { "TimesNewRomanPS-ItalicMT", "Times-Italic" }, { "TimesNewRomanPSMT", "Times-Roman" }, { "TimesNewRomanPSMT,Bold", "Times-Bold" }, { "TimesNewRomanPSMT,BoldItalic", "Times-BoldItalic" }, { "TimesNewRomanPSMT,Italic", "Times-Italic" }, { "ZapfDingbats", "ZapfDingbats" } }; //------------------------------------------------------------------------ // index: {fixed:0, sans-serif:4, serif:8} + bold*2 + italic // NB: must be in same order as psSubstFonts in PSOutputDev.cc static const char *base14SubstFonts[14] = { "Courier", "Courier-Oblique", "Courier-Bold", "Courier-BoldOblique", "Helvetica", "Helvetica-Oblique", "Helvetica-Bold", "Helvetica-BoldOblique", "Times-Roman", "Times-Italic", "Times-Bold", "Times-BoldItalic", // the last two are never used for substitution "Symbol", "ZapfDingbats" }; //------------------------------------------------------------------------ static int parseCharName(char *charName, Unicode *uBuf, int uLen, bool names, bool ligatures, bool numeric, bool hex, bool variants); //------------------------------------------------------------------------ static int readFromStream(void *data) { return ((Stream *)data)->getChar(); } //------------------------------------------------------------------------ // GfxFontLoc //------------------------------------------------------------------------ GfxFontLoc::GfxFontLoc() { fontNum = 0; substIdx = -1; } GfxFontLoc::~GfxFontLoc() = default; 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 //------------------------------------------------------------------------ std::unique_ptr GfxFont::makeFont(XRef *xref, const char *tagA, Ref idA, Dict *fontDict) { std::optional name; Ref embFontIDA; GfxFontType typeA; // get base font name Object obj1 = fontDict->lookup("BaseFont"); if (obj1.isName()) { 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); } else { font = new GfxCIDFont(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) { ok = false; embFontID = embFontIDA; embFontName = nullptr; family = nullptr; stretch = StretchNotDefined; weight = WeightNotDefined; hasToUnicode = false; } GfxFont::~GfxFont() { delete family; if (embFontName) { delete embFontName; } } bool GfxFont::isSubset() const { if (name) { unsigned int i; for (i = 0; i < name->size(); ++i) { if ((*name)[i] < 'A' || (*name)[i] > 'Z') { break; } } return i == 6 && name->size() > 7 && (*name)[6] == '+'; } return false; } std::string GfxFont::getNameWithoutSubsetTag() const { if (!name) { return {}; } if (!isSubset()) { return *name; } return name->substr(7); } // This function extracts three pieces of information: // 1. the "expected" font type, i.e., the font type implied by // Font.Subtype, DescendantFont.Subtype, and // FontDescriptor.FontFile3.Subtype // 2. the embedded font object ID // 3. the actual font type - determined by examining the embedded font // if there is one, otherwise equal to the expected font type // If the expected and actual font types don't match, a warning // message is printed. The expected font type is not used for // anything else. GfxFontType GfxFont::getFontType(XRef *xref, Dict *fontDict, Ref *embID) { GfxFontType t, expectedType; FoFiIdentifierType fft; Dict *fontDict2; bool isType0, err; t = fontUnknownType; *embID = Ref::INVALID(); err = false; Object subtype = fontDict->lookup("Subtype"); expectedType = fontUnknownType; isType0 = false; if (subtype.isName("Type1") || subtype.isName("MMType1")) { expectedType = fontType1; } else if (subtype.isName("Type1C")) { expectedType = fontType1C; } else if (subtype.isName("Type3")) { expectedType = fontType3; } else if (subtype.isName("TrueType")) { expectedType = fontTrueType; } else if (subtype.isName("Type0")) { isType0 = true; } else { error(errSyntaxWarning, -1, "Unknown font type: '{0:s}'", subtype.isName() ? subtype.getName() : "???"); } fontDict2 = fontDict; Object obj1 = fontDict->lookup("DescendantFonts"); Object obj2; // Do not move to inside the if // we need it around so that fontDict2 remains valid if (obj1.isArray()) { if (obj1.arrayGetLength() == 0) { error(errSyntaxWarning, -1, "Empty DescendantFonts array in font"); } else { obj2 = obj1.arrayGet(0); if (obj2.isDict()) { if (!isType0) { error(errSyntaxWarning, -1, "Non-CID font with DescendantFonts array"); } fontDict2 = obj2.getDict(); subtype = fontDict2->lookup("Subtype"); if (subtype.isName("CIDFontType0")) { if (isType0) { expectedType = fontCIDType0; } } else if (subtype.isName("CIDFontType2")) { if (isType0) { expectedType = fontCIDType2; } } } } } Object fontDesc = fontDict2->lookup("FontDescriptor"); if (fontDesc.isDict()) { Object obj3 = fontDesc.dictLookupNF("FontFile").copy(); if (obj3.isRef()) { *embID = obj3.getRef(); if (expectedType != fontType1) { err = true; } } if (*embID == Ref::INVALID() && (obj3 = fontDesc.dictLookupNF("FontFile2").copy(), obj3.isRef())) { *embID = obj3.getRef(); if (isType0) { expectedType = fontCIDType2; } else if (expectedType != fontTrueType) { err = true; } } if (*embID == Ref::INVALID() && (obj3 = fontDesc.dictLookupNF("FontFile3").copy(), obj3.isRef())) { *embID = obj3.getRef(); Object obj4 = obj3.fetch(xref); if (obj4.isStream()) { subtype = obj4.streamGetDict()->lookup("Subtype"); if (subtype.isName("Type1")) { if (expectedType != fontType1) { err = true; expectedType = isType0 ? fontCIDType0 : fontType1; } } else if (subtype.isName("Type1C")) { if (expectedType == fontType1) { expectedType = fontType1C; } else if (expectedType != fontType1C) { err = true; expectedType = isType0 ? fontCIDType0C : fontType1C; } } else if (subtype.isName("TrueType")) { if (expectedType != fontTrueType) { err = true; expectedType = isType0 ? fontCIDType2 : fontTrueType; } } else if (subtype.isName("CIDFontType0C")) { if (expectedType == fontCIDType0) { expectedType = fontCIDType0C; } else { err = true; expectedType = isType0 ? fontCIDType0C : fontType1C; } } else if (subtype.isName("OpenType")) { if (expectedType == fontTrueType) { expectedType = fontTrueTypeOT; } else if (expectedType == fontType1) { expectedType = fontType1COT; } else if (expectedType == fontCIDType0) { expectedType = fontCIDType0COT; } else if (expectedType == fontCIDType2) { expectedType = fontCIDType2OT; } else { err = true; } } else { error(errSyntaxError, -1, "Unknown font type '{0:s}'", subtype.isName() ? subtype.getName() : "???"); } } } } t = fontUnknownType; if (*embID != Ref::INVALID()) { Object obj3(*embID); Object obj4 = obj3.fetch(xref); if (obj4.isStream()) { obj4.streamReset(); fft = FoFiIdentifier::identifyStream(&readFromStream, obj4.getStream()); obj4.streamClose(); switch (fft) { case fofiIdType1PFA: case fofiIdType1PFB: t = fontType1; break; case fofiIdCFF8Bit: t = isType0 ? fontCIDType0C : fontType1C; break; case fofiIdCFFCID: t = fontCIDType0C; break; case fofiIdTrueType: case fofiIdTrueTypeCollection: t = isType0 ? fontCIDType2 : fontTrueType; break; case fofiIdOpenTypeCFF8Bit: t = isType0 ? fontCIDType0COT : fontType1COT; break; case fofiIdOpenTypeCFFCID: t = fontCIDType0COT; break; default: error(errSyntaxError, -1, "Embedded font file may be invalid"); break; } } } if (t == fontUnknownType) { t = expectedType; } if (t != expectedType) { err = true; } if (err) { error(errSyntaxWarning, -1, "Mismatch between font type and embedded font file"); } return t; } void GfxFont::readFontDescriptor(XRef *xref, Dict *fontDict) { double t; // assume Times-Roman by default (for substitution purposes) flags = fontSerif; missingWidth = 0; Object obj1 = fontDict->lookup("FontDescriptor"); if (obj1.isDict()) { // get flags Object obj2 = obj1.dictLookup("Flags"); if (obj2.isInt()) { flags = obj2.getInt(); } // get name obj2 = obj1.dictLookup("FontName"); if (obj2.isName()) { embFontName = new GooString(obj2.getName()); } if (embFontName == nullptr) { // get name with typo obj2 = obj1.dictLookup("Fontname"); if (obj2.isName()) { embFontName = new GooString(obj2.getName()); error(errSyntaxWarning, -1, "The file uses Fontname instead of FontName please notify the creator that the file is broken"); } } // get family obj2 = obj1.dictLookup("FontFamily"); if (obj2.isString()) { family = new GooString(obj2.getString()); } // get stretch obj2 = obj1.dictLookup("FontStretch"); if (obj2.isName()) { if (strcmp(obj2.getName(), "UltraCondensed") == 0) { stretch = UltraCondensed; } else if (strcmp(obj2.getName(), "ExtraCondensed") == 0) { stretch = ExtraCondensed; } else if (strcmp(obj2.getName(), "Condensed") == 0) { stretch = Condensed; } else if (strcmp(obj2.getName(), "SemiCondensed") == 0) { stretch = SemiCondensed; } else if (strcmp(obj2.getName(), "Normal") == 0) { stretch = Normal; } else if (strcmp(obj2.getName(), "SemiExpanded") == 0) { stretch = SemiExpanded; } else if (strcmp(obj2.getName(), "Expanded") == 0) { stretch = Expanded; } else if (strcmp(obj2.getName(), "ExtraExpanded") == 0) { stretch = ExtraExpanded; } else if (strcmp(obj2.getName(), "UltraExpanded") == 0) { stretch = UltraExpanded; } else { error(errSyntaxWarning, -1, "Invalid Font Stretch"); } } // get weight obj2 = obj1.dictLookup("FontWeight"); if (obj2.isNum()) { if (obj2.getNum() == 100) { weight = W100; } else if (obj2.getNum() == 200) { weight = W200; } else if (obj2.getNum() == 300) { weight = W300; } else if (obj2.getNum() == 400) { weight = W400; } else if (obj2.getNum() == 500) { weight = W500; } else if (obj2.getNum() == 600) { weight = W600; } else if (obj2.getNum() == 700) { weight = W700; } else if (obj2.getNum() == 800) { weight = W800; } else if (obj2.getNum() == 900) { weight = W900; } else { error(errSyntaxWarning, -1, "Invalid Font Weight"); } } // look for MissingWidth obj2 = obj1.dictLookup("MissingWidth"); if (obj2.isNum()) { missingWidth = obj2.getNum(); } // get Ascent and Descent obj2 = obj1.dictLookup("Ascent"); if (obj2.isNum()) { t = 0.001 * obj2.getNum(); // some broken font descriptors specify a negative ascent if (t < 0) { t = -t; } // some broken font descriptors set ascent and descent to 0; // others set it to ridiculous values (e.g., 32768) if (t != 0 && t < 3) { ascent = t; } } obj2 = obj1.dictLookup("Descent"); if (obj2.isNum()) { t = 0.001 * obj2.getNum(); // some broken font descriptors specify a positive descent if (t > 0) { t = -t; } // some broken font descriptors set ascent and descent to 0 if (t != 0 && t > -3) { descent = t; } } // font FontBBox obj2 = obj1.dictLookup("FontBBox"); if (obj2.isArray()) { for (int i = 0; i < 4 && i < obj2.arrayGetLength(); ++i) { Object obj3 = obj2.arrayGet(i); if (obj3.isNum()) { fontBBox[i] = 0.001 * obj3.getNum(); } } } } } CharCodeToUnicode *GfxFont::readToUnicodeCMap(Dict *fontDict, int nBits, CharCodeToUnicode *ctu) { GooString *buf; Object obj1 = fontDict->lookup("ToUnicode"); if (!obj1.isStream()) { return nullptr; } buf = new GooString(); obj1.getStream()->fillGooString(buf); obj1.streamClose(); if (ctu) { ctu->mergeCMap(buf, nBits); } else { ctu = CharCodeToUnicode::parseCMap(buf, nBits); } hasToUnicode = true; delete buf; return ctu; } std::optional GfxFont::locateFont(XRef *xref, PSOutputDev *ps, GooString *substituteFontName) { SysFontType sysFontType; GooString *path, *base14Name; int substIdx, fontNum; bool embed; if (type == fontType3) { return std::nullopt; } //----- embedded font if (embFontID != Ref::INVALID()) { embed = true; Object refObj(embFontID); Object embFontObj = refObj.fetch(xref); if (!embFontObj.isStream()) { error(errSyntaxError, -1, "Embedded font object is wrong type"); embed = false; } if (embed) { if (ps) { switch (type) { case fontType1: case fontType1C: case fontType1COT: embed = ps->getEmbedType1(); break; case fontTrueType: case fontTrueTypeOT: embed = ps->getEmbedTrueType(); break; case fontCIDType0C: case fontCIDType0COT: embed = ps->getEmbedCIDPostScript(); break; case fontCIDType2: case fontCIDType2OT: embed = ps->getEmbedCIDTrueType(); break; default: break; } } if (embed) { GfxFontLoc fontLoc; fontLoc.locType = gfxFontLocEmbedded; fontLoc.fontType = type; fontLoc.embFontID = embFontID; return fontLoc; } } } //----- PS passthrough if (ps && !isCIDFont() && ps->getFontPassthrough()) { GfxFontLoc fontLoc; fontLoc.locType = gfxFontLocResident; fontLoc.fontType = fontType1; fontLoc.path = *name; return fontLoc; } //----- PS resident Base-14 font if (ps && !isCIDFont() && ((Gfx8BitFont *)this)->base14) { GfxFontLoc fontLoc; fontLoc.locType = gfxFontLocResident; fontLoc.fontType = fontType1; fontLoc.path = ((Gfx8BitFont *)this)->base14->base14Name; return fontLoc; } //----- external font file (fontFile, fontDir) if (name && (path = globalParams->findFontFile(*name))) { if (std::optional fontLoc = getExternalFont(path, isCIDFont())) { return fontLoc; } } //----- 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, substituteFontName))) { if (std::optional fontLoc = getExternalFont(path, false)) { delete base14Name; return fontLoc; } } delete base14Name; } //----- system font 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.fontNum = fontNum; return fontLoc; } } else { GfxFontLoc fontLoc; fontLoc.setPath(path); fontLoc.locType = gfxFontLocExternal; if (sysFontType == sysFontTTF || sysFontType == sysFontTTC) { fontLoc.fontType = fontTrueType; } else if (sysFontType == sysFontPFA || sysFontType == sysFontPFB) { fontLoc.fontType = fontType1; fontLoc.fontNum = fontNum; } return fontLoc; } delete path; } if (!isCIDFont()) { //----- 8-bit font substitution if (flags & fontFixedWidth) { substIdx = 0; } else if (flags & fontSerif) { substIdx = 8; } else { substIdx = 4; } if (isBold()) { substIdx += 2; } if (isItalic()) { substIdx += 1; } const std::string substName = base14SubstFonts[substIdx]; if (ps) { error(errSyntaxWarning, -1, "Substituting font '{0:s}' for '{1:s}'", base14SubstFonts[substIdx], name ? name->c_str() : "null"); GfxFontLoc fontLoc; fontLoc.locType = gfxFontLocResident; fontLoc.fontType = fontType1; fontLoc.path = substName; fontLoc.substIdx = substIdx; return fontLoc; } else { path = globalParams->findFontFile(substName); if (path) { 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; return fontLoc; } } } // failed to find a substitute font return std::nullopt; } // failed to find a substitute font return std::nullopt; } std::optional GfxFont::getExternalFont(GooString *path, bool cid) { FoFiIdentifierType fft; GfxFontType fontType; fft = FoFiIdentifier::identifyFile(path->c_str()); switch (fft) { case fofiIdType1PFA: case fofiIdType1PFB: fontType = fontType1; break; case fofiIdCFF8Bit: fontType = fontType1C; break; case fofiIdCFFCID: fontType = fontCIDType0C; break; case fofiIdTrueType: case fofiIdTrueTypeCollection: fontType = cid ? fontCIDType2 : fontTrueType; break; case fofiIdOpenTypeCFF8Bit: fontType = fontType1COT; break; case fofiIdOpenTypeCFFCID: fontType = fontCIDType0COT; break; case fofiIdUnknown: case fofiIdError: default: fontType = fontUnknownType; 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 fontLoc; } std::optional> GfxFont::readEmbFontFile(XRef *xref) { Stream *str; Object obj1(embFontID); Object obj2 = obj1.fetch(xref); if (!obj2.isStream()) { error(errSyntaxError, -1, "Embedded font file is not a stream"); embFontID = Ref::INVALID(); return {}; } str = obj2.getStream(); std::vector buf = str->toUnsignedChars(); str->close(); return buf; } struct AlternateNameMap { const char *name; const char *alt; }; static const AlternateNameMap alternateNameMap[] = { { "fi", "f_i" }, { "fl", "f_l" }, { "ff", "f_f" }, { "ffi", "f_f_i" }, { "ffl", "f_f_l" }, { nullptr, nullptr } }; const char *GfxFont::getAlternateName(const char *name) { const AlternateNameMap *map = alternateNameMap; while (map->name) { if (strcmp(name, map->name) == 0) { return map->alt; } map++; } return nullptr; } //------------------------------------------------------------------------ // Gfx8BitFont //------------------------------------------------------------------------ // Parse character names of the form 'Axx', 'xx', 'Ann', 'ABnn', or // 'nn', where 'A' and 'B' are any letters, 'xx' is two hex digits, // and 'nn' is decimal digits. static bool parseNumericName(const char *s, bool hex, unsigned int *u) { char *endptr; // Strip leading alpha characters. if (hex) { int n = 0; // Get string length while ignoring junk at end. while (isalnum(s[n])) { ++n; } // Only 2 hex characters with optional leading alpha is allowed. if (n == 3 && isalpha(*s)) { ++s; } else if (n != 2) { return false; } } else { // Strip up to two alpha characters. for (int i = 0; i < 2 && isalpha(*s); ++i) { ++s; } } int v = strtol(s, &endptr, hex ? 16 : 10); if (endptr == s) { return false; } // Skip trailing junk characters. while (*endptr != '\0' && !isalnum(*endptr)) { ++endptr; } if (*endptr == '\0') { if (u) { *u = v; } return true; } return false; } // Returns true if the font has character names like xx or Axx which // should be parsed for hex or decimal values. static bool testForNumericNames(Dict *fontDict, bool hex) { bool numeric = true; Object enc = fontDict->lookup("Encoding"); if (!enc.isDict()) { return false; } Object diff = enc.dictLookup("Differences"); if (!diff.isArray()) { return false; } for (int i = 0; i < diff.arrayGetLength() && numeric; ++i) { Object obj = diff.arrayGet(i); if (obj.isInt()) { // All sequences must start between character codes 0 and 5. if (obj.getInt() > 5) { numeric = false; } } else if (obj.isName()) { // All character names must successfully parse. if (!parseNumericName(obj.getName(), hex, nullptr)) { numeric = false; } } else { numeric = false; } } return numeric; } Gfx8BitFont::Gfx8BitFont(XRef *xref, const char *tagA, Ref idA, std::optional &&nameA, GfxFontType typeA, Ref embFontIDA, Dict *fontDict) : GfxFont(tagA, idA, std::move(nameA), typeA, embFontIDA) { const BuiltinFont *builtinFont; const char **baseEnc; bool baseEncFromFontFile; int len; FoFiType1 *ffT1; FoFiType1C *ffT1C; char *charName; bool missing, hex; bool numeric; Unicode toUnicode[256]; Unicode uBuf[8]; double mul; int firstChar, lastChar; unsigned short w; Object obj1; int n, a, b, m; ctu = nullptr; // do font name substitution for various aliases of the Base 14 font // names base14 = nullptr; if (name) { std::string name2 = *name; size_t i = 0; while (i < name2.size()) { if (name2[i] == ' ') { name2.erase(i, 1); } else { ++i; } } a = 0; b = sizeof(base14FontMap) / sizeof(Base14FontMapEntry); // invariant: base14FontMap[a].altName <= name2 < base14FontMap[b].altName while (b - a > 1) { m = (a + b) / 2; if (name2.compare(base14FontMap[m].altName) >= 0) { a = m; } else { b = m; } } if (name2 == base14FontMap[a].altName) { base14 = &base14FontMap[a]; } } // is it a built-in font? builtinFont = nullptr; if (base14) { for (const BuiltinFont &bf : builtinFonts) { if (!strcmp(base14->base14Name, bf.name)) { builtinFont = &bf; break; } } } // default ascent/descent values if (builtinFont) { ascent = 0.001 * builtinFont->ascent; descent = 0.001 * builtinFont->descent; fontBBox[0] = 0.001 * builtinFont->bbox[0]; fontBBox[1] = 0.001 * builtinFont->bbox[1]; fontBBox[2] = 0.001 * builtinFont->bbox[2]; fontBBox[3] = 0.001 * builtinFont->bbox[3]; } else { ascent = 0.95; descent = -0.35; fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0; } // get info from font descriptor readFontDescriptor(xref, fontDict); // for non-embedded fonts, don't trust the ascent/descent/bbox // values from the font descriptor if (builtinFont && embFontID == Ref::INVALID()) { ascent = 0.001 * builtinFont->ascent; descent = 0.001 * builtinFont->descent; fontBBox[0] = 0.001 * builtinFont->bbox[0]; fontBBox[1] = 0.001 * builtinFont->bbox[1]; fontBBox[2] = 0.001 * builtinFont->bbox[2]; fontBBox[3] = 0.001 * builtinFont->bbox[3]; } // get font matrix fontMat[0] = fontMat[3] = 1; fontMat[1] = fontMat[2] = fontMat[4] = fontMat[5] = 0; obj1 = fontDict->lookup("FontMatrix"); if (obj1.isArray()) { for (int i = 0; i < 6 && i < obj1.arrayGetLength(); ++i) { Object obj2 = obj1.arrayGet(i); if (obj2.isNum()) { fontMat[i] = obj2.getNum(); } } } // get Type 3 bounding box, font definition, and resources if (type == fontType3) { obj1 = fontDict->lookup("FontBBox"); if (obj1.isArray()) { for (int i = 0; i < 4 && i < obj1.arrayGetLength(); ++i) { Object obj2 = obj1.arrayGet(i); if (obj2.isNum()) { fontBBox[i] = obj2.getNum(); } } } charProcs = fontDict->lookup("CharProcs"); if (!charProcs.isDict()) { error(errSyntaxError, -1, "Missing or invalid CharProcs dictionary in Type 3 font"); charProcs.setToNull(); } resources = fontDict->lookup("Resources"); if (!resources.isDict()) { resources.setToNull(); } } //----- build the font encoding ----- // Encodings start with a base encoding, which can come from // (in order of priority): // 1. FontDict.Encoding or FontDict.Encoding.BaseEncoding // - MacRoman / MacExpert / WinAnsi / Standard // 2. embedded or external font file // 3. default: // - builtin --> builtin encoding // - TrueType --> WinAnsiEncoding // - others --> StandardEncoding // and then add a list of differences (if any) from // FontDict.Encoding.Differences. // check FontDict for base encoding hasEncoding = false; usesMacRomanEnc = false; baseEnc = nullptr; baseEncFromFontFile = false; obj1 = fontDict->lookup("Encoding"); if (obj1.isDict()) { Object obj2 = obj1.dictLookup("BaseEncoding"); if (obj2.isName("MacRomanEncoding")) { hasEncoding = true; usesMacRomanEnc = true; baseEnc = macRomanEncoding; } else if (obj2.isName("MacExpertEncoding")) { hasEncoding = true; baseEnc = macExpertEncoding; } else if (obj2.isName("WinAnsiEncoding")) { hasEncoding = true; baseEnc = winAnsiEncoding; } } else if (obj1.isName("MacRomanEncoding")) { hasEncoding = true; usesMacRomanEnc = true; baseEnc = macRomanEncoding; } else if (obj1.isName("MacExpertEncoding")) { hasEncoding = true; baseEnc = macExpertEncoding; } else if (obj1.isName("WinAnsiEncoding")) { hasEncoding = true; baseEnc = winAnsiEncoding; } // check embedded font file for base encoding // (only for Type 1 fonts - trying to get an encoding out of a // TrueType font is a losing proposition) ffT1 = nullptr; ffT1C = nullptr; if (type == fontType1 && embFontID != Ref::INVALID()) { const std::optional> buf = readEmbFontFile(xref); if (buf) { if ((ffT1 = FoFiType1::make(buf->data(), buf->size()))) { const std::string fontName = ffT1->getName(); if (!fontName.empty()) { delete embFontName; embFontName = new GooString(fontName); } if (!baseEnc) { baseEnc = (const char **)ffT1->getEncoding(); baseEncFromFontFile = true; } } } } else if (type == fontType1C && embFontID != Ref::INVALID()) { const std::optional> buf = readEmbFontFile(xref); if (buf) { if ((ffT1C = FoFiType1C::make(buf->data(), buf->size()))) { if (ffT1C->getName()) { if (embFontName) { delete embFontName; } embFontName = new GooString(ffT1C->getName()); } if (!baseEnc) { baseEnc = (const char **)ffT1C->getEncoding(); baseEncFromFontFile = true; } } } } // get default base encoding if (!baseEnc) { if (builtinFont && embFontID == Ref::INVALID()) { baseEnc = builtinFont->defaultBaseEnc; hasEncoding = true; } else if (type == fontTrueType) { baseEnc = winAnsiEncoding; } else { baseEnc = standardEncoding; } } if (baseEncFromFontFile) { encodingName = "Builtin"; } else if (baseEnc == winAnsiEncoding) { encodingName = "WinAnsi"; } else if (baseEnc == macRomanEncoding) { encodingName = "MacRoman"; } else if (baseEnc == macExpertEncoding) { encodingName = "MacExpert"; } else if (baseEnc == symbolEncoding) { encodingName = "Symbol"; } else if (baseEnc == zapfDingbatsEncoding) { encodingName = "ZapfDingbats"; } else { encodingName = "Standard"; } // copy the base encoding for (int i = 0; i < 256; ++i) { enc[i] = (char *)baseEnc[i]; if ((encFree[i] = baseEncFromFontFile) && enc[i]) { enc[i] = copyString(baseEnc[i]); } } // some Type 1C font files have empty encodings, which can break the // T1C->T1 conversion (since the 'seac' operator depends on having // the accents in the encoding), so we fill in any gaps from // StandardEncoding if (type == fontType1C && embFontID != Ref::INVALID() && baseEncFromFontFile) { for (int i = 0; i < 256; ++i) { if (!enc[i] && standardEncoding[i]) { enc[i] = (char *)standardEncoding[i]; encFree[i] = false; } } } // merge differences into encoding if (obj1.isDict()) { Object obj2 = obj1.dictLookup("Differences"); if (obj2.isArray()) { encodingName = "Custom"; hasEncoding = true; int code = 0; for (int i = 0; i < obj2.arrayGetLength(); ++i) { Object obj3 = obj2.arrayGet(i); if (obj3.isInt()) { code = obj3.getInt(); } else if (obj3.isName()) { if (code >= 0 && code < 256) { if (encFree[code]) { gfree(enc[code]); } enc[code] = copyString(obj3.getName()); encFree[code] = true; ++code; } } else { error(errSyntaxError, -1, "Wrong type in font encoding resource differences ({0:s})", obj3.getTypeName()); } } } } delete ffT1; delete ffT1C; //----- build the mapping to Unicode ----- // pass 1: use the name-to-Unicode mapping table missing = hex = false; bool isZapfDingbats = name && GooString::endsWith(*name, "ZapfDingbats"); for (int code = 0; code < 256; ++code) { if ((charName = enc[code])) { if (isZapfDingbats) { // include ZapfDingbats names toUnicode[code] = globalParams->mapNameToUnicodeAll(charName); } else { toUnicode[code] = globalParams->mapNameToUnicodeText(charName); } if (!toUnicode[code] && strcmp(charName, ".notdef")) { // 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 if ((strlen(charName) == 3 && isalpha(charName[0]) && isxdigit(charName[1]) && isxdigit(charName[2]) && ((charName[1] >= 'a' && charName[1] <= 'f') || (charName[1] >= 'A' && charName[1] <= 'F') || (charName[2] >= 'a' && charName[2] <= 'f') || (charName[2] >= 'A' && charName[2] <= 'F'))) || (strlen(charName) == 2 && isxdigit(charName[0]) && isxdigit(charName[1]) && // Only check idx 1 to avoid misidentifying a decimal // number like a0 ((charName[1] >= 'a' && charName[1] <= 'f') || (charName[1] >= 'A' && charName[1] <= 'F')))) { hex = true; } missing = true; } } else { toUnicode[code] = 0; } } numeric = testForNumericNames(fontDict, hex); // construct the char code -> Unicode mapping object ctu = CharCodeToUnicode::make8BitToUnicode(toUnicode); // pass 1a: Expand ligatures in the Alphabetic Presentation Form // block (eg "fi", "ffi") to normal form for (int code = 0; code < 256; ++code) { if (unicodeIsAlphabeticPresentationForm(toUnicode[code])) { Unicode *normalized = unicodeNormalizeNFKC(&toUnicode[code], 1, &len, nullptr); if (len > 1) { ctu->setMapping((CharCode)code, normalized, len); } gfree(normalized); } } // pass 2: try to fill in the missing chars, looking for ligatures, numeric // references and variants if (missing) { for (int code = 0; code < 256; ++code) { if (!toUnicode[code]) { if ((charName = enc[code]) && strcmp(charName, ".notdef") && (n = parseCharName(charName, uBuf, sizeof(uBuf) / sizeof(*uBuf), false, // don't check simple names (pass 1) true, // do check ligatures numeric, hex, true))) { // do check variants ctu->setMapping((CharCode)code, uBuf, n); continue; } // do a simple pass-through // mapping for unknown character names uBuf[0] = code; ctu->setMapping((CharCode)code, uBuf, 1); } } } // merge in a ToUnicode CMap, if there is one -- this overwrites // existing entries in ctu, i.e., the ToUnicode CMap takes // precedence, but the other encoding info is allowed to fill in any // holes readToUnicodeCMap(fontDict, 16, ctu); //----- get the character widths ----- // initialize all widths for (double &width : widths) { width = missingWidth * 0.001; } // use widths from font dict, if present obj1 = fontDict->lookup("FirstChar"); firstChar = obj1.isInt() ? obj1.getInt() : 0; if (firstChar < 0 || firstChar > 255) { firstChar = 0; } obj1 = fontDict->lookup("LastChar"); lastChar = obj1.isInt() ? obj1.getInt() : 255; if (lastChar < 0 || lastChar > 255) { lastChar = 255; } mul = (type == fontType3) ? fontMat[0] : 0.001; obj1 = fontDict->lookup("Widths"); if (obj1.isArray()) { flags |= fontFixedWidth; if (obj1.arrayGetLength() < lastChar - firstChar + 1) { lastChar = firstChar + obj1.arrayGetLength() - 1; } double firstNonZeroWidth = 0; for (int code = firstChar; code <= lastChar; ++code) { Object obj2 = obj1.arrayGet(code - firstChar); if (obj2.isNum()) { widths[code] = obj2.getNum() * mul; // Check if the font is fixed width if (firstNonZeroWidth == 0) { firstNonZeroWidth = widths[code]; } if (firstNonZeroWidth != 0 && widths[code] != 0 && fabs(widths[code] - firstNonZeroWidth) > 0.00001) { flags &= ~fontFixedWidth; } } } // use widths from built-in font } else if (builtinFont) { // this is a kludge for broken PDF files that encode char 32 // as .notdef if (builtinFont->getWidth("space", &w)) { widths[32] = 0.001 * w; } for (int code = 0; code < 256; ++code) { if (enc[code] && builtinFont->getWidth(enc[code], &w)) { widths[code] = 0.001 * w; } } // couldn't find widths -- use defaults } else { // this is technically an error -- the Widths entry is required // for all but the Base-14 fonts -- but certain PDF generators // apparently don't include widths for Arial and TimesNewRoman int i; if (isFixedWidth()) { i = 0; } else if (isSerif()) { i = 8; } else { i = 4; } if (isBold()) { i += 2; } if (isItalic()) { i += 1; } builtinFont = builtinFontSubst[i]; // this is a kludge for broken PDF files that encode char 32 // as .notdef if (builtinFont->getWidth("space", &w)) { widths[32] = 0.001 * w; } for (int code = 0; code < 256; ++code) { if (enc[code] && builtinFont->getWidth(enc[code], &w)) { widths[code] = 0.001 * w; } } } ok = true; } Gfx8BitFont::~Gfx8BitFont() { int i; for (i = 0; i < 256; ++i) { if (encFree[i] && enc[i]) { gfree(enc[i]); } } ctu->decRefCnt(); } // This function is in part a derived work of the Adobe Glyph Mapping // Convention: http://www.adobe.com/devnet/opentype/archives/glyph.html // Algorithmic comments are excerpted from that document to aid // maintainability. static int parseCharName(char *charName, Unicode *uBuf, int uLen, bool names, bool ligatures, bool numeric, bool hex, bool variants) { if (uLen <= 0) { error(errInternal, -1, "Zero-length output buffer (recursion overflow?) in " "parseCharName, component \"{0:s}\"", charName); return 0; } // Step 1: drop all the characters from the glyph name starting with the // first occurrence of a period (U+002E FULL STOP), if any. if (variants) { char *var_part = strchr(charName, '.'); if (var_part == charName) { return 0; // .notdef or similar } else if (var_part != nullptr) { // parse names of the form 7.oldstyle, P.swash, s.sc, etc. char *main_part = copyString(charName, var_part - charName); bool namesRecurse = true, variantsRecurse = false; int n = parseCharName(main_part, uBuf, uLen, namesRecurse, ligatures, numeric, hex, variantsRecurse); gfree(main_part); return n; } } // Step 2: split the remaining string into a sequence of components, using // underscore (U+005F LOW LINE) as the delimiter. if (ligatures && strchr(charName, '_')) { // parse names of the form A_a (e.g. f_i, T_h, l_quotesingle) char *lig_part, *lig_end, *lig_copy; int n = 0, m; lig_part = lig_copy = copyString(charName); do { if ((lig_end = strchr(lig_part, '_'))) { *lig_end = '\0'; } if (lig_part[0] != '\0') { bool namesRecurse = true, ligaturesRecurse = false; if ((m = parseCharName(lig_part, uBuf + n, uLen - n, namesRecurse, ligaturesRecurse, numeric, hex, variants))) { n += m; } else { error(errSyntaxWarning, -1, "Could not parse ligature component \"{0:s}\" of \"{1:s}\" in " "parseCharName", lig_part, charName); } } if (lig_end) { lig_part = lig_end + 1; } } while (lig_end && n < uLen); gfree(lig_copy); return n; } // Step 3: map each component to a character string according to the // procedure below, and concatenate those strings; the result is the // character string to which the glyph name is mapped. // 3.1. if the font is Zapf Dingbats (PostScript FontName ZapfDingbats), and // the component is in the ZapfDingbats list, then map it to the // corresponding character in that list. // 3.2. otherwise, if the component is in the Adobe Glyph List, then map it // to the corresponding character in that list. if (names && (uBuf[0] = globalParams->mapNameToUnicodeText(charName))) { return 1; } unsigned int n = strlen(charName); // 3.3. otherwise, if the component is of the form "uni" (U+0075 U+006E // U+0069) followed by a sequence of uppercase hexadecimal digits (0 .. 9, // A .. F, i.e. U+0030 .. U+0039, U+0041 .. U+0046), the length of that // sequence is a multiple of four, and each group of four digits represents // a number in the set {0x0000 .. 0xD7FF, 0xE000 .. 0xFFFF}, then interpret // each such number as a Unicode scalar value and map the component to the // string made of those scalar values. Note that the range and digit length // restrictions mean that the "uni" prefix can be used only with Unicode // values from the Basic Multilingual Plane (BMP). if (n >= 7 && (n % 4) == 3 && !strncmp(charName, "uni", 3)) { int i; unsigned int m; for (i = 0, m = 3; i < uLen && m < n; m += 4) { if (isxdigit(charName[m]) && isxdigit(charName[m + 1]) && isxdigit(charName[m + 2]) && isxdigit(charName[m + 3])) { unsigned int u; sscanf(charName + m, "%4x", &u); if (u <= 0xD7FF || (0xE000 <= u && u <= 0xFFFF)) { uBuf[i++] = u; } } } return i; } // 3.4. otherwise, if the component is of the form "u" (U+0075) followed by // a sequence of four to six uppercase hexadecimal digits {0 .. 9, A .. F} // (U+0030 .. U+0039, U+0041 .. U+0046), and those digits represent a // number in {0x0000 .. 0xD7FF, 0xE000 .. 0x10FFFF}, then interpret this // number as a Unicode scalar value and map the component to the string // made of this scalar value. if (n >= 5 && n <= 7 && charName[0] == 'u' && isxdigit(charName[1]) && isxdigit(charName[2]) && isxdigit(charName[3]) && isxdigit(charName[4]) && (n <= 5 || isxdigit(charName[5])) && (n <= 6 || isxdigit(charName[6]))) { unsigned int u; sscanf(charName + 1, "%x", &u); if (u <= 0xD7FF || (0xE000 <= u && u <= 0x10FFFF)) { uBuf[0] = u; return 1; } } // Not in Adobe Glyph Mapping convention: look for names like xx // or Axx and parse for hex or decimal values. if (numeric && parseNumericName(charName, hex, uBuf)) { return 1; } // 3.5. otherwise, map the component to the empty string return 0; } int Gfx8BitFont::getNextChar(const char *s, int len, CharCode *code, Unicode const **u, int *uLen, double *dx, double *dy, double *ox, double *oy) const { CharCode c; *code = c = (CharCode)(*s & 0xff); *uLen = ctu->mapToUnicode(c, u); *dx = widths[c]; *dy = *ox = *oy = 0; return 1; } const CharCodeToUnicode *Gfx8BitFont::getToUnicode() const { return ctu; } int *Gfx8BitFont::getCodeToGIDMap(FoFiTrueType *ff) { int *map; int cmapPlatform, cmapEncoding; int unicodeCmap, macRomanCmap, msSymbolCmap, cmap; bool useMacRoman, useUnicode; char *charName; Unicode u; int code, i, n; map = (int *)gmallocn(256, sizeof(int)); for (i = 0; i < 256; ++i) { map[i] = 0; } // To match up with the Adobe-defined behaviour, we choose a cmap // like this: // 1. If the PDF font has an encoding: // 1a. If the TrueType font has a Microsoft Unicode // cmap or a non-Microsoft Unicode cmap, use it, and use the // Unicode indexes, not the char codes. // 1b. If the PDF font specified MacRomanEncoding and the // TrueType font has a Macintosh Roman cmap, use it, and // reverse map the char names through MacRomanEncoding to // get char codes. // 1c. If the PDF font is symbolic and the TrueType font has a // Microsoft Symbol cmap, use it, and use char codes // directly (possibly with an offset of 0xf000). // 1d. If the TrueType font has a Macintosh Roman cmap, use it, // as in case 1a. // 2. If the PDF font does not have an encoding or the PDF font is // symbolic: // 2a. If the TrueType font has a Macintosh Roman cmap, use it, // and use char codes directly (possibly with an offset of // 0xf000). // 2b. If the TrueType font has a Microsoft Symbol cmap, use it, // and use char codes directly (possible with an offset of // 0xf000). // 3. If none of these rules apply, use the first cmap and hope for // the best (this shouldn't happen). unicodeCmap = macRomanCmap = msSymbolCmap = -1; for (i = 0; i < ff->getNumCmaps(); ++i) { cmapPlatform = ff->getCmapPlatform(i); cmapEncoding = ff->getCmapEncoding(i); if ((cmapPlatform == 3 && cmapEncoding == 1) || cmapPlatform == 0) { unicodeCmap = i; } else if (cmapPlatform == 1 && cmapEncoding == 0) { macRomanCmap = i; } else if (cmapPlatform == 3 && cmapEncoding == 0) { msSymbolCmap = i; } } cmap = 0; useMacRoman = false; useUnicode = false; if (hasEncoding || type == fontType1) { if (unicodeCmap >= 0) { cmap = unicodeCmap; useUnicode = true; } else if (usesMacRomanEnc && macRomanCmap >= 0) { cmap = macRomanCmap; useMacRoman = true; } else if ((flags & fontSymbolic) && msSymbolCmap >= 0) { cmap = msSymbolCmap; } else if ((flags & fontSymbolic) && macRomanCmap >= 0) { cmap = macRomanCmap; } else if (macRomanCmap >= 0) { cmap = macRomanCmap; useMacRoman = true; } } else { if (msSymbolCmap >= 0) { cmap = msSymbolCmap; } else if (macRomanCmap >= 0) { cmap = macRomanCmap; } } // reverse map the char names through MacRomanEncoding, then map the // char codes through the cmap if (useMacRoman) { for (i = 0; i < 256; ++i) { if ((charName = enc[i])) { if ((code = globalParams->getMacRomanCharCode(charName))) { map[i] = ff->mapCodeToGID(cmap, code); } } else { map[i] = -1; } } // map Unicode through the cmap } else if (useUnicode) { const Unicode *uAux; for (i = 0; i < 256; ++i) { if (((charName = enc[i]) && (u = globalParams->mapNameToUnicodeAll(charName)))) { map[i] = ff->mapCodeToGID(cmap, u); } else { n = ctu->mapToUnicode((CharCode)i, &uAux); if (n > 0) { map[i] = ff->mapCodeToGID(cmap, uAux[0]); } else { map[i] = -1; } } } // map the char codes through the cmap, possibly with an offset of // 0xf000 } else { for (i = 0; i < 256; ++i) { if (!(map[i] = ff->mapCodeToGID(cmap, i))) { map[i] = ff->mapCodeToGID(cmap, 0xf000 + i); } } } // try the TrueType 'post' table to handle any unmapped characters for (i = 0; i < 256; ++i) { if (map[i] <= 0 && (charName = enc[i])) { map[i] = ff->mapNameToGID(charName); } } return map; } Dict *Gfx8BitFont::getCharProcs() { return charProcs.isDict() ? charProcs.getDict() : nullptr; } Object Gfx8BitFont::getCharProc(int code) { if (enc[code] && charProcs.isDict()) { return charProcs.dictLookup(enc[code]); } else { return Object(objNull); } } Object Gfx8BitFont::getCharProcNF(int code) { if (enc[code] && charProcs.isDict()) { return charProcs.dictLookupNF(enc[code]).copy(); } else { return Object(objNull); } } Dict *Gfx8BitFont::getResources() { return resources.isDict() ? resources.getDict() : nullptr; } //------------------------------------------------------------------------ // GfxCIDFont //------------------------------------------------------------------------ struct cmpWidthExcepFunctor { bool operator()(const GfxFontCIDWidthExcep w1, const GfxFontCIDWidthExcep w2) { return w1.first < w2.first; } }; struct cmpWidthExcepVFunctor { bool operator()(const GfxFontCIDWidthExcepV &w1, const GfxFontCIDWidthExcepV &w2) { return w1.first < w2.first; } }; GfxCIDFont::GfxCIDFont(XRef *xref, const char *tagA, Ref idA, std::optional &&nameA, GfxFontType typeA, Ref embFontIDA, Dict *fontDict) : GfxFont(tagA, idA, std::move(nameA), typeA, embFontIDA) { Dict *desFontDict; Object desFontDictObj; Object obj1, obj2, obj3, obj4, obj5, obj6; int c1, c2; int excepsSize; ascent = 0.95; descent = -0.35; fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0; collection = nullptr; ctu = nullptr; ctuUsesCharCode = true; widths.defWidth = 1.0; widths.defHeight = -1.0; widths.defVY = 0.880; widths.exceps = nullptr; widths.nExceps = 0; widths.excepsV = nullptr; widths.nExcepsV = 0; cidToGID = nullptr; cidToGIDLen = 0; // get the descendant font obj1 = fontDict->lookup("DescendantFonts"); if (!obj1.isArray() || obj1.arrayGetLength() == 0) { error(errSyntaxError, -1, "Missing or empty DescendantFonts entry in Type 0 font"); return; } desFontDictObj = obj1.arrayGet(0); if (!desFontDictObj.isDict()) { error(errSyntaxError, -1, "Bad descendant font in Type 0 font"); return; } desFontDict = desFontDictObj.getDict(); // get info from font descriptor readFontDescriptor(xref, desFontDict); //----- encoding info ----- // char collection obj1 = desFontDict->lookup("CIDSystemInfo"); if (!obj1.isDict()) { 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; } collection = obj2.getString()->copy()->append('-')->append(obj3.getString()); // look for a ToUnicode CMap if (!(ctu = readToUnicodeCMap(fontDict, 16, nullptr))) { ctuUsesCharCode = false; // use an identity mapping for the "Adobe-Identity" and // "Adobe-UCS" collections if (!collection->cmp("Adobe-Identity") || !collection->cmp("Adobe-UCS")) { ctu = CharCodeToUnicode::makeIdentityMapping(); } else { // look for a user-supplied .cidToUnicode file if (!(ctu = globalParams->getCIDToUnicode(collection))) { // 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 // we know that assuming the Identity mapping is definitely wrong. // -- jrmuizel static const char *knownCollections[] = { "Adobe-CNS1", "Adobe-GB1", "Adobe-Japan1", "Adobe-Japan2", "Adobe-Korea1", }; for (const char *knownCollection : knownCollections) { if (collection->cmp(knownCollection) == 0) { error(errSyntaxError, -1, "Missing language pack for '{0:t}' mapping", collection); return; } } error(errSyntaxError, -1, "Unknown character collection '{0:t}'", collection); // fall-through, assuming the Identity mapping -- this appears // to match Adobe's behavior } } } // encoding (i.e., CMap) obj1 = fontDict->lookup("Encoding"); if (obj1.isNull()) { error(errSyntaxError, -1, "Missing Encoding entry in Type 0 font"); return; } if (!(cMap = CMap::parse(nullptr, collection, &obj1))) { return; } if (cMap->getCMapName()) { encodingName = cMap->getCMapName()->toStr(); } else { encodingName = "Custom"; } // CIDToGIDMap (for embedded TrueType fonts) obj1 = desFontDict->lookup("CIDToGIDMap"); if (obj1.isStream()) { cidToGIDLen = 0; unsigned int i = 64; cidToGID = (int *)gmallocn(i, sizeof(int)); 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; } } else if (!obj1.isName("Identity") && !obj1.isNull()) { error(errSyntaxError, -1, "Invalid CIDToGIDMap entry in CID font"); } //----- character metrics ----- // default char width obj1 = desFontDict->lookup("DW"); if (obj1.isInt()) { widths.defWidth = obj1.getInt() * 0.001; } // char width exceptions obj1 = desFontDict->lookup("W"); 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 + 2 < obj1.arrayGetLength()) { obj4 = obj1.arrayGet(i + 2); if (obj4.isNum()) { if (widths.nExceps == excepsSize) { excepsSize += 16; widths.exceps = (GfxFontCIDWidthExcep *)greallocn(widths.exceps, excepsSize, sizeof(GfxFontCIDWidthExcep)); } widths.exceps[widths.nExceps].first = obj2.getInt(); widths.exceps[widths.nExceps].last = obj3.getInt(); widths.exceps[widths.nExceps].width = obj4.getNum() * 0.001; ++widths.nExceps; } 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; ++j; ++widths.nExceps; } else { error(errSyntaxError, -1, "Bad widths array in Type 0 font"); } } } i += 2; } else { error(errSyntaxError, -1, "Bad widths array in Type 0 font"); ++i; } } std::sort(widths.exceps, widths.exceps + widths.nExceps, cmpWidthExcepFunctor()); } // default metrics for vertical font obj1 = desFontDict->lookup("DW2"); if (obj1.isArray() && obj1.arrayGetLength() == 2) { obj2 = obj1.arrayGet(0); if (obj2.isNum()) { widths.defVY = obj2.getNum() * 0.001; } obj2 = obj1.arrayGet(1); if (obj2.isNum()) { widths.defHeight = obj2.getNum() * 0.001; } } // char metric exceptions for vertical font obj1 = desFontDict->lookup("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; } 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; ++j; ++widths.nExcepsV; } else { error(errSyntaxError, -1, "Bad widths (W2) array in Type 0 font"); } } i += 2; } else { error(errSyntaxError, -1, "Bad widths (W2) array in Type 0 font"); ++i; } } std::sort(widths.excepsV, widths.excepsV + widths.nExcepsV, cmpWidthExcepVFunctor()); } ok = true; } GfxCIDFont::~GfxCIDFont() { if (collection) { delete collection; } if (ctu) { ctu->decRefCnt(); } gfree(widths.exceps); gfree(widths.excepsV); if (cidToGID) { gfree(cidToGID); } } int GfxCIDFont::getNextChar(const char *s, int len, CharCode *code, Unicode const **u, int *uLen, double *dx, double *dy, double *ox, double *oy) const { CID cid; CharCode dummy; double w, h, vx, vy; int n, a, b, m; if (!cMap) { *code = 0; *uLen = 0; *dx = *dy = *ox = *oy = 0; return 1; } *code = (CharCode)(cid = cMap->getCID(s, len, &dummy, &n)); if (ctu) { if (hasToUnicode) { int i = 0, c = 0; while (i < n) { c = (c << 8) + (s[i] & 0xff); ++i; } *uLen = ctu->mapToUnicode(c, u); } else { *uLen = ctu->mapToUnicode(cid, u); } } else { *uLen = 0; } // horizontal if (cMap->getWMode() == 0) { w = getWidth(cid); h = vx = vy = 0; // vertical } else { w = 0; h = widths.defHeight; vx = getWidth(cid) / 2; vy = widths.defVY; if (widths.nExcepsV > 0 && cid >= widths.excepsV[0].first) { a = 0; b = widths.nExcepsV; // invariant: widths.excepsV[a].first <= cid < widths.excepsV[b].first while (b - a > 1) { m = (a + b) / 2; if (widths.excepsV[m].last <= cid) { a = m; } else { b = m; } } if (cid <= widths.excepsV[a].last) { h = widths.excepsV[a].height; vx = widths.excepsV[a].vx; vy = widths.excepsV[a].vy; } } } *dx = w; *dy = h; *ox = vx; *oy = vy; return n; } int GfxCIDFont::getWMode() const { return cMap ? cMap->getWMode() : 0; } const CharCodeToUnicode *GfxCIDFont::getToUnicode() const { return ctu; } const GooString *GfxCIDFont::getCollection() const { return cMap ? cMap->getCollection() : nullptr; } int GfxCIDFont::mapCodeToGID(FoFiTrueType *ff, int cmapi, Unicode unicode, bool wmode) { unsigned short gid = ff->mapCodeToGID(cmapi, unicode); if (wmode) { unsigned short vgid = ff->mapToVertGID(gid); if (vgid != 0) { gid = vgid; } } return gid; } int *GfxCIDFont::getCodeToGIDMap(FoFiTrueType *ff, int *codeToGIDLen) { #define N_UCS_CANDIDATES 2 /* space characters */ static const unsigned long spaces[] = { 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x00A0, 0x200B, 0x2060, 0x3000, 0xFEFF, 0 }; static const char *adobe_cns1_cmaps[] = { "UniCNS-UTF32-V", "UniCNS-UCS2-V", "UniCNS-UTF32-H", "UniCNS-UCS2-H", nullptr }; static const char *adobe_gb1_cmaps[] = { "UniGB-UTF32-V", "UniGB-UCS2-V", "UniGB-UTF32-H", "UniGB-UCS2-H", nullptr }; static const char *adobe_japan1_cmaps[] = { "UniJIS-UTF32-V", "UniJIS-UCS2-V", "UniJIS-UTF32-H", "UniJIS-UCS2-H", nullptr }; static const char *adobe_japan2_cmaps[] = { "UniHojo-UTF32-V", "UniHojo-UCS2-V", "UniHojo-UTF32-H", "UniHojo-UCS2-H", nullptr }; static const char *adobe_korea1_cmaps[] = { "UniKS-UTF32-V", "UniKS-UCS2-V", "UniKS-UTF32-H", "UniKS-UCS2-H", nullptr }; static struct CMapListEntry { const char *collection; const char *scriptTag; const char *languageTag; const char *toUnicodeMap; const char **CMaps; } CMapList[] = { { "Adobe-CNS1", "hani", "CHN ", "Adobe-CNS1-UCS2", adobe_cns1_cmaps, }, { "Adobe-GB1", "hani", "CHN ", "Adobe-GB1-UCS2", adobe_gb1_cmaps, }, { "Adobe-Japan1", "kana", "JAN ", "Adobe-Japan1-UCS2", adobe_japan1_cmaps, }, { "Adobe-Japan2", "kana", "JAN ", "Adobe-Japan2-UCS2", adobe_japan2_cmaps, }, { "Adobe-Korea1", "hang", "KOR ", "Adobe-Korea1-UCS2", adobe_korea1_cmaps, }, { nullptr, nullptr, nullptr, nullptr, nullptr } }; Unicode *humap = nullptr; Unicode *vumap = nullptr; Unicode *tumap = nullptr; int *codeToGID = nullptr; int i; unsigned long code; int wmode; const char **cmapName; CMapListEntry *lp; int cmap; int cmapPlatform, cmapEncoding; Ref embID; *codeToGIDLen = 0; if (!ctu || !getCollection()) { return nullptr; } if (getEmbeddedFontID(&embID)) { if (getCollection()->cmp("Adobe-Identity") == 0) { return nullptr; } /* if this font is embedded font, * CIDToGIDMap should be embedded in PDF file * and already set. So return it. */ *codeToGIDLen = getCIDToGIDLen(); return getCIDToGID(); } /* we use only unicode cmap */ cmap = -1; for (i = 0; i < ff->getNumCmaps(); ++i) { cmapPlatform = ff->getCmapPlatform(i); cmapEncoding = ff->getCmapEncoding(i); if (cmapPlatform == 3 && cmapEncoding == 10) { /* UCS-4 */ cmap = i; /* use UCS-4 cmap */ break; } else if (cmapPlatform == 3 && cmapEncoding == 1) { /* Unicode */ cmap = i; } else if (cmapPlatform == 0 && cmap < 0) { cmap = i; } } if (cmap < 0) { return nullptr; } wmode = getWMode(); for (lp = CMapList; lp->collection != nullptr; lp++) { if (strcmp(lp->collection, getCollection()->c_str()) == 0) { break; } } const unsigned int n = 65536; 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) { tumap = new Unicode[n]; CharCode cid; for (cid = 0; cid < n; cid++) { int len; const Unicode *ucodes; len = tctu->mapToUnicode(cid, &ucodes); if (len == 1) { tumap[cid] = ucodes[0]; } else { /* if not single character, ignore it */ 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); std::shared_ptr cnameCMap; if ((cnameCMap = globalParams->getCMap(getCollection(), &cname)) != nullptr) { if (cnameCMap->getWMode()) { cnameCMap->setReverseMap(vumap, n, 1); } else { cnameCMap->setReverseMap(humap, n, N_UCS_CANDIDATES); } } } ff->setupGSUB(lp->scriptTag, lp->languageTag); } else { 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()); } if (ctu) { CharCode cid; for (cid = 0; cid < n; cid++) { const Unicode *ucode; if (ctu->mapToUnicode(cid, &ucode)) { humap[cid * N_UCS_CANDIDATES] = ucode[0]; } else { humap[cid * N_UCS_CANDIDATES] = 0; } for (i = 1; i < N_UCS_CANDIDATES; i++) { humap[cid * N_UCS_CANDIDATES + i] = 0; } } } } // map CID -> Unicode -> GID codeToGID = (int *)gmallocn(n, sizeof(int)); for (code = 0; code < n; ++code) { Unicode unicode; unsigned long gid; unicode = 0; gid = 0; if (humap != nullptr) { for (i = 0; i < N_UCS_CANDIDATES && gid == 0 && (unicode = humap[code * N_UCS_CANDIDATES + i]) != 0; i++) { gid = mapCodeToGID(ff, cmap, unicode, false); } } if (gid == 0 && vumap != nullptr) { unicode = vumap[code]; if (unicode != 0) { gid = mapCodeToGID(ff, cmap, unicode, true); if (gid == 0 && tumap != nullptr) { if ((unicode = tumap[code]) != 0) { gid = mapCodeToGID(ff, cmap, unicode, true); } } } } if (gid == 0 && tumap != nullptr) { if ((unicode = tumap[code]) != 0) { gid = mapCodeToGID(ff, cmap, unicode, false); } } if (gid == 0) { /* special handling space characters */ const unsigned long *p; if (humap != nullptr) { unicode = humap[code]; } if (unicode != 0) { /* check if code is space character , so map code to 0x0020 */ for (p = spaces; *p != 0; p++) { if (*p == unicode) { unicode = 0x20; gid = mapCodeToGID(ff, cmap, unicode, wmode); break; } } } } codeToGID[code] = gid; } *codeToGIDLen = n; if (humap != nullptr) { delete[] humap; } if (tumap != nullptr) { delete[] tumap; } if (vumap != nullptr) { delete[] vumap; } return codeToGID; } double GfxCIDFont::getWidth(CID cid) const { double w; int a, b, m; w = widths.defWidth; if (widths.nExceps > 0 && cid >= widths.exceps[0].first) { a = 0; b = widths.nExceps; // invariant: widths.exceps[a].first <= cid < widths.exceps[b].first while (b - a > 1) { m = (a + b) / 2; if (widths.exceps[m].first <= cid) { a = m; } else { b = m; } } if (cid <= widths.exceps[a].last) { w = widths.exceps[a].width; } } return w; } double GfxCIDFont::getWidth(char *s, int len) const { int nUsed; CharCode c; CID cid = cMap->getCID(s, len, &c, &nUsed); return getWidth(cid); } //------------------------------------------------------------------------ // GfxFontDict //------------------------------------------------------------------------ GfxFontDict::GfxFontDict(XRef *xref, Ref *fontDictRef, Dict *fontDict) { Ref r; fonts.resize(fontDict->getLength()); for (std::size_t i = 0; i < fonts.size(); ++i) { const Object &obj1 = fontDict->getValNF(i); Object obj2 = obj1.fetch(xref); if (obj2.isDict()) { if (obj1.isRef()) { r = obj1.getRef(); } else if (fontDictRef) { // legal generation numbers are five digits, so we use a // 6-digit number here r.gen = 100000 + fontDictRef->num; r.num = i; } else { // no indirect reference for this font, or for the containing // font dict, so hash the font and use that r.gen = 100000; r.num = hashFontObject(&obj2); } fonts[i] = GfxFont::makeFont(xref, fontDict->getKey(i), r, obj2.getDict()); if (fonts[i] && !fonts[i]->isOk()) { // XXX: it may be meaningful to distinguish between // NULL and !isOk() so that when we do lookups // we can tell the difference between a missing font // and a font that is just !isOk() fonts[i].reset(); } } else { error(errSyntaxError, -1, "font resource is not a dictionary"); fonts[i] = nullptr; } } } std::shared_ptr GfxFontDict::lookup(const char *tag) const { for (const auto &font : fonts) { if (font && font->matches(tag)) { return font; } } return nullptr; } // FNV-1a hash class FNVHash { public: FNVHash() { h = 2166136261U; } void hash(char c) { h ^= c & 0xff; h *= 16777619; } void hash(const char *p, int n) { int i; for (i = 0; i < n; ++i) { hash(p[i]); } } int get31() { return (h ^ (h >> 31)) & 0x7fffffff; } private: unsigned int h; }; int GfxFontDict::hashFontObject(Object *obj) { FNVHash h; hashFontObject1(obj, &h); return h.get31(); } void GfxFontDict::hashFontObject1(const Object *obj, FNVHash *h) { const GooString *s; const char *p; double r; int n, i; switch (obj->getType()) { case objBool: h->hash('b'); h->hash(obj->getBool() ? 1 : 0); break; case objInt: h->hash('i'); n = obj->getInt(); h->hash((char *)&n, sizeof(int)); break; case objReal: h->hash('r'); r = obj->getReal(); h->hash((char *)&r, sizeof(double)); break; case objString: h->hash('s'); s = obj->getString(); h->hash(s->c_str(), s->getLength()); break; case objName: h->hash('n'); p = obj->getName(); h->hash(p, (int)strlen(p)); break; case objNull: h->hash('z'); break; case objArray: h->hash('a'); n = obj->arrayGetLength(); h->hash((char *)&n, sizeof(int)); for (i = 0; i < n; ++i) { const Object &obj2 = obj->arrayGetNF(i); hashFontObject1(&obj2, h); } break; case objDict: h->hash('d'); n = obj->dictGetLength(); h->hash((char *)&n, sizeof(int)); for (i = 0; i < n; ++i) { p = obj->dictGetKey(i); h->hash(p, (int)strlen(p)); const Object &obj2 = obj->dictGetValNF(i); hashFontObject1(&obj2, h); } break; case objStream: // this should never happen - streams must be indirect refs break; case objRef: h->hash('f'); n = obj->getRefNum(); h->hash((char *)&n, sizeof(int)); n = obj->getRefGen(); h->hash((char *)&n, sizeof(int)); break; default: h->hash('u'); break; } } poppler-24.02.0/poppler/GfxFont.h000066400000000000000000000371741455701731300165670ustar00rootroot00000000000000//======================================================================== // // GfxFont.h // // 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) 2005, 2008, 2015, 2017-2022 Albert Astals Cid // Copyright (C) 2006 Takashi Iwai // Copyright (C) 2006 Kristian Høgsberg // Copyright (C) 2007 Julien Rebetez // Copyright (C) 2007 Jeff Muizelaar // Copyright (C) 2007 Koji Otani // Copyright (C) 2011 Axel Strübing // Copyright (C) 2011, 2012, 2014 Adrian Johnson // 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 // // 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 GFXFONT_H #define GFXFONT_H #include #include #include "goo/GooString.h" #include "Object.h" #include "CharTypes.h" #include "poppler_private_export.h" class Dict; class CMap; class CharCodeToUnicode; class FoFiTrueType; class PSOutputDev; struct GfxFontCIDWidths; struct Base14FontMapEntry; class FNVHash; //------------------------------------------------------------------------ // GfxFontType //------------------------------------------------------------------------ enum GfxFontType { //----- Gfx8BitFont fontUnknownType, fontType1, fontType1C, fontType1COT, fontType3, fontTrueType, fontTrueTypeOT, //----- GfxCIDFont fontCIDType0, fontCIDType0C, fontCIDType0COT, fontCIDType2, fontCIDType2OT }; //------------------------------------------------------------------------ // GfxFontCIDWidths //------------------------------------------------------------------------ struct GfxFontCIDWidthExcep { CID first; // this record applies to CID last; // CIDs .. double width; // char width }; struct GfxFontCIDWidthExcepV { CID first; // this record applies to CID last; // CIDs .. double height; // char height double vx, vy; // origin position }; 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 }; //------------------------------------------------------------------------ // GfxFontLoc //------------------------------------------------------------------------ enum GfxFontLocType { gfxFontLocEmbedded, // font embedded in PDF file gfxFontLocExternal, // external font file gfxFontLocResident // font resident in PS printer }; class POPPLER_PRIVATE_EXPORT GfxFontLoc { public: GfxFontLoc(); ~GfxFontLoc(); GfxFontLoc(const GfxFontLoc &) = delete; GfxFontLoc(GfxFontLoc &&) noexcept; 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 // (if locType == gfxFontLocEmbedded) std::string path; // font file path // (if locType == gfxFontLocExternal) // PS font name // (if locType == gfxFontLocResident) int fontNum; // for TrueType collections // (if locType == gfxFontLocExternal) int substIdx; // substitute font index // (if locType == gfxFontLocExternal, // and a Base-14 substitution was made) }; //------------------------------------------------------------------------ // GfxFont //------------------------------------------------------------------------ #define fontFixedWidth (1 << 0) #define fontSerif (1 << 1) #define fontSymbolic (1 << 2) #define fontItalic (1 << 6) #define fontBold (1 << 18) class POPPLER_PRIVATE_EXPORT GfxFont { public: enum Stretch { StretchNotDefined, UltraCondensed, ExtraCondensed, Condensed, SemiCondensed, Normal, SemiExpanded, Expanded, ExtraExpanded, UltraExpanded }; enum Weight { WeightNotDefined, W100, W200, W300, W400, // Normal W500, W600, W700, // Bold W800, W900 }; // Build a GfxFont object. static std::unique_ptr makeFont(XRef *xref, const char *tagA, Ref idA, Dict *fontDict); GfxFont(const GfxFont &) = delete; GfxFont &operator=(const GfxFont &other) = delete; virtual ~GfxFont(); bool isOk() const { return ok; } // Get font tag. const std::string &getTag() const { return tag; } // Get font dictionary ID. const Ref *getID() const { return &id; } // Does this font match the tag? bool matches(const char *tagA) const { return tag == tagA; } // Get font family name. GooString *getFamily() const { return family; } // Get font stretch. Stretch getStretch() const { return stretch; } // Get font weight. Weight getWeight() const { return weight; } // Get the original font name (ignornig any munging that might have // been done to map to a canonical Base-14 font name). const std::optional &getName() const { return name; } bool isSubset() const; // Returns the original font name without the subset tag (if it has one) std::string getNameWithoutSubsetTag() const; // Get font type. GfxFontType getType() const { return type; } virtual bool isCIDFont() const { return false; } // Get embedded font ID, i.e., a ref for the font file stream. // Returns false if there is no embedded font. bool getEmbeddedFontID(Ref *embID) const { *embID = embFontID; return embFontID != Ref::INVALID(); } // Invalidate an embedded font // Returns false if there is no embedded font. bool invalidateEmbeddedFont() { if (embFontID != Ref::INVALID()) { embFontID = Ref::INVALID(); return true; } return false; } // Get the PostScript font name for the embedded font. Returns // NULL if there is no embedded font. const GooString *getEmbeddedFontName() const { return embFontName; } // Get font descriptor flags. int getFlags() const { return 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; } // Return the Unicode map. virtual const CharCodeToUnicode *getToUnicode() const = 0; // Return the font matrix. const double *getFontMatrix() const { return fontMat; } // Return the font bounding box. const double *getFontBBox() const { return fontBBox; } // Return the ascent and descent values. double getAscent() const { return ascent; } double getDescent() const { return descent; } // Return the writing mode (0=horizontal, 1=vertical). virtual int getWMode() const { return 0; } // Locate the font file for this font. If is not null, includes PS // printer-resident fonts. Returns std::optional without a value on failure. // 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); // Get the next char from a string of bytes, returning the // char , its Unicode mapping , its displacement vector // (, ), and its origin offset vector (, ). // is the number of entries available in , and is set to // the number actually used. Returns the number of bytes used by // the char code. virtual int getNextChar(const char *s, int len, CharCode *code, Unicode const **u, int *uLen, double *dx, double *dy, double *ox, double *oy) const = 0; // Does this font have a toUnicode map? bool hasToUnicodeCMap() const { return hasToUnicode; } // Return the name of the encoding const std::string &getEncodingName() const { return encodingName; } // Return AGLFN names of ligatures in the Standard and Expert encodings // for use with fonts that are not compatible with the Standard 14 fonts. // http://sourceforge.net/adobe/aglfn/wiki/AGL%20Specification/ static const char *getAlternateName(const char *name); protected: GfxFont(const char *tagA, Ref idA, std::optional &&nameA, GfxFontType typeA, Ref embFontIDA); 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); const std::string tag; // PDF font tag const Ref id; // reference (used as unique ID) std::optional name; // font name GooString *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 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) double missingWidth; // "default" width double ascent; // max height above baseline double descent; // max depth below baseline bool ok; bool hasToUnicode; std::string encodingName; }; //------------------------------------------------------------------------ // Gfx8BitFont //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT Gfx8BitFont : public GfxFont { public: Gfx8BitFont(XRef *xref, const char *tagA, Ref idA, std::optional &&nameA, GfxFontType typeA, Ref embFontIDA, Dict *fontDict); int getNextChar(const char *s, int len, CharCode *code, Unicode const **u, int *uLen, double *dx, double *dy, double *ox, double *oy) const override; // Return the encoding. char **getEncoding() { return enc; } // Return the Unicode map. const CharCodeToUnicode *getToUnicode() const override; // Return the character name associated with . const char *getCharName(int code) const { return enc[code]; } // Returns true if the PDF font specified an encoding. bool getHasEncoding() const { return hasEncoding; } // Returns true if the PDF font specified MacRomanEncoding. bool getUsesMacRomanEnc() const { return usesMacRomanEnc; } // Get width of a character. double getWidth(unsigned char c) const { return widths[c]; } // Return a char code-to-GID mapping for the provided font file. // (This is only useful for TrueType fonts.) int *getCodeToGIDMap(FoFiTrueType *ff); // Return the Type 3 CharProc dictionary, or NULL if none. Dict *getCharProcs(); // Return the Type 3 CharProc for the character associated with . Object getCharProc(int code); Object getCharProcNF(int code); // Return the Type 3 Resources dictionary, or NULL if none. Dict *getResources(); private: ~Gfx8BitFont() override; 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 bool hasEncoding; bool usesMacRomanEnc; double widths[256]; // character widths Object charProcs; // Type 3 CharProcs dictionary Object resources; // Type 3 Resources dictionary friend class GfxFont; }; //------------------------------------------------------------------------ // GfxCIDFont //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT GfxCIDFont : public GfxFont { public: GfxCIDFont(XRef *xref, const char *tagA, Ref idA, std::optional &&nameA, GfxFontType typeA, Ref embFontIDA, Dict *fontDict); bool isCIDFont() const override { return true; } int getNextChar(const char *s, int len, CharCode *code, Unicode const **u, int *uLen, double *dx, double *dy, double *ox, double *oy) const override; // Return the writing mode (0=horizontal, 1=vertical). int getWMode() const override; // Return the Unicode map. const CharCodeToUnicode *getToUnicode() const override; // Get the collection name (-). const GooString *getCollection() const; // 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; } int *getCodeToGIDMap(FoFiTrueType *ff, int *codeToGIDLen); double getWidth(char *s, int len) const; private: ~GfxCIDFont() override; 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::shared_ptr cMap; // char code --> CID CharCodeToUnicode *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; }; //------------------------------------------------------------------------ // GfxFontDict //------------------------------------------------------------------------ class GfxFontDict { public: // Build the font dictionary, given the PDF font dictionary. GfxFontDict(XRef *xref, Ref *fontDictRef, Dict *fontDict); GfxFontDict(const GfxFontDict &) = delete; GfxFontDict &operator=(const GfxFontDict &) = delete; // Get the specified font. std::shared_ptr lookup(const char *tag) const; // Iterative access. int getNumFonts() const { return fonts.size(); } const std::shared_ptr &getFont(int i) const { return fonts[i]; } private: int hashFontObject(Object *obj); void hashFontObject1(const Object *obj, FNVHash *h); std::vector> fonts; }; #endif poppler-24.02.0/poppler/GfxState.cc000066400000000000000000006666601455701731300171070ustar00rootroot00000000000000//======================================================================== // // GfxState.cc // // 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) 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) 2009, 2012 Koji Otani // 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 // Copyright (C) 2011 Andrea Canciani // Copyright (C) 2012, 2020 William Bader // Copyright (C) 2013 Lu Wang // Copyright (C) 2013 Hib Eris // Copyright (C) 2013 Fabio D'Urso // Copyright (C) 2015, 2020 Adrian Johnson // Copyright (C) 2016 Marek Kasik // Copyright (C) 2017, 2019, 2022 Oliver Sander // 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 Volker Krause // Copyright (C) 2018, 2019 Adam Reichold // Copyright (C) 2019 LE GARREC Vincent // Copyright (C) 2020, 2021 Philipp Knechtges // Copyright (C) 2020 Lluís Batlle i Rossell // // 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 #include #include #include #include #include #include "goo/gfile.h" #include "goo/gmem.h" #include "Error.h" #include "Object.h" #include "Array.h" #include "Page.h" #include "Gfx.h" #include "GfxState.h" #include "GfxState_helpers.h" #include "GfxFont.h" #include "GlobalParams.h" #include "PopplerCache.h" #include "OutputDev.h" #include "splash/SplashTypes.h" //------------------------------------------------------------------------ // Max depth of nested color spaces. This is used to catch infinite // loops in the color space object structure. #define colorSpaceRecursionLimit 8 //------------------------------------------------------------------------ bool Matrix::invertTo(Matrix *other) const { const double det_denominator = determinant(); if (unlikely(det_denominator == 0)) { *other = { 1, 0, 0, 1, 0, 0 }; return false; } const double det = 1 / det_denominator; other->m[0] = m[3] * det; other->m[1] = -m[1] * det; other->m[2] = -m[2] * det; other->m[3] = m[0] * det; other->m[4] = (m[2] * m[5] - m[3] * m[4]) * det; other->m[5] = (m[1] * m[4] - m[0] * m[5]) * det; return true; } void Matrix::translate(double tx, double ty) { double x0 = tx * m[0] + ty * m[2] + m[4]; double y0 = tx * m[1] + ty * m[3] + m[5]; m[4] = x0; m[5] = y0; } void Matrix::scale(double sx, double sy) { m[0] *= sx; m[1] *= sx; m[2] *= sy; m[3] *= sy; } void Matrix::transform(double x, double y, double *tx, double *ty) const { double temp_x, temp_y; temp_x = m[0] * x + m[2] * y + m[4]; temp_y = m[1] * x + m[3] * y + m[5]; *tx = temp_x; *ty = temp_y; } // Matrix norm, taken from _cairo_matrix_transformed_circle_major_axis double Matrix::norm() const { double f, g, h, i, j; i = m[0] * m[0] + m[1] * m[1]; j = m[2] * m[2] + m[3] * m[3]; f = 0.5 * (i + j); g = 0.5 * (i - j); h = m[0] * m[2] + m[1] * m[3]; return sqrt(f + hypot(g, h)); } //------------------------------------------------------------------------ struct GfxBlendModeInfo { const char *name; GfxBlendMode mode; }; static const GfxBlendModeInfo gfxBlendModeNames[] = { { "Normal", gfxBlendNormal }, { "Compatible", gfxBlendNormal }, { "Multiply", gfxBlendMultiply }, { "Screen", gfxBlendScreen }, { "Overlay", gfxBlendOverlay }, { "Darken", gfxBlendDarken }, { "Lighten", gfxBlendLighten }, { "ColorDodge", gfxBlendColorDodge }, { "ColorBurn", gfxBlendColorBurn }, { "HardLight", gfxBlendHardLight }, { "SoftLight", gfxBlendSoftLight }, { "Difference", gfxBlendDifference }, { "Exclusion", gfxBlendExclusion }, { "Hue", gfxBlendHue }, { "Saturation", gfxBlendSaturation }, { "Color", gfxBlendColor }, { "Luminosity", gfxBlendLuminosity } }; #define nGfxBlendModeNames ((int)((sizeof(gfxBlendModeNames) / sizeof(GfxBlendModeInfo)))) //------------------------------------------------------------------------ // // 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" }; #define nGfxColorSpaceModes ((sizeof(gfxColorSpaceModeNames) / sizeof(char *))) #ifdef USE_CMS static const std::map::size_type CMSCACHE_LIMIT = 2048; # include # define LCMS_FLAGS cmsFLAGS_NOOPTIMIZE | cmsFLAGS_BLACKPOINTCOMPENSATION static void lcmsprofiledeleter(void *profile) { cmsCloseProfile(profile); } GfxLCMSProfilePtr make_GfxLCMSProfilePtr(void *profile) { if (profile == nullptr) { return GfxLCMSProfilePtr(); } return GfxLCMSProfilePtr(profile, lcmsprofiledeleter); } void GfxColorTransform::doTransform(void *in, void *out, unsigned int size) { cmsDoTransform(transform, in, out, size); } // transformA should be a cmsHTRANSFORM GfxColorTransform::GfxColorTransform(void *transformA, int cmsIntentA, unsigned int inputPixelTypeA, unsigned int transformPixelTypeA) { transform = transformA; cmsIntent = cmsIntentA; inputPixelType = inputPixelTypeA; transformPixelType = transformPixelTypeA; } GfxColorTransform::~GfxColorTransform() { cmsDeleteTransform(transform); } // convert color space signature to cmsColor type static unsigned int getCMSColorSpaceType(cmsColorSpaceSignature cs); static unsigned int getCMSNChannels(cmsColorSpaceSignature cs); #endif //------------------------------------------------------------------------ // GfxColorSpace //------------------------------------------------------------------------ GfxColorSpace::GfxColorSpace() { overprintMask = 0x0f; mapping = nullptr; } GfxColorSpace::~GfxColorSpace() { } GfxColorSpace *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; } 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(); } else { cs = GfxColorSpace::parse(nullptr, &objCS, out, state); } } else { cs = state->copyDefaultGrayColorSpace(); } } else if (csObj->isName("DeviceRGB") || csObj->isName("RGB")) { if (res != nullptr) { Object objCS = res->lookupColorSpace("DefaultRGB"); if (objCS.isNull()) { cs = state->copyDefaultRGBColorSpace(); } else { cs = GfxColorSpace::parse(nullptr, &objCS, out, state); } } else { cs = state->copyDefaultRGBColorSpace(); } } else if (csObj->isName("DeviceCMYK") || csObj->isName("CMYK")) { if (res != nullptr) { Object objCS = res->lookupColorSpace("DefaultCMYK"); if (objCS.isNull()) { cs = state->copyDefaultCMYKColorSpace(); } else { cs = GfxColorSpace::parse(nullptr, &objCS, out, state); } } else { cs = state->copyDefaultCMYKColorSpace(); } } else if (csObj->isName("Pattern")) { cs = new GfxPatternColorSpace(nullptr); } else { error(errSyntaxWarning, -1, "Bad color space '{0:s}'", csObj->getName()); } } else if (csObj->isArray() && csObj->arrayGetLength() > 0) { obj1 = csObj->arrayGet(0); if (obj1.isName("DeviceGray") || obj1.isName("G")) { if (res != nullptr) { Object objCS = res->lookupColorSpace("DefaultGray"); if (objCS.isNull()) { cs = state->copyDefaultGrayColorSpace(); } else { cs = GfxColorSpace::parse(nullptr, &objCS, out, state); } } else { cs = state->copyDefaultGrayColorSpace(); } } else if (obj1.isName("DeviceRGB") || obj1.isName("RGB")) { if (res != nullptr) { Object objCS = res->lookupColorSpace("DefaultRGB"); if (objCS.isNull()) { cs = state->copyDefaultRGBColorSpace(); } else { cs = GfxColorSpace::parse(nullptr, &objCS, out, state); } } else { cs = state->copyDefaultRGBColorSpace(); } } else if (obj1.isName("DeviceCMYK") || obj1.isName("CMYK")) { if (res != nullptr) { Object objCS = res->lookupColorSpace("DefaultCMYK"); if (objCS.isNull()) { cs = state->copyDefaultCMYKColorSpace(); } else { cs = GfxColorSpace::parse(nullptr, &objCS, out, state); } } else { cs = state->copyDefaultCMYKColorSpace(); } } else if (obj1.isName("CalGray")) { cs = GfxCalGrayColorSpace::parse(csObj->getArray(), state); } else if (obj1.isName("CalRGB")) { cs = GfxCalRGBColorSpace::parse(csObj->getArray(), state); } else if (obj1.isName("Lab")) { cs = GfxLabColorSpace::parse(csObj->getArray(), state); } else if (obj1.isName("ICCBased")) { cs = GfxICCBasedColorSpace::parse(csObj->getArray(), out, state, recursion); } else if (obj1.isName("Indexed") || obj1.isName("I")) { cs = GfxIndexedColorSpace::parse(res, csObj->getArray(), out, state, recursion); } else if (obj1.isName("Separation")) { cs = GfxSeparationColorSpace::parse(res, csObj->getArray(), out, state, recursion); } else if (obj1.isName("DeviceN")) { cs = GfxDeviceNColorSpace::parse(res, csObj->getArray(), out, state, recursion); } else if (obj1.isName("Pattern")) { cs = GfxPatternColorSpace::parse(res, csObj->getArray(), out, state, recursion); } else { error(errSyntaxWarning, -1, "Bad color space"); } } else if (csObj->isDict()) { obj1 = csObj->dictLookup("ColorSpace"); if (obj1.isName("DeviceGray")) { if (res != nullptr) { Object objCS = res->lookupColorSpace("DefaultGray"); if (objCS.isNull()) { cs = state->copyDefaultGrayColorSpace(); } else { cs = GfxColorSpace::parse(nullptr, &objCS, out, state); } } else { cs = state->copyDefaultGrayColorSpace(); } } else if (obj1.isName("DeviceRGB")) { if (res != nullptr) { Object objCS = res->lookupColorSpace("DefaultRGB"); if (objCS.isNull()) { cs = state->copyDefaultRGBColorSpace(); } else { cs = GfxColorSpace::parse(nullptr, &objCS, out, state); } } else { cs = state->copyDefaultRGBColorSpace(); } } else if (obj1.isName("DeviceCMYK")) { if (res != nullptr) { Object objCS = res->lookupColorSpace("DefaultCMYK"); if (objCS.isNull()) { cs = state->copyDefaultCMYKColorSpace(); } else { cs = GfxColorSpace::parse(nullptr, &objCS, out, state); } } else { cs = state->copyDefaultCMYKColorSpace(); } } else { error(errSyntaxWarning, -1, "Bad color space dict'"); } } else { error(errSyntaxWarning, -1, "Bad color space - expected name or array or dict"); } return cs; } void GfxColorSpace::createMapping(std::vector *separationList, int maxSepComps) { return; } void GfxColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange, int maxImgPixel) const { int i; for (i = 0; i < getNComps(); ++i) { decodeLow[i] = 0; decodeRange[i] = 1; } } int GfxColorSpace::getNumColorSpaceModes() { return nGfxColorSpaceModes; } const char *GfxColorSpace::getColorSpaceModeName(int idx) { return gfxColorSpaceModeNames[idx]; } #ifdef USE_CMS static void CMSError(cmsContext /*contextId*/, cmsUInt32Number /*ecode*/, const char *text) { error(errSyntaxWarning, -1, "{0:s}", text); } unsigned int getCMSColorSpaceType(cmsColorSpaceSignature cs) { switch (cs) { case cmsSigXYZData: return PT_XYZ; break; case cmsSigLabData: return PT_Lab; break; case cmsSigLuvData: return PT_YUV; break; case cmsSigYCbCrData: return PT_YCbCr; break; case cmsSigYxyData: return PT_Yxy; break; case cmsSigRgbData: return PT_RGB; break; case cmsSigGrayData: return PT_GRAY; break; case cmsSigHsvData: return PT_HSV; break; case cmsSigHlsData: return PT_HLS; break; case cmsSigCmykData: return PT_CMYK; break; case cmsSigCmyData: return PT_CMY; break; case cmsSig2colorData: case cmsSig3colorData: case cmsSig4colorData: case cmsSig5colorData: case cmsSig6colorData: case cmsSig7colorData: case cmsSig8colorData: case cmsSig9colorData: case cmsSig10colorData: case cmsSig11colorData: case cmsSig12colorData: case cmsSig13colorData: case cmsSig14colorData: case cmsSig15colorData: default: break; } return PT_RGB; } unsigned int getCMSNChannels(cmsColorSpaceSignature cs) { switch (cs) { case cmsSigXYZData: case cmsSigLuvData: case cmsSigLabData: case cmsSigYCbCrData: case cmsSigYxyData: case cmsSigRgbData: case cmsSigHsvData: case cmsSigHlsData: case cmsSigCmyData: case cmsSig3colorData: return 3; break; case cmsSigGrayData: return 1; break; case cmsSigCmykData: case cmsSig4colorData: return 4; break; case cmsSig2colorData: return 2; break; case cmsSig5colorData: return 5; break; case cmsSig6colorData: return 6; break; case cmsSig7colorData: return 7; break; case cmsSig8colorData: return 8; break; case cmsSig9colorData: return 9; break; case cmsSig10colorData: return 10; break; case cmsSig11colorData: return 11; break; case cmsSig12colorData: return 12; break; case cmsSig13colorData: return 13; break; case cmsSig14colorData: return 14; break; case cmsSig15colorData: return 15; default: break; } return 3; } #endif //------------------------------------------------------------------------ // GfxDeviceGrayColorSpace //------------------------------------------------------------------------ GfxDeviceGrayColorSpace::GfxDeviceGrayColorSpace() { } GfxDeviceGrayColorSpace::~GfxDeviceGrayColorSpace() { } GfxColorSpace *GfxDeviceGrayColorSpace::copy() const { return new GfxDeviceGrayColorSpace(); } void GfxDeviceGrayColorSpace::getGray(const GfxColor *color, GfxGray *gray) const { *gray = clip01(color->c[0]); } void GfxDeviceGrayColorSpace::getGrayLine(unsigned char *in, unsigned char *out, int length) { memcpy(out, in, length); } void GfxDeviceGrayColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const { rgb->r = rgb->g = rgb->b = clip01(color->c[0]); } void GfxDeviceGrayColorSpace::getRGBLine(unsigned char *in, unsigned int *out, int length) { int i; for (i = 0; i < length; i++) { out[i] = (in[i] << 16) | (in[i] << 8) | (in[i] << 0); } } void GfxDeviceGrayColorSpace::getRGBLine(unsigned char *in, unsigned char *out, int length) { for (int i = 0; i < length; i++) { *out++ = in[i]; *out++ = in[i]; *out++ = in[i]; } } void GfxDeviceGrayColorSpace::getRGBXLine(unsigned char *in, unsigned char *out, int length) { for (int i = 0; i < length; i++) { *out++ = in[i]; *out++ = in[i]; *out++ = in[i]; *out++ = 255; } } void GfxDeviceGrayColorSpace::getCMYKLine(unsigned char *in, unsigned char *out, int length) { for (int i = 0; i < length; i++) { *out++ = 0; *out++ = 0; *out++ = 0; *out++ = in[i]; } } void GfxDeviceGrayColorSpace::getDeviceNLine(unsigned char *in, unsigned char *out, int length) { for (int i = 0; i < length; i++) { for (int j = 0; j < SPOT_NCOMPS + 4; j++) { out[j] = 0; } out[4] = in[i]; out += (SPOT_NCOMPS + 4); } } void GfxDeviceGrayColorSpace::getCMYK(const GfxColor *color, GfxCMYK *cmyk) const { cmyk->c = cmyk->m = cmyk->y = 0; cmyk->k = clip01(gfxColorComp1 - color->c[0]); } void GfxDeviceGrayColorSpace::getDeviceN(const GfxColor *color, GfxColor *deviceN) const { clearGfxColor(deviceN); deviceN->c[3] = clip01(gfxColorComp1 - color->c[0]); } void GfxDeviceGrayColorSpace::getDefaultColor(GfxColor *color) const { color->c[0] = 0; } //------------------------------------------------------------------------ // GfxCalGrayColorSpace //------------------------------------------------------------------------ GfxCalGrayColorSpace::GfxCalGrayColorSpace() { whiteX = whiteY = whiteZ = 1; blackX = blackY = blackZ = 0; gamma = 1; } GfxCalGrayColorSpace::~GfxCalGrayColorSpace() { } GfxColorSpace *GfxCalGrayColorSpace::copy() const { GfxCalGrayColorSpace *cs; cs = new GfxCalGrayColorSpace(); cs->whiteX = whiteX; cs->whiteY = whiteY; cs->whiteZ = whiteZ; cs->blackX = blackX; cs->blackY = blackY; cs->blackZ = blackZ; cs->gamma = gamma; #ifdef USE_CMS cs->transform = transform; #endif return cs; } // This is the inverse of MatrixLMN in Example 4.10 from the PostScript // Language Reference, Third Edition. static const double xyzrgb[3][3] = { { 3.240449, -1.537136, -0.498531 }, { -0.969265, 1.876011, 0.041556 }, { 0.055643, -0.204026, 1.057229 } }; // From the same reference as above, the inverse of the DecodeLMN function. // This is essentially the gamma function of the sRGB profile. static double srgb_gamma_function(double x) { // 0.04045 is what lcms2 uses, but the PS Reference Example 4.10 specifies 0.03928??? // if (x <= 0.04045 / 12.92321) { if (x <= 0.03928 / 12.92321) { return x * 12.92321; } return 1.055 * pow(x, 1.0 / 2.4) - 0.055; } // D65 is the white point of the sRGB profile as it is specified above in the xyzrgb array static const double white_d65_X = 0.9505; static const double white_d65_Y = 1.0; static const double white_d65_Z = 1.0890; #ifdef USE_CMS // D50 is the default white point as used in ICC profiles and in the lcms2 library static const double white_d50_X = 0.96422; static const double white_d50_Y = 1.0; static const double white_d50_Z = 0.82521; static void inline bradford_transform_to_d50(double &X, double &Y, double &Z, const double source_whiteX, const double source_whiteY, const double source_whiteZ) { if (source_whiteX == white_d50_X && source_whiteY == white_d50_Y && source_whiteZ == white_d50_Z) { // early exit if noop return; } // at first apply Bradford matrix double rho_in = 0.8951000 * X + 0.2664000 * Y - 0.1614000 * Z; double gamma_in = -0.7502000 * X + 1.7135000 * Y + 0.0367000 * Z; double beta_in = 0.0389000 * X - 0.0685000 * Y + 1.0296000 * Z; // apply a diagonal matrix with the diagonal entries being the inverse bradford-transformed white point rho_in /= 0.8951000 * source_whiteX + 0.2664000 * source_whiteY - 0.1614000 * source_whiteZ; gamma_in /= -0.7502000 * source_whiteX + 1.7135000 * source_whiteY + 0.0367000 * source_whiteZ; beta_in /= 0.0389000 * source_whiteX - 0.0685000 * source_whiteY + 1.0296000 * source_whiteZ; // now revert the two steps above, but substituting the source white point by the device white point (D50) // Since the white point is known a priori this has been combined into a single operation. X = 0.98332566 * rho_in - 0.15005819 * gamma_in + 0.13095252 * beta_in; Y = 0.43069901 * rho_in + 0.52894900 * gamma_in + 0.04035199 * beta_in; Z = 0.00849698 * rho_in + 0.04086079 * gamma_in + 0.79284618 * beta_in; } #endif static void inline bradford_transform_to_d65(double &X, double &Y, double &Z, const double source_whiteX, const double source_whiteY, const double source_whiteZ) { if (source_whiteX == white_d65_X && source_whiteY == white_d65_Y && source_whiteZ == white_d65_Z) { // early exit if noop return; } // at first apply Bradford matrix double rho_in = 0.8951000 * X + 0.2664000 * Y - 0.1614000 * Z; double gamma_in = -0.7502000 * X + 1.7135000 * Y + 0.0367000 * Z; double beta_in = 0.0389000 * X - 0.0685000 * Y + 1.0296000 * Z; // apply a diagonal matrix with the diagonal entries being the inverse bradford-transformed white point rho_in /= 0.8951000 * source_whiteX + 0.2664000 * source_whiteY - 0.1614000 * source_whiteZ; gamma_in /= -0.7502000 * source_whiteX + 1.7135000 * source_whiteY + 0.0367000 * source_whiteZ; beta_in /= 0.0389000 * source_whiteX - 0.0685000 * source_whiteY + 1.0296000 * source_whiteZ; // now revert the two steps above, but substituting the source white point by the device white point (D65) // Since the white point is known a priori this has been combined into a single operation. X = 0.92918329 * rho_in - 0.15299782 * gamma_in + 0.17428453 * beta_in; Y = 0.40698452 * rho_in + 0.53931108 * gamma_in + 0.05370440 * beta_in; Z = -0.00802913 * rho_in + 0.04166125 * gamma_in + 1.05519788 * beta_in; } GfxColorSpace *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; } cs = new GfxCalGrayColorSpace(); obj2 = obj1.dictLookup("WhitePoint"); if (obj2.isArray() && obj2.arrayGetLength() == 3) { cs->whiteX = obj2.arrayGet(0).getNumWithDefaultValue(1); cs->whiteY = obj2.arrayGet(1).getNumWithDefaultValue(1); cs->whiteZ = obj2.arrayGet(2).getNumWithDefaultValue(1); } obj2 = obj1.dictLookup("BlackPoint"); if (obj2.isArray() && obj2.arrayGetLength() == 3) { cs->blackX = obj2.arrayGet(0).getNumWithDefaultValue(0); cs->blackY = obj2.arrayGet(1).getNumWithDefaultValue(0); cs->blackZ = obj2.arrayGet(2).getNumWithDefaultValue(0); } cs->gamma = obj1.dictLookup("Gamma").getNumWithDefaultValue(1); #ifdef USE_CMS cs->transform = (state != nullptr) ? state->getXYZ2DisplayTransform() : nullptr; #endif return cs; } // convert CalGray to media XYZ color space // (not multiply by the white point) void GfxCalGrayColorSpace::getXYZ(const GfxColor *color, double *pX, double *pY, double *pZ) const { const double A = colToDbl(color->c[0]); const double xyzColor = pow(A, gamma); *pX = xyzColor; *pY = xyzColor; *pZ = xyzColor; } void GfxCalGrayColorSpace::getGray(const GfxColor *color, GfxGray *gray) const { GfxRGB rgb; #ifdef USE_CMS if (transform && transform->getTransformPixelType() == PT_GRAY) { unsigned char out[gfxColorMaxComps]; double in[gfxColorMaxComps]; double X, Y, Z; getXYZ(color, &X, &Y, &Z); bradford_transform_to_d50(X, Y, Z, whiteX, whiteY, whiteZ); in[0] = X; in[1] = Y; in[2] = Z; transform->doTransform(in, out, 1); *gray = byteToCol(out[0]); return; } #endif getRGB(color, &rgb); *gray = clip01((GfxColorComp)(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b + 0.5)); } void GfxCalGrayColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const { double X, Y, Z; double r, g, b; getXYZ(color, &X, &Y, &Z); #ifdef USE_CMS if (transform && transform->getTransformPixelType() == PT_RGB) { unsigned char out[gfxColorMaxComps]; double in[gfxColorMaxComps]; bradford_transform_to_d50(X, Y, Z, whiteX, whiteY, whiteZ); in[0] = X; in[1] = Y; in[2] = Z; transform->doTransform(in, out, 1); rgb->r = byteToCol(out[0]); rgb->g = byteToCol(out[1]); rgb->b = byteToCol(out[2]); return; } #endif bradford_transform_to_d65(X, Y, Z, whiteX, whiteY, whiteZ); // convert XYZ to RGB, including gamut mapping and gamma correction r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z; g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z; b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z; rgb->r = dblToCol(srgb_gamma_function(clip01(r))); rgb->g = dblToCol(srgb_gamma_function(clip01(g))); rgb->b = dblToCol(srgb_gamma_function(clip01(b))); } void GfxCalGrayColorSpace::getCMYK(const GfxColor *color, GfxCMYK *cmyk) const { GfxRGB rgb; GfxColorComp c, m, y, k; #ifdef USE_CMS if (transform && transform->getTransformPixelType() == PT_CMYK) { double in[gfxColorMaxComps]; unsigned char out[gfxColorMaxComps]; double X, Y, Z; getXYZ(color, &X, &Y, &Z); bradford_transform_to_d50(X, Y, Z, whiteX, whiteY, whiteZ); in[0] = X; in[1] = Y; in[2] = Z; transform->doTransform(in, out, 1); cmyk->c = byteToCol(out[0]); cmyk->m = byteToCol(out[1]); cmyk->y = byteToCol(out[2]); cmyk->k = byteToCol(out[3]); return; } #endif getRGB(color, &rgb); c = clip01(gfxColorComp1 - rgb.r); m = clip01(gfxColorComp1 - rgb.g); y = clip01(gfxColorComp1 - rgb.b); k = c; if (m < k) { k = m; } if (y < k) { k = y; } cmyk->c = c - k; cmyk->m = m - k; cmyk->y = y - k; cmyk->k = k; } void GfxCalGrayColorSpace::getDeviceN(const GfxColor *color, GfxColor *deviceN) const { GfxCMYK cmyk; clearGfxColor(deviceN); getCMYK(color, &cmyk); deviceN->c[0] = cmyk.c; deviceN->c[1] = cmyk.m; deviceN->c[2] = cmyk.y; deviceN->c[3] = cmyk.k; } void GfxCalGrayColorSpace::getDefaultColor(GfxColor *color) const { color->c[0] = 0; } //------------------------------------------------------------------------ // GfxDeviceRGBColorSpace //------------------------------------------------------------------------ GfxDeviceRGBColorSpace::GfxDeviceRGBColorSpace() { } GfxDeviceRGBColorSpace::~GfxDeviceRGBColorSpace() { } GfxColorSpace *GfxDeviceRGBColorSpace::copy() const { return new GfxDeviceRGBColorSpace(); } void GfxDeviceRGBColorSpace::getGray(const GfxColor *color, GfxGray *gray) const { *gray = clip01((GfxColorComp)(0.3 * color->c[0] + 0.59 * color->c[1] + 0.11 * color->c[2] + 0.5)); } void GfxDeviceRGBColorSpace::getGrayLine(unsigned char *in, unsigned char *out, int length) { int i; for (i = 0; i < length; i++) { out[i] = (in[i * 3 + 0] * 19595 + in[i * 3 + 1] * 38469 + in[i * 3 + 2] * 7472) / 65536; } } void GfxDeviceRGBColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const { rgb->r = clip01(color->c[0]); rgb->g = clip01(color->c[1]); rgb->b = clip01(color->c[2]); } void GfxDeviceRGBColorSpace::getRGBLine(unsigned char *in, unsigned int *out, int length) { unsigned char *p; int i; for (i = 0, p = in; i < length; i++, p += 3) { out[i] = (p[0] << 16) | (p[1] << 8) | (p[2] << 0); } } void GfxDeviceRGBColorSpace::getRGBLine(unsigned char *in, unsigned char *out, int length) { for (int i = 0; i < length; i++) { *out++ = *in++; *out++ = *in++; *out++ = *in++; } } void GfxDeviceRGBColorSpace::getRGBXLine(unsigned char *in, unsigned char *out, int length) { for (int i = 0; i < length; i++) { *out++ = *in++; *out++ = *in++; *out++ = *in++; *out++ = 255; } } void GfxDeviceRGBColorSpace::getCMYKLine(unsigned char *in, unsigned char *out, int length) { GfxColorComp c, m, y, k; for (int i = 0; i < length; i++) { c = byteToCol(255 - *in++); m = byteToCol(255 - *in++); y = byteToCol(255 - *in++); k = c; if (m < k) { k = m; } if (y < k) { k = y; } *out++ = colToByte(c - k); *out++ = colToByte(m - k); *out++ = colToByte(y - k); *out++ = colToByte(k); } } void GfxDeviceRGBColorSpace::getDeviceNLine(unsigned char *in, unsigned char *out, int length) { GfxColorComp c, m, y, k; for (int i = 0; i < length; i++) { for (int j = 0; j < SPOT_NCOMPS + 4; j++) { out[j] = 0; } c = byteToCol(255 - *in++); m = byteToCol(255 - *in++); y = byteToCol(255 - *in++); k = c; if (m < k) { k = m; } if (y < k) { k = y; } out[0] = colToByte(c - k); out[1] = colToByte(m - k); out[2] = colToByte(y - k); out[3] = colToByte(k); out += (SPOT_NCOMPS + 4); } } void GfxDeviceRGBColorSpace::getCMYK(const GfxColor *color, GfxCMYK *cmyk) const { GfxColorComp c, m, y, k; c = clip01(gfxColorComp1 - color->c[0]); m = clip01(gfxColorComp1 - color->c[1]); y = clip01(gfxColorComp1 - color->c[2]); k = c; if (m < k) { k = m; } if (y < k) { k = y; } cmyk->c = c - k; cmyk->m = m - k; cmyk->y = y - k; cmyk->k = k; } void GfxDeviceRGBColorSpace::getDeviceN(const GfxColor *color, GfxColor *deviceN) const { GfxCMYK cmyk; clearGfxColor(deviceN); getCMYK(color, &cmyk); deviceN->c[0] = cmyk.c; deviceN->c[1] = cmyk.m; deviceN->c[2] = cmyk.y; deviceN->c[3] = cmyk.k; } void GfxDeviceRGBColorSpace::getDefaultColor(GfxColor *color) const { color->c[0] = 0; color->c[1] = 0; color->c[2] = 0; } //------------------------------------------------------------------------ // GfxCalRGBColorSpace //------------------------------------------------------------------------ GfxCalRGBColorSpace::GfxCalRGBColorSpace() { whiteX = whiteY = whiteZ = 1; blackX = blackY = blackZ = 0; gammaR = gammaG = gammaB = 1; mat[0] = 1; mat[1] = 0; mat[2] = 0; mat[3] = 0; mat[4] = 1; mat[5] = 0; mat[6] = 0; mat[7] = 0; mat[8] = 1; } GfxCalRGBColorSpace::~GfxCalRGBColorSpace() { } GfxColorSpace *GfxCalRGBColorSpace::copy() const { GfxCalRGBColorSpace *cs; int i; cs = new GfxCalRGBColorSpace(); cs->whiteX = whiteX; cs->whiteY = whiteY; cs->whiteZ = whiteZ; cs->blackX = blackX; cs->blackY = blackY; cs->blackZ = blackZ; cs->gammaR = gammaR; cs->gammaG = gammaG; cs->gammaB = gammaB; for (i = 0; i < 9; ++i) { cs->mat[i] = mat[i]; } #ifdef USE_CMS cs->transform = transform; #endif return cs; } GfxColorSpace *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; } cs = new GfxCalRGBColorSpace(); obj2 = obj1.dictLookup("WhitePoint"); if (obj2.isArray() && obj2.arrayGetLength() == 3) { cs->whiteX = obj2.arrayGet(0).getNumWithDefaultValue(1); cs->whiteY = obj2.arrayGet(1).getNumWithDefaultValue(1); cs->whiteZ = obj2.arrayGet(2).getNumWithDefaultValue(1); } obj2 = obj1.dictLookup("BlackPoint"); if (obj2.isArray() && obj2.arrayGetLength() == 3) { cs->blackX = obj2.arrayGet(0).getNumWithDefaultValue(0); cs->blackY = obj2.arrayGet(1).getNumWithDefaultValue(0); cs->blackZ = obj2.arrayGet(2).getNumWithDefaultValue(0); } obj2 = obj1.dictLookup("Gamma"); if (obj2.isArray() && obj2.arrayGetLength() == 3) { cs->gammaR = obj2.arrayGet(0).getNumWithDefaultValue(1); cs->gammaG = obj2.arrayGet(1).getNumWithDefaultValue(1); cs->gammaB = obj2.arrayGet(2).getNumWithDefaultValue(1); } obj2 = obj1.dictLookup("Matrix"); if (obj2.isArray() && obj2.arrayGetLength() == 9) { for (i = 0; i < 9; ++i) { Object obj3 = obj2.arrayGet(i); if (likely(obj3.isNum())) { cs->mat[i] = obj3.getNum(); } } } #ifdef USE_CMS cs->transform = (state != nullptr) ? state->getXYZ2DisplayTransform() : nullptr; #endif return cs; } // convert CalRGB to XYZ color space void GfxCalRGBColorSpace::getXYZ(const GfxColor *color, double *pX, double *pY, double *pZ) const { double A, B, C; A = pow(colToDbl(color->c[0]), gammaR); B = pow(colToDbl(color->c[1]), gammaG); C = pow(colToDbl(color->c[2]), gammaB); *pX = mat[0] * A + mat[3] * B + mat[6] * C; *pY = mat[1] * A + mat[4] * B + mat[7] * C; *pZ = mat[2] * A + mat[5] * B + mat[8] * C; } void GfxCalRGBColorSpace::getGray(const GfxColor *color, GfxGray *gray) const { GfxRGB rgb; #ifdef USE_CMS if (transform != nullptr && transform->getTransformPixelType() == PT_GRAY) { unsigned char out[gfxColorMaxComps]; double in[gfxColorMaxComps]; double X, Y, Z; getXYZ(color, &X, &Y, &Z); bradford_transform_to_d50(X, Y, Z, whiteX, whiteY, whiteZ); in[0] = X; in[1] = Y; in[2] = Z; transform->doTransform(in, out, 1); *gray = byteToCol(out[0]); return; } #endif getRGB(color, &rgb); *gray = clip01((GfxColorComp)(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b + 0.5)); } void GfxCalRGBColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const { double X, Y, Z; double r, g, b; getXYZ(color, &X, &Y, &Z); #ifdef USE_CMS if (transform != nullptr && transform->getTransformPixelType() == PT_RGB) { unsigned char out[gfxColorMaxComps]; double in[gfxColorMaxComps]; bradford_transform_to_d50(X, Y, Z, whiteX, whiteY, whiteZ); in[0] = X; in[1] = Y; in[2] = Z; transform->doTransform(in, out, 1); rgb->r = byteToCol(out[0]); rgb->g = byteToCol(out[1]); rgb->b = byteToCol(out[2]); return; } #endif bradford_transform_to_d65(X, Y, Z, whiteX, whiteY, whiteZ); // convert XYZ to RGB, including gamut mapping and gamma correction r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z; g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z; b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z; rgb->r = dblToCol(srgb_gamma_function(clip01(r))); rgb->g = dblToCol(srgb_gamma_function(clip01(g))); rgb->b = dblToCol(srgb_gamma_function(clip01(b))); } void GfxCalRGBColorSpace::getCMYK(const GfxColor *color, GfxCMYK *cmyk) const { GfxRGB rgb; GfxColorComp c, m, y, k; #ifdef USE_CMS if (transform != nullptr && transform->getTransformPixelType() == PT_CMYK) { double in[gfxColorMaxComps]; unsigned char out[gfxColorMaxComps]; double X, Y, Z; getXYZ(color, &X, &Y, &Z); bradford_transform_to_d50(X, Y, Z, whiteX, whiteY, whiteZ); in[0] = X; in[1] = Y; in[2] = Z; transform->doTransform(in, out, 1); cmyk->c = byteToCol(out[0]); cmyk->m = byteToCol(out[1]); cmyk->y = byteToCol(out[2]); cmyk->k = byteToCol(out[3]); return; } #endif getRGB(color, &rgb); c = clip01(gfxColorComp1 - rgb.r); m = clip01(gfxColorComp1 - rgb.g); y = clip01(gfxColorComp1 - rgb.b); k = c; if (m < k) { k = m; } if (y < k) { k = y; } cmyk->c = c - k; cmyk->m = m - k; cmyk->y = y - k; cmyk->k = k; } void GfxCalRGBColorSpace::getDeviceN(const GfxColor *color, GfxColor *deviceN) const { GfxCMYK cmyk; clearGfxColor(deviceN); getCMYK(color, &cmyk); deviceN->c[0] = cmyk.c; deviceN->c[1] = cmyk.m; deviceN->c[2] = cmyk.y; deviceN->c[3] = cmyk.k; } void GfxCalRGBColorSpace::getDefaultColor(GfxColor *color) const { color->c[0] = 0; color->c[1] = 0; color->c[2] = 0; } //------------------------------------------------------------------------ // GfxDeviceCMYKColorSpace //------------------------------------------------------------------------ GfxDeviceCMYKColorSpace::GfxDeviceCMYKColorSpace() { } GfxDeviceCMYKColorSpace::~GfxDeviceCMYKColorSpace() { } GfxColorSpace *GfxDeviceCMYKColorSpace::copy() const { return new GfxDeviceCMYKColorSpace(); } void GfxDeviceCMYKColorSpace::getGray(const GfxColor *color, GfxGray *gray) const { *gray = clip01((GfxColorComp)(gfxColorComp1 - color->c[3] - 0.3 * color->c[0] - 0.59 * color->c[1] - 0.11 * color->c[2] + 0.5)); } void GfxDeviceCMYKColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const { double c, m, y, k, c1, m1, y1, k1, r, g, b; c = colToDbl(color->c[0]); m = colToDbl(color->c[1]); y = colToDbl(color->c[2]); k = colToDbl(color->c[3]); c1 = 1 - c; m1 = 1 - m; y1 = 1 - y; k1 = 1 - k; cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b); rgb->r = clip01(dblToCol(r)); rgb->g = clip01(dblToCol(g)); rgb->b = clip01(dblToCol(b)); } static inline void GfxDeviceCMYKColorSpacegetRGBLineHelper(unsigned char *&in, double &r, double &g, double &b) { double c, m, y, k, c1, m1, y1, k1; c = byteToDbl(*in++); m = byteToDbl(*in++); y = byteToDbl(*in++); k = byteToDbl(*in++); c1 = 1 - c; m1 = 1 - m; y1 = 1 - y; k1 = 1 - k; cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b); } void GfxDeviceCMYKColorSpace::getRGBLine(unsigned char *in, unsigned int *out, int length) { double r, g, b; for (int i = 0; i < length; i++) { GfxDeviceCMYKColorSpacegetRGBLineHelper(in, r, g, b); *out++ = (dblToByte(clip01(r)) << 16) | (dblToByte(clip01(g)) << 8) | dblToByte(clip01(b)); } } void GfxDeviceCMYKColorSpace::getRGBLine(unsigned char *in, unsigned char *out, int length) { double r, g, b; for (int i = 0; i < length; i++) { GfxDeviceCMYKColorSpacegetRGBLineHelper(in, r, g, b); *out++ = dblToByte(clip01(r)); *out++ = dblToByte(clip01(g)); *out++ = dblToByte(clip01(b)); } } void GfxDeviceCMYKColorSpace::getRGBXLine(unsigned char *in, unsigned char *out, int length) { double r, g, b; for (int i = 0; i < length; i++) { GfxDeviceCMYKColorSpacegetRGBLineHelper(in, r, g, b); *out++ = dblToByte(clip01(r)); *out++ = dblToByte(clip01(g)); *out++ = dblToByte(clip01(b)); *out++ = 255; } } void GfxDeviceCMYKColorSpace::getCMYKLine(unsigned char *in, unsigned char *out, int length) { for (int i = 0; i < length; i++) { *out++ = *in++; *out++ = *in++; *out++ = *in++; *out++ = *in++; } } void GfxDeviceCMYKColorSpace::getDeviceNLine(unsigned char *in, unsigned char *out, int length) { for (int i = 0; i < length; i++) { for (int j = 0; j < SPOT_NCOMPS + 4; j++) { out[j] = 0; } out[0] = *in++; out[1] = *in++; out[2] = *in++; out[3] = *in++; out += (SPOT_NCOMPS + 4); } } void GfxDeviceCMYKColorSpace::getCMYK(const GfxColor *color, GfxCMYK *cmyk) const { cmyk->c = clip01(color->c[0]); cmyk->m = clip01(color->c[1]); cmyk->y = clip01(color->c[2]); cmyk->k = clip01(color->c[3]); } void GfxDeviceCMYKColorSpace::getDeviceN(const GfxColor *color, GfxColor *deviceN) const { clearGfxColor(deviceN); deviceN->c[0] = clip01(color->c[0]); deviceN->c[1] = clip01(color->c[1]); deviceN->c[2] = clip01(color->c[2]); deviceN->c[3] = clip01(color->c[3]); } void GfxDeviceCMYKColorSpace::getDefaultColor(GfxColor *color) const { color->c[0] = 0; color->c[1] = 0; color->c[2] = 0; color->c[3] = gfxColorComp1; } //------------------------------------------------------------------------ // GfxLabColorSpace //------------------------------------------------------------------------ GfxLabColorSpace::GfxLabColorSpace() { whiteX = whiteY = whiteZ = 1; blackX = blackY = blackZ = 0; aMin = bMin = -100; aMax = bMax = 100; } GfxLabColorSpace::~GfxLabColorSpace() { } GfxColorSpace *GfxLabColorSpace::copy() const { GfxLabColorSpace *cs; cs = new GfxLabColorSpace(); cs->whiteX = whiteX; cs->whiteY = whiteY; cs->whiteZ = whiteZ; cs->blackX = blackX; cs->blackY = blackY; cs->blackZ = blackZ; cs->aMin = aMin; cs->aMax = aMax; cs->bMin = bMin; cs->bMax = bMax; #ifdef USE_CMS cs->transform = transform; #endif return cs; } GfxColorSpace *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; } cs = new GfxLabColorSpace(); bool ok = true; obj2 = obj1.dictLookup("WhitePoint"); if (obj2.isArray() && obj2.arrayGetLength() == 3) { cs->whiteX = obj2.arrayGet(0).getNum(&ok); cs->whiteY = obj2.arrayGet(1).getNum(&ok); cs->whiteZ = obj2.arrayGet(2).getNum(&ok); } obj2 = obj1.dictLookup("BlackPoint"); if (obj2.isArray() && obj2.arrayGetLength() == 3) { cs->blackX = obj2.arrayGet(0).getNum(&ok); cs->blackY = obj2.arrayGet(1).getNum(&ok); cs->blackZ = obj2.arrayGet(2).getNum(&ok); } obj2 = obj1.dictLookup("Range"); if (obj2.isArray() && obj2.arrayGetLength() == 4) { cs->aMin = obj2.arrayGet(0).getNum(&ok); cs->aMax = obj2.arrayGet(1).getNum(&ok); cs->bMin = obj2.arrayGet(2).getNum(&ok); cs->bMax = obj2.arrayGet(3).getNum(&ok); } if (!ok) { error(errSyntaxWarning, -1, "Bad Lab color space"); #ifdef USE_CMS cs->transform = nullptr; #endif delete cs; return nullptr; } #ifdef USE_CMS cs->transform = (state != nullptr) ? state->getXYZ2DisplayTransform() : nullptr; #endif return cs; } void GfxLabColorSpace::getGray(const GfxColor *color, GfxGray *gray) const { GfxRGB rgb; #ifdef USE_CMS if (transform != nullptr && transform->getTransformPixelType() == PT_GRAY) { unsigned char out[gfxColorMaxComps]; double in[gfxColorMaxComps]; getXYZ(color, &in[0], &in[1], &in[2]); bradford_transform_to_d50(in[0], in[1], in[2], whiteX, whiteY, whiteZ); transform->doTransform(in, out, 1); *gray = byteToCol(out[0]); return; } #endif getRGB(color, &rgb); *gray = clip01((GfxColorComp)(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b + 0.5)); } // convert L*a*b* to media XYZ color space // (not multiply by the white point) void GfxLabColorSpace::getXYZ(const GfxColor *color, double *pX, double *pY, double *pZ) const { double X, Y, Z; double t1, t2; t1 = (colToDbl(color->c[0]) + 16) / 116; t2 = t1 + colToDbl(color->c[1]) / 500; if (t2 >= (6.0 / 29.0)) { X = t2 * t2 * t2; } else { X = (108.0 / 841.0) * (t2 - (4.0 / 29.0)); } if (t1 >= (6.0 / 29.0)) { Y = t1 * t1 * t1; } else { Y = (108.0 / 841.0) * (t1 - (4.0 / 29.0)); } t2 = t1 - colToDbl(color->c[2]) / 200; if (t2 >= (6.0 / 29.0)) { Z = t2 * t2 * t2; } else { Z = (108.0 / 841.0) * (t2 - (4.0 / 29.0)); } *pX = X; *pY = Y; *pZ = Z; } void GfxLabColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const { double X, Y, Z; getXYZ(color, &X, &Y, &Z); X *= whiteX; Y *= whiteY; Z *= whiteZ; #ifdef USE_CMS if (transform != nullptr && transform->getTransformPixelType() == PT_RGB) { unsigned char out[gfxColorMaxComps]; double in[gfxColorMaxComps]; bradford_transform_to_d50(X, Y, Z, whiteX, whiteY, whiteZ); in[0] = X; in[1] = Y; in[2] = Z; transform->doTransform(in, out, 1); rgb->r = byteToCol(out[0]); rgb->g = byteToCol(out[1]); rgb->b = byteToCol(out[2]); return; } else if (transform != nullptr && transform->getTransformPixelType() == PT_CMYK) { unsigned char out[gfxColorMaxComps]; double in[gfxColorMaxComps]; double c, m, y, k, c1, m1, y1, k1, r, g, b; bradford_transform_to_d50(X, Y, Z, whiteX, whiteY, whiteZ); in[0] = X; in[1] = Y; in[2] = Z; transform->doTransform(in, out, 1); c = byteToDbl(out[0]); m = byteToDbl(out[1]); y = byteToDbl(out[2]); k = byteToDbl(out[3]); c1 = 1 - c; m1 = 1 - m; y1 = 1 - y; k1 = 1 - k; cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b); rgb->r = clip01(dblToCol(r)); rgb->g = clip01(dblToCol(g)); rgb->b = clip01(dblToCol(b)); return; } #endif bradford_transform_to_d65(X, Y, Z, whiteX, whiteY, whiteZ); // convert XYZ to RGB, including gamut mapping and gamma correction const double r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z; const double g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z; const double b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z; rgb->r = dblToCol(srgb_gamma_function(clip01(r))); rgb->g = dblToCol(srgb_gamma_function(clip01(g))); rgb->b = dblToCol(srgb_gamma_function(clip01(b))); } void GfxLabColorSpace::getCMYK(const GfxColor *color, GfxCMYK *cmyk) const { GfxRGB rgb; GfxColorComp c, m, y, k; #ifdef USE_CMS if (transform != nullptr && transform->getTransformPixelType() == PT_CMYK) { double in[gfxColorMaxComps]; unsigned char out[gfxColorMaxComps]; getXYZ(color, &in[0], &in[1], &in[2]); bradford_transform_to_d50(in[0], in[1], in[2], whiteX, whiteY, whiteZ); transform->doTransform(in, out, 1); cmyk->c = byteToCol(out[0]); cmyk->m = byteToCol(out[1]); cmyk->y = byteToCol(out[2]); cmyk->k = byteToCol(out[3]); return; } #endif getRGB(color, &rgb); c = clip01(gfxColorComp1 - rgb.r); m = clip01(gfxColorComp1 - rgb.g); y = clip01(gfxColorComp1 - rgb.b); k = c; if (m < k) { k = m; } if (y < k) { k = y; } cmyk->c = c - k; cmyk->m = m - k; cmyk->y = y - k; cmyk->k = k; } void GfxLabColorSpace::getDeviceN(const GfxColor *color, GfxColor *deviceN) const { GfxCMYK cmyk; clearGfxColor(deviceN); getCMYK(color, &cmyk); deviceN->c[0] = cmyk.c; deviceN->c[1] = cmyk.m; deviceN->c[2] = cmyk.y; deviceN->c[3] = cmyk.k; } void GfxLabColorSpace::getDefaultColor(GfxColor *color) const { color->c[0] = 0; if (aMin > 0) { color->c[1] = dblToCol(aMin); } else if (aMax < 0) { color->c[1] = dblToCol(aMax); } else { color->c[1] = 0; } if (bMin > 0) { color->c[2] = dblToCol(bMin); } else if (bMax < 0) { color->c[2] = dblToCol(bMax); } else { color->c[2] = 0; } } void GfxLabColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange, int maxImgPixel) const { decodeLow[0] = 0; decodeRange[0] = 100; decodeLow[1] = aMin; decodeRange[1] = aMax - aMin; decodeLow[2] = bMin; decodeRange[2] = bMax - bMin; } //------------------------------------------------------------------------ // GfxICCBasedColorSpace //------------------------------------------------------------------------ GfxICCBasedColorSpace::GfxICCBasedColorSpace(int nCompsA, GfxColorSpace *altA, const Ref *iccProfileStreamA) { nComps = nCompsA; alt = altA; iccProfileStream = *iccProfileStreamA; rangeMin[0] = rangeMin[1] = rangeMin[2] = rangeMin[3] = 0; rangeMax[0] = rangeMax[1] = rangeMax[2] = rangeMax[3] = 1; #ifdef USE_CMS transform = nullptr; lineTransform = nullptr; psCSA = nullptr; #endif } GfxICCBasedColorSpace::~GfxICCBasedColorSpace() { delete alt; #ifdef USE_CMS if (psCSA) { gfree(psCSA); } #endif } GfxColorSpace *GfxICCBasedColorSpace::copy() const { GfxICCBasedColorSpace *cs; int i; cs = new GfxICCBasedColorSpace(nComps, alt->copy(), &iccProfileStream); for (i = 0; i < 4; ++i) { cs->rangeMin[i] = rangeMin[i]; cs->rangeMax[i] = rangeMax[i]; } #ifdef USE_CMS cs->profile = profile; cs->transform = transform; cs->lineTransform = lineTransform; #endif return cs; } GfxColorSpace *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; } const Object &obj1Ref = arr->getNF(1); const Ref iccProfileStreamA = obj1Ref.isRef() ? obj1Ref.getRef() : Ref::INVALID(); #ifdef USE_CMS // check cache if (out && iccProfileStreamA != Ref::INVALID()) { if (auto *item = out->getIccColorSpaceCache()->lookup(iccProfileStreamA)) { cs = static_cast(item->copy()); int transformIntent = cs->getIntent(); int cmsIntent = INTENT_RELATIVE_COLORIMETRIC; if (state != nullptr) { cmsIntent = state->getCmsRenderingIntent(); } if (transformIntent == cmsIntent) { return cs; } delete cs; } } #endif obj1 = arr->get(1); if (!obj1.isStream()) { error(errSyntaxWarning, -1, "Bad ICCBased color space (stream)"); return nullptr; } dict = obj1.streamGetDict(); obj2 = dict->lookup("N"); if (!obj2.isInt()) { error(errSyntaxWarning, -1, "Bad ICCBased color space (N)"); return nullptr; } nCompsA = obj2.getInt(); if (nCompsA > 4) { error(errSyntaxError, -1, "ICCBased color space with too many ({0:d} > 4) components", nCompsA); nCompsA = 4; } obj2 = dict->lookup("Alternate"); if (obj2.isNull() || !(altA = GfxColorSpace::parse(nullptr, &obj2, out, state, recursion + 1))) { switch (nCompsA) { case 1: altA = new GfxDeviceGrayColorSpace(); break; case 3: altA = new GfxDeviceRGBColorSpace(); break; case 4: altA = new GfxDeviceCMYKColorSpace(); break; default: error(errSyntaxWarning, -1, "Bad ICCBased color space - invalid N"); return nullptr; } } if (altA->getNComps() != nCompsA) { error(errSyntaxWarning, -1, "Bad ICCBased color space - N doesn't match alt color space"); delete altA; return nullptr; } cs = new GfxICCBasedColorSpace(nCompsA, altA, &iccProfileStreamA); obj2 = dict->lookup("Range"); if (obj2.isArray() && obj2.arrayGetLength() == 2 * nCompsA) { for (i = 0; i < nCompsA; ++i) { cs->rangeMin[i] = obj2.arrayGet(2 * i).getNumWithDefaultValue(0); cs->rangeMax[i] = obj2.arrayGet(2 * i + 1).getNumWithDefaultValue(1); } } #ifdef USE_CMS obj1 = arr->get(1); if (!obj1.isStream()) { error(errSyntaxWarning, -1, "Bad ICCBased color space (stream)"); delete cs; return nullptr; } Stream *iccStream = obj1.getStream(); const std::vector profBuf = iccStream->toUnsignedChars(65536, 65536); auto hp = make_GfxLCMSProfilePtr(cmsOpenProfileFromMem(profBuf.data(), profBuf.size())); cs->profile = hp; if (!hp) { error(errSyntaxWarning, -1, "read ICCBased color space profile error"); } else { cs->buildTransforms(state); } // put this colorSpace into cache if (out && iccProfileStreamA != Ref::INVALID()) { out->getIccColorSpaceCache()->put(iccProfileStreamA, static_cast(cs->copy())); } #endif return cs; } #ifdef USE_CMS void GfxICCBasedColorSpace::buildTransforms(GfxState *state) { auto dhp = (state != nullptr && state->getDisplayProfile() != nullptr) ? state->getDisplayProfile() : nullptr; if (!dhp) { dhp = GfxState::sRGBProfile; } unsigned int cst = getCMSColorSpaceType(cmsGetColorSpace(profile.get())); unsigned int dNChannels = getCMSNChannels(cmsGetColorSpace(dhp.get())); unsigned int dcst = getCMSColorSpaceType(cmsGetColorSpace(dhp.get())); cmsHTRANSFORM transformA; int cmsIntent = INTENT_RELATIVE_COLORIMETRIC; if (state != nullptr) { cmsIntent = state->getCmsRenderingIntent(); } if ((transformA = cmsCreateTransform(profile.get(), COLORSPACE_SH(cst) | CHANNELS_SH(nComps) | BYTES_SH(1), dhp.get(), COLORSPACE_SH(dcst) | CHANNELS_SH(dNChannels) | BYTES_SH(1), cmsIntent, LCMS_FLAGS)) == nullptr) { error(errSyntaxWarning, -1, "Can't create transform"); transform = nullptr; } else { transform = std::make_shared(transformA, cmsIntent, cst, dcst); } if (dcst == PT_RGB || dcst == PT_CMYK) { // create line transform only when the display is RGB type color space if ((transformA = cmsCreateTransform(profile.get(), CHANNELS_SH(nComps) | BYTES_SH(1), dhp.get(), (dcst == PT_RGB) ? TYPE_RGB_8 : TYPE_CMYK_8, cmsIntent, LCMS_FLAGS)) == nullptr) { error(errSyntaxWarning, -1, "Can't create transform"); lineTransform = nullptr; } else { lineTransform = std::make_shared(transformA, cmsIntent, cst, dcst); } } } #endif void GfxICCBasedColorSpace::getGray(const GfxColor *color, GfxGray *gray) const { #ifdef USE_CMS if (transform != nullptr && transform->getTransformPixelType() == PT_GRAY) { unsigned char in[gfxColorMaxComps]; unsigned char out[gfxColorMaxComps]; if (nComps == 3 && transform->getInputPixelType() == PT_Lab) { in[0] = colToByte(dblToCol(colToDbl(color->c[0]) / 100.0)); in[1] = colToByte(dblToCol((colToDbl(color->c[1]) + 128.0) / 255.0)); in[2] = colToByte(dblToCol((colToDbl(color->c[2]) + 128.0) / 255.0)); } else { for (int i = 0; i < nComps; i++) { in[i] = colToByte(color->c[i]); } } if (nComps <= 4) { unsigned int key = 0; for (int j = 0; j < nComps; j++) { key = (key << 8) + in[j]; } std::map::iterator it = cmsCache.find(key); if (it != cmsCache.end()) { unsigned int value = it->second; *gray = byteToCol(value & 0xff); return; } } transform->doTransform(in, out, 1); *gray = byteToCol(out[0]); if (nComps <= 4 && cmsCache.size() <= CMSCACHE_LIMIT) { unsigned int key = 0; for (int j = 0; j < nComps; j++) { key = (key << 8) + in[j]; } unsigned int value = out[0]; cmsCache.insert(std::pair(key, value)); } } else { GfxRGB rgb; getRGB(color, &rgb); *gray = clip01((GfxColorComp)(0.3 * rgb.r + 0.59 * rgb.g + 0.11 * rgb.b + 0.5)); } #else alt->getGray(color, gray); #endif } void GfxICCBasedColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const { #ifdef USE_CMS if (transform != nullptr && transform->getTransformPixelType() == PT_RGB) { unsigned char in[gfxColorMaxComps]; unsigned char out[gfxColorMaxComps]; if (nComps == 3 && transform->getInputPixelType() == PT_Lab) { in[0] = colToByte(dblToCol(colToDbl(color->c[0]) / 100.0)); in[1] = colToByte(dblToCol((colToDbl(color->c[1]) + 128.0) / 255.0)); in[2] = colToByte(dblToCol((colToDbl(color->c[2]) + 128.0) / 255.0)); } else { for (int i = 0; i < nComps; i++) { in[i] = colToByte(color->c[i]); } } if (nComps <= 4) { unsigned int key = 0; for (int j = 0; j < nComps; j++) { key = (key << 8) + in[j]; } std::map::iterator it = cmsCache.find(key); if (it != cmsCache.end()) { unsigned int value = it->second; rgb->r = byteToCol(value >> 16); rgb->g = byteToCol((value >> 8) & 0xff); rgb->b = byteToCol(value & 0xff); return; } } transform->doTransform(in, out, 1); rgb->r = byteToCol(out[0]); rgb->g = byteToCol(out[1]); rgb->b = byteToCol(out[2]); if (nComps <= 4 && cmsCache.size() <= CMSCACHE_LIMIT) { unsigned int key = 0; for (int j = 0; j < nComps; j++) { key = (key << 8) + in[j]; } unsigned int value = (out[0] << 16) + (out[1] << 8) + out[2]; cmsCache.insert(std::pair(key, value)); } } else if (transform != nullptr && transform->getTransformPixelType() == PT_CMYK) { unsigned char in[gfxColorMaxComps]; unsigned char out[gfxColorMaxComps]; double c, m, y, k, c1, m1, y1, k1, r, g, b; if (nComps == 3 && transform->getInputPixelType() == PT_Lab) { in[0] = colToByte(dblToCol(colToDbl(color->c[0]) / 100.0)); in[1] = colToByte(dblToCol((colToDbl(color->c[1]) + 128.0) / 255.0)); in[2] = colToByte(dblToCol((colToDbl(color->c[2]) + 128.0) / 255.0)); } else { for (int i = 0; i < nComps; i++) { in[i] = colToByte(color->c[i]); } } if (nComps <= 4) { unsigned int key = 0; for (int j = 0; j < nComps; j++) { key = (key << 8) + in[j]; } std::map::iterator it = cmsCache.find(key); if (it != cmsCache.end()) { unsigned int value = it->second; rgb->r = byteToCol(value >> 16); rgb->g = byteToCol((value >> 8) & 0xff); rgb->b = byteToCol(value & 0xff); return; } } transform->doTransform(in, out, 1); c = byteToDbl(out[0]); m = byteToDbl(out[1]); y = byteToDbl(out[2]); k = byteToDbl(out[3]); c1 = 1 - c; m1 = 1 - m; y1 = 1 - y; k1 = 1 - k; cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b); rgb->r = clip01(dblToCol(r)); rgb->g = clip01(dblToCol(g)); rgb->b = clip01(dblToCol(b)); if (nComps <= 4 && cmsCache.size() <= CMSCACHE_LIMIT) { unsigned int key = 0; for (int j = 0; j < nComps; j++) { key = (key << 8) + in[j]; } unsigned int value = (dblToByte(r) << 16) + (dblToByte(g) << 8) + dblToByte(b); cmsCache.insert(std::pair(key, value)); } } else { alt->getRGB(color, rgb); } #else alt->getRGB(color, rgb); #endif } void GfxICCBasedColorSpace::getRGBLine(unsigned char *in, unsigned int *out, int length) { #ifdef USE_CMS if (lineTransform != nullptr && lineTransform->getTransformPixelType() == PT_RGB) { unsigned char *tmp = (unsigned char *)gmallocn(3 * length, sizeof(unsigned char)); lineTransform->doTransform(in, tmp, length); for (int i = 0; i < length; ++i) { unsigned char *current = tmp + (i * 3); out[i] = (current[0] << 16) | (current[1] << 8) | current[2]; } gfree(tmp); } else { alt->getRGBLine(in, out, length); } #else alt->getRGBLine(in, out, length); #endif } void GfxICCBasedColorSpace::getRGBLine(unsigned char *in, unsigned char *out, int length) { #ifdef USE_CMS if (lineTransform != nullptr && lineTransform->getTransformPixelType() == PT_RGB) { unsigned char *tmp = (unsigned char *)gmallocn(3 * length, sizeof(unsigned char)); lineTransform->doTransform(in, tmp, length); unsigned char *current = tmp; for (int i = 0; i < length; ++i) { *out++ = *current++; *out++ = *current++; *out++ = *current++; } gfree(tmp); } else if (lineTransform != nullptr && lineTransform->getTransformPixelType() == PT_CMYK) { unsigned char *tmp = (unsigned char *)gmallocn(4 * length, sizeof(unsigned char)); lineTransform->doTransform(in, tmp, length); unsigned char *current = tmp; double c, m, y, k, c1, m1, y1, k1, r, g, b; for (int i = 0; i < length; ++i) { c = byteToDbl(*current++); m = byteToDbl(*current++); y = byteToDbl(*current++); k = byteToDbl(*current++); c1 = 1 - c; m1 = 1 - m; y1 = 1 - y; k1 = 1 - k; cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b); *out++ = dblToByte(r); *out++ = dblToByte(g); *out++ = dblToByte(b); } gfree(tmp); } else { alt->getRGBLine(in, out, length); } #else alt->getRGBLine(in, out, length); #endif } void GfxICCBasedColorSpace::getRGBXLine(unsigned char *in, unsigned char *out, int length) { #ifdef USE_CMS if (lineTransform != nullptr && lineTransform->getTransformPixelType() == PT_RGB) { unsigned char *tmp = (unsigned char *)gmallocn(3 * length, sizeof(unsigned char)); lineTransform->doTransform(in, tmp, length); unsigned char *current = tmp; for (int i = 0; i < length; ++i) { *out++ = *current++; *out++ = *current++; *out++ = *current++; *out++ = 255; } gfree(tmp); } else { alt->getRGBXLine(in, out, length); } #else alt->getRGBXLine(in, out, length); #endif } void GfxICCBasedColorSpace::getCMYKLine(unsigned char *in, unsigned char *out, int length) { #ifdef USE_CMS if (lineTransform != nullptr && lineTransform->getTransformPixelType() == PT_CMYK) { transform->doTransform(in, out, length); } else if (lineTransform != nullptr && nComps != 4) { GfxColorComp c, m, y, k; unsigned char *tmp = (unsigned char *)gmallocn(3 * length, sizeof(unsigned char)); getRGBLine(in, tmp, length); unsigned char *p = tmp; for (int i = 0; i < length; i++) { c = byteToCol(255 - *p++); m = byteToCol(255 - *p++); y = byteToCol(255 - *p++); k = c; if (m < k) { k = m; } if (y < k) { k = y; } *out++ = colToByte(c - k); *out++ = colToByte(m - k); *out++ = colToByte(y - k); *out++ = colToByte(k); } gfree(tmp); } else { alt->getCMYKLine(in, out, length); } #else alt->getCMYKLine(in, out, length); #endif } void GfxICCBasedColorSpace::getDeviceNLine(unsigned char *in, unsigned char *out, int length) { #ifdef USE_CMS if (lineTransform != nullptr && lineTransform->getTransformPixelType() == PT_CMYK) { unsigned char *tmp = (unsigned char *)gmallocn(4 * length, sizeof(unsigned char)); transform->doTransform(in, tmp, length); unsigned char *p = tmp; for (int i = 0; i < length; i++) { for (int j = 0; j < 4; j++) { *out++ = *p++; } for (int j = 4; j < SPOT_NCOMPS + 4; j++) { *out++ = 0; } } gfree(tmp); } else if (lineTransform != nullptr && nComps != 4) { GfxColorComp c, m, y, k; unsigned char *tmp = (unsigned char *)gmallocn(3 * length, sizeof(unsigned char)); getRGBLine(in, tmp, length); unsigned char *p = tmp; for (int i = 0; i < length; i++) { for (int j = 0; j < SPOT_NCOMPS + 4; j++) { out[j] = 0; } c = byteToCol(255 - *p++); m = byteToCol(255 - *p++); y = byteToCol(255 - *p++); k = c; if (m < k) { k = m; } if (y < k) { k = y; } out[0] = colToByte(c - k); out[1] = colToByte(m - k); out[2] = colToByte(y - k); out[3] = colToByte(k); out += (SPOT_NCOMPS + 4); } gfree(tmp); } else { alt->getDeviceNLine(in, out, length); } #else alt->getDeviceNLine(in, out, length); #endif } void GfxICCBasedColorSpace::getCMYK(const GfxColor *color, GfxCMYK *cmyk) const { #ifdef USE_CMS if (transform != nullptr && transform->getTransformPixelType() == PT_CMYK) { unsigned char in[gfxColorMaxComps]; unsigned char out[gfxColorMaxComps]; if (nComps == 3 && transform->getInputPixelType() == PT_Lab) { in[0] = colToByte(dblToCol(colToDbl(color->c[0]) / 100.0)); in[1] = colToByte(dblToCol((colToDbl(color->c[1]) + 128.0) / 255.0)); in[2] = colToByte(dblToCol((colToDbl(color->c[2]) + 128.0) / 255.0)); } else { for (int i = 0; i < nComps; i++) { in[i] = colToByte(color->c[i]); } } if (nComps <= 4) { unsigned int key = 0; for (int j = 0; j < nComps; j++) { key = (key << 8) + in[j]; } std::map::iterator it = cmsCache.find(key); if (it != cmsCache.end()) { unsigned int value = it->second; cmyk->c = byteToCol(value >> 24); cmyk->m = byteToCol((value >> 16) & 0xff); cmyk->y = byteToCol((value >> 8) & 0xff); cmyk->k = byteToCol(value & 0xff); return; } } transform->doTransform(in, out, 1); cmyk->c = byteToCol(out[0]); cmyk->m = byteToCol(out[1]); cmyk->y = byteToCol(out[2]); cmyk->k = byteToCol(out[3]); if (nComps <= 4 && cmsCache.size() <= CMSCACHE_LIMIT) { unsigned int key = 0; for (int j = 0; j < nComps; j++) { key = (key << 8) + in[j]; } unsigned int value = (out[0] << 24) + (out[1] << 16) + (out[2] << 8) + out[3]; cmsCache.insert(std::pair(key, value)); } } else if (nComps != 4 && transform != nullptr && transform->getTransformPixelType() == PT_RGB) { GfxRGB rgb; GfxColorComp c, m, y, k; getRGB(color, &rgb); c = clip01(gfxColorComp1 - rgb.r); m = clip01(gfxColorComp1 - rgb.g); y = clip01(gfxColorComp1 - rgb.b); k = c; if (m < k) { k = m; } if (y < k) { k = y; } cmyk->c = c - k; cmyk->m = m - k; cmyk->y = y - k; cmyk->k = k; } else { alt->getCMYK(color, cmyk); } #else alt->getCMYK(color, cmyk); #endif } bool GfxICCBasedColorSpace::useGetRGBLine() const { #ifdef USE_CMS return lineTransform != nullptr || alt->useGetRGBLine(); #else return alt->useGetRGBLine(); #endif } bool GfxICCBasedColorSpace::useGetCMYKLine() const { #ifdef USE_CMS return lineTransform != nullptr || alt->useGetCMYKLine(); #else return alt->useGetCMYKLine(); #endif } bool GfxICCBasedColorSpace::useGetDeviceNLine() const { #ifdef USE_CMS return lineTransform != nullptr || alt->useGetDeviceNLine(); #else return alt->useGetDeviceNLine(); #endif } void GfxICCBasedColorSpace::getDeviceN(const GfxColor *color, GfxColor *deviceN) const { GfxCMYK cmyk; clearGfxColor(deviceN); getCMYK(color, &cmyk); deviceN->c[0] = cmyk.c; deviceN->c[1] = cmyk.m; deviceN->c[2] = cmyk.y; deviceN->c[3] = cmyk.k; } void GfxICCBasedColorSpace::getDefaultColor(GfxColor *color) const { int i; for (i = 0; i < nComps; ++i) { if (rangeMin[i] > 0) { color->c[i] = dblToCol(rangeMin[i]); } else if (rangeMax[i] < 0) { color->c[i] = dblToCol(rangeMax[i]); } else { color->c[i] = 0; } } } void GfxICCBasedColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange, int maxImgPixel) const { alt->getDefaultRanges(decodeLow, decodeRange, maxImgPixel); #if 0 // this is nominally correct, but some PDF files don't set the // correct ranges in the ICCBased dict int i; for (i = 0; i < nComps; ++i) { decodeLow[i] = rangeMin[i]; decodeRange[i] = rangeMax[i] - rangeMin[i]; } #endif } #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; } if (!profile) { error(errSyntaxWarning, -1, "profile is nullptr"); return nullptr; } void *rawprofile = profile.get(); size = cmsGetPostScriptCSA(cmsGetProfileContextID(rawprofile), rawprofile, getIntent(), 0, nullptr, 0); if (size == 0) { error(errSyntaxWarning, -1, "PostScript CSA is nullptr"); return nullptr; } psCSA = (char *)gmalloc(size + 1); 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 //------------------------------------------------------------------------ // GfxIndexedColorSpace //------------------------------------------------------------------------ GfxIndexedColorSpace::GfxIndexedColorSpace(GfxColorSpace *baseA, int indexHighA) { base = baseA; indexHigh = indexHighA; lookup = (unsigned char *)gmallocn((indexHigh + 1) * base->getNComps(), sizeof(unsigned char)); overprintMask = base->getOverprintMask(); } GfxIndexedColorSpace::~GfxIndexedColorSpace() { delete base; gfree(lookup); } GfxColorSpace *GfxIndexedColorSpace::copy() const { GfxIndexedColorSpace *cs; cs = new GfxIndexedColorSpace(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) { GfxColorSpace *baseA; int indexHighA; Object obj1; const char *s; int i, j; if (arr->getLength() != 4) { error(errSyntaxWarning, -1, "Bad Indexed color space"); return nullptr; } 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; } obj1 = arr->get(2); if (!obj1.isInt()) { error(errSyntaxWarning, -1, "Bad Indexed color space (hival)"); delete baseA; return nullptr; } indexHighA = obj1.getInt(); if (indexHighA < 0 || indexHighA > 255) { // the PDF spec requires indexHigh to be in [0,255] -- allowing // values larger than 255 creates a security hole: if nComps * // indexHigh is greater than 2^31, the loop below may overwrite // past the end of the array int previousValue = indexHighA; if (indexHighA < 0) { indexHighA = 0; } else { indexHighA = 255; } 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); obj1 = arr->get(3); const int n = baseA->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) { error(errSyntaxWarning, -1, "Bad Indexed color space (lookup table stream too short) padding with zeroes"); cs->lookup[i * n + j] = 0; } } obj1.streamClose(); } else if (obj1.isString()) { if (obj1.getString()->getLength() < (indexHighA + 1) * n) { error(errSyntaxWarning, -1, "Bad Indexed color space (lookup table string too short)"); goto err3; } s = obj1.getString()->c_str(); for (i = 0; i <= indexHighA; ++i) { for (j = 0; j < n; ++j) { cs->lookup[i * n + j] = (unsigned char)*s++; } } } else { error(errSyntaxWarning, -1, "Bad Indexed color space (lookup table)"); goto err3; } return cs; err3: delete cs; return nullptr; } GfxColor *GfxIndexedColorSpace::mapColorToBase(const GfxColor *color, GfxColor *baseColor) const { unsigned char *p; double low[gfxColorMaxComps], range[gfxColorMaxComps]; int n, i; n = base->getNComps(); base->getDefaultRanges(low, range, indexHigh); const int idx = (int)(colToDbl(color->c[0]) + 0.5) * n; if (likely((idx + n - 1 < (indexHigh + 1) * base->getNComps()) && idx >= 0)) { p = &lookup[idx]; for (i = 0; i < n; ++i) { baseColor->c[i] = dblToCol(low[i] + (p[i] / 255.0) * range[i]); } } else { for (i = 0; i < n; ++i) { baseColor->c[i] = 0; } } return baseColor; } void GfxIndexedColorSpace::getGray(const GfxColor *color, GfxGray *gray) const { GfxColor color2; base->getGray(mapColorToBase(color, &color2), gray); } void GfxIndexedColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const { GfxColor color2; base->getRGB(mapColorToBase(color, &color2), rgb); } void GfxIndexedColorSpace::getRGBLine(unsigned char *in, unsigned int *out, int length) { unsigned char *line; int i, j, n; n = base->getNComps(); line = (unsigned char *)gmallocn(length, n); for (i = 0; i < length; i++) { for (j = 0; j < n; j++) { line[i * n + j] = lookup[in[i] * n + j]; } } base->getRGBLine(line, out, length); gfree(line); } void GfxIndexedColorSpace::getRGBLine(unsigned char *in, unsigned char *out, int length) { unsigned char *line; int i, j, n; n = base->getNComps(); line = (unsigned char *)gmallocn(length, n); for (i = 0; i < length; i++) { for (j = 0; j < n; j++) { line[i * n + j] = lookup[in[i] * n + j]; } } base->getRGBLine(line, out, length); gfree(line); } void GfxIndexedColorSpace::getRGBXLine(unsigned char *in, unsigned char *out, int length) { unsigned char *line; int i, j, n; n = base->getNComps(); line = (unsigned char *)gmallocn(length, n); for (i = 0; i < length; i++) { for (j = 0; j < n; j++) { line[i * n + j] = lookup[in[i] * n + j]; } } base->getRGBXLine(line, out, length); gfree(line); } void GfxIndexedColorSpace::getCMYKLine(unsigned char *in, unsigned char *out, int length) { unsigned char *line; int i, j, n; n = base->getNComps(); line = (unsigned char *)gmallocn(length, n); for (i = 0; i < length; i++) { for (j = 0; j < n; j++) { line[i * n + j] = lookup[in[i] * n + j]; } } base->getCMYKLine(line, out, length); gfree(line); } void GfxIndexedColorSpace::getDeviceNLine(unsigned char *in, unsigned char *out, int length) { unsigned char *line; int i, j, n; n = base->getNComps(); line = (unsigned char *)gmallocn(length, n); for (i = 0; i < length; i++) { for (j = 0; j < n; j++) { line[i * n + j] = lookup[in[i] * n + j]; } } base->getDeviceNLine(line, out, length); gfree(line); } void GfxIndexedColorSpace::getCMYK(const GfxColor *color, GfxCMYK *cmyk) const { GfxColor color2; base->getCMYK(mapColorToBase(color, &color2), cmyk); } void GfxIndexedColorSpace::getDeviceN(const GfxColor *color, GfxColor *deviceN) const { GfxColor color2; base->getDeviceN(mapColorToBase(color, &color2), deviceN); } void GfxIndexedColorSpace::getDefaultColor(GfxColor *color) const { color->c[0] = 0; } void GfxIndexedColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange, int maxImgPixel) const { decodeLow[0] = 0; decodeRange[0] = maxImgPixel; } //------------------------------------------------------------------------ // GfxSeparationColorSpace //------------------------------------------------------------------------ GfxSeparationColorSpace::GfxSeparationColorSpace(GooString *nameA, GfxColorSpace *altA, Function *funcA) { name = nameA; alt = altA; func = funcA; nonMarking = !name->cmp("None"); if (!name->cmp("Cyan")) { overprintMask = 0x01; } else if (!name->cmp("Magenta")) { overprintMask = 0x02; } else if (!name->cmp("Yellow")) { overprintMask = 0x04; } else if (!name->cmp("Black")) { overprintMask = 0x08; } else if (!name->cmp("All")) { overprintMask = 0xffffffff; } } GfxSeparationColorSpace::GfxSeparationColorSpace(GooString *nameA, GfxColorSpace *altA, Function *funcA, bool nonMarkingA, unsigned int overprintMaskA, int *mappingA) { name = nameA; alt = altA; func = funcA; nonMarking = nonMarkingA; overprintMask = overprintMaskA; mapping = mappingA; } GfxSeparationColorSpace::~GfxSeparationColorSpace() { delete name; delete alt; delete func; if (mapping != nullptr) { gfree(mapping); } } GfxColorSpace *GfxSeparationColorSpace::copy() 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); } //~ handle the 'All' and 'None' colorants GfxColorSpace *GfxSeparationColorSpace::parse(GfxResources *res, Array *arr, OutputDev *out, GfxState *state, int recursion) { GooString *nameA; GfxColorSpace *altA; Function *funcA; Object obj1; if (arr->getLength() != 4) { error(errSyntaxWarning, -1, "Bad Separation color space"); goto err1; } obj1 = arr->get(1); if (!obj1.isName()) { error(errSyntaxWarning, -1, "Bad Separation color space (name)"); goto err1; } nameA = new GooString(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; } obj1 = arr->get(3); if (!(funcA = Function::parse(&obj1))) { goto err4; } if (funcA->getInputSize() != 1) { error(errSyntaxWarning, -1, "Bad SeparationColorSpace function"); goto err5; } if (altA->getNComps() <= funcA->getOutputSize()) { return new GfxSeparationColorSpace(nameA, altA, funcA); } err5: delete funcA; err4: delete altA; err3: delete nameA; err1: return nullptr; } void GfxSeparationColorSpace::getGray(const GfxColor *color, GfxGray *gray) const { double x; double c[gfxColorMaxComps]; GfxColor color2; int i; if (alt->getMode() == csDeviceGray && name->cmp("Black") == 0) { *gray = clip01(gfxColorComp1 - color->c[0]); } else { x = colToDbl(color->c[0]); func->transform(&x, c); for (i = 0; i < alt->getNComps(); ++i) { color2.c[i] = dblToCol(c[i]); } alt->getGray(&color2, gray); } } void GfxSeparationColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const { double x; double c[gfxColorMaxComps]; GfxColor color2; int i; if (alt->getMode() == csDeviceGray && name->cmp("Black") == 0) { rgb->r = clip01(gfxColorComp1 - color->c[0]); rgb->g = clip01(gfxColorComp1 - color->c[0]); rgb->b = clip01(gfxColorComp1 - color->c[0]); } else { x = colToDbl(color->c[0]); func->transform(&x, c); const int altNComps = alt->getNComps(); for (i = 0; i < altNComps; ++i) { color2.c[i] = dblToCol(c[i]); } alt->getRGB(&color2, rgb); } } void GfxSeparationColorSpace::getCMYK(const GfxColor *color, GfxCMYK *cmyk) const { double x; double c[gfxColorMaxComps]; GfxColor color2; int i; if (name->cmp("Black") == 0) { cmyk->c = 0; cmyk->m = 0; cmyk->y = 0; cmyk->k = color->c[0]; } else if (name->cmp("Cyan") == 0) { cmyk->c = color->c[0]; cmyk->m = 0; cmyk->y = 0; cmyk->k = 0; } else if (name->cmp("Magenta") == 0) { cmyk->c = 0; cmyk->m = color->c[0]; cmyk->y = 0; cmyk->k = 0; } else if (name->cmp("Yellow") == 0) { cmyk->c = 0; cmyk->m = 0; cmyk->y = color->c[0]; cmyk->k = 0; } else { x = colToDbl(color->c[0]); func->transform(&x, c); for (i = 0; i < alt->getNComps(); ++i) { color2.c[i] = dblToCol(c[i]); } alt->getCMYK(&color2, cmyk); } } void GfxSeparationColorSpace::getDeviceN(const GfxColor *color, GfxColor *deviceN) const { clearGfxColor(deviceN); if (mapping == nullptr || mapping[0] == -1) { GfxCMYK cmyk; getCMYK(color, &cmyk); deviceN->c[0] = cmyk.c; deviceN->c[1] = cmyk.m; deviceN->c[2] = cmyk.y; deviceN->c[3] = cmyk.k; } else { deviceN->c[mapping[0]] = color->c[0]; } } void GfxSeparationColorSpace::getDefaultColor(GfxColor *color) const { color->c[0] = gfxColorComp1; } void GfxSeparationColorSpace::createMapping(std::vector *separationList, int maxSepComps) { if (nonMarking) { return; } mapping = (int *)gmalloc(sizeof(int)); switch (overprintMask) { case 0x01: *mapping = 0; break; case 0x02: *mapping = 1; break; case 0x04: *mapping = 2; break; case 0x08: *mapping = 3; break; default: unsigned int newOverprintMask = 0x10; for (std::size_t i = 0; i < separationList->size(); i++) { GfxSeparationColorSpace *sepCS = (*separationList)[i]; if (!sepCS->getName()->cmp(name)) { if (sepCS->getFunc()->hasDifferentResultSet(func)) { error(errSyntaxWarning, -1, "Different functions found for '{0:t}', convert immediately", name); gfree(mapping); mapping = nullptr; return; } *mapping = i + 4; overprintMask = newOverprintMask; return; } newOverprintMask <<= 1; } if ((int)separationList->size() == maxSepComps) { error(errSyntaxWarning, -1, "Too many ({0:d}) spots, convert '{1:t}' immediately", maxSepComps, name); gfree(mapping); mapping = nullptr; return; } *mapping = separationList->size() + 4; separationList->push_back((GfxSeparationColorSpace *)copy()); overprintMask = newOverprintMask; break; } } //------------------------------------------------------------------------ // GfxDeviceNColorSpace //------------------------------------------------------------------------ GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA, std::vector &&namesA, GfxColorSpace *altA, Function *funcA, std::vector *sepsCSA) : nComps(nCompsA), names(std::move(namesA)) { alt = altA; func = funcA; sepsCS = sepsCSA; nonMarking = true; overprintMask = 0; mapping = nullptr; for (int i = 0; i < nComps; ++i) { if (names[i] != "None") { nonMarking = false; } if (names[i] == "Cyan") { overprintMask |= 0x01; } else if (names[i] == "Magenta") { overprintMask |= 0x02; } else if (names[i] == "Yellow") { overprintMask |= 0x04; } else if (names[i] == "Black") { overprintMask |= 0x08; } else if (names[i] == "All") { overprintMask = 0xffffffff; } 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) { alt = altA; func = funcA; sepsCS = sepsCSA; mapping = mappingA; nonMarking = nonMarkingA; overprintMask = overprintMaskA; } GfxDeviceNColorSpace::~GfxDeviceNColorSpace() { delete alt; delete func; for (auto entry : *sepsCS) { delete entry; } delete sepsCS; if (mapping != nullptr) { gfree(mapping); } } GfxColorSpace *GfxDeviceNColorSpace::copy() const { int *mappingA = nullptr; auto sepsCSA = new std::vector(); sepsCSA->reserve(sepsCS->size()); for (const GfxSeparationColorSpace *scs : *sepsCS) { if (likely(scs != nullptr)) { sepsCSA->push_back((GfxSeparationColorSpace *)scs->copy()); } } if (mapping != nullptr) { mappingA = (int *)gmalloc(sizeof(int) * nComps); for (int i = 0; i < nComps; i++) { mappingA[i] = mapping[i]; } } return new GfxDeviceNColorSpace(nComps, names, alt->copy(), func->copy(), sepsCSA, mappingA, nonMarking, overprintMask); } //~ handle the 'None' colorant GfxColorSpace *GfxDeviceNColorSpace::parse(GfxResources *res, Array *arr, OutputDev *out, GfxState *state, int recursion) { int nCompsA; std::vector namesA; GfxColorSpace *altA; Function *funcA; Object obj1; auto separationList = new std::vector(); if (arr->getLength() != 4 && arr->getLength() != 5) { error(errSyntaxWarning, -1, "Bad DeviceN color space"); goto err1; } obj1 = arr->get(1); if (!obj1.isArray()) { error(errSyntaxWarning, -1, "Bad DeviceN color space (names)"); goto err1; } nCompsA = obj1.arrayGetLength(); if (nCompsA > gfxColorMaxComps) { error(errSyntaxWarning, -1, "DeviceN color space with too many ({0:d} > {1:d}) components", nCompsA, gfxColorMaxComps); nCompsA = gfxColorMaxComps; } for (int i = 0; i < nCompsA; ++i) { Object obj2 = obj1.arrayGet(i); if (!obj2.isName()) { error(errSyntaxWarning, -1, "Bad DeviceN color space (names)"); nCompsA = i; goto err1; } namesA.emplace_back(obj2.getName()); } obj1 = arr->get(2); if (!(altA = GfxColorSpace::parse(res, &obj1, out, state, recursion + 1))) { error(errSyntaxWarning, -1, "Bad DeviceN color space (alternate color space)"); goto err1; } obj1 = arr->get(3); if (!(funcA = Function::parse(&obj1))) { goto err4; } if (arr->getLength() == 5) { obj1 = arr->get(4); if (!obj1.isDict()) { error(errSyntaxWarning, -1, "Bad DeviceN color space (attributes)"); goto err5; } Dict *attribs = obj1.getDict(); Object obj2 = attribs->lookup("Colorants"); if (obj2.isDict()) { Dict *colorants = obj2.getDict(); 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); if (cs) { separationList->push_back(cs); } } else { error(errSyntaxWarning, -1, "Bad DeviceN color space (colorant value entry is not an Array)"); goto err5; } } } } if (likely(nCompsA >= funcA->getInputSize() && altA->getNComps() <= funcA->getOutputSize())) { return new GfxDeviceNColorSpace(nCompsA, std::move(namesA), altA, funcA, separationList); } err5: delete funcA; err4: delete altA; err1: delete separationList; return nullptr; } void GfxDeviceNColorSpace::getGray(const GfxColor *color, GfxGray *gray) const { double x[gfxColorMaxComps], c[gfxColorMaxComps]; GfxColor color2; int i; for (i = 0; i < nComps; ++i) { x[i] = colToDbl(color->c[i]); } func->transform(x, c); for (i = 0; i < alt->getNComps(); ++i) { color2.c[i] = dblToCol(c[i]); } alt->getGray(&color2, gray); } void GfxDeviceNColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const { double x[gfxColorMaxComps], c[gfxColorMaxComps]; GfxColor color2; int i; for (i = 0; i < nComps; ++i) { x[i] = colToDbl(color->c[i]); } func->transform(x, c); for (i = 0; i < alt->getNComps(); ++i) { color2.c[i] = dblToCol(c[i]); } alt->getRGB(&color2, rgb); } void GfxDeviceNColorSpace::getCMYK(const GfxColor *color, GfxCMYK *cmyk) const { double x[gfxColorMaxComps], c[gfxColorMaxComps]; GfxColor color2; int i; for (i = 0; i < nComps; ++i) { x[i] = colToDbl(color->c[i]); } func->transform(x, c); for (i = 0; i < alt->getNComps(); ++i) { color2.c[i] = dblToCol(c[i]); } alt->getCMYK(&color2, cmyk); } void GfxDeviceNColorSpace::getDeviceN(const GfxColor *color, GfxColor *deviceN) const { clearGfxColor(deviceN); if (mapping == nullptr) { GfxCMYK cmyk; getCMYK(color, &cmyk); deviceN->c[0] = cmyk.c; deviceN->c[1] = cmyk.m; deviceN->c[2] = cmyk.y; deviceN->c[3] = cmyk.k; } else { for (int j = 0; j < nComps; j++) { if (mapping[j] != -1) { deviceN->c[mapping[j]] = color->c[j]; } } } } void GfxDeviceNColorSpace::getDefaultColor(GfxColor *color) const { int i; for (i = 0; i < nComps; ++i) { color->c[i] = gfxColorComp1; } } void GfxDeviceNColorSpace::createMapping(std::vector *separationList, int maxSepComps) { if (nonMarking) { // None return; } mapping = (int *)gmalloc(sizeof(int) * nComps); unsigned int newOverprintMask = 0; for (int i = 0; i < nComps; i++) { if (names[i] == "None") { mapping[i] = -1; } else if (names[i] == "Cyan") { newOverprintMask |= 0x01; mapping[i] = 0; } else if (names[i] == "Magenta") { newOverprintMask |= 0x02; mapping[i] = 1; } else if (names[i] == "Yellow") { newOverprintMask |= 0x04; mapping[i] = 2; } else if (names[i] == "Black") { newOverprintMask |= 0x08; mapping[i] = 3; } else { unsigned int startOverprintMask = 0x10; bool found = false; const Function *sepFunc = nullptr; if (nComps == 1) { sepFunc = func; } else { for (const GfxSeparationColorSpace *sepCS : *sepsCS) { if (!sepCS->getName()->cmp(names[i])) { sepFunc = sepCS->getFunc(); break; } } } for (std::size_t j = 0; j < separationList->size(); j++) { GfxSeparationColorSpace *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()); gfree(mapping); mapping = nullptr; overprintMask = 0xffffffff; return; } mapping[i] = j + 4; newOverprintMask |= startOverprintMask; found = true; break; } startOverprintMask <<= 1; } if (!found) { if ((int)separationList->size() == maxSepComps) { error(errSyntaxWarning, -1, "Too many ({0:d}) spots, convert '{1:s}' immediately", maxSepComps, names[i].c_str()); gfree(mapping); mapping = nullptr; overprintMask = 0xffffffff; return; } mapping[i] = separationList->size() + 4; newOverprintMask |= startOverprintMask; if (nComps == 1) { separationList->push_back(new GfxSeparationColorSpace(new GooString(names[i]), alt->copy(), func->copy())); } else { for (const GfxSeparationColorSpace *sepCS : *sepsCS) { if (!sepCS->getName()->cmp(names[i])) { found = true; separationList->push_back((GfxSeparationColorSpace *)sepCS->copy()); break; } } if (!found) { error(errSyntaxWarning, -1, "DeviceN has no suitable colorant"); gfree(mapping); mapping = nullptr; overprintMask = 0xffffffff; return; } } } } } overprintMask = newOverprintMask; } //------------------------------------------------------------------------ // GfxPatternColorSpace //------------------------------------------------------------------------ GfxPatternColorSpace::GfxPatternColorSpace(GfxColorSpace *underA) { under = underA; } GfxPatternColorSpace::~GfxPatternColorSpace() { if (under) { delete under; } } GfxColorSpace *GfxPatternColorSpace::copy() const { return new GfxPatternColorSpace(under ? under->copy() : nullptr); } GfxColorSpace *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; } underA = nullptr; 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; } } cs = new GfxPatternColorSpace(underA); return cs; } void GfxPatternColorSpace::getGray(const GfxColor *color, GfxGray *gray) const { *gray = 0; } void GfxPatternColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const { rgb->r = rgb->g = rgb->b = 0; } void GfxPatternColorSpace::getCMYK(const GfxColor *color, GfxCMYK *cmyk) const { cmyk->c = cmyk->m = cmyk->y = 0; cmyk->k = 1; } void GfxPatternColorSpace::getDeviceN(const GfxColor *color, GfxColor *deviceN) const { clearGfxColor(deviceN); deviceN->c[3] = 1; } void GfxPatternColorSpace::getDefaultColor(GfxColor *color) const { color->c[0] = 0; } //------------------------------------------------------------------------ // Pattern //------------------------------------------------------------------------ GfxPattern::GfxPattern(int typeA, int patternRefNumA) : type(typeA), patternRefNum(patternRefNumA) { } GfxPattern::~GfxPattern() { } GfxPattern *GfxPattern::parse(GfxResources *res, Object *obj, OutputDev *out, GfxState *state, int patternRefNum) { GfxPattern *pattern; Object obj1; if (obj->isDict()) { obj1 = obj->dictLookup("PatternType"); } else if (obj->isStream()) { obj1 = obj->streamGetDict()->lookup("PatternType"); } else { return nullptr; } pattern = nullptr; if (obj1.isInt() && obj1.getInt() == 1) { pattern = GfxTilingPattern::parse(obj, patternRefNum); } else if (obj1.isInt() && obj1.getInt() == 2) { pattern = GfxShadingPattern::parse(res, obj, out, state, patternRefNum); } return pattern; } //------------------------------------------------------------------------ // GfxTilingPattern //------------------------------------------------------------------------ GfxTilingPattern *GfxTilingPattern::parse(Object *patObj, int patternRefNum) { Dict *dict; int paintTypeA, tilingTypeA; double bboxA[4], matrixA[6]; double xStepA, yStepA; Object resDictA; Object obj1; int i; if (!patObj->isStream()) { return nullptr; } dict = patObj->streamGetDict(); obj1 = dict->lookup("PaintType"); if (obj1.isInt()) { paintTypeA = obj1.getInt(); } else { paintTypeA = 1; error(errSyntaxWarning, -1, "Invalid or missing PaintType in pattern"); } obj1 = dict->lookup("TilingType"); if (obj1.isInt()) { tilingTypeA = obj1.getInt(); } else { tilingTypeA = 1; error(errSyntaxWarning, -1, "Invalid or missing TilingType in pattern"); } bboxA[0] = bboxA[1] = 0; bboxA[2] = bboxA[3] = 1; obj1 = dict->lookup("BBox"); if (obj1.isArray() && obj1.arrayGetLength() == 4) { for (i = 0; i < 4; ++i) { Object obj2 = obj1.arrayGet(i); if (obj2.isNum()) { bboxA[i] = obj2.getNum(); } } } else { error(errSyntaxWarning, -1, "Invalid or missing BBox in pattern"); } obj1 = dict->lookup("XStep"); if (obj1.isNum()) { xStepA = obj1.getNum(); } else { xStepA = 1; error(errSyntaxWarning, -1, "Invalid or missing XStep in pattern"); } obj1 = dict->lookup("YStep"); if (obj1.isNum()) { yStepA = obj1.getNum(); } else { yStepA = 1; error(errSyntaxWarning, -1, "Invalid or missing YStep in pattern"); } resDictA = dict->lookup("Resources"); if (!resDictA.isDict()) { error(errSyntaxWarning, -1, "Invalid or missing Resources in pattern"); } matrixA[0] = 1; matrixA[1] = 0; matrixA[2] = 0; matrixA[3] = 1; matrixA[4] = 0; matrixA[5] = 0; obj1 = dict->lookup("Matrix"); if (obj1.isArray() && obj1.arrayGetLength() == 6) { for (i = 0; i < 6; ++i) { Object obj2 = obj1.arrayGet(i); if (obj2.isNum()) { matrixA[i] = obj2.getNum(); } } } return new GfxTilingPattern(paintTypeA, tilingTypeA, bboxA, xStepA, yStepA, &resDictA, matrixA, patObj, patternRefNum); } GfxTilingPattern::GfxTilingPattern(int paintTypeA, int tilingTypeA, const double *bboxA, double xStepA, double yStepA, const Object *resDictA, const double *matrixA, const Object *contentStreamA, int patternRefNumA) : GfxPattern(1, patternRefNumA) { int i; paintType = paintTypeA; tilingType = tilingTypeA; for (i = 0; i < 4; ++i) { bbox[i] = bboxA[i]; } xStep = xStepA; yStep = yStepA; resDict = resDictA->copy(); for (i = 0; i < 6; ++i) { matrix[i] = matrixA[i]; } contentStream = contentStreamA->copy(); } GfxTilingPattern::~GfxTilingPattern() { } GfxPattern *GfxTilingPattern::copy() const { return new GfxTilingPattern(paintType, tilingType, bbox, xStep, yStep, &resDict, matrix, &contentStream, getPatternRefNum()); } //------------------------------------------------------------------------ // GfxShadingPattern //------------------------------------------------------------------------ GfxShadingPattern *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; } dict = patObj->getDict(); obj1 = dict->lookup("Shading"); shadingA = GfxShading::parse(res, &obj1, out, state); if (!shadingA) { return nullptr; } matrixA[0] = 1; matrixA[1] = 0; matrixA[2] = 0; matrixA[3] = 1; matrixA[4] = 0; matrixA[5] = 0; obj1 = dict->lookup("Matrix"); if (obj1.isArray() && obj1.arrayGetLength() == 6) { for (i = 0; i < 6; ++i) { Object obj2 = obj1.arrayGet(i); if (obj2.isNum()) { matrixA[i] = obj2.getNum(); } } } return new GfxShadingPattern(shadingA, matrixA, patternRefNum); } GfxShadingPattern::GfxShadingPattern(GfxShading *shadingA, const double *matrixA, int patternRefNumA) : GfxPattern(2, patternRefNumA) { int i; shading = shadingA; for (i = 0; i < 6; ++i) { matrix[i] = matrixA[i]; } } GfxShadingPattern::~GfxShadingPattern() { delete shading; } GfxPattern *GfxShadingPattern::copy() const { return new GfxShadingPattern(shading->copy(), matrix, getPatternRefNum()); } //------------------------------------------------------------------------ // GfxShading //------------------------------------------------------------------------ GfxShading::GfxShading(int typeA) { type = typeA; colorSpace = nullptr; } GfxShading::GfxShading(const GfxShading *shading) { int i; type = shading->type; colorSpace = shading->colorSpace->copy(); for (i = 0; i < gfxColorMaxComps; ++i) { background.c[i] = shading->background.c[i]; } hasBackground = shading->hasBackground; bbox_xMin = shading->bbox_xMin; bbox_yMin = shading->bbox_yMin; bbox_xMax = shading->bbox_xMax; bbox_yMax = shading->bbox_yMax; hasBBox = shading->hasBBox; } GfxShading::~GfxShading() { if (colorSpace) { delete colorSpace; } } GfxShading *GfxShading::parse(GfxResources *res, Object *obj, OutputDev *out, GfxState *state) { GfxShading *shading; Dict *dict; int typeA; Object obj1; if (obj->isDict()) { dict = obj->getDict(); } else if (obj->isStream()) { dict = obj->streamGetDict(); } else { return nullptr; } obj1 = dict->lookup("ShadingType"); if (!obj1.isInt()) { error(errSyntaxWarning, -1, "Invalid ShadingType in shading dictionary"); return nullptr; } typeA = obj1.getInt(); switch (typeA) { case 1: shading = GfxFunctionShading::parse(res, dict, out, state); break; case 2: shading = GfxAxialShading::parse(res, dict, out, state); break; case 3: shading = GfxRadialShading::parse(res, dict, out, state); break; case 4: if (obj->isStream()) { shading = 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); } 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); } 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); } 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; } bool GfxShading::init(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state) { Object obj1; int i; obj1 = dict->lookup("ColorSpace"); if (!(colorSpace = GfxColorSpace::parse(res, &obj1, out, state))) { error(errSyntaxWarning, -1, "Bad color space in shading dictionary"); return false; } for (i = 0; i < gfxColorMaxComps; ++i) { background.c[i] = 0; } hasBackground = false; obj1 = dict->lookup("Background"); if (obj1.isArray()) { if (obj1.arrayGetLength() == colorSpace->getNComps()) { hasBackground = true; for (i = 0; i < colorSpace->getNComps(); ++i) { Object obj2 = obj1.arrayGet(i); background.c[i] = dblToCol(obj2.getNum(&hasBackground)); } if (!hasBackground) { error(errSyntaxWarning, -1, "Bad Background in shading dictionary"); } } else { error(errSyntaxWarning, -1, "Bad Background in shading dictionary"); } } bbox_xMin = bbox_yMin = bbox_xMax = bbox_yMax = 0; hasBBox = false; obj1 = dict->lookup("BBox"); if (obj1.isArray()) { if (obj1.arrayGetLength() == 4) { hasBBox = true; bbox_xMin = obj1.arrayGet(0).getNum(&hasBBox); bbox_yMin = obj1.arrayGet(1).getNum(&hasBBox); bbox_xMax = obj1.arrayGet(2).getNum(&hasBBox); bbox_yMax = obj1.arrayGet(3).getNum(&hasBBox); if (!hasBBox) { error(errSyntaxWarning, -1, "Bad BBox in shading dictionary (Values not numbers)"); } } else { error(errSyntaxWarning, -1, "Bad BBox in shading dictionary"); } } return true; } //------------------------------------------------------------------------ // GfxFunctionShading //------------------------------------------------------------------------ GfxFunctionShading::GfxFunctionShading(double x0A, double y0A, double x1A, double y1A, const double *matrixA, std::vector> &&funcsA) : GfxShading(1), funcs(std::move(funcsA)) { x0 = x0A; y0 = y0A; x1 = x1A; y1 = y1A; for (int i = 0; i < 6; ++i) { matrix[i] = matrixA[i]; } } GfxFunctionShading::GfxFunctionShading(const GfxFunctionShading *shading) : GfxShading(shading) { x0 = shading->x0; y0 = shading->y0; x1 = shading->x1; y1 = shading->y1; for (int i = 0; i < 6; ++i) { matrix[i] = shading->matrix[i]; } for (const auto &f : shading->funcs) { funcs.emplace_back(f->copy()); } } GfxFunctionShading::~GfxFunctionShading() { } GfxFunctionShading *GfxFunctionShading::parse(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state) { GfxFunctionShading *shading; double x0A, y0A, x1A, y1A; double matrixA[6]; std::vector> funcsA; Object obj1; int i; x0A = y0A = 0; x1A = y1A = 1; obj1 = dict->lookup("Domain"); if (obj1.isArray() && obj1.arrayGetLength() == 4) { bool decodeOk = true; x0A = obj1.arrayGet(0).getNum(&decodeOk); x1A = obj1.arrayGet(1).getNum(&decodeOk); y0A = obj1.arrayGet(2).getNum(&decodeOk); y1A = obj1.arrayGet(3).getNum(&decodeOk); if (!decodeOk) { error(errSyntaxWarning, -1, "Invalid Domain array in function shading dictionary"); return nullptr; } } matrixA[0] = 1; matrixA[1] = 0; matrixA[2] = 0; matrixA[3] = 1; matrixA[4] = 0; matrixA[5] = 0; obj1 = dict->lookup("Matrix"); if (obj1.isArray() && obj1.arrayGetLength() == 6) { bool decodeOk = true; matrixA[0] = obj1.arrayGet(0).getNum(&decodeOk); matrixA[1] = obj1.arrayGet(1).getNum(&decodeOk); matrixA[2] = obj1.arrayGet(2).getNum(&decodeOk); matrixA[3] = obj1.arrayGet(3).getNum(&decodeOk); matrixA[4] = obj1.arrayGet(4).getNum(&decodeOk); matrixA[5] = obj1.arrayGet(5).getNum(&decodeOk); if (!decodeOk) { error(errSyntaxWarning, -1, "Invalid Matrix array in function shading dictionary"); return nullptr; } } obj1 = dict->lookup("Function"); if (obj1.isArray()) { const int nFuncsA = obj1.arrayGetLength(); if (nFuncsA > gfxColorMaxComps || nFuncsA <= 0) { error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary"); return nullptr; } for (i = 0; i < nFuncsA; ++i) { Object obj2 = obj1.arrayGet(i); Function *f = Function::parse(&obj2); if (!f) { return nullptr; } funcsA.emplace_back(f); } } else { Function *f = Function::parse(&obj1); if (!f) { return nullptr; } funcsA.emplace_back(f); } shading = new GfxFunctionShading(x0A, y0A, x1A, y1A, matrixA, std::move(funcsA)); if (!shading->init(res, dict, out, state)) { delete shading; return nullptr; } return shading; } bool GfxFunctionShading::init(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state) { const bool parentInit = GfxShading::init(res, dict, out, state); if (!parentInit) { return false; } // funcs needs to be one of the two: // * One function 2-in -> nComps-out // * nComps functions 2-in -> 1-out const int nComps = colorSpace->getNComps(); const int nFuncs = funcs.size(); if (nFuncs == 1) { if (funcs[0]->getInputSize() != 2) { error(errSyntaxWarning, -1, "GfxFunctionShading: function with input size != 2"); return false; } if (funcs[0]->getOutputSize() != nComps) { error(errSyntaxWarning, -1, "GfxFunctionShading: function with wrong output size"); return false; } } else if (nFuncs == nComps) { for (const std::unique_ptr &f : funcs) { if (f->getInputSize() != 2) { error(errSyntaxWarning, -1, "GfxFunctionShading: function with input size != 2"); return false; } if (f->getOutputSize() != 1) { error(errSyntaxWarning, -1, "GfxFunctionShading: function with wrong output size"); return false; } } } else { return false; } return true; } GfxShading *GfxFunctionShading::copy() const { return new GfxFunctionShading(this); } void GfxFunctionShading::getColor(double x, double y, GfxColor *color) const { double in[2], out[gfxColorMaxComps]; // NB: there can be one function with n outputs or n functions with // one output each (where n = number of color components) for (double &i : out) { i = 0; } in[0] = x; in[1] = y; for (int i = 0; i < getNFuncs(); ++i) { funcs[i]->transform(in, &out[i]); } for (int i = 0; i < gfxColorMaxComps; ++i) { color->c[i] = dblToCol(out[i]); } } //------------------------------------------------------------------------ // GfxUnivariateShading //------------------------------------------------------------------------ GfxUnivariateShading::GfxUnivariateShading(int typeA, double t0A, double t1A, std::vector> &&funcsA, bool extend0A, bool extend1A) : GfxShading(typeA), funcs(std::move(funcsA)) { t0 = t0A; t1 = t1A; extend0 = extend0A; extend1 = extend1A; cacheSize = 0; lastMatch = 0; cacheBounds = nullptr; cacheCoeff = nullptr; cacheValues = nullptr; } GfxUnivariateShading::GfxUnivariateShading(const GfxUnivariateShading *shading) : GfxShading(shading) { t0 = shading->t0; t1 = shading->t1; for (const auto &f : shading->funcs) { funcs.emplace_back(f->copy()); } extend0 = shading->extend0; extend1 = shading->extend1; cacheSize = 0; lastMatch = 0; cacheBounds = nullptr; cacheCoeff = nullptr; cacheValues = nullptr; } GfxUnivariateShading::~GfxUnivariateShading() { gfree(cacheBounds); } int GfxUnivariateShading::getColor(double t, GfxColor *color) { double out[gfxColorMaxComps]; // NB: there can be one function with n outputs or n functions with // one output each (where n = number of color components) const int nComps = getNFuncs() * funcs[0]->getOutputSize(); if (cacheSize > 0) { double x, ix, *l, *u, *upper; if (cacheBounds[lastMatch - 1] >= t) { upper = std::lower_bound(cacheBounds, cacheBounds + lastMatch - 1, t); lastMatch = static_cast(upper - cacheBounds); lastMatch = std::min(std::max(1, lastMatch), cacheSize - 1); } else if (cacheBounds[lastMatch] < t) { upper = std::lower_bound(cacheBounds + lastMatch + 1, cacheBounds + cacheSize, t); lastMatch = static_cast(upper - cacheBounds); lastMatch = std::min(std::max(1, lastMatch), cacheSize - 1); } x = (t - cacheBounds[lastMatch - 1]) * cacheCoeff[lastMatch]; ix = 1.0 - x; u = cacheValues + lastMatch * nComps; l = u - nComps; for (int i = 0; i < nComps; ++i) { out[i] = ix * l[i] + x * u[i]; } } else { for (int i = 0; i < nComps; ++i) { out[i] = 0; } for (int i = 0; i < getNFuncs(); ++i) { funcs[i]->transform(&t, &out[i]); } } for (int i = 0; i < nComps; ++i) { color->c[i] = dblToCol(out[i]); } return nComps; } void GfxUnivariateShading::setupCache(const Matrix *ctm, double xMin, double yMin, double xMax, double yMax) { double sMin, sMax, tMin, tMax, upperBound; int i, j, nComps, maxSize; gfree(cacheBounds); cacheBounds = nullptr; cacheSize = 0; if (unlikely(getNFuncs() < 1)) { return; } // NB: there can be one function with n outputs or n functions with // one output each (where n = number of color components) nComps = getNFuncs() * funcs[0]->getOutputSize(); getParameterRange(&sMin, &sMax, xMin, yMin, xMax, yMax); upperBound = ctm->norm() * getDistance(sMin, sMax); maxSize = static_cast(ceil(upperBound)); maxSize = std::max(maxSize, 2); { double x[4], y[4]; ctm->transform(xMin, yMin, &x[0], &y[0]); ctm->transform(xMax, yMin, &x[1], &y[1]); ctm->transform(xMin, yMax, &x[2], &y[2]); ctm->transform(xMax, yMax, &x[3], &y[3]); xMin = xMax = x[0]; yMin = yMax = y[0]; for (i = 1; i < 4; i++) { xMin = std::min(xMin, x[i]); yMin = std::min(yMin, y[i]); xMax = std::max(xMax, x[i]); yMax = std::max(yMax, y[i]); } } if (maxSize > (xMax - xMin) * (yMax - yMin)) { return; } if (t0 < t1) { tMin = t0 + sMin * (t1 - t0); tMax = t0 + sMax * (t1 - t0); } else { tMin = t0 + sMax * (t1 - t0); tMax = t0 + sMin * (t1 - t0); } cacheBounds = (double *)gmallocn_checkoverflow(maxSize, sizeof(double) * (nComps + 2)); if (unlikely(!cacheBounds)) { return; } cacheCoeff = cacheBounds + maxSize; cacheValues = cacheCoeff + maxSize; if (cacheSize != 0) { for (j = 0; j < cacheSize; ++j) { cacheCoeff[j] = 1 / (cacheBounds[j + 1] - cacheBounds[j]); } } else if (tMax != tMin) { double step = (tMax - tMin) / (maxSize - 1); double coeff = (maxSize - 1) / (tMax - tMin); cacheSize = maxSize; for (j = 0; j < cacheSize; ++j) { cacheBounds[j] = tMin + j * step; cacheCoeff[j] = coeff; for (i = 0; i < nComps; ++i) { cacheValues[j * nComps + i] = 0; } for (i = 0; i < getNFuncs(); ++i) { funcs[i]->transform(&cacheBounds[j], &cacheValues[j * nComps + i]); } } } lastMatch = 1; } bool GfxUnivariateShading::init(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state) { const bool parentInit = GfxShading::init(res, dict, out, state); if (!parentInit) { return false; } // funcs needs to be one of the two: // * One function 1-in -> nComps-out // * nComps functions 1-in -> 1-out const int nComps = colorSpace->getNComps(); const int nFuncs = funcs.size(); if (nFuncs == 1) { if (funcs[0]->getInputSize() != 1) { error(errSyntaxWarning, -1, "GfxUnivariateShading: function with input size != 2"); return false; } if (funcs[0]->getOutputSize() != nComps) { error(errSyntaxWarning, -1, "GfxUnivariateShading: function with wrong output size"); return false; } } else if (nFuncs == nComps) { for (const std::unique_ptr &f : funcs) { if (f->getInputSize() != 1) { error(errSyntaxWarning, -1, "GfxUnivariateShading: function with input size != 2"); return false; } if (f->getOutputSize() != 1) { error(errSyntaxWarning, -1, "GfxUnivariateShading: function with wrong output size"); return false; } } } else { return false; } return true; } //------------------------------------------------------------------------ // GfxAxialShading //------------------------------------------------------------------------ GfxAxialShading::GfxAxialShading(double x0A, double y0A, double x1A, double y1A, double t0A, double t1A, std::vector> &&funcsA, bool extend0A, bool extend1A) : GfxUnivariateShading(2, t0A, t1A, std::move(funcsA), extend0A, extend1A) { x0 = x0A; y0 = y0A; x1 = x1A; y1 = y1A; } GfxAxialShading::GfxAxialShading(const GfxAxialShading *shading) : GfxUnivariateShading(shading) { x0 = shading->x0; y0 = shading->y0; x1 = shading->x1; y1 = shading->y1; } GfxAxialShading::~GfxAxialShading() { } GfxAxialShading *GfxAxialShading::parse(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state) { GfxAxialShading *shading; double x0A, y0A, x1A, y1A; double t0A, t1A; std::vector> funcsA; bool extend0A, extend1A; Object obj1; x0A = y0A = x1A = y1A = 0; obj1 = dict->lookup("Coords"); if (obj1.isArray() && obj1.arrayGetLength() == 4) { x0A = obj1.arrayGet(0).getNumWithDefaultValue(0); y0A = obj1.arrayGet(1).getNumWithDefaultValue(0); x1A = obj1.arrayGet(2).getNumWithDefaultValue(0); y1A = obj1.arrayGet(3).getNumWithDefaultValue(0); } else { error(errSyntaxWarning, -1, "Missing or invalid Coords in shading dictionary"); return nullptr; } t0A = 0; t1A = 1; obj1 = dict->lookup("Domain"); if (obj1.isArray() && obj1.arrayGetLength() == 2) { t0A = obj1.arrayGet(0).getNumWithDefaultValue(0); t1A = obj1.arrayGet(1).getNumWithDefaultValue(1); } obj1 = dict->lookup("Function"); if (obj1.isArray()) { const int nFuncsA = obj1.arrayGetLength(); if (nFuncsA > gfxColorMaxComps || nFuncsA == 0) { error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary"); return nullptr; } for (int i = 0; i < nFuncsA; ++i) { Object obj2 = obj1.arrayGet(i); Function *f = Function::parse(&obj2); if (!f) { return nullptr; } funcsA.emplace_back(f); } } else { Function *f = Function::parse(&obj1); if (!f) { return nullptr; } funcsA.emplace_back(f); } extend0A = extend1A = false; obj1 = dict->lookup("Extend"); if (obj1.isArray() && obj1.arrayGetLength() == 2) { Object obj2 = obj1.arrayGet(0); if (obj2.isBool()) { extend0A = obj2.getBool(); } else { error(errSyntaxWarning, -1, "Invalid axial shading extend (0)"); } obj2 = obj1.arrayGet(1); if (obj2.isBool()) { extend1A = obj2.getBool(); } else { error(errSyntaxWarning, -1, "Invalid axial shading extend (1)"); } } shading = new GfxAxialShading(x0A, y0A, x1A, y1A, t0A, t1A, std::move(funcsA), extend0A, extend1A); if (!shading->init(res, dict, out, state)) { delete shading; shading = nullptr; } return shading; } GfxShading *GfxAxialShading::copy() const { return new GfxAxialShading(this); } double GfxAxialShading::getDistance(double sMin, double sMax) const { double xMin, yMin, xMax, yMax; xMin = x0 + sMin * (x1 - x0); yMin = y0 + sMin * (y1 - y0); xMax = x0 + sMax * (x1 - x0); yMax = y0 + sMax * (y1 - y0); return hypot(xMax - xMin, yMax - yMin); } void GfxAxialShading::getParameterRange(double *lower, double *upper, double xMin, double yMin, double xMax, double yMax) { double pdx, pdy, invsqnorm, tdx, tdy, t, range[2]; // Linear gradients are orthogonal to the line passing through their // extremes. Because of convexity, the parameter range can be // computed as the convex hull (one the real line) of the parameter // values of the 4 corners of the box. // // The parameter value t for a point (x,y) can be computed as: // // t = (p2 - p1) . (x,y) / |p2 - p1|^2 // // t0 is the t value for the top left corner // tdx is the difference between left and right corners // tdy is the difference between top and bottom corners pdx = x1 - x0; pdy = y1 - y0; const double invsqnorm_denominator = (pdx * pdx + pdy * pdy); if (unlikely(invsqnorm_denominator == 0)) { *lower = 0; *upper = 0; return; } invsqnorm = 1.0 / invsqnorm_denominator; pdx *= invsqnorm; pdy *= invsqnorm; t = (xMin - x0) * pdx + (yMin - y0) * pdy; tdx = (xMax - xMin) * pdx; tdy = (yMax - yMin) * pdy; // Because of the linearity of the t value, tdx can simply be added // the t0 to move along the top edge. After this, *lower and *upper // represent the parameter range for the top edge, so extending it // to include the whole box simply requires adding tdy to the // correct extreme. range[0] = range[1] = t; if (tdx < 0) { range[0] += tdx; } else { range[1] += tdx; } if (tdy < 0) { range[0] += tdy; } else { range[1] += tdy; } *lower = std::max(0., std::min(1., range[0])); *upper = std::max(0., std::min(1., range[1])); } //------------------------------------------------------------------------ // GfxRadialShading //------------------------------------------------------------------------ #ifndef RADIAL_EPSILON # define RADIAL_EPSILON (1. / 1024 / 1024) #endif GfxRadialShading::GfxRadialShading(double x0A, double y0A, double r0A, double x1A, double y1A, double r1A, double t0A, double t1A, std::vector> &&funcsA, bool extend0A, bool extend1A) : GfxUnivariateShading(3, t0A, t1A, std::move(funcsA), extend0A, extend1A) { x0 = x0A; y0 = y0A; r0 = r0A; x1 = x1A; y1 = y1A; r1 = r1A; } GfxRadialShading::GfxRadialShading(const GfxRadialShading *shading) : GfxUnivariateShading(shading) { x0 = shading->x0; y0 = shading->y0; r0 = shading->r0; x1 = shading->x1; y1 = shading->y1; r1 = shading->r1; } GfxRadialShading::~GfxRadialShading() { } GfxRadialShading *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; bool extend0A, extend1A; Object obj1; int i; x0A = y0A = r0A = x1A = y1A = r1A = 0; obj1 = dict->lookup("Coords"); if (obj1.isArray() && obj1.arrayGetLength() == 6) { x0A = obj1.arrayGet(0).getNumWithDefaultValue(0); y0A = obj1.arrayGet(1).getNumWithDefaultValue(0); r0A = obj1.arrayGet(2).getNumWithDefaultValue(0); x1A = obj1.arrayGet(3).getNumWithDefaultValue(0); y1A = obj1.arrayGet(4).getNumWithDefaultValue(0); r1A = obj1.arrayGet(5).getNumWithDefaultValue(0); } else { error(errSyntaxWarning, -1, "Missing or invalid Coords in shading dictionary"); return nullptr; } t0A = 0; t1A = 1; obj1 = dict->lookup("Domain"); if (obj1.isArray() && obj1.arrayGetLength() == 2) { t0A = obj1.arrayGet(0).getNumWithDefaultValue(0); t1A = obj1.arrayGet(1).getNumWithDefaultValue(1); } obj1 = dict->lookup("Function"); if (obj1.isArray()) { const int nFuncsA = obj1.arrayGetLength(); if (nFuncsA > gfxColorMaxComps) { error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary"); return nullptr; } for (i = 0; i < nFuncsA; ++i) { Object obj2 = obj1.arrayGet(i); Function *f = Function::parse(&obj2); if (!f) { return nullptr; } funcsA.emplace_back(f); } } else { Function *f = Function::parse(&obj1); if (!f) { return nullptr; } funcsA.emplace_back(f); } extend0A = extend1A = false; obj1 = dict->lookup("Extend"); if (obj1.isArray() && obj1.arrayGetLength() == 2) { extend0A = obj1.arrayGet(0).getBoolWithDefaultValue(false); extend1A = obj1.arrayGet(1).getBoolWithDefaultValue(false); } shading = new GfxRadialShading(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 shading; } GfxShading *GfxRadialShading::copy() const { return new GfxRadialShading(this); } double GfxRadialShading::getDistance(double sMin, double sMax) const { double xMin, yMin, rMin, xMax, yMax, rMax; xMin = x0 + sMin * (x1 - x0); yMin = y0 + sMin * (y1 - y0); rMin = r0 + sMin * (r1 - r0); xMax = x0 + sMax * (x1 - x0); yMax = y0 + sMax * (y1 - y0); rMax = r0 + sMax * (r1 - r0); return hypot(xMax - xMin, yMax - yMin) + fabs(rMax - rMin); } // extend range, adapted from cairo, radialExtendRange static bool radialExtendRange(double range[2], double value, bool valid) { if (!valid) { range[0] = range[1] = value; } else if (value < range[0]) { range[0] = value; } else if (value > range[1]) { range[1] = value; } return true; } inline void radialEdge(double num, double den, double delta, double lower, double upper, double dr, double mindr, bool &valid, double *range) { if (fabs(den) >= RADIAL_EPSILON) { double t_edge, v; t_edge = (num) / (den); v = t_edge * (delta); if (t_edge * dr >= mindr && (lower) <= v && v <= (upper)) { valid = radialExtendRange(range, t_edge, valid); } } } inline void radialCorner1(double x, double y, double &b, double dx, double dy, double cr, double dr, double mindr, bool &valid, double *range) { b = (x)*dx + (y)*dy + cr * dr; if (fabs(b) >= RADIAL_EPSILON) { double t_corner; double x2 = (x) * (x); double y2 = (y) * (y); double cr2 = (cr) * (cr); double c = x2 + y2 - cr2; t_corner = 0.5 * c / b; if (t_corner * dr >= mindr) { valid = radialExtendRange(range, t_corner, valid); } } } inline void radialCorner2(double x, double y, double a, double &b, double &c, double &d, double dx, double dy, double cr, double inva, double dr, double mindr, bool &valid, double *range) { b = (x)*dx + (y)*dy + cr * dr; c = (x) * (x) + (y) * (y)-cr * cr; d = b * b - a * c; if (d >= 0) { double t_corner; d = sqrt(d); t_corner = (b + d) * inva; if (t_corner * dr >= mindr) { valid = radialExtendRange(range, t_corner, valid); } t_corner = (b - d) * inva; if (t_corner * dr >= mindr) { valid = radialExtendRange(range, t_corner, valid); } } } void GfxRadialShading::getParameterRange(double *lower, double *upper, double xMin, double yMin, double xMax, double yMax) { double cx, cy, cr, dx, dy, dr; double a, x_focus, y_focus; double mindr, minx, miny, maxx, maxy; double range[2]; bool valid; // A radial pattern is considered degenerate if it can be // represented as a solid or clear pattern. This corresponds to one // of the two cases: // // 1) The radii are both very small: // |dr| < FLT_EPSILON && min (r0, r1) < FLT_EPSILON // // 2) The two circles have about the same radius and are very // close to each other (approximately a cylinder gradient that // doesn't move with the parameter): // |dr| < FLT_EPSILON && max (|dx|, |dy|) < 2 * FLT_EPSILON if (xMin >= xMax || yMin >= yMax || (fabs(r0 - r1) < RADIAL_EPSILON && (std::min(r0, r1) < RADIAL_EPSILON || std::max(fabs(x0 - x1), fabs(y0 - y1)) < 2 * RADIAL_EPSILON))) { *lower = *upper = 0; return; } range[0] = range[1] = 0; valid = false; x_focus = y_focus = 0; // silence gcc cx = x0; cy = y0; cr = r0; dx = x1 - cx; dy = y1 - cy; dr = r1 - cr; // translate by -(cx, cy) to simplify computations xMin -= cx; yMin -= cy; xMax -= cx; yMax -= cy; // enlarge boundaries slightly to avoid rounding problems in the // parameter range computation xMin -= RADIAL_EPSILON; yMin -= RADIAL_EPSILON; xMax += RADIAL_EPSILON; yMax += RADIAL_EPSILON; // enlarge boundaries even more to avoid rounding problems when // testing if a point belongs to the box minx = xMin - RADIAL_EPSILON; miny = yMin - RADIAL_EPSILON; maxx = xMax + RADIAL_EPSILON; maxy = yMax + RADIAL_EPSILON; // we dont' allow negative radiuses, so we will be checking that // t*dr >= mindr to consider t valid mindr = -(cr + RADIAL_EPSILON); // After the previous transformations, the start circle is centered // in the origin and has radius cr. A 1-unit change in the t // parameter corresponds to dx,dy,dr changes in the x,y,r of the // circle (center coordinates, radius). // // To compute the minimum range needed to correctly draw the // pattern, we start with an empty range and extend it to include // the circles touching the bounding box or within it. // Focus, the point where the circle has radius == 0. // // r = cr + t * dr = 0 // t = -cr / dr // // If the radius is constant (dr == 0) there is no focus (the // gradient represents a cylinder instead of a cone). if (fabs(dr) >= RADIAL_EPSILON) { double t_focus; t_focus = -cr / dr; x_focus = t_focus * dx; y_focus = t_focus * dy; if (minx <= x_focus && x_focus <= maxx && miny <= y_focus && y_focus <= maxy) { valid = radialExtendRange(range, t_focus, valid); } } // Circles externally tangent to box edges. // // All circles have center in (dx, dy) * t // // If the circle is tangent to the line defined by the edge of the // box, then at least one of the following holds true: // // (dx*t) + (cr + dr*t) == x0 (left edge) // (dx*t) - (cr + dr*t) == x1 (right edge) // (dy*t) + (cr + dr*t) == y0 (top edge) // (dy*t) - (cr + dr*t) == y1 (bottom edge) // // The solution is only valid if the tangent point is actually on // the edge, i.e. if its y coordinate is in [y0,y1] for left/right // edges and if its x coordinate is in [x0,x1] for top/bottom edges. // // For the first equation: // // (dx + dr) * t = x0 - cr // t = (x0 - cr) / (dx + dr) // y = dy * t // // in the code this becomes: // // t_edge = (num) / (den) // v = (delta) * t_edge // // If the denominator in t is 0, the pattern is tangent to a line // parallel to the edge under examination. The corner-case where the // boundary line is the same as the edge is handled by the focus // point case and/or by the a==0 case. // circles tangent (externally) to left/right/top/bottom edge radialEdge(xMin - cr, dx + dr, dy, miny, maxy, dr, mindr, valid, range); radialEdge(xMax + cr, dx - dr, dy, miny, maxy, dr, mindr, valid, range); radialEdge(yMin - cr, dy + dr, dx, minx, maxx, dr, mindr, valid, range); radialEdge(yMax + cr, dy - dr, dx, minx, maxx, dr, mindr, valid, range); // Circles passing through a corner. // // A circle passing through the point (x,y) satisfies: // // (x-t*dx)^2 + (y-t*dy)^2 == (cr + t*dr)^2 // // If we set: // a = dx^2 + dy^2 - dr^2 // b = x*dx + y*dy + cr*dr // c = x^2 + y^2 - cr^2 // we have: // a*t^2 - 2*b*t + c == 0 a = dx * dx + dy * dy - dr * dr; if (fabs(a) < RADIAL_EPSILON * RADIAL_EPSILON) { double b; // Ensure that gradients with both a and dr small are // considered degenerate. // The floating point version of the degeneracy test implemented // in _radial_pattern_is_degenerate() is: // // 1) The circles are practically the same size: // |dr| < RADIAL_EPSILON // AND // 2a) The circles are both very small: // min (r0, r1) < RADIAL_EPSILON // OR // 2b) The circles are very close to each other: // max (|dx|, |dy|) < 2 * RADIAL_EPSILON // // Assuming that the gradient is not degenerate, we want to // show that |a| < RADIAL_EPSILON^2 implies |dr| >= RADIAL_EPSILON. // // If the gradient is not degenerate yet it has |dr| < // RADIAL_EPSILON, (2b) is false, thus: // // max (|dx|, |dy|) >= 2*RADIAL_EPSILON // which implies: // 4*RADIAL_EPSILON^2 <= max (|dx|, |dy|)^2 <= dx^2 + dy^2 // // From the definition of a, we get: // a = dx^2 + dy^2 - dr^2 < RADIAL_EPSILON^2 // dx^2 + dy^2 - RADIAL_EPSILON^2 < dr^2 // 3*RADIAL_EPSILON^2 < dr^2 // // which is inconsistent with the hypotheses, thus |dr| < // RADIAL_EPSILON is false or the gradient is degenerate. assert(fabs(dr) >= RADIAL_EPSILON); // If a == 0, all the circles are tangent to a line in the // focus point. If this line is within the box extents, we // should add the circle with infinite radius, but this would // make the range unbounded. We will be limiting the range to // [0,1] anyway, so we simply add the biggest legitimate // circle (it happens for 0 or for 1). if (dr < 0) { valid = radialExtendRange(range, 0, valid); } else { valid = radialExtendRange(range, 1, valid); } // Nondegenerate, nonlimit circles passing through the corners. // // a == 0 && a*t^2 - 2*b*t + c == 0 // // t = c / (2*b) // // The b == 0 case has just been handled, so we only have to // compute this if b != 0. // circles touching each corner radialCorner1(xMin, yMin, b, dx, dy, cr, dr, mindr, valid, range); radialCorner1(xMin, yMax, b, dx, dy, cr, dr, mindr, valid, range); radialCorner1(xMax, yMin, b, dx, dy, cr, dr, mindr, valid, range); radialCorner1(xMax, yMax, b, dx, dy, cr, dr, mindr, valid, range); } else { double inva, b, c, d; inva = 1 / a; // Nondegenerate, nonlimit circles passing through the corners. // // a != 0 && a*t^2 - 2*b*t + c == 0 // // t = (b +- sqrt (b*b - a*c)) / a // // If the argument of sqrt() is negative, then no circle // passes through the corner. // circles touching each corner radialCorner2(xMin, yMin, a, b, c, d, dx, dy, cr, inva, dr, mindr, valid, range); radialCorner2(xMin, yMax, a, b, c, d, dx, dy, cr, inva, dr, mindr, valid, range); radialCorner2(xMax, yMin, a, b, c, d, dx, dy, cr, inva, dr, mindr, valid, range); radialCorner2(xMax, yMax, a, b, c, d, dx, dy, cr, inva, dr, mindr, valid, range); } *lower = std::max(0., std::min(1., range[0])); *upper = std::max(0., std::min(1., range[1])); } //------------------------------------------------------------------------ // GfxShadingBitBuf //------------------------------------------------------------------------ class GfxShadingBitBuf { public: explicit GfxShadingBitBuf(Stream *strA); ~GfxShadingBitBuf(); GfxShadingBitBuf(const GfxShadingBitBuf &) = delete; GfxShadingBitBuf &operator=(const GfxShadingBitBuf &) = delete; bool getBits(int n, unsigned int *val); void flushBits(); private: Stream *str; int bitBuf; int nBits; }; GfxShadingBitBuf::GfxShadingBitBuf(Stream *strA) { str = strA; str->reset(); bitBuf = 0; nBits = 0; } GfxShadingBitBuf::~GfxShadingBitBuf() { str->close(); } bool GfxShadingBitBuf::getBits(int n, unsigned int *val) { unsigned int x; if (nBits >= n) { x = (bitBuf >> (nBits - n)) & ((1 << n) - 1); nBits -= n; } else { x = 0; if (nBits > 0) { x = bitBuf & ((1 << nBits) - 1); n -= nBits; nBits = 0; } while (n > 0) { if ((bitBuf = str->getChar()) == EOF) { nBits = 0; return false; } if (n >= 8) { x = (x << 8) | bitBuf; n -= 8; } else { x = (x << n) | (bitBuf >> (8 - n)); nBits = 8 - n; n = 0; } } } *val = x; return true; } void GfxShadingBitBuf::flushBits() { bitBuf = 0; nBits = 0; } //------------------------------------------------------------------------ // GfxGouraudTriangleShading //------------------------------------------------------------------------ GfxGouraudTriangleShading::GfxGouraudTriangleShading(int typeA, GfxGouraudVertex *verticesA, int nVerticesA, int (*trianglesA)[3], int nTrianglesA, std::vector> &&funcsA) : GfxShading(typeA), funcs(std::move(funcsA)) { vertices = verticesA; nVertices = nVerticesA; triangles = trianglesA; nTriangles = nTrianglesA; } GfxGouraudTriangleShading::GfxGouraudTriangleShading(const GfxGouraudTriangleShading *shading) : GfxShading(shading) { nVertices = shading->nVertices; vertices = (GfxGouraudVertex *)gmallocn(nVertices, sizeof(GfxGouraudVertex)); memcpy(vertices, shading->vertices, nVertices * sizeof(GfxGouraudVertex)); nTriangles = shading->nTriangles; triangles = (int(*)[3])gmallocn(nTriangles * 3, sizeof(int)); memcpy(triangles, shading->triangles, nTriangles * 3 * sizeof(int)); for (const auto &f : shading->funcs) { funcs.emplace_back(f->copy()); } } GfxGouraudTriangleShading::~GfxGouraudTriangleShading() { gfree(vertices); gfree(triangles); } GfxGouraudTriangleShading *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; double cMin[gfxColorMaxComps], cMax[gfxColorMaxComps]; double xMul, yMul; double cMul[gfxColorMaxComps]; GfxGouraudVertex *verticesA; int(*trianglesA)[3]; int nComps, nVerticesA, nTrianglesA, vertSize, triSize; unsigned int x, y, flag; unsigned int c[gfxColorMaxComps]; GfxShadingBitBuf *bitBuf; Object obj1; int i, j, k, state; obj1 = dict->lookup("BitsPerCoordinate"); if (obj1.isInt()) { coordBits = obj1.getInt(); } else { error(errSyntaxWarning, -1, "Missing or invalid BitsPerCoordinate in shading dictionary"); return nullptr; } if (unlikely(coordBits <= 0)) { error(errSyntaxWarning, -1, "Invalid BitsPerCoordinate in shading dictionary"); return nullptr; } obj1 = dict->lookup("BitsPerComponent"); if (obj1.isInt()) { compBits = obj1.getInt(); } else { error(errSyntaxWarning, -1, "Missing or invalid BitsPerComponent in shading dictionary"); return nullptr; } if (unlikely(compBits <= 0 || compBits > 31)) { error(errSyntaxWarning, -1, "Invalid BitsPerComponent in shading dictionary"); return nullptr; } flagBits = vertsPerRow = 0; // make gcc happy if (typeA == 4) { obj1 = dict->lookup("BitsPerFlag"); if (obj1.isInt()) { flagBits = obj1.getInt(); } else { error(errSyntaxWarning, -1, "Missing or invalid BitsPerFlag in shading dictionary"); return nullptr; } } else { obj1 = dict->lookup("VerticesPerRow"); if (obj1.isInt()) { vertsPerRow = obj1.getInt(); } else { error(errSyntaxWarning, -1, "Missing or invalid VerticesPerRow in shading dictionary"); return nullptr; } } obj1 = dict->lookup("Decode"); if (obj1.isArray() && obj1.arrayGetLength() >= 6) { bool decodeOk = true; xMin = obj1.arrayGet(0).getNum(&decodeOk); xMax = obj1.arrayGet(1).getNum(&decodeOk); xMul = (xMax - xMin) / (pow(2.0, coordBits) - 1); yMin = obj1.arrayGet(2).getNum(&decodeOk); yMax = obj1.arrayGet(3).getNum(&decodeOk); yMul = (yMax - yMin) / (pow(2.0, coordBits) - 1); for (i = 0; 5 + 2 * i < obj1.arrayGetLength() && i < gfxColorMaxComps; ++i) { cMin[i] = obj1.arrayGet(4 + 2 * i).getNum(&decodeOk); cMax[i] = obj1.arrayGet(5 + 2 * i).getNum(&decodeOk); cMul[i] = (cMax[i] - cMin[i]) / (double)((1u << compBits) - 1); } nComps = i; if (!decodeOk) { error(errSyntaxWarning, -1, "Missing or invalid Decode array in shading dictionary"); return nullptr; } } else { error(errSyntaxWarning, -1, "Missing or invalid Decode array in shading dictionary"); return nullptr; } obj1 = dict->lookup("Function"); if (!obj1.isNull()) { if (obj1.isArray()) { const int nFuncsA = obj1.arrayGetLength(); if (nFuncsA > gfxColorMaxComps) { error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary"); return nullptr; } for (i = 0; i < nFuncsA; ++i) { Object obj2 = obj1.arrayGet(i); Function *f = Function::parse(&obj2); if (!f) { return nullptr; } funcsA.emplace_back(f); } } else { Function *f = Function::parse(&obj1); if (!f) { return nullptr; } funcsA.emplace_back(f); } } nVerticesA = nTrianglesA = 0; verticesA = nullptr; trianglesA = nullptr; vertSize = triSize = 0; state = 0; flag = 0; // make gcc happy bitBuf = new GfxShadingBitBuf(str); while (true) { if (typeA == 4) { if (!bitBuf->getBits(flagBits, &flag)) { break; } } if (!bitBuf->getBits(coordBits, &x) || !bitBuf->getBits(coordBits, &y)) { break; } for (i = 0; i < nComps; ++i) { if (!bitBuf->getBits(compBits, &c[i])) { break; } } if (i < nComps) { break; } if (nVerticesA == vertSize) { int oldVertSize = vertSize; vertSize = (vertSize == 0) ? 16 : 2 * vertSize; verticesA = (GfxGouraudVertex *)greallocn_checkoverflow(verticesA, vertSize, sizeof(GfxGouraudVertex)); if (unlikely(!verticesA)) { error(errSyntaxWarning, -1, "GfxGouraudTriangleShading::parse: vertices size overflow"); gfree(trianglesA); delete bitBuf; return nullptr; } memset(verticesA + oldVertSize, 0, (vertSize - oldVertSize) * sizeof(GfxGouraudVertex)); } verticesA[nVerticesA].x = xMin + xMul * (double)x; verticesA[nVerticesA].y = yMin + yMul * (double)y; for (i = 0; i < nComps; ++i) { verticesA[nVerticesA].color.c[i] = dblToCol(cMin[i] + cMul[i] * (double)c[i]); } ++nVerticesA; bitBuf->flushBits(); if (typeA == 4) { if (state == 0 || state == 1) { ++state; } else if (state == 2 || flag > 0) { if (nTrianglesA == triSize) { triSize = (triSize == 0) ? 16 : 2 * triSize; trianglesA = (int(*)[3])greallocn(trianglesA, triSize * 3, sizeof(int)); } if (state == 2) { trianglesA[nTrianglesA][0] = nVerticesA - 3; trianglesA[nTrianglesA][1] = nVerticesA - 2; trianglesA[nTrianglesA][2] = nVerticesA - 1; ++state; } else if (flag == 1) { trianglesA[nTrianglesA][0] = trianglesA[nTrianglesA - 1][1]; trianglesA[nTrianglesA][1] = trianglesA[nTrianglesA - 1][2]; trianglesA[nTrianglesA][2] = nVerticesA - 1; } else { // flag == 2 trianglesA[nTrianglesA][0] = trianglesA[nTrianglesA - 1][0]; trianglesA[nTrianglesA][1] = trianglesA[nTrianglesA - 1][2]; trianglesA[nTrianglesA][2] = nVerticesA - 1; } ++nTrianglesA; } else { // state == 3 && flag == 0 state = 1; } } } delete bitBuf; if (typeA == 5 && nVerticesA > 0 && vertsPerRow > 0) { nRows = nVerticesA / vertsPerRow; nTrianglesA = (nRows - 1) * 2 * (vertsPerRow - 1); trianglesA = (int(*)[3])gmallocn_checkoverflow(nTrianglesA * 3, sizeof(int)); if (unlikely(!trianglesA)) { gfree(verticesA); return nullptr; } k = 0; for (i = 0; i < nRows - 1; ++i) { for (j = 0; j < vertsPerRow - 1; ++j) { trianglesA[k][0] = i * vertsPerRow + j; trianglesA[k][1] = i * vertsPerRow + j + 1; trianglesA[k][2] = (i + 1) * vertsPerRow + j; ++k; trianglesA[k][0] = i * vertsPerRow + j + 1; trianglesA[k][1] = (i + 1) * vertsPerRow + j; trianglesA[k][2] = (i + 1) * vertsPerRow + j + 1; ++k; } } } shading = new GfxGouraudTriangleShading(typeA, verticesA, nVerticesA, trianglesA, nTrianglesA, std::move(funcsA)); if (!shading->init(res, dict, out, gfxState)) { delete shading; return nullptr; } return shading; } bool GfxGouraudTriangleShading::init(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state) { const bool parentInit = GfxShading::init(res, dict, out, state); if (!parentInit) { return false; } // funcs needs to be one of the three: // * One function 1-in -> nComps-out // * nComps functions 1-in -> 1-out // * empty const int nComps = colorSpace->getNComps(); const int nFuncs = funcs.size(); if (nFuncs == 1) { if (funcs[0]->getInputSize() != 1) { error(errSyntaxWarning, -1, "GfxGouraudTriangleShading: function with input size != 2"); return false; } if (funcs[0]->getOutputSize() != nComps) { error(errSyntaxWarning, -1, "GfxGouraudTriangleShading: function with wrong output size"); return false; } } else if (nFuncs == nComps) { for (const std::unique_ptr &f : funcs) { if (f->getInputSize() != 1) { error(errSyntaxWarning, -1, "GfxGouraudTriangleShading: function with input size != 2"); return false; } if (f->getOutputSize() != 1) { error(errSyntaxWarning, -1, "GfxGouraudTriangleShading: function with wrong output size"); return false; } } } else if (nFuncs != 0) { return false; } return true; } GfxShading *GfxGouraudTriangleShading::copy() const { return new GfxGouraudTriangleShading(this); } void GfxGouraudTriangleShading::getTriangle(int i, double *x0, double *y0, GfxColor *color0, double *x1, double *y1, GfxColor *color1, double *x2, double *y2, GfxColor *color2) { int v; assert(!isParameterized()); v = triangles[i][0]; *x0 = vertices[v].x; *y0 = vertices[v].y; *color0 = vertices[v].color; v = triangles[i][1]; *x1 = vertices[v].x; *y1 = vertices[v].y; *color1 = vertices[v].color; v = triangles[i][2]; *x2 = vertices[v].x; *y2 = vertices[v].y; *color2 = vertices[v].color; } void GfxGouraudTriangleShading::getParameterizedColor(double t, GfxColor *color) const { double out[gfxColorMaxComps]; for (unsigned int j = 0; j < funcs.size(); ++j) { funcs[j]->transform(&t, &out[j]); } for (int j = 0; j < gfxColorMaxComps; ++j) { color->c[j] = dblToCol(out[j]); } } void GfxGouraudTriangleShading::getTriangle(int i, double *x0, double *y0, double *color0, double *x1, double *y1, double *color1, double *x2, double *y2, double *color2) { int v; assert(isParameterized()); v = triangles[i][0]; if (likely(v >= 0 && v < nVertices)) { *x0 = vertices[v].x; *y0 = vertices[v].y; *color0 = colToDbl(vertices[v].color.c[0]); } v = triangles[i][1]; if (likely(v >= 0 && v < nVertices)) { *x1 = vertices[v].x; *y1 = vertices[v].y; *color1 = colToDbl(vertices[v].color.c[0]); } v = triangles[i][2]; if (likely(v >= 0 && v < nVertices)) { *x2 = vertices[v].x; *y2 = vertices[v].y; *color2 = colToDbl(vertices[v].color.c[0]); } } //------------------------------------------------------------------------ // GfxPatchMeshShading //------------------------------------------------------------------------ GfxPatchMeshShading::GfxPatchMeshShading(int typeA, GfxPatch *patchesA, int nPatchesA, std::vector> &&funcsA) : GfxShading(typeA), funcs(std::move(funcsA)) { patches = patchesA; nPatches = nPatchesA; } GfxPatchMeshShading::GfxPatchMeshShading(const GfxPatchMeshShading *shading) : GfxShading(shading) { nPatches = shading->nPatches; patches = (GfxPatch *)gmallocn(nPatches, sizeof(GfxPatch)); memcpy(patches, shading->patches, nPatches * sizeof(GfxPatch)); for (const auto &f : shading->funcs) { funcs.emplace_back(f->copy()); } } GfxPatchMeshShading::~GfxPatchMeshShading() { gfree(patches); } GfxPatchMeshShading *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; double cMin[gfxColorMaxComps], cMax[gfxColorMaxComps]; double xMul, yMul; double cMul[gfxColorMaxComps]; GfxPatch *patchesA, *p; int nComps, nPatchesA, patchesSize, nPts, nColors; unsigned int flag; double x[16], y[16]; unsigned int xi, yi; double c[4][gfxColorMaxComps]; unsigned int ci; Object obj1; int i, j; obj1 = dict->lookup("BitsPerCoordinate"); if (obj1.isInt()) { coordBits = obj1.getInt(); } else { error(errSyntaxWarning, -1, "Missing or invalid BitsPerCoordinate in shading dictionary"); return nullptr; } if (unlikely(coordBits <= 0)) { error(errSyntaxWarning, -1, "Invalid BitsPerCoordinate in shading dictionary"); return nullptr; } obj1 = dict->lookup("BitsPerComponent"); if (obj1.isInt()) { compBits = obj1.getInt(); } else { error(errSyntaxWarning, -1, "Missing or invalid BitsPerComponent in shading dictionary"); return nullptr; } if (unlikely(compBits <= 0 || compBits > 31)) { error(errSyntaxWarning, -1, "Invalid BitsPerComponent in shading dictionary"); return nullptr; } obj1 = dict->lookup("BitsPerFlag"); if (obj1.isInt()) { flagBits = obj1.getInt(); } else { error(errSyntaxWarning, -1, "Missing or invalid BitsPerFlag in shading dictionary"); return nullptr; } obj1 = dict->lookup("Decode"); if (obj1.isArray() && obj1.arrayGetLength() >= 6) { bool decodeOk = true; xMin = obj1.arrayGet(0).getNum(&decodeOk); xMax = obj1.arrayGet(1).getNum(&decodeOk); xMul = (xMax - xMin) / (pow(2.0, coordBits) - 1); yMin = obj1.arrayGet(2).getNum(&decodeOk); yMax = obj1.arrayGet(3).getNum(&decodeOk); yMul = (yMax - yMin) / (pow(2.0, coordBits) - 1); for (i = 0; 5 + 2 * i < obj1.arrayGetLength() && i < gfxColorMaxComps; ++i) { cMin[i] = obj1.arrayGet(4 + 2 * i).getNum(&decodeOk); cMax[i] = obj1.arrayGet(5 + 2 * i).getNum(&decodeOk); cMul[i] = (cMax[i] - cMin[i]) / (double)((1u << compBits) - 1); } nComps = i; if (!decodeOk) { error(errSyntaxWarning, -1, "Missing or invalid Decode array in shading dictionary"); return nullptr; } } else { error(errSyntaxWarning, -1, "Missing or invalid Decode array in shading dictionary"); return nullptr; } obj1 = dict->lookup("Function"); if (!obj1.isNull()) { if (obj1.isArray()) { const int nFuncsA = obj1.arrayGetLength(); if (nFuncsA > gfxColorMaxComps) { error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary"); return nullptr; } for (i = 0; i < nFuncsA; ++i) { Object obj2 = obj1.arrayGet(i); Function *f = Function::parse(&obj2); if (!f) { return nullptr; } funcsA.emplace_back(f); } } else { Function *f = Function::parse(&obj1); if (!f) { return nullptr; } funcsA.emplace_back(f); } } nPatchesA = 0; patchesA = nullptr; patchesSize = 0; auto bitBuf = std::make_unique(str); while (true) { if (!bitBuf->getBits(flagBits, &flag)) { break; } if (typeA == 6) { switch (flag) { case 0: nPts = 12; nColors = 4; break; case 1: case 2: case 3: default: nPts = 8; nColors = 2; break; } } else { switch (flag) { case 0: nPts = 16; nColors = 4; break; case 1: case 2: case 3: default: nPts = 12; nColors = 2; break; } } for (i = 0; i < nPts; ++i) { if (!bitBuf->getBits(coordBits, &xi) || !bitBuf->getBits(coordBits, &yi)) { break; } x[i] = xMin + xMul * (double)xi; y[i] = yMin + yMul * (double)yi; } if (i < nPts) { break; } for (i = 0; i < nColors; ++i) { for (j = 0; j < nComps; ++j) { if (!bitBuf->getBits(compBits, &ci)) { break; } c[i][j] = cMin[j] + cMul[j] * (double)ci; if (funcsA.empty()) { // ... and colorspace values can also be stored into doubles. // They will be casted later. c[i][j] = dblToCol(c[i][j]); } } if (j < nComps) { break; } } if (i < nColors) { break; } if (nPatchesA == patchesSize) { int oldPatchesSize = patchesSize; patchesSize = (patchesSize == 0) ? 16 : 2 * patchesSize; patchesA = (GfxPatch *)greallocn_checkoverflow(patchesA, patchesSize, sizeof(GfxPatch)); if (unlikely(!patchesA)) { return nullptr; } memset(patchesA + oldPatchesSize, 0, (patchesSize - oldPatchesSize) * sizeof(GfxPatch)); } p = &patchesA[nPatchesA]; if (typeA == 6) { switch (flag) { case 0: p->x[0][0] = x[0]; p->y[0][0] = y[0]; p->x[0][1] = x[1]; p->y[0][1] = y[1]; p->x[0][2] = x[2]; p->y[0][2] = y[2]; p->x[0][3] = x[3]; p->y[0][3] = y[3]; p->x[1][3] = x[4]; p->y[1][3] = y[4]; p->x[2][3] = x[5]; p->y[2][3] = y[5]; p->x[3][3] = x[6]; p->y[3][3] = y[6]; p->x[3][2] = x[7]; p->y[3][2] = y[7]; p->x[3][1] = x[8]; p->y[3][1] = y[8]; p->x[3][0] = x[9]; p->y[3][0] = y[9]; p->x[2][0] = x[10]; p->y[2][0] = y[10]; p->x[1][0] = x[11]; p->y[1][0] = y[11]; for (j = 0; j < nComps; ++j) { p->color[0][0].c[j] = c[0][j]; p->color[0][1].c[j] = c[1][j]; p->color[1][1].c[j] = c[2][j]; p->color[1][0].c[j] = c[3][j]; } break; case 1: if (nPatchesA == 0) { gfree(patchesA); return nullptr; } p->x[0][0] = patchesA[nPatchesA - 1].x[0][3]; p->y[0][0] = patchesA[nPatchesA - 1].y[0][3]; p->x[0][1] = patchesA[nPatchesA - 1].x[1][3]; p->y[0][1] = patchesA[nPatchesA - 1].y[1][3]; p->x[0][2] = patchesA[nPatchesA - 1].x[2][3]; p->y[0][2] = patchesA[nPatchesA - 1].y[2][3]; p->x[0][3] = patchesA[nPatchesA - 1].x[3][3]; p->y[0][3] = patchesA[nPatchesA - 1].y[3][3]; p->x[1][3] = x[0]; p->y[1][3] = y[0]; p->x[2][3] = x[1]; p->y[2][3] = y[1]; p->x[3][3] = x[2]; p->y[3][3] = y[2]; p->x[3][2] = x[3]; p->y[3][2] = y[3]; p->x[3][1] = x[4]; p->y[3][1] = y[4]; p->x[3][0] = x[5]; p->y[3][0] = y[5]; p->x[2][0] = x[6]; p->y[2][0] = y[6]; p->x[1][0] = x[7]; p->y[1][0] = y[7]; for (j = 0; j < nComps; ++j) { p->color[0][0].c[j] = patchesA[nPatchesA - 1].color[0][1].c[j]; p->color[0][1].c[j] = patchesA[nPatchesA - 1].color[1][1].c[j]; p->color[1][1].c[j] = c[0][j]; p->color[1][0].c[j] = c[1][j]; } break; case 2: if (nPatchesA == 0) { gfree(patchesA); return nullptr; } p->x[0][0] = patchesA[nPatchesA - 1].x[3][3]; p->y[0][0] = patchesA[nPatchesA - 1].y[3][3]; p->x[0][1] = patchesA[nPatchesA - 1].x[3][2]; p->y[0][1] = patchesA[nPatchesA - 1].y[3][2]; p->x[0][2] = patchesA[nPatchesA - 1].x[3][1]; p->y[0][2] = patchesA[nPatchesA - 1].y[3][1]; p->x[0][3] = patchesA[nPatchesA - 1].x[3][0]; p->y[0][3] = patchesA[nPatchesA - 1].y[3][0]; p->x[1][3] = x[0]; p->y[1][3] = y[0]; p->x[2][3] = x[1]; p->y[2][3] = y[1]; p->x[3][3] = x[2]; p->y[3][3] = y[2]; p->x[3][2] = x[3]; p->y[3][2] = y[3]; p->x[3][1] = x[4]; p->y[3][1] = y[4]; p->x[3][0] = x[5]; p->y[3][0] = y[5]; p->x[2][0] = x[6]; p->y[2][0] = y[6]; p->x[1][0] = x[7]; p->y[1][0] = y[7]; for (j = 0; j < nComps; ++j) { p->color[0][0].c[j] = patchesA[nPatchesA - 1].color[1][1].c[j]; p->color[0][1].c[j] = patchesA[nPatchesA - 1].color[1][0].c[j]; p->color[1][1].c[j] = c[0][j]; p->color[1][0].c[j] = c[1][j]; } break; case 3: if (nPatchesA == 0) { gfree(patchesA); return nullptr; } p->x[0][0] = patchesA[nPatchesA - 1].x[3][0]; p->y[0][0] = patchesA[nPatchesA - 1].y[3][0]; p->x[0][1] = patchesA[nPatchesA - 1].x[2][0]; p->y[0][1] = patchesA[nPatchesA - 1].y[2][0]; p->x[0][2] = patchesA[nPatchesA - 1].x[1][0]; p->y[0][2] = patchesA[nPatchesA - 1].y[1][0]; p->x[0][3] = patchesA[nPatchesA - 1].x[0][0]; p->y[0][3] = patchesA[nPatchesA - 1].y[0][0]; p->x[1][3] = x[0]; p->y[1][3] = y[0]; p->x[2][3] = x[1]; p->y[2][3] = y[1]; p->x[3][3] = x[2]; p->y[3][3] = y[2]; p->x[3][2] = x[3]; p->y[3][2] = y[3]; p->x[3][1] = x[4]; p->y[3][1] = y[4]; p->x[3][0] = x[5]; p->y[3][0] = y[5]; p->x[2][0] = x[6]; p->y[2][0] = y[6]; p->x[1][0] = x[7]; p->y[1][0] = y[7]; for (j = 0; j < nComps; ++j) { p->color[0][0].c[j] = patchesA[nPatchesA - 1].color[1][0].c[j]; p->color[0][1].c[j] = patchesA[nPatchesA - 1].color[0][0].c[j]; p->color[1][1].c[j] = c[0][j]; p->color[1][0].c[j] = c[1][j]; } break; } } else { switch (flag) { case 0: p->x[0][0] = x[0]; p->y[0][0] = y[0]; p->x[0][1] = x[1]; p->y[0][1] = y[1]; p->x[0][2] = x[2]; p->y[0][2] = y[2]; p->x[0][3] = x[3]; p->y[0][3] = y[3]; p->x[1][3] = x[4]; p->y[1][3] = y[4]; p->x[2][3] = x[5]; p->y[2][3] = y[5]; p->x[3][3] = x[6]; p->y[3][3] = y[6]; p->x[3][2] = x[7]; p->y[3][2] = y[7]; p->x[3][1] = x[8]; p->y[3][1] = y[8]; p->x[3][0] = x[9]; p->y[3][0] = y[9]; p->x[2][0] = x[10]; p->y[2][0] = y[10]; p->x[1][0] = x[11]; p->y[1][0] = y[11]; p->x[1][1] = x[12]; p->y[1][1] = y[12]; p->x[1][2] = x[13]; p->y[1][2] = y[13]; p->x[2][2] = x[14]; p->y[2][2] = y[14]; p->x[2][1] = x[15]; p->y[2][1] = y[15]; for (j = 0; j < nComps; ++j) { p->color[0][0].c[j] = c[0][j]; p->color[0][1].c[j] = c[1][j]; p->color[1][1].c[j] = c[2][j]; p->color[1][0].c[j] = c[3][j]; } break; case 1: if (nPatchesA == 0) { gfree(patchesA); return nullptr; } p->x[0][0] = patchesA[nPatchesA - 1].x[0][3]; p->y[0][0] = patchesA[nPatchesA - 1].y[0][3]; p->x[0][1] = patchesA[nPatchesA - 1].x[1][3]; p->y[0][1] = patchesA[nPatchesA - 1].y[1][3]; p->x[0][2] = patchesA[nPatchesA - 1].x[2][3]; p->y[0][2] = patchesA[nPatchesA - 1].y[2][3]; p->x[0][3] = patchesA[nPatchesA - 1].x[3][3]; p->y[0][3] = patchesA[nPatchesA - 1].y[3][3]; p->x[1][3] = x[0]; p->y[1][3] = y[0]; p->x[2][3] = x[1]; p->y[2][3] = y[1]; p->x[3][3] = x[2]; p->y[3][3] = y[2]; p->x[3][2] = x[3]; p->y[3][2] = y[3]; p->x[3][1] = x[4]; p->y[3][1] = y[4]; p->x[3][0] = x[5]; p->y[3][0] = y[5]; p->x[2][0] = x[6]; p->y[2][0] = y[6]; p->x[1][0] = x[7]; p->y[1][0] = y[7]; p->x[1][1] = x[8]; p->y[1][1] = y[8]; p->x[1][2] = x[9]; p->y[1][2] = y[9]; p->x[2][2] = x[10]; p->y[2][2] = y[10]; p->x[2][1] = x[11]; p->y[2][1] = y[11]; for (j = 0; j < nComps; ++j) { p->color[0][0].c[j] = patchesA[nPatchesA - 1].color[0][1].c[j]; p->color[0][1].c[j] = patchesA[nPatchesA - 1].color[1][1].c[j]; p->color[1][1].c[j] = c[0][j]; p->color[1][0].c[j] = c[1][j]; } break; case 2: if (nPatchesA == 0) { gfree(patchesA); return nullptr; } p->x[0][0] = patchesA[nPatchesA - 1].x[3][3]; p->y[0][0] = patchesA[nPatchesA - 1].y[3][3]; p->x[0][1] = patchesA[nPatchesA - 1].x[3][2]; p->y[0][1] = patchesA[nPatchesA - 1].y[3][2]; p->x[0][2] = patchesA[nPatchesA - 1].x[3][1]; p->y[0][2] = patchesA[nPatchesA - 1].y[3][1]; p->x[0][3] = patchesA[nPatchesA - 1].x[3][0]; p->y[0][3] = patchesA[nPatchesA - 1].y[3][0]; p->x[1][3] = x[0]; p->y[1][3] = y[0]; p->x[2][3] = x[1]; p->y[2][3] = y[1]; p->x[3][3] = x[2]; p->y[3][3] = y[2]; p->x[3][2] = x[3]; p->y[3][2] = y[3]; p->x[3][1] = x[4]; p->y[3][1] = y[4]; p->x[3][0] = x[5]; p->y[3][0] = y[5]; p->x[2][0] = x[6]; p->y[2][0] = y[6]; p->x[1][0] = x[7]; p->y[1][0] = y[7]; p->x[1][1] = x[8]; p->y[1][1] = y[8]; p->x[1][2] = x[9]; p->y[1][2] = y[9]; p->x[2][2] = x[10]; p->y[2][2] = y[10]; p->x[2][1] = x[11]; p->y[2][1] = y[11]; for (j = 0; j < nComps; ++j) { p->color[0][0].c[j] = patchesA[nPatchesA - 1].color[1][1].c[j]; p->color[0][1].c[j] = patchesA[nPatchesA - 1].color[1][0].c[j]; p->color[1][1].c[j] = c[0][j]; p->color[1][0].c[j] = c[1][j]; } break; case 3: if (nPatchesA == 0) { gfree(patchesA); return nullptr; } p->x[0][0] = patchesA[nPatchesA - 1].x[3][0]; p->y[0][0] = patchesA[nPatchesA - 1].y[3][0]; p->x[0][1] = patchesA[nPatchesA - 1].x[2][0]; p->y[0][1] = patchesA[nPatchesA - 1].y[2][0]; p->x[0][2] = patchesA[nPatchesA - 1].x[1][0]; p->y[0][2] = patchesA[nPatchesA - 1].y[1][0]; p->x[0][3] = patchesA[nPatchesA - 1].x[0][0]; p->y[0][3] = patchesA[nPatchesA - 1].y[0][0]; p->x[1][3] = x[0]; p->y[1][3] = y[0]; p->x[2][3] = x[1]; p->y[2][3] = y[1]; p->x[3][3] = x[2]; p->y[3][3] = y[2]; p->x[3][2] = x[3]; p->y[3][2] = y[3]; p->x[3][1] = x[4]; p->y[3][1] = y[4]; p->x[3][0] = x[5]; p->y[3][0] = y[5]; p->x[2][0] = x[6]; p->y[2][0] = y[6]; p->x[1][0] = x[7]; p->y[1][0] = y[7]; p->x[1][1] = x[8]; p->y[1][1] = y[8]; p->x[1][2] = x[9]; p->y[1][2] = y[9]; p->x[2][2] = x[10]; p->y[2][2] = y[10]; p->x[2][1] = x[11]; p->y[2][1] = y[11]; for (j = 0; j < nComps; ++j) { p->color[0][0].c[j] = patchesA[nPatchesA - 1].color[1][0].c[j]; p->color[0][1].c[j] = patchesA[nPatchesA - 1].color[0][0].c[j]; p->color[1][1].c[j] = c[0][j]; p->color[1][0].c[j] = c[1][j]; } break; } } ++nPatchesA; bitBuf->flushBits(); } if (typeA == 6) { for (i = 0; i < nPatchesA; ++i) { p = &patchesA[i]; p->x[1][1] = (-4 * p->x[0][0] + 6 * (p->x[0][1] + p->x[1][0]) - 2 * (p->x[0][3] + p->x[3][0]) + 3 * (p->x[3][1] + p->x[1][3]) - p->x[3][3]) / 9; p->y[1][1] = (-4 * p->y[0][0] + 6 * (p->y[0][1] + p->y[1][0]) - 2 * (p->y[0][3] + p->y[3][0]) + 3 * (p->y[3][1] + p->y[1][3]) - p->y[3][3]) / 9; p->x[1][2] = (-4 * p->x[0][3] + 6 * (p->x[0][2] + p->x[1][3]) - 2 * (p->x[0][0] + p->x[3][3]) + 3 * (p->x[3][2] + p->x[1][0]) - p->x[3][0]) / 9; p->y[1][2] = (-4 * p->y[0][3] + 6 * (p->y[0][2] + p->y[1][3]) - 2 * (p->y[0][0] + p->y[3][3]) + 3 * (p->y[3][2] + p->y[1][0]) - p->y[3][0]) / 9; p->x[2][1] = (-4 * p->x[3][0] + 6 * (p->x[3][1] + p->x[2][0]) - 2 * (p->x[3][3] + p->x[0][0]) + 3 * (p->x[0][1] + p->x[2][3]) - p->x[0][3]) / 9; p->y[2][1] = (-4 * p->y[3][0] + 6 * (p->y[3][1] + p->y[2][0]) - 2 * (p->y[3][3] + p->y[0][0]) + 3 * (p->y[0][1] + p->y[2][3]) - p->y[0][3]) / 9; p->x[2][2] = (-4 * p->x[3][3] + 6 * (p->x[3][2] + p->x[2][3]) - 2 * (p->x[3][0] + p->x[0][3]) + 3 * (p->x[0][2] + p->x[2][0]) - p->x[0][0]) / 9; p->y[2][2] = (-4 * p->y[3][3] + 6 * (p->y[3][2] + p->y[2][3]) - 2 * (p->y[3][0] + p->y[0][3]) + 3 * (p->y[0][2] + p->y[2][0]) - p->y[0][0]) / 9; } } shading = new GfxPatchMeshShading(typeA, patchesA, nPatchesA, std::move(funcsA)); if (!shading->init(res, dict, out, state)) { delete shading; return nullptr; } return shading; } bool GfxPatchMeshShading::init(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state) { const bool parentInit = GfxShading::init(res, dict, out, state); if (!parentInit) { return false; } // funcs needs to be one of the three: // * One function 1-in -> nComps-out // * nComps functions 1-in -> 1-out // * empty const int nComps = colorSpace->getNComps(); const int nFuncs = funcs.size(); if (nFuncs == 1) { if (funcs[0]->getInputSize() != 1) { error(errSyntaxWarning, -1, "GfxPatchMeshShading: function with input size != 2"); return false; } if (funcs[0]->getOutputSize() != nComps) { error(errSyntaxWarning, -1, "GfxPatchMeshShading: function with wrong output size"); return false; } } else if (nFuncs == nComps) { for (const std::unique_ptr &f : funcs) { if (f->getInputSize() != 1) { error(errSyntaxWarning, -1, "GfxPatchMeshShading: function with input size != 2"); return false; } if (f->getOutputSize() != 1) { error(errSyntaxWarning, -1, "GfxPatchMeshShading: function with wrong output size"); return false; } } } else if (nFuncs != 0) { return false; } return true; } void GfxPatchMeshShading::getParameterizedColor(double t, GfxColor *color) const { double out[gfxColorMaxComps] = {}; for (unsigned int j = 0; j < funcs.size(); ++j) { funcs[j]->transform(&t, &out[j]); } for (int j = 0; j < gfxColorMaxComps; ++j) { color->c[j] = dblToCol(out[j]); } } GfxShading *GfxPatchMeshShading::copy() const { return new GfxPatchMeshShading(this); } //------------------------------------------------------------------------ // GfxImageColorMap //------------------------------------------------------------------------ GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode, GfxColorSpace *colorSpaceA) { GfxIndexedColorSpace *indexedCS; GfxSeparationColorSpace *sepCS; int maxPixel, indexHigh; unsigned char *indexedLookup; const Function *sepFunc; double x[gfxColorMaxComps]; double y[gfxColorMaxComps] = {}; int i, j, k; double mapped; bool useByteLookup; ok = true; useMatte = false; colorSpace = colorSpaceA; // initialize for (k = 0; k < gfxColorMaxComps; ++k) { lookup[k] = nullptr; lookup2[k] = nullptr; } byte_lookup = nullptr; // bits per component and color space if (unlikely(bitsA <= 0 || bitsA > 30)) { goto err1; } bits = bitsA; maxPixel = (1 << bits) - 1; // this is a hack to support 16 bits images, everywhere // we assume a component fits in 8 bits, with this hack // we treat 16 bit images as 8 bit ones until it's fixed correctly. // The hack has another part on ImageStream::getLine if (maxPixel > 255) { maxPixel = 255; } // get decode map if (decode->isNull()) { nComps = colorSpace->getNComps(); colorSpace->getDefaultRanges(decodeLow, decodeRange, maxPixel); } else if (decode->isArray()) { nComps = decode->arrayGetLength() / 2; if (nComps < colorSpace->getNComps()) { goto err1; } if (nComps > colorSpace->getNComps()) { error(errSyntaxWarning, -1, "Too many elements in Decode array"); nComps = colorSpace->getNComps(); } for (i = 0; i < nComps; ++i) { Object obj = decode->arrayGet(2 * i); if (!obj.isNum()) { goto err1; } decodeLow[i] = obj.getNum(); obj = decode->arrayGet(2 * i + 1); if (!obj.isNum()) { goto err1; } decodeRange[i] = obj.getNum() - decodeLow[i]; } } else { goto err1; } // Construct a lookup table -- this stores pre-computed decoded // values for each component, i.e., the result of applying the // decode mapping to each possible image pixel component value. for (k = 0; k < nComps; ++k) { lookup[k] = (GfxColorComp *)gmallocn(maxPixel + 1, sizeof(GfxColorComp)); for (i = 0; i <= maxPixel; ++i) { lookup[k][i] = dblToCol(decodeLow[k] + (i * decodeRange[k]) / maxPixel); } } // Optimization: for Indexed and Separation color spaces (which have // only one component), we pre-compute a second lookup table with // color values colorSpace2 = nullptr; nComps2 = 0; useByteLookup = false; switch (colorSpace->getMode()) { 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; colorSpace2 = indexedCS->getBase(); indexHigh = indexedCS->getIndexHigh(); nComps2 = colorSpace2->getNComps(); indexedLookup = indexedCS->getLookup(); colorSpace2->getDefaultRanges(x, y, indexHigh); if (colorSpace2->useGetGrayLine() || colorSpace2->useGetRGBLine() || colorSpace2->useGetCMYKLine() || colorSpace2->useGetDeviceNLine()) { byte_lookup = (unsigned char *)gmallocn((maxPixel + 1), nComps2); useByteLookup = true; } for (k = 0; k < nComps2; ++k) { lookup2[k] = (GfxColorComp *)gmallocn(maxPixel + 1, sizeof(GfxColorComp)); for (i = 0; i <= maxPixel; ++i) { j = (int)(decodeLow[0] + (i * decodeRange[0]) / maxPixel + 0.5); if (j < 0) { j = 0; } else if (j > indexHigh) { j = indexHigh; } mapped = x[k] + (indexedLookup[j * nComps2 + k] / 255.0) * y[k]; lookup2[k][i] = dblToCol(mapped); if (useByteLookup) { byte_lookup[i * nComps2 + k] = (unsigned char)(mapped * 255); } } } break; case csSeparation: sepCS = (GfxSeparationColorSpace *)colorSpace; colorSpace2 = sepCS->getAlt(); nComps2 = colorSpace2->getNComps(); sepFunc = sepCS->getFunc(); if (colorSpace2->useGetGrayLine() || colorSpace2->useGetRGBLine() || colorSpace2->useGetCMYKLine() || colorSpace2->useGetDeviceNLine()) { byte_lookup = (unsigned char *)gmallocn((maxPixel + 1), nComps2); useByteLookup = true; } for (k = 0; k < nComps2; ++k) { lookup2[k] = (GfxColorComp *)gmallocn(maxPixel + 1, sizeof(GfxColorComp)); for (i = 0; i <= maxPixel; ++i) { x[0] = decodeLow[0] + (i * decodeRange[0]) / maxPixel; sepFunc->transform(x, y); lookup2[k][i] = dblToCol(y[k]); if (useByteLookup) { byte_lookup[i * nComps2 + k] = (unsigned char)(y[k] * 255); } } } 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); useByteLookup = true; } for (k = 0; k < nComps; ++k) { lookup2[k] = (GfxColorComp *)gmallocn(maxPixel + 1, sizeof(GfxColorComp)); for (i = 0; i <= maxPixel; ++i) { mapped = decodeLow[k] + (i * decodeRange[k]) / maxPixel; lookup2[k][i] = dblToCol(mapped); if (useByteLookup) { int byte; byte = (int)(mapped * 255.0 + 0.5); if (byte < 0) { byte = 0; } else if (byte > 255) { byte = 255; } byte_lookup[i * nComps + k] = byte; } } } } return; err1: ok = false; } GfxImageColorMap::GfxImageColorMap(const GfxImageColorMap *colorMap) { int n, i, k; colorSpace = colorMap->colorSpace->copy(); bits = colorMap->bits; nComps = colorMap->nComps; nComps2 = colorMap->nComps2; useMatte = colorMap->useMatte; matteColor = colorMap->matteColor; colorSpace2 = nullptr; for (k = 0; k < gfxColorMaxComps; ++k) { lookup[k] = nullptr; lookup2[k] = nullptr; } byte_lookup = nullptr; n = 1 << bits; for (k = 0; k < nComps; ++k) { lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp)); memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp)); } if (colorSpace->getMode() == csIndexed) { colorSpace2 = ((GfxIndexedColorSpace *)colorSpace)->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(); for (k = 0; k < nComps2; ++k) { lookup2[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp)); memcpy(lookup2[k], colorMap->lookup2[k], n * sizeof(GfxColorComp)); } } else { for (k = 0; k < nComps; ++k) { lookup2[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp)); memcpy(lookup2[k], colorMap->lookup2[k], n * sizeof(GfxColorComp)); } } if (colorMap->byte_lookup) { int nc = colorSpace2 ? nComps2 : nComps; byte_lookup = (unsigned char *)gmallocn(n, nc); memcpy(byte_lookup, colorMap->byte_lookup, n * nc); } for (i = 0; i < nComps; ++i) { decodeLow[i] = colorMap->decodeLow[i]; decodeRange[i] = colorMap->decodeRange[i]; } ok = true; } GfxImageColorMap::~GfxImageColorMap() { int i; delete colorSpace; for (i = 0; i < gfxColorMaxComps; ++i) { gfree(lookup[i]); gfree(lookup2[i]); } gfree(byte_lookup); } void GfxImageColorMap::getGray(const unsigned char *x, GfxGray *gray) { GfxColor color; int i; if (colorSpace2) { for (i = 0; i < nComps2; ++i) { color.c[i] = lookup2[i][x[0]]; } colorSpace2->getGray(&color, gray); } else { for (i = 0; i < nComps; ++i) { color.c[i] = lookup2[i][x[i]]; } colorSpace->getGray(&color, gray); } } void GfxImageColorMap::getRGB(const unsigned char *x, GfxRGB *rgb) { GfxColor color; int i; if (colorSpace2) { for (i = 0; i < nComps2; ++i) { color.c[i] = lookup2[i][x[0]]; } colorSpace2->getRGB(&color, rgb); } else { for (i = 0; i < nComps; ++i) { color.c[i] = lookup2[i][x[i]]; } colorSpace->getRGB(&color, rgb); } } void GfxImageColorMap::getGrayLine(unsigned char *in, unsigned char *out, int length) { int i, j; unsigned char *inp, *tmp_line; if ((colorSpace2 && !colorSpace2->useGetGrayLine()) || (!colorSpace2 && !colorSpace->useGetGrayLine())) { GfxGray gray; inp = in; for (i = 0; i < length; i++) { getGray(inp, &gray); out[i] = colToByte(gray); inp += nComps; } return; } switch (colorSpace->getMode()) { case csIndexed: case csSeparation: tmp_line = (unsigned char *)gmallocn(length, nComps2); for (i = 0; i < length; i++) { for (j = 0; j < nComps2; j++) { unsigned char c = in[i]; if (byte_lookup) { c = byte_lookup[c * nComps2 + j]; } tmp_line[i * nComps2 + j] = c; } } colorSpace2->getGrayLine(tmp_line, out, length); gfree(tmp_line); break; default: if (byte_lookup) { inp = in; for (j = 0; j < length; j++) { for (i = 0; i < nComps; i++) { *inp = byte_lookup[*inp * nComps + i]; inp++; } } } colorSpace->getGrayLine(in, out, length); break; } } void GfxImageColorMap::getRGBLine(unsigned char *in, unsigned int *out, int length) { int i, j; unsigned char *inp, *tmp_line; if (!useRGBLine()) { GfxRGB rgb; inp = in; for (i = 0; i < length; i++) { getRGB(inp, &rgb); out[i] = ((int)colToByte(rgb.r) << 16) | ((int)colToByte(rgb.g) << 8) | ((int)colToByte(rgb.b) << 0); inp += nComps; } return; } switch (colorSpace->getMode()) { case csIndexed: case csSeparation: tmp_line = (unsigned char *)gmallocn(length, nComps2); for (i = 0; i < length; i++) { for (j = 0; j < nComps2; j++) { unsigned char c = in[i]; if (byte_lookup) { c = byte_lookup[c * nComps2 + j]; } tmp_line[i * nComps2 + j] = c; } } colorSpace2->getRGBLine(tmp_line, out, length); gfree(tmp_line); break; default: if (byte_lookup) { inp = in; for (j = 0; j < length; j++) { for (i = 0; i < nComps; i++) { *inp = byte_lookup[*inp * nComps + i]; inp++; } } } colorSpace->getRGBLine(in, out, length); break; } } void GfxImageColorMap::getRGBLine(unsigned char *in, unsigned char *out, int length) { int i, j; unsigned char *inp, *tmp_line; if (!useRGBLine()) { GfxRGB rgb; inp = in; for (i = 0; i < length; i++) { getRGB(inp, &rgb); *out++ = colToByte(rgb.r); *out++ = colToByte(rgb.g); *out++ = colToByte(rgb.b); inp += nComps; } return; } switch (colorSpace->getMode()) { case csIndexed: case csSeparation: tmp_line = (unsigned char *)gmallocn(length, nComps2); for (i = 0; i < length; i++) { for (j = 0; j < nComps2; j++) { unsigned char c = in[i]; if (byte_lookup) { c = byte_lookup[c * nComps2 + j]; } tmp_line[i * nComps2 + j] = c; } } colorSpace2->getRGBLine(tmp_line, out, length); gfree(tmp_line); break; default: if (byte_lookup) { inp = in; for (j = 0; j < length; j++) { for (i = 0; i < nComps; i++) { *inp = byte_lookup[*inp * nComps + i]; inp++; } } } colorSpace->getRGBLine(in, out, length); break; } } void GfxImageColorMap::getRGBXLine(unsigned char *in, unsigned char *out, int length) { int i, j; unsigned char *inp, *tmp_line; if (!useRGBLine()) { GfxRGB rgb; inp = in; for (i = 0; i < length; i++) { getRGB(inp, &rgb); *out++ = colToByte(rgb.r); *out++ = colToByte(rgb.g); *out++ = colToByte(rgb.b); *out++ = 255; inp += nComps; } return; } switch (colorSpace->getMode()) { case csIndexed: case csSeparation: tmp_line = (unsigned char *)gmallocn(length, nComps2); for (i = 0; i < length; i++) { for (j = 0; j < nComps2; j++) { unsigned char c = in[i]; if (byte_lookup) { c = byte_lookup[c * nComps2 + j]; } tmp_line[i * nComps2 + j] = c; } } colorSpace2->getRGBXLine(tmp_line, out, length); gfree(tmp_line); break; default: if (byte_lookup) { inp = in; for (j = 0; j < length; j++) { for (i = 0; i < nComps; i++) { *inp = byte_lookup[*inp * nComps + i]; inp++; } } } colorSpace->getRGBXLine(in, out, length); break; } } void GfxImageColorMap::getCMYKLine(unsigned char *in, unsigned char *out, int length) { int i, j; unsigned char *inp, *tmp_line; if (!useCMYKLine()) { GfxCMYK cmyk; inp = in; for (i = 0; i < length; i++) { getCMYK(inp, &cmyk); *out++ = colToByte(cmyk.c); *out++ = colToByte(cmyk.m); *out++ = colToByte(cmyk.y); *out++ = colToByte(cmyk.k); inp += nComps; } return; } switch (colorSpace->getMode()) { case csIndexed: case csSeparation: tmp_line = (unsigned char *)gmallocn(length, nComps2); for (i = 0; i < length; i++) { for (j = 0; j < nComps2; j++) { unsigned char c = in[i]; if (byte_lookup) { c = byte_lookup[c * nComps2 + j]; } tmp_line[i * nComps2 + j] = c; } } colorSpace2->getCMYKLine(tmp_line, out, length); gfree(tmp_line); break; default: if (byte_lookup) { inp = in; for (j = 0; j < length; j++) { for (i = 0; i < nComps; i++) { *inp = byte_lookup[*inp * nComps + i]; inp++; } } } colorSpace->getCMYKLine(in, out, length); break; } } void GfxImageColorMap::getDeviceNLine(unsigned char *in, unsigned char *out, int length) { unsigned char *inp, *tmp_line; if (!useDeviceNLine()) { GfxColor deviceN; inp = in; for (int i = 0; i < length; i++) { getDeviceN(inp, &deviceN); for (int j = 0; j < SPOT_NCOMPS + 4; j++) { *out++ = deviceN.c[j]; } inp += nComps; } return; } switch (colorSpace->getMode()) { case csIndexed: case csSeparation: tmp_line = (unsigned char *)gmallocn(length, nComps2); for (int i = 0; i < length; i++) { for (int j = 0; j < nComps2; j++) { unsigned char c = in[i]; if (byte_lookup) { c = byte_lookup[c * nComps2 + j]; } tmp_line[i * nComps2 + j] = c; } } colorSpace2->getDeviceNLine(tmp_line, out, length); gfree(tmp_line); break; default: if (byte_lookup) { inp = in; for (int j = 0; j < length; j++) { for (int i = 0; i < nComps; i++) { *inp = byte_lookup[*inp * nComps + i]; inp++; } } } colorSpace->getDeviceNLine(in, out, length); break; } } void GfxImageColorMap::getCMYK(const unsigned char *x, GfxCMYK *cmyk) { GfxColor color; int i; if (colorSpace2) { for (i = 0; i < nComps2; ++i) { color.c[i] = lookup2[i][x[0]]; } colorSpace2->getCMYK(&color, cmyk); } else { for (i = 0; i < nComps; ++i) { color.c[i] = lookup[i][x[i]]; } colorSpace->getCMYK(&color, cmyk); } } void GfxImageColorMap::getDeviceN(const unsigned char *x, GfxColor *deviceN) { GfxColor color; int i; if (colorSpace2 && (colorSpace->getMapping() == nullptr || colorSpace->getMapping()[0] == -1)) { for (i = 0; i < nComps2; ++i) { color.c[i] = lookup2[i][x[0]]; } colorSpace2->getDeviceN(&color, deviceN); } else { for (i = 0; i < nComps; ++i) { color.c[i] = lookup[i][x[i]]; } colorSpace->getDeviceN(&color, deviceN); } } void GfxImageColorMap::getColor(const unsigned char *x, GfxColor *color) { int maxPixel, i; maxPixel = (1 << bits) - 1; for (i = 0; i < nComps; ++i) { color->c[i] = dblToCol(decodeLow[i] + (x[i] * decodeRange[i]) / maxPixel); } } //------------------------------------------------------------------------ // GfxSubpath and GfxPath //------------------------------------------------------------------------ GfxSubpath::GfxSubpath(double x1, double y1) { size = 16; x = (double *)gmallocn(size, sizeof(double)); y = (double *)gmallocn(size, sizeof(double)); curve = (bool *)gmallocn(size, sizeof(bool)); n = 1; x[0] = x1; y[0] = y1; curve[0] = false; closed = false; } GfxSubpath::~GfxSubpath() { gfree(x); gfree(y); gfree(curve); } // Used for copy(). GfxSubpath::GfxSubpath(const GfxSubpath *subpath) { size = subpath->size; n = subpath->n; x = (double *)gmallocn(size, sizeof(double)); y = (double *)gmallocn(size, sizeof(double)); curve = (bool *)gmallocn(size, sizeof(bool)); memcpy(x, subpath->x, n * sizeof(double)); memcpy(y, subpath->y, n * sizeof(double)); memcpy(curve, subpath->curve, n * sizeof(bool)); closed = subpath->closed; } void GfxSubpath::lineTo(double x1, double y1) { if (n >= size) { size *= 2; x = (double *)greallocn(x, size, sizeof(double)); y = (double *)greallocn(y, size, sizeof(double)); curve = (bool *)greallocn(curve, size, sizeof(bool)); } x[n] = x1; y[n] = y1; curve[n] = false; ++n; } void GfxSubpath::curveTo(double x1, double y1, double x2, double y2, double x3, double y3) { if (n + 3 > size) { size *= 2; x = (double *)greallocn(x, size, sizeof(double)); y = (double *)greallocn(y, size, sizeof(double)); curve = (bool *)greallocn(curve, size, sizeof(bool)); } x[n] = x1; y[n] = y1; x[n + 1] = x2; y[n + 1] = y2; x[n + 2] = x3; y[n + 2] = y3; curve[n] = curve[n + 1] = true; curve[n + 2] = false; n += 3; } void GfxSubpath::close() { if (x[n - 1] != x[0] || y[n - 1] != y[0]) { lineTo(x[0], y[0]); } closed = true; } void GfxSubpath::offset(double dx, double dy) { int i; for (i = 0; i < n; ++i) { x[i] += dx; y[i] += dy; } } GfxPath::GfxPath() { justMoved = false; size = 16; n = 0; firstX = firstY = 0; subpaths = (GfxSubpath **)gmallocn(size, sizeof(GfxSubpath *)); } GfxPath::~GfxPath() { int i; for (i = 0; i < n; ++i) { delete subpaths[i]; } gfree(subpaths); } // Used for copy(). GfxPath::GfxPath(bool justMoved1, double firstX1, double firstY1, GfxSubpath **subpaths1, int n1, int size1) { int i; justMoved = justMoved1; firstX = firstX1; firstY = firstY1; size = size1; n = n1; subpaths = (GfxSubpath **)gmallocn(size, sizeof(GfxSubpath *)); for (i = 0; i < n; ++i) { subpaths[i] = subpaths1[i]->copy(); } } void GfxPath::moveTo(double x, double y) { justMoved = true; firstX = x; firstY = y; } 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 *)); } if (justMoved) { subpaths[n] = new GfxSubpath(firstX, firstY); } else { subpaths[n] = new GfxSubpath(subpaths[n - 1]->getLastX(), subpaths[n - 1]->getLastY()); } ++n; justMoved = false; } subpaths[n - 1]->lineTo(x, y); } void GfxPath::curveTo(double x1, double y1, double x2, double y2, double x3, double y3) { if (justMoved || (n > 0 && subpaths[n - 1]->isClosed())) { if (n >= size) { size *= 2; subpaths = (GfxSubpath **)greallocn(subpaths, size, sizeof(GfxSubpath *)); } if (justMoved) { subpaths[n] = new GfxSubpath(firstX, firstY); } else { subpaths[n] = new GfxSubpath(subpaths[n - 1]->getLastX(), subpaths[n - 1]->getLastY()); } ++n; justMoved = false; } subpaths[n - 1]->curveTo(x1, y1, x2, y2, x3, y3); } void GfxPath::close() { // this is necessary to handle the pathological case of // moveto/closepath/clip, which defines an empty clipping region if (justMoved) { if (n >= size) { size *= 2; subpaths = (GfxSubpath **)greallocn(subpaths, size, sizeof(GfxSubpath *)); } subpaths[n] = new GfxSubpath(firstX, firstY); ++n; justMoved = false; } subpaths[n - 1]->close(); } void GfxPath::append(GfxPath *path) { int i; if (n + path->n > size) { size = n + path->n; subpaths = (GfxSubpath **)greallocn(subpaths, size, sizeof(GfxSubpath *)); } for (i = 0; i < path->n; ++i) { subpaths[n++] = path->subpaths[i]->copy(); } justMoved = false; } void GfxPath::offset(double dx, double dy) { int i; for (i = 0; i < n; ++i) { subpaths[i]->offset(dx, dy); } } //------------------------------------------------------------------------ // //------------------------------------------------------------------------ GfxState::ReusablePathIterator::ReusablePathIterator(GfxPath *pathA) : path(pathA), subPathOff(0), coordOff(0), numCoords(0), curSubPath(nullptr) { if (path->getNumSubpaths()) { curSubPath = path->getSubpath(subPathOff); numCoords = curSubPath->getNumPoints(); } } bool GfxState::ReusablePathIterator::isEnd() const { return coordOff >= numCoords; } void GfxState::ReusablePathIterator::next() { ++coordOff; if (coordOff == numCoords) { ++subPathOff; if (subPathOff < path->getNumSubpaths()) { coordOff = 0; curSubPath = path->getSubpath(subPathOff); numCoords = curSubPath->getNumPoints(); } } } void GfxState::ReusablePathIterator::setCoord(double x, double y) { curSubPath->setX(coordOff, x); curSubPath->setY(coordOff, y); } void GfxState::ReusablePathIterator::reset() { coordOff = 0; subPathOff = 0; curSubPath = path->getSubpath(0); numCoords = curSubPath->getNumPoints(); } GfxState::GfxState(double hDPIA, double vDPIA, const PDFRectangle *pageBox, int rotateA, bool upsideDown) { double kx, ky; hDPI = hDPIA; vDPI = vDPIA; rotate = rotateA; px1 = pageBox->x1; py1 = pageBox->y1; px2 = pageBox->x2; py2 = pageBox->y2; kx = hDPI / 72.0; ky = vDPI / 72.0; if (rotate == 90) { ctm[0] = 0; ctm[1] = upsideDown ? ky : -ky; ctm[2] = kx; ctm[3] = 0; ctm[4] = -kx * py1; ctm[5] = ky * (upsideDown ? -px1 : px2); pageWidth = kx * (py2 - py1); pageHeight = ky * (px2 - px1); } else if (rotate == 180) { ctm[0] = -kx; ctm[1] = 0; ctm[2] = 0; ctm[3] = upsideDown ? ky : -ky; ctm[4] = kx * px2; ctm[5] = ky * (upsideDown ? -py1 : py2); pageWidth = kx * (px2 - px1); pageHeight = ky * (py2 - py1); } else if (rotate == 270) { ctm[0] = 0; ctm[1] = upsideDown ? -ky : ky; ctm[2] = -kx; ctm[3] = 0; ctm[4] = kx * py2; ctm[5] = ky * (upsideDown ? px2 : -px1); pageWidth = kx * (py2 - py1); pageHeight = ky * (px2 - px1); } else { ctm[0] = kx; ctm[1] = 0; ctm[2] = 0; ctm[3] = upsideDown ? -ky : ky; ctm[4] = -kx * px1; ctm[5] = ky * (upsideDown ? py2 : -py1); pageWidth = kx * (px2 - px1); pageHeight = ky * (py2 - py1); } fillColorSpace = new GfxDeviceGrayColorSpace(); strokeColorSpace = new GfxDeviceGrayColorSpace(); fillColor.c[0] = 0; strokeColor.c[0] = 0; fillPattern = nullptr; strokePattern = nullptr; blendMode = gfxBlendNormal; fillOpacity = 1; strokeOpacity = 1; fillOverprint = false; strokeOverprint = false; overprintMode = 0; transfer[0] = transfer[1] = transfer[2] = transfer[3] = nullptr; lineWidth = 1; lineDashStart = 0; flatness = 1; lineJoin = 0; lineCap = 0; miterLimit = 10; strokeAdjust = false; alphaIsShape = false; textKnockout = false; font = nullptr; fontSize = 0; textMat[0] = 1; textMat[1] = 0; textMat[2] = 0; textMat[3] = 1; textMat[4] = 0; textMat[5] = 0; charSpace = 0; wordSpace = 0; horizScaling = 1; leading = 0; rise = 0; render = 0; path = new GfxPath(); curX = curY = 0; lineX = lineY = 0; clipXMin = 0; clipYMin = 0; clipXMax = pageWidth; clipYMax = pageHeight; renderingIntent[0] = 0; saved = nullptr; defaultGrayColorSpace = nullptr; defaultRGBColorSpace = nullptr; defaultCMYKColorSpace = nullptr; #ifdef USE_CMS XYZ2DisplayTransformRelCol = nullptr; XYZ2DisplayTransformAbsCol = nullptr; XYZ2DisplayTransformSat = nullptr; XYZ2DisplayTransformPerc = nullptr; localDisplayProfile = nullptr; if (!sRGBProfile) { // This is probably the one of the first invocations of lcms2, so we set the error handler cmsSetLogErrorHandler(CMSError); sRGBProfile = make_GfxLCMSProfilePtr(cmsCreate_sRGBProfile()); } if (!XYZProfile) { XYZProfile = make_GfxLCMSProfilePtr(cmsCreateXYZProfile()); } #endif } 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; } // Used for copy(); GfxState::GfxState(const GfxState *state, bool copyPath) { int i; hDPI = state->hDPI; vDPI = state->vDPI; memcpy(ctm, state->ctm, sizeof(ctm)); px1 = state->px1; py1 = state->py1; px2 = state->px2; py2 = state->py2; pageWidth = state->pageWidth; pageHeight = state->pageHeight; rotate = state->rotate; fillColorSpace = state->fillColorSpace; if (fillColorSpace) { fillColorSpace = state->fillColorSpace->copy(); } strokeColorSpace = state->strokeColorSpace; if (strokeColorSpace) { strokeColorSpace = state->strokeColorSpace->copy(); } fillColor = state->fillColor; strokeColor = state->strokeColor; fillPattern = state->fillPattern; if (fillPattern) { fillPattern = state->fillPattern->copy(); } strokePattern = state->strokePattern; if (strokePattern) { strokePattern = state->strokePattern->copy(); } blendMode = state->blendMode; fillOpacity = state->fillOpacity; strokeOpacity = state->strokeOpacity; fillOverprint = state->fillOverprint; strokeOverprint = state->strokeOverprint; overprintMode = state->overprintMode; for (i = 0; i < 4; ++i) { transfer[i] = state->transfer[i]; if (transfer[i]) { transfer[i] = state->transfer[i]->copy(); } } lineWidth = state->lineWidth; lineDash = state->lineDash; lineDashStart = state->lineDashStart; flatness = state->flatness; lineJoin = state->lineJoin; lineCap = state->lineCap; miterLimit = state->miterLimit; strokeAdjust = state->strokeAdjust; alphaIsShape = state->alphaIsShape; textKnockout = state->textKnockout; font = state->font; fontSize = state->fontSize; memcpy(textMat, state->textMat, sizeof(textMat)); charSpace = state->charSpace; wordSpace = state->wordSpace; horizScaling = state->horizScaling; leading = state->leading; rise = state->rise; render = state->render; path = state->path; if (copyPath) { path = state->path->copy(); } curX = state->curX; curY = state->curY; lineX = state->lineX; lineY = state->lineY; clipXMin = state->clipXMin; clipYMin = state->clipYMin; clipXMax = state->clipXMax; clipYMax = state->clipYMax; memcpy(renderingIntent, state->renderingIntent, sizeof(renderingIntent)); saved = nullptr; #ifdef USE_CMS localDisplayProfile = state->localDisplayProfile; XYZ2DisplayTransformRelCol = state->XYZ2DisplayTransformRelCol; XYZ2DisplayTransformAbsCol = state->XYZ2DisplayTransformAbsCol; XYZ2DisplayTransformSat = state->XYZ2DisplayTransformSat; XYZ2DisplayTransformPerc = state->XYZ2DisplayTransformPerc; #endif if (state->defaultGrayColorSpace) { defaultGrayColorSpace = state->defaultGrayColorSpace->copy(); } else { defaultGrayColorSpace = nullptr; } if (state->defaultRGBColorSpace) { defaultRGBColorSpace = state->defaultRGBColorSpace->copy(); } else { defaultRGBColorSpace = nullptr; } if (state->defaultCMYKColorSpace) { defaultCMYKColorSpace = state->defaultCMYKColorSpace->copy(); } else { defaultCMYKColorSpace = nullptr; } } #ifdef USE_CMS GfxLCMSProfilePtr GfxState::sRGBProfile = nullptr; GfxLCMSProfilePtr GfxState::XYZProfile = nullptr; void GfxState::setDisplayProfile(const GfxLCMSProfilePtr &localDisplayProfileA) { localDisplayProfile = localDisplayProfileA; if (localDisplayProfile) { cmsHTRANSFORM transform; unsigned int nChannels; unsigned int localDisplayPixelType; localDisplayPixelType = getCMSColorSpaceType(cmsGetColorSpace(localDisplayProfile.get())); nChannels = getCMSNChannels(cmsGetColorSpace(localDisplayProfile.get())); // create transform from XYZ if ((transform = cmsCreateTransform(XYZProfile.get(), TYPE_XYZ_DBL, localDisplayProfile.get(), COLORSPACE_SH(localDisplayPixelType) | CHANNELS_SH(nChannels) | BYTES_SH(1), INTENT_RELATIVE_COLORIMETRIC, LCMS_FLAGS)) == nullptr) { error(errSyntaxWarning, -1, "Can't create Lab transform"); } else { XYZ2DisplayTransformRelCol = std::make_shared(transform, INTENT_RELATIVE_COLORIMETRIC, PT_XYZ, localDisplayPixelType); } if ((transform = cmsCreateTransform(XYZProfile.get(), TYPE_XYZ_DBL, localDisplayProfile.get(), COLORSPACE_SH(localDisplayPixelType) | CHANNELS_SH(nChannels) | BYTES_SH(1), INTENT_ABSOLUTE_COLORIMETRIC, LCMS_FLAGS)) == nullptr) { error(errSyntaxWarning, -1, "Can't create Lab transform"); } else { XYZ2DisplayTransformAbsCol = std::make_shared(transform, INTENT_ABSOLUTE_COLORIMETRIC, PT_XYZ, localDisplayPixelType); } if ((transform = cmsCreateTransform(XYZProfile.get(), TYPE_XYZ_DBL, localDisplayProfile.get(), COLORSPACE_SH(localDisplayPixelType) | CHANNELS_SH(nChannels) | BYTES_SH(1), INTENT_SATURATION, LCMS_FLAGS)) == nullptr) { error(errSyntaxWarning, -1, "Can't create Lab transform"); } else { XYZ2DisplayTransformSat = std::make_shared(transform, INTENT_SATURATION, PT_XYZ, localDisplayPixelType); } if ((transform = cmsCreateTransform(XYZProfile.get(), TYPE_XYZ_DBL, localDisplayProfile.get(), COLORSPACE_SH(localDisplayPixelType) | CHANNELS_SH(nChannels) | BYTES_SH(1), INTENT_PERCEPTUAL, LCMS_FLAGS)) == nullptr) { error(errSyntaxWarning, -1, "Can't create Lab transform"); } else { XYZ2DisplayTransformPerc = std::make_shared(transform, INTENT_PERCEPTUAL, PT_XYZ, localDisplayPixelType); } } } std::shared_ptr GfxState::getXYZ2DisplayTransform() { auto transform = XYZ2DisplayTransformRelCol; if (strcmp(renderingIntent, "AbsoluteColorimetric") == 0) { transform = XYZ2DisplayTransformAbsCol; } else if (strcmp(renderingIntent, "Saturation") == 0) { transform = XYZ2DisplayTransformSat; } else if (strcmp(renderingIntent, "Perceptual") == 0) { transform = XYZ2DisplayTransformPerc; } return transform; } int GfxState::getCmsRenderingIntent() { const char *intent = getRenderingIntent(); int cmsIntent = INTENT_RELATIVE_COLORIMETRIC; if (intent) { if (strcmp(intent, "AbsoluteColorimetric") == 0) { cmsIntent = INTENT_ABSOLUTE_COLORIMETRIC; } else if (strcmp(intent, "Saturation") == 0) { cmsIntent = INTENT_SATURATION; } else if (strcmp(intent, "Perceptual") == 0) { cmsIntent = INTENT_PERCEPTUAL; } } return cmsIntent; } #endif void GfxState::setPath(GfxPath *pathA) { delete path; path = pathA; } void GfxState::getUserClipBBox(double *xMin, double *yMin, double *xMax, double *yMax) const { double ictm[6]; double xMin1, yMin1, xMax1, yMax1, tx, ty; // invert the CTM const double det_denominator = (ctm[0] * ctm[3] - ctm[1] * ctm[2]); if (unlikely(det_denominator == 0)) { *xMin = 0; *yMin = 0; *xMax = 0; *yMax = 0; return; } const double det = 1 / det_denominator; ictm[0] = ctm[3] * det; ictm[1] = -ctm[1] * det; ictm[2] = -ctm[2] * det; ictm[3] = ctm[0] * det; ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det; ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det; // transform all four corners of the clip bbox; find the min and max // x and y values xMin1 = xMax1 = clipXMin * ictm[0] + clipYMin * ictm[2] + ictm[4]; yMin1 = yMax1 = clipXMin * ictm[1] + clipYMin * ictm[3] + ictm[5]; tx = clipXMin * ictm[0] + clipYMax * ictm[2] + ictm[4]; ty = clipXMin * ictm[1] + clipYMax * ictm[3] + ictm[5]; if (tx < xMin1) { xMin1 = tx; } else if (tx > xMax1) { xMax1 = tx; } if (ty < yMin1) { yMin1 = ty; } else if (ty > yMax1) { yMax1 = ty; } tx = clipXMax * ictm[0] + clipYMin * ictm[2] + ictm[4]; ty = clipXMax * ictm[1] + clipYMin * ictm[3] + ictm[5]; if (tx < xMin1) { xMin1 = tx; } else if (tx > xMax1) { xMax1 = tx; } if (ty < yMin1) { yMin1 = ty; } else if (ty > yMax1) { yMax1 = ty; } tx = clipXMax * ictm[0] + clipYMax * ictm[2] + ictm[4]; ty = clipXMax * ictm[1] + clipYMax * ictm[3] + ictm[5]; if (tx < xMin1) { xMin1 = tx; } else if (tx > xMax1) { xMax1 = tx; } if (ty < yMin1) { yMin1 = ty; } else if (ty > yMax1) { yMax1 = ty; } *xMin = xMin1; *yMin = yMin1; *xMax = xMax1; *yMax = yMax1; } double GfxState::transformWidth(double w) const { double x, y; x = ctm[0] + ctm[2]; y = ctm[1] + ctm[3]; return w * sqrt(0.5 * (x * x + y * y)); } double GfxState::getTransformedFontSize() const { double x1, y1, x2, y2; x1 = textMat[2] * fontSize; y1 = textMat[3] * fontSize; x2 = ctm[0] * x1 + ctm[2] * y1; y2 = ctm[1] * x1 + ctm[3] * y1; return sqrt(x2 * x2 + y2 * y2); } void GfxState::getFontTransMat(double *m11, double *m12, double *m21, double *m22) const { *m11 = (textMat[0] * ctm[0] + textMat[1] * ctm[2]) * fontSize; *m12 = (textMat[0] * ctm[1] + textMat[1] * ctm[3]) * fontSize; *m21 = (textMat[2] * ctm[0] + textMat[3] * ctm[2]) * fontSize; *m22 = (textMat[2] * ctm[1] + textMat[3] * ctm[3]) * fontSize; } void GfxState::setCTM(double a, double b, double c, double d, double e, double f) { ctm[0] = a; ctm[1] = b; ctm[2] = c; ctm[3] = d; ctm[4] = e; ctm[5] = f; } void GfxState::concatCTM(double a, double b, double c, double d, double e, double f) { double a1 = ctm[0]; double b1 = ctm[1]; double c1 = ctm[2]; double d1 = ctm[3]; ctm[0] = a * a1 + b * c1; ctm[1] = a * b1 + b * d1; ctm[2] = c * a1 + d * c1; ctm[3] = c * b1 + d * d1; ctm[4] = e * a1 + f * c1 + ctm[4]; ctm[5] = e * b1 + f * d1 + ctm[5]; } void GfxState::shiftCTMAndClip(double tx, double ty) { ctm[4] += tx; ctm[5] += ty; clipXMin += tx; clipYMin += ty; clipXMax += tx; clipYMax += ty; } void GfxState::setFillColorSpace(GfxColorSpace *colorSpace) { if (fillColorSpace) { delete fillColorSpace; } fillColorSpace = colorSpace; } void GfxState::setStrokeColorSpace(GfxColorSpace *colorSpace) { if (strokeColorSpace) { delete strokeColorSpace; } strokeColorSpace = colorSpace; } void GfxState::setFillPattern(GfxPattern *pattern) { if (fillPattern) { delete fillPattern; } fillPattern = pattern; } void GfxState::setStrokePattern(GfxPattern *pattern) { if (strokePattern) { delete strokePattern; } strokePattern = pattern; } void GfxState::setFont(std::shared_ptr fontA, double fontSizeA) { font = std::move(fontA); fontSize = fontSizeA; } void GfxState::setTransfer(Function **funcs) { int i; for (i = 0; i < 4; ++i) { if (transfer[i]) { delete transfer[i]; } transfer[i] = funcs[i]; } } void GfxState::setLineDash(std::vector &&dash, double start) { lineDash = dash; lineDashStart = start; } void GfxState::clearPath() { delete path; path = new GfxPath(); } void GfxState::clip() { double xMin, yMin, xMax, yMax, x, y; GfxSubpath *subpath; int i, j; xMin = xMax = yMin = yMax = 0; // make gcc happy for (i = 0; i < path->getNumSubpaths(); ++i) { subpath = path->getSubpath(i); for (j = 0; j < subpath->getNumPoints(); ++j) { transform(subpath->getX(j), subpath->getY(j), &x, &y); if (i == 0 && j == 0) { xMin = xMax = x; yMin = yMax = y; } else { if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } } } } if (xMin > clipXMin) { clipXMin = xMin; } if (yMin > clipYMin) { clipYMin = yMin; } if (xMax < clipXMax) { clipXMax = xMax; } if (yMax < clipYMax) { clipYMax = yMax; } } void GfxState::clipToStrokePath() { double xMin, yMin, xMax, yMax, x, y, t0, t1; GfxSubpath *subpath; int i, j; xMin = xMax = yMin = yMax = 0; // make gcc happy for (i = 0; i < path->getNumSubpaths(); ++i) { subpath = path->getSubpath(i); for (j = 0; j < subpath->getNumPoints(); ++j) { transform(subpath->getX(j), subpath->getY(j), &x, &y); if (i == 0 && j == 0) { xMin = xMax = x; yMin = yMax = y; } else { if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } } } } // allow for the line width //~ miter joins can extend farther than this t0 = fabs(ctm[0]); t1 = fabs(ctm[2]); if (t0 > t1) { xMin -= 0.5 * lineWidth * t0; xMax += 0.5 * lineWidth * t0; } else { xMin -= 0.5 * lineWidth * t1; xMax += 0.5 * lineWidth * t1; } t0 = fabs(ctm[0]); t1 = fabs(ctm[3]); if (t0 > t1) { yMin -= 0.5 * lineWidth * t0; yMax += 0.5 * lineWidth * t0; } else { yMin -= 0.5 * lineWidth * t1; yMax += 0.5 * lineWidth * t1; } if (xMin > clipXMin) { clipXMin = xMin; } if (yMin > clipYMin) { clipYMin = yMin; } if (xMax < clipXMax) { clipXMax = xMax; } if (yMax < clipYMax) { clipYMax = yMax; } } void GfxState::clipToRect(double xMin, double yMin, double xMax, double yMax) { double x, y, xMin1, yMin1, xMax1, yMax1; transform(xMin, yMin, &x, &y); xMin1 = xMax1 = x; yMin1 = yMax1 = y; transform(xMax, yMin, &x, &y); if (x < xMin1) { xMin1 = x; } else if (x > xMax1) { xMax1 = x; } if (y < yMin1) { yMin1 = y; } else if (y > yMax1) { yMax1 = y; } transform(xMax, yMax, &x, &y); if (x < xMin1) { xMin1 = x; } else if (x > xMax1) { xMax1 = x; } if (y < yMin1) { yMin1 = y; } else if (y > yMax1) { yMax1 = y; } transform(xMin, yMax, &x, &y); if (x < xMin1) { xMin1 = x; } else if (x > xMax1) { xMax1 = x; } if (y < yMin1) { yMin1 = y; } else if (y > yMax1) { yMax1 = y; } if (xMin1 > clipXMin) { clipXMin = xMin1; } if (yMin1 > clipYMin) { clipYMin = yMin1; } if (xMax1 < clipXMax) { clipXMax = xMax1; } if (yMax1 < clipYMax) { clipYMax = yMax1; } } void GfxState::textShift(double tx, double ty) { double dx, dy; textTransformDelta(tx, ty, &dx, &dy); curX += dx; curY += dy; } void GfxState::shift(double dx, double dy) { curX += dx; curY += dy; } GfxState *GfxState::save() { GfxState *newState; newState = copy(); newState->saved = this; return newState; } GfxState *GfxState::restore() { GfxState *oldState; if (saved) { oldState = saved; // these attributes aren't saved/restored by the q/Q operators oldState->path = path; oldState->curX = curX; oldState->curY = curY; oldState->lineX = lineX; oldState->lineY = lineY; path = nullptr; saved = nullptr; delete this; } else { oldState = this; } return oldState; } bool GfxState::parseBlendMode(Object *obj, GfxBlendMode *mode) { int i, j; if (obj->isName()) { for (i = 0; i < nGfxBlendModeNames; ++i) { if (!strcmp(obj->getName(), gfxBlendModeNames[i].name)) { *mode = gfxBlendModeNames[i].mode; return true; } } return false; } else if (obj->isArray()) { for (i = 0; i < obj->arrayGetLength(); ++i) { Object obj2 = obj->arrayGet(i); if (!obj2.isName()) { return false; } for (j = 0; j < nGfxBlendModeNames; ++j) { if (!strcmp(obj2.getName(), gfxBlendModeNames[j].name)) { *mode = gfxBlendModeNames[j].mode; return true; } } } *mode = gfxBlendNormal; return true; } else { return false; } } poppler-24.02.0/poppler/GfxState.h000066400000000000000000001776401455701731300167440ustar00rootroot00000000000000//======================================================================== // // GfxState.h // // 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) 2005 Kristian Høgsberg // 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) 2010 Christian Feuersänger // Copyright (C) 2011 Andrea Canciani // Copyright (C) 2011-2014, 2016, 2020 Thomas Freitag // Copyright (C) 2013 Lu Wang // Copyright (C) 2015, 2017, 2020, 2022 Adrian Johnson // Copyright (C) 2017, 2019, 2022 Oliver Sander // Copyright (C) 2018 Adam Reichold // Copyright (C) 2020, 2021 Philipp Knechtges // // 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 GFXSTATE_H #define GFXSTATE_H #include "poppler-config.h" #include "poppler_private_export.h" #include "Object.h" #include "Function.h" #include #include #include #include class Array; class Gfx; class GfxFont; class PDFRectangle; class GfxShading; class OutputDev; class GfxState; class GfxResources; class GfxSeparationColorSpace; class Matrix { public: double m[6]; void init(double xx, double yx, double xy, double yy, double x0, double y0) { m[0] = xx; m[1] = yx; m[2] = xy; m[3] = yy; m[4] = x0; m[5] = y0; } bool invertTo(Matrix *other) const; void translate(double tx, double ty); void scale(double sx, double sy); void transform(double x, double y, double *tx, double *ty) const; double determinant() const { return m[0] * m[3] - m[1] * m[2]; } double norm() const; }; //------------------------------------------------------------------------ // GfxBlendMode //------------------------------------------------------------------------ enum GfxBlendMode { gfxBlendNormal, gfxBlendMultiply, gfxBlendScreen, gfxBlendOverlay, gfxBlendDarken, gfxBlendLighten, gfxBlendColorDodge, gfxBlendColorBurn, gfxBlendHardLight, gfxBlendSoftLight, gfxBlendDifference, gfxBlendExclusion, gfxBlendHue, gfxBlendSaturation, gfxBlendColor, gfxBlendLuminosity }; //------------------------------------------------------------------------ // GfxColorComp //------------------------------------------------------------------------ // 16.16 fixed point color component typedef int GfxColorComp; #define gfxColorComp1 0x10000 static inline GfxColorComp dblToCol(double x) { return (GfxColorComp)(x * gfxColorComp1); } static inline double colToDbl(GfxColorComp x) { return (double)x / (double)gfxColorComp1; } static inline unsigned char dblToByte(double x) { return static_cast(x * 255.0); } static inline double byteToDbl(unsigned char x) { return (double)x / (double)255.0; } static inline GfxColorComp byteToCol(unsigned char x) { // (x / 255) << 16 = (0.0000000100000001... * x) << 16 // = ((x << 8) + (x) + (x >> 8) + ...) << 16 // = (x << 8) + (x) + (x >> 7) // [for rounding] return (GfxColorComp)((x << 8) + x + (x >> 7)); } static inline unsigned char colToByte(GfxColorComp x) { // 255 * x + 0.5 = 256 * x - x + 0x8000 return (unsigned char)(((x << 8) - x + 0x8000) >> 16); } static inline unsigned short colToShort(GfxColorComp x) { return (unsigned short)(x); } //------------------------------------------------------------------------ // GfxColor //------------------------------------------------------------------------ #define gfxColorMaxComps funcMaxOutputs struct GfxColor { GfxColorComp c[gfxColorMaxComps]; }; static inline void clearGfxColor(GfxColor *gfxColor) { memset(gfxColor->c, 0, sizeof(GfxColorComp) * gfxColorMaxComps); } //------------------------------------------------------------------------ // GfxGray //------------------------------------------------------------------------ typedef GfxColorComp GfxGray; //------------------------------------------------------------------------ // GfxRGB //------------------------------------------------------------------------ struct GfxRGB { GfxColorComp r, g, b; bool operator==(GfxRGB other) const { return r == other.r && g == other.g && b == other.b; } }; //------------------------------------------------------------------------ // GfxCMYK //------------------------------------------------------------------------ struct GfxCMYK { GfxColorComp c, m, y, k; }; //------------------------------------------------------------------------ // GfxColorSpace //------------------------------------------------------------------------ // NB: The nGfxColorSpaceModes constant and the gfxColorSpaceModeNames // array defined in GfxState.cc must match this enum. enum GfxColorSpaceMode { csDeviceGray, csCalGray, csDeviceRGB, csCalRGB, csDeviceCMYK, csLab, csICCBased, csIndexed, csSeparation, csDeviceN, csPattern }; // This shall hold a cmsHPROFILE handle. // Only use the make_GfxLCMSProfilePtr function to construct this pointer, // to ensure that the resources are properly released after usage. typedef std::shared_ptr GfxLCMSProfilePtr; #ifdef USE_CMS GfxLCMSProfilePtr POPPLER_PRIVATE_EXPORT make_GfxLCMSProfilePtr(void *profile); #endif // wrapper of cmsHTRANSFORM to copy class GfxColorTransform { public: void doTransform(void *in, void *out, unsigned int size); // transformA should be a cmsHTRANSFORM GfxColorTransform(void *transformA, int cmsIntent, unsigned int inputPixelType, unsigned int transformPixelType); ~GfxColorTransform(); GfxColorTransform(const GfxColorTransform &) = delete; GfxColorTransform &operator=(const GfxColorTransform &) = delete; int getIntent() const { return cmsIntent; } int getInputPixelType() const { return inputPixelType; } int getTransformPixelType() const { return transformPixelType; } private: GfxColorTransform() { } void *transform; int cmsIntent; unsigned int inputPixelType; unsigned int transformPixelType; }; class POPPLER_PRIVATE_EXPORT GfxColorSpace { public: GfxColorSpace(); virtual ~GfxColorSpace(); GfxColorSpace(const GfxColorSpace &) = delete; GfxColorSpace &operator=(const GfxColorSpace &other) = delete; virtual GfxColorSpace *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); // Convert to gray, RGB, or CMYK. virtual void getGray(const GfxColor *color, GfxGray *gray) const = 0; virtual void getRGB(const GfxColor *color, GfxRGB *rgb) const = 0; virtual void getCMYK(const GfxColor *color, GfxCMYK *cmyk) const = 0; virtual void getDeviceN(const GfxColor *color, GfxColor *deviceN) const = 0; virtual void getGrayLine(unsigned char * /*in*/, unsigned char * /*out*/, int /*length*/) { error(errInternal, -1, "GfxColorSpace::getGrayLine this should not happen"); } virtual void getRGBLine(unsigned char * /*in*/, unsigned int * /*out*/, int /*length*/) { error(errInternal, -1, "GfxColorSpace::getRGBLine (first variant) this should not happen"); } virtual void getRGBLine(unsigned char * /*in*/, unsigned char * /*out*/, int /*length*/) { error(errInternal, -1, "GfxColorSpace::getRGBLine (second variant) this should not happen"); } virtual void getRGBXLine(unsigned char * /*in*/, unsigned char * /*out*/, int /*length*/) { error(errInternal, -1, "GfxColorSpace::getRGBXLine this should not happen"); } virtual void getCMYKLine(unsigned char * /*in*/, unsigned char * /*out*/, int /*length*/) { error(errInternal, -1, "GfxColorSpace::getCMYKLine this should not happen"); } 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); int *getMapping() const { return mapping; } // Does this ColorSpace support getRGBLine? virtual bool useGetRGBLine() const { return false; } // Does this ColorSpace support getGrayLine? virtual bool useGetGrayLine() const { return false; } // Does this ColorSpace support getCMYKLine? virtual bool useGetCMYKLine() const { return false; } // Does this ColorSpace support getDeviceNLine? virtual bool useGetDeviceNLine() const { return false; } // Return the number of color components. virtual int getNComps() const = 0; // Get this color space's default color. virtual void getDefaultColor(GfxColor *color) const = 0; // Return the default ranges for each component, assuming an image // with a max pixel value of . virtual void getDefaultRanges(double *decodeLow, double *decodeRange, int maxImgPixel) const; // Returns true if painting operations in this color space never // mark the page (e.g., the "None" colorant). virtual bool isNonMarking() const { return false; } // Return the color space's overprint mask. unsigned int getOverprintMask() const { return overprintMask; } // Return the number of color space modes static int getNumColorSpaceModes(); // Return the name of the th color space mode. static const char *getColorSpaceModeName(int idx); protected: unsigned int overprintMask; int *mapping; }; //------------------------------------------------------------------------ // GfxDeviceGrayColorSpace //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT GfxDeviceGrayColorSpace : public GfxColorSpace { public: GfxDeviceGrayColorSpace(); ~GfxDeviceGrayColorSpace() override; GfxColorSpace *copy() const override; GfxColorSpaceMode getMode() const override { return csDeviceGray; } 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 getGrayLine(unsigned char *in, unsigned char *out, int length) override; void getRGBLine(unsigned char *in, unsigned int *out, int length) override; void getRGBLine(unsigned char *in, unsigned char *out, int length) override; void getRGBXLine(unsigned char *in, unsigned char *out, int length) override; void getCMYKLine(unsigned char *in, unsigned char *out, int length) override; void getDeviceNLine(unsigned char *in, unsigned char *out, int length) override; bool useGetRGBLine() const override { return true; } bool useGetGrayLine() const override { return true; } bool useGetCMYKLine() const override { return true; } bool useGetDeviceNLine() const override { return true; } int getNComps() const override { return 1; } void getDefaultColor(GfxColor *color) const override; private: }; //------------------------------------------------------------------------ // GfxCalGrayColorSpace //------------------------------------------------------------------------ class GfxCalGrayColorSpace : public GfxColorSpace { public: GfxCalGrayColorSpace(); ~GfxCalGrayColorSpace() override; GfxColorSpace *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); 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; int getNComps() const override { return 1; } void getDefaultColor(GfxColor *color) const override; // CalGray-specific access. double getWhiteX() const { return whiteX; } double getWhiteY() const { return whiteY; } double getWhiteZ() const { return whiteZ; } double getBlackX() const { return blackX; } double getBlackY() const { return blackY; } double getBlackZ() const { return blackZ; } double getGamma() const { return gamma; } private: double whiteX, whiteY, whiteZ; // white point double blackX, blackY, blackZ; // black point double gamma; // gamma value void getXYZ(const GfxColor *color, double *pX, double *pY, double *pZ) const; #ifdef USE_CMS std::shared_ptr transform; #endif }; //------------------------------------------------------------------------ // GfxDeviceRGBColorSpace //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT GfxDeviceRGBColorSpace : public GfxColorSpace { public: GfxDeviceRGBColorSpace(); ~GfxDeviceRGBColorSpace() override; GfxColorSpace *copy() const override; GfxColorSpaceMode getMode() const override { return csDeviceRGB; } 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 getGrayLine(unsigned char *in, unsigned char *out, int length) override; void getRGBLine(unsigned char *in, unsigned int *out, int length) override; void getRGBLine(unsigned char *in, unsigned char *out, int length) override; void getRGBXLine(unsigned char *in, unsigned char *out, int length) override; void getCMYKLine(unsigned char *in, unsigned char *out, int length) override; void getDeviceNLine(unsigned char *in, unsigned char *out, int length) override; bool useGetRGBLine() const override { return true; } bool useGetGrayLine() const override { return true; } bool useGetCMYKLine() const override { return true; } bool useGetDeviceNLine() const override { return true; } int getNComps() const override { return 3; } void getDefaultColor(GfxColor *color) const override; private: }; //------------------------------------------------------------------------ // GfxCalRGBColorSpace //------------------------------------------------------------------------ class GfxCalRGBColorSpace : public GfxColorSpace { public: GfxCalRGBColorSpace(); ~GfxCalRGBColorSpace() override; GfxColorSpace *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); 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; int getNComps() const override { return 3; } void getDefaultColor(GfxColor *color) const override; // CalRGB-specific access. double getWhiteX() const { return whiteX; } double getWhiteY() const { return whiteY; } double getWhiteZ() const { return whiteZ; } double getBlackX() const { return blackX; } double getBlackY() const { return blackY; } double getBlackZ() const { return blackZ; } double getGammaR() const { return gammaR; } double getGammaG() const { return gammaG; } double getGammaB() const { return gammaB; } const double *getMatrix() const { return mat; } private: double whiteX, whiteY, whiteZ; // white point double blackX, blackY, blackZ; // black point double gammaR, gammaG, gammaB; // gamma values double mat[9]; // ABC -> XYZ transform matrix void getXYZ(const GfxColor *color, double *pX, double *pY, double *pZ) const; #ifdef USE_CMS std::shared_ptr transform; #endif }; //------------------------------------------------------------------------ // GfxDeviceCMYKColorSpace //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT GfxDeviceCMYKColorSpace : public GfxColorSpace { public: GfxDeviceCMYKColorSpace(); ~GfxDeviceCMYKColorSpace() override; GfxColorSpace *copy() const override; GfxColorSpaceMode getMode() const override { return csDeviceCMYK; } 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 getRGBLine(unsigned char *in, unsigned int *out, int length) override; void getRGBLine(unsigned char *, unsigned char *out, int length) override; void getRGBXLine(unsigned char *in, unsigned char *out, int length) override; void getCMYKLine(unsigned char *in, unsigned char *out, int length) override; void getDeviceNLine(unsigned char *in, unsigned char *out, int length) override; bool useGetRGBLine() const override { return true; } bool useGetCMYKLine() const override { return true; } bool useGetDeviceNLine() const override { return true; } int getNComps() const override { return 4; } void getDefaultColor(GfxColor *color) const override; private: }; //------------------------------------------------------------------------ // GfxLabColorSpace //------------------------------------------------------------------------ class GfxLabColorSpace : public GfxColorSpace { public: GfxLabColorSpace(); ~GfxLabColorSpace() override; GfxColorSpace *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); 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; int getNComps() const override { return 3; } void getDefaultColor(GfxColor *color) const override; void getDefaultRanges(double *decodeLow, double *decodeRange, int maxImgPixel) const override; // Lab-specific access. double getWhiteX() const { return whiteX; } double getWhiteY() const { return whiteY; } double getWhiteZ() const { return whiteZ; } double getBlackX() const { return blackX; } double getBlackY() const { return blackY; } double getBlackZ() const { return blackZ; } double getAMin() const { return aMin; } double getAMax() const { return aMax; } double getBMin() const { return bMin; } double getBMax() const { return bMax; } private: double whiteX, whiteY, whiteZ; // white point double blackX, blackY, blackZ; // black point double aMin, aMax, bMin, bMax; // range for the a and b components void getXYZ(const GfxColor *color, double *pX, double *pY, double *pZ) const; #ifdef USE_CMS std::shared_ptr transform; #endif }; //------------------------------------------------------------------------ // GfxICCBasedColorSpace //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT GfxICCBasedColorSpace : public GfxColorSpace { public: GfxICCBasedColorSpace(int nCompsA, GfxColorSpace *altA, const Ref *iccProfileStreamA); ~GfxICCBasedColorSpace() override; GfxColorSpace *copy() const override; GfxColorSpaceMode getMode() const override { return csICCBased; } // Construct an ICCBased color space. Returns nullptr if unsuccessful. static GfxColorSpace *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; void getCMYK(const GfxColor *color, GfxCMYK *cmyk) const override; void getDeviceN(const GfxColor *color, GfxColor *deviceN) const override; void getRGBLine(unsigned char *in, unsigned int *out, int length) override; void getRGBLine(unsigned char *in, unsigned char *out, int length) override; void getRGBXLine(unsigned char *in, unsigned char *out, int length) override; void getCMYKLine(unsigned char *in, unsigned char *out, int length) override; void getDeviceNLine(unsigned char *in, unsigned char *out, int length) override; bool useGetRGBLine() const override; bool useGetCMYKLine() const override; bool useGetDeviceNLine() const override; int getNComps() const override { return nComps; } void getDefaultColor(GfxColor *color) const override; void getDefaultRanges(double *decodeLow, double *decodeRange, int maxImgPixel) const override; // ICCBased-specific access. GfxColorSpace *getAlt() { return alt; } Ref getRef() { return iccProfileStream; } #ifdef USE_CMS char *getPostScriptCSA(); void buildTransforms(GfxState *state); 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 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; } std::shared_ptr transform; std::shared_ptr lineTransform; // color transform for line mutable std::map cmsCache; #endif }; //------------------------------------------------------------------------ // GfxIndexedColorSpace //------------------------------------------------------------------------ class GfxIndexedColorSpace : public GfxColorSpace { public: GfxIndexedColorSpace(GfxColorSpace *baseA, int indexHighA); ~GfxIndexedColorSpace() override; GfxColorSpace *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); 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 getRGBLine(unsigned char *in, unsigned int *out, int length) override; void getRGBLine(unsigned char *in, unsigned char *out, int length) override; void getRGBXLine(unsigned char *in, unsigned char *out, int length) override; void getCMYKLine(unsigned char *in, unsigned char *out, int length) override; void getDeviceNLine(unsigned char *in, unsigned char *out, int length) override; bool useGetRGBLine() const override { return true; } bool useGetCMYKLine() const override { return true; } bool useGetDeviceNLine() const override { return true; } int getNComps() const override { return 1; } void getDefaultColor(GfxColor *color) const override; void getDefaultRanges(double *decodeLow, double *decodeRange, int maxImgPixel) const override; // Indexed-specific access. GfxColorSpace *getBase() { return base; } 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); } private: GfxColorSpace *base; // base color space int indexHigh; // max pixel value unsigned char *lookup; // lookup table }; //------------------------------------------------------------------------ // GfxSeparationColorSpace //------------------------------------------------------------------------ class GfxSeparationColorSpace : public GfxColorSpace { public: GfxSeparationColorSpace(GooString *nameA, GfxColorSpace *altA, Function *funcA); ~GfxSeparationColorSpace() override; GfxColorSpace *copy() const override; GfxColorSpaceMode getMode() const override { return csSeparation; } // Construct a Separation color space. Returns nullptr if unsuccessful. static GfxColorSpace *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; int getNComps() const override { return 1; } void getDefaultColor(GfxColor *color) const override; bool isNonMarking() const override { return nonMarking; } // Separation-specific access. const GooString *getName() const { return name; } GfxColorSpace *getAlt() { return alt; } const Function *getFunc() const { return func; } private: GfxSeparationColorSpace(GooString *nameA, GfxColorSpace *altA, Function *funcA, bool nonMarkingA, unsigned int overprintMaskA, int *mappingA); GooString *name; // colorant name GfxColorSpace *alt; // alternate color space Function *func; // tint transform (into alternate color space) bool nonMarking; }; //------------------------------------------------------------------------ // GfxDeviceNColorSpace //------------------------------------------------------------------------ class GfxDeviceNColorSpace : public GfxColorSpace { public: GfxDeviceNColorSpace(int nCompsA, std::vector &&namesA, GfxColorSpace *alt, Function *func, std::vector *sepsCS); ~GfxDeviceNColorSpace() override; GfxColorSpace *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); 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; int getNComps() const override { return nComps; } void getDefaultColor(GfxColor *color) const override; bool isNonMarking() const override { return nonMarking; } // DeviceN-specific access. const std::string &getColorantName(int i) const { return names[i]; } GfxColorSpace *getAlt() { return alt; } 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); const int nComps; // number of components const std::vector names; // colorant names GfxColorSpace *alt; // alternate color space Function *func; // tint transform (into alternate color space) bool nonMarking; std::vector *sepsCS; // list of separation cs for spot colorants; }; //------------------------------------------------------------------------ // GfxPatternColorSpace //------------------------------------------------------------------------ class GfxPatternColorSpace : public GfxColorSpace { public: explicit GfxPatternColorSpace(GfxColorSpace *underA); ~GfxPatternColorSpace() override; GfxColorSpace *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); 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; int getNComps() const override { return 0; } void getDefaultColor(GfxColor *color) const override; // Pattern-specific access. GfxColorSpace *getUnder() { return under; } private: GfxColorSpace *under; // underlying color space (for uncolored // patterns) }; //------------------------------------------------------------------------ // GfxPattern //------------------------------------------------------------------------ class GfxPattern { public: GfxPattern(int typeA, int patternRefNumA); virtual ~GfxPattern(); GfxPattern(const GfxPattern &) = delete; GfxPattern &operator=(const GfxPattern &other) = delete; static GfxPattern *parse(GfxResources *res, Object *obj, OutputDev *out, GfxState *state, int patternRefNum); virtual GfxPattern *copy() const = 0; int getType() const { return type; } int getPatternRefNum() const { return patternRefNum; } private: int type; int patternRefNum; }; //------------------------------------------------------------------------ // GfxTilingPattern //------------------------------------------------------------------------ class GfxTilingPattern : public GfxPattern { public: static GfxTilingPattern *parse(Object *patObj, int patternRefNum); ~GfxTilingPattern() override; GfxPattern *copy() const override; int getPaintType() const { return paintType; } int getTilingType() const { return tilingType; } const double *getBBox() const { return bbox; } double getXStep() const { return xStep; } double getYStep() const { return yStep; } Dict *getResDict() { return resDict.isDict() ? resDict.getDict() : (Dict *)nullptr; } const double *getMatrix() const { return matrix; } Object *getContentStream() { return &contentStream; } private: GfxTilingPattern(int paintTypeA, int tilingTypeA, const double *bboxA, double xStepA, double yStepA, const Object *resDictA, const double *matrixA, const Object *contentStreamA, int patternRefNumA); int paintType; int tilingType; double bbox[4]; double xStep, yStep; Object resDict; double matrix[6]; Object contentStream; }; //------------------------------------------------------------------------ // GfxShadingPattern //------------------------------------------------------------------------ class GfxShadingPattern : public GfxPattern { public: static GfxShadingPattern *parse(GfxResources *res, Object *patObj, OutputDev *out, GfxState *state, int patternRefNum); ~GfxShadingPattern() override; GfxPattern *copy() const override; GfxShading *getShading() { return shading; } const double *getMatrix() const { return matrix; } private: GfxShadingPattern(GfxShading *shadingA, const double *matrixA, int patternRefNumA); GfxShading *shading; double matrix[6]; }; //------------------------------------------------------------------------ // GfxShading //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT GfxShading { public: explicit GfxShading(int typeA); explicit GfxShading(const GfxShading *shading); virtual ~GfxShading(); GfxShading(const GfxShading &) = delete; GfxShading &operator=(const GfxShading &other) = delete; static GfxShading *parse(GfxResources *res, Object *obj, OutputDev *out, GfxState *state); virtual GfxShading *copy() const = 0; int getType() const { return type; } GfxColorSpace *getColorSpace() { return colorSpace; } const GfxColor *getBackground() const { return &background; } bool getHasBackground() const { return hasBackground; } void getBBox(double *xMinA, double *yMinA, double *xMaxA, double *yMaxA) const { *xMinA = bbox_xMin; *yMinA = bbox_yMin; *xMaxA = bbox_xMax; *yMaxA = bbox_yMax; } bool getHasBBox() const { return hasBBox; } 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; bool hasBackground; bool hasBBox; GfxColorSpace *colorSpace; GfxColor background; double bbox_xMin, bbox_yMin, bbox_xMax, bbox_yMax; }; //------------------------------------------------------------------------ // GfxUnivariateShading //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT GfxUnivariateShading : public GfxShading { public: GfxUnivariateShading(int typeA, double t0A, double t1A, std::vector> &&funcsA, bool extend0A, bool extend1A); explicit GfxUnivariateShading(const GfxUnivariateShading *shading); ~GfxUnivariateShading() override; double getDomain0() const { return t0; } double getDomain1() const { return t1; } bool getExtend0() const { return extend0; } bool getExtend1() const { return extend1; } int getNFuncs() const { return funcs.size(); } const Function *getFunc(int i) const { return funcs[i].get(); } // returns the nComps of the shading // i.e. how many positions of color have been set int getColor(double t, GfxColor *color); void setupCache(const Matrix *ctm, double xMin, double yMin, double xMax, double yMax); virtual void getParameterRange(double *lower, double *upper, double xMin, double yMin, double xMax, double yMax) = 0; virtual double getDistance(double sMin, double sMax) const = 0; protected: bool init(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state) override; private: double t0, t1; std::vector> funcs; bool extend0, extend1; int cacheSize, lastMatch; double *cacheBounds; double *cacheCoeff; double *cacheValues; }; //------------------------------------------------------------------------ // GfxFunctionShading //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT GfxFunctionShading : public GfxShading { public: GfxFunctionShading(double x0A, double y0A, double x1A, double y1A, const double *matrixA, std::vector> &&funcsA); explicit GfxFunctionShading(const GfxFunctionShading *shading); ~GfxFunctionShading() override; static GfxFunctionShading *parse(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state); GfxShading *copy() const override; void getDomain(double *x0A, double *y0A, double *x1A, double *y1A) const { *x0A = x0; *y0A = y0; *x1A = x1; *y1A = y1; } const double *getMatrix() const { return matrix; } int getNFuncs() const { return funcs.size(); } const Function *getFunc(int i) const { return funcs[i].get(); } void getColor(double x, double y, GfxColor *color) const; protected: bool init(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state) override; private: double x0, y0, x1, y1; double matrix[6]; std::vector> funcs; }; //------------------------------------------------------------------------ // GfxAxialShading //------------------------------------------------------------------------ class GfxAxialShading : public GfxUnivariateShading { public: GfxAxialShading(double x0A, double y0A, double x1A, double y1A, double t0A, double t1A, std::vector> &&funcsA, bool extend0A, bool extend1A); explicit GfxAxialShading(const GfxAxialShading *shading); ~GfxAxialShading() override; static GfxAxialShading *parse(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state); GfxShading *copy() const override; void getCoords(double *x0A, double *y0A, double *x1A, double *y1A) const { *x0A = x0; *y0A = y0; *x1A = x1; *y1A = y1; } void getParameterRange(double *lower, double *upper, double xMin, double yMin, double xMax, double yMax) override; double getDistance(double sMin, double sMax) const override; private: double x0, y0, x1, y1; }; //------------------------------------------------------------------------ // GfxRadialShading //------------------------------------------------------------------------ class GfxRadialShading : public GfxUnivariateShading { public: GfxRadialShading(double x0A, double y0A, double r0A, double x1A, double y1A, double r1A, double t0A, double t1A, std::vector> &&funcsA, bool extend0A, bool extend1A); explicit GfxRadialShading(const GfxRadialShading *shading); ~GfxRadialShading() override; static GfxRadialShading *parse(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state); GfxShading *copy() const override; void getCoords(double *x0A, double *y0A, double *r0A, double *x1A, double *y1A, double *r1A) const { *x0A = x0; *y0A = y0; *r0A = r0; *x1A = x1; *y1A = y1; *r1A = r1; } void getParameterRange(double *lower, double *upper, double xMin, double yMin, double xMax, double yMax) override; double getDistance(double sMin, double sMax) const override; private: double x0, y0, r0, x1, y1, r1; }; //------------------------------------------------------------------------ // GfxGouraudTriangleShading //------------------------------------------------------------------------ struct GfxGouraudVertex { double x, y; GfxColor color; }; class POPPLER_PRIVATE_EXPORT GfxGouraudTriangleShading : public GfxShading { public: GfxGouraudTriangleShading(int typeA, GfxGouraudVertex *verticesA, int nVerticesA, int (*trianglesA)[3], int nTrianglesA, std::vector> &&funcsA); explicit GfxGouraudTriangleShading(const GfxGouraudTriangleShading *shading); ~GfxGouraudTriangleShading() override; static GfxGouraudTriangleShading *parse(GfxResources *res, int typeA, Dict *dict, Stream *str, OutputDev *out, GfxState *state); GfxShading *copy() const override; int getNTriangles() const { return nTriangles; } bool isParameterized() const { return !funcs.empty(); } /** * @precondition isParameterized() == true */ double getParameterDomainMin() const { assert(isParameterized()); return funcs[0]->getDomainMin(0); } /** * @precondition isParameterized() == true */ double getParameterDomainMax() const { assert(isParameterized()); return funcs[0]->getDomainMax(0); } /** * @precondition isParameterized() == false */ void getTriangle(int i, double *x0, double *y0, GfxColor *color0, double *x1, double *y1, GfxColor *color1, double *x2, double *y2, GfxColor *color2); /** * Variant for functions. * * @precondition isParameterized() == true */ void getTriangle(int i, double *x0, double *y0, double *color0, double *x1, double *y1, double *color1, double *x2, double *y2, double *color2); void getParameterizedColor(double t, GfxColor *color) const; protected: bool init(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state) override; private: GfxGouraudVertex *vertices; int nVertices; int (*triangles)[3]; int nTriangles; std::vector> funcs; }; //------------------------------------------------------------------------ // GfxPatchMeshShading //------------------------------------------------------------------------ /** * A tensor product cubic bezier patch consisting of 4x4 points and 4 color * values. * * See the Shading Type 7 specifications. Note that Shading Type 6 is also * represented using GfxPatch. */ struct GfxPatch { /** * Represents a single color value for the patch. */ struct ColorValue { /** * For parameterized patches, only element 0 is valid; it contains * the single parameter. * * For non-parameterized patches, c contains all color components * as decoded from the input stream. In this case, you will need to * use dblToCol() before assigning them to GfxColor. */ double c[gfxColorMaxComps]; }; double x[4][4]; double y[4][4]; ColorValue color[2][2]; }; class POPPLER_PRIVATE_EXPORT GfxPatchMeshShading : public GfxShading { public: GfxPatchMeshShading(int typeA, GfxPatch *patchesA, int nPatchesA, std::vector> &&funcsA); explicit GfxPatchMeshShading(const GfxPatchMeshShading *shading); ~GfxPatchMeshShading() override; static GfxPatchMeshShading *parse(GfxResources *res, int typeA, Dict *dict, Stream *str, OutputDev *out, GfxState *state); GfxShading *copy() const override; int getNPatches() const { return nPatches; } const GfxPatch *getPatch(int i) const { return &patches[i]; } bool isParameterized() const { return !funcs.empty(); } /** * @precondition isParameterized() == true */ double getParameterDomainMin() const { assert(isParameterized()); return funcs[0]->getDomainMin(0); } /** * @precondition isParameterized() == true */ double getParameterDomainMax() const { assert(isParameterized()); return funcs[0]->getDomainMax(0); } void getParameterizedColor(double t, GfxColor *color) const; protected: bool init(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state) override; private: GfxPatch *patches; int nPatches; std::vector> funcs; }; //------------------------------------------------------------------------ // GfxImageColorMap //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT GfxImageColorMap { public: // Constructor. GfxImageColorMap(int bitsA, Object *decode, GfxColorSpace *colorSpaceA); // Destructor. ~GfxImageColorMap(); GfxImageColorMap(const GfxImageColorMap &) = delete; GfxImageColorMap &operator=(const GfxImageColorMap &) = delete; // Return a copy of this color map. GfxImageColorMap *copy() const { return new GfxImageColorMap(this); } // Is color map valid? bool isOk() const { return ok; } // Get the color space. GfxColorSpace *getColorSpace() { return colorSpace; } // Get stream decoding info. int getNumPixelComps() const { return nComps; } int getBits() const { return bits; } // Get decode table. double getDecodeLow(int i) const { return decodeLow[i]; } double getDecodeHigh(int i) const { return decodeLow[i] + decodeRange[i]; } bool useRGBLine() const { return (colorSpace2 && colorSpace2->useGetRGBLine()) || (!colorSpace2 && colorSpace->useGetRGBLine()); } bool useCMYKLine() const { return (colorSpace2 && colorSpace2->useGetCMYKLine()) || (!colorSpace2 && colorSpace->useGetCMYKLine()); } bool useDeviceNLine() const { return (colorSpace2 && colorSpace2->useGetDeviceNLine()) || (!colorSpace2 && colorSpace->useGetDeviceNLine()); } // Convert an image pixel to a color. void getGray(const unsigned char *x, GfxGray *gray); void getRGB(const unsigned char *x, GfxRGB *rgb); void getRGBLine(unsigned char *in, unsigned int *out, int length); void getRGBLine(unsigned char *in, unsigned char *out, int length); void getRGBXLine(unsigned char *in, unsigned char *out, int length); void getGrayLine(unsigned char *in, unsigned char *out, int length); void getCMYKLine(unsigned char *in, unsigned char *out, int length); void getDeviceNLine(unsigned char *in, unsigned char *out, int length); void getCMYK(const unsigned char *x, GfxCMYK *cmyk); void getDeviceN(const unsigned char *x, GfxColor *deviceN); void getColor(const unsigned char *x, GfxColor *color); // Matte color ops void setMatteColor(const GfxColor *color) { useMatte = true; matteColor = *color; } const GfxColor *getMatteColor() const { return (useMatte) ? &matteColor : nullptr; } private: explicit GfxImageColorMap(const GfxImageColorMap *colorMap); GfxColorSpace *colorSpace; // the image color space int bits; // bits per component int nComps; // number of components in a pixel GfxColorSpace *colorSpace2; // secondary color space int nComps2; // number of components in colorSpace2 GfxColorComp * // lookup table lookup[gfxColorMaxComps]; GfxColorComp * // optimized case lookup table lookup2[gfxColorMaxComps]; unsigned char *byte_lookup; double // minimum values for each component decodeLow[gfxColorMaxComps]; double // max - min value for each component decodeRange[gfxColorMaxComps]; bool useMatte; GfxColor matteColor; bool ok; }; //------------------------------------------------------------------------ // GfxSubpath and GfxPath //------------------------------------------------------------------------ class GfxSubpath { public: // Constructor. GfxSubpath(double x1, double y1); // Destructor. ~GfxSubpath(); GfxSubpath(const GfxSubpath &) = delete; GfxSubpath &operator=(const GfxSubpath &) = delete; // Copy. GfxSubpath *copy() const { return new GfxSubpath(this); } // Get points. int getNumPoints() const { return n; } double getX(int i) const { return x[i]; } double getY(int i) const { return y[i]; } bool getCurve(int i) const { return curve[i]; } void setX(int i, double a) { x[i] = a; } void setY(int i, double a) { y[i] = a; } // Get last point. double getLastX() const { return x[n - 1]; } double getLastY() const { return y[n - 1]; } // Add a line segment. void lineTo(double x1, double y1); // Add a Bezier curve. void curveTo(double x1, double y1, double x2, double y2, double x3, double y3); // Close the subpath. void close(); bool isClosed() const { return closed; } // Add (, ) to each point in the subpath. void offset(double dx, double dy); private: double *x, *y; // points bool *curve; // curve[i] => point i is a control point // for a Bezier curve int n; // number of points int size; // size of x/y arrays bool closed; // set if path is closed explicit GfxSubpath(const GfxSubpath *subpath); }; class POPPLER_PRIVATE_EXPORT GfxPath { public: // Constructor. GfxPath(); // Destructor. ~GfxPath(); GfxPath(const GfxPath &) = delete; GfxPath &operator=(const GfxPath &) = delete; // Copy. GfxPath *copy() const { return new GfxPath(justMoved, firstX, firstY, subpaths, n, size); } // Is there a current point? bool isCurPt() const { return n > 0 || justMoved; } // Is the path non-empty, i.e., is there at least one segment? bool isPath() const { return n > 0; } // Get subpaths. int getNumSubpaths() const { return n; } GfxSubpath *getSubpath(int i) { return subpaths[i]; } const GfxSubpath *getSubpath(int i) const { return subpaths[i]; } // Get last point on last subpath. double getLastX() const { return subpaths[n - 1]->getLastX(); } double getLastY() const { return subpaths[n - 1]->getLastY(); } // Move the current point. void moveTo(double x, double y); // Add a segment to the last subpath. void lineTo(double x, double y); // Add a Bezier curve to the last subpath void curveTo(double x1, double y1, double x2, double y2, double x3, double y3); // Close the last subpath. void close(); // Append to . void append(GfxPath *path); // Add (, ) to each point in the path. void offset(double dx, double dy); private: bool justMoved; // set if a new subpath was just started double firstX, firstY; // first point in new subpath GfxSubpath **subpaths; // subpaths int n; // number of subpaths int size; // size of subpaths array GfxPath(bool justMoved1, double firstX1, double firstY1, GfxSubpath **subpaths1, int n1, int size1); }; //------------------------------------------------------------------------ // GfxState //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT GfxState { public: /** * When GfxState::getReusablePath() is invoked, the currently active * path is taken per reference and its coordinates can be re-edited. * * A ReusablePathIterator is intended to reduce overhead when the same * path type is used a lot of times, only with different coordinates. It * allows just to update the coordinates (occurring in the same order as * in the original path). */ class ReusablePathIterator { public: /** * Creates the ReusablePathIterator. This should only be done from * GfxState::getReusablePath(). * * @param path the path as it is used so far. Changing this path, * deleting it or starting a new path from scratch will most likely * invalidate the iterator (and may cause serious problems). Make * sure the path's memory structure is not changed during the * lifetime of the ReusablePathIterator. */ explicit ReusablePathIterator(GfxPath *path); /** * Returns true if and only if the current iterator position is * beyond the last valid point. * * A call to setCoord() will be undefined. */ bool isEnd() const; /** * Advances the iterator. */ void next(); /** * Updates the coordinates associated to the current iterator * position. */ void setCoord(double x, double y); /** * Resets the iterator. */ void reset(); private: GfxPath *path; int subPathOff; int coordOff; int numCoords; GfxSubpath *curSubPath; }; // Construct a default GfxState, for a device with resolution // x , page box , page rotation , and // coordinate system specified by . GfxState(double hDPIA, double vDPIA, const PDFRectangle *pageBox, int rotateA, bool upsideDown); // Destructor. ~GfxState(); GfxState(const GfxState &) = delete; GfxState &operator=(const GfxState &) = delete; // Copy. GfxState *copy(bool copyPath = false) const { return new GfxState(this, copyPath); } // Accessors. double getHDPI() const { return hDPI; } double getVDPI() const { return vDPI; } const double *getCTM() const { return ctm; } void getCTM(Matrix *m) const { memcpy(m->m, ctm, sizeof m->m); } double getX1() const { return px1; } double getY1() const { return py1; } double getX2() const { return px2; } double getY2() const { return py2; } double getPageWidth() const { return pageWidth; } double getPageHeight() const { return pageHeight; } int getRotate() const { return rotate; } const GfxColor *getFillColor() const { return &fillColor; } const GfxColor *getStrokeColor() const { return &strokeColor; } void getFillGray(GfxGray *gray) { fillColorSpace->getGray(&fillColor, gray); } void getStrokeGray(GfxGray *gray) { strokeColorSpace->getGray(&strokeColor, gray); } void getFillRGB(GfxRGB *rgb) const { fillColorSpace->getRGB(&fillColor, rgb); } void getStrokeRGB(GfxRGB *rgb) const { strokeColorSpace->getRGB(&strokeColor, rgb); } void getFillCMYK(GfxCMYK *cmyk) { fillColorSpace->getCMYK(&fillColor, cmyk); } 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; } GfxBlendMode getBlendMode() const { return blendMode; } double getFillOpacity() const { return fillOpacity; } double getStrokeOpacity() const { return strokeOpacity; } bool getFillOverprint() const { return fillOverprint; } bool getStrokeOverprint() const { return strokeOverprint; } int getOverprintMode() const { return overprintMode; } Function **getTransfer() { return transfer; } double getLineWidth() const { return lineWidth; } const std::vector &getLineDash(double *start) { *start = lineDashStart; return lineDash; } int getFlatness() const { return flatness; } int getLineJoin() const { return lineJoin; } int getLineCap() const { return lineCap; } double getMiterLimit() const { return miterLimit; } bool getStrokeAdjust() const { return strokeAdjust; } bool getAlphaIsShape() const { return alphaIsShape; } bool getTextKnockout() const { return textKnockout; } const std::shared_ptr &getFont() const { return font; } double getFontSize() const { return fontSize; } const double *getTextMat() const { return textMat; } double getCharSpace() const { return charSpace; } double getWordSpace() const { return wordSpace; } double getHorizScaling() const { return horizScaling; } double getLeading() const { return leading; } double getRise() const { return rise; } int getRender() const { return render; } const char *getRenderingIntent() const { return renderingIntent; } const GfxPath *getPath() const { return path; } void setPath(GfxPath *pathA); double getCurX() const { return curX; } double getCurY() const { return curY; } void getClipBBox(double *xMin, double *yMin, double *xMax, double *yMax) const { *xMin = clipXMin; *yMin = clipYMin; *xMax = clipXMax; *yMax = clipYMax; } void getUserClipBBox(double *xMin, double *yMin, double *xMax, double *yMax) const; double getLineX() const { return lineX; } double getLineY() const { return lineY; } // Is there a current point/path? bool isCurPt() const { return path->isCurPt(); } bool isPath() const { return path->isPath(); } // Transforms. void transform(double x1, double y1, double *x2, double *y2) const { *x2 = ctm[0] * x1 + ctm[2] * y1 + ctm[4]; *y2 = ctm[1] * x1 + ctm[3] * y1 + ctm[5]; } void transformDelta(double x1, double y1, double *x2, double *y2) const { *x2 = ctm[0] * x1 + ctm[2] * y1; *y2 = ctm[1] * x1 + ctm[3] * y1; } void textTransform(double x1, double y1, double *x2, double *y2) const { *x2 = textMat[0] * x1 + textMat[2] * y1 + textMat[4]; *y2 = textMat[1] * x1 + textMat[3] * y1 + textMat[5]; } void textTransformDelta(double x1, double y1, double *x2, double *y2) const { *x2 = textMat[0] * x1 + textMat[2] * y1; *y2 = textMat[1] * x1 + textMat[3] * y1; } double transformWidth(double w) const; double getTransformedLineWidth() const { return transformWidth(lineWidth); } double getTransformedFontSize() const; void getFontTransMat(double *m11, double *m12, double *m21, double *m22) const; // Change state parameters. 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 setFillColor(const GfxColor *color) { fillColor = *color; } void setStrokeColor(const GfxColor *color) { strokeColor = *color; } void setFillPattern(GfxPattern *pattern); void setStrokePattern(GfxPattern *pattern); void setBlendMode(GfxBlendMode mode) { blendMode = mode; } void setFillOpacity(double opac) { fillOpacity = opac; } void setStrokeOpacity(double opac) { strokeOpacity = opac; } void setFillOverprint(bool op) { fillOverprint = op; } void setStrokeOverprint(bool op) { strokeOverprint = op; } void setOverprintMode(int op) { overprintMode = op; } void setTransfer(Function **funcs); 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 setMiterLimit(double limit) { miterLimit = limit; } void setStrokeAdjust(bool sa) { strokeAdjust = sa; } void setAlphaIsShape(bool ais) { alphaIsShape = ais; } void setTextKnockout(bool tk) { textKnockout = tk; } void setFont(std::shared_ptr fontA, double fontSizeA); void setTextMat(double a, double b, double c, double d, double e, double f) { textMat[0] = a; textMat[1] = b; textMat[2] = c; textMat[3] = d; textMat[4] = e; textMat[5] = f; } void setCharSpace(double space) { charSpace = space; } void setWordSpace(double space) { wordSpace = space; } void setHorizScaling(double scale) { horizScaling = 0.01 * scale; } void setLeading(double leadingA) { leading = leadingA; } void setRise(double riseA) { rise = riseA; } void setRender(int renderA) { render = renderA; } void setRenderingIntent(const char *intent) { strncpy(renderingIntent, intent, 31); } #ifdef USE_CMS void setDisplayProfile(const GfxLCMSProfilePtr &localDisplayProfileA); GfxLCMSProfilePtr getDisplayProfile() { return localDisplayProfile; } std::shared_ptr getXYZ2DisplayTransform(); int getCmsRenderingIntent(); static GfxLCMSProfilePtr sRGBProfile; #endif void setDefaultGrayColorSpace(GfxColorSpace *cs) { defaultGrayColorSpace = cs; } void setDefaultRGBColorSpace(GfxColorSpace *cs) { defaultRGBColorSpace = cs; } void setDefaultCMYKColorSpace(GfxColorSpace *cs) { defaultCMYKColorSpace = cs; } GfxColorSpace *copyDefaultGrayColorSpace() { if (defaultGrayColorSpace) { return defaultGrayColorSpace->copy(); } return new GfxDeviceGrayColorSpace(); } GfxColorSpace *copyDefaultRGBColorSpace() { if (defaultRGBColorSpace) { return defaultRGBColorSpace->copy(); } return new GfxDeviceRGBColorSpace(); } GfxColorSpace *copyDefaultCMYKColorSpace() { if (defaultCMYKColorSpace) { return defaultCMYKColorSpace->copy(); } return new GfxDeviceCMYKColorSpace(); } // 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 closePath() { path->close(); curX = path->getLastX(); curY = path->getLastY(); } void clearPath(); // Update clip region. void clip(); void clipToStrokePath(); void clipToRect(double xMin, double yMin, double xMax, double yMax); // Text position. void textSetPos(double tx, double ty) { lineX = tx; lineY = ty; } void textMoveTo(double tx, double ty) { lineX = tx; lineY = ty; textTransform(tx, ty, &curX, &curY); } void textShift(double tx, double ty); void shift(double dx, double dy); // 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)); } // Misc bool parseBlendMode(Object *obj, GfxBlendMode *mode); ReusablePathIterator *getReusablePath() { return new ReusablePathIterator(path); } private: double hDPI, vDPI; // resolution double ctm[6]; // coord transform matrix double px1, py1, px2, py2; // page corners (user coords) double pageWidth, pageHeight; // page size (pixels) int rotate; // page rotation angle GfxColorSpace *fillColorSpace; // fill color space GfxColorSpace *strokeColorSpace; // stroke color space GfxColor fillColor; // fill color GfxColor strokeColor; // stroke color GfxPattern *fillPattern; // fill pattern GfxPattern *strokePattern; // stroke pattern GfxBlendMode blendMode; // transparency blend mode double fillOpacity; // fill opacity double strokeOpacity; // stroke opacity bool fillOverprint; // fill overprint bool strokeOverprint; // stroke overprint int overprintMode; // overprint mode Function *transfer[4]; // transfer function (entries may be: all // nullptr = identity; last three nullptr = // single function; all four non-nullptr = // R,G,B,gray functions) double lineWidth; // line width std::vector lineDash; // line dash double lineDashStart; int flatness; // curve flatness int lineJoin; // line join style int lineCap; // line cap style double miterLimit; // line miter limit bool strokeAdjust; // stroke adjustment bool alphaIsShape; // alpha is shape bool textKnockout; // text knockout std::shared_ptr font; // font double fontSize; // font size double textMat[6]; // text matrix double charSpace; // character spacing double wordSpace; // word spacing double horizScaling; // horizontal scaling double leading; // text leading double rise; // text rise int render; // text rendering mode GfxPath *path; // array of path elements double curX, curY; // current point (user coords) double lineX, lineY; // start of current text line (text coords) double clipXMin, clipYMin, // bounding box for clip region clipXMax, clipYMax; char renderingIntent[32]; GfxState *saved; // next GfxState on stack GfxState(const GfxState *state, bool copyPath); #ifdef USE_CMS GfxLCMSProfilePtr localDisplayProfile; std::shared_ptr XYZ2DisplayTransformRelCol; std::shared_ptr XYZ2DisplayTransformAbsCol; std::shared_ptr XYZ2DisplayTransformSat; std::shared_ptr XYZ2DisplayTransformPerc; static GfxLCMSProfilePtr XYZProfile; #endif GfxColorSpace *defaultGrayColorSpace; GfxColorSpace *defaultRGBColorSpace; GfxColorSpace *defaultCMYKColorSpace; }; #endif poppler-24.02.0/poppler/GfxState_helpers.h000066400000000000000000000050671455701731300204570ustar00rootroot00000000000000//======================================================================== // // GfxState.cc // // 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) 2009, 2011, 2018, 2019 Albert Astals Cid // Copyright (C) 2019 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 // //======================================================================== #ifndef GFXSTATE_HELPERS_H #define GFXSTATE_HELPERS_H #include "GfxState.h" static inline GfxColorComp clip01(GfxColorComp x) { return (x < 0) ? 0 : (x > gfxColorComp1) ? gfxColorComp1 : x; } static inline double clip01(double x) { return (x < 0) ? 0 : (x > 1) ? 1 : x; } static inline void cmykToRGBMatrixMultiplication(const double c, const double m, const double y, const double k, const double c1, const double m1, const double y1, const double k1, double &r, double &g, double &b) { double x; // this is a matrix multiplication, unrolled for performance // C M Y K x = c1 * m1 * y1 * k1; // 0 0 0 0 r = g = b = x; x = c1 * m1 * y1 * k; // 0 0 0 1 r += 0.1373 * x; g += 0.1216 * x; b += 0.1255 * x; x = c1 * m1 * y * k1; // 0 0 1 0 r += x; g += 0.9490 * x; x = c1 * m1 * y * k; // 0 0 1 1 r += 0.1098 * x; g += 0.1020 * x; x = c1 * m * y1 * k1; // 0 1 0 0 r += 0.9255 * x; b += 0.5490 * x; x = c1 * m * y1 * k; // 0 1 0 1 r += 0.1412 * x; x = c1 * m * y * k1; // 0 1 1 0 r += 0.9294 * x; g += 0.1098 * x; b += 0.1412 * x; x = c1 * m * y * k; // 0 1 1 1 r += 0.1333 * x; x = c * m1 * y1 * k1; // 1 0 0 0 g += 0.6784 * x; b += 0.9373 * x; x = c * m1 * y1 * k; // 1 0 0 1 g += 0.0588 * x; b += 0.1412 * x; x = c * m1 * y * k1; // 1 0 1 0 g += 0.6510 * x; b += 0.3137 * x; x = c * m1 * y * k; // 1 0 1 1 g += 0.0745 * x; x = c * m * y1 * k1; // 1 1 0 0 r += 0.1804 * x; g += 0.1922 * x; b += 0.5725 * x; x = c * m * y1 * k; // 1 1 0 1 b += 0.0078 * x; x = c * m * y * k1; // 1 1 1 0 r += 0.2118 * x; g += 0.2119 * x; b += 0.2235 * x; } #endif poppler-24.02.0/poppler/GlobalParams.cc000066400000000000000000001475251455701731300177200ustar00rootroot00000000000000//======================================================================== // // GlobalParams.cc // // Copyright 2001-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) 2005 Martin Kretzschmar // Copyright (C) 2005, 2006 Kristian Høgsberg // Copyright (C) 2005, 2007-2010, 2012, 2015, 2017-2023 Albert Astals Cid // Copyright (C) 2005 Jonathan Blandford // Copyright (C) 2006, 2007 Jeff Muizelaar // Copyright (C) 2006 Takashi Iwai // Copyright (C) 2006 Ed Catmur // Copyright (C) 2007 Krzysztof Kowalczyk // Copyright (C) 2007, 2009 Jonathan Kew // Copyright (C) 2009 Petr Gajdos // Copyright (C) 2009, 2011, 2012, 2015 William Bader // Copyright (C) 2009 Kovid Goyal // Copyright (C) 2010, 2012 Hib Eris // Copyright (C) 2010 Patrick Spendrin // Copyright (C) 2010 Jakub Wilk // Copyright (C) 2011 Pino Toscano // Copyright (C) 2011 Koji Otani // Copyright (C) 2012 Yi Yang // Copyright (C) 2012, 2017 Adrian Johnson // Copyright (C) 2012 Thomas Freitag // Copyright (C) 2012 Peter Breitenlohner // Copyright (C) 2013, 2014 Jason Crain // Copyright (C) 2017 Christoph Cullmann // Copyright (C) 2017 Jean Ghali // 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) 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 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 // //======================================================================== #include #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" #include "UnicodeMap.h" #include "CMap.h" #include "FontEncodingTables.h" #include "GlobalParams.h" #include "GfxFont.h" #ifdef WITH_FONTCONFIGURATION_FONTCONFIG # include #endif #ifndef _MSC_VER # include #endif #ifndef FC_WEIGHT_BOOK # define FC_WEIGHT_BOOK 75 #endif #include "NameToUnicodeTable.h" #include "UnicodeMapTables.h" #include "UnicodeMapFuncs.h" #include "fofi/FoFiTrueType.h" #include "fofi/FoFiIdentifier.h" //------------------------------------------------------------------------ #define cidToUnicodeCacheSize 4 #define unicodeToUnicodeCacheSize 4 //------------------------------------------------------------------------ std::unique_ptr globalParams; #if defined(ENABLE_RELOCATABLE) && defined(_WIN32) /* search for data relative to where we are installed */ static HMODULE hmodule; extern "C" { /* Provide declaration to squelch -Wmissing-declarations warning */ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved); BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { switch (fdwReason) { case DLL_PROCESS_ATTACH: hmodule = hinstDLL; break; } return TRUE; } } static std::string get_poppler_localdir(const std::string &suffix) { const std::string binSuffix("\\bin"); std::string retval(MAX_PATH, '\0'); if (!GetModuleFileNameA(hmodule, retval.data(), retval.size())) { return POPPLER_DATADIR; } const std::string::size_type p = retval.rfind('\\'); if (p != std::string::npos) { retval.erase(p); if (retval.size() > binSuffix.size() && stricmp(retval.substr(p - binSuffix.size()).c_str(), binSuffix.c_str()) == 0) { retval.erase(p - binSuffix.size()); } } retval += suffix; retval.shrink_to_fit(); return retval; } static const char *get_poppler_datadir(void) { static std::string retval; static bool beenhere = false; if (!beenhere) { retval = get_poppler_localdir("\\share\\poppler"); beenhere = true; } return retval.c_str(); } # undef POPPLER_DATADIR # define POPPLER_DATADIR get_poppler_datadir() static const char *get_poppler_fontsdir(void) { static std::string retval; static bool beenhere = false; if (!beenhere) { retval = get_poppler_localdir("\\share\\fonts"); beenhere = true; } return retval.c_str(); } # undef POPPLER_FONTSDIR # define POPPLER_FONTSDIR get_poppler_fontsdir() #else # define POPPLER_FONTSDIR nullptr #endif //------------------------------------------------------------------------ // SysFontInfo //------------------------------------------------------------------------ class SysFontInfo { public: GooString *name; bool bold; bool italic; bool oblique; bool fixedWidth; GooString *path; SysFontType type; int fontNum; // for TrueType collections GooString *substituteName; SysFontInfo(GooString *nameA, bool boldA, bool italicA, bool obliqueA, bool fixedWidthA, GooString *pathA, SysFontType typeA, int fontNumA, GooString *substituteNameA); ~SysFontInfo(); SysFontInfo(const SysFontInfo &) = delete; SysFontInfo &operator=(const SysFontInfo &) = delete; bool match(const SysFontInfo *fi) const; bool match(const GooString *nameA, bool boldA, bool italicA, bool obliqueA, bool fixedWidthA) const; 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) { 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; } bool SysFontInfo::match(const SysFontInfo *fi) const { return !strcasecmp(name->c_str(), fi->name->c_str()) && bold == fi->bold && italic == fi->italic && oblique == fi->oblique && fixedWidth == fi->fixedWidth; } bool SysFontInfo::match(const GooString *nameA, bool boldA, bool italicA, bool obliqueA, bool fixedWidthA) const { return !strcasecmp(name->c_str(), nameA->c_str()) && bold == boldA && italic == italicA && oblique == obliqueA && fixedWidth == fixedWidthA; } bool SysFontInfo::match(const GooString *nameA, bool boldA, bool italicA) const { return !strcasecmp(name->c_str(), nameA->c_str()) && bold == boldA && italic == italicA; } //------------------------------------------------------------------------ // SysFontList //------------------------------------------------------------------------ class SysFontList { public: SysFontList(); ~SysFontList(); SysFontList(const SysFontList &) = delete; SysFontList &operator=(const SysFontList &) = delete; const SysFontInfo *find(const std::string &name, bool isFixedWidth, bool exact, const std::vector &filesToIgnore = {}); const std::vector &getFonts() const { return fonts; } #ifdef _WIN32 void scanWindowsFonts(const std::string &winFontDir); #endif #ifdef WITH_FONTCONFIGURATION_FONTCONFIG void addFcFont(SysFontInfo *si) { fonts.push_back(si); } #endif private: #ifdef _WIN32 SysFontInfo *makeWindowsFont(const char *name, int fontNum, const char *path); #endif std::vector fonts; }; SysFontList::SysFontList() { } SysFontList::~SysFontList() { for (auto entry : fonts) { delete entry; } } const SysFontInfo *SysFontList::find(const std::string &name, bool fixedWidth, bool exact, const std::vector &filesToIgnore) { GooString *name2; bool bold, italic, oblique; int n; name2 = new GooString(name); // remove space, comma, dash chars { int i = 0; while (i < name2->getLength()) { const char c = name2->getChar(i); if (c == ' ' || c == ',' || c == '-') { name2->del(i); } else { ++i; } } n = name2->getLength(); } // remove trailing "MT" (Foo-MT, Foo-BoldMT, etc.) if (n > 2 && !strcmp(name2->c_str() + n - 2, "MT")) { name2->del(n - 2, 2); n -= 2; } // look for "Regular" if (n > 7 && !strcmp(name2->c_str() + n - 7, "Regular")) { name2->del(n - 7, 7); n -= 7; } // look for "Italic" if (n > 6 && !strcmp(name2->c_str() + n - 6, "Italic")) { name2->del(n - 6, 6); italic = true; n -= 6; } else { italic = false; } // look for "Oblique" if (n > 6 && !strcmp(name2->c_str() + n - 7, "Oblique")) { name2->del(n - 7, 7); oblique = true; n -= 6; } else { oblique = false; } // look for "Bold" if (n > 4 && !strcmp(name2->c_str() + n - 4, "Bold")) { name2->del(n - 4, 4); bold = true; n -= 4; } else { bold = false; } // remove trailing "MT" (FooMT-Bold, etc.) if (n > 2 && !strcmp(name2->c_str() + n - 2, "MT")) { name2->del(n - 2, 2); n -= 2; } // remove trailing "PS" if (n > 2 && !strcmp(name2->c_str() + n - 2, "PS")) { name2->del(n - 2, 2); n -= 2; } // remove trailing "IdentityH" if (n > 9 && !strcmp(name2->c_str() + n - 9, "IdentityH")) { name2->del(n - 9, 9); n -= 9; } // search for the font const SysFontInfo *fi = nullptr; 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()) { break; } } fi = nullptr; } if (!fi && !exact && bold) { // try ignoring the bold flag 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()) { break; } } fi = nullptr; } } if (!fi && !exact && (bold || italic)) { // try ignoring the bold and italic flags 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()) { break; } } fi = nullptr; } } delete name2; return fi; } #define globalParamsLocker() const std::scoped_lock locker(mutex) #define unicodeMapCacheLocker() const std::scoped_lock locker(unicodeMapCacheMutex) #define cMapCacheLocker() const std::scoped_lock locker(cMapCacheMutex) //------------------------------------------------------------------------ // parsing //------------------------------------------------------------------------ GlobalParams::GlobalParams(const char *customPopplerDataDir) : popplerDataDir(customPopplerDataDir) { // scan the encoding in reverse because we want the lowest-numbered // index for each char name ('space' is encoded twice) macRomanReverseMap = new NameToCharCode(); for (int i = 255; i >= 0; --i) { if (macRomanEncoding[i]) { macRomanReverseMap->add(macRomanEncoding[i], (CharCode)i); } } nameToUnicodeZapfDingbats = new NameToCharCode(); nameToUnicodeText = new NameToCharCode(); sysFonts = new SysFontList(); textEncoding = new GooString("UTF-8"); printCommands = false; profileCommands = false; errQuiet = false; cidToUnicodeCache = new CharCodeToUnicodeCache(cidToUnicodeCacheSize); unicodeToUnicodeCache = new CharCodeToUnicodeCache(unicodeToUnicodeCacheSize); unicodeMapCache = new UnicodeMapCache(); cMapCache = new CMapCache(); utf8Map = nullptr; baseFontsInitialized = false; // set up the initial nameToUnicode tables for (int i = 0; nameToUnicodeZapfDingbatsTab[i].name; ++i) { nameToUnicodeZapfDingbats->add(nameToUnicodeZapfDingbatsTab[i].name, nameToUnicodeZapfDingbatsTab[i].u); } for (int i = 0; nameToUnicodeTextTab[i].name; ++i) { nameToUnicodeText->add(nameToUnicodeTextTab[i].name, nameToUnicodeTextTab[i].u); } // set up the residentUnicodeMaps table residentUnicodeMaps.reserve(6); UnicodeMap map = { "Latin1", false, latin1UnicodeMapRanges, latin1UnicodeMapLen }; residentUnicodeMaps.emplace(map.getEncodingName(), std::move(map)); map = { "ASCII7", false, ascii7UnicodeMapRanges, ascii7UnicodeMapLen }; residentUnicodeMaps.emplace(map.getEncodingName(), std::move(map)); map = { "Symbol", false, symbolUnicodeMapRanges, symbolUnicodeMapLen }; residentUnicodeMaps.emplace(map.getEncodingName(), std::move(map)); map = { "ZapfDingbats", false, zapfDingbatsUnicodeMapRanges, zapfDingbatsUnicodeMapLen }; residentUnicodeMaps.emplace(map.getEncodingName(), std::move(map)); map = { "UTF-8", true, &mapUTF8 }; residentUnicodeMaps.emplace(map.getEncodingName(), std::move(map)); map = { "UTF-16", true, &mapUTF16 }; residentUnicodeMaps.emplace(map.getEncodingName(), std::move(map)); scanEncodingDirs(); } 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]; snprintf(dataPathBuffer, bufSize, "%s/nameToUnicode", dataRoot); dir = new GDir(dataPathBuffer, true); while (entry = dir->getNextEntry(), entry != nullptr) { if (!entry->isDir()) { parseNameToUnicode(entry->getFullPath()); } } delete dir; snprintf(dataPathBuffer, bufSize, "%s/cidToUnicode", dataRoot); dir = new GDir(dataPathBuffer, false); while (entry = dir->getNextEntry(), entry != nullptr) { addCIDToUnicode(entry->getName(), entry->getFullPath()); } delete dir; snprintf(dataPathBuffer, bufSize, "%s/unicodeMap", dataRoot); dir = new GDir(dataPathBuffer, false); while (entry = dir->getNextEntry(), entry != nullptr) { addUnicodeMap(entry->getName(), entry->getFullPath()); } 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()); } delete dir; delete[] dataPathBuffer; } void GlobalParams::parseNameToUnicode(const GooString *name) { char *tok1, *tok2; FILE *f; char buf[256]; int line; Unicode u; char *tokptr; if (!(f = openFile(name->c_str(), "r"))) { error(errIO, -1, "Couldn't open 'nameToUnicode' file '{0:t}'", name); return; } line = 1; while (getLine(buf, sizeof(buf), f)) { tok1 = strtok_r(buf, " \t\r\n", &tokptr); tok2 = strtok_r(nullptr, " \t\r\n", &tokptr); if (tok1 && tok2) { sscanf(tok1, "%x", &u); nameToUnicodeText->add(tok2, u); } else { error(errConfig, -1, "Bad line in 'nameToUnicode' file ({0:t}:{1:d})", name, line); } ++line; } fclose(f); } void GlobalParams::addCIDToUnicode(const GooString *collection, const GooString *fileName) { cidToUnicodes[collection->toStr()] = fileName->toStr(); } void GlobalParams::addUnicodeMap(const GooString *encodingName, const GooString *fileName) { unicodeMaps[encodingName->toStr()] = fileName->toStr(); } void GlobalParams::addCMapDir(const GooString *collection, const GooString *dir) { cMapDirs.emplace(collection->toStr(), dir->toStr()); } bool GlobalParams::parseYesNo2(const char *token, bool *flag) { if (!strcmp(token, "yes")) { *flag = true; } else if (!strcmp(token, "no")) { *flag = false; } else { return false; } return true; } GlobalParams::~GlobalParams() { delete macRomanReverseMap; delete nameToUnicodeZapfDingbats; delete nameToUnicodeText; for (auto entry : toUnicodeDirs) { delete entry; } delete sysFonts; delete textEncoding; delete cidToUnicodeCache; delete unicodeToUnicodeCache; delete unicodeMapCache; delete cMapCache; } //------------------------------------------------------------------------ // accessors //------------------------------------------------------------------------ CharCode GlobalParams::getMacRomanCharCode(const char *charName) { // no need to lock - macRomanReverseMap is constant return macRomanReverseMap->lookup(charName); } Unicode GlobalParams::mapNameToUnicodeAll(const char *charName) { // no need to lock - nameToUnicodeZapfDingbats and nameToUnicodeText are constant Unicode u = nameToUnicodeZapfDingbats->lookup(charName); if (!u) { u = nameToUnicodeText->lookup(charName); } return u; } Unicode GlobalParams::mapNameToUnicodeText(const char *charName) { // no need to lock - nameToUnicodeText is constant return nameToUnicodeText->lookup(charName); } UnicodeMap *GlobalParams::getResidentUnicodeMap(const std::string &encodingName) { UnicodeMap *map = nullptr; globalParamsLocker(); const auto unicodeMap = residentUnicodeMaps.find(encodingName); if (unicodeMap != residentUnicodeMaps.end()) { map = &unicodeMap->second; } return map; } FILE *GlobalParams::getUnicodeMapFile(const std::string &encodingName) { FILE *file = nullptr; globalParamsLocker(); const auto unicodeMap = unicodeMaps.find(encodingName); if (unicodeMap != unicodeMaps.end()) { file = openFile(unicodeMap->second.c_str(), "r"); } return file; } FILE *GlobalParams::findCMapFile(const GooString *collection, const GooString *cMapName) { FILE *file = nullptr; globalParamsLocker(); 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()); file = openFile(path->c_str(), "r"); delete path; if (file) { break; } } return file; } FILE *GlobalParams::findToUnicodeFile(const GooString *name) { GooString *fileName; FILE *f; globalParamsLocker(); for (const GooString *dir : toUnicodeDirs) { fileName = appendToPath(dir->copy(), name->c_str()); f = openFile(fileName->c_str(), "r"); delete fileName; if (f) { return f; } } return nullptr; } #ifdef WITH_FONTCONFIGURATION_FONTCONFIG static bool findModifier(const std::string &name, const size_t modStart, const char *modifier, size_t &start) { if (modStart == std::string::npos) { return false; } size_t match = name.find(modifier, modStart); if (match == std::string::npos) { return false; } else { if (start == std::string::npos || match < start) { start = match; } return true; } } static const char *getFontLang(const GfxFont *font) { const char *lang; // find the language we want the font to support if (font->isCIDFont()) { const GooString *collection = ((GfxCIDFont *)font)->getCollection(); if (collection) { if (strcmp(collection->c_str(), "Adobe-GB1") == 0) { lang = "zh-cn"; // Simplified Chinese } else if (strcmp(collection->c_str(), "Adobe-CNS1") == 0) { lang = "zh-tw"; // Traditional Chinese } else if (strcmp(collection->c_str(), "Adobe-Japan1") == 0) { lang = "ja"; // Japanese } else if (strcmp(collection->c_str(), "Adobe-Japan2") == 0) { lang = "ja"; // Japanese } else if (strcmp(collection->c_str(), "Adobe-Korea1") == 0) { lang = "ko"; // Korean } else if (strcmp(collection->c_str(), "Adobe-UCS") == 0) { lang = "xx"; } else if (strcmp(collection->c_str(), "Adobe-Identity") == 0) { lang = "xx"; } else { 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 { lang = "xx"; } } else { lang = "xx"; } return lang; } static FcPattern *buildFcPattern(const GfxFont *font, const GooString *base14Name) { int weight = -1, slant = -1, width = -1, spacing = -1; FcPattern *p; // this is all heuristics will be overwritten if font had proper info std::string fontName; if (base14Name == nullptr) { fontName = font->getNameWithoutSubsetTag(); } else { fontName = base14Name->toStr(); } size_t modStart = fontName.find(','); if (modStart == std::string::npos) { modStart = fontName.find('-'); } // 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(), '-', ' '); size_t start = std::string::npos; findModifier(fontName, modStart, "Regular", start); findModifier(fontName, modStart, "Roman", start); if (findModifier(fontName, modStart, "Oblique", start)) { slant = FC_SLANT_OBLIQUE; } if (findModifier(fontName, modStart, "Italic", start)) { slant = FC_SLANT_ITALIC; } if (findModifier(fontName, modStart, "Bold", start)) { weight = FC_WEIGHT_BOLD; } if (findModifier(fontName, modStart, "Light", start)) { weight = FC_WEIGHT_LIGHT; } if (findModifier(fontName, modStart, "Medium", start)) { weight = FC_WEIGHT_MEDIUM; } if (findModifier(fontName, modStart, "Condensed", start)) { width = FC_WIDTH_CONDENSED; } std::string family; if (start == std::string::npos) { family = fontName; } else { // There have been "modifiers" in the name, crop them to obtain // the family name family = fontName.substr(0, modStart); } // use font flags if (font->isFixedWidth()) { spacing = FC_MONO; } if (font->isBold()) { weight = FC_WEIGHT_BOLD; } if (font->isItalic()) { slant = FC_SLANT_ITALIC; } // if the FontDescriptor specified a family name use it if (font->getFamily()) { family = font->getFamily()->toStr(); } // if the FontDescriptor specified a weight use it switch (font->getWeight()) { case GfxFont::W100: weight = FC_WEIGHT_EXTRALIGHT; break; case GfxFont::W200: weight = FC_WEIGHT_LIGHT; break; case GfxFont::W300: weight = FC_WEIGHT_BOOK; break; case GfxFont::W400: weight = FC_WEIGHT_NORMAL; break; case GfxFont::W500: weight = FC_WEIGHT_MEDIUM; break; case GfxFont::W600: weight = FC_WEIGHT_DEMIBOLD; break; case GfxFont::W700: weight = FC_WEIGHT_BOLD; break; case GfxFont::W800: weight = FC_WEIGHT_EXTRABOLD; break; case GfxFont::W900: weight = FC_WEIGHT_BLACK; break; default: break; } // if the FontDescriptor specified a width use it switch (font->getStretch()) { case GfxFont::UltraCondensed: width = FC_WIDTH_ULTRACONDENSED; break; case GfxFont::ExtraCondensed: width = FC_WIDTH_EXTRACONDENSED; break; case GfxFont::Condensed: width = FC_WIDTH_CONDENSED; break; case GfxFont::SemiCondensed: width = FC_WIDTH_SEMICONDENSED; break; case GfxFont::Normal: width = FC_WIDTH_NORMAL; break; case GfxFont::SemiExpanded: width = FC_WIDTH_SEMIEXPANDED; break; case GfxFont::Expanded: width = FC_WIDTH_EXPANDED; break; case GfxFont::ExtraExpanded: width = FC_WIDTH_EXTRAEXPANDED; break; case GfxFont::UltraExpanded: width = FC_WIDTH_ULTRAEXPANDED; break; default: break; } const char *lang = getFontLang(font); p = FcPatternBuild(nullptr, FC_FAMILY, FcTypeString, family.c_str(), FC_LANG, FcTypeString, lang, NULL); if (slant != -1) { FcPatternAddInteger(p, FC_SLANT, slant); } if (weight != -1) { FcPatternAddInteger(p, FC_WEIGHT, weight); } if (width != -1) { FcPatternAddInteger(p, FC_WIDTH, width); } if (spacing != -1) { FcPatternAddInteger(p, FC_SPACING, spacing); } return p; } #endif GooString *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 path; } #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")) { // for now we only support ttf, ttc, otf fonts return false; } const FoFiIdentifierType fontFoFiType = FoFiIdentifier::identifyFile(filepath); if (fontFoFiType != fofiIdTrueType && fontFoFiType != fofiIdTrueTypeCollection && fontFoFiType != fofiIdOpenTypeCFF8Bit && fontFoFiType != fofiIdOpenTypeCFFCID) { // for now we only support ttf, ttc, otf fonts return false; } const std::unique_ptr fft = FoFiTrueType::load(filepath, faceIndex); if (!fft) { error(errIO, -1, "Form::addFontToDefaultResources. Failed to FoFiTrueType::load {0:s}", filepath); return false; } // Look for the Unicode BMP cmaps, which are 0/3 or 3/1 int unicodeBMPCMap = fft->findCmap(0, 3); if (unicodeBMPCMap < 0) { unicodeBMPCMap = fft->findCmap(3, 1); } if (unicodeBMPCMap < 0) { // for now we only support files with unicode bmp cmaps return false; } const int glyph = fft->mapCodeToGID(unicodeBMPCMap, uChar); return glyph > 0; } #endif /* if you can't or don't want to use Fontconfig, you need to implement this function for your platform. For Windows, it's in GlobalParamsWin.cc */ #ifdef WITH_FONTCONFIGURATION_FONTCONFIG // not needed for fontconfig void GlobalParams::setupBaseFonts(const char *) { } GooString *GlobalParams::findBase14FontFile(const GooString *base14Name, const GfxFont *font, GooString *substituteFontName) { SysFontType type; int fontNum; return findSystemFontFile(font, &type, &fontNum, substituteFontName, base14Name); } GooString *GlobalParams::findSystemFontFile(const GfxFont *font, SysFontType *type, int *fontNum, GooString *substituteFontName, const GooString *base14Name) { const SysFontInfo *fi = nullptr; FcPattern *p = nullptr; GooString *path = nullptr; const std::optional &fontName = font->getName(); GooString substituteName; if (!fontName) { return nullptr; } globalParamsLocker(); if ((fi = sysFonts->find(*fontName, font->isFixedWidth(), true))) { path = fi->path->copy(); *type = fi->type; *fontNum = fi->fontNum; substituteName.Set(fi->substituteName->c_str()); } else { FcChar8 *s; char *ext; FcResult res; FcFontSet *set; int i; FcLangSet *lb = nullptr; p = buildFcPattern(font, base14Name); if (!p) { goto fin; } FcConfigSubstitute(nullptr, p, FcMatchPattern); FcDefaultSubstitute(p); set = FcFontSort(nullptr, p, FcFalse, nullptr, &res); if (!set) { goto fin; } // find the language we want the font to support const char *lang = getFontLang(font); if (strcmp(lang, "xx") != 0) { lb = FcLangSetCreate(); FcLangSetAdd(lb, (FcChar8 *)lang); } /* scan twice. first: fonts support the language second: all fonts (fall back) */ while (fi == nullptr) { for (i = 0; i < set->nfont; ++i) { res = FcPatternGetString(set->fonts[i], FC_FILE, 0, &s); if (res != FcResultMatch || !s) { continue; } if (lb != nullptr) { FcLangSet *l; res = FcPatternGetLangSet(set->fonts[i], FC_LANG, 0, &l); if (res != FcResultMatch || !FcLangSetContains(l, lb)) { continue; } } FcChar8 *s2; res = FcPatternGetString(set->fonts[i], FC_FULLNAME, 0, &s2); if (res == FcResultMatch && s2) { substituteName.Set((char *)s2); } else { // fontconfig does not extract fullname for some fonts // create the fullname from family and style res = FcPatternGetString(set->fonts[i], FC_FAMILY, 0, &s2); if (res == FcResultMatch && s2) { substituteName.Set((char *)s2); res = FcPatternGetString(set->fonts[i], FC_STYLE, 0, &s2); if (res == FcResultMatch && s2) { GooString *style = new GooString((char *)s2); if (style->cmp("Regular") != 0) { substituteName.append(" "); substituteName.append(style); } delete style; } } } ext = strrchr((char *)s, '.'); if (!ext) { continue; } if (!strncasecmp(ext, ".ttf", 4) || !strncasecmp(ext, ".ttc", 4) || !strncasecmp(ext, ".otf", 4)) { int weight, slant; bool bold = font->isBold(); bool italic = font->isItalic(); bool oblique = false; FcPatternGetInteger(set->fonts[i], FC_WEIGHT, 0, &weight); FcPatternGetInteger(set->fonts[i], FC_SLANT, 0, &slant); if (weight == FC_WEIGHT_DEMIBOLD || weight == FC_WEIGHT_BOLD || weight == FC_WEIGHT_EXTRABOLD || weight == FC_WEIGHT_BLACK) { bold = true; } if (slant == FC_SLANT_ITALIC) { italic = true; } if (slant == FC_SLANT_OBLIQUE) { oblique = true; } *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()); sysFonts->addFcFont(sfi); fi = sfi; path = new GooString((char *)s); } else if (!strncasecmp(ext, ".pfa", 4) || !strncasecmp(ext, ".pfb", 4)) { int weight, slant; bool bold = font->isBold(); bool italic = font->isItalic(); bool oblique = false; FcPatternGetInteger(set->fonts[i], FC_WEIGHT, 0, &weight); FcPatternGetInteger(set->fonts[i], FC_SLANT, 0, &slant); if (weight == FC_WEIGHT_DEMIBOLD || weight == FC_WEIGHT_BOLD || weight == FC_WEIGHT_EXTRABOLD || weight == FC_WEIGHT_BLACK) { bold = true; } if (slant == FC_SLANT_ITALIC) { italic = true; } if (slant == FC_SLANT_OBLIQUE) { oblique = true; } *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()); sysFonts->addFcFont(sfi); fi = sfi; path = new GooString((char *)s); } else { continue; } break; } if (lb != nullptr) { FcLangSetDestroy(lb); lb = nullptr; } else { /* scan all fonts of the list */ break; } } FcFontSetDestroy(set); } if (path == nullptr && (fi = sysFonts->find(*fontName, font->isFixedWidth(), false))) { path = fi->path->copy(); *type = fi->type; *fontNum = fi->fontNum; } if (substituteFontName) { substituteFontName->Set(substituteName.c_str()); } fin: if (p) { FcPatternDestroy(p); } return path; } FamilyStyleFontSearchResult GlobalParams::findSystemFontFileForFamilyAndStyle(const std::string &fontFamily, const std::string &fontStyle, const std::vector &filesToIgnore) { FcPattern *p = FcPatternBuild(nullptr, FC_FAMILY, FcTypeString, fontFamily.c_str(), FC_STYLE, FcTypeString, fontStyle.c_str(), nullptr); 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) { const std::unique_ptr fontSetDeleter(fontSet, [](FcFontSet *fSet) { FcFontSetDestroy(fSet); }); if (res == FcResultMatch) { for (int i = 0; i < fontSet->nfont; i++) { FcChar8 *fcFilePath = nullptr; int faceIndex = 0; FcPatternGetString(fontSet->fonts[i], FC_FILE, 0, &fcFilePath); 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()) { return FamilyStyleFontSearchResult(sFilePath, faceIndex); } } } } } error(errIO, -1, "Couldn't find font file for {0:s} {1:s}", fontFamily.c_str(), fontStyle.c_str()); return {}; } UCharFontSearchResult GlobalParams::findSystemFontFileForUChar(Unicode uChar, const GfxFont &fontToEmulate) { FcPattern *pattern = buildFcPattern(&fontToEmulate, nullptr); FcConfigSubstitute(nullptr, pattern, FcMatchPattern); FcDefaultSubstitute(pattern); FcResult result = FcResultMatch; FcFontSet *fontSet = FcFontSort(nullptr, pattern, FcFalse, nullptr, &result); FcPatternDestroy(pattern); if (fontSet) { const std::unique_ptr fontSetDeleter(fontSet, [](FcFontSet *fSet) { FcFontSetDestroy(fSet); }); for (int i = 0; i < fontSet->nfont; i++) { FcChar8 *fcFilePath = nullptr; 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); FcPatternGetCharSet(fontSet->fonts[i], FC_CHARSET, 0, &fcCharSet); if (!fcFilePath || !fcFamily || !fcStyle || !fcCharSet) { continue; } if (!FcCharSetHasChar(fcCharSet, uChar)) { continue; } const char *filepath = reinterpret_cast(fcFilePath); if (supportedFontForEmbedding(uChar, filepath, faceIndex)) { return UCharFontSearchResult(filepath, faceIndex, reinterpret_cast(fcFamily), reinterpret_cast(fcStyle)); } } } 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 GooString *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 GooString *GlobalParams::findSystemFontFile(const GfxFont *font, SysFontType *type, int *fontNum, GooString *substituteFontName, const GooString *base14Name) { GooString *path = nullptr; const std::optional &fontName = font->getName(); if (!fontName) { return nullptr; } 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) }; path = new GooString(AFont_getFontFilePath(afont.get())); // Set the type of font. Fonts returned by AFontMatcher are of // four possible types - ttf, otf, ttc, otc. if (path->endsWith(".ttf") || path->endsWith(".otf")) { *type = sysFontTTF; } else if (path->endsWith(".ttc") || path->endsWith(".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, GooString * /*substituteFontName*/) { return findFontFile(base14Name->toStr()); } #else 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 {}; } GooString *GlobalParams::findBase14FontFile(const GooString *base14Name, const GfxFont *font, GooString * /*substituteFontName*/) { return findFontFile(base14Name->toStr()); } static struct { const char *name; const char *t1FileName; const char *ttFileName; } displayFontTab[] = { { "Courier", "n022003l.pfb", "cour.ttf" }, { "Courier-Bold", "n022004l.pfb", "courbd.ttf" }, { "Courier-BoldOblique", "n022024l.pfb", "courbi.ttf" }, { "Courier-Oblique", "n022023l.pfb", "couri.ttf" }, { "Helvetica", "n019003l.pfb", "arial.ttf" }, { "Helvetica-Bold", "n019004l.pfb", "arialbd.ttf" }, { "Helvetica-BoldOblique", "n019024l.pfb", "arialbi.ttf" }, { "Helvetica-Oblique", "n019023l.pfb", "ariali.ttf" }, { "Symbol", "s050000l.pfb", nullptr }, { "Times-Bold", "n021004l.pfb", "timesbd.ttf" }, { "Times-BoldItalic", "n021024l.pfb", "timesbi.ttf" }, { "Times-Italic", "n021023l.pfb", "timesi.ttf" }, { "Times-Roman", "n021003l.pfb", "times.ttf" }, { "ZapfDingbats", "d050000l.pfb", nullptr }, { nullptr, nullptr, nullptr } }; static const char *displayFontDirs[] = { "/usr/share/ghostscript/fonts", "/usr/local/share/ghostscript/fonts", "/usr/share/fonts/default/Type1", "/usr/share/fonts/default/ghostscript", "/usr/share/fonts/type1/gsfonts", nullptr }; void GlobalParams::setupBaseFonts(const char *dir) { FILE *f; int i, j; 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].t1FileName)); if ((f = openFile(fileName->c_str(), "rb"))) { fclose(f); } else { fileName.reset(); } } for (j = 0; !fileName && displayFontDirs[j]; ++j) { fileName.reset(appendToPath(new GooString(displayFontDirs[j]), displayFontTab[i].t1FileName)); 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()); } } GooString *GlobalParams::findSystemFontFile(const GfxFont *font, SysFontType *type, int *fontNum, GooString * /*substituteFontName*/, const GooString * /*base14Name*/) { const SysFontInfo *fi; GooString *path; const std::optional &fontName = font->getName(); if (!fontName) { return nullptr; } path = nullptr; globalParamsLocker(); if ((fi = sysFonts->find(*fontName, font->isFixedWidth(), false))) { path = fi->path->copy(); *type = fi->type; *fontNum = fi->fontNum; } return path; } #endif std::string GlobalParams::getTextEncodingName() const { globalParamsLocker(); return textEncoding->toStr(); } const UnicodeMap *GlobalParams::getUtf8Map() { if (!utf8Map) { utf8Map = globalParams->getUnicodeMap("UTF-8"); } return utf8Map; } bool GlobalParams::getPrintCommands() { globalParamsLocker(); return printCommands; } bool GlobalParams::getProfileCommands() { globalParamsLocker(); return profileCommands; } bool GlobalParams::getErrQuiet() { // no locking -- this function may get called from inside a locked // section return errQuiet; } CharCodeToUnicode *GlobalParams::getCIDToUnicode(const GooString *collection) { CharCodeToUnicode *ctu; globalParamsLocker(); if (!(ctu = cidToUnicodeCache->getCharCodeToUnicode(collection))) { const auto cidToUnicode = cidToUnicodes.find(collection->toStr()); if (cidToUnicode != cidToUnicodes.end()) { if ((ctu = CharCodeToUnicode::parseCIDToUnicode(cidToUnicode->second.c_str(), collection))) { cidToUnicodeCache->add(ctu); } } } return ctu; } const UnicodeMap *GlobalParams::getUnicodeMap(const std::string &encodingName) { const UnicodeMap *map; if (!(map = getResidentUnicodeMap(encodingName))) { unicodeMapCacheLocker(); map = unicodeMapCache->getUnicodeMap(encodingName); } return map; } std::shared_ptr GlobalParams::getCMap(const GooString *collection, const GooString *cMapName) { cMapCacheLocker(); return cMapCache->getCMap(collection, cMapName); } const UnicodeMap *GlobalParams::getTextEncoding() { return getUnicodeMap(textEncoding->toStr()); } std::vector GlobalParams::getEncodingNames() { std::vector result; result.reserve(residentUnicodeMaps.size() + unicodeMaps.size()); for (const auto &unicodeMap : residentUnicodeMaps) { result.push_back(unicodeMap.first); } for (const auto &unicodeMap : unicodeMaps) { result.push_back(unicodeMap.first); } return result; } //------------------------------------------------------------------------ // functions to set parameters //------------------------------------------------------------------------ void GlobalParams::addFontFile(const std::string &fontName, const std::string &path) { globalParamsLocker(); fontFiles[fontName] = path; } void GlobalParams::setTextEncoding(const char *encodingName) { globalParamsLocker(); delete textEncoding; textEncoding = new GooString(encodingName); } void GlobalParams::setPrintCommands(bool printCommandsA) { globalParamsLocker(); printCommands = printCommandsA; } void GlobalParams::setProfileCommands(bool profileCommandsA) { globalParamsLocker(); profileCommands = profileCommandsA; } void GlobalParams::setErrQuiet(bool errQuietA) { globalParamsLocker(); 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); setErrorCallback(errorCallback); } count++; } GlobalParamsIniter::~GlobalParamsIniter() { const std::scoped_lock lock { mutex }; --count; if (count == 0) { globalParams.reset(); } } bool GlobalParamsIniter::setCustomDataDir(const std::string &dir) { const std::scoped_lock lock { mutex }; if (count == 0) { customDataDir = dir; return true; } return false; } std::mutex GlobalParamsIniter::mutex; int GlobalParamsIniter::count = 0; std::string GlobalParamsIniter::customDataDir; poppler-24.02.0/poppler/GlobalParams.h000066400000000000000000000212061455701731300175450ustar00rootroot00000000000000//======================================================================== // // GlobalParams.h // // Copyright 2001-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) 2005, 2007-2010, 2012, 2015, 2017-2023 Albert Astals Cid // Copyright (C) 2005 Jonathan Blandford // Copyright (C) 2006 Takashi Iwai // Copyright (C) 2006 Kristian Høgsberg // Copyright (C) 2007 Krzysztof Kowalczyk // Copyright (C) 2009 Jonathan Kew // Copyright (C) 2009 Petr Gajdos // Copyright (C) 2009, 2011, 2012, 2014, 2015 William Bader // Copyright (C) 2010 Hib Eris // Copyright (C) 2011 Pino Toscano // Copyright (C) 2012, 2017 Adrian Johnson // Copyright (C) 2012 Thomas Freitag // Copyright (C) 2013 Jason Crain // Copyright (C) 2018, 2020 Adam Reichold // Copyright (C) 2019 Oliver Sander // Copyright (C) 2023 Shivodit Gill // // 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 GLOBALPARAMS_H #define GLOBALPARAMS_H #include #include "poppler-config.h" #include "poppler_private_export.h" #include #include "CharTypes.h" #include "UnicodeMap.h" #include "Error.h" #include #include #include #include #include class GooString; class NameToCharCode; class CharCodeToUnicode; class CharCodeToUnicodeCache; class UnicodeMapCache; class CMap; class CMapCache; class GlobalParams; class GfxFont; class Stream; class SysFontList; //------------------------------------------------------------------------ // The global parameters object. extern std::unique_ptr POPPLER_PRIVATE_EXPORT globalParams; //------------------------------------------------------------------------ enum SysFontType { sysFontPFA, sysFontPFB, sysFontTTF, sysFontTTC }; //------------------------------------------------------------------------ struct FamilyStyleFontSearchResult { FamilyStyleFontSearchResult() = default; FamilyStyleFontSearchResult(const std::string &filepathA, int faceIndexA) : filepath(filepathA), faceIndex(faceIndexA) { } std::string filepath; int faceIndex = 0; }; //------------------------------------------------------------------------ struct UCharFontSearchResult { UCharFontSearchResult() = default; UCharFontSearchResult(const std::string &filepathA, int faceIndexA, const std::string &familyA, const std::string &styleA) : filepath(filepathA), faceIndex(faceIndexA), family(familyA), style(styleA) { } const std::string filepath; const int faceIndex = 0; const std::string family; const std::string style; }; //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT GlobalParams { public: // Initialize the global parameters explicit GlobalParams(const char *customPopplerDataDir = nullptr); ~GlobalParams(); GlobalParams(const GlobalParams &) = delete; GlobalParams &operator=(const GlobalParams &) = delete; void setupBaseFonts(const char *dir); //----- accessors CharCode getMacRomanCharCode(const char *charName); // Return Unicode values for character names. Used for general text // extraction. Unicode mapNameToUnicodeText(const char *charName); // Return Unicode values for character names. Used for glyph // lookups or text extraction with ZapfDingbats fonts. Unicode mapNameToUnicodeAll(const char *charName); UnicodeMap *getResidentUnicodeMap(const std::string &encodingName); FILE *getUnicodeMapFile(const std::string &encodingName); 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 *substituteFontName = nullptr); GooString *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; bool getPrintCommands(); bool getProfileCommands(); bool getErrQuiet(); CharCodeToUnicode *getCIDToUnicode(const GooString *collection); const UnicodeMap *getUnicodeMap(const std::string &encodingName); std::shared_ptr getCMap(const GooString *collection, const GooString *cMapName); const UnicodeMap *getTextEncoding(); const UnicodeMap *getUtf8Map(); std::vector getEncodingNames(); //----- functions to set parameters 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 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); //----- static tables NameToCharCode * // mapping from char name to macRomanReverseMap; // MacRomanEncoding index //----- user-modifiable settings NameToCharCode * // mapping from char name to Unicode for ZapfDingbats nameToUnicodeZapfDingbats; NameToCharCode * // mapping from char name to Unicode for text nameToUnicodeText; // extraction // files for mappings from char collections // to Unicode, indexed by collection name std::unordered_map cidToUnicodes; // mappings from Unicode to char codes, // indexed by encoding name std::unordered_map residentUnicodeMaps; // files for mappings from Unicode to char // codes, indexed by encoding name std::unordered_map unicodeMaps; // list of CMap dirs, indexed by collection std::unordered_multimap cMapDirs; std::vector toUnicodeDirs; // list of ToUnicode CMap dirs bool baseFontsInitialized; #ifdef _WIN32 // windows font substitutes (for CID fonts) std::unordered_map substFiles; #endif // font files: font name mapped to path std::unordered_map fontFiles; SysFontList *sysFonts; // system fonts GooString *textEncoding; // encoding (unicodeMap) to use for text // output bool printCommands; // print the drawing commands bool profileCommands; // profile the drawing commands bool errQuiet; // suppress error messages? CharCodeToUnicodeCache *cidToUnicodeCache; CharCodeToUnicodeCache *unicodeToUnicodeCache; UnicodeMapCache *unicodeMapCache; CMapCache *cMapCache; const UnicodeMap *utf8Map; mutable std::recursive_mutex mutex; mutable std::recursive_mutex unicodeMapCacheMutex; mutable std::recursive_mutex cMapCacheMutex; const char *popplerDataDir; }; class POPPLER_PRIVATE_EXPORT GlobalParamsIniter { public: explicit GlobalParamsIniter(ErrorCallback errorCallback); ~GlobalParamsIniter(); GlobalParamsIniter(const GlobalParamsIniter &) = delete; GlobalParamsIniter &operator=(const GlobalParamsIniter &) = delete; static bool setCustomDataDir(const std::string &dir); private: static std::mutex mutex; static int count; static std::string customDataDir; }; #endif poppler-24.02.0/poppler/GlobalParamsWin.cc000066400000000000000000000520441455701731300203650ustar00rootroot00000000000000/* Written by Krzysztof Kowalczyk (http://blog.kowalczyk.info) but mostly based on xpdf code. // 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 Mark Brand // Copyright (C) 2013, 2018, 2019 Adam Reichold // Copyright (C) 2013 Dmytro Morgun // Copyright (C) 2017 Christoph Cullmann // Copyright (C) 2017, 2018, 2020-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) 2019 Christian Persch // Copyright (C) 2019 Oliver Sander // Copyright (C) 2021 Stefan Löffler // Copyright (C) 2021 sunderme TODO: instead of a fixed mapping defined in displayFontTab, it could scan the whole fonts directory, parse TTF files and build font description for all fonts available in Windows. That's how MuPDF works. */ #ifndef PACKAGE_NAME # include #endif #include #include #include #include #include #include #include "goo/gmem.h" #include "goo/GooString.h" #include "goo/gfile.h" #include "Error.h" #include "NameToCharCode.h" #include "CharCodeToUnicode.h" #include "UnicodeMap.h" #include "CMap.h" #include "FontEncodingTables.h" #include "GlobalParams.h" #include "GfxFont.h" #include #include "Object.h" #include "Stream.h" #include "Lexer.h" #include "Parser.h" #define DEFAULT_SUBSTITUTE_FONT "Helvetica" #define DEFAULT_CID_FONT_AC1_MSWIN "MingLiU" /* Adobe-CNS1 for Taiwan, HongKong */ #define DEFAULT_CID_FONT_AG1_MSWIN "SimSun" /* Adobe-GB1 for PRC, Singapore */ #define DEFAULT_CID_FONT_AJ1_MSWIN "MS-Mincho" /* Adobe-Japan1 */ #define DEFAULT_CID_FONT_AJ2_MSWIN "MS-Mincho" /* Adobe-Japan2 (legacy) */ #define DEFAULT_CID_FONT_AK1_MSWIN "Batang" /* Adobe-Korea1 */ #define DEFAULT_CID_FONT_MSWIN "ArialUnicode" /* Unknown */ static const struct { const char *name; 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 }, { "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 }, // those seem to be frequently accessed by PDF files and I kind of guess // which font file do the refer to { "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", { "mingliu.ttf" }, false }, { "SimSun", { "simsun.ttf" }, false }, { "MS-Mincho", { "msmincho.ttf" }, false }, { "Batang", { "batang.ttf" }, false }, { "ArialUnicode", { "arialuni.ttf" }, true }, {} }; static std::string GetWindowsFontDir() { char winFontDir[MAX_PATH]; winFontDir[0] = '\0'; if (SHGetFolderPathA(nullptr, CSIDL_FONTS, nullptr, SHGFP_TYPE_CURRENT, winFontDir) == S_OK) { return winFontDir; } // return the windows directory + fonts GetWindowsDirectoryA(winFontDir, MAX_PATH); if (winFontDir[0]) { return std::string(winFontDir) + "\\fonts"; } return {}; } static bool FileExists(const char *path) { FILE *f = openFile(path, "rb"); if (f) { fclose(f); return true; } return false; } void SysFontList::scanWindowsFonts(const std::string &winFontDir) { OSVERSIONINFO version; const char *path; DWORD idx, valNameLen, dataLen, type; HKEY regKey; char valName[1024], data[1024]; int n, fontNum; char *p0, *p1; GooString *fontPath; version.dwOSVersionInfoSize = sizeof(version); GetVersionEx(&version); if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) { path = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts\\"; } else { path = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts\\"; } if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, path, 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, ®Key) == ERROR_SUCCESS) { idx = 0; while (1) { valNameLen = sizeof(valName) - 1; dataLen = sizeof(data) - 1; if (RegEnumValueA(regKey, idx, valName, &valNameLen, nullptr, &type, (LPBYTE)data, &dataLen) != ERROR_SUCCESS) { break; } if (type == REG_SZ && valNameLen > 0 && valNameLen < sizeof(valName) && dataLen > 0 && dataLen < sizeof(data)) { valName[valNameLen] = '\0'; data[dataLen] = '\0'; n = strlen(data); if (!strcasecmp(data + n - 4, ".ttf") || !strcasecmp(data + n - 4, ".ttc") || !strcasecmp(data + n - 4, ".otf")) { fontPath = new GooString(data); if (!(dataLen >= 3 && data[1] == ':' && data[2] == '\\')) { fontPath->insert(0, '\\'); fontPath->insert(0, winFontDir); fontPath->append('\0'); } p0 = valName; fontNum = 0; while (*p0) { p1 = strstr(p0, " & "); if (p1) { *p1 = '\0'; p1 = p1 + 3; } else { p1 = p0 + strlen(p0); } fonts.push_back(makeWindowsFont(p0, fontNum, fontPath->c_str())); p0 = p1; ++fontNum; } delete fontPath; } } ++idx; } RegCloseKey(regKey); } } SysFontInfo *SysFontList::makeWindowsFont(const char *name, int fontNum, const char *path) { int n; bool bold, italic, oblique, fixedWidth; GooString *s; char c; int i; SysFontType type; GooString substituteName; n = strlen(name); bold = italic = oblique = fixedWidth = false; // remove trailing ' (TrueType)' if (n > 11 && !strncmp(name + n - 11, " (TrueType)", 11)) { n -= 11; } // remove trailing ' (OpenType)' if (n > 11 && !strncmp(name + n - 11, " (OpenType)", 11)) { n -= 11; } // remove trailing ' Italic' if (n > 7 && !strncmp(name + n - 7, " Italic", 7)) { n -= 7; italic = true; } // remove trailing ' Oblique' if (n > 7 && !strncmp(name + n - 8, " Oblique", 8)) { n -= 8; oblique = true; } // remove trailing ' Bold' if (n > 5 && !strncmp(name + n - 5, " Bold", 5)) { n -= 5; bold = true; } // remove trailing ' Regular' if (n > 5 && !strncmp(name + n - 8, " Regular", 8)) { n -= 8; } // the familyname cannot indicate whether a font is fixedWidth or not. // some well-known fixedWidth typeface family names or keyword are checked. if (strstr(name, "Courier") || strstr(name, "Fixed") || (strstr(name, "Mono") && !strstr(name, "Monotype")) || strstr(name, "Typewriter")) fixedWidth = true; else fixedWidth = false; //----- normalize the font name s = new GooString(name, n); i = 0; while (i < s->getLength()) { c = s->getChar(i); if (c == ' ' || c == ',' || c == '-') { s->del(i); } else { ++i; } } if (!strcasecmp(path + strlen(path) - 4, ".ttc")) { type = sysFontTTC; } else { type = sysFontTTF; } return new SysFontInfo(s, bold, italic, oblique, fixedWidth, new GooString(path), type, fontNum, substituteName.copy()); } static GooString *replaceSuffix(GooString *path, const char *suffixA, const char *suffixB) { int suffLenA = strlen(suffixA); int suffLenB = strlen(suffixB); int baseLenA = path->getLength() - suffLenA; int baseLenB = path->getLength() - suffLenB; if (!strcasecmp(path->c_str() + baseLenA, suffixA)) { path->del(baseLenA, suffLenA)->append(suffixB); } else if (!strcasecmp(path->c_str() + baseLenB, suffixB)) { path->del(baseLenB, suffLenB)->append(suffixA); } return path; } void GlobalParams::setupBaseFonts(const char *dir) { 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; const GooString fontName = GooString(displayFontTab[i].name); 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; } } if (fontFound) { break; } } if (!fontFound && displayFontTab[i].warnIfMissing) { error(errSyntaxError, -1, "No display font for '{0:s}'", displayFontTab[i].name); } } if (!winFontDir.empty()) { sysFonts->scanWindowsFonts(winFontDir); } const char *dataRoot = popplerDataDir ? popplerDataDir : POPPLER_DATADIR; const std::string fileName = std::string(dataRoot).append("/cidfmap"); // try to open file const std::unique_ptr file = GooFile::open(fileName); if (file) { Parser *parser; parser = new Parser(nullptr, new FileStream(file.get(), 0, false, file->size(), Object(objNull)), true); Object obj1 = parser->getObj(); while (!obj1.isEOF()) { Object obj2 = parser->getObj(); if (obj1.isName()) { // Substitutions if (obj2.isDict()) { Object obj3 = obj2.getDict()->lookup("Path"); if (obj3.isString()) addFontFile(GooString(obj1.getName()).toStr(), obj3.getString()->toStr()); // Aliases } else if (obj2.isName()) { substFiles.emplace(obj1.getName(), obj2.getName()); } } obj1 = parser->getObj(); // skip trailing ';' while (obj1.isCmd(";")) { obj1 = parser->getObj(); } } delete parser; } } static const char *findSubstituteName(const GfxFont *font, const std::unordered_map &fontFiles, const std::unordered_map &substFiles, const char *origName) { assert(origName); if (!origName) return nullptr; GooString *name2 = new GooString(origName); int n = strlen(origName); // remove trailing "-Identity-H" if (n > 11 && !strcmp(name2->c_str() + n - 11, "-Identity-H")) { name2->del(n - 11, 11); n -= 11; } // remove trailing "-Identity-V" if (n > 11 && !strcmp(name2->c_str() + n - 11, "-Identity-V")) { name2->del(n - 11, 11); n -= 11; } const auto substFile = substFiles.find(name2->c_str()); if (substFile != substFiles.end()) { delete name2; return substFile->second.c_str(); } /* TODO: try to at least guess bold/italic/bolditalic from the name */ delete name2; if (font->isCIDFont()) { const GooString *collection = ((GfxCIDFont *)font)->getCollection(); const char *name3 = nullptr; if (!collection->cmp("Adobe-CNS1")) name3 = DEFAULT_CID_FONT_AC1_MSWIN; else if (!collection->cmp("Adobe-GB1")) name3 = DEFAULT_CID_FONT_AG1_MSWIN; else if (!collection->cmp("Adobe-Japan1")) name3 = DEFAULT_CID_FONT_AJ1_MSWIN; else if (!collection->cmp("Adobe-Japan2")) name3 = DEFAULT_CID_FONT_AJ2_MSWIN; else if (!collection->cmp("Adobe-Korea1")) name3 = DEFAULT_CID_FONT_AK1_MSWIN; if (name3 && fontFiles.count(name3) != 0) return name3; if (fontFiles.count(DEFAULT_CID_FONT_MSWIN) != 0) return DEFAULT_CID_FONT_MSWIN; } return DEFAULT_SUBSTITUTE_FONT; } /* Windows implementation of external font matching code */ GooString *GlobalParams::findSystemFontFile(const GfxFont *font, SysFontType *type, int *fontNum, GooString *substituteFontName, const GooString *base14Name) { const SysFontInfo *fi; GooString *path = nullptr; const std::optional &fontName = font->getName(); if (!fontName) return nullptr; const std::scoped_lock locker(mutex); setupBaseFonts(POPPLER_FONTSDIR); // TODO: base14Name should be changed? // In the system using FontConfig, findSystemFontFile() uses // base14Name only for the creation of query pattern. if ((fi = sysFonts->find(*fontName, false, false))) { path = fi->path->copy(); *type = fi->type; *fontNum = fi->fontNum; if (substituteFontName) substituteFontName->Set(fi->substituteName->c_str()); } else { GooString *substFontName = new GooString(findSubstituteName(font, fontFiles, substFiles, fontName->c_str())); 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()); if (substituteFontName) substituteFontName->Set(path->c_str()); if (!strcasecmp(path->c_str() + path->getLength() - 4, ".ttc")) { *type = sysFontTTC; } else { *type = sysFontTTF; } *fontNum = 0; } } return path; } FamilyStyleFontSearchResult GlobalParams::findSystemFontFileForFamilyAndStyle(const std::string &fontFamily, const std::string &fontStyle, const std::vector &filesToIgnore) { const std::scoped_lock locker(mutex); setupBaseFonts(POPPLER_FONTSDIR); const std::string familyAndStyle = fontFamily + " " + fontStyle; const SysFontInfo *fi = sysFonts->find(familyAndStyle, false, false, filesToIgnore); if (fi) { return FamilyStyleFontSearchResult(fi->path->toStr(), fi->fontNum); } return {}; } UCharFontSearchResult GlobalParams::findSystemFontFileForUChar(Unicode uChar, const GfxFont &fontToEmulate) { const std::scoped_lock locker(mutex); setupBaseFonts(POPPLER_FONTSDIR); const std::vector &fonts = sysFonts->getFonts(); for (SysFontInfo *f : fonts) { // This is not super great given that it ignores fontToEmulate, but will do for now if (supportedFontForEmbedding(uChar, f->path->c_str(), f->fontNum)) { std::string style; if (f->italic) { style = "Italic"; } if (f->oblique) { if (!style.empty()) { style += " "; } style += "Oblique"; } if (f->bold) { if (!style.empty()) { style += " "; } style += "Bold"; } return UCharFontSearchResult(f->path->toStr(), f->fontNum, f->name->toStr(), style); } } return {}; } poppler-24.02.0/poppler/HashAlgorithm.h000066400000000000000000000007741455701731300177420ustar00rootroot00000000000000//======================================================================== // // 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 poppler-24.02.0/poppler/HelveticaBoldObliqueWidths.gperf000066400000000000000000000101571455701731300232710ustar00rootroot00000000000000%{ #include #include "BuiltinFontWidth.h" %} %language=ANSI-C %define initializer-suffix ,0 %define lookup-function-name HelveticaBoldObliqueWidthsLookup %struct-type %omit-struct-type %readonly-tables struct BuiltinFontWidth %% #### Ntilde, 722 rcaron, 389 kcommaaccent, 556 Ncommaaccent, 722 Zacute, 611 comma, 278 cedilla, 333 plusminus, 584 circumflex, 333 dotaccent, 333 edotaccent, 556 asciitilde, 584 colon, 333 onehalf, 834 dollar, 556 Lcaron, 611 ntilde, 611 Aogonek, 722 ncommaaccent, 611 minus, 584 Iogonek, 278 zacute, 500 yen, 556 space, 278 Omacron, 778 questiondown, 611 emdash, 1000 Agrave, 722 three, 556 numbersign, 556 lcaron, 400 A, 722 B, 722 C, 722 aogonek, 556 D, 722 E, 667 onequarter, 834 F, 611 G, 778 H, 722 I, 278 J, 556 K, 722 iogonek, 278 backslash, 278 L, 611 periodcentered, 278 M, 833 N, 722 omacron, 611 Tcommaaccent, 611 O, 778 P, 667 Q, 778 Uhungarumlaut, 722 R, 722 Aacute, 722 caron, 333 S, 667 T, 611 U, 722 agrave, 556 V, 667 W, 944 X, 667 question, 611 equal, 584 Y, 667 Z, 611 four, 556 a, 556 Gcommaaccent, 778 b, 611 c, 556 d, 611 e, 556 f, 333 g, 611 bullet, 350 h, 611 i, 278 Oslash, 778 dagger, 556 j, 278 k, 556 l, 278 m, 889 n, 611 tcommaaccent, 333 o, 611 ordfeminine, 370 ring, 333 p, 611 q, 611 uhungarumlaut, 611 r, 389 twosuperior, 333 aacute, 556 s, 556 OE, 1000 t, 333 divide, 584 u, 611 Ccaron, 722 v, 556 w, 778 x, 556 y, 556 z, 500 Gbreve, 778 commaaccent, 250 hungarumlaut, 333 Idotaccent, 278 Nacute, 722 quotedbl, 474 gcommaaccent, 611 mu, 611 greaterequal, 549 Scaron, 667 Lslash, 611 semicolon, 333 oslash, 611 lessequal, 549 lozenge, 494 parenright, 333 ccaron, 556 Ecircumflex, 667 gbreve, 611 trademark, 1000 daggerdbl, 556 nacute, 611 macron, 333 Otilde, 778 Emacron, 667 ellipsis, 1000 scaron, 556 AE, 1000 Ucircumflex, 722 lslash, 278 quotedblleft, 500 guilsinglright, 333 hyphen, 333 quotesingle, 238 eight, 556 exclamdown, 333 endash, 556 oe, 944 Abreve, 722 Umacron, 722 ecircumflex, 556 Adieresis, 722 copyright, 737 Egrave, 667 slash, 278 Edieresis, 667 otilde, 611 Idieresis, 278 parenleft, 333 one, 556 emacron, 556 Odieresis, 778 ucircumflex, 611 bracketleft, 333 Ugrave, 722 quoteright, 278 Udieresis, 722 perthousand, 1000 Ydieresis, 667 umacron, 611 abreve, 556 Eacute, 667 adieresis, 556 egrave, 556 edieresis, 556 idieresis, 278 Eth, 722 ae, 889 asterisk, 389 odieresis, 611 Uacute, 722 ugrave, 611 nine, 556 five, 556 udieresis, 611 Zcaron, 611 Scommaaccent, 667 threequarters, 834 guillemotright, 556 Ccedilla, 722 ydieresis, 556 tilde, 333 at, 975 eacute, 556 underscore, 556 Euro, 556 Dcroat, 722 multiply, 584 zero, 556 eth, 611 Scedilla, 667 Ograve, 778 Racute, 722 partialdiff, 494 uacute, 611 braceleft, 389 Thorn, 667 zcaron, 500 scommaaccent, 556 ccedilla, 556 Dcaron, 722 dcroat, 611 Ocircumflex, 778 Oacute, 778 scedilla, 556 ogonek, 333 ograve, 611 racute, 389 Tcaron, 611 Eogonek, 667 thorn, 611 degree, 400 registered, 737 radical, 549 Aring, 722 percent, 889 six, 556 paragraph, 556 dcaron, 743 Uogonek, 722 two, 556 summation, 600 Igrave, 278 Lacute, 611 ocircumflex, 611 oacute, 611 Uring, 722 Lcommaaccent, 611 tcaron, 389 eogonek, 556 Delta, 612 Ohungarumlaut, 778 asciicircum, 584 aring, 556 grave, 333 uogonek, 611 bracketright, 333 Iacute, 278 ampersand, 722 igrave, 278 lacute, 278 Ncaron, 722 plus, 584 uring, 611 quotesinglbase, 278 lcommaaccent, 278 Yacute, 667 ohungarumlaut, 611 threesuperior, 333 acute, 333 section, 556 dieresis, 333 iacute, 278 quotedblbase, 500 ncaron, 611 florin, 556 yacute, 556 Rcommaaccent, 722 fi, 611 fl, 611 Acircumflex, 722 Cacute, 722 Icircumflex, 278 guillemotleft, 556 germandbls, 611 Amacron, 722 seven, 556 Sacute, 667 ordmasculine, 365 dotlessi, 278 sterling, 556 notequal, 549 Imacron, 278 rcommaaccent, 389 Zdotaccent, 611 acircumflex, 556 cacute, 556 Ecaron, 667 icircumflex, 278 braceright, 389 quotedblright, 500 amacron, 556 sacute, 556 imacron, 278 cent, 556 currency, 556 logicalnot, 584 zdotaccent, 500 Atilde, 722 breve, 333 bar, 280 fraction, 167 less, 584 ecaron, 556 guilsinglleft, 333 exclam, 333 period, 278 Rcaron, 722 Kcommaaccent, 722 greater, 584 atilde, 556 brokenbar, 280 quoteleft, 278 Edotaccent, 667 onesuperior, 333 #### %% poppler-24.02.0/poppler/HelveticaBoldObliqueWidths.pregenerated.c000066400000000000000000002701521455701731300250570ustar00rootroot00000000000000/* ANSI-C code produced by gperf version 3.1 */ /* Command-line: gperf poppler/HelveticaBoldObliqueWidths.gperf */ /* Computed positions: -k'1-2,5,$' */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) && ('-' == 45) && ('.' == 46) && ('/' == 47) \ && ('0' == 48) && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) && ('=' == 61) && ('>' == 62) \ && ('?' == 63) && ('A' == 65) && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) && ('N' == 78) \ && ('O' == 79) && ('P' == 80) && ('Q' == 81) && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) && ('k' == 107) && ('l' == 108) \ && ('m' == 109) && ('n' == 110) && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) /* The character set is not based on ISO-646. */ # error "gperf generated tables don't work with this execution character set. Please report a bug to ." #endif #line 1 "poppler/HelveticaBoldObliqueWidths.gperf" #include #include "BuiltinFontWidth.h" #define TOTAL_KEYWORDS 315 #define MIN_WORD_LENGTH 1 #define MAX_WORD_LENGTH 14 #define MIN_HASH_VALUE 1 #define MAX_HASH_VALUE 1041 /* maximum key range = 1041, duplicates = 0 */ #ifdef __GNUC__ __inline #else # ifdef __cplusplus inline # endif #endif static unsigned int hash(register const char *str, register size_t len) { static const unsigned short asso_values[] = { 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 270, 415, 28, 8, 150, 390, 290, 375, 370, 335, 5, 455, 330, 405, 355, 325, 310, 3, 320, 160, 240, 225, 145, 70, 410, 460, 1042, 1042, 1042, 1042, 1042, 1042, 20, 345, 30, 115, 0, 395, 140, 165, 135, 35, 380, 170, 130, 15, 45, 215, 260, 100, 65, 10, 155, 400, 300, 305, 280, 315, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042 }; register unsigned int hval = len; switch (hval) { default: hval += asso_values[(unsigned char)str[4]]; /*FALLTHROUGH*/ case 4: case 3: case 2: hval += asso_values[(unsigned char)str[1]]; /*FALLTHROUGH*/ case 1: hval += asso_values[(unsigned char)str[0]]; break; } return hval + asso_values[(unsigned char)str[len - 1]]; } const struct BuiltinFontWidth *HelveticaBoldObliqueWidthsLookup(register const char *str, register size_t len) { static const struct BuiltinFontWidth wordlist[] = { { "", 0 }, #line 90 "poppler/HelveticaBoldObliqueWidths.gperf" { "e", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 70 "poppler/HelveticaBoldObliqueWidths.gperf" { "R", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 57 "poppler/HelveticaBoldObliqueWidths.gperf" { "K", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 49 "poppler/HelveticaBoldObliqueWidths.gperf" { "D", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 115 "poppler/HelveticaBoldObliqueWidths.gperf" { "t", 333 }, #line 191 "poppler/HelveticaBoldObliqueWidths.gperf" { "ae", 889 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 102 "poppler/HelveticaBoldObliqueWidths.gperf" { "n", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 207 "poppler/HelveticaBoldObliqueWidths.gperf" { "eacute", 556 }, { "", 0 }, { "", 0 }, #line 216 "poppler/HelveticaBoldObliqueWidths.gperf" { "Racute", 722 }, { "", 0 }, #line 85 "poppler/HelveticaBoldObliqueWidths.gperf" { "a", 556 }, #line 206 "poppler/HelveticaBoldObliqueWidths.gperf" { "at", 975 }, { "", 0 }, #line 308 "poppler/HelveticaBoldObliqueWidths.gperf" { "cent", 556 }, { "", 0 }, { "", 0 }, #line 161 "poppler/HelveticaBoldObliqueWidths.gperf" { "oe", 944 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 145 "poppler/HelveticaBoldObliqueWidths.gperf" { "nacute", 611 }, { "", 0 }, #line 254 "poppler/HelveticaBoldObliqueWidths.gperf" { "Delta", 612 }, { "", 0 }, #line 273 "poppler/HelveticaBoldObliqueWidths.gperf" { "acute", 333 }, #line 112 "poppler/HelveticaBoldObliqueWidths.gperf" { "aacute", 556 }, #line 47 "poppler/HelveticaBoldObliqueWidths.gperf" { "C", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 88 "poppler/HelveticaBoldObliqueWidths.gperf" { "c", 556 }, { "", 0 }, #line 173 "poppler/HelveticaBoldObliqueWidths.gperf" { "one", 556 }, #line 285 "poppler/HelveticaBoldObliqueWidths.gperf" { "Cacute", 722 }, { "", 0 }, #line 300 "poppler/HelveticaBoldObliqueWidths.gperf" { "cacute", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 98 "poppler/HelveticaBoldObliqueWidths.gperf" { "j", 278 }, { "", 0 }, { "", 0 }, #line 210 "poppler/HelveticaBoldObliqueWidths.gperf" { "Dcroat", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 249 "poppler/HelveticaBoldObliqueWidths.gperf" { "oacute", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 72 "poppler/HelveticaBoldObliqueWidths.gperf" { "caron", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 104 "poppler/HelveticaBoldObliqueWidths.gperf" { "o", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 317 "poppler/HelveticaBoldObliqueWidths.gperf" { "ecaron", 556 }, { "", 0 }, { "", 0 }, #line 321 "poppler/HelveticaBoldObliqueWidths.gperf" { "Rcaron", 722 }, #line 290 "poppler/HelveticaBoldObliqueWidths.gperf" { "seven", 556 }, #line 306 "poppler/HelveticaBoldObliqueWidths.gperf" { "sacute", 556 }, { "", 0 }, { "", 0 }, #line 224 "poppler/HelveticaBoldObliqueWidths.gperf" { "Dcaron", 722 }, { "", 0 }, #line 252 "poppler/HelveticaBoldObliqueWidths.gperf" { "tcaron", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 26 "poppler/HelveticaBoldObliqueWidths.gperf" { "colon", 333 }, #line 278 "poppler/HelveticaBoldObliqueWidths.gperf" { "ncaron", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 125 "poppler/HelveticaBoldObliqueWidths.gperf" { "commaaccent", 250 }, { "", 0 }, { "", 0 }, #line 135 "poppler/HelveticaBoldObliqueWidths.gperf" { "semicolon", 333 }, #line 19 "poppler/HelveticaBoldObliqueWidths.gperf" { "comma", 278 }, #line 235 "poppler/HelveticaBoldObliqueWidths.gperf" { "degree", 400 }, { "", 0 }, { "", 0 }, #line 118 "poppler/HelveticaBoldObliqueWidths.gperf" { "Ccaron", 722 }, { "", 0 }, #line 140 "poppler/HelveticaBoldObliqueWidths.gperf" { "ccaron", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 113 "poppler/HelveticaBoldObliqueWidths.gperf" { "s", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 231 "poppler/HelveticaBoldObliqueWidths.gperf" { "racute", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 79 "poppler/HelveticaBoldObliqueWidths.gperf" { "X", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 30 "poppler/HelveticaBoldObliqueWidths.gperf" { "ntilde", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 205 "poppler/HelveticaBoldObliqueWidths.gperf" { "tilde", 333 }, #line 324 "poppler/HelveticaBoldObliqueWidths.gperf" { "atilde", 556 }, { "", 0 }, { "", 0 }, #line 196 "poppler/HelveticaBoldObliqueWidths.gperf" { "nine", 556 }, #line 24 "poppler/HelveticaBoldObliqueWidths.gperf" { "edotaccent", 556 }, #line 105 "poppler/HelveticaBoldObliqueWidths.gperf" { "ordfeminine", 370 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 158 "poppler/HelveticaBoldObliqueWidths.gperf" { "eight", 556 }, #line 150 "poppler/HelveticaBoldObliqueWidths.gperf" { "scaron", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 276 "poppler/HelveticaBoldObliqueWidths.gperf" { "iacute", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 170 "poppler/HelveticaBoldObliqueWidths.gperf" { "otilde", 611 }, #line 292 "poppler/HelveticaBoldObliqueWidths.gperf" { "ordmasculine", 365 }, #line 213 "poppler/HelveticaBoldObliqueWidths.gperf" { "eth", 611 }, { "", 0 }, #line 42 "poppler/HelveticaBoldObliqueWidths.gperf" { "three", 556 }, #line 225 "poppler/HelveticaBoldObliqueWidths.gperf" { "dcroat", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 281 "poppler/HelveticaBoldObliqueWidths.gperf" { "Rcommaaccent", 722 }, #line 185 "poppler/HelveticaBoldObliqueWidths.gperf" { "Eacute", 667 }, #line 322 "poppler/HelveticaBoldObliqueWidths.gperf" { "Kcommaaccent", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 218 "poppler/HelveticaBoldObliqueWidths.gperf" { "uacute", 611 }, #line 103 "poppler/HelveticaBoldObliqueWidths.gperf" { "tcommaaccent", 333 }, { "", 0 }, #line 166 "poppler/HelveticaBoldObliqueWidths.gperf" { "copyright", 737 }, #line 43 "poppler/HelveticaBoldObliqueWidths.gperf" { "numbersign", 556 }, #line 15 "poppler/HelveticaBoldObliqueWidths.gperf" { "rcaron", 389 }, #line 32 "poppler/HelveticaBoldObliqueWidths.gperf" { "ncommaaccent", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 110 "poppler/HelveticaBoldObliqueWidths.gperf" { "r", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 264 "poppler/HelveticaBoldObliqueWidths.gperf" { "lacute", 278 }, { "", 0 }, { "", 0 }, #line 23 "poppler/HelveticaBoldObliqueWidths.gperf" { "dotaccent", 333 }, #line 234 "poppler/HelveticaBoldObliqueWidths.gperf" { "thorn", 611 }, #line 242 "poppler/HelveticaBoldObliqueWidths.gperf" { "dcaron", 743 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 146 "poppler/HelveticaBoldObliqueWidths.gperf" { "macron", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 203 "poppler/HelveticaBoldObliqueWidths.gperf" { "Ccedilla", 722 }, #line 274 "poppler/HelveticaBoldObliqueWidths.gperf" { "section", 556 }, #line 223 "poppler/HelveticaBoldObliqueWidths.gperf" { "ccedilla", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 20 "poppler/HelveticaBoldObliqueWidths.gperf" { "cedilla", 333 }, { "", 0 }, { "", 0 }, #line 25 "poppler/HelveticaBoldObliqueWidths.gperf" { "asciitilde", 584 }, #line 89 "poppler/HelveticaBoldObliqueWidths.gperf" { "d", 611 }, #line 239 "poppler/HelveticaBoldObliqueWidths.gperf" { "percent", 889 }, { "", 0 }, { "", 0 }, #line 288 "poppler/HelveticaBoldObliqueWidths.gperf" { "germandbls", 611 }, { "", 0 }, #line 138 "poppler/HelveticaBoldObliqueWidths.gperf" { "lozenge", 494 }, { "", 0 }, #line 316 "poppler/HelveticaBoldObliqueWidths.gperf" { "less", 584 }, { "", 0 }, #line 97 "poppler/HelveticaBoldObliqueWidths.gperf" { "dagger", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 258 "poppler/HelveticaBoldObliqueWidths.gperf" { "grave", 333 }, #line 301 "poppler/HelveticaBoldObliqueWidths.gperf" { "Ecaron", 667 }, #line 222 "poppler/HelveticaBoldObliqueWidths.gperf" { "scommaaccent", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 160 "poppler/HelveticaBoldObliqueWidths.gperf" { "endash", 556 }, #line 174 "poppler/HelveticaBoldObliqueWidths.gperf" { "emacron", 556 }, #line 201 "poppler/HelveticaBoldObliqueWidths.gperf" { "threequarters", 834 }, { "", 0 }, { "", 0 }, #line 232 "poppler/HelveticaBoldObliqueWidths.gperf" { "Tcaron", 611 }, { "", 0 }, #line 228 "poppler/HelveticaBoldObliqueWidths.gperf" { "scedilla", 556 }, { "", 0 }, { "", 0 }, #line 101 "poppler/HelveticaBoldObliqueWidths.gperf" { "m", 889 }, { "", 0 }, { "", 0 }, #line 245 "poppler/HelveticaBoldObliqueWidths.gperf" { "summation", 600 }, #line 310 "poppler/HelveticaBoldObliqueWidths.gperf" { "logicalnot", 584 }, #line 44 "poppler/HelveticaBoldObliqueWidths.gperf" { "lcaron", 400 }, { "", 0 }, { "", 0 }, #line 172 "poppler/HelveticaBoldObliqueWidths.gperf" { "parenleft", 333 }, #line 139 "poppler/HelveticaBoldObliqueWidths.gperf" { "parenright", 333 }, #line 95 "poppler/HelveticaBoldObliqueWidths.gperf" { "i", 278 }, #line 305 "poppler/HelveticaBoldObliqueWidths.gperf" { "amacron", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 194 "poppler/HelveticaBoldObliqueWidths.gperf" { "Uacute", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 208 "poppler/HelveticaBoldObliqueWidths.gperf" { "underscore", 556 }, #line 92 "poppler/HelveticaBoldObliqueWidths.gperf" { "g", 611 }, #line 297 "poppler/HelveticaBoldObliqueWidths.gperf" { "rcommaaccent", 389 }, { "", 0 }, { "", 0 }, #line 37 "poppler/HelveticaBoldObliqueWidths.gperf" { "space", 278 }, #line 28 "poppler/HelveticaBoldObliqueWidths.gperf" { "dollar", 556 }, { "", 0 }, #line 272 "poppler/HelveticaBoldObliqueWidths.gperf" { "threesuperior", 333 }, #line 188 "poppler/HelveticaBoldObliqueWidths.gperf" { "edieresis", 556 }, #line 236 "poppler/HelveticaBoldObliqueWidths.gperf" { "registered", 737 }, #line 78 "poppler/HelveticaBoldObliqueWidths.gperf" { "W", 944 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 64 "poppler/HelveticaBoldObliqueWidths.gperf" { "omacron", 611 }, #line 36 "poppler/HelveticaBoldObliqueWidths.gperf" { "yen", 556 }, { "", 0 }, { "", 0 }, #line 50 "poppler/HelveticaBoldObliqueWidths.gperf" { "E", 667 }, { "", 0 }, #line 293 "poppler/HelveticaBoldObliqueWidths.gperf" { "dotlessi", 278 }, { "", 0 }, #line 327 "poppler/HelveticaBoldObliqueWidths.gperf" { "Edotaccent", 667 }, #line 71 "poppler/HelveticaBoldObliqueWidths.gperf" { "Aacute", 722 }, { "", 0 }, { "", 0 }, #line 186 "poppler/HelveticaBoldObliqueWidths.gperf" { "adieresis", 556 }, { "", 0 }, #line 117 "poppler/HelveticaBoldObliqueWidths.gperf" { "u", 611 }, { "", 0 }, { "", 0 }, #line 144 "poppler/HelveticaBoldObliqueWidths.gperf" { "daggerdbl", 556 }, { "", 0 }, #line 280 "poppler/HelveticaBoldObliqueWidths.gperf" { "yacute", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 74 "poppler/HelveticaBoldObliqueWidths.gperf" { "T", 611 }, #line 130 "poppler/HelveticaBoldObliqueWidths.gperf" { "gcommaaccent", 611 }, #line 275 "poppler/HelveticaBoldObliqueWidths.gperf" { "dieresis", 333 }, { "", 0 }, #line 51 "poppler/HelveticaBoldObliqueWidths.gperf" { "onequarter", 834 }, #line 328 "poppler/HelveticaBoldObliqueWidths.gperf" { "onesuperior", 333 }, #line 237 "poppler/HelveticaBoldObliqueWidths.gperf" { "radical", 549 }, #line 190 "poppler/HelveticaBoldObliqueWidths.gperf" { "Eth", 722 }, { "", 0 }, { "", 0 }, #line 94 "poppler/HelveticaBoldObliqueWidths.gperf" { "h", 611 }, { "", 0 }, { "", 0 }, #line 193 "poppler/HelveticaBoldObliqueWidths.gperf" { "odieresis", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 100 "poppler/HelveticaBoldObliqueWidths.gperf" { "l", 278 }, #line 65 "poppler/HelveticaBoldObliqueWidths.gperf" { "Tcommaaccent", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 136 "poppler/HelveticaBoldObliqueWidths.gperf" { "oslash", 611 }, { "", 0 }, { "", 0 }, #line 137 "poppler/HelveticaBoldObliqueWidths.gperf" { "lessequal", 549 }, #line 159 "poppler/HelveticaBoldObliqueWidths.gperf" { "exclamdown", 333 }, #line 35 "poppler/HelveticaBoldObliqueWidths.gperf" { "zacute", 500 }, #line 269 "poppler/HelveticaBoldObliqueWidths.gperf" { "lcommaaccent", 278 }, { "", 0 }, #line 209 "poppler/HelveticaBoldObliqueWidths.gperf" { "Euro", 556 }, { "", 0 }, #line 291 "poppler/HelveticaBoldObliqueWidths.gperf" { "Sacute", 667 }, #line 323 "poppler/HelveticaBoldObliqueWidths.gperf" { "greater", 584 }, #line 244 "poppler/HelveticaBoldObliqueWidths.gperf" { "two", 556 }, { "", 0 }, #line 220 "poppler/HelveticaBoldObliqueWidths.gperf" { "Thorn", 667 }, #line 256 "poppler/HelveticaBoldObliqueWidths.gperf" { "asciicircum", 584 }, #line 126 "poppler/HelveticaBoldObliqueWidths.gperf" { "hungarumlaut", 333 }, { "", 0 }, #line 212 "poppler/HelveticaBoldObliqueWidths.gperf" { "zero", 556 }, { "", 0 }, #line 40 "poppler/HelveticaBoldObliqueWidths.gperf" { "emdash", 1000 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 116 "poppler/HelveticaBoldObliqueWidths.gperf" { "divide", 584 }, { "", 0 }, #line 271 "poppler/HelveticaBoldObliqueWidths.gperf" { "ohungarumlaut", 611 }, #line 262 "poppler/HelveticaBoldObliqueWidths.gperf" { "ampersand", 722 }, { "", 0 }, #line 164 "poppler/HelveticaBoldObliqueWidths.gperf" { "ecircumflex", 556 }, { "", 0 }, { "", 0 }, #line 106 "poppler/HelveticaBoldObliqueWidths.gperf" { "ring", 333 }, { "", 0 }, #line 320 "poppler/HelveticaBoldObliqueWidths.gperf" { "period", 278 }, { "", 0 }, #line 318 "poppler/HelveticaBoldObliqueWidths.gperf" { "guilsinglleft", 333 }, #line 155 "poppler/HelveticaBoldObliqueWidths.gperf" { "guilsinglright", 333 }, { "", 0 }, { "", 0 }, #line 307 "poppler/HelveticaBoldObliqueWidths.gperf" { "imacron", 278 }, { "", 0 }, #line 61 "poppler/HelveticaBoldObliqueWidths.gperf" { "periodcentered", 278 }, { "", 0 }, #line 227 "poppler/HelveticaBoldObliqueWidths.gperf" { "Oacute", 778 }, { "", 0 }, #line 294 "poppler/HelveticaBoldObliqueWidths.gperf" { "sterling", 556 }, { "", 0 }, { "", 0 }, #line 299 "poppler/HelveticaBoldObliqueWidths.gperf" { "acircumflex", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 33 "poppler/HelveticaBoldObliqueWidths.gperf" { "minus", 584 }, #line 312 "poppler/HelveticaBoldObliqueWidths.gperf" { "Atilde", 722 }, #line 148 "poppler/HelveticaBoldObliqueWidths.gperf" { "Emacron", 667 }, { "", 0 }, { "", 0 }, #line 257 "poppler/HelveticaBoldObliqueWidths.gperf" { "aring", 556 }, #line 261 "poppler/HelveticaBoldObliqueWidths.gperf" { "Iacute", 278 }, #line 183 "poppler/HelveticaBoldObliqueWidths.gperf" { "umacron", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 221 "poppler/HelveticaBoldObliqueWidths.gperf" { "zcaron", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 133 "poppler/HelveticaBoldObliqueWidths.gperf" { "Scaron", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 248 "poppler/HelveticaBoldObliqueWidths.gperf" { "ocircumflex", 611 }, { "", 0 }, { "", 0 }, #line 189 "poppler/HelveticaBoldObliqueWidths.gperf" { "idieresis", 278 }, { "", 0 }, #line 157 "poppler/HelveticaBoldObliqueWidths.gperf" { "quotesingle", 238 }, #line 277 "poppler/HelveticaBoldObliqueWidths.gperf" { "quotedblbase", 500 }, { "", 0 }, #line 268 "poppler/HelveticaBoldObliqueWidths.gperf" { "quotesinglbase", 278 }, { "", 0 }, #line 107 "poppler/HelveticaBoldObliqueWidths.gperf" { "p", 611 }, #line 132 "poppler/HelveticaBoldObliqueWidths.gperf" { "greaterequal", 549 }, { "", 0 }, #line 326 "poppler/HelveticaBoldObliqueWidths.gperf" { "quoteleft", 278 }, #line 179 "poppler/HelveticaBoldObliqueWidths.gperf" { "quoteright", 278 }, { "", 0 }, #line 154 "poppler/HelveticaBoldObliqueWidths.gperf" { "quotedblleft", 500 }, #line 304 "poppler/HelveticaBoldObliqueWidths.gperf" { "quotedblright", 500 }, #line 169 "poppler/HelveticaBoldObliqueWidths.gperf" { "Edieresis", 667 }, { "", 0 }, #line 128 "poppler/HelveticaBoldObliqueWidths.gperf" { "Nacute", 722 }, #line 131 "poppler/HelveticaBoldObliqueWidths.gperf" { "mu", 611 }, { "", 0 }, #line 198 "poppler/HelveticaBoldObliqueWidths.gperf" { "udieresis", 611 }, { "", 0 }, #line 270 "poppler/HelveticaBoldObliqueWidths.gperf" { "Yacute", 667 }, #line 253 "poppler/HelveticaBoldObliqueWidths.gperf" { "eogonek", 556 }, #line 80 "poppler/HelveticaBoldObliqueWidths.gperf" { "question", 611 }, { "", 0 }, #line 313 "poppler/HelveticaBoldObliqueWidths.gperf" { "breve", 333 }, #line 77 "poppler/HelveticaBoldObliqueWidths.gperf" { "V", 667 }, #line 39 "poppler/HelveticaBoldObliqueWidths.gperf" { "questiondown", 611 }, { "", 0 }, #line 266 "poppler/HelveticaBoldObliqueWidths.gperf" { "plus", 584 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 149 "poppler/HelveticaBoldObliqueWidths.gperf" { "ellipsis", 1000 }, { "", 0 }, { "", 0 }, #line 319 "poppler/HelveticaBoldObliqueWidths.gperf" { "exclam", 333 }, { "", 0 }, { "", 0 }, #line 219 "poppler/HelveticaBoldObliqueWidths.gperf" { "braceleft", 389 }, #line 303 "poppler/HelveticaBoldObliqueWidths.gperf" { "braceright", 389 }, #line 156 "poppler/HelveticaBoldObliqueWidths.gperf" { "hyphen", 333 }, #line 48 "poppler/HelveticaBoldObliqueWidths.gperf" { "aogonek", 556 }, #line 314 "poppler/HelveticaBoldObliqueWidths.gperf" { "bar", 280 }, { "", 0 }, #line 311 "poppler/HelveticaBoldObliqueWidths.gperf" { "zdotaccent", 500 }, #line 153 "poppler/HelveticaBoldObliqueWidths.gperf" { "lslash", 278 }, #line 86 "poppler/HelveticaBoldObliqueWidths.gperf" { "Gcommaaccent", 778 }, #line 309 "poppler/HelveticaBoldObliqueWidths.gperf" { "currency", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 75 "poppler/HelveticaBoldObliqueWidths.gperf" { "U", 722 }, #line 27 "poppler/HelveticaBoldObliqueWidths.gperf" { "onehalf", 834 }, #line 109 "poppler/HelveticaBoldObliqueWidths.gperf" { "uhungarumlaut", 611 }, { "", 0 }, { "", 0 }, #line 147 "poppler/HelveticaBoldObliqueWidths.gperf" { "Otilde", 778 }, { "", 0 }, #line 287 "poppler/HelveticaBoldObliqueWidths.gperf" { "guillemotleft", 556 }, #line 202 "poppler/HelveticaBoldObliqueWidths.gperf" { "guillemotright", 556 }, { "", 0 }, #line 247 "poppler/HelveticaBoldObliqueWidths.gperf" { "Lacute", 611 }, #line 163 "poppler/HelveticaBoldObliqueWidths.gperf" { "Umacron", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 18 "poppler/HelveticaBoldObliqueWidths.gperf" { "Zacute", 611 }, { "", 0 }, #line 295 "poppler/HelveticaBoldObliqueWidths.gperf" { "notequal", 549 }, #line 143 "poppler/HelveticaBoldObliqueWidths.gperf" { "trademark", 1000 }, { "", 0 }, #line 265 "poppler/HelveticaBoldObliqueWidths.gperf" { "Ncaron", 722 }, #line 200 "poppler/HelveticaBoldObliqueWidths.gperf" { "Scommaaccent", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 181 "poppler/HelveticaBoldObliqueWidths.gperf" { "perthousand", 1000 }, { "", 0 }, #line 240 "poppler/HelveticaBoldObliqueWidths.gperf" { "six", 556 }, { "", 0 }, { "", 0 }, #line 302 "poppler/HelveticaBoldObliqueWidths.gperf" { "icircumflex", 278 }, { "", 0 }, #line 214 "poppler/HelveticaBoldObliqueWidths.gperf" { "Scedilla", 667 }, { "", 0 }, { "", 0 }, #line 93 "poppler/HelveticaBoldObliqueWidths.gperf" { "bullet", 350 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 108 "poppler/HelveticaBoldObliqueWidths.gperf" { "q", 611 }, #line 289 "poppler/HelveticaBoldObliqueWidths.gperf" { "Amacron", 722 }, { "", 0 }, { "", 0 }, #line 127 "poppler/HelveticaBoldObliqueWidths.gperf" { "Idotaccent", 278 }, #line 141 "poppler/HelveticaBoldObliqueWidths.gperf" { "Ecircumflex", 667 }, { "", 0 }, #line 315 "poppler/HelveticaBoldObliqueWidths.gperf" { "fraction", 167 }, #line 180 "poppler/HelveticaBoldObliqueWidths.gperf" { "Udieresis", 722 }, { "", 0 }, #line 176 "poppler/HelveticaBoldObliqueWidths.gperf" { "ucircumflex", 611 }, { "", 0 }, { "", 0 }, #line 197 "poppler/HelveticaBoldObliqueWidths.gperf" { "five", 556 }, { "", 0 }, #line 14 "poppler/HelveticaBoldObliqueWidths.gperf" { "Ntilde", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 267 "poppler/HelveticaBoldObliqueWidths.gperf" { "uring", 611 }, #line 45 "poppler/HelveticaBoldObliqueWidths.gperf" { "A", 722 }, { "", 0 }, { "", 0 }, #line 84 "poppler/HelveticaBoldObliqueWidths.gperf" { "four", 556 }, { "", 0 }, #line 187 "poppler/HelveticaBoldObliqueWidths.gperf" { "egrave", 556 }, { "", 0 }, { "", 0 }, #line 241 "poppler/HelveticaBoldObliqueWidths.gperf" { "paragraph", 556 }, { "", 0 }, #line 29 "poppler/HelveticaBoldObliqueWidths.gperf" { "Lcaron", 611 }, { "", 0 }, { "", 0 }, #line 325 "poppler/HelveticaBoldObliqueWidths.gperf" { "brokenbar", 280 }, { "", 0 }, #line 199 "poppler/HelveticaBoldObliqueWidths.gperf" { "Zcaron", 611 }, { "", 0 }, { "", 0 }, #line 165 "poppler/HelveticaBoldObliqueWidths.gperf" { "Adieresis", 722 }, { "", 0 }, #line 122 "poppler/HelveticaBoldObliqueWidths.gperf" { "y", 556 }, #line 16 "poppler/HelveticaBoldObliqueWidths.gperf" { "kcommaaccent", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 76 "poppler/HelveticaBoldObliqueWidths.gperf" { "agrave", 556 }, { "", 0 }, #line 69 "poppler/HelveticaBoldObliqueWidths.gperf" { "Uhungarumlaut", 722 }, #line 204 "poppler/HelveticaBoldObliqueWidths.gperf" { "ydieresis", 556 }, #line 168 "poppler/HelveticaBoldObliqueWidths.gperf" { "slash", 278 }, #line 229 "poppler/HelveticaBoldObliqueWidths.gperf" { "ogonek", 333 }, #line 151 "poppler/HelveticaBoldObliqueWidths.gperf" { "AE", 1000 }, #line 192 "poppler/HelveticaBoldObliqueWidths.gperf" { "asterisk", 389 }, { "", 0 }, { "", 0 }, #line 111 "poppler/HelveticaBoldObliqueWidths.gperf" { "twosuperior", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 53 "poppler/HelveticaBoldObliqueWidths.gperf" { "G", 778 }, #line 58 "poppler/HelveticaBoldObliqueWidths.gperf" { "iogonek", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 17 "poppler/HelveticaBoldObliqueWidths.gperf" { "Ncommaaccent", 722 }, { "", 0 }, #line 21 "poppler/HelveticaBoldObliqueWidths.gperf" { "plusminus", 584 }, { "", 0 }, #line 230 "poppler/HelveticaBoldObliqueWidths.gperf" { "ograve", 611 }, { "", 0 }, #line 129 "poppler/HelveticaBoldObliqueWidths.gperf" { "quotedbl", 474 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 233 "poppler/HelveticaBoldObliqueWidths.gperf" { "Eogonek", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 120 "poppler/HelveticaBoldObliqueWidths.gperf" { "w", 778 }, #line 259 "poppler/HelveticaBoldObliqueWidths.gperf" { "uogonek", 611 }, { "", 0 }, #line 59 "poppler/HelveticaBoldObliqueWidths.gperf" { "backslash", 278 }, #line 81 "poppler/HelveticaBoldObliqueWidths.gperf" { "equal", 584 }, { "", 0 }, #line 38 "poppler/HelveticaBoldObliqueWidths.gperf" { "Omacron", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 121 "poppler/HelveticaBoldObliqueWidths.gperf" { "x", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 298 "poppler/HelveticaBoldObliqueWidths.gperf" { "Zdotaccent", 611 }, #line 152 "poppler/HelveticaBoldObliqueWidths.gperf" { "Ucircumflex", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 68 "poppler/HelveticaBoldObliqueWidths.gperf" { "Q", 778 }, #line 296 "poppler/HelveticaBoldObliqueWidths.gperf" { "Imacron", 278 }, { "", 0 }, { "", 0 }, #line 250 "poppler/HelveticaBoldObliqueWidths.gperf" { "Uring", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 123 "poppler/HelveticaBoldObliqueWidths.gperf" { "z", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 22 "poppler/HelveticaBoldObliqueWidths.gperf" { "circumflex", 333 }, { "", 0 }, #line 251 "poppler/HelveticaBoldObliqueWidths.gperf" { "Lcommaaccent", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 73 "poppler/HelveticaBoldObliqueWidths.gperf" { "S", 667 }, { "", 0 }, { "", 0 }, #line 175 "poppler/HelveticaBoldObliqueWidths.gperf" { "Odieresis", 778 }, { "", 0 }, #line 284 "poppler/HelveticaBoldObliqueWidths.gperf" { "Acircumflex", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 67 "poppler/HelveticaBoldObliqueWidths.gperf" { "P", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 238 "poppler/HelveticaBoldObliqueWidths.gperf" { "Aring", 722 }, #line 96 "poppler/HelveticaBoldObliqueWidths.gperf" { "Oslash", 778 }, #line 114 "poppler/HelveticaBoldObliqueWidths.gperf" { "OE", 1000 }, { "", 0 }, #line 171 "poppler/HelveticaBoldObliqueWidths.gperf" { "Idieresis", 278 }, { "", 0 }, #line 62 "poppler/HelveticaBoldObliqueWidths.gperf" { "M", 833 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 282 "poppler/HelveticaBoldObliqueWidths.gperf" { "fi", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 56 "poppler/HelveticaBoldObliqueWidths.gperf" { "J", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 263 "poppler/HelveticaBoldObliqueWidths.gperf" { "igrave", 278 }, { "", 0 }, #line 255 "poppler/HelveticaBoldObliqueWidths.gperf" { "Ohungarumlaut", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 243 "poppler/HelveticaBoldObliqueWidths.gperf" { "Uogonek", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 87 "poppler/HelveticaBoldObliqueWidths.gperf" { "b", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 167 "poppler/HelveticaBoldObliqueWidths.gperf" { "Egrave", 667 }, { "", 0 }, { "", 0 }, #line 182 "poppler/HelveticaBoldObliqueWidths.gperf" { "Ydieresis", 667 }, { "", 0 }, #line 195 "poppler/HelveticaBoldObliqueWidths.gperf" { "ugrave", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 211 "poppler/HelveticaBoldObliqueWidths.gperf" { "multiply", 584 }, { "", 0 }, { "", 0 }, #line 66 "poppler/HelveticaBoldObliqueWidths.gperf" { "O", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 31 "poppler/HelveticaBoldObliqueWidths.gperf" { "Aogonek", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 279 "poppler/HelveticaBoldObliqueWidths.gperf" { "florin", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 226 "poppler/HelveticaBoldObliqueWidths.gperf" { "Ocircumflex", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 283 "poppler/HelveticaBoldObliqueWidths.gperf" { "fl", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 55 "poppler/HelveticaBoldObliqueWidths.gperf" { "I", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 286 "poppler/HelveticaBoldObliqueWidths.gperf" { "Icircumflex", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 54 "poppler/HelveticaBoldObliqueWidths.gperf" { "H", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 134 "poppler/HelveticaBoldObliqueWidths.gperf" { "Lslash", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 99 "poppler/HelveticaBoldObliqueWidths.gperf" { "k", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 184 "poppler/HelveticaBoldObliqueWidths.gperf" { "abreve", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 217 "poppler/HelveticaBoldObliqueWidths.gperf" { "partialdiff", 494 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 52 "poppler/HelveticaBoldObliqueWidths.gperf" { "F", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 178 "poppler/HelveticaBoldObliqueWidths.gperf" { "Ugrave", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 91 "poppler/HelveticaBoldObliqueWidths.gperf" { "f", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 119 "poppler/HelveticaBoldObliqueWidths.gperf" { "v", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 63 "poppler/HelveticaBoldObliqueWidths.gperf" { "N", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 41 "poppler/HelveticaBoldObliqueWidths.gperf" { "Agrave", 722 }, #line 34 "poppler/HelveticaBoldObliqueWidths.gperf" { "Iogonek", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 82 "poppler/HelveticaBoldObliqueWidths.gperf" { "Y", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 46 "poppler/HelveticaBoldObliqueWidths.gperf" { "B", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 177 "poppler/HelveticaBoldObliqueWidths.gperf" { "bracketleft", 333 }, #line 260 "poppler/HelveticaBoldObliqueWidths.gperf" { "bracketright", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 142 "poppler/HelveticaBoldObliqueWidths.gperf" { "gbreve", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 215 "poppler/HelveticaBoldObliqueWidths.gperf" { "Ograve", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 60 "poppler/HelveticaBoldObliqueWidths.gperf" { "L", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 246 "poppler/HelveticaBoldObliqueWidths.gperf" { "Igrave", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 83 "poppler/HelveticaBoldObliqueWidths.gperf" { "Z", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 162 "poppler/HelveticaBoldObliqueWidths.gperf" { "Abreve", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 124 "poppler/HelveticaBoldObliqueWidths.gperf" { "Gbreve", 778 } }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) { register unsigned int key = hash(str, len); if (key <= MAX_HASH_VALUE) { register const char *s = wordlist[key].name; if (*str == *s && !strcmp(str + 1, s + 1)) return &wordlist[key]; } } return 0; } #line 330 "poppler/HelveticaBoldObliqueWidths.gperf" poppler-24.02.0/poppler/HelveticaBoldWidths.gperf000066400000000000000000000101751455701731300217500ustar00rootroot00000000000000%{ #include #include "BuiltinFontWidth.h" %} %language=ANSI-C %define initializer-suffix ,0 %define lookup-function-name HelveticaBoldWidthsLookup %struct-type %omit-struct-type %readonly-tables struct BuiltinFontWidth %% #### Ntilde, 722 rcaron, 389 kcommaaccent, 556 Ncommaaccent, 722 Zacute, 611 comma, 278 cedilla, 333 plusminus, 584 circumflex, 333 dotaccent, 333 edotaccent, 556 asciitilde, 584 colon, 333 onehalf, 834 dollar, 556 Lcaron, 611 ntilde, 611 Aogonek, 722 ncommaaccent, 611 minus, 584 Iogonek, 278 zacute, 500 yen, 556 space, 278 Omacron, 778 questiondown, 611 emdash, 1000 Agrave, 722 three, 556 numbersign, 556 lcaron, 400 A, 722 B, 722 C, 722 aogonek, 556 D, 722 E, 667 onequarter, 834 F, 611 G, 778 H, 722 I, 278 J, 556 K, 722 iogonek, 278 backslash, 278 L, 611 periodcentered, 278 M, 833 N, 722 omacron, 611 Tcommaaccent, 611 O, 778 P, 667 Q, 778 Uhungarumlaut, 722 R, 722 Aacute, 722 caron, 333 S, 667 T, 611 U, 722 agrave, 556 V, 667 W, 944 X, 667 question, 611 equal, 584 Y, 667 Z, 611 four, 556 a, 556 Gcommaaccent, 778 b, 611 c, 556 d, 611 e, 556 f, 333 g, 611 bullet, 350 h, 611 i, 278 Oslash, 778 dagger, 556 j, 278 k, 556 l, 278 m, 889 n, 611 tcommaaccent, 333 o, 611 ordfeminine, 370 ring, 333 p, 611 q, 611 uhungarumlaut, 611 r, 389 twosuperior, 333 aacute, 556 s, 556 OE, 1000 t, 333 divide, 584 u, 611 Ccaron, 722 v, 556 w, 778 x, 556 y, 556 z, 500 Gbreve, 778 commaaccent, 250 hungarumlaut, 333 Idotaccent, 278 Nacute, 722 quotedbl, 474 gcommaaccent, 611 mu, 611 greaterequal, 549 Scaron, 667 Lslash, 611 semicolon, 333 oslash, 611 lessequal, 549 lozenge, 494 parenright, 333 ccaron, 556 Ecircumflex, 667 gbreve, 611 trademark, 1000 daggerdbl, 556 nacute, 611 macron, 333 Otilde, 778 Emacron, 667 ellipsis, 1000 scaron, 556 AE, 1000 Ucircumflex, 722 lslash, 278 quotedblleft, 500 guilsinglright, 333 hyphen, 333 quotesingle, 238 eight, 556 exclamdown, 333 endash, 556 oe, 944 Abreve, 722 Umacron, 722 ecircumflex, 556 Adieresis, 722 copyright, 737 Egrave, 667 slash, 278 Edieresis, 667 otilde, 611 Idieresis, 278 parenleft, 333 one, 556 emacron, 556 Odieresis, 778 ucircumflex, 611 bracketleft, 333 Ugrave, 722 quoteright, 278 Udieresis, 722 perthousand, 1000 Ydieresis, 667 umacron, 611 abreve, 556 Eacute, 667 adieresis, 556 egrave, 556 edieresis, 556 idieresis, 278 Eth, 722 ae, 889 asterisk, 389 odieresis, 611 Uacute, 722 ugrave, 611 nine, 556 five, 556 udieresis, 611 Zcaron, 611 Scommaaccent, 667 threequarters, 834 guillemotright, 556 Ccedilla, 722 ydieresis, 556 tilde, 333 dbldaggerumlaut, 556 at, 975 eacute, 556 underscore, 556 Euro, 556 Dcroat, 722 multiply, 584 zero, 556 eth, 611 Scedilla, 667 Ograve, 778 Racute, 722 partialdiff, 494 uacute, 611 braceleft, 389 Thorn, 667 zcaron, 500 scommaaccent, 556 ccedilla, 556 Dcaron, 722 dcroat, 611 Ocircumflex, 778 Oacute, 778 scedilla, 556 ogonek, 333 ograve, 611 racute, 389 Tcaron, 611 Eogonek, 667 thorn, 611 degree, 400 registered, 737 radical, 549 Aring, 722 percent, 889 six, 556 paragraph, 556 dcaron, 743 Uogonek, 722 two, 556 summation, 600 Igrave, 278 Lacute, 611 ocircumflex, 611 oacute, 611 Uring, 722 Lcommaaccent, 611 tcaron, 389 eogonek, 556 Delta, 612 Ohungarumlaut, 778 asciicircum, 584 aring, 556 grave, 333 uogonek, 611 bracketright, 333 Iacute, 278 ampersand, 722 igrave, 278 lacute, 278 Ncaron, 722 plus, 584 uring, 611 quotesinglbase, 278 lcommaaccent, 278 Yacute, 667 ohungarumlaut, 611 threesuperior, 333 acute, 333 section, 556 dieresis, 333 iacute, 278 quotedblbase, 500 ncaron, 611 florin, 556 yacute, 556 Rcommaaccent, 722 fi, 611 fl, 611 Acircumflex, 722 Cacute, 722 Icircumflex, 278 guillemotleft, 556 germandbls, 611 Amacron, 722 seven, 556 Sacute, 667 ordmasculine, 365 dotlessi, 278 sterling, 556 notequal, 549 Imacron, 278 rcommaaccent, 389 Zdotaccent, 611 acircumflex, 556 cacute, 556 Ecaron, 667 icircumflex, 278 braceright, 389 quotedblright, 500 amacron, 556 sacute, 556 imacron, 278 cent, 556 currency, 556 logicalnot, 584 zdotaccent, 500 Atilde, 722 breve, 333 bar, 280 fraction, 167 less, 584 ecaron, 556 guilsinglleft, 333 exclam, 333 period, 278 Rcaron, 722 Kcommaaccent, 722 greater, 584 atilde, 556 brokenbar, 280 quoteleft, 278 Edotaccent, 667 onesuperior, 333 #### %% poppler-24.02.0/poppler/HelveticaBoldWidths.pregenerated.c000066400000000000000000002565701455701731300235460ustar00rootroot00000000000000/* ANSI-C code produced by gperf version 3.1 */ /* Command-line: gperf poppler/HelveticaBoldWidths.gperf */ /* Computed positions: -k'1-2,5,$' */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) && ('-' == 45) && ('.' == 46) && ('/' == 47) \ && ('0' == 48) && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) && ('=' == 61) && ('>' == 62) \ && ('?' == 63) && ('A' == 65) && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) && ('N' == 78) \ && ('O' == 79) && ('P' == 80) && ('Q' == 81) && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) && ('k' == 107) && ('l' == 108) \ && ('m' == 109) && ('n' == 110) && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) /* The character set is not based on ISO-646. */ # error "gperf generated tables don't work with this execution character set. Please report a bug to ." #endif #line 1 "poppler/HelveticaBoldWidths.gperf" #include #include "BuiltinFontWidth.h" #define TOTAL_KEYWORDS 316 #define MIN_WORD_LENGTH 1 #define MAX_WORD_LENGTH 15 #define MIN_HASH_VALUE 1 #define MAX_HASH_VALUE 1001 /* maximum key range = 1001, duplicates = 0 */ #ifdef __GNUC__ __inline #else # ifdef __cplusplus inline # endif #endif static unsigned int hash(register const char *str, register size_t len) { static const unsigned short asso_values[] = { 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 270, 440, 28, 480, 315, 435, 5, 430, 405, 385, 35, 3, 330, 460, 355, 325, 310, 500, 350, 160, 370, 300, 275, 145, 415, 420, 1002, 1002, 1002, 1002, 1002, 1002, 20, 320, 30, 115, 0, 395, 140, 165, 135, 70, 380, 170, 130, 15, 45, 215, 260, 100, 65, 10, 155, 335, 290, 305, 280, 150, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002 }; register unsigned int hval = len; switch (hval) { default: hval += asso_values[(unsigned char)str[4]]; /*FALLTHROUGH*/ case 4: case 3: case 2: hval += asso_values[(unsigned char)str[1]]; /*FALLTHROUGH*/ case 1: hval += asso_values[(unsigned char)str[0]]; break; } return hval + asso_values[(unsigned char)str[len - 1]]; } const struct BuiltinFontWidth *HelveticaBoldWidthsLookup(register const char *str, register size_t len) { static const struct BuiltinFontWidth wordlist[] = { { "", 0 }, #line 90 "poppler/HelveticaBoldWidths.gperf" { "e", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 60 "poppler/HelveticaBoldWidths.gperf" { "L", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 53 "poppler/HelveticaBoldWidths.gperf" { "G", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 115 "poppler/HelveticaBoldWidths.gperf" { "t", 333 }, #line 191 "poppler/HelveticaBoldWidths.gperf" { "ae", 889 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 102 "poppler/HelveticaBoldWidths.gperf" { "n", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 208 "poppler/HelveticaBoldWidths.gperf" { "eacute", 556 }, { "", 0 }, { "", 0 }, #line 248 "poppler/HelveticaBoldWidths.gperf" { "Lacute", 611 }, { "", 0 }, #line 85 "poppler/HelveticaBoldWidths.gperf" { "a", 556 }, #line 207 "poppler/HelveticaBoldWidths.gperf" { "at", 975 }, { "", 0 }, #line 309 "poppler/HelveticaBoldWidths.gperf" { "cent", 556 }, { "", 0 }, { "", 0 }, #line 161 "poppler/HelveticaBoldWidths.gperf" { "oe", 944 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 145 "poppler/HelveticaBoldWidths.gperf" { "nacute", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 274 "poppler/HelveticaBoldWidths.gperf" { "acute", 333 }, #line 112 "poppler/HelveticaBoldWidths.gperf" { "aacute", 556 }, #line 47 "poppler/HelveticaBoldWidths.gperf" { "C", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 88 "poppler/HelveticaBoldWidths.gperf" { "c", 556 }, { "", 0 }, #line 173 "poppler/HelveticaBoldWidths.gperf" { "one", 556 }, #line 286 "poppler/HelveticaBoldWidths.gperf" { "Cacute", 722 }, { "", 0 }, #line 301 "poppler/HelveticaBoldWidths.gperf" { "cacute", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 57 "poppler/HelveticaBoldWidths.gperf" { "K", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 250 "poppler/HelveticaBoldWidths.gperf" { "oacute", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 72 "poppler/HelveticaBoldWidths.gperf" { "caron", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 104 "poppler/HelveticaBoldWidths.gperf" { "o", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 318 "poppler/HelveticaBoldWidths.gperf" { "ecaron", 556 }, { "", 0 }, { "", 0 }, #line 29 "poppler/HelveticaBoldWidths.gperf" { "Lcaron", 611 }, #line 291 "poppler/HelveticaBoldWidths.gperf" { "seven", 556 }, #line 307 "poppler/HelveticaBoldWidths.gperf" { "sacute", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 253 "poppler/HelveticaBoldWidths.gperf" { "tcaron", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 26 "poppler/HelveticaBoldWidths.gperf" { "colon", 333 }, #line 279 "poppler/HelveticaBoldWidths.gperf" { "ncaron", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 125 "poppler/HelveticaBoldWidths.gperf" { "commaaccent", 250 }, { "", 0 }, { "", 0 }, #line 135 "poppler/HelveticaBoldWidths.gperf" { "semicolon", 333 }, #line 19 "poppler/HelveticaBoldWidths.gperf" { "comma", 278 }, #line 236 "poppler/HelveticaBoldWidths.gperf" { "degree", 400 }, { "", 0 }, { "", 0 }, #line 118 "poppler/HelveticaBoldWidths.gperf" { "Ccaron", 722 }, { "", 0 }, #line 140 "poppler/HelveticaBoldWidths.gperf" { "ccaron", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 113 "poppler/HelveticaBoldWidths.gperf" { "s", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 232 "poppler/HelveticaBoldWidths.gperf" { "racute", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 98 "poppler/HelveticaBoldWidths.gperf" { "j", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 30 "poppler/HelveticaBoldWidths.gperf" { "ntilde", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 205 "poppler/HelveticaBoldWidths.gperf" { "tilde", 333 }, #line 325 "poppler/HelveticaBoldWidths.gperf" { "atilde", 556 }, { "", 0 }, { "", 0 }, #line 196 "poppler/HelveticaBoldWidths.gperf" { "nine", 556 }, #line 24 "poppler/HelveticaBoldWidths.gperf" { "edotaccent", 556 }, #line 105 "poppler/HelveticaBoldWidths.gperf" { "ordfeminine", 370 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 158 "poppler/HelveticaBoldWidths.gperf" { "eight", 556 }, #line 150 "poppler/HelveticaBoldWidths.gperf" { "scaron", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 277 "poppler/HelveticaBoldWidths.gperf" { "iacute", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 170 "poppler/HelveticaBoldWidths.gperf" { "otilde", 611 }, #line 293 "poppler/HelveticaBoldWidths.gperf" { "ordmasculine", 365 }, #line 214 "poppler/HelveticaBoldWidths.gperf" { "eth", 611 }, { "", 0 }, #line 42 "poppler/HelveticaBoldWidths.gperf" { "three", 556 }, #line 226 "poppler/HelveticaBoldWidths.gperf" { "dcroat", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 252 "poppler/HelveticaBoldWidths.gperf" { "Lcommaaccent", 611 }, #line 35 "poppler/HelveticaBoldWidths.gperf" { "zacute", 500 }, #line 86 "poppler/HelveticaBoldWidths.gperf" { "Gcommaaccent", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 219 "poppler/HelveticaBoldWidths.gperf" { "uacute", 611 }, #line 103 "poppler/HelveticaBoldWidths.gperf" { "tcommaaccent", 333 }, { "", 0 }, #line 166 "poppler/HelveticaBoldWidths.gperf" { "copyright", 737 }, #line 43 "poppler/HelveticaBoldWidths.gperf" { "numbersign", 556 }, #line 15 "poppler/HelveticaBoldWidths.gperf" { "rcaron", 389 }, #line 32 "poppler/HelveticaBoldWidths.gperf" { "ncommaaccent", 611 }, { "", 0 }, #line 213 "poppler/HelveticaBoldWidths.gperf" { "zero", 556 }, { "", 0 }, #line 110 "poppler/HelveticaBoldWidths.gperf" { "r", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 265 "poppler/HelveticaBoldWidths.gperf" { "lacute", 278 }, { "", 0 }, { "", 0 }, #line 23 "poppler/HelveticaBoldWidths.gperf" { "dotaccent", 333 }, #line 235 "poppler/HelveticaBoldWidths.gperf" { "thorn", 611 }, #line 243 "poppler/HelveticaBoldWidths.gperf" { "dcaron", 743 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 146 "poppler/HelveticaBoldWidths.gperf" { "macron", 333 }, #line 323 "poppler/HelveticaBoldWidths.gperf" { "Kcommaaccent", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 203 "poppler/HelveticaBoldWidths.gperf" { "Ccedilla", 722 }, #line 275 "poppler/HelveticaBoldWidths.gperf" { "section", 556 }, #line 224 "poppler/HelveticaBoldWidths.gperf" { "ccedilla", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 20 "poppler/HelveticaBoldWidths.gperf" { "cedilla", 333 }, { "", 0 }, { "", 0 }, #line 25 "poppler/HelveticaBoldWidths.gperf" { "asciitilde", 584 }, #line 89 "poppler/HelveticaBoldWidths.gperf" { "d", 611 }, #line 240 "poppler/HelveticaBoldWidths.gperf" { "percent", 889 }, { "", 0 }, { "", 0 }, #line 289 "poppler/HelveticaBoldWidths.gperf" { "germandbls", 611 }, { "", 0 }, #line 138 "poppler/HelveticaBoldWidths.gperf" { "lozenge", 494 }, { "", 0 }, #line 317 "poppler/HelveticaBoldWidths.gperf" { "less", 584 }, { "", 0 }, #line 97 "poppler/HelveticaBoldWidths.gperf" { "dagger", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 259 "poppler/HelveticaBoldWidths.gperf" { "grave", 333 }, #line 222 "poppler/HelveticaBoldWidths.gperf" { "zcaron", 500 }, #line 223 "poppler/HelveticaBoldWidths.gperf" { "scommaaccent", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 160 "poppler/HelveticaBoldWidths.gperf" { "endash", 556 }, #line 174 "poppler/HelveticaBoldWidths.gperf" { "emacron", 556 }, #line 201 "poppler/HelveticaBoldWidths.gperf" { "threequarters", 834 }, { "", 0 }, { "", 0 }, #line 233 "poppler/HelveticaBoldWidths.gperf" { "Tcaron", 611 }, { "", 0 }, #line 229 "poppler/HelveticaBoldWidths.gperf" { "scedilla", 556 }, { "", 0 }, { "", 0 }, #line 101 "poppler/HelveticaBoldWidths.gperf" { "m", 889 }, { "", 0 }, { "", 0 }, #line 246 "poppler/HelveticaBoldWidths.gperf" { "summation", 600 }, #line 311 "poppler/HelveticaBoldWidths.gperf" { "logicalnot", 584 }, #line 44 "poppler/HelveticaBoldWidths.gperf" { "lcaron", 400 }, { "", 0 }, { "", 0 }, #line 172 "poppler/HelveticaBoldWidths.gperf" { "parenleft", 333 }, #line 139 "poppler/HelveticaBoldWidths.gperf" { "parenright", 333 }, #line 95 "poppler/HelveticaBoldWidths.gperf" { "i", 278 }, #line 306 "poppler/HelveticaBoldWidths.gperf" { "amacron", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 209 "poppler/HelveticaBoldWidths.gperf" { "underscore", 556 }, #line 92 "poppler/HelveticaBoldWidths.gperf" { "g", 611 }, #line 298 "poppler/HelveticaBoldWidths.gperf" { "rcommaaccent", 389 }, { "", 0 }, { "", 0 }, #line 37 "poppler/HelveticaBoldWidths.gperf" { "space", 278 }, #line 28 "poppler/HelveticaBoldWidths.gperf" { "dollar", 556 }, { "", 0 }, #line 273 "poppler/HelveticaBoldWidths.gperf" { "threesuperior", 333 }, #line 188 "poppler/HelveticaBoldWidths.gperf" { "edieresis", 556 }, #line 237 "poppler/HelveticaBoldWidths.gperf" { "registered", 737 }, #line 79 "poppler/HelveticaBoldWidths.gperf" { "X", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 64 "poppler/HelveticaBoldWidths.gperf" { "omacron", 611 }, #line 36 "poppler/HelveticaBoldWidths.gperf" { "yen", 556 }, { "", 0 }, { "", 0 }, #line 123 "poppler/HelveticaBoldWidths.gperf" { "z", 500 }, { "", 0 }, #line 294 "poppler/HelveticaBoldWidths.gperf" { "dotlessi", 278 }, #line 134 "poppler/HelveticaBoldWidths.gperf" { "Lslash", 611 }, #line 312 "poppler/HelveticaBoldWidths.gperf" { "zdotaccent", 500 }, #line 71 "poppler/HelveticaBoldWidths.gperf" { "Aacute", 722 }, { "", 0 }, { "", 0 }, #line 186 "poppler/HelveticaBoldWidths.gperf" { "adieresis", 556 }, { "", 0 }, #line 117 "poppler/HelveticaBoldWidths.gperf" { "u", 611 }, { "", 0 }, { "", 0 }, #line 144 "poppler/HelveticaBoldWidths.gperf" { "daggerdbl", 556 }, { "", 0 }, #line 281 "poppler/HelveticaBoldWidths.gperf" { "yacute", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 74 "poppler/HelveticaBoldWidths.gperf" { "T", 611 }, #line 130 "poppler/HelveticaBoldWidths.gperf" { "gcommaaccent", 611 }, #line 276 "poppler/HelveticaBoldWidths.gperf" { "dieresis", 333 }, { "", 0 }, #line 51 "poppler/HelveticaBoldWidths.gperf" { "onequarter", 834 }, #line 329 "poppler/HelveticaBoldWidths.gperf" { "onesuperior", 333 }, #line 238 "poppler/HelveticaBoldWidths.gperf" { "radical", 549 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 94 "poppler/HelveticaBoldWidths.gperf" { "h", 611 }, { "", 0 }, { "", 0 }, #line 193 "poppler/HelveticaBoldWidths.gperf" { "odieresis", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 100 "poppler/HelveticaBoldWidths.gperf" { "l", 278 }, #line 65 "poppler/HelveticaBoldWidths.gperf" { "Tcommaaccent", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 136 "poppler/HelveticaBoldWidths.gperf" { "oslash", 611 }, { "", 0 }, #line 245 "poppler/HelveticaBoldWidths.gperf" { "two", 556 }, #line 137 "poppler/HelveticaBoldWidths.gperf" { "lessequal", 549 }, #line 159 "poppler/HelveticaBoldWidths.gperf" { "exclamdown", 333 }, #line 185 "poppler/HelveticaBoldWidths.gperf" { "Eacute", 667 }, #line 270 "poppler/HelveticaBoldWidths.gperf" { "lcommaaccent", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 324 "poppler/HelveticaBoldWidths.gperf" { "greater", 584 }, { "", 0 }, { "", 0 }, #line 221 "poppler/HelveticaBoldWidths.gperf" { "Thorn", 667 }, #line 257 "poppler/HelveticaBoldWidths.gperf" { "asciicircum", 584 }, #line 126 "poppler/HelveticaBoldWidths.gperf" { "hungarumlaut", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 40 "poppler/HelveticaBoldWidths.gperf" { "emdash", 1000 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 116 "poppler/HelveticaBoldWidths.gperf" { "divide", 584 }, { "", 0 }, #line 272 "poppler/HelveticaBoldWidths.gperf" { "ohungarumlaut", 611 }, #line 263 "poppler/HelveticaBoldWidths.gperf" { "ampersand", 722 }, { "", 0 }, #line 164 "poppler/HelveticaBoldWidths.gperf" { "ecircumflex", 556 }, { "", 0 }, { "", 0 }, #line 106 "poppler/HelveticaBoldWidths.gperf" { "ring", 333 }, { "", 0 }, #line 321 "poppler/HelveticaBoldWidths.gperf" { "period", 278 }, { "", 0 }, #line 319 "poppler/HelveticaBoldWidths.gperf" { "guilsinglleft", 333 }, #line 155 "poppler/HelveticaBoldWidths.gperf" { "guilsinglright", 333 }, { "", 0 }, #line 292 "poppler/HelveticaBoldWidths.gperf" { "Sacute", 667 }, #line 308 "poppler/HelveticaBoldWidths.gperf" { "imacron", 278 }, { "", 0 }, #line 61 "poppler/HelveticaBoldWidths.gperf" { "periodcentered", 278 }, { "", 0 }, #line 228 "poppler/HelveticaBoldWidths.gperf" { "Oacute", 778 }, { "", 0 }, #line 295 "poppler/HelveticaBoldWidths.gperf" { "sterling", 556 }, { "", 0 }, { "", 0 }, #line 300 "poppler/HelveticaBoldWidths.gperf" { "acircumflex", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 33 "poppler/HelveticaBoldWidths.gperf" { "minus", 584 }, #line 313 "poppler/HelveticaBoldWidths.gperf" { "Atilde", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 258 "poppler/HelveticaBoldWidths.gperf" { "aring", 556 }, #line 194 "poppler/HelveticaBoldWidths.gperf" { "Uacute", 722 }, #line 183 "poppler/HelveticaBoldWidths.gperf" { "umacron", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 302 "poppler/HelveticaBoldWidths.gperf" { "Ecaron", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 249 "poppler/HelveticaBoldWidths.gperf" { "ocircumflex", 611 }, { "", 0 }, { "", 0 }, #line 189 "poppler/HelveticaBoldWidths.gperf" { "idieresis", 278 }, #line 314 "poppler/HelveticaBoldWidths.gperf" { "breve", 333 }, #line 157 "poppler/HelveticaBoldWidths.gperf" { "quotesingle", 238 }, #line 278 "poppler/HelveticaBoldWidths.gperf" { "quotedblbase", 500 }, { "", 0 }, #line 269 "poppler/HelveticaBoldWidths.gperf" { "quotesinglbase", 278 }, { "", 0 }, #line 107 "poppler/HelveticaBoldWidths.gperf" { "p", 611 }, #line 132 "poppler/HelveticaBoldWidths.gperf" { "greaterequal", 549 }, { "", 0 }, #line 327 "poppler/HelveticaBoldWidths.gperf" { "quoteleft", 278 }, #line 179 "poppler/HelveticaBoldWidths.gperf" { "quoteright", 278 }, { "", 0 }, #line 154 "poppler/HelveticaBoldWidths.gperf" { "quotedblleft", 500 }, #line 305 "poppler/HelveticaBoldWidths.gperf" { "quotedblright", 500 }, #line 220 "poppler/HelveticaBoldWidths.gperf" { "braceleft", 389 }, #line 304 "poppler/HelveticaBoldWidths.gperf" { "braceright", 389 }, #line 262 "poppler/HelveticaBoldWidths.gperf" { "Iacute", 278 }, #line 131 "poppler/HelveticaBoldWidths.gperf" { "mu", 611 }, #line 315 "poppler/HelveticaBoldWidths.gperf" { "bar", 280 }, #line 198 "poppler/HelveticaBoldWidths.gperf" { "udieresis", 611 }, { "", 0 }, #line 133 "poppler/HelveticaBoldWidths.gperf" { "Scaron", 667 }, #line 254 "poppler/HelveticaBoldWidths.gperf" { "eogonek", 556 }, #line 80 "poppler/HelveticaBoldWidths.gperf" { "question", 611 }, { "", 0 }, { "", 0 }, #line 271 "poppler/HelveticaBoldWidths.gperf" { "Yacute", 667 }, #line 39 "poppler/HelveticaBoldWidths.gperf" { "questiondown", 611 }, { "", 0 }, #line 267 "poppler/HelveticaBoldWidths.gperf" { "plus", 584 }, { "", 0 }, #line 18 "poppler/HelveticaBoldWidths.gperf" { "Zacute", 611 }, { "", 0 }, #line 149 "poppler/HelveticaBoldWidths.gperf" { "ellipsis", 1000 }, { "", 0 }, { "", 0 }, #line 320 "poppler/HelveticaBoldWidths.gperf" { "exclam", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 156 "poppler/HelveticaBoldWidths.gperf" { "hyphen", 333 }, #line 48 "poppler/HelveticaBoldWidths.gperf" { "aogonek", 556 }, { "", 0 }, { "", 0 }, #line 328 "poppler/HelveticaBoldWidths.gperf" { "Edotaccent", 667 }, #line 153 "poppler/HelveticaBoldWidths.gperf" { "lslash", 278 }, { "", 0 }, #line 310 "poppler/HelveticaBoldWidths.gperf" { "currency", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 206 "poppler/HelveticaBoldWidths.gperf" { "dbldaggerumlaut", 556 }, #line 187 "poppler/HelveticaBoldWidths.gperf" { "egrave", 556 }, #line 27 "poppler/HelveticaBoldWidths.gperf" { "onehalf", 834 }, #line 109 "poppler/HelveticaBoldWidths.gperf" { "uhungarumlaut", 611 }, { "", 0 }, { "", 0 }, #line 147 "poppler/HelveticaBoldWidths.gperf" { "Otilde", 778 }, { "", 0 }, #line 288 "poppler/HelveticaBoldWidths.gperf" { "guillemotleft", 556 }, #line 202 "poppler/HelveticaBoldWidths.gperf" { "guillemotright", 556 }, { "", 0 }, #line 93 "poppler/HelveticaBoldWidths.gperf" { "bullet", 350 }, { "", 0 }, #line 190 "poppler/HelveticaBoldWidths.gperf" { "Eth", 722 }, { "", 0 }, { "", 0 }, #line 128 "poppler/HelveticaBoldWidths.gperf" { "Nacute", 722 }, { "", 0 }, #line 296 "poppler/HelveticaBoldWidths.gperf" { "notequal", 549 }, #line 143 "poppler/HelveticaBoldWidths.gperf" { "trademark", 1000 }, { "", 0 }, #line 76 "poppler/HelveticaBoldWidths.gperf" { "agrave", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 181 "poppler/HelveticaBoldWidths.gperf" { "perthousand", 1000 }, { "", 0 }, #line 241 "poppler/HelveticaBoldWidths.gperf" { "six", 556 }, { "", 0 }, { "", 0 }, #line 303 "poppler/HelveticaBoldWidths.gperf" { "icircumflex", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 199 "poppler/HelveticaBoldWidths.gperf" { "Zcaron", 611 }, { "", 0 }, { "", 0 }, #line 210 "poppler/HelveticaBoldWidths.gperf" { "Euro", 556 }, { "", 0 }, #line 108 "poppler/HelveticaBoldWidths.gperf" { "q", 611 }, #line 290 "poppler/HelveticaBoldWidths.gperf" { "Amacron", 722 }, { "", 0 }, { "", 0 }, #line 255 "poppler/HelveticaBoldWidths.gperf" { "Delta", 612 }, #line 231 "poppler/HelveticaBoldWidths.gperf" { "ograve", 611 }, { "", 0 }, #line 316 "poppler/HelveticaBoldWidths.gperf" { "fraction", 167 }, #line 326 "poppler/HelveticaBoldWidths.gperf" { "brokenbar", 280 }, { "", 0 }, #line 176 "poppler/HelveticaBoldWidths.gperf" { "ucircumflex", 611 }, #line 200 "poppler/HelveticaBoldWidths.gperf" { "Scommaaccent", 667 }, { "", 0 }, #line 197 "poppler/HelveticaBoldWidths.gperf" { "five", 556 }, { "", 0 }, #line 217 "poppler/HelveticaBoldWidths.gperf" { "Racute", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 268 "poppler/HelveticaBoldWidths.gperf" { "uring", 611 }, #line 45 "poppler/HelveticaBoldWidths.gperf" { "A", 722 }, { "", 0 }, #line 215 "poppler/HelveticaBoldWidths.gperf" { "Scedilla", 667 }, #line 84 "poppler/HelveticaBoldWidths.gperf" { "four", 556 }, { "", 0 }, #line 211 "poppler/HelveticaBoldWidths.gperf" { "Dcroat", 722 }, { "", 0 }, { "", 0 }, #line 242 "poppler/HelveticaBoldWidths.gperf" { "paragraph", 556 }, { "", 0 }, #line 78 "poppler/HelveticaBoldWidths.gperf" { "W", 944 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 266 "poppler/HelveticaBoldWidths.gperf" { "Ncaron", 722 }, { "", 0 }, { "", 0 }, #line 165 "poppler/HelveticaBoldWidths.gperf" { "Adieresis", 722 }, #line 127 "poppler/HelveticaBoldWidths.gperf" { "Idotaccent", 278 }, #line 122 "poppler/HelveticaBoldWidths.gperf" { "y", 556 }, #line 16 "poppler/HelveticaBoldWidths.gperf" { "kcommaaccent", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 111 "poppler/HelveticaBoldWidths.gperf" { "twosuperior", 333 }, #line 148 "poppler/HelveticaBoldWidths.gperf" { "Emacron", 667 }, { "", 0 }, #line 204 "poppler/HelveticaBoldWidths.gperf" { "ydieresis", 556 }, #line 168 "poppler/HelveticaBoldWidths.gperf" { "slash", 278 }, #line 230 "poppler/HelveticaBoldWidths.gperf" { "ogonek", 333 }, { "", 0 }, #line 192 "poppler/HelveticaBoldWidths.gperf" { "asterisk", 389 }, { "", 0 }, #line 299 "poppler/HelveticaBoldWidths.gperf" { "Zdotaccent", 611 }, #line 225 "poppler/HelveticaBoldWidths.gperf" { "Dcaron", 722 }, { "", 0 }, { "", 0 }, #line 59 "poppler/HelveticaBoldWidths.gperf" { "backslash", 278 }, { "", 0 }, #line 120 "poppler/HelveticaBoldWidths.gperf" { "w", 778 }, #line 58 "poppler/HelveticaBoldWidths.gperf" { "iogonek", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 21 "poppler/HelveticaBoldWidths.gperf" { "plusminus", 584 }, { "", 0 }, #line 14 "poppler/HelveticaBoldWidths.gperf" { "Ntilde", 722 }, { "", 0 }, #line 129 "poppler/HelveticaBoldWidths.gperf" { "quotedbl", 474 }, { "", 0 }, { "", 0 }, #line 322 "poppler/HelveticaBoldWidths.gperf" { "Rcaron", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 77 "poppler/HelveticaBoldWidths.gperf" { "V", 667 }, #line 260 "poppler/HelveticaBoldWidths.gperf" { "uogonek", 611 }, { "", 0 }, #line 169 "poppler/HelveticaBoldWidths.gperf" { "Edieresis", 667 }, #line 81 "poppler/HelveticaBoldWidths.gperf" { "equal", 584 }, { "", 0 }, #line 38 "poppler/HelveticaBoldWidths.gperf" { "Omacron", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 121 "poppler/HelveticaBoldWidths.gperf" { "x", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 264 "poppler/HelveticaBoldWidths.gperf" { "igrave", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 68 "poppler/HelveticaBoldWidths.gperf" { "Q", 778 }, #line 163 "poppler/HelveticaBoldWidths.gperf" { "Umacron", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 50 "poppler/HelveticaBoldWidths.gperf" { "E", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 22 "poppler/HelveticaBoldWidths.gperf" { "circumflex", 333 }, #line 195 "poppler/HelveticaBoldWidths.gperf" { "ugrave", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 87 "poppler/HelveticaBoldWidths.gperf" { "b", 611 }, #line 17 "poppler/HelveticaBoldWidths.gperf" { "Ncommaaccent", 722 }, { "", 0 }, #line 175 "poppler/HelveticaBoldWidths.gperf" { "Odieresis", 778 }, { "", 0 }, #line 285 "poppler/HelveticaBoldWidths.gperf" { "Acircumflex", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 67 "poppler/HelveticaBoldWidths.gperf" { "P", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 239 "poppler/HelveticaBoldWidths.gperf" { "Aring", 722 }, #line 96 "poppler/HelveticaBoldWidths.gperf" { "Oslash", 778 }, #line 297 "poppler/HelveticaBoldWidths.gperf" { "Imacron", 278 }, { "", 0 }, #line 180 "poppler/HelveticaBoldWidths.gperf" { "Udieresis", 722 }, { "", 0 }, #line 62 "poppler/HelveticaBoldWidths.gperf" { "M", 833 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 124 "poppler/HelveticaBoldWidths.gperf" { "Gbreve", 778 }, #line 283 "poppler/HelveticaBoldWidths.gperf" { "fi", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 119 "poppler/HelveticaBoldWidths.gperf" { "v", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 184 "poppler/HelveticaBoldWidths.gperf" { "abreve", 556 }, #line 282 "poppler/HelveticaBoldWidths.gperf" { "Rcommaaccent", 722 }, #line 256 "poppler/HelveticaBoldWidths.gperf" { "Ohungarumlaut", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 141 "poppler/HelveticaBoldWidths.gperf" { "Ecircumflex", 667 }, { "", 0 }, { "", 0 }, #line 171 "poppler/HelveticaBoldWidths.gperf" { "Idieresis", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 69 "poppler/HelveticaBoldWidths.gperf" { "Uhungarumlaut", 722 }, { "", 0 }, { "", 0 }, #line 73 "poppler/HelveticaBoldWidths.gperf" { "S", 667 }, { "", 0 }, { "", 0 }, #line 182 "poppler/HelveticaBoldWidths.gperf" { "Ydieresis", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 212 "poppler/HelveticaBoldWidths.gperf" { "multiply", 584 }, { "", 0 }, { "", 0 }, #line 66 "poppler/HelveticaBoldWidths.gperf" { "O", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 31 "poppler/HelveticaBoldWidths.gperf" { "Aogonek", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 280 "poppler/HelveticaBoldWidths.gperf" { "florin", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 227 "poppler/HelveticaBoldWidths.gperf" { "Ocircumflex", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 284 "poppler/HelveticaBoldWidths.gperf" { "fl", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 75 "poppler/HelveticaBoldWidths.gperf" { "U", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 152 "poppler/HelveticaBoldWidths.gperf" { "Ucircumflex", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 41 "poppler/HelveticaBoldWidths.gperf" { "Agrave", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 251 "poppler/HelveticaBoldWidths.gperf" { "Uring", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 99 "poppler/HelveticaBoldWidths.gperf" { "k", 556 }, #line 234 "poppler/HelveticaBoldWidths.gperf" { "Eogonek", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 56 "poppler/HelveticaBoldWidths.gperf" { "J", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 218 "poppler/HelveticaBoldWidths.gperf" { "partialdiff", 494 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 287 "poppler/HelveticaBoldWidths.gperf" { "Icircumflex", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 91 "poppler/HelveticaBoldWidths.gperf" { "f", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 167 "poppler/HelveticaBoldWidths.gperf" { "Egrave", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 142 "poppler/HelveticaBoldWidths.gperf" { "gbreve", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 55 "poppler/HelveticaBoldWidths.gperf" { "I", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 244 "poppler/HelveticaBoldWidths.gperf" { "Uogonek", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 177 "poppler/HelveticaBoldWidths.gperf" { "bracketleft", 333 }, #line 261 "poppler/HelveticaBoldWidths.gperf" { "bracketright", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 82 "poppler/HelveticaBoldWidths.gperf" { "Y", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 216 "poppler/HelveticaBoldWidths.gperf" { "Ograve", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 83 "poppler/HelveticaBoldWidths.gperf" { "Z", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 178 "poppler/HelveticaBoldWidths.gperf" { "Ugrave", 722 }, #line 34 "poppler/HelveticaBoldWidths.gperf" { "Iogonek", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 54 "poppler/HelveticaBoldWidths.gperf" { "H", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 52 "poppler/HelveticaBoldWidths.gperf" { "F", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 46 "poppler/HelveticaBoldWidths.gperf" { "B", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 247 "poppler/HelveticaBoldWidths.gperf" { "Igrave", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 151 "poppler/HelveticaBoldWidths.gperf" { "AE", 1000 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 63 "poppler/HelveticaBoldWidths.gperf" { "N", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 162 "poppler/HelveticaBoldWidths.gperf" { "Abreve", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 49 "poppler/HelveticaBoldWidths.gperf" { "D", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 114 "poppler/HelveticaBoldWidths.gperf" { "OE", 1000 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 70 "poppler/HelveticaBoldWidths.gperf" { "R", 722 } }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) { register unsigned int key = hash(str, len); if (key <= MAX_HASH_VALUE) { register const char *s = wordlist[key].name; if (*str == *s && !strcmp(str + 1, s + 1)) return &wordlist[key]; } } return 0; } #line 331 "poppler/HelveticaBoldWidths.gperf" poppler-24.02.0/poppler/HelveticaObliqueWidths.gperf000066400000000000000000000101541455701731300224650ustar00rootroot00000000000000%{ #include #include "BuiltinFontWidth.h" %} %language=ANSI-C %define initializer-suffix ,0 %define lookup-function-name HelveticaObliqueWidthsLookup %struct-type %omit-struct-type %readonly-tables struct BuiltinFontWidth %% #### Ntilde, 722 rcaron, 333 kcommaaccent, 500 Ncommaaccent, 722 Zacute, 611 comma, 278 cedilla, 333 plusminus, 584 circumflex, 333 dotaccent, 333 edotaccent, 556 asciitilde, 584 colon, 278 onehalf, 834 dollar, 556 Lcaron, 556 ntilde, 556 Aogonek, 667 ncommaaccent, 556 minus, 584 Iogonek, 278 zacute, 500 yen, 556 space, 278 Omacron, 778 questiondown, 611 emdash, 1000 Agrave, 667 three, 556 numbersign, 556 lcaron, 299 A, 667 B, 667 C, 722 aogonek, 556 D, 722 E, 667 onequarter, 834 F, 611 G, 778 H, 722 I, 278 J, 500 K, 667 iogonek, 222 backslash, 278 L, 556 periodcentered, 278 M, 833 N, 722 omacron, 556 Tcommaaccent, 611 O, 778 P, 667 Q, 778 Uhungarumlaut, 722 R, 722 Aacute, 667 caron, 333 S, 667 T, 611 U, 722 agrave, 556 V, 667 W, 944 X, 667 question, 556 equal, 584 Y, 667 Z, 611 four, 556 a, 556 Gcommaaccent, 778 b, 556 c, 500 d, 556 e, 556 f, 278 g, 556 bullet, 350 h, 556 i, 222 Oslash, 778 dagger, 556 j, 222 k, 500 l, 222 m, 833 n, 556 tcommaaccent, 278 o, 556 ordfeminine, 370 ring, 333 p, 556 q, 556 uhungarumlaut, 556 r, 333 twosuperior, 333 aacute, 556 s, 500 OE, 1000 t, 278 divide, 584 u, 556 Ccaron, 722 v, 500 w, 722 x, 500 y, 500 z, 500 Gbreve, 778 commaaccent, 250 hungarumlaut, 333 Idotaccent, 278 Nacute, 722 quotedbl, 355 gcommaaccent, 556 mu, 556 greaterequal, 549 Scaron, 667 Lslash, 556 semicolon, 278 oslash, 611 lessequal, 549 lozenge, 471 parenright, 333 ccaron, 500 Ecircumflex, 667 gbreve, 556 trademark, 1000 daggerdbl, 556 nacute, 556 macron, 333 Otilde, 778 Emacron, 667 ellipsis, 1000 scaron, 500 AE, 1000 Ucircumflex, 722 lslash, 222 quotedblleft, 333 guilsinglright, 333 hyphen, 333 quotesingle, 191 eight, 556 exclamdown, 333 endash, 556 oe, 944 Abreve, 667 Umacron, 722 ecircumflex, 556 Adieresis, 667 copyright, 737 Egrave, 667 slash, 278 Edieresis, 667 otilde, 556 Idieresis, 278 parenleft, 333 one, 556 emacron, 556 Odieresis, 778 ucircumflex, 556 bracketleft, 278 Ugrave, 722 quoteright, 222 Udieresis, 722 perthousand, 1000 Ydieresis, 667 umacron, 556 abreve, 556 Eacute, 667 adieresis, 556 egrave, 556 edieresis, 556 idieresis, 278 Eth, 722 ae, 889 asterisk, 389 odieresis, 556 Uacute, 722 ugrave, 556 nine, 556 five, 556 udieresis, 556 Zcaron, 611 Scommaaccent, 667 threequarters, 834 guillemotright, 556 Ccedilla, 722 ydieresis, 500 tilde, 333 at, 1015 eacute, 556 underscore, 556 Euro, 556 Dcroat, 722 multiply, 584 zero, 556 eth, 556 Scedilla, 667 Ograve, 778 Racute, 722 partialdiff, 476 uacute, 556 braceleft, 334 Thorn, 667 zcaron, 500 scommaaccent, 500 ccedilla, 500 Dcaron, 722 dcroat, 556 Ocircumflex, 778 Oacute, 778 scedilla, 500 ogonek, 333 ograve, 556 racute, 333 Tcaron, 611 Eogonek, 667 thorn, 556 degree, 400 registered, 737 radical, 453 Aring, 667 percent, 889 six, 556 paragraph, 537 dcaron, 643 Uogonek, 722 two, 556 summation, 600 Igrave, 278 Lacute, 556 ocircumflex, 556 oacute, 556 Uring, 722 Lcommaaccent, 556 tcaron, 317 eogonek, 556 Delta, 612 Ohungarumlaut, 778 asciicircum, 469 aring, 556 grave, 333 uogonek, 556 bracketright, 278 Iacute, 278 ampersand, 667 igrave, 278 lacute, 222 Ncaron, 722 plus, 584 uring, 556 quotesinglbase, 222 lcommaaccent, 222 Yacute, 667 ohungarumlaut, 556 threesuperior, 333 acute, 333 section, 556 dieresis, 333 iacute, 278 quotedblbase, 333 ncaron, 556 florin, 556 yacute, 500 Rcommaaccent, 722 fi, 500 fl, 500 Acircumflex, 667 Cacute, 722 Icircumflex, 278 guillemotleft, 556 germandbls, 611 Amacron, 667 seven, 556 Sacute, 667 ordmasculine, 365 dotlessi, 278 sterling, 556 notequal, 549 Imacron, 278 rcommaaccent, 333 Zdotaccent, 611 acircumflex, 556 cacute, 500 Ecaron, 667 icircumflex, 278 braceright, 334 quotedblright, 333 amacron, 556 sacute, 500 imacron, 278 cent, 556 currency, 556 logicalnot, 584 zdotaccent, 500 Atilde, 667 breve, 333 bar, 260 fraction, 167 less, 584 ecaron, 556 guilsinglleft, 333 exclam, 278 period, 278 Rcaron, 722 Kcommaaccent, 667 greater, 584 atilde, 556 brokenbar, 260 quoteleft, 222 Edotaccent, 667 onesuperior, 333 #### %% poppler-24.02.0/poppler/HelveticaObliqueWidths.pregenerated.c000066400000000000000000002655571455701731300242730ustar00rootroot00000000000000/* ANSI-C code produced by gperf version 3.1 */ /* Command-line: gperf poppler/HelveticaObliqueWidths.gperf */ /* Computed positions: -k'1-2,5,$' */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) && ('-' == 45) && ('.' == 46) && ('/' == 47) \ && ('0' == 48) && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) && ('=' == 61) && ('>' == 62) \ && ('?' == 63) && ('A' == 65) && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) && ('N' == 78) \ && ('O' == 79) && ('P' == 80) && ('Q' == 81) && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) && ('k' == 107) && ('l' == 108) \ && ('m' == 109) && ('n' == 110) && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) /* The character set is not based on ISO-646. */ # error "gperf generated tables don't work with this execution character set. Please report a bug to ." #endif #line 1 "poppler/HelveticaObliqueWidths.gperf" #include #include "BuiltinFontWidth.h" #define TOTAL_KEYWORDS 315 #define MIN_WORD_LENGTH 1 #define MAX_WORD_LENGTH 14 #define MIN_HASH_VALUE 1 #define MAX_HASH_VALUE 1041 /* maximum key range = 1041, duplicates = 0 */ #ifdef __GNUC__ __inline #else # ifdef __cplusplus inline # endif #endif static unsigned int hash(register const char *str, register size_t len) { static const unsigned short asso_values[] = { 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 270, 415, 28, 8, 150, 390, 290, 375, 370, 335, 5, 455, 330, 405, 355, 325, 310, 3, 320, 160, 240, 225, 145, 70, 410, 460, 1042, 1042, 1042, 1042, 1042, 1042, 20, 345, 30, 115, 0, 395, 140, 165, 135, 35, 380, 170, 130, 15, 45, 215, 260, 100, 65, 10, 155, 400, 300, 305, 280, 315, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042 }; register unsigned int hval = len; switch (hval) { default: hval += asso_values[(unsigned char)str[4]]; /*FALLTHROUGH*/ case 4: case 3: case 2: hval += asso_values[(unsigned char)str[1]]; /*FALLTHROUGH*/ case 1: hval += asso_values[(unsigned char)str[0]]; break; } return hval + asso_values[(unsigned char)str[len - 1]]; } const struct BuiltinFontWidth *HelveticaObliqueWidthsLookup(register const char *str, register size_t len) { static const struct BuiltinFontWidth wordlist[] = { { "", 0 }, #line 90 "poppler/HelveticaObliqueWidths.gperf" { "e", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 70 "poppler/HelveticaObliqueWidths.gperf" { "R", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 57 "poppler/HelveticaObliqueWidths.gperf" { "K", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 49 "poppler/HelveticaObliqueWidths.gperf" { "D", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 115 "poppler/HelveticaObliqueWidths.gperf" { "t", 278 }, #line 191 "poppler/HelveticaObliqueWidths.gperf" { "ae", 889 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 102 "poppler/HelveticaObliqueWidths.gperf" { "n", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 207 "poppler/HelveticaObliqueWidths.gperf" { "eacute", 556 }, { "", 0 }, { "", 0 }, #line 216 "poppler/HelveticaObliqueWidths.gperf" { "Racute", 722 }, { "", 0 }, #line 85 "poppler/HelveticaObliqueWidths.gperf" { "a", 556 }, #line 206 "poppler/HelveticaObliqueWidths.gperf" { "at", 1015 }, { "", 0 }, #line 308 "poppler/HelveticaObliqueWidths.gperf" { "cent", 556 }, { "", 0 }, { "", 0 }, #line 161 "poppler/HelveticaObliqueWidths.gperf" { "oe", 944 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 145 "poppler/HelveticaObliqueWidths.gperf" { "nacute", 556 }, { "", 0 }, #line 254 "poppler/HelveticaObliqueWidths.gperf" { "Delta", 612 }, { "", 0 }, #line 273 "poppler/HelveticaObliqueWidths.gperf" { "acute", 333 }, #line 112 "poppler/HelveticaObliqueWidths.gperf" { "aacute", 556 }, #line 47 "poppler/HelveticaObliqueWidths.gperf" { "C", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 88 "poppler/HelveticaObliqueWidths.gperf" { "c", 500 }, { "", 0 }, #line 173 "poppler/HelveticaObliqueWidths.gperf" { "one", 556 }, #line 285 "poppler/HelveticaObliqueWidths.gperf" { "Cacute", 722 }, { "", 0 }, #line 300 "poppler/HelveticaObliqueWidths.gperf" { "cacute", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 98 "poppler/HelveticaObliqueWidths.gperf" { "j", 222 }, { "", 0 }, { "", 0 }, #line 210 "poppler/HelveticaObliqueWidths.gperf" { "Dcroat", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 249 "poppler/HelveticaObliqueWidths.gperf" { "oacute", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 72 "poppler/HelveticaObliqueWidths.gperf" { "caron", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 104 "poppler/HelveticaObliqueWidths.gperf" { "o", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 317 "poppler/HelveticaObliqueWidths.gperf" { "ecaron", 556 }, { "", 0 }, { "", 0 }, #line 321 "poppler/HelveticaObliqueWidths.gperf" { "Rcaron", 722 }, #line 290 "poppler/HelveticaObliqueWidths.gperf" { "seven", 556 }, #line 306 "poppler/HelveticaObliqueWidths.gperf" { "sacute", 500 }, { "", 0 }, { "", 0 }, #line 224 "poppler/HelveticaObliqueWidths.gperf" { "Dcaron", 722 }, { "", 0 }, #line 252 "poppler/HelveticaObliqueWidths.gperf" { "tcaron", 317 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 26 "poppler/HelveticaObliqueWidths.gperf" { "colon", 278 }, #line 278 "poppler/HelveticaObliqueWidths.gperf" { "ncaron", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 125 "poppler/HelveticaObliqueWidths.gperf" { "commaaccent", 250 }, { "", 0 }, { "", 0 }, #line 135 "poppler/HelveticaObliqueWidths.gperf" { "semicolon", 278 }, #line 19 "poppler/HelveticaObliqueWidths.gperf" { "comma", 278 }, #line 235 "poppler/HelveticaObliqueWidths.gperf" { "degree", 400 }, { "", 0 }, { "", 0 }, #line 118 "poppler/HelveticaObliqueWidths.gperf" { "Ccaron", 722 }, { "", 0 }, #line 140 "poppler/HelveticaObliqueWidths.gperf" { "ccaron", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 113 "poppler/HelveticaObliqueWidths.gperf" { "s", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 231 "poppler/HelveticaObliqueWidths.gperf" { "racute", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 79 "poppler/HelveticaObliqueWidths.gperf" { "X", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 30 "poppler/HelveticaObliqueWidths.gperf" { "ntilde", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 205 "poppler/HelveticaObliqueWidths.gperf" { "tilde", 333 }, #line 324 "poppler/HelveticaObliqueWidths.gperf" { "atilde", 556 }, { "", 0 }, { "", 0 }, #line 196 "poppler/HelveticaObliqueWidths.gperf" { "nine", 556 }, #line 24 "poppler/HelveticaObliqueWidths.gperf" { "edotaccent", 556 }, #line 105 "poppler/HelveticaObliqueWidths.gperf" { "ordfeminine", 370 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 158 "poppler/HelveticaObliqueWidths.gperf" { "eight", 556 }, #line 150 "poppler/HelveticaObliqueWidths.gperf" { "scaron", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 276 "poppler/HelveticaObliqueWidths.gperf" { "iacute", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 170 "poppler/HelveticaObliqueWidths.gperf" { "otilde", 556 }, #line 292 "poppler/HelveticaObliqueWidths.gperf" { "ordmasculine", 365 }, #line 213 "poppler/HelveticaObliqueWidths.gperf" { "eth", 556 }, { "", 0 }, #line 42 "poppler/HelveticaObliqueWidths.gperf" { "three", 556 }, #line 225 "poppler/HelveticaObliqueWidths.gperf" { "dcroat", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 281 "poppler/HelveticaObliqueWidths.gperf" { "Rcommaaccent", 722 }, #line 185 "poppler/HelveticaObliqueWidths.gperf" { "Eacute", 667 }, #line 322 "poppler/HelveticaObliqueWidths.gperf" { "Kcommaaccent", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 218 "poppler/HelveticaObliqueWidths.gperf" { "uacute", 556 }, #line 103 "poppler/HelveticaObliqueWidths.gperf" { "tcommaaccent", 278 }, { "", 0 }, #line 166 "poppler/HelveticaObliqueWidths.gperf" { "copyright", 737 }, #line 43 "poppler/HelveticaObliqueWidths.gperf" { "numbersign", 556 }, #line 15 "poppler/HelveticaObliqueWidths.gperf" { "rcaron", 333 }, #line 32 "poppler/HelveticaObliqueWidths.gperf" { "ncommaaccent", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 110 "poppler/HelveticaObliqueWidths.gperf" { "r", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 264 "poppler/HelveticaObliqueWidths.gperf" { "lacute", 222 }, { "", 0 }, { "", 0 }, #line 23 "poppler/HelveticaObliqueWidths.gperf" { "dotaccent", 333 }, #line 234 "poppler/HelveticaObliqueWidths.gperf" { "thorn", 556 }, #line 242 "poppler/HelveticaObliqueWidths.gperf" { "dcaron", 643 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 146 "poppler/HelveticaObliqueWidths.gperf" { "macron", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 203 "poppler/HelveticaObliqueWidths.gperf" { "Ccedilla", 722 }, #line 274 "poppler/HelveticaObliqueWidths.gperf" { "section", 556 }, #line 223 "poppler/HelveticaObliqueWidths.gperf" { "ccedilla", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 20 "poppler/HelveticaObliqueWidths.gperf" { "cedilla", 333 }, { "", 0 }, { "", 0 }, #line 25 "poppler/HelveticaObliqueWidths.gperf" { "asciitilde", 584 }, #line 89 "poppler/HelveticaObliqueWidths.gperf" { "d", 556 }, #line 239 "poppler/HelveticaObliqueWidths.gperf" { "percent", 889 }, { "", 0 }, { "", 0 }, #line 288 "poppler/HelveticaObliqueWidths.gperf" { "germandbls", 611 }, { "", 0 }, #line 138 "poppler/HelveticaObliqueWidths.gperf" { "lozenge", 471 }, { "", 0 }, #line 316 "poppler/HelveticaObliqueWidths.gperf" { "less", 584 }, { "", 0 }, #line 97 "poppler/HelveticaObliqueWidths.gperf" { "dagger", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 258 "poppler/HelveticaObliqueWidths.gperf" { "grave", 333 }, #line 301 "poppler/HelveticaObliqueWidths.gperf" { "Ecaron", 667 }, #line 222 "poppler/HelveticaObliqueWidths.gperf" { "scommaaccent", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 160 "poppler/HelveticaObliqueWidths.gperf" { "endash", 556 }, #line 174 "poppler/HelveticaObliqueWidths.gperf" { "emacron", 556 }, #line 201 "poppler/HelveticaObliqueWidths.gperf" { "threequarters", 834 }, { "", 0 }, { "", 0 }, #line 232 "poppler/HelveticaObliqueWidths.gperf" { "Tcaron", 611 }, { "", 0 }, #line 228 "poppler/HelveticaObliqueWidths.gperf" { "scedilla", 500 }, { "", 0 }, { "", 0 }, #line 101 "poppler/HelveticaObliqueWidths.gperf" { "m", 833 }, { "", 0 }, { "", 0 }, #line 245 "poppler/HelveticaObliqueWidths.gperf" { "summation", 600 }, #line 310 "poppler/HelveticaObliqueWidths.gperf" { "logicalnot", 584 }, #line 44 "poppler/HelveticaObliqueWidths.gperf" { "lcaron", 299 }, { "", 0 }, { "", 0 }, #line 172 "poppler/HelveticaObliqueWidths.gperf" { "parenleft", 333 }, #line 139 "poppler/HelveticaObliqueWidths.gperf" { "parenright", 333 }, #line 95 "poppler/HelveticaObliqueWidths.gperf" { "i", 222 }, #line 305 "poppler/HelveticaObliqueWidths.gperf" { "amacron", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 194 "poppler/HelveticaObliqueWidths.gperf" { "Uacute", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 208 "poppler/HelveticaObliqueWidths.gperf" { "underscore", 556 }, #line 92 "poppler/HelveticaObliqueWidths.gperf" { "g", 556 }, #line 297 "poppler/HelveticaObliqueWidths.gperf" { "rcommaaccent", 333 }, { "", 0 }, { "", 0 }, #line 37 "poppler/HelveticaObliqueWidths.gperf" { "space", 278 }, #line 28 "poppler/HelveticaObliqueWidths.gperf" { "dollar", 556 }, { "", 0 }, #line 272 "poppler/HelveticaObliqueWidths.gperf" { "threesuperior", 333 }, #line 188 "poppler/HelveticaObliqueWidths.gperf" { "edieresis", 556 }, #line 236 "poppler/HelveticaObliqueWidths.gperf" { "registered", 737 }, #line 78 "poppler/HelveticaObliqueWidths.gperf" { "W", 944 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 64 "poppler/HelveticaObliqueWidths.gperf" { "omacron", 556 }, #line 36 "poppler/HelveticaObliqueWidths.gperf" { "yen", 556 }, { "", 0 }, { "", 0 }, #line 50 "poppler/HelveticaObliqueWidths.gperf" { "E", 667 }, { "", 0 }, #line 293 "poppler/HelveticaObliqueWidths.gperf" { "dotlessi", 278 }, { "", 0 }, #line 327 "poppler/HelveticaObliqueWidths.gperf" { "Edotaccent", 667 }, #line 71 "poppler/HelveticaObliqueWidths.gperf" { "Aacute", 667 }, { "", 0 }, { "", 0 }, #line 186 "poppler/HelveticaObliqueWidths.gperf" { "adieresis", 556 }, { "", 0 }, #line 117 "poppler/HelveticaObliqueWidths.gperf" { "u", 556 }, { "", 0 }, { "", 0 }, #line 144 "poppler/HelveticaObliqueWidths.gperf" { "daggerdbl", 556 }, { "", 0 }, #line 280 "poppler/HelveticaObliqueWidths.gperf" { "yacute", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 74 "poppler/HelveticaObliqueWidths.gperf" { "T", 611 }, #line 130 "poppler/HelveticaObliqueWidths.gperf" { "gcommaaccent", 556 }, #line 275 "poppler/HelveticaObliqueWidths.gperf" { "dieresis", 333 }, { "", 0 }, #line 51 "poppler/HelveticaObliqueWidths.gperf" { "onequarter", 834 }, #line 328 "poppler/HelveticaObliqueWidths.gperf" { "onesuperior", 333 }, #line 237 "poppler/HelveticaObliqueWidths.gperf" { "radical", 453 }, #line 190 "poppler/HelveticaObliqueWidths.gperf" { "Eth", 722 }, { "", 0 }, { "", 0 }, #line 94 "poppler/HelveticaObliqueWidths.gperf" { "h", 556 }, { "", 0 }, { "", 0 }, #line 193 "poppler/HelveticaObliqueWidths.gperf" { "odieresis", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 100 "poppler/HelveticaObliqueWidths.gperf" { "l", 222 }, #line 65 "poppler/HelveticaObliqueWidths.gperf" { "Tcommaaccent", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 136 "poppler/HelveticaObliqueWidths.gperf" { "oslash", 611 }, { "", 0 }, { "", 0 }, #line 137 "poppler/HelveticaObliqueWidths.gperf" { "lessequal", 549 }, #line 159 "poppler/HelveticaObliqueWidths.gperf" { "exclamdown", 333 }, #line 35 "poppler/HelveticaObliqueWidths.gperf" { "zacute", 500 }, #line 269 "poppler/HelveticaObliqueWidths.gperf" { "lcommaaccent", 222 }, { "", 0 }, #line 209 "poppler/HelveticaObliqueWidths.gperf" { "Euro", 556 }, { "", 0 }, #line 291 "poppler/HelveticaObliqueWidths.gperf" { "Sacute", 667 }, #line 323 "poppler/HelveticaObliqueWidths.gperf" { "greater", 584 }, #line 244 "poppler/HelveticaObliqueWidths.gperf" { "two", 556 }, { "", 0 }, #line 220 "poppler/HelveticaObliqueWidths.gperf" { "Thorn", 667 }, #line 256 "poppler/HelveticaObliqueWidths.gperf" { "asciicircum", 469 }, #line 126 "poppler/HelveticaObliqueWidths.gperf" { "hungarumlaut", 333 }, { "", 0 }, #line 212 "poppler/HelveticaObliqueWidths.gperf" { "zero", 556 }, { "", 0 }, #line 40 "poppler/HelveticaObliqueWidths.gperf" { "emdash", 1000 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 116 "poppler/HelveticaObliqueWidths.gperf" { "divide", 584 }, { "", 0 }, #line 271 "poppler/HelveticaObliqueWidths.gperf" { "ohungarumlaut", 556 }, #line 262 "poppler/HelveticaObliqueWidths.gperf" { "ampersand", 667 }, { "", 0 }, #line 164 "poppler/HelveticaObliqueWidths.gperf" { "ecircumflex", 556 }, { "", 0 }, { "", 0 }, #line 106 "poppler/HelveticaObliqueWidths.gperf" { "ring", 333 }, { "", 0 }, #line 320 "poppler/HelveticaObliqueWidths.gperf" { "period", 278 }, { "", 0 }, #line 318 "poppler/HelveticaObliqueWidths.gperf" { "guilsinglleft", 333 }, #line 155 "poppler/HelveticaObliqueWidths.gperf" { "guilsinglright", 333 }, { "", 0 }, { "", 0 }, #line 307 "poppler/HelveticaObliqueWidths.gperf" { "imacron", 278 }, { "", 0 }, #line 61 "poppler/HelveticaObliqueWidths.gperf" { "periodcentered", 278 }, { "", 0 }, #line 227 "poppler/HelveticaObliqueWidths.gperf" { "Oacute", 778 }, { "", 0 }, #line 294 "poppler/HelveticaObliqueWidths.gperf" { "sterling", 556 }, { "", 0 }, { "", 0 }, #line 299 "poppler/HelveticaObliqueWidths.gperf" { "acircumflex", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 33 "poppler/HelveticaObliqueWidths.gperf" { "minus", 584 }, #line 312 "poppler/HelveticaObliqueWidths.gperf" { "Atilde", 667 }, #line 148 "poppler/HelveticaObliqueWidths.gperf" { "Emacron", 667 }, { "", 0 }, { "", 0 }, #line 257 "poppler/HelveticaObliqueWidths.gperf" { "aring", 556 }, #line 261 "poppler/HelveticaObliqueWidths.gperf" { "Iacute", 278 }, #line 183 "poppler/HelveticaObliqueWidths.gperf" { "umacron", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 221 "poppler/HelveticaObliqueWidths.gperf" { "zcaron", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 133 "poppler/HelveticaObliqueWidths.gperf" { "Scaron", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 248 "poppler/HelveticaObliqueWidths.gperf" { "ocircumflex", 556 }, { "", 0 }, { "", 0 }, #line 189 "poppler/HelveticaObliqueWidths.gperf" { "idieresis", 278 }, { "", 0 }, #line 157 "poppler/HelveticaObliqueWidths.gperf" { "quotesingle", 191 }, #line 277 "poppler/HelveticaObliqueWidths.gperf" { "quotedblbase", 333 }, { "", 0 }, #line 268 "poppler/HelveticaObliqueWidths.gperf" { "quotesinglbase", 222 }, { "", 0 }, #line 107 "poppler/HelveticaObliqueWidths.gperf" { "p", 556 }, #line 132 "poppler/HelveticaObliqueWidths.gperf" { "greaterequal", 549 }, { "", 0 }, #line 326 "poppler/HelveticaObliqueWidths.gperf" { "quoteleft", 222 }, #line 179 "poppler/HelveticaObliqueWidths.gperf" { "quoteright", 222 }, { "", 0 }, #line 154 "poppler/HelveticaObliqueWidths.gperf" { "quotedblleft", 333 }, #line 304 "poppler/HelveticaObliqueWidths.gperf" { "quotedblright", 333 }, #line 169 "poppler/HelveticaObliqueWidths.gperf" { "Edieresis", 667 }, { "", 0 }, #line 128 "poppler/HelveticaObliqueWidths.gperf" { "Nacute", 722 }, #line 131 "poppler/HelveticaObliqueWidths.gperf" { "mu", 556 }, { "", 0 }, #line 198 "poppler/HelveticaObliqueWidths.gperf" { "udieresis", 556 }, { "", 0 }, #line 270 "poppler/HelveticaObliqueWidths.gperf" { "Yacute", 667 }, #line 253 "poppler/HelveticaObliqueWidths.gperf" { "eogonek", 556 }, #line 80 "poppler/HelveticaObliqueWidths.gperf" { "question", 556 }, { "", 0 }, #line 313 "poppler/HelveticaObliqueWidths.gperf" { "breve", 333 }, #line 77 "poppler/HelveticaObliqueWidths.gperf" { "V", 667 }, #line 39 "poppler/HelveticaObliqueWidths.gperf" { "questiondown", 611 }, { "", 0 }, #line 266 "poppler/HelveticaObliqueWidths.gperf" { "plus", 584 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 149 "poppler/HelveticaObliqueWidths.gperf" { "ellipsis", 1000 }, { "", 0 }, { "", 0 }, #line 319 "poppler/HelveticaObliqueWidths.gperf" { "exclam", 278 }, { "", 0 }, { "", 0 }, #line 219 "poppler/HelveticaObliqueWidths.gperf" { "braceleft", 334 }, #line 303 "poppler/HelveticaObliqueWidths.gperf" { "braceright", 334 }, #line 156 "poppler/HelveticaObliqueWidths.gperf" { "hyphen", 333 }, #line 48 "poppler/HelveticaObliqueWidths.gperf" { "aogonek", 556 }, #line 314 "poppler/HelveticaObliqueWidths.gperf" { "bar", 260 }, { "", 0 }, #line 311 "poppler/HelveticaObliqueWidths.gperf" { "zdotaccent", 500 }, #line 153 "poppler/HelveticaObliqueWidths.gperf" { "lslash", 222 }, #line 86 "poppler/HelveticaObliqueWidths.gperf" { "Gcommaaccent", 778 }, #line 309 "poppler/HelveticaObliqueWidths.gperf" { "currency", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 75 "poppler/HelveticaObliqueWidths.gperf" { "U", 722 }, #line 27 "poppler/HelveticaObliqueWidths.gperf" { "onehalf", 834 }, #line 109 "poppler/HelveticaObliqueWidths.gperf" { "uhungarumlaut", 556 }, { "", 0 }, { "", 0 }, #line 147 "poppler/HelveticaObliqueWidths.gperf" { "Otilde", 778 }, { "", 0 }, #line 287 "poppler/HelveticaObliqueWidths.gperf" { "guillemotleft", 556 }, #line 202 "poppler/HelveticaObliqueWidths.gperf" { "guillemotright", 556 }, { "", 0 }, #line 247 "poppler/HelveticaObliqueWidths.gperf" { "Lacute", 556 }, #line 163 "poppler/HelveticaObliqueWidths.gperf" { "Umacron", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 18 "poppler/HelveticaObliqueWidths.gperf" { "Zacute", 611 }, { "", 0 }, #line 295 "poppler/HelveticaObliqueWidths.gperf" { "notequal", 549 }, #line 143 "poppler/HelveticaObliqueWidths.gperf" { "trademark", 1000 }, { "", 0 }, #line 265 "poppler/HelveticaObliqueWidths.gperf" { "Ncaron", 722 }, #line 200 "poppler/HelveticaObliqueWidths.gperf" { "Scommaaccent", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 181 "poppler/HelveticaObliqueWidths.gperf" { "perthousand", 1000 }, { "", 0 }, #line 240 "poppler/HelveticaObliqueWidths.gperf" { "six", 556 }, { "", 0 }, { "", 0 }, #line 302 "poppler/HelveticaObliqueWidths.gperf" { "icircumflex", 278 }, { "", 0 }, #line 214 "poppler/HelveticaObliqueWidths.gperf" { "Scedilla", 667 }, { "", 0 }, { "", 0 }, #line 93 "poppler/HelveticaObliqueWidths.gperf" { "bullet", 350 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 108 "poppler/HelveticaObliqueWidths.gperf" { "q", 556 }, #line 289 "poppler/HelveticaObliqueWidths.gperf" { "Amacron", 667 }, { "", 0 }, { "", 0 }, #line 127 "poppler/HelveticaObliqueWidths.gperf" { "Idotaccent", 278 }, #line 141 "poppler/HelveticaObliqueWidths.gperf" { "Ecircumflex", 667 }, { "", 0 }, #line 315 "poppler/HelveticaObliqueWidths.gperf" { "fraction", 167 }, #line 180 "poppler/HelveticaObliqueWidths.gperf" { "Udieresis", 722 }, { "", 0 }, #line 176 "poppler/HelveticaObliqueWidths.gperf" { "ucircumflex", 556 }, { "", 0 }, { "", 0 }, #line 197 "poppler/HelveticaObliqueWidths.gperf" { "five", 556 }, { "", 0 }, #line 14 "poppler/HelveticaObliqueWidths.gperf" { "Ntilde", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 267 "poppler/HelveticaObliqueWidths.gperf" { "uring", 556 }, #line 45 "poppler/HelveticaObliqueWidths.gperf" { "A", 667 }, { "", 0 }, { "", 0 }, #line 84 "poppler/HelveticaObliqueWidths.gperf" { "four", 556 }, { "", 0 }, #line 187 "poppler/HelveticaObliqueWidths.gperf" { "egrave", 556 }, { "", 0 }, { "", 0 }, #line 241 "poppler/HelveticaObliqueWidths.gperf" { "paragraph", 537 }, { "", 0 }, #line 29 "poppler/HelveticaObliqueWidths.gperf" { "Lcaron", 556 }, { "", 0 }, { "", 0 }, #line 325 "poppler/HelveticaObliqueWidths.gperf" { "brokenbar", 260 }, { "", 0 }, #line 199 "poppler/HelveticaObliqueWidths.gperf" { "Zcaron", 611 }, { "", 0 }, { "", 0 }, #line 165 "poppler/HelveticaObliqueWidths.gperf" { "Adieresis", 667 }, { "", 0 }, #line 122 "poppler/HelveticaObliqueWidths.gperf" { "y", 500 }, #line 16 "poppler/HelveticaObliqueWidths.gperf" { "kcommaaccent", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 76 "poppler/HelveticaObliqueWidths.gperf" { "agrave", 556 }, { "", 0 }, #line 69 "poppler/HelveticaObliqueWidths.gperf" { "Uhungarumlaut", 722 }, #line 204 "poppler/HelveticaObliqueWidths.gperf" { "ydieresis", 500 }, #line 168 "poppler/HelveticaObliqueWidths.gperf" { "slash", 278 }, #line 229 "poppler/HelveticaObliqueWidths.gperf" { "ogonek", 333 }, #line 151 "poppler/HelveticaObliqueWidths.gperf" { "AE", 1000 }, #line 192 "poppler/HelveticaObliqueWidths.gperf" { "asterisk", 389 }, { "", 0 }, { "", 0 }, #line 111 "poppler/HelveticaObliqueWidths.gperf" { "twosuperior", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 53 "poppler/HelveticaObliqueWidths.gperf" { "G", 778 }, #line 58 "poppler/HelveticaObliqueWidths.gperf" { "iogonek", 222 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 17 "poppler/HelveticaObliqueWidths.gperf" { "Ncommaaccent", 722 }, { "", 0 }, #line 21 "poppler/HelveticaObliqueWidths.gperf" { "plusminus", 584 }, { "", 0 }, #line 230 "poppler/HelveticaObliqueWidths.gperf" { "ograve", 556 }, { "", 0 }, #line 129 "poppler/HelveticaObliqueWidths.gperf" { "quotedbl", 355 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 233 "poppler/HelveticaObliqueWidths.gperf" { "Eogonek", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 120 "poppler/HelveticaObliqueWidths.gperf" { "w", 722 }, #line 259 "poppler/HelveticaObliqueWidths.gperf" { "uogonek", 556 }, { "", 0 }, #line 59 "poppler/HelveticaObliqueWidths.gperf" { "backslash", 278 }, #line 81 "poppler/HelveticaObliqueWidths.gperf" { "equal", 584 }, { "", 0 }, #line 38 "poppler/HelveticaObliqueWidths.gperf" { "Omacron", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 121 "poppler/HelveticaObliqueWidths.gperf" { "x", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 298 "poppler/HelveticaObliqueWidths.gperf" { "Zdotaccent", 611 }, #line 152 "poppler/HelveticaObliqueWidths.gperf" { "Ucircumflex", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 68 "poppler/HelveticaObliqueWidths.gperf" { "Q", 778 }, #line 296 "poppler/HelveticaObliqueWidths.gperf" { "Imacron", 278 }, { "", 0 }, { "", 0 }, #line 250 "poppler/HelveticaObliqueWidths.gperf" { "Uring", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 123 "poppler/HelveticaObliqueWidths.gperf" { "z", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 22 "poppler/HelveticaObliqueWidths.gperf" { "circumflex", 333 }, { "", 0 }, #line 251 "poppler/HelveticaObliqueWidths.gperf" { "Lcommaaccent", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 73 "poppler/HelveticaObliqueWidths.gperf" { "S", 667 }, { "", 0 }, { "", 0 }, #line 175 "poppler/HelveticaObliqueWidths.gperf" { "Odieresis", 778 }, { "", 0 }, #line 284 "poppler/HelveticaObliqueWidths.gperf" { "Acircumflex", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 67 "poppler/HelveticaObliqueWidths.gperf" { "P", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 238 "poppler/HelveticaObliqueWidths.gperf" { "Aring", 667 }, #line 96 "poppler/HelveticaObliqueWidths.gperf" { "Oslash", 778 }, #line 114 "poppler/HelveticaObliqueWidths.gperf" { "OE", 1000 }, { "", 0 }, #line 171 "poppler/HelveticaObliqueWidths.gperf" { "Idieresis", 278 }, { "", 0 }, #line 62 "poppler/HelveticaObliqueWidths.gperf" { "M", 833 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 282 "poppler/HelveticaObliqueWidths.gperf" { "fi", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 56 "poppler/HelveticaObliqueWidths.gperf" { "J", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 263 "poppler/HelveticaObliqueWidths.gperf" { "igrave", 278 }, { "", 0 }, #line 255 "poppler/HelveticaObliqueWidths.gperf" { "Ohungarumlaut", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 243 "poppler/HelveticaObliqueWidths.gperf" { "Uogonek", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 87 "poppler/HelveticaObliqueWidths.gperf" { "b", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 167 "poppler/HelveticaObliqueWidths.gperf" { "Egrave", 667 }, { "", 0 }, { "", 0 }, #line 182 "poppler/HelveticaObliqueWidths.gperf" { "Ydieresis", 667 }, { "", 0 }, #line 195 "poppler/HelveticaObliqueWidths.gperf" { "ugrave", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 211 "poppler/HelveticaObliqueWidths.gperf" { "multiply", 584 }, { "", 0 }, { "", 0 }, #line 66 "poppler/HelveticaObliqueWidths.gperf" { "O", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 31 "poppler/HelveticaObliqueWidths.gperf" { "Aogonek", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 279 "poppler/HelveticaObliqueWidths.gperf" { "florin", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 226 "poppler/HelveticaObliqueWidths.gperf" { "Ocircumflex", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 283 "poppler/HelveticaObliqueWidths.gperf" { "fl", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 55 "poppler/HelveticaObliqueWidths.gperf" { "I", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 286 "poppler/HelveticaObliqueWidths.gperf" { "Icircumflex", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 54 "poppler/HelveticaObliqueWidths.gperf" { "H", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 134 "poppler/HelveticaObliqueWidths.gperf" { "Lslash", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 99 "poppler/HelveticaObliqueWidths.gperf" { "k", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 184 "poppler/HelveticaObliqueWidths.gperf" { "abreve", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 217 "poppler/HelveticaObliqueWidths.gperf" { "partialdiff", 476 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 52 "poppler/HelveticaObliqueWidths.gperf" { "F", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 178 "poppler/HelveticaObliqueWidths.gperf" { "Ugrave", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 91 "poppler/HelveticaObliqueWidths.gperf" { "f", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 119 "poppler/HelveticaObliqueWidths.gperf" { "v", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 63 "poppler/HelveticaObliqueWidths.gperf" { "N", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 41 "poppler/HelveticaObliqueWidths.gperf" { "Agrave", 667 }, #line 34 "poppler/HelveticaObliqueWidths.gperf" { "Iogonek", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 82 "poppler/HelveticaObliqueWidths.gperf" { "Y", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 46 "poppler/HelveticaObliqueWidths.gperf" { "B", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 177 "poppler/HelveticaObliqueWidths.gperf" { "bracketleft", 278 }, #line 260 "poppler/HelveticaObliqueWidths.gperf" { "bracketright", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 142 "poppler/HelveticaObliqueWidths.gperf" { "gbreve", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 215 "poppler/HelveticaObliqueWidths.gperf" { "Ograve", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 60 "poppler/HelveticaObliqueWidths.gperf" { "L", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 246 "poppler/HelveticaObliqueWidths.gperf" { "Igrave", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 83 "poppler/HelveticaObliqueWidths.gperf" { "Z", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 162 "poppler/HelveticaObliqueWidths.gperf" { "Abreve", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 124 "poppler/HelveticaObliqueWidths.gperf" { "Gbreve", 778 } }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) { register unsigned int key = hash(str, len); if (key <= MAX_HASH_VALUE) { register const char *s = wordlist[key].name; if (*str == *s && !strcmp(str + 1, s + 1)) return &wordlist[key]; } } return 0; } #line 330 "poppler/HelveticaObliqueWidths.gperf" poppler-24.02.0/poppler/HelveticaWidths.gperf000066400000000000000000000101451455701731300211440ustar00rootroot00000000000000%{ #include #include "BuiltinFontWidth.h" %} %language=ANSI-C %define initializer-suffix ,0 %define lookup-function-name HelveticaWidthsLookup %struct-type %omit-struct-type %readonly-tables struct BuiltinFontWidth %% #### Ntilde, 722 rcaron, 333 kcommaaccent, 500 Ncommaaccent, 722 Zacute, 611 comma, 278 cedilla, 333 plusminus, 584 circumflex, 333 dotaccent, 333 edotaccent, 556 asciitilde, 584 colon, 278 onehalf, 834 dollar, 556 Lcaron, 556 ntilde, 556 Aogonek, 667 ncommaaccent, 556 minus, 584 Iogonek, 278 zacute, 500 yen, 556 space, 278 Omacron, 778 questiondown, 611 emdash, 1000 Agrave, 667 three, 556 numbersign, 556 lcaron, 299 A, 667 B, 667 C, 722 aogonek, 556 D, 722 E, 667 onequarter, 834 F, 611 G, 778 H, 722 I, 278 J, 500 K, 667 iogonek, 222 backslash, 278 L, 556 periodcentered, 278 M, 833 N, 722 omacron, 556 Tcommaaccent, 611 O, 778 P, 667 Q, 778 Uhungarumlaut, 722 R, 722 Aacute, 667 caron, 333 S, 667 T, 611 U, 722 agrave, 556 V, 667 W, 944 X, 667 question, 556 equal, 584 Y, 667 Z, 611 four, 556 a, 556 Gcommaaccent, 778 b, 556 c, 500 d, 556 e, 556 f, 278 g, 556 bullet, 350 h, 556 i, 222 Oslash, 778 dagger, 556 j, 222 k, 500 l, 222 m, 833 n, 556 tcommaaccent, 278 o, 556 ordfeminine, 370 ring, 333 p, 556 q, 556 uhungarumlaut, 556 r, 333 twosuperior, 333 aacute, 556 s, 500 OE, 1000 t, 278 divide, 584 u, 556 Ccaron, 722 v, 500 w, 722 x, 500 y, 500 z, 500 Gbreve, 778 commaaccent, 250 hungarumlaut, 333 Idotaccent, 278 Nacute, 722 quotedbl, 355 gcommaaccent, 556 mu, 556 greaterequal, 549 Scaron, 667 Lslash, 556 semicolon, 278 oslash, 611 lessequal, 549 lozenge, 471 parenright, 333 ccaron, 500 Ecircumflex, 667 gbreve, 556 trademark, 1000 daggerdbl, 556 nacute, 556 macron, 333 Otilde, 778 Emacron, 667 ellipsis, 1000 scaron, 500 AE, 1000 Ucircumflex, 722 lslash, 222 quotedblleft, 333 guilsinglright, 333 hyphen, 333 quotesingle, 191 eight, 556 exclamdown, 333 endash, 556 oe, 944 Abreve, 667 Umacron, 722 ecircumflex, 556 Adieresis, 667 copyright, 737 Egrave, 667 slash, 278 Edieresis, 667 otilde, 556 Idieresis, 278 parenleft, 333 one, 556 emacron, 556 Odieresis, 778 ucircumflex, 556 bracketleft, 278 Ugrave, 722 quoteright, 222 Udieresis, 722 perthousand, 1000 Ydieresis, 667 umacron, 556 abreve, 556 Eacute, 667 adieresis, 556 egrave, 556 edieresis, 556 idieresis, 278 Eth, 722 ae, 889 asterisk, 389 odieresis, 556 Uacute, 722 ugrave, 556 nine, 556 five, 556 udieresis, 556 Zcaron, 611 Scommaaccent, 667 threequarters, 834 guillemotright, 556 Ccedilla, 722 ydieresis, 500 tilde, 333 at, 1015 eacute, 556 underscore, 556 Euro, 556 Dcroat, 722 multiply, 584 zero, 556 eth, 556 Scedilla, 667 Ograve, 778 Racute, 722 partialdiff, 476 uacute, 556 braceleft, 334 Thorn, 667 zcaron, 500 scommaaccent, 500 ccedilla, 500 Dcaron, 722 dcroat, 556 Ocircumflex, 778 Oacute, 778 scedilla, 500 ogonek, 333 ograve, 556 racute, 333 Tcaron, 611 Eogonek, 667 thorn, 556 degree, 400 registered, 737 radical, 453 Aring, 667 percent, 889 six, 556 paragraph, 537 dcaron, 643 Uogonek, 722 two, 556 summation, 600 Igrave, 278 Lacute, 556 ocircumflex, 556 oacute, 556 Uring, 722 Lcommaaccent, 556 tcaron, 317 eogonek, 556 Delta, 612 Ohungarumlaut, 778 asciicircum, 469 aring, 556 grave, 333 uogonek, 556 bracketright, 278 Iacute, 278 ampersand, 667 igrave, 278 lacute, 222 Ncaron, 722 plus, 584 uring, 556 quotesinglbase, 222 lcommaaccent, 222 Yacute, 667 ohungarumlaut, 556 threesuperior, 333 acute, 333 section, 556 dieresis, 333 iacute, 278 quotedblbase, 333 ncaron, 556 florin, 556 yacute, 500 Rcommaaccent, 722 fi, 500 fl, 500 Acircumflex, 667 Cacute, 722 Icircumflex, 278 guillemotleft, 556 germandbls, 611 Amacron, 667 seven, 556 Sacute, 667 ordmasculine, 365 dotlessi, 278 sterling, 556 notequal, 549 Imacron, 278 rcommaaccent, 333 Zdotaccent, 611 acircumflex, 556 cacute, 500 Ecaron, 667 icircumflex, 278 braceright, 334 quotedblright, 333 amacron, 556 sacute, 500 imacron, 278 cent, 556 currency, 556 logicalnot, 584 zdotaccent, 500 Atilde, 667 breve, 333 bar, 260 fraction, 167 less, 584 ecaron, 556 guilsinglleft, 333 exclam, 278 period, 278 Rcaron, 722 Kcommaaccent, 667 greater, 584 atilde, 556 brokenbar, 260 quoteleft, 222 Edotaccent, 667 onesuperior, 333 #### %% poppler-24.02.0/poppler/HelveticaWidths.pregenerated.c000066400000000000000000002612661455701731300227430ustar00rootroot00000000000000/* ANSI-C code produced by gperf version 3.1 */ /* Command-line: gperf poppler/HelveticaWidths.gperf */ /* Computed positions: -k'1-2,5,$' */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) && ('-' == 45) && ('.' == 46) && ('/' == 47) \ && ('0' == 48) && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) && ('=' == 61) && ('>' == 62) \ && ('?' == 63) && ('A' == 65) && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) && ('N' == 78) \ && ('O' == 79) && ('P' == 80) && ('Q' == 81) && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) && ('k' == 107) && ('l' == 108) \ && ('m' == 109) && ('n' == 110) && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) /* The character set is not based on ISO-646. */ # error "gperf generated tables don't work with this execution character set. Please report a bug to ." #endif #line 1 "poppler/HelveticaWidths.gperf" #include #include "BuiltinFontWidth.h" #define TOTAL_KEYWORDS 315 #define MIN_WORD_LENGTH 1 #define MAX_WORD_LENGTH 14 #define MIN_HASH_VALUE 1 #define MAX_HASH_VALUE 1041 /* maximum key range = 1041, duplicates = 0 */ #ifdef __GNUC__ __inline #else # ifdef __cplusplus inline # endif #endif static unsigned int hash(register const char *str, register size_t len) { static const unsigned short asso_values[] = { 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 270, 415, 28, 8, 150, 390, 290, 375, 370, 335, 5, 455, 330, 405, 355, 325, 310, 3, 320, 160, 240, 225, 145, 70, 410, 460, 1042, 1042, 1042, 1042, 1042, 1042, 20, 345, 30, 115, 0, 395, 140, 165, 135, 35, 380, 170, 130, 15, 45, 215, 260, 100, 65, 10, 155, 400, 300, 305, 280, 315, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042 }; register unsigned int hval = len; switch (hval) { default: hval += asso_values[(unsigned char)str[4]]; /*FALLTHROUGH*/ case 4: case 3: case 2: hval += asso_values[(unsigned char)str[1]]; /*FALLTHROUGH*/ case 1: hval += asso_values[(unsigned char)str[0]]; break; } return hval + asso_values[(unsigned char)str[len - 1]]; } const struct BuiltinFontWidth *HelveticaWidthsLookup(register const char *str, register size_t len) { static const struct BuiltinFontWidth wordlist[] = { { "", 0 }, #line 90 "poppler/HelveticaWidths.gperf" { "e", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 70 "poppler/HelveticaWidths.gperf" { "R", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 57 "poppler/HelveticaWidths.gperf" { "K", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 49 "poppler/HelveticaWidths.gperf" { "D", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 115 "poppler/HelveticaWidths.gperf" { "t", 278 }, #line 191 "poppler/HelveticaWidths.gperf" { "ae", 889 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 102 "poppler/HelveticaWidths.gperf" { "n", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 207 "poppler/HelveticaWidths.gperf" { "eacute", 556 }, { "", 0 }, { "", 0 }, #line 216 "poppler/HelveticaWidths.gperf" { "Racute", 722 }, { "", 0 }, #line 85 "poppler/HelveticaWidths.gperf" { "a", 556 }, #line 206 "poppler/HelveticaWidths.gperf" { "at", 1015 }, { "", 0 }, #line 308 "poppler/HelveticaWidths.gperf" { "cent", 556 }, { "", 0 }, { "", 0 }, #line 161 "poppler/HelveticaWidths.gperf" { "oe", 944 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 145 "poppler/HelveticaWidths.gperf" { "nacute", 556 }, { "", 0 }, #line 254 "poppler/HelveticaWidths.gperf" { "Delta", 612 }, { "", 0 }, #line 273 "poppler/HelveticaWidths.gperf" { "acute", 333 }, #line 112 "poppler/HelveticaWidths.gperf" { "aacute", 556 }, #line 47 "poppler/HelveticaWidths.gperf" { "C", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 88 "poppler/HelveticaWidths.gperf" { "c", 500 }, { "", 0 }, #line 173 "poppler/HelveticaWidths.gperf" { "one", 556 }, #line 285 "poppler/HelveticaWidths.gperf" { "Cacute", 722 }, { "", 0 }, #line 300 "poppler/HelveticaWidths.gperf" { "cacute", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 98 "poppler/HelveticaWidths.gperf" { "j", 222 }, { "", 0 }, { "", 0 }, #line 210 "poppler/HelveticaWidths.gperf" { "Dcroat", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 249 "poppler/HelveticaWidths.gperf" { "oacute", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 72 "poppler/HelveticaWidths.gperf" { "caron", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 104 "poppler/HelveticaWidths.gperf" { "o", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 317 "poppler/HelveticaWidths.gperf" { "ecaron", 556 }, { "", 0 }, { "", 0 }, #line 321 "poppler/HelveticaWidths.gperf" { "Rcaron", 722 }, #line 290 "poppler/HelveticaWidths.gperf" { "seven", 556 }, #line 306 "poppler/HelveticaWidths.gperf" { "sacute", 500 }, { "", 0 }, { "", 0 }, #line 224 "poppler/HelveticaWidths.gperf" { "Dcaron", 722 }, { "", 0 }, #line 252 "poppler/HelveticaWidths.gperf" { "tcaron", 317 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 26 "poppler/HelveticaWidths.gperf" { "colon", 278 }, #line 278 "poppler/HelveticaWidths.gperf" { "ncaron", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 125 "poppler/HelveticaWidths.gperf" { "commaaccent", 250 }, { "", 0 }, { "", 0 }, #line 135 "poppler/HelveticaWidths.gperf" { "semicolon", 278 }, #line 19 "poppler/HelveticaWidths.gperf" { "comma", 278 }, #line 235 "poppler/HelveticaWidths.gperf" { "degree", 400 }, { "", 0 }, { "", 0 }, #line 118 "poppler/HelveticaWidths.gperf" { "Ccaron", 722 }, { "", 0 }, #line 140 "poppler/HelveticaWidths.gperf" { "ccaron", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 113 "poppler/HelveticaWidths.gperf" { "s", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 231 "poppler/HelveticaWidths.gperf" { "racute", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 79 "poppler/HelveticaWidths.gperf" { "X", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 30 "poppler/HelveticaWidths.gperf" { "ntilde", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 205 "poppler/HelveticaWidths.gperf" { "tilde", 333 }, #line 324 "poppler/HelveticaWidths.gperf" { "atilde", 556 }, { "", 0 }, { "", 0 }, #line 196 "poppler/HelveticaWidths.gperf" { "nine", 556 }, #line 24 "poppler/HelveticaWidths.gperf" { "edotaccent", 556 }, #line 105 "poppler/HelveticaWidths.gperf" { "ordfeminine", 370 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 158 "poppler/HelveticaWidths.gperf" { "eight", 556 }, #line 150 "poppler/HelveticaWidths.gperf" { "scaron", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 276 "poppler/HelveticaWidths.gperf" { "iacute", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 170 "poppler/HelveticaWidths.gperf" { "otilde", 556 }, #line 292 "poppler/HelveticaWidths.gperf" { "ordmasculine", 365 }, #line 213 "poppler/HelveticaWidths.gperf" { "eth", 556 }, { "", 0 }, #line 42 "poppler/HelveticaWidths.gperf" { "three", 556 }, #line 225 "poppler/HelveticaWidths.gperf" { "dcroat", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 281 "poppler/HelveticaWidths.gperf" { "Rcommaaccent", 722 }, #line 185 "poppler/HelveticaWidths.gperf" { "Eacute", 667 }, #line 322 "poppler/HelveticaWidths.gperf" { "Kcommaaccent", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 218 "poppler/HelveticaWidths.gperf" { "uacute", 556 }, #line 103 "poppler/HelveticaWidths.gperf" { "tcommaaccent", 278 }, { "", 0 }, #line 166 "poppler/HelveticaWidths.gperf" { "copyright", 737 }, #line 43 "poppler/HelveticaWidths.gperf" { "numbersign", 556 }, #line 15 "poppler/HelveticaWidths.gperf" { "rcaron", 333 }, #line 32 "poppler/HelveticaWidths.gperf" { "ncommaaccent", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 110 "poppler/HelveticaWidths.gperf" { "r", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 264 "poppler/HelveticaWidths.gperf" { "lacute", 222 }, { "", 0 }, { "", 0 }, #line 23 "poppler/HelveticaWidths.gperf" { "dotaccent", 333 }, #line 234 "poppler/HelveticaWidths.gperf" { "thorn", 556 }, #line 242 "poppler/HelveticaWidths.gperf" { "dcaron", 643 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 146 "poppler/HelveticaWidths.gperf" { "macron", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 203 "poppler/HelveticaWidths.gperf" { "Ccedilla", 722 }, #line 274 "poppler/HelveticaWidths.gperf" { "section", 556 }, #line 223 "poppler/HelveticaWidths.gperf" { "ccedilla", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 20 "poppler/HelveticaWidths.gperf" { "cedilla", 333 }, { "", 0 }, { "", 0 }, #line 25 "poppler/HelveticaWidths.gperf" { "asciitilde", 584 }, #line 89 "poppler/HelveticaWidths.gperf" { "d", 556 }, #line 239 "poppler/HelveticaWidths.gperf" { "percent", 889 }, { "", 0 }, { "", 0 }, #line 288 "poppler/HelveticaWidths.gperf" { "germandbls", 611 }, { "", 0 }, #line 138 "poppler/HelveticaWidths.gperf" { "lozenge", 471 }, { "", 0 }, #line 316 "poppler/HelveticaWidths.gperf" { "less", 584 }, { "", 0 }, #line 97 "poppler/HelveticaWidths.gperf" { "dagger", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 258 "poppler/HelveticaWidths.gperf" { "grave", 333 }, #line 301 "poppler/HelveticaWidths.gperf" { "Ecaron", 667 }, #line 222 "poppler/HelveticaWidths.gperf" { "scommaaccent", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 160 "poppler/HelveticaWidths.gperf" { "endash", 556 }, #line 174 "poppler/HelveticaWidths.gperf" { "emacron", 556 }, #line 201 "poppler/HelveticaWidths.gperf" { "threequarters", 834 }, { "", 0 }, { "", 0 }, #line 232 "poppler/HelveticaWidths.gperf" { "Tcaron", 611 }, { "", 0 }, #line 228 "poppler/HelveticaWidths.gperf" { "scedilla", 500 }, { "", 0 }, { "", 0 }, #line 101 "poppler/HelveticaWidths.gperf" { "m", 833 }, { "", 0 }, { "", 0 }, #line 245 "poppler/HelveticaWidths.gperf" { "summation", 600 }, #line 310 "poppler/HelveticaWidths.gperf" { "logicalnot", 584 }, #line 44 "poppler/HelveticaWidths.gperf" { "lcaron", 299 }, { "", 0 }, { "", 0 }, #line 172 "poppler/HelveticaWidths.gperf" { "parenleft", 333 }, #line 139 "poppler/HelveticaWidths.gperf" { "parenright", 333 }, #line 95 "poppler/HelveticaWidths.gperf" { "i", 222 }, #line 305 "poppler/HelveticaWidths.gperf" { "amacron", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 194 "poppler/HelveticaWidths.gperf" { "Uacute", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 208 "poppler/HelveticaWidths.gperf" { "underscore", 556 }, #line 92 "poppler/HelveticaWidths.gperf" { "g", 556 }, #line 297 "poppler/HelveticaWidths.gperf" { "rcommaaccent", 333 }, { "", 0 }, { "", 0 }, #line 37 "poppler/HelveticaWidths.gperf" { "space", 278 }, #line 28 "poppler/HelveticaWidths.gperf" { "dollar", 556 }, { "", 0 }, #line 272 "poppler/HelveticaWidths.gperf" { "threesuperior", 333 }, #line 188 "poppler/HelveticaWidths.gperf" { "edieresis", 556 }, #line 236 "poppler/HelveticaWidths.gperf" { "registered", 737 }, #line 78 "poppler/HelveticaWidths.gperf" { "W", 944 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 64 "poppler/HelveticaWidths.gperf" { "omacron", 556 }, #line 36 "poppler/HelveticaWidths.gperf" { "yen", 556 }, { "", 0 }, { "", 0 }, #line 50 "poppler/HelveticaWidths.gperf" { "E", 667 }, { "", 0 }, #line 293 "poppler/HelveticaWidths.gperf" { "dotlessi", 278 }, { "", 0 }, #line 327 "poppler/HelveticaWidths.gperf" { "Edotaccent", 667 }, #line 71 "poppler/HelveticaWidths.gperf" { "Aacute", 667 }, { "", 0 }, { "", 0 }, #line 186 "poppler/HelveticaWidths.gperf" { "adieresis", 556 }, { "", 0 }, #line 117 "poppler/HelveticaWidths.gperf" { "u", 556 }, { "", 0 }, { "", 0 }, #line 144 "poppler/HelveticaWidths.gperf" { "daggerdbl", 556 }, { "", 0 }, #line 280 "poppler/HelveticaWidths.gperf" { "yacute", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 74 "poppler/HelveticaWidths.gperf" { "T", 611 }, #line 130 "poppler/HelveticaWidths.gperf" { "gcommaaccent", 556 }, #line 275 "poppler/HelveticaWidths.gperf" { "dieresis", 333 }, { "", 0 }, #line 51 "poppler/HelveticaWidths.gperf" { "onequarter", 834 }, #line 328 "poppler/HelveticaWidths.gperf" { "onesuperior", 333 }, #line 237 "poppler/HelveticaWidths.gperf" { "radical", 453 }, #line 190 "poppler/HelveticaWidths.gperf" { "Eth", 722 }, { "", 0 }, { "", 0 }, #line 94 "poppler/HelveticaWidths.gperf" { "h", 556 }, { "", 0 }, { "", 0 }, #line 193 "poppler/HelveticaWidths.gperf" { "odieresis", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 100 "poppler/HelveticaWidths.gperf" { "l", 222 }, #line 65 "poppler/HelveticaWidths.gperf" { "Tcommaaccent", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 136 "poppler/HelveticaWidths.gperf" { "oslash", 611 }, { "", 0 }, { "", 0 }, #line 137 "poppler/HelveticaWidths.gperf" { "lessequal", 549 }, #line 159 "poppler/HelveticaWidths.gperf" { "exclamdown", 333 }, #line 35 "poppler/HelveticaWidths.gperf" { "zacute", 500 }, #line 269 "poppler/HelveticaWidths.gperf" { "lcommaaccent", 222 }, { "", 0 }, #line 209 "poppler/HelveticaWidths.gperf" { "Euro", 556 }, { "", 0 }, #line 291 "poppler/HelveticaWidths.gperf" { "Sacute", 667 }, #line 323 "poppler/HelveticaWidths.gperf" { "greater", 584 }, #line 244 "poppler/HelveticaWidths.gperf" { "two", 556 }, { "", 0 }, #line 220 "poppler/HelveticaWidths.gperf" { "Thorn", 667 }, #line 256 "poppler/HelveticaWidths.gperf" { "asciicircum", 469 }, #line 126 "poppler/HelveticaWidths.gperf" { "hungarumlaut", 333 }, { "", 0 }, #line 212 "poppler/HelveticaWidths.gperf" { "zero", 556 }, { "", 0 }, #line 40 "poppler/HelveticaWidths.gperf" { "emdash", 1000 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 116 "poppler/HelveticaWidths.gperf" { "divide", 584 }, { "", 0 }, #line 271 "poppler/HelveticaWidths.gperf" { "ohungarumlaut", 556 }, #line 262 "poppler/HelveticaWidths.gperf" { "ampersand", 667 }, { "", 0 }, #line 164 "poppler/HelveticaWidths.gperf" { "ecircumflex", 556 }, { "", 0 }, { "", 0 }, #line 106 "poppler/HelveticaWidths.gperf" { "ring", 333 }, { "", 0 }, #line 320 "poppler/HelveticaWidths.gperf" { "period", 278 }, { "", 0 }, #line 318 "poppler/HelveticaWidths.gperf" { "guilsinglleft", 333 }, #line 155 "poppler/HelveticaWidths.gperf" { "guilsinglright", 333 }, { "", 0 }, { "", 0 }, #line 307 "poppler/HelveticaWidths.gperf" { "imacron", 278 }, { "", 0 }, #line 61 "poppler/HelveticaWidths.gperf" { "periodcentered", 278 }, { "", 0 }, #line 227 "poppler/HelveticaWidths.gperf" { "Oacute", 778 }, { "", 0 }, #line 294 "poppler/HelveticaWidths.gperf" { "sterling", 556 }, { "", 0 }, { "", 0 }, #line 299 "poppler/HelveticaWidths.gperf" { "acircumflex", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 33 "poppler/HelveticaWidths.gperf" { "minus", 584 }, #line 312 "poppler/HelveticaWidths.gperf" { "Atilde", 667 }, #line 148 "poppler/HelveticaWidths.gperf" { "Emacron", 667 }, { "", 0 }, { "", 0 }, #line 257 "poppler/HelveticaWidths.gperf" { "aring", 556 }, #line 261 "poppler/HelveticaWidths.gperf" { "Iacute", 278 }, #line 183 "poppler/HelveticaWidths.gperf" { "umacron", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 221 "poppler/HelveticaWidths.gperf" { "zcaron", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 133 "poppler/HelveticaWidths.gperf" { "Scaron", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 248 "poppler/HelveticaWidths.gperf" { "ocircumflex", 556 }, { "", 0 }, { "", 0 }, #line 189 "poppler/HelveticaWidths.gperf" { "idieresis", 278 }, { "", 0 }, #line 157 "poppler/HelveticaWidths.gperf" { "quotesingle", 191 }, #line 277 "poppler/HelveticaWidths.gperf" { "quotedblbase", 333 }, { "", 0 }, #line 268 "poppler/HelveticaWidths.gperf" { "quotesinglbase", 222 }, { "", 0 }, #line 107 "poppler/HelveticaWidths.gperf" { "p", 556 }, #line 132 "poppler/HelveticaWidths.gperf" { "greaterequal", 549 }, { "", 0 }, #line 326 "poppler/HelveticaWidths.gperf" { "quoteleft", 222 }, #line 179 "poppler/HelveticaWidths.gperf" { "quoteright", 222 }, { "", 0 }, #line 154 "poppler/HelveticaWidths.gperf" { "quotedblleft", 333 }, #line 304 "poppler/HelveticaWidths.gperf" { "quotedblright", 333 }, #line 169 "poppler/HelveticaWidths.gperf" { "Edieresis", 667 }, { "", 0 }, #line 128 "poppler/HelveticaWidths.gperf" { "Nacute", 722 }, #line 131 "poppler/HelveticaWidths.gperf" { "mu", 556 }, { "", 0 }, #line 198 "poppler/HelveticaWidths.gperf" { "udieresis", 556 }, { "", 0 }, #line 270 "poppler/HelveticaWidths.gperf" { "Yacute", 667 }, #line 253 "poppler/HelveticaWidths.gperf" { "eogonek", 556 }, #line 80 "poppler/HelveticaWidths.gperf" { "question", 556 }, { "", 0 }, #line 313 "poppler/HelveticaWidths.gperf" { "breve", 333 }, #line 77 "poppler/HelveticaWidths.gperf" { "V", 667 }, #line 39 "poppler/HelveticaWidths.gperf" { "questiondown", 611 }, { "", 0 }, #line 266 "poppler/HelveticaWidths.gperf" { "plus", 584 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 149 "poppler/HelveticaWidths.gperf" { "ellipsis", 1000 }, { "", 0 }, { "", 0 }, #line 319 "poppler/HelveticaWidths.gperf" { "exclam", 278 }, { "", 0 }, { "", 0 }, #line 219 "poppler/HelveticaWidths.gperf" { "braceleft", 334 }, #line 303 "poppler/HelveticaWidths.gperf" { "braceright", 334 }, #line 156 "poppler/HelveticaWidths.gperf" { "hyphen", 333 }, #line 48 "poppler/HelveticaWidths.gperf" { "aogonek", 556 }, #line 314 "poppler/HelveticaWidths.gperf" { "bar", 260 }, { "", 0 }, #line 311 "poppler/HelveticaWidths.gperf" { "zdotaccent", 500 }, #line 153 "poppler/HelveticaWidths.gperf" { "lslash", 222 }, #line 86 "poppler/HelveticaWidths.gperf" { "Gcommaaccent", 778 }, #line 309 "poppler/HelveticaWidths.gperf" { "currency", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 75 "poppler/HelveticaWidths.gperf" { "U", 722 }, #line 27 "poppler/HelveticaWidths.gperf" { "onehalf", 834 }, #line 109 "poppler/HelveticaWidths.gperf" { "uhungarumlaut", 556 }, { "", 0 }, { "", 0 }, #line 147 "poppler/HelveticaWidths.gperf" { "Otilde", 778 }, { "", 0 }, #line 287 "poppler/HelveticaWidths.gperf" { "guillemotleft", 556 }, #line 202 "poppler/HelveticaWidths.gperf" { "guillemotright", 556 }, { "", 0 }, #line 247 "poppler/HelveticaWidths.gperf" { "Lacute", 556 }, #line 163 "poppler/HelveticaWidths.gperf" { "Umacron", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 18 "poppler/HelveticaWidths.gperf" { "Zacute", 611 }, { "", 0 }, #line 295 "poppler/HelveticaWidths.gperf" { "notequal", 549 }, #line 143 "poppler/HelveticaWidths.gperf" { "trademark", 1000 }, { "", 0 }, #line 265 "poppler/HelveticaWidths.gperf" { "Ncaron", 722 }, #line 200 "poppler/HelveticaWidths.gperf" { "Scommaaccent", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 181 "poppler/HelveticaWidths.gperf" { "perthousand", 1000 }, { "", 0 }, #line 240 "poppler/HelveticaWidths.gperf" { "six", 556 }, { "", 0 }, { "", 0 }, #line 302 "poppler/HelveticaWidths.gperf" { "icircumflex", 278 }, { "", 0 }, #line 214 "poppler/HelveticaWidths.gperf" { "Scedilla", 667 }, { "", 0 }, { "", 0 }, #line 93 "poppler/HelveticaWidths.gperf" { "bullet", 350 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 108 "poppler/HelveticaWidths.gperf" { "q", 556 }, #line 289 "poppler/HelveticaWidths.gperf" { "Amacron", 667 }, { "", 0 }, { "", 0 }, #line 127 "poppler/HelveticaWidths.gperf" { "Idotaccent", 278 }, #line 141 "poppler/HelveticaWidths.gperf" { "Ecircumflex", 667 }, { "", 0 }, #line 315 "poppler/HelveticaWidths.gperf" { "fraction", 167 }, #line 180 "poppler/HelveticaWidths.gperf" { "Udieresis", 722 }, { "", 0 }, #line 176 "poppler/HelveticaWidths.gperf" { "ucircumflex", 556 }, { "", 0 }, { "", 0 }, #line 197 "poppler/HelveticaWidths.gperf" { "five", 556 }, { "", 0 }, #line 14 "poppler/HelveticaWidths.gperf" { "Ntilde", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 267 "poppler/HelveticaWidths.gperf" { "uring", 556 }, #line 45 "poppler/HelveticaWidths.gperf" { "A", 667 }, { "", 0 }, { "", 0 }, #line 84 "poppler/HelveticaWidths.gperf" { "four", 556 }, { "", 0 }, #line 187 "poppler/HelveticaWidths.gperf" { "egrave", 556 }, { "", 0 }, { "", 0 }, #line 241 "poppler/HelveticaWidths.gperf" { "paragraph", 537 }, { "", 0 }, #line 29 "poppler/HelveticaWidths.gperf" { "Lcaron", 556 }, { "", 0 }, { "", 0 }, #line 325 "poppler/HelveticaWidths.gperf" { "brokenbar", 260 }, { "", 0 }, #line 199 "poppler/HelveticaWidths.gperf" { "Zcaron", 611 }, { "", 0 }, { "", 0 }, #line 165 "poppler/HelveticaWidths.gperf" { "Adieresis", 667 }, { "", 0 }, #line 122 "poppler/HelveticaWidths.gperf" { "y", 500 }, #line 16 "poppler/HelveticaWidths.gperf" { "kcommaaccent", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 76 "poppler/HelveticaWidths.gperf" { "agrave", 556 }, { "", 0 }, #line 69 "poppler/HelveticaWidths.gperf" { "Uhungarumlaut", 722 }, #line 204 "poppler/HelveticaWidths.gperf" { "ydieresis", 500 }, #line 168 "poppler/HelveticaWidths.gperf" { "slash", 278 }, #line 229 "poppler/HelveticaWidths.gperf" { "ogonek", 333 }, #line 151 "poppler/HelveticaWidths.gperf" { "AE", 1000 }, #line 192 "poppler/HelveticaWidths.gperf" { "asterisk", 389 }, { "", 0 }, { "", 0 }, #line 111 "poppler/HelveticaWidths.gperf" { "twosuperior", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 53 "poppler/HelveticaWidths.gperf" { "G", 778 }, #line 58 "poppler/HelveticaWidths.gperf" { "iogonek", 222 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 17 "poppler/HelveticaWidths.gperf" { "Ncommaaccent", 722 }, { "", 0 }, #line 21 "poppler/HelveticaWidths.gperf" { "plusminus", 584 }, { "", 0 }, #line 230 "poppler/HelveticaWidths.gperf" { "ograve", 556 }, { "", 0 }, #line 129 "poppler/HelveticaWidths.gperf" { "quotedbl", 355 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 233 "poppler/HelveticaWidths.gperf" { "Eogonek", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 120 "poppler/HelveticaWidths.gperf" { "w", 722 }, #line 259 "poppler/HelveticaWidths.gperf" { "uogonek", 556 }, { "", 0 }, #line 59 "poppler/HelveticaWidths.gperf" { "backslash", 278 }, #line 81 "poppler/HelveticaWidths.gperf" { "equal", 584 }, { "", 0 }, #line 38 "poppler/HelveticaWidths.gperf" { "Omacron", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 121 "poppler/HelveticaWidths.gperf" { "x", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 298 "poppler/HelveticaWidths.gperf" { "Zdotaccent", 611 }, #line 152 "poppler/HelveticaWidths.gperf" { "Ucircumflex", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 68 "poppler/HelveticaWidths.gperf" { "Q", 778 }, #line 296 "poppler/HelveticaWidths.gperf" { "Imacron", 278 }, { "", 0 }, { "", 0 }, #line 250 "poppler/HelveticaWidths.gperf" { "Uring", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 123 "poppler/HelveticaWidths.gperf" { "z", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 22 "poppler/HelveticaWidths.gperf" { "circumflex", 333 }, { "", 0 }, #line 251 "poppler/HelveticaWidths.gperf" { "Lcommaaccent", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 73 "poppler/HelveticaWidths.gperf" { "S", 667 }, { "", 0 }, { "", 0 }, #line 175 "poppler/HelveticaWidths.gperf" { "Odieresis", 778 }, { "", 0 }, #line 284 "poppler/HelveticaWidths.gperf" { "Acircumflex", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 67 "poppler/HelveticaWidths.gperf" { "P", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 238 "poppler/HelveticaWidths.gperf" { "Aring", 667 }, #line 96 "poppler/HelveticaWidths.gperf" { "Oslash", 778 }, #line 114 "poppler/HelveticaWidths.gperf" { "OE", 1000 }, { "", 0 }, #line 171 "poppler/HelveticaWidths.gperf" { "Idieresis", 278 }, { "", 0 }, #line 62 "poppler/HelveticaWidths.gperf" { "M", 833 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 282 "poppler/HelveticaWidths.gperf" { "fi", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 56 "poppler/HelveticaWidths.gperf" { "J", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 263 "poppler/HelveticaWidths.gperf" { "igrave", 278 }, { "", 0 }, #line 255 "poppler/HelveticaWidths.gperf" { "Ohungarumlaut", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 243 "poppler/HelveticaWidths.gperf" { "Uogonek", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 87 "poppler/HelveticaWidths.gperf" { "b", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 167 "poppler/HelveticaWidths.gperf" { "Egrave", 667 }, { "", 0 }, { "", 0 }, #line 182 "poppler/HelveticaWidths.gperf" { "Ydieresis", 667 }, { "", 0 }, #line 195 "poppler/HelveticaWidths.gperf" { "ugrave", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 211 "poppler/HelveticaWidths.gperf" { "multiply", 584 }, { "", 0 }, { "", 0 }, #line 66 "poppler/HelveticaWidths.gperf" { "O", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 31 "poppler/HelveticaWidths.gperf" { "Aogonek", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 279 "poppler/HelveticaWidths.gperf" { "florin", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 226 "poppler/HelveticaWidths.gperf" { "Ocircumflex", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 283 "poppler/HelveticaWidths.gperf" { "fl", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 55 "poppler/HelveticaWidths.gperf" { "I", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 286 "poppler/HelveticaWidths.gperf" { "Icircumflex", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 54 "poppler/HelveticaWidths.gperf" { "H", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 134 "poppler/HelveticaWidths.gperf" { "Lslash", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 99 "poppler/HelveticaWidths.gperf" { "k", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 184 "poppler/HelveticaWidths.gperf" { "abreve", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 217 "poppler/HelveticaWidths.gperf" { "partialdiff", 476 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 52 "poppler/HelveticaWidths.gperf" { "F", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 178 "poppler/HelveticaWidths.gperf" { "Ugrave", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 91 "poppler/HelveticaWidths.gperf" { "f", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 119 "poppler/HelveticaWidths.gperf" { "v", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 63 "poppler/HelveticaWidths.gperf" { "N", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 41 "poppler/HelveticaWidths.gperf" { "Agrave", 667 }, #line 34 "poppler/HelveticaWidths.gperf" { "Iogonek", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 82 "poppler/HelveticaWidths.gperf" { "Y", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 46 "poppler/HelveticaWidths.gperf" { "B", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 177 "poppler/HelveticaWidths.gperf" { "bracketleft", 278 }, #line 260 "poppler/HelveticaWidths.gperf" { "bracketright", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 142 "poppler/HelveticaWidths.gperf" { "gbreve", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 215 "poppler/HelveticaWidths.gperf" { "Ograve", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 60 "poppler/HelveticaWidths.gperf" { "L", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 246 "poppler/HelveticaWidths.gperf" { "Igrave", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 83 "poppler/HelveticaWidths.gperf" { "Z", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 162 "poppler/HelveticaWidths.gperf" { "Abreve", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 124 "poppler/HelveticaWidths.gperf" { "Gbreve", 778 } }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) { register unsigned int key = hash(str, len); if (key <= MAX_HASH_VALUE) { register const char *s = wordlist[key].name; if (*str == *s && !strcmp(str + 1, s + 1)) return &wordlist[key]; } } return 0; } #line 330 "poppler/HelveticaWidths.gperf" poppler-24.02.0/poppler/Hints.cc000066400000000000000000000400111455701731300164170ustar00rootroot00000000000000//======================================================================== // // Hints.cc // // This file is licensed under the GPLv2 or later // // Copyright 2010, 2012, 2013 Hib Eris // Copyright 2010, 2011, 2013, 2014, 2016-2019, 2021, 2022 Albert Astals Cid // Copyright 2010, 2013 Pino Toscano // Copyright 2013 Adrian Johnson // Copyright 2014 Fabio D'Urso // Copyright 2016 Jeffrey Morlan // Copyright 2019 LE GARREC Vincent // Copyright 2019 Adam Reichold // //======================================================================== #include #include "Hints.h" #include "Linearization.h" #include "Object.h" #include "Stream.h" #include "XRef.h" #include "Parser.h" #include "Lexer.h" #include "SecurityHandler.h" #include class StreamBitReader { public: explicit StreamBitReader(Stream *strA) : str(strA), inputBits(0), isAtEof(false) { } void resetInputBits() { inputBits = 0; } bool atEOF() const { return isAtEof; } unsigned int readBit() { unsigned int bit; int c; if (inputBits == 0) { if ((c = str->getChar()) == EOF) { isAtEof = true; return (unsigned int)-1; } bitsBuffer = c; inputBits = 8; } bit = (bitsBuffer >> (inputBits - 1)) & 1; --inputBits; return bit; } unsigned int readBits(int n) { unsigned int bit, bits; if (n < 0) { return -1; } if (n == 0) { return 0; } if (n == 1) { return readBit(); } bit = readBit(); if (bit == (unsigned int)-1) { return -1; } bit = bit << (n - 1); bits = readBits(n - 1); if (bits == (unsigned int)-1) { return -1; } return bit | bits; } private: Stream *str; int inputBits; char bitsBuffer; bool isAtEof; }; //------------------------------------------------------------------------ // Hints //------------------------------------------------------------------------ Hints::Hints(BaseStream *str, Linearization *linearization, XRef *xref, SecurityHandler *secHdlr) { mainXRefEntriesOffset = linearization->getMainXRefEntriesOffset(); nPages = linearization->getNumPages(); pageFirst = linearization->getPageFirst(); pageEndFirst = linearization->getEndFirst(); pageObjectFirst = linearization->getObjectNumberFirst(); if (pageObjectFirst < 0 || pageObjectFirst >= xref->getNumObjects()) { error(errSyntaxWarning, -1, "Invalid reference for first page object ({0:d}) in linearization table ", pageObjectFirst); pageObjectFirst = 0; } XRefEntry *pageObjectFirstXRefEntry = xref->getEntry(pageObjectFirst); if (!pageObjectFirstXRefEntry) { error(errSyntaxWarning, -1, "No XRef entry for first page object"); pageOffsetFirst = 0; } else { pageOffsetFirst = pageObjectFirstXRefEntry->offset; } if (nPages >= INT_MAX / (int)sizeof(unsigned int)) { error(errSyntaxWarning, -1, "Invalid number of pages ({0:d}) for hints table", nPages); nPages = 0; } nObjects = (unsigned int *)gmallocn_checkoverflow(nPages, sizeof(unsigned int)); pageObjectNum = (int *)gmallocn_checkoverflow(nPages, sizeof(int)); xRefOffset = (unsigned int *)gmallocn_checkoverflow(nPages, sizeof(unsigned int)); pageLength = (unsigned int *)gmallocn_checkoverflow(nPages, sizeof(unsigned int)); pageOffset = (Goffset *)gmallocn_checkoverflow(nPages, sizeof(Goffset)); numSharedObject = (unsigned int *)gmallocn_checkoverflow(nPages, sizeof(unsigned int)); sharedObjectId = (unsigned int **)gmallocn_checkoverflow(nPages, sizeof(unsigned int *)); if (!nObjects || !pageObjectNum || !xRefOffset || !pageLength || !pageOffset || !numSharedObject || !sharedObjectId) { error(errSyntaxWarning, -1, "Failed to allocate memory for hints table"); nPages = 0; } if (nPages != 0) { memset(pageLength, 0, nPages * sizeof(unsigned int)); memset(pageOffset, 0, nPages * sizeof(unsigned int)); memset(numSharedObject, 0, nPages * sizeof(unsigned int)); memset(pageObjectNum, 0, nPages * sizeof(int)); } groupLength = nullptr; groupOffset = nullptr; groupHasSignature = nullptr; groupNumObjects = nullptr; groupXRefOffset = nullptr; ok = true; readTables(str, linearization, xref, secHdlr); } Hints::~Hints() { gfree(nObjects); gfree(pageObjectNum); gfree(xRefOffset); gfree(pageLength); gfree(pageOffset); for (int i = 0; i < nPages; i++) { if (numSharedObject[i]) { gfree(sharedObjectId[i]); } } gfree(sharedObjectId); gfree(numSharedObject); gfree(groupLength); gfree(groupOffset); gfree(groupHasSignature); gfree(groupNumObjects); gfree(groupXRefOffset); } void Hints::readTables(BaseStream *str, Linearization *linearization, XRef *xref, SecurityHandler *secHdlr) { hintsOffset = linearization->getHintsOffset(); hintsLength = linearization->getHintsLength(); hintsOffset2 = linearization->getHintsOffset2(); hintsLength2 = linearization->getHintsLength2(); const unsigned int bufLength = hintsLength + hintsLength2; if (bufLength == 0) { ok = false; return; } std::vector buf(bufLength); char *p = &buf[0]; if (hintsOffset && hintsLength) { std::unique_ptr s(str->makeSubStream(hintsOffset, false, hintsLength, Object(objNull))); s->reset(); for (unsigned int i = 0; i < hintsLength; i++) { const int c = s->getChar(); if (unlikely(c == EOF)) { error(errSyntaxWarning, -1, "Found EOF while reading hints"); ok = false; return; } *p++ = c; } } if (hintsOffset2 && hintsLength2) { std::unique_ptr s(str->makeSubStream(hintsOffset2, false, hintsLength2, Object(objNull))); s->reset(); for (unsigned int i = 0; i < hintsLength2; i++) { const int c = s->getChar(); if (unlikely(c == EOF)) { error(errSyntaxWarning, -1, "Found EOF while reading hints2"); ok = false; return; } *p++ = c; } } MemStream *memStream = new MemStream(&buf[0], 0, bufLength, Object(objNull)); Parser *parser = new Parser(xref, memStream, true); int num, gen; Object obj; if ((obj = parser->getObj(), obj.isInt()) && (num = obj.getInt(), obj = parser->getObj(), obj.isInt()) && (gen = obj.getInt(), obj = parser->getObj(), obj.isCmd("obj")) && (obj = parser->getObj(false, secHdlr ? secHdlr->getFileKey() : nullptr, secHdlr ? secHdlr->getEncAlgorithm() : cryptRC4, secHdlr ? secHdlr->getFileKeyLength() : 0, num, gen, 0, true), obj.isStream())) { Stream *hintsStream = obj.getStream(); Dict *hintsDict = obj.streamGetDict(); 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(); } ok = readSharedObjectsTable(hintsStream); } } else { error(errSyntaxWarning, -1, "Invalid shared object hint table offset"); ok = false; } } else { error(errSyntaxWarning, -1, "Failed parsing hints table object"); ok = false; } delete parser; } bool Hints::readPageOffsetTable(Stream *str) { if (nPages < 1) { error(errSyntaxWarning, -1, "Invalid number of pages reading page offset hints table"); return false; } StreamBitReader sbr(str); nObjectLeast = sbr.readBits(32); if (nObjectLeast < 1) { error(errSyntaxWarning, -1, "Invalid least number of objects reading page offset hints table"); nPages = 0; return false; } objectOffsetFirst = sbr.readBits(32); if (objectOffsetFirst >= hintsOffset) { objectOffsetFirst += hintsLength; } nBitsDiffObjects = sbr.readBits(16); if (nBitsDiffObjects > 32) { error(errSyntaxWarning, -1, "Invalid number of bits needed to represent the difference between the greatest and least number of objects in a page"); nPages = 0; return false; } pageLengthLeast = sbr.readBits(32); nBitsDiffPageLength = sbr.readBits(16); OffsetStreamLeast = sbr.readBits(32); nBitsOffsetStream = sbr.readBits(16); lengthStreamLeast = sbr.readBits(32); nBitsLengthStream = sbr.readBits(16); nBitsNumShared = sbr.readBits(16); nBitsShared = sbr.readBits(16); nBitsNumerator = sbr.readBits(16); denominator = sbr.readBits(16); if ((nBitsDiffPageLength > 32) || (nBitsOffsetStream > 32) || (nBitsLengthStream > 32) || (nBitsNumShared > 32) || (nBitsShared > 32) || (nBitsNumerator > 32)) { error(errSyntaxWarning, -1, "Invalid number of bits reading page offset hints table"); return false; } for (int i = 0; i < nPages && !sbr.atEOF(); i++) { nObjects[i] = nObjectLeast + sbr.readBits(nBitsDiffObjects); } if (sbr.atEOF()) { return false; } nObjects[0] = 0; xRefOffset[0] = mainXRefEntriesOffset + 20; for (int i = 1; i < nPages; i++) { xRefOffset[i] = xRefOffset[i - 1] + 20 * nObjects[i - 1]; } pageObjectNum[0] = 1; for (int i = 1; i < nPages; i++) { pageObjectNum[i] = pageObjectNum[i - 1] + nObjects[i - 1]; } pageObjectNum[0] = pageObjectFirst; sbr.resetInputBits(); // reset on byte boundary. Not in specs! for (int i = 0; i < nPages && !sbr.atEOF(); i++) { pageLength[i] = pageLengthLeast + sbr.readBits(nBitsDiffPageLength); } if (sbr.atEOF()) { return false; } sbr.resetInputBits(); // reset on byte boundary. Not in specs! numSharedObject[0] = sbr.readBits(nBitsNumShared); numSharedObject[0] = 0; // Do not trust the read value to be 0. sharedObjectId[0] = nullptr; for (int i = 1; i < nPages && !sbr.atEOF(); i++) { numSharedObject[i] = sbr.readBits(nBitsNumShared); if (numSharedObject[i] >= INT_MAX / (int)sizeof(unsigned int)) { error(errSyntaxWarning, -1, "Invalid number of shared objects"); numSharedObject[i] = 0; return false; } sharedObjectId[i] = (unsigned int *)gmallocn_checkoverflow(numSharedObject[i], sizeof(unsigned int)); if (numSharedObject[i] && !sharedObjectId[i]) { error(errSyntaxWarning, -1, "Failed to allocate memory for shared object IDs"); numSharedObject[i] = 0; return false; } } if (sbr.atEOF()) { return false; } sbr.resetInputBits(); // reset on byte boundary. Not in specs! for (int i = 1; i < nPages; i++) { for (unsigned int j = 0; j < numSharedObject[i] && !sbr.atEOF(); j++) { sharedObjectId[i][j] = sbr.readBits(nBitsShared); } } pageOffset[0] = pageOffsetFirst; // find pageOffsets. for (int i = 1; i < nPages; i++) { pageOffset[i] = pageOffset[i - 1] + pageLength[i - 1]; } return !sbr.atEOF(); } bool Hints::readSharedObjectsTable(Stream *str) { StreamBitReader sbr(str); const unsigned int firstSharedObjectNumber = sbr.readBits(32); const unsigned int firstSharedObjectOffset = sbr.readBits(32) + hintsLength; const unsigned int nSharedGroupsFirst = sbr.readBits(32); const unsigned int nSharedGroups = sbr.readBits(32); const unsigned int nBitsNumObjects = sbr.readBits(16); const unsigned int groupLengthLeast = sbr.readBits(32); const unsigned int nBitsDiffGroupLength = sbr.readBits(16); if ((!nSharedGroups) || (nSharedGroups >= INT_MAX / (int)sizeof(unsigned int))) { error(errSyntaxWarning, -1, "Invalid number of shared object groups"); return false; } if ((!nSharedGroupsFirst) || (nSharedGroupsFirst > nSharedGroups)) { error(errSyntaxWarning, -1, "Invalid number of first page shared object groups"); return false; } if (nBitsNumObjects > 32 || nBitsDiffGroupLength > 32) { error(errSyntaxWarning, -1, "Invalid shared object groups bit length"); return false; } groupLength = (unsigned int *)gmallocn_checkoverflow(nSharedGroups, sizeof(unsigned int)); groupOffset = (unsigned int *)gmallocn_checkoverflow(nSharedGroups, sizeof(unsigned int)); groupHasSignature = (unsigned int *)gmallocn_checkoverflow(nSharedGroups, sizeof(unsigned int)); groupNumObjects = (unsigned int *)gmallocn_checkoverflow(nSharedGroups, sizeof(unsigned int)); groupXRefOffset = (unsigned int *)gmallocn_checkoverflow(nSharedGroups, sizeof(unsigned int)); if (!groupLength || !groupOffset || !groupHasSignature || !groupNumObjects || !groupXRefOffset) { error(errSyntaxWarning, -1, "Failed to allocate memory for shared object groups"); return false; } sbr.resetInputBits(); // reset on byte boundary. Not in specs! for (unsigned int i = 0; i < nSharedGroups && !sbr.atEOF(); i++) { groupLength[i] = groupLengthLeast + sbr.readBits(nBitsDiffGroupLength); } if (sbr.atEOF()) { return false; } groupOffset[0] = objectOffsetFirst; for (unsigned int i = 1; i < nSharedGroupsFirst; i++) { groupOffset[i] = groupOffset[i - 1] + groupLength[i - 1]; } if (nSharedGroups > nSharedGroupsFirst) { groupOffset[nSharedGroupsFirst] = firstSharedObjectOffset; for (unsigned int i = nSharedGroupsFirst + 1; i < nSharedGroups; i++) { groupOffset[i] = groupOffset[i - 1] + groupLength[i - 1]; } } sbr.resetInputBits(); // reset on byte boundary. Not in specs! for (unsigned int i = 0; i < nSharedGroups && !sbr.atEOF(); i++) { groupHasSignature[i] = sbr.readBits(1); } if (sbr.atEOF()) { return false; } sbr.resetInputBits(); // reset on byte boundary. Not in specs! for (unsigned int i = 0; i < nSharedGroups && !sbr.atEOF(); i++) { if (groupHasSignature[i]) { // readBits doesn't supports more than 32 bits. sbr.readBits(32); sbr.readBits(32); sbr.readBits(32); sbr.readBits(32); } } if (sbr.atEOF()) { return false; } sbr.resetInputBits(); // reset on byte boundary. Not in specs! for (unsigned int i = 0; i < nSharedGroups && !sbr.atEOF(); i++) { groupNumObjects[i] = nBitsNumObjects ? 1 + sbr.readBits(nBitsNumObjects) : 1; } for (unsigned int i = 0; i < nSharedGroupsFirst; i++) { groupNumObjects[i] = 0; groupXRefOffset[i] = 0; } if (nSharedGroups > nSharedGroupsFirst) { groupXRefOffset[nSharedGroupsFirst] = mainXRefEntriesOffset + 20 * firstSharedObjectNumber; for (unsigned int i = nSharedGroupsFirst + 1; i < nSharedGroups; i++) { groupXRefOffset[i] = groupXRefOffset[i - 1] + 20 * groupNumObjects[i - 1]; } } return !sbr.atEOF(); } bool Hints::isOk() const { return ok; } Goffset Hints::getPageOffset(int page) { if ((page < 1) || (page > nPages)) { return 0; } if (page - 1 > pageFirst) { return pageOffset[page - 1]; } else if (page - 1 < pageFirst) { return pageOffset[page]; } else { return pageOffset[0]; } } int Hints::getPageObjectNum(int page) { if ((page < 1) || (page > nPages)) { return 0; } if (page - 1 > pageFirst) { return pageObjectNum[page - 1]; } else if (page - 1 < pageFirst) { return pageObjectNum[page]; } else { return pageObjectNum[0]; } } poppler-24.02.0/poppler/Hints.h000066400000000000000000000044671455701731300163000ustar00rootroot00000000000000//======================================================================== // // Hints.h // // This file is licensed under the GPLv2 or later // // Copyright 2010 Hib Eris // Copyright 2010, 2013, 2016, 2018 Albert Astals Cid // Copyright 2013 Adrian Johnson // //======================================================================== #ifndef HINTS_H #define HINTS_H #include #include #include "PDFDoc.h" class Stream; class BaseStream; class Linearization; class XRef; //------------------------------------------------------------------------ // Hints //------------------------------------------------------------------------ class Hints { public: Hints(BaseStream *str, Linearization *linearization, XRef *xref, SecurityHandler *secHdlr); ~Hints(); Hints(const Hints &) = delete; Hints &operator=(const Hints &) = delete; bool isOk() const; int getPageObjectNum(int page); Goffset getPageOffset(int page); private: void readTables(BaseStream *str, Linearization *linearization, XRef *xref, SecurityHandler *secHdlr); bool readPageOffsetTable(Stream *str); bool readSharedObjectsTable(Stream *str); unsigned int hintsOffset; unsigned int hintsLength; unsigned int hintsOffset2; unsigned int hintsLength2; unsigned int mainXRefEntriesOffset; int nPages; int pageFirst; int pageObjectFirst; Goffset pageOffsetFirst; unsigned int pageEndFirst; unsigned int nObjectLeast; unsigned int objectOffsetFirst; unsigned int nBitsDiffObjects; unsigned int pageLengthLeast; unsigned int nBitsDiffPageLength; unsigned int OffsetStreamLeast; unsigned int nBitsOffsetStream; unsigned int lengthStreamLeast; unsigned int nBitsLengthStream; unsigned int nBitsNumShared; unsigned int nBitsShared; unsigned int nBitsNumerator; unsigned int denominator; unsigned int *nObjects; int *pageObjectNum; unsigned int *xRefOffset; unsigned int *pageLength; Goffset *pageOffset; unsigned int *numSharedObject; unsigned int **sharedObjectId; unsigned int *groupLength; unsigned int *groupOffset; unsigned int *groupHasSignature; unsigned int *groupNumObjects; unsigned int *groupXRefOffset; bool ok; }; #endif poppler-24.02.0/poppler/ImageEmbeddingUtils.cc000066400000000000000000000367721455701731300212170ustar00rootroot00000000000000//======================================================================== // // ImageEmbeddingUtils.cc // // 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 // //======================================================================== #include #include #ifdef ENABLE_LIBJPEG # include extern "C" { # include } # include #endif #ifdef ENABLE_LIBPNG # include #endif #include "ImageEmbeddingUtils.h" #include "goo/gmem.h" #include "goo/GooCheckedOps.h" #include "Object.h" #include "Array.h" #include "Error.h" #include "PDFDoc.h" namespace ImageEmbeddingUtils { static const uint8_t PNG_MAGIC_NUM[] = { 0x89, 0x50, 0x4e, 0x47 }; static const uint8_t JPEG_MAGIC_NUM[] = { 0xff, 0xd8, 0xff }; static const uint8_t JPEG2000_MAGIC_NUM[] = { 0x00, 0x00, 0x00, 0x0c, 0x6a, 0x50, 0x20, 0x20 }; static const Goffset MAX_MAGIC_NUM_SIZE = sizeof(JPEG2000_MAGIC_NUM); static bool checkMagicNum(const uint8_t *fileContent, const uint8_t *magicNum, const uint8_t size) { return (memcmp(fileContent, magicNum, size) == 0); } // Transforms an image to XObject. class ImageEmbedder { protected: static constexpr const char *DEVICE_GRAY = "DeviceGray"; static constexpr const char *DEVICE_RGB = "DeviceRGB"; int m_width; int m_height; ImageEmbedder(const int width, const int height) : m_width(width), m_height(height) { } // Creates an object of type XObject. You own the returned ptr. static Dict *createImageDict(XRef *xref, const char *colorSpace, const int width, const int height, const int bitsPerComponent) { Dict *imageDict = new Dict(xref); imageDict->add("Type", Object(objName, "XObject")); imageDict->add("Subtype", Object(objName, "Image")); imageDict->add("ColorSpace", Object(objName, colorSpace)); imageDict->add("Width", Object(width)); imageDict->add("Height", Object(height)); imageDict->add("BitsPerComponent", Object(bitsPerComponent)); return imageDict; } public: ImageEmbedder() = delete; ImageEmbedder(const ImageEmbedder &) = delete; ImageEmbedder &operator=(const ImageEmbedder &) = delete; virtual ~ImageEmbedder(); // Call it only once. // Returns ref to a new object or Ref::INVALID. virtual Ref embedImage(XRef *xref) = 0; }; ImageEmbedder::~ImageEmbedder() { } #ifdef ENABLE_LIBPNG // Transforms a PNG image to XObject. class PngEmbedder : public ImageEmbedder { // LibpngInputStream is a simple replacement for GInputStream. // Used with png_set_read_fn(). class LibpngInputStream { std::unique_ptr m_fileContent; uint8_t *m_iterator; png_size_t m_remainingSize; void read(png_bytep out, const png_size_t size) { const png_size_t fixedSize = (m_remainingSize >= size) ? size : m_remainingSize; memcpy(out, m_iterator, fixedSize); m_iterator += fixedSize; m_remainingSize -= fixedSize; } public: LibpngInputStream(std::unique_ptr &&fileContent, const Goffset size) : m_fileContent(std::move(fileContent)), m_iterator(m_fileContent.get()), m_remainingSize(size) { } LibpngInputStream() = delete; LibpngInputStream(const LibpngInputStream &) = delete; LibpngInputStream &operator=(const LibpngInputStream &) = delete; ~LibpngInputStream() = default; // Pass this static function to png_set_read_fn(). static void readCallback(png_structp png, png_bytep out, png_size_t size) { LibpngInputStream *stream = (LibpngInputStream *)png_get_io_ptr(png); if (stream) { stream->read(out, size); } } }; png_structp m_png; png_infop m_info; LibpngInputStream *m_stream; const png_byte m_type; const bool m_hasAlpha; // Number of color channels. const png_byte m_n; // Number of color channels excluding alpha channel. Should be 1 or 3. const png_byte m_nWithoutAlpha; // Shold be 8 or 16. const png_byte m_bitDepth; // Should be 1 or 2. const png_byte m_byteDepth; PngEmbedder(png_structp png, png_infop info, LibpngInputStream *stream) : ImageEmbedder(png_get_image_width(png, info), png_get_image_height(png, info)), m_png(png), m_info(info), m_stream(stream), m_type(png_get_color_type(m_png, m_info)), m_hasAlpha(m_type & PNG_COLOR_MASK_ALPHA), m_n(png_get_channels(m_png, m_info)), m_nWithoutAlpha(m_hasAlpha ? m_n - 1 : m_n), m_bitDepth(png_get_bit_depth(m_png, m_info)), m_byteDepth(m_bitDepth / 8) { } // Reads pixels into mainBuffer (RGB/gray channels) and maskBuffer (alpha channel). void readPixels(png_bytep mainBuffer, png_bytep maskBuffer) { // Read pixels from m_png. const int rowSize = png_get_rowbytes(m_png, m_info); png_bytepp pixels = new png_bytep[m_height]; for (int y = 0; y < m_height; y++) { pixels[y] = new png_byte[rowSize]; } png_read_image(m_png, pixels); // Copy pixels into mainBuffer and maskBuffer. const png_byte pixelSizeWithoutAlpha = m_nWithoutAlpha * m_byteDepth; for (int y = 0; y < m_height; y++) { png_bytep row = pixels[y]; for (int x = 0; x < m_width; x++) { memcpy(mainBuffer, row, pixelSizeWithoutAlpha); mainBuffer += pixelSizeWithoutAlpha; row += pixelSizeWithoutAlpha; if (m_hasAlpha) { memcpy(maskBuffer, row, m_byteDepth); maskBuffer += m_byteDepth; row += m_byteDepth; } } } // Cleanup. for (int y = 0; y < m_height; y++) { delete[] pixels[y]; } delete[] pixels; } // Supportive function for create(). // We don't want to deal with palette images. // We don't want to deal with 1/2/4-bit samples. static void fixPng(png_structp png, png_infop info) { const png_byte colorType = png_get_color_type(png, info); const png_byte bitDepth = png_get_bit_depth(png, info); bool updateRequired = false; if (colorType == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png); updateRequired = true; } if ((colorType == PNG_COLOR_TYPE_GRAY) && (bitDepth < 8)) { png_set_expand_gray_1_2_4_to_8(png); updateRequired = true; } if (png_get_valid(png, info, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(png); updateRequired = true; } if (bitDepth < 8) { png_set_packing(png); updateRequired = true; } if (updateRequired) { png_read_update_info(png, info); } } public: PngEmbedder() = delete; PngEmbedder(const PngEmbedder &) = delete; PngEmbedder &operator=(const PngEmbedder &) = delete; ~PngEmbedder() override { png_destroy_read_struct(&m_png, &m_info, nullptr); delete m_stream; } Ref embedImage(XRef *xref) override; static std::unique_ptr create(std::unique_ptr &&fileContent, const Goffset fileSize) { png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); if (png == nullptr) { error(errInternal, -1, "Couldn't load PNG. png_create_read_struct() failed"); return nullptr; } png_infop info = png_create_info_struct(png); if (info == nullptr) { error(errInternal, -1, "Couldn't load PNG. png_create_info_struct() failed"); png_destroy_read_struct(&png, nullptr, nullptr); return nullptr; } if (setjmp(png_jmpbuf(png))) { error(errInternal, -1, "Couldn't load PNG. Failed to set up error handling for reading PNG"); png_destroy_read_struct(&png, &info, nullptr); return nullptr; } LibpngInputStream *stream = new LibpngInputStream(std::move(fileContent), fileSize); png_set_read_fn(png, stream, LibpngInputStream::readCallback); png_read_info(png, info); fixPng(png, info); const png_byte bitDepth = png_get_bit_depth(png, info); if ((bitDepth != 8) && (bitDepth != 16)) { error(errInternal, -1, "Couldn't load PNG. Fixing bit depth failed"); png_destroy_read_struct(&png, &info, nullptr); delete stream; return nullptr; } return std::unique_ptr(new PngEmbedder(png, info, stream)); } }; Ref PngEmbedder::embedImage(XRef *xref) { // Read pixels. Goffset area; if (checkedMultiply(static_cast(m_width), static_cast(m_height), &area)) { error(errIO, -1, "PngEmbedder::embedImage: width * height overflows Goffset"); return Ref::INVALID(); } Goffset maskBufferSize; static_assert(sizeof(Goffset) >= sizeof(m_byteDepth)); if (checkedMultiply(area, static_cast(m_byteDepth), &maskBufferSize)) { error(errIO, -1, "PngEmbedder::embedImage: width * height * m_byteDepth overflows Goffset"); return Ref::INVALID(); } Goffset mainBufferSize; static_assert(sizeof(Goffset) >= sizeof(m_nWithoutAlpha)); if (checkedMultiply(maskBufferSize, static_cast(m_nWithoutAlpha), &mainBufferSize)) { error(errIO, -1, "PngEmbedder::embedImage: width * height * m_byteDepth * m_nWithoutAlpha overflows Goffset"); return Ref::INVALID(); } png_bytep mainBuffer = (png_bytep)gmalloc(mainBufferSize); png_bytep maskBuffer = (m_hasAlpha) ? (png_bytep)gmalloc(maskBufferSize) : nullptr; readPixels(mainBuffer, maskBuffer); // Create a mask XObject and a main XObject. const char *colorSpace = ((m_type == PNG_COLOR_TYPE_GRAY) || (m_type == PNG_COLOR_TYPE_GRAY_ALPHA)) ? DEVICE_GRAY : DEVICE_RGB; 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, StreamCompression::Compress); baseImageDict->add("SMask", Object(maskImageRef)); } return xref->addStreamObject(baseImageDict, mainBuffer, mainBufferSize, StreamCompression::Compress); } #endif #ifdef ENABLE_LIBJPEG struct JpegErrorManager { jpeg_error_mgr pub; jmp_buf setjmpBuffer; }; // Note: an address of pub is equal to an address of a JpegErrorManager instance. static void jpegExitErrorHandler(j_common_ptr info) { JpegErrorManager *errorManager = (JpegErrorManager *)info->err; (*errorManager->pub.output_message)(info); // Jump to the setjmp point. longjmp(errorManager->setjmpBuffer, 1); } // Transforms a JPEG image to XObject. class JpegEmbedder : public ImageEmbedder { std::unique_ptr m_fileContent; Goffset m_fileSize; JpegEmbedder(const int width, const int height, std::unique_ptr &&fileContent, const Goffset fileSize) : ImageEmbedder(width, height), m_fileContent(std::move(fileContent)), m_fileSize(fileSize) { } public: JpegEmbedder() = delete; JpegEmbedder(const JpegEmbedder &) = delete; JpegEmbedder &operator=(const JpegEmbedder &) = delete; ~JpegEmbedder() override = default; Ref embedImage(XRef *xref) override; static std::unique_ptr create(std::unique_ptr &&fileContent, const Goffset fileSize) { jpeg_decompress_struct info; JpegErrorManager errorManager; info.err = jpeg_std_error(&errorManager.pub); errorManager.pub.error_exit = jpegExitErrorHandler; if (setjmp(errorManager.setjmpBuffer)) { // The setjmp point. jpeg_destroy_decompress(&info); error(errInternal, -1, "libjpeg failed to process the file"); return nullptr; } jpeg_create_decompress(&info); // fileSize is guaranteed to be in the range 0..int max by the checks in embed() // jpeg_mem_src takes an unsigned long in the 3rd parameter jpeg_mem_src(&info, fileContent.get(), static_cast(fileSize)); jpeg_read_header(&info, TRUE); jpeg_start_decompress(&info); auto result = std::unique_ptr(new JpegEmbedder(info.output_width, info.output_height, std::move(fileContent), fileSize)); jpeg_abort_decompress(&info); jpeg_destroy_decompress(&info); return result; } }; Ref JpegEmbedder::embedImage(XRef *xref) { if (m_fileContent == nullptr) { return Ref::INVALID(); } 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, StreamCompression::None); return baseImageRef; } #endif Ref embed(XRef *xref, const GooFile &imageFile) { // Load the image file. const Goffset fileSize = imageFile.size(); if (fileSize < 0) { error(errIO, -1, "Image file size could not be calculated"); return Ref::INVALID(); } // GooFile::read only takes an integer so for now we don't support huge images if (fileSize > std::numeric_limits::max()) { error(errIO, -1, "file size too big"); return Ref::INVALID(); } std::unique_ptr fileContent = std::make_unique(fileSize); const int bytesRead = imageFile.read((char *)fileContent.get(), static_cast(fileSize), 0); if ((bytesRead != fileSize) || (fileSize < MAX_MAGIC_NUM_SIZE)) { error(errIO, -1, "Couldn't load the image file"); return Ref::INVALID(); } std::unique_ptr embedder; if (checkMagicNum(fileContent.get(), PNG_MAGIC_NUM, sizeof(PNG_MAGIC_NUM))) { #ifdef ENABLE_LIBPNG embedder = PngEmbedder::create(std::move(fileContent), fileSize); #else error(errUnimplemented, -1, "PNG format is not supported"); #endif } else if (checkMagicNum(fileContent.get(), JPEG_MAGIC_NUM, sizeof(JPEG_MAGIC_NUM))) { #ifdef ENABLE_LIBJPEG embedder = JpegEmbedder::create(std::move(fileContent), fileSize); #else error(errUnimplemented, -1, "JPEG format is not supported"); #endif } else if (checkMagicNum(fileContent.get(), JPEG2000_MAGIC_NUM, sizeof(JPEG2000_MAGIC_NUM))) { // TODO: implement JPEG2000 support using libopenjpeg2. error(errUnimplemented, -1, "JPEG2000 format is not supported"); return Ref::INVALID(); } else { error(errUnimplemented, -1, "Image format is not supported"); return Ref::INVALID(); } if (!embedder) { return Ref::INVALID(); } return embedder->embedImage(xref); } Ref embed(XRef *xref, const std::string &imagePath) { std::unique_ptr imageFile(GooFile::open(imagePath)); if (!imageFile) { error(errIO, -1, "Couldn't open {0:s}", imagePath.c_str()); return Ref::INVALID(); } return embed(xref, *imageFile); } } poppler-24.02.0/poppler/ImageEmbeddingUtils.h000066400000000000000000000017451455701731300210510ustar00rootroot00000000000000//======================================================================== // // ImageEmbeddingUtils.h // // Copyright (C) 2021 Georgiy Sgibnev . Work sponsored by lab50.net. // // This file is licensed under the GPLv2 or later // //======================================================================== #ifndef IMAGE_EMBEDDING_UTILS_H #define IMAGE_EMBEDDING_UTILS_H #include #include "poppler_private_export.h" class GooFile; struct Ref; class XRef; namespace ImageEmbeddingUtils { // Creates a new base image (an object of type XObject referred to in a resource dictionary). // Supported formats: PNG, JPEG. // Args: // xref: Document's xref. // imageFile: An image file to embed. // Returns ref to a new object or Ref::INVALID. Ref POPPLER_PRIVATE_EXPORT embed(XRef *xref, const GooFile &imageFile); // Same as above, but imagePath is a path to an image file. Ref POPPLER_PRIVATE_EXPORT embed(XRef *xref, const std::string &imagePath); } #endif poppler-24.02.0/poppler/JArithmeticDecoder.cc000066400000000000000000000234661455701731300210420ustar00rootroot00000000000000//======================================================================== // // JArithmeticDecoder.cc // // Copyright 2002-2004 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) 2019 Volker Krause // Copyright (C) 2020 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 // //======================================================================== #include #include "Object.h" #include "Stream.h" #include "JArithmeticDecoder.h" //------------------------------------------------------------------------ // JArithmeticDecoderStates //------------------------------------------------------------------------ JArithmeticDecoderStats::JArithmeticDecoderStats(int contextSizeA) { contextSize = contextSizeA; cxTab = (unsigned char *)gmallocn_checkoverflow(contextSize, sizeof(unsigned char)); if (cxTab) { reset(); } } JArithmeticDecoderStats::~JArithmeticDecoderStats() { gfree(cxTab); } JArithmeticDecoderStats *JArithmeticDecoderStats::copy() { JArithmeticDecoderStats *stats; stats = new JArithmeticDecoderStats(contextSize); memcpy(stats->cxTab, cxTab, contextSize); return stats; } void JArithmeticDecoderStats::reset() { memset(cxTab, 0, contextSize); } void JArithmeticDecoderStats::copyFrom(JArithmeticDecoderStats *stats) { memcpy(cxTab, stats->cxTab, contextSize); } void JArithmeticDecoderStats::setEntry(unsigned int cx, int i, int mps) { cxTab[cx] = (i << 1) + mps; } //------------------------------------------------------------------------ // JArithmeticDecoder //------------------------------------------------------------------------ unsigned const int JArithmeticDecoder::qeTab[47] = { 0x56010000, 0x34010000, 0x18010000, 0x0AC10000, 0x05210000, 0x02210000, 0x56010000, 0x54010000, 0x48010000, 0x38010000, 0x30010000, 0x24010000, 0x1C010000, 0x16010000, 0x56010000, 0x54010000, 0x51010000, 0x48010000, 0x38010000, 0x34010000, 0x30010000, 0x28010000, 0x24010000, 0x22010000, 0x1C010000, 0x18010000, 0x16010000, 0x14010000, 0x12010000, 0x11010000, 0x0AC10000, 0x09C10000, 0x08A10000, 0x05210000, 0x04410000, 0x02A10000, 0x02210000, 0x01410000, 0x01110000, 0x00850000, 0x00490000, 0x00250000, 0x00150000, 0x00090000, 0x00050000, 0x00010000, 0x56010000 }; const int JArithmeticDecoder::nmpsTab[47] = { 1, 2, 3, 4, 5, 38, 7, 8, 9, 10, 11, 12, 13, 29, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 45, 46 }; const int JArithmeticDecoder::nlpsTab[47] = { 1, 6, 9, 12, 29, 33, 6, 14, 14, 14, 17, 18, 20, 21, 14, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 46 }; const int JArithmeticDecoder::switchTab[47] = { 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; JArithmeticDecoder::JArithmeticDecoder() { str = nullptr; dataLen = 0; limitStream = false; nBytesRead = 0; } inline unsigned int JArithmeticDecoder::readByte() { if (limitStream) { --dataLen; if (dataLen < 0) { return 0xff; } } ++nBytesRead; return (unsigned int)str->getChar() & 0xff; } JArithmeticDecoder::~JArithmeticDecoder() { cleanup(); } void JArithmeticDecoder::start() { buf0 = readByte(); buf1 = readByte(); // INITDEC c = (buf0 ^ 0xff) << 16; byteIn(); c <<= 7; ct -= 7; a = 0x80000000; } void JArithmeticDecoder::restart(int dataLenA) { unsigned int cAdd; bool prevFF; int k, nBits; if (dataLen >= 0) { dataLen = dataLenA; } else if (dataLen == -1) { dataLen = dataLenA; buf1 = readByte(); } else { k = (-dataLen - 1) * 8 - ct; dataLen = dataLenA; cAdd = 0; prevFF = false; while (k > 0) { buf0 = readByte(); if (prevFF) { cAdd += 0xfe00 - (buf0 << 9); nBits = 7; } else { cAdd += 0xff00 - (buf0 << 8); nBits = 8; } prevFF = buf0 == 0xff; if (k > nBits) { cAdd <<= nBits; k -= nBits; } else { cAdd <<= k; ct = nBits - k; k = 0; } } c += cAdd; buf1 = readByte(); } } void JArithmeticDecoder::cleanup() { if (limitStream) { while (dataLen > 0) { buf0 = buf1; buf1 = readByte(); } } } int JArithmeticDecoder::decodeBit(unsigned int context, JArithmeticDecoderStats *stats) { int bit; unsigned int qe; int iCX, mpsCX; iCX = stats->cxTab[context] >> 1; mpsCX = stats->cxTab[context] & 1; qe = qeTab[iCX]; a -= qe; if (c < a) { if (a & 0x80000000) { bit = mpsCX; } else { // MPS_EXCHANGE if (a < qe) { bit = 1 - mpsCX; if (switchTab[iCX]) { stats->cxTab[context] = (nlpsTab[iCX] << 1) | (1 - mpsCX); } else { stats->cxTab[context] = (nlpsTab[iCX] << 1) | mpsCX; } } else { bit = mpsCX; stats->cxTab[context] = (nmpsTab[iCX] << 1) | mpsCX; } // RENORMD do { if (ct == 0) { byteIn(); } a <<= 1; c <<= 1; --ct; } while (!(a & 0x80000000)); } } else { c -= a; // LPS_EXCHANGE if (a < qe) { bit = mpsCX; stats->cxTab[context] = (nmpsTab[iCX] << 1) | mpsCX; } else { bit = 1 - mpsCX; if (switchTab[iCX]) { stats->cxTab[context] = (nlpsTab[iCX] << 1) | (1 - mpsCX); } else { stats->cxTab[context] = (nlpsTab[iCX] << 1) | mpsCX; } } a = qe; // RENORMD do { if (ct == 0) { byteIn(); } a <<= 1; c <<= 1; --ct; } while (!(a & 0x80000000)); } return bit; } int JArithmeticDecoder::decodeByte(unsigned int context, JArithmeticDecoderStats *stats) { int byte; int i; byte = 0; for (i = 0; i < 8; ++i) { byte = (byte << 1) | decodeBit(context, stats); } return byte; } bool JArithmeticDecoder::decodeInt(int *x, JArithmeticDecoderStats *stats) { int s; unsigned int v; int i; prev = 1; s = decodeIntBit(stats); if (decodeIntBit(stats)) { if (decodeIntBit(stats)) { if (decodeIntBit(stats)) { if (decodeIntBit(stats)) { if (decodeIntBit(stats)) { v = 0; for (i = 0; i < 32; ++i) { v = (v << 1) | decodeIntBit(stats); } v += 4436; } else { v = 0; for (i = 0; i < 12; ++i) { v = (v << 1) | decodeIntBit(stats); } v += 340; } } else { v = 0; for (i = 0; i < 8; ++i) { v = (v << 1) | decodeIntBit(stats); } v += 84; } } else { v = 0; for (i = 0; i < 6; ++i) { v = (v << 1) | decodeIntBit(stats); } v += 20; } } else { v = decodeIntBit(stats); v = (v << 1) | decodeIntBit(stats); v = (v << 1) | decodeIntBit(stats); v = (v << 1) | decodeIntBit(stats); v += 4; } } else { v = decodeIntBit(stats); v = (v << 1) | decodeIntBit(stats); } if (s) { if (v == 0) { return false; } *x = -(int)v; } else { *x = (int)v; } return true; } int JArithmeticDecoder::decodeIntBit(JArithmeticDecoderStats *stats) { int bit; bit = decodeBit(prev, stats); if (prev < 0x100) { prev = (prev << 1) | bit; } else { prev = (((prev << 1) | bit) & 0x1ff) | 0x100; } return bit; } unsigned int JArithmeticDecoder::decodeIAID(unsigned int codeLen, JArithmeticDecoderStats *stats) { unsigned int i; int bit; prev = 1; for (i = 0; i < codeLen; ++i) { bit = decodeBit(prev, stats); prev = (prev << 1) | bit; } return prev - (1 << codeLen); } void JArithmeticDecoder::byteIn() { if (buf0 == 0xff) { if (buf1 > 0x8f) { if (limitStream) { buf0 = buf1; buf1 = readByte(); c = c + 0xff00 - (buf0 << 8); } ct = 8; } else { buf0 = buf1; buf1 = readByte(); c = c + 0xfe00 - (buf0 << 9); ct = 7; } } else { buf0 = buf1; buf1 = readByte(); c = c + 0xff00 - (buf0 << 8); ct = 8; } } poppler-24.02.0/poppler/JArithmeticDecoder.h000066400000000000000000000076021455701731300206760ustar00rootroot00000000000000//======================================================================== // // JArithmeticDecoder.h // // Arithmetic decoder used by the JBIG2 and JPEG2000 decoders. // // Copyright 2002-2004 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) 2018, 2021 Albert Astals Cid // Copyright (C) 2019 Volker Krause // Copyright (C) 2020 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 // //======================================================================== #ifndef JARITHMETICDECODER_H #define JARITHMETICDECODER_H class Stream; //------------------------------------------------------------------------ // JArithmeticDecoderStats //------------------------------------------------------------------------ class JArithmeticDecoderStats { public: explicit JArithmeticDecoderStats(int contextSizeA); ~JArithmeticDecoderStats(); JArithmeticDecoderStats(const JArithmeticDecoderStats &) = delete; JArithmeticDecoderStats &operator=(const JArithmeticDecoderStats &) = delete; JArithmeticDecoderStats *copy(); void reset(); int getContextSize() { return contextSize; } void copyFrom(JArithmeticDecoderStats *stats); void setEntry(unsigned int cx, int i, int mps); bool isValid() const { return cxTab != nullptr; } private: unsigned char *cxTab; // cxTab[cx] = (i[cx] << 1) + mps[cx] int contextSize; friend class JArithmeticDecoder; }; //------------------------------------------------------------------------ // JArithmeticDecoder //------------------------------------------------------------------------ class JArithmeticDecoder { public: JArithmeticDecoder(); ~JArithmeticDecoder(); JArithmeticDecoder(const JArithmeticDecoder &) = delete; JArithmeticDecoder &operator=(const JArithmeticDecoder &) = delete; void setStream(Stream *strA) { str = strA; dataLen = 0; limitStream = false; } void setStream(Stream *strA, int dataLenA) { str = strA; dataLen = dataLenA; limitStream = true; } // Start decoding on a new stream. This fills the byte buffers and // runs INITDEC. void start(); // Restart decoding on an interrupted stream. This refills the // buffers if needed, but does not run INITDEC. (This is used in // JPEG 2000 streams when codeblock data is split across multiple // packets/layers.) void restart(int dataLenA); // Read any leftover data in the stream. void cleanup(); // Decode one bit. int decodeBit(unsigned int context, JArithmeticDecoderStats *stats); // Decode eight bits. int decodeByte(unsigned int context, JArithmeticDecoderStats *stats); // Returns false for OOB, otherwise sets * and returns true. bool decodeInt(int *x, JArithmeticDecoderStats *stats); unsigned int decodeIAID(unsigned int codeLen, JArithmeticDecoderStats *stats); void resetByteCounter() { nBytesRead = 0; } unsigned int getByteCounter() { return nBytesRead; } private: unsigned int readByte(); int decodeIntBit(JArithmeticDecoderStats *stats); void byteIn(); static const unsigned int qeTab[47]; static const int nmpsTab[47]; static const int nlpsTab[47]; static const int switchTab[47]; unsigned int buf0, buf1; unsigned int c, a; int ct; unsigned int prev; // for the integer decoder Stream *str; unsigned int nBytesRead; int dataLen; bool limitStream; }; #endif poppler-24.02.0/poppler/JBIG2Stream.cc000066400000000000000000004363711455701731300173250ustar00rootroot00000000000000//======================================================================== // // JBIG2Stream.cc // // Copyright 2002-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 Raj Kumar // Copyright (C) 2006 Paul Walmsley // Copyright (C) 2006-2010, 2012, 2014-2022 Albert Astals Cid // Copyright (C) 2009 David Benjamin // Copyright (C) 2011 Edward Jiang // Copyright (C) 2012 William Bader // Copyright (C) 2012 Thomas Freitag // Copyright (C) 2013 Adrian Johnson // Copyright (C) 2013, 2014 Fabio D'Urso // Copyright (C) 2015 Suzuki Toshiya // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019 LE GARREC Vincent // Copyright (C) 2019-2021 Oliver Sander // Copyright (C) 2019 Volker Krause // Copyright (C) 2019-2021 Even Rouault // Copyright (C) 2014 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 // //======================================================================== #include #include #include #include #include "Error.h" #include "JArithmeticDecoder.h" #include "JBIG2Stream.h" //~ share these tables #include "Stream-CCITT.h" //------------------------------------------------------------------------ static const int contextSize[4] = { 16, 13, 10, 10 }; static const int refContextSize[2] = { 13, 10 }; //------------------------------------------------------------------------ // JBIG2HuffmanTable //------------------------------------------------------------------------ #define jbig2HuffmanLOW 0xfffffffd #define jbig2HuffmanOOB 0xfffffffe #define jbig2HuffmanEOT 0xffffffff struct JBIG2HuffmanTable { int val; unsigned int prefixLen; unsigned int rangeLen; // can also be LOW, OOB, or EOT unsigned int prefix; }; static const JBIG2HuffmanTable huffTableA[] = { { 0, 1, 4, 0x000 }, { 16, 2, 8, 0x002 }, { 272, 3, 16, 0x006 }, { 65808, 3, 32, 0x007 }, { 0, 0, jbig2HuffmanEOT, 0 } }; static const JBIG2HuffmanTable huffTableB[] = { { 0, 1, 0, 0x000 }, { 1, 2, 0, 0x002 }, { 2, 3, 0, 0x006 }, { 3, 4, 3, 0x00e }, { 11, 5, 6, 0x01e }, { 75, 6, 32, 0x03e }, { 0, 6, jbig2HuffmanOOB, 0x03f }, { 0, 0, jbig2HuffmanEOT, 0 } }; static const JBIG2HuffmanTable huffTableC[] = { { 0, 1, 0, 0x000 }, { 1, 2, 0, 0x002 }, { 2, 3, 0, 0x006 }, { 3, 4, 3, 0x00e }, { 11, 5, 6, 0x01e }, { 0, 6, jbig2HuffmanOOB, 0x03e }, { 75, 7, 32, 0x0fe }, { -256, 8, 8, 0x0fe }, { -257, 8, jbig2HuffmanLOW, 0x0ff }, { 0, 0, jbig2HuffmanEOT, 0 } }; static const JBIG2HuffmanTable huffTableD[] = { { 1, 1, 0, 0x000 }, { 2, 2, 0, 0x002 }, { 3, 3, 0, 0x006 }, { 4, 4, 3, 0x00e }, { 12, 5, 6, 0x01e }, { 76, 5, 32, 0x01f }, { 0, 0, jbig2HuffmanEOT, 0 } }; static const JBIG2HuffmanTable huffTableE[] = { { 1, 1, 0, 0x000 }, { 2, 2, 0, 0x002 }, { 3, 3, 0, 0x006 }, { 4, 4, 3, 0x00e }, { 12, 5, 6, 0x01e }, { 76, 6, 32, 0x03e }, { -255, 7, 8, 0x07e }, { -256, 7, jbig2HuffmanLOW, 0x07f }, { 0, 0, jbig2HuffmanEOT, 0 } }; static const JBIG2HuffmanTable huffTableF[] = { { 0, 2, 7, 0x000 }, { 128, 3, 7, 0x002 }, { 256, 3, 8, 0x003 }, { -1024, 4, 9, 0x008 }, { -512, 4, 8, 0x009 }, { -256, 4, 7, 0x00a }, { -32, 4, 5, 0x00b }, { 512, 4, 9, 0x00c }, { 1024, 4, 10, 0x00d }, { -2048, 5, 10, 0x01c }, { -128, 5, 6, 0x01d }, { -64, 5, 5, 0x01e }, { -2049, 6, jbig2HuffmanLOW, 0x03e }, { 2048, 6, 32, 0x03f }, { 0, 0, jbig2HuffmanEOT, 0 } }; static const JBIG2HuffmanTable huffTableG[] = { { -512, 3, 8, 0x000 }, { 256, 3, 8, 0x001 }, { 512, 3, 9, 0x002 }, { 1024, 3, 10, 0x003 }, { -1024, 4, 9, 0x008 }, { -256, 4, 7, 0x009 }, { -32, 4, 5, 0x00a }, { 0, 4, 5, 0x00b }, { 128, 4, 7, 0x00c }, { -128, 5, 6, 0x01a }, { -64, 5, 5, 0x01b }, { 32, 5, 5, 0x01c }, { 64, 5, 6, 0x01d }, { -1025, 5, jbig2HuffmanLOW, 0x01e }, { 2048, 5, 32, 0x01f }, { 0, 0, jbig2HuffmanEOT, 0 } }; static const JBIG2HuffmanTable huffTableH[] = { { 0, 2, 1, 0x000 }, { 0, 2, jbig2HuffmanOOB, 0x001 }, { 4, 3, 4, 0x004 }, { -1, 4, 0, 0x00a }, { 22, 4, 4, 0x00b }, { 38, 4, 5, 0x00c }, { 2, 5, 0, 0x01a }, { 70, 5, 6, 0x01b }, { 134, 5, 7, 0x01c }, { 3, 6, 0, 0x03a }, { 20, 6, 1, 0x03b }, { 262, 6, 7, 0x03c }, { 646, 6, 10, 0x03d }, { -2, 7, 0, 0x07c }, { 390, 7, 8, 0x07d }, { -15, 8, 3, 0x0fc }, { -5, 8, 1, 0x0fd }, { -7, 9, 1, 0x1fc }, { -3, 9, 0, 0x1fd }, { -16, 9, jbig2HuffmanLOW, 0x1fe }, { 1670, 9, 32, 0x1ff }, { 0, 0, jbig2HuffmanEOT, 0 } }; static const JBIG2HuffmanTable huffTableI[] = { { 0, 2, jbig2HuffmanOOB, 0x000 }, { -1, 3, 1, 0x002 }, { 1, 3, 1, 0x003 }, { 7, 3, 5, 0x004 }, { -3, 4, 1, 0x00a }, { 43, 4, 5, 0x00b }, { 75, 4, 6, 0x00c }, { 3, 5, 1, 0x01a }, { 139, 5, 7, 0x01b }, { 267, 5, 8, 0x01c }, { 5, 6, 1, 0x03a }, { 39, 6, 2, 0x03b }, { 523, 6, 8, 0x03c }, { 1291, 6, 11, 0x03d }, { -5, 7, 1, 0x07c }, { 779, 7, 9, 0x07d }, { -31, 8, 4, 0x0fc }, { -11, 8, 2, 0x0fd }, { -15, 9, 2, 0x1fc }, { -7, 9, 1, 0x1fd }, { -32, 9, jbig2HuffmanLOW, 0x1fe }, { 3339, 9, 32, 0x1ff }, { 0, 0, jbig2HuffmanEOT, 0 } }; static const JBIG2HuffmanTable huffTableJ[] = { { -2, 2, 2, 0x000 }, { 6, 2, 6, 0x001 }, { 0, 2, jbig2HuffmanOOB, 0x002 }, { -3, 5, 0, 0x018 }, { 2, 5, 0, 0x019 }, { 70, 5, 5, 0x01a }, { 3, 6, 0, 0x036 }, { 102, 6, 5, 0x037 }, { 134, 6, 6, 0x038 }, { 198, 6, 7, 0x039 }, { 326, 6, 8, 0x03a }, { 582, 6, 9, 0x03b }, { 1094, 6, 10, 0x03c }, { -21, 7, 4, 0x07a }, { -4, 7, 0, 0x07b }, { 4, 7, 0, 0x07c }, { 2118, 7, 11, 0x07d }, { -5, 8, 0, 0x0fc }, { 5, 8, 0, 0x0fd }, { -22, 8, jbig2HuffmanLOW, 0x0fe }, { 4166, 8, 32, 0x0ff }, { 0, 0, jbig2HuffmanEOT, 0 } }; static const JBIG2HuffmanTable huffTableK[] = { { 1, 1, 0, 0x000 }, { 2, 2, 1, 0x002 }, { 4, 4, 0, 0x00c }, { 5, 4, 1, 0x00d }, { 7, 5, 1, 0x01c }, { 9, 5, 2, 0x01d }, { 13, 6, 2, 0x03c }, { 17, 7, 2, 0x07a }, { 21, 7, 3, 0x07b }, { 29, 7, 4, 0x07c }, { 45, 7, 5, 0x07d }, { 77, 7, 6, 0x07e }, { 141, 7, 32, 0x07f }, { 0, 0, jbig2HuffmanEOT, 0 } }; static const JBIG2HuffmanTable huffTableL[] = { { 1, 1, 0, 0x000 }, { 2, 2, 0, 0x002 }, { 3, 3, 1, 0x006 }, { 5, 5, 0, 0x01c }, { 6, 5, 1, 0x01d }, { 8, 6, 1, 0x03c }, { 10, 7, 0, 0x07a }, { 11, 7, 1, 0x07b }, { 13, 7, 2, 0x07c }, { 17, 7, 3, 0x07d }, { 25, 7, 4, 0x07e }, { 41, 8, 5, 0x0fe }, { 73, 8, 32, 0x0ff }, { 0, 0, jbig2HuffmanEOT, 0 } }; static const JBIG2HuffmanTable huffTableM[] = { { 1, 1, 0, 0x000 }, { 2, 3, 0, 0x004 }, { 7, 3, 3, 0x005 }, { 3, 4, 0, 0x00c }, { 5, 4, 1, 0x00d }, { 4, 5, 0, 0x01c }, { 15, 6, 1, 0x03a }, { 17, 6, 2, 0x03b }, { 21, 6, 3, 0x03c }, { 29, 6, 4, 0x03d }, { 45, 6, 5, 0x03e }, { 77, 7, 6, 0x07e }, { 141, 7, 32, 0x07f }, { 0, 0, jbig2HuffmanEOT, 0 } }; static const JBIG2HuffmanTable huffTableN[] = { { 0, 1, 0, 0x000 }, { -2, 3, 0, 0x004 }, { -1, 3, 0, 0x005 }, { 1, 3, 0, 0x006 }, { 2, 3, 0, 0x007 }, { 0, 0, jbig2HuffmanEOT, 0 } }; static const JBIG2HuffmanTable huffTableO[] = { { 0, 1, 0, 0x000 }, { -1, 3, 0, 0x004 }, { 1, 3, 0, 0x005 }, { -2, 4, 0, 0x00c }, { 2, 4, 0, 0x00d }, { -4, 5, 1, 0x01c }, { 3, 5, 1, 0x01d }, { -8, 6, 2, 0x03c }, { 5, 6, 2, 0x03d }, { -24, 7, 4, 0x07c }, { 9, 7, 4, 0x07d }, { -25, 7, jbig2HuffmanLOW, 0x07e }, { 25, 7, 32, 0x07f }, { 0, 0, jbig2HuffmanEOT, 0 } }; //------------------------------------------------------------------------ // JBIG2HuffmanDecoder //------------------------------------------------------------------------ class JBIG2HuffmanDecoder { public: JBIG2HuffmanDecoder(); ~JBIG2HuffmanDecoder(); void setStream(Stream *strA) { str = strA; } void reset(); // Returns false for OOB, otherwise sets * and returns true. bool decodeInt(int *x, const JBIG2HuffmanTable *table); unsigned int readBits(unsigned int n); unsigned int readBit(); // Sort the table by prefix length and assign prefix values. static bool buildTable(JBIG2HuffmanTable *table, unsigned int len); void resetByteCounter() { byteCounter = 0; } unsigned int getByteCounter() const { return byteCounter; } private: Stream *str; unsigned int buf; unsigned int bufLen; unsigned int byteCounter; }; JBIG2HuffmanDecoder::JBIG2HuffmanDecoder() { str = nullptr; byteCounter = 0; reset(); } JBIG2HuffmanDecoder::~JBIG2HuffmanDecoder() { } void JBIG2HuffmanDecoder::reset() { buf = 0; bufLen = 0; } //~ optimize this bool JBIG2HuffmanDecoder::decodeInt(int *x, const JBIG2HuffmanTable *table) { unsigned int i, len, prefix; i = 0; len = 0; prefix = 0; while (table[i].rangeLen != jbig2HuffmanEOT) { while (len < table[i].prefixLen) { prefix = (prefix << 1) | readBit(); ++len; } if (prefix == table[i].prefix) { if (table[i].rangeLen == jbig2HuffmanOOB) { return false; } if (table[i].rangeLen == jbig2HuffmanLOW) { *x = table[i].val - readBits(32); } else if (table[i].rangeLen > 0) { *x = table[i].val + readBits(table[i].rangeLen); } else { *x = table[i].val; } return true; } ++i; } return false; } unsigned int JBIG2HuffmanDecoder::readBits(unsigned int n) { unsigned int x, mask, nLeft; mask = (n == 32) ? 0xffffffff : ((1 << n) - 1); if (bufLen >= n) { x = (buf >> (bufLen - n)) & mask; bufLen -= n; } else { x = buf & ((1 << bufLen) - 1); nLeft = n - bufLen; bufLen = 0; while (nLeft >= 8) { x = (x << 8) | (str->getChar() & 0xff); ++byteCounter; nLeft -= 8; } if (nLeft > 0) { buf = str->getChar(); ++byteCounter; bufLen = 8 - nLeft; x = (x << nLeft) | ((buf >> bufLen) & ((1 << nLeft) - 1)); } } return x; } unsigned int JBIG2HuffmanDecoder::readBit() { if (bufLen == 0) { buf = str->getChar(); ++byteCounter; bufLen = 8; } --bufLen; return (buf >> bufLen) & 1; } bool JBIG2HuffmanDecoder::buildTable(JBIG2HuffmanTable *table, unsigned int len) { unsigned int i, j, k, prefix; JBIG2HuffmanTable tab; // stable selection sort: // - entries with prefixLen > 0, in ascending prefixLen order // - entry with prefixLen = 0, rangeLen = EOT // - all other entries with prefixLen = 0 // (on entry, table[len] has prefixLen = 0, rangeLen = EOT) for (i = 0; i < len; ++i) { for (j = i; j < len && table[j].prefixLen == 0; ++j) { ; } if (j == len) { break; } for (k = j + 1; k < len; ++k) { if (table[k].prefixLen > 0 && table[k].prefixLen < table[j].prefixLen) { j = k; } } if (j != i) { tab = table[j]; for (k = j; k > i; --k) { table[k] = table[k - 1]; } table[i] = tab; } } table[i] = table[len]; // assign prefixes if (table[0].rangeLen != jbig2HuffmanEOT) { i = 0; prefix = 0; table[i++].prefix = prefix++; for (; table[i].rangeLen != jbig2HuffmanEOT; ++i) { if (table[i].prefixLen - table[i - 1].prefixLen > 32) { error(errSyntaxError, -1, "Failed to build table for JBIG2 stream"); return false; } else { prefix <<= table[i].prefixLen - table[i - 1].prefixLen; } table[i].prefix = prefix++; } } return true; } //------------------------------------------------------------------------ // JBIG2MMRDecoder //------------------------------------------------------------------------ class JBIG2MMRDecoder { public: JBIG2MMRDecoder(); ~JBIG2MMRDecoder(); void setStream(Stream *strA) { str = strA; } void reset(); int get2DCode(); int getBlackCode(); int getWhiteCode(); unsigned int get24Bits(); void resetByteCounter() { byteCounter = 0; } unsigned int getByteCounter() const { return byteCounter; } void skipTo(unsigned int length); private: Stream *str; unsigned int buf; unsigned int bufLen; unsigned int nBytesRead; unsigned int byteCounter; }; JBIG2MMRDecoder::JBIG2MMRDecoder() { str = nullptr; byteCounter = 0; reset(); } JBIG2MMRDecoder::~JBIG2MMRDecoder() { } void JBIG2MMRDecoder::reset() { buf = 0; bufLen = 0; nBytesRead = 0; } int JBIG2MMRDecoder::get2DCode() { const CCITTCode *p = nullptr; if (bufLen == 0) { buf = str->getChar() & 0xff; bufLen = 8; ++nBytesRead; ++byteCounter; p = &twoDimTab1[(buf >> 1) & 0x7f]; } else if (bufLen == 8) { p = &twoDimTab1[(buf >> 1) & 0x7f]; } else if (bufLen < 8) { p = &twoDimTab1[(buf << (7 - bufLen)) & 0x7f]; if (p->bits < 0 || p->bits > (int)bufLen) { buf = (buf << 8) | (str->getChar() & 0xff); bufLen += 8; ++nBytesRead; ++byteCounter; p = &twoDimTab1[(buf >> (bufLen - 7)) & 0x7f]; } } if (p == nullptr || p->bits < 0) { error(errSyntaxError, str->getPos(), "Bad two dim code in JBIG2 MMR stream"); return EOF; } bufLen -= p->bits; return p->n; } int JBIG2MMRDecoder::getWhiteCode() { const CCITTCode *p; unsigned int code; if (bufLen == 0) { buf = str->getChar() & 0xff; bufLen = 8; ++nBytesRead; ++byteCounter; } while (true) { if (bufLen >= 11 && ((buf >> (bufLen - 7)) & 0x7f) == 0) { if (bufLen <= 12) { code = buf << (12 - bufLen); } else { code = buf >> (bufLen - 12); } p = &whiteTab1[code & 0x1f]; } else { if (bufLen <= 9) { code = buf << (9 - bufLen); } else { code = buf >> (bufLen - 9); } p = &whiteTab2[code & 0x1ff]; } if (p->bits > 0 && p->bits <= (int)bufLen) { bufLen -= p->bits; return p->n; } if (bufLen >= 12) { break; } buf = (buf << 8) | (str->getChar() & 0xff); bufLen += 8; ++nBytesRead; ++byteCounter; } error(errSyntaxError, str->getPos(), "Bad white code in JBIG2 MMR stream"); // eat a bit and return a positive number so that the caller doesn't // go into an infinite loop --bufLen; return 1; } int JBIG2MMRDecoder::getBlackCode() { const CCITTCode *p; unsigned int code; if (bufLen == 0) { buf = str->getChar() & 0xff; bufLen = 8; ++nBytesRead; ++byteCounter; } while (true) { if (bufLen >= 10 && ((buf >> (bufLen - 6)) & 0x3f) == 0) { if (bufLen <= 13) { code = buf << (13 - bufLen); } else { code = buf >> (bufLen - 13); } p = &blackTab1[code & 0x7f]; } else if (bufLen >= 7 && ((buf >> (bufLen - 4)) & 0x0f) == 0 && ((buf >> (bufLen - 6)) & 0x03) != 0) { if (bufLen <= 12) { code = buf << (12 - bufLen); } else { code = buf >> (bufLen - 12); } if (unlikely((code & 0xff) < 64)) { break; } p = &blackTab2[(code & 0xff) - 64]; } else { if (bufLen <= 6) { code = buf << (6 - bufLen); } else { code = buf >> (bufLen - 6); } p = &blackTab3[code & 0x3f]; } if (p->bits > 0 && p->bits <= (int)bufLen) { bufLen -= p->bits; return p->n; } if (bufLen >= 13) { break; } buf = (buf << 8) | (str->getChar() & 0xff); bufLen += 8; ++nBytesRead; ++byteCounter; } error(errSyntaxError, str->getPos(), "Bad black code in JBIG2 MMR stream"); // eat a bit and return a positive number so that the caller doesn't // go into an infinite loop --bufLen; return 1; } unsigned int JBIG2MMRDecoder::get24Bits() { while (bufLen < 24) { buf = (buf << 8) | (str->getChar() & 0xff); bufLen += 8; ++nBytesRead; ++byteCounter; } return (buf >> (bufLen - 24)) & 0xffffff; } void JBIG2MMRDecoder::skipTo(unsigned int length) { int n = str->discardChars(length - nBytesRead); nBytesRead += n; byteCounter += n; } //------------------------------------------------------------------------ // JBIG2Segment //------------------------------------------------------------------------ enum JBIG2SegmentType { jbig2SegBitmap, jbig2SegSymbolDict, jbig2SegPatternDict, jbig2SegCodeTable }; class JBIG2Segment { public: explicit JBIG2Segment(unsigned int segNumA) { segNum = segNumA; } virtual ~JBIG2Segment(); JBIG2Segment(const JBIG2Segment &) = delete; JBIG2Segment &operator=(const JBIG2Segment &) = delete; void setSegNum(unsigned int segNumA) { segNum = segNumA; } unsigned int getSegNum() { return segNum; } virtual JBIG2SegmentType getType() = 0; private: unsigned int segNum; }; JBIG2Segment::~JBIG2Segment() = default; //------------------------------------------------------------------------ // JBIG2Bitmap //------------------------------------------------------------------------ struct JBIG2BitmapPtr { unsigned char *p; int shift; int x; }; class JBIG2Bitmap : public JBIG2Segment { public: JBIG2Bitmap(unsigned int segNumA, int wA, int hA); explicit JBIG2Bitmap(JBIG2Bitmap *bitmap); ~JBIG2Bitmap() override; JBIG2SegmentType getType() override { return jbig2SegBitmap; } JBIG2Bitmap *getSlice(unsigned int x, unsigned int y, unsigned int wA, unsigned int hA); void expand(int newH, unsigned int pixel); void clearToZero(); void clearToOne(); int getWidth() const { return w; } int getHeight() const { return h; } int getLineSize() const { return line; } int getPixel(int x, int y) const { return (x < 0 || x >= w || y < 0 || y >= h) ? 0 : (data[y * line + (x >> 3)] >> (7 - (x & 7))) & 1; } void setPixel(int x, int y) { data[y * line + (x >> 3)] |= 1 << (7 - (x & 7)); } void clearPixel(int x, int y) { data[y * line + (x >> 3)] &= 0x7f7f >> (x & 7); } void getPixelPtr(int x, int y, JBIG2BitmapPtr *ptr); int nextPixel(JBIG2BitmapPtr *ptr); void duplicateRow(int yDest, int ySrc); void combine(JBIG2Bitmap *bitmap, int x, int y, unsigned int combOp); unsigned char *getDataPtr() { return data; } int getDataSize() const { return h * line; } bool isOk() const { return data != nullptr; } private: int w, h, line; unsigned char *data; }; JBIG2Bitmap::JBIG2Bitmap(unsigned int segNumA, int wA, int hA) : JBIG2Segment(segNumA) { w = wA; h = hA; int auxW; if (unlikely(checkedAdd(wA, 7, &auxW))) { error(errSyntaxError, -1, "invalid width"); data = nullptr; return; } line = auxW >> 3; if (w <= 0 || h <= 0 || line <= 0 || h >= (INT_MAX - 1) / line) { error(errSyntaxError, -1, "invalid width/height"); data = nullptr; return; } // need to allocate one extra guard byte for use in combine() data = (unsigned char *)gmalloc_checkoverflow(h * line + 1); if (data != nullptr) { data[h * line] = 0; } } JBIG2Bitmap::JBIG2Bitmap(JBIG2Bitmap *bitmap) : JBIG2Segment(0) { if (unlikely(bitmap == nullptr)) { error(errSyntaxError, -1, "NULL bitmap in JBIG2Bitmap"); w = h = line = 0; data = nullptr; return; } w = bitmap->w; h = bitmap->h; line = bitmap->line; if (w <= 0 || h <= 0 || line <= 0 || h >= (INT_MAX - 1) / line) { error(errSyntaxError, -1, "invalid width/height"); data = nullptr; return; } // need to allocate one extra guard byte for use in combine() data = (unsigned char *)gmalloc(h * line + 1); memcpy(data, bitmap->data, h * line); data[h * line] = 0; } JBIG2Bitmap::~JBIG2Bitmap() { gfree(data); } //~ optimize this JBIG2Bitmap *JBIG2Bitmap::getSlice(unsigned int x, unsigned int y, unsigned int wA, unsigned int hA) { JBIG2Bitmap *slice; unsigned int xx, yy; if (!data) { return nullptr; } slice = new JBIG2Bitmap(0, wA, hA); if (slice->isOk()) { slice->clearToZero(); for (yy = 0; yy < hA; ++yy) { for (xx = 0; xx < wA; ++xx) { if (getPixel(x + xx, y + yy)) { slice->setPixel(xx, yy); } } } } else { delete slice; slice = nullptr; } return slice; } void JBIG2Bitmap::expand(int newH, unsigned int pixel) { if (unlikely(!data)) { return; } if (newH <= h || line <= 0 || newH >= (INT_MAX - 1) / line) { error(errSyntaxError, -1, "invalid width/height"); gfree(data); data = nullptr; return; } // need to allocate one extra guard byte for use in combine() data = (unsigned char *)grealloc(data, newH * line + 1); if (pixel) { memset(data + h * line, 0xff, (newH - h) * line); } else { memset(data + h * line, 0x00, (newH - h) * line); } h = newH; data[h * line] = 0; } void JBIG2Bitmap::clearToZero() { memset(data, 0, h * line); } void JBIG2Bitmap::clearToOne() { memset(data, 0xff, h * line); } inline void JBIG2Bitmap::getPixelPtr(int x, int y, JBIG2BitmapPtr *ptr) { if (y < 0 || y >= h || x >= w) { ptr->p = nullptr; ptr->shift = 0; // make gcc happy ptr->x = 0; // make gcc happy } else if (x < 0) { ptr->p = &data[y * line]; ptr->shift = 7; ptr->x = x; } else { ptr->p = &data[y * line + (x >> 3)]; ptr->shift = 7 - (x & 7); ptr->x = x; } } inline int JBIG2Bitmap::nextPixel(JBIG2BitmapPtr *ptr) { int pix; if (!ptr->p) { pix = 0; } else if (ptr->x < 0) { ++ptr->x; pix = 0; } else { pix = (*ptr->p >> ptr->shift) & 1; if (++ptr->x == w) { ptr->p = nullptr; } else if (ptr->shift == 0) { ++ptr->p; ptr->shift = 7; } else { --ptr->shift; } } return pix; } void JBIG2Bitmap::duplicateRow(int yDest, int ySrc) { memcpy(data + yDest * line, data + ySrc * line, line); } void JBIG2Bitmap::combine(JBIG2Bitmap *bitmap, int x, int y, unsigned int combOp) { int x0, x1, y0, y1, xx, yy; unsigned char *srcPtr, *destPtr; unsigned int src0, src1, src, dest, s1, s2, m1, m2, m3; bool oneByte; // check for the pathological case where y = -2^31 if (y < -0x7fffffff) { return; } if (y < 0) { y0 = -y; } else { y0 = 0; } if (y + bitmap->h > h) { y1 = h - y; } else { y1 = bitmap->h; } if (y0 >= y1) { return; } if (x >= 0) { x0 = x & ~7; } else { x0 = 0; } if (unlikely(checkedAdd(x, bitmap->w, &x1))) { return; } if (x1 > w) { x1 = w; } if (x0 >= x1) { return; } s1 = x & 7; s2 = 8 - s1; m1 = 0xff >> (x1 & 7); m2 = 0xff << (((x1 & 7) == 0) ? 0 : 8 - (x1 & 7)); m3 = (0xff >> s1) & m2; oneByte = x0 == ((x1 - 1) & ~7); for (yy = y0; yy < y1; ++yy) { if (unlikely((y + yy >= h) || (y + yy < 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); srcPtr = bitmap->data + yy * bitmap->line; dest = *destPtr; src1 = *srcPtr; switch (combOp) { case 0: // or dest |= (src1 >> s1) & m2; break; case 1: // and dest &= ((0xff00 | src1) >> s1) | m1; break; case 2: // xor dest ^= (src1 >> s1) & m2; break; case 3: // xnor dest ^= ((src1 ^ 0xff) >> s1) & m2; break; case 4: // replace dest = (dest & ~m3) | ((src1 >> s1) & m3); break; } *destPtr = dest; } else { destPtr = data + (y + yy) * line; srcPtr = bitmap->data + yy * bitmap->line + (-x >> 3); dest = *destPtr; src1 = *srcPtr; switch (combOp) { case 0: // or dest |= src1 & m2; break; case 1: // and dest &= src1 | m1; break; case 2: // xor dest ^= src1 & m2; break; case 3: // xnor dest ^= (src1 ^ 0xff) & m2; break; case 4: // replace dest = (src1 & m2) | (dest & m1); break; } *destPtr = dest; } // multiple bytes per line -- need to mask left side of left-most // byte and right side of right-most byte } else { // left-most byte if (x >= 0) { destPtr = data + (y + yy) * line + (x >> 3); srcPtr = bitmap->data + yy * bitmap->line; src1 = *srcPtr++; dest = *destPtr; switch (combOp) { case 0: // or dest |= src1 >> s1; break; case 1: // and dest &= (0xff00 | src1) >> s1; break; case 2: // xor dest ^= src1 >> s1; break; case 3: // xnor dest ^= (src1 ^ 0xff) >> s1; break; case 4: // replace dest = (dest & (0xff << s2)) | (src1 >> s1); break; } *destPtr++ = dest; xx = x0 + 8; } else { destPtr = data + (y + yy) * line; srcPtr = bitmap->data + yy * bitmap->line + (-x >> 3); src1 = *srcPtr++; xx = x0; } // middle bytes for (; xx < x1 - 8; xx += 8) { dest = *destPtr; src0 = src1; src1 = *srcPtr++; src = (((src0 << 8) | src1) >> s1) & 0xff; switch (combOp) { case 0: // or dest |= src; break; case 1: // and dest &= src; break; case 2: // xor dest ^= src; break; case 3: // xnor dest ^= src ^ 0xff; break; case 4: // replace dest = src; break; } *destPtr++ = dest; } // right-most byte // note: this last byte (src1) may not actually be used, depending // on the values of s1, m1, and m2 - and in fact, it may be off // the edge of the source bitmap, which means we need to allocate // one extra guard byte at the end of each bitmap dest = *destPtr; src0 = src1; src1 = *srcPtr++; src = (((src0 << 8) | src1) >> s1) & 0xff; switch (combOp) { case 0: // or dest |= src & m2; break; case 1: // and dest &= src | m1; break; case 2: // xor dest ^= src & m2; break; case 3: // xnor dest ^= (src ^ 0xff) & m2; break; case 4: // replace dest = (src & m2) | (dest & m1); break; } *destPtr = dest; } } } //------------------------------------------------------------------------ // JBIG2SymbolDict //------------------------------------------------------------------------ class JBIG2SymbolDict : public JBIG2Segment { public: JBIG2SymbolDict(unsigned int segNumA, unsigned int sizeA); ~JBIG2SymbolDict() override; JBIG2SegmentType getType() override { return jbig2SegSymbolDict; } unsigned int getSize() { return size; } void setBitmap(unsigned int idx, JBIG2Bitmap *bitmap) { bitmaps[idx] = bitmap; } JBIG2Bitmap *getBitmap(unsigned int idx) { return bitmaps[idx]; } bool isOk() const { return ok; } void setGenericRegionStats(JArithmeticDecoderStats *stats) { genericRegionStats = stats; } void setRefinementRegionStats(JArithmeticDecoderStats *stats) { refinementRegionStats = stats; } JArithmeticDecoderStats *getGenericRegionStats() { return genericRegionStats; } JArithmeticDecoderStats *getRefinementRegionStats() { return refinementRegionStats; } private: bool ok; unsigned int size; JBIG2Bitmap **bitmaps; JArithmeticDecoderStats *genericRegionStats; JArithmeticDecoderStats *refinementRegionStats; }; JBIG2SymbolDict::JBIG2SymbolDict(unsigned int segNumA, unsigned int sizeA) : JBIG2Segment(segNumA) { ok = true; size = sizeA; if (size != 0) { bitmaps = (JBIG2Bitmap **)gmallocn_checkoverflow(size, sizeof(JBIG2Bitmap *)); if (!bitmaps) { ok = false; size = 0; } } else { bitmaps = nullptr; } for (unsigned int i = 0; i < size; ++i) { bitmaps[i] = nullptr; } genericRegionStats = nullptr; refinementRegionStats = nullptr; } JBIG2SymbolDict::~JBIG2SymbolDict() { unsigned int i; for (i = 0; i < size; ++i) { delete bitmaps[i]; } gfree(bitmaps); if (genericRegionStats) { delete genericRegionStats; } if (refinementRegionStats) { delete refinementRegionStats; } } //------------------------------------------------------------------------ // JBIG2PatternDict //------------------------------------------------------------------------ class JBIG2PatternDict : public JBIG2Segment { public: JBIG2PatternDict(unsigned int segNumA, unsigned int sizeA); ~JBIG2PatternDict() override; JBIG2SegmentType getType() override { return jbig2SegPatternDict; } unsigned int getSize() { return size; } void setBitmap(unsigned int idx, JBIG2Bitmap *bitmap) { if (likely(idx < size)) { bitmaps[idx] = bitmap; } } JBIG2Bitmap *getBitmap(unsigned int idx) { return (idx < size) ? bitmaps[idx] : nullptr; } private: unsigned int size; JBIG2Bitmap **bitmaps; }; JBIG2PatternDict::JBIG2PatternDict(unsigned int segNumA, unsigned int sizeA) : JBIG2Segment(segNumA) { bitmaps = (JBIG2Bitmap **)gmallocn_checkoverflow(sizeA, sizeof(JBIG2Bitmap *)); if (bitmaps) { size = sizeA; } else { size = 0; error(errSyntaxError, -1, "JBIG2PatternDict: can't allocate bitmaps"); } } JBIG2PatternDict::~JBIG2PatternDict() { unsigned int i; for (i = 0; i < size; ++i) { delete bitmaps[i]; } gfree(bitmaps); } //------------------------------------------------------------------------ // JBIG2CodeTable //------------------------------------------------------------------------ class JBIG2CodeTable : public JBIG2Segment { public: JBIG2CodeTable(unsigned int segNumA, JBIG2HuffmanTable *tableA); ~JBIG2CodeTable() override; JBIG2SegmentType getType() override { return jbig2SegCodeTable; } JBIG2HuffmanTable *getHuffTable() { return table; } private: JBIG2HuffmanTable *table; }; JBIG2CodeTable::JBIG2CodeTable(unsigned int segNumA, JBIG2HuffmanTable *tableA) : JBIG2Segment(segNumA) { table = tableA; } JBIG2CodeTable::~JBIG2CodeTable() { gfree(table); } //------------------------------------------------------------------------ // JBIG2Stream //------------------------------------------------------------------------ JBIG2Stream::JBIG2Stream(Stream *strA, Object &&globalsStreamA, Object *globalsStreamRefA) : FilterStream(strA) { pageBitmap = nullptr; arithDecoder = new JArithmeticDecoder(); genericRegionStats = new JArithmeticDecoderStats(1 << 1); refinementRegionStats = new JArithmeticDecoderStats(1 << 1); iadhStats = new JArithmeticDecoderStats(1 << 9); iadwStats = new JArithmeticDecoderStats(1 << 9); iaexStats = new JArithmeticDecoderStats(1 << 9); iaaiStats = new JArithmeticDecoderStats(1 << 9); iadtStats = new JArithmeticDecoderStats(1 << 9); iaitStats = new JArithmeticDecoderStats(1 << 9); iafsStats = new JArithmeticDecoderStats(1 << 9); iadsStats = new JArithmeticDecoderStats(1 << 9); iardxStats = new JArithmeticDecoderStats(1 << 9); iardyStats = new JArithmeticDecoderStats(1 << 9); iardwStats = new JArithmeticDecoderStats(1 << 9); iardhStats = new JArithmeticDecoderStats(1 << 9); iariStats = new JArithmeticDecoderStats(1 << 9); iaidStats = new JArithmeticDecoderStats(1 << 1); huffDecoder = new JBIG2HuffmanDecoder(); mmrDecoder = new JBIG2MMRDecoder(); if (globalsStreamA.isStream()) { globalsStream = std::move(globalsStreamA); if (globalsStreamRefA->isRef()) { globalsStreamRef = globalsStreamRefA->getRef(); } } curStr = nullptr; dataPtr = dataEnd = nullptr; } JBIG2Stream::~JBIG2Stream() { close(); delete arithDecoder; delete genericRegionStats; delete refinementRegionStats; delete iadhStats; delete iadwStats; delete iaexStats; delete iaaiStats; delete iadtStats; delete iaitStats; delete iafsStats; delete iadsStats; delete iardxStats; delete iardyStats; delete iardwStats; delete iardhStats; delete iariStats; delete iaidStats; delete huffDecoder; delete mmrDecoder; delete str; } void JBIG2Stream::reset() { segments.resize(0); globalSegments.resize(0); // read the globals stream if (globalsStream.isStream()) { curStr = globalsStream.getStream(); curStr->reset(); arithDecoder->setStream(curStr); huffDecoder->setStream(curStr); mmrDecoder->setStream(curStr); readSegments(); curStr->close(); // swap the newly read segments list into globalSegments std::swap(segments, globalSegments); } // read the main stream curStr = str; curStr->reset(); arithDecoder->setStream(curStr); huffDecoder->setStream(curStr); mmrDecoder->setStream(curStr); readSegments(); if (pageBitmap) { dataPtr = pageBitmap->getDataPtr(); dataEnd = dataPtr + pageBitmap->getDataSize(); } else { dataPtr = dataEnd = nullptr; } } void JBIG2Stream::close() { if (pageBitmap) { delete pageBitmap; pageBitmap = nullptr; } segments.resize(0); globalSegments.resize(0); dataPtr = dataEnd = nullptr; FilterStream::close(); } int JBIG2Stream::getChar() { if (dataPtr && dataPtr < dataEnd) { return (*dataPtr++ ^ 0xff) & 0xff; } return EOF; } int JBIG2Stream::lookChar() { if (dataPtr && dataPtr < dataEnd) { return (*dataPtr ^ 0xff) & 0xff; } return EOF; } Goffset JBIG2Stream::getPos() { if (pageBitmap == nullptr) { return 0; } return dataPtr - pageBitmap->getDataPtr(); } int JBIG2Stream::getChars(int nChars, unsigned char *buffer) { int n, i; if (nChars <= 0 || !dataPtr) { return 0; } if (dataEnd - dataPtr < nChars) { n = (int)(dataEnd - dataPtr); } else { n = nChars; } for (i = 0; i < n; ++i) { buffer[i] = *dataPtr++ ^ 0xff; } return n; } GooString *JBIG2Stream::getPSFilter(int psLevel, const char *indent) { return nullptr; } bool JBIG2Stream::isBinary(bool last) const { return str->isBinary(true); } void JBIG2Stream::readSegments() { unsigned int segNum, segFlags, segType, page, segLength; unsigned int refFlags, nRefSegs; unsigned int *refSegs; int c1, c2, c3; bool done = false; while (!done && readULong(&segNum)) { // segment header flags if (!readUByte(&segFlags)) { goto eofError1; } segType = segFlags & 0x3f; // referred-to segment count and retention flags if (!readUByte(&refFlags)) { goto eofError1; } nRefSegs = refFlags >> 5; if (nRefSegs == 7) { if ((c1 = curStr->getChar()) == EOF || (c2 = curStr->getChar()) == EOF || (c3 = curStr->getChar()) == EOF) { goto eofError1; } refFlags = (refFlags << 24) | (c1 << 16) | (c2 << 8) | c3; nRefSegs = refFlags & 0x1fffffff; const unsigned int nCharsToRead = (nRefSegs + 9) >> 3; for (unsigned int i = 0; i < nCharsToRead; ++i) { if ((c1 = curStr->getChar()) == EOF) { goto eofError1; } } } // referred-to segment numbers refSegs = (unsigned int *)gmallocn_checkoverflow(nRefSegs, sizeof(unsigned int)); if (nRefSegs > 0 && !refSegs) { return; } if (segNum <= 256) { for (unsigned int i = 0; i < nRefSegs; ++i) { if (!readUByte(&refSegs[i])) { goto eofError2; } } } else if (segNum <= 65536) { for (unsigned int i = 0; i < nRefSegs; ++i) { if (!readUWord(&refSegs[i])) { goto eofError2; } } } else { for (unsigned int i = 0; i < nRefSegs; ++i) { if (!readULong(&refSegs[i])) { goto eofError2; } } } // segment page association if (segFlags & 0x40) { if (!readULong(&page)) { goto eofError2; } } else { if (!readUByte(&page)) { goto eofError2; } } // segment data length if (!readULong(&segLength)) { goto eofError2; } // check for missing page information segment if (!pageBitmap && ((segType >= 4 && segType <= 7) || (segType >= 20 && segType <= 43))) { error(errSyntaxError, curStr->getPos(), "First JBIG2 segment associated with a page must be a page information segment"); goto syntaxError; } // read the segment data arithDecoder->resetByteCounter(); huffDecoder->resetByteCounter(); mmrDecoder->resetByteCounter(); byteCounter = 0; switch (segType) { case 0: if (!readSymbolDictSeg(segNum, segLength, refSegs, nRefSegs)) { error(errSyntaxError, curStr->getPos(), "readSymbolDictSeg reports syntax error!"); goto syntaxError; } break; case 4: readTextRegionSeg(segNum, false, false, segLength, refSegs, nRefSegs); break; case 6: readTextRegionSeg(segNum, true, false, segLength, refSegs, nRefSegs); break; case 7: readTextRegionSeg(segNum, true, true, segLength, refSegs, nRefSegs); break; case 16: readPatternDictSeg(segNum, segLength); break; case 20: readHalftoneRegionSeg(segNum, false, false, segLength, refSegs, nRefSegs); break; case 22: readHalftoneRegionSeg(segNum, true, false, segLength, refSegs, nRefSegs); break; case 23: readHalftoneRegionSeg(segNum, true, true, segLength, refSegs, nRefSegs); break; case 36: readGenericRegionSeg(segNum, false, false, segLength); break; case 38: readGenericRegionSeg(segNum, true, false, segLength); break; case 39: readGenericRegionSeg(segNum, true, true, segLength); break; case 40: readGenericRefinementRegionSeg(segNum, false, false, segLength, refSegs, nRefSegs); break; case 42: readGenericRefinementRegionSeg(segNum, true, false, segLength, refSegs, nRefSegs); break; case 43: readGenericRefinementRegionSeg(segNum, true, true, segLength, refSegs, nRefSegs); break; case 48: readPageInfoSeg(segLength); break; case 50: readEndOfStripeSeg(segLength); break; case 51: // end of file segment done = true; break; case 52: readProfilesSeg(segLength); break; case 53: readCodeTableSeg(segNum, segLength); break; case 62: readExtensionSeg(segLength); break; default: error(errSyntaxError, curStr->getPos(), "Unknown segment type in JBIG2 stream"); for (unsigned int i = 0; i < segLength; ++i) { if ((c1 = curStr->getChar()) == EOF) { goto eofError2; } } break; } // Make sure the segment handler read all of the bytes in the // segment data, unless this segment is marked as having an // unknown length (section 7.2.7 of the JBIG2 Final Committee Draft) if (!(segType == 38 && segLength == 0xffffffff)) { byteCounter += arithDecoder->getByteCounter(); byteCounter += huffDecoder->getByteCounter(); byteCounter += mmrDecoder->getByteCounter(); if (segLength > byteCounter) { const unsigned int segExtraBytes = segLength - byteCounter; // If we didn't read all of the bytes in the segment data, // indicate an error, and throw away the rest of the data. // v.3.1.01.13 of the LuraTech PDF Compressor Server will // sometimes generate an extraneous NULL byte at the end of // arithmetic-coded symbol dictionary segments when numNewSyms // == 0. Segments like this often occur for blank pages. error(errSyntaxError, curStr->getPos(), "{0:ud} extraneous byte{1:s} after segment", segExtraBytes, (segExtraBytes > 1) ? "s" : ""); byteCounter += curStr->discardChars(segExtraBytes); } else if (segLength < byteCounter) { // If we read more bytes than we should have, according to the // segment length field, note an error. error(errSyntaxError, curStr->getPos(), "Previous segment handler read too many bytes"); goto syntaxError; } } gfree(refSegs); } return; syntaxError: gfree(refSegs); return; eofError2: gfree(refSegs); eofError1: error(errSyntaxError, curStr->getPos(), "Unexpected EOF in JBIG2 stream"); } bool JBIG2Stream::readSymbolDictSeg(unsigned int segNum, unsigned int length, unsigned int *refSegs, unsigned int nRefSegs) { std::unique_ptr symbolDict; const JBIG2HuffmanTable *huffDHTable, *huffDWTable; const JBIG2HuffmanTable *huffBMSizeTable, *huffAggInstTable; JBIG2Segment *seg; std::vector codeTables; JBIG2SymbolDict *inputSymbolDict; unsigned int flags, sdTemplate, sdrTemplate, huff, refAgg; unsigned int huffDH, huffDW, huffBMSize, huffAggInst; unsigned int contextUsed, contextRetained; int sdATX[4], sdATY[4], sdrATX[2], sdrATY[2]; unsigned int numExSyms, numNewSyms, numInputSyms, symCodeLen; JBIG2Bitmap **bitmaps; JBIG2Bitmap *collBitmap, *refBitmap; unsigned int *symWidths; unsigned int symHeight, symWidth, totalWidth, x, symID; int dh = 0, dw, refAggNum, refDX = 0, refDY = 0, bmSize; bool ex; int run, cnt, c; unsigned int i, j, k; unsigned char *p; symWidths = nullptr; // symbol dictionary flags if (!readUWord(&flags)) { goto eofError; } sdTemplate = (flags >> 10) & 3; sdrTemplate = (flags >> 12) & 1; huff = flags & 1; refAgg = (flags >> 1) & 1; huffDH = (flags >> 2) & 3; huffDW = (flags >> 4) & 3; huffBMSize = (flags >> 6) & 1; huffAggInst = (flags >> 7) & 1; contextUsed = (flags >> 8) & 1; contextRetained = (flags >> 9) & 1; // symbol dictionary AT flags if (!huff) { if (sdTemplate == 0) { if (!readByte(&sdATX[0]) || !readByte(&sdATY[0]) || !readByte(&sdATX[1]) || !readByte(&sdATY[1]) || !readByte(&sdATX[2]) || !readByte(&sdATY[2]) || !readByte(&sdATX[3]) || !readByte(&sdATY[3])) { goto eofError; } } else { if (!readByte(&sdATX[0]) || !readByte(&sdATY[0])) { goto eofError; } } } // symbol dictionary refinement AT flags if (refAgg && !sdrTemplate) { if (!readByte(&sdrATX[0]) || !readByte(&sdrATY[0]) || !readByte(&sdrATX[1]) || !readByte(&sdrATY[1])) { goto eofError; } } // SDNUMEXSYMS and SDNUMNEWSYMS if (!readULong(&numExSyms) || !readULong(&numNewSyms)) { goto eofError; } // get referenced segments: input symbol dictionaries and code tables numInputSyms = 0; for (i = 0; i < nRefSegs; ++i) { // This is need by bug 12014, returning false makes it not crash // but we end up with a empty page while acroread is able to render // part of it if ((seg = findSegment(refSegs[i]))) { if (seg->getType() == jbig2SegSymbolDict) { j = ((JBIG2SymbolDict *)seg)->getSize(); if (numInputSyms > UINT_MAX - j) { error(errSyntaxError, curStr->getPos(), "Too many input symbols in JBIG2 symbol dictionary"); goto eofError; } numInputSyms += j; } else if (seg->getType() == jbig2SegCodeTable) { codeTables.push_back(seg); } } else { return false; } } if (numInputSyms > UINT_MAX - numNewSyms) { error(errSyntaxError, curStr->getPos(), "Too many input symbols in JBIG2 symbol dictionary"); goto eofError; } // compute symbol code length, per 6.5.8.2.3 // symCodeLen = ceil( log2( numInputSyms + numNewSyms ) ) i = numInputSyms + numNewSyms; if (i <= 1) { symCodeLen = huff ? 1 : 0; } else { --i; symCodeLen = 0; // i = floor((numSyms-1) / 2^symCodeLen) while (i > 0) { ++symCodeLen; i >>= 1; } } // get the input symbol bitmaps bitmaps = (JBIG2Bitmap **)gmallocn_checkoverflow(numInputSyms + numNewSyms, sizeof(JBIG2Bitmap *)); if (!bitmaps && (numInputSyms + numNewSyms > 0)) { error(errSyntaxError, curStr->getPos(), "Too many input symbols in JBIG2 symbol dictionary"); goto eofError; } for (i = 0; i < numInputSyms + numNewSyms; ++i) { bitmaps[i] = nullptr; } k = 0; inputSymbolDict = nullptr; for (i = 0; i < nRefSegs; ++i) { seg = findSegment(refSegs[i]); if (seg != nullptr && seg->getType() == jbig2SegSymbolDict) { inputSymbolDict = (JBIG2SymbolDict *)seg; for (j = 0; j < inputSymbolDict->getSize(); ++j) { bitmaps[k++] = inputSymbolDict->getBitmap(j); } } } // get the Huffman tables huffDHTable = huffDWTable = nullptr; // make gcc happy huffBMSizeTable = huffAggInstTable = nullptr; // make gcc happy i = 0; if (huff) { if (huffDH == 0) { huffDHTable = huffTableD; } else if (huffDH == 1) { huffDHTable = huffTableE; } else { if (i >= codeTables.size()) { goto codeTableError; } huffDHTable = ((JBIG2CodeTable *)codeTables[i++])->getHuffTable(); } if (huffDW == 0) { huffDWTable = huffTableB; } else if (huffDW == 1) { huffDWTable = huffTableC; } else { if (i >= codeTables.size()) { goto codeTableError; } huffDWTable = ((JBIG2CodeTable *)codeTables[i++])->getHuffTable(); } if (huffBMSize == 0) { huffBMSizeTable = huffTableA; } else { if (i >= codeTables.size()) { goto codeTableError; } huffBMSizeTable = ((JBIG2CodeTable *)codeTables[i++])->getHuffTable(); } if (huffAggInst == 0) { huffAggInstTable = huffTableA; } else { if (i >= codeTables.size()) { goto codeTableError; } huffAggInstTable = ((JBIG2CodeTable *)codeTables[i++])->getHuffTable(); } } // set up the Huffman decoder if (huff) { huffDecoder->reset(); // set up the arithmetic decoder } else { if (contextUsed && inputSymbolDict) { resetGenericStats(sdTemplate, inputSymbolDict->getGenericRegionStats()); } else { resetGenericStats(sdTemplate, nullptr); } if (!resetIntStats(symCodeLen)) { goto syntaxError; } arithDecoder->start(); } // set up the arithmetic decoder for refinement/aggregation if (refAgg) { if (contextUsed && inputSymbolDict) { resetRefinementStats(sdrTemplate, inputSymbolDict->getRefinementRegionStats()); } else { resetRefinementStats(sdrTemplate, nullptr); } } // allocate symbol widths storage if (huff && !refAgg) { symWidths = (unsigned int *)gmallocn_checkoverflow(numNewSyms, sizeof(unsigned int)); if (numNewSyms > 0 && !symWidths) { goto syntaxError; } } symHeight = 0; i = 0; while (i < numNewSyms) { // read the height class delta height if (huff) { huffDecoder->decodeInt(&dh, huffDHTable); } else { arithDecoder->decodeInt(&dh, iadhStats); } if (dh < 0 && (unsigned int)-dh >= symHeight) { error(errSyntaxError, curStr->getPos(), "Bad delta-height value in JBIG2 symbol dictionary"); goto syntaxError; } symHeight += dh; if (unlikely(symHeight > 0x40000000)) { error(errSyntaxError, curStr->getPos(), "Bad height value in JBIG2 symbol dictionary"); goto syntaxError; } symWidth = 0; totalWidth = 0; j = i; // read the symbols in this height class while (true) { // read the delta width if (huff) { if (!huffDecoder->decodeInt(&dw, huffDWTable)) { break; } } else { if (!arithDecoder->decodeInt(&dw, iadwStats)) { break; } } if (dw < 0 && (unsigned int)-dw >= symWidth) { error(errSyntaxError, curStr->getPos(), "Bad delta-height value in JBIG2 symbol dictionary"); goto syntaxError; } symWidth += dw; if (i >= numNewSyms) { error(errSyntaxError, curStr->getPos(), "Too many symbols in JBIG2 symbol dictionary"); goto syntaxError; } // using a collective bitmap, so don't read a bitmap here if (huff && !refAgg) { symWidths[i] = symWidth; totalWidth += symWidth; // refinement/aggregate coding } else if (refAgg) { if (huff) { if (!huffDecoder->decodeInt(&refAggNum, huffAggInstTable)) { break; } } else { if (!arithDecoder->decodeInt(&refAggNum, iaaiStats)) { break; } } //~ This special case was added about a year before the final draft //~ of the JBIG2 spec was released. I have encountered some old //~ JBIG2 images that predate it. //~ if (0) { if (refAggNum == 1) { if (huff) { symID = huffDecoder->readBits(symCodeLen); huffDecoder->decodeInt(&refDX, huffTableO); huffDecoder->decodeInt(&refDY, huffTableO); huffDecoder->decodeInt(&bmSize, huffTableA); huffDecoder->reset(); arithDecoder->start(); } else { if (iaidStats == nullptr) { goto syntaxError; } symID = arithDecoder->decodeIAID(symCodeLen, iaidStats); arithDecoder->decodeInt(&refDX, iardxStats); arithDecoder->decodeInt(&refDY, iardyStats); } if (symID >= numInputSyms + i) { error(errSyntaxError, curStr->getPos(), "Invalid symbol ID in JBIG2 symbol dictionary"); goto syntaxError; } refBitmap = bitmaps[symID]; if (unlikely(refBitmap == nullptr)) { error(errSyntaxError, curStr->getPos(), "Invalid ref bitmap for symbol ID {0:ud} in JBIG2 symbol dictionary", symID); goto syntaxError; } bitmaps[numInputSyms + i] = readGenericRefinementRegion(symWidth, symHeight, sdrTemplate, false, refBitmap, refDX, refDY, sdrATX, sdrATY).release(); //~ do we need to use the bmSize value here (in Huffman mode)? } else { bitmaps[numInputSyms + i] = readTextRegion(huff, true, symWidth, symHeight, refAggNum, 0, numInputSyms + i, nullptr, symCodeLen, bitmaps, 0, 0, 0, 1, 0, huffTableF, huffTableH, huffTableK, huffTableO, huffTableO, huffTableO, huffTableO, huffTableA, sdrTemplate, sdrATX, sdrATY) .release(); if (unlikely(!bitmaps[numInputSyms + i])) { error(errSyntaxError, curStr->getPos(), "NULL bitmap in readTextRegion"); goto syntaxError; } } // non-ref/agg coding } else { bitmaps[numInputSyms + i] = readGenericBitmap(false, symWidth, symHeight, sdTemplate, false, false, nullptr, sdATX, sdATY, 0).release(); if (unlikely(!bitmaps[numInputSyms + i])) { error(errSyntaxError, curStr->getPos(), "NULL bitmap in readGenericBitmap"); goto syntaxError; } } ++i; } // read the collective bitmap if (huff && !refAgg) { huffDecoder->decodeInt(&bmSize, huffBMSizeTable); huffDecoder->reset(); if (bmSize == 0) { collBitmap = new JBIG2Bitmap(0, totalWidth, symHeight); bmSize = symHeight * ((totalWidth + 7) >> 3); p = collBitmap->getDataPtr(); if (unlikely(p == nullptr)) { delete collBitmap; goto syntaxError; } for (k = 0; k < (unsigned int)bmSize; ++k) { if ((c = curStr->getChar()) == EOF) { memset(p, 0, bmSize - k); break; } *p++ = (unsigned char)c; } byteCounter += k; } else { collBitmap = readGenericBitmap(true, totalWidth, symHeight, 0, false, false, nullptr, nullptr, nullptr, bmSize).release(); } if (likely(collBitmap != nullptr)) { x = 0; for (; j < i; ++j) { bitmaps[numInputSyms + j] = collBitmap->getSlice(x, 0, symWidths[j], symHeight); x += symWidths[j]; } delete collBitmap; } else { error(errSyntaxError, curStr->getPos(), "collBitmap was null"); goto syntaxError; } } } // create the symbol dict object symbolDict = std::make_unique(segNum, numExSyms); if (!symbolDict->isOk()) { goto syntaxError; } // exported symbol list i = j = 0; ex = false; run = 0; // initialize it once in case the first decodeInt fails // we do not want to use uninitialized memory while (i < numInputSyms + numNewSyms) { if (huff) { huffDecoder->decodeInt(&run, huffTableA); } else { arithDecoder->decodeInt(&run, iaexStats); } if (i + run > numInputSyms + numNewSyms || (ex && j + run > numExSyms)) { error(errSyntaxError, curStr->getPos(), "Too many exported symbols in JBIG2 symbol dictionary"); for (; j < numExSyms; ++j) { symbolDict->setBitmap(j, nullptr); } goto syntaxError; } if (ex) { for (cnt = 0; cnt < run; ++cnt) { symbolDict->setBitmap(j++, new JBIG2Bitmap(bitmaps[i++])); } } else { i += run; } ex = !ex; } if (j != numExSyms) { error(errSyntaxError, curStr->getPos(), "Too few symbols in JBIG2 symbol dictionary"); for (; j < numExSyms; ++j) { symbolDict->setBitmap(j, nullptr); } goto syntaxError; } for (i = 0; i < numNewSyms; ++i) { delete bitmaps[numInputSyms + i]; } gfree(bitmaps); if (symWidths) { gfree(symWidths); } // save the arithmetic decoder stats if (!huff && contextRetained) { symbolDict->setGenericRegionStats(genericRegionStats->copy()); if (refAgg) { symbolDict->setRefinementRegionStats(refinementRegionStats->copy()); } } // store the new symbol dict segments.push_back(std::move(symbolDict)); return true; codeTableError: error(errSyntaxError, curStr->getPos(), "Missing code table in JBIG2 symbol dictionary"); syntaxError: for (i = 0; i < numNewSyms; ++i) { if (bitmaps[numInputSyms + i]) { delete bitmaps[numInputSyms + i]; } } gfree(bitmaps); if (symWidths) { gfree(symWidths); } return false; eofError: error(errSyntaxError, curStr->getPos(), "Unexpected EOF in JBIG2 stream"); return false; } void JBIG2Stream::readTextRegionSeg(unsigned int segNum, bool imm, bool lossless, unsigned int length, unsigned int *refSegs, unsigned int nRefSegs) { std::unique_ptr bitmap; JBIG2HuffmanTable runLengthTab[36]; JBIG2HuffmanTable *symCodeTab = nullptr; const JBIG2HuffmanTable *huffFSTable, *huffDSTable, *huffDTTable; const JBIG2HuffmanTable *huffRDWTable, *huffRDHTable; const JBIG2HuffmanTable *huffRDXTable, *huffRDYTable, *huffRSizeTable; JBIG2Segment *seg; std::vector codeTables; JBIG2SymbolDict *symbolDict; JBIG2Bitmap **syms; unsigned int w, h, x, y, segInfoFlags, extCombOp; unsigned int flags, huff, refine, logStrips, refCorner, transposed; unsigned int combOp, defPixel, templ; int sOffset; unsigned int huffFlags, huffFS, huffDS, huffDT; unsigned int huffRDW, huffRDH, huffRDX, huffRDY, huffRSize; unsigned int numInstances, numSyms, symCodeLen; int atx[2], aty[2]; unsigned int i, k, kk; int j = 0; // region segment info field if (!readULong(&w) || !readULong(&h) || !readULong(&x) || !readULong(&y) || !readUByte(&segInfoFlags)) { goto eofError; } extCombOp = segInfoFlags & 7; // rest of the text region header if (!readUWord(&flags)) { goto eofError; } huff = flags & 1; refine = (flags >> 1) & 1; logStrips = (flags >> 2) & 3; refCorner = (flags >> 4) & 3; transposed = (flags >> 6) & 1; combOp = (flags >> 7) & 3; defPixel = (flags >> 9) & 1; sOffset = (flags >> 10) & 0x1f; if (sOffset & 0x10) { sOffset |= -1 - 0x0f; } templ = (flags >> 15) & 1; huffFS = huffDS = huffDT = 0; // make gcc happy huffRDW = huffRDH = huffRDX = huffRDY = huffRSize = 0; // make gcc happy if (huff) { if (!readUWord(&huffFlags)) { goto eofError; } huffFS = huffFlags & 3; huffDS = (huffFlags >> 2) & 3; huffDT = (huffFlags >> 4) & 3; huffRDW = (huffFlags >> 6) & 3; huffRDH = (huffFlags >> 8) & 3; huffRDX = (huffFlags >> 10) & 3; huffRDY = (huffFlags >> 12) & 3; huffRSize = (huffFlags >> 14) & 1; } if (refine && templ == 0) { if (!readByte(&atx[0]) || !readByte(&aty[0]) || !readByte(&atx[1]) || !readByte(&aty[1])) { goto eofError; } } if (!readULong(&numInstances)) { goto eofError; } // get symbol dictionaries and tables numSyms = 0; for (i = 0; i < nRefSegs; ++i) { if ((seg = findSegment(refSegs[i]))) { if (seg->getType() == jbig2SegSymbolDict) { const unsigned int segSize = ((JBIG2SymbolDict *)seg)->getSize(); if (unlikely(checkedAdd(numSyms, segSize, &numSyms))) { error(errSyntaxError, getPos(), "Too many symbols in JBIG2 text region"); return; } } else if (seg->getType() == jbig2SegCodeTable) { codeTables.push_back(seg); } } else { error(errSyntaxError, curStr->getPos(), "Invalid segment reference in JBIG2 text region"); return; } } i = numSyms; if (i <= 1) { symCodeLen = huff ? 1 : 0; } else { --i; symCodeLen = 0; // i = floor((numSyms-1) / 2^symCodeLen) while (i > 0) { ++symCodeLen; i >>= 1; } } // get the symbol bitmaps syms = (JBIG2Bitmap **)gmallocn_checkoverflow(numSyms, sizeof(JBIG2Bitmap *)); if (numSyms > 0 && !syms) { return; } kk = 0; for (i = 0; i < nRefSegs; ++i) { if ((seg = findSegment(refSegs[i]))) { if (seg->getType() == jbig2SegSymbolDict) { symbolDict = (JBIG2SymbolDict *)seg; for (k = 0; k < symbolDict->getSize(); ++k) { syms[kk++] = symbolDict->getBitmap(k); } } } } // get the Huffman tables huffFSTable = huffDSTable = huffDTTable = nullptr; // make gcc happy huffRDWTable = huffRDHTable = nullptr; // make gcc happy huffRDXTable = huffRDYTable = huffRSizeTable = nullptr; // make gcc happy i = 0; if (huff) { if (huffFS == 0) { huffFSTable = huffTableF; } else if (huffFS == 1) { huffFSTable = huffTableG; } else { if (i >= codeTables.size()) { goto codeTableError; } huffFSTable = ((JBIG2CodeTable *)codeTables[i++])->getHuffTable(); } if (huffDS == 0) { huffDSTable = huffTableH; } else if (huffDS == 1) { huffDSTable = huffTableI; } else if (huffDS == 2) { huffDSTable = huffTableJ; } else { if (i >= codeTables.size()) { goto codeTableError; } huffDSTable = ((JBIG2CodeTable *)codeTables[i++])->getHuffTable(); } if (huffDT == 0) { huffDTTable = huffTableK; } else if (huffDT == 1) { huffDTTable = huffTableL; } else if (huffDT == 2) { huffDTTable = huffTableM; } else { if (i >= codeTables.size()) { goto codeTableError; } huffDTTable = ((JBIG2CodeTable *)codeTables[i++])->getHuffTable(); } if (huffRDW == 0) { huffRDWTable = huffTableN; } else if (huffRDW == 1) { huffRDWTable = huffTableO; } else { if (i >= codeTables.size()) { goto codeTableError; } huffRDWTable = ((JBIG2CodeTable *)codeTables[i++])->getHuffTable(); } if (huffRDH == 0) { huffRDHTable = huffTableN; } else if (huffRDH == 1) { huffRDHTable = huffTableO; } else { if (i >= codeTables.size()) { goto codeTableError; } huffRDHTable = ((JBIG2CodeTable *)codeTables[i++])->getHuffTable(); } if (huffRDX == 0) { huffRDXTable = huffTableN; } else if (huffRDX == 1) { huffRDXTable = huffTableO; } else { if (i >= codeTables.size()) { goto codeTableError; } huffRDXTable = ((JBIG2CodeTable *)codeTables[i++])->getHuffTable(); } if (huffRDY == 0) { huffRDYTable = huffTableN; } else if (huffRDY == 1) { huffRDYTable = huffTableO; } else { if (i >= codeTables.size()) { goto codeTableError; } huffRDYTable = ((JBIG2CodeTable *)codeTables[i++])->getHuffTable(); } if (huffRSize == 0) { huffRSizeTable = huffTableA; } else { if (i >= codeTables.size()) { goto codeTableError; } huffRSizeTable = ((JBIG2CodeTable *)codeTables[i++])->getHuffTable(); } } // symbol ID Huffman decoding table if (huff) { huffDecoder->reset(); for (i = 0; i < 32; ++i) { runLengthTab[i].val = i; runLengthTab[i].prefixLen = huffDecoder->readBits(4); runLengthTab[i].rangeLen = 0; } runLengthTab[32].val = 0x103; runLengthTab[32].prefixLen = huffDecoder->readBits(4); runLengthTab[32].rangeLen = 2; runLengthTab[33].val = 0x203; runLengthTab[33].prefixLen = huffDecoder->readBits(4); runLengthTab[33].rangeLen = 3; runLengthTab[34].val = 0x20b; runLengthTab[34].prefixLen = huffDecoder->readBits(4); runLengthTab[34].rangeLen = 7; runLengthTab[35].prefixLen = 0; runLengthTab[35].rangeLen = jbig2HuffmanEOT; if (!JBIG2HuffmanDecoder::buildTable(runLengthTab, 35)) { huff = false; } } if (huff) { symCodeTab = (JBIG2HuffmanTable *)gmallocn_checkoverflow(numSyms + 1, sizeof(JBIG2HuffmanTable)); if (!symCodeTab) { gfree(syms); return; } for (i = 0; i < numSyms; ++i) { symCodeTab[i].val = i; symCodeTab[i].rangeLen = 0; } i = 0; while (i < numSyms) { huffDecoder->decodeInt(&j, runLengthTab); if (j > 0x200) { for (j -= 0x200; j && i < numSyms; --j) { symCodeTab[i++].prefixLen = 0; } } else if (j > 0x100) { if (unlikely(i == 0)) { symCodeTab[i].prefixLen = 0; ++i; } for (j -= 0x100; j && i < numSyms; --j) { symCodeTab[i].prefixLen = symCodeTab[i - 1].prefixLen; ++i; } } else { symCodeTab[i++].prefixLen = j; } } symCodeTab[numSyms].prefixLen = 0; symCodeTab[numSyms].rangeLen = jbig2HuffmanEOT; if (!JBIG2HuffmanDecoder::buildTable(symCodeTab, numSyms)) { huff = false; gfree(symCodeTab); symCodeTab = nullptr; } huffDecoder->reset(); // set up the arithmetic decoder } if (!huff) { if (!resetIntStats(symCodeLen)) { gfree(syms); return; } arithDecoder->start(); } if (refine) { resetRefinementStats(templ, nullptr); } 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); if (bitmap) { // combine the region bitmap into the page bitmap if (imm) { if (pageH == 0xffffffff && y + h > curPageH) { pageBitmap->expand(y + h, pageDefPixel); } if (pageBitmap->isOk()) { pageBitmap->combine(bitmap.get(), x, y, extCombOp); } // store the region bitmap } else { bitmap->setSegNum(segNum); segments.push_back(std::move(bitmap)); } } // clean up the Huffman decoder if (huff) { gfree(symCodeTab); } return; codeTableError: error(errSyntaxError, curStr->getPos(), "Missing code table in JBIG2 text region"); gfree(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, unsigned int defPixel, unsigned int combOp, unsigned int transposed, unsigned int refCorner, int sOffset, const JBIG2HuffmanTable *huffFSTable, const JBIG2HuffmanTable *huffDSTable, const JBIG2HuffmanTable *huffDTTable, const JBIG2HuffmanTable *huffRDWTable, const JBIG2HuffmanTable *huffRDHTable, const JBIG2HuffmanTable *huffRDXTable, const JBIG2HuffmanTable *huffRDYTable, const JBIG2HuffmanTable *huffRSizeTable, unsigned int templ, int *atx, int *aty) { JBIG2Bitmap *symbolBitmap; unsigned int strips; int t = 0, dt = 0, tt, s, ds = 0, sFirst, j = 0; int rdw, rdh, rdx, rdy, ri = 0, refDX, refDY, bmSize; unsigned int symID, inst, bw, bh; strips = 1 << logStrips; // allocate the bitmap std::unique_ptr bitmap = std::make_unique(0, w, h); if (!bitmap->isOk()) { return nullptr; } if (defPixel) { bitmap->clearToOne(); } else { bitmap->clearToZero(); } // decode initial T value if (huff) { huffDecoder->decodeInt(&t, huffDTTable); } else { arithDecoder->decodeInt(&t, iadtStats); } if (checkedMultiply(t, -(int)strips, &t)) { return {}; } inst = 0; sFirst = 0; while (inst < numInstances) { // decode delta-T if (huff) { huffDecoder->decodeInt(&dt, huffDTTable); } else { arithDecoder->decodeInt(&dt, iadtStats); } t += dt * strips; // first S value if (huff) { huffDecoder->decodeInt(&ds, huffFSTable); } else { arithDecoder->decodeInt(&ds, iafsStats); } if (unlikely(checkedAdd(sFirst, ds, &sFirst))) { return nullptr; } s = sFirst; // read the instances // (this loop test is here to avoid an infinite loop with damaged // JBIG2 streams where the normal loop exit doesn't get triggered) while (inst < numInstances) { // T value if (strips == 1) { dt = 0; } else if (huff) { dt = huffDecoder->readBits(logStrips); } else { arithDecoder->decodeInt(&dt, iaitStats); } if (unlikely(checkedAdd(t, dt, &tt))) { return nullptr; } // symbol ID if (huff) { if (symCodeTab) { huffDecoder->decodeInt(&j, symCodeTab); symID = (unsigned int)j; } else { symID = huffDecoder->readBits(symCodeLen); } } else { if (iaidStats == nullptr) { return nullptr; } symID = arithDecoder->decodeIAID(symCodeLen, iaidStats); } if (symID >= (unsigned int)numSyms) { error(errSyntaxError, curStr->getPos(), "Invalid symbol number in JBIG2 text region"); if (unlikely(numInstances - inst > 0x800)) { // don't loop too often with damaged JBIg2 streams return nullptr; } } else { // get the symbol bitmap symbolBitmap = nullptr; if (refine) { if (huff) { ri = (int)huffDecoder->readBit(); } else { arithDecoder->decodeInt(&ri, iariStats); } } else { ri = 0; } if (ri) { bool decodeSuccess; if (huff) { decodeSuccess = huffDecoder->decodeInt(&rdw, huffRDWTable); decodeSuccess = decodeSuccess && huffDecoder->decodeInt(&rdh, huffRDHTable); decodeSuccess = decodeSuccess && huffDecoder->decodeInt(&rdx, huffRDXTable); decodeSuccess = decodeSuccess && huffDecoder->decodeInt(&rdy, huffRDYTable); decodeSuccess = decodeSuccess && huffDecoder->decodeInt(&bmSize, huffRSizeTable); huffDecoder->reset(); arithDecoder->start(); } else { decodeSuccess = arithDecoder->decodeInt(&rdw, iardwStats); decodeSuccess = decodeSuccess && arithDecoder->decodeInt(&rdh, iardhStats); decodeSuccess = decodeSuccess && arithDecoder->decodeInt(&rdx, iardxStats); decodeSuccess = decodeSuccess && arithDecoder->decodeInt(&rdy, iardyStats); } if (decodeSuccess && syms[symID]) { refDX = ((rdw >= 0) ? rdw : rdw - 1) / 2 + rdx; if (checkedAdd(((rdh >= 0) ? rdh : rdh - 1) / 2, rdy, &refDY)) { return nullptr; } symbolBitmap = readGenericRefinementRegion(rdw + syms[symID]->getWidth(), rdh + syms[symID]->getHeight(), templ, false, syms[symID], refDX, refDY, atx, aty).release(); } //~ do we need to use the bmSize value here (in Huffman mode)? } else { symbolBitmap = syms[symID]; } if (symbolBitmap) { // combine the symbol bitmap into the region bitmap //~ something is wrong here - refCorner shouldn't degenerate into //~ two cases bw = symbolBitmap->getWidth() - 1; if (unlikely(symbolBitmap->getHeight() == 0)) { error(errSyntaxError, curStr->getPos(), "Invalid symbol bitmap height"); if (ri) { delete symbolBitmap; } return nullptr; } bh = symbolBitmap->getHeight() - 1; if (transposed) { if (unlikely(s > 2 * bitmap->getHeight())) { error(errSyntaxError, curStr->getPos(), "Invalid JBIG2 combine"); if (ri) { delete symbolBitmap; } return nullptr; } switch (refCorner) { case 0: // bottom left bitmap->combine(symbolBitmap, tt, s, combOp); break; case 1: // top left bitmap->combine(symbolBitmap, tt, s, combOp); break; case 2: // bottom right bitmap->combine(symbolBitmap, tt - bw, s, combOp); break; case 3: // top right bitmap->combine(symbolBitmap, tt - bw, s, combOp); break; } s += bh; } else { switch (refCorner) { case 0: // bottom left if (unlikely(tt - (int)bh > 2 * bitmap->getHeight())) { error(errSyntaxError, curStr->getPos(), "Invalid JBIG2 combine"); if (ri) { delete symbolBitmap; } return nullptr; } bitmap->combine(symbolBitmap, s, tt - bh, combOp); break; case 1: // top left if (unlikely(tt > 2 * bitmap->getHeight())) { error(errSyntaxError, curStr->getPos(), "Invalid JBIG2 combine"); if (ri) { delete symbolBitmap; } return nullptr; } bitmap->combine(symbolBitmap, s, tt, combOp); break; case 2: // bottom right if (unlikely(tt - (int)bh > 2 * bitmap->getHeight())) { error(errSyntaxError, curStr->getPos(), "Invalid JBIG2 combine"); if (ri) { delete symbolBitmap; } return nullptr; } bitmap->combine(symbolBitmap, s, tt - bh, combOp); break; case 3: // top right if (unlikely(tt > 2 * bitmap->getHeight())) { error(errSyntaxError, curStr->getPos(), "Invalid JBIG2 combine"); if (ri) { delete symbolBitmap; } return nullptr; } bitmap->combine(symbolBitmap, s, tt, combOp); break; } s += bw; } if (ri) { delete symbolBitmap; } } else { // NULL symbolBitmap only happens on error return nullptr; } } // next instance ++inst; // next S value if (huff) { if (!huffDecoder->decodeInt(&ds, huffDSTable)) { break; } } else { if (!arithDecoder->decodeInt(&ds, iadsStats)) { break; } } if (checkedAdd(s, sOffset + ds, &s)) { return nullptr; } } } return bitmap; } void JBIG2Stream::readPatternDictSeg(unsigned int segNum, unsigned int length) { std::unique_ptr patternDict; std::unique_ptr bitmap; unsigned int flags, patternW, patternH, grayMax, templ, mmr; int atx[4], aty[4]; unsigned int i, x; // halftone dictionary flags, pattern width and height, max gray value if (!readUByte(&flags) || !readUByte(&patternW) || !readUByte(&patternH) || !readULong(&grayMax)) { goto eofError; } templ = (flags >> 1) & 3; mmr = flags & 1; // set up the arithmetic decoder if (!mmr) { resetGenericStats(templ, nullptr); arithDecoder->start(); } // read the bitmap atx[0] = -(int)patternW; aty[0] = 0; atx[1] = -3; aty[1] = -1; atx[2] = 2; aty[2] = -2; atx[3] = -2; aty[3] = -2; unsigned int grayMaxPlusOne; if (unlikely(checkedAdd(grayMax, 1u, &grayMaxPlusOne))) { return; } unsigned int bitmapW; if (unlikely(checkedMultiply(grayMaxPlusOne, patternW, &bitmapW))) { return; } if (bitmapW >= INT_MAX) { return; } bitmap = readGenericBitmap(mmr, static_cast(bitmapW), patternH, templ, false, false, nullptr, atx, aty, length - 7); if (!bitmap) { return; } // create the pattern dict object patternDict = std::make_unique(segNum, grayMax + 1); // split up the bitmap x = 0; for (i = 0; i <= grayMax && i < patternDict->getSize(); ++i) { patternDict->setBitmap(i, bitmap->getSlice(x, 0, patternW, patternH)); x += patternW; } // store the new pattern dict segments.push_back(std::move(patternDict)); return; eofError: error(errSyntaxError, curStr->getPos(), "Unexpected EOF in JBIG2 stream"); } void JBIG2Stream::readHalftoneRegionSeg(unsigned int segNum, bool imm, bool lossless, unsigned int length, unsigned int *refSegs, unsigned int nRefSegs) { std::unique_ptr bitmap; JBIG2Segment *seg; JBIG2PatternDict *patternDict; std::unique_ptr skipBitmap; unsigned int *grayImg; JBIG2Bitmap *patternBitmap; unsigned int w, h, x, y, segInfoFlags, extCombOp; unsigned int flags, mmr, templ, enableSkip, combOp; unsigned int gridW, gridH, stepX, stepY, patW, patH; int atx[4], aty[4]; int gridX, gridY, xx, yy, bit, j; unsigned int bpp, m, n, i; // region segment info field if (!readULong(&w) || !readULong(&h) || !readULong(&x) || !readULong(&y) || !readUByte(&segInfoFlags)) { goto eofError; } extCombOp = segInfoFlags & 7; // rest of the halftone region header if (!readUByte(&flags)) { goto eofError; } mmr = flags & 1; templ = (flags >> 1) & 3; enableSkip = (flags >> 3) & 1; combOp = (flags >> 4) & 7; if (!readULong(&gridW) || !readULong(&gridH) || !readLong(&gridX) || !readLong(&gridY) || !readUWord(&stepX) || !readUWord(&stepY)) { goto eofError; } if (w == 0 || h == 0 || w >= INT_MAX / h) { error(errSyntaxError, curStr->getPos(), "Bad bitmap size in JBIG2 halftone segment"); return; } if (gridH == 0 || gridW >= INT_MAX / gridH) { error(errSyntaxError, curStr->getPos(), "Bad grid size in JBIG2 halftone segment"); return; } // get pattern dictionary if (nRefSegs != 1) { error(errSyntaxError, curStr->getPos(), "Bad symbol dictionary reference in JBIG2 halftone segment"); return; } seg = findSegment(refSegs[0]); if (seg == nullptr || seg->getType() != jbig2SegPatternDict) { error(errSyntaxError, curStr->getPos(), "Bad symbol dictionary reference in JBIG2 halftone segment"); return; } patternDict = (JBIG2PatternDict *)seg; i = patternDict->getSize(); if (i <= 1) { bpp = 0; } else { --i; bpp = 0; // i = floor((size-1) / 2^bpp) while (i > 0) { ++bpp; i >>= 1; } } patternBitmap = patternDict->getBitmap(0); if (unlikely(patternBitmap == nullptr)) { error(errSyntaxError, curStr->getPos(), "Bad pattern bitmap"); return; } patW = patternBitmap->getWidth(); patH = patternBitmap->getHeight(); // set up the arithmetic decoder if (!mmr) { resetGenericStats(templ, nullptr); arithDecoder->start(); } // allocate the bitmap bitmap = std::make_unique(segNum, w, h); if (flags & 0x80) { // HDEFPIXEL bitmap->clearToOne(); } else { bitmap->clearToZero(); } // compute the skip bitmap if (enableSkip) { skipBitmap = std::make_unique(0, gridW, gridH); skipBitmap->clearToZero(); for (m = 0; m < gridH; ++m) { for (n = 0; n < gridW; ++n) { xx = gridX + m * stepY + n * stepX; yy = gridY + m * stepX - n * stepY; if (((xx + (int)patW) >> 8) <= 0 || (xx >> 8) >= (int)w || ((yy + (int)patH) >> 8) <= 0 || (yy >> 8) >= (int)h) { skipBitmap->setPixel(n, m); } } } } // read the gray-scale image grayImg = (unsigned int *)gmallocn_checkoverflow(gridW * gridH, sizeof(unsigned int)); if (!grayImg) { return; } memset(grayImg, 0, gridW * gridH * sizeof(unsigned int)); atx[0] = templ <= 1 ? 3 : 2; aty[0] = -1; atx[1] = -3; aty[1] = -1; atx[2] = 2; aty[2] = -2; atx[3] = -2; aty[3] = -2; for (j = bpp - 1; j >= 0; --j) { std::unique_ptr grayBitmap = readGenericBitmap(mmr, gridW, gridH, templ, false, enableSkip, skipBitmap.get(), atx, aty, -1); i = 0; for (m = 0; m < gridH; ++m) { for (n = 0; n < gridW; ++n) { bit = grayBitmap->getPixel(n, m) ^ (grayImg[i] & 1); grayImg[i] = (grayImg[i] << 1) | bit; ++i; } } } // decode the image i = 0; for (m = 0; m < gridH; ++m) { xx = gridX + m * stepY; yy = gridY + m * stepX; for (n = 0; n < gridW; ++n) { if (!(enableSkip && skipBitmap->getPixel(n, m))) { patternBitmap = patternDict->getBitmap(grayImg[i]); if (unlikely(patternBitmap == nullptr)) { gfree(grayImg); error(errSyntaxError, curStr->getPos(), "Bad pattern bitmap"); return; } bitmap->combine(patternBitmap, xx >> 8, yy >> 8, combOp); } xx += stepX; yy -= stepY; ++i; } } gfree(grayImg); // combine the region bitmap into the page bitmap if (imm) { if (pageH == 0xffffffff && y + h > curPageH) { pageBitmap->expand(y + h, pageDefPixel); } pageBitmap->combine(bitmap.get(), x, y, extCombOp); // store the region bitmap } else { segments.push_back(std::move(bitmap)); } return; eofError: error(errSyntaxError, curStr->getPos(), "Unexpected EOF in JBIG2 stream"); } void JBIG2Stream::readGenericRegionSeg(unsigned int segNum, bool imm, bool lossless, unsigned int length) { std::unique_ptr bitmap; unsigned int w, h, x, y, segInfoFlags, extCombOp, rowCount; unsigned int flags, mmr, templ, tpgdOn; int atx[4], aty[4]; // region segment info field if (!readULong(&w) || !readULong(&h) || !readULong(&x) || !readULong(&y) || !readUByte(&segInfoFlags)) { goto eofError; } extCombOp = segInfoFlags & 7; // rest of the generic region segment header if (!readUByte(&flags)) { goto eofError; } mmr = flags & 1; templ = (flags >> 1) & 3; tpgdOn = (flags >> 3) & 1; // AT flags if (!mmr) { if (templ == 0) { if (!readByte(&atx[0]) || !readByte(&aty[0]) || !readByte(&atx[1]) || !readByte(&aty[1]) || !readByte(&atx[2]) || !readByte(&aty[2]) || !readByte(&atx[3]) || !readByte(&aty[3])) { goto eofError; } } else { if (!readByte(&atx[0]) || !readByte(&aty[0])) { goto eofError; } } } // set up the arithmetic decoder if (!mmr) { resetGenericStats(templ, nullptr); arithDecoder->start(); } // read the bitmap bitmap = readGenericBitmap(mmr, w, h, templ, tpgdOn, false, nullptr, atx, aty, mmr ? length - 18 : 0); if (!bitmap) { return; } // combine the region bitmap into the page bitmap if (imm) { if (pageH == 0xffffffff && y + h > curPageH) { pageBitmap->expand(y + h, pageDefPixel); if (!pageBitmap->isOk()) { error(errSyntaxError, curStr->getPos(), "JBIG2Stream::readGenericRegionSeg: expand failed"); return; } } pageBitmap->combine(bitmap.get(), x, y, extCombOp); // store the region bitmap } else { bitmap->setSegNum(segNum); segments.push_back(std::move(bitmap)); } // immediate generic segments can have an unspecified length, in // which case, a row count is stored at the end of the segment if (imm && length == 0xffffffff) { readULong(&rowCount); } return; eofError: error(errSyntaxError, curStr->getPos(), "Unexpected EOF in JBIG2 stream"); } inline void JBIG2Stream::mmrAddPixels(int a1, int blackPixels, int *codingLine, int *a0i, int w) { if (a1 > codingLine[*a0i]) { if (a1 > w) { error(errSyntaxError, curStr->getPos(), "JBIG2 MMR row is wrong length ({0:d})", a1); a1 = w; } if ((*a0i & 1) ^ blackPixels) { ++*a0i; } codingLine[*a0i] = a1; } } inline void JBIG2Stream::mmrAddPixelsNeg(int a1, int blackPixels, int *codingLine, int *a0i, int w) { if (a1 > codingLine[*a0i]) { if (a1 > w) { error(errSyntaxError, curStr->getPos(), "JBIG2 MMR row is wrong length ({0:d})", a1); a1 = w; } if ((*a0i & 1) ^ blackPixels) { ++*a0i; } codingLine[*a0i] = a1; } else if (a1 < codingLine[*a0i]) { if (a1 < 0) { error(errSyntaxError, curStr->getPos(), "Invalid JBIG2 MMR code"); a1 = 0; } while (*a0i > 0 && a1 <= codingLine[*a0i - 1]) { --*a0i; } codingLine[*a0i] = a1; } } std::unique_ptr JBIG2Stream::readGenericBitmap(bool mmr, int w, int h, int templ, bool tpgdOn, bool useSkip, JBIG2Bitmap *skip, int *atx, int *aty, int mmrDataLength) { bool ltp; unsigned int ltpCX, cx, cx0, cx1, cx2; int *refLine, *codingLine; int code1, code2, code3; unsigned char *p0, *p1, *p2, *pp; unsigned char *atP0, *atP1, *atP2, *atP3; unsigned int buf0, buf1, buf2; unsigned int atBuf0, atBuf1, atBuf2, atBuf3; int atShift0, atShift1, atShift2, atShift3; unsigned char mask; int x, y, x0, x1, a0i, b1i, blackPixels, pix, i; auto bitmap = std::make_unique(0, w, h); if (!bitmap->isOk()) { return nullptr; } bitmap->clearToZero(); //----- MMR decode if (mmr) { mmrDecoder->reset(); // 0 <= codingLine[0] < codingLine[1] < ... < codingLine[n] = w // ---> max codingLine size = w + 1 // refLine has one extra guard entry at the end // ---> max refLine size = w + 2 codingLine = (int *)gmallocn_checkoverflow(w + 1, sizeof(int)); refLine = (int *)gmallocn_checkoverflow(w + 2, sizeof(int)); if (unlikely(!codingLine || !refLine)) { gfree(codingLine); error(errSyntaxError, curStr->getPos(), "Bad width in JBIG2 generic bitmap"); return nullptr; } memset(refLine, 0, (w + 2) * sizeof(int)); for (i = 0; i < w + 1; ++i) { codingLine[i] = w; } for (y = 0; y < h; ++y) { // copy coding line to ref line for (i = 0; codingLine[i] < w; ++i) { refLine[i] = codingLine[i]; } refLine[i++] = w; refLine[i] = w; // decode a line codingLine[0] = 0; a0i = 0; b1i = 0; blackPixels = 0; // invariant: // refLine[b1i-1] <= codingLine[a0i] < refLine[b1i] < refLine[b1i+1] <= w // exception at left edge: // codingLine[a0i = 0] = refLine[b1i = 0] = 0 is possible // exception at right edge: // refLine[b1i] = refLine[b1i+1] = w is possible while (codingLine[a0i] < w) { code1 = mmrDecoder->get2DCode(); switch (code1) { case twoDimPass: if (unlikely(b1i + 1 >= w + 2)) { break; } mmrAddPixels(refLine[b1i + 1], blackPixels, codingLine, &a0i, w); if (refLine[b1i + 1] < w) { b1i += 2; } break; case twoDimHoriz: code1 = code2 = 0; if (blackPixels) { do { code1 += code3 = mmrDecoder->getBlackCode(); } while (code3 >= 64); do { code2 += code3 = mmrDecoder->getWhiteCode(); } while (code3 >= 64); } else { do { code1 += code3 = mmrDecoder->getWhiteCode(); } while (code3 >= 64); do { code2 += code3 = mmrDecoder->getBlackCode(); } while (code3 >= 64); } mmrAddPixels(codingLine[a0i] + code1, blackPixels, codingLine, &a0i, w); if (codingLine[a0i] < w) { mmrAddPixels(codingLine[a0i] + code2, blackPixels ^ 1, codingLine, &a0i, w); } while (likely(b1i < w + 2) && refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) { b1i += 2; } break; case twoDimVertR3: if (unlikely(b1i >= w + 2)) { break; } mmrAddPixels(refLine[b1i] + 3, blackPixels, codingLine, &a0i, w); blackPixels ^= 1; if (codingLine[a0i] < w) { ++b1i; while (likely(b1i < w + 2) && refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) { b1i += 2; } } break; case twoDimVertR2: if (unlikely(b1i >= w + 2)) { break; } mmrAddPixels(refLine[b1i] + 2, blackPixels, codingLine, &a0i, w); blackPixels ^= 1; if (codingLine[a0i] < w) { ++b1i; while (likely(b1i < w + 2) && refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) { b1i += 2; } } break; case twoDimVertR1: if (unlikely(b1i >= w + 2)) { break; } mmrAddPixels(refLine[b1i] + 1, blackPixels, codingLine, &a0i, w); blackPixels ^= 1; if (codingLine[a0i] < w) { ++b1i; while (likely(b1i < w + 2) && refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) { b1i += 2; } } break; case twoDimVert0: if (unlikely(b1i >= w + 2)) { break; } mmrAddPixels(refLine[b1i], blackPixels, codingLine, &a0i, w); blackPixels ^= 1; if (codingLine[a0i] < w) { ++b1i; while (likely(b1i < w + 2) && refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) { b1i += 2; } } break; case twoDimVertL3: if (unlikely(b1i >= w + 2)) { break; } mmrAddPixelsNeg(refLine[b1i] - 3, blackPixels, codingLine, &a0i, w); blackPixels ^= 1; if (codingLine[a0i] < w) { if (b1i > 0) { --b1i; } else { ++b1i; } while (likely(b1i < w + 2) && refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) { b1i += 2; } } break; case twoDimVertL2: if (unlikely(b1i >= w + 2)) { break; } mmrAddPixelsNeg(refLine[b1i] - 2, blackPixels, codingLine, &a0i, w); blackPixels ^= 1; if (codingLine[a0i] < w) { if (b1i > 0) { --b1i; } else { ++b1i; } while (likely(b1i < w + 2) && refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) { b1i += 2; } } break; case twoDimVertL1: if (unlikely(b1i >= w + 2)) { break; } mmrAddPixelsNeg(refLine[b1i] - 1, blackPixels, codingLine, &a0i, w); blackPixels ^= 1; if (codingLine[a0i] < w) { if (b1i > 0) { --b1i; } else { ++b1i; } while (likely(b1i < w + 2) && refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) { b1i += 2; } } break; case EOF: mmrAddPixels(w, 0, codingLine, &a0i, w); break; default: error(errSyntaxError, curStr->getPos(), "Illegal code in JBIG2 MMR bitmap data"); mmrAddPixels(w, 0, codingLine, &a0i, w); break; } } // convert the run lengths to a bitmap line i = 0; while (true) { for (x = codingLine[i]; x < codingLine[i + 1]; ++x) { bitmap->setPixel(x, y); } if (codingLine[i + 1] >= w || codingLine[i + 2] >= w) { break; } i += 2; } } if (mmrDataLength >= 0) { mmrDecoder->skipTo(mmrDataLength); } else { if (mmrDecoder->get24Bits() != 0x001001) { error(errSyntaxError, curStr->getPos(), "Missing EOFB in JBIG2 MMR bitmap data"); } } gfree(refLine); gfree(codingLine); //----- arithmetic decode } else { // set up the typical row context ltpCX = 0; // make gcc happy if (tpgdOn) { switch (templ) { case 0: ltpCX = 0x3953; // 001 11001 0101 0011 break; case 1: ltpCX = 0x079a; // 0011 11001 101 0 break; case 2: ltpCX = 0x0e3; // 001 1100 01 1 break; case 3: ltpCX = 0x18b; // 01100 0101 1 break; } } ltp = false; cx = cx0 = cx1 = cx2 = 0; // make gcc happy for (y = 0; y < h; ++y) { // check for a "typical" (duplicate) row if (tpgdOn) { if (arithDecoder->decodeBit(ltpCX, genericRegionStats)) { ltp = !ltp; } if (ltp) { if (y > 0) { bitmap->duplicateRow(y, y - 1); } continue; } } switch (templ) { case 0: // set up the context p2 = pp = bitmap->getDataPtr() + y * bitmap->getLineSize(); buf2 = *p2++ << 8; if (y >= 1) { p1 = bitmap->getDataPtr() + (y - 1) * bitmap->getLineSize(); buf1 = *p1++ << 8; if (y >= 2) { p0 = bitmap->getDataPtr() + (y - 2) * bitmap->getLineSize(); buf0 = *p0++ << 8; } else { p0 = nullptr; buf0 = 0; } } else { p1 = p0 = nullptr; buf1 = buf0 = 0; } if (atx[0] >= -8 && atx[0] <= 8 && atx[1] >= -8 && atx[1] <= 8 && atx[2] >= -8 && atx[2] <= 8 && atx[3] >= -8 && atx[3] <= 8) { // set up the adaptive context if (y + aty[0] >= 0 && y + aty[0] < bitmap->getHeight()) { atP0 = bitmap->getDataPtr() + (y + aty[0]) * bitmap->getLineSize(); atBuf0 = *atP0++ << 8; } else { atP0 = nullptr; atBuf0 = 0; } atShift0 = 15 - atx[0]; if (y + aty[1] >= 0 && y + aty[1] < bitmap->getHeight()) { atP1 = bitmap->getDataPtr() + (y + aty[1]) * bitmap->getLineSize(); atBuf1 = *atP1++ << 8; } else { atP1 = nullptr; atBuf1 = 0; } atShift1 = 15 - atx[1]; if (y + aty[2] >= 0 && y + aty[2] < bitmap->getHeight()) { atP2 = bitmap->getDataPtr() + (y + aty[2]) * bitmap->getLineSize(); atBuf2 = *atP2++ << 8; } else { atP2 = nullptr; atBuf2 = 0; } atShift2 = 15 - atx[2]; if (y + aty[3] >= 0 && y + aty[3] < bitmap->getHeight()) { atP3 = bitmap->getDataPtr() + (y + aty[3]) * bitmap->getLineSize(); atBuf3 = *atP3++ << 8; } else { atP3 = nullptr; atBuf3 = 0; } atShift3 = 15 - atx[3]; // decode the row for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) { if (x0 + 8 < w) { if (p0) { buf0 |= *p0++; } if (p1) { buf1 |= *p1++; } buf2 |= *p2++; if (atP0) { atBuf0 |= *atP0++; } if (atP1) { atBuf1 |= *atP1++; } if (atP2) { atBuf2 |= *atP2++; } if (atP3) { atBuf3 |= *atP3++; } } for (x1 = 0, mask = 0x80; x1 < 8 && x < w; ++x1, ++x, mask >>= 1) { // build the context cx0 = (buf0 >> 14) & 0x07; cx1 = (buf1 >> 13) & 0x1f; cx2 = (buf2 >> 16) & 0x0f; cx = (cx0 << 13) | (cx1 << 8) | (cx2 << 4) | (((atBuf0 >> atShift0) & 1) << 3) | (((atBuf1 >> atShift1) & 1) << 2) | (((atBuf2 >> atShift2) & 1) << 1) | ((atBuf3 >> atShift3) & 1); // check for a skipped pixel if (!(useSkip && skip->getPixel(x, y))) { // decode the pixel if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) { *pp |= mask; buf2 |= 0x8000; if (aty[0] == 0) { atBuf0 |= 0x8000; } if (aty[1] == 0) { atBuf1 |= 0x8000; } if (aty[2] == 0) { atBuf2 |= 0x8000; } if (aty[3] == 0) { atBuf3 |= 0x8000; } } } // update the context buf0 <<= 1; buf1 <<= 1; buf2 <<= 1; atBuf0 <<= 1; atBuf1 <<= 1; atBuf2 <<= 1; atBuf3 <<= 1; } } } else { // decode the row for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) { if (x0 + 8 < w) { if (p0) { buf0 |= *p0++; } if (p1) { buf1 |= *p1++; } buf2 |= *p2++; } for (x1 = 0, mask = 0x80; x1 < 8 && x < w; ++x1, ++x, mask >>= 1) { // build the context cx0 = (buf0 >> 14) & 0x07; cx1 = (buf1 >> 13) & 0x1f; cx2 = (buf2 >> 16) & 0x0f; cx = (cx0 << 13) | (cx1 << 8) | (cx2 << 4) | (bitmap->getPixel(x + atx[0], y + aty[0]) << 3) | (bitmap->getPixel(x + atx[1], y + aty[1]) << 2) | (bitmap->getPixel(x + atx[2], y + aty[2]) << 1) | bitmap->getPixel(x + atx[3], y + aty[3]); // check for a skipped pixel if (!(useSkip && skip->getPixel(x, y))) { // decode the pixel if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) { *pp |= mask; buf2 |= 0x8000; } } // update the context buf0 <<= 1; buf1 <<= 1; buf2 <<= 1; } } } break; case 1: // set up the context p2 = pp = bitmap->getDataPtr() + y * bitmap->getLineSize(); buf2 = *p2++ << 8; if (y >= 1) { p1 = bitmap->getDataPtr() + (y - 1) * bitmap->getLineSize(); buf1 = *p1++ << 8; if (y >= 2) { p0 = bitmap->getDataPtr() + (y - 2) * bitmap->getLineSize(); buf0 = *p0++ << 8; } else { p0 = nullptr; buf0 = 0; } } else { p1 = p0 = nullptr; buf1 = buf0 = 0; } if (atx[0] >= -8 && atx[0] <= 8) { // set up the adaptive context const int atY = y + aty[0]; if ((atY >= 0) && (atY < bitmap->getHeight())) { atP0 = bitmap->getDataPtr() + atY * bitmap->getLineSize(); atBuf0 = *atP0++ << 8; } else { atP0 = nullptr; atBuf0 = 0; } atShift0 = 15 - atx[0]; // decode the row for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) { if (x0 + 8 < w) { if (p0) { buf0 |= *p0++; } if (p1) { buf1 |= *p1++; } buf2 |= *p2++; if (atP0) { atBuf0 |= *atP0++; } } for (x1 = 0, mask = 0x80; x1 < 8 && x < w; ++x1, ++x, mask >>= 1) { // build the context cx0 = (buf0 >> 13) & 0x0f; cx1 = (buf1 >> 13) & 0x1f; cx2 = (buf2 >> 16) & 0x07; cx = (cx0 << 9) | (cx1 << 4) | (cx2 << 1) | ((atBuf0 >> atShift0) & 1); // check for a skipped pixel if (!(useSkip && skip->getPixel(x, y))) { // decode the pixel if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) { *pp |= mask; buf2 |= 0x8000; if (aty[0] == 0) { atBuf0 |= 0x8000; } } } // update the context buf0 <<= 1; buf1 <<= 1; buf2 <<= 1; atBuf0 <<= 1; } } } else { // decode the row for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) { if (x0 + 8 < w) { if (p0) { buf0 |= *p0++; } if (p1) { buf1 |= *p1++; } buf2 |= *p2++; } for (x1 = 0, mask = 0x80; x1 < 8 && x < w; ++x1, ++x, mask >>= 1) { // build the context cx0 = (buf0 >> 13) & 0x0f; cx1 = (buf1 >> 13) & 0x1f; cx2 = (buf2 >> 16) & 0x07; cx = (cx0 << 9) | (cx1 << 4) | (cx2 << 1) | bitmap->getPixel(x + atx[0], y + aty[0]); // check for a skipped pixel if (!(useSkip && skip->getPixel(x, y))) { // decode the pixel if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) { *pp |= mask; buf2 |= 0x8000; } } // update the context buf0 <<= 1; buf1 <<= 1; buf2 <<= 1; } } } break; case 2: // set up the context p2 = pp = bitmap->getDataPtr() + y * bitmap->getLineSize(); buf2 = *p2++ << 8; if (y >= 1) { p1 = bitmap->getDataPtr() + (y - 1) * bitmap->getLineSize(); buf1 = *p1++ << 8; if (y >= 2) { p0 = bitmap->getDataPtr() + (y - 2) * bitmap->getLineSize(); buf0 = *p0++ << 8; } else { p0 = nullptr; buf0 = 0; } } else { p1 = p0 = nullptr; buf1 = buf0 = 0; } if (atx[0] >= -8 && atx[0] <= 8) { // set up the adaptive context const int atY = y + aty[0]; if ((atY >= 0) && (atY < bitmap->getHeight())) { atP0 = bitmap->getDataPtr() + atY * bitmap->getLineSize(); atBuf0 = *atP0++ << 8; } else { atP0 = nullptr; atBuf0 = 0; } atShift0 = 15 - atx[0]; // decode the row for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) { if (x0 + 8 < w) { if (p0) { buf0 |= *p0++; } if (p1) { buf1 |= *p1++; } buf2 |= *p2++; if (atP0) { atBuf0 |= *atP0++; } } for (x1 = 0, mask = 0x80; x1 < 8 && x < w; ++x1, ++x, mask >>= 1) { // build the context cx0 = (buf0 >> 14) & 0x07; cx1 = (buf1 >> 14) & 0x0f; cx2 = (buf2 >> 16) & 0x03; cx = (cx0 << 7) | (cx1 << 3) | (cx2 << 1) | ((atBuf0 >> atShift0) & 1); // check for a skipped pixel if (!(useSkip && skip->getPixel(x, y))) { // decode the pixel if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) { *pp |= mask; buf2 |= 0x8000; if (aty[0] == 0) { atBuf0 |= 0x8000; } } } // update the context buf0 <<= 1; buf1 <<= 1; buf2 <<= 1; atBuf0 <<= 1; } } } else { // decode the row for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) { if (x0 + 8 < w) { if (p0) { buf0 |= *p0++; } if (p1) { buf1 |= *p1++; } buf2 |= *p2++; } for (x1 = 0, mask = 0x80; x1 < 8 && x < w; ++x1, ++x, mask >>= 1) { // build the context cx0 = (buf0 >> 14) & 0x07; cx1 = (buf1 >> 14) & 0x0f; cx2 = (buf2 >> 16) & 0x03; cx = (cx0 << 7) | (cx1 << 3) | (cx2 << 1) | bitmap->getPixel(x + atx[0], y + aty[0]); // check for a skipped pixel if (!(useSkip && skip->getPixel(x, y))) { // decode the pixel if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) { *pp |= mask; buf2 |= 0x8000; } } // update the context buf0 <<= 1; buf1 <<= 1; buf2 <<= 1; } } } break; case 3: // set up the context p2 = pp = bitmap->getDataPtr() + y * bitmap->getLineSize(); buf2 = *p2++ << 8; if (y >= 1) { p1 = bitmap->getDataPtr() + (y - 1) * bitmap->getLineSize(); buf1 = *p1++ << 8; } else { p1 = nullptr; buf1 = 0; } if (atx[0] >= -8 && atx[0] <= 8) { // set up the adaptive context const int atY = y + aty[0]; if ((atY >= 0) && (atY < bitmap->getHeight())) { atP0 = bitmap->getDataPtr() + atY * bitmap->getLineSize(); atBuf0 = *atP0++ << 8; } else { atP0 = nullptr; atBuf0 = 0; } atShift0 = 15 - atx[0]; // decode the row for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) { if (x0 + 8 < w) { if (p1) { buf1 |= *p1++; } buf2 |= *p2++; if (atP0) { atBuf0 |= *atP0++; } } for (x1 = 0, mask = 0x80; x1 < 8 && x < w; ++x1, ++x, mask >>= 1) { // build the context cx1 = (buf1 >> 14) & 0x1f; cx2 = (buf2 >> 16) & 0x0f; cx = (cx1 << 5) | (cx2 << 1) | ((atBuf0 >> atShift0) & 1); // check for a skipped pixel if (!(useSkip && skip->getPixel(x, y))) { // decode the pixel if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) { *pp |= mask; buf2 |= 0x8000; if (aty[0] == 0) { atBuf0 |= 0x8000; } } } // update the context buf1 <<= 1; buf2 <<= 1; atBuf0 <<= 1; } } } else { // decode the row for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) { if (x0 + 8 < w) { if (p1) { buf1 |= *p1++; } buf2 |= *p2++; } for (x1 = 0, mask = 0x80; x1 < 8 && x < w; ++x1, ++x, mask >>= 1) { // build the context cx1 = (buf1 >> 14) & 0x1f; cx2 = (buf2 >> 16) & 0x0f; cx = (cx1 << 5) | (cx2 << 1) | bitmap->getPixel(x + atx[0], y + aty[0]); // check for a skipped pixel if (!(useSkip && skip->getPixel(x, y))) { // decode the pixel if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) { *pp |= mask; buf2 |= 0x8000; } } // update the context buf1 <<= 1; buf2 <<= 1; } } } break; } } } return bitmap; } void JBIG2Stream::readGenericRefinementRegionSeg(unsigned int segNum, bool imm, bool lossless, unsigned int length, unsigned int *refSegs, unsigned int nRefSegs) { std::unique_ptr bitmap; JBIG2Bitmap *refBitmap; unsigned int w, h, x, y, segInfoFlags, extCombOp; unsigned int flags, templ, tpgrOn; int atx[2], aty[2]; JBIG2Segment *seg; // region segment info field if (!readULong(&w) || !readULong(&h) || !readULong(&x) || !readULong(&y) || !readUByte(&segInfoFlags)) { goto eofError; } extCombOp = segInfoFlags & 7; // rest of the generic refinement region segment header if (!readUByte(&flags)) { goto eofError; } templ = flags & 1; tpgrOn = (flags >> 1) & 1; // AT flags if (!templ) { if (!readByte(&atx[0]) || !readByte(&aty[0]) || !readByte(&atx[1]) || !readByte(&aty[1])) { goto eofError; } } // resize the page bitmap if needed if (nRefSegs == 0 || imm) { if (pageH == 0xffffffff && y + h > curPageH) { pageBitmap->expand(y + h, pageDefPixel); } } // get referenced bitmap if (nRefSegs > 1) { error(errSyntaxError, curStr->getPos(), "Bad reference in JBIG2 generic refinement segment"); return; } if (nRefSegs == 1) { seg = findSegment(refSegs[0]); if (seg == nullptr || seg->getType() != jbig2SegBitmap) { error(errSyntaxError, curStr->getPos(), "Bad bitmap reference in JBIG2 generic refinement segment"); return; } refBitmap = (JBIG2Bitmap *)seg; } else { refBitmap = pageBitmap->getSlice(x, y, w, h); } // set up the arithmetic decoder resetRefinementStats(templ, nullptr); arithDecoder->start(); // read bitmap = readGenericRefinementRegion(w, h, templ, tpgrOn, refBitmap, 0, 0, atx, aty); // combine the region bitmap into the page bitmap if (imm && bitmap) { pageBitmap->combine(bitmap.get(), x, y, extCombOp); // store the region bitmap } else { if (bitmap) { bitmap->setSegNum(segNum); segments.push_back(std::move(bitmap)); } else { error(errSyntaxError, curStr->getPos(), "readGenericRefinementRegionSeg with null bitmap"); } } // delete the referenced bitmap if (nRefSegs == 1) { discardSegment(refSegs[0]); } else { delete refBitmap; } return; eofError: error(errSyntaxError, curStr->getPos(), "Unexpected EOF in JBIG2 stream"); } std::unique_ptr JBIG2Stream::readGenericRefinementRegion(int w, int h, int templ, bool tpgrOn, JBIG2Bitmap *refBitmap, int refDX, int refDY, int *atx, int *aty) { bool ltp; unsigned int ltpCX, cx, cx0, cx2, cx3, cx4, tpgrCX0, tpgrCX1, tpgrCX2; JBIG2BitmapPtr cxPtr0 = { nullptr, 0, 0 }; JBIG2BitmapPtr cxPtr1 = { nullptr, 0, 0 }; JBIG2BitmapPtr cxPtr2 = { nullptr, 0, 0 }; JBIG2BitmapPtr cxPtr3 = { nullptr, 0, 0 }; JBIG2BitmapPtr cxPtr4 = { nullptr, 0, 0 }; JBIG2BitmapPtr cxPtr5 = { nullptr, 0, 0 }; JBIG2BitmapPtr cxPtr6 = { nullptr, 0, 0 }; JBIG2BitmapPtr tpgrCXPtr0 = { nullptr, 0, 0 }; JBIG2BitmapPtr tpgrCXPtr1 = { nullptr, 0, 0 }; JBIG2BitmapPtr tpgrCXPtr2 = { nullptr, 0, 0 }; int x, y, pix; if (!refBitmap) { return nullptr; } auto bitmap = std::make_unique(0, w, h); if (!bitmap->isOk()) { return nullptr; } bitmap->clearToZero(); // set up the typical row context if (templ) { ltpCX = 0x008; } else { ltpCX = 0x0010; } ltp = false; for (y = 0; y < h; ++y) { if (templ) { // set up the context bitmap->getPixelPtr(0, y - 1, &cxPtr0); cx0 = bitmap->nextPixel(&cxPtr0); bitmap->getPixelPtr(-1, y, &cxPtr1); refBitmap->getPixelPtr(-refDX, y - 1 - refDY, &cxPtr2); refBitmap->getPixelPtr(-1 - refDX, y - refDY, &cxPtr3); cx3 = refBitmap->nextPixel(&cxPtr3); cx3 = (cx3 << 1) | refBitmap->nextPixel(&cxPtr3); refBitmap->getPixelPtr(-refDX, y + 1 - refDY, &cxPtr4); cx4 = refBitmap->nextPixel(&cxPtr4); // set up the typical prediction context tpgrCX0 = tpgrCX1 = tpgrCX2 = 0; // make gcc happy if (tpgrOn) { refBitmap->getPixelPtr(-1 - refDX, y - 1 - refDY, &tpgrCXPtr0); tpgrCX0 = refBitmap->nextPixel(&tpgrCXPtr0); tpgrCX0 = (tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0); tpgrCX0 = (tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0); refBitmap->getPixelPtr(-1 - refDX, y - refDY, &tpgrCXPtr1); tpgrCX1 = refBitmap->nextPixel(&tpgrCXPtr1); tpgrCX1 = (tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1); tpgrCX1 = (tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1); refBitmap->getPixelPtr(-1 - refDX, y + 1 - refDY, &tpgrCXPtr2); tpgrCX2 = refBitmap->nextPixel(&tpgrCXPtr2); tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2); tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2); } else { tpgrCXPtr0.p = tpgrCXPtr1.p = tpgrCXPtr2.p = nullptr; // make gcc happy tpgrCXPtr0.shift = tpgrCXPtr1.shift = tpgrCXPtr2.shift = 0; tpgrCXPtr0.x = tpgrCXPtr1.x = tpgrCXPtr2.x = 0; } for (x = 0; x < w; ++x) { // update the context cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 7; cx3 = ((cx3 << 1) | refBitmap->nextPixel(&cxPtr3)) & 7; cx4 = ((cx4 << 1) | refBitmap->nextPixel(&cxPtr4)) & 3; if (tpgrOn) { // update the typical predictor context tpgrCX0 = ((tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0)) & 7; tpgrCX1 = ((tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1)) & 7; tpgrCX2 = ((tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2)) & 7; // check for a "typical" pixel if (arithDecoder->decodeBit(ltpCX, refinementRegionStats)) { ltp = !ltp; } if (tpgrCX0 == 0 && tpgrCX1 == 0 && tpgrCX2 == 0) { bitmap->clearPixel(x, y); continue; } else if (tpgrCX0 == 7 && tpgrCX1 == 7 && tpgrCX2 == 7) { bitmap->setPixel(x, y); continue; } } // build the context cx = (cx0 << 7) | (bitmap->nextPixel(&cxPtr1) << 6) | (refBitmap->nextPixel(&cxPtr2) << 5) | (cx3 << 2) | cx4; // decode the pixel if ((pix = arithDecoder->decodeBit(cx, refinementRegionStats))) { bitmap->setPixel(x, y); } } } else { // set up the context bitmap->getPixelPtr(0, y - 1, &cxPtr0); cx0 = bitmap->nextPixel(&cxPtr0); bitmap->getPixelPtr(-1, y, &cxPtr1); refBitmap->getPixelPtr(-refDX, y - 1 - refDY, &cxPtr2); cx2 = refBitmap->nextPixel(&cxPtr2); refBitmap->getPixelPtr(-1 - refDX, y - refDY, &cxPtr3); cx3 = refBitmap->nextPixel(&cxPtr3); cx3 = (cx3 << 1) | refBitmap->nextPixel(&cxPtr3); refBitmap->getPixelPtr(-1 - refDX, y + 1 - refDY, &cxPtr4); cx4 = refBitmap->nextPixel(&cxPtr4); cx4 = (cx4 << 1) | refBitmap->nextPixel(&cxPtr4); bitmap->getPixelPtr(atx[0], y + aty[0], &cxPtr5); refBitmap->getPixelPtr(atx[1] - refDX, y + aty[1] - refDY, &cxPtr6); // set up the typical prediction context tpgrCX0 = tpgrCX1 = tpgrCX2 = 0; // make gcc happy if (tpgrOn) { refBitmap->getPixelPtr(-1 - refDX, y - 1 - refDY, &tpgrCXPtr0); tpgrCX0 = refBitmap->nextPixel(&tpgrCXPtr0); tpgrCX0 = (tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0); tpgrCX0 = (tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0); refBitmap->getPixelPtr(-1 - refDX, y - refDY, &tpgrCXPtr1); tpgrCX1 = refBitmap->nextPixel(&tpgrCXPtr1); tpgrCX1 = (tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1); tpgrCX1 = (tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1); refBitmap->getPixelPtr(-1 - refDX, y + 1 - refDY, &tpgrCXPtr2); tpgrCX2 = refBitmap->nextPixel(&tpgrCXPtr2); tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2); tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2); } else { tpgrCXPtr0.p = tpgrCXPtr1.p = tpgrCXPtr2.p = nullptr; // make gcc happy tpgrCXPtr0.shift = tpgrCXPtr1.shift = tpgrCXPtr2.shift = 0; tpgrCXPtr0.x = tpgrCXPtr1.x = tpgrCXPtr2.x = 0; } for (x = 0; x < w; ++x) { // update the context cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 3; cx2 = ((cx2 << 1) | refBitmap->nextPixel(&cxPtr2)) & 3; cx3 = ((cx3 << 1) | refBitmap->nextPixel(&cxPtr3)) & 7; cx4 = ((cx4 << 1) | refBitmap->nextPixel(&cxPtr4)) & 7; if (tpgrOn) { // update the typical predictor context tpgrCX0 = ((tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0)) & 7; tpgrCX1 = ((tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1)) & 7; tpgrCX2 = ((tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2)) & 7; // check for a "typical" pixel if (arithDecoder->decodeBit(ltpCX, refinementRegionStats)) { ltp = !ltp; } if (tpgrCX0 == 0 && tpgrCX1 == 0 && tpgrCX2 == 0) { bitmap->clearPixel(x, y); continue; } else if (tpgrCX0 == 7 && tpgrCX1 == 7 && tpgrCX2 == 7) { bitmap->setPixel(x, y); continue; } } // build the context cx = (cx0 << 11) | (bitmap->nextPixel(&cxPtr1) << 10) | (cx2 << 8) | (cx3 << 5) | (cx4 << 2) | (bitmap->nextPixel(&cxPtr5) << 1) | refBitmap->nextPixel(&cxPtr6); // decode the pixel if ((pix = arithDecoder->decodeBit(cx, refinementRegionStats))) { bitmap->setPixel(x, y); } } } } return bitmap; } void JBIG2Stream::readPageInfoSeg(unsigned int length) { unsigned int xRes, yRes, flags, striping; if (!readULong(&pageW) || !readULong(&pageH) || !readULong(&xRes) || !readULong(&yRes) || !readUByte(&flags) || !readUWord(&striping)) { goto eofError; } pageDefPixel = (flags >> 2) & 1; defCombOp = (flags >> 3) & 3; // allocate the page bitmap if (pageH == 0xffffffff) { curPageH = striping & 0x7fff; } else { curPageH = pageH; } delete pageBitmap; pageBitmap = new JBIG2Bitmap(0, pageW, curPageH); if (!pageBitmap->isOk()) { delete pageBitmap; pageBitmap = nullptr; return; } // default pixel value if (pageDefPixel) { pageBitmap->clearToOne(); } else { pageBitmap->clearToZero(); } return; eofError: error(errSyntaxError, curStr->getPos(), "Unexpected EOF in JBIG2 stream"); } void JBIG2Stream::readEndOfStripeSeg(unsigned int length) { // skip the segment byteCounter += curStr->discardChars(length); } void JBIG2Stream::readProfilesSeg(unsigned int length) { // skip the segment byteCounter += curStr->discardChars(length); } void JBIG2Stream::readCodeTableSeg(unsigned int segNum, unsigned int length) { JBIG2HuffmanTable *huffTab; unsigned int flags, oob, prefixBits, rangeBits; int lowVal, highVal, val; unsigned int huffTabSize, i; if (!readUByte(&flags) || !readLong(&lowVal) || !readLong(&highVal)) { goto eofError; } oob = flags & 1; prefixBits = ((flags >> 1) & 7) + 1; rangeBits = ((flags >> 4) & 7) + 1; huffDecoder->reset(); huffTabSize = 8; huffTab = (JBIG2HuffmanTable *)gmallocn_checkoverflow(huffTabSize, sizeof(JBIG2HuffmanTable)); if (unlikely(!huffTab)) { goto oomError; } i = 0; val = lowVal; while (val < highVal) { if (i == huffTabSize) { huffTabSize *= 2; huffTab = (JBIG2HuffmanTable *)greallocn_checkoverflow(huffTab, huffTabSize, sizeof(JBIG2HuffmanTable)); if (unlikely(!huffTab)) { goto oomError; } } huffTab[i].val = val; huffTab[i].prefixLen = huffDecoder->readBits(prefixBits); huffTab[i].rangeLen = huffDecoder->readBits(rangeBits); if (unlikely(checkedAdd(val, 1 << huffTab[i].rangeLen, &val))) { free(huffTab); return; } ++i; } if (i + oob + 3 > huffTabSize) { huffTabSize = i + oob + 3; huffTab = (JBIG2HuffmanTable *)greallocn_checkoverflow(huffTab, huffTabSize, sizeof(JBIG2HuffmanTable)); if (unlikely(!huffTab)) { goto oomError; } } huffTab[i].val = lowVal - 1; huffTab[i].prefixLen = huffDecoder->readBits(prefixBits); huffTab[i].rangeLen = jbig2HuffmanLOW; ++i; huffTab[i].val = highVal; huffTab[i].prefixLen = huffDecoder->readBits(prefixBits); huffTab[i].rangeLen = 32; ++i; if (oob) { huffTab[i].val = 0; huffTab[i].prefixLen = huffDecoder->readBits(prefixBits); huffTab[i].rangeLen = jbig2HuffmanOOB; ++i; } huffTab[i].val = 0; huffTab[i].prefixLen = 0; huffTab[i].rangeLen = jbig2HuffmanEOT; if (JBIG2HuffmanDecoder::buildTable(huffTab, i)) { // create and store the new table segment segments.push_back(std::make_unique(segNum, huffTab)); } else { free(huffTab); } return; eofError: error(errSyntaxError, curStr->getPos(), "Unexpected EOF in JBIG2 stream"); oomError: error(errInternal, curStr->getPos(), "Failed allocation when processing JBIG2 stream"); } void JBIG2Stream::readExtensionSeg(unsigned int length) { // skip the segment byteCounter += curStr->discardChars(length); } JBIG2Segment *JBIG2Stream::findSegment(unsigned int segNum) { for (std::unique_ptr &seg : globalSegments) { if (seg->getSegNum() == segNum) { return seg.get(); } } for (std::unique_ptr &seg : segments) { if (seg->getSegNum() == segNum) { return seg.get(); } } return nullptr; } void JBIG2Stream::discardSegment(unsigned int segNum) { for (auto it = globalSegments.begin(); it != globalSegments.end(); ++it) { if ((*it)->getSegNum() == segNum) { globalSegments.erase(it); return; } } for (auto it = segments.begin(); it != segments.end(); ++it) { if ((*it)->getSegNum() == segNum) { segments.erase(it); return; } } } void JBIG2Stream::resetGenericStats(unsigned int templ, JArithmeticDecoderStats *prevStats) { int size; size = contextSize[templ]; if (prevStats && prevStats->getContextSize() == size) { if (genericRegionStats->getContextSize() == size) { genericRegionStats->copyFrom(prevStats); } else { delete genericRegionStats; genericRegionStats = prevStats->copy(); } } else { if (genericRegionStats->getContextSize() == size) { genericRegionStats->reset(); } else { delete genericRegionStats; genericRegionStats = new JArithmeticDecoderStats(1 << size); } } } void JBIG2Stream::resetRefinementStats(unsigned int templ, JArithmeticDecoderStats *prevStats) { int size; size = refContextSize[templ]; if (prevStats && prevStats->getContextSize() == size) { if (refinementRegionStats->getContextSize() == size) { refinementRegionStats->copyFrom(prevStats); } else { delete refinementRegionStats; refinementRegionStats = prevStats->copy(); } } else { if (refinementRegionStats->getContextSize() == size) { refinementRegionStats->reset(); } else { delete refinementRegionStats; refinementRegionStats = new JArithmeticDecoderStats(1 << size); } } } bool JBIG2Stream::resetIntStats(int symCodeLen) { iadhStats->reset(); iadwStats->reset(); iaexStats->reset(); iaaiStats->reset(); iadtStats->reset(); iaitStats->reset(); iafsStats->reset(); iadsStats->reset(); iardxStats->reset(); iardyStats->reset(); iardwStats->reset(); iardhStats->reset(); iariStats->reset(); if (symCodeLen + 1 >= 31) { return false; } if (iaidStats != nullptr && iaidStats->getContextSize() == 1 << (symCodeLen + 1)) { iaidStats->reset(); } else { delete iaidStats; iaidStats = new JArithmeticDecoderStats(1 << (symCodeLen + 1)); if (!iaidStats->isValid()) { delete iaidStats; iaidStats = nullptr; return false; } } return true; } bool JBIG2Stream::readUByte(unsigned int *x) { int c0; if ((c0 = curStr->getChar()) == EOF) { return false; } ++byteCounter; *x = (unsigned int)c0; return true; } bool JBIG2Stream::readByte(int *x) { int c0; if ((c0 = curStr->getChar()) == EOF) { return false; } ++byteCounter; *x = c0; if (c0 & 0x80) { *x |= -1 - 0xff; } return true; } bool JBIG2Stream::readUWord(unsigned int *x) { int c0, c1; if ((c0 = curStr->getChar()) == EOF || (c1 = curStr->getChar()) == EOF) { return false; } byteCounter += 2; *x = (unsigned int)((c0 << 8) | c1); return true; } bool JBIG2Stream::readULong(unsigned int *x) { int c0, c1, c2, c3; if ((c0 = curStr->getChar()) == EOF || (c1 = curStr->getChar()) == EOF || (c2 = curStr->getChar()) == EOF || (c3 = curStr->getChar()) == EOF) { return false; } byteCounter += 4; *x = (unsigned int)((c0 << 24) | (c1 << 16) | (c2 << 8) | c3); return true; } bool JBIG2Stream::readLong(int *x) { int c0, c1, c2, c3; if ((c0 = curStr->getChar()) == EOF || (c1 = curStr->getChar()) == EOF || (c2 = curStr->getChar()) == EOF || (c3 = curStr->getChar()) == EOF) { return false; } byteCounter += 4; *x = ((c0 << 24) | (c1 << 16) | (c2 << 8) | c3); if (c0 & 0x80) { *x |= -1 - (int)0xffffffff; } return true; } poppler-24.02.0/poppler/JBIG2Stream.h000066400000000000000000000141441455701731300171550ustar00rootroot00000000000000//======================================================================== // // JBIG2Stream.h // // Copyright 2002-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) 2009 David Benjamin // Copyright (C) 2013 Adrian Johnson // Copyright (C) 2015 Suzuki Toshiya // Copyright (C) 2019-2021 Oliver Sander // Copyright (C) 2019 Volker Krause // Copyright (C) 2019, 2021 Albert Astals Cid // Copyright (C) 2019, 2020 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 // //======================================================================== #ifndef JBIG2STREAM_H #define JBIG2STREAM_H #include "Object.h" #include "Stream.h" class JBIG2Segment; class JBIG2Bitmap; class JArithmeticDecoder; class JArithmeticDecoderStats; class JBIG2HuffmanDecoder; struct JBIG2HuffmanTable; class JBIG2MMRDecoder; //------------------------------------------------------------------------ class JBIG2Stream : public FilterStream { public: JBIG2Stream(Stream *strA, Object &&globalsStreamA, Object *globalsStreamRefA); ~JBIG2Stream() override; StreamKind getKind() const override { return strJBIG2; } void reset() override; void close() override; Goffset getPos() override; int getChar() override; int lookChar() override; GooString *getPSFilter(int psLevel, const char *indent) override; bool isBinary(bool last = true) const override; virtual Object *getGlobalsStream() { return &globalsStream; } virtual Ref getGlobalsStreamRef() { return globalsStreamRef; } private: bool hasGetChars() override { return true; } int getChars(int nChars, unsigned char *buffer) override; void readSegments(); bool readSymbolDictSeg(unsigned int segNum, unsigned int length, unsigned int *refSegs, unsigned int nRefSegs); void readTextRegionSeg(unsigned int segNum, bool imm, bool lossless, unsigned int length, unsigned int *refSegs, unsigned int nRefSegs); std::unique_ptr 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, unsigned int defPixel, unsigned int combOp, unsigned int transposed, unsigned int refCorner, int sOffset, const JBIG2HuffmanTable *huffFSTable, const JBIG2HuffmanTable *huffDSTable, const JBIG2HuffmanTable *huffDTTable, const JBIG2HuffmanTable *huffRDWTable, const JBIG2HuffmanTable *huffRDHTable, const JBIG2HuffmanTable *huffRDXTable, const JBIG2HuffmanTable *huffRDYTable, const JBIG2HuffmanTable *huffRSizeTable, unsigned int templ, int *atx, int *aty); void readPatternDictSeg(unsigned int segNum, unsigned int length); void readHalftoneRegionSeg(unsigned int segNum, bool imm, bool lossless, unsigned int length, unsigned int *refSegs, unsigned int nRefSegs); void readGenericRegionSeg(unsigned int segNum, bool imm, bool lossless, unsigned int length); void mmrAddPixels(int a1, int blackPixels, int *codingLine, int *a0i, int w); void mmrAddPixelsNeg(int a1, int blackPixels, int *codingLine, int *a0i, int w); std::unique_ptr readGenericBitmap(bool mmr, int w, int h, int templ, bool tpgdOn, bool useSkip, JBIG2Bitmap *skip, int *atx, int *aty, int mmrDataLength); void readGenericRefinementRegionSeg(unsigned int segNum, bool imm, bool lossless, unsigned int length, unsigned int *refSegs, unsigned int nRefSegs); std::unique_ptr readGenericRefinementRegion(int w, int h, int templ, bool tpgrOn, JBIG2Bitmap *refBitmap, int refDX, int refDY, int *atx, int *aty); void readPageInfoSeg(unsigned int length); void readEndOfStripeSeg(unsigned int length); void readProfilesSeg(unsigned int length); void readCodeTableSeg(unsigned int segNum, unsigned int length); void readExtensionSeg(unsigned int length); JBIG2Segment *findSegment(unsigned int segNum); void discardSegment(unsigned int segNum); void resetGenericStats(unsigned int templ, JArithmeticDecoderStats *prevStats); void resetRefinementStats(unsigned int templ, JArithmeticDecoderStats *prevStats); bool resetIntStats(int symCodeLen); bool readUByte(unsigned int *x); bool readByte(int *x); bool readUWord(unsigned int *x); bool readULong(unsigned int *x); bool readLong(int *x); Object globalsStream; Ref globalsStreamRef; unsigned int pageW, pageH, curPageH; unsigned int pageDefPixel; JBIG2Bitmap *pageBitmap; unsigned int defCombOp; std::vector> segments; std::vector> globalSegments; Stream *curStr; unsigned char *dataPtr; unsigned char *dataEnd; unsigned int byteCounter; JArithmeticDecoder *arithDecoder; JArithmeticDecoderStats *genericRegionStats; JArithmeticDecoderStats *refinementRegionStats; JArithmeticDecoderStats *iadhStats; JArithmeticDecoderStats *iadwStats; JArithmeticDecoderStats *iaexStats; JArithmeticDecoderStats *iaaiStats; JArithmeticDecoderStats *iadtStats; JArithmeticDecoderStats *iaitStats; JArithmeticDecoderStats *iafsStats; JArithmeticDecoderStats *iadsStats; JArithmeticDecoderStats *iardxStats; JArithmeticDecoderStats *iardyStats; JArithmeticDecoderStats *iardwStats; JArithmeticDecoderStats *iardhStats; JArithmeticDecoderStats *iariStats; JArithmeticDecoderStats *iaidStats; JBIG2HuffmanDecoder *huffDecoder; JBIG2MMRDecoder *mmrDecoder; }; #endif poppler-24.02.0/poppler/JPEG2000Stream.cc000066400000000000000000000256651455701731300175570ustar00rootroot00000000000000//======================================================================== // // JPEG2000Stream.cc // // A JPX stream decoder using OpenJPEG // // 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 // // Licensed under GPLv2 or later // //======================================================================== #include "config.h" #include "JPEG2000Stream.h" #include struct JPXStreamPrivate { opj_image_t *image = nullptr; int counter = 0; int ccounter = 0; int npixels = 0; int ncomps = 0; bool inited = false; void init2(OPJ_CODEC_FORMAT format, const unsigned char *buf, int length, bool indexed); }; static inline unsigned char adjustComp(int r, int adjust, int depth, int sgndcorr, bool indexed) { if (!indexed) { r += sgndcorr; if (adjust) { r = (r >> adjust) + ((r >> (adjust - 1)) % 2); } else if (depth < 8) { r = r << (8 - depth); } } if (unlikely(r > 255)) { r = 255; } return r; } static inline int doLookChar(JPXStreamPrivate *priv) { if (unlikely(priv->counter >= priv->npixels)) { return EOF; } return ((unsigned char *)priv->image->comps[priv->ccounter].data)[priv->counter]; } static inline int doGetChar(JPXStreamPrivate *priv) { const int result = doLookChar(priv); if (++priv->ccounter == priv->ncomps) { priv->ccounter = 0; ++priv->counter; } return result; } JPXStream::JPXStream(Stream *strA) : FilterStream(strA) { priv = new JPXStreamPrivate; } JPXStream::~JPXStream() { delete str; close(); delete priv; } void JPXStream::reset() { priv->counter = 0; priv->ccounter = 0; } void JPXStream::close() { if (priv->image != nullptr) { opj_image_destroy(priv->image); priv->image = nullptr; priv->npixels = 0; } } Goffset JPXStream::getPos() { return priv->counter * priv->ncomps + priv->ccounter; } int JPXStream::getChars(int nChars, unsigned char *buffer) { if (unlikely(priv->inited == false)) { init(); } for (int i = 0; i < nChars; ++i) { const int c = doGetChar(priv); if (likely(c != EOF)) { buffer[i] = c; } else { return i; } } return nChars; } int JPXStream::getChar() { if (unlikely(priv->inited == false)) { init(); } return doGetChar(priv); } int JPXStream::lookChar() { if (unlikely(priv->inited == false)) { init(); } return doLookChar(priv); } GooString *JPXStream::getPSFilter(int psLevel, const char *indent) { return nullptr; } bool JPXStream::isBinary(bool last) const { return str->isBinary(true); } void JPXStream::getImageParams(int *bitsPerComponent, StreamColorSpaceMode *csMode) { if (unlikely(priv->inited == false)) { init(); } *bitsPerComponent = 8; int numComps = (priv->image) ? priv->image->numcomps : 1; if (priv->image) { if (priv->image->color_space == OPJ_CLRSPC_SRGB && numComps == 4) { numComps = 3; } else if (priv->image->color_space == OPJ_CLRSPC_SYCC && numComps == 4) { numComps = 3; } else if (numComps == 2) { numComps = 1; } else if (numComps > 4) { numComps = 4; } } if (numComps == 3) { *csMode = streamCSDeviceRGB; } else if (numComps == 4) { *csMode = streamCSDeviceCMYK; } else { *csMode = streamCSDeviceGray; } } static void libopenjpeg_error_callback(const char *msg, void * /*client_data*/) { error(errSyntaxError, -1, "{0:s}", msg); } static void libopenjpeg_warning_callback(const char *msg, void * /*client_data*/) { error(errSyntaxWarning, -1, "{0:s}", msg); } typedef struct JPXData_s { const unsigned char *data; int size; OPJ_OFF_T pos; } JPXData; #define BUFFER_INITIAL_SIZE 4096 static OPJ_SIZE_T jpxRead_callback(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data) { JPXData *jpxData = (JPXData *)p_user_data; if (unlikely(jpxData->size <= jpxData->pos)) { return (OPJ_SIZE_T)-1; /* End of file! */ } OPJ_SIZE_T len = jpxData->size - jpxData->pos; if (len > p_nb_bytes) { len = p_nb_bytes; } memcpy(p_buffer, jpxData->data + jpxData->pos, len); jpxData->pos += len; return len; } static OPJ_OFF_T jpxSkip_callback(OPJ_OFF_T skip, void *p_user_data) { JPXData *jpxData = (JPXData *)p_user_data; jpxData->pos += (skip > jpxData->size - jpxData->pos) ? jpxData->size - jpxData->pos : skip; /* Always return input value to avoid "Problem with skipping JPEG2000 box, stream error" */ return skip; } static OPJ_BOOL jpxSeek_callback(OPJ_OFF_T seek_pos, void *p_user_data) { JPXData *jpxData = (JPXData *)p_user_data; if (seek_pos > jpxData->size) { return OPJ_FALSE; } jpxData->pos = seek_pos; return OPJ_TRUE; } void JPXStream::init() { Object oLen, cspace, smaskInDataObj; if (getDict()) { oLen = getDict()->lookup("Length"); cspace = getDict()->lookup("ColorSpace"); smaskInDataObj = getDict()->lookup("SMaskInData"); } int bufSize = BUFFER_INITIAL_SIZE; if (oLen.isInt() && oLen.getInt() > 0) { bufSize = oLen.getInt(); } bool indexed = false; if (cspace.isArray() && cspace.arrayGetLength() > 0) { const Object cstype = cspace.arrayGet(0); if (cstype.isName("Indexed")) { indexed = true; } } const int smaskInData = smaskInDataObj.isInt() ? smaskInDataObj.getInt() : 0; const std::vector buf = str->toUnsignedChars(bufSize); priv->init2(OPJ_CODEC_JP2, buf.data(), buf.size(), indexed); if (priv->image) { int numComps = priv->image->numcomps; int alpha = 0; if (priv->image->color_space == OPJ_CLRSPC_SRGB && numComps == 4) { numComps = 3; alpha = 1; } else if (priv->image->color_space == OPJ_CLRSPC_SYCC && numComps == 4) { numComps = 3; alpha = 1; } else if (numComps == 2) { numComps = 1; alpha = 1; } else if (numComps > 4) { numComps = 4; alpha = 1; } else { alpha = 0; } priv->npixels = priv->image->comps[0].w * priv->image->comps[0].h; priv->ncomps = priv->image->numcomps; if (alpha == 1 && smaskInData == 0) { priv->ncomps--; } for (int component = 0; component < priv->ncomps; component++) { if (priv->image->comps[component].data == nullptr) { close(); break; } const int componentPixels = priv->image->comps[component].w * priv->image->comps[component].h; if (componentPixels != priv->npixels) { error(errSyntaxWarning, -1, "Component {0:d} has different WxH than component 0", component); close(); break; } unsigned char *cdata = (unsigned char *)priv->image->comps[component].data; int adjust = 0; int depth = priv->image->comps[component].prec; if (priv->image->comps[component].prec > 8) { adjust = priv->image->comps[component].prec - 8; } int sgndcorr = 0; if (priv->image->comps[component].sgnd) { sgndcorr = 1 << (priv->image->comps[0].prec - 1); } for (int i = 0; i < priv->npixels; i++) { int r = priv->image->comps[component].data[i]; *(cdata++) = adjustComp(r, adjust, depth, sgndcorr, indexed); } } } else { priv->npixels = 0; } priv->counter = 0; priv->ccounter = 0; priv->inited = true; } void JPXStreamPrivate::init2(OPJ_CODEC_FORMAT format, const unsigned char *buf, int length, bool indexed) { JPXData jpxData; jpxData.data = buf; jpxData.pos = 0; jpxData.size = length; opj_stream_t *stream; stream = opj_stream_default_create(OPJ_TRUE); opj_stream_set_user_data(stream, &jpxData, nullptr); opj_stream_set_read_function(stream, jpxRead_callback); opj_stream_set_skip_function(stream, jpxSkip_callback); opj_stream_set_seek_function(stream, jpxSeek_callback); /* Set the length to avoid an assert */ opj_stream_set_user_data_length(stream, length); opj_codec_t *decoder; /* Use default decompression parameters */ opj_dparameters_t parameters; opj_set_default_decoder_parameters(¶meters); if (indexed) { parameters.flags |= OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG; } /* Get the decoder handle of the format */ decoder = opj_create_decompress(format); if (decoder == nullptr) { error(errSyntaxWarning, -1, "Unable to create decoder"); goto error; } /* Catch events using our callbacks */ opj_set_warning_handler(decoder, libopenjpeg_warning_callback, nullptr); opj_set_error_handler(decoder, libopenjpeg_error_callback, nullptr); /* Setup the decoder decoding parameters */ if (!opj_setup_decoder(decoder, ¶meters)) { error(errSyntaxWarning, -1, "Unable to set decoder parameters"); goto error; } /* Decode the stream and fill the image structure */ image = nullptr; if (!opj_read_header(stream, decoder, &image)) { error(errSyntaxWarning, -1, "Unable to read header"); goto error; } /* Optional if you want decode the entire image */ if (!opj_set_decode_area(decoder, image, parameters.DA_x0, parameters.DA_y0, parameters.DA_x1, parameters.DA_y1)) { error(errSyntaxWarning, -1, "X2"); goto error; } /* Get the decoded image */ if (!(opj_decode(decoder, stream, image) && opj_end_decompress(decoder, stream))) { error(errSyntaxWarning, -1, "Unable to decode image"); goto error; } opj_destroy_codec(decoder); opj_stream_destroy(stream); if (image != nullptr) { return; } error: if (image != nullptr) { opj_image_destroy(image); image = nullptr; } opj_stream_destroy(stream); opj_destroy_codec(decoder); if (format == OPJ_CODEC_JP2) { error(errSyntaxWarning, -1, "Did no succeed opening JPX Stream as JP2, trying as J2K."); init2(OPJ_CODEC_J2K, buf, length, indexed); } else if (format == OPJ_CODEC_J2K) { error(errSyntaxWarning, -1, "Did no succeed opening JPX Stream as J2K, trying as JPT."); init2(OPJ_CODEC_JPT, buf, length, indexed); } else { error(errSyntaxError, -1, "Did no succeed opening JPX Stream."); } } poppler-24.02.0/poppler/JPEG2000Stream.h000066400000000000000000000030201455701731300173760ustar00rootroot00000000000000//======================================================================== // // JPEG2000Stream.h // // A JPX stream decoder using OpenJPEG // // Copyright 2008, 2010, 2019, 2021 Albert Astals Cid // Copyright 2011 Daniel Glöckner // Copyright 2013, 2014 Adrian Johnson // Copyright 2015 Adam Reichold // // Licensed under GPLv2 or later // //======================================================================== #ifndef JPEG2000STREAM_H #define JPEG2000STREAM_H #include "config.h" #include "Object.h" #include "Stream.h" struct JPXStreamPrivate; class JPXStream : public FilterStream { public: explicit JPXStream(Stream *strA); ~JPXStream() override; JPXStream(const JPXStream &other) = delete; JPXStream &operator=(const JPXStream &other) = delete; StreamKind getKind() const override { return strJPX; } void reset() override; void close() override; Goffset getPos() override; int getChar() override; int lookChar() override; GooString *getPSFilter(int psLevel, const char *indent) override; bool isBinary(bool last = true) const override; void getImageParams(int *bitsPerComponent, StreamColorSpaceMode *csMode) override; int readStream(int nChars, unsigned char *buffer) { return str->doGetChars(nChars, buffer); } private: JPXStreamPrivate *priv; void init(); bool hasGetChars() override { return true; } int getChars(int nChars, unsigned char *buffer) override; }; #endif poppler-24.02.0/poppler/JPXStream.cc000066400000000000000000004037721455701731300171700ustar00rootroot00000000000000//======================================================================== // // JPXStream.cc // // Copyright 2002-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) 2008, 2012, 2021 Albert Astals Cid // Copyright (C) 2012 Thomas Freitag // Copyright (C) 2012 Even Rouault // Copyright (C) 2019 Robert Niemi // // 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 #include #include "gmem.h" #include "Error.h" #include "JArithmeticDecoder.h" #include "JPXStream.h" //~ to do: // - precincts // - ROI // - progression order changes // - packed packet headers // - support for palettes, channel maps, etc. // - make sure all needed JP2/JPX subboxes are parsed (readBoxes) // - can we assume that QCC segments must come after the QCD segment? // - handle tilePartToEOC in readTilePartData // - progression orders 2, 3, and 4 // - in coefficient decoding (readCodeBlockData): // - selective arithmetic coding bypass // (this also affects reading the cb->dataLen array) // - coeffs longer than 31 bits (should just ignore the extra bits?) // - handle boxes larger than 2^32 bytes // - the fixed-point arithmetic won't handle 16-bit pixels //------------------------------------------------------------------------ // number of contexts for the arithmetic decoder #define jpxNContexts 19 #define jpxContextSigProp 0 // 0 - 8: significance prop and cleanup #define jpxContextSign 9 // 9 - 13: sign #define jpxContextMagRef 14 // 14 -16: magnitude refinement #define jpxContextRunLength 17 // cleanup: run length #define jpxContextUniform 18 // cleanup: first signif coeff //------------------------------------------------------------------------ #define jpxPassSigProp 0 #define jpxPassMagRef 1 #define jpxPassCleanup 2 //------------------------------------------------------------------------ // arithmetic decoder context for the significance propagation and // cleanup passes: // [horiz][vert][diag][subband] // where subband = 0 for HL // = 1 for LH and LL // = 2 for HH static const unsigned int sigPropContext[3][3][5][3] = { { { { 0, 0, 0 }, // horiz=0, vert=0, diag=0 { 1, 1, 3 }, // horiz=0, vert=0, diag=1 { 2, 2, 6 }, // horiz=0, vert=0, diag=2 { 2, 2, 8 }, // horiz=0, vert=0, diag=3 { 2, 2, 8 } }, // horiz=0, vert=0, diag=4 { { 5, 3, 1 }, // horiz=0, vert=1, diag=0 { 6, 3, 4 }, // horiz=0, vert=1, diag=1 { 6, 3, 7 }, // horiz=0, vert=1, diag=2 { 6, 3, 8 }, // horiz=0, vert=1, diag=3 { 6, 3, 8 } }, // horiz=0, vert=1, diag=4 { { 8, 4, 2 }, // horiz=0, vert=2, diag=0 { 8, 4, 5 }, // horiz=0, vert=2, diag=1 { 8, 4, 7 }, // horiz=0, vert=2, diag=2 { 8, 4, 8 }, // horiz=0, vert=2, diag=3 { 8, 4, 8 } } }, // horiz=0, vert=2, diag=4 { { { 3, 5, 1 }, // horiz=1, vert=0, diag=0 { 3, 6, 4 }, // horiz=1, vert=0, diag=1 { 3, 6, 7 }, // horiz=1, vert=0, diag=2 { 3, 6, 8 }, // horiz=1, vert=0, diag=3 { 3, 6, 8 } }, // horiz=1, vert=0, diag=4 { { 7, 7, 2 }, // horiz=1, vert=1, diag=0 { 7, 7, 5 }, // horiz=1, vert=1, diag=1 { 7, 7, 7 }, // horiz=1, vert=1, diag=2 { 7, 7, 8 }, // horiz=1, vert=1, diag=3 { 7, 7, 8 } }, // horiz=1, vert=1, diag=4 { { 8, 7, 2 }, // horiz=1, vert=2, diag=0 { 8, 7, 5 }, // horiz=1, vert=2, diag=1 { 8, 7, 7 }, // horiz=1, vert=2, diag=2 { 8, 7, 8 }, // horiz=1, vert=2, diag=3 { 8, 7, 8 } } }, // horiz=1, vert=2, diag=4 { { { 4, 8, 2 }, // horiz=2, vert=0, diag=0 { 4, 8, 5 }, // horiz=2, vert=0, diag=1 { 4, 8, 7 }, // horiz=2, vert=0, diag=2 { 4, 8, 8 }, // horiz=2, vert=0, diag=3 { 4, 8, 8 } }, // horiz=2, vert=0, diag=4 { { 7, 8, 2 }, // horiz=2, vert=1, diag=0 { 7, 8, 5 }, // horiz=2, vert=1, diag=1 { 7, 8, 7 }, // horiz=2, vert=1, diag=2 { 7, 8, 8 }, // horiz=2, vert=1, diag=3 { 7, 8, 8 } }, // horiz=2, vert=1, diag=4 { { 8, 8, 2 }, // horiz=2, vert=2, diag=0 { 8, 8, 5 }, // horiz=2, vert=2, diag=1 { 8, 8, 7 }, // horiz=2, vert=2, diag=2 { 8, 8, 8 }, // horiz=2, vert=2, diag=3 { 8, 8, 8 } } } // horiz=2, vert=2, diag=4 }; // arithmetic decoder context and xor bit for the sign bit in the // significance propagation pass: // [horiz][vert][k] // where horiz/vert are offset by 2 (i.e., range is -2 .. 2) // and k = 0 for the context // = 1 for the xor bit static const unsigned int signContext[5][5][2] = { { { 13, 1 }, // horiz=-2, vert=-2 { 13, 1 }, // horiz=-2, vert=-1 { 12, 1 }, // horiz=-2, vert= 0 { 11, 1 }, // horiz=-2, vert=+1 { 11, 1 } }, // horiz=-2, vert=+2 { { 13, 1 }, // horiz=-1, vert=-2 { 13, 1 }, // horiz=-1, vert=-1 { 12, 1 }, // horiz=-1, vert= 0 { 11, 1 }, // horiz=-1, vert=+1 { 11, 1 } }, // horiz=-1, vert=+2 { { 10, 1 }, // horiz= 0, vert=-2 { 10, 1 }, // horiz= 0, vert=-1 { 9, 0 }, // horiz= 0, vert= 0 { 10, 0 }, // horiz= 0, vert=+1 { 10, 0 } }, // horiz= 0, vert=+2 { { 11, 0 }, // horiz=+1, vert=-2 { 11, 0 }, // horiz=+1, vert=-1 { 12, 0 }, // horiz=+1, vert= 0 { 13, 0 }, // horiz=+1, vert=+1 { 13, 0 } }, // horiz=+1, vert=+2 { { 11, 0 }, // horiz=+2, vert=-2 { 11, 0 }, // horiz=+2, vert=-1 { 12, 0 }, // horiz=+2, vert= 0 { 13, 0 }, // horiz=+2, vert=+1 { 13, 0 } }, // horiz=+2, vert=+2 }; //------------------------------------------------------------------------ // constants used in the IDWT #define idwtAlpha -1.586134342059924 #define idwtBeta -0.052980118572961 #define idwtGamma 0.882911075530934 #define idwtDelta 0.443506852043971 #define idwtKappa 1.230174104914001 #define idwtIKappa (1.0 / idwtKappa) // number of bits to the right of the decimal point for the fixed // point arithmetic used in the IDWT #define fracBits 16 //------------------------------------------------------------------------ // floor(x / y) #define jpxFloorDiv(x, y) ((x) / (y)) // floor(x / 2^y) #define jpxFloorDivPow2(x, y) ((x) >> (y)) // ceil(x / y) #define jpxCeilDiv(x, y) (((x) + (y)-1) / (y)) // ceil(x / 2^y) #define jpxCeilDivPow2(x, y) (((x) + (1 << (y)) - 1) >> (y)) //------------------------------------------------------------------------ #if 1 //----- disable coverage tracking # define cover(idx) #else //----- enable coverage tracking class JPXCover { public: JPXCover(int sizeA); ~JPXCover(); void incr(int idx); private: int size, used; int *data; }; JPXCover::JPXCover(int sizeA) { size = sizeA; used = -1; data = (int *)gmallocn(size, sizeof(int)); memset(data, 0, size * sizeof(int)); } JPXCover::~JPXCover() { int i; printf("JPX coverage:\n"); for (i = 0; i <= used; ++i) { printf(" %4d: %8d\n", i, data[i]); } gfree(data); } void JPXCover::incr(int idx) { if (idx < size) { ++data[idx]; if (idx > used) { used = idx; } } } JPXCover jpxCover(150); # define cover(idx) jpxCover.incr(idx) #endif //----- coverage tracking //------------------------------------------------------------------------ JPXStream::JPXStream(Stream *strA) : FilterStream(strA) { bufStr = new BufStream(str, 2); nComps = 0; bpc = nullptr; width = height = 0; haveCS = false; havePalette = false; haveCompMap = false; haveChannelDefn = false; img.tiles = nullptr; bitBuf = 0; bitBufLen = 0; bitBufSkip = false; byteCount = 0; curX = curY = 0; curComp = 0; readBufLen = 0; } JPXStream::~JPXStream() { close(); delete bufStr; } void JPXStream::reset() { bufStr->reset(); if (readBoxes()) { curY = img.yOffset; } else { // readBoxes reported an error, so we go immediately to EOF curY = img.ySize; } curX = img.xOffset; curComp = 0; readBufLen = 0; } void JPXStream::close() { JPXTile *tile; JPXTileComp *tileComp; JPXResLevel *resLevel; JPXPrecinct *precinct; JPXSubband *subband; JPXCodeBlock *cb; unsigned int comp, i, k, r, pre, sb; gfree(bpc); bpc = nullptr; if (havePalette) { gfree(palette.bpc); gfree(palette.c); havePalette = false; } if (haveCompMap) { gfree(compMap.comp); gfree(compMap.type); gfree(compMap.pComp); haveCompMap = false; } if (haveChannelDefn) { gfree(channelDefn.idx); gfree(channelDefn.type); gfree(channelDefn.assoc); haveChannelDefn = false; } if (img.tiles) { for (i = 0; i < img.nXTiles * img.nYTiles; ++i) { tile = &img.tiles[i]; if (tile->tileComps) { for (comp = 0; comp < img.nComps; ++comp) { tileComp = &tile->tileComps[comp]; gfree(tileComp->quantSteps); gfree(tileComp->data); gfree(tileComp->buf); if (tileComp->resLevels) { for (r = 0; r <= tileComp->nDecompLevels; ++r) { resLevel = &tileComp->resLevels[r]; if (resLevel->precincts) { for (pre = 0; pre < 1; ++pre) { precinct = &resLevel->precincts[pre]; if (precinct->subbands) { for (sb = 0; sb < (unsigned int)(r == 0 ? 1 : 3); ++sb) { subband = &precinct->subbands[sb]; gfree(subband->inclusion); gfree(subband->zeroBitPlane); if (subband->cbs) { for (k = 0; k < subband->nXCBs * subband->nYCBs; ++k) { cb = &subband->cbs[k]; gfree(cb->dataLen); gfree(cb->touched); if (cb->arithDecoder) { delete cb->arithDecoder; } if (cb->stats) { delete cb->stats; } } gfree(subband->cbs); } } gfree(precinct->subbands); } } gfree(img.tiles[i].tileComps[comp].resLevels[r].precincts); } } gfree(img.tiles[i].tileComps[comp].resLevels); } } gfree(img.tiles[i].tileComps); } } gfree(img.tiles); img.tiles = nullptr; } bufStr->close(); } int JPXStream::getChar() { int c; if (readBufLen < 8) { fillReadBuf(); } if (readBufLen == 8) { c = readBuf & 0xff; readBufLen = 0; } else if (readBufLen > 8) { c = (readBuf >> (readBufLen - 8)) & 0xff; readBufLen -= 8; } else if (readBufLen == 0) { c = EOF; } else { c = (readBuf << (8 - readBufLen)) & 0xff; readBufLen = 0; } return c; } int JPXStream::lookChar() { int c; if (readBufLen < 8) { fillReadBuf(); } if (readBufLen == 8) { c = readBuf & 0xff; } else if (readBufLen > 8) { c = (readBuf >> (readBufLen - 8)) & 0xff; } else if (readBufLen == 0) { c = EOF; } else { c = (readBuf << (8 - readBufLen)) & 0xff; } return c; } void JPXStream::fillReadBuf() { JPXTileComp *tileComp; unsigned int tileIdx, tx, ty; int pix, pixBits; do { if (curY >= img.ySize) { return; } tileIdx = ((curY - img.yTileOffset) / img.yTileSize) * img.nXTiles + (curX - img.xTileOffset) / img.xTileSize; #if 1 //~ ignore the palette, assume the PDF ColorSpace object is valid if (img.tiles == nullptr || tileIdx >= img.nXTiles * img.nYTiles || img.tiles[tileIdx].tileComps == nullptr) { error(errSyntaxError, getPos(), "Unexpected tileIdx in fillReadBuf in JPX stream"); return; } tileComp = &img.tiles[tileIdx].tileComps[curComp]; #else tileComp = &img.tiles[tileIdx].tileComps[havePalette ? 0 : curComp]; #endif tx = jpxCeilDiv((curX - img.xTileOffset) % img.xTileSize, tileComp->hSep); ty = jpxCeilDiv((curY - img.yTileOffset) % img.yTileSize, tileComp->vSep); if (unlikely(ty >= (tileComp->y1 - tileComp->y0))) { error(errSyntaxError, getPos(), "Unexpected ty in fillReadBuf in JPX stream"); return; } if (unlikely(tx >= (tileComp->x1 - tileComp->x0))) { error(errSyntaxError, getPos(), "Unexpected tx in fillReadBuf in JPX stream"); return; } pix = (int)tileComp->data[ty * (tileComp->x1 - tileComp->x0) + tx]; pixBits = tileComp->prec; #if 1 //~ ignore the palette, assume the PDF ColorSpace object is valid if (++curComp == img.nComps) { #else if (havePalette) { if (pix >= 0 && pix < palette.nEntries) { pix = palette.c[pix * palette.nComps + curComp]; } else { pix = 0; } pixBits = palette.bpc[curComp]; } if (++curComp == (unsigned int)(havePalette ? palette.nComps : img.nComps)) { #endif curComp = 0; if (++curX == img.xSize) { curX = img.xOffset; ++curY; if (pixBits < 8) { pix <<= 8 - pixBits; pixBits = 8; } } } if (pixBits == 8) { readBuf = (readBuf << 8) | (pix & 0xff); } else { readBuf = (readBuf << pixBits) | (pix & ((1 << pixBits) - 1)); } readBufLen += pixBits; } while (readBufLen < 8); } GooString *JPXStream::getPSFilter(int psLevel, const char *indent) { return nullptr; } bool JPXStream::isBinary(bool last) const { return str->isBinary(true); } void JPXStream::getImageParams(int *bitsPerComponent, StreamColorSpaceMode *csMode) { unsigned int boxType, boxLen, dataLen, csEnum; unsigned int bpc1, dummy, i; int csMeth, csPrec, csPrec1, dummy2; StreamColorSpaceMode csMode1; bool haveBPC, haveCSMode; csPrec = 0; // make gcc happy haveBPC = haveCSMode = false; bufStr->reset(); if (bufStr->lookChar() == 0xff) { getImageParams2(bitsPerComponent, csMode); } else { while (readBoxHdr(&boxType, &boxLen, &dataLen)) { if (boxType == 0x6a703268) { // JP2 header cover(0); // skip the superbox } else if (boxType == 0x69686472) { // image header cover(1); if (readULong(&dummy) && readULong(&dummy) && readUWord(&dummy) && readUByte(&bpc1) && readUByte(&dummy) && readUByte(&dummy) && readUByte(&dummy)) { *bitsPerComponent = bpc1 + 1; haveBPC = true; } } else if (boxType == 0x636F6C72) { // color specification cover(2); if (readByte(&csMeth) && readByte(&csPrec1) && readByte(&dummy2)) { if (csMeth == 1) { if (readULong(&csEnum)) { csMode1 = streamCSNone; if (csEnum == jpxCSBiLevel || csEnum == jpxCSGrayscale) { csMode1 = streamCSDeviceGray; } else if (csEnum == jpxCSCMYK) { csMode1 = streamCSDeviceCMYK; } else if (csEnum == jpxCSsRGB || csEnum == jpxCSCISesRGB || csEnum == jpxCSROMMRGB) { csMode1 = streamCSDeviceRGB; } if (csMode1 != streamCSNone && (!haveCSMode || csPrec1 > csPrec)) { *csMode = csMode1; csPrec = csPrec1; haveCSMode = true; } if (dataLen >= 7) { for (i = 0; i < dataLen - 7; ++i) { if (bufStr->getChar() == EOF) break; } } } } else { if (dataLen >= 3) { for (i = 0; i < dataLen - 3; ++i) { if (bufStr->getChar() == EOF) break; } } } } } else if (boxType == 0x6A703263) { // codestream cover(3); if (!(haveBPC && haveCSMode)) { getImageParams2(bitsPerComponent, csMode); } break; } else { cover(4); for (i = 0; i < dataLen; ++i) { if (unlikely(bufStr->getChar() == EOF)) { error(errSyntaxError, getPos(), "Unexpected EOF in getImageParams in JPX stream"); break; } } } } } bufStr->close(); } // Get image parameters from the codestream. void JPXStream::getImageParams2(int *bitsPerComponent, StreamColorSpaceMode *csMode) { int segType; unsigned int segLen, nComps1, bpc1, dummy, i; while (readMarkerHdr(&segType, &segLen)) { if (segType == 0x51) { // SIZ - image and tile size cover(5); if (readUWord(&dummy) && readULong(&dummy) && readULong(&dummy) && readULong(&dummy) && readULong(&dummy) && readULong(&dummy) && readULong(&dummy) && readULong(&dummy) && readULong(&dummy) && readUWord(&nComps1) && readUByte(&bpc1)) { *bitsPerComponent = (bpc1 & 0x7f) + 1; // if there's no color space info, take a guess if (nComps1 == 1) { *csMode = streamCSDeviceGray; } else if (nComps1 == 3) { *csMode = streamCSDeviceRGB; } else if (nComps1 == 4) { *csMode = streamCSDeviceCMYK; } } break; } else { cover(6); if (segLen > 2) { for (i = 0; i < segLen - 2; ++i) { bufStr->getChar(); } } } } } bool JPXStream::readBoxes() { unsigned int boxType, boxLen, dataLen; unsigned int bpc1, compression, unknownColorspace, ipr; unsigned int i, j; haveImgHdr = false; // initialize in case there is a parse error img.xSize = img.ySize = 0; img.xOffset = img.yOffset = 0; img.xTileSize = img.yTileSize = 0; img.xTileOffset = img.yTileOffset = 0; img.nComps = 0; // check for a naked JPEG 2000 codestream (without the JP2/JPX // wrapper) -- this appears to be a violation of the PDF spec, but // Acrobat allows it if (bufStr->lookChar() == 0xff) { cover(7); error(errSyntaxWarning, getPos(), "Naked JPEG 2000 codestream, missing JP2/JPX wrapper"); if (!readCodestream(0)) { return false; } nComps = img.nComps; bpc = (unsigned int *)gmallocn(nComps, sizeof(unsigned int)); for (i = 0; i < nComps; ++i) { bpc[i] = img.tiles[0].tileComps[i].prec; } width = img.xSize - img.xOffset; height = img.ySize - img.yOffset; return true; } while (readBoxHdr(&boxType, &boxLen, &dataLen)) { switch (boxType) { case 0x6a703268: // JP2 header // this is a grouping box ('superbox') which has no real // contents and doesn't appear to be used consistently, i.e., // some things which should be subboxes of the JP2 header box // show up outside of it - so we simply ignore the JP2 header // box cover(8); break; case 0x69686472: // image header cover(9); if (!readULong(&height) || !readULong(&width) || !readUWord(&nComps) || !readUByte(&bpc1) || !readUByte(&compression) || !readUByte(&unknownColorspace) || !readUByte(&ipr)) { error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream"); return false; } if (compression != 7) { error(errSyntaxError, getPos(), "Unknown compression type in JPX stream"); return false; } bpc = (unsigned int *)gmallocn(nComps, sizeof(unsigned int)); for (i = 0; i < nComps; ++i) { bpc[i] = bpc1; } haveImgHdr = true; break; case 0x62706363: // bits per component cover(10); if (!haveImgHdr) { error(errSyntaxError, getPos(), "Found bits per component box before image header box in JPX stream"); return false; } if (dataLen != nComps) { error(errSyntaxError, getPos(), "Invalid bits per component box in JPX stream"); return false; } for (i = 0; i < nComps; ++i) { if (!readUByte(&bpc[i])) { error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream"); return false; } } break; case 0x636F6C72: // color specification cover(11); if (!readColorSpecBox(dataLen)) { return false; } break; case 0x70636c72: // palette cover(12); if (!readUWord(&palette.nEntries) || !readUByte(&palette.nComps)) { error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream"); return false; } palette.bpc = (unsigned int *)gmallocn(palette.nComps, sizeof(unsigned int)); palette.c = (int *)gmallocn(palette.nEntries * palette.nComps, sizeof(int)); for (i = 0; i < palette.nComps; ++i) { if (!readUByte(&palette.bpc[i])) { error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream"); return false; } ++palette.bpc[i]; } for (i = 0; i < palette.nEntries; ++i) { for (j = 0; j < palette.nComps; ++j) { if (!readNBytes(((palette.bpc[j] & 0x7f) + 7) >> 3, (palette.bpc[j] & 0x80) ? true : false, &palette.c[i * palette.nComps + j])) { error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream"); return false; } } } havePalette = true; break; case 0x636d6170: // component mapping cover(13); compMap.nChannels = dataLen / 4; compMap.comp = (unsigned int *)gmallocn(compMap.nChannels, sizeof(unsigned int)); compMap.type = (unsigned int *)gmallocn(compMap.nChannels, sizeof(unsigned int)); compMap.pComp = (unsigned int *)gmallocn(compMap.nChannels, sizeof(unsigned int)); for (i = 0; i < compMap.nChannels; ++i) { if (!readUWord(&compMap.comp[i]) || !readUByte(&compMap.type[i]) || !readUByte(&compMap.pComp[i])) { error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream"); return false; } } haveCompMap = true; break; case 0x63646566: // channel definition cover(14); if (!readUWord(&channelDefn.nChannels)) { error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream"); return false; } channelDefn.idx = (unsigned int *)gmallocn(channelDefn.nChannels, sizeof(unsigned int)); channelDefn.type = (unsigned int *)gmallocn(channelDefn.nChannels, sizeof(unsigned int)); channelDefn.assoc = (unsigned int *)gmallocn(channelDefn.nChannels, sizeof(unsigned int)); for (i = 0; i < channelDefn.nChannels; ++i) { if (!readUWord(&channelDefn.idx[i]) || !readUWord(&channelDefn.type[i]) || !readUWord(&channelDefn.assoc[i])) { error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream"); return false; } } haveChannelDefn = true; break; case 0x6A703263: // contiguous codestream cover(15); if (!bpc) { error(errSyntaxError, getPos(), "JPX stream is missing the image header box"); } if (!haveCS) { error(errSyntaxError, getPos(), "JPX stream has no supported color spec"); } if (!readCodestream(dataLen)) { return false; } break; default: cover(16); for (i = 0; i < dataLen; ++i) { if (bufStr->getChar() == EOF) { error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream"); return false; } } break; } } return true; } bool JPXStream::readColorSpecBox(unsigned int dataLen) { JPXColorSpec newCS; unsigned int csApprox, csEnum; unsigned int i; bool ok; ok = false; if (!readUByte(&newCS.meth) || !readByte(&newCS.prec) || !readUByte(&csApprox)) { goto err; } switch (newCS.meth) { case 1: // enumerated colorspace cover(17); if (!readULong(&csEnum)) { goto err; } newCS.enumerated.type = (JPXColorSpaceType)csEnum; switch (newCS.enumerated.type) { case jpxCSBiLevel: ok = true; break; case jpxCSYCbCr1: ok = true; break; case jpxCSYCbCr2: ok = true; break; case jpxCSYCBCr3: ok = true; break; case jpxCSPhotoYCC: ok = true; break; case jpxCSCMY: ok = true; break; case jpxCSCMYK: ok = true; break; case jpxCSYCCK: ok = true; break; case jpxCSCIELab: if (dataLen == 7 + 7 * 4) { if (!readULong(&newCS.enumerated.cieLab.rl) || !readULong(&newCS.enumerated.cieLab.ol) || !readULong(&newCS.enumerated.cieLab.ra) || !readULong(&newCS.enumerated.cieLab.oa) || !readULong(&newCS.enumerated.cieLab.rb) || !readULong(&newCS.enumerated.cieLab.ob) || !readULong(&newCS.enumerated.cieLab.il)) { goto err; } } else if (dataLen == 7) { //~ this assumes the 8-bit case cover(92); newCS.enumerated.cieLab.rl = 100; newCS.enumerated.cieLab.ol = 0; newCS.enumerated.cieLab.ra = 255; newCS.enumerated.cieLab.oa = 128; newCS.enumerated.cieLab.rb = 255; newCS.enumerated.cieLab.ob = 96; newCS.enumerated.cieLab.il = 0x00443530; } else { goto err; } ok = true; break; case jpxCSsRGB: ok = true; break; case jpxCSGrayscale: ok = true; break; case jpxCSBiLevel2: ok = true; break; case jpxCSCIEJab: // not allowed in PDF goto err; case jpxCSCISesRGB: ok = true; break; case jpxCSROMMRGB: ok = true; break; case jpxCSsRGBYCbCr: ok = true; break; case jpxCSYPbPr1125: ok = true; break; case jpxCSYPbPr1250: ok = true; break; default: goto err; } break; case 2: // restricted ICC profile case 3: // any ICC profile (JPX) case 4: // vendor color (JPX) cover(18); for (i = 0; i < dataLen - 3; ++i) { if (bufStr->getChar() == EOF) { goto err; } } break; } if (ok && (!haveCS || newCS.prec > cs.prec)) { cs = newCS; haveCS = true; } return true; err: error(errSyntaxError, getPos(), "Error in JPX color spec"); return false; } bool JPXStream::readCodestream(unsigned int len) { JPXTile *tile; JPXTileComp *tileComp; int segType; bool haveSIZ, haveCOD, haveQCD, haveSOT; unsigned int precinctSize, style, nDecompLevels; unsigned int segLen, capabilities, comp, i, j, r; //----- main header haveSIZ = haveCOD = haveQCD = haveSOT = false; do { if (!readMarkerHdr(&segType, &segLen)) { error(errSyntaxError, getPos(), "Error in JPX codestream"); return false; } switch (segType) { case 0x4f: // SOC - start of codestream // marker only cover(19); break; case 0x51: // SIZ - image and tile size cover(20); if (haveSIZ) { error(errSyntaxError, getPos(), "Duplicate SIZ marker segment in JPX stream"); return false; } if (!readUWord(&capabilities) || !readULong(&img.xSize) || !readULong(&img.ySize) || !readULong(&img.xOffset) || !readULong(&img.yOffset) || !readULong(&img.xTileSize) || !readULong(&img.yTileSize) || !readULong(&img.xTileOffset) || !readULong(&img.yTileOffset) || !readUWord(&img.nComps)) { error(errSyntaxError, getPos(), "Error in JPX SIZ marker segment"); return false; } if (haveImgHdr && img.nComps != nComps) { error(errSyntaxError, getPos(), "Different number of components in JPX SIZ marker segment"); return false; } if (img.xSize == 0 || img.ySize == 0 || img.xOffset >= img.xSize || img.yOffset >= img.ySize || img.xTileSize == 0 || img.yTileSize == 0 || img.xTileOffset > img.xOffset || img.yTileOffset > img.yOffset || img.xTileSize + img.xTileOffset <= img.xOffset || img.yTileSize + img.yTileOffset <= img.yOffset) { error(errSyntaxError, getPos(), "Error in JPX SIZ marker segment"); return false; } img.nXTiles = (img.xSize - img.xTileOffset + img.xTileSize - 1) / img.xTileSize; img.nYTiles = (img.ySize - img.yTileOffset + img.yTileSize - 1) / img.yTileSize; // check for overflow before allocating memory if (img.nXTiles <= 0 || img.nYTiles <= 0 || img.nXTiles >= 65535 / img.nYTiles) { error(errSyntaxError, getPos(), "Bad tile count in JPX SIZ marker segment"); return false; } img.tiles = (JPXTile *)gmallocn(img.nXTiles * img.nYTiles, sizeof(JPXTile)); for (i = 0; i < img.nXTiles * img.nYTiles; ++i) { img.tiles[i].init = false; img.tiles[i].tileComps = (JPXTileComp *)gmallocn(img.nComps, sizeof(JPXTileComp)); for (comp = 0; comp < img.nComps; ++comp) { img.tiles[i].tileComps[comp].quantSteps = nullptr; img.tiles[i].tileComps[comp].data = nullptr; img.tiles[i].tileComps[comp].buf = nullptr; img.tiles[i].tileComps[comp].resLevels = nullptr; } } for (comp = 0; comp < img.nComps; ++comp) { if (!readUByte(&img.tiles[0].tileComps[comp].prec) || !readUByte(&img.tiles[0].tileComps[comp].hSep) || !readUByte(&img.tiles[0].tileComps[comp].vSep)) { error(errSyntaxError, getPos(), "Error in JPX SIZ marker segment"); return false; } if (img.tiles[0].tileComps[comp].hSep == 0 || img.tiles[0].tileComps[comp].vSep == 0) { error(errSyntaxError, getPos(), "Error in JPX SIZ marker segment"); return false; } img.tiles[0].tileComps[comp].sgned = (img.tiles[0].tileComps[comp].prec & 0x80) ? true : false; img.tiles[0].tileComps[comp].prec = (img.tiles[0].tileComps[comp].prec & 0x7f) + 1; for (i = 1; i < img.nXTiles * img.nYTiles; ++i) { img.tiles[i].tileComps[comp] = img.tiles[0].tileComps[comp]; } } haveSIZ = true; break; case 0x52: // COD - coding style default cover(21); if (!haveSIZ) { error(errSyntaxError, getPos(), "JPX COD marker segment before SIZ segment"); return false; } if (img.tiles == nullptr || img.nXTiles * img.nYTiles == 0 || img.tiles[0].tileComps == nullptr) { error(errSyntaxError, getPos(), "Error in JPX COD marker segment"); return false; } if (!readUByte(&img.tiles[0].tileComps[0].style) || !readUByte(&img.tiles[0].progOrder) || !readUWord(&img.tiles[0].nLayers) || !readUByte(&img.tiles[0].multiComp) || !readUByte(&nDecompLevels) || !readUByte(&img.tiles[0].tileComps[0].codeBlockW) || !readUByte(&img.tiles[0].tileComps[0].codeBlockH) || !readUByte(&img.tiles[0].tileComps[0].codeBlockStyle) || !readUByte(&img.tiles[0].tileComps[0].transform)) { error(errSyntaxError, getPos(), "Error in JPX COD marker segment"); return false; } if (nDecompLevels > 32 || img.tiles[0].tileComps[0].codeBlockW > 8 || img.tiles[0].tileComps[0].codeBlockH > 8) { error(errSyntaxError, getPos(), "Error in JPX COD marker segment"); return false; } img.tiles[0].tileComps[0].nDecompLevels = nDecompLevels; img.tiles[0].tileComps[0].codeBlockW += 2; img.tiles[0].tileComps[0].codeBlockH += 2; for (i = 0; i < img.nXTiles * img.nYTiles; ++i) { if (i != 0) { img.tiles[i].progOrder = img.tiles[0].progOrder; img.tiles[i].nLayers = img.tiles[0].nLayers; img.tiles[i].multiComp = img.tiles[0].multiComp; } for (comp = 0; comp < img.nComps; ++comp) { if (!(i == 0 && comp == 0)) { img.tiles[i].tileComps[comp].style = img.tiles[0].tileComps[0].style; img.tiles[i].tileComps[comp].nDecompLevels = img.tiles[0].tileComps[0].nDecompLevels; img.tiles[i].tileComps[comp].codeBlockW = img.tiles[0].tileComps[0].codeBlockW; img.tiles[i].tileComps[comp].codeBlockH = img.tiles[0].tileComps[0].codeBlockH; img.tiles[i].tileComps[comp].codeBlockStyle = img.tiles[0].tileComps[0].codeBlockStyle; img.tiles[i].tileComps[comp].transform = img.tiles[0].tileComps[0].transform; } img.tiles[i].tileComps[comp].resLevels = (JPXResLevel *)gmallocn_checkoverflow((img.tiles[i].tileComps[comp].nDecompLevels + 1), sizeof(JPXResLevel)); if (img.tiles[i].tileComps[comp].resLevels == nullptr) { error(errSyntaxError, getPos(), "Error in JPX COD marker segment"); return false; } for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) { img.tiles[i].tileComps[comp].resLevels[r].precincts = nullptr; } } } for (r = 0; r <= img.tiles[0].tileComps[0].nDecompLevels; ++r) { if (img.tiles[0].tileComps[0].style & 0x01) { cover(91); if (!readUByte(&precinctSize)) { error(errSyntaxError, getPos(), "Error in JPX COD marker segment"); return false; } img.tiles[0].tileComps[0].resLevels[r].precinctWidth = precinctSize & 0x0f; img.tiles[0].tileComps[0].resLevels[r].precinctHeight = (precinctSize >> 4) & 0x0f; } else { img.tiles[0].tileComps[0].resLevels[r].precinctWidth = 15; img.tiles[0].tileComps[0].resLevels[r].precinctHeight = 15; } } for (i = 0; i < img.nXTiles * img.nYTiles; ++i) { for (comp = 0; comp < img.nComps; ++comp) { if (!(i == 0 && comp == 0)) { for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) { img.tiles[i].tileComps[comp].resLevels[r].precinctWidth = img.tiles[0].tileComps[0].resLevels[r].precinctWidth; img.tiles[i].tileComps[comp].resLevels[r].precinctHeight = img.tiles[0].tileComps[0].resLevels[r].precinctHeight; } } } } haveCOD = true; break; case 0x53: // COC - coding style component cover(22); if (!haveCOD) { error(errSyntaxError, getPos(), "JPX COC marker segment before COD segment"); return false; } if ((img.nComps > 256 && !readUWord(&comp)) || (img.nComps <= 256 && !readUByte(&comp)) || comp >= img.nComps || !readUByte(&style) || !readUByte(&nDecompLevels) || !readUByte(&img.tiles[0].tileComps[comp].codeBlockW) || !readUByte(&img.tiles[0].tileComps[comp].codeBlockH) || !readUByte(&img.tiles[0].tileComps[comp].codeBlockStyle) || !readUByte(&img.tiles[0].tileComps[comp].transform)) { error(errSyntaxError, getPos(), "Error in JPX COC marker segment"); return false; } if (nDecompLevels > 32 || img.tiles[0].tileComps[comp].codeBlockW > 8 || img.tiles[0].tileComps[comp].codeBlockH > 8) { error(errSyntaxError, getPos(), "Error in JPX COC marker segment"); return false; } img.tiles[0].tileComps[comp].nDecompLevels = nDecompLevels; img.tiles[0].tileComps[comp].style = (img.tiles[0].tileComps[comp].style & ~1) | (style & 1); img.tiles[0].tileComps[comp].codeBlockW += 2; img.tiles[0].tileComps[comp].codeBlockH += 2; for (i = 0; i < img.nXTiles * img.nYTiles; ++i) { if (i != 0) { img.tiles[i].tileComps[comp].style = img.tiles[0].tileComps[comp].style; img.tiles[i].tileComps[comp].nDecompLevels = img.tiles[0].tileComps[comp].nDecompLevels; img.tiles[i].tileComps[comp].codeBlockW = img.tiles[0].tileComps[comp].codeBlockW; img.tiles[i].tileComps[comp].codeBlockH = img.tiles[0].tileComps[comp].codeBlockH; img.tiles[i].tileComps[comp].codeBlockStyle = img.tiles[0].tileComps[comp].codeBlockStyle; img.tiles[i].tileComps[comp].transform = img.tiles[0].tileComps[comp].transform; } img.tiles[i].tileComps[comp].resLevels = (JPXResLevel *)greallocn(img.tiles[i].tileComps[comp].resLevels, (img.tiles[i].tileComps[comp].nDecompLevels + 1), sizeof(JPXResLevel)); for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) { img.tiles[i].tileComps[comp].resLevels[r].precincts = nullptr; } } for (r = 0; r <= img.tiles[0].tileComps[comp].nDecompLevels; ++r) { if (img.tiles[0].tileComps[comp].style & 0x01) { if (!readUByte(&precinctSize)) { error(errSyntaxError, getPos(), "Error in JPX COD marker segment"); return false; } img.tiles[0].tileComps[comp].resLevels[r].precinctWidth = precinctSize & 0x0f; img.tiles[0].tileComps[comp].resLevels[r].precinctHeight = (precinctSize >> 4) & 0x0f; } else { img.tiles[0].tileComps[comp].resLevels[r].precinctWidth = 15; img.tiles[0].tileComps[comp].resLevels[r].precinctHeight = 15; } } for (i = 1; i < img.nXTiles * img.nYTiles; ++i) { for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) { img.tiles[i].tileComps[comp].resLevels[r].precinctWidth = img.tiles[0].tileComps[comp].resLevels[r].precinctWidth; img.tiles[i].tileComps[comp].resLevels[r].precinctHeight = img.tiles[0].tileComps[comp].resLevels[r].precinctHeight; } } break; case 0x5c: // QCD - quantization default cover(23); if (!haveSIZ) { error(errSyntaxError, getPos(), "JPX QCD marker segment before SIZ segment"); return false; } if (!readUByte(&img.tiles[0].tileComps[0].quantStyle)) { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return false; } if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x00) { if (segLen <= 3) { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return false; } img.tiles[0].tileComps[0].nQuantSteps = segLen - 3; img.tiles[0].tileComps[0].quantSteps = (unsigned int *)greallocn(img.tiles[0].tileComps[0].quantSteps, img.tiles[0].tileComps[0].nQuantSteps, sizeof(unsigned int)); for (i = 0; i < img.tiles[0].tileComps[0].nQuantSteps; ++i) { if (!readUByte(&img.tiles[0].tileComps[0].quantSteps[i])) { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return false; } } } else if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x01) { img.tiles[0].tileComps[0].nQuantSteps = 1; img.tiles[0].tileComps[0].quantSteps = (unsigned int *)greallocn(img.tiles[0].tileComps[0].quantSteps, img.tiles[0].tileComps[0].nQuantSteps, sizeof(unsigned int)); if (!readUWord(&img.tiles[0].tileComps[0].quantSteps[0])) { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return false; } } else if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x02) { if (segLen < 5) { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return false; } img.tiles[0].tileComps[0].nQuantSteps = (segLen - 3) / 2; img.tiles[0].tileComps[0].quantSteps = (unsigned int *)greallocn(img.tiles[0].tileComps[0].quantSteps, img.tiles[0].tileComps[0].nQuantSteps, sizeof(unsigned int)); for (i = 0; i < img.tiles[0].tileComps[0].nQuantSteps; ++i) { if (!readUWord(&img.tiles[0].tileComps[0].quantSteps[i])) { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return false; } } } else { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return false; } for (i = 0; i < img.nXTiles * img.nYTiles; ++i) { for (comp = 0; comp < img.nComps; ++comp) { if (!(i == 0 && comp == 0)) { img.tiles[i].tileComps[comp].quantStyle = img.tiles[0].tileComps[0].quantStyle; img.tiles[i].tileComps[comp].nQuantSteps = img.tiles[0].tileComps[0].nQuantSteps; img.tiles[i].tileComps[comp].quantSteps = (unsigned int *)greallocn(img.tiles[i].tileComps[comp].quantSteps, img.tiles[0].tileComps[0].nQuantSteps, sizeof(unsigned int)); for (j = 0; j < img.tiles[0].tileComps[0].nQuantSteps; ++j) { img.tiles[i].tileComps[comp].quantSteps[j] = img.tiles[0].tileComps[0].quantSteps[j]; } } } } haveQCD = true; break; case 0x5d: // QCC - quantization component cover(24); if (!haveQCD) { error(errSyntaxError, getPos(), "JPX QCC marker segment before QCD segment"); return false; } if ((img.nComps > 256 && !readUWord(&comp)) || (img.nComps <= 256 && !readUByte(&comp)) || comp >= img.nComps || !readUByte(&img.tiles[0].tileComps[comp].quantStyle)) { error(errSyntaxError, getPos(), "Error in JPX QCC marker segment"); return false; } if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x00) { if (segLen <= (img.nComps > 256 ? 5U : 4U)) { error(errSyntaxError, getPos(), "Error in JPX QCC marker segment"); return false; } img.tiles[0].tileComps[comp].nQuantSteps = segLen - (img.nComps > 256 ? 5 : 4); img.tiles[0].tileComps[comp].quantSteps = (unsigned int *)greallocn(img.tiles[0].tileComps[comp].quantSteps, img.tiles[0].tileComps[comp].nQuantSteps, sizeof(unsigned int)); for (i = 0; i < img.tiles[0].tileComps[comp].nQuantSteps; ++i) { if (!readUByte(&img.tiles[0].tileComps[comp].quantSteps[i])) { error(errSyntaxError, getPos(), "Error in JPX QCC marker segment"); return false; } } } else if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x01) { img.tiles[0].tileComps[comp].nQuantSteps = 1; img.tiles[0].tileComps[comp].quantSteps = (unsigned int *)greallocn(img.tiles[0].tileComps[comp].quantSteps, img.tiles[0].tileComps[comp].nQuantSteps, sizeof(unsigned int)); if (!readUWord(&img.tiles[0].tileComps[comp].quantSteps[0])) { error(errSyntaxError, getPos(), "Error in JPX QCC marker segment"); return false; } } else if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x02) { if (segLen < (img.nComps > 256 ? 5U : 4U) + 2) { error(errSyntaxError, getPos(), "Error in JPX QCC marker segment"); return false; } img.tiles[0].tileComps[comp].nQuantSteps = (segLen - (img.nComps > 256 ? 5 : 4)) / 2; img.tiles[0].tileComps[comp].quantSteps = (unsigned int *)greallocn(img.tiles[0].tileComps[comp].quantSteps, img.tiles[0].tileComps[comp].nQuantSteps, sizeof(unsigned int)); for (i = 0; i < img.tiles[0].tileComps[comp].nQuantSteps; ++i) { if (!readUWord(&img.tiles[0].tileComps[comp].quantSteps[i])) { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return false; } } } else { error(errSyntaxError, getPos(), "Error in JPX QCC marker segment"); return false; } for (i = 1; i < img.nXTiles * img.nYTiles; ++i) { img.tiles[i].tileComps[comp].quantStyle = img.tiles[0].tileComps[comp].quantStyle; img.tiles[i].tileComps[comp].nQuantSteps = img.tiles[0].tileComps[comp].nQuantSteps; img.tiles[i].tileComps[comp].quantSteps = (unsigned int *)greallocn(img.tiles[i].tileComps[comp].quantSteps, img.tiles[0].tileComps[comp].nQuantSteps, sizeof(unsigned int)); for (j = 0; j < img.tiles[0].tileComps[comp].nQuantSteps; ++j) { img.tiles[i].tileComps[comp].quantSteps[j] = img.tiles[0].tileComps[comp].quantSteps[j]; } } break; case 0x5e: // RGN - region of interest cover(25); #if 1 //~ ROI is unimplemented error(errUnimplemented, -1, "got a JPX RGN segment"); for (i = 0; i < segLen - 2; ++i) { if (bufStr->getChar() == EOF) { error(errSyntaxError, getPos(), "Error in JPX RGN marker segment"); return false; } } #else if ((img.nComps > 256 && !readUWord(&comp)) || (img.nComps <= 256 && !readUByte(&comp)) || comp >= img.nComps || !readUByte(&compInfo[comp].defROI.style) || !readUByte(&compInfo[comp].defROI.shift)) { error(errSyntaxError, getPos(), "Error in JPX RGN marker segment"); return false; } #endif break; case 0x5f: // POC - progression order change cover(26); #if 1 //~ progression order changes are unimplemented error(errUnimplemented, -1, "got a JPX POC segment"); for (i = 0; i < segLen - 2; ++i) { if (bufStr->getChar() == EOF) { error(errSyntaxError, getPos(), "Error in JPX POC marker segment"); return false; } } #else nProgs = (segLen - 2) / (img.nComps > 256 ? 9 : 7); progs = (JPXProgOrder *)gmallocn(nProgs, sizeof(JPXProgOrder)); for (i = 0; i < nProgs; ++i) { if (!readUByte(&progs[i].startRes) || !(img.nComps > 256 && readUWord(&progs[i].startComp)) || !(img.nComps <= 256 && readUByte(&progs[i].startComp)) || !readUWord(&progs[i].endLayer) || !readUByte(&progs[i].endRes) || !(img.nComps > 256 && readUWord(&progs[i].endComp)) || !(img.nComps <= 256 && readUByte(&progs[i].endComp)) || !readUByte(&progs[i].progOrder)) { error(errSyntaxError, getPos(), "Error in JPX POC marker segment"); return false; } } #endif break; case 0x60: // PPM - packed packet headers, main header cover(27); #if 1 //~ packed packet headers are unimplemented error(errUnimplemented, -1, "Got a JPX PPM segment"); for (i = 0; i < segLen - 2; ++i) { if (bufStr->getChar() == EOF) { error(errSyntaxError, getPos(), "Error in JPX PPM marker segment"); return false; } } #endif break; case 0x55: // TLM - tile-part lengths // skipped cover(28); for (i = 0; i < segLen - 2; ++i) { if (bufStr->getChar() == EOF) { error(errSyntaxError, getPos(), "Error in JPX TLM marker segment"); return false; } } break; case 0x57: // PLM - packet length, main header // skipped cover(29); for (i = 0; i < segLen - 2; ++i) { if (bufStr->getChar() == EOF) { error(errSyntaxError, getPos(), "Error in JPX PLM marker segment"); return false; } } break; case 0x63: // CRG - component registration // skipped cover(30); for (i = 0; i < segLen - 2; ++i) { if (bufStr->getChar() == EOF) { error(errSyntaxError, getPos(), "Error in JPX CRG marker segment"); return false; } } break; case 0x64: // COM - comment // skipped cover(31); for (i = 0; i < segLen - 2; ++i) { if (bufStr->getChar() == EOF) { error(errSyntaxError, getPos(), "Error in JPX COM marker segment"); return false; } } break; case 0x90: // SOT - start of tile cover(32); haveSOT = true; break; default: cover(33); error(errSyntaxError, getPos(), "Unknown marker segment {0:02x} in JPX stream", segType); for (i = 0; i < segLen - 2; ++i) { if (bufStr->getChar() == EOF) { break; } } break; } } while (!haveSOT); if (!haveSIZ) { error(errSyntaxError, getPos(), "Missing SIZ marker segment in JPX stream"); return false; } if (!haveCOD) { error(errSyntaxError, getPos(), "Missing COD marker segment in JPX stream"); return false; } if (!haveQCD) { error(errSyntaxError, getPos(), "Missing QCD marker segment in JPX stream"); return false; } //----- read the tile-parts while (1) { if (!readTilePart()) { return false; } if (!readMarkerHdr(&segType, &segLen)) { error(errSyntaxError, getPos(), "Error in JPX codestream"); return false; } if (segType != 0x90) { // SOT - start of tile break; } } if (segType != 0xd9) { // EOC - end of codestream error(errSyntaxError, getPos(), "Missing EOC marker in JPX codestream"); return false; } //----- finish decoding the image for (i = 0; i < img.nXTiles * img.nYTiles; ++i) { tile = &img.tiles[i]; if (!tile->init) { error(errSyntaxError, getPos(), "Uninitialized tile in JPX codestream"); return false; } for (comp = 0; comp < img.nComps; ++comp) { tileComp = &tile->tileComps[comp]; inverseTransform(tileComp); } if (!inverseMultiCompAndDC(tile)) { return false; } } //~ can free memory below tileComps here, and also tileComp.buf return true; } bool JPXStream::readTilePart() { JPXTile *tile; JPXTileComp *tileComp; JPXResLevel *resLevel; JPXPrecinct *precinct; JPXSubband *subband; JPXCodeBlock *cb; int *sbCoeffs; bool haveSOD; unsigned int tileIdx, tilePartLen, tilePartIdx, nTileParts; bool tilePartToEOC; unsigned int precinctSize, style, nDecompLevels; unsigned int n, nSBs, nx, ny, sbx0, sby0, comp, segLen; unsigned int i, j, k, cbX, cbY, r, pre, sb, cbi, cbj; int segType, level; // process the SOT marker segment if (!readUWord(&tileIdx) || !readULong(&tilePartLen) || !readUByte(&tilePartIdx) || !readUByte(&nTileParts)) { error(errSyntaxError, getPos(), "Error in JPX SOT marker segment"); return false; } if (tileIdx >= img.nXTiles * img.nYTiles || (tilePartIdx > 0 && !img.tiles[tileIdx].init)) { error(errSyntaxError, getPos(), "Weird tile index in JPX stream"); return false; } tilePartToEOC = tilePartLen == 0; tilePartLen -= 12; // subtract size of SOT segment haveSOD = false; do { if (!readMarkerHdr(&segType, &segLen)) { error(errSyntaxError, getPos(), "Error in JPX tile-part codestream"); return false; } tilePartLen -= 2 + segLen; switch (segType) { case 0x52: // COD - coding style default cover(34); if (!readUByte(&img.tiles[tileIdx].tileComps[0].style) || !readUByte(&img.tiles[tileIdx].progOrder) || !readUWord(&img.tiles[tileIdx].nLayers) || !readUByte(&img.tiles[tileIdx].multiComp) || !readUByte(&nDecompLevels) || !readUByte(&img.tiles[tileIdx].tileComps[0].codeBlockW) || !readUByte(&img.tiles[tileIdx].tileComps[0].codeBlockH) || !readUByte(&img.tiles[tileIdx].tileComps[0].codeBlockStyle) || !readUByte(&img.tiles[tileIdx].tileComps[0].transform)) { error(errSyntaxError, getPos(), "Error in JPX COD marker segment"); return false; } if (nDecompLevels > 32 || img.tiles[tileIdx].tileComps[0].codeBlockW > 8 || img.tiles[tileIdx].tileComps[0].codeBlockH > 8) { error(errSyntaxError, getPos(), "Error in JPX COD marker segment"); return false; } img.tiles[tileIdx].tileComps[0].nDecompLevels = nDecompLevels; img.tiles[tileIdx].tileComps[0].codeBlockW += 2; img.tiles[tileIdx].tileComps[0].codeBlockH += 2; for (comp = 0; comp < img.nComps; ++comp) { if (comp != 0) { img.tiles[tileIdx].tileComps[comp].style = img.tiles[tileIdx].tileComps[0].style; img.tiles[tileIdx].tileComps[comp].nDecompLevels = img.tiles[tileIdx].tileComps[0].nDecompLevels; img.tiles[tileIdx].tileComps[comp].codeBlockW = img.tiles[tileIdx].tileComps[0].codeBlockW; img.tiles[tileIdx].tileComps[comp].codeBlockH = img.tiles[tileIdx].tileComps[0].codeBlockH; img.tiles[tileIdx].tileComps[comp].codeBlockStyle = img.tiles[tileIdx].tileComps[0].codeBlockStyle; img.tiles[tileIdx].tileComps[comp].transform = img.tiles[tileIdx].tileComps[0].transform; } img.tiles[tileIdx].tileComps[comp].resLevels = (JPXResLevel *)greallocn(img.tiles[tileIdx].tileComps[comp].resLevels, (img.tiles[tileIdx].tileComps[comp].nDecompLevels + 1), sizeof(JPXResLevel)); for (r = 0; r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels; ++r) { img.tiles[tileIdx].tileComps[comp].resLevels[r].precincts = nullptr; } } for (r = 0; r <= img.tiles[tileIdx].tileComps[0].nDecompLevels; ++r) { if (img.tiles[tileIdx].tileComps[0].style & 0x01) { if (!readUByte(&precinctSize)) { error(errSyntaxError, getPos(), "Error in JPX COD marker segment"); return false; } img.tiles[tileIdx].tileComps[0].resLevels[r].precinctWidth = precinctSize & 0x0f; img.tiles[tileIdx].tileComps[0].resLevels[r].precinctHeight = (precinctSize >> 4) & 0x0f; } else { img.tiles[tileIdx].tileComps[0].resLevels[r].precinctWidth = 15; img.tiles[tileIdx].tileComps[0].resLevels[r].precinctHeight = 15; } } for (comp = 1; comp < img.nComps; ++comp) { for (r = 0; r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels; ++r) { img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctWidth = img.tiles[tileIdx].tileComps[0].resLevels[r].precinctWidth; img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctHeight = img.tiles[tileIdx].tileComps[0].resLevels[r].precinctHeight; } } break; case 0x53: // COC - coding style component cover(35); if ((img.nComps > 256 && !readUWord(&comp)) || (img.nComps <= 256 && !readUByte(&comp)) || comp >= img.nComps || !readUByte(&style) || !readUByte(&nDecompLevels) || !readUByte(&img.tiles[tileIdx].tileComps[comp].codeBlockW) || !readUByte(&img.tiles[tileIdx].tileComps[comp].codeBlockH) || !readUByte(&img.tiles[tileIdx].tileComps[comp].codeBlockStyle) || !readUByte(&img.tiles[tileIdx].tileComps[comp].transform)) { error(errSyntaxError, getPos(), "Error in JPX COC marker segment"); return false; } if (nDecompLevels > 32 || img.tiles[tileIdx].tileComps[comp].codeBlockW > 8 || img.tiles[tileIdx].tileComps[comp].codeBlockH > 8) { error(errSyntaxError, getPos(), "Error in JPX COD marker segment"); return false; } img.tiles[tileIdx].tileComps[comp].nDecompLevels = nDecompLevels; img.tiles[tileIdx].tileComps[comp].style = (img.tiles[tileIdx].tileComps[comp].style & ~1) | (style & 1); img.tiles[tileIdx].tileComps[comp].codeBlockW += 2; img.tiles[tileIdx].tileComps[comp].codeBlockH += 2; img.tiles[tileIdx].tileComps[comp].resLevels = (JPXResLevel *)greallocn(img.tiles[tileIdx].tileComps[comp].resLevels, (img.tiles[tileIdx].tileComps[comp].nDecompLevels + 1), sizeof(JPXResLevel)); for (r = 0; r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels; ++r) { img.tiles[tileIdx].tileComps[comp].resLevels[r].precincts = nullptr; } for (r = 0; r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels; ++r) { if (img.tiles[tileIdx].tileComps[comp].style & 0x01) { if (!readUByte(&precinctSize)) { error(errSyntaxError, getPos(), "Error in JPX COD marker segment"); return false; } img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctWidth = precinctSize & 0x0f; img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctHeight = (precinctSize >> 4) & 0x0f; } else { img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctWidth = 15; img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctHeight = 15; } } break; case 0x5c: // QCD - quantization default cover(36); if (!readUByte(&img.tiles[tileIdx].tileComps[0].quantStyle)) { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return false; } if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x00) { if (segLen <= 3) { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return false; } img.tiles[tileIdx].tileComps[0].nQuantSteps = segLen - 3; img.tiles[tileIdx].tileComps[0].quantSteps = (unsigned int *)greallocn(img.tiles[tileIdx].tileComps[0].quantSteps, img.tiles[tileIdx].tileComps[0].nQuantSteps, sizeof(unsigned int)); for (i = 0; i < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++i) { if (!readUByte(&img.tiles[tileIdx].tileComps[0].quantSteps[i])) { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return false; } } } else if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x01) { img.tiles[tileIdx].tileComps[0].nQuantSteps = 1; img.tiles[tileIdx].tileComps[0].quantSteps = (unsigned int *)greallocn(img.tiles[tileIdx].tileComps[0].quantSteps, img.tiles[tileIdx].tileComps[0].nQuantSteps, sizeof(unsigned int)); if (!readUWord(&img.tiles[tileIdx].tileComps[0].quantSteps[0])) { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return false; } } else if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x02) { if (segLen < 5) { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return false; } img.tiles[tileIdx].tileComps[0].nQuantSteps = (segLen - 3) / 2; img.tiles[tileIdx].tileComps[0].quantSteps = (unsigned int *)greallocn(img.tiles[tileIdx].tileComps[0].quantSteps, img.tiles[tileIdx].tileComps[0].nQuantSteps, sizeof(unsigned int)); for (i = 0; i < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++i) { if (!readUWord(&img.tiles[tileIdx].tileComps[0].quantSteps[i])) { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return false; } } } else { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return false; } for (comp = 1; comp < img.nComps; ++comp) { img.tiles[tileIdx].tileComps[comp].quantStyle = img.tiles[tileIdx].tileComps[0].quantStyle; img.tiles[tileIdx].tileComps[comp].nQuantSteps = img.tiles[tileIdx].tileComps[0].nQuantSteps; img.tiles[tileIdx].tileComps[comp].quantSteps = (unsigned int *)greallocn(img.tiles[tileIdx].tileComps[comp].quantSteps, img.tiles[tileIdx].tileComps[0].nQuantSteps, sizeof(unsigned int)); for (j = 0; j < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++j) { img.tiles[tileIdx].tileComps[comp].quantSteps[j] = img.tiles[tileIdx].tileComps[0].quantSteps[j]; } } break; case 0x5d: // QCC - quantization component cover(37); if ((img.nComps > 256 && !readUWord(&comp)) || (img.nComps <= 256 && !readUByte(&comp)) || comp >= img.nComps || !readUByte(&img.tiles[tileIdx].tileComps[comp].quantStyle)) { error(errSyntaxError, getPos(), "Error in JPX QCC marker segment"); return false; } if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f) == 0x00) { if (segLen <= (img.nComps > 256 ? 5U : 4U)) { error(errSyntaxError, getPos(), "Error in JPX QCC marker segment"); return false; } img.tiles[tileIdx].tileComps[comp].nQuantSteps = segLen - (img.nComps > 256 ? 5 : 4); img.tiles[tileIdx].tileComps[comp].quantSteps = (unsigned int *)greallocn(img.tiles[tileIdx].tileComps[comp].quantSteps, img.tiles[tileIdx].tileComps[comp].nQuantSteps, sizeof(unsigned int)); for (i = 0; i < img.tiles[tileIdx].tileComps[comp].nQuantSteps; ++i) { if (!readUByte(&img.tiles[tileIdx].tileComps[comp].quantSteps[i])) { error(errSyntaxError, getPos(), "Error in JPX QCC marker segment"); return false; } } } else if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f) == 0x01) { img.tiles[tileIdx].tileComps[comp].nQuantSteps = 1; img.tiles[tileIdx].tileComps[comp].quantSteps = (unsigned int *)greallocn(img.tiles[tileIdx].tileComps[comp].quantSteps, img.tiles[tileIdx].tileComps[comp].nQuantSteps, sizeof(unsigned int)); if (!readUWord(&img.tiles[tileIdx].tileComps[comp].quantSteps[0])) { error(errSyntaxError, getPos(), "Error in JPX QCC marker segment"); return false; } } else if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f) == 0x02) { if (segLen < (img.nComps > 256 ? 5U : 4U) + 2) { error(errSyntaxError, getPos(), "Error in JPX QCC marker segment"); return false; } img.tiles[tileIdx].tileComps[comp].nQuantSteps = (segLen - (img.nComps > 256 ? 5 : 4)) / 2; img.tiles[tileIdx].tileComps[comp].quantSteps = (unsigned int *)greallocn(img.tiles[tileIdx].tileComps[comp].quantSteps, img.tiles[tileIdx].tileComps[comp].nQuantSteps, sizeof(unsigned int)); for (i = 0; i < img.tiles[tileIdx].tileComps[comp].nQuantSteps; ++i) { if (!readUWord(&img.tiles[tileIdx].tileComps[comp].quantSteps[i])) { error(errSyntaxError, getPos(), "Error in JPX QCD marker segment"); return false; } } } else { error(errSyntaxError, getPos(), "Error in JPX QCC marker segment"); return false; } break; case 0x5e: // RGN - region of interest cover(38); #if 1 //~ ROI is unimplemented error(errUnimplemented, -1, "Got a JPX RGN segment"); for (i = 0; i < segLen - 2; ++i) { if (bufStr->getChar() == EOF) { error(errSyntaxError, getPos(), "Error in JPX RGN marker segment"); return false; } } #else if ((img.nComps > 256 && !readUWord(&comp)) || (img.nComps <= 256 && !readUByte(&comp)) || comp >= img.nComps || !readUByte(&compInfo[comp].roi.style) || !readUByte(&compInfo[comp].roi.shift)) { error(errSyntaxError, getPos(), "Error in JPX RGN marker segment"); return false; } #endif break; case 0x5f: // POC - progression order change cover(39); #if 1 //~ progression order changes are unimplemented error(errUnimplemented, -1, "Got a JPX POC segment"); for (i = 0; i < segLen - 2; ++i) { if (bufStr->getChar() == EOF) { error(errSyntaxError, getPos(), "Error in JPX POC marker segment"); return false; } } #else nTileProgs = (segLen - 2) / (img.nComps > 256 ? 9 : 7); tileProgs = (JPXProgOrder *)gmallocn(nTileProgs, sizeof(JPXProgOrder)); for (i = 0; i < nTileProgs; ++i) { if (!readUByte(&tileProgs[i].startRes) || !(img.nComps > 256 && readUWord(&tileProgs[i].startComp)) || !(img.nComps <= 256 && readUByte(&tileProgs[i].startComp)) || !readUWord(&tileProgs[i].endLayer) || !readUByte(&tileProgs[i].endRes) || !(img.nComps > 256 && readUWord(&tileProgs[i].endComp)) || !(img.nComps <= 256 && readUByte(&tileProgs[i].endComp)) || !readUByte(&tileProgs[i].progOrder)) { error(errSyntaxError, getPos(), "Error in JPX POC marker segment"); return false; } } #endif break; case 0x61: // PPT - packed packet headers, tile-part hdr cover(40); #if 1 //~ packed packet headers are unimplemented error(errUnimplemented, -1, "Got a JPX PPT segment"); for (i = 0; i < segLen - 2; ++i) { if (bufStr->getChar() == EOF) { error(errSyntaxError, getPos(), "Error in JPX PPT marker segment"); return false; } } #endif case 0x58: // PLT - packet length, tile-part header // skipped cover(41); for (i = 0; i < segLen - 2; ++i) { if (bufStr->getChar() == EOF) { error(errSyntaxError, getPos(), "Error in JPX PLT marker segment"); return false; } } break; case 0x64: // COM - comment // skipped cover(42); for (i = 0; i < segLen - 2; ++i) { if (bufStr->getChar() == EOF) { error(errSyntaxError, getPos(), "Error in JPX COM marker segment"); return false; } } break; case 0x93: // SOD - start of data cover(43); haveSOD = true; break; default: cover(44); error(errSyntaxError, getPos(), "Unknown marker segment {0:02x} in JPX tile-part stream", segType); for (i = 0; i < segLen - 2; ++i) { if (bufStr->getChar() == EOF) { break; } } break; } } while (!haveSOD); //----- initialize the tile, precincts, and code-blocks if (tilePartIdx == 0) { tile = &img.tiles[tileIdx]; tile->init = true; i = tileIdx / img.nXTiles; j = tileIdx % img.nXTiles; if ((tile->x0 = img.xTileOffset + j * img.xTileSize) < img.xOffset) { tile->x0 = img.xOffset; } if ((tile->y0 = img.yTileOffset + i * img.yTileSize) < img.yOffset) { tile->y0 = img.yOffset; } if ((tile->x1 = img.xTileOffset + (j + 1) * img.xTileSize) > img.xSize) { tile->x1 = img.xSize; } if ((tile->y1 = img.yTileOffset + (i + 1) * img.yTileSize) > img.ySize) { tile->y1 = img.ySize; } tile->comp = 0; tile->res = 0; tile->precinct = 0; tile->layer = 0; tile->maxNDecompLevels = 0; for (comp = 0; comp < img.nComps; ++comp) { tileComp = &tile->tileComps[comp]; if (tileComp->nDecompLevels > tile->maxNDecompLevels) { tile->maxNDecompLevels = tileComp->nDecompLevels; } tileComp->x0 = jpxCeilDiv(tile->x0, tileComp->hSep); tileComp->y0 = jpxCeilDiv(tile->y0, tileComp->vSep); tileComp->x1 = jpxCeilDiv(tile->x1, tileComp->hSep); tileComp->y1 = jpxCeilDiv(tile->y1, tileComp->vSep); tileComp->w = tileComp->x1 - tileComp->x0; tileComp->cbW = 1 << tileComp->codeBlockW; tileComp->cbH = 1 << tileComp->codeBlockH; tileComp->data = (int *)gmallocn((tileComp->x1 - tileComp->x0) * (tileComp->y1 - tileComp->y0), sizeof(int)); if (tileComp->x1 - tileComp->x0 > tileComp->y1 - tileComp->y0) { n = tileComp->x1 - tileComp->x0; } else { n = tileComp->y1 - tileComp->y0; } tileComp->buf = (int *)gmallocn(n + 8, sizeof(int)); for (r = 0; r <= tileComp->nDecompLevels; ++r) { resLevel = &tileComp->resLevels[r]; k = r == 0 ? tileComp->nDecompLevels : tileComp->nDecompLevels - r + 1; resLevel->x0 = jpxCeilDivPow2(tileComp->x0, k); resLevel->y0 = jpxCeilDivPow2(tileComp->y0, k); resLevel->x1 = jpxCeilDivPow2(tileComp->x1, k); resLevel->y1 = jpxCeilDivPow2(tileComp->y1, k); if (r == 0) { resLevel->bx0[0] = resLevel->x0; resLevel->by0[0] = resLevel->y0; resLevel->bx1[0] = resLevel->x1; resLevel->by1[0] = resLevel->y1; } else { resLevel->bx0[0] = jpxCeilDivPow2(tileComp->x0 - (1 << (k - 1)), k); resLevel->by0[0] = resLevel->y0; resLevel->bx1[0] = jpxCeilDivPow2(tileComp->x1 - (1 << (k - 1)), k); resLevel->by1[0] = resLevel->y1; resLevel->bx0[1] = resLevel->x0; resLevel->by0[1] = jpxCeilDivPow2(tileComp->y0 - (1 << (k - 1)), k); resLevel->bx1[1] = resLevel->x1; resLevel->by1[1] = jpxCeilDivPow2(tileComp->y1 - (1 << (k - 1)), k); resLevel->bx0[2] = jpxCeilDivPow2(tileComp->x0 - (1 << (k - 1)), k); resLevel->by0[2] = jpxCeilDivPow2(tileComp->y0 - (1 << (k - 1)), k); resLevel->bx1[2] = jpxCeilDivPow2(tileComp->x1 - (1 << (k - 1)), k); resLevel->by1[2] = jpxCeilDivPow2(tileComp->y1 - (1 << (k - 1)), k); } resLevel->precincts = (JPXPrecinct *)gmallocn(1, sizeof(JPXPrecinct)); for (pre = 0; pre < 1; ++pre) { precinct = &resLevel->precincts[pre]; precinct->x0 = resLevel->x0; precinct->y0 = resLevel->y0; precinct->x1 = resLevel->x1; precinct->y1 = resLevel->y1; nSBs = r == 0 ? 1 : 3; precinct->subbands = (JPXSubband *)gmallocn(nSBs, sizeof(JPXSubband)); for (sb = 0; sb < nSBs; ++sb) { subband = &precinct->subbands[sb]; subband->x0 = resLevel->bx0[sb]; subband->y0 = resLevel->by0[sb]; subband->x1 = resLevel->bx1[sb]; subband->y1 = resLevel->by1[sb]; subband->nXCBs = jpxCeilDivPow2(subband->x1, tileComp->codeBlockW) - jpxFloorDivPow2(subband->x0, tileComp->codeBlockW); subband->nYCBs = jpxCeilDivPow2(subband->y1, tileComp->codeBlockH) - jpxFloorDivPow2(subband->y0, tileComp->codeBlockH); n = subband->nXCBs > subband->nYCBs ? subband->nXCBs : subband->nYCBs; for (subband->maxTTLevel = 0, --n; n; ++subband->maxTTLevel, n >>= 1) ; n = 0; for (level = subband->maxTTLevel; level >= 0; --level) { nx = jpxCeilDivPow2(subband->nXCBs, level); ny = jpxCeilDivPow2(subband->nYCBs, level); n += nx * ny; } subband->inclusion = (JPXTagTreeNode *)gmallocn(n, sizeof(JPXTagTreeNode)); subband->zeroBitPlane = (JPXTagTreeNode *)gmallocn(n, sizeof(JPXTagTreeNode)); for (k = 0; k < n; ++k) { subband->inclusion[k].finished = false; subband->inclusion[k].val = 0; subband->zeroBitPlane[k].finished = false; subband->zeroBitPlane[k].val = 0; } subband->cbs = (JPXCodeBlock *)gmallocn(subband->nXCBs * subband->nYCBs, sizeof(JPXCodeBlock)); sbx0 = jpxFloorDivPow2(subband->x0, tileComp->codeBlockW); sby0 = jpxFloorDivPow2(subband->y0, tileComp->codeBlockH); if (r == 0) { // (NL)LL sbCoeffs = tileComp->data; } else if (sb == 0) { // (NL-r+1)HL sbCoeffs = tileComp->data + resLevel->bx1[1] - resLevel->bx0[1]; } else if (sb == 1) { // (NL-r+1)LH sbCoeffs = tileComp->data + (resLevel->by1[0] - resLevel->by0[0]) * tileComp->w; } else { // (NL-r+1)HH sbCoeffs = tileComp->data + (resLevel->by1[0] - resLevel->by0[0]) * tileComp->w + resLevel->bx1[1] - resLevel->bx0[1]; } cb = subband->cbs; for (cbY = 0; cbY < subband->nYCBs; ++cbY) { for (cbX = 0; cbX < subband->nXCBs; ++cbX) { cb->x0 = (sbx0 + cbX) << tileComp->codeBlockW; cb->x1 = cb->x0 + tileComp->cbW; if (subband->x0 > cb->x0) { cb->x0 = subband->x0; } if (subband->x1 < cb->x1) { cb->x1 = subband->x1; } cb->y0 = (sby0 + cbY) << tileComp->codeBlockH; cb->y1 = cb->y0 + tileComp->cbH; if (subband->y0 > cb->y0) { cb->y0 = subband->y0; } if (subband->y1 < cb->y1) { cb->y1 = subband->y1; } cb->seen = false; cb->lBlock = 3; cb->nextPass = jpxPassCleanup; cb->nZeroBitPlanes = 0; cb->dataLenSize = 1; cb->dataLen = (unsigned int *)gmalloc(sizeof(unsigned int)); cb->coeffs = sbCoeffs + (cb->y0 - subband->y0) * tileComp->w + (cb->x0 - subband->x0); cb->touched = (char *)gmalloc(1 << (tileComp->codeBlockW + tileComp->codeBlockH)); cb->len = 0; for (cbj = 0; cbj < cb->y1 - cb->y0; ++cbj) { for (cbi = 0; cbi < cb->x1 - cb->x0; ++cbi) { cb->coeffs[cbj * tileComp->w + cbi] = 0; } } memset(cb->touched, 0, (1 << (tileComp->codeBlockW + tileComp->codeBlockH))); cb->arithDecoder = nullptr; cb->stats = nullptr; ++cb; } } } } } } } return readTilePartData(tileIdx, tilePartLen, tilePartToEOC); } bool JPXStream::readTilePartData(unsigned int tileIdx, unsigned int tilePartLen, bool tilePartToEOC) { JPXTile *tile; JPXTileComp *tileComp; JPXResLevel *resLevel; JPXPrecinct *precinct; JPXSubband *subband; JPXCodeBlock *cb; unsigned int ttVal; unsigned int bits, cbX, cbY, nx, ny, i, j, n, sb; int level; tile = &img.tiles[tileIdx]; // read all packets from this tile-part while (1) { if (tilePartToEOC) { //~ peek for an EOC marker cover(93); } else if (tilePartLen == 0) { break; } tileComp = &tile->tileComps[tile->comp]; resLevel = &tileComp->resLevels[tile->res]; precinct = &resLevel->precincts[tile->precinct]; //----- packet header // setup startBitBuf(tilePartLen); if (tileComp->style & 0x02) { skipSOP(); } // zero-length flag if (!readBits(1, &bits)) { goto err; } if (!bits) { // packet is empty -- clear all code-block inclusion flags cover(45); for (sb = 0; sb < (unsigned int)(tile->res == 0 ? 1 : 3); ++sb) { subband = &precinct->subbands[sb]; for (cbY = 0; cbY < subband->nYCBs; ++cbY) { for (cbX = 0; cbX < subband->nXCBs; ++cbX) { cb = &subband->cbs[cbY * subband->nXCBs + cbX]; cb->included = false; } } } } else { for (sb = 0; sb < (unsigned int)(tile->res == 0 ? 1 : 3); ++sb) { subband = &precinct->subbands[sb]; for (cbY = 0; cbY < subband->nYCBs; ++cbY) { for (cbX = 0; cbX < subband->nXCBs; ++cbX) { cb = &subband->cbs[cbY * subband->nXCBs + cbX]; // skip code-blocks with no coefficients if (cb->x0 >= cb->x1 || cb->y0 >= cb->y1) { cover(46); cb->included = false; continue; } // code-block inclusion if (cb->seen) { cover(47); if (!readBits(1, &cb->included)) { goto err; } } else { cover(48); ttVal = 0; i = 0; for (level = subband->maxTTLevel; level >= 0; --level) { nx = jpxCeilDivPow2(subband->nXCBs, level); ny = jpxCeilDivPow2(subband->nYCBs, level); j = i + (cbY >> level) * nx + (cbX >> level); if (!subband->inclusion[j].finished && !subband->inclusion[j].val) { subband->inclusion[j].val = ttVal; } else { ttVal = subband->inclusion[j].val; } while (!subband->inclusion[j].finished && ttVal <= tile->layer) { if (!readBits(1, &bits)) { goto err; } if (bits == 1) { subband->inclusion[j].finished = true; } else { ++ttVal; } } subband->inclusion[j].val = ttVal; if (ttVal > tile->layer) { break; } i += nx * ny; } cb->included = level < 0; } if (cb->included) { cover(49); // zero bit-plane count if (!cb->seen) { cover(50); ttVal = 0; i = 0; for (level = subband->maxTTLevel; level >= 0; --level) { nx = jpxCeilDivPow2(subband->nXCBs, level); ny = jpxCeilDivPow2(subband->nYCBs, level); j = i + (cbY >> level) * nx + (cbX >> level); if (!subband->zeroBitPlane[j].finished && !subband->zeroBitPlane[j].val) { subband->zeroBitPlane[j].val = ttVal; } else { ttVal = subband->zeroBitPlane[j].val; } while (!subband->zeroBitPlane[j].finished) { if (!readBits(1, &bits)) { goto err; } if (bits == 1) { subband->zeroBitPlane[j].finished = true; } else { ++ttVal; } } subband->zeroBitPlane[j].val = ttVal; i += nx * ny; } cb->nZeroBitPlanes = ttVal; } // number of coding passes if (!readBits(1, &bits)) { goto err; } if (bits == 0) { cover(51); cb->nCodingPasses = 1; } else { if (!readBits(1, &bits)) { goto err; } if (bits == 0) { cover(52); cb->nCodingPasses = 2; } else { cover(53); if (!readBits(2, &bits)) { goto err; } if (bits < 3) { cover(54); cb->nCodingPasses = 3 + bits; } else { cover(55); if (!readBits(5, &bits)) { goto err; } if (bits < 31) { cover(56); cb->nCodingPasses = 6 + bits; } else { cover(57); if (!readBits(7, &bits)) { goto err; } cb->nCodingPasses = 37 + bits; } } } } // update Lblock while (1) { if (!readBits(1, &bits)) { goto err; } if (!bits) { break; } ++cb->lBlock; } // one codeword segment for each of the coding passes if (tileComp->codeBlockStyle & 0x04) { if (cb->nCodingPasses > cb->dataLenSize) { cb->dataLenSize = cb->nCodingPasses; cb->dataLen = (unsigned int *)greallocn(cb->dataLen, cb->dataLenSize, sizeof(unsigned int)); } // read the lengths for (i = 0; i < cb->nCodingPasses; ++i) { if (!readBits(cb->lBlock, &cb->dataLen[i])) { goto err; } } // one codeword segment for all of the coding passes } else { // read the length for (n = cb->lBlock, i = cb->nCodingPasses >> 1; i; ++n, i >>= 1) ; if (!readBits(n, &cb->dataLen[0])) { goto err; } } } } } } } if (tileComp->style & 0x04) { skipEPH(); } tilePartLen = finishBitBuf(); //----- packet data for (sb = 0; sb < (unsigned int)(tile->res == 0 ? 1 : 3); ++sb) { subband = &precinct->subbands[sb]; for (cbY = 0; cbY < subband->nYCBs; ++cbY) { for (cbX = 0; cbX < subband->nXCBs; ++cbX) { cb = &subband->cbs[cbY * subband->nXCBs + cbX]; if (cb->included) { if (!readCodeBlockData(tileComp, resLevel, precinct, subband, tile->res, sb, cb)) { return false; } if (tileComp->codeBlockStyle & 0x04) { for (i = 0; i < cb->nCodingPasses; ++i) { tilePartLen -= cb->dataLen[i]; } } else { tilePartLen -= cb->dataLen[0]; } cb->seen = true; } } } } //----- next packet switch (tile->progOrder) { case 0: // layer, resolution level, component, precinct cover(58); if (++tile->comp == img.nComps) { tile->comp = 0; if (++tile->res == tile->maxNDecompLevels + 1) { tile->res = 0; if (++tile->layer == tile->nLayers) { tile->layer = 0; } } } break; case 1: // resolution level, layer, component, precinct cover(59); if (++tile->comp == img.nComps) { tile->comp = 0; if (++tile->layer == tile->nLayers) { tile->layer = 0; if (++tile->res == tile->maxNDecompLevels + 1) { tile->res = 0; } } } break; case 2: // resolution level, precinct, component, layer //~ this isn't correct -- see B.12.1.3 cover(60); if (++tile->layer == tile->nLayers) { tile->layer = 0; if (++tile->comp == img.nComps) { tile->comp = 0; if (++tile->res == tile->maxNDecompLevels + 1) { tile->res = 0; } } tileComp = &tile->tileComps[tile->comp]; if (tile->res >= tileComp->nDecompLevels + 1) { if (++tile->comp == img.nComps) { return true; } } } break; case 3: // precinct, component, resolution level, layer //~ this isn't correct -- see B.12.1.4 cover(61); if (++tile->layer == tile->nLayers) { tile->layer = 0; if (++tile->res == tile->maxNDecompLevels + 1) { tile->res = 0; if (++tile->comp == img.nComps) { tile->comp = 0; } } } break; case 4: // component, precinct, resolution level, layer //~ this isn't correct -- see B.12.1.5 cover(62); if (++tile->layer == tile->nLayers) { tile->layer = 0; if (++tile->res == tile->maxNDecompLevels + 1) { tile->res = 0; if (++tile->comp == img.nComps) { tile->comp = 0; } } } break; } } return true; err: error(errSyntaxError, getPos(), "Error in JPX stream"); return false; } bool JPXStream::readCodeBlockData(JPXTileComp *tileComp, JPXResLevel *resLevel, JPXPrecinct *precinct, JPXSubband *subband, unsigned int res, unsigned int sb, JPXCodeBlock *cb) { int *coeff0, *coeff1, *coeff; char *touched0, *touched1, *touched; unsigned int horiz, vert, diag, all, cx, xorBit; int horizSign, vertSign, bit; int segSym; unsigned int i, x, y0, y1; if (cb->arithDecoder) { cover(63); cb->arithDecoder->restart(cb->dataLen[0]); } else { cover(64); cb->arithDecoder = new JArithmeticDecoder(); cb->arithDecoder->setStream(bufStr, cb->dataLen[0]); cb->arithDecoder->start(); cb->stats = new JArithmeticDecoderStats(jpxNContexts); cb->stats->setEntry(jpxContextSigProp, 4, 0); cb->stats->setEntry(jpxContextRunLength, 3, 0); cb->stats->setEntry(jpxContextUniform, 46, 0); } for (i = 0; i < cb->nCodingPasses; ++i) { if ((tileComp->codeBlockStyle & 0x04) && i > 0) { cb->arithDecoder->setStream(bufStr, cb->dataLen[i]); cb->arithDecoder->start(); } switch (cb->nextPass) { //----- significance propagation pass case jpxPassSigProp: cover(65); for (y0 = cb->y0, coeff0 = cb->coeffs, touched0 = cb->touched; y0 < cb->y1; y0 += 4, coeff0 += 4 * tileComp->w, touched0 += 4 << tileComp->codeBlockW) { for (x = cb->x0, coeff1 = coeff0, touched1 = touched0; x < cb->x1; ++x, ++coeff1, ++touched1) { for (y1 = 0, coeff = coeff1, touched = touched1; y1 < 4 && y0 + y1 < cb->y1; ++y1, coeff += tileComp->w, touched += tileComp->cbW) { if (!*coeff) { horiz = vert = diag = 0; horizSign = vertSign = 2; if (x > cb->x0) { if (coeff[-1]) { ++horiz; horizSign += coeff[-1] < 0 ? -1 : 1; } if (y0 + y1 > cb->y0) { diag += coeff[-(int)tileComp->w - 1] ? 1 : 0; } if (y0 + y1 < cb->y1 - 1 && (!(tileComp->codeBlockStyle & 0x08) || y1 < 3)) { diag += coeff[tileComp->w - 1] ? 1 : 0; } } if (x < cb->x1 - 1) { if (coeff[1]) { ++horiz; horizSign += coeff[1] < 0 ? -1 : 1; } if (y0 + y1 > cb->y0) { diag += coeff[-(int)tileComp->w + 1] ? 1 : 0; } if (y0 + y1 < cb->y1 - 1 && (!(tileComp->codeBlockStyle & 0x08) || y1 < 3)) { diag += coeff[tileComp->w + 1] ? 1 : 0; } } if (y0 + y1 > cb->y0) { if (coeff[-(int)tileComp->w]) { ++vert; vertSign += coeff[-(int)tileComp->w] < 0 ? -1 : 1; } } if (y0 + y1 < cb->y1 - 1 && (!(tileComp->codeBlockStyle & 0x08) || y1 < 3)) { if (coeff[tileComp->w]) { ++vert; vertSign += coeff[tileComp->w] < 0 ? -1 : 1; } } cx = sigPropContext[horiz][vert][diag][res == 0 ? 1 : sb]; if (cx != 0) { if (cb->arithDecoder->decodeBit(cx, cb->stats)) { cx = signContext[horizSign][vertSign][0]; xorBit = signContext[horizSign][vertSign][1]; if (cb->arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) { *coeff = -1; } else { *coeff = 1; } } *touched = 1; } } } } } ++cb->nextPass; break; //----- magnitude refinement pass case jpxPassMagRef: cover(66); for (y0 = cb->y0, coeff0 = cb->coeffs, touched0 = cb->touched; y0 < cb->y1; y0 += 4, coeff0 += 4 * tileComp->w, touched0 += 4 << tileComp->codeBlockW) { for (x = cb->x0, coeff1 = coeff0, touched1 = touched0; x < cb->x1; ++x, ++coeff1, ++touched1) { for (y1 = 0, coeff = coeff1, touched = touched1; y1 < 4 && y0 + y1 < cb->y1; ++y1, coeff += tileComp->w, touched += tileComp->cbW) { if (*coeff && !*touched) { if (*coeff == 1 || *coeff == -1) { all = 0; if (x > cb->x0) { all += coeff[-1] ? 1 : 0; if (y0 + y1 > cb->y0) { all += coeff[-(int)tileComp->w - 1] ? 1 : 0; } if (y0 + y1 < cb->y1 - 1 && (!(tileComp->codeBlockStyle & 0x08) || y1 < 3)) { all += coeff[tileComp->w - 1] ? 1 : 0; } } if (x < cb->x1 - 1) { all += coeff[1] ? 1 : 0; if (y0 + y1 > cb->y0) { all += coeff[-(int)tileComp->w + 1] ? 1 : 0; } if (y0 + y1 < cb->y1 - 1 && (!(tileComp->codeBlockStyle & 0x08) || y1 < 3)) { all += coeff[tileComp->w + 1] ? 1 : 0; } } if (y0 + y1 > cb->y0) { all += coeff[-(int)tileComp->w] ? 1 : 0; } if (y0 + y1 < cb->y1 - 1 && (!(tileComp->codeBlockStyle & 0x08) || y1 < 3)) { all += coeff[tileComp->w] ? 1 : 0; } cx = all ? 15 : 14; } else { cx = 16; } bit = cb->arithDecoder->decodeBit(cx, cb->stats); if (*coeff < 0) { *coeff = (*coeff << 1) - bit; } else { *coeff = (*coeff << 1) + bit; } *touched = 1; } } } } ++cb->nextPass; break; //----- cleanup pass case jpxPassCleanup: cover(67); for (y0 = cb->y0, coeff0 = cb->coeffs, touched0 = cb->touched; y0 < cb->y1; y0 += 4, coeff0 += 4 * tileComp->w, touched0 += 4 << tileComp->codeBlockW) { for (x = cb->x0, coeff1 = coeff0, touched1 = touched0; x < cb->x1; ++x, ++coeff1, ++touched1) { y1 = 0; if (y0 + 3 < cb->y1 && !(*touched1) && !(touched1[tileComp->cbW]) && !(touched1[2 * tileComp->cbW]) && !(touched1[3 * tileComp->cbW]) && (x == cb->x0 || y0 == cb->y0 || !coeff1[-(int)tileComp->w - 1]) && (y0 == cb->y0 || !coeff1[-(int)tileComp->w]) && (x == cb->x1 - 1 || y0 == cb->y0 || !coeff1[-(int)tileComp->w + 1]) && (x == cb->x0 || (!coeff1[-1] && !coeff1[tileComp->w - 1] && !coeff1[2 * tileComp->w - 1] && !coeff1[3 * tileComp->w - 1])) && (x == cb->x1 - 1 || (!coeff1[1] && !coeff1[tileComp->w + 1] && !coeff1[2 * tileComp->w + 1] && !coeff1[3 * tileComp->w + 1])) && ((tileComp->codeBlockStyle & 0x08) || ((x == cb->x0 || y0 + 4 == cb->y1 || !coeff1[4 * tileComp->w - 1]) && (y0 + 4 == cb->y1 || !coeff1[4 * tileComp->w]) && (x == cb->x1 - 1 || y0 + 4 == cb->y1 || !coeff1[4 * tileComp->w + 1])))) { if (cb->arithDecoder->decodeBit(jpxContextRunLength, cb->stats)) { y1 = cb->arithDecoder->decodeBit(jpxContextUniform, cb->stats); y1 = (y1 << 1) | cb->arithDecoder->decodeBit(jpxContextUniform, cb->stats); coeff = &coeff1[y1 * tileComp->w]; cx = signContext[2][2][0]; xorBit = signContext[2][2][1]; if (cb->arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) { *coeff = -1; } else { *coeff = 1; } ++y1; } else { y1 = 4; } } for (coeff = &coeff1[y1 * tileComp->w], touched = &touched1[y1 << tileComp->codeBlockW]; y1 < 4 && y0 + y1 < cb->y1; ++y1, coeff += tileComp->w, touched += tileComp->cbW) { if (!*touched) { horiz = vert = diag = 0; horizSign = vertSign = 2; if (x > cb->x0) { if (coeff[-1]) { ++horiz; horizSign += coeff[-1] < 0 ? -1 : 1; } if (y0 + y1 > cb->y0) { diag += coeff[-(int)tileComp->w - 1] ? 1 : 0; } if (y0 + y1 < cb->y1 - 1 && (!(tileComp->codeBlockStyle & 0x08) || y1 < 3)) { diag += coeff[tileComp->w - 1] ? 1 : 0; } } if (x < cb->x1 - 1) { if (coeff[1]) { ++horiz; horizSign += coeff[1] < 0 ? -1 : 1; } if (y0 + y1 > cb->y0) { diag += coeff[-(int)tileComp->w + 1] ? 1 : 0; } if (y0 + y1 < cb->y1 - 1 && (!(tileComp->codeBlockStyle & 0x08) || y1 < 3)) { diag += coeff[tileComp->w + 1] ? 1 : 0; } } if (y0 + y1 > cb->y0) { if (coeff[-(int)tileComp->w]) { ++vert; vertSign += coeff[-(int)tileComp->w] < 0 ? -1 : 1; } } if (y0 + y1 < cb->y1 - 1 && (!(tileComp->codeBlockStyle & 0x08) || y1 < 3)) { if (coeff[tileComp->w]) { ++vert; vertSign += coeff[tileComp->w] < 0 ? -1 : 1; } } cx = sigPropContext[horiz][vert][diag][res == 0 ? 1 : sb]; if (cb->arithDecoder->decodeBit(cx, cb->stats)) { cx = signContext[horizSign][vertSign][0]; xorBit = signContext[horizSign][vertSign][1]; if (cb->arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) { *coeff = -1; } else { *coeff = 1; } } } else { *touched = 0; } } } } ++cb->len; // look for a segmentation symbol if (tileComp->codeBlockStyle & 0x20) { segSym = cb->arithDecoder->decodeBit(jpxContextUniform, cb->stats) << 3; segSym |= cb->arithDecoder->decodeBit(jpxContextUniform, cb->stats) << 2; segSym |= cb->arithDecoder->decodeBit(jpxContextUniform, cb->stats) << 1; segSym |= cb->arithDecoder->decodeBit(jpxContextUniform, cb->stats); if (segSym != 0x0a) { // in theory this should be a fatal error, but it seems to // be problematic error(errSyntaxWarning, getPos(), "Missing or invalid segmentation symbol in JPX stream"); } } cb->nextPass = jpxPassSigProp; break; } if (tileComp->codeBlockStyle & 0x02) { cb->stats->reset(); cb->stats->setEntry(jpxContextSigProp, 4, 0); cb->stats->setEntry(jpxContextRunLength, 3, 0); cb->stats->setEntry(jpxContextUniform, 46, 0); } if (tileComp->codeBlockStyle & 0x04) { cb->arithDecoder->cleanup(); } } cb->arithDecoder->cleanup(); return true; } // Inverse quantization, and wavelet transform (IDWT). This also does // the initial shift to convert to fixed point format. void JPXStream::inverseTransform(JPXTileComp *tileComp) { JPXResLevel *resLevel; JPXPrecinct *precinct; JPXSubband *subband; JPXCodeBlock *cb; int *coeff0, *coeff; char *touched0, *touched; unsigned int qStyle, guard, eps, shift; int shift2; double mu; int val; unsigned int r, cbX, cbY, x, y; cover(68); //----- (NL)LL subband (resolution level 0) resLevel = &tileComp->resLevels[0]; precinct = &resLevel->precincts[0]; subband = &precinct->subbands[0]; // i-quant parameters qStyle = tileComp->quantStyle & 0x1f; guard = (tileComp->quantStyle >> 5) & 7; if (qStyle == 0) { cover(69); eps = (tileComp->quantSteps[0] >> 3) & 0x1f; shift = guard + eps - 1; mu = 0; // make gcc happy } else { cover(70); shift = guard - 1 + tileComp->prec; mu = (double)(0x800 + (tileComp->quantSteps[0] & 0x7ff)) / 2048.0; } if (tileComp->transform == 0) { cover(71); shift += fracBits; } // do fixed point adjustment and dequantization on (NL)LL cb = subband->cbs; for (cbY = 0; cbY < subband->nYCBs; ++cbY) { for (cbX = 0; cbX < subband->nXCBs; ++cbX) { for (y = cb->y0, coeff0 = cb->coeffs, touched0 = cb->touched; y < cb->y1; ++y, coeff0 += tileComp->w, touched0 += tileComp->cbW) { for (x = cb->x0, coeff = coeff0, touched = touched0; x < cb->x1; ++x, ++coeff, ++touched) { val = *coeff; if (val != 0) { shift2 = shift - (cb->nZeroBitPlanes + cb->len + *touched); if (shift2 > 0) { cover(94); if (val < 0) { val = (((unsigned int)val) << shift2) - (1 << (shift2 - 1)); } else { val = (val << shift2) + (1 << (shift2 - 1)); } } else { cover(95); val >>= -shift2; } if (qStyle == 0) { cover(96); if (tileComp->transform == 0) { cover(97); val &= 0xFFFFFFFF << fracBits; } } else { cover(98); val = (int)((double)val * mu); } } *coeff = val; } } ++cb; } } //----- IDWT for each level for (r = 1; r <= tileComp->nDecompLevels; ++r) { resLevel = &tileComp->resLevels[r]; // (n)LL is already in the upper-left corner of the // tile-component data array -- interleave with (n)HL/LH/HH // and inverse transform to get (n-1)LL, which will be stored // in the upper-left corner of the tile-component data array inverseTransformLevel(tileComp, r, resLevel); } } // Do one level of the inverse transform: // - take (n)LL, (n)HL, (n)LH, and (n)HH from the upper-left corner // of the tile-component data array // - leave the resulting (n-1)LL in the same place void JPXStream::inverseTransformLevel(JPXTileComp *tileComp, unsigned int r, JPXResLevel *resLevel) { JPXPrecinct *precinct; JPXSubband *subband; JPXCodeBlock *cb; int *coeff0, *coeff; char *touched0, *touched; unsigned int qStyle, guard, eps, shift, t; int shift2; double mu; int val; int *dataPtr, *bufPtr; unsigned int nx1, nx2, ny1, ny2, offset; unsigned int x, y, sb, cbX, cbY; //----- fixed-point adjustment and dequantization qStyle = tileComp->quantStyle & 0x1f; guard = (tileComp->quantStyle >> 5) & 7; precinct = &resLevel->precincts[0]; for (sb = 0; sb < 3; ++sb) { // i-quant parameters if (qStyle == 0) { cover(100); const unsigned int stepIndex = 3 * r - 2 + sb; if (unlikely(stepIndex >= tileComp->nQuantSteps)) { error(errSyntaxError, getPos(), "Wrong index for quantSteps in inverseTransformLevel in JPX stream"); break; } eps = (tileComp->quantSteps[stepIndex] >> 3) & 0x1f; shift = guard + eps - 1; mu = 0; // make gcc happy } else { cover(101); shift = guard + tileComp->prec; if (sb == 2) { cover(102); ++shift; } const unsigned int stepIndex = qStyle == 1 ? 0 : (3 * r - 2 + sb); if (unlikely(stepIndex >= tileComp->nQuantSteps)) { error(errSyntaxError, getPos(), "Wrong index for quantSteps in inverseTransformLevel in JPX stream"); break; } t = tileComp->quantSteps[stepIndex]; mu = (double)(0x800 + (t & 0x7ff)) / 2048.0; } if (tileComp->transform == 0) { cover(103); shift += fracBits; } // fixed point adjustment and dequantization subband = &precinct->subbands[sb]; cb = subband->cbs; for (cbY = 0; cbY < subband->nYCBs; ++cbY) { for (cbX = 0; cbX < subband->nXCBs; ++cbX) { for (y = cb->y0, coeff0 = cb->coeffs, touched0 = cb->touched; y < cb->y1; ++y, coeff0 += tileComp->w, touched0 += tileComp->cbW) { for (x = cb->x0, coeff = coeff0, touched = touched0; x < cb->x1; ++x, ++coeff, ++touched) { val = *coeff; if (val != 0) { shift2 = shift - (cb->nZeroBitPlanes + cb->len + *touched); if (shift2 > 0) { cover(74); if (val < 0) { val = (((unsigned int)val) << shift2) - (1 << (shift2 - 1)); } else { val = (val << shift2) + (1 << (shift2 - 1)); } } else { cover(75); val >>= -shift2; } if (qStyle == 0) { cover(76); if (tileComp->transform == 0) { val &= 0xFFFFFFFF << fracBits; } } else { cover(77); val = (int)((double)val * mu); } } *coeff = val; } } ++cb; } } } //----- inverse transform // compute the subband bounds: // 0 nx1 nx2 // | | | // v v v // +----+----+ // | LL | HL | <- 0 // +----+----+ // | LH | HH | <- ny1 // +----+----+ // <- ny2 nx1 = precinct->subbands[1].x1 - precinct->subbands[1].x0; nx2 = nx1 + precinct->subbands[0].x1 - precinct->subbands[0].x0; ny1 = precinct->subbands[0].y1 - precinct->subbands[0].y0; ny2 = ny1 + precinct->subbands[1].y1 - precinct->subbands[1].y0; // horizontal (row) transforms if (r == tileComp->nDecompLevels) { offset = 3 + (tileComp->x0 & 1); } else { offset = 3 + (tileComp->resLevels[r + 1].x0 & 1); } for (y = 0, dataPtr = tileComp->data; y < ny2; ++y, dataPtr += tileComp->w) { if (precinct->subbands[0].x0 == precinct->subbands[1].x0) { // fetch LL/LH for (x = 0, bufPtr = tileComp->buf + offset; x < nx1; ++x, bufPtr += 2) { *bufPtr = dataPtr[x]; } // fetch HL/HH for (x = nx1, bufPtr = tileComp->buf + offset + 1; x < nx2; ++x, bufPtr += 2) { *bufPtr = dataPtr[x]; } } else { // fetch LL/LH for (x = 0, bufPtr = tileComp->buf + offset + 1; x < nx1; ++x, bufPtr += 2) { *bufPtr = dataPtr[x]; } // fetch HL/HH for (x = nx1, bufPtr = tileComp->buf + offset; x < nx2; ++x, bufPtr += 2) { *bufPtr = dataPtr[x]; } } if (tileComp->x1 - tileComp->x0 > tileComp->y1 - tileComp->y0) { x = tileComp->x1 - tileComp->x0 + 5; } else { x = tileComp->y1 - tileComp->y0 + 5; } if (offset + nx2 > x || nx2 == 0) { error(errSyntaxError, getPos(), "Invalid call of inverseTransform1D in inverseTransformLevel in JPX stream"); return; } inverseTransform1D(tileComp, tileComp->buf, offset, nx2); for (x = 0, bufPtr = tileComp->buf + offset; x < nx2; ++x, ++bufPtr) { dataPtr[x] = *bufPtr; } } // vertical (column) transforms if (r == tileComp->nDecompLevels) { offset = 3 + (tileComp->y0 & 1); } else { offset = 3 + (tileComp->resLevels[r + 1].y0 & 1); } for (x = 0, dataPtr = tileComp->data; x < nx2; ++x, ++dataPtr) { if (precinct->subbands[1].y0 == precinct->subbands[0].y0) { // fetch LL/HL for (y = 0, bufPtr = tileComp->buf + offset; y < ny1; ++y, bufPtr += 2) { *bufPtr = dataPtr[y * tileComp->w]; } // fetch LH/HH for (y = ny1, bufPtr = tileComp->buf + offset + 1; y < ny2; ++y, bufPtr += 2) { *bufPtr = dataPtr[y * tileComp->w]; } } else { // fetch LL/HL for (y = 0, bufPtr = tileComp->buf + offset + 1; y < ny1; ++y, bufPtr += 2) { *bufPtr = dataPtr[y * tileComp->w]; } // fetch LH/HH for (y = ny1, bufPtr = tileComp->buf + offset; y < ny2; ++y, bufPtr += 2) { *bufPtr = dataPtr[y * tileComp->w]; } } if (tileComp->x1 - tileComp->x0 > tileComp->y1 - tileComp->y0) { y = tileComp->x1 - tileComp->x0 + 5; } else { y = tileComp->y1 - tileComp->y0 + 5; } if (offset + ny2 > y || ny2 == 0) { error(errSyntaxError, getPos(), "Invalid call of inverseTransform1D in inverseTransformLevel in JPX stream"); return; } inverseTransform1D(tileComp, tileComp->buf, offset, ny2); for (y = 0, bufPtr = tileComp->buf + offset; y < ny2; ++y, ++bufPtr) { dataPtr[y * tileComp->w] = *bufPtr; } } } void JPXStream::inverseTransform1D(JPXTileComp *tileComp, int *data, unsigned int offset, unsigned int n) { unsigned int end, i; //----- special case for length = 1 if (n == 1) { cover(79); if (offset == 4) { cover(104); *data >>= 1; } } else { cover(80); end = offset + n; //----- extend right data[end] = data[end - 2]; if (n == 2) { cover(81); data[end + 1] = data[offset + 1]; data[end + 2] = data[offset]; data[end + 3] = data[offset + 1]; } else { cover(82); data[end + 1] = data[end - 3]; if (n == 3) { cover(105); data[end + 2] = data[offset + 1]; data[end + 3] = data[offset + 2]; } else { cover(106); data[end + 2] = data[end - 4]; if (n == 4) { cover(107); data[end + 3] = data[offset + 1]; } else { cover(108); data[end + 3] = data[end - 5]; } } } //----- extend left data[offset - 1] = data[offset + 1]; data[offset - 2] = data[offset + 2]; data[offset - 3] = data[offset + 3]; if (offset == 4) { cover(83); data[0] = data[offset + 4]; } //----- 9-7 irreversible filter if (tileComp->transform == 0) { cover(84); // step 1 (even) for (i = 1; i <= end + 2; i += 2) { data[i] = (int)(idwtKappa * data[i]); } // step 2 (odd) for (i = 0; i <= end + 3; i += 2) { data[i] = (int)(idwtIKappa * data[i]); } // step 3 (even) for (i = 1; i <= end + 2; i += 2) { data[i] = (int)(data[i] - idwtDelta * (data[i - 1] + data[i + 1])); } // step 4 (odd) for (i = 2; i <= end + 1; i += 2) { data[i] = (int)(data[i] - idwtGamma * (data[i - 1] + data[i + 1])); } // step 5 (even) for (i = 3; i <= end; i += 2) { data[i] = (int)(data[i] - idwtBeta * (data[i - 1] + data[i + 1])); } // step 6 (odd) for (i = 4; i <= end - 1; i += 2) { data[i] = (int)(data[i] - idwtAlpha * (data[i - 1] + data[i + 1])); } //----- 5-3 reversible filter } else { cover(85); // step 1 (even) for (i = 3; i <= end; i += 2) { data[i] -= (data[i - 1] + data[i + 1] + 2) >> 2; } // step 2 (odd) for (i = 4; i < end; i += 2) { data[i] += (data[i - 1] + data[i + 1]) >> 1; } } } } // Inverse multi-component transform and DC level shift. This also // converts fixed point samples back to integers. bool JPXStream::inverseMultiCompAndDC(JPXTile *tile) { JPXTileComp *tileComp; int coeff, d0, d1, d2, t, minVal, maxVal, zeroVal; int *dataPtr; unsigned int j, comp, x, y; //----- inverse multi-component transform if (tile->multiComp == 1) { cover(86); if (img.nComps < 3 || tile->tileComps[0].hSep != tile->tileComps[1].hSep || tile->tileComps[0].vSep != tile->tileComps[1].vSep || tile->tileComps[1].hSep != tile->tileComps[2].hSep || tile->tileComps[1].vSep != tile->tileComps[2].vSep) { return false; } // inverse irreversible multiple component transform if (tile->tileComps[0].transform == 0) { cover(87); j = 0; for (y = 0; y < tile->tileComps[0].y1 - tile->tileComps[0].y0; ++y) { for (x = 0; x < tile->tileComps[0].x1 - tile->tileComps[0].x0; ++x) { d0 = tile->tileComps[0].data[j]; d1 = tile->tileComps[1].data[j]; d2 = tile->tileComps[2].data[j]; tile->tileComps[0].data[j] = (int)(d0 + 1.402 * d2 + 0.5); tile->tileComps[1].data[j] = (int)(d0 - 0.34413 * d1 - 0.71414 * d2 + 0.5); tile->tileComps[2].data[j] = (int)(d0 + 1.772 * d1 + 0.5); ++j; } } // inverse reversible multiple component transform } else { cover(88); j = 0; for (y = 0; y < tile->tileComps[0].y1 - tile->tileComps[0].y0; ++y) { for (x = 0; x < tile->tileComps[0].x1 - tile->tileComps[0].x0; ++x) { d0 = tile->tileComps[0].data[j]; d1 = tile->tileComps[1].data[j]; d2 = tile->tileComps[2].data[j]; tile->tileComps[1].data[j] = t = d0 - ((d2 + d1) >> 2); tile->tileComps[0].data[j] = d2 + t; tile->tileComps[2].data[j] = d1 + t; ++j; } } } } //----- DC level shift for (comp = 0; comp < img.nComps; ++comp) { tileComp = &tile->tileComps[comp]; // signed: clip if (tileComp->sgned) { cover(89); minVal = -(1 << (tileComp->prec - 1)); maxVal = (1 << (tileComp->prec - 1)) - 1; dataPtr = tileComp->data; for (y = 0; y < tileComp->y1 - tileComp->y0; ++y) { for (x = 0; x < tileComp->x1 - tileComp->x0; ++x) { coeff = *dataPtr; if (tileComp->transform == 0) { cover(109); coeff >>= fracBits; } if (coeff < minVal) { cover(110); coeff = minVal; } else if (coeff > maxVal) { cover(111); coeff = maxVal; } *dataPtr++ = coeff; } } // unsigned: inverse DC level shift and clip } else { cover(90); maxVal = (1 << tileComp->prec) - 1; zeroVal = 1 << (tileComp->prec - 1); dataPtr = tileComp->data; for (y = 0; y < tileComp->y1 - tileComp->y0; ++y) { for (x = 0; x < tileComp->x1 - tileComp->x0; ++x) { coeff = *dataPtr; if (tileComp->transform == 0) { cover(112); coeff >>= fracBits; } coeff += zeroVal; if (coeff < 0) { cover(113); coeff = 0; } else if (coeff > maxVal) { cover(114); coeff = maxVal; } *dataPtr++ = coeff; } } } } return true; } bool JPXStream::readBoxHdr(unsigned int *boxType, unsigned int *boxLen, unsigned int *dataLen) { unsigned int len, lenH; if (!readULong(&len) || !readULong(boxType)) { return false; } if (len == 1) { if (!readULong(&lenH) || !readULong(&len)) { return false; } if (lenH) { error(errSyntaxError, getPos(), "JPX stream contains a box larger than 2^32 bytes"); return false; } *boxLen = len; *dataLen = len - 16; } else if (len == 0) { *boxLen = 0; *dataLen = 0; } else { *boxLen = len; *dataLen = len - 8; } return true; } int JPXStream::readMarkerHdr(int *segType, unsigned int *segLen) { int c; do { do { if ((c = bufStr->getChar()) == EOF) { return false; } } while (c != 0xff); do { if ((c = bufStr->getChar()) == EOF) { return false; } } while (c == 0xff); } while (c == 0x00); *segType = c; if ((c >= 0x30 && c <= 0x3f) || c == 0x4f || c == 0x92 || c == 0x93 || c == 0xd9) { *segLen = 0; return true; } return readUWord(segLen); } bool JPXStream::readUByte(unsigned int *x) { int c0; if ((c0 = bufStr->getChar()) == EOF) { return false; } *x = (unsigned int)c0; return true; } bool JPXStream::readByte(int *x) { int c0; if ((c0 = bufStr->getChar()) == EOF) { return false; } *x = c0; if (c0 & 0x80) { *x |= -1 - 0xff; } return true; } bool JPXStream::readUWord(unsigned int *x) { int c0, c1; if ((c0 = bufStr->getChar()) == EOF || (c1 = bufStr->getChar()) == EOF) { return false; } *x = (unsigned int)((c0 << 8) | c1); return true; } bool JPXStream::readULong(unsigned int *x) { int c0, c1, c2, c3; if ((c0 = bufStr->getChar()) == EOF || (c1 = bufStr->getChar()) == EOF || (c2 = bufStr->getChar()) == EOF || (c3 = bufStr->getChar()) == EOF) { return false; } *x = (unsigned int)((c0 << 24) | (c1 << 16) | (c2 << 8) | c3); return true; } bool JPXStream::readNBytes(int nBytes, bool signd, int *x) { int y, c, i; y = 0; for (i = 0; i < nBytes; ++i) { if ((c = bufStr->getChar()) == EOF) { return false; } y = (y << 8) + c; } if (signd) { if (y & (1 << (8 * nBytes - 1))) { y |= -1 << (8 * nBytes); } } *x = y; return true; } void JPXStream::startBitBuf(unsigned int byteCountA) { bitBufLen = 0; bitBufSkip = false; byteCount = byteCountA; } bool JPXStream::readBits(int nBits, unsigned int *x) { int c; while (bitBufLen < nBits) { if (byteCount == 0 || (c = bufStr->getChar()) == EOF) { return false; } --byteCount; if (bitBufSkip) { bitBuf = (bitBuf << 7) | (c & 0x7f); bitBufLen += 7; } else { bitBuf = (bitBuf << 8) | (c & 0xff); bitBufLen += 8; } bitBufSkip = c == 0xff; } *x = (bitBuf >> (bitBufLen - nBits)) & ((1 << nBits) - 1); bitBufLen -= nBits; return true; } void JPXStream::skipSOP() { int i; // SOP occurs at the start of the packet header, so we don't need to // worry about bit-stuff prior to it if (byteCount >= 6 && bufStr->lookChar(0) == 0xff && bufStr->lookChar(1) == 0x91) { for (i = 0; i < 6; ++i) { bufStr->getChar(); } byteCount -= 6; bitBufLen = 0; bitBufSkip = false; } } void JPXStream::skipEPH() { int i, k; k = bitBufSkip ? 1 : 0; if (byteCount >= (unsigned int)(k + 2) && bufStr->lookChar(k) == 0xff && bufStr->lookChar(k + 1) == 0x92) { for (i = 0; i < k + 2; ++i) { bufStr->getChar(); } byteCount -= k + 2; bitBufLen = 0; bitBufSkip = false; } } unsigned int JPXStream::finishBitBuf() { if (bitBufSkip) { bufStr->getChar(); --byteCount; } return byteCount; } poppler-24.02.0/poppler/JPXStream.h000066400000000000000000000265741455701731300170330ustar00rootroot00000000000000//======================================================================== // // JPXStream.h // // Copyright 2002-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) 2019, 2021 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 // //======================================================================== #ifndef JPXSTREAM_H #define JPXSTREAM_H #include "Object.h" #include "Stream.h" class JArithmeticDecoder; class JArithmeticDecoderStats; //------------------------------------------------------------------------ enum JPXColorSpaceType { jpxCSBiLevel = 0, jpxCSYCbCr1 = 1, jpxCSYCbCr2 = 3, jpxCSYCBCr3 = 4, jpxCSPhotoYCC = 9, jpxCSCMY = 11, jpxCSCMYK = 12, jpxCSYCCK = 13, jpxCSCIELab = 14, jpxCSsRGB = 16, jpxCSGrayscale = 17, jpxCSBiLevel2 = 18, jpxCSCIEJab = 19, jpxCSCISesRGB = 20, jpxCSROMMRGB = 21, jpxCSsRGBYCbCr = 22, jpxCSYPbPr1125 = 23, jpxCSYPbPr1250 = 24 }; struct JPXColorSpecCIELab { unsigned int rl, ol, ra, oa, rb, ob, il; }; struct JPXColorSpecEnumerated { JPXColorSpaceType type; // color space type union { JPXColorSpecCIELab cieLab; }; }; struct JPXColorSpec { unsigned int meth; // method int prec; // precedence union { JPXColorSpecEnumerated enumerated; }; }; //------------------------------------------------------------------------ struct JPXPalette { unsigned int nEntries; // number of entries in the palette unsigned int nComps; // number of components in each entry unsigned int *bpc; // bits per component, for each component int *c; // color data: // c[i*nComps+j] = entry i, component j }; //------------------------------------------------------------------------ struct JPXCompMap { unsigned int nChannels; // number of channels unsigned int *comp; // codestream components mapped to each channel unsigned int *type; // 0 for direct use, 1 for palette mapping unsigned int *pComp; // palette components to use }; //------------------------------------------------------------------------ struct JPXChannelDefn { unsigned int nChannels; // number of channels unsigned int *idx; // channel indexes unsigned int *type; // channel types unsigned int *assoc; // channel associations }; //------------------------------------------------------------------------ struct JPXTagTreeNode { bool finished; // true if this node is finished unsigned int val; // current value }; //------------------------------------------------------------------------ struct JPXCodeBlock { //----- size unsigned int x0, y0, x1, y1; // bounds //----- persistent state bool seen; // true if this code-block has already // been seen unsigned int lBlock; // base number of bits used for pkt data length unsigned int nextPass; // next coding pass //---- info from first packet unsigned int nZeroBitPlanes; // number of zero bit planes //----- info for the current packet unsigned int included; // code-block inclusion in this packet: // 0=not included, 1=included unsigned int nCodingPasses; // number of coding passes in this pkt unsigned int *dataLen; // data lengths (one per codeword segment) unsigned int dataLenSize; // size of the dataLen array //----- coefficient data int *coeffs; char *touched; // coefficient 'touched' flags unsigned short len; // coefficient length JArithmeticDecoder // arithmetic decoder *arithDecoder; JArithmeticDecoderStats // arithmetic decoder stats *stats; }; //------------------------------------------------------------------------ struct JPXSubband { //----- computed unsigned int x0, y0, x1, y1; // bounds unsigned int nXCBs, nYCBs; // number of code-blocks in the x and y // directions //----- tag trees unsigned int maxTTLevel; // max tag tree level JPXTagTreeNode *inclusion; // inclusion tag tree for each subband JPXTagTreeNode *zeroBitPlane; // zero-bit plane tag tree for each // subband //----- children JPXCodeBlock *cbs; // the code-blocks (len = nXCBs * nYCBs) }; //------------------------------------------------------------------------ struct JPXPrecinct { //----- computed unsigned int x0, y0, x1, y1; // bounds of the precinct //----- children JPXSubband *subbands; // the subbands }; //------------------------------------------------------------------------ struct JPXResLevel { //----- from the COD and COC segments (main and tile) unsigned int precinctWidth; // log2(precinct width) unsigned int precinctHeight; // log2(precinct height) //----- computed unsigned int x0, y0, x1, y1; // bounds of the tile-comp (for this res level) unsigned int bx0[3], by0[3], // subband bounds bx1[3], by1[3]; //---- children JPXPrecinct *precincts; // the precincts }; //------------------------------------------------------------------------ struct JPXTileComp { //----- from the SIZ segment bool sgned; // 1 for signed, 0 for unsigned unsigned int prec; // precision, in bits unsigned int hSep; // horizontal separation of samples unsigned int vSep; // vertical separation of samples //----- from the COD and COC segments (main and tile) unsigned int style; // coding style parameter (Scod / Scoc) unsigned int nDecompLevels; // number of decomposition levels unsigned int codeBlockW; // log2(code-block width) unsigned int codeBlockH; // log2(code-block height) unsigned int codeBlockStyle; // code-block style unsigned int transform; // wavelet transformation //----- from the QCD and QCC segments (main and tile) unsigned int quantStyle; // quantization style unsigned int *quantSteps; // quantization step size for each subband unsigned int nQuantSteps; // number of entries in quantSteps //----- computed unsigned int x0, y0, x1, y1; // bounds of the tile-comp, in ref coords unsigned int w; // x1 - x0 unsigned int cbW; // code-block width unsigned int cbH; // code-block height //----- image data int *data; // the decoded image data int *buf; // intermediate buffer for the inverse // transform //----- children JPXResLevel *resLevels; // the resolution levels // (len = nDecompLevels + 1) }; //------------------------------------------------------------------------ struct JPXTile { bool init; //----- from the COD segments (main and tile) unsigned int progOrder; // progression order unsigned int nLayers; // number of layers unsigned int multiComp; // multiple component transformation //----- computed unsigned int x0, y0, x1, y1; // bounds of the tile, in ref coords unsigned int maxNDecompLevels; // max number of decomposition levels used // in any component in this tile //----- progression order loop counters unsigned int comp; // component unsigned int res; // resolution level unsigned int precinct; // precinct unsigned int layer; // layer //----- children JPXTileComp *tileComps; // the tile-components (len = JPXImage.nComps) }; //------------------------------------------------------------------------ struct JPXImage { //----- from the SIZ segment unsigned int xSize, ySize; // size of reference grid unsigned int xOffset, yOffset; // image offset unsigned int xTileSize, yTileSize; // size of tiles unsigned int xTileOffset, // offset of first tile yTileOffset; unsigned int nComps; // number of components //----- computed unsigned int nXTiles; // number of tiles in x direction unsigned int nYTiles; // number of tiles in y direction //----- children JPXTile *tiles; // the tiles (len = nXTiles * nYTiles) }; //------------------------------------------------------------------------ class JPXStream : public FilterStream { public: JPXStream(Stream *strA); virtual ~JPXStream(); StreamKind getKind() const override { return strJPX; } void reset() override; void close() override; int getChar() override; int lookChar() override; GooString *getPSFilter(int psLevel, const char *indent) override; bool isBinary(bool last = true) const override; void getImageParams(int *bitsPerComponent, StreamColorSpaceMode *csMode) override; private: void fillReadBuf(); void getImageParams2(int *bitsPerComponent, StreamColorSpaceMode *csMode); bool readBoxes(); bool readColorSpecBox(unsigned int dataLen); bool readCodestream(unsigned int len); bool readTilePart(); bool readTilePartData(unsigned int tileIdx, unsigned int tilePartLen, bool tilePartToEOC); bool readCodeBlockData(JPXTileComp *tileComp, JPXResLevel *resLevel, JPXPrecinct *precinct, JPXSubband *subband, unsigned int res, unsigned int sb, JPXCodeBlock *cb); void inverseTransform(JPXTileComp *tileComp); void inverseTransformLevel(JPXTileComp *tileComp, unsigned int r, JPXResLevel *resLevel); void inverseTransform1D(JPXTileComp *tileComp, int *data, unsigned int offset, unsigned int n); bool inverseMultiCompAndDC(JPXTile *tile); bool readBoxHdr(unsigned int *boxType, unsigned int *boxLen, unsigned int *dataLen); int readMarkerHdr(int *segType, unsigned int *segLen); bool readUByte(unsigned int *x); bool readByte(int *x); bool readUWord(unsigned int *x); bool readULong(unsigned int *x); bool readNBytes(int nBytes, bool signd, int *x); void startBitBuf(unsigned int byteCountA); bool readBits(int nBits, unsigned int *x); void skipSOP(); void skipEPH(); unsigned int finishBitBuf(); BufStream *bufStr; // buffered stream (for lookahead) unsigned int nComps; // number of components unsigned int *bpc; // bits per component, for each component unsigned int width, height; // image size bool haveImgHdr; // set if a JP2/JPX image header has been // found JPXColorSpec cs; // color specification bool haveCS; // set if a color spec has been found JPXPalette palette; // the palette bool havePalette; // set if a palette has been found JPXCompMap compMap; // the component mapping bool haveCompMap; // set if a component mapping has been found JPXChannelDefn channelDefn; // channel definition bool haveChannelDefn; // set if a channel defn has been found JPXImage img; // JPEG2000 decoder data unsigned int bitBuf; // buffer for bit reads int bitBufLen; // number of bits in bitBuf bool bitBufSkip; // true if next bit should be skipped // (for bit stuffing) unsigned int byteCount; // number of available bytes left unsigned int curX, curY, curComp; // current position for lookChar/getChar unsigned int readBuf; // read buffer unsigned int readBufLen; // number of valid bits in readBuf }; #endif poppler-24.02.0/poppler/JSInfo.cc000066400000000000000000000226101455701731300164670ustar00rootroot00000000000000//======================================================================== // // JSInfo.cc // // This file is licensed under the GPLv2 or later // // Copyright (C) 2013 Adrian Johnson // Copyright (C) 2017, 2020, 2021 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 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 // //======================================================================== #include "config.h" #include #include "Object.h" #include "Dict.h" #include "Annot.h" #include "PDFDoc.h" #include "JSInfo.h" #include "Link.h" #include "Form.h" #include "UnicodeMap.h" #include "UTF.h" // #include "Win32Console.h" JSInfo::JSInfo(PDFDoc *docA, int firstPage) { doc = docA; currentPage = firstPage + 1; } JSInfo::~JSInfo() { } void JSInfo::printJS(const GooString *js) { char buf[8]; if (!js || !js->c_str()) { return; } std::vector u = TextStringToUCS4(js->toStr()); for (auto &c : u) { int n = uniMap->mapUnicode(c, buf, sizeof(buf)); fwrite(buf, 1, n, file); } } void JSInfo::scanLinkAction(LinkAction *link, const char *action) { if (!link) { return; } if (link->getKind() == actionJavaScript) { hasJS = true; if (print) { LinkJavaScript *linkjs = static_cast(link); if (linkjs->isOk()) { const std::string &s = linkjs->getScript(); fprintf(file, "%s:\n", action); GooString gooS = GooString(s); printJS(&gooS); fputs("\n\n", file); } } } if (link->getKind() == actionRendition) { LinkRendition *linkr = static_cast(link); if (!linkr->getScript().empty()) { hasJS = true; if (print) { fprintf(file, "%s (Rendition):\n", action); const GooString s(linkr->getScript()); printJS(&s); fputs("\n\n", file); } } } } void JSInfo::scanJS(int nPages) { print = false; file = nullptr; onlyFirstJS = false; scan(nPages); } void JSInfo::scanJS(int nPages, FILE *fout, const UnicodeMap *uMap) { print = true; file = fout; uniMap = uMap; onlyFirstJS = false; scan(nPages); } void JSInfo::scanJS(int nPages, bool stopOnFirstJS) { print = false; file = nullptr; onlyFirstJS = stopOnFirstJS; scan(nPages); } void JSInfo::scan(int nPages) { Page *page; Annots *annots; int lastPage; hasJS = false; // Names int numNames = doc->getCatalog()->numJS(); if (numNames > 0) { hasJS = true; if (onlyFirstJS) { return; } 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); printJS(js); delete js; fputs("\n\n", file); } } } // document actions 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"); scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionPrintDocumentStart).get(), "Before Print Document"); scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionPrintDocumentFinish).get(), "After Print Document"); if (onlyFirstJS && hasJS) { return; } // form field actions if (doc->getCatalog()->getFormType() == Catalog::AcroForm) { Form *form = doc->getCatalog()->getForm(); for (int i = 0; i < form->getNumFields(); i++) { FormField *field = form->getRootField(i); for (int j = 0; j < field->getNumWidgets(); j++) { FormWidget *widget = field->getWidget(j); scanLinkAction(widget->getActivationAction(), "Field Activated"); scanLinkAction(widget->getAdditionalAction(Annot::actionFieldModified).get(), "Field Modified"); scanLinkAction(widget->getAdditionalAction(Annot::actionFormatField).get(), "Format Field"); scanLinkAction(widget->getAdditionalAction(Annot::actionValidateField).get(), "Validate Field"); scanLinkAction(widget->getAdditionalAction(Annot::actionCalculateField).get(), "Calculate Field"); if (onlyFirstJS && hasJS) { return; } } } } // scan pages if (currentPage > doc->getNumPages()) { return; } lastPage = currentPage + nPages; if (lastPage > doc->getNumPages() + 1) { lastPage = doc->getNumPages() + 1; } for (int pg = currentPage; pg < lastPage; ++pg) { page = doc->getPage(pg); if (!page) { continue; } // page actions (open, close) scanLinkAction(page->getAdditionalAction(Page::actionOpenPage).get(), "Page Open"); scanLinkAction(page->getAdditionalAction(Page::actionClosePage).get(), "Page Close"); if (onlyFirstJS && hasJS) { return; } // annotation actions (links, screen, widget) annots = page->getAnnots(); for (Annot *a : annots->getAnnots()) { if (a->getType() == Annot::typeLink) { AnnotLink *annot = static_cast(a); scanLinkAction(annot->getAction(), "Link Annotation Activated"); if (onlyFirstJS && hasJS) { return; } } else if (a->getType() == Annot::typeScreen) { AnnotScreen *annot = static_cast(a); scanLinkAction(annot->getAction(), "Screen Annotation Activated"); scanLinkAction(annot->getAdditionalAction(Annot::actionCursorEntering).get(), "Screen Annotation Cursor Enter"); scanLinkAction(annot->getAdditionalAction(Annot::actionCursorLeaving).get(), "Screen Annotation Cursor Leave"); scanLinkAction(annot->getAdditionalAction(Annot::actionMousePressed).get(), "Screen Annotation Mouse Pressed"); scanLinkAction(annot->getAdditionalAction(Annot::actionMouseReleased).get(), "Screen Annotation Mouse Released"); scanLinkAction(annot->getAdditionalAction(Annot::actionFocusIn).get(), "Screen Annotation Focus In"); scanLinkAction(annot->getAdditionalAction(Annot::actionFocusOut).get(), "Screen Annotation Focus Out"); scanLinkAction(annot->getAdditionalAction(Annot::actionPageOpening).get(), "Screen Annotation Page Open"); scanLinkAction(annot->getAdditionalAction(Annot::actionPageClosing).get(), "Screen Annotation Page Close"); scanLinkAction(annot->getAdditionalAction(Annot::actionPageVisible).get(), "Screen Annotation Page Visible"); scanLinkAction(annot->getAdditionalAction(Annot::actionPageInvisible).get(), "Screen Annotation Page Invisible"); if (onlyFirstJS && hasJS) { return; } } else if (a->getType() == Annot::typeWidget) { AnnotWidget *annot = static_cast(a); scanLinkAction(annot->getAction(), "Widget Annotation Activated"); scanLinkAction(annot->getAdditionalAction(Annot::actionCursorEntering).get(), "Widget Annotation Cursor Enter"); scanLinkAction(annot->getAdditionalAction(Annot::actionCursorLeaving).get(), "Widget Annotation Cursor Leave"); scanLinkAction(annot->getAdditionalAction(Annot::actionMousePressed).get(), "Widget Annotation Mouse Pressed"); scanLinkAction(annot->getAdditionalAction(Annot::actionMouseReleased).get(), "Widget Annotation Mouse Released"); scanLinkAction(annot->getAdditionalAction(Annot::actionFocusIn).get(), "Widget Annotation Focus In"); scanLinkAction(annot->getAdditionalAction(Annot::actionFocusOut).get(), "Widget Annotation Focus Out"); scanLinkAction(annot->getAdditionalAction(Annot::actionPageOpening).get(), "Widget Annotation Page Open"); scanLinkAction(annot->getAdditionalAction(Annot::actionPageClosing).get(), "Widget Annotation Page Close"); scanLinkAction(annot->getAdditionalAction(Annot::actionPageVisible).get(), "Widget Annotation Page Visible"); scanLinkAction(annot->getAdditionalAction(Annot::actionPageInvisible).get(), "Widget Annotation Page Invisible"); if (onlyFirstJS && hasJS) { return; } } } } currentPage = lastPage; } bool JSInfo::containsJS() { return hasJS; } poppler-24.02.0/poppler/JSInfo.h000066400000000000000000000034041455701731300163310ustar00rootroot00000000000000//======================================================================== // // JSInfo.h // // This file is licensed under the GPLv2 or later // // Copyright (C) 2013 Adrian Johnson // Copyright (C) 2020, 2021 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 // // 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 JS_INFO_H #define JS_INFO_H #include #include "Object.h" #include "PDFDoc.h" #include "poppler_private_export.h" #include "Link.h" #include "UnicodeMap.h" class PDFDoc; class POPPLER_PRIVATE_EXPORT JSInfo { public: // Constructor. explicit JSInfo(PDFDoc *doc, int firstPage = 0); // Destructor. ~JSInfo(); // scan for JS in the PDF void scanJS(int nPages); // scan and print JS in the PDF void scanJS(int nPages, FILE *fout, const UnicodeMap *uMap); // scan but exit after finding first JS in the PDF void scanJS(int nPages, bool stopOnFirstJS); // return true if PDF contains JavaScript bool containsJS(); private: PDFDoc *doc; int currentPage; bool hasJS; bool print; FILE *file; const UnicodeMap *uniMap; bool onlyFirstJS; /* stop scanning after finding first JS */ void scan(int nPages); void scanLinkAction(LinkAction *link, const char *action); void printJS(const GooString *js); }; #endif poppler-24.02.0/poppler/Lexer.cc000066400000000000000000000453021455701731300164210ustar00rootroot00000000000000//======================================================================== // // Lexer.cc // // 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-2010, 2012-2014, 2017-2019 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 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 // //======================================================================== #include #include #include #include #include #include #include "Lexer.h" #include "Error.h" #include "UTF.h" #include "XRef.h" //------------------------------------------------------------------------ // A '1' in this array means the character is white space. A '1' or // '2' means the character ends a name or command. static const char specialChars[256] = { 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, // 0x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x 1, 0, 0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, // 2x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, // 3x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, // 5x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, // 7x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ax 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // bx 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // cx 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // dx 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ex 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // fx }; static const int IntegerSafeLimit = (INT_MAX - 9) / 10; static const long long LongLongSafeLimit = (LLONG_MAX - 9) / 10; //------------------------------------------------------------------------ // Lexer //------------------------------------------------------------------------ Lexer::Lexer(XRef *xrefA, Stream *str) { lookCharLastValueCached = LOOK_VALUE_NOT_CACHED; xref = xrefA; curStr = Object(str); streams = new Array(xref); streams->add(curStr.copy()); strPtr = 0; freeArray = true; curStr.streamReset(); } Lexer::Lexer(XRef *xrefA, Object *obj) { lookCharLastValueCached = LOOK_VALUE_NOT_CACHED; xref = xrefA; if (obj->isStream()) { streams = new Array(xref); freeArray = true; streams->add(obj->copy()); } else { streams = obj->getArray(); freeArray = false; } strPtr = 0; if (streams->getLength() > 0) { curStr = streams->get(strPtr); if (curStr.isStream()) { curStr.streamReset(); } } } Lexer::~Lexer() { if (curStr.isStream()) { curStr.streamClose(); } if (freeArray) { delete streams; } } int Lexer::getChar(bool comesFromLook) { int c; if (LOOK_VALUE_NOT_CACHED != lookCharLastValueCached) { c = lookCharLastValueCached; lookCharLastValueCached = LOOK_VALUE_NOT_CACHED; return c; } c = EOF; while (curStr.isStream() && (c = curStr.streamGetChar()) == EOF) { if (comesFromLook == true) { return EOF; } else { curStr.streamClose(); curStr = Object(); ++strPtr; if (strPtr < streams->getLength()) { curStr = streams->get(strPtr); if (curStr.isStream()) { curStr.streamReset(); } } } } return c; } int Lexer::lookChar() { if (LOOK_VALUE_NOT_CACHED != lookCharLastValueCached) { return lookCharLastValueCached; } lookCharLastValueCached = getChar(true); if (lookCharLastValueCached == EOF) { lookCharLastValueCached = LOOK_VALUE_NOT_CACHED; return EOF; } else { return lookCharLastValueCached; } } Object Lexer::getObj(int objNum) { char *p; int c, c2; bool comment, neg, done, overflownInteger, overflownLongLong; int numParen; int xi; long long xll = 0; double xf = 0, scale; int n, m; // skip whitespace and comments comment = false; while (true) { if ((c = getChar()) == EOF) { return Object(objEOF); } if (comment) { if (c == '\r' || c == '\n') { comment = false; } } else if (c == '%') { comment = true; } else if (specialChars[c] != 1) { break; } } // start reading token switch (c) { // number case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '+': case '-': case '.': overflownInteger = false; overflownLongLong = false; neg = false; xi = 0; if (c == '-') { neg = true; } else if (c == '.') { goto doReal; } else if (c != '+') { xi = c - '0'; } while (true) { c = lookChar(); if (isdigit(c)) { getChar(); if (unlikely(overflownLongLong)) { xf = xf * 10.0 + (c - '0'); } else if (unlikely(overflownInteger)) { if (unlikely(xll > LongLongSafeLimit) && (xll > (LLONG_MAX - (c - '0')) / 10)) { overflownLongLong = true; xf = xll * 10.0 + (c - '0'); } else { xll = xll * 10 + (c - '0'); } } else { if (unlikely(xi > IntegerSafeLimit) && (xi > (INT_MAX - (c - '0')) / 10.0)) { overflownInteger = true; xll = xi * 10LL + (c - '0'); } else { xi = xi * 10 + (c - '0'); } } } else if (c == '.') { getChar(); goto doReal; } else { break; } } if (neg) { xi = -xi; xll = -xll; xf = -xf; } if (unlikely(overflownInteger)) { if (overflownLongLong) { return Object(xf); } else { if (unlikely(xll == INT_MIN)) { return Object(static_cast(INT_MIN)); } else { return Object(xll); } } } else { return Object(xi); } break; doReal: if (likely(!overflownInteger)) { xf = xi; } else if (!overflownLongLong) { xf = xll; } scale = 0.1; while (true) { c = lookChar(); if (c == '-') { // ignore minus signs in the middle of numbers to match // Adobe's behavior error(errSyntaxWarning, getPos(), "Badly formatted number"); getChar(); continue; } if (!isdigit(c)) { break; } getChar(); xf = xf + scale * (c - '0'); scale *= 0.1; } if (neg) { xf = -xf; } return Object(xf); break; // string case '(': { p = tokBuf; n = 0; numParen = 1; done = false; std::string s; do { c2 = EOF; switch (c = getChar()) { case EOF: #if 0 // This breaks some PDF files, e.g., ones from Photoshop. case '\r': case '\n': #endif error(errSyntaxError, getPos(), "Unterminated string"); done = true; break; case '(': ++numParen; c2 = c; break; case ')': if (--numParen == 0) { done = true; } else { c2 = c; } break; case '\\': switch (c = getChar()) { case 'n': c2 = '\n'; break; case 'r': c2 = '\r'; break; case 't': c2 = '\t'; break; case 'b': c2 = '\b'; break; case 'f': c2 = '\f'; break; case '\\': case '(': case ')': c2 = c; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': c2 = c - '0'; c = lookChar(); if (c >= '0' && c <= '7') { getChar(); c2 = (c2 << 3) + (c - '0'); c = lookChar(); if (c >= '0' && c <= '7') { getChar(); c2 = (c2 << 3) + (c - '0'); } } break; case '\r': c = lookChar(); if (c == '\n') { getChar(); } break; case '\n': break; case EOF: error(errSyntaxError, getPos(), "Unterminated string"); done = true; break; default: c2 = c; break; } break; default: c2 = c; break; } if (c2 != EOF) { if (n == tokBufSize) { s.append(tokBuf, tokBufSize); p = tokBuf; n = 0; // we are growing see if the document is not malformed and we are growing too much if (objNum > 0 && xref != nullptr) { const int newObjNum = xref->getNumEntry(getPos()); if (newObjNum != objNum) { error(errSyntaxError, getPos(), "Unterminated string"); done = true; s.clear(); n = -2; } } } *p++ = (char)c2; ++n; } } while (!done); if (n >= 0) { s.append(tokBuf, n); // Check utf8 if (isUtf8WithBom(s)) { s = utf8ToUtf16WithBom(s); } return Object(std::move(s)); } else { return Object(objEOF); } break; } // name case '/': { p = tokBuf; n = 0; std::string s; while ((c = lookChar()) != EOF && !specialChars[c]) { getChar(); if (c == '#') { c2 = lookChar(); if (c2 >= '0' && c2 <= '9') { c = c2 - '0'; } else if (c2 >= 'A' && c2 <= 'F') { c = c2 - 'A' + 10; } else if (c2 >= 'a' && c2 <= 'f') { c = c2 - 'a' + 10; } else { goto notEscChar; } getChar(); c <<= 4; c2 = getChar(); if (c2 >= '0' && c2 <= '9') { c += c2 - '0'; } else if (c2 >= 'A' && c2 <= 'F') { c += c2 - 'A' + 10; } else if (c2 >= 'a' && c2 <= 'f') { c += c2 - 'a' + 10; } else { error(errSyntaxError, getPos(), "Illegal digit in hex char in name"); } } notEscChar: // the PDF spec claims that names are limited to 127 chars, but // Distiller 8 will produce longer names, and Acrobat 8 will // accept longer names ++n; if (n < tokBufSize) { *p++ = c; } else if (n == tokBufSize) { error(errSyntaxError, getPos(), "Warning: name token is longer than what the specification says it can be"); *p = c; s = std::string(tokBuf, n); } else { // 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()); return obj; } break; } // array punctuation case '[': case ']': tokBuf[0] = c; tokBuf[1] = '\0'; return Object(objCmd, tokBuf); break; // hex string or dict punctuation case '<': c = lookChar(); // dict punctuation if (c == '<') { getChar(); tokBuf[0] = tokBuf[1] = '<'; tokBuf[2] = '\0'; return Object(objCmd, tokBuf); // hex string } else { p = tokBuf; m = n = 0; c2 = 0; std::string s; while (true) { c = getChar(); if (c == '>') { break; } else if (c == EOF) { error(errSyntaxError, getPos(), "Unterminated hex string"); break; } else if (specialChars[c] != 1) { c2 = c2 << 4; if (c >= '0' && c <= '9') { c2 += c - '0'; } else if (c >= 'A' && c <= 'F') { c2 += c - 'A' + 10; } else if (c >= 'a' && c <= 'f') { c2 += c - 'a' + 10; } else { error(errSyntaxError, getPos(), "Illegal character <{0:02x}> in hex string", c); } if (++m == 2) { if (n == tokBufSize) { s.append(tokBuf, tokBufSize); p = tokBuf; n = 0; } *p++ = (char)c2; ++n; c2 = 0; m = 0; } } } s.append(tokBuf, n); if (m == 1) { s.push_back((char)(c2 << 4)); } if (isUtf8WithBom(s)) { s = utf8ToUtf16WithBom(s); } return Object(std::move(s)); } break; // dict punctuation case '>': c = lookChar(); if (c == '>') { getChar(); tokBuf[0] = tokBuf[1] = '>'; tokBuf[2] = '\0'; return Object(objCmd, tokBuf); } else { error(errSyntaxError, getPos(), "Illegal character '>'"); return Object(objError); } break; // error case ')': case '{': case '}': error(errSyntaxError, getPos(), "Illegal character '{0:c}'", c); return Object(objError); break; // command default: p = tokBuf; *p++ = c; n = 1; while ((c = lookChar()) != EOF && !specialChars[c]) { getChar(); if (++n == tokBufSize) { error(errSyntaxError, getPos(), "Command token too long"); break; } *p++ = c; } *p = '\0'; if (tokBuf[0] == 't' && !strcmp(tokBuf, "true")) { return Object(true); } else if (tokBuf[0] == 'f' && !strcmp(tokBuf, "false")) { return Object(false); } else if (tokBuf[0] == 'n' && !strcmp(tokBuf, "null")) { return Object(objNull); } else { return Object(objCmd, tokBuf); } break; } return Object(); } Object Lexer::getObj(const char *cmdA, int objNum) { char *p; int c; bool comment; int n; // skip whitespace and comments comment = false; const char *cmd1 = tokBuf; *tokBuf = 0; while (strcmp(cmdA, cmd1) && (objNum < 0 || (xref && xref->getNumEntry(getPos()) == objNum))) { while (true) { if ((c = getChar()) == EOF) { return Object(objEOF); } if (comment) { if (c == '\r' || c == '\n') { comment = false; } } else if (c == '%') { comment = true; } else if (specialChars[c] != 1) { break; } } p = tokBuf; *p++ = c; n = 1; while ((c = lookChar()) != EOF && specialChars[c] == 0) { getChar(); if (++n == tokBufSize) { break; } *p++ = c; } *p = '\0'; } return Object(objCmd, tokBuf); } void Lexer::skipToNextLine() { int c; while (true) { c = getChar(); if (c == EOF || c == '\n') { return; } if (c == '\r') { if ((c = lookChar()) == '\n') { getChar(); } return; } } } bool Lexer::isSpace(int c) { return c >= 0 && c <= 0xff && specialChars[c] == 1; } poppler-24.02.0/poppler/Lexer.h000066400000000000000000000070051455701731300162610ustar00rootroot00000000000000//======================================================================== // // Lexer.h // // 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, 2007, 2010, 2013, 2017-2019 Albert Astals Cid // Copyright (C) 2006 Krzysztof Kowalczyk // Copyright (C) 2013 Adrian Johnson // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2019 Adam Reichold // // 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 LEXER_H #define LEXER_H #include "Object.h" #include "Stream.h" class XRef; #define tokBufSize 128 // size of token buffer //------------------------------------------------------------------------ // Lexer //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT Lexer { public: // Construct a lexer for a single stream. Deletes the stream when // lexer is deleted. Lexer(XRef *xrefA, Stream *str); // Construct a lexer for a stream or array of streams (assumes obj // is either a stream or array of streams). Lexer(XRef *xrefA, Object *obj); // Destructor. ~Lexer(); Lexer(const Lexer &) = delete; Lexer &operator=(const Lexer &) = delete; // Get the next object from the input stream. Object getObj(int objNum = -1); Object getObj(const char *cmdA, int objNum); template Object getObj(T) = delete; // Skip to the beginning of the next line in the input stream. void skipToNextLine(); // Skip over one character. void skipChar() { getChar(); } // Get stream. Stream *getStream() { return curStr.isStream() ? curStr.getStream() : nullptr; } // Get current position in file. This is only used for error // messages. Goffset getPos() const { return curStr.isStream() ? curStr.getStream()->getPos() : -1; } // Set position in file. void setPos(Goffset pos) { if (curStr.isStream()) { curStr.getStream()->setPos(pos); } } // Returns true if is a whitespace character. static bool isSpace(int c); // often (e.g. ~30% on PDF Refernce 1.6 pdf file from Adobe site) getChar // is called right after lookChar. In order to avoid expensive re-doing // getChar() of underlying stream, we cache the last value found by // lookChar() in lookCharLastValueCached. A special value // LOOK_VALUE_NOT_CACHED that should never be part of stream indicates // that no value was cached static const int LOOK_VALUE_NOT_CACHED = -3; int lookCharLastValueCached; XRef *getXRef() const { return xref; } bool hasXRef() const { return xref != nullptr; } private: int getChar(bool comesFromLook = false); int lookChar(); Array *streams; // array of input streams int strPtr; // index of current stream Object curStr; // current stream bool freeArray; // should lexer free the streams array? char tokBuf[tokBufSize]; // temporary token buffer XRef *xref; }; #endif poppler-24.02.0/poppler/Linearization.cc000066400000000000000000000127741455701731300201610ustar00rootroot00000000000000//======================================================================== // // Linearization.cc // // This file is licensed under the GPLv2 or later // // Copyright 2010, 2012 Hib Eris // Copyright 2015 Jason Crain // Copyright 2017, 2019 Albert Astals Cid // Copyright 2019 Adam Reichold // Copyright 2019 Even Rouault // //======================================================================== #include "Linearization.h" #include "Parser.h" #include "Lexer.h" //------------------------------------------------------------------------ // Linearization //------------------------------------------------------------------------ Linearization::Linearization(BaseStream *str) { Parser *parser; str->reset(); parser = new Parser(nullptr, str->makeSubStream(str->getStart(), false, 0, Object(objNull)), false); Object obj1 = parser->getObj(); Object obj2 = parser->getObj(); Object obj3 = parser->getObj(); linDict = parser->getObj(); if (obj1.isInt() && obj2.isInt() && obj3.isCmd("obj") && linDict.isDict()) { Object obj5 = linDict.dictLookup("Linearized"); if (!(obj5.isNum() && obj5.getNum() > 0)) { linDict.setToNull(); } } else { linDict.setToNull(); } delete parser; } Linearization::~Linearization() { } unsigned int Linearization::getLength() const { if (!linDict.isDict()) { return 0; } int length; if (linDict.getDict()->lookupInt("L", nullptr, &length) && length > 0) { return length; } else { error(errSyntaxWarning, -1, "Length in linearization table is invalid"); return 0; } } unsigned int Linearization::getHintsOffset() const { int hintsOffset; Object obj1, obj2; if (linDict.isDict() && (obj1 = linDict.dictLookup("H"), obj1.isArray()) && obj1.arrayGetLength() >= 2 && (obj2 = obj1.arrayGet(0), obj2.isInt()) && obj2.getInt() > 0) { hintsOffset = obj2.getInt(); } else { error(errSyntaxWarning, -1, "Hints table offset in linearization table is invalid"); hintsOffset = 0; } return hintsOffset; } unsigned int Linearization::getHintsLength() const { int hintsLength; Object obj1, obj2; if (linDict.isDict() && (obj1 = linDict.dictLookup("H"), obj1.isArray()) && obj1.arrayGetLength() >= 2 && (obj2 = obj1.arrayGet(1), obj2.isInt()) && obj2.getInt() > 0) { hintsLength = obj2.getInt(); } else { error(errSyntaxWarning, -1, "Hints table length in linearization table is invalid"); hintsLength = 0; } return hintsLength; } unsigned int Linearization::getHintsOffset2() const { int hintsOffset2 = 0; // default to 0 Object obj1; if (linDict.isDict() && (obj1 = linDict.dictLookup("H"), obj1.isArray()) && obj1.arrayGetLength() >= 4) { Object obj2 = obj1.arrayGet(2); if (obj2.isInt() && obj2.getInt() > 0) { hintsOffset2 = obj2.getInt(); } else { error(errSyntaxWarning, -1, "Second hints table offset in linearization table is invalid"); hintsOffset2 = 0; } } return hintsOffset2; } unsigned int Linearization::getHintsLength2() const { int hintsLength2 = 0; // default to 0 Object obj1; if (linDict.isDict() && (obj1 = linDict.dictLookup("H"), obj1.isArray()) && obj1.arrayGetLength() >= 4) { Object obj2 = obj1.arrayGet(3); if (obj2.isInt() && obj2.getInt() > 0) { hintsLength2 = obj2.getInt(); } else { error(errSyntaxWarning, -1, "Second hints table length in linearization table is invalid"); hintsLength2 = 0; } } return hintsLength2; } int Linearization::getObjectNumberFirst() const { int objectNumberFirst = 0; if (linDict.isDict() && linDict.getDict()->lookupInt("O", nullptr, &objectNumberFirst) && objectNumberFirst > 0) { return objectNumberFirst; } else { error(errSyntaxWarning, -1, "Object number of first page in linearization table is invalid"); return 0; } } unsigned int Linearization::getEndFirst() const { int pageEndFirst = 0; if (linDict.isDict() && linDict.getDict()->lookupInt("E", nullptr, &pageEndFirst) && pageEndFirst > 0) { return pageEndFirst; } else { error(errSyntaxWarning, -1, "First page end offset in linearization table is invalid"); return 0; } } int Linearization::getNumPages() const { int numPages = 0; if (linDict.isDict() && linDict.getDict()->lookupInt("N", nullptr, &numPages) && numPages > 0) { return numPages; } else { error(errSyntaxWarning, -1, "Page count in linearization table is invalid"); return 0; } } unsigned int Linearization::getMainXRefEntriesOffset() const { int mainXRefEntriesOffset = 0; if (linDict.isDict() && linDict.getDict()->lookupInt("T", nullptr, &mainXRefEntriesOffset) && mainXRefEntriesOffset > 0) { return mainXRefEntriesOffset; } else { error(errSyntaxWarning, -1, "Main Xref offset in linearization table is invalid"); return 0; } } int Linearization::getPageFirst() const { int pageFirst = 0; // Optional, defaults to 0. if (linDict.isDict()) { linDict.getDict()->lookupInt("P", nullptr, &pageFirst); } if ((pageFirst < 0) || (pageFirst >= getNumPages())) { error(errSyntaxWarning, -1, "First page in linearization table is invalid"); return 0; } return pageFirst; } poppler-24.02.0/poppler/Linearization.h000066400000000000000000000021351455701731300200110ustar00rootroot00000000000000//======================================================================== // // Linearization.h // // This file is licensed under the GPLv2 or later // // Copyright 2010 Hib Eris // Copyright 2019, 2021 Albert Astals Cid // //======================================================================== #ifndef LINEARIZATION_H #define LINEARIZATION_H #include "Object.h" class BaseStream; //------------------------------------------------------------------------ // Linearization //------------------------------------------------------------------------ class Linearization { public: explicit Linearization(BaseStream *str); ~Linearization(); unsigned int getLength() const; unsigned int getHintsOffset() const; unsigned int getHintsLength() const; unsigned int getHintsOffset2() const; unsigned int getHintsLength2() const; int getObjectNumberFirst() const; unsigned int getEndFirst() const; int getNumPages() const; unsigned int getMainXRefEntriesOffset() const; int getPageFirst() const; private: Object linDict; }; #endif poppler-24.02.0/poppler/Link.cc000066400000000000000000000670751455701731300162520ustar00rootroot00000000000000//======================================================================== // // Link.cc // // 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, 2008 Pino Toscano // Copyright (C) 2007, 2010, 2011 Carlos Garcia Campos // Copyright (C) 2008 Hugo Mercier // Copyright (C) 2008-2010, 2012-2014, 2016-2023 Albert Astals Cid // Copyright (C) 2009 Kovid Goyal // Copyright (C) 2009 Ilya Gorenbein // Copyright (C) 2012 Tobias Koening // 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) 2018, 2020 Adam Reichold // Copyright (C) 2019, 2020 Oliver Sander // Copyright (C) 2020 Marek Kasik // // 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 #include #include #include "goo/gmem.h" #include "goo/GooString.h" #include "Error.h" #include "Object.h" #include "Array.h" #include "Dict.h" #include "Link.h" #include "Sound.h" #include "FileSpec.h" #include "Rendition.h" #include "Annot.h" //------------------------------------------------------------------------ // LinkAction //------------------------------------------------------------------------ LinkAction::LinkAction() = default; LinkAction::~LinkAction() = default; std::unique_ptr LinkAction::parseDest(const Object *obj) { auto action = std::unique_ptr(new LinkGoTo(obj)); if (!action->isOk()) { action.reset(); } return action; } std::unique_ptr LinkAction::parseAction(const Object *obj, const std::optional &baseURI) { std::set seenNextActions; return parseAction(obj, baseURI, &seenNextActions); } std::unique_ptr LinkAction::parseAction(const Object *obj, const std::optional &baseURI, std::set *seenNextActions) { if (!obj->isDict()) { error(errSyntaxWarning, -1, "parseAction: Bad annotation action for URI '{0:s}'", baseURI ? baseURI->c_str() : "NULL"); return nullptr; } std::unique_ptr action; Object obj2 = obj->dictLookup("S"); // GoTo action if (obj2.isName("GoTo")) { Object obj3 = obj->dictLookup("D"); action = std::make_unique(&obj3); // GoToR action } else if (obj2.isName("GoToR")) { Object obj3 = obj->dictLookup("F"); Object obj4 = obj->dictLookup("D"); action = std::make_unique(&obj3, &obj4); // Launch action } else if (obj2.isName("Launch")) { action = std::make_unique(obj); // URI action } else if (obj2.isName("URI")) { Object obj3 = obj->dictLookup("URI"); action = std::make_unique(&obj3, baseURI); // Named action } else if (obj2.isName("Named")) { Object obj3 = obj->dictLookup("N"); action = std::make_unique(&obj3); // Movie action } else if (obj2.isName("Movie")) { action = std::make_unique(obj); // Rendition action } else if (obj2.isName("Rendition")) { action = std::make_unique(obj); // Sound action } else if (obj2.isName("Sound")) { action = std::make_unique(obj); // JavaScript action } else if (obj2.isName("JavaScript")) { Object obj3 = obj->dictLookup("JS"); action = std::make_unique(&obj3); // Set-OCG-State action } else if (obj2.isName("SetOCGState")) { action = std::make_unique(obj); // Hide action } else if (obj2.isName("Hide")) { action = std::make_unique(obj); // ResetForm action } else if (obj2.isName("ResetForm")) { action = std::make_unique(obj); // unknown action } else if (obj2.isName()) { action = std::make_unique(obj2.getName()); // action is missing or wrong type } else { error(errSyntaxWarning, -1, "parseAction: Unknown annotation action object: URI = '{0:s}'", baseURI ? baseURI->c_str() : "NULL"); action = nullptr; } if (action && !action->isOk()) { action.reset(); return nullptr; } if (!action) { return nullptr; } // parse the next actions const Object nextObj = obj->dictLookup("Next"); std::vector> actionList; if (nextObj.isDict()) { // Prevent circles in the tree by checking the ref against used refs in // our current tree branch. const Object &nextRefObj = obj->dictLookupNF("Next"); if (nextRefObj.isRef()) { const Ref ref = nextRefObj.getRef(); if (!seenNextActions->insert(ref.num).second) { error(errSyntaxWarning, -1, "parseAction: Circular next actions detected."); return action; } } actionList.reserve(1); actionList.push_back(parseAction(&nextObj, {}, seenNextActions)); } else if (nextObj.isArray()) { const Array *a = nextObj.getArray(); const int n = a->getLength(); actionList.reserve(n); for (int i = 0; i < n; ++i) { const Object obj3 = a->get(i); if (!obj3.isDict()) { error(errSyntaxWarning, -1, "parseAction: Next array does not contain only dicts"); continue; } // Similar circle check as above. const Object &obj3Ref = a->getNF(i); if (obj3Ref.isRef()) { const Ref ref = obj3Ref.getRef(); if (!seenNextActions->insert(ref.num).second) { error(errSyntaxWarning, -1, "parseAction: Circular next actions detected in array."); return action; } } actionList.push_back(parseAction(&obj3, {}, seenNextActions)); } } action->nextActionList = std::move(actionList); return action; } const std::vector> &LinkAction::nextActions() const { return nextActionList; } //------------------------------------------------------------------------ // LinkDest //------------------------------------------------------------------------ LinkDest::LinkDest(const Array *a) { // initialize fields left = bottom = right = top = zoom = 0; changeLeft = changeTop = changeZoom = false; ok = false; // get page if (a->getLength() < 2) { error(errSyntaxWarning, -1, "Annotation destination array is too short"); return; } const Object &obj0 = a->getNF(0); if (obj0.isInt()) { pageNum = obj0.getInt() + 1; pageIsRef = false; } else if (obj0.isRef()) { pageRef = obj0.getRef(); pageIsRef = true; } else { error(errSyntaxWarning, -1, "Bad annotation destination"); return; } // get destination type Object obj1 = a->get(1); // XYZ link if (obj1.isName("XYZ")) { kind = destXYZ; if (a->getLength() < 3) { changeLeft = false; } else { Object obj2 = a->get(2); if (obj2.isNull()) { changeLeft = false; } else if (obj2.isNum()) { changeLeft = true; left = obj2.getNum(); } else { error(errSyntaxWarning, -1, "Bad annotation destination position"); return; } } if (a->getLength() < 4) { changeTop = false; } else { Object obj2 = a->get(3); if (obj2.isNull()) { changeTop = false; } else if (obj2.isNum()) { changeTop = true; top = obj2.getNum(); } else { error(errSyntaxWarning, -1, "Bad annotation destination position"); return; } } if (a->getLength() < 5) { changeZoom = false; } else { Object obj2 = a->get(4); if (obj2.isNull()) { changeZoom = false; } else if (obj2.isNum()) { zoom = obj2.getNum(); changeZoom = (zoom == 0) ? false : true; } else { error(errSyntaxWarning, -1, "Bad annotation destination position"); return; } } // Fit link } else if (obj1.isName("Fit")) { kind = destFit; // FitH link } else if (obj1.isName("FitH")) { kind = destFitH; if (a->getLength() < 3) { changeTop = false; } else { Object obj2 = a->get(2); if (obj2.isNull()) { changeTop = false; } else if (obj2.isNum()) { changeTop = true; top = obj2.getNum(); } else { error(errSyntaxWarning, -1, "Bad annotation destination position"); kind = destFit; } } // FitV link } else if (obj1.isName("FitV")) { if (a->getLength() < 3) { error(errSyntaxWarning, -1, "Annotation destination array is too short"); return; } kind = destFitV; Object obj2 = a->get(2); if (obj2.isNull()) { changeLeft = false; } else if (obj2.isNum()) { changeLeft = true; left = obj2.getNum(); } else { error(errSyntaxWarning, -1, "Bad annotation destination position"); kind = destFit; } // FitR link } else if (obj1.isName("FitR")) { if (a->getLength() < 6) { error(errSyntaxWarning, -1, "Annotation destination array is too short"); return; } kind = destFitR; Object obj2 = a->get(2); if (obj2.isNum()) { left = obj2.getNum(); } else { error(errSyntaxWarning, -1, "Bad annotation destination position"); kind = destFit; } obj2 = a->get(3); if (obj2.isNum()) { bottom = obj2.getNum(); } else { error(errSyntaxWarning, -1, "Bad annotation destination position"); kind = destFit; } obj2 = a->get(4); if (obj2.isNum()) { right = obj2.getNum(); } else { error(errSyntaxWarning, -1, "Bad annotation destination position"); kind = destFit; } obj2 = a->get(5); if (obj2.isNum()) { top = obj2.getNum(); } else { error(errSyntaxWarning, -1, "Bad annotation destination position"); kind = destFit; } // FitB link } else if (obj1.isName("FitB")) { kind = destFitB; // FitBH link } else if (obj1.isName("FitBH")) { if (a->getLength() < 3) { error(errSyntaxWarning, -1, "Annotation destination array is too short"); return; } kind = destFitBH; Object obj2 = a->get(2); if (obj2.isNull()) { changeTop = false; } else if (obj2.isNum()) { changeTop = true; top = obj2.getNum(); } else { error(errSyntaxWarning, -1, "Bad annotation destination position"); kind = destFit; } // FitBV link } else if (obj1.isName("FitBV")) { if (a->getLength() < 3) { error(errSyntaxWarning, -1, "Annotation destination array is too short"); return; } kind = destFitBV; Object obj2 = a->get(2); if (obj2.isNull()) { changeLeft = false; } else if (obj2.isNum()) { changeLeft = true; left = obj2.getNum(); } else { error(errSyntaxWarning, -1, "Bad annotation destination position"); kind = destFit; } // unknown link kind } else { error(errSyntaxWarning, -1, "Unknown annotation destination type"); return; } ok = true; } //------------------------------------------------------------------------ // LinkGoTo //------------------------------------------------------------------------ LinkGoTo::LinkGoTo(const Object *destObj) { // named destination if (destObj->isName()) { namedDest = std::make_unique(destObj->getName()); } else if (destObj->isString()) { namedDest = std::unique_ptr(destObj->getString()->copy()); // destination dictionary } else if (destObj->isArray()) { dest = std::make_unique(destObj->getArray()); if (!dest->isOk()) { dest.reset(); } // error } else { error(errSyntaxWarning, -1, "Illegal annotation destination"); } } LinkGoTo::~LinkGoTo() = default; //------------------------------------------------------------------------ // LinkGoToR //------------------------------------------------------------------------ LinkGoToR::LinkGoToR(Object *fileSpecObj, Object *destObj) { // get file name Object obj1 = getFileSpecNameForPlatform(fileSpecObj); if (obj1.isString()) { fileName = std::unique_ptr(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()); // destination dictionary } else if (destObj->isArray()) { dest = std::make_unique(destObj->getArray()); if (!dest->isOk()) { dest.reset(); } // error } else { error(errSyntaxWarning, -1, "Illegal annotation destination"); } } LinkGoToR::~LinkGoToR() = default; //------------------------------------------------------------------------ // LinkLaunch //------------------------------------------------------------------------ LinkLaunch::LinkLaunch(const Object *actionObj) { if (actionObj->isDict()) { Object obj1 = actionObj->dictLookup("F"); if (!obj1.isNull()) { Object obj3 = getFileSpecNameForPlatform(&obj1); if (obj3.isString()) { fileName = std::unique_ptr(obj3.getString()->copy()); } } else { #ifdef _WIN32 obj1 = actionObj->dictLookup("Win"); #else //~ This hasn't been defined by Adobe yet, so assume it looks //~ just like the Win dictionary until they say otherwise. obj1 = actionObj->dictLookup("Unix"); #endif if (obj1.isDict()) { Object obj2 = obj1.dictLookup("F"); Object obj3 = getFileSpecNameForPlatform(&obj2); if (obj3.isString()) { fileName = std::unique_ptr(obj3.getString()->copy()); } obj2 = obj1.dictLookup("P"); if (obj2.isString()) { params = std::unique_ptr(obj2.getString()->copy()); } } else { error(errSyntaxWarning, -1, "Bad launch-type link action"); } } } } LinkLaunch::~LinkLaunch() = default; //------------------------------------------------------------------------ // LinkURI //------------------------------------------------------------------------ LinkURI::LinkURI(const Object *uriObj, const std::optional &baseURI) { hasURIFlag = false; if (uriObj->isString()) { hasURIFlag = true; const std::string &uri2 = uriObj->getString()->toStr(); size_t n = strcspn(uri2.c_str(), "/:"); if (n < uri2.size() && uri2[n] == ':') { // "http:..." etc. uri = uri2; } else if (!uri2.compare(0, 4, "www.")) { // "www.[...]" without the leading "http://" uri = "http://" + uri2; } else { // relative URI if (baseURI) { uri = *baseURI; if (uri.size() > 0) { char c = uri.back(); if (c != '/' && c != '?') { uri += '/'; } } if (uri2[0] == '/') { uri.append(uri2.c_str() + 1, uri2.size() - 1); } else { uri += uri2; } } else { uri = uri2; } } } else { error(errSyntaxWarning, -1, "Illegal URI-type link"); } } LinkURI::~LinkURI() = default; //------------------------------------------------------------------------ // LinkNamed //------------------------------------------------------------------------ LinkNamed::LinkNamed(const Object *nameObj) { hasNameFlag = false; if (nameObj->isName()) { name = (nameObj->getName()) ? nameObj->getName() : ""; hasNameFlag = true; } } LinkNamed::~LinkNamed() = default; //------------------------------------------------------------------------ // LinkMovie //------------------------------------------------------------------------ LinkMovie::LinkMovie(const Object *obj) { annotRef = Ref::INVALID(); hasAnnotTitleFlag = false; const Object &annotationObj = obj->dictLookupNF("Annotation"); if (annotationObj.isRef()) { annotRef = annotationObj.getRef(); } Object tmp = obj->dictLookup("T"); if (tmp.isString()) { annotTitle = tmp.getString()->toStr(); hasAnnotTitleFlag = true; } if ((!hasAnnotTitleFlag) && (annotRef == Ref::INVALID())) { error(errSyntaxError, -1, "Movie action is missing both the Annot and T keys"); } tmp = obj->dictLookup("Operation"); if (tmp.isName()) { const char *name = tmp.getName(); if (!strcmp(name, "Play")) { operation = operationTypePlay; } else if (!strcmp(name, "Stop")) { operation = operationTypeStop; } else if (!strcmp(name, "Pause")) { operation = operationTypePause; } else if (!strcmp(name, "Resume")) { operation = operationTypeResume; } } } LinkMovie::~LinkMovie() = default; //------------------------------------------------------------------------ // LinkSound //------------------------------------------------------------------------ LinkSound::LinkSound(const Object *soundObj) { volume = 1.0; sync = false; repeat = false; mix = false; sound = nullptr; if (soundObj->isDict()) { // volume Object tmp = soundObj->dictLookup("Volume"); if (tmp.isNum()) { volume = tmp.getNum(); } // sync tmp = soundObj->dictLookup("Synchronous"); if (tmp.isBool()) { sync = tmp.getBool(); } // repeat tmp = soundObj->dictLookup("Repeat"); if (tmp.isBool()) { repeat = tmp.getBool(); } // mix tmp = soundObj->dictLookup("Mix"); if (tmp.isBool()) { mix = tmp.getBool(); } // 'Sound' object tmp = soundObj->dictLookup("Sound"); sound = Sound::parseSound(&tmp); } } LinkSound::~LinkSound() = default; //------------------------------------------------------------------------ // LinkRendition //------------------------------------------------------------------------ LinkRendition::LinkRendition(const Object *obj) { operation = NoRendition; media = nullptr; int operationCode = -1; screenRef = Ref::INVALID(); if (obj->isDict()) { Object tmp = obj->dictLookup("JS"); if (!tmp.isNull()) { if (tmp.isString()) { js = tmp.getString()->toStr(); } else if (tmp.isStream()) { Stream *stream = tmp.getStream(); stream->fillString(js); } else { error(errSyntaxWarning, -1, "Invalid Rendition Action: JS not string or stream"); } } tmp = obj->dictLookup("OP"); if (tmp.isInt()) { operationCode = tmp.getInt(); if (js.empty() && (operationCode < 0 || operationCode > 4)) { error(errSyntaxWarning, -1, "Invalid Rendition Action: unrecognized operation valued: {0:d}", operationCode); } else { // retrieve rendition object Object renditionObj = obj->dictLookup("R"); if (renditionObj.isDict()) { media = new MediaRendition(&renditionObj); } else if (operationCode == 0 || operationCode == 4) { error(errSyntaxWarning, -1, "Invalid Rendition Action: no R field with op = {0:d}", operationCode); renditionObj.setToNull(); } const Object &anObj = obj->dictLookupNF("AN"); if (anObj.isRef()) { screenRef = anObj.getRef(); } else if (operation >= 0 && operation <= 4) { error(errSyntaxWarning, -1, "Invalid Rendition Action: no AN field with op = {0:d}", operationCode); } } switch (operationCode) { case 0: operation = PlayRendition; break; case 1: operation = StopRendition; break; case 2: operation = PauseRendition; break; case 3: operation = ResumeRendition; break; case 4: operation = PlayRendition; break; } } else if (js == "") { error(errSyntaxWarning, -1, "Invalid Rendition action: no OP or JS field defined"); } } } LinkRendition::~LinkRendition() { delete media; } //------------------------------------------------------------------------ // LinkJavaScript //------------------------------------------------------------------------ LinkJavaScript::LinkJavaScript(Object *jsObj) { isValid = false; if (jsObj->isString()) { js = jsObj->getString()->toStr(); isValid = true; } else if (jsObj->isStream()) { Stream *stream = jsObj->getStream(); stream->fillString(js); isValid = true; } } LinkJavaScript::~LinkJavaScript() = default; Object LinkJavaScript::createObject(XRef *xref, const std::string &js) { Dict *linkDict = new Dict(xref); linkDict->add("S", Object(objName, "JavaScript")); linkDict->add("JS", Object(new GooString(js))); return Object(linkDict); } //------------------------------------------------------------------------ // LinkOCGState //------------------------------------------------------------------------ LinkOCGState::LinkOCGState(const Object *obj) : isValid(true) { Object obj1 = obj->dictLookup("State"); if (obj1.isArray()) { StateList stList; for (int i = 0; i < obj1.arrayGetLength(); ++i) { const Object &obj2 = obj1.arrayGetNF(i); if (obj2.isName()) { if (!stList.list.empty()) { stateList.push_back(stList); } const char *name = obj2.getName(); stList.list.clear(); if (!strcmp(name, "ON")) { stList.st = On; } else if (!strcmp(name, "OFF")) { stList.st = Off; } else if (!strcmp(name, "Toggle")) { stList.st = Toggle; } else { error(errSyntaxWarning, -1, "Invalid name '{0:s}' in OCG Action state array", name); isValid = false; } } else if (obj2.isRef()) { stList.list.push_back(obj2.getRef()); } else { error(errSyntaxWarning, -1, "Invalid item in OCG Action State array"); isValid = false; } } // Add the last group if (!stList.list.empty()) { stateList.push_back(stList); } } else { error(errSyntaxWarning, -1, "Invalid OCGState action"); isValid = false; } preserveRB = obj->dictLookup("PreserveRB").getBoolWithDefaultValue(true); } LinkOCGState::~LinkOCGState() = default; //------------------------------------------------------------------------ // LinkHide //------------------------------------------------------------------------ LinkHide::LinkHide(const Object *hideObj) { hasTargetNameFlag = false; show = false; // Default if (hideObj->isDict()) { const Object targetObj = hideObj->dictLookup("T"); if (targetObj.isString()) { targetName = targetObj.getString()->toStr(); hasTargetNameFlag = true; } const Object shouldHide = hideObj->dictLookup("H"); if (shouldHide.isBool()) { show = !shouldHide.getBool(); } } } LinkHide::~LinkHide() = default; //------------------------------------------------------------------------ // LinkResetForm //------------------------------------------------------------------------ LinkResetForm::LinkResetForm(const Object *obj) { Object obj1; exclude = false; obj1 = obj->dictLookup("Fields"); if (obj1.isArray()) { fields.resize(obj1.arrayGetLength()); for (int i = 0; i < obj1.arrayGetLength(); ++i) { const Object &obj2 = obj1.arrayGetNF(i); if (obj2.isName()) { fields[i] = std::string(obj2.getName()); } else if (obj2.isString()) { fields[i] = obj2.getString()->toStr(); } else if (obj2.isRef()) { fields[i] = std::to_string(obj2.getRef().num); fields[i].append(" "); fields[i].append(std::to_string(obj2.getRef().gen)); fields[i].append(" R"); } else { error(errSyntaxWarning, -1, "LinkResetForm: unexpected Field type"); } } } obj1 = obj->dictLookup("Flags"); if (obj1.isInt()) { int flags = obj1.getInt(); if (flags & 0x1) { exclude = true; } } } LinkResetForm::~LinkResetForm() = default; //------------------------------------------------------------------------ // LinkUnknown //------------------------------------------------------------------------ LinkUnknown::LinkUnknown(const char *actionA) { action = std::string(actionA ? actionA : ""); } LinkUnknown::~LinkUnknown() = default; //------------------------------------------------------------------------ // Links //------------------------------------------------------------------------ Links::Links(Annots *annots) { if (!annots) { return; } for (Annot *annot : annots->getAnnots()) { if (annot->getType() != Annot::typeLink) { continue; } annot->incRefCnt(); links.push_back(static_cast(annot)); } } Links::~Links() { for (AnnotLink *link : links) { link->decRefCnt(); } } poppler-24.02.0/poppler/Link.h000066400000000000000000000410411455701731300160750ustar00rootroot00000000000000//======================================================================== // // Link.h // // 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, 2008 Pino Toscano // Copyright (C) 2008 Hugo Mercier // Copyright (C) 2010, 2011 Carlos Garcia Campos // Copyright (C) 2012 Tobias Koening // 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 // // 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 LINK_H #define LINK_H #include "Object.h" #include "poppler_private_export.h" #include #include #include class GooString; class Array; class Dict; class Sound; class MediaRendition; class AnnotLink; class Annots; //------------------------------------------------------------------------ // LinkAction //------------------------------------------------------------------------ enum LinkActionKind { actionGoTo, // go to destination actionGoToR, // go to destination in new file actionLaunch, // launch app (or open document) actionURI, // URI actionNamed, // named action actionMovie, // movie action actionRendition, // rendition action actionSound, // sound action actionJavaScript, // JavaScript action actionOCGState, // Set-OCG-State action actionHide, // Hide action actionResetForm, // ResetForm action actionUnknown // anything else }; class POPPLER_PRIVATE_EXPORT LinkAction { public: LinkAction(); LinkAction(const LinkAction &) = delete; LinkAction &operator=(const LinkAction &other) = delete; // Destructor. virtual ~LinkAction(); // Was the LinkAction created successfully? virtual bool isOk() const = 0; // Check link action type. virtual LinkActionKind getKind() const = 0; // Parse a destination (old-style action) name, string, or array. static std::unique_ptr parseDest(const Object *obj); // Parse an action dictionary. static std::unique_ptr parseAction(const Object *obj, const std::optional &baseURI = {}); // A List of the next actions to execute in order. const std::vector> &nextActions() const; private: static std::unique_ptr parseAction(const Object *obj, const std::optional &baseURI, std::set *seenNextActions); std::vector> nextActionList; }; //------------------------------------------------------------------------ // LinkDest //------------------------------------------------------------------------ enum LinkDestKind { destXYZ, destFit, destFitH, destFitV, destFitR, destFitB, destFitBH, destFitBV }; class POPPLER_PRIVATE_EXPORT LinkDest { public: // Build a LinkDest from the array. explicit LinkDest(const Array *a); // Was the LinkDest created successfully? bool isOk() const { return ok; } // Accessors. LinkDestKind getKind() const { return kind; } bool isPageRef() const { return pageIsRef; } int getPageNum() const { return pageNum; } Ref getPageRef() const { return pageRef; } double getLeft() const { return left; } double getBottom() const { return bottom; } double getRight() const { return right; } double getTop() const { return top; } double getZoom() const { return zoom; } bool getChangeLeft() const { return changeLeft; } bool getChangeTop() const { return changeTop; } bool getChangeZoom() const { return changeZoom; } private: LinkDestKind kind; // destination type bool pageIsRef; // is the page a reference or number? union { Ref pageRef; // reference to page int pageNum; // one-relative page number }; double left, bottom; // position double right, top; double zoom; // zoom factor bool changeLeft, changeTop; // which position components to change: bool changeZoom; // destXYZ uses all three; // destFitH/BH use changeTop; // destFitV/BV use changeLeft bool ok; // set if created successfully }; //------------------------------------------------------------------------ // LinkGoTo //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT LinkGoTo : public LinkAction { public: // Build a LinkGoTo from a destination (dictionary, name, or string). explicit LinkGoTo(const Object *destObj); ~LinkGoTo() override; // Was the LinkGoTo created successfully? bool isOk() const override { return dest || namedDest; } // Accessors. LinkActionKind getKind() const override { return actionGoTo; } const LinkDest *getDest() const { return dest.get(); } const GooString *getNamedDest() const { return namedDest.get(); } private: std::unique_ptr dest; // regular destination (nullptr for remote // link with bad destination) std::unique_ptr namedDest; // named destination (only one of dest and // and namedDest may be non-nullptr) }; //------------------------------------------------------------------------ // LinkGoToR //------------------------------------------------------------------------ class LinkGoToR : public LinkAction { public: // Build a LinkGoToR from a file spec (dictionary) and destination // (dictionary, name, or string). LinkGoToR(Object *fileSpecObj, Object *destObj); ~LinkGoToR() override; // Was the LinkGoToR created successfully? bool isOk() const override { return fileName && (dest || namedDest); } // Accessors. LinkActionKind getKind() const override { return actionGoToR; } const GooString *getFileName() const { return fileName.get(); } const LinkDest *getDest() const { return dest.get(); } const GooString *getNamedDest() const { return namedDest.get(); } private: std::unique_ptr fileName; // file name std::unique_ptr dest; // regular destination (nullptr for remote // link with bad destination) std::unique_ptr namedDest; // named destination (only one of dest and // and namedDest may be non-nullptr) }; //------------------------------------------------------------------------ // LinkLaunch //------------------------------------------------------------------------ class LinkLaunch : public LinkAction { public: // Build a LinkLaunch from an action dictionary. explicit LinkLaunch(const Object *actionObj); ~LinkLaunch() override; // Was the LinkLaunch created successfully? bool isOk() const override { return fileName != nullptr; } // Accessors. LinkActionKind getKind() const override { return actionLaunch; } const GooString *getFileName() const { return fileName.get(); } const GooString *getParams() const { return params.get(); } private: std::unique_ptr fileName; // file name std::unique_ptr params; // parameters }; //------------------------------------------------------------------------ // LinkURI //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT LinkURI : public LinkAction { public: // Build a LinkURI given the URI (string) and base URI. LinkURI(const Object *uriObj, const std::optional &baseURI); ~LinkURI() override; // Was the LinkURI created successfully? bool isOk() const override { return hasURIFlag; } // Accessors. LinkActionKind getKind() const override { return actionURI; } const std::string &getURI() const { return uri; } private: std::string uri; // the URI bool hasURIFlag; }; //------------------------------------------------------------------------ // LinkNamed //------------------------------------------------------------------------ class LinkNamed : public LinkAction { public: // Build a LinkNamed given the action name. explicit LinkNamed(const Object *nameObj); ~LinkNamed() override; bool isOk() const override { return hasNameFlag; } LinkActionKind getKind() const override { return actionNamed; } const std::string &getName() const { return name; } private: std::string name; bool hasNameFlag; }; //------------------------------------------------------------------------ // LinkMovie //------------------------------------------------------------------------ class LinkMovie : public LinkAction { public: enum OperationType { operationTypePlay, operationTypePause, operationTypeResume, operationTypeStop }; explicit LinkMovie(const Object *obj); ~LinkMovie() override; bool isOk() const override { return hasAnnotRef() || hasAnnotTitleFlag; } LinkActionKind getKind() const override { return actionMovie; } // a movie action stores either an indirect reference to a movie annotation // or the movie annotation title bool hasAnnotRef() const { return annotRef != Ref::INVALID(); } bool hasAnnotTitle() const { return hasAnnotTitleFlag; } const Ref *getAnnotRef() const { return &annotRef; } const std::string &getAnnotTitle() const { return annotTitle; } OperationType getOperation() const { return operation; } private: Ref annotRef; // Annotation std::string annotTitle; // T bool hasAnnotTitleFlag; OperationType operation; // Operation }; //------------------------------------------------------------------------ // LinkRendition //------------------------------------------------------------------------ class LinkRendition : public LinkAction { public: /** * Describes the possible rendition operations. */ enum RenditionOperation { NoRendition, PlayRendition, StopRendition, PauseRendition, ResumeRendition }; explicit LinkRendition(const Object *Obj); ~LinkRendition() override; bool isOk() const override { return true; } LinkActionKind getKind() const override { return actionRendition; } bool hasScreenAnnot() const { return screenRef != Ref::INVALID(); } Ref getScreenAnnot() const { return screenRef; } RenditionOperation getOperation() const { return operation; } const MediaRendition *getMedia() const { return media; } const std::string &getScript() const { return js; } private: Ref screenRef; RenditionOperation operation; MediaRendition *media; std::string js; }; //------------------------------------------------------------------------ // LinkSound //------------------------------------------------------------------------ class LinkSound : public LinkAction { public: explicit LinkSound(const Object *soundObj); ~LinkSound() override; bool isOk() const override { return sound != nullptr; } LinkActionKind getKind() const override { return actionSound; } double getVolume() const { return volume; } bool getSynchronous() const { return sync; } bool getRepeat() const { return repeat; } bool getMix() const { return mix; } Sound *getSound() const { return sound.get(); } private: double volume; bool sync; bool repeat; bool mix; std::unique_ptr sound; }; //------------------------------------------------------------------------ // LinkJavaScript //------------------------------------------------------------------------ class LinkJavaScript : public LinkAction { public: // Build a LinkJavaScript given the action name. explicit LinkJavaScript(Object *jsObj); ~LinkJavaScript() override; bool isOk() const override { return isValid; } LinkActionKind getKind() const override { return actionJavaScript; } const std::string &getScript() const { return js; } static Object createObject(XRef *xref, const std::string &js); private: std::string js; bool isValid; }; //------------------------------------------------------------------------ // LinkOCGState //------------------------------------------------------------------------ class LinkOCGState : public LinkAction { public: explicit LinkOCGState(const Object *obj); ~LinkOCGState() override; bool isOk() const override { return isValid; } LinkActionKind getKind() const override { return actionOCGState; } enum State { On, Off, Toggle }; struct StateList { StateList() = default; ~StateList() = default; State st; std::vector list; }; const std::vector &getStateList() const { return stateList; } bool getPreserveRB() const { return preserveRB; } private: std::vector stateList; bool isValid; bool preserveRB; }; //------------------------------------------------------------------------ // LinkHide //------------------------------------------------------------------------ class LinkHide : public LinkAction { public: explicit LinkHide(const Object *hideObj); ~LinkHide() override; bool isOk() const override { return hasTargetNameFlag; } LinkActionKind getKind() const override { return actionHide; } // According to spec the target can be either: // a) A text string containing the fully qualified name of the target // field. // b) An indirect reference to an annotation dictionary. // c) An array of "such dictionaries or text strings". // // While b / c appear to be very uncommon and can't easily be // created with Adobe Acrobat DC. So only support hide // actions with named targets (yet). bool hasTargetName() const { return hasTargetNameFlag; } const std::string &getTargetName() const { return targetName; } // Should this action show or hide. bool isShowAction() const { return show; } private: bool hasTargetNameFlag; std::string targetName; bool show; }; //------------------------------------------------------------------------ // LinkResetForm //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT LinkResetForm : public LinkAction { public: // Build a LinkResetForm. explicit LinkResetForm(const Object *nameObj); ~LinkResetForm() override; bool isOk() const override { return true; } LinkActionKind getKind() const override { return actionResetForm; } const std::vector &getFields() const { return fields; } bool getExclude() const { return exclude; } private: std::vector fields; bool exclude; }; //------------------------------------------------------------------------ // LinkUnknown //------------------------------------------------------------------------ class LinkUnknown : public LinkAction { public: // Build a LinkUnknown with the specified action type. explicit LinkUnknown(const char *actionA); ~LinkUnknown() override; // Was the LinkUnknown create successfully? // Yes: nothing can go wrong when creating LinkUnknown objects bool isOk() const override { return true; } // Accessors. LinkActionKind getKind() const override { return actionUnknown; } const std::string &getAction() const { return action; } private: std::string action; // action subtype }; //------------------------------------------------------------------------ // Links //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT Links { public: // Extract links from array of annotations. explicit Links(Annots *annots); // Destructor. ~Links(); Links(const Links &) = delete; Links &operator=(const Links &) = delete; const std::vector &getLinks() const { return links; } private: std::vector links; }; #endif poppler-24.02.0/poppler/LocalPDFDocBuilder.cc000066400000000000000000000026061455701731300206630ustar00rootroot00000000000000//======================================================================== // // LocalPDFDocBuilder.cc // // This file is licensed under the GPLv2 or later // // Copyright 2010 Hib Eris // Copyright 2010, 2022 Albert Astals Cid // Copyright 2021 Oliver Sander // //======================================================================== #include #include "LocalPDFDocBuilder.h" //------------------------------------------------------------------------ // LocalPDFDocBuilder //------------------------------------------------------------------------ std::unique_ptr LocalPDFDocBuilder::buildPDFDoc(const GooString &uri, const std::optional &ownerPassword, const std::optional &userPassword, void *guiDataA) { if (uri.cmpN("file://", 7) == 0) { std::unique_ptr fileName(uri.copy()); fileName->del(0, 7); return std::make_unique(std::move(fileName), ownerPassword, userPassword, guiDataA); } else { return std::make_unique(std::unique_ptr(uri.copy()), ownerPassword, userPassword, guiDataA); } } bool LocalPDFDocBuilder::supports(const GooString &uri) { if (uri.cmpN("file://", 7) == 0) { return true; } else if (!strstr(uri.c_str(), "://")) { return true; } else { return false; } } poppler-24.02.0/poppler/LocalPDFDocBuilder.h000066400000000000000000000021001455701731300205120ustar00rootroot00000000000000//======================================================================== // // LocalPDFDocBuilder.h // // This file is licensed under the GPLv2 or later // // Copyright 2010 Hib Eris // Copyright 2010, 2018, 2022 Albert Astals Cid // Copyright 2021 Oliver Sander // //======================================================================== #ifndef LOCALPDFDOCBUILDER_H #define LOCALPDFDOCBUILDER_H #include "PDFDocBuilder.h" //------------------------------------------------------------------------ // LocalPDFDocBuilder // // The LocalPDFDocBuilder implements a PDFDocBuilder for local files. //------------------------------------------------------------------------ class LocalPDFDocBuilder : public PDFDocBuilder { public: std::unique_ptr buildPDFDoc(const GooString &uri, const std::optional &ownerPassword = {}, const std::optional &userPassword = {}, void *guiDataA = nullptr) override; bool supports(const GooString &uri) override; }; #endif /* LOCALPDFDOCBUILDER_H */ poppler-24.02.0/poppler/MarkedContentOutputDev.cc000066400000000000000000000135031455701731300217560ustar00rootroot00000000000000//======================================================================== // // MarkedContentOutputDev.cc // // This file is licensed under the GPLv2 or later // // Copyright 2013 Igalia S.L. // Copyright 2018-2020, 2022 Albert Astals Cid // Copyright 2021, 2023 Adrian Johnson // Copyright 2022 Oliver Sander // //======================================================================== #include "MarkedContentOutputDev.h" #include "GlobalParams.h" #include "UnicodeMap.h" #include "GfxState.h" #include "GfxFont.h" #include "Annot.h" #include #include MarkedContentOutputDev::MarkedContentOutputDev(int mcidA, const Object &stmObj) : currentFont(nullptr), currentText(nullptr), mcid(mcidA), pageWidth(0.0), pageHeight(0.0), unicodeMap(nullptr) { stmRef = stmObj.copy(); currentColor.r = currentColor.g = currentColor.b = 0; } MarkedContentOutputDev::~MarkedContentOutputDev() { delete currentText; } 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)); } currentText = nullptr; } void MarkedContentOutputDev::startPage(int pageNum, GfxState *state, XRef *xref) { if (state) { pageWidth = state->getPageWidth(); pageHeight = state->getPageHeight(); } else { pageWidth = pageHeight = 0.0; } } void MarkedContentOutputDev::endPage() { pageWidth = pageHeight = 0.0; } void MarkedContentOutputDev::beginForm(Object * /* obj */, Ref id) { formStack.push_back(id); } void MarkedContentOutputDev::endForm(Object * /* obj */, Ref id) { formStack.pop_back(); } bool MarkedContentOutputDev::contentStreamMatch() { if (stmRef.isRef()) { if (formStack.empty()) { return false; } return formStack.back() == stmRef.getRef(); } return formStack.empty(); } void MarkedContentOutputDev::beginMarkedContent(const char *name, Dict *properties) { int id = -1; if (properties) { properties->lookupInt("MCID", nullptr, &id); } if (id == -1) { return; } // The stack keep track of MCIDs of nested marked content. if (inMarkedContent() || (id == mcid && contentStreamMatch())) { mcidStack.push_back(id); } } void MarkedContentOutputDev::endMarkedContent(GfxState *state) { if (inMarkedContent()) { mcidStack.pop_back(); // The outer marked content sequence MCID was popped, ensure // that the last piece of text collected ends up in a TextSpan. if (!inMarkedContent()) { endSpan(); } } } bool MarkedContentOutputDev::needFontChange(const std::shared_ptr &font) const { if (currentFont == font) { return false; } if (!currentFont) { return font != nullptr && font->isOk(); } if (font == nullptr) { return true; } // Two non-null valid fonts are the same if they point to the same Ref if (*currentFont->getID() == *font->getID()) { return false; } return true; } void MarkedContentOutputDev::drawChar(GfxState *state, double xx, double yy, double dx, double dy, double ox, double oy, CharCode c, int nBytes, const Unicode *u, int uLen) { if (!inMarkedContent() || !uLen) { return; } // Color changes are tracked here so the color can be chosen depending on // the render mode (for mode 1 stroke color is used), so there is no need // to implement both updateFillColor() and updateStrokeColor(). bool colorChange = false; GfxRGB color; if ((state->getRender() & 3) == 1) { state->getStrokeRGB(&color); } else { state->getFillRGB(&color); } colorChange = (color.r != currentColor.r || color.g != currentColor.g || color.b != currentColor.b); // Check also for font changes. bool fontChange = needFontChange(state->getFont()); // Save a span with the current changes. if (colorChange || fontChange) { endSpan(); } // Perform the color/font changes. if (colorChange) { currentColor = color; } if (fontChange) { currentFont = state->getFont(); } double sp, dx2, dy2, w1, h1, x1, y1; // Subtract char and word spacing from the (dx,dy) values sp = state->getCharSpace(); if (c == (CharCode)0x20) { sp += state->getWordSpace(); } state->textTransformDelta(sp * state->getHorizScaling(), 0, &dx2, &dy2); dx -= dx2; dy -= dy2; state->transformDelta(dx, dy, &w1, &h1); state->transform(xx, yy, &x1, &y1); // Throw away characters that are not inside the page boundaries. if (x1 + w1 < 0 || x1 > pageWidth || y1 + h1 < 0 || y1 > pageHeight) { return; } if (std::isnan(x1) || std::isnan(y1) || std::isnan(w1) || std::isnan(h1)) { return; } for (int i = 0; i < uLen; i++) { // Soft hyphen markers are skipped, as they are invisible unless // rendering is done to an actual device and the hyphenation hint // used. MarkedContentOutputDev extracts the *visible* text content. if (u[i] != 0x00AD) { // Add the UTF-8 sequence to the current text span. if (!unicodeMap) { unicodeMap = globalParams->getTextEncoding(); } char buf[8]; int n = unicodeMap->mapUnicode(u[i], buf, sizeof(buf)); if (n > 0) { if (currentText == nullptr) { currentText = new GooString(); } currentText->append(buf, n); } } } } const TextSpanArray &MarkedContentOutputDev::getTextSpans() const { return textSpans; } poppler-24.02.0/poppler/MarkedContentOutputDev.h000066400000000000000000000070351455701731300216230ustar00rootroot00000000000000//======================================================================== // // MarkedContentOutputDev.h // // This file is licensed under the GPLv2 or later // // Copyright 2013 Igalia S.L. // Copyright 2018-2021 Albert Astals Cid // Copyright 2021, 2023 Adrian Johnson // Copyright 2022 Oliver Sander // //======================================================================== #ifndef MARKEDCONTENTOUTPUTDEV_H #define MARKEDCONTENTOUTPUTDEV_H #include "goo/gmem.h" #include "poppler_private_export.h" #include "OutputDev.h" #include "GfxState.h" #include "GfxFont.h" #include class Dict; class UnicodeMap; class TextSpan { public: TextSpan(const TextSpan &other) : data(other.data) { data->refcount++; } TextSpan &operator=(const TextSpan &other) { if (this != &other) { data = other.data; data->refcount++; } return *this; } ~TextSpan() { if (data && --data->refcount == 0) { delete data; } } const std::shared_ptr &getFont() const { return data->font; } GooString *getText() const { return data->text; } 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) { data->text = text; data->font = std::move(font); data->color = color; } struct Data { std::shared_ptr font; GooString *text; GfxRGB color; unsigned refcount; Data() : refcount(1) { } ~Data() { assert(refcount == 0); delete text; } Data(const Data &) = delete; Data &operator=(const Data &) = delete; }; Data *data; friend class MarkedContentOutputDev; }; typedef std::vector TextSpanArray; class POPPLER_PRIVATE_EXPORT MarkedContentOutputDev : public OutputDev { public: explicit MarkedContentOutputDev(int mcidA, const Object &stmObj); ~MarkedContentOutputDev() override; virtual bool isOk() { return true; } bool upsideDown() override { return true; } bool useDrawChar() override { return true; } bool interpretType3Chars() override { return false; } bool needNonText() override { return false; } bool needCharCount() override { return false; } void startPage(int pageNum, GfxState *state, XRef *xref) override; void endPage() 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; void beginMarkedContent(const char *name, Dict *properties) override; void endMarkedContent(GfxState *state) override; const TextSpanArray &getTextSpans() const; private: void endSpan(); bool inMarkedContent() const { return mcidStack.size() > 0; } bool contentStreamMatch(); bool needFontChange(const std::shared_ptr &font) const; std::shared_ptr currentFont; GooString *currentText; GfxRGB currentColor; TextSpanArray textSpans; int mcid; std::vector mcidStack; std::vector formStack; double pageWidth; double pageHeight; const UnicodeMap *unicodeMap; Object stmRef; }; #endif /* !MARKEDCONTENTOUTPUTDEV_H */ poppler-24.02.0/poppler/Movie.cc000066400000000000000000000171401455701731300164200ustar00rootroot00000000000000//********************************************************************************* // Movie.cc //--------------------------------------------------------------------------------- // //--------------------------------------------------------------------------------- // Hugo Mercier (c) 2008 // Pino Toscano (c) 2008 // Carlos Garcia Campos (c) 2010 // Albert Astals Cid (c) 2010, 2017-2019, 2022 // Evgeny Stambulchik (c) 2019 // // 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA //********************************************************************************* #include #include "Movie.h" #include "FileSpec.h" MovieActivationParameters::MovieActivationParameters() { // default values floatingWindow = false; xPosition = 0.5; yPosition = 0.5; rate = 1.0; volume = 100; showControls = false; synchronousPlay = false; repeatMode = repeatModeOnce; start.units = 0; duration.units = 0; znum = 1; zdenum = 1; } MovieActivationParameters::~MovieActivationParameters() { } void MovieActivationParameters::parseMovieActivation(const Object *aDict) { Object obj1 = aDict->dictLookup("Start"); if (obj1.isInt()) { // If it is representable as an integer (subject to the implementation limit for // integers, as described in Appendix C), it should be specified as such. start.units = obj1.getInt(); } else if (obj1.isString()) { // If it is not representable as an integer, it should be specified as an 8-byte // string representing a 64-bit twos-complement integer, most significant // byte first. // UNSUPPORTED } else if (obj1.isArray()) { Array *a = obj1.getArray(); Object tmp = a->get(0); if (tmp.isInt()) { start.units = tmp.getInt(); } if (tmp.isString()) { // UNSUPPORTED } tmp = a->get(1); if (tmp.isInt()) { start.units_per_second = tmp.getInt(); } } obj1 = aDict->dictLookup("Duration"); if (obj1.isInt()) { duration.units = obj1.getInt(); } else if (obj1.isString()) { // UNSUPPORTED } else if (obj1.isArray()) { Array *a = obj1.getArray(); Object tmp = a->get(0); if (tmp.isInt()) { duration.units = tmp.getInt(); } if (tmp.isString()) { // UNSUPPORTED } tmp = a->get(1); if (tmp.isInt()) { duration.units_per_second = tmp.getInt(); } } obj1 = aDict->dictLookup("Rate"); if (obj1.isNum()) { rate = obj1.getNum(); } obj1 = aDict->dictLookup("Volume"); if (obj1.isNum()) { // convert volume to [0 100] volume = int((obj1.getNum() + 1.0) * 50); } obj1 = aDict->dictLookup("ShowControls"); if (obj1.isBool()) { showControls = obj1.getBool(); } obj1 = aDict->dictLookup("Synchronous"); if (obj1.isBool()) { synchronousPlay = obj1.getBool(); } obj1 = aDict->dictLookup("Mode"); if (obj1.isName()) { const char *name = obj1.getName(); if (!strcmp(name, "Once")) { repeatMode = repeatModeOnce; } else if (!strcmp(name, "Open")) { repeatMode = repeatModeOpen; } else if (!strcmp(name, "Repeat")) { repeatMode = repeatModeRepeat; } else if (!strcmp(name, "Palindrome")) { repeatMode = repeatModePalindrome; } } obj1 = aDict->dictLookup("FWScale"); if (obj1.isArray()) { // the presence of that entry implies that the movie is to be played // in a floating window floatingWindow = true; Array *scale = obj1.getArray(); if (scale->getLength() >= 2) { Object tmp = scale->get(1); if (tmp.isInt()) { znum = tmp.getInt(); } tmp = scale->get(1); if (tmp.isInt()) { zdenum = tmp.getInt(); } } } obj1 = aDict->dictLookup("FWPosition"); if (obj1.isArray()) { Array *pos = obj1.getArray(); if (pos->getLength() >= 2) { Object tmp = pos->get(0); if (tmp.isNum()) { xPosition = tmp.getNum(); } tmp = pos->get(1); if (tmp.isNum()) { yPosition = tmp.getNum(); } } } } void Movie::parseMovie(const Object *movieDict) { fileName = nullptr; rotationAngle = 0; width = -1; height = -1; showPoster = false; Object obj1 = movieDict->dictLookup("F"); Object obj2 = getFileSpecNameForPlatform(&obj1); if (obj2.isString()) { fileName = obj2.getString()->copy(); } else { error(errSyntaxError, -1, "Invalid Movie"); ok = false; return; } obj1 = movieDict->dictLookup("Aspect"); if (obj1.isArray()) { Array *aspect = obj1.getArray(); if (aspect->getLength() >= 2) { Object tmp = aspect->get(0); if (tmp.isNum()) { width = (int)floor(tmp.getNum() + 0.5); } tmp = aspect->get(1); if (tmp.isNum()) { height = (int)floor(tmp.getNum() + 0.5); } } } obj1 = movieDict->dictLookup("Rotate"); if (obj1.isInt()) { // round up to 90° rotationAngle = (((obj1.getInt() + 360) % 360) % 90) * 90; } // // movie poster // poster = movieDict->dictLookupNF("Poster").copy(); if (!poster.isNull()) { if (poster.isRef() || poster.isStream()) { showPoster = true; } else if (poster.isBool()) { showPoster = poster.getBool(); poster.setToNull(); } else { poster.setToNull(); } } } Movie::~Movie() { delete fileName; } Movie::Movie(const Object *movieDict) { ok = true; if (movieDict->isDict()) { parseMovie(movieDict); } else { ok = false; } } Movie::Movie(const Object *movieDict, const Object *aDict) { ok = true; if (movieDict->isDict()) { parseMovie(movieDict); if (aDict->isDict()) { MA.parseMovieActivation(aDict); } } else { ok = false; } } Movie::Movie(const Movie &other) { ok = other.ok; rotationAngle = other.rotationAngle; width = other.width; height = other.height; showPoster = other.showPoster; MA = other.MA; poster = other.poster.copy(); if (other.fileName) { fileName = other.fileName->copy(); } else { fileName = nullptr; } } void Movie::getFloatingWindowSize(int *widthA, int *heightA) { *widthA = int(width * double(MA.znum) / MA.zdenum); *heightA = int(height * double(MA.znum) / MA.zdenum); } std::unique_ptr Movie::copy() const { return std::make_unique(*this); } poppler-24.02.0/poppler/Movie.h000066400000000000000000000065551455701731300162720ustar00rootroot00000000000000//********************************************************************************* // Movie.h //--------------------------------------------------------------------------------- // //--------------------------------------------------------------------------------- // Hugo Mercier (c) 2008 // Carlos Garcia Campos (c) 2010 // Albert Astals Cid (c) 2017-2019, 2021, 2022 // // 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA //********************************************************************************* #ifndef _MOVIE_H_ #define _MOVIE_H_ #include "Object.h" #include "poppler_private_export.h" #include struct MovieActivationParameters { MovieActivationParameters(); ~MovieActivationParameters(); // parse from a "Movie Activation" dictionary void parseMovieActivation(const Object *aDict); enum MovieRepeatMode { repeatModeOnce, repeatModeOpen, repeatModeRepeat, repeatModePalindrome }; struct MovieTime { MovieTime() { units_per_second = 0; } unsigned long units; int units_per_second; // 0 : defined by movie }; MovieTime start; // 0 MovieTime duration; // 0 double rate; // 1.0 int volume; // 100 bool showControls; // false bool synchronousPlay; // false MovieRepeatMode repeatMode; // repeatModeOnce // floating window position bool floatingWindow; double xPosition; // 0.5 double yPosition; // 0.5 int znum; // 1 int zdenum; // 1 }; class POPPLER_PRIVATE_EXPORT Movie { public: Movie(const Object *movieDict, const Object *aDict); explicit Movie(const Object *movieDict); Movie(const Movie &other); ~Movie(); Movie &operator=(const Movie &) = delete; bool isOk() const { return ok; } const MovieActivationParameters *getActivationParameters() const { return &MA; } const GooString *getFileName() const { return fileName; } unsigned short getRotationAngle() const { return rotationAngle; } void getAspect(int *widthA, int *heightA) const { *widthA = width; *heightA = height; } Object getPoster() const { return poster.copy(); } bool getShowPoster() const { return showPoster; } bool getUseFloatingWindow() const { return MA.floatingWindow; } void getFloatingWindowSize(int *width, int *height); std::unique_ptr copy() const; private: void parseMovie(const Object *movieDict); bool ok; unsigned short rotationAngle; // 0 int width; // Aspect int height; // Aspect Object poster; bool showPoster; GooString *fileName; MovieActivationParameters MA; }; #endif poppler-24.02.0/poppler/NSSCryptoSignBackend.cc000066400000000000000000001221271455701731300213000ustar00rootroot00000000000000//======================================================================== // // SignatureHandler.cc // // This file is licensed under the GPLv2 or later // // Copyright 2015, 2016 André Guerreiro // Copyright 2015 André Esser // Copyright 2015, 2016, 2018, 2019, 2021-2023 Albert Astals Cid // Copyright 2015 Markus Kilås // Copyright 2017 Sebastian Rasmussen // 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 2022 Erich E. Hoover // Copyright 2023 Tobias Deiminger // Copyright 2023 g10 Code GmbH, Author: Sune Stolborg Vuorela // Copyright 2023 Ingo Klöcker // //======================================================================== #include #include "NSSCryptoSignBackend.h" #include "goo/gdir.h" #include "goo/gmem.h" #include #include #include /* NSS headers */ #include #include #include #include #include #include #include #include #include #include #include #include /** * General name, defined by RFC 3280. */ struct GeneralName { CERTName name; }; /** * List of general names (only one for now), defined by RFC 3280. */ struct GeneralNames { GeneralName names; }; /** * Supplies different fields to identify a certificate, defined by RFC 5035. */ struct IssuerSerial { GeneralNames issuer; SECItem serialNumber; }; /** * Supplies different fields that are used to identify certificates, defined by * RFC 5035. */ struct ESSCertIDv2 { SECAlgorithmID hashAlgorithm; SECItem certHash; IssuerSerial issuerSerial; }; /** * This attribute uses the ESSCertIDv2 structure, defined by RFC 5035. */ struct SigningCertificateV2 { ESSCertIDv2 **certs; SigningCertificateV2() : certs(nullptr) { } }; /** * GeneralName ::= CHOICE { * otherName [0] OtherName, * rfc822Name [1] IA5String, * dNSName [2] IA5String, * x400Address [3] ORAddress, * directoryName [4] Name, * ediPartyName [5] EDIPartyName, * uniformResourceIdentifier [6] IA5String, * iPAddress [7] OCTET STRING, * registeredID [8] OBJECT IDENTIFIER * } */ const SEC_ASN1Template GeneralNameTemplate[] = { { SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(GeneralName) }, { SEC_ASN1_INLINE, offsetof(GeneralName, name), SEC_ASN1_GET(CERT_NameTemplate), 0 }, { 0, 0, nullptr, 0 } }; /** * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName */ const SEC_ASN1Template GeneralNamesTemplate[] = { { SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(GeneralNames) }, { SEC_ASN1_INLINE | SEC_ASN1_CONTEXT_SPECIFIC | 4, offsetof(GeneralNames, names), GeneralNameTemplate, 0 }, { 0, 0, nullptr, 0 } }; /** * IssuerSerial ::= SEQUENCE { * issuer GeneralNames, * serialNumber CertificateSerialNumber * } */ const SEC_ASN1Template IssuerSerialTemplate[] = { { SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(IssuerSerial) }, { SEC_ASN1_INLINE, offsetof(IssuerSerial, issuer), GeneralNamesTemplate, 0 }, { SEC_ASN1_INTEGER, offsetof(IssuerSerial, serialNumber), nullptr, 0 }, { 0, 0, nullptr, 0 } }; /** * Hash ::= OCTET STRING * * ESSCertIDv2 ::= SEQUENCE { * hashAlgorithm AlgorithmIdentifier DEFAULT {algorithm id-sha256}, * certHash Hash, * issuerSerial IssuerSerial OPTIONAL * } */ const SEC_ASN1Template ESSCertIDv2Template[] = { { SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(ESSCertIDv2) }, { SEC_ASN1_INLINE, offsetof(ESSCertIDv2, hashAlgorithm), SEC_ASN1_GET(SECOID_AlgorithmIDTemplate), 0 }, { SEC_ASN1_OCTET_STRING, offsetof(ESSCertIDv2, certHash), nullptr, 0 }, { SEC_ASN1_INLINE, offsetof(ESSCertIDv2, issuerSerial), IssuerSerialTemplate, 0 }, { 0, 0, nullptr, 0 } }; /** * SigningCertificateV2 ::= SEQUENCE { * } */ const SEC_ASN1Template SigningCertificateV2Template[] = { { SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(SigningCertificateV2) }, { SEC_ASN1_SEQUENCE_OF, offsetof(SigningCertificateV2, certs), ESSCertIDv2Template, 0 }, { 0, 0, nullptr, 0 } }; /* struct PKIStatusInfo { SECItem status; SECItem statusString; SECItem failInfo; }; const SEC_ASN1Template PKIStatusInfo_Template[] = { { SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(PKIStatusInfo) }, { SEC_ASN1_INTEGER, offsetof(PKIStatusInfo, status), nullptr, 0 }, { SEC_ASN1_CONSTRUCTED | SEC_ASN1_SEQUENCE | SEC_ASN1_OPTIONAL, offsetof(PKIStatusInfo, statusString), nullptr, 0 }, { SEC_ASN1_BIT_STRING | SEC_ASN1_OPTIONAL, offsetof(PKIStatusInfo, failInfo), nullptr, 0 }, { 0, 0, nullptr, 0 } }; const SEC_ASN1Template Any_Template[] = { { SEC_ASN1_ANY, 0, nullptr, sizeof(SECItem) } }; struct TimeStampResp { PKIStatusInfo status; SECItem timeStampToken; }; const SEC_ASN1Template TimeStampResp_Template[] = { { SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(TimeStampResp) }, { SEC_ASN1_INLINE, offsetof(TimeStampResp, status), PKIStatusInfo_Template, 0 }, { SEC_ASN1_ANY | SEC_ASN1_OPTIONAL, offsetof(TimeStampResp, timeStampToken), Any_Template, 0 }, { 0, 0, nullptr, 0 } }; const SEC_ASN1Template MessageImprint_Template[] = { { SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(MessageImprint) }, { SEC_ASN1_INLINE, offsetof(MessageImprint, hashAlgorithm), SECOID_AlgorithmIDTemplate, 0 }, { SEC_ASN1_OCTET_STRING, offsetof(MessageImprint, hashedMessage), nullptr, 0 }, { 0, 0, nullptr, 0 } }; const SEC_ASN1Template Extension_Template[] = { { SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(Extension) }, { SEC_ASN1_OBJECT_ID, offsetof(Extension, extnID), nullptr, 0 }, { SEC_ASN1_BOOLEAN, offsetof(Extension, critical), nullptr, 0 }, { SEC_ASN1_OCTET_STRING, offsetof(Extension, extnValue), nullptr, 0 }, { 0, 0, nullptr, 0 } }; const SEC_ASN1Template Extensions_Template[] = { { SEC_ASN1_SEQUENCE_OF, 0, Extension_Template, 0 } }; const SEC_ASN1Template TimeStampReq_Template[] = { { SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(TimeStampReq) }, { SEC_ASN1_INTEGER, offsetof(TimeStampReq, version), nullptr, 0 }, { SEC_ASN1_INLINE, offsetof(TimeStampReq, messageImprint), MessageImprint_Template, 0 }, { SEC_ASN1_OBJECT_ID | SEC_ASN1_OPTIONAL, offsetof(TimeStampReq, reqPolicy), nullptr, 0 }, { SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL, offsetof(TimeStampReq, nonce), nullptr, 0 }, { SEC_ASN1_BOOLEAN | SEC_ASN1_OPTIONAL, offsetof(TimeStampReq, certReq), nullptr, 0 }, { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | 0, offsetof(TimeStampReq, extensions), Extensions_Template, 0 }, { 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) { return PL_strdup(static_cast(arg)); } static void shutdownNss() { if (NSS_Shutdown() != SECSuccess) { fprintf(stderr, "NSS_Shutdown failed: %s\n", PR_ErrorToString(PORT_GetError(), PR_LANGUAGE_I_DEFAULT)); } } // SEC_StringToOID() and NSS_CMSSignerInfo_AddUnauthAttr() are // not exported from libsmime, so copy them here. Sigh. static SECStatus my_SEC_StringToOID(PLArenaPool *arena, 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(arena, to, &result_item); } return rv; } static NSSCMSAttribute *my_NSS_CMSAttributeArray_FindAttrByOidTag(NSSCMSAttribute **attrs, SECOidTag oidtag, PRBool only) { SECOidData *oid; NSSCMSAttribute *attr1, *attr2; if (attrs == nullptr) { return nullptr; } oid = SECOID_FindOIDByTag(oidtag); if (oid == nullptr) { return nullptr; } while ((attr1 = *attrs++) != nullptr) { if (attr1->type.len == oid->oid.len && PORT_Memcmp(attr1->type.data, oid->oid.data, oid->oid.len) == 0) { break; } } if (attr1 == nullptr) { return nullptr; } if (!only) { return attr1; } while ((attr2 = *attrs++) != nullptr) { if (attr2->type.len == oid->oid.len && PORT_Memcmp(attr2->type.data, oid->oid.data, oid->oid.len) == 0) { break; } } if (attr2 != nullptr) { return nullptr; } return attr1; } static SECStatus my_NSS_CMSArray_Add(PLArenaPool *poolp, void ***array, void *obj) { int n = 0; void **dest; PORT_Assert(array != NULL); if (array == nullptr) { return SECFailure; } if (*array == nullptr) { dest = static_cast(PORT_ArenaAlloc(poolp, 2 * sizeof(void *))); } else { void **p = *array; while (*p++) { n++; } dest = static_cast(PORT_ArenaGrow(poolp, *array, (n + 1) * sizeof(void *), (n + 2) * sizeof(void *))); } if (dest == nullptr) { return SECFailure; } dest[n] = obj; dest[n + 1] = nullptr; *array = dest; return SECSuccess; } static SECOidTag my_NSS_CMSAttribute_GetType(NSSCMSAttribute *attr) { SECOidData *typetag; typetag = SECOID_FindOID(&(attr->type)); if (typetag == nullptr) { return SEC_OID_UNKNOWN; } return typetag->offset; } static SECStatus my_NSS_CMSAttributeArray_AddAttr(PLArenaPool *poolp, NSSCMSAttribute ***attrs, NSSCMSAttribute *attr) { NSSCMSAttribute *oattr; void *mark; SECOidTag type; mark = PORT_ArenaMark(poolp); /* find oidtag of attr */ type = my_NSS_CMSAttribute_GetType(attr); /* see if we have one already */ oattr = my_NSS_CMSAttributeArray_FindAttrByOidTag(*attrs, type, PR_FALSE); PORT_Assert(oattr == NULL); if (oattr != nullptr) { goto loser; /* XXX or would it be better to replace it? */ } /* no, shove it in */ if (my_NSS_CMSArray_Add(poolp, reinterpret_cast(attrs), static_cast(attr)) != SECSuccess) { goto loser; } PORT_ArenaUnmark(poolp, mark); return SECSuccess; loser: PORT_ArenaRelease(poolp, mark); return SECFailure; } static SECStatus my_NSS_CMSSignerInfo_AddAuthAttr(NSSCMSSignerInfo *signerinfo, NSSCMSAttribute *attr) { return my_NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->authAttr), attr); } 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 HashAlgorithm::Sha1: return 20; case HashAlgorithm::Sha256: return 32; case HashAlgorithm::Sha384: return 48; case HashAlgorithm::Sha512: return 64; default: printf("ERROR: Unrecognized Hash ID\n"); return 0; } } std::string NSSSignatureVerification::getSignerName() const { if (!NSS_IsInitialized()) { return {}; } if (!CMSSignerInfo) { return {}; } auto signing_cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB()); if (!signing_cert) { return {}; } char *commonName = CERT_GetCommonName(&signing_cert->subject); if (!commonName) { return {}; } std::string name(commonName); PORT_Free(commonName); return name; } std::string NSSSignatureVerification::getSignerSubjectDN() const { if (!CMSSignerInfo) { return {}; } auto signing_cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB()); if (!signing_cert) { return {}; } return std::string { signing_cert->subjectName }; } std::chrono::system_clock::time_point NSSSignatureVerification::getSigningTime() const { if (!CMSSignerInfo) { return {}; } PRTime sTime; // time in microseconds since the epoch if (NSS_CMSSignerInfo_GetSigningTime(CMSSignerInfo, &sTime) != SECSuccess) { return {}; } return std::chrono::system_clock::from_time_t(static_cast(sTime / 1000000)); } static X509CertificateInfo::EntityInfo getEntityInfo(CERTName *entityName) { X509CertificateInfo::EntityInfo info; if (!entityName) { return info; } char *dn = CERT_NameToAscii(entityName); if (dn) { info.distinguishedName = dn; PORT_Free(dn); } char *cn = CERT_GetCommonName(entityName); if (cn) { info.commonName = cn; PORT_Free(cn); } char *email = CERT_GetCertEmailAddress(entityName); if (email) { info.email = email; PORT_Free(email); } char *org = CERT_GetOrgName(entityName); if (org) { info.organization = org; PORT_Free(org); } return info; } static GooString SECItemToGooString(const SECItem &secItem) { // TODO do we need to handle secItem.type; return GooString((const char *)secItem.data, secItem.len); } static std::unique_ptr getCertificateInfoFromCERT(CERTCertificate *cert) { auto certInfo = std::make_unique(); certInfo->setVersion(DER_GetInteger(&cert->version) + 1); certInfo->setSerialNumber(SECItemToGooString(cert->serialNumber)); // issuer info certInfo->setIssuerInfo(getEntityInfo(&cert->issuer)); // validity PRTime notBefore, notAfter; CERT_GetCertTimes(cert, ¬Before, ¬After); X509CertificateInfo::Validity certValidity; certValidity.notBefore = static_cast(notBefore / 1000000); certValidity.notAfter = static_cast(notAfter / 1000000); certInfo->setValidity(certValidity); // subject info certInfo->setSubjectInfo(getEntityInfo(&cert->subject)); // nickname (as a handle to refer to the CERT later) certInfo->setNickName(GooString(cert->dbnickname)); // public key info X509CertificateInfo::PublicKeyInfo pkInfo; SECKEYPublicKey *pk = CERT_ExtractPublicKey(cert); 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.publicKey = SECItemToGooString(cert->subjectPublicKeyInfo.subjectPublicKey); pkInfo.publicKeyType = OTHERKEY; } certInfo->setPublicKeyInfo(std::move(pkInfo)); certInfo->setKeyUsageExtensions(cert->keyUsage); certInfo->setCertificateDER(SECItemToGooString(cert->derCert)); certInfo->setIsSelfSigned(CERT_CompareName(&cert->subject, &cert->issuer) == SECEqual); return certInfo; } std::unique_ptr NSSSignatureVerification::getCertificateInfo() const { if (!CMSSignerInfo) { return nullptr; } CERTCertificate *cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB()); if (!cert) { return nullptr; } return getCertificateInfoFromCERT(cert); } std::unique_ptr NSSSignatureCreation::getCertificateInfo() const { if (!signing_cert) { return nullptr; } return getCertificateInfoFromCERT(signing_cert); } static std::optional getDefaultFirefoxCertDB() { #ifdef _WIN32 const char *env = getenv("APPDATA"); if (!env) { return {}; } const std::string firefoxPath = std::string(env) + "/Mozilla/Firefox/Profiles/"; #else const char *env = getenv("HOME"); if (!env) { return {}; } 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(); } } return {}; } std::string NSSSignatureConfiguration::sNssDir; /** * Initialise NSS */ void NSSSignatureConfiguration::setNSSDir(const GooString &nssDir) { static bool setNssDirCalled = false; if (NSS_IsInitialized() && nssDir.getLength() > 0) { error(errInternal, 0, "You need to call setNSSDir before signature validation related operations happen"); return; } if (setNssDirCalled) { return; } setNssDirCalled = true; atexit(shutdownNss); bool initSuccess = false; if (nssDir.getLength() > 0) { initSuccess = (NSS_Init(nssDir.c_str()) == SECSuccess); sNssDir = nssDir.toStr(); } else { const std::optional certDBPath = getDefaultFirefoxCertDB(); if (!certDBPath) { initSuccess = (NSS_Init("sql:/etc/pki/nssdb") == SECSuccess); sNssDir = "sql:/etc/pki/nssdb"; } else { initSuccess = (NSS_Init(certDBPath->c_str()) == SECSuccess); sNssDir = *certDBPath; } if (!initSuccess) { GooString homeNssDb(getenv("HOME")); homeNssDb.append("/.pki/nssdb"); initSuccess = (NSS_Init(homeNssDb.c_str()) == SECSuccess); sNssDir = homeNssDb.toStr(); } } 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 NSSSignatureConfiguration::getNSSDir() { return sNssDir; } static std::function PasswordFunction; void NSSSignatureConfiguration::setNSSPasswordCallback(const std::function &f) { PasswordFunction = f; } NSSSignatureVerification::NSSSignatureVerification(std::vector &&p7data) : p7(std::move(p7data)), CMSMessage(nullptr), CMSSignedData(nullptr), CMSSignerInfo(nullptr) { NSSSignatureConfiguration::setNSSDir({}); CMSitem.data = p7.data(); CMSitem.len = p7.size(); CMSMessage = CMS_MessageCreate(&CMSitem); CMSSignedData = CMS_SignedDataCreate(CMSMessage); if (CMSSignedData) { CMSSignerInfo = CMS_SignerInfoCreate(CMSSignedData); 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; } } } NSSSignatureCreation::NSSSignatureCreation(const std::string &certNickname, HashAlgorithm digestAlgTag) : hashContext(HashContext::create(digestAlgTag)), signing_cert(nullptr) { NSSSignatureConfiguration::setNSSDir({}); signing_cert = CERT_FindCertByNickname(CERT_GetDefaultCertDB(), certNickname.c_str()); } HashAlgorithm NSSSignatureVerification::getHashAlgorithm() const { if (hashContext) { return hashContext->getHashAlgorithm(); } else { return HashAlgorithm::Unknown; } } void NSSSignatureVerification::addData(unsigned char *data_block, int data_len) { if (hashContext) { hashContext->updateHash(data_block, data_len); } } void NSSSignatureCreation::addData(unsigned char *data_block, int data_len) { hashContext->updateHash(data_block, data_len); } NSSSignatureCreation::~NSSSignatureCreation() { if (signing_cert) { CERT_DestroyCertificate(signing_cert); } } NSSSignatureVerification::~NSSSignatureVerification() { 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(toFree); } } static NSSCMSMessage *CMS_MessageCreate(SECItem *cms_item) { if (cms_item->data) { return NSS_CMSMessage_CreateFromDER(cms_item, nullptr, nullptr /* Content callback */ , nullptr, nullptr /*Password callback*/ , nullptr, nullptr /*Decrypt callback*/); } else { return nullptr; } } 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"); return nullptr; } NSSCMSContentInfo *cinfo = NSS_CMSMessage_ContentLevel(cms_msg, 0); if (!cinfo) { error(errInternal, 0, "Error in NSS_CMSMessage_ContentLevel"); return nullptr; } NSSCMSSignedData *signedData = (NSSCMSSignedData *)NSS_CMSContentInfo_GetContent(cinfo); if (!signedData) { error(errInternal, 0, "CError in NSS_CMSContentInfo_GetContent()"); return nullptr; } if (signedData->rawCerts) { size_t i; for (i = 0; signedData->rawCerts[i]; ++i) { } // just count the length of the certificate chain // 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 *)); // 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); } return signedData; } else { return nullptr; } } static NSSCMSSignerInfo *CMS_SignerInfoCreate(NSSCMSSignedData *cms_sig_data) { NSSCMSSignerInfo *signerInfo = NSS_CMSSignedData_GetSignerInfo(cms_sig_data, 0); if (!signerInfo) { printf("Error in NSS_CMSSignedData_GetSignerInfo()\n"); return nullptr; } else { return signerInfo; } } static SignatureValidationStatus NSS_SigTranslate(NSSCMSVerificationStatus nss_code) { switch (nss_code) { case NSSCMSVS_GoodSignature: return SIGNATURE_VALID; case NSSCMSVS_BadSignature: return SIGNATURE_INVALID; case NSSCMSVS_DigestMismatch: return SIGNATURE_DIGEST_MISMATCH; case NSSCMSVS_ProcessingError: return SIGNATURE_DECODING_ERROR; default: return SIGNATURE_GENERIC_ERROR; } } SignatureValidationStatus NSSSignatureVerification::validateSignature() { if (!CMSSignedData) { return SIGNATURE_GENERIC_ERROR; } if (!NSS_IsInitialized()) { return SIGNATURE_GENERIC_ERROR; } if (!hashContext) { return SIGNATURE_GENERIC_ERROR; } std::vector digest_buffer = hashContext->endHash(); SECItem digest; digest.data = digest_buffer.data(); digest.len = digest_buffer.size(); if ((NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB())) == nullptr) { CMSSignerInfo->verificationStatus = NSSCMSVS_SigningCertNotFound; } SECItem *content_info_data = CMSSignedData->contentInfo.content.data; if (content_info_data != nullptr && content_info_data->data != nullptr) { /* 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 { return SIGNATURE_DIGEST_MISMATCH; } } else if (NSS_CMSSignerInfo_Verify(CMSSignerInfo, &digest, nullptr) != SECSuccess) { return NSS_SigTranslate(CMSSignerInfo->verificationStatus); } else { return SIGNATURE_VALID; } } CertificateValidationStatus NSSSignatureVerification::validateCertificate(std::chrono::system_clock::time_point validation_time, bool ocspRevocationCheck, bool useAIACertFetch) { CERTCertificate *cert; if (!CMSSignerInfo) { return CERTIFICATE_GENERIC_ERROR; } if ((cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB())) == nullptr) { CMSSignerInfo->verificationStatus = NSSCMSVS_SigningCertNotFound; } PRTime vTime = 0; // time in microseconds since the epoch, special value 0 means now 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; if (ocspRevocationCheck) { inParams[0].value.pointer.revocation = CERT_GetClassicOCSPEnabledSoftFailurePolicy(); } else { inParams[0].value.pointer.revocation = CERT_GetClassicOCSPDisabledPolicy(); } inParams[1].type = cert_pi_date; inParams[1].value.scalar.time = vTime; if (useAIACertFetch) { inParams[2].type = cert_pi_useAIACertFetch; inParams[2].value.scalar.b = PR_TRUE; inParams[3].type = cert_pi_end; } else { inParams[2].type = cert_pi_end; } 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; case SEC_ERROR_UNKNOWN_ISSUER: return CERTIFICATE_UNKNOWN_ISSUER; case SEC_ERROR_UNTRUSTED_ISSUER: return CERTIFICATE_UNTRUSTED_ISSUER; case SEC_ERROR_REVOKED_CERTIFICATE: return CERTIFICATE_REVOKED; case SEC_ERROR_EXPIRED_CERTIFICATE: return CERTIFICATE_EXPIRED; } return CERTIFICATE_GENERIC_ERROR; } std::optional NSSSignatureCreation::signDetached(const std::string &password) { if (!hashContext) { return {}; } std::vector digest_buffer = hashContext->endHash(); SECItem digest; digest.data = digest_buffer.data(); digest.len = digest_buffer.size(); ///////////////////////////////////// /// Code from LibreOffice under MPLv2 ///////////////////////////////////// struct NSSCMSMessageDestroyer { void operator()(NSSCMSMessage *message) { NSS_CMSMessage_Destroy(message); } }; std::unique_ptr cms_msg { NSS_CMSMessage_Create(nullptr) }; if (!cms_msg) { return {}; } NSSCMSSignedData *cms_sd = NSS_CMSSignedData_Create(cms_msg.get()); if (!cms_sd) { return {}; } NSSCMSContentInfo *cms_cinfo = NSS_CMSMessage_GetContentInfo(cms_msg.get()); if (NSS_CMSContentInfo_SetContent_SignedData(cms_msg.get(), cms_cinfo, cms_sd) != SECSuccess) { return {}; } cms_cinfo = NSS_CMSSignedData_GetContentInfo(cms_sd); // Attach NULL data as detached data if (NSS_CMSContentInfo_SetContent_Data(cms_msg.get(), cms_cinfo, nullptr, PR_TRUE) != SECSuccess) { return {}; } // hardcode SHA256 these days... NSSCMSSignerInfo *cms_signer = NSS_CMSSignerInfo_Create(cms_msg.get(), signing_cert, SEC_OID_SHA256); if (!cms_signer) { return {}; } if (NSS_CMSSignerInfo_IncludeCerts(cms_signer, NSSCMSCM_CertChain, certUsageEmailSigner) != SECSuccess) { return {}; } if (NSS_CMSSignedData_AddCertificate(cms_sd, signing_cert) != SECSuccess) { return {}; } if (NSS_CMSSignedData_AddSignerInfo(cms_sd, cms_signer) != SECSuccess) { return {}; } if (NSS_CMSSignedData_SetDigestValue(cms_sd, SEC_OID_SHA256, &digest) != SECSuccess) { return {}; } 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(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 {}; } aCertHashItem.type = siBuffer; aCertHashItem.data = certhash; aCertHashItem.len = 32; aCertID.certHash = aCertHashItem; // Write ESSCertIDv2.issuerSerial. IssuerSerial aSerial; GeneralName aName; aName.name = signing_cert->issuer; aSerial.issuer.names = aName; aSerial.serialNumber = signing_cert->serialNumber; aCertID.issuerSerial = aSerial; // Write SigningCertificateV2.certs. aCertIDs[0] = &aCertID; aCertIDs[1] = nullptr; SigningCertificateV2 aCertificate; aCertificate.certs = &aCertIDs[0]; SECItem *pEncodedCertificate = SEC_ASN1EncodeItem(nullptr, nullptr, &aCertificate, SigningCertificateV2Template); if (!pEncodedCertificate) { return {}; } NSSCMSAttribute aAttribute; SECItem aAttributeValues[2]; SECItem *pAttributeValues[2]; pAttributeValues[0] = aAttributeValues; pAttributeValues[1] = nullptr; aAttributeValues[0] = *pEncodedCertificate; aAttributeValues[1].type = siBuffer; aAttributeValues[1].data = nullptr; aAttributeValues[1].len = 0; 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(arena.get(), &aOidData.oid, "1.2.840.113549.1.9.16.2.47", 0) != SECSuccess) { return {}; } aOidData.offset = SEC_OID_UNKNOWN; aOidData.desc = "id-aa-signingCertificateV2"; aOidData.mechanism = CKM_SHA_1; aOidData.supportedExtension = UNSUPPORTED_CERT_EXTENSION; aAttribute.typeTag = &aOidData; aAttribute.type = aOidData.oid; aAttribute.encoded = PR_TRUE; if (my_NSS_CMSSignerInfo_AddAuthAttr(cms_signer, &aAttribute) != SECSuccess) { return {}; } SECItem cms_output; cms_output.data = nullptr; cms_output.len = 0; 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) { return {}; } if (NSS_CMSEncoder_Finish(cms_ecx) != SECSuccess) { return {}; } auto signature = GooString(reinterpret_cast(cms_output.data), cms_output.len); SECITEM_FreeItem(pEncodedCertificate, PR_TRUE); return signature; } static char *GetPasswordFunction(PK11SlotInfo *slot, PRBool /*retry*/, void * /*arg*/) { const char *name = PK11_GetTokenName(slot); if (PasswordFunction) { return PasswordFunction(name); } return nullptr; } std::unique_ptr NSSCryptoSignBackend::createVerificationHandler(std::vector &&pkcs7) { return std::make_unique(std::move(pkcs7)); } 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); NSSSignatureConfiguration::setNSSDir({}); std::vector> certsList; PK11SlotList *slotList = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_FALSE, nullptr); if (slotList) { for (PK11SlotListElement *slotElement = slotList->head; slotElement; slotElement = slotElement->next) { PK11SlotInfo *pSlot = slotElement->slot; if (PK11_NeedLogin(pSlot)) { SECStatus nRet = PK11_Authenticate(pSlot, PR_TRUE, nullptr); // PK11_Authenticate may fail in case the a slot has not been initialized. // this is the case if the user has a new profile, so that they have never // added a personal certificate. if (nRet != SECSuccess && PORT_GetError() != SEC_ERROR_IO) { continue; } } SECKEYPrivateKeyList *privKeyList = PK11_ListPrivateKeysInSlot(pSlot); if (privKeyList) { for (SECKEYPrivateKeyListNode *curPri = PRIVKEY_LIST_HEAD(privKeyList); !PRIVKEY_LIST_END(curPri, privKeyList) && curPri != nullptr; curPri = PRIVKEY_LIST_NEXT(curPri)) { if (curPri->key) { CERTCertificate *cert = PK11_GetCertFromPrivateKey(curPri->key); if (cert) { certsList.push_back(getCertificateInfoFromCERT(cert)); CERT_DestroyCertificate(cert); } } } SECKEY_DestroyPrivateKeyList(privKeyList); } } PK11_FreeSlotList(slotList); } PK11_SetPasswordFunc(nullptr); 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; poppler-24.02.0/poppler/NSSCryptoSignBackend.h000066400000000000000000000120551455701731300211400ustar00rootroot00000000000000//======================================================================== // // 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 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 /* 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 validateCertificate(std::chrono::system_clock::time_point validation_time, bool ocspRevocationCheck, bool useAIACertFetch) 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; }; 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::optional signDetached(const std::string &password) final; 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) final; std::unique_ptr createSigningHandler(const std::string &certID, HashAlgorithm digestAlgTag) final; std::vector> getAvailableSigningCertificates() final; ~NSSCryptoSignBackend() final; }; #endif poppler-24.02.0/poppler/NameToCharCode.cc000066400000000000000000000056651455701731300201260ustar00rootroot00000000000000//======================================================================== // // NameToCharCode.cc // // Copyright 2001-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) 2019 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 #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; } 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; } } 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); } poppler-24.02.0/poppler/NameToCharCode.h000066400000000000000000000025141455701731300177560ustar00rootroot00000000000000//======================================================================== // // NameToCharCode.h // // Copyright 2001-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) 2018, 2019 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 // //======================================================================== #ifndef NAMETOCHARCODE_H #define NAMETOCHARCODE_H #include "CharTypes.h" 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; }; #endif poppler-24.02.0/poppler/NameToUnicodeTable.h000066400000000000000000014011651455701731300206520ustar00rootroot00000000000000//======================================================================== // // NameToUnicodeTable.h // // Copyright 2001-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) 2011, 2012, 2020 Albert Astals Cid // Copyright (C) 2013 Jason Crain // 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 // //======================================================================== #include "CharTypes.h" #include struct NameToUnicodeTab { Unicode u; const char *name; }; // map character names to Unicode static const struct NameToUnicodeTab nameToUnicodeTextTab[] = { { 0x0021, "!" }, { 0x0023, "#" }, { 0x0024, "$" }, { 0x0025, "%" }, { 0x0026, "&" }, { 0x0027, "'" }, { 0x0028, "(" }, { 0x0029, ")" }, { 0x002a, "*" }, { 0x002b, "+" }, { 0x002c, "," }, { 0x002d, "-" }, { 0x002e, "." }, { 0x002f, "/" }, { 0x0030, "0" }, { 0x0031, "1" }, { 0x0032, "2" }, { 0x0033, "3" }, { 0x0034, "4" }, { 0x0035, "5" }, { 0x0036, "6" }, { 0x0037, "7" }, { 0x0038, "8" }, { 0x0039, "9" }, { 0x003a, ":" }, { 0x003b, ";" }, { 0x003c, "<" }, { 0x003d, "=" }, { 0x003e, ">" }, { 0x003f, "?" }, { 0x0040, "@" }, { 0x0041, "A" }, { 0x00c6, "AE" }, { 0x01fc, "AEacute" }, { 0x01e2, "AEmacron" }, { 0xf7e6, "AEsmall" }, { 0x00c1, "Aacute" }, { 0xf7e1, "Aacutesmall" }, { 0x0102, "Abreve" }, { 0x1eae, "Abreveacute" }, { 0x04d0, "Abrevecyrillic" }, { 0x1eb6, "Abrevedotbelow" }, { 0x1eb0, "Abrevegrave" }, { 0x1eb2, "Abrevehookabove" }, { 0x1eb4, "Abrevetilde" }, { 0x01cd, "Acaron" }, { 0x24b6, "Acircle" }, { 0x00c2, "Acircumflex" }, { 0x1ea4, "Acircumflexacute" }, { 0x1eac, "Acircumflexdotbelow" }, { 0x1ea6, "Acircumflexgrave" }, { 0x1ea8, "Acircumflexhookabove" }, { 0xf7e2, "Acircumflexsmall" }, { 0x1eaa, "Acircumflextilde" }, { 0xf6c9, "Acute" }, { 0xf7b4, "Acutesmall" }, { 0x0410, "Acyrillic" }, { 0x0200, "Adblgrave" }, { 0x00c4, "Adieresis" }, { 0x04d2, "Adieresiscyrillic" }, { 0x01de, "Adieresismacron" }, { 0xf7e4, "Adieresissmall" }, { 0x1ea0, "Adotbelow" }, { 0x01e0, "Adotmacron" }, { 0x00c0, "Agrave" }, { 0xf7e0, "Agravesmall" }, { 0x1ea2, "Ahookabove" }, { 0x04d4, "Aiecyrillic" }, { 0x0202, "Ainvertedbreve" }, { 0x0391, "Alpha" }, { 0x0386, "Alphatonos" }, { 0x0100, "Amacron" }, { 0xff21, "Amonospace" }, { 0x0104, "Aogonek" }, { 0x00c5, "Aring" }, { 0x01fa, "Aringacute" }, { 0x1e00, "Aringbelow" }, { 0xf7e5, "Aringsmall" }, { 0xf761, "Asmall" }, { 0x00c3, "Atilde" }, { 0xf7e3, "Atildesmall" }, { 0x0531, "Aybarmenian" }, { 0x0042, "B" }, { 0x24b7, "Bcircle" }, { 0x1e02, "Bdotaccent" }, { 0x1e04, "Bdotbelow" }, { 0x0411, "Becyrillic" }, { 0x0532, "Benarmenian" }, { 0x0392, "Beta" }, { 0x0181, "Bhook" }, { 0x1e06, "Blinebelow" }, { 0xff22, "Bmonospace" }, { 0xf6f4, "Brevesmall" }, { 0xf762, "Bsmall" }, { 0x0182, "Btopbar" }, { 0x0043, "C" }, { 0x053e, "Caarmenian" }, { 0x0106, "Cacute" }, { 0xf6ca, "Caron" }, { 0xf6f5, "Caronsmall" }, { 0x010c, "Ccaron" }, { 0x00c7, "Ccedilla" }, { 0x1e08, "Ccedillaacute" }, { 0xf7e7, "Ccedillasmall" }, { 0x24b8, "Ccircle" }, { 0x0108, "Ccircumflex" }, { 0x010a, "Cdot" }, { 0x010a, "Cdotaccent" }, { 0xf7b8, "Cedillasmall" }, { 0x0549, "Chaarmenian" }, { 0x04bc, "Cheabkhasiancyrillic" }, { 0x0427, "Checyrillic" }, { 0x04be, "Chedescenderabkhasiancyrillic" }, { 0x04b6, "Chedescendercyrillic" }, { 0x04f4, "Chedieresiscyrillic" }, { 0x0543, "Cheharmenian" }, { 0x04cb, "Chekhakassiancyrillic" }, { 0x04b8, "Cheverticalstrokecyrillic" }, { 0x03a7, "Chi" }, { 0x0187, "Chook" }, { 0xf6f6, "Circumflexsmall" }, { 0xff23, "Cmonospace" }, { 0x0551, "Coarmenian" }, { 0xf763, "Csmall" }, { 0x0044, "D" }, { 0x01f1, "DZ" }, { 0x01c4, "DZcaron" }, { 0x0534, "Daarmenian" }, { 0x0189, "Dafrican" }, { 0x010e, "Dcaron" }, { 0x1e10, "Dcedilla" }, { 0x24b9, "Dcircle" }, { 0x1e12, "Dcircumflexbelow" }, { 0x0110, "Dcroat" }, { 0x1e0a, "Ddotaccent" }, { 0x1e0c, "Ddotbelow" }, { 0x0414, "Decyrillic" }, { 0x03ee, "Deicoptic" }, { 0x2206, "Delta" }, { 0x0394, "Deltagreek" }, { 0x018a, "Dhook" }, { 0xf6cb, "Dieresis" }, { 0xf6cc, "DieresisAcute" }, { 0xf6cd, "DieresisGrave" }, { 0xf7a8, "Dieresissmall" }, { 0x03dc, "Digammagreek" }, { 0x0402, "Djecyrillic" }, { 0x1e0e, "Dlinebelow" }, { 0xff24, "Dmonospace" }, { 0xf6f7, "Dotaccentsmall" }, { 0x0110, "Dslash" }, { 0xf764, "Dsmall" }, { 0x018b, "Dtopbar" }, { 0x01f2, "Dz" }, { 0x01c5, "Dzcaron" }, { 0x04e0, "Dzeabkhasiancyrillic" }, { 0x0405, "Dzecyrillic" }, { 0x040f, "Dzhecyrillic" }, { 0x0045, "E" }, { 0x00c9, "Eacute" }, { 0xf7e9, "Eacutesmall" }, { 0x0114, "Ebreve" }, { 0x011a, "Ecaron" }, { 0x1e1c, "Ecedillabreve" }, { 0x0535, "Echarmenian" }, { 0x24ba, "Ecircle" }, { 0x00ca, "Ecircumflex" }, { 0x1ebe, "Ecircumflexacute" }, { 0x1e18, "Ecircumflexbelow" }, { 0x1ec6, "Ecircumflexdotbelow" }, { 0x1ec0, "Ecircumflexgrave" }, { 0x1ec2, "Ecircumflexhookabove" }, { 0xf7ea, "Ecircumflexsmall" }, { 0x1ec4, "Ecircumflextilde" }, { 0x0404, "Ecyrillic" }, { 0x0204, "Edblgrave" }, { 0x00cb, "Edieresis" }, { 0xf7eb, "Edieresissmall" }, { 0x0116, "Edot" }, { 0x0116, "Edotaccent" }, { 0x1eb8, "Edotbelow" }, { 0x0424, "Efcyrillic" }, { 0x00c8, "Egrave" }, { 0xf7e8, "Egravesmall" }, { 0x0537, "Eharmenian" }, { 0x1eba, "Ehookabove" }, { 0x2167, "Eightroman" }, { 0x0206, "Einvertedbreve" }, { 0x0464, "Eiotifiedcyrillic" }, { 0x041b, "Elcyrillic" }, { 0x216a, "Elevenroman" }, { 0x0112, "Emacron" }, { 0x1e16, "Emacronacute" }, { 0x1e14, "Emacrongrave" }, { 0x041c, "Emcyrillic" }, { 0xff25, "Emonospace" }, { 0x041d, "Encyrillic" }, { 0x04a2, "Endescendercyrillic" }, { 0x014a, "Eng" }, { 0x04a4, "Enghecyrillic" }, { 0x04c7, "Enhookcyrillic" }, { 0x0118, "Eogonek" }, { 0x0190, "Eopen" }, { 0x0395, "Epsilon" }, { 0x0388, "Epsilontonos" }, { 0x0420, "Ercyrillic" }, { 0x018e, "Ereversed" }, { 0x042d, "Ereversedcyrillic" }, { 0x0421, "Escyrillic" }, { 0x04aa, "Esdescendercyrillic" }, { 0x01a9, "Esh" }, { 0xf765, "Esmall" }, { 0x0397, "Eta" }, { 0x0538, "Etarmenian" }, { 0x0389, "Etatonos" }, { 0x00d0, "Eth" }, { 0xf7f0, "Ethsmall" }, { 0x1ebc, "Etilde" }, { 0x1e1a, "Etildebelow" }, { 0x20ac, "Euro" }, { 0x01b7, "Ezh" }, { 0x01ee, "Ezhcaron" }, { 0x01b8, "Ezhreversed" }, { 0x0046, "F" }, { 0x24bb, "Fcircle" }, { 0x1e1e, "Fdotaccent" }, { 0x0556, "Feharmenian" }, { 0x03e4, "Feicoptic" }, { 0x0191, "Fhook" }, { 0x0472, "Fitacyrillic" }, { 0x2164, "Fiveroman" }, { 0xff26, "Fmonospace" }, { 0x2163, "Fourroman" }, { 0xf766, "Fsmall" }, { 0x0047, "G" }, { 0x3387, "GBsquare" }, { 0x01f4, "Gacute" }, { 0x0393, "Gamma" }, { 0x0194, "Gammaafrican" }, { 0x03ea, "Gangiacoptic" }, { 0x011e, "Gbreve" }, { 0x01e6, "Gcaron" }, { 0x0122, "Gcedilla" }, { 0x24bc, "Gcircle" }, { 0x011c, "Gcircumflex" }, { 0x0122, "Gcommaaccent" }, { 0x0120, "Gdot" }, { 0x0120, "Gdotaccent" }, { 0x0413, "Gecyrillic" }, { 0x0542, "Ghadarmenian" }, { 0x0494, "Ghemiddlehookcyrillic" }, { 0x0492, "Ghestrokecyrillic" }, { 0x0490, "Gheupturncyrillic" }, { 0x0193, "Ghook" }, { 0x0533, "Gimarmenian" }, { 0x0403, "Gjecyrillic" }, { 0x1e20, "Gmacron" }, { 0xff27, "Gmonospace" }, { 0xf6ce, "Grave" }, { 0xf760, "Gravesmall" }, { 0xf767, "Gsmall" }, { 0x029b, "Gsmallhook" }, { 0x01e4, "Gstroke" }, { 0x0048, "H" }, { 0x25cf, "H18533" }, { 0x25aa, "H18543" }, { 0x25ab, "H18551" }, { 0x25a1, "H22073" }, { 0x33cb, "HPsquare" }, { 0x04a8, "Haabkhasiancyrillic" }, { 0x04b2, "Hadescendercyrillic" }, { 0x042a, "Hardsigncyrillic" }, { 0x0126, "Hbar" }, { 0x1e2a, "Hbrevebelow" }, { 0x1e28, "Hcedilla" }, { 0x24bd, "Hcircle" }, { 0x0124, "Hcircumflex" }, { 0x1e26, "Hdieresis" }, { 0x1e22, "Hdotaccent" }, { 0x1e24, "Hdotbelow" }, { 0xff28, "Hmonospace" }, { 0x0540, "Hoarmenian" }, { 0x03e8, "Horicoptic" }, { 0xf768, "Hsmall" }, { 0xf6cf, "Hungarumlaut" }, { 0xf6f8, "Hungarumlautsmall" }, { 0x3390, "Hzsquare" }, { 0x0049, "I" }, { 0x042f, "IAcyrillic" }, { 0x0132, "IJ" }, { 0x042e, "IUcyrillic" }, { 0x00cd, "Iacute" }, { 0xf7ed, "Iacutesmall" }, { 0x012c, "Ibreve" }, { 0x01cf, "Icaron" }, { 0x24be, "Icircle" }, { 0x00ce, "Icircumflex" }, { 0xf7ee, "Icircumflexsmall" }, { 0x0406, "Icyrillic" }, { 0x0208, "Idblgrave" }, { 0x00cf, "Idieresis" }, { 0x1e2e, "Idieresisacute" }, { 0x04e4, "Idieresiscyrillic" }, { 0xf7ef, "Idieresissmall" }, { 0x0130, "Idot" }, { 0x0130, "Idotaccent" }, { 0x1eca, "Idotbelow" }, { 0x04d6, "Iebrevecyrillic" }, { 0x0415, "Iecyrillic" }, { 0x2111, "Ifraktur" }, { 0x00cc, "Igrave" }, { 0xf7ec, "Igravesmall" }, { 0x1ec8, "Ihookabove" }, { 0x0418, "Iicyrillic" }, { 0x020a, "Iinvertedbreve" }, { 0x0419, "Iishortcyrillic" }, { 0x012a, "Imacron" }, { 0x04e2, "Imacroncyrillic" }, { 0xff29, "Imonospace" }, { 0x053b, "Iniarmenian" }, { 0x0401, "Iocyrillic" }, { 0x012e, "Iogonek" }, { 0x0399, "Iota" }, { 0x0196, "Iotaafrican" }, { 0x03aa, "Iotadieresis" }, { 0x038a, "Iotatonos" }, { 0xf769, "Ismall" }, { 0x0197, "Istroke" }, { 0x0128, "Itilde" }, { 0x1e2c, "Itildebelow" }, { 0x0474, "Izhitsacyrillic" }, { 0x0476, "Izhitsadblgravecyrillic" }, { 0x004a, "J" }, { 0x0541, "Jaarmenian" }, { 0x24bf, "Jcircle" }, { 0x0134, "Jcircumflex" }, { 0x0408, "Jecyrillic" }, { 0x054b, "Jheharmenian" }, { 0xff2a, "Jmonospace" }, { 0xf76a, "Jsmall" }, { 0x004b, "K" }, { 0x3385, "KBsquare" }, { 0x33cd, "KKsquare" }, { 0x04a0, "Kabashkircyrillic" }, { 0x1e30, "Kacute" }, { 0x041a, "Kacyrillic" }, { 0x049a, "Kadescendercyrillic" }, { 0x04c3, "Kahookcyrillic" }, { 0x039a, "Kappa" }, { 0x049e, "Kastrokecyrillic" }, { 0x049c, "Kaverticalstrokecyrillic" }, { 0x01e8, "Kcaron" }, { 0x0136, "Kcedilla" }, { 0x24c0, "Kcircle" }, { 0x0136, "Kcommaaccent" }, { 0x1e32, "Kdotbelow" }, { 0x0554, "Keharmenian" }, { 0x053f, "Kenarmenian" }, { 0x0425, "Khacyrillic" }, { 0x03e6, "Kheicoptic" }, { 0x0198, "Khook" }, { 0x040c, "Kjecyrillic" }, { 0x1e34, "Klinebelow" }, { 0xff2b, "Kmonospace" }, { 0x0480, "Koppacyrillic" }, { 0x03de, "Koppagreek" }, { 0x046e, "Ksicyrillic" }, { 0xf76b, "Ksmall" }, { 0x004c, "L" }, { 0x01c7, "LJ" }, { 0xf6bf, "LL" }, { 0x0139, "Lacute" }, { 0x039b, "Lambda" }, { 0x013d, "Lcaron" }, { 0x013b, "Lcedilla" }, { 0x24c1, "Lcircle" }, { 0x1e3c, "Lcircumflexbelow" }, { 0x013b, "Lcommaaccent" }, { 0x013f, "Ldot" }, { 0x013f, "Ldotaccent" }, { 0x1e36, "Ldotbelow" }, { 0x1e38, "Ldotbelowmacron" }, { 0x053c, "Liwnarmenian" }, { 0x01c8, "Lj" }, { 0x0409, "Ljecyrillic" }, { 0x1e3a, "Llinebelow" }, { 0xff2c, "Lmonospace" }, { 0x0141, "Lslash" }, { 0xf6f9, "Lslashsmall" }, { 0xf76c, "Lsmall" }, { 0x004d, "M" }, { 0x3386, "MBsquare" }, { 0xf6d0, "Macron" }, { 0xf7af, "Macronsmall" }, { 0x1e3e, "Macute" }, { 0x24c2, "Mcircle" }, { 0x1e40, "Mdotaccent" }, { 0x1e42, "Mdotbelow" }, { 0x0544, "Menarmenian" }, { 0xff2d, "Mmonospace" }, { 0xf76d, "Msmall" }, { 0x019c, "Mturned" }, { 0x039c, "Mu" }, { 0x004e, "N" }, { 0x01ca, "NJ" }, { 0x0143, "Nacute" }, { 0x0147, "Ncaron" }, { 0x0145, "Ncedilla" }, { 0x24c3, "Ncircle" }, { 0x1e4a, "Ncircumflexbelow" }, { 0x0145, "Ncommaaccent" }, { 0x1e44, "Ndotaccent" }, { 0x1e46, "Ndotbelow" }, { 0x019d, "Nhookleft" }, { 0x2168, "Nineroman" }, { 0x01cb, "Nj" }, { 0x040a, "Njecyrillic" }, { 0x1e48, "Nlinebelow" }, { 0xff2e, "Nmonospace" }, { 0x0546, "Nowarmenian" }, { 0xf76e, "Nsmall" }, { 0x00d1, "Ntilde" }, { 0xf7f1, "Ntildesmall" }, { 0x039d, "Nu" }, { 0x004f, "O" }, { 0x0152, "OE" }, { 0xf6fa, "OEsmall" }, { 0x00d3, "Oacute" }, { 0xf7f3, "Oacutesmall" }, { 0x04e8, "Obarredcyrillic" }, { 0x04ea, "Obarreddieresiscyrillic" }, { 0x014e, "Obreve" }, { 0x01d1, "Ocaron" }, { 0x019f, "Ocenteredtilde" }, { 0x24c4, "Ocircle" }, { 0x00d4, "Ocircumflex" }, { 0x1ed0, "Ocircumflexacute" }, { 0x1ed8, "Ocircumflexdotbelow" }, { 0x1ed2, "Ocircumflexgrave" }, { 0x1ed4, "Ocircumflexhookabove" }, { 0xf7f4, "Ocircumflexsmall" }, { 0x1ed6, "Ocircumflextilde" }, { 0x041e, "Ocyrillic" }, { 0x0150, "Odblacute" }, { 0x020c, "Odblgrave" }, { 0x00d6, "Odieresis" }, { 0x04e6, "Odieresiscyrillic" }, { 0xf7f6, "Odieresissmall" }, { 0x1ecc, "Odotbelow" }, { 0xf6fb, "Ogoneksmall" }, { 0x00d2, "Ograve" }, { 0xf7f2, "Ogravesmall" }, { 0x0555, "Oharmenian" }, { 0x2126, "Ohm" }, { 0x1ece, "Ohookabove" }, { 0x01a0, "Ohorn" }, { 0x1eda, "Ohornacute" }, { 0x1ee2, "Ohorndotbelow" }, { 0x1edc, "Ohorngrave" }, { 0x1ede, "Ohornhookabove" }, { 0x1ee0, "Ohorntilde" }, { 0x0150, "Ohungarumlaut" }, { 0x01a2, "Oi" }, { 0x020e, "Oinvertedbreve" }, { 0x014c, "Omacron" }, { 0x1e52, "Omacronacute" }, { 0x1e50, "Omacrongrave" }, { 0x2126, "Omega" }, { 0x0460, "Omegacyrillic" }, { 0x03a9, "Omegagreek" }, { 0x047a, "Omegaroundcyrillic" }, { 0x047c, "Omegatitlocyrillic" }, { 0x038f, "Omegatonos" }, { 0x039f, "Omicron" }, { 0x038c, "Omicrontonos" }, { 0xff2f, "Omonospace" }, { 0x2160, "Oneroman" }, { 0x01ea, "Oogonek" }, { 0x01ec, "Oogonekmacron" }, { 0x0186, "Oopen" }, { 0x00d8, "Oslash" }, { 0x01fe, "Oslashacute" }, { 0xf7f8, "Oslashsmall" }, { 0xf76f, "Osmall" }, { 0x01fe, "Ostrokeacute" }, { 0x047e, "Otcyrillic" }, { 0x00d5, "Otilde" }, { 0x1e4c, "Otildeacute" }, { 0x1e4e, "Otildedieresis" }, { 0xf7f5, "Otildesmall" }, { 0x0050, "P" }, { 0x1e54, "Pacute" }, { 0x24c5, "Pcircle" }, { 0x1e56, "Pdotaccent" }, { 0x041f, "Pecyrillic" }, { 0x054a, "Peharmenian" }, { 0x04a6, "Pemiddlehookcyrillic" }, { 0x03a6, "Phi" }, { 0x01a4, "Phook" }, { 0x03a0, "Pi" }, { 0x0553, "Piwrarmenian" }, { 0xff30, "Pmonospace" }, { 0x03a8, "Psi" }, { 0x0470, "Psicyrillic" }, { 0xf770, "Psmall" }, { 0x0051, "Q" }, { 0x24c6, "Qcircle" }, { 0xff31, "Qmonospace" }, { 0xf771, "Qsmall" }, { 0x0052, "R" }, { 0x054c, "Raarmenian" }, { 0x0154, "Racute" }, { 0x0158, "Rcaron" }, { 0x0156, "Rcedilla" }, { 0x24c7, "Rcircle" }, { 0x0156, "Rcommaaccent" }, { 0x0210, "Rdblgrave" }, { 0x1e58, "Rdotaccent" }, { 0x1e5a, "Rdotbelow" }, { 0x1e5c, "Rdotbelowmacron" }, { 0x0550, "Reharmenian" }, { 0x211c, "Rfraktur" }, { 0x03a1, "Rho" }, { 0xf6fc, "Ringsmall" }, { 0x0212, "Rinvertedbreve" }, { 0x1e5e, "Rlinebelow" }, { 0xff32, "Rmonospace" }, { 0xf772, "Rsmall" }, { 0x0281, "Rsmallinverted" }, { 0x02b6, "Rsmallinvertedsuperior" }, { 0x0053, "S" }, { 0x250c, "SF010000" }, { 0x2514, "SF020000" }, { 0x2510, "SF030000" }, { 0x2518, "SF040000" }, { 0x253c, "SF050000" }, { 0x252c, "SF060000" }, { 0x2534, "SF070000" }, { 0x251c, "SF080000" }, { 0x2524, "SF090000" }, { 0x2500, "SF100000" }, { 0x2502, "SF110000" }, { 0x2561, "SF190000" }, { 0x2562, "SF200000" }, { 0x2556, "SF210000" }, { 0x2555, "SF220000" }, { 0x2563, "SF230000" }, { 0x2551, "SF240000" }, { 0x2557, "SF250000" }, { 0x255d, "SF260000" }, { 0x255c, "SF270000" }, { 0x255b, "SF280000" }, { 0x255e, "SF360000" }, { 0x255f, "SF370000" }, { 0x255a, "SF380000" }, { 0x2554, "SF390000" }, { 0x2569, "SF400000" }, { 0x2566, "SF410000" }, { 0x2560, "SF420000" }, { 0x2550, "SF430000" }, { 0x256c, "SF440000" }, { 0x2567, "SF450000" }, { 0x2568, "SF460000" }, { 0x2564, "SF470000" }, { 0x2565, "SF480000" }, { 0x2559, "SF490000" }, { 0x2558, "SF500000" }, { 0x2552, "SF510000" }, { 0x2553, "SF520000" }, { 0x256b, "SF530000" }, { 0x256a, "SF540000" }, { 0x015a, "Sacute" }, { 0x1e64, "Sacutedotaccent" }, { 0x03e0, "Sampigreek" }, { 0x0160, "Scaron" }, { 0x1e66, "Scarondotaccent" }, { 0xf6fd, "Scaronsmall" }, { 0x015e, "Scedilla" }, { 0x018f, "Schwa" }, { 0x04d8, "Schwacyrillic" }, { 0x04da, "Schwadieresiscyrillic" }, { 0x24c8, "Scircle" }, { 0x015c, "Scircumflex" }, { 0x0218, "Scommaaccent" }, { 0x1e60, "Sdotaccent" }, { 0x1e62, "Sdotbelow" }, { 0x1e68, "Sdotbelowdotaccent" }, { 0x054d, "Seharmenian" }, { 0x2166, "Sevenroman" }, { 0x0547, "Shaarmenian" }, { 0x0428, "Shacyrillic" }, { 0x0429, "Shchacyrillic" }, { 0x03e2, "Sheicoptic" }, { 0x04ba, "Shhacyrillic" }, { 0x03ec, "Shimacoptic" }, { 0x03a3, "Sigma" }, { 0x2165, "Sixroman" }, { 0xff33, "Smonospace" }, { 0x042c, "Softsigncyrillic" }, { 0xf773, "Ssmall" }, { 0x03da, "Stigmagreek" }, { 0x0054, "T" }, { 0x03a4, "Tau" }, { 0x0166, "Tbar" }, { 0x0164, "Tcaron" }, { 0x0162, "Tcedilla" }, { 0x24c9, "Tcircle" }, { 0x1e70, "Tcircumflexbelow" }, { 0x0162, "Tcommaaccent" }, { 0x1e6a, "Tdotaccent" }, { 0x1e6c, "Tdotbelow" }, { 0x0422, "Tecyrillic" }, { 0x04ac, "Tedescendercyrillic" }, { 0x2169, "Tenroman" }, { 0x04b4, "Tetsecyrillic" }, { 0x0398, "Theta" }, { 0x01ac, "Thook" }, { 0x00de, "Thorn" }, { 0xf7fe, "Thornsmall" }, { 0x2162, "Threeroman" }, { 0xf6fe, "Tildesmall" }, { 0x054f, "Tiwnarmenian" }, { 0x1e6e, "Tlinebelow" }, { 0xff34, "Tmonospace" }, { 0x0539, "Toarmenian" }, { 0x01bc, "Tonefive" }, { 0x0184, "Tonesix" }, { 0x01a7, "Tonetwo" }, { 0x01ae, "Tretroflexhook" }, { 0x0426, "Tsecyrillic" }, { 0x040b, "Tshecyrillic" }, { 0xf774, "Tsmall" }, { 0x216b, "Twelveroman" }, { 0x2161, "Tworoman" }, { 0x0055, "U" }, { 0x00da, "Uacute" }, { 0xf7fa, "Uacutesmall" }, { 0x016c, "Ubreve" }, { 0x01d3, "Ucaron" }, { 0x24ca, "Ucircle" }, { 0x00db, "Ucircumflex" }, { 0x1e76, "Ucircumflexbelow" }, { 0xf7fb, "Ucircumflexsmall" }, { 0x0423, "Ucyrillic" }, { 0x0170, "Udblacute" }, { 0x0214, "Udblgrave" }, { 0x00dc, "Udieresis" }, { 0x01d7, "Udieresisacute" }, { 0x1e72, "Udieresisbelow" }, { 0x01d9, "Udieresiscaron" }, { 0x04f0, "Udieresiscyrillic" }, { 0x01db, "Udieresisgrave" }, { 0x01d5, "Udieresismacron" }, { 0xf7fc, "Udieresissmall" }, { 0x1ee4, "Udotbelow" }, { 0x00d9, "Ugrave" }, { 0xf7f9, "Ugravesmall" }, { 0x1ee6, "Uhookabove" }, { 0x01af, "Uhorn" }, { 0x1ee8, "Uhornacute" }, { 0x1ef0, "Uhorndotbelow" }, { 0x1eea, "Uhorngrave" }, { 0x1eec, "Uhornhookabove" }, { 0x1eee, "Uhorntilde" }, { 0x0170, "Uhungarumlaut" }, { 0x04f2, "Uhungarumlautcyrillic" }, { 0x0216, "Uinvertedbreve" }, { 0x0478, "Ukcyrillic" }, { 0x016a, "Umacron" }, { 0x04ee, "Umacroncyrillic" }, { 0x1e7a, "Umacrondieresis" }, { 0xff35, "Umonospace" }, { 0x0172, "Uogonek" }, { 0x03a5, "Upsilon" }, { 0x03d2, "Upsilon1" }, { 0x03d3, "Upsilonacutehooksymbolgreek" }, { 0x01b1, "Upsilonafrican" }, { 0x03ab, "Upsilondieresis" }, { 0x03d4, "Upsilondieresishooksymbolgreek" }, { 0x03d2, "Upsilonhooksymbol" }, { 0x038e, "Upsilontonos" }, { 0x016e, "Uring" }, { 0x040e, "Ushortcyrillic" }, { 0xf775, "Usmall" }, { 0x04ae, "Ustraightcyrillic" }, { 0x04b0, "Ustraightstrokecyrillic" }, { 0x0168, "Utilde" }, { 0x1e78, "Utildeacute" }, { 0x1e74, "Utildebelow" }, { 0x0056, "V" }, { 0x24cb, "Vcircle" }, { 0x1e7e, "Vdotbelow" }, { 0x0412, "Vecyrillic" }, { 0x054e, "Vewarmenian" }, { 0x01b2, "Vhook" }, { 0xff36, "Vmonospace" }, { 0x0548, "Voarmenian" }, { 0xf776, "Vsmall" }, { 0x1e7c, "Vtilde" }, { 0x0057, "W" }, { 0x1e82, "Wacute" }, { 0x24cc, "Wcircle" }, { 0x0174, "Wcircumflex" }, { 0x1e84, "Wdieresis" }, { 0x1e86, "Wdotaccent" }, { 0x1e88, "Wdotbelow" }, { 0x1e80, "Wgrave" }, { 0xff37, "Wmonospace" }, { 0xf777, "Wsmall" }, { 0x0058, "X" }, { 0x24cd, "Xcircle" }, { 0x1e8c, "Xdieresis" }, { 0x1e8a, "Xdotaccent" }, { 0x053d, "Xeharmenian" }, { 0x039e, "Xi" }, { 0xff38, "Xmonospace" }, { 0xf778, "Xsmall" }, { 0x0059, "Y" }, { 0x00dd, "Yacute" }, { 0xf7fd, "Yacutesmall" }, { 0x0462, "Yatcyrillic" }, { 0x24ce, "Ycircle" }, { 0x0176, "Ycircumflex" }, { 0x0178, "Ydieresis" }, { 0xf7ff, "Ydieresissmall" }, { 0x1e8e, "Ydotaccent" }, { 0x1ef4, "Ydotbelow" }, { 0x042b, "Yericyrillic" }, { 0x04f8, "Yerudieresiscyrillic" }, { 0x1ef2, "Ygrave" }, { 0x01b3, "Yhook" }, { 0x1ef6, "Yhookabove" }, { 0x0545, "Yiarmenian" }, { 0x0407, "Yicyrillic" }, { 0x0552, "Yiwnarmenian" }, { 0xff39, "Ymonospace" }, { 0xf779, "Ysmall" }, { 0x1ef8, "Ytilde" }, { 0x046a, "Yusbigcyrillic" }, { 0x046c, "Yusbigiotifiedcyrillic" }, { 0x0466, "Yuslittlecyrillic" }, { 0x0468, "Yuslittleiotifiedcyrillic" }, { 0x005a, "Z" }, { 0x0536, "Zaarmenian" }, { 0x0179, "Zacute" }, { 0x017d, "Zcaron" }, { 0xf6ff, "Zcaronsmall" }, { 0x24cf, "Zcircle" }, { 0x1e90, "Zcircumflex" }, { 0x017b, "Zdot" }, { 0x017b, "Zdotaccent" }, { 0x1e92, "Zdotbelow" }, { 0x0417, "Zecyrillic" }, { 0x0498, "Zedescendercyrillic" }, { 0x04de, "Zedieresiscyrillic" }, { 0x0396, "Zeta" }, { 0x053a, "Zhearmenian" }, { 0x04c1, "Zhebrevecyrillic" }, { 0x0416, "Zhecyrillic" }, { 0x0496, "Zhedescendercyrillic" }, { 0x04dc, "Zhedieresiscyrillic" }, { 0x1e94, "Zlinebelow" }, { 0xff3a, "Zmonospace" }, { 0xf77a, "Zsmall" }, { 0x01b5, "Zstroke" }, { 0x0022, "\"" }, { 0x005c, "\\" }, { 0x005d, "]" }, { 0x005e, "^" }, { 0x005f, "_" }, { 0x0060, "`" }, { 0x0061, "a" }, { 0x0986, "aabengali" }, { 0x00e1, "aacute" }, { 0x0906, "aadeva" }, { 0x0a86, "aagujarati" }, { 0x0a06, "aagurmukhi" }, { 0x0a3e, "aamatragurmukhi" }, { 0x3303, "aarusquare" }, { 0x09be, "aavowelsignbengali" }, { 0x093e, "aavowelsigndeva" }, { 0x0abe, "aavowelsigngujarati" }, { 0x055f, "abbreviationmarkarmenian" }, { 0x0970, "abbreviationsigndeva" }, { 0x0985, "abengali" }, { 0x311a, "abopomofo" }, { 0x0103, "abreve" }, { 0x1eaf, "abreveacute" }, { 0x04d1, "abrevecyrillic" }, { 0x1eb7, "abrevedotbelow" }, { 0x1eb1, "abrevegrave" }, { 0x1eb3, "abrevehookabove" }, { 0x1eb5, "abrevetilde" }, { 0x01ce, "acaron" }, { 0x24d0, "acircle" }, { 0x00e2, "acircumflex" }, { 0x1ea5, "acircumflexacute" }, { 0x1ead, "acircumflexdotbelow" }, { 0x1ea7, "acircumflexgrave" }, { 0x1ea9, "acircumflexhookabove" }, { 0x1eab, "acircumflextilde" }, { 0x00b4, "acute" }, { 0x0317, "acutebelowcmb" }, { 0x0301, "acutecmb" }, { 0x0301, "acutecomb" }, { 0x0954, "acutedeva" }, { 0x02cf, "acutelowmod" }, { 0x0341, "acutetonecmb" }, { 0x0430, "acyrillic" }, { 0x0201, "adblgrave" }, { 0x0a71, "addakgurmukhi" }, { 0x0905, "adeva" }, { 0x00e4, "adieresis" }, { 0x04d3, "adieresiscyrillic" }, { 0x01df, "adieresismacron" }, { 0x1ea1, "adotbelow" }, { 0x01e1, "adotmacron" }, { 0x00e6, "ae" }, { 0x01fd, "aeacute" }, { 0x3150, "aekorean" }, { 0x01e3, "aemacron" }, { 0x2015, "afii00208" }, { 0x20a4, "afii08941" }, { 0x0410, "afii10017" }, { 0x0411, "afii10018" }, { 0x0412, "afii10019" }, { 0x0413, "afii10020" }, { 0x0414, "afii10021" }, { 0x0415, "afii10022" }, { 0x0401, "afii10023" }, { 0x0416, "afii10024" }, { 0x0417, "afii10025" }, { 0x0418, "afii10026" }, { 0x0419, "afii10027" }, { 0x041a, "afii10028" }, { 0x041b, "afii10029" }, { 0x041c, "afii10030" }, { 0x041d, "afii10031" }, { 0x041e, "afii10032" }, { 0x041f, "afii10033" }, { 0x0420, "afii10034" }, { 0x0421, "afii10035" }, { 0x0422, "afii10036" }, { 0x0423, "afii10037" }, { 0x0424, "afii10038" }, { 0x0425, "afii10039" }, { 0x0426, "afii10040" }, { 0x0427, "afii10041" }, { 0x0428, "afii10042" }, { 0x0429, "afii10043" }, { 0x042a, "afii10044" }, { 0x042b, "afii10045" }, { 0x042c, "afii10046" }, { 0x042d, "afii10047" }, { 0x042e, "afii10048" }, { 0x042f, "afii10049" }, { 0x0490, "afii10050" }, { 0x0402, "afii10051" }, { 0x0403, "afii10052" }, { 0x0404, "afii10053" }, { 0x0405, "afii10054" }, { 0x0406, "afii10055" }, { 0x0407, "afii10056" }, { 0x0408, "afii10057" }, { 0x0409, "afii10058" }, { 0x040a, "afii10059" }, { 0x040b, "afii10060" }, { 0x040c, "afii10061" }, { 0x040e, "afii10062" }, { 0xf6c4, "afii10063" }, { 0xf6c5, "afii10064" }, { 0x0430, "afii10065" }, { 0x0431, "afii10066" }, { 0x0432, "afii10067" }, { 0x0433, "afii10068" }, { 0x0434, "afii10069" }, { 0x0435, "afii10070" }, { 0x0451, "afii10071" }, { 0x0436, "afii10072" }, { 0x0437, "afii10073" }, { 0x0438, "afii10074" }, { 0x0439, "afii10075" }, { 0x043a, "afii10076" }, { 0x043b, "afii10077" }, { 0x043c, "afii10078" }, { 0x043d, "afii10079" }, { 0x043e, "afii10080" }, { 0x043f, "afii10081" }, { 0x0440, "afii10082" }, { 0x0441, "afii10083" }, { 0x0442, "afii10084" }, { 0x0443, "afii10085" }, { 0x0444, "afii10086" }, { 0x0445, "afii10087" }, { 0x0446, "afii10088" }, { 0x0447, "afii10089" }, { 0x0448, "afii10090" }, { 0x0449, "afii10091" }, { 0x044a, "afii10092" }, { 0x044b, "afii10093" }, { 0x044c, "afii10094" }, { 0x044d, "afii10095" }, { 0x044e, "afii10096" }, { 0x044f, "afii10097" }, { 0x0491, "afii10098" }, { 0x0452, "afii10099" }, { 0x0453, "afii10100" }, { 0x0454, "afii10101" }, { 0x0455, "afii10102" }, { 0x0456, "afii10103" }, { 0x0457, "afii10104" }, { 0x0458, "afii10105" }, { 0x0459, "afii10106" }, { 0x045a, "afii10107" }, { 0x045b, "afii10108" }, { 0x045c, "afii10109" }, { 0x045e, "afii10110" }, { 0x040f, "afii10145" }, { 0x0462, "afii10146" }, { 0x0472, "afii10147" }, { 0x0474, "afii10148" }, { 0xf6c6, "afii10192" }, { 0x045f, "afii10193" }, { 0x0463, "afii10194" }, { 0x0473, "afii10195" }, { 0x0475, "afii10196" }, { 0xf6c7, "afii10831" }, { 0xf6c8, "afii10832" }, { 0x04d9, "afii10846" }, { 0x200e, "afii299" }, { 0x200f, "afii300" }, { 0x200d, "afii301" }, { 0x066a, "afii57381" }, { 0x060c, "afii57388" }, { 0x0660, "afii57392" }, { 0x0661, "afii57393" }, { 0x0662, "afii57394" }, { 0x0663, "afii57395" }, { 0x0664, "afii57396" }, { 0x0665, "afii57397" }, { 0x0666, "afii57398" }, { 0x0667, "afii57399" }, { 0x0668, "afii57400" }, { 0x0669, "afii57401" }, { 0x061b, "afii57403" }, { 0x061f, "afii57407" }, { 0x0621, "afii57409" }, { 0x0622, "afii57410" }, { 0x0623, "afii57411" }, { 0x0624, "afii57412" }, { 0x0625, "afii57413" }, { 0x0626, "afii57414" }, { 0x0627, "afii57415" }, { 0x0628, "afii57416" }, { 0x0629, "afii57417" }, { 0x062a, "afii57418" }, { 0x062b, "afii57419" }, { 0x062c, "afii57420" }, { 0x062d, "afii57421" }, { 0x062e, "afii57422" }, { 0x062f, "afii57423" }, { 0x0630, "afii57424" }, { 0x0631, "afii57425" }, { 0x0632, "afii57426" }, { 0x0633, "afii57427" }, { 0x0634, "afii57428" }, { 0x0635, "afii57429" }, { 0x0636, "afii57430" }, { 0x0637, "afii57431" }, { 0x0638, "afii57432" }, { 0x0639, "afii57433" }, { 0x063a, "afii57434" }, { 0x0640, "afii57440" }, { 0x0641, "afii57441" }, { 0x0642, "afii57442" }, { 0x0643, "afii57443" }, { 0x0644, "afii57444" }, { 0x0645, "afii57445" }, { 0x0646, "afii57446" }, { 0x0648, "afii57448" }, { 0x0649, "afii57449" }, { 0x064a, "afii57450" }, { 0x064b, "afii57451" }, { 0x064c, "afii57452" }, { 0x064d, "afii57453" }, { 0x064e, "afii57454" }, { 0x064f, "afii57455" }, { 0x0650, "afii57456" }, { 0x0651, "afii57457" }, { 0x0652, "afii57458" }, { 0x0647, "afii57470" }, { 0x06a4, "afii57505" }, { 0x067e, "afii57506" }, { 0x0686, "afii57507" }, { 0x0698, "afii57508" }, { 0x06af, "afii57509" }, { 0x0679, "afii57511" }, { 0x0688, "afii57512" }, { 0x0691, "afii57513" }, { 0x06ba, "afii57514" }, { 0x06d2, "afii57519" }, { 0x06d5, "afii57534" }, { 0x20aa, "afii57636" }, { 0x05be, "afii57645" }, { 0x05c3, "afii57658" }, { 0x05d0, "afii57664" }, { 0x05d1, "afii57665" }, { 0x05d2, "afii57666" }, { 0x05d3, "afii57667" }, { 0x05d4, "afii57668" }, { 0x05d5, "afii57669" }, { 0x05d6, "afii57670" }, { 0x05d7, "afii57671" }, { 0x05d8, "afii57672" }, { 0x05d9, "afii57673" }, { 0x05da, "afii57674" }, { 0x05db, "afii57675" }, { 0x05dc, "afii57676" }, { 0x05dd, "afii57677" }, { 0x05de, "afii57678" }, { 0x05df, "afii57679" }, { 0x05e0, "afii57680" }, { 0x05e1, "afii57681" }, { 0x05e2, "afii57682" }, { 0x05e3, "afii57683" }, { 0x05e4, "afii57684" }, { 0x05e5, "afii57685" }, { 0x05e6, "afii57686" }, { 0x05e7, "afii57687" }, { 0x05e8, "afii57688" }, { 0x05e9, "afii57689" }, { 0x05ea, "afii57690" }, { 0xfb2a, "afii57694" }, { 0xfb2b, "afii57695" }, { 0xfb4b, "afii57700" }, { 0xfb1f, "afii57705" }, { 0x05f0, "afii57716" }, { 0x05f1, "afii57717" }, { 0x05f2, "afii57718" }, { 0xfb35, "afii57723" }, { 0x05b4, "afii57793" }, { 0x05b5, "afii57794" }, { 0x05b6, "afii57795" }, { 0x05bb, "afii57796" }, { 0x05b8, "afii57797" }, { 0x05b7, "afii57798" }, { 0x05b0, "afii57799" }, { 0x05b2, "afii57800" }, { 0x05b1, "afii57801" }, { 0x05b3, "afii57802" }, { 0x05c2, "afii57803" }, { 0x05c1, "afii57804" }, { 0x05b9, "afii57806" }, { 0x05bc, "afii57807" }, { 0x05bd, "afii57839" }, { 0x05bf, "afii57841" }, { 0x05c0, "afii57842" }, { 0x02bc, "afii57929" }, { 0x2105, "afii61248" }, { 0x2113, "afii61289" }, { 0x2116, "afii61352" }, { 0x202c, "afii61573" }, { 0x202d, "afii61574" }, { 0x202e, "afii61575" }, { 0x200c, "afii61664" }, { 0x066d, "afii63167" }, { 0x02bd, "afii64937" }, { 0x00e0, "agrave" }, { 0x0a85, "agujarati" }, { 0x0a05, "agurmukhi" }, { 0x3042, "ahiragana" }, { 0x1ea3, "ahookabove" }, { 0x0990, "aibengali" }, { 0x311e, "aibopomofo" }, { 0x0910, "aideva" }, { 0x04d5, "aiecyrillic" }, { 0x0a90, "aigujarati" }, { 0x0a10, "aigurmukhi" }, { 0x0a48, "aimatragurmukhi" }, { 0x0639, "ainarabic" }, { 0xfeca, "ainfinalarabic" }, { 0xfecb, "aininitialarabic" }, { 0xfecc, "ainmedialarabic" }, { 0x0203, "ainvertedbreve" }, { 0x09c8, "aivowelsignbengali" }, { 0x0948, "aivowelsigndeva" }, { 0x0ac8, "aivowelsigngujarati" }, { 0x30a2, "akatakana" }, { 0xff71, "akatakanahalfwidth" }, { 0x314f, "akorean" }, { 0x05d0, "alef" }, { 0x0627, "alefarabic" }, { 0xfb30, "alefdageshhebrew" }, { 0xfe8e, "aleffinalarabic" }, { 0x0623, "alefhamzaabovearabic" }, { 0xfe84, "alefhamzaabovefinalarabic" }, { 0x0625, "alefhamzabelowarabic" }, { 0xfe88, "alefhamzabelowfinalarabic" }, { 0x05d0, "alefhebrew" }, { 0xfb4f, "aleflamedhebrew" }, { 0x0622, "alefmaddaabovearabic" }, { 0xfe82, "alefmaddaabovefinalarabic" }, { 0x0649, "alefmaksuraarabic" }, { 0xfef0, "alefmaksurafinalarabic" }, { 0xfef3, "alefmaksurainitialarabic" }, { 0xfef4, "alefmaksuramedialarabic" }, { 0xfb2e, "alefpatahhebrew" }, { 0xfb2f, "alefqamatshebrew" }, { 0x2135, "aleph" }, { 0x224c, "allequal" }, { 0x03b1, "alpha" }, { 0x03ac, "alphatonos" }, { 0x0101, "amacron" }, { 0xff41, "amonospace" }, { 0x0026, "ampersand" }, { 0xff06, "ampersandmonospace" }, { 0xf726, "ampersandsmall" }, { 0x33c2, "amsquare" }, { 0x3122, "anbopomofo" }, { 0x3124, "angbopomofo" }, { 0x0e5a, "angkhankhuthai" }, { 0x2220, "angle" }, { 0x3008, "anglebracketleft" }, { 0xfe3f, "anglebracketleftvertical" }, { 0x3009, "anglebracketright" }, { 0xfe40, "anglebracketrightvertical" }, { 0x2329, "angleleft" }, { 0x232a, "angleright" }, { 0x212b, "angstrom" }, { 0x0387, "anoteleia" }, { 0x0952, "anudattadeva" }, { 0x0982, "anusvarabengali" }, { 0x0902, "anusvaradeva" }, { 0x0a82, "anusvaragujarati" }, { 0x0105, "aogonek" }, { 0x3300, "apaatosquare" }, { 0x249c, "aparen" }, { 0x055a, "apostrophearmenian" }, { 0x02bc, "apostrophemod" }, { 0xf8ff, "apple" }, { 0x2250, "approaches" }, { 0x2248, "approxequal" }, { 0x2252, "approxequalorimage" }, { 0x2245, "approximatelyequal" }, { 0x318e, "araeaekorean" }, { 0x318d, "araeakorean" }, { 0x2312, "arc" }, { 0x1e9a, "arighthalfring" }, { 0x00e5, "aring" }, { 0x01fb, "aringacute" }, { 0x1e01, "aringbelow" }, { 0x2194, "arrowboth" }, { 0x21e3, "arrowdashdown" }, { 0x21e0, "arrowdashleft" }, { 0x21e2, "arrowdashright" }, { 0x21e1, "arrowdashup" }, { 0x21d4, "arrowdblboth" }, { 0x21d3, "arrowdbldown" }, { 0x21d0, "arrowdblleft" }, { 0x21d2, "arrowdblright" }, { 0x21d1, "arrowdblup" }, { 0x2193, "arrowdown" }, { 0x2199, "arrowdownleft" }, { 0x2198, "arrowdownright" }, { 0x21e9, "arrowdownwhite" }, { 0x02c5, "arrowheaddownmod" }, { 0x02c2, "arrowheadleftmod" }, { 0x02c3, "arrowheadrightmod" }, { 0x02c4, "arrowheadupmod" }, { 0xf8e7, "arrowhorizex" }, { 0x2190, "arrowleft" }, { 0x21d0, "arrowleftdbl" }, { 0x21cd, "arrowleftdblstroke" }, { 0x21c6, "arrowleftoverright" }, { 0x21e6, "arrowleftwhite" }, { 0x2192, "arrowright" }, { 0x21cf, "arrowrightdblstroke" }, { 0x279e, "arrowrightheavy" }, { 0x21c4, "arrowrightoverleft" }, { 0x21e8, "arrowrightwhite" }, { 0x21e4, "arrowtableft" }, { 0x21e5, "arrowtabright" }, { 0x2191, "arrowup" }, { 0x2195, "arrowupdn" }, { 0x21a8, "arrowupdnbse" }, { 0x21a8, "arrowupdownbase" }, { 0x2196, "arrowupleft" }, { 0x21c5, "arrowupleftofdown" }, { 0x2197, "arrowupright" }, { 0x21e7, "arrowupwhite" }, { 0xf8e6, "arrowvertex" }, { 0x005e, "asciicircum" }, { 0xff3e, "asciicircummonospace" }, { 0x007e, "asciitilde" }, { 0xff5e, "asciitildemonospace" }, { 0x0251, "ascript" }, { 0x0252, "ascriptturned" }, { 0x3041, "asmallhiragana" }, { 0x30a1, "asmallkatakana" }, { 0xff67, "asmallkatakanahalfwidth" }, { 0x002a, "asterisk" }, { 0x066d, "asteriskaltonearabic" }, { 0x066d, "asteriskarabic" }, { 0x2217, "asteriskmath" }, { 0xff0a, "asteriskmonospace" }, { 0xfe61, "asterisksmall" }, { 0x2042, "asterism" }, { 0xf6e9, "asuperior" }, { 0x2243, "asymptoticallyequal" }, { 0x0040, "at" }, { 0x00e3, "atilde" }, { 0xff20, "atmonospace" }, { 0xfe6b, "atsmall" }, { 0x0250, "aturned" }, { 0x0994, "aubengali" }, { 0x3120, "aubopomofo" }, { 0x0914, "audeva" }, { 0x0a94, "augujarati" }, { 0x0a14, "augurmukhi" }, { 0x09d7, "aulengthmarkbengali" }, { 0x0a4c, "aumatragurmukhi" }, { 0x09cc, "auvowelsignbengali" }, { 0x094c, "auvowelsigndeva" }, { 0x0acc, "auvowelsigngujarati" }, { 0x093d, "avagrahadeva" }, { 0x0561, "aybarmenian" }, { 0x05e2, "ayin" }, { 0xfb20, "ayinaltonehebrew" }, { 0x05e2, "ayinhebrew" }, { 0x0062, "b" }, { 0x09ac, "babengali" }, { 0x005c, "backslash" }, { 0xff3c, "backslashmonospace" }, { 0x092c, "badeva" }, { 0x0aac, "bagujarati" }, { 0x0a2c, "bagurmukhi" }, { 0x3070, "bahiragana" }, { 0x0e3f, "bahtthai" }, { 0x30d0, "bakatakana" }, { 0x007c, "bar" }, { 0xff5c, "barmonospace" }, { 0x3105, "bbopomofo" }, { 0x24d1, "bcircle" }, { 0x1e03, "bdotaccent" }, { 0x1e05, "bdotbelow" }, { 0x266c, "beamedsixteenthnotes" }, { 0x2235, "because" }, { 0x0431, "becyrillic" }, { 0x0628, "beharabic" }, { 0xfe90, "behfinalarabic" }, { 0xfe91, "behinitialarabic" }, { 0x3079, "behiragana" }, { 0xfe92, "behmedialarabic" }, { 0xfc9f, "behmeeminitialarabic" }, { 0xfc08, "behmeemisolatedarabic" }, { 0xfc6d, "behnoonfinalarabic" }, { 0x30d9, "bekatakana" }, { 0x0562, "benarmenian" }, { 0x05d1, "bet" }, { 0x03b2, "beta" }, { 0x03d0, "betasymbolgreek" }, { 0xfb31, "betdagesh" }, { 0xfb31, "betdageshhebrew" }, { 0x05d1, "bethebrew" }, { 0xfb4c, "betrafehebrew" }, { 0x09ad, "bhabengali" }, { 0x092d, "bhadeva" }, { 0x0aad, "bhagujarati" }, { 0x0a2d, "bhagurmukhi" }, { 0x0253, "bhook" }, { 0x3073, "bihiragana" }, { 0x30d3, "bikatakana" }, { 0x0298, "bilabialclick" }, { 0x0a02, "bindigurmukhi" }, { 0x3331, "birusquare" }, { 0x25cf, "blackcircle" }, { 0x25c6, "blackdiamond" }, { 0x25bc, "blackdownpointingtriangle" }, { 0x25c4, "blackleftpointingpointer" }, { 0x25c0, "blackleftpointingtriangle" }, { 0x3010, "blacklenticularbracketleft" }, { 0xfe3b, "blacklenticularbracketleftvertical" }, { 0x3011, "blacklenticularbracketright" }, { 0xfe3c, "blacklenticularbracketrightvertical" }, { 0x25e3, "blacklowerlefttriangle" }, { 0x25e2, "blacklowerrighttriangle" }, { 0x25ac, "blackrectangle" }, { 0x25ba, "blackrightpointingpointer" }, { 0x25b6, "blackrightpointingtriangle" }, { 0x25aa, "blacksmallsquare" }, { 0x263b, "blacksmilingface" }, { 0x25a0, "blacksquare" }, { 0x2605, "blackstar" }, { 0x25e4, "blackupperlefttriangle" }, { 0x25e5, "blackupperrighttriangle" }, { 0x25b4, "blackuppointingsmalltriangle" }, { 0x25b2, "blackuppointingtriangle" }, { 0x2423, "blank" }, { 0x1e07, "blinebelow" }, { 0x2588, "block" }, { 0xff42, "bmonospace" }, { 0x0e1a, "bobaimaithai" }, { 0x307c, "bohiragana" }, { 0x30dc, "bokatakana" }, { 0x249d, "bparen" }, { 0x33c3, "bqsquare" }, { 0xf8f4, "braceex" }, { 0x007b, "braceleft" }, { 0xf8f3, "braceleftbt" }, { 0xf8f2, "braceleftmid" }, { 0xff5b, "braceleftmonospace" }, { 0xfe5b, "braceleftsmall" }, { 0xf8f1, "bracelefttp" }, { 0xfe37, "braceleftvertical" }, { 0x007d, "braceright" }, { 0xf8fe, "bracerightbt" }, { 0xf8fd, "bracerightmid" }, { 0xff5d, "bracerightmonospace" }, { 0xfe5c, "bracerightsmall" }, { 0xf8fc, "bracerighttp" }, { 0xfe38, "bracerightvertical" }, { 0x005b, "bracketleft" }, { 0xf8f0, "bracketleftbt" }, { 0xf8ef, "bracketleftex" }, { 0xff3b, "bracketleftmonospace" }, { 0xf8ee, "bracketlefttp" }, { 0x005d, "bracketright" }, { 0xf8fb, "bracketrightbt" }, { 0xf8fa, "bracketrightex" }, { 0xff3d, "bracketrightmonospace" }, { 0xf8f9, "bracketrighttp" }, { 0x02d8, "breve" }, { 0x032e, "brevebelowcmb" }, { 0x0306, "brevecmb" }, { 0x032f, "breveinvertedbelowcmb" }, { 0x0311, "breveinvertedcmb" }, { 0x0361, "breveinverteddoublecmb" }, { 0x032a, "bridgebelowcmb" }, { 0x033a, "bridgeinvertedbelowcmb" }, { 0x00a6, "brokenbar" }, { 0x0180, "bstroke" }, { 0xf6ea, "bsuperior" }, { 0x0183, "btopbar" }, { 0x3076, "buhiragana" }, { 0x30d6, "bukatakana" }, { 0x2022, "bullet" }, { 0x25d8, "bulletinverse" }, { 0x2219, "bulletoperator" }, { 0x25ce, "bullseye" }, { 0x0063, "c" }, { 0x056e, "caarmenian" }, { 0x099a, "cabengali" }, { 0x0107, "cacute" }, { 0x091a, "cadeva" }, { 0x0a9a, "cagujarati" }, { 0x0a1a, "cagurmukhi" }, { 0x3388, "calsquare" }, { 0x0981, "candrabindubengali" }, { 0x0310, "candrabinducmb" }, { 0x0901, "candrabindudeva" }, { 0x0a81, "candrabindugujarati" }, { 0x21ea, "capslock" }, { 0x2105, "careof" }, { 0x02c7, "caron" }, { 0x032c, "caronbelowcmb" }, { 0x030c, "caroncmb" }, { 0x21b5, "carriagereturn" }, { 0x3118, "cbopomofo" }, { 0x010d, "ccaron" }, { 0x00e7, "ccedilla" }, { 0x1e09, "ccedillaacute" }, { 0x24d2, "ccircle" }, { 0x0109, "ccircumflex" }, { 0x0255, "ccurl" }, { 0x010b, "cdot" }, { 0x010b, "cdotaccent" }, { 0x33c5, "cdsquare" }, { 0x00b8, "cedilla" }, { 0x0327, "cedillacmb" }, { 0x00a2, "cent" }, { 0x2103, "centigrade" }, { 0xf6df, "centinferior" }, { 0xffe0, "centmonospace" }, { 0xf7a2, "centoldstyle" }, { 0xf6e0, "centsuperior" }, { 0x0579, "chaarmenian" }, { 0x099b, "chabengali" }, { 0x091b, "chadeva" }, { 0x0a9b, "chagujarati" }, { 0x0a1b, "chagurmukhi" }, { 0x3114, "chbopomofo" }, { 0x04bd, "cheabkhasiancyrillic" }, { 0x2713, "checkmark" }, { 0x0447, "checyrillic" }, { 0x04bf, "chedescenderabkhasiancyrillic" }, { 0x04b7, "chedescendercyrillic" }, { 0x04f5, "chedieresiscyrillic" }, { 0x0573, "cheharmenian" }, { 0x04cc, "chekhakassiancyrillic" }, { 0x04b9, "cheverticalstrokecyrillic" }, { 0x03c7, "chi" }, { 0x3277, "chieuchacirclekorean" }, { 0x3217, "chieuchaparenkorean" }, { 0x3269, "chieuchcirclekorean" }, { 0x314a, "chieuchkorean" }, { 0x3209, "chieuchparenkorean" }, { 0x0e0a, "chochangthai" }, { 0x0e08, "chochanthai" }, { 0x0e09, "chochingthai" }, { 0x0e0c, "chochoethai" }, { 0x0188, "chook" }, { 0x3276, "cieucacirclekorean" }, { 0x3216, "cieucaparenkorean" }, { 0x3268, "cieuccirclekorean" }, { 0x3148, "cieuckorean" }, { 0x3208, "cieucparenkorean" }, { 0x321c, "cieucuparenkorean" }, { 0x25cb, "circle" }, { 0x2297, "circlemultiply" }, { 0x2299, "circleot" }, { 0x2295, "circleplus" }, { 0x3036, "circlepostalmark" }, { 0x25d0, "circlewithlefthalfblack" }, { 0x25d1, "circlewithrighthalfblack" }, { 0x02c6, "circumflex" }, { 0x032d, "circumflexbelowcmb" }, { 0x0302, "circumflexcmb" }, { 0x2327, "clear" }, { 0x01c2, "clickalveolar" }, { 0x01c0, "clickdental" }, { 0x01c1, "clicklateral" }, { 0x01c3, "clickretroflex" }, { 0x2663, "club" }, { 0x2663, "clubsuitblack" }, { 0x2667, "clubsuitwhite" }, { 0x33a4, "cmcubedsquare" }, { 0xff43, "cmonospace" }, { 0x33a0, "cmsquaredsquare" }, { 0x0581, "coarmenian" }, { 0x003a, "colon" }, { 0x20a1, "colonmonetary" }, { 0xff1a, "colonmonospace" }, { 0x20a1, "colonsign" }, { 0xfe55, "colonsmall" }, { 0x02d1, "colontriangularhalfmod" }, { 0x02d0, "colontriangularmod" }, { 0x002c, "comma" }, { 0x0313, "commaabovecmb" }, { 0x0315, "commaaboverightcmb" }, { 0xf6c3, "commaaccent" }, { 0x060c, "commaarabic" }, { 0x055d, "commaarmenian" }, { 0xf6e1, "commainferior" }, { 0xff0c, "commamonospace" }, { 0x0314, "commareversedabovecmb" }, { 0x02bd, "commareversedmod" }, { 0xfe50, "commasmall" }, { 0xf6e2, "commasuperior" }, { 0x0312, "commaturnedabovecmb" }, { 0x02bb, "commaturnedmod" }, { 0x263c, "compass" }, { 0x2245, "congruent" }, { 0x222e, "contourintegral" }, { 0x2303, "control" }, { 0x0006, "controlACK" }, { 0x0007, "controlBEL" }, { 0x0008, "controlBS" }, { 0x0018, "controlCAN" }, { 0x000d, "controlCR" }, { 0x0011, "controlDC1" }, { 0x0012, "controlDC2" }, { 0x0013, "controlDC3" }, { 0x0014, "controlDC4" }, { 0x007f, "controlDEL" }, { 0x0010, "controlDLE" }, { 0x0019, "controlEM" }, { 0x0005, "controlENQ" }, { 0x0004, "controlEOT" }, { 0x001b, "controlESC" }, { 0x0017, "controlETB" }, { 0x0003, "controlETX" }, { 0x000c, "controlFF" }, { 0x001c, "controlFS" }, { 0x001d, "controlGS" }, { 0x0009, "controlHT" }, { 0x000a, "controlLF" }, { 0x0015, "controlNAK" }, { 0x001e, "controlRS" }, { 0x000f, "controlSI" }, { 0x000e, "controlSO" }, { 0x0002, "controlSOT" }, { 0x0001, "controlSTX" }, { 0x001a, "controlSUB" }, { 0x0016, "controlSYN" }, { 0x001f, "controlUS" }, { 0x000b, "controlVT" }, { 0x00a9, "copyright" }, { 0xf8e9, "copyrightsans" }, { 0xf6d9, "copyrightserif" }, { 0x300c, "cornerbracketleft" }, { 0xff62, "cornerbracketlefthalfwidth" }, { 0xfe41, "cornerbracketleftvertical" }, { 0x300d, "cornerbracketright" }, { 0xff63, "cornerbracketrighthalfwidth" }, { 0xfe42, "cornerbracketrightvertical" }, { 0x337f, "corporationsquare" }, { 0x33c7, "cosquare" }, { 0x33c6, "coverkgsquare" }, { 0x249e, "cparen" }, { 0x20a2, "cruzeiro" }, { 0x0297, "cstretched" }, { 0x22cf, "curlyand" }, { 0x22ce, "curlyor" }, { 0x00a4, "currency" }, { 0xf6d1, "cyrBreve" }, { 0xf6d2, "cyrFlex" }, { 0xf6d4, "cyrbreve" }, { 0xf6d5, "cyrflex" }, { 0x0064, "d" }, { 0x0564, "daarmenian" }, { 0x09a6, "dabengali" }, { 0x0636, "dadarabic" }, { 0x0926, "dadeva" }, { 0xfebe, "dadfinalarabic" }, { 0xfebf, "dadinitialarabic" }, { 0xfec0, "dadmedialarabic" }, { 0x05bc, "dagesh" }, { 0x05bc, "dageshhebrew" }, { 0x2020, "dagger" }, { 0x2021, "daggerdbl" }, { 0x0aa6, "dagujarati" }, { 0x0a26, "dagurmukhi" }, { 0x3060, "dahiragana" }, { 0x30c0, "dakatakana" }, { 0x062f, "dalarabic" }, { 0x05d3, "dalet" }, { 0xfb33, "daletdagesh" }, { 0xfb33, "daletdageshhebrew" }, { 0x05d3, "dalethebrew" }, { 0xfeaa, "dalfinalarabic" }, { 0x064f, "dammaarabic" }, { 0x064f, "dammalowarabic" }, { 0x064c, "dammatanaltonearabic" }, { 0x064c, "dammatanarabic" }, { 0x0964, "danda" }, { 0x05a7, "dargahebrew" }, { 0x05a7, "dargalefthebrew" }, { 0x0485, "dasiapneumatacyrilliccmb" }, { 0xf6d3, "dblGrave" }, { 0x300a, "dblanglebracketleft" }, { 0xfe3d, "dblanglebracketleftvertical" }, { 0x300b, "dblanglebracketright" }, { 0xfe3e, "dblanglebracketrightvertical" }, { 0x032b, "dblarchinvertedbelowcmb" }, { 0x21d4, "dblarrowleft" }, { 0x21d2, "dblarrowright" }, { 0x0965, "dbldanda" }, { 0xf6d6, "dblgrave" }, { 0x030f, "dblgravecmb" }, { 0x222c, "dblintegral" }, { 0x2017, "dbllowline" }, { 0x0333, "dbllowlinecmb" }, { 0x033f, "dbloverlinecmb" }, { 0x02ba, "dblprimemod" }, { 0x2016, "dblverticalbar" }, { 0x030e, "dblverticallineabovecmb" }, { 0x3109, "dbopomofo" }, { 0x33c8, "dbsquare" }, { 0x010f, "dcaron" }, { 0x1e11, "dcedilla" }, { 0x24d3, "dcircle" }, { 0x1e13, "dcircumflexbelow" }, { 0x0111, "dcroat" }, { 0x09a1, "ddabengali" }, { 0x0921, "ddadeva" }, { 0x0aa1, "ddagujarati" }, { 0x0a21, "ddagurmukhi" }, { 0x0688, "ddalarabic" }, { 0xfb89, "ddalfinalarabic" }, { 0x095c, "dddhadeva" }, { 0x09a2, "ddhabengali" }, { 0x0922, "ddhadeva" }, { 0x0aa2, "ddhagujarati" }, { 0x0a22, "ddhagurmukhi" }, { 0x1e0b, "ddotaccent" }, { 0x1e0d, "ddotbelow" }, { 0x066b, "decimalseparatorarabic" }, { 0x066b, "decimalseparatorpersian" }, { 0x0434, "decyrillic" }, { 0x00b0, "degree" }, { 0x05ad, "dehihebrew" }, { 0x3067, "dehiragana" }, { 0x03ef, "deicoptic" }, { 0x30c7, "dekatakana" }, { 0x232b, "deleteleft" }, { 0x2326, "deleteright" }, { 0x03b4, "delta" }, { 0x018d, "deltaturned" }, { 0x09f8, "denominatorminusonenumeratorbengali" }, { 0x02a4, "dezh" }, { 0x09a7, "dhabengali" }, { 0x0927, "dhadeva" }, { 0x0aa7, "dhagujarati" }, { 0x0a27, "dhagurmukhi" }, { 0x0257, "dhook" }, { 0x0385, "dialytikatonos" }, { 0x0344, "dialytikatonoscmb" }, { 0x2666, "diamond" }, { 0x2662, "diamondsuitwhite" }, { 0x00a8, "dieresis" }, { 0xf6d7, "dieresisacute" }, { 0x0324, "dieresisbelowcmb" }, { 0x0308, "dieresiscmb" }, { 0xf6d8, "dieresisgrave" }, { 0x0385, "dieresistonos" }, { 0x3062, "dihiragana" }, { 0x30c2, "dikatakana" }, { 0x3003, "dittomark" }, { 0x00f7, "divide" }, { 0x2223, "divides" }, { 0x2215, "divisionslash" }, { 0x0452, "djecyrillic" }, { 0x2593, "dkshade" }, { 0x1e0f, "dlinebelow" }, { 0x3397, "dlsquare" }, { 0x0111, "dmacron" }, { 0xff44, "dmonospace" }, { 0x2584, "dnblock" }, { 0x0e0e, "dochadathai" }, { 0x0e14, "dodekthai" }, { 0x3069, "dohiragana" }, { 0x30c9, "dokatakana" }, { 0x0024, "dollar" }, { 0xf6e3, "dollarinferior" }, { 0xff04, "dollarmonospace" }, { 0xf724, "dollaroldstyle" }, { 0xfe69, "dollarsmall" }, { 0xf6e4, "dollarsuperior" }, { 0x20ab, "dong" }, { 0x3326, "dorusquare" }, { 0x02d9, "dotaccent" }, { 0x0307, "dotaccentcmb" }, { 0x0323, "dotbelowcmb" }, { 0x0323, "dotbelowcomb" }, { 0x30fb, "dotkatakana" }, { 0x0131, "dotlessi" }, { 0xf6be, "dotlessj" }, { 0x0284, "dotlessjstrokehook" }, { 0x22c5, "dotmath" }, { 0x25cc, "dottedcircle" }, { 0xfb1f, "doubleyodpatah" }, { 0xfb1f, "doubleyodpatahhebrew" }, { 0x031e, "downtackbelowcmb" }, { 0x02d5, "downtackmod" }, { 0x249f, "dparen" }, { 0xf6eb, "dsuperior" }, { 0x0256, "dtail" }, { 0x018c, "dtopbar" }, { 0x3065, "duhiragana" }, { 0x30c5, "dukatakana" }, { 0x01f3, "dz" }, { 0x02a3, "dzaltone" }, { 0x01c6, "dzcaron" }, { 0x02a5, "dzcurl" }, { 0x04e1, "dzeabkhasiancyrillic" }, { 0x0455, "dzecyrillic" }, { 0x045f, "dzhecyrillic" }, { 0x0065, "e" }, { 0x00e9, "eacute" }, { 0x2641, "earth" }, { 0x098f, "ebengali" }, { 0x311c, "ebopomofo" }, { 0x0115, "ebreve" }, { 0x090d, "ecandradeva" }, { 0x0a8d, "ecandragujarati" }, { 0x0945, "ecandravowelsigndeva" }, { 0x0ac5, "ecandravowelsigngujarati" }, { 0x011b, "ecaron" }, { 0x1e1d, "ecedillabreve" }, { 0x0565, "echarmenian" }, { 0x0587, "echyiwnarmenian" }, { 0x24d4, "ecircle" }, { 0x00ea, "ecircumflex" }, { 0x1ebf, "ecircumflexacute" }, { 0x1e19, "ecircumflexbelow" }, { 0x1ec7, "ecircumflexdotbelow" }, { 0x1ec1, "ecircumflexgrave" }, { 0x1ec3, "ecircumflexhookabove" }, { 0x1ec5, "ecircumflextilde" }, { 0x0454, "ecyrillic" }, { 0x0205, "edblgrave" }, { 0x090f, "edeva" }, { 0x00eb, "edieresis" }, { 0x0117, "edot" }, { 0x0117, "edotaccent" }, { 0x1eb9, "edotbelow" }, { 0x0a0f, "eegurmukhi" }, { 0x0a47, "eematragurmukhi" }, { 0x0444, "efcyrillic" }, { 0x00e8, "egrave" }, { 0x0a8f, "egujarati" }, { 0x0567, "eharmenian" }, { 0x311d, "ehbopomofo" }, { 0x3048, "ehiragana" }, { 0x1ebb, "ehookabove" }, { 0x311f, "eibopomofo" }, { 0x0038, "eight" }, { 0x0668, "eightarabic" }, { 0x09ee, "eightbengali" }, { 0x2467, "eightcircle" }, { 0x2791, "eightcircleinversesansserif" }, { 0x096e, "eightdeva" }, { 0x2471, "eighteencircle" }, { 0x2485, "eighteenparen" }, { 0x2499, "eighteenperiod" }, { 0x0aee, "eightgujarati" }, { 0x0a6e, "eightgurmukhi" }, { 0x0668, "eighthackarabic" }, { 0x3028, "eighthangzhou" }, { 0x266b, "eighthnotebeamed" }, { 0x3227, "eightideographicparen" }, { 0x2088, "eightinferior" }, { 0xff18, "eightmonospace" }, { 0xf738, "eightoldstyle" }, { 0x247b, "eightparen" }, { 0x248f, "eightperiod" }, { 0x06f8, "eightpersian" }, { 0x2177, "eightroman" }, { 0x2078, "eightsuperior" }, { 0x0e58, "eightthai" }, { 0x0207, "einvertedbreve" }, { 0x0465, "eiotifiedcyrillic" }, { 0x30a8, "ekatakana" }, { 0xff74, "ekatakanahalfwidth" }, { 0x0a74, "ekonkargurmukhi" }, { 0x3154, "ekorean" }, { 0x043b, "elcyrillic" }, { 0x2208, "element" }, { 0x246a, "elevencircle" }, { 0x247e, "elevenparen" }, { 0x2492, "elevenperiod" }, { 0x217a, "elevenroman" }, { 0x2026, "ellipsis" }, { 0x22ee, "ellipsisvertical" }, { 0x0113, "emacron" }, { 0x1e17, "emacronacute" }, { 0x1e15, "emacrongrave" }, { 0x043c, "emcyrillic" }, { 0x2014, "emdash" }, { 0xfe31, "emdashvertical" }, { 0xff45, "emonospace" }, { 0x055b, "emphasismarkarmenian" }, { 0x2205, "emptyset" }, { 0x3123, "enbopomofo" }, { 0x043d, "encyrillic" }, { 0x2013, "endash" }, { 0xfe32, "endashvertical" }, { 0x04a3, "endescendercyrillic" }, { 0x014b, "eng" }, { 0x3125, "engbopomofo" }, { 0x04a5, "enghecyrillic" }, { 0x04c8, "enhookcyrillic" }, { 0x2002, "enspace" }, { 0x0119, "eogonek" }, { 0x3153, "eokorean" }, { 0x025b, "eopen" }, { 0x029a, "eopenclosed" }, { 0x025c, "eopenreversed" }, { 0x025e, "eopenreversedclosed" }, { 0x025d, "eopenreversedhook" }, { 0x24a0, "eparen" }, { 0x03b5, "epsilon" }, { 0x03ad, "epsilontonos" }, { 0x003d, "equal" }, { 0xff1d, "equalmonospace" }, { 0xfe66, "equalsmall" }, { 0x207c, "equalsuperior" }, { 0x2261, "equivalence" }, { 0x3126, "erbopomofo" }, { 0x0440, "ercyrillic" }, { 0x0258, "ereversed" }, { 0x044d, "ereversedcyrillic" }, { 0x0441, "escyrillic" }, { 0x04ab, "esdescendercyrillic" }, { 0x0283, "esh" }, { 0x0286, "eshcurl" }, { 0x090e, "eshortdeva" }, { 0x0946, "eshortvowelsigndeva" }, { 0x01aa, "eshreversedloop" }, { 0x0285, "eshsquatreversed" }, { 0x3047, "esmallhiragana" }, { 0x30a7, "esmallkatakana" }, { 0xff6a, "esmallkatakanahalfwidth" }, { 0x212e, "estimated" }, { 0xf6ec, "esuperior" }, { 0x03b7, "eta" }, { 0x0568, "etarmenian" }, { 0x03ae, "etatonos" }, { 0x00f0, "eth" }, { 0x1ebd, "etilde" }, { 0x1e1b, "etildebelow" }, { 0x0591, "etnahtafoukhhebrew" }, { 0x0591, "etnahtafoukhlefthebrew" }, { 0x0591, "etnahtahebrew" }, { 0x0591, "etnahtalefthebrew" }, { 0x01dd, "eturned" }, { 0x3161, "eukorean" }, { 0x20ac, "euro" }, { 0x09c7, "evowelsignbengali" }, { 0x0947, "evowelsigndeva" }, { 0x0ac7, "evowelsigngujarati" }, { 0x0021, "exclam" }, { 0x055c, "exclamarmenian" }, { 0x203c, "exclamdbl" }, { 0x00a1, "exclamdown" }, { 0xf7a1, "exclamdownsmall" }, { 0x0021, "exclamleft" }, { 0xff01, "exclammonospace" }, { 0xf721, "exclamsmall" }, { 0x2203, "existential" }, { 0x0292, "ezh" }, { 0x01ef, "ezhcaron" }, { 0x0293, "ezhcurl" }, { 0x01b9, "ezhreversed" }, { 0x01ba, "ezhtail" }, { 0x0066, "f" }, { 0x095e, "fadeva" }, { 0x0a5e, "fagurmukhi" }, { 0x2109, "fahrenheit" }, { 0x064e, "fathaarabic" }, { 0x064e, "fathalowarabic" }, { 0x064b, "fathatanarabic" }, { 0x3108, "fbopomofo" }, { 0x24d5, "fcircle" }, { 0x1e1f, "fdotaccent" }, { 0x0641, "feharabic" }, { 0x0586, "feharmenian" }, { 0xfed2, "fehfinalarabic" }, { 0xfed3, "fehinitialarabic" }, { 0xfed4, "fehmedialarabic" }, { 0x03e5, "feicoptic" }, { 0x2640, "female" }, { 0xfb00, "ff" }, { 0xfb03, "ffi" }, { 0xfb04, "ffl" }, { 0xfb01, "fi" }, { 0x246e, "fifteencircle" }, { 0x2482, "fifteenparen" }, { 0x2496, "fifteenperiod" }, { 0x2012, "figuredash" }, { 0x25a0, "filledbox" }, { 0x25ac, "filledrect" }, { 0x05da, "finalkaf" }, { 0xfb3a, "finalkafdagesh" }, { 0xfb3a, "finalkafdageshhebrew" }, { 0x05da, "finalkafhebrew" }, { 0x05dd, "finalmem" }, { 0x05dd, "finalmemhebrew" }, { 0x05df, "finalnun" }, { 0x05df, "finalnunhebrew" }, { 0x05e3, "finalpe" }, { 0x05e3, "finalpehebrew" }, { 0x05e5, "finaltsadi" }, { 0x05e5, "finaltsadihebrew" }, { 0x02c9, "firsttonechinese" }, { 0x25c9, "fisheye" }, { 0x0473, "fitacyrillic" }, { 0x0035, "five" }, { 0x0665, "fivearabic" }, { 0x09eb, "fivebengali" }, { 0x2464, "fivecircle" }, { 0x278e, "fivecircleinversesansserif" }, { 0x096b, "fivedeva" }, { 0x215d, "fiveeighths" }, { 0x0aeb, "fivegujarati" }, { 0x0a6b, "fivegurmukhi" }, { 0x0665, "fivehackarabic" }, { 0x3025, "fivehangzhou" }, { 0x3224, "fiveideographicparen" }, { 0x2085, "fiveinferior" }, { 0xff15, "fivemonospace" }, { 0xf735, "fiveoldstyle" }, { 0x2478, "fiveparen" }, { 0x248c, "fiveperiod" }, { 0x06f5, "fivepersian" }, { 0x2174, "fiveroman" }, { 0x2075, "fivesuperior" }, { 0x0e55, "fivethai" }, { 0xfb02, "fl" }, { 0x0192, "florin" }, { 0xff46, "fmonospace" }, { 0x3399, "fmsquare" }, { 0x0e1f, "fofanthai" }, { 0x0e1d, "fofathai" }, { 0x0e4f, "fongmanthai" }, { 0x2200, "forall" }, { 0x0034, "four" }, { 0x0664, "fourarabic" }, { 0x09ea, "fourbengali" }, { 0x2463, "fourcircle" }, { 0x278d, "fourcircleinversesansserif" }, { 0x096a, "fourdeva" }, { 0x0aea, "fourgujarati" }, { 0x0a6a, "fourgurmukhi" }, { 0x0664, "fourhackarabic" }, { 0x3024, "fourhangzhou" }, { 0x3223, "fourideographicparen" }, { 0x2084, "fourinferior" }, { 0xff14, "fourmonospace" }, { 0x09f7, "fournumeratorbengali" }, { 0xf734, "fouroldstyle" }, { 0x2477, "fourparen" }, { 0x248b, "fourperiod" }, { 0x06f4, "fourpersian" }, { 0x2173, "fourroman" }, { 0x2074, "foursuperior" }, { 0x246d, "fourteencircle" }, { 0x2481, "fourteenparen" }, { 0x2495, "fourteenperiod" }, { 0x0e54, "fourthai" }, { 0x02cb, "fourthtonechinese" }, { 0x24a1, "fparen" }, { 0x2044, "fraction" }, { 0x20a3, "franc" }, { 0x0067, "g" }, { 0x0997, "gabengali" }, { 0x01f5, "gacute" }, { 0x0917, "gadeva" }, { 0x06af, "gafarabic" }, { 0xfb93, "gaffinalarabic" }, { 0xfb94, "gafinitialarabic" }, { 0xfb95, "gafmedialarabic" }, { 0x0a97, "gagujarati" }, { 0x0a17, "gagurmukhi" }, { 0x304c, "gahiragana" }, { 0x30ac, "gakatakana" }, { 0x03b3, "gamma" }, { 0x0263, "gammalatinsmall" }, { 0x02e0, "gammasuperior" }, { 0x03eb, "gangiacoptic" }, { 0x310d, "gbopomofo" }, { 0x011f, "gbreve" }, { 0x01e7, "gcaron" }, { 0x0123, "gcedilla" }, { 0x24d6, "gcircle" }, { 0x011d, "gcircumflex" }, { 0x0123, "gcommaaccent" }, { 0x0121, "gdot" }, { 0x0121, "gdotaccent" }, { 0x0433, "gecyrillic" }, { 0x3052, "gehiragana" }, { 0x30b2, "gekatakana" }, { 0x2251, "geometricallyequal" }, { 0x059c, "gereshaccenthebrew" }, { 0x05f3, "gereshhebrew" }, { 0x059d, "gereshmuqdamhebrew" }, { 0x00df, "germandbls" }, { 0x059e, "gershayimaccenthebrew" }, { 0x05f4, "gershayimhebrew" }, { 0x3013, "getamark" }, { 0x0998, "ghabengali" }, { 0x0572, "ghadarmenian" }, { 0x0918, "ghadeva" }, { 0x0a98, "ghagujarati" }, { 0x0a18, "ghagurmukhi" }, { 0x063a, "ghainarabic" }, { 0xfece, "ghainfinalarabic" }, { 0xfecf, "ghaininitialarabic" }, { 0xfed0, "ghainmedialarabic" }, { 0x0495, "ghemiddlehookcyrillic" }, { 0x0493, "ghestrokecyrillic" }, { 0x0491, "gheupturncyrillic" }, { 0x095a, "ghhadeva" }, { 0x0a5a, "ghhagurmukhi" }, { 0x0260, "ghook" }, { 0x3393, "ghzsquare" }, { 0x304e, "gihiragana" }, { 0x30ae, "gikatakana" }, { 0x0563, "gimarmenian" }, { 0x05d2, "gimel" }, { 0xfb32, "gimeldagesh" }, { 0xfb32, "gimeldageshhebrew" }, { 0x05d2, "gimelhebrew" }, { 0x0453, "gjecyrillic" }, { 0x01be, "glottalinvertedstroke" }, { 0x0294, "glottalstop" }, { 0x0296, "glottalstopinverted" }, { 0x02c0, "glottalstopmod" }, { 0x0295, "glottalstopreversed" }, { 0x02c1, "glottalstopreversedmod" }, { 0x02e4, "glottalstopreversedsuperior" }, { 0x02a1, "glottalstopstroke" }, { 0x02a2, "glottalstopstrokereversed" }, { 0x1e21, "gmacron" }, { 0xff47, "gmonospace" }, { 0x3054, "gohiragana" }, { 0x30b4, "gokatakana" }, { 0x24a2, "gparen" }, { 0x33ac, "gpasquare" }, { 0x2207, "gradient" }, { 0x0060, "grave" }, { 0x0316, "gravebelowcmb" }, { 0x0300, "gravecmb" }, { 0x0300, "gravecomb" }, { 0x0953, "gravedeva" }, { 0x02ce, "gravelowmod" }, { 0xff40, "gravemonospace" }, { 0x0340, "gravetonecmb" }, { 0x003e, "greater" }, { 0x2265, "greaterequal" }, { 0x22db, "greaterequalorless" }, { 0xff1e, "greatermonospace" }, { 0x2a7e, "greaterorequalslant" }, { 0x2273, "greaterorequivalent" }, { 0x2277, "greaterorless" }, { 0x2267, "greateroverequal" }, { 0xfe65, "greatersmall" }, { 0x0261, "gscript" }, { 0x01e5, "gstroke" }, { 0x3050, "guhiragana" }, { 0x00ab, "guillemotleft" }, { 0x00bb, "guillemotright" }, { 0x2039, "guilsinglleft" }, { 0x203a, "guilsinglright" }, { 0x30b0, "gukatakana" }, { 0x3318, "guramusquare" }, { 0x33c9, "gysquare" }, { 0x0068, "h" }, { 0x04a9, "haabkhasiancyrillic" }, { 0x06c1, "haaltonearabic" }, { 0x09b9, "habengali" }, { 0x04b3, "hadescendercyrillic" }, { 0x0939, "hadeva" }, { 0x0ab9, "hagujarati" }, { 0x0a39, "hagurmukhi" }, { 0x062d, "haharabic" }, { 0xfea2, "hahfinalarabic" }, { 0xfea3, "hahinitialarabic" }, { 0x306f, "hahiragana" }, { 0xfea4, "hahmedialarabic" }, { 0x332a, "haitusquare" }, { 0x30cf, "hakatakana" }, { 0xff8a, "hakatakanahalfwidth" }, { 0x0a4d, "halantgurmukhi" }, { 0x0621, "hamzaarabic" }, { 0x0621, "hamzalowarabic" }, { 0x3164, "hangulfiller" }, { 0x044a, "hardsigncyrillic" }, { 0x21bc, "harpoonleftbarbup" }, { 0x21c0, "harpoonrightbarbup" }, { 0x33ca, "hasquare" }, { 0x05b2, "hatafpatah" }, { 0x05b2, "hatafpatah16" }, { 0x05b2, "hatafpatah23" }, { 0x05b2, "hatafpatah2f" }, { 0x05b2, "hatafpatahhebrew" }, { 0x05b2, "hatafpatahnarrowhebrew" }, { 0x05b2, "hatafpatahquarterhebrew" }, { 0x05b2, "hatafpatahwidehebrew" }, { 0x05b3, "hatafqamats" }, { 0x05b3, "hatafqamats1b" }, { 0x05b3, "hatafqamats28" }, { 0x05b3, "hatafqamats34" }, { 0x05b3, "hatafqamatshebrew" }, { 0x05b3, "hatafqamatsnarrowhebrew" }, { 0x05b3, "hatafqamatsquarterhebrew" }, { 0x05b3, "hatafqamatswidehebrew" }, { 0x05b1, "hatafsegol" }, { 0x05b1, "hatafsegol17" }, { 0x05b1, "hatafsegol24" }, { 0x05b1, "hatafsegol30" }, { 0x05b1, "hatafsegolhebrew" }, { 0x05b1, "hatafsegolnarrowhebrew" }, { 0x05b1, "hatafsegolquarterhebrew" }, { 0x05b1, "hatafsegolwidehebrew" }, { 0x0127, "hbar" }, { 0x310f, "hbopomofo" }, { 0x1e2b, "hbrevebelow" }, { 0x1e29, "hcedilla" }, { 0x24d7, "hcircle" }, { 0x0125, "hcircumflex" }, { 0x1e27, "hdieresis" }, { 0x1e23, "hdotaccent" }, { 0x1e25, "hdotbelow" }, { 0x05d4, "he" }, { 0x2665, "heart" }, { 0x2665, "heartsuitblack" }, { 0x2661, "heartsuitwhite" }, { 0xfb34, "hedagesh" }, { 0xfb34, "hedageshhebrew" }, { 0x06c1, "hehaltonearabic" }, { 0x0647, "heharabic" }, { 0x05d4, "hehebrew" }, { 0xfba7, "hehfinalaltonearabic" }, { 0xfeea, "hehfinalalttwoarabic" }, { 0xfeea, "hehfinalarabic" }, { 0xfba5, "hehhamzaabovefinalarabic" }, { 0xfba4, "hehhamzaaboveisolatedarabic" }, { 0xfba8, "hehinitialaltonearabic" }, { 0xfeeb, "hehinitialarabic" }, { 0x3078, "hehiragana" }, { 0xfba9, "hehmedialaltonearabic" }, { 0xfeec, "hehmedialarabic" }, { 0x337b, "heiseierasquare" }, { 0x30d8, "hekatakana" }, { 0xff8d, "hekatakanahalfwidth" }, { 0x3336, "hekutaarusquare" }, { 0x0267, "henghook" }, { 0x3339, "herutusquare" }, { 0x05d7, "het" }, { 0x05d7, "hethebrew" }, { 0x0266, "hhook" }, { 0x02b1, "hhooksuperior" }, { 0x327b, "hieuhacirclekorean" }, { 0x321b, "hieuhaparenkorean" }, { 0x326d, "hieuhcirclekorean" }, { 0x314e, "hieuhkorean" }, { 0x320d, "hieuhparenkorean" }, { 0x3072, "hihiragana" }, { 0x30d2, "hikatakana" }, { 0xff8b, "hikatakanahalfwidth" }, { 0x05b4, "hiriq" }, { 0x05b4, "hiriq14" }, { 0x05b4, "hiriq21" }, { 0x05b4, "hiriq2d" }, { 0x05b4, "hiriqhebrew" }, { 0x05b4, "hiriqnarrowhebrew" }, { 0x05b4, "hiriqquarterhebrew" }, { 0x05b4, "hiriqwidehebrew" }, { 0x1e96, "hlinebelow" }, { 0xff48, "hmonospace" }, { 0x0570, "hoarmenian" }, { 0x0e2b, "hohipthai" }, { 0x307b, "hohiragana" }, { 0x30db, "hokatakana" }, { 0xff8e, "hokatakanahalfwidth" }, { 0x05b9, "holam" }, { 0x05b9, "holam19" }, { 0x05b9, "holam26" }, { 0x05b9, "holam32" }, { 0x05b9, "holamhebrew" }, { 0x05b9, "holamnarrowhebrew" }, { 0x05b9, "holamquarterhebrew" }, { 0x05b9, "holamwidehebrew" }, { 0x0e2e, "honokhukthai" }, { 0x0309, "hookabovecomb" }, { 0x0309, "hookcmb" }, { 0x0321, "hookpalatalizedbelowcmb" }, { 0x0322, "hookretroflexbelowcmb" }, { 0x3342, "hoonsquare" }, { 0x03e9, "horicoptic" }, { 0x2015, "horizontalbar" }, { 0x031b, "horncmb" }, { 0x2668, "hotsprings" }, { 0x2302, "house" }, { 0x24a3, "hparen" }, { 0x02b0, "hsuperior" }, { 0x0265, "hturned" }, { 0x3075, "huhiragana" }, { 0x3333, "huiitosquare" }, { 0x30d5, "hukatakana" }, { 0xff8c, "hukatakanahalfwidth" }, { 0x02dd, "hungarumlaut" }, { 0x030b, "hungarumlautcmb" }, { 0x0195, "hv" }, { 0x002d, "hyphen" }, { 0xf6e5, "hypheninferior" }, { 0xff0d, "hyphenmonospace" }, { 0xfe63, "hyphensmall" }, { 0xf6e6, "hyphensuperior" }, { 0x2010, "hyphentwo" }, { 0x0069, "i" }, { 0x00ed, "iacute" }, { 0x044f, "iacyrillic" }, { 0x0987, "ibengali" }, { 0x3127, "ibopomofo" }, { 0x012d, "ibreve" }, { 0x01d0, "icaron" }, { 0x24d8, "icircle" }, { 0x00ee, "icircumflex" }, { 0x0456, "icyrillic" }, { 0x0209, "idblgrave" }, { 0x328f, "ideographearthcircle" }, { 0x328b, "ideographfirecircle" }, { 0x323f, "ideographicallianceparen" }, { 0x323a, "ideographiccallparen" }, { 0x32a5, "ideographiccentrecircle" }, { 0x3006, "ideographicclose" }, { 0x3001, "ideographiccomma" }, { 0xff64, "ideographiccommaleft" }, { 0x3237, "ideographiccongratulationparen" }, { 0x32a3, "ideographiccorrectcircle" }, { 0x322f, "ideographicearthparen" }, { 0x323d, "ideographicenterpriseparen" }, { 0x329d, "ideographicexcellentcircle" }, { 0x3240, "ideographicfestivalparen" }, { 0x3296, "ideographicfinancialcircle" }, { 0x3236, "ideographicfinancialparen" }, { 0x322b, "ideographicfireparen" }, { 0x3232, "ideographichaveparen" }, { 0x32a4, "ideographichighcircle" }, { 0x3005, "ideographiciterationmark" }, { 0x3298, "ideographiclaborcircle" }, { 0x3238, "ideographiclaborparen" }, { 0x32a7, "ideographicleftcircle" }, { 0x32a6, "ideographiclowcircle" }, { 0x32a9, "ideographicmedicinecircle" }, { 0x322e, "ideographicmetalparen" }, { 0x322a, "ideographicmoonparen" }, { 0x3234, "ideographicnameparen" }, { 0x3002, "ideographicperiod" }, { 0x329e, "ideographicprintcircle" }, { 0x3243, "ideographicreachparen" }, { 0x3239, "ideographicrepresentparen" }, { 0x323e, "ideographicresourceparen" }, { 0x32a8, "ideographicrightcircle" }, { 0x3299, "ideographicsecretcircle" }, { 0x3242, "ideographicselfparen" }, { 0x3233, "ideographicsocietyparen" }, { 0x3000, "ideographicspace" }, { 0x3235, "ideographicspecialparen" }, { 0x3231, "ideographicstockparen" }, { 0x323b, "ideographicstudyparen" }, { 0x3230, "ideographicsunparen" }, { 0x323c, "ideographicsuperviseparen" }, { 0x322c, "ideographicwaterparen" }, { 0x322d, "ideographicwoodparen" }, { 0x3007, "ideographiczero" }, { 0x328e, "ideographmetalcircle" }, { 0x328a, "ideographmooncircle" }, { 0x3294, "ideographnamecircle" }, { 0x3290, "ideographsuncircle" }, { 0x328c, "ideographwatercircle" }, { 0x328d, "ideographwoodcircle" }, { 0x0907, "ideva" }, { 0x00ef, "idieresis" }, { 0x1e2f, "idieresisacute" }, { 0x04e5, "idieresiscyrillic" }, { 0x1ecb, "idotbelow" }, { 0x04d7, "iebrevecyrillic" }, { 0x0435, "iecyrillic" }, { 0x3275, "ieungacirclekorean" }, { 0x3215, "ieungaparenkorean" }, { 0x3267, "ieungcirclekorean" }, { 0x3147, "ieungkorean" }, { 0x3207, "ieungparenkorean" }, { 0x00ec, "igrave" }, { 0x0a87, "igujarati" }, { 0x0a07, "igurmukhi" }, { 0x3044, "ihiragana" }, { 0x1ec9, "ihookabove" }, { 0x0988, "iibengali" }, { 0x0438, "iicyrillic" }, { 0x0908, "iideva" }, { 0x0a88, "iigujarati" }, { 0x0a08, "iigurmukhi" }, { 0x0a40, "iimatragurmukhi" }, { 0x020b, "iinvertedbreve" }, { 0x0439, "iishortcyrillic" }, { 0x09c0, "iivowelsignbengali" }, { 0x0940, "iivowelsigndeva" }, { 0x0ac0, "iivowelsigngujarati" }, { 0x0133, "ij" }, { 0x30a4, "ikatakana" }, { 0xff72, "ikatakanahalfwidth" }, { 0x3163, "ikorean" }, { 0x02dc, "ilde" }, { 0x05ac, "iluyhebrew" }, { 0x012b, "imacron" }, { 0x04e3, "imacroncyrillic" }, { 0x2253, "imageorapproximatelyequal" }, { 0x0a3f, "imatragurmukhi" }, { 0xff49, "imonospace" }, { 0x2206, "increment" }, { 0x221e, "infinity" }, { 0x056b, "iniarmenian" }, { 0x222b, "integral" }, { 0x2321, "integralbottom" }, { 0x2321, "integralbt" }, { 0xf8f5, "integralex" }, { 0x2320, "integraltop" }, { 0x2320, "integraltp" }, { 0x2229, "intersection" }, { 0x3305, "intisquare" }, { 0x25d8, "invbullet" }, { 0x25d9, "invcircle" }, { 0x263b, "invsmileface" }, { 0x0451, "iocyrillic" }, { 0x012f, "iogonek" }, { 0x03b9, "iota" }, { 0x03ca, "iotadieresis" }, { 0x0390, "iotadieresistonos" }, { 0x0269, "iotalatin" }, { 0x03af, "iotatonos" }, { 0x24a4, "iparen" }, { 0x0a72, "irigurmukhi" }, { 0x3043, "ismallhiragana" }, { 0x30a3, "ismallkatakana" }, { 0xff68, "ismallkatakanahalfwidth" }, { 0x09fa, "issharbengali" }, { 0x0268, "istroke" }, { 0xf6ed, "isuperior" }, { 0x309d, "iterationhiragana" }, { 0x30fd, "iterationkatakana" }, { 0x0129, "itilde" }, { 0x1e2d, "itildebelow" }, { 0x3129, "iubopomofo" }, { 0x044e, "iucyrillic" }, { 0x09bf, "ivowelsignbengali" }, { 0x093f, "ivowelsigndeva" }, { 0x0abf, "ivowelsigngujarati" }, { 0x0475, "izhitsacyrillic" }, { 0x0477, "izhitsadblgravecyrillic" }, { 0x006a, "j" }, { 0x0571, "jaarmenian" }, { 0x099c, "jabengali" }, { 0x091c, "jadeva" }, { 0x0a9c, "jagujarati" }, { 0x0a1c, "jagurmukhi" }, { 0x3110, "jbopomofo" }, { 0x01f0, "jcaron" }, { 0x24d9, "jcircle" }, { 0x0135, "jcircumflex" }, { 0x029d, "jcrossedtail" }, { 0x025f, "jdotlessstroke" }, { 0x0458, "jecyrillic" }, { 0x062c, "jeemarabic" }, { 0xfe9e, "jeemfinalarabic" }, { 0xfe9f, "jeeminitialarabic" }, { 0xfea0, "jeemmedialarabic" }, { 0x0698, "jeharabic" }, { 0xfb8b, "jehfinalarabic" }, { 0x099d, "jhabengali" }, { 0x091d, "jhadeva" }, { 0x0a9d, "jhagujarati" }, { 0x0a1d, "jhagurmukhi" }, { 0x057b, "jheharmenian" }, { 0x3004, "jis" }, { 0xff4a, "jmonospace" }, { 0x24a5, "jparen" }, { 0x02b2, "jsuperior" }, { 0x006b, "k" }, { 0x04a1, "kabashkircyrillic" }, { 0x0995, "kabengali" }, { 0x1e31, "kacute" }, { 0x043a, "kacyrillic" }, { 0x049b, "kadescendercyrillic" }, { 0x0915, "kadeva" }, { 0x05db, "kaf" }, { 0x0643, "kafarabic" }, { 0xfb3b, "kafdagesh" }, { 0xfb3b, "kafdageshhebrew" }, { 0xfeda, "kaffinalarabic" }, { 0x05db, "kafhebrew" }, { 0xfedb, "kafinitialarabic" }, { 0xfedc, "kafmedialarabic" }, { 0xfb4d, "kafrafehebrew" }, { 0x0a95, "kagujarati" }, { 0x0a15, "kagurmukhi" }, { 0x304b, "kahiragana" }, { 0x04c4, "kahookcyrillic" }, { 0x30ab, "kakatakana" }, { 0xff76, "kakatakanahalfwidth" }, { 0x03ba, "kappa" }, { 0x03f0, "kappasymbolgreek" }, { 0x3171, "kapyeounmieumkorean" }, { 0x3184, "kapyeounphieuphkorean" }, { 0x3178, "kapyeounpieupkorean" }, { 0x3179, "kapyeounssangpieupkorean" }, { 0x330d, "karoriisquare" }, { 0x0640, "kashidaautoarabic" }, { 0x0640, "kashidaautonosidebearingarabic" }, { 0x30f5, "kasmallkatakana" }, { 0x3384, "kasquare" }, { 0x0650, "kasraarabic" }, { 0x064d, "kasratanarabic" }, { 0x049f, "kastrokecyrillic" }, { 0xff70, "katahiraprolongmarkhalfwidth" }, { 0x049d, "kaverticalstrokecyrillic" }, { 0x310e, "kbopomofo" }, { 0x3389, "kcalsquare" }, { 0x01e9, "kcaron" }, { 0x0137, "kcedilla" }, { 0x24da, "kcircle" }, { 0x0137, "kcommaaccent" }, { 0x1e33, "kdotbelow" }, { 0x0584, "keharmenian" }, { 0x3051, "kehiragana" }, { 0x30b1, "kekatakana" }, { 0xff79, "kekatakanahalfwidth" }, { 0x056f, "kenarmenian" }, { 0x30f6, "kesmallkatakana" }, { 0x0138, "kgreenlandic" }, { 0x0996, "khabengali" }, { 0x0445, "khacyrillic" }, { 0x0916, "khadeva" }, { 0x0a96, "khagujarati" }, { 0x0a16, "khagurmukhi" }, { 0x062e, "khaharabic" }, { 0xfea6, "khahfinalarabic" }, { 0xfea7, "khahinitialarabic" }, { 0xfea8, "khahmedialarabic" }, { 0x03e7, "kheicoptic" }, { 0x0959, "khhadeva" }, { 0x0a59, "khhagurmukhi" }, { 0x3278, "khieukhacirclekorean" }, { 0x3218, "khieukhaparenkorean" }, { 0x326a, "khieukhcirclekorean" }, { 0x314b, "khieukhkorean" }, { 0x320a, "khieukhparenkorean" }, { 0x0e02, "khokhaithai" }, { 0x0e05, "khokhonthai" }, { 0x0e03, "khokhuatthai" }, { 0x0e04, "khokhwaithai" }, { 0x0e5b, "khomutthai" }, { 0x0199, "khook" }, { 0x0e06, "khorakhangthai" }, { 0x3391, "khzsquare" }, { 0x304d, "kihiragana" }, { 0x30ad, "kikatakana" }, { 0xff77, "kikatakanahalfwidth" }, { 0x3315, "kiroguramusquare" }, { 0x3316, "kiromeetorusquare" }, { 0x3314, "kirosquare" }, { 0x326e, "kiyeokacirclekorean" }, { 0x320e, "kiyeokaparenkorean" }, { 0x3260, "kiyeokcirclekorean" }, { 0x3131, "kiyeokkorean" }, { 0x3200, "kiyeokparenkorean" }, { 0x3133, "kiyeoksioskorean" }, { 0x045c, "kjecyrillic" }, { 0x1e35, "klinebelow" }, { 0x3398, "klsquare" }, { 0x33a6, "kmcubedsquare" }, { 0xff4b, "kmonospace" }, { 0x33a2, "kmsquaredsquare" }, { 0x3053, "kohiragana" }, { 0x33c0, "kohmsquare" }, { 0x0e01, "kokaithai" }, { 0x30b3, "kokatakana" }, { 0xff7a, "kokatakanahalfwidth" }, { 0x331e, "kooposquare" }, { 0x0481, "koppacyrillic" }, { 0x327f, "koreanstandardsymbol" }, { 0x0343, "koroniscmb" }, { 0x24a6, "kparen" }, { 0x33aa, "kpasquare" }, { 0x046f, "ksicyrillic" }, { 0x33cf, "ktsquare" }, { 0x029e, "kturned" }, { 0x304f, "kuhiragana" }, { 0x30af, "kukatakana" }, { 0xff78, "kukatakanahalfwidth" }, { 0x33b8, "kvsquare" }, { 0x33be, "kwsquare" }, { 0x006c, "l" }, { 0x09b2, "labengali" }, { 0x013a, "lacute" }, { 0x0932, "ladeva" }, { 0x0ab2, "lagujarati" }, { 0x0a32, "lagurmukhi" }, { 0x0e45, "lakkhangyaothai" }, { 0xfefc, "lamaleffinalarabic" }, { 0xfef8, "lamalefhamzaabovefinalarabic" }, { 0xfef7, "lamalefhamzaaboveisolatedarabic" }, { 0xfefa, "lamalefhamzabelowfinalarabic" }, { 0xfef9, "lamalefhamzabelowisolatedarabic" }, { 0xfefb, "lamalefisolatedarabic" }, { 0xfef6, "lamalefmaddaabovefinalarabic" }, { 0xfef5, "lamalefmaddaaboveisolatedarabic" }, { 0x0644, "lamarabic" }, { 0x03bb, "lambda" }, { 0x019b, "lambdastroke" }, { 0x05dc, "lamed" }, { 0xfb3c, "lameddagesh" }, { 0xfb3c, "lameddageshhebrew" }, { 0x05dc, "lamedhebrew" }, { 0xfede, "lamfinalarabic" }, { 0xfcca, "lamhahinitialarabic" }, { 0xfedf, "laminitialarabic" }, { 0xfcc9, "lamjeeminitialarabic" }, { 0xfccb, "lamkhahinitialarabic" }, { 0xfdf2, "lamlamhehisolatedarabic" }, { 0xfee0, "lammedialarabic" }, { 0xfd88, "lammeemhahinitialarabic" }, { 0xfccc, "lammeeminitialarabic" }, { 0x25ef, "largecircle" }, { 0x019a, "lbar" }, { 0x026c, "lbelt" }, { 0x310c, "lbopomofo" }, { 0x013e, "lcaron" }, { 0x013c, "lcedilla" }, { 0x24db, "lcircle" }, { 0x1e3d, "lcircumflexbelow" }, { 0x013c, "lcommaaccent" }, { 0x0140, "ldot" }, { 0x0140, "ldotaccent" }, { 0x1e37, "ldotbelow" }, { 0x1e39, "ldotbelowmacron" }, { 0x031a, "leftangleabovecmb" }, { 0x0318, "lefttackbelowcmb" }, { 0x003c, "less" }, { 0x2264, "lessequal" }, { 0x22da, "lessequalorgreater" }, { 0xff1c, "lessmonospace" }, { 0x2a7d, "lessorequalslant" }, { 0x2272, "lessorequivalent" }, { 0x2276, "lessorgreater" }, { 0x2266, "lessoverequal" }, { 0xfe64, "lesssmall" }, { 0x026e, "lezh" }, { 0x258c, "lfblock" }, { 0x026d, "lhookretroflex" }, { 0x20a4, "lira" }, { 0x056c, "liwnarmenian" }, { 0x01c9, "lj" }, { 0x0459, "ljecyrillic" }, { 0xf6c0, "ll" }, { 0x0933, "lladeva" }, { 0x0ab3, "llagujarati" }, { 0x1e3b, "llinebelow" }, { 0x0934, "llladeva" }, { 0x09e1, "llvocalicbengali" }, { 0x0961, "llvocalicdeva" }, { 0x09e3, "llvocalicvowelsignbengali" }, { 0x0963, "llvocalicvowelsigndeva" }, { 0x026b, "lmiddletilde" }, { 0xff4c, "lmonospace" }, { 0x33d0, "lmsquare" }, { 0x0e2c, "lochulathai" }, { 0x2227, "logicaland" }, { 0x00ac, "logicalnot" }, { 0x2310, "logicalnotreversed" }, { 0x2228, "logicalor" }, { 0x0e25, "lolingthai" }, { 0x017f, "longs" }, { 0xfe4e, "lowlinecenterline" }, { 0x0332, "lowlinecmb" }, { 0xfe4d, "lowlinedashed" }, { 0x25ca, "lozenge" }, { 0x24a7, "lparen" }, { 0x0142, "lslash" }, { 0x2113, "lsquare" }, { 0xf6ee, "lsuperior" }, { 0x2591, "ltshade" }, { 0x0e26, "luthai" }, { 0x098c, "lvocalicbengali" }, { 0x090c, "lvocalicdeva" }, { 0x09e2, "lvocalicvowelsignbengali" }, { 0x0962, "lvocalicvowelsigndeva" }, { 0x33d3, "lxsquare" }, { 0x006d, "m" }, { 0x09ae, "mabengali" }, { 0x00af, "macron" }, { 0x0331, "macronbelowcmb" }, { 0x0304, "macroncmb" }, { 0x02cd, "macronlowmod" }, { 0xffe3, "macronmonospace" }, { 0x1e3f, "macute" }, { 0x092e, "madeva" }, { 0x0aae, "magujarati" }, { 0x0a2e, "magurmukhi" }, { 0x05a4, "mahapakhhebrew" }, { 0x05a4, "mahapakhlefthebrew" }, { 0x307e, "mahiragana" }, { 0xf895, "maichattawalowleftthai" }, { 0xf894, "maichattawalowrightthai" }, { 0x0e4b, "maichattawathai" }, { 0xf893, "maichattawaupperleftthai" }, { 0xf88c, "maieklowleftthai" }, { 0xf88b, "maieklowrightthai" }, { 0x0e48, "maiekthai" }, { 0xf88a, "maiekupperleftthai" }, { 0xf884, "maihanakatleftthai" }, { 0x0e31, "maihanakatthai" }, { 0xf889, "maitaikhuleftthai" }, { 0x0e47, "maitaikhuthai" }, { 0xf88f, "maitholowleftthai" }, { 0xf88e, "maitholowrightthai" }, { 0x0e49, "maithothai" }, { 0xf88d, "maithoupperleftthai" }, { 0xf892, "maitrilowleftthai" }, { 0xf891, "maitrilowrightthai" }, { 0x0e4a, "maitrithai" }, { 0xf890, "maitriupperleftthai" }, { 0x0e46, "maiyamokthai" }, { 0x30de, "makatakana" }, { 0xff8f, "makatakanahalfwidth" }, { 0x2642, "male" }, { 0x3347, "mansyonsquare" }, { 0x05be, "maqafhebrew" }, { 0x2642, "mars" }, { 0x05af, "masoracirclehebrew" }, { 0x3383, "masquare" }, { 0x3107, "mbopomofo" }, { 0x33d4, "mbsquare" }, { 0x24dc, "mcircle" }, { 0x33a5, "mcubedsquare" }, { 0x1e41, "mdotaccent" }, { 0x1e43, "mdotbelow" }, { 0x0645, "meemarabic" }, { 0xfee2, "meemfinalarabic" }, { 0xfee3, "meeminitialarabic" }, { 0xfee4, "meemmedialarabic" }, { 0xfcd1, "meemmeeminitialarabic" }, { 0xfc48, "meemmeemisolatedarabic" }, { 0x334d, "meetorusquare" }, { 0x3081, "mehiragana" }, { 0x337e, "meizierasquare" }, { 0x30e1, "mekatakana" }, { 0xff92, "mekatakanahalfwidth" }, { 0x05de, "mem" }, { 0xfb3e, "memdagesh" }, { 0xfb3e, "memdageshhebrew" }, { 0x05de, "memhebrew" }, { 0x0574, "menarmenian" }, { 0x05a5, "merkhahebrew" }, { 0x05a6, "merkhakefulahebrew" }, { 0x05a6, "merkhakefulalefthebrew" }, { 0x05a5, "merkhalefthebrew" }, { 0x0271, "mhook" }, { 0x3392, "mhzsquare" }, { 0xff65, "middledotkatakanahalfwidth" }, { 0x00b7, "middot" }, { 0x3272, "mieumacirclekorean" }, { 0x3212, "mieumaparenkorean" }, { 0x3264, "mieumcirclekorean" }, { 0x3141, "mieumkorean" }, { 0x3170, "mieumpansioskorean" }, { 0x3204, "mieumparenkorean" }, { 0x316e, "mieumpieupkorean" }, { 0x316f, "mieumsioskorean" }, { 0x307f, "mihiragana" }, { 0x30df, "mikatakana" }, { 0xff90, "mikatakanahalfwidth" }, { 0x2212, "minus" }, { 0x0320, "minusbelowcmb" }, { 0x2296, "minuscircle" }, { 0x02d7, "minusmod" }, { 0x2213, "minusplus" }, { 0x2032, "minute" }, { 0x334a, "miribaarusquare" }, { 0x3349, "mirisquare" }, { 0x0270, "mlonglegturned" }, { 0x3396, "mlsquare" }, { 0x33a3, "mmcubedsquare" }, { 0xff4d, "mmonospace" }, { 0x339f, "mmsquaredsquare" }, { 0x3082, "mohiragana" }, { 0x33c1, "mohmsquare" }, { 0x30e2, "mokatakana" }, { 0xff93, "mokatakanahalfwidth" }, { 0x33d6, "molsquare" }, { 0x0e21, "momathai" }, { 0x33a7, "moverssquare" }, { 0x33a8, "moverssquaredsquare" }, { 0x24a8, "mparen" }, { 0x33ab, "mpasquare" }, { 0x33b3, "mssquare" }, { 0xf6ef, "msuperior" }, { 0x026f, "mturned" }, { 0x00b5, "mu" }, { 0x00b5, "mu1" }, { 0x3382, "muasquare" }, { 0x226b, "muchgreater" }, { 0x226a, "muchless" }, { 0x338c, "mufsquare" }, { 0x03bc, "mugreek" }, { 0x338d, "mugsquare" }, { 0x3080, "muhiragana" }, { 0x30e0, "mukatakana" }, { 0xff91, "mukatakanahalfwidth" }, { 0x3395, "mulsquare" }, { 0x00d7, "multiply" }, { 0x339b, "mumsquare" }, { 0x05a3, "munahhebrew" }, { 0x05a3, "munahlefthebrew" }, { 0x266a, "musicalnote" }, { 0x266b, "musicalnotedbl" }, { 0x266d, "musicflatsign" }, { 0x266f, "musicsharpsign" }, { 0x33b2, "mussquare" }, { 0x33b6, "muvsquare" }, { 0x33bc, "muwsquare" }, { 0x33b9, "mvmegasquare" }, { 0x33b7, "mvsquare" }, { 0x33bf, "mwmegasquare" }, { 0x33bd, "mwsquare" }, { 0x006e, "n" }, { 0x09a8, "nabengali" }, { 0x2207, "nabla" }, { 0x0144, "nacute" }, { 0x0928, "nadeva" }, { 0x0aa8, "nagujarati" }, { 0x0a28, "nagurmukhi" }, { 0x306a, "nahiragana" }, { 0x30ca, "nakatakana" }, { 0xff85, "nakatakanahalfwidth" }, { 0x0149, "napostrophe" }, { 0x3381, "nasquare" }, { 0x310b, "nbopomofo" }, { 0x00a0, "nbspace" }, { 0x0148, "ncaron" }, { 0x0146, "ncedilla" }, { 0x24dd, "ncircle" }, { 0x1e4b, "ncircumflexbelow" }, { 0x0146, "ncommaaccent" }, { 0x1e45, "ndotaccent" }, { 0x1e47, "ndotbelow" }, { 0x306d, "nehiragana" }, { 0x30cd, "nekatakana" }, { 0xff88, "nekatakanahalfwidth" }, { 0x20aa, "newsheqelsign" }, { 0x338b, "nfsquare" }, { 0x0999, "ngabengali" }, { 0x0919, "ngadeva" }, { 0x0a99, "ngagujarati" }, { 0x0a19, "ngagurmukhi" }, { 0x0e07, "ngonguthai" }, { 0x3093, "nhiragana" }, { 0x0272, "nhookleft" }, { 0x0273, "nhookretroflex" }, { 0x326f, "nieunacirclekorean" }, { 0x320f, "nieunaparenkorean" }, { 0x3135, "nieuncieuckorean" }, { 0x3261, "nieuncirclekorean" }, { 0x3136, "nieunhieuhkorean" }, { 0x3134, "nieunkorean" }, { 0x3168, "nieunpansioskorean" }, { 0x3201, "nieunparenkorean" }, { 0x3167, "nieunsioskorean" }, { 0x3166, "nieuntikeutkorean" }, { 0x306b, "nihiragana" }, { 0x30cb, "nikatakana" }, { 0xff86, "nikatakanahalfwidth" }, { 0xf899, "nikhahitleftthai" }, { 0x0e4d, "nikhahitthai" }, { 0x0039, "nine" }, { 0x0669, "ninearabic" }, { 0x09ef, "ninebengali" }, { 0x2468, "ninecircle" }, { 0x2792, "ninecircleinversesansserif" }, { 0x096f, "ninedeva" }, { 0x0aef, "ninegujarati" }, { 0x0a6f, "ninegurmukhi" }, { 0x0669, "ninehackarabic" }, { 0x3029, "ninehangzhou" }, { 0x3228, "nineideographicparen" }, { 0x2089, "nineinferior" }, { 0xff19, "ninemonospace" }, { 0xf739, "nineoldstyle" }, { 0x247c, "nineparen" }, { 0x2490, "nineperiod" }, { 0x06f9, "ninepersian" }, { 0x2178, "nineroman" }, { 0x2079, "ninesuperior" }, { 0x2472, "nineteencircle" }, { 0x2486, "nineteenparen" }, { 0x249a, "nineteenperiod" }, { 0x0e59, "ninethai" }, { 0x01cc, "nj" }, { 0x045a, "njecyrillic" }, { 0x30f3, "nkatakana" }, { 0xff9d, "nkatakanahalfwidth" }, { 0x019e, "nlegrightlong" }, { 0x1e49, "nlinebelow" }, { 0xff4e, "nmonospace" }, { 0x339a, "nmsquare" }, { 0x09a3, "nnabengali" }, { 0x0923, "nnadeva" }, { 0x0aa3, "nnagujarati" }, { 0x0a23, "nnagurmukhi" }, { 0x0929, "nnnadeva" }, { 0x306e, "nohiragana" }, { 0x30ce, "nokatakana" }, { 0xff89, "nokatakanahalfwidth" }, { 0x00a0, "nonbreakingspace" }, { 0x0e13, "nonenthai" }, { 0x0e19, "nonuthai" }, { 0x0646, "noonarabic" }, { 0xfee6, "noonfinalarabic" }, { 0x06ba, "noonghunnaarabic" }, { 0xfb9f, "noonghunnafinalarabic" }, { 0xfee7, "nooninitialarabic" }, { 0xfcd2, "noonjeeminitialarabic" }, { 0xfc4b, "noonjeemisolatedarabic" }, { 0xfee8, "noonmedialarabic" }, { 0xfcd5, "noonmeeminitialarabic" }, { 0xfc4e, "noonmeemisolatedarabic" }, { 0xfc8d, "noonnoonfinalarabic" }, { 0x220c, "notcontains" }, { 0x2209, "notelement" }, { 0x2209, "notelementof" }, { 0x2260, "notequal" }, { 0x226f, "notgreater" }, { 0x2271, "notgreaternorequal" }, { 0x2279, "notgreaternorless" }, { 0x2262, "notidentical" }, { 0x226e, "notless" }, { 0x2270, "notlessnorequal" }, { 0x2226, "notparallel" }, { 0x2280, "notprecedes" }, { 0x2284, "notsubset" }, { 0x2281, "notsucceeds" }, { 0x2285, "notsuperset" }, { 0x0576, "nowarmenian" }, { 0x24a9, "nparen" }, { 0x33b1, "nssquare" }, { 0x207f, "nsuperior" }, { 0x00f1, "ntilde" }, { 0x03bd, "nu" }, { 0x306c, "nuhiragana" }, { 0x30cc, "nukatakana" }, { 0xff87, "nukatakanahalfwidth" }, { 0x09bc, "nuktabengali" }, { 0x093c, "nuktadeva" }, { 0x0abc, "nuktagujarati" }, { 0x0a3c, "nuktagurmukhi" }, { 0x0023, "numbersign" }, { 0xff03, "numbersignmonospace" }, { 0xfe5f, "numbersignsmall" }, { 0x0374, "numeralsigngreek" }, { 0x0375, "numeralsignlowergreek" }, { 0x2116, "numero" }, { 0x05e0, "nun" }, { 0xfb40, "nundagesh" }, { 0xfb40, "nundageshhebrew" }, { 0x05e0, "nunhebrew" }, { 0x33b5, "nvsquare" }, { 0x33bb, "nwsquare" }, { 0x099e, "nyabengali" }, { 0x091e, "nyadeva" }, { 0x0a9e, "nyagujarati" }, { 0x0a1e, "nyagurmukhi" }, { 0x006f, "o" }, { 0x00f3, "oacute" }, { 0x0e2d, "oangthai" }, { 0x0275, "obarred" }, { 0x04e9, "obarredcyrillic" }, { 0x04eb, "obarreddieresiscyrillic" }, { 0x0993, "obengali" }, { 0x311b, "obopomofo" }, { 0x014f, "obreve" }, { 0x0911, "ocandradeva" }, { 0x0a91, "ocandragujarati" }, { 0x0949, "ocandravowelsigndeva" }, { 0x0ac9, "ocandravowelsigngujarati" }, { 0x01d2, "ocaron" }, { 0x24de, "ocircle" }, { 0x00f4, "ocircumflex" }, { 0x1ed1, "ocircumflexacute" }, { 0x1ed9, "ocircumflexdotbelow" }, { 0x1ed3, "ocircumflexgrave" }, { 0x1ed5, "ocircumflexhookabove" }, { 0x1ed7, "ocircumflextilde" }, { 0x043e, "ocyrillic" }, { 0x0151, "odblacute" }, { 0x020d, "odblgrave" }, { 0x0913, "odeva" }, { 0x00f6, "odieresis" }, { 0x04e7, "odieresiscyrillic" }, { 0x1ecd, "odotbelow" }, { 0x0153, "oe" }, { 0x315a, "oekorean" }, { 0x02db, "ogonek" }, { 0x0328, "ogonekcmb" }, { 0x00f2, "ograve" }, { 0x0a93, "ogujarati" }, { 0x0585, "oharmenian" }, { 0x304a, "ohiragana" }, { 0x1ecf, "ohookabove" }, { 0x01a1, "ohorn" }, { 0x1edb, "ohornacute" }, { 0x1ee3, "ohorndotbelow" }, { 0x1edd, "ohorngrave" }, { 0x1edf, "ohornhookabove" }, { 0x1ee1, "ohorntilde" }, { 0x0151, "ohungarumlaut" }, { 0x01a3, "oi" }, { 0x020f, "oinvertedbreve" }, { 0x30aa, "okatakana" }, { 0xff75, "okatakanahalfwidth" }, { 0x3157, "okorean" }, { 0x05ab, "olehebrew" }, { 0x014d, "omacron" }, { 0x1e53, "omacronacute" }, { 0x1e51, "omacrongrave" }, { 0x0950, "omdeva" }, { 0x03c9, "omega" }, { 0x03d6, "omega1" }, { 0x0461, "omegacyrillic" }, { 0x0277, "omegalatinclosed" }, { 0x047b, "omegaroundcyrillic" }, { 0x047d, "omegatitlocyrillic" }, { 0x03ce, "omegatonos" }, { 0x0ad0, "omgujarati" }, { 0x03bf, "omicron" }, { 0x03cc, "omicrontonos" }, { 0xff4f, "omonospace" }, { 0x0031, "one" }, { 0x0661, "onearabic" }, { 0x09e7, "onebengali" }, { 0x2460, "onecircle" }, { 0x278a, "onecircleinversesansserif" }, { 0x0967, "onedeva" }, { 0x2024, "onedotenleader" }, { 0x215b, "oneeighth" }, { 0xf6dc, "onefitted" }, { 0x0ae7, "onegujarati" }, { 0x0a67, "onegurmukhi" }, { 0x0661, "onehackarabic" }, { 0x00bd, "onehalf" }, { 0x3021, "onehangzhou" }, { 0x3220, "oneideographicparen" }, { 0x2081, "oneinferior" }, { 0xff11, "onemonospace" }, { 0x09f4, "onenumeratorbengali" }, { 0xf731, "oneoldstyle" }, { 0x2474, "oneparen" }, { 0x2488, "oneperiod" }, { 0x06f1, "onepersian" }, { 0x00bc, "onequarter" }, { 0x2170, "oneroman" }, { 0x00b9, "onesuperior" }, { 0x0e51, "onethai" }, { 0x2153, "onethird" }, { 0x01eb, "oogonek" }, { 0x01ed, "oogonekmacron" }, { 0x0a13, "oogurmukhi" }, { 0x0a4b, "oomatragurmukhi" }, { 0x0254, "oopen" }, { 0x24aa, "oparen" }, { 0x25e6, "openbullet" }, { 0x2325, "option" }, { 0x00aa, "ordfeminine" }, { 0x00ba, "ordmasculine" }, { 0x221f, "orthogonal" }, { 0x0912, "oshortdeva" }, { 0x094a, "oshortvowelsigndeva" }, { 0x00f8, "oslash" }, { 0x01ff, "oslashacute" }, { 0x3049, "osmallhiragana" }, { 0x30a9, "osmallkatakana" }, { 0xff6b, "osmallkatakanahalfwidth" }, { 0x01ff, "ostrokeacute" }, { 0xf6f0, "osuperior" }, { 0x047f, "otcyrillic" }, { 0x00f5, "otilde" }, { 0x1e4d, "otildeacute" }, { 0x1e4f, "otildedieresis" }, { 0x3121, "oubopomofo" }, { 0x203e, "overline" }, { 0xfe4a, "overlinecenterline" }, { 0x0305, "overlinecmb" }, { 0xfe49, "overlinedashed" }, { 0xfe4c, "overlinedblwavy" }, { 0xfe4b, "overlinewavy" }, { 0x00af, "overscore" }, { 0x09cb, "ovowelsignbengali" }, { 0x094b, "ovowelsigndeva" }, { 0x0acb, "ovowelsigngujarati" }, { 0x0070, "p" }, { 0x3380, "paampssquare" }, { 0x332b, "paasentosquare" }, { 0x09aa, "pabengali" }, { 0x1e55, "pacute" }, { 0x092a, "padeva" }, { 0x21df, "pagedown" }, { 0x21de, "pageup" }, { 0x0aaa, "pagujarati" }, { 0x0a2a, "pagurmukhi" }, { 0x3071, "pahiragana" }, { 0x0e2f, "paiyannoithai" }, { 0x30d1, "pakatakana" }, { 0x0484, "palatalizationcyrilliccmb" }, { 0x04c0, "palochkacyrillic" }, { 0x317f, "pansioskorean" }, { 0x00b6, "paragraph" }, { 0x2225, "parallel" }, { 0x0028, "parenleft" }, { 0xfd3e, "parenleftaltonearabic" }, { 0xf8ed, "parenleftbt" }, { 0xf8ec, "parenleftex" }, { 0x208d, "parenleftinferior" }, { 0xff08, "parenleftmonospace" }, { 0xfe59, "parenleftsmall" }, { 0x207d, "parenleftsuperior" }, { 0xf8eb, "parenlefttp" }, { 0xfe35, "parenleftvertical" }, { 0x0029, "parenright" }, { 0xfd3f, "parenrightaltonearabic" }, { 0xf8f8, "parenrightbt" }, { 0xf8f7, "parenrightex" }, { 0x208e, "parenrightinferior" }, { 0xff09, "parenrightmonospace" }, { 0xfe5a, "parenrightsmall" }, { 0x207e, "parenrightsuperior" }, { 0xf8f6, "parenrighttp" }, { 0xfe36, "parenrightvertical" }, { 0x2202, "partialdiff" }, { 0x05c0, "paseqhebrew" }, { 0x0599, "pashtahebrew" }, { 0x33a9, "pasquare" }, { 0x05b7, "patah" }, { 0x05b7, "patah11" }, { 0x05b7, "patah1d" }, { 0x05b7, "patah2a" }, { 0x05b7, "patahhebrew" }, { 0x05b7, "patahnarrowhebrew" }, { 0x05b7, "patahquarterhebrew" }, { 0x05b7, "patahwidehebrew" }, { 0x05a1, "pazerhebrew" }, { 0x3106, "pbopomofo" }, { 0x24df, "pcircle" }, { 0x1e57, "pdotaccent" }, { 0x05e4, "pe" }, { 0x043f, "pecyrillic" }, { 0xfb44, "pedagesh" }, { 0xfb44, "pedageshhebrew" }, { 0x333b, "peezisquare" }, { 0xfb43, "pefinaldageshhebrew" }, { 0x067e, "peharabic" }, { 0x057a, "peharmenian" }, { 0x05e4, "pehebrew" }, { 0xfb57, "pehfinalarabic" }, { 0xfb58, "pehinitialarabic" }, { 0x307a, "pehiragana" }, { 0xfb59, "pehmedialarabic" }, { 0x30da, "pekatakana" }, { 0x04a7, "pemiddlehookcyrillic" }, { 0xfb4e, "perafehebrew" }, { 0x0025, "percent" }, { 0x066a, "percentarabic" }, { 0xff05, "percentmonospace" }, { 0xfe6a, "percentsmall" }, { 0x002e, "period" }, { 0x0589, "periodarmenian" }, { 0x00b7, "periodcentered" }, { 0xff61, "periodhalfwidth" }, { 0xf6e7, "periodinferior" }, { 0xff0e, "periodmonospace" }, { 0xfe52, "periodsmall" }, { 0xf6e8, "periodsuperior" }, { 0x0342, "perispomenigreekcmb" }, { 0x22a5, "perpendicular" }, { 0x2030, "perthousand" }, { 0x20a7, "peseta" }, { 0x338a, "pfsquare" }, { 0x09ab, "phabengali" }, { 0x092b, "phadeva" }, { 0x0aab, "phagujarati" }, { 0x0a2b, "phagurmukhi" }, { 0x03c6, "phi" }, { 0x03d5, "phi1" }, { 0x327a, "phieuphacirclekorean" }, { 0x321a, "phieuphaparenkorean" }, { 0x326c, "phieuphcirclekorean" }, { 0x314d, "phieuphkorean" }, { 0x320c, "phieuphparenkorean" }, { 0x0278, "philatin" }, { 0x0e3a, "phinthuthai" }, { 0x03d5, "phisymbolgreek" }, { 0x01a5, "phook" }, { 0x0e1e, "phophanthai" }, { 0x0e1c, "phophungthai" }, { 0x0e20, "phosamphaothai" }, { 0x03c0, "pi" }, { 0x3273, "pieupacirclekorean" }, { 0x3213, "pieupaparenkorean" }, { 0x3176, "pieupcieuckorean" }, { 0x3265, "pieupcirclekorean" }, { 0x3172, "pieupkiyeokkorean" }, { 0x3142, "pieupkorean" }, { 0x3205, "pieupparenkorean" }, { 0x3174, "pieupsioskiyeokkorean" }, { 0x3144, "pieupsioskorean" }, { 0x3175, "pieupsiostikeutkorean" }, { 0x3177, "pieupthieuthkorean" }, { 0x3173, "pieuptikeutkorean" }, { 0x3074, "pihiragana" }, { 0x30d4, "pikatakana" }, { 0x03d6, "pisymbolgreek" }, { 0x0583, "piwrarmenian" }, { 0x002b, "plus" }, { 0x031f, "plusbelowcmb" }, { 0x2295, "pluscircle" }, { 0x00b1, "plusminus" }, { 0x02d6, "plusmod" }, { 0xff0b, "plusmonospace" }, { 0xfe62, "plussmall" }, { 0x207a, "plussuperior" }, { 0xff50, "pmonospace" }, { 0x33d8, "pmsquare" }, { 0x307d, "pohiragana" }, { 0x261f, "pointingindexdownwhite" }, { 0x261c, "pointingindexleftwhite" }, { 0x261e, "pointingindexrightwhite" }, { 0x261d, "pointingindexupwhite" }, { 0x30dd, "pokatakana" }, { 0x0e1b, "poplathai" }, { 0x3012, "postalmark" }, { 0x3020, "postalmarkface" }, { 0x24ab, "pparen" }, { 0x227a, "precedes" }, { 0x211e, "prescription" }, { 0x02b9, "primemod" }, { 0x2035, "primereversed" }, { 0x220f, "product" }, { 0x2305, "projective" }, { 0x30fc, "prolongedkana" }, { 0x2318, "propellor" }, { 0x2282, "propersubset" }, { 0x2283, "propersuperset" }, { 0x2237, "proportion" }, { 0x221d, "proportional" }, { 0x03c8, "psi" }, { 0x0471, "psicyrillic" }, { 0x0486, "psilipneumatacyrilliccmb" }, { 0x33b0, "pssquare" }, { 0x3077, "puhiragana" }, { 0x30d7, "pukatakana" }, { 0x33b4, "pvsquare" }, { 0x33ba, "pwsquare" }, { 0x0071, "q" }, { 0x0958, "qadeva" }, { 0x05a8, "qadmahebrew" }, { 0x0642, "qafarabic" }, { 0xfed6, "qaffinalarabic" }, { 0xfed7, "qafinitialarabic" }, { 0xfed8, "qafmedialarabic" }, { 0x05b8, "qamats" }, { 0x05b8, "qamats10" }, { 0x05b8, "qamats1a" }, { 0x05b8, "qamats1c" }, { 0x05b8, "qamats27" }, { 0x05b8, "qamats29" }, { 0x05b8, "qamats33" }, { 0x05b8, "qamatsde" }, { 0x05b8, "qamatshebrew" }, { 0x05b8, "qamatsnarrowhebrew" }, { 0x05b8, "qamatsqatanhebrew" }, { 0x05b8, "qamatsqatannarrowhebrew" }, { 0x05b8, "qamatsqatanquarterhebrew" }, { 0x05b8, "qamatsqatanwidehebrew" }, { 0x05b8, "qamatsquarterhebrew" }, { 0x05b8, "qamatswidehebrew" }, { 0x059f, "qarneyparahebrew" }, { 0x3111, "qbopomofo" }, { 0x24e0, "qcircle" }, { 0x02a0, "qhook" }, { 0xff51, "qmonospace" }, { 0x05e7, "qof" }, { 0xfb47, "qofdagesh" }, { 0xfb47, "qofdageshhebrew" }, { 0x05e7, "qofhebrew" }, { 0x24ac, "qparen" }, { 0x2669, "quarternote" }, { 0x05bb, "qubuts" }, { 0x05bb, "qubuts18" }, { 0x05bb, "qubuts25" }, { 0x05bb, "qubuts31" }, { 0x05bb, "qubutshebrew" }, { 0x05bb, "qubutsnarrowhebrew" }, { 0x05bb, "qubutsquarterhebrew" }, { 0x05bb, "qubutswidehebrew" }, { 0x003f, "question" }, { 0x061f, "questionarabic" }, { 0x055e, "questionarmenian" }, { 0x00bf, "questiondown" }, { 0xf7bf, "questiondownsmall" }, { 0x037e, "questiongreek" }, { 0xff1f, "questionmonospace" }, { 0xf73f, "questionsmall" }, { 0x0022, "quotedbl" }, { 0x201e, "quotedblbase" }, { 0x201c, "quotedblleft" }, { 0xff02, "quotedblmonospace" }, { 0x301e, "quotedblprime" }, { 0x301d, "quotedblprimereversed" }, { 0x201d, "quotedblright" }, { 0x2018, "quoteleft" }, { 0x201b, "quoteleftreversed" }, { 0x201b, "quotereversed" }, { 0x2019, "quoteright" }, { 0x0149, "quoterightn" }, { 0x201a, "quotesinglbase" }, { 0x0027, "quotesingle" }, { 0xff07, "quotesinglemonospace" }, { 0x0072, "r" }, { 0x057c, "raarmenian" }, { 0x09b0, "rabengali" }, { 0x0155, "racute" }, { 0x0930, "radeva" }, { 0x221a, "radical" }, { 0xf8e5, "radicalex" }, { 0x33ae, "radoverssquare" }, { 0x33af, "radoverssquaredsquare" }, { 0x33ad, "radsquare" }, { 0x05bf, "rafe" }, { 0x05bf, "rafehebrew" }, { 0x0ab0, "ragujarati" }, { 0x0a30, "ragurmukhi" }, { 0x3089, "rahiragana" }, { 0x30e9, "rakatakana" }, { 0xff97, "rakatakanahalfwidth" }, { 0x09f1, "ralowerdiagonalbengali" }, { 0x09f0, "ramiddlediagonalbengali" }, { 0x0264, "ramshorn" }, { 0x2236, "ratio" }, { 0x3116, "rbopomofo" }, { 0x0159, "rcaron" }, { 0x0157, "rcedilla" }, { 0x24e1, "rcircle" }, { 0x0157, "rcommaaccent" }, { 0x0211, "rdblgrave" }, { 0x1e59, "rdotaccent" }, { 0x1e5b, "rdotbelow" }, { 0x1e5d, "rdotbelowmacron" }, { 0x203b, "referencemark" }, { 0x2286, "reflexsubset" }, { 0x2287, "reflexsuperset" }, { 0x00ae, "registered" }, { 0xf8e8, "registersans" }, { 0xf6da, "registerserif" }, { 0x0631, "reharabic" }, { 0x0580, "reharmenian" }, { 0xfeae, "rehfinalarabic" }, { 0x308c, "rehiragana" }, { 0x30ec, "rekatakana" }, { 0xff9a, "rekatakanahalfwidth" }, { 0x05e8, "resh" }, { 0xfb48, "reshdageshhebrew" }, { 0x05e8, "reshhebrew" }, { 0x223d, "reversedtilde" }, { 0x0597, "reviahebrew" }, { 0x0597, "reviamugrashhebrew" }, { 0x2310, "revlogicalnot" }, { 0x027e, "rfishhook" }, { 0x027f, "rfishhookreversed" }, { 0x09dd, "rhabengali" }, { 0x095d, "rhadeva" }, { 0x03c1, "rho" }, { 0x027d, "rhook" }, { 0x027b, "rhookturned" }, { 0x02b5, "rhookturnedsuperior" }, { 0x03f1, "rhosymbolgreek" }, { 0x02de, "rhotichookmod" }, { 0x3271, "rieulacirclekorean" }, { 0x3211, "rieulaparenkorean" }, { 0x3263, "rieulcirclekorean" }, { 0x3140, "rieulhieuhkorean" }, { 0x313a, "rieulkiyeokkorean" }, { 0x3169, "rieulkiyeoksioskorean" }, { 0x3139, "rieulkorean" }, { 0x313b, "rieulmieumkorean" }, { 0x316c, "rieulpansioskorean" }, { 0x3203, "rieulparenkorean" }, { 0x313f, "rieulphieuphkorean" }, { 0x313c, "rieulpieupkorean" }, { 0x316b, "rieulpieupsioskorean" }, { 0x313d, "rieulsioskorean" }, { 0x313e, "rieulthieuthkorean" }, { 0x316a, "rieultikeutkorean" }, { 0x316d, "rieulyeorinhieuhkorean" }, { 0x221f, "rightangle" }, { 0x0319, "righttackbelowcmb" }, { 0x22bf, "righttriangle" }, { 0x308a, "rihiragana" }, { 0x30ea, "rikatakana" }, { 0xff98, "rikatakanahalfwidth" }, { 0x02da, "ring" }, { 0x0325, "ringbelowcmb" }, { 0x030a, "ringcmb" }, { 0x02bf, "ringhalfleft" }, { 0x0559, "ringhalfleftarmenian" }, { 0x031c, "ringhalfleftbelowcmb" }, { 0x02d3, "ringhalfleftcentered" }, { 0x02be, "ringhalfright" }, { 0x0339, "ringhalfrightbelowcmb" }, { 0x02d2, "ringhalfrightcentered" }, { 0x0213, "rinvertedbreve" }, { 0x3351, "rittorusquare" }, { 0x1e5f, "rlinebelow" }, { 0x027c, "rlongleg" }, { 0x027a, "rlonglegturned" }, { 0xff52, "rmonospace" }, { 0x308d, "rohiragana" }, { 0x30ed, "rokatakana" }, { 0xff9b, "rokatakanahalfwidth" }, { 0x0e23, "roruathai" }, { 0x24ad, "rparen" }, { 0x09dc, "rrabengali" }, { 0x0931, "rradeva" }, { 0x0a5c, "rragurmukhi" }, { 0x0691, "rreharabic" }, { 0xfb8d, "rrehfinalarabic" }, { 0x09e0, "rrvocalicbengali" }, { 0x0960, "rrvocalicdeva" }, { 0x0ae0, "rrvocalicgujarati" }, { 0x09c4, "rrvocalicvowelsignbengali" }, { 0x0944, "rrvocalicvowelsigndeva" }, { 0x0ac4, "rrvocalicvowelsigngujarati" }, { 0xf6f1, "rsuperior" }, { 0x2590, "rtblock" }, { 0x0279, "rturned" }, { 0x02b4, "rturnedsuperior" }, { 0x308b, "ruhiragana" }, { 0x30eb, "rukatakana" }, { 0xff99, "rukatakanahalfwidth" }, { 0x09f2, "rupeemarkbengali" }, { 0x09f3, "rupeesignbengali" }, { 0xf6dd, "rupiah" }, { 0x0e24, "ruthai" }, { 0x098b, "rvocalicbengali" }, { 0x090b, "rvocalicdeva" }, { 0x0a8b, "rvocalicgujarati" }, { 0x09c3, "rvocalicvowelsignbengali" }, { 0x0943, "rvocalicvowelsigndeva" }, { 0x0ac3, "rvocalicvowelsigngujarati" }, { 0x0073, "s" }, { 0x09b8, "sabengali" }, { 0x015b, "sacute" }, { 0x1e65, "sacutedotaccent" }, { 0x0635, "sadarabic" }, { 0x0938, "sadeva" }, { 0xfeba, "sadfinalarabic" }, { 0xfebb, "sadinitialarabic" }, { 0xfebc, "sadmedialarabic" }, { 0x0ab8, "sagujarati" }, { 0x0a38, "sagurmukhi" }, { 0x3055, "sahiragana" }, { 0x30b5, "sakatakana" }, { 0xff7b, "sakatakanahalfwidth" }, { 0xfdfa, "sallallahoualayhewasallamarabic" }, { 0x05e1, "samekh" }, { 0xfb41, "samekhdagesh" }, { 0xfb41, "samekhdageshhebrew" }, { 0x05e1, "samekhhebrew" }, { 0x0e32, "saraaathai" }, { 0x0e41, "saraaethai" }, { 0x0e44, "saraaimaimalaithai" }, { 0x0e43, "saraaimaimuanthai" }, { 0x0e33, "saraamthai" }, { 0x0e30, "saraathai" }, { 0x0e40, "saraethai" }, { 0xf886, "saraiileftthai" }, { 0x0e35, "saraiithai" }, { 0xf885, "saraileftthai" }, { 0x0e34, "saraithai" }, { 0x0e42, "saraothai" }, { 0xf888, "saraueeleftthai" }, { 0x0e37, "saraueethai" }, { 0xf887, "saraueleftthai" }, { 0x0e36, "sarauethai" }, { 0x0e38, "sarauthai" }, { 0x0e39, "sarauuthai" }, { 0x3119, "sbopomofo" }, { 0x0161, "scaron" }, { 0x1e67, "scarondotaccent" }, { 0x015f, "scedilla" }, { 0x0259, "schwa" }, { 0x04d9, "schwacyrillic" }, { 0x04db, "schwadieresiscyrillic" }, { 0x025a, "schwahook" }, { 0x24e2, "scircle" }, { 0x015d, "scircumflex" }, { 0x0219, "scommaaccent" }, { 0x1e61, "sdotaccent" }, { 0x1e63, "sdotbelow" }, { 0x1e69, "sdotbelowdotaccent" }, { 0x033c, "seagullbelowcmb" }, { 0x2033, "second" }, { 0x02ca, "secondtonechinese" }, { 0x00a7, "section" }, { 0x0633, "seenarabic" }, { 0xfeb2, "seenfinalarabic" }, { 0xfeb3, "seeninitialarabic" }, { 0xfeb4, "seenmedialarabic" }, { 0x05b6, "segol" }, { 0x05b6, "segol13" }, { 0x05b6, "segol1f" }, { 0x05b6, "segol2c" }, { 0x05b6, "segolhebrew" }, { 0x05b6, "segolnarrowhebrew" }, { 0x05b6, "segolquarterhebrew" }, { 0x0592, "segoltahebrew" }, { 0x05b6, "segolwidehebrew" }, { 0x057d, "seharmenian" }, { 0x305b, "sehiragana" }, { 0x30bb, "sekatakana" }, { 0xff7e, "sekatakanahalfwidth" }, { 0x003b, "semicolon" }, { 0x061b, "semicolonarabic" }, { 0xff1b, "semicolonmonospace" }, { 0xfe54, "semicolonsmall" }, { 0x309c, "semivoicedmarkkana" }, { 0xff9f, "semivoicedmarkkanahalfwidth" }, { 0x3322, "sentisquare" }, { 0x3323, "sentosquare" }, { 0x0037, "seven" }, { 0x0667, "sevenarabic" }, { 0x09ed, "sevenbengali" }, { 0x2466, "sevencircle" }, { 0x2790, "sevencircleinversesansserif" }, { 0x096d, "sevendeva" }, { 0x215e, "seveneighths" }, { 0x0aed, "sevengujarati" }, { 0x0a6d, "sevengurmukhi" }, { 0x0667, "sevenhackarabic" }, { 0x3027, "sevenhangzhou" }, { 0x3226, "sevenideographicparen" }, { 0x2087, "seveninferior" }, { 0xff17, "sevenmonospace" }, { 0xf737, "sevenoldstyle" }, { 0x247a, "sevenparen" }, { 0x248e, "sevenperiod" }, { 0x06f7, "sevenpersian" }, { 0x2176, "sevenroman" }, { 0x2077, "sevensuperior" }, { 0x2470, "seventeencircle" }, { 0x2484, "seventeenparen" }, { 0x2498, "seventeenperiod" }, { 0x0e57, "seventhai" }, { 0x00ad, "sfthyphen" }, { 0x0577, "shaarmenian" }, { 0x09b6, "shabengali" }, { 0x0448, "shacyrillic" }, { 0x0651, "shaddaarabic" }, { 0xfc61, "shaddadammaarabic" }, { 0xfc5e, "shaddadammatanarabic" }, { 0xfc60, "shaddafathaarabic" }, { 0xfc62, "shaddakasraarabic" }, { 0xfc5f, "shaddakasratanarabic" }, { 0x2592, "shade" }, { 0x2593, "shadedark" }, { 0x2591, "shadelight" }, { 0x2592, "shademedium" }, { 0x0936, "shadeva" }, { 0x0ab6, "shagujarati" }, { 0x0a36, "shagurmukhi" }, { 0x0593, "shalshelethebrew" }, { 0x3115, "shbopomofo" }, { 0x0449, "shchacyrillic" }, { 0x0634, "sheenarabic" }, { 0xfeb6, "sheenfinalarabic" }, { 0xfeb7, "sheeninitialarabic" }, { 0xfeb8, "sheenmedialarabic" }, { 0x03e3, "sheicoptic" }, { 0x20aa, "sheqel" }, { 0x20aa, "sheqelhebrew" }, { 0x05b0, "sheva" }, { 0x05b0, "sheva115" }, { 0x05b0, "sheva15" }, { 0x05b0, "sheva22" }, { 0x05b0, "sheva2e" }, { 0x05b0, "shevahebrew" }, { 0x05b0, "shevanarrowhebrew" }, { 0x05b0, "shevaquarterhebrew" }, { 0x05b0, "shevawidehebrew" }, { 0x04bb, "shhacyrillic" }, { 0x03ed, "shimacoptic" }, { 0x05e9, "shin" }, { 0xfb49, "shindagesh" }, { 0xfb49, "shindageshhebrew" }, { 0xfb2c, "shindageshshindot" }, { 0xfb2c, "shindageshshindothebrew" }, { 0xfb2d, "shindageshsindot" }, { 0xfb2d, "shindageshsindothebrew" }, { 0x05c1, "shindothebrew" }, { 0x05e9, "shinhebrew" }, { 0xfb2a, "shinshindot" }, { 0xfb2a, "shinshindothebrew" }, { 0xfb2b, "shinsindot" }, { 0xfb2b, "shinsindothebrew" }, { 0x0282, "shook" }, { 0x03c3, "sigma" }, { 0x03c2, "sigma1" }, { 0x03c2, "sigmafinal" }, { 0x03f2, "sigmalunatesymbolgreek" }, { 0x3057, "sihiragana" }, { 0x30b7, "sikatakana" }, { 0xff7c, "sikatakanahalfwidth" }, { 0x05bd, "siluqhebrew" }, { 0x05bd, "siluqlefthebrew" }, { 0x223c, "similar" }, { 0x2243, "similarequal" }, { 0x05c2, "sindothebrew" }, { 0x3274, "siosacirclekorean" }, { 0x3214, "siosaparenkorean" }, { 0x317e, "sioscieuckorean" }, { 0x3266, "sioscirclekorean" }, { 0x317a, "sioskiyeokkorean" }, { 0x3145, "sioskorean" }, { 0x317b, "siosnieunkorean" }, { 0x3206, "siosparenkorean" }, { 0x317d, "siospieupkorean" }, { 0x317c, "siostikeutkorean" }, { 0x0036, "six" }, { 0x0666, "sixarabic" }, { 0x09ec, "sixbengali" }, { 0x2465, "sixcircle" }, { 0x278f, "sixcircleinversesansserif" }, { 0x096c, "sixdeva" }, { 0x0aec, "sixgujarati" }, { 0x0a6c, "sixgurmukhi" }, { 0x0666, "sixhackarabic" }, { 0x3026, "sixhangzhou" }, { 0x3225, "sixideographicparen" }, { 0x2086, "sixinferior" }, { 0xff16, "sixmonospace" }, { 0xf736, "sixoldstyle" }, { 0x2479, "sixparen" }, { 0x248d, "sixperiod" }, { 0x06f6, "sixpersian" }, { 0x2175, "sixroman" }, { 0x2076, "sixsuperior" }, { 0x246f, "sixteencircle" }, { 0x09f9, "sixteencurrencydenominatorbengali" }, { 0x2483, "sixteenparen" }, { 0x2497, "sixteenperiod" }, { 0x0e56, "sixthai" }, { 0x002f, "slash" }, { 0xff0f, "slashmonospace" }, { 0x017f, "slong" }, { 0x1e9b, "slongdotaccent" }, { 0x263a, "smileface" }, { 0xff53, "smonospace" }, { 0x05c3, "sofpasuqhebrew" }, { 0x00ad, "softhyphen" }, { 0x044c, "softsigncyrillic" }, { 0x305d, "sohiragana" }, { 0x30bd, "sokatakana" }, { 0xff7f, "sokatakanahalfwidth" }, { 0x0338, "soliduslongoverlaycmb" }, { 0x0337, "solidusshortoverlaycmb" }, { 0x0e29, "sorusithai" }, { 0x0e28, "sosalathai" }, { 0x0e0b, "sosothai" }, { 0x0e2a, "sosuathai" }, { 0x0020, "space" }, { 0x0020, "spacehackarabic" }, { 0x2660, "spade" }, { 0x2660, "spadesuitblack" }, { 0x2664, "spadesuitwhite" }, { 0x24ae, "sparen" }, { 0x033b, "squarebelowcmb" }, { 0x33c4, "squarecc" }, { 0x339d, "squarecm" }, { 0x25a9, "squarediagonalcrosshatchfill" }, { 0x25a4, "squarehorizontalfill" }, { 0x338f, "squarekg" }, { 0x339e, "squarekm" }, { 0x33ce, "squarekmcapital" }, { 0x33d1, "squareln" }, { 0x33d2, "squarelog" }, { 0x338e, "squaremg" }, { 0x33d5, "squaremil" }, { 0x339c, "squaremm" }, { 0x33a1, "squaremsquared" }, { 0x25a6, "squareorthogonalcrosshatchfill" }, { 0x25a7, "squareupperlefttolowerrightfill" }, { 0x25a8, "squareupperrighttolowerleftfill" }, { 0x25a5, "squareverticalfill" }, { 0x25a3, "squarewhitewithsmallblack" }, { 0x33db, "srsquare" }, { 0x09b7, "ssabengali" }, { 0x0937, "ssadeva" }, { 0x0ab7, "ssagujarati" }, { 0x3149, "ssangcieuckorean" }, { 0x3185, "ssanghieuhkorean" }, { 0x3180, "ssangieungkorean" }, { 0x3132, "ssangkiyeokkorean" }, { 0x3165, "ssangnieunkorean" }, { 0x3143, "ssangpieupkorean" }, { 0x3146, "ssangsioskorean" }, { 0x3138, "ssangtikeutkorean" }, { 0xf6f2, "ssuperior" }, { 0x00a3, "sterling" }, { 0xffe1, "sterlingmonospace" }, { 0x0336, "strokelongoverlaycmb" }, { 0x0335, "strokeshortoverlaycmb" }, { 0x2282, "subset" }, { 0x228a, "subsetnotequal" }, { 0x2286, "subsetorequal" }, { 0x227b, "succeeds" }, { 0x220b, "suchthat" }, { 0x3059, "suhiragana" }, { 0x30b9, "sukatakana" }, { 0xff7d, "sukatakanahalfwidth" }, { 0x0652, "sukunarabic" }, { 0x2211, "summation" }, { 0x263c, "sun" }, { 0x2283, "superset" }, { 0x228b, "supersetnotequal" }, { 0x2287, "supersetorequal" }, { 0x33dc, "svsquare" }, { 0x337c, "syouwaerasquare" }, { 0x0074, "t" }, { 0x09a4, "tabengali" }, { 0x22a4, "tackdown" }, { 0x22a3, "tackleft" }, { 0x0924, "tadeva" }, { 0x0aa4, "tagujarati" }, { 0x0a24, "tagurmukhi" }, { 0x0637, "taharabic" }, { 0xfec2, "tahfinalarabic" }, { 0xfec3, "tahinitialarabic" }, { 0x305f, "tahiragana" }, { 0xfec4, "tahmedialarabic" }, { 0x337d, "taisyouerasquare" }, { 0x30bf, "takatakana" }, { 0xff80, "takatakanahalfwidth" }, { 0x0640, "tatweelarabic" }, { 0x03c4, "tau" }, { 0x05ea, "tav" }, { 0xfb4a, "tavdages" }, { 0xfb4a, "tavdagesh" }, { 0xfb4a, "tavdageshhebrew" }, { 0x05ea, "tavhebrew" }, { 0x0167, "tbar" }, { 0x310a, "tbopomofo" }, { 0x0165, "tcaron" }, { 0x02a8, "tccurl" }, { 0x0163, "tcedilla" }, { 0x0686, "tcheharabic" }, { 0xfb7b, "tchehfinalarabic" }, { 0xfb7c, "tchehinitialarabic" }, { 0xfb7d, "tchehmedialarabic" }, { 0x24e3, "tcircle" }, { 0x1e71, "tcircumflexbelow" }, { 0x0163, "tcommaaccent" }, { 0x1e97, "tdieresis" }, { 0x1e6b, "tdotaccent" }, { 0x1e6d, "tdotbelow" }, { 0x0442, "tecyrillic" }, { 0x04ad, "tedescendercyrillic" }, { 0x062a, "teharabic" }, { 0xfe96, "tehfinalarabic" }, { 0xfca2, "tehhahinitialarabic" }, { 0xfc0c, "tehhahisolatedarabic" }, { 0xfe97, "tehinitialarabic" }, { 0x3066, "tehiragana" }, { 0xfca1, "tehjeeminitialarabic" }, { 0xfc0b, "tehjeemisolatedarabic" }, { 0x0629, "tehmarbutaarabic" }, { 0xfe94, "tehmarbutafinalarabic" }, { 0xfe98, "tehmedialarabic" }, { 0xfca4, "tehmeeminitialarabic" }, { 0xfc0e, "tehmeemisolatedarabic" }, { 0xfc73, "tehnoonfinalarabic" }, { 0x30c6, "tekatakana" }, { 0xff83, "tekatakanahalfwidth" }, { 0x2121, "telephone" }, { 0x260e, "telephoneblack" }, { 0x05a0, "telishagedolahebrew" }, { 0x05a9, "telishaqetanahebrew" }, { 0x2469, "tencircle" }, { 0x3229, "tenideographicparen" }, { 0x247d, "tenparen" }, { 0x2491, "tenperiod" }, { 0x2179, "tenroman" }, { 0x02a7, "tesh" }, { 0x05d8, "tet" }, { 0xfb38, "tetdagesh" }, { 0xfb38, "tetdageshhebrew" }, { 0x05d8, "tethebrew" }, { 0x04b5, "tetsecyrillic" }, { 0x059b, "tevirhebrew" }, { 0x059b, "tevirlefthebrew" }, { 0x09a5, "thabengali" }, { 0x0925, "thadeva" }, { 0x0aa5, "thagujarati" }, { 0x0a25, "thagurmukhi" }, { 0x0630, "thalarabic" }, { 0xfeac, "thalfinalarabic" }, { 0xf898, "thanthakhatlowleftthai" }, { 0xf897, "thanthakhatlowrightthai" }, { 0x0e4c, "thanthakhatthai" }, { 0xf896, "thanthakhatupperleftthai" }, { 0x062b, "theharabic" }, { 0xfe9a, "thehfinalarabic" }, { 0xfe9b, "thehinitialarabic" }, { 0xfe9c, "thehmedialarabic" }, { 0x2203, "thereexists" }, { 0x2234, "therefore" }, { 0x03b8, "theta" }, { 0x03d1, "theta1" }, { 0x03d1, "thetasymbolgreek" }, { 0x3279, "thieuthacirclekorean" }, { 0x3219, "thieuthaparenkorean" }, { 0x326b, "thieuthcirclekorean" }, { 0x314c, "thieuthkorean" }, { 0x320b, "thieuthparenkorean" }, { 0x246c, "thirteencircle" }, { 0x2480, "thirteenparen" }, { 0x2494, "thirteenperiod" }, { 0x0e11, "thonangmonthothai" }, { 0x01ad, "thook" }, { 0x0e12, "thophuthaothai" }, { 0x00fe, "thorn" }, { 0x0e17, "thothahanthai" }, { 0x0e10, "thothanthai" }, { 0x0e18, "thothongthai" }, { 0x0e16, "thothungthai" }, { 0x0482, "thousandcyrillic" }, { 0x066c, "thousandsseparatorarabic" }, { 0x066c, "thousandsseparatorpersian" }, { 0x0033, "three" }, { 0x0663, "threearabic" }, { 0x09e9, "threebengali" }, { 0x2462, "threecircle" }, { 0x278c, "threecircleinversesansserif" }, { 0x0969, "threedeva" }, { 0x215c, "threeeighths" }, { 0x0ae9, "threegujarati" }, { 0x0a69, "threegurmukhi" }, { 0x0663, "threehackarabic" }, { 0x3023, "threehangzhou" }, { 0x3222, "threeideographicparen" }, { 0x2083, "threeinferior" }, { 0xff13, "threemonospace" }, { 0x09f6, "threenumeratorbengali" }, { 0xf733, "threeoldstyle" }, { 0x2476, "threeparen" }, { 0x248a, "threeperiod" }, { 0x06f3, "threepersian" }, { 0x00be, "threequarters" }, { 0xf6de, "threequartersemdash" }, { 0x2172, "threeroman" }, { 0x00b3, "threesuperior" }, { 0x0e53, "threethai" }, { 0x3394, "thzsquare" }, { 0x3061, "tihiragana" }, { 0x30c1, "tikatakana" }, { 0xff81, "tikatakanahalfwidth" }, { 0x3270, "tikeutacirclekorean" }, { 0x3210, "tikeutaparenkorean" }, { 0x3262, "tikeutcirclekorean" }, { 0x3137, "tikeutkorean" }, { 0x3202, "tikeutparenkorean" }, { 0x02dc, "tilde" }, { 0x0330, "tildebelowcmb" }, { 0x0303, "tildecmb" }, { 0x0303, "tildecomb" }, { 0x0360, "tildedoublecmb" }, { 0x223c, "tildeoperator" }, { 0x0334, "tildeoverlaycmb" }, { 0x033e, "tildeverticalcmb" }, { 0x2297, "timescircle" }, { 0x0596, "tipehahebrew" }, { 0x0596, "tipehalefthebrew" }, { 0x0a70, "tippigurmukhi" }, { 0x0483, "titlocyrilliccmb" }, { 0x057f, "tiwnarmenian" }, { 0x1e6f, "tlinebelow" }, { 0xff54, "tmonospace" }, { 0x0569, "toarmenian" }, { 0x3068, "tohiragana" }, { 0x30c8, "tokatakana" }, { 0xff84, "tokatakanahalfwidth" }, { 0x02e5, "tonebarextrahighmod" }, { 0x02e9, "tonebarextralowmod" }, { 0x02e6, "tonebarhighmod" }, { 0x02e8, "tonebarlowmod" }, { 0x02e7, "tonebarmidmod" }, { 0x01bd, "tonefive" }, { 0x0185, "tonesix" }, { 0x01a8, "tonetwo" }, { 0x0384, "tonos" }, { 0x3327, "tonsquare" }, { 0x0e0f, "topatakthai" }, { 0x3014, "tortoiseshellbracketleft" }, { 0xfe5d, "tortoiseshellbracketleftsmall" }, { 0xfe39, "tortoiseshellbracketleftvertical" }, { 0x3015, "tortoiseshellbracketright" }, { 0xfe5e, "tortoiseshellbracketrightsmall" }, { 0xfe3a, "tortoiseshellbracketrightvertical" }, { 0x0e15, "totaothai" }, { 0x01ab, "tpalatalhook" }, { 0x24af, "tparen" }, { 0x2122, "trademark" }, { 0xf8ea, "trademarksans" }, { 0xf6db, "trademarkserif" }, { 0x0288, "tretroflexhook" }, { 0x25bc, "triagdn" }, { 0x25c4, "triaglf" }, { 0x25ba, "triagrt" }, { 0x25b2, "triagup" }, { 0x02a6, "ts" }, { 0x05e6, "tsadi" }, { 0xfb46, "tsadidagesh" }, { 0xfb46, "tsadidageshhebrew" }, { 0x05e6, "tsadihebrew" }, { 0x0446, "tsecyrillic" }, { 0x05b5, "tsere" }, { 0x05b5, "tsere12" }, { 0x05b5, "tsere1e" }, { 0x05b5, "tsere2b" }, { 0x05b5, "tserehebrew" }, { 0x05b5, "tserenarrowhebrew" }, { 0x05b5, "tserequarterhebrew" }, { 0x05b5, "tserewidehebrew" }, { 0x045b, "tshecyrillic" }, { 0xf6f3, "tsuperior" }, { 0x099f, "ttabengali" }, { 0x091f, "ttadeva" }, { 0x0a9f, "ttagujarati" }, { 0x0a1f, "ttagurmukhi" }, { 0x0679, "tteharabic" }, { 0xfb67, "ttehfinalarabic" }, { 0xfb68, "ttehinitialarabic" }, { 0xfb69, "ttehmedialarabic" }, { 0x09a0, "tthabengali" }, { 0x0920, "tthadeva" }, { 0x0aa0, "tthagujarati" }, { 0x0a20, "tthagurmukhi" }, { 0x0287, "tturned" }, { 0x3064, "tuhiragana" }, { 0x30c4, "tukatakana" }, { 0xff82, "tukatakanahalfwidth" }, { 0x3063, "tusmallhiragana" }, { 0x30c3, "tusmallkatakana" }, { 0xff6f, "tusmallkatakanahalfwidth" }, { 0x246b, "twelvecircle" }, { 0x247f, "twelveparen" }, { 0x2493, "twelveperiod" }, { 0x217b, "twelveroman" }, { 0x2473, "twentycircle" }, { 0x5344, "twentyhangzhou" }, { 0x2487, "twentyparen" }, { 0x249b, "twentyperiod" }, { 0x0032, "two" }, { 0x0662, "twoarabic" }, { 0x09e8, "twobengali" }, { 0x2461, "twocircle" }, { 0x278b, "twocircleinversesansserif" }, { 0x0968, "twodeva" }, { 0x2025, "twodotenleader" }, { 0x2025, "twodotleader" }, { 0xfe30, "twodotleadervertical" }, { 0x0ae8, "twogujarati" }, { 0x0a68, "twogurmukhi" }, { 0x0662, "twohackarabic" }, { 0x3022, "twohangzhou" }, { 0x3221, "twoideographicparen" }, { 0x2082, "twoinferior" }, { 0xff12, "twomonospace" }, { 0x09f5, "twonumeratorbengali" }, { 0xf732, "twooldstyle" }, { 0x2475, "twoparen" }, { 0x2489, "twoperiod" }, { 0x06f2, "twopersian" }, { 0x2171, "tworoman" }, { 0x01bb, "twostroke" }, { 0x00b2, "twosuperior" }, { 0x0e52, "twothai" }, { 0x2154, "twothirds" }, { 0x0075, "u" }, { 0x00fa, "uacute" }, { 0x0289, "ubar" }, { 0x0989, "ubengali" }, { 0x3128, "ubopomofo" }, { 0x016d, "ubreve" }, { 0x01d4, "ucaron" }, { 0x24e4, "ucircle" }, { 0x00fb, "ucircumflex" }, { 0x1e77, "ucircumflexbelow" }, { 0x0443, "ucyrillic" }, { 0x0951, "udattadeva" }, { 0x0171, "udblacute" }, { 0x0215, "udblgrave" }, { 0x0909, "udeva" }, { 0x00fc, "udieresis" }, { 0x01d8, "udieresisacute" }, { 0x1e73, "udieresisbelow" }, { 0x01da, "udieresiscaron" }, { 0x04f1, "udieresiscyrillic" }, { 0x01dc, "udieresisgrave" }, { 0x01d6, "udieresismacron" }, { 0x1ee5, "udotbelow" }, { 0x00f9, "ugrave" }, { 0x0a89, "ugujarati" }, { 0x0a09, "ugurmukhi" }, { 0x3046, "uhiragana" }, { 0x1ee7, "uhookabove" }, { 0x01b0, "uhorn" }, { 0x1ee9, "uhornacute" }, { 0x1ef1, "uhorndotbelow" }, { 0x1eeb, "uhorngrave" }, { 0x1eed, "uhornhookabove" }, { 0x1eef, "uhorntilde" }, { 0x0171, "uhungarumlaut" }, { 0x04f3, "uhungarumlautcyrillic" }, { 0x0217, "uinvertedbreve" }, { 0x30a6, "ukatakana" }, { 0xff73, "ukatakanahalfwidth" }, { 0x0479, "ukcyrillic" }, { 0x315c, "ukorean" }, { 0x016b, "umacron" }, { 0x04ef, "umacroncyrillic" }, { 0x1e7b, "umacrondieresis" }, { 0x0a41, "umatragurmukhi" }, { 0xff55, "umonospace" }, { 0x005f, "underscore" }, { 0x2017, "underscoredbl" }, { 0xff3f, "underscoremonospace" }, { 0xfe33, "underscorevertical" }, { 0xfe4f, "underscorewavy" }, { 0x222a, "union" }, { 0x2200, "universal" }, { 0x0173, "uogonek" }, { 0x24b0, "uparen" }, { 0x2580, "upblock" }, { 0x05c4, "upperdothebrew" }, { 0x03c5, "upsilon" }, { 0x03cb, "upsilondieresis" }, { 0x03b0, "upsilondieresistonos" }, { 0x028a, "upsilonlatin" }, { 0x03cd, "upsilontonos" }, { 0x031d, "uptackbelowcmb" }, { 0x02d4, "uptackmod" }, { 0x0a73, "uragurmukhi" }, { 0x016f, "uring" }, { 0x045e, "ushortcyrillic" }, { 0x3045, "usmallhiragana" }, { 0x30a5, "usmallkatakana" }, { 0xff69, "usmallkatakanahalfwidth" }, { 0x04af, "ustraightcyrillic" }, { 0x04b1, "ustraightstrokecyrillic" }, { 0x0169, "utilde" }, { 0x1e79, "utildeacute" }, { 0x1e75, "utildebelow" }, { 0x098a, "uubengali" }, { 0x090a, "uudeva" }, { 0x0a8a, "uugujarati" }, { 0x0a0a, "uugurmukhi" }, { 0x0a42, "uumatragurmukhi" }, { 0x09c2, "uuvowelsignbengali" }, { 0x0942, "uuvowelsigndeva" }, { 0x0ac2, "uuvowelsigngujarati" }, { 0x09c1, "uvowelsignbengali" }, { 0x0941, "uvowelsigndeva" }, { 0x0ac1, "uvowelsigngujarati" }, { 0x0076, "v" }, { 0x0935, "vadeva" }, { 0x0ab5, "vagujarati" }, { 0x0a35, "vagurmukhi" }, { 0x30f7, "vakatakana" }, { 0x05d5, "vav" }, { 0xfb35, "vavdagesh" }, { 0xfb35, "vavdagesh65" }, { 0xfb35, "vavdageshhebrew" }, { 0x05d5, "vavhebrew" }, { 0xfb4b, "vavholam" }, { 0xfb4b, "vavholamhebrew" }, { 0x05f0, "vavvavhebrew" }, { 0x05f1, "vavyodhebrew" }, { 0x24e5, "vcircle" }, { 0x1e7f, "vdotbelow" }, { 0x0432, "vecyrillic" }, { 0x06a4, "veharabic" }, { 0xfb6b, "vehfinalarabic" }, { 0xfb6c, "vehinitialarabic" }, { 0xfb6d, "vehmedialarabic" }, { 0x30f9, "vekatakana" }, { 0x2640, "venus" }, { 0x007c, "verticalbar" }, { 0x030d, "verticallineabovecmb" }, { 0x0329, "verticallinebelowcmb" }, { 0x02cc, "verticallinelowmod" }, { 0x02c8, "verticallinemod" }, { 0x057e, "vewarmenian" }, { 0x028b, "vhook" }, { 0x30f8, "vikatakana" }, { 0x09cd, "viramabengali" }, { 0x094d, "viramadeva" }, { 0x0acd, "viramagujarati" }, { 0x0983, "visargabengali" }, { 0x0903, "visargadeva" }, { 0x0a83, "visargagujarati" }, { 0xff56, "vmonospace" }, { 0x0578, "voarmenian" }, { 0x309e, "voicediterationhiragana" }, { 0x30fe, "voicediterationkatakana" }, { 0x309b, "voicedmarkkana" }, { 0xff9e, "voicedmarkkanahalfwidth" }, { 0x30fa, "vokatakana" }, { 0x24b1, "vparen" }, { 0x1e7d, "vtilde" }, { 0x028c, "vturned" }, { 0x3094, "vuhiragana" }, { 0x30f4, "vukatakana" }, { 0x0077, "w" }, { 0x1e83, "wacute" }, { 0x3159, "waekorean" }, { 0x308f, "wahiragana" }, { 0x30ef, "wakatakana" }, { 0xff9c, "wakatakanahalfwidth" }, { 0x3158, "wakorean" }, { 0x308e, "wasmallhiragana" }, { 0x30ee, "wasmallkatakana" }, { 0x3357, "wattosquare" }, { 0x301c, "wavedash" }, { 0xfe34, "wavyunderscorevertical" }, { 0x0648, "wawarabic" }, { 0xfeee, "wawfinalarabic" }, { 0x0624, "wawhamzaabovearabic" }, { 0xfe86, "wawhamzaabovefinalarabic" }, { 0x33dd, "wbsquare" }, { 0x24e6, "wcircle" }, { 0x0175, "wcircumflex" }, { 0x1e85, "wdieresis" }, { 0x1e87, "wdotaccent" }, { 0x1e89, "wdotbelow" }, { 0x3091, "wehiragana" }, { 0x2118, "weierstrass" }, { 0x30f1, "wekatakana" }, { 0x315e, "wekorean" }, { 0x315d, "weokorean" }, { 0x1e81, "wgrave" }, { 0x25e6, "whitebullet" }, { 0x25cb, "whitecircle" }, { 0x25d9, "whitecircleinverse" }, { 0x300e, "whitecornerbracketleft" }, { 0xfe43, "whitecornerbracketleftvertical" }, { 0x300f, "whitecornerbracketright" }, { 0xfe44, "whitecornerbracketrightvertical" }, { 0x25c7, "whitediamond" }, { 0x25c8, "whitediamondcontainingblacksmalldiamond" }, { 0x25bf, "whitedownpointingsmalltriangle" }, { 0x25bd, "whitedownpointingtriangle" }, { 0x25c3, "whiteleftpointingsmalltriangle" }, { 0x25c1, "whiteleftpointingtriangle" }, { 0x3016, "whitelenticularbracketleft" }, { 0x3017, "whitelenticularbracketright" }, { 0x25b9, "whiterightpointingsmalltriangle" }, { 0x25b7, "whiterightpointingtriangle" }, { 0x25ab, "whitesmallsquare" }, { 0x263a, "whitesmilingface" }, { 0x25a1, "whitesquare" }, { 0x2606, "whitestar" }, { 0x260f, "whitetelephone" }, { 0x3018, "whitetortoiseshellbracketleft" }, { 0x3019, "whitetortoiseshellbracketright" }, { 0x25b5, "whiteuppointingsmalltriangle" }, { 0x25b3, "whiteuppointingtriangle" }, { 0x3090, "wihiragana" }, { 0x30f0, "wikatakana" }, { 0x315f, "wikorean" }, { 0xff57, "wmonospace" }, { 0x3092, "wohiragana" }, { 0x30f2, "wokatakana" }, { 0xff66, "wokatakanahalfwidth" }, { 0x20a9, "won" }, { 0xffe6, "wonmonospace" }, { 0x0e27, "wowaenthai" }, { 0x24b2, "wparen" }, { 0x1e98, "wring" }, { 0x02b7, "wsuperior" }, { 0x028d, "wturned" }, { 0x01bf, "wynn" }, { 0x0078, "x" }, { 0x033d, "xabovecmb" }, { 0x3112, "xbopomofo" }, { 0x24e7, "xcircle" }, { 0x1e8d, "xdieresis" }, { 0x1e8b, "xdotaccent" }, { 0x056d, "xeharmenian" }, { 0x03be, "xi" }, { 0xff58, "xmonospace" }, { 0x24b3, "xparen" }, { 0x02e3, "xsuperior" }, { 0x0079, "y" }, { 0x334e, "yaadosquare" }, { 0x09af, "yabengali" }, { 0x00fd, "yacute" }, { 0x092f, "yadeva" }, { 0x3152, "yaekorean" }, { 0x0aaf, "yagujarati" }, { 0x0a2f, "yagurmukhi" }, { 0x3084, "yahiragana" }, { 0x30e4, "yakatakana" }, { 0xff94, "yakatakanahalfwidth" }, { 0x3151, "yakorean" }, { 0x0e4e, "yamakkanthai" }, { 0x3083, "yasmallhiragana" }, { 0x30e3, "yasmallkatakana" }, { 0xff6c, "yasmallkatakanahalfwidth" }, { 0x0463, "yatcyrillic" }, { 0x24e8, "ycircle" }, { 0x0177, "ycircumflex" }, { 0x00ff, "ydieresis" }, { 0x1e8f, "ydotaccent" }, { 0x1ef5, "ydotbelow" }, { 0x064a, "yeharabic" }, { 0x06d2, "yehbarreearabic" }, { 0xfbaf, "yehbarreefinalarabic" }, { 0xfef2, "yehfinalarabic" }, { 0x0626, "yehhamzaabovearabic" }, { 0xfe8a, "yehhamzaabovefinalarabic" }, { 0xfe8b, "yehhamzaaboveinitialarabic" }, { 0xfe8c, "yehhamzaabovemedialarabic" }, { 0xfef3, "yehinitialarabic" }, { 0xfef4, "yehmedialarabic" }, { 0xfcdd, "yehmeeminitialarabic" }, { 0xfc58, "yehmeemisolatedarabic" }, { 0xfc94, "yehnoonfinalarabic" }, { 0x06d1, "yehthreedotsbelowarabic" }, { 0x3156, "yekorean" }, { 0x00a5, "yen" }, { 0xffe5, "yenmonospace" }, { 0x3155, "yeokorean" }, { 0x3186, "yeorinhieuhkorean" }, { 0x05aa, "yerahbenyomohebrew" }, { 0x05aa, "yerahbenyomolefthebrew" }, { 0x044b, "yericyrillic" }, { 0x04f9, "yerudieresiscyrillic" }, { 0x3181, "yesieungkorean" }, { 0x3183, "yesieungpansioskorean" }, { 0x3182, "yesieungsioskorean" }, { 0x059a, "yetivhebrew" }, { 0x1ef3, "ygrave" }, { 0x01b4, "yhook" }, { 0x1ef7, "yhookabove" }, { 0x0575, "yiarmenian" }, { 0x0457, "yicyrillic" }, { 0x3162, "yikorean" }, { 0x262f, "yinyang" }, { 0x0582, "yiwnarmenian" }, { 0xff59, "ymonospace" }, { 0x05d9, "yod" }, { 0xfb39, "yoddagesh" }, { 0xfb39, "yoddageshhebrew" }, { 0x05d9, "yodhebrew" }, { 0x05f2, "yodyodhebrew" }, { 0xfb1f, "yodyodpatahhebrew" }, { 0x3088, "yohiragana" }, { 0x3189, "yoikorean" }, { 0x30e8, "yokatakana" }, { 0xff96, "yokatakanahalfwidth" }, { 0x315b, "yokorean" }, { 0x3087, "yosmallhiragana" }, { 0x30e7, "yosmallkatakana" }, { 0xff6e, "yosmallkatakanahalfwidth" }, { 0x03f3, "yotgreek" }, { 0x3188, "yoyaekorean" }, { 0x3187, "yoyakorean" }, { 0x0e22, "yoyakthai" }, { 0x0e0d, "yoyingthai" }, { 0x24b4, "yparen" }, { 0x037a, "ypogegrammeni" }, { 0x0345, "ypogegrammenigreekcmb" }, { 0x01a6, "yr" }, { 0x1e99, "yring" }, { 0x02b8, "ysuperior" }, { 0x1ef9, "ytilde" }, { 0x028e, "yturned" }, { 0x3086, "yuhiragana" }, { 0x318c, "yuikorean" }, { 0x30e6, "yukatakana" }, { 0xff95, "yukatakanahalfwidth" }, { 0x3160, "yukorean" }, { 0x046b, "yusbigcyrillic" }, { 0x046d, "yusbigiotifiedcyrillic" }, { 0x0467, "yuslittlecyrillic" }, { 0x0469, "yuslittleiotifiedcyrillic" }, { 0x3085, "yusmallhiragana" }, { 0x30e5, "yusmallkatakana" }, { 0xff6d, "yusmallkatakanahalfwidth" }, { 0x318b, "yuyekorean" }, { 0x318a, "yuyeokorean" }, { 0x09df, "yyabengali" }, { 0x095f, "yyadeva" }, { 0x007a, "z" }, { 0x0566, "zaarmenian" }, { 0x017a, "zacute" }, { 0x095b, "zadeva" }, { 0x0a5b, "zagurmukhi" }, { 0x0638, "zaharabic" }, { 0xfec6, "zahfinalarabic" }, { 0xfec7, "zahinitialarabic" }, { 0x3056, "zahiragana" }, { 0xfec8, "zahmedialarabic" }, { 0x0632, "zainarabic" }, { 0xfeb0, "zainfinalarabic" }, { 0x30b6, "zakatakana" }, { 0x0595, "zaqefgadolhebrew" }, { 0x0594, "zaqefqatanhebrew" }, { 0x0598, "zarqahebrew" }, { 0x05d6, "zayin" }, { 0xfb36, "zayindagesh" }, { 0xfb36, "zayindageshhebrew" }, { 0x05d6, "zayinhebrew" }, { 0x3117, "zbopomofo" }, { 0x017e, "zcaron" }, { 0x24e9, "zcircle" }, { 0x1e91, "zcircumflex" }, { 0x0291, "zcurl" }, { 0x017c, "zdot" }, { 0x017c, "zdotaccent" }, { 0x1e93, "zdotbelow" }, { 0x0437, "zecyrillic" }, { 0x0499, "zedescendercyrillic" }, { 0x04df, "zedieresiscyrillic" }, { 0x305c, "zehiragana" }, { 0x30bc, "zekatakana" }, { 0x0030, "zero" }, { 0x0660, "zeroarabic" }, { 0x09e6, "zerobengali" }, { 0x0966, "zerodeva" }, { 0x0ae6, "zerogujarati" }, { 0x0a66, "zerogurmukhi" }, { 0x0660, "zerohackarabic" }, { 0x2080, "zeroinferior" }, { 0xff10, "zeromonospace" }, { 0xf730, "zerooldstyle" }, { 0x06f0, "zeropersian" }, { 0x2070, "zerosuperior" }, { 0x0e50, "zerothai" }, { 0xfeff, "zerowidthjoiner" }, { 0x200c, "zerowidthnonjoiner" }, { 0x200b, "zerowidthspace" }, { 0x03b6, "zeta" }, { 0x3113, "zhbopomofo" }, { 0x056a, "zhearmenian" }, { 0x04c2, "zhebrevecyrillic" }, { 0x0436, "zhecyrillic" }, { 0x0497, "zhedescendercyrillic" }, { 0x04dd, "zhedieresiscyrillic" }, { 0x3058, "zihiragana" }, { 0x30b8, "zikatakana" }, { 0x05ae, "zinorhebrew" }, { 0x1e95, "zlinebelow" }, { 0xff5a, "zmonospace" }, { 0x305e, "zohiragana" }, { 0x30be, "zokatakana" }, { 0x24b5, "zparen" }, { 0x0290, "zretroflexhook" }, { 0x01b6, "zstroke" }, { 0x305a, "zuhiragana" }, { 0x30ba, "zukatakana" }, { 0x007b, "{" }, { 0x007c, "|" }, { 0x007d, "}" }, { 0x007e, "~" }, { 0, nullptr } }; // map ZapfDingbats names to Unicode static const struct NameToUnicodeTab nameToUnicodeZapfDingbatsTab[] = { { 0x275e, "a100" }, { 0x2761, "a101" }, { 0x2762, "a102" }, { 0x2763, "a103" }, { 0x2764, "a104" }, { 0x2710, "a105" }, { 0x2765, "a106" }, { 0x2766, "a107" }, { 0x2767, "a108" }, { 0x2660, "a109" }, { 0x2721, "a10" }, { 0x2665, "a110" }, { 0x2666, "a111" }, { 0x2663, "a112" }, { 0x2709, "a117" }, { 0x2708, "a118" }, { 0x2707, "a119" }, { 0x261b, "a11" }, { 0x2460, "a120" }, { 0x2461, "a121" }, { 0x2462, "a122" }, { 0x2463, "a123" }, { 0x2464, "a124" }, { 0x2465, "a125" }, { 0x2466, "a126" }, { 0x2467, "a127" }, { 0x2468, "a128" }, { 0x2469, "a129" }, { 0x261e, "a12" }, { 0x2776, "a130" }, { 0x2777, "a131" }, { 0x2778, "a132" }, { 0x2779, "a133" }, { 0x277a, "a134" }, { 0x277b, "a135" }, { 0x277c, "a136" }, { 0x277d, "a137" }, { 0x277e, "a138" }, { 0x277f, "a139" }, { 0x270c, "a13" }, { 0x2780, "a140" }, { 0x2781, "a141" }, { 0x2782, "a142" }, { 0x2783, "a143" }, { 0x2784, "a144" }, { 0x2785, "a145" }, { 0x2786, "a146" }, { 0x2787, "a147" }, { 0x2788, "a148" }, { 0x2789, "a149" }, { 0x270d, "a14" }, { 0x278a, "a150" }, { 0x278b, "a151" }, { 0x278c, "a152" }, { 0x278d, "a153" }, { 0x278e, "a154" }, { 0x278f, "a155" }, { 0x2790, "a156" }, { 0x2791, "a157" }, { 0x2792, "a158" }, { 0x2793, "a159" }, { 0x270e, "a15" }, { 0x2794, "a160" }, { 0x2192, "a161" }, { 0x27a3, "a162" }, { 0x2194, "a163" }, { 0x2195, "a164" }, { 0x2799, "a165" }, { 0x279b, "a166" }, { 0x279c, "a167" }, { 0x279d, "a168" }, { 0x279e, "a169" }, { 0x270f, "a16" }, { 0x279f, "a170" }, { 0x27a0, "a171" }, { 0x27a1, "a172" }, { 0x27a2, "a173" }, { 0x27a4, "a174" }, { 0x27a5, "a175" }, { 0x27a6, "a176" }, { 0x27a7, "a177" }, { 0x27a8, "a178" }, { 0x27a9, "a179" }, { 0x2711, "a17" }, { 0x27ab, "a180" }, { 0x27ad, "a181" }, { 0x27af, "a182" }, { 0x27b2, "a183" }, { 0x27b3, "a184" }, { 0x27b5, "a185" }, { 0x27b8, "a186" }, { 0x27ba, "a187" }, { 0x27bb, "a188" }, { 0x27bc, "a189" }, { 0x2712, "a18" }, { 0x27bd, "a190" }, { 0x27be, "a191" }, { 0x279a, "a192" }, { 0x27aa, "a193" }, { 0x27b6, "a194" }, { 0x27b9, "a195" }, { 0x2798, "a196" }, { 0x27b4, "a197" }, { 0x27b7, "a198" }, { 0x27ac, "a199" }, { 0x2713, "a19" }, { 0x2701, "a1" }, { 0x27ae, "a200" }, { 0x27b1, "a201" }, { 0x2703, "a202" }, { 0x2750, "a203" }, { 0x2752, "a204" }, { 0x276e, "a205" }, { 0x2770, "a206" }, { 0x2714, "a20" }, { 0x2715, "a21" }, { 0x2716, "a22" }, { 0x2717, "a23" }, { 0x2718, "a24" }, { 0x2719, "a25" }, { 0x271a, "a26" }, { 0x271b, "a27" }, { 0x271c, "a28" }, { 0x2722, "a29" }, { 0x2702, "a2" }, { 0x2723, "a30" }, { 0x2724, "a31" }, { 0x2725, "a32" }, { 0x2726, "a33" }, { 0x2727, "a34" }, { 0x2605, "a35" }, { 0x2729, "a36" }, { 0x272a, "a37" }, { 0x272b, "a38" }, { 0x272c, "a39" }, { 0x2704, "a3" }, { 0x272d, "a40" }, { 0x272e, "a41" }, { 0x272f, "a42" }, { 0x2730, "a43" }, { 0x2731, "a44" }, { 0x2732, "a45" }, { 0x2733, "a46" }, { 0x2734, "a47" }, { 0x2735, "a48" }, { 0x2736, "a49" }, { 0x260e, "a4" }, { 0x2737, "a50" }, { 0x2738, "a51" }, { 0x2739, "a52" }, { 0x273a, "a53" }, { 0x273b, "a54" }, { 0x273c, "a55" }, { 0x273d, "a56" }, { 0x273e, "a57" }, { 0x273f, "a58" }, { 0x2740, "a59" }, { 0x2706, "a5" }, { 0x2741, "a60" }, { 0x2742, "a61" }, { 0x2743, "a62" }, { 0x2744, "a63" }, { 0x2745, "a64" }, { 0x2746, "a65" }, { 0x2747, "a66" }, { 0x2748, "a67" }, { 0x2749, "a68" }, { 0x274a, "a69" }, { 0x271d, "a6" }, { 0x274b, "a70" }, { 0x25cf, "a71" }, { 0x274d, "a72" }, { 0x25a0, "a73" }, { 0x274f, "a74" }, { 0x2751, "a75" }, { 0x25b2, "a76" }, { 0x25bc, "a77" }, { 0x25c6, "a78" }, { 0x2756, "a79" }, { 0x271e, "a7" }, { 0x25d7, "a81" }, { 0x2758, "a82" }, { 0x2759, "a83" }, { 0x275a, "a84" }, { 0x276f, "a85" }, { 0x2771, "a86" }, { 0x2772, "a87" }, { 0x2773, "a88" }, { 0x2768, "a89" }, { 0x271f, "a8" }, { 0x2769, "a90" }, { 0x276c, "a91" }, { 0x276d, "a92" }, { 0x276a, "a93" }, { 0x276b, "a94" }, { 0x2774, "a95" }, { 0x2775, "a96" }, { 0x275b, "a97" }, { 0x275c, "a98" }, { 0x275d, "a99" }, { 0x2720, "a9" }, { 0, nullptr } }; poppler-24.02.0/poppler/Object.cc000066400000000000000000000123341455701731300165470ustar00rootroot00000000000000//======================================================================== // // Object.cc // // 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) 2008, 2010, 2012, 2017, 2019 Albert Astals Cid // Copyright (C) 2013 Adrian Johnson // Copyright (C) 2018 Adam Reichold // Copyright (C) 2020 Jakub Alba // // 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 #include #include "Object.h" #include "Array.h" #include "Dict.h" #include "Error.h" #include "Stream.h" #include "XRef.h" //------------------------------------------------------------------------ // Object //------------------------------------------------------------------------ static const char *objTypeNames[numObjTypes] = { "boolean", "integer", "real", "string", "name", "null", "array", "dictionary", "stream", "ref", "cmd", "error", "eof", "none", "integer64", "hexString", "dead" }; Object Object::copy() const { CHECK_NOT_DEAD; Object obj; std::memcpy(reinterpret_cast(&obj), this, sizeof(Object)); switch (type) { case objString: case objHexString: obj.string = string->copy(); break; case objName: case objCmd: obj.cString = copyString(cString); break; case objArray: array->incRef(); break; case objDict: dict->incRef(); break; case objStream: stream->incRef(); break; default: break; } return obj; } Object Object::deepCopy() const { CHECK_NOT_DEAD; Object obj; std::memcpy(reinterpret_cast(&obj), this, sizeof(Object)); switch (type) { case objString: case objHexString: obj.string = string->copy(); break; case objName: case objCmd: obj.cString = copyString(cString); break; case objArray: obj.array = array->deepCopy(); break; case objDict: obj.dict = dict->deepCopy(); break; case objStream: stream->incRef(); break; default: break; } return obj; } Object Object::fetch(XRef *xref, int recursion) const { CHECK_NOT_DEAD; return (type == objRef && xref) ? xref->fetch(ref, recursion) : copy(); } void Object::free() { switch (type) { case objString: case objHexString: delete string; break; case objName: case objCmd: gfree(cString); break; case objArray: if (!array->decRef()) { delete array; } break; case objDict: if (!dict->decRef()) { delete dict; } break; case objStream: if (!stream->decRef()) { delete stream; } break; default: break; } type = objNone; } const char *Object::getTypeName() const { return objTypeNames[type]; } void Object::print(FILE *f) const { int i; switch (type) { case objBool: fprintf(f, "%s", booln ? "true" : "false"); break; case objInt: fprintf(f, "%d", intg); break; case objReal: fprintf(f, "%g", real); break; case objString: fprintf(f, "("); fwrite(string->c_str(), 1, string->getLength(), f); fprintf(f, ")"); break; case objHexString: fprintf(f, "<"); for (i = 0; i < string->getLength(); i++) { fprintf(f, "%02x", string->getChar(i) & 0xff); } fprintf(f, ">"); break; case objName: fprintf(f, "/%s", cString); break; case objNull: fprintf(f, "null"); break; case objArray: fprintf(f, "["); for (i = 0; i < arrayGetLength(); ++i) { if (i > 0) { fprintf(f, " "); } const Object &obj = arrayGetNF(i); obj.print(f); } fprintf(f, "]"); break; case objDict: fprintf(f, "<<"); for (i = 0; i < dictGetLength(); ++i) { fprintf(f, " /%s ", dictGetKey(i)); const Object &obj = dictGetValNF(i); obj.print(f); } fprintf(f, " >>"); break; case objStream: fprintf(f, ""); break; case objRef: fprintf(f, "%d %d R", ref.num, ref.gen); break; case objCmd: fprintf(f, "%s", cString); break; case objError: fprintf(f, ""); break; case objEOF: fprintf(f, ""); break; case objNone: fprintf(f, ""); break; case objDead: fprintf(f, ""); break; case objInt64: fprintf(f, "%lld", int64g); break; } } poppler-24.02.0/poppler/Object.h000066400000000000000000000540651455701731300164200ustar00rootroot00000000000000//======================================================================== // // Object.h // // 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) 2007 Julien Rebetez // Copyright (C) 2008 Kees Cook // Copyright (C) 2008, 2010, 2017-2021, 2023 Albert Astals Cid // Copyright (C) 2009 Jakub Wilk // Copyright (C) 2012 Fabio D'Urso // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2013, 2017, 2018 Adrian Johnson // Copyright (C) 2013 Adrian Perez de Castro // Copyright (C) 2016, 2020 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 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 // // 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 OBJECT_H #define OBJECT_H #include #include #include #include #include #include "goo/gmem.h" #include "goo/GooString.h" #include "goo/GooLikely.h" #include "Error.h" #include "poppler_private_export.h" #define OBJECT_TYPE_CHECK(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}", \ type, wanted_type); \ abort(); \ } #define OBJECT_2TYPES_CHECK(wanted_type1, 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}", \ type, wanted_type1, wanted_type2); \ abort(); \ } #define OBJECT_3TYPES_CHECK(wanted_type1, wanted_type2, 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}", \ type, wanted_type1, wanted_type2, wanted_type3); \ abort(); \ } #define CHECK_NOT_DEAD \ if (unlikely(type == objDead)) { \ error(errInternal, 0, "Call to dead object"); \ abort(); \ } class XRef; class Array; class Dict; class Stream; //------------------------------------------------------------------------ // Ref //------------------------------------------------------------------------ struct Ref { int num; // object number int gen; // generation number static constexpr Ref INVALID() { return { -1, -1 }; }; }; inline bool operator==(const Ref lhs, const Ref rhs) noexcept { return lhs.num == rhs.num && lhs.gen == rhs.gen; } inline bool operator!=(const Ref lhs, const Ref rhs) noexcept { return lhs.num != rhs.num || lhs.gen != rhs.gen; } inline bool operator<(const Ref lhs, const Ref rhs) noexcept { if (lhs.num != rhs.num) { return lhs.num < rhs.num; } return lhs.gen < rhs.gen; } struct RefRecursionChecker { RefRecursionChecker() { } 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; } private: std::set alreadySeenRefs; }; namespace std { template<> struct hash { using argument_type = Ref; using result_type = size_t; result_type operator()(const argument_type ref) const noexcept { return std::hash {}(ref.num) ^ (std::hash {}(ref.gen) << 1); } }; } //------------------------------------------------------------------------ // object types //------------------------------------------------------------------------ enum ObjType { // simple objects objBool, // boolean objInt, // integer objReal, // real objString, // string objName, // name objNull, // null // complex objects objArray, // array objDict, // dictionary objStream, // stream objRef, // indirect reference // special objects objCmd, // command name objError, // error return from Lexer objEOF, // end of file return from Lexer objNone, // uninitialized object // poppler-only objects objInt64, // integer with at least 64-bits objHexString, // hex string objDead // and object after shallowCopy }; constexpr int numObjTypes = 17; // total number of object types //------------------------------------------------------------------------ // Object //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT Object { public: Object() : type(objNone) { } ~Object() { free(); } explicit Object(bool boolnA) { type = objBool; booln = boolnA; } explicit Object(int intgA) { type = objInt; intg = intgA; } explicit Object(ObjType typeA) { type = typeA; } explicit Object(double realA) { type = objReal; real = realA; } explicit Object(GooString *stringA) { assert(stringA); type = objString; string = stringA; } explicit Object(std::string &&stringA) { type = objString; string = new GooString(stringA); } Object(ObjType typeA, GooString *stringA) { assert(typeA == objHexString); assert(stringA); type = typeA; string = stringA; } Object(ObjType typeA, const char *stringA) { assert(typeA == objName || typeA == objCmd); assert(stringA); type = typeA; cString = copyString(stringA); } explicit Object(long long int64gA) { type = objInt64; int64g = int64gA; } explicit Object(Array *arrayA) { assert(arrayA); type = objArray; array = arrayA; } explicit Object(Dict *dictA) { assert(dictA); type = objDict; dict = dictA; } explicit Object(Stream *streamA) { assert(streamA); type = objStream; stream = streamA; } explicit Object(const Ref r) { type = objRef; ref = r; } template Object(T) = delete; Object(Object &&other) noexcept { std::memcpy(reinterpret_cast(this), &other, sizeof(Object)); other.type = objDead; } Object &operator=(Object &&other) noexcept { free(); std::memcpy(reinterpret_cast(this), &other, sizeof(Object)); other.type = objDead; return *this; } Object &operator=(const Object &other) = delete; Object(const Object &other) = delete; // Set object to null. void setToNull() { free(); type = objNull; } // Copies all object types except // objArray, objDict, objStream whose refcount is increased by 1 Object copy() const; // Deep copies all object types (recursively) // except objStream whose refcount is increased by 1 Object deepCopy() const; // If object is a Ref, fetch and return the referenced object. // Otherwise, return a copy of the object. Object fetch(XRef *xref, int recursion = 0) const; // Type checking. ObjType getType() const { CHECK_NOT_DEAD; return type; } bool isBool() const { CHECK_NOT_DEAD; return type == objBool; } bool isInt() const { CHECK_NOT_DEAD; return type == objInt; } bool isReal() const { CHECK_NOT_DEAD; return type == objReal; } bool isNum() const { CHECK_NOT_DEAD; return type == objInt || type == objReal || type == objInt64; } bool isString() const { CHECK_NOT_DEAD; return type == objString; } bool isHexString() const { CHECK_NOT_DEAD; return type == objHexString; } bool isName() const { CHECK_NOT_DEAD; return type == objName; } bool isNull() const { CHECK_NOT_DEAD; return type == objNull; } bool isArray() const { CHECK_NOT_DEAD; return type == objArray; } bool isDict() const { CHECK_NOT_DEAD; return type == objDict; } bool isStream() const { CHECK_NOT_DEAD; return type == objStream; } bool isRef() const { CHECK_NOT_DEAD; return type == objRef; } bool isCmd() const { CHECK_NOT_DEAD; return type == objCmd; } bool isError() const { CHECK_NOT_DEAD; return type == objError; } bool isEOF() const { CHECK_NOT_DEAD; return type == objEOF; } bool isNone() const { CHECK_NOT_DEAD; return type == objNone; } bool isInt64() const { CHECK_NOT_DEAD; return type == objInt64; } bool isIntOrInt64() const { CHECK_NOT_DEAD; return type == objInt || type == objInt64; } // Special type checking. bool isName(const char *nameA) const { return type == objName && !strcmp(cString, nameA); } bool isDict(const char *dictType) const; bool isCmd(const char *cmdA) const { return type == objCmd && !strcmp(cString, cmdA); } // Accessors. bool getBool() const { OBJECT_TYPE_CHECK(objBool); return booln; } int getInt() const { OBJECT_TYPE_CHECK(objInt); return intg; } double getReal() const { OBJECT_TYPE_CHECK(objReal); return real; } // Note: integers larger than 2^53 can not be exactly represented by a double. // Where the exact value of integers up to 2^63 is required, use isInt64()/getInt64(). double getNum() const { OBJECT_3TYPES_CHECK(objInt, objInt64, objReal); return type == objInt ? (double)intg : type == objInt64 ? (double)int64g : real; } double getNum(bool *ok) const { if (unlikely(type != objInt && type != objInt64 && type != objReal)) { *ok = false; return 0.; } return type == objInt ? (double)intg : type == objInt64 ? (double)int64g : real; } const GooString *getString() const { OBJECT_TYPE_CHECK(objString); return string; } const GooString *getHexString() const { OBJECT_TYPE_CHECK(objHexString); return string; } const char *getName() const { OBJECT_TYPE_CHECK(objName); return cString; } Array *getArray() const { OBJECT_TYPE_CHECK(objArray); return array; } Dict *getDict() const { OBJECT_TYPE_CHECK(objDict); return dict; } Stream *getStream() const { OBJECT_TYPE_CHECK(objStream); return stream; } Ref getRef() const { OBJECT_TYPE_CHECK(objRef); return ref; } int getRefNum() const { OBJECT_TYPE_CHECK(objRef); return ref.num; } int getRefGen() const { OBJECT_TYPE_CHECK(objRef); return ref.gen; } const char *getCmd() const { OBJECT_TYPE_CHECK(objCmd); return cString; } long long getInt64() const { OBJECT_TYPE_CHECK(objInt64); return int64g; } long long getIntOrInt64() const { OBJECT_2TYPES_CHECK(objInt, objInt64); return type == objInt ? intg : int64g; } // Array accessors. int arrayGetLength() const; void arrayAdd(Object &&elem); void arrayRemove(int i); Object arrayGet(int i, int recursion) const; const Object &arrayGetNF(int i) const; // Dict accessors. int dictGetLength() const; void dictAdd(char *key, Object &&val) = delete; void dictAdd(const char *key, Object &&val); void dictSet(const char *key, Object &&val); void dictRemove(const char *key); bool dictIs(const char *dictType) const; Object dictLookup(const char *key, int recursion = 0) const; const Object &dictLookupNF(const char *key) const; const char *dictGetKey(int i) const; Object dictGetVal(int i) const; const Object &dictGetValNF(int i) const; // Stream accessors. void streamReset(); void streamClose(); int streamGetChar(); int streamGetChars(int nChars, unsigned char *buffer); void streamSetPos(Goffset pos, int dir = 0); Dict *streamGetDict() const; // Output. const char *getTypeName() const; void print(FILE *f = stdout) const; double getNumWithDefaultValue(double defaultValue) const { if (unlikely(type != objInt && type != objInt64 && type != objReal)) { return defaultValue; } return type == objInt ? (double)intg : type == objInt64 ? (double)int64g : real; } bool getBoolWithDefaultValue(bool defaultValue) const { return (type == objBool) ? booln : defaultValue; } private: // Free object contents. void free(); ObjType type; // object type union { // value for each type: bool booln; // boolean int intg; // integer long long int64g; // 64-bit integer double real; // real GooString *string; // [hex] string char *cString; // name or command, depending on objType Array *array; // array Dict *dict; // dictionary Stream *stream; // stream Ref ref; // indirect reference }; }; //------------------------------------------------------------------------ // Array accessors. //------------------------------------------------------------------------ #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 poppler-24.02.0/poppler/OptionalContent.cc000066400000000000000000000262241455701731300204640ustar00rootroot00000000000000//======================================================================== // // OptionalContent.cc // // Copyright 2007 Brad Hards // Copyright 2008 Pino Toscano // Copyright 2008, 2010 Carlos Garcia Campos // Copyright 2008, 2010, 2011, 2017-2019 Albert Astals Cid // Copyright 2008 Mark Kaplan // Copyright 2018 Adam Reichold // Copyright 2019 Oliver Sander // // Released under the GPL (version 2, or later, at your option) // //======================================================================== #include #include "goo/gmem.h" #include "goo/GooString.h" #include "Error.h" #include "OptionalContent.h" // Max depth of nested visibility expressions. This is used to catch // infinite loops in the visibility expression object structure. #define visibilityExprRecursionLimit 50 // Max depth of nested display nodes. This is used to catch infinite // loops in the "Order" object structure. #define displayNodeRecursionLimit 50 //------------------------------------------------------------------------ OCGs::OCGs(Object *ocgObject, XRef *xref) : m_xref(xref) { // we need to parse the dictionary here, and build optionalContentGroups ok = true; 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; return; } // we now enumerate over the ocgList, and build up the optionalContentGroups list. for (int i = 0; i < ocgList.arrayGetLength(); ++i) { Object ocgDict = ocgList.arrayGet(i); if (!ocgDict.isDict()) { break; } auto thisOptionalContentGroup = std::make_unique(ocgDict.getDict()); const Object &ocgRef = ocgList.arrayGetNF(i); if (!ocgRef.isRef()) { break; } thisOptionalContentGroup->setRef(ocgRef.getRef()); // the default is ON - we change state later, depending on BaseState, ON and OFF thisOptionalContentGroup->setState(OptionalContentGroup::On); optionalContentGroups.emplace(ocgRef.getRef(), std::move(thisOptionalContentGroup)); } 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; return; } Object baseState = defaultOcgConfig.dictLookup("BaseState"); if (baseState.isName("OFF")) { for (auto &group : optionalContentGroups) { group.second->setState(OptionalContentGroup::Off); } } Object on = defaultOcgConfig.dictLookup("ON"); if (on.isArray()) { // ON is an optional element for (int i = 0; i < on.arrayGetLength(); ++i) { const Object &reference = on.arrayGetNF(i); if (!reference.isRef()) { // there can be null entries break; } OptionalContentGroup *group = findOcgByRef(reference.getRef()); if (!group) { error(errSyntaxWarning, -1, "Couldn't find group for reference"); break; } group->setState(OptionalContentGroup::On); } } Object off = defaultOcgConfig.dictLookup("OFF"); if (off.isArray()) { // OFF is an optional element for (int i = 0; i < off.arrayGetLength(); ++i) { const Object &reference = off.arrayGetNF(i); if (!reference.isRef()) { // there can be null entries break; } OptionalContentGroup *group = findOcgByRef(reference.getRef()); if (!group) { error(errSyntaxWarning, -1, "Couldn't find group for reference to set OFF"); break; } group->setState(OptionalContentGroup::Off); } } order = defaultOcgConfig.dictLookup("Order"); rbgroups = defaultOcgConfig.dictLookup("RBGroups"); } bool OCGs::hasOCGs() const { return !(optionalContentGroups.empty()); } OptionalContentGroup *OCGs::findOcgByRef(const Ref ref) { const auto ocg = optionalContentGroups.find(ref); return ocg != optionalContentGroups.end() ? ocg->second.get() : nullptr; } bool OCGs::optContentIsVisible(const Object *dictRef) { Dict *dict; bool result = true; if (dictRef->isNull()) { return result; } if (dictRef->isRef()) { OptionalContentGroup *oc = findOcgByRef(dictRef->getRef()); if (oc) { return oc->getState() == OptionalContentGroup::On; } } Object dictObj = dictRef->fetch(m_xref); if (!dictObj.isDict()) { error(errSyntaxWarning, -1, "Unexpected oc reference target: {0:d}", dictObj.getType()); return result; } dict = dictObj.getDict(); Object dictType = dict->lookup("Type"); if (dictType.isName("OCMD")) { Object ve = dict->lookup("VE"); if (ve.isArray()) { result = evalOCVisibilityExpr(&ve, 0); } else { const Object &ocg = dict->lookupNF("OCGs"); if (ocg.isArray()) { Object policy = dict->lookup("P"); if (policy.isName("AllOn")) { result = allOn(ocg.getArray()); } else if (policy.isName("AllOff")) { result = allOff(ocg.getArray()); } else if (policy.isName("AnyOff")) { result = anyOff(ocg.getArray()); } else if ((!policy.isName()) || (policy.isName("AnyOn"))) { // this is the default result = anyOn(ocg.getArray()); } } else if (ocg.isRef()) { OptionalContentGroup *oc = findOcgByRef(ocg.getRef()); if (oc && oc->getState() == OptionalContentGroup::Off) { result = false; } else { result = true; } } } } else if (dictType.isName("OCG") && dictRef->isRef()) { OptionalContentGroup *oc = findOcgByRef(dictRef->getRef()); if (oc && oc->getState() == OptionalContentGroup::Off) { result = false; } } return result; } bool OCGs::evalOCVisibilityExpr(const Object *expr, int recursion) { OptionalContentGroup *ocg; bool ret; if (recursion > visibilityExprRecursionLimit) { error(errSyntaxError, -1, "Loop detected in optional content visibility expression"); return true; } if (expr->isRef()) { if ((ocg = findOcgByRef(expr->getRef()))) { return ocg->getState() == OptionalContentGroup::On; } } Object expr2 = expr->fetch(m_xref); if (!expr2.isArray() || expr2.arrayGetLength() < 1) { error(errSyntaxError, -1, "Invalid optional content visibility expression"); return true; } Object op = expr2.arrayGet(0); if (op.isName("Not")) { if (expr2.arrayGetLength() == 2) { const Object &obj = expr2.arrayGetNF(1); ret = !evalOCVisibilityExpr(&obj, recursion + 1); } else { error(errSyntaxError, -1, "Invalid optional content visibility expression"); ret = true; } } else if (op.isName("And")) { ret = true; for (int i = 1; i < expr2.arrayGetLength() && ret; ++i) { const Object &obj = expr2.arrayGetNF(i); ret = evalOCVisibilityExpr(&obj, recursion + 1); } } else if (op.isName("Or")) { ret = false; for (int i = 1; i < expr2.arrayGetLength() && !ret; ++i) { const Object &obj = expr2.arrayGetNF(i); ret = evalOCVisibilityExpr(&obj, recursion + 1); } } else { error(errSyntaxError, -1, "Invalid optional content visibility expression"); ret = true; } return ret; } bool OCGs::allOn(Array *ocgArray) { for (int i = 0; i < ocgArray->getLength(); ++i) { const Object &ocgItem = ocgArray->getNF(i); if (ocgItem.isRef()) { OptionalContentGroup *oc = findOcgByRef(ocgItem.getRef()); if (oc && oc->getState() == OptionalContentGroup::Off) { return false; } } } return true; } bool OCGs::allOff(Array *ocgArray) { for (int i = 0; i < ocgArray->getLength(); ++i) { const Object &ocgItem = ocgArray->getNF(i); if (ocgItem.isRef()) { OptionalContentGroup *oc = findOcgByRef(ocgItem.getRef()); if (oc && oc->getState() == OptionalContentGroup::On) { return false; } } } return true; } bool OCGs::anyOn(Array *ocgArray) { for (int i = 0; i < ocgArray->getLength(); ++i) { const Object &ocgItem = ocgArray->getNF(i); if (ocgItem.isRef()) { OptionalContentGroup *oc = findOcgByRef(ocgItem.getRef()); if (oc && oc->getState() == OptionalContentGroup::On) { return true; } } } return false; } bool OCGs::anyOff(Array *ocgArray) { for (int i = 0; i < ocgArray->getLength(); ++i) { const Object &ocgItem = ocgArray->getNF(i); if (ocgItem.isRef()) { OptionalContentGroup *oc = findOcgByRef(ocgItem.getRef()); if (oc && oc->getState() == OptionalContentGroup::Off) { return true; } } } return false; } //------------------------------------------------------------------------ OptionalContentGroup::OptionalContentGroup(Dict *ocgDict) : m_name(nullptr) { Object ocgName = ocgDict->lookup("Name"); 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()); } viewState = printState = ocUsageUnset; Object obj1 = ocgDict->lookup("Usage"); if (obj1.isDict()) { Object obj2 = obj1.dictLookup("View"); if (obj2.isDict()) { Object obj3 = obj2.dictLookup("ViewState"); if (obj3.isName()) { if (obj3.isName("ON")) { viewState = ocUsageOn; } else { viewState = ocUsageOff; } } } obj2 = obj1.dictLookup("Print"); if (obj2.isDict()) { Object obj3 = obj2.dictLookup("PrintState"); if (obj3.isName()) { if (obj3.isName("ON")) { printState = ocUsageOn; } else { printState = ocUsageOff; } } } } } OptionalContentGroup::OptionalContentGroup(GooString *label) { m_name = label; m_state = On; } const GooString *OptionalContentGroup::getName() const { return m_name; } void OptionalContentGroup::setRef(const Ref ref) { m_ref = ref; } Ref OptionalContentGroup::getRef() const { return m_ref; } OptionalContentGroup::~OptionalContentGroup() { delete m_name; } poppler-24.02.0/poppler/OptionalContent.h000066400000000000000000000061311455701731300203210ustar00rootroot00000000000000//======================================================================== // // OptionalContent.h // // Copyright 2007 Brad Hards // Copyright 2008 Carlos Garcia Campos // Copyright 2013, 2018, 2019, 2021 Albert Astals Cid // Copyright 2018 Adam Reichold // Copyright 2019 Oliver Sander // // Released under the GPL (version 2, or later, at your option) // //======================================================================== #ifndef OPTIONALCONTENT_H #define OPTIONALCONTENT_H #include "Object.h" #include "CharTypes.h" #include "poppler_private_export.h" #include #include class GooString; class XRef; class OptionalContentGroup; //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT OCGs { public: OCGs(Object *ocgObject, XRef *xref); OCGs(const OCGs &) = delete; OCGs &operator=(const OCGs &) = delete; // Is OCGS valid? bool isOk() const { return ok; } bool hasOCGs() const; const std::unordered_map> &getOCGs() const { return optionalContentGroups; } OptionalContentGroup *findOcgByRef(const Ref ref); Array *getOrderArray() { return (order.isArray() && order.arrayGetLength() > 0) ? order.getArray() : nullptr; } Array *getRBGroupsArray() { return (rbgroups.isArray() && rbgroups.arrayGetLength()) ? rbgroups.getArray() : nullptr; } bool optContentIsVisible(const Object *dictRef); 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); std::unordered_map> optionalContentGroups; Object order; Object rbgroups; XRef *m_xref; }; //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT OptionalContentGroup { public: enum State { On, Off }; // Values from the optional content usage dictionary. enum UsageState { ocUsageOn, ocUsageOff, ocUsageUnset }; explicit OptionalContentGroup(Dict *dict); explicit OptionalContentGroup(GooString *label); ~OptionalContentGroup(); OptionalContentGroup(const OptionalContentGroup &) = delete; OptionalContentGroup &operator=(const OptionalContentGroup &) = delete; const GooString *getName() const; Ref getRef() const; void setRef(const Ref ref); State getState() const { return m_state; }; void setState(State state) { m_state = state; }; UsageState getViewState() const { return viewState; } UsageState getPrintState() const { return printState; } private: GooString *m_name; Ref m_ref; State m_state; UsageState viewState; // suggested state when viewing UsageState printState; // suggested state when printing }; //------------------------------------------------------------------------ #endif poppler-24.02.0/poppler/Outline.cc000066400000000000000000000440131455701731300167570ustar00rootroot00000000000000//======================================================================== // // Outline.cc // // Copyright 2002-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) 2005 Marco Pesenti Gritti // 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 // 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) 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 // //======================================================================== #include #include "goo/gmem.h" #include "goo/GooString.h" #include "PDFDoc.h" #include "XRef.h" #include "Link.h" #include "PDFDocEncoding.h" #include "Outline.h" #include "UTF.h" //------------------------------------------------------------------------ Outline::Outline(Object *outlineObjA, XRef *xrefA, PDFDoc *docA) { outlineObj = outlineObjA; xref = xrefA; doc = docA; items = nullptr; if (!outlineObj->isDict()) { return; } const Object &first = outlineObj->dictLookupNF("First"); items = OutlineItem::readItemList(nullptr, &first, xref, doc); } Outline::~Outline() { if (items) { for (auto entry : *items) { delete entry; } delete items; } } static void insertChildHelper(const std::string &itemTitle, int destPageNum, unsigned int pos, Ref parentObjRef, PDFDoc *doc, XRef *xref, std::vector &items) { std::vector::const_iterator it; if (pos >= items.size()) { it = items.end(); } else { it = items.begin() + pos; } Array *a = new Array(xref); Ref *pageRef = doc->getCatalog()->getPageRef(destPageNum); if (pageRef != nullptr) { a->add(Object(*pageRef)); } else { // if the page obj doesn't exist put the page number // PDF32000-2008 12.3.2.2 Para 2 // as if it's a "Remote-Go-To Actions" // it's not strictly valid, but most viewers seem // to handle it without crashing // alternately, could put 0, or omit it a->add(Object(destPageNum - 1)); } a->add(Object(objName, "Fit")); Object outlineItem = Object(new Dict(xref)); GooString *g = new GooString(itemTitle); outlineItem.dictSet("Title", Object(g)); outlineItem.dictSet("Dest", Object(a)); outlineItem.dictSet("Count", Object(1)); outlineItem.dictAdd("Parent", Object(parentObjRef)); // add one to the main outline Object's count Object parentObj = xref->fetch(parentObjRef); int parentCount = parentObj.dictLookup("Count").getInt(); parentObj.dictSet("Count", Object(parentCount + 1)); xref->setModifiedObject(&parentObj, parentObjRef); Object prevItemObject; Object nextItemObject; Ref outlineItemRef = xref->addIndirectObject(outlineItem); // the next two statements fix up the parent object // for clarity we separate this out if (it == items.begin()) { // we will be the first item in the list // fix our parent parentObj.dictSet("First", Object(outlineItemRef)); } if (it == items.end()) { // we will be the last item on the list // fix up our parent parentObj.dictSet("Last", Object(outlineItemRef)); } if (it == items.end()) { if (!items.empty()) { // insert at the end, we handle this separately prevItemObject = xref->fetch((*(it - 1))->getRef()); prevItemObject.dictSet("Next", Object(outlineItemRef)); outlineItem.dictSet("Prev", Object((*(it - 1))->getRef())); xref->setModifiedObject(&prevItemObject, (*(it - 1))->getRef()); } } else { nextItemObject = xref->fetch((*it)->getRef()); nextItemObject.dictSet("Prev", Object(outlineItemRef)); xref->setModifiedObject(&nextItemObject, (*it)->getRef()); outlineItem.dictSet("Next", Object((*(it))->getRef())); if (it != items.begin()) { prevItemObject = xref->fetch((*(it - 1))->getRef()); prevItemObject.dictSet("Next", Object(outlineItemRef)); outlineItem.dictSet("Prev", Object((*(it - 1))->getRef())); xref->setModifiedObject(&prevItemObject, (*(it - 1))->getRef()); } } OutlineItem *item = new OutlineItem(outlineItem.getDict(), outlineItemRef, nullptr, xref, doc); items.insert(it, item); } void Outline::insertChild(const std::string &itemTitle, int destPageNum, unsigned int pos) { Ref outlineObjRef = xref->getCatalog().dictLookupNF("Outlines").getRef(); insertChildHelper(itemTitle, destPageNum, pos, outlineObjRef, doc, xref, *items); } // ref is a valid reference to a list // walk the list and free any children // returns the number items deleted (just in case) static int recursiveRemoveList(Ref ref, XRef *xref) { int count = 0; bool done = false; Ref nextRef; Object tempObj; while (!done) { tempObj = xref->fetch(ref); if (!tempObj.isDict()) { // something horrible has happened break; } const Object &firstRef = tempObj.dictLookupNF("First"); if (firstRef.isRef()) { count += recursiveRemoveList(firstRef.getRef(), xref); } const Object &nextObjRef = tempObj.dictLookupNF("Next"); if (nextObjRef.isRef()) { nextRef = nextObjRef.getRef(); } else { done = true; } xref->removeIndirectObject(ref); count++; ref = nextRef; } return count; } static void removeChildHelper(unsigned int pos, PDFDoc *doc, XRef *xref, std::vector &items) { std::vector::const_iterator it; if (pos >= items.size()) { // position is out of range, do nothing return; } else { it = items.begin() + pos; } // relink around this node Object itemObject = xref->fetch((*it)->getRef()); Object parentObj = itemObject.dictLookup("Parent"); Object prevItemObject = itemObject.dictLookup("Prev"); Object nextItemObject = itemObject.dictLookup("Next"); // delete 1 from the parent Count if it's positive Object countObj = parentObj.dictLookup("Count"); int count = countObj.getInt(); if (count > 0) { count--; parentObj.dictSet("Count", Object(count)); xref->setModifiedObject(&parentObj, itemObject.dictLookupNF("Parent").getRef()); } if (!prevItemObject.isNull() && !nextItemObject.isNull()) { // deletion is in the middle prevItemObject.dictSet("Next", Object((*(it + 1))->getRef())); xref->setModifiedObject(&prevItemObject, (*(it - 1))->getRef()); nextItemObject.dictSet("Prev", Object((*(it - 1))->getRef())); xref->setModifiedObject(&nextItemObject, (*(it + 1))->getRef()); } else if (prevItemObject.isNull() && nextItemObject.isNull()) { // deletion is only child parentObj.dictRemove("First"); parentObj.dictRemove("Last"); xref->setModifiedObject(&parentObj, itemObject.dictLookupNF("Parent").getRef()); } else if (prevItemObject.isNull()) { // deletion at the front parentObj.dictSet("First", Object((*(it + 1))->getRef())); xref->setModifiedObject(&parentObj, itemObject.dictLookupNF("Parent").getRef()); nextItemObject.dictRemove("Prev"); xref->setModifiedObject(&nextItemObject, (*(it + 1))->getRef()); } else { // deletion at the end parentObj.dictSet("Last", Object((*(it - 1))->getRef())); xref->setModifiedObject(&parentObj, itemObject.dictLookupNF("Parent").getRef()); prevItemObject.dictRemove("Next"); xref->setModifiedObject(&prevItemObject, (*(it - 1))->getRef()); } // free any children const Object &firstRef = itemObject.dictLookupNF("First"); if (firstRef.isRef()) { recursiveRemoveList(firstRef.getRef(), xref); } // free the pdf objects and the representation xref->removeIndirectObject((*it)->getRef()); OutlineItem *oi = *it; items.erase(it); // deletion of the OutlineItem will delete all child // outline items in its destructor delete oi; } void Outline::removeChild(unsigned int pos) { removeChildHelper(pos, doc, xref, *items); } //------------------------------------------------------------------------ int Outline::addOutlineTreeNodeList(const std::vector &nodeList, Ref &parentRef, Ref &firstRef, Ref &lastRef) { firstRef = Ref::INVALID(); lastRef = Ref::INVALID(); if (nodeList.empty()) { return 0; } int itemCount = 0; Ref prevNodeRef = Ref::INVALID(); for (auto &node : nodeList) { Array *a = new Array(doc->getXRef()); Ref *pageRef = doc->getCatalog()->getPageRef(node.destPageNum); if (pageRef != nullptr) { a->add(Object(*pageRef)); } else { // if the page obj doesn't exist put the page number // PDF32000-2008 12.3.2.2 Para 2 // as if it's a "Remote-Go-To Actions" // it's not strictly valid, but most viewers seem // to handle it without crashing // alternately, could put 0, or omit it a->add(Object(node.destPageNum - 1)); } a->add(Object(objName, "Fit")); Object outlineItem = Object(new Dict(doc->getXRef())); Ref outlineItemRef = doc->getXRef()->addIndirectObject(outlineItem); if (firstRef == Ref::INVALID()) { firstRef = outlineItemRef; } lastRef = outlineItemRef; GooString *g = new GooString(node.title); outlineItem.dictSet("Title", Object(g)); outlineItem.dictSet("Dest", Object(a)); itemCount++; if (prevNodeRef != Ref::INVALID()) { outlineItem.dictSet("Prev", Object(prevNodeRef)); // maybe easier way to fix up the previous object Object prevOutlineItem = xref->fetch(prevNodeRef); prevOutlineItem.dictSet("Next", Object(outlineItemRef)); xref->setModifiedObject(&prevOutlineItem, prevNodeRef); } prevNodeRef = outlineItemRef; Ref firstChildRef; Ref lastChildRef; itemCount += addOutlineTreeNodeList(node.children, outlineItemRef, firstChildRef, lastChildRef); if (firstChildRef != Ref::INVALID()) { outlineItem.dictSet("First", Object(firstChildRef)); outlineItem.dictSet("Last", Object(lastChildRef)); } outlineItem.dictSet("Count", Object(itemCount)); outlineItem.dictAdd("Parent", Object(parentRef)); } return itemCount; } /* insert an outline into a PDF outline->setOutline({ {"page 1", 1, { { "1.1", 1, {} } } }, {"page 2", 2, {} }, {"page 3", 3, {} }, {"page 4", 4,{ { "4.1", 4, {} }, { "4.2", 4, {} }, }, } }); */ void Outline::setOutline(const std::vector &nodeList) { // check if outlineObj is an object, if it's not make sure it exists if (!outlineObj->isDict()) { outlineObj = doc->getCatalog()->getCreateOutline(); // make sure it was created if (!outlineObj->isDict()) { return; } } Ref outlineObjRef = xref->getCatalog().dictLookupNF("Outlines").getRef(); Ref firstChildRef; Ref lastChildRef; // free any OutlineItem objects that will be replaced const Object &firstChildRefObj = outlineObj->dictLookupNF("First"); if (firstChildRefObj.isRef()) { recursiveRemoveList(firstChildRefObj.getRef(), xref); } const int count = addOutlineTreeNodeList(nodeList, outlineObjRef, firstChildRef, lastChildRef); // modify the parent Outlines dict if (firstChildRef != Ref::INVALID()) { outlineObj->dictSet("First", Object(firstChildRef)); outlineObj->dictSet("Last", Object(lastChildRef)); } else { // nothing was inserted into the outline, so just remove the // child references in the top-level outline outlineObj->dictRemove("First"); outlineObj->dictRemove("Last"); } outlineObj->dictSet("Count", Object(count)); xref->setModifiedObject(outlineObj, outlineObjRef); // reload the outline object from the xrefs if (items) { for (auto entry : *items) { delete entry; } delete items; } const Object &first = outlineObj->dictLookupNF("First"); // we probably want to allow readItemList to create an empty list // but for now just check and do it ourselves here if (first.isRef()) { items = OutlineItem::readItemList(nullptr, &first, xref, doc); } else { items = new std::vector(); } } //------------------------------------------------------------------------ OutlineItem::OutlineItem(const Dict *dict, Ref refA, OutlineItem *parentA, XRef *xrefA, PDFDoc *docA) { Object obj1; ref = refA; parent = parentA; xref = xrefA; doc = docA; kids = nullptr; obj1 = dict->lookup("Title"); if (obj1.isString()) { const GooString *s = obj1.getString(); title = TextStringToUCS4(s->toStr()); } obj1 = dict->lookup("Dest"); if (!obj1.isNull()) { action = LinkAction::parseDest(&obj1); } else { obj1 = dict->lookup("A"); if (!obj1.isNull()) { action = LinkAction::parseAction(&obj1); } } startsOpen = false; obj1 = dict->lookup("Count"); if (obj1.isInt()) { if (obj1.getInt() > 0) { startsOpen = true; } } } OutlineItem::~OutlineItem() { if (kids) { for (auto entry : *kids) { delete entry; } delete kids; kids = nullptr; } } std::vector *OutlineItem::readItemList(OutlineItem *parent, const Object *firstItemRef, XRef *xrefA, PDFDoc *docA) { auto items = new std::vector(); // could be a hash (unordered_map) too for better avg case check // small number of objects expected, likely doesn't matter std::set alreadyRead; OutlineItem *parentO = parent; while (parentO) { alreadyRead.insert(parentO->getRef()); parentO = parentO->parent; } Object tempObj = firstItemRef->copy(); while (tempObj.isRef() && (tempObj.getRefNum() >= 0) && (tempObj.getRefNum() < xrefA->getNumObjects()) && alreadyRead.find(tempObj.getRef()) == alreadyRead.end()) { Object obj = tempObj.fetch(xrefA); if (!obj.isDict()) { break; } alreadyRead.insert(tempObj.getRef()); OutlineItem *item = new OutlineItem(obj.getDict(), tempObj.getRef(), parent, xrefA, docA); items->push_back(item); tempObj = obj.dictLookupNF("Next").copy(); } return items; } void OutlineItem::open() { if (!kids) { Object itemDict = xref->fetch(ref); 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) { Object dict = xref->fetch(ref); GooString *g = new GooString(titleA); title = TextStringToUCS4(g->toStr()); dict.dictSet("Title", Object(g)); xref->setModifiedObject(&dict, ref); } bool OutlineItem::setPageDest(int i) { Object dict = xref->fetch(ref); Object obj1; if (i < 1) { return false; } obj1 = dict.dictLookup("Dest"); if (!obj1.isNull()) { int arrayLength = obj1.arrayGetLength(); for (int index = 0; index < arrayLength; index++) { obj1.arrayRemove(0); } obj1.arrayAdd(Object(i - 1)); obj1.arrayAdd(Object(objName, "Fit")); // unique_ptr will destroy previous on assignment action = LinkAction::parseDest(&obj1); } else { obj1 = dict.dictLookup("A"); if (!obj1.isNull()) { // RM 20210505 Implement } else { } return false; } xref->setModifiedObject(&dict, ref); return true; } void OutlineItem::insertChild(const std::string &itemTitle, int destPageNum, unsigned int pos) { open(); insertChildHelper(itemTitle, destPageNum, pos, ref, doc, xref, *kids); } void OutlineItem::removeChild(unsigned int pos) { open(); removeChildHelper(pos, doc, xref, *kids); } void OutlineItem::setStartsOpen(bool value) { startsOpen = value; Object dict = xref->fetch(ref); Object obj1 = dict.dictLookup("Count"); if (obj1.isInt()) { const int count = obj1.getInt(); if ((count > 0 && !value) || (count < 0 && value)) { // states requires change of sign dict.dictSet("Count", Object(-count)); xref->setModifiedObject(&dict, ref); } } } bool OutlineItem::hasKids() { open(); return !kids->empty(); } const std::vector *OutlineItem::getKids() { open(); if (!kids || kids->empty()) { return nullptr; } else { return kids; } } poppler-24.02.0/poppler/Outline.h000066400000000000000000000074451455701731300166310ustar00rootroot00000000000000//======================================================================== // // Outline.h // // Copyright 2002-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) 2005 Marco Pesenti Gritti // 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 // //======================================================================== #ifndef OUTLINE_H #define OUTLINE_H #include #include #include "Object.h" #include "CharTypes.h" #include "poppler_private_export.h" class PDFDoc; class GooString; class XRef; class LinkAction; class OutlineItem; //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT Outline { PDFDoc *doc; XRef *xref; Object *outlineObj; // outline dict in catalog public: Outline(Object *outlineObj, XRef *xref, PDFDoc *doc); ~Outline(); Outline(const Outline &) = delete; Outline &operator=(const Outline &) = delete; const std::vector *getItems() const { if (!items || items->empty()) { return nullptr; } else { return items; } } struct OutlineTreeNode { std::string title; int destPageNum; std::vector children; }; // insert/remove child don't propagate changes to 'Count' up the entire // tree void setOutline(const std::vector &nodeList); void insertChild(const std::string &itemTitle, int destPageNum, unsigned int pos); void removeChild(unsigned int pos); private: std::vector *items; // nullptr if document has no outline int addOutlineTreeNodeList(const std::vector &nodeList, Ref &parentRef, Ref &firstRef, Ref &lastRef); }; //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT OutlineItem { friend Outline; public: OutlineItem(const Dict *dict, Ref refA, OutlineItem *parentA, XRef *xrefA, PDFDoc *docA); ~OutlineItem(); OutlineItem(const OutlineItem &) = delete; OutlineItem &operator=(const OutlineItem &) = delete; static std::vector *readItemList(OutlineItem *parent, const Object *firstItemRef, XRef *xrefA, PDFDoc *docA); const std::vector &getTitle() const { return title; } void setTitle(const std::string &titleA); bool setPageDest(int i); // OutlineItem keeps the ownership of the action const LinkAction *getAction() const { return action.get(); } void setStartsOpen(bool value); bool isOpen() const { return startsOpen; } bool hasKids(); void open(); const std::vector *getKids(); int getRefNum() const { return ref.num; } Ref getRef() const { return ref; } void insertChild(const std::string &itemTitle, int destPageNum, unsigned int pos); void removeChild(unsigned int pos); private: Ref ref; OutlineItem *parent; PDFDoc *doc; XRef *xref; std::vector title; std::unique_ptr action; bool startsOpen; std::vector *kids; // nullptr if this item is closed or has no kids }; #endif poppler-24.02.0/poppler/OutputDev.cc000066400000000000000000000130171455701731300172770ustar00rootroot00000000000000//======================================================================== // // OutputDev.cc // // 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) 2005 Jonathan Blandford // Copyright (C) 2006 Thorkild Stray // Copyright (C) 2007, 2017 Adrian Johnson // Copyright (C) 2009 Carlos Garcia Campos // Copyright (C) 2009, 2012, 2013, 2018, 2019 Albert Astals Cid // Copyright (C) 2012 Thomas Freitag // Copyright (C) 2018 Adam Reichold // // 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 #include #include "Object.h" #include "Stream.h" #include "GfxState.h" #include "OutputDev.h" //------------------------------------------------------------------------ // OutputDev //------------------------------------------------------------------------ OutputDev::OutputDev() #ifdef USE_CMS : iccColorSpaceCache(5) #endif { } OutputDev::~OutputDev() = default; void OutputDev::setDefaultCTM(const double *ctm) { int i; double det; for (i = 0; i < 6; ++i) { defCTM[i] = ctm[i]; } det = 1 / (defCTM[0] * defCTM[3] - defCTM[1] * defCTM[2]); defICTM[0] = defCTM[3] * det; defICTM[1] = -defCTM[1] * det; defICTM[2] = -defCTM[2] * det; defICTM[3] = defCTM[0] * det; defICTM[4] = (defCTM[2] * defCTM[5] - defCTM[3] * defCTM[4]) * det; defICTM[5] = (defCTM[1] * defCTM[4] - defCTM[0] * defCTM[5]) * det; } void OutputDev::cvtDevToUser(double dx, double dy, double *ux, double *uy) { *ux = defICTM[0] * dx + defICTM[2] * dy + defICTM[4]; *uy = defICTM[1] * dx + defICTM[3] * dy + defICTM[5]; } void OutputDev::cvtUserToDev(double ux, double uy, int *dx, int *dy) { *dx = (int)(defCTM[0] * ux + defCTM[2] * uy + defCTM[4] + 0.5); *dy = (int)(defCTM[1] * ux + defCTM[3] * uy + defCTM[5] + 0.5); } void OutputDev::updateAll(GfxState *state) { updateLineDash(state); updateFlatness(state); updateLineJoin(state); updateLineCap(state); updateMiterLimit(state); updateLineWidth(state); updateStrokeAdjust(state); updateFillColorSpace(state); updateFillColor(state); updateStrokeColorSpace(state); updateStrokeColor(state); updateBlendMode(state); updateFillOpacity(state); updateStrokeOpacity(state); updateFillOverprint(state); updateStrokeOverprint(state); updateTransfer(state); updateFont(state); } bool OutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, const Unicode *u, int uLen) { return false; } void OutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg) { int i, j; if (inlineImg) { str->reset(); j = height * ((width + 7) / 8); for (i = 0; i < j; ++i) { str->getChar(); } str->close(); } } void OutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool inlineImg, double *baseMatrix) { drawImageMask(state, ref, str, width, height, invert, false, inlineImg); } void OutputDev::unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix) { return; } 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(); j = height * ((width * colorMap->getNumPixelComps() * colorMap->getBits() + 7) / 8); for (i = 0; i < j; ++i) { str->getChar(); } str->close(); } } void OutputDev::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) { drawImage(state, ref, str, width, height, colorMap, interpolate, nullptr, false); } void OutputDev::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) { drawImage(state, ref, str, width, height, colorMap, interpolate, nullptr, false); } void OutputDev::endMarkedContent(GfxState *state) { } void OutputDev::beginMarkedContent(const char *name, Dict *properties) { } void OutputDev::markPoint(const char *name) { } void OutputDev::markPoint(const char *name, Dict *properties) { } #ifdef OPI_SUPPORT void OutputDev::opiBegin(GfxState *state, Dict *opiDict) { } void OutputDev::opiEnd(GfxState *state, Dict *opiDict) { } #endif void OutputDev::startProfile() { profileHash = std::make_unique>(); } std::unique_ptr> OutputDev::endProfile() { return std::move(profileHash); } poppler-24.02.0/poppler/OutputDev.h000066400000000000000000000440661455701731300171510ustar00rootroot00000000000000//======================================================================== // // OutputDev.h // // 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) 2005 Jonathan Blandford // Copyright (C) 2006 Thorkild Stray // Copyright (C) 2007 Jeff Muizelaar // 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) 2010 Christian Feuersänger // Copyright (C) 2012 Fabio D'Urso // Copyright (C) 2012 William Bader // Copyright (C) 2017, 2018, 2020 Oliver Sander // 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 // // 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 OUTPUTDEV_H #define OUTPUTDEV_H #include "poppler-config.h" #include "poppler_private_export.h" #include "CharTypes.h" #include "Object.h" #include "PopplerCache.h" #include "ProfileData.h" #include "GfxState.h" #include #include #include class Annot; class Dict; class GooString; class Gfx; class Stream; class Links; class AnnotLink; class Catalog; class Page; class Function; //------------------------------------------------------------------------ // OutputDev //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT OutputDev { public: // Constructor. OutputDev(); // Destructor. virtual ~OutputDev(); //----- get info about output device // Does this device use upside-down coordinates? // (Upside-down means (0,0) is the top left corner of the page.) virtual bool upsideDown() = 0; // Does this device use drawChar() or drawString()? virtual bool useDrawChar() = 0; // Does this device use tilingPatternFill()? If this returns false, // tiling pattern fills will be reduced to a series of other drawing // operations. virtual bool useTilingPatternFill() { return false; } // Does this device support specific shading types? // see gouraudTriangleShadedFill() and patchMeshShadedFill() virtual bool useShadedFills(int type) { return false; } // Does this device use FillColorStop()? virtual bool useFillColorStop() { return false; } // Does this device use drawForm()? If this returns false, // form-type XObjects will be interpreted (i.e., unrolled). virtual bool useDrawForm() { return false; } // Does this device use beginType3Char/endType3Char? Otherwise, // text in Type 3 fonts will be drawn with drawChar/drawString. virtual bool interpretType3Chars() = 0; // Does this device need non-text content? virtual bool needNonText() { return true; } // Does this device require incCharCount to be called for text on // non-shown layers? virtual bool needCharCount() { return false; } // Does this device need to clip pages to the crop box even when the // box is the crop box? virtual bool needClipToCropBox() { return false; } //----- initialization and control // Set default transform matrix. virtual void setDefaultCTM(const double *ctm); // Check to see if a page slice should be displayed. If this // returns false, the page display is aborted. Typically, an // OutputDev will use some alternate means to display the page // before returning false. virtual bool checkPageSlice(Page *page, double hDPI, double vDPI, int rotate, bool useMediaBox, bool crop, int sliceX, int sliceY, int sliceW, int sliceH, bool printing, bool (*abortCheckCbk)(void *data) = nullptr, void *abortCheckCbkData = nullptr, bool (*annotDisplayDecideCbk)(Annot *annot, void *user_data) = nullptr, void *annotDisplayDecideCbkData = nullptr) { return true; } // Start a page. virtual void startPage(int pageNum, GfxState *state, XRef *xref) { } // End a page. virtual void endPage() { } // Dump page contents to display. virtual void dump() { } virtual void initGfxState(GfxState *state) { #ifdef USE_CMS state->setDisplayProfile(displayprofile); auto invalidref = Ref::INVALID(); if (defaultGrayProfile) { auto cs = new GfxICCBasedColorSpace(1, new GfxDeviceGrayColorSpace(), &invalidref); cs->setProfile(defaultGrayProfile); cs->buildTransforms(state); // needs to happen after state->setDisplayProfile has been called state->setDefaultGrayColorSpace(cs); } if (defaultRGBProfile) { auto cs = new GfxICCBasedColorSpace(3, new GfxDeviceRGBColorSpace(), &invalidref); cs->setProfile(defaultRGBProfile); cs->buildTransforms(state); // needs to happen after state->setDisplayProfile has been called state->setDefaultRGBColorSpace(cs); } if (defaultCMYKProfile) { auto cs = new GfxICCBasedColorSpace(4, new GfxDeviceCMYKColorSpace(), &invalidref); cs->setProfile(defaultCMYKProfile); cs->buildTransforms(state); // needs to happen after state->setDisplayProfile has been called state->setDefaultCMYKColorSpace(cs); } #endif } //----- coordinate conversion // Convert between device and user coordinates. 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; } //----- save/restore graphics state virtual void saveState(GfxState * /*state*/) { } virtual void restoreState(GfxState * /*state*/) { } //----- update graphics state virtual void updateAll(GfxState *state); // Update the Current Transformation Matrix (CTM), i.e., the new matrix // given in m11, ..., m32 is combined with the current value of the CTM. // At the same time, when this method is called, state->getCTM() already // contains the correct new CTM, so one may as well replace the // CTM of the renderer with that. virtual void updateCTM(GfxState * /*state*/, double /*m11*/, double /*m12*/, double /*m21*/, double /*m22*/, double /*m31*/, double /*m32*/) { } virtual void updateLineDash(GfxState * /*state*/) { } virtual void updateFlatness(GfxState * /*state*/) { } virtual void updateLineJoin(GfxState * /*state*/) { } virtual void updateLineCap(GfxState * /*state*/) { } virtual void updateMiterLimit(GfxState * /*state*/) { } virtual void updateLineWidth(GfxState * /*state*/) { } virtual void updateStrokeAdjust(GfxState * /*state*/) { } virtual void updateAlphaIsShape(GfxState * /*state*/) { } virtual void updateTextKnockout(GfxState * /*state*/) { } virtual void updateFillColorSpace(GfxState * /*state*/) { } virtual void updateStrokeColorSpace(GfxState * /*state*/) { } virtual void updateFillColor(GfxState * /*state*/) { } virtual void updateStrokeColor(GfxState * /*state*/) { } virtual void updateBlendMode(GfxState * /*state*/) { } virtual void updateFillOpacity(GfxState * /*state*/) { } virtual void updateStrokeOpacity(GfxState * /*state*/) { } virtual void updatePatternOpacity(GfxState * /*state*/) { } virtual void clearPatternOpacity(GfxState * /*state*/) { } virtual void updateFillOverprint(GfxState * /*state*/) { } virtual void updateStrokeOverprint(GfxState * /*state*/) { } virtual void updateOverprintMode(GfxState * /*state*/) { } virtual void updateTransfer(GfxState * /*state*/) { } virtual void updateFillColorStop(GfxState * /*state*/, double /*offset*/) { } //----- update text state virtual void updateFont(GfxState * /*state*/) { } virtual void updateTextMat(GfxState * /*state*/) { } virtual void updateCharSpace(GfxState * /*state*/) { } virtual void updateRender(GfxState * /*state*/) { } virtual void updateRise(GfxState * /*state*/) { } virtual void updateWordSpace(GfxState * /*state*/) { } virtual void updateHorizScaling(GfxState * /*state*/) { } virtual void updateTextPos(GfxState * /*state*/) { } virtual void updateTextShift(GfxState * /*state*/, double /*shift*/) { } virtual void saveTextPos(GfxState * /*state*/) { } virtual void restoreTextPos(GfxState * /*state*/) { } //----- path painting virtual void stroke(GfxState * /*state*/) { } virtual void fill(GfxState * /*state*/) { } virtual void eoFill(GfxState * /*state*/) { } virtual 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*/) { 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 // Update the clipping path. The new path is the intersection of the old path // with the path given in 'state'. // Additionally, set the clipping mode to the 'nonzero winding number rule'. // That is, a point is inside the clipping region if its winding number // with respect to the clipping path is nonzero. virtual void clip(GfxState * /*state*/) { } // Update the clipping path. The new path is the intersection of the old path // with the path given in 'state'. // Additionally, set the clipping mode to the 'even-odd rule'. That is, a point is // inside the clipping region if a ray from it to infinity will cross the clipping // path an odd number of times (disregarding the path orientation). virtual void eoClip(GfxState * /*state*/) { } // Update the clipping path. Unlike for the previous two methods, the clipping region // is not the region surrounded by the path in 'state', but rather the path itself, // rendered with the current pen settings. virtual void clipToStrokePath(GfxState * /*state*/) { } //----- text drawing virtual void beginStringOp(GfxState * /*state*/) { } virtual void endStringOp(GfxState * /*state*/) { } virtual void beginString(GfxState * /*state*/, const GooString * /*s*/) { } virtual void endString(GfxState * /*state*/) { } // Draw one glyph at a specified position // // Arguments are: // CharCode code: This is the character code in the content stream. It needs to be mapped back to a glyph index. // int nBytes: The text strings in the content stream can consists of either 8-bit or 16-bit // character codes depending on the font. nBytes is the number of bytes in the character code. // Unicode *u: The UCS-4 mapping used for text extraction (TextOutputDev). // int uLen: The number of unicode entries in u. Usually '1', for a single character, // but it may also have larger values, for example for ligatures. virtual void drawChar(GfxState * /*state*/, double /*x*/, double /*y*/, double /*dx*/, double /*dy*/, double /*originX*/, double /*originY*/, CharCode /*code*/, int /*nBytes*/, const Unicode * /*u*/, int /*uLen*/) { } virtual void drawString(GfxState * /*state*/, const GooString * /*s*/) { } virtual bool beginType3Char(GfxState * /*state*/, double /*x*/, double /*y*/, double /*dx*/, double /*dy*/, CharCode /*code*/, const Unicode * /*u*/, int /*uLen*/); virtual void endType3Char(GfxState * /*state*/) { } virtual void beginTextObject(GfxState * /*state*/) { } virtual void endTextObject(GfxState * /*state*/) { } virtual void incCharCount(int /*nChars*/) { } virtual void beginActualText(GfxState * /*state*/, const GooString * /*text*/) { } virtual void endActualText(GfxState * /*state*/) { } //----- image drawing // Draw an image mask. An image mask is a one-bit-per-pixel image, where each pixel // can only be 'fill color' or 'transparent'. // // If 'invert' is false, a sample value of 0 marks the page with the current color, // and a 1 leaves the previous contents unchanged. If 'invert' is true, these meanings are reversed. virtual void drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg); virtual void setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool inlineImg, double *baseMatrix); virtual void unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix); virtual void drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg); virtual 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); virtual void 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); //----- grouping operators virtual void endMarkedContent(GfxState *state); virtual void beginMarkedContent(const char *name, Dict *properties); virtual void markPoint(const char *name); virtual void markPoint(const char *name, Dict *properties); #ifdef OPI_SUPPORT //----- OPI functions virtual void opiBegin(GfxState *state, Dict *opiDict); virtual void opiEnd(GfxState *state, Dict *opiDict); #endif //----- Type 3 font operators virtual void type3D0(GfxState * /*state*/, double /*wx*/, double /*wy*/) { } virtual void type3D1(GfxState * /*state*/, double /*wx*/, double /*wy*/, double /*llx*/, double /*lly*/, double /*urx*/, double /*ury*/) { } //----- form XObjects virtual void beginForm(Object * /* obj */, Ref /*id*/) { } virtual void drawForm(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::unique_ptr> endProfile(); //----- transparency groups and soft masks 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*/) { } virtual void setSoftMask(GfxState * /*state*/, const double * /*bbox*/, bool /*alpha*/, Function * /*transferFunc*/, GfxColor * /*backdropColor*/) { } virtual void clearSoftMask(GfxState * /*state*/) { } //----- links virtual void processLink(AnnotLink * /*link*/) { } #if 1 //~tmp: turn off anti-aliasing temporarily 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; } #endif private: double defCTM[6]; // default coordinate transform matrix double defICTM[6]; // inverse of default CTM std::unique_ptr> profileHash; #ifdef USE_CMS GfxLCMSProfilePtr displayprofile; GfxLCMSProfilePtr defaultGrayProfile; GfxLCMSProfilePtr defaultRGBProfile; GfxLCMSProfilePtr defaultCMYKProfile; PopplerCache iccColorSpaceCache; #endif }; #endif poppler-24.02.0/poppler/PDFDoc.cc000066400000000000000000002376171455701731300164150ustar00rootroot00000000000000//======================================================================== // // PDFDoc.cc // // 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) 2005, 2006, 2008 Brad Hards // Copyright (C) 2005, 2007-2009, 2011-2023 Albert Astals Cid // Copyright (C) 2008 Julien Rebetez // Copyright (C) 2008, 2010 Pino Toscano // Copyright (C) 2008, 2010, 2011 Carlos Garcia Campos // Copyright (C) 2009 Eric Toombs // Copyright (C) 2009 Kovid Goyal // Copyright (C) 2009, 2011 Axel Struebing // Copyright (C) 2010-2012, 2014 Hib Eris // Copyright (C) 2010 Jakub Wilk // Copyright (C) 2010 Ilya Gorenbein // Copyright (C) 2010 Srinivas Adicherla // Copyright (C) 2010 Philip Lorenz // Copyright (C) 2011-2016 Thomas Freitag // Copyright (C) 2012, 2013 Fabio D'Urso // Copyright (C) 2013, 2014, 2017 Adrian Johnson // Copyright (C) 2013, 2018 Adam Reichold // Copyright (C) 2014 Bogdan Cristea // Copyright (C) 2015 Li Junling // Copyright (C) 2015 André Guerreiro // Copyright (C) 2015 André Esser // Copyright (C) 2016, 2020 Jakub Alba // Copyright (C) 2017 Jean Ghali // Copyright (C) 2017 Fredrik Fornwall // Copyright (C) 2018 Ben Timby // Copyright (C) 2018 Evangelos Foutras // 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) 2018 Philipp Knechtges // Copyright (C) 2019 Christian Persch // Copyright (C) 2020 Nelson Benítez León // Copyright (C) 2020 Thorsten Behrens // Copyright (C) 2020 Adam Sampson // Copyright (C) 2021-2023 Oliver Sander // Copyright (C) 2021 Mahmoud Khalil // Copyright (C) 2021 RM // Copyright (C) 2021 Georgiy Sgibnev . Work sponsored by lab50.net. // Copyright (C) 2021-2022 Marek Kasik // Copyright (C) 2022 Felix Jung // Copyright (C) 2022 crt // Copyright (C) 2022 Erich E. Hoover // Copyright (C) 2023 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 // //======================================================================== #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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" #include "Stream.h" #include "XRef.h" #include "Linearization.h" #include "Link.h" #include "OutputDev.h" #include "Error.h" #include "Lexer.h" #include "Parser.h" #include "SecurityHandler.h" #include "Decrypt.h" #include "Outline.h" #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' #define pdfIdLength 32 // PDF Document IDs (PermanentId, UpdateId) length #define linearizationSearchSize \ 1024 // read this many bytes at beginning of // file to look for linearization // dictionary #define xrefSearchSize \ 1024 // read this many bytes at end of file // to look for 'startxref' //------------------------------------------------------------------------ // PDFDoc //------------------------------------------------------------------------ #define pdfdocLocker() const std::scoped_lock locker(mutex) PDFDoc::PDFDoc() { } 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) { #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[n] = L'\0'; wchar_t *wFileName = (wchar_t *)utf8ToUtf16(fileName->c_str()); file = GooFile::open(wFileName); gfree(wFileName); #else file = GooFile::open(fileName->toStr()); #endif if (!file) { // fopen() has failed. // Keep a copy of the errno returned by fopen so that it can be // referred to later. fopenErrno = errno; error(errIO, -1, "Couldn't open file '{0:t}': {1:s}.", fileName.get(), strerror(errno)); errCode = errOpenFile; return; } // create stream str = new FileStream(file.get(), 0, false, file->size(), Object(objNull)); ok = setup(ownerPassword, userPassword, xrefReconstructedCallback); } #ifdef _WIN32 PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, const std::optional &ownerPassword, const std::optional &userPassword, void *guiDataA, const std::function &xrefReconstructedCallback) : guiData(guiDataA) { 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)); for (int i = 0; i < fileNameLen; ++i) { fileNameG->append((char)fileNameA[i]); fileNameU[i] = fileNameA[i]; } fileName.reset(fileNameG); fileNameU[fileNameLen] = L'\0'; // 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); } else { file = GooFile::open(fileName->toStr()); } if (!file) { error(errIO, -1, "Couldn't open file '{0:t}'", fileName.get()); errCode = errOpenFile; return; } // create stream str = new FileStream(file.get(), 0, false, file->size(), Object(objNull)); ok = setup(ownerPassword, userPassword, xrefReconstructedCallback); } #endif PDFDoc::PDFDoc(BaseStream *strA, const std::optional &ownerPassword, const std::optional &userPassword, void *guiDataA, const std::function &xrefReconstructedCallback) : guiData(guiDataA) { if (strA->getFileName()) { fileName.reset(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[n] = L'\0'; #endif } str = strA; ok = setup(ownerPassword, userPassword, xrefReconstructedCallback); } bool PDFDoc::setup(const std::optional &ownerPassword, const std::optional &userPassword, const std::function &xrefReconstructedCallback) { pdfdocLocker(); if (str->getLength() <= 0) { error(errSyntaxError, -1, "Document stream is empty"); errCode = errDamaged; return false; } str->setPos(0, -1); if (str->getPos() < 0) { error(errSyntaxError, -1, "Document base stream is not seekable"); errCode = errFileIO; return false; } str->reset(); // check footer // Adobe does not seem to enforce %%EOF, so we do the same // if (!checkFooter()) return false; // check header checkHeader(); bool wasReconstructed = false; // read xref table xref = new XRef(str, getStartXRef(), getMainXRefEntriesOffset(), &wasReconstructed, false, xrefReconstructedCallback); if (!xref->isOk()) { if (wasReconstructed) { delete xref; startXRefPos = -1; xref = new XRef(str, getStartXRef(true), getMainXRefEntriesOffset(true), &wasReconstructed, false, xrefReconstructedCallback); } if (!xref->isOk()) { error(errSyntaxError, -1, "Couldn't read xref table"); errCode = xref->getErrorCode(); return false; } } // check for encryption if (!checkEncryption(ownerPassword, userPassword)) { errCode = errEncrypted; return false; } // read catalog catalog = new Catalog(this); if (catalog && !catalog->isOk()) { if (!wasReconstructed) { // try one more time to construct the Catalog, maybe the problem is damaged XRef delete catalog; delete xref; xref = new XRef(str, 0, 0, nullptr, true, xrefReconstructedCallback); catalog = new Catalog(this); } if (catalog && !catalog->isOk()) { error(errSyntaxError, -1, "Couldn't read page catalog"); errCode = errBadCatalog; return false; } } // Extract PDF Subtype information extractPDFSubtype(); // done return true; } 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; delete xref; delete hints; delete linearization; delete str; #ifdef _WIN32 gfree(fileNameU); #endif } // Check for a %%EOF at the end of this stream bool PDFDoc::checkFooter() { // we look in the last 1024 chars because Adobe does the same char *eof = new char[1025]; Goffset pos = str->getPos(); str->setPos(1024, -1); int i, ch; for (i = 0; i < 1024; i++) { ch = str->getChar(); if (ch == EOF) { break; } eof[i] = ch; } eof[i] = '\0'; bool found = false; for (i = i - 5; i >= 0; i--) { if (strncmp(&eof[i], "%%EOF", 5) == 0) { found = true; break; } } if (!found) { error(errSyntaxError, -1, "Document has not the mandatory ending %%EOF"); errCode = errDamaged; delete[] eof; return false; } delete[] eof; str->setPos(pos); return true; } // Check for a PDF header on this stream. Skip past some garbage // if necessary. void PDFDoc::checkHeader() { char hdrBuf[headerSearchSize + 1]; char *p; char *tokptr; int i; int bytesRead; headerPdfMajorVersion = 0; headerPdfMinorVersion = 0; // read up to headerSearchSize bytes from the beginning of the document for (i = 0; i < headerSearchSize; ++i) { const int c = str->getChar(); if (c == EOF) { break; } hdrBuf[i] = c; } bytesRead = i; hdrBuf[bytesRead] = '\0'; // find the start of the PDF header if it exists and parse the version bool headerFound = false; for (i = 0; i < bytesRead - 5; ++i) { if (!strncmp(&hdrBuf[i], "%PDF-", 5)) { headerFound = true; break; } } if (!headerFound) { error(errSyntaxWarning, -1, "May not be a PDF file (continuing anyway)"); return; } str->moveStart(i); if (!(p = strtok_r(&hdrBuf[i + 5], " \t\n\r", &tokptr))) { error(errSyntaxWarning, -1, "May not be a PDF file (continuing anyway)"); return; } sscanf(p, "%d.%d", &headerPdfMajorVersion, &headerPdfMinorVersion); // We don't do the version check. Don't add it back in. } bool PDFDoc::checkEncryption(const std::optional &ownerPassword, const std::optional &userPassword) { bool encrypted; bool ret; Object encrypt = xref->getTrailerDict()->dictLookup("Encrypt"); if ((encrypted = encrypt.isDict())) { if ((secHdlr = SecurityHandler::make(this, &encrypt))) { if (secHdlr->isUnencrypted()) { // no encryption ret = true; } else if (secHdlr->checkEncryption(ownerPassword, userPassword)) { // authorization succeeded xref->setEncryption(secHdlr->getPermissionFlags(), secHdlr->getOwnerPasswordOk(), secHdlr->getFileKey(), secHdlr->getFileKeyLength(), secHdlr->getEncVersion(), secHdlr->getEncRevision(), secHdlr->getEncAlgorithm()); ret = true; } else { // authorization failed ret = false; } } else { // couldn't find the matching security handler ret = false; } } else { // document is not encrypted ret = true; } return ret; } static PDFSubtypePart pdfPartFromString(PDFSubtype subtype, const std::string &pdfsubver) { const std::regex regex("PDF/(?:A|X|VT|E|UA)-([[:digit:]])(?:[[:alpha:]]{1,2})?:?([[:digit:]]{4})?"); std::smatch match; PDFSubtypePart subtypePart = subtypePartNone; if (std::regex_search(pdfsubver, match, regex)) { int date = 0; const int part = std::stoi(match.str(1)); if (match[2].matched) { date = std::stoi(match.str(2)); } switch (subtype) { case subtypePDFX: switch (part) { case 1: switch (date) { case 2001: default: subtypePart = subtypePart1; break; case 2003: subtypePart = subtypePart4; break; } break; case 2: subtypePart = subtypePart5; break; case 3: switch (date) { case 2002: default: subtypePart = subtypePart3; break; case 2003: subtypePart = subtypePart6; break; } break; case 4: subtypePart = subtypePart7; break; case 5: subtypePart = subtypePart8; break; } break; default: subtypePart = (PDFSubtypePart)part; break; } } return subtypePart; } static PDFSubtypeConformance pdfConformanceFromString(const std::string &pdfsubver) { 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; } delete conf; } return pdfConf; } void PDFDoc::extractPDFSubtype() { pdfSubtype = subtypeNull; pdfPart = subtypePartNull; pdfConformance = subtypeConfNull; std::unique_ptr pdfSubtypeVersion; // Find PDF InfoDict subtype key if any if ((pdfSubtypeVersion = getDocInfoStringEntry("GTS_PDFA1Version"))) { pdfSubtype = subtypePDFA; } else if ((pdfSubtypeVersion = getDocInfoStringEntry("GTS_PDFEVersion"))) { pdfSubtype = subtypePDFE; } else if ((pdfSubtypeVersion = getDocInfoStringEntry("GTS_PDFUAVersion"))) { pdfSubtype = subtypePDFUA; } else if ((pdfSubtypeVersion = getDocInfoStringEntry("GTS_PDFVTVersion"))) { pdfSubtype = subtypePDFVT; } else if ((pdfSubtypeVersion = getDocInfoStringEntry("GTS_PDFXVersion"))) { pdfSubtype = subtypePDFX; } else { pdfSubtype = subtypeNone; pdfPart = subtypePartNone; pdfConformance = subtypeConfNone; return; } // Extract part from version string pdfPart = pdfPartFromString(pdfSubtype, pdfSubtypeVersion->toStr()); // Extract conformance from version string pdfConformance = pdfConformanceFromString(pdfSubtypeVersion->toStr()); } static void addSignatureFieldsToVector(FormField *ff, std::vector &res) { if (ff->getNumChildren() == 0) { if (ff->getType() == formSignature) { res.push_back(static_cast(ff)); } } else { for (int i = 0; i < ff->getNumChildren(); ++i) { FormField *children = ff->getChildren(i); addSignatureFieldsToVector(children, res); } } } std::vector PDFDoc::getSignatureFields() { // Unfortunately there's files with signatures in Forms but not in Annots // and files with signatures in Annots but no in forms so we need to search both std::vector res; // First search const Form *f = catalog->getForm(); if (f) { const int nRootFields = f->getNumFields(); for (int i = 0; i < nRootFields; ++i) { FormField *ff = f->getRootField(i); addSignatureFieldsToVector(ff, res); } } // Second search for (int page = 1; page <= getNumPages(); ++page) { Page *p = getPage(page); if (p) { const std::unique_ptr pw = p->getFormWidgets(); for (int i = 0; i < pw->getNumWidgets(); ++i) { FormWidget *fw = pw->getWidget(i); 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()) { res.push_back(ffs); } } } } } return res; } void PDFDoc::displayPage(OutputDev *out, int page, double hDPI, double vDPI, int rotate, bool useMediaBox, bool crop, bool printing, bool (*abortCheckCbk)(void *data), void *abortCheckCbkData, bool (*annotDisplayDecideCbk)(Annot *annot, void *user_data), void *annotDisplayDecideCbkData, bool copyXRef) { if (globalParams->getPrintCommands()) { printf("***** page %d *****\n", page); } if (getPage(page)) { getPage(page)->display(out, hDPI, vDPI, rotate, useMediaBox, crop, printing, abortCheckCbk, abortCheckCbkData, annotDisplayDecideCbk, annotDisplayDecideCbkData, copyXRef); } } void PDFDoc::displayPages(OutputDev *out, int firstPage, int lastPage, double hDPI, double vDPI, int rotate, bool useMediaBox, bool crop, bool printing, bool (*abortCheckCbk)(void *data), void *abortCheckCbkData, bool (*annotDisplayDecideCbk)(Annot *annot, void *user_data), void *annotDisplayDecideCbkData) { int page; for (page = firstPage; page <= lastPage; ++page) { displayPage(out, page, hDPI, vDPI, rotate, useMediaBox, crop, printing, abortCheckCbk, abortCheckCbkData, annotDisplayDecideCbk, annotDisplayDecideCbkData); } } void PDFDoc::displayPageSlice(OutputDev *out, int page, double hDPI, double vDPI, int rotate, bool useMediaBox, bool crop, bool printing, int sliceX, int sliceY, int sliceW, int sliceH, bool (*abortCheckCbk)(void *data), void *abortCheckCbkData, bool (*annotDisplayDecideCbk)(Annot *annot, void *user_data), void *annotDisplayDecideCbkData, bool copyXRef) { if (getPage(page)) { getPage(page)->displaySlice(out, hDPI, vDPI, rotate, useMediaBox, crop, sliceX, sliceY, sliceW, sliceH, printing, abortCheckCbk, abortCheckCbkData, annotDisplayDecideCbk, annotDisplayDecideCbkData, copyXRef); } } std::unique_ptr PDFDoc::getLinks(int page) { Page *p = getPage(page); if (!p) { return std::make_unique(nullptr); } return p->getLinks(); } void PDFDoc::processLinks(OutputDev *out, int page) { if (getPage(page)) { getPage(page)->processLinks(out); } } Linearization *PDFDoc::getLinearization() { if (!linearization) { linearization = new Linearization(str); linearizationState = 0; } return linearization; } bool PDFDoc::checkLinearization() { if (linearization == nullptr) { return false; } if (linearizationState == 1) { return true; } if (linearizationState == 2) { return false; } if (!hints) { hints = new Hints(str, linearization, getXRef(), secHdlr); } if (!hints->isOk()) { linearizationState = 2; return false; } for (int page = 1; page <= linearization->getNumPages(); page++) { Ref pageRef; pageRef.num = hints->getPageObjectNum(page); if (!pageRef.num) { linearizationState = 2; return false; } // check for bogus ref - this can happen in corrupted PDF files if (pageRef.num < 0 || pageRef.num >= xref->getNumObjects()) { linearizationState = 2; return false; } pageRef.gen = xref->getEntry(pageRef.num)->gen; Object obj = xref->fetch(pageRef); if (!obj.isDict("Page")) { linearizationState = 2; return false; } } linearizationState = 1; return true; } bool PDFDoc::isLinearized(bool tryingToReconstruct) { if ((str->getLength()) && (getLinearization()->getLength() == str->getLength())) { return true; } else { if (tryingToReconstruct) { return getLinearization()->getLength() > 0; } else { return false; } } } void PDFDoc::setDocInfoStringEntry(const char *key, GooString *value) { bool removeEntry = !value || value->getLength() == 0 || (value->toStr() == unicodeByteOrderMark); if (removeEntry) { delete value; } Object infoObj = getDocInfo(); if (infoObj.isNull() && removeEntry) { // No info dictionary, so no entry to remove. return; } Ref infoObjRef; infoObj = xref->createDocInfoIfNeeded(&infoObjRef); if (removeEntry) { infoObj.dictSet(key, Object(objNull)); } else { infoObj.dictSet(key, Object(value)); } if (infoObj.dictGetLength() == 0) { // Info dictionary is empty. Remove it altogether. removeDocInfo(); } else { xref->setModifiedObject(&infoObj, infoObjRef); } } std::unique_ptr PDFDoc::getDocInfoStringEntry(const char *key) { Object infoObj = getDocInfo(); if (!infoObj.isDict()) { return {}; } const Object entryObj = infoObj.dictLookup(key); if (!entryObj.isString()) { return {}; } return std::unique_ptr(entryObj.getString()->copy()); } static bool get_id(const GooString *encodedidstring, GooString *id) { const char *encodedid = encodedidstring->c_str(); char pdfid[pdfIdLength + 1]; int n; if (encodedidstring->getLength() != pdfIdLength / 2) { return false; } n = sprintf(pdfid, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", encodedid[0] & 0xff, encodedid[1] & 0xff, encodedid[2] & 0xff, encodedid[3] & 0xff, encodedid[4] & 0xff, encodedid[5] & 0xff, encodedid[6] & 0xff, encodedid[7] & 0xff, encodedid[8] & 0xff, encodedid[9] & 0xff, encodedid[10] & 0xff, encodedid[11] & 0xff, encodedid[12] & 0xff, encodedid[13] & 0xff, encodedid[14] & 0xff, encodedid[15] & 0xff); if (n != pdfIdLength) { return false; } id->Set(pdfid, pdfIdLength); return true; } bool PDFDoc::getID(GooString *permanent_id, GooString *update_id) const { Object obj = xref->getTrailerDict()->dictLookup("ID"); if (obj.isArray() && obj.arrayGetLength() == 2) { if (permanent_id) { Object obj2 = obj.arrayGet(0); if (obj2.isString()) { if (!get_id(obj2.getString(), permanent_id)) { return false; } } else { error(errSyntaxError, -1, "Invalid permanent ID"); return false; } } if (update_id) { Object obj2 = obj.arrayGet(1); if (obj2.isString()) { if (!get_id(obj2.getString(), update_id)) { return false; } } else { error(errSyntaxError, -1, "Invalid update ID"); return false; } } return true; } return false; } Hints *PDFDoc::getHints() { if (!hints && isLinearized()) { hints = new Hints(str, getLinearization(), getXRef(), secHdlr); } return hints; } int PDFDoc::savePageAs(const GooString &name, int pageNo) { FILE *f; if (file && file->modificationTimeChangedSinceOpen()) { return errFileChangedSinceOpen; } int rootNum = getXRef()->getNumObjects() + 1; // Make sure that special flags are set, because we are going to read // all objects, including Unencrypted ones. xref->scanSpecialFlags(); unsigned char *fileKey; CryptAlgorithm encAlgorithm; int keyLength; xref->getEncryptionParameters(&fileKey, &encAlgorithm, &keyLength); if (pageNo < 1 || pageNo > getNumPages() || !getCatalog()->getPage(pageNo)) { error(errInternal, -1, "Illegal pageNo: {0:d}({1:d})", pageNo, getNumPages()); return errOpenFile; } const PDFRectangle *cropBox = nullptr; if (getCatalog()->getPage(pageNo)->isCropped()) { cropBox = getCatalog()->getPage(pageNo)->getCropBox(); } replacePageDict(pageNo, getCatalog()->getPage(pageNo)->getRotate(), getCatalog()->getPage(pageNo)->getMediaBox(), cropBox); Ref *refPage = getCatalog()->getPageRef(pageNo); Object page = getXRef()->fetch(*refPage); if (!(f = openFile(name.c_str(), "wb"))) { error(errIO, -1, "Couldn't open file '{0:t}'", &name); return errOpenFile; } // 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); 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); } const std::unique_ptr countRef = std::make_unique(); Object *trailerObj = getXRef()->getTrailerDict(); if (trailerObj->isDict()) { markPageObjects(trailerObj->getDict(), yRef.get(), countRef.get(), 0, refPage->num, rootNum + 2); } yRef->add(0, 65535, 0, false); writeHeader(outStr.get(), getPDFMajorVersion(), getPDFMinorVersion()); // get and mark info dict Object infoObj = getXRef()->getDocInfo(); if (infoObj.isDict()) { Dict *infoDict = infoObj.getDict(); markPageObjects(infoDict, yRef.get(), countRef.get(), 0, refPage->num, rootNum + 2); if (trailerObj->isDict()) { Dict *trailerDict = trailerObj->getDict(); const Object &ref = trailerDict->lookupNF("Info"); if (ref.isRef()) { yRef->add(ref.getRef(), 0, true); if (getXRef()->getEntry(ref.getRef().num)->type == xrefEntryCompressed) { yRef->getEntry(ref.getRef().num)->type = xrefEntryCompressed; } } } } // get and mark output intents etc. Object catObj = getXRef()->getCatalog(); if (!catObj.isDict()) { 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.get(), countRef.get(), 0, refPage->num, rootNum + 2); } Dict *pagesDict = pagesObj.getDict(); Object resourcesObj = pagesDict->lookup("Resources"); if (resourcesObj.isDict()) { markPageObjects(resourcesObj.getDict(), yRef.get(), countRef.get(), 0, refPage->num, rootNum + 2); } 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.get(), countRef.get(), 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.get(), countRef.get(), 0, refPage->num, rootNum + 2); } yRef->markUnencrypted(); writePageObjects(outStr.get(), yRef.get(), 0); yRef->add(rootNum, 0, outStr->getPos(), true); outStr->printf("%d 0 obj\n", rootNum); outStr->printf("<< /Type /Catalog /Pages %d 0 R", rootNum + 1); for (int j = 0; j < catDict->getLength(); j++) { const char *key = catDict->getKey(j); if (strcmp(key, "Type") != 0 && strcmp(key, "Catalog") != 0 && strcmp(key, "Pages") != 0) { if (j > 0) { outStr->printf(" "); } Object value = catDict->getValNF(j).copy(); outStr->printf("/%s ", key); writeObject(&value, outStr.get(), getXRef(), 0, nullptr, cryptRC4, 0, 0, 0); } } outStr->printf(">>\nendobj\n"); yRef->add(rootNum + 1, 0, outStr->getPos(), true); outStr->printf("%d 0 obj\n", rootNum + 1); outStr->printf("<< /Type /Pages /Kids [ %d 0 R ] /Count 1 ", rootNum + 2); if (resourcesObj.isDict()) { outStr->printf("/Resources "); writeObject(&resourcesObj, outStr.get(), getXRef(), 0, nullptr, cryptRC4, 0, 0, 0); } outStr->printf(">>\n"); outStr->printf("endobj\n"); yRef->add(rootNum + 2, 0, outStr->getPos(), true); outStr->printf("%d 0 obj\n", rootNum + 2); outStr->printf("<< "); for (int n = 0; n < pageDict->getLength(); n++) { if (n > 0) { outStr->printf(" "); } const char *key = pageDict->getKey(n); Object value = pageDict->getValNF(n).copy(); if (strcmp(key, "Parent") == 0) { outStr->printf("/Parent %d 0 R", rootNum + 1); } else { outStr->printf("/%s ", key); writeObject(&value, outStr.get(), getXRef(), 0, nullptr, cryptRC4, 0, 0, 0); } } outStr->printf(" >>\nendobj\n"); Goffset uxrefOffset = outStr->getPos(); Ref ref; ref.num = rootNum; ref.gen = 0; Object trailerDict = createTrailerDict(rootNum + 3, false, 0, &ref, getXRef(), name.c_str(), uxrefOffset); writeXRefTableTrailer(std::move(trailerDict), yRef.get(), false /* do not write unnecessary entries */, uxrefOffset, outStr.get(), getXRef()); outStr->close(); return errNone; } int PDFDoc::saveAs(const GooString &name, PDFWriteMode mode) { FILE *f; OutStream *outStr; int res; if (!(f = openFile(name.c_str(), "wb"))) { error(errIO, -1, "Couldn't open file '{0:t}'", &name); return errOpenFile; } outStr = new FileOutStream(f, 0); res = saveAs(outStr, mode); delete outStr; fclose(f); return res; } int PDFDoc::saveAs(OutStream *outStr, PDFWriteMode mode) { if (file && file->modificationTimeChangedSinceOpen()) { return errFileChangedSinceOpen; } if (!xref->isModified() && mode == writeStandard) { // simply copy the original file saveWithoutChangesAs(outStr); } else if (mode == writeForceRewrite) { saveCompleteRewrite(outStr); } else { saveIncrementalUpdate(outStr); } return errNone; } int PDFDoc::saveWithoutChangesAs(const GooString &name) { FILE *f; OutStream *outStr; int res; if (!(f = openFile(name.c_str(), "wb"))) { error(errIO, -1, "Couldn't open file '{0:t}'", &name); return errOpenFile; } outStr = new FileOutStream(f, 0); res = saveWithoutChangesAs(outStr); delete outStr; fclose(f); return res; } 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); } 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); } copyStr->close(); delete copyStr; unsigned char *fileKey; CryptAlgorithm encAlgorithm; int keyLength; xref->getEncryptionParameters(&fileKey, &encAlgorithm, &keyLength); uxref = new XRef(); uxref->add(0, 65535, 0, false); xref->lock(); for (int i = 0; i < xref->getNumObjects(); i++) { if ((xref->getEntry(i)->type == xrefEntryFree) && (xref->getEntry(i)->gen == 0)) { // we skip the irrelevant free objects continue; } if (xref->getEntry(i)->getFlag(XRefEntry::Updated)) { // we have an updated object Ref ref; ref.num = i; ref.gen = xref->getEntry(i)->type == xrefEntryCompressed ? 0 : xref->getEntry(i)->gen; if (xref->getEntry(i)->type != xrefEntryFree) { Object obj1 = xref->fetch(ref, 1 /* recursion */); Goffset offset = writeObjectHeader(&ref, outStr); writeObject(&obj1, outStr, fileKey, encAlgorithm, keyLength, ref); writeObjectFooter(outStr); uxref->add(ref, offset, true); } else { uxref->add(ref, 0, false); } } } xref->unlock(); // because of "uxref->add(0, 65535, 0, false);" uxref->getNumObjects() will // always be >= 1; if it is 1, it means there is nothing to update if (uxref->getNumObjects() == 1) { delete uxref; return; } Goffset uxrefOffset = outStr->getPos(); int numobjects = xref->getNumObjects(); const char *fileNameA = fileName ? fileName->c_str() : nullptr; Ref rootRef, uxrefStreamRef; rootRef.num = getXRef()->getRootNum(); rootRef.gen = getXRef()->getRootGen(); // Output a xref stream if there is a xref stream already bool xRefStream = xref->isXRefStream(); if (xRefStream) { // Append an entry for the xref stream itself uxrefStreamRef.num = numobjects++; uxrefStreamRef.gen = 0; uxref->add(uxrefStreamRef, uxrefOffset, true); } Object trailerDict = createTrailerDict(numobjects, true, getStartXRef(), &rootRef, getXRef(), fileNameA, uxrefOffset); if (xRefStream) { writeXRefStreamTrailer(std::move(trailerDict), uxref, &uxrefStreamRef, uxrefOffset, outStr, getXRef()); } else { writeXRefTableTrailer(std::move(trailerDict), uxref, false, uxrefOffset, outStr, getXRef()); } delete uxref; } void PDFDoc::saveCompleteRewrite(OutStream *outStr) { // Make sure that special flags are set, because we are going to read // all objects, including Unencrypted ones. xref->scanSpecialFlags(); unsigned char *fileKey; CryptAlgorithm encAlgorithm; int keyLength; xref->getEncryptionParameters(&fileKey, &encAlgorithm, &keyLength); writeHeader(outStr, getPDFMajorVersion(), getPDFMinorVersion()); XRef *uxref = new XRef(); uxref->add(0, 65535, 0, false); xref->lock(); for (int i = 0; i < xref->getNumObjects(); i++) { Ref ref; XRefEntryType type = xref->getEntry(i)->type; if (type == xrefEntryFree) { ref.num = i; ref.gen = xref->getEntry(i)->gen; /* the XRef class adds a lot of irrelevant free entries, we only want the significant one and we don't want the one with num=0 because it has already been added (gen = 65535)*/ if (ref.gen > 0 && ref.num > 0) { uxref->add(ref, 0, false); } } else if (xref->getEntry(i)->getFlag(XRefEntry::DontRewrite)) { // This entry must not be written, put a free entry instead (with incremented gen) ref.num = i; ref.gen = xref->getEntry(i)->gen + 1; uxref->add(ref, 0, false); } else if (type == xrefEntryUncompressed) { ref.num = i; ref.gen = xref->getEntry(i)->gen; Object obj1 = xref->fetch(ref, 1 /* recursion */); Goffset offset = writeObjectHeader(&ref, outStr); // Write unencrypted objects in unencrypted form if (xref->getEntry(i)->getFlag(XRefEntry::Unencrypted)) { writeObject(&obj1, outStr, nullptr, cryptRC4, 0, 0, 0); } else { writeObject(&obj1, outStr, fileKey, encAlgorithm, keyLength, ref); } writeObjectFooter(outStr); uxref->add(ref, offset, true); } else if (type == xrefEntryCompressed) { ref.num = i; ref.gen = 0; // compressed entries have gen == 0 Object obj1 = xref->fetch(ref, 1 /* recursion */); Goffset offset = writeObjectHeader(&ref, outStr); writeObject(&obj1, outStr, fileKey, encAlgorithm, keyLength, ref); writeObjectFooter(outStr); uxref->add(ref, offset, true); } } xref->unlock(); Goffset uxrefOffset = outStr->getPos(); writeXRefTableTrailer(uxrefOffset, uxref, true /* write all entries */, uxref->getNumObjects(), outStr, false /* complete rewrite */); 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; if (!alreadyWrittenDicts) { alreadyWrittenDicts = new std::set; deleteSet = true; } if (alreadyWrittenDicts->find(dict) != alreadyWrittenDicts->end()) { error(errSyntaxWarning, -1, "PDFDoc::writeDictionary: Found recursive dicts"); if (deleteSet) { delete alreadyWrittenDicts; } return; } else { alreadyWrittenDicts->insert(dict); } outStr->printf("<<"); for (int i = 0; i < dict->getLength(); i++) { GooString keyName(dict->getKey(i)); outStr->printf("/%s ", sanitizedName(keyName.toStr()).c_str()); Object obj1 = dict->getValNF(i).copy(); writeObject(&obj1, outStr, xRef, numOffset, fileKey, encAlgorithm, keyLength, ref, alreadyWrittenDicts); } outStr->printf(">> "); if (deleteSet) { delete alreadyWrittenDicts; } } void PDFDoc::writeStream(Stream *str, OutStream *outStr) { outStr->printf("stream\r\n"); str->reset(); for (int c = str->getChar(); c != EOF; c = str->getChar()) { outStr->printf("%c", c); } outStr->printf("\r\nendstream\r\n"); } void PDFDoc::writeRawStream(Stream *str, OutStream *outStr) { Object obj1 = str->getDict()->lookup("Length"); if (!obj1.isInt() && !obj1.isInt64()) { error(errSyntaxError, -1, "PDFDoc::writeRawStream, no Length in stream dict"); return; } Goffset length; if (obj1.isInt()) { length = obj1.getInt(); } else { length = obj1.getInt64(); } outStr->printf("stream\r\n"); str->unfilteredReset(); for (Goffset i = 0; i < length; i++) { int c = str->getUnfilteredChar(); if (unlikely(c == EOF)) { error(errSyntaxError, -1, "PDFDoc::writeRawStream: EOF reading stream"); break; } outStr->printf("%c", c); } 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; if (fileKey) { EncryptStream *enc = new EncryptStream(new MemStream(s->c_str(), 0, s->getLength(), Object(objNull)), fileKey, encAlgorithm, keyLength, ref); sEnc = new GooString(); int c; enc->reset(); while ((c = enc->getChar()) != EOF) { sEnc->append((char)c); } delete enc; s = sEnc; } // Write data if (s->hasUnicodeMarker()) { // unicode string don't necessary end with \0 const char *c = s->c_str(); std::stringstream stream; stream << std::setfill('0') << std::hex; for (int i = 0; i < s->getLength(); i++) { stream << std::setw(2) << (0xff & (unsigned int)*(c + i)); } outStr->printf("<"); outStr->printf("%s", stream.str().c_str()); outStr->printf("> "); } else { const char *c = s->c_str(); outStr->printf("("); for (int i = 0; i < s->getLength(); i++) { char unescaped = *(c + i) & 0x000000ff; // escape if needed if (unescaped == '\r') { outStr->printf("\\r"); } else if (unescaped == '\n') { outStr->printf("\\n"); } else { if (unescaped == '(' || unescaped == ')' || unescaped == '\\') { outStr->printf("%c", '\\'); } outStr->printf("%c", unescaped); } } outStr->printf(") "); } delete sEnc; } Goffset PDFDoc::writeObjectHeader(Ref *ref, OutStream *outStr) { Goffset offset = outStr->getPos(); outStr->printf("%i %i obj\r\n", ref->num, ref->gen); return offset; } void PDFDoc::writeObject(Object *obj, OutStream *outStr, XRef *xRef, unsigned int numOffset, unsigned char *fileKey, CryptAlgorithm encAlgorithm, int keyLength, int objNum, int objGen, std::set *alreadyWrittenDicts) { writeObject(obj, outStr, xRef, numOffset, fileKey, encAlgorithm, keyLength, { objNum, objGen }, alreadyWrittenDicts); } void PDFDoc::writeObject(Object *obj, OutStream *outStr, XRef *xRef, unsigned int numOffset, unsigned char *fileKey, CryptAlgorithm encAlgorithm, int keyLength, Ref ref, std::set *alreadyWrittenDicts) { Array *array; switch (obj->getType()) { case objBool: outStr->printf("%s ", obj->getBool() ? "true" : "false"); break; case objInt: outStr->printf("%i ", obj->getInt()); break; case objInt64: outStr->printf("%lli ", obj->getInt64()); break; case objReal: { GooString s; s.appendf("{0:.10g}", obj->getReal()); outStr->printf("%s ", s.c_str()); break; } case objString: writeString(obj->getString(), outStr, fileKey, encAlgorithm, keyLength, ref); break; case objHexString: { const GooString *s = obj->getHexString(); outStr->printf("<"); for (int i = 0; i < s->getLength(); i++) { outStr->printf("%02x", s->getChar(i) & 0xff); } outStr->printf("> "); break; } case objName: { GooString name(obj->getName()); outStr->printf("/%s ", sanitizedName(name.toStr()).c_str()); break; } case objNull: outStr->printf("null "); break; case objArray: array = obj->getArray(); outStr->printf("["); for (int i = 0; i < array->getLength(); i++) { Object obj1 = array->getNF(i).copy(); writeObject(&obj1, outStr, xRef, numOffset, fileKey, encAlgorithm, keyLength, ref); } outStr->printf("] "); break; case objDict: writeDictionary(obj->getDict(), outStr, xRef, numOffset, fileKey, encAlgorithm, keyLength, ref, alreadyWrittenDicts); break; case objStream: { // We can't modify stream with the current implementation (no write functions in Stream API) // => the only type of streams which that have been modified are internal streams (=strWeird) Stream *stream = obj->getStream(); if (stream->getKind() == strWeird || stream->getKind() == strCrypt) { // we write the stream unencoded => TODO: write stream encoder // Encrypt stream bool removeFilter = true; bool addEncryptstream = false; if (stream->getKind() == strWeird && fileKey) { Object filter = stream->getDict()->lookup("Filter"); if (!filter.isName("Crypt")) { if (filter.isArray()) { for (int i = 0; i < filter.arrayGetLength(); i++) { Object filterEle = filter.arrayGet(i); if (filterEle.isName("Crypt")) { removeFilter = false; break; } } if (removeFilter) { addEncryptstream = true; } } else { addEncryptstream = true; } } else { removeFilter = false; } } else if (fileKey != nullptr) { // Encrypt stream 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.get(); } stream->reset(); // recalculate stream length Goffset tmp = 0; for (int c = stream->getChar(); c != EOF; c = stream->getChar()) { tmp++; } stream->getDict()->set("Length", Object(tmp)); // Remove Stream encoding AutoFreeMemStream *internalStream = dynamic_cast(stream); if (internalStream && internalStream->isFilterRemovalForbidden()) { removeFilter = false; } if (removeFilter) { stream->getDict()->remove("Filter"); } stream->getDict()->remove("DecodeParms"); writeDictionary(stream->getDict(), outStr, xRef, numOffset, fileKey, encAlgorithm, keyLength, ref, alreadyWrittenDicts); writeStream(stream, outStr); } else if (fileKey != nullptr && stream->getKind() == strFile && static_cast(stream)->getNeedsEncryptionOnSave()) { EncryptStream *encStream = new EncryptStream(stream, fileKey, encAlgorithm, keyLength, ref); encStream->setAutoDelete(false); writeDictionary(encStream->getDict(), outStr, xRef, numOffset, fileKey, encAlgorithm, keyLength, ref, alreadyWrittenDicts); writeStream(encStream, outStr); delete encStream; } else { // raw stream copy FilterStream *fs = dynamic_cast(stream); if (fs) { BaseStream *bs = fs->getBaseStream(); if (bs) { Goffset streamEnd; if (xRef->getStreamEnd(bs->getStart(), &streamEnd)) { Goffset val = streamEnd - bs->getStart(); stream->getDict()->set("Length", Object(val)); } } } writeDictionary(stream->getDict(), outStr, xRef, numOffset, fileKey, encAlgorithm, keyLength, ref, alreadyWrittenDicts); writeRawStream(stream, outStr); } break; } case objRef: outStr->printf("%i %i R ", obj->getRef().num + numOffset, obj->getRef().gen); break; case objCmd: outStr->printf("%s\n", obj->getCmd()); break; case objError: outStr->printf("error\r\n"); break; case objEOF: outStr->printf("eof\r\n"); break; case objNone: 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()); break; } } void PDFDoc::writeObjectFooter(OutStream *outStr) { outStr->printf("\r\nendobj\r\n"); } Object PDFDoc::createTrailerDict(int uxrefSize, bool incrUpdate, Goffset startxRef, Ref *root, XRef *xRef, const char *fileName, Goffset fileSize) { Dict *trailerDict = new Dict(xRef); trailerDict->set("Size", Object(uxrefSize)); // build a new ID, as recommended in the reference, uses: // - current time // - file name // - file size // - values of entry in information dictionary GooString message; char buffer[256]; sprintf(buffer, "%i", (int)time(nullptr)); message.append(buffer); if (fileName) { message.append(fileName); } sprintf(buffer, "%lli", (long long)fileSize); message.append(buffer); // info dict -- only use text string if (!xRef->getTrailerDict()->isNone()) { Object docInfo = xRef->getDocInfo(); if (docInfo.isDict()) { for (int i = 0; i < docInfo.getDict()->getLength(); i++) { Object obj2 = docInfo.getDict()->getVal(i); if (obj2.isString()) { message.append(obj2.getString()); } } } } bool hasEncrypt = false; if (!xRef->getTrailerDict()->isNone()) { Object obj2 = xRef->getTrailerDict()->dictLookupNF("Encrypt").copy(); if (!obj2.isNull()) { trailerDict->set("Encrypt", std::move(obj2)); hasEncrypt = true; } } // calculate md5 digest unsigned char digest[16]; md5((unsigned char *)message.c_str(), message.getLength(), digest); // create ID array // In case of encrypted files, the ID must not be changed because it's used to calculate the key if (incrUpdate || hasEncrypt) { // only update the second part of the array Object obj4 = xRef->getTrailerDict()->getDict()->lookup("ID"); if (!obj4.isArray()) { if (hasEncrypt) { error(errSyntaxWarning, -1, "PDFDoc::createTrailerDict original file's ID entry isn't an array. Trying to continue"); } } else { 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))); 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))); trailerDict->set("ID", Object(array)); } trailerDict->set("Root", Object(*root)); if (incrUpdate) { trailerDict->set("Prev", Object(startxRef)); } if (!xRef->getTrailerDict()->isNone()) { Object obj5 = xRef->getDocInfoNF(); if (!obj5.isNull()) { trailerDict->set("Info", std::move(obj5)); } } return Object(trailerDict); } void PDFDoc::writeXRefTableTrailer(Object &&trailerDict, XRef *uxref, bool writeAllEntries, Goffset uxrefOffset, OutStream *outStr, XRef *xRef) { uxref->writeTableToFile(outStr, writeAllEntries); outStr->printf("trailer\r\n"); writeDictionary(trailerDict.getDict(), outStr, xRef, 0, nullptr, cryptRC4, 0, { 0, 0 }, nullptr); outStr->printf("\r\nstartxref\r\n"); outStr->printf("%lli\r\n", uxrefOffset); outStr->printf("%%%%EOF\r\n"); } void PDFDoc::writeXRefStreamTrailer(Object &&trailerDict, XRef *uxref, Ref *uxrefStreamRef, Goffset uxrefOffset, OutStream *outStr, XRef *xRef) { GooString stmData; // Fill stmData and some trailerDict fields uxref->writeStreamToBuffer(&stmData, trailerDict.getDict(), xRef); // Create XRef stream object and write it MemStream *mStream = new MemStream(stmData.c_str(), 0, stmData.getLength(), std::move(trailerDict)); writeObjectHeader(uxrefStreamRef, outStr); Object obj1(static_cast(mStream)); writeObject(&obj1, outStr, xRef, 0, nullptr, cryptRC4, 0, 0, 0); writeObjectFooter(outStr); outStr->printf("startxref\r\n"); outStr->printf("%lli\r\n", uxrefOffset); outStr->printf("%%%%EOF\r\n"); } void PDFDoc::writeXRefTableTrailer(Goffset uxrefOffset, XRef *uxref, bool writeAllEntries, int uxrefSize, OutStream *outStr, bool incrUpdate) { const char *fileNameA = fileName ? fileName->c_str() : nullptr; // file size (doesn't include the trailer) unsigned int fileSize = 0; int c; str->reset(); while ((c = str->getChar()) != EOF) { fileSize++; } str->close(); Ref ref; ref.num = getXRef()->getRootNum(); ref.gen = getXRef()->getRootGen(); Object trailerDict = createTrailerDict(uxrefSize, incrUpdate, getStartXRef(), &ref, getXRef(), fileNameA, fileSize); writeXRefTableTrailer(std::move(trailerDict), uxref, writeAllEntries, uxrefOffset, outStr, getXRef()); } void PDFDoc::writeHeader(OutStream *outStr, int major, int minor) { outStr->printf("%%PDF-%d.%d\n", major, minor); outStr->printf("%%%c%c%c%c\n", 0xE2, 0xE3, 0xCF, 0xD3); } bool PDFDoc::markDictionary(Dict *dict, XRef *xRef, XRef *countRef, unsigned int numOffset, int oldRefNum, int newRefNum, std::set *alreadyMarkedDicts) { bool deleteSet = false; if (!alreadyMarkedDicts) { alreadyMarkedDicts = new std::set; deleteSet = true; } if (alreadyMarkedDicts->find(dict) != alreadyMarkedDicts->end()) { error(errSyntaxWarning, -1, "PDFDoc::markDictionary: Found recursive dicts"); if (deleteSet) { delete alreadyMarkedDicts; } return true; } else { alreadyMarkedDicts->insert(dict); } for (int i = 0; i < dict->getLength(); i++) { const char *key = dict->getKey(i); if (strcmp(key, "Annots") != 0) { Object obj1 = dict->getValNF(i).copy(); const bool success = markObject(&obj1, xRef, countRef, numOffset, oldRefNum, newRefNum, alreadyMarkedDicts); if (unlikely(!success)) { return false; } } else { Object annotsObj = dict->getValNF(i).copy(); if (!annotsObj.isNull()) { markAnnotations(&annotsObj, xRef, countRef, 0, oldRefNum, newRefNum, alreadyMarkedDicts); } } } if (deleteSet) { delete alreadyMarkedDicts; } return true; } bool PDFDoc::markObject(Object *obj, XRef *xRef, XRef *countRef, unsigned int numOffset, int oldRefNum, int newRefNum, std::set *alreadyMarkedDicts) { Array *array; switch (obj->getType()) { case objArray: array = obj->getArray(); for (int i = 0; i < array->getLength(); i++) { Object obj1 = array->getNF(i).copy(); const bool success = markObject(&obj1, xRef, countRef, numOffset, oldRefNum, newRefNum, alreadyMarkedDicts); if (unlikely(!success)) { return false; } } break; case objDict: { const bool success = markDictionary(obj->getDict(), xRef, countRef, numOffset, oldRefNum, newRefNum, alreadyMarkedDicts); if (unlikely(!success)) { return false; } } break; case objStream: { Stream *stream = obj->getStream(); const bool success = markDictionary(stream->getDict(), xRef, countRef, numOffset, oldRefNum, newRefNum, alreadyMarkedDicts); if (unlikely(!success)) { return false; } } break; case objRef: { if (obj->getRef().num + (int)numOffset >= xRef->getNumObjects() || xRef->getEntry(obj->getRef().num + numOffset)->type == xrefEntryFree) { if (getXRef()->getEntry(obj->getRef().num)->type == xrefEntryFree) { return true; // already marked as free => should be replaced } const bool success = xRef->add(obj->getRef().num + numOffset, obj->getRef().gen, 0, true); if (unlikely(!success)) { return false; } if (getXRef()->getEntry(obj->getRef().num)->type == xrefEntryCompressed) { xRef->getEntry(obj->getRef().num + numOffset)->type = xrefEntryCompressed; } } if (obj->getRef().num + (int)numOffset >= countRef->getNumObjects() || countRef->getEntry(obj->getRef().num + numOffset)->type == xrefEntryFree) { countRef->add(obj->getRef().num + numOffset, 1, 0, true); } else { XRefEntry *entry = countRef->getEntry(obj->getRef().num + numOffset); entry->gen++; if (entry->gen > 9) { break; } } Object obj1 = getXRef()->fetch(obj->getRef()); const bool success = markObject(&obj1, xRef, countRef, numOffset, oldRefNum, newRefNum); if (unlikely(!success)) { return false; } } break; default: break; } return true; } bool PDFDoc::replacePageDict(int pageNo, int rotate, const PDFRectangle *mediaBox, const PDFRectangle *cropBox) { Ref *refPage = getCatalog()->getPageRef(pageNo); Object page = getXRef()->fetch(*refPage); if (!page.isDict()) { return false; } Dict *pageDict = page.getDict(); pageDict->remove("MediaBoxssdf"); pageDict->remove("MediaBox"); pageDict->remove("CropBox"); pageDict->remove("ArtBox"); pageDict->remove("BleedBox"); pageDict->remove("TrimBox"); pageDict->remove("Rotate"); Array *mediaBoxArray = new Array(getXRef()); mediaBoxArray->add(Object(mediaBox->x1)); mediaBoxArray->add(Object(mediaBox->y1)); mediaBoxArray->add(Object(mediaBox->x2)); mediaBoxArray->add(Object(mediaBox->y2)); Object mediaBoxObject(mediaBoxArray); Object trimBoxObject = mediaBoxObject.copy(); pageDict->add("MediaBox", std::move(mediaBoxObject)); if (cropBox != nullptr) { Array *cropBoxArray = new Array(getXRef()); cropBoxArray->add(Object(cropBox->x1)); cropBoxArray->add(Object(cropBox->y1)); cropBoxArray->add(Object(cropBox->x2)); cropBoxArray->add(Object(cropBox->y2)); Object cropBoxObject(cropBoxArray); trimBoxObject = cropBoxObject.copy(); pageDict->add("CropBox", std::move(cropBoxObject)); } pageDict->add("TrimBox", std::move(trimBoxObject)); pageDict->add("Rotate", Object(rotate)); getXRef()->setModifiedObject(&page, *refPage); return true; } bool PDFDoc::markPageObjects(Dict *pageDict, XRef *xRef, XRef *countRef, unsigned int numOffset, int oldRefNum, int newRefNum, std::set *alreadyMarkedDicts) { pageDict->remove("OpenAction"); pageDict->remove("Outlines"); pageDict->remove("StructTreeRoot"); for (int n = 0; n < pageDict->getLength(); n++) { const char *key = pageDict->getKey(n); Object value = pageDict->getValNF(n).copy(); if (strcmp(key, "Parent") != 0 && strcmp(key, "Pages") != 0 && strcmp(key, "AcroForm") != 0 && strcmp(key, "Annots") != 0 && strcmp(key, "P") != 0 && strcmp(key, "Root") != 0) { const bool success = markObject(&value, xRef, countRef, numOffset, oldRefNum, newRefNum, alreadyMarkedDicts); if (unlikely(!success)) { return false; } } } return true; } bool PDFDoc::markAnnotations(Object *annotsObj, XRef *xRef, XRef *countRef, unsigned int numOffset, int oldPageNum, int newPageNum, std::set *alreadyMarkedDicts) { bool modified = false; Object annots = annotsObj->fetch(getXRef()); if (annots.isArray()) { Array *array = annots.getArray(); for (int i = array->getLength() - 1; i >= 0; i--) { Object obj1 = array->get(i); if (obj1.isDict()) { Dict *dict = obj1.getDict(); Object type = dict->lookup("Type"); if (type.isName() && strcmp(type.getName(), "Annot") == 0) { const Object &obj2 = dict->lookupNF("P"); if (obj2.isRef()) { if (obj2.getRef().num == oldPageNum) { const Object &obj3 = array->getNF(i); if (obj3.isRef()) { Ref r; r.num = newPageNum; r.gen = 0; dict->set("P", Object(r)); getXRef()->setModifiedObject(&obj1, obj3.getRef()); } } else if (obj2.getRef().num == newPageNum) { continue; } else { Object page = getXRef()->fetch(obj2.getRef()); if (page.isDict()) { Dict *pageDict = page.getDict(); Object pagetype = pageDict->lookup("Type"); if (!pagetype.isName() || strcmp(pagetype.getName(), "Page") != 0) { continue; } } array->remove(i); modified = true; continue; } } } markPageObjects(dict, xRef, countRef, numOffset, oldPageNum, newPageNum, alreadyMarkedDicts); } obj1 = array->getNF(i).copy(); if (obj1.isRef()) { if (obj1.getRef().num + (int)numOffset >= xRef->getNumObjects() || xRef->getEntry(obj1.getRef().num + numOffset)->type == xrefEntryFree) { if (getXRef()->getEntry(obj1.getRef().num)->type == xrefEntryFree) { continue; // already marked as free => should be replaced } xRef->add(obj1.getRef().num + numOffset, obj1.getRef().gen, 0, true); if (getXRef()->getEntry(obj1.getRef().num)->type == xrefEntryCompressed) { xRef->getEntry(obj1.getRef().num + numOffset)->type = xrefEntryCompressed; } } if (obj1.getRef().num + (int)numOffset >= countRef->getNumObjects() || countRef->getEntry(obj1.getRef().num + numOffset)->type == xrefEntryFree) { countRef->add(obj1.getRef().num + numOffset, 1, 0, true); } else { XRefEntry *entry = countRef->getEntry(obj1.getRef().num + numOffset); entry->gen++; } } } } if (annotsObj->isRef()) { if (annotsObj->getRef().num + (int)numOffset >= xRef->getNumObjects() || xRef->getEntry(annotsObj->getRef().num + numOffset)->type == xrefEntryFree) { if (getXRef()->getEntry(annotsObj->getRef().num)->type == xrefEntryFree) { return modified; // already marked as free => should be replaced } xRef->add(annotsObj->getRef().num + numOffset, annotsObj->getRef().gen, 0, true); if (getXRef()->getEntry(annotsObj->getRef().num)->type == xrefEntryCompressed) { xRef->getEntry(annotsObj->getRef().num + numOffset)->type = xrefEntryCompressed; } } if (annotsObj->getRef().num + (int)numOffset >= countRef->getNumObjects() || countRef->getEntry(annotsObj->getRef().num + numOffset)->type == xrefEntryFree) { countRef->add(annotsObj->getRef().num + numOffset, 1, 0, true); } else { XRefEntry *entry = countRef->getEntry(annotsObj->getRef().num + numOffset); entry->gen++; } getXRef()->setModifiedObject(&annots, annotsObj->getRef()); } return modified; } void PDFDoc::markAcroForm(Object *afObj, XRef *xRef, XRef *countRef, unsigned int numOffset, int oldRefNum, int newRefNum) { bool modified = false; Object acroform = afObj->fetch(getXRef()); if (acroform.isDict()) { Dict *dict = acroform.getDict(); for (int i = 0; i < dict->getLength(); i++) { if (strcmp(dict->getKey(i), "Fields") == 0) { Object fields = dict->getValNF(i).copy(); modified = markAnnotations(&fields, xRef, countRef, numOffset, oldRefNum, newRefNum); } else { Object obj = dict->getValNF(i).copy(); markObject(&obj, xRef, countRef, numOffset, oldRefNum, newRefNum); } } } if (afObj->isRef()) { if (afObj->getRef().num + (int)numOffset >= xRef->getNumObjects() || xRef->getEntry(afObj->getRef().num + numOffset)->type == xrefEntryFree) { if (getXRef()->getEntry(afObj->getRef().num)->type == xrefEntryFree) { return; // already marked as free => should be replaced } xRef->add(afObj->getRef().num + numOffset, afObj->getRef().gen, 0, true); if (getXRef()->getEntry(afObj->getRef().num)->type == xrefEntryCompressed) { xRef->getEntry(afObj->getRef().num + numOffset)->type = xrefEntryCompressed; } } if (afObj->getRef().num + (int)numOffset >= countRef->getNumObjects() || countRef->getEntry(afObj->getRef().num + numOffset)->type == xrefEntryFree) { countRef->add(afObj->getRef().num + numOffset, 1, 0, true); } else { XRefEntry *entry = countRef->getEntry(afObj->getRef().num + numOffset); entry->gen++; } if (modified) { getXRef()->setModifiedObject(&acroform, afObj->getRef()); } } return; } unsigned int PDFDoc::writePageObjects(OutStream *outStr, XRef *xRef, unsigned int numOffset, bool combine) { unsigned int objectsCount = 0; // count the number of objects in the XRef(s) unsigned char *fileKey; CryptAlgorithm encAlgorithm; int keyLength; xRef->getEncryptionParameters(&fileKey, &encAlgorithm, &keyLength); for (int n = numOffset; n < xRef->getNumObjects(); n++) { if (xRef->getEntry(n)->type != xrefEntryFree) { Ref ref; ref.num = n; ref.gen = xRef->getEntry(n)->gen; objectsCount++; Object obj = getXRef()->fetch(ref.num - numOffset, ref.gen); Goffset offset = writeObjectHeader(&ref, outStr); if (combine) { writeObject(&obj, outStr, getXRef(), numOffset, nullptr, cryptRC4, 0, 0, 0); } else if (xRef->getEntry(n)->getFlag(XRefEntry::Unencrypted)) { writeObject(&obj, outStr, nullptr, cryptRC4, 0, 0, 0); } else { writeObject(&obj, outStr, fileKey, encAlgorithm, keyLength, ref); } writeObjectFooter(outStr); xRef->add(ref, offset, true); } } return objectsCount; } Outline *PDFDoc::getOutline() { if (!outline) { pdfdocLocker(); // read outline outline = new Outline(catalog->getOutline(), xref, this); } return outline; } std::unique_ptr PDFDoc::ErrorPDFDoc(int errorCode, std::unique_ptr &&fileNameA) { // We cannot call std::make_unique here because the PDFDoc constructor is private PDFDoc *doc = new PDFDoc(); doc->errCode = errorCode; doc->fileName = std::move(fileNameA); return std::unique_ptr(doc); } long long PDFDoc::strToLongLong(const char *s) { long long x, d; const char *p; x = 0; for (p = s; *p && isdigit(*p & 0xff); ++p) { d = *p - '0'; if (x > (LLONG_MAX - d) / 10) { break; } x = 10 * x + d; } return x; } // Read the 'startxref' position. Goffset PDFDoc::getStartXRef(bool tryingToReconstruct) { if (startXRefPos == -1) { if (isLinearized(tryingToReconstruct)) { char buf[linearizationSearchSize + 1]; int c, n, i; str->setPos(0); for (n = 0; n < linearizationSearchSize; ++n) { if ((c = str->getChar()) == EOF) { break; } buf[n] = c; } buf[n] = '\0'; // find end of first obj (linearization dictionary) startXRefPos = 0; for (i = 0; i < n; i++) { if (!strncmp("endobj", &buf[i], 6)) { i += 6; // skip whitespace while (buf[i] && Lexer::isSpace(buf[i])) { ++i; } startXRefPos = i; break; } } } else { char buf[xrefSearchSize + 1]; const char *p; int c, n, i; // read last xrefSearchSize bytes int segnum = 0; int maxXRefSearch = 24576; if (str->getLength() < maxXRefSearch) { maxXRefSearch = static_cast(str->getLength()); } for (; (xrefSearchSize - 16) * segnum < maxXRefSearch; segnum++) { str->setPos((xrefSearchSize - 16) * segnum + xrefSearchSize, -1); for (n = 0; n < xrefSearchSize; ++n) { if ((c = str->getChar()) == EOF) { break; } buf[n] = c; } buf[n] = '\0'; // find startxref for (i = n - 9; i >= 0; --i) { if (!strncmp(&buf[i], "startxref", 9)) { break; } } if (i < 0) { startXRefPos = 0; } else { for (p = &buf[i + 9]; isspace(*p); ++p) { ; } startXRefPos = strToLongLong(p); break; } } } } return startXRefPos; } Goffset PDFDoc::getMainXRefEntriesOffset(bool tryingToReconstruct) { unsigned int mainXRefEntriesOffset = 0; if (isLinearized(tryingToReconstruct)) { mainXRefEntriesOffset = getLinearization()->getMainXRefEntriesOffset(); } return mainXRefEntriesOffset; } int PDFDoc::getNumPages() { if (isLinearized()) { int n; if ((n = getLinearization()->getNumPages())) { return n; } } return catalog->getNumPages(); } Page *PDFDoc::parsePage(int page) { Ref pageRef; pageRef.num = getHints()->getPageObjectNum(page); if (!pageRef.num) { error(errSyntaxWarning, -1, "Failed to get object num from hint tables for page {0:d}", page); return nullptr; } // check for bogus ref - this can happen in corrupted PDF files if (pageRef.num < 0 || pageRef.num >= xref->getNumObjects()) { error(errSyntaxWarning, -1, "Invalid object num ({0:d}) for page {1:d}", pageRef.num, page); return nullptr; } pageRef.gen = xref->getEntry(pageRef.num)->gen; Object obj = xref->fetch(pageRef); if (!obj.isDict("Page")) { error(errSyntaxWarning, -1, "Object ({0:d} {1:d}) is not a pageDict", pageRef.num, pageRef.gen); return nullptr; } Dict *pageDict = obj.getDict(); return new Page(this, page, std::move(obj), pageRef, new PageAttrs(nullptr, pageDict), catalog->getForm()); } Page *PDFDoc::getPage(int page) { if ((page < 1) || page > getNumPages()) { return nullptr; } if (isLinearized() && checkLinearization()) { pdfdocLocker(); if (!pageCache) { pageCache = (Page **)gmallocn(getNumPages(), sizeof(Page *)); for (int i = 0; i < getNumPages(); i++) { pageCache[i] = nullptr; } } if (!pageCache[page - 1]) { pageCache[page - 1] = parsePage(page); } if (pageCache[page - 1]) { return pageCache[page - 1]; } else { error(errSyntaxWarning, -1, "Failed parsing page {0:d} using hint tables", page); } } return catalog->getPage(page); } bool PDFDoc::hasJavascript() { JSInfo jsInfo(this); jsInfo.scanJS(getNumPages(), true); return jsInfo.containsJS(); } bool PDFDoc::sign(const std::string &saveFilename, const std::string &certNickname, const std::string &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) { ::Page *destPage = getPage(page); if (destPage == nullptr) { return false; } Ref imageResourceRef = Ref::INVALID(); if (!imagePath.empty()) { imageResourceRef = ImageEmbeddingUtils::embed(xref, imagePath); if (imageResourceRef == Ref::INVALID()) { return false; } } Form *form = catalog->getCreateForm(); const std::string pdfFontName = form->findPdfFontNameToUseForSigning(); if (pdfFontName.empty()) { return false; } 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)); Array *rectArray = new Array(getXRef()); rectArray->add(Object(rect.x1)); rectArray->add(Object(rect.y1)); rectArray->add(Object(rect.x2)); rectArray->add(Object(rect.y2)); annotObj.dictSet("Rect", Object(rectArray)); const std::string daStr = da.toAppearanceString(); annotObj.dictSet("DA", Object(new GooString(daStr))); 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); 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, field->getObj(), &refObj, field.get()); signatureAnnot->setFlags(signatureAnnot->getFlags() | Annot::flagPrint | Annot::flagLocked | 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)); signatureAnnot->generateFieldAppearance(); signatureAnnot->updateAppearanceStream(); FormWidget *formWidget = field->getWidget(field->getNumWidgets() - 1); formWidget->setWidgetAnnotation(signatureAnnot); destPage->addAnnot(signatureAnnot); std::unique_ptr border(new AnnotBorderArray()); border->setWidth(borderWidth); signatureAnnot->setBorder(std::move(border)); FormWidgetSignature *fws = dynamic_cast(formWidget); if (fws) { const bool 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 = field->getObj()->dictLookupNF("V"); if (vRefObj.isRef()) { getXRef()->removeIndirectObject(vRefObj.getRef()); } destPage->removeAnnot(signatureAnnot); catalog->removeFormFromAcroForm(ref); getXRef()->removeIndirectObject(ref); return res; } return false; } poppler-24.02.0/poppler/PDFDoc.h000066400000000000000000000477701455701731300162560ustar00rootroot00000000000000//======================================================================== // // PDFDoc.h // // 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) 2005, 2006, 2008 Brad Hards // Copyright (C) 2005, 2009, 2014, 2015, 2017-2022 Albert Astals Cid // Copyright (C) 2008 Julien Rebetez // Copyright (C) 2008 Pino Toscano // Copyright (C) 2008 Carlos Garcia Campos // Copyright (C) 2009 Eric Toombs // Copyright (C) 2009 Kovid Goyal // Copyright (C) 2010, 2014 Hib Eris // Copyright (C) 2010 Srinivas Adicherla // Copyright (C) 2011, 2013, 2014, 2016 Thomas Freitag // Copyright (C) 2012 Fabio D'Urso // Copyright (C) 2013, 2017 Adrian Johnson // Copyright (C) 2013, 2018 Adam Reichold // Copyright (C) 2013 Adrian Perez de Castro // Copyright (C) 2015 André Guerreiro // Copyright (C) 2015 André Esser // 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-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 2023 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 // //======================================================================== #ifndef PDFDOC_H #define PDFDOC_H #include #include #include #include "poppler-config.h" #include "poppler_private_export.h" #include "XRef.h" #include "Catalog.h" #include "Page.h" #include "Annot.h" #include "ErrorCodes.h" #include "Form.h" #include "OptionalContent.h" #include "Stream.h" class GooString; class GooFile; class BaseStream; class OutputDev; class Links; class LinkAction; class LinkDest; class Outline; class Linearization; class SecurityHandler; class Hints; class StructTreeRoot; enum PDFWriteMode { writeStandard, writeForceRewrite, writeForceIncremental }; enum PDFSubtype { subtypeNull, subtypePDFA, subtypePDFE, subtypePDFUA, subtypePDFVT, subtypePDFX, subtypeNone }; enum PDFSubtypePart { subtypePartNull, subtypePart1, subtypePart2, subtypePart3, subtypePart4, subtypePart5, subtypePart6, subtypePart7, subtypePart8, subtypePartNone }; enum PDFSubtypeConformance { subtypeConfNull, subtypeConfA, subtypeConfB, subtypeConfG, subtypeConfN, subtypeConfP, subtypeConfPG, subtypeConfU, subtypeConfNone }; //------------------------------------------------------------------------ // PDFDoc //------------------------------------------------------------------------ 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 = {}); #ifdef _WIN32 PDFDoc(wchar_t *fileNameA, int fileNameLen, const std::optional &ownerPassword = {}, const std::optional &userPassword = {}, void *guiDataA = nullptr, const std::function &xrefReconstructedCallback = {}); #endif explicit PDFDoc(BaseStream *strA, const std::optional &ownerPassword = {}, const std::optional &userPassword = {}, void *guiDataA = nullptr, const std::function &xrefReconstructedCallback = {}); ~PDFDoc(); PDFDoc(const PDFDoc &) = delete; PDFDoc &operator=(const PDFDoc &) = delete; static std::unique_ptr ErrorPDFDoc(int errorCode, std::unique_ptr &&fileNameA); // Was PDF document successfully opened? bool isOk() const { return ok; } // Get the error code (if isOk() returns false). int getErrorCode() const { return errCode; } // Get the error code returned by fopen() (if getErrorCode() == // errOpenFile). int getFopenErrno() const { return fopenErrno; } // Get file name. const GooString *getFileName() const { return fileName.get(); } #ifdef _WIN32 wchar_t *getFileNameU() { return fileNameU; } #endif // Get the linearization table. Linearization *getLinearization(); bool checkLinearization(); // Get the xref table. XRef *getXRef() const { return xref; } // Get catalog. Catalog *getCatalog() const { return catalog; } // Get optional content configuration OCGs *getOptContentConfig() const { return catalog->getOptContentConfig(); } // Get base stream. 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; } // 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(); } // Return the structure tree root object. const StructTreeRoot *getStructTreeRoot() const { return catalog->getStructTreeRoot(); } // Get page. Page *getPage(int page); // Display a page. void displayPage(OutputDev *out, int page, double hDPI, double vDPI, int rotate, bool useMediaBox, bool crop, bool printing, bool (*abortCheckCbk)(void *data) = nullptr, void *abortCheckCbkData = nullptr, bool (*annotDisplayDecideCbk)(Annot *annot, void *user_data) = nullptr, void *annotDisplayDecideCbkData = nullptr, bool copyXRef = false); // Display a range of pages. void displayPages(OutputDev *out, int firstPage, int lastPage, double hDPI, double vDPI, int rotate, bool useMediaBox, bool crop, bool printing, bool (*abortCheckCbk)(void *data) = nullptr, void *abortCheckCbkData = nullptr, bool (*annotDisplayDecideCbk)(Annot *annot, void *user_data) = nullptr, void *annotDisplayDecideCbkData = nullptr); // Display part of a page. void displayPageSlice(OutputDev *out, int page, double hDPI, double vDPI, int rotate, bool useMediaBox, bool crop, bool printing, int sliceX, int sliceY, int sliceW, int sliceH, bool (*abortCheckCbk)(void *data) = nullptr, void *abortCheckCbkData = nullptr, bool (*annotDisplayDecideCbk)(Annot *annot, void *user_data) = nullptr, void *annotDisplayDecideCbkData = nullptr, bool copyXRef = false); // Find a page, given its object ID. Returns page number, or 0 if // not found. int findPage(const Ref ref) { return catalog->findPage(ref); } // Returns the links for the current page, transferring ownership to // the caller. std::unique_ptr getLinks(int page); // 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); } // Process the links for a page. void processLinks(OutputDev *out, int page); // Return the outline object. Outline *getOutline(); // Is the file encrypted? 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); } // 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(); } // Remove the document's Info dictionary and update the trailer dictionary. 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); // 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); } // 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"); } // Return the PDF subtype, part, and conformance 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 getPDFMinorVersion() const { const int catalogMajorVersion = catalog->getPDFMajorVersion(); if (catalogMajorVersion > headerPdfMajorVersion) { return catalog->getPDFMinorVersion(); } else if (headerPdfMajorVersion > catalogMajorVersion) { return headerPdfMinorVersion; } else { return std::max(headerPdfMinorVersion, catalog->getPDFMinorVersion()); } } // Return the PDF ID in the trailer dictionary (if any). bool getID(GooString *permanent_id, GooString *update_id) const; // Save one page with another name. int savePageAs(const GooString &name, int pageNo); // Save this file with another name. int saveAs(const GooString &name, PDFWriteMode mode = writeStandard); // Save this file in the given output stream. int saveAs(OutStream *outStr, PDFWriteMode mode = writeStandard); // Save this file with another name without saving changes int saveWithoutChangesAs(const GooString &name); // 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); bool markAnnotations(Object *annots, XRef *xRef, XRef *countRef, unsigned int numOffset, int oldPageNum, int newPageNum, std::set *alreadyMarkedDicts = nullptr); void markAcroForm(Object *afObj, XRef *xRef, XRef *countRef, unsigned int numOffset, int oldRefNum, int newRefNum); // write all objects used by pageDict to outStr unsigned int writePageObjects(OutStream *outStr, XRef *xRef, unsigned int numOffset, bool combine = false); static void writeObject(Object *obj, OutStream *outStr, XRef *xref, unsigned int numOffset, unsigned char *fileKey, CryptAlgorithm encAlgorithm, int keyLength, int objNum, int objGen, std::set *alreadyWrittenDicts = nullptr); static void writeObject(Object *obj, OutStream *outStr, XRef *xref, unsigned int numOffset, unsigned char *fileKey, CryptAlgorithm encAlgorithm, int keyLength, Ref ref, std::set *alreadyWrittenDicts = nullptr); static void writeHeader(OutStream *outStr, int major, int minor); static Object createTrailerDict(int uxrefSize, bool incrUpdate, Goffset startxRef, Ref *root, XRef *xRef, const char *fileName, Goffset fileSize); static void writeXRefTableTrailer(Object &&trailerDict, XRef *uxref, bool writeAllEntries, Goffset uxrefOffset, OutStream *outStr, XRef *xRef); static void writeXRefStreamTrailer(Object &&trailerDict, XRef *uxref, Ref *uxrefStreamRef, Goffset uxrefOffset, OutStream *outStr, XRef *xRef); // scans the PDF and returns whether it contains any javascript bool hasJavascript(); // Arguments signatureText and signatureTextLeft are UTF-16 big endian strings with BOM. // 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 std::string &saveFilename, const std::string &certNickname, const std::string &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 = {}); 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 static Goffset writeObjectHeader(Ref *ref, OutStream *outStr); static void writeObjectFooter(OutStream *outStr); inline void writeObject(Object *obj, OutStream *outStr, unsigned char *fileKey, CryptAlgorithm encAlgorithm, int keyLength, int objNum, int objGen) { 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); } 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); static void writeString(const GooString *s, OutStream *outStr, const unsigned char *fileKey, CryptAlgorithm encAlgorithm, int keyLength, Ref ref); void saveIncrementalUpdate(OutStream *outStr); void saveCompleteRewrite(OutStream *outStr); Page *parsePage(int page); // Get hints. Hints *getHints(); PDFDoc(); bool setup(const std::optional &ownerPassword, const std::optional &userPassword, const std::function &xrefReconstructedCallback); bool checkFooter(); void checkHeader(); bool checkEncryption(const std::optional &ownerPassword, const std::optional &userPassword); void extractPDFSubtype(); // Get the offset of the start xref table. Goffset getStartXRef(bool tryingToReconstruct = false); // Get the offset of the entries in the main XRef table of a // linearized document (0 for non linearized documents). Goffset getMainXRefEntriesOffset(bool tryingToReconstruct = false); long long strToLongLong(const char *s); std::unique_ptr fileName; #ifdef _WIN32 wchar_t *fileNameU = nullptr; #endif std::unique_ptr file; BaseStream *str = nullptr; void *guiData = nullptr; int headerPdfMajorVersion; int headerPdfMinorVersion; PDFSubtype pdfSubtype; PDFSubtypePart pdfPart; PDFSubtypeConformance pdfConformance; Linearization *linearization = nullptr; // linearizationState = 0: unchecked // linearizationState = 1: checked and valid // linearizationState = 2: checked and invalid int linearizationState; XRef *xref = nullptr; SecurityHandler *secHdlr = nullptr; Catalog *catalog = nullptr; Hints *hints = nullptr; Outline *outline = nullptr; Page **pageCache = nullptr; bool ok = false; int errCode = errNone; // If there is an error opening the PDF file with fopen() in the constructor, // then the POSIX errno will be here. int fopenErrno; Goffset startXRefPos = -1; // offset of last xref table mutable std::recursive_mutex mutex; }; #endif poppler-24.02.0/poppler/PDFDocBuilder.cc000066400000000000000000000005441455701731300177070ustar00rootroot00000000000000//======================================================================== // // PDFDocBuilder.cc // // This file is licensed under the GPLv2 or later // // Copyright 2020 Albert Astals Cid // //======================================================================== #include "PDFDocBuilder.h" PDFDocBuilder::~PDFDocBuilder() = default; poppler-24.02.0/poppler/PDFDocBuilder.h000066400000000000000000000030261455701731300175470ustar00rootroot00000000000000//======================================================================== // // PDFDocBuilder.h // // This file is licensed under the GPLv2 or later // // Copyright 2010 Hib Eris // Copyright 2010, 2018, 2020, 2022 Albert Astals Cid // Copyright 2021 Oliver Sander // //======================================================================== #ifndef PDFDOCBUILDER_H #define PDFDOCBUILDER_H #include #include "PDFDoc.h" class GooString; //------------------------------------------------------------------------ // PDFDocBuilder // // PDFDocBuilder is an abstract class that specifies the interface for // constructing PDFDocs. //------------------------------------------------------------------------ class PDFDocBuilder { public: PDFDocBuilder() = default; virtual ~PDFDocBuilder(); PDFDocBuilder(const PDFDocBuilder &) = delete; PDFDocBuilder &operator=(const PDFDocBuilder &) = delete; // 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; // Returns true if the builder supports building a PDFDoc from the URI. virtual bool supports(const GooString &uri) = 0; }; #endif /* PDFDOCBUILDER_H */ poppler-24.02.0/poppler/PDFDocEncoding.cc000066400000000000000000000102101455701731300200360ustar00rootroot00000000000000//======================================================================== // // PDFDocEncoding.cc // // Copyright 2002-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) 2008 Michael Vrable // Copyright (C) 2019 Volker Krause // // 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 "PDFDocEncoding.h" // Mapping of PDFDocEncoding (used to represent text values such as document // metadata or annotation text) to Unicode codepoints. Not all 8-bit values in // PDFDocEncoding are defined; undefined bytes are mapped to U+FFFD (Unicode // replacement character). // // PDFDocEncoding is only directly defined for printable characters, but some // control characters such as carriage return will still be used. We define // mappings of the standard whitespace control characters (tabs, newlines) to // the corresponding Unicode values. Other control characters are left // undefined. const Unicode pdfDocEncoding[256] = { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, // 00 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, // 10 0x02d8, 0x02c7, 0x02c6, 0x02d9, 0x02dd, 0x02db, 0x02da, 0x02dc, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, // 20 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, // 30 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, // 40 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, // 50 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, // 60 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, // 70 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0xfffd, 0x2022, 0x2020, 0x2021, 0x2026, 0x2014, 0x2013, 0x0192, 0x2044, // 80 0x2039, 0x203a, 0x2212, 0x2030, 0x201e, 0x201c, 0x201d, 0x2018, 0x2019, 0x201a, 0x2122, 0xfb01, 0xfb02, 0x0141, 0x0152, 0x0160, // 90 0x0178, 0x017d, 0x0131, 0x0142, 0x0153, 0x0161, 0x017e, 0xfffd, 0x20ac, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, // a0 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0xfffd, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, // b0 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, // c0 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, // d0 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, // e0 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, // f0 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff }; poppler-24.02.0/poppler/PDFDocEncoding.h000066400000000000000000000022651455701731300177130ustar00rootroot00000000000000//======================================================================== // // PDFDocEncoding.h // // Copyright 2002-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) 2007 Adrian Johnson // Copyright (C) 2019 Volker Krause // Copyright (C) 2020 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 // //======================================================================== #ifndef PDFDOCENCODING_H #define PDFDOCENCODING_H #include #include "CharTypes.h" #include "poppler_private_export.h" class GooString; extern const Unicode POPPLER_PRIVATE_EXPORT pdfDocEncoding[256]; char POPPLER_PRIVATE_EXPORT *pdfDocEncodingToUTF16(const std::string &orig, int *length); #endif poppler-24.02.0/poppler/PDFDocFactory.cc000066400000000000000000000043151455701731300177300ustar00rootroot00000000000000//======================================================================== // // PDFDocFactory.cc // // This file is licensed under the GPLv2 or later // // Copyright 2010 Hib Eris // Copyright 2010, 2022 Albert Astals Cid // Copyright 2017 Adrian Johnson // Copyright 2018 Adam Reichold // Copyright 2019, 2021 Oliver Sander // Copyright 2021 Christian Persch // //======================================================================== #include #include "PDFDocFactory.h" #include "goo/GooString.h" #include "PDFDoc.h" #include "LocalPDFDocBuilder.h" #include "FDPDFDocBuilder.h" #ifdef ENABLE_LIBCURL # include "CurlPDFDocBuilder.h" #endif #include "ErrorCodes.h" //------------------------------------------------------------------------ // PDFDocFactory //------------------------------------------------------------------------ PDFDocFactory::PDFDocFactory(std::vector *pdfDocBuilders) { if (pdfDocBuilders) { builders = pdfDocBuilders; } else { builders = new std::vector(); } builders->push_back(new LocalPDFDocBuilder()); builders->push_back(new FileDescriptorPDFDocBuilder()); #ifdef ENABLE_LIBCURL builders->push_back(new CurlPDFDocBuilder()); #endif } PDFDocFactory::~PDFDocFactory() { if (builders) { for (auto entry : *builders) { delete entry; } delete builders; } } std::unique_ptr PDFDocFactory::createPDFDoc(const GooString &uri, const std::optional &ownerPassword, const std::optional &userPassword, void *guiDataA) { for (int i = builders->size() - 1; i >= 0; i--) { PDFDocBuilder *builder = (*builders)[i]; if (builder->supports(uri)) { return builder->buildPDFDoc(uri, ownerPassword, userPassword, guiDataA); } } error(errInternal, -1, "Cannot handle URI '{0:t}'.", &uri); return PDFDoc::ErrorPDFDoc(errOpenFile, std::unique_ptr(uri.copy())); } void PDFDocFactory::registerPDFDocBuilder(PDFDocBuilder *pdfDocBuilder) { builders->push_back(pdfDocBuilder); } poppler-24.02.0/poppler/PDFDocFactory.h000066400000000000000000000037211455701731300175720ustar00rootroot00000000000000//======================================================================== // // PDFDocFactory.h // // This file is licensed under the GPLv2 or later // // Copyright 2010 Hib Eris // Copyright 2010, 2018, 2021, 2022 Albert Astals Cid // Copyright 2019, 2021 Oliver Sander // //======================================================================== #ifndef PDFDOCFACTORY_H #define PDFDOCFACTORY_H #include #include "PDFDoc.h" #include "poppler_private_export.h" class GooString; class PDFDocBuilder; //------------------------------------------------------------------------ // PDFDocFactory // // PDFDocFactory allows the construction of PDFDocs from different URIs. // // By default, it supports local files, 'file://' and 'fd:0' (stdin). When // compiled with libcurl, it also supports 'http://' and 'https://'. // // You can extend the supported URIs by giving a list of PDFDocBuilders to // the constructor, or by registering a new PDFDocBuilder afterwards. //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT PDFDocFactory { public: explicit PDFDocFactory(std::vector *pdfDocBuilders = nullptr); ~PDFDocFactory(); PDFDocFactory(const PDFDocFactory &) = delete; PDFDocFactory &operator=(const PDFDocFactory &) = delete; // 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); // Extend supported URIs with the ones from the PDFDocBuilder. void registerPDFDocBuilder(PDFDocBuilder *pdfDocBuilder); private: std::vector *builders; }; #endif /* PDFDOCFACTORY_H */ poppler-24.02.0/poppler/PSOutputDev.cc000066400000000000000000010443471455701731300175550ustar00rootroot00000000000000//======================================================================== // // PSOutputDev.cc // // Copyright 1996-2013 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) 2005 Martin Kretzschmar // Copyright (C) 2005, 2006 Kristian Høgsberg // Copyright (C) 2006-2009, 2011-2013, 2015-2022 Albert Astals Cid // Copyright (C) 2006 Jeff Muizelaar // Copyright (C) 2007, 2008 Brad Hards // Copyright (C) 2008, 2009 Koji Otani // Copyright (C) 2008, 2010 Hib Eris // Copyright (C) 2009-2013 Thomas Freitag // Copyright (C) 2009 Till Kamppeter // Copyright (C) 2009 Carlos Garcia Campos // Copyright (C) 2009, 2011, 2012, 2014-2017, 2019, 2020 William Bader // Copyright (C) 2009 Kovid Goyal // Copyright (C) 2009-2011, 2013-2015, 2017, 2020 Adrian Johnson // Copyright (C) 2012, 2014 Fabio D'Urso // Copyright (C) 2012 Lu Wang // Copyright (C) 2014 Till Kamppeter // Copyright (C) 2015 Marek Kasik // Copyright (C) 2016 Caolán McNamara // 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 Philipp Knechtges // Copyright (C) 2019, 2021 Christian Persch // Copyright (C) 2019, 2021-2023 Oliver Sander // Copyright (C) 2020, 2021 Philipp Knechtges // Copyright (C) 2021 Hubert Figuiere // Copyright (C) 2023 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 // //======================================================================== #include #include #include #include #include #include #include #include #include #include "goo/GooString.h" #include "poppler-config.h" #include "GlobalParams.h" #include "Object.h" #include "Error.h" #include "Function.h" #include "Gfx.h" #include "GfxState.h" #include "GfxFont.h" #include "UnicodeMap.h" #include #include #include "Catalog.h" #include "Page.h" #include "Stream.h" #include "FlateEncoder.h" #ifdef ENABLE_ZLIB_UNCOMPRESS # include "FlateStream.h" #endif #include "Annot.h" #include "XRef.h" #include "PreScanOutputDev.h" #include "FileSpec.h" #include "CharCodeToUnicode.h" #include "splash/Splash.h" #include "splash/SplashBitmap.h" #include "SplashOutputDev.h" #include "PSOutputDev.h" #include "PDFDoc.h" #ifdef USE_CMS # include #endif // the MSVC math.h doesn't define this #ifndef M_PI # define M_PI 3.14159265358979323846 #endif //------------------------------------------------------------------------ // Max size of a slice when rasterizing pages, in pixels. #define rasterizationSliceSize 20000000 //------------------------------------------------------------------------ // PostScript prolog and setup //------------------------------------------------------------------------ // The '~' escapes mark prolog code that is emitted only in certain // levels: // // ~[123][sn] // ^ ^----- s=psLevel*Sep, n=psLevel* // +----- 1=psLevel1*, 2=psLevel2*, 3=psLevel3* static const char *prolog[] = { "/xpdf 75 dict def xpdf begin", "% PDF special state", "/pdfDictSize 15 def", "~1sn", "/pdfStates 64 array def", " 0 1 63 {", " pdfStates exch pdfDictSize dict", " dup /pdfStateIdx 3 index put", " put", " } for", "~123sn", "/pdfSetup {", " /setpagedevice where {", " pop 2 dict begin", " /Policies 1 dict dup begin /PageSize 6 def end def", " { /Duplex true def } if", " currentdict end setpagedevice", " } {", " pop", " } ifelse", "} def", "/pdfSetupPaper {", " % Change paper size, but only if different from previous paper size otherwise", " % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size", " % so we use the same when checking if the size changes.", " /setpagedevice where {", " pop currentpagedevice", " /PageSize known {", " 2 copy", " currentpagedevice /PageSize get aload pop", " exch 4 1 roll", " sub abs 5 gt", " 3 1 roll", " sub abs 5 gt", " or", " } {", " true", " } ifelse", " {", " 2 array astore", " 2 dict begin", " /PageSize exch def", " /ImagingBBox null def", " currentdict end", " setpagedevice", " } {", " pop pop", " } ifelse", " } {", " pop", " } ifelse", "} def", "~1sn", "/pdfOpNames [", " /pdfFill /pdfStroke /pdfLastFill /pdfLastStroke", " /pdfTextMat /pdfFontSize /pdfCharSpacing /pdfTextRender /pdfPatternCS", " /pdfTextRise /pdfWordSpacing /pdfHorizScaling /pdfTextClipPath", "] def", "~123sn", "/pdfStartPage {", "~1sn", " pdfStates 0 get begin", "~23sn", " pdfDictSize dict begin", "~23n", " /pdfFillCS [] def", " /pdfFillXform {} def", " /pdfStrokeCS [] def", " /pdfStrokeXform {} def", "~1n", " /pdfFill 0 def", " /pdfStroke 0 def", "~1s", " /pdfFill [0 0 0 1] def", " /pdfStroke [0 0 0 1] def", "~23sn", " /pdfFill [0] def", " /pdfStroke [0] def", " /pdfFillOP false def", " /pdfStrokeOP false def", "~3sn", " /pdfOPM false def", "~123sn", " /pdfLastFill false def", " /pdfLastStroke false def", " /pdfTextMat [1 0 0 1 0 0] def", " /pdfFontSize 0 def", " /pdfCharSpacing 0 def", " /pdfTextRender 0 def", " /pdfPatternCS false def", " /pdfTextRise 0 def", " /pdfWordSpacing 0 def", " /pdfHorizScaling 1 def", " /pdfTextClipPath [] def", "} def", "/pdfEndPage { end } def", "~23s", "% separation convention operators", "/findcmykcustomcolor where {", " pop", "}{", " /findcmykcustomcolor { 5 array astore } def", "} ifelse", "/setcustomcolor where {", " pop", "}{", " /setcustomcolor {", " exch", " [ exch /Separation exch dup 4 get exch /DeviceCMYK exch", " 0 4 getinterval cvx", " [ exch /dup load exch { mul exch dup } /forall load", " /pop load dup ] cvx", " ] setcolorspace setcolor", " } def", "} ifelse", "/customcolorimage where {", " pop", "}{", " /customcolorimage {", " gsave", " [ exch /Separation exch dup 4 get exch /DeviceCMYK exch", " 0 4 getinterval", " [ exch /dup load exch { mul exch dup } /forall load", " /pop load dup ] cvx", " ] setcolorspace", " 10 dict begin", " /ImageType 1 def", " /DataSource exch def", " /ImageMatrix exch def", " /BitsPerComponent exch def", " /Height exch def", " /Width exch def", " /Decode [1 0] def", " currentdict end", " image", " grestore", " } def", "} ifelse", "~123sn", "% PDF color state", "~1n", "/g { dup /pdfFill exch def setgray", " /pdfLastFill true def /pdfLastStroke false def } def", "/G { dup /pdfStroke exch def setgray", " /pdfLastStroke true def /pdfLastFill false def } def", "/fCol {", " pdfLastFill not {", " pdfFill setgray", " /pdfLastFill true def /pdfLastStroke false def", " } if", "} def", "/sCol {", " pdfLastStroke not {", " pdfStroke setgray", " /pdfLastStroke true def /pdfLastFill false def", " } if", "} def", "~1s", "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor", " /pdfLastFill true def /pdfLastStroke false def } def", "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor", " /pdfLastStroke true def /pdfLastFill false def } def", "/fCol {", " pdfLastFill not {", " pdfFill aload pop setcmykcolor", " /pdfLastFill true def /pdfLastStroke false def", " } if", "} def", "/sCol {", " pdfLastStroke not {", " pdfStroke aload pop setcmykcolor", " /pdfLastStroke true def /pdfLastFill false def", " } if", "} def", "~3n", "/opm { dup /pdfOPM exch def", " /setoverprintmode where{pop setoverprintmode}{pop}ifelse } def", "~23n", "/cs { /pdfFillXform exch def dup /pdfFillCS exch def", " setcolorspace } def", "/CS { /pdfStrokeXform exch def dup /pdfStrokeCS exch def", " setcolorspace } def", "/sc { pdfLastFill not { pdfFillCS setcolorspace } if", " dup /pdfFill exch def aload pop pdfFillXform setcolor", " /pdfLastFill true def /pdfLastStroke false def } def", "/SC { pdfLastStroke not { pdfStrokeCS setcolorspace } if", " dup /pdfStroke exch def aload pop pdfStrokeXform setcolor", " /pdfLastStroke true def /pdfLastFill false def } def", "/op { /pdfFillOP exch def", " pdfLastFill { pdfFillOP setoverprint } if } def", "/OP { /pdfStrokeOP exch def", " pdfLastStroke { pdfStrokeOP setoverprint } if } def", "/fCol {", " pdfLastFill not {", " pdfFillCS setcolorspace", " pdfFill aload pop pdfFillXform setcolor", " pdfFillOP setoverprint", " /pdfLastFill true def /pdfLastStroke false def", " } if", "} def", "/sCol {", " pdfLastStroke not {", " pdfStrokeCS setcolorspace", " pdfStroke aload pop pdfStrokeXform setcolor", " pdfStrokeOP setoverprint", " /pdfLastStroke true def /pdfLastFill false def", " } if", "} def", "~3s", "/opm { dup /pdfOPM exch def", " /setoverprintmode where{pop setoverprintmode}{pop}ifelse } def", "~23s", "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor", " /pdfLastFill true def /pdfLastStroke false def } def", "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor", " /pdfLastStroke true def /pdfLastFill false def } def", "/ck { 6 copy 6 array astore /pdfFill exch def", " findcmykcustomcolor exch setcustomcolor", " /pdfLastFill true def /pdfLastStroke false def } def", "/CK { 6 copy 6 array astore /pdfStroke exch def", " findcmykcustomcolor exch setcustomcolor", " /pdfLastStroke true def /pdfLastFill false def } def", "/op { /pdfFillOP exch def", " pdfLastFill { pdfFillOP setoverprint } if } def", "/OP { /pdfStrokeOP exch def", " pdfLastStroke { pdfStrokeOP setoverprint } if } def", "/fCol {", " pdfLastFill not {", " pdfFill aload length 4 eq {", " setcmykcolor", " }{", " findcmykcustomcolor exch setcustomcolor", " } ifelse", " pdfFillOP setoverprint", " /pdfLastFill true def /pdfLastStroke false def", " } if", "} def", "/sCol {", " pdfLastStroke not {", " pdfStroke aload length 4 eq {", " setcmykcolor", " }{", " findcmykcustomcolor exch setcustomcolor", " } ifelse", " pdfStrokeOP setoverprint", " /pdfLastStroke true def /pdfLastFill false def", " } if", "} def", "~123sn", "% build a font", "/pdfMakeFont {", " 4 3 roll findfont", " 4 2 roll matrix scale makefont", " dup length dict begin", " { 1 index /FID ne { def } { pop pop } ifelse } forall", " /Encoding exch def", " currentdict", " end", " definefont pop", "} def", "/pdfMakeFont16 {", " exch findfont", " dup length dict begin", " { 1 index /FID ne { def } { pop pop } ifelse } forall", " /WMode exch def", " currentdict", " end", " definefont pop", "} def", "~3sn", "/pdfMakeFont16L3 {", " 1 index /CIDFont resourcestatus {", " pop pop 1 index /CIDFont findresource /CIDFontType known", " } {", " false", " } ifelse", " {", " 0 eq { /Identity-H } { /Identity-V } ifelse", " exch 1 array astore composefont pop", " } {", " pdfMakeFont16", " } ifelse", "} def", "~123sn", "% graphics state operators", "~1sn", "/q {", " gsave", " pdfOpNames length 1 sub -1 0 { pdfOpNames exch get load } for", " pdfStates pdfStateIdx 1 add get begin", " pdfOpNames { exch def } forall", "} def", "/Q { end grestore } def", "~23sn", "/q { gsave pdfDictSize dict begin } def", "/Q {", " end grestore", " /pdfLastFill where {", " pop", " pdfLastFill {", " pdfFillOP setoverprint", " } {", " pdfStrokeOP setoverprint", " } ifelse", " } if", "~3sn", " /pdfOPM where {", " pop", " pdfOPM /setoverprintmode where{pop setoverprintmode}{pop}ifelse ", " } if", "~23sn", "} def", "~123sn", "/cm { concat } def", "/d { setdash } def", "/i { setflat } def", "/j { setlinejoin } def", "/J { setlinecap } def", "/M { setmiterlimit } def", "/w { setlinewidth } def", "% path segment operators", "/m { moveto } def", "/l { lineto } def", "/c { curveto } def", "/re { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto", " neg 0 rlineto closepath } def", "/h { closepath } def", "% path painting operators", "/S { sCol stroke } def", "/Sf { fCol stroke } def", "/f { fCol fill } def", "/f* { fCol eofill } def", "% clipping operators", "/W { clip newpath } def", "/W* { eoclip newpath } def", "/Ws { strokepath clip newpath } def", "% text state operators", "/Tc { /pdfCharSpacing exch def } def", "/Tf { dup /pdfFontSize exch def", " dup pdfHorizScaling mul exch matrix scale", " pdfTextMat matrix concatmatrix dup 4 0 put dup 5 0 put", " exch findfont exch makefont setfont } def", "/Tr { /pdfTextRender exch def } def", "/Tp { /pdfPatternCS exch def } def", "/Ts { /pdfTextRise exch def } def", "/Tw { /pdfWordSpacing exch def } def", "/Tz { /pdfHorizScaling exch def } def", "% text positioning operators", "/Td { pdfTextMat transform moveto } def", "/Tm { /pdfTextMat exch def } def", "% text string operators", "/xyshow where {", " pop", " /xyshow2 {", " dup length array", " 0 2 2 index length 1 sub {", " 2 index 1 index 2 copy get 3 1 roll 1 add get", " pdfTextMat dtransform", " 4 2 roll 2 copy 6 5 roll put 1 add 3 1 roll dup 4 2 roll put", " } for", " exch pop", " xyshow", " } def", "}{", " /xyshow2 {", " currentfont /FontType get 0 eq {", " 0 2 3 index length 1 sub {", " currentpoint 4 index 3 index 2 getinterval show moveto", " 2 copy get 2 index 3 2 roll 1 add get", " pdfTextMat dtransform rmoveto", " } for", " } {", " 0 1 3 index length 1 sub {", " currentpoint 4 index 3 index 1 getinterval show moveto", " 2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get", " pdfTextMat dtransform rmoveto", " } for", " } ifelse", " pop pop", " } def", "} ifelse", "/cshow where {", " pop", " /xycp {", // xycharpath " 0 3 2 roll", " {", " pop pop currentpoint 3 2 roll", " 1 string dup 0 4 3 roll put false charpath moveto", " 2 copy get 2 index 2 index 1 add get", " pdfTextMat dtransform rmoveto", " 2 add", " } exch cshow", " pop pop", " } def", "}{", " /xycp {", // xycharpath " currentfont /FontType get 0 eq {", " 0 2 3 index length 1 sub {", " currentpoint 4 index 3 index 2 getinterval false charpath moveto", " 2 copy get 2 index 3 2 roll 1 add get", " pdfTextMat dtransform rmoveto", " } for", " } {", " 0 1 3 index length 1 sub {", " currentpoint 4 index 3 index 1 getinterval false charpath moveto", " 2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get", " pdfTextMat dtransform rmoveto", " } for", " } ifelse", " pop pop", " } def", "} ifelse", "/Tj {", " fCol", // because stringwidth has to draw Type 3 chars " 0 pdfTextRise pdfTextMat dtransform rmoveto", " currentpoint 4 2 roll", " pdfTextRender 1 and 0 eq {", " 2 copy xyshow2", " } if", " pdfTextRender 3 and dup 1 eq exch 2 eq or {", " 3 index 3 index moveto", " 2 copy", " currentfont /FontType get 3 eq { fCol } { sCol } ifelse", " xycp currentpoint stroke moveto", " } if", " pdfTextRender 4 and 0 ne {", " 4 2 roll moveto xycp", " /pdfTextClipPath [ pdfTextClipPath aload pop", " {/moveto cvx}", " {/lineto cvx}", " {/curveto cvx}", " {/closepath cvx}", " pathforall ] def", " currentpoint newpath moveto", " } {", " pop pop pop pop", " } ifelse", " 0 pdfTextRise neg pdfTextMat dtransform rmoveto", "} def", "/TJm { 0.001 mul pdfFontSize mul pdfHorizScaling mul neg 0", " pdfTextMat dtransform rmoveto } def", "/TJmV { 0.001 mul pdfFontSize mul neg 0 exch", " pdfTextMat dtransform rmoveto } def", "/Tclip { pdfTextClipPath cvx exec clip newpath", " /pdfTextClipPath [] def } def", "/Tclip* { pdfTextClipPath cvx exec eoclip newpath", " /pdfTextClipPath [] def } def", "~1ns", "% Level 1 image operators", "/pdfIm1 {", " /pdfImBuf1 4 index string def", " { currentfile pdfImBuf1 readhexstring pop } image", "} def", "/pdfIm1Bin {", " /pdfImBuf1 4 index string def", " { currentfile pdfImBuf1 readstring pop } image", "} def", "~1s", "/pdfIm1Sep {", " /pdfImBuf1 4 index string def", " /pdfImBuf2 4 index string def", " /pdfImBuf3 4 index string def", " /pdfImBuf4 4 index string def", " { currentfile pdfImBuf1 readhexstring pop }", " { currentfile pdfImBuf2 readhexstring pop }", " { currentfile pdfImBuf3 readhexstring pop }", " { currentfile pdfImBuf4 readhexstring pop }", " true 4 colorimage", "} def", "/pdfIm1SepBin {", " /pdfImBuf1 4 index string def", " /pdfImBuf2 4 index string def", " /pdfImBuf3 4 index string def", " /pdfImBuf4 4 index string def", " { currentfile pdfImBuf1 readstring pop }", " { currentfile pdfImBuf2 readstring pop }", " { currentfile pdfImBuf3 readstring pop }", " { currentfile pdfImBuf4 readstring pop }", " true 4 colorimage", "} def", "~1ns", "/pdfImM1 {", " fCol /pdfImBuf1 4 index 7 add 8 idiv string def", " { currentfile pdfImBuf1 readhexstring pop } imagemask", "} def", "/pdfImM1Bin {", " fCol /pdfImBuf1 4 index 7 add 8 idiv string def", " { currentfile pdfImBuf1 readstring pop } imagemask", "} def", "/pdfImStr {", " 2 copy exch length lt {", " 2 copy get exch 1 add exch", " } {", " ()", " } ifelse", "} def", "/pdfImM1a {", " { pdfImStr } imagemask", " pop pop", "} def", "~23sn", "% Level 2/3 image operators", "/pdfImBuf 100 string def", "/pdfImStr {", " 2 copy exch length lt {", " 2 copy get exch 1 add exch", " } {", " ()", " } ifelse", "} def", "/skipEOD {", " { currentfile pdfImBuf readline", " not { pop exit } if", " (%-EOD-) eq { exit } if } loop", "} def", "/pdfIm { image skipEOD } def", "~3sn", "/pdfMask {", " /ReusableStreamDecode filter", " skipEOD", " /maskStream exch def", "} def", "/pdfMaskEnd { maskStream closefile } def", "/pdfMaskInit {", " /maskArray exch def", " /maskIdx 0 def", "} def", "/pdfMaskSrc {", " maskIdx maskArray length lt {", " maskArray maskIdx get", " /maskIdx maskIdx 1 add def", " } {", " ()", " } ifelse", "} def", "~23s", "/pdfImSep {", " findcmykcustomcolor exch", " dup /Width get /pdfImBuf1 exch string def", " dup /Decode get aload pop 1 index sub /pdfImDecodeRange exch def", " /pdfImDecodeLow exch def", " begin Width Height BitsPerComponent ImageMatrix DataSource end", " /pdfImData exch def", " { pdfImData pdfImBuf1 readstring pop", " 0 1 2 index length 1 sub {", " 1 index exch 2 copy get", " pdfImDecodeRange mul 255 div pdfImDecodeLow add round cvi", " 255 exch sub put", " } for }", " 6 5 roll customcolorimage", " skipEOD", "} def", "~23sn", "/pdfImM { fCol imagemask skipEOD } def", "~123sn", "/pr { 2 index 2 index 3 2 roll putinterval 4 add } def", "/pdfImClip {", " gsave", " 0 2 4 index length 1 sub {", " dup 4 index exch 2 copy", " get 5 index div put", " 1 add 3 index exch 2 copy", " get 3 index div put", " } for", " pop pop rectclip", "} def", "/pdfImClipEnd { grestore } def", "~23sn", "% shading operators", "/colordelta {", " false 0 1 3 index length 1 sub {", " dup 4 index exch get 3 index 3 2 roll get sub abs 0.004 gt {", " pop true", " } if", " } for", " exch pop exch pop", "} def", "/funcCol { func n array astore } def", "/funcSH {", " dup 0 eq {", " true", " } {", " dup 6 eq {", " false", " } {", " 4 index 4 index funcCol dup", " 6 index 4 index funcCol dup", " 3 1 roll colordelta 3 1 roll", " 5 index 5 index funcCol dup", " 3 1 roll colordelta 3 1 roll", " 6 index 8 index funcCol dup", " 3 1 roll colordelta 3 1 roll", " colordelta or or or", " } ifelse", " } ifelse", " {", " 1 add", " 4 index 3 index add 0.5 mul exch 4 index 3 index add 0.5 mul exch", " 6 index 6 index 4 index 4 index 4 index funcSH", " 2 index 6 index 6 index 4 index 4 index funcSH", " 6 index 2 index 4 index 6 index 4 index funcSH", " 5 3 roll 3 2 roll funcSH pop pop", " } {", " pop 3 index 2 index add 0.5 mul 3 index 2 index add 0.5 mul", "~23n", " funcCol sc", "~23s", " funcCol aload pop k", "~23sn", " dup 4 index exch mat transform m", " 3 index 3 index mat transform l", " 1 index 3 index mat transform l", " mat transform l pop pop h f*", " } ifelse", "} def", "/axialCol {", " dup 0 lt {", " pop t0", " } {", " dup 1 gt {", " pop t1", " } {", " dt mul t0 add", " } ifelse", " } ifelse", " func n array astore", "} def", "/axialSH {", " dup 0 eq {", " true", " } {", " dup 8 eq {", " false", " } {", " 2 index axialCol 2 index axialCol colordelta", " } ifelse", " } ifelse", " {", " 1 add 3 1 roll 2 copy add 0.5 mul", " dup 4 3 roll exch 4 index axialSH", " exch 3 2 roll axialSH", " } {", " pop 2 copy add 0.5 mul", "~23n", " axialCol sc", "~23s", " axialCol aload pop k", "~23sn", " exch dup dx mul x0 add exch dy mul y0 add", " 3 2 roll dup dx mul x0 add exch dy mul y0 add", " dx abs dy abs ge {", " 2 copy yMin sub dy mul dx div add yMin m", " yMax sub dy mul dx div add yMax l", " 2 copy yMax sub dy mul dx div add yMax l", " yMin sub dy mul dx div add yMin l", " h f*", " } {", " exch 2 copy xMin sub dx mul dy div add xMin exch m", " xMax sub dx mul dy div add xMax exch l", " exch 2 copy xMax sub dx mul dy div add xMax exch l", " xMin sub dx mul dy div add xMin exch l", " h f*", " } ifelse", " } ifelse", "} def", "/radialCol {", " dup t0 lt {", " pop t0", " } {", " dup t1 gt {", " pop t1", " } if", " } ifelse", " func n array astore", "} def", "/radialSH {", " dup 0 eq {", " true", " } {", " dup 8 eq {", " false", " } {", " 2 index dt mul t0 add radialCol", " 2 index dt mul t0 add radialCol colordelta", " } ifelse", " } ifelse", " {", " 1 add 3 1 roll 2 copy add 0.5 mul", " dup 4 3 roll exch 4 index radialSH", " exch 3 2 roll radialSH", " } {", " pop 2 copy add 0.5 mul dt mul t0 add", "~23n", " radialCol sc", "~23s", " radialCol aload pop k", "~23sn", " encl {", " exch dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", " 0 360 arc h", " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", " 360 0 arcn h f", " } {", " 2 copy", " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", " a1 a2 arcn", " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", " a2 a1 arcn h", " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", " a1 a2 arc", " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", " a2 a1 arc h f", " } ifelse", " } ifelse", "} def", "~123sn", "end", nullptr }; static const char *cmapProlog[] = { "/CIDInit /ProcSet findresource begin", "10 dict begin", " begincmap", " /CMapType 1 def", " /CMapName /Identity-H def", " /CIDSystemInfo 3 dict dup begin", " /Registry (Adobe) def", " /Ordering (Identity) def", " /Supplement 0 def", " end def", " 1 begincodespacerange", " <0000> ", " endcodespacerange", " 0 usefont", " 1 begincidrange", " <0000> 0", " endcidrange", " endcmap", " currentdict CMapName exch /CMap defineresource pop", "end", "10 dict begin", " begincmap", " /CMapType 1 def", " /CMapName /Identity-V def", " /CIDSystemInfo 3 dict dup begin", " /Registry (Adobe) def", " /Ordering (Identity) def", " /Supplement 0 def", " end def", " /WMode 1 def", " 1 begincodespacerange", " <0000> ", " endcodespacerange", " 0 usefont", " 1 begincidrange", " <0000> 0", " endcidrange", " endcmap", " currentdict CMapName exch /CMap defineresource pop", "end", "end", nullptr }; //------------------------------------------------------------------------ // Fonts //------------------------------------------------------------------------ struct PSSubstFont { const char *psName; // PostScript name double mWidth; // width of 'm' character }; // NB: must be in same order as base14SubstFonts in GfxFont.cc static const PSSubstFont psBase14SubstFonts[14] = { { "Courier", 0.600 }, { "Courier-Oblique", 0.600 }, { "Courier-Bold", 0.600 }, { "Courier-BoldOblique", 0.600 }, { "Helvetica", 0.833 }, { "Helvetica-Oblique", 0.833 }, { "Helvetica-Bold", 0.889 }, { "Helvetica-BoldOblique", 0.889 }, { "Times-Roman", 0.788 }, { "Times-Italic", 0.722 }, { "Times-Bold", 0.833 }, { "Times-BoldItalic", 0.778 }, // the last two are never used for substitution { "Symbol", 0 }, { "ZapfDingbats", 0 } }; // Mapping from Type 1/1C font file to PS font name. struct PST1FontName { Ref fontFileID; GooString *psName; // PostScript font name used for this // embedded font file }; // Info for 8-bit fonts struct PSFont8Info { Ref fontID; int *codeToGID; // code-to-GID mapping for TrueType fonts }; // Encoding info for substitute 16-bit font struct PSFont16Enc { Ref fontID; GooString *enc; }; //------------------------------------------------------------------------ // process colors //------------------------------------------------------------------------ #define psProcessCyan 1 #define psProcessMagenta 2 #define psProcessYellow 4 #define psProcessBlack 8 #define psProcessCMYK 15 //------------------------------------------------------------------------ // PSOutCustomColor //------------------------------------------------------------------------ class PSOutCustomColor { public: PSOutCustomColor(double cA, double mA, double yA, double kA, GooString *nameA); ~PSOutCustomColor(); PSOutCustomColor(const PSOutCustomColor &) = delete; PSOutCustomColor &operator=(const PSOutCustomColor &) = delete; double c, m, y, k; GooString *name; PSOutCustomColor *next; }; PSOutCustomColor::PSOutCustomColor(double cA, double mA, double yA, double kA, GooString *nameA) { c = cA; m = mA; y = yA; k = kA; name = nameA; next = nullptr; } PSOutCustomColor::~PSOutCustomColor() { delete name; } //------------------------------------------------------------------------ struct PSOutImgClipRect { int x0, x1, y0, y1; }; //------------------------------------------------------------------------ // DeviceNRecoder //------------------------------------------------------------------------ class DeviceNRecoder : public FilterStream { public: DeviceNRecoder(Stream *strA, int widthA, int heightA, GfxImageColorMap *colorMapA); ~DeviceNRecoder() override; StreamKind getKind() const override { return strWeird; } void 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; } bool isBinary(bool last = true) const override { return true; } bool isEncoder() const override { return true; } private: bool fillBuf(); int width, height; GfxImageColorMap *colorMap; const Function *func; ImageStream *imgStr; int buf[gfxColorMaxComps]; int pixelIdx; int bufIdx; int bufSize; }; DeviceNRecoder::DeviceNRecoder(Stream *strA, int widthA, int heightA, GfxImageColorMap *colorMapA) : FilterStream(strA) { width = widthA; height = heightA; colorMap = colorMapA; imgStr = nullptr; pixelIdx = 0; bufIdx = gfxColorMaxComps; bufSize = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->getAlt()->getNComps(); func = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->getTintTransformFunc(); } DeviceNRecoder::~DeviceNRecoder() { if (imgStr) { delete imgStr; } if (str->isEncoder()) { delete str; } } void DeviceNRecoder::reset() { imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgStr->reset(); } bool DeviceNRecoder::fillBuf() { unsigned char pixBuf[gfxColorMaxComps]; GfxColor color; double x[gfxColorMaxComps], y[gfxColorMaxComps]; int i; if (pixelIdx >= width * height) { return false; } imgStr->getPixel(pixBuf); colorMap->getColor(pixBuf, &color); for (i = 0; i < ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->getNComps(); ++i) { x[i] = colToDbl(color.c[i]); } func->transform(x, y); for (i = 0; i < bufSize; ++i) { buf[i] = (int)(y[i] * 255 + 0.5); } bufIdx = 0; ++pixelIdx; return true; } //------------------------------------------------------------------------ // PSOutputDev //------------------------------------------------------------------------ extern "C" { typedef void (*SignalFunc)(int); } static void outputToFile(void *stream, const char *data, size_t len) { fwrite(data, 1, len, (FILE *)stream); } PSOutputDev::PSOutputDev(const char *fileName, PDFDoc *docA, char *psTitleA, const std::vector &pagesA, PSOutMode modeA, int paperWidthA, int paperHeightA, bool noCropA, bool duplexA, int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, PSForceRasterize forceRasterizeA, bool manualCtrlA, PSOutCustomCodeCbk customCodeCbkA, void *customCodeCbkDataA, PSLevel levelA) { FILE *f; PSFileType fileTypeA; underlayCbk = nullptr; underlayCbkData = nullptr; overlayCbk = nullptr; overlayCbkData = nullptr; customCodeCbk = customCodeCbkA; customCodeCbkData = customCodeCbkDataA; t1FontNames = nullptr; font8Info = nullptr; font16Enc = nullptr; imgIDs = nullptr; formIDs = nullptr; embFontList = nullptr; customColors = nullptr; haveTextClip = false; t3String = nullptr; forceRasterize = forceRasterizeA; psTitle = nullptr; // open file or pipe if (!strcmp(fileName, "-")) { fileTypeA = psStdout; f = stdout; } else if (fileName[0] == '|') { fileTypeA = psPipe; #ifdef HAVE_POPEN # ifndef _WIN32 signal(SIGPIPE, (SignalFunc)SIG_IGN); # endif if (!(f = popen(fileName + 1, "w"))) { error(errIO, -1, "Couldn't run print command '{0:s}'", fileName); ok = false; return; } #else error(errIO, -1, "Print commands are not supported ('{0:s}')", fileName); ok = false; return; #endif } else { fileTypeA = psFile; if (!(f = openFile(fileName, "w"))) { error(errIO, -1, "Couldn't open PostScript file '{0:s}'", fileName); ok = false; return; } } init(outputToFile, f, fileTypeA, psTitleA, docA, pagesA, modeA, imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA, paperWidthA, paperHeightA, noCropA, duplexA, levelA); } PSOutputDev::PSOutputDev(int fdA, PDFDoc *docA, char *psTitleA, const std::vector &pagesA, PSOutMode modeA, int paperWidthA, int paperHeightA, bool noCropA, bool duplexA, int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, PSForceRasterize forceRasterizeA, bool manualCtrlA, PSOutCustomCodeCbk customCodeCbkA, void *customCodeCbkDataA, PSLevel levelA) { FILE *f; PSFileType fileTypeA; underlayCbk = nullptr; underlayCbkData = nullptr; overlayCbk = nullptr; overlayCbkData = nullptr; customCodeCbk = customCodeCbkA; customCodeCbkData = customCodeCbkDataA; t1FontNames = nullptr; font8Info = nullptr; font16Enc = nullptr; imgIDs = nullptr; formIDs = nullptr; embFontList = nullptr; customColors = nullptr; haveTextClip = false; t3String = nullptr; forceRasterize = forceRasterizeA; psTitle = nullptr; // open file or pipe if (fdA == fileno(stdout)) { fileTypeA = psStdout; f = stdout; } else { fileTypeA = psFile; if (!(f = fdopen(fdA, "w"))) { error(errIO, -1, "Couldn't open PostScript file descriptor '{0:d}'", fdA); ok = false; return; } } init(outputToFile, f, fileTypeA, psTitleA, docA, pagesA, modeA, imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA, paperWidthA, paperHeightA, noCropA, duplexA, levelA); } PSOutputDev::PSOutputDev(FoFiOutputFunc outputFuncA, void *outputStreamA, char *psTitleA, PDFDoc *docA, const std::vector &pagesA, PSOutMode modeA, int paperWidthA, int paperHeightA, bool noCropA, bool duplexA, int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, PSForceRasterize forceRasterizeA, bool manualCtrlA, PSOutCustomCodeCbk customCodeCbkA, void *customCodeCbkDataA, PSLevel levelA) { underlayCbk = nullptr; underlayCbkData = nullptr; overlayCbk = nullptr; overlayCbkData = nullptr; customCodeCbk = customCodeCbkA; customCodeCbkData = customCodeCbkDataA; t1FontNames = nullptr; font8Info = nullptr; font16Enc = nullptr; imgIDs = nullptr; formIDs = nullptr; embFontList = nullptr; customColors = nullptr; haveTextClip = false; t3String = nullptr; forceRasterize = forceRasterizeA; psTitle = nullptr; init(outputFuncA, outputStreamA, psGeneric, psTitleA, docA, pagesA, modeA, imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA, paperWidthA, paperHeightA, noCropA, duplexA, levelA); } struct StandardMedia { const char *name; int width; int height; }; static const StandardMedia standardMedia[] = { { "A0", 2384, 3371 }, { "A1", 1685, 2384 }, { "A2", 1190, 1684 }, { "A3", 842, 1190 }, { "A4", 595, 842 }, { "A5", 420, 595 }, { "B4", 729, 1032 }, { "B5", 516, 729 }, { "Letter", 612, 792 }, { "Tabloid", 792, 1224 }, { "Ledger", 1224, 792 }, { "Legal", 612, 1008 }, { "Statement", 396, 612 }, { "Executive", 540, 720 }, { "Folio", 612, 936 }, { "Quarto", 610, 780 }, { "10x14", 720, 1008 }, { nullptr, 0, 0 } }; /* PLRM specifies a tolerance of 5 points when matching page sizes */ static bool pageDimensionEqual(int a, int b) { int aux; if (unlikely(checkedSubtraction(a, b, &aux))) { return false; } return (abs(aux) < 5); } // Shared initialization of PSOutputDev members. // Store the values but do not process them so the function that // created the PSOutputDev can use the various setters to change defaults. void PSOutputDev::init(FoFiOutputFunc outputFuncA, void *outputStreamA, PSFileType fileTypeA, char *psTitleA, PDFDoc *docA, const std::vector &pagesA, PSOutMode modeA, int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, bool manualCtrlA, int paperWidthA, int paperHeightA, bool noCropA, bool duplexA, PSLevel levelA) { if (pagesA.empty()) { ok = false; return; } // initialize postInitDone = false; embedType1 = true; embedTrueType = true; embedCIDPostScript = true; embedCIDTrueType = true; fontPassthrough = false; optimizeColorSpace = false; passLevel1CustomColor = false; preloadImagesForms = false; generateOPI = false; useASCIIHex = false; useBinary = false; enableLZW = true; enableFlate = true; rasterResolution = 300; uncompressPreloadedImages = false; psCenter = true; rasterAntialias = false; displayText = true; ok = true; outputFunc = outputFuncA; outputStream = outputStreamA; fileType = fileTypeA; psTitle = (psTitleA ? strdup(psTitleA) : nullptr); doc = docA; level = levelA; pages = pagesA; mode = modeA; paperWidth = paperWidthA; paperHeight = paperHeightA; noCrop = noCropA; duplex = duplexA; imgLLX = imgLLXA; imgLLY = imgLLYA; imgURX = imgURXA; imgURY = imgURYA; manualCtrl = manualCtrlA; xref = nullptr; processColors = 0; inType3Char = false; inUncoloredPattern = false; t3FillColorOnly = false; #ifdef OPI_SUPPORT // initialize OPI nesting levels opi13Nest = 0; opi20Nest = 0; #endif tx0 = ty0 = -1; xScale0 = yScale0 = 0; rotate0 = -1; clipLLX0 = clipLLY0 = 0; clipURX0 = clipURY0 = -1; processColorFormatSpecified = false; // initialize sequential page number seqPage = 1; } // Complete the initialization after the function that created the PSOutputDev // has had a chance to modify default values with the various setters. void PSOutputDev::postInit() { Catalog *catalog; PDFRectangle *box; int w, h, i; if (postInitDone || !ok) { return; } postInitDone = true; xref = doc->getXRef(); catalog = doc->getCatalog(); if (paperWidth < 0 || paperHeight < 0) { paperMatch = true; } else { paperMatch = false; } paperSizes.clear(); for (const int pg : pages) { Page *page = catalog->getPage(pg); if (page == nullptr) { paperMatch = false; } if (!paperMatch) { w = paperWidth; h = paperHeight; if (w < 0 || h < 0) { // Unable to obtain a paper size from the document and no page size // specified. In this case use A4 as the page size to ensure the PS output is // valid. This will only occur if the PDF is very broken. w = 595; h = 842; } } else if (noCrop) { w = (int)ceil(page->getMediaWidth()); h = (int)ceil(page->getMediaHeight()); } else { w = (int)ceil(page->getCropWidth()); h = (int)ceil(page->getCropHeight()); } if (paperMatch) { const int pageRotate = page->getRotate(); if (pageRotate == 90 || pageRotate == 270) { std::swap(w, h); } } if (w > paperWidth) { paperWidth = w; } if (h > paperHeight) { paperHeight = 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()) { const StandardMedia *media = standardMedia; std::string name; while (media->name) { if (pageDimensionEqual(w, media->width) && pageDimensionEqual(h, media->height)) { name = std::string(media->name); w = media->width; h = media->height; break; } media++; } if (name.empty()) { name = GooString::format("{0:d}x{1:d}mm", int(w * 25.4 / 72), int(h * 25.4 / 72))->toStr(); } paperSizes.emplace_back(std::move(name), w, h); } pagePaperSize.insert(std::pair(pg, i)); if (!paperMatch) { break; // we only need one entry when all pages are the same size } } if (imgLLX == 0 && imgURX == 0 && imgLLY == 0 && imgURY == 0) { imgLLX = imgLLY = 0; imgURX = paperWidth; imgURY = paperHeight; } std::vector pageList; if (mode == psModeForm) { pageList.push_back(pages[0]); } else { pageList = pages; } // initialize fontIDs, fontFileIDs, and fontFileNames lists 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; imgIDSize = 0; formIDLen = 0; formIDSize = 0; numSaves = 0; numTilingPatterns = 0; nextFunc = 0; // set some default process color format if none is set if (!processColorFormatSpecified) { if (level == psLevel1) { processColorFormat = splashModeMono8; } else if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep || overprintPreview) { processColorFormat = splashModeCMYK8; } #ifdef USE_CMS else if (getDisplayProfile()) { auto processcolorspace = cmsGetColorSpace(getDisplayProfile().get()); if (processcolorspace == cmsSigCmykData) { processColorFormat = splashModeCMYK8; } else if (processcolorspace == cmsSigGrayData) { processColorFormat = splashModeMono8; } else { processColorFormat = splashModeRGB8; } } #endif else { processColorFormat = splashModeRGB8; } } // check for consistency between the processColorFormat the LanguageLevel and other settings if (level == psLevel1 && processColorFormat != splashModeMono8) { error(errConfig, -1, "Conflicting settings between LanguageLevel=psLevel1 and processColorFormat." " Resetting processColorFormat to MONO8."); processColorFormat = splashModeMono8; } else if ((level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep || overprintPreview) && processColorFormat != splashModeCMYK8) { error(errConfig, -1, "Conflicting settings between LanguageLevel and/or overprint simulation, and processColorFormat." " Resetting processColorFormat to CMYK8."); processColorFormat = splashModeCMYK8; } #ifdef USE_CMS if (getDisplayProfile()) { auto processcolorspace = cmsGetColorSpace(getDisplayProfile().get()); if (processColorFormat == splashModeCMYK8) { if (processcolorspace != cmsSigCmykData) { error(errConfig, -1, "Mismatch between processColorFormat=CMYK8 and ICC profile color format."); } } else if (processColorFormat == splashModeMono8) { if (processcolorspace != cmsSigGrayData) { error(errConfig, -1, "Mismatch between processColorFormat=MONO8 and ICC profile color format."); } } else if (processColorFormat == splashModeRGB8) { if (processcolorspace != cmsSigRgbData) { error(errConfig, -1, "Mismatch between processColorFormat=RGB8 and ICC profile color format."); } } } #endif // initialize embedded font resource comment list embFontList = new GooString(); if (!manualCtrl) { Page *page; // this check is needed in case the document has zero pages if ((page = doc->getPage(pageList[0]))) { writeHeader(pageList.size(), page->getMediaBox(), page->getCropBox(), page->getRotate(), psTitle); } else { error(errSyntaxError, -1, "Invalid page {0:d}", pageList[0]); box = new PDFRectangle(0, 0, 1, 1); writeHeader(pageList.size(), box, box, 0, psTitle); delete box; } if (mode != psModeForm) { writePS("%%BeginProlog\n"); } writeXpdfProcset(); if (mode != psModeForm) { writePS("%%EndProlog\n"); writePS("%%BeginSetup\n"); } writeDocSetup(catalog, pageList, duplex); if (mode != psModeForm) { writePS("%%EndSetup\n"); } } } PSOutputDev::~PSOutputDev() { PSOutCustomColor *cc; int i; if (ok) { if (!postInitDone) { postInit(); } if (!manualCtrl) { writePS("%%Trailer\n"); writeTrailer(); if (mode != psModeForm) { writePS("%%EOF\n"); } } if (fileType == psFile) { fclose((FILE *)outputStream); } #ifdef HAVE_POPEN else if (fileType == psPipe) { pclose((FILE *)outputStream); # ifndef _WIN32 signal(SIGPIPE, (SignalFunc)SIG_DFL); # endif } #endif } if (embFontList) { delete embFontList; } 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); } if (font16Enc) { for (i = 0; i < font16EncLen; ++i) { if (font16Enc[i].enc) { delete font16Enc[i].enc; } } gfree(font16Enc); } gfree(imgIDs); gfree(formIDs); while (customColors) { cc = customColors; customColors = cc->next; delete cc; } gfree(psTitle); delete t3String; } void PSOutputDev::writeHeader(int nPages, const PDFRectangle *mediaBox, const PDFRectangle *cropBox, int pageRotate, const char *title) { double x1, y1, x2, y2; switch (mode) { case psModePS: writePS("%!PS-Adobe-3.0\n"); break; case psModeEPS: writePS("%!PS-Adobe-3.0 EPSF-3.0\n"); break; case psModeForm: 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(); if (info.isDict()) { Object obj1 = info.dictLookup("Creator"); if (obj1.isString()) { writePS("%%Creator: "); writePSTextLine(obj1.getString()); } } if (title) { char *sanitizedTitle = strdup(title); for (size_t i = 0; i < strlen(sanitizedTitle); ++i) { if (sanitizedTitle[i] == '\n' || sanitizedTitle[i] == '\r') { sanitizedTitle[i] = ' '; } } writePSFmt("%%Title: {0:s}\n", sanitizedTitle); free(sanitizedTitle); } writePSFmt("%%LanguageLevel: {0:d}\n", (level == psLevel1 || level == psLevel1Sep) ? 1 : (level == psLevel2 || level == psLevel2Sep) ? 2 : 3); if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) { writePS("%%DocumentProcessColors: (atend)\n"); writePS("%%DocumentCustomColors: (atend)\n"); } writePS("%%DocumentSuppliedResources: (atend)\n"); if ((level == psLevel1 || level == psLevel1Sep) && useBinary) { writePS("%%DocumentData: Binary\n"); } switch (mode) { case psModePS: 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) { writePS("%%BeginDefaults\n"); writePSFmt("%%PageMedia: {0:s}\n", paperSizes[0].name.c_str()); writePS("%%EndDefaults\n"); } break; case psModeEPS: epsX1 = cropBox->x1; epsY1 = cropBox->y1; epsX2 = cropBox->x2; epsY2 = cropBox->y2; if (pageRotate == 0 || pageRotate == 180) { x1 = epsX1; y1 = epsY1; x2 = epsX2; y2 = epsY2; } else { // pageRotate == 90 || pageRotate == 270 x1 = 0; y1 = 0; x2 = epsY2 - epsY1; y2 = epsX2 - epsX1; } writePSFmt("%%BoundingBox: {0:d} {1:d} {2:d} {3:d}\n", (int)floor(x1), (int)floor(y1), (int)ceil(x2), (int)ceil(y2)); writePSFmt("%%HiResBoundingBox: {0:.6g} {1:.6g} {2:.6g} {3:.6g}\n", x1, y1, x2, y2); writePS("%%DocumentSuppliedResources: (atend)\n"); writePS("%%EndComments\n"); break; case psModeForm: writePS("%%EndComments\n"); writePS("32 dict dup begin\n"); writePSFmt("/BBox [{0:d} {1:d} {2:d} {3:d}] def\n", (int)floor(mediaBox->x1), (int)floor(mediaBox->y1), (int)ceil(mediaBox->x2), (int)ceil(mediaBox->y2)); writePS("/FormType 1 def\n"); writePS("/Matrix [1 0 0 1 0 0] def\n"); break; } } void PSOutputDev::writeXpdfProcset() { bool lev1, lev2, lev3, sep, nonSep; const char **p; const char *q; writePSFmt("%%BeginResource: procset xpdf {0:s} 0\n", "3.00"); writePSFmt("%%Copyright: {0:s}\n", xpdfCopyright); lev1 = lev2 = lev3 = sep = nonSep = true; for (p = prolog; *p; ++p) { if ((*p)[0] == '~') { lev1 = lev2 = lev3 = sep = nonSep = false; for (q = *p + 1; *q; ++q) { switch (*q) { case '1': lev1 = true; break; case '2': lev2 = true; break; case '3': lev3 = true; break; case 's': sep = true; break; case 'n': nonSep = true; break; } } } else if ((level == psLevel1 && lev1 && nonSep) || (level == psLevel1Sep && lev1 && sep) || (level == psLevel1Sep && lev2 && sep && getPassLevel1CustomColor()) || (level == psLevel2 && lev2 && nonSep) || (level == psLevel2Sep && lev2 && sep) || (level == psLevel3 && lev3 && nonSep) || (level == psLevel3Sep && lev3 && sep)) { writePSFmt("{0:s}\n", *p); } } writePS("%%EndResource\n"); if (level >= psLevel3) { for (p = cmapProlog; *p; ++p) { writePSFmt("{0:s}\n", *p); } } } void PSOutputDev::writeDocSetup(Catalog *catalog, const std::vector &pageList, bool duplexA) { Page *page; Dict *resDict; Annots *annots; Object *acroForm; GooString *s; if (mode == psModeForm) { // swap the form and xpdf dicts writePS("xpdf end begin dup begin\n"); } else { writePS("xpdf begin\n"); } for (const int pg : pageList) { page = doc->getPage(pg); if (!page) { error(errSyntaxError, -1, "Failed writing resources for page {0:d}", pg); continue; } if ((resDict = page->getResourceDict())) { setupResources(resDict); } annots = page->getAnnots(); for (Annot *annot : annots->getAnnots()) { Object obj1 = annot->getAppearanceResDict(); if (obj1.isDict()) { setupResources(obj1.getDict()); } } } if ((acroForm = catalog->getAcroForm()) && acroForm->isDict()) { Object obj1 = acroForm->dictLookup("DR"); if (obj1.isDict()) { setupResources(obj1.getDict()); } obj1 = acroForm->dictLookup("Fields"); if (obj1.isArray()) { for (int i = 0; i < obj1.arrayGetLength(); ++i) { Object obj2 = obj1.arrayGet(i); if (obj2.isDict()) { Object obj3 = obj2.dictLookup("DR"); if (obj3.isDict()) { setupResources(obj3.getDict()); } } } } } if (mode != psModeForm) { if (mode != psModeEPS && !manualCtrl) { writePSFmt("{0:s} pdfSetup\n", duplexA ? "true" : "false"); if (!paperMatch) { writePSFmt("{0:d} {1:d} pdfSetupPaper\n", paperWidth, paperHeight); } } #ifdef OPI_SUPPORT if (generateOPI) { writePS("/opiMatrix matrix currentmatrix def\n"); } #endif } if (customCodeCbk) { if ((s = (*customCodeCbk)(this, psOutCustomDocSetup, 0, customCodeCbkData))) { writePS(s->c_str()); delete s; } } } void PSOutputDev::writePageTrailer() { if (mode != psModeForm) { writePS("pdfEndPage\n"); } } void PSOutputDev::writeTrailer() { PSOutCustomColor *cc; if (mode == psModeForm) { writePS("/Foo exch /Form defineresource pop\n"); } else { writePS("end\n"); writePS("%%DocumentSuppliedResources:\n"); writePS(embFontList->c_str()); if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) { writePS("%%DocumentProcessColors:"); if (processColors & psProcessCyan) { writePS(" Cyan"); } if (processColors & psProcessMagenta) { writePS(" Magenta"); } if (processColors & psProcessYellow) { writePS(" Yellow"); } if (processColors & psProcessBlack) { writePS(" Black"); } writePS("\n"); writePS("%%DocumentCustomColors:"); for (cc = customColors; cc; cc = cc->next) { writePS(" "); writePSString(cc->name->toStr()); } writePS("\n"); writePS("%%CMYKCustomColor:\n"); for (cc = customColors; cc; cc = cc->next) { writePSFmt("%%+ {0:.4g} {1:.4g} {2:.4g} {3:.4g} ", cc->c, cc->m, cc->y, cc->k); writePSString(cc->name->toStr()); writePS("\n"); } } } } void PSOutputDev::setupResources(Dict *resDict) { bool skip; setupFonts(resDict); setupImages(resDict); setupForms(resDict); //----- recursively scan XObjects Object xObjDict = resDict->lookup("XObject"); if (xObjDict.isDict()) { for (int i = 0; i < xObjDict.dictGetLength(); ++i) { // avoid infinite recursion on XObjects skip = false; const Object &xObjRef = xObjDict.dictGetValNF(i); if (xObjRef.isRef()) { Ref ref0 = xObjRef.getRef(); if (resourceIDs.find(ref0.num) != resourceIDs.end()) { skip = true; } else { resourceIDs.insert(ref0.num); } } if (!skip) { // process the XObject's resource dictionary Object xObj = xObjDict.dictGetVal(i); if (xObj.isStream()) { Ref resObjRef; Object resObj = xObj.streamGetDict()->lookup("Resources", &resObjRef); if (resObj.isDict()) { if (resObjRef != Ref::INVALID()) { const int numObj = resObjRef.num; if (resourceIDs.find(numObj) != resourceIDs.end()) { error(errSyntaxError, -1, "loop in Resources (numObj: {0:d})", numObj); continue; } resourceIDs.insert(numObj); } setupResources(resObj.getDict()); } } } } } //----- recursively scan Patterns Object patDict = resDict->lookup("Pattern"); if (patDict.isDict()) { inType3Char = true; for (int i = 0; i < patDict.dictGetLength(); ++i) { // avoid infinite recursion on Patterns skip = false; const Object &patRef = patDict.dictGetValNF(i); if (patRef.isRef()) { Ref ref0 = patRef.getRef(); if (resourceIDs.find(ref0.num) != resourceIDs.end()) { skip = true; } else { resourceIDs.insert(ref0.num); } } if (!skip) { // process the Pattern's resource dictionary Object pat = patDict.dictGetVal(i); if (pat.isStream()) { Ref resObjRef; Object resObj = pat.streamGetDict()->lookup("Resources", &resObjRef); if (resObj.isDict()) { if (resObjRef != Ref::INVALID() && !resourceIDs.insert(resObjRef.num).second) { error(errSyntaxWarning, -1, "PSOutputDev::setupResources: Circular resources found."); continue; } setupResources(resObj.getDict()); } } } } inType3Char = false; } } 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)) { setupFont(font.get(), resDict); } } delete gfxFontDict; } } void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) { GooString *psName; char buf[16]; bool subst; const char *charName; double xs, ys; int code; double w1, w2; int i, j; // check if font is already set up for (Ref fontID : fontIDs) { if (fontID == *font->getID()) { return; } } 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); } else { std::optional fontLoc = font->locateFont(xref, this); if (fontLoc) { switch (fontLoc->locType) { case gfxFontLocEmbedded: 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); break; case fontType1C: psName = makePSFontName(font, &fontLoc->embFontID); setupEmbeddedType1CFont(font, &fontLoc->embFontID, psName); break; case fontType1COT: psName = makePSFontName(font, &fontLoc->embFontID); setupEmbeddedOpenTypeT1CFont(font, &fontLoc->embFontID, psName); break; case fontTrueType: case fontTrueTypeOT: psName = makePSFontName(font, font->getID()); setupEmbeddedTrueTypeFont(font, &fontLoc->embFontID, psName); break; case fontCIDType0C: psName = makePSFontName(font, &fontLoc->embFontID); setupEmbeddedCIDType0Font(font, &fontLoc->embFontID, psName); 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); break; case fontCIDType0COT: psName = makePSFontName(font, &fontLoc->embFontID); setupEmbeddedOpenTypeCFFFont(font, &fontLoc->embFontID, psName); break; default: break; } break; case gfxFontLocExternal: //~ add cases for external 16-bit fonts switch (fontLoc->fontType) { case fontType1: if (font->getEmbeddedFontName()) { // this assumes that the PS font name matches the PDF font name psName = font->getEmbeddedFontName()->copy(); } else { //~ this won't work -- the PS font name won't match psName = makePSFontName(font, font->getID()); } setupExternalType1Font(fontLoc->pathAsGooString(), psName); break; case fontTrueType: case fontTrueTypeOT: psName = makePSFontName(font, font->getID()); setupExternalTrueTypeFont(font, fontLoc->pathAsGooString(), psName); 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); break; default: break; } break; case gfxFontLocResident: psName = new GooString(fontLoc->path); break; } } if (!psName) { if (font->isCIDFont()) { error(errSyntaxError, -1, "Couldn't find a font to substitute for '{0:s}' ('{1:s}' character collection)", font->getName() ? font->getName()->c_str() : "(unnamed)", ((GfxCIDFont *)font)->getCollection() ? ((GfxCIDFont *)font)->getCollection()->c_str() : "(unknown)"); if (font16EncLen >= font16EncSize) { font16EncSize += 16; font16Enc = (PSFont16Enc *)greallocn(font16Enc, font16EncSize, sizeof(PSFont16Enc)); } font16Enc[font16EncLen].fontID = *font->getID(); font16Enc[font16EncLen].enc = nullptr; ++font16EncLen; } else { error(errSyntaxError, -1, "Couldn't find a font to substitute for '{0:s}'", font->getName() ? font->getName()->c_str() : "(unnamed)"); } return; } // scale substituted 8-bit fonts if (fontLoc->locType == gfxFontLocResident && fontLoc->substIdx >= 0) { subst = true; for (code = 0; code < 256; ++code) { if ((charName = ((Gfx8BitFont *)font)->getCharName(code)) && charName[0] == 'm' && charName[1] == '\0') { break; } } if (code < 256) { w1 = ((Gfx8BitFont *)font)->getWidth(code); } else { w1 = 0; } w2 = psBase14SubstFonts[fontLoc->substIdx].mWidth; xs = w1 / w2; if (xs < 0.1) { xs = 1; } } } // 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()); } else { writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16\n", font->getID()->num, font->getID()->gen, psName, 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); for (i = 0; i < 256; i += 8) { writePS((char *)((i == 0) ? "[ " : " ")); for (j = 0; j < 8; ++j) { if (font->getType() == fontTrueType && !subst && !((Gfx8BitFont *)font)->getHasEncoding()) { sprintf(buf, "c%02x", i + j); charName = buf; } else { charName = ((Gfx8BitFont *)font)->getCharName(i + j); } writePS("/"); writePSName(charName ? charName : (char *)".notdef"); // the empty name is legal in PDF and PostScript, but PostScript // uses a double-slash (//...) for "immediately evaluated names", // so we need to add a space character here if (charName && !charName[0]) { writePS(" "); } } writePS((i == 256 - 8) ? (char *)"]\n" : (char *)"\n"); } writePS("pdfMakeFont\n"); } delete psName; } void PSOutputDev::setupEmbeddedType1Font(Ref *id, GooString *psName) { static const char hexChar[17] = "0123456789abcdef"; Dict *dict; long length1, length2, length3, i; int c; int start[4]; bool binMode; bool writePadding = true; // check if font is already embedded if (!fontNames.emplace(psName->toStr()).second) { return; } // get the font stream and info Object obj1, obj2, obj3; Object refObj(*id); Object strObj = refObj.fetch(xref); if (!strObj.isStream()) { error(errSyntaxError, -1, "Embedded font file object is not a stream"); goto err1; } if (!(dict = strObj.streamGetDict())) { error(errSyntaxError, -1, "Embedded font stream is missing its dictionary"); goto err1; } obj1 = dict->lookup("Length1"); obj2 = dict->lookup("Length2"); obj3 = dict->lookup("Length3"); if (!obj1.isInt() || !obj2.isInt() || !obj3.isInt()) { error(errSyntaxError, -1, "Missing length fields in embedded font stream dictionary"); goto err1; } length1 = obj1.getInt(); length2 = obj2.getInt(); length3 = obj3.getInt(); // 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(); } // copy ASCII portion of font for (i = 0; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i) { writePSChar(c); } // figure out if encrypted portion is binary or ASCII binMode = false; for (i = 0; i < 4; ++i) { start[i] = strObj.streamGetChar(); if (start[i] == EOF) { error(errSyntaxError, -1, "Unexpected end of file in embedded font stream"); goto err1; } if (!((start[i] >= '0' && start[i] <= '9') || (start[i] >= 'A' && start[i] <= 'F') || (start[i] >= 'a' && start[i] <= 'f'))) { binMode = true; } } if (length2 == 0) { // length2 == 0 is an error // trying to solve it by just piping all // the stream data error(errSyntaxWarning, -1, "Font has length2 as 0, trying to overcome the problem reading the stream until the end"); length2 = INT_MAX; writePadding = false; } // convert binary data to ASCII if (binMode) { if (start[0] == 0x80 && start[1] == 2) { length2 = start[2] | (start[3] << 8) | (strObj.streamGetChar() << 16) | (strObj.streamGetChar() << 24); i = 0; } else { for (i = 0; i < 4; ++i) { writePSChar(hexChar[(start[i] >> 4) & 0x0f]); writePSChar(hexChar[start[i] & 0x0f]); } } #if 0 // this causes trouble for various PostScript printers // if Length2 is incorrect (too small), font data gets chopped, so // we take a few extra characters from the trailer just in case length2 += length3 >= 8 ? 8 : length3; #endif while (i < length2) { if ((c = strObj.streamGetChar()) == EOF) { break; } writePSChar(hexChar[(c >> 4) & 0x0f]); writePSChar(hexChar[c & 0x0f]); if (++i % 32 == 0) { writePSChar('\n'); } } if (i % 32 > 0) { writePSChar('\n'); } // already in ASCII format -- just copy it } else { for (i = 0; i < 4; ++i) { writePSChar(start[i]); } for (i = 4; i < length2; ++i) { if ((c = strObj.streamGetChar()) == EOF) { break; } writePSChar(c); } } if (writePadding) { if (length3 > 0) { // write fixed-content portion c = strObj.streamGetChar(); if (c == 0x80) { c = strObj.streamGetChar(); if (c == 1) { length3 = strObj.streamGetChar() | (strObj.streamGetChar() << 8) | (strObj.streamGetChar() << 16) | (strObj.streamGetChar() << 24); i = 0; while (i < length3) { if ((c = strObj.streamGetChar()) == EOF) { break; } writePSChar(c); ++i; } } } else { if (c != EOF) { writePSChar(c); while ((c = strObj.streamGetChar()) != EOF) { writePSChar(c); } } } } else { // write padding and "cleartomark" for (i = 0; i < 8; ++i) { writePS("00000000000000000000000000000000" "00000000000000000000000000000000\n"); } writePS("cleartomark\n"); } } // ending comment writePS("%%EndResource\n"); err1: if (strObj.isStream()) { strObj.streamClose(); } } void PSOutputDev::setupExternalType1Font(const GooString *fileName, GooString *psName) { static const char hexChar[17] = "0123456789abcdef"; FILE *fontFile; int c; if (!fontNames.emplace(psName->toStr()).second) { return; } // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->c_str()); embFontList->append("\n"); // copy the font file if (!(fontFile = openFile(fileName->c_str(), "rb"))) { error(errIO, -1, "Couldn't open external font file"); return; } c = fgetc(fontFile); if (c == 0x80) { // PFB file ungetc(c, fontFile); while (!feof(fontFile)) { fgetc(fontFile); // skip start of segment byte (0x80) int segType = fgetc(fontFile); long segLen = fgetc(fontFile) | (fgetc(fontFile) << 8) | (fgetc(fontFile) << 16) | (fgetc(fontFile) << 24); if (feof(fontFile)) { break; } if (segType == 1) { // ASCII segment for (long i = 0; i < segLen; i++) { c = fgetc(fontFile); if (c == EOF) { break; } writePSChar(c); } } else if (segType == 2) { // binary segment for (long i = 0; i < segLen; i++) { c = fgetc(fontFile); if (c == EOF) { break; } writePSChar(hexChar[(c >> 4) & 0x0f]); writePSChar(hexChar[c & 0x0f]); if (i % 36 == 35) { writePSChar('\n'); } } } else { // end of file break; } } } else if (c != EOF) { writePSChar(c); while ((c = fgetc(fontFile)) != EOF) { writePSChar(c); } } fclose(fontFile); // ending comment writePS("%%EndResource\n"); } 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) { psName->clear(); psName->insert(0, t1FontNames[i].psName); return; } } if (t1FontNameLen == t1FontNameSize) { t1FontNameSize *= 2; t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize, sizeof(PST1FontName)); } t1FontNames[t1FontNameLen].fontFileID = *id; t1FontNames[t1FontNameLen].psName = psName->copy(); ++t1FontNameLen; // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->c_str()); embFontList->append("\n"); // convert it to a Type 1 font const std::optional> fontBuf = font->readEmbFontFile(xref); if (fontBuf) { if ((ffT1C = FoFiType1C::make(fontBuf->data(), fontBuf->size()))) { ffT1C->convertToType1(psName->c_str(), nullptr, true, outputFunc, outputStream); delete ffT1C; } } // ending comment writePS("%%EndResource\n"); } void PSOutputDev::setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id, GooString *psName) { int i; // check if font is already embedded for (i = 0; i < t1FontNameLen; ++i) { if (t1FontNames[i].fontFileID == *id) { psName->clear(); psName->insert(0, t1FontNames[i].psName); return; } } if (t1FontNameLen == t1FontNameSize) { t1FontNameSize *= 2; t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize, sizeof(PST1FontName)); } t1FontNames[t1FontNameLen].fontFileID = *id; t1FontNames[t1FontNameLen].psName = psName->copy(); ++t1FontNameLen; // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->c_str()); embFontList->append("\n"); // 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 (ffTT->isOpenTypeCFF()) { ffTT->convertToType1(psName->c_str(), nullptr, true, outputFunc, outputStream); } } } // ending comment writePS("%%EndResource\n"); } void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id, GooString *psName) { int *codeToGID; // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->c_str()); embFontList->append("\n"); // 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()); 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; } } } // ending comment writePS("%%EndResource\n"); } void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, const GooString *fileName, GooString *psName) { int *codeToGID; // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->c_str()); 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()); 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; } } // ending comment writePS("%%EndResource\n"); } void PSOutputDev::updateFontMaxValidGlyph(GfxFont *font, int maxValidGlyph) { if (maxValidGlyph >= 0 && font->getName()) { auto &fontMaxValidGlyph = perFontMaxValidGlyph[*font->getName()]; if (fontMaxValidGlyph < maxValidGlyph) { fontMaxValidGlyph = maxValidGlyph; } } } void PSOutputDev::setupExternalCIDTrueTypeFont(GfxFont *font, const GooString *fileName, GooString *psName, bool needVerticalMetrics) { int *codeToGID; int codeToGIDLen; // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->c_str()); embFontList->append("\n"); // 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())) { // 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)); } } else { codeToGID = ((GfxCIDFont *)font)->getCodeToGIDMap(ffTT.get(), &codeToGIDLen); } if (ffTT->isOpenTypeCFF()) { ffTT->convertToCIDType0(psName->c_str(), codeToGID, codeToGIDLen, outputFunc, outputStream); } else if (level >= psLevel3) { // Level 3: use a CID font ffTT->convertToCIDType2(psName->c_str(), codeToGID, codeToGIDLen, 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); updateFontMaxValidGlyph(font, maxValidGlyph); } gfree(codeToGID); } else { error(errSyntaxError, -1, "TrueType font '{0:s}' does not allow embedding", font->getName() ? font->getName()->c_str() : "(unnamed)"); } } // ending comment writePS("%%EndResource\n"); } 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) { psName->clear(); psName->insert(0, t1FontNames[i].psName); return; } } if (t1FontNameLen == t1FontNameSize) { t1FontNameSize *= 2; t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize, sizeof(PST1FontName)); } t1FontNames[t1FontNameLen].fontFileID = *id; t1FontNames[t1FontNameLen].psName = psName->copy(); ++t1FontNameLen; // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->c_str()); embFontList->append("\n"); // convert it to a Type 0 font const std::optional> fontBuf = font->readEmbFontFile(xref); if (fontBuf) { 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); } else { // otherwise: use a non-CID composite font ffT1C->convertToType0(psName->c_str(), nullptr, 0, outputFunc, outputStream); } delete ffT1C; } } // ending comment writePS("%%EndResource\n"); } void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id, GooString *psName, bool needVerticalMetrics) { // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->c_str()); embFontList->append("\n"); // 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 (level >= psLevel3) { // Level 3: use a CID font ffTT->convertToCIDType2(psName->c_str(), ((GfxCIDFont *)font)->getCIDToGID(), ((GfxCIDFont *)font)->getCIDToGIDLen(), 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); updateFontMaxValidGlyph(font, maxValidGlyph); } } } // ending comment writePS("%%EndResource\n"); } void PSOutputDev::setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id, GooString *psName) { int i; // check if font is already embedded for (i = 0; i < t1FontNameLen; ++i) { if (t1FontNames[i].fontFileID == *id) { psName->clear(); psName->insert(0, t1FontNames[i].psName); return; } } if (t1FontNameLen == t1FontNameSize) { t1FontNameSize *= 2; t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize, sizeof(PST1FontName)); } t1FontNames[t1FontNameLen].fontFileID = *id; t1FontNames[t1FontNameLen].psName = psName->copy(); ++t1FontNameLen; // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->c_str()); embFontList->append("\n"); // 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 (ffTT->isOpenTypeCFF()) { if (level >= psLevel3) { // Level 3: use a CID font ffTT->convertToCIDType0(psName->c_str(), ((GfxCIDFont *)font)->getCIDToGID(), ((GfxCIDFont *)font)->getCIDToGIDLen(), outputFunc, outputStream); } else { // otherwise: use a non-CID composite font ffTT->convertToType0(psName->c_str(), ((GfxCIDFont *)font)->getCIDToGID(), ((GfxCIDFont *)font)->getCIDToGIDLen(), outputFunc, outputStream); } } } } // ending comment writePS("%%EndResource\n"); } void PSOutputDev::setupType3Font(GfxFont *font, GooString *psName, Dict *parentResDict) { Dict *resDict; Dict *charProcs; Gfx *gfx; PDFRectangle box; const double *m; int i; // set up resources used by font if ((resDict = ((Gfx8BitFont *)font)->getResources())) { inType3Char = true; setupResources(resDict); inType3Char = false; } else { resDict = parentResDict; } // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->c_str()); embFontList->append("\n"); // font dictionary writePS("8 dict begin\n"); writePS("/FontType 3 def\n"); m = font->getFontMatrix(); writePSFmt("/FontMatrix [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] def\n", m[0], m[1], m[2], m[3], m[4], m[5]); m = font->getFontBBox(); writePSFmt("/FontBBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}] def\n", m[0], m[1], m[2], m[3]); writePS("/Encoding 256 array def\n"); writePS(" 0 1 255 { Encoding exch /.notdef put } for\n"); writePS("/BuildGlyph {\n"); writePS(" exch /CharProcs get exch\n"); writePS(" 2 copy known not { pop /.notdef } if\n"); writePS(" get exec\n"); writePS("} bind def\n"); writePS("/BuildChar {\n"); writePS(" 1 index /Encoding get exch get\n"); writePS(" 1 index /BuildGlyph get exec\n"); writePS("} bind def\n"); if ((charProcs = ((Gfx8BitFont *)font)->getCharProcs())) { writePSFmt("/CharProcs {0:d} dict def\n", charProcs->getLength()); writePS("CharProcs begin\n"); box.x1 = m[0]; box.y1 = m[1]; box.x2 = m[2]; box.y2 = m[3]; gfx = new Gfx(doc, this, resDict, &box, nullptr); inType3Char = true; for (i = 0; i < charProcs->getLength(); ++i) { t3FillColorOnly = false; t3Cacheable = false; t3NeedsRestore = false; writePS("/"); writePSName(charProcs->getKey(i)); writePS(" {\n"); Object charProc = charProcs->getVal(i); gfx->display(&charProc); if (t3String) { std::unique_ptr 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, t3String->c_str(), t3String->getLength()); delete t3String; t3String = nullptr; } if (t3NeedsRestore) { (*outputFunc)(outputStream, "Q\n", 2); } writePS("} def\n"); } inType3Char = false; delete gfx; writePS("end\n"); } writePS("currentdict end\n"); writePSFmt("/{0:t} exch definefont pop\n", psName); // ending comment writePS("%%EndResource\n"); } // 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) { const GooString *s; if ((s = font->getEmbeddedFontName())) { std::string psName = filterPSName(s->toStr()); if (fontNames.emplace(psName).second) { return new GooString(std::move(psName)); } } if (font->getName()) { std::string psName = filterPSName(*font->getName()); if (fontNames.emplace(psName).second) { return new GooString(std::move(psName)); } } GooString *psName = GooString::format("FF{0:d}_{1:d}", id->num, id->gen).release(); if ((s = font->getEmbeddedFontName())) { std::string filteredName = filterPSName(s->toStr()); psName->append('_')->append(filteredName); } else if (font->getName()) { std::string filteredName = filterPSName(*font->getName()); psName->append('_')->append(filteredName); } fontNames.emplace(psName->toStr()); return psName; } void PSOutputDev::setupImages(Dict *resDict) { Ref imgID; if (!(mode == psModeForm || inType3Char || preloadImagesForms)) { return; } //----- recursively scan XObjects Object xObjDict = resDict->lookup("XObject"); if (xObjDict.isDict()) { for (int i = 0; i < xObjDict.dictGetLength(); ++i) { const Object &xObjRef = xObjDict.dictGetValNF(i); Object xObj = xObjDict.dictGetVal(i); if (xObj.isStream()) { Object subtypeObj = xObj.streamGetDict()->lookup("Subtype"); if (subtypeObj.isName("Image")) { if (xObjRef.isRef()) { imgID = xObjRef.getRef(); int j; for (j = 0; j < imgIDLen; ++j) { if (imgIDs[j] == imgID) { break; } } if (j == imgIDLen) { if (imgIDLen >= imgIDSize) { if (imgIDSize == 0) { imgIDSize = 64; } else { imgIDSize *= 2; } imgIDs = (Ref *)greallocn(imgIDs, imgIDSize, sizeof(Ref)); } imgIDs[imgIDLen++] = imgID; setupImage(imgID, xObj.getStream(), false); if (level >= psLevel3) { Object maskObj = xObj.streamGetDict()->lookup("Mask"); if (maskObj.isStream()) { setupImage(imgID, maskObj.getStream(), true); } } } } else { error(errSyntaxError, -1, "Image in resource dict is not an indirect reference"); } } } } } } 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; // filters //~ this does not correctly handle the DeviceN color space //~ -- need to use DeviceNRecoder useFlate = useLZW = useRLE = false; useCompressed = false; doUseASCIIHex = false; if (level < psLevel2) { doUseASCIIHex = true; } else { if (uncompressPreloadedImages) { /* nothing to do */; } else { s = str->getPSFilter(level < psLevel3 ? 2 : 3, ""); if (s) { useCompressed = true; delete s; } else { if (level >= psLevel3 && getEnableFlate()) { useFlate = true; } else if (getEnableLZW()) { useLZW = true; } else { useRLE = true; } } } doUseASCIIHex = useASCIIHex; } if (useCompressed) { str = str->getUndecodedStream(); } if (useFlate) { str = new FlateEncoder(str); } else if (useLZW) { str = new LZWEncoder(str); } else if (useRLE) { str = new RunLengthEncoder(str); } if (doUseASCIIHex) { str = new ASCIIHexEncoder(str); } else { str = new ASCII85Encoder(str); } // compute image data size str->reset(); col = size = 0; do { do { c = str->getChar(); } while (c == '\n' || c == '\r'); if (c == (doUseASCIIHex ? '>' : '~') || c == EOF) { break; } if (c == 'z') { ++col; } else { ++col; for (i = 1; i <= (doUseASCIIHex ? 1 : 4); ++i) { do { c = str->getChar(); } while (c == '\n' || c == '\r'); if (c == (doUseASCIIHex ? '>' : '~') || c == EOF) { break; } ++col; } if (c == (doUseASCIIHex ? '>' : '~') || c == EOF) { break; } } if (col > 225) { ++size; col = 0; } } while (c != (doUseASCIIHex ? '>' : '~') && c != EOF); // add one entry for the final line of data; add another entry // because the LZWDecode/RunLengthDecode filter may read past the end ++size; if (useLZW || useRLE) { ++size; } outerSize = size / 65535 + 1; writePSFmt("{0:d} array dup /{1:s}Data_{2:d}_{3:d} exch def\n", outerSize, mask ? "Mask" : "Im", id.num, id.gen); str->close(); // write the data into the array str->reset(); for (outer = 0; outer < outerSize; outer++) { int innerSize = size > 65535 ? 65535 : size; // put the inner array into the outer array writePSFmt("{0:d} array 1 index {1:d} 2 index put\n", innerSize, outer); line = col = 0; writePS((char *)(doUseASCIIHex ? "dup 0 <" : "dup 0 <~")); for (;;) { do { c = str->getChar(); } while (c == '\n' || c == '\r'); if (c == (doUseASCIIHex ? '>' : '~') || c == EOF) { break; } if (c == 'z') { writePSChar(c); ++col; } else { writePSChar(c); ++col; for (i = 1; i <= (doUseASCIIHex ? 1 : 4); ++i) { do { c = str->getChar(); } while (c == '\n' || c == '\r'); if (c == (doUseASCIIHex ? '>' : '~') || c == EOF) { break; } writePSChar(c); ++col; } } if (c == (doUseASCIIHex ? '>' : '~') || c == EOF) { break; } // each line is: "dup nnnnn <~...data...~> put" // so max data length = 255 - 20 = 235 // chunks are 1 or 4 bytes each, so we have to stop at 232 // but make it 225 just to be safe if (col > 225) { writePS((char *)(doUseASCIIHex ? "> put\n" : "~> put\n")); ++line; if (line >= innerSize) { break; } writePSFmt((char *)(doUseASCIIHex ? "dup {0:d} <" : "dup {0:d} <~"), line); col = 0; } } if (c == (doUseASCIIHex ? '>' : '~') || c == EOF) { writePS((char *)(doUseASCIIHex ? "> put\n" : "~> put\n")); if (useLZW || useRLE) { ++line; writePSFmt("{0:d} <> put\n", line); } else { writePS("pop\n"); } break; } writePS("pop\n"); size -= innerSize; } writePS("pop\n"); str->close(); delete str; } void PSOutputDev::setupForms(Dict *resDict) { if (!preloadImagesForms) { return; } Object xObjDict = resDict->lookup("XObject"); if (xObjDict.isDict()) { for (int i = 0; i < xObjDict.dictGetLength(); ++i) { const Object &xObjRef = xObjDict.dictGetValNF(i); Object xObj = xObjDict.dictGetVal(i); if (xObj.isStream()) { Object subtypeObj = xObj.streamGetDict()->lookup("Subtype"); if (subtypeObj.isName("Form")) { if (xObjRef.isRef()) { setupForm(xObjRef.getRef(), &xObj); } else { error(errSyntaxError, -1, "Form in resource dict is not an indirect reference"); } } } } } } void PSOutputDev::setupForm(Ref id, Object *strObj) { Dict *dict, *resDict; double m[6], bbox[4]; PDFRectangle box; Gfx *gfx; // check if form is already defined for (int i = 0; i < formIDLen; ++i) { if (formIDs[i] == id) { return; } } // add entry to formIDs list if (formIDLen >= formIDSize) { if (formIDSize == 0) { formIDSize = 64; } else { formIDSize *= 2; } formIDs = (Ref *)greallocn(formIDs, formIDSize, sizeof(Ref)); } formIDs[formIDLen++] = id; dict = strObj->streamGetDict(); // get bounding box Object bboxObj = dict->lookup("BBox"); if (!bboxObj.isArray()) { error(errSyntaxError, -1, "Bad form bounding box"); return; } for (int i = 0; i < 4; ++i) { Object obj1 = bboxObj.arrayGet(i); bbox[i] = obj1.getNum(); } // get matrix Object matrixObj = dict->lookup("Matrix"); if (matrixObj.isArray()) { for (int i = 0; i < 6; ++i) { Object obj1 = matrixObj.arrayGet(i); m[i] = obj1.getNum(); } } else { m[0] = 1; m[1] = 0; m[2] = 0; m[3] = 1; m[4] = 0; m[5] = 0; } // get resources Object resObj = dict->lookup("Resources"); resDict = resObj.isDict() ? resObj.getDict() : nullptr; writePSFmt("/f_{0:d}_{1:d} {{\n", id.num, id.gen); writePS("q\n"); writePSFmt("[{0:.6gs} {1:.6gs} {2:.6gs} {3:.6gs} {4:.6gs} {5:.6gs}] cm\n", m[0], m[1], m[2], m[3], m[4], m[5]); box.x1 = bbox[0]; box.y1 = bbox[1]; box.x2 = bbox[2]; box.y2 = bbox[3]; gfx = new Gfx(doc, this, resDict, &box, &box); gfx->display(strObj); delete gfx; writePS("Q\n"); writePS("} def\n"); } bool PSOutputDev::checkPageSlice(Page *page, double /*hDPI*/, double /*vDPI*/, int rotateA, 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) { PreScanOutputDev *scan; bool rasterize; bool useFlate, useLZW; SplashOutputDev *splashOut; SplashColor paperColor; PDFRectangle box; GfxState *state; SplashBitmap *bitmap; Stream *str0, *str; unsigned char *p; unsigned char col[4]; double hDPI2, vDPI2; double m0, m1, m2, m3, m4, m5; int nStripes, stripeH, stripeY; int c, w, h, x, y, comp, i; int numComps, initialNumComps; char hexBuf[32 * 2 + 2]; // 32 values X 2 chars/value + line ending + null unsigned char digit; bool isOptimizedGray; bool overprint; SplashColorMode internalColorFormat; if (!postInitDone) { postInit(); } if (forceRasterize == psAlwaysRasterize) { rasterize = true; } else if (forceRasterize == psNeverRasterize) { rasterize = false; } else { scan = new PreScanOutputDev(level); page->displaySlice(scan, 72, 72, rotateA, useMediaBox, crop, sliceX, sliceY, sliceW, sliceH, printing, abortCheckCbk, abortCheckCbkData, annotDisplayDecideCbk, annotDisplayDecideCbkData); rasterize = scan->usesTransparency() || scan->usesPatternImageMask(); delete scan; } if (!rasterize) { return true; } // get the rasterization parameters useFlate = getEnableFlate() && level >= psLevel3; useLZW = getEnableLZW(); // start the PS page page->makeBox(rasterResolution, rasterResolution, rotateA, useMediaBox, false, sliceX, sliceY, sliceW, sliceH, &box, &crop); rotateA += page->getRotate(); if (rotateA >= 360) { rotateA -= 360; } else if (rotateA < 0) { rotateA += 360; } state = new GfxState(rasterResolution, rasterResolution, &box, rotateA, false); startPage(page->getNum(), state, xref); delete state; // If we would not rasterize this page, we would emit the overprint code anyway for language level 2 and upwards. // As such it is safe to assume for a CMYK printer that it would respect the overprint operands. overprint = overprintPreview || (processColorFormat == splashModeCMYK8 && level >= psLevel2); // set up the SplashOutputDev internalColorFormat = processColorFormat; if (processColorFormat == splashModeMono8) { numComps = 1; paperColor[0] = 0xff; } else if (processColorFormat == splashModeCMYK8) { numComps = 4; splashClearColor(paperColor); // If overprinting is emulated, it is not sufficient to just store the CMYK values in a bitmap. // All separation channels need to be stored and collapsed at the end. // Cf. PDF32000_2008 Section 11.7.4.5 and Tables 148, 149 if (overprint) { internalColorFormat = splashModeDeviceN8; } } else if (processColorFormat == splashModeRGB8) { numComps = 3; paperColor[0] = paperColor[1] = paperColor[2] = 0xff; } else { error(errUnimplemented, -1, "Unsupported processColorMode. Falling back to RGB8."); processColorFormat = splashModeRGB8; internalColorFormat = processColorFormat; numComps = 3; paperColor[0] = paperColor[1] = paperColor[2] = 0xff; } splashOut = new SplashOutputDev(internalColorFormat, 1, false, paperColor, false, splashThinLineDefault, overprint); splashOut->setFontAntialias(rasterAntialias); splashOut->setVectorAntialias(rasterAntialias); #ifdef USE_CMS splashOut->setDisplayProfile(getDisplayProfile()); splashOut->setDefaultGrayProfile(getDefaultGrayProfile()); splashOut->setDefaultRGBProfile(getDefaultRGBProfile()); splashOut->setDefaultCMYKProfile(getDefaultCMYKProfile()); #endif splashOut->startDoc(doc); // break the page into stripes hDPI2 = xScale * rasterResolution; vDPI2 = yScale * rasterResolution; if (sliceW < 0 || sliceH < 0) { if (useMediaBox) { box = *page->getMediaBox(); } else { box = *page->getCropBox(); } sliceX = sliceY = 0; sliceW = (int)((box.x2 - box.x1) * hDPI2 / 72.0); sliceH = (int)((box.y2 - box.y1) * vDPI2 / 72.0); } int sliceArea; if (checkedMultiply(sliceW, sliceH, &sliceArea)) { delete splashOut; return false; } nStripes = (int)ceil((double)(sliceArea) / (double)rasterizationSliceSize); if (unlikely(nStripes == 0)) { delete splashOut; return false; } stripeH = (sliceH + nStripes - 1) / nStripes; // render the stripes initialNumComps = numComps; for (stripeY = sliceY; stripeY < sliceH; stripeY += stripeH) { // rasterize a stripe page->makeBox(hDPI2, vDPI2, 0, useMediaBox, false, sliceX, stripeY, sliceW, stripeH, &box, &crop); m0 = box.x2 - box.x1; m1 = 0; m2 = 0; m3 = box.y2 - box.y1; m4 = box.x1; m5 = box.y1; page->displaySlice(splashOut, hDPI2, vDPI2, (360 - page->getRotate()) % 360, useMediaBox, crop, sliceX, stripeY, sliceW, stripeH, printing, abortCheckCbk, abortCheckCbkData, annotDisplayDecideCbk, annotDisplayDecideCbkData); // draw the rasterized image bitmap = splashOut->getBitmap(); numComps = initialNumComps; w = bitmap->getWidth(); h = bitmap->getHeight(); writePS("gsave\n"); writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n", m0, m1, m2, m3, m4, m5); switch (level) { case psLevel1: writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1{5:s}\n", w, h, w, -h, h, useBinary ? "Bin" : ""); p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize(); i = 0; if (useBinary) { for (y = 0; y < h; ++y) { for (x = 0; x < w; ++x) { hexBuf[i++] = *p++; if (i >= 64) { writePSBuf(hexBuf, i); i = 0; } } } } else { for (y = 0; y < h; ++y) { for (x = 0; x < w; ++x) { digit = *p / 16; hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0'); digit = *p++ % 16; hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0'); if (i >= 64) { hexBuf[i++] = '\n'; writePSBuf(hexBuf, i); i = 0; } } } } if (i != 0) { if (!useBinary) { hexBuf[i++] = '\n'; } writePSBuf(hexBuf, i); } break; case psLevel1Sep: p = bitmap->getDataPtr(); // Check for an all gray image if (getOptimizeColorSpace()) { isOptimizedGray = true; for (y = 0; y < h; ++y) { for (x = 0; x < w; ++x) { if (p[4 * x] != p[4 * x + 1] || p[4 * x] != p[4 * x + 2]) { isOptimizedGray = false; y = h; break; } } p += bitmap->getRowSize(); } } else { isOptimizedGray = false; } writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1{5:s}{6:s}\n", w, h, w, -h, h, isOptimizedGray ? "" : "Sep", useBinary ? "Bin" : ""); p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize(); i = 0; col[0] = col[1] = col[2] = col[3] = 0; if (isOptimizedGray) { int g; if ((psProcessBlack & processColors) == 0) { // Check if the image uses black for (y = 0; y < h; ++y) { for (x = 0; x < w; ++x) { if (p[4 * x] > 0 || p[4 * x + 3] > 0) { col[3] = 1; y = h; break; } } p -= bitmap->getRowSize(); } p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize(); } for (y = 0; y < h; ++y) { if (useBinary) { // Binary gray image for (x = 0; x < w; ++x) { g = p[4 * x] + p[4 * x + 3]; g = 255 - g; if (g < 0) { g = 0; } hexBuf[i++] = (unsigned char)g; if (i >= 64) { writePSBuf(hexBuf, i); i = 0; } } } else { // Hex gray image for (x = 0; x < w; ++x) { g = p[4 * x] + p[4 * x + 3]; g = 255 - g; if (g < 0) { g = 0; } digit = g / 16; hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0'); digit = g % 16; hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0'); if (i >= 64) { hexBuf[i++] = '\n'; writePSBuf(hexBuf, i); i = 0; } } } p -= bitmap->getRowSize(); } } else if (((psProcessCyan | psProcessMagenta | psProcessYellow | psProcessBlack) & ~processColors) != 0) { // Color image, need to check color flags for each dot for (y = 0; y < h; ++y) { for (comp = 0; comp < 4; ++comp) { if (useBinary) { // Binary color image for (x = 0; x < w; ++x) { col[comp] |= p[4 * x + comp]; hexBuf[i++] = p[4 * x + comp]; if (i >= 64) { writePSBuf(hexBuf, i); i = 0; } } } else { // Gray color image for (x = 0; x < w; ++x) { col[comp] |= p[4 * x + comp]; digit = p[4 * x + comp] / 16; hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0'); digit = p[4 * x + comp] % 16; hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0'); if (i >= 64) { hexBuf[i++] = '\n'; writePSBuf(hexBuf, i); i = 0; } } } } p -= bitmap->getRowSize(); } } else { // Color image, do not need to check color flags for (y = 0; y < h; ++y) { for (comp = 0; comp < 4; ++comp) { if (useBinary) { // Binary color image for (x = 0; x < w; ++x) { hexBuf[i++] = p[4 * x + comp]; if (i >= 64) { writePSBuf(hexBuf, i); i = 0; } } } else { // Hex color image for (x = 0; x < w; ++x) { digit = p[4 * x + comp] / 16; hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0'); digit = p[4 * x + comp] % 16; hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0'); if (i >= 64) { hexBuf[i++] = '\n'; writePSBuf(hexBuf, i); i = 0; } } } } p -= bitmap->getRowSize(); } } if (i != 0) { if (!useBinary) { hexBuf[i++] = '\n'; } writePSBuf(hexBuf, i); } if (col[0]) { processColors |= psProcessCyan; } if (col[1]) { processColors |= psProcessMagenta; } if (col[2]) { processColors |= psProcessYellow; } if (col[3]) { processColors |= psProcessBlack; } break; case psLevel2: case psLevel2Sep: case psLevel3: case psLevel3Sep: p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize(); if (processColorFormat == splashModeCMYK8 && internalColorFormat != splashModeCMYK8) { str0 = new SplashBitmapCMYKEncoder(bitmap); } else { str0 = new MemStream((char *)p, 0, w * h * numComps, Object(objNull)); } // Check for a color image that uses only gray if (!getOptimizeColorSpace()) { isOptimizedGray = false; } else if (numComps == 4) { int compCyan; isOptimizedGray = true; while ((compCyan = str0->getChar()) != EOF) { if (str0->getChar() != compCyan || str0->getChar() != compCyan) { isOptimizedGray = false; break; } str0->getChar(); } } else if (numComps == 3) { int compRed; isOptimizedGray = true; while ((compRed = str0->getChar()) != EOF) { if (str0->getChar() != compRed || str0->getChar() != compRed) { isOptimizedGray = false; break; } } } else { isOptimizedGray = false; } str0->reset(); if (useFlate) { if (isOptimizedGray && numComps == 4) { str = new FlateEncoder(new CMYKGrayEncoder(str0)); numComps = 1; } else if (isOptimizedGray && numComps == 3) { str = new FlateEncoder(new RGBGrayEncoder(str0)); numComps = 1; } else { str = new FlateEncoder(str0); } } else if (useLZW) { if (isOptimizedGray && numComps == 4) { str = new LZWEncoder(new CMYKGrayEncoder(str0)); numComps = 1; } else if (isOptimizedGray && numComps == 3) { str = new LZWEncoder(new RGBGrayEncoder(str0)); numComps = 1; } else { str = new LZWEncoder(str0); } } else { if (isOptimizedGray && numComps == 4) { str = new RunLengthEncoder(new CMYKGrayEncoder(str0)); numComps = 1; } else if (isOptimizedGray && numComps == 3) { str = new RunLengthEncoder(new RGBGrayEncoder(str0)); numComps = 1; } else { str = new RunLengthEncoder(str0); } } if (numComps == 1) { writePS("/DeviceGray setcolorspace\n"); } else if (numComps == 3) { writePS("/DeviceRGB setcolorspace\n"); } else { writePS("/DeviceCMYK setcolorspace\n"); } writePS("<<\n /ImageType 1\n"); writePSFmt(" /Width {0:d}\n", bitmap->getWidth()); writePSFmt(" /Height {0:d}\n", bitmap->getHeight()); writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", w, -h, h); writePS(" /BitsPerComponent 8\n"); if (numComps == 1) { // the optimized gray variants are implemented as a subtractive color space, // such that the range is flipped for them if (isOptimizedGray) { writePS(" /Decode [1 0]\n"); } else { writePS(" /Decode [0 1]\n"); } } else if (numComps == 3) { writePS(" /Decode [0 1 0 1 0 1]\n"); } else { writePS(" /Decode [0 1 0 1 0 1 0 1]\n"); } writePS(" /DataSource currentfile\n"); if (useBinary) { /* nothing to do */; } else if (useASCIIHex) { writePS(" /ASCIIHexDecode filter\n"); } else { writePS(" /ASCII85Decode filter\n"); } if (useFlate) { writePS(" /FlateDecode filter\n"); } else if (useLZW) { writePS(" /LZWDecode filter\n"); } else { writePS(" /RunLengthDecode filter\n"); } writePS(">>\n"); if (useBinary) { /* nothing to do */; } else if (useASCIIHex) { str = new ASCIIHexEncoder(str); } else { str = new ASCII85Encoder(str); } str->reset(); if (useBinary) { // Count the bytes to write a document comment int len = 0; while (str->getChar() != EOF) { len++; } str->reset(); writePSFmt("%%BeginData: {0:d} Binary Bytes\n", len + 6 + 1); } writePS("image\n"); while ((c = str->getChar()) != EOF) { writePSChar(c); } str->close(); delete str; delete str0; writePSChar('\n'); if (useBinary) { writePS("%%EndData\n"); } processColors |= (numComps == 1) ? psProcessBlack : psProcessCMYK; break; } writePS("grestore\n"); } delete splashOut; // finish the PS page endPage(); return false; } void PSOutputDev::startPage(int pageNum, GfxState *state, XRef *xrefA) { Page *page; int x1, y1, x2, y2, width, height, t; int imgWidth, imgHeight, imgWidth2, imgHeight2; bool landscape; GooString *s; if (!postInitDone) { postInit(); } xref = xrefA; if (mode == psModePS) { GooString pageLabel; const bool gotLabel = doc->getCatalog()->indexToLabel(pageNum - 1, &pageLabel); if (gotLabel) { // See bug13338 for why we try to avoid parentheses... bool needParens; GooString *filteredString = filterPSLabel(&pageLabel, &needParens); if (needParens) { writePSFmt("%%Page: ({0:t}) {1:d}\n", filteredString, seqPage); } else { writePSFmt("%%Page: {0:t} {1:d}\n", filteredString, seqPage); } delete filteredString; } else { writePSFmt("%%Page: {0:d} {1:d}\n", pageNum, seqPage); } if (paperMatch) { page = doc->getCatalog()->getPage(pageNum); imgLLX = imgLLY = 0; if (noCrop) { imgURX = (int)ceil(page->getMediaWidth()); imgURY = (int)ceil(page->getMediaHeight()); } else { imgURX = (int)ceil(page->getCropWidth()); imgURY = (int)ceil(page->getCropHeight()); } if (state->getRotate() == 90 || state->getRotate() == 270) { t = imgURX; imgURX = imgURY; imgURY = t; } } } // underlays if (underlayCbk) { (*underlayCbk)(this, underlayCbkData); } if (overlayCbk) { saveState(nullptr); } xScale = yScale = 1; switch (mode) { case psModePS: // rotate, translate, and scale page imgWidth = imgURX - imgLLX; imgHeight = imgURY - imgLLY; x1 = (int)floor(state->getX1()); y1 = (int)floor(state->getY1()); x2 = (int)ceil(state->getX2()); y2 = (int)ceil(state->getY2()); if (unlikely(checkedSubtraction(x2, x1, &width))) { error(errSyntaxError, -1, "width too big"); return; } if (unlikely(checkedSubtraction(y2, y1, &height))) { error(errSyntaxError, -1, "height too big"); return; } tx = ty = 0; // rotation and portrait/landscape mode if (paperMatch) { rotate = (360 - state->getRotate()) % 360; landscape = false; } else if (rotate0 >= 0) { rotate = (360 - rotate0) % 360; landscape = false; } else { rotate = (360 - state->getRotate()) % 360; if (rotate == 0 || rotate == 180) { if ((width < height && imgWidth > imgHeight && height > imgHeight) || (width > height && imgWidth < imgHeight && width > imgWidth)) { rotate += 90; landscape = true; } else { landscape = false; } } else { // rotate == 90 || rotate == 270 if ((height < width && imgWidth > imgHeight && width > imgHeight) || (height > width && imgWidth < imgHeight && height > imgWidth)) { rotate = 270 - rotate; landscape = true; } else { landscape = false; } } } if (rotate == 0) { imgWidth2 = imgWidth; imgHeight2 = imgHeight; } else if (rotate == 90) { ty = -imgWidth; imgWidth2 = imgHeight; imgHeight2 = imgWidth; } else if (rotate == 180) { imgWidth2 = imgWidth; imgHeight2 = imgHeight; tx = -imgWidth; ty = -imgHeight; } else { // rotate == 270 tx = -imgHeight; imgWidth2 = imgHeight; imgHeight2 = imgWidth; } // shrink or expand if (xScale0 > 0 && yScale0 > 0) { xScale = xScale0; yScale = yScale0; } else if ((psShrinkLarger && (width > imgWidth2 || height > imgHeight2)) || (psExpandSmaller && (width < imgWidth2 && height < imgHeight2))) { if (unlikely(width == 0)) { error(errSyntaxError, -1, "width 0, xScale would be infinite"); return; } xScale = (double)imgWidth2 / (double)width; yScale = (double)imgHeight2 / (double)height; if (yScale < xScale) { xScale = yScale; } else { yScale = xScale; } } // deal with odd bounding boxes or clipping if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) { tx -= xScale * clipLLX0; ty -= yScale * clipLLY0; } else { tx -= xScale * x1; ty -= yScale * y1; } // center if (tx0 >= 0 && ty0 >= 0) { tx += (rotate == 0 || rotate == 180) ? tx0 : ty0; ty += (rotate == 0 || rotate == 180) ? ty0 : -tx0; } else if (psCenter) { if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) { tx += (imgWidth2 - xScale * (clipURX0 - clipLLX0)) / 2; ty += (imgHeight2 - yScale * (clipURY0 - clipLLY0)) / 2; } else { tx += (imgWidth2 - xScale * width) / 2; ty += (imgHeight2 - yScale * height) / 2; } } tx += (rotate == 0 || rotate == 180) ? imgLLX : imgLLY; ty += (rotate == 0 || rotate == 180) ? imgLLY : -imgLLX; if (paperMatch) { 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 Matrix m; switch (rotate) { default: case 0: m.init(1, 0, 0, 1, 0, 0); break; case 90: m.init(0, 1, -1, 0, 0, 0); break; case 180: m.init(-1, 0, 0, -1, 0, 0); break; case 270: m.init(0, -1, 1, 0, 0, 0); break; } m.translate(tx, ty); m.scale(xScale, yScale); double bboxX1, bboxY1, bboxX2, bboxY2; m.transform(0, 0, &bboxX1, &bboxY1); m.transform(width, height, &bboxX2, &bboxY2); writePSFmt("%%PageBoundingBox: {0:g} {1:g} {2:g} {3:g}\n", floor(std::min(bboxX1, bboxX2)), floor(std::min(bboxY1, bboxY2)), ceil(std::max(bboxX1, bboxX2)), ceil(std::max(bboxY1, bboxY2))); writePSFmt("%%PageOrientation: {0:s}\n", landscape ? "Landscape" : "Portrait"); writePS("%%BeginPageSetup\n"); if (paperMatch) { writePSFmt("{0:d} {1:d} pdfSetupPaper\n", imgURX, imgURY); } writePS("pdfStartPage\n"); if (rotate) { writePSFmt("{0:d} rotate\n", rotate); } if (tx != 0 || ty != 0) { writePSFmt("{0:.6g} {1:.6g} translate\n", tx, ty); } if (xScale != 1 || yScale != 1) { writePSFmt("{0:.6f} {1:.6f} scale\n", xScale, yScale); } if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) { writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re W\n", clipLLX0, clipLLY0, clipURX0 - clipLLX0, clipURY0 - clipLLY0); } else { writePSFmt("{0:d} {1:d} {2:d} {3:d} re W\n", x1, y1, x2 - x1, y2 - y1); } ++seqPage; break; case psModeEPS: writePS("pdfStartPage\n"); tx = ty = 0; rotate = (360 - state->getRotate()) % 360; if (rotate == 0) { } else if (rotate == 90) { writePS("90 rotate\n"); tx = -epsX1; ty = -epsY2; } else if (rotate == 180) { writePS("180 rotate\n"); tx = -(epsX1 + epsX2); ty = -(epsY1 + epsY2); } else { // rotate == 270 writePS("270 rotate\n"); tx = -epsX2; ty = -epsY1; } if (tx != 0 || ty != 0) { writePSFmt("{0:.6g} {1:.6g} translate\n", tx, ty); } break; case psModeForm: writePS("/PaintProc {\n"); writePS("begin xpdf begin\n"); writePS("pdfStartPage\n"); tx = ty = 0; rotate = 0; break; } if (customCodeCbk) { if ((s = (*customCodeCbk)(this, psOutCustomPageSetup, pageNum, customCodeCbkData))) { writePS(s->c_str()); delete s; } } writePS("%%EndPageSetup\n"); } void PSOutputDev::endPage() { if (overlayCbk) { restoreState(nullptr); (*overlayCbk)(this, overlayCbkData); } for (const auto &item : iccEmitted) { writePSFmt("userdict /{0:s} undef\n", item.c_str()); } iccEmitted.clear(); if (mode == psModeForm) { writePS("pdfEndPage\n"); writePS("end end\n"); writePS("} def\n"); writePS("end end\n"); } else { if (!manualCtrl) { writePS("showpage\n"); } writePS("%%PageTrailer\n"); writePageTrailer(); } } void PSOutputDev::saveState(GfxState *state) { writePS("q\n"); ++numSaves; } void PSOutputDev::restoreState(GfxState *state) { writePS("Q\n"); --numSaves; } void PSOutputDev::updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32) { writePSFmt("[{0:.6gs} {1:.6gs} {2:.6gs} {3:.6gs} {4:.6gs} {5:.6gs}] cm\n", m11, m12, m21, m22, m31, m32); } void PSOutputDev::updateLineDash(GfxState *state) { double start; const std::vector &dash = state->getLineDash(&start); writePS("["); for (std::vector::size_type i = 0; i < dash.size(); ++i) { writePSFmt("{0:.6g}{1:w}", dash[i] < 0 ? 0 : dash[i], (i == dash.size() - 1) ? 0 : 1); } writePSFmt("] {0:.6g} d\n", start); } void PSOutputDev::updateFlatness(GfxState *state) { writePSFmt("{0:d} i\n", state->getFlatness()); } void PSOutputDev::updateLineJoin(GfxState *state) { writePSFmt("{0:d} j\n", state->getLineJoin()); } void PSOutputDev::updateLineCap(GfxState *state) { writePSFmt("{0:d} J\n", state->getLineCap()); } void PSOutputDev::updateMiterLimit(GfxState *state) { writePSFmt("{0:.6g} M\n", state->getMiterLimit()); } void PSOutputDev::updateLineWidth(GfxState *state) { writePSFmt("{0:.6g} w\n", state->getLineWidth()); } void PSOutputDev::updateFillColorSpace(GfxState *state) { if (inUncoloredPattern) { return; } switch (level) { case psLevel1: case psLevel1Sep: break; case psLevel2: case psLevel3: if (state->getFillColorSpace()->getMode() != csPattern) { dumpColorSpaceL2(state, state->getFillColorSpace(), true, false, false); writePS(" cs\n"); } break; case psLevel2Sep: case psLevel3Sep: break; } } void PSOutputDev::updateStrokeColorSpace(GfxState *state) { if (inUncoloredPattern) { return; } switch (level) { case psLevel1: case psLevel1Sep: break; case psLevel2: case psLevel3: if (state->getStrokeColorSpace()->getMode() != csPattern) { dumpColorSpaceL2(state, state->getStrokeColorSpace(), true, false, false); writePS(" CS\n"); } break; case psLevel2Sep: case psLevel3Sep: break; } } void PSOutputDev::updateFillColor(GfxState *state) { GfxColor color; GfxGray gray; GfxCMYK cmyk; GfxSeparationColorSpace *sepCS; double c, m, y, k; int i; if (inUncoloredPattern) { return; } switch (level) { case psLevel1: state->getFillGray(&gray); writePSFmt("{0:.4g} g\n", colToDbl(gray)); break; case psLevel2: case psLevel3: if (state->getFillColorSpace()->getMode() != csPattern) { const GfxColor *colorPtr = state->getFillColor(); writePS("["); for (i = 0; i < state->getFillColorSpace()->getNComps(); ++i) { if (i > 0) { writePS(" "); } writePSFmt("{0:.4g}", colToDbl(colorPtr->c[i])); } writePS("] sc\n"); } break; case psLevel1Sep: case psLevel2Sep: case psLevel3Sep: if (state->getFillColorSpace()->getMode() == csSeparation && (level > psLevel1Sep || getPassLevel1CustomColor())) { sepCS = (GfxSeparationColorSpace *)state->getFillColorSpace(); color.c[0] = gfxColorComp1; sepCS->getCMYK(&color, &cmyk); writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} ({5:t}) ck\n", colToDbl(state->getFillColor()->c[0]), colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k), sepCS->getName()); addCustomColor(sepCS); } else { state->getFillCMYK(&cmyk); c = colToDbl(cmyk.c); m = colToDbl(cmyk.m); y = colToDbl(cmyk.y); k = colToDbl(cmyk.k); if (getOptimizeColorSpace()) { double g; g = 0.299 * c + 0.587 * m + 0.114 * y; if ((fabs(m - c) < 0.01 && fabs(m - y) < 0.01) || (fabs(m - c) < 0.2 && fabs(m - y) < 0.2 && k + g > 1.5)) { c = m = y = 0.0; k += g; if (k > 1.0) { k = 1.0; } } } writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} k\n", c, m, y, k); addProcessColor(c, m, y, k); } break; } t3Cacheable = false; } void PSOutputDev::updateStrokeColor(GfxState *state) { GfxColor color; GfxGray gray; GfxCMYK cmyk; GfxSeparationColorSpace *sepCS; double c, m, y, k; int i; if (inUncoloredPattern) { return; } switch (level) { case psLevel1: state->getStrokeGray(&gray); writePSFmt("{0:.4g} G\n", colToDbl(gray)); break; case psLevel2: case psLevel3: if (state->getStrokeColorSpace()->getMode() != csPattern) { const GfxColor *colorPtr = state->getStrokeColor(); writePS("["); for (i = 0; i < state->getStrokeColorSpace()->getNComps(); ++i) { if (i > 0) { writePS(" "); } writePSFmt("{0:.4g}", colToDbl(colorPtr->c[i])); } writePS("] SC\n"); } break; case psLevel1Sep: case psLevel2Sep: case psLevel3Sep: if (state->getStrokeColorSpace()->getMode() == csSeparation && (level > psLevel1Sep || getPassLevel1CustomColor())) { sepCS = (GfxSeparationColorSpace *)state->getStrokeColorSpace(); color.c[0] = gfxColorComp1; sepCS->getCMYK(&color, &cmyk); writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} ({5:t}) CK\n", colToDbl(state->getStrokeColor()->c[0]), colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k), sepCS->getName()); addCustomColor(sepCS); } else { state->getStrokeCMYK(&cmyk); c = colToDbl(cmyk.c); m = colToDbl(cmyk.m); y = colToDbl(cmyk.y); k = colToDbl(cmyk.k); if (getOptimizeColorSpace()) { double g; g = 0.299 * c + 0.587 * m + 0.114 * y; if ((fabs(m - c) < 0.01 && fabs(m - y) < 0.01) || (fabs(m - c) < 0.2 && fabs(m - y) < 0.2 && k + g > 1.5)) { c = m = y = 0.0; k += g; if (k > 1.0) { k = 1.0; } } } writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} K\n", c, m, y, k); addProcessColor(c, m, y, k); } break; } t3Cacheable = false; } void PSOutputDev::addProcessColor(double c, double m, double y, double k) { if (c > 0) { processColors |= psProcessCyan; } if (m > 0) { processColors |= psProcessMagenta; } if (y > 0) { processColors |= psProcessYellow; } if (k > 0) { processColors |= psProcessBlack; } } void PSOutputDev::addCustomColor(GfxSeparationColorSpace *sepCS) { PSOutCustomColor *cc; GfxColor color; GfxCMYK cmyk; if (!sepCS->getName()->cmp("Black")) { processColors |= psProcessBlack; return; } if (!sepCS->getName()->cmp("Cyan")) { processColors |= psProcessCyan; return; } if (!sepCS->getName()->cmp("Yellow")) { processColors |= psProcessYellow; return; } if (!sepCS->getName()->cmp("Magenta")) { processColors |= psProcessMagenta; return; } if (!sepCS->getName()->cmp("All")) { return; } if (!sepCS->getName()->cmp("None")) { return; } for (cc = customColors; cc; cc = cc->next) { if (!cc->name->cmp(sepCS->getName())) { return; } } color.c[0] = gfxColorComp1; sepCS->getCMYK(&color, &cmyk); cc = new PSOutCustomColor(colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k), sepCS->getName()->copy()); cc->next = customColors; customColors = cc; } void PSOutputDev::updateFillOverprint(GfxState *state) { if (level >= psLevel2) { writePSFmt("{0:s} op\n", state->getFillOverprint() ? "true" : "false"); } } void PSOutputDev::updateStrokeOverprint(GfxState *state) { if (level >= psLevel2) { writePSFmt("{0:s} OP\n", state->getStrokeOverprint() ? "true" : "false"); } } void PSOutputDev::updateOverprintMode(GfxState *state) { if (level >= psLevel3) { writePSFmt("{0:s} opm\n", state->getOverprintMode() ? "true" : "false"); } } void PSOutputDev::updateTransfer(GfxState *state) { Function **funcs; int i; funcs = state->getTransfer(); if (funcs[0] && funcs[1] && funcs[2] && funcs[3]) { if (level >= psLevel2) { for (i = 0; i < 4; ++i) { cvtFunction(funcs[i]); } writePS("setcolortransfer\n"); } else { cvtFunction(funcs[3]); writePS("settransfer\n"); } } else if (funcs[0]) { cvtFunction(funcs[0]); writePS("settransfer\n"); } else { writePS("{} settransfer\n"); } } void PSOutputDev::updateFont(GfxState *state) { if (state->getFont()) { writePSFmt("/F{0:d}_{1:d} {2:.6g} Tf\n", state->getFont()->getID()->num, state->getFont()->getID()->gen, fabs(state->getFontSize()) < 0.0001 ? 0.0001 : state->getFontSize()); } } void PSOutputDev::updateTextMat(GfxState *state) { const double *mat = state->getTextMat(); if (fabs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.00001) { // avoid a singular (or close-to-singular) matrix writePSFmt("[0.00001 0 0 0.00001 {0:.6g} {1:.6g}] Tm\n", mat[4], mat[5]); } else { writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] Tm\n", mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); } } void PSOutputDev::updateCharSpace(GfxState *state) { writePSFmt("{0:.6g} Tc\n", state->getCharSpace()); } void PSOutputDev::updateRender(GfxState *state) { int rm; rm = state->getRender(); writePSFmt("{0:d} Tr\n", rm); rm &= 3; if (rm != 0 && rm != 3) { t3Cacheable = false; } } void PSOutputDev::updateRise(GfxState *state) { writePSFmt("{0:.6g} Ts\n", state->getRise()); } void PSOutputDev::updateWordSpace(GfxState *state) { writePSFmt("{0:.6g} Tw\n", state->getWordSpace()); } void PSOutputDev::updateHorizScaling(GfxState *state) { double h; h = state->getHorizScaling(); if (fabs(h) < 0.01) { h = 0.01; } writePSFmt("{0:.6g} Tz\n", h); } void PSOutputDev::updateTextPos(GfxState *state) { writePSFmt("{0:.6g} {1:.6g} Td\n", state->getLineX(), state->getLineY()); } void PSOutputDev::updateTextShift(GfxState *state, double shift) { if (state->getFont()->getWMode()) { writePSFmt("{0:.6g} TJmV\n", shift); } else { writePSFmt("{0:.6g} TJm\n", shift); } } void PSOutputDev::saveTextPos(GfxState *state) { writePS("currentpoint\n"); } void PSOutputDev::restoreTextPos(GfxState *state) { writePS("m\n"); } void PSOutputDev::stroke(GfxState *state) { doPath(state->getPath()); if (inType3Char && t3FillColorOnly) { // if we're constructing a cacheable Type 3 glyph, we need to do // everything in the fill color writePS("Sf\n"); } else { writePS("S\n"); } } void PSOutputDev::fill(GfxState *state) { doPath(state->getPath()); writePS("f\n"); } void PSOutputDev::eoFill(GfxState *state) { doPath(state->getPath()); writePS("f*\n"); } bool PSOutputDev::tilingPatternFillL1(GfxState *state, Catalog *cat, Object *str, const double *pmat, int paintType, int tilingType, Dict *resDict, const double *mat, const double *bbox, int x0, int y0, int x1, int y1, double xStep, double yStep) { PDFRectangle box; Gfx *gfx; // define a Type 3 font writePS("8 dict begin\n"); writePS("/FontType 3 def\n"); writePS("/FontMatrix [1 0 0 1 0 0] def\n"); writePSFmt("/FontBBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}] def\n", bbox[0], bbox[1], bbox[2], bbox[3]); writePS("/Encoding 256 array def\n"); writePS(" 0 1 255 { Encoding exch /.notdef put } for\n"); writePS(" Encoding 120 /x put\n"); writePS("/BuildGlyph {\n"); writePS(" exch /CharProcs get exch\n"); writePS(" 2 copy known not { pop /.notdef } if\n"); writePS(" get exec\n"); writePS("} bind def\n"); writePS("/BuildChar {\n"); writePS(" 1 index /Encoding get exch get\n"); writePS(" 1 index /BuildGlyph get exec\n"); writePS("} bind def\n"); writePS("/CharProcs 1 dict def\n"); writePS("CharProcs begin\n"); box.x1 = bbox[0]; box.y1 = bbox[1]; box.x2 = bbox[2]; box.y2 = bbox[3]; gfx = new Gfx(doc, this, resDict, &box, nullptr); writePS("/x {\n"); if (paintType == 2) { writePSFmt("{0:.6g} 0 {1:.6g} {2:.6g} {3:.6g} {4:.6g} setcachedevice\n", xStep, bbox[0], bbox[1], bbox[2], bbox[3]); t3FillColorOnly = true; } else { if (x1 - 1 <= x0) { writePS("1 0 setcharwidth\n"); } else { writePSFmt("{0:.6g} 0 setcharwidth\n", xStep); } t3FillColorOnly = false; } inType3Char = true; if (paintType == 2) { inUncoloredPattern = true; // ensure any PS procedures that contain sCol or fCol do not change the color writePS("/pdfLastFill true def\n"); writePS("/pdfLastStroke true def\n"); } ++numTilingPatterns; gfx->display(str); --numTilingPatterns; if (paintType == 2) { inUncoloredPattern = false; // ensure the next PS procedures that uses sCol or fCol will update the color writePS("/pdfLastFill false def\n"); writePS("/pdfLastStroke false def\n"); } inType3Char = false; writePS("} def\n"); delete gfx; writePS("end\n"); writePS("currentdict end\n"); writePSFmt("/xpdfTile{0:d} exch definefont pop\n", numTilingPatterns); // draw the tiles writePSFmt("/xpdfTile{0:d} findfont setfont\n", numTilingPatterns); writePS("fCol\n"); writePSFmt("gsave [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n", mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); writePSFmt("{0:d} 1 {1:d} {{ {2:.6g} exch {3:.6g} mul m {4:d} 1 {5:d} {{ pop (x) show }} for }} for\n", y0, y1 - 1, x0 * xStep, yStep, x0, x1 - 1); writePS("grestore\n"); return true; } bool PSOutputDev::tilingPatternFillL2(GfxState *state, Catalog *cat, Object *str, const double *pmat, int paintType, int tilingType, Dict *resDict, const double *mat, const double *bbox, int x0, int y0, int x1, int y1, double xStep, double yStep) { PDFRectangle box; Gfx *gfx; if (paintType == 2) { // setpattern with PaintType 2 needs the paint color writePS("currentcolor\n"); } writePS("<<\n /PatternType 1\n"); writePSFmt(" /PaintType {0:d}\n", paintType); writePSFmt(" /TilingType {0:d}\n", tilingType); writePSFmt(" /BBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}]\n", bbox[0], bbox[1], bbox[2], bbox[3]); writePSFmt(" /XStep {0:.6g}\n", xStep); writePSFmt(" /YStep {0:.6g}\n", yStep); writePS(" /PaintProc { \n"); box.x1 = bbox[0]; box.y1 = bbox[1]; box.x2 = bbox[2]; box.y2 = bbox[3]; gfx = new Gfx(doc, this, resDict, &box, nullptr); inType3Char = true; if (paintType == 2) { inUncoloredPattern = true; // ensure any PS procedures that contain sCol or fCol do not change the color writePS("/pdfLastFill true def\n"); writePS("/pdfLastStroke true def\n"); } gfx->display(str); if (paintType == 2) { inUncoloredPattern = false; // ensure the next PS procedures that uses sCol or fCol will update the color writePS("/pdfLastFill false def\n"); writePS("/pdfLastStroke false def\n"); } inType3Char = false; delete gfx; writePS(" }\n"); writePS(">>\n"); writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}]\n", mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); writePS("makepattern setpattern\n"); writePS("clippath fill\n"); // Gfx sets up a clip before calling out->tilingPatternFill() return true; } bool PSOutputDev::tilingPatternFill(GfxState *state, Gfx *gfxA, Catalog *cat, GfxTilingPattern *tPat, const double *mat, int x0, int y0, int x1, int y1, double xStep, double yStep) { std::set::iterator patternRefIt; const int patternRefNum = tPat->getPatternRefNum(); if (patternRefNum != -1) { if (patternsBeingTiled.find(patternRefNum) == patternsBeingTiled.end()) { patternRefIt = patternsBeingTiled.insert(patternRefNum).first; } else { // pretend we drew it anyway error(errSyntaxError, -1, "Loop in pattern fills"); return true; } } const double *bbox = tPat->getBBox(); const double *pmat = tPat->getMatrix(); const int paintType = tPat->getPaintType(); const int tilingType = tPat->getTilingType(); Dict *resDict = tPat->getResDict(); Object *str = tPat->getContentStream(); bool res; if (x1 - x0 == 1 && y1 - y0 == 1) { // Don't need to use patterns if only one instance of the pattern is used PDFRectangle box; Gfx *gfx; const double singleStep_x = x0 * xStep; const double singleStep_y = y0 * yStep; const double singleStep_tx = singleStep_x * mat[0] + singleStep_y * mat[2] + mat[4]; const double singleStep_ty = singleStep_x * mat[1] + singleStep_y * mat[3] + mat[5]; box.x1 = bbox[0]; box.y1 = bbox[1]; box.x2 = bbox[2]; box.y2 = bbox[3]; gfx = new Gfx(doc, this, resDict, &box, nullptr, nullptr, nullptr, gfxA); writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] cm\n", mat[0], mat[1], mat[2], mat[3], singleStep_tx, singleStep_ty); inType3Char = true; gfx->display(str); inType3Char = false; delete gfx; res = true; } else if (level == psLevel1 || level == psLevel1Sep) { res = tilingPatternFillL1(state, cat, str, pmat, paintType, tilingType, resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep); } else { res = tilingPatternFillL2(state, cat, str, pmat, paintType, tilingType, resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep); } if (patternRefNum != -1) { patternsBeingTiled.erase(patternRefIt); } return res; } bool PSOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading) { double x0, y0, x1, y1; int i; if (level == psLevel2Sep || level == psLevel3Sep) { if (shading->getColorSpace()->getMode() != csDeviceCMYK) { return false; } processColors |= psProcessCMYK; } shading->getDomain(&x0, &y0, &x1, &y1); const double *mat = shading->getMatrix(); writePSFmt("/mat [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] def\n", mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps()); if (shading->getNFuncs() == 1) { writePS("/func "); cvtFunction(shading->getFunc(0)); writePS("def\n"); } else { writePS("/func {\n"); for (i = 0; i < shading->getNFuncs(); ++i) { if (i < shading->getNFuncs() - 1) { writePS("2 copy\n"); } cvtFunction(shading->getFunc(i)); writePS("exec\n"); if (i < shading->getNFuncs() - 1) { writePS("3 1 roll\n"); } } writePS("} def\n"); } writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} 0 funcSH\n", x0, y0, x1, y1); return true; } bool PSOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double /*tMin*/, double /*tMax*/) { double xMin, yMin, xMax, yMax; double x0, y0, x1, y1, dx, dy, mul; double tMin, tMax, t, t0, t1; int i; if (level == psLevel2Sep || level == psLevel3Sep) { if (shading->getColorSpace()->getMode() != csDeviceCMYK) { return false; } processColors |= psProcessCMYK; } // get the clip region bbox state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); // compute min and max t values, based on the four corners of the // clip region bbox shading->getCoords(&x0, &y0, &x1, &y1); dx = x1 - x0; dy = y1 - y0; if (fabs(dx) < 0.01 && fabs(dy) < 0.01) { return true; } else { mul = 1 / (dx * dx + dy * dy); tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul; t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul; if (t < tMin) { tMin = t; } else if (t > tMax) { tMax = t; } t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul; if (t < tMin) { tMin = t; } else if (t > tMax) { tMax = t; } t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul; if (t < tMin) { tMin = t; } else if (t > tMax) { tMax = t; } if (tMin < 0 && !shading->getExtend0()) { tMin = 0; } if (tMax > 1 && !shading->getExtend1()) { tMax = 1; } } // get the function domain t0 = shading->getDomain0(); t1 = shading->getDomain1(); // generate the PS code writePSFmt("/t0 {0:.6g} def\n", t0); writePSFmt("/t1 {0:.6g} def\n", t1); writePSFmt("/dt {0:.6g} def\n", t1 - t0); writePSFmt("/x0 {0:.6g} def\n", x0); writePSFmt("/y0 {0:.6g} def\n", y0); writePSFmt("/dx {0:.6g} def\n", x1 - x0); writePSFmt("/x1 {0:.6g} def\n", x1); writePSFmt("/y1 {0:.6g} def\n", y1); writePSFmt("/dy {0:.6g} def\n", y1 - y0); writePSFmt("/xMin {0:.6g} def\n", xMin); writePSFmt("/yMin {0:.6g} def\n", yMin); writePSFmt("/xMax {0:.6g} def\n", xMax); writePSFmt("/yMax {0:.6g} def\n", yMax); writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps()); if (shading->getNFuncs() == 1) { writePS("/func "); cvtFunction(shading->getFunc(0)); writePS("def\n"); } else { writePS("/func {\n"); for (i = 0; i < shading->getNFuncs(); ++i) { if (i < shading->getNFuncs() - 1) { writePS("dup\n"); } cvtFunction(shading->getFunc(i)); writePS("exec\n"); if (i < shading->getNFuncs() - 1) { writePS("exch\n"); } } writePS("} def\n"); } writePSFmt("{0:.6g} {1:.6g} 0 axialSH\n", tMin, tMax); return true; } bool PSOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading, double /*sMin*/, double /*sMax*/) { double xMin, yMin, xMax, yMax; double x0, y0, r0, x1, y1, r1, t0, t1; double xa, ya, ra; double sMin, sMax, h, ta; double sLeft, sRight, sTop, sBottom, sZero, sDiag; bool haveSLeft, haveSRight, haveSTop, haveSBottom, haveSZero; bool haveSMin, haveSMax; double theta, alpha, a1, a2; bool enclosed; int i; if (level == psLevel2Sep || level == psLevel3Sep) { if (shading->getColorSpace()->getMode() != csDeviceCMYK) { return false; } processColors |= psProcessCMYK; } // get the shading info shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1); t0 = shading->getDomain0(); t1 = shading->getDomain1(); // Compute the point at which r(s) = 0; check for the enclosed // circles case; and compute the angles for the tangent lines. // Compute the point at which r(s) = 0; check for the enclosed // circles case; and compute the angles for the tangent lines. h = sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)); if (h == 0) { enclosed = true; theta = 0; // make gcc happy } else if (r1 - r0 == 0) { enclosed = false; theta = 0; } else if (fabs(r1 - r0) >= h) { enclosed = true; theta = 0; // make gcc happy } else { enclosed = false; theta = asin((r1 - r0) / h); } if (enclosed) { a1 = 0; a2 = 360; } else { alpha = atan2(y1 - y0, x1 - x0); a1 = (180 / M_PI) * (alpha + theta) + 90; a2 = (180 / M_PI) * (alpha - theta) - 90; while (a2 < a1) { a2 += 360; } } // compute the (possibly extended) s range state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); if (enclosed) { sMin = 0; sMax = 1; } else { // solve x(sLeft) + r(sLeft) = xMin if ((haveSLeft = fabs((x1 + r1) - (x0 + r0)) > 0.000001)) { sLeft = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0)); } else { sLeft = 0; // make gcc happy } // solve x(sRight) - r(sRight) = xMax if ((haveSRight = fabs((x1 - r1) - (x0 - r0)) > 0.000001)) { sRight = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0)); } else { sRight = 0; // make gcc happy } // solve y(sBottom) + r(sBottom) = yMin if ((haveSBottom = fabs((y1 + r1) - (y0 + r0)) > 0.000001)) { sBottom = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0)); } else { sBottom = 0; // make gcc happy } // solve y(sTop) - r(sTop) = yMax if ((haveSTop = fabs((y1 - r1) - (y0 - r0)) > 0.000001)) { sTop = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0)); } else { sTop = 0; // make gcc happy } // solve r(sZero) = 0 if ((haveSZero = fabs(r1 - r0) > 0.000001)) { sZero = -r0 / (r1 - r0); } else { sZero = 0; // make gcc happy } // solve r(sDiag) = sqrt((xMax-xMin)^2 + (yMax-yMin)^2) if (haveSZero) { sDiag = (sqrt((xMax - xMin) * (xMax - xMin) + (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0); } else { sDiag = 0; // make gcc happy } // compute sMin if (shading->getExtend0()) { sMin = 0; haveSMin = false; if (x0 < x1 && haveSLeft && sLeft < 0) { sMin = sLeft; haveSMin = true; } else if (x0 > x1 && haveSRight && sRight < 0) { sMin = sRight; haveSMin = true; } if (y0 < y1 && haveSBottom && sBottom < 0) { if (!haveSMin || sBottom > sMin) { sMin = sBottom; haveSMin = true; } } else if (y0 > y1 && haveSTop && sTop < 0) { if (!haveSMin || sTop > sMin) { sMin = sTop; haveSMin = true; } } if (haveSZero && sZero < 0) { if (!haveSMin || sZero > sMin) { sMin = sZero; } } } else { sMin = 0; } // compute sMax if (shading->getExtend1()) { sMax = 1; haveSMax = false; if (x1 < x0 && haveSLeft && sLeft > 1) { sMax = sLeft; haveSMax = true; } else if (x1 > x0 && haveSRight && sRight > 1) { sMax = sRight; haveSMax = true; } if (y1 < y0 && haveSBottom && sBottom > 1) { if (!haveSMax || sBottom < sMax) { sMax = sBottom; haveSMax = true; } } else if (y1 > y0 && haveSTop && sTop > 1) { if (!haveSMax || sTop < sMax) { sMax = sTop; haveSMax = true; } } if (haveSZero && sDiag > 1) { if (!haveSMax || sDiag < sMax) { sMax = sDiag; } } } else { sMax = 1; } } // generate the PS code writePSFmt("/x0 {0:.6g} def\n", x0); writePSFmt("/x1 {0:.6g} def\n", x1); writePSFmt("/dx {0:.6g} def\n", x1 - x0); writePSFmt("/y0 {0:.6g} def\n", y0); writePSFmt("/y1 {0:.6g} def\n", y1); writePSFmt("/dy {0:.6g} def\n", y1 - y0); writePSFmt("/r0 {0:.6g} def\n", r0); writePSFmt("/r1 {0:.6g} def\n", r1); writePSFmt("/dr {0:.6g} def\n", r1 - r0); writePSFmt("/t0 {0:.6g} def\n", t0); writePSFmt("/t1 {0:.6g} def\n", t1); writePSFmt("/dt {0:.6g} def\n", t1 - t0); writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps()); writePSFmt("/encl {0:s} def\n", enclosed ? "true" : "false"); writePSFmt("/a1 {0:.6g} def\n", a1); writePSFmt("/a2 {0:.6g} def\n", a2); if (shading->getNFuncs() == 1) { writePS("/func "); cvtFunction(shading->getFunc(0)); writePS("def\n"); } else { writePS("/func {\n"); for (i = 0; i < shading->getNFuncs(); ++i) { if (i < shading->getNFuncs() - 1) { writePS("dup\n"); } cvtFunction(shading->getFunc(i)); writePS("exec\n"); if (i < shading->getNFuncs() - 1) { writePS("exch\n"); } } writePS("} def\n"); } writePSFmt("{0:.6g} {1:.6g} 0 radialSH\n", sMin, sMax); // extend the 'enclosed' case if (enclosed) { // extend the smaller circle if ((shading->getExtend0() && r0 <= r1) || (shading->getExtend1() && r1 < r0)) { if (r0 <= r1) { ta = t0; ra = r0; xa = x0; ya = y0; } else { ta = t1; ra = r1; xa = x1; ya = y1; } if (level == psLevel2Sep || level == psLevel3Sep) { writePSFmt("{0:.6g} radialCol aload pop k\n", ta); } else { writePSFmt("{0:.6g} radialCol sc\n", ta); } writePSFmt("{0:.6g} {1:.6g} {2:.6g} 0 360 arc h f*\n", xa, ya, ra); } // extend the larger circle if ((shading->getExtend0() && r0 > r1) || (shading->getExtend1() && r1 >= r0)) { if (r0 > r1) { ta = t0; ra = r0; xa = x0; ya = y0; } else { ta = t1; ra = r1; xa = x1; ya = y1; } if (level == psLevel2Sep || level == psLevel3Sep) { writePSFmt("{0:.6g} radialCol aload pop k\n", ta); } else { writePSFmt("{0:.6g} radialCol sc\n", ta); } writePSFmt("{0:.6g} {1:.6g} {2:.6g} 0 360 arc h\n", xa, ya, ra); writePSFmt("{0:.6g} {1:.6g} m {2:.6g} {3:.6g} l {4:.6g} {5:.6g} l {6:.6g} {7:.6g} l h f*\n", xMin, yMin, xMin, yMax, xMax, yMax, xMax, yMin); } } return true; } bool PSOutputDev::patchMeshShadedFill(GfxState *state, GfxPatchMeshShading *shading) { // TODO: support parametrized shading if (level < psLevel3 || shading->isParameterized()) { return false; } writePS("%% Begin patchMeshShadedFill\n"); // ShadingType 7 shadings are pretty much the same for pdf and ps. // As such, we basically just need to invert GfxPatchMeshShading::parse here. writePS("<<\n"); writePS(" /ShadingType 7\n"); writePS(" /ColorSpace "); dumpColorSpaceL2(state, shading->getColorSpace(), false, false, false); writePS("\n"); writePS(" /DataSource [\n"); const int ncomps = shading->getColorSpace()->getNComps(); for (int i = 0; i < shading->getNPatches(); ++i) { const auto &patch = *shading->getPatch(i); // Print Flag, for us always f = 0 writePS(" 0 \n"); // Print coordinates const std::array, 16> coordindices = { { { 0, 0 }, { 0, 1 }, { 0, 2 }, { 0, 3 }, { 1, 3 }, { 2, 3 }, { 3, 3 }, { 3, 2 }, { 3, 1 }, { 3, 0 }, { 2, 0 }, { 1, 0 }, { 1, 1 }, { 1, 2 }, { 2, 2 }, { 2, 1 } } }; for (const auto &index : coordindices) { writePSFmt(" {0:.6g} {1:.6g}\n", patch.x[index.first][index.second], patch.y[index.first][index.second]); } // Print colors const std::array, 4> colindices = { { { 0, 0 }, { 0, 1 }, { 1, 1 }, { 1, 0 } } }; for (const auto &index : colindices) { writePS(" "); for (int comp = 0; comp < ncomps; ++comp) { writePSFmt(" {0:.6g}", colToDbl(patch.color[index.first][index.second].c[comp])); } writePS("\n"); } } writePS(" ]\n"); writePS(">> shfill\n"); writePS("%% End patchMeshShadedFill\n"); return true; } void PSOutputDev::clip(GfxState *state) { doPath(state->getPath()); writePS("W\n"); } void PSOutputDev::eoClip(GfxState *state) { doPath(state->getPath()); writePS("W*\n"); } void PSOutputDev::clipToStrokePath(GfxState *state) { doPath(state->getPath()); writePS("Ws\n"); } void PSOutputDev::doPath(const GfxPath *path) { double x0, y0, x1, y1, x2, y2, x3, y3, x4, y4; int n, m, i, j; n = path->getNumSubpaths(); if (n == 1 && path->getSubpath(0)->getNumPoints() == 5) { const GfxSubpath *subpath = path->getSubpath(0); x0 = subpath->getX(0); y0 = subpath->getY(0); x4 = subpath->getX(4); y4 = subpath->getY(4); if (x4 == x0 && y4 == y0) { x1 = subpath->getX(1); y1 = subpath->getY(1); x2 = subpath->getX(2); y2 = subpath->getY(2); x3 = subpath->getX(3); y3 = subpath->getY(3); if (x0 == x1 && x2 == x3 && y0 == y3 && y1 == y2) { writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re\n", x0 < x2 ? x0 : x2, y0 < y1 ? y0 : y1, fabs(x2 - x0), fabs(y1 - y0)); return; } else if (x0 == x3 && x1 == x2 && y0 == y1 && y2 == y3) { writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re\n", x0 < x1 ? x0 : x1, y0 < y2 ? y0 : y2, fabs(x1 - x0), fabs(y2 - y0)); return; } } } for (i = 0; i < n; ++i) { const GfxSubpath *subpath = path->getSubpath(i); m = subpath->getNumPoints(); writePSFmt("{0:.6g} {1:.6g} m\n", subpath->getX(0), subpath->getY(0)); j = 1; while (j < m) { if (subpath->getCurve(j)) { writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g} c\n", subpath->getX(j), subpath->getY(j), subpath->getX(j + 1), subpath->getY(j + 1), subpath->getX(j + 2), subpath->getY(j + 2)); j += 3; } else { writePSFmt("{0:.6g} {1:.6g} l\n", subpath->getX(j), subpath->getY(j)); ++j; } } if (subpath->isClosed()) { writePS("h\n"); } } } void PSOutputDev::drawString(GfxState *state, const GooString *s) { std::shared_ptr font; int wMode; int *codeToGID; GooString *s2; double dx, dy, originX, originY; const char *p; const UnicodeMap *uMap; CharCode code; const Unicode *u; char buf[8]; double *dxdy; int dxdySize, len, nChars, uLen, n, m, i, j; int maxGlyphInt; CharCode maxGlyph; // for pdftohtml, output PS without text if (displayText == false) { return; } // check for invisible text -- this is used by Acrobat Capture if (state->getRender() == 3) { return; } // ignore empty strings if (s->getLength() == 0) { return; } // get the font if (!(font = state->getFont())) { return; } maxGlyphInt = (font->getName() ? perFontMaxValidGlyph[*font->getName()] : 0); if (maxGlyphInt < 0) { maxGlyphInt = 0; } maxGlyph = (CharCode)maxGlyphInt; wMode = font->getWMode(); // 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) { if (!font16Enc[i].enc) { // font substitution failed, so don't output any text return; } uMap = globalParams->getUnicodeMap(font16Enc[i].enc->toStr()); break; } } // check for a code-to-GID map } else { for (i = 0; i < font8InfoLen; ++i) { if (*font->getID() == font8Info[i].fontID) { codeToGID = font8Info[i].codeToGID; break; } } } // compute the positioning (dx, dy) for each char in the string nChars = 0; p = s->c_str(); len = s->getLength(); s2 = new GooString(); dxdySize = font->isCIDFont() ? 8 : s->getLength(); dxdy = (double *)gmallocn(2 * dxdySize, sizeof(double)); while (len > 0) { n = font->getNextChar(p, len, &code, &u, &uLen, &dx, &dy, &originX, &originY); dx *= state->getFontSize(); dy *= state->getFontSize(); if (wMode) { dy += state->getCharSpace(); if (n == 1 && *p == ' ') { dy += state->getWordSpace(); } } else { dx += state->getCharSpace(); if (n == 1 && *p == ' ') { dx += state->getWordSpace(); } } dx *= state->getHorizScaling(); if (font->isCIDFont()) { if (uMap) { if (nChars + uLen > dxdySize) { do { dxdySize *= 2; } while (nChars + uLen > dxdySize); dxdy = (double *)greallocn(dxdy, 2 * dxdySize, sizeof(double)); } for (i = 0; i < uLen; ++i) { m = uMap->mapUnicode(u[i], buf, (int)sizeof(buf)); for (j = 0; j < m; ++j) { s2->append(buf[j]); } //~ this really needs to get the number of chars in the target //~ encoding - which may be more than the number of Unicode //~ chars dxdy[2 * nChars] = dx; dxdy[2 * nChars + 1] = dy; ++nChars; } } else if (maxGlyph > 0 && code > maxGlyph) { // Ignore this code. // Using it will exceed the number of glyphs in the font and generate // /rangecheck in --xyshow-- if (nChars > 0) { dxdy[2 * (nChars - 1)] += dx; dxdy[2 * (nChars - 1) + 1] += dy; } } else { if (nChars + 1 > dxdySize) { dxdySize *= 2; dxdy = (double *)greallocn(dxdy, 2 * dxdySize, sizeof(double)); } s2->append((char)((code >> 8) & 0xff)); s2->append((char)(code & 0xff)); dxdy[2 * nChars] = dx; dxdy[2 * nChars + 1] = dy; ++nChars; } } else { if (!codeToGID || codeToGID[code] >= 0) { s2->append((char)code); dxdy[2 * nChars] = dx; dxdy[2 * nChars + 1] = dy; ++nChars; } } p += n; len -= n; } if (nChars > 0) { writePSString(s2->toStr()); writePS("\n["); for (i = 0; i < 2 * nChars; ++i) { if (i > 0) { writePS("\n"); } writePSFmt("{0:.6g}", dxdy[i]); } writePS("] Tj\n"); } gfree(dxdy); delete s2; if (state->getRender() & 4) { haveTextClip = true; } } void PSOutputDev::beginTextObject(GfxState *state) { } void PSOutputDev::endTextObject(GfxState *state) { if (haveTextClip) { writePS("Tclip\n"); haveTextClip = false; } } void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg) { int len; len = height * ((width + 7) / 8); switch (level) { case psLevel1: case psLevel1Sep: doImageL1(ref, nullptr, invert, inlineImg, str, width, height, len, nullptr, nullptr, 0, 0, false); break; case psLevel2: case psLevel2Sep: doImageL2(state, ref, nullptr, invert, inlineImg, str, width, height, len, nullptr, nullptr, 0, 0, false); break; case psLevel3: case psLevel3Sep: doImageL3(state, ref, nullptr, invert, inlineImg, str, width, height, len, nullptr, nullptr, 0, 0, false); break; } } void PSOutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool inlineImg, double *baseMatrix) { if (level != psLevel1 && level != psLevel1Sep) { maskToClippingPath(str, width, height, invert); } } void PSOutputDev::unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix) { if (level != psLevel1 && level != psLevel1Sep) { writePS("pdfImClipEnd\n"); } } void PSOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) { int len; len = height * ((width * colorMap->getNumPixelComps() * colorMap->getBits() + 7) / 8); switch (level) { case psLevel1: doImageL1(ref, colorMap, false, inlineImg, str, width, height, len, maskColors, nullptr, 0, 0, false); break; case psLevel1Sep: //~ handle indexed, separation, ... color spaces doImageL1Sep(ref, colorMap, false, inlineImg, str, width, height, len, maskColors, nullptr, 0, 0, false); break; case psLevel2: case psLevel2Sep: doImageL2(state, ref, colorMap, false, inlineImg, str, width, height, len, maskColors, nullptr, 0, 0, false); break; case psLevel3: case psLevel3Sep: doImageL3(state, ref, colorMap, false, inlineImg, str, width, height, len, maskColors, nullptr, 0, 0, false); break; } t3Cacheable = false; } void PSOutputDev::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) { int len; len = height * ((width * colorMap->getNumPixelComps() * colorMap->getBits() + 7) / 8); switch (level) { case psLevel1: doImageL1(ref, colorMap, false, false, str, width, height, len, nullptr, maskStr, maskWidth, maskHeight, maskInvert); break; case psLevel1Sep: //~ handle indexed, separation, ... color spaces doImageL1Sep(ref, colorMap, false, false, str, width, height, len, nullptr, maskStr, maskWidth, maskHeight, maskInvert); break; case psLevel2: case psLevel2Sep: doImageL2(state, ref, colorMap, false, false, str, width, height, len, nullptr, maskStr, maskWidth, maskHeight, maskInvert); break; case psLevel3: case psLevel3Sep: doImageL3(state, ref, colorMap, false, false, str, width, height, len, nullptr, maskStr, maskWidth, maskHeight, maskInvert); break; } t3Cacheable = false; } 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; char hexBuf[32 * 2 + 2]; // 32 values X 2 chars/value + line ending + null unsigned char digit, grayValue; // explicit masking if (maskStr && !(maskColors && colorMap)) { maskToClippingPath(maskStr, maskWidth, maskHeight, maskInvert); } if ((inType3Char || preloadImagesForms) && !colorMap) { if (inlineImg) { // create an array str = new FixedLengthEncoder(str, len); str = new ASCIIHexEncoder(str); str->reset(); col = 0; writePS("[<"); do { do { c = str->getChar(); } while (c == '\n' || c == '\r'); if (c == '>' || c == EOF) { break; } writePSChar(c); ++col; // each line is: "<...data...>" // so max data length = 255 - 4 = 251 // but make it 240 just to be safe // chunks are 2 bytes each, so we need to stop on an even col number if (col == 240) { writePS(">\n<"); col = 0; } } while (c != '>' && c != EOF); writePS(">]\n"); writePS("0\n"); str->close(); delete str; } else { // make sure the image is setup, it sometimes is not like on bug #17645 setupImage(ref->getRef(), str, false); // set up to use the array already created by setupImages() writePSFmt("ImData_{0:d}_{1:d} 0 0\n", ref->getRefNum(), ref->getRefGen()); } } // image/imagemask command if ((inType3Char || preloadImagesForms) && !colorMap) { writePSFmt("{0:d} {1:d} {2:s} [{3:d} 0 0 {4:d} 0 {5:d}] pdfImM1a\n", width, height, invert ? "true" : "false", width, -height, height); } else if (colorMap) { writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1{5:s}\n", width, height, width, -height, height, useBinary ? "Bin" : ""); } else { writePSFmt("{0:d} {1:d} {2:s} [{3:d} 0 0 {4:d} 0 {5:d}] pdfImM1{6:s}\n", width, height, invert ? "true" : "false", width, -height, height, useBinary ? "Bin" : ""); } // image data if (!((inType3Char || preloadImagesForms) && !colorMap)) { if (colorMap) { // set up to process the data stream imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgStr->reset(); // process the data stream i = 0; for (y = 0; y < height; ++y) { // write the line for (x = 0; x < width; ++x) { imgStr->getPixel(pixBuf); colorMap->getGray(pixBuf, &gray); grayValue = colToByte(gray); if (useBinary) { hexBuf[i++] = grayValue; } else { digit = grayValue / 16; hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0'); digit = grayValue % 16; hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0'); } if (i >= 64) { if (!useBinary) { hexBuf[i++] = '\n'; } writePSBuf(hexBuf, i); i = 0; } } } if (i != 0) { if (!useBinary) { hexBuf[i++] = '\n'; } writePSBuf(hexBuf, i); } str->close(); delete imgStr; // imagemask } else { str->reset(); i = 0; for (y = 0; y < height; ++y) { for (x = 0; x < width; x += 8) { grayValue = str->getChar(); if (useBinary) { hexBuf[i++] = grayValue; } else { digit = grayValue / 16; hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0'); digit = grayValue % 16; hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0'); } if (i >= 64) { if (!useBinary) { hexBuf[i++] = '\n'; } writePSBuf(hexBuf, i); i = 0; } } } if (i != 0) { if (!useBinary) { hexBuf[i++] = '\n'; } writePSBuf(hexBuf, i); } str->close(); } } if (maskStr && !(maskColors && colorMap)) { writePS("pdfImClipEnd\n"); } } 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; bool checkProcessColor; char hexBuf[32 * 2 + 2]; // 32 values X 2 chars/value + line ending + null unsigned char digit; bool isGray; // explicit masking if (maskStr && !(maskColors && colorMap)) { 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(); isGray = true; for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { imgCheckStr->getPixel(pixBuf); colorMap->getCMYK(pixBuf, &cmyk); if (colToByte(cmyk.c) != colToByte(cmyk.m) || colToByte(cmyk.c) != colToByte(cmyk.y)) { isGray = false; y = height; // end outer loop break; } } } imgCheckStr->close(); delete imgCheckStr; } else { isGray = false; } // set up to process the data stream imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgStr->reset(); // 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" : ""); // process the data stream checkProcessColor = true; i = 0; if (isGray) { int g; for (y = 0; y < height; ++y) { // read the line if (checkProcessColor) { checkProcessColor = ((psProcessBlack & processColors) == 0); } for (x = 0; x < width; ++x) { imgStr->getPixel(pixBuf); colorMap->getCMYK(pixBuf, &cmyk); g = colToByte(cmyk.c) + colToByte(cmyk.k); if (checkProcessColor && g > 0) { processColors |= psProcessBlack; } g = 255 - g; if (g < 0) { g = 0; } if (useBinary) { hexBuf[i++] = g; } else { digit = g / 16; hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0'); digit = g % 16; hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0'); } if (i >= 64) { if (!useBinary) { hexBuf[i++] = '\n'; } writePSBuf(hexBuf, i); i = 0; } } } } else { for (y = 0; y < height; ++y) { // read the line if (checkProcessColor) { checkProcessColor = (((psProcessCyan | psProcessMagenta | psProcessYellow | psProcessBlack) & ~processColors) != 0); } if (checkProcessColor) { for (x = 0; x < width; ++x) { imgStr->getPixel(pixBuf); colorMap->getCMYK(pixBuf, &cmyk); lineBuf[4 * x + 0] = colToByte(cmyk.c); lineBuf[4 * x + 1] = colToByte(cmyk.m); lineBuf[4 * x + 2] = colToByte(cmyk.y); lineBuf[4 * x + 3] = colToByte(cmyk.k); addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k)); } } else { for (x = 0; x < width; ++x) { imgStr->getPixel(pixBuf); colorMap->getCMYK(pixBuf, &cmyk); lineBuf[4 * x + 0] = colToByte(cmyk.c); lineBuf[4 * x + 1] = colToByte(cmyk.m); lineBuf[4 * x + 2] = colToByte(cmyk.y); lineBuf[4 * x + 3] = colToByte(cmyk.k); } } // write one line of each color component if (useBinary) { for (comp = 0; comp < 4; ++comp) { for (x = 0; x < width; ++x) { hexBuf[i++] = lineBuf[4 * x + comp]; if (i >= 64) { writePSBuf(hexBuf, i); i = 0; } } } } else { for (comp = 0; comp < 4; ++comp) { for (x = 0; x < width; ++x) { digit = lineBuf[4 * x + comp] / 16; hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0'); digit = lineBuf[4 * x + comp] % 16; hexBuf[i++] = digit + ((digit >= 10) ? 'a' - 10 : '0'); if (i >= 64) { hexBuf[i++] = '\n'; writePSBuf(hexBuf, i); i = 0; } } } } } } if (i != 0) { if (!useBinary) { hexBuf[i++] = '\n'; } writePSBuf(hexBuf, i); } str->close(); delete imgStr; gfree(lineBuf); if (maskStr && !(maskColors && colorMap)) { writePS("pdfImClipEnd\n"); } } 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(); rects0Len = rects1Len = rectsOutLen = 0; rectsSize = rectsOutSize = 64; rects0 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect)); rects1 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect)); rectsOut = (PSOutImgClipRect *)gmallocn(rectsOutSize, sizeof(PSOutImgClipRect)); maskXor = maskInvert ? 1 : 0; for (y = 0; y < maskHeight; ++y) { if (!(line = imgStr->getLine())) { break; } i = 0; rects1Len = 0; for (x0 = 0; x0 < maskWidth && (line[x0] ^ maskXor); ++x0) { ; } for (x1 = x0; x1 < maskWidth && !(line[x1] ^ maskXor); ++x1) { ; } while (x0 < maskWidth || i < rects0Len) { emitRect = addRect = extendRect = false; if (x0 >= maskWidth) { emitRect = true; } else if (i >= rects0Len) { addRect = true; } else if (rects0[i].x0 < x0) { emitRect = true; } else if (x0 < rects0[i].x0) { addRect = true; } else if (rects0[i].x1 == x1) { extendRect = true; } else { emitRect = addRect = true; } if (emitRect) { if (rectsOutLen == rectsOutSize) { rectsOutSize *= 2; rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize, sizeof(PSOutImgClipRect)); } rectsOut[rectsOutLen].x0 = rects0[i].x0; rectsOut[rectsOutLen].x1 = rects0[i].x1; rectsOut[rectsOutLen].y0 = maskHeight - y; rectsOut[rectsOutLen].y1 = maskHeight - rects0[i].y0; ++rectsOutLen; ++i; } if (addRect || extendRect) { if (rects1Len == rectsSize) { rectsSize *= 2; rects0 = (PSOutImgClipRect *)greallocn(rects0, rectsSize, sizeof(PSOutImgClipRect)); rects1 = (PSOutImgClipRect *)greallocn(rects1, rectsSize, sizeof(PSOutImgClipRect)); } rects1[rects1Len].x0 = x0; rects1[rects1Len].x1 = x1; if (addRect) { rects1[rects1Len].y0 = y; } if (extendRect) { rects1[rects1Len].y0 = rects0[i].y0; ++i; } ++rects1Len; for (x0 = x1; x0 < maskWidth && (line[x0] ^ maskXor); ++x0) { ; } for (x1 = x0; x1 < maskWidth && !(line[x1] ^ maskXor); ++x1) { ; } } } rectsTmp = rects0; rects0 = rects1; rects1 = rectsTmp; i = rects0Len; rects0Len = rects1Len; rects1Len = i; } for (i = 0; i < rects0Len; ++i) { if (rectsOutLen == rectsOutSize) { rectsOutSize *= 2; rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize, sizeof(PSOutImgClipRect)); } rectsOut[rectsOutLen].x0 = rects0[i].x0; rectsOut[rectsOutLen].x1 = rects0[i].x1; rectsOut[rectsOutLen].y0 = maskHeight - y; rectsOut[rectsOutLen].y1 = maskHeight - rects0[i].y0; ++rectsOutLen; } if (rectsOutLen < 65536 / 4) { writePSFmt("{0:d} array 0\n", rectsOutLen * 4); for (i = 0; i < rectsOutLen; ++i) { writePSFmt("[{0:d} {1:d} {2:d} {3:d}] pr\n", rectsOut[i].x0, rectsOut[i].y0, rectsOut[i].x1 - rectsOut[i].x0, rectsOut[i].y1 - rectsOut[i].y0); } writePSFmt("pop {0:d} {1:d} pdfImClip\n", maskWidth, maskHeight); } else { // would be over the limit of array size. // make each rectangle path and clip. writePS("gsave newpath\n"); for (i = 0; i < rectsOutLen; ++i) { writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re\n", ((double)rectsOut[i].x0) / maskWidth, ((double)rectsOut[i].y0) / maskHeight, ((double)(rectsOut[i].x1 - rectsOut[i].x0)) / maskWidth, ((double)(rectsOut[i].y1 - rectsOut[i].y0)) / maskHeight); } writePS("clip\n"); } gfree(rectsOut); gfree(rects0); gfree(rects1); delete imgStr; maskStr->close(); } void PSOutputDev::doImageL2(GfxState *state, 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) { 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; GfxColor color; GfxCMYK cmyk; int c; int col, i, j, x0, x1, y; char dataBuf[4096]; rectsOutLen = 0; // color key masking if (maskColors && colorMap && !inlineImg) { // 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(); 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())) { break; } i = 0; rects1Len = 0; for (x0 = 0; x0 < width; ++x0) { for (j = 0; j < numComps; ++j) { if (line[x0 * numComps + j] < maskColors[2 * j] || line[x0 * numComps + j] > maskColors[2 * j + 1]) { break; } } if (j < numComps) { break; } } for (x1 = x0; x1 < width; ++x1) { for (j = 0; j < numComps; ++j) { if (line[x1 * numComps + j] < maskColors[2 * j] || line[x1 * numComps + j] > maskColors[2 * j + 1]) { break; } } if (j == numComps) { break; } } while (x0 < width || i < rects0Len) { emitRect = addRect = extendRect = false; if (x0 >= width) { emitRect = true; } else if (i >= rects0Len) { addRect = true; } else if (rects0[i].x0 < x0) { emitRect = true; } else if (x0 < rects0[i].x0) { addRect = true; } else if (rects0[i].x1 == x1) { extendRect = true; } else { emitRect = addRect = true; } if (emitRect) { if (rectsOutLen == rectsOutSize) { rectsOutSize *= 2; rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize, sizeof(PSOutImgClipRect)); } rectsOut[rectsOutLen].x0 = rects0[i].x0; rectsOut[rectsOutLen].x1 = rects0[i].x1; rectsOut[rectsOutLen].y0 = height - y; rectsOut[rectsOutLen].y1 = height - rects0[i].y0; ++rectsOutLen; ++i; } if (addRect || extendRect) { if (rects1Len == rectsSize) { rectsSize *= 2; rects0 = (PSOutImgClipRect *)greallocn(rects0, rectsSize, sizeof(PSOutImgClipRect)); rects1 = (PSOutImgClipRect *)greallocn(rects1, rectsSize, sizeof(PSOutImgClipRect)); } rects1[rects1Len].x0 = x0; rects1[rects1Len].x1 = x1; if (addRect) { rects1[rects1Len].y0 = y; } if (extendRect) { rects1[rects1Len].y0 = rects0[i].y0; ++i; } ++rects1Len; for (x0 = x1; x0 < width; ++x0) { for (j = 0; j < numComps; ++j) { if (line[x0 * numComps + j] < maskColors[2 * j] || line[x0 * numComps + j] > maskColors[2 * j + 1]) { break; } } if (j < numComps) { break; } } for (x1 = x0; x1 < width; ++x1) { for (j = 0; j < numComps; ++j) { if (line[x1 * numComps + j] < maskColors[2 * j] || line[x1 * numComps + j] > maskColors[2 * j + 1]) { break; } } if (j == numComps) { break; } } } } rectsTmp = rects0; rects0 = rects1; rects1 = rectsTmp; i = rects0Len; rects0Len = rects1Len; rects1Len = i; } for (i = 0; i < rects0Len; ++i) { if (rectsOutLen == rectsOutSize) { rectsOutSize *= 2; rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize, sizeof(PSOutImgClipRect)); } rectsOut[rectsOutLen].x0 = rects0[i].x0; rectsOut[rectsOutLen].x1 = rects0[i].x1; rectsOut[rectsOutLen].y0 = height - y; rectsOut[rectsOutLen].y1 = height - rects0[i].y0; ++rectsOutLen; } if (rectsOutLen < 65536 / 4) { writePSFmt("{0:d} array 0\n", rectsOutLen * 4); for (i = 0; i < rectsOutLen; ++i) { writePSFmt("[{0:d} {1:d} {2:d} {3:d}] pr\n", rectsOut[i].x0, rectsOut[i].y0, rectsOut[i].x1 - rectsOut[i].x0, rectsOut[i].y1 - rectsOut[i].y0); } writePSFmt("pop {0:d} {1:d} pdfImClip\n", width, height); } else { // would be over the limit of array size. // make each rectangle path and clip. writePS("gsave newpath\n"); for (i = 0; i < rectsOutLen; ++i) { writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re\n", ((double)rectsOut[i].x0) / width, ((double)rectsOut[i].y0) / height, ((double)(rectsOut[i].x1 - rectsOut[i].x0)) / width, ((double)(rectsOut[i].y1 - rectsOut[i].y0)) / height); } writePS("clip\n"); } gfree(rectsOut); gfree(rects0); gfree(rects1); delete imgStr; str->close(); // explicit masking } else if (maskStr) { maskToClippingPath(maskStr, maskWidth, maskHeight, maskInvert); } // color space if (colorMap) { // Do not update the process color list for custom colors bool isCustomColor = (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) && colorMap->getColorSpace()->getMode() == csDeviceN; dumpColorSpaceL2(state, colorMap->getColorSpace(), false, !isCustomColor, false); writePS(" setcolorspace\n"); } // set up the image data if (mode == psModeForm || inType3Char || preloadImagesForms) { if (inlineImg) { // create an array str2 = new FixedLengthEncoder(str, len); if (getEnableLZW()) { str2 = new LZWEncoder(str2); } else { str2 = new RunLengthEncoder(str2); } if (useASCIIHex) { str2 = new ASCIIHexEncoder(str2); } else { str2 = new ASCII85Encoder(str2); } str2->reset(); col = 0; writePS((char *)(useASCIIHex ? "[<" : "[<~")); do { do { c = str2->getChar(); } while (c == '\n' || c == '\r'); if (c == (useASCIIHex ? '>' : '~') || c == EOF) { break; } if (c == 'z') { writePSChar(c); ++col; } else { writePSChar(c); ++col; for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) { do { c = str2->getChar(); } while (c == '\n' || c == '\r'); if (c == (useASCIIHex ? '>' : '~') || c == EOF) { break; } writePSChar(c); ++col; } } // each line is: "<~...data...~>" // so max data length = 255 - 6 = 249 // chunks are 1 or 5 bytes each, so we have to stop at 245 // but make it 240 just to be safe if (col > 240) { writePS((char *)(useASCIIHex ? ">\n<" : "~>\n<~")); col = 0; } } while (c != (useASCIIHex ? '>' : '~') && c != EOF); writePS((char *)(useASCIIHex ? ">\n" : "~>\n")); // add an extra entry because the LZWDecode/RunLengthDecode filter may // read past the end writePS("<>]\n"); writePS("0\n"); str2->close(); delete str2; } else { // make sure the image is setup, it sometimes is not like on bug #17645 setupImage(ref->getRef(), str, false); // set up to use the array already created by setupImages() writePSFmt("ImData_{0:d}_{1:d} 0 0\n", ref->getRefNum(), ref->getRefGen()); } } // image dictionary writePS("<<\n /ImageType 1\n"); // width, height, matrix, bits per component writePSFmt(" /Width {0:d}\n", width); writePSFmt(" /Height {0:d}\n", height); writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", width, -height, height); if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) { writePS(" /BitsPerComponent 8\n"); } else { writePSFmt(" /BitsPerComponent {0:d}\n", colorMap ? colorMap->getBits() : 1); } // decode if (colorMap) { writePS(" /Decode ["); if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap->getColorSpace()->getMode() == csSeparation) { // this matches up with the code in the pdfImSep operator n = (1 << colorMap->getBits()) - 1; writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(0) * n, colorMap->getDecodeHigh(0) * n); } else if (colorMap->getColorSpace()->getMode() == csDeviceN) { numComps = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->getAlt()->getNComps(); for (i = 0; i < numComps; ++i) { if (i > 0) { writePS(" "); } writePS("0 1"); } } else { numComps = colorMap->getNumPixelComps(); for (i = 0; i < numComps; ++i) { if (i > 0) { writePS(" "); } writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(i), colorMap->getDecodeHigh(i)); } } writePS("]\n"); } else { writePSFmt(" /Decode [{0:d} {1:d}]\n", invert ? 1 : 0, invert ? 0 : 1); } // data source if (mode == psModeForm || inType3Char || preloadImagesForms) { if (inlineImg) { writePS(" /DataSource { pdfImStr }\n"); } else { writePS(" /DataSource { dup 65535 ge { pop 1 add 0 } if 2 index 2" " index get 1 index get exch 1 add exch }\n"); } } else { writePS(" /DataSource currentfile\n"); } // filters if ((mode == psModeForm || inType3Char || preloadImagesForms) && uncompressPreloadedImages) { s = nullptr; useLZW = useRLE = false; useCompressed = false; useASCII = false; } else { s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3, " "); if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) || inlineImg || !s) { if (getEnableLZW()) { useLZW = true; useRLE = false; } else { useRLE = true; useLZW = false; } useASCII = !(mode == psModeForm || inType3Char || preloadImagesForms); useCompressed = false; } else { useLZW = useRLE = false; useASCII = str->isBinary() && !(mode == psModeForm || inType3Char || preloadImagesForms); useCompressed = true; } } if (useASCII) { writePSFmt(" /ASCII{0:s}Decode filter\n", useASCIIHex ? "Hex" : "85"); } if (useLZW) { writePS(" /LZWDecode filter\n"); } else if (useRLE) { writePS(" /RunLengthDecode filter\n"); } if (useCompressed) { writePS(s->c_str()); } if (s) { delete s; } if (mode == psModeForm || inType3Char || preloadImagesForms) { // end of image dictionary writePSFmt(">>\n{0:s}\n", colorMap ? "image" : "imagemask"); // get rid of the array and index if (!inlineImg) { writePS("pop "); } writePS("pop pop\n"); } else { // cut off inline image streams at appropriate length if (inlineImg) { str = new FixedLengthEncoder(str, len); } else if (useCompressed) { str = str->getUndecodedStream(); } // recode DeviceN data if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) { str = new DeviceNRecoder(str, width, height, colorMap); } // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters if (useLZW) { str = new LZWEncoder(str); } else if (useRLE) { str = new RunLengthEncoder(str); } if (useASCII) { if (useASCIIHex) { str = new ASCIIHexEncoder(str); } else { str = new ASCII85Encoder(str); } } // end of image dictionary writePS(">>\n"); #ifdef OPI_SUPPORT if (opi13Nest) { if (inlineImg) { // this can't happen -- OPI dictionaries are in XObjects error(errSyntaxError, -1, "OPI in inline image"); n = 0; } 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; } str->close(); } // +6/7 for "pdfIm\n" / "pdfImM\n" // +8 for newline + trailer n += colorMap ? 14 : 15; writePSFmt("%%BeginData: {0:d} Hex Bytes\n", n); } #endif if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap && colorMap->getColorSpace()->getMode() == csSeparation && colorMap->getBits() == 8) { color.c[0] = gfxColorComp1; sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace(); sepCS->getCMYK(&color, &cmyk); writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} ({4:t}) pdfImSep\n", colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k), sepCS->getName()); } else { writePSFmt("{0:s}\n", colorMap ? "pdfIm" : "pdfImM"); } // copy the stream data 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); } str->close(); // add newline and trailer to the end writePSChar('\n'); writePS("%-EOD-\n"); #ifdef OPI_SUPPORT if (opi13Nest) { writePS("%%EndData\n"); } #endif // delete encoders if (useLZW || useRLE || useASCII || inlineImg) { delete str; } } if ((maskColors && colorMap && !inlineImg) || maskStr) { if (rectsOutLen < 65536 / 4) { writePS("pdfImClipEnd\n"); } else { writePS("grestore\n"); } } } //~ this doesn't currently support OPI void PSOutputDev::doImageL3(GfxState *state, 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) { Stream *str2; GooString *s; int n, numComps; bool useFlate, useLZW, useRLE, useASCII, useCompressed; bool maskUseFlate, maskUseLZW, maskUseRLE, maskUseASCII, maskUseCompressed; GooString *maskFilters; GfxSeparationColorSpace *sepCS; GfxColor color; GfxCMYK cmyk; int c; int col, i; useFlate = useLZW = useRLE = useASCII = useCompressed = false; maskUseFlate = maskUseLZW = maskUseRLE = maskUseASCII = maskUseCompressed = false; maskFilters = nullptr; // make gcc happy // explicit masking if (maskStr) { // mask data source if ((mode == psModeForm || inType3Char || preloadImagesForms) && uncompressPreloadedImages) { s = nullptr; } else { s = maskStr->getPSFilter(3, " "); if (!s) { if (getEnableFlate()) { maskUseFlate = true; } else if (getEnableLZW()) { maskUseLZW = true; } else { maskUseRLE = true; } maskUseASCII = !(mode == psModeForm || inType3Char || preloadImagesForms); } else { maskUseASCII = maskStr->isBinary() && !(mode == psModeForm || inType3Char || preloadImagesForms); maskUseCompressed = true; } } maskFilters = new GooString(); if (maskUseASCII) { maskFilters->appendf(" /ASCII{0:s}Decode filter\n", useASCIIHex ? "Hex" : "85"); } if (maskUseFlate) { maskFilters->append(" /FlateDecode filter\n"); } else if (maskUseLZW) { maskFilters->append(" /LZWDecode filter\n"); } else if (maskUseRLE) { maskFilters->append(" /RunLengthDecode filter\n"); } if (maskUseCompressed) { maskFilters->append(s); } if (s) { delete s; } if (mode == psModeForm || inType3Char || preloadImagesForms) { writePSFmt("MaskData_{0:d}_{1:d} pdfMaskInit\n", ref->getRefNum(), ref->getRefGen()); } else { writePS("currentfile\n"); writePS(maskFilters->c_str()); writePS("pdfMask\n"); // add FlateEncode/LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters if (maskUseCompressed) { maskStr = maskStr->getUndecodedStream(); } if (maskUseFlate) { maskStr = new FlateEncoder(maskStr); } else if (maskUseLZW) { maskStr = new LZWEncoder(maskStr); } else if (maskUseRLE) { maskStr = new RunLengthEncoder(maskStr); } if (maskUseASCII) { if (useASCIIHex) { maskStr = new ASCIIHexEncoder(maskStr); } else { maskStr = new ASCII85Encoder(maskStr); } } // copy the stream data maskStr->reset(); while ((c = maskStr->getChar()) != EOF) { writePSChar(c); } maskStr->close(); writePSChar('\n'); writePS("%-EOD-\n"); // delete encoders if (maskUseFlate || maskUseLZW || maskUseRLE || maskUseASCII) { delete maskStr; } } } // color space if (colorMap) { // Do not update the process color list for custom colors bool isCustomColor = (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) && colorMap->getColorSpace()->getMode() == csDeviceN; dumpColorSpaceL2(state, colorMap->getColorSpace(), false, !isCustomColor, false); writePS(" setcolorspace\n"); } // set up the image data if (mode == psModeForm || inType3Char || preloadImagesForms) { if (inlineImg) { // create an array str2 = new FixedLengthEncoder(str, len); if (getEnableFlate()) { str2 = new FlateEncoder(str2); } else if (getEnableLZW()) { str2 = new LZWEncoder(str2); } else { str2 = new RunLengthEncoder(str2); } if (useASCIIHex) { str2 = new ASCIIHexEncoder(str2); } else { str2 = new ASCII85Encoder(str2); } str2->reset(); col = 0; writePS((char *)(useASCIIHex ? "[<" : "[<~")); do { do { c = str2->getChar(); } while (c == '\n' || c == '\r'); if (c == (useASCIIHex ? '>' : '~') || c == EOF) { break; } if (c == 'z') { writePSChar(c); ++col; } else { writePSChar(c); ++col; for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) { do { c = str2->getChar(); } while (c == '\n' || c == '\r'); if (c == (useASCIIHex ? '>' : '~') || c == EOF) { break; } writePSChar(c); ++col; } } // each line is: "<~...data...~>" // so max data length = 255 - 6 = 249 // chunks are 1 or 5 bytes each, so we have to stop at 245 // but make it 240 just to be safe if (col > 240) { writePS((char *)(useASCIIHex ? ">\n<" : "~>\n<~")); col = 0; } } while (c != (useASCIIHex ? '>' : '~') && c != EOF); writePS((char *)(useASCIIHex ? ">\n" : "~>\n")); // add an extra entry because the FlateEncode/LZWDecode/RunLengthDecode filter may // read past the end writePS("<>]\n"); writePS("0\n"); str2->close(); delete str2; } else { // make sure the image is setup, it sometimes is not like on bug #17645 setupImage(ref->getRef(), str, false); // set up to use the array already created by setupImages() writePSFmt("ImData_{0:d}_{1:d} 0 0\n", ref->getRefNum(), ref->getRefGen()); } } // explicit masking if (maskStr) { writePS("<<\n /ImageType 3\n"); writePS(" /InterleaveType 3\n"); writePS(" /DataDict\n"); } // image (data) dictionary writePSFmt("<<\n /ImageType {0:d}\n", (maskColors && colorMap) ? 4 : 1); // color key masking if (maskColors && colorMap) { writePS(" /MaskColor [\n"); numComps = colorMap->getNumPixelComps(); for (i = 0; i < 2 * numComps; i += 2) { writePSFmt(" {0:d} {1:d}\n", maskColors[i], maskColors[i + 1]); } writePS(" ]\n"); } // width, height, matrix, bits per component writePSFmt(" /Width {0:d}\n", width); writePSFmt(" /Height {0:d}\n", height); writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", width, -height, height); if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) { writePS(" /BitsPerComponent 8\n"); } else { writePSFmt(" /BitsPerComponent {0:d}\n", colorMap ? colorMap->getBits() : 1); } // decode if (colorMap) { writePS(" /Decode ["); if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap->getColorSpace()->getMode() == csSeparation) { // this matches up with the code in the pdfImSep operator n = (1 << colorMap->getBits()) - 1; writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(0) * n, colorMap->getDecodeHigh(0) * n); } else { numComps = colorMap->getNumPixelComps(); for (i = 0; i < numComps; ++i) { if (i > 0) { writePS(" "); } writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(i), colorMap->getDecodeHigh(i)); } } writePS("]\n"); } else { writePSFmt(" /Decode [{0:d} {1:d}]\n", invert ? 1 : 0, invert ? 0 : 1); } // data source if (mode == psModeForm || inType3Char || preloadImagesForms) { if (inlineImg) { writePS(" /DataSource { pdfImStr }\n"); } else { writePS(" /DataSource { dup 65535 ge { pop 1 add 0 } if 2 index 2" " index get 1 index get exch 1 add exch }\n"); } } else { writePS(" /DataSource currentfile\n"); } // filters useFlate = useLZW = useRLE = false; useCompressed = false; useASCII = false; if ((mode == psModeForm || inType3Char || preloadImagesForms) && uncompressPreloadedImages) { s = nullptr; } else { s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3, " "); if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) || inlineImg || !s) { if (getEnableFlate()) { useFlate = true; } else if (getEnableLZW()) { useLZW = true; } else { useRLE = true; } useASCII = !(mode == psModeForm || inType3Char || preloadImagesForms); } else { useASCII = str->isBinary() && !(mode == psModeForm || inType3Char || preloadImagesForms); useCompressed = true; } } if (useASCII) { writePSFmt(" /ASCII{0:s}Decode filter\n", useASCIIHex ? "Hex" : "85"); } if (useFlate) { writePS(" /FlateDecode filter\n"); } else if (useLZW) { writePS(" /LZWDecode filter\n"); } else if (useRLE) { writePS(" /RunLengthDecode filter\n"); } if (useCompressed) { writePS(s->c_str()); } if (s) { delete s; } // end of image (data) dictionary writePS(">>\n"); // explicit masking if (maskStr) { writePS(" /MaskDict\n"); writePS("<<\n"); writePS(" /ImageType 1\n"); writePSFmt(" /Width {0:d}\n", maskWidth); writePSFmt(" /Height {0:d}\n", maskHeight); writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", maskWidth, -maskHeight, maskHeight); writePS(" /BitsPerComponent 1\n"); writePSFmt(" /Decode [{0:d} {1:d}]\n", maskInvert ? 1 : 0, maskInvert ? 0 : 1); // mask data source if (mode == psModeForm || inType3Char || preloadImagesForms) { writePS(" /DataSource {pdfMaskSrc}\n"); writePS(maskFilters->c_str()); } else { writePS(" /DataSource maskStream\n"); } delete maskFilters; writePS(">>\n"); writePS(">>\n"); } if (mode == psModeForm || inType3Char || preloadImagesForms) { // image command writePSFmt("{0:s}\n", colorMap ? "image" : "imagemask"); } else { if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap && colorMap->getColorSpace()->getMode() == csSeparation && colorMap->getBits() == 8) { color.c[0] = gfxColorComp1; sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace(); sepCS->getCMYK(&color, &cmyk); writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} ({4:t}) pdfImSep\n", colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k), sepCS->getName()); } else { writePSFmt("{0:s}\n", colorMap ? "pdfIm" : "pdfImM"); } } // get rid of the array and index if (mode == psModeForm || inType3Char || preloadImagesForms) { if (!inlineImg) { writePS("pop "); } writePS("pop pop\n"); // image data } else { // cut off inline image streams at appropriate length if (inlineImg) { str = new FixedLengthEncoder(str, len); } else if (useCompressed) { str = str->getUndecodedStream(); } // add FlateEncode/LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters if (useFlate) { str = new FlateEncoder(str); } else if (useLZW) { str = new LZWEncoder(str); } else if (useRLE) { str = new RunLengthEncoder(str); } if (useASCII) { if (useASCIIHex) { str = new ASCIIHexEncoder(str); } else { str = new ASCII85Encoder(str); } } // copy the stream data str->reset(); while ((c = str->getChar()) != EOF) { writePSChar(c); } str->close(); // add newline and trailer to the end writePSChar('\n'); writePS("%-EOD-\n"); // delete encoders if (useFlate || useLZW || useRLE || useASCII || inlineImg) { delete str; } } // close the mask stream if (maskStr) { if (!(mode == psModeForm || inType3Char || preloadImagesForms)) { writePS("pdfMaskEnd\n"); } } } void PSOutputDev::dumpColorSpaceL2(GfxState *state, GfxColorSpace *colorSpace, bool genXform, bool updateColors, bool map01) { GfxCalGrayColorSpace *calGrayCS; GfxCalRGBColorSpace *calRGBCS; GfxLabColorSpace *labCS; GfxIndexedColorSpace *indexedCS; GfxSeparationColorSpace *separationCS; GfxDeviceNColorSpace *deviceNCS; GfxColorSpace *baseCS; unsigned char *lookup, *p; double x[gfxColorMaxComps], y[gfxColorMaxComps]; double low[gfxColorMaxComps], range[gfxColorMaxComps]; GfxColor color; GfxCMYK cmyk; int n, numComps, numAltComps; int byte; int i, j, k; switch (colorSpace->getMode()) { case csDeviceGray: writePS("/DeviceGray"); if (genXform) { writePS(" {}"); } if (updateColors) { processColors |= psProcessBlack; } break; case csCalGray: calGrayCS = (GfxCalGrayColorSpace *)colorSpace; writePS("[/CIEBasedA <<\n"); writePSFmt(" /DecodeA {{{0:.4g} exp}} bind\n", calGrayCS->getGamma()); writePSFmt(" /MatrixA [{0:.4g} {1:.4g} {2:.4g}]\n", calGrayCS->getWhiteX(), calGrayCS->getWhiteY(), calGrayCS->getWhiteZ()); writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n", calGrayCS->getWhiteX(), calGrayCS->getWhiteY(), calGrayCS->getWhiteZ()); writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n", calGrayCS->getBlackX(), calGrayCS->getBlackY(), calGrayCS->getBlackZ()); writePS(">>]"); if (genXform) { writePS(" {}"); } if (updateColors) { processColors |= psProcessBlack; } break; case csDeviceRGB: writePS("/DeviceRGB"); if (genXform) { writePS(" {}"); } if (updateColors) { processColors |= psProcessCMYK; } break; case csCalRGB: calRGBCS = (GfxCalRGBColorSpace *)colorSpace; writePS("[/CIEBasedABC <<\n"); writePSFmt(" /DecodeABC [{{{0:.4g} exp}} bind {{{1:.4g} exp}} bind {{{2:.4g} exp}} bind]\n", calRGBCS->getGammaR(), calRGBCS->getGammaG(), calRGBCS->getGammaB()); writePSFmt(" /MatrixABC [{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} {6:.4g} {7:.4g} {8:.4g}]\n", calRGBCS->getMatrix()[0], calRGBCS->getMatrix()[1], calRGBCS->getMatrix()[2], calRGBCS->getMatrix()[3], calRGBCS->getMatrix()[4], calRGBCS->getMatrix()[5], calRGBCS->getMatrix()[6], calRGBCS->getMatrix()[7], calRGBCS->getMatrix()[8]); writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n", calRGBCS->getWhiteX(), calRGBCS->getWhiteY(), calRGBCS->getWhiteZ()); writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n", calRGBCS->getBlackX(), calRGBCS->getBlackY(), calRGBCS->getBlackZ()); writePS(">>]"); if (genXform) { writePS(" {}"); } if (updateColors) { processColors |= psProcessCMYK; } break; case csDeviceCMYK: writePS("/DeviceCMYK"); if (genXform) { writePS(" {}"); } if (updateColors) { processColors |= psProcessCMYK; } break; case csLab: labCS = (GfxLabColorSpace *)colorSpace; writePS("[/CIEBasedABC <<\n"); if (map01) { writePS(" /RangeABC [0 1 0 1 0 1]\n"); writePSFmt(" /DecodeABC [{{100 mul 16 add 116 div}} bind {{{0:.4g} mul {1:.4g} add}} bind {{{2:.4g} mul {3:.4g} add}} bind]\n", (labCS->getAMax() - labCS->getAMin()) / 500.0, labCS->getAMin() / 500.0, (labCS->getBMax() - labCS->getBMin()) / 200.0, labCS->getBMin() / 200.0); } else { writePSFmt(" /RangeABC [0 100 {0:.4g} {1:.4g} {2:.4g} {3:.4g}]\n", labCS->getAMin(), labCS->getAMax(), labCS->getBMin(), labCS->getBMax()); writePS(" /DecodeABC [{16 add 116 div} bind {500 div} bind {200 div} bind]\n"); } writePS(" /MatrixABC [1 1 1 1 0 0 0 0 -1]\n"); writePS(" /DecodeLMN\n"); writePS(" [{dup 6 29 div ge {dup dup mul mul}\n"); writePSFmt(" {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind\n", labCS->getWhiteX()); writePS(" {dup 6 29 div ge {dup dup mul mul}\n"); writePSFmt(" {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind\n", labCS->getWhiteY()); writePS(" {dup 6 29 div ge {dup dup mul mul}\n"); writePSFmt(" {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind]\n", labCS->getWhiteZ()); writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n", labCS->getWhiteX(), labCS->getWhiteY(), labCS->getWhiteZ()); writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n", labCS->getBlackX(), labCS->getBlackY(), labCS->getBlackZ()); writePS(">>]"); if (genXform) { writePS(" {}"); } if (updateColors) { processColors |= psProcessCMYK; } break; case csICCBased: #ifdef USE_CMS { GfxICCBasedColorSpace *iccBasedCS; iccBasedCS = (GfxICCBasedColorSpace *)colorSpace; Ref ref = iccBasedCS->getRef(); const bool validref = ref != Ref::INVALID(); int intent = state->getCmsRenderingIntent(); std::unique_ptr 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()); if (it != iccEmitted.end()) { writePSFmt("{0:t}", name.get()); 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()); if (genXform) { writePS(" {}"); } } else { dumpColorSpaceL2(state, ((GfxICCBasedColorSpace *)colorSpace)->getAlt(), genXform, updateColors, false); } } } #else // there is no transform function to the alternate color space, so // we can use it directly dumpColorSpaceL2(state, ((GfxICCBasedColorSpace *)colorSpace)->getAlt(), genXform, updateColors, false); #endif break; case csIndexed: indexedCS = (GfxIndexedColorSpace *)colorSpace; baseCS = indexedCS->getBase(); writePS("[/Indexed "); dumpColorSpaceL2(state, baseCS, false, false, true); n = indexedCS->getIndexHigh(); numComps = baseCS->getNComps(); lookup = indexedCS->getLookup(); writePSFmt(" {0:d} <\n", n); if (baseCS->getMode() == csDeviceN && level != psLevel3 && level != psLevel3Sep) { const Function *func = ((GfxDeviceNColorSpace *)baseCS)->getTintTransformFunc(); baseCS->getDefaultRanges(low, range, indexedCS->getIndexHigh()); if (((GfxDeviceNColorSpace *)baseCS)->getAlt()->getMode() == csLab) { labCS = (GfxLabColorSpace *)((GfxDeviceNColorSpace *)baseCS)->getAlt(); } else { labCS = nullptr; } numAltComps = ((GfxDeviceNColorSpace *)baseCS)->getAlt()->getNComps(); p = lookup; for (i = 0; i <= n; i += 8) { writePS(" "); for (j = i; j < i + 8 && j <= n; ++j) { for (k = 0; k < numComps; ++k) { x[k] = low[k] + (*p++ / 255.0) * range[k]; } func->transform(x, y); if (labCS) { y[0] /= 100.0; y[1] = (y[1] - labCS->getAMin()) / (labCS->getAMax() - labCS->getAMin()); y[2] = (y[2] - labCS->getBMin()) / (labCS->getBMax() - labCS->getBMin()); } for (k = 0; k < numAltComps; ++k) { byte = (int)(y[k] * 255 + 0.5); if (byte < 0) { byte = 0; } else if (byte > 255) { byte = 255; } writePSFmt("{0:02x}", byte); } if (updateColors) { color.c[0] = dblToCol(j); indexedCS->getCMYK(&color, &cmyk); addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k)); } } writePS("\n"); } } else { for (i = 0; i <= n; i += 8) { writePS(" "); for (j = i; j < i + 8 && j <= n; ++j) { for (k = 0; k < numComps; ++k) { writePSFmt("{0:02x}", lookup[j * numComps + k]); } if (updateColors) { color.c[0] = dblToCol(j); indexedCS->getCMYK(&color, &cmyk); addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k)); } } writePS("\n"); } } writePS(">]"); if (genXform) { writePS(" {}"); } break; case csSeparation: separationCS = (GfxSeparationColorSpace *)colorSpace; writePS("[/Separation "); writePSString(separationCS->getName()->toStr()); writePS(" "); dumpColorSpaceL2(state, separationCS->getAlt(), false, false, false); writePS("\n"); cvtFunction(separationCS->getFunc()); writePS("]"); if (genXform) { writePS(" {}"); } if (updateColors) { addCustomColor(separationCS); } break; case csDeviceN: deviceNCS = (GfxDeviceNColorSpace *)colorSpace; if (level == psLevel3 || level == psLevel3Sep) { writePS("[/DeviceN\n"); writePS(" [ "); for (i = 0; i < deviceNCS->getNComps(); i++) { writePSString(deviceNCS->getColorantName(i)); writePS(" "); } writePS("]\n"); dumpColorSpaceL2(state, deviceNCS->getAlt(), false, updateColors, false); writePS("\n"); cvtFunction(deviceNCS->getTintTransformFunc(), map01 && deviceNCS->getAlt()->getMode() == csLab); writePS("]\n"); if (genXform) { writePS(" {}"); } } else { // DeviceN color spaces are a Level 3 PostScript feature. dumpColorSpaceL2(state, deviceNCS->getAlt(), false, updateColors, map01); if (genXform) { writePS(" "); cvtFunction(deviceNCS->getTintTransformFunc()); } } break; case csPattern: //~ unimplemented break; } } #ifdef OPI_SUPPORT void PSOutputDev::opiBegin(GfxState *state, Dict *opiDict) { if (generateOPI) { Object dict = opiDict->lookup("2.0"); if (dict.isDict()) { opiBegin20(state, dict.getDict()); } else { dict = opiDict->lookup("1.3"); if (dict.isDict()) { opiBegin13(state, dict.getDict()); } } } } void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) { double width, height, left, right, top, bottom; int w, h; writePS("%%BeginOPI: 2.0\n"); writePS("%%Distilled\n"); Object obj1 = dict->lookup("F"); Object obj2 = getFileSpecName(&obj1); if (obj2.isString()) { writePSFmt("%%ImageFileName: {0:t}\n", obj2.getString()); } obj1 = dict->lookup("MainImage"); if (obj1.isString()) { writePSFmt("%%MainImage: {0:t}\n", obj1.getString()); } //~ ignoring 'Tags' entry //~ need to use writePSString() and deal with >255-char lines obj1 = dict->lookup("Size"); if (obj1.isArray() && obj1.arrayGetLength() == 2) { obj2 = obj1.arrayGet(0); width = obj2.getNum(); obj2 = obj1.arrayGet(1); height = obj2.getNum(); writePSFmt("%%ImageDimensions: {0:.6g} {1:.6g}\n", width, height); } obj1 = dict->lookup("CropRect"); if (obj1.isArray() && obj1.arrayGetLength() == 4) { obj2 = obj1.arrayGet(0); left = obj2.getNum(); obj2 = obj1.arrayGet(1); top = obj2.getNum(); obj2 = obj1.arrayGet(2); right = obj2.getNum(); obj2 = obj1.arrayGet(3); bottom = obj2.getNum(); writePSFmt("%%ImageCropRect: {0:.6g} {1:.6g} {2:.6g} {3:.6g}\n", left, top, right, bottom); } obj1 = dict->lookup("Overprint"); if (obj1.isBool()) { writePSFmt("%%ImageOverprint: {0:s}\n", obj1.getBool() ? "true" : "false"); } obj1 = dict->lookup("Inks"); if (obj1.isName()) { writePSFmt("%%ImageInks: {0:s}\n", obj1.getName()); } else if (obj1.isArray() && obj1.arrayGetLength() >= 1) { obj2 = obj1.arrayGet(0); if (obj2.isName()) { writePSFmt("%%ImageInks: {0:s} {1:d}", obj2.getName(), (obj1.arrayGetLength() - 1) / 2); for (int i = 1; i + 1 < obj1.arrayGetLength(); i += 2) { Object obj3 = obj1.arrayGet(i); Object obj4 = obj1.arrayGet(i + 1); if (obj3.isString() && obj4.isNum()) { writePS(" "); writePSString(obj3.getString()->toStr()); writePSFmt(" {0:.6g}", obj4.getNum()); } } writePS("\n"); } } writePS("gsave\n"); writePS("%%BeginIncludedImage\n"); obj1 = dict->lookup("IncludedImageDimensions"); if (obj1.isArray() && obj1.arrayGetLength() == 2) { obj2 = obj1.arrayGet(0); w = obj2.getInt(); obj2 = obj1.arrayGet(1); h = obj2.getInt(); writePSFmt("%%IncludedImageDimensions: {0:d} {1:d}\n", w, h); } obj1 = dict->lookup("IncludedImageQuality"); if (obj1.isNum()) { writePSFmt("%%IncludedImageQuality: {0:.6g}\n", obj1.getNum()); } ++opi20Nest; } void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) { int left, right, top, bottom, samples, bits, width, height; double c, m, y, k; double llx, lly, ulx, uly, urx, ury, lrx, lry; double tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry; double horiz, vert; int i, j; writePS("save\n"); writePS("/opiMatrix2 matrix currentmatrix def\n"); writePS("opiMatrix setmatrix\n"); Object obj1 = dict->lookup("F"); Object obj2 = getFileSpecName(&obj1); if (obj2.isString()) { writePSFmt("%ALDImageFileName: {0:t}\n", obj2.getString()); } obj1 = dict->lookup("CropRect"); if (obj1.isArray() && obj1.arrayGetLength() == 4) { obj2 = obj1.arrayGet(0); left = obj2.getInt(); obj2 = obj1.arrayGet(1); top = obj2.getInt(); obj2 = obj1.arrayGet(2); right = obj2.getInt(); obj2 = obj1.arrayGet(3); bottom = obj2.getInt(); writePSFmt("%ALDImageCropRect: {0:d} {1:d} {2:d} {3:d}\n", left, top, right, bottom); } obj1 = dict->lookup("Color"); if (obj1.isArray() && obj1.arrayGetLength() == 5) { obj2 = obj1.arrayGet(0); c = obj2.getNum(); obj2 = obj1.arrayGet(1); m = obj2.getNum(); obj2 = obj1.arrayGet(2); y = obj2.getNum(); obj2 = obj1.arrayGet(3); k = obj2.getNum(); obj2 = obj1.arrayGet(4); if (obj2.isString()) { writePSFmt("%ALDImageColor: {0:.4g} {1:.4g} {2:.4g} {3:.4g} ", c, m, y, k); writePSString(obj2.getString()->toStr()); writePS("\n"); } } obj1 = dict->lookup("ColorType"); if (obj1.isName()) { writePSFmt("%ALDImageColorType: {0:s}\n", obj1.getName()); } //~ ignores 'Comments' entry //~ need to handle multiple lines obj1 = dict->lookup("CropFixed"); if (obj1.isArray()) { obj2 = obj1.arrayGet(0); ulx = obj2.getNum(); obj2 = obj1.arrayGet(1); uly = obj2.getNum(); obj2 = obj1.arrayGet(2); lrx = obj2.getNum(); obj2 = obj1.arrayGet(3); lry = obj2.getNum(); writePSFmt("%ALDImageCropFixed: {0:.6g} {1:.6g} {2:.6g} {3:.6g}\n", ulx, uly, lrx, lry); } obj1 = dict->lookup("GrayMap"); if (obj1.isArray()) { writePS("%ALDImageGrayMap:"); for (i = 0; i < obj1.arrayGetLength(); i += 16) { if (i > 0) { writePS("\n%%+"); } for (j = 0; j < 16 && i + j < obj1.arrayGetLength(); ++j) { obj2 = obj1.arrayGet(i + j); writePSFmt(" {0:d}", obj2.getInt()); } } writePS("\n"); } obj1 = dict->lookup("ID"); if (obj1.isString()) { writePSFmt("%ALDImageID: {0:t}\n", obj1.getString()); } obj1 = dict->lookup("ImageType"); if (obj1.isArray() && obj1.arrayGetLength() == 2) { obj2 = obj1.arrayGet(0); samples = obj2.getInt(); obj2 = obj1.arrayGet(1); bits = obj2.getInt(); writePSFmt("%ALDImageType: {0:d} {1:d}\n", samples, bits); } dict->lookup("Overprint"); if (obj1.isBool()) { writePSFmt("%ALDImageOverprint: {0:s}\n", obj1.getBool() ? "true" : "false"); } obj1 = dict->lookup("Position"); if (obj1.isArray() && obj1.arrayGetLength() == 8) { obj2 = obj1.arrayGet(0); llx = obj2.getNum(); obj2 = obj1.arrayGet(1); lly = obj2.getNum(); obj2 = obj1.arrayGet(2); ulx = obj2.getNum(); obj2 = obj1.arrayGet(3); uly = obj2.getNum(); obj2 = obj1.arrayGet(4); urx = obj2.getNum(); obj2 = obj1.arrayGet(5); ury = obj2.getNum(); obj2 = obj1.arrayGet(6); lrx = obj2.getNum(); obj2 = obj1.arrayGet(7); lry = obj2.getNum(); opiTransform(state, llx, lly, &tllx, &tlly); opiTransform(state, ulx, uly, &tulx, &tuly); opiTransform(state, urx, ury, &turx, &tury); opiTransform(state, lrx, lry, &tlrx, &tlry); writePSFmt("%ALDImagePosition: {0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g} {6:.6g} {7:.6g}\n", tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry); } obj1 = dict->lookup("Resolution"); if (obj1.isArray() && obj1.arrayGetLength() == 2) { obj2 = obj1.arrayGet(0); horiz = obj2.getNum(); obj2 = obj1.arrayGet(1); vert = obj2.getNum(); writePSFmt("%ALDImageResoution: {0:.6g} {1:.6g}\n", horiz, vert); } obj1 = dict->lookup("Size"); if (obj1.isArray() && obj1.arrayGetLength() == 2) { obj2 = obj1.arrayGet(0); width = obj2.getInt(); obj2 = obj1.arrayGet(1); height = obj2.getInt(); writePSFmt("%ALDImageDimensions: {0:d} {1:d}\n", width, height); } //~ ignoring 'Tags' entry //~ need to use writePSString() and deal with >255-char lines obj1 = dict->lookup("Tint"); if (obj1.isNum()) { writePSFmt("%ALDImageTint: {0:.6g}\n", obj1.getNum()); } obj1 = dict->lookup("Transparency"); if (obj1.isBool()) { writePSFmt("%ALDImageTransparency: {0:s}\n", obj1.getBool() ? "true" : "false"); } writePS("%%BeginObject: image\n"); writePS("opiMatrix2 setmatrix\n"); ++opi13Nest; } // Convert PDF user space coordinates to PostScript default user space // coordinates. This has to account for both the PDF CTM and the // PSOutputDev page-fitting transform. void PSOutputDev::opiTransform(GfxState *state, double x0, double y0, double *x1, double *y1) { double t; state->transform(x0, y0, x1, y1); *x1 += tx; *y1 += ty; if (rotate == 90) { t = *x1; *x1 = -*y1; *y1 = t; } else if (rotate == 180) { *x1 = -*x1; *y1 = -*y1; } else if (rotate == 270) { t = *x1; *x1 = *y1; *y1 = -t; } *x1 *= xScale; *y1 *= yScale; } void PSOutputDev::opiEnd(GfxState *state, Dict *opiDict) { if (generateOPI) { Object dict = opiDict->lookup("2.0"); if (dict.isDict()) { writePS("%%EndIncludedImage\n"); writePS("%%EndOPI\n"); writePS("grestore\n"); --opi20Nest; } else { dict = opiDict->lookup("1.3"); if (dict.isDict()) { writePS("%%EndObject\n"); writePS("restore\n"); --opi13Nest; } } } } #endif // OPI_SUPPORT void PSOutputDev::type3D0(GfxState *state, double wx, double wy) { writePSFmt("{0:.6g} {1:.6g} setcharwidth\n", wx, wy); writePS("q\n"); t3NeedsRestore = true; } void PSOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) { t3WX = wx; t3WY = wy; t3LLX = llx; t3LLY = lly; t3URX = urx; t3URY = ury; delete t3String; t3String = new GooString(); writePS("q\n"); t3FillColorOnly = true; t3Cacheable = true; t3NeedsRestore = true; } void PSOutputDev::drawForm(Ref ref) { writePSFmt("f_{0:d}_{1:d}\n", ref.num, ref.gen); } void PSOutputDev::psXObject(Stream *psStream, Stream *level1Stream) { Stream *str; int c; if ((level == psLevel1 || level == psLevel1Sep) && level1Stream) { str = level1Stream; } else { str = psStream; } str->reset(); while ((c = str->getChar()) != EOF) { writePSChar(c); } str->close(); } //~ can nextFunc be reset to 0 -- maybe at the start of each page? //~ or maybe at the start of each color space / pattern? void PSOutputDev::cvtFunction(const Function *func, bool invertPSFunction) { const SampledFunction *func0; const ExponentialFunction *func2; const StitchingFunction *func3; const PostScriptFunction *func4; int thisFunc, m, n, nSamples, i, j, k; switch (func->getType()) { case -1: // identity writePS("{}\n"); break; case 0: // sampled func0 = (const SampledFunction *)func; thisFunc = nextFunc++; m = func0->getInputSize(); n = func0->getOutputSize(); nSamples = n; for (i = 0; i < m; ++i) { nSamples *= func0->getSampleSize(i); } writePSFmt("/xpdfSamples{0:d} [\n", thisFunc); for (i = 0; i < nSamples; ++i) { writePSFmt("{0:.6g}\n", func0->getSamples()[i]); } writePS("] def\n"); writePSFmt("{{ {0:d} array {1:d} array {2:d} 2 roll\n", 2 * m, m, m + 2); // [e01] [efrac] x0 x1 ... xm-1 for (i = m - 1; i >= 0; --i) { // [e01] [efrac] x0 x1 ... xi writePSFmt("{0:.6g} sub {1:.6g} mul {2:.6g} add\n", func0->getDomainMin(i), (func0->getEncodeMax(i) - func0->getEncodeMin(i)) / (func0->getDomainMax(i) - func0->getDomainMin(i)), func0->getEncodeMin(i)); // [e01] [efrac] x0 x1 ... xi-1 xi' writePSFmt("dup 0 lt {{ pop 0 }} {{ dup {0:d} gt {{ pop {1:d} }} if }} ifelse\n", func0->getSampleSize(i) - 1, func0->getSampleSize(i) - 1); // [e01] [efrac] x0 x1 ... xi-1 xi' writePS("dup floor cvi exch dup ceiling cvi exch 2 index sub\n"); // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi') xi'-floor(xi') writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i + 3, i); // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi') writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i + 3, 2 * i + 1); // [e01] [efrac] x0 x1 ... xi-1 floor(xi') writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i + 2, 2 * i); // [e01] [efrac] x0 x1 ... xi-1 } // [e01] [efrac] for (i = 0; i < n; ++i) { // [e01] [efrac] y(0) ... y(i-1) for (j = 0; j < (1 << m); ++j) { // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(j-1) writePSFmt("xpdfSamples{0:d}\n", thisFunc); k = m - 1; writePSFmt("{0:d} index {1:d} get\n", i + j + 2, 2 * k + ((j >> k) & 1)); for (k = m - 2; k >= 0; --k) { writePSFmt("{0:d} mul {1:d} index {2:d} get add\n", func0->getSampleSize(k), i + j + 3, 2 * k + ((j >> k) & 1)); } if (n > 1) { writePSFmt("{0:d} mul {1:d} add ", n, i); } writePS("get\n"); } // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^m-1) for (j = 0; j < m; ++j) { // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^(m-j)-1) for (k = 0; k < (1 << (m - j)); k += 2) { // [e01] [efrac] y(0) ... y(i-1) <2^(m-j)-k s values> writePSFmt("{0:d} index {1:d} get dup\n", i + k / 2 + (1 << (m - j)) - k, j); writePS("3 2 roll mul exch 1 exch sub 3 2 roll mul add\n"); writePSFmt("{0:d} 1 roll\n", k / 2 + (1 << (m - j)) - k - 1); } // [e01] [efrac] s'(0) s'(1) ... s(2^(m-j-1)-1) } // [e01] [efrac] y(0) ... y(i-1) s writePSFmt("{0:.6g} mul {1:.6g} add\n", func0->getDecodeMax(i) - func0->getDecodeMin(i), func0->getDecodeMin(i)); writePSFmt("dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n", func0->getRangeMin(i), func0->getRangeMin(i), func0->getRangeMax(i), func0->getRangeMax(i)); // [e01] [efrac] y(0) ... y(i-1) y(i) } // [e01] [efrac] y(0) ... y(n-1) writePSFmt("{0:d} {1:d} roll pop pop \n", n + 2, n); if (invertPSFunction) { for (i = 0; i < n; ++i) { writePSFmt("{0:d} -1 roll ", n); writePSFmt("{0:.6g} sub {1:.6g} div ", func0->getRangeMin(i), func0->getRangeMax(i) - func0->getRangeMin(i)); } } writePS("}\n"); break; case 2: // 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)); // x for (i = 0; i < n; ++i) { // x y(0) .. y(i-1) writePSFmt("{0:d} index {1:.6g} exp {2:.6g} mul {3:.6g} add\n", i, func2->getE(), func2->getC1()[i] - func2->getC0()[i], func2->getC0()[i]); if (func2->getHasRange()) { writePSFmt("dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n", func2->getRangeMin(i), func2->getRangeMin(i), func2->getRangeMax(i), func2->getRangeMax(i)); } } // x y(0) .. y(n-1) writePSFmt("{0:d} {1:d} roll pop \n", n + 1, n); if (invertPSFunction && func2->getHasRange()) { for (i = 0; i < n; ++i) { writePSFmt("{0:d} -1 roll ", n); writePSFmt("{0:.6g} sub {1:.6g} div ", func2->getRangeMin(i), func2->getRangeMax(i) - func2->getRangeMin(i)); } } writePS("}\n"); break; case 3: // stitching func3 = (const StitchingFunction *)func; thisFunc = nextFunc++; for (i = 0; i < func3->getNumFuncs(); ++i) { cvtFunction(func3->getFunc(i)); writePSFmt("/xpdfFunc{0:d}_{1:d} exch def\n", thisFunc, i); } writePSFmt("{{ dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n", func3->getDomainMin(0), func3->getDomainMin(0), func3->getDomainMax(0), func3->getDomainMax(0)); for (i = 0; i < func3->getNumFuncs() - 1; ++i) { writePSFmt("dup {0:.6g} lt {{ {1:.6g} sub {2:.6g} mul {3:.6g} add xpdfFunc{4:d}_{5:d} }} {{\n", func3->getBounds()[i + 1], func3->getBounds()[i], func3->getScale()[i], func3->getEncode()[2 * i], thisFunc, i); } writePSFmt("{0:.6g} sub {1:.6g} mul {2:.6g} add xpdfFunc{3:d}_{4:d}\n", func3->getBounds()[i], func3->getScale()[i], func3->getEncode()[2 * i], thisFunc, i); for (i = 0; i < func3->getNumFuncs() - 1; ++i) { writePS("} ifelse\n"); } if (invertPSFunction && func3->getHasRange()) { n = func3->getOutputSize(); for (i = 0; i < n; ++i) { writePSFmt("{0:d} -1 roll ", n); writePSFmt("{0:.6g} sub {1:.6g} div ", func3->getRangeMin(i), func3->getRangeMax(i) - func3->getRangeMin(i)); } } writePS("}\n"); break; case 4: // PostScript func4 = (const PostScriptFunction *)func; if (invertPSFunction) { GooString *codeString = new GooString(func4->getCodeString()); for (i = codeString->getLength() - 1; i > 0; i--) { if (codeString->getChar(i) == '}') { codeString->del(i); break; } } writePS(codeString->c_str()); writePS("\n"); delete codeString; n = func4->getOutputSize(); for (i = 0; i < n; ++i) { writePSFmt("{0:d} -1 roll ", n); writePSFmt("{0:.6g} sub {1:.6g} div ", func4->getRangeMin(i), func4->getRangeMax(i) - func4->getRangeMin(i)); } writePS("}\n"); } else { writePS(func4->getCodeString()->c_str()); writePS("\n"); } break; } } void PSOutputDev::writePSChar(char c) { if (t3String) { t3String->append(c); } else { (*outputFunc)(outputStream, &c, 1); } } void PSOutputDev::writePS(const char *s) { if (t3String) { t3String->append(s); } else { (*outputFunc)(outputStream, s, strlen(s)); } } void PSOutputDev::writePSBuf(const char *s, int len) { if (t3String) { for (int i = 0; i < len; i++) { t3String->append(s[i]); } } else { (*outputFunc)(outputStream, s, len); } } void PSOutputDev::writePSFmt(const char *fmt, ...) { va_list args; va_start(args, 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()); } va_end(args); } void PSOutputDev::writePSString(const std::string &s) { unsigned char *p; int n, line; char buf[8]; writePSChar('('); line = 1; for (p = (unsigned char *)s.c_str(), n = s.size(); n; ++p, --n) { if (line >= 64) { writePSChar('\\'); writePSChar('\n'); line = 0; } if (*p == '(' || *p == ')' || *p == '\\') { writePSChar('\\'); writePSChar((char)*p); line += 2; } else if (*p < 0x20 || *p >= 0x80) { sprintf(buf, "\\%03o", *p); writePS(buf); line += 4; } else { writePSChar((char)*p); ++line; } } writePSChar(')'); } void PSOutputDev::writePSName(const char *s) { const char *p; char c; p = s; while ((c = *p++)) { if (c <= (char)0x20 || c >= (char)0x7f || c == '(' || c == ')' || c == '<' || c == '>' || c == '[' || c == ']' || c == '{' || c == '}' || c == '/' || c == '%' || c == '\\') { writePSFmt("#{0:02x}", c & 0xff); } else { writePSChar(c); } } } std::string PSOutputDev::filterPSName(const std::string &name) { 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 += '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); } else { name2 += c; } } return name2; } // Convert GooString to GooString, with appropriate escaping // of things that can't appear in a label // This is heavily based on the writePSTextLine() method GooString *PSOutputDev::filterPSLabel(GooString *label, bool *needParens) { int i, step; bool isNumeric; // - DSC comments must be printable ASCII; control chars and // backslashes have to be escaped (we do cheap UCS2-to-ASCII // conversion by simply ignoring the high byte) // - parentheses are escaped. this isn't strictly necessary for matched // parentheses, but shouldn't be a problem // - lines are limited to 255 chars (we limit to 200 here to allow // for the keyword, which was emitted by the caller) GooString *label2 = new GooString(); int labelLength = label->getLength(); if (labelLength == 0) { isNumeric = false; } else { // this gets changed later if we find a non-numeric character isNumeric = true; } if ((labelLength >= 2) && ((label->getChar(0) & 0xff) == 0xfe) && ((label->getChar(1) & 0xff) == 0xff)) { // UCS2 mode i = 3; step = 2; if ((label->getChar(labelLength - 1) == 0)) { // prune the trailing null (0x000 for UCS2) labelLength -= 2; } } else { i = 0; step = 1; } for (int j = 0; i < labelLength && j < 200; i += step) { char c = label->getChar(i); if ((c < '0') || (c > '9')) { isNumeric = false; } if (c == '\\') { label2->append("\\\\"); j += 2; } else if (c == ')') { label2->append("\\)"); } else if (c == '(') { label2->append("\\("); } else if (c < 0x20 || c > 0x7e) { std::unique_ptr aux = GooString::format("\\{0:03o}", c); label2->append(aux.get()); j += 4; } else { label2->append(c); ++j; } } if (needParens) { *needParens = !(isNumeric); } return label2; } // Write a DSC-compliant . void PSOutputDev::writePSTextLine(const GooString *s) { int i, j, step; int c; // - DSC comments must be printable ASCII; control chars and // backslashes have to be escaped (we do cheap Unicode-to-ASCII // conversion by simply ignoring the high byte) // - lines are limited to 255 chars (we limit to 200 here to allow // 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) { i = 3; step = 2; } else { i = 0; step = 1; } for (j = 0; i < s->getLength() && j < 200; i += step) { c = s->getChar(i) & 0xff; if (c == '\\') { writePS("\\\\"); j += 2; } else if (c < 0x20 || c > 0x7e || (j == 0 && c == '(')) { writePSFmt("\\{0:03o}", c); j += 4; } else { writePSChar(c); ++j; } } writePS("\n"); } poppler-24.02.0/poppler/PSOutputDev.h000066400000000000000000000626231455701731300174130ustar00rootroot00000000000000//======================================================================== // // PSOutputDev.h // // 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) 2005 Martin Kretzschmar // Copyright (C) 2005 Kristian Høgsberg // Copyright (C) 2006-2008, 2012, 2013, 2015, 2017-2023 Albert Astals Cid // Copyright (C) 2007 Brad Hards // Copyright (C) 2009-2013 Thomas Freitag // Copyright (C) 2009 Till Kamppeter // Copyright (C) 2009 Carlos Garcia Campos // Copyright (C) 2009, 2011, 2015-2017, 2020 William Bader // Copyright (C) 2010 Hib Eris // Copyright (C) 2011, 2014, 2017, 2020 Adrian Johnson // Copyright (C) 2012 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) 2018, 2020 Philipp Knechtges // Copyright (C) 2019, 2023 Oliver Sander // Copyright (C) 2021 Hubert Figuiere // Copyright (C) 2021 Christian Persch // Copyright (C) 2023 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 // //======================================================================== #ifndef PSOUTPUTDEV_H #define PSOUTPUTDEV_H #include "poppler-config.h" #include "poppler_private_export.h" #include #include "Object.h" #include "GfxState.h" #include "GlobalParams.h" #include "OutputDev.h" #include "fofi/FoFiBase.h" #include #include #include #include #include #include #include "splash/Splash.h" class PDFDoc; class XRef; class Function; class GfxPath; class GfxFont; class GfxColorSpace; class GfxSeparationColorSpace; class PDFRectangle; struct PST1FontName; struct PSFont8Info; struct PSFont16Enc; class PSOutCustomColor; struct PSOutPaperSize; class PSOutputDev; //------------------------------------------------------------------------ // PSOutputDev //------------------------------------------------------------------------ enum PSLevel { psLevel1, psLevel1Sep, psLevel2, psLevel2Sep, psLevel3, psLevel3Sep }; enum PSOutMode { psModePS, psModeEPS, psModeForm }; enum PSFileType { psFile, // write to file psPipe, // write to pipe psStdout, // write to stdout psGeneric // write to a generic stream }; enum PSOutCustomCodeLocation { psOutCustomDocSetup, psOutCustomPageSetup }; enum PSForceRasterize { psRasterizeWhenNeeded, // default psAlwaysRasterize, // always rasterize, useful for testing psNeverRasterize // never rasterize, may produce incorrect output }; typedef GooString *(*PSOutCustomCodeCbk)(PSOutputDev *psOut, PSOutCustomCodeLocation loc, int n, void *data); class POPPLER_PRIVATE_EXPORT PSOutputDev : public OutputDev { public: // Open a PostScript output file, and write the prolog. // pages has to be sorted in increasing order PSOutputDev(const char *fileName, PDFDoc *docA, char *psTitleA, const std::vector &pages, PSOutMode modeA, int paperWidthA = -1, int paperHeightA = -1, bool noCrop = false, bool duplexA = true, int imgLLXA = 0, int imgLLYA = 0, int imgURXA = 0, int imgURYA = 0, PSForceRasterize forceRasterizeA = psRasterizeWhenNeeded, bool manualCtrlA = false, PSOutCustomCodeCbk customCodeCbkA = nullptr, void *customCodeCbkDataA = nullptr, PSLevel levelA = psLevel2); // Open a PSOutputDev that will write to a file descriptor PSOutputDev(int fdA, PDFDoc *docA, char *psTitleA, const std::vector &pages, PSOutMode modeA, int paperWidthA = -1, int paperHeightA = -1, bool noCrop = false, bool duplexA = true, int imgLLXA = 0, int imgLLYA = 0, int imgURXA = 0, int imgURYA = 0, PSForceRasterize forceRasterizeA = psRasterizeWhenNeeded, bool manualCtrlA = false, PSOutCustomCodeCbk customCodeCbkA = nullptr, void *customCodeCbkDataA = nullptr, PSLevel levelA = psLevel2); // Open a PSOutputDev that will write to a generic stream. // pages has to be sorted in increasing order PSOutputDev(FoFiOutputFunc outputFuncA, void *outputStreamA, char *psTitleA, PDFDoc *docA, const std::vector &pages, PSOutMode modeA, int paperWidthA = -1, int paperHeightA = -1, bool noCrop = false, bool duplexA = true, int imgLLXA = 0, int imgLLYA = 0, int imgURXA = 0, int imgURYA = 0, PSForceRasterize forceRasterizeA = psRasterizeWhenNeeded, bool manualCtrlA = false, PSOutCustomCodeCbk customCodeCbkA = nullptr, void *customCodeCbkDataA = nullptr, PSLevel levelA = psLevel2); // Destructor -- writes the trailer and closes the file. ~PSOutputDev() override; // Check if file was successfully created. virtual bool isOk() { return ok; } //---- get info about output device // Does this device use upside-down coordinates? // (Upside-down means (0,0) is the top left corner of the page.) bool upsideDown() override { return false; } // Does this device use drawChar() or drawString()? bool useDrawChar() override { return false; } // Does this device use tilingPatternFill()? If this returns false, // tiling pattern fills will be reduced to a series of other drawing // operations. bool useTilingPatternFill() override { return true; } // 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. bool useShadedFills(int type) override { return (type < 4 && level >= psLevel2) || (type == 7 && level >= psLevel3); } // Does this device use drawForm()? If this returns false, // form-type XObjects will be interpreted (i.e., unrolled). bool useDrawForm() override { return preloadImagesForms; } // Does this device use beginType3Char/endType3Char? Otherwise, // text in Type 3 fonts will be drawn with drawChar/drawString. bool interpretType3Chars() override { return false; } bool needClipToCropBox() override { return mode == psModeEPS; } //----- header/trailer (used only if manualCtrl is true) // Write the document-level header. void writeHeader(int nPages, const PDFRectangle *mediaBox, const PDFRectangle *cropBox, int pageRotate, const char *title); // Write the Xpdf procset. void writeXpdfProcset(); // Write the trailer for the current page. void writePageTrailer(); // Write the document trailer. void writeTrailer(); //----- initialization and control // Check to see if a page slice should be displayed. If this // returns false, the page display is aborted. Typically, an // OutputDev will use some alternate means to display the page // before returning false. bool checkPageSlice(Page *page, double hDPI, double vDPI, int rotate, bool useMediaBox, bool crop, int sliceX, int sliceY, int sliceW, int sliceH, bool printing, bool (*abortCheckCbk)(void *data) = nullptr, void *abortCheckCbkData = nullptr, bool (*annotDisplayDecideCbk)(Annot *annot, void *user_data) = nullptr, void *annotDisplayDecideCbkData = nullptr) override; // Start a page. void startPage(int pageNum, GfxState *state, XRef *xref) override; // End a page. void endPage() override; //----- save/restore graphics state void saveState(GfxState *state) override; void restoreState(GfxState *state) override; //----- update graphics state void updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32) override; void updateLineDash(GfxState *state) override; void updateFlatness(GfxState *state) override; void updateLineJoin(GfxState *state) override; void updateLineCap(GfxState *state) override; void updateMiterLimit(GfxState *state) override; void updateLineWidth(GfxState *state) override; void updateFillColorSpace(GfxState *state) override; void updateStrokeColorSpace(GfxState *state) override; void updateFillColor(GfxState *state) override; void updateStrokeColor(GfxState *state) override; void updateFillOverprint(GfxState *state) override; void updateStrokeOverprint(GfxState *state) override; void updateOverprintMode(GfxState *state) override; void updateTransfer(GfxState *state) override; //----- update text state void updateFont(GfxState *state) override; void updateTextMat(GfxState *state) override; void updateCharSpace(GfxState *state) override; void updateRender(GfxState *state) override; void updateRise(GfxState *state) override; void updateWordSpace(GfxState *state) override; void updateHorizScaling(GfxState *state) override; void updateTextPos(GfxState *state) override; void updateTextShift(GfxState *state, double shift) override; void saveTextPos(GfxState *state) override; void restoreTextPos(GfxState *state) override; //----- path painting void stroke(GfxState *state) override; void fill(GfxState *state) override; void eoFill(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; bool functionShadedFill(GfxState *state, GfxFunctionShading *shading) override; bool axialShadedFill(GfxState *state, GfxAxialShading *shading, double /*tMin*/, double /*tMax*/) override; bool radialShadedFill(GfxState *state, GfxRadialShading *shading, double /*sMin*/, double /*sMax*/) override; bool patchMeshShadedFill(GfxState *state, GfxPatchMeshShading *shading) override; //----- path clipping void clip(GfxState *state) override; void eoClip(GfxState *state) override; void clipToStrokePath(GfxState *state) override; //----- text drawing void drawString(GfxState *state, const GooString *s) override; void beginTextObject(GfxState *state) override; void endTextObject(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 drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) override; 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; #ifdef OPI_SUPPORT //----- OPI functions void opiBegin(GfxState *state, Dict *opiDict) override; void opiEnd(GfxState *state, Dict *opiDict) override; #endif //----- Type 3 font operators void type3D0(GfxState *state, double wx, double wy) override; void type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) override; //----- form XObjects void drawForm(Ref ref) override; //----- PostScript XObjects void psXObject(Stream *psStream, Stream *level1Stream) override; //----- miscellaneous void setOffset(double x, double y) { tx0 = x; ty0 = y; } void setScale(double x, double y) { xScale0 = x; yScale0 = y; } void setRotate(int rotateA) { rotate0 = rotateA; } void setClip(double llx, double lly, double urx, double ury) { clipLLX0 = llx; clipLLY0 = lly; clipURX0 = urx; clipURY0 = ury; } void setUnderlayCbk(void (*cbk)(PSOutputDev *psOut, void *data), void *data) { underlayCbk = cbk; underlayCbkData = data; } void setOverlayCbk(void (*cbk)(PSOutputDev *psOut, void *data), void *data) { overlayCbk = cbk; overlayCbkData = data; } 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 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 { 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) { processColorFormat = format; processColorFormatSpecified = true; } 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(); void setupResources(Dict *resDict); void setupFonts(Dict *resDict); 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 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 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 setupType3Font(GfxFont *font, GooString *psName, Dict *parentResDict); GooString *makePSFontName(GfxFont *font, const Ref *id); void setupImages(Dict *resDict); void setupImage(Ref id, Stream *str, bool mask); void setupForms(Dict *resDict); void setupForm(Ref id, Object *strObj); void addProcessColor(double c, double m, double y, double k); void addCustomColor(GfxSeparationColorSpace *sepCS); void doPath(const GfxPath *path); void maskToClippingPath(Stream *maskStr, int maskWidth, int maskHeight, bool maskInvert); void 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); void 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); void doImageL2(GfxState *state, 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); void doImageL3(GfxState *state, 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); void dumpColorSpaceL2(GfxState *state, GfxColorSpace *colorSpace, bool genXform, bool updateColors, bool map01); bool tilingPatternFillL1(GfxState *state, Catalog *cat, Object *str, const double *pmat, int paintType, int tilingType, Dict *resDict, const double *mat, const double *bbox, int x0, int y0, int x1, int y1, double xStep, double yStep); bool tilingPatternFillL2(GfxState *state, Catalog *cat, Object *str, const double *pmat, int paintType, int tilingType, Dict *resDict, const double *mat, const double *bbox, int x0, int y0, int x1, int y1, double xStep, double yStep); #ifdef OPI_SUPPORT void opiBegin20(GfxState *state, Dict *dict); void opiBegin13(GfxState *state, Dict *dict); void opiTransform(GfxState *state, double x0, double y0, double *x1, double *y1); #endif void cvtFunction(const Function *func, bool invertPSFunction = false); static std::string filterPSName(const std::string &name); // Write the document-level setup. void writeDocSetup(Catalog *catalog, const std::vector &pageList, bool duplexA); void writePSChar(char c); void writePS(const char *s); void writePSBuf(const char *s, int len); 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); PSLevel level; // PostScript level (1, 2, separation) PSOutMode mode; // PostScript mode (PS, EPS, form) int paperWidth; // width of paper, in pts int paperHeight; // height of paper, in pts bool paperMatch; // true if paper size is set to match each page int prevWidth; // width of previous page // (only psModePSOrigPageSizes output mode) int prevHeight; // height of previous page // (only psModePSOrigPageSizes output mode) int imgLLX, imgLLY, // imageable area, in pts imgURX, imgURY; bool noCrop; bool duplex; std::vector pages; char *psTitle; bool postInitDone; // true if postInit() was called FoFiOutputFunc outputFunc; void *outputStream; PSFileType fileType; // file / pipe / stdout bool manualCtrl; int seqPage; // current sequential page number void (*underlayCbk)(PSOutputDev *psOut, void *data); void *underlayCbkData; void (*overlayCbk)(PSOutputDev *psOut, void *data); void *overlayCbkData; GooString *(*customCodeCbk)(PSOutputDev *psOut, PSOutCustomCodeLocation loc, int n, void *data); void *customCodeCbkData; PDFDoc *doc; XRef *xref; // the xref table for this PDF file 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 PSFont16Enc *font16Enc; // encodings for substitute 16-bit fonts int font16EncLen; // number of entries in font16Enc array int font16EncSize; // size of font16Enc array Ref *imgIDs; // list of image IDs for in-memory images int imgIDLen; // number of entries in imgIDs array int imgIDSize; // size of imgIDs array Ref *formIDs; // list of IDs for predefined forms int formIDLen; // number of entries in formIDs array int formIDSize; // size of formIDs array int numSaves; // current number of gsaves 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::map pagePaperSize; // page num to paperSize entry mapping double tx0, ty0; // global translation double xScale0, yScale0; // global scaling int rotate0; // rotation angle (0, 90, 180, 270) double clipLLX0, clipLLY0, clipURX0, clipURY0; double tx, ty; // global translation for current page double xScale, yScale; // global scaling for current page int rotate; // rotation angle for current page double epsX1, epsY1, // EPS bounding box (unrotated) epsX2, epsY2; GooString *embFontList; // resource comments for embedded fonts int processColors; // used process colors PSOutCustomColor // used custom colors *customColors; bool haveTextClip; // set if text has been drawn with a // clipping render mode bool inType3Char; // inside a Type 3 CharProc bool inUncoloredPattern; // inside a uncolored pattern (PaintType = 2) GooString *t3String; // Type 3 content string double t3WX, t3WY, // Type 3 character parameters t3LLX, t3LLY, t3URX, t3URY; bool t3FillColorOnly; // operators should only use the fill color bool t3Cacheable; // cleared if char is not cacheable bool t3NeedsRestore; // set if a 'q' operator was issued PSForceRasterize forceRasterize; // controls the rasterization of pages into images bool displayText; // displayText bool psCenter; // center pages on the paper bool psExpandSmaller = false; // expand smaller pages to fill paper bool psShrinkLarger = true; // shrink larger pages to fit paper bool overprintPreview = false; // enable overprint preview bool rasterAntialias; // antialias on rasterize bool uncompressPreloadedImages; double rasterResolution; // PostScript rasterization resolution (dpi) bool embedType1; // embed Type 1 fonts? bool embedTrueType; // embed TrueType fonts? bool embedCIDPostScript; // embed CID PostScript fonts? bool embedCIDTrueType; // embed CID TrueType fonts? bool fontPassthrough; // pass all fonts through as-is? bool optimizeColorSpace; // false to keep gray RGB images in their original color space // true to optimize gray images to DeviceGray color space bool passLevel1CustomColor; // false to convert all custom colors to CMYK // true to pass custom colors // has effect only when doing a level1sep bool preloadImagesForms; // preload PostScript images and forms into // memory bool generateOPI; // generate PostScript OPI comments? bool useASCIIHex; // use ASCIIHex instead of ASCII85? bool useBinary; // use binary instead of hex bool enableLZW; // enable LZW compression bool enableFlate; // enable Flate compression SplashColorMode processColorFormat; bool processColorFormatSpecified; std::unordered_set iccEmitted; // contains ICCBased CSAs that have been emitted #ifdef OPI_SUPPORT int opi13Nest; // nesting level of OPI 1.3 objects int opi20Nest; // nesting level of OPI 2.0 objects #endif bool ok; // set up ok? std::set patternsBeingTiled; // the patterns that are being tiled friend class WinPDFPrinter; }; #endif poppler-24.02.0/poppler/PSTokenizer.cc000066400000000000000000000106261455701731300175600ustar00rootroot00000000000000//======================================================================== // // PSTokenizer.cc // // Copyright 2002-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 Scott Turner // Copyright (C) 2008 Albert Astals Cid // Copyright (C) 2017 Vincent Le Garrec // // 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 #include #include #include "PSTokenizer.h" //------------------------------------------------------------------------ // A '1' in this array means the character is white space. A '1' or // '2' means the character ends a name or command. static const char specialChars[256] = { 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, // 0x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x 1, 0, 0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, // 2x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, // 3x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, // 5x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, // 7x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ax 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // bx 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // cx 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // dx 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ex 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // fx }; //------------------------------------------------------------------------ PSTokenizer::PSTokenizer(int (*getCharFuncA)(void *), void *dataA) { getCharFunc = getCharFuncA; data = dataA; charBuf = -1; } PSTokenizer::~PSTokenizer() { } bool PSTokenizer::getToken(char *buf, int size, int *length) { bool comment, backslash; int c; int i; // skip leading whitespace and comments comment = false; while (true) { if ((c = getChar()) == EOF) { buf[0] = '\0'; *length = 0; return false; } if (comment) { if (c == '\x0a' || c == '\x0d') { comment = false; } } else if (c == '%') { comment = true; } else if (specialChars[static_cast(c)] != 1) { break; } } // Reserve room for terminating '\0' size--; // read a token i = 0; buf[i++] = c; if (c == '(') { backslash = false; while ((c = lookChar()) != EOF) { consumeChar(); if (i < size) { buf[i++] = c; } if (c == '\\') { backslash = true; } else if (!backslash && c == ')') { break; } else { backslash = false; } } } else if (c == '<') { while ((c = lookChar()) != EOF) { consumeChar(); if (i < size && specialChars[static_cast(c)] != 1) { buf[i++] = c; } if (c == '>') { break; } } } else if (c != '[' && c != ']') { while ((c = lookChar()) != EOF && !specialChars[static_cast(c)]) { consumeChar(); if (i < size) { buf[i++] = c; } } } // Zero terminate token string buf[i] = '\0'; // Return length of token *length = i; return true; } int PSTokenizer::lookChar() { if (charBuf < 0) { charBuf = (*getCharFunc)(data); } return charBuf; } void PSTokenizer::consumeChar() { charBuf = -1; } int PSTokenizer::getChar() { int c = charBuf; if (c < 0) { c = (*getCharFunc)(data); } else { charBuf = -1; } return c; } poppler-24.02.0/poppler/PSTokenizer.h000066400000000000000000000023651455701731300174230ustar00rootroot00000000000000//======================================================================== // // PSTokenizer.h // // Copyright 2002-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 Scott Turner // // 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 PSTOKENIZER_H #define PSTOKENIZER_H //------------------------------------------------------------------------ class PSTokenizer { public: PSTokenizer(int (*getCharFuncA)(void *), void *dataA); ~PSTokenizer(); // Get the next PostScript token. Returns false at end-of-stream. bool getToken(char *buf, int size, int *length); private: int lookChar(); void consumeChar(); int getChar(); int (*getCharFunc)(void *); void *data; int charBuf; }; #endif poppler-24.02.0/poppler/Page.cc000066400000000000000000000624121455701731300162170ustar00rootroot00000000000000//======================================================================== // // Page.cc // // Copyright 1996-2007 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) 2005 Kristian Høgsberg // Copyright (C) 2005 Jeff Muizelaar // Copyright (C) 2005-2013, 2016-2023 Albert Astals Cid // Copyright (C) 2006-2008 Pino Toscano // Copyright (C) 2006 Nickolay V. Shmyrev // Copyright (C) 2006 Scott Turner // Copyright (C) 2006-2011, 2015 Carlos Garcia Campos // Copyright (C) 2007 Julien Rebetez // Copyright (C) 2008 Iñigo Martínez // Copyright (C) 2008 Brad Hards // Copyright (C) 2008 Ilya Gorenbein // Copyright (C) 2012, 2013 Fabio D'Urso // Copyright (C) 2013, 2014 Thomas Freitag // Copyright (C) 2013 Jason Crain // 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 // // 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 #include #include #include "GlobalParams.h" #include "Object.h" #include "Array.h" #include "Dict.h" #include "PDFDoc.h" #include "XRef.h" #include "Link.h" #include "OutputDev.h" #include "Gfx.h" #include "GfxState.h" #include "Annot.h" #include "TextOutputDev.h" #include "Form.h" #include "Error.h" #include "Page.h" #include "Catalog.h" #include "Form.h" //------------------------------------------------------------------------ // PDFRectangle //------------------------------------------------------------------------ void PDFRectangle::clipTo(PDFRectangle *rect) { if (x1 < rect->x1) { x1 = rect->x1; } else if (x1 > rect->x2) { x1 = rect->x2; } if (x2 < rect->x1) { x2 = rect->x1; } else if (x2 > rect->x2) { x2 = rect->x2; } if (y1 < rect->y1) { y1 = rect->y1; } else if (y1 > rect->y2) { y1 = rect->y2; } if (y2 < rect->y1) { y2 = rect->y1; } else if (y2 > rect->y2) { y2 = rect->y2; } } //------------------------------------------------------------------------ // PageAttrs //------------------------------------------------------------------------ PageAttrs::PageAttrs(PageAttrs *attrs, Dict *dict) { Object obj1; PDFRectangle mBox; const bool isPage = dict->is("Page"); // get old/default values if (attrs) { mediaBox = attrs->mediaBox; cropBox = attrs->cropBox; haveCropBox = attrs->haveCropBox; rotate = attrs->rotate; resources = attrs->resources.copy(); } else { // set default MediaBox to 8.5" x 11" -- this shouldn't be necessary // but some (non-compliant) PDF files don't specify a MediaBox mediaBox.x1 = 0; mediaBox.y1 = 0; mediaBox.x2 = 612; mediaBox.y2 = 792; cropBox.x1 = cropBox.y1 = cropBox.x2 = cropBox.y2 = 0; haveCropBox = false; rotate = 0; resources.setToNull(); } // media box if (readBox(dict, "MediaBox", &mBox)) { mediaBox = mBox; } // crop box if (readBox(dict, "CropBox", &cropBox)) { haveCropBox = true; } if (!haveCropBox) { cropBox = mediaBox; } if (isPage) { // cropBox can not be bigger than mediaBox if (cropBox.x2 - cropBox.x1 > mediaBox.x2 - mediaBox.x1) { cropBox.x1 = mediaBox.x1; cropBox.x2 = mediaBox.x2; } if (cropBox.y2 - cropBox.y1 > mediaBox.y2 - mediaBox.y1) { cropBox.y1 = mediaBox.y1; cropBox.y2 = mediaBox.y2; } } // other boxes bleedBox = cropBox; readBox(dict, "BleedBox", &bleedBox); trimBox = cropBox; readBox(dict, "TrimBox", &trimBox); artBox = cropBox; readBox(dict, "ArtBox", &artBox); // rotate obj1 = dict->lookup("Rotate"); if (obj1.isInt()) { rotate = obj1.getInt(); } while (rotate < 0) { rotate += 360; } while (rotate >= 360) { rotate -= 360; } // misc attributes lastModified = dict->lookup("LastModified"); boxColorInfo = dict->lookup("BoxColorInfo"); group = dict->lookup("Group"); metadata = dict->lookup("Metadata"); pieceInfo = dict->lookup("PieceInfo"); separationInfo = dict->lookup("SeparationInfo"); // resource dictionary Object objResources = dict->lookup("Resources"); if (objResources.isDict()) { resources = std::move(objResources); } } PageAttrs::~PageAttrs() { } void PageAttrs::clipBoxes() { cropBox.clipTo(&mediaBox); bleedBox.clipTo(&mediaBox); trimBox.clipTo(&mediaBox); artBox.clipTo(&mediaBox); } bool PageAttrs::readBox(Dict *dict, const char *key, PDFRectangle *box) { PDFRectangle tmp; double t; Object obj1, obj2; bool ok; obj1 = dict->lookup(key); if (obj1.isArray() && obj1.arrayGetLength() == 4) { ok = true; obj2 = obj1.arrayGet(0); if (obj2.isNum()) { tmp.x1 = obj2.getNum(); } else { ok = false; } obj2 = obj1.arrayGet(1); if (obj2.isNum()) { tmp.y1 = obj2.getNum(); } else { ok = false; } obj2 = obj1.arrayGet(2); if (obj2.isNum()) { tmp.x2 = obj2.getNum(); } else { ok = false; } obj2 = obj1.arrayGet(3); if (obj2.isNum()) { tmp.y2 = obj2.getNum(); } else { ok = false; } if (tmp.x1 == 0 && tmp.x2 == 0 && tmp.y1 == 0 && tmp.y2 == 0) { ok = false; } if (ok) { if (tmp.x1 > tmp.x2) { t = tmp.x1; tmp.x1 = tmp.x2; tmp.x2 = t; } if (tmp.y1 > tmp.y2) { t = tmp.y1; tmp.y1 = tmp.y2; tmp.y2 = t; } *box = tmp; } } else { ok = false; } return ok; } //------------------------------------------------------------------------ // Page //------------------------------------------------------------------------ #define pageLocker() const std::scoped_lock locker(mutex) Page::Page(PDFDoc *docA, int numA, Object &&pageDict, Ref pageRefA, PageAttrs *attrsA, Form *form) : pageRef(pageRefA) { ok = true; doc = docA; xref = doc->getXRef(); num = numA; duration = -1; annots = nullptr; structParents = -1; pageObj = std::move(pageDict); // get attributes attrs = attrsA; attrs->clipBoxes(); // transtion trans = pageObj.dictLookupNF("Trans").copy(); if (!(trans.isRef() || trans.isDict() || trans.isNull())) { error(errSyntaxError, -1, "Page transition object (page {0:d}) is wrong type ({1:s})", num, trans.getTypeName()); trans = Object(); } // duration const Object &tmp = pageObj.dictLookupNF("Dur"); if (!(tmp.isNum() || tmp.isNull())) { error(errSyntaxError, -1, "Page duration object (page {0:d}) is wrong type ({1:s})", num, tmp.getTypeName()); } else if (tmp.isNum()) { 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())) { error(errSyntaxError, -1, "Page annotations object (page {0:d}) is wrong type ({1:s})", num, annotsObj.getTypeName()); goto err2; } // contents contents = pageObj.dictLookupNF("Contents").copy(); if (!(contents.isRef() || contents.isArray() || contents.isNull())) { error(errSyntaxError, -1, "Page contents object (page {0:d}) is wrong type ({1:s})", num, contents.getTypeName()); goto err1; } // thumb thumb = pageObj.dictLookupNF("Thumb").copy(); if (!(thumb.isStream() || thumb.isNull() || thumb.isRef())) { error(errSyntaxError, -1, "Page thumb object (page {0:d}) is wrong type ({1:s})", num, thumb.getTypeName()); thumb.setToNull(); } // actions actions = pageObj.dictLookupNF("AA").copy(); if (!(actions.isDict() || actions.isNull())) { error(errSyntaxError, -1, "Page additional action object (page {0:d}) is wrong type ({1:s})", num, actions.getTypeName()); actions.setToNull(); } return; err2: annotsObj.setToNull(); err1: contents.setToNull(); ok = false; } Page::~Page() { delete attrs; delete annots; for (auto frmField : standaloneFields) { delete frmField; } } Dict *Page::getResourceDict() { return attrs->getResourceDict(); } Object *Page::getResourceDictObject() { return attrs->getResourceDictObject(); } Dict *Page::getResourceDictCopy(XRef *xrefA) { pageLocker(); Dict *dict = attrs->getResourceDict(); return dict ? dict->copy(xrefA) : nullptr; } void Page::replaceXRef(XRef *xrefA) { Dict *pageDict = pageObj.getDict()->copy(xrefA); xref = xrefA; trans = pageDict->lookupNF("Trans").copy(); annotsObj = pageDict->lookupNF("Annots").copy(); contents = pageDict->lookupNF("Contents").copy(); if (contents.isArray()) { contents = Object(contents.getArray()->copy(xrefA)); } thumb = pageDict->lookupNF("Thumb").copy(); actions = pageDict->lookupNF("AA").copy(); Object resources = pageDict->lookup("Resources"); if (resources.isDict()) { attrs->replaceResource(std::move(resources)); } delete pageDict; } /* Loads standalone fields into Page, should be called once per page only */ void Page::loadStandaloneFields(Annots *annotations, Form *form) { /* Look for standalone annots, identified by being: 1) of type Widget * 2) not referenced from the Catalog's Form Field array */ for (Annot *annot : annots->getAnnots()) { if (annot->getType() != Annot::typeWidget || !annot->getHasRef()) { continue; } const Ref r = annot->getRef(); if (form && form->findWidgetByRef(r)) { continue; // this annot is referenced inside Form, skip it } std::set parents; FormField *field = Form::createFieldFromDict(annot->getAnnotObj().copy(), annot->getDoc(), r, nullptr, &parents); if (field && field->getNumWidgets() == 1) { static_cast(annot)->setField(field); field->setStandAlone(true); FormWidget *formWidget = field->getWidget(0); if (!formWidget->getWidgetAnnotation()) { formWidget->createWidgetAnnotation(); } standaloneFields.push_back(field); } else if (field) { delete field; } } } Annots *Page::getAnnots(XRef *xrefA) { if (!annots) { Object obj = getAnnotsObject(xrefA); annots = new Annots(doc, num, &obj); // Load standalone fields once for the page loadStandaloneFields(annots, doc->getCatalog()->getForm()); } return annots; } 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 // even if it's an empty list so that we can safely // call annots->appendAnnot(annot) pageLocker(); getAnnots(); if (annotsObj.isNull()) { Ref annotsRef; // page doesn't have annots array, // we have to create it Array *annotsArray = new Array(xref); annotsArray->add(Object(annotRef)); annotsRef = xref->addIndirectObject(Object(annotsArray)); annotsObj = Object(annotsRef); pageObj.dictSet("Annots", Object(annotsRef)); xref->setModifiedObject(&pageObj, pageRef); } else { Object obj1 = getAnnotsObject(); if (obj1.isArray()) { obj1.arrayAdd(Object(annotRef)); if (annotsObj.isRef()) { xref->setModifiedObject(&obj1, annotsObj.getRef()); } else { xref->setModifiedObject(&pageObj, pageRef); } } } // Popup annots are already handled by markup annots, // so add to the list only Popup annots without a // markup annotation associated. if (annot->getType() != Annot::typePopup || !static_cast(annot)->hasParent()) { annots->appendAnnot(annot); } annot->setPage(num, true); AnnotMarkup *annotMarkup = dynamic_cast(annot); if (annotMarkup) { AnnotPopup *annotPopup = annotMarkup->getPopup(); if (annotPopup) { addAnnot(annotPopup); } } return true; } void Page::removeAnnot(Annot *annot) { Ref annotRef = annot->getRef(); pageLocker(); Object annArray = getAnnotsObject(); if (annArray.isArray()) { int idx = -1; // Get annotation position for (int i = 0; idx == -1 && i < annArray.arrayGetLength(); ++i) { const Object &tmp = annArray.arrayGetNF(i); if (tmp.isRef()) { const Ref currAnnot = tmp.getRef(); if (currAnnot == annotRef) { idx = i; } } } if (idx == -1) { error(errInternal, -1, "Annotation doesn't belong to this page"); return; } annots->removeAnnot(annot); // Gracefully fails on popup windows annArray.arrayRemove(idx); if (annotsObj.isRef()) { xref->setModifiedObject(&annArray, annotsObj.getRef()); } else { xref->setModifiedObject(&pageObj, pageRef); } } annot->removeReferencedObjects(); // Note: Might recurse in removeAnnot again if (annArray.isArray()) { xref->removeIndirectObject(annotRef); } annot->setPage(0, false); } std::unique_ptr Page::getLinks() { return std::make_unique(getAnnots()); } std::unique_ptr Page::getFormWidgets() { auto frmPageWidgets = std::make_unique(getAnnots(), num, doc->getCatalog()->getForm()); frmPageWidgets->addWidgets(standaloneFields, num); return frmPageWidgets; } void Page::display(OutputDev *out, double hDPI, double vDPI, int rotate, bool useMediaBox, bool crop, bool printing, bool (*abortCheckCbk)(void *data), void *abortCheckCbkData, bool (*annotDisplayDecideCbk)(Annot *annot, void *user_data), void *annotDisplayDecideCbkData, bool copyXRef) { 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) { const PDFRectangle *mediaBox, *cropBox; PDFRectangle box; Gfx *gfx; rotate += getRotate(); if (rotate >= 360) { rotate -= 360; } else if (rotate < 0) { rotate += 360; } makeBox(hDPI, vDPI, rotate, useMediaBox, out->upsideDown(), sliceX, sliceY, sliceW, sliceH, &box, &crop); cropBox = getCropBox(); mediaBox = getMediaBox(); if (globalParams->getPrintCommands()) { printf("***** MediaBox = ll:%g,%g ur:%g,%g\n", mediaBox->x1, mediaBox->y1, mediaBox->x2, mediaBox->y2); printf("***** CropBox = ll:%g,%g ur:%g,%g\n", cropBox->x1, cropBox->y1, cropBox->x2, cropBox->y2); printf("***** Rotate = %d\n", attrs->getRotate()); } 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; } 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)) { return; } pageLocker(); XRef *localXRef = (copyXRef) ? xref->copy() : xref; if (copyXRef) { replaceXRef(localXRef); } gfx = createGfx(out, hDPI, vDPI, rotate, useMediaBox, crop, sliceX, sliceY, sliceW, sliceH, printing, abortCheckCbk, abortCheckCbkData, localXRef); Object obj = contents.fetch(localXRef); if (!obj.isNull()) { gfx->saveState(); gfx->display(&obj); gfx->restoreState(); } else { // empty pages need to call dump to do any setup required by the // OutputDev out->dump(); } // draw annotations annotList = getAnnots(); if (!annotList->getAnnots().empty()) { if (globalParams->getPrintCommands()) { printf("***** Annotations\n"); } for (Annot *annot : annots->getAnnots()) { if ((annotDisplayDecideCbk && (*annotDisplayDecideCbk)(annot, annotDisplayDecideCbkData)) || !annotDisplayDecideCbk) { annot->draw(gfx, printing); } } out->dump(); } delete gfx; if (copyXRef) { replaceXRef(doc->getXRef()); delete localXRef; } } void Page::display(Gfx *gfx) { Object obj = contents.fetch(xref); if (!obj.isNull()) { gfx->saveState(); gfx->display(&obj); gfx->restoreState(); } } bool Page::loadThumb(unsigned char **data_out, int *width_out, int *height_out, int *rowstride_out) { unsigned int pixbufdatasize; int width, height, bits; Object obj1; Dict *dict; GfxColorSpace *colorSpace; Stream *str; GfxImageColorMap *colorMap; /* Get stream dict */ pageLocker(); Object fetched_thumb = thumb.fetch(xref); if (!fetched_thumb.isStream()) { return false; } dict = fetched_thumb.streamGetDict(); str = fetched_thumb.getStream(); if (!dict->lookupInt("Width", "W", &width)) { return false; } if (!dict->lookupInt("Height", "H", &height)) { return false; } if (!dict->lookupInt("BitsPerComponent", "BPC", &bits)) { return false; } /* Check for invalid dimensions and integer overflow. */ if (width <= 0 || height <= 0) { return false; } if (width > INT_MAX / 3 / height) { return false; } pixbufdatasize = width * height * 3; /* Get color space */ obj1 = dict->lookup("ColorSpace"); if (obj1.isNull()) { obj1 = dict->lookup("CS"); } // Just initialize some dummy GfxState for GfxColorSpace::parse. // 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()); if (!colorSpace) { fprintf(stderr, "Error: Cannot parse color space\n"); return false; } obj1 = dict->lookup("Decode"); if (obj1.isNull()) { obj1 = dict->lookup("D"); } colorMap = new GfxImageColorMap(bits, &obj1, colorSpace); if (!colorMap->isOk()) { fprintf(stderr, "Error: invalid colormap\n"); delete colorMap; return false; } if (data_out) { 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); *p++ = colToByte(rgb.r); *p++ = colToByte(rgb.g); *p++ = colToByte(rgb.b); } } *data_out = pixbufdata; imgstr->close(); delete imgstr; } if (width_out) { *width_out = width; } if (height_out) { *height_out = height; } if (rowstride_out) { *rowstride_out = width * 3; } delete colorMap; return true; } void Page::makeBox(double hDPI, double vDPI, int rotate, bool useMediaBox, bool upsideDown, double sliceX, double sliceY, double sliceW, double sliceH, PDFRectangle *box, bool *crop) { const PDFRectangle *mediaBox, *cropBox, *baseBox; double kx, ky; mediaBox = getMediaBox(); cropBox = getCropBox(); if (sliceW >= 0 && sliceH >= 0) { baseBox = useMediaBox ? mediaBox : cropBox; kx = 72.0 / hDPI; ky = 72.0 / vDPI; if (rotate == 90) { if (upsideDown) { box->x1 = baseBox->x1 + ky * sliceY; box->x2 = baseBox->x1 + ky * (sliceY + sliceH); } else { box->x1 = baseBox->x2 - ky * (sliceY + sliceH); box->x2 = baseBox->x2 - ky * sliceY; } box->y1 = baseBox->y1 + kx * sliceX; box->y2 = baseBox->y1 + kx * (sliceX + sliceW); } else if (rotate == 180) { box->x1 = baseBox->x2 - kx * (sliceX + sliceW); box->x2 = baseBox->x2 - kx * sliceX; if (upsideDown) { box->y1 = baseBox->y1 + ky * sliceY; box->y2 = baseBox->y1 + ky * (sliceY + sliceH); } else { box->y1 = baseBox->y2 - ky * (sliceY + sliceH); box->y2 = baseBox->y2 - ky * sliceY; } } else if (rotate == 270) { if (upsideDown) { box->x1 = baseBox->x2 - ky * (sliceY + sliceH); box->x2 = baseBox->x2 - ky * sliceY; } else { box->x1 = baseBox->x1 + ky * sliceY; box->x2 = baseBox->x1 + ky * (sliceY + sliceH); } box->y1 = baseBox->y2 - kx * (sliceX + sliceW); box->y2 = baseBox->y2 - kx * sliceX; } else { box->x1 = baseBox->x1 + kx * sliceX; box->x2 = baseBox->x1 + kx * (sliceX + sliceW); if (upsideDown) { box->y1 = baseBox->y2 - ky * (sliceY + sliceH); box->y2 = baseBox->y2 - ky * sliceY; } else { box->y1 = baseBox->y1 + ky * sliceY; box->y2 = baseBox->y1 + ky * (sliceY + sliceH); } } } else if (useMediaBox) { *box = *mediaBox; } else { *box = *cropBox; *crop = false; } } void Page::processLinks(OutputDev *out) { std::unique_ptr links = getLinks(); for (AnnotLink *link : links->getLinks()) { out->processLink(link); } } void Page::getDefaultCTM(double *ctm, double hDPI, double vDPI, int rotate, bool useMediaBox, bool upsideDown) { GfxState *state; int i; rotate += getRotate(); if (rotate >= 360) { rotate -= 360; } else if (rotate < 0) { rotate += 360; } state = new GfxState(hDPI, vDPI, useMediaBox ? getMediaBox() : getCropBox(), rotate, upsideDown); for (i = 0; i < 6; ++i) { ctm[i] = state->getCTM()[i]; } delete state; } std::unique_ptr Page::getAdditionalAction(PageAdditionalActionsType type) { Object additionalActionsObject = actions.fetch(doc->getXRef()); if (additionalActionsObject.isDict()) { const char *key = (type == actionOpenPage ? "O" : type == actionClosePage ? "C" : nullptr); Object actionObject = additionalActionsObject.dictLookup(key); if (actionObject.isDict()) { return LinkAction::parseAction(&actionObject, doc->getCatalog()->getBaseURI()); } } return nullptr; } poppler-24.02.0/poppler/Page.h000066400000000000000000000256101455701731300160600ustar00rootroot00000000000000//======================================================================== // // Page.h // // 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) 2005 Kristian Høgsberg // Copyright (C) 2005 Jeff Muizelaar // Copyright (C) 2006 Pino Toscano // Copyright (C) 2006, 2011 Carlos Garcia Campos // 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, 2023 Albert Astals Cid // Copyright (C) 2013 Thomas Freitag // 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 // // 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 PAGE_H #define PAGE_H #include #include #include "poppler-config.h" #include "Object.h" #include "poppler_private_export.h" class Dict; class PDFDoc; class XRef; class OutputDev; class Links; class LinkAction; class Annots; class Annot; class Gfx; class FormPageWidgets; class Form; class FormField; //------------------------------------------------------------------------ class PDFRectangle { public: double x1, y1, x2, y2; PDFRectangle() { x1 = y1 = x2 = y2 = 0; } PDFRectangle(double x1A, double y1A, double x2A, double y2A) { x1 = x1A; y1 = y1A; x2 = x2A; y2 = y2A; } bool isValid() const { return x1 != 0 || y1 != 0 || x2 != 0 || y2 != 0; } bool contains(double x, double y) const { return x1 <= x && x <= x2 && y1 <= y && y <= y2; } void clipTo(PDFRectangle *rect); bool operator==(const PDFRectangle &rect) const { return x1 == rect.x1 && y1 == rect.y1 && x2 == rect.x2 && y2 == rect.y2; } }; //------------------------------------------------------------------------ // PageAttrs //------------------------------------------------------------------------ class PageAttrs { public: // 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); // Destructor. ~PageAttrs(); // Accessors. const PDFRectangle *getMediaBox() const { return &mediaBox; } const PDFRectangle *getCropBox() const { return &cropBox; } bool isCropped() const { return haveCropBox; } const PDFRectangle *getBleedBox() const { return &bleedBox; } const PDFRectangle *getTrimBox() const { return &trimBox; } const PDFRectangle *getArtBox() const { return &artBox; } int getRotate() const { return rotate; } const GooString *getLastModified() const { return lastModified.isString() ? lastModified.getString() : nullptr; } Dict *getBoxColorInfo() { return boxColorInfo.isDict() ? boxColorInfo.getDict() : nullptr; } Dict *getGroup() { return group.isDict() ? group.getDict() : nullptr; } Stream *getMetadata() { return metadata.isStream() ? metadata.getStream() : nullptr; } Dict *getPieceInfo() { return pieceInfo.isDict() ? pieceInfo.getDict() : nullptr; } Dict *getSeparationInfo() { return separationInfo.isDict() ? separationInfo.getDict() : nullptr; } Dict *getResourceDict() { return resources.isDict() ? resources.getDict() : nullptr; } Object *getResourceDictObject() { return &resources; } void replaceResource(Object &&obj1) { resources = std::move(obj1); } // Clip all other boxes to the MediaBox. void clipBoxes(); private: bool readBox(Dict *dict, const char *key, PDFRectangle *box); PDFRectangle mediaBox; PDFRectangle cropBox; bool haveCropBox; PDFRectangle bleedBox; PDFRectangle trimBox; PDFRectangle artBox; int rotate; Object lastModified; Object boxColorInfo; Object group; Object metadata; Object pieceInfo; Object separationInfo; Object resources; }; //------------------------------------------------------------------------ // Page //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT Page { public: // Constructor. Page(PDFDoc *docA, int numA, Object &&pageDict, Ref pageRefA, PageAttrs *attrsA, Form *form); // Destructor. ~Page(); Page(const Page &) = delete; Page &operator=(const Page &) = delete; // Is page valid? bool isOk() const { return ok; } // Get page parameters. int getNum() const { return num; } const PDFRectangle *getMediaBox() const { return attrs->getMediaBox(); } const PDFRectangle *getCropBox() const { return attrs->getCropBox(); } bool isCropped() const { return attrs->isCropped(); } double getMediaWidth() const { return attrs->getMediaBox()->x2 - attrs->getMediaBox()->x1; } double getMediaHeight() const { return attrs->getMediaBox()->y2 - attrs->getMediaBox()->y1; } double getCropWidth() const { return attrs->getCropBox()->x2 - attrs->getCropBox()->x1; } double getCropHeight() const { return attrs->getCropBox()->y2 - attrs->getCropBox()->y1; } const PDFRectangle *getBleedBox() const { return attrs->getBleedBox(); } const PDFRectangle *getTrimBox() const { return attrs->getTrimBox(); } const PDFRectangle *getArtBox() const { return attrs->getArtBox(); } int getRotate() const { return attrs->getRotate(); } const GooString *getLastModified() const { return attrs->getLastModified(); } Dict *getBoxColorInfo() { return attrs->getBoxColorInfo(); } Dict *getGroup() { return attrs->getGroup(); } Stream *getMetadata() { return attrs->getMetadata(); } Dict *getPieceInfo() { return attrs->getPieceInfo(); } Dict *getSeparationInfo() { return attrs->getSeparationInfo(); } PDFDoc *getDoc() { return doc; } Ref getRef() { return pageRef; } // Get resource dictionary. Dict *getResourceDict(); Object *getResourceDictObject(); Dict *getResourceDictCopy(XRef *xrefA); // Get annotations array. Object getAnnotsObject(XRef *xrefA = nullptr) { return annotsObj.fetch(xrefA ? xrefA : xref); } // Add a new annotation to the page bool addAnnot(Annot *annot); // Remove an existing annotation from the page void removeAnnot(Annot *annot); // Return a list of links. std::unique_ptr getLinks(); // Return a list of annots. It will be valid until the page is destroyed Annots *getAnnots(XRef *xrefA = nullptr); // Get contents. Object getContents() { return contents.fetch(xref); } // Get thumb. Object getThumb() { return thumb.fetch(xref); } bool loadThumb(unsigned char **data, int *width, int *height, int *rowstride); // Get transition. Object getTrans() { return trans.fetch(xref); } // Get form. std::unique_ptr getFormWidgets(); // Get duration, the maximum length of time, in seconds, // that the page is displayed before the presentation automatically // advances to the next page double getDuration() { return duration; } // Get actions Object getActions() { return actions.fetch(xref); } enum PageAdditionalActionsType { actionOpenPage, ///< Performed when opening the page actionClosePage, ///< Performed when closing the 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); // 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, bool (*annotDisplayDecideCbk)(Annot *annot, void *user_data) = nullptr, void *annotDisplayDecideCbkData = nullptr, bool copyXRef = false); // Display part of a page. void 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) = nullptr, void *abortCheckCbkData = nullptr, bool (*annotDisplayDecideCbk)(Annot *annot, void *user_data) = nullptr, void *annotDisplayDecideCbkData = nullptr, bool copyXRef = false); void display(Gfx *gfx); void makeBox(double hDPI, double vDPI, int rotate, bool useMediaBox, bool upsideDown, double sliceX, double sliceY, double sliceW, double sliceH, PDFRectangle *box, bool *crop); void processLinks(OutputDev *out); // Get the page's default CTM. void getDefaultCTM(double *ctm, double hDPI, double vDPI, int rotate, bool useMediaBox, bool upsideDown); 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); PDFDoc *doc; XRef *xref; // the xref table for this PDF file Object pageObj; // page dictionary const Ref pageRef; // page reference int num; // page number PageAttrs *attrs; // page attributes Annots *annots; // annotations Object annotsObj; // annotations array Object contents; // page contents Object thumb; // page thumbnail 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* // referenced from the Catalog's Field array. That means they are standlone, // 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; void loadStandaloneFields(Annots *annotations, Form *form); }; #endif poppler-24.02.0/poppler/PageLabelInfo.cc000066400000000000000000000154231455701731300177730ustar00rootroot00000000000000//======================================================================== // // 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, 2023 Albert Astals Cid // Copyright (C) 2011 Simon Kellner // Copyright (C) 2012 Fabio D'Urso // Copyright (C) 2018 Adam Reichold // // 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 #include #include #include #include #include #include "PageLabelInfo.h" #include "PageLabelInfo_p.h" PageLabelInfo::Interval::Interval(Object *dict, int baseA) { style = None; Object obj = dict->dictLookup("S"); if (obj.isName()) { if (obj.isName("D")) { style = Arabic; } else if (obj.isName("R")) { style = UppercaseRoman; } else if (obj.isName("r")) { style = LowercaseRoman; } else if (obj.isName("A")) { style = UppercaseLatin; } else if (obj.isName("a")) { style = LowercaseLatin; } } obj = dict->dictLookup("P"); if (obj.isString()) { const auto str = obj.getString(); prefix.assign(str->c_str(), str->getLength()); } obj = dict->dictLookup("St"); if (obj.isInt()) { first = obj.getInt(); } else { first = 1; } base = baseA; } PageLabelInfo::PageLabelInfo(Object *tree, int numPages) { RefRecursionChecker alreadyParsedRefs; parse(tree, alreadyParsedRefs); if (intervals.empty()) { return; } auto curr = intervals.begin(); for (auto next = curr + 1; next != intervals.end(); ++next, ++curr) { curr->length = std::max(0, next->base - curr->base); } curr->length = std::max(0, numPages - curr->base); } void PageLabelInfo::parse(const Object *tree, RefRecursionChecker &alreadyParsedRefs) { // leaf node Object nums = tree->dictLookup("Nums"); if (nums.isArray()) { for (int i = 0; i < nums.arrayGetLength(); i += 2) { Object obj = nums.arrayGet(i); if (!obj.isInt()) { continue; } const int base = obj.getInt(); if (base < 0) { continue; } obj = nums.arrayGet(i + 1); if (!obj.isDict()) { continue; } intervals.emplace_back(&obj, base); } } Object kids = tree->dictLookup("Kids"); if (kids.isArray()) { const Array *kidsArray = kids.getArray(); for (int i = 0; i < kidsArray->getLength(); ++i) { Ref ref; const Object kid = kidsArray->get(i, &ref); if (!alreadyParsedRefs.insert(ref)) { error(errSyntaxError, -1, "loop in PageLabelInfo (ref.num: {0:d})", ref.num); continue; } if (kid.isDict()) { parse(&kid, alreadyParsedRefs); } } } } 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(); int number; bool ok; for (const auto &interval : intervals) { const std::size_t prefixLen = interval.prefix.size(); if (strLen < prefixLen || interval.prefix.compare(0, prefixLen, str, prefixLen) != 0) { continue; } switch (interval.style) { case Interval::Arabic: std::tie(number, ok) = fromDecimal(label->toStr().substr(prefixLen), strUnicode); if (ok && number - interval.first < interval.length) { *index = interval.base + number - interval.first; return true; } break; case Interval::LowercaseRoman: case Interval::UppercaseRoman: number = fromRoman(str + prefixLen); if (number >= 0 && number - interval.first < interval.length) { *index = interval.base + number - interval.first; return true; } break; case Interval::UppercaseLatin: case Interval::LowercaseLatin: number = fromLatin(str + prefixLen); if (number >= 0 && number - interval.first < interval.length) { *index = interval.base + number - interval.first; return true; } break; case Interval::None: if (interval.length == 1 && label->toStr() == interval.prefix) { *index = interval.base; return true; } else { error(errSyntaxError, -1, "asking to convert label to page index in an unknown scenario, report a bug"); } break; } } return false; } bool PageLabelInfo::indexToLabel(int index, GooString *label) const { char buffer[32]; int base, number; const Interval *matching_interval; GooString number_string; base = 0; matching_interval = nullptr; for (const auto &interval : intervals) { if (base <= index && index < base + interval.length) { matching_interval = &interval; break; } base += interval.length; } if (!matching_interval) { return false; } number = index - base + matching_interval->first; switch (matching_interval->style) { case Interval::Arabic: snprintf(buffer, sizeof(buffer), "%d", number); number_string.append(buffer); break; case Interval::LowercaseRoman: toRoman(number, &number_string, false); break; case Interval::UppercaseRoman: toRoman(number, &number_string, true); break; case Interval::LowercaseLatin: toLatin(number, &number_string, false); break; case Interval::UppercaseLatin: toLatin(number, &number_string, true); break; case Interval::None: break; } label->clear(); label->append(matching_interval->prefix.c_str(), matching_interval->prefix.size()); if (label->hasUnicodeMarker()) { int i, len; char ucs2_char[2]; /* Convert the ascii number string to ucs2 and append. */ len = number_string.getLength(); ucs2_char[0] = 0; for (i = 0; i < len; ++i) { ucs2_char[1] = number_string.getChar(i); label->append(ucs2_char, 2); } } else { label->append(&number_string); } return true; } poppler-24.02.0/poppler/PageLabelInfo.h000066400000000000000000000031201455701731300176240ustar00rootroot00000000000000//======================================================================== // // This file is under the GPLv2 or later license // // Copyright (C) 2005-2006 Kristian Høgsberg // Copyright (C) 2005, 2018-2020, 2023 Albert Astals Cid // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019 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 // //======================================================================== #ifndef PAGELABELINFO_H #define PAGELABELINFO_H #include #include #include #include #include #include #include #include "Object.h" class PageLabelInfo { public: PageLabelInfo(Object *tree, int numPages); PageLabelInfo(const PageLabelInfo &) = delete; PageLabelInfo &operator=(const PageLabelInfo &) = delete; bool labelToIndex(GooString *label, int *index) const; bool indexToLabel(int index, GooString *label) const; private: void parse(const Object *tree, RefRecursionChecker &parsedRefs); private: struct Interval { Interval(Object *dict, int baseA); std::string prefix; enum NumberStyle { None, Arabic, LowercaseRoman, UppercaseRoman, UppercaseLatin, LowercaseLatin } style; int first, base, length; }; std::vector intervals; }; #endif poppler-24.02.0/poppler/PageLabelInfo_p.h000066400000000000000000000124451455701731300201550ustar00rootroot00000000000000//======================================================================== // // This file is under the GPLv2 or later license // // Copyright (C) 2005-2006 Kristian Høgsberg // Copyright (C) 2005, 2009, 2014, 2019, 2020, 2022 Albert Astals Cid // Copyright (C) 2011 Simon Kellner // Copyright (C) 2012 Fabio D'Urso // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019 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 // //======================================================================== #ifndef PAGELABELINFO_P_H #define PAGELABELINFO_P_H /* http://mathworld.wolfram.com/RomanNumerals.html */ #include "config.h" #include "goo/GooString.h" #include "Error.h" static std::pair fromDecimal(const std::string &str, const bool unicode) { if (unicode && (str.size() % 2 == 0)) { if (GooString::hasUnicodeMarker(str)) { // strip the marker if it is there return fromDecimal(str.substr(2), true /*unicode*/); } // Since we only care about numbers here, the first byte needs to be // 0 and second will be the actual ascii number, so we're going to reconstruct a // non unicode string that then we will use strtol to "translate" std::string newString; bool allGood = true; for (size_t i = 0; allGood && i < str.size(); i += 2) { if (str[i] == 0) { newString += str[i + 1]; } else { allGood = false; } } if (allGood) { return fromDecimal(newString, false /*unicode*/); } } const char *const begin = str.data(); const char *const end = begin + str.size(); char *parsed; const int number = std::strtol(begin, &parsed, 10); return std::make_pair(number, parsed >= end); } static int fromRoman(const char *buffer) { int digit_value, prev_digit_value, value; int i; prev_digit_value = INT_MAX; value = 0; for (i = 0; buffer[i] != '\0'; i++) { switch (buffer[i]) { case 'm': case 'M': digit_value = 1000; break; case 'd': case 'D': digit_value = 500; break; case 'c': case 'C': digit_value = 100; break; case 'l': case 'L': digit_value = 50; break; case 'x': case 'X': digit_value = 10; break; case 'v': case 'V': digit_value = 5; break; case 'i': case 'I': digit_value = 1; break; default: return -1; } if (digit_value <= prev_digit_value) { value += digit_value; } else { value += digit_value - prev_digit_value * 2; } prev_digit_value = digit_value; } return value; } static void toRoman(int number, GooString *str, bool uppercase) { static const char uppercaseNumerals[] = "IVXLCDM"; static const char lowercaseNumerals[] = "ivxlcdm"; int divisor; int i, j, k; const char *wh; if (number >= 4000) { error(errUnimplemented, -1, "Conversion to roman numerals of numbers >= 4000 not implemented"); return; } if (uppercase) { wh = uppercaseNumerals; } else { wh = lowercaseNumerals; } divisor = 1000; for (k = 3; k >= 0; k--) { i = number / divisor; number = number % divisor; switch (i) { case 0: break; case 5: str->append(wh[2 * k + 1]); break; case 9: str->append(wh[2 * k + 0]); str->append(wh[2 * k + 2]); break; case 4: str->append(wh[2 * k + 0]); str->append(wh[2 * k + 1]); break; default: if (i > 5) { str->append(wh[2 * k + 1]); i -= 5; } for (j = 0; j < i; j++) { str->append(wh[2 * k + 0]); } } divisor = divisor / 10; } } static int fromLatin(const char *buffer) { const char *p; for (p = buffer; *p; p++) { if (*p != buffer[0]) { return -1; } } const intptr_t diff = p - buffer; if (unlikely(diff > std::numeric_limits::max() / 100)) { error(errUnimplemented, -1, "Something went wrong in fromLatin conversion"); return -1; } const int count = static_cast(diff); if (buffer[0] >= 'a' && buffer[0] <= 'z') { return 26 * (count - 1) + buffer[0] - 'a' + 1; } if (buffer[0] >= 'A' && buffer[0] <= 'Z') { return 26 * (count - 1) + buffer[0] - 'A' + 1; } return -1; } static void toLatin(int number, GooString *str, bool uppercase) { char base, letter; int i, count; if (uppercase) { base = 'A'; } else { base = 'a'; } count = (number - 1) / 26 + 1; letter = base + (number - 1) % 26; for (i = 0; i < count; i++) { str->append(letter); } } #endif poppler-24.02.0/poppler/PageTransition.cc000066400000000000000000000074061455701731300202740ustar00rootroot00000000000000/* PageTransition.cc * Copyright (C) 2005, Net Integration Technologies, Inc. * Copyright (C) 2010, 2017, 2020, Albert Astals Cid * Copyright (C) 2013 Adrian Johnson * Copyright (C) 2015, Arseniy Lartsev * * 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 "PageTransition.h" //------------------------------------------------------------------------ // PageTransition //------------------------------------------------------------------------ PageTransition::PageTransition(Object *trans) { Object obj; Dict *dict; type = transitionReplace; duration = 1; alignment = transitionHorizontal; direction = transitionInward; angle = 0; scale = 1.0; ok = true; if (!trans || !trans->isDict()) { ok = false; return; } dict = trans->getDict(); // get type obj = dict->lookup("S"); if (obj.isName()) { const char *s = obj.getName(); if (strcmp("R", s) == 0) { type = transitionReplace; } else if (strcmp("Split", s) == 0) { type = transitionSplit; } else if (strcmp("Blinds", s) == 0) { type = transitionBlinds; } else if (strcmp("Box", s) == 0) { type = transitionBox; } else if (strcmp("Wipe", s) == 0) { type = transitionWipe; } else if (strcmp("Dissolve", s) == 0) { type = transitionDissolve; } else if (strcmp("Glitter", s) == 0) { type = transitionGlitter; } else if (strcmp("Fly", s) == 0) { type = transitionFly; } else if (strcmp("Push", s) == 0) { type = transitionPush; } else if (strcmp("Cover", s) == 0) { type = transitionCover; } else if (strcmp("Uncover", s) == 0) { type = transitionUncover; } else if (strcmp("Fade", s) == 0) { type = transitionFade; } } // get duration obj = dict->lookup("D"); if (obj.isNum()) { duration = obj.getNum(); } // get alignment obj = dict->lookup("Dm"); if (obj.isName()) { const char *dm = obj.getName(); if (strcmp("H", dm) == 0) { alignment = transitionHorizontal; } else if (strcmp("V", dm) == 0) { alignment = transitionVertical; } } // get direction obj = dict->lookup("M"); if (obj.isName()) { const char *m = obj.getName(); if (strcmp("I", m) == 0) { direction = transitionInward; } else if (strcmp("O", m) == 0) { direction = transitionOutward; } } // get angle obj = dict->lookup("Di"); if (obj.isInt()) { angle = obj.getInt(); } obj = dict->lookup("Di"); if (obj.isName()) { if (strcmp("None", obj.getName()) == 0) { angle = 0; } } // get scale obj = dict->lookup("SS"); if (obj.isNum()) { scale = obj.getNum(); } // get rectangular rectangular = dict->lookup("B").getBoolWithDefaultValue(false); } PageTransition::~PageTransition() { } poppler-24.02.0/poppler/PageTransition.h000066400000000000000000000057641455701731300201430ustar00rootroot00000000000000/* PageTransition.cc * Copyright (C) 2005, Net Integration Technologies, Inc. * Copyright (C) 2015, Arseniy Lartsev * Copyright (C) 2019, 2021, 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 * 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 PAGE_TRANSITION_H #define PAGE_TRANSITION_H #include "Object.h" //------------------------------------------------------------------------ // PageTransition //------------------------------------------------------------------------ // if changed remember to keep in sync with frontend enums enum PageTransitionType { transitionReplace = 0, transitionSplit, transitionBlinds, transitionBox, transitionWipe, transitionDissolve, transitionGlitter, transitionFly, transitionPush, transitionCover, transitionUncover, transitionFade }; // if changed remember to keep in sync with frontend enums enum PageTransitionAlignment { transitionHorizontal = 0, transitionVertical }; // if changed remember to keep in sync with frontend enums enum PageTransitionDirection { transitionInward = 0, transitionOutward }; class POPPLER_PRIVATE_EXPORT PageTransition { public: // Construct a Page Transition. explicit PageTransition(Object *trans); // Destructor. ~PageTransition(); // Was the Page Transition created successfully? bool isOk() const { return ok; } // Get type PageTransitionType getType() const { return type; } // Get duration double getDuration() const { return duration; } // Get alignment PageTransitionAlignment getAlignment() const { return alignment; } // Get direction PageTransitionDirection getDirection() const { return direction; } // Get angle int getAngle() const { return angle; } // Get scale double getScale() const { return scale; } // Is rectangular? bool isRectangular() const { return rectangular; } private: PageTransitionType type; // transition style double duration; // duration of the effect in seconds PageTransitionAlignment alignment; // dimension of the effect PageTransitionDirection direction; // direction of motion int angle; // direction in degrees double scale; // scale bool rectangular; // is the area to be flown in rectangular? bool ok; // set if created successfully }; #endif /* PAGE_TRANSITION_H */ poppler-24.02.0/poppler/Parser.cc000066400000000000000000000311601455701731300165730ustar00rootroot00000000000000//======================================================================== // // Parser.cc // // 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, 2009, 201, 2010, 2013, 2014, 2017-2020 Albert Astals Cid // Copyright (C) 2006 Krzysztof Kowalczyk // Copyright (C) 2009 Ilya Gorenbein // Copyright (C) 2012 Hib Eris // Copyright (C) 2013 Adrian Johnson // 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) 2018, 2019 Adam Reichold // Copyright (C) 2018 Marek Kasik // // 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 #include #include "Object.h" #include "Array.h" #include "Dict.h" #include "Decrypt.h" #include "Parser.h" #include "XRef.h" #include "Error.h" // Max number of nested objects. This is used to catch infinite loops // in the object structure. And also technically valid files with // lots of nested arrays that made us consume all the stack #define recursionLimit 500 Parser::Parser(XRef *xrefA, Stream *streamA, bool allowStreamsA) : lexer { xrefA, streamA } { allowStreams = allowStreamsA; buf1 = lexer.getObj(); buf2 = lexer.getObj(); inlineImg = 0; } Parser::Parser(XRef *xrefA, Object *objectA, bool allowStreamsA) : lexer { xrefA, objectA } { allowStreams = allowStreamsA; buf1 = lexer.getObj(); buf2 = lexer.getObj(); inlineImg = 0; } Parser::~Parser() = default; Object Parser::getObj(int recursion) { return getObj(false, nullptr, cryptRC4, 0, 0, 0, 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(); std::unique_ptr res = std::make_unique(); int c; while ((c = decrypt.getChar()) != EOF) { res->append((char)c); } return res; } Object Parser::getObj(bool simpleOnly, const unsigned char *fileKey, CryptAlgorithm encAlgorithm, int keyLength, int objNum, int objGen, int recursion, bool strict, bool decryptString) { Object obj; Stream *str; // refill buffer after inline image data if (inlineImg == 2) { buf1 = lexer.getObj(); buf2 = lexer.getObj(); inlineImg = 0; } if (unlikely(recursion >= recursionLimit)) { return Object(objError); } // array if (!simpleOnly && buf1.isCmd("[")) { shift(); obj = Object(new Array(lexer.getXRef())); while (!buf1.isCmd("]") && !buf1.isEOF() && recursion + 1 < recursionLimit) { Object obj2 = getObj(false, fileKey, encAlgorithm, keyLength, objNum, objGen, recursion + 1); obj.arrayAdd(std::move(obj2)); } if (recursion + 1 >= recursionLimit && strict) { goto err; } if (buf1.isEOF()) { error(errSyntaxError, getPos(), "End of file inside array"); if (strict) { goto err; } } shift(); // dictionary or stream } else if (!simpleOnly && buf1.isCmd("<<")) { shift(objNum); obj = Object(new Dict(lexer.getXRef())); bool hasContentsEntry = false; while (!buf1.isCmd(">>") && !buf1.isEOF()) { if (!buf1.isName()) { error(errSyntaxError, getPos(), "Dictionary key must be a name object"); if (strict) { goto err; } shift(); } else { // buf1 will go away in shift(), so keep the key const auto key = std::move(buf1); shift(); if (buf1.isEOF() || buf1.isError()) { if (strict && buf1.isError()) { goto err; } break; } // We don't decrypt strings that are the value of "Contents" key entries. We decrypt them if needed a few lines below. // The "Contents" field of Sig dictionaries is not encrypted, but we can't know the type of the dictionary here yet // so we don't decrypt any Contents and if later we find it's not a Sig dictionary we decrypt it 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)) { break; } obj.dictAdd(key.getName(), std::move(obj2)); } } if (buf1.isEOF()) { error(errSyntaxError, getPos(), "End of file inside dictionary"); if (strict) { goto err; } } if (fileKey && hasContentsEntry) { Dict *dict = obj.getDict(); const bool isSigDict = dict->is("Sig"); if (!isSigDict) { 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())); } } } // stream objects are not allowed inside content streams or // object streams if (buf2.isCmd("stream")) { if (allowStreams && (str = makeStream(std::move(obj), fileKey, encAlgorithm, keyLength, objNum, objGen, recursion + 1, strict))) { return Object(str); } else { return Object(objError); } } else { shift(); } // indirect reference or integer } else if (buf1.isInt()) { const int num = buf1.getInt(); shift(); if (buf1.isInt() && buf2.isCmd("R")) { const int gen = buf1.getInt(); shift(); shift(); if (unlikely(num <= 0 || gen < 0)) { return Object(); } Ref r; r.num = num; r.gen = gen; return Object(r); } else { return Object(num); } // string } else if (decryptString && buf1.isString() && fileKey) { std::unique_ptr s2 = decryptedString(buf1.getString(), fileKey, encAlgorithm, keyLength, objNum, objGen); obj = Object(s2.release()); shift(); // simple object } else { // avoid re-allocating memory for complex objects like strings by // shallow copy of to and nulling so that // subsequent buf1.free() won't free this memory obj = std::move(buf1); shift(); } return obj; err: return Object(objError); } Stream *Parser::makeStream(Object &&dict, const unsigned char *fileKey, CryptAlgorithm encAlgorithm, int keyLength, int objNum, int objGen, int recursion, bool strict) { BaseStream *baseStr; Stream *str; Goffset length; Goffset pos, endPos; if (XRef *xref = lexer.getXRef()) { XRefEntry *entry = xref->getEntry(objNum, false); if (entry) { if (!entry->getFlag(XRefEntry::Parsing) || (objNum == 0 && objGen == 0)) { entry->setFlag(XRefEntry::Parsing, true); } else { error(errSyntaxError, getPos(), "Object '{0:d} {1:d} obj' is being already parsed", objNum, objGen); return nullptr; } } } // get stream start position lexer.skipToNextLine(); if (!(str = lexer.getStream())) { return nullptr; } pos = str->getPos(); // get length Object obj = dict.dictLookup("Length", recursion); if (obj.isInt()) { length = obj.getInt(); } else if (obj.isInt64()) { length = obj.getInt64(); } else { error(errSyntaxError, getPos(), "Bad 'Length' attribute in stream"); if (strict) { return nullptr; } length = 0; } // check for length in damaged file if (lexer.hasXRef() && lexer.getXRef()->getStreamEnd(pos, &endPos)) { length = endPos - pos; } // in badly damaged PDF files, we can run off the end of the input // stream immediately after the "stream" token if (!lexer.getStream()) { return nullptr; } baseStr = lexer.getStream()->getBaseStream(); // skip over stream data if (Lexer::LOOK_VALUE_NOT_CACHED != lexer.lookCharLastValueCached) { // take into account the fact that we've cached one value pos = pos - 1; lexer.lookCharLastValueCached = Lexer::LOOK_VALUE_NOT_CACHED; } if (unlikely(length < 0)) { return nullptr; } if (unlikely(pos > LLONG_MAX - length)) { return nullptr; } lexer.setPos(pos + length); // refill token buffers and check for 'endstream' shift(); // kill '>>' shift("endstream", objNum); // kill 'stream' if (buf1.isCmd("endstream")) { shift(); } else { error(errSyntaxError, getPos(), "Missing 'endstream' or incorrect stream length"); if (strict) { return nullptr; } if (lexer.hasXRef() && lexer.getStream()) { // shift until we find the proper endstream or we change to another object or reach eof length = lexer.getPos() - pos; if (buf1.isCmd("endstream")) { dict.dictSet("Length", Object(length)); } } else { // When building the xref we can't use it so use this // kludge for broken PDF files: just add 5k to the length, and // hope its enough if (length < LLONG_MAX - pos - 5000) { length += 5000; } } } // make base stream str = baseStr->makeSubStream(pos, true, length, std::move(dict)); // handle decryption if (fileKey) { str = new DecryptStream(str, fileKey, encAlgorithm, keyLength, { objNum, objGen }); } // get filters str = str->addFilters(str->getDict(), recursion); if (XRef *xref = lexer.getXRef()) { // Don't try to reuse the entry from the block at the start // of the function, xref can change in the middle because of // reconstruction XRefEntry *entry = xref->getEntry(objNum, false); if (entry) { entry->setFlag(XRefEntry::Parsing, false); } } return str; } void Parser::shift(int objNum) { if (inlineImg > 0) { if (inlineImg < 2) { ++inlineImg; } else { // in a damaged content stream, if 'ID' shows up in the middle // of a dictionary, we need to reset inlineImg = 0; } } else if (buf2.isCmd("ID")) { lexer.skipChar(); // skip char after 'ID' command inlineImg = 1; } buf1 = std::move(buf2); if (inlineImg > 0) { // don't buffer inline image data buf2.setToNull(); } else { buf2 = lexer.getObj(objNum); } } void Parser::shift(const char *cmdA, int objNum) { if (inlineImg > 0) { if (inlineImg < 2) { ++inlineImg; } else { // in a damaged content stream, if 'ID' shows up in the middle // of a dictionary, we need to reset inlineImg = 0; } } else if (buf2.isCmd("ID")) { lexer.skipChar(); // skip char after 'ID' command inlineImg = 1; } buf1 = std::move(buf2); if (inlineImg > 0) { buf2.setToNull(); } else if (buf1.isCmd(cmdA)) { buf2 = lexer.getObj(objNum); } else { buf2 = lexer.getObj(cmdA, objNum); } } poppler-24.02.0/poppler/Parser.h000066400000000000000000000051611455701731300164370ustar00rootroot00000000000000//======================================================================== // // Parser.h // // 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, 2010, 2013, 2017, 2018, 2020 Albert Astals Cid // Copyright (C) 2012 Hib Eris // Copyright (C) 2013 Adrian Johnson // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2019 Adam Reichold // // 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 PARSER_H #define PARSER_H #include "Lexer.h" //------------------------------------------------------------------------ // Parser //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT Parser { public: // Constructor. Parser(XRef *xrefA, Stream *streamA, bool allowStreamsA); Parser(XRef *xrefA, Object *objectA, bool allowStreamsA); // Destructor. ~Parser(); Parser(const Parser &) = delete; Parser &operator=(const Parser &) = delete; // Get the next object from the input stream. If is // true, do not parse compound objects (arrays, dictionaries, or // streams). Object getObj(bool simpleOnly = false, const unsigned char *fileKey = nullptr, CryptAlgorithm encAlgorithm = cryptRC4, int keyLength = 0, int objNum = 0, int objGen = 0, int recursion = 0, bool strict = false, bool decryptString = true); Object getObj(int recursion); template Object getObj(T) = delete; // Get stream. Stream *getStream() { return lexer.getStream(); } // Get current position in file. Goffset getPos() { return lexer.getPos(); } private: Lexer lexer; // input stream bool allowStreams; // parse stream objects? Object buf1, buf2; // next two tokens int inlineImg; // set when inline image data is encountered Stream *makeStream(Object &&dict, const unsigned char *fileKey, CryptAlgorithm encAlgorithm, int keyLength, int objNum, int objGen, int recursion, bool strict); void shift(int objNum = -1); void shift(const char *cmdA, int objNum); }; #endif poppler-24.02.0/poppler/PopplerCache.h000066400000000000000000000033561455701731300175540ustar00rootroot00000000000000//======================================================================== // // PopplerCache.h // // 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) 2010 Carlos Garcia Campos // Copyright (C) 2018 Adam Reichold // //======================================================================== #ifndef POPPLER_CACHE_H #define POPPLER_CACHE_H #include #include #include #include template class PopplerCache { public: PopplerCache(const PopplerCache &) = delete; PopplerCache &operator=(const PopplerCache &other) = delete; explicit PopplerCache(std::size_t cacheSizeA) { entries.reserve(cacheSizeA); } /* The item returned is owned by the cache */ Item *lookup(const Key &key) { if (!entries.empty() && entries.front().first == key) { return entries.front().second.get(); } for (auto it = entries.begin(); it != entries.end(); ++it) { if (it->first == key) { auto *item = it->second.get(); std::rotate(entries.begin(), it, std::next(it)); return item; } } return nullptr; } /* The key and item pointers ownership is taken by the cache */ void put(const Key &key, Item *item) { if (entries.size() == entries.capacity()) { entries.pop_back(); } entries.emplace(entries.begin(), key, std::unique_ptr { item }); } private: std::vector>> entries; }; #endif poppler-24.02.0/poppler/PreScanOutputDev.cc000066400000000000000000000251201455701731300205510ustar00rootroot00000000000000//======================================================================== // // PreScanOutputDev.cc // // Copyright 2005 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) 2009 Carlos Garcia Campos // Copyright (C) 2010, 2011, 2018-2021 Albert Astals Cid // Copyright (C) 2011, 2014 William Bader // Copyright (C) 2011, 2013 Thomas Freitag // Copyright (C) 2011 Adrian Johnson // Copyright (C) 2022 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 // //======================================================================== #include #include #include "GlobalParams.h" #include "Gfx.h" #include "GfxFont.h" #include "Link.h" #include "Catalog.h" #include "Page.h" #include "PreScanOutputDev.h" //------------------------------------------------------------------------ // PreScanOutputDev //------------------------------------------------------------------------ PreScanOutputDev::PreScanOutputDev(PSLevel levelA) : level(levelA) { clearStats(); } PreScanOutputDev::~PreScanOutputDev() { } void PreScanOutputDev::startPage(int /*pageNum*/, GfxState * /*state*/, XRef * /*xref*/) { } void PreScanOutputDev::endPage() { } void PreScanOutputDev::stroke(GfxState *state) { double dashStart; check(state->getStrokeColorSpace(), state->getStrokeColor(), state->getStrokeOpacity(), state->getBlendMode()); const std::vector &dash = state->getLineDash(&dashStart); if (dash.size() != 0) { gdi = false; } } void PreScanOutputDev::fill(GfxState *state) { check(state->getFillColorSpace(), state->getFillColor(), state->getFillOpacity(), state->getBlendMode()); } void PreScanOutputDev::eoFill(GfxState *state) { check(state->getFillColorSpace(), state->getFillColor(), state->getFillOpacity(), state->getBlendMode()); } bool PreScanOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *catalog, GfxTilingPattern *tPat, const double *mat, int x0, int y0, int x1, int y1, double xStep, double yStep) { if (tPat->getPaintType() == 1) { bool tilingNeeded = (x1 - x0 != 1 || y1 - y0 != 1); if (tilingNeeded) { inTilingPatternFill++; } gfx->drawForm(tPat->getContentStream(), tPat->getResDict(), mat, tPat->getBBox()); if (tilingNeeded) { inTilingPatternFill--; } } else { check(state->getFillColorSpace(), state->getFillColor(), state->getFillOpacity(), state->getBlendMode()); } return true; } bool PreScanOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading) { if (shading->getColorSpace()->getMode() != csDeviceGray && shading->getColorSpace()->getMode() != csCalGray) { gray = false; } mono = false; if (state->getFillOpacity() != 1 || state->getBlendMode() != gfxBlendNormal) { transparency = true; } return true; } bool PreScanOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double /*tMin*/, double /*tMax*/) { if (shading->getColorSpace()->getMode() != csDeviceGray && shading->getColorSpace()->getMode() != csCalGray) { gray = false; } mono = false; if (state->getFillOpacity() != 1 || state->getBlendMode() != gfxBlendNormal) { transparency = true; } return true; } bool PreScanOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading, double /*sMin*/, double /*sMax*/) { if (shading->getColorSpace()->getMode() != csDeviceGray && shading->getColorSpace()->getMode() != csCalGray) { gray = false; } mono = false; if (state->getFillOpacity() != 1 || state->getBlendMode() != gfxBlendNormal) { transparency = true; } return true; } void PreScanOutputDev::clip(GfxState * /*state*/) { //~ check for a rectangle "near" the edge of the page; //~ else set gdi to false } void PreScanOutputDev::eoClip(GfxState * /*state*/) { //~ see clip() } void PreScanOutputDev::beginStringOp(GfxState *state) { int render; double m11, m12, m21, m22; bool simpleTTF; render = state->getRender(); if (!(render & 1)) { check(state->getFillColorSpace(), state->getFillColor(), state->getFillOpacity(), state->getBlendMode()); } if ((render & 3) == 1 || (render & 3) == 2) { check(state->getStrokeColorSpace(), state->getStrokeColor(), state->getStrokeOpacity(), state->getBlendMode()); } std::shared_ptr font = state->getFont(); state->getFontTransMat(&m11, &m12, &m21, &m22); //~ this should check for external fonts that are non-TrueType simpleTTF = fabs(m11 + m22) < 0.01 && m11 > 0 && fabs(m12) < 0.01 && fabs(m21) < 0.01 && fabs(state->getHorizScaling() - 1) < 0.001 && (font->getType() == fontTrueType || font->getType() == fontTrueTypeOT); if (simpleTTF) { //~ need to create a FoFiTrueType object, and check for a Unicode cmap } if (state->getRender() != 0 || !simpleTTF) { gdi = false; } } void PreScanOutputDev::endStringOp(GfxState * /*state*/) { } bool PreScanOutputDev::beginType3Char(GfxState * /*state*/, double /*x*/, double /*y*/, double /*dx*/, double /*dy*/, CharCode /*code*/, const Unicode * /*u*/, int /*uLen*/) { // return false so all Type 3 chars get rendered (no caching) return false; } void PreScanOutputDev::endType3Char(GfxState * /*state*/) { } void PreScanOutputDev::drawImageMask(GfxState *state, Object * /*ref*/, Stream *str, int width, int height, bool /*invert*/, bool /*interpolate*/, bool inlineImg) { int i, j; check(state->getFillColorSpace(), state->getFillColor(), state->getFillOpacity(), state->getBlendMode()); gdi = false; if ((level == psLevel1 || level == psLevel1Sep) && (state->getFillColorSpace()->getMode() == csPattern || inTilingPatternFill > 0)) { patternImgMask = true; } if (inlineImg) { str->reset(); j = height * ((width + 7) / 8); for (i = 0; i < j; ++i) { str->getChar(); } str->close(); } } void PreScanOutputDev::drawImage(GfxState *state, Object * /*ref*/, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool /*interpolate*/, const int * /*maskColors*/, bool inlineImg) { GfxColorSpace *colorSpace; int i, j; colorSpace = colorMap->getColorSpace(); if (colorSpace->getMode() == csIndexed) { colorSpace = ((GfxIndexedColorSpace *)colorSpace)->getBase(); } if (colorSpace->getMode() == csDeviceGray || colorSpace->getMode() == csCalGray) { if (colorMap->getBits() > 1) { mono = false; } } else { gray = false; mono = false; } if (state->getFillOpacity() != 1 || state->getBlendMode() != gfxBlendNormal) { transparency = true; } gdi = false; if ((level == psLevel1 || level == psLevel1Sep) && inTilingPatternFill > 0) { patternImgMask = true; } if (inlineImg) { str->reset(); j = height * ((width * colorMap->getNumPixelComps() * colorMap->getBits() + 7) / 8); for (i = 0; i < j; ++i) { str->getChar(); } str->close(); } } void PreScanOutputDev::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*/) { GfxColorSpace *colorSpace; colorSpace = colorMap->getColorSpace(); if (colorSpace->getMode() == csIndexed) { colorSpace = ((GfxIndexedColorSpace *)colorSpace)->getBase(); } if (colorSpace->getMode() == csDeviceGray || colorSpace->getMode() == csCalGray) { if (colorMap->getBits() > 1) { mono = false; } } else { gray = false; mono = false; } if (state->getFillOpacity() != 1 || state->getBlendMode() != gfxBlendNormal) { transparency = true; } gdi = false; } void PreScanOutputDev::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*/) { GfxColorSpace *colorSpace; colorSpace = colorMap->getColorSpace(); if (colorSpace->getMode() == csIndexed) { colorSpace = ((GfxIndexedColorSpace *)colorSpace)->getBase(); } if (colorSpace->getMode() != csDeviceGray && colorSpace->getMode() != csCalGray) { gray = false; } mono = false; transparency = true; gdi = false; } void PreScanOutputDev::beginTransparencyGroup(GfxState * /*state*/, const double * /*bbox*/, GfxColorSpace * /*blendingColorSpace*/, bool /*isolated*/, bool /*knockout*/, bool /*forSoftMask*/) { gdi = false; } void PreScanOutputDev::paintTransparencyGroup(GfxState *state, const double * /*bbox*/) { check(state->getFillColorSpace(), state->getFillColor(), state->getFillOpacity(), state->getBlendMode()); } void PreScanOutputDev::setSoftMask(GfxState * /*state*/, const double * /*bbox*/, bool /*alpha*/, Function * /*transferFunc*/, GfxColor * /*backdropColor*/) { transparency = true; } void PreScanOutputDev::check(GfxColorSpace *colorSpace, const GfxColor *color, double opacity, GfxBlendMode blendMode) { GfxRGB rgb; if (colorSpace->getMode() == csPattern) { mono = false; gray = false; gdi = false; } else { colorSpace->getRGB(color, &rgb); if (rgb.r != rgb.g || rgb.g != rgb.b || rgb.b != rgb.r) { mono = false; gray = false; } else if (!((rgb.r == 0 && rgb.g == 0 && rgb.b == 0) || (rgb.r == gfxColorComp1 && rgb.g == gfxColorComp1 && rgb.b == gfxColorComp1))) { mono = false; } } if (opacity != 1 || blendMode != gfxBlendNormal) { transparency = true; } } void PreScanOutputDev::clearStats() { mono = true; gray = true; transparency = false; gdi = true; patternImgMask = false; inTilingPatternFill = 0; } poppler-24.02.0/poppler/PreScanOutputDev.h000066400000000000000000000143241455701731300204170ustar00rootroot00000000000000//======================================================================== // // PreScanOutputDev.h // // Copyright 2005 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) 2009 Carlos Garcia Campos // Copyright (C) 2010, 2018-2021 Albert Astals Cid // Copyright (C) 2011, 2014 William Bader // Copyright (C) 2011, 2013 Thomas Freitag // Copyright (C) 2011 Adrian Johnson // // 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 PRESCANOUTPUTDEV_H #define PRESCANOUTPUTDEV_H #include "Object.h" #include "GfxState.h" #include "OutputDev.h" #include "PSOutputDev.h" //------------------------------------------------------------------------ // PreScanOutputDev //------------------------------------------------------------------------ class PreScanOutputDev : public OutputDev { public: // Constructor. explicit PreScanOutputDev(PSLevel levelA); // Destructor. ~PreScanOutputDev() override; //----- get info about output device // Does this device use upside-down coordinates? // (Upside-down means (0,0) is the top left corner of the page.) bool upsideDown() override { return true; } // Does this device use drawChar() or drawString()? bool useDrawChar() override { return true; } // Does this device use tilingPatternFill()? If this returns false, // tiling pattern fills will be reduced to a series of other drawing // operations. bool useTilingPatternFill() override { return true; } // 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. bool useShadedFills(int type) 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 true; } //----- initialization and control // Start a page. void startPage(int pageNum, GfxState *state, XRef *xref) override; // End a page. void endPage() override; //----- path painting void stroke(GfxState *state) override; void fill(GfxState *state) override; void eoFill(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; bool functionShadedFill(GfxState *state, GfxFunctionShading *shading) override; bool axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) override; bool radialShadedFill(GfxState *state, GfxRadialShading *shading, double tMin, double tMax) override; //----- path clipping void clip(GfxState *state) override; void eoClip(GfxState *state) override; //----- text drawing void beginStringOp(GfxState *state) override; void endStringOp(GfxState *state) override; bool beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, const Unicode *u, int uLen) override; void endType3Char(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 drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) override; 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; void 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) override; //----- transparency groups and soft masks void beginTransparencyGroup(GfxState *state, const double *bbox, GfxColorSpace *blendingColorSpace, bool isolated, bool knockout, bool forSoftMask) override; void paintTransparencyGroup(GfxState *state, const double *bbox) override; void setSoftMask(GfxState *state, const double *bbox, bool alpha, Function *transferFunc, GfxColor *backdropColor) override; //----- special access // Returns true if the operations performed since the last call to // clearStats() are all monochrome (black or white). bool isMonochrome() { return mono; } // Returns true if the operations performed since the last call to // clearStats() are all gray. bool isGray() { return gray; } // Returns true if the operations performed since the last call to // clearStats() included any transparency. bool usesTransparency() { return transparency; } // Returns true if the operations performed since the last call to // clearStats() are all rasterizable by GDI calls in GDIOutputDev. bool isAllGDI() { return gdi; } // Returns true if the operations performed since the last call to // clearStats() included any image mask fills with a pattern color // space. (only level1!) bool usesPatternImageMask() { return patternImgMask; } // Clear the stats used by the above functions. void clearStats(); private: void check(GfxColorSpace *colorSpace, const GfxColor *color, double opacity, GfxBlendMode blendMode); bool mono; bool gray; bool transparency; bool gdi; PSLevel level; // PostScript level (1, 2, separation) bool patternImgMask; int inTilingPatternFill; }; #endif poppler-24.02.0/poppler/ProfileData.cc000066400000000000000000000014471455701731300175360ustar00rootroot00000000000000//======================================================================== // // ProfileData.cc // // Copyright 2005 Jonathan Blandford // Copyright 2018 Adam Reichold // //======================================================================== #include #include "ProfileData.h" //------------------------------------------------------------------------ // ProfileData //------------------------------------------------------------------------ void ProfileData::addElement(double elapsed) { if (count == 0) { min = elapsed; max = elapsed; } else { if (elapsed < min) { min = elapsed; } if (elapsed > max) { max = elapsed; } } total += elapsed; count++; } poppler-24.02.0/poppler/ProfileData.h000066400000000000000000000017441455701731300174000ustar00rootroot00000000000000//======================================================================== // // ProfileData.h // // Copyright 2005 Jonathan Blandford // Copyright 2018 Adam Reichold // Copyright 2021 Albert Astals Cid // //======================================================================== #ifndef PROFILE_DATA_H #define PROFILE_DATA_H //------------------------------------------------------------------------ // ProfileData //------------------------------------------------------------------------ class ProfileData { public: void addElement(double elapsed); int getCount() const { return count; } double getTotal() const { return total; } double getMin() const { return min; } double getMax() const { return max; } private: int count = 0; // size of array double total = 0.0; // number of elements in array double min = 0.0; // reference count double max = 0.0; // reference count }; #endif poppler-24.02.0/poppler/Rendition.cc000066400000000000000000000254461455701731300173040ustar00rootroot00000000000000//********************************************************************************* // Rendition.cc //--------------------------------------------------------------------------------- // //--------------------------------------------------------------------------------- // Hugo Mercier (c) 2008 // Pino Toscano (c) 2008 // Carlos Garcia Campos (c) 2010 // Tobias Koenig (c) 2012 // Albert Astals Cid (C) 2017, 2018 // // 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA //********************************************************************************* #include #include "Rendition.h" #include "FileSpec.h" MediaWindowParameters::MediaWindowParameters() { // default values type = windowEmbedded; width = -1; height = -1; relativeTo = windowRelativeToDocument; XPosition = 0.5; YPosition = 0.5; hasTitleBar = true; hasCloseButton = true; isResizeable = true; } MediaWindowParameters::~MediaWindowParameters() { } void MediaWindowParameters::parseFWParams(Object *obj) { Object tmp = obj->dictLookup("D"); if (tmp.isArray()) { Array *dim = tmp.getArray(); if (dim->getLength() >= 2) { Object dd = dim->get(0); if (dd.isInt()) { width = dd.getInt(); } dd = dim->get(1); if (dd.isInt()) { height = dd.getInt(); } } } tmp = obj->dictLookup("RT"); if (tmp.isInt()) { int t = tmp.getInt(); switch (t) { case 0: relativeTo = windowRelativeToDocument; break; case 1: relativeTo = windowRelativeToApplication; break; case 2: relativeTo = windowRelativeToDesktop; break; } } tmp = obj->dictLookup("P"); if (tmp.isInt()) { int t = tmp.getInt(); switch (t) { case 0: // Upper left XPosition = 0.0; YPosition = 0.0; break; case 1: // Upper Center XPosition = 0.5; YPosition = 0.0; break; case 2: // Upper Right XPosition = 1.0; YPosition = 0.0; break; case 3: // Center Left XPosition = 0.0; YPosition = 0.5; break; case 4: // Center XPosition = 0.5; YPosition = 0.5; break; case 5: // Center Right XPosition = 1.0; YPosition = 0.5; break; case 6: // Lower Left XPosition = 0.0; YPosition = 1.0; break; case 7: // Lower Center XPosition = 0.5; YPosition = 1.0; break; case 8: // Lower Right XPosition = 1.0; YPosition = 1.0; break; } } tmp = obj->dictLookup("T"); if (tmp.isBool()) { hasTitleBar = tmp.getBool(); } tmp = obj->dictLookup("UC"); if (tmp.isBool()) { hasCloseButton = tmp.getBool(); } tmp = obj->dictLookup("R"); if (tmp.isInt()) { isResizeable = (tmp.getInt() != 0); } } MediaParameters::MediaParameters() { // instanciate to default values volume = 100; fittingPolicy = fittingUndefined; autoPlay = true; repeatCount = 1.0; opacity = 1.0; showControls = false; duration = 0; } MediaParameters::~MediaParameters() { } void MediaParameters::parseMediaPlayParameters(Object *obj) { Object tmp = obj->dictLookup("V"); if (tmp.isInt()) { volume = tmp.getInt(); } tmp = obj->dictLookup("C"); if (tmp.isBool()) { showControls = tmp.getBool(); } tmp = obj->dictLookup("F"); if (tmp.isInt()) { int t = tmp.getInt(); switch (t) { case 0: fittingPolicy = fittingMeet; break; case 1: fittingPolicy = fittingSlice; break; case 2: fittingPolicy = fittingFill; break; case 3: fittingPolicy = fittingScroll; break; case 4: fittingPolicy = fittingHidden; break; case 5: fittingPolicy = fittingUndefined; break; } } // duration parsing // duration's default value is set to 0, which means : intrinsinc media duration tmp = obj->dictLookup("D"); if (tmp.isDict()) { Object oname = tmp.dictLookup("S"); if (oname.isName()) { const char *name = oname.getName(); if (!strcmp(name, "F")) { duration = -1; // infinity } else if (!strcmp(name, "T")) { Object ddict = tmp.dictLookup("T"); if (ddict.isDict()) { Object tmp2 = ddict.dictLookup("V"); if (tmp2.isNum()) { duration = (unsigned long)(tmp2.getNum()); } } } } } tmp = obj->dictLookup("A"); if (tmp.isBool()) { autoPlay = tmp.getBool(); } tmp = obj->dictLookup("RC"); if (tmp.isNum()) { repeatCount = tmp.getNum(); } } void MediaParameters::parseMediaScreenParameters(Object *obj) { Object tmp = obj->dictLookup("W"); if (tmp.isInt()) { int t = tmp.getInt(); switch (t) { case 0: windowParams.type = MediaWindowParameters::windowFloating; break; case 1: windowParams.type = MediaWindowParameters::windowFullscreen; break; case 2: windowParams.type = MediaWindowParameters::windowHidden; break; case 3: windowParams.type = MediaWindowParameters::windowEmbedded; break; } } // background color tmp = obj->dictLookup("B"); if (tmp.isArray()) { Array *color = tmp.getArray(); Object component = color->get(0); bgColor.r = component.getNum(); component = color->get(1); bgColor.g = component.getNum(); component = color->get(2); bgColor.b = component.getNum(); } // opacity tmp = obj->dictLookup("O"); if (tmp.isNum()) { opacity = tmp.getNum(); } if (windowParams.type == MediaWindowParameters::windowFloating) { Object winDict = obj->dictLookup("F"); if (winDict.isDict()) { windowParams.parseFWParams(&winDict); } } } MediaRendition::~MediaRendition() { delete fileName; delete contentType; } MediaRendition::MediaRendition(Object *obj) { bool hasClip = false; ok = true; fileName = nullptr; contentType = nullptr; isEmbedded = false; // // Parse media clip data // Object tmp2 = obj->dictLookup("C"); if (tmp2.isDict()) { // media clip hasClip = true; Object tmp = tmp2.dictLookup("S"); if (tmp.isName()) { if (!strcmp(tmp.getName(), "MCD")) { // media clip data Object obj1 = tmp2.dictLookup("D"); if (obj1.isDict()) { Object obj2 = obj1.dictLookup("F"); if (obj2.isString()) { fileName = obj2.getString()->copy(); } obj2 = obj1.dictLookup("EF"); if (obj2.isDict()) { Object embedded = obj2.dictLookup("F"); if (embedded.isStream()) { isEmbedded = true; embeddedStreamObject = embedded.copy(); } } // TODO: D might be a form XObject too } else { error(errSyntaxError, -1, "Invalid Media Clip Data"); ok = false; } // FIXME: ignore CT if D is a form XObject obj1 = tmp2.dictLookup("CT"); if (obj1.isString()) { contentType = obj1.getString()->copy(); } } else if (!strcmp(tmp.getName(), "MCS")) { // media clip data // TODO } } else { error(errSyntaxError, -1, "Invalid Media Clip"); ok = false; } } if (!ok) { return; } // // parse Media Play Parameters tmp2 = obj->dictLookup("P"); if (tmp2.isDict()) { // media play parameters Object params = tmp2.dictLookup("MH"); if (params.isDict()) { MH.parseMediaPlayParameters(¶ms); } params = tmp2.dictLookup("BE"); if (params.isDict()) { BE.parseMediaPlayParameters(¶ms); } } else if (!hasClip) { error(errSyntaxError, -1, "Invalid Media Rendition"); ok = false; } // // parse Media Screen Parameters tmp2 = obj->dictLookup("SP"); if (tmp2.isDict()) { // media screen parameters Object params = tmp2.dictLookup("MH"); if (params.isDict()) { MH.parseMediaScreenParameters(¶ms); } params = tmp2.dictLookup("BE"); if (params.isDict()) { BE.parseMediaScreenParameters(¶ms); } } } MediaRendition::MediaRendition(const MediaRendition &other) { ok = other.ok; MH = other.MH; BE = other.BE; isEmbedded = other.isEmbedded; embeddedStreamObject = other.embeddedStreamObject.copy(); if (other.contentType) { contentType = other.contentType->copy(); } else { contentType = nullptr; } if (other.fileName) { fileName = other.fileName->copy(); } else { fileName = nullptr; } } void MediaRendition::outputToFile(FILE *fp) { if (!isEmbedded) { return; } embeddedStreamObject.streamReset(); while (true) { int c = embeddedStreamObject.streamGetChar(); if (c == EOF) { break; } fwrite(&c, 1, 1, fp); } } MediaRendition *MediaRendition::copy() const { return new MediaRendition(*this); } // TODO: SelectorRendition poppler-24.02.0/poppler/Rendition.h000066400000000000000000000106241455701731300171360ustar00rootroot00000000000000//********************************************************************************* // Rendition.h //--------------------------------------------------------------------------------- // //--------------------------------------------------------------------------------- // Hugo Mercier (c) 2008 // Carlos Garcia Campos (c) 2010 // Albert Astals Cid (C) 2017, 2018, 2021 // // 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA //********************************************************************************* #ifndef _RENDITION_H_ #define _RENDITION_H_ #include "Object.h" struct MediaWindowParameters { MediaWindowParameters(); ~MediaWindowParameters(); // parse from a floating window parameters dictionary void parseFWParams(Object *obj); enum MediaWindowType { windowFloating = 0, windowFullscreen, windowHidden, windowEmbedded }; enum MediaWindowRelativeTo { windowRelativeToDocument = 0, windowRelativeToApplication, windowRelativeToDesktop }; // DEFAULT VALUE MediaWindowType type; // movieWindowEmbedded int width; // -1 int height; // -1 // floating window position MediaWindowRelativeTo relativeTo; // windowRelativeToDocument (or to desktop) double XPosition; // 0.5 double YPosition; // 0.5 bool hasTitleBar; // true bool hasCloseButton; // true bool isResizeable; // true }; struct MediaParameters { MediaParameters(); ~MediaParameters(); // parse from a "Media Play Parameters" dictionary void parseMediaPlayParameters(Object *playObj); // parse from a "Media Screen Parameters" dictionary void parseMediaScreenParameters(Object *screenObj); enum MediaFittingPolicy { fittingMeet = 0, fittingSlice, fittingFill, fittingScroll, fittingHidden, fittingUndefined }; struct Color { double r, g, b; }; int duration; // 0 int volume; // 100 // defined in media play parameters, p 770 // correspond to 'fit' SMIL's attribute MediaFittingPolicy fittingPolicy; // fittingUndefined bool autoPlay; // true // repeat count, can be real values, 0 means forever double repeatCount; // 1.0 // background color // black = (0.0 0.0 0.0) Color bgColor; // opacity in [0.0 1.0] double opacity; // 1.0 bool showControls; // false MediaWindowParameters windowParams; }; class POPPLER_PRIVATE_EXPORT MediaRendition { public: explicit MediaRendition(Object *obj); MediaRendition(const MediaRendition &other); ~MediaRendition(); MediaRendition &operator=(const MediaRendition &) = delete; bool isOk() const { return ok; } const MediaParameters *getMHParameters() const { return &MH; } const MediaParameters *getBEParameters() const { return &BE; } const GooString *getContentType() const { return contentType; } const GooString *getFileName() const { return fileName; } bool getIsEmbedded() const { return isEmbedded; } Stream *getEmbbededStream() const { return isEmbedded ? embeddedStreamObject.getStream() : nullptr; } const Object *getEmbbededStreamObject() const { return isEmbedded ? &embeddedStreamObject : nullptr; } // write embedded stream to file void outputToFile(FILE *); MediaRendition *copy() const; private: bool ok; // "Must Honor" parameters MediaParameters MH; // "Best Effort" parameters MediaParameters BE; bool isEmbedded; GooString *contentType; // if it's embedded Object embeddedStreamObject; // if it's not embedded GooString *fileName; }; #endif /* _RENDITION_H_ */ poppler-24.02.0/poppler/SecurityHandler.cc000066400000000000000000000313141455701731300204450ustar00rootroot00000000000000//======================================================================== // // SecurityHandler.cc // // Copyright 2004 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) 2010, 2012, 2015, 2017, 2018, 2020-2022 Albert Astals Cid // Copyright (C) 2013 Adrian Johnson // Copyright (C) 2014 Fabio D'Urso // Copyright (C) 2016 Alok Anand // // 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 #include "GooString.h" #include "PDFDoc.h" #include "Decrypt.h" #include "Error.h" #include "GlobalParams.h" #include "SecurityHandler.h" #include //------------------------------------------------------------------------ // SecurityHandler //------------------------------------------------------------------------ SecurityHandler *SecurityHandler::make(PDFDoc *docA, Object *encryptDictA) { SecurityHandler *secHdlr; Object filterObj = encryptDictA->dictLookup("Filter"); if (filterObj.isName("Standard")) { secHdlr = new StandardSecurityHandler(docA, encryptDictA); } else if (filterObj.isName()) { error(errSyntaxError, -1, "Couldn't find the '{0:s}' security handler", filterObj.getName()); secHdlr = nullptr; } else { error(errSyntaxError, -1, "Missing or invalid 'Filter' entry in encryption dictionary"); secHdlr = nullptr; } return secHdlr; } SecurityHandler::SecurityHandler(PDFDoc *docA) { doc = docA; } SecurityHandler::~SecurityHandler() { } bool SecurityHandler::checkEncryption(const std::optional &ownerPassword, const std::optional &userPassword) { void *authData; if (ownerPassword || userPassword) { authData = makeAuthData(ownerPassword, userPassword); } else { authData = nullptr; } const bool ok = authorize(authData); if (authData) { freeAuthData(authData); } if (!ok) { if (!ownerPassword && !userPassword) { return checkEncryption(GooString(), GooString()); } else { error(errCommandLine, -1, "Incorrect password"); } } return ok; } //------------------------------------------------------------------------ // StandardSecurityHandler //------------------------------------------------------------------------ class StandardAuthData { public: StandardAuthData(GooString *ownerPasswordA, GooString *userPasswordA) { ownerPassword = ownerPasswordA; userPassword = userPasswordA; } ~StandardAuthData() { if (ownerPassword) { delete ownerPassword; } if (userPassword) { delete userPassword; } } StandardAuthData(const StandardAuthData &) = delete; StandardAuthData &operator=(const StandardAuthData &) = delete; GooString *ownerPassword; GooString *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; Object versionObj = encryptDictA->dictLookup("V"); Object revisionObj = encryptDictA->dictLookup("R"); Object lengthObj = encryptDictA->dictLookup("Length"); Object ownerKeyObj = encryptDictA->dictLookup("O"); Object userKeyObj = encryptDictA->dictLookup("U"); Object ownerEncObj = encryptDictA->dictLookup("OE"); Object userEncObj = encryptDictA->dictLookup("UE"); Object permObj = encryptDictA->dictLookup("P"); if (permObj.isInt64()) { unsigned int permUint = static_cast(permObj.getInt64()); int perms = permUint - UINT_MAX - 1; permObj = Object(perms); } Object fileIDObj = doc->getXRef()->getTrailerDict()->dictLookup("ID"); if (versionObj.isInt() && revisionObj.isInt() && permObj.isInt() && ownerKeyObj.isString() && userKeyObj.isString()) { encVersion = versionObj.getInt(); encRevision = revisionObj.getInt(); if ((encRevision <= 4 && ownerKeyObj.getString()->getLength() >= 1 && userKeyObj.getString()->getLength() >= 1) || ((encRevision == 5 || encRevision == 6) && // the spec says 48 bytes, but Acrobat pads them out longer ownerKeyObj.getString()->getLength() >= 48 && userKeyObj.getString()->getLength() >= 48 && ownerEncObj.isString() && ownerEncObj.getString()->getLength() == 32 && userEncObj.isString() && userEncObj.getString()->getLength() == 32)) { encAlgorithm = cryptRC4; // revision 2 forces a 40-bit key - some buggy PDF generators // set the Length value incorrectly if (encRevision == 2 || !lengthObj.isInt()) { fileKeyLength = 5; } else { fileKeyLength = lengthObj.getInt() / 8; } encryptMetadata = true; //~ this currently only handles a subset of crypt filter functionality //~ (in particular, it ignores the EFF entry in encryptDictA, and //~ doesn't handle the case where StmF, StrF, and EFF are not all the //~ same) if ((encVersion == 4 || encVersion == 5) && (encRevision == 4 || encRevision == 5 || encRevision == 6)) { Object cryptFiltersObj = encryptDictA->dictLookup("CF"); Object streamFilterObj = encryptDictA->dictLookup("StmF"); Object stringFilterObj = encryptDictA->dictLookup("StrF"); if (cryptFiltersObj.isDict() && streamFilterObj.isName() && stringFilterObj.isName() && !strcmp(streamFilterObj.getName(), stringFilterObj.getName())) { if (!strcmp(streamFilterObj.getName(), "Identity")) { // no encryption on streams or strings encVersion = encRevision = -1; } else { Object cryptFilterObj = cryptFiltersObj.dictLookup(streamFilterObj.getName()); if (cryptFilterObj.isDict()) { Object cfmObj = cryptFilterObj.dictLookup("CFM"); if (cfmObj.isName("V2")) { encVersion = 2; encRevision = 3; Object cfLengthObj = cryptFilterObj.dictLookup("Length"); if (cfLengthObj.isInt()) { //~ according to the spec, this should be cfLengthObj / 8 fileKeyLength = cfLengthObj.getInt(); } } else if (cfmObj.isName("AESV2")) { encVersion = 2; encRevision = 3; encAlgorithm = cryptAES; Object cfLengthObj = cryptFilterObj.dictLookup("Length"); if (cfLengthObj.isInt()) { //~ according to the spec, this should be cfLengthObj / 8 fileKeyLength = cfLengthObj.getInt(); } } else if (cfmObj.isName("AESV3")) { encVersion = 5; // let encRevision be 5 or 6 encAlgorithm = cryptAES256; Object cfLengthObj = cryptFilterObj.dictLookup("Length"); if (cfLengthObj.isInt()) { //~ according to the spec, this should be cfLengthObj / 8 fileKeyLength = cfLengthObj.getInt(); } } } } } Object encryptMetadataObj = encryptDictA->dictLookup("EncryptMetadata"); if (encryptMetadataObj.isBool()) { encryptMetadata = encryptMetadataObj.getBool(); } } permFlags = permObj.getInt(); ownerKey = ownerKeyObj.getString()->copy(); userKey = userKeyObj.getString()->copy(); if (encVersion >= 1 && encVersion <= 2 && encRevision >= 2 && encRevision <= 3) { if (fileIDObj.isArray()) { Object fileIDObj1 = fileIDObj.arrayGet(0); if (fileIDObj1.isString()) { fileID = fileIDObj1.getString()->copy(); } else { fileID = new GooString(); } } else { fileID = new GooString(); } 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 if (ownerEncObj.isString() && userEncObj.isString()) { ownerEnc = ownerEncObj.getString()->copy(); userEnc = userEncObj.getString()->copy(); if (fileKeyLength > 32 || fileKeyLength < 0) { fileKeyLength = 32; } ok = true; } else { error(errSyntaxError, -1, "Weird encryption owner/user info"); } } else if (!(encVersion == -1 && encRevision == -1)) { error(errUnimplemented, -1, "Unsupported version/revision ({0:d}/{1:d}) of Standard security handler", encVersion, encRevision); } if (encRevision <= 4) { // Adobe apparently zero-pads the U value (and maybe the O value?) // if it's short while (ownerKey->getLength() < 32) { ownerKey->append((char)0x00); } while (userKey->getLength() < 32) { userKey->append((char)0x00); } } } else { error(errSyntaxError, -1, "Invalid encryption key length. version: {0:d} - revision: {1:d} - ownerKeyLength: {2:d} - userKeyLength: {3:d} - ownerEncIsString: {4:d} - ownerEncLength: {5:d} - userEncIsString: {6:d} - userEncLength: {7:d}", encVersion, encRevision, ownerKeyObj.getString()->getLength(), userKeyObj.getString()->getLength(), ownerEncObj.isString(), ownerEncObj.isString() ? ownerEncObj.getString()->getLength() : -1, userEncObj.isString(), userEncObj.isString() ? userEncObj.getString()->getLength() : -1); } } else { error(errSyntaxError, -1, "Weird encryption info"); } } StandardSecurityHandler::~StandardSecurityHandler() { if (fileID) { delete fileID; } if (ownerKey) { delete ownerKey; } if (userKey) { delete userKey; } if (ownerEnc) { delete ownerEnc; } if (userEnc) { delete userEnc; } } bool StandardSecurityHandler::isUnencrypted() const { if (!ok) { return true; } return encVersion == -1 && encRevision == -1; } void *StandardSecurityHandler::makeAuthData(const std::optional &ownerPassword, const std::optional &userPassword) { return new StandardAuthData(ownerPassword ? ownerPassword->copy() : nullptr, userPassword ? userPassword->copy() : nullptr); } void StandardSecurityHandler::freeAuthData(void *authData) { delete (StandardAuthData *)authData; } bool StandardSecurityHandler::authorize(void *authData) { GooString *ownerPassword, *userPassword; if (!ok) { return false; } if (authData) { ownerPassword = ((StandardAuthData *)authData)->ownerPassword; userPassword = ((StandardAuthData *)authData)->userPassword; } else { ownerPassword = nullptr; userPassword = nullptr; } if (!Decrypt::makeFileKey(encVersion, encRevision, fileKeyLength, ownerKey, userKey, ownerEnc, userEnc, permFlags, fileID, ownerPassword, userPassword, fileKey, encryptMetadata, &ownerPasswordOk)) { return false; } return true; } poppler-24.02.0/poppler/SecurityHandler.h000066400000000000000000000113641455701731300203120ustar00rootroot00000000000000//======================================================================== // // SecurityHandler.h // // Copyright 2004 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) 2012, 2018, 2020-2022 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 // //======================================================================== #ifndef SECURITYHANDLER_H #define SECURITYHANDLER_H #include "poppler-config.h" #include "Object.h" #include class GooString; class PDFDoc; //------------------------------------------------------------------------ // SecurityHandler //------------------------------------------------------------------------ class SecurityHandler { public: static SecurityHandler *make(PDFDoc *docA, Object *encryptDictA); explicit SecurityHandler(PDFDoc *docA); virtual ~SecurityHandler(); SecurityHandler(const SecurityHandler &) = delete; SecurityHandler &operator=(const SecurityHandler &) = delete; // Returns true if the file is actually unencrypted. virtual bool isUnencrypted() const { return false; } // Check the document's encryption. If the document is encrypted, // this will first try and (in // "batch" mode), and if those fail, it will attempt to request a // password from the user. This is the high-level function that // calls the lower level functions for the specific security handler // (requesting a password three times, etc.). Returns true if the // document can be opened (if it's unencrypted, or if a correct // password is obtained); false otherwise (encrypted and no correct // password). bool checkEncryption(const std::optional &ownerPassword, const std::optional &userPassword); // Create authorization data for the specified owner and user // passwords. If the security handler doesn't support "batch" mode, // this function should return NULL. virtual void *makeAuthData(const std::optional &ownerPassword, const std::optional &userPassword) = 0; // Free the authorization data returned by makeAuthData or // getAuthData. virtual void freeAuthData(void *authData) = 0; // Attempt to authorize the document, using the supplied // authorization data (which may be NULL). Returns true if // successful (i.e., if at least the right to open the document was // granted). virtual bool authorize(void *authData) = 0; // Return the various authorization parameters. These are only // valid after authorize has returned true. virtual int getPermissionFlags() const = 0; virtual bool getOwnerPasswordOk() const = 0; virtual const unsigned char *getFileKey() const = 0; virtual int getFileKeyLength() const = 0; virtual int getEncVersion() const = 0; virtual int getEncRevision() const = 0; virtual CryptAlgorithm getEncAlgorithm() const = 0; protected: PDFDoc *doc; }; //------------------------------------------------------------------------ // StandardSecurityHandler //------------------------------------------------------------------------ class StandardSecurityHandler : public SecurityHandler { public: StandardSecurityHandler(PDFDoc *docA, Object *encryptDictA); ~StandardSecurityHandler() override; bool isUnencrypted() const override; void *makeAuthData(const std::optional &ownerPassword, const std::optional &userPassword) override; void freeAuthData(void *authData) override; bool authorize(void *authData) override; int getPermissionFlags() const override { return permFlags; } bool getOwnerPasswordOk() const override { return ownerPasswordOk; } const unsigned char *getFileKey() const override { return fileKey; } int getFileKeyLength() const override { return ok ? fileKeyLength : 0; } int getEncVersion() const override { return encVersion; } int getEncRevision() const override { return encRevision; } CryptAlgorithm getEncAlgorithm() const override { return encAlgorithm; } private: int permFlags; bool ownerPasswordOk; unsigned char fileKey[32]; int fileKeyLength; int encVersion; int encRevision; bool encryptMetadata; CryptAlgorithm encAlgorithm; GooString *ownerKey, *userKey; GooString *ownerEnc, *userEnc; GooString *fileID; bool ok; }; #endif poppler-24.02.0/poppler/SignatureInfo.cc000066400000000000000000000054451455701731300201230ustar00rootroot00000000000000//======================================================================== // // SignatureInfo.cc // // This file is licensed under the GPLv2 or later // // Copyright 2015 André Guerreiro // Copyright 2015 André Esser // Copyright 2017 Hans-Ulrich Jüttner // Copyright 2017-2020 Albert Astals Cid // Copyright 2018 Chinmoy Ranjan Pradhan // Copyright 2018 Oliver Sander // Copyright 2021 Georgiy Sgibnev . Work sponsored by lab50.net. // Copyright 2021 André Guerreiro // Copyright 2021 Marek Kasik // Copyright 2023 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== #include #include "SignatureInfo.h" #include "CertificateInfo.h" #include "goo/gmem.h" #include #include /* Constructor & Destructor */ SignatureInfo::~SignatureInfo() = default; /* GETTERS */ SignatureValidationStatus SignatureInfo::getSignatureValStatus() const { return sig_status; } CertificateValidationStatus SignatureInfo::getCertificateValStatus() const { return cert_status; } std::string SignatureInfo::getSignerName() const { return signer_name; } std::string SignatureInfo::getSubjectDN() const { return subject_dn; } const GooString &SignatureInfo::getLocation() const { return location; } const GooString &SignatureInfo::getReason() const { return reason; } HashAlgorithm SignatureInfo::getHashAlgorithm() const { return hash_type; } time_t SignatureInfo::getSigningTime() const { return signing_time; } const X509CertificateInfo *SignatureInfo::getCertificateInfo() const { return cert_info.get(); } /* SETTERS */ void SignatureInfo::setSignatureValStatus(enum SignatureValidationStatus sig_val_status) { sig_status = sig_val_status; } void SignatureInfo::setCertificateValStatus(enum CertificateValidationStatus cert_val_status) { cert_status = cert_val_status; } void SignatureInfo::setSignerName(const std::string &signerName) { signer_name = signerName; } void SignatureInfo::setSubjectDN(const std::string &subjectDN) { subject_dn = subjectDN; } void SignatureInfo::setLocation(const GooString *loc) { location = GooString(loc); } void SignatureInfo::setReason(const GooString *signingReason) { reason = GooString(signingReason); } void SignatureInfo::setHashAlgorithm(HashAlgorithm type) { hash_type = type; } void SignatureInfo::setSigningTime(time_t signingTime) { signing_time = signingTime; } void SignatureInfo::setCertificateInfo(std::unique_ptr certInfo) { cert_info = std::move(certInfo); } poppler-24.02.0/poppler/SignatureInfo.h000066400000000000000000000064621455701731300177650ustar00rootroot00000000000000//======================================================================== // // SignatureInfo.h // // This file is licensed under the GPLv2 or later // // Copyright 2015 André Guerreiro // Copyright 2015 André Esser // Copyright 2015, 2017, 2018, 2020 Albert Astals Cid // Copyright 2017 Hans-Ulrich Jüttner // Copyright 2018 Chinmoy Ranjan Pradhan // Copyright 2018 Oliver Sander // Copyright 2021 Georgiy Sgibnev . Work sponsored by lab50.net. // Copyright 2021 André Guerreiro // Copyright 2021 Marek Kasik // Copyright 2023 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== #ifndef SIGNATUREINFO_H #define SIGNATUREINFO_H #include #include #include "poppler_private_export.h" #include "goo/GooString.h" #include "HashAlgorithm.h" enum SignatureValidationStatus { SIGNATURE_VALID, SIGNATURE_INVALID, SIGNATURE_DIGEST_MISMATCH, SIGNATURE_DECODING_ERROR, SIGNATURE_GENERIC_ERROR, SIGNATURE_NOT_FOUND, SIGNATURE_NOT_VERIFIED }; enum CertificateValidationStatus { CERTIFICATE_TRUSTED, CERTIFICATE_UNTRUSTED_ISSUER, CERTIFICATE_UNKNOWN_ISSUER, CERTIFICATE_REVOKED, CERTIFICATE_EXPIRED, CERTIFICATE_GENERIC_ERROR, CERTIFICATE_NOT_VERIFIED }; class X509CertificateInfo; class POPPLER_PRIVATE_EXPORT SignatureInfo { public: SignatureInfo() = default; ~SignatureInfo(); SignatureInfo(const SignatureInfo &) = delete; SignatureInfo &operator=(const SignatureInfo &) = delete; /* GETTERS */ SignatureValidationStatus getSignatureValStatus() const; CertificateValidationStatus getCertificateValStatus() const; std::string getSignerName() const; std::string getSubjectDN() const; const GooString &getLocation() const; const GooString &getReason() const; 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 std::string &); void setSubjectDN(const std::string &); void setLocation(const GooString *); void setReason(const GooString *); void setHashAlgorithm(HashAlgorithm); void setSigningTime(time_t); void setSubFilterSupport(bool isSupported) { sig_subfilter_supported = isSupported; } void setCertificateInfo(std::unique_ptr); private: SignatureValidationStatus sig_status = SIGNATURE_NOT_VERIFIED; CertificateValidationStatus cert_status = CERTIFICATE_NOT_VERIFIED; std::unique_ptr cert_info; std::string signer_name; std::string subject_dn; GooString location; GooString reason; HashAlgorithm hash_type = HashAlgorithm::Unknown; time_t signing_time = 0; bool sig_subfilter_supported = false; }; #endif poppler-24.02.0/poppler/Sound.cc000066400000000000000000000072111455701731300164270ustar00rootroot00000000000000/* Sound.cc - an object that holds the sound structure * Copyright (C) 2006-2007, Pino Toscano * Copyright (C) 2009, 2017-2020, Albert Astals Cid * Copyright (C) 2020, Oliver Sander * * 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 "Object.h" #include "Sound.h" #include "Stream.h" #include "FileSpec.h" std::unique_ptr Sound::parseSound(Object *obj) { // let's try to see if this Object is a Sound, according to the PDF specs // (section 9.2) Stream *str = nullptr; // the Object must be a Stream if (obj->isStream()) { str = obj->getStream(); } else { return nullptr; } // the Stream must have a Dict Dict *dict = str->getDict(); if (dict == nullptr) { return nullptr; } // the Dict must have the 'R' key of type num Object tmp = dict->lookup("R"); if (tmp.isNum()) { return std::unique_ptr(new Sound(obj)); } else { return nullptr; } } Sound::Sound(const Object *obj, bool readAttrs) { streamObj = obj->copy(); samplingRate = 0.0; channels = 1; bitsPerSample = 8; encoding = soundRaw; if (readAttrs) { Dict *dict = streamObj.getStream()->getDict(); Object tmp = dict->lookup("F"); if (!tmp.isNull()) { // valid 'F' key -> external file kind = soundExternal; Object obj1 = getFileSpecNameForPlatform(&tmp); if (obj1.isString()) { fileName = obj1.getString()->toStr(); } } else { // no file specification, then the sound data have to be // extracted from the stream kind = soundEmbedded; } // sampling rate samplingRate = dict->lookup("R").getNumWithDefaultValue(0); // sound channels tmp = dict->lookup("C"); if (tmp.isInt()) { channels = tmp.getInt(); } // bits per sample tmp = dict->lookup("B"); if (tmp.isInt()) { bitsPerSample = tmp.getInt(); } // encoding format tmp = dict->lookup("E"); if (tmp.isName()) { const char *enc = tmp.getName(); if (strcmp("Raw", enc) == 0) { encoding = soundRaw; } else if (strcmp("Signed", enc) == 0) { encoding = soundSigned; } else if (strcmp("muLaw", enc) == 0) { encoding = soundMuLaw; } else if (strcmp("ALaw", enc) == 0) { encoding = soundALaw; } } } } Sound::~Sound() { } Stream *Sound::getStream() { return streamObj.getStream(); } Sound *Sound::copy() const { Sound *newsound = new Sound(&streamObj, false); newsound->kind = kind; newsound->fileName = fileName; newsound->samplingRate = samplingRate; newsound->channels = channels; newsound->bitsPerSample = bitsPerSample; newsound->encoding = encoding; return newsound; } poppler-24.02.0/poppler/Sound.h000066400000000000000000000045541455701731300163000ustar00rootroot00000000000000/* Sound.h - an object that holds the sound structure * Copyright (C) 2006-2007, Pino Toscano * Copyright (C) 2017-2021, Albert Astals Cid * Copyright (C) 2020, Oliver Sander * * 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 Sound_H #define Sound_H #include class Object; class Stream; //------------------------------------------------------------------------ enum SoundKind { soundEmbedded, // embedded sound soundExternal // external sound }; enum SoundEncoding { soundRaw, // raw encoding soundSigned, // twos-complement values soundMuLaw, // mu-law-encoded samples soundALaw // A-law-encoded samples }; class POPPLER_PRIVATE_EXPORT Sound { public: // Try to parse the Object obj static std::unique_ptr parseSound(Object *obj); // Destructor ~Sound(); Sound(const Sound &) = delete; Sound &operator=(const Sound &) = delete; const Object *getObject() const { return &streamObj; } Stream *getStream(); SoundKind getSoundKind() const { return kind; } const std::string &getFileName() const { return fileName; } double getSamplingRate() const { return samplingRate; } int getChannels() const { return channels; } int getBitsPerSample() const { return bitsPerSample; } SoundEncoding getEncoding() const { return encoding; } Sound *copy() const; private: // Create a sound. The Object obj is ensured to be a Stream with a Dict explicit Sound(const Object *obj, bool readAttrs = true); Object streamObj; SoundKind kind; std::string fileName; double samplingRate; int channels; int bitsPerSample; SoundEncoding encoding; }; #endif poppler-24.02.0/poppler/SplashOutputDev.cc000066400000000000000000004637511455701731300204700ustar00rootroot00000000000000//======================================================================== // // SplashOutputDev.cc // // Copyright 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) 2005 Takashi Iwai // Copyright (C) 2006 Stefan Schweizer // Copyright (C) 2006-2022 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, 2022, 2023 Thomas Freitag // Copyright (C) 2009 Carlos Garcia Campos // Copyright (C) 2009, 2014-2016, 2019 William Bader // Copyright (C) 2010 Patrick Spendrin // Copyright (C) 2010 Brian Cameron // Copyright (C) 2010 Paweł Wiejacha // Copyright (C) 2010 Christian Feuersänger // Copyright (C) 2011 Andreas Hartmetz // Copyright (C) 2011 Andrea Canciani // Copyright (C) 2011, 2012, 2017 Adrian Johnson // Copyright (C) 2013 Lu Wang // Copyright (C) 2013 Li Junling // Copyright (C) 2014 Ed Porras // Copyright (C) 2014 Richard PALO // Copyright (C) 2015 Tamas Szekeres // Copyright (C) 2015 Kenji Uno // Copyright (C) 2016 Takahiro Hashimoto // Copyright (C) 2017, 2021 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 // // 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 #include #include #include #include "goo/gfile.h" #include "GlobalParams.h" #include "Error.h" #include "Object.h" #include "Gfx.h" #include "GfxFont.h" #include "Page.h" #include "PDFDoc.h" #include "Link.h" #include "FontEncodingTables.h" #include "fofi/FoFiTrueType.h" #include "splash/SplashBitmap.h" #include "splash/SplashGlyphBitmap.h" #include "splash/SplashPattern.h" #include "splash/SplashScreen.h" #include "splash/SplashPath.h" #include "splash/SplashState.h" #include "splash/SplashErrorCodes.h" #include "splash/SplashFontEngine.h" #include "splash/SplashFont.h" #include "splash/SplashFontFile.h" #include "splash/SplashFontFileID.h" #include "splash/SplashMath.h" #include "splash/Splash.h" #include "SplashOutputDev.h" #include static const double s_minLineWidth = 0.0; static inline void convertGfxColor(SplashColorPtr dest, const SplashColorMode colorMode, const GfxColorSpace *colorSpace, const GfxColor *src) { SplashColor color; GfxGray gray; GfxRGB rgb; GfxCMYK cmyk; GfxColor deviceN; // make gcc happy color[0] = color[1] = color[2] = 0; color[3] = 0; switch (colorMode) { case splashModeMono1: case splashModeMono8: colorSpace->getGray(src, &gray); color[0] = colToByte(gray); break; case splashModeXBGR8: color[3] = 255; // fallthrough case splashModeBGR8: case splashModeRGB8: colorSpace->getRGB(src, &rgb); color[0] = colToByte(rgb.r); color[1] = colToByte(rgb.g); color[2] = colToByte(rgb.b); break; case splashModeCMYK8: colorSpace->getCMYK(src, &cmyk); color[0] = colToByte(cmyk.c); color[1] = colToByte(cmyk.m); color[2] = colToByte(cmyk.y); color[3] = colToByte(cmyk.k); break; case splashModeDeviceN8: colorSpace->getDeviceN(src, &deviceN); for (int i = 0; i < SPOT_NCOMPS + 4; i++) { color[i] = colToByte(deviceN.c[i]); } break; } splashColorCopy(dest, color); } // Copy a color according to the color mode. // Use convertGfxShortColor() below when the destination is a bitmap // to avoid overwriting cells. // Calling this in SplashGouraudPattern::getParameterizedColor() fixes bug 90570. // Use convertGfxColor() above when the destination is an array of SPOT_NCOMPS+4 bytes, // to ensure that everything is initialized. static inline void convertGfxShortColor(SplashColorPtr dest, const SplashColorMode colorMode, const GfxColorSpace *colorSpace, const GfxColor *src) { switch (colorMode) { case splashModeMono1: case splashModeMono8: { GfxGray gray; colorSpace->getGray(src, &gray); dest[0] = colToByte(gray); } break; case splashModeXBGR8: dest[3] = 255; // fallthrough case splashModeBGR8: case splashModeRGB8: { GfxRGB rgb; colorSpace->getRGB(src, &rgb); dest[0] = colToByte(rgb.r); dest[1] = colToByte(rgb.g); dest[2] = colToByte(rgb.b); } break; case splashModeCMYK8: { GfxCMYK cmyk; colorSpace->getCMYK(src, &cmyk); dest[0] = colToByte(cmyk.c); dest[1] = colToByte(cmyk.m); dest[2] = colToByte(cmyk.y); dest[3] = colToByte(cmyk.k); } break; case splashModeDeviceN8: { GfxColor deviceN; colorSpace->getDeviceN(src, &deviceN); for (int i = 0; i < SPOT_NCOMPS + 4; i++) { dest[i] = colToByte(deviceN.c[i]); } } break; } } //------------------------------------------------------------------------ // SplashGouraudPattern //------------------------------------------------------------------------ SplashGouraudPattern::SplashGouraudPattern(bool bDirectColorTranslationA, GfxState *stateA, GfxGouraudTriangleShading *shadingA) { state = stateA; shading = shadingA; bDirectColorTranslation = bDirectColorTranslationA; gfxMode = shadingA->getColorSpace()->getMode(); } SplashGouraudPattern::~SplashGouraudPattern() { } 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) { GfxColor c0, c1, c2; shading->getTriangle(i, x0, y0, &c0, x1, y1, &c1, x2, y2, &c2); const GfxColorSpace *srcColorSpace = shading->getColorSpace(); convertGfxColor(color0, mode, srcColorSpace, &c0); convertGfxColor(color1, mode, srcColorSpace, &c1); convertGfxColor(color2, mode, srcColorSpace, &c2); } void SplashGouraudPattern::getParameterizedColor(double colorinterp, SplashColorMode mode, SplashColorPtr dest) { GfxColor src; shading->getParameterizedColor(colorinterp, &src); if (bDirectColorTranslation) { const int colorComps = splashColorModeNComps[mode]; for (int m = 0; m < colorComps; ++m) { dest[m] = colToByte(src.c[m]); } } else { GfxColorSpace *srcColorSpace = shading->getColorSpace(); convertGfxShortColor(dest, mode, srcColorSpace, &src); } } //------------------------------------------------------------------------ // SplashFunctionPattern //------------------------------------------------------------------------ SplashFunctionPattern::SplashFunctionPattern(SplashColorMode colorModeA, GfxState *stateA, GfxFunctionShading *shadingA) { Matrix ctm; SplashColor defaultColor; GfxColor srcColor; const double *matrix = shadingA->getMatrix(); shading = shadingA; state = stateA; colorMode = colorModeA; state->getCTM(&ctm); double a1 = ctm.m[0]; double b1 = ctm.m[1]; double c1 = ctm.m[2]; double d1 = ctm.m[3]; ctm.m[0] = matrix[0] * a1 + matrix[1] * c1; ctm.m[1] = matrix[0] * b1 + matrix[1] * d1; ctm.m[2] = matrix[2] * a1 + matrix[3] * c1; ctm.m[3] = matrix[2] * b1 + matrix[3] * d1; ctm.m[4] = matrix[4] * a1 + matrix[5] * c1 + ctm.m[4]; ctm.m[5] = matrix[4] * b1 + matrix[5] * d1 + ctm.m[5]; ctm.invertTo(&ictm); gfxMode = shadingA->getColorSpace()->getMode(); shadingA->getColorSpace()->getDefaultColor(&srcColor); shadingA->getDomain(&xMin, &yMin, &xMax, &yMax); convertGfxColor(defaultColor, colorModeA, shadingA->getColorSpace(), &srcColor); } SplashFunctionPattern::~SplashFunctionPattern() { } bool SplashFunctionPattern::getColor(int x, int y, SplashColorPtr c) { GfxColor gfxColor; double xc, yc; ictm.transform(x, y, &xc, &yc); if (xc < xMin || xc > xMax || yc < yMin || yc > yMax) { return false; } shading->getColor(xc, yc, &gfxColor); convertGfxColor(c, colorMode, shading->getColorSpace(), &gfxColor); return true; } //------------------------------------------------------------------------ // SplashUnivariatePattern //------------------------------------------------------------------------ SplashUnivariatePattern::SplashUnivariatePattern(SplashColorMode colorModeA, GfxState *stateA, GfxUnivariateShading *shadingA) { Matrix ctm; double xMin, yMin, xMax, yMax; shading = shadingA; state = stateA; colorMode = colorModeA; state->getCTM(&ctm); ctm.invertTo(&ictm); // get the function domain t0 = shading->getDomain0(); t1 = shading->getDomain1(); dt = t1 - t0; stateA->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); shadingA->setupCache(&ctm, xMin, yMin, xMax, yMax); gfxMode = shadingA->getColorSpace()->getMode(); } SplashUnivariatePattern::~SplashUnivariatePattern() { } bool SplashUnivariatePattern::getColor(int x, int y, SplashColorPtr c) { GfxColor gfxColor; double xc, yc, t; ictm.transform(x, y, &xc, &yc); if (!getParameter(xc, yc, &t)) { return false; } const int filled = shading->getColor(t, &gfxColor); if (unlikely(filled < shading->getColorSpace()->getNComps())) { for (int i = filled; i < shading->getColorSpace()->getNComps(); ++i) { gfxColor.c[i] = 0; } } convertGfxColor(c, colorMode, shading->getColorSpace(), &gfxColor); return true; } bool SplashUnivariatePattern::testPosition(int x, int y) { double xc, yc, t; ictm.transform(x, y, &xc, &yc); if (!getParameter(xc, yc, &t)) { return false; } return (t0 < t1) ? (t > t0 && t < t1) : (t > t1 && t < t0); } //------------------------------------------------------------------------ // SplashRadialPattern //------------------------------------------------------------------------ #define RADIAL_EPSILON (1. / 1024 / 1024) SplashRadialPattern::SplashRadialPattern(SplashColorMode colorModeA, GfxState *stateA, GfxRadialShading *shadingA) : SplashUnivariatePattern(colorModeA, stateA, shadingA) { SplashColor defaultColor; GfxColor srcColor; shadingA->getCoords(&x0, &y0, &r0, &dx, &dy, &dr); dx -= x0; dy -= y0; dr -= r0; a = dx * dx + dy * dy - dr * dr; if (fabs(a) > RADIAL_EPSILON) { inva = 1.0 / a; } shadingA->getColorSpace()->getDefaultColor(&srcColor); convertGfxColor(defaultColor, colorModeA, shadingA->getColorSpace(), &srcColor); } SplashRadialPattern::~SplashRadialPattern() { } bool SplashRadialPattern::getParameter(double xs, double ys, double *t) { double b, c, s0, s1; // We want to solve this system of equations: // // 1. (x - xc(s))^2 + (y -yc(s))^2 = rc(s)^2 // 2. xc(s) = x0 + s * (x1 - xo) // 3. yc(s) = y0 + s * (y1 - yo) // 4. rc(s) = r0 + s * (r1 - ro) // // To simplify the system a little, we translate // our coordinates to have the origin in (x0,y0) xs -= x0; ys -= y0; // Then we have to solve the equation: // A*s^2 - 2*B*s + C = 0 // where // A = dx^2 + dy^2 - dr^2 // B = xs*dx + ys*dy + r0*dr // C = xs^2 + ys^2 - r0^2 b = xs * dx + ys * dy + r0 * dr; c = xs * xs + ys * ys - r0 * r0; if (fabs(a) <= RADIAL_EPSILON) { // A is 0, thus the equation simplifies to: // -2*B*s + C = 0 // If B is 0, we can either have no solution or an indeterminate // equation, thus we behave as if we had an invalid solution if (fabs(b) <= RADIAL_EPSILON) { return false; } s0 = s1 = 0.5 * c / b; } else { double d; d = b * b - a * c; if (d < 0) { return false; } d = sqrt(d); s0 = b + d; s1 = b - d; // If A < 0, one of the two solutions will have negative radius, // thus it will be ignored. Otherwise we know that s1 <= s0 // (because d >=0 implies b - d <= b + d), so if both are valid it // will be the true solution. s0 *= inva; s1 *= inva; } if (r0 + s0 * dr >= 0) { if (0 <= s0 && s0 <= 1) { *t = t0 + dt * s0; return true; } else if (s0 < 0 && shading->getExtend0()) { *t = t0; return true; } else if (s0 > 1 && shading->getExtend1()) { *t = t1; return true; } } if (r0 + s1 * dr >= 0) { if (0 <= s1 && s1 <= 1) { *t = t0 + dt * s1; return true; } else if (s1 < 0 && shading->getExtend0()) { *t = t0; return true; } else if (s1 > 1 && shading->getExtend1()) { *t = t1; return true; } } return false; } #undef RADIAL_EPSILON //------------------------------------------------------------------------ // SplashAxialPattern //------------------------------------------------------------------------ SplashAxialPattern::SplashAxialPattern(SplashColorMode colorModeA, GfxState *stateA, GfxAxialShading *shadingA) : SplashUnivariatePattern(colorModeA, stateA, shadingA) { SplashColor defaultColor; GfxColor srcColor; shadingA->getCoords(&x0, &y0, &x1, &y1); dx = x1 - x0; dy = y1 - y0; const double mul_denominator = (dx * dx + dy * dy); if (unlikely(mul_denominator == 0)) { mul = 0; } else { mul = 1 / mul_denominator; } shadingA->getColorSpace()->getDefaultColor(&srcColor); convertGfxColor(defaultColor, colorModeA, shadingA->getColorSpace(), &srcColor); } SplashAxialPattern::~SplashAxialPattern() { } bool SplashAxialPattern::getParameter(double xc, double yc, double *t) { double s; xc -= x0; yc -= y0; s = (xc * dx + yc * dy) * mul; if (0 <= s && s <= 1) { *t = t0 + dt * s; } else if (s < 0 && shading->getExtend0()) { *t = t0; } else if (s > 1 && shading->getExtend1()) { *t = t1; } else { return false; } return true; } //------------------------------------------------------------------------ // Type 3 font cache size parameters #define type3FontCacheAssoc 8 #define type3FontCacheMaxSets 8 #define type3FontCacheSize (128 * 1024) //------------------------------------------------------------------------ // Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result. static inline unsigned char div255(int x) { return (unsigned char)((x + (x >> 8) + 0x80) >> 8); } //------------------------------------------------------------------------ // Blend functions //------------------------------------------------------------------------ static void splashOutBlendMultiply(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; } } { for (i = 0; i < splashColorModeNComps[cm]; ++i) { blend[i] = (dest[i] * src[i]) / 255; } } if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; blend[i] = 255 - blend[i]; } } } static void splashOutBlendScreen(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; } } { for (i = 0; i < splashColorModeNComps[cm]; ++i) { blend[i] = dest[i] + src[i] - (dest[i] * src[i]) / 255; } } if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; blend[i] = 255 - blend[i]; } } } static void splashOutBlendOverlay(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; } } { for (i = 0; i < splashColorModeNComps[cm]; ++i) { blend[i] = dest[i] < 0x80 ? (src[i] * 2 * dest[i]) / 255 : 255 - 2 * ((255 - src[i]) * (255 - dest[i])) / 255; } } if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; blend[i] = 255 - blend[i]; } } } static void splashOutBlendDarken(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; } } { for (i = 0; i < splashColorModeNComps[cm]; ++i) { blend[i] = dest[i] < src[i] ? dest[i] : src[i]; } } if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; blend[i] = 255 - blend[i]; } } } static void splashOutBlendLighten(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; } } { for (i = 0; i < splashColorModeNComps[cm]; ++i) { blend[i] = dest[i] > src[i] ? dest[i] : src[i]; } } if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; blend[i] = 255 - blend[i]; } } } static void splashOutBlendColorDodge(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i, x; if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; } } { for (i = 0; i < splashColorModeNComps[cm]; ++i) { if (src[i] == 255) { blend[i] = 255; } else { x = (dest[i] * 255) / (255 - src[i]); blend[i] = x <= 255 ? x : 255; } } } if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; blend[i] = 255 - blend[i]; } } } static void splashOutBlendColorBurn(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i, x; if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; } } { for (i = 0; i < splashColorModeNComps[cm]; ++i) { if (src[i] == 0) { blend[i] = 0; } else { x = ((255 - dest[i]) * 255) / src[i]; blend[i] = x <= 255 ? 255 - x : 0; } } } if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; blend[i] = 255 - blend[i]; } } } static void splashOutBlendHardLight(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; } } { for (i = 0; i < splashColorModeNComps[cm]; ++i) { blend[i] = src[i] < 0x80 ? (dest[i] * 2 * src[i]) / 255 : 255 - 2 * ((255 - dest[i]) * (255 - src[i])) / 255; } } if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; blend[i] = 255 - blend[i]; } } } static void splashOutBlendSoftLight(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i, x; if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; } } { for (i = 0; i < splashColorModeNComps[cm]; ++i) { if (src[i] < 0x80) { blend[i] = dest[i] - (255 - 2 * src[i]) * dest[i] * (255 - dest[i]) / (255 * 255); } else { if (dest[i] < 0x40) { x = (((((16 * dest[i] - 12 * 255) * dest[i]) / 255) + 4 * 255) * dest[i]) / 255; } else { x = (int)sqrt(255.0 * dest[i]); } blend[i] = dest[i] + (2 * src[i] - 255) * (x - dest[i]) / 255; } } } if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; blend[i] = 255 - blend[i]; } } } static void splashOutBlendDifference(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; } } { for (i = 0; i < splashColorModeNComps[cm]; ++i) { blend[i] = dest[i] < src[i] ? src[i] - dest[i] : dest[i] - src[i]; } } if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; blend[i] = 255 - blend[i]; } } if (cm == splashModeDeviceN8) { for (i = 4; i < splashColorModeNComps[cm]; ++i) { if (dest[i] == 0 && src[i] == 0) { blend[i] = 0; } } } } static void splashOutBlendExclusion(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; } } { for (i = 0; i < splashColorModeNComps[cm]; ++i) { blend[i] = dest[i] + src[i] - (2 * dest[i] * src[i]) / 255; } } if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; blend[i] = 255 - blend[i]; } } if (cm == splashModeDeviceN8) { for (i = 4; i < splashColorModeNComps[cm]; ++i) { if (dest[i] == 0 && src[i] == 0) { blend[i] = 0; } } } } static int getLum(int r, int g, int 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 = std::min({ r, g, b }); int rgbMax = std::max({ r, g, b }); return rgbMax - rgbMin; } static void clipColor(int rIn, int gIn, int bIn, unsigned char *rOut, unsigned char *gOut, unsigned char *bOut) { int lum = getLum(rIn, gIn, bIn); int rgbMin = std::min({ rIn, bIn, gIn }); int rgbMax = std::max({ rIn, bIn, gIn }); if (rgbMin < 0) { *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)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; *bOut = bIn; } } static void setLum(unsigned char rIn, unsigned char gIn, unsigned char bIn, int lum, unsigned char *rOut, unsigned char *gOut, unsigned char *bOut) { int d; d = lum - getLum(rIn, gIn, bIn); clipColor(rIn + d, gIn + d, bIn + d, rOut, gOut, bOut); } static void setSat(unsigned char rIn, unsigned char gIn, unsigned char bIn, int sat, unsigned char *rOut, unsigned char *gOut, unsigned char *bOut) { int rgbMin, rgbMid, rgbMax; unsigned char *minOut, *midOut, *maxOut; if (rIn < gIn) { rgbMin = rIn; minOut = rOut; rgbMid = gIn; midOut = gOut; } else { rgbMin = gIn; minOut = gOut; rgbMid = rIn; midOut = rOut; } if (bIn > rgbMid) { rgbMax = bIn; maxOut = bOut; } else if (bIn > rgbMin) { rgbMax = rgbMid; maxOut = midOut; rgbMid = bIn; midOut = bOut; } else { rgbMax = rgbMid; maxOut = midOut; rgbMid = rgbMin; midOut = minOut; rgbMin = bIn; minOut = bOut; } if (rgbMax > rgbMin) { *midOut = (unsigned char)std::clamp(((rgbMid - rgbMin) * sat) / (rgbMax - rgbMin), 0, 255); *maxOut = (unsigned char)std::clamp(sat, 0, 255); } else { *midOut = *maxOut = 0; } *minOut = 0; } static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { unsigned char r0, g0, b0; unsigned char r1, g1, b1; int i; SplashColor src2, dest2; switch (cm) { case splashModeMono1: case splashModeMono8: blend[0] = dest[0]; break; case splashModeXBGR8: src[3] = 255; // fallthrough case splashModeRGB8: case splashModeBGR8: setSat(src[0], src[1], src[2], getSat(dest[0], dest[1], dest[2]), &r0, &g0, &b0); setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]), &blend[0], &blend[1], &blend[2]); break; case splashModeCMYK8: case splashModeDeviceN8: for (i = 0; i < 4; i++) { // convert to additive src2[i] = 0xff - src[i]; dest2[i] = 0xff - dest[i]; } // NB: inputs have already been converted to additive mode setSat(src2[0], src2[1], src2[2], getSat(dest2[0], dest2[1], dest2[2]), &r0, &g0, &b0); setLum(r0, g0, b0, getLum(dest2[0], dest2[1], dest2[2]), &r1, &g1, &b1); blend[0] = r1; blend[1] = g1; blend[2] = b1; blend[3] = dest2[3]; for (i = 0; i < 4; i++) { // convert back to subtractive blend[i] = 0xff - blend[i]; } break; } } static void splashOutBlendSaturation(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { unsigned char r0, g0, b0; unsigned char r1, g1, b1; int i; SplashColor src2, dest2; switch (cm) { case splashModeMono1: case splashModeMono8: blend[0] = dest[0]; break; case splashModeXBGR8: src[3] = 255; // fallthrough case splashModeRGB8: case splashModeBGR8: setSat(dest[0], dest[1], dest[2], getSat(src[0], src[1], src[2]), &r0, &g0, &b0); setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]), &blend[0], &blend[1], &blend[2]); break; case splashModeCMYK8: case splashModeDeviceN8: for (i = 0; i < 4; i++) { // convert to additive src2[i] = 0xff - src[i]; dest2[i] = 0xff - dest[i]; } setSat(dest2[0], dest2[1], dest2[2], getSat(src2[0], src2[1], src2[2]), &r0, &g0, &b0); setLum(r0, g0, b0, getLum(dest2[0], dest2[1], dest2[2]), &r1, &g1, &b1); blend[0] = r1; blend[1] = g1; blend[2] = b1; blend[3] = dest2[3]; for (i = 0; i < 4; i++) { // convert back to subtractive blend[i] = 0xff - blend[i]; } break; } } static void splashOutBlendColor(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { unsigned char r, g, b; int i; SplashColor src2, dest2; switch (cm) { case splashModeMono1: case splashModeMono8: blend[0] = dest[0]; break; case splashModeXBGR8: src[3] = 255; // fallthrough case splashModeRGB8: case splashModeBGR8: setLum(src[0], src[1], src[2], getLum(dest[0], dest[1], dest[2]), &blend[0], &blend[1], &blend[2]); break; case splashModeCMYK8: case splashModeDeviceN8: for (i = 0; i < 4; i++) { // convert to additive src2[i] = 0xff - src[i]; dest2[i] = 0xff - dest[i]; } setLum(src2[0], src2[1], src2[2], getLum(dest2[0], dest2[1], dest2[2]), &r, &g, &b); blend[0] = r; blend[1] = g; blend[2] = b; blend[3] = dest2[3]; for (i = 0; i < 4; i++) { // convert back to subtractive blend[i] = 0xff - blend[i]; } break; } } static void splashOutBlendLuminosity(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { unsigned char r, g, b; int i; SplashColor src2, dest2; switch (cm) { case splashModeMono1: case splashModeMono8: blend[0] = dest[0]; break; case splashModeXBGR8: src[3] = 255; // fallthrough case splashModeRGB8: case splashModeBGR8: setLum(dest[0], dest[1], dest[2], getLum(src[0], src[1], src[2]), &blend[0], &blend[1], &blend[2]); break; case splashModeCMYK8: case splashModeDeviceN8: for (i = 0; i < 4; i++) { // convert to additive src2[i] = 0xff - src[i]; dest2[i] = 0xff - dest[i]; } setLum(dest2[0], dest2[1], dest2[2], getLum(src2[0], src2[1], src2[2]), &r, &g, &b); blend[0] = r; blend[1] = g; blend[2] = b; blend[3] = src2[3]; for (i = 0; i < 4; i++) { // convert back to subtractive blend[i] = 0xff - blend[i]; } break; } } // NB: This must match the GfxBlendMode enum defined in GfxState.h. static const SplashBlendFunc splashOutBlendFuncs[] = { nullptr, &splashOutBlendMultiply, &splashOutBlendScreen, &splashOutBlendOverlay, &splashOutBlendDarken, &splashOutBlendLighten, &splashOutBlendColorDodge, &splashOutBlendColorBurn, &splashOutBlendHardLight, &splashOutBlendSoftLight, &splashOutBlendDifference, &splashOutBlendExclusion, &splashOutBlendHue, &splashOutBlendSaturation, &splashOutBlendColor, &splashOutBlendLuminosity }; //------------------------------------------------------------------------ // SplashOutFontFileID //------------------------------------------------------------------------ class SplashOutFontFileID : public SplashFontFileID { public: explicit SplashOutFontFileID(const Ref *rA) { r = *rA; } ~SplashOutFontFileID() override; bool matches(SplashFontFileID *id) override { return ((SplashOutFontFileID *)id)->r == r; } private: Ref r; }; SplashOutFontFileID::~SplashOutFontFileID() = default; //------------------------------------------------------------------------ // T3FontCache //------------------------------------------------------------------------ struct T3FontCacheTag { unsigned short code; unsigned short mru; // valid bit (0x8000) and MRU index }; class T3FontCache { public: T3FontCache(const Ref *fontID, double m11A, double m12A, double m21A, double m22A, int glyphXA, int glyphYA, int glyphWA, int glyphHA, bool validBBoxA, bool aa); ~T3FontCache(); T3FontCache(const T3FontCache &) = delete; T3FontCache &operator=(const T3FontCache &) = delete; bool matches(const Ref *idA, double m11A, double m12A, double m21A, double m22A) { return fontID == *idA && m11 == m11A && m12 == m12A && m21 == m21A && m22 == m22A; } Ref fontID; // PDF font ID double m11, m12, m21, m22; // transform matrix int glyphX, glyphY; // pixel offset of glyph bitmaps int glyphW, glyphH; // size of glyph bitmaps, in pixels bool validBBox; // false if the bbox was [0 0 0 0] int glyphSize; // size of glyph bitmaps, in bytes int cacheSets; // number of sets in cache int cacheAssoc; // cache associativity (glyphs per set) unsigned char *cacheData; // glyph pixmap cache T3FontCacheTag *cacheTags; // cache tags, i.e., char codes }; T3FontCache::T3FontCache(const Ref *fontIDA, double m11A, double m12A, double m21A, double m22A, int glyphXA, int glyphYA, int glyphWA, int glyphHA, bool validBBoxA, bool aa) { fontID = *fontIDA; m11 = m11A; m12 = m12A; m21 = m21A; m22 = m22A; glyphX = glyphXA; glyphY = glyphYA; glyphW = glyphWA; glyphH = glyphHA; validBBox = validBBoxA; // sanity check for excessively large glyphs (which most likely // indicate an incorrect BBox) if (glyphW > INT_MAX / glyphH || glyphW <= 0 || glyphH <= 0 || glyphW * glyphH > 100000) { glyphW = glyphH = 100; validBBox = false; } if (aa) { glyphSize = glyphW * glyphH; } else { glyphSize = ((glyphW + 7) >> 3) * glyphH; } cacheAssoc = type3FontCacheAssoc; for (cacheSets = type3FontCacheMaxSets; cacheSets > 1 && cacheSets * cacheAssoc * glyphSize > type3FontCacheSize; cacheSets >>= 1) { ; } if (glyphSize < 10485760 / cacheAssoc / cacheSets) { cacheData = (unsigned char *)gmallocn_checkoverflow(cacheSets * cacheAssoc, glyphSize); } else { error(errSyntaxWarning, -1, "Not creating cacheData for T3FontCache, it asked for too much memory.\n" " This could teoretically result in wrong rendering,\n" " but most probably the document is bogus.\n" " Please report a bug if you think the rendering may be wrong because of this."); cacheData = nullptr; } if (cacheData != nullptr) { cacheTags = (T3FontCacheTag *)gmallocn(cacheSets * cacheAssoc, sizeof(T3FontCacheTag)); for (int i = 0; i < cacheSets * cacheAssoc; ++i) { cacheTags[i].mru = i & (cacheAssoc - 1); } } else { cacheTags = nullptr; } } T3FontCache::~T3FontCache() { gfree(cacheData); gfree(cacheTags); } struct T3GlyphStack { unsigned short code; // character code bool haveDx; // set after seeing a d0/d1 operator bool doNotCache; // set if we see a gsave/grestore before // the d0/d1 //----- cache info T3FontCache *cache; // font cache for the current font T3FontCacheTag *cacheTag; // pointer to cache tag for the glyph unsigned char *cacheData; // pointer to cache data for the glyph //----- saved state SplashBitmap *origBitmap; Splash *origSplash; double origCTM4, origCTM5; T3GlyphStack *next; // next object on stack }; //------------------------------------------------------------------------ // SplashTransparencyGroup //------------------------------------------------------------------------ struct SplashTransparencyGroup { int tx, ty; // translation coordinates SplashBitmap *tBitmap; // bitmap for transparency group SplashBitmap *softmask; // bitmap for softmasks GfxColorSpace *blendingColorSpace; bool isolated; //----- for knockout SplashBitmap *shape; bool knockout; SplashCoord knockoutOpacity; bool fontAA; //----- saved state SplashBitmap *origBitmap; Splash *origSplash; SplashTransparencyGroup *next; }; //------------------------------------------------------------------------ // SplashOutputDev //------------------------------------------------------------------------ SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA, int bitmapRowPadA, bool reverseVideoA, SplashColorPtr paperColorA, bool bitmapTopDownA, SplashThinLineMode thinLineMode, bool overprintPreviewA) { colorMode = colorModeA; bitmapRowPad = bitmapRowPadA; bitmapTopDown = bitmapTopDownA; fontAntialias = true; vectorAntialias = true; overprintPreview = overprintPreviewA; enableFreeType = true; enableFreeTypeHinting = false; enableSlightHinting = false; setupScreenParams(72.0, 72.0); reverseVideo = reverseVideoA; if (paperColorA != nullptr) { splashColorCopy(paperColor, paperColorA); } else { splashClearColor(paperColor); } skipHorizText = false; skipRotatedText = false; keepAlphaChannel = paperColorA == nullptr; doc = nullptr; bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode, colorMode != splashModeMono1, bitmapTopDown); splash = new Splash(bitmap, vectorAntialias, &screenParams); splash->setMinLineWidth(s_minLineWidth); splash->setThinLineMode(thinLineMode); splash->clear(paperColor, 0); fontEngine = nullptr; nT3Fonts = 0; t3GlyphStack = nullptr; font = nullptr; needFontUpdate = false; textClipPath = nullptr; transpGroupStack = nullptr; xref = nullptr; } void SplashOutputDev::setupScreenParams(double hDPI, double vDPI) { screenParams.size = -1; screenParams.dotRadius = -1; screenParams.gamma = (SplashCoord)1.0; screenParams.blackThreshold = (SplashCoord)0.0; screenParams.whiteThreshold = (SplashCoord)1.0; // use clustered dithering for resolution >= 300 dpi // (compare to 299.9 to avoid floating point issues) if (hDPI > 299.9 && vDPI > 299.9) { screenParams.type = splashScreenStochasticClustered; if (screenParams.size < 0) { screenParams.size = 64; } if (screenParams.dotRadius < 0) { screenParams.dotRadius = 2; } } else { screenParams.type = splashScreenDispersed; if (screenParams.size < 0) { screenParams.size = 4; } } } SplashOutputDev::~SplashOutputDev() { int i; for (i = 0; i < nT3Fonts; ++i) { delete t3FontCache[i]; } if (fontEngine) { delete fontEngine; } if (splash) { delete splash; } if (bitmap) { delete bitmap; } delete textClipPath; } void SplashOutputDev::startDoc(PDFDoc *docA) { int i; doc = docA; if (fontEngine) { delete fontEngine; } fontEngine = new SplashFontEngine(enableFreeType, enableFreeTypeHinting, enableSlightHinting, getFontAntialias() && colorMode != splashModeMono1); for (i = 0; i < nT3Fonts; ++i) { delete t3FontCache[i]; } nT3Fonts = 0; } void SplashOutputDev::startPage(int pageNum, GfxState *state, XRef *xrefA) { int w, h; SplashCoord mat[6]; SplashColor color; xref = xrefA; if (state) { setupScreenParams(state->getHDPI(), state->getVDPI()); w = (int)(state->getPageWidth() + 0.5); if (w <= 0) { w = 1; } h = (int)(state->getPageHeight() + 0.5); if (h <= 0) { h = 1; } } else { w = h = 1; } SplashThinLineMode thinLineMode = splashThinLineDefault; if (splash) { thinLineMode = splash->getThinLineMode(); delete splash; splash = nullptr; } if (!bitmap || w != bitmap->getWidth() || h != bitmap->getHeight()) { if (bitmap) { delete bitmap; bitmap = nullptr; } bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, colorMode != splashModeMono1, bitmapTopDown); if (!bitmap->getDataPtr()) { delete bitmap; w = h = 1; bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, colorMode != splashModeMono1, bitmapTopDown); } } splash = new Splash(bitmap, vectorAntialias, &screenParams); splash->setThinLineMode(thinLineMode); splash->setMinLineWidth(s_minLineWidth); if (state) { const double *ctm = state->getCTM(); mat[0] = (SplashCoord)ctm[0]; mat[1] = (SplashCoord)ctm[1]; mat[2] = (SplashCoord)ctm[2]; mat[3] = (SplashCoord)ctm[3]; mat[4] = (SplashCoord)ctm[4]; mat[5] = (SplashCoord)ctm[5]; splash->setMatrix(mat); } switch (colorMode) { case splashModeMono1: case splashModeMono8: color[0] = 0; break; case splashModeXBGR8: color[3] = 255; // fallthrough case splashModeRGB8: case splashModeBGR8: color[0] = color[1] = color[2] = 0; break; case splashModeCMYK8: color[0] = color[1] = color[2] = color[3] = 0; break; case splashModeDeviceN8: splashClearColor(color); break; } splash->setStrokePattern(new SplashSolidColor(color)); splash->setFillPattern(new SplashSolidColor(color)); splash->setLineCap(splashLineCapButt); splash->setLineJoin(splashLineJoinMiter); splash->setLineDash({}, 0); splash->setMiterLimit(10); splash->setFlatness(1); // the SA parameter supposedly defaults to false, but Acrobat // apparently hardwires it to true splash->setStrokeAdjust(true); splash->clear(paperColor, 0); } void SplashOutputDev::endPage() { if (colorMode != splashModeMono1 && !keepAlphaChannel) { splash->compositeBackground(paperColor); } } void SplashOutputDev::saveState(GfxState *state) { splash->saveState(); if (t3GlyphStack && !t3GlyphStack->haveDx) { t3GlyphStack->doNotCache = true; error(errSyntaxWarning, -1, "Save (q) operator before d0/d1 in Type 3 glyph"); } } void SplashOutputDev::restoreState(GfxState *state) { splash->restoreState(); needFontUpdate = true; if (t3GlyphStack && !t3GlyphStack->haveDx) { t3GlyphStack->doNotCache = true; error(errSyntaxWarning, -1, "Restore (Q) operator before d0/d1 in Type 3 glyph"); } } void SplashOutputDev::updateAll(GfxState *state) { updateLineDash(state); updateLineJoin(state); updateLineCap(state); updateLineWidth(state); updateFlatness(state); updateMiterLimit(state); updateStrokeAdjust(state); updateFillColorSpace(state); updateFillColor(state); updateStrokeColorSpace(state); updateStrokeColor(state); needFontUpdate = true; } void SplashOutputDev::updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32) { SplashCoord mat[6]; const double *ctm = state->getCTM(); mat[0] = (SplashCoord)ctm[0]; mat[1] = (SplashCoord)ctm[1]; mat[2] = (SplashCoord)ctm[2]; mat[3] = (SplashCoord)ctm[3]; mat[4] = (SplashCoord)ctm[4]; mat[5] = (SplashCoord)ctm[5]; splash->setMatrix(mat); } void SplashOutputDev::updateLineDash(GfxState *state) { double dashStart; const std::vector &dashPattern = state->getLineDash(&dashStart); std::vector dash(dashPattern.size()); for (std::vector::size_type i = 0; i < dashPattern.size(); ++i) { dash[i] = (SplashCoord)dashPattern[i]; if (dash[i] < 0) { dash[i] = 0; } } splash->setLineDash(std::move(dash), (SplashCoord)dashStart); } void SplashOutputDev::updateFlatness(GfxState *state) { #if 0 // Acrobat ignores the flatness setting, and always renders curves // with a fairly small flatness value splash->setFlatness(state->getFlatness()); #endif } void SplashOutputDev::updateLineJoin(GfxState *state) { splash->setLineJoin(state->getLineJoin()); } void SplashOutputDev::updateLineCap(GfxState *state) { splash->setLineCap(state->getLineCap()); } void SplashOutputDev::updateMiterLimit(GfxState *state) { splash->setMiterLimit(state->getMiterLimit()); } void SplashOutputDev::updateLineWidth(GfxState *state) { splash->setLineWidth(state->getLineWidth()); } void SplashOutputDev::updateStrokeAdjust(GfxState * /*state*/) { #if 0 // the SA parameter supposedly defaults to false, but Acrobat // apparently hardwires it to true splash->setStrokeAdjust(state->getStrokeAdjust()); #endif } void SplashOutputDev::updateFillColorSpace(GfxState *state) { if (colorMode == splashModeDeviceN8) { state->getFillColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); } } void SplashOutputDev::updateStrokeColorSpace(GfxState *state) { if (colorMode == splashModeDeviceN8) { state->getStrokeColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); } } void SplashOutputDev::updateFillColor(GfxState *state) { GfxGray gray; GfxRGB rgb; GfxCMYK cmyk; GfxColor deviceN; switch (colorMode) { case splashModeMono1: case splashModeMono8: state->getFillGray(&gray); splash->setFillPattern(getColor(gray)); break; case splashModeXBGR8: case splashModeRGB8: case splashModeBGR8: state->getFillRGB(&rgb); splash->setFillPattern(getColor(&rgb)); break; case splashModeCMYK8: state->getFillCMYK(&cmyk); splash->setFillPattern(getColor(&cmyk)); break; case splashModeDeviceN8: state->getFillDeviceN(&deviceN); splash->setFillPattern(getColor(&deviceN)); break; } } void SplashOutputDev::updateStrokeColor(GfxState *state) { GfxGray gray; GfxRGB rgb; GfxCMYK cmyk; GfxColor deviceN; switch (colorMode) { case splashModeMono1: case splashModeMono8: state->getStrokeGray(&gray); splash->setStrokePattern(getColor(gray)); break; case splashModeXBGR8: case splashModeRGB8: case splashModeBGR8: state->getStrokeRGB(&rgb); splash->setStrokePattern(getColor(&rgb)); break; case splashModeCMYK8: state->getStrokeCMYK(&cmyk); splash->setStrokePattern(getColor(&cmyk)); break; case splashModeDeviceN8: state->getStrokeDeviceN(&deviceN); splash->setStrokePattern(getColor(&deviceN)); break; } } SplashPattern *SplashOutputDev::getColor(GfxGray gray) { SplashColor color; if (reverseVideo) { gray = gfxColorComp1 - gray; } color[0] = colToByte(gray); return new SplashSolidColor(color); } SplashPattern *SplashOutputDev::getColor(GfxRGB *rgb) { GfxColorComp r, g, b; SplashColor color; if (reverseVideo) { r = gfxColorComp1 - rgb->r; g = gfxColorComp1 - rgb->g; b = gfxColorComp1 - rgb->b; } else { r = rgb->r; g = rgb->g; b = rgb->b; } color[0] = colToByte(r); color[1] = colToByte(g); color[2] = colToByte(b); if (colorMode == splashModeXBGR8) { color[3] = 255; } return new SplashSolidColor(color); } SplashPattern *SplashOutputDev::getColor(GfxCMYK *cmyk) { SplashColor color; color[0] = colToByte(cmyk->c); color[1] = colToByte(cmyk->m); color[2] = colToByte(cmyk->y); color[3] = colToByte(cmyk->k); return new SplashSolidColor(color); } SplashPattern *SplashOutputDev::getColor(GfxColor *deviceN) { SplashColor color; for (int i = 0; i < 4 + SPOT_NCOMPS; i++) { color[i] = colToByte(deviceN->c[i]); } return new SplashSolidColor(color); } void SplashOutputDev::getMatteColor(SplashColorMode colorMode, GfxImageColorMap *colorMap, const GfxColor *matteColorIn, SplashColor matteColor) { GfxGray gray; GfxRGB rgb; GfxCMYK cmyk; GfxColor deviceN; switch (colorMode) { case splashModeMono1: case splashModeMono8: colorMap->getColorSpace()->getGray(matteColorIn, &gray); matteColor[0] = colToByte(gray); break; case splashModeRGB8: case splashModeBGR8: colorMap->getColorSpace()->getRGB(matteColorIn, &rgb); matteColor[0] = colToByte(rgb.r); matteColor[1] = colToByte(rgb.g); matteColor[2] = colToByte(rgb.b); break; case splashModeXBGR8: colorMap->getColorSpace()->getRGB(matteColorIn, &rgb); matteColor[0] = colToByte(rgb.r); matteColor[1] = colToByte(rgb.g); matteColor[2] = colToByte(rgb.b); matteColor[3] = 255; break; case splashModeCMYK8: colorMap->getColorSpace()->getCMYK(matteColorIn, &cmyk); matteColor[0] = colToByte(cmyk.c); matteColor[1] = colToByte(cmyk.m); matteColor[2] = colToByte(cmyk.y); matteColor[3] = colToByte(cmyk.k); break; case splashModeDeviceN8: colorMap->getColorSpace()->getDeviceN(matteColorIn, &deviceN); for (int cp = 0; cp < SPOT_NCOMPS + 4; cp++) { matteColor[cp] = colToByte(deviceN.c[cp]); } break; } } void SplashOutputDev::setOverprintMask(GfxColorSpace *colorSpace, bool overprintFlag, int overprintMode, const GfxColor *singleColor, bool grayIndexed) { unsigned int mask; GfxCMYK cmyk; bool additive = false; int i; if (colorSpace->getMode() == csIndexed) { setOverprintMask(((GfxIndexedColorSpace *)colorSpace)->getBase(), overprintFlag, overprintMode, singleColor, grayIndexed); return; } if (overprintFlag && overprintPreview) { mask = colorSpace->getOverprintMask(); if (singleColor && overprintMode && colorSpace->getMode() == csDeviceCMYK) { colorSpace->getCMYK(singleColor, &cmyk); if (cmyk.c == 0) { mask &= ~1; } if (cmyk.m == 0) { mask &= ~2; } if (cmyk.y == 0) { mask &= ~4; } if (cmyk.k == 0) { mask &= ~8; } } if (grayIndexed && colorSpace->getMode() != csDeviceN) { mask &= ~7; } else if (colorSpace->getMode() == csSeparation) { GfxSeparationColorSpace *deviceSep = (GfxSeparationColorSpace *)colorSpace; additive = deviceSep->getName()->cmp("All") != 0 && mask == 0x0f && !deviceSep->isNonMarking(); } else if (colorSpace->getMode() == csDeviceN) { GfxDeviceNColorSpace *deviceNCS = (GfxDeviceNColorSpace *)colorSpace; additive = mask == 0x0f && !deviceNCS->isNonMarking(); for (i = 0; i < deviceNCS->getNComps() && additive; i++) { if (deviceNCS->getColorantName(i) == "Cyan") { additive = false; } else if (deviceNCS->getColorantName(i) == "Magenta") { additive = false; } else if (deviceNCS->getColorantName(i) == "Yellow") { additive = false; } else if (deviceNCS->getColorantName(i) == "Black") { additive = false; } } } } else { mask = 0xffffffff; } splash->setOverprintMask(mask, additive); } void SplashOutputDev::updateBlendMode(GfxState *state) { splash->setBlendFunc(splashOutBlendFuncs[state->getBlendMode()]); } void SplashOutputDev::updateFillOpacity(GfxState *state) { splash->setFillAlpha((SplashCoord)state->getFillOpacity()); if (transpGroupStack != nullptr && (SplashCoord)state->getFillOpacity() < transpGroupStack->knockoutOpacity) { transpGroupStack->knockoutOpacity = (SplashCoord)state->getFillOpacity(); } } void SplashOutputDev::updateStrokeOpacity(GfxState *state) { splash->setStrokeAlpha((SplashCoord)state->getStrokeOpacity()); if (transpGroupStack != nullptr && (SplashCoord)state->getStrokeOpacity() < transpGroupStack->knockoutOpacity) { transpGroupStack->knockoutOpacity = (SplashCoord)state->getStrokeOpacity(); } } void SplashOutputDev::updatePatternOpacity(GfxState *state) { splash->setPatternAlpha((SplashCoord)state->getStrokeOpacity(), (SplashCoord)state->getFillOpacity()); } void SplashOutputDev::clearPatternOpacity(GfxState *state) { splash->clearPatternAlpha(); } void SplashOutputDev::updateFillOverprint(GfxState *state) { splash->setFillOverprint(state->getFillOverprint()); } void SplashOutputDev::updateStrokeOverprint(GfxState *state) { splash->setStrokeOverprint(state->getStrokeOverprint()); } void SplashOutputDev::updateOverprintMode(GfxState *state) { splash->setOverprintMode(state->getOverprintMode()); } void SplashOutputDev::updateTransfer(GfxState *state) { Function **transfer; unsigned char red[256], green[256], blue[256], gray[256]; double x, y; int i; transfer = state->getTransfer(); if (transfer[0] && transfer[0]->getInputSize() == 1 && transfer[0]->getOutputSize() == 1) { if (transfer[1] && transfer[1]->getInputSize() == 1 && transfer[1]->getOutputSize() == 1 && transfer[2] && transfer[2]->getInputSize() == 1 && transfer[2]->getOutputSize() == 1 && transfer[3] && transfer[3]->getInputSize() == 1 && transfer[3]->getOutputSize() == 1) { for (i = 0; i < 256; ++i) { x = i / 255.0; transfer[0]->transform(&x, &y); red[i] = (unsigned char)(y * 255.0 + 0.5); transfer[1]->transform(&x, &y); green[i] = (unsigned char)(y * 255.0 + 0.5); transfer[2]->transform(&x, &y); blue[i] = (unsigned char)(y * 255.0 + 0.5); transfer[3]->transform(&x, &y); gray[i] = (unsigned char)(y * 255.0 + 0.5); } } else { for (i = 0; i < 256; ++i) { x = i / 255.0; transfer[0]->transform(&x, &y); red[i] = green[i] = blue[i] = gray[i] = (unsigned char)(y * 255.0 + 0.5); } } } else { for (i = 0; i < 256; ++i) { red[i] = green[i] = blue[i] = gray[i] = (unsigned char)i; } } splash->setTransfer(red, green, blue, gray); } void SplashOutputDev::updateFont(GfxState * /*state*/) { needFontUpdate = true; } void SplashOutputDev::doUpdateFont(GfxState *state) { GfxFontType fontType; SplashOutFontFileID *id = nullptr; SplashFontFile *fontFile; SplashFontSrc *fontsrc = nullptr; const double *textMat; double m11, m12, m21, m22, fontSize; int faceIndex = 0; SplashCoord mat[4]; bool recreateFont = false; bool doAdjustFontMatrix = false; needFontUpdate = false; font = nullptr; GfxFont *const gfxFont = state->getFont().get(); if (!gfxFont) { goto err1; } fontType = gfxFont->getType(); if (fontType == fontType3) { goto err1; } // 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; } // 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; } 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; } // embedded font std::string fileName; std::optional> tmpBuf; if (fontLoc->locType == gfxFontLocEmbedded) { // if there is an embedded font, read it to memory tmpBuf = gfxFont->readEmbFontFile((xref) ? xref : doc->getXRef()); if (!tmpBuf) { goto err2; } // external font } else { // gfxFontLocExternal fileName = fontLoc->path; fontType = fontLoc->fontType; doAdjustFontMatrix = true; } fontsrc = new SplashFontSrc; if (!fileName.empty()) { fontsrc->setFile(fileName); } else { fontsrc->setBuf(std::move(tmpBuf.value())); } // load the font file switch (fontType) { case fontType1: if (!(fontFile = fontEngine->loadType1Font(id, fontsrc, (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { 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; } break; case fontType1C: if (!(fontFile = fontEngine->loadType1CFont(id, fontsrc, (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { 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; } break; case fontType1COT: if (!(fontFile = fontEngine->loadOpenTypeT1CFont(id, fontsrc, (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { 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; } break; case fontTrueType: case fontTrueTypeOT: { std::unique_ptr ff; if (!fileName.empty()) { ff = FoFiTrueType::load(fileName.c_str()); } else { ff = FoFiTrueType::make(fontsrc->buf.data(), fontsrc->buf.size()); } int *codeToGID; const int n = ff ? 256 : 0; if (ff) { codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff.get()); // if we're substituting for a non-TrueType font, we need to mark // all notdef codes as "do not draw" (rather than drawing TrueType // notdef glyphs) if (gfxFont->getType() != fontTrueType && gfxFont->getType() != fontTrueTypeOT) { for (int i = 0; i < 256; ++i) { if (codeToGID[i] == 0) { codeToGID[i] = -1; } } } } else { codeToGID = nullptr; } if (!(fontFile = fontEngine->loadTrueTypeFont(id, fontsrc, codeToGID, n))) { 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; } break; } case fontCIDType0: case fontCIDType0C: if (!(fontFile = fontEngine->loadCIDFont(id, fontsrc))) { 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; } 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)); } else { codeToGID = nullptr; n = 0; } if (!(fontFile = fontEngine->loadOpenTypeCFFFont(id, fontsrc, codeToGID, n))) { 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; } 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)); } } else { std::unique_ptr ff; if (!fileName.empty()) { ff = FoFiTrueType::load(fileName.c_str()); } else { ff = FoFiTrueType::make(fontsrc->buf.data(), fontsrc->buf.size()); } if (!ff) { error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)"); goto err2; } codeToGID = ((GfxCIDFont *)gfxFont)->getCodeToGIDMap(ff.get(), &n); } if (!(fontFile = fontEngine->loadTrueTypeFont(id, fontsrc, codeToGID, n, faceIndex))) { 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; } break; } default: // this shouldn't happen goto err2; } fontFile->doAdjustMatrix = doAdjustFontMatrix; } // get the font matrix textMat = state->getTextMat(); fontSize = state->getFontSize(); m11 = textMat[0] * fontSize * state->getHorizScaling(); m12 = textMat[1] * fontSize * state->getHorizScaling(); m21 = textMat[2] * fontSize; m22 = textMat[3] * fontSize; // create the scaled font mat[0] = m11; mat[1] = m12; mat[2] = m21; mat[3] = m22; font = fontEngine->getFont(fontFile, mat, splash->getMatrix()); // for substituted fonts: adjust the font matrix -- compare the // width of 'm' in the original font and the substituted font if (fontFile->doAdjustMatrix && !gfxFont->isCIDFont()) { double w1, w2, w3; CharCode code; const char *name; for (code = 0; code < 256; ++code) { if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) && name[0] == 'm' && name[1] == '\0') { break; } } if (code < 256) { w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code); w2 = font->getGlyphAdvance(code); w3 = ((Gfx8BitFont *)gfxFont)->getWidth(0); if (!gfxFont->isSymbolic() && w2 > 0 && w1 > w3) { // if real font is substantially narrower than substituted // font, reduce the font size accordingly if (w1 > 0.01 && w1 < 0.9 * w2) { w1 /= w2; m11 *= w1; m21 *= w1; recreateFont = true; } } } } if (recreateFont) { mat[0] = m11; mat[1] = m12; mat[2] = m21; mat[3] = m22; font = fontEngine->getFont(fontFile, mat, splash->getMatrix()); } if (fontsrc && !fontsrc->isFile) { fontsrc->unref(); } return; err2: delete id; err1: if (fontsrc && !fontsrc->isFile) { fontsrc->unref(); } return; } void SplashOutputDev::stroke(GfxState *state) { if (state->getStrokeColorSpace()->isNonMarking()) { return; } setOverprintMask(state->getStrokeColorSpace(), state->getStrokeOverprint(), state->getOverprintMode(), state->getStrokeColor()); SplashPath path = convertPath(state, state->getPath(), false); splash->stroke(&path); } void SplashOutputDev::fill(GfxState *state) { if (state->getFillColorSpace()->isNonMarking()) { return; } setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), state->getOverprintMode(), state->getFillColor()); SplashPath path = convertPath(state, state->getPath(), true); splash->fill(&path, false); } void SplashOutputDev::eoFill(GfxState *state) { if (state->getFillColorSpace()->isNonMarking()) { return; } setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), state->getOverprintMode(), state->getFillColor()); SplashPath path = convertPath(state, state->getPath(), true); splash->fill(&path, true); } void SplashOutputDev::clip(GfxState *state) { SplashPath path = convertPath(state, state->getPath(), true); splash->clipToPath(&path, false); } void SplashOutputDev::eoClip(GfxState *state) { SplashPath path = convertPath(state, state->getPath(), true); splash->clipToPath(&path, true); } void SplashOutputDev::clipToStrokePath(GfxState *state) { SplashPath *path2; SplashPath path = convertPath(state, state->getPath(), false); path2 = splash->makeStrokePath(&path, state->getLineWidth()); splash->clipToPath(path2, false); delete path2; } SplashPath SplashOutputDev::convertPath(GfxState *state, const GfxPath *path, bool dropEmptySubpaths) { SplashPath sPath; int n, i, j; n = dropEmptySubpaths ? 1 : 0; for (i = 0; i < path->getNumSubpaths(); ++i) { const GfxSubpath *subpath = path->getSubpath(i); if (subpath->getNumPoints() > n) { sPath.reserve(subpath->getNumPoints() + 1); sPath.moveTo((SplashCoord)subpath->getX(0), (SplashCoord)subpath->getY(0)); j = 1; while (j < subpath->getNumPoints()) { if (subpath->getCurve(j)) { sPath.curveTo((SplashCoord)subpath->getX(j), (SplashCoord)subpath->getY(j), (SplashCoord)subpath->getX(j + 1), (SplashCoord)subpath->getY(j + 1), (SplashCoord)subpath->getX(j + 2), (SplashCoord)subpath->getY(j + 2)); j += 3; } else { sPath.lineTo((SplashCoord)subpath->getX(j), (SplashCoord)subpath->getY(j)); ++j; } } if (subpath->isClosed()) { sPath.close(); } } } return sPath; } void SplashOutputDev::drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, const Unicode *u, int uLen) { SplashPath *path; int render; bool doFill, doStroke, doClip, strokeAdjust; double m[4]; bool horiz; if (skipHorizText || skipRotatedText) { state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]); horiz = m[0] > 0 && fabs(m[1]) < 0.001 && fabs(m[2]) < 0.001 && m[3] < 0; if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) { return; } } // check for invisible text -- this is used by Acrobat Capture render = state->getRender(); if (render == 3) { return; } if (needFontUpdate) { doUpdateFont(state); } if (!font) { return; } x -= originX; y -= originY; doFill = !(render & 1) && !state->getFillColorSpace()->isNonMarking(); doStroke = ((render & 3) == 1 || (render & 3) == 2) && !state->getStrokeColorSpace()->isNonMarking(); doClip = render & 4; path = nullptr; SplashCoord lineWidth = splash->getLineWidth(); if (doStroke && lineWidth == 0.0) { splash->setLineWidth(1 / state->getVDPI()); } if (doStroke || doClip) { if ((path = font->getGlyphPath(code))) { path->offset((SplashCoord)x, (SplashCoord)y); } } // don't use stroke adjustment when stroking text -- the results // tend to be ugly (because characters with horizontal upper or // lower edges get misaligned relative to the other characters) strokeAdjust = false; // make gcc happy if (doStroke) { strokeAdjust = splash->getStrokeAdjust(); splash->setStrokeAdjust(false); } // fill and stroke if (doFill && doStroke) { if (path) { setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), state->getOverprintMode(), state->getFillColor()); splash->fill(path, false); setOverprintMask(state->getStrokeColorSpace(), state->getStrokeOverprint(), state->getOverprintMode(), state->getStrokeColor()); splash->stroke(path); } // fill } else if (doFill) { setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), state->getOverprintMode(), state->getFillColor()); splash->fillChar((SplashCoord)x, (SplashCoord)y, code, font); // stroke } else if (doStroke) { if (path) { setOverprintMask(state->getStrokeColorSpace(), state->getStrokeOverprint(), state->getOverprintMode(), state->getStrokeColor()); splash->stroke(path); } } splash->setLineWidth(lineWidth); // clip if (doClip) { if (path) { if (textClipPath) { textClipPath->append(path); } else { textClipPath = path; path = nullptr; } } } if (doStroke) { splash->setStrokeAdjust(strokeAdjust); } if (path) { delete path; } } bool SplashOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, const Unicode *u, int uLen) { std::shared_ptr gfxFont; const Ref *fontID; const double *ctm, *bbox; T3FontCache *t3Font; T3GlyphStack *t3gs; bool validBBox; double m[4]; bool horiz; double x1, y1, xMin, yMin, xMax, yMax, xt, yt; int i, j; // check for invisible text -- this is used by Acrobat Capture if (state->getRender() == 3) { // this is a bit of cheating, we say yes, font is already on cache // so we actually skip the rendering of it return true; } if (skipHorizText || skipRotatedText) { state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]); horiz = m[0] > 0 && fabs(m[1]) < 0.001 && fabs(m[2]) < 0.001 && m[3] < 0; if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) { return true; } } if (!(gfxFont = state->getFont())) { return false; } fontID = gfxFont->getID(); ctm = state->getCTM(); state->transform(0, 0, &xt, &yt); // is it the first (MRU) font in the cache? if (!(nT3Fonts > 0 && t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) { // is the font elsewhere in the cache? for (i = 1; i < nT3Fonts; ++i) { if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) { t3Font = t3FontCache[i]; for (j = i; j > 0; --j) { t3FontCache[j] = t3FontCache[j - 1]; } t3FontCache[0] = t3Font; break; } } if (i >= nT3Fonts) { // create new entry in the font cache if (nT3Fonts == splashOutT3FontCacheSize) { t3gs = t3GlyphStack; while (t3gs != nullptr) { if (t3gs->cache == t3FontCache[nT3Fonts - 1]) { error(errSyntaxWarning, -1, "t3FontCache reaches limit but font still on stack in SplashOutputDev::beginType3Char"); return true; } t3gs = t3gs->next; } delete t3FontCache[nT3Fonts - 1]; --nT3Fonts; } for (j = nT3Fonts; j > 0; --j) { t3FontCache[j] = t3FontCache[j - 1]; } ++nT3Fonts; bbox = gfxFont->getFontBBox(); if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) { // unspecified bounding box -- just take a guess xMin = xt - 5; xMax = xMin + 30; yMax = yt + 15; yMin = yMax - 45; validBBox = false; } else { state->transform(bbox[0], bbox[1], &x1, &y1); xMin = xMax = x1; yMin = yMax = y1; state->transform(bbox[0], bbox[3], &x1, &y1); if (x1 < xMin) { xMin = x1; } else if (x1 > xMax) { xMax = x1; } if (y1 < yMin) { yMin = y1; } else if (y1 > yMax) { yMax = y1; } state->transform(bbox[2], bbox[1], &x1, &y1); if (x1 < xMin) { xMin = x1; } else if (x1 > xMax) { xMax = x1; } if (y1 < yMin) { yMin = y1; } else if (y1 > yMax) { yMax = y1; } state->transform(bbox[2], bbox[3], &x1, &y1); if (x1 < xMin) { xMin = x1; } else if (x1 > xMax) { xMax = x1; } if (y1 < yMin) { yMin = y1; } else if (y1 > yMax) { yMax = y1; } validBBox = true; } t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3], (int)floor(xMin - xt) - 2, (int)floor(yMin - yt) - 2, (int)ceil(xMax) - (int)floor(xMin) + 4, (int)ceil(yMax) - (int)floor(yMin) + 4, validBBox, colorMode != splashModeMono1); } } t3Font = t3FontCache[0]; // is the glyph in the cache? i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc; for (j = 0; j < t3Font->cacheAssoc; ++j) { if (t3Font->cacheTags != nullptr) { if ((t3Font->cacheTags[i + j].mru & 0x8000) && t3Font->cacheTags[i + j].code == code) { drawType3Glyph(state, t3Font, &t3Font->cacheTags[i + j], t3Font->cacheData + (i + j) * t3Font->glyphSize); return true; } } } // push a new Type 3 glyph record t3gs = new T3GlyphStack(); t3gs->next = t3GlyphStack; t3GlyphStack = t3gs; t3GlyphStack->code = code; t3GlyphStack->cache = t3Font; t3GlyphStack->cacheTag = nullptr; t3GlyphStack->cacheData = nullptr; t3GlyphStack->haveDx = false; t3GlyphStack->doNotCache = false; return false; } void SplashOutputDev::endType3Char(GfxState *state) { T3GlyphStack *t3gs; if (t3GlyphStack->cacheTag) { memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr(), t3GlyphStack->cache->glyphSize); delete bitmap; delete splash; bitmap = t3GlyphStack->origBitmap; splash = t3GlyphStack->origSplash; const double *ctm = state->getCTM(); state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3], t3GlyphStack->origCTM4, t3GlyphStack->origCTM5); updateCTM(state, 0, 0, 0, 0, 0, 0); drawType3Glyph(state, t3GlyphStack->cache, t3GlyphStack->cacheTag, t3GlyphStack->cacheData); } t3gs = t3GlyphStack; t3GlyphStack = t3gs->next; delete t3gs; } void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) { if (likely(t3GlyphStack != nullptr)) { t3GlyphStack->haveDx = true; } else { error(errSyntaxWarning, -1, "t3GlyphStack was null in SplashOutputDev::type3D0"); } } void SplashOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) { T3FontCache *t3Font; SplashColor color; double xt, yt, xMin, xMax, yMin, yMax, x1, y1; int i, j; // ignore multiple d0/d1 operators if (!t3GlyphStack || t3GlyphStack->haveDx) { return; } t3GlyphStack->haveDx = true; // don't cache if we got a gsave/grestore before the d1 if (t3GlyphStack->doNotCache) { return; } if (unlikely(t3GlyphStack == nullptr)) { error(errSyntaxWarning, -1, "t3GlyphStack was null in SplashOutputDev::type3D1"); return; } if (unlikely(t3GlyphStack->origBitmap != nullptr)) { error(errSyntaxWarning, -1, "t3GlyphStack origBitmap was not null in SplashOutputDev::type3D1"); return; } if (unlikely(t3GlyphStack->origSplash != nullptr)) { error(errSyntaxWarning, -1, "t3GlyphStack origSplash was not null in SplashOutputDev::type3D1"); return; } t3Font = t3GlyphStack->cache; // check for a valid bbox state->transform(0, 0, &xt, &yt); state->transform(llx, lly, &x1, &y1); xMin = xMax = x1; yMin = yMax = y1; state->transform(llx, ury, &x1, &y1); if (x1 < xMin) { xMin = x1; } else if (x1 > xMax) { xMax = x1; } if (y1 < yMin) { yMin = y1; } else if (y1 > yMax) { yMax = y1; } state->transform(urx, lly, &x1, &y1); if (x1 < xMin) { xMin = x1; } else if (x1 > xMax) { xMax = x1; } if (y1 < yMin) { yMin = y1; } else if (y1 > yMax) { yMax = y1; } state->transform(urx, ury, &x1, &y1); if (x1 < xMin) { xMin = x1; } else if (x1 > xMax) { xMax = x1; } if (y1 < yMin) { yMin = y1; } else if (y1 > yMax) { yMax = y1; } if (xMin - xt < t3Font->glyphX || yMin - yt < t3Font->glyphY || xMax - xt > t3Font->glyphX + t3Font->glyphW || yMax - yt > t3Font->glyphY + t3Font->glyphH) { if (t3Font->validBBox) { error(errSyntaxWarning, -1, "Bad bounding box in Type 3 glyph"); } return; } if (t3Font->cacheTags == nullptr) { return; } // allocate a cache entry i = (t3GlyphStack->code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc; for (j = 0; j < t3Font->cacheAssoc; ++j) { if ((t3Font->cacheTags[i + j].mru & 0x7fff) == t3Font->cacheAssoc - 1) { t3Font->cacheTags[i + j].mru = 0x8000; t3Font->cacheTags[i + j].code = t3GlyphStack->code; t3GlyphStack->cacheTag = &t3Font->cacheTags[i + j]; t3GlyphStack->cacheData = t3Font->cacheData + (i + j) * t3Font->glyphSize; } else { ++t3Font->cacheTags[i + j].mru; } } // save state t3GlyphStack->origBitmap = bitmap; t3GlyphStack->origSplash = splash; const double *ctm = state->getCTM(); t3GlyphStack->origCTM4 = ctm[4]; t3GlyphStack->origCTM5 = ctm[5]; // create the temporary bitmap if (colorMode == splashModeMono1) { bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1, splashModeMono1, false); splash = new Splash(bitmap, false, t3GlyphStack->origSplash->getScreen()); color[0] = 0; splash->clear(color); color[0] = 0xff; } else { bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1, splashModeMono8, false); splash = new Splash(bitmap, vectorAntialias, t3GlyphStack->origSplash->getScreen()); color[0] = 0x00; splash->clear(color); color[0] = 0xff; } splash->setMinLineWidth(s_minLineWidth); splash->setThinLineMode(splashThinLineDefault); splash->setFillPattern(new SplashSolidColor(color)); splash->setStrokePattern(new SplashSolidColor(color)); //~ this should copy other state from t3GlyphStack->origSplash? state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3], -t3Font->glyphX, -t3Font->glyphY); updateCTM(state, 0, 0, 0, 0, 0, 0); } void SplashOutputDev::drawType3Glyph(GfxState *state, T3FontCache *t3Font, T3FontCacheTag * /*tag*/, unsigned char *data) { SplashGlyphBitmap glyph; setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), state->getOverprintMode(), state->getFillColor()); glyph.x = -t3Font->glyphX; glyph.y = -t3Font->glyphY; glyph.w = t3Font->glyphW; glyph.h = t3Font->glyphH; glyph.aa = colorMode != splashModeMono1; glyph.data = data; glyph.freeData = false; splash->fillGlyph(0, 0, &glyph); } void SplashOutputDev::beginTextObject(GfxState *state) { } void SplashOutputDev::endTextObject(GfxState *state) { if (textClipPath) { splash->clipToPath(textClipPath, false); delete textClipPath; textClipPath = nullptr; } } struct SplashOutImageMaskData { ImageStream *imgStr; bool invert; int width, height, y; }; bool SplashOutputDev::imageMaskSrc(void *data, SplashColorPtr line) { SplashOutImageMaskData *imgMaskData = (SplashOutImageMaskData *)data; unsigned char *p; SplashColorPtr q; int x; if (imgMaskData->y == imgMaskData->height) { return false; } if (!(p = imgMaskData->imgStr->getLine())) { return false; } for (x = 0, q = line; x < imgMaskData->width; ++x) { *q++ = *p++ ^ imgMaskData->invert; } ++imgMaskData->y; return true; } void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg) { SplashCoord mat[6]; SplashOutImageMaskData imgMaskData; if (state->getFillColorSpace()->isNonMarking()) { return; } setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), state->getOverprintMode(), state->getFillColor()); const double *ctm = state->getCTM(); for (int i = 0; i < 6; ++i) { if (!std::isfinite(ctm[i])) { return; } } mat[0] = ctm[0]; mat[1] = ctm[1]; mat[2] = -ctm[2]; 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; imgMaskData.y = 0; splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat, t3GlyphStack != nullptr); if (inlineImg) { while (imgMaskData.y < height) { imgMaskData.imgStr->getLine(); ++imgMaskData.y; } } delete imgMaskData.imgStr; str->close(); } void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool inlineImg, double *baseMatrix) { const double *ctm; SplashCoord mat[6]; SplashOutImageMaskData imgMaskData; Splash *maskSplash; SplashColor maskColor; double bbox[4] = { 0, 0, 1, 1 }; // default; if (state->getFillColorSpace()->isNonMarking()) { return; } ctm = state->getCTM(); for (int i = 0; i < 6; ++i) { if (!std::isfinite(ctm[i])) { return; } } beginTransparencyGroup(state, bbox, nullptr, false, false, false); baseMatrix[4] -= transpGroupStack->tx; baseMatrix[5] -= transpGroupStack->ty; ctm = state->getCTM(); mat[0] = ctm[0]; mat[1] = ctm[1]; mat[2] = -ctm[2]; 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; imgMaskData.y = 0; transpGroupStack->softmask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), 1, splashModeMono8, false); maskSplash = new Splash(transpGroupStack->softmask, vectorAntialias); maskColor[0] = 0; maskSplash->clear(maskColor); maskColor[0] = 0xff; maskSplash->setFillPattern(new SplashSolidColor(maskColor)); maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat, t3GlyphStack != nullptr); delete maskSplash; delete imgMaskData.imgStr; str->close(); } void SplashOutputDev::unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix) { double bbox[4] = { 0, 0, 1, 1 }; // dummy if (!transpGroupStack) { return; } /* transfer mask to alpha channel! */ // memcpy(maskBitmap->getAlphaPtr(), maskBitmap->getDataPtr(), bitmap->getRowSize() * bitmap->getHeight()); // memset(maskBitmap->getDataPtr(), 0, bitmap->getRowSize() * bitmap->getHeight()); if (transpGroupStack->softmask != nullptr) { unsigned char *dest = bitmap->getAlphaPtr(); unsigned char *src = transpGroupStack->softmask->getDataPtr(); for (int c = 0; c < transpGroupStack->softmask->getRowSize() * transpGroupStack->softmask->getHeight(); c++) { dest[c] = src[c]; } delete transpGroupStack->softmask; transpGroupStack->softmask = nullptr; } endTransparencyGroup(state); baseMatrix[4] += transpGroupStack->tx; baseMatrix[5] += transpGroupStack->ty; paintTransparencyGroup(state, bbox); } struct SplashOutImageData { ImageStream *imgStr; GfxImageColorMap *colorMap; SplashColorPtr lookup; const int *maskColors; SplashColorMode colorMode; int width, height, y; ImageStream *maskStr; GfxImageColorMap *maskColorMap; SplashColor matteColor; }; #ifdef USE_CMS bool SplashOutputDev::useIccImageSrc(void *data) { SplashOutImageData *imgData = (SplashOutImageData *)data; if (!imgData->lookup && imgData->colorMap->getColorSpace()->getMode() == csICCBased && imgData->colorMap->getBits() != 1) { GfxICCBasedColorSpace *colorSpace = (GfxICCBasedColorSpace *)imgData->colorMap->getColorSpace(); switch (imgData->colorMode) { case splashModeMono1: case splashModeMono8: if (colorSpace->getAlt() != nullptr && colorSpace->getAlt()->getMode() == csDeviceGray) { return true; } break; case splashModeXBGR8: case splashModeRGB8: case splashModeBGR8: if (colorSpace->getAlt() != nullptr && colorSpace->getAlt()->getMode() == csDeviceRGB) { return true; } break; case splashModeCMYK8: if (colorSpace->getAlt() != nullptr && colorSpace->getAlt()->getMode() == csDeviceCMYK) { return true; } break; case splashModeDeviceN8: if (colorSpace->getAlt() != nullptr && colorSpace->getAlt()->getMode() == csDeviceN) { return true; } break; } } return false; } #endif // Clip x to lie in [0, 255]. static inline unsigned char clip255(int x) { return x < 0 ? 0 : x > 255 ? 255 : x; } bool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine, unsigned char * /*alphaLine*/) { SplashOutImageData *imgData = (SplashOutImageData *)data; unsigned char *p; SplashColorPtr q, col; GfxRGB rgb; GfxGray gray; GfxCMYK cmyk; GfxColor deviceN; int nComps, x; if (imgData->y == imgData->height) { return false; } if (!(p = imgData->imgStr->getLine())) { int destComps = 1; if (imgData->colorMode == splashModeRGB8 || imgData->colorMode == splashModeBGR8) { destComps = 3; } else if (imgData->colorMode == splashModeXBGR8) { destComps = 4; } else if (imgData->colorMode == splashModeCMYK8) { destComps = 4; } else if (imgData->colorMode == splashModeDeviceN8) { destComps = SPOT_NCOMPS + 4; } memset(colorLine, 0, imgData->width * destComps); return false; } nComps = imgData->colorMap->getNumPixelComps(); if (imgData->lookup) { switch (imgData->colorMode) { case splashModeMono1: case splashModeMono8: for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { *q++ = imgData->lookup[*p]; } break; case splashModeRGB8: case splashModeBGR8: for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { col = &imgData->lookup[3 * *p]; *q++ = col[0]; *q++ = col[1]; *q++ = col[2]; } break; case splashModeXBGR8: for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { col = &imgData->lookup[4 * *p]; *q++ = col[0]; *q++ = col[1]; *q++ = col[2]; *q++ = col[3]; } break; case splashModeCMYK8: for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { col = &imgData->lookup[4 * *p]; *q++ = col[0]; *q++ = col[1]; *q++ = col[2]; *q++ = col[3]; } break; case splashModeDeviceN8: for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { col = &imgData->lookup[(SPOT_NCOMPS + 4) * *p]; for (int cp = 0; cp < SPOT_NCOMPS + 4; cp++) { *q++ = col[cp]; } } break; } } else { switch (imgData->colorMode) { case splashModeMono1: case splashModeMono8: for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) { imgData->colorMap->getGray(p, &gray); *q++ = colToByte(gray); } break; case splashModeRGB8: case splashModeBGR8: if (imgData->colorMap->useRGBLine()) { imgData->colorMap->getRGBLine(p, (unsigned char *)colorLine, imgData->width); } else { for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) { imgData->colorMap->getRGB(p, &rgb); *q++ = colToByte(rgb.r); *q++ = colToByte(rgb.g); *q++ = colToByte(rgb.b); } } break; case splashModeXBGR8: if (imgData->colorMap->useRGBLine()) { imgData->colorMap->getRGBXLine(p, (unsigned char *)colorLine, imgData->width); } else { for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) { imgData->colorMap->getRGB(p, &rgb); *q++ = colToByte(rgb.r); *q++ = colToByte(rgb.g); *q++ = colToByte(rgb.b); *q++ = 255; } } break; case splashModeCMYK8: if (imgData->colorMap->useCMYKLine()) { imgData->colorMap->getCMYKLine(p, (unsigned char *)colorLine, imgData->width); } else { for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) { imgData->colorMap->getCMYK(p, &cmyk); *q++ = colToByte(cmyk.c); *q++ = colToByte(cmyk.m); *q++ = colToByte(cmyk.y); *q++ = colToByte(cmyk.k); } } break; case splashModeDeviceN8: if (imgData->colorMap->useDeviceNLine()) { imgData->colorMap->getDeviceNLine(p, (unsigned char *)colorLine, imgData->width); } else { for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) { imgData->colorMap->getDeviceN(p, &deviceN); for (int cp = 0; cp < SPOT_NCOMPS + 4; cp++) { *q++ = colToByte(deviceN.c[cp]); } } } break; } } if (imgData->maskStr != nullptr && (p = imgData->maskStr->getLine()) != nullptr) { int destComps = splashColorModeNComps[imgData->colorMode]; int convComps = (imgData->colorMode == splashModeXBGR8) ? 3 : destComps; imgData->maskColorMap->getGrayLine(p, p, imgData->width); for (x = 0, q = colorLine; x < imgData->width; ++x, p++, q += destComps) { for (int cp = 0; cp < convComps; cp++) { q[cp] = (*p) ? clip255(imgData->matteColor[cp] + (int)(q[cp] - imgData->matteColor[cp]) * 255 / *p) : imgData->matteColor[cp]; } } } ++imgData->y; return true; } #ifdef USE_CMS bool SplashOutputDev::iccImageSrc(void *data, SplashColorPtr colorLine, unsigned char * /*alphaLine*/) { SplashOutImageData *imgData = (SplashOutImageData *)data; unsigned char *p; int nComps; if (imgData->y == imgData->height) { return false; } if (!(p = imgData->imgStr->getLine())) { int destComps = 1; if (imgData->colorMode == splashModeRGB8 || imgData->colorMode == splashModeBGR8) { destComps = 3; } else if (imgData->colorMode == splashModeXBGR8) { destComps = 4; } else if (imgData->colorMode == splashModeCMYK8) { destComps = 4; } else if (imgData->colorMode == splashModeDeviceN8) { destComps = SPOT_NCOMPS + 4; } memset(colorLine, 0, imgData->width * destComps); return false; } if (imgData->colorMode == splashModeXBGR8) { SplashColorPtr q; int x; for (x = 0, q = colorLine; x < imgData->width; ++x) { *q++ = *p++; *q++ = *p++; *q++ = *p++; *q++ = 255; } } else { nComps = imgData->colorMap->getNumPixelComps(); memcpy(colorLine, p, imgData->width * nComps); } ++imgData->y; return true; } void SplashOutputDev::iccTransform(void *data, SplashBitmap *bitmap) { SplashOutImageData *imgData = (SplashOutImageData *)data; int nComps = imgData->colorMap->getNumPixelComps(); unsigned char *colorLine = (unsigned char *)gmalloc(nComps * bitmap->getWidth()); unsigned char *rgbxLine = (imgData->colorMode == splashModeXBGR8) ? (unsigned char *)gmalloc(3 * bitmap->getWidth()) : nullptr; for (int i = 0; i < bitmap->getHeight(); i++) { unsigned char *p = bitmap->getDataPtr() + i * bitmap->getRowSize(); switch (imgData->colorMode) { case splashModeMono1: case splashModeMono8: imgData->colorMap->getGrayLine(p, colorLine, bitmap->getWidth()); memcpy(p, colorLine, nComps * bitmap->getWidth()); break; case splashModeRGB8: case splashModeBGR8: imgData->colorMap->getRGBLine(p, colorLine, bitmap->getWidth()); memcpy(p, colorLine, nComps * bitmap->getWidth()); break; case splashModeCMYK8: imgData->colorMap->getCMYKLine(p, colorLine, bitmap->getWidth()); memcpy(p, colorLine, nComps * bitmap->getWidth()); break; case splashModeDeviceN8: imgData->colorMap->getDeviceNLine(p, colorLine, bitmap->getWidth()); memcpy(p, colorLine, nComps * bitmap->getWidth()); break; case splashModeXBGR8: unsigned char *q; unsigned char *b = p; int x; for (x = 0, q = rgbxLine; x < bitmap->getWidth(); ++x, b += 4) { *q++ = b[2]; *q++ = b[1]; *q++ = b[0]; } imgData->colorMap->getRGBLine(rgbxLine, colorLine, bitmap->getWidth()); b = p; for (x = 0, q = colorLine; x < bitmap->getWidth(); ++x, b += 4) { b[2] = *q++; b[1] = *q++; b[0] = *q++; } break; } } gfree(colorLine); if (rgbxLine != nullptr) { gfree(rgbxLine); } } #endif bool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine, unsigned char *alphaLine) { SplashOutImageData *imgData = (SplashOutImageData *)data; unsigned char *p, *aq; SplashColorPtr q, col; GfxRGB rgb; GfxGray gray; GfxCMYK cmyk; GfxColor deviceN; unsigned char alpha; int nComps, x, i; if (imgData->y == imgData->height) { return false; } if (!(p = imgData->imgStr->getLine())) { return false; } nComps = imgData->colorMap->getNumPixelComps(); for (x = 0, q = colorLine, aq = alphaLine; x < imgData->width; ++x, p += nComps) { alpha = 0; for (i = 0; i < nComps; ++i) { if (p[i] < imgData->maskColors[2 * i] || p[i] > imgData->maskColors[2 * i + 1]) { alpha = 0xff; break; } } if (imgData->lookup) { switch (imgData->colorMode) { case splashModeMono1: case splashModeMono8: *q++ = imgData->lookup[*p]; break; case splashModeRGB8: case splashModeBGR8: col = &imgData->lookup[3 * *p]; *q++ = col[0]; *q++ = col[1]; *q++ = col[2]; break; case splashModeXBGR8: col = &imgData->lookup[4 * *p]; *q++ = col[0]; *q++ = col[1]; *q++ = col[2]; *q++ = 255; break; case splashModeCMYK8: col = &imgData->lookup[4 * *p]; *q++ = col[0]; *q++ = col[1]; *q++ = col[2]; *q++ = col[3]; break; case splashModeDeviceN8: col = &imgData->lookup[(SPOT_NCOMPS + 4) * *p]; for (int cp = 0; cp < SPOT_NCOMPS + 4; cp++) { *q++ = col[cp]; } break; } *aq++ = alpha; } else { switch (imgData->colorMode) { case splashModeMono1: case splashModeMono8: imgData->colorMap->getGray(p, &gray); *q++ = colToByte(gray); break; case splashModeXBGR8: case splashModeRGB8: case splashModeBGR8: imgData->colorMap->getRGB(p, &rgb); *q++ = colToByte(rgb.r); *q++ = colToByte(rgb.g); *q++ = colToByte(rgb.b); if (imgData->colorMode == splashModeXBGR8) { *q++ = 255; } break; case splashModeCMYK8: imgData->colorMap->getCMYK(p, &cmyk); *q++ = colToByte(cmyk.c); *q++ = colToByte(cmyk.m); *q++ = colToByte(cmyk.y); *q++ = colToByte(cmyk.k); break; case splashModeDeviceN8: imgData->colorMap->getDeviceN(p, &deviceN); for (int cp = 0; cp < SPOT_NCOMPS + 4; cp++) { *q++ = colToByte(deviceN.c[cp]); } break; } *aq++ = alpha; } } ++imgData->y; return true; } struct TilingSplashOutBitmap { SplashBitmap *bitmap; SplashPattern *pattern; SplashColorMode colorMode; int paintType; int repeatX; int repeatY; int y; }; bool SplashOutputDev::tilingBitmapSrc(void *data, SplashColorPtr colorLine, unsigned char *alphaLine) { TilingSplashOutBitmap *imgData = (TilingSplashOutBitmap *)data; if (imgData->y == imgData->bitmap->getHeight()) { imgData->repeatY--; if (imgData->repeatY == 0) { return false; } imgData->y = 0; } if (imgData->paintType == 1) { const SplashColorMode cMode = imgData->bitmap->getMode(); SplashColorPtr q = colorLine; // For splashModeBGR8 and splashModeXBGR8 we need to use getPixel // for the others we can use raw access if (cMode == splashModeBGR8 || cMode == splashModeXBGR8) { for (int m = 0; m < imgData->repeatX; m++) { for (int x = 0; x < imgData->bitmap->getWidth(); x++) { imgData->bitmap->getPixel(x, imgData->y, q); q += splashColorModeNComps[cMode]; } } } else { const int n = imgData->bitmap->getRowSize(); SplashColorPtr p; for (int m = 0; m < imgData->repeatX; m++) { p = imgData->bitmap->getDataPtr() + imgData->y * imgData->bitmap->getRowSize(); for (int x = 0; x < n; ++x) { *q++ = *p++; } } } if (alphaLine != nullptr) { SplashColorPtr aq = alphaLine; SplashColorPtr p; const int n = imgData->bitmap->getWidth() - 1; for (int m = 0; m < imgData->repeatX; m++) { p = imgData->bitmap->getAlphaPtr() + imgData->y * imgData->bitmap->getWidth(); for (int x = 0; x < n; ++x) { *aq++ = *p++; } // This is a hack, because of how Splash antialias works if we overwrite the // last alpha pixel of the tile most/all of the files look much better *aq++ = (n == 0) ? *p : *(p - 1); } } } else { SplashColor col, pat; SplashColorPtr dest = colorLine; for (int m = 0; m < imgData->repeatX; m++) { for (int x = 0; x < imgData->bitmap->getWidth(); x++) { imgData->bitmap->getPixel(x, imgData->y, col); imgData->pattern->getColor(x, imgData->y, pat); for (int i = 0; i < splashColorModeNComps[imgData->colorMode]; ++i) { if (imgData->colorMode == splashModeCMYK8 || imgData->colorMode == splashModeDeviceN8) { dest[i] = div255(pat[i] * (255 - col[0])); } else { dest[i] = 255 - div255((255 - pat[i]) * (255 - col[0])); } } dest += splashColorModeNComps[imgData->colorMode]; } } if (alphaLine != nullptr) { const int y = (imgData->y == imgData->bitmap->getHeight() - 1 && imgData->y > 50) ? imgData->y - 1 : imgData->y; SplashColorPtr aq = alphaLine; SplashColorPtr p; const int n = imgData->bitmap->getWidth(); for (int m = 0; m < imgData->repeatX; m++) { p = imgData->bitmap->getAlphaPtr() + y * imgData->bitmap->getWidth(); for (int x = 0; x < n; ++x) { *aq++ = *p++; } } } } ++imgData->y; return true; } void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) { SplashCoord mat[6]; SplashOutImageData imgData; SplashColorMode srcMode; SplashImageSource src; SplashICCTransform tf; GfxGray gray; GfxRGB rgb; GfxCMYK cmyk; bool grayIndexed = false; GfxColor deviceN; unsigned char pix; int n, i; const double *ctm = state->getCTM(); for (i = 0; i < 6; ++i) { if (!std::isfinite(ctm[i])) { return; } } mat[0] = ctm[0]; mat[1] = ctm[1]; mat[2] = -ctm[2]; mat[3] = -ctm[3]; 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; // special case for one-channel (monochrome/gray/separation) images: // build a lookup table here imgData.lookup = nullptr; if (colorMap->getNumPixelComps() == 1) { n = 1 << colorMap->getBits(); switch (colorMode) { case splashModeMono1: case splashModeMono8: imgData.lookup = (SplashColorPtr)gmalloc_checkoverflow(n); if (likely(imgData.lookup != nullptr)) { for (i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getGray(&pix, &gray); imgData.lookup[i] = colToByte(gray); } } break; case splashModeRGB8: case splashModeBGR8: imgData.lookup = (SplashColorPtr)gmallocn_checkoverflow(n, 3); if (likely(imgData.lookup != nullptr)) { for (i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getRGB(&pix, &rgb); imgData.lookup[3 * i] = colToByte(rgb.r); imgData.lookup[3 * i + 1] = colToByte(rgb.g); imgData.lookup[3 * i + 2] = colToByte(rgb.b); } } break; case splashModeXBGR8: imgData.lookup = (SplashColorPtr)gmallocn_checkoverflow(n, 4); if (likely(imgData.lookup != nullptr)) { for (i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getRGB(&pix, &rgb); imgData.lookup[4 * i] = colToByte(rgb.r); imgData.lookup[4 * i + 1] = colToByte(rgb.g); imgData.lookup[4 * i + 2] = colToByte(rgb.b); imgData.lookup[4 * i + 3] = 255; } } break; case splashModeCMYK8: grayIndexed = colorMap->getColorSpace()->getMode() != csDeviceGray; imgData.lookup = (SplashColorPtr)gmallocn_checkoverflow(n, 4); if (likely(imgData.lookup != nullptr)) { for (i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getCMYK(&pix, &cmyk); if (cmyk.c != 0 || cmyk.m != 0 || cmyk.y != 0) { grayIndexed = false; } imgData.lookup[4 * i] = colToByte(cmyk.c); imgData.lookup[4 * i + 1] = colToByte(cmyk.m); imgData.lookup[4 * i + 2] = colToByte(cmyk.y); imgData.lookup[4 * i + 3] = colToByte(cmyk.k); } } break; case splashModeDeviceN8: colorMap->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); grayIndexed = colorMap->getColorSpace()->getMode() != csDeviceGray; imgData.lookup = (SplashColorPtr)gmallocn_checkoverflow(n, SPOT_NCOMPS + 4); if (likely(imgData.lookup != nullptr)) { for (i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getCMYK(&pix, &cmyk); if (cmyk.c != 0 || cmyk.m != 0 || cmyk.y != 0) { grayIndexed = false; } colorMap->getDeviceN(&pix, &deviceN); for (int cp = 0; cp < SPOT_NCOMPS + 4; cp++) { imgData.lookup[(SPOT_NCOMPS + 4) * i + cp] = colToByte(deviceN.c[cp]); } } } break; } } setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(), state->getOverprintMode(), nullptr, grayIndexed); if (colorMode == splashModeMono1) { srcMode = splashModeMono8; } else { srcMode = colorMode; } #ifdef USE_CMS src = maskColors ? &alphaImageSrc : useIccImageSrc(&imgData) ? &iccImageSrc : &imageSrc; tf = maskColors == nullptr && useIccImageSrc(&imgData) ? &iccTransform : nullptr; #else src = maskColors ? &alphaImageSrc : &imageSrc; tf = nullptr; #endif splash->drawImage(src, tf, &imgData, srcMode, maskColors ? true : false, width, height, mat, interpolate); if (inlineImg) { while (imgData.y < height) { imgData.imgStr->getLine(); ++imgData.y; } } gfree(imgData.lookup); delete imgData.imgStr; str->close(); } struct SplashOutMaskedImageData { ImageStream *imgStr; GfxImageColorMap *colorMap; SplashBitmap *mask; SplashColorPtr lookup; SplashColorMode colorMode; int width, height, y; }; bool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine, unsigned char *alphaLine) { SplashOutMaskedImageData *imgData = (SplashOutMaskedImageData *)data; unsigned char *p, *aq; SplashColorPtr q, col; GfxRGB rgb; GfxGray gray; GfxCMYK cmyk; GfxColor deviceN; unsigned char alpha; unsigned char *maskPtr; int maskBit; int nComps, x; if (imgData->y == imgData->height) { return false; } if (!(p = imgData->imgStr->getLine())) { return false; } nComps = imgData->colorMap->getNumPixelComps(); maskPtr = imgData->mask->getDataPtr() + imgData->y * imgData->mask->getRowSize(); maskBit = 0x80; for (x = 0, q = colorLine, aq = alphaLine; x < imgData->width; ++x, p += nComps) { alpha = (*maskPtr & maskBit) ? 0xff : 0x00; if (!(maskBit >>= 1)) { ++maskPtr; maskBit = 0x80; } if (imgData->lookup) { switch (imgData->colorMode) { case splashModeMono1: case splashModeMono8: *q++ = imgData->lookup[*p]; break; case splashModeRGB8: case splashModeBGR8: col = &imgData->lookup[3 * *p]; *q++ = col[0]; *q++ = col[1]; *q++ = col[2]; break; case splashModeXBGR8: col = &imgData->lookup[4 * *p]; *q++ = col[0]; *q++ = col[1]; *q++ = col[2]; *q++ = 255; break; case splashModeCMYK8: col = &imgData->lookup[4 * *p]; *q++ = col[0]; *q++ = col[1]; *q++ = col[2]; *q++ = col[3]; break; case splashModeDeviceN8: col = &imgData->lookup[(SPOT_NCOMPS + 4) * *p]; for (int cp = 0; cp < SPOT_NCOMPS + 4; cp++) { *q++ = col[cp]; } break; } *aq++ = alpha; } else { switch (imgData->colorMode) { case splashModeMono1: case splashModeMono8: imgData->colorMap->getGray(p, &gray); *q++ = colToByte(gray); break; case splashModeXBGR8: case splashModeRGB8: case splashModeBGR8: imgData->colorMap->getRGB(p, &rgb); *q++ = colToByte(rgb.r); *q++ = colToByte(rgb.g); *q++ = colToByte(rgb.b); if (imgData->colorMode == splashModeXBGR8) { *q++ = 255; } break; case splashModeCMYK8: imgData->colorMap->getCMYK(p, &cmyk); *q++ = colToByte(cmyk.c); *q++ = colToByte(cmyk.m); *q++ = colToByte(cmyk.y); *q++ = colToByte(cmyk.k); break; case splashModeDeviceN8: imgData->colorMap->getDeviceN(p, &deviceN); for (int cp = 0; cp < SPOT_NCOMPS + 4; cp++) { *q++ = colToByte(deviceN.c[cp]); } break; } *aq++ = alpha; } } ++imgData->y; return true; } 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; GfxCMYK cmyk; GfxColor deviceN; unsigned char pix; int n, i; colorMap->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(), state->getOverprintMode(), nullptr); // If the mask is higher resolution than the image, use // drawSoftMaskedImage() instead. if (maskWidth > width || maskHeight > height) { 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; } else { //----- scale the mask image to the same size as the source image mat[0] = (SplashCoord)width; mat[1] = 0; mat[2] = 0; mat[3] = (SplashCoord)height; mat[4] = 0; mat[5] = 0; imgMaskData.imgStr = new ImageStream(maskStr, maskWidth, 1, 1); imgMaskData.imgStr->reset(); imgMaskData.invert = maskInvert ? false : true; imgMaskData.width = maskWidth; imgMaskData.height = maskHeight; imgMaskData.y = 0; maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1, false); if (!maskBitmap->getDataPtr()) { delete maskBitmap; width = height = 1; maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1, false); } 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; } } mat[0] = ctm[0]; mat[1] = ctm[1]; mat[2] = -ctm[2]; mat[3] = -ctm[3]; 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.mask = maskBitmap; imgData.colorMode = colorMode; imgData.width = width; imgData.height = height; imgData.y = 0; // special case for one-channel (monochrome/gray/separation) images: // build a lookup table here imgData.lookup = nullptr; if (colorMap->getNumPixelComps() == 1) { n = 1 << colorMap->getBits(); switch (colorMode) { case splashModeMono1: case splashModeMono8: imgData.lookup = (SplashColorPtr)gmalloc(n); for (i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getGray(&pix, &gray); imgData.lookup[i] = colToByte(gray); } break; case splashModeRGB8: case splashModeBGR8: imgData.lookup = (SplashColorPtr)gmallocn(n, 3); for (i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getRGB(&pix, &rgb); imgData.lookup[3 * i] = colToByte(rgb.r); imgData.lookup[3 * i + 1] = colToByte(rgb.g); imgData.lookup[3 * i + 2] = colToByte(rgb.b); } break; case splashModeXBGR8: imgData.lookup = (SplashColorPtr)gmallocn(n, 4); for (i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getRGB(&pix, &rgb); imgData.lookup[4 * i] = colToByte(rgb.r); imgData.lookup[4 * i + 1] = colToByte(rgb.g); imgData.lookup[4 * i + 2] = colToByte(rgb.b); imgData.lookup[4 * i + 3] = 255; } break; case splashModeCMYK8: imgData.lookup = (SplashColorPtr)gmallocn(n, 4); for (i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getCMYK(&pix, &cmyk); imgData.lookup[4 * i] = colToByte(cmyk.c); imgData.lookup[4 * i + 1] = colToByte(cmyk.m); imgData.lookup[4 * i + 2] = colToByte(cmyk.y); imgData.lookup[4 * i + 3] = colToByte(cmyk.k); } break; case splashModeDeviceN8: imgData.lookup = (SplashColorPtr)gmallocn(n, SPOT_NCOMPS + 4); for (i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getDeviceN(&pix, &deviceN); for (int cp = 0; cp < SPOT_NCOMPS + 4; cp++) { imgData.lookup[(SPOT_NCOMPS + 4) * i + cp] = colToByte(deviceN.c[cp]); } } break; } } if (colorMode == splashModeMono1) { srcMode = splashModeMono8; } else { srcMode = colorMode; } splash->drawImage(&maskedImageSrc, nullptr, &imgData, srcMode, true, width, height, mat, interpolate); delete maskBitmap; gfree(imgData.lookup); delete imgData.imgStr; str->close(); } } void SplashOutputDev::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) { SplashCoord mat[6]; SplashOutImageData imgData; SplashOutImageData imgMaskData; SplashColorMode srcMode; SplashBitmap *maskBitmap; Splash *maskSplash; SplashColor maskColor; GfxGray gray; GfxRGB rgb; GfxCMYK cmyk; GfxColor deviceN; unsigned char pix; colorMap->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(), state->getOverprintMode(), nullptr); const double *ctm = state->getCTM(); for (int i = 0; i < 6; ++i) { if (!std::isfinite(ctm[i])) { return; } } mat[0] = ctm[0]; mat[1] = ctm[1]; mat[2] = -ctm[2]; mat[3] = -ctm[3]; mat[4] = ctm[2] + ctm[4]; mat[5] = ctm[3] + ctm[5]; //----- set up the soft mask if (maskColorMap->getMatteColor() != nullptr) { int maskChars; if (checkedMultiply(maskWidth, maskHeight, &maskChars)) { 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()); } 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); for (unsigned i = 0; i < imgMaskDataLookupSize; ++i) { pix = (unsigned char)i; maskColorMap->getGray(&pix, &gray); 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(); } 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.colorMap = colorMap; imgData.maskColors = nullptr; imgData.colorMode = colorMode; imgData.width = width; imgData.height = height; imgData.maskStr = nullptr; imgData.maskColorMap = nullptr; 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.y = 0; // special case for one-channel (monochrome/gray/separation) images: // build a lookup table here imgData.lookup = nullptr; if (colorMap->getNumPixelComps() == 1) { const unsigned n = 1 << colorMap->getBits(); switch (colorMode) { case splashModeMono1: case splashModeMono8: imgData.lookup = (SplashColorPtr)gmalloc(n); for (unsigned i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getGray(&pix, &gray); imgData.lookup[i] = colToByte(gray); } break; case splashModeRGB8: case splashModeBGR8: imgData.lookup = (SplashColorPtr)gmallocn_checkoverflow(n, 3); if (likely(imgData.lookup != nullptr)) { for (unsigned i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getRGB(&pix, &rgb); imgData.lookup[3 * i] = colToByte(rgb.r); imgData.lookup[3 * i + 1] = colToByte(rgb.g); imgData.lookup[3 * i + 2] = colToByte(rgb.b); } } break; case splashModeXBGR8: imgData.lookup = (SplashColorPtr)gmallocn_checkoverflow(n, 4); if (likely(imgData.lookup != nullptr)) { for (unsigned i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getRGB(&pix, &rgb); imgData.lookup[4 * i] = colToByte(rgb.r); imgData.lookup[4 * i + 1] = colToByte(rgb.g); imgData.lookup[4 * i + 2] = colToByte(rgb.b); imgData.lookup[4 * i + 3] = 255; } } break; case splashModeCMYK8: imgData.lookup = (SplashColorPtr)gmallocn_checkoverflow(n, 4); if (likely(imgData.lookup != nullptr)) { for (unsigned i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getCMYK(&pix, &cmyk); imgData.lookup[4 * i] = colToByte(cmyk.c); imgData.lookup[4 * i + 1] = colToByte(cmyk.m); imgData.lookup[4 * i + 2] = colToByte(cmyk.y); imgData.lookup[4 * i + 3] = colToByte(cmyk.k); } } break; case splashModeDeviceN8: imgData.lookup = (SplashColorPtr)gmallocn_checkoverflow(n, SPOT_NCOMPS + 4); if (likely(imgData.lookup != nullptr)) { for (unsigned i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getDeviceN(&pix, &deviceN); for (int cp = 0; cp < SPOT_NCOMPS + 4; cp++) { imgData.lookup[(SPOT_NCOMPS + 4) * i + cp] = colToByte(deviceN.c[cp]); } } } break; } } if (colorMode == splashModeMono1) { srcMode = splashModeMono8; } else { srcMode = colorMode; } 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; } str->close(); } bool SplashOutputDev::checkTransparencyGroup(GfxState *state, bool knockout) { if (state->getFillOpacity() != 1 || state->getStrokeOpacity() != 1 || state->getAlphaIsShape() || state->getBlendMode() != gfxBlendNormal || splash->getSoftMask() != nullptr || knockout) { return true; } return transpGroupStack != nullptr && transpGroupStack->shape != nullptr; } void SplashOutputDev::beginTransparencyGroup(GfxState *state, const double *bbox, GfxColorSpace *blendingColorSpace, bool isolated, bool knockout, bool forSoftMask) { SplashTransparencyGroup *transpGroup; SplashColor color; double xMin, yMin, xMax, yMax, x, y; int tx, ty, w, h; // transform the bbox state->transform(bbox[0], bbox[1], &x, &y); xMin = xMax = x; yMin = yMax = y; state->transform(bbox[0], bbox[3], &x, &y); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } state->transform(bbox[2], bbox[1], &x, &y); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } state->transform(bbox[2], bbox[3], &x, &y); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } tx = (int)floor(xMin); if (tx < 0) { tx = 0; } else if (tx >= bitmap->getWidth()) { tx = bitmap->getWidth() - 1; } ty = (int)floor(yMin); if (ty < 0) { ty = 0; } else if (ty >= bitmap->getHeight()) { ty = bitmap->getHeight() - 1; } w = (int)ceil(xMax) - tx + 1; if (tx + w > bitmap->getWidth()) { w = bitmap->getWidth() - tx; } if (w < 1) { w = 1; } h = (int)ceil(yMax) - ty + 1; if (ty + h > bitmap->getHeight()) { h = bitmap->getHeight() - ty; } if (h < 1) { h = 1; } // push a new stack entry transpGroup = new SplashTransparencyGroup(); transpGroup->softmask = nullptr; transpGroup->tx = tx; transpGroup->ty = ty; transpGroup->blendingColorSpace = blendingColorSpace; transpGroup->isolated = isolated; transpGroup->shape = (knockout && !isolated) ? SplashBitmap::copy(bitmap) : nullptr; transpGroup->knockout = (knockout && isolated); transpGroup->knockoutOpacity = 1.0; transpGroup->next = transpGroupStack; transpGroupStack = transpGroup; // save state transpGroup->origBitmap = bitmap; transpGroup->origSplash = splash; transpGroup->fontAA = fontEngine->getAA(); //~ this handles the blendingColorSpace arg for soft masks, but //~ not yet for transparency groups // switch to the blending color space if (forSoftMask && isolated && blendingColorSpace) { if (blendingColorSpace->getMode() == csDeviceGray || blendingColorSpace->getMode() == csCalGray || (blendingColorSpace->getMode() == csICCBased && blendingColorSpace->getNComps() == 1)) { colorMode = splashModeMono8; } else if (blendingColorSpace->getMode() == csDeviceRGB || blendingColorSpace->getMode() == csCalRGB || (blendingColorSpace->getMode() == csICCBased && blendingColorSpace->getNComps() == 3)) { //~ does this need to use BGR8? colorMode = splashModeRGB8; } else if (blendingColorSpace->getMode() == csDeviceCMYK || (blendingColorSpace->getMode() == csICCBased && blendingColorSpace->getNComps() == 4)) { colorMode = splashModeCMYK8; } } // create the temporary bitmap bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, true, bitmapTopDown, bitmap->getSeparationList()); if (!bitmap->getDataPtr()) { delete bitmap; w = h = 1; bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, true, bitmapTopDown); } splash = new Splash(bitmap, vectorAntialias, transpGroup->origSplash->getScreen()); if (transpGroup->next != nullptr && transpGroup->next->knockout) { fontEngine->setAA(false); } splash->setThinLineMode(transpGroup->origSplash->getThinLineMode()); splash->setMinLineWidth(s_minLineWidth); //~ Acrobat apparently copies at least the fill and stroke colors, and //~ maybe other state(?) -- but not the clipping path (and not sure //~ what else) //~ [this is likely the same situation as in type3D1()] splash->setFillPattern(transpGroup->origSplash->getFillPattern()->copy()); splash->setStrokePattern(transpGroup->origSplash->getStrokePattern()->copy()); if (isolated) { splashClearColor(color); if (colorMode == splashModeXBGR8) { color[3] = 255; } splash->clear(color, 0); } else { SplashBitmap *shape = (knockout) ? transpGroup->shape : (transpGroup->next != nullptr && transpGroup->next->shape != nullptr) ? transpGroup->next->shape : transpGroup->origBitmap; int shapeTx = (knockout) ? tx : (transpGroup->next != nullptr && transpGroup->next->shape != nullptr) ? transpGroup->next->tx + tx : tx; int shapeTy = (knockout) ? ty : (transpGroup->next != nullptr && transpGroup->next->shape != nullptr) ? transpGroup->next->ty + ty : ty; splash->blitTransparent(transpGroup->origBitmap, tx, ty, 0, 0, w, h); splash->setInNonIsolatedGroup(shape, shapeTx, shapeTy); } transpGroup->tBitmap = bitmap; state->shiftCTMAndClip(-tx, -ty); updateCTM(state, 0, 0, 0, 0, 0, 0); } void SplashOutputDev::endTransparencyGroup(GfxState *state) { // restore state delete splash; bitmap = transpGroupStack->origBitmap; colorMode = bitmap->getMode(); splash = transpGroupStack->origSplash; state->shiftCTMAndClip(transpGroupStack->tx, transpGroupStack->ty); updateCTM(state, 0, 0, 0, 0, 0, 0); } void SplashOutputDev::paintTransparencyGroup(GfxState *state, const double *bbox) { SplashBitmap *tBitmap; SplashTransparencyGroup *transpGroup; bool isolated; int tx, ty; tx = transpGroupStack->tx; ty = transpGroupStack->ty; tBitmap = transpGroupStack->tBitmap; isolated = transpGroupStack->isolated; // paint the transparency group onto the parent bitmap // - the clip path was set in the parent's state) if (tx < bitmap->getWidth() && ty < bitmap->getHeight()) { SplashCoord knockoutOpacity = (transpGroupStack->next != nullptr) ? transpGroupStack->next->knockoutOpacity : transpGroupStack->knockoutOpacity; splash->setOverprintMask(0xffffffff, false); splash->composite(tBitmap, 0, 0, tx, ty, tBitmap->getWidth(), tBitmap->getHeight(), false, !isolated, transpGroupStack->next != nullptr && transpGroupStack->next->knockout, knockoutOpacity); fontEngine->setAA(transpGroupStack->fontAA); if (transpGroupStack->next != nullptr && transpGroupStack->next->shape != nullptr) { transpGroupStack->next->knockout = true; } } // pop the stack transpGroup = transpGroupStack; transpGroupStack = transpGroup->next; if (transpGroupStack != nullptr && transpGroup->knockoutOpacity < transpGroupStack->knockoutOpacity) { transpGroupStack->knockoutOpacity = transpGroup->knockoutOpacity; } delete transpGroup->shape; delete transpGroup; delete tBitmap; } void SplashOutputDev::setSoftMask(GfxState *state, const double *bbox, bool alpha, Function *transferFunc, GfxColor *backdropColor) { SplashBitmap *softMask, *tBitmap; Splash *tSplash; SplashTransparencyGroup *transpGroup; SplashColor color; SplashColorPtr p; GfxGray gray; GfxRGB rgb; GfxCMYK cmyk; GfxColor deviceN; double lum, lum2; int tx, ty, x, y; tx = transpGroupStack->tx; ty = transpGroupStack->ty; tBitmap = transpGroupStack->tBitmap; // composite with backdrop color if (!alpha && tBitmap->getMode() != splashModeMono1) { //~ need to correctly handle the case where no blending color //~ space is given if (transpGroupStack->blendingColorSpace) { tSplash = new Splash(tBitmap, vectorAntialias, transpGroupStack->origSplash->getScreen()); switch (tBitmap->getMode()) { case splashModeMono1: // transparency is not supported in mono1 mode break; case splashModeMono8: transpGroupStack->blendingColorSpace->getGray(backdropColor, &gray); color[0] = colToByte(gray); tSplash->compositeBackground(color); break; case splashModeXBGR8: color[3] = 255; // fallthrough case splashModeRGB8: case splashModeBGR8: transpGroupStack->blendingColorSpace->getRGB(backdropColor, &rgb); color[0] = colToByte(rgb.r); color[1] = colToByte(rgb.g); color[2] = colToByte(rgb.b); tSplash->compositeBackground(color); break; case splashModeCMYK8: transpGroupStack->blendingColorSpace->getCMYK(backdropColor, &cmyk); color[0] = colToByte(cmyk.c); color[1] = colToByte(cmyk.m); color[2] = colToByte(cmyk.y); color[3] = colToByte(cmyk.k); tSplash->compositeBackground(color); break; case splashModeDeviceN8: transpGroupStack->blendingColorSpace->getDeviceN(backdropColor, &deviceN); for (int cp = 0; cp < SPOT_NCOMPS + 4; cp++) { color[cp] = colToByte(deviceN.c[cp]); } tSplash->compositeBackground(color); break; } delete tSplash; } } softMask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), 1, splashModeMono8, false); unsigned char fill = 0; if (transpGroupStack->blendingColorSpace) { transpGroupStack->blendingColorSpace->getGray(backdropColor, &gray); fill = colToByte(gray); } memset(softMask->getDataPtr(), fill, softMask->getRowSize() * softMask->getHeight()); 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 (yMax > bitmap->getHeight() - ty) { yMax = bitmap->getHeight() - ty; } for (y = 0; y < yMax; ++y) { for (x = 0; x < xMax; ++x) { if (alpha) { if (transferFunc) { lum = tBitmap->getAlpha(x, y) / 255.0; transferFunc->transform(&lum, &lum2); p[x] = (int)(lum2 * 255.0 + 0.5); } else { p[x] = tBitmap->getAlpha(x, y); } } else { tBitmap->getPixel(x, y, color); // convert to luminosity switch (tBitmap->getMode()) { case splashModeMono1: case splashModeMono8: lum = color[0] / 255.0; break; case splashModeXBGR8: case splashModeRGB8: case splashModeBGR8: lum = (0.3 / 255.0) * color[0] + (0.59 / 255.0) * color[1] + (0.11 / 255.0) * color[2]; break; case splashModeCMYK8: case splashModeDeviceN8: lum = (1 - color[3] / 255.0) - (0.3 / 255.0) * color[0] - (0.59 / 255.0) * color[1] - (0.11 / 255.0) * color[2]; if (lum < 0) { lum = 0; } break; } if (transferFunc) { transferFunc->transform(&lum, &lum2); } else { lum2 = lum; } p[x] = (int)(lum2 * 255.0 + 0.5); } } p += softMask->getRowSize(); } splash->setSoftMask(softMask); // pop the stack transpGroup = transpGroupStack; transpGroupStack = transpGroup->next; delete transpGroup; delete tBitmap; } void SplashOutputDev::clearSoftMask(GfxState *state) { splash->setSoftMask(nullptr); } void SplashOutputDev::setPaperColor(SplashColorPtr paperColorA) { splashColorCopy(paperColor, paperColorA); } int SplashOutputDev::getBitmapWidth() { return bitmap->getWidth(); } int SplashOutputDev::getBitmapHeight() { return bitmap->getHeight(); } SplashBitmap *SplashOutputDev::takeBitmap() { SplashBitmap *ret; ret = bitmap; bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode, colorMode != splashModeMono1, bitmapTopDown); return ret; } #if 1 //~tmp: turn off anti-aliasing temporarily bool SplashOutputDev::getVectorAntialias() { return splash->getVectorAntialias(); } void SplashOutputDev::setVectorAntialias(bool vaa) { vaa = vaa && colorMode != splashModeMono1; vectorAntialias = vaa; splash->setVectorAntialias(vaa); } #endif void SplashOutputDev::setFreeTypeHinting(bool enable, bool enableSlightHintingA) { enableFreeTypeHinting = enable; enableSlightHinting = enableSlightHintingA; } bool SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfxA, Catalog *catalog, GfxTilingPattern *tPat, const double *mat, int x0, int y0, int x1, int y1, double xStep, double yStep) { PDFRectangle box; Splash *formerSplash = splash; SplashBitmap *formerBitmap = bitmap; double width, height; int surface_width, surface_height, result_width, result_height, i; int repeatX, repeatY; SplashCoord matc[6]; Matrix m1; const double *ctm; double savedCTM[6]; double kx, ky, sx, sy; bool retValue = false; const double *bbox = tPat->getBBox(); const double *ptm = tPat->getMatrix(); const int paintType = tPat->getPaintType(); Dict *resDict = tPat->getResDict(); width = bbox[2] - bbox[0]; height = bbox[3] - bbox[1]; if (xStep != width || yStep != height) { return false; } // calculate offsets ctm = state->getCTM(); for (i = 0; i < 6; ++i) { savedCTM[i] = ctm[i]; } state->concatCTM(mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); state->concatCTM(1, 0, 0, 1, bbox[0], bbox[1]); ctm = state->getCTM(); for (i = 0; i < 6; ++i) { if (!std::isfinite(ctm[i])) { state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]); return false; } } matc[4] = x0 * xStep * ctm[0] + y0 * yStep * ctm[2] + ctm[4]; matc[5] = x0 * xStep * ctm[1] + y0 * yStep * ctm[3] + ctm[5]; if (splashAbs(ctm[1]) > splashAbs(ctm[0])) { kx = -ctm[1]; ky = ctm[2] - (ctm[0] * ctm[3]) / ctm[1]; } else { kx = ctm[0]; ky = ctm[3] - (ctm[1] * ctm[2]) / ctm[0]; } result_width = (int)ceil(fabs(kx * width * (x1 - x0))); result_height = (int)ceil(fabs(ky * height * (y1 - y0))); kx = state->getHDPI() / 72.0; ky = state->getVDPI() / 72.0; m1.m[0] = std::max(fabs(ptm[0]), fabs(ptm[2])) * kx; m1.m[1] = 0; m1.m[2] = 0; m1.m[3] = std::max(fabs(ptm[1]), fabs(ptm[3])) * ky; m1.m[4] = 0; m1.m[5] = 0; m1.transform(width, height, &kx, &ky); surface_width = (int)ceil(fabs(kx)); surface_height = (int)ceil(fabs(ky)); sx = (double)result_width / (surface_width * (x1 - x0)); sy = (double)result_height / (surface_height * (y1 - y0)); m1.m[0] *= sx; m1.m[3] *= sy; m1.transform(width, height, &kx, &ky); if (fabs(kx) < 1 && fabs(ky) < 1) { kx = std::min(kx, ky); ky = 2 / kx; m1.m[0] *= ky; m1.m[3] *= ky; m1.transform(width, height, &kx, &ky); surface_width = (int)ceil(fabs(kx)); 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]); return false; } while (fabs(kx) > 16384 || fabs(ky) > 16384) { // limit pattern bitmap size m1.m[0] /= 2; m1.m[3] /= 2; m1.transform(width, height, &kx, &ky); } surface_width = (int)ceil(fabs(kx)); surface_height = (int)ceil(fabs(ky)); // adjust repeat values to completely fill region if (unlikely(surface_width == 0 || surface_height == 0)) { state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]); return false; } repeatX = result_width / surface_width; repeatY = result_height / surface_height; if (surface_width * repeatX < result_width) { repeatX++; } if (surface_height * repeatY < result_height) { repeatY++; } if (x1 - x0 > repeatX) { repeatX = x1 - x0; } if (y1 - y0 > repeatY) { repeatY = y1 - y0; } } // restore CTM and calculate rotate and scale with rounded matrix state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]); state->concatCTM(mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); state->concatCTM(width * repeatX, 0, 0, height * repeatY, bbox[0], bbox[1]); ctm = state->getCTM(); matc[0] = ctm[0]; matc[1] = ctm[1]; matc[2] = ctm[2]; matc[3] = ctm[3]; if (surface_width == 0 || surface_height == 0 || repeatX * repeatY <= 4) { state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]); return false; } m1.transform(bbox[0], bbox[1], &kx, &ky); m1.m[4] = -kx; m1.m[5] = -ky; box.x1 = bbox[0]; box.y1 = bbox[1]; box.x2 = bbox[2]; box.y2 = bbox[3]; std::unique_ptr gfx = std::make_unique(doc, this, resDict, &box, nullptr, nullptr, nullptr, gfxA); // set pattern transformation matrix gfx->getState()->setCTM(m1.m[0], m1.m[1], m1.m[2], m1.m[3], m1.m[4], m1.m[5]); if (splashAbs(matc[1]) > splashAbs(matc[0])) { kx = -matc[1]; ky = matc[2] - (matc[0] * matc[3]) / matc[1]; } else { kx = matc[0]; ky = matc[3] - (matc[1] * matc[2]) / matc[0]; } result_width = surface_width * repeatX; result_height = surface_height * repeatY; kx = result_width / (fabs(kx) + 1); ky = result_height / (fabs(ky) + 1); state->concatCTM(kx, 0, 0, ky, 0, 0); ctm = state->getCTM(); matc[0] = ctm[0]; matc[1] = ctm[1]; matc[2] = ctm[2]; matc[3] = ctm[3]; const bool doFastBlit = matc[0] > 0 && matc[1] == 0 && matc[2] == 0 && matc[3] > 0; bitmap = new SplashBitmap(surface_width, surface_height, 1, (paintType == 1 || doFastBlit) ? colorMode : splashModeMono8, true); if (bitmap->getDataPtr() == nullptr) { SplashBitmap *tBitmap = bitmap; bitmap = formerBitmap; delete tBitmap; state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]); return false; } splash = new Splash(bitmap, true); updateCTM(gfx->getState(), m1.m[0], m1.m[1], m1.m[2], m1.m[3], m1.m[4], m1.m[5]); if (paintType == 2) { SplashColor clearColor; clearColor[0] = (colorMode == splashModeCMYK8 || colorMode == splashModeDeviceN8) ? 0x00 : 0xFF; splash->clear(clearColor, 0); } else { splash->clear(paperColor, 0); } splash->setThinLineMode(formerSplash->getThinLineMode()); splash->setMinLineWidth(s_minLineWidth); if (doFastBlit) { // drawImage would colorize the greyscale pattern in tilingBitmapSrc buffer accessor while tiling. // blitImage can't, it has no buffer accessor. We instead colorize the pattern prototype in advance. splash->setFillPattern(formerSplash->getFillPattern()->copy()); splash->setStrokePattern(formerSplash->getStrokePattern()->copy()); } gfx->display(tPat->getContentStream()); delete splash; splash = formerSplash; TilingSplashOutBitmap imgData; imgData.bitmap = bitmap; imgData.paintType = paintType; imgData.pattern = splash->getFillPattern(); imgData.colorMode = colorMode; imgData.y = 0; imgData.repeatX = repeatX; imgData.repeatY = repeatY; SplashBitmap *tBitmap = bitmap; bitmap = formerBitmap; if (doFastBlit) { // draw the tiles for (int y = 0; y < imgData.repeatY; ++y) { for (int x = 0; x < imgData.repeatX; ++x) { x0 = splashFloor(matc[4]) + x * tBitmap->getWidth(); y0 = splashFloor(matc[5]) + y * tBitmap->getHeight(); splash->blitImage(tBitmap, true, x0, y0); } } retValue = true; } else { retValue = splash->drawImage(&tilingBitmapSrc, nullptr, &imgData, colorMode, true, result_width, result_height, matc, false, true) == splashOk; } delete tBitmap; if (!retValue) { state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]); } return retValue; } bool SplashOutputDev::gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading) { GfxColorSpaceMode shadingMode = shading->getColorSpace()->getMode(); bool bDirectColorTranslation = false; // triggers an optimization. switch (colorMode) { case splashModeRGB8: bDirectColorTranslation = (shadingMode == csDeviceRGB); break; case splashModeCMYK8: case splashModeDeviceN8: bDirectColorTranslation = (shadingMode == csDeviceCMYK); break; default: break; } // restore vector antialias because we support it here SplashGouraudPattern splashShading(bDirectColorTranslation, state, shading); const bool vaa = getVectorAntialias(); setVectorAntialias(true); const bool retVal = splash->gouraudTriangleShadedFill(&splashShading); setVectorAntialias(vaa); return retVal; } bool SplashOutputDev::univariateShadedFill(GfxState *state, SplashUnivariatePattern *pattern, double tMin, double tMax) { double xMin, yMin, xMax, yMax; bool vaa = getVectorAntialias(); // restore vector antialias because we support it here setVectorAntialias(true); bool retVal = false; // get the clip region bbox if (pattern->getShading()->getHasBBox()) { pattern->getShading()->getBBox(&xMin, &yMin, &xMax, &yMax); } else { state->getClipBBox(&xMin, &yMin, &xMax, &yMax); xMin = floor(xMin); yMin = floor(yMin); xMax = ceil(xMax); yMax = ceil(yMax); { Matrix ctm, ictm; double x[4], y[4]; int i; state->getCTM(&ctm); ctm.invertTo(&ictm); ictm.transform(xMin, yMin, &x[0], &y[0]); ictm.transform(xMax, yMin, &x[1], &y[1]); ictm.transform(xMin, yMax, &x[2], &y[2]); ictm.transform(xMax, yMax, &x[3], &y[3]); xMin = xMax = x[0]; yMin = yMax = y[0]; for (i = 1; i < 4; i++) { xMin = std::min(xMin, x[i]); yMin = std::min(yMin, y[i]); xMax = std::max(xMax, x[i]); yMax = std::max(yMax, y[i]); } } } // fill the region state->moveTo(xMin, yMin); state->lineTo(xMax, yMin); state->lineTo(xMax, yMax); state->lineTo(xMin, yMax); state->closePath(); SplashPath path = convertPath(state, state->getPath(), true); pattern->getShading()->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); setOverprintMask(pattern->getShading()->getColorSpace(), state->getFillOverprint(), state->getOverprintMode(), nullptr); // If state->getStrokePattern() is set, then the current clipping region // is a stroke path. retVal = (splash->shadedFill(&path, pattern->getShading()->getHasBBox(), pattern, (state->getStrokePattern() != nullptr)) == splashOk); state->clearPath(); setVectorAntialias(vaa); return retVal; } bool SplashOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading) { SplashFunctionPattern *pattern = new SplashFunctionPattern(colorMode, state, shading); double xMin, yMin, xMax, yMax; bool vaa = getVectorAntialias(); // restore vector antialias because we support it here setVectorAntialias(true); bool retVal = false; // get the clip region bbox if (pattern->getShading()->getHasBBox()) { pattern->getShading()->getBBox(&xMin, &yMin, &xMax, &yMax); } else { state->getClipBBox(&xMin, &yMin, &xMax, &yMax); xMin = floor(xMin); yMin = floor(yMin); xMax = ceil(xMax); yMax = ceil(yMax); { Matrix ctm, ictm; double x[4], y[4]; int i; state->getCTM(&ctm); ctm.invertTo(&ictm); ictm.transform(xMin, yMin, &x[0], &y[0]); ictm.transform(xMax, yMin, &x[1], &y[1]); ictm.transform(xMin, yMax, &x[2], &y[2]); ictm.transform(xMax, yMax, &x[3], &y[3]); xMin = xMax = x[0]; yMin = yMax = y[0]; for (i = 1; i < 4; i++) { xMin = std::min(xMin, x[i]); yMin = std::min(yMin, y[i]); xMax = std::max(xMax, x[i]); yMax = std::max(yMax, y[i]); } } } // fill the region state->moveTo(xMin, yMin); state->lineTo(xMax, yMin); state->lineTo(xMax, yMax); state->lineTo(xMin, yMax); state->closePath(); SplashPath path = convertPath(state, state->getPath(), true); pattern->getShading()->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); setOverprintMask(pattern->getShading()->getColorSpace(), state->getFillOverprint(), state->getOverprintMode(), nullptr); // If state->getStrokePattern() is set, then the current clipping region // is a stroke path. retVal = (splash->shadedFill(&path, pattern->getShading()->getHasBBox(), pattern, (state->getStrokePattern() != nullptr)) == splashOk); state->clearPath(); setVectorAntialias(vaa); delete pattern; return retVal; } bool SplashOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) { SplashAxialPattern *pattern = new SplashAxialPattern(colorMode, state, shading); bool retVal = univariateShadedFill(state, pattern, tMin, tMax); delete pattern; return retVal; } bool SplashOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading, double tMin, double tMax) { SplashRadialPattern *pattern = new SplashRadialPattern(colorMode, state, shading); bool retVal = univariateShadedFill(state, pattern, tMin, tMax); delete pattern; return retVal; } poppler-24.02.0/poppler/SplashOutputDev.h000066400000000000000000000407161455701731300203220ustar00rootroot00000000000000//======================================================================== // // SplashOutputDev.h // // Copyright 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) 2005 Takashi Iwai // Copyright (C) 2009-2016 Thomas Freitag // Copyright (C) 2009 Carlos Garcia Campos // Copyright (C) 2010 Christian Feuersänger // Copyright (C) 2011 Andreas Hartmetz // Copyright (C) 2011 Andrea Canciani // Copyright (C) 2011, 2017 Adrian Johnson // Copyright (C) 2012, 2015, 2018-2021 Albert Astals Cid // Copyright (C) 2015, 2016 William Bader // Copyright (C) 2018 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 // //======================================================================== #ifndef SPLASHOUTPUTDEV_H #define SPLASHOUTPUTDEV_H #include "splash/SplashTypes.h" #include "splash/SplashPattern.h" #include "poppler-config.h" #include "poppler_private_export.h" #include "OutputDev.h" #include "GfxState.h" #include "GlobalParams.h" class PDFDoc; class Gfx8BitFont; class SplashBitmap; class Splash; class SplashPath; class SplashFontEngine; class SplashFont; class T3FontCache; struct T3FontCacheTag; struct T3GlyphStack; struct SplashTransparencyGroup; //------------------------------------------------------------------------ // Splash dynamic pattern //------------------------------------------------------------------------ class SplashFunctionPattern : public SplashPattern { public: SplashFunctionPattern(SplashColorMode colorMode, GfxState *state, GfxFunctionShading *shading); SplashPattern *copy() const override { return new SplashFunctionPattern(colorMode, state, (GfxFunctionShading *)shading); } ~SplashFunctionPattern() override; bool testPosition(int x, int y) override { return true; } bool isStatic() override { return false; } bool getColor(int x, int y, SplashColorPtr c) override; virtual GfxFunctionShading *getShading() { return shading; } bool isCMYK() override { return gfxMode == csDeviceCMYK; } protected: Matrix ictm; double xMin, yMin, xMax, yMax; GfxFunctionShading *shading; GfxState *state; SplashColorMode colorMode; GfxColorSpaceMode gfxMode; }; class SplashUnivariatePattern : public SplashPattern { public: SplashUnivariatePattern(SplashColorMode colorMode, GfxState *state, GfxUnivariateShading *shading); ~SplashUnivariatePattern() override; bool getColor(int x, int y, SplashColorPtr c) override; bool testPosition(int x, int y) override; bool isStatic() override { return false; } virtual bool getParameter(double xs, double ys, double *t) = 0; virtual GfxUnivariateShading *getShading() { return shading; } bool isCMYK() override { return gfxMode == csDeviceCMYK; } protected: Matrix ictm; double t0, t1, dt; GfxUnivariateShading *shading; GfxState *state; SplashColorMode colorMode; GfxColorSpaceMode gfxMode; }; class SplashAxialPattern : public SplashUnivariatePattern { public: SplashAxialPattern(SplashColorMode colorMode, GfxState *state, GfxAxialShading *shading); SplashPattern *copy() const override { return new SplashAxialPattern(colorMode, state, (GfxAxialShading *)shading); } ~SplashAxialPattern() override; bool getParameter(double xc, double yc, double *t) override; private: double x0, y0, x1, y1; double dx, dy, mul; }; // see GfxState.h, GfxGouraudTriangleShading class SplashGouraudPattern : public SplashGouraudColor { public: SplashGouraudPattern(bool bDirectColorTranslation, GfxState *state, GfxGouraudTriangleShading *shading); SplashPattern *copy() const override { return new SplashGouraudPattern(bDirectColorTranslation, state, shading); } ~SplashGouraudPattern() override; bool getColor(int x, int y, SplashColorPtr c) override { return false; } bool testPosition(int x, int y) override { return false; } bool isStatic() override { return false; } bool isCMYK() override { return gfxMode == csDeviceCMYK; } bool isParameterized() override { return shading->isParameterized(); } int getNTriangles() override { return shading->getNTriangles(); } void getParametrizedTriangle(int i, double *x0, double *y0, double *color0, double *x1, double *y1, double *color1, double *x2, double *y2, double *color2) override { shading->getTriangle(i, x0, y0, color0, x1, y1, color1, x2, y2, color2); } void getNonParametrizedTriangle(int i, SplashColorMode mode, double *x0, double *y0, SplashColorPtr color0, double *x1, double *y1, SplashColorPtr color1, double *x2, double *y2, SplashColorPtr color2) override; void getParameterizedColor(double colorinterp, SplashColorMode mode, SplashColorPtr dest) override; private: GfxGouraudTriangleShading *shading; GfxState *state; bool bDirectColorTranslation; GfxColorSpaceMode gfxMode; }; // see GfxState.h, GfxRadialShading class SplashRadialPattern : public SplashUnivariatePattern { public: SplashRadialPattern(SplashColorMode colorMode, GfxState *state, GfxRadialShading *shading); SplashPattern *copy() const override { return new SplashRadialPattern(colorMode, state, (GfxRadialShading *)shading); } ~SplashRadialPattern() override; bool getParameter(double xs, double ys, double *t) override; private: double x0, y0, r0, dx, dy, dr; double a, inva; }; //------------------------------------------------------------------------ // number of Type 3 fonts to cache #define splashOutT3FontCacheSize 8 //------------------------------------------------------------------------ // SplashOutputDev //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT SplashOutputDev : public OutputDev { public: // Constructor. SplashOutputDev(SplashColorMode colorModeA, int bitmapRowPadA, bool reverseVideoA, SplashColorPtr paperColorA, bool bitmapTopDownA = true, SplashThinLineMode thinLineMode = splashThinLineDefault, bool overprintPreviewA = false); // Destructor. ~SplashOutputDev() override; //----- get info about output device // Does this device use tilingPatternFill()? If this returns false, // tiling pattern fills will be reduced to a series of other drawing // operations. bool useTilingPatternFill() override { return true; } // 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. bool useShadedFills(int type) override { return (type >= 1 && type <= 5) ? true : false; } // Does this device use upside-down coordinates? // (Upside-down means (0,0) is the top left corner of the page.) bool upsideDown() override { return bitmapTopDown; } // Does this device use drawChar() or drawString()? bool useDrawChar() 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 true; } //----- initialization and control // Start a page. void startPage(int pageNum, GfxState *state, XRef *xref) override; // End a page. void endPage() override; //----- save/restore graphics state void saveState(GfxState *state) override; void restoreState(GfxState *state) override; //----- update graphics state void updateAll(GfxState *state) override; void updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32) override; void updateLineDash(GfxState *state) override; void updateFlatness(GfxState *state) override; void updateLineJoin(GfxState *state) override; void updateLineCap(GfxState *state) override; void updateMiterLimit(GfxState *state) override; void updateLineWidth(GfxState *state) override; void updateStrokeAdjust(GfxState *state) override; void updateFillColorSpace(GfxState *state) override; void updateStrokeColorSpace(GfxState *state) override; void updateFillColor(GfxState *state) override; void updateStrokeColor(GfxState *state) override; void updateBlendMode(GfxState *state) override; void updateFillOpacity(GfxState *state) override; void updateStrokeOpacity(GfxState *state) override; void updatePatternOpacity(GfxState *state) override; void clearPatternOpacity(GfxState *state) override; void updateFillOverprint(GfxState *state) override; void updateStrokeOverprint(GfxState *state) override; void updateOverprintMode(GfxState *state) override; void updateTransfer(GfxState *state) override; //----- update text state void updateFont(GfxState *state) override; //----- path painting void stroke(GfxState *state) override; void fill(GfxState *state) override; void eoFill(GfxState *state) override; bool tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *catalog, GfxTilingPattern *tPat, const double *mat, int x0, int y0, int x1, int y1, double xStep, double yStep) override; bool functionShadedFill(GfxState *state, GfxFunctionShading *shading) override; bool axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) override; bool radialShadedFill(GfxState *state, GfxRadialShading *shading, double tMin, double tMax) override; bool gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading) override; //----- path clipping void clip(GfxState *state) override; void eoClip(GfxState *state) override; void clipToStrokePath(GfxState *state) override; //----- text drawing void drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, const Unicode *u, int uLen) override; bool beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, const Unicode *u, int uLen) override; void endType3Char(GfxState *state) override; void beginTextObject(GfxState *state) override; void endTextObject(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 drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) override; 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; void 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) override; //----- Type 3 font operators void type3D0(GfxState *state, double wx, double wy) override; void type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) override; //----- transparency groups and soft masks bool checkTransparencyGroup(GfxState *state, bool knockout) override; void beginTransparencyGroup(GfxState *state, const double *bbox, GfxColorSpace *blendingColorSpace, bool isolated, bool knockout, bool forSoftMask) override; void endTransparencyGroup(GfxState *state) override; void paintTransparencyGroup(GfxState *state, const double *bbox) override; void setSoftMask(GfxState *state, const double *bbox, bool alpha, Function *transferFunc, GfxColor *backdropColor) override; void clearSoftMask(GfxState *state) override; //----- special access // Called to indicate that a new PDF document has been loaded. void startDoc(PDFDoc *docA); void setPaperColor(SplashColorPtr paperColorA); bool isReverseVideo() { return reverseVideo; } void setReverseVideo(bool reverseVideoA) { reverseVideo = reverseVideoA; } // Get the bitmap and its size. SplashBitmap *getBitmap() { return bitmap; } int getBitmapWidth(); int getBitmapHeight(); // Returns the last rasterized bitmap, transferring ownership to the // caller. SplashBitmap *takeBitmap(); // Get the Splash object. Splash *getSplash() { return splash; } SplashFont *getCurrentFont() { return font; } // If is true, don't draw horizontal text. // If is true, don't draw rotated (non-horizontal) text. void setSkipText(bool skipHorizTextA, bool skipRotatedTextA) { skipHorizText = skipHorizTextA; skipRotatedText = skipRotatedTextA; } #if 1 //~tmp: turn off anti-aliasing temporarily bool getVectorAntialias() override; void setVectorAntialias(bool vaa) override; #endif bool getFontAntialias() { return fontAntialias; } void setFontAntialias(bool anti) { fontAntialias = anti; } void setFreeTypeHinting(bool enable, bool enableSlightHinting); void setEnableFreeType(bool enable) { enableFreeType = enable; } protected: void doUpdateFont(GfxState *state); private: bool univariateShadedFill(GfxState *state, SplashUnivariatePattern *pattern, double tMin, double tMax); void setupScreenParams(double hDPI, double vDPI); SplashPattern *getColor(GfxGray gray); SplashPattern *getColor(GfxRGB *rgb); SplashPattern *getColor(GfxCMYK *cmyk); SplashPattern *getColor(GfxColor *deviceN); static void getMatteColor(SplashColorMode colorMode, GfxImageColorMap *colorMap, const GfxColor *matteColor, SplashColor splashMatteColor); void setOverprintMask(GfxColorSpace *colorSpace, bool overprintFlag, int overprintMode, const GfxColor *singleColor, bool grayIndexed = false); SplashPath convertPath(GfxState *state, const GfxPath *path, bool dropEmptySubpaths); void drawType3Glyph(GfxState *state, T3FontCache *t3Font, T3FontCacheTag *tag, unsigned char *data); #ifdef USE_CMS bool useIccImageSrc(void *data); static void iccTransform(void *data, SplashBitmap *bitmap); static bool iccImageSrc(void *data, SplashColorPtr colorLine, unsigned char *alphaLine); #endif static bool imageMaskSrc(void *data, SplashColorPtr line); static bool imageSrc(void *data, SplashColorPtr colorLine, unsigned char *alphaLine); static bool alphaImageSrc(void *data, SplashColorPtr line, unsigned char *alphaLine); static bool maskedImageSrc(void *data, SplashColorPtr line, unsigned char *alphaLine); static bool tilingBitmapSrc(void *data, SplashColorPtr line, unsigned char *alphaLine); bool keepAlphaChannel; // don't fill with paper color, keep alpha channel SplashColorMode colorMode; int bitmapRowPad; bool bitmapTopDown; bool fontAntialias; bool vectorAntialias; bool overprintPreview; bool enableFreeType; bool enableFreeTypeHinting; bool enableSlightHinting; bool reverseVideo; // reverse video mode SplashColor paperColor; // paper color SplashScreenParams screenParams; bool skipHorizText; bool skipRotatedText; PDFDoc *doc; // the current document XRef *xref; // the xref of the current document SplashBitmap *bitmap; Splash *splash; SplashFontEngine *fontEngine; T3FontCache * // Type 3 font cache t3FontCache[splashOutT3FontCacheSize]; int nT3Fonts; // number of valid entries in t3FontCache T3GlyphStack *t3GlyphStack; // Type 3 glyph context stack SplashFont *font; // current font bool needFontUpdate; // set when the font needs to be updated SplashPath *textClipPath; // clipping path built with text object SplashTransparencyGroup * // transparency group stack transpGroupStack; }; #endif poppler-24.02.0/poppler/Stream-CCITT.h000066400000000000000000000567531455701731300173170ustar00rootroot00000000000000//======================================================================== // // Stream-CCITT.h // // Tables for CCITT Fax decoding. // // 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) 2008 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 // //======================================================================== #ifndef STREAM_CCITT_H #define STREAM_CCITT_H struct CCITTCode { short bits; short n; }; #define ccittEOL -2 //------------------------------------------------------------------------ // 2D codes //------------------------------------------------------------------------ #define twoDimPass 0 #define twoDimHoriz 1 #define twoDimVert0 2 #define twoDimVertR1 3 #define twoDimVertL1 4 #define twoDimVertR2 5 #define twoDimVertL2 6 #define twoDimVertR3 7 #define twoDimVertL3 8 // 1-7 bit codes static const CCITTCode twoDimTab1[128] = { { -1, -1 }, { -1, -1 }, // 000000x { 7, twoDimVertL3 }, // 0000010 { 7, twoDimVertR3 }, // 0000011 { 6, twoDimVertL2 }, { 6, twoDimVertL2 }, // 000010x { 6, twoDimVertR2 }, { 6, twoDimVertR2 }, // 000011x { 4, twoDimPass }, { 4, twoDimPass }, // 0001xxx { 4, twoDimPass }, { 4, twoDimPass }, { 4, twoDimPass }, { 4, twoDimPass }, { 4, twoDimPass }, { 4, twoDimPass }, { 3, twoDimHoriz }, { 3, twoDimHoriz }, // 001xxxx { 3, twoDimHoriz }, { 3, twoDimHoriz }, { 3, twoDimHoriz }, { 3, twoDimHoriz }, { 3, twoDimHoriz }, { 3, twoDimHoriz }, { 3, twoDimHoriz }, { 3, twoDimHoriz }, { 3, twoDimHoriz }, { 3, twoDimHoriz }, { 3, twoDimHoriz }, { 3, twoDimHoriz }, { 3, twoDimHoriz }, { 3, twoDimHoriz }, { 3, twoDimVertL1 }, { 3, twoDimVertL1 }, // 010xxxx { 3, twoDimVertL1 }, { 3, twoDimVertL1 }, { 3, twoDimVertL1 }, { 3, twoDimVertL1 }, { 3, twoDimVertL1 }, { 3, twoDimVertL1 }, { 3, twoDimVertL1 }, { 3, twoDimVertL1 }, { 3, twoDimVertL1 }, { 3, twoDimVertL1 }, { 3, twoDimVertL1 }, { 3, twoDimVertL1 }, { 3, twoDimVertL1 }, { 3, twoDimVertL1 }, { 3, twoDimVertR1 }, { 3, twoDimVertR1 }, // 011xxxx { 3, twoDimVertR1 }, { 3, twoDimVertR1 }, { 3, twoDimVertR1 }, { 3, twoDimVertR1 }, { 3, twoDimVertR1 }, { 3, twoDimVertR1 }, { 3, twoDimVertR1 }, { 3, twoDimVertR1 }, { 3, twoDimVertR1 }, { 3, twoDimVertR1 }, { 3, twoDimVertR1 }, { 3, twoDimVertR1 }, { 3, twoDimVertR1 }, { 3, twoDimVertR1 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, // 1xxxxxx { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 }, { 1, twoDimVert0 } }; //------------------------------------------------------------------------ // white run lengths //------------------------------------------------------------------------ // 11-12 bit codes (upper 7 bits are 0) static const CCITTCode whiteTab1[32] = { { -1, -1 }, // 00000 { 12, ccittEOL }, // 00001 { -1, -1 }, { -1, -1 }, // 0001x { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, // 001xx { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, // 010xx { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, // 011xx { 11, 1792 }, { 11, 1792 }, // 1000x { 12, 1984 }, // 10010 { 12, 2048 }, // 10011 { 12, 2112 }, // 10100 { 12, 2176 }, // 10101 { 12, 2240 }, // 10110 { 12, 2304 }, // 10111 { 11, 1856 }, { 11, 1856 }, // 1100x { 11, 1920 }, { 11, 1920 }, // 1101x { 12, 2368 }, // 11100 { 12, 2432 }, // 11101 { 12, 2496 }, // 11110 { 12, 2560 } // 11111 }; // 1-9 bit codes static const CCITTCode whiteTab2[512] = { { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, // 0000000xx { 8, 29 }, { 8, 29 }, // 00000010x { 8, 30 }, { 8, 30 }, // 00000011x { 8, 45 }, { 8, 45 }, // 00000100x { 8, 46 }, { 8, 46 }, // 00000101x { 7, 22 }, { 7, 22 }, { 7, 22 }, { 7, 22 }, // 0000011xx { 7, 23 }, { 7, 23 }, { 7, 23 }, { 7, 23 }, // 0000100xx { 8, 47 }, { 8, 47 }, // 00001010x { 8, 48 }, { 8, 48 }, // 00001011x { 6, 13 }, { 6, 13 }, { 6, 13 }, { 6, 13 }, // 000011xxx { 6, 13 }, { 6, 13 }, { 6, 13 }, { 6, 13 }, { 7, 20 }, { 7, 20 }, { 7, 20 }, { 7, 20 }, // 0001000xx { 8, 33 }, { 8, 33 }, // 00010010x { 8, 34 }, { 8, 34 }, // 00010011x { 8, 35 }, { 8, 35 }, // 00010100x { 8, 36 }, { 8, 36 }, // 00010101x { 8, 37 }, { 8, 37 }, // 00010110x { 8, 38 }, { 8, 38 }, // 00010111x { 7, 19 }, { 7, 19 }, { 7, 19 }, { 7, 19 }, // 0001100xx { 8, 31 }, { 8, 31 }, // 00011010x { 8, 32 }, { 8, 32 }, // 00011011x { 6, 1 }, { 6, 1 }, { 6, 1 }, { 6, 1 }, // 000111xxx { 6, 1 }, { 6, 1 }, { 6, 1 }, { 6, 1 }, { 6, 12 }, { 6, 12 }, { 6, 12 }, { 6, 12 }, // 001000xxx { 6, 12 }, { 6, 12 }, { 6, 12 }, { 6, 12 }, { 8, 53 }, { 8, 53 }, // 00100100x { 8, 54 }, { 8, 54 }, // 00100101x { 7, 26 }, { 7, 26 }, { 7, 26 }, { 7, 26 }, // 0010011xx { 8, 39 }, { 8, 39 }, // 00101000x { 8, 40 }, { 8, 40 }, // 00101001x { 8, 41 }, { 8, 41 }, // 00101010x { 8, 42 }, { 8, 42 }, // 00101011x { 8, 43 }, { 8, 43 }, // 00101100x { 8, 44 }, { 8, 44 }, // 00101101x { 7, 21 }, { 7, 21 }, { 7, 21 }, { 7, 21 }, // 0010111xx { 7, 28 }, { 7, 28 }, { 7, 28 }, { 7, 28 }, // 0011000xx { 8, 61 }, { 8, 61 }, // 00110010x { 8, 62 }, { 8, 62 }, // 00110011x { 8, 63 }, { 8, 63 }, // 00110100x { 8, 0 }, { 8, 0 }, // 00110101x { 8, 320 }, { 8, 320 }, // 00110110x { 8, 384 }, { 8, 384 }, // 00110111x { 5, 10 }, { 5, 10 }, { 5, 10 }, { 5, 10 }, // 00111xxxx { 5, 10 }, { 5, 10 }, { 5, 10 }, { 5, 10 }, { 5, 10 }, { 5, 10 }, { 5, 10 }, { 5, 10 }, { 5, 10 }, { 5, 10 }, { 5, 10 }, { 5, 10 }, { 5, 11 }, { 5, 11 }, { 5, 11 }, { 5, 11 }, // 01000xxxx { 5, 11 }, { 5, 11 }, { 5, 11 }, { 5, 11 }, { 5, 11 }, { 5, 11 }, { 5, 11 }, { 5, 11 }, { 5, 11 }, { 5, 11 }, { 5, 11 }, { 5, 11 }, { 7, 27 }, { 7, 27 }, { 7, 27 }, { 7, 27 }, // 0100100xx { 8, 59 }, { 8, 59 }, // 01001010x { 8, 60 }, { 8, 60 }, // 01001011x { 9, 1472 }, // 010011000 { 9, 1536 }, // 010011001 { 9, 1600 }, // 010011010 { 9, 1728 }, // 010011011 { 7, 18 }, { 7, 18 }, { 7, 18 }, { 7, 18 }, // 0100111xx { 7, 24 }, { 7, 24 }, { 7, 24 }, { 7, 24 }, // 0101000xx { 8, 49 }, { 8, 49 }, // 01010010x { 8, 50 }, { 8, 50 }, // 01010011x { 8, 51 }, { 8, 51 }, // 01010100x { 8, 52 }, { 8, 52 }, // 01010101x { 7, 25 }, { 7, 25 }, { 7, 25 }, { 7, 25 }, // 0101011xx { 8, 55 }, { 8, 55 }, // 01011000x { 8, 56 }, { 8, 56 }, // 01011001x { 8, 57 }, { 8, 57 }, // 01011010x { 8, 58 }, { 8, 58 }, // 01011011x { 6, 192 }, { 6, 192 }, { 6, 192 }, { 6, 192 }, // 010111xxx { 6, 192 }, { 6, 192 }, { 6, 192 }, { 6, 192 }, { 6, 1664 }, { 6, 1664 }, { 6, 1664 }, { 6, 1664 }, // 011000xxx { 6, 1664 }, { 6, 1664 }, { 6, 1664 }, { 6, 1664 }, { 8, 448 }, { 8, 448 }, // 01100100x { 8, 512 }, { 8, 512 }, // 01100101x { 9, 704 }, // 011001100 { 9, 768 }, // 011001101 { 8, 640 }, { 8, 640 }, // 01100111x { 8, 576 }, { 8, 576 }, // 01101000x { 9, 832 }, // 011010010 { 9, 896 }, // 011010011 { 9, 960 }, // 011010100 { 9, 1024 }, // 011010101 { 9, 1088 }, // 011010110 { 9, 1152 }, // 011010111 { 9, 1216 }, // 011011000 { 9, 1280 }, // 011011001 { 9, 1344 }, // 011011010 { 9, 1408 }, // 011011011 { 7, 256 }, { 7, 256 }, { 7, 256 }, { 7, 256 }, // 0110111xx { 4, 2 }, { 4, 2 }, { 4, 2 }, { 4, 2 }, // 0111xxxxx { 4, 2 }, { 4, 2 }, { 4, 2 }, { 4, 2 }, { 4, 2 }, { 4, 2 }, { 4, 2 }, { 4, 2 }, { 4, 2 }, { 4, 2 }, { 4, 2 }, { 4, 2 }, { 4, 2 }, { 4, 2 }, { 4, 2 }, { 4, 2 }, { 4, 2 }, { 4, 2 }, { 4, 2 }, { 4, 2 }, { 4, 2 }, { 4, 2 }, { 4, 2 }, { 4, 2 }, { 4, 2 }, { 4, 2 }, { 4, 2 }, { 4, 2 }, { 4, 3 }, { 4, 3 }, { 4, 3 }, { 4, 3 }, // 1000xxxxx { 4, 3 }, { 4, 3 }, { 4, 3 }, { 4, 3 }, { 4, 3 }, { 4, 3 }, { 4, 3 }, { 4, 3 }, { 4, 3 }, { 4, 3 }, { 4, 3 }, { 4, 3 }, { 4, 3 }, { 4, 3 }, { 4, 3 }, { 4, 3 }, { 4, 3 }, { 4, 3 }, { 4, 3 }, { 4, 3 }, { 4, 3 }, { 4, 3 }, { 4, 3 }, { 4, 3 }, { 4, 3 }, { 4, 3 }, { 4, 3 }, { 4, 3 }, { 5, 128 }, { 5, 128 }, { 5, 128 }, { 5, 128 }, // 10010xxxx { 5, 128 }, { 5, 128 }, { 5, 128 }, { 5, 128 }, { 5, 128 }, { 5, 128 }, { 5, 128 }, { 5, 128 }, { 5, 128 }, { 5, 128 }, { 5, 128 }, { 5, 128 }, { 5, 8 }, { 5, 8 }, { 5, 8 }, { 5, 8 }, // 10011xxxx { 5, 8 }, { 5, 8 }, { 5, 8 }, { 5, 8 }, { 5, 8 }, { 5, 8 }, { 5, 8 }, { 5, 8 }, { 5, 8 }, { 5, 8 }, { 5, 8 }, { 5, 8 }, { 5, 9 }, { 5, 9 }, { 5, 9 }, { 5, 9 }, // 10100xxxx { 5, 9 }, { 5, 9 }, { 5, 9 }, { 5, 9 }, { 5, 9 }, { 5, 9 }, { 5, 9 }, { 5, 9 }, { 5, 9 }, { 5, 9 }, { 5, 9 }, { 5, 9 }, { 6, 16 }, { 6, 16 }, { 6, 16 }, { 6, 16 }, // 101010xxx { 6, 16 }, { 6, 16 }, { 6, 16 }, { 6, 16 }, { 6, 17 }, { 6, 17 }, { 6, 17 }, { 6, 17 }, // 101011xxx { 6, 17 }, { 6, 17 }, { 6, 17 }, { 6, 17 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, // 1011xxxxx { 4, 4 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, { 4, 4 }, { 4, 5 }, { 4, 5 }, { 4, 5 }, { 4, 5 }, // 1100xxxxx { 4, 5 }, { 4, 5 }, { 4, 5 }, { 4, 5 }, { 4, 5 }, { 4, 5 }, { 4, 5 }, { 4, 5 }, { 4, 5 }, { 4, 5 }, { 4, 5 }, { 4, 5 }, { 4, 5 }, { 4, 5 }, { 4, 5 }, { 4, 5 }, { 4, 5 }, { 4, 5 }, { 4, 5 }, { 4, 5 }, { 4, 5 }, { 4, 5 }, { 4, 5 }, { 4, 5 }, { 4, 5 }, { 4, 5 }, { 4, 5 }, { 4, 5 }, { 6, 14 }, { 6, 14 }, { 6, 14 }, { 6, 14 }, // 110100xxx { 6, 14 }, { 6, 14 }, { 6, 14 }, { 6, 14 }, { 6, 15 }, { 6, 15 }, { 6, 15 }, { 6, 15 }, // 110101xxx { 6, 15 }, { 6, 15 }, { 6, 15 }, { 6, 15 }, { 5, 64 }, { 5, 64 }, { 5, 64 }, { 5, 64 }, // 11011xxxx { 5, 64 }, { 5, 64 }, { 5, 64 }, { 5, 64 }, { 5, 64 }, { 5, 64 }, { 5, 64 }, { 5, 64 }, { 5, 64 }, { 5, 64 }, { 5, 64 }, { 5, 64 }, { 4, 6 }, { 4, 6 }, { 4, 6 }, { 4, 6 }, // 1110xxxxx { 4, 6 }, { 4, 6 }, { 4, 6 }, { 4, 6 }, { 4, 6 }, { 4, 6 }, { 4, 6 }, { 4, 6 }, { 4, 6 }, { 4, 6 }, { 4, 6 }, { 4, 6 }, { 4, 6 }, { 4, 6 }, { 4, 6 }, { 4, 6 }, { 4, 6 }, { 4, 6 }, { 4, 6 }, { 4, 6 }, { 4, 6 }, { 4, 6 }, { 4, 6 }, { 4, 6 }, { 4, 6 }, { 4, 6 }, { 4, 6 }, { 4, 6 }, { 4, 7 }, { 4, 7 }, { 4, 7 }, { 4, 7 }, // 1111xxxxx { 4, 7 }, { 4, 7 }, { 4, 7 }, { 4, 7 }, { 4, 7 }, { 4, 7 }, { 4, 7 }, { 4, 7 }, { 4, 7 }, { 4, 7 }, { 4, 7 }, { 4, 7 }, { 4, 7 }, { 4, 7 }, { 4, 7 }, { 4, 7 }, { 4, 7 }, { 4, 7 }, { 4, 7 }, { 4, 7 }, { 4, 7 }, { 4, 7 }, { 4, 7 }, { 4, 7 }, { 4, 7 }, { 4, 7 }, { 4, 7 }, { 4, 7 } }; //------------------------------------------------------------------------ // black run lengths //------------------------------------------------------------------------ // 10-13 bit codes (upper 6 bits are 0) static const CCITTCode blackTab1[128] = { { -1, -1 }, { -1, -1 }, // 000000000000x { 12, ccittEOL }, { 12, ccittEOL }, // 000000000001x { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, // 00000000001xx { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, // 00000000010xx { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, // 00000000011xx { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, // 00000000100xx { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, // 00000000101xx { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, // 00000000110xx { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, // 00000000111xx { 11, 1792 }, { 11, 1792 }, { 11, 1792 }, { 11, 1792 }, // 00000001000xx { 12, 1984 }, { 12, 1984 }, // 000000010010x { 12, 2048 }, { 12, 2048 }, // 000000010011x { 12, 2112 }, { 12, 2112 }, // 000000010100x { 12, 2176 }, { 12, 2176 }, // 000000010101x { 12, 2240 }, { 12, 2240 }, // 000000010110x { 12, 2304 }, { 12, 2304 }, // 000000010111x { 11, 1856 }, { 11, 1856 }, { 11, 1856 }, { 11, 1856 }, // 00000001100xx { 11, 1920 }, { 11, 1920 }, { 11, 1920 }, { 11, 1920 }, // 00000001101xx { 12, 2368 }, { 12, 2368 }, // 000000011100x { 12, 2432 }, { 12, 2432 }, // 000000011101x { 12, 2496 }, { 12, 2496 }, // 000000011110x { 12, 2560 }, { 12, 2560 }, // 000000011111x { 10, 18 }, { 10, 18 }, { 10, 18 }, { 10, 18 }, // 0000001000xxx { 10, 18 }, { 10, 18 }, { 10, 18 }, { 10, 18 }, { 12, 52 }, { 12, 52 }, // 000000100100x { 13, 640 }, // 0000001001010 { 13, 704 }, // 0000001001011 { 13, 768 }, // 0000001001100 { 13, 832 }, // 0000001001101 { 12, 55 }, { 12, 55 }, // 000000100111x { 12, 56 }, { 12, 56 }, // 000000101000x { 13, 1280 }, // 0000001010010 { 13, 1344 }, // 0000001010011 { 13, 1408 }, // 0000001010100 { 13, 1472 }, // 0000001010101 { 12, 59 }, { 12, 59 }, // 000000101011x { 12, 60 }, { 12, 60 }, // 000000101100x { 13, 1536 }, // 0000001011010 { 13, 1600 }, // 0000001011011 { 11, 24 }, { 11, 24 }, { 11, 24 }, { 11, 24 }, // 00000010111xx { 11, 25 }, { 11, 25 }, { 11, 25 }, { 11, 25 }, // 00000011000xx { 13, 1664 }, // 0000001100100 { 13, 1728 }, // 0000001100101 { 12, 320 }, { 12, 320 }, // 000000110011x { 12, 384 }, { 12, 384 }, // 000000110100x { 12, 448 }, { 12, 448 }, // 000000110101x { 13, 512 }, // 0000001101100 { 13, 576 }, // 0000001101101 { 12, 53 }, { 12, 53 }, // 000000110111x { 12, 54 }, { 12, 54 }, // 000000111000x { 13, 896 }, // 0000001110010 { 13, 960 }, // 0000001110011 { 13, 1024 }, // 0000001110100 { 13, 1088 }, // 0000001110101 { 13, 1152 }, // 0000001110110 { 13, 1216 }, // 0000001110111 { 10, 64 }, { 10, 64 }, { 10, 64 }, { 10, 64 }, // 0000001111xxx { 10, 64 }, { 10, 64 }, { 10, 64 }, { 10, 64 } }; // 7-12 bit codes (upper 4 bits are 0) static const CCITTCode blackTab2[192] = { { 8, 13 }, { 8, 13 }, { 8, 13 }, { 8, 13 }, // 00000100xxxx { 8, 13 }, { 8, 13 }, { 8, 13 }, { 8, 13 }, { 8, 13 }, { 8, 13 }, { 8, 13 }, { 8, 13 }, { 8, 13 }, { 8, 13 }, { 8, 13 }, { 8, 13 }, { 11, 23 }, { 11, 23 }, // 00000101000x { 12, 50 }, // 000001010010 { 12, 51 }, // 000001010011 { 12, 44 }, // 000001010100 { 12, 45 }, // 000001010101 { 12, 46 }, // 000001010110 { 12, 47 }, // 000001010111 { 12, 57 }, // 000001011000 { 12, 58 }, // 000001011001 { 12, 61 }, // 000001011010 { 12, 256 }, // 000001011011 { 10, 16 }, { 10, 16 }, { 10, 16 }, { 10, 16 }, // 0000010111xx { 10, 17 }, { 10, 17 }, { 10, 17 }, { 10, 17 }, // 0000011000xx { 12, 48 }, // 000001100100 { 12, 49 }, // 000001100101 { 12, 62 }, // 000001100110 { 12, 63 }, // 000001100111 { 12, 30 }, // 000001101000 { 12, 31 }, // 000001101001 { 12, 32 }, // 000001101010 { 12, 33 }, // 000001101011 { 12, 40 }, // 000001101100 { 12, 41 }, // 000001101101 { 11, 22 }, { 11, 22 }, // 00000110111x { 8, 14 }, { 8, 14 }, { 8, 14 }, { 8, 14 }, // 00000111xxxx { 8, 14 }, { 8, 14 }, { 8, 14 }, { 8, 14 }, { 8, 14 }, { 8, 14 }, { 8, 14 }, { 8, 14 }, { 8, 14 }, { 8, 14 }, { 8, 14 }, { 8, 14 }, { 7, 10 }, { 7, 10 }, { 7, 10 }, { 7, 10 }, // 0000100xxxxx { 7, 10 }, { 7, 10 }, { 7, 10 }, { 7, 10 }, { 7, 10 }, { 7, 10 }, { 7, 10 }, { 7, 10 }, { 7, 10 }, { 7, 10 }, { 7, 10 }, { 7, 10 }, { 7, 10 }, { 7, 10 }, { 7, 10 }, { 7, 10 }, { 7, 10 }, { 7, 10 }, { 7, 10 }, { 7, 10 }, { 7, 10 }, { 7, 10 }, { 7, 10 }, { 7, 10 }, { 7, 10 }, { 7, 10 }, { 7, 10 }, { 7, 10 }, { 7, 11 }, { 7, 11 }, { 7, 11 }, { 7, 11 }, // 0000101xxxxx { 7, 11 }, { 7, 11 }, { 7, 11 }, { 7, 11 }, { 7, 11 }, { 7, 11 }, { 7, 11 }, { 7, 11 }, { 7, 11 }, { 7, 11 }, { 7, 11 }, { 7, 11 }, { 7, 11 }, { 7, 11 }, { 7, 11 }, { 7, 11 }, { 7, 11 }, { 7, 11 }, { 7, 11 }, { 7, 11 }, { 7, 11 }, { 7, 11 }, { 7, 11 }, { 7, 11 }, { 7, 11 }, { 7, 11 }, { 7, 11 }, { 7, 11 }, { 9, 15 }, { 9, 15 }, { 9, 15 }, { 9, 15 }, // 000011000xxx { 9, 15 }, { 9, 15 }, { 9, 15 }, { 9, 15 }, { 12, 128 }, // 000011001000 { 12, 192 }, // 000011001001 { 12, 26 }, // 000011001010 { 12, 27 }, // 000011001011 { 12, 28 }, // 000011001100 { 12, 29 }, // 000011001101 { 11, 19 }, { 11, 19 }, // 00001100111x { 11, 20 }, { 11, 20 }, // 00001101000x { 12, 34 }, // 000011010010 { 12, 35 }, // 000011010011 { 12, 36 }, // 000011010100 { 12, 37 }, // 000011010101 { 12, 38 }, // 000011010110 { 12, 39 }, // 000011010111 { 11, 21 }, { 11, 21 }, // 00001101100x { 12, 42 }, // 000011011010 { 12, 43 }, // 000011011011 { 10, 0 }, { 10, 0 }, { 10, 0 }, { 10, 0 }, // 0000110111xx { 7, 12 }, { 7, 12 }, { 7, 12 }, { 7, 12 }, // 0000111xxxxx { 7, 12 }, { 7, 12 }, { 7, 12 }, { 7, 12 }, { 7, 12 }, { 7, 12 }, { 7, 12 }, { 7, 12 }, { 7, 12 }, { 7, 12 }, { 7, 12 }, { 7, 12 }, { 7, 12 }, { 7, 12 }, { 7, 12 }, { 7, 12 }, { 7, 12 }, { 7, 12 }, { 7, 12 }, { 7, 12 }, { 7, 12 }, { 7, 12 }, { 7, 12 }, { 7, 12 }, { 7, 12 }, { 7, 12 }, { 7, 12 }, { 7, 12 } }; // 2-6 bit codes static const CCITTCode blackTab3[64] = { { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, // 0000xx { 6, 9 }, // 000100 { 6, 8 }, // 000101 { 5, 7 }, { 5, 7 }, // 00011x { 4, 6 }, { 4, 6 }, { 4, 6 }, { 4, 6 }, // 0010xx { 4, 5 }, { 4, 5 }, { 4, 5 }, { 4, 5 }, // 0011xx { 3, 1 }, { 3, 1 }, { 3, 1 }, { 3, 1 }, // 010xxx { 3, 1 }, { 3, 1 }, { 3, 1 }, { 3, 1 }, { 3, 4 }, { 3, 4 }, { 3, 4 }, { 3, 4 }, // 011xxx { 3, 4 }, { 3, 4 }, { 3, 4 }, { 3, 4 }, { 2, 3 }, { 2, 3 }, { 2, 3 }, { 2, 3 }, // 10xxxx { 2, 3 }, { 2, 3 }, { 2, 3 }, { 2, 3 }, { 2, 3 }, { 2, 3 }, { 2, 3 }, { 2, 3 }, { 2, 3 }, { 2, 3 }, { 2, 3 }, { 2, 3 }, { 2, 2 }, { 2, 2 }, { 2, 2 }, { 2, 2 }, // 11xxxx { 2, 2 }, { 2, 2 }, { 2, 2 }, { 2, 2 }, { 2, 2 }, { 2, 2 }, { 2, 2 }, { 2, 2 }, { 2, 2 }, { 2, 2 }, { 2, 2 }, { 2, 2 } }; #endif poppler-24.02.0/poppler/Stream.cc000066400000000000000000004527371455701731300166130ustar00rootroot00000000000000//======================================================================== // // Stream.cc // // 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) 2005 Jeff Muizelaar // Copyright (C) 2006-2010, 2012-2014, 2016-2021, 2023 Albert Astals Cid // Copyright (C) 2007 Krzysztof Kowalczyk // Copyright (C) 2008 Julien Rebetez // Copyright (C) 2009 Carlos Garcia Campos // Copyright (C) 2009 Glenn Ganz // Copyright (C) 2009 Stefan Thomas // Copyright (C) 2010 Hib Eris // Copyright (C) 2010 Tomas Hoger // Copyright (C) 2011, 2012, 2016, 2020 William Bader // 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) 2013, 2017, 2018 Adrian Johnson // Copyright (C) 2013, 2018 Adam Reichold // Copyright (C) 2013 Pino Toscano // Copyright (C) 2015 Suzuki Toshiya // Copyright (C) 2015 Jason Crain // Copyright (C) 2017 Jose Aliste // Copyright (C) 2017 Kay Dohmann // Copyright (C) 2019 Christian Persch // Copyright (C) 2019 LE GARREC Vincent // Copyright (C) 2019 Volker Krause // Copyright (C) 2019 Alexander Volkov // Copyright (C) 2020 Philipp Knechtges // Copyright (C) 2021 Hubert Figuiere // Copyright (C) 2021 Georgiy Sgibnev . Work sponsored by lab50.net. // // 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 #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include "goo/gmem.h" #include "goo/gfile.h" #include "poppler-config.h" #include "Error.h" #include "Object.h" #include "Lexer.h" #include "GfxState.h" #include "Stream.h" #include "XRef.h" #include "JBIG2Stream.h" #include "Stream-CCITT.h" #include "CachedFile.h" #include "splash/SplashBitmap.h" #ifdef ENABLE_LIBJPEG # include "DCTStream.h" #endif #ifdef ENABLE_ZLIB_UNCOMPRESS # include "FlateStream.h" #endif #ifdef ENABLE_LIBOPENJPEG # include "JPEG2000Stream.h" #else # include "JPXStream.h" #endif #ifdef __DJGPP__ static bool setDJSYSFLAGS = false; #endif //------------------------------------------------------------------------ // Stream (base class) //------------------------------------------------------------------------ Stream::Stream() { ref = 1; } Stream::~Stream() = default; void Stream::close() { } int Stream::getRawChar() { error(errInternal, -1, "Internal: called getRawChar() on non-predictor stream"); return EOF; } int Stream::getChars(int nChars, unsigned char *buffer) { error(errInternal, -1, "Internal: called getChars() on non-predictor stream"); return 0; } void Stream::getRawChars(int nChars, int *buffer) { error(errInternal, -1, "Internal: called getRawChars() on non-predictor stream"); } char *Stream::getLine(char *buf, int size) { int i; int c; if (lookChar() == EOF || size < 0) { return nullptr; } for (i = 0; i < size - 1; ++i) { c = getChar(); if (c == EOF || c == '\n') { break; } if (c == '\r') { if ((c = lookChar()) == '\n') { getChar(); } break; } buf[i] = c; } buf[i] = '\0'; return buf; } unsigned int Stream::discardChars(unsigned int n) { unsigned char buf[4096]; unsigned int count, i, j; count = 0; while (count < n) { if ((i = n - count) > sizeof(buf)) { i = (unsigned int)sizeof(buf); } j = (unsigned int)doGetChars((int)i, buf); count += j; if (j != i) { break; } } return count; } GooString *Stream::getPSFilter(int psLevel, const char *indent) { return new GooString(); } static Stream *wrapEOFStream(Stream *str) { if (dynamic_cast(str)) { // str is already a EOFStream, no need to wrap it in another EOFStream return str; } else { return new EOFStream(str); } } Stream *Stream::addFilters(Dict *dict, int recursion) { Object obj, obj2; Object params, params2; Stream *str; int i; str = this; obj = dict->lookup("Filter", recursion); if (obj.isNull()) { obj = dict->lookup("F", recursion); } params = dict->lookup("DecodeParms", recursion); if (params.isNull()) { params = dict->lookup("DP", recursion); } if (obj.isName()) { str = makeFilter(obj.getName(), str, ¶ms, recursion, dict); } else if (obj.isArray()) { for (i = 0; i < obj.arrayGetLength(); ++i) { obj2 = obj.arrayGet(i, recursion); if (params.isArray()) { params2 = params.arrayGet(i, recursion); } else { params2.setToNull(); } if (obj2.isName()) { str = makeFilter(obj2.getName(), str, ¶ms2, recursion); } else { error(errSyntaxError, getPos(), "Bad filter name"); str = wrapEOFStream(str); } } } else if (!obj.isNull()) { error(errSyntaxError, getPos(), "Bad 'Filter' attribute in stream"); } return str; } bool Stream::isEncrypted() const { for (const Stream *str = this; str != nullptr; str = str->getNextStream()) { if (str->getKind() == strCrypt) { return true; } } return false; } class BaseStreamStream : public Stream { public: explicit BaseStreamStream(Stream *strA) : str(strA) { } ~BaseStreamStream() override; StreamKind getKind() const override { return str->getBaseStream()->getKind(); } void reset() override { 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(); } 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(); } Stream *getUndecodedStream() override { return str->getBaseStream()->getUndecodedStream(); } Dict *getDict() override { return str->getBaseStream()->getDict(); } Object *getDictObject() override { return str->getBaseStream()->getDictObject(); } private: std::unique_ptr str; }; BaseStreamStream::~BaseStreamStream() = default; Stream *Stream::makeFilter(const char *name, Stream *str, Object *params, int recursion, Dict *dict) { int pred; // parameters int colors; int bits; int early; int encoding; bool endOfLine, byteAlign, endOfBlock, black, damagedRowsBeforeError; int columns, rows; Object obj; if (!strcmp(name, "ASCIIHexDecode") || !strcmp(name, "AHx")) { str = new ASCIIHexStream(str); } else if (!strcmp(name, "ASCII85Decode") || !strcmp(name, "A85")) { str = new ASCII85Stream(str); } else if (!strcmp(name, "LZWDecode") || !strcmp(name, "LZW")) { pred = 1; columns = 1; colors = 1; bits = 8; early = 1; if (params->isDict()) { obj = params->dictLookup("Predictor", recursion); if (obj.isInt()) { pred = obj.getInt(); } obj = params->dictLookup("Columns", recursion); if (obj.isInt()) { columns = obj.getInt(); } obj = params->dictLookup("Colors", recursion); if (obj.isInt()) { colors = obj.getInt(); } obj = params->dictLookup("BitsPerComponent", recursion); if (obj.isInt()) { bits = obj.getInt(); } obj = params->dictLookup("EarlyChange", recursion); if (obj.isInt()) { early = obj.getInt(); } } str = new LZWStream(str, pred, columns, colors, bits, early); } else if (!strcmp(name, "RunLengthDecode") || !strcmp(name, "RL")) { str = new RunLengthStream(str); } else if (!strcmp(name, "CCITTFaxDecode") || !strcmp(name, "CCF")) { encoding = 0; endOfLine = false; byteAlign = false; columns = 1728; rows = 0; endOfBlock = true; black = false; damagedRowsBeforeError = false; if (params->isDict()) { obj = params->dictLookup("K", recursion); if (obj.isInt()) { encoding = obj.getInt(); } obj = params->dictLookup("EndOfLine", recursion); if (obj.isBool()) { endOfLine = obj.getBool(); } obj = params->dictLookup("EncodedByteAlign", recursion); if (obj.isBool()) { byteAlign = obj.getBool(); } obj = params->dictLookup("Columns", recursion); if (obj.isInt()) { columns = obj.getInt(); } obj = params->dictLookup("Rows", recursion); if (obj.isInt()) { rows = obj.getInt(); } obj = params->dictLookup("EndOfBlock", recursion); if (obj.isBool()) { endOfBlock = obj.getBool(); } obj = params->dictLookup("BlackIs1", recursion); if (obj.isBool()) { black = obj.getBool(); } obj = params->dictLookup("DamagedRowsBeforeError", recursion); if (obj.isInt()) { damagedRowsBeforeError = obj.getInt(); } } str = new CCITTFaxStream(str, encoding, endOfLine, byteAlign, columns, rows, endOfBlock, black, damagedRowsBeforeError); } else if (!strcmp(name, "DCTDecode") || !strcmp(name, "DCT")) { #ifdef HAVE_DCT_DECODER int colorXform = -1; if (params->isDict()) { obj = params->dictLookup("ColorTransform", recursion); if (obj.isInt()) { colorXform = obj.getInt(); } } str = new DCTStream(str, colorXform, dict, recursion); #else error(errSyntaxError, getPos(), "Unknown filter '{0:s}'", name); str = wrapEOFStream(str); #endif } else if (!strcmp(name, "FlateDecode") || !strcmp(name, "Fl")) { pred = 1; columns = 1; colors = 1; bits = 8; if (params->isDict()) { obj = params->dictLookup("Predictor", recursion); if (obj.isInt()) { pred = obj.getInt(); } obj = params->dictLookup("Columns", recursion); if (obj.isInt()) { columns = obj.getInt(); } obj = params->dictLookup("Colors", recursion); if (obj.isInt()) { colors = obj.getInt(); } obj = params->dictLookup("BitsPerComponent", recursion); if (obj.isInt()) { bits = obj.getInt(); } } str = new FlateStream(str, pred, columns, colors, bits); } else if (!strcmp(name, "JBIG2Decode")) { Object globals; if (params->isDict()) { XRef *xref = params->getDict()->getXRef(); obj = params->dictLookupNF("JBIG2Globals").copy(); globals = obj.fetch(xref, recursion); } str = new JBIG2Stream(str, std::move(globals), &obj); } else if (!strcmp(name, "JPXDecode")) { #ifdef HAVE_JPX_DECODER str = new JPXStream(str); #else error(errSyntaxError, getPos(), "Unknown filter '{0:s}'", name); str = wrapEOFStream(str); #endif } else if (!strcmp(name, "Crypt")) { if (str->getKind() == strCrypt) { str = new BaseStreamStream(str); } else { error(errSyntaxError, getPos(), "Can't revert non decrypt streams"); } } else { error(errSyntaxError, getPos(), "Unknown filter '{0:s}'", name); str = wrapEOFStream(str); } return str; } //------------------------------------------------------------------------ // OutStream //------------------------------------------------------------------------ OutStream::OutStream() { } OutStream::~OutStream() { } //------------------------------------------------------------------------ // FileOutStream //------------------------------------------------------------------------ FileOutStream::FileOutStream(FILE *fa, Goffset startA) { f = fa; start = startA; } FileOutStream::~FileOutStream() { close(); } void FileOutStream::close() { } Goffset FileOutStream::getPos() { return Gftell(f); } void FileOutStream::put(char c) { fputc(c, f); } void FileOutStream::printf(const char *format, ...) { va_list argptr; va_start(argptr, format); vfprintf(f, format, argptr); va_end(argptr); } //------------------------------------------------------------------------ // BaseStream //------------------------------------------------------------------------ BaseStream::BaseStream(Object &&dictA, Goffset lengthA) { dict = std::move(dictA); length = lengthA; } BaseStream::~BaseStream() { } //------------------------------------------------------------------------ // BaseStream //------------------------------------------------------------------------ BaseSeekInputStream::BaseSeekInputStream(Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA) : BaseStream(std::move(dictA), lengthA), start(startA), limited(limitedA), bufPtr(buf), bufEnd(buf), bufPos(start), savePos(0), saved(false) { } BaseSeekInputStream::~BaseSeekInputStream() { } void BaseSeekInputStream::reset() { savePos = currentPos(); setCurrentPos(start); saved = true; bufPtr = bufEnd = buf; bufPos = start; } void BaseSeekInputStream::close() { if (!saved) { return; } setCurrentPos(savePos); saved = false; } void BaseSeekInputStream::setPos(Goffset pos, int dir) { if (dir >= 0) { setCurrentPos(pos); bufPos = pos; } else { if (pos > length) { pos = length; } bufPos = length - pos; setCurrentPos(bufPos); } bufPtr = bufEnd = buf; } void BaseSeekInputStream::moveStart(Goffset delta) { start += delta; bufPtr = bufEnd = buf; bufPos = start; } bool BaseSeekInputStream::fillBuf() { Goffset n; bufPos += bufEnd - buf; bufPtr = bufEnd = buf; if (limited && bufPos >= start + length) { return false; } if (limited && bufPos + seekInputStreamBufSize > start + length) { n = start + length - bufPos; } else { n = seekInputStreamBufSize - (bufPos % seekInputStreamBufSize); } n = read(buf, n); bufEnd = buf + n; if (bufPtr >= bufEnd) { return false; } return true; } int BaseSeekInputStream::getChars(int nChars, unsigned char *buffer) { int n, m; n = 0; while (n < nChars) { if (bufPtr >= bufEnd) { if (!fillBuf()) { break; } } m = (int)(bufEnd - bufPtr); if (m > nChars - n) { m = nChars - n; } memcpy(buffer + n, bufPtr, m); bufPtr += m; n += m; } return n; } //------------------------------------------------------------------------ // FilterStream //------------------------------------------------------------------------ FilterStream::FilterStream(Stream *strA) { str = strA; } FilterStream::~FilterStream() { } void FilterStream::close() { str->close(); } void FilterStream::setPos(Goffset pos, int dir) { error(errInternal, -1, "Internal: called setPos() on FilterStream"); } //------------------------------------------------------------------------ // ImageStream //------------------------------------------------------------------------ ImageStream::ImageStream(Stream *strA, int widthA, int nCompsA, int nBitsA) { int imgLineSize; str = strA; width = widthA; nComps = nCompsA; nBits = nBitsA; nVals = width * nComps; inputLineSize = (nVals * nBits + 7) >> 3; if (nComps <= 0 || nBits <= 0 || nVals > INT_MAX / nBits - 7 || width > INT_MAX / nComps) { inputLineSize = -1; } inputLine = (unsigned char *)gmallocn_checkoverflow(inputLineSize, sizeof(char)); if (nBits == 8) { imgLine = (unsigned char *)inputLine; } else { if (nBits == 1) { imgLineSize = (nVals + 7) & ~7; } else { imgLineSize = nVals; } if (nComps <= 0 || width > INT_MAX / nComps) { imgLineSize = -1; } imgLine = (unsigned char *)gmallocn_checkoverflow(imgLineSize, sizeof(unsigned char)); } imgIdx = nVals; } ImageStream::~ImageStream() { if (imgLine != (unsigned char *)inputLine) { gfree(imgLine); } gfree(inputLine); } void ImageStream::reset() { str->reset(); } void ImageStream::close() { str->close(); } bool ImageStream::getPixel(unsigned char *pix) { int i; if (imgIdx >= nVals) { if (!getLine()) { return false; } imgIdx = 0; } for (i = 0; i < nComps; ++i) { pix[i] = imgLine[imgIdx++]; } return true; } unsigned char *ImageStream::getLine() { if (unlikely(inputLine == nullptr)) { return nullptr; } int readChars = str->doGetChars(inputLineSize, inputLine); if (unlikely(readChars == -1)) { readChars = 0; } for (; readChars < inputLineSize; readChars++) { inputLine[readChars] = EOF; } if (nBits == 1) { unsigned char *p = inputLine; for (int i = 0; i < nVals; i += 8) { const int c = *p++; imgLine[i + 0] = (unsigned char)((c >> 7) & 1); imgLine[i + 1] = (unsigned char)((c >> 6) & 1); imgLine[i + 2] = (unsigned char)((c >> 5) & 1); imgLine[i + 3] = (unsigned char)((c >> 4) & 1); imgLine[i + 4] = (unsigned char)((c >> 3) & 1); imgLine[i + 5] = (unsigned char)((c >> 2) & 1); imgLine[i + 6] = (unsigned char)((c >> 1) & 1); imgLine[i + 7] = (unsigned char)(c & 1); } } else if (nBits == 8) { // special case: imgLine == inputLine } else if (nBits == 16) { // this is a hack to support 16 bits images, everywhere // we assume a component fits in 8 bits, with this hack // we treat 16 bit images as 8 bit ones until it's fixed correctly. // The hack has another part on GfxImageColorMap::GfxImageColorMap unsigned char *p = inputLine; for (int i = 0; i < nVals; ++i) { imgLine[i] = *p++; p++; } } else { const unsigned long bitMask = (1 << nBits) - 1; unsigned long buf = 0; int bits = 0; unsigned char *p = inputLine; for (int i = 0; i < nVals; ++i) { while (bits < nBits) { buf = (buf << 8) | (*p++ & 0xff); bits += 8; } imgLine[i] = (unsigned char)((buf >> (bits - nBits)) & bitMask); bits -= nBits; } } return imgLine; } void ImageStream::skipLine() { str->doGetChars(inputLineSize, inputLine); } //------------------------------------------------------------------------ // StreamPredictor //------------------------------------------------------------------------ StreamPredictor::StreamPredictor(Stream *strA, int predictorA, int widthA, int nCompsA, int nBitsA) { str = strA; predictor = predictorA; width = widthA; nComps = nCompsA; nBits = nBitsA; predLine = nullptr; ok = false; 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; rowBytes = ((nVals * nBits + 7) >> 3) + pixBytes; predLine = (unsigned char *)gmalloc(rowBytes); memset(predLine, 0, rowBytes); predIdx = rowBytes; ok = true; } StreamPredictor::~StreamPredictor() { gfree(predLine); } int StreamPredictor::lookChar() { if (predIdx >= rowBytes) { if (!getNextLine()) { return EOF; } } return predLine[predIdx]; } int StreamPredictor::getChar() { if (predIdx >= rowBytes) { if (!getNextLine()) { return EOF; } } return predLine[predIdx++]; } int StreamPredictor::getChars(int nChars, unsigned char *buffer) { int n, m; n = 0; while (n < nChars) { if (predIdx >= rowBytes) { if (!getNextLine()) { break; } } m = rowBytes - predIdx; if (m > nChars - n) { m = nChars - n; } memcpy(buffer + n, predLine + predIdx, m); predIdx += m; n += m; } return n; } bool StreamPredictor::getNextLine() { int curPred; unsigned char upLeftBuf[gfxColorMaxComps * 2 + 1]; int left, up, upLeft, p, pa, pb, pc; int c; unsigned long inBuf, outBuf; int inBits, outBits; int i, j, k, kk; // get PNG optimum predictor number if (predictor >= 10) { if ((curPred = str->getRawChar()) == EOF) { return false; } curPred += 10; } else { curPred = predictor; } // read the raw line, apply PNG (byte) predictor int *rawCharLine = new int[rowBytes - pixBytes]; str->getRawChars(rowBytes - pixBytes, rawCharLine); memset(upLeftBuf, 0, pixBytes + 1); for (i = pixBytes; i < rowBytes; ++i) { for (j = pixBytes; j > 0; --j) { upLeftBuf[j] = upLeftBuf[j - 1]; } upLeftBuf[0] = predLine[i]; if ((c = rawCharLine[i - pixBytes]) == EOF) { if (i > pixBytes) { // this ought to return false, but some (broken) PDF files // contain truncated image data, and Adobe apparently reads the // last partial line break; } delete[] rawCharLine; return false; } switch (curPred) { case 11: // PNG sub predLine[i] = predLine[i - pixBytes] + (unsigned char)c; break; case 12: // PNG up predLine[i] = predLine[i] + (unsigned char)c; break; case 13: // PNG average predLine[i] = ((predLine[i - pixBytes] + predLine[i]) >> 1) + (unsigned char)c; break; case 14: // PNG Paeth left = predLine[i - pixBytes]; up = predLine[i]; upLeft = upLeftBuf[pixBytes]; p = left + up - upLeft; if ((pa = p - left) < 0) { pa = -pa; } if ((pb = p - up) < 0) { pb = -pb; } if ((pc = p - upLeft) < 0) { pc = -pc; } if (pa <= pb && pa <= pc) { predLine[i] = left + (unsigned char)c; } else if (pb <= pc) { predLine[i] = up + (unsigned char)c; } else { predLine[i] = upLeft + (unsigned char)c; } break; case 10: // PNG none default: // no predictor or TIFF predictor predLine[i] = (unsigned char)c; break; } } delete[] rawCharLine; // apply TIFF (component) predictor if (predictor == 2) { if (nBits == 1 && nComps == 1) { inBuf = predLine[pixBytes - 1]; for (i = pixBytes; i < rowBytes; ++i) { c = predLine[i] ^ inBuf; c ^= c >> 1; c ^= c >> 2; c ^= c >> 4; inBuf = (c & 1) << 7; predLine[i] = c; } } else if (nBits == 8) { for (i = pixBytes; i < rowBytes; ++i) { predLine[i] += predLine[i - nComps]; } } else { memset(upLeftBuf, 0, nComps + 1); const unsigned long bitMask = (1 << nBits) - 1; inBuf = outBuf = 0; inBits = outBits = 0; j = k = pixBytes; for (i = 0; i < width; ++i) { for (kk = 0; kk < nComps; ++kk) { while (inBits < nBits) { inBuf = (inBuf << 8) | (predLine[j++] & 0xff); inBits += 8; } upLeftBuf[kk] = (unsigned char)((upLeftBuf[kk] + (inBuf >> (inBits - nBits))) & bitMask); inBits -= nBits; outBuf = (outBuf << nBits) | upLeftBuf[kk]; outBits += nBits; if (outBits >= 8) { predLine[k++] = (unsigned char)(outBuf >> (outBits - 8)); outBits -= 8; } } } if (outBits > 0) { predLine[k++] = (unsigned char)((outBuf << (8 - outBits)) + (inBuf & ((1 << (8 - outBits)) - 1))); } } } // reset to start of line predIdx = pixBytes; return true; } //------------------------------------------------------------------------ // FileStream //------------------------------------------------------------------------ FileStream::FileStream(GooFile *fileA, Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA) : BaseStream(std::move(dictA), lengthA) { file = fileA; offset = start = startA; limited = limitedA; length = lengthA; bufPtr = bufEnd = buf; bufPos = start; savePos = 0; saved = false; needsEncryptionOnSave = false; } FileStream::~FileStream() { close(); } BaseStream *FileStream::copy() { return new FileStream(file, start, limited, length, dict.copy()); } Stream *FileStream::makeSubStream(Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA) { return new FileStream(file, startA, limitedA, lengthA, std::move(dictA)); } void FileStream::reset() { savePos = offset; offset = start; saved = true; bufPtr = bufEnd = buf; bufPos = start; } void FileStream::close() { if (saved) { offset = savePos; saved = false; } } bool FileStream::fillBuf() { int n; bufPos += bufEnd - buf; bufPtr = bufEnd = buf; if (limited && bufPos >= start + length) { return false; } if (limited && bufPos + fileStreamBufSize > start + length) { n = start + length - bufPos; } else { n = fileStreamBufSize; } n = file->read(buf, n, offset); if (n == -1) { return false; } offset += n; bufEnd = buf + n; if (bufPtr >= bufEnd) { return false; } return true; } void FileStream::setPos(Goffset pos, int dir) { Goffset size; if (dir >= 0) { offset = bufPos = pos; } else { size = file->size(); if (pos > size) { pos = size; } offset = size - pos; bufPos = offset; } bufPtr = bufEnd = buf; } void FileStream::moveStart(Goffset delta) { start += delta; bufPtr = bufEnd = buf; bufPos = start; } //------------------------------------------------------------------------ // CachedFileStream //------------------------------------------------------------------------ CachedFileStream::CachedFileStream(CachedFile *ccA, Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA) : BaseStream(std::move(dictA), lengthA) { cc = ccA; start = startA; limited = limitedA; length = lengthA; bufPtr = bufEnd = buf; bufPos = start; savePos = 0; saved = false; } CachedFileStream::~CachedFileStream() { close(); cc->decRefCnt(); } BaseStream *CachedFileStream::copy() { cc->incRefCnt(); return new CachedFileStream(cc, start, limited, length, dict.copy()); } Stream *CachedFileStream::makeSubStream(Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA) { cc->incRefCnt(); return new CachedFileStream(cc, startA, limitedA, lengthA, std::move(dictA)); } void CachedFileStream::reset() { savePos = (unsigned int)cc->tell(); cc->seek(start, SEEK_SET); saved = true; bufPtr = bufEnd = buf; bufPos = start; } void CachedFileStream::close() { if (saved) { cc->seek(savePos, SEEK_SET); saved = false; } } bool CachedFileStream::fillBuf() { int n; bufPos += bufEnd - buf; bufPtr = bufEnd = buf; if (limited && bufPos >= start + length) { return false; } if (limited && bufPos + cachedStreamBufSize > start + length) { n = start + length - bufPos; } else { n = cachedStreamBufSize - (bufPos % cachedStreamBufSize); } n = cc->read(buf, 1, n); bufEnd = buf + n; if (bufPtr >= bufEnd) { return false; } return true; } void CachedFileStream::setPos(Goffset pos, int dir) { unsigned int size; if (dir >= 0) { cc->seek(pos, SEEK_SET); bufPos = pos; } else { cc->seek(0, SEEK_END); size = (unsigned int)cc->tell(); if (pos > size) { pos = (unsigned int)size; } cc->seek(-(int)pos, SEEK_END); bufPos = (unsigned int)cc->tell(); } bufPtr = bufEnd = buf; } void CachedFileStream::moveStart(Goffset delta) { start += delta; bufPtr = bufEnd = buf; bufPos = start; } MemStream::~MemStream() = default; AutoFreeMemStream::~AutoFreeMemStream() { gfree(buf); } bool AutoFreeMemStream::isFilterRemovalForbidden() const { return filterRemovalForbidden; } void AutoFreeMemStream::setFilterRemovalForbidden(bool forbidden) { filterRemovalForbidden = forbidden; } //------------------------------------------------------------------------ // EmbedStream //------------------------------------------------------------------------ EmbedStream::EmbedStream(Stream *strA, Object &&dictA, bool limitedA, Goffset lengthA, bool reusableA) : BaseStream(std::move(dictA), lengthA) { str = strA; limited = limitedA; length = lengthA; reusable = reusableA; record = false; replay = false; start = str->getPos(); if (reusable) { bufData = (unsigned char *)gmalloc(16384); bufMax = 16384; bufLen = 0; record = true; } } EmbedStream::~EmbedStream() { if (reusable) { gfree(bufData); } } void EmbedStream::reset() { if (str->getPos() != start) { str->reset(); // Might be a FilterStream that does not support str->setPos(start) while (str->getPos() < start) { if (str->getChar() == EOF) { break; } } if (str->getPos() != start) { error(errInternal, -1, "Failed to reset EmbedStream"); } } record = false; replay = false; bufPos = 0; } BaseStream *EmbedStream::copy() { error(errInternal, -1, "Called copy() on EmbedStream"); return nullptr; } Stream *EmbedStream::makeSubStream(Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA) { error(errInternal, -1, "Called makeSubStream() on EmbedStream"); return nullptr; } void EmbedStream::rewind() { record = false; replay = true; bufPos = 0; } void EmbedStream::restore() { replay = false; } Goffset EmbedStream::getPos() { if (replay) { return bufPos; } else { return str->getPos(); } } int EmbedStream::getChar() { if (replay) { if (bufPos < bufLen) { return bufData[bufPos++]; } else { return EOF; } } else { if (limited && !length) { return EOF; } int c = str->getChar(); --length; if (record) { bufData[bufLen] = c; bufLen++; if (bufLen >= bufMax) { bufMax *= 2; bufData = (unsigned char *)grealloc(bufData, bufMax); } } return c; } } int EmbedStream::lookChar() { if (replay) { if (bufPos < bufLen) { return bufData[bufPos]; } else { return EOF; } } else { if (limited && !length) { return EOF; } return str->lookChar(); } } int EmbedStream::getChars(int nChars, unsigned char *buffer) { int len; if (nChars <= 0) { return 0; } if (replay) { if (bufPos >= bufLen) { return EOF; } len = bufLen - bufPos; if (nChars > len) { nChars = len; } memcpy(buffer, bufData, nChars); return len; } else { if (limited && length < nChars) { nChars = length; } len = str->doGetChars(nChars, buffer); if (record) { if (bufLen + len >= bufMax) { while (bufLen + len >= bufMax) { bufMax *= 2; } bufData = (unsigned char *)grealloc(bufData, bufMax); } memcpy(bufData + bufLen, buffer, len); bufLen += len; } } return len; } void EmbedStream::setPos(Goffset pos, int dir) { error(errInternal, -1, "Internal: called setPos() on EmbedStream"); } Goffset EmbedStream::getStart() { error(errInternal, -1, "Internal: called getStart() on EmbedStream"); return 0; } void EmbedStream::moveStart(Goffset delta) { error(errInternal, -1, "Internal: called moveStart() on EmbedStream"); } //------------------------------------------------------------------------ // ASCIIHexStream //------------------------------------------------------------------------ ASCIIHexStream::ASCIIHexStream(Stream *strA) : FilterStream(strA) { buf = EOF; eof = false; } ASCIIHexStream::~ASCIIHexStream() { delete str; } void ASCIIHexStream::reset() { str->reset(); buf = EOF; eof = false; } int ASCIIHexStream::lookChar() { int c1, c2, x; if (buf != EOF) { return buf; } if (eof) { buf = EOF; return EOF; } do { c1 = str->getChar(); } while (isspace(c1)); if (c1 == '>') { eof = true; buf = EOF; return buf; } do { c2 = str->getChar(); } while (isspace(c2)); if (c2 == '>') { eof = true; c2 = '0'; } if (c1 >= '0' && c1 <= '9') { x = (c1 - '0') << 4; } else if (c1 >= 'A' && c1 <= 'F') { x = (c1 - 'A' + 10) << 4; } else if (c1 >= 'a' && c1 <= 'f') { x = (c1 - 'a' + 10) << 4; } else if (c1 == EOF) { eof = true; x = 0; } else { error(errSyntaxError, getPos(), "Illegal character <{0:02x}> in ASCIIHex stream", c1); x = 0; } if (c2 >= '0' && c2 <= '9') { x += c2 - '0'; } else if (c2 >= 'A' && c2 <= 'F') { x += c2 - 'A' + 10; } else if (c2 >= 'a' && c2 <= 'f') { x += c2 - 'a' + 10; } else if (c2 == EOF) { eof = true; x = 0; } else { error(errSyntaxError, getPos(), "Illegal character <{0:02x}> in ASCIIHex stream", c2); } buf = x & 0xff; return buf; } GooString *ASCIIHexStream::getPSFilter(int psLevel, const char *indent) { GooString *s; if (psLevel < 2) { return nullptr; } if (!(s = str->getPSFilter(psLevel, indent))) { return nullptr; } s->append(indent)->append("/ASCIIHexDecode filter\n"); return s; } bool ASCIIHexStream::isBinary(bool last) const { return str->isBinary(false); } //------------------------------------------------------------------------ // ASCII85Stream //------------------------------------------------------------------------ ASCII85Stream::ASCII85Stream(Stream *strA) : FilterStream(strA) { index = n = 0; eof = false; } ASCII85Stream::~ASCII85Stream() { delete str; } void ASCII85Stream::reset() { str->reset(); index = n = 0; eof = false; } int ASCII85Stream::lookChar() { int k; unsigned long t; if (index >= n) { if (eof) { return EOF; } index = 0; do { c[0] = str->getChar(); } while (Lexer::isSpace(c[0])); if (c[0] == '~' || c[0] == EOF) { eof = true; n = 0; return EOF; } else if (c[0] == 'z') { b[0] = b[1] = b[2] = b[3] = 0; n = 4; } else { for (k = 1; k < 5; ++k) { do { c[k] = str->getChar(); } while (Lexer::isSpace(c[k])); if (c[k] == '~' || c[k] == EOF) { break; } } n = k - 1; if (k < 5 && (c[k] == '~' || c[k] == EOF)) { for (++k; k < 5; ++k) { c[k] = 0x21 + 84; } eof = true; } t = 0; for (k = 0; k < 5; ++k) { t = t * 85 + (c[k] - 0x21); } for (k = 3; k >= 0; --k) { b[k] = (int)(t & 0xff); t >>= 8; } } } return b[index]; } GooString *ASCII85Stream::getPSFilter(int psLevel, const char *indent) { GooString *s; if (psLevel < 2) { return nullptr; } if (!(s = str->getPSFilter(psLevel, indent))) { return nullptr; } s->append(indent)->append("/ASCII85Decode filter\n"); return s; } bool ASCII85Stream::isBinary(bool last) const { return str->isBinary(false); } //------------------------------------------------------------------------ // LZWStream //------------------------------------------------------------------------ LZWStream::LZWStream(Stream *strA, int predictor, int columns, int colors, int bits, int earlyA) : FilterStream(strA) { if (predictor != 1) { pred = new StreamPredictor(this, predictor, columns, colors, bits); if (!pred->isOk()) { delete pred; pred = nullptr; } } else { pred = nullptr; } early = earlyA; eof = false; inputBits = 0; clearTable(); } LZWStream::~LZWStream() { if (pred) { delete pred; } delete str; } int LZWStream::getChar() { if (pred) { return pred->getChar(); } if (eof) { return EOF; } if (seqIndex >= seqLength) { if (!processNextCode()) { return EOF; } } return seqBuf[seqIndex++]; } int LZWStream::lookChar() { if (pred) { return pred->lookChar(); } if (eof) { return EOF; } if (seqIndex >= seqLength) { if (!processNextCode()) { return EOF; } } return seqBuf[seqIndex]; } void LZWStream::getRawChars(int nChars, int *buffer) { for (int i = 0; i < nChars; ++i) { buffer[i] = doGetRawChar(); } } int LZWStream::getRawChar() { return doGetRawChar(); } int LZWStream::getChars(int nChars, unsigned char *buffer) { int n, m; if (pred) { return pred->getChars(nChars, buffer); } if (eof) { return 0; } n = 0; while (n < nChars) { if (seqIndex >= seqLength) { if (!processNextCode()) { break; } } m = seqLength - seqIndex; if (m > nChars - n) { m = nChars - n; } memcpy(buffer + n, seqBuf + seqIndex, m); seqIndex += m; n += m; } return n; } void LZWStream::reset() { str->reset(); eof = false; inputBits = 0; clearTable(); } bool LZWStream::processNextCode() { int code; int nextLength; int i, j; // check for EOF if (eof) { return false; } // check for eod and clear-table codes start: code = getCode(); if (code == EOF || code == 257) { eof = true; return false; } if (code == 256) { clearTable(); goto start; } // process the next code nextLength = seqLength + 1; if (code < 256) { seqBuf[0] = code; seqLength = 1; } else if (code < nextCode) { seqLength = table[code].length; for (i = seqLength - 1, j = code; i > 0; --i) { seqBuf[i] = table[j].tail; j = table[j].head; } seqBuf[0] = j; } else if (code == nextCode) { seqBuf[seqLength] = newChar; ++seqLength; } else { error(errSyntaxError, getPos(), "Bad LZW stream - unexpected code"); eof = true; return false; } newChar = seqBuf[0]; if (first) { first = false; } else { if (nextCode < 4097) { table[nextCode].length = nextLength; table[nextCode].head = prevCode; table[nextCode].tail = newChar; ++nextCode; } if (nextCode + early == 512) { nextBits = 10; } else if (nextCode + early == 1024) { nextBits = 11; } else if (nextCode + early == 2048) { nextBits = 12; } } prevCode = code; // reset buffer seqIndex = 0; return true; } void LZWStream::clearTable() { nextCode = 258; nextBits = 9; seqIndex = seqLength = 0; first = true; newChar = 0; } int LZWStream::getCode() { int c; int code; while (inputBits < nextBits) { if ((c = str->getChar()) == EOF) { return EOF; } inputBuf = (inputBuf << 8) | static_cast(c & 0xff); inputBits += 8; } code = static_cast((inputBuf >> (inputBits - nextBits)) & ((1 << nextBits) - 1)); inputBits -= nextBits; return code; } GooString *LZWStream::getPSFilter(int psLevel, const char *indent) { GooString *s; if (psLevel < 2 || pred) { return nullptr; } if (!(s = str->getPSFilter(psLevel, indent))) { return nullptr; } s->append(indent)->append("<< "); if (!early) { s->append("/EarlyChange 0 "); } s->append(">> /LZWDecode filter\n"); return s; } bool LZWStream::isBinary(bool last) const { return str->isBinary(true); } //------------------------------------------------------------------------ // RunLengthStream //------------------------------------------------------------------------ RunLengthStream::RunLengthStream(Stream *strA) : FilterStream(strA) { bufPtr = bufEnd = buf; eof = false; } RunLengthStream::~RunLengthStream() { delete str; } void RunLengthStream::reset() { str->reset(); bufPtr = bufEnd = buf; eof = false; } int RunLengthStream::getChars(int nChars, unsigned char *buffer) { int n, m; n = 0; while (n < nChars) { if (bufPtr >= bufEnd) { if (!fillBuf()) { break; } } m = (int)(bufEnd - bufPtr); if (m > nChars - n) { m = nChars - n; } memcpy(buffer + n, bufPtr, m); bufPtr += m; n += m; } return n; } GooString *RunLengthStream::getPSFilter(int psLevel, const char *indent) { GooString *s; if (psLevel < 2) { return nullptr; } if (!(s = str->getPSFilter(psLevel, indent))) { return nullptr; } s->append(indent)->append("/RunLengthDecode filter\n"); return s; } bool RunLengthStream::isBinary(bool last) const { return str->isBinary(true); } bool RunLengthStream::fillBuf() { int c; int n, i; if (eof) { return false; } c = str->getChar(); if (c == 0x80 || c == EOF) { eof = true; return false; } if (c < 0x80) { n = c + 1; for (i = 0; i < n; ++i) { buf[i] = (char)str->getChar(); } } else { n = 0x101 - c; c = str->getChar(); for (i = 0; i < n; ++i) { buf[i] = (char)c; } } bufPtr = buf; bufEnd = buf + n; return true; } //------------------------------------------------------------------------ // CCITTFaxStream //------------------------------------------------------------------------ CCITTFaxStream::CCITTFaxStream(Stream *strA, int encodingA, bool endOfLineA, bool byteAlignA, int columnsA, int rowsA, bool endOfBlockA, bool blackA, int damagedRowsBeforeErrorA) : FilterStream(strA) { encoding = encodingA; endOfLine = endOfLineA; byteAlign = byteAlignA; columns = columnsA; damagedRowsBeforeError = damagedRowsBeforeErrorA; if (columns < 1) { columns = 1; } else if (columns > INT_MAX - 2) { columns = INT_MAX - 2; } rows = rowsA; endOfBlock = endOfBlockA; black = blackA; // 0 <= codingLine[0] < codingLine[1] < ... < codingLine[n] = columns // ---> max codingLine size = columns + 1 // refLine has one extra guard entry at the end // ---> max refLine size = columns + 2 codingLine = (int *)gmallocn_checkoverflow(columns + 1, sizeof(int)); refLine = (int *)gmallocn_checkoverflow(columns + 2, sizeof(int)); if (codingLine != nullptr && refLine != nullptr) { eof = false; codingLine[0] = columns; } else { eof = true; } row = 0; nextLine2D = encoding < 0; inputBits = 0; a0i = 0; outputBits = 0; buf = EOF; } CCITTFaxStream::~CCITTFaxStream() { delete str; gfree(refLine); gfree(codingLine); } void CCITTFaxStream::ccittReset(bool unfiltered) { if (unfiltered) { str->unfilteredReset(); } else { str->reset(); } row = 0; nextLine2D = encoding < 0; inputBits = 0; a0i = 0; outputBits = 0; buf = EOF; } void CCITTFaxStream::unfilteredReset() { ccittReset(true); } void CCITTFaxStream::reset() { int code1; ccittReset(false); if (codingLine != nullptr && refLine != nullptr) { eof = false; codingLine[0] = columns; } else { eof = true; } // skip any initial zero bits and end-of-line marker, and get the 2D // encoding tag while ((code1 = lookBits(12)) == 0) { eatBits(1); } if (code1 == 0x001) { eatBits(12); endOfLine = true; } if (encoding > 0) { nextLine2D = !lookBits(1); eatBits(1); } } inline void CCITTFaxStream::addPixels(int a1, int blackPixels) { if (a1 > codingLine[a0i]) { if (a1 > columns) { error(errSyntaxError, getPos(), "CCITTFax row is wrong length ({0:d})", a1); err = true; a1 = columns; } if ((a0i & 1) ^ blackPixels) { ++a0i; } codingLine[a0i] = a1; } } inline void CCITTFaxStream::addPixelsNeg(int a1, int blackPixels) { if (a1 > codingLine[a0i]) { if (a1 > columns) { error(errSyntaxError, getPos(), "CCITTFax row is wrong length ({0:d})", a1); err = true; a1 = columns; } if ((a0i & 1) ^ blackPixels) { ++a0i; } codingLine[a0i] = a1; } else if (a1 < codingLine[a0i]) { if (a1 < 0) { error(errSyntaxError, getPos(), "Invalid CCITTFax code"); err = true; a1 = columns; } while (a0i > 0 && a1 <= codingLine[a0i - 1]) { --a0i; } codingLine[a0i] = a1; } } int CCITTFaxStream::lookChar() { int code1, code2, code3; int b1i, blackPixels, i, bits; bool gotEOL; if (buf != EOF) { return buf; } // read the next row if (outputBits == 0) { // if at eof just return EOF if (eof) { return EOF; } err = false; // 2-D encoding if (nextLine2D) { for (i = 0; i < columns && codingLine[i] < columns; ++i) { refLine[i] = codingLine[i]; } for (; i < columns + 2; ++i) { refLine[i] = columns; } codingLine[0] = 0; a0i = 0; b1i = 0; blackPixels = 0; // invariant: // refLine[b1i-1] <= codingLine[a0i] < refLine[b1i] < refLine[b1i+1] // <= columns // exception at left edge: // codingLine[a0i = 0] = refLine[b1i = 0] = 0 is possible // exception at right edge: // refLine[b1i] = refLine[b1i+1] = columns is possible while (codingLine[a0i] < columns && !err) { code1 = getTwoDimCode(); switch (code1) { case twoDimPass: if (likely(b1i + 1 < columns + 2)) { addPixels(refLine[b1i + 1], blackPixels); if (refLine[b1i + 1] < columns) { b1i += 2; } } break; case twoDimHoriz: code1 = code2 = 0; if (blackPixels) { do { code1 += code3 = getBlackCode(); } while (code3 >= 64); do { code2 += code3 = getWhiteCode(); } while (code3 >= 64); } else { do { code1 += code3 = getWhiteCode(); } while (code3 >= 64); do { code2 += code3 = getBlackCode(); } while (code3 >= 64); } addPixels(codingLine[a0i] + code1, blackPixels); if (codingLine[a0i] < columns) { addPixels(codingLine[a0i] + code2, blackPixels ^ 1); } while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; if (unlikely(b1i > columns + 1)) { error(errSyntaxError, getPos(), "Bad 2D code {0:04x} in CCITTFax stream", code1); err = true; break; } } break; case twoDimVertR3: if (unlikely(b1i > columns + 1)) { error(errSyntaxError, getPos(), "Bad 2D code {0:04x} in CCITTFax stream", code1); err = true; break; } addPixels(refLine[b1i] + 3, blackPixels); blackPixels ^= 1; if (codingLine[a0i] < columns) { ++b1i; while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; if (unlikely(b1i > columns + 1)) { error(errSyntaxError, getPos(), "Bad 2D code {0:04x} in CCITTFax stream", code1); err = true; break; } } } break; case twoDimVertR2: if (unlikely(b1i > columns + 1)) { error(errSyntaxError, getPos(), "Bad 2D code {0:04x} in CCITTFax stream", code1); err = true; break; } addPixels(refLine[b1i] + 2, blackPixels); blackPixels ^= 1; if (codingLine[a0i] < columns) { ++b1i; while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; if (unlikely(b1i > columns + 1)) { error(errSyntaxError, getPos(), "Bad 2D code {0:04x} in CCITTFax stream", code1); err = true; break; } } } break; case twoDimVertR1: if (unlikely(b1i > columns + 1)) { error(errSyntaxError, getPos(), "Bad 2D code {0:04x} in CCITTFax stream", code1); err = true; break; } addPixels(refLine[b1i] + 1, blackPixels); blackPixels ^= 1; if (codingLine[a0i] < columns) { ++b1i; while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; if (unlikely(b1i > columns + 1)) { error(errSyntaxError, getPos(), "Bad 2D code {0:04x} in CCITTFax stream", code1); err = true; break; } } } break; case twoDimVert0: if (unlikely(b1i > columns + 1)) { error(errSyntaxError, getPos(), "Bad 2D code {0:04x} in CCITTFax stream", code1); err = true; break; } addPixels(refLine[b1i], blackPixels); blackPixels ^= 1; if (codingLine[a0i] < columns) { ++b1i; while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; if (unlikely(b1i > columns + 1)) { error(errSyntaxError, getPos(), "Bad 2D code {0:04x} in CCITTFax stream", code1); err = true; break; } } } break; case twoDimVertL3: if (unlikely(b1i > columns + 1)) { error(errSyntaxError, getPos(), "Bad 2D code {0:04x} in CCITTFax stream", code1); err = true; break; } addPixelsNeg(refLine[b1i] - 3, blackPixels); blackPixels ^= 1; if (codingLine[a0i] < columns) { if (b1i > 0) { --b1i; } else { ++b1i; } while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; if (unlikely(b1i > columns + 1)) { error(errSyntaxError, getPos(), "Bad 2D code {0:04x} in CCITTFax stream", code1); err = true; break; } } } break; case twoDimVertL2: if (unlikely(b1i > columns + 1)) { error(errSyntaxError, getPos(), "Bad 2D code {0:04x} in CCITTFax stream", code1); err = true; break; } addPixelsNeg(refLine[b1i] - 2, blackPixels); blackPixels ^= 1; if (codingLine[a0i] < columns) { if (b1i > 0) { --b1i; } else { ++b1i; } while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; if (unlikely(b1i > columns + 1)) { error(errSyntaxError, getPos(), "Bad 2D code {0:04x} in CCITTFax stream", code1); err = true; break; } } } break; case twoDimVertL1: if (unlikely(b1i > columns + 1)) { error(errSyntaxError, getPos(), "Bad 2D code {0:04x} in CCITTFax stream", code1); err = true; break; } addPixelsNeg(refLine[b1i] - 1, blackPixels); blackPixels ^= 1; if (codingLine[a0i] < columns) { if (b1i > 0) { --b1i; } else { ++b1i; } while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; if (unlikely(b1i > columns + 1)) { error(errSyntaxError, getPos(), "Bad 2D code {0:04x} in CCITTFax stream", code1); err = true; break; } } } break; case EOF: addPixels(columns, 0); eof = true; break; default: error(errSyntaxError, getPos(), "Bad 2D code {0:04x} in CCITTFax stream", code1); addPixels(columns, 0); err = true; break; } } // 1-D encoding } else { codingLine[0] = 0; a0i = 0; blackPixels = 0; while (codingLine[a0i] < columns) { code1 = 0; if (blackPixels) { do { code1 += code3 = getBlackCode(); } while (code3 >= 64); } else { do { code1 += code3 = getWhiteCode(); } while (code3 >= 64); } addPixels(codingLine[a0i] + code1, blackPixels); blackPixels ^= 1; } } // check for end-of-line marker, skipping over any extra zero bits // (if EncodedByteAlign is true and EndOfLine is false, there can // be "false" EOL markers -- i.e., if the last n unused bits in // row i are set to zero, and the first 11-n bits in row i+1 // happen to be zero -- so we don't look for EOL markers in this // case) gotEOL = false; if (!endOfBlock && row == rows - 1) { eof = true; } else if (endOfLine || !byteAlign) { code1 = lookBits(12); if (endOfLine) { while (code1 != EOF && code1 != 0x001) { eatBits(1); code1 = lookBits(12); } } else { while (code1 == 0) { eatBits(1); code1 = lookBits(12); } } if (code1 == 0x001) { eatBits(12); gotEOL = true; } } // byte-align the row // (Adobe apparently doesn't do byte alignment after EOL markers // -- I've seen CCITT image data streams in two different formats, // both with the byteAlign flag set: // 1. xx:x0:01:yy:yy // 2. xx:00:1y:yy:yy // where xx is the previous line, yy is the next line, and colons // separate bytes.) if (byteAlign && !gotEOL) { inputBits &= ~7; } // check for end of stream if (lookBits(1) == EOF) { eof = true; } // get 2D encoding tag if (!eof && encoding > 0) { nextLine2D = !lookBits(1); eatBits(1); } // check for end-of-block marker if (endOfBlock && !endOfLine && byteAlign) { // in this case, we didn't check for an EOL code above, so we // need to check here code1 = lookBits(24); if (code1 == 0x001001) { eatBits(12); gotEOL = true; } } if (endOfBlock && gotEOL) { code1 = lookBits(12); if (code1 == 0x001) { eatBits(12); if (encoding > 0) { lookBits(1); eatBits(1); } if (encoding >= 0) { for (i = 0; i < 4; ++i) { code1 = lookBits(12); if (code1 != 0x001) { error(errSyntaxError, getPos(), "Bad RTC code in CCITTFax stream"); } eatBits(12); if (encoding > 0) { lookBits(1); eatBits(1); } } } eof = true; } // look for an end-of-line marker after an error -- we only do // this if we know the stream contains end-of-line markers because // the "just plow on" technique tends to work better otherwise } else if (err && endOfLine) { while (true) { code1 = lookBits(13); if (code1 == EOF) { eof = true; return EOF; } if ((code1 >> 1) == 0x001) { break; } eatBits(1); } eatBits(12); if (encoding > 0) { eatBits(1); nextLine2D = !(code1 & 1); } } // set up for output if (codingLine[0] > 0) { outputBits = codingLine[a0i = 0]; } else { outputBits = codingLine[a0i = 1]; } ++row; } // get a byte if (outputBits >= 8) { buf = (a0i & 1) ? 0x00 : 0xff; outputBits -= 8; if (outputBits == 0 && codingLine[a0i] < columns) { ++a0i; outputBits = codingLine[a0i] - codingLine[a0i - 1]; } } else { bits = 8; buf = 0; do { if (outputBits > bits) { buf <<= bits; if (!(a0i & 1)) { buf |= 0xff >> (8 - bits); } outputBits -= bits; bits = 0; } else { buf <<= outputBits; if (!(a0i & 1)) { buf |= 0xff >> (8 - outputBits); } bits -= outputBits; outputBits = 0; if (codingLine[a0i] < columns) { ++a0i; if (unlikely(a0i > columns)) { error(errSyntaxError, getPos(), "Bad bits {0:04x} in CCITTFax stream", bits); err = true; break; } outputBits = codingLine[a0i] - codingLine[a0i - 1]; } else if (bits > 0) { buf <<= bits; bits = 0; } } } while (bits); } if (black) { buf ^= 0xff; } return buf; } short CCITTFaxStream::getTwoDimCode() { int code; const CCITTCode *p; int n; code = 0; // make gcc happy if (endOfBlock) { if ((code = lookBits(7)) != EOF) { p = &twoDimTab1[code]; if (p->bits > 0) { eatBits(p->bits); return p->n; } } } else { for (n = 1; n <= 7; ++n) { if ((code = lookBits(n)) == EOF) { break; } if (n < 7) { code <<= 7 - n; } p = &twoDimTab1[code]; if (p->bits == n) { eatBits(n); return p->n; } } } error(errSyntaxError, getPos(), "Bad two dim code ({0:04x}) in CCITTFax stream", code); return EOF; } short CCITTFaxStream::getWhiteCode() { short code; const CCITTCode *p; int n; code = 0; // make gcc happy if (endOfBlock) { code = lookBits(12); if (code == EOF) { return 1; } if ((code >> 5) == 0) { p = &whiteTab1[code]; } else { p = &whiteTab2[code >> 3]; } if (p->bits > 0) { eatBits(p->bits); return p->n; } } else { for (n = 1; n <= 9; ++n) { code = lookBits(n); if (code == EOF) { return 1; } if (n < 9) { code <<= 9 - n; } p = &whiteTab2[code]; if (p->bits == n) { eatBits(n); return p->n; } } for (n = 11; n <= 12; ++n) { code = lookBits(n); if (code == EOF) { return 1; } if (n < 12) { code <<= 12 - n; } p = &whiteTab1[code]; if (p->bits == n) { eatBits(n); return p->n; } } } error(errSyntaxError, getPos(), "Bad white code ({0:04x}) in CCITTFax stream", code); // eat a bit and return a positive number so that the caller doesn't // go into an infinite loop eatBits(1); return 1; } short CCITTFaxStream::getBlackCode() { short code; const CCITTCode *p; int n; code = 0; // make gcc happy if (endOfBlock) { code = lookBits(13); if (code == EOF) { return 1; } if ((code >> 7) == 0) { p = &blackTab1[code]; } else if ((code >> 9) == 0 && (code >> 7) != 0) { p = &blackTab2[(code >> 1) - 64]; } else { p = &blackTab3[code >> 7]; } if (p->bits > 0) { eatBits(p->bits); return p->n; } } else { for (n = 2; n <= 6; ++n) { code = lookBits(n); if (code == EOF) { return 1; } if (n < 6) { code <<= 6 - n; } p = &blackTab3[code]; if (p->bits == n) { eatBits(n); return p->n; } } for (n = 7; n <= 12; ++n) { code = lookBits(n); if (code == EOF) { return 1; } if (n < 12) { code <<= 12 - n; } if (code >= 64) { p = &blackTab2[code - 64]; if (p->bits == n) { eatBits(n); return p->n; } } } for (n = 10; n <= 13; ++n) { code = lookBits(n); if (code == EOF) { return 1; } if (n < 13) { code <<= 13 - n; } p = &blackTab1[code]; if (p->bits == n) { eatBits(n); return p->n; } } } error(errSyntaxError, getPos(), "Bad black code ({0:04x}) in CCITTFax stream", code); // eat a bit and return a positive number so that the caller doesn't // go into an infinite loop eatBits(1); return 1; } short CCITTFaxStream::lookBits(int n) { int c; while (inputBits < n) { if ((c = str->getChar()) == EOF) { if (inputBits == 0) { return EOF; } // near the end of the stream, the caller may ask for more bits // than are available, but there may still be a valid code in // however many bits are available -- we need to return correct // data in this case return (inputBuf << (n - inputBits)) & (0xffffffff >> (32 - n)); } inputBuf = (inputBuf << 8) + c; inputBits += 8; } return (inputBuf >> (inputBits - n)) & (0xffffffff >> (32 - n)); } GooString *CCITTFaxStream::getPSFilter(int psLevel, const char *indent) { GooString *s; char s1[50]; if (psLevel < 2) { return nullptr; } if (!(s = str->getPSFilter(psLevel, indent))) { return nullptr; } s->append(indent)->append("<< "); if (encoding != 0) { sprintf(s1, "/K %d ", encoding); s->append(s1); } if (endOfLine) { s->append("/EndOfLine true "); } if (byteAlign) { s->append("/EncodedByteAlign true "); } sprintf(s1, "/Columns %d ", columns); s->append(s1); if (rows != 0) { sprintf(s1, "/Rows %d ", rows); s->append(s1); } if (!endOfBlock) { s->append("/EndOfBlock false "); } if (black) { s->append("/BlackIs1 true "); } s->append(">> /CCITTFaxDecode filter\n"); return s; } bool CCITTFaxStream::isBinary(bool last) const { return str->isBinary(true); } #ifndef ENABLE_LIBJPEG //------------------------------------------------------------------------ // DCTStream //------------------------------------------------------------------------ // IDCT constants (20.12 fixed point format) # define dctCos1 4017 // cos(pi/16) # define dctSin1 799 // sin(pi/16) # define dctCos3 3406 // cos(3*pi/16) # define dctSin3 2276 // sin(3*pi/16) # define dctCos6 1567 // cos(6*pi/16) # define dctSin6 3784 // sin(6*pi/16) # define dctSqrt2 5793 // sqrt(2) # define dctSqrt1d2 2896 // sqrt(2) / 2 // color conversion parameters (16.16 fixed point format) # define dctCrToR 91881 // 1.4020 # define dctCbToG -22553 // -0.3441363 # define dctCrToG -46802 // -0.71413636 # define dctCbToB 116130 // 1.772 // clip [-256,511] --> [0,255] # define dctClipOffset 256 # define dctClipLength 768 static unsigned char dctClip[dctClipLength]; static int dctClipInit = 0; // zig zag decode map static const int dctZigZag[64] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 }; DCTStream::DCTStream(Stream *strA, int colorXformA, Dict *dict, int recursion) : FilterStream(strA) { int i, j; colorXform = colorXformA; progressive = interleaved = false; width = height = 0; mcuWidth = mcuHeight = 0; numComps = 0; comp = 0; x = y = dy = 0; for (i = 0; i < 4; ++i) { for (j = 0; j < 32; ++j) { rowBuf[i][j] = nullptr; } frameBuf[i] = nullptr; } if (!dctClipInit) { for (i = -256; i < 0; ++i) dctClip[dctClipOffset + i] = 0; for (i = 0; i < 256; ++i) dctClip[dctClipOffset + i] = i; for (i = 256; i < 512; ++i) dctClip[dctClipOffset + i] = 255; dctClipInit = 1; } } DCTStream::~DCTStream() { close(); delete str; } void DCTStream::dctReset(bool unfiltered) { if (unfiltered) str->unfilteredReset(); else str->reset(); progressive = interleaved = false; width = height = 0; numComps = 0; numQuantTables = 0; numDCHuffTables = 0; numACHuffTables = 0; gotJFIFMarker = false; gotAdobeMarker = false; restartInterval = 0; } void DCTStream::unfilteredReset() { dctReset(true); } void DCTStream::reset() { int i, j; dctReset(false); if (!readHeader()) { y = height; return; } // compute MCU size if (numComps == 1) { compInfo[0].hSample = compInfo[0].vSample = 1; } mcuWidth = compInfo[0].hSample; mcuHeight = compInfo[0].vSample; for (i = 1; i < numComps; ++i) { if (compInfo[i].hSample > mcuWidth) { mcuWidth = compInfo[i].hSample; } if (compInfo[i].vSample > mcuHeight) { mcuHeight = compInfo[i].vSample; } } mcuWidth *= 8; mcuHeight *= 8; // figure out color transform if (colorXform == -1) { if (numComps == 3) { if (gotJFIFMarker) { colorXform = 1; } else if (compInfo[0].id == 82 && compInfo[1].id == 71 && compInfo[2].id == 66) { // ASCII "RGB" colorXform = 0; } else { colorXform = 1; } } else { colorXform = 0; } } if (progressive || !interleaved) { // allocate a buffer for the whole image bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth; bufHeight = ((height + mcuHeight - 1) / mcuHeight) * mcuHeight; if (bufWidth <= 0 || bufHeight <= 0 || bufWidth > INT_MAX / bufWidth / (int)sizeof(int)) { error(errSyntaxError, getPos(), "Invalid image size in DCT stream"); y = height; return; } for (i = 0; i < numComps; ++i) { frameBuf[i] = (int *)gmallocn(bufWidth * bufHeight, sizeof(int)); memset(frameBuf[i], 0, bufWidth * bufHeight * sizeof(int)); } // read the image data do { restartMarker = 0xd0; restart(); readScan(); } while (readHeader()); // decode decodeImage(); // initialize counters comp = 0; x = 0; y = 0; } else { // allocate a buffer for one row of MCUs bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth; for (i = 0; i < numComps; ++i) { for (j = 0; j < mcuHeight; ++j) { rowBuf[i][j] = (unsigned char *)gmallocn(bufWidth, sizeof(unsigned char)); } } // initialize counters comp = 0; x = 0; y = 0; dy = mcuHeight; restartMarker = 0xd0; restart(); } } void DCTStream::close() { int i, j; for (i = 0; i < 4; ++i) { for (j = 0; j < 32; ++j) { gfree(rowBuf[i][j]); rowBuf[i][j] = nullptr; } gfree(frameBuf[i]); frameBuf[i] = nullptr; } FilterStream::close(); } int DCTStream::getChar() { int c; if (y >= height) { return EOF; } if (progressive || !interleaved) { c = frameBuf[comp][y * bufWidth + x]; if (++comp == numComps) { comp = 0; if (++x == width) { x = 0; ++y; } } } else { if (dy >= mcuHeight) { if (!readMCURow()) { y = height; return EOF; } comp = 0; x = 0; dy = 0; } c = rowBuf[comp][dy][x]; if (++comp == numComps) { comp = 0; if (++x == width) { x = 0; ++y; ++dy; if (y == height) { readTrailer(); } } } } return c; } int DCTStream::lookChar() { if (y >= height) { return EOF; } if (progressive || !interleaved) { return frameBuf[comp][y * bufWidth + x]; } else { if (dy >= mcuHeight) { if (!readMCURow()) { y = height; return EOF; } comp = 0; x = 0; dy = 0; } return rowBuf[comp][dy][x]; } } void DCTStream::restart() { int i; inputBits = 0; restartCtr = restartInterval; for (i = 0; i < numComps; ++i) { compInfo[i].prevDC = 0; } eobRun = 0; } // Read one row of MCUs from a sequential JPEG stream. bool DCTStream::readMCURow() { int data1[64]; unsigned char data2[64]; unsigned char *p1, *p2; int pY, pCb, pCr, pR, pG, pB; int h, v, horiz, vert, hSub, vSub; int x1, x2, y2, x3, y3, x4, y4, x5, y5, cc, i; int c; for (x1 = 0; x1 < width; x1 += mcuWidth) { // deal with restart marker if (restartInterval > 0 && restartCtr == 0) { c = readMarker(); if (c != restartMarker) { error(errSyntaxError, getPos(), "Bad DCT data: incorrect restart marker"); return false; } if (++restartMarker == 0xd8) restartMarker = 0xd0; restart(); } // read one MCU for (cc = 0; cc < numComps; ++cc) { h = compInfo[cc].hSample; v = compInfo[cc].vSample; horiz = mcuWidth / h; vert = mcuHeight / v; hSub = horiz / 8; vSub = vert / 8; for (y2 = 0; y2 < mcuHeight; y2 += vert) { for (x2 = 0; x2 < mcuWidth; x2 += horiz) { if (unlikely(scanInfo.dcHuffTable[cc] >= 4) || unlikely(scanInfo.acHuffTable[cc] >= 4)) { return false; } if (!readDataUnit(&dcHuffTables[scanInfo.dcHuffTable[cc]], &acHuffTables[scanInfo.acHuffTable[cc]], &compInfo[cc].prevDC, data1)) { return false; } transformDataUnit(quantTables[compInfo[cc].quantTable], data1, data2); if (hSub == 1 && vSub == 1) { for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { p1 = &rowBuf[cc][y2 + y3][x1 + x2]; p1[0] = data2[i]; p1[1] = data2[i + 1]; p1[2] = data2[i + 2]; p1[3] = data2[i + 3]; p1[4] = data2[i + 4]; p1[5] = data2[i + 5]; p1[6] = data2[i + 6]; p1[7] = data2[i + 7]; } } else if (hSub == 2 && vSub == 2) { for (y3 = 0, i = 0; y3 < 16; y3 += 2, i += 8) { p1 = &rowBuf[cc][y2 + y3][x1 + x2]; p2 = &rowBuf[cc][y2 + y3 + 1][x1 + x2]; p1[0] = p1[1] = p2[0] = p2[1] = data2[i]; p1[2] = p1[3] = p2[2] = p2[3] = data2[i + 1]; p1[4] = p1[5] = p2[4] = p2[5] = data2[i + 2]; p1[6] = p1[7] = p2[6] = p2[7] = data2[i + 3]; p1[8] = p1[9] = p2[8] = p2[9] = data2[i + 4]; p1[10] = p1[11] = p2[10] = p2[11] = data2[i + 5]; p1[12] = p1[13] = p2[12] = p2[13] = data2[i + 6]; p1[14] = p1[15] = p2[14] = p2[15] = data2[i + 7]; } } else { i = 0; for (y3 = 0, y4 = 0; y3 < 8; ++y3, y4 += vSub) { for (x3 = 0, x4 = 0; x3 < 8; ++x3, x4 += hSub) { for (y5 = 0; y5 < vSub; ++y5) for (x5 = 0; x5 < hSub; ++x5) rowBuf[cc][y2 + y4 + y5][x1 + x2 + x4 + x5] = data2[i]; ++i; } } } } } } --restartCtr; // color space conversion if (colorXform) { // convert YCbCr to RGB if (numComps == 3) { for (y2 = 0; y2 < mcuHeight; ++y2) { for (x2 = 0; x2 < mcuWidth; ++x2) { pY = rowBuf[0][y2][x1 + x2]; pCb = rowBuf[1][y2][x1 + x2] - 128; pCr = rowBuf[2][y2][x1 + x2] - 128; pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16; rowBuf[0][y2][x1 + x2] = dctClip[dctClipOffset + pR]; pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16; rowBuf[1][y2][x1 + x2] = dctClip[dctClipOffset + pG]; pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16; rowBuf[2][y2][x1 + x2] = dctClip[dctClipOffset + pB]; } } // convert YCbCrK to CMYK (K is passed through unchanged) } else if (numComps == 4) { for (y2 = 0; y2 < mcuHeight; ++y2) { for (x2 = 0; x2 < mcuWidth; ++x2) { pY = rowBuf[0][y2][x1 + x2]; pCb = rowBuf[1][y2][x1 + x2] - 128; pCr = rowBuf[2][y2][x1 + x2] - 128; pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16; rowBuf[0][y2][x1 + x2] = 255 - dctClip[dctClipOffset + pR]; pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16; rowBuf[1][y2][x1 + x2] = 255 - dctClip[dctClipOffset + pG]; pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16; rowBuf[2][y2][x1 + x2] = 255 - dctClip[dctClipOffset + pB]; } } } } } return true; } // Read one scan from a progressive or non-interleaved JPEG stream. void DCTStream::readScan() { int data[64]; int x1, y1, dx1, dy1, x2, y2, y3, cc, i; int h, v, horiz, vert, vSub; int *p1; int c; if (scanInfo.numComps == 1) { for (cc = 0; cc < numComps; ++cc) { if (scanInfo.comp[cc]) { break; } } dx1 = mcuWidth / compInfo[cc].hSample; dy1 = mcuHeight / compInfo[cc].vSample; } else { dx1 = mcuWidth; dy1 = mcuHeight; } for (y1 = 0; y1 < height; y1 += dy1) { for (x1 = 0; x1 < width; x1 += dx1) { // deal with restart marker if (restartInterval > 0 && restartCtr == 0) { c = readMarker(); if (c != restartMarker) { error(errSyntaxError, getPos(), "Bad DCT data: incorrect restart marker"); return; } if (++restartMarker == 0xd8) { restartMarker = 0xd0; } restart(); } // read one MCU for (cc = 0; cc < numComps; ++cc) { if (!scanInfo.comp[cc]) { continue; } h = compInfo[cc].hSample; v = compInfo[cc].vSample; horiz = mcuWidth / h; vert = mcuHeight / v; vSub = vert / 8; for (y2 = 0; y2 < dy1; y2 += vert) { for (x2 = 0; x2 < dx1; x2 += horiz) { // pull out the current values p1 = &frameBuf[cc][(y1 + y2) * bufWidth + (x1 + x2)]; for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { data[i] = p1[0]; data[i + 1] = p1[1]; data[i + 2] = p1[2]; data[i + 3] = p1[3]; data[i + 4] = p1[4]; data[i + 5] = p1[5]; data[i + 6] = p1[6]; data[i + 7] = p1[7]; p1 += bufWidth * vSub; } // read one data unit if (progressive) { if (!readProgressiveDataUnit(&dcHuffTables[scanInfo.dcHuffTable[cc]], &acHuffTables[scanInfo.acHuffTable[cc]], &compInfo[cc].prevDC, data)) { return; } } else { if (!readDataUnit(&dcHuffTables[scanInfo.dcHuffTable[cc]], &acHuffTables[scanInfo.acHuffTable[cc]], &compInfo[cc].prevDC, data)) { return; } } // add the data unit into frameBuf p1 = &frameBuf[cc][(y1 + y2) * bufWidth + (x1 + x2)]; for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { p1[0] = data[i]; p1[1] = data[i + 1]; p1[2] = data[i + 2]; p1[3] = data[i + 3]; p1[4] = data[i + 4]; p1[5] = data[i + 5]; p1[6] = data[i + 6]; p1[7] = data[i + 7]; p1 += bufWidth * vSub; } } } } --restartCtr; } } } // Read one data unit from a sequential JPEG stream. bool DCTStream::readDataUnit(DCTHuffTable *dcHuffTable, DCTHuffTable *acHuffTable, int *prevDC, int data[64]) { int run, size, amp; int c; int i, j; if ((size = readHuffSym(dcHuffTable)) == 9999) { return false; } if (size > 0) { if ((amp = readAmp(size)) == 9999) { return false; } } else { amp = 0; } data[0] = *prevDC += amp; for (i = 1; i < 64; ++i) { data[i] = 0; } i = 1; while (i < 64) { run = 0; while ((c = readHuffSym(acHuffTable)) == 0xf0 && run < 0x30) { run += 0x10; } if (c == 9999) { return false; } if (c == 0x00) { break; } else { run += (c >> 4) & 0x0f; size = c & 0x0f; amp = readAmp(size); if (amp == 9999) { return false; } i += run; if (i < 64) { j = dctZigZag[i++]; data[j] = amp; } } } return true; } // Read one data unit from a sequential JPEG stream. bool DCTStream::readProgressiveDataUnit(DCTHuffTable *dcHuffTable, DCTHuffTable *acHuffTable, int *prevDC, int data[64]) { int run, size, amp, bit, c; int i, j, k; // get the DC coefficient i = scanInfo.firstCoeff; if (i == 0) { if (scanInfo.ah == 0) { if ((size = readHuffSym(dcHuffTable)) == 9999) { return false; } if (size > 0) { if ((amp = readAmp(size)) == 9999) { return false; } } else { amp = 0; } data[0] += (*prevDC += amp) << scanInfo.al; } else { if ((bit = readBit()) == 9999) { return false; } data[0] += bit << scanInfo.al; } ++i; } if (scanInfo.lastCoeff == 0) { return true; } // check for an EOB run if (eobRun > 0) { while (i <= scanInfo.lastCoeff) { j = dctZigZag[i++]; if (data[j] != 0) { if ((bit = readBit()) == EOF) { return false; } if (bit) { data[j] += 1 << scanInfo.al; } } } --eobRun; return true; } // read the AC coefficients while (i <= scanInfo.lastCoeff) { if ((c = readHuffSym(acHuffTable)) == 9999) { return false; } // ZRL if (c == 0xf0) { k = 0; while (k < 16 && i <= scanInfo.lastCoeff) { j = dctZigZag[i++]; if (data[j] == 0) { ++k; } else { if ((bit = readBit()) == EOF) { return false; } if (bit) { data[j] += 1 << scanInfo.al; } } } // EOB run } else if ((c & 0x0f) == 0x00) { j = c >> 4; eobRun = 0; for (k = 0; k < j; ++k) { if ((bit = readBit()) == EOF) { return false; } eobRun = (eobRun << 1) | bit; } eobRun += 1 << j; while (i <= scanInfo.lastCoeff) { j = dctZigZag[i++]; if (data[j] != 0) { if ((bit = readBit()) == EOF) { return false; } if (bit) { data[j] += 1 << scanInfo.al; } } } --eobRun; break; // zero run and one AC coefficient } else { run = (c >> 4) & 0x0f; size = c & 0x0f; if ((amp = readAmp(size)) == 9999) { return false; } j = 0; // make gcc happy for (k = 0; k <= run && i <= scanInfo.lastCoeff; ++k) { j = dctZigZag[i++]; while (data[j] != 0 && i <= scanInfo.lastCoeff) { if ((bit = readBit()) == EOF) { return false; } if (bit) { data[j] += 1 << scanInfo.al; } j = dctZigZag[i++]; } } data[j] = amp << scanInfo.al; } } return true; } // Decode a progressive JPEG image. void DCTStream::decodeImage() { int dataIn[64]; unsigned char dataOut[64]; unsigned short *quantTable; int pY, pCb, pCr, pR, pG, pB; int x1, y1, x2, y2, x3, y3, x4, y4, x5, y5, cc, i; int h, v, horiz, vert, hSub, vSub; int *p0, *p1, *p2; for (y1 = 0; y1 < bufHeight; y1 += mcuHeight) { for (x1 = 0; x1 < bufWidth; x1 += mcuWidth) { for (cc = 0; cc < numComps; ++cc) { quantTable = quantTables[compInfo[cc].quantTable]; h = compInfo[cc].hSample; v = compInfo[cc].vSample; horiz = mcuWidth / h; vert = mcuHeight / v; hSub = horiz / 8; vSub = vert / 8; for (y2 = 0; y2 < mcuHeight; y2 += vert) { for (x2 = 0; x2 < mcuWidth; x2 += horiz) { // pull out the coded data unit p1 = &frameBuf[cc][(y1 + y2) * bufWidth + (x1 + x2)]; for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { dataIn[i] = p1[0]; dataIn[i + 1] = p1[1]; dataIn[i + 2] = p1[2]; dataIn[i + 3] = p1[3]; dataIn[i + 4] = p1[4]; dataIn[i + 5] = p1[5]; dataIn[i + 6] = p1[6]; dataIn[i + 7] = p1[7]; p1 += bufWidth * vSub; } // transform transformDataUnit(quantTable, dataIn, dataOut); // store back into frameBuf, doing replication for // subsampled components p1 = &frameBuf[cc][(y1 + y2) * bufWidth + (x1 + x2)]; if (hSub == 1 && vSub == 1) { for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { p1[0] = dataOut[i] & 0xff; p1[1] = dataOut[i + 1] & 0xff; p1[2] = dataOut[i + 2] & 0xff; p1[3] = dataOut[i + 3] & 0xff; p1[4] = dataOut[i + 4] & 0xff; p1[5] = dataOut[i + 5] & 0xff; p1[6] = dataOut[i + 6] & 0xff; p1[7] = dataOut[i + 7] & 0xff; p1 += bufWidth; } } else if (hSub == 2 && vSub == 2) { p2 = p1 + bufWidth; for (y3 = 0, i = 0; y3 < 16; y3 += 2, i += 8) { p1[0] = p1[1] = p2[0] = p2[1] = dataOut[i] & 0xff; p1[2] = p1[3] = p2[2] = p2[3] = dataOut[i + 1] & 0xff; p1[4] = p1[5] = p2[4] = p2[5] = dataOut[i + 2] & 0xff; p1[6] = p1[7] = p2[6] = p2[7] = dataOut[i + 3] & 0xff; p1[8] = p1[9] = p2[8] = p2[9] = dataOut[i + 4] & 0xff; p1[10] = p1[11] = p2[10] = p2[11] = dataOut[i + 5] & 0xff; p1[12] = p1[13] = p2[12] = p2[13] = dataOut[i + 6] & 0xff; p1[14] = p1[15] = p2[14] = p2[15] = dataOut[i + 7] & 0xff; p1 += bufWidth * 2; p2 += bufWidth * 2; } } else { i = 0; for (y3 = 0, y4 = 0; y3 < 8; ++y3, y4 += vSub) { for (x3 = 0, x4 = 0; x3 < 8; ++x3, x4 += hSub) { p2 = p1 + x4; for (y5 = 0; y5 < vSub; ++y5) { for (x5 = 0; x5 < hSub; ++x5) { p2[x5] = dataOut[i] & 0xff; } p2 += bufWidth; } ++i; } p1 += bufWidth * vSub; } } } } } // color space conversion if (colorXform) { // convert YCbCr to RGB if (numComps == 3) { for (y2 = 0; y2 < mcuHeight; ++y2) { p0 = &frameBuf[0][(y1 + y2) * bufWidth + x1]; p1 = &frameBuf[1][(y1 + y2) * bufWidth + x1]; p2 = &frameBuf[2][(y1 + y2) * bufWidth + x1]; for (x2 = 0; x2 < mcuWidth; ++x2) { pY = *p0; pCb = *p1 - 128; pCr = *p2 - 128; pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16; *p0++ = dctClip[dctClipOffset + pR]; pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16; *p1++ = dctClip[dctClipOffset + pG]; pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16; *p2++ = dctClip[dctClipOffset + pB]; } } // convert YCbCrK to CMYK (K is passed through unchanged) } else if (numComps == 4) { for (y2 = 0; y2 < mcuHeight; ++y2) { p0 = &frameBuf[0][(y1 + y2) * bufWidth + x1]; p1 = &frameBuf[1][(y1 + y2) * bufWidth + x1]; p2 = &frameBuf[2][(y1 + y2) * bufWidth + x1]; for (x2 = 0; x2 < mcuWidth; ++x2) { pY = *p0; pCb = *p1 - 128; pCr = *p2 - 128; pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16; *p0++ = 255 - dctClip[dctClipOffset + pR]; pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16; *p1++ = 255 - dctClip[dctClipOffset + pG]; pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16; *p2++ = 255 - dctClip[dctClipOffset + pB]; } } } } } } } // Transform one data unit -- this performs the dequantization and // IDCT steps. This IDCT algorithm is taken from: // Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz, // "Practical Fast 1-D DCT Algorithms with 11 Multiplications", // IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989, // 988-991. // The stage numbers mentioned in the comments refer to Figure 1 in this // paper. void DCTStream::transformDataUnit(unsigned short *quantTable, int dataIn[64], unsigned char dataOut[64]) { int v0, v1, v2, v3, v4, v5, v6, v7, t; int *p; int i; // dequant for (i = 0; i < 64; ++i) { dataIn[i] *= quantTable[i]; } // inverse DCT on rows for (i = 0; i < 64; i += 8) { p = dataIn + i; // check for all-zero AC coefficients if (p[1] == 0 && p[2] == 0 && p[3] == 0 && p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] == 0) { t = (dctSqrt2 * p[0] + 512) >> 10; p[0] = t; p[1] = t; p[2] = t; p[3] = t; p[4] = t; p[5] = t; p[6] = t; p[7] = t; continue; } // stage 4 v0 = (dctSqrt2 * p[0] + 128) >> 8; v1 = (dctSqrt2 * p[4] + 128) >> 8; v2 = p[2]; v3 = p[6]; v4 = (dctSqrt1d2 * (p[1] - p[7]) + 128) >> 8; v7 = (dctSqrt1d2 * (p[1] + p[7]) + 128) >> 8; v5 = p[3] << 4; v6 = p[5] << 4; // stage 3 t = (v0 - v1 + 1) >> 1; v0 = (v0 + v1 + 1) >> 1; v1 = t; t = (v2 * dctSin6 + v3 * dctCos6 + 128) >> 8; v2 = (v2 * dctCos6 - v3 * dctSin6 + 128) >> 8; v3 = t; t = (v4 - v6 + 1) >> 1; v4 = (v4 + v6 + 1) >> 1; v6 = t; t = (v7 + v5 + 1) >> 1; v5 = (v7 - v5 + 1) >> 1; v7 = t; // stage 2 t = (v0 - v3 + 1) >> 1; v0 = (v0 + v3 + 1) >> 1; v3 = t; t = (v1 - v2 + 1) >> 1; v1 = (v1 + v2 + 1) >> 1; v2 = t; t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12; v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12; v7 = t; t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12; v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12; v6 = t; // stage 1 p[0] = v0 + v7; p[7] = v0 - v7; p[1] = v1 + v6; p[6] = v1 - v6; p[2] = v2 + v5; p[5] = v2 - v5; p[3] = v3 + v4; p[4] = v3 - v4; } // inverse DCT on columns for (i = 0; i < 8; ++i) { p = dataIn + i; // check for all-zero AC coefficients if (p[1 * 8] == 0 && p[2 * 8] == 0 && p[3 * 8] == 0 && p[4 * 8] == 0 && p[5 * 8] == 0 && p[6 * 8] == 0 && p[7 * 8] == 0) { t = (dctSqrt2 * dataIn[i + 0] + 8192) >> 14; p[0 * 8] = t; p[1 * 8] = t; p[2 * 8] = t; p[3 * 8] = t; p[4 * 8] = t; p[5 * 8] = t; p[6 * 8] = t; p[7 * 8] = t; continue; } // stage 4 v0 = (dctSqrt2 * p[0 * 8] + 2048) >> 12; v1 = (dctSqrt2 * p[4 * 8] + 2048) >> 12; v2 = p[2 * 8]; v3 = p[6 * 8]; v4 = (dctSqrt1d2 * (p[1 * 8] - p[7 * 8]) + 2048) >> 12; v7 = (dctSqrt1d2 * (p[1 * 8] + p[7 * 8]) + 2048) >> 12; v5 = p[3 * 8]; v6 = p[5 * 8]; // stage 3 t = (v0 - v1 + 1) >> 1; v0 = (v0 + v1 + 1) >> 1; v1 = t; t = (v2 * dctSin6 + v3 * dctCos6 + 2048) >> 12; v2 = (v2 * dctCos6 - v3 * dctSin6 + 2048) >> 12; v3 = t; t = (v4 - v6 + 1) >> 1; v4 = (v4 + v6 + 1) >> 1; v6 = t; t = (v7 + v5 + 1) >> 1; v5 = (v7 - v5 + 1) >> 1; v7 = t; // stage 2 t = (v0 - v3 + 1) >> 1; v0 = (v0 + v3 + 1) >> 1; v3 = t; t = (v1 - v2 + 1) >> 1; v1 = (v1 + v2 + 1) >> 1; v2 = t; t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12; v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12; v7 = t; t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12; v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12; v6 = t; // stage 1 p[0 * 8] = v0 + v7; p[7 * 8] = v0 - v7; p[1 * 8] = v1 + v6; p[6 * 8] = v1 - v6; p[2 * 8] = v2 + v5; p[5 * 8] = v2 - v5; p[3 * 8] = v3 + v4; p[4 * 8] = v3 - v4; } // convert to 8-bit integers for (i = 0; i < 64; ++i) { const int ix = dctClipOffset + 128 + ((dataIn[i] + 8) >> 4); if (unlikely(ix < 0 || ix >= dctClipLength)) { dataOut[i] = 0; } else { dataOut[i] = dctClip[ix]; } } } int DCTStream::readHuffSym(DCTHuffTable *table) { unsigned short code; int bit; int codeBits; code = 0; codeBits = 0; do { // add a bit to the code if ((bit = readBit()) == EOF) { return 9999; } code = (code << 1) + bit; ++codeBits; // look up code if (code < table->firstCode[codeBits]) { break; } if (code - table->firstCode[codeBits] < table->numCodes[codeBits]) { code -= table->firstCode[codeBits]; return table->sym[table->firstSym[codeBits] + code]; } } while (codeBits < 16); error(errSyntaxError, getPos(), "Bad Huffman code in DCT stream"); return 9999; } int DCTStream::readAmp(int size) { int amp, bit; int bits; amp = 0; for (bits = 0; bits < size; ++bits) { if ((bit = readBit()) == EOF) return 9999; amp = (amp << 1) + bit; } if (amp < (1 << (size - 1))) amp -= (1 << size) - 1; return amp; } int DCTStream::readBit() { int bit; int c, c2; if (inputBits == 0) { if ((c = str->getChar()) == EOF) return EOF; if (c == 0xff) { do { c2 = str->getChar(); } while (c2 == 0xff); if (c2 != 0x00) { error(errSyntaxError, getPos(), "Bad DCT data: missing 00 after ff"); return EOF; } } inputBuf = c; inputBits = 8; } bit = (inputBuf >> (inputBits - 1)) & 1; --inputBits; return bit; } bool DCTStream::readHeader() { bool doScan; int n; int c = 0; int i; // read headers doScan = false; while (!doScan) { c = readMarker(); switch (c) { case 0xc0: // SOF0 (sequential) case 0xc1: // SOF1 (extended sequential) if (!readBaselineSOF()) { return false; } break; case 0xc2: // SOF2 (progressive) if (!readProgressiveSOF()) { return false; } break; case 0xc4: // DHT if (!readHuffmanTables()) { return false; } break; case 0xd8: // SOI break; case 0xd9: // EOI return false; case 0xda: // SOS if (!readScanInfo()) { return false; } doScan = true; break; case 0xdb: // DQT if (!readQuantTables()) { return false; } break; case 0xdd: // DRI if (!readRestartInterval()) { return false; } break; case 0xe0: // APP0 if (!readJFIFMarker()) { return false; } break; case 0xee: // APP14 if (!readAdobeMarker()) { return false; } break; case EOF: error(errSyntaxError, getPos(), "Bad DCT header"); return false; default: // skip APPn / COM / etc. if (c >= 0xe0) { n = read16() - 2; for (i = 0; i < n; ++i) { str->getChar(); } } else { error(errSyntaxError, getPos(), "Unknown DCT marker <{0:02x}>", c); return false; } break; } } return true; } bool DCTStream::readBaselineSOF() { int length; int prec; int i; int c; length = read16(); prec = str->getChar(); height = read16(); width = read16(); numComps = str->getChar(); if (numComps <= 0 || numComps > 4) { error(errSyntaxError, getPos(), "Bad number of components in DCT stream"); numComps = 0; return false; } if (prec != 8) { error(errSyntaxError, getPos(), "Bad DCT precision {0:d}", prec); return false; } for (i = 0; i < numComps; ++i) { compInfo[i].id = str->getChar(); c = str->getChar(); compInfo[i].hSample = (c >> 4) & 0x0f; compInfo[i].vSample = c & 0x0f; compInfo[i].quantTable = str->getChar(); if (compInfo[i].hSample < 1 || compInfo[i].hSample > 4 || compInfo[i].vSample < 1 || compInfo[i].vSample > 4) { error(errSyntaxError, getPos(), "Bad DCT sampling factor"); return false; } if (compInfo[i].quantTable < 0 || compInfo[i].quantTable > 3) { error(errSyntaxError, getPos(), "Bad DCT quant table selector"); return false; } } progressive = false; return true; } bool DCTStream::readProgressiveSOF() { int length; int prec; int i; int c; length = read16(); prec = str->getChar(); height = read16(); width = read16(); numComps = str->getChar(); if (numComps <= 0 || numComps > 4) { error(errSyntaxError, getPos(), "Bad number of components in DCT stream"); numComps = 0; return false; } if (prec != 8) { error(errSyntaxError, getPos(), "Bad DCT precision {0:d}", prec); return false; } for (i = 0; i < numComps; ++i) { compInfo[i].id = str->getChar(); c = str->getChar(); compInfo[i].hSample = (c >> 4) & 0x0f; compInfo[i].vSample = c & 0x0f; compInfo[i].quantTable = str->getChar(); if (compInfo[i].hSample < 1 || compInfo[i].hSample > 4 || compInfo[i].vSample < 1 || compInfo[i].vSample > 4) { error(errSyntaxError, getPos(), "Bad DCT sampling factor"); return false; } if (compInfo[i].quantTable < 0 || compInfo[i].quantTable > 3) { error(errSyntaxError, getPos(), "Bad DCT quant table selector"); return false; } } progressive = true; return true; } bool DCTStream::readScanInfo() { int length; int id, c; int i, j; length = read16() - 2; scanInfo.numComps = str->getChar(); if (scanInfo.numComps <= 0 || scanInfo.numComps > 4) { error(errSyntaxError, getPos(), "Bad number of components in DCT stream"); scanInfo.numComps = 0; return false; } --length; if (length != 2 * scanInfo.numComps + 3) { error(errSyntaxError, getPos(), "Bad DCT scan info block"); return false; } interleaved = scanInfo.numComps == numComps; for (j = 0; j < numComps; ++j) { scanInfo.comp[j] = false; scanInfo.dcHuffTable[j] = 0; scanInfo.acHuffTable[j] = 0; } for (i = 0; i < scanInfo.numComps; ++i) { id = str->getChar(); // some (broken) DCT streams reuse ID numbers, but at least they // keep the components in order, so we check compInfo[i] first to // work around the problem if (id == compInfo[i].id) { j = i; } else { for (j = 0; j < numComps; ++j) { if (id == compInfo[j].id) { break; } } if (j == numComps) { error(errSyntaxError, getPos(), "Bad DCT component ID in scan info block"); return false; } } scanInfo.comp[j] = true; c = str->getChar(); scanInfo.dcHuffTable[j] = (c >> 4) & 0x0f; scanInfo.acHuffTable[j] = c & 0x0f; } scanInfo.firstCoeff = str->getChar(); scanInfo.lastCoeff = str->getChar(); if (scanInfo.firstCoeff < 0 || scanInfo.lastCoeff > 63 || scanInfo.firstCoeff > scanInfo.lastCoeff) { error(errSyntaxError, getPos(), "Bad DCT coefficient numbers in scan info block"); return false; } c = str->getChar(); scanInfo.ah = (c >> 4) & 0x0f; scanInfo.al = c & 0x0f; return true; } bool DCTStream::readQuantTables() { int length, prec, i, index; length = read16() - 2; while (length > 0) { index = str->getChar(); prec = (index >> 4) & 0x0f; index &= 0x0f; if (prec > 1 || index >= 4) { error(errSyntaxError, getPos(), "Bad DCT quantization table"); return false; } if (index == numQuantTables) { numQuantTables = index + 1; } for (i = 0; i < 64; ++i) { if (prec) { quantTables[index][dctZigZag[i]] = read16(); } else { quantTables[index][dctZigZag[i]] = str->getChar(); } } if (prec) { length -= 129; } else { length -= 65; } } return true; } bool DCTStream::readHuffmanTables() { DCTHuffTable *tbl; int length; int index; unsigned short code; unsigned char sym; int i; int c; length = read16() - 2; while (length > 0) { index = str->getChar(); --length; if ((index & 0x0f) >= 4) { error(errSyntaxError, getPos(), "Bad DCT Huffman table"); return false; } if (index & 0x10) { index &= 0x0f; if (index >= numACHuffTables) numACHuffTables = index + 1; tbl = &acHuffTables[index]; } else { index &= 0x0f; if (index >= numDCHuffTables) numDCHuffTables = index + 1; tbl = &dcHuffTables[index]; } sym = 0; code = 0; for (i = 1; i <= 16; ++i) { c = str->getChar(); tbl->firstSym[i] = sym; tbl->firstCode[i] = code; tbl->numCodes[i] = c; sym += c; code = (code + c) << 1; } length -= 16; for (i = 0; i < sym; ++i) tbl->sym[i] = str->getChar(); length -= sym; } return true; } bool DCTStream::readRestartInterval() { int length; length = read16(); if (length != 4) { error(errSyntaxError, getPos(), "Bad DCT restart interval"); return false; } restartInterval = read16(); return true; } bool DCTStream::readJFIFMarker() { int length, i; char buf[5]; int c; length = read16(); length -= 2; if (length >= 5) { for (i = 0; i < 5; ++i) { if ((c = str->getChar()) == EOF) { error(errSyntaxError, getPos(), "Bad DCT APP0 marker"); return false; } buf[i] = c; } length -= 5; if (!memcmp(buf, "JFIF\0", 5)) { gotJFIFMarker = true; } } while (length > 0) { if (str->getChar() == EOF) { error(errSyntaxError, getPos(), "Bad DCT APP0 marker"); return false; } --length; } return true; } bool DCTStream::readAdobeMarker() { int length, i; char buf[12]; int c; length = read16(); if (length < 14) { goto err; } for (i = 0; i < 12; ++i) { if ((c = str->getChar()) == EOF) { goto err; } buf[i] = c; } if (strncmp(buf, "Adobe", 5)) { goto err; } colorXform = buf[11]; gotAdobeMarker = true; for (i = 14; i < length; ++i) { if (str->getChar() == EOF) { goto err; } } return true; err: error(errSyntaxError, getPos(), "Bad DCT Adobe APP14 marker"); return false; } bool DCTStream::readTrailer() { int c; c = readMarker(); if (c != 0xd9) { // EOI error(errSyntaxError, getPos(), "Bad DCT trailer"); return false; } return true; } int DCTStream::readMarker() { int c; do { do { c = str->getChar(); } while (c != 0xff && c != EOF); while (c == 0xff) { c = str->getChar(); } } while (c == 0x00); return c; } int DCTStream::read16() { int c1, c2; if ((c1 = str->getChar()) == EOF) return EOF; if ((c2 = str->getChar()) == EOF) return EOF; return (c1 << 8) + c2; } GooString *DCTStream::getPSFilter(int psLevel, const char *indent) { GooString *s; if (psLevel < 2) { return nullptr; } if (!(s = str->getPSFilter(psLevel, indent))) { return nullptr; } s->append(indent)->append("<< >> /DCTDecode filter\n"); return s; } bool DCTStream::isBinary(bool last) const { return str->isBinary(true); } #endif #ifndef ENABLE_ZLIB_UNCOMPRESS //------------------------------------------------------------------------ // FlateStream //------------------------------------------------------------------------ const int FlateStream::codeLenCodeMap[flateMaxCodeLenCodes] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; const FlateDecode FlateStream::lengthDecode[flateMaxLitCodes - 257] = { { 0, 3 }, { 0, 4 }, { 0, 5 }, { 0, 6 }, { 0, 7 }, { 0, 8 }, { 0, 9 }, { 0, 10 }, { 1, 11 }, { 1, 13 }, { 1, 15 }, { 1, 17 }, { 2, 19 }, { 2, 23 }, { 2, 27 }, { 2, 31 }, { 3, 35 }, { 3, 43 }, { 3, 51 }, { 3, 59 }, { 4, 67 }, { 4, 83 }, { 4, 99 }, { 4, 115 }, { 5, 131 }, { 5, 163 }, { 5, 195 }, { 5, 227 }, { 0, 258 }, { 0, 258 }, { 0, 258 } }; const FlateDecode FlateStream::distDecode[flateMaxDistCodes] = { { 0, 1 }, { 0, 2 }, { 0, 3 }, { 0, 4 }, { 1, 5 }, { 1, 7 }, { 2, 9 }, { 2, 13 }, { 3, 17 }, { 3, 25 }, { 4, 33 }, { 4, 49 }, { 5, 65 }, { 5, 97 }, { 6, 129 }, { 6, 193 }, { 7, 257 }, { 7, 385 }, { 8, 513 }, { 8, 769 }, { 9, 1025 }, { 9, 1537 }, { 10, 2049 }, { 10, 3073 }, { 11, 4097 }, { 11, 6145 }, { 12, 8193 }, { 12, 12289 }, { 13, 16385 }, { 13, 24577 } }; static const FlateCode flateFixedLitCodeTabCodes[512] = { { 7, 0x0100 }, { 8, 0x0050 }, { 8, 0x0010 }, { 8, 0x0118 }, { 7, 0x0110 }, { 8, 0x0070 }, { 8, 0x0030 }, { 9, 0x00c0 }, { 7, 0x0108 }, { 8, 0x0060 }, { 8, 0x0020 }, { 9, 0x00a0 }, { 8, 0x0000 }, { 8, 0x0080 }, { 8, 0x0040 }, { 9, 0x00e0 }, { 7, 0x0104 }, { 8, 0x0058 }, { 8, 0x0018 }, { 9, 0x0090 }, { 7, 0x0114 }, { 8, 0x0078 }, { 8, 0x0038 }, { 9, 0x00d0 }, { 7, 0x010c }, { 8, 0x0068 }, { 8, 0x0028 }, { 9, 0x00b0 }, { 8, 0x0008 }, { 8, 0x0088 }, { 8, 0x0048 }, { 9, 0x00f0 }, { 7, 0x0102 }, { 8, 0x0054 }, { 8, 0x0014 }, { 8, 0x011c }, { 7, 0x0112 }, { 8, 0x0074 }, { 8, 0x0034 }, { 9, 0x00c8 }, { 7, 0x010a }, { 8, 0x0064 }, { 8, 0x0024 }, { 9, 0x00a8 }, { 8, 0x0004 }, { 8, 0x0084 }, { 8, 0x0044 }, { 9, 0x00e8 }, { 7, 0x0106 }, { 8, 0x005c }, { 8, 0x001c }, { 9, 0x0098 }, { 7, 0x0116 }, { 8, 0x007c }, { 8, 0x003c }, { 9, 0x00d8 }, { 7, 0x010e }, { 8, 0x006c }, { 8, 0x002c }, { 9, 0x00b8 }, { 8, 0x000c }, { 8, 0x008c }, { 8, 0x004c }, { 9, 0x00f8 }, { 7, 0x0101 }, { 8, 0x0052 }, { 8, 0x0012 }, { 8, 0x011a }, { 7, 0x0111 }, { 8, 0x0072 }, { 8, 0x0032 }, { 9, 0x00c4 }, { 7, 0x0109 }, { 8, 0x0062 }, { 8, 0x0022 }, { 9, 0x00a4 }, { 8, 0x0002 }, { 8, 0x0082 }, { 8, 0x0042 }, { 9, 0x00e4 }, { 7, 0x0105 }, { 8, 0x005a }, { 8, 0x001a }, { 9, 0x0094 }, { 7, 0x0115 }, { 8, 0x007a }, { 8, 0x003a }, { 9, 0x00d4 }, { 7, 0x010d }, { 8, 0x006a }, { 8, 0x002a }, { 9, 0x00b4 }, { 8, 0x000a }, { 8, 0x008a }, { 8, 0x004a }, { 9, 0x00f4 }, { 7, 0x0103 }, { 8, 0x0056 }, { 8, 0x0016 }, { 8, 0x011e }, { 7, 0x0113 }, { 8, 0x0076 }, { 8, 0x0036 }, { 9, 0x00cc }, { 7, 0x010b }, { 8, 0x0066 }, { 8, 0x0026 }, { 9, 0x00ac }, { 8, 0x0006 }, { 8, 0x0086 }, { 8, 0x0046 }, { 9, 0x00ec }, { 7, 0x0107 }, { 8, 0x005e }, { 8, 0x001e }, { 9, 0x009c }, { 7, 0x0117 }, { 8, 0x007e }, { 8, 0x003e }, { 9, 0x00dc }, { 7, 0x010f }, { 8, 0x006e }, { 8, 0x002e }, { 9, 0x00bc }, { 8, 0x000e }, { 8, 0x008e }, { 8, 0x004e }, { 9, 0x00fc }, { 7, 0x0100 }, { 8, 0x0051 }, { 8, 0x0011 }, { 8, 0x0119 }, { 7, 0x0110 }, { 8, 0x0071 }, { 8, 0x0031 }, { 9, 0x00c2 }, { 7, 0x0108 }, { 8, 0x0061 }, { 8, 0x0021 }, { 9, 0x00a2 }, { 8, 0x0001 }, { 8, 0x0081 }, { 8, 0x0041 }, { 9, 0x00e2 }, { 7, 0x0104 }, { 8, 0x0059 }, { 8, 0x0019 }, { 9, 0x0092 }, { 7, 0x0114 }, { 8, 0x0079 }, { 8, 0x0039 }, { 9, 0x00d2 }, { 7, 0x010c }, { 8, 0x0069 }, { 8, 0x0029 }, { 9, 0x00b2 }, { 8, 0x0009 }, { 8, 0x0089 }, { 8, 0x0049 }, { 9, 0x00f2 }, { 7, 0x0102 }, { 8, 0x0055 }, { 8, 0x0015 }, { 8, 0x011d }, { 7, 0x0112 }, { 8, 0x0075 }, { 8, 0x0035 }, { 9, 0x00ca }, { 7, 0x010a }, { 8, 0x0065 }, { 8, 0x0025 }, { 9, 0x00aa }, { 8, 0x0005 }, { 8, 0x0085 }, { 8, 0x0045 }, { 9, 0x00ea }, { 7, 0x0106 }, { 8, 0x005d }, { 8, 0x001d }, { 9, 0x009a }, { 7, 0x0116 }, { 8, 0x007d }, { 8, 0x003d }, { 9, 0x00da }, { 7, 0x010e }, { 8, 0x006d }, { 8, 0x002d }, { 9, 0x00ba }, { 8, 0x000d }, { 8, 0x008d }, { 8, 0x004d }, { 9, 0x00fa }, { 7, 0x0101 }, { 8, 0x0053 }, { 8, 0x0013 }, { 8, 0x011b }, { 7, 0x0111 }, { 8, 0x0073 }, { 8, 0x0033 }, { 9, 0x00c6 }, { 7, 0x0109 }, { 8, 0x0063 }, { 8, 0x0023 }, { 9, 0x00a6 }, { 8, 0x0003 }, { 8, 0x0083 }, { 8, 0x0043 }, { 9, 0x00e6 }, { 7, 0x0105 }, { 8, 0x005b }, { 8, 0x001b }, { 9, 0x0096 }, { 7, 0x0115 }, { 8, 0x007b }, { 8, 0x003b }, { 9, 0x00d6 }, { 7, 0x010d }, { 8, 0x006b }, { 8, 0x002b }, { 9, 0x00b6 }, { 8, 0x000b }, { 8, 0x008b }, { 8, 0x004b }, { 9, 0x00f6 }, { 7, 0x0103 }, { 8, 0x0057 }, { 8, 0x0017 }, { 8, 0x011f }, { 7, 0x0113 }, { 8, 0x0077 }, { 8, 0x0037 }, { 9, 0x00ce }, { 7, 0x010b }, { 8, 0x0067 }, { 8, 0x0027 }, { 9, 0x00ae }, { 8, 0x0007 }, { 8, 0x0087 }, { 8, 0x0047 }, { 9, 0x00ee }, { 7, 0x0107 }, { 8, 0x005f }, { 8, 0x001f }, { 9, 0x009e }, { 7, 0x0117 }, { 8, 0x007f }, { 8, 0x003f }, { 9, 0x00de }, { 7, 0x010f }, { 8, 0x006f }, { 8, 0x002f }, { 9, 0x00be }, { 8, 0x000f }, { 8, 0x008f }, { 8, 0x004f }, { 9, 0x00fe }, { 7, 0x0100 }, { 8, 0x0050 }, { 8, 0x0010 }, { 8, 0x0118 }, { 7, 0x0110 }, { 8, 0x0070 }, { 8, 0x0030 }, { 9, 0x00c1 }, { 7, 0x0108 }, { 8, 0x0060 }, { 8, 0x0020 }, { 9, 0x00a1 }, { 8, 0x0000 }, { 8, 0x0080 }, { 8, 0x0040 }, { 9, 0x00e1 }, { 7, 0x0104 }, { 8, 0x0058 }, { 8, 0x0018 }, { 9, 0x0091 }, { 7, 0x0114 }, { 8, 0x0078 }, { 8, 0x0038 }, { 9, 0x00d1 }, { 7, 0x010c }, { 8, 0x0068 }, { 8, 0x0028 }, { 9, 0x00b1 }, { 8, 0x0008 }, { 8, 0x0088 }, { 8, 0x0048 }, { 9, 0x00f1 }, { 7, 0x0102 }, { 8, 0x0054 }, { 8, 0x0014 }, { 8, 0x011c }, { 7, 0x0112 }, { 8, 0x0074 }, { 8, 0x0034 }, { 9, 0x00c9 }, { 7, 0x010a }, { 8, 0x0064 }, { 8, 0x0024 }, { 9, 0x00a9 }, { 8, 0x0004 }, { 8, 0x0084 }, { 8, 0x0044 }, { 9, 0x00e9 }, { 7, 0x0106 }, { 8, 0x005c }, { 8, 0x001c }, { 9, 0x0099 }, { 7, 0x0116 }, { 8, 0x007c }, { 8, 0x003c }, { 9, 0x00d9 }, { 7, 0x010e }, { 8, 0x006c }, { 8, 0x002c }, { 9, 0x00b9 }, { 8, 0x000c }, { 8, 0x008c }, { 8, 0x004c }, { 9, 0x00f9 }, { 7, 0x0101 }, { 8, 0x0052 }, { 8, 0x0012 }, { 8, 0x011a }, { 7, 0x0111 }, { 8, 0x0072 }, { 8, 0x0032 }, { 9, 0x00c5 }, { 7, 0x0109 }, { 8, 0x0062 }, { 8, 0x0022 }, { 9, 0x00a5 }, { 8, 0x0002 }, { 8, 0x0082 }, { 8, 0x0042 }, { 9, 0x00e5 }, { 7, 0x0105 }, { 8, 0x005a }, { 8, 0x001a }, { 9, 0x0095 }, { 7, 0x0115 }, { 8, 0x007a }, { 8, 0x003a }, { 9, 0x00d5 }, { 7, 0x010d }, { 8, 0x006a }, { 8, 0x002a }, { 9, 0x00b5 }, { 8, 0x000a }, { 8, 0x008a }, { 8, 0x004a }, { 9, 0x00f5 }, { 7, 0x0103 }, { 8, 0x0056 }, { 8, 0x0016 }, { 8, 0x011e }, { 7, 0x0113 }, { 8, 0x0076 }, { 8, 0x0036 }, { 9, 0x00cd }, { 7, 0x010b }, { 8, 0x0066 }, { 8, 0x0026 }, { 9, 0x00ad }, { 8, 0x0006 }, { 8, 0x0086 }, { 8, 0x0046 }, { 9, 0x00ed }, { 7, 0x0107 }, { 8, 0x005e }, { 8, 0x001e }, { 9, 0x009d }, { 7, 0x0117 }, { 8, 0x007e }, { 8, 0x003e }, { 9, 0x00dd }, { 7, 0x010f }, { 8, 0x006e }, { 8, 0x002e }, { 9, 0x00bd }, { 8, 0x000e }, { 8, 0x008e }, { 8, 0x004e }, { 9, 0x00fd }, { 7, 0x0100 }, { 8, 0x0051 }, { 8, 0x0011 }, { 8, 0x0119 }, { 7, 0x0110 }, { 8, 0x0071 }, { 8, 0x0031 }, { 9, 0x00c3 }, { 7, 0x0108 }, { 8, 0x0061 }, { 8, 0x0021 }, { 9, 0x00a3 }, { 8, 0x0001 }, { 8, 0x0081 }, { 8, 0x0041 }, { 9, 0x00e3 }, { 7, 0x0104 }, { 8, 0x0059 }, { 8, 0x0019 }, { 9, 0x0093 }, { 7, 0x0114 }, { 8, 0x0079 }, { 8, 0x0039 }, { 9, 0x00d3 }, { 7, 0x010c }, { 8, 0x0069 }, { 8, 0x0029 }, { 9, 0x00b3 }, { 8, 0x0009 }, { 8, 0x0089 }, { 8, 0x0049 }, { 9, 0x00f3 }, { 7, 0x0102 }, { 8, 0x0055 }, { 8, 0x0015 }, { 8, 0x011d }, { 7, 0x0112 }, { 8, 0x0075 }, { 8, 0x0035 }, { 9, 0x00cb }, { 7, 0x010a }, { 8, 0x0065 }, { 8, 0x0025 }, { 9, 0x00ab }, { 8, 0x0005 }, { 8, 0x0085 }, { 8, 0x0045 }, { 9, 0x00eb }, { 7, 0x0106 }, { 8, 0x005d }, { 8, 0x001d }, { 9, 0x009b }, { 7, 0x0116 }, { 8, 0x007d }, { 8, 0x003d }, { 9, 0x00db }, { 7, 0x010e }, { 8, 0x006d }, { 8, 0x002d }, { 9, 0x00bb }, { 8, 0x000d }, { 8, 0x008d }, { 8, 0x004d }, { 9, 0x00fb }, { 7, 0x0101 }, { 8, 0x0053 }, { 8, 0x0013 }, { 8, 0x011b }, { 7, 0x0111 }, { 8, 0x0073 }, { 8, 0x0033 }, { 9, 0x00c7 }, { 7, 0x0109 }, { 8, 0x0063 }, { 8, 0x0023 }, { 9, 0x00a7 }, { 8, 0x0003 }, { 8, 0x0083 }, { 8, 0x0043 }, { 9, 0x00e7 }, { 7, 0x0105 }, { 8, 0x005b }, { 8, 0x001b }, { 9, 0x0097 }, { 7, 0x0115 }, { 8, 0x007b }, { 8, 0x003b }, { 9, 0x00d7 }, { 7, 0x010d }, { 8, 0x006b }, { 8, 0x002b }, { 9, 0x00b7 }, { 8, 0x000b }, { 8, 0x008b }, { 8, 0x004b }, { 9, 0x00f7 }, { 7, 0x0103 }, { 8, 0x0057 }, { 8, 0x0017 }, { 8, 0x011f }, { 7, 0x0113 }, { 8, 0x0077 }, { 8, 0x0037 }, { 9, 0x00cf }, { 7, 0x010b }, { 8, 0x0067 }, { 8, 0x0027 }, { 9, 0x00af }, { 8, 0x0007 }, { 8, 0x0087 }, { 8, 0x0047 }, { 9, 0x00ef }, { 7, 0x0107 }, { 8, 0x005f }, { 8, 0x001f }, { 9, 0x009f }, { 7, 0x0117 }, { 8, 0x007f }, { 8, 0x003f }, { 9, 0x00df }, { 7, 0x010f }, { 8, 0x006f }, { 8, 0x002f }, { 9, 0x00bf }, { 8, 0x000f }, { 8, 0x008f }, { 8, 0x004f }, { 9, 0x00ff } }; FlateHuffmanTab FlateStream::fixedLitCodeTab = { flateFixedLitCodeTabCodes, 9 }; static const FlateCode flateFixedDistCodeTabCodes[32] = { { 5, 0x0000 }, { 5, 0x0010 }, { 5, 0x0008 }, { 5, 0x0018 }, { 5, 0x0004 }, { 5, 0x0014 }, { 5, 0x000c }, { 5, 0x001c }, { 5, 0x0002 }, { 5, 0x0012 }, { 5, 0x000a }, { 5, 0x001a }, { 5, 0x0006 }, { 5, 0x0016 }, { 5, 0x000e }, { 0, 0x0000 }, { 5, 0x0001 }, { 5, 0x0011 }, { 5, 0x0009 }, { 5, 0x0019 }, { 5, 0x0005 }, { 5, 0x0015 }, { 5, 0x000d }, { 5, 0x001d }, { 5, 0x0003 }, { 5, 0x0013 }, { 5, 0x000b }, { 5, 0x001b }, { 5, 0x0007 }, { 5, 0x0017 }, { 5, 0x000f }, { 0, 0x0000 } }; FlateHuffmanTab FlateStream::fixedDistCodeTab = { flateFixedDistCodeTabCodes, 5 }; FlateStream::FlateStream(Stream *strA, int predictor, int columns, int colors, int bits) : FilterStream(strA) { if (predictor != 1) { pred = new StreamPredictor(this, predictor, columns, colors, bits); if (!pred->isOk()) { delete pred; pred = nullptr; } } else { pred = nullptr; } litCodeTab.codes = nullptr; distCodeTab.codes = nullptr; memset(buf, 0, flateWindow); } FlateStream::~FlateStream() { if (litCodeTab.codes != fixedLitCodeTab.codes) { gfree(const_cast(litCodeTab.codes)); } if (distCodeTab.codes != fixedDistCodeTab.codes) { gfree(const_cast(distCodeTab.codes)); } if (pred) { delete pred; } delete str; } void FlateStream::flateReset(bool unfiltered) { if (unfiltered) { str->unfilteredReset(); } else { str->reset(); } index = 0; remain = 0; codeBuf = 0; codeSize = 0; compressedBlock = false; endOfBlock = true; eof = true; } void FlateStream::unfilteredReset() { flateReset(true); } void FlateStream::reset() { int cmf, flg; flateReset(false); // read header //~ need to look at window size? endOfBlock = eof = true; cmf = str->getChar(); flg = str->getChar(); if (cmf == EOF || flg == EOF) { return; } if ((cmf & 0x0f) != 0x08) { error(errSyntaxError, getPos(), "Unknown compression method in flate stream"); return; } if ((((cmf << 8) + flg) % 31) != 0) { error(errSyntaxError, getPos(), "Bad FCHECK in flate stream"); return; } if (flg & 0x20) { error(errSyntaxError, getPos(), "FDICT bit set in flate stream"); return; } eof = false; } int FlateStream::getChar() { if (pred) { return pred->getChar(); } return doGetRawChar(); } int FlateStream::getChars(int nChars, unsigned char *buffer) { if (pred) { return pred->getChars(nChars, buffer); } else { for (int i = 0; i < nChars; ++i) { const int c = doGetRawChar(); if (likely(c != EOF)) { buffer[i] = c; } else { return i; } } return nChars; } } int FlateStream::lookChar() { int c; if (pred) { return pred->lookChar(); } while (remain == 0) { if (endOfBlock && eof) { return EOF; } readSome(); } c = buf[index]; return c; } void FlateStream::getRawChars(int nChars, int *buffer) { for (int i = 0; i < nChars; ++i) { buffer[i] = doGetRawChar(); } } int FlateStream::getRawChar() { return doGetRawChar(); } GooString *FlateStream::getPSFilter(int psLevel, const char *indent) { GooString *s; if (psLevel < 3 || pred) { return nullptr; } if (!(s = str->getPSFilter(psLevel, indent))) { return nullptr; } s->append(indent)->append("<< >> /FlateDecode filter\n"); return s; } bool FlateStream::isBinary(bool last) const { return str->isBinary(true); } void FlateStream::readSome() { int code1, code2; int len, dist; int i, j, k; int c; if (endOfBlock) { if (!startBlock()) { return; } } if (compressedBlock) { if ((code1 = getHuffmanCodeWord(&litCodeTab)) == EOF) { goto err; } if (code1 < 256) { buf[index] = code1; remain = 1; } else if (code1 == 256) { endOfBlock = true; remain = 0; } else { code1 -= 257; code2 = lengthDecode[code1].bits; if (code2 > 0 && (code2 = getCodeWord(code2)) == EOF) { goto err; } len = lengthDecode[code1].first + code2; if ((code1 = getHuffmanCodeWord(&distCodeTab)) == EOF) { goto err; } code2 = distDecode[code1].bits; if (code2 > 0 && (code2 = getCodeWord(code2)) == EOF) { goto err; } dist = distDecode[code1].first + code2; i = index; j = (index - dist) & flateMask; for (k = 0; k < len; ++k) { buf[i] = buf[j]; i = (i + 1) & flateMask; j = (j + 1) & flateMask; } remain = len; } } else { len = (blockLen < flateWindow) ? blockLen : flateWindow; for (i = 0, j = index; i < len; ++i, j = (j + 1) & flateMask) { if ((c = str->getChar()) == EOF) { endOfBlock = eof = true; break; } buf[j] = c & 0xff; } remain = i; blockLen -= len; if (blockLen == 0) { endOfBlock = true; } } return; err: error(errSyntaxError, getPos(), "Unexpected end of file in flate stream"); endOfBlock = eof = true; remain = 0; } bool FlateStream::startBlock() { int blockHdr; int c; int check; // free the code tables from the previous block if (litCodeTab.codes != fixedLitCodeTab.codes) { gfree(const_cast(litCodeTab.codes)); } litCodeTab.codes = nullptr; if (distCodeTab.codes != fixedDistCodeTab.codes) { gfree(const_cast(distCodeTab.codes)); } distCodeTab.codes = nullptr; // read block header blockHdr = getCodeWord(3); if (blockHdr & 1) { eof = true; } blockHdr >>= 1; // uncompressed block if (blockHdr == 0) { compressedBlock = false; if ((c = str->getChar()) == EOF) { goto err; } blockLen = c & 0xff; if ((c = str->getChar()) == EOF) { goto err; } blockLen |= (c & 0xff) << 8; if ((c = str->getChar()) == EOF) { goto err; } check = c & 0xff; if ((c = str->getChar()) == EOF) { goto err; } check |= (c & 0xff) << 8; if (check != (~blockLen & 0xffff)) { error(errSyntaxError, getPos(), "Bad uncompressed block length in flate stream"); } codeBuf = 0; codeSize = 0; // compressed block with fixed codes } else if (blockHdr == 1) { compressedBlock = true; loadFixedCodes(); // compressed block with dynamic codes } else if (blockHdr == 2) { compressedBlock = true; if (!readDynamicCodes()) { goto err; } // unknown block type } else { goto err; } endOfBlock = false; return true; err: error(errSyntaxError, getPos(), "Bad block header in flate stream"); endOfBlock = eof = true; return false; } void FlateStream::loadFixedCodes() { litCodeTab.codes = fixedLitCodeTab.codes; litCodeTab.maxLen = fixedLitCodeTab.maxLen; distCodeTab.codes = fixedDistCodeTab.codes; distCodeTab.maxLen = fixedDistCodeTab.maxLen; } bool FlateStream::readDynamicCodes() { int numCodeLenCodes; int numLitCodes; int numDistCodes; int codeLenCodeLengths[flateMaxCodeLenCodes]; FlateHuffmanTab codeLenCodeTab; int len, repeat, code; int i; codeLenCodeTab.codes = nullptr; // read lengths if ((numLitCodes = getCodeWord(5)) == EOF) { goto err; } numLitCodes += 257; if ((numDistCodes = getCodeWord(5)) == EOF) { goto err; } numDistCodes += 1; if ((numCodeLenCodes = getCodeWord(4)) == EOF) { goto err; } numCodeLenCodes += 4; if (numLitCodes > flateMaxLitCodes || numDistCodes > flateMaxDistCodes || numCodeLenCodes > flateMaxCodeLenCodes) { goto err; } // build the code length code table for (i = 0; i < flateMaxCodeLenCodes; ++i) { codeLenCodeLengths[i] = 0; } for (i = 0; i < numCodeLenCodes; ++i) { if ((codeLenCodeLengths[codeLenCodeMap[i]] = getCodeWord(3)) == -1) { goto err; } } codeLenCodeTab.codes = compHuffmanCodes(codeLenCodeLengths, flateMaxCodeLenCodes, &codeLenCodeTab.maxLen); // build the literal and distance code tables len = 0; repeat = 0; i = 0; while (i < numLitCodes + numDistCodes) { if ((code = getHuffmanCodeWord(&codeLenCodeTab)) == EOF) { goto err; } if (code == 16) { if ((repeat = getCodeWord(2)) == EOF) { goto err; } repeat += 3; if (i + repeat > numLitCodes + numDistCodes) { goto err; } for (; repeat > 0; --repeat) { codeLengths[i++] = len; } } else if (code == 17) { if ((repeat = getCodeWord(3)) == EOF) { goto err; } repeat += 3; if (i + repeat > numLitCodes + numDistCodes) { goto err; } len = 0; for (; repeat > 0; --repeat) { codeLengths[i++] = 0; } } else if (code == 18) { if ((repeat = getCodeWord(7)) == EOF) { goto err; } repeat += 11; if (i + repeat > numLitCodes + numDistCodes) { goto err; } len = 0; for (; repeat > 0; --repeat) { codeLengths[i++] = 0; } } else { codeLengths[i++] = len = code; } } litCodeTab.codes = compHuffmanCodes(codeLengths, numLitCodes, &litCodeTab.maxLen); distCodeTab.codes = compHuffmanCodes(codeLengths + numLitCodes, numDistCodes, &distCodeTab.maxLen); gfree(const_cast(codeLenCodeTab.codes)); return true; err: error(errSyntaxError, getPos(), "Bad dynamic code table in flate stream"); gfree(const_cast(codeLenCodeTab.codes)); return false; } // Convert an array of lengths, in value order, into a // Huffman code lookup table. FlateCode *FlateStream::compHuffmanCodes(const int *lengths, int n, int *maxLen) { int len, code, code2, skip, val, i, t; // find max code length *maxLen = 0; for (val = 0; val < n; ++val) { if (lengths[val] > *maxLen) { *maxLen = lengths[val]; } } // allocate the table const int tabSize = 1 << *maxLen; FlateCode *codes = (FlateCode *)gmallocn(tabSize, sizeof(FlateCode)); // clear the table for (i = 0; i < tabSize; ++i) { codes[i].len = 0; codes[i].val = 0; } // build the table for (len = 1, code = 0, skip = 2; len <= *maxLen; ++len, code <<= 1, skip <<= 1) { for (val = 0; val < n; ++val) { if (lengths[val] == len) { // bit-reverse the code code2 = 0; t = code; for (i = 0; i < len; ++i) { code2 = (code2 << 1) | (t & 1); t >>= 1; } // fill in the table entries for (i = code2; i < tabSize; i += skip) { codes[i].len = (unsigned short)len; codes[i].val = (unsigned short)val; } ++code; } } } return codes; } int FlateStream::getHuffmanCodeWord(FlateHuffmanTab *tab) { const FlateCode *code; int c; while (codeSize < tab->maxLen) { if ((c = str->getChar()) == EOF) { break; } codeBuf |= (c & 0xff) << codeSize; codeSize += 8; } code = &tab->codes[codeBuf & ((1 << tab->maxLen) - 1)]; if (codeSize == 0 || codeSize < code->len || code->len == 0) { return EOF; } codeBuf >>= code->len; codeSize -= code->len; return (int)code->val; } int FlateStream::getCodeWord(int bits) { int c; while (codeSize < bits) { if ((c = str->getChar()) == EOF) { return EOF; } codeBuf |= (c & 0xff) << codeSize; codeSize += 8; } c = codeBuf & ((1 << bits) - 1); codeBuf >>= bits; codeSize -= bits; return c; } #endif //------------------------------------------------------------------------ // EOFStream //------------------------------------------------------------------------ EOFStream::EOFStream(Stream *strA) : FilterStream(strA) { } EOFStream::~EOFStream() { delete str; } //------------------------------------------------------------------------ // BufStream //------------------------------------------------------------------------ BufStream::BufStream(Stream *strA, int bufSizeA) : FilterStream(strA) { bufSize = bufSizeA; buf = (int *)gmallocn(bufSize, sizeof(int)); } BufStream::~BufStream() { gfree(buf); delete str; } void BufStream::reset() { int i; str->reset(); for (i = 0; i < bufSize; ++i) { buf[i] = str->getChar(); } } int BufStream::getChar() { int c, i; c = buf[0]; for (i = 1; i < bufSize; ++i) { buf[i - 1] = buf[i]; } buf[bufSize - 1] = str->getChar(); return c; } int BufStream::lookChar() { return buf[0]; } int BufStream::lookChar(int idx) { return buf[idx]; } bool BufStream::isBinary(bool last) const { return str->isBinary(true); } //------------------------------------------------------------------------ // FixedLengthEncoder //------------------------------------------------------------------------ FixedLengthEncoder::FixedLengthEncoder(Stream *strA, int lengthA) : FilterStream(strA) { length = lengthA; count = 0; } FixedLengthEncoder::~FixedLengthEncoder() { if (str->isEncoder()) { delete str; } } void FixedLengthEncoder::reset() { str->reset(); count = 0; } int FixedLengthEncoder::getChar() { if (length >= 0 && count >= length) { return EOF; } ++count; return str->getChar(); } int FixedLengthEncoder::lookChar() { if (length >= 0 && count >= length) { return EOF; } return str->getChar(); } bool FixedLengthEncoder::isBinary(bool last) const { return str->isBinary(true); } //------------------------------------------------------------------------ // ASCIIHexEncoder //------------------------------------------------------------------------ ASCIIHexEncoder::ASCIIHexEncoder(Stream *strA) : FilterStream(strA) { bufPtr = bufEnd = buf; lineLen = 0; eof = false; } ASCIIHexEncoder::~ASCIIHexEncoder() { if (str->isEncoder()) { delete str; } } void ASCIIHexEncoder::reset() { str->reset(); bufPtr = bufEnd = buf; lineLen = 0; eof = false; } bool ASCIIHexEncoder::fillBuf() { static const char *hex = "0123456789abcdef"; int c; if (eof) { return false; } bufPtr = bufEnd = buf; if ((c = str->getChar()) == EOF) { *bufEnd++ = '>'; eof = true; } else { if (lineLen >= 64) { *bufEnd++ = '\n'; lineLen = 0; } *bufEnd++ = hex[(c >> 4) & 0x0f]; *bufEnd++ = hex[c & 0x0f]; lineLen += 2; } return true; } //------------------------------------------------------------------------ // ASCII85Encoder //------------------------------------------------------------------------ ASCII85Encoder::ASCII85Encoder(Stream *strA) : FilterStream(strA) { bufPtr = bufEnd = buf; lineLen = 0; eof = false; } ASCII85Encoder::~ASCII85Encoder() { if (str->isEncoder()) { delete str; } } void ASCII85Encoder::reset() { str->reset(); bufPtr = bufEnd = buf; lineLen = 0; eof = false; } bool ASCII85Encoder::fillBuf() { unsigned int t; char buf1[5]; int c0, c1, c2, c3; int n, i; if (eof) { return false; } c0 = str->getChar(); c1 = str->getChar(); c2 = str->getChar(); c3 = str->getChar(); bufPtr = bufEnd = buf; if (c3 == EOF) { if (c0 == EOF) { n = 0; t = 0; } else { if (c1 == EOF) { n = 1; t = c0 << 24; } else if (c2 == EOF) { n = 2; t = (c0 << 24) | (c1 << 16); } else { n = 3; t = (c0 << 24) | (c1 << 16) | (c2 << 8); } for (i = 4; i >= 0; --i) { buf1[i] = (char)(t % 85 + 0x21); t /= 85; } for (i = 0; i <= n; ++i) { *bufEnd++ = buf1[i]; if (++lineLen == 65) { *bufEnd++ = '\n'; lineLen = 0; } } } *bufEnd++ = '~'; *bufEnd++ = '>'; eof = true; } else { t = (c0 << 24) | (c1 << 16) | (c2 << 8) | c3; if (t == 0) { *bufEnd++ = 'z'; if (++lineLen == 65) { *bufEnd++ = '\n'; lineLen = 0; } } else { for (i = 4; i >= 0; --i) { buf1[i] = (char)(t % 85 + 0x21); t /= 85; } for (i = 0; i <= 4; ++i) { *bufEnd++ = buf1[i]; if (++lineLen == 65) { *bufEnd++ = '\n'; lineLen = 0; } } } } return true; } //------------------------------------------------------------------------ // RunLengthEncoder //------------------------------------------------------------------------ RunLengthEncoder::RunLengthEncoder(Stream *strA) : FilterStream(strA) { bufPtr = bufEnd = nextEnd = buf; eof = false; } RunLengthEncoder::~RunLengthEncoder() { if (str->isEncoder()) { delete str; } } void RunLengthEncoder::reset() { str->reset(); bufPtr = bufEnd = nextEnd = buf; eof = false; } // // When fillBuf finishes, buf[] looks like this: // +-----+--------------+-----------------+-- // + tag | ... data ... | next 0, 1, or 2 | // +-----+--------------+-----------------+-- // ^ ^ ^ // bufPtr bufEnd nextEnd // bool RunLengthEncoder::fillBuf() { int c, c1, c2; int n; // already hit EOF? if (eof) { return false; } // grab two bytes if (nextEnd < bufEnd + 1) { if ((c1 = str->getChar()) == EOF) { eof = true; return false; } } else { c1 = bufEnd[0] & 0xff; } if (nextEnd < bufEnd + 2) { if ((c2 = str->getChar()) == EOF) { eof = true; buf[0] = 0; buf[1] = c1; bufPtr = buf; bufEnd = &buf[2]; return true; } } else { c2 = bufEnd[1] & 0xff; } // check for repeat c = 0; // make gcc happy if (c1 == c2) { n = 2; while (n < 128 && (c = str->getChar()) == c1) { ++n; } buf[0] = (char)(257 - n); buf[1] = c1; bufEnd = &buf[2]; if (c == EOF) { eof = true; } else if (n < 128) { buf[2] = c; nextEnd = &buf[3]; } else { nextEnd = bufEnd; } // get up to 128 chars } else { buf[1] = c1; buf[2] = c2; n = 2; while (n < 128) { if ((c = str->getChar()) == EOF) { eof = true; break; } ++n; buf[n] = c; if (buf[n] == buf[n - 1]) { break; } } if (buf[n] == buf[n - 1]) { buf[0] = (char)(n - 2 - 1); bufEnd = &buf[n - 1]; nextEnd = &buf[n + 1]; } else { buf[0] = (char)(n - 1); bufEnd = nextEnd = &buf[n + 1]; } } bufPtr = buf; return true; } //------------------------------------------------------------------------ // LZWEncoder //------------------------------------------------------------------------ LZWEncoder::LZWEncoder(Stream *strA) : FilterStream(strA) { inBufLen = 0; outBufLen = 0; } LZWEncoder::~LZWEncoder() { if (str->isEncoder()) { delete str; } } void LZWEncoder::reset() { int i; str->reset(); // initialize code table for (i = 0; i < 256; ++i) { table[i].byte = i; table[i].next = nullptr; table[i].children = nullptr; } nextSeq = 258; codeLen = 9; // initialize input buffer inBufLen = str->doGetChars(sizeof(inBuf), inBuf); // initialize output buffer with a clear-table code outBuf = 256; outBufLen = 9; needEOD = false; } int LZWEncoder::getChar() { int ret; if (inBufLen == 0 && !needEOD && outBufLen == 0) { return EOF; } if (outBufLen < 8 && (inBufLen > 0 || needEOD)) { fillBuf(); } if (outBufLen >= 8) { ret = (outBuf >> (outBufLen - 8)) & 0xff; outBufLen -= 8; } else { ret = (outBuf << (8 - outBufLen)) & 0xff; outBufLen = 0; } return ret; } int LZWEncoder::lookChar() { if (inBufLen == 0 && !needEOD && outBufLen == 0) { return EOF; } if (outBufLen < 8 && (inBufLen > 0 || needEOD)) { fillBuf(); } if (outBufLen >= 8) { return (outBuf >> (outBufLen - 8)) & 0xff; } else { return (outBuf << (8 - outBufLen)) & 0xff; } } // On input, outBufLen < 8. // This function generates, at most, 2 12-bit codes // --> outBufLen < 8 + 12 + 12 = 32 void LZWEncoder::fillBuf() { LZWEncoderNode *p0, *p1; int seqLen, code, i; if (needEOD) { outBuf = (outBuf << codeLen) | 257; outBufLen += codeLen; needEOD = false; return; } // find longest matching sequence (if any) p0 = table + inBuf[0]; seqLen = 1; while (inBufLen > seqLen) { for (p1 = p0->children; p1; p1 = p1->next) { if (p1->byte == inBuf[seqLen]) { break; } } if (!p1) { break; } p0 = p1; ++seqLen; } code = (int)(p0 - table); // generate an output code outBuf = (outBuf << codeLen) | code; outBufLen += codeLen; // update the table table[nextSeq].byte = seqLen < inBufLen ? inBuf[seqLen] : 0; table[nextSeq].children = nullptr; if (table[code].children) { table[nextSeq].next = table[code].children; } else { table[nextSeq].next = nullptr; } table[code].children = table + nextSeq; ++nextSeq; // update the input buffer memmove(inBuf, inBuf + seqLen, inBufLen - seqLen); inBufLen -= seqLen; inBufLen += str->doGetChars(sizeof(inBuf) - inBufLen, inBuf + inBufLen); // increment codeLen; generate clear-table code if (nextSeq == (1 << codeLen)) { ++codeLen; if (codeLen == 13) { outBuf = (outBuf << 12) | 256; outBufLen += 12; for (i = 0; i < 256; ++i) { table[i].next = nullptr; table[i].children = nullptr; } nextSeq = 258; codeLen = 9; } } // generate EOD next time if (inBufLen == 0) { needEOD = true; } } //------------------------------------------------------------------------ // CMYKGrayEncoder //------------------------------------------------------------------------ CMYKGrayEncoder::CMYKGrayEncoder(Stream *strA) : FilterStream(strA) { bufPtr = bufEnd = buf; eof = false; } CMYKGrayEncoder::~CMYKGrayEncoder() { if (str->isEncoder()) { delete str; } } void CMYKGrayEncoder::reset() { str->reset(); bufPtr = bufEnd = buf; eof = false; } bool CMYKGrayEncoder::fillBuf() { int c0, c1, c2, c3; int i; if (eof) { return false; } c0 = str->getChar(); c1 = str->getChar(); c2 = str->getChar(); c3 = str->getChar(); if (c3 == EOF) { eof = true; return false; } i = (3 * c0 + 6 * c1 + c2) / 10 + c3; if (i > 255) { i = 255; } bufPtr = bufEnd = buf; *bufEnd++ = (char)i; return true; } //------------------------------------------------------------------------ // RGBGrayEncoder //------------------------------------------------------------------------ RGBGrayEncoder::RGBGrayEncoder(Stream *strA) : FilterStream(strA) { bufPtr = bufEnd = buf; eof = false; } RGBGrayEncoder::~RGBGrayEncoder() { if (str->isEncoder()) { delete str; } } void RGBGrayEncoder::reset() { str->reset(); bufPtr = bufEnd = buf; eof = false; } bool RGBGrayEncoder::fillBuf() { int c0, c1, c2; int i; if (eof) { return false; } c0 = str->getChar(); c1 = str->getChar(); c2 = str->getChar(); if (c2 == EOF) { eof = true; return false; } i = 255 - (3 * c0 + 6 * c1 + c2) / 10; if (i < 0) { i = 0; } bufPtr = bufEnd = buf; *bufEnd++ = (char)i; return true; } //------------------------------------------------------------------------ // SplashBitmapCMYKEncoder //------------------------------------------------------------------------ SplashBitmapCMYKEncoder::SplashBitmapCMYKEncoder(SplashBitmap *bitmapA) : bitmap(bitmapA) { width = (size_t)4 * bitmap->getWidth(); height = bitmap->getHeight(); buf.resize(width); bufPtr = width; curLine = height - 1; } SplashBitmapCMYKEncoder::~SplashBitmapCMYKEncoder() { } void SplashBitmapCMYKEncoder::reset() { bufPtr = width; curLine = height - 1; } int SplashBitmapCMYKEncoder::lookChar() { if (bufPtr >= width && !fillBuf()) { return EOF; } return buf[bufPtr]; } int SplashBitmapCMYKEncoder::getChar() { int ret = lookChar(); bufPtr++; return ret; } bool SplashBitmapCMYKEncoder::fillBuf() { if (curLine < 0) { return false; } if (bufPtr < width) { return true; } bitmap->getCMYKLine(curLine, &buf[0]); bufPtr = 0; curLine--; return true; } Goffset SplashBitmapCMYKEncoder::getPos() { return (height - 1 - curLine) * width + bufPtr; } void SplashBitmapCMYKEncoder::setPos(Goffset pos, int dir) { // This code is mostly untested! if (dir < 0) { curLine = pos / width; } else { curLine = height - 1 - pos / width; } bufPtr = width; fillBuf(); if (dir < 0) { bufPtr = width - 1 - pos % width; } else { bufPtr = pos % width; } } poppler-24.02.0/poppler/Stream.h000066400000000000000000001362571455701731300164510ustar00rootroot00000000000000//======================================================================== // // Stream.h // // 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) 2005 Jeff Muizelaar // Copyright (C) 2008 Julien Rebetez // Copyright (C) 2008, 2010, 2011, 2016-2022 Albert Astals Cid // Copyright (C) 2009 Carlos Garcia Campos // Copyright (C) 2009 Stefan Thomas // Copyright (C) 2010 Hib Eris // Copyright (C) 2011, 2012, 2016, 2020 William Bader // Copyright (C) 2012, 2013 Thomas Freitag // Copyright (C) 2012, 2013 Fabio D'Urso // Copyright (C) 2013, 2017 Adrian Johnson // Copyright (C) 2013 Peter Breitenlohner // Copyright (C) 2013, 2018 Adam Reichold // Copyright (C) 2013 Pino Toscano // Copyright (C) 2019 Volker Krause // Copyright (C) 2019 Alexander Volkov // Copyright (C) 2020-2022 Oliver Sander // Copyright (C) 2020 Philipp Knechtges // Copyright (C) 2021 Hubert Figuiere // Copyright (C) 2021 Christian Persch // Copyright (C) 2021 Georgiy Sgibnev . Work sponsored by lab50.net. // // 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 STREAM_H #define STREAM_H #include #include #include #include "poppler-config.h" #include "poppler_private_export.h" #include "Object.h" class GooFile; class BaseStream; class CachedFile; class SplashBitmap; //------------------------------------------------------------------------ enum StreamKind { strFile, strCachedFile, strASCIIHex, strASCII85, strLZW, strRunLength, strCCITTFax, strDCT, strFlate, strJBIG2, strJPX, strWeird, // internal-use stream types strCrypt // internal-use to detect decode streams }; enum StreamColorSpaceMode { streamCSNone, streamCSDeviceGray, streamCSDeviceRGB, streamCSDeviceCMYK }; //------------------------------------------------------------------------ // This is in Stream.h instead of Decrypt.h to avoid really annoying // include file dependency loops. enum CryptAlgorithm { cryptRC4, cryptAES, cryptAES256, cryptNone }; //------------------------------------------------------------------------ typedef struct _ByteRange { size_t offset; unsigned int length; } ByteRange; //------------------------------------------------------------------------ // Stream (base class) //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT Stream { public: // Constructor. Stream(); // Destructor. virtual ~Stream(); Stream(const Stream &) = delete; Stream &operator=(const Stream &other) = delete; // Get kind of stream. virtual StreamKind getKind() const = 0; // Reset stream to beginning. virtual void reset() = 0; // Close down the stream. virtual void close(); inline int doGetChars(int nChars, unsigned char *buffer) { if (hasGetChars()) { return getChars(nChars, buffer); } else { for (int i = 0; i < nChars; ++i) { const int c = getChar(); if (likely(c != EOF)) { buffer[i] = c; } else { return i; } } return nChars; } } inline void fillString(std::string &s) { unsigned char readBuf[4096]; int readChars; reset(); while ((readChars = doGetChars(4096, readBuf)) != 0) { s.append((const char *)readBuf, readChars); } } inline void fillGooString(GooString *s) { fillString(s->toNonConstStr()); } inline std::vector toUnsignedChars(int initialSize = 4096, int sizeIncrement = 4096) { std::vector buf(initialSize); int readChars; int size = initialSize; int length = 0; int charsToRead = initialSize; bool continueReading = true; reset(); while (continueReading && (readChars = doGetChars(charsToRead, buf.data() + length)) != 0) { length += readChars; if (readChars == charsToRead) { if (lookChar() != EOF) { size += sizeIncrement; charsToRead = sizeIncrement; buf.resize(size); } else { continueReading = false; } } else { continueReading = false; } } buf.resize(length); return buf; } // Get next char from stream. virtual int getChar() = 0; // Peek at next char in stream. virtual int lookChar() = 0; // Get next char from stream without using the predictor. // This is only used by StreamPredictor. virtual int getRawChar(); virtual void getRawChars(int nChars, int *buffer); // Get next char directly from stream source, without filtering it virtual int getUnfilteredChar() = 0; // 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; // Get next line from stream. virtual char *getLine(char *buf, int size); // Discard the next bytes from stream. Returns the number of // bytes discarded, which will be less than only if EOF is // reached. virtual unsigned int discardChars(unsigned int n); // Get current position in file. virtual Goffset getPos() = 0; // Go to a position in the stream. If is negative, the // position is from the end of the file; otherwise the position is // from the start of the file. virtual void setPos(Goffset pos, int dir = 0) = 0; // Get PostScript command for the filter(s). virtual GooString *getPSFilter(int psLevel, const char *indent); // Does this stream type potentially contain non-printable chars? virtual bool isBinary(bool last = true) const = 0; // Get the BaseStream of this stream. virtual BaseStream *getBaseStream() = 0; // Get the stream after the last decoder (this may be a BaseStream // or a DecryptStream). virtual Stream *getUndecodedStream() = 0; // Get the dictionary associated with this stream. virtual Dict *getDict() = 0; virtual Object *getDictObject() = 0; // Is this an encoding filter? virtual bool isEncoder() const { return false; } // Get image parameters which are defined by the stream contents. virtual void getImageParams(int * /*bitsPerComponent*/, StreamColorSpaceMode * /*csMode*/) { } // Return the next stream in the "stack". virtual Stream *getNextStream() const { return nullptr; } // Add filters to this stream according to the parameters in . // Returns the new stream. Stream *addFilters(Dict *dict, int recursion = 0); // Returns true if this stream includes a crypt filter. bool isEncrypted() const; private: friend class Object; // for incRef/decRef // Reference counting. int incRef() { return ++ref; } int decRef() { return --ref; } virtual bool hasGetChars() { return false; } virtual int getChars(int nChars, unsigned char *buffer); Stream *makeFilter(const char *name, Stream *str, Object *params, int recursion = 0, Dict *dict = nullptr); std::atomic_int ref; // reference count }; //------------------------------------------------------------------------ // OutStream // // This is the base class for all streams that output to a file //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT OutStream { public: // Constructor. OutStream(); // Desctructor. virtual ~OutStream(); OutStream(const OutStream &) = delete; OutStream &operator=(const OutStream &other) = delete; // Close the stream virtual void close() = 0; // Return position in stream virtual Goffset getPos() = 0; // Put a char in the stream virtual void put(char c) = 0; virtual void printf(const char *format, ...) GCC_PRINTF_FORMAT(2, 3) = 0; }; //------------------------------------------------------------------------ // FileOutStream //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT FileOutStream : public OutStream { public: FileOutStream(FILE *fa, Goffset startA); ~FileOutStream() override; void close() override; Goffset getPos() override; void put(char c) override; void printf(const char *format, ...) override GCC_PRINTF_FORMAT(2, 3); private: FILE *f; Goffset start; }; //------------------------------------------------------------------------ // BaseStream // // This is the base class for all streams that read directly from a file. //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT BaseStream : public Stream { public: BaseStream(Object &&dictA, Goffset lengthA); ~BaseStream() override; virtual BaseStream *copy() = 0; virtual Stream *makeSubStream(Goffset start, bool limited, Goffset length, Object &&dict) = 0; void setPos(Goffset pos, int dir = 0) override = 0; bool isBinary(bool last = true) const override { return last; } BaseStream *getBaseStream() override { return this; } Stream *getUndecodedStream() override { return this; } Dict *getDict() override { return dict.getDict(); } Object *getDictObject() override { return &dict; } virtual GooString *getFileName() { return nullptr; } virtual Goffset getLength() { return length; } // Get/set position of first byte of stream within the file. virtual Goffset getStart() = 0; virtual void moveStart(Goffset delta) = 0; protected: Goffset length; Object dict; }; //------------------------------------------------------------------------ // BaseInputStream //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT BaseSeekInputStream : public BaseStream { public: // This enum is used to tell the seek() method how it must reposition // the stream offset. enum SeekType { SeekSet, // the offset is set to offset bytes SeekCur, // the offset is set to its current location plus offset bytes SeekEnd // the offset is set to the size of the stream plus offset bytes }; BaseSeekInputStream(Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA); ~BaseSeekInputStream() override; StreamKind getKind() const override { return strWeird; } void 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); } Goffset getPos() override { return bufPos + (bufPtr - buf); } void setPos(Goffset pos, int dir = 0) override; Goffset getStart() override { return start; } void moveStart(Goffset delta) override; int getUnfilteredChar() override { return getChar(); } void unfilteredReset() override { reset(); } protected: Goffset start; bool limited; private: bool fillBuf(); bool hasGetChars() override { return true; } int getChars(int nChars, unsigned char *buffer) override; virtual Goffset currentPos() const = 0; virtual void setCurrentPos(Goffset offset) = 0; virtual Goffset read(char *buf, Goffset size) = 0; static constexpr int seekInputStreamBufSize = 1024; char buf[seekInputStreamBufSize]; char *bufPtr; char *bufEnd; Goffset bufPos; Goffset savePos; bool saved; }; //------------------------------------------------------------------------ // FilterStream // // This is the base class for all streams that filter another stream. //------------------------------------------------------------------------ class FilterStream : public Stream { public: explicit FilterStream(Stream *strA); ~FilterStream() override; void close() override; Goffset getPos() override { return str->getPos(); } void setPos(Goffset pos, int dir = 0) override; BaseStream *getBaseStream() override { return str->getBaseStream(); } Stream *getUndecodedStream() override { return str->getUndecodedStream(); } Dict *getDict() override { return str->getDict(); } Object *getDictObject() override { return str->getDictObject(); } Stream *getNextStream() const override { return str; } int getUnfilteredChar() override { return str->getUnfilteredChar(); } void unfilteredReset() override { str->unfilteredReset(); } protected: Stream *str; }; //------------------------------------------------------------------------ // ImageStream //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT ImageStream { public: // Create an image stream object for an image with the specified // parameters. Note that these are the actual image parameters, // which may be different from the predictor parameters. ImageStream(Stream *strA, int widthA, int nCompsA, int nBitsA); ~ImageStream(); ImageStream(const ImageStream &) = delete; ImageStream &operator=(const ImageStream &other) = delete; // Reset the stream. void reset(); // Close the stream previously reset void close(); // Gets the next pixel from the stream. should be able to hold // at least nComps elements. Returns false at end of file. bool getPixel(unsigned char *pix); // Returns a pointer to the next line of pixels. Returns NULL at // end of file. unsigned char *getLine(); // Skip an entire line from the image. void skipLine(); private: Stream *str; // base stream int width; // pixels per line int nComps; // components per pixel int nBits; // bits per component int nVals; // components per line int inputLineSize; // input line buffer size unsigned char *inputLine; // input line buffer unsigned char *imgLine; // line buffer int imgIdx; // current index in imgLine }; //------------------------------------------------------------------------ // StreamPredictor //------------------------------------------------------------------------ class StreamPredictor { public: // Create a predictor object. Note that the parameters are for the // predictor, and may not match the actual image parameters. StreamPredictor(Stream *strA, int predictorA, int widthA, int nCompsA, int nBitsA); ~StreamPredictor(); StreamPredictor(const StreamPredictor &) = delete; StreamPredictor &operator=(const StreamPredictor &) = delete; bool isOk() { return ok; } int lookChar(); int getChar(); int getChars(int nChars, unsigned char *buffer); private: bool getNextLine(); Stream *str; // base stream int predictor; // predictor int width; // pixels per line int nComps; // components per pixel int nBits; // bits per component int nVals; // components per line int pixBytes; // bytes per pixel int rowBytes; // bytes per line unsigned char *predLine; // line buffer int predIdx; // current index in predLine bool ok; }; //------------------------------------------------------------------------ // FileStream //------------------------------------------------------------------------ #define fileStreamBufSize 256 class POPPLER_PRIVATE_EXPORT FileStream : public BaseStream { public: FileStream(GooFile *fileA, Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA); ~FileStream() override; BaseStream *copy() override; Stream *makeSubStream(Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA) override; StreamKind getKind() const override { return strFile; } void 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); } Goffset getPos() override { return bufPos + (bufPtr - buf); } void setPos(Goffset pos, int dir = 0) override; Goffset getStart() override { return start; } void moveStart(Goffset delta) override; int getUnfilteredChar() override { return getChar(); } void unfilteredReset() override { reset(); } bool getNeedsEncryptionOnSave() const { return needsEncryptionOnSave; } void setNeedsEncryptionOnSave(bool needsEncryptionOnSaveA) { needsEncryptionOnSave = needsEncryptionOnSaveA; } private: bool fillBuf(); bool hasGetChars() override { return true; } int getChars(int nChars, unsigned char *buffer) override { int n, m; n = 0; while (n < nChars) { if (bufPtr >= bufEnd) { if (!fillBuf()) { break; } } m = (int)(bufEnd - bufPtr); if (m > nChars - n) { m = nChars - n; } memcpy(buffer + n, bufPtr, m); bufPtr += m; n += m; } return n; } private: GooFile *file; Goffset offset; Goffset start; bool limited; char buf[fileStreamBufSize]; char *bufPtr; char *bufEnd; Goffset bufPos; Goffset savePos; bool saved; bool needsEncryptionOnSave; // Needed for FileStreams that point to "external" files // and thus when saving we can't do a raw copy }; //------------------------------------------------------------------------ // CachedFileStream //------------------------------------------------------------------------ #define cachedStreamBufSize 1024 class POPPLER_PRIVATE_EXPORT CachedFileStream : public BaseStream { public: CachedFileStream(CachedFile *ccA, Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA); ~CachedFileStream() override; BaseStream *copy() override; Stream *makeSubStream(Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA) override; StreamKind getKind() const override { return strCachedFile; } void 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); } Goffset getPos() override { return bufPos + (bufPtr - buf); } void setPos(Goffset pos, int dir = 0) override; Goffset getStart() override { return start; } void moveStart(Goffset delta) override; int getUnfilteredChar() override { return getChar(); } void unfilteredReset() override { reset(); } private: bool fillBuf(); CachedFile *cc; Goffset start; bool limited; char buf[cachedStreamBufSize]; char *bufPtr; char *bufEnd; unsigned int bufPos; int savePos; bool saved; }; //------------------------------------------------------------------------ // MemStream //------------------------------------------------------------------------ template class BaseMemStream : public BaseStream { public: BaseMemStream(T *bufA, Goffset startA, Goffset lengthA, Object &&dictA) : BaseStream(std::move(dictA), lengthA) { buf = bufA; start = startA; length = lengthA; bufEnd = buf + start + length; bufPtr = buf + start; } BaseStream *copy() override { return new BaseMemStream(buf, start, length, dict.copy()); } Stream *makeSubStream(Goffset startA, bool limited, Goffset lengthA, Object &&dictA) override { Goffset newLength; if (!limited || startA + lengthA > start + length) { newLength = start + length - startA; } else { newLength = lengthA; } return new BaseMemStream(buf, startA, newLength, std::move(dictA)); } StreamKind getKind() const override { return strWeird; } void reset() override { bufPtr = buf + start; } void close() override { } int getChar() override { return (bufPtr < bufEnd) ? (*bufPtr++ & 0xff) : EOF; } int lookChar() override { return (bufPtr < bufEnd) ? (*bufPtr & 0xff) : EOF; } Goffset getPos() override { return (int)(bufPtr - buf); } void setPos(Goffset pos, int dir = 0) override { Goffset i; if (dir >= 0) { i = pos; } else { i = start + length - pos; } if (i < start) { i = start; } else if (i > start + length) { i = start + length; } bufPtr = buf + i; } Goffset getStart() override { return start; } void moveStart(Goffset delta) override { start += delta; length -= delta; bufPtr = buf + start; } int getUnfilteredChar() override { return getChar(); } void unfilteredReset() override { reset(); } protected: T *buf; private: bool hasGetChars() override { return true; } int getChars(int nChars, unsigned char *buffer) override { int n; if (unlikely(nChars <= 0)) { return 0; } if (unlikely(bufPtr >= bufEnd)) { return 0; } if (bufEnd - bufPtr < nChars) { n = (int)(bufEnd - bufPtr); } else { n = nChars; } memcpy(buffer, bufPtr, n); bufPtr += n; return n; } Goffset start; T *bufEnd; T *bufPtr; }; class POPPLER_PRIVATE_EXPORT MemStream : public BaseMemStream { public: MemStream(const char *bufA, Goffset startA, Goffset lengthA, Object &&dictA) : BaseMemStream(bufA, startA, lengthA, std::move(dictA)) { } ~MemStream() override; }; class AutoFreeMemStream : public BaseMemStream { bool filterRemovalForbidden = false; public: // AutoFreeMemStream takes ownership over the buffer. // The buffer should be created using gmalloc(). AutoFreeMemStream(char *bufA, Goffset startA, Goffset lengthA, Object &&dictA) : BaseMemStream(bufA, startA, lengthA, std::move(dictA)) { } ~AutoFreeMemStream() override; // A hack to deal with the strange behaviour of PDFDoc::writeObject(). bool isFilterRemovalForbidden() const; void setFilterRemovalForbidden(bool forbidden); }; //------------------------------------------------------------------------ // EmbedStream // // This is a special stream type used for embedded streams (inline // images). It reads directly from the base stream -- after the // EmbedStream is deleted, reads from the base stream will proceed where // the BaseStream left off. Note that this is very different behavior // that creating a new FileStream (using makeSubStream). //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT EmbedStream : public BaseStream { public: EmbedStream(Stream *strA, Object &&dictA, bool limitedA, Goffset lengthA, bool reusableA = false); ~EmbedStream() override; BaseStream *copy() override; Stream *makeSubStream(Goffset start, bool limitedA, Goffset lengthA, Object &&dictA) override; StreamKind getKind() const override { return str->getKind(); } void reset() override; int getChar() override; int lookChar() override; Goffset getPos() override; void setPos(Goffset pos, int dir = 0) override; Goffset getStart() override; void moveStart(Goffset delta) override; int getUnfilteredChar() override { return str->getUnfilteredChar(); } void unfilteredReset() override { str->unfilteredReset(); } void rewind(); void restore(); private: bool hasGetChars() override { return true; } int getChars(int nChars, unsigned char *buffer) override; Stream *str; bool limited; bool reusable; bool record; bool replay; unsigned char *bufData; long bufMax; long bufLen; long bufPos; Goffset start; }; //------------------------------------------------------------------------ // ASCIIHexStream //------------------------------------------------------------------------ class ASCIIHexStream : public FilterStream { public: explicit ASCIIHexStream(Stream *strA); ~ASCIIHexStream() override; StreamKind getKind() const override { return strASCIIHex; } void reset() override; int getChar() override { int c = lookChar(); buf = EOF; return c; } int lookChar() override; GooString *getPSFilter(int psLevel, const char *indent) override; bool isBinary(bool last = true) const override; private: int buf; bool eof; }; //------------------------------------------------------------------------ // ASCII85Stream //------------------------------------------------------------------------ class ASCII85Stream : public FilterStream { public: explicit ASCII85Stream(Stream *strA); ~ASCII85Stream() override; StreamKind getKind() const override { return strASCII85; } void reset() override; int getChar() override { int ch = lookChar(); ++index; return ch; } int lookChar() override; GooString *getPSFilter(int psLevel, const char *indent) override; bool isBinary(bool last = true) const override; private: int c[5]; int b[4]; int index, n; bool eof; }; //------------------------------------------------------------------------ // LZWStream //------------------------------------------------------------------------ class LZWStream : public FilterStream { public: LZWStream(Stream *strA, int predictor, int columns, int colors, int bits, int earlyA); ~LZWStream() override; StreamKind getKind() const override { return strLZW; } void 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; bool isBinary(bool last = true) const override; private: bool hasGetChars() override { return true; } int getChars(int nChars, unsigned char *buffer) override; inline int doGetRawChar() { if (eof) { return EOF; } if (seqIndex >= seqLength) { if (!processNextCode()) { return EOF; } } return seqBuf[seqIndex++]; } StreamPredictor *pred; // predictor int early; // early parameter bool eof; // true if at eof unsigned int inputBuf; // input buffer int inputBits; // number of bits in input buffer struct { // decoding table int length; int head; unsigned char tail; } table[4097]; int nextCode; // next code to be used int nextBits; // number of bits in next code word int prevCode; // previous code used in stream int newChar; // next char to be added to table unsigned char seqBuf[4097]; // buffer for current sequence int seqLength; // length of current sequence int seqIndex; // index into current sequence bool first; // first code after a table clear bool processNextCode(); void clearTable(); int getCode(); }; //------------------------------------------------------------------------ // RunLengthStream //------------------------------------------------------------------------ class RunLengthStream : public FilterStream { public: explicit RunLengthStream(Stream *strA); ~RunLengthStream() override; StreamKind getKind() const override { return strRunLength; } void 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; bool isBinary(bool last = true) const override; private: bool hasGetChars() override { return true; } int getChars(int nChars, unsigned char *buffer) override; char buf[128]; // buffer char *bufPtr; // next char to read char *bufEnd; // end of buffer bool eof; bool fillBuf(); }; //------------------------------------------------------------------------ // CCITTFaxStream //------------------------------------------------------------------------ struct CCITTCodeTable; class CCITTFaxStream : public FilterStream { public: 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; int getChar() override { int c = lookChar(); buf = EOF; return c; } int lookChar() override; GooString *getPSFilter(int psLevel, const char *indent) override; bool isBinary(bool last = true) const override; void unfilteredReset() override; int getEncoding() { return encoding; } bool getEndOfLine() { return endOfLine; } bool getEncodedByteAlign() { return byteAlign; } bool getEndOfBlock() { return endOfBlock; } int getColumns() { return columns; } bool getBlackIs1() { return black; } int getDamagedRowsBeforeError() { return damagedRowsBeforeError; } private: void ccittReset(bool unfiltered); int encoding; // 'K' parameter bool endOfLine; // 'EndOfLine' parameter bool byteAlign; // 'EncodedByteAlign' parameter int columns; // 'Columns' parameter int rows; // 'Rows' parameter bool endOfBlock; // 'EndOfBlock' parameter bool black; // 'BlackIs1' parameter int damagedRowsBeforeError; // 'DamagedRowsBeforeError' parameter bool eof; // true if at eof bool nextLine2D; // true if next line uses 2D encoding int row; // current row unsigned int inputBuf; // input buffer int inputBits; // number of bits in input buffer int *codingLine; // coding line changing elements int *refLine; // reference line changing elements int a0i; // index into codingLine bool err; // error on current line int outputBits; // remaining ouput bits int buf; // character buffer void addPixels(int a1, int blackPixels); void addPixelsNeg(int a1, int blackPixels); short getTwoDimCode(); short getWhiteCode(); short getBlackCode(); short lookBits(int n); void eatBits(int n) { if ((inputBits -= n) < 0) { inputBits = 0; } } }; #ifndef ENABLE_LIBJPEG //------------------------------------------------------------------------ // DCTStream //------------------------------------------------------------------------ // DCT component info struct DCTCompInfo { int id; // component ID int hSample, vSample; // horiz/vert sampling resolutions int quantTable; // quantization table number int prevDC; // DC coefficient accumulator }; struct DCTScanInfo { bool comp[4]; // comp[i] is set if component i is // included in this scan int numComps; // number of components in the scan int dcHuffTable[4]; // DC Huffman table numbers int acHuffTable[4]; // AC Huffman table numbers int firstCoeff, lastCoeff; // first and last DCT coefficient int ah, al; // successive approximation parameters }; // DCT Huffman decoding table struct DCTHuffTable { unsigned char firstSym[17]; // first symbol for this bit length unsigned short firstCode[17]; // first code for this bit length unsigned short numCodes[17]; // number of codes of this bit length unsigned char sym[256]; // symbols }; class DCTStream : public FilterStream { public: DCTStream(Stream *strA, int colorXformA, Dict *dict, int recursion); ~DCTStream() override; StreamKind getKind() const override { return strDCT; } void reset() override; void close() override; int getChar() override; int lookChar() override; GooString *getPSFilter(int psLevel, const char *indent) override; bool isBinary(bool last = true) const override; void unfilteredReset() override; private: void dctReset(bool unfiltered); bool progressive; // set if in progressive mode bool interleaved; // set if in interleaved mode int width, height; // image size int mcuWidth, mcuHeight; // size of min coding unit, in data units int bufWidth, bufHeight; // frameBuf size DCTCompInfo compInfo[4]; // info for each component DCTScanInfo scanInfo; // info for the current scan int numComps; // number of components in image int colorXform; // color transform: -1 = unspecified // 0 = none // 1 = YUV/YUVK -> RGB/CMYK bool gotJFIFMarker; // set if APP0 JFIF marker was present bool gotAdobeMarker; // set if APP14 Adobe marker was present int restartInterval; // restart interval, in MCUs unsigned short quantTables[4][64]; // quantization tables int numQuantTables; // number of quantization tables DCTHuffTable dcHuffTables[4]; // DC Huffman tables DCTHuffTable acHuffTables[4]; // AC Huffman tables int numDCHuffTables; // number of DC Huffman tables int numACHuffTables; // number of AC Huffman tables unsigned char *rowBuf[4][32]; // buffer for one MCU (non-progressive mode) int *frameBuf[4]; // buffer for frame (progressive mode) int comp, x, y, dy; // current position within image/MCU int restartCtr; // MCUs left until restart int restartMarker; // next restart marker int eobRun; // number of EOBs left in the current run int inputBuf; // input buffer for variable length codes int inputBits; // number of valid bits in input buffer void restart(); bool readMCURow(); void readScan(); bool readDataUnit(DCTHuffTable *dcHuffTable, DCTHuffTable *acHuffTable, int *prevDC, int data[64]); bool readProgressiveDataUnit(DCTHuffTable *dcHuffTable, DCTHuffTable *acHuffTable, int *prevDC, int data[64]); void decodeImage(); void transformDataUnit(unsigned short *quantTable, int dataIn[64], unsigned char dataOut[64]); int readHuffSym(DCTHuffTable *table); int readAmp(int size); int readBit(); bool readHeader(); bool readBaselineSOF(); bool readProgressiveSOF(); bool readScanInfo(); bool readQuantTables(); bool readHuffmanTables(); bool readRestartInterval(); bool readJFIFMarker(); bool readAdobeMarker(); bool readTrailer(); int readMarker(); int read16(); }; #endif #ifndef ENABLE_ZLIB_UNCOMPRESS //------------------------------------------------------------------------ // FlateStream //------------------------------------------------------------------------ # define flateWindow 32768 // buffer size # define flateMask (flateWindow - 1) # define flateMaxHuffman 15 // max Huffman code length # define flateMaxCodeLenCodes 19 // max # code length codes # define flateMaxLitCodes 288 // max # literal codes # define flateMaxDistCodes 30 // max # distance codes // Huffman code table entry struct FlateCode { unsigned short len; // code length, in bits unsigned short val; // value represented by this code }; struct FlateHuffmanTab { const FlateCode *codes; int maxLen; }; // Decoding info for length and distance code words struct FlateDecode { int bits; // # extra bits int first; // first length/distance }; class FlateStream : public FilterStream { public: FlateStream(Stream *strA, int predictor, int columns, int colors, int bits); ~FlateStream() override; StreamKind getKind() const override { return strFlate; } void 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; bool isBinary(bool last = true) const override; void unfilteredReset() override; private: void flateReset(bool unfiltered); inline int doGetRawChar() { int c; while (remain == 0) { if (endOfBlock && eof) { return EOF; } readSome(); } c = buf[index]; index = (index + 1) & flateMask; --remain; return c; } bool hasGetChars() override { return true; } int getChars(int nChars, unsigned char *buffer) override; StreamPredictor *pred; // predictor unsigned char buf[flateWindow]; // output data buffer int index; // current index into output buffer int remain; // number valid bytes in output buffer int codeBuf; // input buffer int codeSize; // number of bits in input buffer int // literal and distance code lengths codeLengths[flateMaxLitCodes + flateMaxDistCodes]; FlateHuffmanTab litCodeTab; // literal code table FlateHuffmanTab distCodeTab; // distance code table bool compressedBlock; // set if reading a compressed block int blockLen; // remaining length of uncompressed block bool endOfBlock; // set when end of block is reached bool eof; // set when end of stream is reached static const int // code length code reordering codeLenCodeMap[flateMaxCodeLenCodes]; static const FlateDecode // length decoding info lengthDecode[flateMaxLitCodes - 257]; static const FlateDecode // distance decoding info distDecode[flateMaxDistCodes]; static FlateHuffmanTab // fixed literal code table fixedLitCodeTab; static FlateHuffmanTab // fixed distance code table fixedDistCodeTab; void readSome(); bool startBlock(); void loadFixedCodes(); bool readDynamicCodes(); FlateCode *compHuffmanCodes(const int *lengths, int n, int *maxLen); int getHuffmanCodeWord(FlateHuffmanTab *tab); int getCodeWord(int bits); }; #endif //------------------------------------------------------------------------ // EOFStream //------------------------------------------------------------------------ class EOFStream : public FilterStream { public: explicit EOFStream(Stream *strA); ~EOFStream() override; StreamKind getKind() const override { return strWeird; } void reset() override { } int getChar() override { return EOF; } int lookChar() override { return EOF; } GooString *getPSFilter(int /*psLevel*/, const char * /*indent*/) override { return nullptr; } bool isBinary(bool /*last = true*/) const override { return false; } }; //------------------------------------------------------------------------ // BufStream //------------------------------------------------------------------------ class BufStream : public FilterStream { public: BufStream(Stream *strA, int bufSizeA); ~BufStream() override; StreamKind getKind() const override { return strWeird; } void reset() override; int getChar() override; int lookChar() override; GooString *getPSFilter(int psLevel, const char *indent) override { return nullptr; } bool isBinary(bool last = true) const override; int lookChar(int idx); private: int *buf; int bufSize; }; //------------------------------------------------------------------------ // FixedLengthEncoder //------------------------------------------------------------------------ class FixedLengthEncoder : public FilterStream { public: FixedLengthEncoder(Stream *strA, int lengthA); ~FixedLengthEncoder() override; StreamKind getKind() const override { return strWeird; } void reset() override; int getChar() override; int lookChar() override; GooString *getPSFilter(int /*psLevel*/, const char * /*indent*/) override { return nullptr; } bool isBinary(bool /*last = true*/) const override; bool isEncoder() const override { return true; } private: int length; int count; }; //------------------------------------------------------------------------ // ASCIIHexEncoder //------------------------------------------------------------------------ class ASCIIHexEncoder : public FilterStream { public: explicit ASCIIHexEncoder(Stream *strA); ~ASCIIHexEncoder() override; StreamKind getKind() const override { return strWeird; } void 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; } bool isBinary(bool /*last = true*/) const override { return false; } bool isEncoder() const override { return true; } private: char buf[4]; char *bufPtr; char *bufEnd; int lineLen; bool eof; bool fillBuf(); }; //------------------------------------------------------------------------ // ASCII85Encoder //------------------------------------------------------------------------ class ASCII85Encoder : public FilterStream { public: explicit ASCII85Encoder(Stream *strA); ~ASCII85Encoder() override; StreamKind getKind() const override { return strWeird; } void 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; } bool isBinary(bool /*last = true*/) const override { return false; } bool isEncoder() const override { return true; } private: char buf[8]; char *bufPtr; char *bufEnd; int lineLen; bool eof; bool fillBuf(); }; //------------------------------------------------------------------------ // RunLengthEncoder //------------------------------------------------------------------------ class RunLengthEncoder : public FilterStream { public: explicit RunLengthEncoder(Stream *strA); ~RunLengthEncoder() override; StreamKind getKind() const override { return strWeird; } void 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; } bool isBinary(bool /*last = true*/) const override { return true; } bool isEncoder() const override { return true; } private: char buf[131]; char *bufPtr; char *bufEnd; char *nextEnd; bool eof; bool fillBuf(); }; //------------------------------------------------------------------------ // LZWEncoder //------------------------------------------------------------------------ struct LZWEncoderNode { int byte; LZWEncoderNode *next; // next sibling LZWEncoderNode *children; // first child }; class LZWEncoder : public FilterStream { public: explicit LZWEncoder(Stream *strA); ~LZWEncoder() override; StreamKind getKind() const override { return strWeird; } void reset() override; int getChar() override; int lookChar() override; GooString *getPSFilter(int psLevel, const char *indent) override { return nullptr; } bool isBinary(bool last = true) const override { return true; } bool isEncoder() const override { return true; } private: LZWEncoderNode table[4096]; int nextSeq; int codeLen; unsigned char inBuf[4096]; int inBufLen; int outBuf; int outBufLen; bool needEOD; void fillBuf(); }; //------------------------------------------------------------------------ // CMYKGrayEncoder //------------------------------------------------------------------------ class CMYKGrayEncoder : public FilterStream { public: explicit CMYKGrayEncoder(Stream *strA); ~CMYKGrayEncoder() override; StreamKind getKind() const override { return strWeird; } void 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; } bool isBinary(bool /*last = true*/) const override { return false; } bool isEncoder() const override { return true; } private: char buf[2]; char *bufPtr; char *bufEnd; bool eof; bool fillBuf(); }; //------------------------------------------------------------------------ // RGBGrayEncoder //------------------------------------------------------------------------ class RGBGrayEncoder : public FilterStream { public: explicit RGBGrayEncoder(Stream *strA); ~RGBGrayEncoder() override; StreamKind getKind() const override { return strWeird; } void 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; } bool isBinary(bool /*last = true*/) const override { return false; } bool isEncoder() const override { return true; } private: char buf[2]; char *bufPtr; char *bufEnd; bool eof; bool fillBuf(); }; //------------------------------------------------------------------------ // SplashBitmapCMYKEncoder // // This stream helps to condense SplashBitmaps (mostly of DeviceN8 type) into // pure CMYK colors. In particular for a DeviceN8 bitmap it redacts the spot colorants. //------------------------------------------------------------------------ class SplashBitmapCMYKEncoder : public Stream { public: explicit SplashBitmapCMYKEncoder(SplashBitmap *bitmapA); ~SplashBitmapCMYKEncoder() override; StreamKind getKind() const override { return strWeird; } void reset() override; int getChar() override; int lookChar() override; GooString *getPSFilter(int /*psLevel*/, const char * /*indent*/) override { return nullptr; } 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 // successive streams. bool isEncoder() const override { return false; } int getUnfilteredChar() override { return getChar(); } void unfilteredReset() override { reset(); } BaseStream *getBaseStream() override { return nullptr; } Stream *getUndecodedStream() override { return this; } Dict *getDict() override { return nullptr; } Object *getDictObject() override { return nullptr; } Goffset getPos() override; void setPos(Goffset pos, int dir = 0) override; private: SplashBitmap *bitmap; size_t width; int height; std::vector buf; size_t bufPtr; int curLine; bool fillBuf(); }; #endif poppler-24.02.0/poppler/StructElement.cc000066400000000000000000001412701455701731300201410ustar00rootroot00000000000000//======================================================================== // // StructElement.cc // // This file is licensed under the GPLv2 or later // // Copyright 2013, 2014 Igalia S.L. // Copyright 2014 Luigi Scarso // Copyright 2014, 2017-2019, 2021, 2023 Albert Astals Cid // Copyright 2015 Dmytro Morgun // 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 // //======================================================================== #include "StructElement.h" #include "StructTreeRoot.h" #include "GlobalParams.h" #include "UnicodeMap.h" #include "PDFDoc.h" #include "Dict.h" #include class GfxState; static bool isPlacementName(Object *value) { return value->isName("Block") || value->isName("Inline") || value->isName("Before") || value->isName("Start") || value->isName("End"); } static bool isWritingModeName(Object *value) { return value->isName("LrTb") || value->isName("RlTb") || value->isName("TbRl"); } static bool isBorderStyleName(Object *value) { return value->isName("None") || value->isName("Hidden") || value->isName("Dotted") || value->isName("Dashed") || value->isName("Solid") || value->isName("Double") || value->isName("Groove") || value->isName("Ridge") || value->isName("Inset") || value->isName("Outset"); } static bool isTextAlignName(Object *value) { return value->isName("Start") || value->isName("End") || value->isName("Center") || value->isName("Justify"); } static bool isBlockAlignName(Object *value) { return value->isName("Before") || value->isName("Middle") || value->isName("After") || value->isName("Justify"); } static bool isInlineAlignName(Object *value) { return value->isName("Start") || value->isName("End") || value->isName("Center"); } static bool isNumber(Object *value) { return value->isNum(); } static bool isLineHeight(Object *value) { return value->isName("Normal") || value->isName("Auto") || isNumber(value); } static bool isTextDecorationName(Object *value) { return value->isName("None") || value->isName("Underline") || value->isName("Overline") || value->isName("LineThrough"); } static bool isRubyAlignName(Object *value) { return value->isName("Start") || value->isName("End") || value->isName("Center") || value->isName("Justify") || value->isName("Distribute"); } static bool isRubyPositionName(Object *value) { return value->isName("Before") || value->isName("After") || value->isName("Warichu") || value->isName("Inline"); } static bool isGlyphOrientationName(Object *value) { return value->isName("Auto") || value->isName("90") || value->isName("180") || value->isName("270") || value->isName("360") || value->isName("-90") || value->isName("-180"); } static bool isListNumberingName(Object *value) { return value->isName("None") || value->isName("Disc") || value->isName("Circle") || value->isName("Square") || value->isName("Decimal") || value->isName("UpperRoman") || value->isName("LowerRoman") || value->isName("UpperAlpha") || value->isName("LowerAlpha"); } static bool isFieldRoleName(Object *value) { return value->isName("rb") || value->isName("cb") || value->isName("pb") || value->isName("tv"); } static bool isFieldCheckedName(Object *value) { return value->isName("on") || value->isName("off") || value->isName("neutral"); } static bool isTableScopeName(Object *value) { return value->isName("Row") || value->isName("Column") || value->isName("Both"); } static bool isRGBColor(Object *value) { if (!(value->isArray() && value->arrayGetLength() == 3)) { return false; } bool okay = true; for (int i = 0; i < 3; i++) { Object obj = value->arrayGet(i); if (!obj.isNum()) { okay = false; break; } if (obj.getNum() < 0.0 || obj.getNum() > 1.0) { okay = false; break; } } return okay; } static bool isNatural(Object *value) { return (value->isInt() && value->getInt() > 0) || (value->isInt64() && value->getInt64() > 0); } static bool isPositive(Object *value) { return value->isNum() && value->getNum() >= 0.0; } static bool isNumberOrAuto(Object *value) { return isNumber(value) || value->isName("Auto"); } static bool isTextString(Object *value) { // XXX: Shall isName() also be checked? return value->isString(); } #define ARRAY_CHECKER(name, checkItem, length, allowSingle, allowNulls) \ static bool name(Object *value) \ { \ if (!value->isArray()) \ return allowSingle ? checkItem(value) : false; \ \ 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)) { \ okay = false; \ break; \ } \ } \ return okay; \ } ARRAY_CHECKER(isRGBColorOrOptionalArray4, isRGBColor, 4, true, true) ARRAY_CHECKER(isPositiveOrOptionalArray4, isPositive, 4, true, true) ARRAY_CHECKER(isPositiveOrArray4, isPositive, 4, true, false) ARRAY_CHECKER(isBorderStyle, isBorderStyleName, 4, true, true) ARRAY_CHECKER(isNumberArray4, isNumber, 4, false, false) ARRAY_CHECKER(isNumberOrArrayN, isNumber, 0, true, false) ARRAY_CHECKER(isTableHeaders, isTextString, 0, false, false) // Type of functions used to do type-checking on attribute values typedef bool (*AttributeCheckFunc)(Object *); // Maps attributes to their names and whether the attribute can be inherited. struct AttributeMapEntry { Attribute::Type type; const char *name; const Object *defval; bool inherit; AttributeCheckFunc check; }; struct AttributeDefaults { AttributeDefaults() {}; // needed to support old clang Object Inline = Object(objName, "Inline"); Object LrTb = Object(objName, "LrTb"); Object Normal = Object(objName, "Normal"); Object Distribute = Object(objName, "Distribute"); Object off = Object(objName, "off"); Object Zero = Object(0.0); Object Auto = Object(objName, "Auto"); Object Start = Object(objName, "Start"); Object None = Object(objName, "None"); Object Before = Object(objName, "Before"); Object Nat1 = Object(1); }; static const AttributeDefaults attributeDefaults; #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(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), ATTR(BackgroundColor, false, isRGBColor), ATTR(BorderColor, true, isRGBColorOrOptionalArray4), ATTR_WITH_DEFAULT(BorderStyle, false, isBorderStyle, None), ATTR(BorderThickness, true, isPositiveOrOptionalArray4), ATTR_WITH_DEFAULT(Padding, false, isPositiveOrArray4, Zero), ATTR(Color, true, isRGBColor), ATTR_LIST_END }; static const AttributeMapEntry attributeMapCommonBlock[] = { ATTR_WITH_DEFAULT(SpaceBefore, false, isPositive, Zero), ATTR_WITH_DEFAULT(SpaceAfter, false, isPositive, Zero), ATTR_WITH_DEFAULT(StartIndent, true, isNumber, Zero), ATTR_WITH_DEFAULT(EndIndent, true, isNumber, Zero), ATTR_WITH_DEFAULT(TextIndent, true, isNumber, Zero), ATTR_WITH_DEFAULT(TextAlign, true, isTextAlignName, Start), ATTR(BBox, false, isNumberArray4), ATTR_WITH_DEFAULT(Width, false, isNumberOrAuto, Auto), ATTR_WITH_DEFAULT(Height, false, isNumberOrAuto, Auto), ATTR_WITH_DEFAULT(BlockAlign, true, isBlockAlignName, Before), ATTR_WITH_DEFAULT(InlineAlign, true, isInlineAlignName, Start), ATTR_LIST_END }; static const AttributeMapEntry attributeMapCommonInline[] = { ATTR_WITH_DEFAULT(BaselineShift, false, isNumber, Zero), ATTR_WITH_DEFAULT(LineHeight, true, isLineHeight, Normal), ATTR(TextDecorationColor, true, isRGBColor), ATTR(TextDecorationThickness, true, isPositive), ATTR_WITH_DEFAULT(TextDecorationType, false, isTextDecorationName, None), ATTR_WITH_DEFAULT(GlyphOrientationVertical, true, isGlyphOrientationName, Auto), ATTR_LIST_END }; static const AttributeMapEntry attributeMapCommonRubyText[] = { ATTR_WITH_DEFAULT(RubyPosition, true, isRubyPositionName, Before), ATTR_WITH_DEFAULT(RubyAlign, true, isRubyAlignName, Distribute), ATTR_LIST_END }; static const AttributeMapEntry attributeMapCommonColumns[] = { ATTR_WITH_DEFAULT(ColumnCount, false, isNatural, Nat1), ATTR(ColumnGap, false, isNumberOrArrayN), ATTR(ColumnWidths, false, isNumberOrArrayN), ATTR_LIST_END }; static const AttributeMapEntry attributeMapCommonList[] = { ATTR_WITH_DEFAULT(ListNumbering, true, isListNumberingName, None), ATTR_LIST_END }; static const AttributeMapEntry attributeMapCommonPrintField[] = { ATTR(Role, false, isFieldRoleName), ATTR_WITH_DEFAULT(checked, false, isFieldCheckedName, off), ATTR(Desc, false, isTextString), ATTR_LIST_END }; static const AttributeMapEntry attributeMapCommonTable[] = { ATTR(Headers, false, isTableHeaders), ATTR(Scope, false, isTableScopeName), ATTR(Summary, false, isTextString), ATTR_LIST_END }; static const AttributeMapEntry attributeMapCommonTableCell[] = { ATTR_WITH_DEFAULT(RowSpan, false, isNatural, Nat1), ATTR_WITH_DEFAULT(ColSpan, false, isNatural, Nat1), ATTR_WITH_DEFAULT(TBorderStyle, true, isBorderStyle, None), ATTR_WITH_DEFAULT(TPadding, true, isPositiveOrArray4, Zero), ATTR_LIST_END }; #undef ATTR_WITH_DEFAULT #undef ATTR static const AttributeMapEntry *attributeMapAll[] = { attributeMapCommonShared, attributeMapCommonBlock, attributeMapCommonInline, attributeMapCommonRubyText, attributeMapCommonColumns, attributeMapCommonList, attributeMapCommonPrintField, attributeMapCommonTable, attributeMapCommonTableCell, nullptr, }; static const AttributeMapEntry *attributeMapShared[] = { attributeMapCommonShared, nullptr, }; static const AttributeMapEntry *attributeMapBlock[] = { attributeMapCommonShared, attributeMapCommonBlock, nullptr, }; static const AttributeMapEntry *attributeMapInline[] = { attributeMapCommonShared, attributeMapCommonInline, nullptr, }; static const AttributeMapEntry *attributeMapTableCell[] = { attributeMapCommonShared, attributeMapCommonBlock, attributeMapCommonTable, attributeMapCommonTableCell, nullptr, }; static const AttributeMapEntry *attributeMapRubyText[] = { attributeMapCommonShared, attributeMapCommonInline, attributeMapCommonRubyText, nullptr, }; static const AttributeMapEntry *attributeMapColumns[] = { attributeMapCommonShared, attributeMapCommonInline, attributeMapCommonColumns, nullptr, }; static const AttributeMapEntry *attributeMapList[] = { attributeMapCommonShared, attributeMapCommonList, nullptr, }; static const AttributeMapEntry *attributeMapTable[] = { attributeMapCommonShared, attributeMapCommonBlock, attributeMapCommonTable, nullptr, }; static const AttributeMapEntry *attributeMapIllustration[] = { // XXX: Illustrations may have some attributes from the "shared", "inline", // the "block" sets. This is a loose specification; making it better // means duplicating entries from the sets. This seems good enough... attributeMapCommonShared, attributeMapCommonBlock, attributeMapCommonInline, nullptr, }; // Table mapping owners of attributes to their names. static const struct OwnerMapEntry { Attribute::Owner owner; const char *name; } ownerMap[] = { // XXX: Those are sorted in the owner priority resolution order. If the // same attribute is defined with two owners, the order in the table // can be used to know which one has more priority. { Attribute::XML_1_00, "XML-1.00" }, { Attribute::HTML_3_20, "HTML-3.20" }, { Attribute::HTML_4_01, "HTML-4.01" }, { Attribute::OEB_1_00, "OEB-1.00" }, { Attribute::RTF_1_05, "RTF-1.05" }, { Attribute::CSS_1_00, "CSS-1.00" }, { Attribute::CSS_2_00, "CSS-2.00" }, { Attribute::Layout, "Layout" }, { Attribute::PrintField, "PrintField" }, { Attribute::Table, "Table" }, { Attribute::List, "List" }, { Attribute::UserProperties, "UserProperties" }, }; static bool ownerHasMorePriority(Attribute::Owner a, Attribute::Owner b) { size_t aIndex, bIndex, i; for (i = aIndex = bIndex = 0; i < sizeof(ownerMap) / sizeof(ownerMap[0]); i++) { if (ownerMap[i].owner == a) { aIndex = i; } if (ownerMap[i].owner == b) { bIndex = i; } } return aIndex < bIndex; } // Maps element types to their names and also serves as lookup table // for additional element type attributes. enum ElementType { elementTypeUndefined, elementTypeGrouping, elementTypeInline, elementTypeBlock, }; static const struct TypeMapEntry { StructElement::Type type; const char *name; ElementType elementType; const AttributeMapEntry **attributes; } typeMap[] = { { StructElement::Document, "Document", elementTypeGrouping, attributeMapShared }, { StructElement::Part, "Part", elementTypeGrouping, attributeMapShared }, { StructElement::Art, "Art", elementTypeGrouping, attributeMapColumns }, { StructElement::Sect, "Sect", elementTypeGrouping, attributeMapColumns }, { StructElement::Div, "Div", elementTypeGrouping, attributeMapColumns }, { StructElement::BlockQuote, "BlockQuote", elementTypeGrouping, attributeMapInline }, { StructElement::Caption, "Caption", elementTypeGrouping, attributeMapInline }, { StructElement::NonStruct, "NonStruct", elementTypeGrouping, attributeMapInline }, { StructElement::Index, "Index", elementTypeGrouping, attributeMapInline }, { StructElement::Private, "Private", elementTypeGrouping, attributeMapInline }, { StructElement::Span, "Span", elementTypeInline, attributeMapInline }, { StructElement::Quote, "Quote", elementTypeInline, attributeMapInline }, { StructElement::Note, "Note", elementTypeInline, attributeMapInline }, { StructElement::Reference, "Reference", elementTypeInline, attributeMapInline }, { StructElement::BibEntry, "BibEntry", elementTypeInline, attributeMapInline }, { StructElement::Code, "Code", elementTypeInline, attributeMapInline }, { StructElement::Link, "Link", elementTypeInline, attributeMapInline }, { StructElement::Annot, "Annot", elementTypeInline, attributeMapInline }, { StructElement::Ruby, "Ruby", elementTypeInline, attributeMapRubyText }, { StructElement::RB, "RB", elementTypeUndefined, attributeMapRubyText }, { StructElement::RT, "RT", elementTypeUndefined, attributeMapRubyText }, { StructElement::RP, "RP", elementTypeUndefined, attributeMapShared }, { StructElement::Warichu, "Warichu", elementTypeInline, attributeMapRubyText }, { StructElement::WT, "WT", elementTypeUndefined, attributeMapShared }, { StructElement::WP, "WP", elementTypeUndefined, attributeMapShared }, { StructElement::P, "P", elementTypeBlock, attributeMapBlock }, { StructElement::H, "H", elementTypeBlock, attributeMapBlock }, { StructElement::H1, "H1", elementTypeBlock, attributeMapBlock }, { StructElement::H2, "H2", elementTypeBlock, attributeMapBlock }, { StructElement::H3, "H3", elementTypeBlock, attributeMapBlock }, { StructElement::H4, "H4", elementTypeBlock, attributeMapBlock }, { StructElement::H5, "H5", elementTypeBlock, attributeMapBlock }, { StructElement::H6, "H6", elementTypeBlock, attributeMapBlock }, { StructElement::L, "L", elementTypeBlock, attributeMapList }, { StructElement::LI, "LI", elementTypeBlock, attributeMapBlock }, { StructElement::Lbl, "Lbl", elementTypeBlock, attributeMapBlock }, { StructElement::LBody, "LBody", elementTypeBlock, attributeMapBlock }, { StructElement::Table, "Table", elementTypeBlock, attributeMapTable }, { StructElement::TR, "TR", elementTypeUndefined, attributeMapShared }, { StructElement::TH, "TH", elementTypeUndefined, attributeMapTableCell }, { StructElement::TD, "TD", elementTypeUndefined, attributeMapTableCell }, { StructElement::THead, "THead", elementTypeUndefined, attributeMapShared }, { StructElement::TFoot, "TFoot", elementTypeUndefined, attributeMapShared }, { StructElement::TBody, "TBody", elementTypeUndefined, attributeMapShared }, { StructElement::Figure, "Figure", elementTypeUndefined, attributeMapIllustration }, { StructElement::Formula, "Formula", elementTypeUndefined, attributeMapIllustration }, { StructElement::Form, "Form", elementTypeUndefined, attributeMapIllustration }, { StructElement::TOC, "TOC", elementTypeGrouping, attributeMapShared }, { StructElement::TOCI, "TOCI", elementTypeGrouping, attributeMapShared }, }; //------------------------------------------------------------------------ // Helpers for the attribute and structure type tables //------------------------------------------------------------------------ static inline const AttributeMapEntry *getAttributeMapEntry(const AttributeMapEntry **entryList, Attribute::Type type) { assert(entryList); while (*entryList) { const AttributeMapEntry *entry = *entryList; while (entry->type != Attribute::Unknown) { assert(entry->name); if (type == entry->type) { return entry; } entry++; } entryList++; } return nullptr; } static inline const AttributeMapEntry *getAttributeMapEntry(const AttributeMapEntry **entryList, const char *name) { assert(entryList); while (*entryList) { const AttributeMapEntry *entry = *entryList; while (entry->type != Attribute::Unknown) { assert(entry->name); if (strcmp(name, entry->name) == 0) { return entry; } entry++; } entryList++; } return nullptr; } static inline const OwnerMapEntry *getOwnerMapEntry(Attribute::Owner owner) { for (const OwnerMapEntry &entry : ownerMap) { if (owner == entry.owner) { return &entry; } } return nullptr; } static inline const OwnerMapEntry *getOwnerMapEntry(const char *name) { for (const OwnerMapEntry &entry : ownerMap) { if (strcmp(name, entry.name) == 0) { return &entry; } } return nullptr; } static const char *ownerToName(Attribute::Owner owner) { const OwnerMapEntry *entry = getOwnerMapEntry(owner); return entry ? entry->name : "UnknownOwner"; } static Attribute::Owner nameToOwner(const char *name) { const OwnerMapEntry *entry = getOwnerMapEntry(name); return entry ? entry->owner : Attribute::UnknownOwner; } static inline const TypeMapEntry *getTypeMapEntry(StructElement::Type type) { for (const TypeMapEntry &entry : typeMap) { if (type == entry.type) { return &entry; } } return nullptr; } static inline const TypeMapEntry *getTypeMapEntry(const char *name) { for (const TypeMapEntry &entry : typeMap) { if (strcmp(name, entry.name) == 0) { return &entry; } } return nullptr; } static const char *typeToName(StructElement::Type type) { if (type == StructElement::MCID) { return "MarkedContent"; } if (type == StructElement::OBJR) { return "ObjectReference"; } const TypeMapEntry *entry = getTypeMapEntry(type); return entry ? entry->name : "Unknown"; } static StructElement::Type nameToType(const char *name) { const TypeMapEntry *entry = getTypeMapEntry(name); return entry ? entry->type : StructElement::Unknown; } //------------------------------------------------------------------------ // Attribute //------------------------------------------------------------------------ Attribute::Attribute(GooString &&nameA, Object *valueA) : type(UserProperty), owner(UserProperties), revision(0), name(std::move(nameA)), value(), hidden(false), formatted(nullptr) { assert(valueA); value = valueA->copy(); } Attribute::Attribute(Type typeA, Object *valueA) : type(typeA), owner(UserProperties), // TODO: Determine corresponding owner from Type revision(0), name(), value(), hidden(false), formatted(nullptr) { assert(valueA); value = valueA->copy(); if (!checkType()) { type = Unknown; } } Attribute::~Attribute() { delete formatted; } const char *Attribute::getTypeName() const { if (type == UserProperty) { return name.c_str(); } const AttributeMapEntry *entry = getAttributeMapEntry(attributeMapAll, type); if (entry) { return entry->name; } return "Unknown"; } const char *Attribute::getOwnerName() const { return ownerToName(owner); } Object *Attribute::getDefaultValue(Attribute::Type type) { const AttributeMapEntry *entry = getAttributeMapEntry(attributeMapAll, type); return entry ? const_cast(entry->defval) : nullptr; } void Attribute::setFormattedValue(const char *formattedA) { if (formattedA) { if (formatted) { formatted->Set(formattedA); } else { formatted = new GooString(formattedA); } } else { delete formatted; formatted = nullptr; } } bool Attribute::checkType(StructElement *element) { // If an element is passed, tighter type-checking can be done. if (!element) { return true; } const TypeMapEntry *elementTypeEntry = getTypeMapEntry(element->getType()); if (elementTypeEntry && elementTypeEntry->attributes) { const AttributeMapEntry *entry = getAttributeMapEntry(elementTypeEntry->attributes, type); if (entry) { if (entry->check && !((*entry->check)(&value))) { return false; } } else { // No entry: the attribute is not valid for the containing element. return false; } } return true; } Attribute::Type Attribute::getTypeForName(const char *name, StructElement *element) { const AttributeMapEntry **attributes = attributeMapAll; if (element) { const TypeMapEntry *elementTypeEntry = getTypeMapEntry(element->getType()); if (elementTypeEntry && elementTypeEntry->attributes) { attributes = elementTypeEntry->attributes; } } const AttributeMapEntry *entry = getAttributeMapEntry(attributes, name); return entry ? entry->type : Unknown; } Attribute *Attribute::parseUserProperty(Dict *property) { Object obj, value; GooString name; obj = property->lookup("N"); if (obj.isString()) { name.Set(obj.getString()); } else if (obj.isName()) { name.Set(obj.getName()); } else { error(errSyntaxError, -1, "N object is wrong type ({0:s})", obj.getTypeName()); return nullptr; } value = property->lookup("V"); if (value.isNull()) { error(errSyntaxError, -1, "V object is wrong type ({0:s})", value.getTypeName()); return nullptr; } Attribute *attribute = new Attribute(std::move(name), &value); obj = property->lookup("F"); if (obj.isString()) { attribute->setFormattedValue(obj.getString()->c_str()); } else if (!obj.isNull()) { error(errSyntaxWarning, -1, "F object is wrong type ({0:s})", obj.getTypeName()); } obj = property->lookup("H"); if (obj.isBool()) { attribute->setHidden(obj.getBool()); } else if (!obj.isNull()) { error(errSyntaxWarning, -1, "H object is wrong type ({0:s})", obj.getTypeName()); } return attribute; } //------------------------------------------------------------------------ // StructElement //------------------------------------------------------------------------ StructElement::StructData::StructData() : altText(nullptr), actualText(nullptr), id(nullptr), title(nullptr), expandedAbbr(nullptr), language(nullptr), revision(0) { } StructElement::StructData::~StructData() { delete altText; delete actualText; delete id; delete title; delete language; for (StructElement *element : elements) { delete element; } for (Attribute *attribute : attributes) { delete attribute; } } StructElement::StructElement(Dict *element, StructTreeRoot *treeRootA, StructElement *parentA, RefRecursionChecker &seen) : type(Unknown), treeRoot(treeRootA), parent(parentA), s(new StructData()) { assert(treeRoot); assert(element); parse(element); parseChildren(element, seen); } StructElement::StructElement(int mcid, StructTreeRoot *treeRootA, StructElement *parentA) : type(MCID), treeRoot(treeRootA), parent(parentA), c(new ContentData(mcid)) { assert(treeRoot); assert(parent); } StructElement::StructElement(const Ref ref, StructTreeRoot *treeRootA, StructElement *parentA) : type(OBJR), treeRoot(treeRootA), parent(parentA), c(new ContentData(ref)) { assert(treeRoot); assert(parent); } StructElement::~StructElement() { if (isContent()) { delete c; } else { delete s; } } bool StructElement::isBlock() const { const TypeMapEntry *entry = getTypeMapEntry(type); return entry ? (entry->elementType == elementTypeBlock) : false; } bool StructElement::isInline() const { const TypeMapEntry *entry = getTypeMapEntry(type); return entry ? (entry->elementType == elementTypeInline) : false; } bool StructElement::isGrouping() const { const TypeMapEntry *entry = getTypeMapEntry(type); return entry ? (entry->elementType == elementTypeGrouping) : false; } bool StructElement::hasPageRef() const { return pageRef.isRef() || (parent && parent->hasPageRef()); } bool StructElement::getPageRef(Ref &ref) const { if (pageRef.isRef()) { ref = pageRef.getRef(); return true; } if (parent) { return parent->getPageRef(ref); } 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); } const Attribute *StructElement::findAttribute(Attribute::Type attributeType, bool inherit, Attribute::Owner attributeOwner) const { if (isContent()) { return parent->findAttribute(attributeType, inherit, attributeOwner); } if (attributeType == Attribute::Unknown || attributeType == Attribute::UserProperty) { return nullptr; } const Attribute *result = nullptr; if (attributeOwner == Attribute::UnknownOwner) { // Search for the attribute, no matter who the owner is for (unsigned i = 0; i < getNumAttributes(); i++) { const Attribute *attr = getAttribute(i); if (attributeType == attr->getType()) { if (!result || ownerHasMorePriority(attr->getOwner(), result->getOwner())) { result = attr; } } } } else { // Search for the attribute, with a specific owner for (unsigned i = 0; i < getNumAttributes(); i++) { const Attribute *attr = getAttribute(i); if (attributeType == attr->getType() && attributeOwner == attr->getOwner()) { result = attr; break; } } } if (result) { return result; } if (inherit && parent) { const AttributeMapEntry *entry = getAttributeMapEntry(attributeMapAll, attributeType); assert(entry); // TODO: Take into account special inheritance cases, for example: // inline elements which have been changed to be block using // "/Placement/Block" have slightly different rules. if (entry->inherit) { return parent->findAttribute(attributeType, inherit, attributeOwner); } } return nullptr; } GooString *StructElement::appendSubTreeText(GooString *string, bool recursive) const { if (isContent() && !isObjectRef()) { MarkedContentOutputDev mcdev(getMCID(), stmRef); const TextSpanArray &spans(getTextSpansInternal(mcdev)); if (!string) { string = new GooString(); } for (const TextSpan &span : spans) { string->append(span.getText()); } return string; } if (!recursive) { return nullptr; } // Do a depth-first traversal, to get elements in logical order if (!string) { string = new GooString(); } for (unsigned i = 0; i < getNumChildren(); i++) { getChild(i)->appendSubTreeText(string, recursive); } return string; } const TextSpanArray &StructElement::getTextSpansInternal(MarkedContentOutputDev &mcdev) const { assert(isContent()); int startPage = 0, endPage = 0; Ref ref; if (getPageRef(ref)) { startPage = endPage = treeRoot->getDoc()->findPage(ref); } if (!(startPage && endPage)) { startPage = 1; endPage = treeRoot->getDoc()->getNumPages(); } treeRoot->getDoc()->displayPages(&mcdev, startPage, endPage, 72.0, 72.0, 0, true, false, false); return mcdev.getTextSpans(); } static StructElement::Type roleMapResolve(Dict *roleMap, const char *name, const char *curName) { // Circular reference if (curName && !strcmp(name, curName)) { return StructElement::Unknown; } Object resolved = roleMap->lookup(curName ? curName : name); if (resolved.isName()) { StructElement::Type type = nameToType(resolved.getName()); return type == StructElement::Unknown ? roleMapResolve(roleMap, name, resolved.getName()) : type; } if (!resolved.isNull()) { error(errSyntaxWarning, -1, "RoleMap entry is wrong type ({0:s})", resolved.getTypeName()); } return StructElement::Unknown; } void StructElement::parse(Dict *element) { Object obj; // Type is optional, but if present must be StructElem obj = element->lookup("Type"); if (!obj.isNull() && !obj.isName("StructElem")) { error(errSyntaxError, -1, "Type of StructElem object is wrong"); return; } // Parent object reference (required). const Object &objP = element->lookupNF("P"); if (!objP.isRef()) { error(errSyntaxError, -1, "P object is wrong type ({0:s})", obj.getTypeName()); return; } s->parentRef = objP.getRef(); // Check whether the S-type is valid for the top level // element and create a node of the appropriate type. obj = element->lookup("S"); if (!obj.isName()) { error(errSyntaxError, -1, "S object is wrong type ({0:s})", obj.getTypeName()); return; } // Type name may not be standard, resolve through RoleMap first. if (treeRoot->getRoleMap()) { type = roleMapResolve(treeRoot->getRoleMap(), obj.getName(), nullptr); } // Resolving through RoleMap may leave type as Unknown, e.g. for types // which are not present in it, yet they are standard element types. if (type == Unknown) { type = nameToType(obj.getName()); } // At this point either the type name must have been resolved. if (type == Unknown) { error(errSyntaxError, -1, "StructElem object is wrong type ({0:s})", obj.getName()); return; } // Object ID (optional), to be looked at the IDTree in the tree root. obj = element->lookup("ID"); if (obj.isString()) { s->id = obj.getString()->copy(); } // Page reference (optional) in which at least one of the child items // is to be rendered in. Note: each element stores only the /Pg value // contained by it, and StructElement::getPageRef() may look in parent // elements to find the page where an element belongs. pageRef = element->lookupNF("Pg").copy(); // Revision number (optional). obj = element->lookup("R"); if (obj.isInt()) { s->revision = obj.getInt(); } // Element title (optional). obj = element->lookup("T"); if (obj.isString()) { s->title = obj.getString()->copy(); } // Language (optional). obj = element->lookup("Lang"); if (obj.isString()) { s->language = obj.getString()->copy(); } // Alternative text (optional). obj = element->lookup("Alt"); if (obj.isString()) { s->altText = obj.getString()->copy(); } // Expanded form of an abbreviation (optional). obj = element->lookup("E"); if (obj.isString()) { s->expandedAbbr = obj.getString()->copy(); } // Actual text (optional). obj = element->lookup("ActualText"); if (obj.isString()) { s->actualText = obj.getString()->copy(); } // Attributes directly attached to the element (optional). obj = element->lookup("A"); if (obj.isDict()) { parseAttributes(obj.getDict()); } else if (obj.isArray()) { unsigned attrIndex = getNumAttributes(); for (int i = 0; i < obj.arrayGetLength(); i++) { Object iobj = obj.arrayGet(i); if (iobj.isDict()) { attrIndex = getNumAttributes(); parseAttributes(iobj.getDict()); } else if (iobj.isInt()) { const int revision = iobj.getInt(); // Set revision numbers for the elements previously created. for (unsigned j = attrIndex; j < getNumAttributes(); j++) { getAttribute(j)->setRevision(revision); } } else { error(errSyntaxWarning, -1, "A item is wrong type ({0:s})", iobj.getTypeName()); } } } else if (!obj.isNull()) { error(errSyntaxWarning, -1, "A is wrong type ({0:s})", obj.getTypeName()); } // Attributes referenced indirectly through the ClassMap (optional). if (treeRoot->getClassMap()) { Object classes = element->lookup("C"); if (classes.isName()) { Object attr = treeRoot->getClassMap()->lookup(classes.getName()); if (attr.isDict()) { parseAttributes(attr.getDict(), true); } else if (attr.isArray()) { for (int i = 0; i < attr.arrayGetLength(); i++) { unsigned attrIndex = getNumAttributes(); Object iobj = attr.arrayGet(i); if (iobj.isDict()) { attrIndex = getNumAttributes(); parseAttributes(iobj.getDict(), true); } else if (iobj.isInt()) { // Set revision numbers for the elements previously created. const int revision = iobj.getInt(); for (unsigned j = attrIndex; j < getNumAttributes(); j++) { getAttribute(j)->setRevision(revision); } } else { error(errSyntaxWarning, -1, "C item is wrong type ({0:s})", iobj.getTypeName()); } } } else if (!attr.isNull()) { error(errSyntaxWarning, -1, "C object is wrong type ({0:s})", classes.getTypeName()); } } } } StructElement *StructElement::parseChild(const Object *ref, Object *childObj, RefRecursionChecker &seen) { assert(childObj); assert(ref); StructElement *child = nullptr; if (childObj->isInt()) { child = new StructElement(childObj->getInt(), treeRoot, this); } else if (childObj->isDict("MCR")) { /* * TODO: The optional StmOwn attribute is not handled. */ Object mcidObj = childObj->dictLookup("MCID"); if (!mcidObj.isInt()) { error(errSyntaxError, -1, "MCID object is wrong type ({0:s})", mcidObj.getTypeName()); return nullptr; } child = new StructElement(mcidObj.getInt(), treeRoot, this); Object pageRefObj = childObj->dictLookupNF("Pg").copy(); if (pageRefObj.isRef()) { child->pageRef = std::move(pageRefObj); } const Object &stmObj = childObj->dictLookupNF("Stm"); if (stmObj.isRef()) { child->stmRef = stmObj.copy(); } else if (!stmObj.isNull()) { error(errSyntaxError, -1, "Stm object is wrong type ({0:s})", stmObj.getTypeName()); delete child; return nullptr; } } else if (childObj->isDict("OBJR")) { const Object &refObj = childObj->dictLookupNF("Obj"); if (refObj.isRef()) { child = new StructElement(refObj.getRef(), treeRoot, this); Object pageRefObj = childObj->dictLookupNF("Pg").copy(); if (pageRefObj.isRef()) { child->pageRef = std::move(pageRefObj); } } else { error(errSyntaxError, -1, "Obj object is wrong type ({0:s})", refObj.getTypeName()); } } 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.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()); } } else { error(errSyntaxWarning, -1, "K has a child of wrong type ({0:s})", childObj->getTypeName()); } if (child) { if (child->isOk()) { appendChild(child); if (ref->isRef()) { treeRoot->parentTreeAdd(ref->getRef(), child); } } else { delete child; child = nullptr; } } return child; } void StructElement::parseChildren(Dict *element, RefRecursionChecker &seen) { Object kids = element->lookup("K"); if (kids.isArray()) { for (int i = 0; i < kids.arrayGetLength(); i++) { Object obj = kids.arrayGet(i); const Object &ref = kids.arrayGetNF(i); parseChild(&ref, &obj, seen); } } else if (kids.isDict() || kids.isInt()) { const Object &ref = element->lookupNF("K"); parseChild(&ref, &kids, seen); } } void StructElement::parseAttributes(Dict *attributes, bool keepExisting) { Object owner = attributes->lookup("O"); if (owner.isName("UserProperties")) { // In this case /P is an array of UserProperty dictionaries Object userProperties = attributes->lookup("P"); if (userProperties.isArray()) { for (int i = 0; i < userProperties.arrayGetLength(); i++) { Object property = userProperties.arrayGet(i); if (property.isDict()) { Attribute *attribute = Attribute::parseUserProperty(property.getDict()); if (attribute && attribute->isOk()) { appendAttribute(attribute); } else { error(errSyntaxWarning, -1, "Item in P is invalid"); delete attribute; } } else { error(errSyntaxWarning, -1, "Item in P is wrong type ({0:s})", property.getTypeName()); } } } } else if (owner.isName()) { // In this case /P contains standard attributes. // Check first if the owner is a valid standard one. Attribute::Owner ownerValue = nameToOwner(owner.getName()); if (ownerValue != Attribute::UnknownOwner) { // Iterate over the entries of the "attributes" dictionary. // The /O entry (owner) is skipped. for (int i = 0; i < attributes->getLength(); i++) { const char *key = attributes->getKey(i); if (strcmp(key, "O") != 0) { Attribute::Type t = Attribute::getTypeForName(key, this); // Check if the attribute is already defined. if (keepExisting) { bool exists = false; for (unsigned j = 0; j < getNumAttributes(); j++) { if (getAttribute(j)->getType() == t) { exists = true; break; } } if (exists) { continue; } } if (t != Attribute::Unknown) { Object value = attributes->getVal(i); bool typeCheckOk = true; Attribute *attribute = new Attribute(t, &value); if (attribute->isOk() && (typeCheckOk = attribute->checkType(this))) { appendAttribute(attribute); } else { // It is not needed to free "value", the Attribute instance // owns the contents, so deleting "attribute" is enough. if (!typeCheckOk) { error(errSyntaxWarning, -1, "Attribute {0:s} value is of wrong type ({1:s})", attribute->getTypeName(), attribute->getValue()->getTypeName()); } delete attribute; } } else { error(errSyntaxWarning, -1, "Wrong Attribute '{0:s}' in element {1:s}", key, getTypeName()); } } } } else { error(errSyntaxWarning, -1, "O object is invalid value ({0:s})", owner.getName()); } } else if (!owner.isNull()) { error(errSyntaxWarning, -1, "O is wrong type ({0:s})", owner.getTypeName()); } } poppler-24.02.0/poppler/StructElement.h000066400000000000000000000273051455701731300200050ustar00rootroot00000000000000//======================================================================== // // StructElement.h // // This file is licensed under the GPLv2 or later // // Copyright 2013, 2014 Igalia S.L. // Copyright 2014 Luigi Scarso // Copyright 2014, 2018, 2019, 2021, 2023 Albert Astals Cid // Copyright 2018 Adam Reichold // Copyright 2021, 2023 Adrian Johnson // //======================================================================== #ifndef STRUCTELEMENT_H #define STRUCTELEMENT_H #include "goo/GooString.h" #include "MarkedContentOutputDev.h" #include "Object.h" #include "poppler_private_export.h" #include #include class GooString; class Dict; class StructElement; class StructTreeRoot; class POPPLER_PRIVATE_EXPORT Attribute { public: enum Type { Unknown = 0, // Uninitialized, parsing error, etc. UserProperty, // User defined attribute (i.e. non-standard) // Common standard attributes Placement, WritingMode, BackgroundColor, BorderColor, BorderStyle, BorderThickness, Color, Padding, // Block element standard attributes SpaceBefore, SpaceAfter, StartIndent, EndIndent, TextIndent, TextAlign, BBox, Width, Height, BlockAlign, InlineAlign, TBorderStyle, TPadding, // Inline element standard attributes BaselineShift, LineHeight, TextDecorationColor, TextDecorationThickness, TextDecorationType, RubyAlign, RubyPosition, GlyphOrientationVertical, // Column-only standard attributes ColumnCount, ColumnGap, ColumnWidths, // List-only standard attributes ListNumbering, // PrintField-only standard attributes Role, checked, Desc, // Table-only standard attributes RowSpan, ColSpan, Headers, Scope, Summary, }; enum Owner { UnknownOwner = 0, // User-defined attributes UserProperties, // Standard attributes Layout, List, PrintField, Table, // Translation to other formats XML_1_00, HTML_3_20, HTML_4_01, OEB_1_00, RTF_1_05, CSS_1_00, CSS_2_00, }; // Creates a standard attribute. The name is predefined, and the // value is type-checked to conform to the PDF specification. Attribute(Type type, Object *value); // Creates an UserProperty attribute, with an arbitrary name and value. Attribute(GooString &&name, Object *value); bool isOk() const { return type != Unknown; } // Name, type and value can be set only on construction. Type getType() const { return type; } Owner getOwner() const { return owner; } const char *getTypeName() const; const char *getOwnerName() const; 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()); } // The revision is optional, and defaults to zero. unsigned int getRevision() const { return revision; } void setRevision(unsigned int revisionA) { revision = revisionA; } // Hidden elements should not be displayed by the user agent bool isHidden() const { return hidden; } void setHidden(bool hiddenA) { hidden = hiddenA; } // The formatted value may be in the PDF, or be left undefined (nullptr). // In the later case the user agent should provide a default representation. const char *getFormattedValue() const { return formatted ? formatted->c_str() : nullptr; } void setFormattedValue(const char *formattedA); ~Attribute(); private: Type type; Owner owner; unsigned int revision; GooString name; Object value; bool hidden; GooString *formatted; bool checkType(StructElement *element = nullptr); static Type getTypeForName(const char *name, StructElement *element = nullptr); static Attribute *parseUserProperty(Dict *property); friend class StructElement; }; class POPPLER_PRIVATE_EXPORT StructElement { public: enum Type { Unknown = 0, MCID, // MCID reference, used internally OBJR, // Object reference, used internally Document, Part, Art, Sect, Div, // Structural elements Span, Quote, Note, Reference, BibEntry, // Inline elements Code, Link, Annot, BlockQuote, Caption, NonStruct, TOC, TOCI, Index, Private, P, H, H1, H2, H3, H4, H5, H6, // Paragraph-like L, LI, Lbl, LBody, // List elements Table, TR, TH, TD, THead, TFoot, TBody, // Table elements Ruby, RB, RT, RP, // Ruby text elements Warichu, WT, WP, Figure, Formula, Form, // Illustration-like elements }; static const Ref InvalidRef; const char *getTypeName() const; Type getType() const { return type; } bool isOk() const { return type != Unknown; } bool isBlock() const; bool isInline() const; bool isGrouping() const; inline bool isContent() const { return (type == MCID) || isObjectRef(); } inline bool isObjectRef() const { return (type == OBJR && c->ref != Ref::INVALID()); } int getMCID() const { return c->mcid; } Ref getObjectRef() const { return c->ref; } 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; } // Optional ISO language name, e.g. en_US GooString *getLanguage() { if (!isContent() && s->language) { return s->language; } return parent ? parent->getLanguage() : nullptr; } const GooString *getLanguage() const { if (!isContent() && s->language) { return s->language; } return parent ? parent->getLanguage() : nullptr; } // Optional revision number, defaults to zero. unsigned int getRevision() const { return isContent() ? 0 : s->revision; } void setRevision(unsigned int revision) { if (isContent()) { s->revision = revision; } } // Optional element title, in human-readable form. const GooString *getTitle() const { return isContent() ? nullptr : s->title; } GooString *getTitle() { return isContent() ? nullptr : s->title; } // Optional element expanded abbreviation text. const GooString *getExpandedAbbr() const { return isContent() ? nullptr : s->expandedAbbr; } GooString *getExpandedAbbr() { return isContent() ? nullptr : s->expandedAbbr; } unsigned getNumChildren() const { return isContent() ? 0 : s->elements.size(); } const StructElement *getChild(int i) const { return isContent() ? nullptr : s->elements.at(i); } StructElement *getChild(int i) { return isContent() ? nullptr : s->elements.at(i); } void appendChild(StructElement *element) { if (!isContent() && element && element->isOk()) { s->elements.push_back(element); } } unsigned getNumAttributes() const { return isContent() ? 0 : s->attributes.size(); } const Attribute *getAttribute(int i) const { return isContent() ? nullptr : s->attributes.at(i); } Attribute *getAttribute(int i) { return isContent() ? nullptr : s->attributes.at(i); } void appendAttribute(Attribute *attribute) { if (!isContent() && attribute) { s->attributes.push_back(attribute); } } 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 *getActualText() const { return isContent() ? nullptr : s->actualText; } GooString *getActualText() { return isContent() ? nullptr : s->actualText; } // Content text referenced by the element: // // - For MCID reference elements, this is just the text of the // corresponding marked content object in the page stream, regardless // of the setting of the "recursive" flag. // - For other elements, if the "recursive" flag is set, the text // enclosed by *all* the child MCID reference elements of the subtree // is returned. The text is assembled by traversing the leaf MCID // reference elements in logical order. // - In any other case, the function returns nullptr. // // A new string is returned, and the ownership passed to the caller. // GooString *getText(bool recursive = true) const { return appendSubTreeText(nullptr, recursive); } const TextSpanArray getTextSpans() const { if (!isContent()) { return TextSpanArray(); } MarkedContentOutputDev mcdev(getMCID(), stmRef); return getTextSpansInternal(mcdev); } ~StructElement(); private: GooString *appendSubTreeText(GooString *string, bool recursive) const; const TextSpanArray &getTextSpansInternal(MarkedContentOutputDev &mcdev) const; typedef std::vector AttrPtrArray; typedef std::vector ElemPtrArray; struct StructData { Ref parentRef; GooString *altText; GooString *actualText; GooString *id; GooString *title; GooString *expandedAbbr; GooString *language; unsigned int revision; ElemPtrArray elements; AttrPtrArray attributes; StructData(); ~StructData(); StructData(const StructData &) = delete; StructData &operator=(const StructData &) = delete; }; // Data in content elements (MCID, MCR) struct ContentData { union { int mcid; Ref ref; }; explicit ContentData(int mcidA) : mcid(mcidA) { } explicit ContentData(const Ref r) { ref = r; } }; // Common data Type type; StructTreeRoot *treeRoot; StructElement *parent; mutable Object pageRef; Object stmRef; union { StructData *s; ContentData *c; }; 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, RefRecursionChecker &seen); void parseChildren(Dict *element, RefRecursionChecker &seen); void parseAttributes(Dict *attributes, bool keepExisting = false); friend class StructTreeRoot; }; #endif poppler-24.02.0/poppler/StructTreeRoot.cc000066400000000000000000000162311455701731300203110ustar00rootroot00000000000000//======================================================================== // // StructTreeRoot.cc // // This file is licensed under the GPLv2 or later // // Copyright 2013, 2014 Igalia S.L. // Copyright 2014 Fabio D'Urso // Copyright 2017 Jan-Erik S // Copyright 2017-2019, 2023 Albert Astals Cid // Copyright 2017, 2018 Adrian Johnson // Copyright 2018, Adam Reichold // //======================================================================== #include "goo/GooString.h" #include "StructTreeRoot.h" #include "StructElement.h" #include "PDFDoc.h" #include "Object.h" #include "Dict.h" #include #include StructTreeRoot::StructTreeRoot(PDFDoc *docA, Dict *structTreeRootDict) : doc(docA) { assert(doc); assert(structTreeRootDict); parse(structTreeRootDict); } StructTreeRoot::~StructTreeRoot() { for (StructElement *element : elements) { delete element; } } void StructTreeRoot::parse(Dict *root) { // The RoleMap/ClassMap dictionaries are needed by all the parsing // functions, which will resolve the custom names to canonical // standard names. roleMap = root->lookup("RoleMap"); classMap = root->lookup("ClassMap"); // ParentTree (optional). If present, it must be a number tree, // otherwise it is not possible to map stream objects to their // corresponding structure element. Here only the references are // loaded into the array, the pointers to the StructElements will // be filled-in later when parsing them. const Object parentTreeObj = root->lookup("ParentTree"); if (parentTreeObj.isDict()) { parseNumberTreeNode(parentTreeObj.getDict()); } RefRecursionChecker seenElements; // Parse the children StructElements const bool marked = doc->getCatalog()->getMarkInfo() & Catalog::markInfoMarked; Object kids = root->lookup("K"); if (kids.isArray()) { if (marked && kids.arrayGetLength() > 1) { error(errSyntaxWarning, -1, "K in StructTreeRoot has more than one children in a tagged PDF"); } for (int i = 0; i < kids.arrayGetLength(); i++) { const Object &ref = kids.arrayGetNF(i); if (ref.isRef()) { seenElements.insert(ref.getRef()); } Object obj = kids.arrayGet(i); if (obj.isDict()) { StructElement *child = new StructElement(obj.getDict(), this, nullptr, seenElements); if (child->isOk()) { if (marked && !(child->getType() == StructElement::Document || child->getType() == StructElement::Part || child->getType() == StructElement::Art || child->getType() == StructElement::Div)) { error(errSyntaxWarning, -1, "StructTreeRoot element of tagged PDF is wrong type ({0:s})", child->getTypeName()); } appendChild(child); if (ref.isRef()) { parentTreeAdd(ref.getRef(), child); } } else { error(errSyntaxWarning, -1, "StructTreeRoot element could not be parsed"); delete child; } } else { error(errSyntaxWarning, -1, "K has a child of wrong type ({0:s})", obj.getTypeName()); } } } else if (kids.isDict()) { StructElement *child = new StructElement(kids.getDict(), this, nullptr, seenElements); if (child->isOk()) { appendChild(child); const Object &ref = root->lookupNF("K"); if (ref.isRef()) { parentTreeAdd(ref.getRef(), child); } } else { error(errSyntaxWarning, -1, "StructTreeRoot element could not be parsed"); delete child; } } else if (!kids.isNull()) { error(errSyntaxWarning, -1, "K in StructTreeRoot is wrong type ({0:s})", kids.getTypeName()); } // refToParentMap is only used during parsing. Ensure all memory used by it is freed. std::multimap().swap(refToParentMap); } void StructTreeRoot::parseNumberTreeNode(Dict *node) { Object kids = node->lookup("Kids"); if (kids.isArray()) { for (int i = 0; i < kids.arrayGetLength(); i++) { Object obj = kids.arrayGet(i); if (obj.isDict()) { parseNumberTreeNode(obj.getDict()); } else { error(errSyntaxError, -1, "Kids item at position {0:d} is wrong type ({1:s})", i, obj.getTypeName()); } } return; } else if (!kids.isNull()) { error(errSyntaxError, -1, "Kids object is wrong type ({0:s})", kids.getTypeName()); } Object nums = node->lookup("Nums"); if (nums.isArray()) { if (nums.arrayGetLength() % 2 == 0) { // keys in even positions, references in odd ones for (int i = 0; i < nums.arrayGetLength(); i += 2) { Object key = nums.arrayGet(i); if (!key.isInt()) { error(errSyntaxError, -1, "Nums item at position {0:d} is wrong type ({1:s})", i, key.getTypeName()); continue; } int keyVal = key.getInt(); std::vector &vec = parentTree[keyVal]; Object valueArray = nums.arrayGet(i + 1); if (valueArray.isArray()) { vec.resize(valueArray.arrayGetLength()); for (int j = 0; j < valueArray.arrayGetLength(); j++) { const Object &itemvalue = valueArray.arrayGetNF(j); if (itemvalue.isRef()) { Ref ref = itemvalue.getRef(); vec[j].ref = ref; refToParentMap.insert(std::pair(ref, &vec[j])); } else if (!itemvalue.isNull()) { error(errSyntaxError, -1, "Nums array item at position {0:d}/{1:d} is invalid type ({2:s})", i, j, itemvalue.getTypeName()); } } } else { const Object &valueRef = nums.arrayGetNF(i + 1); if (valueRef.isRef()) { Ref ref = valueRef.getRef(); vec.resize(1); vec[0].ref = ref; refToParentMap.insert(std::pair(ref, &vec[0])); } else { error(errSyntaxError, -1, "Nums item at position {0:d} is wrong type ({1:s})", i + 1, valueRef.getTypeName()); } } } } else { error(errSyntaxError, -1, "Nums array length is not a even ({0:d})", nums.arrayGetLength()); } } else { error(errSyntaxError, -1, "Nums object is wrong type ({0:s})", nums.getTypeName()); } } void StructTreeRoot::parentTreeAdd(const Ref objectRef, StructElement *element) { auto range = refToParentMap.equal_range(objectRef); for (auto it = range.first; it != range.second; ++it) { it->second->element = element; } } poppler-24.02.0/poppler/StructTreeRoot.h000066400000000000000000000047271455701731300201620ustar00rootroot00000000000000//======================================================================== // // StructTreeRoot.h // // This file is licensed under the GPLv2 or later // // Copyright 2013, 2014 Igalia S.L. // Copyright 2018, 2019 Albert Astals Cid // Copyright 2018 Adrian Johnson // Copyright 2018 Adam Reichold // //======================================================================== #ifndef STRUCTTREEROOT_H #define STRUCTTREEROOT_H #include "Object.h" #include "StructElement.h" #include #include class Dict; class PDFDoc; class StructTreeRoot { public: StructTreeRoot(PDFDoc *docA, Dict *rootDict); ~StructTreeRoot(); StructTreeRoot &operator=(const StructTreeRoot &) = delete; StructTreeRoot(const StructTreeRoot &) = delete; PDFDoc *getDoc() { return doc; } Dict *getRoleMap() { return roleMap.isDict() ? roleMap.getDict() : nullptr; } Dict *getClassMap() { return classMap.isDict() ? classMap.getDict() : nullptr; } unsigned getNumChildren() const { return elements.size(); } const StructElement *getChild(int i) const { return elements.at(i); } StructElement *getChild(int i) { return elements.at(i); } void appendChild(StructElement *element) { if (element && element->isOk()) { elements.push_back(element); } } const StructElement *findParentElement(int key, unsigned mcid = 0) const { auto it = parentTree.find(key); if (it != parentTree.end()) { if (mcid < it->second.size()) { return it->second[mcid].element; } } return nullptr; } private: typedef std::vector ElemPtrArray; // Structure for items in /ParentTree, it keeps a mapping of // 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() { } }; PDFDoc *doc; Object roleMap; Object classMap; ElemPtrArray elements; std::map> parentTree; std::multimap refToParentMap; void parse(Dict *rootDict); void parseNumberTreeNode(Dict *node); void parentTreeAdd(const Ref objectRef, StructElement *element); friend class StructElement; }; #endif poppler-24.02.0/poppler/SymbolWidths.gperf000066400000000000000000000054251455701731300205120ustar00rootroot00000000000000%{ #include #include "BuiltinFontWidth.h" %} %language=ANSI-C %define initializer-suffix ,0 %define lookup-function-name SymbolWidthsLookup %struct-type %omit-struct-type %readonly-tables struct BuiltinFontWidth %% #### bracketleftex, 384 alpha, 631 union, 768 infinity, 713 comma, 250 copyrightsans, 790 plusminus, 549 arrowup, 603 apple, 790 parenleftbt, 384 notelement, 713 colon, 278 beta, 549 braceleftbt, 494 Lambda, 686 Phi, 763 minus, 549 space, 250 Sigma, 592 approxequal, 549 minute, 247 circleplus, 768 Omicron, 722 three, 500 numbersign, 500 lambda, 549 phi, 521 aleph, 823 Tau, 611 spade, 753 logicaland, 603 sigma, 603 propersuperset, 713 omicron, 549 question, 444 equal, 549 Epsilon, 611 emptyset, 823 diamond, 753 four, 500 Mu, 889 parenlefttp, 384 club, 753 bullet, 460 Omega, 768 tau, 439 Upsilon, 690 bracelefttp, 494 heart, 753 divide, 549 epsilon, 439 logicalor, 603 parenleftex, 384 greaterequal, 549 mu, 576 Nu, 722 therefore, 863 notsubset, 713 omega, 686 semicolon, 278 element, 713 upsilon, 576 existential, 549 integralbt, 686 lessequal, 549 phi1, 603 lozenge, 494 trademarkserif, 890 parenright, 333 reflexsuperset, 713 sigma1, 439 nu, 521 Gamma, 603 angleright, 329 ellipsis, 1000 Rho, 556 parenrightbt, 384 radicalex, 500 eight, 500 angleleft, 329 arrowdbldown, 603 congruent, 549 Theta, 741 intersection, 768 Pi, 768 slash, 278 registerserif, 790 parenleft, 333 one, 500 gamma, 411 bracketleft, 333 rho, 549 circlemultiply, 768 Chi, 722 theta, 521 pi, 549 integraltp, 686 Eta, 722 product, 823 nine, 500 five, 500 propersubset, 713 bracketrightbt, 384 trademarksans, 786 dotmath, 250 integralex, 686 chi, 549 parenrighttp, 384 eta, 603 underscore, 500 Euro, 750 multiply, 549 zero, 500 partialdiff, 494 angle, 768 arrowdblleft, 987 braceleft, 480 parenrightex, 384 Rfraktur, 795 Zeta, 611 braceex, 494 arrowdblup, 603 arrowdown, 603 Ifraktur, 686 degree, 400 Iota, 333 perpendicular, 658 radical, 549 asteriskmath, 500 percent, 833 zeta, 494 six, 500 two, 500 weierstrass, 987 summation, 713 bracketrighttp, 384 carriagereturn, 658 suchthat, 439 arrowvertex, 603 Delta, 612 iota, 329 arrowhorizex, 1000 bracketrightex, 384 bracketright, 333 ampersand, 778 plus, 549 proportional, 713 delta, 494 copyrightserif, 790 bracerightmid, 494 arrowleft, 987 second, 411 arrowdblboth, 1042 florin, 500 Psi, 795 bracerightbt, 494 bracketleftbt, 384 seven, 500 braceleftmid, 494 notequal, 549 psi, 686 equivalence, 549 universal, 713 arrowdblright, 987 braceright, 480 reflexsubset, 713 Xi, 645 theta1, 631 logicalnot, 713 Kappa, 722 similar, 549 bar, 200 fraction, 167 less, 549 registersans, 790 omega1, 713 exclam, 333 Upsilon1, 620 bracerighttp, 494 xi, 493 period, 250 Alpha, 722 arrowright, 987 greater, 549 bracketlefttp, 384 kappa, 549 gradient, 713 integral, 274 arrowboth, 1042 Beta, 667 #### %% poppler-24.02.0/poppler/SymbolWidths.pregenerated.c000066400000000000000000001205111455701731300222670ustar00rootroot00000000000000/* ANSI-C code produced by gperf version 3.1 */ /* Command-line: gperf poppler/SymbolWidths.gperf */ /* Computed positions: -k'1-2,4-5,$' */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) && ('-' == 45) && ('.' == 46) && ('/' == 47) \ && ('0' == 48) && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) && ('=' == 61) && ('>' == 62) \ && ('?' == 63) && ('A' == 65) && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) && ('N' == 78) \ && ('O' == 79) && ('P' == 80) && ('Q' == 81) && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) && ('k' == 107) && ('l' == 108) \ && ('m' == 109) && ('n' == 110) && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) /* The character set is not based on ISO-646. */ # error "gperf generated tables don't work with this execution character set. Please report a bug to ." #endif #line 1 "poppler/SymbolWidths.gperf" #include #include "BuiltinFontWidth.h" #define TOTAL_KEYWORDS 190 #define MIN_WORD_LENGTH 2 #define MAX_WORD_LENGTH 14 #define MIN_HASH_VALUE 5 #define MAX_HASH_VALUE 406 /* maximum key range = 402, duplicates = 0 */ #ifdef __GNUC__ __inline #else # ifdef __cplusplus inline # endif #endif static unsigned int hash(register const char *str, register size_t len) { static const unsigned short asso_values[] = { 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 80, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 60, 10, 5, 0, 45, 407, 25, 407, 25, 407, 0, 5, 30, 25, 15, 80, 407, 135, 5, 65, 70, 407, 407, 5, 407, 5, 407, 407, 407, 407, 407, 407, 0, 45, 100, 90, 15, 45, 145, 70, 5, 407, 30, 20, 105, 10, 70, 0, 50, 0, 25, 5, 145, 0, 220, 35, 0, 0, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407 }; register unsigned int hval = len; switch (hval) { default: hval += asso_values[(unsigned char)str[4]]; /*FALLTHROUGH*/ case 4: hval += asso_values[(unsigned char)str[3]]; /*FALLTHROUGH*/ case 3: case 2: hval += asso_values[(unsigned char)str[1]]; /*FALLTHROUGH*/ case 1: hval += asso_values[(unsigned char)str[0]]; break; } return hval + asso_values[(unsigned char)str[len - 1]]; } const struct BuiltinFontWidth *SymbolWidthsLookup(register const char *str, register size_t len) { static const struct BuiltinFontWidth wordlist[] = { { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 183 "poppler/SymbolWidths.gperf" { "Kappa", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 109 "poppler/SymbolWidths.gperf" { "pi", 549 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 180 "poppler/SymbolWidths.gperf" { "Xi", 645 }, { "", 0 }, #line 144 "poppler/SymbolWidths.gperf" { "zeta", 494 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 122 "poppler/SymbolWidths.gperf" { "eta", 603 }, #line 133 "poppler/SymbolWidths.gperf" { "Zeta", 611 }, #line 153 "poppler/SymbolWidths.gperf" { "Delta", 612 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 203 "poppler/SymbolWidths.gperf" { "Beta", 667 }, { "", 0 }, { "", 0 }, #line 115 "poppler/SymbolWidths.gperf" { "propersubset", 713 }, #line 174 "poppler/SymbolWidths.gperf" { "psi", 686 }, #line 46 "poppler/SymbolWidths.gperf" { "propersuperset", 713 }, #line 199 "poppler/SymbolWidths.gperf" { "kappa", 549 }, #line 55 "poppler/SymbolWidths.gperf" { "parenlefttp", 384 }, #line 121 "poppler/SymbolWidths.gperf" { "parenrighttp", 384 }, #line 17 "poppler/SymbolWidths.gperf" { "infinity", 713 }, #line 101 "poppler/SymbolWidths.gperf" { "parenleft", 333 }, #line 82 "poppler/SymbolWidths.gperf" { "parenright", 333 }, #line 23 "poppler/SymbolWidths.gperf" { "parenleftbt", 384 }, #line 90 "poppler/SymbolWidths.gperf" { "parenrightbt", 384 }, #line 140 "poppler/SymbolWidths.gperf" { "perpendicular", 658 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 193 "poppler/SymbolWidths.gperf" { "xi", 493 }, #line 185 "poppler/SymbolWidths.gperf" { "bar", 200 }, #line 113 "poppler/SymbolWidths.gperf" { "nine", 500 }, { "", 0 }, { "", 0 }, #line 97 "poppler/SymbolWidths.gperf" { "intersection", 768 }, #line 111 "poppler/SymbolWidths.gperf" { "Eta", 722 }, { "", 0 }, #line 22 "poppler/SymbolWidths.gperf" { "apple", 790 }, { "", 0 }, #line 64 "poppler/SymbolWidths.gperf" { "epsilon", 439 }, { "", 0 }, #line 93 "poppler/SymbolWidths.gperf" { "angleleft", 329 }, #line 87 "poppler/SymbolWidths.gperf" { "angleright", 329 }, { "", 0 }, #line 184 "poppler/SymbolWidths.gperf" { "similar", 549 }, { "", 0 }, #line 26 "poppler/SymbolWidths.gperf" { "beta", 549 }, #line 128 "poppler/SymbolWidths.gperf" { "angle", 768 }, #line 127 "poppler/SymbolWidths.gperf" { "partialdiff", 494 }, #line 179 "poppler/SymbolWidths.gperf" { "reflexsubset", 713 }, #line 145 "poppler/SymbolWidths.gperf" { "six", 500 }, #line 83 "poppler/SymbolWidths.gperf" { "reflexsuperset", 713 }, { "", 0 }, #line 66 "poppler/SymbolWidths.gperf" { "parenleftex", 384 }, #line 131 "poppler/SymbolWidths.gperf" { "parenrightex", 384 }, #line 88 "poppler/SymbolWidths.gperf" { "ellipsis", 1000 }, #line 159 "poppler/SymbolWidths.gperf" { "plus", 549 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 40 "poppler/SymbolWidths.gperf" { "phi", 521 }, #line 154 "poppler/SymbolWidths.gperf" { "iota", 329 }, #line 171 "poppler/SymbolWidths.gperf" { "seven", 500 }, { "", 0 }, #line 188 "poppler/SymbolWidths.gperf" { "registersans", 790 }, #line 107 "poppler/SymbolWidths.gperf" { "Chi", 722 }, #line 114 "poppler/SymbolWidths.gperf" { "five", 500 }, #line 108 "poppler/SymbolWidths.gperf" { "theta", 521 }, #line 167 "poppler/SymbolWidths.gperf" { "florin", 500 }, #line 50 "poppler/SymbolWidths.gperf" { "Epsilon", 611 }, { "", 0 }, #line 187 "poppler/SymbolWidths.gperf" { "less", 549 }, { "", 0 }, { "", 0 }, #line 98 "poppler/SymbolWidths.gperf" { "Pi", 768 }, { "", 0 }, { "", 0 }, #line 15 "poppler/SymbolWidths.gperf" { "alpha", 631 }, #line 175 "poppler/SymbolWidths.gperf" { "equivalence", 549 }, { "", 0 }, #line 102 "poppler/SymbolWidths.gperf" { "one", 500 }, #line 139 "poppler/SymbolWidths.gperf" { "Iota", 333 }, #line 62 "poppler/SymbolWidths.gperf" { "heart", 753 }, #line 33 "poppler/SymbolWidths.gperf" { "approxequal", 549 }, #line 160 "poppler/SymbolWidths.gperf" { "proportional", 713 }, #line 100 "poppler/SymbolWidths.gperf" { "registerserif", 790 }, #line 78 "poppler/SymbolWidths.gperf" { "lessequal", 549 }, #line 92 "poppler/SymbolWidths.gperf" { "eight", 500 }, { "", 0 }, { "", 0 }, #line 137 "poppler/SymbolWidths.gperf" { "Ifraktur", 686 }, { "", 0 }, #line 49 "poppler/SymbolWidths.gperf" { "equal", 549 }, #line 76 "poppler/SymbolWidths.gperf" { "existential", 549 }, #line 60 "poppler/SymbolWidths.gperf" { "Upsilon", 690 }, #line 168 "poppler/SymbolWidths.gperf" { "Psi", 795 }, #line 70 "poppler/SymbolWidths.gperf" { "therefore", 863 }, #line 161 "poppler/SymbolWidths.gperf" { "delta", 494 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 53 "poppler/SymbolWidths.gperf" { "four", 500 }, #line 32 "poppler/SymbolWidths.gperf" { "Sigma", 592 }, { "", 0 }, #line 142 "poppler/SymbolWidths.gperf" { "asteriskmath", 500 }, { "", 0 }, { "", 0 }, #line 37 "poppler/SymbolWidths.gperf" { "three", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 150 "poppler/SymbolWidths.gperf" { "carriagereturn", 658 }, #line 24 "poppler/SymbolWidths.gperf" { "notelement", 713 }, { "", 0 }, #line 141 "poppler/SymbolWidths.gperf" { "radical", 549 }, { "", 0 }, { "", 0 }, #line 86 "poppler/SymbolWidths.gperf" { "Gamma", 603 }, { "", 0 }, #line 80 "poppler/SymbolWidths.gperf" { "lozenge", 494 }, #line 51 "poppler/SymbolWidths.gperf" { "emptyset", 823 }, { "", 0 }, #line 45 "poppler/SymbolWidths.gperf" { "sigma", 603 }, #line 138 "poppler/SymbolWidths.gperf" { "degree", 400 }, #line 143 "poppler/SymbolWidths.gperf" { "percent", 833 }, #line 105 "poppler/SymbolWidths.gperf" { "rho", 549 }, { "", 0 }, #line 96 "poppler/SymbolWidths.gperf" { "Theta", 741 }, #line 28 "poppler/SymbolWidths.gperf" { "Lambda", 686 }, { "", 0 }, #line 117 "poppler/SymbolWidths.gperf" { "trademarksans", 786 }, #line 91 "poppler/SymbolWidths.gperf" { "radicalex", 500 }, #line 43 "poppler/SymbolWidths.gperf" { "spade", 753 }, { "", 0 }, { "", 0 }, #line 59 "poppler/SymbolWidths.gperf" { "tau", 439 }, { "", 0 }, #line 195 "poppler/SymbolWidths.gperf" { "Alpha", 722 }, { "", 0 }, #line 197 "poppler/SymbolWidths.gperf" { "greater", 549 }, #line 29 "poppler/SymbolWidths.gperf" { "Phi", 763 }, #line 126 "poppler/SymbolWidths.gperf" { "zero", 500 }, #line 31 "poppler/SymbolWidths.gperf" { "space", 250 }, #line 39 "poppler/SymbolWidths.gperf" { "lambda", 549 }, { "", 0 }, { "", 0 }, #line 73 "poppler/SymbolWidths.gperf" { "semicolon", 278 }, #line 41 "poppler/SymbolWidths.gperf" { "aleph", 823 }, #line 181 "poppler/SymbolWidths.gperf" { "theta1", 631 }, #line 74 "poppler/SymbolWidths.gperf" { "element", 713 }, #line 186 "poppler/SymbolWidths.gperf" { "fraction", 167 }, #line 81 "poppler/SymbolWidths.gperf" { "trademarkserif", 890 }, { "", 0 }, #line 61 "poppler/SymbolWidths.gperf" { "bracelefttp", 494 }, #line 192 "poppler/SymbolWidths.gperf" { "bracerighttp", 494 }, #line 173 "poppler/SymbolWidths.gperf" { "notequal", 549 }, #line 130 "poppler/SymbolWidths.gperf" { "braceleft", 480 }, #line 178 "poppler/SymbolWidths.gperf" { "braceright", 480 }, #line 27 "poppler/SymbolWidths.gperf" { "braceleftbt", 494 }, #line 169 "poppler/SymbolWidths.gperf" { "bracerightbt", 494 }, #line 120 "poppler/SymbolWidths.gperf" { "chi", 549 }, { "", 0 }, { "", 0 }, #line 190 "poppler/SymbolWidths.gperf" { "exclam", 333 }, #line 67 "poppler/SymbolWidths.gperf" { "greaterequal", 549 }, #line 191 "poppler/SymbolWidths.gperf" { "Upsilon1", 620 }, #line 20 "poppler/SymbolWidths.gperf" { "plusminus", 549 }, #line 110 "poppler/SymbolWidths.gperf" { "integraltp", 686 }, #line 194 "poppler/SymbolWidths.gperf" { "period", 250 }, #line 75 "poppler/SymbolWidths.gperf" { "upsilon", 576 }, #line 198 "poppler/SymbolWidths.gperf" { "bracketlefttp", 384 }, #line 149 "poppler/SymbolWidths.gperf" { "bracketrighttp", 384 }, #line 77 "poppler/SymbolWidths.gperf" { "integralbt", 686 }, #line 104 "poppler/SymbolWidths.gperf" { "bracketleft", 333 }, #line 157 "poppler/SymbolWidths.gperf" { "bracketright", 333 }, #line 170 "poppler/SymbolWidths.gperf" { "bracketleftbt", 384 }, #line 116 "poppler/SymbolWidths.gperf" { "bracketrightbt", 384 }, #line 123 "poppler/SymbolWidths.gperf" { "underscore", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 176 "poppler/SymbolWidths.gperf" { "universal", 713 }, { "", 0 }, { "", 0 }, #line 134 "poppler/SymbolWidths.gperf" { "braceex", 494 }, #line 201 "poppler/SymbolWidths.gperf" { "integral", 274 }, #line 65 "poppler/SymbolWidths.gperf" { "logicalor", 603 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 19 "poppler/SymbolWidths.gperf" { "copyrightsans", 790 }, { "", 0 }, #line 182 "poppler/SymbolWidths.gperf" { "logicalnot", 713 }, #line 63 "poppler/SymbolWidths.gperf" { "divide", 549 }, { "", 0 }, #line 42 "poppler/SymbolWidths.gperf" { "Tau", 611 }, #line 56 "poppler/SymbolWidths.gperf" { "club", 753 }, #line 99 "poppler/SymbolWidths.gperf" { "slash", 278 }, #line 165 "poppler/SymbolWidths.gperf" { "second", 411 }, { "", 0 }, #line 132 "poppler/SymbolWidths.gperf" { "Rfraktur", 795 }, #line 158 "poppler/SymbolWidths.gperf" { "ampersand", 778 }, #line 119 "poppler/SymbolWidths.gperf" { "integralex", 686 }, #line 84 "poppler/SymbolWidths.gperf" { "sigma1", 439 }, { "", 0 }, #line 14 "poppler/SymbolWidths.gperf" { "bracketleftex", 384 }, #line 156 "poppler/SymbolWidths.gperf" { "bracketrightex", 384 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 162 "poppler/SymbolWidths.gperf" { "copyrightserif", 790 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 79 "poppler/SymbolWidths.gperf" { "phi1", 603 }, #line 38 "poppler/SymbolWidths.gperf" { "numbersign", 500 }, #line 57 "poppler/SymbolWidths.gperf" { "bullet", 460 }, #line 36 "poppler/SymbolWidths.gperf" { "Omicron", 722 }, { "", 0 }, #line 106 "poppler/SymbolWidths.gperf" { "circlemultiply", 768 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 48 "poppler/SymbolWidths.gperf" { "question", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 112 "poppler/SymbolWidths.gperf" { "product", 823 }, { "", 0 }, { "", 0 }, #line 16 "poppler/SymbolWidths.gperf" { "union", 768 }, { "", 0 }, { "", 0 }, #line 200 "poppler/SymbolWidths.gperf" { "gradient", 713 }, { "", 0 }, #line 103 "poppler/SymbolWidths.gperf" { "gamma", 411 }, { "", 0 }, { "", 0 }, #line 151 "poppler/SymbolWidths.gperf" { "suchthat", 439 }, { "", 0 }, #line 35 "poppler/SymbolWidths.gperf" { "circleplus", 768 }, { "", 0 }, #line 172 "poppler/SymbolWidths.gperf" { "braceleftmid", 494 }, #line 163 "poppler/SymbolWidths.gperf" { "bracerightmid", 494 }, #line 71 "poppler/SymbolWidths.gperf" { "notsubset", 713 }, #line 25 "poppler/SymbolWidths.gperf" { "colon", 278 }, { "", 0 }, { "", 0 }, #line 125 "poppler/SymbolWidths.gperf" { "multiply", 549 }, { "", 0 }, #line 58 "poppler/SymbolWidths.gperf" { "Omega", 768 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 89 "poppler/SymbolWidths.gperf" { "Rho", 556 }, { "", 0 }, #line 18 "poppler/SymbolWidths.gperf" { "comma", 250 }, #line 34 "poppler/SymbolWidths.gperf" { "minute", 247 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 147 "poppler/SymbolWidths.gperf" { "weierstrass", 987 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 47 "poppler/SymbolWidths.gperf" { "omicron", 549 }, { "", 0 }, #line 148 "poppler/SymbolWidths.gperf" { "summation", 713 }, #line 44 "poppler/SymbolWidths.gperf" { "logicaland", 603 }, { "", 0 }, #line 21 "poppler/SymbolWidths.gperf" { "arrowup", 603 }, #line 146 "poppler/SymbolWidths.gperf" { "two", 500 }, { "", 0 }, #line 135 "poppler/SymbolWidths.gperf" { "arrowdblup", 603 }, { "", 0 }, #line 85 "poppler/SymbolWidths.gperf" { "nu", 521 }, { "", 0 }, #line 164 "poppler/SymbolWidths.gperf" { "arrowleft", 987 }, #line 196 "poppler/SymbolWidths.gperf" { "arrowright", 987 }, { "", 0 }, #line 129 "poppler/SymbolWidths.gperf" { "arrowdblleft", 987 }, #line 177 "poppler/SymbolWidths.gperf" { "arrowdblright", 987 }, #line 136 "poppler/SymbolWidths.gperf" { "arrowdown", 603 }, #line 30 "poppler/SymbolWidths.gperf" { "minus", 549 }, { "", 0 }, #line 94 "poppler/SymbolWidths.gperf" { "arrowdbldown", 603 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 69 "poppler/SymbolWidths.gperf" { "Nu", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 54 "poppler/SymbolWidths.gperf" { "Mu", 889 }, { "", 0 }, { "", 0 }, #line 72 "poppler/SymbolWidths.gperf" { "omega", 686 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 95 "poppler/SymbolWidths.gperf" { "congruent", 549 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 124 "poppler/SymbolWidths.gperf" { "Euro", 750 }, { "", 0 }, #line 152 "poppler/SymbolWidths.gperf" { "arrowvertex", 603 }, #line 155 "poppler/SymbolWidths.gperf" { "arrowhorizex", 1000 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 118 "poppler/SymbolWidths.gperf" { "dotmath", 250 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 52 "poppler/SymbolWidths.gperf" { "diamond", 753 }, { "", 0 }, #line 202 "poppler/SymbolWidths.gperf" { "arrowboth", 1042 }, { "", 0 }, { "", 0 }, #line 166 "poppler/SymbolWidths.gperf" { "arrowdblboth", 1042 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 68 "poppler/SymbolWidths.gperf" { "mu", 576 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 189 "poppler/SymbolWidths.gperf" { "omega1", 713 } }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) { register unsigned int key = hash(str, len); if (key <= MAX_HASH_VALUE) { register const char *s = wordlist[key].name; if (*str == *s && !strcmp(str + 1, s + 1)) return &wordlist[key]; } } return 0; } #line 205 "poppler/SymbolWidths.gperf" poppler-24.02.0/poppler/TextOutputDev.cc000066400000000000000000006014471455701731300201560ustar00rootroot00000000000000//======================================================================== // // TextOutputDev.cc // // Copyright 1997-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) 2005-2007 Kristian Høgsberg // Copyright (C) 2005 Nickolay V. Shmyrev // Copyright (C) 2006-2008, 2011-2013 Carlos Garcia Campos // Copyright (C) 2006, 2007, 2013 Ed Catmur // 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 Pino Toscano // Copyright (C) 2008, 2010 Hib Eris // Copyright (C) 2009 Ross Moore // Copyright (C) 2009 Kovid Goyal // Copyright (C) 2010 Brian Ewins // Copyright (C) 2010, 2021 Marek Kasik // Copyright (C) 2010, 2020 Suzuki Toshiya // Copyright (C) 2011 Sam Liao // Copyright (C) 2012 Horst Prote // Copyright (C) 2012, 2013-2018 Jason Crain // Copyright (C) 2012 Peter Breitenlohner // Copyright (C) 2013 José Aliste // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2013 Ed Catmur // Copyright (C) 2016 Khaled Hosny // 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) 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 // // 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 #include #include #include #include #include #include #include #if defined(_WIN32) || defined(__CYGWIN__) # include // for O_BINARY # include // for _setmode #endif #include "goo/gfile.h" #include "goo/gmem.h" #include "goo/GooString.h" #include "poppler-config.h" #include "Error.h" #include "GlobalParams.h" #include "UnicodeMap.h" #include "UnicodeTypeTable.h" #include "Link.h" #include "TextOutputDev.h" #include "Page.h" #include "Annot.h" #include "UTF.h" //------------------------------------------------------------------------ // parameters //------------------------------------------------------------------------ // Each bucket in a text pool includes baselines within a range of // this many points. #define textPoolStep 4 // Inter-character space width which will cause addChar to start a new // word. #define minWordBreakSpace 0.1 // Negative inter-character space width, i.e., overlap, which will // cause addChar to start a new word. #define minDupBreakOverlap 0.2 // Max distance between baselines of two lines within a block, as a // fraction of the font size. #define maxLineSpacingDelta 1.5 // Max difference in primary font sizes on two lines in the same // block. Delta1 is used when examining new lines above and below the // current block; delta2 is used when examining text that overlaps the // current block; delta3 is used when examining text to the left and // right of the current block. #define maxBlockFontSizeDelta1 0.05 #define maxBlockFontSizeDelta2 0.6 #define maxBlockFontSizeDelta3 0.2 // Max difference in font sizes inside a word. #define maxWordFontSizeDelta 0.05 // Maximum distance between baselines of two words on the same line, // e.g., distance between subscript or superscript and the primary // baseline, as a fraction of the font size. #define maxIntraLineDelta 0.5 // Minimum inter-word spacing, as a fraction of the font size. (Only // used for raw ordering.) #define minWordSpacing 0.15 // Maximum inter-word spacing, as a fraction of the font size. #define maxWordSpacing 1.5 // Maximum horizontal spacing which will allow a word to be pulled // into a block, as a fraction of the font size. // This default value can be tweaked via API. double TextOutputDev::minColSpacing1_default = 0.7; // Minimum spacing between columns, as a fraction of the font size. #define minColSpacing2 1.0 // Maximum vertical spacing between blocks within a flow, as a // multiple of the font size. #define maxBlockSpacing 2.5 // Minimum spacing between characters within a word, as a fraction of // the font size. #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 // spacing. #define maxCharSpacing 0.03 // When extra-wide character spacing is detected, the inter-character // space threshold is set to the minimum inter-character space // multiplied by this constant. #define maxWideCharSpacingMul 1.3 // Upper limit on spacing between characters in a word. #define maxWideCharSpacing 0.4 // Max difference in primary,secondary coordinates (as a fraction of // the font size) allowed for duplicated text (fake boldface, drop // shadows) which is to be discarded. #define dupMaxPriDelta 0.1 #define dupMaxSecDelta 0.2 // Max width of underlines (in points). #define maxUnderlineWidth 3 // Min distance between baseline and underline (in points). //~ this should be font-size-dependent #define minUnderlineGap -2 // Max distance between baseline and underline (in points). //~ this should be font-size-dependent #define maxUnderlineGap 4 // Max horizontal distance between edge of word and start of underline // (in points). //~ this should be font-size-dependent #define underlineSlack 1 // Max distance between edge of text and edge of link border #define hyperlinkSlack 2 // Max distance between characters when combining a base character and // combining character #define combMaxMidDelta 0.3 #define combMaxBaseDelta 0.4 // Text is considered diagonal if abs(tan(angle)) > diagonalThreshold. // (Or 1/tan(angle) for 90/270 degrees.) #define diagonalThreshold 0.1 // How opaque a selection on a glyphless font should be. Since the font is // glyphless and overlaid over text in image form, this must enable users // to read the underlying image. Issue #157 #define glyphlessSelectionOpacity 0.4 // Returns whether x is between a and b or equal to a or b. // a and b don't need to be sorted. #define XBetweenAB(x, a, b) (!(((x) > (a) && (x) > (b)) || ((x) < (a) && (x) < (b))) ? true : false) namespace { inline bool isAscii7(Unicode uchar) { return uchar < 128; } } static int reorderText(const Unicode *text, int len, const UnicodeMap *uMap, bool primaryLR, GooString *s, Unicode *u) { char lre[8], rle[8], popdf[8], buf[8]; int lreLen = 0, rleLen = 0, popdfLen = 0, n; int nCols, i, j, k; nCols = 0; if (s) { lreLen = uMap->mapUnicode(0x202a, lre, sizeof(lre)); rleLen = uMap->mapUnicode(0x202b, rle, sizeof(rle)); popdfLen = uMap->mapUnicode(0x202c, popdf, sizeof(popdf)); } if (primaryLR) { i = 0; while (i < len) { // output a left-to-right section for (j = i; j < len && !unicodeTypeR(text[j]); ++j) { ; } for (k = i; k < j; ++k) { if (s) { n = uMap->mapUnicode(text[k], buf, sizeof(buf)); s->append(buf, n); } if (u) { u[nCols] = text[k]; } ++nCols; } i = j; // output a right-to-left section for (j = i; j < len && !(unicodeTypeL(text[j]) || unicodeTypeNum(text[j])); ++j) { ; } if (j > i) { if (s) { s->append(rle, rleLen); } for (k = j - 1; k >= i; --k) { if (s) { n = uMap->mapUnicode(text[k], buf, sizeof(buf)); s->append(buf, n); } if (u) { u[nCols] = text[k]; } ++nCols; } if (s) { s->append(popdf, popdfLen); } i = j; } } } else { // Note: This code treats numeric characters (European and // Arabic/Indic) as left-to-right, which isn't strictly correct // (incurs extra LRE/POPDF pairs), but does produce correct // visual formatting. if (s) { s->append(rle, rleLen); } i = len - 1; while (i >= 0) { // output a right-to-left section for (j = i; j >= 0 && !(unicodeTypeL(text[j]) || unicodeTypeNum(text[j])); --j) { ; } for (k = i; k > j; --k) { if (s) { n = uMap->mapUnicode(text[k], buf, sizeof(buf)); s->append(buf, n); } if (u) { u[nCols] = text[k]; } ++nCols; } i = j; // output a left-to-right section for (j = i; j >= 0 && !unicodeTypeR(text[j]); --j) { ; } if (j < i) { if (s) { s->append(lre, lreLen); } for (k = j + 1; k <= i; ++k) { if (s) { n = uMap->mapUnicode(text[k], buf, sizeof(buf)); s->append(buf, n); } if (u) { u[nCols] = text[k]; } ++nCols; } if (s) { s->append(popdf, popdfLen); } i = j; } } if (s) { s->append(popdf, popdfLen); } } return nCols; } //------------------------------------------------------------------------ // TextUnderline //------------------------------------------------------------------------ class TextUnderline { public: TextUnderline(double x0A, double y0A, double x1A, double y1A) { x0 = x0A; y0 = y0A; x1 = x1A; y1 = y1A; horiz = y0 == y1; } ~TextUnderline() { } double x0, y0, x1, y1; bool horiz; }; //------------------------------------------------------------------------ // TextLink //------------------------------------------------------------------------ class TextLink { public: TextLink(int xMinA, int yMinA, int xMaxA, int yMaxA, AnnotLink *linkA) { xMin = xMinA; yMin = yMinA; xMax = xMaxA; yMax = yMaxA; link = linkA; } ~TextLink() { } int xMin, yMin, xMax, yMax; AnnotLink *link; }; //------------------------------------------------------------------------ // TextFontInfo //------------------------------------------------------------------------ TextFontInfo::TextFontInfo(const GfxState *state) { gfxFont = state->getFont(); #ifdef TEXTOUT_WORD_LIST fontName = (gfxFont && gfxFont->getName()) ? new GooString(*gfxFont->getName()) : nullptr; flags = gfxFont ? gfxFont->getFlags() : 0; #endif } TextFontInfo::~TextFontInfo() { #ifdef TEXTOUT_WORD_LIST if (fontName) { delete fontName; } #endif } bool TextFontInfo::matches(const GfxState *state) const { return state->getFont() == gfxFont; } bool TextFontInfo::matches(const TextFontInfo *fontInfo) const { return gfxFont == fontInfo->gfxFont; } bool TextFontInfo::matches(const Ref *ref) const { return (*(gfxFont->getID()) == *ref); } double TextFontInfo::getAscent() const { return gfxFont ? gfxFont->getAscent() : 0.95; } double TextFontInfo::getDescent() const { return gfxFont ? gfxFont->getDescent() : -0.35; } int TextFontInfo::getWMode() const { return gfxFont ? gfxFont->getWMode() : 0; } //------------------------------------------------------------------------ // TextWord //------------------------------------------------------------------------ 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; #ifdef TEXTOUT_WORD_LIST GfxRGB rgb; if ((state->getRender() & 3) == 1) { state->getStrokeRGB(&rgb); } else { state->getFillRGB(&rgb); } colorR = colToDbl(rgb.r); colorG = colToDbl(rgb.g); colorB = colToDbl(rgb.b); #endif underlined = false; link = nullptr; } TextWord::~TextWord() { gfree(text); gfree(charcode); gfree(edge); gfree(charPos); gfree(font); gfree(textMat); } 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; if (len == 0) { setInitialBounds(fontA, x, y); } if (wMode) { // vertical writing mode // NB: the rotation value has been incremented by 1 (in // TextPage::beginWord()) for vertical writing mode switch (rot) { case 0: edge[len] = x - fontSize; xMax = edge[len + 1] = x; break; case 1: edge[len] = y - fontSize; yMax = edge[len + 1] = y; break; case 2: edge[len] = x + fontSize; xMin = edge[len + 1] = x; break; case 3: edge[len] = y + fontSize; yMin = edge[len + 1] = y; break; } } else { // horizontal writing mode switch (rot) { case 0: edge[len] = x; xMax = edge[len + 1] = x + dx; break; case 1: edge[len] = y; yMax = edge[len + 1] = y + dy; break; case 2: edge[len] = x; xMin = edge[len + 1] = x + dx; break; case 3: edge[len] = y; yMin = edge[len + 1] = y + dy; break; } } ++len; } void TextWord::setInitialBounds(TextFontInfo *fontA, double x, double y) { double ascent = fontA->getAscent() * fontSize; double descent = fontA->getDescent() * fontSize; wMode = fontA->getWMode(); if (wMode) { // vertical writing mode // NB: the rotation value has been incremented by 1 (in // TextPage::beginWord()) for vertical writing mode switch (rot) { case 0: xMin = x - fontSize; yMin = y - fontSize; yMax = y; base = y; break; case 1: xMin = x; yMin = y - fontSize; xMax = x + fontSize; base = x; break; case 2: yMin = y; xMax = x + fontSize; yMax = y + fontSize; base = y; break; case 3: xMin = x - fontSize; xMax = x; yMax = y + fontSize; base = x; break; } } else { // horizontal writing mode switch (rot) { case 0: xMin = x; yMin = y - ascent; yMax = y - descent; if (yMin == yMax) { // this is a sanity check for a case that shouldn't happen -- but // if it does happen, we want to avoid dividing by zero later yMin = y; yMax = y + 1; } base = y; break; case 1: xMin = x + descent; yMin = y; xMax = x + ascent; if (xMin == xMax) { // this is a sanity check for a case that shouldn't happen -- but // if it does happen, we want to avoid dividing by zero later xMin = x; xMax = x + 1; } base = x; break; case 2: yMin = y + descent; xMax = x; yMax = y + ascent; if (yMin == yMax) { // this is a sanity check for a case that shouldn't happen -- but // if it does happen, we want to avoid dividing by zero later yMin = y; yMax = y + 1; } base = y; break; case 3: xMin = x - ascent; xMax = x - descent; yMax = y; if (xMin == xMax) { // this is a sanity check for a case that shouldn't happen -- but // if it does happen, we want to avoid dividing by zero later xMin = x; xMax = x + 1; } base = x; break; } } } 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; Unicode comb; }; static const struct CombiningTable combiningTable[] = { { 0x0060, 0x0300 }, // grave { 0x00a8, 0x0308 }, // dieresis { 0x00af, 0x0304 }, // macron { 0x00b4, 0x0301 }, // acute { 0x00b8, 0x0327 }, // cedilla { 0x02c6, 0x0302 }, // circumflex { 0x02c7, 0x030c }, // caron { 0x02d8, 0x0306 }, // breve { 0x02d9, 0x0307 }, // dotaccent { 0x02da, 0x030a }, // ring { 0x02dc, 0x0303 }, // tilde { 0x02dd, 0x030b } // hungarumlaut (double acute accent) }; // returning combining versions of characters static Unicode getCombiningChar(Unicode u) { for (const CombiningTable &combining : combiningTable) { if (u == combining.base) { return combining.comb; } } return 0; } 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) { 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])) { // Current is a combining character, previous is base character maxScaledMidDelta = fabs(edge[len] - edge[len - 1]) * combMaxMidDelta; charMid = charBase = maxScaledBaseDelta = 0; // Test if characters overlap if (rot == 0 || rot == 2) { charMid = x + (dx / 2); charBase = y; maxScaledBaseDelta = (yMax - yMin) * combMaxBaseDelta; } else { charMid = y + (dy / 2); charBase = x; maxScaledBaseDelta = (xMax - xMin) * combMaxBaseDelta; } 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; return true; } 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; // Test if characters overlap if (rot == 0 || rot == 2) { charMid = x + (dx / 2); charBase = y; maxScaledMidDelta = fabs(dx * combMaxMidDelta); } else { charMid = y + (dy / 2); charBase = x; maxScaledMidDelta = fabs(dy * combMaxMidDelta); } 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) { setInitialBounds(fontA, x, y); } // Updated edges / bounding box because we changed the base // character. if (wMode) { switch (rot) { case 0: edge[len - 1] = x - fontSize; xMax = edge[len + 1] = x; break; case 1: edge[len - 1] = y - fontSize; yMax = edge[len + 1] = y; break; case 2: edge[len - 1] = x + fontSize; xMin = edge[len + 1] = x; break; case 3: edge[len - 1] = y + fontSize; yMin = edge[len + 1] = y; break; } } else { switch (rot) { case 0: edge[len - 1] = x; xMax = edge[len + 1] = x + dx; break; case 1: edge[len - 1] = y; yMax = edge[len + 1] = y + dy; break; case 2: edge[len - 1] = x; xMin = edge[len + 1] = x + dx; break; case 3: edge[len - 1] = y; yMin = edge[len + 1] = y + dy; break; } } edge[len] = (edge[len + 1] + edge[len - 1]) / 2; ++len; return true; } return false; } void TextWord::merge(TextWord *word) { int i; if (word->xMin < xMin) { xMin = word->xMin; } if (word->yMin < yMin) { yMin = word->yMin; } if (word->xMax > xMax) { xMax = word->xMax; } 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; } inline int TextWord::primaryCmp(const TextWord *word) const { double cmp; cmp = 0; // make gcc happy switch (rot) { case 0: cmp = xMin - word->xMin; break; case 1: cmp = yMin - word->yMin; break; case 2: cmp = word->xMax - xMax; break; case 3: cmp = word->yMax - yMax; break; } return cmp < 0 ? -1 : cmp > 0 ? 1 : 0; } double TextWord::primaryDelta(const TextWord *word) const { double delta; delta = 0; // make gcc happy switch (rot) { case 0: delta = word->xMin - xMax; break; case 1: delta = word->yMin - yMax; break; case 2: delta = xMin - word->xMax; break; case 3: delta = yMin - word->yMax; break; } return delta; } int TextWord::cmpYX(const void *p1, const void *p2) { 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; } #ifdef TEXTOUT_WORD_LIST 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)); s->append(buf, n); } return s; } void TextWord::getCharBBox(int charIdx, double *xMinA, double *yMinA, double *xMaxA, double *yMaxA) const { if (charIdx < 0 || charIdx >= len) { return; } switch (rot) { case 0: *xMinA = edge[charIdx]; *xMaxA = edge[charIdx + 1]; *yMinA = yMin; *yMaxA = yMax; break; case 1: *xMinA = xMin; *xMaxA = xMax; *yMinA = edge[charIdx]; *yMaxA = edge[charIdx + 1]; break; case 2: *xMinA = edge[charIdx + 1]; *xMaxA = edge[charIdx]; *yMinA = yMin; *yMaxA = yMax; break; case 3: *xMinA = xMin; *xMaxA = xMax; *yMinA = edge[charIdx + 1]; *yMaxA = edge[charIdx]; break; } } #endif // TEXTOUT_WORD_LIST //------------------------------------------------------------------------ // TextPool //------------------------------------------------------------------------ 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; } } gfree(pool); } int TextPool::getBaseIdx(double base) const { const double baseIdxDouble = base / textPoolStep; if (std::isnan(baseIdxDouble) || baseIdxDouble < minBaseIdx) { return minBaseIdx; } if (baseIdxDouble > maxBaseIdx) { return maxBaseIdx; } return (int)baseIdxDouble; } void TextPool::addWord(TextWord *word) { int wordBaseIdx, newMinBaseIdx, newMaxBaseIdx, baseIdx; TextWord *w0, *w1; // expand the array if needed 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) { 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; } // insert the new word if (cursor && wordBaseIdx == cursorBaseIdx && word->primaryCmp(cursor) >= 0) { w0 = cursor; w1 = cursor->next; } else { w0 = nullptr; w1 = pool[wordBaseIdx - minBaseIdx]; } for (; w1 && word->primaryCmp(w1) > 0; w0 = w1, w1 = w1->next) { ; } word->next = w1; if (w0) { w0->next = word; } else { pool[wordBaseIdx - minBaseIdx] = word; } cursor = word; cursorBaseIdx = wordBaseIdx; } //------------------------------------------------------------------------ // TextLine //------------------------------------------------------------------------ TextLine::TextLine(TextBlock *blkA, int rotA, double baseA) { blk = blkA; rot = rotA; base = baseA; words = lastWord = nullptr; text = nullptr; edge = nullptr; col = nullptr; len = 0; convertedLen = 0; hyphenated = false; next = nullptr; xMin = yMin = 0; xMax = yMax = -1; normalized = nullptr; normalized_len = 0; normalized_idx = nullptr; ascii_translation = nullptr; ascii_len = 0; ascii_idx = nullptr; } TextLine::~TextLine() { TextWord *word; while (words) { word = words; words = words->next; delete word; } gfree(text); gfree(edge); gfree(col); if (normalized) { gfree(normalized); gfree(normalized_idx); } if (ascii_translation) { gfree(ascii_translation); gfree(ascii_idx); } } void TextLine::addWord(TextWord *word) { if (lastWord) { lastWord->next = word; } else { words = word; } lastWord = word; if (xMin > xMax) { xMin = word->xMin; xMax = word->xMax; yMin = word->yMin; yMax = word->yMax; } else { if (word->xMin < xMin) { xMin = word->xMin; } if (word->xMax > xMax) { xMax = word->xMax; } if (word->yMin < yMin) { yMin = word->yMin; } if (word->yMax > yMax) { yMax = word->yMax; } } } double TextLine::primaryDelta(const TextLine *line) const { double delta; delta = 0; // make gcc happy switch (rot) { case 0: delta = line->xMin - xMax; break; case 1: delta = line->yMin - yMax; break; case 2: delta = xMin - line->xMax; break; case 3: delta = yMin - line->yMax; break; } return delta; } int TextLine::primaryCmp(const TextLine *line) const { double cmp; cmp = 0; // make gcc happy switch (rot) { case 0: cmp = xMin - line->xMin; break; case 1: cmp = yMin - line->yMin; break; case 2: cmp = line->xMax - xMax; break; case 3: cmp = line->yMax - yMax; break; } return cmp < 0 ? -1 : cmp > 0 ? 1 : 0; } int TextLine::secondaryCmp(const TextLine *line) const { double cmp; cmp = (rot == 0 || rot == 3) ? base - line->base : line->base - base; return cmp < 0 ? -1 : cmp > 0 ? 1 : 0; } int TextLine::cmpYX(const TextLine *line) const { int cmp; if ((cmp = secondaryCmp(line))) { return cmp; } return primaryCmp(line); } int TextLine::cmpXY(const void *p1, const void *p2) { TextLine *line1 = *(TextLine **)p1; TextLine *line2 = *(TextLine **)p2; int cmp; if ((cmp = line1->primaryCmp(line2))) { return cmp; } return line1->secondaryCmp(line2); } 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) { 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) { minSpace = 0; } delta = word0->primaryDelta(word1); if (delta < minSpace) { minSpace = delta; } } } if (minSpace <= 0) { space = maxCharSpacing * words->fontSize; } else { space = maxWideCharSpacingMul * minSpace; if (space > maxWideCharSpacing * words->fontSize) { space = maxWideCharSpacing * words->fontSize; } } // merge words word0 = words; 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]) { word0->merge(word1); word0->next = word1->next; delete word1; word1 = word0->next; } else { word0 = word1; word1 = word1->next; } } } // build the line text isUnicode = uMap ? uMap->isUnicode() : false; len = 0; for (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]; ++i; } edge[i] = word1->edge[word1->len]; if (word1->spaceAfter) { text[i] = (Unicode)0x0020; ++i; } } // 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; if (isUnicode) { ++convertedLen; } else if (uMap) { convertedLen += uMap->mapUnicode(text[i], buf, sizeof(buf)); } } col[len] = convertedLen; // check for hyphen at end of line //~ need to check for other chars used as hyphens hyphenated = text[len - 1] == (Unicode)'-'; } //------------------------------------------------------------------------ // TextLineFrag //------------------------------------------------------------------------ class TextLineFrag { public: TextLine *line; // the line object int start, len; // offset and length of this fragment // (in Unicode chars) double xMin, xMax; // bounding box coordinates double yMin, yMax; double base; // baseline virtual coordinate int col; // first column 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); }; void TextLineFrag::init(TextLine *lineA, int startA, int lenA) { line = lineA; start = startA; len = lenA; col = line->col[start]; } void TextLineFrag::computeCoords(bool oneRot) { TextBlock *blk; double d0, d1, d2, d3, d4; if (oneRot) { switch (line->rot) { case 0: xMin = line->edge[start]; xMax = line->edge[start + len]; yMin = line->yMin; yMax = line->yMax; break; case 1: xMin = line->xMin; xMax = line->xMax; yMin = line->edge[start]; yMax = line->edge[start + len]; break; case 2: xMin = line->edge[start + len]; xMax = line->edge[start]; yMin = line->yMin; yMax = line->yMax; break; case 3: xMin = line->xMin; xMax = line->xMax; yMin = line->edge[start + len]; yMax = line->edge[start]; break; } base = line->base; } else { if (line->rot == 0 && line->blk->page->primaryRot == 0) { xMin = line->edge[start]; xMax = line->edge[start + len]; yMin = line->yMin; yMax = line->yMax; base = line->base; } else { blk = line->blk; d0 = line->edge[start]; d1 = line->edge[start + len]; d2 = d3 = d4 = 0; // make gcc happy switch (line->rot) { case 0: d2 = line->yMin; d3 = line->yMax; d4 = line->base; d0 = (d0 - blk->xMin) / (blk->xMax - blk->xMin); d1 = (d1 - blk->xMin) / (blk->xMax - blk->xMin); d2 = (d2 - blk->yMin) / (blk->yMax - blk->yMin); d3 = (d3 - blk->yMin) / (blk->yMax - blk->yMin); d4 = (d4 - blk->yMin) / (blk->yMax - blk->yMin); break; case 1: d2 = line->xMax; d3 = line->xMin; d4 = line->base; d0 = (d0 - blk->yMin) / (blk->yMax - blk->yMin); d1 = (d1 - blk->yMin) / (blk->yMax - blk->yMin); d2 = (blk->xMax - d2) / (blk->xMax - blk->xMin); d3 = (blk->xMax - d3) / (blk->xMax - blk->xMin); d4 = (blk->xMax - d4) / (blk->xMax - blk->xMin); break; case 2: d2 = line->yMax; d3 = line->yMin; d4 = line->base; d0 = (blk->xMax - d0) / (blk->xMax - blk->xMin); d1 = (blk->xMax - d1) / (blk->xMax - blk->xMin); d2 = (blk->yMax - d2) / (blk->yMax - blk->yMin); d3 = (blk->yMax - d3) / (blk->yMax - blk->yMin); d4 = (blk->yMax - d4) / (blk->yMax - blk->yMin); break; case 3: d2 = line->xMin; d3 = line->xMax; d4 = line->base; d0 = (blk->yMax - d0) / (blk->yMax - blk->yMin); d1 = (blk->yMax - d1) / (blk->yMax - blk->yMin); d2 = (d2 - blk->xMin) / (blk->xMax - blk->xMin); d3 = (d3 - blk->xMin) / (blk->xMax - blk->xMin); d4 = (d4 - blk->xMin) / (blk->xMax - blk->xMin); break; } switch (line->blk->page->primaryRot) { case 0: xMin = blk->xMin + d0 * (blk->xMax - blk->xMin); xMax = blk->xMin + d1 * (blk->xMax - blk->xMin); yMin = blk->yMin + d2 * (blk->yMax - blk->yMin); yMax = blk->yMin + d3 * (blk->yMax - blk->yMin); base = blk->yMin + d4 * (blk->yMax - blk->yMin); break; case 1: xMin = blk->xMax - d3 * (blk->xMax - blk->xMin); xMax = blk->xMax - d2 * (blk->xMax - blk->xMin); yMin = blk->yMin + d0 * (blk->yMax - blk->yMin); yMax = blk->yMin + d1 * (blk->yMax - blk->yMin); base = blk->xMax - d4 * (blk->xMax - blk->xMin); break; case 2: xMin = blk->xMax - d1 * (blk->xMax - blk->xMin); xMax = blk->xMax - d0 * (blk->xMax - blk->xMin); yMin = blk->yMax - d3 * (blk->yMax - blk->yMin); yMax = blk->yMax - d2 * (blk->yMax - blk->yMin); base = blk->yMax - d4 * (blk->yMax - blk->yMin); break; case 3: xMin = blk->xMin + d2 * (blk->xMax - blk->xMin); xMax = blk->xMin + d3 * (blk->xMax - blk->xMin); yMin = blk->yMax - d1 * (blk->yMax - blk->yMin); yMax = blk->yMax - d0 * (blk->yMax - blk->yMin); base = blk->xMin + d4 * (blk->xMax - blk->xMin); break; } } } } int TextLineFrag::cmpYXPrimaryRot(const void *p1, const void *p2) { TextLineFrag *frag1 = (TextLineFrag *)p1; TextLineFrag *frag2 = (TextLineFrag *)p2; double cmp; cmp = 0; // make gcc happy switch (frag1->line->blk->page->primaryRot) { case 0: 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; } break; case 2: 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; } break; } return cmp < 0 ? -1 : cmp > 0 ? 1 : 0; } int TextLineFrag::cmpYXLineRot(const void *p1, const void *p2) { TextLineFrag *frag1 = (TextLineFrag *)p1; TextLineFrag *frag2 = (TextLineFrag *)p2; double cmp; cmp = 0; // make gcc happy switch (frag1->line->rot) { case 0: 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; } break; case 2: 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; } break; } return cmp < 0 ? -1 : cmp > 0 ? 1 : 0; } int TextLineFrag::cmpXYLineRot(const void *p1, const void *p2) { TextLineFrag *frag1 = (TextLineFrag *)p1; TextLineFrag *frag2 = (TextLineFrag *)p2; double cmp; cmp = 0; // make gcc happy switch (frag1->line->rot) { case 0: 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; } break; case 2: 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; } break; } return cmp < 0 ? -1 : cmp > 0 ? 1 : 0; } int TextLineFrag::cmpXYColumnPrimaryRot(const void *p1, const void *p2) { 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])) { cmp = 0; // make gcc happy switch (frag1->line->blk->page->primaryRot) { case 0: cmp = frag1->yMin - frag2->yMin; break; case 1: cmp = frag2->xMax - frag1->xMax; break; case 2: cmp = frag2->yMin - frag1->yMin; break; case 3: cmp = frag1->xMax - frag2->xMax; break; } return cmp < 0 ? -1 : cmp > 0 ? 1 : 0; } // otherwise, compare starting column return frag1->col - frag2->col; } int TextLineFrag::cmpXYColumnLineRot(const void *p1, const void *p2) { 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])) { cmp = 0; // make gcc happy switch (frag1->line->rot) { case 0: cmp = frag1->yMin - frag2->yMin; break; case 1: cmp = frag2->xMax - frag1->xMax; break; case 2: cmp = frag2->yMin - frag1->yMin; break; case 3: cmp = frag1->xMax - frag2->xMax; break; } return cmp < 0 ? -1 : cmp > 0 ? 1 : 0; } // otherwise, compare starting column return frag1->col - frag2->col; } //------------------------------------------------------------------------ // TextBlock //------------------------------------------------------------------------ TextBlock::TextBlock(TextPage *pageA, int rotA) { page = pageA; rot = rotA; xMin = yMin = 0; xMax = yMax = -1; priMin = 0; priMax = page->pageWidth; pool = new TextPool(); lines = nullptr; curLine = nullptr; next = nullptr; stackNext = nullptr; tableId = -1; tableEnd = false; } TextBlock::~TextBlock() { TextLine *line; delete pool; while (lines) { line = lines; lines = lines->next; delete line; } } void TextBlock::addWord(TextWord *word) { pool->addWord(word); if (xMin > xMax) { xMin = word->xMin; xMax = word->xMax; yMin = word->yMin; yMax = word->yMax; } else { if (word->xMin < xMin) { xMin = word->xMin; } if (word->xMax > xMax) { xMax = word->xMax; } if (word->yMin < yMin) { yMin = word->yMin; } if (word->yMax > yMax) { yMax = word->yMax; } } } 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; // discard duplicated text (fake boldface, drop shadows) for (idx0 = pool->minBaseIdx; idx0 <= pool->maxBaseIdx; ++idx0) { 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); } 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 (found) { break; } } if (found) { break; } } if (found) { if (word1) { word1->next = word2->next; } else { pool->setPool(idx1, word2->next); } delete word2; } else { word0 = word0->next; } } } // build the lines curLine = nullptr; poolMinBaseIdx = pool->minBaseIdx; charCount = 0; nLines = 0; while (true) { // find the first non-empty line in the pool for (; poolMinBaseIdx <= pool->maxBaseIdx && !pool->getPool(poolMinBaseIdx); ++poolMinBaseIdx) { ; } if (poolMinBaseIdx > pool->maxBaseIdx) { break; } // look for the left-most word in the first four lines of the // pool -- this avoids starting with a superscript word startBaseIdx = poolMinBaseIdx; for (baseIdx = poolMinBaseIdx + 1; baseIdx < poolMinBaseIdx + 4 && baseIdx <= pool->maxBaseIdx; ++baseIdx) { if (!pool->getPool(baseIdx)) { continue; } if (pool->getPool(baseIdx)->primaryCmp(pool->getPool(startBaseIdx)) < 0) { startBaseIdx = baseIdx; } } // create a new line word0 = pool->getPool(startBaseIdx); pool->setPool(startBaseIdx, word0->next); word0->next = nullptr; line = new TextLine(this, word0->rot, word0->base); line->addWord(word0); lastWord = word0; // compute the search range fontSize = word0->fontSize; minBase = word0->base - maxIntraLineDelta * fontSize; maxBase = word0->base + maxIntraLineDelta * fontSize; minBaseIdx = pool->getBaseIdx(minBase); maxBaseIdx = pool->getBaseIdx(maxBase); wordSpacing = fixedPitch ? fixedPitch : maxWordSpacing * fontSize; // find the rest of the words in this line while (true) { // find the left-most word whose baseline is in the range for // this line bestWordBaseIdx = 0; bestWord0 = bestWord1 = nullptr; overlap = false; for (baseIdx = minBaseIdx; !overlap && baseIdx <= maxBaseIdx; ++baseIdx) { for (word0 = nullptr, word1 = pool->getPool(baseIdx); word1; word0 = word1, word1 = word1->next) { if (word1->base >= minBase && word1->base <= maxBase) { delta = lastWord->primaryDelta(word1); if (delta < minCharSpacing * fontSize) { overlap = true; break; } else { if (delta < wordSpacing && (!bestWord1 || word1->primaryCmp(bestWord1) < 0)) { bestWordBaseIdx = baseIdx; bestWord0 = word0; bestWord1 = word1; } break; } } } } if (overlap || !bestWord1) { break; } // remove it from the pool, and add it to the line if (bestWord0) { bestWord0->next = bestWord1->next; } else { pool->setPool(bestWordBaseIdx, bestWord1->next); } bestWord1->next = nullptr; line->addWord(bestWord1); lastWord = bestWord1; } // add the line if (curLine && line->cmpYX(curLine) > 0) { line0 = curLine; line1 = curLine->next; } else { line0 = nullptr; line1 = lines; } for (; line1 && line->cmpYX(line1) > 0; line0 = line1, line1 = line1->next) { ; } if (line0) { line0->next = line; } else { lines = line; } line->next = line1; curLine = line; line->coalesce(uMap); charCount += line->len; ++nLines; } // sort lines into xy order for column assignment lineArray = (TextLine **)gmallocn(nLines, sizeof(TextLine *)); for (line = lines, i = 0; line; line = line->next, ++i) { lineArray[i] = line; } qsort(lineArray, nLines, sizeof(TextLine *), &TextLine::cmpXY); // column assignment nColumns = 0; if (fixedPitch) { for (i = 0; i < nLines; ++i) { line0 = lineArray[i]; col1 = 0; // make gcc happy switch (rot) { case 0: col1 = (int)((line0->xMin - xMin) / fixedPitch + 0.5); break; case 1: col1 = (int)((line0->yMin - yMin) / fixedPitch + 0.5); break; case 2: col1 = (int)((xMax - line0->xMax) / fixedPitch + 0.5); break; case 3: col1 = (int)((yMax - line0->yMax) / fixedPitch + 0.5); break; } for (k = 0; k <= line0->len; ++k) { line0->col[k] += col1; } if (line0->col[line0->len] > nColumns) { nColumns = line0->col[line0->len]; } } } else { for (i = 0; i < nLines; ++i) { line0 = lineArray[i]; col1 = 0; for (j = 0; j < i; ++j) { line1 = lineArray[j]; if (line1->primaryDelta(line0) >= 0) { col2 = line1->col[line1->len] + 1; } else { k = 0; // make gcc happy switch (rot) { case 0: for (k = 0; k < line1->len && line0->xMin >= 0.5 * (line1->edge[k] + line1->edge[k + 1]); ++k) { ; } break; case 1: for (k = 0; k < line1->len && line0->yMin >= 0.5 * (line1->edge[k] + line1->edge[k + 1]); ++k) { ; } break; case 2: for (k = 0; k < line1->len && line0->xMax <= 0.5 * (line1->edge[k] + line1->edge[k + 1]); ++k) { ; } break; case 3: for (k = 0; k < line1->len && line0->yMax <= 0.5 * (line1->edge[k] + line1->edge[k + 1]); ++k) { ; } break; } col2 = line1->col[k]; } if (col2 > col1) { col1 = col2; } } for (k = 0; k <= line0->len; ++k) { line0->col[k] += col1; } if (line0->col[line0->len] > nColumns) { nColumns = line0->col[line0->len]; } } } gfree(lineArray); } void TextBlock::updatePriMinMax(const TextBlock *blk) { double newPriMin, newPriMax; bool gotPriMin, gotPriMax; gotPriMin = gotPriMax = false; newPriMin = newPriMax = 0; // make gcc happy switch (page->primaryRot) { case 0: case 2: if (blk->yMin < yMax && blk->yMax > yMin) { if (blk->xMin < xMin) { newPriMin = blk->xMax; gotPriMin = true; } if (blk->xMax > xMax) { newPriMax = blk->xMin; gotPriMax = true; } } break; case 1: case 3: if (blk->xMin < xMax && blk->xMax > xMin) { if (blk->yMin < yMin) { newPriMin = blk->yMax; gotPriMin = true; } if (blk->yMax > yMax) { newPriMax = blk->yMin; gotPriMax = true; } } break; } if (gotPriMin) { if (newPriMin > xMin) { newPriMin = xMin; } if (newPriMin > priMin) { priMin = newPriMin; } } if (gotPriMax) { if (newPriMax < xMax) { newPriMax = xMax; } if (newPriMax < priMax) { priMax = newPriMax; } } } int TextBlock::cmpXYPrimaryRot(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->xMin - blk2->xMin) == 0) { cmp = blk1->yMin - blk2->yMin; } break; case 1: if ((cmp = blk1->yMin - blk2->yMin) == 0) { cmp = blk2->xMax - blk1->xMax; } break; case 2: if ((cmp = blk2->xMax - blk1->xMax) == 0) { cmp = blk2->yMin - blk1->yMin; } break; case 3: if ((cmp = blk2->yMax - blk1->yMax) == 0) { cmp = blk1->xMax - blk2->xMax; } 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; } int TextBlock::primaryCmp(const TextBlock *blk) const { double cmp; cmp = 0; // make gcc happy switch (rot) { case 0: cmp = xMin - blk->xMin; break; case 1: cmp = yMin - blk->yMin; break; case 2: cmp = blk->xMax - xMax; break; case 3: cmp = blk->yMax - yMax; break; } return cmp < 0 ? -1 : cmp > 0 ? 1 : 0; } double TextBlock::secondaryDelta(const TextBlock *blk) const { double delta; delta = 0; // make gcc happy switch (rot) { case 0: delta = blk->yMin - yMax; break; case 1: delta = xMin - blk->xMax; break; case 2: delta = yMin - blk->yMax; break; case 3: delta = blk->xMin - xMax; break; } return delta; } bool TextBlock::isBelow(const TextBlock *blk) const { bool below; below = false; // make gcc happy switch (page->primaryRot) { case 0: below = xMin >= blk->priMin && xMax <= blk->priMax && yMin > blk->yMin; break; case 1: below = yMin >= blk->priMin && yMax <= blk->priMax && xMax < blk->xMax; break; case 2: below = xMin >= blk->priMin && xMax <= blk->priMax && yMax < blk->yMax; break; case 3: below = yMin >= blk->priMin && yMax <= blk->priMax && xMin > blk->xMin; break; } return below; } bool TextBlock::isBeforeByRule1(const TextBlock *blk1) { bool before = false; bool overlap = false; switch (this->page->primaryRot) { case 0: case 2: overlap = ((this->ExMin <= blk1->ExMin) && (blk1->ExMin <= this->ExMax)) || ((blk1->ExMin <= this->ExMin) && (this->ExMin <= blk1->ExMax)); break; case 1: case 3: overlap = ((this->EyMin <= blk1->EyMin) && (blk1->EyMin <= this->EyMax)) || ((blk1->EyMin <= this->EyMin) && (this->EyMin <= blk1->EyMax)); break; } switch (this->page->primaryRot) { case 0: before = overlap && this->EyMin < blk1->EyMin; break; case 1: before = overlap && this->ExMax > blk1->ExMax; break; case 2: before = overlap && this->EyMax > blk1->EyMax; break; case 3: before = overlap && this->ExMin < blk1->ExMin; break; } return before; } bool TextBlock::isBeforeByRule2(const TextBlock *blk1) { double cmp = 0; int rotLR = rot; if (!page->primaryLR) { rotLR = (rotLR + 2) % 4; } switch (rotLR) { case 0: cmp = ExMax - blk1->ExMin; break; case 1: cmp = EyMin - blk1->EyMax; break; case 2: cmp = blk1->ExMax - ExMin; break; case 3: cmp = blk1->EyMin - EyMax; break; } return cmp <= 0; } // Sort into reading order by performing a topological sort using the rules // given in "High Performance Document Layout Analysis", T.M. Breuel, 2003. // See http://pubs.iupr.org/#2003-breuel-sdiut // Topological sort is done by depth first search, see // http://en.wikipedia.org/wiki/Topological_sorting int TextBlock::visitDepthFirst(TextBlock *blkList, int pos1, TextBlock **sorted, int sortPos, bool *visited, TextBlock **cache, int cacheSize) { int pos2; TextBlock *blk1, *blk2, *blk3; bool before; if (visited[pos1]) { return sortPos; } blk1 = this; #if 0 // for debugging printf("visited: %d %.2f..%.2f %.2f..%.2f\n", sortPos, blk1->ExMin, blk1->ExMax, blk1->EyMin, blk1->EyMax); #endif visited[pos1] = true; pos2 = -1; for (blk2 = blkList; blk2; blk2 = blk2->next) { pos2++; if (visited[pos2]) { // skip visited nodes continue; } before = false; // is blk2 before blk1? (for table entries) if (blk1->tableId >= 0 && blk1->tableId == blk2->tableId) { if (page->primaryLR) { if (blk2->xMax <= blk1->xMin && blk2->yMin <= blk1->yMax && blk2->yMax >= blk1->yMin) { before = true; } } else { if (blk2->xMin >= blk1->xMax && blk2->yMin <= blk1->yMax && blk2->yMax >= blk1->yMin) { before = true; } } if (blk2->yMax <= blk1->yMin) { before = true; } } else { if (blk2->isBeforeByRule1(blk1)) { // Rule (1) blk1 and blk2 overlap, and blk2 is above blk1. before = true; #if 0 // for debugging printf("rule1: %.2f..%.2f %.2f..%.2f %.2f..%.2f %.2f..%.2f\n", blk2->ExMin, blk2->ExMax, blk2->EyMin, blk2->EyMax, blk1->ExMin, blk1->ExMax, blk1->EyMin, blk1->EyMax); #endif } else if (blk2->isBeforeByRule2(blk1)) { // Rule (2) blk2 left of blk1, and no intervening blk3 // such that blk1 is before blk3 by rule 1, // and blk3 is before blk2 by rule 1. before = true; for (int i = 0; i < cacheSize && cache[i]; ++i) { if (blk1->isBeforeByRule1(cache[i]) && cache[i]->isBeforeByRule1(blk2)) { before = false; std::rotate(cache, cache + i, cache + i + 1); break; } } if (before) { for (blk3 = blkList; blk3; blk3 = blk3->next) { if (blk3 == blk2 || blk3 == blk1) { continue; } if (blk1->isBeforeByRule1(blk3) && blk3->isBeforeByRule1(blk2)) { before = false; std::copy_backward(cache, cache + cacheSize - 1, cache + cacheSize); cache[0] = blk3; break; } } } #if 0 // for debugging if (before) { printf("rule2: %.2f..%.2f %.2f..%.2f %.2f..%.2f %.2f..%.2f\n", blk1->ExMin, blk1->ExMax, blk1->EyMin, blk1->EyMax, blk2->ExMin, blk2->ExMax, blk2->EyMin, blk2->EyMax); } #endif } } if (before) { // blk2 is before blk1, so it needs to be visited // before we can add blk1 to the sorted list. sortPos = blk2->visitDepthFirst(blkList, pos2, sorted, sortPos, visited, cache, cacheSize); } } #if 0 // for debugging printf("sorted: %d %.2f..%.2f %.2f..%.2f\n", sortPos, blk1->ExMin, blk1->ExMax, blk1->EyMin, blk1->EyMax); #endif sorted[sortPos++] = blk1; return sortPos; } int TextBlock::visitDepthFirst(TextBlock *blkList, int pos1, TextBlock **sorted, int sortPos, bool *visited) { const int blockCacheSize = 4; TextBlock *blockCache[blockCacheSize]; std::fill(blockCache, blockCache + blockCacheSize, nullptr); return visitDepthFirst(blkList, pos1, sorted, sortPos, visited, blockCache, blockCacheSize); } //------------------------------------------------------------------------ // TextFlow //------------------------------------------------------------------------ TextFlow::TextFlow(TextPage *pageA, TextBlock *blk) { page = pageA; xMin = blk->xMin; xMax = blk->xMax; yMin = blk->yMin; yMax = blk->yMax; priMin = blk->priMin; priMax = blk->priMax; blocks = lastBlk = blk; next = nullptr; } TextFlow::~TextFlow() { TextBlock *blk; while (blocks) { blk = blocks; blocks = blocks->next; delete blk; } } void TextFlow::addBlock(TextBlock *blk) { if (lastBlk) { lastBlk->next = blk; } else { blocks = blk; } lastBlk = blk; if (blk->xMin < xMin) { xMin = blk->xMin; } if (blk->xMax > xMax) { xMax = blk->xMax; } if (blk->yMin < yMin) { yMin = blk->yMin; } if (blk->yMax > yMax) { yMax = blk->yMax; } } bool TextFlow::blockFits(const TextBlock *blk, const TextBlock *prevBlk) const { bool fits; // lower blocks must use smaller fonts if (blk->lines->words->fontSize > lastBlk->lines->words->fontSize) { return false; } fits = false; // make gcc happy switch (page->primaryRot) { case 0: fits = blk->xMin >= priMin && blk->xMax <= priMax; break; case 1: fits = blk->yMin >= priMin && blk->yMax <= priMax; break; case 2: fits = blk->xMin >= priMin && blk->xMax <= priMax; break; case 3: fits = blk->yMin >= priMin && blk->yMax <= priMax; break; } return fits; } #ifdef TEXTOUT_WORD_LIST //------------------------------------------------------------------------ // TextWordList //------------------------------------------------------------------------ TextWordList::TextWordList(const TextPage *text, bool physLayout) { TextFlow *flow; TextBlock *blk; TextLine *line; TextWord *word; TextWord **wordArray; int nWords, i; if (text->rawOrder) { for (word = text->rawWords; word; word = word->next) { words.push_back(word); } } else if (physLayout) { // this is inefficient, but it's also the least useful of these // three cases 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) { for (word = line->words; word; word = word->next) { ++nWords; } } } } wordArray = (TextWord **)gmallocn(nWords, sizeof(TextWord *)); i = 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) { for (word = line->words; word; word = word->next) { wordArray[i++] = word; } } } } qsort(wordArray, nWords, sizeof(TextWord *), &TextWord::cmpYX); for (i = 0; i < nWords; ++i) { words.push_back(wordArray[i]); } gfree(wordArray); } else { 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) { words.push_back(word); } } } } } } TextWordList::~TextWordList() { } int TextWordList::getLength() const { return words.size(); } TextWord *TextWordList::get(int idx) { if (idx < 0 || idx >= (int)words.size()) { return nullptr; } return words[idx]; } #endif // TEXTOUT_WORD_LIST //------------------------------------------------------------------------ // TextPage //------------------------------------------------------------------------ TextPage::TextPage(bool rawOrderA, bool discardDiagA) { int rot; refCnt = 1; rawOrder = rawOrderA; discardDiag = discardDiagA; curWord = nullptr; charPos = 0; curFont = nullptr; curFontSize = 0; nest = 0; nTinyChars = 0; lastCharOverlap = false; if (!rawOrder) { for (rot = 0; rot < 4; ++rot) { pools[rot] = std::make_unique(); } } flows = nullptr; blocks = nullptr; rawWords = nullptr; rawLastWord = nullptr; lastFindXMin = lastFindYMin = 0; haveLastFind = false; mergeCombining = true; diagonal = false; } TextPage::~TextPage() { clear(); } void TextPage::incRefCnt() { refCnt++; } void TextPage::decRefCnt() { if (--refCnt == 0) { delete this; } } void TextPage::startPage(const GfxState *state) { clear(); if (state) { pageWidth = state->getPageWidth(); pageHeight = state->getPageHeight(); } else { pageWidth = pageHeight = 0; } } void TextPage::endPage() { if (curWord) { endWord(); } } void TextPage::clear() { int rot; TextFlow *flow; TextWord *word; if (curWord) { delete curWord; curWord = nullptr; } if (rawOrder) { while (rawWords) { word = rawWords; rawWords = rawWords->next; delete word; } } else { for (rot = 0; rot < 4; ++rot) { pools[rot] = std::make_unique(); } while (flows) { flow = flows; flows = flows->next; delete flow; } gfree(blocks); } fonts.clear(); underlines.clear(); links.clear(); diagonal = false; curWord = nullptr; charPos = 0; curFont = nullptr; curFontSize = 0; nest = 0; nTinyChars = 0; flows = nullptr; blocks = nullptr; rawWords = nullptr; rawLastWord = nullptr; } void TextPage::updateFont(const GfxState *state) { const double *fm; const char *name; int code, mCode, letterCode, anyCode; double w; // get the font info object curFont = nullptr; for (const std::unique_ptr &f : fonts) { if (f->matches(state)) { curFont = f.get(); break; } } if (!curFont) { fonts.emplace_back(std::make_unique(state)); curFont = fonts.back().get(); } // adjust the font size GfxFont *const gfxFont = state->getFont().get(); curFontSize = state->getTransformedFontSize(); if (gfxFont && gfxFont->getType() == fontType3) { // This is a hack which makes it possible to deal with some Type 3 // fonts. The problem is that it's impossible to know what the // base coordinate system used in the font is without actually // rendering the font. This code tries to guess by looking at the // width of the character 'm' (which breaks if the font is a // subset that doesn't contain 'm'). mCode = letterCode = anyCode = -1; for (code = 0; code < 256; ++code) { name = ((Gfx8BitFont *)gfxFont)->getCharName(code); int nameLen = name ? strlen(name) : 0; bool nameOneChar = nameLen == 1 || (nameLen > 1 && name[1] == '\0'); if (nameOneChar && name[0] == 'm') { mCode = code; } if (letterCode < 0 && nameOneChar && ((name[0] >= 'A' && name[0] <= 'Z') || (name[0] >= 'a' && name[0] <= 'z'))) { letterCode = code; } if (anyCode < 0 && name && ((Gfx8BitFont *)gfxFont)->getWidth(code) > 0) { anyCode = code; } } if (mCode >= 0 && (w = ((Gfx8BitFont *)gfxFont)->getWidth(mCode)) > 0) { // 0.6 is a generic average 'm' width -- yes, this is a hack curFontSize *= w / 0.6; } else if (letterCode >= 0 && (w = ((Gfx8BitFont *)gfxFont)->getWidth(letterCode)) > 0) { // even more of a hack: 0.5 is a generic letter width curFontSize *= w / 0.5; } else if (anyCode >= 0 && (w = ((Gfx8BitFont *)gfxFont)->getWidth(anyCode)) > 0) { // better than nothing: 0.5 is a generic character width curFontSize *= w / 0.5; } fm = gfxFont->getFontMatrix(); if (fm[0] != 0) { curFontSize *= fabs(fm[3] / fm[0]); } } } void TextPage::beginWord(const GfxState *state) { const double *fontm; double m[4], m2[4]; int rot; // This check is needed because Type 3 characters can contain // text-drawing operations (when TextPage is being used via // {X,Win}SplashOutputDev rather than TextOutputDev). if (curWord) { ++nest; return; } // compute the rotation state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]); 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]; m2[1] = fontm[0] * m[1] + fontm[1] * m[3]; m2[2] = fontm[2] * m[0] + fontm[3] * m[2]; m2[3] = fontm[2] * m[1] + fontm[3] * m[3]; m[0] = m2[0]; m[1] = m2[1]; m[2] = m2[2]; m[3] = m2[3]; } if (fabs(m[0] * m[3]) > fabs(m[1] * m[2])) { rot = (m[0] > 0 || m[3] < 0) ? 0 : 2; } else { rot = (m[2] > 0) ? 1 : 3; } if (fabs(m[0]) >= fabs(m[1])) { diagonal = fabs(m[1]) > diagonalThreshold * fabs(m[0]); } else { diagonal = fabs(m[0]) > diagonalThreshold * fabs(m[1]); } // for vertical writing mode, the lines are effectively rotated 90 // degrees if (gfxFont && gfxFont->getWMode()) { rot = (rot + 1) & 3; } curWord = new TextWord(state, rot, curFontSize); } void TextPage::addChar(const GfxState *state, double x, double y, double dx, double dy, CharCode c, int nBytes, const Unicode *u, int uLen) { double x1, y1, w1, h1, dx2, dy2, base, sp, delta; bool overlap; int i; int wMode; Matrix mat; // subtract char and word spacing from the dx,dy values sp = state->getCharSpace(); if (c == (CharCode)0x20) { sp += state->getWordSpace(); } state->textTransformDelta(sp * state->getHorizScaling(), 0, &dx2, &dy2); dx -= dx2; dy -= dy2; state->transformDelta(dx, dy, &w1, &h1); // throw away chars that aren't inside the page bounds // (and also do a sanity check on the character size) state->transform(x, y, &x1, &y1); if (x1 + w1 < 0 || x1 > pageWidth || y1 + h1 < 0 || y1 > pageHeight || std::isnan(x1) || std::isnan(y1) || std::isnan(w1) || std::isnan(h1)) { charPos += nBytes; return; } // check the tiny chars limit if (fabs(w1) < 3 && fabs(h1) < 3) { if (++nTinyChars > 50000) { charPos += nBytes; return; } } // break words at space character if (uLen == 1 && UnicodeIsWhitespace(u[0])) { charPos += nBytes; endWord(); return; } else if (uLen == 1 && u[0] == (Unicode)0x0) { // ignore null characters charPos += nBytes; return; } state->getFontTransMat(&mat.m[0], &mat.m[1], &mat.m[2], &mat.m[3]); mat.m[0] *= state->getHorizScaling(); mat.m[1] *= state->getHorizScaling(); mat.m[4] = x1; mat.m[5] = y1; if (mergeCombining && curWord && uLen == 1 && curWord->addCombining(state, curFont, curFontSize, x1, y1, w1, h1, charPos, nBytes, c, u[0], mat)) { charPos += nBytes; return; } // start a new word if: // (1) this character doesn't fall in the right place relative to // the end of the previous word (this places upper and lower // constraints on the position deltas along both the primary // and secondary axes), or // (2) this character overlaps the previous one (duplicated text), or // (3) the previous character was an overlap (we want each duplicated // 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) { 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]; break; case 1: base = x1; sp = y1 - curWord->yMax; delta = y1 - curWord->edge[curWord->len - 1]; break; case 2: base = y1; sp = curWord->xMin - x1; delta = curWord->edge[curWord->len - 1] - x1; break; case 3: base = x1; sp = curWord->yMin - y1; delta = curWord->edge[curWord->len - 1] - y1; break; } overlap = fabs(delta) < dupMaxPriDelta * curWord->fontSize && fabs(base - curWord->base) < dupMaxSecDelta * curWord->fontSize; wMode = curFont->getWMode(); if (overlap || lastCharOverlap || sp < -minDupBreakOverlap * curWord->fontSize || sp > minWordBreakSpace * curWord->fontSize || fabs(base - curWord->base) > 0.5 || curFontSize != curWord->fontSize || wMode != curWord->wMode) { endWord(); } lastCharOverlap = overlap; } else { lastCharOverlap = false; } if (uLen != 0) { // start a new word if needed if (!curWord) { beginWord(state); } // throw away diagonal chars if (discardDiag && diagonal) { charPos += nBytes; return; } // page rotation and/or transform matrices can cause text to be // drawn in reverse order -- in this case, swap the begin/end // coordinates and break text into individual chars if ((curWord->rot == 0 && w1 < 0) || (curWord->rot == 1 && h1 < 0) || (curWord->rot == 2 && w1 > 0) || (curWord->rot == 3 && h1 > 0)) { endWord(); beginWord(state); // throw away diagonal chars if (discardDiag && diagonal) { charPos += nBytes; return; } x1 += w1; y1 += h1; w1 = -w1; h1 = -h1; } // add the characters to the current word w1 /= uLen; h1 /= uLen; for (i = 0; i < uLen; ++i) { curWord->addChar(state, curFont, x1 + i * w1, y1 + i * h1, w1, h1, charPos, nBytes, c, u[i], mat); } } charPos += nBytes; } void TextPage::incCharCount(int nChars) { charPos += nChars; } void TextPage::endWord() { // This check is needed because Type 3 characters can contain // text-drawing operations (when TextPage is being used via // {X,Win}SplashOutputDev rather than TextOutputDev). if (nest > 0) { --nest; return; } if (curWord) { addWord(curWord); curWord = nullptr; } } 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) { delete word; return; } if (rawOrder) { if (rawLastWord) { rawLastWord->next = word; } else { rawWords = word; } rawLastWord = word; } else { pools[word->rot]->addWord(word); } } void TextPage::addUnderline(double x0, double y0, double x1, double y1) { underlines.emplace_back(std::make_unique(x0, y0, x1, y1)); } void TextPage::addLink(int xMin, int yMin, int xMax, int yMax, AnnotLink *link) { links.emplace_back(std::make_unique(xMin, yMin, xMax, yMax, link)); } void TextPage::coalesce(bool physLayout, double fixedPitch, bool doHTML) { coalesce(physLayout, fixedPitch, doHTML, TextOutputDev::minColSpacing1_default); } void TextPage::coalesce(bool physLayout, double fixedPitch, bool doHTML, double minColSpacing1) { TextWord *word0, *word1, *word2; TextLine *line; TextBlock *blkList, *blk, *lastBlk, *blk0, *blk1, *blk2; TextFlow *flow, *lastFlow; int rot, poolMinBaseIdx, baseIdx, startBaseIdx, endBaseIdx; double minBase, maxBase, newMinBase, newMaxBase; double fontSize, colSpace1, colSpace2, lineSpace, intraLineSpace, blkSpace; bool found; int count[4]; int lrCount; int col1, col2; int j, n; if (rawOrder) { primaryRot = 0; primaryLR = true; return; } const UnicodeMap *uMap = globalParams->getTextEncoding(); blkList = nullptr; lastBlk = nullptr; nBlocks = 0; primaryRot = 0; #if 0 // for debugging printf("*** initial words ***\n"); for (rot = 0; rot < 4; ++rot) { pool = pools[rot]; for (baseIdx = pool->minBaseIdx; baseIdx <= pool->maxBaseIdx; ++baseIdx) { for (word0 = pool->getPool(baseIdx); word0; word0 = word0->next) { printf(" word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSize=%.2f rot=%d link=%p '", word0->xMin, word0->xMax, word0->yMin, word0->yMax, word0->base, word0->fontSize, rot*90, word0->link); for (i = 0; i < word0->len; ++i) { fputc(word0->text[i] & 0xff, stdout); } printf("'\n"); } } } printf("\n"); #endif #if 0 //~ for debugging for (i = 0; i < underlines->getLength(); ++i) { underline = (TextUnderline *)underlines->get(i); printf("underline: x=%g..%g y=%g..%g horiz=%d\n", underline->x0, underline->x1, underline->y0, underline->y1, underline->horiz); } #endif if (doHTML) { //----- handle underlining for (const std::unique_ptr &underline : underlines) { if (underline->horiz) { // rot = 0 if (pools[0]->minBaseIdx <= pools[0]->maxBaseIdx) { startBaseIdx = pools[0]->getBaseIdx(underline->y0 + minUnderlineGap); endBaseIdx = pools[0]->getBaseIdx(underline->y0 + maxUnderlineGap); for (j = startBaseIdx; j <= endBaseIdx; ++j) { for (word0 = pools[0]->getPool(j); word0; word0 = word0->next) { //~ need to check the y value against the word baseline if (underline->x0 < word0->xMin + underlineSlack && word0->xMax - underlineSlack < underline->x1) { word0->underlined = true; } } } } // rot = 2 if (pools[2]->minBaseIdx <= pools[2]->maxBaseIdx) { startBaseIdx = pools[2]->getBaseIdx(underline->y0 - maxUnderlineGap); endBaseIdx = pools[2]->getBaseIdx(underline->y0 - minUnderlineGap); for (j = startBaseIdx; j <= endBaseIdx; ++j) { for (word0 = pools[2]->getPool(j); word0; word0 = word0->next) { if (underline->x0 < word0->xMin + underlineSlack && word0->xMax - underlineSlack < underline->x1) { word0->underlined = true; } } } } } else { // rot = 1 if (pools[1]->minBaseIdx <= pools[1]->maxBaseIdx) { startBaseIdx = pools[1]->getBaseIdx(underline->x0 - maxUnderlineGap); endBaseIdx = pools[1]->getBaseIdx(underline->x0 - minUnderlineGap); for (j = startBaseIdx; j <= endBaseIdx; ++j) { for (word0 = pools[1]->getPool(j); word0; word0 = word0->next) { if (underline->y0 < word0->yMin + underlineSlack && word0->yMax - underlineSlack < underline->y1) { word0->underlined = true; } } } } // rot = 3 if (pools[3]->minBaseIdx <= pools[3]->maxBaseIdx) { startBaseIdx = pools[3]->getBaseIdx(underline->x0 + minUnderlineGap); endBaseIdx = pools[3]->getBaseIdx(underline->x0 + maxUnderlineGap); for (j = startBaseIdx; j <= endBaseIdx; ++j) { for (word0 = pools[3]->getPool(j); word0; word0 = word0->next) { if (underline->y0 < word0->yMin + underlineSlack && word0->yMax - underlineSlack < underline->y1) { word0->underlined = true; } } } } } } //----- handle links for (const std::unique_ptr &link : links) { // rot = 0 if (pools[0]->minBaseIdx <= pools[0]->maxBaseIdx) { startBaseIdx = pools[0]->getBaseIdx(link->yMin); endBaseIdx = pools[0]->getBaseIdx(link->yMax); for (j = startBaseIdx; j <= endBaseIdx; ++j) { for (word0 = pools[0]->getPool(j); word0; word0 = word0->next) { if (link->xMin < word0->xMin + hyperlinkSlack && word0->xMax - hyperlinkSlack < link->xMax && link->yMin < word0->yMin + hyperlinkSlack && word0->yMax - hyperlinkSlack < link->yMax) { word0->link = link->link; } } } } // rot = 2 if (pools[2]->minBaseIdx <= pools[2]->maxBaseIdx) { startBaseIdx = pools[2]->getBaseIdx(link->yMin); endBaseIdx = pools[2]->getBaseIdx(link->yMax); for (j = startBaseIdx; j <= endBaseIdx; ++j) { for (word0 = pools[2]->getPool(j); word0; word0 = word0->next) { if (link->xMin < word0->xMin + hyperlinkSlack && word0->xMax - hyperlinkSlack < link->xMax && link->yMin < word0->yMin + hyperlinkSlack && word0->yMax - hyperlinkSlack < link->yMax) { word0->link = link->link; } } } } // rot = 1 if (pools[1]->minBaseIdx <= pools[1]->maxBaseIdx) { startBaseIdx = pools[1]->getBaseIdx(link->xMin); endBaseIdx = pools[1]->getBaseIdx(link->xMax); for (j = startBaseIdx; j <= endBaseIdx; ++j) { for (word0 = pools[1]->getPool(j); word0; word0 = word0->next) { if (link->yMin < word0->yMin + hyperlinkSlack && word0->yMax - hyperlinkSlack < link->yMax && link->xMin < word0->xMin + hyperlinkSlack && word0->xMax - hyperlinkSlack < link->xMax) { word0->link = link->link; } } } } // rot = 3 if (pools[3]->minBaseIdx <= pools[3]->maxBaseIdx) { startBaseIdx = pools[3]->getBaseIdx(link->xMin); endBaseIdx = pools[3]->getBaseIdx(link->xMax); for (j = startBaseIdx; j <= endBaseIdx; ++j) { for (word0 = pools[3]->getPool(j); word0; word0 = word0->next) { if (link->yMin < word0->yMin + hyperlinkSlack && word0->yMax - hyperlinkSlack < link->yMax && link->xMin < word0->xMin + hyperlinkSlack && word0->xMax - hyperlinkSlack < link->xMax) { word0->link = link->link; } } } } } } //----- assemble the blocks //~ add an outer loop for writing mode (vertical text) // build blocks for each rotation value for (rot = 0; rot < 4; ++rot) { std::unique_ptr &pool = pools[rot]; poolMinBaseIdx = pool->minBaseIdx; count[rot] = 0; // add blocks until no more words are left while (true) { // find the first non-empty line in the pool for (; poolMinBaseIdx <= pool->maxBaseIdx && !pool->getPool(poolMinBaseIdx); ++poolMinBaseIdx) { ; } if (poolMinBaseIdx > pool->maxBaseIdx) { break; } // look for the left-most word in the first four lines of the // pool -- this avoids starting with a superscript word startBaseIdx = poolMinBaseIdx; for (baseIdx = poolMinBaseIdx + 1; baseIdx < poolMinBaseIdx + 4 && baseIdx <= pool->maxBaseIdx; ++baseIdx) { if (!pool->getPool(baseIdx)) { continue; } if (pool->getPool(baseIdx)->primaryCmp(pool->getPool(startBaseIdx)) < 0) { startBaseIdx = baseIdx; } } // create a new block word0 = pool->getPool(startBaseIdx); pool->setPool(startBaseIdx, word0->next); word0->next = nullptr; blk = new TextBlock(this, rot); blk->addWord(word0); fontSize = word0->fontSize; minBase = maxBase = word0->base; colSpace1 = minColSpacing1 * fontSize; colSpace2 = minColSpacing2 * fontSize; lineSpace = maxLineSpacingDelta * fontSize; intraLineSpace = maxIntraLineDelta * fontSize; // add words to the block do { found = false; // look for words on the line above the current top edge of // the block newMinBase = minBase; for (baseIdx = pool->getBaseIdx(minBase); baseIdx >= pool->getBaseIdx(minBase - lineSpace); --baseIdx) { word0 = nullptr; word1 = pool->getPool(baseIdx); while (word1) { if (word1->base < minBase && word1->base >= minBase - lineSpace && ((rot == 0 || rot == 2) ? (word1->xMin < blk->xMax && word1->xMax > blk->xMin) : (word1->yMin < blk->yMax && word1->yMax > blk->yMin)) && fabs(word1->fontSize - fontSize) < maxBlockFontSizeDelta1 * fontSize) { word2 = word1; if (word0) { word0->next = word1->next; } else { pool->setPool(baseIdx, word1->next); } word1 = word1->next; word2->next = nullptr; blk->addWord(word2); found = true; newMinBase = word2->base; } else { word0 = word1; word1 = word1->next; } } } minBase = newMinBase; // look for words on the line below the current bottom edge of // the block newMaxBase = maxBase; for (baseIdx = pool->getBaseIdx(maxBase); baseIdx <= pool->getBaseIdx(maxBase + lineSpace); ++baseIdx) { word0 = nullptr; word1 = pool->getPool(baseIdx); while (word1) { if (word1->base > maxBase && word1->base <= maxBase + lineSpace && ((rot == 0 || rot == 2) ? (word1->xMin < blk->xMax && word1->xMax > blk->xMin) : (word1->yMin < blk->yMax && word1->yMax > blk->yMin)) && fabs(word1->fontSize - fontSize) < maxBlockFontSizeDelta1 * fontSize) { word2 = word1; if (word0) { word0->next = word1->next; } else { pool->setPool(baseIdx, word1->next); } word1 = word1->next; word2->next = nullptr; blk->addWord(word2); found = true; newMaxBase = word2->base; } else { word0 = word1; word1 = word1->next; } } } maxBase = newMaxBase; // look for words that are on lines already in the block, and // that overlap the block horizontally for (baseIdx = pool->getBaseIdx(minBase - intraLineSpace); baseIdx <= pool->getBaseIdx(maxBase + intraLineSpace); ++baseIdx) { word0 = nullptr; word1 = pool->getPool(baseIdx); while (word1) { if (word1->base >= minBase - intraLineSpace && word1->base <= maxBase + intraLineSpace && ((rot == 0 || rot == 2) ? (word1->xMin < blk->xMax + colSpace1 && word1->xMax > blk->xMin - colSpace1) : (word1->yMin < blk->yMax + colSpace1 && word1->yMax > blk->yMin - colSpace1)) && fabs(word1->fontSize - fontSize) < maxBlockFontSizeDelta2 * fontSize) { word2 = word1; if (word0) { word0->next = word1->next; } else { pool->setPool(baseIdx, word1->next); } word1 = word1->next; word2->next = nullptr; blk->addWord(word2); found = true; } else { word0 = word1; word1 = word1->next; } } } // only check for outlying words (the next two chunks of code) // if we didn't find anything else if (found) { continue; } // scan down the left side of the block, looking for words // that are near (but not overlapping) the block; if there are // three or fewer, add them to the block n = 0; for (baseIdx = pool->getBaseIdx(minBase - intraLineSpace); baseIdx <= pool->getBaseIdx(maxBase + intraLineSpace); ++baseIdx) { word1 = pool->getPool(baseIdx); while (word1) { if (word1->base >= minBase - intraLineSpace && word1->base <= maxBase + intraLineSpace && ((rot == 0 || rot == 2) ? (word1->xMax <= blk->xMin && word1->xMax > blk->xMin - colSpace2) : (word1->yMax <= blk->yMin && word1->yMax > blk->yMin - colSpace2)) && fabs(word1->fontSize - fontSize) < maxBlockFontSizeDelta3 * fontSize) { ++n; break; } word1 = word1->next; } } if (n > 0 && n <= 3) { for (baseIdx = pool->getBaseIdx(minBase - intraLineSpace); baseIdx <= pool->getBaseIdx(maxBase + intraLineSpace); ++baseIdx) { word0 = nullptr; word1 = pool->getPool(baseIdx); while (word1) { if (word1->base >= minBase - intraLineSpace && word1->base <= maxBase + intraLineSpace && ((rot == 0 || rot == 2) ? (word1->xMax <= blk->xMin && word1->xMax > blk->xMin - colSpace2) : (word1->yMax <= blk->yMin && word1->yMax > blk->yMin - colSpace2)) && fabs(word1->fontSize - fontSize) < maxBlockFontSizeDelta3 * fontSize) { word2 = word1; if (word0) { word0->next = word1->next; } else { pool->setPool(baseIdx, word1->next); } word1 = word1->next; word2->next = nullptr; blk->addWord(word2); if (word2->base < minBase) { minBase = word2->base; } else if (word2->base > maxBase) { maxBase = word2->base; } found = true; break; } else { word0 = word1; word1 = word1->next; } } } } // scan down the right side of the block, looking for words // that are near (but not overlapping) the block; if there are // three or fewer, add them to the block n = 0; for (baseIdx = pool->getBaseIdx(minBase - intraLineSpace); baseIdx <= pool->getBaseIdx(maxBase + intraLineSpace); ++baseIdx) { word1 = pool->getPool(baseIdx); while (word1) { if (word1->base >= minBase - intraLineSpace && word1->base <= maxBase + intraLineSpace && ((rot == 0 || rot == 2) ? (word1->xMin >= blk->xMax && word1->xMin < blk->xMax + colSpace2) : (word1->yMin >= blk->yMax && word1->yMin < blk->yMax + colSpace2)) && fabs(word1->fontSize - fontSize) < maxBlockFontSizeDelta3 * fontSize) { ++n; break; } word1 = word1->next; } } if (n > 0 && n <= 3) { for (baseIdx = pool->getBaseIdx(minBase - intraLineSpace); baseIdx <= pool->getBaseIdx(maxBase + intraLineSpace); ++baseIdx) { word0 = nullptr; word1 = pool->getPool(baseIdx); while (word1) { if (word1->base >= minBase - intraLineSpace && word1->base <= maxBase + intraLineSpace && ((rot == 0 || rot == 2) ? (word1->xMin >= blk->xMax && word1->xMin < blk->xMax + colSpace2) : (word1->yMin >= blk->yMax && word1->yMin < blk->yMax + colSpace2)) && fabs(word1->fontSize - fontSize) < maxBlockFontSizeDelta3 * fontSize) { word2 = word1; if (word0) { word0->next = word1->next; } else { pool->setPool(baseIdx, word1->next); } word1 = word1->next; word2->next = nullptr; blk->addWord(word2); if (word2->base < minBase) { minBase = word2->base; } else if (word2->base > maxBase) { maxBase = word2->base; } found = true; break; } else { word0 = word1; word1 = word1->next; } } } } } while (found); //~ need to compute the primary writing mode (horiz/vert) in //~ addition to primary rotation // coalesce the block, and add it to the list blk->coalesce(uMap, fixedPitch); if (lastBlk) { lastBlk->next = blk; } else { blkList = blk; } lastBlk = blk; count[rot] += blk->charCount; ++nBlocks; } if (count[rot] > count[primaryRot]) { primaryRot = rot; } } #if 0 // for debugging printf("*** rotation ***\n"); for (rot = 0; rot < 4; ++rot) { printf(" %d: %6d\n", rot, count[rot]); } printf(" primary rot = %d\n", primaryRot); printf("\n"); #endif #if 0 // for debugging printf("*** blocks ***\n"); for (blk = blkList; blk; blk = blk->next) { printf("block: rot=%d x=%.2f..%.2f y=%.2f..%.2f\n", blk->rot, blk->xMin, blk->xMax, blk->yMin, blk->yMax); for (line = blk->lines; line; line = line->next) { printf(" line: x=%.2f..%.2f y=%.2f..%.2f base=%.2f\n", line->xMin, line->xMax, line->yMin, line->yMax, line->base); for (word0 = line->words; word0; word0 = word0->next) { printf(" word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSize=%.2f space=%d: '", word0->xMin, word0->xMax, word0->yMin, word0->yMax, word0->base, word0->fontSize, word0->spaceAfter); for (i = 0; i < word0->len; ++i) { fputc(word0->text[i] & 0xff, stdout); } printf("'\n"); } } } printf("\n"); #endif // determine the primary direction lrCount = 0; 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])) { ++lrCount; } else if (unicodeTypeR(word0->text[i])) { --lrCount; } } } } } primaryLR = lrCount >= 0; #if 0 // for debugging printf("*** direction ***\n"); printf("lrCount = %d\n", lrCount); printf("primaryLR = %d\n", primaryLR); #endif //----- column assignment // sort blocks into xy order for column assignment if (blocks) { gfree(blocks); } if (physLayout && fixedPitch) { blocks = (TextBlock **)gmallocn(nBlocks, sizeof(TextBlock *)); int i; for (blk = blkList, i = 0; blk; blk = blk->next, ++i) { blocks[i] = blk; col1 = 0; // make gcc happy switch (primaryRot) { case 0: col1 = (int)(blk->xMin / fixedPitch + 0.5); break; case 1: col1 = (int)(blk->yMin / fixedPitch + 0.5); break; case 2: col1 = (int)((pageWidth - blk->xMax) / fixedPitch + 0.5); break; case 3: col1 = (int)((pageHeight - blk->yMax) / fixedPitch + 0.5); break; } blk->col = col1; for (line = blk->lines; line; line = line->next) { for (j = 0; j <= line->len; ++j) { line->col[j] += col1; } } } } else { // sort blocks into xy order for column assignment blocks = (TextBlock **)gmallocn(nBlocks, sizeof(TextBlock *)); int i; for (blk = blkList, i = 0; blk; blk = blk->next, ++i) { blocks[i] = blk; } if (blocks) { qsort(blocks, nBlocks, sizeof(TextBlock *), &TextBlock::cmpXYPrimaryRot); } // column assignment for (i = 0; i < nBlocks; ++i) { blk0 = blocks[i]; col1 = 0; for (j = 0; j < i; ++j) { blk1 = blocks[j]; col2 = 0; // make gcc happy switch (primaryRot) { case 0: if (blk0->xMin > blk1->xMax) { col2 = blk1->col + blk1->nColumns + 3; } else if (blk1->xMax == blk1->xMin) { col2 = blk1->col; } else { col2 = blk1->col + (int)(((blk0->xMin - blk1->xMin) / (blk1->xMax - blk1->xMin)) * blk1->nColumns); } break; case 1: if (blk0->yMin > blk1->yMax) { col2 = blk1->col + blk1->nColumns + 3; } else if (blk1->yMax == blk1->yMin) { col2 = blk1->col; } else { col2 = blk1->col + (int)(((blk0->yMin - blk1->yMin) / (blk1->yMax - blk1->yMin)) * blk1->nColumns); } break; case 2: if (blk0->xMax < blk1->xMin) { col2 = blk1->col + blk1->nColumns + 3; } else if (blk1->xMin == blk1->xMax) { col2 = blk1->col; } else { col2 = blk1->col + (int)(((blk0->xMax - blk1->xMax) / (blk1->xMin - blk1->xMax)) * blk1->nColumns); } break; case 3: if (blk0->yMax < blk1->yMin) { col2 = blk1->col + blk1->nColumns + 3; } else if (blk1->yMin == blk1->yMax) { col2 = blk1->col; } else { col2 = blk1->col + (int)(((blk0->yMax - blk1->yMax) / (blk1->yMin - blk1->yMax)) * blk1->nColumns); } break; } if (col2 > col1) { col1 = col2; } } blk0->col = col1; for (line = blk0->lines; line; line = line->next) { for (j = 0; j <= line->len; ++j) { line->col[j] += col1; } } } } #if 0 // for debugging printf("*** blocks, after column assignment ***\n"); for (blk = blkList; blk; blk = blk->next) { printf("block: rot=%d x=%.2f..%.2f y=%.2f..%.2f col=%d nCols=%d\n", blk->rot, blk->xMin, blk->xMax, blk->yMin, blk->yMax, blk->col, blk->nColumns); for (line = blk->lines; line; line = line->next) { printf(" line: col[0]=%d\n", line->col[0]); for (word0 = line->words; word0; word0 = word0->next) { printf(" word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSize=%.2f space=%d: '", word0->xMin, word0->xMax, word0->yMin, word0->yMax, word0->base, word0->fontSize, word0->spaceAfter); for (i = 0; i < word0->len; ++i) { fputc(word0->text[i] & 0xff, stdout); } printf("'\n"); } } } printf("\n"); #endif //----- reading order sort // compute space on left and right sides of each block for (int i = 0; i < nBlocks; ++i) { blk0 = blocks[i]; for (j = 0; j < nBlocks; ++j) { blk1 = blocks[j]; if (blk1 != blk0) { blk0->updatePriMinMax(blk1); } } } #if 0 // for debugging printf("PAGE\n"); #endif int sortPos = 0; bool *visited = (bool *)gmallocn(nBlocks, sizeof(bool)); for (int i = 0; i < nBlocks; i++) { visited[i] = false; } double bxMin0, byMin0, bxMin1, byMin1; int numTables = 0; int tableId = -1; int correspondenceX, correspondenceY; double xCentre1, yCentre1, xCentre2, yCentre2; double xCentre3, yCentre3, xCentre4, yCentre4; double deltaX, deltaY; TextBlock *fblk2 = nullptr, *fblk3 = nullptr, *fblk4 = nullptr; for (blk1 = blkList; blk1; blk1 = blk1->next) { blk1->ExMin = blk1->xMin; blk1->ExMax = blk1->xMax; blk1->EyMin = blk1->yMin; blk1->EyMax = blk1->yMax; bxMin0 = DBL_MAX; byMin0 = DBL_MAX; bxMin1 = DBL_MAX; byMin1 = DBL_MAX; fblk2 = nullptr; fblk3 = nullptr; fblk4 = nullptr; /* find fblk2, fblk3 and fblk4 so that * fblk2 is on the right of blk1 and overlap with blk1 in y axis * fblk3 is under blk1 and overlap with blk1 in x axis * fblk4 is under blk1 and on the right of blk1 * and they are closest to blk1 */ for (blk2 = blkList; blk2; blk2 = blk2->next) { if (blk2 != blk1) { if (blk2->yMin <= blk1->yMax && blk2->yMax >= blk1->yMin && blk2->xMin > blk1->xMax && blk2->xMin < bxMin0) { bxMin0 = blk2->xMin; fblk2 = blk2; } else if (blk2->xMin <= blk1->xMax && blk2->xMax >= blk1->xMin && blk2->yMin > blk1->yMax && blk2->yMin < byMin0) { byMin0 = blk2->yMin; fblk3 = blk2; } else if (blk2->xMin > blk1->xMax && blk2->xMin < bxMin1 && blk2->yMin > blk1->yMax && blk2->yMin < byMin1) { bxMin1 = blk2->xMin; byMin1 = blk2->yMin; fblk4 = blk2; } } } /* fblk4 can not overlap with fblk3 in x and with fblk2 in y * fblk2 can not overlap with fblk3 in x and y * fblk4 has to overlap with fblk3 in y and with fblk2 in x */ if (fblk2 != nullptr && fblk3 != nullptr && fblk4 != nullptr) { if (((fblk3->xMin <= fblk4->xMax && fblk3->xMax >= fblk4->xMin) || (fblk2->yMin <= fblk4->yMax && fblk2->yMax >= fblk4->yMin) || (fblk2->xMin <= fblk3->xMax && fblk2->xMax >= fblk3->xMin) || (fblk2->yMin <= fblk3->yMax && fblk2->yMax >= fblk3->yMin)) || !(fblk4->xMin <= fblk2->xMax && fblk4->xMax >= fblk2->xMin && fblk4->yMin <= fblk3->yMax && fblk4->yMax >= fblk3->yMin)) { fblk2 = nullptr; fblk3 = nullptr; fblk4 = nullptr; } } // if we found any then look whether they form a table if (fblk2 != nullptr && fblk3 != nullptr && fblk4 != nullptr) { tableId = -1; correspondenceX = 0; correspondenceY = 0; deltaX = 0.0; deltaY = 0.0; if (blk1->lines && blk1->lines->words) { deltaX = blk1->lines->words->getFontSize(); } if (fblk2->lines && fblk2->lines->words) { deltaX = deltaX < fblk2->lines->words->getFontSize() ? deltaX : fblk2->lines->words->getFontSize(); } if (fblk3->lines && fblk3->lines->words) { deltaX = deltaX < fblk3->lines->words->getFontSize() ? deltaX : fblk3->lines->words->getFontSize(); } if (fblk4->lines && fblk4->lines->words) { deltaX = deltaX < fblk4->lines->words->getFontSize() ? deltaX : fblk4->lines->words->getFontSize(); } deltaY = deltaX; deltaX *= minColSpacing1; deltaY *= maxIntraLineDelta; xCentre1 = (blk1->xMax + blk1->xMin) / 2.0; yCentre1 = (blk1->yMax + blk1->yMin) / 2.0; xCentre2 = (fblk2->xMax + fblk2->xMin) / 2.0; yCentre2 = (fblk2->yMax + fblk2->yMin) / 2.0; xCentre3 = (fblk3->xMax + fblk3->xMin) / 2.0; yCentre3 = (fblk3->yMax + fblk3->yMin) / 2.0; xCentre4 = (fblk4->xMax + fblk4->xMin) / 2.0; yCentre4 = (fblk4->yMax + fblk4->yMin) / 2.0; // are blocks centrally aligned in x ? if (fabs(xCentre1 - xCentre3) <= deltaX && fabs(xCentre2 - xCentre4) <= deltaX) { correspondenceX++; } // are blocks centrally aligned in y ? if (fabs(yCentre1 - yCentre2) <= deltaY && fabs(yCentre3 - yCentre4) <= deltaY) { correspondenceY++; } // are blocks aligned to the left ? if (fabs(blk1->xMin - fblk3->xMin) <= deltaX && fabs(fblk2->xMin - fblk4->xMin) <= deltaX) { correspondenceX++; } // are blocks aligned to the right ? if (fabs(blk1->xMax - fblk3->xMax) <= deltaX && fabs(fblk2->xMax - fblk4->xMax) <= deltaX) { correspondenceX++; } // are blocks aligned to the top ? if (fabs(blk1->yMin - fblk2->yMin) <= deltaY && fabs(fblk3->yMin - fblk4->yMin) <= deltaY) { correspondenceY++; } // are blocks aligned to the bottom ? if (fabs(blk1->yMax - fblk2->yMax) <= deltaY && fabs(fblk3->yMax - fblk4->yMax) <= deltaY) { correspondenceY++; } // are blocks aligned in x and y ? if (correspondenceX > 0 && correspondenceY > 0) { // find maximal tableId tableId = tableId < fblk4->tableId ? fblk4->tableId : tableId; tableId = tableId < fblk3->tableId ? fblk3->tableId : tableId; tableId = tableId < fblk2->tableId ? fblk2->tableId : tableId; tableId = tableId < blk1->tableId ? blk1->tableId : tableId; // if the tableId is -1, then we found new table if (tableId < 0) { tableId = numTables; numTables++; } blk1->tableId = tableId; fblk2->tableId = tableId; fblk3->tableId = tableId; fblk4->tableId = tableId; } } } /* set extended bounding boxes of all table entries * so that they contain whole table * (we need to process whole table size when comparing it * with regular text blocks) */ PDFRectangle *envelopes = new PDFRectangle[numTables]; TextBlock **ending_blocks = new TextBlock *[numTables]; for (int i = 0; i < numTables; i++) { envelopes[i].x1 = DBL_MAX; envelopes[i].x2 = DBL_MIN; envelopes[i].y1 = DBL_MAX; envelopes[i].y2 = DBL_MIN; ending_blocks[i] = nullptr; } for (blk1 = blkList; blk1; blk1 = blk1->next) { if (blk1->tableId >= 0) { if (blk1->ExMin < envelopes[blk1->tableId].x1) { envelopes[blk1->tableId].x1 = blk1->ExMin; if (!blk1->page->primaryLR) { ending_blocks[blk1->tableId] = blk1; } } if (blk1->ExMax > envelopes[blk1->tableId].x2) { envelopes[blk1->tableId].x2 = blk1->ExMax; if (blk1->page->primaryLR) { ending_blocks[blk1->tableId] = blk1; } } envelopes[blk1->tableId].y1 = blk1->EyMin < envelopes[blk1->tableId].y1 ? blk1->EyMin : envelopes[blk1->tableId].y1; envelopes[blk1->tableId].y2 = blk1->EyMax > envelopes[blk1->tableId].y2 ? blk1->EyMax : envelopes[blk1->tableId].y2; } } for (blk1 = blkList; blk1; blk1 = blk1->next) { if (blk1->tableId >= 0 && ending_blocks[blk1->tableId] && blk1->xMin <= ending_blocks[blk1->tableId]->xMax && blk1->xMax >= ending_blocks[blk1->tableId]->xMin) { blk1->tableEnd = true; } } for (blk1 = blkList; blk1; blk1 = blk1->next) { if (blk1->tableId >= 0) { blk1->ExMin = envelopes[blk1->tableId].x1; blk1->ExMax = envelopes[blk1->tableId].x2; blk1->EyMin = envelopes[blk1->tableId].y1; blk1->EyMax = envelopes[blk1->tableId].y2; } } delete[] envelopes; delete[] ending_blocks; /* set extended bounding boxes of all other blocks * so that they extend in x without hitting neighbours */ for (blk1 = blkList; blk1; blk1 = blk1->next) { if (!(blk1->tableId >= 0)) { double xMax = DBL_MAX; double xMin = DBL_MIN; for (blk2 = blkList; blk2; blk2 = blk2->next) { if (blk2 == blk1) { continue; } if (blk1->yMin <= blk2->yMax && blk1->yMax >= blk2->yMin) { if (blk2->xMin < xMax && blk2->xMin > blk1->xMax) { xMax = blk2->xMin; } if (blk2->xMax > xMin && blk2->xMax < blk1->xMin) { xMin = blk2->xMax; } } } for (blk2 = blkList; blk2; blk2 = blk2->next) { if (blk2 == blk1) { continue; } if (blk2->xMax > blk1->ExMax && blk2->xMax <= xMax && blk2->yMin >= blk1->yMax) { blk1->ExMax = blk2->xMax; } if (blk2->xMin < blk1->ExMin && blk2->xMin >= xMin && blk2->yMin >= blk1->yMax) { blk1->ExMin = blk2->xMin; } } } } int i = -1; for (blk1 = blkList; blk1; blk1 = blk1->next) { i++; sortPos = blk1->visitDepthFirst(blkList, i, blocks, sortPos, visited); } if (visited) { gfree(visited); } #if 0 // for debugging printf("*** blocks, after ro sort ***\n"); for (i = 0; i < nBlocks; ++i) { blk = blocks[i]; printf("block: rot=%d x=%.2f..%.2f y=%.2f..%.2f space=%.2f..%.2f\n", blk->rot, blk->xMin, blk->xMax, blk->yMin, blk->yMax, blk->priMin, blk->priMax); for (line = blk->lines; line; line = line->next) { printf(" line:\n"); for (word0 = line->words; word0; word0 = word0->next) { printf(" word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSize=%.2f space=%d: '", word0->xMin, word0->xMax, word0->yMin, word0->yMax, word0->base, word0->fontSize, word0->spaceAfter); for (j = 0; j < word0->len; ++j) { fputc(word0->text[j] & 0xff, stdout); } printf("'\n"); } } } printf("\n"); fflush(stdout); #endif // build the flows //~ this needs to be adjusted for writing mode (vertical text) //~ this also needs to account for right-to-left column ordering while (flows) { flow = flows; flows = flows->next; delete flow; } flow = nullptr; flows = lastFlow = nullptr; // assume blocks are already in reading order, // and construct flows accordingly. for (i = 0; i < nBlocks; i++) { blk = blocks[i]; blk->next = nullptr; if (flow) { blk1 = blocks[i - 1]; blkSpace = maxBlockSpacing * blk1->lines->words->fontSize; if (blk1->secondaryDelta(blk) <= blkSpace && blk->isBelow(blk1) && flow->blockFits(blk, blk1)) { flow->addBlock(blk); continue; } } flow = new TextFlow(this, blk); if (lastFlow) { lastFlow->next = flow; } else { flows = flow; } lastFlow = flow; } #if 0 // for debugging printf("*** flows ***\n"); for (flow = flows; flow; flow = flow->next) { printf("flow: x=%.2f..%.2f y=%.2f..%.2f pri:%.2f..%.2f\n", flow->xMin, flow->xMax, flow->yMin, flow->yMax, flow->priMin, flow->priMax); for (blk = flow->blocks; blk; blk = blk->next) { printf(" block: rot=%d x=%.2f..%.2f y=%.2f..%.2f pri=%.2f..%.2f\n", blk->rot, blk->ExMin, blk->ExMax, blk->EyMin, blk->EyMax, blk->priMin, blk->priMax); for (line = blk->lines; line; line = line->next) { printf(" line:\n"); for (word0 = line->words; word0; word0 = word0->next) { printf(" word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSize=%.2f space=%d: '", word0->xMin, word0->xMax, word0->yMin, word0->yMax, word0->base, word0->fontSize, word0->spaceAfter); for (i = 0; i < word0->len; ++i) { fputc(word0->text[i] & 0xff, stdout); } printf("'\n"); } } } } printf("\n"); #endif } void TextPage::adjustRotation(TextLine *line, int start, int end, double *xMin, double *xMax, double *yMin, double *yMax) { switch (line->rot) { case 0: *xMin = line->edge[start]; *xMax = line->edge[end]; *yMin = line->yMin; *yMax = line->yMax; break; case 1: *xMin = line->xMin; *xMax = line->xMax; *yMin = line->edge[start]; *yMax = line->edge[end]; break; case 2: *xMin = line->edge[end]; *xMax = line->edge[start]; *yMin = line->yMin; *yMax = line->yMax; break; case 3: *xMin = line->xMin; *xMax = line->xMax; *yMin = line->edge[end]; *yMax = line->edge[start]; break; } } bool TextPage::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) { return findText(s, len, startAtTop, stopAtBottom, startAtLast, stopAtLast, caseSensitive, false, false, backward, wholeWord, xMin, yMin, xMax, yMax, nullptr, nullptr); } bool TextPage::findText(const Unicode *s, int len, bool startAtTop, bool stopAtBottom, bool startAtLast, bool stopAtLast, bool caseSensitive, bool ignoreDiacritics, bool backward, bool wholeWord, double *xMin, double *yMin, double *xMax, double *yMax) { return findText(s, len, startAtTop, stopAtBottom, startAtLast, stopAtLast, caseSensitive, ignoreDiacritics, false, backward, wholeWord, xMin, yMin, xMax, yMax, nullptr, nullptr); } bool TextPage::findText(const Unicode *s, int len, bool startAtTop, bool stopAtBottom, bool startAtLast, bool stopAtLast, bool caseSensitive, bool ignoreDiacritics, bool matchAcrossLines, bool backward, bool wholeWord, double *xMin, double *yMin, double *xMax, double *yMax, PDFRectangle *continueMatch, bool *ignoredHyphen) { TextBlock *blk; TextLine *line; Unicode *s2, *txt, *reordered; Unicode *p; Unicode *nextline; int nextline_len; bool nextlineAfterHyphen = false; int txtSize, m, i, j, k; double xStart, yStart, xStop, yStop; double xMin0, yMin0, xMax0, yMax0; double xMin1, yMin1, xMax1, yMax1; double xMin2, yMin2, xMax2, yMax2; bool found; if (len == 0) { return false; } if (rawOrder) { return false; } if (matchAcrossLines && backward) { // matchAcrossLines is unimplemented for backward search matchAcrossLines = false; } // handle right-to-left text reordered = (Unicode *)gmallocn(len, sizeof(Unicode)); reorderText(s, len, nullptr, primaryLR, nullptr, reordered); // normalize the search string s2 = unicodeNormalizeNFKC(reordered, len, &len, nullptr); // if search string is not pure ascii then don't // use ignoreDiacritics (as they won't match) if (!caseSensitive) { // convert the search string to uppercase for (i = 0; i < len; ++i) { s2[i] = unicodeToUpper(s2[i]); if (ignoreDiacritics && !isAscii7(s2[i])) { ignoreDiacritics = false; } } } else if (ignoreDiacritics) { for (i = 0; i < len; ++i) { if (!isAscii7(s2[i])) { ignoreDiacritics = false; break; } } } txt = nullptr; txtSize = 0; xStart = yStart = xStop = yStop = 0; if (startAtLast && haveLastFind) { xStart = lastFindXMin; yStart = lastFindYMin; } else if (!startAtTop) { xStart = *xMin; yStart = *yMin; } if (stopAtLast && haveLastFind) { xStop = lastFindXMin; yStop = lastFindYMin; } else if (!stopAtBottom) { xStop = *xMax; yStop = *yMax; } found = false; xMin0 = xMax0 = yMin0 = yMax0 = 0; // make gcc happy xMin1 = xMax1 = yMin1 = yMax1 = 0; // make gcc happy for (i = backward ? nBlocks - 1 : 0; backward ? i >= 0 : i < nBlocks; i += backward ? -1 : 1) { blk = blocks[i]; // check: is the block above the top limit? // (this only works if the page's primary rotation is zero -- // otherwise the blocks won't be sorted in the useful order) if (!startAtTop && primaryRot == 0 && (backward ? blk->yMin > yStart : blk->yMax < yStart)) { continue; } // check: is the block below the bottom limit? // (this only works if the page's primary rotation is zero -- // otherwise the blocks won't be sorted in the useful order) if (!stopAtBottom && primaryRot == 0 && (backward ? blk->yMax < yStop : blk->yMin > yStop)) { break; } for (line = blk->lines; line; line = line->next) { // check: is the line above the top limit? // (this only works if the page's primary rotation is zero -- // otherwise the lines won't be sorted in the useful order) if (!startAtTop && primaryRot == 0 && (backward ? line->yMin > yStart : line->yMin < yStart)) { continue; } // check: is the line below the bottom limit? // (this only works if the page's primary rotation is zero -- // otherwise the lines won't be sorted in the useful order) if (!stopAtBottom && primaryRot == 0 && (backward ? line->yMin < yStop : line->yMin > yStop)) { continue; } if (!line->normalized) { 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_len = 0; // 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); } if (line->ascii_len) { m = line->ascii_len; } else { 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 (!caseSensitive) { if (m > txtSize) { txt = (Unicode *)greallocn(txt, m, sizeof(Unicode)); txtSize = m; } for (k = 0; k < m; ++k) { if (ignoreDiacritics) { txt[k] = unicodeToUpper(line->ascii_translation[k]); } else { 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)); for (k = 0; k < nextline_len; ++k) { nextline[k] = ignoreDiacritics ? unicodeToUpper(line->next->ascii_translation[k]) : unicodeToUpper(line->next->normalized[k]); } } } else { if (ignoreDiacritics) { txt = line->ascii_translation; } else { 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; } } // search each position in this line j = backward ? m - len : 0; p = txt + j; while (backward ? j >= 0 : j <= m - (nextline ? 1 : len)) { bool wholeWordStartIsOk, wholeWordEndIsOk; if (wholeWord) { wholeWordStartIsOk = j == 0 || !unicodeTypeAlphaNum(txt[j - 1]); if (nextline) { wholeWordEndIsOk = true; // word end may be in next line, so we'll check it later } else { wholeWordEndIsOk = j + len == m || !unicodeTypeAlphaNum(txt[j + len]); } } if (!wholeWord || (wholeWordStartIsOk && wholeWordEndIsOk)) { int n = 0; bool spaceConsumedByNewline = false; bool found_it; // compare the strings for (k = 0; k < len; ++k) { bool last_char_of_line = j + k == m - 1; 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)) { // now check if the comparison failed at the end-of-line hyphen, // and if so, keep on comparing at the next line nextlineAfterHyphen = false; if (s2[k] == p[k]) { if (p[k] != (Unicode)'-' && !UnicodeIsWhitespace(s2[k + 1])) { break; } k++; } else if (!match_started || p[k] != (Unicode)'-' || !last_char_of_line || UnicodeIsWhitespace(s2[k])) { break; } else { nextlineAfterHyphen = true; } for (; n < nextline_len && k < len; ++k, ++n) { if (nextline[n] != s2[k]) { if (!spaceConsumedByNewline && !n && UnicodeIsWhitespace(s2[k])) { n = -1; spaceConsumedByNewline = true; continue; } break; } } break; } } found_it = k == len; if (found_it && nextline && wholeWord) { // check word end for nextline case if (n) { // Match ended at next line wholeWordEndIsOk = n == nextline_len || !unicodeTypeAlphaNum(nextline[n]); } else { // Match ended on same line wholeWordEndIsOk = j + len == m || !unicodeTypeAlphaNum(txt[j + len]); } if (!wholeWordEndIsOk) { found_it = false; } } // found it if (found_it) { bool nextLineMatch = (bool)n; if (spaceConsumedByNewline) { k--; } // where s2 matches a subsequence of a compatibility equivalence // decomposition, highlight the entire glyph, since we don't know // the internal layout of subglyph components int normStart, normAfterEnd; if (ignoreDiacritics) { normStart = line->ascii_idx[j]; if (nextline) { normAfterEnd = line->ascii_idx[j + k - n]; } else { normAfterEnd = line->ascii_idx[j + len - 1] + 1; } } else { normStart = line->normalized_idx[j]; if (nextline) { normAfterEnd = line->normalized_idx[j + k - n]; } else { normAfterEnd = line->normalized_idx[j + len - 1] + 1; } } adjustRotation(line, normStart, normAfterEnd, &xMin1, &xMax1, &yMin1, &yMax1); if (backward) { if ((startAtTop || yMin1 < yStart || (yMin1 == yStart && xMin1 < xStart)) && (stopAtBottom || yMin1 > yStop || (yMin1 == yStop && xMin1 > xStop))) { if (!found || yMin1 > yMin0 || (yMin1 == yMin0 && xMin1 > xMin0)) { xMin0 = xMin1; xMax0 = xMax1; yMin0 = yMin1; yMax0 = yMax1; found = true; } } } else { if ((startAtTop || yMin1 > yStart || (yMin1 == yStart && xMin1 > xStart)) && (stopAtBottom || yMin1 < yStop || (yMin1 == yStop && xMin1 < xStop))) { if (!found || yMin1 < yMin0 || (yMin1 == yMin0 && xMin1 < xMin0)) { xMin0 = xMin1; xMax0 = xMax1; yMin0 = yMin1; yMax0 = yMax1; found = true; if (nextLineMatch) { // set the out parameters if (ignoredHyphen) { *ignoredHyphen = nextlineAfterHyphen; } if (continueMatch) { adjustRotation(line->next, 0, n, &xMin2, &xMax2, &yMin2, &yMax2); continueMatch->x1 = xMin2; continueMatch->y1 = yMax2; continueMatch->x2 = xMax2; continueMatch->y2 = yMin2; } } else if (continueMatch && continueMatch->x1 != std::numeric_limits::max()) { if (ignoredHyphen) { *ignoredHyphen = false; } continueMatch->x1 = std::numeric_limits::max(); } } } } } } if (backward) { --j; --p; } else { ++j; ++p; } } if (nextline && nextline != line->next->ascii_translation && nextline != line->next->normalized) { gfree(nextline); } } } gfree(s2); gfree(reordered); if (!caseSensitive) { gfree(txt); } if (found) { *xMin = xMin0; *xMax = xMax0; *yMin = yMin0; *yMax = yMax0; lastFindXMin = xMin0; lastFindYMin = yMin0; haveLastFind = true; return true; } return false; } 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(); // get the output encoding if (!(uMap = globalParams->getTextEncoding())) { return s; } 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) { 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); } } } return s; } spaceLen = uMap->mapUnicode(0x20, space, sizeof(space)); eolLen = 0; // make gcc happy switch (textEOL) { case eolUnix: eolLen = uMap->mapUnicode(0x0a, eol, sizeof(eol)); break; case eolDOS: eolLen = uMap->mapUnicode(0x0d, eol, sizeof(eol)); eolLen += uMap->mapUnicode(0x0a, eol + eolLen, sizeof(eol) - eolLen); break; case eolMac: eolLen = uMap->mapUnicode(0x0d, eol, sizeof(eol)); break; } //~ 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]; if (xMin < blk->xMax && blk->xMin < xMax && yMin < blk->yMax && blk->yMin < yMax) { for (line = blk->lines; line; line = line->next) { if (xMin < line->xMax && line->xMin < xMax && yMin < line->yMax && line->yMin < yMax) { idx0 = idx1 = -1; switch (line->rot) { case 0: y = 0.5 * (line->yMin + line->yMax); if (yMin < y && y < yMax) { j = 0; while (j < line->len) { if (0.5 * (line->edge[j] + line->edge[j + 1]) > xMin) { idx0 = j; break; } ++j; } j = line->len - 1; while (j >= 0) { if (0.5 * (line->edge[j] + line->edge[j + 1]) < xMax) { idx1 = j; break; } --j; } } break; case 1: x = 0.5 * (line->xMin + line->xMax); if (xMin < x && x < xMax) { j = 0; while (j < line->len) { if (0.5 * (line->edge[j] + line->edge[j + 1]) > yMin) { idx0 = j; break; } ++j; } j = line->len - 1; while (j >= 0) { if (0.5 * (line->edge[j] + line->edge[j + 1]) < yMax) { idx1 = j; break; } --j; } } break; case 2: y = 0.5 * (line->yMin + line->yMax); if (yMin < y && y < yMax) { j = 0; while (j < line->len) { if (0.5 * (line->edge[j] + line->edge[j + 1]) < xMax) { idx0 = j; break; } ++j; } j = line->len - 1; while (j >= 0) { if (0.5 * (line->edge[j] + line->edge[j + 1]) > xMin) { idx1 = j; break; } --j; } } break; case 3: x = 0.5 * (line->xMin + line->xMax); if (xMin < x && x < xMax) { j = 0; while (j < line->len) { if (0.5 * (line->edge[j] + line->edge[j + 1]) < yMax) { idx0 = j; break; } ++j; } j = line->len - 1; while (j >= 0) { if (0.5 * (line->edge[j] + line->edge[j + 1]) > yMin) { idx1 = j; break; } --j; } } 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; if (lastRot >= 0 && line->rot != lastRot) { oneRot = false; } lastRot = line->rot; } } } } } // sort the fragments and generate the string if (nFrags > 0) { for (i = 0; i < nFrags; ++i) { frags[i].computeCoords(oneRot); } assignColumns(frags, nFrags, 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); } else { 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), oneRot ? &TextLineFrag::cmpXYColumnLineRot : &TextLineFrag::cmpXYColumnPrimaryRot); i = j; } col = 0; multiLine = false; for (i = 0; i < nFrags; ++i) { 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); col = 0; multiLine = true; } // column alignment for (; col < frag->col; ++col) { s->append(space, spaceLen); } // get the fragment text col += dumpFragment(frag->line->text + frag->start, frag->len, uMap, s); } if (multiLine) { s->append(eol, eolLen); } } gfree(frags); return s; } class TextSelectionVisitor { public: explicit TextSelectionVisitor(TextPage *page); virtual ~TextSelectionVisitor(); TextSelectionVisitor(const TextSelectionVisitor &) = delete; TextSelectionVisitor &operator=(const TextSelectionVisitor &) = delete; virtual void visitBlock(TextBlock *block, TextLine *begin, TextLine *end, const PDFRectangle *selection) = 0; virtual void visitLine(TextLine *line, TextWord *begin, TextWord *end, int edge_begin, int edge_end, const PDFRectangle *selection) = 0; virtual void visitWord(TextWord *word, int begin, int end, const PDFRectangle *selection) = 0; protected: TextPage *page; }; TextSelectionVisitor::TextSelectionVisitor(TextPage *p) : page(p) { } TextSelectionVisitor::~TextSelectionVisitor() = default; class TextSelectionDumper : public TextSelectionVisitor { public: explicit TextSelectionDumper(TextPage *page); ~TextSelectionDumper() 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); private: void startLine(); void finishLine(); std::vector **lines; int nLines, linesSize; 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); } void TextSelectionDumper::startLine() { finishLine(); words = new std::vector(); } void TextSelectionDumper::finishLine() { if (nLines == linesSize) { linesSize *= 2; lines = (std::vector **)grealloc(lines, linesSize * sizeof(std::vector *)); } if (words && words->size() > 0) { // Reverse word order for RTL text. Fixes #53 for glib backend (Evince) if (!page->primaryLR) { std::reverse(words->begin(), words->end()); } lines[nLines++] = words; } else if (words) { delete words; } words = nullptr; } void TextSelectionDumper::visitLine(TextLine *line, TextWord *begin, TextWord *end, int edge_begin, int edge_end, const PDFRectangle *selection) { TextLineFrag frag; frag.init(line, edge_begin, edge_end - edge_begin); if (tableId >= 0 && frag.line->blk->tableId < 0) { finishLine(); tableId = -1; currentBlock = nullptr; } if (frag.line->blk->tableId >= 0) { // a table if (tableId == -1) { tableId = frag.line->blk->tableId; currentBlock = frag.line->blk; } if (currentBlock == frag.line->blk) { // the same block startLine(); } else { // another block if (currentBlock->tableEnd) { // previous block ended its row startLine(); } currentBlock = frag.line->blk; } } else { // not a table startLine(); } } void TextSelectionDumper::visitWord(TextWord *word, int begin, int end, const PDFRectangle *selection) { words->push_back(new TextWordSelection(word, begin, end)); } void TextSelectionDumper::endPage() { finishLine(); } GooString *TextSelectionDumper::getText() { GooString *text; int i; const UnicodeMap *uMap; char space[8], eol[16]; int spaceLen, eolLen; text = new GooString(); if (!(uMap = globalParams->getTextEncoding())) { return text; } 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]; 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 (i < nLines - 1) { text->append(eol, eolLen); } } return text; } std::vector **TextSelectionDumper::takeWordList(int *nLinesOut) { std::vector **returnValue = lines; *nLinesOut = nLines; if (nLines == 0) { return nullptr; } nLines = 0; lines = nullptr; return returnValue; } class TextSelectionSizer : public TextSelectionVisitor { public: TextSelectionSizer(TextPage *page, double scale); ~TextSelectionSizer() override { delete list; } 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 {}; std::vector *takeRegion() { auto aux = list; list = nullptr; return aux; } private: std::vector *list; double scale; }; TextSelectionSizer::TextSelectionSizer(TextPage *p, double s) : TextSelectionVisitor(p), scale(s) { list = new std::vector(); } void TextSelectionSizer::visitLine(TextLine *line, TextWord *begin, TextWord *end, int edge_begin, int edge_end, const PDFRectangle *selection) { PDFRectangle *rect; double x1, y1, x2, y2, margin; switch (line->rot) { default: case 0: margin = (line->yMax - line->yMin) / 8; x1 = line->edge[edge_begin]; x2 = line->edge[edge_end]; y1 = line->yMin - margin; y2 = line->yMax + margin; break; case 1: margin = (line->xMax - line->xMin) / 8; x1 = line->xMin - margin; x2 = line->xMax + margin; y1 = line->edge[edge_begin]; y2 = line->edge[edge_end]; break; case 2: margin = (line->yMax - line->yMin) / 8; x1 = line->edge[edge_end]; x2 = line->edge[edge_begin]; y1 = line->yMin - margin; y2 = line->yMax + margin; break; case 3: margin = (line->xMax - line->xMin) / 8; x1 = line->xMin - margin; x2 = line->xMax + margin; y1 = line->edge[edge_end]; y2 = line->edge[edge_begin]; break; } rect = new PDFRectangle(floor(x1 * scale), floor(y1 * scale), ceil(x2 * scale), ceil(y2 * scale)); list->push_back(rect); } class TextSelectionPainter : public TextSelectionVisitor { public: 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 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(); private: OutputDev *out; const GfxColor *glyph_color; GfxState *state; std::vector *selectionList; Matrix ctm, ictm; bool hasGlyphLessFont(); }; TextSelectionPainter::TextSelectionPainter(TextPage *p, double scale, int rotation, OutputDev *outA, const GfxColor *box_color, const GfxColor *glyph_colorA) : TextSelectionVisitor(p), out(outA), glyph_color(glyph_colorA) { PDFRectangle box(0, 0, p->pageWidth, p->pageHeight); selectionList = new std::vector(); state = new GfxState(72 * scale, 72 * scale, &box, rotation, false); state->getCTM(&ctm); ctm.invertTo(&ictm); out->startPage(0, state, nullptr); out->setDefaultCTM(state->getCTM()); state->setFillColorSpace(new GfxDeviceRGBColorSpace()); state->setFillColor(box_color); out->updateFillColor(state); } TextSelectionPainter::~TextSelectionPainter() { for (auto entry : *selectionList) { delete entry; } delete selectionList; delete state; } void TextSelectionPainter::visitLine(TextLine *line, TextWord *begin, TextWord *end, int edge_begin, int edge_end, const PDFRectangle *selection) { double x1, y1, x2, y2, margin; switch (line->rot) { default: case 0: margin = (line->yMax - line->yMin) / 8; x1 = line->edge[edge_begin]; x2 = line->edge[edge_end]; y1 = line->yMin - margin; y2 = line->yMax + margin; break; case 1: margin = (line->xMax - line->xMin) / 8; x1 = line->xMin - margin; x2 = line->xMax + margin; y1 = line->edge[edge_begin]; y2 = line->edge[edge_end]; break; case 2: margin = (line->yMax - line->yMin) / 8; x1 = line->edge[edge_end]; x2 = line->edge[edge_begin]; y1 = line->yMin - margin; y2 = line->yMax + margin; break; case 3: margin = (line->xMax - line->xMin) / 8; x1 = line->xMin - margin; x2 = line->xMax + margin; y1 = line->edge[edge_end]; y2 = line->edge[edge_begin]; break; } ctm.transform(x1, y1, &x1, &y1); ctm.transform(x2, y2, &x2, &y2); if (x1 < x2) { x1 = floor(x1); x2 = ceil(x2); } else { x1 = ceil(x1); x2 = floor(x2); } if (y1 < y2) { y1 = floor(y1); y2 = ceil(y2); } else { y1 = ceil(y1); y2 = floor(y2); } ictm.transform(x1, y1, &x1, &y1); ictm.transform(x2, y2, &x2, &y2); state->moveTo(x1, y1); state->lineTo(x2, y1); state->lineTo(x2, y2); state->lineTo(x1, y2); state->closePath(); } void TextSelectionPainter::visitWord(TextWord *word, int begin, int end, const PDFRectangle *selection) { selectionList->push_back(new TextWordSelection(word, begin, end)); } bool TextSelectionPainter::hasGlyphLessFont() { if (selectionList && selectionList->size()) { TextWordSelection *sel = (*selectionList)[0]; return sel->word->invisible; } return false; } void TextSelectionPainter::endPage() { out->fill(state); out->saveState(state); out->clip(state); state->clearPath(); 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); 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]; 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]) { 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); 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); } } out->endString(state); delete string; begin = fEnd; } } out->restoreState(state); out->endPage(); } void TextWord::visitSelection(TextSelectionVisitor *visitor, const PDFRectangle *selection, SelectionStyle style) { int i, begin, end; double mid, s1, s2; if (rot == 0 || rot == 2) { s1 = selection->x1; s2 = selection->x2; } else { s1 = selection->y1; s2 = selection->y2; } begin = len; end = 0; for (i = 0; i < len; i++) { mid = (edge[i] + edge[i + 1]) / 2; if (XBetweenAB(mid, s1, s2)) { if (i < begin) { begin = i; } end = i + 1; } } /* Skip empty selection. */ if (end <= begin) { return; } visitor->visitWord(this, begin, end, selection); } void TextLine::visitSelection(TextSelectionVisitor *visitor, const PDFRectangle *selection, SelectionStyle style) { TextWord *p, *begin, *end, *current; int i, edge_begin, edge_end; PDFRectangle child_selection; double s1, s2, pMin, pMax; if (rot == 0 || rot == 2) { s1 = selection->x1; s2 = selection->x2; } else { s1 = selection->y1; s2 = selection->y2; } begin = nullptr; end = nullptr; current = nullptr; for (p = words; p != nullptr; p = p->next) { if (rot == 0 || rot == 2) { pMin = p->xMin; pMax = p->xMax; } else { pMin = p->yMin; pMax = p->yMax; } if (blk->page->primaryLR) { if (((s1 < pMax) || (s2 < pMax)) && begin == nullptr) { begin = p; } if (((s1 > pMin) || (s2 > pMin)) && begin != nullptr) { end = p->next; current = p; } } else { if (((s1 > pMin) || (s2 > pMin)) && begin == nullptr) { begin = p; } if (((s1 < pMax) || (s2 < pMax)) && begin != nullptr) { end = p->next; current = p; } } } if (!current) { current = begin; } child_selection = *selection; if (style == selectionStyleWord) { if (rot == 0 || rot == 2) { child_selection.x1 = begin ? begin->xMin : xMin; if (end && end->xMax != -1) { child_selection.x2 = current->xMax; } else { child_selection.x2 = xMax; } } else { child_selection.y1 = begin ? begin->yMin : yMin; if (end && end->yMax != -1) { child_selection.y2 = current->yMax; } else { child_selection.y2 = yMax; } } } if (rot == 0 || rot == 2) { s1 = child_selection.x1; s2 = child_selection.x2; } else { s1 = child_selection.y1; s2 = child_selection.y2; } edge_begin = len; edge_end = 0; for (i = 0; i < len; i++) { double mid = (edge[i] + edge[i + 1]) / 2; if (XBetweenAB(mid, s1, s2)) { if (i < edge_begin) { edge_begin = i; } edge_end = i + 1; } } /* Skip empty selection. */ if (edge_end <= edge_begin) { return; } visitor->visitLine(this, begin, end, edge_begin, edge_end, &child_selection); for (p = begin; p != end; p = p->next) { p->visitSelection(visitor, &child_selection, style); } } void TextBlock::visitSelection(TextSelectionVisitor *visitor, const PDFRectangle *selection, SelectionStyle style) { PDFRectangle child_selection; double x[2], y[2], d, best_d[2]; TextLine *p, *best_line[2]; int i, count = 0, best_count[2], start, stop; bool all[2]; x[0] = selection->x1; y[0] = selection->y1; x[1] = selection->x2; y[1] = selection->y2; for (i = 0; i < 2; i++) { // the first/last lines are often not nearest // the corners, so we have to force them to be // selected when the selection runs outside this // block. if (page->primaryLR) { all[i] = x[i] >= this->xMax && y[i] >= this->yMax; if (x[i] <= this->xMin && y[i] <= this->yMin) { best_line[i] = this->lines; best_count[i] = 1; } else { best_line[i] = nullptr; best_count[i] = 0; } } else { all[i] = x[i] <= this->xMin && y[i] >= this->yMax; if (x[i] >= this->xMax && y[i] <= this->yMin) { best_line[i] = this->lines; best_count[i] = 1; } else { best_line[i] = nullptr; best_count[i] = 0; } } best_d[i] = 0; } // find the nearest line to the selection points // using the manhattan distance. for (p = this->lines; p; p = p->next) { count++; for (i = 0; i < 2; i++) { d = fmax(p->xMin - x[i], 0.0) + fmax(x[i] - p->xMax, 0.0) + fmax(p->yMin - y[i], 0.0) + fmax(y[i] - p->yMax, 0.0); if (!best_line[i] || all[i] || d < best_d[i]) { best_line[i] = p; best_count[i] = count; best_d[i] = d; } } } // assert: best is always set. if (!best_line[0] || !best_line[1]) { return; } // Now decide which point was first. if (best_count[0] < best_count[1] || (best_count[0] == best_count[1] && y[0] < y[1])) { start = 0; stop = 1; } else { start = 1; stop = 0; } visitor->visitBlock(this, best_line[start], best_line[stop], selection); for (p = best_line[start]; p; p = p->next) { if (page->primaryLR) { child_selection.x1 = p->xMin; child_selection.x2 = p->xMax; } else { child_selection.x1 = p->xMax; child_selection.x2 = p->xMin; } child_selection.y1 = p->yMin; child_selection.y2 = p->yMax; if (style == selectionStyleLine) { if (p == best_line[start]) { child_selection.x1 = 0; child_selection.y1 = 0; } if (p == best_line[stop]) { child_selection.x2 = page->pageWidth; child_selection.y2 = page->pageHeight; } } else { if (p == best_line[start]) { child_selection.x1 = fmax(p->xMin, fmin(p->xMax, x[start])); child_selection.y1 = fmax(p->yMin, fmin(p->yMax, y[start])); } if (p == best_line[stop]) { child_selection.x2 = fmax(p->xMin, fmin(p->xMax, x[stop])); child_selection.y2 = fmax(p->yMin, fmin(p->yMax, y[stop])); } } p->visitSelection(visitor, &child_selection, style); if (p == best_line[stop]) { return; } } } void TextPage::visitSelection(TextSelectionVisitor *visitor, const PDFRectangle *selection, SelectionStyle style) { PDFRectangle child_selection; double x[2], y[2], d, best_d[2]; double xMin, yMin, xMax, yMax; TextFlow *flow, *best_flow[2]; TextBlock *blk, *best_block[2]; int i, count = 0, best_count[2], start, stop; if (!flows) { return; } x[0] = selection->x1; y[0] = selection->y1; x[1] = selection->x2; y[1] = selection->y2; xMin = pageWidth; yMin = pageHeight; xMax = 0.0; yMax = 0.0; for (i = 0; i < 2; i++) { best_block[i] = nullptr; best_flow[i] = nullptr; best_count[i] = 0; best_d[i] = 0; } // find the nearest blocks to the selection points // using the manhattan distance. for (flow = flows; flow; flow = flow->next) { for (blk = flow->blocks; blk; blk = blk->next) { count++; // the first/last blocks in reading order are // often not the closest to the page corners; // track the corners, force those blocks to // be selected if the selection runs across // multiple pages. xMin = fmin(xMin, blk->xMin); yMin = fmin(yMin, blk->yMin); xMax = fmax(xMax, blk->xMax); yMax = fmax(yMax, blk->yMax); for (i = 0; i < 2; i++) { d = fmax(blk->xMin - x[i], 0.0) + fmax(x[i] - blk->xMax, 0.0) + fmax(blk->yMin - y[i], 0.0) + fmax(y[i] - blk->yMax, 0.0); if (!best_block[i] || d < best_d[i] || (!blk->next && !flow->next && x[i] >= fmin(xMax, pageWidth) && y[i] >= fmin(yMax, pageHeight))) { best_block[i] = blk; best_flow[i] = flow; best_count[i] = count; best_d[i] = d; } } } } for (i = 0; i < 2; i++) { if (primaryLR) { if (x[i] < xMin && y[i] < yMin) { best_block[i] = flows->blocks; best_flow[i] = flows; best_count[i] = 1; } } else { if (x[i] > xMax && y[i] < yMin) { best_block[i] = flows->blocks; best_flow[i] = flows; best_count[i] = 1; } } } // assert: best is always set. if (!best_block[0] || !best_block[1]) { return; } // Now decide which point was first. if (best_count[0] < best_count[1] || (best_count[0] == best_count[1] && y[0] < y[1])) { start = 0; stop = 1; } else { start = 1; stop = 0; } for (flow = best_flow[start]; flow; flow = flow->next) { if (flow == best_flow[start]) { blk = best_block[start]; } else { blk = flow->blocks; } for (; blk; blk = blk->next) { if (primaryLR) { child_selection.x1 = blk->xMin; child_selection.x2 = blk->xMax; } else { child_selection.x1 = blk->xMax; child_selection.x2 = blk->xMin; } child_selection.y1 = blk->yMin; child_selection.y2 = blk->yMax; if (blk == best_block[start]) { child_selection.x1 = fmax(blk->xMin, fmin(blk->xMax, x[start])); child_selection.y1 = fmax(blk->yMin, fmin(blk->yMax, y[start])); } if (blk == best_block[stop]) { child_selection.x2 = fmax(blk->xMin, fmin(blk->xMax, x[stop])); child_selection.y2 = fmax(blk->yMin, fmin(blk->yMax, y[stop])); blk->visitSelection(visitor, &child_selection, style); return; } blk->visitSelection(visitor, &child_selection, style); } } } void TextPage::drawSelection(OutputDev *out, double scale, int rotation, const PDFRectangle *selection, SelectionStyle style, const GfxColor *glyph_color, const GfxColor *box_color) { TextSelectionPainter painter(this, scale, rotation, out, box_color, glyph_color); visitSelection(&painter, selection, style); painter.endPage(); } std::vector *TextPage::getSelectionRegion(const PDFRectangle *selection, SelectionStyle style, double scale) { TextSelectionSizer sizer(this, scale); visitSelection(&sizer, selection, style); return sizer.takeRegion(); } GooString *TextPage::getSelectionText(const PDFRectangle *selection, SelectionStyle style) { TextSelectionDumper dumper(this); visitSelection(&dumper, selection, style); dumper.endPage(); return dumper.getText(); } std::vector **TextPage::getSelectionWords(const PDFRectangle *selection, SelectionStyle style, int *nLines) { TextSelectionDumper dumper(this); visitSelection(&dumper, selection, style); dumper.endPage(); return dumper.takeWordList(nLines); } bool TextPage::findCharRange(int pos, int length, double *xMin, double *yMin, double *xMax, double *yMax) const { TextBlock *blk; TextLine *line; TextWord *word; double xMin0, xMax0, yMin0, yMax0; double xMin1, xMax1, yMin1, yMax1; bool first; int i, j0, j1; if (rawOrder) { return false; } //~ this doesn't correctly handle ranges split across multiple lines //~ (the highlighted region is the bounding box of all the parts of //~ the range) first = true; xMin0 = xMax0 = yMin0 = yMax0 = 0; // make gcc happy xMin1 = xMax1 = yMin1 = yMax1 = 0; // make gcc happy for (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) { ; } for (j1 = word->len - 1; j1 > j0 && pos + length <= word->charPos[j1]; --j1) { ; } switch (line->rot) { case 0: xMin1 = word->edge[j0]; xMax1 = word->edge[j1 + 1]; yMin1 = word->yMin; yMax1 = word->yMax; break; case 1: xMin1 = word->xMin; xMax1 = word->xMax; yMin1 = word->edge[j0]; yMax1 = word->edge[j1 + 1]; break; case 2: xMin1 = word->edge[j1 + 1]; xMax1 = word->edge[j0]; yMin1 = word->yMin; yMax1 = word->yMax; break; case 3: xMin1 = word->xMin; xMax1 = word->xMax; yMin1 = word->edge[j1 + 1]; yMax1 = word->edge[j0]; break; } if (first || xMin1 < xMin0) { xMin0 = xMin1; } if (first || xMax1 > xMax0) { xMax0 = xMax1; } if (first || yMin1 < yMin0) { yMin0 = yMin1; } if (first || yMax1 > yMax0) { yMax0 = yMax1; } first = false; } } } } if (!first) { *xMin = xMin0; *xMax = xMax0; *yMin = yMin0; *yMax = yMax0; return true; } return false; } 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())) { return; } spaceLen = uMap->mapUnicode(0x20, space, sizeof(space)); eolLen = 0; // make gcc happy switch (textEOL) { case eolUnix: eolLen = uMap->mapUnicode(0x0a, eol, sizeof(eol)); break; case eolDOS: eolLen = uMap->mapUnicode(0x0d, eol, sizeof(eol)); eolLen += uMap->mapUnicode(0x0a, eol + eolLen, sizeof(eol) - eolLen); break; case eolMac: eolLen = uMap->mapUnicode(0x0d, eol, sizeof(eol)); break; } eopLen = uMap->mapUnicode(0x0c, eop, sizeof(eop)); //~ writing mode (horiz/vert) // 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; 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); } } else { (*outputFunc)(outputStream, eol, eolLen); } } // output the page, maintaining the original physical layout } 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; } } 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; } #if 0 // for debugging printf("*** line fragments ***\n"); for (i = 0; i < nFrags; ++i) { frag = &frags[i]; 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); } printf("'\n"); } printf("\n"); #endif // generate output col = 0; for (i = 0; i < nFrags; ++i) { frag = &frags[i]; // column alignment 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; // 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; } for (; d > 0; --d) { (*outputFunc)(outputStream, eol, eolLen); } col = 0; } } 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; 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; // output a newline when a hyphen is not suppressed if (n == line->len) { (*outputFunc)(outputStream, eol, eolLen); } } } (*outputFunc)(outputStream, eol, eolLen); } } // end of page if (pageBreaks) { (*outputFunc)(outputStream, eop, eopLen); } } void TextPage::setMergeCombining(bool merge) { mergeCombining = merge; } void TextPage::assignColumns(TextLineFrag *frags, int nFrags, bool oneRot) const { TextLineFrag *frag0, *frag1; int rot, col1, col2, i, j, k; // 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); rot = frags[0].line->rot; for (i = 0; i < nFrags; ++i) { frag0 = &frags[i]; col1 = 0; for (j = 0; j < i; ++j) { frag1 = &frags[j]; col2 = 0; // make gcc happy switch (rot) { case 0: if (frag0->xMin >= frag1->xMax) { col2 = frag1->col + (frag1->line->col[frag1->start + frag1->len] - frag1->line->col[frag1->start]) + 1; } else { for (k = frag1->start; k < frag1->start + frag1->len && frag0->xMin >= 0.5 * (frag1->line->edge[k] + frag1->line->edge[k + 1]); ++k) { ; } col2 = frag1->col + frag1->line->col[k] - frag1->line->col[frag1->start]; } break; case 1: if (frag0->yMin >= frag1->yMax) { col2 = frag1->col + (frag1->line->col[frag1->start + frag1->len] - frag1->line->col[frag1->start]) + 1; } else { for (k = frag1->start; k < frag1->start + frag1->len && frag0->yMin >= 0.5 * (frag1->line->edge[k] + frag1->line->edge[k + 1]); ++k) { ; } col2 = frag1->col + frag1->line->col[k] - frag1->line->col[frag1->start]; } break; case 2: if (frag0->xMax <= frag1->xMin) { col2 = frag1->col + (frag1->line->col[frag1->start + frag1->len] - frag1->line->col[frag1->start]) + 1; } else { for (k = frag1->start; k < frag1->start + frag1->len && frag0->xMax <= 0.5 * (frag1->line->edge[k] + frag1->line->edge[k + 1]); ++k) { ; } col2 = frag1->col + frag1->line->col[k] - frag1->line->col[frag1->start]; } break; case 3: if (frag0->yMax <= frag1->yMin) { col2 = frag1->col + (frag1->line->col[frag1->start + frag1->len] - frag1->line->col[frag1->start]) + 1; } else { for (k = frag1->start; k < frag1->start + frag1->len && frag0->yMax <= 0.5 * (frag1->line->edge[k] + frag1->line->edge[k + 1]); ++k) { ; } col2 = frag1->col + frag1->line->col[k] - frag1->line->col[frag1->start]; } break; } if (col2 > col1) { col1 = col2; } } frag0->col = col1; } // the region includes text at different rotations -- use the // globally assigned column numbers, offset by the minimum column // number (i.e., shift everything over to column 0) } else { col1 = frags[0].col; for (i = 1; i < nFrags; ++i) { if (frags[i].col < col1) { col1 = frags[i].col; } } for (i = 0; i < nFrags; ++i) { frags[i].col -= col1; } } } int TextPage::dumpFragment(const Unicode *text, int len, const UnicodeMap *uMap, GooString *s) const { if (uMap->isUnicode()) { return reorderText(text, len, uMap, primaryLR, s, nullptr); } else { int nCols = 0; char buf[8]; int buflen = 0; for (int i = 0; i < len; ++i) { buflen = uMap->mapUnicode(text[i], buf, sizeof(buf)); s->append(buf, buflen); nCols += buflen; } return nCols; } } #ifdef TEXTOUT_WORD_LIST std::unique_ptr TextPage::makeWordList(bool physLayout) { return std::make_unique(this, physLayout); } #endif //------------------------------------------------------------------------ // ActualText //------------------------------------------------------------------------ ActualText::ActualText(TextPage *out) { out->incRefCnt(); text = out; actualText = nullptr; actualTextNBytes = 0; } ActualText::~ActualText() { if (actualText) { delete actualText; } text->decRefCnt(); } void ActualText::addChar(const GfxState *state, double x, double y, double dx, double dy, CharCode c, int nBytes, const Unicode *u, int uLen) { if (!actualText) { text->addChar(state, x, y, dx, dy, c, nBytes, u, uLen); return; } // Inside ActualText span. if (!actualTextNBytes) { actualTextX0 = x; actualTextY0 = y; } actualTextX1 = x + dx; actualTextY1 = y + dy; actualTextNBytes += nBytes; } void ActualText::begin(const GfxState *state, const GooString *t) { if (actualText) { delete actualText; } actualText = new GooString(t); actualTextNBytes = 0; } void ActualText::end(const GfxState *state) { // ActualText span closed. Output the span text and the // extents of all the glyphs inside the span if (actualTextNBytes) { // 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() std::vector uni = TextStringToUCS4(actualText->toStr()); text->addChar(state, actualTextX0, actualTextY0, actualTextX1 - actualTextX0, actualTextY1 - actualTextY0, 0, actualTextNBytes, uni.data(), uni.size()); } delete actualText; actualText = nullptr; actualTextNBytes = 0; } //------------------------------------------------------------------------ // TextOutputDev //------------------------------------------------------------------------ static void TextOutputDev_outputToFile(void *stream, const char *text, int len) { fwrite(text, 1, len, (FILE *)stream); } TextOutputDev::TextOutputDev(const char *fileName, bool physLayoutA, double fixedPitchA, bool rawOrderA, bool append, bool discardDiagA) { text = nullptr; physLayout = physLayoutA; fixedPitch = physLayout ? fixedPitchA : 0; rawOrder = rawOrderA; discardDiag = discardDiagA; doHTML = false; textEOL = defaultEndOfLine(); textPageBreaks = true; ok = true; minColSpacing1 = minColSpacing1_default; // open file needClose = false; if (fileName) { if (!strcmp(fileName, "-")) { outputStream = stdout; #if defined(_WIN32) || defined(__CYGWIN__) // keep DOS from munging the end-of-line characters _setmode(fileno(stdout), O_BINARY); #endif } else if ((outputStream = openFile(fileName, append ? "ab" : "wb"))) { needClose = true; } else { error(errIO, -1, "Couldn't open text file '{0:s}'", fileName); ok = false; actualText = nullptr; return; } outputFunc = &TextOutputDev_outputToFile; } else { outputStream = nullptr; } // set up text object text = new TextPage(rawOrderA, discardDiagA); actualText = new ActualText(text); } TextOutputDev::TextOutputDev(TextOutputFunc func, void *stream, bool physLayoutA, double fixedPitchA, bool rawOrderA, bool discardDiagA) { outputFunc = func; outputStream = stream; needClose = false; physLayout = physLayoutA; fixedPitch = physLayout ? fixedPitchA : 0; rawOrder = rawOrderA; discardDiag = discardDiagA; doHTML = false; text = new TextPage(rawOrderA, discardDiagA); actualText = new ActualText(text); textEOL = defaultEndOfLine(); textPageBreaks = true; ok = true; minColSpacing1 = minColSpacing1_default; } TextOutputDev::~TextOutputDev() { if (needClose) { fclose((FILE *)outputStream); } if (text) { text->decRefCnt(); } delete actualText; } void TextOutputDev::startPage(int pageNum, GfxState *state, XRef *xref) { text->startPage(state); } void TextOutputDev::endPage() { text->endPage(); text->coalesce(physLayout, fixedPitch, doHTML, minColSpacing1); if (outputStream) { text->dump(outputStream, outputFunc, physLayout, textEOL, textPageBreaks); } } void TextOutputDev::restoreState(GfxState *state) { text->updateFont(state); } void TextOutputDev::updateFont(GfxState *state) { text->updateFont(state); } void TextOutputDev::beginString(GfxState *state, const GooString *s) { } void TextOutputDev::endString(GfxState *state) { } void TextOutputDev::drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode c, int nBytes, const Unicode *u, int uLen) { actualText->addChar(state, x, y, dx, dy, c, nBytes, u, uLen); } void TextOutputDev::incCharCount(int nChars) { text->incCharCount(nChars); } void TextOutputDev::beginActualText(GfxState *state, const GooString *t) { actualText->begin(state, t); } void TextOutputDev::endActualText(GfxState *state) { actualText->end(state); } void TextOutputDev::stroke(GfxState *state) { double x[2], y[2]; if (!doHTML) { return; } const GfxPath *path = state->getPath(); if (path->getNumSubpaths() != 1) { return; } const GfxSubpath *subpath = path->getSubpath(0); if (subpath->getNumPoints() != 2) { return; } state->transform(subpath->getX(0), subpath->getY(0), &x[0], &y[0]); state->transform(subpath->getX(1), subpath->getY(1), &x[1], &y[1]); // look for a vertical or horizontal line if (x[0] == x[1] || y[0] == y[1]) { text->addUnderline(x[0], y[0], x[1], y[1]); } } void TextOutputDev::fill(GfxState *state) { double x[5], y[5]; double rx0, ry0, rx1, ry1, t; int i; if (!doHTML) { return; } const GfxPath *path = state->getPath(); if (path->getNumSubpaths() != 1) { return; } const GfxSubpath *subpath = path->getSubpath(0); if (subpath->getNumPoints() != 5) { return; } for (i = 0; i < 5; ++i) { if (subpath->getCurve(i)) { return; } state->transform(subpath->getX(i), subpath->getY(i), &x[i], &y[i]); } // look for a rectangle if (x[0] == x[1] && y[1] == y[2] && x[2] == x[3] && y[3] == y[4] && x[0] == x[4] && y[0] == y[4]) { rx0 = x[0]; ry0 = y[0]; rx1 = x[2]; ry1 = y[1]; } else if (y[0] == y[1] && x[1] == x[2] && y[2] == y[3] && x[3] == x[4] && x[0] == x[4] && y[0] == y[4]) { rx0 = x[0]; ry0 = y[0]; rx1 = x[1]; ry1 = y[2]; } else { return; } if (rx1 < rx0) { t = rx0; rx0 = rx1; rx1 = t; } if (ry1 < ry0) { t = ry0; ry0 = ry1; ry1 = t; } // skinny horizontal rectangle if (ry1 - ry0 < rx1 - rx0) { if (ry1 - ry0 < maxUnderlineWidth) { ry0 = 0.5 * (ry0 + ry1); text->addUnderline(rx0, ry0, rx1, ry0); } // skinny vertical rectangle } else { if (rx1 - rx0 < maxUnderlineWidth) { rx0 = 0.5 * (rx0 + rx1); text->addUnderline(rx0, ry0, rx0, ry1); } } } void TextOutputDev::eoFill(GfxState *state) { if (!doHTML) { return; } fill(state); } void TextOutputDev::processLink(AnnotLink *link) { double x1, y1, x2, y2; int xMin, yMin, xMax, yMax, x, y; if (!doHTML) { return; } link->getRect(&x1, &y1, &x2, &y2); cvtUserToDev(x1, y1, &x, &y); xMin = xMax = x; yMin = yMax = y; cvtUserToDev(x1, y2, &x, &y); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } cvtUserToDev(x2, y1, &x, &y); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } cvtUserToDev(x2, y2, &x, &y); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } text->addLink(xMin, yMin, xMax, yMax, link); } bool TextOutputDev::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 { 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 { return text->getText(xMin, yMin, xMax, yMax, textEOL); } void TextOutputDev::drawSelection(OutputDev *out, double scale, int rotation, const PDFRectangle *selection, SelectionStyle style, const GfxColor *glyph_color, const GfxColor *box_color) { text->drawSelection(out, scale, rotation, selection, style, glyph_color, box_color); } std::vector *TextOutputDev::getSelectionRegion(const PDFRectangle *selection, SelectionStyle style, double scale) { return text->getSelectionRegion(selection, style, scale); } GooString *TextOutputDev::getSelectionText(const PDFRectangle *selection, SelectionStyle style) { return text->getSelectionText(selection, style); } bool TextOutputDev::findCharRange(int pos, int length, double *xMin, double *yMin, double *xMax, double *yMax) const { return text->findCharRange(pos, length, xMin, yMin, xMax, yMax); } void TextOutputDev::setMergeCombining(bool merge) { text->setMergeCombining(merge); } #ifdef TEXTOUT_WORD_LIST std::unique_ptr TextOutputDev::makeWordList() { return text->makeWordList(physLayout); } #endif TextPage *TextOutputDev::takeText() { TextPage *ret; ret = text; text = new TextPage(rawOrder, discardDiag); delete actualText; actualText = new ActualText(text); return ret; } const TextFlow *TextOutputDev::getFlows() const { return text->getFlows(); } poppler-24.02.0/poppler/TextOutputDev.h000066400000000000000000001032161455701731300200070ustar00rootroot00000000000000//======================================================================== // // TextOutputDev.h // // Copyright 1997-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) 2005-2007 Kristian Høgsberg // Copyright (C) 2006 Ed Catmur // Copyright (C) 2007, 2008, 2011, 2013 Carlos Garcia Campos // Copyright (C) 2007, 2017 Adrian Johnson // Copyright (C) 2008, 2010, 2015, 2016, 2018, 2019, 2021 Albert Astals Cid // Copyright (C) 2010 Brian Ewins // Copyright (C) 2012, 2013, 2015, 2016 Jason Crain // 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) 2018 Sanchit Anand // Copyright (C) 2018, 2020, 2021 Nelson Benítez León // Copyright (C) 2019, 2022 Oliver Sander // Copyright (C) 2019 Dan Shea // Copyright (C) 2020 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 // //======================================================================== #ifndef TEXTOUTPUTDEV_H #define TEXTOUTPUTDEV_H #include "poppler-config.h" #include "poppler_private_export.h" #include #include "GfxFont.h" #include "GfxState.h" #include "OutputDev.h" class GooString; class Gfx; class GfxFont; class GfxState; class UnicodeMap; class AnnotLink; class TextWord; class TextPool; class TextLine; class TextLineFrag; class TextBlock; class TextFlow; class TextLink; class TextUnderline; class TextWordList; class TextPage; class TextSelectionVisitor; //------------------------------------------------------------------------ typedef void (*TextOutputFunc)(void *stream, const char *text, int len); enum SelectionStyle { selectionStyleGlyph, selectionStyleWord, selectionStyleLine }; enum EndOfLineKind { eolUnix, // LF eolDOS, // CR+LF eolMac // CR }; //------------------------------------------------------------------------ // TextFontInfo //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT TextFontInfo { public: explicit TextFontInfo(const GfxState *state); ~TextFontInfo(); TextFontInfo(const TextFontInfo &) = delete; TextFontInfo &operator=(const TextFontInfo &) = delete; bool matches(const GfxState *state) const; bool matches(const TextFontInfo *fontInfo) const; bool matches(const Ref *ref) const; // Get the font ascent, or a default value if the font is not set double getAscent() const; // Get the font descent, or a default value if the font is not set double getDescent() const; // Get the writing mode (0 or 1), or 0 if the font is not set int getWMode() const; #ifdef TEXTOUT_WORD_LIST // Get the font name (which may be NULL). 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; } #endif private: std::shared_ptr gfxFont; #ifdef TEXTOUT_WORD_LIST GooString *fontName; int flags; #endif friend class TextWord; friend class TextPage; friend class TextSelectionPainter; }; //------------------------------------------------------------------------ // TextWord //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT TextWord { public: // Constructor. TextWord(const GfxState *state, int rotA, double fontSize); // Destructor. ~TextWord(); TextWord(const TextWord &) = delete; TextWord &operator=(const TextWord &) = delete; // Add a character to the word. void 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); // Attempt to add a character to the word as a combining character. // Either character u or the last character in the word must be an // acute, dieresis, or other combining character. Returns true if // the character was added. bool 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); // Merge onto the end of . void merge(TextWord *word); // Compares to , returning -1 (<), 0 (=), or +1 (>), // based on a primary-axis comparison, e.g., x ordering if rot=0. int primaryCmp(const TextWord *word) const; // Return the distance along the primary axis between and // . double primaryDelta(const TextWord *word) const; static int cmpYX(const void *p1, const void *p2); 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]; } // 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]; } GooString *getText() const; const GooString *getFontName(int idx) const { return font[idx]->fontName; } void getColor(double *r, double *g, double *b) const { *r = colorR; *g = colorG; *b = colorB; } void getBBox(double *xMinA, double *yMinA, double *xMaxA, double *yMaxA) const { *xMinA = xMin; *yMinA = yMin; *xMaxA = xMax; *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; } #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; }; private: void ensureCapacity(int capacity); void setInitialBounds(TextFontInfo *fontA, double x, double y); int rot; // rotation, multiple of 90 degrees // (0, 1, 2, or 3) int wMode; // horizontal (0) or vertical (1) writing mode 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 bool spaceAfter; // set if there is a space between this // word and the next word on the line bool underlined; bool invisible; // whether we are invisible (glyphless) TextWord *next; // next word in line #ifdef TEXTOUT_WORD_LIST double colorR, // word color colorG, colorB; #endif AnnotLink *link; friend class TextPool; friend class TextLine; friend class TextBlock; friend class TextFlow; friend class TextWordList; friend class TextPage; friend class TextSelectionPainter; friend class TextSelectionDumper; }; //------------------------------------------------------------------------ // TextPool //------------------------------------------------------------------------ class TextPool { public: TextPool(); ~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; } int getBaseIdx(double base) const; void addWord(TextWord *word); 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 friend class TextBlock; friend class TextPage; }; struct TextFlowData; //------------------------------------------------------------------------ // TextLine //------------------------------------------------------------------------ class TextLine { public: TextLine(TextBlock *blkA, int rotA, double baseA); ~TextLine(); TextLine(const TextLine &) = delete; TextLine &operator=(const TextLine &) = delete; void addWord(TextWord *word); // Return the distance along the primary axis between and // . double primaryDelta(const TextLine *line) const; // Compares to , returning -1 (<), 0 (=), or +1 (>), // based on a primary-axis comparison, e.g., x ordering if rot=0. int primaryCmp(const TextLine *line) const; // Compares to , returning -1 (<), 0 (=), or +1 (>), // based on a secondary-axis comparison of the baselines, e.g., y // ordering if rot=0. int secondaryCmp(const TextLine *line) const; int cmpYX(const TextLine *line) const; static int cmpXY(const void *p1, const void *p2); void coalesce(const UnicodeMap *uMap); void visitSelection(TextSelectionVisitor *visitor, const PDFRectangle *selection, SelectionStyle style); // Get the head of the linked list of TextWords. const TextWord *getWords() const { return words; } // Get the next TextLine on the linked list. const TextLine *getNext() const { return next; } // Returns true if the last char of the line is a hyphen. bool isHyphenated() const { return hyphenated; } private: TextBlock *blk; // parent block int rot; // text rotation double xMin, xMax; // bounding box x coordinates double yMin, yMax; // bounding box y coordinates double base; // baseline x or y coordinate TextWord *words; // words in this line TextWord *lastWord; // last word in this line Unicode *text; // Unicode text of the line, including // spaces between words double *edge; // "near" edge x or y coord of each char // (plus one extra entry for the last char) int *col; // starting column number of each Unicode char int len; // number of Unicode chars int convertedLen; // total number of converted characters bool hyphenated; // set if last char is a hyphen TextLine *next; // next line in block Unicode *normalized; // normalized form of Unicode text int normalized_len; // number of normalized Unicode chars int *normalized_idx; // indices of normalized chars into Unicode text Unicode *ascii_translation; // ascii translation from the normalized text int ascii_len; // length of ascii translation text int *ascii_idx; // indices of ascii chars into Unicode text of line friend class TextLineFrag; friend class TextBlock; friend class TextFlow; friend class TextWordList; friend class TextPage; friend class TextSelectionPainter; friend class TextSelectionSizer; friend class TextSelectionDumper; }; //------------------------------------------------------------------------ // TextBlock //------------------------------------------------------------------------ class TextBlock { public: TextBlock(TextPage *pageA, int rotA); ~TextBlock(); TextBlock(const TextBlock &) = delete; TextBlock &operator=(const TextBlock &) = delete; void addWord(TextWord *word); void coalesce(const UnicodeMap *uMap, double fixedPitch); // 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); int primaryCmp(const TextBlock *blk) const; double secondaryDelta(const TextBlock *blk) const; // Returns true if is below , relative to the page's // primary rotation. bool isBelow(const TextBlock *blk) const; void visitSelection(TextSelectionVisitor *visitor, const PDFRectangle *selection, SelectionStyle style); // Get the head of the linked list of TextLines. const TextLine *getLines() const { return lines; } // Get the next TextBlock on the linked list. const TextBlock *getNext() const { return next; } void getBBox(double *xMinA, double *yMinA, double *xMaxA, double *yMaxA) const { *xMinA = xMin; *yMinA = yMin; *xMaxA = xMax; *yMaxA = yMax; } int getLineCount() const { return nLines; } private: bool isBeforeByRule1(const TextBlock *blk1); bool isBeforeByRepeatedRule1(const TextBlock *blkList, const TextBlock *blk1); bool isBeforeByRule2(const TextBlock *blk1); int visitDepthFirst(TextBlock *blkList, int pos1, TextBlock **sorted, int sortPos, bool *visited); int visitDepthFirst(TextBlock *blkList, int pos1, TextBlock **sorted, int sortPos, bool *visited, TextBlock **cache, int cacheSize); TextPage *page; // the parent page int rot; // text rotation double xMin, xMax; // bounding box x coordinates double yMin, yMax; // bounding box y coordinates double priMin, priMax; // whitespace bounding box along primary axis double ExMin, ExMax; // extended bounding box x coordinates double EyMin, EyMax; // extended bounding box y coordinates int tableId; // id of table to which this block belongs bool tableEnd; // is this block at end of line of actual table TextPool *pool; // pool of words (used only until lines // are built) TextLine *lines; // linked list of lines TextLine *curLine; // most recently added line int nLines; // number of lines int charCount; // number of characters in the block int col; // starting column int nColumns; // number of columns in the block TextBlock *next; TextBlock *stackNext; friend class TextLine; friend class TextLineFrag; friend class TextFlow; friend class TextWordList; friend class TextPage; friend class TextSelectionPainter; friend class TextSelectionDumper; }; //------------------------------------------------------------------------ // TextFlow //------------------------------------------------------------------------ class TextFlow { public: TextFlow(TextPage *pageA, TextBlock *blk); ~TextFlow(); TextFlow(const TextFlow &) = delete; TextFlow &operator=(const TextFlow &) = delete; // Add a block to the end of this flow. void addBlock(TextBlock *blk); // Returns true if fits below in the flow, i.e., (1) // it uses a font no larger than the last block added to the flow, // and (2) it fits within the flow's [priMin, priMax] along the // primary axis. bool blockFits(const TextBlock *blk, const TextBlock *prevBlk) const; // Get the head of the linked list of TextBlocks. const TextBlock *getBlocks() const { return blocks; } // Get the next TextFlow on the linked list. const TextFlow *getNext() const { return next; } private: TextPage *page; // the parent page double xMin, xMax; // bounding box x coordinates double yMin, yMax; // bounding box y coordinates double priMin, priMax; // whitespace bounding box along primary axis TextBlock *blocks; // blocks in flow TextBlock *lastBlk; // last block in this flow TextFlow *next; friend class TextWordList; friend class TextPage; }; #ifdef TEXTOUT_WORD_LIST //------------------------------------------------------------------------ // TextWordList //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT TextWordList { public: // Build a flat word list, in content stream order (if // text->rawOrder is true), physical layout order (if // is true and text->rawOrder is false), or reading order (if both // flags are false). TextWordList(const TextPage *text, bool physLayout); ~TextWordList(); TextWordList(const TextWordList &) = delete; TextWordList &operator=(const TextWordList &) = delete; // Return the number of words on the list. int getLength() const; // Return the th word from the list. TextWord *get(int idx); private: std::vector words; }; #endif // TEXTOUT_WORD_LIST class TextWordSelection { public: TextWordSelection(const TextWord *wordA, int beginA, int endA) : word(wordA), begin(beginA), end(endA) { } const TextWord *getWord() const { return word; } int getBegin() const { return begin; } int getEnd() const { return end; } private: const TextWord *word; int begin; int end; friend class TextSelectionPainter; friend class TextSelectionDumper; }; //------------------------------------------------------------------------ // TextPage //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT TextPage { public: // Constructor. explicit TextPage(bool rawOrderA, bool discardDiagA = false); TextPage(const TextPage &) = delete; TextPage &operator=(const TextPage &) = delete; void incRefCnt(); void decRefCnt(); // Start a new page. void startPage(const GfxState *state); // End the current page. void endPage(); // Update the current font. void updateFont(const GfxState *state); // Begin a new word. void beginWord(const GfxState *state); // Add a character to the current word. void addChar(const GfxState *state, double x, double y, double dx, double dy, CharCode c, int nBytes, const Unicode *u, int uLen); // Add invisible characters. void incCharCount(int nChars); // End the current word, sorting it into the list of words. void endWord(); // Add a word, sorting it into the list of words. void addWord(TextWord *word); // Add a (potential) underline. void addUnderline(double x0, double y0, double x1, double y1); // Add a hyperlink. void addLink(int xMin, int yMin, int xMax, int yMax, AnnotLink *link); // Coalesce strings that look like parts of the same line. void coalesce(bool physLayout, double fixedPitch, bool doHTML); void coalesce(bool physLayout, double fixedPitch, bool doHTML, double minColSpacing1); // Find a string. If is true, starts looking at the // top of the page; else if is true, starts looking // immediately after the last find result; else starts looking at // ,. If is true, stops looking at the // bottom of the page; else if is true, stops looking // just before the last find result; else stops looking at // ,. 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); // Adds new parameter ignoreDiacritics, which will do diacritics // insensitive search, i.e. ignore accents, umlauts, diaeresis,etc. // while matching. This option will be ignored if contains characters // which are not pure ascii. bool findText(const Unicode *s, int len, bool startAtTop, bool stopAtBottom, bool startAtLast, bool stopAtLast, bool caseSensitive, bool ignoreDiacritics, bool backward, bool wholeWord, double *xMin, double *yMin, double *xMax, double *yMax); // Adds new parameter , which allows to match on text // spanning from end of a line to the next line. In that case, the rect for // the part of match that falls on the next line will be stored in // , and if hyphenation (i.e. ignoring hyphen at end of line) // was used while matching at the end of the line prior to , // then will be true, otherwise will be false. // Only finding across two lines is supported, i.e. it won't match where // spans more than two lines. // // will be ignored if is true (as that // combination has not been implemented yet). bool findText(const Unicode *s, int len, bool startAtTop, bool stopAtBottom, bool startAtLast, bool stopAtLast, bool caseSensitive, bool ignoreDiacritics, bool matchAcrossLines, bool backward, bool wholeWord, double *xMin, double *yMin, 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; void visitSelection(TextSelectionVisitor *visitor, const PDFRectangle *selection, SelectionStyle style); void drawSelection(OutputDev *out, double scale, int rotation, const PDFRectangle *selection, SelectionStyle style, const GfxColor *glyph_color, const GfxColor *box_color); std::vector *getSelectionRegion(const PDFRectangle *selection, SelectionStyle style, double scale); GooString *getSelectionText(const PDFRectangle *selection, SelectionStyle style); std::vector **getSelectionWords(const PDFRectangle *selection, SelectionStyle style, int *nLines); // Find a string by character position and length. If found, sets // the text bounding rectangle and returns true; otherwise returns // false. bool findCharRange(int pos, int length, double *xMin, double *yMin, double *xMax, double *yMax) const; // Dump contents of page to a file. void dump(void *outputStream, TextOutputFunc outputFunc, bool physLayout, EndOfLineKind textEOL, bool pageBreaks); // Get the head of the linked list of TextFlows. const TextFlow *getFlows() const { return flows; } // If true, will combine characters when a base and combining // character are drawn on eachother. void setMergeCombining(bool merge); #ifdef TEXTOUT_WORD_LIST // Build a flat word list, in content stream order (if // this->rawOrder is true), physical layout order (if // is true and this->rawOrder is false), or reading order (if both // flags are false). std::unique_ptr makeWordList(bool physLayout); #endif private: // Destructor. ~TextPage(); void clear(); void assignColumns(TextLineFrag *frags, int nFrags, bool rot) const; int dumpFragment(const Unicode *text, int len, const UnicodeMap *uMap, GooString *s) const; void adjustRotation(TextLine *line, int start, int end, double *xMin, double *xMax, double *yMin, double *yMax); bool rawOrder; // keep text in content stream order bool discardDiag; // discard diagonal text bool mergeCombining; // merge when combining and base characters // are drawn on top of each other double pageWidth, pageHeight; // width and height of current page TextWord *curWord; // currently active string int charPos; // next character position (within content // stream) TextFontInfo *curFont; // current font double curFontSize; // current font size int nest; // current nesting level (for Type 3 fonts) int nTinyChars; // number of "tiny" chars seen so far bool lastCharOverlap; // set if the last added char overlapped the // previous char bool diagonal; // whether the current text is diagonal std::unique_ptr pools[4]; // a "pool" of TextWords for each rotation TextFlow *flows; // linked list of flows TextBlock **blocks; // array of blocks, in yx order int nBlocks; // number of blocks int primaryRot; // primary rotation bool primaryLR; // primary direction (true means L-to-R, // false means R-to-L) TextWord *rawWords; // list of words, in raw order (only if // rawOrder is set) TextWord *rawLastWord; // last word on rawWords list std::vector> fonts; // all font info objects used on this page double lastFindXMin, // coordinates of the last "find" result lastFindYMin; bool haveLastFind; std::vector> underlines; std::vector> links; int refCnt; friend class TextLine; friend class TextLineFrag; friend class TextBlock; friend class TextFlow; friend class TextWordList; friend class TextSelectionPainter; friend class TextSelectionDumper; }; //------------------------------------------------------------------------ // ActualText //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT ActualText { public: // Create an ActualText explicit ActualText(TextPage *out); ~ActualText(); ActualText(const ActualText &) = delete; ActualText &operator=(const ActualText &) = delete; void addChar(const GfxState *state, double x, double y, double dx, double dy, CharCode c, int nBytes, const Unicode *u, int uLen); void begin(const GfxState *state, const GooString *text); void end(const GfxState *state); private: TextPage *text; GooString *actualText; // replacement text for the span double actualTextX0; double actualTextY0; double actualTextX1; double actualTextY1; int actualTextNBytes; }; //------------------------------------------------------------------------ // TextOutputDev //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT TextOutputDev : public OutputDev { public: static double minColSpacing1_default; // Open a text output file. If is NULL, no file is // written (this is useful, e.g., for searching text). If // is true, the original physical layout of the text // is maintained. If is true, the text is kept in // content stream order. If is true, diagonal text // is removed from output. TextOutputDev(const char *fileName, bool physLayoutA, double fixedPitchA, bool rawOrderA, bool append, bool discardDiagA = false); // Create a TextOutputDev which will write to a generic stream. If // is true, the original physical layout of the text // is maintained. If is true, the text is kept in // content stream order. If is true, diagonal text // is removed from output. TextOutputDev(TextOutputFunc func, void *stream, bool physLayoutA, double fixedPitchA, bool rawOrderA, bool discardDiagA = false); // Destructor. ~TextOutputDev() override; // Check if file was successfully created. virtual bool isOk() { return ok; } //---- get info about output device // Does this device use upside-down coordinates? // (Upside-down means (0,0) is the top left corner of the page.) bool upsideDown() override { return true; } // Does this device use drawChar() or drawString()? bool useDrawChar() 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; } // Does this device need non-text content? bool needNonText() override { return false; } // Does this device require incCharCount to be called for text on // non-shown layers? bool needCharCount() override { return true; } //----- initialization and control // Start a page. void startPage(int pageNum, GfxState *state, XRef *xref) override; // End a page. void endPage() override; //----- save/restore graphics state void restoreState(GfxState *state) override; //----- update text state void updateFont(GfxState *state) override; //----- text drawing void beginString(GfxState *state, const GooString *s) override; void endString(GfxState *state) override; void drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode c, int nBytes, const Unicode *u, int uLen) override; void incCharCount(int nChars) override; void beginActualText(GfxState *state, const GooString *text) override; void endActualText(GfxState *state) override; //----- path painting void stroke(GfxState *state) override; void fill(GfxState *state) override; void eoFill(GfxState *state) override; //----- link borders void processLink(AnnotLink *link) override; //----- special access // Find a string. If is true, starts looking at the // top of the page; else if is true, starts looking // immediately after the last find result; else starts looking at // ,. If is true, stops looking at the // bottom of the page; else if is true, stops looking // just before the last find result; else stops looking at // ,. 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; // Find a string by character position and length. If found, sets // the text bounding rectangle and returns true; otherwise returns // false. bool findCharRange(int pos, int length, double *xMin, double *yMin, double *xMax, double *yMax) const; void drawSelection(OutputDev *out, double scale, int rotation, const PDFRectangle *selection, SelectionStyle style, const GfxColor *glyph_color, const GfxColor *box_color); std::vector *getSelectionRegion(const PDFRectangle *selection, SelectionStyle style, double scale); GooString *getSelectionText(const PDFRectangle *selection, SelectionStyle style); // If true, will combine characters when a base and combining // character are drawn on eachother. void setMergeCombining(bool merge); #ifdef TEXTOUT_WORD_LIST // Build a flat word list, in content stream order (if // this->rawOrder is true), physical layout order (if // this->physLayout is true and this->rawOrder is false), or reading // order (if both flags are false). std::unique_ptr makeWordList(); #endif // Returns the TextPage object for the last rasterized page, // transferring ownership to the caller. TextPage *takeText(); // Turn extra processing for HTML conversion on or off. void enableHTMLExtras(bool doHTMLA) { doHTML = doHTMLA; } // Get the head of the linked list of TextFlows for the // last rasterized page. const TextFlow *getFlows() const; static constexpr EndOfLineKind defaultEndOfLine() { #if defined(_WIN32) return eolDOS; #else 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; } private: TextOutputFunc outputFunc; // output function void *outputStream; // output stream bool needClose; // need to close the output file? // (only if outputStream is a FILE*) TextPage *text; // text for the current page bool physLayout; // maintain original physical layout when // dumping text double fixedPitch; // if physLayout is true and this is non-zero, // assume fixed-pitch characters with this // width double minColSpacing1; // see default value defined with same name at TextOutputDev.cc bool rawOrder; // keep text in content stream order bool discardDiag; // Diagonal text, i.e., text that is not close to one of the // 0, 90, 180, or 270 degree axes, is discarded. This is useful // to skip watermarks drawn on top of body text, etc. bool doHTML; // extra processing for HTML conversion bool ok; // set up ok? bool textPageBreaks; // insert end-of-page markers? EndOfLineKind textEOL; // type of EOL marker to use ActualText *actualText; }; #endif poppler-24.02.0/poppler/TimesBoldItalicWidths.gperf000066400000000000000000000101501455701731300222440ustar00rootroot00000000000000%{ #include #include "BuiltinFontWidth.h" %} %language=ANSI-C %define initializer-suffix ,0 %define lookup-function-name TimesBoldItalicWidthsLookup %struct-type %omit-struct-type %readonly-tables struct BuiltinFontWidth %% #### Ntilde, 722 rcaron, 389 kcommaaccent, 500 Ncommaaccent, 722 Zacute, 611 comma, 250 cedilla, 333 plusminus, 570 circumflex, 333 dotaccent, 333 edotaccent, 444 asciitilde, 570 colon, 333 onehalf, 750 dollar, 500 Lcaron, 611 ntilde, 556 Aogonek, 667 ncommaaccent, 556 minus, 606 Iogonek, 389 zacute, 389 yen, 500 space, 250 Omacron, 722 questiondown, 500 emdash, 1000 Agrave, 667 three, 500 numbersign, 500 lcaron, 382 A, 667 B, 667 C, 667 aogonek, 500 D, 722 E, 667 onequarter, 750 F, 667 G, 722 H, 778 I, 389 J, 500 K, 667 iogonek, 278 backslash, 278 L, 611 periodcentered, 250 M, 889 N, 722 omacron, 500 Tcommaaccent, 611 O, 722 P, 611 Q, 722 Uhungarumlaut, 722 R, 667 Aacute, 667 caron, 333 S, 556 T, 611 U, 722 agrave, 500 V, 667 W, 889 X, 667 question, 500 equal, 570 Y, 611 Z, 611 four, 500 a, 500 Gcommaaccent, 722 b, 500 c, 444 d, 500 e, 444 f, 333 g, 500 bullet, 350 h, 556 i, 278 Oslash, 722 dagger, 500 j, 278 k, 500 l, 278 m, 778 n, 556 tcommaaccent, 278 o, 500 ordfeminine, 266 ring, 333 p, 500 q, 500 uhungarumlaut, 556 r, 389 twosuperior, 300 aacute, 500 s, 389 OE, 944 t, 278 divide, 570 u, 556 Ccaron, 667 v, 444 w, 667 x, 500 y, 444 z, 389 Gbreve, 722 commaaccent, 250 hungarumlaut, 333 Idotaccent, 389 Nacute, 722 quotedbl, 555 gcommaaccent, 500 mu, 576 greaterequal, 549 Scaron, 556 Lslash, 611 semicolon, 333 oslash, 500 lessequal, 549 lozenge, 494 parenright, 333 ccaron, 444 Ecircumflex, 667 gbreve, 500 trademark, 1000 daggerdbl, 500 nacute, 556 macron, 333 Otilde, 722 Emacron, 667 ellipsis, 1000 scaron, 389 AE, 944 Ucircumflex, 722 lslash, 278 quotedblleft, 500 guilsinglright, 333 hyphen, 333 quotesingle, 278 eight, 500 exclamdown, 389 endash, 500 oe, 722 Abreve, 667 Umacron, 722 ecircumflex, 444 Adieresis, 667 copyright, 747 Egrave, 667 slash, 278 Edieresis, 667 otilde, 500 Idieresis, 389 parenleft, 333 one, 500 emacron, 444 Odieresis, 722 ucircumflex, 556 bracketleft, 333 Ugrave, 722 quoteright, 333 Udieresis, 722 perthousand, 1000 Ydieresis, 611 umacron, 556 abreve, 500 Eacute, 667 adieresis, 500 egrave, 444 edieresis, 444 idieresis, 278 Eth, 722 ae, 722 asterisk, 500 odieresis, 500 Uacute, 722 ugrave, 556 nine, 500 five, 500 udieresis, 556 Zcaron, 611 Scommaaccent, 556 threequarters, 750 guillemotright, 500 Ccedilla, 667 ydieresis, 444 tilde, 333 at, 832 eacute, 444 underscore, 500 Euro, 500 Dcroat, 722 multiply, 570 zero, 500 eth, 500 Scedilla, 556 Ograve, 722 Racute, 667 partialdiff, 494 uacute, 556 braceleft, 348 Thorn, 611 zcaron, 389 scommaaccent, 389 ccedilla, 444 Dcaron, 722 dcroat, 500 Ocircumflex, 722 Oacute, 722 scedilla, 389 ogonek, 333 ograve, 500 racute, 389 Tcaron, 611 Eogonek, 667 thorn, 500 degree, 400 registered, 747 radical, 549 Aring, 667 percent, 833 six, 500 paragraph, 500 dcaron, 608 Uogonek, 722 two, 500 summation, 600 Igrave, 389 Lacute, 611 ocircumflex, 500 oacute, 500 Uring, 722 Lcommaaccent, 611 tcaron, 366 eogonek, 444 Delta, 612 Ohungarumlaut, 722 asciicircum, 570 aring, 500 grave, 333 uogonek, 556 bracketright, 333 Iacute, 389 ampersand, 778 igrave, 278 lacute, 278 Ncaron, 722 plus, 570 uring, 556 quotesinglbase, 333 lcommaaccent, 278 Yacute, 611 ohungarumlaut, 500 threesuperior, 300 acute, 333 section, 500 dieresis, 333 iacute, 278 quotedblbase, 500 ncaron, 556 florin, 500 yacute, 444 Rcommaaccent, 667 fi, 556 fl, 556 Acircumflex, 667 Cacute, 667 Icircumflex, 389 guillemotleft, 500 germandbls, 500 Amacron, 667 seven, 500 Sacute, 556 ordmasculine, 300 dotlessi, 278 sterling, 500 notequal, 549 Imacron, 389 rcommaaccent, 389 Zdotaccent, 611 acircumflex, 500 cacute, 444 Ecaron, 667 icircumflex, 278 braceright, 348 quotedblright, 500 amacron, 500 sacute, 389 imacron, 278 cent, 500 currency, 500 logicalnot, 606 zdotaccent, 389 Atilde, 667 breve, 333 bar, 220 fraction, 167 less, 570 ecaron, 444 guilsinglleft, 333 exclam, 389 period, 250 Rcaron, 667 Kcommaaccent, 667 greater, 570 atilde, 500 brokenbar, 220 quoteleft, 333 Edotaccent, 667 onesuperior, 300 #### %% poppler-24.02.0/poppler/TimesBoldItalicWidths.pregenerated.c000066400000000000000000002650551455701731300240470ustar00rootroot00000000000000/* ANSI-C code produced by gperf version 3.1 */ /* Command-line: gperf poppler/TimesBoldItalicWidths.gperf */ /* Computed positions: -k'1-2,5,$' */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) && ('-' == 45) && ('.' == 46) && ('/' == 47) \ && ('0' == 48) && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) && ('=' == 61) && ('>' == 62) \ && ('?' == 63) && ('A' == 65) && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) && ('N' == 78) \ && ('O' == 79) && ('P' == 80) && ('Q' == 81) && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) && ('k' == 107) && ('l' == 108) \ && ('m' == 109) && ('n' == 110) && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) /* The character set is not based on ISO-646. */ # error "gperf generated tables don't work with this execution character set. Please report a bug to ." #endif #line 1 "poppler/TimesBoldItalicWidths.gperf" #include #include "BuiltinFontWidth.h" #define TOTAL_KEYWORDS 315 #define MIN_WORD_LENGTH 1 #define MAX_WORD_LENGTH 14 #define MIN_HASH_VALUE 1 #define MAX_HASH_VALUE 1041 /* maximum key range = 1041, duplicates = 0 */ #ifdef __GNUC__ __inline #else # ifdef __cplusplus inline # endif #endif static unsigned int hash(register const char *str, register size_t len) { static const unsigned short asso_values[] = { 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 270, 415, 28, 8, 150, 390, 290, 375, 370, 335, 5, 455, 330, 405, 355, 325, 310, 3, 320, 160, 240, 225, 145, 70, 410, 460, 1042, 1042, 1042, 1042, 1042, 1042, 20, 345, 30, 115, 0, 395, 140, 165, 135, 35, 380, 170, 130, 15, 45, 215, 260, 100, 65, 10, 155, 400, 300, 305, 280, 315, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042 }; register unsigned int hval = len; switch (hval) { default: hval += asso_values[(unsigned char)str[4]]; /*FALLTHROUGH*/ case 4: case 3: case 2: hval += asso_values[(unsigned char)str[1]]; /*FALLTHROUGH*/ case 1: hval += asso_values[(unsigned char)str[0]]; break; } return hval + asso_values[(unsigned char)str[len - 1]]; } const struct BuiltinFontWidth *TimesBoldItalicWidthsLookup(register const char *str, register size_t len) { static const struct BuiltinFontWidth wordlist[] = { { "", 0 }, #line 90 "poppler/TimesBoldItalicWidths.gperf" { "e", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 70 "poppler/TimesBoldItalicWidths.gperf" { "R", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 57 "poppler/TimesBoldItalicWidths.gperf" { "K", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 49 "poppler/TimesBoldItalicWidths.gperf" { "D", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 115 "poppler/TimesBoldItalicWidths.gperf" { "t", 278 }, #line 191 "poppler/TimesBoldItalicWidths.gperf" { "ae", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 102 "poppler/TimesBoldItalicWidths.gperf" { "n", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 207 "poppler/TimesBoldItalicWidths.gperf" { "eacute", 444 }, { "", 0 }, { "", 0 }, #line 216 "poppler/TimesBoldItalicWidths.gperf" { "Racute", 667 }, { "", 0 }, #line 85 "poppler/TimesBoldItalicWidths.gperf" { "a", 500 }, #line 206 "poppler/TimesBoldItalicWidths.gperf" { "at", 832 }, { "", 0 }, #line 308 "poppler/TimesBoldItalicWidths.gperf" { "cent", 500 }, { "", 0 }, { "", 0 }, #line 161 "poppler/TimesBoldItalicWidths.gperf" { "oe", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 145 "poppler/TimesBoldItalicWidths.gperf" { "nacute", 556 }, { "", 0 }, #line 254 "poppler/TimesBoldItalicWidths.gperf" { "Delta", 612 }, { "", 0 }, #line 273 "poppler/TimesBoldItalicWidths.gperf" { "acute", 333 }, #line 112 "poppler/TimesBoldItalicWidths.gperf" { "aacute", 500 }, #line 47 "poppler/TimesBoldItalicWidths.gperf" { "C", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 88 "poppler/TimesBoldItalicWidths.gperf" { "c", 444 }, { "", 0 }, #line 173 "poppler/TimesBoldItalicWidths.gperf" { "one", 500 }, #line 285 "poppler/TimesBoldItalicWidths.gperf" { "Cacute", 667 }, { "", 0 }, #line 300 "poppler/TimesBoldItalicWidths.gperf" { "cacute", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 98 "poppler/TimesBoldItalicWidths.gperf" { "j", 278 }, { "", 0 }, { "", 0 }, #line 210 "poppler/TimesBoldItalicWidths.gperf" { "Dcroat", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 249 "poppler/TimesBoldItalicWidths.gperf" { "oacute", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 72 "poppler/TimesBoldItalicWidths.gperf" { "caron", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 104 "poppler/TimesBoldItalicWidths.gperf" { "o", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 317 "poppler/TimesBoldItalicWidths.gperf" { "ecaron", 444 }, { "", 0 }, { "", 0 }, #line 321 "poppler/TimesBoldItalicWidths.gperf" { "Rcaron", 667 }, #line 290 "poppler/TimesBoldItalicWidths.gperf" { "seven", 500 }, #line 306 "poppler/TimesBoldItalicWidths.gperf" { "sacute", 389 }, { "", 0 }, { "", 0 }, #line 224 "poppler/TimesBoldItalicWidths.gperf" { "Dcaron", 722 }, { "", 0 }, #line 252 "poppler/TimesBoldItalicWidths.gperf" { "tcaron", 366 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 26 "poppler/TimesBoldItalicWidths.gperf" { "colon", 333 }, #line 278 "poppler/TimesBoldItalicWidths.gperf" { "ncaron", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 125 "poppler/TimesBoldItalicWidths.gperf" { "commaaccent", 250 }, { "", 0 }, { "", 0 }, #line 135 "poppler/TimesBoldItalicWidths.gperf" { "semicolon", 333 }, #line 19 "poppler/TimesBoldItalicWidths.gperf" { "comma", 250 }, #line 235 "poppler/TimesBoldItalicWidths.gperf" { "degree", 400 }, { "", 0 }, { "", 0 }, #line 118 "poppler/TimesBoldItalicWidths.gperf" { "Ccaron", 667 }, { "", 0 }, #line 140 "poppler/TimesBoldItalicWidths.gperf" { "ccaron", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 113 "poppler/TimesBoldItalicWidths.gperf" { "s", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 231 "poppler/TimesBoldItalicWidths.gperf" { "racute", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 79 "poppler/TimesBoldItalicWidths.gperf" { "X", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 30 "poppler/TimesBoldItalicWidths.gperf" { "ntilde", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 205 "poppler/TimesBoldItalicWidths.gperf" { "tilde", 333 }, #line 324 "poppler/TimesBoldItalicWidths.gperf" { "atilde", 500 }, { "", 0 }, { "", 0 }, #line 196 "poppler/TimesBoldItalicWidths.gperf" { "nine", 500 }, #line 24 "poppler/TimesBoldItalicWidths.gperf" { "edotaccent", 444 }, #line 105 "poppler/TimesBoldItalicWidths.gperf" { "ordfeminine", 266 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 158 "poppler/TimesBoldItalicWidths.gperf" { "eight", 500 }, #line 150 "poppler/TimesBoldItalicWidths.gperf" { "scaron", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 276 "poppler/TimesBoldItalicWidths.gperf" { "iacute", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 170 "poppler/TimesBoldItalicWidths.gperf" { "otilde", 500 }, #line 292 "poppler/TimesBoldItalicWidths.gperf" { "ordmasculine", 300 }, #line 213 "poppler/TimesBoldItalicWidths.gperf" { "eth", 500 }, { "", 0 }, #line 42 "poppler/TimesBoldItalicWidths.gperf" { "three", 500 }, #line 225 "poppler/TimesBoldItalicWidths.gperf" { "dcroat", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 281 "poppler/TimesBoldItalicWidths.gperf" { "Rcommaaccent", 667 }, #line 185 "poppler/TimesBoldItalicWidths.gperf" { "Eacute", 667 }, #line 322 "poppler/TimesBoldItalicWidths.gperf" { "Kcommaaccent", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 218 "poppler/TimesBoldItalicWidths.gperf" { "uacute", 556 }, #line 103 "poppler/TimesBoldItalicWidths.gperf" { "tcommaaccent", 278 }, { "", 0 }, #line 166 "poppler/TimesBoldItalicWidths.gperf" { "copyright", 747 }, #line 43 "poppler/TimesBoldItalicWidths.gperf" { "numbersign", 500 }, #line 15 "poppler/TimesBoldItalicWidths.gperf" { "rcaron", 389 }, #line 32 "poppler/TimesBoldItalicWidths.gperf" { "ncommaaccent", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 110 "poppler/TimesBoldItalicWidths.gperf" { "r", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 264 "poppler/TimesBoldItalicWidths.gperf" { "lacute", 278 }, { "", 0 }, { "", 0 }, #line 23 "poppler/TimesBoldItalicWidths.gperf" { "dotaccent", 333 }, #line 234 "poppler/TimesBoldItalicWidths.gperf" { "thorn", 500 }, #line 242 "poppler/TimesBoldItalicWidths.gperf" { "dcaron", 608 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 146 "poppler/TimesBoldItalicWidths.gperf" { "macron", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 203 "poppler/TimesBoldItalicWidths.gperf" { "Ccedilla", 667 }, #line 274 "poppler/TimesBoldItalicWidths.gperf" { "section", 500 }, #line 223 "poppler/TimesBoldItalicWidths.gperf" { "ccedilla", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 20 "poppler/TimesBoldItalicWidths.gperf" { "cedilla", 333 }, { "", 0 }, { "", 0 }, #line 25 "poppler/TimesBoldItalicWidths.gperf" { "asciitilde", 570 }, #line 89 "poppler/TimesBoldItalicWidths.gperf" { "d", 500 }, #line 239 "poppler/TimesBoldItalicWidths.gperf" { "percent", 833 }, { "", 0 }, { "", 0 }, #line 288 "poppler/TimesBoldItalicWidths.gperf" { "germandbls", 500 }, { "", 0 }, #line 138 "poppler/TimesBoldItalicWidths.gperf" { "lozenge", 494 }, { "", 0 }, #line 316 "poppler/TimesBoldItalicWidths.gperf" { "less", 570 }, { "", 0 }, #line 97 "poppler/TimesBoldItalicWidths.gperf" { "dagger", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 258 "poppler/TimesBoldItalicWidths.gperf" { "grave", 333 }, #line 301 "poppler/TimesBoldItalicWidths.gperf" { "Ecaron", 667 }, #line 222 "poppler/TimesBoldItalicWidths.gperf" { "scommaaccent", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 160 "poppler/TimesBoldItalicWidths.gperf" { "endash", 500 }, #line 174 "poppler/TimesBoldItalicWidths.gperf" { "emacron", 444 }, #line 201 "poppler/TimesBoldItalicWidths.gperf" { "threequarters", 750 }, { "", 0 }, { "", 0 }, #line 232 "poppler/TimesBoldItalicWidths.gperf" { "Tcaron", 611 }, { "", 0 }, #line 228 "poppler/TimesBoldItalicWidths.gperf" { "scedilla", 389 }, { "", 0 }, { "", 0 }, #line 101 "poppler/TimesBoldItalicWidths.gperf" { "m", 778 }, { "", 0 }, { "", 0 }, #line 245 "poppler/TimesBoldItalicWidths.gperf" { "summation", 600 }, #line 310 "poppler/TimesBoldItalicWidths.gperf" { "logicalnot", 606 }, #line 44 "poppler/TimesBoldItalicWidths.gperf" { "lcaron", 382 }, { "", 0 }, { "", 0 }, #line 172 "poppler/TimesBoldItalicWidths.gperf" { "parenleft", 333 }, #line 139 "poppler/TimesBoldItalicWidths.gperf" { "parenright", 333 }, #line 95 "poppler/TimesBoldItalicWidths.gperf" { "i", 278 }, #line 305 "poppler/TimesBoldItalicWidths.gperf" { "amacron", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 194 "poppler/TimesBoldItalicWidths.gperf" { "Uacute", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 208 "poppler/TimesBoldItalicWidths.gperf" { "underscore", 500 }, #line 92 "poppler/TimesBoldItalicWidths.gperf" { "g", 500 }, #line 297 "poppler/TimesBoldItalicWidths.gperf" { "rcommaaccent", 389 }, { "", 0 }, { "", 0 }, #line 37 "poppler/TimesBoldItalicWidths.gperf" { "space", 250 }, #line 28 "poppler/TimesBoldItalicWidths.gperf" { "dollar", 500 }, { "", 0 }, #line 272 "poppler/TimesBoldItalicWidths.gperf" { "threesuperior", 300 }, #line 188 "poppler/TimesBoldItalicWidths.gperf" { "edieresis", 444 }, #line 236 "poppler/TimesBoldItalicWidths.gperf" { "registered", 747 }, #line 78 "poppler/TimesBoldItalicWidths.gperf" { "W", 889 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 64 "poppler/TimesBoldItalicWidths.gperf" { "omacron", 500 }, #line 36 "poppler/TimesBoldItalicWidths.gperf" { "yen", 500 }, { "", 0 }, { "", 0 }, #line 50 "poppler/TimesBoldItalicWidths.gperf" { "E", 667 }, { "", 0 }, #line 293 "poppler/TimesBoldItalicWidths.gperf" { "dotlessi", 278 }, { "", 0 }, #line 327 "poppler/TimesBoldItalicWidths.gperf" { "Edotaccent", 667 }, #line 71 "poppler/TimesBoldItalicWidths.gperf" { "Aacute", 667 }, { "", 0 }, { "", 0 }, #line 186 "poppler/TimesBoldItalicWidths.gperf" { "adieresis", 500 }, { "", 0 }, #line 117 "poppler/TimesBoldItalicWidths.gperf" { "u", 556 }, { "", 0 }, { "", 0 }, #line 144 "poppler/TimesBoldItalicWidths.gperf" { "daggerdbl", 500 }, { "", 0 }, #line 280 "poppler/TimesBoldItalicWidths.gperf" { "yacute", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 74 "poppler/TimesBoldItalicWidths.gperf" { "T", 611 }, #line 130 "poppler/TimesBoldItalicWidths.gperf" { "gcommaaccent", 500 }, #line 275 "poppler/TimesBoldItalicWidths.gperf" { "dieresis", 333 }, { "", 0 }, #line 51 "poppler/TimesBoldItalicWidths.gperf" { "onequarter", 750 }, #line 328 "poppler/TimesBoldItalicWidths.gperf" { "onesuperior", 300 }, #line 237 "poppler/TimesBoldItalicWidths.gperf" { "radical", 549 }, #line 190 "poppler/TimesBoldItalicWidths.gperf" { "Eth", 722 }, { "", 0 }, { "", 0 }, #line 94 "poppler/TimesBoldItalicWidths.gperf" { "h", 556 }, { "", 0 }, { "", 0 }, #line 193 "poppler/TimesBoldItalicWidths.gperf" { "odieresis", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 100 "poppler/TimesBoldItalicWidths.gperf" { "l", 278 }, #line 65 "poppler/TimesBoldItalicWidths.gperf" { "Tcommaaccent", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 136 "poppler/TimesBoldItalicWidths.gperf" { "oslash", 500 }, { "", 0 }, { "", 0 }, #line 137 "poppler/TimesBoldItalicWidths.gperf" { "lessequal", 549 }, #line 159 "poppler/TimesBoldItalicWidths.gperf" { "exclamdown", 389 }, #line 35 "poppler/TimesBoldItalicWidths.gperf" { "zacute", 389 }, #line 269 "poppler/TimesBoldItalicWidths.gperf" { "lcommaaccent", 278 }, { "", 0 }, #line 209 "poppler/TimesBoldItalicWidths.gperf" { "Euro", 500 }, { "", 0 }, #line 291 "poppler/TimesBoldItalicWidths.gperf" { "Sacute", 556 }, #line 323 "poppler/TimesBoldItalicWidths.gperf" { "greater", 570 }, #line 244 "poppler/TimesBoldItalicWidths.gperf" { "two", 500 }, { "", 0 }, #line 220 "poppler/TimesBoldItalicWidths.gperf" { "Thorn", 611 }, #line 256 "poppler/TimesBoldItalicWidths.gperf" { "asciicircum", 570 }, #line 126 "poppler/TimesBoldItalicWidths.gperf" { "hungarumlaut", 333 }, { "", 0 }, #line 212 "poppler/TimesBoldItalicWidths.gperf" { "zero", 500 }, { "", 0 }, #line 40 "poppler/TimesBoldItalicWidths.gperf" { "emdash", 1000 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 116 "poppler/TimesBoldItalicWidths.gperf" { "divide", 570 }, { "", 0 }, #line 271 "poppler/TimesBoldItalicWidths.gperf" { "ohungarumlaut", 500 }, #line 262 "poppler/TimesBoldItalicWidths.gperf" { "ampersand", 778 }, { "", 0 }, #line 164 "poppler/TimesBoldItalicWidths.gperf" { "ecircumflex", 444 }, { "", 0 }, { "", 0 }, #line 106 "poppler/TimesBoldItalicWidths.gperf" { "ring", 333 }, { "", 0 }, #line 320 "poppler/TimesBoldItalicWidths.gperf" { "period", 250 }, { "", 0 }, #line 318 "poppler/TimesBoldItalicWidths.gperf" { "guilsinglleft", 333 }, #line 155 "poppler/TimesBoldItalicWidths.gperf" { "guilsinglright", 333 }, { "", 0 }, { "", 0 }, #line 307 "poppler/TimesBoldItalicWidths.gperf" { "imacron", 278 }, { "", 0 }, #line 61 "poppler/TimesBoldItalicWidths.gperf" { "periodcentered", 250 }, { "", 0 }, #line 227 "poppler/TimesBoldItalicWidths.gperf" { "Oacute", 722 }, { "", 0 }, #line 294 "poppler/TimesBoldItalicWidths.gperf" { "sterling", 500 }, { "", 0 }, { "", 0 }, #line 299 "poppler/TimesBoldItalicWidths.gperf" { "acircumflex", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 33 "poppler/TimesBoldItalicWidths.gperf" { "minus", 606 }, #line 312 "poppler/TimesBoldItalicWidths.gperf" { "Atilde", 667 }, #line 148 "poppler/TimesBoldItalicWidths.gperf" { "Emacron", 667 }, { "", 0 }, { "", 0 }, #line 257 "poppler/TimesBoldItalicWidths.gperf" { "aring", 500 }, #line 261 "poppler/TimesBoldItalicWidths.gperf" { "Iacute", 389 }, #line 183 "poppler/TimesBoldItalicWidths.gperf" { "umacron", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 221 "poppler/TimesBoldItalicWidths.gperf" { "zcaron", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 133 "poppler/TimesBoldItalicWidths.gperf" { "Scaron", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 248 "poppler/TimesBoldItalicWidths.gperf" { "ocircumflex", 500 }, { "", 0 }, { "", 0 }, #line 189 "poppler/TimesBoldItalicWidths.gperf" { "idieresis", 278 }, { "", 0 }, #line 157 "poppler/TimesBoldItalicWidths.gperf" { "quotesingle", 278 }, #line 277 "poppler/TimesBoldItalicWidths.gperf" { "quotedblbase", 500 }, { "", 0 }, #line 268 "poppler/TimesBoldItalicWidths.gperf" { "quotesinglbase", 333 }, { "", 0 }, #line 107 "poppler/TimesBoldItalicWidths.gperf" { "p", 500 }, #line 132 "poppler/TimesBoldItalicWidths.gperf" { "greaterequal", 549 }, { "", 0 }, #line 326 "poppler/TimesBoldItalicWidths.gperf" { "quoteleft", 333 }, #line 179 "poppler/TimesBoldItalicWidths.gperf" { "quoteright", 333 }, { "", 0 }, #line 154 "poppler/TimesBoldItalicWidths.gperf" { "quotedblleft", 500 }, #line 304 "poppler/TimesBoldItalicWidths.gperf" { "quotedblright", 500 }, #line 169 "poppler/TimesBoldItalicWidths.gperf" { "Edieresis", 667 }, { "", 0 }, #line 128 "poppler/TimesBoldItalicWidths.gperf" { "Nacute", 722 }, #line 131 "poppler/TimesBoldItalicWidths.gperf" { "mu", 576 }, { "", 0 }, #line 198 "poppler/TimesBoldItalicWidths.gperf" { "udieresis", 556 }, { "", 0 }, #line 270 "poppler/TimesBoldItalicWidths.gperf" { "Yacute", 611 }, #line 253 "poppler/TimesBoldItalicWidths.gperf" { "eogonek", 444 }, #line 80 "poppler/TimesBoldItalicWidths.gperf" { "question", 500 }, { "", 0 }, #line 313 "poppler/TimesBoldItalicWidths.gperf" { "breve", 333 }, #line 77 "poppler/TimesBoldItalicWidths.gperf" { "V", 667 }, #line 39 "poppler/TimesBoldItalicWidths.gperf" { "questiondown", 500 }, { "", 0 }, #line 266 "poppler/TimesBoldItalicWidths.gperf" { "plus", 570 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 149 "poppler/TimesBoldItalicWidths.gperf" { "ellipsis", 1000 }, { "", 0 }, { "", 0 }, #line 319 "poppler/TimesBoldItalicWidths.gperf" { "exclam", 389 }, { "", 0 }, { "", 0 }, #line 219 "poppler/TimesBoldItalicWidths.gperf" { "braceleft", 348 }, #line 303 "poppler/TimesBoldItalicWidths.gperf" { "braceright", 348 }, #line 156 "poppler/TimesBoldItalicWidths.gperf" { "hyphen", 333 }, #line 48 "poppler/TimesBoldItalicWidths.gperf" { "aogonek", 500 }, #line 314 "poppler/TimesBoldItalicWidths.gperf" { "bar", 220 }, { "", 0 }, #line 311 "poppler/TimesBoldItalicWidths.gperf" { "zdotaccent", 389 }, #line 153 "poppler/TimesBoldItalicWidths.gperf" { "lslash", 278 }, #line 86 "poppler/TimesBoldItalicWidths.gperf" { "Gcommaaccent", 722 }, #line 309 "poppler/TimesBoldItalicWidths.gperf" { "currency", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 75 "poppler/TimesBoldItalicWidths.gperf" { "U", 722 }, #line 27 "poppler/TimesBoldItalicWidths.gperf" { "onehalf", 750 }, #line 109 "poppler/TimesBoldItalicWidths.gperf" { "uhungarumlaut", 556 }, { "", 0 }, { "", 0 }, #line 147 "poppler/TimesBoldItalicWidths.gperf" { "Otilde", 722 }, { "", 0 }, #line 287 "poppler/TimesBoldItalicWidths.gperf" { "guillemotleft", 500 }, #line 202 "poppler/TimesBoldItalicWidths.gperf" { "guillemotright", 500 }, { "", 0 }, #line 247 "poppler/TimesBoldItalicWidths.gperf" { "Lacute", 611 }, #line 163 "poppler/TimesBoldItalicWidths.gperf" { "Umacron", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 18 "poppler/TimesBoldItalicWidths.gperf" { "Zacute", 611 }, { "", 0 }, #line 295 "poppler/TimesBoldItalicWidths.gperf" { "notequal", 549 }, #line 143 "poppler/TimesBoldItalicWidths.gperf" { "trademark", 1000 }, { "", 0 }, #line 265 "poppler/TimesBoldItalicWidths.gperf" { "Ncaron", 722 }, #line 200 "poppler/TimesBoldItalicWidths.gperf" { "Scommaaccent", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 181 "poppler/TimesBoldItalicWidths.gperf" { "perthousand", 1000 }, { "", 0 }, #line 240 "poppler/TimesBoldItalicWidths.gperf" { "six", 500 }, { "", 0 }, { "", 0 }, #line 302 "poppler/TimesBoldItalicWidths.gperf" { "icircumflex", 278 }, { "", 0 }, #line 214 "poppler/TimesBoldItalicWidths.gperf" { "Scedilla", 556 }, { "", 0 }, { "", 0 }, #line 93 "poppler/TimesBoldItalicWidths.gperf" { "bullet", 350 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 108 "poppler/TimesBoldItalicWidths.gperf" { "q", 500 }, #line 289 "poppler/TimesBoldItalicWidths.gperf" { "Amacron", 667 }, { "", 0 }, { "", 0 }, #line 127 "poppler/TimesBoldItalicWidths.gperf" { "Idotaccent", 389 }, #line 141 "poppler/TimesBoldItalicWidths.gperf" { "Ecircumflex", 667 }, { "", 0 }, #line 315 "poppler/TimesBoldItalicWidths.gperf" { "fraction", 167 }, #line 180 "poppler/TimesBoldItalicWidths.gperf" { "Udieresis", 722 }, { "", 0 }, #line 176 "poppler/TimesBoldItalicWidths.gperf" { "ucircumflex", 556 }, { "", 0 }, { "", 0 }, #line 197 "poppler/TimesBoldItalicWidths.gperf" { "five", 500 }, { "", 0 }, #line 14 "poppler/TimesBoldItalicWidths.gperf" { "Ntilde", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 267 "poppler/TimesBoldItalicWidths.gperf" { "uring", 556 }, #line 45 "poppler/TimesBoldItalicWidths.gperf" { "A", 667 }, { "", 0 }, { "", 0 }, #line 84 "poppler/TimesBoldItalicWidths.gperf" { "four", 500 }, { "", 0 }, #line 187 "poppler/TimesBoldItalicWidths.gperf" { "egrave", 444 }, { "", 0 }, { "", 0 }, #line 241 "poppler/TimesBoldItalicWidths.gperf" { "paragraph", 500 }, { "", 0 }, #line 29 "poppler/TimesBoldItalicWidths.gperf" { "Lcaron", 611 }, { "", 0 }, { "", 0 }, #line 325 "poppler/TimesBoldItalicWidths.gperf" { "brokenbar", 220 }, { "", 0 }, #line 199 "poppler/TimesBoldItalicWidths.gperf" { "Zcaron", 611 }, { "", 0 }, { "", 0 }, #line 165 "poppler/TimesBoldItalicWidths.gperf" { "Adieresis", 667 }, { "", 0 }, #line 122 "poppler/TimesBoldItalicWidths.gperf" { "y", 444 }, #line 16 "poppler/TimesBoldItalicWidths.gperf" { "kcommaaccent", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 76 "poppler/TimesBoldItalicWidths.gperf" { "agrave", 500 }, { "", 0 }, #line 69 "poppler/TimesBoldItalicWidths.gperf" { "Uhungarumlaut", 722 }, #line 204 "poppler/TimesBoldItalicWidths.gperf" { "ydieresis", 444 }, #line 168 "poppler/TimesBoldItalicWidths.gperf" { "slash", 278 }, #line 229 "poppler/TimesBoldItalicWidths.gperf" { "ogonek", 333 }, #line 151 "poppler/TimesBoldItalicWidths.gperf" { "AE", 944 }, #line 192 "poppler/TimesBoldItalicWidths.gperf" { "asterisk", 500 }, { "", 0 }, { "", 0 }, #line 111 "poppler/TimesBoldItalicWidths.gperf" { "twosuperior", 300 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 53 "poppler/TimesBoldItalicWidths.gperf" { "G", 722 }, #line 58 "poppler/TimesBoldItalicWidths.gperf" { "iogonek", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 17 "poppler/TimesBoldItalicWidths.gperf" { "Ncommaaccent", 722 }, { "", 0 }, #line 21 "poppler/TimesBoldItalicWidths.gperf" { "plusminus", 570 }, { "", 0 }, #line 230 "poppler/TimesBoldItalicWidths.gperf" { "ograve", 500 }, { "", 0 }, #line 129 "poppler/TimesBoldItalicWidths.gperf" { "quotedbl", 555 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 233 "poppler/TimesBoldItalicWidths.gperf" { "Eogonek", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 120 "poppler/TimesBoldItalicWidths.gperf" { "w", 667 }, #line 259 "poppler/TimesBoldItalicWidths.gperf" { "uogonek", 556 }, { "", 0 }, #line 59 "poppler/TimesBoldItalicWidths.gperf" { "backslash", 278 }, #line 81 "poppler/TimesBoldItalicWidths.gperf" { "equal", 570 }, { "", 0 }, #line 38 "poppler/TimesBoldItalicWidths.gperf" { "Omacron", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 121 "poppler/TimesBoldItalicWidths.gperf" { "x", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 298 "poppler/TimesBoldItalicWidths.gperf" { "Zdotaccent", 611 }, #line 152 "poppler/TimesBoldItalicWidths.gperf" { "Ucircumflex", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 68 "poppler/TimesBoldItalicWidths.gperf" { "Q", 722 }, #line 296 "poppler/TimesBoldItalicWidths.gperf" { "Imacron", 389 }, { "", 0 }, { "", 0 }, #line 250 "poppler/TimesBoldItalicWidths.gperf" { "Uring", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 123 "poppler/TimesBoldItalicWidths.gperf" { "z", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 22 "poppler/TimesBoldItalicWidths.gperf" { "circumflex", 333 }, { "", 0 }, #line 251 "poppler/TimesBoldItalicWidths.gperf" { "Lcommaaccent", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 73 "poppler/TimesBoldItalicWidths.gperf" { "S", 556 }, { "", 0 }, { "", 0 }, #line 175 "poppler/TimesBoldItalicWidths.gperf" { "Odieresis", 722 }, { "", 0 }, #line 284 "poppler/TimesBoldItalicWidths.gperf" { "Acircumflex", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 67 "poppler/TimesBoldItalicWidths.gperf" { "P", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 238 "poppler/TimesBoldItalicWidths.gperf" { "Aring", 667 }, #line 96 "poppler/TimesBoldItalicWidths.gperf" { "Oslash", 722 }, #line 114 "poppler/TimesBoldItalicWidths.gperf" { "OE", 944 }, { "", 0 }, #line 171 "poppler/TimesBoldItalicWidths.gperf" { "Idieresis", 389 }, { "", 0 }, #line 62 "poppler/TimesBoldItalicWidths.gperf" { "M", 889 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 282 "poppler/TimesBoldItalicWidths.gperf" { "fi", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 56 "poppler/TimesBoldItalicWidths.gperf" { "J", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 263 "poppler/TimesBoldItalicWidths.gperf" { "igrave", 278 }, { "", 0 }, #line 255 "poppler/TimesBoldItalicWidths.gperf" { "Ohungarumlaut", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 243 "poppler/TimesBoldItalicWidths.gperf" { "Uogonek", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 87 "poppler/TimesBoldItalicWidths.gperf" { "b", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 167 "poppler/TimesBoldItalicWidths.gperf" { "Egrave", 667 }, { "", 0 }, { "", 0 }, #line 182 "poppler/TimesBoldItalicWidths.gperf" { "Ydieresis", 611 }, { "", 0 }, #line 195 "poppler/TimesBoldItalicWidths.gperf" { "ugrave", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 211 "poppler/TimesBoldItalicWidths.gperf" { "multiply", 570 }, { "", 0 }, { "", 0 }, #line 66 "poppler/TimesBoldItalicWidths.gperf" { "O", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 31 "poppler/TimesBoldItalicWidths.gperf" { "Aogonek", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 279 "poppler/TimesBoldItalicWidths.gperf" { "florin", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 226 "poppler/TimesBoldItalicWidths.gperf" { "Ocircumflex", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 283 "poppler/TimesBoldItalicWidths.gperf" { "fl", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 55 "poppler/TimesBoldItalicWidths.gperf" { "I", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 286 "poppler/TimesBoldItalicWidths.gperf" { "Icircumflex", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 54 "poppler/TimesBoldItalicWidths.gperf" { "H", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 134 "poppler/TimesBoldItalicWidths.gperf" { "Lslash", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 99 "poppler/TimesBoldItalicWidths.gperf" { "k", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 184 "poppler/TimesBoldItalicWidths.gperf" { "abreve", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 217 "poppler/TimesBoldItalicWidths.gperf" { "partialdiff", 494 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 52 "poppler/TimesBoldItalicWidths.gperf" { "F", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 178 "poppler/TimesBoldItalicWidths.gperf" { "Ugrave", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 91 "poppler/TimesBoldItalicWidths.gperf" { "f", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 119 "poppler/TimesBoldItalicWidths.gperf" { "v", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 63 "poppler/TimesBoldItalicWidths.gperf" { "N", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 41 "poppler/TimesBoldItalicWidths.gperf" { "Agrave", 667 }, #line 34 "poppler/TimesBoldItalicWidths.gperf" { "Iogonek", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 82 "poppler/TimesBoldItalicWidths.gperf" { "Y", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 46 "poppler/TimesBoldItalicWidths.gperf" { "B", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 177 "poppler/TimesBoldItalicWidths.gperf" { "bracketleft", 333 }, #line 260 "poppler/TimesBoldItalicWidths.gperf" { "bracketright", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 142 "poppler/TimesBoldItalicWidths.gperf" { "gbreve", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 215 "poppler/TimesBoldItalicWidths.gperf" { "Ograve", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 60 "poppler/TimesBoldItalicWidths.gperf" { "L", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 246 "poppler/TimesBoldItalicWidths.gperf" { "Igrave", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 83 "poppler/TimesBoldItalicWidths.gperf" { "Z", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 162 "poppler/TimesBoldItalicWidths.gperf" { "Abreve", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 124 "poppler/TimesBoldItalicWidths.gperf" { "Gbreve", 722 } }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) { register unsigned int key = hash(str, len); if (key <= MAX_HASH_VALUE) { register const char *s = wordlist[key].name; if (*str == *s && !strcmp(str + 1, s + 1)) return &wordlist[key]; } } return 0; } #line 330 "poppler/TimesBoldItalicWidths.gperf" poppler-24.02.0/poppler/TimesBoldWidths.gperf000066400000000000000000000101461455701731300211230ustar00rootroot00000000000000%{ #include #include "BuiltinFontWidth.h" %} %language=ANSI-C %define initializer-suffix ,0 %define lookup-function-name TimesBoldWidthsLookup %struct-type %omit-struct-type %readonly-tables struct BuiltinFontWidth %% #### Ntilde, 722 rcaron, 444 kcommaaccent, 556 Ncommaaccent, 722 Zacute, 667 comma, 250 cedilla, 333 plusminus, 570 circumflex, 333 dotaccent, 333 edotaccent, 444 asciitilde, 520 colon, 333 onehalf, 750 dollar, 500 Lcaron, 667 ntilde, 556 Aogonek, 722 ncommaaccent, 556 minus, 570 Iogonek, 389 zacute, 444 yen, 500 space, 250 Omacron, 778 questiondown, 500 emdash, 1000 Agrave, 722 three, 500 numbersign, 500 lcaron, 394 A, 722 B, 667 C, 722 aogonek, 500 D, 722 E, 667 onequarter, 750 F, 611 G, 778 H, 778 I, 389 J, 500 K, 778 iogonek, 278 backslash, 278 L, 667 periodcentered, 250 M, 944 N, 722 omacron, 500 Tcommaaccent, 667 O, 778 P, 611 Q, 778 Uhungarumlaut, 722 R, 722 Aacute, 722 caron, 333 S, 556 T, 667 U, 722 agrave, 500 V, 722 W, 1000 X, 722 question, 500 equal, 570 Y, 722 Z, 667 four, 500 a, 500 Gcommaaccent, 778 b, 556 c, 444 d, 556 e, 444 f, 333 g, 500 bullet, 350 h, 556 i, 278 Oslash, 778 dagger, 500 j, 333 k, 556 l, 278 m, 833 n, 556 tcommaaccent, 333 o, 500 ordfeminine, 300 ring, 333 p, 556 q, 556 uhungarumlaut, 556 r, 444 twosuperior, 300 aacute, 500 s, 389 OE, 1000 t, 333 divide, 570 u, 556 Ccaron, 722 v, 500 w, 722 x, 500 y, 500 z, 444 Gbreve, 778 commaaccent, 250 hungarumlaut, 333 Idotaccent, 389 Nacute, 722 quotedbl, 555 gcommaaccent, 500 mu, 556 greaterequal, 549 Scaron, 556 Lslash, 667 semicolon, 333 oslash, 500 lessequal, 549 lozenge, 494 parenright, 333 ccaron, 444 Ecircumflex, 667 gbreve, 500 trademark, 1000 daggerdbl, 500 nacute, 556 macron, 333 Otilde, 778 Emacron, 667 ellipsis, 1000 scaron, 389 AE, 1000 Ucircumflex, 722 lslash, 278 quotedblleft, 500 guilsinglright, 333 hyphen, 333 quotesingle, 278 eight, 500 exclamdown, 333 endash, 500 oe, 722 Abreve, 722 Umacron, 722 ecircumflex, 444 Adieresis, 722 copyright, 747 Egrave, 667 slash, 278 Edieresis, 667 otilde, 500 Idieresis, 389 parenleft, 333 one, 500 emacron, 444 Odieresis, 778 ucircumflex, 556 bracketleft, 333 Ugrave, 722 quoteright, 333 Udieresis, 722 perthousand, 1000 Ydieresis, 722 umacron, 556 abreve, 500 Eacute, 667 adieresis, 500 egrave, 444 edieresis, 444 idieresis, 278 Eth, 722 ae, 722 asterisk, 500 odieresis, 500 Uacute, 722 ugrave, 556 nine, 500 five, 500 udieresis, 556 Zcaron, 667 Scommaaccent, 556 threequarters, 750 guillemotright, 500 Ccedilla, 722 ydieresis, 500 tilde, 333 at, 930 eacute, 444 underscore, 500 Euro, 500 Dcroat, 722 multiply, 570 zero, 500 eth, 500 Scedilla, 556 Ograve, 778 Racute, 722 partialdiff, 494 uacute, 556 braceleft, 394 Thorn, 611 zcaron, 444 scommaaccent, 389 ccedilla, 444 Dcaron, 722 dcroat, 556 Ocircumflex, 778 Oacute, 778 scedilla, 389 ogonek, 333 ograve, 500 racute, 444 Tcaron, 667 Eogonek, 667 thorn, 556 degree, 400 registered, 747 radical, 549 Aring, 722 percent, 1000 six, 500 paragraph, 540 dcaron, 672 Uogonek, 722 two, 500 summation, 600 Igrave, 389 Lacute, 667 ocircumflex, 500 oacute, 500 Uring, 722 Lcommaaccent, 667 tcaron, 416 eogonek, 444 Delta, 612 Ohungarumlaut, 778 asciicircum, 581 aring, 500 grave, 333 uogonek, 556 bracketright, 333 Iacute, 389 ampersand, 833 igrave, 278 lacute, 278 Ncaron, 722 plus, 570 uring, 556 quotesinglbase, 333 lcommaaccent, 278 Yacute, 722 ohungarumlaut, 500 threesuperior, 300 acute, 333 section, 500 dieresis, 333 iacute, 278 quotedblbase, 500 ncaron, 556 florin, 500 yacute, 500 Rcommaaccent, 722 fi, 556 fl, 556 Acircumflex, 722 Cacute, 722 Icircumflex, 389 guillemotleft, 500 germandbls, 556 Amacron, 722 seven, 500 Sacute, 556 ordmasculine, 330 dotlessi, 278 sterling, 500 notequal, 549 Imacron, 389 rcommaaccent, 444 Zdotaccent, 667 acircumflex, 500 cacute, 444 Ecaron, 667 icircumflex, 278 braceright, 394 quotedblright, 500 amacron, 500 sacute, 389 imacron, 278 cent, 500 currency, 500 logicalnot, 570 zdotaccent, 444 Atilde, 722 breve, 333 bar, 220 fraction, 167 less, 570 ecaron, 444 guilsinglleft, 333 exclam, 333 period, 250 Rcaron, 722 Kcommaaccent, 778 greater, 570 atilde, 500 brokenbar, 220 quoteleft, 333 Edotaccent, 667 onesuperior, 300 #### %% poppler-24.02.0/poppler/TimesBoldWidths.pregenerated.c000066400000000000000000002612671455701731300227220ustar00rootroot00000000000000/* ANSI-C code produced by gperf version 3.1 */ /* Command-line: gperf poppler/TimesBoldWidths.gperf */ /* Computed positions: -k'1-2,5,$' */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) && ('-' == 45) && ('.' == 46) && ('/' == 47) \ && ('0' == 48) && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) && ('=' == 61) && ('>' == 62) \ && ('?' == 63) && ('A' == 65) && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) && ('N' == 78) \ && ('O' == 79) && ('P' == 80) && ('Q' == 81) && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) && ('k' == 107) && ('l' == 108) \ && ('m' == 109) && ('n' == 110) && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) /* The character set is not based on ISO-646. */ # error "gperf generated tables don't work with this execution character set. Please report a bug to ." #endif #line 1 "poppler/TimesBoldWidths.gperf" #include #include "BuiltinFontWidth.h" #define TOTAL_KEYWORDS 315 #define MIN_WORD_LENGTH 1 #define MAX_WORD_LENGTH 14 #define MIN_HASH_VALUE 1 #define MAX_HASH_VALUE 1041 /* maximum key range = 1041, duplicates = 0 */ #ifdef __GNUC__ __inline #else # ifdef __cplusplus inline # endif #endif static unsigned int hash(register const char *str, register size_t len) { static const unsigned short asso_values[] = { 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 270, 415, 28, 8, 150, 390, 290, 375, 370, 335, 5, 455, 330, 405, 355, 325, 310, 3, 320, 160, 240, 225, 145, 70, 410, 460, 1042, 1042, 1042, 1042, 1042, 1042, 20, 345, 30, 115, 0, 395, 140, 165, 135, 35, 380, 170, 130, 15, 45, 215, 260, 100, 65, 10, 155, 400, 300, 305, 280, 315, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042 }; register unsigned int hval = len; switch (hval) { default: hval += asso_values[(unsigned char)str[4]]; /*FALLTHROUGH*/ case 4: case 3: case 2: hval += asso_values[(unsigned char)str[1]]; /*FALLTHROUGH*/ case 1: hval += asso_values[(unsigned char)str[0]]; break; } return hval + asso_values[(unsigned char)str[len - 1]]; } const struct BuiltinFontWidth *TimesBoldWidthsLookup(register const char *str, register size_t len) { static const struct BuiltinFontWidth wordlist[] = { { "", 0 }, #line 90 "poppler/TimesBoldWidths.gperf" { "e", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 70 "poppler/TimesBoldWidths.gperf" { "R", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 57 "poppler/TimesBoldWidths.gperf" { "K", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 49 "poppler/TimesBoldWidths.gperf" { "D", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 115 "poppler/TimesBoldWidths.gperf" { "t", 333 }, #line 191 "poppler/TimesBoldWidths.gperf" { "ae", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 102 "poppler/TimesBoldWidths.gperf" { "n", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 207 "poppler/TimesBoldWidths.gperf" { "eacute", 444 }, { "", 0 }, { "", 0 }, #line 216 "poppler/TimesBoldWidths.gperf" { "Racute", 722 }, { "", 0 }, #line 85 "poppler/TimesBoldWidths.gperf" { "a", 500 }, #line 206 "poppler/TimesBoldWidths.gperf" { "at", 930 }, { "", 0 }, #line 308 "poppler/TimesBoldWidths.gperf" { "cent", 500 }, { "", 0 }, { "", 0 }, #line 161 "poppler/TimesBoldWidths.gperf" { "oe", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 145 "poppler/TimesBoldWidths.gperf" { "nacute", 556 }, { "", 0 }, #line 254 "poppler/TimesBoldWidths.gperf" { "Delta", 612 }, { "", 0 }, #line 273 "poppler/TimesBoldWidths.gperf" { "acute", 333 }, #line 112 "poppler/TimesBoldWidths.gperf" { "aacute", 500 }, #line 47 "poppler/TimesBoldWidths.gperf" { "C", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 88 "poppler/TimesBoldWidths.gperf" { "c", 444 }, { "", 0 }, #line 173 "poppler/TimesBoldWidths.gperf" { "one", 500 }, #line 285 "poppler/TimesBoldWidths.gperf" { "Cacute", 722 }, { "", 0 }, #line 300 "poppler/TimesBoldWidths.gperf" { "cacute", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 98 "poppler/TimesBoldWidths.gperf" { "j", 333 }, { "", 0 }, { "", 0 }, #line 210 "poppler/TimesBoldWidths.gperf" { "Dcroat", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 249 "poppler/TimesBoldWidths.gperf" { "oacute", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 72 "poppler/TimesBoldWidths.gperf" { "caron", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 104 "poppler/TimesBoldWidths.gperf" { "o", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 317 "poppler/TimesBoldWidths.gperf" { "ecaron", 444 }, { "", 0 }, { "", 0 }, #line 321 "poppler/TimesBoldWidths.gperf" { "Rcaron", 722 }, #line 290 "poppler/TimesBoldWidths.gperf" { "seven", 500 }, #line 306 "poppler/TimesBoldWidths.gperf" { "sacute", 389 }, { "", 0 }, { "", 0 }, #line 224 "poppler/TimesBoldWidths.gperf" { "Dcaron", 722 }, { "", 0 }, #line 252 "poppler/TimesBoldWidths.gperf" { "tcaron", 416 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 26 "poppler/TimesBoldWidths.gperf" { "colon", 333 }, #line 278 "poppler/TimesBoldWidths.gperf" { "ncaron", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 125 "poppler/TimesBoldWidths.gperf" { "commaaccent", 250 }, { "", 0 }, { "", 0 }, #line 135 "poppler/TimesBoldWidths.gperf" { "semicolon", 333 }, #line 19 "poppler/TimesBoldWidths.gperf" { "comma", 250 }, #line 235 "poppler/TimesBoldWidths.gperf" { "degree", 400 }, { "", 0 }, { "", 0 }, #line 118 "poppler/TimesBoldWidths.gperf" { "Ccaron", 722 }, { "", 0 }, #line 140 "poppler/TimesBoldWidths.gperf" { "ccaron", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 113 "poppler/TimesBoldWidths.gperf" { "s", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 231 "poppler/TimesBoldWidths.gperf" { "racute", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 79 "poppler/TimesBoldWidths.gperf" { "X", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 30 "poppler/TimesBoldWidths.gperf" { "ntilde", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 205 "poppler/TimesBoldWidths.gperf" { "tilde", 333 }, #line 324 "poppler/TimesBoldWidths.gperf" { "atilde", 500 }, { "", 0 }, { "", 0 }, #line 196 "poppler/TimesBoldWidths.gperf" { "nine", 500 }, #line 24 "poppler/TimesBoldWidths.gperf" { "edotaccent", 444 }, #line 105 "poppler/TimesBoldWidths.gperf" { "ordfeminine", 300 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 158 "poppler/TimesBoldWidths.gperf" { "eight", 500 }, #line 150 "poppler/TimesBoldWidths.gperf" { "scaron", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 276 "poppler/TimesBoldWidths.gperf" { "iacute", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 170 "poppler/TimesBoldWidths.gperf" { "otilde", 500 }, #line 292 "poppler/TimesBoldWidths.gperf" { "ordmasculine", 330 }, #line 213 "poppler/TimesBoldWidths.gperf" { "eth", 500 }, { "", 0 }, #line 42 "poppler/TimesBoldWidths.gperf" { "three", 500 }, #line 225 "poppler/TimesBoldWidths.gperf" { "dcroat", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 281 "poppler/TimesBoldWidths.gperf" { "Rcommaaccent", 722 }, #line 185 "poppler/TimesBoldWidths.gperf" { "Eacute", 667 }, #line 322 "poppler/TimesBoldWidths.gperf" { "Kcommaaccent", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 218 "poppler/TimesBoldWidths.gperf" { "uacute", 556 }, #line 103 "poppler/TimesBoldWidths.gperf" { "tcommaaccent", 333 }, { "", 0 }, #line 166 "poppler/TimesBoldWidths.gperf" { "copyright", 747 }, #line 43 "poppler/TimesBoldWidths.gperf" { "numbersign", 500 }, #line 15 "poppler/TimesBoldWidths.gperf" { "rcaron", 444 }, #line 32 "poppler/TimesBoldWidths.gperf" { "ncommaaccent", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 110 "poppler/TimesBoldWidths.gperf" { "r", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 264 "poppler/TimesBoldWidths.gperf" { "lacute", 278 }, { "", 0 }, { "", 0 }, #line 23 "poppler/TimesBoldWidths.gperf" { "dotaccent", 333 }, #line 234 "poppler/TimesBoldWidths.gperf" { "thorn", 556 }, #line 242 "poppler/TimesBoldWidths.gperf" { "dcaron", 672 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 146 "poppler/TimesBoldWidths.gperf" { "macron", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 203 "poppler/TimesBoldWidths.gperf" { "Ccedilla", 722 }, #line 274 "poppler/TimesBoldWidths.gperf" { "section", 500 }, #line 223 "poppler/TimesBoldWidths.gperf" { "ccedilla", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 20 "poppler/TimesBoldWidths.gperf" { "cedilla", 333 }, { "", 0 }, { "", 0 }, #line 25 "poppler/TimesBoldWidths.gperf" { "asciitilde", 520 }, #line 89 "poppler/TimesBoldWidths.gperf" { "d", 556 }, #line 239 "poppler/TimesBoldWidths.gperf" { "percent", 1000 }, { "", 0 }, { "", 0 }, #line 288 "poppler/TimesBoldWidths.gperf" { "germandbls", 556 }, { "", 0 }, #line 138 "poppler/TimesBoldWidths.gperf" { "lozenge", 494 }, { "", 0 }, #line 316 "poppler/TimesBoldWidths.gperf" { "less", 570 }, { "", 0 }, #line 97 "poppler/TimesBoldWidths.gperf" { "dagger", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 258 "poppler/TimesBoldWidths.gperf" { "grave", 333 }, #line 301 "poppler/TimesBoldWidths.gperf" { "Ecaron", 667 }, #line 222 "poppler/TimesBoldWidths.gperf" { "scommaaccent", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 160 "poppler/TimesBoldWidths.gperf" { "endash", 500 }, #line 174 "poppler/TimesBoldWidths.gperf" { "emacron", 444 }, #line 201 "poppler/TimesBoldWidths.gperf" { "threequarters", 750 }, { "", 0 }, { "", 0 }, #line 232 "poppler/TimesBoldWidths.gperf" { "Tcaron", 667 }, { "", 0 }, #line 228 "poppler/TimesBoldWidths.gperf" { "scedilla", 389 }, { "", 0 }, { "", 0 }, #line 101 "poppler/TimesBoldWidths.gperf" { "m", 833 }, { "", 0 }, { "", 0 }, #line 245 "poppler/TimesBoldWidths.gperf" { "summation", 600 }, #line 310 "poppler/TimesBoldWidths.gperf" { "logicalnot", 570 }, #line 44 "poppler/TimesBoldWidths.gperf" { "lcaron", 394 }, { "", 0 }, { "", 0 }, #line 172 "poppler/TimesBoldWidths.gperf" { "parenleft", 333 }, #line 139 "poppler/TimesBoldWidths.gperf" { "parenright", 333 }, #line 95 "poppler/TimesBoldWidths.gperf" { "i", 278 }, #line 305 "poppler/TimesBoldWidths.gperf" { "amacron", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 194 "poppler/TimesBoldWidths.gperf" { "Uacute", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 208 "poppler/TimesBoldWidths.gperf" { "underscore", 500 }, #line 92 "poppler/TimesBoldWidths.gperf" { "g", 500 }, #line 297 "poppler/TimesBoldWidths.gperf" { "rcommaaccent", 444 }, { "", 0 }, { "", 0 }, #line 37 "poppler/TimesBoldWidths.gperf" { "space", 250 }, #line 28 "poppler/TimesBoldWidths.gperf" { "dollar", 500 }, { "", 0 }, #line 272 "poppler/TimesBoldWidths.gperf" { "threesuperior", 300 }, #line 188 "poppler/TimesBoldWidths.gperf" { "edieresis", 444 }, #line 236 "poppler/TimesBoldWidths.gperf" { "registered", 747 }, #line 78 "poppler/TimesBoldWidths.gperf" { "W", 1000 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 64 "poppler/TimesBoldWidths.gperf" { "omacron", 500 }, #line 36 "poppler/TimesBoldWidths.gperf" { "yen", 500 }, { "", 0 }, { "", 0 }, #line 50 "poppler/TimesBoldWidths.gperf" { "E", 667 }, { "", 0 }, #line 293 "poppler/TimesBoldWidths.gperf" { "dotlessi", 278 }, { "", 0 }, #line 327 "poppler/TimesBoldWidths.gperf" { "Edotaccent", 667 }, #line 71 "poppler/TimesBoldWidths.gperf" { "Aacute", 722 }, { "", 0 }, { "", 0 }, #line 186 "poppler/TimesBoldWidths.gperf" { "adieresis", 500 }, { "", 0 }, #line 117 "poppler/TimesBoldWidths.gperf" { "u", 556 }, { "", 0 }, { "", 0 }, #line 144 "poppler/TimesBoldWidths.gperf" { "daggerdbl", 500 }, { "", 0 }, #line 280 "poppler/TimesBoldWidths.gperf" { "yacute", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 74 "poppler/TimesBoldWidths.gperf" { "T", 667 }, #line 130 "poppler/TimesBoldWidths.gperf" { "gcommaaccent", 500 }, #line 275 "poppler/TimesBoldWidths.gperf" { "dieresis", 333 }, { "", 0 }, #line 51 "poppler/TimesBoldWidths.gperf" { "onequarter", 750 }, #line 328 "poppler/TimesBoldWidths.gperf" { "onesuperior", 300 }, #line 237 "poppler/TimesBoldWidths.gperf" { "radical", 549 }, #line 190 "poppler/TimesBoldWidths.gperf" { "Eth", 722 }, { "", 0 }, { "", 0 }, #line 94 "poppler/TimesBoldWidths.gperf" { "h", 556 }, { "", 0 }, { "", 0 }, #line 193 "poppler/TimesBoldWidths.gperf" { "odieresis", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 100 "poppler/TimesBoldWidths.gperf" { "l", 278 }, #line 65 "poppler/TimesBoldWidths.gperf" { "Tcommaaccent", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 136 "poppler/TimesBoldWidths.gperf" { "oslash", 500 }, { "", 0 }, { "", 0 }, #line 137 "poppler/TimesBoldWidths.gperf" { "lessequal", 549 }, #line 159 "poppler/TimesBoldWidths.gperf" { "exclamdown", 333 }, #line 35 "poppler/TimesBoldWidths.gperf" { "zacute", 444 }, #line 269 "poppler/TimesBoldWidths.gperf" { "lcommaaccent", 278 }, { "", 0 }, #line 209 "poppler/TimesBoldWidths.gperf" { "Euro", 500 }, { "", 0 }, #line 291 "poppler/TimesBoldWidths.gperf" { "Sacute", 556 }, #line 323 "poppler/TimesBoldWidths.gperf" { "greater", 570 }, #line 244 "poppler/TimesBoldWidths.gperf" { "two", 500 }, { "", 0 }, #line 220 "poppler/TimesBoldWidths.gperf" { "Thorn", 611 }, #line 256 "poppler/TimesBoldWidths.gperf" { "asciicircum", 581 }, #line 126 "poppler/TimesBoldWidths.gperf" { "hungarumlaut", 333 }, { "", 0 }, #line 212 "poppler/TimesBoldWidths.gperf" { "zero", 500 }, { "", 0 }, #line 40 "poppler/TimesBoldWidths.gperf" { "emdash", 1000 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 116 "poppler/TimesBoldWidths.gperf" { "divide", 570 }, { "", 0 }, #line 271 "poppler/TimesBoldWidths.gperf" { "ohungarumlaut", 500 }, #line 262 "poppler/TimesBoldWidths.gperf" { "ampersand", 833 }, { "", 0 }, #line 164 "poppler/TimesBoldWidths.gperf" { "ecircumflex", 444 }, { "", 0 }, { "", 0 }, #line 106 "poppler/TimesBoldWidths.gperf" { "ring", 333 }, { "", 0 }, #line 320 "poppler/TimesBoldWidths.gperf" { "period", 250 }, { "", 0 }, #line 318 "poppler/TimesBoldWidths.gperf" { "guilsinglleft", 333 }, #line 155 "poppler/TimesBoldWidths.gperf" { "guilsinglright", 333 }, { "", 0 }, { "", 0 }, #line 307 "poppler/TimesBoldWidths.gperf" { "imacron", 278 }, { "", 0 }, #line 61 "poppler/TimesBoldWidths.gperf" { "periodcentered", 250 }, { "", 0 }, #line 227 "poppler/TimesBoldWidths.gperf" { "Oacute", 778 }, { "", 0 }, #line 294 "poppler/TimesBoldWidths.gperf" { "sterling", 500 }, { "", 0 }, { "", 0 }, #line 299 "poppler/TimesBoldWidths.gperf" { "acircumflex", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 33 "poppler/TimesBoldWidths.gperf" { "minus", 570 }, #line 312 "poppler/TimesBoldWidths.gperf" { "Atilde", 722 }, #line 148 "poppler/TimesBoldWidths.gperf" { "Emacron", 667 }, { "", 0 }, { "", 0 }, #line 257 "poppler/TimesBoldWidths.gperf" { "aring", 500 }, #line 261 "poppler/TimesBoldWidths.gperf" { "Iacute", 389 }, #line 183 "poppler/TimesBoldWidths.gperf" { "umacron", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 221 "poppler/TimesBoldWidths.gperf" { "zcaron", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 133 "poppler/TimesBoldWidths.gperf" { "Scaron", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 248 "poppler/TimesBoldWidths.gperf" { "ocircumflex", 500 }, { "", 0 }, { "", 0 }, #line 189 "poppler/TimesBoldWidths.gperf" { "idieresis", 278 }, { "", 0 }, #line 157 "poppler/TimesBoldWidths.gperf" { "quotesingle", 278 }, #line 277 "poppler/TimesBoldWidths.gperf" { "quotedblbase", 500 }, { "", 0 }, #line 268 "poppler/TimesBoldWidths.gperf" { "quotesinglbase", 333 }, { "", 0 }, #line 107 "poppler/TimesBoldWidths.gperf" { "p", 556 }, #line 132 "poppler/TimesBoldWidths.gperf" { "greaterequal", 549 }, { "", 0 }, #line 326 "poppler/TimesBoldWidths.gperf" { "quoteleft", 333 }, #line 179 "poppler/TimesBoldWidths.gperf" { "quoteright", 333 }, { "", 0 }, #line 154 "poppler/TimesBoldWidths.gperf" { "quotedblleft", 500 }, #line 304 "poppler/TimesBoldWidths.gperf" { "quotedblright", 500 }, #line 169 "poppler/TimesBoldWidths.gperf" { "Edieresis", 667 }, { "", 0 }, #line 128 "poppler/TimesBoldWidths.gperf" { "Nacute", 722 }, #line 131 "poppler/TimesBoldWidths.gperf" { "mu", 556 }, { "", 0 }, #line 198 "poppler/TimesBoldWidths.gperf" { "udieresis", 556 }, { "", 0 }, #line 270 "poppler/TimesBoldWidths.gperf" { "Yacute", 722 }, #line 253 "poppler/TimesBoldWidths.gperf" { "eogonek", 444 }, #line 80 "poppler/TimesBoldWidths.gperf" { "question", 500 }, { "", 0 }, #line 313 "poppler/TimesBoldWidths.gperf" { "breve", 333 }, #line 77 "poppler/TimesBoldWidths.gperf" { "V", 722 }, #line 39 "poppler/TimesBoldWidths.gperf" { "questiondown", 500 }, { "", 0 }, #line 266 "poppler/TimesBoldWidths.gperf" { "plus", 570 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 149 "poppler/TimesBoldWidths.gperf" { "ellipsis", 1000 }, { "", 0 }, { "", 0 }, #line 319 "poppler/TimesBoldWidths.gperf" { "exclam", 333 }, { "", 0 }, { "", 0 }, #line 219 "poppler/TimesBoldWidths.gperf" { "braceleft", 394 }, #line 303 "poppler/TimesBoldWidths.gperf" { "braceright", 394 }, #line 156 "poppler/TimesBoldWidths.gperf" { "hyphen", 333 }, #line 48 "poppler/TimesBoldWidths.gperf" { "aogonek", 500 }, #line 314 "poppler/TimesBoldWidths.gperf" { "bar", 220 }, { "", 0 }, #line 311 "poppler/TimesBoldWidths.gperf" { "zdotaccent", 444 }, #line 153 "poppler/TimesBoldWidths.gperf" { "lslash", 278 }, #line 86 "poppler/TimesBoldWidths.gperf" { "Gcommaaccent", 778 }, #line 309 "poppler/TimesBoldWidths.gperf" { "currency", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 75 "poppler/TimesBoldWidths.gperf" { "U", 722 }, #line 27 "poppler/TimesBoldWidths.gperf" { "onehalf", 750 }, #line 109 "poppler/TimesBoldWidths.gperf" { "uhungarumlaut", 556 }, { "", 0 }, { "", 0 }, #line 147 "poppler/TimesBoldWidths.gperf" { "Otilde", 778 }, { "", 0 }, #line 287 "poppler/TimesBoldWidths.gperf" { "guillemotleft", 500 }, #line 202 "poppler/TimesBoldWidths.gperf" { "guillemotright", 500 }, { "", 0 }, #line 247 "poppler/TimesBoldWidths.gperf" { "Lacute", 667 }, #line 163 "poppler/TimesBoldWidths.gperf" { "Umacron", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 18 "poppler/TimesBoldWidths.gperf" { "Zacute", 667 }, { "", 0 }, #line 295 "poppler/TimesBoldWidths.gperf" { "notequal", 549 }, #line 143 "poppler/TimesBoldWidths.gperf" { "trademark", 1000 }, { "", 0 }, #line 265 "poppler/TimesBoldWidths.gperf" { "Ncaron", 722 }, #line 200 "poppler/TimesBoldWidths.gperf" { "Scommaaccent", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 181 "poppler/TimesBoldWidths.gperf" { "perthousand", 1000 }, { "", 0 }, #line 240 "poppler/TimesBoldWidths.gperf" { "six", 500 }, { "", 0 }, { "", 0 }, #line 302 "poppler/TimesBoldWidths.gperf" { "icircumflex", 278 }, { "", 0 }, #line 214 "poppler/TimesBoldWidths.gperf" { "Scedilla", 556 }, { "", 0 }, { "", 0 }, #line 93 "poppler/TimesBoldWidths.gperf" { "bullet", 350 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 108 "poppler/TimesBoldWidths.gperf" { "q", 556 }, #line 289 "poppler/TimesBoldWidths.gperf" { "Amacron", 722 }, { "", 0 }, { "", 0 }, #line 127 "poppler/TimesBoldWidths.gperf" { "Idotaccent", 389 }, #line 141 "poppler/TimesBoldWidths.gperf" { "Ecircumflex", 667 }, { "", 0 }, #line 315 "poppler/TimesBoldWidths.gperf" { "fraction", 167 }, #line 180 "poppler/TimesBoldWidths.gperf" { "Udieresis", 722 }, { "", 0 }, #line 176 "poppler/TimesBoldWidths.gperf" { "ucircumflex", 556 }, { "", 0 }, { "", 0 }, #line 197 "poppler/TimesBoldWidths.gperf" { "five", 500 }, { "", 0 }, #line 14 "poppler/TimesBoldWidths.gperf" { "Ntilde", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 267 "poppler/TimesBoldWidths.gperf" { "uring", 556 }, #line 45 "poppler/TimesBoldWidths.gperf" { "A", 722 }, { "", 0 }, { "", 0 }, #line 84 "poppler/TimesBoldWidths.gperf" { "four", 500 }, { "", 0 }, #line 187 "poppler/TimesBoldWidths.gperf" { "egrave", 444 }, { "", 0 }, { "", 0 }, #line 241 "poppler/TimesBoldWidths.gperf" { "paragraph", 540 }, { "", 0 }, #line 29 "poppler/TimesBoldWidths.gperf" { "Lcaron", 667 }, { "", 0 }, { "", 0 }, #line 325 "poppler/TimesBoldWidths.gperf" { "brokenbar", 220 }, { "", 0 }, #line 199 "poppler/TimesBoldWidths.gperf" { "Zcaron", 667 }, { "", 0 }, { "", 0 }, #line 165 "poppler/TimesBoldWidths.gperf" { "Adieresis", 722 }, { "", 0 }, #line 122 "poppler/TimesBoldWidths.gperf" { "y", 500 }, #line 16 "poppler/TimesBoldWidths.gperf" { "kcommaaccent", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 76 "poppler/TimesBoldWidths.gperf" { "agrave", 500 }, { "", 0 }, #line 69 "poppler/TimesBoldWidths.gperf" { "Uhungarumlaut", 722 }, #line 204 "poppler/TimesBoldWidths.gperf" { "ydieresis", 500 }, #line 168 "poppler/TimesBoldWidths.gperf" { "slash", 278 }, #line 229 "poppler/TimesBoldWidths.gperf" { "ogonek", 333 }, #line 151 "poppler/TimesBoldWidths.gperf" { "AE", 1000 }, #line 192 "poppler/TimesBoldWidths.gperf" { "asterisk", 500 }, { "", 0 }, { "", 0 }, #line 111 "poppler/TimesBoldWidths.gperf" { "twosuperior", 300 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 53 "poppler/TimesBoldWidths.gperf" { "G", 778 }, #line 58 "poppler/TimesBoldWidths.gperf" { "iogonek", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 17 "poppler/TimesBoldWidths.gperf" { "Ncommaaccent", 722 }, { "", 0 }, #line 21 "poppler/TimesBoldWidths.gperf" { "plusminus", 570 }, { "", 0 }, #line 230 "poppler/TimesBoldWidths.gperf" { "ograve", 500 }, { "", 0 }, #line 129 "poppler/TimesBoldWidths.gperf" { "quotedbl", 555 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 233 "poppler/TimesBoldWidths.gperf" { "Eogonek", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 120 "poppler/TimesBoldWidths.gperf" { "w", 722 }, #line 259 "poppler/TimesBoldWidths.gperf" { "uogonek", 556 }, { "", 0 }, #line 59 "poppler/TimesBoldWidths.gperf" { "backslash", 278 }, #line 81 "poppler/TimesBoldWidths.gperf" { "equal", 570 }, { "", 0 }, #line 38 "poppler/TimesBoldWidths.gperf" { "Omacron", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 121 "poppler/TimesBoldWidths.gperf" { "x", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 298 "poppler/TimesBoldWidths.gperf" { "Zdotaccent", 667 }, #line 152 "poppler/TimesBoldWidths.gperf" { "Ucircumflex", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 68 "poppler/TimesBoldWidths.gperf" { "Q", 778 }, #line 296 "poppler/TimesBoldWidths.gperf" { "Imacron", 389 }, { "", 0 }, { "", 0 }, #line 250 "poppler/TimesBoldWidths.gperf" { "Uring", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 123 "poppler/TimesBoldWidths.gperf" { "z", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 22 "poppler/TimesBoldWidths.gperf" { "circumflex", 333 }, { "", 0 }, #line 251 "poppler/TimesBoldWidths.gperf" { "Lcommaaccent", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 73 "poppler/TimesBoldWidths.gperf" { "S", 556 }, { "", 0 }, { "", 0 }, #line 175 "poppler/TimesBoldWidths.gperf" { "Odieresis", 778 }, { "", 0 }, #line 284 "poppler/TimesBoldWidths.gperf" { "Acircumflex", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 67 "poppler/TimesBoldWidths.gperf" { "P", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 238 "poppler/TimesBoldWidths.gperf" { "Aring", 722 }, #line 96 "poppler/TimesBoldWidths.gperf" { "Oslash", 778 }, #line 114 "poppler/TimesBoldWidths.gperf" { "OE", 1000 }, { "", 0 }, #line 171 "poppler/TimesBoldWidths.gperf" { "Idieresis", 389 }, { "", 0 }, #line 62 "poppler/TimesBoldWidths.gperf" { "M", 944 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 282 "poppler/TimesBoldWidths.gperf" { "fi", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 56 "poppler/TimesBoldWidths.gperf" { "J", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 263 "poppler/TimesBoldWidths.gperf" { "igrave", 278 }, { "", 0 }, #line 255 "poppler/TimesBoldWidths.gperf" { "Ohungarumlaut", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 243 "poppler/TimesBoldWidths.gperf" { "Uogonek", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 87 "poppler/TimesBoldWidths.gperf" { "b", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 167 "poppler/TimesBoldWidths.gperf" { "Egrave", 667 }, { "", 0 }, { "", 0 }, #line 182 "poppler/TimesBoldWidths.gperf" { "Ydieresis", 722 }, { "", 0 }, #line 195 "poppler/TimesBoldWidths.gperf" { "ugrave", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 211 "poppler/TimesBoldWidths.gperf" { "multiply", 570 }, { "", 0 }, { "", 0 }, #line 66 "poppler/TimesBoldWidths.gperf" { "O", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 31 "poppler/TimesBoldWidths.gperf" { "Aogonek", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 279 "poppler/TimesBoldWidths.gperf" { "florin", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 226 "poppler/TimesBoldWidths.gperf" { "Ocircumflex", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 283 "poppler/TimesBoldWidths.gperf" { "fl", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 55 "poppler/TimesBoldWidths.gperf" { "I", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 286 "poppler/TimesBoldWidths.gperf" { "Icircumflex", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 54 "poppler/TimesBoldWidths.gperf" { "H", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 134 "poppler/TimesBoldWidths.gperf" { "Lslash", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 99 "poppler/TimesBoldWidths.gperf" { "k", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 184 "poppler/TimesBoldWidths.gperf" { "abreve", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 217 "poppler/TimesBoldWidths.gperf" { "partialdiff", 494 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 52 "poppler/TimesBoldWidths.gperf" { "F", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 178 "poppler/TimesBoldWidths.gperf" { "Ugrave", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 91 "poppler/TimesBoldWidths.gperf" { "f", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 119 "poppler/TimesBoldWidths.gperf" { "v", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 63 "poppler/TimesBoldWidths.gperf" { "N", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 41 "poppler/TimesBoldWidths.gperf" { "Agrave", 722 }, #line 34 "poppler/TimesBoldWidths.gperf" { "Iogonek", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 82 "poppler/TimesBoldWidths.gperf" { "Y", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 46 "poppler/TimesBoldWidths.gperf" { "B", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 177 "poppler/TimesBoldWidths.gperf" { "bracketleft", 333 }, #line 260 "poppler/TimesBoldWidths.gperf" { "bracketright", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 142 "poppler/TimesBoldWidths.gperf" { "gbreve", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 215 "poppler/TimesBoldWidths.gperf" { "Ograve", 778 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 60 "poppler/TimesBoldWidths.gperf" { "L", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 246 "poppler/TimesBoldWidths.gperf" { "Igrave", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 83 "poppler/TimesBoldWidths.gperf" { "Z", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 162 "poppler/TimesBoldWidths.gperf" { "Abreve", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 124 "poppler/TimesBoldWidths.gperf" { "Gbreve", 778 } }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) { register unsigned int key = hash(str, len); if (key <= MAX_HASH_VALUE) { register const char *s = wordlist[key].name; if (*str == *s && !strcmp(str + 1, s + 1)) return &wordlist[key]; } } return 0; } #line 330 "poppler/TimesBoldWidths.gperf" poppler-24.02.0/poppler/TimesItalicWidths.gperf000066400000000000000000000101411455701731300214430ustar00rootroot00000000000000%{ #include #include "BuiltinFontWidth.h" %} %language=ANSI-C %define initializer-suffix ,0 %define lookup-function-name TimesItalicWidthsLookup %struct-type %omit-struct-type %readonly-tables struct BuiltinFontWidth %% #### Ntilde, 667 rcaron, 389 kcommaaccent, 444 Ncommaaccent, 667 Zacute, 556 comma, 250 cedilla, 333 plusminus, 675 circumflex, 333 dotaccent, 333 edotaccent, 444 asciitilde, 541 colon, 333 onehalf, 750 dollar, 500 Lcaron, 611 ntilde, 500 Aogonek, 611 ncommaaccent, 500 minus, 675 Iogonek, 333 zacute, 389 yen, 500 space, 250 Omacron, 722 questiondown, 500 emdash, 889 Agrave, 611 three, 500 numbersign, 500 lcaron, 300 A, 611 B, 611 C, 667 aogonek, 500 D, 722 E, 611 onequarter, 750 F, 611 G, 722 H, 722 I, 333 J, 444 K, 667 iogonek, 278 backslash, 278 L, 556 periodcentered, 250 M, 833 N, 667 omacron, 500 Tcommaaccent, 556 O, 722 P, 611 Q, 722 Uhungarumlaut, 722 R, 611 Aacute, 611 caron, 333 S, 500 T, 556 U, 722 agrave, 500 V, 611 W, 833 X, 611 question, 500 equal, 675 Y, 556 Z, 556 four, 500 a, 500 Gcommaaccent, 722 b, 500 c, 444 d, 500 e, 444 f, 278 g, 500 bullet, 350 h, 500 i, 278 Oslash, 722 dagger, 500 j, 278 k, 444 l, 278 m, 722 n, 500 tcommaaccent, 278 o, 500 ordfeminine, 276 ring, 333 p, 500 q, 500 uhungarumlaut, 500 r, 389 twosuperior, 300 aacute, 500 s, 389 OE, 944 t, 278 divide, 675 u, 500 Ccaron, 667 v, 444 w, 667 x, 444 y, 444 z, 389 Gbreve, 722 commaaccent, 250 hungarumlaut, 333 Idotaccent, 333 Nacute, 667 quotedbl, 420 gcommaaccent, 500 mu, 500 greaterequal, 549 Scaron, 500 Lslash, 556 semicolon, 333 oslash, 500 lessequal, 549 lozenge, 471 parenright, 333 ccaron, 444 Ecircumflex, 611 gbreve, 500 trademark, 980 daggerdbl, 500 nacute, 500 macron, 333 Otilde, 722 Emacron, 611 ellipsis, 889 scaron, 389 AE, 889 Ucircumflex, 722 lslash, 278 quotedblleft, 556 guilsinglright, 333 hyphen, 333 quotesingle, 214 eight, 500 exclamdown, 389 endash, 500 oe, 667 Abreve, 611 Umacron, 722 ecircumflex, 444 Adieresis, 611 copyright, 760 Egrave, 611 slash, 278 Edieresis, 611 otilde, 500 Idieresis, 333 parenleft, 333 one, 500 emacron, 444 Odieresis, 722 ucircumflex, 500 bracketleft, 389 Ugrave, 722 quoteright, 333 Udieresis, 722 perthousand, 1000 Ydieresis, 556 umacron, 500 abreve, 500 Eacute, 611 adieresis, 500 egrave, 444 edieresis, 444 idieresis, 278 Eth, 722 ae, 667 asterisk, 500 odieresis, 500 Uacute, 722 ugrave, 500 nine, 500 five, 500 udieresis, 500 Zcaron, 556 Scommaaccent, 500 threequarters, 750 guillemotright, 500 Ccedilla, 667 ydieresis, 444 tilde, 333 at, 920 eacute, 444 underscore, 500 Euro, 500 Dcroat, 722 multiply, 675 zero, 500 eth, 500 Scedilla, 500 Ograve, 722 Racute, 611 partialdiff, 476 uacute, 500 braceleft, 400 Thorn, 611 zcaron, 389 scommaaccent, 389 ccedilla, 444 Dcaron, 722 dcroat, 500 Ocircumflex, 722 Oacute, 722 scedilla, 389 ogonek, 333 ograve, 500 racute, 389 Tcaron, 556 Eogonek, 611 thorn, 500 degree, 400 registered, 760 radical, 453 Aring, 611 percent, 833 six, 500 paragraph, 523 dcaron, 544 Uogonek, 722 two, 500 summation, 600 Igrave, 333 Lacute, 556 ocircumflex, 500 oacute, 500 Uring, 722 Lcommaaccent, 556 tcaron, 300 eogonek, 444 Delta, 612 Ohungarumlaut, 722 asciicircum, 422 aring, 500 grave, 333 uogonek, 500 bracketright, 389 Iacute, 333 ampersand, 778 igrave, 278 lacute, 278 Ncaron, 667 plus, 675 uring, 500 quotesinglbase, 333 lcommaaccent, 278 Yacute, 556 ohungarumlaut, 500 threesuperior, 300 acute, 333 section, 500 dieresis, 333 iacute, 278 quotedblbase, 556 ncaron, 500 florin, 500 yacute, 444 Rcommaaccent, 611 fi, 500 fl, 500 Acircumflex, 611 Cacute, 667 Icircumflex, 333 guillemotleft, 500 germandbls, 500 Amacron, 611 seven, 500 Sacute, 500 ordmasculine, 310 dotlessi, 278 sterling, 500 notequal, 549 Imacron, 333 rcommaaccent, 389 Zdotaccent, 556 acircumflex, 500 cacute, 444 Ecaron, 611 icircumflex, 278 braceright, 400 quotedblright, 556 amacron, 500 sacute, 389 imacron, 278 cent, 500 currency, 500 logicalnot, 675 zdotaccent, 389 Atilde, 611 breve, 333 bar, 275 fraction, 167 less, 675 ecaron, 444 guilsinglleft, 333 exclam, 333 period, 250 Rcaron, 611 Kcommaaccent, 667 greater, 675 atilde, 500 brokenbar, 275 quoteleft, 333 Edotaccent, 611 onesuperior, 300 #### %% poppler-24.02.0/poppler/TimesItalicWidths.pregenerated.c000066400000000000000000002624561455701731300232500ustar00rootroot00000000000000/* ANSI-C code produced by gperf version 3.1 */ /* Command-line: gperf poppler/TimesItalicWidths.gperf */ /* Computed positions: -k'1-2,5,$' */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) && ('-' == 45) && ('.' == 46) && ('/' == 47) \ && ('0' == 48) && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) && ('=' == 61) && ('>' == 62) \ && ('?' == 63) && ('A' == 65) && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) && ('N' == 78) \ && ('O' == 79) && ('P' == 80) && ('Q' == 81) && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) && ('k' == 107) && ('l' == 108) \ && ('m' == 109) && ('n' == 110) && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) /* The character set is not based on ISO-646. */ # error "gperf generated tables don't work with this execution character set. Please report a bug to ." #endif #line 1 "poppler/TimesItalicWidths.gperf" #include #include "BuiltinFontWidth.h" #define TOTAL_KEYWORDS 315 #define MIN_WORD_LENGTH 1 #define MAX_WORD_LENGTH 14 #define MIN_HASH_VALUE 1 #define MAX_HASH_VALUE 1041 /* maximum key range = 1041, duplicates = 0 */ #ifdef __GNUC__ __inline #else # ifdef __cplusplus inline # endif #endif static unsigned int hash(register const char *str, register size_t len) { static const unsigned short asso_values[] = { 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 270, 415, 28, 8, 150, 390, 290, 375, 370, 335, 5, 455, 330, 405, 355, 325, 310, 3, 320, 160, 240, 225, 145, 70, 410, 460, 1042, 1042, 1042, 1042, 1042, 1042, 20, 345, 30, 115, 0, 395, 140, 165, 135, 35, 380, 170, 130, 15, 45, 215, 260, 100, 65, 10, 155, 400, 300, 305, 280, 315, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042 }; register unsigned int hval = len; switch (hval) { default: hval += asso_values[(unsigned char)str[4]]; /*FALLTHROUGH*/ case 4: case 3: case 2: hval += asso_values[(unsigned char)str[1]]; /*FALLTHROUGH*/ case 1: hval += asso_values[(unsigned char)str[0]]; break; } return hval + asso_values[(unsigned char)str[len - 1]]; } const struct BuiltinFontWidth *TimesItalicWidthsLookup(register const char *str, register size_t len) { static const struct BuiltinFontWidth wordlist[] = { { "", 0 }, #line 90 "poppler/TimesItalicWidths.gperf" { "e", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 70 "poppler/TimesItalicWidths.gperf" { "R", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 57 "poppler/TimesItalicWidths.gperf" { "K", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 49 "poppler/TimesItalicWidths.gperf" { "D", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 115 "poppler/TimesItalicWidths.gperf" { "t", 278 }, #line 191 "poppler/TimesItalicWidths.gperf" { "ae", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 102 "poppler/TimesItalicWidths.gperf" { "n", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 207 "poppler/TimesItalicWidths.gperf" { "eacute", 444 }, { "", 0 }, { "", 0 }, #line 216 "poppler/TimesItalicWidths.gperf" { "Racute", 611 }, { "", 0 }, #line 85 "poppler/TimesItalicWidths.gperf" { "a", 500 }, #line 206 "poppler/TimesItalicWidths.gperf" { "at", 920 }, { "", 0 }, #line 308 "poppler/TimesItalicWidths.gperf" { "cent", 500 }, { "", 0 }, { "", 0 }, #line 161 "poppler/TimesItalicWidths.gperf" { "oe", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 145 "poppler/TimesItalicWidths.gperf" { "nacute", 500 }, { "", 0 }, #line 254 "poppler/TimesItalicWidths.gperf" { "Delta", 612 }, { "", 0 }, #line 273 "poppler/TimesItalicWidths.gperf" { "acute", 333 }, #line 112 "poppler/TimesItalicWidths.gperf" { "aacute", 500 }, #line 47 "poppler/TimesItalicWidths.gperf" { "C", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 88 "poppler/TimesItalicWidths.gperf" { "c", 444 }, { "", 0 }, #line 173 "poppler/TimesItalicWidths.gperf" { "one", 500 }, #line 285 "poppler/TimesItalicWidths.gperf" { "Cacute", 667 }, { "", 0 }, #line 300 "poppler/TimesItalicWidths.gperf" { "cacute", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 98 "poppler/TimesItalicWidths.gperf" { "j", 278 }, { "", 0 }, { "", 0 }, #line 210 "poppler/TimesItalicWidths.gperf" { "Dcroat", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 249 "poppler/TimesItalicWidths.gperf" { "oacute", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 72 "poppler/TimesItalicWidths.gperf" { "caron", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 104 "poppler/TimesItalicWidths.gperf" { "o", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 317 "poppler/TimesItalicWidths.gperf" { "ecaron", 444 }, { "", 0 }, { "", 0 }, #line 321 "poppler/TimesItalicWidths.gperf" { "Rcaron", 611 }, #line 290 "poppler/TimesItalicWidths.gperf" { "seven", 500 }, #line 306 "poppler/TimesItalicWidths.gperf" { "sacute", 389 }, { "", 0 }, { "", 0 }, #line 224 "poppler/TimesItalicWidths.gperf" { "Dcaron", 722 }, { "", 0 }, #line 252 "poppler/TimesItalicWidths.gperf" { "tcaron", 300 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 26 "poppler/TimesItalicWidths.gperf" { "colon", 333 }, #line 278 "poppler/TimesItalicWidths.gperf" { "ncaron", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 125 "poppler/TimesItalicWidths.gperf" { "commaaccent", 250 }, { "", 0 }, { "", 0 }, #line 135 "poppler/TimesItalicWidths.gperf" { "semicolon", 333 }, #line 19 "poppler/TimesItalicWidths.gperf" { "comma", 250 }, #line 235 "poppler/TimesItalicWidths.gperf" { "degree", 400 }, { "", 0 }, { "", 0 }, #line 118 "poppler/TimesItalicWidths.gperf" { "Ccaron", 667 }, { "", 0 }, #line 140 "poppler/TimesItalicWidths.gperf" { "ccaron", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 113 "poppler/TimesItalicWidths.gperf" { "s", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 231 "poppler/TimesItalicWidths.gperf" { "racute", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 79 "poppler/TimesItalicWidths.gperf" { "X", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 30 "poppler/TimesItalicWidths.gperf" { "ntilde", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 205 "poppler/TimesItalicWidths.gperf" { "tilde", 333 }, #line 324 "poppler/TimesItalicWidths.gperf" { "atilde", 500 }, { "", 0 }, { "", 0 }, #line 196 "poppler/TimesItalicWidths.gperf" { "nine", 500 }, #line 24 "poppler/TimesItalicWidths.gperf" { "edotaccent", 444 }, #line 105 "poppler/TimesItalicWidths.gperf" { "ordfeminine", 276 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 158 "poppler/TimesItalicWidths.gperf" { "eight", 500 }, #line 150 "poppler/TimesItalicWidths.gperf" { "scaron", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 276 "poppler/TimesItalicWidths.gperf" { "iacute", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 170 "poppler/TimesItalicWidths.gperf" { "otilde", 500 }, #line 292 "poppler/TimesItalicWidths.gperf" { "ordmasculine", 310 }, #line 213 "poppler/TimesItalicWidths.gperf" { "eth", 500 }, { "", 0 }, #line 42 "poppler/TimesItalicWidths.gperf" { "three", 500 }, #line 225 "poppler/TimesItalicWidths.gperf" { "dcroat", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 281 "poppler/TimesItalicWidths.gperf" { "Rcommaaccent", 611 }, #line 185 "poppler/TimesItalicWidths.gperf" { "Eacute", 611 }, #line 322 "poppler/TimesItalicWidths.gperf" { "Kcommaaccent", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 218 "poppler/TimesItalicWidths.gperf" { "uacute", 500 }, #line 103 "poppler/TimesItalicWidths.gperf" { "tcommaaccent", 278 }, { "", 0 }, #line 166 "poppler/TimesItalicWidths.gperf" { "copyright", 760 }, #line 43 "poppler/TimesItalicWidths.gperf" { "numbersign", 500 }, #line 15 "poppler/TimesItalicWidths.gperf" { "rcaron", 389 }, #line 32 "poppler/TimesItalicWidths.gperf" { "ncommaaccent", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 110 "poppler/TimesItalicWidths.gperf" { "r", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 264 "poppler/TimesItalicWidths.gperf" { "lacute", 278 }, { "", 0 }, { "", 0 }, #line 23 "poppler/TimesItalicWidths.gperf" { "dotaccent", 333 }, #line 234 "poppler/TimesItalicWidths.gperf" { "thorn", 500 }, #line 242 "poppler/TimesItalicWidths.gperf" { "dcaron", 544 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 146 "poppler/TimesItalicWidths.gperf" { "macron", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 203 "poppler/TimesItalicWidths.gperf" { "Ccedilla", 667 }, #line 274 "poppler/TimesItalicWidths.gperf" { "section", 500 }, #line 223 "poppler/TimesItalicWidths.gperf" { "ccedilla", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 20 "poppler/TimesItalicWidths.gperf" { "cedilla", 333 }, { "", 0 }, { "", 0 }, #line 25 "poppler/TimesItalicWidths.gperf" { "asciitilde", 541 }, #line 89 "poppler/TimesItalicWidths.gperf" { "d", 500 }, #line 239 "poppler/TimesItalicWidths.gperf" { "percent", 833 }, { "", 0 }, { "", 0 }, #line 288 "poppler/TimesItalicWidths.gperf" { "germandbls", 500 }, { "", 0 }, #line 138 "poppler/TimesItalicWidths.gperf" { "lozenge", 471 }, { "", 0 }, #line 316 "poppler/TimesItalicWidths.gperf" { "less", 675 }, { "", 0 }, #line 97 "poppler/TimesItalicWidths.gperf" { "dagger", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 258 "poppler/TimesItalicWidths.gperf" { "grave", 333 }, #line 301 "poppler/TimesItalicWidths.gperf" { "Ecaron", 611 }, #line 222 "poppler/TimesItalicWidths.gperf" { "scommaaccent", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 160 "poppler/TimesItalicWidths.gperf" { "endash", 500 }, #line 174 "poppler/TimesItalicWidths.gperf" { "emacron", 444 }, #line 201 "poppler/TimesItalicWidths.gperf" { "threequarters", 750 }, { "", 0 }, { "", 0 }, #line 232 "poppler/TimesItalicWidths.gperf" { "Tcaron", 556 }, { "", 0 }, #line 228 "poppler/TimesItalicWidths.gperf" { "scedilla", 389 }, { "", 0 }, { "", 0 }, #line 101 "poppler/TimesItalicWidths.gperf" { "m", 722 }, { "", 0 }, { "", 0 }, #line 245 "poppler/TimesItalicWidths.gperf" { "summation", 600 }, #line 310 "poppler/TimesItalicWidths.gperf" { "logicalnot", 675 }, #line 44 "poppler/TimesItalicWidths.gperf" { "lcaron", 300 }, { "", 0 }, { "", 0 }, #line 172 "poppler/TimesItalicWidths.gperf" { "parenleft", 333 }, #line 139 "poppler/TimesItalicWidths.gperf" { "parenright", 333 }, #line 95 "poppler/TimesItalicWidths.gperf" { "i", 278 }, #line 305 "poppler/TimesItalicWidths.gperf" { "amacron", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 194 "poppler/TimesItalicWidths.gperf" { "Uacute", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 208 "poppler/TimesItalicWidths.gperf" { "underscore", 500 }, #line 92 "poppler/TimesItalicWidths.gperf" { "g", 500 }, #line 297 "poppler/TimesItalicWidths.gperf" { "rcommaaccent", 389 }, { "", 0 }, { "", 0 }, #line 37 "poppler/TimesItalicWidths.gperf" { "space", 250 }, #line 28 "poppler/TimesItalicWidths.gperf" { "dollar", 500 }, { "", 0 }, #line 272 "poppler/TimesItalicWidths.gperf" { "threesuperior", 300 }, #line 188 "poppler/TimesItalicWidths.gperf" { "edieresis", 444 }, #line 236 "poppler/TimesItalicWidths.gperf" { "registered", 760 }, #line 78 "poppler/TimesItalicWidths.gperf" { "W", 833 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 64 "poppler/TimesItalicWidths.gperf" { "omacron", 500 }, #line 36 "poppler/TimesItalicWidths.gperf" { "yen", 500 }, { "", 0 }, { "", 0 }, #line 50 "poppler/TimesItalicWidths.gperf" { "E", 611 }, { "", 0 }, #line 293 "poppler/TimesItalicWidths.gperf" { "dotlessi", 278 }, { "", 0 }, #line 327 "poppler/TimesItalicWidths.gperf" { "Edotaccent", 611 }, #line 71 "poppler/TimesItalicWidths.gperf" { "Aacute", 611 }, { "", 0 }, { "", 0 }, #line 186 "poppler/TimesItalicWidths.gperf" { "adieresis", 500 }, { "", 0 }, #line 117 "poppler/TimesItalicWidths.gperf" { "u", 500 }, { "", 0 }, { "", 0 }, #line 144 "poppler/TimesItalicWidths.gperf" { "daggerdbl", 500 }, { "", 0 }, #line 280 "poppler/TimesItalicWidths.gperf" { "yacute", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 74 "poppler/TimesItalicWidths.gperf" { "T", 556 }, #line 130 "poppler/TimesItalicWidths.gperf" { "gcommaaccent", 500 }, #line 275 "poppler/TimesItalicWidths.gperf" { "dieresis", 333 }, { "", 0 }, #line 51 "poppler/TimesItalicWidths.gperf" { "onequarter", 750 }, #line 328 "poppler/TimesItalicWidths.gperf" { "onesuperior", 300 }, #line 237 "poppler/TimesItalicWidths.gperf" { "radical", 453 }, #line 190 "poppler/TimesItalicWidths.gperf" { "Eth", 722 }, { "", 0 }, { "", 0 }, #line 94 "poppler/TimesItalicWidths.gperf" { "h", 500 }, { "", 0 }, { "", 0 }, #line 193 "poppler/TimesItalicWidths.gperf" { "odieresis", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 100 "poppler/TimesItalicWidths.gperf" { "l", 278 }, #line 65 "poppler/TimesItalicWidths.gperf" { "Tcommaaccent", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 136 "poppler/TimesItalicWidths.gperf" { "oslash", 500 }, { "", 0 }, { "", 0 }, #line 137 "poppler/TimesItalicWidths.gperf" { "lessequal", 549 }, #line 159 "poppler/TimesItalicWidths.gperf" { "exclamdown", 389 }, #line 35 "poppler/TimesItalicWidths.gperf" { "zacute", 389 }, #line 269 "poppler/TimesItalicWidths.gperf" { "lcommaaccent", 278 }, { "", 0 }, #line 209 "poppler/TimesItalicWidths.gperf" { "Euro", 500 }, { "", 0 }, #line 291 "poppler/TimesItalicWidths.gperf" { "Sacute", 500 }, #line 323 "poppler/TimesItalicWidths.gperf" { "greater", 675 }, #line 244 "poppler/TimesItalicWidths.gperf" { "two", 500 }, { "", 0 }, #line 220 "poppler/TimesItalicWidths.gperf" { "Thorn", 611 }, #line 256 "poppler/TimesItalicWidths.gperf" { "asciicircum", 422 }, #line 126 "poppler/TimesItalicWidths.gperf" { "hungarumlaut", 333 }, { "", 0 }, #line 212 "poppler/TimesItalicWidths.gperf" { "zero", 500 }, { "", 0 }, #line 40 "poppler/TimesItalicWidths.gperf" { "emdash", 889 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 116 "poppler/TimesItalicWidths.gperf" { "divide", 675 }, { "", 0 }, #line 271 "poppler/TimesItalicWidths.gperf" { "ohungarumlaut", 500 }, #line 262 "poppler/TimesItalicWidths.gperf" { "ampersand", 778 }, { "", 0 }, #line 164 "poppler/TimesItalicWidths.gperf" { "ecircumflex", 444 }, { "", 0 }, { "", 0 }, #line 106 "poppler/TimesItalicWidths.gperf" { "ring", 333 }, { "", 0 }, #line 320 "poppler/TimesItalicWidths.gperf" { "period", 250 }, { "", 0 }, #line 318 "poppler/TimesItalicWidths.gperf" { "guilsinglleft", 333 }, #line 155 "poppler/TimesItalicWidths.gperf" { "guilsinglright", 333 }, { "", 0 }, { "", 0 }, #line 307 "poppler/TimesItalicWidths.gperf" { "imacron", 278 }, { "", 0 }, #line 61 "poppler/TimesItalicWidths.gperf" { "periodcentered", 250 }, { "", 0 }, #line 227 "poppler/TimesItalicWidths.gperf" { "Oacute", 722 }, { "", 0 }, #line 294 "poppler/TimesItalicWidths.gperf" { "sterling", 500 }, { "", 0 }, { "", 0 }, #line 299 "poppler/TimesItalicWidths.gperf" { "acircumflex", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 33 "poppler/TimesItalicWidths.gperf" { "minus", 675 }, #line 312 "poppler/TimesItalicWidths.gperf" { "Atilde", 611 }, #line 148 "poppler/TimesItalicWidths.gperf" { "Emacron", 611 }, { "", 0 }, { "", 0 }, #line 257 "poppler/TimesItalicWidths.gperf" { "aring", 500 }, #line 261 "poppler/TimesItalicWidths.gperf" { "Iacute", 333 }, #line 183 "poppler/TimesItalicWidths.gperf" { "umacron", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 221 "poppler/TimesItalicWidths.gperf" { "zcaron", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 133 "poppler/TimesItalicWidths.gperf" { "Scaron", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 248 "poppler/TimesItalicWidths.gperf" { "ocircumflex", 500 }, { "", 0 }, { "", 0 }, #line 189 "poppler/TimesItalicWidths.gperf" { "idieresis", 278 }, { "", 0 }, #line 157 "poppler/TimesItalicWidths.gperf" { "quotesingle", 214 }, #line 277 "poppler/TimesItalicWidths.gperf" { "quotedblbase", 556 }, { "", 0 }, #line 268 "poppler/TimesItalicWidths.gperf" { "quotesinglbase", 333 }, { "", 0 }, #line 107 "poppler/TimesItalicWidths.gperf" { "p", 500 }, #line 132 "poppler/TimesItalicWidths.gperf" { "greaterequal", 549 }, { "", 0 }, #line 326 "poppler/TimesItalicWidths.gperf" { "quoteleft", 333 }, #line 179 "poppler/TimesItalicWidths.gperf" { "quoteright", 333 }, { "", 0 }, #line 154 "poppler/TimesItalicWidths.gperf" { "quotedblleft", 556 }, #line 304 "poppler/TimesItalicWidths.gperf" { "quotedblright", 556 }, #line 169 "poppler/TimesItalicWidths.gperf" { "Edieresis", 611 }, { "", 0 }, #line 128 "poppler/TimesItalicWidths.gperf" { "Nacute", 667 }, #line 131 "poppler/TimesItalicWidths.gperf" { "mu", 500 }, { "", 0 }, #line 198 "poppler/TimesItalicWidths.gperf" { "udieresis", 500 }, { "", 0 }, #line 270 "poppler/TimesItalicWidths.gperf" { "Yacute", 556 }, #line 253 "poppler/TimesItalicWidths.gperf" { "eogonek", 444 }, #line 80 "poppler/TimesItalicWidths.gperf" { "question", 500 }, { "", 0 }, #line 313 "poppler/TimesItalicWidths.gperf" { "breve", 333 }, #line 77 "poppler/TimesItalicWidths.gperf" { "V", 611 }, #line 39 "poppler/TimesItalicWidths.gperf" { "questiondown", 500 }, { "", 0 }, #line 266 "poppler/TimesItalicWidths.gperf" { "plus", 675 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 149 "poppler/TimesItalicWidths.gperf" { "ellipsis", 889 }, { "", 0 }, { "", 0 }, #line 319 "poppler/TimesItalicWidths.gperf" { "exclam", 333 }, { "", 0 }, { "", 0 }, #line 219 "poppler/TimesItalicWidths.gperf" { "braceleft", 400 }, #line 303 "poppler/TimesItalicWidths.gperf" { "braceright", 400 }, #line 156 "poppler/TimesItalicWidths.gperf" { "hyphen", 333 }, #line 48 "poppler/TimesItalicWidths.gperf" { "aogonek", 500 }, #line 314 "poppler/TimesItalicWidths.gperf" { "bar", 275 }, { "", 0 }, #line 311 "poppler/TimesItalicWidths.gperf" { "zdotaccent", 389 }, #line 153 "poppler/TimesItalicWidths.gperf" { "lslash", 278 }, #line 86 "poppler/TimesItalicWidths.gperf" { "Gcommaaccent", 722 }, #line 309 "poppler/TimesItalicWidths.gperf" { "currency", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 75 "poppler/TimesItalicWidths.gperf" { "U", 722 }, #line 27 "poppler/TimesItalicWidths.gperf" { "onehalf", 750 }, #line 109 "poppler/TimesItalicWidths.gperf" { "uhungarumlaut", 500 }, { "", 0 }, { "", 0 }, #line 147 "poppler/TimesItalicWidths.gperf" { "Otilde", 722 }, { "", 0 }, #line 287 "poppler/TimesItalicWidths.gperf" { "guillemotleft", 500 }, #line 202 "poppler/TimesItalicWidths.gperf" { "guillemotright", 500 }, { "", 0 }, #line 247 "poppler/TimesItalicWidths.gperf" { "Lacute", 556 }, #line 163 "poppler/TimesItalicWidths.gperf" { "Umacron", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 18 "poppler/TimesItalicWidths.gperf" { "Zacute", 556 }, { "", 0 }, #line 295 "poppler/TimesItalicWidths.gperf" { "notequal", 549 }, #line 143 "poppler/TimesItalicWidths.gperf" { "trademark", 980 }, { "", 0 }, #line 265 "poppler/TimesItalicWidths.gperf" { "Ncaron", 667 }, #line 200 "poppler/TimesItalicWidths.gperf" { "Scommaaccent", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 181 "poppler/TimesItalicWidths.gperf" { "perthousand", 1000 }, { "", 0 }, #line 240 "poppler/TimesItalicWidths.gperf" { "six", 500 }, { "", 0 }, { "", 0 }, #line 302 "poppler/TimesItalicWidths.gperf" { "icircumflex", 278 }, { "", 0 }, #line 214 "poppler/TimesItalicWidths.gperf" { "Scedilla", 500 }, { "", 0 }, { "", 0 }, #line 93 "poppler/TimesItalicWidths.gperf" { "bullet", 350 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 108 "poppler/TimesItalicWidths.gperf" { "q", 500 }, #line 289 "poppler/TimesItalicWidths.gperf" { "Amacron", 611 }, { "", 0 }, { "", 0 }, #line 127 "poppler/TimesItalicWidths.gperf" { "Idotaccent", 333 }, #line 141 "poppler/TimesItalicWidths.gperf" { "Ecircumflex", 611 }, { "", 0 }, #line 315 "poppler/TimesItalicWidths.gperf" { "fraction", 167 }, #line 180 "poppler/TimesItalicWidths.gperf" { "Udieresis", 722 }, { "", 0 }, #line 176 "poppler/TimesItalicWidths.gperf" { "ucircumflex", 500 }, { "", 0 }, { "", 0 }, #line 197 "poppler/TimesItalicWidths.gperf" { "five", 500 }, { "", 0 }, #line 14 "poppler/TimesItalicWidths.gperf" { "Ntilde", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 267 "poppler/TimesItalicWidths.gperf" { "uring", 500 }, #line 45 "poppler/TimesItalicWidths.gperf" { "A", 611 }, { "", 0 }, { "", 0 }, #line 84 "poppler/TimesItalicWidths.gperf" { "four", 500 }, { "", 0 }, #line 187 "poppler/TimesItalicWidths.gperf" { "egrave", 444 }, { "", 0 }, { "", 0 }, #line 241 "poppler/TimesItalicWidths.gperf" { "paragraph", 523 }, { "", 0 }, #line 29 "poppler/TimesItalicWidths.gperf" { "Lcaron", 611 }, { "", 0 }, { "", 0 }, #line 325 "poppler/TimesItalicWidths.gperf" { "brokenbar", 275 }, { "", 0 }, #line 199 "poppler/TimesItalicWidths.gperf" { "Zcaron", 556 }, { "", 0 }, { "", 0 }, #line 165 "poppler/TimesItalicWidths.gperf" { "Adieresis", 611 }, { "", 0 }, #line 122 "poppler/TimesItalicWidths.gperf" { "y", 444 }, #line 16 "poppler/TimesItalicWidths.gperf" { "kcommaaccent", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 76 "poppler/TimesItalicWidths.gperf" { "agrave", 500 }, { "", 0 }, #line 69 "poppler/TimesItalicWidths.gperf" { "Uhungarumlaut", 722 }, #line 204 "poppler/TimesItalicWidths.gperf" { "ydieresis", 444 }, #line 168 "poppler/TimesItalicWidths.gperf" { "slash", 278 }, #line 229 "poppler/TimesItalicWidths.gperf" { "ogonek", 333 }, #line 151 "poppler/TimesItalicWidths.gperf" { "AE", 889 }, #line 192 "poppler/TimesItalicWidths.gperf" { "asterisk", 500 }, { "", 0 }, { "", 0 }, #line 111 "poppler/TimesItalicWidths.gperf" { "twosuperior", 300 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 53 "poppler/TimesItalicWidths.gperf" { "G", 722 }, #line 58 "poppler/TimesItalicWidths.gperf" { "iogonek", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 17 "poppler/TimesItalicWidths.gperf" { "Ncommaaccent", 667 }, { "", 0 }, #line 21 "poppler/TimesItalicWidths.gperf" { "plusminus", 675 }, { "", 0 }, #line 230 "poppler/TimesItalicWidths.gperf" { "ograve", 500 }, { "", 0 }, #line 129 "poppler/TimesItalicWidths.gperf" { "quotedbl", 420 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 233 "poppler/TimesItalicWidths.gperf" { "Eogonek", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 120 "poppler/TimesItalicWidths.gperf" { "w", 667 }, #line 259 "poppler/TimesItalicWidths.gperf" { "uogonek", 500 }, { "", 0 }, #line 59 "poppler/TimesItalicWidths.gperf" { "backslash", 278 }, #line 81 "poppler/TimesItalicWidths.gperf" { "equal", 675 }, { "", 0 }, #line 38 "poppler/TimesItalicWidths.gperf" { "Omacron", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 121 "poppler/TimesItalicWidths.gperf" { "x", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 298 "poppler/TimesItalicWidths.gperf" { "Zdotaccent", 556 }, #line 152 "poppler/TimesItalicWidths.gperf" { "Ucircumflex", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 68 "poppler/TimesItalicWidths.gperf" { "Q", 722 }, #line 296 "poppler/TimesItalicWidths.gperf" { "Imacron", 333 }, { "", 0 }, { "", 0 }, #line 250 "poppler/TimesItalicWidths.gperf" { "Uring", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 123 "poppler/TimesItalicWidths.gperf" { "z", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 22 "poppler/TimesItalicWidths.gperf" { "circumflex", 333 }, { "", 0 }, #line 251 "poppler/TimesItalicWidths.gperf" { "Lcommaaccent", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 73 "poppler/TimesItalicWidths.gperf" { "S", 500 }, { "", 0 }, { "", 0 }, #line 175 "poppler/TimesItalicWidths.gperf" { "Odieresis", 722 }, { "", 0 }, #line 284 "poppler/TimesItalicWidths.gperf" { "Acircumflex", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 67 "poppler/TimesItalicWidths.gperf" { "P", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 238 "poppler/TimesItalicWidths.gperf" { "Aring", 611 }, #line 96 "poppler/TimesItalicWidths.gperf" { "Oslash", 722 }, #line 114 "poppler/TimesItalicWidths.gperf" { "OE", 944 }, { "", 0 }, #line 171 "poppler/TimesItalicWidths.gperf" { "Idieresis", 333 }, { "", 0 }, #line 62 "poppler/TimesItalicWidths.gperf" { "M", 833 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 282 "poppler/TimesItalicWidths.gperf" { "fi", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 56 "poppler/TimesItalicWidths.gperf" { "J", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 263 "poppler/TimesItalicWidths.gperf" { "igrave", 278 }, { "", 0 }, #line 255 "poppler/TimesItalicWidths.gperf" { "Ohungarumlaut", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 243 "poppler/TimesItalicWidths.gperf" { "Uogonek", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 87 "poppler/TimesItalicWidths.gperf" { "b", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 167 "poppler/TimesItalicWidths.gperf" { "Egrave", 611 }, { "", 0 }, { "", 0 }, #line 182 "poppler/TimesItalicWidths.gperf" { "Ydieresis", 556 }, { "", 0 }, #line 195 "poppler/TimesItalicWidths.gperf" { "ugrave", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 211 "poppler/TimesItalicWidths.gperf" { "multiply", 675 }, { "", 0 }, { "", 0 }, #line 66 "poppler/TimesItalicWidths.gperf" { "O", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 31 "poppler/TimesItalicWidths.gperf" { "Aogonek", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 279 "poppler/TimesItalicWidths.gperf" { "florin", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 226 "poppler/TimesItalicWidths.gperf" { "Ocircumflex", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 283 "poppler/TimesItalicWidths.gperf" { "fl", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 55 "poppler/TimesItalicWidths.gperf" { "I", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 286 "poppler/TimesItalicWidths.gperf" { "Icircumflex", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 54 "poppler/TimesItalicWidths.gperf" { "H", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 134 "poppler/TimesItalicWidths.gperf" { "Lslash", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 99 "poppler/TimesItalicWidths.gperf" { "k", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 184 "poppler/TimesItalicWidths.gperf" { "abreve", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 217 "poppler/TimesItalicWidths.gperf" { "partialdiff", 476 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 52 "poppler/TimesItalicWidths.gperf" { "F", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 178 "poppler/TimesItalicWidths.gperf" { "Ugrave", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 91 "poppler/TimesItalicWidths.gperf" { "f", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 119 "poppler/TimesItalicWidths.gperf" { "v", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 63 "poppler/TimesItalicWidths.gperf" { "N", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 41 "poppler/TimesItalicWidths.gperf" { "Agrave", 611 }, #line 34 "poppler/TimesItalicWidths.gperf" { "Iogonek", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 82 "poppler/TimesItalicWidths.gperf" { "Y", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 46 "poppler/TimesItalicWidths.gperf" { "B", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 177 "poppler/TimesItalicWidths.gperf" { "bracketleft", 389 }, #line 260 "poppler/TimesItalicWidths.gperf" { "bracketright", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 142 "poppler/TimesItalicWidths.gperf" { "gbreve", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 215 "poppler/TimesItalicWidths.gperf" { "Ograve", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 60 "poppler/TimesItalicWidths.gperf" { "L", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 246 "poppler/TimesItalicWidths.gperf" { "Igrave", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 83 "poppler/TimesItalicWidths.gperf" { "Z", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 162 "poppler/TimesItalicWidths.gperf" { "Abreve", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 124 "poppler/TimesItalicWidths.gperf" { "Gbreve", 722 } }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) { register unsigned int key = hash(str, len); if (key <= MAX_HASH_VALUE) { register const char *s = wordlist[key].name; if (*str == *s && !strcmp(str + 1, s + 1)) return &wordlist[key]; } } return 0; } #line 330 "poppler/TimesItalicWidths.gperf" poppler-24.02.0/poppler/TimesRomanWidths.gperf000066400000000000000000000101421455701731300213130ustar00rootroot00000000000000%{ #include #include "BuiltinFontWidth.h" %} %language=ANSI-C %define initializer-suffix ,0 %define lookup-function-name TimesRomanWidthsLookup %struct-type %omit-struct-type %readonly-tables struct BuiltinFontWidth %% #### Ntilde, 722 rcaron, 333 kcommaaccent, 500 Ncommaaccent, 722 Zacute, 611 comma, 250 cedilla, 333 plusminus, 564 circumflex, 333 dotaccent, 333 edotaccent, 444 asciitilde, 541 colon, 278 onehalf, 750 dollar, 500 Lcaron, 611 ntilde, 500 Aogonek, 722 ncommaaccent, 500 minus, 564 Iogonek, 333 zacute, 444 yen, 500 space, 250 Omacron, 722 questiondown, 444 emdash, 1000 Agrave, 722 three, 500 numbersign, 500 lcaron, 344 A, 722 B, 667 C, 667 aogonek, 444 D, 722 E, 611 onequarter, 750 F, 556 G, 722 H, 722 I, 333 J, 389 K, 722 iogonek, 278 backslash, 278 L, 611 periodcentered, 250 M, 889 N, 722 omacron, 500 Tcommaaccent, 611 O, 722 P, 556 Q, 722 Uhungarumlaut, 722 R, 667 Aacute, 722 caron, 333 S, 556 T, 611 U, 722 agrave, 444 V, 722 W, 944 X, 722 question, 444 equal, 564 Y, 722 Z, 611 four, 500 a, 444 Gcommaaccent, 722 b, 500 c, 444 d, 500 e, 444 f, 333 g, 500 bullet, 350 h, 500 i, 278 Oslash, 722 dagger, 500 j, 278 k, 500 l, 278 m, 778 n, 500 tcommaaccent, 278 o, 500 ordfeminine, 276 ring, 333 p, 500 q, 500 uhungarumlaut, 500 r, 333 twosuperior, 300 aacute, 444 s, 389 OE, 889 t, 278 divide, 564 u, 500 Ccaron, 667 v, 500 w, 722 x, 500 y, 500 z, 444 Gbreve, 722 commaaccent, 250 hungarumlaut, 333 Idotaccent, 333 Nacute, 722 quotedbl, 408 gcommaaccent, 500 mu, 500 greaterequal, 549 Scaron, 556 Lslash, 611 semicolon, 278 oslash, 500 lessequal, 549 lozenge, 471 parenright, 333 ccaron, 444 Ecircumflex, 611 gbreve, 500 trademark, 980 daggerdbl, 500 nacute, 500 macron, 333 Otilde, 722 Emacron, 611 ellipsis, 1000 scaron, 389 AE, 889 Ucircumflex, 722 lslash, 278 quotedblleft, 444 guilsinglright, 333 hyphen, 333 quotesingle, 180 eight, 500 exclamdown, 333 endash, 500 oe, 722 Abreve, 722 Umacron, 722 ecircumflex, 444 Adieresis, 722 copyright, 760 Egrave, 611 slash, 278 Edieresis, 611 otilde, 500 Idieresis, 333 parenleft, 333 one, 500 emacron, 444 Odieresis, 722 ucircumflex, 500 bracketleft, 333 Ugrave, 722 quoteright, 333 Udieresis, 722 perthousand, 1000 Ydieresis, 722 umacron, 500 abreve, 444 Eacute, 611 adieresis, 444 egrave, 444 edieresis, 444 idieresis, 278 Eth, 722 ae, 667 asterisk, 500 odieresis, 500 Uacute, 722 ugrave, 500 nine, 500 five, 500 udieresis, 500 Zcaron, 611 Scommaaccent, 556 threequarters, 750 guillemotright, 500 Ccedilla, 667 ydieresis, 500 tilde, 333 at, 921 eacute, 444 underscore, 500 Euro, 500 Dcroat, 722 multiply, 564 zero, 500 eth, 500 Scedilla, 556 Ograve, 722 Racute, 667 partialdiff, 476 uacute, 500 braceleft, 480 Thorn, 556 zcaron, 444 scommaaccent, 389 ccedilla, 444 Dcaron, 722 dcroat, 500 Ocircumflex, 722 Oacute, 722 scedilla, 389 ogonek, 333 ograve, 500 racute, 333 Tcaron, 611 Eogonek, 611 thorn, 500 degree, 400 registered, 760 radical, 453 Aring, 722 percent, 833 six, 500 paragraph, 453 dcaron, 588 Uogonek, 722 two, 500 summation, 600 Igrave, 333 Lacute, 611 ocircumflex, 500 oacute, 500 Uring, 722 Lcommaaccent, 611 tcaron, 326 eogonek, 444 Delta, 612 Ohungarumlaut, 722 asciicircum, 469 aring, 444 grave, 333 uogonek, 500 bracketright, 333 Iacute, 333 ampersand, 778 igrave, 278 lacute, 278 Ncaron, 722 plus, 564 uring, 500 quotesinglbase, 333 lcommaaccent, 278 Yacute, 722 ohungarumlaut, 500 threesuperior, 300 acute, 333 section, 500 dieresis, 333 iacute, 278 quotedblbase, 444 ncaron, 500 florin, 500 yacute, 500 Rcommaaccent, 667 fi, 556 fl, 556 Acircumflex, 722 Cacute, 667 Icircumflex, 333 guillemotleft, 500 germandbls, 500 Amacron, 722 seven, 500 Sacute, 556 ordmasculine, 310 dotlessi, 278 sterling, 500 notequal, 549 Imacron, 333 rcommaaccent, 333 Zdotaccent, 611 acircumflex, 444 cacute, 444 Ecaron, 611 icircumflex, 278 braceright, 480 quotedblright, 444 amacron, 444 sacute, 389 imacron, 278 cent, 500 currency, 500 logicalnot, 564 zdotaccent, 444 Atilde, 722 breve, 333 bar, 200 fraction, 167 less, 564 ecaron, 444 guilsinglleft, 333 exclam, 333 period, 250 Rcaron, 667 Kcommaaccent, 722 greater, 564 atilde, 444 brokenbar, 200 quoteleft, 333 Edotaccent, 611 onesuperior, 300 #### %% poppler-24.02.0/poppler/TimesRomanWidths.pregenerated.c000066400000000000000000002617611455701731300231150ustar00rootroot00000000000000/* ANSI-C code produced by gperf version 3.1 */ /* Command-line: gperf poppler/TimesRomanWidths.gperf */ /* Computed positions: -k'1-2,5,$' */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) && ('-' == 45) && ('.' == 46) && ('/' == 47) \ && ('0' == 48) && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) && ('=' == 61) && ('>' == 62) \ && ('?' == 63) && ('A' == 65) && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) && ('N' == 78) \ && ('O' == 79) && ('P' == 80) && ('Q' == 81) && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) && ('k' == 107) && ('l' == 108) \ && ('m' == 109) && ('n' == 110) && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) /* The character set is not based on ISO-646. */ # error "gperf generated tables don't work with this execution character set. Please report a bug to ." #endif #line 1 "poppler/TimesRomanWidths.gperf" #include #include "BuiltinFontWidth.h" #define TOTAL_KEYWORDS 315 #define MIN_WORD_LENGTH 1 #define MAX_WORD_LENGTH 14 #define MIN_HASH_VALUE 1 #define MAX_HASH_VALUE 1041 /* maximum key range = 1041, duplicates = 0 */ #ifdef __GNUC__ __inline #else # ifdef __cplusplus inline # endif #endif static unsigned int hash(register const char *str, register size_t len) { static const unsigned short asso_values[] = { 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 270, 415, 28, 8, 150, 390, 290, 375, 370, 335, 5, 455, 330, 405, 355, 325, 310, 3, 320, 160, 240, 225, 145, 70, 410, 460, 1042, 1042, 1042, 1042, 1042, 1042, 20, 345, 30, 115, 0, 395, 140, 165, 135, 35, 380, 170, 130, 15, 45, 215, 260, 100, 65, 10, 155, 400, 300, 305, 280, 315, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042 }; register unsigned int hval = len; switch (hval) { default: hval += asso_values[(unsigned char)str[4]]; /*FALLTHROUGH*/ case 4: case 3: case 2: hval += asso_values[(unsigned char)str[1]]; /*FALLTHROUGH*/ case 1: hval += asso_values[(unsigned char)str[0]]; break; } return hval + asso_values[(unsigned char)str[len - 1]]; } const struct BuiltinFontWidth *TimesRomanWidthsLookup(register const char *str, register size_t len) { static const struct BuiltinFontWidth wordlist[] = { { "", 0 }, #line 90 "poppler/TimesRomanWidths.gperf" { "e", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 70 "poppler/TimesRomanWidths.gperf" { "R", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 57 "poppler/TimesRomanWidths.gperf" { "K", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 49 "poppler/TimesRomanWidths.gperf" { "D", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 115 "poppler/TimesRomanWidths.gperf" { "t", 278 }, #line 191 "poppler/TimesRomanWidths.gperf" { "ae", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 102 "poppler/TimesRomanWidths.gperf" { "n", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 207 "poppler/TimesRomanWidths.gperf" { "eacute", 444 }, { "", 0 }, { "", 0 }, #line 216 "poppler/TimesRomanWidths.gperf" { "Racute", 667 }, { "", 0 }, #line 85 "poppler/TimesRomanWidths.gperf" { "a", 444 }, #line 206 "poppler/TimesRomanWidths.gperf" { "at", 921 }, { "", 0 }, #line 308 "poppler/TimesRomanWidths.gperf" { "cent", 500 }, { "", 0 }, { "", 0 }, #line 161 "poppler/TimesRomanWidths.gperf" { "oe", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 145 "poppler/TimesRomanWidths.gperf" { "nacute", 500 }, { "", 0 }, #line 254 "poppler/TimesRomanWidths.gperf" { "Delta", 612 }, { "", 0 }, #line 273 "poppler/TimesRomanWidths.gperf" { "acute", 333 }, #line 112 "poppler/TimesRomanWidths.gperf" { "aacute", 444 }, #line 47 "poppler/TimesRomanWidths.gperf" { "C", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 88 "poppler/TimesRomanWidths.gperf" { "c", 444 }, { "", 0 }, #line 173 "poppler/TimesRomanWidths.gperf" { "one", 500 }, #line 285 "poppler/TimesRomanWidths.gperf" { "Cacute", 667 }, { "", 0 }, #line 300 "poppler/TimesRomanWidths.gperf" { "cacute", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 98 "poppler/TimesRomanWidths.gperf" { "j", 278 }, { "", 0 }, { "", 0 }, #line 210 "poppler/TimesRomanWidths.gperf" { "Dcroat", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 249 "poppler/TimesRomanWidths.gperf" { "oacute", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 72 "poppler/TimesRomanWidths.gperf" { "caron", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 104 "poppler/TimesRomanWidths.gperf" { "o", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 317 "poppler/TimesRomanWidths.gperf" { "ecaron", 444 }, { "", 0 }, { "", 0 }, #line 321 "poppler/TimesRomanWidths.gperf" { "Rcaron", 667 }, #line 290 "poppler/TimesRomanWidths.gperf" { "seven", 500 }, #line 306 "poppler/TimesRomanWidths.gperf" { "sacute", 389 }, { "", 0 }, { "", 0 }, #line 224 "poppler/TimesRomanWidths.gperf" { "Dcaron", 722 }, { "", 0 }, #line 252 "poppler/TimesRomanWidths.gperf" { "tcaron", 326 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 26 "poppler/TimesRomanWidths.gperf" { "colon", 278 }, #line 278 "poppler/TimesRomanWidths.gperf" { "ncaron", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 125 "poppler/TimesRomanWidths.gperf" { "commaaccent", 250 }, { "", 0 }, { "", 0 }, #line 135 "poppler/TimesRomanWidths.gperf" { "semicolon", 278 }, #line 19 "poppler/TimesRomanWidths.gperf" { "comma", 250 }, #line 235 "poppler/TimesRomanWidths.gperf" { "degree", 400 }, { "", 0 }, { "", 0 }, #line 118 "poppler/TimesRomanWidths.gperf" { "Ccaron", 667 }, { "", 0 }, #line 140 "poppler/TimesRomanWidths.gperf" { "ccaron", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 113 "poppler/TimesRomanWidths.gperf" { "s", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 231 "poppler/TimesRomanWidths.gperf" { "racute", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 79 "poppler/TimesRomanWidths.gperf" { "X", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 30 "poppler/TimesRomanWidths.gperf" { "ntilde", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 205 "poppler/TimesRomanWidths.gperf" { "tilde", 333 }, #line 324 "poppler/TimesRomanWidths.gperf" { "atilde", 444 }, { "", 0 }, { "", 0 }, #line 196 "poppler/TimesRomanWidths.gperf" { "nine", 500 }, #line 24 "poppler/TimesRomanWidths.gperf" { "edotaccent", 444 }, #line 105 "poppler/TimesRomanWidths.gperf" { "ordfeminine", 276 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 158 "poppler/TimesRomanWidths.gperf" { "eight", 500 }, #line 150 "poppler/TimesRomanWidths.gperf" { "scaron", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 276 "poppler/TimesRomanWidths.gperf" { "iacute", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 170 "poppler/TimesRomanWidths.gperf" { "otilde", 500 }, #line 292 "poppler/TimesRomanWidths.gperf" { "ordmasculine", 310 }, #line 213 "poppler/TimesRomanWidths.gperf" { "eth", 500 }, { "", 0 }, #line 42 "poppler/TimesRomanWidths.gperf" { "three", 500 }, #line 225 "poppler/TimesRomanWidths.gperf" { "dcroat", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 281 "poppler/TimesRomanWidths.gperf" { "Rcommaaccent", 667 }, #line 185 "poppler/TimesRomanWidths.gperf" { "Eacute", 611 }, #line 322 "poppler/TimesRomanWidths.gperf" { "Kcommaaccent", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 218 "poppler/TimesRomanWidths.gperf" { "uacute", 500 }, #line 103 "poppler/TimesRomanWidths.gperf" { "tcommaaccent", 278 }, { "", 0 }, #line 166 "poppler/TimesRomanWidths.gperf" { "copyright", 760 }, #line 43 "poppler/TimesRomanWidths.gperf" { "numbersign", 500 }, #line 15 "poppler/TimesRomanWidths.gperf" { "rcaron", 333 }, #line 32 "poppler/TimesRomanWidths.gperf" { "ncommaaccent", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 110 "poppler/TimesRomanWidths.gperf" { "r", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 264 "poppler/TimesRomanWidths.gperf" { "lacute", 278 }, { "", 0 }, { "", 0 }, #line 23 "poppler/TimesRomanWidths.gperf" { "dotaccent", 333 }, #line 234 "poppler/TimesRomanWidths.gperf" { "thorn", 500 }, #line 242 "poppler/TimesRomanWidths.gperf" { "dcaron", 588 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 146 "poppler/TimesRomanWidths.gperf" { "macron", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 203 "poppler/TimesRomanWidths.gperf" { "Ccedilla", 667 }, #line 274 "poppler/TimesRomanWidths.gperf" { "section", 500 }, #line 223 "poppler/TimesRomanWidths.gperf" { "ccedilla", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 20 "poppler/TimesRomanWidths.gperf" { "cedilla", 333 }, { "", 0 }, { "", 0 }, #line 25 "poppler/TimesRomanWidths.gperf" { "asciitilde", 541 }, #line 89 "poppler/TimesRomanWidths.gperf" { "d", 500 }, #line 239 "poppler/TimesRomanWidths.gperf" { "percent", 833 }, { "", 0 }, { "", 0 }, #line 288 "poppler/TimesRomanWidths.gperf" { "germandbls", 500 }, { "", 0 }, #line 138 "poppler/TimesRomanWidths.gperf" { "lozenge", 471 }, { "", 0 }, #line 316 "poppler/TimesRomanWidths.gperf" { "less", 564 }, { "", 0 }, #line 97 "poppler/TimesRomanWidths.gperf" { "dagger", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 258 "poppler/TimesRomanWidths.gperf" { "grave", 333 }, #line 301 "poppler/TimesRomanWidths.gperf" { "Ecaron", 611 }, #line 222 "poppler/TimesRomanWidths.gperf" { "scommaaccent", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 160 "poppler/TimesRomanWidths.gperf" { "endash", 500 }, #line 174 "poppler/TimesRomanWidths.gperf" { "emacron", 444 }, #line 201 "poppler/TimesRomanWidths.gperf" { "threequarters", 750 }, { "", 0 }, { "", 0 }, #line 232 "poppler/TimesRomanWidths.gperf" { "Tcaron", 611 }, { "", 0 }, #line 228 "poppler/TimesRomanWidths.gperf" { "scedilla", 389 }, { "", 0 }, { "", 0 }, #line 101 "poppler/TimesRomanWidths.gperf" { "m", 778 }, { "", 0 }, { "", 0 }, #line 245 "poppler/TimesRomanWidths.gperf" { "summation", 600 }, #line 310 "poppler/TimesRomanWidths.gperf" { "logicalnot", 564 }, #line 44 "poppler/TimesRomanWidths.gperf" { "lcaron", 344 }, { "", 0 }, { "", 0 }, #line 172 "poppler/TimesRomanWidths.gperf" { "parenleft", 333 }, #line 139 "poppler/TimesRomanWidths.gperf" { "parenright", 333 }, #line 95 "poppler/TimesRomanWidths.gperf" { "i", 278 }, #line 305 "poppler/TimesRomanWidths.gperf" { "amacron", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 194 "poppler/TimesRomanWidths.gperf" { "Uacute", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 208 "poppler/TimesRomanWidths.gperf" { "underscore", 500 }, #line 92 "poppler/TimesRomanWidths.gperf" { "g", 500 }, #line 297 "poppler/TimesRomanWidths.gperf" { "rcommaaccent", 333 }, { "", 0 }, { "", 0 }, #line 37 "poppler/TimesRomanWidths.gperf" { "space", 250 }, #line 28 "poppler/TimesRomanWidths.gperf" { "dollar", 500 }, { "", 0 }, #line 272 "poppler/TimesRomanWidths.gperf" { "threesuperior", 300 }, #line 188 "poppler/TimesRomanWidths.gperf" { "edieresis", 444 }, #line 236 "poppler/TimesRomanWidths.gperf" { "registered", 760 }, #line 78 "poppler/TimesRomanWidths.gperf" { "W", 944 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 64 "poppler/TimesRomanWidths.gperf" { "omacron", 500 }, #line 36 "poppler/TimesRomanWidths.gperf" { "yen", 500 }, { "", 0 }, { "", 0 }, #line 50 "poppler/TimesRomanWidths.gperf" { "E", 611 }, { "", 0 }, #line 293 "poppler/TimesRomanWidths.gperf" { "dotlessi", 278 }, { "", 0 }, #line 327 "poppler/TimesRomanWidths.gperf" { "Edotaccent", 611 }, #line 71 "poppler/TimesRomanWidths.gperf" { "Aacute", 722 }, { "", 0 }, { "", 0 }, #line 186 "poppler/TimesRomanWidths.gperf" { "adieresis", 444 }, { "", 0 }, #line 117 "poppler/TimesRomanWidths.gperf" { "u", 500 }, { "", 0 }, { "", 0 }, #line 144 "poppler/TimesRomanWidths.gperf" { "daggerdbl", 500 }, { "", 0 }, #line 280 "poppler/TimesRomanWidths.gperf" { "yacute", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 74 "poppler/TimesRomanWidths.gperf" { "T", 611 }, #line 130 "poppler/TimesRomanWidths.gperf" { "gcommaaccent", 500 }, #line 275 "poppler/TimesRomanWidths.gperf" { "dieresis", 333 }, { "", 0 }, #line 51 "poppler/TimesRomanWidths.gperf" { "onequarter", 750 }, #line 328 "poppler/TimesRomanWidths.gperf" { "onesuperior", 300 }, #line 237 "poppler/TimesRomanWidths.gperf" { "radical", 453 }, #line 190 "poppler/TimesRomanWidths.gperf" { "Eth", 722 }, { "", 0 }, { "", 0 }, #line 94 "poppler/TimesRomanWidths.gperf" { "h", 500 }, { "", 0 }, { "", 0 }, #line 193 "poppler/TimesRomanWidths.gperf" { "odieresis", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 100 "poppler/TimesRomanWidths.gperf" { "l", 278 }, #line 65 "poppler/TimesRomanWidths.gperf" { "Tcommaaccent", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 136 "poppler/TimesRomanWidths.gperf" { "oslash", 500 }, { "", 0 }, { "", 0 }, #line 137 "poppler/TimesRomanWidths.gperf" { "lessequal", 549 }, #line 159 "poppler/TimesRomanWidths.gperf" { "exclamdown", 333 }, #line 35 "poppler/TimesRomanWidths.gperf" { "zacute", 444 }, #line 269 "poppler/TimesRomanWidths.gperf" { "lcommaaccent", 278 }, { "", 0 }, #line 209 "poppler/TimesRomanWidths.gperf" { "Euro", 500 }, { "", 0 }, #line 291 "poppler/TimesRomanWidths.gperf" { "Sacute", 556 }, #line 323 "poppler/TimesRomanWidths.gperf" { "greater", 564 }, #line 244 "poppler/TimesRomanWidths.gperf" { "two", 500 }, { "", 0 }, #line 220 "poppler/TimesRomanWidths.gperf" { "Thorn", 556 }, #line 256 "poppler/TimesRomanWidths.gperf" { "asciicircum", 469 }, #line 126 "poppler/TimesRomanWidths.gperf" { "hungarumlaut", 333 }, { "", 0 }, #line 212 "poppler/TimesRomanWidths.gperf" { "zero", 500 }, { "", 0 }, #line 40 "poppler/TimesRomanWidths.gperf" { "emdash", 1000 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 116 "poppler/TimesRomanWidths.gperf" { "divide", 564 }, { "", 0 }, #line 271 "poppler/TimesRomanWidths.gperf" { "ohungarumlaut", 500 }, #line 262 "poppler/TimesRomanWidths.gperf" { "ampersand", 778 }, { "", 0 }, #line 164 "poppler/TimesRomanWidths.gperf" { "ecircumflex", 444 }, { "", 0 }, { "", 0 }, #line 106 "poppler/TimesRomanWidths.gperf" { "ring", 333 }, { "", 0 }, #line 320 "poppler/TimesRomanWidths.gperf" { "period", 250 }, { "", 0 }, #line 318 "poppler/TimesRomanWidths.gperf" { "guilsinglleft", 333 }, #line 155 "poppler/TimesRomanWidths.gperf" { "guilsinglright", 333 }, { "", 0 }, { "", 0 }, #line 307 "poppler/TimesRomanWidths.gperf" { "imacron", 278 }, { "", 0 }, #line 61 "poppler/TimesRomanWidths.gperf" { "periodcentered", 250 }, { "", 0 }, #line 227 "poppler/TimesRomanWidths.gperf" { "Oacute", 722 }, { "", 0 }, #line 294 "poppler/TimesRomanWidths.gperf" { "sterling", 500 }, { "", 0 }, { "", 0 }, #line 299 "poppler/TimesRomanWidths.gperf" { "acircumflex", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 33 "poppler/TimesRomanWidths.gperf" { "minus", 564 }, #line 312 "poppler/TimesRomanWidths.gperf" { "Atilde", 722 }, #line 148 "poppler/TimesRomanWidths.gperf" { "Emacron", 611 }, { "", 0 }, { "", 0 }, #line 257 "poppler/TimesRomanWidths.gperf" { "aring", 444 }, #line 261 "poppler/TimesRomanWidths.gperf" { "Iacute", 333 }, #line 183 "poppler/TimesRomanWidths.gperf" { "umacron", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 221 "poppler/TimesRomanWidths.gperf" { "zcaron", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 133 "poppler/TimesRomanWidths.gperf" { "Scaron", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 248 "poppler/TimesRomanWidths.gperf" { "ocircumflex", 500 }, { "", 0 }, { "", 0 }, #line 189 "poppler/TimesRomanWidths.gperf" { "idieresis", 278 }, { "", 0 }, #line 157 "poppler/TimesRomanWidths.gperf" { "quotesingle", 180 }, #line 277 "poppler/TimesRomanWidths.gperf" { "quotedblbase", 444 }, { "", 0 }, #line 268 "poppler/TimesRomanWidths.gperf" { "quotesinglbase", 333 }, { "", 0 }, #line 107 "poppler/TimesRomanWidths.gperf" { "p", 500 }, #line 132 "poppler/TimesRomanWidths.gperf" { "greaterequal", 549 }, { "", 0 }, #line 326 "poppler/TimesRomanWidths.gperf" { "quoteleft", 333 }, #line 179 "poppler/TimesRomanWidths.gperf" { "quoteright", 333 }, { "", 0 }, #line 154 "poppler/TimesRomanWidths.gperf" { "quotedblleft", 444 }, #line 304 "poppler/TimesRomanWidths.gperf" { "quotedblright", 444 }, #line 169 "poppler/TimesRomanWidths.gperf" { "Edieresis", 611 }, { "", 0 }, #line 128 "poppler/TimesRomanWidths.gperf" { "Nacute", 722 }, #line 131 "poppler/TimesRomanWidths.gperf" { "mu", 500 }, { "", 0 }, #line 198 "poppler/TimesRomanWidths.gperf" { "udieresis", 500 }, { "", 0 }, #line 270 "poppler/TimesRomanWidths.gperf" { "Yacute", 722 }, #line 253 "poppler/TimesRomanWidths.gperf" { "eogonek", 444 }, #line 80 "poppler/TimesRomanWidths.gperf" { "question", 444 }, { "", 0 }, #line 313 "poppler/TimesRomanWidths.gperf" { "breve", 333 }, #line 77 "poppler/TimesRomanWidths.gperf" { "V", 722 }, #line 39 "poppler/TimesRomanWidths.gperf" { "questiondown", 444 }, { "", 0 }, #line 266 "poppler/TimesRomanWidths.gperf" { "plus", 564 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 149 "poppler/TimesRomanWidths.gperf" { "ellipsis", 1000 }, { "", 0 }, { "", 0 }, #line 319 "poppler/TimesRomanWidths.gperf" { "exclam", 333 }, { "", 0 }, { "", 0 }, #line 219 "poppler/TimesRomanWidths.gperf" { "braceleft", 480 }, #line 303 "poppler/TimesRomanWidths.gperf" { "braceright", 480 }, #line 156 "poppler/TimesRomanWidths.gperf" { "hyphen", 333 }, #line 48 "poppler/TimesRomanWidths.gperf" { "aogonek", 444 }, #line 314 "poppler/TimesRomanWidths.gperf" { "bar", 200 }, { "", 0 }, #line 311 "poppler/TimesRomanWidths.gperf" { "zdotaccent", 444 }, #line 153 "poppler/TimesRomanWidths.gperf" { "lslash", 278 }, #line 86 "poppler/TimesRomanWidths.gperf" { "Gcommaaccent", 722 }, #line 309 "poppler/TimesRomanWidths.gperf" { "currency", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 75 "poppler/TimesRomanWidths.gperf" { "U", 722 }, #line 27 "poppler/TimesRomanWidths.gperf" { "onehalf", 750 }, #line 109 "poppler/TimesRomanWidths.gperf" { "uhungarumlaut", 500 }, { "", 0 }, { "", 0 }, #line 147 "poppler/TimesRomanWidths.gperf" { "Otilde", 722 }, { "", 0 }, #line 287 "poppler/TimesRomanWidths.gperf" { "guillemotleft", 500 }, #line 202 "poppler/TimesRomanWidths.gperf" { "guillemotright", 500 }, { "", 0 }, #line 247 "poppler/TimesRomanWidths.gperf" { "Lacute", 611 }, #line 163 "poppler/TimesRomanWidths.gperf" { "Umacron", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 18 "poppler/TimesRomanWidths.gperf" { "Zacute", 611 }, { "", 0 }, #line 295 "poppler/TimesRomanWidths.gperf" { "notequal", 549 }, #line 143 "poppler/TimesRomanWidths.gperf" { "trademark", 980 }, { "", 0 }, #line 265 "poppler/TimesRomanWidths.gperf" { "Ncaron", 722 }, #line 200 "poppler/TimesRomanWidths.gperf" { "Scommaaccent", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 181 "poppler/TimesRomanWidths.gperf" { "perthousand", 1000 }, { "", 0 }, #line 240 "poppler/TimesRomanWidths.gperf" { "six", 500 }, { "", 0 }, { "", 0 }, #line 302 "poppler/TimesRomanWidths.gperf" { "icircumflex", 278 }, { "", 0 }, #line 214 "poppler/TimesRomanWidths.gperf" { "Scedilla", 556 }, { "", 0 }, { "", 0 }, #line 93 "poppler/TimesRomanWidths.gperf" { "bullet", 350 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 108 "poppler/TimesRomanWidths.gperf" { "q", 500 }, #line 289 "poppler/TimesRomanWidths.gperf" { "Amacron", 722 }, { "", 0 }, { "", 0 }, #line 127 "poppler/TimesRomanWidths.gperf" { "Idotaccent", 333 }, #line 141 "poppler/TimesRomanWidths.gperf" { "Ecircumflex", 611 }, { "", 0 }, #line 315 "poppler/TimesRomanWidths.gperf" { "fraction", 167 }, #line 180 "poppler/TimesRomanWidths.gperf" { "Udieresis", 722 }, { "", 0 }, #line 176 "poppler/TimesRomanWidths.gperf" { "ucircumflex", 500 }, { "", 0 }, { "", 0 }, #line 197 "poppler/TimesRomanWidths.gperf" { "five", 500 }, { "", 0 }, #line 14 "poppler/TimesRomanWidths.gperf" { "Ntilde", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 267 "poppler/TimesRomanWidths.gperf" { "uring", 500 }, #line 45 "poppler/TimesRomanWidths.gperf" { "A", 722 }, { "", 0 }, { "", 0 }, #line 84 "poppler/TimesRomanWidths.gperf" { "four", 500 }, { "", 0 }, #line 187 "poppler/TimesRomanWidths.gperf" { "egrave", 444 }, { "", 0 }, { "", 0 }, #line 241 "poppler/TimesRomanWidths.gperf" { "paragraph", 453 }, { "", 0 }, #line 29 "poppler/TimesRomanWidths.gperf" { "Lcaron", 611 }, { "", 0 }, { "", 0 }, #line 325 "poppler/TimesRomanWidths.gperf" { "brokenbar", 200 }, { "", 0 }, #line 199 "poppler/TimesRomanWidths.gperf" { "Zcaron", 611 }, { "", 0 }, { "", 0 }, #line 165 "poppler/TimesRomanWidths.gperf" { "Adieresis", 722 }, { "", 0 }, #line 122 "poppler/TimesRomanWidths.gperf" { "y", 500 }, #line 16 "poppler/TimesRomanWidths.gperf" { "kcommaaccent", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 76 "poppler/TimesRomanWidths.gperf" { "agrave", 444 }, { "", 0 }, #line 69 "poppler/TimesRomanWidths.gperf" { "Uhungarumlaut", 722 }, #line 204 "poppler/TimesRomanWidths.gperf" { "ydieresis", 500 }, #line 168 "poppler/TimesRomanWidths.gperf" { "slash", 278 }, #line 229 "poppler/TimesRomanWidths.gperf" { "ogonek", 333 }, #line 151 "poppler/TimesRomanWidths.gperf" { "AE", 889 }, #line 192 "poppler/TimesRomanWidths.gperf" { "asterisk", 500 }, { "", 0 }, { "", 0 }, #line 111 "poppler/TimesRomanWidths.gperf" { "twosuperior", 300 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 53 "poppler/TimesRomanWidths.gperf" { "G", 722 }, #line 58 "poppler/TimesRomanWidths.gperf" { "iogonek", 278 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 17 "poppler/TimesRomanWidths.gperf" { "Ncommaaccent", 722 }, { "", 0 }, #line 21 "poppler/TimesRomanWidths.gperf" { "plusminus", 564 }, { "", 0 }, #line 230 "poppler/TimesRomanWidths.gperf" { "ograve", 500 }, { "", 0 }, #line 129 "poppler/TimesRomanWidths.gperf" { "quotedbl", 408 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 233 "poppler/TimesRomanWidths.gperf" { "Eogonek", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 120 "poppler/TimesRomanWidths.gperf" { "w", 722 }, #line 259 "poppler/TimesRomanWidths.gperf" { "uogonek", 500 }, { "", 0 }, #line 59 "poppler/TimesRomanWidths.gperf" { "backslash", 278 }, #line 81 "poppler/TimesRomanWidths.gperf" { "equal", 564 }, { "", 0 }, #line 38 "poppler/TimesRomanWidths.gperf" { "Omacron", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 121 "poppler/TimesRomanWidths.gperf" { "x", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 298 "poppler/TimesRomanWidths.gperf" { "Zdotaccent", 611 }, #line 152 "poppler/TimesRomanWidths.gperf" { "Ucircumflex", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 68 "poppler/TimesRomanWidths.gperf" { "Q", 722 }, #line 296 "poppler/TimesRomanWidths.gperf" { "Imacron", 333 }, { "", 0 }, { "", 0 }, #line 250 "poppler/TimesRomanWidths.gperf" { "Uring", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 123 "poppler/TimesRomanWidths.gperf" { "z", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 22 "poppler/TimesRomanWidths.gperf" { "circumflex", 333 }, { "", 0 }, #line 251 "poppler/TimesRomanWidths.gperf" { "Lcommaaccent", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 73 "poppler/TimesRomanWidths.gperf" { "S", 556 }, { "", 0 }, { "", 0 }, #line 175 "poppler/TimesRomanWidths.gperf" { "Odieresis", 722 }, { "", 0 }, #line 284 "poppler/TimesRomanWidths.gperf" { "Acircumflex", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 67 "poppler/TimesRomanWidths.gperf" { "P", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 238 "poppler/TimesRomanWidths.gperf" { "Aring", 722 }, #line 96 "poppler/TimesRomanWidths.gperf" { "Oslash", 722 }, #line 114 "poppler/TimesRomanWidths.gperf" { "OE", 889 }, { "", 0 }, #line 171 "poppler/TimesRomanWidths.gperf" { "Idieresis", 333 }, { "", 0 }, #line 62 "poppler/TimesRomanWidths.gperf" { "M", 889 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 282 "poppler/TimesRomanWidths.gperf" { "fi", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 56 "poppler/TimesRomanWidths.gperf" { "J", 389 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 263 "poppler/TimesRomanWidths.gperf" { "igrave", 278 }, { "", 0 }, #line 255 "poppler/TimesRomanWidths.gperf" { "Ohungarumlaut", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 243 "poppler/TimesRomanWidths.gperf" { "Uogonek", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 87 "poppler/TimesRomanWidths.gperf" { "b", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 167 "poppler/TimesRomanWidths.gperf" { "Egrave", 611 }, { "", 0 }, { "", 0 }, #line 182 "poppler/TimesRomanWidths.gperf" { "Ydieresis", 722 }, { "", 0 }, #line 195 "poppler/TimesRomanWidths.gperf" { "ugrave", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 211 "poppler/TimesRomanWidths.gperf" { "multiply", 564 }, { "", 0 }, { "", 0 }, #line 66 "poppler/TimesRomanWidths.gperf" { "O", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 31 "poppler/TimesRomanWidths.gperf" { "Aogonek", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 279 "poppler/TimesRomanWidths.gperf" { "florin", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 226 "poppler/TimesRomanWidths.gperf" { "Ocircumflex", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 283 "poppler/TimesRomanWidths.gperf" { "fl", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 55 "poppler/TimesRomanWidths.gperf" { "I", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 286 "poppler/TimesRomanWidths.gperf" { "Icircumflex", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 54 "poppler/TimesRomanWidths.gperf" { "H", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 134 "poppler/TimesRomanWidths.gperf" { "Lslash", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 99 "poppler/TimesRomanWidths.gperf" { "k", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 184 "poppler/TimesRomanWidths.gperf" { "abreve", 444 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 217 "poppler/TimesRomanWidths.gperf" { "partialdiff", 476 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 52 "poppler/TimesRomanWidths.gperf" { "F", 556 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 178 "poppler/TimesRomanWidths.gperf" { "Ugrave", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 91 "poppler/TimesRomanWidths.gperf" { "f", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 119 "poppler/TimesRomanWidths.gperf" { "v", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 63 "poppler/TimesRomanWidths.gperf" { "N", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 41 "poppler/TimesRomanWidths.gperf" { "Agrave", 722 }, #line 34 "poppler/TimesRomanWidths.gperf" { "Iogonek", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 82 "poppler/TimesRomanWidths.gperf" { "Y", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 46 "poppler/TimesRomanWidths.gperf" { "B", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 177 "poppler/TimesRomanWidths.gperf" { "bracketleft", 333 }, #line 260 "poppler/TimesRomanWidths.gperf" { "bracketright", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 142 "poppler/TimesRomanWidths.gperf" { "gbreve", 500 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 215 "poppler/TimesRomanWidths.gperf" { "Ograve", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 60 "poppler/TimesRomanWidths.gperf" { "L", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 246 "poppler/TimesRomanWidths.gperf" { "Igrave", 333 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 83 "poppler/TimesRomanWidths.gperf" { "Z", 611 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 162 "poppler/TimesRomanWidths.gperf" { "Abreve", 722 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 124 "poppler/TimesRomanWidths.gperf" { "Gbreve", 722 } }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) { register unsigned int key = hash(str, len); if (key <= MAX_HASH_VALUE) { register const char *s = wordlist[key].name; if (*str == *s && !strcmp(str + 1, s + 1)) return &wordlist[key]; } } return 0; } #line 330 "poppler/TimesRomanWidths.gperf" poppler-24.02.0/poppler/UTF.cc000066400000000000000000000460321455701731300160010ustar00rootroot00000000000000//======================================================================== // // UTF.cc // // Copyright 2001-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) 2008 Koji Otani // Copyright (C) 2012, 2017, 2021, 2023 Adrian Johnson // Copyright (C) 2012 Hib Eris // Copyright (C) 2016, 2018-2022 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, 2024 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 // //======================================================================== #include "goo/gmem.h" #include "PDFDocEncoding.h" #include "GlobalParams.h" #include "UnicodeMap.h" #include "UTF.h" #include "UnicodeMapFuncs.h" #include #include bool UnicodeIsValid(Unicode ucs4) { return (ucs4 < 0x110000) && ((ucs4 & 0xfffff800) != 0xd800) && (ucs4 < 0xfdd0 || ucs4 > 0xfdef) && ((ucs4 & 0xfffe) != 0xfffe); } std::vector UTF16toUCS4(const Unicode *utf16, int utf16Len) { // count characters int len = 0; for (int i = 0; i < utf16Len; i++) { if (utf16[i] >= 0xd800 && utf16[i] < 0xdc00 && i + 1 < utf16Len && utf16[i + 1] >= 0xdc00 && utf16[i + 1] < 0xe000) { i++; /* surrogate pair */ } len++; } std::vector u; u.reserve(len); // convert string for (int i = 0; i < utf16Len; i++) { if (utf16[i] >= 0xd800 && utf16[i] < 0xdc00) { /* surrogate pair */ if (i + 1 < utf16Len && utf16[i + 1] >= 0xdc00 && utf16[i + 1] < 0xe000) { /* next code is a low surrogate */ 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.push_back(0xfffd); } } else if (utf16[i] >= 0xdc00 && utf16[i] < 0xe000) { /* invalid low surrogate replace it with REPLACEMENT CHARACTER (U+FFFD) */ u.push_back(0xfffd); } else { u.push_back(utf16[i]); } if (!UnicodeIsValid(u.back())) { u.back() = 0xfffd; } } return u; } std::vector TextStringToUCS4(const std::string &textStr) { bool isUnicode, isUnicodeLE; int len = textStr.size(); const std::string &s = textStr; if (len == 0) { return {}; } if (GooString::hasUnicodeMarker(textStr)) { isUnicode = true; isUnicodeLE = false; } else if (GooString::hasUnicodeMarkerLE(textStr)) { isUnicode = false; isUnicodeLE = true; } else { isUnicode = false; isUnicodeLE = false; } if (isUnicode || isUnicodeLE) { len = len / 2 - 1; if (len > 0) { std::vector utf16; utf16.reserve(len); for (int i = 0; i < len; i++) { if (isUnicode) { utf16.push_back((s[2 + i * 2] & 0xff) << 8 | (s[3 + i * 2] & 0xff)); } else { // UnicodeLE utf16.push_back((s[3 + i * 2] & 0xff) << 8 | (s[2 + i * 2] & 0xff)); } } return UTF16toUCS4(utf16.data(), utf16.size()); } else { return {}; } } else { std::vector u; u.reserve(len); for (int i = 0; i < len; i++) { u.push_back(pdfDocEncoding[s[i] & 0xff]); } return u; } } bool UnicodeIsWhitespace(Unicode ucs4) { static Unicode const spaces[] = { 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x0020, 0x0085, 0x00A0, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x2028, 0x2029, 0x202F, 0x205F, 0x3000 }; Unicode const *end = spaces + sizeof(spaces) / sizeof(spaces[0]); Unicode const *i = std::lower_bound(spaces, end, ucs4); return (i != end && *i == ucs4); } // // decodeUtf8() and decodeUtf8Table are: // // Copyright (c) 2008-2009 Bjoern Hoehrmann // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, copy, // modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. // static const uint32_t UTF8_ACCEPT = 0; static const uint32_t UTF8_REJECT = 12; static const uint32_t UCS4_MAX = 0x10FFFF; static const Unicode REPLACEMENT_CHAR = 0xFFFD; // clang-format off static const uint8_t decodeUtf8Table[] = { // The first part of the table maps bytes to character classes // to reduce the size of the transition table and create bitmasks. 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, // e0..ff // The second part is a transition table that maps a combination // of a state of the automaton and a character class to a state. 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12, 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12, 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12, 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,12,12,12,12,12, }; // clang-format on // Decode utf8 state machine for fast UTF-8 decoding. Initialise state // to 0 and call decodeUtf8() for each byte of UTF-8. Return value // (and state) is UTF8_ACCEPT when it has found a valid codepoint // (codepoint returned in codep), UTF8_REJECT when the byte is not // allowed to occur at its position, and some other positive value if // more bytes have to be read. Reset state to 0 to recover from // errors. inline uint32_t decodeUtf8(uint32_t *state, uint32_t *codep, char byte) { uint32_t b = (unsigned char)byte; uint32_t type = decodeUtf8Table[b]; *codep = (*state != UTF8_ACCEPT) ? (b & 0x3fu) | (*codep << 6) : (0xff >> type) & (b); *state = decodeUtf8Table[256 + *state + type]; return *state; } int utf8CountUCS4(const char *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; } 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); if (state == UTF8_ACCEPT) { u[n++] = codepoint; } else if (state == UTF8_REJECT) { u[n++] = 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 } *ucs4_out = u; return len; } // 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) { uint32_t codepoint; uint32_t state = 0; int count = 0; while (*utf8) { decodeUtf8(&state, &codepoint, *utf8); if (state == UTF8_ACCEPT) { if (codepoint < 0x10000) { count++; } else if (codepoint <= UCS4_MAX) { count += 2; } else { count++; // replace with REPLACEMENT_CHAR } } 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; } // 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) { 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 (state == UTF8_ACCEPT) { if (codepoint < 0x10000) { *p++ = (uint16_t)codepoint; nOut++; } else if (codepoint <= UCS4_MAX) { *p++ = (uint16_t)(0xD7C0 + (codepoint >> 10)); *p++ = (uint16_t)(0xDC00 + (codepoint & 0x3FF)); nOut += 2; } else { *p++ = REPLACEMENT_CHAR; nOut++; state = 0; } } else if (state == UTF8_REJECT) { *p++ = REPLACEMENT_CHAR; // invalid byte for this position nOut++; } 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) { if (isUtf8WithBom(utf8)) { utf8 += 3; } int n = utf8CountUtf16CodeUnits(utf8); if (len) { *len = n; } uint16_t *utf16 = (uint16_t *)gmallocn(n + 1, sizeof(uint16_t)); utf8ToUtf16(utf8, utf16, n + 1, INT_MAX); return utf16; } std::string utf8ToUtf16WithBom(const std::string &utf8) { if (utf8.empty()) { return {}; } int tmp_length; // Number of UTF-16 symbols. char *tmp_str = (char *)utf8ToUtf16(utf8.c_str(), &tmp_length); #ifndef WORDS_BIGENDIAN for (int i = 0; i < tmp_length; i++) { std::swap(tmp_str[i * 2], tmp_str[i * 2 + 1]); } #endif std::string result(unicodeByteOrderMark); result.append(tmp_str, tmp_length * 2); gfree(tmp_str); return result; } static const uint32_t UTF16_ACCEPT = 0; static const uint32_t UTF16_REJECT = -1; // Initialise state to 0. Returns UTF16_ACCEPT when a valid code point // has been found, UTF16_REJECT when invalid code unit for this state, // some other valid if another code unit needs to be read. inline uint32_t decodeUtf16(uint32_t *state, uint32_t *codePoint, uint16_t codeUnit) { if (*state == 0) { if (codeUnit >= 0xd800 && codeUnit < 0xdc00) { /* surrogate pair */ *state = codeUnit; return *state; } else if (codeUnit >= 0xdc00 && codeUnit < 0xe000) { /* invalid low surrogate */ return UTF16_REJECT; } else { *codePoint = codeUnit; return UTF16_ACCEPT; } } else { if (codeUnit >= 0xdc00 && codeUnit < 0xe000) { *codePoint = (((*state & 0x3ff) << 10) | (codeUnit & 0x3ff)) + 0x10000; *state = 0; return UTF16_ACCEPT; } else { /* invalid high surrogate */ return UTF16_REJECT; } } } // Count number of UTF-8 bytes required to convert a UTF-16 string to // UTF-8 (excluding terminating NULL). int utf16CountUtf8Bytes(const uint16_t *utf16) { uint32_t codepoint = 0; uint32_t state = 0; int count = 0; while (*utf16) { decodeUtf16(&state, &codepoint, *utf16); if (state == UTF16_ACCEPT) { if (codepoint < 0x80) { count++; } else if (codepoint < 0x800) { count += 2; } else if (codepoint < 0x10000) { count += 3; } else if (codepoint <= UCS4_MAX) { count += 4; } else { count += 3; // replace with REPLACEMENT_CHAR } } else if (state == UTF16_REJECT) { count += 3; // replace with REPLACEMENT_CHAR state = 0; } utf16++; } if (state != UTF8_ACCEPT && state != UTF8_REJECT) { count++; // 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) { uint32_t codepoint = 0; uint32_t state = 0; int nIn = 0; int nOut = 0; char *p = utf8; while (*utf16 && nIn < maxUtf16 && nOut < maxUtf8 - 1) { decodeUtf16(&state, &codepoint, *utf16); if (state == UTF16_ACCEPT || state == UTF16_REJECT) { if (state == UTF16_REJECT || codepoint > UCS4_MAX) { codepoint = REPLACEMENT_CHAR; state = 0; } int bufSize = maxUtf8 - nOut; int count = mapUTF8(codepoint, p, bufSize); p += count; nOut += 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; } 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) { const UnicodeMap *uMap = globalParams->getUnicodeMap("ASCII7"); int *idx = nullptr; if (!len) { *ucs4_out = nullptr; *out_len = 0; return; } if (indices) { if (!in_idx) { indices = nullptr; } else { idx = (int *)gmallocn(len * 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; for (i = k = 0; i < len; ++i) { n = uMap->mapUnicode(in[i], buf, sizeof(buf)); if (!n) { // the Unicode char could not be converted to ascii7 counterpart // so just fill with a non-printable ascii char buf[0] = 31; n = 1; } str.append(buf, n); if (indices) { for (; n > 0; n--) { idx[k++] = in_idx[i]; } } } 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]; *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; char *utf8; len = textStr.size(); s = textStr.c_str(); if (GooString::hasUnicodeMarker(textStr)) { uint16_t *utf16; len = len / 2 - 1; utf16 = new uint16_t[len]; for (i = 0; i < len; i++) { utf16[i] = (s[2 + i * 2] & 0xff) << 8 | (s[3 + i * 2] & 0xff); } utf8 = utf16ToUtf8(utf16, &len); delete[] utf16; } else { utf8 = (char *)gmalloc(len + 1); for (i = 0; i < len; i++) { utf8[i] = pdfDocEncoding[s[i] & 0xff]; } utf8[i] = 0; } std::string utf8_string(utf8); gfree(utf8); return utf8_string; } poppler-24.02.0/poppler/UTF.h000066400000000000000000000134521455701731300156430ustar00rootroot00000000000000//======================================================================== // // UTF.h // // This file is licensed under the GPLv2 or later // // Copyright (C) 2012, 2017, 2021, 2023 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) 2021 Georgiy Sgibnev . Work sponsored by lab50.net. // Copyright (C) 2023, 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela // Copyright (C) 2023 Even Rouault // Copyright (C) 2023 Oliver Sander // //======================================================================== #ifndef UTF_H #define UTF_H #include #include #include #include #include "CharTypes.h" #include "poppler_private_export.h" // Magic bytes that mark the byte order in a UTF-16 unicode string constexpr std::string_view unicodeByteOrderMark = "\xFE\xFF"; // Convert a UTF-16 string to a UCS-4 // utf16 - utf16 bytes // utf16_len - number of UTF-16 characters // returns number of UCS-4 characters std::vector UTF16toUCS4(const Unicode *utf16, int utf16Len); // Convert a PDF Text String to UCS-4 // s - PDF text string // returns UCS-4 characters // Convert a PDF text string to UCS-4 std::vector POPPLER_PRIVATE_EXPORT TextStringToUCS4(const std::string &textStr); // check if UCS-4 character is valid bool UnicodeIsValid(Unicode ucs4); // 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); // 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); // 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 maxUtf8); // Allocate utf16 string and convert utf8 into it. uint16_t POPPLER_PRIVATE_EXPORT *utf8ToUtf16(const char *utf8, int *len = nullptr); 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::string POPPLER_PRIVATE_EXPORT utf8ToUtf16WithBom(const std::string &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); // Convert a UCS-4 string to pure ASCII (7bit) // in - UCS-4 string bytes // len - number of UCS-4 characters // ucs4_out - if not NULL, allocates and returns UCS-4 string. Free with gfree. // out_len - number of UCS-4 characters in ucs4_out. // in_idx - if not NULL, the int array returned by the out fourth parameter of // unicodeNormalizeNFKC() function. Optional, needed for @indices out parameter. // indices - if not NULL, @indices is assigned the location of a newly-allocated array // 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); // 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 poppler-24.02.0/poppler/UnicodeCClassTables.h000066400000000000000000003211551455701731300210210ustar00rootroot00000000000000// Copied from gunidecomp.h in GLib: // s/[G]_UNICODE_MAX_TABLE_INDEX/UNICODE_MAX_TABLE_INDEX/g // s/[g]uchar/Unicode/g // s/[g]int16/short/g // Can be regenerated from gen-unicode-tables.pl, also in GLib, with relevant // Unicode data tables from ftp.unicode.org. 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, 220, 220, 220, 220, 220, 220, 220, 220, 220, 1, 1, 1, 1, 1, 220, 220, 220, 220, 230, 230, 230, 230, 230, 230, 230, 230, 240, 230, 220, 220, 220, 230, 230, 230, 220, 220, 0, 230, 230, 230, 220, 220, 220, 220, 230, 0, 0, 0, 0, 0, 234, 234, 233, 234, 234, 233, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 4, index 1 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 5, index 2 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 230, 230, 230, 230, 220, 230, 230, 230, 222, 220, 230, 230, 230, 230, 230, 230, 0, 220, 220, 220, 220, 220, 230, 230, 220, 230, 230, 222, 228, 230, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 20, 21, 22, 0, 23, 0, 24, 25, 0, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 6, index 3 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, 34, 230, 230, 220, 220, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 230, 230, 230, 230, 230, 0, 0, 230, 230, 230, 230, 220, 230, 0, 0, 230, 230, 0, 220, 230, 230, 220, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 7, index 4 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 220, 230, 230, 220, 230, 230, 220, 220, 220, 230, 220, 220, 230, 220, 230, 230, 230, 220, 230, 220, 230, 220, 230, 220, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 9, index 5 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 230, 220, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 10, index 6 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 11, index 7 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 12, index 8 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 84, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 13, index 9 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 14, index 10 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 103, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 107, 107, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 118, 118, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 122, 122, 122, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 15, index 11 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 220, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 0, 220, 0, 216, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 130, 0, 132, 0, 0, 0, 0, 0, 130, 130, 130, 130, 0, 0, 130, 0, 230, 230, 9, 0, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 16, index 12 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 23, index 13 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 24, index 14 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 25, index 15 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 222, 230, 220, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 32, index 16 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 1, 1, 230, 230, 230, 230, 1, 1, 1, 230, 230, 0, 0, 0, 0, 230, 0, 0, 0, 1, 1, 230, 220, 230, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 48, index 17 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 218, 228, 232, 222, 224, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 251, index 18 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 254, index 19 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 465, index 20 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 216, 1, 1, 1, 0, 0, 0, 226, 216, 216, 216, 216, 216, 0, 0, 0, 0, 0, 0, 0, 0, 220, 220, 220, 220, 220, 220, 220, 220, 0, 0, 230, 230, 230, 230, 230, 220, 220, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; static const short combining_class_table_part1[763] = { 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 /* page 3 */, 1 /* page 4 */, 2 /* page 5 */, 3 /* page 6 */, 4 /* page 7 */, 0 + UNICODE_MAX_TABLE_INDEX, 5 /* page 9 */, 6 /* page 10 */, 7 /* page 11 */, 8 /* page 12 */, 9 /* page 13 */, 10 /* page 14 */, 11 /* page 15 */, 12 /* page 16 */, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 13 /* page 23 */, 14 /* page 24 */, 15 /* page 25 */, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 16 /* page 32 */, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 17 /* page 48 */, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 18 /* page 251 */, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 19 /* page 254 */, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 20 /* page 465 */, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX }; static const short combining_class_table_part2[768] = { 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX }; poppler-24.02.0/poppler/UnicodeCompTables.h000066400000000000000000001175531455701731300205540ustar00rootroot00000000000000// Copied from gunicomp.h in GLib: // s/[G]_UNICODE_MAX_TABLE_INDEX/UNICODE_MAX_TABLE_INDEX/g // s/[g]uint16/unsigned short/g // s/[g]int16/short/g // Can be regenerated from gen-unicode-tables.pl, also in GLib, with relevant // Unicode data tables from ftp.unicode.org. #define COMPOSE_FIRST_START 1 #define COMPOSE_FIRST_SINGLE_START 147 #define COMPOSE_SECOND_START 357 #define COMPOSE_SECOND_SINGLE_START 388 #define COMPOSE_TABLE_LAST 48 static const unsigned short compose_data[][256] = { { /* page 0, index 0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 147, 148, 149, 0, 0, 1, 2, 3, 4, 5, 150, 6, 7, 8, 151, 9, 10, 11, 12, 13, 14, 0, 15, 16, 17, 18, 19, 20, 21, 22, 23, 0, 0, 0, 0, 0, 0, 24, 25, 26, 27, 28, 152, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 0, 39, 40, 41, 42, 43, 44, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 0, 153, 154, 50, 155, 0, 0, 51, 0, 0, 0, 0, 156, 0, 0, 0, 0, 52, 53, 157, 0, 158, 0, 0, 0, 54, 0, 0, 0, 0, 0, 55, 0, 159, 160, 56, 161, 0, 0, 57, 0, 0, 0, 0, 162, 0, 0, 0, 0, 58, 59, 163, 0, 164, 0, 0, 0, 60, 0, 0, 0 }, { /* page 1, index 1 */ 0, 0, 61, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 165, 166, 0, 0, 0, 0, 167, 168, 0, 0, 0, 0, 0, 0, 169, 170, 171, 172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 70, 0, 0, 0, 0, 0, 0, 174, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 175, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 2, index 2 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 177, 178, 179, 180, 0, 0, 0, 0, 181, 182, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 183, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 3, index 3 */ 357, 358, 359, 360, 361, 0, 362, 363, 364, 365, 366, 367, 368, 0, 0, 369, 0, 370, 0, 371, 372, 0, 0, 0, 0, 0, 0, 373, 0, 0, 0, 0, 0, 0, 0, 374, 375, 376, 377, 378, 379, 0, 0, 0, 0, 380, 381, 0, 382, 383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 384, 0, 0, 385, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 0, 0, 0, 72, 0, 73, 0, 74, 0, 0, 0, 0, 0, 75, 0, 184, 0, 0, 0, 76, 0, 0, 0, 77, 0, 0, 185, 0, 186, 0, 0, 78, 0, 0, 0, 79, 0, 80, 0, 81, 0, 0, 0, 0, 0, 82, 0, 83, 0, 0, 0, 84, 0, 0, 0, 85, 86, 87, 0, 0, 187, 0, 0, 0, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 4, index 4 */ 0, 0, 0, 0, 0, 0, 188, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, 0, 0, 189, 0, 90, 91, 190, 92, 0, 191, 0, 0, 0, 192, 0, 0, 0, 0, 93, 0, 0, 0, 193, 0, 0, 0, 194, 0, 195, 0, 0, 94, 0, 0, 196, 0, 95, 96, 197, 97, 0, 198, 0, 0, 0, 199, 0, 0, 0, 0, 98, 0, 0, 0, 200, 0, 0, 0, 201, 0, 202, 0, 0, 0, 0, 0, 0, 0, 0, 203, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 204, 205, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 206, 207, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 209, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 6, index 5 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210, 0, 211, 0, 0, 0, 0, 0, 0, 0, 0, 388, 389, 390, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 212, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 213, 0, 0, 214, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 9, index 6 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 215, 0, 0, 0, 0, 0, 0, 0, 216, 0, 0, 217, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 391, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 392, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 11, index 7 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 393, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 394, 395, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 218, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 396, 0, 0, 0, 0, 0, 0, 0, 102, 219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 397, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 12, index 8 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 221, 0, 0, 398, 0, 0, 0, 103, 0, 0, 0, 222, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 399, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 13, index 9 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 401, 0, 0, 0, 0, 0, 0, 0, 104, 223, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 402, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 403, 0, 0, 0, 0, 404, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 0, 224, 0, 0, 405, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 16, index 10 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 225, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 30, index 11 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 226, 227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 229, 0, 0, 0, 0, 0, 0, 230, 231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 233, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 234, 235, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 31, index 12 */ 108, 109, 236, 237, 238, 239, 240, 241, 110, 111, 242, 243, 244, 245, 246, 247, 112, 113, 0, 0, 0, 0, 0, 0, 114, 115, 0, 0, 0, 0, 0, 0, 116, 117, 248, 249, 250, 251, 252, 253, 118, 119, 254, 255, 256, 257, 258, 259, 120, 121, 0, 0, 0, 0, 0, 0, 122, 123, 0, 0, 0, 0, 0, 0, 124, 125, 0, 0, 0, 0, 0, 0, 126, 127, 0, 0, 0, 0, 0, 0, 128, 129, 0, 0, 0, 0, 0, 0, 0, 130, 0, 0, 0, 0, 0, 0, 131, 132, 260, 261, 262, 263, 264, 265, 133, 134, 266, 267, 268, 269, 270, 271, 272, 0, 0, 0, 273, 0, 0, 0, 0, 0, 0, 0, 274, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 275, 0, 0, 0, 0, 0, 0, 0, 0, 135, 0, 0, 0, 0, 0, 0, 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 277, 0, 0, 0, 0, 0, 0, 0, 136, 0 }, { /* page 33, index 13 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 278, 0, 279, 0, 280, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 281, 0, 282, 0, 283, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 34, index 14 */ 0, 0, 0, 284, 0, 0, 0, 0, 285, 0, 0, 286, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 287, 0, 288, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 289, 0, 0, 0, 0, 0, 0, 290, 0, 291, 0, 0, 292, 0, 0, 0, 0, 293, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 294, 0, 0, 295, 296, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 297, 298, 0, 0, 299, 300, 0, 0, 301, 302, 303, 304, 0, 0, 0, 0, 305, 306, 0, 0, 307, 308, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 310, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 311, 0, 0, 0, 0, 0, 312, 313, 0, 314, 0, 0, 0, 0, 0, 0, 315, 316, 317, 318, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { /* page 48, index 15 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 319, 0, 0, 0, 0, 320, 0, 321, 0, 322, 0, 323, 0, 324, 0, 325, 0, 326, 0, 327, 0, 328, 0, 329, 0, 330, 0, 331, 0, 0, 332, 0, 333, 0, 334, 0, 0, 0, 0, 0, 0, 137, 0, 0, 138, 0, 0, 139, 0, 0, 140, 0, 0, 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 386, 387, 0, 0, 335, 0, 0, 0, 0, 0, 0, 0, 0, 336, 0, 0, 0, 0, 337, 0, 338, 0, 339, 0, 340, 0, 341, 0, 342, 0, 343, 0, 344, 0, 345, 0, 346, 0, 347, 0, 348, 0, 0, 349, 0, 350, 0, 351, 0, 0, 0, 0, 0, 0, 142, 0, 0, 143, 0, 0, 144, 0, 0, 145, 0, 0, 146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 352, 353, 354, 355, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 356, 0, 0 } }; static const short compose_table[COMPOSE_TABLE_LAST + 1] = { 0 /* page 0 */, 1 /* page 1 */, 2 /* page 2 */, 3 /* page 3 */, 4 /* page 4 */, 0 + UNICODE_MAX_TABLE_INDEX, 5 /* page 6 */, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 6 /* page 9 */, 0 + UNICODE_MAX_TABLE_INDEX, 7 /* page 11 */, 8 /* page 12 */, 9 /* page 13 */, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 10 /* page 16 */, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 11 /* page 30 */, 12 /* page 31 */, 0 + UNICODE_MAX_TABLE_INDEX, 13 /* page 33 */, 14 /* page 34 */, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 0 + UNICODE_MAX_TABLE_INDEX, 15 /* page 48 */ }; static const unsigned short compose_first_single[][2] = { { 0x0338, 0x226e }, { 0x0338, 0x2260 }, { 0x0338, 0x226f }, { 0x0307, 0x1e1e }, { 0x0302, 0x0134 }, { 0x0307, 0x1e1f }, { 0x0304, 0x01de }, { 0x0301, 0x01fa }, { 0x0301, 0x1e08 }, { 0x0301, 0x1e2e }, { 0x0304, 0x022a }, { 0x0301, 0x01fe }, { 0x0304, 0x01df }, { 0x0301, 0x01fb }, { 0x0301, 0x1e09 }, { 0x0301, 0x1e2f }, { 0x0304, 0x022b }, { 0x0301, 0x01ff }, { 0x0307, 0x1e64 }, { 0x0307, 0x1e65 }, { 0x0307, 0x1e66 }, { 0x0307, 0x1e67 }, { 0x0301, 0x1e78 }, { 0x0301, 0x1e79 }, { 0x0308, 0x1e7a }, { 0x0308, 0x1e7b }, { 0x0307, 0x1e9b }, { 0x030c, 0x01ee }, { 0x0304, 0x01ec }, { 0x0304, 0x01ed }, { 0x0304, 0x01e0 }, { 0x0304, 0x01e1 }, { 0x0306, 0x1e1c }, { 0x0306, 0x1e1d }, { 0x0304, 0x0230 }, { 0x0304, 0x0231 }, { 0x030c, 0x01ef }, { 0x0314, 0x1fec }, { 0x0345, 0x1fb4 }, { 0x0345, 0x1fc4 }, { 0x0345, 0x1ff4 }, { 0x0308, 0x0407 }, { 0x0301, 0x0403 }, { 0x0308, 0x04de }, { 0x0301, 0x040c }, { 0x0308, 0x04e6 }, { 0x0308, 0x04f4 }, { 0x0308, 0x04f8 }, { 0x0308, 0x04ec }, { 0x0301, 0x0453 }, { 0x0308, 0x04df }, { 0x0301, 0x045c }, { 0x0308, 0x04e7 }, { 0x0308, 0x04f5 }, { 0x0308, 0x04f9 }, { 0x0308, 0x04ed }, { 0x0308, 0x0457 }, { 0x030f, 0x0476 }, { 0x030f, 0x0477 }, { 0x0308, 0x04da }, { 0x0308, 0x04db }, { 0x0308, 0x04ea }, { 0x0308, 0x04eb }, { 0x0654, 0x0624 }, { 0x0654, 0x0626 }, { 0x0654, 0x06c2 }, { 0x0654, 0x06d3 }, { 0x0654, 0x06c0 }, { 0x093c, 0x0929 }, { 0x093c, 0x0931 }, { 0x093c, 0x0934 }, { 0x0bd7, 0x0b94 }, { 0x0bbe, 0x0bcb }, { 0x0c56, 0x0c48 }, { 0x0cd5, 0x0cc0 }, { 0x0cd5, 0x0ccb }, { 0x0d3e, 0x0d4b }, { 0x0dca, 0x0ddd }, { 0x102e, 0x1026 }, { 0x0304, 0x1e38 }, { 0x0304, 0x1e39 }, { 0x0304, 0x1e5c }, { 0x0304, 0x1e5d }, { 0x0307, 0x1e68 }, { 0x0307, 0x1e69 }, { 0x0302, 0x1ec6 }, { 0x0302, 0x1ec7 }, { 0x0302, 0x1ed8 }, { 0x0302, 0x1ed9 }, { 0x0345, 0x1f82 }, { 0x0345, 0x1f83 }, { 0x0345, 0x1f84 }, { 0x0345, 0x1f85 }, { 0x0345, 0x1f86 }, { 0x0345, 0x1f87 }, { 0x0345, 0x1f8a }, { 0x0345, 0x1f8b }, { 0x0345, 0x1f8c }, { 0x0345, 0x1f8d }, { 0x0345, 0x1f8e }, { 0x0345, 0x1f8f }, { 0x0345, 0x1f92 }, { 0x0345, 0x1f93 }, { 0x0345, 0x1f94 }, { 0x0345, 0x1f95 }, { 0x0345, 0x1f96 }, { 0x0345, 0x1f97 }, { 0x0345, 0x1f9a }, { 0x0345, 0x1f9b }, { 0x0345, 0x1f9c }, { 0x0345, 0x1f9d }, { 0x0345, 0x1f9e }, { 0x0345, 0x1f9f }, { 0x0345, 0x1fa2 }, { 0x0345, 0x1fa3 }, { 0x0345, 0x1fa4 }, { 0x0345, 0x1fa5 }, { 0x0345, 0x1fa6 }, { 0x0345, 0x1fa7 }, { 0x0345, 0x1faa }, { 0x0345, 0x1fab }, { 0x0345, 0x1fac }, { 0x0345, 0x1fad }, { 0x0345, 0x1fae }, { 0x0345, 0x1faf }, { 0x0345, 0x1fb2 }, { 0x0345, 0x1fc2 }, { 0x0345, 0x1ff2 }, { 0x0345, 0x1fb7 }, { 0x0345, 0x1fc7 }, { 0x0345, 0x1ff7 }, { 0x0338, 0x219a }, { 0x0338, 0x219b }, { 0x0338, 0x21ae }, { 0x0338, 0x21cd }, { 0x0338, 0x21cf }, { 0x0338, 0x21ce }, { 0x0338, 0x2204 }, { 0x0338, 0x2209 }, { 0x0338, 0x220c }, { 0x0338, 0x2224 }, { 0x0338, 0x2226 }, { 0x0338, 0x2241 }, { 0x0338, 0x2244 }, { 0x0338, 0x2247 }, { 0x0338, 0x2249 }, { 0x0338, 0x226d }, { 0x0338, 0x2262 }, { 0x0338, 0x2270 }, { 0x0338, 0x2271 }, { 0x0338, 0x2274 }, { 0x0338, 0x2275 }, { 0x0338, 0x2278 }, { 0x0338, 0x2279 }, { 0x0338, 0x2280 }, { 0x0338, 0x2281 }, { 0x0338, 0x22e0 }, { 0x0338, 0x22e1 }, { 0x0338, 0x2284 }, { 0x0338, 0x2285 }, { 0x0338, 0x2288 }, { 0x0338, 0x2289 }, { 0x0338, 0x22e2 }, { 0x0338, 0x22e3 }, { 0x0338, 0x22ac }, { 0x0338, 0x22ad }, { 0x0338, 0x22ae }, { 0x0338, 0x22af }, { 0x0338, 0x22ea }, { 0x0338, 0x22eb }, { 0x0338, 0x22ec }, { 0x0338, 0x22ed }, { 0x3099, 0x3094 }, { 0x3099, 0x304c }, { 0x3099, 0x304e }, { 0x3099, 0x3050 }, { 0x3099, 0x3052 }, { 0x3099, 0x3054 }, { 0x3099, 0x3056 }, { 0x3099, 0x3058 }, { 0x3099, 0x305a }, { 0x3099, 0x305c }, { 0x3099, 0x305e }, { 0x3099, 0x3060 }, { 0x3099, 0x3062 }, { 0x3099, 0x3065 }, { 0x3099, 0x3067 }, { 0x3099, 0x3069 }, { 0x3099, 0x309e }, { 0x3099, 0x30f4 }, { 0x3099, 0x30ac }, { 0x3099, 0x30ae }, { 0x3099, 0x30b0 }, { 0x3099, 0x30b2 }, { 0x3099, 0x30b4 }, { 0x3099, 0x30b6 }, { 0x3099, 0x30b8 }, { 0x3099, 0x30ba }, { 0x3099, 0x30bc }, { 0x3099, 0x30be }, { 0x3099, 0x30c0 }, { 0x3099, 0x30c2 }, { 0x3099, 0x30c5 }, { 0x3099, 0x30c7 }, { 0x3099, 0x30c9 }, { 0x3099, 0x30f7 }, { 0x3099, 0x30f8 }, { 0x3099, 0x30f9 }, { 0x3099, 0x30fa }, { 0x3099, 0x30fe } }; static const unsigned short compose_second_single[][2] = { { 0x0627, 0x0622 }, { 0x0627, 0x0623 }, { 0x0627, 0x0625 }, { 0x09c7, 0x09cb }, { 0x09c7, 0x09cc }, { 0x0b47, 0x0b4b }, { 0x0b47, 0x0b48 }, { 0x0b47, 0x0b4c }, { 0x0bc6, 0x0bca }, { 0x0bc6, 0x0bcc }, { 0x0cc6, 0x0cca }, { 0x0cc6, 0x0cc7 }, { 0x0cc6, 0x0cc8 }, { 0x0d46, 0x0d4a }, { 0x0d46, 0x0d4c }, { 0x0dd9, 0x0dda }, { 0x0dd9, 0x0ddc }, { 0x0dd9, 0x0dde } }; static const unsigned short compose_array[146][31] = { { 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x0100, 0x0102, 0x0226, 0x00c4, 0x1ea2, 0x00c5, 0, 0x01cd, 0x0200, 0x0202, 0, 0, 0, 0x1ea0, 0, 0x1e00, 0, 0, 0x0104, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0x1e02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1e04, 0, 0, 0, 0, 0, 0, 0, 0, 0x1e06, 0, 0, 0, 0 }, { 0, 0x0106, 0x0108, 0, 0, 0, 0x010a, 0, 0, 0, 0, 0x010c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00c7, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0x1e0a, 0, 0, 0, 0, 0x010e, 0, 0, 0, 0, 0, 0x1e0c, 0, 0, 0, 0x1e10, 0, 0x1e12, 0, 0, 0x1e0e, 0, 0, 0, 0 }, { 0x00c8, 0x00c9, 0x00ca, 0x1ebc, 0x0112, 0x0114, 0x0116, 0x00cb, 0x1eba, 0, 0, 0x011a, 0x0204, 0x0206, 0, 0, 0, 0x1eb8, 0, 0, 0, 0x0228, 0x0118, 0x1e18, 0, 0x1e1a, 0, 0, 0, 0, 0 }, { 0, 0x01f4, 0x011c, 0, 0x1e20, 0x011e, 0x0120, 0, 0, 0, 0, 0x01e6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0122, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0x0124, 0, 0, 0, 0x1e22, 0x1e26, 0, 0, 0, 0x021e, 0, 0, 0, 0, 0, 0x1e24, 0, 0, 0, 0x1e28, 0, 0, 0x1e2a, 0, 0, 0, 0, 0, 0 }, { 0x00cc, 0x00cd, 0x00ce, 0x0128, 0x012a, 0x012c, 0x0130, 0x00cf, 0x1ec8, 0, 0, 0x01cf, 0x0208, 0x020a, 0, 0, 0, 0x1eca, 0, 0, 0, 0, 0x012e, 0, 0, 0x1e2c, 0, 0, 0, 0, 0 }, { 0, 0x1e30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01e8, 0, 0, 0, 0, 0, 0x1e32, 0, 0, 0, 0x0136, 0, 0, 0, 0, 0x1e34, 0, 0, 0, 0 }, { 0, 0x0139, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x013d, 0, 0, 0, 0, 0, 0x1e36, 0, 0, 0, 0x013b, 0, 0x1e3c, 0, 0, 0x1e3a, 0, 0, 0, 0 }, { 0, 0x1e3e, 0, 0, 0, 0, 0x1e40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1e42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x01f8, 0x0143, 0, 0x00d1, 0, 0, 0x1e44, 0, 0, 0, 0, 0x0147, 0, 0, 0, 0, 0, 0x1e46, 0, 0, 0, 0x0145, 0, 0x1e4a, 0, 0, 0x1e48, 0, 0, 0, 0 }, { 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x014c, 0x014e, 0x022e, 0x00d6, 0x1ece, 0, 0x0150, 0x01d1, 0x020c, 0x020e, 0, 0, 0x01a0, 0x1ecc, 0, 0, 0, 0, 0x01ea, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0x1e54, 0, 0, 0, 0, 0x1e56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0x0154, 0, 0, 0, 0, 0x1e58, 0, 0, 0, 0, 0x0158, 0x0210, 0x0212, 0, 0, 0, 0x1e5a, 0, 0, 0, 0x0156, 0, 0, 0, 0, 0x1e5e, 0, 0, 0, 0 }, { 0, 0x015a, 0x015c, 0, 0, 0, 0x1e60, 0, 0, 0, 0, 0x0160, 0, 0, 0, 0, 0, 0x1e62, 0, 0, 0x0218, 0x015e, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0x1e6a, 0, 0, 0, 0, 0x0164, 0, 0, 0, 0, 0, 0x1e6c, 0, 0, 0x021a, 0x0162, 0, 0x1e70, 0, 0, 0x1e6e, 0, 0, 0, 0 }, { 0x00d9, 0x00da, 0x00db, 0x0168, 0x016a, 0x016c, 0, 0x00dc, 0x1ee6, 0x016e, 0x0170, 0x01d3, 0x0214, 0x0216, 0, 0, 0x01af, 0x1ee4, 0x1e72, 0, 0, 0, 0x0172, 0x1e76, 0, 0x1e74, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0x1e7c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1e7e, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1e80, 0x1e82, 0x0174, 0, 0, 0, 0x1e86, 0x1e84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1e88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0x1e8a, 0x1e8c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1ef2, 0x00dd, 0x0176, 0x1ef8, 0x0232, 0, 0x1e8e, 0x0178, 0x1ef6, 0, 0, 0, 0, 0, 0, 0, 0, 0x1ef4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0x0179, 0x1e90, 0, 0, 0, 0x017b, 0, 0, 0, 0, 0x017d, 0, 0, 0, 0, 0, 0x1e92, 0, 0, 0, 0, 0, 0, 0, 0, 0x1e94, 0, 0, 0, 0 }, { 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x0101, 0x0103, 0x0227, 0x00e4, 0x1ea3, 0x00e5, 0, 0x01ce, 0x0201, 0x0203, 0, 0, 0, 0x1ea1, 0, 0x1e01, 0, 0, 0x0105, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0x1e03, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1e05, 0, 0, 0, 0, 0, 0, 0, 0, 0x1e07, 0, 0, 0, 0 }, { 0, 0x0107, 0x0109, 0, 0, 0, 0x010b, 0, 0, 0, 0, 0x010d, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00e7, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0x1e0b, 0, 0, 0, 0, 0x010f, 0, 0, 0, 0, 0, 0x1e0d, 0, 0, 0, 0x1e11, 0, 0x1e13, 0, 0, 0x1e0f, 0, 0, 0, 0 }, { 0x00e8, 0x00e9, 0x00ea, 0x1ebd, 0x0113, 0x0115, 0x0117, 0x00eb, 0x1ebb, 0, 0, 0x011b, 0x0205, 0x0207, 0, 0, 0, 0x1eb9, 0, 0, 0, 0x0229, 0x0119, 0x1e19, 0, 0x1e1b, 0, 0, 0, 0, 0 }, { 0, 0x01f5, 0x011d, 0, 0x1e21, 0x011f, 0x0121, 0, 0, 0, 0, 0x01e7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0123, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0x0125, 0, 0, 0, 0x1e23, 0x1e27, 0, 0, 0, 0x021f, 0, 0, 0, 0, 0, 0x1e25, 0, 0, 0, 0x1e29, 0, 0, 0x1e2b, 0, 0x1e96, 0, 0, 0, 0 }, { 0x00ec, 0x00ed, 0x00ee, 0x0129, 0x012b, 0x012d, 0, 0x00ef, 0x1ec9, 0, 0, 0x01d0, 0x0209, 0x020b, 0, 0, 0, 0x1ecb, 0, 0, 0, 0, 0x012f, 0, 0, 0x1e2d, 0, 0, 0, 0, 0 }, { 0, 0, 0x0135, 0, 0, 0, 0, 0, 0, 0, 0, 0x01f0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0x1e31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01e9, 0, 0, 0, 0, 0, 0x1e33, 0, 0, 0, 0x0137, 0, 0, 0, 0, 0x1e35, 0, 0, 0, 0 }, { 0, 0x013a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x013e, 0, 0, 0, 0, 0, 0x1e37, 0, 0, 0, 0x013c, 0, 0x1e3d, 0, 0, 0x1e3b, 0, 0, 0, 0 }, { 0, 0x1e3f, 0, 0, 0, 0, 0x1e41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1e43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x01f9, 0x0144, 0, 0x00f1, 0, 0, 0x1e45, 0, 0, 0, 0, 0x0148, 0, 0, 0, 0, 0, 0x1e47, 0, 0, 0, 0x0146, 0, 0x1e4b, 0, 0, 0x1e49, 0, 0, 0, 0 }, { 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x014d, 0x014f, 0x022f, 0x00f6, 0x1ecf, 0, 0x0151, 0x01d2, 0x020d, 0x020f, 0, 0, 0x01a1, 0x1ecd, 0, 0, 0, 0, 0x01eb, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0x1e55, 0, 0, 0, 0, 0x1e57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0x0155, 0, 0, 0, 0, 0x1e59, 0, 0, 0, 0, 0x0159, 0x0211, 0x0213, 0, 0, 0, 0x1e5b, 0, 0, 0, 0x0157, 0, 0, 0, 0, 0x1e5f, 0, 0, 0, 0 }, { 0, 0x015b, 0x015d, 0, 0, 0, 0x1e61, 0, 0, 0, 0, 0x0161, 0, 0, 0, 0, 0, 0x1e63, 0, 0, 0x0219, 0x015f, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0x1e6b, 0x1e97, 0, 0, 0, 0x0165, 0, 0, 0, 0, 0, 0x1e6d, 0, 0, 0x021b, 0x0163, 0, 0x1e71, 0, 0, 0x1e6f, 0, 0, 0, 0 }, { 0x00f9, 0x00fa, 0x00fb, 0x0169, 0x016b, 0x016d, 0, 0x00fc, 0x1ee7, 0x016f, 0x0171, 0x01d4, 0x0215, 0x0217, 0, 0, 0x01b0, 0x1ee5, 0x1e73, 0, 0, 0, 0x0173, 0x1e77, 0, 0x1e75, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0x1e7d, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1e7f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1e81, 0x1e83, 0x0175, 0, 0, 0, 0x1e87, 0x1e85, 0, 0x1e98, 0, 0, 0, 0, 0, 0, 0, 0x1e89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0x1e8b, 0x1e8d, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1ef3, 0x00fd, 0x0177, 0x1ef9, 0x0233, 0, 0x1e8f, 0x00ff, 0x1ef7, 0x1e99, 0, 0, 0, 0, 0, 0, 0, 0x1ef5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0x017a, 0x1e91, 0, 0, 0, 0x017c, 0, 0, 0, 0, 0x017e, 0, 0, 0, 0, 0, 0x1e93, 0, 0, 0, 0, 0, 0, 0, 0, 0x1e95, 0, 0, 0, 0 }, { 0x1fed, 0x0385, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1fc1, 0, 0, 0 }, { 0x1ea6, 0x1ea4, 0, 0x1eaa, 0, 0, 0, 0, 0x1ea8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0x01fc, 0, 0, 0x01e2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1ec0, 0x1ebe, 0, 0x1ec4, 0, 0, 0, 0, 0x1ec2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1ed2, 0x1ed0, 0, 0x1ed6, 0, 0, 0, 0, 0x1ed4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0x1e4c, 0, 0, 0x022c, 0, 0, 0x1e4e, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x01db, 0x01d7, 0, 0, 0x01d5, 0, 0, 0, 0, 0, 0, 0x01d9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1ea7, 0x1ea5, 0, 0x1eab, 0, 0, 0, 0, 0x1ea9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0x01fd, 0, 0, 0x01e3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1ec1, 0x1ebf, 0, 0x1ec5, 0, 0, 0, 0, 0x1ec3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1ed3, 0x1ed1, 0, 0x1ed7, 0, 0, 0, 0, 0x1ed5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0x1e4d, 0, 0, 0x022d, 0, 0, 0x1e4f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x01dc, 0x01d8, 0, 0, 0x01d6, 0, 0, 0, 0, 0, 0, 0x01da, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1eb0, 0x1eae, 0, 0x1eb4, 0, 0, 0, 0, 0x1eb2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1eb1, 0x1eaf, 0, 0x1eb5, 0, 0, 0, 0, 0x1eb3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1e14, 0x1e16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1e15, 0x1e17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1e50, 0x1e52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1e51, 0x1e53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1edc, 0x1eda, 0, 0x1ee0, 0, 0, 0, 0, 0x1ede, 0, 0, 0, 0, 0, 0, 0, 0, 0x1ee2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1edd, 0x1edb, 0, 0x1ee1, 0, 0, 0, 0, 0x1edf, 0, 0, 0, 0, 0, 0, 0, 0, 0x1ee3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1eea, 0x1ee8, 0, 0x1eee, 0, 0, 0, 0, 0x1eec, 0, 0, 0, 0, 0, 0, 0, 0, 0x1ef0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1eeb, 0x1ee9, 0, 0x1eef, 0, 0, 0, 0, 0x1eed, 0, 0, 0, 0, 0, 0, 0, 0, 0x1ef1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1fba, 0x0386, 0, 0, 0x1fb9, 0x1fb8, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f08, 0x1f09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1fbc, 0, 0 }, { 0x1fc8, 0x0388, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f18, 0x1f19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1fca, 0x0389, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f28, 0x1f29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1fcc, 0, 0 }, { 0x1fda, 0x038a, 0, 0, 0x1fd9, 0x1fd8, 0, 0x03aa, 0, 0, 0, 0, 0, 0, 0x1f38, 0x1f39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1ff8, 0x038c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f48, 0x1f49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1fea, 0x038e, 0, 0, 0x1fe9, 0x1fe8, 0, 0x03ab, 0, 0, 0, 0, 0, 0, 0, 0x1f59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1ffa, 0x038f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f68, 0x1f69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1ffc, 0, 0 }, { 0x1f70, 0x03ac, 0, 0, 0x1fb1, 0x1fb0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f00, 0x1f01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1fb6, 0x1fb3, 0, 0 }, { 0x1f72, 0x03ad, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f10, 0x1f11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1f74, 0x03ae, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f20, 0x1f21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1fc6, 0x1fc3, 0, 0 }, { 0x1f76, 0x03af, 0, 0, 0x1fd1, 0x1fd0, 0, 0x03ca, 0, 0, 0, 0, 0, 0, 0x1f30, 0x1f31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1fd6, 0, 0, 0 }, { 0x1f78, 0x03cc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f40, 0x1f41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1fe4, 0x1fe5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1f7a, 0x03cd, 0, 0, 0x1fe1, 0x1fe0, 0, 0x03cb, 0, 0, 0, 0, 0, 0, 0x1f50, 0x1f51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1fe6, 0, 0, 0 }, { 0x1f7c, 0x03ce, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f60, 0x1f61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1ff6, 0x1ff3, 0, 0 }, { 0x1fd2, 0x0390, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1fd7, 0, 0, 0 }, { 0x1fe2, 0x03b0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1fe7, 0, 0, 0 }, { 0, 0x03d3, 0, 0, 0, 0, 0, 0x03d4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0x04d0, 0, 0x04d2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x0400, 0, 0, 0, 0, 0x04d6, 0, 0x0401, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0x04c1, 0, 0x04dc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x040d, 0, 0, 0, 0x04e2, 0x0419, 0, 0x04e4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0x04ee, 0x040e, 0, 0x04f0, 0, 0, 0x04f2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0x04d1, 0, 0x04d3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x0450, 0, 0, 0, 0, 0x04d7, 0, 0x0451, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0x04c2, 0, 0x04dd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x045d, 0, 0, 0, 0x04e3, 0x0439, 0, 0x04e5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0x04ef, 0x045e, 0, 0x04f1, 0, 0, 0x04f3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0x1eac, 0, 0, 0x1eb6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0x1ead, 0, 0, 0x1eb7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1f02, 0x1f04, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f06, 0x1f80, 0, 0 }, { 0x1f03, 0x1f05, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f07, 0x1f81, 0, 0 }, { 0x1f0a, 0x1f0c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f0e, 0x1f88, 0, 0 }, { 0x1f0b, 0x1f0d, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f0f, 0x1f89, 0, 0 }, { 0x1f12, 0x1f14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1f13, 0x1f15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1f1a, 0x1f1c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1f1b, 0x1f1d, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1f22, 0x1f24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f26, 0x1f90, 0, 0 }, { 0x1f23, 0x1f25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f27, 0x1f91, 0, 0 }, { 0x1f2a, 0x1f2c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f2e, 0x1f98, 0, 0 }, { 0x1f2b, 0x1f2d, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f2f, 0x1f99, 0, 0 }, { 0x1f32, 0x1f34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f36, 0, 0, 0 }, { 0x1f33, 0x1f35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f37, 0, 0, 0 }, { 0x1f3a, 0x1f3c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f3e, 0, 0, 0 }, { 0x1f3b, 0x1f3d, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f3f, 0, 0, 0 }, { 0x1f42, 0x1f44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1f43, 0x1f45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1f4a, 0x1f4c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1f4b, 0x1f4d, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x1f52, 0x1f54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f56, 0, 0, 0 }, { 0x1f53, 0x1f55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f57, 0, 0, 0 }, { 0x1f5b, 0x1f5d, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f5f, 0, 0, 0 }, { 0x1f62, 0x1f64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f66, 0x1fa0, 0, 0 }, { 0x1f63, 0x1f65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f67, 0x1fa1, 0, 0 }, { 0x1f6a, 0x1f6c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f6e, 0x1fa8, 0, 0 }, { 0x1f6b, 0x1f6d, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1f6f, 0x1fa9, 0, 0 }, { 0x1fcd, 0x1fce, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1fcf, 0, 0, 0 }, { 0x1fdd, 0x1fde, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1fdf, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x3070, 0x3071 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x3073, 0x3074 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x3076, 0x3077 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x3079, 0x307a }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x307c, 0x307d }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x30d0, 0x30d1 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x30d3, 0x30d4 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x30d6, 0x30d7 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x30d9, 0x30da }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x30dc, 0x30dd } }; poppler-24.02.0/poppler/UnicodeDecompTables.h000066400000000000000000010073071455701731300210610ustar00rootroot00000000000000// Generated by gen-unicode-tables.py typedef struct { Unicode character; int length; int offset; } decomposition; #define DECOMP_TABLE_LENGTH 5722 static const decomposition decomp_table[] = { { 0xa0, 1, 0 }, { 0xa8, 2, 1 }, { 0xaa, 1, 3 }, { 0xaf, 2, 4 }, { 0xb2, 1, 6 }, { 0xb3, 1, 7 }, { 0xb4, 2, 8 }, { 0xb5, 1, 10 }, { 0xb8, 2, 11 }, { 0xb9, 1, 13 }, { 0xba, 1, 14 }, { 0xbc, 3, 15 }, { 0xbd, 3, 18 }, { 0xbe, 3, 21 }, { 0xc0, 2, 24 }, { 0xc1, 2, 26 }, { 0xc2, 2, 28 }, { 0xc3, 2, 30 }, { 0xc4, 2, 32 }, { 0xc5, 2, 34 }, { 0xc7, 2, 36 }, { 0xc8, 2, 38 }, { 0xc9, 2, 40 }, { 0xca, 2, 42 }, { 0xcb, 2, 44 }, { 0xcc, 2, 46 }, { 0xcd, 2, 48 }, { 0xce, 2, 50 }, { 0xcf, 2, 52 }, { 0xd1, 2, 54 }, { 0xd2, 2, 56 }, { 0xd3, 2, 58 }, { 0xd4, 2, 60 }, { 0xd5, 2, 62 }, { 0xd6, 2, 64 }, { 0xd9, 2, 66 }, { 0xda, 2, 68 }, { 0xdb, 2, 70 }, { 0xdc, 2, 72 }, { 0xdd, 2, 74 }, { 0xe0, 2, 76 }, { 0xe1, 2, 78 }, { 0xe2, 2, 80 }, { 0xe3, 2, 82 }, { 0xe4, 2, 84 }, { 0xe5, 2, 86 }, { 0xe7, 2, 88 }, { 0xe8, 2, 90 }, { 0xe9, 2, 92 }, { 0xea, 2, 94 }, { 0xeb, 2, 96 }, { 0xec, 2, 98 }, { 0xed, 2, 100 }, { 0xee, 2, 102 }, { 0xef, 2, 104 }, { 0xf1, 2, 106 }, { 0xf2, 2, 108 }, { 0xf3, 2, 110 }, { 0xf4, 2, 112 }, { 0xf5, 2, 114 }, { 0xf6, 2, 116 }, { 0xf9, 2, 118 }, { 0xfa, 2, 120 }, { 0xfb, 2, 122 }, { 0xfc, 2, 124 }, { 0xfd, 2, 126 }, { 0xff, 2, 128 }, { 0x100, 2, 130 }, { 0x101, 2, 132 }, { 0x102, 2, 134 }, { 0x103, 2, 136 }, { 0x104, 2, 138 }, { 0x105, 2, 140 }, { 0x106, 2, 142 }, { 0x107, 2, 144 }, { 0x108, 2, 146 }, { 0x109, 2, 148 }, { 0x10a, 2, 150 }, { 0x10b, 2, 152 }, { 0x10c, 2, 154 }, { 0x10d, 2, 156 }, { 0x10e, 2, 158 }, { 0x10f, 2, 160 }, { 0x112, 2, 162 }, { 0x113, 2, 164 }, { 0x114, 2, 166 }, { 0x115, 2, 168 }, { 0x116, 2, 170 }, { 0x117, 2, 172 }, { 0x118, 2, 174 }, { 0x119, 2, 176 }, { 0x11a, 2, 178 }, { 0x11b, 2, 180 }, { 0x11c, 2, 182 }, { 0x11d, 2, 184 }, { 0x11e, 2, 186 }, { 0x11f, 2, 188 }, { 0x120, 2, 190 }, { 0x121, 2, 192 }, { 0x122, 2, 194 }, { 0x123, 2, 196 }, { 0x124, 2, 198 }, { 0x125, 2, 200 }, { 0x128, 2, 202 }, { 0x129, 2, 204 }, { 0x12a, 2, 206 }, { 0x12b, 2, 208 }, { 0x12c, 2, 210 }, { 0x12d, 2, 212 }, { 0x12e, 2, 214 }, { 0x12f, 2, 216 }, { 0x130, 2, 218 }, { 0x132, 2, 220 }, { 0x133, 2, 222 }, { 0x134, 2, 224 }, { 0x135, 2, 226 }, { 0x136, 2, 228 }, { 0x137, 2, 230 }, { 0x139, 2, 232 }, { 0x13a, 2, 234 }, { 0x13b, 2, 236 }, { 0x13c, 2, 238 }, { 0x13d, 2, 240 }, { 0x13e, 2, 242 }, { 0x13f, 2, 244 }, { 0x140, 2, 246 }, { 0x143, 2, 248 }, { 0x144, 2, 250 }, { 0x145, 2, 252 }, { 0x146, 2, 254 }, { 0x147, 2, 256 }, { 0x148, 2, 258 }, { 0x149, 2, 260 }, { 0x14c, 2, 262 }, { 0x14d, 2, 264 }, { 0x14e, 2, 266 }, { 0x14f, 2, 268 }, { 0x150, 2, 270 }, { 0x151, 2, 272 }, { 0x154, 2, 274 }, { 0x155, 2, 276 }, { 0x156, 2, 278 }, { 0x157, 2, 280 }, { 0x158, 2, 282 }, { 0x159, 2, 284 }, { 0x15a, 2, 286 }, { 0x15b, 2, 288 }, { 0x15c, 2, 290 }, { 0x15d, 2, 292 }, { 0x15e, 2, 294 }, { 0x15f, 2, 296 }, { 0x160, 2, 298 }, { 0x161, 2, 300 }, { 0x162, 2, 302 }, { 0x163, 2, 304 }, { 0x164, 2, 306 }, { 0x165, 2, 308 }, { 0x168, 2, 310 }, { 0x169, 2, 312 }, { 0x16a, 2, 314 }, { 0x16b, 2, 316 }, { 0x16c, 2, 318 }, { 0x16d, 2, 320 }, { 0x16e, 2, 322 }, { 0x16f, 2, 324 }, { 0x170, 2, 326 }, { 0x171, 2, 328 }, { 0x172, 2, 330 }, { 0x173, 2, 332 }, { 0x174, 2, 334 }, { 0x175, 2, 336 }, { 0x176, 2, 338 }, { 0x177, 2, 340 }, { 0x178, 2, 342 }, { 0x179, 2, 344 }, { 0x17a, 2, 346 }, { 0x17b, 2, 348 }, { 0x17c, 2, 350 }, { 0x17d, 2, 352 }, { 0x17e, 2, 354 }, { 0x17f, 1, 356 }, { 0x1a0, 2, 357 }, { 0x1a1, 2, 359 }, { 0x1af, 2, 361 }, { 0x1b0, 2, 363 }, { 0x1c4, 3, 365 }, { 0x1c5, 3, 368 }, { 0x1c6, 3, 371 }, { 0x1c7, 2, 374 }, { 0x1c8, 2, 376 }, { 0x1c9, 2, 378 }, { 0x1ca, 2, 380 }, { 0x1cb, 2, 382 }, { 0x1cc, 2, 384 }, { 0x1cd, 2, 386 }, { 0x1ce, 2, 388 }, { 0x1cf, 2, 390 }, { 0x1d0, 2, 392 }, { 0x1d1, 2, 394 }, { 0x1d2, 2, 396 }, { 0x1d3, 2, 398 }, { 0x1d4, 2, 400 }, { 0x1d5, 3, 402 }, { 0x1d6, 3, 405 }, { 0x1d7, 3, 408 }, { 0x1d8, 3, 411 }, { 0x1d9, 3, 414 }, { 0x1da, 3, 417 }, { 0x1db, 3, 420 }, { 0x1dc, 3, 423 }, { 0x1de, 3, 426 }, { 0x1df, 3, 429 }, { 0x1e0, 3, 432 }, { 0x1e1, 3, 435 }, { 0x1e2, 2, 438 }, { 0x1e3, 2, 440 }, { 0x1e6, 2, 442 }, { 0x1e7, 2, 444 }, { 0x1e8, 2, 446 }, { 0x1e9, 2, 448 }, { 0x1ea, 2, 450 }, { 0x1eb, 2, 452 }, { 0x1ec, 3, 454 }, { 0x1ed, 3, 457 }, { 0x1ee, 2, 460 }, { 0x1ef, 2, 462 }, { 0x1f0, 2, 464 }, { 0x1f1, 2, 466 }, { 0x1f2, 2, 468 }, { 0x1f3, 2, 470 }, { 0x1f4, 2, 472 }, { 0x1f5, 2, 474 }, { 0x1f8, 2, 476 }, { 0x1f9, 2, 478 }, { 0x1fa, 3, 480 }, { 0x1fb, 3, 483 }, { 0x1fc, 2, 486 }, { 0x1fd, 2, 488 }, { 0x1fe, 2, 490 }, { 0x1ff, 2, 492 }, { 0x200, 2, 494 }, { 0x201, 2, 496 }, { 0x202, 2, 498 }, { 0x203, 2, 500 }, { 0x204, 2, 502 }, { 0x205, 2, 504 }, { 0x206, 2, 506 }, { 0x207, 2, 508 }, { 0x208, 2, 510 }, { 0x209, 2, 512 }, { 0x20a, 2, 514 }, { 0x20b, 2, 516 }, { 0x20c, 2, 518 }, { 0x20d, 2, 520 }, { 0x20e, 2, 522 }, { 0x20f, 2, 524 }, { 0x210, 2, 526 }, { 0x211, 2, 528 }, { 0x212, 2, 530 }, { 0x213, 2, 532 }, { 0x214, 2, 534 }, { 0x215, 2, 536 }, { 0x216, 2, 538 }, { 0x217, 2, 540 }, { 0x218, 2, 542 }, { 0x219, 2, 544 }, { 0x21a, 2, 546 }, { 0x21b, 2, 548 }, { 0x21e, 2, 550 }, { 0x21f, 2, 552 }, { 0x226, 2, 554 }, { 0x227, 2, 556 }, { 0x228, 2, 558 }, { 0x229, 2, 560 }, { 0x22a, 3, 562 }, { 0x22b, 3, 565 }, { 0x22c, 3, 568 }, { 0x22d, 3, 571 }, { 0x22e, 2, 574 }, { 0x22f, 2, 576 }, { 0x230, 3, 578 }, { 0x231, 3, 581 }, { 0x232, 2, 584 }, { 0x233, 2, 586 }, { 0x2b0, 1, 588 }, { 0x2b1, 1, 589 }, { 0x2b2, 1, 590 }, { 0x2b3, 1, 591 }, { 0x2b4, 1, 592 }, { 0x2b5, 1, 593 }, { 0x2b6, 1, 594 }, { 0x2b7, 1, 595 }, { 0x2b8, 1, 596 }, { 0x2d8, 2, 597 }, { 0x2d9, 2, 599 }, { 0x2da, 2, 601 }, { 0x2db, 2, 603 }, { 0x2dc, 2, 605 }, { 0x2dd, 2, 607 }, { 0x2e0, 1, 609 }, { 0x2e1, 1, 610 }, { 0x2e2, 1, 356 }, { 0x2e3, 1, 611 }, { 0x2e4, 1, 612 }, { 0x340, 1, 613 }, { 0x341, 1, 614 }, { 0x343, 1, 615 }, { 0x344, 2, 616 }, { 0x374, 1, 618 }, { 0x37a, 2, 619 }, { 0x37e, 1, 621 }, { 0x384, 2, 8 }, { 0x385, 3, 622 }, { 0x386, 2, 625 }, { 0x387, 1, 627 }, { 0x388, 2, 628 }, { 0x389, 2, 630 }, { 0x38a, 2, 632 }, { 0x38c, 2, 634 }, { 0x38e, 2, 636 }, { 0x38f, 2, 638 }, { 0x390, 3, 640 }, { 0x3aa, 2, 643 }, { 0x3ab, 2, 645 }, { 0x3ac, 2, 647 }, { 0x3ad, 2, 649 }, { 0x3ae, 2, 651 }, { 0x3af, 2, 653 }, { 0x3b0, 3, 655 }, { 0x3ca, 2, 658 }, { 0x3cb, 2, 660 }, { 0x3cc, 2, 662 }, { 0x3cd, 2, 664 }, { 0x3ce, 2, 666 }, { 0x3d0, 1, 668 }, { 0x3d1, 1, 669 }, { 0x3d2, 1, 670 }, { 0x3d3, 2, 636 }, { 0x3d4, 2, 645 }, { 0x3d5, 1, 671 }, { 0x3d6, 1, 672 }, { 0x3f0, 1, 673 }, { 0x3f1, 1, 674 }, { 0x3f2, 1, 675 }, { 0x3f4, 1, 676 }, { 0x3f5, 1, 677 }, { 0x3f9, 1, 678 }, { 0x400, 2, 679 }, { 0x401, 2, 681 }, { 0x403, 2, 683 }, { 0x407, 2, 685 }, { 0x40c, 2, 687 }, { 0x40d, 2, 689 }, { 0x40e, 2, 691 }, { 0x419, 2, 693 }, { 0x439, 2, 695 }, { 0x450, 2, 697 }, { 0x451, 2, 699 }, { 0x453, 2, 701 }, { 0x457, 2, 703 }, { 0x45c, 2, 705 }, { 0x45d, 2, 707 }, { 0x45e, 2, 709 }, { 0x476, 2, 711 }, { 0x477, 2, 713 }, { 0x4c1, 2, 715 }, { 0x4c2, 2, 717 }, { 0x4d0, 2, 719 }, { 0x4d1, 2, 721 }, { 0x4d2, 2, 723 }, { 0x4d3, 2, 725 }, { 0x4d6, 2, 727 }, { 0x4d7, 2, 729 }, { 0x4da, 2, 731 }, { 0x4db, 2, 733 }, { 0x4dc, 2, 735 }, { 0x4dd, 2, 737 }, { 0x4de, 2, 739 }, { 0x4df, 2, 741 }, { 0x4e2, 2, 743 }, { 0x4e3, 2, 745 }, { 0x4e4, 2, 747 }, { 0x4e5, 2, 749 }, { 0x4e6, 2, 751 }, { 0x4e7, 2, 753 }, { 0x4ea, 2, 755 }, { 0x4eb, 2, 757 }, { 0x4ec, 2, 759 }, { 0x4ed, 2, 761 }, { 0x4ee, 2, 763 }, { 0x4ef, 2, 765 }, { 0x4f0, 2, 767 }, { 0x4f1, 2, 769 }, { 0x4f2, 2, 771 }, { 0x4f3, 2, 773 }, { 0x4f4, 2, 775 }, { 0x4f5, 2, 777 }, { 0x4f8, 2, 779 }, { 0x4f9, 2, 781 }, { 0x587, 2, 783 }, { 0x622, 2, 785 }, { 0x623, 2, 787 }, { 0x624, 2, 789 }, { 0x625, 2, 791 }, { 0x626, 2, 793 }, { 0x675, 2, 795 }, { 0x676, 2, 797 }, { 0x677, 2, 799 }, { 0x678, 2, 801 }, { 0x6c0, 2, 803 }, { 0x6c2, 2, 805 }, { 0x6d3, 2, 807 }, { 0x929, 2, 809 }, { 0x931, 2, 811 }, { 0x934, 2, 813 }, { 0x958, 2, 815 }, { 0x959, 2, 817 }, { 0x95a, 2, 819 }, { 0x95b, 2, 821 }, { 0x95c, 2, 823 }, { 0x95d, 2, 825 }, { 0x95e, 2, 827 }, { 0x95f, 2, 829 }, { 0x9cb, 2, 831 }, { 0x9cc, 2, 833 }, { 0x9dc, 2, 835 }, { 0x9dd, 2, 837 }, { 0x9df, 2, 839 }, { 0xa33, 2, 841 }, { 0xa36, 2, 843 }, { 0xa59, 2, 845 }, { 0xa5a, 2, 847 }, { 0xa5b, 2, 849 }, { 0xa5e, 2, 851 }, { 0xb48, 2, 853 }, { 0xb4b, 2, 855 }, { 0xb4c, 2, 857 }, { 0xb5c, 2, 859 }, { 0xb5d, 2, 861 }, { 0xb94, 2, 863 }, { 0xbca, 2, 865 }, { 0xbcb, 2, 867 }, { 0xbcc, 2, 869 }, { 0xc48, 2, 871 }, { 0xcc0, 2, 873 }, { 0xcc7, 2, 875 }, { 0xcc8, 2, 877 }, { 0xcca, 2, 879 }, { 0xccb, 3, 881 }, { 0xd4a, 2, 884 }, { 0xd4b, 2, 886 }, { 0xd4c, 2, 888 }, { 0xdda, 2, 890 }, { 0xddc, 2, 892 }, { 0xddd, 3, 894 }, { 0xdde, 2, 897 }, { 0xe33, 2, 899 }, { 0xeb3, 2, 901 }, { 0xedc, 2, 903 }, { 0xedd, 2, 905 }, { 0xf0c, 1, 907 }, { 0xf43, 2, 908 }, { 0xf4d, 2, 910 }, { 0xf52, 2, 912 }, { 0xf57, 2, 914 }, { 0xf5c, 2, 916 }, { 0xf69, 2, 918 }, { 0xf73, 2, 920 }, { 0xf75, 2, 922 }, { 0xf76, 2, 924 }, { 0xf77, 3, 926 }, { 0xf78, 2, 929 }, { 0xf79, 3, 931 }, { 0xf81, 2, 934 }, { 0xf93, 2, 936 }, { 0xf9d, 2, 938 }, { 0xfa2, 2, 940 }, { 0xfa7, 2, 942 }, { 0xfac, 2, 944 }, { 0xfb9, 2, 946 }, { 0x1026, 2, 948 }, { 0x10fc, 1, 950 }, { 0x1b06, 2, 951 }, { 0x1b08, 2, 953 }, { 0x1b0a, 2, 955 }, { 0x1b0c, 2, 957 }, { 0x1b0e, 2, 959 }, { 0x1b12, 2, 961 }, { 0x1b3b, 2, 963 }, { 0x1b3d, 2, 965 }, { 0x1b40, 2, 967 }, { 0x1b41, 2, 969 }, { 0x1b43, 2, 971 }, { 0x1d2c, 1, 973 }, { 0x1d2d, 1, 974 }, { 0x1d2e, 1, 975 }, { 0x1d30, 1, 976 }, { 0x1d31, 1, 977 }, { 0x1d32, 1, 978 }, { 0x1d33, 1, 979 }, { 0x1d34, 1, 980 }, { 0x1d35, 1, 981 }, { 0x1d36, 1, 982 }, { 0x1d37, 1, 983 }, { 0x1d38, 1, 984 }, { 0x1d39, 1, 985 }, { 0x1d3a, 1, 986 }, { 0x1d3c, 1, 987 }, { 0x1d3d, 1, 988 }, { 0x1d3e, 1, 989 }, { 0x1d3f, 1, 990 }, { 0x1d40, 1, 991 }, { 0x1d41, 1, 992 }, { 0x1d42, 1, 993 }, { 0x1d43, 1, 3 }, { 0x1d44, 1, 994 }, { 0x1d45, 1, 995 }, { 0x1d46, 1, 996 }, { 0x1d47, 1, 997 }, { 0x1d48, 1, 998 }, { 0x1d49, 1, 999 }, { 0x1d4a, 1, 1000 }, { 0x1d4b, 1, 1001 }, { 0x1d4c, 1, 1002 }, { 0x1d4d, 1, 1003 }, { 0x1d4f, 1, 1004 }, { 0x1d50, 1, 1005 }, { 0x1d51, 1, 1006 }, { 0x1d52, 1, 14 }, { 0x1d53, 1, 1007 }, { 0x1d54, 1, 1008 }, { 0x1d55, 1, 1009 }, { 0x1d56, 1, 1010 }, { 0x1d57, 1, 1011 }, { 0x1d58, 1, 1012 }, { 0x1d59, 1, 1013 }, { 0x1d5a, 1, 1014 }, { 0x1d5b, 1, 1015 }, { 0x1d5c, 1, 1016 }, { 0x1d5d, 1, 668 }, { 0x1d5e, 1, 1017 }, { 0x1d5f, 1, 1018 }, { 0x1d60, 1, 671 }, { 0x1d61, 1, 1019 }, { 0x1d62, 1, 1020 }, { 0x1d63, 1, 591 }, { 0x1d64, 1, 1012 }, { 0x1d65, 1, 1015 }, { 0x1d66, 1, 668 }, { 0x1d67, 1, 1017 }, { 0x1d68, 1, 674 }, { 0x1d69, 1, 671 }, { 0x1d6a, 1, 1019 }, { 0x1d78, 1, 1021 }, { 0x1d9b, 1, 1022 }, { 0x1d9c, 1, 1023 }, { 0x1d9d, 1, 1024 }, { 0x1d9e, 1, 1025 }, { 0x1d9f, 1, 1002 }, { 0x1da0, 1, 1026 }, { 0x1da1, 1, 1027 }, { 0x1da2, 1, 1028 }, { 0x1da3, 1, 1029 }, { 0x1da4, 1, 1030 }, { 0x1da5, 1, 1031 }, { 0x1da6, 1, 1032 }, { 0x1da7, 1, 1033 }, { 0x1da8, 1, 1034 }, { 0x1da9, 1, 1035 }, { 0x1daa, 1, 1036 }, { 0x1dab, 1, 1037 }, { 0x1dac, 1, 1038 }, { 0x1dad, 1, 1039 }, { 0x1dae, 1, 1040 }, { 0x1daf, 1, 1041 }, { 0x1db0, 1, 1042 }, { 0x1db1, 1, 1043 }, { 0x1db2, 1, 1044 }, { 0x1db3, 1, 1045 }, { 0x1db4, 1, 1046 }, { 0x1db5, 1, 1047 }, { 0x1db6, 1, 1048 }, { 0x1db7, 1, 1049 }, { 0x1db8, 1, 1050 }, { 0x1db9, 1, 1051 }, { 0x1dba, 1, 1052 }, { 0x1dbb, 1, 1053 }, { 0x1dbc, 1, 1054 }, { 0x1dbd, 1, 1055 }, { 0x1dbe, 1, 1056 }, { 0x1dbf, 1, 669 }, { 0x1e00, 2, 1057 }, { 0x1e01, 2, 1059 }, { 0x1e02, 2, 1061 }, { 0x1e03, 2, 1063 }, { 0x1e04, 2, 1065 }, { 0x1e05, 2, 1067 }, { 0x1e06, 2, 1069 }, { 0x1e07, 2, 1071 }, { 0x1e08, 3, 1073 }, { 0x1e09, 3, 1076 }, { 0x1e0a, 2, 1079 }, { 0x1e0b, 2, 1081 }, { 0x1e0c, 2, 1083 }, { 0x1e0d, 2, 1085 }, { 0x1e0e, 2, 1087 }, { 0x1e0f, 2, 1089 }, { 0x1e10, 2, 1091 }, { 0x1e11, 2, 1093 }, { 0x1e12, 2, 1095 }, { 0x1e13, 2, 1097 }, { 0x1e14, 3, 1099 }, { 0x1e15, 3, 1102 }, { 0x1e16, 3, 1105 }, { 0x1e17, 3, 1108 }, { 0x1e18, 2, 1111 }, { 0x1e19, 2, 1113 }, { 0x1e1a, 2, 1115 }, { 0x1e1b, 2, 1117 }, { 0x1e1c, 3, 1119 }, { 0x1e1d, 3, 1122 }, { 0x1e1e, 2, 1125 }, { 0x1e1f, 2, 1127 }, { 0x1e20, 2, 1129 }, { 0x1e21, 2, 1131 }, { 0x1e22, 2, 1133 }, { 0x1e23, 2, 1135 }, { 0x1e24, 2, 1137 }, { 0x1e25, 2, 1139 }, { 0x1e26, 2, 1141 }, { 0x1e27, 2, 1143 }, { 0x1e28, 2, 1145 }, { 0x1e29, 2, 1147 }, { 0x1e2a, 2, 1149 }, { 0x1e2b, 2, 1151 }, { 0x1e2c, 2, 1153 }, { 0x1e2d, 2, 1155 }, { 0x1e2e, 3, 1157 }, { 0x1e2f, 3, 1160 }, { 0x1e30, 2, 1163 }, { 0x1e31, 2, 1165 }, { 0x1e32, 2, 1167 }, { 0x1e33, 2, 1169 }, { 0x1e34, 2, 1171 }, { 0x1e35, 2, 1173 }, { 0x1e36, 2, 1175 }, { 0x1e37, 2, 1177 }, { 0x1e38, 3, 1179 }, { 0x1e39, 3, 1182 }, { 0x1e3a, 2, 1185 }, { 0x1e3b, 2, 1187 }, { 0x1e3c, 2, 1189 }, { 0x1e3d, 2, 1191 }, { 0x1e3e, 2, 1193 }, { 0x1e3f, 2, 1195 }, { 0x1e40, 2, 1197 }, { 0x1e41, 2, 1199 }, { 0x1e42, 2, 1201 }, { 0x1e43, 2, 1203 }, { 0x1e44, 2, 1205 }, { 0x1e45, 2, 1207 }, { 0x1e46, 2, 1209 }, { 0x1e47, 2, 1211 }, { 0x1e48, 2, 1213 }, { 0x1e49, 2, 1215 }, { 0x1e4a, 2, 1217 }, { 0x1e4b, 2, 1219 }, { 0x1e4c, 3, 1221 }, { 0x1e4d, 3, 1224 }, { 0x1e4e, 3, 1227 }, { 0x1e4f, 3, 1230 }, { 0x1e50, 3, 1233 }, { 0x1e51, 3, 1236 }, { 0x1e52, 3, 1239 }, { 0x1e53, 3, 1242 }, { 0x1e54, 2, 1245 }, { 0x1e55, 2, 1247 }, { 0x1e56, 2, 1249 }, { 0x1e57, 2, 1251 }, { 0x1e58, 2, 1253 }, { 0x1e59, 2, 1255 }, { 0x1e5a, 2, 1257 }, { 0x1e5b, 2, 1259 }, { 0x1e5c, 3, 1261 }, { 0x1e5d, 3, 1264 }, { 0x1e5e, 2, 1267 }, { 0x1e5f, 2, 1269 }, { 0x1e60, 2, 1271 }, { 0x1e61, 2, 1273 }, { 0x1e62, 2, 1275 }, { 0x1e63, 2, 1277 }, { 0x1e64, 3, 1279 }, { 0x1e65, 3, 1282 }, { 0x1e66, 3, 1285 }, { 0x1e67, 3, 1288 }, { 0x1e68, 3, 1291 }, { 0x1e69, 3, 1294 }, { 0x1e6a, 2, 1297 }, { 0x1e6b, 2, 1299 }, { 0x1e6c, 2, 1301 }, { 0x1e6d, 2, 1303 }, { 0x1e6e, 2, 1305 }, { 0x1e6f, 2, 1307 }, { 0x1e70, 2, 1309 }, { 0x1e71, 2, 1311 }, { 0x1e72, 2, 1313 }, { 0x1e73, 2, 1315 }, { 0x1e74, 2, 1317 }, { 0x1e75, 2, 1319 }, { 0x1e76, 2, 1321 }, { 0x1e77, 2, 1323 }, { 0x1e78, 3, 1325 }, { 0x1e79, 3, 1328 }, { 0x1e7a, 3, 1331 }, { 0x1e7b, 3, 1334 }, { 0x1e7c, 2, 1337 }, { 0x1e7d, 2, 1339 }, { 0x1e7e, 2, 1341 }, { 0x1e7f, 2, 1343 }, { 0x1e80, 2, 1345 }, { 0x1e81, 2, 1347 }, { 0x1e82, 2, 1349 }, { 0x1e83, 2, 1351 }, { 0x1e84, 2, 1353 }, { 0x1e85, 2, 1355 }, { 0x1e86, 2, 1357 }, { 0x1e87, 2, 1359 }, { 0x1e88, 2, 1361 }, { 0x1e89, 2, 1363 }, { 0x1e8a, 2, 1365 }, { 0x1e8b, 2, 1367 }, { 0x1e8c, 2, 1369 }, { 0x1e8d, 2, 1371 }, { 0x1e8e, 2, 1373 }, { 0x1e8f, 2, 1375 }, { 0x1e90, 2, 1377 }, { 0x1e91, 2, 1379 }, { 0x1e92, 2, 1381 }, { 0x1e93, 2, 1383 }, { 0x1e94, 2, 1385 }, { 0x1e95, 2, 1387 }, { 0x1e96, 2, 1389 }, { 0x1e97, 2, 1391 }, { 0x1e98, 2, 1393 }, { 0x1e99, 2, 1395 }, { 0x1e9a, 2, 1397 }, { 0x1e9b, 2, 1273 }, { 0x1ea0, 2, 1399 }, { 0x1ea1, 2, 1401 }, { 0x1ea2, 2, 1403 }, { 0x1ea3, 2, 1405 }, { 0x1ea4, 3, 1407 }, { 0x1ea5, 3, 1410 }, { 0x1ea6, 3, 1413 }, { 0x1ea7, 3, 1416 }, { 0x1ea8, 3, 1419 }, { 0x1ea9, 3, 1422 }, { 0x1eaa, 3, 1425 }, { 0x1eab, 3, 1428 }, { 0x1eac, 3, 1431 }, { 0x1ead, 3, 1434 }, { 0x1eae, 3, 1437 }, { 0x1eaf, 3, 1440 }, { 0x1eb0, 3, 1443 }, { 0x1eb1, 3, 1446 }, { 0x1eb2, 3, 1449 }, { 0x1eb3, 3, 1452 }, { 0x1eb4, 3, 1455 }, { 0x1eb5, 3, 1458 }, { 0x1eb6, 3, 1461 }, { 0x1eb7, 3, 1464 }, { 0x1eb8, 2, 1467 }, { 0x1eb9, 2, 1469 }, { 0x1eba, 2, 1471 }, { 0x1ebb, 2, 1473 }, { 0x1ebc, 2, 1475 }, { 0x1ebd, 2, 1477 }, { 0x1ebe, 3, 1479 }, { 0x1ebf, 3, 1482 }, { 0x1ec0, 3, 1485 }, { 0x1ec1, 3, 1488 }, { 0x1ec2, 3, 1491 }, { 0x1ec3, 3, 1494 }, { 0x1ec4, 3, 1497 }, { 0x1ec5, 3, 1500 }, { 0x1ec6, 3, 1503 }, { 0x1ec7, 3, 1506 }, { 0x1ec8, 2, 1509 }, { 0x1ec9, 2, 1511 }, { 0x1eca, 2, 1513 }, { 0x1ecb, 2, 1515 }, { 0x1ecc, 2, 1517 }, { 0x1ecd, 2, 1519 }, { 0x1ece, 2, 1521 }, { 0x1ecf, 2, 1523 }, { 0x1ed0, 3, 1525 }, { 0x1ed1, 3, 1528 }, { 0x1ed2, 3, 1531 }, { 0x1ed3, 3, 1534 }, { 0x1ed4, 3, 1537 }, { 0x1ed5, 3, 1540 }, { 0x1ed6, 3, 1543 }, { 0x1ed7, 3, 1546 }, { 0x1ed8, 3, 1549 }, { 0x1ed9, 3, 1552 }, { 0x1eda, 3, 1555 }, { 0x1edb, 3, 1558 }, { 0x1edc, 3, 1561 }, { 0x1edd, 3, 1564 }, { 0x1ede, 3, 1567 }, { 0x1edf, 3, 1570 }, { 0x1ee0, 3, 1573 }, { 0x1ee1, 3, 1576 }, { 0x1ee2, 3, 1579 }, { 0x1ee3, 3, 1582 }, { 0x1ee4, 2, 1585 }, { 0x1ee5, 2, 1587 }, { 0x1ee6, 2, 1589 }, { 0x1ee7, 2, 1591 }, { 0x1ee8, 3, 1593 }, { 0x1ee9, 3, 1596 }, { 0x1eea, 3, 1599 }, { 0x1eeb, 3, 1602 }, { 0x1eec, 3, 1605 }, { 0x1eed, 3, 1608 }, { 0x1eee, 3, 1611 }, { 0x1eef, 3, 1614 }, { 0x1ef0, 3, 1617 }, { 0x1ef1, 3, 1620 }, { 0x1ef2, 2, 1623 }, { 0x1ef3, 2, 1625 }, { 0x1ef4, 2, 1627 }, { 0x1ef5, 2, 1629 }, { 0x1ef6, 2, 1631 }, { 0x1ef7, 2, 1633 }, { 0x1ef8, 2, 1635 }, { 0x1ef9, 2, 1637 }, { 0x1f00, 2, 1639 }, { 0x1f01, 2, 1641 }, { 0x1f02, 3, 1643 }, { 0x1f03, 3, 1646 }, { 0x1f04, 3, 1649 }, { 0x1f05, 3, 1652 }, { 0x1f06, 3, 1655 }, { 0x1f07, 3, 1658 }, { 0x1f08, 2, 1661 }, { 0x1f09, 2, 1663 }, { 0x1f0a, 3, 1665 }, { 0x1f0b, 3, 1668 }, { 0x1f0c, 3, 1671 }, { 0x1f0d, 3, 1674 }, { 0x1f0e, 3, 1677 }, { 0x1f0f, 3, 1680 }, { 0x1f10, 2, 1683 }, { 0x1f11, 2, 1685 }, { 0x1f12, 3, 1687 }, { 0x1f13, 3, 1690 }, { 0x1f14, 3, 1693 }, { 0x1f15, 3, 1696 }, { 0x1f18, 2, 1699 }, { 0x1f19, 2, 1701 }, { 0x1f1a, 3, 1703 }, { 0x1f1b, 3, 1706 }, { 0x1f1c, 3, 1709 }, { 0x1f1d, 3, 1712 }, { 0x1f20, 2, 1715 }, { 0x1f21, 2, 1717 }, { 0x1f22, 3, 1719 }, { 0x1f23, 3, 1722 }, { 0x1f24, 3, 1725 }, { 0x1f25, 3, 1728 }, { 0x1f26, 3, 1731 }, { 0x1f27, 3, 1734 }, { 0x1f28, 2, 1737 }, { 0x1f29, 2, 1739 }, { 0x1f2a, 3, 1741 }, { 0x1f2b, 3, 1744 }, { 0x1f2c, 3, 1747 }, { 0x1f2d, 3, 1750 }, { 0x1f2e, 3, 1753 }, { 0x1f2f, 3, 1756 }, { 0x1f30, 2, 1759 }, { 0x1f31, 2, 1761 }, { 0x1f32, 3, 1763 }, { 0x1f33, 3, 1766 }, { 0x1f34, 3, 1769 }, { 0x1f35, 3, 1772 }, { 0x1f36, 3, 1775 }, { 0x1f37, 3, 1778 }, { 0x1f38, 2, 1781 }, { 0x1f39, 2, 1783 }, { 0x1f3a, 3, 1785 }, { 0x1f3b, 3, 1788 }, { 0x1f3c, 3, 1791 }, { 0x1f3d, 3, 1794 }, { 0x1f3e, 3, 1797 }, { 0x1f3f, 3, 1800 }, { 0x1f40, 2, 1803 }, { 0x1f41, 2, 1805 }, { 0x1f42, 3, 1807 }, { 0x1f43, 3, 1810 }, { 0x1f44, 3, 1813 }, { 0x1f45, 3, 1816 }, { 0x1f48, 2, 1819 }, { 0x1f49, 2, 1821 }, { 0x1f4a, 3, 1823 }, { 0x1f4b, 3, 1826 }, { 0x1f4c, 3, 1829 }, { 0x1f4d, 3, 1832 }, { 0x1f50, 2, 1835 }, { 0x1f51, 2, 1837 }, { 0x1f52, 3, 1839 }, { 0x1f53, 3, 1842 }, { 0x1f54, 3, 1845 }, { 0x1f55, 3, 1848 }, { 0x1f56, 3, 1851 }, { 0x1f57, 3, 1854 }, { 0x1f59, 2, 1857 }, { 0x1f5b, 3, 1859 }, { 0x1f5d, 3, 1862 }, { 0x1f5f, 3, 1865 }, { 0x1f60, 2, 1868 }, { 0x1f61, 2, 1870 }, { 0x1f62, 3, 1872 }, { 0x1f63, 3, 1875 }, { 0x1f64, 3, 1878 }, { 0x1f65, 3, 1881 }, { 0x1f66, 3, 1884 }, { 0x1f67, 3, 1887 }, { 0x1f68, 2, 1890 }, { 0x1f69, 2, 1892 }, { 0x1f6a, 3, 1894 }, { 0x1f6b, 3, 1897 }, { 0x1f6c, 3, 1900 }, { 0x1f6d, 3, 1903 }, { 0x1f6e, 3, 1906 }, { 0x1f6f, 3, 1909 }, { 0x1f70, 2, 1912 }, { 0x1f71, 2, 647 }, { 0x1f72, 2, 1914 }, { 0x1f73, 2, 649 }, { 0x1f74, 2, 1916 }, { 0x1f75, 2, 651 }, { 0x1f76, 2, 1918 }, { 0x1f77, 2, 653 }, { 0x1f78, 2, 1920 }, { 0x1f79, 2, 662 }, { 0x1f7a, 2, 1922 }, { 0x1f7b, 2, 664 }, { 0x1f7c, 2, 1924 }, { 0x1f7d, 2, 666 }, { 0x1f80, 3, 1926 }, { 0x1f81, 3, 1929 }, { 0x1f82, 4, 1932 }, { 0x1f83, 4, 1936 }, { 0x1f84, 4, 1940 }, { 0x1f85, 4, 1944 }, { 0x1f86, 4, 1948 }, { 0x1f87, 4, 1952 }, { 0x1f88, 3, 1956 }, { 0x1f89, 3, 1959 }, { 0x1f8a, 4, 1962 }, { 0x1f8b, 4, 1966 }, { 0x1f8c, 4, 1970 }, { 0x1f8d, 4, 1974 }, { 0x1f8e, 4, 1978 }, { 0x1f8f, 4, 1982 }, { 0x1f90, 3, 1986 }, { 0x1f91, 3, 1989 }, { 0x1f92, 4, 1992 }, { 0x1f93, 4, 1996 }, { 0x1f94, 4, 2000 }, { 0x1f95, 4, 2004 }, { 0x1f96, 4, 2008 }, { 0x1f97, 4, 2012 }, { 0x1f98, 3, 2016 }, { 0x1f99, 3, 2019 }, { 0x1f9a, 4, 2022 }, { 0x1f9b, 4, 2026 }, { 0x1f9c, 4, 2030 }, { 0x1f9d, 4, 2034 }, { 0x1f9e, 4, 2038 }, { 0x1f9f, 4, 2042 }, { 0x1fa0, 3, 2046 }, { 0x1fa1, 3, 2049 }, { 0x1fa2, 4, 2052 }, { 0x1fa3, 4, 2056 }, { 0x1fa4, 4, 2060 }, { 0x1fa5, 4, 2064 }, { 0x1fa6, 4, 2068 }, { 0x1fa7, 4, 2072 }, { 0x1fa8, 3, 2076 }, { 0x1fa9, 3, 2079 }, { 0x1faa, 4, 2082 }, { 0x1fab, 4, 2086 }, { 0x1fac, 4, 2090 }, { 0x1fad, 4, 2094 }, { 0x1fae, 4, 2098 }, { 0x1faf, 4, 2102 }, { 0x1fb0, 2, 2106 }, { 0x1fb1, 2, 2108 }, { 0x1fb2, 3, 2110 }, { 0x1fb3, 2, 2113 }, { 0x1fb4, 3, 2115 }, { 0x1fb6, 2, 2118 }, { 0x1fb7, 3, 2120 }, { 0x1fb8, 2, 2123 }, { 0x1fb9, 2, 2125 }, { 0x1fba, 2, 2127 }, { 0x1fbb, 2, 625 }, { 0x1fbc, 2, 2129 }, { 0x1fbd, 2, 2131 }, { 0x1fbe, 1, 2133 }, { 0x1fbf, 2, 2131 }, { 0x1fc0, 2, 2134 }, { 0x1fc1, 3, 2136 }, { 0x1fc2, 3, 2139 }, { 0x1fc3, 2, 2142 }, { 0x1fc4, 3, 2144 }, { 0x1fc6, 2, 2147 }, { 0x1fc7, 3, 2149 }, { 0x1fc8, 2, 2152 }, { 0x1fc9, 2, 628 }, { 0x1fca, 2, 2154 }, { 0x1fcb, 2, 630 }, { 0x1fcc, 2, 2156 }, { 0x1fcd, 3, 2158 }, { 0x1fce, 3, 2161 }, { 0x1fcf, 3, 2164 }, { 0x1fd0, 2, 2167 }, { 0x1fd1, 2, 2169 }, { 0x1fd2, 3, 2171 }, { 0x1fd3, 3, 640 }, { 0x1fd6, 2, 2174 }, { 0x1fd7, 3, 2176 }, { 0x1fd8, 2, 2179 }, { 0x1fd9, 2, 2181 }, { 0x1fda, 2, 2183 }, { 0x1fdb, 2, 632 }, { 0x1fdd, 3, 2185 }, { 0x1fde, 3, 2188 }, { 0x1fdf, 3, 2191 }, { 0x1fe0, 2, 2194 }, { 0x1fe1, 2, 2196 }, { 0x1fe2, 3, 2198 }, { 0x1fe3, 3, 655 }, { 0x1fe4, 2, 2201 }, { 0x1fe5, 2, 2203 }, { 0x1fe6, 2, 2205 }, { 0x1fe7, 3, 2207 }, { 0x1fe8, 2, 2210 }, { 0x1fe9, 2, 2212 }, { 0x1fea, 2, 2214 }, { 0x1feb, 2, 636 }, { 0x1fec, 2, 2216 }, { 0x1fed, 3, 2218 }, { 0x1fee, 3, 622 }, { 0x1fef, 1, 2221 }, { 0x1ff2, 3, 2222 }, { 0x1ff3, 2, 2225 }, { 0x1ff4, 3, 2227 }, { 0x1ff6, 2, 2230 }, { 0x1ff7, 3, 2232 }, { 0x1ff8, 2, 2235 }, { 0x1ff9, 2, 634 }, { 0x1ffa, 2, 2237 }, { 0x1ffb, 2, 638 }, { 0x1ffc, 2, 2239 }, { 0x1ffd, 2, 8 }, { 0x1ffe, 2, 2241 }, { 0x2000, 1, 0 }, { 0x2001, 1, 0 }, { 0x2002, 1, 0 }, { 0x2003, 1, 0 }, { 0x2004, 1, 0 }, { 0x2005, 1, 0 }, { 0x2006, 1, 0 }, { 0x2007, 1, 0 }, { 0x2008, 1, 0 }, { 0x2009, 1, 0 }, { 0x200a, 1, 0 }, { 0x2011, 1, 2243 }, { 0x2017, 2, 2244 }, { 0x2024, 1, 2246 }, { 0x2025, 2, 2247 }, { 0x2026, 3, 2249 }, { 0x202f, 1, 0 }, { 0x2033, 2, 2252 }, { 0x2034, 3, 2254 }, { 0x2036, 2, 2257 }, { 0x2037, 3, 2259 }, { 0x203c, 2, 2262 }, { 0x203e, 2, 2264 }, { 0x2047, 2, 2266 }, { 0x2048, 2, 2268 }, { 0x2049, 2, 2270 }, { 0x2057, 4, 2272 }, { 0x205f, 1, 0 }, { 0x2070, 1, 2276 }, { 0x2071, 1, 1020 }, { 0x2074, 1, 2277 }, { 0x2075, 1, 2278 }, { 0x2076, 1, 2279 }, { 0x2077, 1, 2280 }, { 0x2078, 1, 2281 }, { 0x2079, 1, 2282 }, { 0x207a, 1, 2283 }, { 0x207b, 1, 2284 }, { 0x207c, 1, 2285 }, { 0x207d, 1, 2286 }, { 0x207e, 1, 2287 }, { 0x207f, 1, 2288 }, { 0x2080, 1, 2276 }, { 0x2081, 1, 13 }, { 0x2082, 1, 6 }, { 0x2083, 1, 7 }, { 0x2084, 1, 2277 }, { 0x2085, 1, 2278 }, { 0x2086, 1, 2279 }, { 0x2087, 1, 2280 }, { 0x2088, 1, 2281 }, { 0x2089, 1, 2282 }, { 0x208a, 1, 2283 }, { 0x208b, 1, 2284 }, { 0x208c, 1, 2285 }, { 0x208d, 1, 2286 }, { 0x208e, 1, 2287 }, { 0x2090, 1, 3 }, { 0x2091, 1, 999 }, { 0x2092, 1, 14 }, { 0x2093, 1, 611 }, { 0x2094, 1, 1000 }, { 0x2095, 1, 588 }, { 0x2096, 1, 1004 }, { 0x2097, 1, 610 }, { 0x2098, 1, 1005 }, { 0x2099, 1, 2288 }, { 0x209a, 1, 1010 }, { 0x209b, 1, 356 }, { 0x209c, 1, 1011 }, { 0x20a8, 2, 2289 }, { 0x2100, 3, 2291 }, { 0x2101, 3, 2294 }, { 0x2102, 1, 2297 }, { 0x2103, 2, 2298 }, { 0x2105, 3, 2300 }, { 0x2106, 3, 2303 }, { 0x2107, 1, 2306 }, { 0x2109, 2, 2307 }, { 0x210a, 1, 1003 }, { 0x210b, 1, 980 }, { 0x210c, 1, 980 }, { 0x210d, 1, 980 }, { 0x210e, 1, 588 }, { 0x210f, 1, 2309 }, { 0x2110, 1, 981 }, { 0x2111, 1, 981 }, { 0x2112, 1, 984 }, { 0x2113, 1, 610 }, { 0x2115, 1, 986 }, { 0x2116, 2, 2310 }, { 0x2119, 1, 989 }, { 0x211a, 1, 2312 }, { 0x211b, 1, 990 }, { 0x211c, 1, 990 }, { 0x211d, 1, 990 }, { 0x2120, 2, 2313 }, { 0x2121, 3, 2315 }, { 0x2122, 2, 2318 }, { 0x2124, 1, 2320 }, { 0x2126, 1, 2321 }, { 0x2128, 1, 2320 }, { 0x212a, 1, 983 }, { 0x212b, 2, 34 }, { 0x212c, 1, 975 }, { 0x212d, 1, 2297 }, { 0x212f, 1, 999 }, { 0x2130, 1, 977 }, { 0x2131, 1, 2322 }, { 0x2133, 1, 985 }, { 0x2134, 1, 14 }, { 0x2135, 1, 2323 }, { 0x2136, 1, 2324 }, { 0x2137, 1, 2325 }, { 0x2138, 1, 2326 }, { 0x2139, 1, 1020 }, { 0x213b, 3, 2327 }, { 0x213c, 1, 672 }, { 0x213d, 1, 1017 }, { 0x213e, 1, 2330 }, { 0x213f, 1, 2331 }, { 0x2140, 1, 2332 }, { 0x2145, 1, 976 }, { 0x2146, 1, 998 }, { 0x2147, 1, 999 }, { 0x2148, 1, 1020 }, { 0x2149, 1, 590 }, { 0x2150, 3, 2333 }, { 0x2151, 3, 2336 }, { 0x2152, 4, 2339 }, { 0x2153, 3, 2343 }, { 0x2154, 3, 2346 }, { 0x2155, 3, 2349 }, { 0x2156, 3, 2352 }, { 0x2157, 3, 2355 }, { 0x2158, 3, 2358 }, { 0x2159, 3, 2361 }, { 0x215a, 3, 2364 }, { 0x215b, 3, 2367 }, { 0x215c, 3, 2370 }, { 0x215d, 3, 2373 }, { 0x215e, 3, 2376 }, { 0x215f, 2, 2379 }, { 0x2160, 1, 981 }, { 0x2161, 2, 2381 }, { 0x2162, 3, 2383 }, { 0x2163, 2, 2386 }, { 0x2164, 1, 2388 }, { 0x2165, 2, 2389 }, { 0x2166, 3, 2391 }, { 0x2167, 4, 2394 }, { 0x2168, 2, 2398 }, { 0x2169, 1, 2400 }, { 0x216a, 2, 2401 }, { 0x216b, 3, 2403 }, { 0x216c, 1, 984 }, { 0x216d, 1, 2297 }, { 0x216e, 1, 976 }, { 0x216f, 1, 985 }, { 0x2170, 1, 1020 }, { 0x2171, 2, 2406 }, { 0x2172, 3, 2408 }, { 0x2173, 2, 2411 }, { 0x2174, 1, 1015 }, { 0x2175, 2, 2413 }, { 0x2176, 3, 2415 }, { 0x2177, 4, 2418 }, { 0x2178, 2, 2422 }, { 0x2179, 1, 611 }, { 0x217a, 2, 2424 }, { 0x217b, 3, 2426 }, { 0x217c, 1, 610 }, { 0x217d, 1, 1023 }, { 0x217e, 1, 998 }, { 0x217f, 1, 1005 }, { 0x2189, 3, 2429 }, { 0x219a, 2, 2432 }, { 0x219b, 2, 2434 }, { 0x21ae, 2, 2436 }, { 0x21cd, 2, 2438 }, { 0x21ce, 2, 2440 }, { 0x21cf, 2, 2442 }, { 0x2204, 2, 2444 }, { 0x2209, 2, 2446 }, { 0x220c, 2, 2448 }, { 0x2224, 2, 2450 }, { 0x2226, 2, 2452 }, { 0x222c, 2, 2454 }, { 0x222d, 3, 2456 }, { 0x222f, 2, 2459 }, { 0x2230, 3, 2461 }, { 0x2241, 2, 2464 }, { 0x2244, 2, 2466 }, { 0x2247, 2, 2468 }, { 0x2249, 2, 2470 }, { 0x2260, 2, 2472 }, { 0x2262, 2, 2474 }, { 0x226d, 2, 2476 }, { 0x226e, 2, 2478 }, { 0x226f, 2, 2480 }, { 0x2270, 2, 2482 }, { 0x2271, 2, 2484 }, { 0x2274, 2, 2486 }, { 0x2275, 2, 2488 }, { 0x2278, 2, 2490 }, { 0x2279, 2, 2492 }, { 0x2280, 2, 2494 }, { 0x2281, 2, 2496 }, { 0x2284, 2, 2498 }, { 0x2285, 2, 2500 }, { 0x2288, 2, 2502 }, { 0x2289, 2, 2504 }, { 0x22ac, 2, 2506 }, { 0x22ad, 2, 2508 }, { 0x22ae, 2, 2510 }, { 0x22af, 2, 2512 }, { 0x22e0, 2, 2514 }, { 0x22e1, 2, 2516 }, { 0x22e2, 2, 2518 }, { 0x22e3, 2, 2520 }, { 0x22ea, 2, 2522 }, { 0x22eb, 2, 2524 }, { 0x22ec, 2, 2526 }, { 0x22ed, 2, 2528 }, { 0x2329, 1, 2530 }, { 0x232a, 1, 2531 }, { 0x2460, 1, 13 }, { 0x2461, 1, 6 }, { 0x2462, 1, 7 }, { 0x2463, 1, 2277 }, { 0x2464, 1, 2278 }, { 0x2465, 1, 2279 }, { 0x2466, 1, 2280 }, { 0x2467, 1, 2281 }, { 0x2468, 1, 2282 }, { 0x2469, 2, 2532 }, { 0x246a, 2, 2534 }, { 0x246b, 2, 2536 }, { 0x246c, 2, 2538 }, { 0x246d, 2, 2540 }, { 0x246e, 2, 2542 }, { 0x246f, 2, 2544 }, { 0x2470, 2, 2546 }, { 0x2471, 2, 2548 }, { 0x2472, 2, 2550 }, { 0x2473, 2, 2552 }, { 0x2474, 3, 2554 }, { 0x2475, 3, 2557 }, { 0x2476, 3, 2560 }, { 0x2477, 3, 2563 }, { 0x2478, 3, 2566 }, { 0x2479, 3, 2569 }, { 0x247a, 3, 2572 }, { 0x247b, 3, 2575 }, { 0x247c, 3, 2578 }, { 0x247d, 4, 2581 }, { 0x247e, 4, 2585 }, { 0x247f, 4, 2589 }, { 0x2480, 4, 2593 }, { 0x2481, 4, 2597 }, { 0x2482, 4, 2601 }, { 0x2483, 4, 2605 }, { 0x2484, 4, 2609 }, { 0x2485, 4, 2613 }, { 0x2486, 4, 2617 }, { 0x2487, 4, 2621 }, { 0x2488, 2, 2625 }, { 0x2489, 2, 2627 }, { 0x248a, 2, 2629 }, { 0x248b, 2, 2631 }, { 0x248c, 2, 2633 }, { 0x248d, 2, 2635 }, { 0x248e, 2, 2637 }, { 0x248f, 2, 2639 }, { 0x2490, 2, 2641 }, { 0x2491, 3, 2643 }, { 0x2492, 3, 2646 }, { 0x2493, 3, 2649 }, { 0x2494, 3, 2652 }, { 0x2495, 3, 2655 }, { 0x2496, 3, 2658 }, { 0x2497, 3, 2661 }, { 0x2498, 3, 2664 }, { 0x2499, 3, 2667 }, { 0x249a, 3, 2670 }, { 0x249b, 3, 2673 }, { 0x249c, 3, 2676 }, { 0x249d, 3, 2679 }, { 0x249e, 3, 2682 }, { 0x249f, 3, 2685 }, { 0x24a0, 3, 2688 }, { 0x24a1, 3, 2691 }, { 0x24a2, 3, 2694 }, { 0x24a3, 3, 2697 }, { 0x24a4, 3, 2700 }, { 0x24a5, 3, 2703 }, { 0x24a6, 3, 2706 }, { 0x24a7, 3, 2709 }, { 0x24a8, 3, 2712 }, { 0x24a9, 3, 2715 }, { 0x24aa, 3, 2718 }, { 0x24ab, 3, 2721 }, { 0x24ac, 3, 2724 }, { 0x24ad, 3, 2727 }, { 0x24ae, 3, 2730 }, { 0x24af, 3, 2733 }, { 0x24b0, 3, 2736 }, { 0x24b1, 3, 2739 }, { 0x24b2, 3, 2742 }, { 0x24b3, 3, 2745 }, { 0x24b4, 3, 2748 }, { 0x24b5, 3, 2751 }, { 0x24b6, 1, 973 }, { 0x24b7, 1, 975 }, { 0x24b8, 1, 2297 }, { 0x24b9, 1, 976 }, { 0x24ba, 1, 977 }, { 0x24bb, 1, 2322 }, { 0x24bc, 1, 979 }, { 0x24bd, 1, 980 }, { 0x24be, 1, 981 }, { 0x24bf, 1, 982 }, { 0x24c0, 1, 983 }, { 0x24c1, 1, 984 }, { 0x24c2, 1, 985 }, { 0x24c3, 1, 986 }, { 0x24c4, 1, 987 }, { 0x24c5, 1, 989 }, { 0x24c6, 1, 2312 }, { 0x24c7, 1, 990 }, { 0x24c8, 1, 2754 }, { 0x24c9, 1, 991 }, { 0x24ca, 1, 992 }, { 0x24cb, 1, 2388 }, { 0x24cc, 1, 993 }, { 0x24cd, 1, 2400 }, { 0x24ce, 1, 2755 }, { 0x24cf, 1, 2320 }, { 0x24d0, 1, 3 }, { 0x24d1, 1, 997 }, { 0x24d2, 1, 1023 }, { 0x24d3, 1, 998 }, { 0x24d4, 1, 999 }, { 0x24d5, 1, 1026 }, { 0x24d6, 1, 1003 }, { 0x24d7, 1, 588 }, { 0x24d8, 1, 1020 }, { 0x24d9, 1, 590 }, { 0x24da, 1, 1004 }, { 0x24db, 1, 610 }, { 0x24dc, 1, 1005 }, { 0x24dd, 1, 2288 }, { 0x24de, 1, 14 }, { 0x24df, 1, 1010 }, { 0x24e0, 1, 2756 }, { 0x24e1, 1, 591 }, { 0x24e2, 1, 356 }, { 0x24e3, 1, 1011 }, { 0x24e4, 1, 1012 }, { 0x24e5, 1, 1015 }, { 0x24e6, 1, 595 }, { 0x24e7, 1, 611 }, { 0x24e8, 1, 596 }, { 0x24e9, 1, 1053 }, { 0x24ea, 1, 2276 }, { 0x2a0c, 4, 2757 }, { 0x2a74, 3, 2761 }, { 0x2a75, 2, 2764 }, { 0x2a76, 3, 2766 }, { 0x2adc, 2, 2769 }, { 0x2c7c, 1, 590 }, { 0x2c7d, 1, 2388 }, { 0x2d6f, 1, 2771 }, { 0x2e9f, 1, 2772 }, { 0x2ef3, 1, 2773 }, { 0x2f00, 1, 2774 }, { 0x2f01, 1, 2775 }, { 0x2f02, 1, 2776 }, { 0x2f03, 1, 2777 }, { 0x2f04, 1, 2778 }, { 0x2f05, 1, 2779 }, { 0x2f06, 1, 2780 }, { 0x2f07, 1, 2781 }, { 0x2f08, 1, 2782 }, { 0x2f09, 1, 2783 }, { 0x2f0a, 1, 2784 }, { 0x2f0b, 1, 2785 }, { 0x2f0c, 1, 2786 }, { 0x2f0d, 1, 2787 }, { 0x2f0e, 1, 2788 }, { 0x2f0f, 1, 2789 }, { 0x2f10, 1, 2790 }, { 0x2f11, 1, 2791 }, { 0x2f12, 1, 2792 }, { 0x2f13, 1, 2793 }, { 0x2f14, 1, 2794 }, { 0x2f15, 1, 2795 }, { 0x2f16, 1, 2796 }, { 0x2f17, 1, 2797 }, { 0x2f18, 1, 2798 }, { 0x2f19, 1, 2799 }, { 0x2f1a, 1, 2800 }, { 0x2f1b, 1, 2801 }, { 0x2f1c, 1, 2802 }, { 0x2f1d, 1, 2803 }, { 0x2f1e, 1, 2804 }, { 0x2f1f, 1, 2805 }, { 0x2f20, 1, 2806 }, { 0x2f21, 1, 2807 }, { 0x2f22, 1, 2808 }, { 0x2f23, 1, 2809 }, { 0x2f24, 1, 2810 }, { 0x2f25, 1, 2811 }, { 0x2f26, 1, 2812 }, { 0x2f27, 1, 2813 }, { 0x2f28, 1, 2814 }, { 0x2f29, 1, 2815 }, { 0x2f2a, 1, 2816 }, { 0x2f2b, 1, 2817 }, { 0x2f2c, 1, 2818 }, { 0x2f2d, 1, 2819 }, { 0x2f2e, 1, 2820 }, { 0x2f2f, 1, 2821 }, { 0x2f30, 1, 2822 }, { 0x2f31, 1, 2823 }, { 0x2f32, 1, 2824 }, { 0x2f33, 1, 2825 }, { 0x2f34, 1, 2826 }, { 0x2f35, 1, 2827 }, { 0x2f36, 1, 2828 }, { 0x2f37, 1, 2829 }, { 0x2f38, 1, 2830 }, { 0x2f39, 1, 2831 }, { 0x2f3a, 1, 2832 }, { 0x2f3b, 1, 2833 }, { 0x2f3c, 1, 2834 }, { 0x2f3d, 1, 2835 }, { 0x2f3e, 1, 2836 }, { 0x2f3f, 1, 2837 }, { 0x2f40, 1, 2838 }, { 0x2f41, 1, 2839 }, { 0x2f42, 1, 2840 }, { 0x2f43, 1, 2841 }, { 0x2f44, 1, 2842 }, { 0x2f45, 1, 2843 }, { 0x2f46, 1, 2844 }, { 0x2f47, 1, 2845 }, { 0x2f48, 1, 2846 }, { 0x2f49, 1, 2847 }, { 0x2f4a, 1, 2848 }, { 0x2f4b, 1, 2849 }, { 0x2f4c, 1, 2850 }, { 0x2f4d, 1, 2851 }, { 0x2f4e, 1, 2852 }, { 0x2f4f, 1, 2853 }, { 0x2f50, 1, 2854 }, { 0x2f51, 1, 2855 }, { 0x2f52, 1, 2856 }, { 0x2f53, 1, 2857 }, { 0x2f54, 1, 2858 }, { 0x2f55, 1, 2859 }, { 0x2f56, 1, 2860 }, { 0x2f57, 1, 2861 }, { 0x2f58, 1, 2862 }, { 0x2f59, 1, 2863 }, { 0x2f5a, 1, 2864 }, { 0x2f5b, 1, 2865 }, { 0x2f5c, 1, 2866 }, { 0x2f5d, 1, 2867 }, { 0x2f5e, 1, 2868 }, { 0x2f5f, 1, 2869 }, { 0x2f60, 1, 2870 }, { 0x2f61, 1, 2871 }, { 0x2f62, 1, 2872 }, { 0x2f63, 1, 2873 }, { 0x2f64, 1, 2874 }, { 0x2f65, 1, 2875 }, { 0x2f66, 1, 2876 }, { 0x2f67, 1, 2877 }, { 0x2f68, 1, 2878 }, { 0x2f69, 1, 2879 }, { 0x2f6a, 1, 2880 }, { 0x2f6b, 1, 2881 }, { 0x2f6c, 1, 2882 }, { 0x2f6d, 1, 2883 }, { 0x2f6e, 1, 2884 }, { 0x2f6f, 1, 2885 }, { 0x2f70, 1, 2886 }, { 0x2f71, 1, 2887 }, { 0x2f72, 1, 2888 }, { 0x2f73, 1, 2889 }, { 0x2f74, 1, 2890 }, { 0x2f75, 1, 2891 }, { 0x2f76, 1, 2892 }, { 0x2f77, 1, 2893 }, { 0x2f78, 1, 2894 }, { 0x2f79, 1, 2895 }, { 0x2f7a, 1, 2896 }, { 0x2f7b, 1, 2897 }, { 0x2f7c, 1, 2898 }, { 0x2f7d, 1, 2899 }, { 0x2f7e, 1, 2900 }, { 0x2f7f, 1, 2901 }, { 0x2f80, 1, 2902 }, { 0x2f81, 1, 2903 }, { 0x2f82, 1, 2904 }, { 0x2f83, 1, 2905 }, { 0x2f84, 1, 2906 }, { 0x2f85, 1, 2907 }, { 0x2f86, 1, 2908 }, { 0x2f87, 1, 2909 }, { 0x2f88, 1, 2910 }, { 0x2f89, 1, 2911 }, { 0x2f8a, 1, 2912 }, { 0x2f8b, 1, 2913 }, { 0x2f8c, 1, 2914 }, { 0x2f8d, 1, 2915 }, { 0x2f8e, 1, 2916 }, { 0x2f8f, 1, 2917 }, { 0x2f90, 1, 2918 }, { 0x2f91, 1, 2919 }, { 0x2f92, 1, 2920 }, { 0x2f93, 1, 2921 }, { 0x2f94, 1, 2922 }, { 0x2f95, 1, 2923 }, { 0x2f96, 1, 2924 }, { 0x2f97, 1, 2925 }, { 0x2f98, 1, 2926 }, { 0x2f99, 1, 2927 }, { 0x2f9a, 1, 2928 }, { 0x2f9b, 1, 2929 }, { 0x2f9c, 1, 2930 }, { 0x2f9d, 1, 2931 }, { 0x2f9e, 1, 2932 }, { 0x2f9f, 1, 2933 }, { 0x2fa0, 1, 2934 }, { 0x2fa1, 1, 2935 }, { 0x2fa2, 1, 2936 }, { 0x2fa3, 1, 2937 }, { 0x2fa4, 1, 2938 }, { 0x2fa5, 1, 2939 }, { 0x2fa6, 1, 2940 }, { 0x2fa7, 1, 2941 }, { 0x2fa8, 1, 2942 }, { 0x2fa9, 1, 2943 }, { 0x2faa, 1, 2944 }, { 0x2fab, 1, 2945 }, { 0x2fac, 1, 2946 }, { 0x2fad, 1, 2947 }, { 0x2fae, 1, 2948 }, { 0x2faf, 1, 2949 }, { 0x2fb0, 1, 2950 }, { 0x2fb1, 1, 2951 }, { 0x2fb2, 1, 2952 }, { 0x2fb3, 1, 2953 }, { 0x2fb4, 1, 2954 }, { 0x2fb5, 1, 2955 }, { 0x2fb6, 1, 2956 }, { 0x2fb7, 1, 2957 }, { 0x2fb8, 1, 2958 }, { 0x2fb9, 1, 2959 }, { 0x2fba, 1, 2960 }, { 0x2fbb, 1, 2961 }, { 0x2fbc, 1, 2962 }, { 0x2fbd, 1, 2963 }, { 0x2fbe, 1, 2964 }, { 0x2fbf, 1, 2965 }, { 0x2fc0, 1, 2966 }, { 0x2fc1, 1, 2967 }, { 0x2fc2, 1, 2968 }, { 0x2fc3, 1, 2969 }, { 0x2fc4, 1, 2970 }, { 0x2fc5, 1, 2971 }, { 0x2fc6, 1, 2972 }, { 0x2fc7, 1, 2973 }, { 0x2fc8, 1, 2974 }, { 0x2fc9, 1, 2975 }, { 0x2fca, 1, 2976 }, { 0x2fcb, 1, 2977 }, { 0x2fcc, 1, 2978 }, { 0x2fcd, 1, 2979 }, { 0x2fce, 1, 2980 }, { 0x2fcf, 1, 2981 }, { 0x2fd0, 1, 2982 }, { 0x2fd1, 1, 2983 }, { 0x2fd2, 1, 2984 }, { 0x2fd3, 1, 2985 }, { 0x2fd4, 1, 2986 }, { 0x2fd5, 1, 2987 }, { 0x3000, 1, 0 }, { 0x3036, 1, 2988 }, { 0x3038, 1, 2797 }, { 0x3039, 1, 2989 }, { 0x303a, 1, 2990 }, { 0x304c, 2, 2991 }, { 0x304e, 2, 2993 }, { 0x3050, 2, 2995 }, { 0x3052, 2, 2997 }, { 0x3054, 2, 2999 }, { 0x3056, 2, 3001 }, { 0x3058, 2, 3003 }, { 0x305a, 2, 3005 }, { 0x305c, 2, 3007 }, { 0x305e, 2, 3009 }, { 0x3060, 2, 3011 }, { 0x3062, 2, 3013 }, { 0x3065, 2, 3015 }, { 0x3067, 2, 3017 }, { 0x3069, 2, 3019 }, { 0x3070, 2, 3021 }, { 0x3071, 2, 3023 }, { 0x3073, 2, 3025 }, { 0x3074, 2, 3027 }, { 0x3076, 2, 3029 }, { 0x3077, 2, 3031 }, { 0x3079, 2, 3033 }, { 0x307a, 2, 3035 }, { 0x307c, 2, 3037 }, { 0x307d, 2, 3039 }, { 0x3094, 2, 3041 }, { 0x309b, 2, 3043 }, { 0x309c, 2, 3045 }, { 0x309e, 2, 3047 }, { 0x309f, 2, 3049 }, { 0x30ac, 2, 3051 }, { 0x30ae, 2, 3053 }, { 0x30b0, 2, 3055 }, { 0x30b2, 2, 3057 }, { 0x30b4, 2, 3059 }, { 0x30b6, 2, 3061 }, { 0x30b8, 2, 3063 }, { 0x30ba, 2, 3065 }, { 0x30bc, 2, 3067 }, { 0x30be, 2, 3069 }, { 0x30c0, 2, 3071 }, { 0x30c2, 2, 3073 }, { 0x30c5, 2, 3075 }, { 0x30c7, 2, 3077 }, { 0x30c9, 2, 3079 }, { 0x30d0, 2, 3081 }, { 0x30d1, 2, 3083 }, { 0x30d3, 2, 3085 }, { 0x30d4, 2, 3087 }, { 0x30d6, 2, 3089 }, { 0x30d7, 2, 3091 }, { 0x30d9, 2, 3093 }, { 0x30da, 2, 3095 }, { 0x30dc, 2, 3097 }, { 0x30dd, 2, 3099 }, { 0x30f4, 2, 3101 }, { 0x30f7, 2, 3103 }, { 0x30f8, 2, 3105 }, { 0x30f9, 2, 3107 }, { 0x30fa, 2, 3109 }, { 0x30fe, 2, 3111 }, { 0x30ff, 2, 3113 }, { 0x3131, 1, 3115 }, { 0x3132, 1, 3116 }, { 0x3133, 1, 3117 }, { 0x3134, 1, 3118 }, { 0x3135, 1, 3119 }, { 0x3136, 1, 3120 }, { 0x3137, 1, 3121 }, { 0x3138, 1, 3122 }, { 0x3139, 1, 3123 }, { 0x313a, 1, 3124 }, { 0x313b, 1, 3125 }, { 0x313c, 1, 3126 }, { 0x313d, 1, 3127 }, { 0x313e, 1, 3128 }, { 0x313f, 1, 3129 }, { 0x3140, 1, 3130 }, { 0x3141, 1, 3131 }, { 0x3142, 1, 3132 }, { 0x3143, 1, 3133 }, { 0x3144, 1, 3134 }, { 0x3145, 1, 3135 }, { 0x3146, 1, 3136 }, { 0x3147, 1, 3137 }, { 0x3148, 1, 3138 }, { 0x3149, 1, 3139 }, { 0x314a, 1, 3140 }, { 0x314b, 1, 3141 }, { 0x314c, 1, 3142 }, { 0x314d, 1, 3143 }, { 0x314e, 1, 3144 }, { 0x314f, 1, 3145 }, { 0x3150, 1, 3146 }, { 0x3151, 1, 3147 }, { 0x3152, 1, 3148 }, { 0x3153, 1, 3149 }, { 0x3154, 1, 3150 }, { 0x3155, 1, 3151 }, { 0x3156, 1, 3152 }, { 0x3157, 1, 3153 }, { 0x3158, 1, 3154 }, { 0x3159, 1, 3155 }, { 0x315a, 1, 3156 }, { 0x315b, 1, 3157 }, { 0x315c, 1, 3158 }, { 0x315d, 1, 3159 }, { 0x315e, 1, 3160 }, { 0x315f, 1, 3161 }, { 0x3160, 1, 3162 }, { 0x3161, 1, 3163 }, { 0x3162, 1, 3164 }, { 0x3163, 1, 3165 }, { 0x3164, 1, 3166 }, { 0x3165, 1, 3167 }, { 0x3166, 1, 3168 }, { 0x3167, 1, 3169 }, { 0x3168, 1, 3170 }, { 0x3169, 1, 3171 }, { 0x316a, 1, 3172 }, { 0x316b, 1, 3173 }, { 0x316c, 1, 3174 }, { 0x316d, 1, 3175 }, { 0x316e, 1, 3176 }, { 0x316f, 1, 3177 }, { 0x3170, 1, 3178 }, { 0x3171, 1, 3179 }, { 0x3172, 1, 3180 }, { 0x3173, 1, 3181 }, { 0x3174, 1, 3182 }, { 0x3175, 1, 3183 }, { 0x3176, 1, 3184 }, { 0x3177, 1, 3185 }, { 0x3178, 1, 3186 }, { 0x3179, 1, 3187 }, { 0x317a, 1, 3188 }, { 0x317b, 1, 3189 }, { 0x317c, 1, 3190 }, { 0x317d, 1, 3191 }, { 0x317e, 1, 3192 }, { 0x317f, 1, 3193 }, { 0x3180, 1, 3194 }, { 0x3181, 1, 3195 }, { 0x3182, 1, 3196 }, { 0x3183, 1, 3197 }, { 0x3184, 1, 3198 }, { 0x3185, 1, 3199 }, { 0x3186, 1, 3200 }, { 0x3187, 1, 3201 }, { 0x3188, 1, 3202 }, { 0x3189, 1, 3203 }, { 0x318a, 1, 3204 }, { 0x318b, 1, 3205 }, { 0x318c, 1, 3206 }, { 0x318d, 1, 3207 }, { 0x318e, 1, 3208 }, { 0x3192, 1, 2774 }, { 0x3193, 1, 2780 }, { 0x3194, 1, 3209 }, { 0x3195, 1, 3210 }, { 0x3196, 1, 3211 }, { 0x3197, 1, 3212 }, { 0x3198, 1, 3213 }, { 0x3199, 1, 3214 }, { 0x319a, 1, 2778 }, { 0x319b, 1, 3215 }, { 0x319c, 1, 3216 }, { 0x319d, 1, 3217 }, { 0x319e, 1, 3218 }, { 0x319f, 1, 2782 }, { 0x3200, 3, 3219 }, { 0x3201, 3, 3222 }, { 0x3202, 3, 3225 }, { 0x3203, 3, 3228 }, { 0x3204, 3, 3231 }, { 0x3205, 3, 3234 }, { 0x3206, 3, 3237 }, { 0x3207, 3, 3240 }, { 0x3208, 3, 3243 }, { 0x3209, 3, 3246 }, { 0x320a, 3, 3249 }, { 0x320b, 3, 3252 }, { 0x320c, 3, 3255 }, { 0x320d, 3, 3258 }, { 0x320e, 4, 3261 }, { 0x320f, 4, 3265 }, { 0x3210, 4, 3269 }, { 0x3211, 4, 3273 }, { 0x3212, 4, 3277 }, { 0x3213, 4, 3281 }, { 0x3214, 4, 3285 }, { 0x3215, 4, 3289 }, { 0x3216, 4, 3293 }, { 0x3217, 4, 3297 }, { 0x3218, 4, 3301 }, { 0x3219, 4, 3305 }, { 0x321a, 4, 3309 }, { 0x321b, 4, 3313 }, { 0x321c, 4, 3317 }, { 0x321d, 7, 3321 }, { 0x321e, 6, 3328 }, { 0x3220, 3, 3334 }, { 0x3221, 3, 3337 }, { 0x3222, 3, 3340 }, { 0x3223, 3, 3343 }, { 0x3224, 3, 3346 }, { 0x3225, 3, 3349 }, { 0x3226, 3, 3352 }, { 0x3227, 3, 3355 }, { 0x3228, 3, 3358 }, { 0x3229, 3, 3361 }, { 0x322a, 3, 3364 }, { 0x322b, 3, 3367 }, { 0x322c, 3, 3370 }, { 0x322d, 3, 3373 }, { 0x322e, 3, 3376 }, { 0x322f, 3, 3379 }, { 0x3230, 3, 3382 }, { 0x3231, 3, 3385 }, { 0x3232, 3, 3388 }, { 0x3233, 3, 3391 }, { 0x3234, 3, 3394 }, { 0x3235, 3, 3397 }, { 0x3236, 3, 3400 }, { 0x3237, 3, 3403 }, { 0x3238, 3, 3406 }, { 0x3239, 3, 3409 }, { 0x323a, 3, 3412 }, { 0x323b, 3, 3415 }, { 0x323c, 3, 3418 }, { 0x323d, 3, 3421 }, { 0x323e, 3, 3424 }, { 0x323f, 3, 3427 }, { 0x3240, 3, 3430 }, { 0x3241, 3, 3433 }, { 0x3242, 3, 3436 }, { 0x3243, 3, 3439 }, { 0x3244, 1, 3442 }, { 0x3245, 1, 3443 }, { 0x3246, 1, 2840 }, { 0x3247, 1, 3444 }, { 0x3250, 3, 3445 }, { 0x3251, 2, 3448 }, { 0x3252, 2, 3450 }, { 0x3253, 2, 3452 }, { 0x3254, 2, 3454 }, { 0x3255, 2, 3456 }, { 0x3256, 2, 3458 }, { 0x3257, 2, 3460 }, { 0x3258, 2, 3462 }, { 0x3259, 2, 3464 }, { 0x325a, 2, 3466 }, { 0x325b, 2, 3468 }, { 0x325c, 2, 3470 }, { 0x325d, 2, 3472 }, { 0x325e, 2, 3474 }, { 0x325f, 2, 3476 }, { 0x3260, 1, 3115 }, { 0x3261, 1, 3118 }, { 0x3262, 1, 3121 }, { 0x3263, 1, 3123 }, { 0x3264, 1, 3131 }, { 0x3265, 1, 3132 }, { 0x3266, 1, 3135 }, { 0x3267, 1, 3137 }, { 0x3268, 1, 3138 }, { 0x3269, 1, 3140 }, { 0x326a, 1, 3141 }, { 0x326b, 1, 3142 }, { 0x326c, 1, 3143 }, { 0x326d, 1, 3144 }, { 0x326e, 2, 3478 }, { 0x326f, 2, 3480 }, { 0x3270, 2, 3482 }, { 0x3271, 2, 3484 }, { 0x3272, 2, 3486 }, { 0x3273, 2, 3488 }, { 0x3274, 2, 3490 }, { 0x3275, 2, 3492 }, { 0x3276, 2, 3494 }, { 0x3277, 2, 3496 }, { 0x3278, 2, 3498 }, { 0x3279, 2, 3500 }, { 0x327a, 2, 3502 }, { 0x327b, 2, 3504 }, { 0x327c, 5, 3506 }, { 0x327d, 4, 3511 }, { 0x327e, 2, 3515 }, { 0x3280, 1, 2774 }, { 0x3281, 1, 2780 }, { 0x3282, 1, 3209 }, { 0x3283, 1, 3210 }, { 0x3284, 1, 3517 }, { 0x3285, 1, 3518 }, { 0x3286, 1, 3519 }, { 0x3287, 1, 2785 }, { 0x3288, 1, 3520 }, { 0x3289, 1, 2797 }, { 0x328a, 1, 2847 }, { 0x328b, 1, 2859 }, { 0x328c, 1, 2858 }, { 0x328d, 1, 2848 }, { 0x328e, 1, 2940 }, { 0x328f, 1, 2805 }, { 0x3290, 1, 2845 }, { 0x3291, 1, 3521 }, { 0x3292, 1, 3522 }, { 0x3293, 1, 3523 }, { 0x3294, 1, 3524 }, { 0x3295, 1, 3525 }, { 0x3296, 1, 3526 }, { 0x3297, 1, 3527 }, { 0x3298, 1, 3528 }, { 0x3299, 1, 3529 }, { 0x329a, 1, 3530 }, { 0x329b, 1, 2811 }, { 0x329c, 1, 3531 }, { 0x329d, 1, 3532 }, { 0x329e, 1, 3533 }, { 0x329f, 1, 3534 }, { 0x32a0, 1, 3535 }, { 0x32a1, 1, 3536 }, { 0x32a2, 1, 3537 }, { 0x32a3, 1, 3538 }, { 0x32a4, 1, 3211 }, { 0x32a5, 1, 3212 }, { 0x32a6, 1, 3213 }, { 0x32a7, 1, 3539 }, { 0x32a8, 1, 3540 }, { 0x32a9, 1, 3541 }, { 0x32aa, 1, 3542 }, { 0x32ab, 1, 3543 }, { 0x32ac, 1, 3544 }, { 0x32ad, 1, 3545 }, { 0x32ae, 1, 3546 }, { 0x32af, 1, 3547 }, { 0x32b0, 1, 3548 }, { 0x32b1, 2, 3549 }, { 0x32b2, 2, 3551 }, { 0x32b3, 2, 3553 }, { 0x32b4, 2, 3555 }, { 0x32b5, 2, 3557 }, { 0x32b6, 2, 3559 }, { 0x32b7, 2, 3561 }, { 0x32b8, 2, 3563 }, { 0x32b9, 2, 3565 }, { 0x32ba, 2, 3567 }, { 0x32bb, 2, 3569 }, { 0x32bc, 2, 3571 }, { 0x32bd, 2, 3573 }, { 0x32be, 2, 3575 }, { 0x32bf, 2, 3577 }, { 0x32c0, 2, 3579 }, { 0x32c1, 2, 3581 }, { 0x32c2, 2, 3583 }, { 0x32c3, 2, 3585 }, { 0x32c4, 2, 3587 }, { 0x32c5, 2, 3589 }, { 0x32c6, 2, 3591 }, { 0x32c7, 2, 3593 }, { 0x32c8, 2, 3595 }, { 0x32c9, 3, 3597 }, { 0x32ca, 3, 3600 }, { 0x32cb, 3, 3603 }, { 0x32cc, 2, 3606 }, { 0x32cd, 3, 3608 }, { 0x32ce, 2, 3611 }, { 0x32cf, 3, 3613 }, { 0x32d0, 1, 3616 }, { 0x32d1, 1, 3617 }, { 0x32d2, 1, 3618 }, { 0x32d3, 1, 3619 }, { 0x32d4, 1, 3620 }, { 0x32d5, 1, 3621 }, { 0x32d6, 1, 3622 }, { 0x32d7, 1, 3623 }, { 0x32d8, 1, 3624 }, { 0x32d9, 1, 3625 }, { 0x32da, 1, 3626 }, { 0x32db, 1, 3627 }, { 0x32dc, 1, 3628 }, { 0x32dd, 1, 3629 }, { 0x32de, 1, 3630 }, { 0x32df, 1, 3631 }, { 0x32e0, 1, 3632 }, { 0x32e1, 1, 3633 }, { 0x32e2, 1, 3634 }, { 0x32e3, 1, 3635 }, { 0x32e4, 1, 3636 }, { 0x32e5, 1, 3637 }, { 0x32e6, 1, 3638 }, { 0x32e7, 1, 3639 }, { 0x32e8, 1, 3640 }, { 0x32e9, 1, 3641 }, { 0x32ea, 1, 3642 }, { 0x32eb, 1, 3643 }, { 0x32ec, 1, 3644 }, { 0x32ed, 1, 3645 }, { 0x32ee, 1, 3646 }, { 0x32ef, 1, 3647 }, { 0x32f0, 1, 3648 }, { 0x32f1, 1, 3649 }, { 0x32f2, 1, 3650 }, { 0x32f3, 1, 3651 }, { 0x32f4, 1, 3652 }, { 0x32f5, 1, 3653 }, { 0x32f6, 1, 3654 }, { 0x32f7, 1, 3655 }, { 0x32f8, 1, 3656 }, { 0x32f9, 1, 3657 }, { 0x32fa, 1, 3658 }, { 0x32fb, 1, 3659 }, { 0x32fc, 1, 3660 }, { 0x32fd, 1, 3661 }, { 0x32fe, 1, 3662 }, { 0x3300, 5, 3663 }, { 0x3301, 4, 3668 }, { 0x3302, 5, 3672 }, { 0x3303, 3, 3677 }, { 0x3304, 5, 3680 }, { 0x3305, 3, 3685 }, { 0x3306, 3, 3688 }, { 0x3307, 6, 3691 }, { 0x3308, 4, 3697 }, { 0x3309, 3, 3701 }, { 0x330a, 3, 3704 }, { 0x330b, 3, 3707 }, { 0x330c, 4, 3710 }, { 0x330d, 4, 3714 }, { 0x330e, 4, 3718 }, { 0x330f, 4, 3722 }, { 0x3310, 4, 3726 }, { 0x3311, 4, 3730 }, { 0x3312, 4, 3734 }, { 0x3313, 6, 3738 }, { 0x3314, 2, 3744 }, { 0x3315, 6, 3746 }, { 0x3316, 6, 3752 }, { 0x3317, 5, 3758 }, { 0x3318, 4, 3763 }, { 0x3319, 6, 3767 }, { 0x331a, 6, 3773 }, { 0x331b, 4, 3779 }, { 0x331c, 3, 3783 }, { 0x331d, 3, 3786 }, { 0x331e, 4, 3789 }, { 0x331f, 4, 3793 }, { 0x3320, 5, 3797 }, { 0x3321, 5, 3802 }, { 0x3322, 3, 3807 }, { 0x3323, 3, 3810 }, { 0x3324, 4, 3813 }, { 0x3325, 3, 3817 }, { 0x3326, 3, 3820 }, { 0x3327, 2, 3823 }, { 0x3328, 2, 3825 }, { 0x3329, 3, 3827 }, { 0x332a, 3, 3830 }, { 0x332b, 6, 3833 }, { 0x332c, 4, 3839 }, { 0x332d, 5, 3843 }, { 0x332e, 6, 3848 }, { 0x332f, 4, 3854 }, { 0x3330, 3, 3858 }, { 0x3331, 3, 3861 }, { 0x3332, 6, 3864 }, { 0x3333, 4, 3870 }, { 0x3334, 6, 3874 }, { 0x3335, 3, 3880 }, { 0x3336, 5, 3883 }, { 0x3337, 3, 3888 }, { 0x3338, 4, 3891 }, { 0x3339, 3, 3895 }, { 0x333a, 4, 3898 }, { 0x333b, 5, 3902 }, { 0x333c, 4, 3907 }, { 0x333d, 5, 3911 }, { 0x333e, 4, 3916 }, { 0x333f, 2, 3920 }, { 0x3340, 5, 3922 }, { 0x3341, 3, 3927 }, { 0x3342, 3, 3930 }, { 0x3343, 4, 3933 }, { 0x3344, 3, 3937 }, { 0x3345, 3, 3940 }, { 0x3346, 3, 3943 }, { 0x3347, 5, 3946 }, { 0x3348, 4, 3951 }, { 0x3349, 2, 3955 }, { 0x334a, 6, 3957 }, { 0x334b, 3, 3963 }, { 0x334c, 5, 3966 }, { 0x334d, 4, 3971 }, { 0x334e, 4, 3975 }, { 0x334f, 3, 3979 }, { 0x3350, 3, 3982 }, { 0x3351, 4, 3985 }, { 0x3352, 2, 3989 }, { 0x3353, 4, 3991 }, { 0x3354, 5, 3995 }, { 0x3355, 2, 4000 }, { 0x3356, 6, 4002 }, { 0x3357, 3, 4008 }, { 0x3358, 2, 4011 }, { 0x3359, 2, 4013 }, { 0x335a, 2, 4015 }, { 0x335b, 2, 4017 }, { 0x335c, 2, 4019 }, { 0x335d, 2, 4021 }, { 0x335e, 2, 4023 }, { 0x335f, 2, 4025 }, { 0x3360, 2, 4027 }, { 0x3361, 2, 4029 }, { 0x3362, 3, 4031 }, { 0x3363, 3, 4034 }, { 0x3364, 3, 4037 }, { 0x3365, 3, 4040 }, { 0x3366, 3, 4043 }, { 0x3367, 3, 4046 }, { 0x3368, 3, 4049 }, { 0x3369, 3, 4052 }, { 0x336a, 3, 4055 }, { 0x336b, 3, 4058 }, { 0x336c, 3, 4061 }, { 0x336d, 3, 4064 }, { 0x336e, 3, 4067 }, { 0x336f, 3, 4070 }, { 0x3370, 3, 4073 }, { 0x3371, 3, 4076 }, { 0x3372, 2, 4079 }, { 0x3373, 2, 4081 }, { 0x3374, 3, 4083 }, { 0x3375, 2, 4086 }, { 0x3376, 2, 4088 }, { 0x3377, 2, 4090 }, { 0x3378, 3, 4092 }, { 0x3379, 3, 4095 }, { 0x337a, 2, 4098 }, { 0x337b, 2, 4100 }, { 0x337c, 2, 4102 }, { 0x337d, 2, 4104 }, { 0x337e, 2, 4106 }, { 0x337f, 4, 4108 }, { 0x3380, 2, 4112 }, { 0x3381, 2, 4114 }, { 0x3382, 2, 4116 }, { 0x3383, 2, 4118 }, { 0x3384, 2, 4120 }, { 0x3385, 2, 4122 }, { 0x3386, 2, 4124 }, { 0x3387, 2, 4126 }, { 0x3388, 3, 4128 }, { 0x3389, 4, 4131 }, { 0x338a, 2, 4135 }, { 0x338b, 2, 4137 }, { 0x338c, 2, 4139 }, { 0x338d, 2, 4141 }, { 0x338e, 2, 4143 }, { 0x338f, 2, 4145 }, { 0x3390, 2, 4147 }, { 0x3391, 3, 4149 }, { 0x3392, 3, 4152 }, { 0x3393, 3, 4155 }, { 0x3394, 3, 4158 }, { 0x3395, 2, 4161 }, { 0x3396, 2, 4163 }, { 0x3397, 2, 4165 }, { 0x3398, 2, 4167 }, { 0x3399, 2, 4169 }, { 0x339a, 2, 4171 }, { 0x339b, 2, 4173 }, { 0x339c, 2, 4175 }, { 0x339d, 2, 4177 }, { 0x339e, 2, 4179 }, { 0x339f, 3, 4181 }, { 0x33a0, 3, 4184 }, { 0x33a1, 2, 4187 }, { 0x33a2, 3, 4189 }, { 0x33a3, 3, 4192 }, { 0x33a4, 3, 4195 }, { 0x33a5, 2, 4198 }, { 0x33a6, 3, 4200 }, { 0x33a7, 3, 4203 }, { 0x33a8, 4, 4206 }, { 0x33a9, 2, 4210 }, { 0x33aa, 3, 4212 }, { 0x33ab, 3, 4215 }, { 0x33ac, 3, 4218 }, { 0x33ad, 3, 4221 }, { 0x33ae, 5, 4224 }, { 0x33af, 6, 4229 }, { 0x33b0, 2, 4235 }, { 0x33b1, 2, 4237 }, { 0x33b2, 2, 4239 }, { 0x33b3, 2, 4241 }, { 0x33b4, 2, 4243 }, { 0x33b5, 2, 4245 }, { 0x33b6, 2, 4247 }, { 0x33b7, 2, 4249 }, { 0x33b8, 2, 4251 }, { 0x33b9, 2, 4253 }, { 0x33ba, 2, 4255 }, { 0x33bb, 2, 4257 }, { 0x33bc, 2, 4259 }, { 0x33bd, 2, 4261 }, { 0x33be, 2, 4263 }, { 0x33bf, 2, 4265 }, { 0x33c0, 2, 4267 }, { 0x33c1, 2, 4269 }, { 0x33c2, 4, 4271 }, { 0x33c3, 2, 4275 }, { 0x33c4, 2, 4277 }, { 0x33c5, 2, 4279 }, { 0x33c6, 4, 4281 }, { 0x33c7, 3, 4285 }, { 0x33c8, 2, 4288 }, { 0x33c9, 2, 4290 }, { 0x33ca, 2, 4292 }, { 0x33cb, 2, 4294 }, { 0x33cc, 2, 4296 }, { 0x33cd, 2, 4298 }, { 0x33ce, 2, 4300 }, { 0x33cf, 2, 4302 }, { 0x33d0, 2, 4304 }, { 0x33d1, 2, 4306 }, { 0x33d2, 3, 4308 }, { 0x33d3, 2, 4311 }, { 0x33d4, 2, 4313 }, { 0x33d5, 3, 4315 }, { 0x33d6, 3, 4318 }, { 0x33d7, 2, 4321 }, { 0x33d8, 4, 4323 }, { 0x33d9, 3, 4327 }, { 0x33da, 2, 4330 }, { 0x33db, 2, 4332 }, { 0x33dc, 2, 4334 }, { 0x33dd, 2, 4336 }, { 0x33de, 3, 4338 }, { 0x33df, 3, 4341 }, { 0x33e0, 2, 4344 }, { 0x33e1, 2, 4346 }, { 0x33e2, 2, 4348 }, { 0x33e3, 2, 4350 }, { 0x33e4, 2, 4352 }, { 0x33e5, 2, 4354 }, { 0x33e6, 2, 4356 }, { 0x33e7, 2, 4358 }, { 0x33e8, 2, 4360 }, { 0x33e9, 3, 4362 }, { 0x33ea, 3, 4365 }, { 0x33eb, 3, 4368 }, { 0x33ec, 3, 4371 }, { 0x33ed, 3, 4374 }, { 0x33ee, 3, 4377 }, { 0x33ef, 3, 4380 }, { 0x33f0, 3, 4383 }, { 0x33f1, 3, 4386 }, { 0x33f2, 3, 4389 }, { 0x33f3, 3, 4392 }, { 0x33f4, 3, 4395 }, { 0x33f5, 3, 4398 }, { 0x33f6, 3, 4401 }, { 0x33f7, 3, 4404 }, { 0x33f8, 3, 4407 }, { 0x33f9, 3, 4410 }, { 0x33fa, 3, 4413 }, { 0x33fb, 3, 4416 }, { 0x33fc, 3, 4419 }, { 0x33fd, 3, 4422 }, { 0x33fe, 3, 4425 }, { 0x33ff, 3, 4428 }, { 0xa69c, 1, 4431 }, { 0xa69d, 1, 4432 }, { 0xa770, 1, 4433 }, { 0xa7f8, 1, 4434 }, { 0xa7f9, 1, 4435 }, { 0xab5c, 1, 4436 }, { 0xab5d, 1, 4437 }, { 0xab5e, 1, 4438 }, { 0xab5f, 1, 4439 }, { 0xf900, 1, 4440 }, { 0xf901, 1, 4441 }, { 0xf902, 1, 2932 }, { 0xf903, 1, 4442 }, { 0xf904, 1, 4443 }, { 0xf905, 1, 4444 }, { 0xf906, 1, 4445 }, { 0xf907, 1, 2986 }, { 0xf908, 1, 2986 }, { 0xf909, 1, 4446 }, { 0xf90a, 1, 2940 }, { 0xf90b, 1, 4447 }, { 0xf90c, 1, 4448 }, { 0xf90d, 1, 4449 }, { 0xf90e, 1, 4450 }, { 0xf90f, 1, 4451 }, { 0xf910, 1, 4452 }, { 0xf911, 1, 4453 }, { 0xf912, 1, 4454 }, { 0xf913, 1, 4455 }, { 0xf914, 1, 4456 }, { 0xf915, 1, 4457 }, { 0xf916, 1, 4458 }, { 0xf917, 1, 4459 }, { 0xf918, 1, 4460 }, { 0xf919, 1, 4461 }, { 0xf91a, 1, 4462 }, { 0xf91b, 1, 4463 }, { 0xf91c, 1, 4464 }, { 0xf91d, 1, 4465 }, { 0xf91e, 1, 4466 }, { 0xf91f, 1, 4467 }, { 0xf920, 1, 4468 }, { 0xf921, 1, 4469 }, { 0xf922, 1, 4470 }, { 0xf923, 1, 4471 }, { 0xf924, 1, 4472 }, { 0xf925, 1, 4473 }, { 0xf926, 1, 4474 }, { 0xf927, 1, 4475 }, { 0xf928, 1, 4476 }, { 0xf929, 1, 4477 }, { 0xf92a, 1, 4478 }, { 0xf92b, 1, 4479 }, { 0xf92c, 1, 4480 }, { 0xf92d, 1, 4481 }, { 0xf92e, 1, 4482 }, { 0xf92f, 1, 4483 }, { 0xf930, 1, 4484 }, { 0xf931, 1, 4485 }, { 0xf932, 1, 4486 }, { 0xf933, 1, 4487 }, { 0xf934, 1, 2898 }, { 0xf935, 1, 4488 }, { 0xf936, 1, 4489 }, { 0xf937, 1, 4490 }, { 0xf938, 1, 4491 }, { 0xf939, 1, 4492 }, { 0xf93a, 1, 4493 }, { 0xf93b, 1, 4494 }, { 0xf93c, 1, 4495 }, { 0xf93d, 1, 4496 }, { 0xf93e, 1, 4497 }, { 0xf93f, 1, 4498 }, { 0xf940, 1, 2971 }, { 0xf941, 1, 4499 }, { 0xf942, 1, 4500 }, { 0xf943, 1, 4501 }, { 0xf944, 1, 4502 }, { 0xf945, 1, 4503 }, { 0xf946, 1, 4504 }, { 0xf947, 1, 4505 }, { 0xf948, 1, 4506 }, { 0xf949, 1, 4507 }, { 0xf94a, 1, 4508 }, { 0xf94b, 1, 4509 }, { 0xf94c, 1, 4510 }, { 0xf94d, 1, 4511 }, { 0xf94e, 1, 4512 }, { 0xf94f, 1, 4513 }, { 0xf950, 1, 4514 }, { 0xf951, 1, 4515 }, { 0xf952, 1, 4516 }, { 0xf953, 1, 4517 }, { 0xf954, 1, 4518 }, { 0xf955, 1, 4519 }, { 0xf956, 1, 4520 }, { 0xf957, 1, 4521 }, { 0xf958, 1, 4522 }, { 0xf959, 1, 4523 }, { 0xf95a, 1, 4524 }, { 0xf95b, 1, 4525 }, { 0xf95c, 1, 4456 }, { 0xf95d, 1, 4526 }, { 0xf95e, 1, 4527 }, { 0xf95f, 1, 4528 }, { 0xf960, 1, 4529 }, { 0xf961, 1, 4530 }, { 0xf962, 1, 4531 }, { 0xf963, 1, 4532 }, { 0xf964, 1, 4533 }, { 0xf965, 1, 4534 }, { 0xf966, 1, 4535 }, { 0xf967, 1, 4536 }, { 0xf968, 1, 4537 }, { 0xf969, 1, 4538 }, { 0xf96a, 1, 4539 }, { 0xf96b, 1, 4540 }, { 0xf96c, 1, 4541 }, { 0xf96d, 1, 4542 }, { 0xf96e, 1, 4543 }, { 0xf96f, 1, 4544 }, { 0xf970, 1, 4545 }, { 0xf971, 1, 2934 }, { 0xf972, 1, 4546 }, { 0xf973, 1, 4547 }, { 0xf974, 1, 4548 }, { 0xf975, 1, 4549 }, { 0xf976, 1, 4550 }, { 0xf977, 1, 4551 }, { 0xf978, 1, 4552 }, { 0xf979, 1, 4553 }, { 0xf97a, 1, 4554 }, { 0xf97b, 1, 4555 }, { 0xf97c, 1, 4556 }, { 0xf97d, 1, 4557 }, { 0xf97e, 1, 4558 }, { 0xf97f, 1, 4559 }, { 0xf980, 1, 4560 }, { 0xf981, 1, 2811 }, { 0xf982, 1, 4561 }, { 0xf983, 1, 4562 }, { 0xf984, 1, 4563 }, { 0xf985, 1, 4564 }, { 0xf986, 1, 4565 }, { 0xf987, 1, 4566 }, { 0xf988, 1, 4567 }, { 0xf989, 1, 4568 }, { 0xf98a, 1, 2792 }, { 0xf98b, 1, 4569 }, { 0xf98c, 1, 4570 }, { 0xf98d, 1, 4571 }, { 0xf98e, 1, 4572 }, { 0xf98f, 1, 4573 }, { 0xf990, 1, 4574 }, { 0xf991, 1, 4575 }, { 0xf992, 1, 4576 }, { 0xf993, 1, 4577 }, { 0xf994, 1, 4578 }, { 0xf995, 1, 4579 }, { 0xf996, 1, 4580 }, { 0xf997, 1, 4581 }, { 0xf998, 1, 4582 }, { 0xf999, 1, 4583 }, { 0xf99a, 1, 4584 }, { 0xf99b, 1, 4585 }, { 0xf99c, 1, 4586 }, { 0xf99d, 1, 4587 }, { 0xf99e, 1, 4588 }, { 0xf99f, 1, 4589 }, { 0xf9a0, 1, 4590 }, { 0xf9a1, 1, 4544 }, { 0xf9a2, 1, 4591 }, { 0xf9a3, 1, 4592 }, { 0xf9a4, 1, 4593 }, { 0xf9a5, 1, 4594 }, { 0xf9a6, 1, 4595 }, { 0xf9a7, 1, 4596 }, { 0xf9a8, 1, 4597 }, { 0xf9a9, 1, 4598 }, { 0xf9aa, 1, 4528 }, { 0xf9ab, 1, 4599 }, { 0xf9ac, 1, 4600 }, { 0xf9ad, 1, 4601 }, { 0xf9ae, 1, 4602 }, { 0xf9af, 1, 4603 }, { 0xf9b0, 1, 4604 }, { 0xf9b1, 1, 4605 }, { 0xf9b2, 1, 4606 }, { 0xf9b3, 1, 4607 }, { 0xf9b4, 1, 4608 }, { 0xf9b5, 1, 4609 }, { 0xf9b6, 1, 4610 }, { 0xf9b7, 1, 4611 }, { 0xf9b8, 1, 4612 }, { 0xf9b9, 1, 4613 }, { 0xf9ba, 1, 4614 }, { 0xf9bb, 1, 4615 }, { 0xf9bc, 1, 4616 }, { 0xf9bd, 1, 4617 }, { 0xf9be, 1, 4618 }, { 0xf9bf, 1, 4456 }, { 0xf9c0, 1, 4619 }, { 0xf9c1, 1, 4620 }, { 0xf9c2, 1, 4621 }, { 0xf9c3, 1, 4622 }, { 0xf9c4, 1, 2985 }, { 0xf9c5, 1, 4623 }, { 0xf9c6, 1, 4624 }, { 0xf9c7, 1, 4625 }, { 0xf9c8, 1, 4626 }, { 0xf9c9, 1, 4627 }, { 0xf9ca, 1, 4628 }, { 0xf9cb, 1, 4629 }, { 0xf9cc, 1, 4630 }, { 0xf9cd, 1, 4631 }, { 0xf9ce, 1, 4632 }, { 0xf9cf, 1, 4633 }, { 0xf9d0, 1, 4634 }, { 0xf9d1, 1, 3518 }, { 0xf9d2, 1, 4635 }, { 0xf9d3, 1, 4636 }, { 0xf9d4, 1, 4637 }, { 0xf9d5, 1, 4638 }, { 0xf9d6, 1, 4639 }, { 0xf9d7, 1, 4640 }, { 0xf9d8, 1, 4641 }, { 0xf9d9, 1, 4642 }, { 0xf9da, 1, 4643 }, { 0xf9db, 1, 4530 }, { 0xf9dc, 1, 4644 }, { 0xf9dd, 1, 4645 }, { 0xf9de, 1, 4646 }, { 0xf9df, 1, 4647 }, { 0xf9e0, 1, 4648 }, { 0xf9e1, 1, 4649 }, { 0xf9e2, 1, 4650 }, { 0xf9e3, 1, 4651 }, { 0xf9e4, 1, 4652 }, { 0xf9e5, 1, 4653 }, { 0xf9e6, 1, 4654 }, { 0xf9e7, 1, 4655 }, { 0xf9e8, 1, 4656 }, { 0xf9e9, 1, 2939 }, { 0xf9ea, 1, 4657 }, { 0xf9eb, 1, 4658 }, { 0xf9ec, 1, 4659 }, { 0xf9ed, 1, 4660 }, { 0xf9ee, 1, 4661 }, { 0xf9ef, 1, 4662 }, { 0xf9f0, 1, 4663 }, { 0xf9f1, 1, 4664 }, { 0xf9f2, 1, 4665 }, { 0xf9f3, 1, 4666 }, { 0xf9f4, 1, 4667 }, { 0xf9f5, 1, 4668 }, { 0xf9f6, 1, 4669 }, { 0xf9f7, 1, 2890 }, { 0xf9f8, 1, 4670 }, { 0xf9f9, 1, 4671 }, { 0xf9fa, 1, 4672 }, { 0xf9fb, 1, 4673 }, { 0xf9fc, 1, 4674 }, { 0xf9fd, 1, 4675 }, { 0xf9fe, 1, 4676 }, { 0xf9ff, 1, 4677 }, { 0xfa00, 1, 4678 }, { 0xfa01, 1, 4679 }, { 0xfa02, 1, 4680 }, { 0xfa03, 1, 4681 }, { 0xfa04, 1, 4682 }, { 0xfa05, 1, 4683 }, { 0xfa06, 1, 4684 }, { 0xfa07, 1, 4685 }, { 0xfa08, 1, 2917 }, { 0xfa09, 1, 4686 }, { 0xfa0a, 1, 2920 }, { 0xfa0b, 1, 4687 }, { 0xfa0c, 1, 4688 }, { 0xfa0d, 1, 4689 }, { 0xfa10, 1, 4690 }, { 0xfa12, 1, 4691 }, { 0xfa15, 1, 4692 }, { 0xfa16, 1, 4693 }, { 0xfa17, 1, 4694 }, { 0xfa18, 1, 4695 }, { 0xfa19, 1, 4696 }, { 0xfa1a, 1, 4697 }, { 0xfa1b, 1, 4698 }, { 0xfa1c, 1, 4699 }, { 0xfa1d, 1, 4700 }, { 0xfa1e, 1, 2897 }, { 0xfa20, 1, 4701 }, { 0xfa22, 1, 4702 }, { 0xfa25, 1, 4703 }, { 0xfa26, 1, 4704 }, { 0xfa2a, 1, 4705 }, { 0xfa2b, 1, 4706 }, { 0xfa2c, 1, 4707 }, { 0xfa2d, 1, 4708 }, { 0xfa2e, 1, 4709 }, { 0xfa2f, 1, 4710 }, { 0xfa30, 1, 4711 }, { 0xfa31, 1, 4712 }, { 0xfa32, 1, 4713 }, { 0xfa33, 1, 4714 }, { 0xfa34, 1, 4715 }, { 0xfa35, 1, 4716 }, { 0xfa36, 1, 4717 }, { 0xfa37, 1, 4718 }, { 0xfa38, 1, 4719 }, { 0xfa39, 1, 4720 }, { 0xfa3a, 1, 4721 }, { 0xfa3b, 1, 4722 }, { 0xfa3c, 1, 2818 }, { 0xfa3d, 1, 4723 }, { 0xfa3e, 1, 4724 }, { 0xfa3f, 1, 4725 }, { 0xfa40, 1, 4726 }, { 0xfa41, 1, 4727 }, { 0xfa42, 1, 4728 }, { 0xfa43, 1, 4729 }, { 0xfa44, 1, 4730 }, { 0xfa45, 1, 4731 }, { 0xfa46, 1, 4732 }, { 0xfa47, 1, 4733 }, { 0xfa48, 1, 4734 }, { 0xfa49, 1, 4735 }, { 0xfa4a, 1, 4736 }, { 0xfa4b, 1, 4737 }, { 0xfa4c, 1, 3523 }, { 0xfa4d, 1, 4738 }, { 0xfa4e, 1, 4739 }, { 0xfa4f, 1, 4740 }, { 0xfa50, 1, 4741 }, { 0xfa51, 1, 3527 }, { 0xfa52, 1, 4742 }, { 0xfa53, 1, 4743 }, { 0xfa54, 1, 4744 }, { 0xfa55, 1, 4745 }, { 0xfa56, 1, 4746 }, { 0xfa57, 1, 4580 }, { 0xfa58, 1, 4747 }, { 0xfa59, 1, 4748 }, { 0xfa5a, 1, 4749 }, { 0xfa5b, 1, 4750 }, { 0xfa5c, 1, 4751 }, { 0xfa5d, 1, 4752 }, { 0xfa5e, 1, 4752 }, { 0xfa5f, 1, 4753 }, { 0xfa60, 1, 4754 }, { 0xfa61, 1, 4755 }, { 0xfa62, 1, 4756 }, { 0xfa63, 1, 4757 }, { 0xfa64, 1, 4758 }, { 0xfa65, 1, 4759 }, { 0xfa66, 1, 4760 }, { 0xfa67, 1, 4703 }, { 0xfa68, 1, 4761 }, { 0xfa69, 1, 4762 }, { 0xfa6a, 1, 4763 }, { 0xfa6b, 1, 4764 }, { 0xfa6c, 1, 4765 }, { 0xfa6d, 1, 4766 }, { 0xfa70, 1, 4767 }, { 0xfa71, 1, 4768 }, { 0xfa72, 1, 4769 }, { 0xfa73, 1, 4770 }, { 0xfa74, 1, 4771 }, { 0xfa75, 1, 4772 }, { 0xfa76, 1, 4773 }, { 0xfa77, 1, 4774 }, { 0xfa78, 1, 4717 }, { 0xfa79, 1, 4775 }, { 0xfa7a, 1, 4776 }, { 0xfa7b, 1, 4777 }, { 0xfa7c, 1, 4690 }, { 0xfa7d, 1, 4778 }, { 0xfa7e, 1, 4779 }, { 0xfa7f, 1, 4780 }, { 0xfa80, 1, 4781 }, { 0xfa81, 1, 4782 }, { 0xfa82, 1, 4783 }, { 0xfa83, 1, 4784 }, { 0xfa84, 1, 4785 }, { 0xfa85, 1, 4786 }, { 0xfa86, 1, 4787 }, { 0xfa87, 1, 4788 }, { 0xfa88, 1, 4789 }, { 0xfa89, 1, 4725 }, { 0xfa8a, 1, 4790 }, { 0xfa8b, 1, 4726 }, { 0xfa8c, 1, 4791 }, { 0xfa8d, 1, 4792 }, { 0xfa8e, 1, 4793 }, { 0xfa8f, 1, 4794 }, { 0xfa90, 1, 4795 }, { 0xfa91, 1, 4691 }, { 0xfa92, 1, 4477 }, { 0xfa93, 1, 4796 }, { 0xfa94, 1, 4797 }, { 0xfa95, 1, 2851 }, { 0xfa96, 1, 4545 }, { 0xfa97, 1, 4628 }, { 0xfa98, 1, 4798 }, { 0xfa99, 1, 4799 }, { 0xfa9a, 1, 4733 }, { 0xfa9b, 1, 4800 }, { 0xfa9c, 1, 4734 }, { 0xfa9d, 1, 4801 }, { 0xfa9e, 1, 4802 }, { 0xfa9f, 1, 4803 }, { 0xfaa0, 1, 4693 }, { 0xfaa1, 1, 4804 }, { 0xfaa2, 1, 4805 }, { 0xfaa3, 1, 4806 }, { 0xfaa4, 1, 4807 }, { 0xfaa5, 1, 4808 }, { 0xfaa6, 1, 4694 }, { 0xfaa7, 1, 4809 }, { 0xfaa8, 1, 4810 }, { 0xfaa9, 1, 4811 }, { 0xfaaa, 1, 4812 }, { 0xfaab, 1, 4813 }, { 0xfaac, 1, 4814 }, { 0xfaad, 1, 4746 }, { 0xfaae, 1, 4815 }, { 0xfaaf, 1, 4816 }, { 0xfab0, 1, 4580 }, { 0xfab1, 1, 4817 }, { 0xfab2, 1, 4750 }, { 0xfab3, 1, 4818 }, { 0xfab4, 1, 4819 }, { 0xfab5, 1, 4820 }, { 0xfab6, 1, 4821 }, { 0xfab7, 1, 4822 }, { 0xfab8, 1, 4755 }, { 0xfab9, 1, 4823 }, { 0xfaba, 1, 4702 }, { 0xfabb, 1, 4824 }, { 0xfabc, 1, 4756 }, { 0xfabd, 1, 4526 }, { 0xfabe, 1, 4825 }, { 0xfabf, 1, 4757 }, { 0xfac0, 1, 4826 }, { 0xfac1, 1, 4759 }, { 0xfac2, 1, 4827 }, { 0xfac3, 1, 4828 }, { 0xfac4, 1, 4829 }, { 0xfac5, 1, 4830 }, { 0xfac6, 1, 4831 }, { 0xfac7, 1, 4761 }, { 0xfac8, 1, 4699 }, { 0xfac9, 1, 4832 }, { 0xfaca, 1, 4762 }, { 0xfacb, 1, 4833 }, { 0xfacc, 1, 4763 }, { 0xfacd, 1, 4834 }, { 0xface, 1, 2986 }, { 0xfacf, 1, 4835 }, { 0xfad0, 1, 4836 }, { 0xfad1, 1, 4837 }, { 0xfad2, 1, 4838 }, { 0xfad3, 1, 4839 }, { 0xfad4, 1, 4840 }, { 0xfad5, 1, 4841 }, { 0xfad6, 1, 4842 }, { 0xfad7, 1, 4843 }, { 0xfad8, 1, 4844 }, { 0xfad9, 1, 4845 }, { 0xfb00, 2, 4846 }, { 0xfb01, 2, 4848 }, { 0xfb02, 2, 4850 }, { 0xfb03, 3, 4852 }, { 0xfb04, 3, 4855 }, { 0xfb05, 2, 4858 }, { 0xfb06, 2, 4858 }, { 0xfb13, 2, 4860 }, { 0xfb14, 2, 4862 }, { 0xfb15, 2, 4864 }, { 0xfb16, 2, 4866 }, { 0xfb17, 2, 4868 }, { 0xfb1d, 2, 4870 }, { 0xfb1f, 2, 4872 }, { 0xfb20, 1, 4874 }, { 0xfb21, 1, 2323 }, { 0xfb22, 1, 2326 }, { 0xfb23, 1, 4875 }, { 0xfb24, 1, 4876 }, { 0xfb25, 1, 4877 }, { 0xfb26, 1, 4878 }, { 0xfb27, 1, 4879 }, { 0xfb28, 1, 4880 }, { 0xfb29, 1, 2283 }, { 0xfb2a, 2, 4881 }, { 0xfb2b, 2, 4883 }, { 0xfb2c, 3, 4885 }, { 0xfb2d, 3, 4888 }, { 0xfb2e, 2, 4891 }, { 0xfb2f, 2, 4893 }, { 0xfb30, 2, 4895 }, { 0xfb31, 2, 4897 }, { 0xfb32, 2, 4899 }, { 0xfb33, 2, 4901 }, { 0xfb34, 2, 4903 }, { 0xfb35, 2, 4905 }, { 0xfb36, 2, 4907 }, { 0xfb38, 2, 4909 }, { 0xfb39, 2, 4911 }, { 0xfb3a, 2, 4913 }, { 0xfb3b, 2, 4915 }, { 0xfb3c, 2, 4917 }, { 0xfb3e, 2, 4919 }, { 0xfb40, 2, 4921 }, { 0xfb41, 2, 4923 }, { 0xfb43, 2, 4925 }, { 0xfb44, 2, 4927 }, { 0xfb46, 2, 4929 }, { 0xfb47, 2, 4931 }, { 0xfb48, 2, 4933 }, { 0xfb49, 2, 4935 }, { 0xfb4a, 2, 4937 }, { 0xfb4b, 2, 4939 }, { 0xfb4c, 2, 4941 }, { 0xfb4d, 2, 4943 }, { 0xfb4e, 2, 4945 }, { 0xfb4f, 2, 4947 }, { 0xfb50, 1, 4949 }, { 0xfb51, 1, 4949 }, { 0xfb52, 1, 4950 }, { 0xfb53, 1, 4950 }, { 0xfb54, 1, 4950 }, { 0xfb55, 1, 4950 }, { 0xfb56, 1, 4951 }, { 0xfb57, 1, 4951 }, { 0xfb58, 1, 4951 }, { 0xfb59, 1, 4951 }, { 0xfb5a, 1, 4952 }, { 0xfb5b, 1, 4952 }, { 0xfb5c, 1, 4952 }, { 0xfb5d, 1, 4952 }, { 0xfb5e, 1, 4953 }, { 0xfb5f, 1, 4953 }, { 0xfb60, 1, 4953 }, { 0xfb61, 1, 4953 }, { 0xfb62, 1, 4954 }, { 0xfb63, 1, 4954 }, { 0xfb64, 1, 4954 }, { 0xfb65, 1, 4954 }, { 0xfb66, 1, 4955 }, { 0xfb67, 1, 4955 }, { 0xfb68, 1, 4955 }, { 0xfb69, 1, 4955 }, { 0xfb6a, 1, 4956 }, { 0xfb6b, 1, 4956 }, { 0xfb6c, 1, 4956 }, { 0xfb6d, 1, 4956 }, { 0xfb6e, 1, 4957 }, { 0xfb6f, 1, 4957 }, { 0xfb70, 1, 4957 }, { 0xfb71, 1, 4957 }, { 0xfb72, 1, 4958 }, { 0xfb73, 1, 4958 }, { 0xfb74, 1, 4958 }, { 0xfb75, 1, 4958 }, { 0xfb76, 1, 4959 }, { 0xfb77, 1, 4959 }, { 0xfb78, 1, 4959 }, { 0xfb79, 1, 4959 }, { 0xfb7a, 1, 4960 }, { 0xfb7b, 1, 4960 }, { 0xfb7c, 1, 4960 }, { 0xfb7d, 1, 4960 }, { 0xfb7e, 1, 4961 }, { 0xfb7f, 1, 4961 }, { 0xfb80, 1, 4961 }, { 0xfb81, 1, 4961 }, { 0xfb82, 1, 4962 }, { 0xfb83, 1, 4962 }, { 0xfb84, 1, 4963 }, { 0xfb85, 1, 4963 }, { 0xfb86, 1, 4964 }, { 0xfb87, 1, 4964 }, { 0xfb88, 1, 4965 }, { 0xfb89, 1, 4965 }, { 0xfb8a, 1, 4966 }, { 0xfb8b, 1, 4966 }, { 0xfb8c, 1, 4967 }, { 0xfb8d, 1, 4967 }, { 0xfb8e, 1, 4968 }, { 0xfb8f, 1, 4968 }, { 0xfb90, 1, 4968 }, { 0xfb91, 1, 4968 }, { 0xfb92, 1, 4969 }, { 0xfb93, 1, 4969 }, { 0xfb94, 1, 4969 }, { 0xfb95, 1, 4969 }, { 0xfb96, 1, 4970 }, { 0xfb97, 1, 4970 }, { 0xfb98, 1, 4970 }, { 0xfb99, 1, 4970 }, { 0xfb9a, 1, 4971 }, { 0xfb9b, 1, 4971 }, { 0xfb9c, 1, 4971 }, { 0xfb9d, 1, 4971 }, { 0xfb9e, 1, 4972 }, { 0xfb9f, 1, 4972 }, { 0xfba0, 1, 4973 }, { 0xfba1, 1, 4973 }, { 0xfba2, 1, 4973 }, { 0xfba3, 1, 4973 }, { 0xfba4, 2, 803 }, { 0xfba5, 2, 803 }, { 0xfba6, 1, 4974 }, { 0xfba7, 1, 4974 }, { 0xfba8, 1, 4974 }, { 0xfba9, 1, 4974 }, { 0xfbaa, 1, 4975 }, { 0xfbab, 1, 4975 }, { 0xfbac, 1, 4975 }, { 0xfbad, 1, 4975 }, { 0xfbae, 1, 4976 }, { 0xfbaf, 1, 4976 }, { 0xfbb0, 2, 807 }, { 0xfbb1, 2, 807 }, { 0xfbd3, 1, 4977 }, { 0xfbd4, 1, 4977 }, { 0xfbd5, 1, 4977 }, { 0xfbd6, 1, 4977 }, { 0xfbd7, 1, 4978 }, { 0xfbd8, 1, 4978 }, { 0xfbd9, 1, 4979 }, { 0xfbda, 1, 4979 }, { 0xfbdb, 1, 4980 }, { 0xfbdc, 1, 4980 }, { 0xfbdd, 2, 799 }, { 0xfbde, 1, 4981 }, { 0xfbdf, 1, 4981 }, { 0xfbe0, 1, 4982 }, { 0xfbe1, 1, 4982 }, { 0xfbe2, 1, 4983 }, { 0xfbe3, 1, 4983 }, { 0xfbe4, 1, 4984 }, { 0xfbe5, 1, 4984 }, { 0xfbe6, 1, 4984 }, { 0xfbe7, 1, 4984 }, { 0xfbe8, 1, 4985 }, { 0xfbe9, 1, 4985 }, { 0xfbea, 3, 4986 }, { 0xfbeb, 3, 4986 }, { 0xfbec, 3, 4989 }, { 0xfbed, 3, 4989 }, { 0xfbee, 3, 4992 }, { 0xfbef, 3, 4992 }, { 0xfbf0, 3, 4995 }, { 0xfbf1, 3, 4995 }, { 0xfbf2, 3, 4998 }, { 0xfbf3, 3, 4998 }, { 0xfbf4, 3, 5001 }, { 0xfbf5, 3, 5001 }, { 0xfbf6, 3, 5004 }, { 0xfbf7, 3, 5004 }, { 0xfbf8, 3, 5004 }, { 0xfbf9, 3, 5007 }, { 0xfbfa, 3, 5007 }, { 0xfbfb, 3, 5007 }, { 0xfbfc, 1, 5010 }, { 0xfbfd, 1, 5010 }, { 0xfbfe, 1, 5010 }, { 0xfbff, 1, 5010 }, { 0xfc00, 3, 5011 }, { 0xfc01, 3, 5014 }, { 0xfc02, 3, 5017 }, { 0xfc03, 3, 5007 }, { 0xfc04, 3, 5020 }, { 0xfc05, 2, 5023 }, { 0xfc06, 2, 5025 }, { 0xfc07, 2, 5027 }, { 0xfc08, 2, 5029 }, { 0xfc09, 2, 5031 }, { 0xfc0a, 2, 5033 }, { 0xfc0b, 2, 5035 }, { 0xfc0c, 2, 5037 }, { 0xfc0d, 2, 5039 }, { 0xfc0e, 2, 5041 }, { 0xfc0f, 2, 5043 }, { 0xfc10, 2, 5045 }, { 0xfc11, 2, 5047 }, { 0xfc12, 2, 5049 }, { 0xfc13, 2, 5051 }, { 0xfc14, 2, 5053 }, { 0xfc15, 2, 5055 }, { 0xfc16, 2, 5057 }, { 0xfc17, 2, 5059 }, { 0xfc18, 2, 5061 }, { 0xfc19, 2, 5063 }, { 0xfc1a, 2, 5065 }, { 0xfc1b, 2, 5067 }, { 0xfc1c, 2, 5069 }, { 0xfc1d, 2, 5071 }, { 0xfc1e, 2, 5073 }, { 0xfc1f, 2, 5075 }, { 0xfc20, 2, 5077 }, { 0xfc21, 2, 5079 }, { 0xfc22, 2, 5081 }, { 0xfc23, 2, 5083 }, { 0xfc24, 2, 5085 }, { 0xfc25, 2, 5087 }, { 0xfc26, 2, 5089 }, { 0xfc27, 2, 5091 }, { 0xfc28, 2, 5093 }, { 0xfc29, 2, 5095 }, { 0xfc2a, 2, 5097 }, { 0xfc2b, 2, 5099 }, { 0xfc2c, 2, 5101 }, { 0xfc2d, 2, 5103 }, { 0xfc2e, 2, 5105 }, { 0xfc2f, 2, 5107 }, { 0xfc30, 2, 5109 }, { 0xfc31, 2, 5111 }, { 0xfc32, 2, 5113 }, { 0xfc33, 2, 5115 }, { 0xfc34, 2, 5117 }, { 0xfc35, 2, 5119 }, { 0xfc36, 2, 5121 }, { 0xfc37, 2, 5123 }, { 0xfc38, 2, 5125 }, { 0xfc39, 2, 5127 }, { 0xfc3a, 2, 5129 }, { 0xfc3b, 2, 5131 }, { 0xfc3c, 2, 5133 }, { 0xfc3d, 2, 5135 }, { 0xfc3e, 2, 5137 }, { 0xfc3f, 2, 5139 }, { 0xfc40, 2, 5141 }, { 0xfc41, 2, 5143 }, { 0xfc42, 2, 5145 }, { 0xfc43, 2, 5147 }, { 0xfc44, 2, 5149 }, { 0xfc45, 2, 5151 }, { 0xfc46, 2, 5153 }, { 0xfc47, 2, 5155 }, { 0xfc48, 2, 5157 }, { 0xfc49, 2, 5159 }, { 0xfc4a, 2, 5161 }, { 0xfc4b, 2, 5163 }, { 0xfc4c, 2, 5165 }, { 0xfc4d, 2, 5167 }, { 0xfc4e, 2, 5169 }, { 0xfc4f, 2, 5171 }, { 0xfc50, 2, 5173 }, { 0xfc51, 2, 5175 }, { 0xfc52, 2, 5177 }, { 0xfc53, 2, 5179 }, { 0xfc54, 2, 5181 }, { 0xfc55, 2, 5183 }, { 0xfc56, 2, 5185 }, { 0xfc57, 2, 5187 }, { 0xfc58, 2, 5189 }, { 0xfc59, 2, 5191 }, { 0xfc5a, 2, 5193 }, { 0xfc5b, 2, 5195 }, { 0xfc5c, 2, 5197 }, { 0xfc5d, 2, 5199 }, { 0xfc5e, 3, 5201 }, { 0xfc5f, 3, 5204 }, { 0xfc60, 3, 5207 }, { 0xfc61, 3, 5210 }, { 0xfc62, 3, 5213 }, { 0xfc63, 3, 5216 }, { 0xfc64, 3, 5219 }, { 0xfc65, 3, 5222 }, { 0xfc66, 3, 5017 }, { 0xfc67, 3, 5225 }, { 0xfc68, 3, 5007 }, { 0xfc69, 3, 5020 }, { 0xfc6a, 2, 5228 }, { 0xfc6b, 2, 5230 }, { 0xfc6c, 2, 5029 }, { 0xfc6d, 2, 5232 }, { 0xfc6e, 2, 5031 }, { 0xfc6f, 2, 5033 }, { 0xfc70, 2, 5234 }, { 0xfc71, 2, 5236 }, { 0xfc72, 2, 5041 }, { 0xfc73, 2, 5238 }, { 0xfc74, 2, 5043 }, { 0xfc75, 2, 5045 }, { 0xfc76, 2, 5240 }, { 0xfc77, 2, 5242 }, { 0xfc78, 2, 5049 }, { 0xfc79, 2, 5244 }, { 0xfc7a, 2, 5051 }, { 0xfc7b, 2, 5053 }, { 0xfc7c, 2, 5111 }, { 0xfc7d, 2, 5113 }, { 0xfc7e, 2, 5119 }, { 0xfc7f, 2, 5121 }, { 0xfc80, 2, 5123 }, { 0xfc81, 2, 5131 }, { 0xfc82, 2, 5133 }, { 0xfc83, 2, 5135 }, { 0xfc84, 2, 5137 }, { 0xfc85, 2, 5145 }, { 0xfc86, 2, 5147 }, { 0xfc87, 2, 5149 }, { 0xfc88, 2, 5246 }, { 0xfc89, 2, 5157 }, { 0xfc8a, 2, 5248 }, { 0xfc8b, 2, 5250 }, { 0xfc8c, 2, 5169 }, { 0xfc8d, 2, 5252 }, { 0xfc8e, 2, 5171 }, { 0xfc8f, 2, 5173 }, { 0xfc90, 2, 5199 }, { 0xfc91, 2, 5254 }, { 0xfc92, 2, 5256 }, { 0xfc93, 2, 5189 }, { 0xfc94, 2, 5258 }, { 0xfc95, 2, 5191 }, { 0xfc96, 2, 5193 }, { 0xfc97, 3, 5011 }, { 0xfc98, 3, 5014 }, { 0xfc99, 3, 5260 }, { 0xfc9a, 3, 5017 }, { 0xfc9b, 3, 5263 }, { 0xfc9c, 2, 5023 }, { 0xfc9d, 2, 5025 }, { 0xfc9e, 2, 5027 }, { 0xfc9f, 2, 5029 }, { 0xfca0, 2, 5266 }, { 0xfca1, 2, 5035 }, { 0xfca2, 2, 5037 }, { 0xfca3, 2, 5039 }, { 0xfca4, 2, 5041 }, { 0xfca5, 2, 5268 }, { 0xfca6, 2, 5049 }, { 0xfca7, 2, 5055 }, { 0xfca8, 2, 5057 }, { 0xfca9, 2, 5059 }, { 0xfcaa, 2, 5061 }, { 0xfcab, 2, 5063 }, { 0xfcac, 2, 5067 }, { 0xfcad, 2, 5069 }, { 0xfcae, 2, 5071 }, { 0xfcaf, 2, 5073 }, { 0xfcb0, 2, 5075 }, { 0xfcb1, 2, 5077 }, { 0xfcb2, 2, 5270 }, { 0xfcb3, 2, 5079 }, { 0xfcb4, 2, 5081 }, { 0xfcb5, 2, 5083 }, { 0xfcb6, 2, 5085 }, { 0xfcb7, 2, 5087 }, { 0xfcb8, 2, 5089 }, { 0xfcb9, 2, 5093 }, { 0xfcba, 2, 5095 }, { 0xfcbb, 2, 5097 }, { 0xfcbc, 2, 5099 }, { 0xfcbd, 2, 5101 }, { 0xfcbe, 2, 5103 }, { 0xfcbf, 2, 5105 }, { 0xfcc0, 2, 5107 }, { 0xfcc1, 2, 5109 }, { 0xfcc2, 2, 5115 }, { 0xfcc3, 2, 5117 }, { 0xfcc4, 2, 5125 }, { 0xfcc5, 2, 5127 }, { 0xfcc6, 2, 5129 }, { 0xfcc7, 2, 5131 }, { 0xfcc8, 2, 5133 }, { 0xfcc9, 2, 5139 }, { 0xfcca, 2, 5141 }, { 0xfccb, 2, 5143 }, { 0xfccc, 2, 5145 }, { 0xfccd, 2, 5272 }, { 0xfcce, 2, 5151 }, { 0xfccf, 2, 5153 }, { 0xfcd0, 2, 5155 }, { 0xfcd1, 2, 5157 }, { 0xfcd2, 2, 5163 }, { 0xfcd3, 2, 5165 }, { 0xfcd4, 2, 5167 }, { 0xfcd5, 2, 5169 }, { 0xfcd6, 2, 5274 }, { 0xfcd7, 2, 5175 }, { 0xfcd8, 2, 5177 }, { 0xfcd9, 2, 5276 }, { 0xfcda, 2, 5183 }, { 0xfcdb, 2, 5185 }, { 0xfcdc, 2, 5187 }, { 0xfcdd, 2, 5189 }, { 0xfcde, 2, 5278 }, { 0xfcdf, 3, 5017 }, { 0xfce0, 3, 5263 }, { 0xfce1, 2, 5029 }, { 0xfce2, 2, 5266 }, { 0xfce3, 2, 5041 }, { 0xfce4, 2, 5268 }, { 0xfce5, 2, 5049 }, { 0xfce6, 2, 5280 }, { 0xfce7, 2, 5075 }, { 0xfce8, 2, 5282 }, { 0xfce9, 2, 5284 }, { 0xfcea, 2, 5286 }, { 0xfceb, 2, 5131 }, { 0xfcec, 2, 5133 }, { 0xfced, 2, 5145 }, { 0xfcee, 2, 5169 }, { 0xfcef, 2, 5274 }, { 0xfcf0, 2, 5189 }, { 0xfcf1, 2, 5278 }, { 0xfcf2, 3, 5288 }, { 0xfcf3, 3, 5291 }, { 0xfcf4, 3, 5294 }, { 0xfcf5, 2, 5297 }, { 0xfcf6, 2, 5299 }, { 0xfcf7, 2, 5301 }, { 0xfcf8, 2, 5303 }, { 0xfcf9, 2, 5305 }, { 0xfcfa, 2, 5307 }, { 0xfcfb, 2, 5309 }, { 0xfcfc, 2, 5311 }, { 0xfcfd, 2, 5313 }, { 0xfcfe, 2, 5315 }, { 0xfcff, 2, 5317 }, { 0xfd00, 2, 5319 }, { 0xfd01, 2, 5321 }, { 0xfd02, 2, 5323 }, { 0xfd03, 2, 5325 }, { 0xfd04, 2, 5327 }, { 0xfd05, 2, 5329 }, { 0xfd06, 2, 5331 }, { 0xfd07, 2, 5333 }, { 0xfd08, 2, 5335 }, { 0xfd09, 2, 5337 }, { 0xfd0a, 2, 5339 }, { 0xfd0b, 2, 5341 }, { 0xfd0c, 2, 5284 }, { 0xfd0d, 2, 5343 }, { 0xfd0e, 2, 5345 }, { 0xfd0f, 2, 5347 }, { 0xfd10, 2, 5349 }, { 0xfd11, 2, 5297 }, { 0xfd12, 2, 5299 }, { 0xfd13, 2, 5301 }, { 0xfd14, 2, 5303 }, { 0xfd15, 2, 5305 }, { 0xfd16, 2, 5307 }, { 0xfd17, 2, 5309 }, { 0xfd18, 2, 5311 }, { 0xfd19, 2, 5313 }, { 0xfd1a, 2, 5315 }, { 0xfd1b, 2, 5317 }, { 0xfd1c, 2, 5319 }, { 0xfd1d, 2, 5321 }, { 0xfd1e, 2, 5323 }, { 0xfd1f, 2, 5325 }, { 0xfd20, 2, 5327 }, { 0xfd21, 2, 5329 }, { 0xfd22, 2, 5331 }, { 0xfd23, 2, 5333 }, { 0xfd24, 2, 5335 }, { 0xfd25, 2, 5337 }, { 0xfd26, 2, 5339 }, { 0xfd27, 2, 5341 }, { 0xfd28, 2, 5284 }, { 0xfd29, 2, 5343 }, { 0xfd2a, 2, 5345 }, { 0xfd2b, 2, 5347 }, { 0xfd2c, 2, 5349 }, { 0xfd2d, 2, 5337 }, { 0xfd2e, 2, 5339 }, { 0xfd2f, 2, 5341 }, { 0xfd30, 2, 5284 }, { 0xfd31, 2, 5282 }, { 0xfd32, 2, 5286 }, { 0xfd33, 2, 5091 }, { 0xfd34, 2, 5069 }, { 0xfd35, 2, 5071 }, { 0xfd36, 2, 5073 }, { 0xfd37, 2, 5337 }, { 0xfd38, 2, 5339 }, { 0xfd39, 2, 5341 }, { 0xfd3a, 2, 5091 }, { 0xfd3b, 2, 5093 }, { 0xfd3c, 2, 5351 }, { 0xfd3d, 2, 5351 }, { 0xfd50, 3, 5353 }, { 0xfd51, 3, 5356 }, { 0xfd52, 3, 5356 }, { 0xfd53, 3, 5359 }, { 0xfd54, 3, 5362 }, { 0xfd55, 3, 5365 }, { 0xfd56, 3, 5368 }, { 0xfd57, 3, 5371 }, { 0xfd58, 3, 5374 }, { 0xfd59, 3, 5374 }, { 0xfd5a, 3, 5377 }, { 0xfd5b, 3, 5380 }, { 0xfd5c, 3, 5383 }, { 0xfd5d, 3, 5386 }, { 0xfd5e, 3, 5389 }, { 0xfd5f, 3, 5392 }, { 0xfd60, 3, 5392 }, { 0xfd61, 3, 5395 }, { 0xfd62, 3, 5398 }, { 0xfd63, 3, 5398 }, { 0xfd64, 3, 5401 }, { 0xfd65, 3, 5401 }, { 0xfd66, 3, 5404 }, { 0xfd67, 3, 5407 }, { 0xfd68, 3, 5407 }, { 0xfd69, 3, 5410 }, { 0xfd6a, 3, 5413 }, { 0xfd6b, 3, 5413 }, { 0xfd6c, 3, 5416 }, { 0xfd6d, 3, 5416 }, { 0xfd6e, 3, 5419 }, { 0xfd6f, 3, 5422 }, { 0xfd70, 3, 5422 }, { 0xfd71, 3, 5425 }, { 0xfd72, 3, 5425 }, { 0xfd73, 3, 5428 }, { 0xfd74, 3, 5431 }, { 0xfd75, 3, 5434 }, { 0xfd76, 3, 5437 }, { 0xfd77, 3, 5437 }, { 0xfd78, 3, 5440 }, { 0xfd79, 3, 5443 }, { 0xfd7a, 3, 5446 }, { 0xfd7b, 3, 5449 }, { 0xfd7c, 3, 5452 }, { 0xfd7d, 3, 5452 }, { 0xfd7e, 3, 5455 }, { 0xfd7f, 3, 5458 }, { 0xfd80, 3, 5461 }, { 0xfd81, 3, 5464 }, { 0xfd82, 3, 5467 }, { 0xfd83, 3, 5470 }, { 0xfd84, 3, 5470 }, { 0xfd85, 3, 5473 }, { 0xfd86, 3, 5473 }, { 0xfd87, 3, 5476 }, { 0xfd88, 3, 5476 }, { 0xfd89, 3, 5479 }, { 0xfd8a, 3, 5482 }, { 0xfd8b, 3, 5485 }, { 0xfd8c, 3, 5488 }, { 0xfd8d, 3, 5491 }, { 0xfd8e, 3, 5494 }, { 0xfd8f, 3, 5497 }, { 0xfd92, 3, 5500 }, { 0xfd93, 3, 5503 }, { 0xfd94, 3, 5506 }, { 0xfd95, 3, 5509 }, { 0xfd96, 3, 5512 }, { 0xfd97, 3, 5515 }, { 0xfd98, 3, 5515 }, { 0xfd99, 3, 5518 }, { 0xfd9a, 3, 5521 }, { 0xfd9b, 3, 5524 }, { 0xfd9c, 3, 5527 }, { 0xfd9d, 3, 5527 }, { 0xfd9e, 3, 5530 }, { 0xfd9f, 3, 5533 }, { 0xfda0, 3, 5536 }, { 0xfda1, 3, 5539 }, { 0xfda2, 3, 5542 }, { 0xfda3, 3, 5545 }, { 0xfda4, 3, 5548 }, { 0xfda5, 3, 5551 }, { 0xfda6, 3, 5554 }, { 0xfda7, 3, 5557 }, { 0xfda8, 3, 5560 }, { 0xfda9, 3, 5563 }, { 0xfdaa, 3, 5566 }, { 0xfdab, 3, 5569 }, { 0xfdac, 3, 5572 }, { 0xfdad, 3, 5575 }, { 0xfdae, 3, 5578 }, { 0xfdaf, 3, 5581 }, { 0xfdb0, 3, 5584 }, { 0xfdb1, 3, 5587 }, { 0xfdb2, 3, 5590 }, { 0xfdb3, 3, 5593 }, { 0xfdb4, 3, 5455 }, { 0xfdb5, 3, 5461 }, { 0xfdb6, 3, 5596 }, { 0xfdb7, 3, 5599 }, { 0xfdb8, 3, 5602 }, { 0xfdb9, 3, 5605 }, { 0xfdba, 3, 5608 }, { 0xfdbb, 3, 5611 }, { 0xfdbc, 3, 5608 }, { 0xfdbd, 3, 5602 }, { 0xfdbe, 3, 5614 }, { 0xfdbf, 3, 5617 }, { 0xfdc0, 3, 5620 }, { 0xfdc1, 3, 5623 }, { 0xfdc2, 3, 5626 }, { 0xfdc3, 3, 5611 }, { 0xfdc4, 3, 5434 }, { 0xfdc5, 3, 5404 }, { 0xfdc6, 3, 5629 }, { 0xfdc7, 3, 5632 }, { 0xfdf0, 3, 5635 }, { 0xfdf1, 3, 5638 }, { 0xfdf2, 4, 5641 }, { 0xfdf3, 4, 5645 }, { 0xfdf4, 4, 5649 }, { 0xfdf5, 4, 5653 }, { 0xfdf6, 4, 5657 }, { 0xfdf7, 4, 5661 }, { 0xfdf8, 4, 5665 }, { 0xfdf9, 3, 5669 }, { 0xfdfa, 18, 5672 }, { 0xfdfb, 8, 5690 }, { 0xfdfc, 4, 5698 }, { 0xfe10, 1, 5702 }, { 0xfe11, 1, 5703 }, { 0xfe12, 1, 5704 }, { 0xfe13, 1, 5705 }, { 0xfe14, 1, 621 }, { 0xfe15, 1, 5706 }, { 0xfe16, 1, 5707 }, { 0xfe17, 1, 5708 }, { 0xfe18, 1, 5709 }, { 0xfe19, 3, 2249 }, { 0xfe30, 2, 2247 }, { 0xfe31, 1, 5710 }, { 0xfe32, 1, 5711 }, { 0xfe33, 1, 5712 }, { 0xfe34, 1, 5712 }, { 0xfe35, 1, 2286 }, { 0xfe36, 1, 2287 }, { 0xfe37, 1, 5713 }, { 0xfe38, 1, 5714 }, { 0xfe39, 1, 5715 }, { 0xfe3a, 1, 5716 }, { 0xfe3b, 1, 5717 }, { 0xfe3c, 1, 5718 }, { 0xfe3d, 1, 5719 }, { 0xfe3e, 1, 5720 }, { 0xfe3f, 1, 2530 }, { 0xfe40, 1, 2531 }, { 0xfe41, 1, 5721 }, { 0xfe42, 1, 5722 }, { 0xfe43, 1, 5723 }, { 0xfe44, 1, 5724 }, { 0xfe47, 1, 5725 }, { 0xfe48, 1, 5726 }, { 0xfe49, 2, 2264 }, { 0xfe4a, 2, 2264 }, { 0xfe4b, 2, 2264 }, { 0xfe4c, 2, 2264 }, { 0xfe4d, 1, 5712 }, { 0xfe4e, 1, 5712 }, { 0xfe4f, 1, 5712 }, { 0xfe50, 1, 5702 }, { 0xfe51, 1, 5703 }, { 0xfe52, 1, 2246 }, { 0xfe54, 1, 621 }, { 0xfe55, 1, 5705 }, { 0xfe56, 1, 5707 }, { 0xfe57, 1, 5706 }, { 0xfe58, 1, 5710 }, { 0xfe59, 1, 2286 }, { 0xfe5a, 1, 2287 }, { 0xfe5b, 1, 5713 }, { 0xfe5c, 1, 5714 }, { 0xfe5d, 1, 5715 }, { 0xfe5e, 1, 5716 }, { 0xfe5f, 1, 5727 }, { 0xfe60, 1, 5728 }, { 0xfe61, 1, 5729 }, { 0xfe62, 1, 2283 }, { 0xfe63, 1, 5730 }, { 0xfe64, 1, 5731 }, { 0xfe65, 1, 5732 }, { 0xfe66, 1, 2285 }, { 0xfe68, 1, 5733 }, { 0xfe69, 1, 5734 }, { 0xfe6a, 1, 5735 }, { 0xfe6b, 1, 5736 }, { 0xfe70, 2, 5737 }, { 0xfe71, 2, 5739 }, { 0xfe72, 2, 5741 }, { 0xfe74, 2, 5743 }, { 0xfe76, 2, 5745 }, { 0xfe77, 2, 5747 }, { 0xfe78, 2, 5749 }, { 0xfe79, 2, 5751 }, { 0xfe7a, 2, 5753 }, { 0xfe7b, 2, 5755 }, { 0xfe7c, 2, 5757 }, { 0xfe7d, 2, 5759 }, { 0xfe7e, 2, 5761 }, { 0xfe7f, 2, 5763 }, { 0xfe80, 1, 5765 }, { 0xfe81, 2, 785 }, { 0xfe82, 2, 785 }, { 0xfe83, 2, 787 }, { 0xfe84, 2, 787 }, { 0xfe85, 2, 789 }, { 0xfe86, 2, 789 }, { 0xfe87, 2, 791 }, { 0xfe88, 2, 791 }, { 0xfe89, 2, 793 }, { 0xfe8a, 2, 793 }, { 0xfe8b, 2, 793 }, { 0xfe8c, 2, 793 }, { 0xfe8d, 1, 5766 }, { 0xfe8e, 1, 5766 }, { 0xfe8f, 1, 5767 }, { 0xfe90, 1, 5767 }, { 0xfe91, 1, 5767 }, { 0xfe92, 1, 5767 }, { 0xfe93, 1, 5768 }, { 0xfe94, 1, 5768 }, { 0xfe95, 1, 5769 }, { 0xfe96, 1, 5769 }, { 0xfe97, 1, 5769 }, { 0xfe98, 1, 5769 }, { 0xfe99, 1, 5770 }, { 0xfe9a, 1, 5770 }, { 0xfe9b, 1, 5770 }, { 0xfe9c, 1, 5770 }, { 0xfe9d, 1, 5771 }, { 0xfe9e, 1, 5771 }, { 0xfe9f, 1, 5771 }, { 0xfea0, 1, 5771 }, { 0xfea1, 1, 5772 }, { 0xfea2, 1, 5772 }, { 0xfea3, 1, 5772 }, { 0xfea4, 1, 5772 }, { 0xfea5, 1, 5773 }, { 0xfea6, 1, 5773 }, { 0xfea7, 1, 5773 }, { 0xfea8, 1, 5773 }, { 0xfea9, 1, 5774 }, { 0xfeaa, 1, 5774 }, { 0xfeab, 1, 5775 }, { 0xfeac, 1, 5775 }, { 0xfead, 1, 5776 }, { 0xfeae, 1, 5776 }, { 0xfeaf, 1, 5777 }, { 0xfeb0, 1, 5777 }, { 0xfeb1, 1, 5778 }, { 0xfeb2, 1, 5778 }, { 0xfeb3, 1, 5778 }, { 0xfeb4, 1, 5778 }, { 0xfeb5, 1, 5779 }, { 0xfeb6, 1, 5779 }, { 0xfeb7, 1, 5779 }, { 0xfeb8, 1, 5779 }, { 0xfeb9, 1, 5780 }, { 0xfeba, 1, 5780 }, { 0xfebb, 1, 5780 }, { 0xfebc, 1, 5780 }, { 0xfebd, 1, 5781 }, { 0xfebe, 1, 5781 }, { 0xfebf, 1, 5781 }, { 0xfec0, 1, 5781 }, { 0xfec1, 1, 5782 }, { 0xfec2, 1, 5782 }, { 0xfec3, 1, 5782 }, { 0xfec4, 1, 5782 }, { 0xfec5, 1, 5783 }, { 0xfec6, 1, 5783 }, { 0xfec7, 1, 5783 }, { 0xfec8, 1, 5783 }, { 0xfec9, 1, 5784 }, { 0xfeca, 1, 5784 }, { 0xfecb, 1, 5784 }, { 0xfecc, 1, 5784 }, { 0xfecd, 1, 5785 }, { 0xfece, 1, 5785 }, { 0xfecf, 1, 5785 }, { 0xfed0, 1, 5785 }, { 0xfed1, 1, 5786 }, { 0xfed2, 1, 5786 }, { 0xfed3, 1, 5786 }, { 0xfed4, 1, 5786 }, { 0xfed5, 1, 5787 }, { 0xfed6, 1, 5787 }, { 0xfed7, 1, 5787 }, { 0xfed8, 1, 5787 }, { 0xfed9, 1, 5788 }, { 0xfeda, 1, 5788 }, { 0xfedb, 1, 5788 }, { 0xfedc, 1, 5788 }, { 0xfedd, 1, 5789 }, { 0xfede, 1, 5789 }, { 0xfedf, 1, 5789 }, { 0xfee0, 1, 5789 }, { 0xfee1, 1, 5790 }, { 0xfee2, 1, 5790 }, { 0xfee3, 1, 5790 }, { 0xfee4, 1, 5790 }, { 0xfee5, 1, 5791 }, { 0xfee6, 1, 5791 }, { 0xfee7, 1, 5791 }, { 0xfee8, 1, 5791 }, { 0xfee9, 1, 5792 }, { 0xfeea, 1, 5792 }, { 0xfeeb, 1, 5792 }, { 0xfeec, 1, 5792 }, { 0xfeed, 1, 5793 }, { 0xfeee, 1, 5793 }, { 0xfeef, 1, 4985 }, { 0xfef0, 1, 4985 }, { 0xfef1, 1, 5794 }, { 0xfef2, 1, 5794 }, { 0xfef3, 1, 5794 }, { 0xfef4, 1, 5794 }, { 0xfef5, 3, 5795 }, { 0xfef6, 3, 5795 }, { 0xfef7, 3, 5798 }, { 0xfef8, 3, 5798 }, { 0xfef9, 3, 5801 }, { 0xfefa, 3, 5801 }, { 0xfefb, 2, 5804 }, { 0xfefc, 2, 5804 }, { 0xff01, 1, 5706 }, { 0xff02, 1, 5806 }, { 0xff03, 1, 5727 }, { 0xff04, 1, 5734 }, { 0xff05, 1, 5735 }, { 0xff06, 1, 5728 }, { 0xff07, 1, 5807 }, { 0xff08, 1, 2286 }, { 0xff09, 1, 2287 }, { 0xff0a, 1, 5729 }, { 0xff0b, 1, 2283 }, { 0xff0c, 1, 5702 }, { 0xff0d, 1, 5730 }, { 0xff0e, 1, 2246 }, { 0xff0f, 1, 5808 }, { 0xff10, 1, 2276 }, { 0xff11, 1, 13 }, { 0xff12, 1, 6 }, { 0xff13, 1, 7 }, { 0xff14, 1, 2277 }, { 0xff15, 1, 2278 }, { 0xff16, 1, 2279 }, { 0xff17, 1, 2280 }, { 0xff18, 1, 2281 }, { 0xff19, 1, 2282 }, { 0xff1a, 1, 5705 }, { 0xff1b, 1, 621 }, { 0xff1c, 1, 5731 }, { 0xff1d, 1, 2285 }, { 0xff1e, 1, 5732 }, { 0xff1f, 1, 5707 }, { 0xff20, 1, 5736 }, { 0xff21, 1, 973 }, { 0xff22, 1, 975 }, { 0xff23, 1, 2297 }, { 0xff24, 1, 976 }, { 0xff25, 1, 977 }, { 0xff26, 1, 2322 }, { 0xff27, 1, 979 }, { 0xff28, 1, 980 }, { 0xff29, 1, 981 }, { 0xff2a, 1, 982 }, { 0xff2b, 1, 983 }, { 0xff2c, 1, 984 }, { 0xff2d, 1, 985 }, { 0xff2e, 1, 986 }, { 0xff2f, 1, 987 }, { 0xff30, 1, 989 }, { 0xff31, 1, 2312 }, { 0xff32, 1, 990 }, { 0xff33, 1, 2754 }, { 0xff34, 1, 991 }, { 0xff35, 1, 992 }, { 0xff36, 1, 2388 }, { 0xff37, 1, 993 }, { 0xff38, 1, 2400 }, { 0xff39, 1, 2755 }, { 0xff3a, 1, 2320 }, { 0xff3b, 1, 5725 }, { 0xff3c, 1, 5733 }, { 0xff3d, 1, 5726 }, { 0xff3e, 1, 5809 }, { 0xff3f, 1, 5712 }, { 0xff40, 1, 2221 }, { 0xff41, 1, 3 }, { 0xff42, 1, 997 }, { 0xff43, 1, 1023 }, { 0xff44, 1, 998 }, { 0xff45, 1, 999 }, { 0xff46, 1, 1026 }, { 0xff47, 1, 1003 }, { 0xff48, 1, 588 }, { 0xff49, 1, 1020 }, { 0xff4a, 1, 590 }, { 0xff4b, 1, 1004 }, { 0xff4c, 1, 610 }, { 0xff4d, 1, 1005 }, { 0xff4e, 1, 2288 }, { 0xff4f, 1, 14 }, { 0xff50, 1, 1010 }, { 0xff51, 1, 2756 }, { 0xff52, 1, 591 }, { 0xff53, 1, 356 }, { 0xff54, 1, 1011 }, { 0xff55, 1, 1012 }, { 0xff56, 1, 1015 }, { 0xff57, 1, 595 }, { 0xff58, 1, 611 }, { 0xff59, 1, 596 }, { 0xff5a, 1, 1053 }, { 0xff5b, 1, 5713 }, { 0xff5c, 1, 5810 }, { 0xff5d, 1, 5714 }, { 0xff5e, 1, 5811 }, { 0xff5f, 1, 5812 }, { 0xff60, 1, 5813 }, { 0xff61, 1, 5704 }, { 0xff62, 1, 5721 }, { 0xff63, 1, 5722 }, { 0xff64, 1, 5703 }, { 0xff65, 1, 5814 }, { 0xff66, 1, 3662 }, { 0xff67, 1, 5815 }, { 0xff68, 1, 5816 }, { 0xff69, 1, 5817 }, { 0xff6a, 1, 5818 }, { 0xff6b, 1, 5819 }, { 0xff6c, 1, 5820 }, { 0xff6d, 1, 5821 }, { 0xff6e, 1, 5822 }, { 0xff6f, 1, 5823 }, { 0xff70, 1, 5824 }, { 0xff71, 1, 3616 }, { 0xff72, 1, 3617 }, { 0xff73, 1, 3618 }, { 0xff74, 1, 3619 }, { 0xff75, 1, 3620 }, { 0xff76, 1, 3621 }, { 0xff77, 1, 3622 }, { 0xff78, 1, 3623 }, { 0xff79, 1, 3624 }, { 0xff7a, 1, 3625 }, { 0xff7b, 1, 3626 }, { 0xff7c, 1, 3627 }, { 0xff7d, 1, 3628 }, { 0xff7e, 1, 3629 }, { 0xff7f, 1, 3630 }, { 0xff80, 1, 3631 }, { 0xff81, 1, 3632 }, { 0xff82, 1, 3633 }, { 0xff83, 1, 3634 }, { 0xff84, 1, 3635 }, { 0xff85, 1, 3636 }, { 0xff86, 1, 3637 }, { 0xff87, 1, 3638 }, { 0xff88, 1, 3639 }, { 0xff89, 1, 3640 }, { 0xff8a, 1, 3641 }, { 0xff8b, 1, 3642 }, { 0xff8c, 1, 3643 }, { 0xff8d, 1, 3644 }, { 0xff8e, 1, 3645 }, { 0xff8f, 1, 3646 }, { 0xff90, 1, 3647 }, { 0xff91, 1, 3648 }, { 0xff92, 1, 3649 }, { 0xff93, 1, 3650 }, { 0xff94, 1, 3651 }, { 0xff95, 1, 3652 }, { 0xff96, 1, 3653 }, { 0xff97, 1, 3654 }, { 0xff98, 1, 3655 }, { 0xff99, 1, 3656 }, { 0xff9a, 1, 3657 }, { 0xff9b, 1, 3658 }, { 0xff9c, 1, 3659 }, { 0xff9d, 1, 5825 }, { 0xff9e, 1, 5826 }, { 0xff9f, 1, 5827 }, { 0xffa0, 1, 3166 }, { 0xffa1, 1, 3115 }, { 0xffa2, 1, 3116 }, { 0xffa3, 1, 3117 }, { 0xffa4, 1, 3118 }, { 0xffa5, 1, 3119 }, { 0xffa6, 1, 3120 }, { 0xffa7, 1, 3121 }, { 0xffa8, 1, 3122 }, { 0xffa9, 1, 3123 }, { 0xffaa, 1, 3124 }, { 0xffab, 1, 3125 }, { 0xffac, 1, 3126 }, { 0xffad, 1, 3127 }, { 0xffae, 1, 3128 }, { 0xffaf, 1, 3129 }, { 0xffb0, 1, 3130 }, { 0xffb1, 1, 3131 }, { 0xffb2, 1, 3132 }, { 0xffb3, 1, 3133 }, { 0xffb4, 1, 3134 }, { 0xffb5, 1, 3135 }, { 0xffb6, 1, 3136 }, { 0xffb7, 1, 3137 }, { 0xffb8, 1, 3138 }, { 0xffb9, 1, 3139 }, { 0xffba, 1, 3140 }, { 0xffbb, 1, 3141 }, { 0xffbc, 1, 3142 }, { 0xffbd, 1, 3143 }, { 0xffbe, 1, 3144 }, { 0xffc2, 1, 3145 }, { 0xffc3, 1, 3146 }, { 0xffc4, 1, 3147 }, { 0xffc5, 1, 3148 }, { 0xffc6, 1, 3149 }, { 0xffc7, 1, 3150 }, { 0xffca, 1, 3151 }, { 0xffcb, 1, 3152 }, { 0xffcc, 1, 3153 }, { 0xffcd, 1, 3154 }, { 0xffce, 1, 3155 }, { 0xffcf, 1, 3156 }, { 0xffd2, 1, 3157 }, { 0xffd3, 1, 3158 }, { 0xffd4, 1, 3159 }, { 0xffd5, 1, 3160 }, { 0xffd6, 1, 3161 }, { 0xffd7, 1, 3162 }, { 0xffda, 1, 3163 }, { 0xffdb, 1, 3164 }, { 0xffdc, 1, 3165 }, { 0xffe0, 1, 5828 }, { 0xffe1, 1, 5829 }, { 0xffe2, 1, 5830 }, { 0xffe3, 2, 4 }, { 0xffe4, 1, 5831 }, { 0xffe5, 1, 5832 }, { 0xffe6, 1, 5833 }, { 0xffe8, 1, 5834 }, { 0xffe9, 1, 5835 }, { 0xffea, 1, 5836 }, { 0xffeb, 1, 5837 }, { 0xffec, 1, 5838 }, { 0xffed, 1, 5839 }, { 0xffee, 1, 5840 }, { 0x1109a, 2, 5841 }, { 0x1109c, 2, 5843 }, { 0x110ab, 2, 5845 }, { 0x1112e, 2, 5847 }, { 0x1112f, 2, 5849 }, { 0x1134b, 2, 5851 }, { 0x1134c, 2, 5853 }, { 0x114bb, 2, 5855 }, { 0x114bc, 2, 5857 }, { 0x114be, 2, 5859 }, { 0x115ba, 2, 5861 }, { 0x115bb, 2, 5863 }, { 0x1d15e, 2, 5865 }, { 0x1d15f, 2, 5867 }, { 0x1d160, 3, 5869 }, { 0x1d161, 3, 5872 }, { 0x1d162, 3, 5875 }, { 0x1d163, 3, 5878 }, { 0x1d164, 3, 5881 }, { 0x1d1bb, 2, 5884 }, { 0x1d1bc, 2, 5886 }, { 0x1d1bd, 3, 5888 }, { 0x1d1be, 3, 5891 }, { 0x1d1bf, 3, 5894 }, { 0x1d1c0, 3, 5897 }, { 0x1d400, 1, 973 }, { 0x1d401, 1, 975 }, { 0x1d402, 1, 2297 }, { 0x1d403, 1, 976 }, { 0x1d404, 1, 977 }, { 0x1d405, 1, 2322 }, { 0x1d406, 1, 979 }, { 0x1d407, 1, 980 }, { 0x1d408, 1, 981 }, { 0x1d409, 1, 982 }, { 0x1d40a, 1, 983 }, { 0x1d40b, 1, 984 }, { 0x1d40c, 1, 985 }, { 0x1d40d, 1, 986 }, { 0x1d40e, 1, 987 }, { 0x1d40f, 1, 989 }, { 0x1d410, 1, 2312 }, { 0x1d411, 1, 990 }, { 0x1d412, 1, 2754 }, { 0x1d413, 1, 991 }, { 0x1d414, 1, 992 }, { 0x1d415, 1, 2388 }, { 0x1d416, 1, 993 }, { 0x1d417, 1, 2400 }, { 0x1d418, 1, 2755 }, { 0x1d419, 1, 2320 }, { 0x1d41a, 1, 3 }, { 0x1d41b, 1, 997 }, { 0x1d41c, 1, 1023 }, { 0x1d41d, 1, 998 }, { 0x1d41e, 1, 999 }, { 0x1d41f, 1, 1026 }, { 0x1d420, 1, 1003 }, { 0x1d421, 1, 588 }, { 0x1d422, 1, 1020 }, { 0x1d423, 1, 590 }, { 0x1d424, 1, 1004 }, { 0x1d425, 1, 610 }, { 0x1d426, 1, 1005 }, { 0x1d427, 1, 2288 }, { 0x1d428, 1, 14 }, { 0x1d429, 1, 1010 }, { 0x1d42a, 1, 2756 }, { 0x1d42b, 1, 591 }, { 0x1d42c, 1, 356 }, { 0x1d42d, 1, 1011 }, { 0x1d42e, 1, 1012 }, { 0x1d42f, 1, 1015 }, { 0x1d430, 1, 595 }, { 0x1d431, 1, 611 }, { 0x1d432, 1, 596 }, { 0x1d433, 1, 1053 }, { 0x1d434, 1, 973 }, { 0x1d435, 1, 975 }, { 0x1d436, 1, 2297 }, { 0x1d437, 1, 976 }, { 0x1d438, 1, 977 }, { 0x1d439, 1, 2322 }, { 0x1d43a, 1, 979 }, { 0x1d43b, 1, 980 }, { 0x1d43c, 1, 981 }, { 0x1d43d, 1, 982 }, { 0x1d43e, 1, 983 }, { 0x1d43f, 1, 984 }, { 0x1d440, 1, 985 }, { 0x1d441, 1, 986 }, { 0x1d442, 1, 987 }, { 0x1d443, 1, 989 }, { 0x1d444, 1, 2312 }, { 0x1d445, 1, 990 }, { 0x1d446, 1, 2754 }, { 0x1d447, 1, 991 }, { 0x1d448, 1, 992 }, { 0x1d449, 1, 2388 }, { 0x1d44a, 1, 993 }, { 0x1d44b, 1, 2400 }, { 0x1d44c, 1, 2755 }, { 0x1d44d, 1, 2320 }, { 0x1d44e, 1, 3 }, { 0x1d44f, 1, 997 }, { 0x1d450, 1, 1023 }, { 0x1d451, 1, 998 }, { 0x1d452, 1, 999 }, { 0x1d453, 1, 1026 }, { 0x1d454, 1, 1003 }, { 0x1d456, 1, 1020 }, { 0x1d457, 1, 590 }, { 0x1d458, 1, 1004 }, { 0x1d459, 1, 610 }, { 0x1d45a, 1, 1005 }, { 0x1d45b, 1, 2288 }, { 0x1d45c, 1, 14 }, { 0x1d45d, 1, 1010 }, { 0x1d45e, 1, 2756 }, { 0x1d45f, 1, 591 }, { 0x1d460, 1, 356 }, { 0x1d461, 1, 1011 }, { 0x1d462, 1, 1012 }, { 0x1d463, 1, 1015 }, { 0x1d464, 1, 595 }, { 0x1d465, 1, 611 }, { 0x1d466, 1, 596 }, { 0x1d467, 1, 1053 }, { 0x1d468, 1, 973 }, { 0x1d469, 1, 975 }, { 0x1d46a, 1, 2297 }, { 0x1d46b, 1, 976 }, { 0x1d46c, 1, 977 }, { 0x1d46d, 1, 2322 }, { 0x1d46e, 1, 979 }, { 0x1d46f, 1, 980 }, { 0x1d470, 1, 981 }, { 0x1d471, 1, 982 }, { 0x1d472, 1, 983 }, { 0x1d473, 1, 984 }, { 0x1d474, 1, 985 }, { 0x1d475, 1, 986 }, { 0x1d476, 1, 987 }, { 0x1d477, 1, 989 }, { 0x1d478, 1, 2312 }, { 0x1d479, 1, 990 }, { 0x1d47a, 1, 2754 }, { 0x1d47b, 1, 991 }, { 0x1d47c, 1, 992 }, { 0x1d47d, 1, 2388 }, { 0x1d47e, 1, 993 }, { 0x1d47f, 1, 2400 }, { 0x1d480, 1, 2755 }, { 0x1d481, 1, 2320 }, { 0x1d482, 1, 3 }, { 0x1d483, 1, 997 }, { 0x1d484, 1, 1023 }, { 0x1d485, 1, 998 }, { 0x1d486, 1, 999 }, { 0x1d487, 1, 1026 }, { 0x1d488, 1, 1003 }, { 0x1d489, 1, 588 }, { 0x1d48a, 1, 1020 }, { 0x1d48b, 1, 590 }, { 0x1d48c, 1, 1004 }, { 0x1d48d, 1, 610 }, { 0x1d48e, 1, 1005 }, { 0x1d48f, 1, 2288 }, { 0x1d490, 1, 14 }, { 0x1d491, 1, 1010 }, { 0x1d492, 1, 2756 }, { 0x1d493, 1, 591 }, { 0x1d494, 1, 356 }, { 0x1d495, 1, 1011 }, { 0x1d496, 1, 1012 }, { 0x1d497, 1, 1015 }, { 0x1d498, 1, 595 }, { 0x1d499, 1, 611 }, { 0x1d49a, 1, 596 }, { 0x1d49b, 1, 1053 }, { 0x1d49c, 1, 973 }, { 0x1d49e, 1, 2297 }, { 0x1d49f, 1, 976 }, { 0x1d4a2, 1, 979 }, { 0x1d4a5, 1, 982 }, { 0x1d4a6, 1, 983 }, { 0x1d4a9, 1, 986 }, { 0x1d4aa, 1, 987 }, { 0x1d4ab, 1, 989 }, { 0x1d4ac, 1, 2312 }, { 0x1d4ae, 1, 2754 }, { 0x1d4af, 1, 991 }, { 0x1d4b0, 1, 992 }, { 0x1d4b1, 1, 2388 }, { 0x1d4b2, 1, 993 }, { 0x1d4b3, 1, 2400 }, { 0x1d4b4, 1, 2755 }, { 0x1d4b5, 1, 2320 }, { 0x1d4b6, 1, 3 }, { 0x1d4b7, 1, 997 }, { 0x1d4b8, 1, 1023 }, { 0x1d4b9, 1, 998 }, { 0x1d4bb, 1, 1026 }, { 0x1d4bd, 1, 588 }, { 0x1d4be, 1, 1020 }, { 0x1d4bf, 1, 590 }, { 0x1d4c0, 1, 1004 }, { 0x1d4c1, 1, 610 }, { 0x1d4c2, 1, 1005 }, { 0x1d4c3, 1, 2288 }, { 0x1d4c5, 1, 1010 }, { 0x1d4c6, 1, 2756 }, { 0x1d4c7, 1, 591 }, { 0x1d4c8, 1, 356 }, { 0x1d4c9, 1, 1011 }, { 0x1d4ca, 1, 1012 }, { 0x1d4cb, 1, 1015 }, { 0x1d4cc, 1, 595 }, { 0x1d4cd, 1, 611 }, { 0x1d4ce, 1, 596 }, { 0x1d4cf, 1, 1053 }, { 0x1d4d0, 1, 973 }, { 0x1d4d1, 1, 975 }, { 0x1d4d2, 1, 2297 }, { 0x1d4d3, 1, 976 }, { 0x1d4d4, 1, 977 }, { 0x1d4d5, 1, 2322 }, { 0x1d4d6, 1, 979 }, { 0x1d4d7, 1, 980 }, { 0x1d4d8, 1, 981 }, { 0x1d4d9, 1, 982 }, { 0x1d4da, 1, 983 }, { 0x1d4db, 1, 984 }, { 0x1d4dc, 1, 985 }, { 0x1d4dd, 1, 986 }, { 0x1d4de, 1, 987 }, { 0x1d4df, 1, 989 }, { 0x1d4e0, 1, 2312 }, { 0x1d4e1, 1, 990 }, { 0x1d4e2, 1, 2754 }, { 0x1d4e3, 1, 991 }, { 0x1d4e4, 1, 992 }, { 0x1d4e5, 1, 2388 }, { 0x1d4e6, 1, 993 }, { 0x1d4e7, 1, 2400 }, { 0x1d4e8, 1, 2755 }, { 0x1d4e9, 1, 2320 }, { 0x1d4ea, 1, 3 }, { 0x1d4eb, 1, 997 }, { 0x1d4ec, 1, 1023 }, { 0x1d4ed, 1, 998 }, { 0x1d4ee, 1, 999 }, { 0x1d4ef, 1, 1026 }, { 0x1d4f0, 1, 1003 }, { 0x1d4f1, 1, 588 }, { 0x1d4f2, 1, 1020 }, { 0x1d4f3, 1, 590 }, { 0x1d4f4, 1, 1004 }, { 0x1d4f5, 1, 610 }, { 0x1d4f6, 1, 1005 }, { 0x1d4f7, 1, 2288 }, { 0x1d4f8, 1, 14 }, { 0x1d4f9, 1, 1010 }, { 0x1d4fa, 1, 2756 }, { 0x1d4fb, 1, 591 }, { 0x1d4fc, 1, 356 }, { 0x1d4fd, 1, 1011 }, { 0x1d4fe, 1, 1012 }, { 0x1d4ff, 1, 1015 }, { 0x1d500, 1, 595 }, { 0x1d501, 1, 611 }, { 0x1d502, 1, 596 }, { 0x1d503, 1, 1053 }, { 0x1d504, 1, 973 }, { 0x1d505, 1, 975 }, { 0x1d507, 1, 976 }, { 0x1d508, 1, 977 }, { 0x1d509, 1, 2322 }, { 0x1d50a, 1, 979 }, { 0x1d50d, 1, 982 }, { 0x1d50e, 1, 983 }, { 0x1d50f, 1, 984 }, { 0x1d510, 1, 985 }, { 0x1d511, 1, 986 }, { 0x1d512, 1, 987 }, { 0x1d513, 1, 989 }, { 0x1d514, 1, 2312 }, { 0x1d516, 1, 2754 }, { 0x1d517, 1, 991 }, { 0x1d518, 1, 992 }, { 0x1d519, 1, 2388 }, { 0x1d51a, 1, 993 }, { 0x1d51b, 1, 2400 }, { 0x1d51c, 1, 2755 }, { 0x1d51e, 1, 3 }, { 0x1d51f, 1, 997 }, { 0x1d520, 1, 1023 }, { 0x1d521, 1, 998 }, { 0x1d522, 1, 999 }, { 0x1d523, 1, 1026 }, { 0x1d524, 1, 1003 }, { 0x1d525, 1, 588 }, { 0x1d526, 1, 1020 }, { 0x1d527, 1, 590 }, { 0x1d528, 1, 1004 }, { 0x1d529, 1, 610 }, { 0x1d52a, 1, 1005 }, { 0x1d52b, 1, 2288 }, { 0x1d52c, 1, 14 }, { 0x1d52d, 1, 1010 }, { 0x1d52e, 1, 2756 }, { 0x1d52f, 1, 591 }, { 0x1d530, 1, 356 }, { 0x1d531, 1, 1011 }, { 0x1d532, 1, 1012 }, { 0x1d533, 1, 1015 }, { 0x1d534, 1, 595 }, { 0x1d535, 1, 611 }, { 0x1d536, 1, 596 }, { 0x1d537, 1, 1053 }, { 0x1d538, 1, 973 }, { 0x1d539, 1, 975 }, { 0x1d53b, 1, 976 }, { 0x1d53c, 1, 977 }, { 0x1d53d, 1, 2322 }, { 0x1d53e, 1, 979 }, { 0x1d540, 1, 981 }, { 0x1d541, 1, 982 }, { 0x1d542, 1, 983 }, { 0x1d543, 1, 984 }, { 0x1d544, 1, 985 }, { 0x1d546, 1, 987 }, { 0x1d54a, 1, 2754 }, { 0x1d54b, 1, 991 }, { 0x1d54c, 1, 992 }, { 0x1d54d, 1, 2388 }, { 0x1d54e, 1, 993 }, { 0x1d54f, 1, 2400 }, { 0x1d550, 1, 2755 }, { 0x1d552, 1, 3 }, { 0x1d553, 1, 997 }, { 0x1d554, 1, 1023 }, { 0x1d555, 1, 998 }, { 0x1d556, 1, 999 }, { 0x1d557, 1, 1026 }, { 0x1d558, 1, 1003 }, { 0x1d559, 1, 588 }, { 0x1d55a, 1, 1020 }, { 0x1d55b, 1, 590 }, { 0x1d55c, 1, 1004 }, { 0x1d55d, 1, 610 }, { 0x1d55e, 1, 1005 }, { 0x1d55f, 1, 2288 }, { 0x1d560, 1, 14 }, { 0x1d561, 1, 1010 }, { 0x1d562, 1, 2756 }, { 0x1d563, 1, 591 }, { 0x1d564, 1, 356 }, { 0x1d565, 1, 1011 }, { 0x1d566, 1, 1012 }, { 0x1d567, 1, 1015 }, { 0x1d568, 1, 595 }, { 0x1d569, 1, 611 }, { 0x1d56a, 1, 596 }, { 0x1d56b, 1, 1053 }, { 0x1d56c, 1, 973 }, { 0x1d56d, 1, 975 }, { 0x1d56e, 1, 2297 }, { 0x1d56f, 1, 976 }, { 0x1d570, 1, 977 }, { 0x1d571, 1, 2322 }, { 0x1d572, 1, 979 }, { 0x1d573, 1, 980 }, { 0x1d574, 1, 981 }, { 0x1d575, 1, 982 }, { 0x1d576, 1, 983 }, { 0x1d577, 1, 984 }, { 0x1d578, 1, 985 }, { 0x1d579, 1, 986 }, { 0x1d57a, 1, 987 }, { 0x1d57b, 1, 989 }, { 0x1d57c, 1, 2312 }, { 0x1d57d, 1, 990 }, { 0x1d57e, 1, 2754 }, { 0x1d57f, 1, 991 }, { 0x1d580, 1, 992 }, { 0x1d581, 1, 2388 }, { 0x1d582, 1, 993 }, { 0x1d583, 1, 2400 }, { 0x1d584, 1, 2755 }, { 0x1d585, 1, 2320 }, { 0x1d586, 1, 3 }, { 0x1d587, 1, 997 }, { 0x1d588, 1, 1023 }, { 0x1d589, 1, 998 }, { 0x1d58a, 1, 999 }, { 0x1d58b, 1, 1026 }, { 0x1d58c, 1, 1003 }, { 0x1d58d, 1, 588 }, { 0x1d58e, 1, 1020 }, { 0x1d58f, 1, 590 }, { 0x1d590, 1, 1004 }, { 0x1d591, 1, 610 }, { 0x1d592, 1, 1005 }, { 0x1d593, 1, 2288 }, { 0x1d594, 1, 14 }, { 0x1d595, 1, 1010 }, { 0x1d596, 1, 2756 }, { 0x1d597, 1, 591 }, { 0x1d598, 1, 356 }, { 0x1d599, 1, 1011 }, { 0x1d59a, 1, 1012 }, { 0x1d59b, 1, 1015 }, { 0x1d59c, 1, 595 }, { 0x1d59d, 1, 611 }, { 0x1d59e, 1, 596 }, { 0x1d59f, 1, 1053 }, { 0x1d5a0, 1, 973 }, { 0x1d5a1, 1, 975 }, { 0x1d5a2, 1, 2297 }, { 0x1d5a3, 1, 976 }, { 0x1d5a4, 1, 977 }, { 0x1d5a5, 1, 2322 }, { 0x1d5a6, 1, 979 }, { 0x1d5a7, 1, 980 }, { 0x1d5a8, 1, 981 }, { 0x1d5a9, 1, 982 }, { 0x1d5aa, 1, 983 }, { 0x1d5ab, 1, 984 }, { 0x1d5ac, 1, 985 }, { 0x1d5ad, 1, 986 }, { 0x1d5ae, 1, 987 }, { 0x1d5af, 1, 989 }, { 0x1d5b0, 1, 2312 }, { 0x1d5b1, 1, 990 }, { 0x1d5b2, 1, 2754 }, { 0x1d5b3, 1, 991 }, { 0x1d5b4, 1, 992 }, { 0x1d5b5, 1, 2388 }, { 0x1d5b6, 1, 993 }, { 0x1d5b7, 1, 2400 }, { 0x1d5b8, 1, 2755 }, { 0x1d5b9, 1, 2320 }, { 0x1d5ba, 1, 3 }, { 0x1d5bb, 1, 997 }, { 0x1d5bc, 1, 1023 }, { 0x1d5bd, 1, 998 }, { 0x1d5be, 1, 999 }, { 0x1d5bf, 1, 1026 }, { 0x1d5c0, 1, 1003 }, { 0x1d5c1, 1, 588 }, { 0x1d5c2, 1, 1020 }, { 0x1d5c3, 1, 590 }, { 0x1d5c4, 1, 1004 }, { 0x1d5c5, 1, 610 }, { 0x1d5c6, 1, 1005 }, { 0x1d5c7, 1, 2288 }, { 0x1d5c8, 1, 14 }, { 0x1d5c9, 1, 1010 }, { 0x1d5ca, 1, 2756 }, { 0x1d5cb, 1, 591 }, { 0x1d5cc, 1, 356 }, { 0x1d5cd, 1, 1011 }, { 0x1d5ce, 1, 1012 }, { 0x1d5cf, 1, 1015 }, { 0x1d5d0, 1, 595 }, { 0x1d5d1, 1, 611 }, { 0x1d5d2, 1, 596 }, { 0x1d5d3, 1, 1053 }, { 0x1d5d4, 1, 973 }, { 0x1d5d5, 1, 975 }, { 0x1d5d6, 1, 2297 }, { 0x1d5d7, 1, 976 }, { 0x1d5d8, 1, 977 }, { 0x1d5d9, 1, 2322 }, { 0x1d5da, 1, 979 }, { 0x1d5db, 1, 980 }, { 0x1d5dc, 1, 981 }, { 0x1d5dd, 1, 982 }, { 0x1d5de, 1, 983 }, { 0x1d5df, 1, 984 }, { 0x1d5e0, 1, 985 }, { 0x1d5e1, 1, 986 }, { 0x1d5e2, 1, 987 }, { 0x1d5e3, 1, 989 }, { 0x1d5e4, 1, 2312 }, { 0x1d5e5, 1, 990 }, { 0x1d5e6, 1, 2754 }, { 0x1d5e7, 1, 991 }, { 0x1d5e8, 1, 992 }, { 0x1d5e9, 1, 2388 }, { 0x1d5ea, 1, 993 }, { 0x1d5eb, 1, 2400 }, { 0x1d5ec, 1, 2755 }, { 0x1d5ed, 1, 2320 }, { 0x1d5ee, 1, 3 }, { 0x1d5ef, 1, 997 }, { 0x1d5f0, 1, 1023 }, { 0x1d5f1, 1, 998 }, { 0x1d5f2, 1, 999 }, { 0x1d5f3, 1, 1026 }, { 0x1d5f4, 1, 1003 }, { 0x1d5f5, 1, 588 }, { 0x1d5f6, 1, 1020 }, { 0x1d5f7, 1, 590 }, { 0x1d5f8, 1, 1004 }, { 0x1d5f9, 1, 610 }, { 0x1d5fa, 1, 1005 }, { 0x1d5fb, 1, 2288 }, { 0x1d5fc, 1, 14 }, { 0x1d5fd, 1, 1010 }, { 0x1d5fe, 1, 2756 }, { 0x1d5ff, 1, 591 }, { 0x1d600, 1, 356 }, { 0x1d601, 1, 1011 }, { 0x1d602, 1, 1012 }, { 0x1d603, 1, 1015 }, { 0x1d604, 1, 595 }, { 0x1d605, 1, 611 }, { 0x1d606, 1, 596 }, { 0x1d607, 1, 1053 }, { 0x1d608, 1, 973 }, { 0x1d609, 1, 975 }, { 0x1d60a, 1, 2297 }, { 0x1d60b, 1, 976 }, { 0x1d60c, 1, 977 }, { 0x1d60d, 1, 2322 }, { 0x1d60e, 1, 979 }, { 0x1d60f, 1, 980 }, { 0x1d610, 1, 981 }, { 0x1d611, 1, 982 }, { 0x1d612, 1, 983 }, { 0x1d613, 1, 984 }, { 0x1d614, 1, 985 }, { 0x1d615, 1, 986 }, { 0x1d616, 1, 987 }, { 0x1d617, 1, 989 }, { 0x1d618, 1, 2312 }, { 0x1d619, 1, 990 }, { 0x1d61a, 1, 2754 }, { 0x1d61b, 1, 991 }, { 0x1d61c, 1, 992 }, { 0x1d61d, 1, 2388 }, { 0x1d61e, 1, 993 }, { 0x1d61f, 1, 2400 }, { 0x1d620, 1, 2755 }, { 0x1d621, 1, 2320 }, { 0x1d622, 1, 3 }, { 0x1d623, 1, 997 }, { 0x1d624, 1, 1023 }, { 0x1d625, 1, 998 }, { 0x1d626, 1, 999 }, { 0x1d627, 1, 1026 }, { 0x1d628, 1, 1003 }, { 0x1d629, 1, 588 }, { 0x1d62a, 1, 1020 }, { 0x1d62b, 1, 590 }, { 0x1d62c, 1, 1004 }, { 0x1d62d, 1, 610 }, { 0x1d62e, 1, 1005 }, { 0x1d62f, 1, 2288 }, { 0x1d630, 1, 14 }, { 0x1d631, 1, 1010 }, { 0x1d632, 1, 2756 }, { 0x1d633, 1, 591 }, { 0x1d634, 1, 356 }, { 0x1d635, 1, 1011 }, { 0x1d636, 1, 1012 }, { 0x1d637, 1, 1015 }, { 0x1d638, 1, 595 }, { 0x1d639, 1, 611 }, { 0x1d63a, 1, 596 }, { 0x1d63b, 1, 1053 }, { 0x1d63c, 1, 973 }, { 0x1d63d, 1, 975 }, { 0x1d63e, 1, 2297 }, { 0x1d63f, 1, 976 }, { 0x1d640, 1, 977 }, { 0x1d641, 1, 2322 }, { 0x1d642, 1, 979 }, { 0x1d643, 1, 980 }, { 0x1d644, 1, 981 }, { 0x1d645, 1, 982 }, { 0x1d646, 1, 983 }, { 0x1d647, 1, 984 }, { 0x1d648, 1, 985 }, { 0x1d649, 1, 986 }, { 0x1d64a, 1, 987 }, { 0x1d64b, 1, 989 }, { 0x1d64c, 1, 2312 }, { 0x1d64d, 1, 990 }, { 0x1d64e, 1, 2754 }, { 0x1d64f, 1, 991 }, { 0x1d650, 1, 992 }, { 0x1d651, 1, 2388 }, { 0x1d652, 1, 993 }, { 0x1d653, 1, 2400 }, { 0x1d654, 1, 2755 }, { 0x1d655, 1, 2320 }, { 0x1d656, 1, 3 }, { 0x1d657, 1, 997 }, { 0x1d658, 1, 1023 }, { 0x1d659, 1, 998 }, { 0x1d65a, 1, 999 }, { 0x1d65b, 1, 1026 }, { 0x1d65c, 1, 1003 }, { 0x1d65d, 1, 588 }, { 0x1d65e, 1, 1020 }, { 0x1d65f, 1, 590 }, { 0x1d660, 1, 1004 }, { 0x1d661, 1, 610 }, { 0x1d662, 1, 1005 }, { 0x1d663, 1, 2288 }, { 0x1d664, 1, 14 }, { 0x1d665, 1, 1010 }, { 0x1d666, 1, 2756 }, { 0x1d667, 1, 591 }, { 0x1d668, 1, 356 }, { 0x1d669, 1, 1011 }, { 0x1d66a, 1, 1012 }, { 0x1d66b, 1, 1015 }, { 0x1d66c, 1, 595 }, { 0x1d66d, 1, 611 }, { 0x1d66e, 1, 596 }, { 0x1d66f, 1, 1053 }, { 0x1d670, 1, 973 }, { 0x1d671, 1, 975 }, { 0x1d672, 1, 2297 }, { 0x1d673, 1, 976 }, { 0x1d674, 1, 977 }, { 0x1d675, 1, 2322 }, { 0x1d676, 1, 979 }, { 0x1d677, 1, 980 }, { 0x1d678, 1, 981 }, { 0x1d679, 1, 982 }, { 0x1d67a, 1, 983 }, { 0x1d67b, 1, 984 }, { 0x1d67c, 1, 985 }, { 0x1d67d, 1, 986 }, { 0x1d67e, 1, 987 }, { 0x1d67f, 1, 989 }, { 0x1d680, 1, 2312 }, { 0x1d681, 1, 990 }, { 0x1d682, 1, 2754 }, { 0x1d683, 1, 991 }, { 0x1d684, 1, 992 }, { 0x1d685, 1, 2388 }, { 0x1d686, 1, 993 }, { 0x1d687, 1, 2400 }, { 0x1d688, 1, 2755 }, { 0x1d689, 1, 2320 }, { 0x1d68a, 1, 3 }, { 0x1d68b, 1, 997 }, { 0x1d68c, 1, 1023 }, { 0x1d68d, 1, 998 }, { 0x1d68e, 1, 999 }, { 0x1d68f, 1, 1026 }, { 0x1d690, 1, 1003 }, { 0x1d691, 1, 588 }, { 0x1d692, 1, 1020 }, { 0x1d693, 1, 590 }, { 0x1d694, 1, 1004 }, { 0x1d695, 1, 610 }, { 0x1d696, 1, 1005 }, { 0x1d697, 1, 2288 }, { 0x1d698, 1, 14 }, { 0x1d699, 1, 1010 }, { 0x1d69a, 1, 2756 }, { 0x1d69b, 1, 591 }, { 0x1d69c, 1, 356 }, { 0x1d69d, 1, 1011 }, { 0x1d69e, 1, 1012 }, { 0x1d69f, 1, 1015 }, { 0x1d6a0, 1, 595 }, { 0x1d6a1, 1, 611 }, { 0x1d6a2, 1, 596 }, { 0x1d6a3, 1, 1053 }, { 0x1d6a4, 1, 5900 }, { 0x1d6a5, 1, 5901 }, { 0x1d6a8, 1, 5902 }, { 0x1d6a9, 1, 5903 }, { 0x1d6aa, 1, 2330 }, { 0x1d6ab, 1, 5904 }, { 0x1d6ac, 1, 5905 }, { 0x1d6ad, 1, 5906 }, { 0x1d6ae, 1, 5907 }, { 0x1d6af, 1, 676 }, { 0x1d6b0, 1, 5908 }, { 0x1d6b1, 1, 5909 }, { 0x1d6b2, 1, 5910 }, { 0x1d6b3, 1, 5911 }, { 0x1d6b4, 1, 5912 }, { 0x1d6b5, 1, 5913 }, { 0x1d6b6, 1, 5914 }, { 0x1d6b7, 1, 2331 }, { 0x1d6b8, 1, 5915 }, { 0x1d6b9, 1, 676 }, { 0x1d6ba, 1, 678 }, { 0x1d6bb, 1, 5916 }, { 0x1d6bc, 1, 670 }, { 0x1d6bd, 1, 5917 }, { 0x1d6be, 1, 5918 }, { 0x1d6bf, 1, 5919 }, { 0x1d6c0, 1, 2321 }, { 0x1d6c1, 1, 5920 }, { 0x1d6c2, 1, 5921 }, { 0x1d6c3, 1, 668 }, { 0x1d6c4, 1, 1017 }, { 0x1d6c5, 1, 1018 }, { 0x1d6c6, 1, 677 }, { 0x1d6c7, 1, 5922 }, { 0x1d6c8, 1, 5923 }, { 0x1d6c9, 1, 669 }, { 0x1d6ca, 1, 2133 }, { 0x1d6cb, 1, 673 }, { 0x1d6cc, 1, 5924 }, { 0x1d6cd, 1, 10 }, { 0x1d6ce, 1, 5925 }, { 0x1d6cf, 1, 5926 }, { 0x1d6d0, 1, 5927 }, { 0x1d6d1, 1, 672 }, { 0x1d6d2, 1, 674 }, { 0x1d6d3, 1, 675 }, { 0x1d6d4, 1, 5928 }, { 0x1d6d5, 1, 5929 }, { 0x1d6d6, 1, 5930 }, { 0x1d6d7, 1, 671 }, { 0x1d6d8, 1, 1019 }, { 0x1d6d9, 1, 5931 }, { 0x1d6da, 1, 5932 }, { 0x1d6db, 1, 5933 }, { 0x1d6dc, 1, 677 }, { 0x1d6dd, 1, 669 }, { 0x1d6de, 1, 673 }, { 0x1d6df, 1, 671 }, { 0x1d6e0, 1, 674 }, { 0x1d6e1, 1, 672 }, { 0x1d6e2, 1, 5902 }, { 0x1d6e3, 1, 5903 }, { 0x1d6e4, 1, 2330 }, { 0x1d6e5, 1, 5904 }, { 0x1d6e6, 1, 5905 }, { 0x1d6e7, 1, 5906 }, { 0x1d6e8, 1, 5907 }, { 0x1d6e9, 1, 676 }, { 0x1d6ea, 1, 5908 }, { 0x1d6eb, 1, 5909 }, { 0x1d6ec, 1, 5910 }, { 0x1d6ed, 1, 5911 }, { 0x1d6ee, 1, 5912 }, { 0x1d6ef, 1, 5913 }, { 0x1d6f0, 1, 5914 }, { 0x1d6f1, 1, 2331 }, { 0x1d6f2, 1, 5915 }, { 0x1d6f3, 1, 676 }, { 0x1d6f4, 1, 678 }, { 0x1d6f5, 1, 5916 }, { 0x1d6f6, 1, 670 }, { 0x1d6f7, 1, 5917 }, { 0x1d6f8, 1, 5918 }, { 0x1d6f9, 1, 5919 }, { 0x1d6fa, 1, 2321 }, { 0x1d6fb, 1, 5920 }, { 0x1d6fc, 1, 5921 }, { 0x1d6fd, 1, 668 }, { 0x1d6fe, 1, 1017 }, { 0x1d6ff, 1, 1018 }, { 0x1d700, 1, 677 }, { 0x1d701, 1, 5922 }, { 0x1d702, 1, 5923 }, { 0x1d703, 1, 669 }, { 0x1d704, 1, 2133 }, { 0x1d705, 1, 673 }, { 0x1d706, 1, 5924 }, { 0x1d707, 1, 10 }, { 0x1d708, 1, 5925 }, { 0x1d709, 1, 5926 }, { 0x1d70a, 1, 5927 }, { 0x1d70b, 1, 672 }, { 0x1d70c, 1, 674 }, { 0x1d70d, 1, 675 }, { 0x1d70e, 1, 5928 }, { 0x1d70f, 1, 5929 }, { 0x1d710, 1, 5930 }, { 0x1d711, 1, 671 }, { 0x1d712, 1, 1019 }, { 0x1d713, 1, 5931 }, { 0x1d714, 1, 5932 }, { 0x1d715, 1, 5933 }, { 0x1d716, 1, 677 }, { 0x1d717, 1, 669 }, { 0x1d718, 1, 673 }, { 0x1d719, 1, 671 }, { 0x1d71a, 1, 674 }, { 0x1d71b, 1, 672 }, { 0x1d71c, 1, 5902 }, { 0x1d71d, 1, 5903 }, { 0x1d71e, 1, 2330 }, { 0x1d71f, 1, 5904 }, { 0x1d720, 1, 5905 }, { 0x1d721, 1, 5906 }, { 0x1d722, 1, 5907 }, { 0x1d723, 1, 676 }, { 0x1d724, 1, 5908 }, { 0x1d725, 1, 5909 }, { 0x1d726, 1, 5910 }, { 0x1d727, 1, 5911 }, { 0x1d728, 1, 5912 }, { 0x1d729, 1, 5913 }, { 0x1d72a, 1, 5914 }, { 0x1d72b, 1, 2331 }, { 0x1d72c, 1, 5915 }, { 0x1d72d, 1, 676 }, { 0x1d72e, 1, 678 }, { 0x1d72f, 1, 5916 }, { 0x1d730, 1, 670 }, { 0x1d731, 1, 5917 }, { 0x1d732, 1, 5918 }, { 0x1d733, 1, 5919 }, { 0x1d734, 1, 2321 }, { 0x1d735, 1, 5920 }, { 0x1d736, 1, 5921 }, { 0x1d737, 1, 668 }, { 0x1d738, 1, 1017 }, { 0x1d739, 1, 1018 }, { 0x1d73a, 1, 677 }, { 0x1d73b, 1, 5922 }, { 0x1d73c, 1, 5923 }, { 0x1d73d, 1, 669 }, { 0x1d73e, 1, 2133 }, { 0x1d73f, 1, 673 }, { 0x1d740, 1, 5924 }, { 0x1d741, 1, 10 }, { 0x1d742, 1, 5925 }, { 0x1d743, 1, 5926 }, { 0x1d744, 1, 5927 }, { 0x1d745, 1, 672 }, { 0x1d746, 1, 674 }, { 0x1d747, 1, 675 }, { 0x1d748, 1, 5928 }, { 0x1d749, 1, 5929 }, { 0x1d74a, 1, 5930 }, { 0x1d74b, 1, 671 }, { 0x1d74c, 1, 1019 }, { 0x1d74d, 1, 5931 }, { 0x1d74e, 1, 5932 }, { 0x1d74f, 1, 5933 }, { 0x1d750, 1, 677 }, { 0x1d751, 1, 669 }, { 0x1d752, 1, 673 }, { 0x1d753, 1, 671 }, { 0x1d754, 1, 674 }, { 0x1d755, 1, 672 }, { 0x1d756, 1, 5902 }, { 0x1d757, 1, 5903 }, { 0x1d758, 1, 2330 }, { 0x1d759, 1, 5904 }, { 0x1d75a, 1, 5905 }, { 0x1d75b, 1, 5906 }, { 0x1d75c, 1, 5907 }, { 0x1d75d, 1, 676 }, { 0x1d75e, 1, 5908 }, { 0x1d75f, 1, 5909 }, { 0x1d760, 1, 5910 }, { 0x1d761, 1, 5911 }, { 0x1d762, 1, 5912 }, { 0x1d763, 1, 5913 }, { 0x1d764, 1, 5914 }, { 0x1d765, 1, 2331 }, { 0x1d766, 1, 5915 }, { 0x1d767, 1, 676 }, { 0x1d768, 1, 678 }, { 0x1d769, 1, 5916 }, { 0x1d76a, 1, 670 }, { 0x1d76b, 1, 5917 }, { 0x1d76c, 1, 5918 }, { 0x1d76d, 1, 5919 }, { 0x1d76e, 1, 2321 }, { 0x1d76f, 1, 5920 }, { 0x1d770, 1, 5921 }, { 0x1d771, 1, 668 }, { 0x1d772, 1, 1017 }, { 0x1d773, 1, 1018 }, { 0x1d774, 1, 677 }, { 0x1d775, 1, 5922 }, { 0x1d776, 1, 5923 }, { 0x1d777, 1, 669 }, { 0x1d778, 1, 2133 }, { 0x1d779, 1, 673 }, { 0x1d77a, 1, 5924 }, { 0x1d77b, 1, 10 }, { 0x1d77c, 1, 5925 }, { 0x1d77d, 1, 5926 }, { 0x1d77e, 1, 5927 }, { 0x1d77f, 1, 672 }, { 0x1d780, 1, 674 }, { 0x1d781, 1, 675 }, { 0x1d782, 1, 5928 }, { 0x1d783, 1, 5929 }, { 0x1d784, 1, 5930 }, { 0x1d785, 1, 671 }, { 0x1d786, 1, 1019 }, { 0x1d787, 1, 5931 }, { 0x1d788, 1, 5932 }, { 0x1d789, 1, 5933 }, { 0x1d78a, 1, 677 }, { 0x1d78b, 1, 669 }, { 0x1d78c, 1, 673 }, { 0x1d78d, 1, 671 }, { 0x1d78e, 1, 674 }, { 0x1d78f, 1, 672 }, { 0x1d790, 1, 5902 }, { 0x1d791, 1, 5903 }, { 0x1d792, 1, 2330 }, { 0x1d793, 1, 5904 }, { 0x1d794, 1, 5905 }, { 0x1d795, 1, 5906 }, { 0x1d796, 1, 5907 }, { 0x1d797, 1, 676 }, { 0x1d798, 1, 5908 }, { 0x1d799, 1, 5909 }, { 0x1d79a, 1, 5910 }, { 0x1d79b, 1, 5911 }, { 0x1d79c, 1, 5912 }, { 0x1d79d, 1, 5913 }, { 0x1d79e, 1, 5914 }, { 0x1d79f, 1, 2331 }, { 0x1d7a0, 1, 5915 }, { 0x1d7a1, 1, 676 }, { 0x1d7a2, 1, 678 }, { 0x1d7a3, 1, 5916 }, { 0x1d7a4, 1, 670 }, { 0x1d7a5, 1, 5917 }, { 0x1d7a6, 1, 5918 }, { 0x1d7a7, 1, 5919 }, { 0x1d7a8, 1, 2321 }, { 0x1d7a9, 1, 5920 }, { 0x1d7aa, 1, 5921 }, { 0x1d7ab, 1, 668 }, { 0x1d7ac, 1, 1017 }, { 0x1d7ad, 1, 1018 }, { 0x1d7ae, 1, 677 }, { 0x1d7af, 1, 5922 }, { 0x1d7b0, 1, 5923 }, { 0x1d7b1, 1, 669 }, { 0x1d7b2, 1, 2133 }, { 0x1d7b3, 1, 673 }, { 0x1d7b4, 1, 5924 }, { 0x1d7b5, 1, 10 }, { 0x1d7b6, 1, 5925 }, { 0x1d7b7, 1, 5926 }, { 0x1d7b8, 1, 5927 }, { 0x1d7b9, 1, 672 }, { 0x1d7ba, 1, 674 }, { 0x1d7bb, 1, 675 }, { 0x1d7bc, 1, 5928 }, { 0x1d7bd, 1, 5929 }, { 0x1d7be, 1, 5930 }, { 0x1d7bf, 1, 671 }, { 0x1d7c0, 1, 1019 }, { 0x1d7c1, 1, 5931 }, { 0x1d7c2, 1, 5932 }, { 0x1d7c3, 1, 5933 }, { 0x1d7c4, 1, 677 }, { 0x1d7c5, 1, 669 }, { 0x1d7c6, 1, 673 }, { 0x1d7c7, 1, 671 }, { 0x1d7c8, 1, 674 }, { 0x1d7c9, 1, 672 }, { 0x1d7ca, 1, 5934 }, { 0x1d7cb, 1, 5935 }, { 0x1d7ce, 1, 2276 }, { 0x1d7cf, 1, 13 }, { 0x1d7d0, 1, 6 }, { 0x1d7d1, 1, 7 }, { 0x1d7d2, 1, 2277 }, { 0x1d7d3, 1, 2278 }, { 0x1d7d4, 1, 2279 }, { 0x1d7d5, 1, 2280 }, { 0x1d7d6, 1, 2281 }, { 0x1d7d7, 1, 2282 }, { 0x1d7d8, 1, 2276 }, { 0x1d7d9, 1, 13 }, { 0x1d7da, 1, 6 }, { 0x1d7db, 1, 7 }, { 0x1d7dc, 1, 2277 }, { 0x1d7dd, 1, 2278 }, { 0x1d7de, 1, 2279 }, { 0x1d7df, 1, 2280 }, { 0x1d7e0, 1, 2281 }, { 0x1d7e1, 1, 2282 }, { 0x1d7e2, 1, 2276 }, { 0x1d7e3, 1, 13 }, { 0x1d7e4, 1, 6 }, { 0x1d7e5, 1, 7 }, { 0x1d7e6, 1, 2277 }, { 0x1d7e7, 1, 2278 }, { 0x1d7e8, 1, 2279 }, { 0x1d7e9, 1, 2280 }, { 0x1d7ea, 1, 2281 }, { 0x1d7eb, 1, 2282 }, { 0x1d7ec, 1, 2276 }, { 0x1d7ed, 1, 13 }, { 0x1d7ee, 1, 6 }, { 0x1d7ef, 1, 7 }, { 0x1d7f0, 1, 2277 }, { 0x1d7f1, 1, 2278 }, { 0x1d7f2, 1, 2279 }, { 0x1d7f3, 1, 2280 }, { 0x1d7f4, 1, 2281 }, { 0x1d7f5, 1, 2282 }, { 0x1d7f6, 1, 2276 }, { 0x1d7f7, 1, 13 }, { 0x1d7f8, 1, 6 }, { 0x1d7f9, 1, 7 }, { 0x1d7fa, 1, 2277 }, { 0x1d7fb, 1, 2278 }, { 0x1d7fc, 1, 2279 }, { 0x1d7fd, 1, 2280 }, { 0x1d7fe, 1, 2281 }, { 0x1d7ff, 1, 2282 }, { 0x1ee00, 1, 5766 }, { 0x1ee01, 1, 5767 }, { 0x1ee02, 1, 5771 }, { 0x1ee03, 1, 5774 }, { 0x1ee05, 1, 5793 }, { 0x1ee06, 1, 5777 }, { 0x1ee07, 1, 5772 }, { 0x1ee08, 1, 5782 }, { 0x1ee09, 1, 5794 }, { 0x1ee0a, 1, 5788 }, { 0x1ee0b, 1, 5789 }, { 0x1ee0c, 1, 5790 }, { 0x1ee0d, 1, 5791 }, { 0x1ee0e, 1, 5778 }, { 0x1ee0f, 1, 5784 }, { 0x1ee10, 1, 5786 }, { 0x1ee11, 1, 5780 }, { 0x1ee12, 1, 5787 }, { 0x1ee13, 1, 5776 }, { 0x1ee14, 1, 5779 }, { 0x1ee15, 1, 5769 }, { 0x1ee16, 1, 5770 }, { 0x1ee17, 1, 5773 }, { 0x1ee18, 1, 5775 }, { 0x1ee19, 1, 5781 }, { 0x1ee1a, 1, 5783 }, { 0x1ee1b, 1, 5785 }, { 0x1ee1c, 1, 5936 }, { 0x1ee1d, 1, 4972 }, { 0x1ee1e, 1, 5937 }, { 0x1ee1f, 1, 5938 }, { 0x1ee21, 1, 5767 }, { 0x1ee22, 1, 5771 }, { 0x1ee24, 1, 5792 }, { 0x1ee27, 1, 5772 }, { 0x1ee29, 1, 5794 }, { 0x1ee2a, 1, 5788 }, { 0x1ee2b, 1, 5789 }, { 0x1ee2c, 1, 5790 }, { 0x1ee2d, 1, 5791 }, { 0x1ee2e, 1, 5778 }, { 0x1ee2f, 1, 5784 }, { 0x1ee30, 1, 5786 }, { 0x1ee31, 1, 5780 }, { 0x1ee32, 1, 5787 }, { 0x1ee34, 1, 5779 }, { 0x1ee35, 1, 5769 }, { 0x1ee36, 1, 5770 }, { 0x1ee37, 1, 5773 }, { 0x1ee39, 1, 5781 }, { 0x1ee3b, 1, 5785 }, { 0x1ee42, 1, 5771 }, { 0x1ee47, 1, 5772 }, { 0x1ee49, 1, 5794 }, { 0x1ee4b, 1, 5789 }, { 0x1ee4d, 1, 5791 }, { 0x1ee4e, 1, 5778 }, { 0x1ee4f, 1, 5784 }, { 0x1ee51, 1, 5780 }, { 0x1ee52, 1, 5787 }, { 0x1ee54, 1, 5779 }, { 0x1ee57, 1, 5773 }, { 0x1ee59, 1, 5781 }, { 0x1ee5b, 1, 5785 }, { 0x1ee5d, 1, 4972 }, { 0x1ee5f, 1, 5938 }, { 0x1ee61, 1, 5767 }, { 0x1ee62, 1, 5771 }, { 0x1ee64, 1, 5792 }, { 0x1ee67, 1, 5772 }, { 0x1ee68, 1, 5782 }, { 0x1ee69, 1, 5794 }, { 0x1ee6a, 1, 5788 }, { 0x1ee6c, 1, 5790 }, { 0x1ee6d, 1, 5791 }, { 0x1ee6e, 1, 5778 }, { 0x1ee6f, 1, 5784 }, { 0x1ee70, 1, 5786 }, { 0x1ee71, 1, 5780 }, { 0x1ee72, 1, 5787 }, { 0x1ee74, 1, 5779 }, { 0x1ee75, 1, 5769 }, { 0x1ee76, 1, 5770 }, { 0x1ee77, 1, 5773 }, { 0x1ee79, 1, 5781 }, { 0x1ee7a, 1, 5783 }, { 0x1ee7b, 1, 5785 }, { 0x1ee7c, 1, 5936 }, { 0x1ee7e, 1, 5937 }, { 0x1ee80, 1, 5766 }, { 0x1ee81, 1, 5767 }, { 0x1ee82, 1, 5771 }, { 0x1ee83, 1, 5774 }, { 0x1ee84, 1, 5792 }, { 0x1ee85, 1, 5793 }, { 0x1ee86, 1, 5777 }, { 0x1ee87, 1, 5772 }, { 0x1ee88, 1, 5782 }, { 0x1ee89, 1, 5794 }, { 0x1ee8b, 1, 5789 }, { 0x1ee8c, 1, 5790 }, { 0x1ee8d, 1, 5791 }, { 0x1ee8e, 1, 5778 }, { 0x1ee8f, 1, 5784 }, { 0x1ee90, 1, 5786 }, { 0x1ee91, 1, 5780 }, { 0x1ee92, 1, 5787 }, { 0x1ee93, 1, 5776 }, { 0x1ee94, 1, 5779 }, { 0x1ee95, 1, 5769 }, { 0x1ee96, 1, 5770 }, { 0x1ee97, 1, 5773 }, { 0x1ee98, 1, 5775 }, { 0x1ee99, 1, 5781 }, { 0x1ee9a, 1, 5783 }, { 0x1ee9b, 1, 5785 }, { 0x1eea1, 1, 5767 }, { 0x1eea2, 1, 5771 }, { 0x1eea3, 1, 5774 }, { 0x1eea5, 1, 5793 }, { 0x1eea6, 1, 5777 }, { 0x1eea7, 1, 5772 }, { 0x1eea8, 1, 5782 }, { 0x1eea9, 1, 5794 }, { 0x1eeab, 1, 5789 }, { 0x1eeac, 1, 5790 }, { 0x1eead, 1, 5791 }, { 0x1eeae, 1, 5778 }, { 0x1eeaf, 1, 5784 }, { 0x1eeb0, 1, 5786 }, { 0x1eeb1, 1, 5780 }, { 0x1eeb2, 1, 5787 }, { 0x1eeb3, 1, 5776 }, { 0x1eeb4, 1, 5779 }, { 0x1eeb5, 1, 5769 }, { 0x1eeb6, 1, 5770 }, { 0x1eeb7, 1, 5773 }, { 0x1eeb8, 1, 5775 }, { 0x1eeb9, 1, 5781 }, { 0x1eeba, 1, 5783 }, { 0x1eebb, 1, 5785 }, { 0x1f100, 2, 5939 }, { 0x1f101, 2, 5941 }, { 0x1f102, 2, 5943 }, { 0x1f103, 2, 5945 }, { 0x1f104, 2, 5947 }, { 0x1f105, 2, 5949 }, { 0x1f106, 2, 5951 }, { 0x1f107, 2, 5953 }, { 0x1f108, 2, 5955 }, { 0x1f109, 2, 5957 }, { 0x1f10a, 2, 5959 }, { 0x1f110, 3, 5961 }, { 0x1f111, 3, 5964 }, { 0x1f112, 3, 5967 }, { 0x1f113, 3, 5970 }, { 0x1f114, 3, 5973 }, { 0x1f115, 3, 5976 }, { 0x1f116, 3, 5979 }, { 0x1f117, 3, 5982 }, { 0x1f118, 3, 5985 }, { 0x1f119, 3, 5988 }, { 0x1f11a, 3, 5991 }, { 0x1f11b, 3, 5994 }, { 0x1f11c, 3, 5997 }, { 0x1f11d, 3, 6000 }, { 0x1f11e, 3, 6003 }, { 0x1f11f, 3, 6006 }, { 0x1f120, 3, 6009 }, { 0x1f121, 3, 6012 }, { 0x1f122, 3, 6015 }, { 0x1f123, 3, 6018 }, { 0x1f124, 3, 6021 }, { 0x1f125, 3, 6024 }, { 0x1f126, 3, 6027 }, { 0x1f127, 3, 6030 }, { 0x1f128, 3, 6033 }, { 0x1f129, 3, 6036 }, { 0x1f12a, 3, 6039 }, { 0x1f12b, 1, 2297 }, { 0x1f12c, 1, 990 }, { 0x1f12d, 2, 6042 }, { 0x1f12e, 2, 6044 }, { 0x1f130, 1, 973 }, { 0x1f131, 1, 975 }, { 0x1f132, 1, 2297 }, { 0x1f133, 1, 976 }, { 0x1f134, 1, 977 }, { 0x1f135, 1, 2322 }, { 0x1f136, 1, 979 }, { 0x1f137, 1, 980 }, { 0x1f138, 1, 981 }, { 0x1f139, 1, 982 }, { 0x1f13a, 1, 983 }, { 0x1f13b, 1, 984 }, { 0x1f13c, 1, 985 }, { 0x1f13d, 1, 986 }, { 0x1f13e, 1, 987 }, { 0x1f13f, 1, 989 }, { 0x1f140, 1, 2312 }, { 0x1f141, 1, 990 }, { 0x1f142, 1, 2754 }, { 0x1f143, 1, 991 }, { 0x1f144, 1, 992 }, { 0x1f145, 1, 2388 }, { 0x1f146, 1, 993 }, { 0x1f147, 1, 2400 }, { 0x1f148, 1, 2755 }, { 0x1f149, 1, 2320 }, { 0x1f14a, 2, 6046 }, { 0x1f14b, 2, 4253 }, { 0x1f14c, 2, 6048 }, { 0x1f14d, 2, 6050 }, { 0x1f14e, 3, 6052 }, { 0x1f14f, 2, 6055 }, { 0x1f16a, 2, 6057 }, { 0x1f16b, 2, 6059 }, { 0x1f190, 2, 6061 }, { 0x1f200, 2, 6063 }, { 0x1f201, 2, 6065 }, { 0x1f202, 1, 3626 }, { 0x1f210, 1, 2837 }, { 0x1f211, 1, 6067 }, { 0x1f212, 1, 6068 }, { 0x1f213, 2, 3077 }, { 0x1f214, 1, 2780 }, { 0x1f215, 1, 6069 }, { 0x1f216, 1, 6070 }, { 0x1f217, 1, 3217 }, { 0x1f218, 1, 6071 }, { 0x1f219, 1, 6072 }, { 0x1f21a, 1, 6073 }, { 0x1f21b, 1, 4618 }, { 0x1f21c, 1, 6074 }, { 0x1f21d, 1, 6075 }, { 0x1f21e, 1, 6076 }, { 0x1f21f, 1, 6077 }, { 0x1f220, 1, 6078 }, { 0x1f221, 1, 6079 }, { 0x1f222, 1, 2873 }, { 0x1f223, 1, 6080 }, { 0x1f224, 1, 6081 }, { 0x1f225, 1, 6082 }, { 0x1f226, 1, 6083 }, { 0x1f227, 1, 6084 }, { 0x1f228, 1, 6085 }, { 0x1f229, 1, 2774 }, { 0x1f22a, 1, 3209 }, { 0x1f22b, 1, 6086 }, { 0x1f22c, 1, 3539 }, { 0x1f22d, 1, 3212 }, { 0x1f22e, 1, 3540 }, { 0x1f22f, 1, 6087 }, { 0x1f230, 1, 2929 }, { 0x1f231, 1, 6088 }, { 0x1f232, 1, 6089 }, { 0x1f233, 1, 6090 }, { 0x1f234, 1, 6091 }, { 0x1f235, 1, 6092 }, { 0x1f236, 1, 3522 }, { 0x1f237, 1, 2847 }, { 0x1f238, 1, 6093 }, { 0x1f239, 1, 6094 }, { 0x1f23a, 1, 6095 }, { 0x1f23b, 1, 6096 }, { 0x1f240, 3, 6097 }, { 0x1f241, 3, 6100 }, { 0x1f242, 3, 6103 }, { 0x1f243, 3, 6106 }, { 0x1f244, 3, 6109 }, { 0x1f245, 3, 6112 }, { 0x1f246, 3, 6115 }, { 0x1f247, 3, 6118 }, { 0x1f248, 3, 6121 }, { 0x1f250, 1, 6124 }, { 0x1f251, 1, 6125 }, { 0x2f800, 1, 6126 }, { 0x2f801, 1, 6127 }, { 0x2f802, 1, 6128 }, { 0x2f803, 1, 6129 }, { 0x2f804, 1, 6130 }, { 0x2f805, 1, 4711 }, { 0x2f806, 1, 6131 }, { 0x2f807, 1, 6132 }, { 0x2f808, 1, 6133 }, { 0x2f809, 1, 6134 }, { 0x2f80a, 1, 4712 }, { 0x2f80b, 1, 6135 }, { 0x2f80c, 1, 6136 }, { 0x2f80d, 1, 6137 }, { 0x2f80e, 1, 4713 }, { 0x2f80f, 1, 6138 }, { 0x2f810, 1, 6139 }, { 0x2f811, 1, 6140 }, { 0x2f812, 1, 6141 }, { 0x2f813, 1, 6142 }, { 0x2f814, 1, 6143 }, { 0x2f815, 1, 6076 }, { 0x2f816, 1, 6144 }, { 0x2f817, 1, 6145 }, { 0x2f818, 1, 6146 }, { 0x2f819, 1, 6147 }, { 0x2f81a, 1, 6148 }, { 0x2f81b, 1, 4768 }, { 0x2f81c, 1, 6149 }, { 0x2f81d, 1, 2790 }, { 0x2f81e, 1, 6150 }, { 0x2f81f, 1, 6151 }, { 0x2f820, 1, 6152 }, { 0x2f821, 1, 6153 }, { 0x2f822, 1, 6094 }, { 0x2f823, 1, 6154 }, { 0x2f824, 1, 6155 }, { 0x2f825, 1, 4773 }, { 0x2f826, 1, 4714 }, { 0x2f827, 1, 4715 }, { 0x2f828, 1, 4774 }, { 0x2f829, 1, 6156 }, { 0x2f82a, 1, 6157 }, { 0x2f82b, 1, 4532 }, { 0x2f82c, 1, 6158 }, { 0x2f82d, 1, 4716 }, { 0x2f82e, 1, 6159 }, { 0x2f82f, 1, 6160 }, { 0x2f830, 1, 6161 }, { 0x2f831, 1, 6162 }, { 0x2f832, 1, 6162 }, { 0x2f833, 1, 6162 }, { 0x2f834, 1, 6163 }, { 0x2f835, 1, 6164 }, { 0x2f836, 1, 6165 }, { 0x2f837, 1, 6166 }, { 0x2f838, 1, 6167 }, { 0x2f839, 1, 6168 }, { 0x2f83a, 1, 6169 }, { 0x2f83b, 1, 6170 }, { 0x2f83c, 1, 6171 }, { 0x2f83d, 1, 6172 }, { 0x2f83e, 1, 6173 }, { 0x2f83f, 1, 6174 }, { 0x2f840, 1, 6175 }, { 0x2f841, 1, 6176 }, { 0x2f842, 1, 6177 }, { 0x2f843, 1, 6178 }, { 0x2f844, 1, 6179 }, { 0x2f845, 1, 6180 }, { 0x2f846, 1, 6180 }, { 0x2f847, 1, 4776 }, { 0x2f848, 1, 6181 }, { 0x2f849, 1, 6182 }, { 0x2f84a, 1, 6183 }, { 0x2f84b, 1, 6184 }, { 0x2f84c, 1, 4718 }, { 0x2f84d, 1, 6185 }, { 0x2f84e, 1, 6186 }, { 0x2f84f, 1, 6187 }, { 0x2f850, 1, 4678 }, { 0x2f851, 1, 6188 }, { 0x2f852, 1, 6189 }, { 0x2f853, 1, 6190 }, { 0x2f854, 1, 6191 }, { 0x2f855, 1, 6192 }, { 0x2f856, 1, 6193 }, { 0x2f857, 1, 6194 }, { 0x2f858, 1, 6195 }, { 0x2f859, 1, 6196 }, { 0x2f85a, 1, 6197 }, { 0x2f85b, 1, 6198 }, { 0x2f85c, 1, 6199 }, { 0x2f85d, 1, 6069 }, { 0x2f85e, 1, 6200 }, { 0x2f85f, 1, 6201 }, { 0x2f860, 1, 6202 }, { 0x2f861, 1, 6203 }, { 0x2f862, 1, 6204 }, { 0x2f863, 1, 6205 }, { 0x2f864, 1, 6206 }, { 0x2f865, 1, 6207 }, { 0x2f866, 1, 6208 }, { 0x2f867, 1, 6209 }, { 0x2f868, 1, 6210 }, { 0x2f869, 1, 6211 }, { 0x2f86a, 1, 6212 }, { 0x2f86b, 1, 6212 }, { 0x2f86c, 1, 6213 }, { 0x2f86d, 1, 6214 }, { 0x2f86e, 1, 6215 }, { 0x2f86f, 1, 4528 }, { 0x2f870, 1, 6216 }, { 0x2f871, 1, 6217 }, { 0x2f872, 1, 6218 }, { 0x2f873, 1, 6219 }, { 0x2f874, 1, 6220 }, { 0x2f875, 1, 2816 }, { 0x2f876, 1, 6221 }, { 0x2f877, 1, 6222 }, { 0x2f878, 1, 2818 }, { 0x2f879, 1, 6223 }, { 0x2f87a, 1, 6224 }, { 0x2f87b, 1, 6225 }, { 0x2f87c, 1, 6226 }, { 0x2f87d, 1, 6227 }, { 0x2f87e, 1, 6228 }, { 0x2f87f, 1, 6229 }, { 0x2f880, 1, 6230 }, { 0x2f881, 1, 6231 }, { 0x2f882, 1, 6232 }, { 0x2f883, 1, 6233 }, { 0x2f884, 1, 6234 }, { 0x2f885, 1, 6235 }, { 0x2f886, 1, 6236 }, { 0x2f887, 1, 6237 }, { 0x2f888, 1, 6238 }, { 0x2f889, 1, 6239 }, { 0x2f88a, 1, 6240 }, { 0x2f88b, 1, 6241 }, { 0x2f88c, 1, 6242 }, { 0x2f88d, 1, 6243 }, { 0x2f88e, 1, 4476 }, { 0x2f88f, 1, 6244 }, { 0x2f890, 1, 2828 }, { 0x2f891, 1, 6245 }, { 0x2f892, 1, 6245 }, { 0x2f893, 1, 6246 }, { 0x2f894, 1, 6247 }, { 0x2f895, 1, 6247 }, { 0x2f896, 1, 6248 }, { 0x2f897, 1, 6249 }, { 0x2f898, 1, 6250 }, { 0x2f899, 1, 6251 }, { 0x2f89a, 1, 6252 }, { 0x2f89b, 1, 6253 }, { 0x2f89c, 1, 6254 }, { 0x2f89d, 1, 6255 }, { 0x2f89e, 1, 6256 }, { 0x2f89f, 1, 6257 }, { 0x2f8a0, 1, 6258 }, { 0x2f8a1, 1, 6259 }, { 0x2f8a2, 1, 6260 }, { 0x2f8a3, 1, 4723 }, { 0x2f8a4, 1, 6261 }, { 0x2f8a5, 1, 6262 }, { 0x2f8a6, 1, 6263 }, { 0x2f8a7, 1, 6264 }, { 0x2f8a8, 1, 4788 }, { 0x2f8a9, 1, 6264 }, { 0x2f8aa, 1, 6265 }, { 0x2f8ab, 1, 4725 }, { 0x2f8ac, 1, 6266 }, { 0x2f8ad, 1, 6267 }, { 0x2f8ae, 1, 6268 }, { 0x2f8af, 1, 6269 }, { 0x2f8b0, 1, 4726 }, { 0x2f8b1, 1, 4449 }, { 0x2f8b2, 1, 6270 }, { 0x2f8b3, 1, 6271 }, { 0x2f8b4, 1, 6272 }, { 0x2f8b5, 1, 6273 }, { 0x2f8b6, 1, 6274 }, { 0x2f8b7, 1, 6275 }, { 0x2f8b8, 1, 6276 }, { 0x2f8b9, 1, 6277 }, { 0x2f8ba, 1, 6278 }, { 0x2f8bb, 1, 6279 }, { 0x2f8bc, 1, 6280 }, { 0x2f8bd, 1, 6281 }, { 0x2f8be, 1, 6282 }, { 0x2f8bf, 1, 6283 }, { 0x2f8c0, 1, 6284 }, { 0x2f8c1, 1, 6285 }, { 0x2f8c2, 1, 6286 }, { 0x2f8c3, 1, 6287 }, { 0x2f8c4, 1, 6288 }, { 0x2f8c5, 1, 6289 }, { 0x2f8c6, 1, 6290 }, { 0x2f8c7, 1, 6291 }, { 0x2f8c8, 1, 4727 }, { 0x2f8c9, 1, 6292 }, { 0x2f8ca, 1, 6293 }, { 0x2f8cb, 1, 6294 }, { 0x2f8cc, 1, 6295 }, { 0x2f8cd, 1, 6296 }, { 0x2f8ce, 1, 6297 }, { 0x2f8cf, 1, 4729 }, { 0x2f8d0, 1, 6298 }, { 0x2f8d1, 1, 6299 }, { 0x2f8d2, 1, 6300 }, { 0x2f8d3, 1, 6301 }, { 0x2f8d4, 1, 6302 }, { 0x2f8d5, 1, 6303 }, { 0x2f8d6, 1, 6304 }, { 0x2f8d7, 1, 6305 }, { 0x2f8d8, 1, 4477 }, { 0x2f8d9, 1, 4796 }, { 0x2f8da, 1, 6306 }, { 0x2f8db, 1, 6307 }, { 0x2f8dc, 1, 6308 }, { 0x2f8dd, 1, 6309 }, { 0x2f8de, 1, 6310 }, { 0x2f8df, 1, 6311 }, { 0x2f8e0, 1, 6312 }, { 0x2f8e1, 1, 6313 }, { 0x2f8e2, 1, 4730 }, { 0x2f8e3, 1, 6314 }, { 0x2f8e4, 1, 6315 }, { 0x2f8e5, 1, 6316 }, { 0x2f8e6, 1, 6317 }, { 0x2f8e7, 1, 4838 }, { 0x2f8e8, 1, 6318 }, { 0x2f8e9, 1, 6319 }, { 0x2f8ea, 1, 6320 }, { 0x2f8eb, 1, 6321 }, { 0x2f8ec, 1, 6322 }, { 0x2f8ed, 1, 6323 }, { 0x2f8ee, 1, 6324 }, { 0x2f8ef, 1, 6325 }, { 0x2f8f0, 1, 6326 }, { 0x2f8f1, 1, 6327 }, { 0x2f8f2, 1, 6328 }, { 0x2f8f3, 1, 6329 }, { 0x2f8f4, 1, 6330 }, { 0x2f8f5, 1, 4545 }, { 0x2f8f6, 1, 6331 }, { 0x2f8f7, 1, 6332 }, { 0x2f8f8, 1, 6333 }, { 0x2f8f9, 1, 6334 }, { 0x2f8fa, 1, 6335 }, { 0x2f8fb, 1, 6336 }, { 0x2f8fc, 1, 6337 }, { 0x2f8fd, 1, 6338 }, { 0x2f8fe, 1, 6339 }, { 0x2f8ff, 1, 6340 }, { 0x2f900, 1, 6341 }, { 0x2f901, 1, 4731 }, { 0x2f902, 1, 4628 }, { 0x2f903, 1, 6342 }, { 0x2f904, 1, 6343 }, { 0x2f905, 1, 6344 }, { 0x2f906, 1, 6345 }, { 0x2f907, 1, 6346 }, { 0x2f908, 1, 6347 }, { 0x2f909, 1, 6348 }, { 0x2f90a, 1, 6349 }, { 0x2f90b, 1, 4799 }, { 0x2f90c, 1, 6350 }, { 0x2f90d, 1, 6351 }, { 0x2f90e, 1, 6352 }, { 0x2f90f, 1, 6353 }, { 0x2f910, 1, 6354 }, { 0x2f911, 1, 6355 }, { 0x2f912, 1, 6356 }, { 0x2f913, 1, 6357 }, { 0x2f914, 1, 4800 }, { 0x2f915, 1, 6358 }, { 0x2f916, 1, 6359 }, { 0x2f917, 1, 6360 }, { 0x2f918, 1, 6361 }, { 0x2f919, 1, 6362 }, { 0x2f91a, 1, 6363 }, { 0x2f91b, 1, 6364 }, { 0x2f91c, 1, 6365 }, { 0x2f91d, 1, 6366 }, { 0x2f91e, 1, 6367 }, { 0x2f91f, 1, 6368 }, { 0x2f920, 1, 6369 }, { 0x2f921, 1, 4802 }, { 0x2f922, 1, 6370 }, { 0x2f923, 1, 6371 }, { 0x2f924, 1, 6372 }, { 0x2f925, 1, 6373 }, { 0x2f926, 1, 6374 }, { 0x2f927, 1, 6375 }, { 0x2f928, 1, 6376 }, { 0x2f929, 1, 6377 }, { 0x2f92a, 1, 6378 }, { 0x2f92b, 1, 6379 }, { 0x2f92c, 1, 6380 }, { 0x2f92d, 1, 6380 }, { 0x2f92e, 1, 6381 }, { 0x2f92f, 1, 6382 }, { 0x2f930, 1, 4804 }, { 0x2f931, 1, 6383 }, { 0x2f932, 1, 6384 }, { 0x2f933, 1, 6385 }, { 0x2f934, 1, 6386 }, { 0x2f935, 1, 6387 }, { 0x2f936, 1, 6388 }, { 0x2f937, 1, 6389 }, { 0x2f938, 1, 4531 }, { 0x2f939, 1, 6390 }, { 0x2f93a, 1, 6391 }, { 0x2f93b, 1, 6392 }, { 0x2f93c, 1, 6393 }, { 0x2f93d, 1, 6394 }, { 0x2f93e, 1, 6395 }, { 0x2f93f, 1, 6396 }, { 0x2f940, 1, 4810 }, { 0x2f941, 1, 6397 }, { 0x2f942, 1, 6398 }, { 0x2f943, 1, 6399 }, { 0x2f944, 1, 6400 }, { 0x2f945, 1, 6401 }, { 0x2f946, 1, 6402 }, { 0x2f947, 1, 6402 }, { 0x2f948, 1, 4811 }, { 0x2f949, 1, 4840 }, { 0x2f94a, 1, 6403 }, { 0x2f94b, 1, 6404 }, { 0x2f94c, 1, 6405 }, { 0x2f94d, 1, 6406 }, { 0x2f94e, 1, 6407 }, { 0x2f94f, 1, 4494 }, { 0x2f950, 1, 4813 }, { 0x2f951, 1, 6408 }, { 0x2f952, 1, 6409 }, { 0x2f953, 1, 4741 }, { 0x2f954, 1, 6410 }, { 0x2f955, 1, 6411 }, { 0x2f956, 1, 4698 }, { 0x2f957, 1, 6412 }, { 0x2f958, 1, 6413 }, { 0x2f959, 1, 4744 }, { 0x2f95a, 1, 6414 }, { 0x2f95b, 1, 6415 }, { 0x2f95c, 1, 6416 }, { 0x2f95d, 1, 6417 }, { 0x2f95e, 1, 6417 }, { 0x2f95f, 1, 6418 }, { 0x2f960, 1, 6419 }, { 0x2f961, 1, 6420 }, { 0x2f962, 1, 6421 }, { 0x2f963, 1, 6422 }, { 0x2f964, 1, 6423 }, { 0x2f965, 1, 6424 }, { 0x2f966, 1, 6425 }, { 0x2f967, 1, 6426 }, { 0x2f968, 1, 6427 }, { 0x2f969, 1, 6428 }, { 0x2f96a, 1, 6429 }, { 0x2f96b, 1, 6430 }, { 0x2f96c, 1, 6431 }, { 0x2f96d, 1, 6432 }, { 0x2f96e, 1, 6433 }, { 0x2f96f, 1, 6434 }, { 0x2f970, 1, 6435 }, { 0x2f971, 1, 6436 }, { 0x2f972, 1, 6437 }, { 0x2f973, 1, 6438 }, { 0x2f974, 1, 6439 }, { 0x2f975, 1, 6440 }, { 0x2f976, 1, 6441 }, { 0x2f977, 1, 6442 }, { 0x2f978, 1, 6443 }, { 0x2f979, 1, 6444 }, { 0x2f97a, 1, 4750 }, { 0x2f97b, 1, 6445 }, { 0x2f97c, 1, 6446 }, { 0x2f97d, 1, 6447 }, { 0x2f97e, 1, 6448 }, { 0x2f97f, 1, 6449 }, { 0x2f980, 1, 6450 }, { 0x2f981, 1, 6451 }, { 0x2f982, 1, 6452 }, { 0x2f983, 1, 6453 }, { 0x2f984, 1, 6454 }, { 0x2f985, 1, 6455 }, { 0x2f986, 1, 6456 }, { 0x2f987, 1, 6457 }, { 0x2f988, 1, 6458 }, { 0x2f989, 1, 6459 }, { 0x2f98a, 1, 6460 }, { 0x2f98b, 1, 6246 }, { 0x2f98c, 1, 6461 }, { 0x2f98d, 1, 6462 }, { 0x2f98e, 1, 6463 }, { 0x2f98f, 1, 6464 }, { 0x2f990, 1, 6465 }, { 0x2f991, 1, 6466 }, { 0x2f992, 1, 6467 }, { 0x2f993, 1, 6468 }, { 0x2f994, 1, 6469 }, { 0x2f995, 1, 6470 }, { 0x2f996, 1, 6471 }, { 0x2f997, 1, 6472 }, { 0x2f998, 1, 4548 }, { 0x2f999, 1, 6473 }, { 0x2f99a, 1, 6474 }, { 0x2f99b, 1, 6475 }, { 0x2f99c, 1, 6476 }, { 0x2f99d, 1, 6477 }, { 0x2f99e, 1, 6478 }, { 0x2f99f, 1, 4753 }, { 0x2f9a0, 1, 6479 }, { 0x2f9a1, 1, 6480 }, { 0x2f9a2, 1, 6481 }, { 0x2f9a3, 1, 6482 }, { 0x2f9a4, 1, 6483 }, { 0x2f9a5, 1, 6484 }, { 0x2f9a6, 1, 6485 }, { 0x2f9a7, 1, 6486 }, { 0x2f9a8, 1, 6487 }, { 0x2f9a9, 1, 6488 }, { 0x2f9aa, 1, 6489 }, { 0x2f9ab, 1, 6490 }, { 0x2f9ac, 1, 6491 }, { 0x2f9ad, 1, 6492 }, { 0x2f9ae, 1, 6493 }, { 0x2f9af, 1, 6494 }, { 0x2f9b0, 1, 6495 }, { 0x2f9b1, 1, 6496 }, { 0x2f9b2, 1, 6497 }, { 0x2f9b3, 1, 6498 }, { 0x2f9b4, 1, 4489 }, { 0x2f9b5, 1, 6499 }, { 0x2f9b6, 1, 6500 }, { 0x2f9b7, 1, 6501 }, { 0x2f9b8, 1, 6502 }, { 0x2f9b9, 1, 6503 }, { 0x2f9ba, 1, 6504 }, { 0x2f9bb, 1, 4820 }, { 0x2f9bc, 1, 6505 }, { 0x2f9bd, 1, 6506 }, { 0x2f9be, 1, 6507 }, { 0x2f9bf, 1, 6508 }, { 0x2f9c0, 1, 6509 }, { 0x2f9c1, 1, 6510 }, { 0x2f9c2, 1, 6511 }, { 0x2f9c3, 1, 6512 }, { 0x2f9c4, 1, 2918 }, { 0x2f9c5, 1, 6513 }, { 0x2f9c6, 1, 6514 }, { 0x2f9c7, 1, 6515 }, { 0x2f9c8, 1, 6516 }, { 0x2f9c9, 1, 6517 }, { 0x2f9ca, 1, 6518 }, { 0x2f9cb, 1, 6519 }, { 0x2f9cc, 1, 6520 }, { 0x2f9cd, 1, 6521 }, { 0x2f9ce, 1, 6522 }, { 0x2f9cf, 1, 6523 }, { 0x2f9d0, 1, 4825 }, { 0x2f9d1, 1, 4826 }, { 0x2f9d2, 1, 2925 }, { 0x2f9d3, 1, 6524 }, { 0x2f9d4, 1, 6525 }, { 0x2f9d5, 1, 6526 }, { 0x2f9d6, 1, 6527 }, { 0x2f9d7, 1, 6528 }, { 0x2f9d8, 1, 6529 }, { 0x2f9d9, 1, 6530 }, { 0x2f9da, 1, 6531 }, { 0x2f9db, 1, 6532 }, { 0x2f9dc, 1, 6533 }, { 0x2f9dd, 1, 6534 }, { 0x2f9de, 1, 6535 }, { 0x2f9df, 1, 4827 }, { 0x2f9e0, 1, 6536 }, { 0x2f9e1, 1, 6537 }, { 0x2f9e2, 1, 6538 }, { 0x2f9e3, 1, 6539 }, { 0x2f9e4, 1, 6540 }, { 0x2f9e5, 1, 6541 }, { 0x2f9e6, 1, 6542 }, { 0x2f9e7, 1, 6543 }, { 0x2f9e8, 1, 6544 }, { 0x2f9e9, 1, 6545 }, { 0x2f9ea, 1, 6546 }, { 0x2f9eb, 1, 6547 }, { 0x2f9ec, 1, 6548 }, { 0x2f9ed, 1, 6549 }, { 0x2f9ee, 1, 6550 }, { 0x2f9ef, 1, 6551 }, { 0x2f9f0, 1, 6552 }, { 0x2f9f1, 1, 6553 }, { 0x2f9f2, 1, 6554 }, { 0x2f9f3, 1, 6555 }, { 0x2f9f4, 1, 6556 }, { 0x2f9f5, 1, 6557 }, { 0x2f9f6, 1, 6558 }, { 0x2f9f7, 1, 6559 }, { 0x2f9f8, 1, 6560 }, { 0x2f9f9, 1, 6561 }, { 0x2f9fa, 1, 6562 }, { 0x2f9fb, 1, 6563 }, { 0x2f9fc, 1, 6564 }, { 0x2f9fd, 1, 6565 }, { 0x2f9fe, 1, 4833 }, { 0x2f9ff, 1, 4833 }, { 0x2fa00, 1, 6566 }, { 0x2fa01, 1, 6567 }, { 0x2fa02, 1, 6568 }, { 0x2fa03, 1, 6569 }, { 0x2fa04, 1, 6570 }, { 0x2fa05, 1, 6571 }, { 0x2fa06, 1, 6572 }, { 0x2fa07, 1, 6573 }, { 0x2fa08, 1, 6574 }, { 0x2fa09, 1, 6575 }, { 0x2fa0a, 1, 4834 }, { 0x2fa0b, 1, 6576 }, { 0x2fa0c, 1, 6577 }, { 0x2fa0d, 1, 6578 }, { 0x2fa0e, 1, 6579 }, { 0x2fa0f, 1, 6580 }, { 0x2fa10, 1, 6581 }, { 0x2fa11, 1, 6582 }, { 0x2fa12, 1, 6583 }, { 0x2fa13, 1, 6584 }, { 0x2fa14, 1, 6585 }, { 0x2fa15, 1, 2973 }, { 0x2fa16, 1, 6586 }, { 0x2fa17, 1, 2977 }, { 0x2fa18, 1, 6587 }, { 0x2fa19, 1, 6588 }, { 0x2fa1a, 1, 6589 }, { 0x2fa1b, 1, 6590 }, { 0x2fa1c, 1, 2982 }, { 0x2fa1d, 1, 6591 } }; static const Unicode decomp_expansion[] = { 0x20 /* offset 0 */, 0x20, 0x308 /* offset 1 */, 0x61 /* offset 3 */, 0x20, 0x304 /* offset 4 */, 0x32 /* offset 6 */, 0x33 /* offset 7 */, 0x20, 0x301 /* offset 8 */, 0x3bc /* offset 10 */, 0x20, 0x327 /* offset 11 */, 0x31 /* offset 13 */, 0x6f /* offset 14 */, 0x31, 0x2044, 0x34 /* offset 15 */, 0x31, 0x2044, 0x32 /* offset 18 */, 0x33, 0x2044, 0x34 /* offset 21 */, 0x41, 0x300 /* offset 24 */, 0x41, 0x301 /* offset 26 */, 0x41, 0x302 /* offset 28 */, 0x41, 0x303 /* offset 30 */, 0x41, 0x308 /* offset 32 */, 0x41, 0x30a /* offset 34 */, 0x43, 0x327 /* offset 36 */, 0x45, 0x300 /* offset 38 */, 0x45, 0x301 /* offset 40 */, 0x45, 0x302 /* offset 42 */, 0x45, 0x308 /* offset 44 */, 0x49, 0x300 /* offset 46 */, 0x49, 0x301 /* offset 48 */, 0x49, 0x302 /* offset 50 */, 0x49, 0x308 /* offset 52 */, 0x4e, 0x303 /* offset 54 */, 0x4f, 0x300 /* offset 56 */, 0x4f, 0x301 /* offset 58 */, 0x4f, 0x302 /* offset 60 */, 0x4f, 0x303 /* offset 62 */, 0x4f, 0x308 /* offset 64 */, 0x55, 0x300 /* offset 66 */, 0x55, 0x301 /* offset 68 */, 0x55, 0x302 /* offset 70 */, 0x55, 0x308 /* offset 72 */, 0x59, 0x301 /* offset 74 */, 0x61, 0x300 /* offset 76 */, 0x61, 0x301 /* offset 78 */, 0x61, 0x302 /* offset 80 */, 0x61, 0x303 /* offset 82 */, 0x61, 0x308 /* offset 84 */, 0x61, 0x30a /* offset 86 */, 0x63, 0x327 /* offset 88 */, 0x65, 0x300 /* offset 90 */, 0x65, 0x301 /* offset 92 */, 0x65, 0x302 /* offset 94 */, 0x65, 0x308 /* offset 96 */, 0x69, 0x300 /* offset 98 */, 0x69, 0x301 /* offset 100 */, 0x69, 0x302 /* offset 102 */, 0x69, 0x308 /* offset 104 */, 0x6e, 0x303 /* offset 106 */, 0x6f, 0x300 /* offset 108 */, 0x6f, 0x301 /* offset 110 */, 0x6f, 0x302 /* offset 112 */, 0x6f, 0x303 /* offset 114 */, 0x6f, 0x308 /* offset 116 */, 0x75, 0x300 /* offset 118 */, 0x75, 0x301 /* offset 120 */, 0x75, 0x302 /* offset 122 */, 0x75, 0x308 /* offset 124 */, 0x79, 0x301 /* offset 126 */, 0x79, 0x308 /* offset 128 */, 0x41, 0x304 /* offset 130 */, 0x61, 0x304 /* offset 132 */, 0x41, 0x306 /* offset 134 */, 0x61, 0x306 /* offset 136 */, 0x41, 0x328 /* offset 138 */, 0x61, 0x328 /* offset 140 */, 0x43, 0x301 /* offset 142 */, 0x63, 0x301 /* offset 144 */, 0x43, 0x302 /* offset 146 */, 0x63, 0x302 /* offset 148 */, 0x43, 0x307 /* offset 150 */, 0x63, 0x307 /* offset 152 */, 0x43, 0x30c /* offset 154 */, 0x63, 0x30c /* offset 156 */, 0x44, 0x30c /* offset 158 */, 0x64, 0x30c /* offset 160 */, 0x45, 0x304 /* offset 162 */, 0x65, 0x304 /* offset 164 */, 0x45, 0x306 /* offset 166 */, 0x65, 0x306 /* offset 168 */, 0x45, 0x307 /* offset 170 */, 0x65, 0x307 /* offset 172 */, 0x45, 0x328 /* offset 174 */, 0x65, 0x328 /* offset 176 */, 0x45, 0x30c /* offset 178 */, 0x65, 0x30c /* offset 180 */, 0x47, 0x302 /* offset 182 */, 0x67, 0x302 /* offset 184 */, 0x47, 0x306 /* offset 186 */, 0x67, 0x306 /* offset 188 */, 0x47, 0x307 /* offset 190 */, 0x67, 0x307 /* offset 192 */, 0x47, 0x327 /* offset 194 */, 0x67, 0x327 /* offset 196 */, 0x48, 0x302 /* offset 198 */, 0x68, 0x302 /* offset 200 */, 0x49, 0x303 /* offset 202 */, 0x69, 0x303 /* offset 204 */, 0x49, 0x304 /* offset 206 */, 0x69, 0x304 /* offset 208 */, 0x49, 0x306 /* offset 210 */, 0x69, 0x306 /* offset 212 */, 0x49, 0x328 /* offset 214 */, 0x69, 0x328 /* offset 216 */, 0x49, 0x307 /* offset 218 */, 0x49, 0x4a /* offset 220 */, 0x69, 0x6a /* offset 222 */, 0x4a, 0x302 /* offset 224 */, 0x6a, 0x302 /* offset 226 */, 0x4b, 0x327 /* offset 228 */, 0x6b, 0x327 /* offset 230 */, 0x4c, 0x301 /* offset 232 */, 0x6c, 0x301 /* offset 234 */, 0x4c, 0x327 /* offset 236 */, 0x6c, 0x327 /* offset 238 */, 0x4c, 0x30c /* offset 240 */, 0x6c, 0x30c /* offset 242 */, 0x4c, 0xb7 /* offset 244 */, 0x6c, 0xb7 /* offset 246 */, 0x4e, 0x301 /* offset 248 */, 0x6e, 0x301 /* offset 250 */, 0x4e, 0x327 /* offset 252 */, 0x6e, 0x327 /* offset 254 */, 0x4e, 0x30c /* offset 256 */, 0x6e, 0x30c /* offset 258 */, 0x2bc, 0x6e /* offset 260 */, 0x4f, 0x304 /* offset 262 */, 0x6f, 0x304 /* offset 264 */, 0x4f, 0x306 /* offset 266 */, 0x6f, 0x306 /* offset 268 */, 0x4f, 0x30b /* offset 270 */, 0x6f, 0x30b /* offset 272 */, 0x52, 0x301 /* offset 274 */, 0x72, 0x301 /* offset 276 */, 0x52, 0x327 /* offset 278 */, 0x72, 0x327 /* offset 280 */, 0x52, 0x30c /* offset 282 */, 0x72, 0x30c /* offset 284 */, 0x53, 0x301 /* offset 286 */, 0x73, 0x301 /* offset 288 */, 0x53, 0x302 /* offset 290 */, 0x73, 0x302 /* offset 292 */, 0x53, 0x327 /* offset 294 */, 0x73, 0x327 /* offset 296 */, 0x53, 0x30c /* offset 298 */, 0x73, 0x30c /* offset 300 */, 0x54, 0x327 /* offset 302 */, 0x74, 0x327 /* offset 304 */, 0x54, 0x30c /* offset 306 */, 0x74, 0x30c /* offset 308 */, 0x55, 0x303 /* offset 310 */, 0x75, 0x303 /* offset 312 */, 0x55, 0x304 /* offset 314 */, 0x75, 0x304 /* offset 316 */, 0x55, 0x306 /* offset 318 */, 0x75, 0x306 /* offset 320 */, 0x55, 0x30a /* offset 322 */, 0x75, 0x30a /* offset 324 */, 0x55, 0x30b /* offset 326 */, 0x75, 0x30b /* offset 328 */, 0x55, 0x328 /* offset 330 */, 0x75, 0x328 /* offset 332 */, 0x57, 0x302 /* offset 334 */, 0x77, 0x302 /* offset 336 */, 0x59, 0x302 /* offset 338 */, 0x79, 0x302 /* offset 340 */, 0x59, 0x308 /* offset 342 */, 0x5a, 0x301 /* offset 344 */, 0x7a, 0x301 /* offset 346 */, 0x5a, 0x307 /* offset 348 */, 0x7a, 0x307 /* offset 350 */, 0x5a, 0x30c /* offset 352 */, 0x7a, 0x30c /* offset 354 */, 0x73 /* offset 356 */, 0x4f, 0x31b /* offset 357 */, 0x6f, 0x31b /* offset 359 */, 0x55, 0x31b /* offset 361 */, 0x75, 0x31b /* offset 363 */, 0x44, 0x5a, 0x30c /* offset 365 */, 0x44, 0x7a, 0x30c /* offset 368 */, 0x64, 0x7a, 0x30c /* offset 371 */, 0x4c, 0x4a /* offset 374 */, 0x4c, 0x6a /* offset 376 */, 0x6c, 0x6a /* offset 378 */, 0x4e, 0x4a /* offset 380 */, 0x4e, 0x6a /* offset 382 */, 0x6e, 0x6a /* offset 384 */, 0x41, 0x30c /* offset 386 */, 0x61, 0x30c /* offset 388 */, 0x49, 0x30c /* offset 390 */, 0x69, 0x30c /* offset 392 */, 0x4f, 0x30c /* offset 394 */, 0x6f, 0x30c /* offset 396 */, 0x55, 0x30c /* offset 398 */, 0x75, 0x30c /* offset 400 */, 0x55, 0x308, 0x304 /* offset 402 */, 0x75, 0x308, 0x304 /* offset 405 */, 0x55, 0x308, 0x301 /* offset 408 */, 0x75, 0x308, 0x301 /* offset 411 */, 0x55, 0x308, 0x30c /* offset 414 */, 0x75, 0x308, 0x30c /* offset 417 */, 0x55, 0x308, 0x300 /* offset 420 */, 0x75, 0x308, 0x300 /* offset 423 */, 0x41, 0x308, 0x304 /* offset 426 */, 0x61, 0x308, 0x304 /* offset 429 */, 0x41, 0x307, 0x304 /* offset 432 */, 0x61, 0x307, 0x304 /* offset 435 */, 0xc6, 0x304 /* offset 438 */, 0xe6, 0x304 /* offset 440 */, 0x47, 0x30c /* offset 442 */, 0x67, 0x30c /* offset 444 */, 0x4b, 0x30c /* offset 446 */, 0x6b, 0x30c /* offset 448 */, 0x4f, 0x328 /* offset 450 */, 0x6f, 0x328 /* offset 452 */, 0x4f, 0x328, 0x304 /* offset 454 */, 0x6f, 0x328, 0x304 /* offset 457 */, 0x1b7, 0x30c /* offset 460 */, 0x292, 0x30c /* offset 462 */, 0x6a, 0x30c /* offset 464 */, 0x44, 0x5a /* offset 466 */, 0x44, 0x7a /* offset 468 */, 0x64, 0x7a /* offset 470 */, 0x47, 0x301 /* offset 472 */, 0x67, 0x301 /* offset 474 */, 0x4e, 0x300 /* offset 476 */, 0x6e, 0x300 /* offset 478 */, 0x41, 0x30a, 0x301 /* offset 480 */, 0x61, 0x30a, 0x301 /* offset 483 */, 0xc6, 0x301 /* offset 486 */, 0xe6, 0x301 /* offset 488 */, 0xd8, 0x301 /* offset 490 */, 0xf8, 0x301 /* offset 492 */, 0x41, 0x30f /* offset 494 */, 0x61, 0x30f /* offset 496 */, 0x41, 0x311 /* offset 498 */, 0x61, 0x311 /* offset 500 */, 0x45, 0x30f /* offset 502 */, 0x65, 0x30f /* offset 504 */, 0x45, 0x311 /* offset 506 */, 0x65, 0x311 /* offset 508 */, 0x49, 0x30f /* offset 510 */, 0x69, 0x30f /* offset 512 */, 0x49, 0x311 /* offset 514 */, 0x69, 0x311 /* offset 516 */, 0x4f, 0x30f /* offset 518 */, 0x6f, 0x30f /* offset 520 */, 0x4f, 0x311 /* offset 522 */, 0x6f, 0x311 /* offset 524 */, 0x52, 0x30f /* offset 526 */, 0x72, 0x30f /* offset 528 */, 0x52, 0x311 /* offset 530 */, 0x72, 0x311 /* offset 532 */, 0x55, 0x30f /* offset 534 */, 0x75, 0x30f /* offset 536 */, 0x55, 0x311 /* offset 538 */, 0x75, 0x311 /* offset 540 */, 0x53, 0x326 /* offset 542 */, 0x73, 0x326 /* offset 544 */, 0x54, 0x326 /* offset 546 */, 0x74, 0x326 /* offset 548 */, 0x48, 0x30c /* offset 550 */, 0x68, 0x30c /* offset 552 */, 0x41, 0x307 /* offset 554 */, 0x61, 0x307 /* offset 556 */, 0x45, 0x327 /* offset 558 */, 0x65, 0x327 /* offset 560 */, 0x4f, 0x308, 0x304 /* offset 562 */, 0x6f, 0x308, 0x304 /* offset 565 */, 0x4f, 0x303, 0x304 /* offset 568 */, 0x6f, 0x303, 0x304 /* offset 571 */, 0x4f, 0x307 /* offset 574 */, 0x6f, 0x307 /* offset 576 */, 0x4f, 0x307, 0x304 /* offset 578 */, 0x6f, 0x307, 0x304 /* offset 581 */, 0x59, 0x304 /* offset 584 */, 0x79, 0x304 /* offset 586 */, 0x68 /* offset 588 */, 0x266 /* offset 589 */, 0x6a /* offset 590 */, 0x72 /* offset 591 */, 0x279 /* offset 592 */, 0x27b /* offset 593 */, 0x281 /* offset 594 */, 0x77 /* offset 595 */, 0x79 /* offset 596 */, 0x20, 0x306 /* offset 597 */, 0x20, 0x307 /* offset 599 */, 0x20, 0x30a /* offset 601 */, 0x20, 0x328 /* offset 603 */, 0x20, 0x303 /* offset 605 */, 0x20, 0x30b /* offset 607 */, 0x263 /* offset 609 */, 0x6c /* offset 610 */, 0x78 /* offset 611 */, 0x295 /* offset 612 */, 0x300 /* offset 613 */, 0x301 /* offset 614 */, 0x313 /* offset 615 */, 0x308, 0x301 /* offset 616 */, 0x2b9 /* offset 618 */, 0x20, 0x345 /* offset 619 */, 0x3b /* offset 621 */, 0x20, 0x308, 0x301 /* offset 622 */, 0x391, 0x301 /* offset 625 */, 0xb7 /* offset 627 */, 0x395, 0x301 /* offset 628 */, 0x397, 0x301 /* offset 630 */, 0x399, 0x301 /* offset 632 */, 0x39f, 0x301 /* offset 634 */, 0x3a5, 0x301 /* offset 636 */, 0x3a9, 0x301 /* offset 638 */, 0x3b9, 0x308, 0x301 /* offset 640 */, 0x399, 0x308 /* offset 643 */, 0x3a5, 0x308 /* offset 645 */, 0x3b1, 0x301 /* offset 647 */, 0x3b5, 0x301 /* offset 649 */, 0x3b7, 0x301 /* offset 651 */, 0x3b9, 0x301 /* offset 653 */, 0x3c5, 0x308, 0x301 /* offset 655 */, 0x3b9, 0x308 /* offset 658 */, 0x3c5, 0x308 /* offset 660 */, 0x3bf, 0x301 /* offset 662 */, 0x3c5, 0x301 /* offset 664 */, 0x3c9, 0x301 /* offset 666 */, 0x3b2 /* offset 668 */, 0x3b8 /* offset 669 */, 0x3a5 /* offset 670 */, 0x3c6 /* offset 671 */, 0x3c0 /* offset 672 */, 0x3ba /* offset 673 */, 0x3c1 /* offset 674 */, 0x3c2 /* offset 675 */, 0x398 /* offset 676 */, 0x3b5 /* offset 677 */, 0x3a3 /* offset 678 */, 0x415, 0x300 /* offset 679 */, 0x415, 0x308 /* offset 681 */, 0x413, 0x301 /* offset 683 */, 0x406, 0x308 /* offset 685 */, 0x41a, 0x301 /* offset 687 */, 0x418, 0x300 /* offset 689 */, 0x423, 0x306 /* offset 691 */, 0x418, 0x306 /* offset 693 */, 0x438, 0x306 /* offset 695 */, 0x435, 0x300 /* offset 697 */, 0x435, 0x308 /* offset 699 */, 0x433, 0x301 /* offset 701 */, 0x456, 0x308 /* offset 703 */, 0x43a, 0x301 /* offset 705 */, 0x438, 0x300 /* offset 707 */, 0x443, 0x306 /* offset 709 */, 0x474, 0x30f /* offset 711 */, 0x475, 0x30f /* offset 713 */, 0x416, 0x306 /* offset 715 */, 0x436, 0x306 /* offset 717 */, 0x410, 0x306 /* offset 719 */, 0x430, 0x306 /* offset 721 */, 0x410, 0x308 /* offset 723 */, 0x430, 0x308 /* offset 725 */, 0x415, 0x306 /* offset 727 */, 0x435, 0x306 /* offset 729 */, 0x4d8, 0x308 /* offset 731 */, 0x4d9, 0x308 /* offset 733 */, 0x416, 0x308 /* offset 735 */, 0x436, 0x308 /* offset 737 */, 0x417, 0x308 /* offset 739 */, 0x437, 0x308 /* offset 741 */, 0x418, 0x304 /* offset 743 */, 0x438, 0x304 /* offset 745 */, 0x418, 0x308 /* offset 747 */, 0x438, 0x308 /* offset 749 */, 0x41e, 0x308 /* offset 751 */, 0x43e, 0x308 /* offset 753 */, 0x4e8, 0x308 /* offset 755 */, 0x4e9, 0x308 /* offset 757 */, 0x42d, 0x308 /* offset 759 */, 0x44d, 0x308 /* offset 761 */, 0x423, 0x304 /* offset 763 */, 0x443, 0x304 /* offset 765 */, 0x423, 0x308 /* offset 767 */, 0x443, 0x308 /* offset 769 */, 0x423, 0x30b /* offset 771 */, 0x443, 0x30b /* offset 773 */, 0x427, 0x308 /* offset 775 */, 0x447, 0x308 /* offset 777 */, 0x42b, 0x308 /* offset 779 */, 0x44b, 0x308 /* offset 781 */, 0x565, 0x582 /* offset 783 */, 0x627, 0x653 /* offset 785 */, 0x627, 0x654 /* offset 787 */, 0x648, 0x654 /* offset 789 */, 0x627, 0x655 /* offset 791 */, 0x64a, 0x654 /* offset 793 */, 0x627, 0x674 /* offset 795 */, 0x648, 0x674 /* offset 797 */, 0x6c7, 0x674 /* offset 799 */, 0x64a, 0x674 /* offset 801 */, 0x6d5, 0x654 /* offset 803 */, 0x6c1, 0x654 /* offset 805 */, 0x6d2, 0x654 /* offset 807 */, 0x928, 0x93c /* offset 809 */, 0x930, 0x93c /* offset 811 */, 0x933, 0x93c /* offset 813 */, 0x915, 0x93c /* offset 815 */, 0x916, 0x93c /* offset 817 */, 0x917, 0x93c /* offset 819 */, 0x91c, 0x93c /* offset 821 */, 0x921, 0x93c /* offset 823 */, 0x922, 0x93c /* offset 825 */, 0x92b, 0x93c /* offset 827 */, 0x92f, 0x93c /* offset 829 */, 0x9c7, 0x9be /* offset 831 */, 0x9c7, 0x9d7 /* offset 833 */, 0x9a1, 0x9bc /* offset 835 */, 0x9a2, 0x9bc /* offset 837 */, 0x9af, 0x9bc /* offset 839 */, 0xa32, 0xa3c /* offset 841 */, 0xa38, 0xa3c /* offset 843 */, 0xa16, 0xa3c /* offset 845 */, 0xa17, 0xa3c /* offset 847 */, 0xa1c, 0xa3c /* offset 849 */, 0xa2b, 0xa3c /* offset 851 */, 0xb47, 0xb56 /* offset 853 */, 0xb47, 0xb3e /* offset 855 */, 0xb47, 0xb57 /* offset 857 */, 0xb21, 0xb3c /* offset 859 */, 0xb22, 0xb3c /* offset 861 */, 0xb92, 0xbd7 /* offset 863 */, 0xbc6, 0xbbe /* offset 865 */, 0xbc7, 0xbbe /* offset 867 */, 0xbc6, 0xbd7 /* offset 869 */, 0xc46, 0xc56 /* offset 871 */, 0xcbf, 0xcd5 /* offset 873 */, 0xcc6, 0xcd5 /* offset 875 */, 0xcc6, 0xcd6 /* offset 877 */, 0xcc6, 0xcc2 /* offset 879 */, 0xcc6, 0xcc2, 0xcd5 /* offset 881 */, 0xd46, 0xd3e /* offset 884 */, 0xd47, 0xd3e /* offset 886 */, 0xd46, 0xd57 /* offset 888 */, 0xdd9, 0xdca /* offset 890 */, 0xdd9, 0xdcf /* offset 892 */, 0xdd9, 0xdcf, 0xdca /* offset 894 */, 0xdd9, 0xddf /* offset 897 */, 0xe4d, 0xe32 /* offset 899 */, 0xecd, 0xeb2 /* offset 901 */, 0xeab, 0xe99 /* offset 903 */, 0xeab, 0xea1 /* offset 905 */, 0xf0b /* offset 907 */, 0xf42, 0xfb7 /* offset 908 */, 0xf4c, 0xfb7 /* offset 910 */, 0xf51, 0xfb7 /* offset 912 */, 0xf56, 0xfb7 /* offset 914 */, 0xf5b, 0xfb7 /* offset 916 */, 0xf40, 0xfb5 /* offset 918 */, 0xf71, 0xf72 /* offset 920 */, 0xf71, 0xf74 /* offset 922 */, 0xfb2, 0xf80 /* offset 924 */, 0xfb2, 0xf71, 0xf80 /* offset 926 */, 0xfb3, 0xf80 /* offset 929 */, 0xfb3, 0xf71, 0xf80 /* offset 931 */, 0xf71, 0xf80 /* offset 934 */, 0xf92, 0xfb7 /* offset 936 */, 0xf9c, 0xfb7 /* offset 938 */, 0xfa1, 0xfb7 /* offset 940 */, 0xfa6, 0xfb7 /* offset 942 */, 0xfab, 0xfb7 /* offset 944 */, 0xf90, 0xfb5 /* offset 946 */, 0x1025, 0x102e /* offset 948 */, 0x10dc /* offset 950 */, 0x1b05, 0x1b35 /* offset 951 */, 0x1b07, 0x1b35 /* offset 953 */, 0x1b09, 0x1b35 /* offset 955 */, 0x1b0b, 0x1b35 /* offset 957 */, 0x1b0d, 0x1b35 /* offset 959 */, 0x1b11, 0x1b35 /* offset 961 */, 0x1b3a, 0x1b35 /* offset 963 */, 0x1b3c, 0x1b35 /* offset 965 */, 0x1b3e, 0x1b35 /* offset 967 */, 0x1b3f, 0x1b35 /* offset 969 */, 0x1b42, 0x1b35 /* offset 971 */, 0x41 /* offset 973 */, 0xc6 /* offset 974 */, 0x42 /* offset 975 */, 0x44 /* offset 976 */, 0x45 /* offset 977 */, 0x18e /* offset 978 */, 0x47 /* offset 979 */, 0x48 /* offset 980 */, 0x49 /* offset 981 */, 0x4a /* offset 982 */, 0x4b /* offset 983 */, 0x4c /* offset 984 */, 0x4d /* offset 985 */, 0x4e /* offset 986 */, 0x4f /* offset 987 */, 0x222 /* offset 988 */, 0x50 /* offset 989 */, 0x52 /* offset 990 */, 0x54 /* offset 991 */, 0x55 /* offset 992 */, 0x57 /* offset 993 */, 0x250 /* offset 994 */, 0x251 /* offset 995 */, 0x1d02 /* offset 996 */, 0x62 /* offset 997 */, 0x64 /* offset 998 */, 0x65 /* offset 999 */, 0x259 /* offset 1000 */, 0x25b /* offset 1001 */, 0x25c /* offset 1002 */, 0x67 /* offset 1003 */, 0x6b /* offset 1004 */, 0x6d /* offset 1005 */, 0x14b /* offset 1006 */, 0x254 /* offset 1007 */, 0x1d16 /* offset 1008 */, 0x1d17 /* offset 1009 */, 0x70 /* offset 1010 */, 0x74 /* offset 1011 */, 0x75 /* offset 1012 */, 0x1d1d /* offset 1013 */, 0x26f /* offset 1014 */, 0x76 /* offset 1015 */, 0x1d25 /* offset 1016 */, 0x3b3 /* offset 1017 */, 0x3b4 /* offset 1018 */, 0x3c7 /* offset 1019 */, 0x69 /* offset 1020 */, 0x43d /* offset 1021 */, 0x252 /* offset 1022 */, 0x63 /* offset 1023 */, 0x255 /* offset 1024 */, 0xf0 /* offset 1025 */, 0x66 /* offset 1026 */, 0x25f /* offset 1027 */, 0x261 /* offset 1028 */, 0x265 /* offset 1029 */, 0x268 /* offset 1030 */, 0x269 /* offset 1031 */, 0x26a /* offset 1032 */, 0x1d7b /* offset 1033 */, 0x29d /* offset 1034 */, 0x26d /* offset 1035 */, 0x1d85 /* offset 1036 */, 0x29f /* offset 1037 */, 0x271 /* offset 1038 */, 0x270 /* offset 1039 */, 0x272 /* offset 1040 */, 0x273 /* offset 1041 */, 0x274 /* offset 1042 */, 0x275 /* offset 1043 */, 0x278 /* offset 1044 */, 0x282 /* offset 1045 */, 0x283 /* offset 1046 */, 0x1ab /* offset 1047 */, 0x289 /* offset 1048 */, 0x28a /* offset 1049 */, 0x1d1c /* offset 1050 */, 0x28b /* offset 1051 */, 0x28c /* offset 1052 */, 0x7a /* offset 1053 */, 0x290 /* offset 1054 */, 0x291 /* offset 1055 */, 0x292 /* offset 1056 */, 0x41, 0x325 /* offset 1057 */, 0x61, 0x325 /* offset 1059 */, 0x42, 0x307 /* offset 1061 */, 0x62, 0x307 /* offset 1063 */, 0x42, 0x323 /* offset 1065 */, 0x62, 0x323 /* offset 1067 */, 0x42, 0x331 /* offset 1069 */, 0x62, 0x331 /* offset 1071 */, 0x43, 0x327, 0x301 /* offset 1073 */, 0x63, 0x327, 0x301 /* offset 1076 */, 0x44, 0x307 /* offset 1079 */, 0x64, 0x307 /* offset 1081 */, 0x44, 0x323 /* offset 1083 */, 0x64, 0x323 /* offset 1085 */, 0x44, 0x331 /* offset 1087 */, 0x64, 0x331 /* offset 1089 */, 0x44, 0x327 /* offset 1091 */, 0x64, 0x327 /* offset 1093 */, 0x44, 0x32d /* offset 1095 */, 0x64, 0x32d /* offset 1097 */, 0x45, 0x304, 0x300 /* offset 1099 */, 0x65, 0x304, 0x300 /* offset 1102 */, 0x45, 0x304, 0x301 /* offset 1105 */, 0x65, 0x304, 0x301 /* offset 1108 */, 0x45, 0x32d /* offset 1111 */, 0x65, 0x32d /* offset 1113 */, 0x45, 0x330 /* offset 1115 */, 0x65, 0x330 /* offset 1117 */, 0x45, 0x327, 0x306 /* offset 1119 */, 0x65, 0x327, 0x306 /* offset 1122 */, 0x46, 0x307 /* offset 1125 */, 0x66, 0x307 /* offset 1127 */, 0x47, 0x304 /* offset 1129 */, 0x67, 0x304 /* offset 1131 */, 0x48, 0x307 /* offset 1133 */, 0x68, 0x307 /* offset 1135 */, 0x48, 0x323 /* offset 1137 */, 0x68, 0x323 /* offset 1139 */, 0x48, 0x308 /* offset 1141 */, 0x68, 0x308 /* offset 1143 */, 0x48, 0x327 /* offset 1145 */, 0x68, 0x327 /* offset 1147 */, 0x48, 0x32e /* offset 1149 */, 0x68, 0x32e /* offset 1151 */, 0x49, 0x330 /* offset 1153 */, 0x69, 0x330 /* offset 1155 */, 0x49, 0x308, 0x301 /* offset 1157 */, 0x69, 0x308, 0x301 /* offset 1160 */, 0x4b, 0x301 /* offset 1163 */, 0x6b, 0x301 /* offset 1165 */, 0x4b, 0x323 /* offset 1167 */, 0x6b, 0x323 /* offset 1169 */, 0x4b, 0x331 /* offset 1171 */, 0x6b, 0x331 /* offset 1173 */, 0x4c, 0x323 /* offset 1175 */, 0x6c, 0x323 /* offset 1177 */, 0x4c, 0x323, 0x304 /* offset 1179 */, 0x6c, 0x323, 0x304 /* offset 1182 */, 0x4c, 0x331 /* offset 1185 */, 0x6c, 0x331 /* offset 1187 */, 0x4c, 0x32d /* offset 1189 */, 0x6c, 0x32d /* offset 1191 */, 0x4d, 0x301 /* offset 1193 */, 0x6d, 0x301 /* offset 1195 */, 0x4d, 0x307 /* offset 1197 */, 0x6d, 0x307 /* offset 1199 */, 0x4d, 0x323 /* offset 1201 */, 0x6d, 0x323 /* offset 1203 */, 0x4e, 0x307 /* offset 1205 */, 0x6e, 0x307 /* offset 1207 */, 0x4e, 0x323 /* offset 1209 */, 0x6e, 0x323 /* offset 1211 */, 0x4e, 0x331 /* offset 1213 */, 0x6e, 0x331 /* offset 1215 */, 0x4e, 0x32d /* offset 1217 */, 0x6e, 0x32d /* offset 1219 */, 0x4f, 0x303, 0x301 /* offset 1221 */, 0x6f, 0x303, 0x301 /* offset 1224 */, 0x4f, 0x303, 0x308 /* offset 1227 */, 0x6f, 0x303, 0x308 /* offset 1230 */, 0x4f, 0x304, 0x300 /* offset 1233 */, 0x6f, 0x304, 0x300 /* offset 1236 */, 0x4f, 0x304, 0x301 /* offset 1239 */, 0x6f, 0x304, 0x301 /* offset 1242 */, 0x50, 0x301 /* offset 1245 */, 0x70, 0x301 /* offset 1247 */, 0x50, 0x307 /* offset 1249 */, 0x70, 0x307 /* offset 1251 */, 0x52, 0x307 /* offset 1253 */, 0x72, 0x307 /* offset 1255 */, 0x52, 0x323 /* offset 1257 */, 0x72, 0x323 /* offset 1259 */, 0x52, 0x323, 0x304 /* offset 1261 */, 0x72, 0x323, 0x304 /* offset 1264 */, 0x52, 0x331 /* offset 1267 */, 0x72, 0x331 /* offset 1269 */, 0x53, 0x307 /* offset 1271 */, 0x73, 0x307 /* offset 1273 */, 0x53, 0x323 /* offset 1275 */, 0x73, 0x323 /* offset 1277 */, 0x53, 0x301, 0x307 /* offset 1279 */, 0x73, 0x301, 0x307 /* offset 1282 */, 0x53, 0x30c, 0x307 /* offset 1285 */, 0x73, 0x30c, 0x307 /* offset 1288 */, 0x53, 0x323, 0x307 /* offset 1291 */, 0x73, 0x323, 0x307 /* offset 1294 */, 0x54, 0x307 /* offset 1297 */, 0x74, 0x307 /* offset 1299 */, 0x54, 0x323 /* offset 1301 */, 0x74, 0x323 /* offset 1303 */, 0x54, 0x331 /* offset 1305 */, 0x74, 0x331 /* offset 1307 */, 0x54, 0x32d /* offset 1309 */, 0x74, 0x32d /* offset 1311 */, 0x55, 0x324 /* offset 1313 */, 0x75, 0x324 /* offset 1315 */, 0x55, 0x330 /* offset 1317 */, 0x75, 0x330 /* offset 1319 */, 0x55, 0x32d /* offset 1321 */, 0x75, 0x32d /* offset 1323 */, 0x55, 0x303, 0x301 /* offset 1325 */, 0x75, 0x303, 0x301 /* offset 1328 */, 0x55, 0x304, 0x308 /* offset 1331 */, 0x75, 0x304, 0x308 /* offset 1334 */, 0x56, 0x303 /* offset 1337 */, 0x76, 0x303 /* offset 1339 */, 0x56, 0x323 /* offset 1341 */, 0x76, 0x323 /* offset 1343 */, 0x57, 0x300 /* offset 1345 */, 0x77, 0x300 /* offset 1347 */, 0x57, 0x301 /* offset 1349 */, 0x77, 0x301 /* offset 1351 */, 0x57, 0x308 /* offset 1353 */, 0x77, 0x308 /* offset 1355 */, 0x57, 0x307 /* offset 1357 */, 0x77, 0x307 /* offset 1359 */, 0x57, 0x323 /* offset 1361 */, 0x77, 0x323 /* offset 1363 */, 0x58, 0x307 /* offset 1365 */, 0x78, 0x307 /* offset 1367 */, 0x58, 0x308 /* offset 1369 */, 0x78, 0x308 /* offset 1371 */, 0x59, 0x307 /* offset 1373 */, 0x79, 0x307 /* offset 1375 */, 0x5a, 0x302 /* offset 1377 */, 0x7a, 0x302 /* offset 1379 */, 0x5a, 0x323 /* offset 1381 */, 0x7a, 0x323 /* offset 1383 */, 0x5a, 0x331 /* offset 1385 */, 0x7a, 0x331 /* offset 1387 */, 0x68, 0x331 /* offset 1389 */, 0x74, 0x308 /* offset 1391 */, 0x77, 0x30a /* offset 1393 */, 0x79, 0x30a /* offset 1395 */, 0x61, 0x2be /* offset 1397 */, 0x41, 0x323 /* offset 1399 */, 0x61, 0x323 /* offset 1401 */, 0x41, 0x309 /* offset 1403 */, 0x61, 0x309 /* offset 1405 */, 0x41, 0x302, 0x301 /* offset 1407 */, 0x61, 0x302, 0x301 /* offset 1410 */, 0x41, 0x302, 0x300 /* offset 1413 */, 0x61, 0x302, 0x300 /* offset 1416 */, 0x41, 0x302, 0x309 /* offset 1419 */, 0x61, 0x302, 0x309 /* offset 1422 */, 0x41, 0x302, 0x303 /* offset 1425 */, 0x61, 0x302, 0x303 /* offset 1428 */, 0x41, 0x323, 0x302 /* offset 1431 */, 0x61, 0x323, 0x302 /* offset 1434 */, 0x41, 0x306, 0x301 /* offset 1437 */, 0x61, 0x306, 0x301 /* offset 1440 */, 0x41, 0x306, 0x300 /* offset 1443 */, 0x61, 0x306, 0x300 /* offset 1446 */, 0x41, 0x306, 0x309 /* offset 1449 */, 0x61, 0x306, 0x309 /* offset 1452 */, 0x41, 0x306, 0x303 /* offset 1455 */, 0x61, 0x306, 0x303 /* offset 1458 */, 0x41, 0x323, 0x306 /* offset 1461 */, 0x61, 0x323, 0x306 /* offset 1464 */, 0x45, 0x323 /* offset 1467 */, 0x65, 0x323 /* offset 1469 */, 0x45, 0x309 /* offset 1471 */, 0x65, 0x309 /* offset 1473 */, 0x45, 0x303 /* offset 1475 */, 0x65, 0x303 /* offset 1477 */, 0x45, 0x302, 0x301 /* offset 1479 */, 0x65, 0x302, 0x301 /* offset 1482 */, 0x45, 0x302, 0x300 /* offset 1485 */, 0x65, 0x302, 0x300 /* offset 1488 */, 0x45, 0x302, 0x309 /* offset 1491 */, 0x65, 0x302, 0x309 /* offset 1494 */, 0x45, 0x302, 0x303 /* offset 1497 */, 0x65, 0x302, 0x303 /* offset 1500 */, 0x45, 0x323, 0x302 /* offset 1503 */, 0x65, 0x323, 0x302 /* offset 1506 */, 0x49, 0x309 /* offset 1509 */, 0x69, 0x309 /* offset 1511 */, 0x49, 0x323 /* offset 1513 */, 0x69, 0x323 /* offset 1515 */, 0x4f, 0x323 /* offset 1517 */, 0x6f, 0x323 /* offset 1519 */, 0x4f, 0x309 /* offset 1521 */, 0x6f, 0x309 /* offset 1523 */, 0x4f, 0x302, 0x301 /* offset 1525 */, 0x6f, 0x302, 0x301 /* offset 1528 */, 0x4f, 0x302, 0x300 /* offset 1531 */, 0x6f, 0x302, 0x300 /* offset 1534 */, 0x4f, 0x302, 0x309 /* offset 1537 */, 0x6f, 0x302, 0x309 /* offset 1540 */, 0x4f, 0x302, 0x303 /* offset 1543 */, 0x6f, 0x302, 0x303 /* offset 1546 */, 0x4f, 0x323, 0x302 /* offset 1549 */, 0x6f, 0x323, 0x302 /* offset 1552 */, 0x4f, 0x31b, 0x301 /* offset 1555 */, 0x6f, 0x31b, 0x301 /* offset 1558 */, 0x4f, 0x31b, 0x300 /* offset 1561 */, 0x6f, 0x31b, 0x300 /* offset 1564 */, 0x4f, 0x31b, 0x309 /* offset 1567 */, 0x6f, 0x31b, 0x309 /* offset 1570 */, 0x4f, 0x31b, 0x303 /* offset 1573 */, 0x6f, 0x31b, 0x303 /* offset 1576 */, 0x4f, 0x31b, 0x323 /* offset 1579 */, 0x6f, 0x31b, 0x323 /* offset 1582 */, 0x55, 0x323 /* offset 1585 */, 0x75, 0x323 /* offset 1587 */, 0x55, 0x309 /* offset 1589 */, 0x75, 0x309 /* offset 1591 */, 0x55, 0x31b, 0x301 /* offset 1593 */, 0x75, 0x31b, 0x301 /* offset 1596 */, 0x55, 0x31b, 0x300 /* offset 1599 */, 0x75, 0x31b, 0x300 /* offset 1602 */, 0x55, 0x31b, 0x309 /* offset 1605 */, 0x75, 0x31b, 0x309 /* offset 1608 */, 0x55, 0x31b, 0x303 /* offset 1611 */, 0x75, 0x31b, 0x303 /* offset 1614 */, 0x55, 0x31b, 0x323 /* offset 1617 */, 0x75, 0x31b, 0x323 /* offset 1620 */, 0x59, 0x300 /* offset 1623 */, 0x79, 0x300 /* offset 1625 */, 0x59, 0x323 /* offset 1627 */, 0x79, 0x323 /* offset 1629 */, 0x59, 0x309 /* offset 1631 */, 0x79, 0x309 /* offset 1633 */, 0x59, 0x303 /* offset 1635 */, 0x79, 0x303 /* offset 1637 */, 0x3b1, 0x313 /* offset 1639 */, 0x3b1, 0x314 /* offset 1641 */, 0x3b1, 0x313, 0x300 /* offset 1643 */, 0x3b1, 0x314, 0x300 /* offset 1646 */, 0x3b1, 0x313, 0x301 /* offset 1649 */, 0x3b1, 0x314, 0x301 /* offset 1652 */, 0x3b1, 0x313, 0x342 /* offset 1655 */, 0x3b1, 0x314, 0x342 /* offset 1658 */, 0x391, 0x313 /* offset 1661 */, 0x391, 0x314 /* offset 1663 */, 0x391, 0x313, 0x300 /* offset 1665 */, 0x391, 0x314, 0x300 /* offset 1668 */, 0x391, 0x313, 0x301 /* offset 1671 */, 0x391, 0x314, 0x301 /* offset 1674 */, 0x391, 0x313, 0x342 /* offset 1677 */, 0x391, 0x314, 0x342 /* offset 1680 */, 0x3b5, 0x313 /* offset 1683 */, 0x3b5, 0x314 /* offset 1685 */, 0x3b5, 0x313, 0x300 /* offset 1687 */, 0x3b5, 0x314, 0x300 /* offset 1690 */, 0x3b5, 0x313, 0x301 /* offset 1693 */, 0x3b5, 0x314, 0x301 /* offset 1696 */, 0x395, 0x313 /* offset 1699 */, 0x395, 0x314 /* offset 1701 */, 0x395, 0x313, 0x300 /* offset 1703 */, 0x395, 0x314, 0x300 /* offset 1706 */, 0x395, 0x313, 0x301 /* offset 1709 */, 0x395, 0x314, 0x301 /* offset 1712 */, 0x3b7, 0x313 /* offset 1715 */, 0x3b7, 0x314 /* offset 1717 */, 0x3b7, 0x313, 0x300 /* offset 1719 */, 0x3b7, 0x314, 0x300 /* offset 1722 */, 0x3b7, 0x313, 0x301 /* offset 1725 */, 0x3b7, 0x314, 0x301 /* offset 1728 */, 0x3b7, 0x313, 0x342 /* offset 1731 */, 0x3b7, 0x314, 0x342 /* offset 1734 */, 0x397, 0x313 /* offset 1737 */, 0x397, 0x314 /* offset 1739 */, 0x397, 0x313, 0x300 /* offset 1741 */, 0x397, 0x314, 0x300 /* offset 1744 */, 0x397, 0x313, 0x301 /* offset 1747 */, 0x397, 0x314, 0x301 /* offset 1750 */, 0x397, 0x313, 0x342 /* offset 1753 */, 0x397, 0x314, 0x342 /* offset 1756 */, 0x3b9, 0x313 /* offset 1759 */, 0x3b9, 0x314 /* offset 1761 */, 0x3b9, 0x313, 0x300 /* offset 1763 */, 0x3b9, 0x314, 0x300 /* offset 1766 */, 0x3b9, 0x313, 0x301 /* offset 1769 */, 0x3b9, 0x314, 0x301 /* offset 1772 */, 0x3b9, 0x313, 0x342 /* offset 1775 */, 0x3b9, 0x314, 0x342 /* offset 1778 */, 0x399, 0x313 /* offset 1781 */, 0x399, 0x314 /* offset 1783 */, 0x399, 0x313, 0x300 /* offset 1785 */, 0x399, 0x314, 0x300 /* offset 1788 */, 0x399, 0x313, 0x301 /* offset 1791 */, 0x399, 0x314, 0x301 /* offset 1794 */, 0x399, 0x313, 0x342 /* offset 1797 */, 0x399, 0x314, 0x342 /* offset 1800 */, 0x3bf, 0x313 /* offset 1803 */, 0x3bf, 0x314 /* offset 1805 */, 0x3bf, 0x313, 0x300 /* offset 1807 */, 0x3bf, 0x314, 0x300 /* offset 1810 */, 0x3bf, 0x313, 0x301 /* offset 1813 */, 0x3bf, 0x314, 0x301 /* offset 1816 */, 0x39f, 0x313 /* offset 1819 */, 0x39f, 0x314 /* offset 1821 */, 0x39f, 0x313, 0x300 /* offset 1823 */, 0x39f, 0x314, 0x300 /* offset 1826 */, 0x39f, 0x313, 0x301 /* offset 1829 */, 0x39f, 0x314, 0x301 /* offset 1832 */, 0x3c5, 0x313 /* offset 1835 */, 0x3c5, 0x314 /* offset 1837 */, 0x3c5, 0x313, 0x300 /* offset 1839 */, 0x3c5, 0x314, 0x300 /* offset 1842 */, 0x3c5, 0x313, 0x301 /* offset 1845 */, 0x3c5, 0x314, 0x301 /* offset 1848 */, 0x3c5, 0x313, 0x342 /* offset 1851 */, 0x3c5, 0x314, 0x342 /* offset 1854 */, 0x3a5, 0x314 /* offset 1857 */, 0x3a5, 0x314, 0x300 /* offset 1859 */, 0x3a5, 0x314, 0x301 /* offset 1862 */, 0x3a5, 0x314, 0x342 /* offset 1865 */, 0x3c9, 0x313 /* offset 1868 */, 0x3c9, 0x314 /* offset 1870 */, 0x3c9, 0x313, 0x300 /* offset 1872 */, 0x3c9, 0x314, 0x300 /* offset 1875 */, 0x3c9, 0x313, 0x301 /* offset 1878 */, 0x3c9, 0x314, 0x301 /* offset 1881 */, 0x3c9, 0x313, 0x342 /* offset 1884 */, 0x3c9, 0x314, 0x342 /* offset 1887 */, 0x3a9, 0x313 /* offset 1890 */, 0x3a9, 0x314 /* offset 1892 */, 0x3a9, 0x313, 0x300 /* offset 1894 */, 0x3a9, 0x314, 0x300 /* offset 1897 */, 0x3a9, 0x313, 0x301 /* offset 1900 */, 0x3a9, 0x314, 0x301 /* offset 1903 */, 0x3a9, 0x313, 0x342 /* offset 1906 */, 0x3a9, 0x314, 0x342 /* offset 1909 */, 0x3b1, 0x300 /* offset 1912 */, 0x3b5, 0x300 /* offset 1914 */, 0x3b7, 0x300 /* offset 1916 */, 0x3b9, 0x300 /* offset 1918 */, 0x3bf, 0x300 /* offset 1920 */, 0x3c5, 0x300 /* offset 1922 */, 0x3c9, 0x300 /* offset 1924 */, 0x3b1, 0x313, 0x345 /* offset 1926 */, 0x3b1, 0x314, 0x345 /* offset 1929 */, 0x3b1, 0x313, 0x300, 0x345 /* offset 1932 */, 0x3b1, 0x314, 0x300, 0x345 /* offset 1936 */, 0x3b1, 0x313, 0x301, 0x345 /* offset 1940 */, 0x3b1, 0x314, 0x301, 0x345 /* offset 1944 */, 0x3b1, 0x313, 0x342, 0x345 /* offset 1948 */, 0x3b1, 0x314, 0x342, 0x345 /* offset 1952 */, 0x391, 0x313, 0x345 /* offset 1956 */, 0x391, 0x314, 0x345 /* offset 1959 */, 0x391, 0x313, 0x300, 0x345 /* offset 1962 */, 0x391, 0x314, 0x300, 0x345 /* offset 1966 */, 0x391, 0x313, 0x301, 0x345 /* offset 1970 */, 0x391, 0x314, 0x301, 0x345 /* offset 1974 */, 0x391, 0x313, 0x342, 0x345 /* offset 1978 */, 0x391, 0x314, 0x342, 0x345 /* offset 1982 */, 0x3b7, 0x313, 0x345 /* offset 1986 */, 0x3b7, 0x314, 0x345 /* offset 1989 */, 0x3b7, 0x313, 0x300, 0x345 /* offset 1992 */, 0x3b7, 0x314, 0x300, 0x345 /* offset 1996 */, 0x3b7, 0x313, 0x301, 0x345 /* offset 2000 */, 0x3b7, 0x314, 0x301, 0x345 /* offset 2004 */, 0x3b7, 0x313, 0x342, 0x345 /* offset 2008 */, 0x3b7, 0x314, 0x342, 0x345 /* offset 2012 */, 0x397, 0x313, 0x345 /* offset 2016 */, 0x397, 0x314, 0x345 /* offset 2019 */, 0x397, 0x313, 0x300, 0x345 /* offset 2022 */, 0x397, 0x314, 0x300, 0x345 /* offset 2026 */, 0x397, 0x313, 0x301, 0x345 /* offset 2030 */, 0x397, 0x314, 0x301, 0x345 /* offset 2034 */, 0x397, 0x313, 0x342, 0x345 /* offset 2038 */, 0x397, 0x314, 0x342, 0x345 /* offset 2042 */, 0x3c9, 0x313, 0x345 /* offset 2046 */, 0x3c9, 0x314, 0x345 /* offset 2049 */, 0x3c9, 0x313, 0x300, 0x345 /* offset 2052 */, 0x3c9, 0x314, 0x300, 0x345 /* offset 2056 */, 0x3c9, 0x313, 0x301, 0x345 /* offset 2060 */, 0x3c9, 0x314, 0x301, 0x345 /* offset 2064 */, 0x3c9, 0x313, 0x342, 0x345 /* offset 2068 */, 0x3c9, 0x314, 0x342, 0x345 /* offset 2072 */, 0x3a9, 0x313, 0x345 /* offset 2076 */, 0x3a9, 0x314, 0x345 /* offset 2079 */, 0x3a9, 0x313, 0x300, 0x345 /* offset 2082 */, 0x3a9, 0x314, 0x300, 0x345 /* offset 2086 */, 0x3a9, 0x313, 0x301, 0x345 /* offset 2090 */, 0x3a9, 0x314, 0x301, 0x345 /* offset 2094 */, 0x3a9, 0x313, 0x342, 0x345 /* offset 2098 */, 0x3a9, 0x314, 0x342, 0x345 /* offset 2102 */, 0x3b1, 0x306 /* offset 2106 */, 0x3b1, 0x304 /* offset 2108 */, 0x3b1, 0x300, 0x345 /* offset 2110 */, 0x3b1, 0x345 /* offset 2113 */, 0x3b1, 0x301, 0x345 /* offset 2115 */, 0x3b1, 0x342 /* offset 2118 */, 0x3b1, 0x342, 0x345 /* offset 2120 */, 0x391, 0x306 /* offset 2123 */, 0x391, 0x304 /* offset 2125 */, 0x391, 0x300 /* offset 2127 */, 0x391, 0x345 /* offset 2129 */, 0x20, 0x313 /* offset 2131 */, 0x3b9 /* offset 2133 */, 0x20, 0x342 /* offset 2134 */, 0x20, 0x308, 0x342 /* offset 2136 */, 0x3b7, 0x300, 0x345 /* offset 2139 */, 0x3b7, 0x345 /* offset 2142 */, 0x3b7, 0x301, 0x345 /* offset 2144 */, 0x3b7, 0x342 /* offset 2147 */, 0x3b7, 0x342, 0x345 /* offset 2149 */, 0x395, 0x300 /* offset 2152 */, 0x397, 0x300 /* offset 2154 */, 0x397, 0x345 /* offset 2156 */, 0x20, 0x313, 0x300 /* offset 2158 */, 0x20, 0x313, 0x301 /* offset 2161 */, 0x20, 0x313, 0x342 /* offset 2164 */, 0x3b9, 0x306 /* offset 2167 */, 0x3b9, 0x304 /* offset 2169 */, 0x3b9, 0x308, 0x300 /* offset 2171 */, 0x3b9, 0x342 /* offset 2174 */, 0x3b9, 0x308, 0x342 /* offset 2176 */, 0x399, 0x306 /* offset 2179 */, 0x399, 0x304 /* offset 2181 */, 0x399, 0x300 /* offset 2183 */, 0x20, 0x314, 0x300 /* offset 2185 */, 0x20, 0x314, 0x301 /* offset 2188 */, 0x20, 0x314, 0x342 /* offset 2191 */, 0x3c5, 0x306 /* offset 2194 */, 0x3c5, 0x304 /* offset 2196 */, 0x3c5, 0x308, 0x300 /* offset 2198 */, 0x3c1, 0x313 /* offset 2201 */, 0x3c1, 0x314 /* offset 2203 */, 0x3c5, 0x342 /* offset 2205 */, 0x3c5, 0x308, 0x342 /* offset 2207 */, 0x3a5, 0x306 /* offset 2210 */, 0x3a5, 0x304 /* offset 2212 */, 0x3a5, 0x300 /* offset 2214 */, 0x3a1, 0x314 /* offset 2216 */, 0x20, 0x308, 0x300 /* offset 2218 */, 0x60 /* offset 2221 */, 0x3c9, 0x300, 0x345 /* offset 2222 */, 0x3c9, 0x345 /* offset 2225 */, 0x3c9, 0x301, 0x345 /* offset 2227 */, 0x3c9, 0x342 /* offset 2230 */, 0x3c9, 0x342, 0x345 /* offset 2232 */, 0x39f, 0x300 /* offset 2235 */, 0x3a9, 0x300 /* offset 2237 */, 0x3a9, 0x345 /* offset 2239 */, 0x20, 0x314 /* offset 2241 */, 0x2010 /* offset 2243 */, 0x20, 0x333 /* offset 2244 */, 0x2e /* offset 2246 */, 0x2e, 0x2e /* offset 2247 */, 0x2e, 0x2e, 0x2e /* offset 2249 */, 0x2032, 0x2032 /* offset 2252 */, 0x2032, 0x2032, 0x2032 /* offset 2254 */, 0x2035, 0x2035 /* offset 2257 */, 0x2035, 0x2035, 0x2035 /* offset 2259 */, 0x21, 0x21 /* offset 2262 */, 0x20, 0x305 /* offset 2264 */, 0x3f, 0x3f /* offset 2266 */, 0x3f, 0x21 /* offset 2268 */, 0x21, 0x3f /* offset 2270 */, 0x2032, 0x2032, 0x2032, 0x2032 /* offset 2272 */, 0x30 /* offset 2276 */, 0x34 /* offset 2277 */, 0x35 /* offset 2278 */, 0x36 /* offset 2279 */, 0x37 /* offset 2280 */, 0x38 /* offset 2281 */, 0x39 /* offset 2282 */, 0x2b /* offset 2283 */, 0x2212 /* offset 2284 */, 0x3d /* offset 2285 */, 0x28 /* offset 2286 */, 0x29 /* offset 2287 */, 0x6e /* offset 2288 */, 0x52, 0x73 /* offset 2289 */, 0x61, 0x2f, 0x63 /* offset 2291 */, 0x61, 0x2f, 0x73 /* offset 2294 */, 0x43 /* offset 2297 */, 0xb0, 0x43 /* offset 2298 */, 0x63, 0x2f, 0x6f /* offset 2300 */, 0x63, 0x2f, 0x75 /* offset 2303 */, 0x190 /* offset 2306 */, 0xb0, 0x46 /* offset 2307 */, 0x127 /* offset 2309 */, 0x4e, 0x6f /* offset 2310 */, 0x51 /* offset 2312 */, 0x53, 0x4d /* offset 2313 */, 0x54, 0x45, 0x4c /* offset 2315 */, 0x54, 0x4d /* offset 2318 */, 0x5a /* offset 2320 */, 0x3a9 /* offset 2321 */, 0x46 /* offset 2322 */, 0x5d0 /* offset 2323 */, 0x5d1 /* offset 2324 */, 0x5d2 /* offset 2325 */, 0x5d3 /* offset 2326 */, 0x46, 0x41, 0x58 /* offset 2327 */, 0x393 /* offset 2330 */, 0x3a0 /* offset 2331 */, 0x2211 /* offset 2332 */, 0x31, 0x2044, 0x37 /* offset 2333 */, 0x31, 0x2044, 0x39 /* offset 2336 */, 0x31, 0x2044, 0x31, 0x30 /* offset 2339 */, 0x31, 0x2044, 0x33 /* offset 2343 */, 0x32, 0x2044, 0x33 /* offset 2346 */, 0x31, 0x2044, 0x35 /* offset 2349 */, 0x32, 0x2044, 0x35 /* offset 2352 */, 0x33, 0x2044, 0x35 /* offset 2355 */, 0x34, 0x2044, 0x35 /* offset 2358 */, 0x31, 0x2044, 0x36 /* offset 2361 */, 0x35, 0x2044, 0x36 /* offset 2364 */, 0x31, 0x2044, 0x38 /* offset 2367 */, 0x33, 0x2044, 0x38 /* offset 2370 */, 0x35, 0x2044, 0x38 /* offset 2373 */, 0x37, 0x2044, 0x38 /* offset 2376 */, 0x31, 0x2044 /* offset 2379 */, 0x49, 0x49 /* offset 2381 */, 0x49, 0x49, 0x49 /* offset 2383 */, 0x49, 0x56 /* offset 2386 */, 0x56 /* offset 2388 */, 0x56, 0x49 /* offset 2389 */, 0x56, 0x49, 0x49 /* offset 2391 */, 0x56, 0x49, 0x49, 0x49 /* offset 2394 */, 0x49, 0x58 /* offset 2398 */, 0x58 /* offset 2400 */, 0x58, 0x49 /* offset 2401 */, 0x58, 0x49, 0x49 /* offset 2403 */, 0x69, 0x69 /* offset 2406 */, 0x69, 0x69, 0x69 /* offset 2408 */, 0x69, 0x76 /* offset 2411 */, 0x76, 0x69 /* offset 2413 */, 0x76, 0x69, 0x69 /* offset 2415 */, 0x76, 0x69, 0x69, 0x69 /* offset 2418 */, 0x69, 0x78 /* offset 2422 */, 0x78, 0x69 /* offset 2424 */, 0x78, 0x69, 0x69 /* offset 2426 */, 0x30, 0x2044, 0x33 /* offset 2429 */, 0x2190, 0x338 /* offset 2432 */, 0x2192, 0x338 /* offset 2434 */, 0x2194, 0x338 /* offset 2436 */, 0x21d0, 0x338 /* offset 2438 */, 0x21d4, 0x338 /* offset 2440 */, 0x21d2, 0x338 /* offset 2442 */, 0x2203, 0x338 /* offset 2444 */, 0x2208, 0x338 /* offset 2446 */, 0x220b, 0x338 /* offset 2448 */, 0x2223, 0x338 /* offset 2450 */, 0x2225, 0x338 /* offset 2452 */, 0x222b, 0x222b /* offset 2454 */, 0x222b, 0x222b, 0x222b /* offset 2456 */, 0x222e, 0x222e /* offset 2459 */, 0x222e, 0x222e, 0x222e /* offset 2461 */, 0x223c, 0x338 /* offset 2464 */, 0x2243, 0x338 /* offset 2466 */, 0x2245, 0x338 /* offset 2468 */, 0x2248, 0x338 /* offset 2470 */, 0x3d, 0x338 /* offset 2472 */, 0x2261, 0x338 /* offset 2474 */, 0x224d, 0x338 /* offset 2476 */, 0x3c, 0x338 /* offset 2478 */, 0x3e, 0x338 /* offset 2480 */, 0x2264, 0x338 /* offset 2482 */, 0x2265, 0x338 /* offset 2484 */, 0x2272, 0x338 /* offset 2486 */, 0x2273, 0x338 /* offset 2488 */, 0x2276, 0x338 /* offset 2490 */, 0x2277, 0x338 /* offset 2492 */, 0x227a, 0x338 /* offset 2494 */, 0x227b, 0x338 /* offset 2496 */, 0x2282, 0x338 /* offset 2498 */, 0x2283, 0x338 /* offset 2500 */, 0x2286, 0x338 /* offset 2502 */, 0x2287, 0x338 /* offset 2504 */, 0x22a2, 0x338 /* offset 2506 */, 0x22a8, 0x338 /* offset 2508 */, 0x22a9, 0x338 /* offset 2510 */, 0x22ab, 0x338 /* offset 2512 */, 0x227c, 0x338 /* offset 2514 */, 0x227d, 0x338 /* offset 2516 */, 0x2291, 0x338 /* offset 2518 */, 0x2292, 0x338 /* offset 2520 */, 0x22b2, 0x338 /* offset 2522 */, 0x22b3, 0x338 /* offset 2524 */, 0x22b4, 0x338 /* offset 2526 */, 0x22b5, 0x338 /* offset 2528 */, 0x3008 /* offset 2530 */, 0x3009 /* offset 2531 */, 0x31, 0x30 /* offset 2532 */, 0x31, 0x31 /* offset 2534 */, 0x31, 0x32 /* offset 2536 */, 0x31, 0x33 /* offset 2538 */, 0x31, 0x34 /* offset 2540 */, 0x31, 0x35 /* offset 2542 */, 0x31, 0x36 /* offset 2544 */, 0x31, 0x37 /* offset 2546 */, 0x31, 0x38 /* offset 2548 */, 0x31, 0x39 /* offset 2550 */, 0x32, 0x30 /* offset 2552 */, 0x28, 0x31, 0x29 /* offset 2554 */, 0x28, 0x32, 0x29 /* offset 2557 */, 0x28, 0x33, 0x29 /* offset 2560 */, 0x28, 0x34, 0x29 /* offset 2563 */, 0x28, 0x35, 0x29 /* offset 2566 */, 0x28, 0x36, 0x29 /* offset 2569 */, 0x28, 0x37, 0x29 /* offset 2572 */, 0x28, 0x38, 0x29 /* offset 2575 */, 0x28, 0x39, 0x29 /* offset 2578 */, 0x28, 0x31, 0x30, 0x29 /* offset 2581 */, 0x28, 0x31, 0x31, 0x29 /* offset 2585 */, 0x28, 0x31, 0x32, 0x29 /* offset 2589 */, 0x28, 0x31, 0x33, 0x29 /* offset 2593 */, 0x28, 0x31, 0x34, 0x29 /* offset 2597 */, 0x28, 0x31, 0x35, 0x29 /* offset 2601 */, 0x28, 0x31, 0x36, 0x29 /* offset 2605 */, 0x28, 0x31, 0x37, 0x29 /* offset 2609 */, 0x28, 0x31, 0x38, 0x29 /* offset 2613 */, 0x28, 0x31, 0x39, 0x29 /* offset 2617 */, 0x28, 0x32, 0x30, 0x29 /* offset 2621 */, 0x31, 0x2e /* offset 2625 */, 0x32, 0x2e /* offset 2627 */, 0x33, 0x2e /* offset 2629 */, 0x34, 0x2e /* offset 2631 */, 0x35, 0x2e /* offset 2633 */, 0x36, 0x2e /* offset 2635 */, 0x37, 0x2e /* offset 2637 */, 0x38, 0x2e /* offset 2639 */, 0x39, 0x2e /* offset 2641 */, 0x31, 0x30, 0x2e /* offset 2643 */, 0x31, 0x31, 0x2e /* offset 2646 */, 0x31, 0x32, 0x2e /* offset 2649 */, 0x31, 0x33, 0x2e /* offset 2652 */, 0x31, 0x34, 0x2e /* offset 2655 */, 0x31, 0x35, 0x2e /* offset 2658 */, 0x31, 0x36, 0x2e /* offset 2661 */, 0x31, 0x37, 0x2e /* offset 2664 */, 0x31, 0x38, 0x2e /* offset 2667 */, 0x31, 0x39, 0x2e /* offset 2670 */, 0x32, 0x30, 0x2e /* offset 2673 */, 0x28, 0x61, 0x29 /* offset 2676 */, 0x28, 0x62, 0x29 /* offset 2679 */, 0x28, 0x63, 0x29 /* offset 2682 */, 0x28, 0x64, 0x29 /* offset 2685 */, 0x28, 0x65, 0x29 /* offset 2688 */, 0x28, 0x66, 0x29 /* offset 2691 */, 0x28, 0x67, 0x29 /* offset 2694 */, 0x28, 0x68, 0x29 /* offset 2697 */, 0x28, 0x69, 0x29 /* offset 2700 */, 0x28, 0x6a, 0x29 /* offset 2703 */, 0x28, 0x6b, 0x29 /* offset 2706 */, 0x28, 0x6c, 0x29 /* offset 2709 */, 0x28, 0x6d, 0x29 /* offset 2712 */, 0x28, 0x6e, 0x29 /* offset 2715 */, 0x28, 0x6f, 0x29 /* offset 2718 */, 0x28, 0x70, 0x29 /* offset 2721 */, 0x28, 0x71, 0x29 /* offset 2724 */, 0x28, 0x72, 0x29 /* offset 2727 */, 0x28, 0x73, 0x29 /* offset 2730 */, 0x28, 0x74, 0x29 /* offset 2733 */, 0x28, 0x75, 0x29 /* offset 2736 */, 0x28, 0x76, 0x29 /* offset 2739 */, 0x28, 0x77, 0x29 /* offset 2742 */, 0x28, 0x78, 0x29 /* offset 2745 */, 0x28, 0x79, 0x29 /* offset 2748 */, 0x28, 0x7a, 0x29 /* offset 2751 */, 0x53 /* offset 2754 */, 0x59 /* offset 2755 */, 0x71 /* offset 2756 */, 0x222b, 0x222b, 0x222b, 0x222b /* offset 2757 */, 0x3a, 0x3a, 0x3d /* offset 2761 */, 0x3d, 0x3d /* offset 2764 */, 0x3d, 0x3d, 0x3d /* offset 2766 */, 0x2add, 0x338 /* offset 2769 */, 0x2d61 /* offset 2771 */, 0x6bcd /* offset 2772 */, 0x9f9f /* offset 2773 */, 0x4e00 /* offset 2774 */, 0x4e28 /* offset 2775 */, 0x4e36 /* offset 2776 */, 0x4e3f /* offset 2777 */, 0x4e59 /* offset 2778 */, 0x4e85 /* offset 2779 */, 0x4e8c /* offset 2780 */, 0x4ea0 /* offset 2781 */, 0x4eba /* offset 2782 */, 0x513f /* offset 2783 */, 0x5165 /* offset 2784 */, 0x516b /* offset 2785 */, 0x5182 /* offset 2786 */, 0x5196 /* offset 2787 */, 0x51ab /* offset 2788 */, 0x51e0 /* offset 2789 */, 0x51f5 /* offset 2790 */, 0x5200 /* offset 2791 */, 0x529b /* offset 2792 */, 0x52f9 /* offset 2793 */, 0x5315 /* offset 2794 */, 0x531a /* offset 2795 */, 0x5338 /* offset 2796 */, 0x5341 /* offset 2797 */, 0x535c /* offset 2798 */, 0x5369 /* offset 2799 */, 0x5382 /* offset 2800 */, 0x53b6 /* offset 2801 */, 0x53c8 /* offset 2802 */, 0x53e3 /* offset 2803 */, 0x56d7 /* offset 2804 */, 0x571f /* offset 2805 */, 0x58eb /* offset 2806 */, 0x5902 /* offset 2807 */, 0x590a /* offset 2808 */, 0x5915 /* offset 2809 */, 0x5927 /* offset 2810 */, 0x5973 /* offset 2811 */, 0x5b50 /* offset 2812 */, 0x5b80 /* offset 2813 */, 0x5bf8 /* offset 2814 */, 0x5c0f /* offset 2815 */, 0x5c22 /* offset 2816 */, 0x5c38 /* offset 2817 */, 0x5c6e /* offset 2818 */, 0x5c71 /* offset 2819 */, 0x5ddb /* offset 2820 */, 0x5de5 /* offset 2821 */, 0x5df1 /* offset 2822 */, 0x5dfe /* offset 2823 */, 0x5e72 /* offset 2824 */, 0x5e7a /* offset 2825 */, 0x5e7f /* offset 2826 */, 0x5ef4 /* offset 2827 */, 0x5efe /* offset 2828 */, 0x5f0b /* offset 2829 */, 0x5f13 /* offset 2830 */, 0x5f50 /* offset 2831 */, 0x5f61 /* offset 2832 */, 0x5f73 /* offset 2833 */, 0x5fc3 /* offset 2834 */, 0x6208 /* offset 2835 */, 0x6236 /* offset 2836 */, 0x624b /* offset 2837 */, 0x652f /* offset 2838 */, 0x6534 /* offset 2839 */, 0x6587 /* offset 2840 */, 0x6597 /* offset 2841 */, 0x65a4 /* offset 2842 */, 0x65b9 /* offset 2843 */, 0x65e0 /* offset 2844 */, 0x65e5 /* offset 2845 */, 0x66f0 /* offset 2846 */, 0x6708 /* offset 2847 */, 0x6728 /* offset 2848 */, 0x6b20 /* offset 2849 */, 0x6b62 /* offset 2850 */, 0x6b79 /* offset 2851 */, 0x6bb3 /* offset 2852 */, 0x6bcb /* offset 2853 */, 0x6bd4 /* offset 2854 */, 0x6bdb /* offset 2855 */, 0x6c0f /* offset 2856 */, 0x6c14 /* offset 2857 */, 0x6c34 /* offset 2858 */, 0x706b /* offset 2859 */, 0x722a /* offset 2860 */, 0x7236 /* offset 2861 */, 0x723b /* offset 2862 */, 0x723f /* offset 2863 */, 0x7247 /* offset 2864 */, 0x7259 /* offset 2865 */, 0x725b /* offset 2866 */, 0x72ac /* offset 2867 */, 0x7384 /* offset 2868 */, 0x7389 /* offset 2869 */, 0x74dc /* offset 2870 */, 0x74e6 /* offset 2871 */, 0x7518 /* offset 2872 */, 0x751f /* offset 2873 */, 0x7528 /* offset 2874 */, 0x7530 /* offset 2875 */, 0x758b /* offset 2876 */, 0x7592 /* offset 2877 */, 0x7676 /* offset 2878 */, 0x767d /* offset 2879 */, 0x76ae /* offset 2880 */, 0x76bf /* offset 2881 */, 0x76ee /* offset 2882 */, 0x77db /* offset 2883 */, 0x77e2 /* offset 2884 */, 0x77f3 /* offset 2885 */, 0x793a /* offset 2886 */, 0x79b8 /* offset 2887 */, 0x79be /* offset 2888 */, 0x7a74 /* offset 2889 */, 0x7acb /* offset 2890 */, 0x7af9 /* offset 2891 */, 0x7c73 /* offset 2892 */, 0x7cf8 /* offset 2893 */, 0x7f36 /* offset 2894 */, 0x7f51 /* offset 2895 */, 0x7f8a /* offset 2896 */, 0x7fbd /* offset 2897 */, 0x8001 /* offset 2898 */, 0x800c /* offset 2899 */, 0x8012 /* offset 2900 */, 0x8033 /* offset 2901 */, 0x807f /* offset 2902 */, 0x8089 /* offset 2903 */, 0x81e3 /* offset 2904 */, 0x81ea /* offset 2905 */, 0x81f3 /* offset 2906 */, 0x81fc /* offset 2907 */, 0x820c /* offset 2908 */, 0x821b /* offset 2909 */, 0x821f /* offset 2910 */, 0x826e /* offset 2911 */, 0x8272 /* offset 2912 */, 0x8278 /* offset 2913 */, 0x864d /* offset 2914 */, 0x866b /* offset 2915 */, 0x8840 /* offset 2916 */, 0x884c /* offset 2917 */, 0x8863 /* offset 2918 */, 0x897e /* offset 2919 */, 0x898b /* offset 2920 */, 0x89d2 /* offset 2921 */, 0x8a00 /* offset 2922 */, 0x8c37 /* offset 2923 */, 0x8c46 /* offset 2924 */, 0x8c55 /* offset 2925 */, 0x8c78 /* offset 2926 */, 0x8c9d /* offset 2927 */, 0x8d64 /* offset 2928 */, 0x8d70 /* offset 2929 */, 0x8db3 /* offset 2930 */, 0x8eab /* offset 2931 */, 0x8eca /* offset 2932 */, 0x8f9b /* offset 2933 */, 0x8fb0 /* offset 2934 */, 0x8fb5 /* offset 2935 */, 0x9091 /* offset 2936 */, 0x9149 /* offset 2937 */, 0x91c6 /* offset 2938 */, 0x91cc /* offset 2939 */, 0x91d1 /* offset 2940 */, 0x9577 /* offset 2941 */, 0x9580 /* offset 2942 */, 0x961c /* offset 2943 */, 0x96b6 /* offset 2944 */, 0x96b9 /* offset 2945 */, 0x96e8 /* offset 2946 */, 0x9751 /* offset 2947 */, 0x975e /* offset 2948 */, 0x9762 /* offset 2949 */, 0x9769 /* offset 2950 */, 0x97cb /* offset 2951 */, 0x97ed /* offset 2952 */, 0x97f3 /* offset 2953 */, 0x9801 /* offset 2954 */, 0x98a8 /* offset 2955 */, 0x98db /* offset 2956 */, 0x98df /* offset 2957 */, 0x9996 /* offset 2958 */, 0x9999 /* offset 2959 */, 0x99ac /* offset 2960 */, 0x9aa8 /* offset 2961 */, 0x9ad8 /* offset 2962 */, 0x9adf /* offset 2963 */, 0x9b25 /* offset 2964 */, 0x9b2f /* offset 2965 */, 0x9b32 /* offset 2966 */, 0x9b3c /* offset 2967 */, 0x9b5a /* offset 2968 */, 0x9ce5 /* offset 2969 */, 0x9e75 /* offset 2970 */, 0x9e7f /* offset 2971 */, 0x9ea5 /* offset 2972 */, 0x9ebb /* offset 2973 */, 0x9ec3 /* offset 2974 */, 0x9ecd /* offset 2975 */, 0x9ed1 /* offset 2976 */, 0x9ef9 /* offset 2977 */, 0x9efd /* offset 2978 */, 0x9f0e /* offset 2979 */, 0x9f13 /* offset 2980 */, 0x9f20 /* offset 2981 */, 0x9f3b /* offset 2982 */, 0x9f4a /* offset 2983 */, 0x9f52 /* offset 2984 */, 0x9f8d /* offset 2985 */, 0x9f9c /* offset 2986 */, 0x9fa0 /* offset 2987 */, 0x3012 /* offset 2988 */, 0x5344 /* offset 2989 */, 0x5345 /* offset 2990 */, 0x304b, 0x3099 /* offset 2991 */, 0x304d, 0x3099 /* offset 2993 */, 0x304f, 0x3099 /* offset 2995 */, 0x3051, 0x3099 /* offset 2997 */, 0x3053, 0x3099 /* offset 2999 */, 0x3055, 0x3099 /* offset 3001 */, 0x3057, 0x3099 /* offset 3003 */, 0x3059, 0x3099 /* offset 3005 */, 0x305b, 0x3099 /* offset 3007 */, 0x305d, 0x3099 /* offset 3009 */, 0x305f, 0x3099 /* offset 3011 */, 0x3061, 0x3099 /* offset 3013 */, 0x3064, 0x3099 /* offset 3015 */, 0x3066, 0x3099 /* offset 3017 */, 0x3068, 0x3099 /* offset 3019 */, 0x306f, 0x3099 /* offset 3021 */, 0x306f, 0x309a /* offset 3023 */, 0x3072, 0x3099 /* offset 3025 */, 0x3072, 0x309a /* offset 3027 */, 0x3075, 0x3099 /* offset 3029 */, 0x3075, 0x309a /* offset 3031 */, 0x3078, 0x3099 /* offset 3033 */, 0x3078, 0x309a /* offset 3035 */, 0x307b, 0x3099 /* offset 3037 */, 0x307b, 0x309a /* offset 3039 */, 0x3046, 0x3099 /* offset 3041 */, 0x20, 0x3099 /* offset 3043 */, 0x20, 0x309a /* offset 3045 */, 0x309d, 0x3099 /* offset 3047 */, 0x3088, 0x308a /* offset 3049 */, 0x30ab, 0x3099 /* offset 3051 */, 0x30ad, 0x3099 /* offset 3053 */, 0x30af, 0x3099 /* offset 3055 */, 0x30b1, 0x3099 /* offset 3057 */, 0x30b3, 0x3099 /* offset 3059 */, 0x30b5, 0x3099 /* offset 3061 */, 0x30b7, 0x3099 /* offset 3063 */, 0x30b9, 0x3099 /* offset 3065 */, 0x30bb, 0x3099 /* offset 3067 */, 0x30bd, 0x3099 /* offset 3069 */, 0x30bf, 0x3099 /* offset 3071 */, 0x30c1, 0x3099 /* offset 3073 */, 0x30c4, 0x3099 /* offset 3075 */, 0x30c6, 0x3099 /* offset 3077 */, 0x30c8, 0x3099 /* offset 3079 */, 0x30cf, 0x3099 /* offset 3081 */, 0x30cf, 0x309a /* offset 3083 */, 0x30d2, 0x3099 /* offset 3085 */, 0x30d2, 0x309a /* offset 3087 */, 0x30d5, 0x3099 /* offset 3089 */, 0x30d5, 0x309a /* offset 3091 */, 0x30d8, 0x3099 /* offset 3093 */, 0x30d8, 0x309a /* offset 3095 */, 0x30db, 0x3099 /* offset 3097 */, 0x30db, 0x309a /* offset 3099 */, 0x30a6, 0x3099 /* offset 3101 */, 0x30ef, 0x3099 /* offset 3103 */, 0x30f0, 0x3099 /* offset 3105 */, 0x30f1, 0x3099 /* offset 3107 */, 0x30f2, 0x3099 /* offset 3109 */, 0x30fd, 0x3099 /* offset 3111 */, 0x30b3, 0x30c8 /* offset 3113 */, 0x1100 /* offset 3115 */, 0x1101 /* offset 3116 */, 0x11aa /* offset 3117 */, 0x1102 /* offset 3118 */, 0x11ac /* offset 3119 */, 0x11ad /* offset 3120 */, 0x1103 /* offset 3121 */, 0x1104 /* offset 3122 */, 0x1105 /* offset 3123 */, 0x11b0 /* offset 3124 */, 0x11b1 /* offset 3125 */, 0x11b2 /* offset 3126 */, 0x11b3 /* offset 3127 */, 0x11b4 /* offset 3128 */, 0x11b5 /* offset 3129 */, 0x111a /* offset 3130 */, 0x1106 /* offset 3131 */, 0x1107 /* offset 3132 */, 0x1108 /* offset 3133 */, 0x1121 /* offset 3134 */, 0x1109 /* offset 3135 */, 0x110a /* offset 3136 */, 0x110b /* offset 3137 */, 0x110c /* offset 3138 */, 0x110d /* offset 3139 */, 0x110e /* offset 3140 */, 0x110f /* offset 3141 */, 0x1110 /* offset 3142 */, 0x1111 /* offset 3143 */, 0x1112 /* offset 3144 */, 0x1161 /* offset 3145 */, 0x1162 /* offset 3146 */, 0x1163 /* offset 3147 */, 0x1164 /* offset 3148 */, 0x1165 /* offset 3149 */, 0x1166 /* offset 3150 */, 0x1167 /* offset 3151 */, 0x1168 /* offset 3152 */, 0x1169 /* offset 3153 */, 0x116a /* offset 3154 */, 0x116b /* offset 3155 */, 0x116c /* offset 3156 */, 0x116d /* offset 3157 */, 0x116e /* offset 3158 */, 0x116f /* offset 3159 */, 0x1170 /* offset 3160 */, 0x1171 /* offset 3161 */, 0x1172 /* offset 3162 */, 0x1173 /* offset 3163 */, 0x1174 /* offset 3164 */, 0x1175 /* offset 3165 */, 0x1160 /* offset 3166 */, 0x1114 /* offset 3167 */, 0x1115 /* offset 3168 */, 0x11c7 /* offset 3169 */, 0x11c8 /* offset 3170 */, 0x11cc /* offset 3171 */, 0x11ce /* offset 3172 */, 0x11d3 /* offset 3173 */, 0x11d7 /* offset 3174 */, 0x11d9 /* offset 3175 */, 0x111c /* offset 3176 */, 0x11dd /* offset 3177 */, 0x11df /* offset 3178 */, 0x111d /* offset 3179 */, 0x111e /* offset 3180 */, 0x1120 /* offset 3181 */, 0x1122 /* offset 3182 */, 0x1123 /* offset 3183 */, 0x1127 /* offset 3184 */, 0x1129 /* offset 3185 */, 0x112b /* offset 3186 */, 0x112c /* offset 3187 */, 0x112d /* offset 3188 */, 0x112e /* offset 3189 */, 0x112f /* offset 3190 */, 0x1132 /* offset 3191 */, 0x1136 /* offset 3192 */, 0x1140 /* offset 3193 */, 0x1147 /* offset 3194 */, 0x114c /* offset 3195 */, 0x11f1 /* offset 3196 */, 0x11f2 /* offset 3197 */, 0x1157 /* offset 3198 */, 0x1158 /* offset 3199 */, 0x1159 /* offset 3200 */, 0x1184 /* offset 3201 */, 0x1185 /* offset 3202 */, 0x1188 /* offset 3203 */, 0x1191 /* offset 3204 */, 0x1192 /* offset 3205 */, 0x1194 /* offset 3206 */, 0x119e /* offset 3207 */, 0x11a1 /* offset 3208 */, 0x4e09 /* offset 3209 */, 0x56db /* offset 3210 */, 0x4e0a /* offset 3211 */, 0x4e2d /* offset 3212 */, 0x4e0b /* offset 3213 */, 0x7532 /* offset 3214 */, 0x4e19 /* offset 3215 */, 0x4e01 /* offset 3216 */, 0x5929 /* offset 3217 */, 0x5730 /* offset 3218 */, 0x28, 0x1100, 0x29 /* offset 3219 */, 0x28, 0x1102, 0x29 /* offset 3222 */, 0x28, 0x1103, 0x29 /* offset 3225 */, 0x28, 0x1105, 0x29 /* offset 3228 */, 0x28, 0x1106, 0x29 /* offset 3231 */, 0x28, 0x1107, 0x29 /* offset 3234 */, 0x28, 0x1109, 0x29 /* offset 3237 */, 0x28, 0x110b, 0x29 /* offset 3240 */, 0x28, 0x110c, 0x29 /* offset 3243 */, 0x28, 0x110e, 0x29 /* offset 3246 */, 0x28, 0x110f, 0x29 /* offset 3249 */, 0x28, 0x1110, 0x29 /* offset 3252 */, 0x28, 0x1111, 0x29 /* offset 3255 */, 0x28, 0x1112, 0x29 /* offset 3258 */, 0x28, 0x1100, 0x1161, 0x29 /* offset 3261 */, 0x28, 0x1102, 0x1161, 0x29 /* offset 3265 */, 0x28, 0x1103, 0x1161, 0x29 /* offset 3269 */, 0x28, 0x1105, 0x1161, 0x29 /* offset 3273 */, 0x28, 0x1106, 0x1161, 0x29 /* offset 3277 */, 0x28, 0x1107, 0x1161, 0x29 /* offset 3281 */, 0x28, 0x1109, 0x1161, 0x29 /* offset 3285 */, 0x28, 0x110b, 0x1161, 0x29 /* offset 3289 */, 0x28, 0x110c, 0x1161, 0x29 /* offset 3293 */, 0x28, 0x110e, 0x1161, 0x29 /* offset 3297 */, 0x28, 0x110f, 0x1161, 0x29 /* offset 3301 */, 0x28, 0x1110, 0x1161, 0x29 /* offset 3305 */, 0x28, 0x1111, 0x1161, 0x29 /* offset 3309 */, 0x28, 0x1112, 0x1161, 0x29 /* offset 3313 */, 0x28, 0x110c, 0x116e, 0x29 /* offset 3317 */, 0x28, 0x110b, 0x1169, 0x110c, 0x1165, 0x11ab, 0x29 /* offset 3321 */, 0x28, 0x110b, 0x1169, 0x1112, 0x116e, 0x29 /* offset 3328 */, 0x28, 0x4e00, 0x29 /* offset 3334 */, 0x28, 0x4e8c, 0x29 /* offset 3337 */, 0x28, 0x4e09, 0x29 /* offset 3340 */, 0x28, 0x56db, 0x29 /* offset 3343 */, 0x28, 0x4e94, 0x29 /* offset 3346 */, 0x28, 0x516d, 0x29 /* offset 3349 */, 0x28, 0x4e03, 0x29 /* offset 3352 */, 0x28, 0x516b, 0x29 /* offset 3355 */, 0x28, 0x4e5d, 0x29 /* offset 3358 */, 0x28, 0x5341, 0x29 /* offset 3361 */, 0x28, 0x6708, 0x29 /* offset 3364 */, 0x28, 0x706b, 0x29 /* offset 3367 */, 0x28, 0x6c34, 0x29 /* offset 3370 */, 0x28, 0x6728, 0x29 /* offset 3373 */, 0x28, 0x91d1, 0x29 /* offset 3376 */, 0x28, 0x571f, 0x29 /* offset 3379 */, 0x28, 0x65e5, 0x29 /* offset 3382 */, 0x28, 0x682a, 0x29 /* offset 3385 */, 0x28, 0x6709, 0x29 /* offset 3388 */, 0x28, 0x793e, 0x29 /* offset 3391 */, 0x28, 0x540d, 0x29 /* offset 3394 */, 0x28, 0x7279, 0x29 /* offset 3397 */, 0x28, 0x8ca1, 0x29 /* offset 3400 */, 0x28, 0x795d, 0x29 /* offset 3403 */, 0x28, 0x52b4, 0x29 /* offset 3406 */, 0x28, 0x4ee3, 0x29 /* offset 3409 */, 0x28, 0x547c, 0x29 /* offset 3412 */, 0x28, 0x5b66, 0x29 /* offset 3415 */, 0x28, 0x76e3, 0x29 /* offset 3418 */, 0x28, 0x4f01, 0x29 /* offset 3421 */, 0x28, 0x8cc7, 0x29 /* offset 3424 */, 0x28, 0x5354, 0x29 /* offset 3427 */, 0x28, 0x796d, 0x29 /* offset 3430 */, 0x28, 0x4f11, 0x29 /* offset 3433 */, 0x28, 0x81ea, 0x29 /* offset 3436 */, 0x28, 0x81f3, 0x29 /* offset 3439 */, 0x554f /* offset 3442 */, 0x5e7c /* offset 3443 */, 0x7b8f /* offset 3444 */, 0x50, 0x54, 0x45 /* offset 3445 */, 0x32, 0x31 /* offset 3448 */, 0x32, 0x32 /* offset 3450 */, 0x32, 0x33 /* offset 3452 */, 0x32, 0x34 /* offset 3454 */, 0x32, 0x35 /* offset 3456 */, 0x32, 0x36 /* offset 3458 */, 0x32, 0x37 /* offset 3460 */, 0x32, 0x38 /* offset 3462 */, 0x32, 0x39 /* offset 3464 */, 0x33, 0x30 /* offset 3466 */, 0x33, 0x31 /* offset 3468 */, 0x33, 0x32 /* offset 3470 */, 0x33, 0x33 /* offset 3472 */, 0x33, 0x34 /* offset 3474 */, 0x33, 0x35 /* offset 3476 */, 0x1100, 0x1161 /* offset 3478 */, 0x1102, 0x1161 /* offset 3480 */, 0x1103, 0x1161 /* offset 3482 */, 0x1105, 0x1161 /* offset 3484 */, 0x1106, 0x1161 /* offset 3486 */, 0x1107, 0x1161 /* offset 3488 */, 0x1109, 0x1161 /* offset 3490 */, 0x110b, 0x1161 /* offset 3492 */, 0x110c, 0x1161 /* offset 3494 */, 0x110e, 0x1161 /* offset 3496 */, 0x110f, 0x1161 /* offset 3498 */, 0x1110, 0x1161 /* offset 3500 */, 0x1111, 0x1161 /* offset 3502 */, 0x1112, 0x1161 /* offset 3504 */, 0x110e, 0x1161, 0x11b7, 0x1100, 0x1169 /* offset 3506 */, 0x110c, 0x116e, 0x110b, 0x1174 /* offset 3511 */, 0x110b, 0x116e /* offset 3515 */, 0x4e94 /* offset 3517 */, 0x516d /* offset 3518 */, 0x4e03 /* offset 3519 */, 0x4e5d /* offset 3520 */, 0x682a /* offset 3521 */, 0x6709 /* offset 3522 */, 0x793e /* offset 3523 */, 0x540d /* offset 3524 */, 0x7279 /* offset 3525 */, 0x8ca1 /* offset 3526 */, 0x795d /* offset 3527 */, 0x52b4 /* offset 3528 */, 0x79d8 /* offset 3529 */, 0x7537 /* offset 3530 */, 0x9069 /* offset 3531 */, 0x512a /* offset 3532 */, 0x5370 /* offset 3533 */, 0x6ce8 /* offset 3534 */, 0x9805 /* offset 3535 */, 0x4f11 /* offset 3536 */, 0x5199 /* offset 3537 */, 0x6b63 /* offset 3538 */, 0x5de6 /* offset 3539 */, 0x53f3 /* offset 3540 */, 0x533b /* offset 3541 */, 0x5b97 /* offset 3542 */, 0x5b66 /* offset 3543 */, 0x76e3 /* offset 3544 */, 0x4f01 /* offset 3545 */, 0x8cc7 /* offset 3546 */, 0x5354 /* offset 3547 */, 0x591c /* offset 3548 */, 0x33, 0x36 /* offset 3549 */, 0x33, 0x37 /* offset 3551 */, 0x33, 0x38 /* offset 3553 */, 0x33, 0x39 /* offset 3555 */, 0x34, 0x30 /* offset 3557 */, 0x34, 0x31 /* offset 3559 */, 0x34, 0x32 /* offset 3561 */, 0x34, 0x33 /* offset 3563 */, 0x34, 0x34 /* offset 3565 */, 0x34, 0x35 /* offset 3567 */, 0x34, 0x36 /* offset 3569 */, 0x34, 0x37 /* offset 3571 */, 0x34, 0x38 /* offset 3573 */, 0x34, 0x39 /* offset 3575 */, 0x35, 0x30 /* offset 3577 */, 0x31, 0x6708 /* offset 3579 */, 0x32, 0x6708 /* offset 3581 */, 0x33, 0x6708 /* offset 3583 */, 0x34, 0x6708 /* offset 3585 */, 0x35, 0x6708 /* offset 3587 */, 0x36, 0x6708 /* offset 3589 */, 0x37, 0x6708 /* offset 3591 */, 0x38, 0x6708 /* offset 3593 */, 0x39, 0x6708 /* offset 3595 */, 0x31, 0x30, 0x6708 /* offset 3597 */, 0x31, 0x31, 0x6708 /* offset 3600 */, 0x31, 0x32, 0x6708 /* offset 3603 */, 0x48, 0x67 /* offset 3606 */, 0x65, 0x72, 0x67 /* offset 3608 */, 0x65, 0x56 /* offset 3611 */, 0x4c, 0x54, 0x44 /* offset 3613 */, 0x30a2 /* offset 3616 */, 0x30a4 /* offset 3617 */, 0x30a6 /* offset 3618 */, 0x30a8 /* offset 3619 */, 0x30aa /* offset 3620 */, 0x30ab /* offset 3621 */, 0x30ad /* offset 3622 */, 0x30af /* offset 3623 */, 0x30b1 /* offset 3624 */, 0x30b3 /* offset 3625 */, 0x30b5 /* offset 3626 */, 0x30b7 /* offset 3627 */, 0x30b9 /* offset 3628 */, 0x30bb /* offset 3629 */, 0x30bd /* offset 3630 */, 0x30bf /* offset 3631 */, 0x30c1 /* offset 3632 */, 0x30c4 /* offset 3633 */, 0x30c6 /* offset 3634 */, 0x30c8 /* offset 3635 */, 0x30ca /* offset 3636 */, 0x30cb /* offset 3637 */, 0x30cc /* offset 3638 */, 0x30cd /* offset 3639 */, 0x30ce /* offset 3640 */, 0x30cf /* offset 3641 */, 0x30d2 /* offset 3642 */, 0x30d5 /* offset 3643 */, 0x30d8 /* offset 3644 */, 0x30db /* offset 3645 */, 0x30de /* offset 3646 */, 0x30df /* offset 3647 */, 0x30e0 /* offset 3648 */, 0x30e1 /* offset 3649 */, 0x30e2 /* offset 3650 */, 0x30e4 /* offset 3651 */, 0x30e6 /* offset 3652 */, 0x30e8 /* offset 3653 */, 0x30e9 /* offset 3654 */, 0x30ea /* offset 3655 */, 0x30eb /* offset 3656 */, 0x30ec /* offset 3657 */, 0x30ed /* offset 3658 */, 0x30ef /* offset 3659 */, 0x30f0 /* offset 3660 */, 0x30f1 /* offset 3661 */, 0x30f2 /* offset 3662 */, 0x30a2, 0x30cf, 0x309a, 0x30fc, 0x30c8 /* offset 3663 */, 0x30a2, 0x30eb, 0x30d5, 0x30a1 /* offset 3668 */, 0x30a2, 0x30f3, 0x30d8, 0x309a, 0x30a2 /* offset 3672 */, 0x30a2, 0x30fc, 0x30eb /* offset 3677 */, 0x30a4, 0x30cb, 0x30f3, 0x30af, 0x3099 /* offset 3680 */, 0x30a4, 0x30f3, 0x30c1 /* offset 3685 */, 0x30a6, 0x30a9, 0x30f3 /* offset 3688 */, 0x30a8, 0x30b9, 0x30af, 0x30fc, 0x30c8, 0x3099 /* offset 3691 */, 0x30a8, 0x30fc, 0x30ab, 0x30fc /* offset 3697 */, 0x30aa, 0x30f3, 0x30b9 /* offset 3701 */, 0x30aa, 0x30fc, 0x30e0 /* offset 3704 */, 0x30ab, 0x30a4, 0x30ea /* offset 3707 */, 0x30ab, 0x30e9, 0x30c3, 0x30c8 /* offset 3710 */, 0x30ab, 0x30ed, 0x30ea, 0x30fc /* offset 3714 */, 0x30ab, 0x3099, 0x30ed, 0x30f3 /* offset 3718 */, 0x30ab, 0x3099, 0x30f3, 0x30de /* offset 3722 */, 0x30ad, 0x3099, 0x30ab, 0x3099 /* offset 3726 */, 0x30ad, 0x3099, 0x30cb, 0x30fc /* offset 3730 */, 0x30ad, 0x30e5, 0x30ea, 0x30fc /* offset 3734 */, 0x30ad, 0x3099, 0x30eb, 0x30bf, 0x3099, 0x30fc /* offset 3738 */, 0x30ad, 0x30ed /* offset 3744 */, 0x30ad, 0x30ed, 0x30af, 0x3099, 0x30e9, 0x30e0 /* offset 3746 */, 0x30ad, 0x30ed, 0x30e1, 0x30fc, 0x30c8, 0x30eb /* offset 3752 */, 0x30ad, 0x30ed, 0x30ef, 0x30c3, 0x30c8 /* offset 3758 */, 0x30af, 0x3099, 0x30e9, 0x30e0 /* offset 3763 */, 0x30af, 0x3099, 0x30e9, 0x30e0, 0x30c8, 0x30f3 /* offset 3767 */, 0x30af, 0x30eb, 0x30bb, 0x3099, 0x30a4, 0x30ed /* offset 3773 */, 0x30af, 0x30ed, 0x30fc, 0x30cd /* offset 3779 */, 0x30b1, 0x30fc, 0x30b9 /* offset 3783 */, 0x30b3, 0x30eb, 0x30ca /* offset 3786 */, 0x30b3, 0x30fc, 0x30db, 0x309a /* offset 3789 */, 0x30b5, 0x30a4, 0x30af, 0x30eb /* offset 3793 */, 0x30b5, 0x30f3, 0x30c1, 0x30fc, 0x30e0 /* offset 3797 */, 0x30b7, 0x30ea, 0x30f3, 0x30af, 0x3099 /* offset 3802 */, 0x30bb, 0x30f3, 0x30c1 /* offset 3807 */, 0x30bb, 0x30f3, 0x30c8 /* offset 3810 */, 0x30bf, 0x3099, 0x30fc, 0x30b9 /* offset 3813 */, 0x30c6, 0x3099, 0x30b7 /* offset 3817 */, 0x30c8, 0x3099, 0x30eb /* offset 3820 */, 0x30c8, 0x30f3 /* offset 3823 */, 0x30ca, 0x30ce /* offset 3825 */, 0x30ce, 0x30c3, 0x30c8 /* offset 3827 */, 0x30cf, 0x30a4, 0x30c4 /* offset 3830 */, 0x30cf, 0x309a, 0x30fc, 0x30bb, 0x30f3, 0x30c8 /* offset 3833 */, 0x30cf, 0x309a, 0x30fc, 0x30c4 /* offset 3839 */, 0x30cf, 0x3099, 0x30fc, 0x30ec, 0x30eb /* offset 3843 */, 0x30d2, 0x309a, 0x30a2, 0x30b9, 0x30c8, 0x30eb /* offset 3848 */, 0x30d2, 0x309a, 0x30af, 0x30eb /* offset 3854 */, 0x30d2, 0x309a, 0x30b3 /* offset 3858 */, 0x30d2, 0x3099, 0x30eb /* offset 3861 */, 0x30d5, 0x30a1, 0x30e9, 0x30c3, 0x30c8, 0x3099 /* offset 3864 */, 0x30d5, 0x30a3, 0x30fc, 0x30c8 /* offset 3870 */, 0x30d5, 0x3099, 0x30c3, 0x30b7, 0x30a7, 0x30eb /* offset 3874 */, 0x30d5, 0x30e9, 0x30f3 /* offset 3880 */, 0x30d8, 0x30af, 0x30bf, 0x30fc, 0x30eb /* offset 3883 */, 0x30d8, 0x309a, 0x30bd /* offset 3888 */, 0x30d8, 0x309a, 0x30cb, 0x30d2 /* offset 3891 */, 0x30d8, 0x30eb, 0x30c4 /* offset 3895 */, 0x30d8, 0x309a, 0x30f3, 0x30b9 /* offset 3898 */, 0x30d8, 0x309a, 0x30fc, 0x30b7, 0x3099 /* offset 3902 */, 0x30d8, 0x3099, 0x30fc, 0x30bf /* offset 3907 */, 0x30db, 0x309a, 0x30a4, 0x30f3, 0x30c8 /* offset 3911 */, 0x30db, 0x3099, 0x30eb, 0x30c8 /* offset 3916 */, 0x30db, 0x30f3 /* offset 3920 */, 0x30db, 0x309a, 0x30f3, 0x30c8, 0x3099 /* offset 3922 */, 0x30db, 0x30fc, 0x30eb /* offset 3927 */, 0x30db, 0x30fc, 0x30f3 /* offset 3930 */, 0x30de, 0x30a4, 0x30af, 0x30ed /* offset 3933 */, 0x30de, 0x30a4, 0x30eb /* offset 3937 */, 0x30de, 0x30c3, 0x30cf /* offset 3940 */, 0x30de, 0x30eb, 0x30af /* offset 3943 */, 0x30de, 0x30f3, 0x30b7, 0x30e7, 0x30f3 /* offset 3946 */, 0x30df, 0x30af, 0x30ed, 0x30f3 /* offset 3951 */, 0x30df, 0x30ea /* offset 3955 */, 0x30df, 0x30ea, 0x30cf, 0x3099, 0x30fc, 0x30eb /* offset 3957 */, 0x30e1, 0x30ab, 0x3099 /* offset 3963 */, 0x30e1, 0x30ab, 0x3099, 0x30c8, 0x30f3 /* offset 3966 */, 0x30e1, 0x30fc, 0x30c8, 0x30eb /* offset 3971 */, 0x30e4, 0x30fc, 0x30c8, 0x3099 /* offset 3975 */, 0x30e4, 0x30fc, 0x30eb /* offset 3979 */, 0x30e6, 0x30a2, 0x30f3 /* offset 3982 */, 0x30ea, 0x30c3, 0x30c8, 0x30eb /* offset 3985 */, 0x30ea, 0x30e9 /* offset 3989 */, 0x30eb, 0x30d2, 0x309a, 0x30fc /* offset 3991 */, 0x30eb, 0x30fc, 0x30d5, 0x3099, 0x30eb /* offset 3995 */, 0x30ec, 0x30e0 /* offset 4000 */, 0x30ec, 0x30f3, 0x30c8, 0x30b1, 0x3099, 0x30f3 /* offset 4002 */, 0x30ef, 0x30c3, 0x30c8 /* offset 4008 */, 0x30, 0x70b9 /* offset 4011 */, 0x31, 0x70b9 /* offset 4013 */, 0x32, 0x70b9 /* offset 4015 */, 0x33, 0x70b9 /* offset 4017 */, 0x34, 0x70b9 /* offset 4019 */, 0x35, 0x70b9 /* offset 4021 */, 0x36, 0x70b9 /* offset 4023 */, 0x37, 0x70b9 /* offset 4025 */, 0x38, 0x70b9 /* offset 4027 */, 0x39, 0x70b9 /* offset 4029 */, 0x31, 0x30, 0x70b9 /* offset 4031 */, 0x31, 0x31, 0x70b9 /* offset 4034 */, 0x31, 0x32, 0x70b9 /* offset 4037 */, 0x31, 0x33, 0x70b9 /* offset 4040 */, 0x31, 0x34, 0x70b9 /* offset 4043 */, 0x31, 0x35, 0x70b9 /* offset 4046 */, 0x31, 0x36, 0x70b9 /* offset 4049 */, 0x31, 0x37, 0x70b9 /* offset 4052 */, 0x31, 0x38, 0x70b9 /* offset 4055 */, 0x31, 0x39, 0x70b9 /* offset 4058 */, 0x32, 0x30, 0x70b9 /* offset 4061 */, 0x32, 0x31, 0x70b9 /* offset 4064 */, 0x32, 0x32, 0x70b9 /* offset 4067 */, 0x32, 0x33, 0x70b9 /* offset 4070 */, 0x32, 0x34, 0x70b9 /* offset 4073 */, 0x68, 0x50, 0x61 /* offset 4076 */, 0x64, 0x61 /* offset 4079 */, 0x41, 0x55 /* offset 4081 */, 0x62, 0x61, 0x72 /* offset 4083 */, 0x6f, 0x56 /* offset 4086 */, 0x70, 0x63 /* offset 4088 */, 0x64, 0x6d /* offset 4090 */, 0x64, 0x6d, 0x32 /* offset 4092 */, 0x64, 0x6d, 0x33 /* offset 4095 */, 0x49, 0x55 /* offset 4098 */, 0x5e73, 0x6210 /* offset 4100 */, 0x662d, 0x548c /* offset 4102 */, 0x5927, 0x6b63 /* offset 4104 */, 0x660e, 0x6cbb /* offset 4106 */, 0x682a, 0x5f0f, 0x4f1a, 0x793e /* offset 4108 */, 0x70, 0x41 /* offset 4112 */, 0x6e, 0x41 /* offset 4114 */, 0x3bc, 0x41 /* offset 4116 */, 0x6d, 0x41 /* offset 4118 */, 0x6b, 0x41 /* offset 4120 */, 0x4b, 0x42 /* offset 4122 */, 0x4d, 0x42 /* offset 4124 */, 0x47, 0x42 /* offset 4126 */, 0x63, 0x61, 0x6c /* offset 4128 */, 0x6b, 0x63, 0x61, 0x6c /* offset 4131 */, 0x70, 0x46 /* offset 4135 */, 0x6e, 0x46 /* offset 4137 */, 0x3bc, 0x46 /* offset 4139 */, 0x3bc, 0x67 /* offset 4141 */, 0x6d, 0x67 /* offset 4143 */, 0x6b, 0x67 /* offset 4145 */, 0x48, 0x7a /* offset 4147 */, 0x6b, 0x48, 0x7a /* offset 4149 */, 0x4d, 0x48, 0x7a /* offset 4152 */, 0x47, 0x48, 0x7a /* offset 4155 */, 0x54, 0x48, 0x7a /* offset 4158 */, 0x3bc, 0x6c /* offset 4161 */, 0x6d, 0x6c /* offset 4163 */, 0x64, 0x6c /* offset 4165 */, 0x6b, 0x6c /* offset 4167 */, 0x66, 0x6d /* offset 4169 */, 0x6e, 0x6d /* offset 4171 */, 0x3bc, 0x6d /* offset 4173 */, 0x6d, 0x6d /* offset 4175 */, 0x63, 0x6d /* offset 4177 */, 0x6b, 0x6d /* offset 4179 */, 0x6d, 0x6d, 0x32 /* offset 4181 */, 0x63, 0x6d, 0x32 /* offset 4184 */, 0x6d, 0x32 /* offset 4187 */, 0x6b, 0x6d, 0x32 /* offset 4189 */, 0x6d, 0x6d, 0x33 /* offset 4192 */, 0x63, 0x6d, 0x33 /* offset 4195 */, 0x6d, 0x33 /* offset 4198 */, 0x6b, 0x6d, 0x33 /* offset 4200 */, 0x6d, 0x2215, 0x73 /* offset 4203 */, 0x6d, 0x2215, 0x73, 0x32 /* offset 4206 */, 0x50, 0x61 /* offset 4210 */, 0x6b, 0x50, 0x61 /* offset 4212 */, 0x4d, 0x50, 0x61 /* offset 4215 */, 0x47, 0x50, 0x61 /* offset 4218 */, 0x72, 0x61, 0x64 /* offset 4221 */, 0x72, 0x61, 0x64, 0x2215, 0x73 /* offset 4224 */, 0x72, 0x61, 0x64, 0x2215, 0x73, 0x32 /* offset 4229 */, 0x70, 0x73 /* offset 4235 */, 0x6e, 0x73 /* offset 4237 */, 0x3bc, 0x73 /* offset 4239 */, 0x6d, 0x73 /* offset 4241 */, 0x70, 0x56 /* offset 4243 */, 0x6e, 0x56 /* offset 4245 */, 0x3bc, 0x56 /* offset 4247 */, 0x6d, 0x56 /* offset 4249 */, 0x6b, 0x56 /* offset 4251 */, 0x4d, 0x56 /* offset 4253 */, 0x70, 0x57 /* offset 4255 */, 0x6e, 0x57 /* offset 4257 */, 0x3bc, 0x57 /* offset 4259 */, 0x6d, 0x57 /* offset 4261 */, 0x6b, 0x57 /* offset 4263 */, 0x4d, 0x57 /* offset 4265 */, 0x6b, 0x3a9 /* offset 4267 */, 0x4d, 0x3a9 /* offset 4269 */, 0x61, 0x2e, 0x6d, 0x2e /* offset 4271 */, 0x42, 0x71 /* offset 4275 */, 0x63, 0x63 /* offset 4277 */, 0x63, 0x64 /* offset 4279 */, 0x43, 0x2215, 0x6b, 0x67 /* offset 4281 */, 0x43, 0x6f, 0x2e /* offset 4285 */, 0x64, 0x42 /* offset 4288 */, 0x47, 0x79 /* offset 4290 */, 0x68, 0x61 /* offset 4292 */, 0x48, 0x50 /* offset 4294 */, 0x69, 0x6e /* offset 4296 */, 0x4b, 0x4b /* offset 4298 */, 0x4b, 0x4d /* offset 4300 */, 0x6b, 0x74 /* offset 4302 */, 0x6c, 0x6d /* offset 4304 */, 0x6c, 0x6e /* offset 4306 */, 0x6c, 0x6f, 0x67 /* offset 4308 */, 0x6c, 0x78 /* offset 4311 */, 0x6d, 0x62 /* offset 4313 */, 0x6d, 0x69, 0x6c /* offset 4315 */, 0x6d, 0x6f, 0x6c /* offset 4318 */, 0x50, 0x48 /* offset 4321 */, 0x70, 0x2e, 0x6d, 0x2e /* offset 4323 */, 0x50, 0x50, 0x4d /* offset 4327 */, 0x50, 0x52 /* offset 4330 */, 0x73, 0x72 /* offset 4332 */, 0x53, 0x76 /* offset 4334 */, 0x57, 0x62 /* offset 4336 */, 0x56, 0x2215, 0x6d /* offset 4338 */, 0x41, 0x2215, 0x6d /* offset 4341 */, 0x31, 0x65e5 /* offset 4344 */, 0x32, 0x65e5 /* offset 4346 */, 0x33, 0x65e5 /* offset 4348 */, 0x34, 0x65e5 /* offset 4350 */, 0x35, 0x65e5 /* offset 4352 */, 0x36, 0x65e5 /* offset 4354 */, 0x37, 0x65e5 /* offset 4356 */, 0x38, 0x65e5 /* offset 4358 */, 0x39, 0x65e5 /* offset 4360 */, 0x31, 0x30, 0x65e5 /* offset 4362 */, 0x31, 0x31, 0x65e5 /* offset 4365 */, 0x31, 0x32, 0x65e5 /* offset 4368 */, 0x31, 0x33, 0x65e5 /* offset 4371 */, 0x31, 0x34, 0x65e5 /* offset 4374 */, 0x31, 0x35, 0x65e5 /* offset 4377 */, 0x31, 0x36, 0x65e5 /* offset 4380 */, 0x31, 0x37, 0x65e5 /* offset 4383 */, 0x31, 0x38, 0x65e5 /* offset 4386 */, 0x31, 0x39, 0x65e5 /* offset 4389 */, 0x32, 0x30, 0x65e5 /* offset 4392 */, 0x32, 0x31, 0x65e5 /* offset 4395 */, 0x32, 0x32, 0x65e5 /* offset 4398 */, 0x32, 0x33, 0x65e5 /* offset 4401 */, 0x32, 0x34, 0x65e5 /* offset 4404 */, 0x32, 0x35, 0x65e5 /* offset 4407 */, 0x32, 0x36, 0x65e5 /* offset 4410 */, 0x32, 0x37, 0x65e5 /* offset 4413 */, 0x32, 0x38, 0x65e5 /* offset 4416 */, 0x32, 0x39, 0x65e5 /* offset 4419 */, 0x33, 0x30, 0x65e5 /* offset 4422 */, 0x33, 0x31, 0x65e5 /* offset 4425 */, 0x67, 0x61, 0x6c /* offset 4428 */, 0x44a /* offset 4431 */, 0x44c /* offset 4432 */, 0xa76f /* offset 4433 */, 0x126 /* offset 4434 */, 0x153 /* offset 4435 */, 0xa727 /* offset 4436 */, 0xab37 /* offset 4437 */, 0x26b /* offset 4438 */, 0xab52 /* offset 4439 */, 0x8c48 /* offset 4440 */, 0x66f4 /* offset 4441 */, 0x8cc8 /* offset 4442 */, 0x6ed1 /* offset 4443 */, 0x4e32 /* offset 4444 */, 0x53e5 /* offset 4445 */, 0x5951 /* offset 4446 */, 0x5587 /* offset 4447 */, 0x5948 /* offset 4448 */, 0x61f6 /* offset 4449 */, 0x7669 /* offset 4450 */, 0x7f85 /* offset 4451 */, 0x863f /* offset 4452 */, 0x87ba /* offset 4453 */, 0x88f8 /* offset 4454 */, 0x908f /* offset 4455 */, 0x6a02 /* offset 4456 */, 0x6d1b /* offset 4457 */, 0x70d9 /* offset 4458 */, 0x73de /* offset 4459 */, 0x843d /* offset 4460 */, 0x916a /* offset 4461 */, 0x99f1 /* offset 4462 */, 0x4e82 /* offset 4463 */, 0x5375 /* offset 4464 */, 0x6b04 /* offset 4465 */, 0x721b /* offset 4466 */, 0x862d /* offset 4467 */, 0x9e1e /* offset 4468 */, 0x5d50 /* offset 4469 */, 0x6feb /* offset 4470 */, 0x85cd /* offset 4471 */, 0x8964 /* offset 4472 */, 0x62c9 /* offset 4473 */, 0x81d8 /* offset 4474 */, 0x881f /* offset 4475 */, 0x5eca /* offset 4476 */, 0x6717 /* offset 4477 */, 0x6d6a /* offset 4478 */, 0x72fc /* offset 4479 */, 0x90ce /* offset 4480 */, 0x4f86 /* offset 4481 */, 0x51b7 /* offset 4482 */, 0x52de /* offset 4483 */, 0x64c4 /* offset 4484 */, 0x6ad3 /* offset 4485 */, 0x7210 /* offset 4486 */, 0x76e7 /* offset 4487 */, 0x8606 /* offset 4488 */, 0x865c /* offset 4489 */, 0x8def /* offset 4490 */, 0x9732 /* offset 4491 */, 0x9b6f /* offset 4492 */, 0x9dfa /* offset 4493 */, 0x788c /* offset 4494 */, 0x797f /* offset 4495 */, 0x7da0 /* offset 4496 */, 0x83c9 /* offset 4497 */, 0x9304 /* offset 4498 */, 0x8ad6 /* offset 4499 */, 0x58df /* offset 4500 */, 0x5f04 /* offset 4501 */, 0x7c60 /* offset 4502 */, 0x807e /* offset 4503 */, 0x7262 /* offset 4504 */, 0x78ca /* offset 4505 */, 0x8cc2 /* offset 4506 */, 0x96f7 /* offset 4507 */, 0x58d8 /* offset 4508 */, 0x5c62 /* offset 4509 */, 0x6a13 /* offset 4510 */, 0x6dda /* offset 4511 */, 0x6f0f /* offset 4512 */, 0x7d2f /* offset 4513 */, 0x7e37 /* offset 4514 */, 0x964b /* offset 4515 */, 0x52d2 /* offset 4516 */, 0x808b /* offset 4517 */, 0x51dc /* offset 4518 */, 0x51cc /* offset 4519 */, 0x7a1c /* offset 4520 */, 0x7dbe /* offset 4521 */, 0x83f1 /* offset 4522 */, 0x9675 /* offset 4523 */, 0x8b80 /* offset 4524 */, 0x62cf /* offset 4525 */, 0x8afe /* offset 4526 */, 0x4e39 /* offset 4527 */, 0x5be7 /* offset 4528 */, 0x6012 /* offset 4529 */, 0x7387 /* offset 4530 */, 0x7570 /* offset 4531 */, 0x5317 /* offset 4532 */, 0x78fb /* offset 4533 */, 0x4fbf /* offset 4534 */, 0x5fa9 /* offset 4535 */, 0x4e0d /* offset 4536 */, 0x6ccc /* offset 4537 */, 0x6578 /* offset 4538 */, 0x7d22 /* offset 4539 */, 0x53c3 /* offset 4540 */, 0x585e /* offset 4541 */, 0x7701 /* offset 4542 */, 0x8449 /* offset 4543 */, 0x8aaa /* offset 4544 */, 0x6bba /* offset 4545 */, 0x6c88 /* offset 4546 */, 0x62fe /* offset 4547 */, 0x82e5 /* offset 4548 */, 0x63a0 /* offset 4549 */, 0x7565 /* offset 4550 */, 0x4eae /* offset 4551 */, 0x5169 /* offset 4552 */, 0x51c9 /* offset 4553 */, 0x6881 /* offset 4554 */, 0x7ce7 /* offset 4555 */, 0x826f /* offset 4556 */, 0x8ad2 /* offset 4557 */, 0x91cf /* offset 4558 */, 0x52f5 /* offset 4559 */, 0x5442 /* offset 4560 */, 0x5eec /* offset 4561 */, 0x65c5 /* offset 4562 */, 0x6ffe /* offset 4563 */, 0x792a /* offset 4564 */, 0x95ad /* offset 4565 */, 0x9a6a /* offset 4566 */, 0x9e97 /* offset 4567 */, 0x9ece /* offset 4568 */, 0x66c6 /* offset 4569 */, 0x6b77 /* offset 4570 */, 0x8f62 /* offset 4571 */, 0x5e74 /* offset 4572 */, 0x6190 /* offset 4573 */, 0x6200 /* offset 4574 */, 0x649a /* offset 4575 */, 0x6f23 /* offset 4576 */, 0x7149 /* offset 4577 */, 0x7489 /* offset 4578 */, 0x79ca /* offset 4579 */, 0x7df4 /* offset 4580 */, 0x806f /* offset 4581 */, 0x8f26 /* offset 4582 */, 0x84ee /* offset 4583 */, 0x9023 /* offset 4584 */, 0x934a /* offset 4585 */, 0x5217 /* offset 4586 */, 0x52a3 /* offset 4587 */, 0x54bd /* offset 4588 */, 0x70c8 /* offset 4589 */, 0x88c2 /* offset 4590 */, 0x5ec9 /* offset 4591 */, 0x5ff5 /* offset 4592 */, 0x637b /* offset 4593 */, 0x6bae /* offset 4594 */, 0x7c3e /* offset 4595 */, 0x7375 /* offset 4596 */, 0x4ee4 /* offset 4597 */, 0x56f9 /* offset 4598 */, 0x5dba /* offset 4599 */, 0x601c /* offset 4600 */, 0x73b2 /* offset 4601 */, 0x7469 /* offset 4602 */, 0x7f9a /* offset 4603 */, 0x8046 /* offset 4604 */, 0x9234 /* offset 4605 */, 0x96f6 /* offset 4606 */, 0x9748 /* offset 4607 */, 0x9818 /* offset 4608 */, 0x4f8b /* offset 4609 */, 0x79ae /* offset 4610 */, 0x91b4 /* offset 4611 */, 0x96b8 /* offset 4612 */, 0x60e1 /* offset 4613 */, 0x4e86 /* offset 4614 */, 0x50da /* offset 4615 */, 0x5bee /* offset 4616 */, 0x5c3f /* offset 4617 */, 0x6599 /* offset 4618 */, 0x71ce /* offset 4619 */, 0x7642 /* offset 4620 */, 0x84fc /* offset 4621 */, 0x907c /* offset 4622 */, 0x6688 /* offset 4623 */, 0x962e /* offset 4624 */, 0x5289 /* offset 4625 */, 0x677b /* offset 4626 */, 0x67f3 /* offset 4627 */, 0x6d41 /* offset 4628 */, 0x6e9c /* offset 4629 */, 0x7409 /* offset 4630 */, 0x7559 /* offset 4631 */, 0x786b /* offset 4632 */, 0x7d10 /* offset 4633 */, 0x985e /* offset 4634 */, 0x622e /* offset 4635 */, 0x9678 /* offset 4636 */, 0x502b /* offset 4637 */, 0x5d19 /* offset 4638 */, 0x6dea /* offset 4639 */, 0x8f2a /* offset 4640 */, 0x5f8b /* offset 4641 */, 0x6144 /* offset 4642 */, 0x6817 /* offset 4643 */, 0x9686 /* offset 4644 */, 0x5229 /* offset 4645 */, 0x540f /* offset 4646 */, 0x5c65 /* offset 4647 */, 0x6613 /* offset 4648 */, 0x674e /* offset 4649 */, 0x68a8 /* offset 4650 */, 0x6ce5 /* offset 4651 */, 0x7406 /* offset 4652 */, 0x75e2 /* offset 4653 */, 0x7f79 /* offset 4654 */, 0x88cf /* offset 4655 */, 0x88e1 /* offset 4656 */, 0x96e2 /* offset 4657 */, 0x533f /* offset 4658 */, 0x6eba /* offset 4659 */, 0x541d /* offset 4660 */, 0x71d0 /* offset 4661 */, 0x7498 /* offset 4662 */, 0x85fa /* offset 4663 */, 0x96a3 /* offset 4664 */, 0x9c57 /* offset 4665 */, 0x9e9f /* offset 4666 */, 0x6797 /* offset 4667 */, 0x6dcb /* offset 4668 */, 0x81e8 /* offset 4669 */, 0x7b20 /* offset 4670 */, 0x7c92 /* offset 4671 */, 0x72c0 /* offset 4672 */, 0x7099 /* offset 4673 */, 0x8b58 /* offset 4674 */, 0x4ec0 /* offset 4675 */, 0x8336 /* offset 4676 */, 0x523a /* offset 4677 */, 0x5207 /* offset 4678 */, 0x5ea6 /* offset 4679 */, 0x62d3 /* offset 4680 */, 0x7cd6 /* offset 4681 */, 0x5b85 /* offset 4682 */, 0x6d1e /* offset 4683 */, 0x66b4 /* offset 4684 */, 0x8f3b /* offset 4685 */, 0x964d /* offset 4686 */, 0x5ed3 /* offset 4687 */, 0x5140 /* offset 4688 */, 0x55c0 /* offset 4689 */, 0x585a /* offset 4690 */, 0x6674 /* offset 4691 */, 0x51de /* offset 4692 */, 0x732a /* offset 4693 */, 0x76ca /* offset 4694 */, 0x793c /* offset 4695 */, 0x795e /* offset 4696 */, 0x7965 /* offset 4697 */, 0x798f /* offset 4698 */, 0x9756 /* offset 4699 */, 0x7cbe /* offset 4700 */, 0x8612 /* offset 4701 */, 0x8af8 /* offset 4702 */, 0x9038 /* offset 4703 */, 0x90fd /* offset 4704 */, 0x98ef /* offset 4705 */, 0x98fc /* offset 4706 */, 0x9928 /* offset 4707 */, 0x9db4 /* offset 4708 */, 0x90de /* offset 4709 */, 0x96b7 /* offset 4710 */, 0x4fae /* offset 4711 */, 0x50e7 /* offset 4712 */, 0x514d /* offset 4713 */, 0x52c9 /* offset 4714 */, 0x52e4 /* offset 4715 */, 0x5351 /* offset 4716 */, 0x559d /* offset 4717 */, 0x5606 /* offset 4718 */, 0x5668 /* offset 4719 */, 0x5840 /* offset 4720 */, 0x58a8 /* offset 4721 */, 0x5c64 /* offset 4722 */, 0x6094 /* offset 4723 */, 0x6168 /* offset 4724 */, 0x618e /* offset 4725 */, 0x61f2 /* offset 4726 */, 0x654f /* offset 4727 */, 0x65e2 /* offset 4728 */, 0x6691 /* offset 4729 */, 0x6885 /* offset 4730 */, 0x6d77 /* offset 4731 */, 0x6e1a /* offset 4732 */, 0x6f22 /* offset 4733 */, 0x716e /* offset 4734 */, 0x722b /* offset 4735 */, 0x7422 /* offset 4736 */, 0x7891 /* offset 4737 */, 0x7949 /* offset 4738 */, 0x7948 /* offset 4739 */, 0x7950 /* offset 4740 */, 0x7956 /* offset 4741 */, 0x798d /* offset 4742 */, 0x798e /* offset 4743 */, 0x7a40 /* offset 4744 */, 0x7a81 /* offset 4745 */, 0x7bc0 /* offset 4746 */, 0x7e09 /* offset 4747 */, 0x7e41 /* offset 4748 */, 0x7f72 /* offset 4749 */, 0x8005 /* offset 4750 */, 0x81ed /* offset 4751 */, 0x8279 /* offset 4752 */, 0x8457 /* offset 4753 */, 0x8910 /* offset 4754 */, 0x8996 /* offset 4755 */, 0x8b01 /* offset 4756 */, 0x8b39 /* offset 4757 */, 0x8cd3 /* offset 4758 */, 0x8d08 /* offset 4759 */, 0x8fb6 /* offset 4760 */, 0x96e3 /* offset 4761 */, 0x97ff /* offset 4762 */, 0x983b /* offset 4763 */, 0x6075 /* offset 4764 */, 0x242ee /* offset 4765 */, 0x8218 /* offset 4766 */, 0x4e26 /* offset 4767 */, 0x51b5 /* offset 4768 */, 0x5168 /* offset 4769 */, 0x4f80 /* offset 4770 */, 0x5145 /* offset 4771 */, 0x5180 /* offset 4772 */, 0x52c7 /* offset 4773 */, 0x52fa /* offset 4774 */, 0x5555 /* offset 4775 */, 0x5599 /* offset 4776 */, 0x55e2 /* offset 4777 */, 0x58b3 /* offset 4778 */, 0x5944 /* offset 4779 */, 0x5954 /* offset 4780 */, 0x5a62 /* offset 4781 */, 0x5b28 /* offset 4782 */, 0x5ed2 /* offset 4783 */, 0x5ed9 /* offset 4784 */, 0x5f69 /* offset 4785 */, 0x5fad /* offset 4786 */, 0x60d8 /* offset 4787 */, 0x614e /* offset 4788 */, 0x6108 /* offset 4789 */, 0x6160 /* offset 4790 */, 0x6234 /* offset 4791 */, 0x63c4 /* offset 4792 */, 0x641c /* offset 4793 */, 0x6452 /* offset 4794 */, 0x6556 /* offset 4795 */, 0x671b /* offset 4796 */, 0x6756 /* offset 4797 */, 0x6edb /* offset 4798 */, 0x6ecb /* offset 4799 */, 0x701e /* offset 4800 */, 0x77a7 /* offset 4801 */, 0x7235 /* offset 4802 */, 0x72af /* offset 4803 */, 0x7471 /* offset 4804 */, 0x7506 /* offset 4805 */, 0x753b /* offset 4806 */, 0x761d /* offset 4807 */, 0x761f /* offset 4808 */, 0x76db /* offset 4809 */, 0x76f4 /* offset 4810 */, 0x774a /* offset 4811 */, 0x7740 /* offset 4812 */, 0x78cc /* offset 4813 */, 0x7ab1 /* offset 4814 */, 0x7c7b /* offset 4815 */, 0x7d5b /* offset 4816 */, 0x7f3e /* offset 4817 */, 0x8352 /* offset 4818 */, 0x83ef /* offset 4819 */, 0x8779 /* offset 4820 */, 0x8941 /* offset 4821 */, 0x8986 /* offset 4822 */, 0x8abf /* offset 4823 */, 0x8acb /* offset 4824 */, 0x8aed /* offset 4825 */, 0x8b8a /* offset 4826 */, 0x8f38 /* offset 4827 */, 0x9072 /* offset 4828 */, 0x9199 /* offset 4829 */, 0x9276 /* offset 4830 */, 0x967c /* offset 4831 */, 0x97db /* offset 4832 */, 0x980b /* offset 4833 */, 0x9b12 /* offset 4834 */, 0x2284a /* offset 4835 */, 0x22844 /* offset 4836 */, 0x233d5 /* offset 4837 */, 0x3b9d /* offset 4838 */, 0x4018 /* offset 4839 */, 0x4039 /* offset 4840 */, 0x25249 /* offset 4841 */, 0x25cd0 /* offset 4842 */, 0x27ed3 /* offset 4843 */, 0x9f43 /* offset 4844 */, 0x9f8e /* offset 4845 */, 0x66, 0x66 /* offset 4846 */, 0x66, 0x69 /* offset 4848 */, 0x66, 0x6c /* offset 4850 */, 0x66, 0x66, 0x69 /* offset 4852 */, 0x66, 0x66, 0x6c /* offset 4855 */, 0x73, 0x74 /* offset 4858 */, 0x574, 0x576 /* offset 4860 */, 0x574, 0x565 /* offset 4862 */, 0x574, 0x56b /* offset 4864 */, 0x57e, 0x576 /* offset 4866 */, 0x574, 0x56d /* offset 4868 */, 0x5d9, 0x5b4 /* offset 4870 */, 0x5f2, 0x5b7 /* offset 4872 */, 0x5e2 /* offset 4874 */, 0x5d4 /* offset 4875 */, 0x5db /* offset 4876 */, 0x5dc /* offset 4877 */, 0x5dd /* offset 4878 */, 0x5e8 /* offset 4879 */, 0x5ea /* offset 4880 */, 0x5e9, 0x5c1 /* offset 4881 */, 0x5e9, 0x5c2 /* offset 4883 */, 0x5e9, 0x5bc, 0x5c1 /* offset 4885 */, 0x5e9, 0x5bc, 0x5c2 /* offset 4888 */, 0x5d0, 0x5b7 /* offset 4891 */, 0x5d0, 0x5b8 /* offset 4893 */, 0x5d0, 0x5bc /* offset 4895 */, 0x5d1, 0x5bc /* offset 4897 */, 0x5d2, 0x5bc /* offset 4899 */, 0x5d3, 0x5bc /* offset 4901 */, 0x5d4, 0x5bc /* offset 4903 */, 0x5d5, 0x5bc /* offset 4905 */, 0x5d6, 0x5bc /* offset 4907 */, 0x5d8, 0x5bc /* offset 4909 */, 0x5d9, 0x5bc /* offset 4911 */, 0x5da, 0x5bc /* offset 4913 */, 0x5db, 0x5bc /* offset 4915 */, 0x5dc, 0x5bc /* offset 4917 */, 0x5de, 0x5bc /* offset 4919 */, 0x5e0, 0x5bc /* offset 4921 */, 0x5e1, 0x5bc /* offset 4923 */, 0x5e3, 0x5bc /* offset 4925 */, 0x5e4, 0x5bc /* offset 4927 */, 0x5e6, 0x5bc /* offset 4929 */, 0x5e7, 0x5bc /* offset 4931 */, 0x5e8, 0x5bc /* offset 4933 */, 0x5e9, 0x5bc /* offset 4935 */, 0x5ea, 0x5bc /* offset 4937 */, 0x5d5, 0x5b9 /* offset 4939 */, 0x5d1, 0x5bf /* offset 4941 */, 0x5db, 0x5bf /* offset 4943 */, 0x5e4, 0x5bf /* offset 4945 */, 0x5d0, 0x5dc /* offset 4947 */, 0x671 /* offset 4949 */, 0x67b /* offset 4950 */, 0x67e /* offset 4951 */, 0x680 /* offset 4952 */, 0x67a /* offset 4953 */, 0x67f /* offset 4954 */, 0x679 /* offset 4955 */, 0x6a4 /* offset 4956 */, 0x6a6 /* offset 4957 */, 0x684 /* offset 4958 */, 0x683 /* offset 4959 */, 0x686 /* offset 4960 */, 0x687 /* offset 4961 */, 0x68d /* offset 4962 */, 0x68c /* offset 4963 */, 0x68e /* offset 4964 */, 0x688 /* offset 4965 */, 0x698 /* offset 4966 */, 0x691 /* offset 4967 */, 0x6a9 /* offset 4968 */, 0x6af /* offset 4969 */, 0x6b3 /* offset 4970 */, 0x6b1 /* offset 4971 */, 0x6ba /* offset 4972 */, 0x6bb /* offset 4973 */, 0x6c1 /* offset 4974 */, 0x6be /* offset 4975 */, 0x6d2 /* offset 4976 */, 0x6ad /* offset 4977 */, 0x6c7 /* offset 4978 */, 0x6c6 /* offset 4979 */, 0x6c8 /* offset 4980 */, 0x6cb /* offset 4981 */, 0x6c5 /* offset 4982 */, 0x6c9 /* offset 4983 */, 0x6d0 /* offset 4984 */, 0x649 /* offset 4985 */, 0x64a, 0x654, 0x627 /* offset 4986 */, 0x64a, 0x654, 0x6d5 /* offset 4989 */, 0x64a, 0x654, 0x648 /* offset 4992 */, 0x64a, 0x654, 0x6c7 /* offset 4995 */, 0x64a, 0x654, 0x6c6 /* offset 4998 */, 0x64a, 0x654, 0x6c8 /* offset 5001 */, 0x64a, 0x654, 0x6d0 /* offset 5004 */, 0x64a, 0x654, 0x649 /* offset 5007 */, 0x6cc /* offset 5010 */, 0x64a, 0x654, 0x62c /* offset 5011 */, 0x64a, 0x654, 0x62d /* offset 5014 */, 0x64a, 0x654, 0x645 /* offset 5017 */, 0x64a, 0x654, 0x64a /* offset 5020 */, 0x628, 0x62c /* offset 5023 */, 0x628, 0x62d /* offset 5025 */, 0x628, 0x62e /* offset 5027 */, 0x628, 0x645 /* offset 5029 */, 0x628, 0x649 /* offset 5031 */, 0x628, 0x64a /* offset 5033 */, 0x62a, 0x62c /* offset 5035 */, 0x62a, 0x62d /* offset 5037 */, 0x62a, 0x62e /* offset 5039 */, 0x62a, 0x645 /* offset 5041 */, 0x62a, 0x649 /* offset 5043 */, 0x62a, 0x64a /* offset 5045 */, 0x62b, 0x62c /* offset 5047 */, 0x62b, 0x645 /* offset 5049 */, 0x62b, 0x649 /* offset 5051 */, 0x62b, 0x64a /* offset 5053 */, 0x62c, 0x62d /* offset 5055 */, 0x62c, 0x645 /* offset 5057 */, 0x62d, 0x62c /* offset 5059 */, 0x62d, 0x645 /* offset 5061 */, 0x62e, 0x62c /* offset 5063 */, 0x62e, 0x62d /* offset 5065 */, 0x62e, 0x645 /* offset 5067 */, 0x633, 0x62c /* offset 5069 */, 0x633, 0x62d /* offset 5071 */, 0x633, 0x62e /* offset 5073 */, 0x633, 0x645 /* offset 5075 */, 0x635, 0x62d /* offset 5077 */, 0x635, 0x645 /* offset 5079 */, 0x636, 0x62c /* offset 5081 */, 0x636, 0x62d /* offset 5083 */, 0x636, 0x62e /* offset 5085 */, 0x636, 0x645 /* offset 5087 */, 0x637, 0x62d /* offset 5089 */, 0x637, 0x645 /* offset 5091 */, 0x638, 0x645 /* offset 5093 */, 0x639, 0x62c /* offset 5095 */, 0x639, 0x645 /* offset 5097 */, 0x63a, 0x62c /* offset 5099 */, 0x63a, 0x645 /* offset 5101 */, 0x641, 0x62c /* offset 5103 */, 0x641, 0x62d /* offset 5105 */, 0x641, 0x62e /* offset 5107 */, 0x641, 0x645 /* offset 5109 */, 0x641, 0x649 /* offset 5111 */, 0x641, 0x64a /* offset 5113 */, 0x642, 0x62d /* offset 5115 */, 0x642, 0x645 /* offset 5117 */, 0x642, 0x649 /* offset 5119 */, 0x642, 0x64a /* offset 5121 */, 0x643, 0x627 /* offset 5123 */, 0x643, 0x62c /* offset 5125 */, 0x643, 0x62d /* offset 5127 */, 0x643, 0x62e /* offset 5129 */, 0x643, 0x644 /* offset 5131 */, 0x643, 0x645 /* offset 5133 */, 0x643, 0x649 /* offset 5135 */, 0x643, 0x64a /* offset 5137 */, 0x644, 0x62c /* offset 5139 */, 0x644, 0x62d /* offset 5141 */, 0x644, 0x62e /* offset 5143 */, 0x644, 0x645 /* offset 5145 */, 0x644, 0x649 /* offset 5147 */, 0x644, 0x64a /* offset 5149 */, 0x645, 0x62c /* offset 5151 */, 0x645, 0x62d /* offset 5153 */, 0x645, 0x62e /* offset 5155 */, 0x645, 0x645 /* offset 5157 */, 0x645, 0x649 /* offset 5159 */, 0x645, 0x64a /* offset 5161 */, 0x646, 0x62c /* offset 5163 */, 0x646, 0x62d /* offset 5165 */, 0x646, 0x62e /* offset 5167 */, 0x646, 0x645 /* offset 5169 */, 0x646, 0x649 /* offset 5171 */, 0x646, 0x64a /* offset 5173 */, 0x647, 0x62c /* offset 5175 */, 0x647, 0x645 /* offset 5177 */, 0x647, 0x649 /* offset 5179 */, 0x647, 0x64a /* offset 5181 */, 0x64a, 0x62c /* offset 5183 */, 0x64a, 0x62d /* offset 5185 */, 0x64a, 0x62e /* offset 5187 */, 0x64a, 0x645 /* offset 5189 */, 0x64a, 0x649 /* offset 5191 */, 0x64a, 0x64a /* offset 5193 */, 0x630, 0x670 /* offset 5195 */, 0x631, 0x670 /* offset 5197 */, 0x649, 0x670 /* offset 5199 */, 0x20, 0x64c, 0x651 /* offset 5201 */, 0x20, 0x64d, 0x651 /* offset 5204 */, 0x20, 0x64e, 0x651 /* offset 5207 */, 0x20, 0x64f, 0x651 /* offset 5210 */, 0x20, 0x650, 0x651 /* offset 5213 */, 0x20, 0x651, 0x670 /* offset 5216 */, 0x64a, 0x654, 0x631 /* offset 5219 */, 0x64a, 0x654, 0x632 /* offset 5222 */, 0x64a, 0x654, 0x646 /* offset 5225 */, 0x628, 0x631 /* offset 5228 */, 0x628, 0x632 /* offset 5230 */, 0x628, 0x646 /* offset 5232 */, 0x62a, 0x631 /* offset 5234 */, 0x62a, 0x632 /* offset 5236 */, 0x62a, 0x646 /* offset 5238 */, 0x62b, 0x631 /* offset 5240 */, 0x62b, 0x632 /* offset 5242 */, 0x62b, 0x646 /* offset 5244 */, 0x645, 0x627 /* offset 5246 */, 0x646, 0x631 /* offset 5248 */, 0x646, 0x632 /* offset 5250 */, 0x646, 0x646 /* offset 5252 */, 0x64a, 0x631 /* offset 5254 */, 0x64a, 0x632 /* offset 5256 */, 0x64a, 0x646 /* offset 5258 */, 0x64a, 0x654, 0x62e /* offset 5260 */, 0x64a, 0x654, 0x647 /* offset 5263 */, 0x628, 0x647 /* offset 5266 */, 0x62a, 0x647 /* offset 5268 */, 0x635, 0x62e /* offset 5270 */, 0x644, 0x647 /* offset 5272 */, 0x646, 0x647 /* offset 5274 */, 0x647, 0x670 /* offset 5276 */, 0x64a, 0x647 /* offset 5278 */, 0x62b, 0x647 /* offset 5280 */, 0x633, 0x647 /* offset 5282 */, 0x634, 0x645 /* offset 5284 */, 0x634, 0x647 /* offset 5286 */, 0x640, 0x64e, 0x651 /* offset 5288 */, 0x640, 0x64f, 0x651 /* offset 5291 */, 0x640, 0x650, 0x651 /* offset 5294 */, 0x637, 0x649 /* offset 5297 */, 0x637, 0x64a /* offset 5299 */, 0x639, 0x649 /* offset 5301 */, 0x639, 0x64a /* offset 5303 */, 0x63a, 0x649 /* offset 5305 */, 0x63a, 0x64a /* offset 5307 */, 0x633, 0x649 /* offset 5309 */, 0x633, 0x64a /* offset 5311 */, 0x634, 0x649 /* offset 5313 */, 0x634, 0x64a /* offset 5315 */, 0x62d, 0x649 /* offset 5317 */, 0x62d, 0x64a /* offset 5319 */, 0x62c, 0x649 /* offset 5321 */, 0x62c, 0x64a /* offset 5323 */, 0x62e, 0x649 /* offset 5325 */, 0x62e, 0x64a /* offset 5327 */, 0x635, 0x649 /* offset 5329 */, 0x635, 0x64a /* offset 5331 */, 0x636, 0x649 /* offset 5333 */, 0x636, 0x64a /* offset 5335 */, 0x634, 0x62c /* offset 5337 */, 0x634, 0x62d /* offset 5339 */, 0x634, 0x62e /* offset 5341 */, 0x634, 0x631 /* offset 5343 */, 0x633, 0x631 /* offset 5345 */, 0x635, 0x631 /* offset 5347 */, 0x636, 0x631 /* offset 5349 */, 0x627, 0x64b /* offset 5351 */, 0x62a, 0x62c, 0x645 /* offset 5353 */, 0x62a, 0x62d, 0x62c /* offset 5356 */, 0x62a, 0x62d, 0x645 /* offset 5359 */, 0x62a, 0x62e, 0x645 /* offset 5362 */, 0x62a, 0x645, 0x62c /* offset 5365 */, 0x62a, 0x645, 0x62d /* offset 5368 */, 0x62a, 0x645, 0x62e /* offset 5371 */, 0x62c, 0x645, 0x62d /* offset 5374 */, 0x62d, 0x645, 0x64a /* offset 5377 */, 0x62d, 0x645, 0x649 /* offset 5380 */, 0x633, 0x62d, 0x62c /* offset 5383 */, 0x633, 0x62c, 0x62d /* offset 5386 */, 0x633, 0x62c, 0x649 /* offset 5389 */, 0x633, 0x645, 0x62d /* offset 5392 */, 0x633, 0x645, 0x62c /* offset 5395 */, 0x633, 0x645, 0x645 /* offset 5398 */, 0x635, 0x62d, 0x62d /* offset 5401 */, 0x635, 0x645, 0x645 /* offset 5404 */, 0x634, 0x62d, 0x645 /* offset 5407 */, 0x634, 0x62c, 0x64a /* offset 5410 */, 0x634, 0x645, 0x62e /* offset 5413 */, 0x634, 0x645, 0x645 /* offset 5416 */, 0x636, 0x62d, 0x649 /* offset 5419 */, 0x636, 0x62e, 0x645 /* offset 5422 */, 0x637, 0x645, 0x62d /* offset 5425 */, 0x637, 0x645, 0x645 /* offset 5428 */, 0x637, 0x645, 0x64a /* offset 5431 */, 0x639, 0x62c, 0x645 /* offset 5434 */, 0x639, 0x645, 0x645 /* offset 5437 */, 0x639, 0x645, 0x649 /* offset 5440 */, 0x63a, 0x645, 0x645 /* offset 5443 */, 0x63a, 0x645, 0x64a /* offset 5446 */, 0x63a, 0x645, 0x649 /* offset 5449 */, 0x641, 0x62e, 0x645 /* offset 5452 */, 0x642, 0x645, 0x62d /* offset 5455 */, 0x642, 0x645, 0x645 /* offset 5458 */, 0x644, 0x62d, 0x645 /* offset 5461 */, 0x644, 0x62d, 0x64a /* offset 5464 */, 0x644, 0x62d, 0x649 /* offset 5467 */, 0x644, 0x62c, 0x62c /* offset 5470 */, 0x644, 0x62e, 0x645 /* offset 5473 */, 0x644, 0x645, 0x62d /* offset 5476 */, 0x645, 0x62d, 0x62c /* offset 5479 */, 0x645, 0x62d, 0x645 /* offset 5482 */, 0x645, 0x62d, 0x64a /* offset 5485 */, 0x645, 0x62c, 0x62d /* offset 5488 */, 0x645, 0x62c, 0x645 /* offset 5491 */, 0x645, 0x62e, 0x62c /* offset 5494 */, 0x645, 0x62e, 0x645 /* offset 5497 */, 0x645, 0x62c, 0x62e /* offset 5500 */, 0x647, 0x645, 0x62c /* offset 5503 */, 0x647, 0x645, 0x645 /* offset 5506 */, 0x646, 0x62d, 0x645 /* offset 5509 */, 0x646, 0x62d, 0x649 /* offset 5512 */, 0x646, 0x62c, 0x645 /* offset 5515 */, 0x646, 0x62c, 0x649 /* offset 5518 */, 0x646, 0x645, 0x64a /* offset 5521 */, 0x646, 0x645, 0x649 /* offset 5524 */, 0x64a, 0x645, 0x645 /* offset 5527 */, 0x628, 0x62e, 0x64a /* offset 5530 */, 0x62a, 0x62c, 0x64a /* offset 5533 */, 0x62a, 0x62c, 0x649 /* offset 5536 */, 0x62a, 0x62e, 0x64a /* offset 5539 */, 0x62a, 0x62e, 0x649 /* offset 5542 */, 0x62a, 0x645, 0x64a /* offset 5545 */, 0x62a, 0x645, 0x649 /* offset 5548 */, 0x62c, 0x645, 0x64a /* offset 5551 */, 0x62c, 0x62d, 0x649 /* offset 5554 */, 0x62c, 0x645, 0x649 /* offset 5557 */, 0x633, 0x62e, 0x649 /* offset 5560 */, 0x635, 0x62d, 0x64a /* offset 5563 */, 0x634, 0x62d, 0x64a /* offset 5566 */, 0x636, 0x62d, 0x64a /* offset 5569 */, 0x644, 0x62c, 0x64a /* offset 5572 */, 0x644, 0x645, 0x64a /* offset 5575 */, 0x64a, 0x62d, 0x64a /* offset 5578 */, 0x64a, 0x62c, 0x64a /* offset 5581 */, 0x64a, 0x645, 0x64a /* offset 5584 */, 0x645, 0x645, 0x64a /* offset 5587 */, 0x642, 0x645, 0x64a /* offset 5590 */, 0x646, 0x62d, 0x64a /* offset 5593 */, 0x639, 0x645, 0x64a /* offset 5596 */, 0x643, 0x645, 0x64a /* offset 5599 */, 0x646, 0x62c, 0x62d /* offset 5602 */, 0x645, 0x62e, 0x64a /* offset 5605 */, 0x644, 0x62c, 0x645 /* offset 5608 */, 0x643, 0x645, 0x645 /* offset 5611 */, 0x62c, 0x62d, 0x64a /* offset 5614 */, 0x62d, 0x62c, 0x64a /* offset 5617 */, 0x645, 0x62c, 0x64a /* offset 5620 */, 0x641, 0x645, 0x64a /* offset 5623 */, 0x628, 0x62d, 0x64a /* offset 5626 */, 0x633, 0x62e, 0x64a /* offset 5629 */, 0x646, 0x62c, 0x64a /* offset 5632 */, 0x635, 0x644, 0x6d2 /* offset 5635 */, 0x642, 0x644, 0x6d2 /* offset 5638 */, 0x627, 0x644, 0x644, 0x647 /* offset 5641 */, 0x627, 0x643, 0x628, 0x631 /* offset 5645 */, 0x645, 0x62d, 0x645, 0x62f /* offset 5649 */, 0x635, 0x644, 0x639, 0x645 /* offset 5653 */, 0x631, 0x633, 0x648, 0x644 /* offset 5657 */, 0x639, 0x644, 0x64a, 0x647 /* offset 5661 */, 0x648, 0x633, 0x644, 0x645 /* offset 5665 */, 0x635, 0x644, 0x649 /* offset 5669 */, 0x635, 0x644, 0x649, 0x20, 0x627, 0x644, 0x644, 0x647, 0x20, 0x639, 0x644, 0x64a, 0x647, 0x20, 0x648, 0x633, 0x644, 0x645 /* offset 5672 */, 0x62c, 0x644, 0x20, 0x62c, 0x644, 0x627, 0x644, 0x647 /* offset 5690 */, 0x631, 0x6cc, 0x627, 0x644 /* offset 5698 */, 0x2c /* offset 5702 */, 0x3001 /* offset 5703 */, 0x3002 /* offset 5704 */, 0x3a /* offset 5705 */, 0x21 /* offset 5706 */, 0x3f /* offset 5707 */, 0x3016 /* offset 5708 */, 0x3017 /* offset 5709 */, 0x2014 /* offset 5710 */, 0x2013 /* offset 5711 */, 0x5f /* offset 5712 */, 0x7b /* offset 5713 */, 0x7d /* offset 5714 */, 0x3014 /* offset 5715 */, 0x3015 /* offset 5716 */, 0x3010 /* offset 5717 */, 0x3011 /* offset 5718 */, 0x300a /* offset 5719 */, 0x300b /* offset 5720 */, 0x300c /* offset 5721 */, 0x300d /* offset 5722 */, 0x300e /* offset 5723 */, 0x300f /* offset 5724 */, 0x5b /* offset 5725 */, 0x5d /* offset 5726 */, 0x23 /* offset 5727 */, 0x26 /* offset 5728 */, 0x2a /* offset 5729 */, 0x2d /* offset 5730 */, 0x3c /* offset 5731 */, 0x3e /* offset 5732 */, 0x5c /* offset 5733 */, 0x24 /* offset 5734 */, 0x25 /* offset 5735 */, 0x40 /* offset 5736 */, 0x20, 0x64b /* offset 5737 */, 0x640, 0x64b /* offset 5739 */, 0x20, 0x64c /* offset 5741 */, 0x20, 0x64d /* offset 5743 */, 0x20, 0x64e /* offset 5745 */, 0x640, 0x64e /* offset 5747 */, 0x20, 0x64f /* offset 5749 */, 0x640, 0x64f /* offset 5751 */, 0x20, 0x650 /* offset 5753 */, 0x640, 0x650 /* offset 5755 */, 0x20, 0x651 /* offset 5757 */, 0x640, 0x651 /* offset 5759 */, 0x20, 0x652 /* offset 5761 */, 0x640, 0x652 /* offset 5763 */, 0x621 /* offset 5765 */, 0x627 /* offset 5766 */, 0x628 /* offset 5767 */, 0x629 /* offset 5768 */, 0x62a /* offset 5769 */, 0x62b /* offset 5770 */, 0x62c /* offset 5771 */, 0x62d /* offset 5772 */, 0x62e /* offset 5773 */, 0x62f /* offset 5774 */, 0x630 /* offset 5775 */, 0x631 /* offset 5776 */, 0x632 /* offset 5777 */, 0x633 /* offset 5778 */, 0x634 /* offset 5779 */, 0x635 /* offset 5780 */, 0x636 /* offset 5781 */, 0x637 /* offset 5782 */, 0x638 /* offset 5783 */, 0x639 /* offset 5784 */, 0x63a /* offset 5785 */, 0x641 /* offset 5786 */, 0x642 /* offset 5787 */, 0x643 /* offset 5788 */, 0x644 /* offset 5789 */, 0x645 /* offset 5790 */, 0x646 /* offset 5791 */, 0x647 /* offset 5792 */, 0x648 /* offset 5793 */, 0x64a /* offset 5794 */, 0x644, 0x627, 0x653 /* offset 5795 */, 0x644, 0x627, 0x654 /* offset 5798 */, 0x644, 0x627, 0x655 /* offset 5801 */, 0x644, 0x627 /* offset 5804 */, 0x22 /* offset 5806 */, 0x27 /* offset 5807 */, 0x2f /* offset 5808 */, 0x5e /* offset 5809 */, 0x7c /* offset 5810 */, 0x7e /* offset 5811 */, 0x2985 /* offset 5812 */, 0x2986 /* offset 5813 */, 0x30fb /* offset 5814 */, 0x30a1 /* offset 5815 */, 0x30a3 /* offset 5816 */, 0x30a5 /* offset 5817 */, 0x30a7 /* offset 5818 */, 0x30a9 /* offset 5819 */, 0x30e3 /* offset 5820 */, 0x30e5 /* offset 5821 */, 0x30e7 /* offset 5822 */, 0x30c3 /* offset 5823 */, 0x30fc /* offset 5824 */, 0x30f3 /* offset 5825 */, 0x3099 /* offset 5826 */, 0x309a /* offset 5827 */, 0xa2 /* offset 5828 */, 0xa3 /* offset 5829 */, 0xac /* offset 5830 */, 0xa6 /* offset 5831 */, 0xa5 /* offset 5832 */, 0x20a9 /* offset 5833 */, 0x2502 /* offset 5834 */, 0x2190 /* offset 5835 */, 0x2191 /* offset 5836 */, 0x2192 /* offset 5837 */, 0x2193 /* offset 5838 */, 0x25a0 /* offset 5839 */, 0x25cb /* offset 5840 */, 0x11099, 0x110ba /* offset 5841 */, 0x1109b, 0x110ba /* offset 5843 */, 0x110a5, 0x110ba /* offset 5845 */, 0x11131, 0x11127 /* offset 5847 */, 0x11132, 0x11127 /* offset 5849 */, 0x11347, 0x1133e /* offset 5851 */, 0x11347, 0x11357 /* offset 5853 */, 0x114b9, 0x114ba /* offset 5855 */, 0x114b9, 0x114b0 /* offset 5857 */, 0x114b9, 0x114bd /* offset 5859 */, 0x115b8, 0x115af /* offset 5861 */, 0x115b9, 0x115af /* offset 5863 */, 0x1d157, 0x1d165 /* offset 5865 */, 0x1d158, 0x1d165 /* offset 5867 */, 0x1d158, 0x1d165, 0x1d16e /* offset 5869 */, 0x1d158, 0x1d165, 0x1d16f /* offset 5872 */, 0x1d158, 0x1d165, 0x1d170 /* offset 5875 */, 0x1d158, 0x1d165, 0x1d171 /* offset 5878 */, 0x1d158, 0x1d165, 0x1d172 /* offset 5881 */, 0x1d1b9, 0x1d165 /* offset 5884 */, 0x1d1ba, 0x1d165 /* offset 5886 */, 0x1d1b9, 0x1d165, 0x1d16e /* offset 5888 */, 0x1d1ba, 0x1d165, 0x1d16e /* offset 5891 */, 0x1d1b9, 0x1d165, 0x1d16f /* offset 5894 */, 0x1d1ba, 0x1d165, 0x1d16f /* offset 5897 */, 0x131 /* offset 5900 */, 0x237 /* offset 5901 */, 0x391 /* offset 5902 */, 0x392 /* offset 5903 */, 0x394 /* offset 5904 */, 0x395 /* offset 5905 */, 0x396 /* offset 5906 */, 0x397 /* offset 5907 */, 0x399 /* offset 5908 */, 0x39a /* offset 5909 */, 0x39b /* offset 5910 */, 0x39c /* offset 5911 */, 0x39d /* offset 5912 */, 0x39e /* offset 5913 */, 0x39f /* offset 5914 */, 0x3a1 /* offset 5915 */, 0x3a4 /* offset 5916 */, 0x3a6 /* offset 5917 */, 0x3a7 /* offset 5918 */, 0x3a8 /* offset 5919 */, 0x2207 /* offset 5920 */, 0x3b1 /* offset 5921 */, 0x3b6 /* offset 5922 */, 0x3b7 /* offset 5923 */, 0x3bb /* offset 5924 */, 0x3bd /* offset 5925 */, 0x3be /* offset 5926 */, 0x3bf /* offset 5927 */, 0x3c3 /* offset 5928 */, 0x3c4 /* offset 5929 */, 0x3c5 /* offset 5930 */, 0x3c8 /* offset 5931 */, 0x3c9 /* offset 5932 */, 0x2202 /* offset 5933 */, 0x3dc /* offset 5934 */, 0x3dd /* offset 5935 */, 0x66e /* offset 5936 */, 0x6a1 /* offset 5937 */, 0x66f /* offset 5938 */, 0x30, 0x2e /* offset 5939 */, 0x30, 0x2c /* offset 5941 */, 0x31, 0x2c /* offset 5943 */, 0x32, 0x2c /* offset 5945 */, 0x33, 0x2c /* offset 5947 */, 0x34, 0x2c /* offset 5949 */, 0x35, 0x2c /* offset 5951 */, 0x36, 0x2c /* offset 5953 */, 0x37, 0x2c /* offset 5955 */, 0x38, 0x2c /* offset 5957 */, 0x39, 0x2c /* offset 5959 */, 0x28, 0x41, 0x29 /* offset 5961 */, 0x28, 0x42, 0x29 /* offset 5964 */, 0x28, 0x43, 0x29 /* offset 5967 */, 0x28, 0x44, 0x29 /* offset 5970 */, 0x28, 0x45, 0x29 /* offset 5973 */, 0x28, 0x46, 0x29 /* offset 5976 */, 0x28, 0x47, 0x29 /* offset 5979 */, 0x28, 0x48, 0x29 /* offset 5982 */, 0x28, 0x49, 0x29 /* offset 5985 */, 0x28, 0x4a, 0x29 /* offset 5988 */, 0x28, 0x4b, 0x29 /* offset 5991 */, 0x28, 0x4c, 0x29 /* offset 5994 */, 0x28, 0x4d, 0x29 /* offset 5997 */, 0x28, 0x4e, 0x29 /* offset 6000 */, 0x28, 0x4f, 0x29 /* offset 6003 */, 0x28, 0x50, 0x29 /* offset 6006 */, 0x28, 0x51, 0x29 /* offset 6009 */, 0x28, 0x52, 0x29 /* offset 6012 */, 0x28, 0x53, 0x29 /* offset 6015 */, 0x28, 0x54, 0x29 /* offset 6018 */, 0x28, 0x55, 0x29 /* offset 6021 */, 0x28, 0x56, 0x29 /* offset 6024 */, 0x28, 0x57, 0x29 /* offset 6027 */, 0x28, 0x58, 0x29 /* offset 6030 */, 0x28, 0x59, 0x29 /* offset 6033 */, 0x28, 0x5a, 0x29 /* offset 6036 */, 0x3014, 0x53, 0x3015 /* offset 6039 */, 0x43, 0x44 /* offset 6042 */, 0x57, 0x5a /* offset 6044 */, 0x48, 0x56 /* offset 6046 */, 0x53, 0x44 /* offset 6048 */, 0x53, 0x53 /* offset 6050 */, 0x50, 0x50, 0x56 /* offset 6052 */, 0x57, 0x43 /* offset 6055 */, 0x4d, 0x43 /* offset 6057 */, 0x4d, 0x44 /* offset 6059 */, 0x44, 0x4a /* offset 6061 */, 0x307b, 0x304b /* offset 6063 */, 0x30b3, 0x30b3 /* offset 6065 */, 0x5b57 /* offset 6067 */, 0x53cc /* offset 6068 */, 0x591a /* offset 6069 */, 0x89e3 /* offset 6070 */, 0x4ea4 /* offset 6071 */, 0x6620 /* offset 6072 */, 0x7121 /* offset 6073 */, 0x524d /* offset 6074 */, 0x5f8c /* offset 6075 */, 0x518d /* offset 6076 */, 0x65b0 /* offset 6077 */, 0x521d /* offset 6078 */, 0x7d42 /* offset 6079 */, 0x8ca9 /* offset 6080 */, 0x58f0 /* offset 6081 */, 0x5439 /* offset 6082 */, 0x6f14 /* offset 6083 */, 0x6295 /* offset 6084 */, 0x6355 /* offset 6085 */, 0x904a /* offset 6086 */, 0x6307 /* offset 6087 */, 0x6253 /* offset 6088 */, 0x7981 /* offset 6089 */, 0x7a7a /* offset 6090 */, 0x5408 /* offset 6091 */, 0x6e80 /* offset 6092 */, 0x7533 /* offset 6093 */, 0x5272 /* offset 6094 */, 0x55b6 /* offset 6095 */, 0x914d /* offset 6096 */, 0x3014, 0x672c, 0x3015 /* offset 6097 */, 0x3014, 0x4e09, 0x3015 /* offset 6100 */, 0x3014, 0x4e8c, 0x3015 /* offset 6103 */, 0x3014, 0x5b89, 0x3015 /* offset 6106 */, 0x3014, 0x70b9, 0x3015 /* offset 6109 */, 0x3014, 0x6253, 0x3015 /* offset 6112 */, 0x3014, 0x76d7, 0x3015 /* offset 6115 */, 0x3014, 0x52dd, 0x3015 /* offset 6118 */, 0x3014, 0x6557, 0x3015 /* offset 6121 */, 0x5f97 /* offset 6124 */, 0x53ef /* offset 6125 */, 0x4e3d /* offset 6126 */, 0x4e38 /* offset 6127 */, 0x4e41 /* offset 6128 */, 0x20122 /* offset 6129 */, 0x4f60 /* offset 6130 */, 0x4fbb /* offset 6131 */, 0x5002 /* offset 6132 */, 0x507a /* offset 6133 */, 0x5099 /* offset 6134 */, 0x50cf /* offset 6135 */, 0x349e /* offset 6136 */, 0x2063a /* offset 6137 */, 0x5154 /* offset 6138 */, 0x5164 /* offset 6139 */, 0x5177 /* offset 6140 */, 0x2051c /* offset 6141 */, 0x34b9 /* offset 6142 */, 0x5167 /* offset 6143 */, 0x2054b /* offset 6144 */, 0x5197 /* offset 6145 */, 0x51a4 /* offset 6146 */, 0x4ecc /* offset 6147 */, 0x51ac /* offset 6148 */, 0x291df /* offset 6149 */, 0x5203 /* offset 6150 */, 0x34df /* offset 6151 */, 0x523b /* offset 6152 */, 0x5246 /* offset 6153 */, 0x5277 /* offset 6154 */, 0x3515 /* offset 6155 */, 0x5305 /* offset 6156 */, 0x5306 /* offset 6157 */, 0x5349 /* offset 6158 */, 0x535a /* offset 6159 */, 0x5373 /* offset 6160 */, 0x537d /* offset 6161 */, 0x537f /* offset 6162 */, 0x20a2c /* offset 6163 */, 0x7070 /* offset 6164 */, 0x53ca /* offset 6165 */, 0x53df /* offset 6166 */, 0x20b63 /* offset 6167 */, 0x53eb /* offset 6168 */, 0x53f1 /* offset 6169 */, 0x5406 /* offset 6170 */, 0x549e /* offset 6171 */, 0x5438 /* offset 6172 */, 0x5448 /* offset 6173 */, 0x5468 /* offset 6174 */, 0x54a2 /* offset 6175 */, 0x54f6 /* offset 6176 */, 0x5510 /* offset 6177 */, 0x5553 /* offset 6178 */, 0x5563 /* offset 6179 */, 0x5584 /* offset 6180 */, 0x55ab /* offset 6181 */, 0x55b3 /* offset 6182 */, 0x55c2 /* offset 6183 */, 0x5716 /* offset 6184 */, 0x5717 /* offset 6185 */, 0x5651 /* offset 6186 */, 0x5674 /* offset 6187 */, 0x58ee /* offset 6188 */, 0x57ce /* offset 6189 */, 0x57f4 /* offset 6190 */, 0x580d /* offset 6191 */, 0x578b /* offset 6192 */, 0x5832 /* offset 6193 */, 0x5831 /* offset 6194 */, 0x58ac /* offset 6195 */, 0x214e4 /* offset 6196 */, 0x58f2 /* offset 6197 */, 0x58f7 /* offset 6198 */, 0x5906 /* offset 6199 */, 0x5922 /* offset 6200 */, 0x5962 /* offset 6201 */, 0x216a8 /* offset 6202 */, 0x216ea /* offset 6203 */, 0x59ec /* offset 6204 */, 0x5a1b /* offset 6205 */, 0x5a27 /* offset 6206 */, 0x59d8 /* offset 6207 */, 0x5a66 /* offset 6208 */, 0x36ee /* offset 6209 */, 0x36fc /* offset 6210 */, 0x5b08 /* offset 6211 */, 0x5b3e /* offset 6212 */, 0x219c8 /* offset 6213 */, 0x5bc3 /* offset 6214 */, 0x5bd8 /* offset 6215 */, 0x5bf3 /* offset 6216 */, 0x21b18 /* offset 6217 */, 0x5bff /* offset 6218 */, 0x5c06 /* offset 6219 */, 0x5f53 /* offset 6220 */, 0x3781 /* offset 6221 */, 0x5c60 /* offset 6222 */, 0x5cc0 /* offset 6223 */, 0x5c8d /* offset 6224 */, 0x21de4 /* offset 6225 */, 0x5d43 /* offset 6226 */, 0x21de6 /* offset 6227 */, 0x5d6e /* offset 6228 */, 0x5d6b /* offset 6229 */, 0x5d7c /* offset 6230 */, 0x5de1 /* offset 6231 */, 0x5de2 /* offset 6232 */, 0x382f /* offset 6233 */, 0x5dfd /* offset 6234 */, 0x5e28 /* offset 6235 */, 0x5e3d /* offset 6236 */, 0x5e69 /* offset 6237 */, 0x3862 /* offset 6238 */, 0x22183 /* offset 6239 */, 0x387c /* offset 6240 */, 0x5eb0 /* offset 6241 */, 0x5eb3 /* offset 6242 */, 0x5eb6 /* offset 6243 */, 0x2a392 /* offset 6244 */, 0x22331 /* offset 6245 */, 0x8201 /* offset 6246 */, 0x5f22 /* offset 6247 */, 0x38c7 /* offset 6248 */, 0x232b8 /* offset 6249 */, 0x261da /* offset 6250 */, 0x5f62 /* offset 6251 */, 0x5f6b /* offset 6252 */, 0x38e3 /* offset 6253 */, 0x5f9a /* offset 6254 */, 0x5fcd /* offset 6255 */, 0x5fd7 /* offset 6256 */, 0x5ff9 /* offset 6257 */, 0x6081 /* offset 6258 */, 0x393a /* offset 6259 */, 0x391c /* offset 6260 */, 0x226d4 /* offset 6261 */, 0x60c7 /* offset 6262 */, 0x6148 /* offset 6263 */, 0x614c /* offset 6264 */, 0x617a /* offset 6265 */, 0x61b2 /* offset 6266 */, 0x61a4 /* offset 6267 */, 0x61af /* offset 6268 */, 0x61de /* offset 6269 */, 0x6210 /* offset 6270 */, 0x621b /* offset 6271 */, 0x625d /* offset 6272 */, 0x62b1 /* offset 6273 */, 0x62d4 /* offset 6274 */, 0x6350 /* offset 6275 */, 0x22b0c /* offset 6276 */, 0x633d /* offset 6277 */, 0x62fc /* offset 6278 */, 0x6368 /* offset 6279 */, 0x6383 /* offset 6280 */, 0x63e4 /* offset 6281 */, 0x22bf1 /* offset 6282 */, 0x6422 /* offset 6283 */, 0x63c5 /* offset 6284 */, 0x63a9 /* offset 6285 */, 0x3a2e /* offset 6286 */, 0x6469 /* offset 6287 */, 0x647e /* offset 6288 */, 0x649d /* offset 6289 */, 0x6477 /* offset 6290 */, 0x3a6c /* offset 6291 */, 0x656c /* offset 6292 */, 0x2300a /* offset 6293 */, 0x65e3 /* offset 6294 */, 0x66f8 /* offset 6295 */, 0x6649 /* offset 6296 */, 0x3b19 /* offset 6297 */, 0x3b08 /* offset 6298 */, 0x3ae4 /* offset 6299 */, 0x5192 /* offset 6300 */, 0x5195 /* offset 6301 */, 0x6700 /* offset 6302 */, 0x669c /* offset 6303 */, 0x80ad /* offset 6304 */, 0x43d9 /* offset 6305 */, 0x6721 /* offset 6306 */, 0x675e /* offset 6307 */, 0x6753 /* offset 6308 */, 0x233c3 /* offset 6309 */, 0x3b49 /* offset 6310 */, 0x67fa /* offset 6311 */, 0x6785 /* offset 6312 */, 0x6852 /* offset 6313 */, 0x2346d /* offset 6314 */, 0x688e /* offset 6315 */, 0x681f /* offset 6316 */, 0x6914 /* offset 6317 */, 0x6942 /* offset 6318 */, 0x69a3 /* offset 6319 */, 0x69ea /* offset 6320 */, 0x6aa8 /* offset 6321 */, 0x236a3 /* offset 6322 */, 0x6adb /* offset 6323 */, 0x3c18 /* offset 6324 */, 0x6b21 /* offset 6325 */, 0x238a7 /* offset 6326 */, 0x6b54 /* offset 6327 */, 0x3c4e /* offset 6328 */, 0x6b72 /* offset 6329 */, 0x6b9f /* offset 6330 */, 0x6bbb /* offset 6331 */, 0x23a8d /* offset 6332 */, 0x21d0b /* offset 6333 */, 0x23afa /* offset 6334 */, 0x6c4e /* offset 6335 */, 0x23cbc /* offset 6336 */, 0x6cbf /* offset 6337 */, 0x6ccd /* offset 6338 */, 0x6c67 /* offset 6339 */, 0x6d16 /* offset 6340 */, 0x6d3e /* offset 6341 */, 0x6d69 /* offset 6342 */, 0x6d78 /* offset 6343 */, 0x6d85 /* offset 6344 */, 0x23d1e /* offset 6345 */, 0x6d34 /* offset 6346 */, 0x6e2f /* offset 6347 */, 0x6e6e /* offset 6348 */, 0x3d33 /* offset 6349 */, 0x6ec7 /* offset 6350 */, 0x23ed1 /* offset 6351 */, 0x6df9 /* offset 6352 */, 0x6f6e /* offset 6353 */, 0x23f5e /* offset 6354 */, 0x23f8e /* offset 6355 */, 0x6fc6 /* offset 6356 */, 0x7039 /* offset 6357 */, 0x701b /* offset 6358 */, 0x3d96 /* offset 6359 */, 0x704a /* offset 6360 */, 0x707d /* offset 6361 */, 0x7077 /* offset 6362 */, 0x70ad /* offset 6363 */, 0x20525 /* offset 6364 */, 0x7145 /* offset 6365 */, 0x24263 /* offset 6366 */, 0x719c /* offset 6367 */, 0x243ab /* offset 6368 */, 0x7228 /* offset 6369 */, 0x7250 /* offset 6370 */, 0x24608 /* offset 6371 */, 0x7280 /* offset 6372 */, 0x7295 /* offset 6373 */, 0x24735 /* offset 6374 */, 0x24814 /* offset 6375 */, 0x737a /* offset 6376 */, 0x738b /* offset 6377 */, 0x3eac /* offset 6378 */, 0x73a5 /* offset 6379 */, 0x3eb8 /* offset 6380 */, 0x7447 /* offset 6381 */, 0x745c /* offset 6382 */, 0x7485 /* offset 6383 */, 0x74ca /* offset 6384 */, 0x3f1b /* offset 6385 */, 0x7524 /* offset 6386 */, 0x24c36 /* offset 6387 */, 0x753e /* offset 6388 */, 0x24c92 /* offset 6389 */, 0x2219f /* offset 6390 */, 0x7610 /* offset 6391 */, 0x24fa1 /* offset 6392 */, 0x24fb8 /* offset 6393 */, 0x25044 /* offset 6394 */, 0x3ffc /* offset 6395 */, 0x4008 /* offset 6396 */, 0x250f3 /* offset 6397 */, 0x250f2 /* offset 6398 */, 0x25119 /* offset 6399 */, 0x25133 /* offset 6400 */, 0x771e /* offset 6401 */, 0x771f /* offset 6402 */, 0x778b /* offset 6403 */, 0x4046 /* offset 6404 */, 0x4096 /* offset 6405 */, 0x2541d /* offset 6406 */, 0x784e /* offset 6407 */, 0x40e3 /* offset 6408 */, 0x25626 /* offset 6409 */, 0x2569a /* offset 6410 */, 0x256c5 /* offset 6411 */, 0x79eb /* offset 6412 */, 0x412f /* offset 6413 */, 0x7a4a /* offset 6414 */, 0x7a4f /* offset 6415 */, 0x2597c /* offset 6416 */, 0x25aa7 /* offset 6417 */, 0x7aee /* offset 6418 */, 0x4202 /* offset 6419 */, 0x25bab /* offset 6420 */, 0x7bc6 /* offset 6421 */, 0x7bc9 /* offset 6422 */, 0x4227 /* offset 6423 */, 0x25c80 /* offset 6424 */, 0x7cd2 /* offset 6425 */, 0x42a0 /* offset 6426 */, 0x7ce8 /* offset 6427 */, 0x7ce3 /* offset 6428 */, 0x7d00 /* offset 6429 */, 0x25f86 /* offset 6430 */, 0x7d63 /* offset 6431 */, 0x4301 /* offset 6432 */, 0x7dc7 /* offset 6433 */, 0x7e02 /* offset 6434 */, 0x7e45 /* offset 6435 */, 0x4334 /* offset 6436 */, 0x26228 /* offset 6437 */, 0x26247 /* offset 6438 */, 0x4359 /* offset 6439 */, 0x262d9 /* offset 6440 */, 0x7f7a /* offset 6441 */, 0x2633e /* offset 6442 */, 0x7f95 /* offset 6443 */, 0x7ffa /* offset 6444 */, 0x264da /* offset 6445 */, 0x26523 /* offset 6446 */, 0x8060 /* offset 6447 */, 0x265a8 /* offset 6448 */, 0x8070 /* offset 6449 */, 0x2335f /* offset 6450 */, 0x43d5 /* offset 6451 */, 0x80b2 /* offset 6452 */, 0x8103 /* offset 6453 */, 0x440b /* offset 6454 */, 0x813e /* offset 6455 */, 0x5ab5 /* offset 6456 */, 0x267a7 /* offset 6457 */, 0x267b5 /* offset 6458 */, 0x23393 /* offset 6459 */, 0x2339c /* offset 6460 */, 0x8204 /* offset 6461 */, 0x8f9e /* offset 6462 */, 0x446b /* offset 6463 */, 0x8291 /* offset 6464 */, 0x828b /* offset 6465 */, 0x829d /* offset 6466 */, 0x52b3 /* offset 6467 */, 0x82b1 /* offset 6468 */, 0x82b3 /* offset 6469 */, 0x82bd /* offset 6470 */, 0x82e6 /* offset 6471 */, 0x26b3c /* offset 6472 */, 0x831d /* offset 6473 */, 0x8363 /* offset 6474 */, 0x83ad /* offset 6475 */, 0x8323 /* offset 6476 */, 0x83bd /* offset 6477 */, 0x83e7 /* offset 6478 */, 0x8353 /* offset 6479 */, 0x83ca /* offset 6480 */, 0x83cc /* offset 6481 */, 0x83dc /* offset 6482 */, 0x26c36 /* offset 6483 */, 0x26d6b /* offset 6484 */, 0x26cd5 /* offset 6485 */, 0x452b /* offset 6486 */, 0x84f1 /* offset 6487 */, 0x84f3 /* offset 6488 */, 0x8516 /* offset 6489 */, 0x273ca /* offset 6490 */, 0x8564 /* offset 6491 */, 0x26f2c /* offset 6492 */, 0x455d /* offset 6493 */, 0x4561 /* offset 6494 */, 0x26fb1 /* offset 6495 */, 0x270d2 /* offset 6496 */, 0x456b /* offset 6497 */, 0x8650 /* offset 6498 */, 0x8667 /* offset 6499 */, 0x8669 /* offset 6500 */, 0x86a9 /* offset 6501 */, 0x8688 /* offset 6502 */, 0x870e /* offset 6503 */, 0x86e2 /* offset 6504 */, 0x8728 /* offset 6505 */, 0x876b /* offset 6506 */, 0x8786 /* offset 6507 */, 0x45d7 /* offset 6508 */, 0x87e1 /* offset 6509 */, 0x8801 /* offset 6510 */, 0x45f9 /* offset 6511 */, 0x8860 /* offset 6512 */, 0x27667 /* offset 6513 */, 0x88d7 /* offset 6514 */, 0x88de /* offset 6515 */, 0x4635 /* offset 6516 */, 0x88fa /* offset 6517 */, 0x34bb /* offset 6518 */, 0x278ae /* offset 6519 */, 0x27966 /* offset 6520 */, 0x46be /* offset 6521 */, 0x46c7 /* offset 6522 */, 0x8aa0 /* offset 6523 */, 0x27ca8 /* offset 6524 */, 0x8cab /* offset 6525 */, 0x8cc1 /* offset 6526 */, 0x8d1b /* offset 6527 */, 0x8d77 /* offset 6528 */, 0x27f2f /* offset 6529 */, 0x20804 /* offset 6530 */, 0x8dcb /* offset 6531 */, 0x8dbc /* offset 6532 */, 0x8df0 /* offset 6533 */, 0x208de /* offset 6534 */, 0x8ed4 /* offset 6535 */, 0x285d2 /* offset 6536 */, 0x285ed /* offset 6537 */, 0x9094 /* offset 6538 */, 0x90f1 /* offset 6539 */, 0x9111 /* offset 6540 */, 0x2872e /* offset 6541 */, 0x911b /* offset 6542 */, 0x9238 /* offset 6543 */, 0x92d7 /* offset 6544 */, 0x92d8 /* offset 6545 */, 0x927c /* offset 6546 */, 0x93f9 /* offset 6547 */, 0x9415 /* offset 6548 */, 0x28bfa /* offset 6549 */, 0x958b /* offset 6550 */, 0x4995 /* offset 6551 */, 0x95b7 /* offset 6552 */, 0x28d77 /* offset 6553 */, 0x49e6 /* offset 6554 */, 0x96c3 /* offset 6555 */, 0x5db2 /* offset 6556 */, 0x9723 /* offset 6557 */, 0x29145 /* offset 6558 */, 0x2921a /* offset 6559 */, 0x4a6e /* offset 6560 */, 0x4a76 /* offset 6561 */, 0x97e0 /* offset 6562 */, 0x2940a /* offset 6563 */, 0x4ab2 /* offset 6564 */, 0x29496 /* offset 6565 */, 0x9829 /* offset 6566 */, 0x295b6 /* offset 6567 */, 0x98e2 /* offset 6568 */, 0x4b33 /* offset 6569 */, 0x9929 /* offset 6570 */, 0x99a7 /* offset 6571 */, 0x99c2 /* offset 6572 */, 0x99fe /* offset 6573 */, 0x4bce /* offset 6574 */, 0x29b30 /* offset 6575 */, 0x9c40 /* offset 6576 */, 0x9cfd /* offset 6577 */, 0x4cce /* offset 6578 */, 0x4ced /* offset 6579 */, 0x9d67 /* offset 6580 */, 0x2a0ce /* offset 6581 */, 0x4cf8 /* offset 6582 */, 0x2a105 /* offset 6583 */, 0x2a20e /* offset 6584 */, 0x2a291 /* offset 6585 */, 0x4d56 /* offset 6586 */, 0x9efe /* offset 6587 */, 0x9f05 /* offset 6588 */, 0x9f0f /* offset 6589 */, 0x9f16 /* offset 6590 */, 0x2a600 /* offset 6591 */ }; poppler-24.02.0/poppler/UnicodeMap.cc000066400000000000000000000206531455701731300173700ustar00rootroot00000000000000//======================================================================== // // UnicodeMap.cc // // Copyright 2001-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) 2010 Jakub Wilk // Copyright (C) 2017-2020, 2022 Albert Astals Cid // Copyright (C) 2017 Adrian Johnson // Copyright (C) 2017 Jean Ghali // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019 Oliver Sander // Copyright (C) 2019 Volker Krause // // 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 #include #include #include "goo/glibc.h" #include "goo/gmem.h" #include "goo/gfile.h" #include "goo/GooString.h" #include "Error.h" #include "GlobalParams.h" #include "UnicodeMap.h" //------------------------------------------------------------------------ #define maxExtCode 16 struct UnicodeMapExt { Unicode u; // Unicode char char code[maxExtCode]; unsigned int nBytes; }; //------------------------------------------------------------------------ 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; char *tok1, *tok2, *tok3; char *tokptr; if (!(f = globalParams->getUnicodeMapFile(encodingNameA))) { error(errSyntaxError, -1, "Couldn't find unicodeMap file for the '{0:s}' encoding", encodingNameA.c_str()); return {}; } auto map = std::unique_ptr(new UnicodeMap(encodingNameA)); size = 8; UnicodeMapRange *customRanges = (UnicodeMapRange *)gmallocn(size, sizeof(UnicodeMapRange)); eMapsSize = 0; line = 1; while (getLine(buf, sizeof(buf), f)) { if ((tok1 = strtok_r(buf, " \t\r\n", &tokptr)) && (tok2 = strtok_r(nullptr, " \t\r\n", &tokptr))) { if (!(tok3 = strtok_r(nullptr, " \t\r\n", &tokptr))) { tok3 = tok2; tok2 = tok1; } 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; } 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) { unsigned int x; sscanf(tok3 + i * 2, "%2x", &x); eMap->code[i] = (char)x; } eMap->nBytes = nBytes; ++map->eMapsLen; } else { error(errSyntaxError, -1, "Bad line ({0:d}) in unicodeMap file for the '{1:s}' encoding", line, encodingNameA.c_str()); } } else { error(errSyntaxError, -1, "Bad line ({0:d}) in unicodeMap file for the '{1:s}' encoding", line, encodingNameA.c_str()); } ++line; } fclose(f); map->ranges = customRanges; return map; } 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) { encodingName = encodingNameA; unicodeOut = unicodeOutA; kind = unicodeMapResident; ranges = rangesA; len = lenA; eMaps = nullptr; eMapsLen = 0; } UnicodeMap::UnicodeMap(const char *encodingNameA, bool unicodeOutA, UnicodeMapFunc funcA) { encodingName = encodingNameA; unicodeOut = unicodeOutA; kind = unicodeMapFunc; func = funcA; eMaps = nullptr; eMapsLen = 0; } UnicodeMap::~UnicodeMap() { if (kind == unicodeMapUser && ranges) { gfree(const_cast(ranges)); } if (eMaps) { gfree(eMaps); } } 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::operator=(UnicodeMap &&other) noexcept { if (this != &other) { swap(other); } return *this; } 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(eMaps, other.eMaps); swap(eMapsLen, other.eMapsLen); } bool UnicodeMap::match(const std::string &encodingNameA) const { return encodingName == encodingNameA; } 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; } //------------------------------------------------------------------------ UnicodeMapCache::UnicodeMapCache() { } const UnicodeMap *UnicodeMapCache::getUnicodeMap(const std::string &encodingName) { for (const std::unique_ptr &map : cache) { if (map->match(encodingName)) { return map.get(); } } std::unique_ptr map = UnicodeMap::parse(encodingName); if (map) { UnicodeMap *m = map.get(); cache.emplace_back(std::move(map)); return m; } return nullptr; } poppler-24.02.0/poppler/UnicodeMap.h000066400000000000000000000074101455701731300172260ustar00rootroot00000000000000//======================================================================== // // UnicodeMap.h // // Mapping from Unicode to an encoding. // // Copyright 2001-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) 2017 Adrian Johnson // Copyright (C) 2018-2022 Albert Astals Cid // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019 Volker Krause // // 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 UNICODEMAP_H #define UNICODEMAP_H #include "poppler-config.h" #include "poppler_private_export.h" #include "CharTypes.h" #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 { Unicode start, end; // range of Unicode chars unsigned int code, nBytes; // first output code }; struct UnicodeMapExt; //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT UnicodeMap { public: // Create the UnicodeMap specified by . Sets the // initial reference count to 1. Returns NULL on failure. static std::unique_ptr parse(const std::string &encodingNameA); // Create a resident UnicodeMap. UnicodeMap(const char *encodingNameA, bool unicodeOutA, const UnicodeMapRange *rangesA, int lenA); // Create a resident UnicodeMap that uses a function instead of a // list of ranges. UnicodeMap(const char *encodingNameA, bool unicodeOutA, UnicodeMapFunc funcA); UnicodeMap(UnicodeMap &&other) noexcept; UnicodeMap &operator=(UnicodeMap &&other) noexcept; void swap(UnicodeMap &other) noexcept; ~UnicodeMap(); UnicodeMap(const UnicodeMap &) = delete; UnicodeMap &operator=(const UnicodeMap &) = delete; std::string getEncodingName() const { return encodingName; } bool isUnicode() const { return unicodeOut; } // Return true if this UnicodeMap matches the specified // . bool match(const std::string &encodingNameA) const; // Map Unicode to the target encoding. Fills in with the // output and returns the number of bytes used. Output will be // truncated at bytes. No string terminator is written. // Returns 0 if no mapping is found. int mapUnicode(Unicode u, char *buf, int bufSize) const; private: 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) }; //------------------------------------------------------------------------ class UnicodeMapCache { public: UnicodeMapCache(); // Get the UnicodeMap for . Returns NULL on failure. const UnicodeMap *getUnicodeMap(const std::string &encodingName); private: std::vector> cache; }; #endif poppler-24.02.0/poppler/UnicodeMapFuncs.cc000066400000000000000000000047551455701731300203740ustar00rootroot00000000000000//======================================================================== // // UnicodeMapFuncs.cc // // Copyright 2001-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) 2008 Koji Otani // Copyright (C) 2017 Adrian Johnson // Copyright (C) 2018 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 "UnicodeMapFuncs.h" int mapUTF8(Unicode u, char *buf, int bufSize) { if (u <= 0x0000007f) { if (bufSize < 1) { return 0; } buf[0] = (char)u; return 1; } else if (u <= 0x000007ff) { if (bufSize < 2) { return 0; } buf[0] = (char)(0xc0 + (u >> 6)); buf[1] = (char)(0x80 + (u & 0x3f)); return 2; } else if (u <= 0x0000ffff) { if (bufSize < 3) { return 0; } buf[0] = (char)(0xe0 + (u >> 12)); buf[1] = (char)(0x80 + ((u >> 6) & 0x3f)); buf[2] = (char)(0x80 + (u & 0x3f)); return 3; } else if (u <= 0x0010ffff) { if (bufSize < 4) { return 0; } buf[0] = (char)(0xf0 + (u >> 18)); buf[1] = (char)(0x80 + ((u >> 12) & 0x3f)); buf[2] = (char)(0x80 + ((u >> 6) & 0x3f)); buf[3] = (char)(0x80 + (u & 0x3f)); return 4; } else { return 0; } } int mapUTF16(Unicode u, char *buf, int bufSize) { if (u <= 0xffff) { if (bufSize < 2) { return 0; } buf[0] = (char)((u >> 8) & 0xff); buf[1] = (char)(u & 0xff); return 2; } else if (u < 0x110000) { Unicode uu; /* using surrogate pair */ if (bufSize < 4) { return 0; } uu = ((u - 0x10000) >> 10) + 0xd800; buf[0] = (char)((uu >> 8) & 0xff); buf[1] = (char)(uu & 0xff); uu = (u & 0x3ff) + 0xdc00; buf[2] = (char)((uu >> 8) & 0xff); buf[3] = (char)(uu & 0xff); return 4; } else { return 0; } } poppler-24.02.0/poppler/UnicodeMapFuncs.h000066400000000000000000000021741455701731300202270ustar00rootroot00000000000000//======================================================================== // // UnicodeMapFuncs.h // // Copyright 2001-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) 2008 Koji Otani // Copyright (C) 2017 Adrian Johnson // Copyright (C) 2018, 2019 Albert Astals Cid // Copyright (C) 2019 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 // //======================================================================== #ifndef UNICODEMAPFUNCS_H #define UNICODEMAPFUNCS_H #include "UTF.h" int POPPLER_PRIVATE_EXPORT mapUTF8(Unicode u, char *buf, int bufSize); int mapUTF16(Unicode u, char *buf, int bufSize); #endif poppler-24.02.0/poppler/UnicodeMapTables.h000066400000000000000000000277541455701731300203760ustar00rootroot00000000000000//======================================================================== // // UnicodeMapTables.h // // Copyright 2001-2009 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) 2019 Volker Krause // // 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 // //======================================================================== 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 }, { 0x0161, 0x0161, 0x73, 1 }, { 0x0178, 0x0178, 0x59, 1 }, { 0x017d, 0x017d, 0x5a, 1 }, { 0x017e, 0x017e, 0x7a, 1 }, { 0x02c6, 0x02c6, 0x5e, 1 }, { 0x02da, 0x02da, 0xb0, 1 }, { 0x02dc, 0x02dc, 0x7e, 1 }, { 0x2013, 0x2013, 0xad, 1 }, { 0x2014, 0x2014, 0x2d2d, 2 }, { 0x2018, 0x2018, 0x60, 1 }, { 0x2019, 0x2019, 0x27, 1 }, { 0x201a, 0x201a, 0x2c, 1 }, { 0x201c, 0x201c, 0x22, 1 }, { 0x201d, 0x201d, 0x22, 1 }, { 0x201e, 0x201e, 0x2c2c, 2 }, { 0x2022, 0x2022, 0xb7, 1 }, { 0x2026, 0x2026, 0x2e2e2e, 3 }, { 0x2039, 0x2039, 0x3c, 1 }, { 0x203a, 0x203a, 0x3e, 1 }, { 0x2044, 0x2044, 0x2f, 1 }, { 0x2122, 0x2122, 0x544d, 2 }, { 0x2212, 0x2212, 0x2d, 1 }, { 0xf6f9, 0xf6f9, 0x4c, 1 }, { 0xf6fa, 0xf6fa, 0x4f45, 2 }, { 0xf6fc, 0xf6fc, 0xb0, 1 }, { 0xf6fd, 0xf6fd, 0x53, 1 }, { 0xf6fe, 0xf6fe, 0x7e, 1 }, { 0xf6ff, 0xf6ff, 0x5a, 1 }, { 0xf721, 0xf721, 0x21, 1 }, { 0xf724, 0xf724, 0x24, 1 }, { 0xf726, 0xf726, 0x26, 1 }, { 0xf730, 0xf739, 0x30, 1 }, { 0xf73f, 0xf73f, 0x3f, 1 }, { 0xf761, 0xf77a, 0x41, 1 }, { 0xf7a1, 0xf7a2, 0xa1, 1 }, { 0xf7bf, 0xf7bf, 0xbf, 1 }, { 0xf7e0, 0xf7f6, 0xc0, 1 }, { 0xf7f8, 0xf7fe, 0xd8, 1 }, { 0xf7ff, 0xf7ff, 0x59, 1 }, { 0xfb00, 0xfb00, 0x6666, 2 }, { 0xfb01, 0xfb01, 0x6669, 2 }, { 0xfb02, 0xfb02, 0x666c, 2 }, { 0xfb03, 0xfb03, 0x666669, 3 }, { 0xfb04, 0xfb04, 0x66666c, 3 }, { 0xfb05, 0xfb05, 0x7374, 2 }, { 0xfb06, 0xfb06, 0x7374, 2 } }; #define latin1UnicodeMapLen (sizeof(latin1UnicodeMapRanges) / sizeof(UnicodeMapRange)) static const UnicodeMapRange ascii7UnicodeMapRanges[] = { { 0x000a, 0x000a, 0x0a, 1 }, { 0x000c, 0x000d, 0x0c, 1 }, { 0x0020, 0x005f, 0x20, 1 }, { 0x0061, 0x007e, 0x61, 1 }, { 0x00a6, 0x00a6, 0x7c, 1 }, { 0x00a9, 0x00a9, 0x286329, 3 }, { 0x00ae, 0x00ae, 0x285229, 3 }, { 0x00b7, 0x00b7, 0x2a, 1 }, { 0x00bc, 0x00bc, 0x312f34, 3 }, { 0x00bd, 0x00bd, 0x312f32, 3 }, { 0x00be, 0x00be, 0x332f34, 3 }, { 0x00c0, 0x00c0, 0x41, 1 }, { 0x00c1, 0x00c1, 0x41, 1 }, { 0x00c2, 0x00c2, 0x41, 1 }, { 0x00c3, 0x00c3, 0x41, 1 }, { 0x00c4, 0x00c4, 0x41, 1 }, { 0x00c5, 0x00c5, 0x41, 1 }, { 0x00c6, 0x00c6, 0x4145, 2 }, { 0x00c7, 0x00c7, 0x43, 1 }, { 0x00c8, 0x00c8, 0x45, 1 }, { 0x00c9, 0x00c9, 0x45, 1 }, { 0x00ca, 0x00ca, 0x45, 1 }, { 0x00cb, 0x00cb, 0x45, 1 }, { 0x00cc, 0x00cc, 0x49, 1 }, { 0x00cd, 0x00cd, 0x49, 1 }, { 0x00ce, 0x00ce, 0x49, 1 }, { 0x00cf, 0x00cf, 0x49, 1 }, { 0x00d1, 0x00d2, 0x4e, 1 }, { 0x00d3, 0x00d3, 0x4f, 1 }, { 0x00d4, 0x00d4, 0x4f, 1 }, { 0x00d5, 0x00d5, 0x4f, 1 }, { 0x00d6, 0x00d6, 0x4f, 1 }, { 0x00d7, 0x00d7, 0x78, 1 }, { 0x00d8, 0x00d8, 0x4f, 1 }, { 0x00d9, 0x00d9, 0x55, 1 }, { 0x00da, 0x00da, 0x55, 1 }, { 0x00db, 0x00db, 0x55, 1 }, { 0x00dc, 0x00dc, 0x55, 1 }, { 0x00dd, 0x00dd, 0x59, 1 }, { 0x00e0, 0x00e0, 0x61, 1 }, { 0x00e1, 0x00e1, 0x61, 1 }, { 0x00e2, 0x00e2, 0x61, 1 }, { 0x00e3, 0x00e3, 0x61, 1 }, { 0x00e4, 0x00e4, 0x61, 1 }, { 0x00e5, 0x00e5, 0x61, 1 }, { 0x00e6, 0x00e6, 0x6165, 2 }, { 0x00e7, 0x00e7, 0x63, 1 }, { 0x00e8, 0x00e8, 0x65, 1 }, { 0x00e9, 0x00e9, 0x65, 1 }, { 0x00ea, 0x00ea, 0x65, 1 }, { 0x00eb, 0x00eb, 0x65, 1 }, { 0x00ec, 0x00ec, 0x69, 1 }, { 0x00ed, 0x00ed, 0x69, 1 }, { 0x00ee, 0x00ee, 0x69, 1 }, { 0x00ef, 0x00ef, 0x69, 1 }, { 0x00f1, 0x00f2, 0x6e, 1 }, { 0x00f3, 0x00f3, 0x6f, 1 }, { 0x00f4, 0x00f4, 0x6f, 1 }, { 0x00f5, 0x00f5, 0x6f, 1 }, { 0x00f6, 0x00f6, 0x6f, 1 }, { 0x00f7, 0x00f7, 0x2f, 1 }, { 0x00f8, 0x00f8, 0x6f, 1 }, { 0x00f9, 0x00f9, 0x75, 1 }, { 0x00fa, 0x00fa, 0x75, 1 }, { 0x00fb, 0x00fb, 0x75, 1 }, { 0x00fc, 0x00fc, 0x75, 1 }, { 0x00fd, 0x00fd, 0x79, 1 }, { 0x00ff, 0x00ff, 0x79, 1 }, { 0x0131, 0x0131, 0x69, 1 }, { 0x0141, 0x0141, 0x4c, 1 }, { 0x0152, 0x0152, 0x4f45, 2 }, { 0x0153, 0x0153, 0x6f65, 2 }, { 0x0160, 0x0160, 0x53, 1 }, { 0x0178, 0x0178, 0x59, 1 }, { 0x017d, 0x017d, 0x5a, 1 }, { 0x2013, 0x2013, 0x2d, 1 }, { 0x2014, 0x2014, 0x2d2d, 2 }, { 0x2018, 0x2018, 0x60, 1 }, { 0x2019, 0x2019, 0x27, 1 }, { 0x201c, 0x201c, 0x22, 1 }, { 0x201d, 0x201d, 0x22, 1 }, { 0x2022, 0x2022, 0x2a, 1 }, { 0x2026, 0x2026, 0x2e2e2e, 3 }, { 0x2122, 0x2122, 0x544d, 2 }, { 0x2212, 0x2212, 0x2d, 1 }, { 0xf6f9, 0xf6f9, 0x4c, 1 }, { 0xf6fa, 0xf6fa, 0x4f45, 2 }, { 0xf6fd, 0xf6fd, 0x53, 1 }, { 0xf6fe, 0xf6fe, 0x7e, 1 }, { 0xf6ff, 0xf6ff, 0x5a, 1 }, { 0xf721, 0xf721, 0x21, 1 }, { 0xf724, 0xf724, 0x24, 1 }, { 0xf726, 0xf726, 0x26, 1 }, { 0xf730, 0xf739, 0x30, 1 }, { 0xf73f, 0xf73f, 0x3f, 1 }, { 0xf761, 0xf77a, 0x41, 1 }, { 0xf7e0, 0xf7e0, 0x41, 1 }, { 0xf7e1, 0xf7e1, 0x41, 1 }, { 0xf7e2, 0xf7e2, 0x41, 1 }, { 0xf7e3, 0xf7e3, 0x41, 1 }, { 0xf7e4, 0xf7e4, 0x41, 1 }, { 0xf7e5, 0xf7e5, 0x41, 1 }, { 0xf7e6, 0xf7e6, 0x4145, 2 }, { 0xf7e7, 0xf7e7, 0x43, 1 }, { 0xf7e8, 0xf7e8, 0x45, 1 }, { 0xf7e9, 0xf7e9, 0x45, 1 }, { 0xf7ea, 0xf7ea, 0x45, 1 }, { 0xf7eb, 0xf7eb, 0x45, 1 }, { 0xf7ec, 0xf7ec, 0x49, 1 }, { 0xf7ed, 0xf7ed, 0x49, 1 }, { 0xf7ee, 0xf7ee, 0x49, 1 }, { 0xf7ef, 0xf7ef, 0x49, 1 }, { 0xf7f1, 0xf7f2, 0x4e, 1 }, { 0xf7f3, 0xf7f3, 0x4f, 1 }, { 0xf7f4, 0xf7f4, 0x4f, 1 }, { 0xf7f5, 0xf7f5, 0x4f, 1 }, { 0xf7f6, 0xf7f6, 0x4f, 1 }, { 0xf7f8, 0xf7f8, 0x4f, 1 }, { 0xf7f9, 0xf7f9, 0x55, 1 }, { 0xf7fa, 0xf7fa, 0x55, 1 }, { 0xf7fb, 0xf7fb, 0x55, 1 }, { 0xf7fc, 0xf7fc, 0x55, 1 }, { 0xf7fd, 0xf7fd, 0x59, 1 }, { 0xf7ff, 0xf7ff, 0x59, 1 }, { 0xfb00, 0xfb00, 0x6666, 2 }, { 0xfb01, 0xfb01, 0x6669, 2 }, { 0xfb02, 0xfb02, 0x666c, 2 }, { 0xfb03, 0xfb03, 0x666669, 3 }, { 0xfb04, 0xfb04, 0x66666c, 3 }, { 0xfb05, 0xfb05, 0x7374, 2 }, { 0xfb06, 0xfb06, 0x7374, 2 } }; #define ascii7UnicodeMapLen (sizeof(ascii7UnicodeMapRanges) / sizeof(UnicodeMapRange)) static const UnicodeMapRange symbolUnicodeMapRanges[] = { { 0x0020, 0x0021, 0x20, 1 }, { 0x0023, 0x0023, 0x23, 1 }, { 0x0025, 0x0026, 0x25, 1 }, { 0x0028, 0x0029, 0x28, 1 }, { 0x002b, 0x002c, 0x2b, 1 }, { 0x002e, 0x003f, 0x2e, 1 }, { 0x005b, 0x005b, 0x5b, 1 }, { 0x005d, 0x005d, 0x5d, 1 }, { 0x005f, 0x005f, 0x5f, 1 }, { 0x007b, 0x007d, 0x7b, 1 }, { 0x00ac, 0x00ac, 0xd8, 1 }, { 0x00b0, 0x00b1, 0xb0, 1 }, { 0x00b5, 0x00b5, 0x6d, 1 }, { 0x00d7, 0x00d7, 0xb4, 1 }, { 0x00f7, 0x00f7, 0xb8, 1 }, { 0x0192, 0x0192, 0xa6, 1 }, { 0x0391, 0x0392, 0x41, 1 }, { 0x0393, 0x0393, 0x47, 1 }, { 0x0395, 0x0395, 0x45, 1 }, { 0x0396, 0x0396, 0x5a, 1 }, { 0x0397, 0x0397, 0x48, 1 }, { 0x0398, 0x0398, 0x51, 1 }, { 0x0399, 0x0399, 0x49, 1 }, { 0x039a, 0x039d, 0x4b, 1 }, { 0x039e, 0x039e, 0x58, 1 }, { 0x039f, 0x03a0, 0x4f, 1 }, { 0x03a1, 0x03a1, 0x52, 1 }, { 0x03a3, 0x03a5, 0x53, 1 }, { 0x03a6, 0x03a6, 0x46, 1 }, { 0x03a7, 0x03a7, 0x43, 1 }, { 0x03a8, 0x03a8, 0x59, 1 }, { 0x03b1, 0x03b2, 0x61, 1 }, { 0x03b3, 0x03b3, 0x67, 1 }, { 0x03b4, 0x03b5, 0x64, 1 }, { 0x03b6, 0x03b6, 0x7a, 1 }, { 0x03b7, 0x03b7, 0x68, 1 }, { 0x03b8, 0x03b8, 0x71, 1 }, { 0x03b9, 0x03b9, 0x69, 1 }, { 0x03ba, 0x03bb, 0x6b, 1 }, { 0x03bd, 0x03bd, 0x6e, 1 }, { 0x03be, 0x03be, 0x78, 1 }, { 0x03bf, 0x03c0, 0x6f, 1 }, { 0x03c1, 0x03c1, 0x72, 1 }, { 0x03c2, 0x03c2, 0x56, 1 }, { 0x03c3, 0x03c5, 0x73, 1 }, { 0x03c6, 0x03c6, 0x66, 1 }, { 0x03c7, 0x03c7, 0x63, 1 }, { 0x03c8, 0x03c8, 0x79, 1 }, { 0x03c9, 0x03c9, 0x77, 1 }, { 0x03d1, 0x03d1, 0x4a, 1 }, { 0x03d2, 0x03d2, 0xa1, 1 }, { 0x03d5, 0x03d5, 0x6a, 1 }, { 0x03d6, 0x03d6, 0x76, 1 }, { 0x2022, 0x2022, 0xb7, 1 }, { 0x2026, 0x2026, 0xbc, 1 }, { 0x2032, 0x2032, 0xa2, 1 }, { 0x2033, 0x2033, 0xb2, 1 }, { 0x2044, 0x2044, 0xa4, 1 }, { 0x2111, 0x2111, 0xc1, 1 }, { 0x2118, 0x2118, 0xc3, 1 }, { 0x211c, 0x211c, 0xc2, 1 }, { 0x2126, 0x2126, 0x57, 1 }, { 0x2135, 0x2135, 0xc0, 1 }, { 0x2190, 0x2193, 0xac, 1 }, { 0x2194, 0x2194, 0xab, 1 }, { 0x21b5, 0x21b5, 0xbf, 1 }, { 0x21d0, 0x21d3, 0xdc, 1 }, { 0x21d4, 0x21d4, 0xdb, 1 }, { 0x2200, 0x2200, 0x22, 1 }, { 0x2202, 0x2202, 0xb6, 1 }, { 0x2203, 0x2203, 0x24, 1 }, { 0x2205, 0x2205, 0xc6, 1 }, { 0x2206, 0x2206, 0x44, 1 }, { 0x2207, 0x2207, 0xd1, 1 }, { 0x2208, 0x2209, 0xce, 1 }, { 0x220b, 0x220b, 0x27, 1 }, { 0x220f, 0x220f, 0xd5, 1 }, { 0x2211, 0x2211, 0xe5, 1 }, { 0x2212, 0x2212, 0x2d, 1 }, { 0x2217, 0x2217, 0x2a, 1 }, { 0x221a, 0x221a, 0xd6, 1 }, { 0x221d, 0x221d, 0xb5, 1 }, { 0x221e, 0x221e, 0xa5, 1 }, { 0x2220, 0x2220, 0xd0, 1 }, { 0x2227, 0x2228, 0xd9, 1 }, { 0x2229, 0x222a, 0xc7, 1 }, { 0x222b, 0x222b, 0xf2, 1 }, { 0x2234, 0x2234, 0x5c, 1 }, { 0x223c, 0x223c, 0x7e, 1 }, { 0x2245, 0x2245, 0x40, 1 }, { 0x2248, 0x2248, 0xbb, 1 }, { 0x2260, 0x2261, 0xb9, 1 }, { 0x2264, 0x2264, 0xa3, 1 }, { 0x2265, 0x2265, 0xb3, 1 }, { 0x2282, 0x2282, 0xcc, 1 }, { 0x2283, 0x2283, 0xc9, 1 }, { 0x2284, 0x2284, 0xcb, 1 }, { 0x2286, 0x2286, 0xcd, 1 }, { 0x2287, 0x2287, 0xca, 1 }, { 0x2295, 0x2295, 0xc5, 1 }, { 0x2297, 0x2297, 0xc4, 1 }, { 0x22a5, 0x22a5, 0x5e, 1 }, { 0x22c5, 0x22c5, 0xd7, 1 }, { 0x2320, 0x2320, 0xf3, 1 }, { 0x2321, 0x2321, 0xf5, 1 }, { 0x2329, 0x2329, 0xe1, 1 }, { 0x232a, 0x232a, 0xf1, 1 }, { 0x25ca, 0x25ca, 0xe0, 1 }, { 0x2660, 0x2660, 0xaa, 1 }, { 0x2663, 0x2663, 0xa7, 1 }, { 0x2665, 0x2665, 0xa9, 1 }, { 0x2666, 0x2666, 0xa8, 1 }, { 0xf6d9, 0xf6d9, 0xd3, 1 }, { 0xf6da, 0xf6da, 0xd2, 1 }, { 0xf6db, 0xf6db, 0xd4, 1 }, { 0xf8e5, 0xf8e5, 0x60, 1 }, { 0xf8e6, 0xf8e7, 0xbd, 1 }, { 0xf8e8, 0xf8ea, 0xe2, 1 }, { 0xf8eb, 0xf8f4, 0xe6, 1 }, { 0xf8f5, 0xf8f5, 0xf4, 1 }, { 0xf8f6, 0xf8fe, 0xf6, 1 } }; #define symbolUnicodeMapLen (sizeof(symbolUnicodeMapRanges) / sizeof(UnicodeMapRange)) static const UnicodeMapRange zapfDingbatsUnicodeMapRanges[] = { { 0x0020, 0x0020, 0x20, 1 }, { 0x2192, 0x2192, 0xd5, 1 }, { 0x2194, 0x2195, 0xd6, 1 }, { 0x2460, 0x2469, 0xac, 1 }, { 0x25a0, 0x25a0, 0x6e, 1 }, { 0x25b2, 0x25b2, 0x73, 1 }, { 0x25bc, 0x25bc, 0x74, 1 }, { 0x25c6, 0x25c6, 0x75, 1 }, { 0x25cf, 0x25cf, 0x6c, 1 }, { 0x25d7, 0x25d7, 0x77, 1 }, { 0x2605, 0x2605, 0x48, 1 }, { 0x260e, 0x260e, 0x25, 1 }, { 0x261b, 0x261b, 0x2a, 1 }, { 0x261e, 0x261e, 0x2b, 1 }, { 0x2660, 0x2660, 0xab, 1 }, { 0x2663, 0x2663, 0xa8, 1 }, { 0x2665, 0x2665, 0xaa, 1 }, { 0x2666, 0x2666, 0xa9, 1 }, { 0x2701, 0x2704, 0x21, 1 }, { 0x2706, 0x2709, 0x26, 1 }, { 0x270c, 0x2727, 0x2c, 1 }, { 0x2729, 0x274b, 0x49, 1 }, { 0x274d, 0x274d, 0x6d, 1 }, { 0x274f, 0x2752, 0x6f, 1 }, { 0x2756, 0x2756, 0x76, 1 }, { 0x2758, 0x275e, 0x78, 1 }, { 0x2761, 0x2767, 0xa1, 1 }, { 0x2776, 0x2794, 0xb6, 1 }, { 0x2798, 0x27af, 0xd8, 1 }, { 0x27b1, 0x27be, 0xf1, 1 } }; #define zapfDingbatsUnicodeMapLen (sizeof(zapfDingbatsUnicodeMapRanges) / sizeof(UnicodeMapRange)) poppler-24.02.0/poppler/UnicodeTypeTable.cc000066400000000000000000002065111455701731300205430ustar00rootroot00000000000000//======================================================================== // // UnicodeTypeTable.cc // // Copyright 2004 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, 2007 Ed Catmur // Copyright (C) 2007 Jeff Muizelaar // Copyright (C) 2008, 2016 Albert Astals Cid // Copyright (C) 2012 Adrian Johnson // Copyright (C) 2016 Khaled Hosny // Copyright (C) 2019 Adriaan de Groot // Copyright (C) 2020 Alex Henrie // // 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 #include "CharTypes.h" #include "UnicodeTypeTable.h" #include "goo/gmem.h" struct UnicodeMapTableEntry { const char *vector; const char type; }; struct UnicodeCaseTableVector { Unicode codes[256]; }; static const UnicodeMapTableEntry typeTable[256] = { { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN###NNNNN################NNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN#N####NNNNLNNNNN####NLNNN#" "LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL", 'X' }, { nullptr, 'L' }, { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLNNNNNNNNNNNNNNLLNNNNNNNNNNNNNNLLLLLNNN" "NNNNNNLNNNNNNNNNNNNNNNNN", 'X' }, { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLNNNNNNNNNNNLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL" "LLLLLLLLLLLLLLNLLLLLLLLL", 'X' }, { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL" "LLLLLLLLLLLLLLLLLLLLLLLL", 'X' }, { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNRNRNNRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR" "RRRRRRRRRRRRRRRRRRRRRRRR", 'X' }, { "RRRR#########RNNNNNNNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNNNNNNNN####################" "RRRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNRNNNNNNNRRNNNNNNNRR##########RRRRRR", 'X' }, { "RRRRRRRRRRRRRRNNRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNNNNNNNNNNNNNNNNNNNNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNNNNNRNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN" "NNNNNNNNNNNNNNNNNNNNNNNN", 'X' }, { nullptr, 'N' }, { "NNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNLLLLNLLLNNNNLLLLLLLLLLLLLNNLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNLLLLLLLLNLLLLLLLLLLLLLLLLLLLLNNLLLL" "LLLLLLLLLL##LLLLLLLNNNNN", 'X' }, { "NNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNLLLLNLLLLLLLLLLLLLLLLLLLLNNLLLL" "LLLLLLLL##NNNNNNNNNNNNNN", 'X' }, { "NNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLNLNNNLLLLLLLLLNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLL" "LLLLLLLLLLLNNNNNN#NLLLLL", 'X' }, { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLL" "LLLLLLLLLLLLLLLLLLLLLLLL", 'X' }, { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNLLLLLLLNNNNNLLLLLLLLLLLLLLLLL" "LLLLLLLLLLLLLLLLLLLLLLLL", 'X' }, { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLNNNNNNN#####" "LLLLLLLNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLNNNNNNNNNLLLLLLLLLLNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' }, { "LLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLNLNLNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNLNNNNNLNNLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL" "LLLLLLLLLLLLLLLLLLLLLLLL", 'X' }, { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNLNNNNNNLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL" "LLLLLLLLLLLLLLLLLLLLLLLL", 'X' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL" "LLLLLLLLLLLLLLLLLLLLLLLL", 'X' }, { "LLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLLLLLNLLNNNNNNNNNNNLLLLLLL#" "LNLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNN", 'X' }, { "NNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL" "LLLLLLLLLLLLLLLLLLLLLLLL", 'X' }, { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLNNNNNLLLLLLNLLLLLLNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN" "NNNNNNNNNNNNNNNNNNNNNNNN", 'X' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLNNNLLLLLLLLLLLNNNLLLLLLLLLLLLNNNNLLLLLLLL" "LLLLLNNNLLLLLLLLLLLLLNNN", 'X' }, { "NNNNNNNNNNNNNNLRNNNNNNNNNNNNNNNNNNNNNNNNNNLRNLRN#####NNNNNNNNNNNNNNN#NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN#L##########NNNL############NNN###################################" "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' }, { "NNLNNNNLNNLLLLLLLLLLNLNNNLLLLLNNNNNNLNLNLNLLLL#" "LLLNLLLLLLLNNLLLLNNNNNLLLLLNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' }, { "NNNNNNNNNNNNNNNNNN##" "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN" "NNNN", 'X' }, { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN" "NNNNNNNNNNNNNNNNNNNNNNNN", 'X' }, { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN####################" "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNN", 'X' }, { nullptr, 'N' }, { nullptr, 'N' }, { nullptr, 'N' }, { nullptr, 'L' }, { nullptr, 'N' }, { nullptr, 'N' }, { nullptr, 'N' }, { nullptr, 'N' }, { nullptr, 'N' }, { nullptr, 'N' }, { nullptr, 'N' }, { "NNNNNLLLNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLNNNNNNNLLLLLNNLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL" "LLLLLLLLLLLLLLLLLLLNLLLL", 'X' }, { nullptr, 'L' }, { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNLLLLLLLLLLLLNNNNLLLLLLLLLLLLLLLLLLLLLLLL" "LLLLLLLLLLLLLLLLLLLLLLLL", 'X' }, { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLL" "LLLLLLLLLLLLLLLLLLLLLLLN", 'X' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN" "NNNNNNNNNNNNNNNNNNNNNNNN", 'X' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL" "LLLLLLLLLLLLLLLLLLLLLLLL", 'X' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { nullptr, 'L' }, { "LLLLLLLLLLLLLLLLLLLLLLLLRRRRRRNRRRRRRRRRR#" "RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR", 'X' }, { nullptr, 'R' }, { "RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR" "RRRRRRRRRRRRRRRRRRRRRNNN", 'X' }, { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN#N#NN#NNNNNNNNN#NN##NNNNN##" "NRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNN", 'X' }, { "NNN###NNNNN################NNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#####NNN##" "NNNNNNNNNNNNNNNNNNNNNNNLL", 'X' } }; static const UnicodeCaseTableVector caseTable00 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x03bc, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00d7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff } }; static const UnicodeCaseTableVector caseTable01 = { { 0x0101, 0x0101, 0x0103, 0x0103, 0x0105, 0x0105, 0x0107, 0x0107, 0x0109, 0x0109, 0x010b, 0x010b, 0x010d, 0x010d, 0x010f, 0x010f, 0x0111, 0x0111, 0x0113, 0x0113, 0x0115, 0x0115, 0x0117, 0x0117, 0x0119, 0x0119, 0x011b, 0x011b, 0x011d, 0x011d, 0x011f, 0x011f, 0x0121, 0x0121, 0x0123, 0x0123, 0x0125, 0x0125, 0x0127, 0x0127, 0x0129, 0x0129, 0x012b, 0x012b, 0x012d, 0x012d, 0x012f, 0x012f, 0x0130, 0x0131, 0x0133, 0x0133, 0x0135, 0x0135, 0x0137, 0x0137, 0x0138, 0x013a, 0x013a, 0x013c, 0x013c, 0x013e, 0x013e, 0x0140, 0x0140, 0x0142, 0x0142, 0x0144, 0x0144, 0x0146, 0x0146, 0x0148, 0x0148, 0x0149, 0x014b, 0x014b, 0x014d, 0x014d, 0x014f, 0x014f, 0x0151, 0x0151, 0x0153, 0x0153, 0x0155, 0x0155, 0x0157, 0x0157, 0x0159, 0x0159, 0x015b, 0x015b, 0x015d, 0x015d, 0x015f, 0x015f, 0x0161, 0x0161, 0x0163, 0x0163, 0x0165, 0x0165, 0x0167, 0x0167, 0x0169, 0x0169, 0x016b, 0x016b, 0x016d, 0x016d, 0x016f, 0x016f, 0x0171, 0x0171, 0x0173, 0x0173, 0x0175, 0x0175, 0x0177, 0x0177, 0x00ff, 0x017a, 0x017a, 0x017c, 0x017c, 0x017e, 0x017e, 0x0073, 0x0180, 0x0253, 0x0183, 0x0183, 0x0185, 0x0185, 0x0254, 0x0188, 0x0188, 0x0256, 0x0257, 0x018c, 0x018c, 0x018d, 0x01dd, 0x0259, 0x025b, 0x0192, 0x0192, 0x0260, 0x0263, 0x0195, 0x0269, 0x0268, 0x0199, 0x0199, 0x019a, 0x019b, 0x026f, 0x0272, 0x019e, 0x0275, 0x01a1, 0x01a1, 0x01a3, 0x01a3, 0x01a5, 0x01a5, 0x0280, 0x01a8, 0x01a8, 0x0283, 0x01aa, 0x01ab, 0x01ad, 0x01ad, 0x0288, 0x01b0, 0x01b0, 0x028a, 0x028b, 0x01b4, 0x01b4, 0x01b6, 0x01b6, 0x0292, 0x01b9, 0x01b9, 0x01ba, 0x01bb, 0x01bd, 0x01bd, 0x01be, 0x01bf, 0x01c0, 0x01c1, 0x01c2, 0x01c3, 0x01c6, 0x01c6, 0x01c6, 0x01c9, 0x01c9, 0x01c9, 0x01cc, 0x01cc, 0x01cc, 0x01ce, 0x01ce, 0x01d0, 0x01d0, 0x01d2, 0x01d2, 0x01d4, 0x01d4, 0x01d6, 0x01d6, 0x01d8, 0x01d8, 0x01da, 0x01da, 0x01dc, 0x01dc, 0x01dd, 0x01df, 0x01df, 0x01e1, 0x01e1, 0x01e3, 0x01e3, 0x01e5, 0x01e5, 0x01e7, 0x01e7, 0x01e9, 0x01e9, 0x01eb, 0x01eb, 0x01ed, 0x01ed, 0x01ef, 0x01ef, 0x01f0, 0x01f3, 0x01f3, 0x01f3, 0x01f5, 0x01f5, 0x0195, 0x01bf, 0x01f9, 0x01f9, 0x01fb, 0x01fb, 0x01fd, 0x01fd, 0x01ff, 0x01ff } }; static const UnicodeCaseTableVector caseTable02 = { { 0x0201, 0x0201, 0x0203, 0x0203, 0x0205, 0x0205, 0x0207, 0x0207, 0x0209, 0x0209, 0x020b, 0x020b, 0x020d, 0x020d, 0x020f, 0x020f, 0x0211, 0x0211, 0x0213, 0x0213, 0x0215, 0x0215, 0x0217, 0x0217, 0x0219, 0x0219, 0x021b, 0x021b, 0x021d, 0x021d, 0x021f, 0x021f, 0x019e, 0x0221, 0x0223, 0x0223, 0x0225, 0x0225, 0x0227, 0x0227, 0x0229, 0x0229, 0x022b, 0x022b, 0x022d, 0x022d, 0x022f, 0x022f, 0x0231, 0x0231, 0x0233, 0x0233, 0x0234, 0x0235, 0x0236, 0x0237, 0x0238, 0x0239, 0x023a, 0x023b, 0x023c, 0x023d, 0x023e, 0x023f, 0x0240, 0x0241, 0x0242, 0x0243, 0x0244, 0x0245, 0x0246, 0x0247, 0x0248, 0x0249, 0x024a, 0x024b, 0x024c, 0x024d, 0x024e, 0x024f, 0x0250, 0x0251, 0x0252, 0x0253, 0x0254, 0x0255, 0x0256, 0x0257, 0x0258, 0x0259, 0x025a, 0x025b, 0x025c, 0x025d, 0x025e, 0x025f, 0x0260, 0x0261, 0x0262, 0x0263, 0x0264, 0x0265, 0x0266, 0x0267, 0x0268, 0x0269, 0x026a, 0x026b, 0x026c, 0x026d, 0x026e, 0x026f, 0x0270, 0x0271, 0x0272, 0x0273, 0x0274, 0x0275, 0x0276, 0x0277, 0x0278, 0x0279, 0x027a, 0x027b, 0x027c, 0x027d, 0x027e, 0x027f, 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0286, 0x0287, 0x0288, 0x0289, 0x028a, 0x028b, 0x028c, 0x028d, 0x028e, 0x028f, 0x0290, 0x0291, 0x0292, 0x0293, 0x0294, 0x0295, 0x0296, 0x0297, 0x0298, 0x0299, 0x029a, 0x029b, 0x029c, 0x029d, 0x029e, 0x029f, 0x02a0, 0x02a1, 0x02a2, 0x02a3, 0x02a4, 0x02a5, 0x02a6, 0x02a7, 0x02a8, 0x02a9, 0x02aa, 0x02ab, 0x02ac, 0x02ad, 0x02ae, 0x02af, 0x02b0, 0x02b1, 0x02b2, 0x02b3, 0x02b4, 0x02b5, 0x02b6, 0x02b7, 0x02b8, 0x02b9, 0x02ba, 0x02bb, 0x02bc, 0x02bd, 0x02be, 0x02bf, 0x02c0, 0x02c1, 0x02c2, 0x02c3, 0x02c4, 0x02c5, 0x02c6, 0x02c7, 0x02c8, 0x02c9, 0x02ca, 0x02cb, 0x02cc, 0x02cd, 0x02ce, 0x02cf, 0x02d0, 0x02d1, 0x02d2, 0x02d3, 0x02d4, 0x02d5, 0x02d6, 0x02d7, 0x02d8, 0x02d9, 0x02da, 0x02db, 0x02dc, 0x02dd, 0x02de, 0x02df, 0x02e0, 0x02e1, 0x02e2, 0x02e3, 0x02e4, 0x02e5, 0x02e6, 0x02e7, 0x02e8, 0x02e9, 0x02ea, 0x02eb, 0x02ec, 0x02ed, 0x02ee, 0x02ef, 0x02f0, 0x02f1, 0x02f2, 0x02f3, 0x02f4, 0x02f5, 0x02f6, 0x02f7, 0x02f8, 0x02f9, 0x02fa, 0x02fb, 0x02fc, 0x02fd, 0x02fe, 0x02ff } }; static const UnicodeCaseTableVector caseTable03 = { { 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, 0x0308, 0x0309, 0x030a, 0x030b, 0x030c, 0x030d, 0x030e, 0x030f, 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317, 0x0318, 0x0319, 0x031a, 0x031b, 0x031c, 0x031d, 0x031e, 0x031f, 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327, 0x0328, 0x0329, 0x032a, 0x032b, 0x032c, 0x032d, 0x032e, 0x032f, 0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337, 0x0338, 0x0339, 0x033a, 0x033b, 0x033c, 0x033d, 0x033e, 0x033f, 0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x03b9, 0x0346, 0x0347, 0x0348, 0x0349, 0x034a, 0x034b, 0x034c, 0x034d, 0x034e, 0x034f, 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357, 0x0358, 0x0359, 0x035a, 0x035b, 0x035c, 0x035d, 0x035e, 0x035f, 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367, 0x0368, 0x0369, 0x036a, 0x036b, 0x036c, 0x036d, 0x036e, 0x036f, 0x0370, 0x0371, 0x0372, 0x0373, 0x0374, 0x0375, 0x0376, 0x0377, 0x0378, 0x0379, 0x037a, 0x037b, 0x037c, 0x037d, 0x037e, 0x037f, 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x03ac, 0x0387, 0x03ad, 0x03ae, 0x03af, 0x038b, 0x03cc, 0x038d, 0x03cd, 0x03ce, 0x0390, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, 0x03c0, 0x03c1, 0x03a2, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8, 0x03c9, 0x03ca, 0x03cb, 0x03ac, 0x03ad, 0x03ae, 0x03af, 0x03b0, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, 0x03c0, 0x03c1, 0x03c3, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8, 0x03c9, 0x03ca, 0x03cb, 0x03cc, 0x03cd, 0x03ce, 0x03cf, 0x03b2, 0x03b8, 0x03d2, 0x03d3, 0x03d4, 0x03c6, 0x03c0, 0x03d7, 0x03d9, 0x03d9, 0x03db, 0x03db, 0x03dd, 0x03dd, 0x03df, 0x03df, 0x03e1, 0x03e1, 0x03e3, 0x03e3, 0x03e5, 0x03e5, 0x03e7, 0x03e7, 0x03e9, 0x03e9, 0x03eb, 0x03eb, 0x03ed, 0x03ed, 0x03ef, 0x03ef, 0x03ba, 0x03c1, 0x03f2, 0x03f3, 0x03b8, 0x03b5, 0x03f6, 0x03f8, 0x03f8, 0x03f2, 0x03fb, 0x03fb, 0x03fc, 0x03fd, 0x03fe, 0x03ff } }; static const UnicodeCaseTableVector caseTable04 = { { 0x0450, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, 0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x045d, 0x045e, 0x045f, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f, 0x0450, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, 0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x045d, 0x045e, 0x045f, 0x0461, 0x0461, 0x0463, 0x0463, 0x0465, 0x0465, 0x0467, 0x0467, 0x0469, 0x0469, 0x046b, 0x046b, 0x046d, 0x046d, 0x046f, 0x046f, 0x0471, 0x0471, 0x0473, 0x0473, 0x0475, 0x0475, 0x0477, 0x0477, 0x0479, 0x0479, 0x047b, 0x047b, 0x047d, 0x047d, 0x047f, 0x047f, 0x0481, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, 0x0488, 0x0489, 0x048b, 0x048b, 0x048d, 0x048d, 0x048f, 0x048f, 0x0491, 0x0491, 0x0493, 0x0493, 0x0495, 0x0495, 0x0497, 0x0497, 0x0499, 0x0499, 0x049b, 0x049b, 0x049d, 0x049d, 0x049f, 0x049f, 0x04a1, 0x04a1, 0x04a3, 0x04a3, 0x04a5, 0x04a5, 0x04a7, 0x04a7, 0x04a9, 0x04a9, 0x04ab, 0x04ab, 0x04ad, 0x04ad, 0x04af, 0x04af, 0x04b1, 0x04b1, 0x04b3, 0x04b3, 0x04b5, 0x04b5, 0x04b7, 0x04b7, 0x04b9, 0x04b9, 0x04bb, 0x04bb, 0x04bd, 0x04bd, 0x04bf, 0x04bf, 0x04c0, 0x04c2, 0x04c2, 0x04c4, 0x04c4, 0x04c6, 0x04c6, 0x04c8, 0x04c8, 0x04ca, 0x04ca, 0x04cc, 0x04cc, 0x04ce, 0x04ce, 0x04cf, 0x04d1, 0x04d1, 0x04d3, 0x04d3, 0x04d5, 0x04d5, 0x04d7, 0x04d7, 0x04d9, 0x04d9, 0x04db, 0x04db, 0x04dd, 0x04dd, 0x04df, 0x04df, 0x04e1, 0x04e1, 0x04e3, 0x04e3, 0x04e5, 0x04e5, 0x04e7, 0x04e7, 0x04e9, 0x04e9, 0x04eb, 0x04eb, 0x04ed, 0x04ed, 0x04ef, 0x04ef, 0x04f1, 0x04f1, 0x04f3, 0x04f3, 0x04f5, 0x04f5, 0x04f6, 0x04f7, 0x04f9, 0x04f9, 0x04fa, 0x04fb, 0x04fc, 0x04fd, 0x04fe, 0x04ff } }; static const UnicodeCaseTableVector caseTable05 = { { 0x0501, 0x0501, 0x0503, 0x0503, 0x0505, 0x0505, 0x0507, 0x0507, 0x0509, 0x0509, 0x050b, 0x050b, 0x050d, 0x050d, 0x050f, 0x050f, 0x0510, 0x0511, 0x0512, 0x0513, 0x0514, 0x0515, 0x0516, 0x0517, 0x0518, 0x0519, 0x051a, 0x051b, 0x051c, 0x051d, 0x051e, 0x051f, 0x0520, 0x0521, 0x0522, 0x0523, 0x0524, 0x0525, 0x0526, 0x0527, 0x0528, 0x0529, 0x052a, 0x052b, 0x052c, 0x052d, 0x052e, 0x052f, 0x0530, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567, 0x0568, 0x0569, 0x056a, 0x056b, 0x056c, 0x056d, 0x056e, 0x056f, 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577, 0x0578, 0x0579, 0x057a, 0x057b, 0x057c, 0x057d, 0x057e, 0x057f, 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0557, 0x0558, 0x0559, 0x055a, 0x055b, 0x055c, 0x055d, 0x055e, 0x055f, 0x0560, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567, 0x0568, 0x0569, 0x056a, 0x056b, 0x056c, 0x056d, 0x056e, 0x056f, 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577, 0x0578, 0x0579, 0x057a, 0x057b, 0x057c, 0x057d, 0x057e, 0x057f, 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0587, 0x0588, 0x0589, 0x058a, 0x058b, 0x058c, 0x058d, 0x058e, 0x058f, 0x0590, 0x0591, 0x0592, 0x0593, 0x0594, 0x0595, 0x0596, 0x0597, 0x0598, 0x0599, 0x059a, 0x059b, 0x059c, 0x059d, 0x059e, 0x059f, 0x05a0, 0x05a1, 0x05a2, 0x05a3, 0x05a4, 0x05a5, 0x05a6, 0x05a7, 0x05a8, 0x05a9, 0x05aa, 0x05ab, 0x05ac, 0x05ad, 0x05ae, 0x05af, 0x05b0, 0x05b1, 0x05b2, 0x05b3, 0x05b4, 0x05b5, 0x05b6, 0x05b7, 0x05b8, 0x05b9, 0x05ba, 0x05bb, 0x05bc, 0x05bd, 0x05be, 0x05bf, 0x05c0, 0x05c1, 0x05c2, 0x05c3, 0x05c4, 0x05c5, 0x05c6, 0x05c7, 0x05c8, 0x05c9, 0x05ca, 0x05cb, 0x05cc, 0x05cd, 0x05ce, 0x05cf, 0x05d0, 0x05d1, 0x05d2, 0x05d3, 0x05d4, 0x05d5, 0x05d6, 0x05d7, 0x05d8, 0x05d9, 0x05da, 0x05db, 0x05dc, 0x05dd, 0x05de, 0x05df, 0x05e0, 0x05e1, 0x05e2, 0x05e3, 0x05e4, 0x05e5, 0x05e6, 0x05e7, 0x05e8, 0x05e9, 0x05ea, 0x05eb, 0x05ec, 0x05ed, 0x05ee, 0x05ef, 0x05f0, 0x05f1, 0x05f2, 0x05f3, 0x05f4, 0x05f5, 0x05f6, 0x05f7, 0x05f8, 0x05f9, 0x05fa, 0x05fb, 0x05fc, 0x05fd, 0x05fe, 0x05ff } }; static const UnicodeCaseTableVector caseTable1e = { { 0x1e01, 0x1e01, 0x1e03, 0x1e03, 0x1e05, 0x1e05, 0x1e07, 0x1e07, 0x1e09, 0x1e09, 0x1e0b, 0x1e0b, 0x1e0d, 0x1e0d, 0x1e0f, 0x1e0f, 0x1e11, 0x1e11, 0x1e13, 0x1e13, 0x1e15, 0x1e15, 0x1e17, 0x1e17, 0x1e19, 0x1e19, 0x1e1b, 0x1e1b, 0x1e1d, 0x1e1d, 0x1e1f, 0x1e1f, 0x1e21, 0x1e21, 0x1e23, 0x1e23, 0x1e25, 0x1e25, 0x1e27, 0x1e27, 0x1e29, 0x1e29, 0x1e2b, 0x1e2b, 0x1e2d, 0x1e2d, 0x1e2f, 0x1e2f, 0x1e31, 0x1e31, 0x1e33, 0x1e33, 0x1e35, 0x1e35, 0x1e37, 0x1e37, 0x1e39, 0x1e39, 0x1e3b, 0x1e3b, 0x1e3d, 0x1e3d, 0x1e3f, 0x1e3f, 0x1e41, 0x1e41, 0x1e43, 0x1e43, 0x1e45, 0x1e45, 0x1e47, 0x1e47, 0x1e49, 0x1e49, 0x1e4b, 0x1e4b, 0x1e4d, 0x1e4d, 0x1e4f, 0x1e4f, 0x1e51, 0x1e51, 0x1e53, 0x1e53, 0x1e55, 0x1e55, 0x1e57, 0x1e57, 0x1e59, 0x1e59, 0x1e5b, 0x1e5b, 0x1e5d, 0x1e5d, 0x1e5f, 0x1e5f, 0x1e61, 0x1e61, 0x1e63, 0x1e63, 0x1e65, 0x1e65, 0x1e67, 0x1e67, 0x1e69, 0x1e69, 0x1e6b, 0x1e6b, 0x1e6d, 0x1e6d, 0x1e6f, 0x1e6f, 0x1e71, 0x1e71, 0x1e73, 0x1e73, 0x1e75, 0x1e75, 0x1e77, 0x1e77, 0x1e79, 0x1e79, 0x1e7b, 0x1e7b, 0x1e7d, 0x1e7d, 0x1e7f, 0x1e7f, 0x1e81, 0x1e81, 0x1e83, 0x1e83, 0x1e85, 0x1e85, 0x1e87, 0x1e87, 0x1e89, 0x1e89, 0x1e8b, 0x1e8b, 0x1e8d, 0x1e8d, 0x1e8f, 0x1e8f, 0x1e91, 0x1e91, 0x1e93, 0x1e93, 0x1e95, 0x1e95, 0x1e96, 0x1e97, 0x1e98, 0x1e99, 0x1e9a, 0x1e61, 0x1e9c, 0x1e9d, 0x1e9e, 0x1e9f, 0x1ea1, 0x1ea1, 0x1ea3, 0x1ea3, 0x1ea5, 0x1ea5, 0x1ea7, 0x1ea7, 0x1ea9, 0x1ea9, 0x1eab, 0x1eab, 0x1ead, 0x1ead, 0x1eaf, 0x1eaf, 0x1eb1, 0x1eb1, 0x1eb3, 0x1eb3, 0x1eb5, 0x1eb5, 0x1eb7, 0x1eb7, 0x1eb9, 0x1eb9, 0x1ebb, 0x1ebb, 0x1ebd, 0x1ebd, 0x1ebf, 0x1ebf, 0x1ec1, 0x1ec1, 0x1ec3, 0x1ec3, 0x1ec5, 0x1ec5, 0x1ec7, 0x1ec7, 0x1ec9, 0x1ec9, 0x1ecb, 0x1ecb, 0x1ecd, 0x1ecd, 0x1ecf, 0x1ecf, 0x1ed1, 0x1ed1, 0x1ed3, 0x1ed3, 0x1ed5, 0x1ed5, 0x1ed7, 0x1ed7, 0x1ed9, 0x1ed9, 0x1edb, 0x1edb, 0x1edd, 0x1edd, 0x1edf, 0x1edf, 0x1ee1, 0x1ee1, 0x1ee3, 0x1ee3, 0x1ee5, 0x1ee5, 0x1ee7, 0x1ee7, 0x1ee9, 0x1ee9, 0x1eeb, 0x1eeb, 0x1eed, 0x1eed, 0x1eef, 0x1eef, 0x1ef1, 0x1ef1, 0x1ef3, 0x1ef3, 0x1ef5, 0x1ef5, 0x1ef7, 0x1ef7, 0x1ef9, 0x1ef9, 0x1efa, 0x1efb, 0x1efc, 0x1efd, 0x1efe, 0x1eff } }; static const UnicodeCaseTableVector caseTable1f = { { 0x1f00, 0x1f01, 0x1f02, 0x1f03, 0x1f04, 0x1f05, 0x1f06, 0x1f07, 0x1f00, 0x1f01, 0x1f02, 0x1f03, 0x1f04, 0x1f05, 0x1f06, 0x1f07, 0x1f10, 0x1f11, 0x1f12, 0x1f13, 0x1f14, 0x1f15, 0x1f16, 0x1f17, 0x1f10, 0x1f11, 0x1f12, 0x1f13, 0x1f14, 0x1f15, 0x1f1e, 0x1f1f, 0x1f20, 0x1f21, 0x1f22, 0x1f23, 0x1f24, 0x1f25, 0x1f26, 0x1f27, 0x1f20, 0x1f21, 0x1f22, 0x1f23, 0x1f24, 0x1f25, 0x1f26, 0x1f27, 0x1f30, 0x1f31, 0x1f32, 0x1f33, 0x1f34, 0x1f35, 0x1f36, 0x1f37, 0x1f30, 0x1f31, 0x1f32, 0x1f33, 0x1f34, 0x1f35, 0x1f36, 0x1f37, 0x1f40, 0x1f41, 0x1f42, 0x1f43, 0x1f44, 0x1f45, 0x1f46, 0x1f47, 0x1f40, 0x1f41, 0x1f42, 0x1f43, 0x1f44, 0x1f45, 0x1f4e, 0x1f4f, 0x1f50, 0x1f51, 0x1f52, 0x1f53, 0x1f54, 0x1f55, 0x1f56, 0x1f57, 0x1f58, 0x1f51, 0x1f5a, 0x1f53, 0x1f5c, 0x1f55, 0x1f5e, 0x1f57, 0x1f60, 0x1f61, 0x1f62, 0x1f63, 0x1f64, 0x1f65, 0x1f66, 0x1f67, 0x1f60, 0x1f61, 0x1f62, 0x1f63, 0x1f64, 0x1f65, 0x1f66, 0x1f67, 0x1f70, 0x1f71, 0x1f72, 0x1f73, 0x1f74, 0x1f75, 0x1f76, 0x1f77, 0x1f78, 0x1f79, 0x1f7a, 0x1f7b, 0x1f7c, 0x1f7d, 0x1f7e, 0x1f7f, 0x1f80, 0x1f81, 0x1f82, 0x1f83, 0x1f84, 0x1f85, 0x1f86, 0x1f87, 0x1f80, 0x1f81, 0x1f82, 0x1f83, 0x1f84, 0x1f85, 0x1f86, 0x1f87, 0x1f90, 0x1f91, 0x1f92, 0x1f93, 0x1f94, 0x1f95, 0x1f96, 0x1f97, 0x1f90, 0x1f91, 0x1f92, 0x1f93, 0x1f94, 0x1f95, 0x1f96, 0x1f97, 0x1fa0, 0x1fa1, 0x1fa2, 0x1fa3, 0x1fa4, 0x1fa5, 0x1fa6, 0x1fa7, 0x1fa0, 0x1fa1, 0x1fa2, 0x1fa3, 0x1fa4, 0x1fa5, 0x1fa6, 0x1fa7, 0x1fb0, 0x1fb1, 0x1fb2, 0x1fb3, 0x1fb4, 0x1fb5, 0x1fb6, 0x1fb7, 0x1fb0, 0x1fb1, 0x1f70, 0x1f71, 0x1fb3, 0x1fbd, 0x03b9, 0x1fbf, 0x1fc0, 0x1fc1, 0x1fc2, 0x1fc3, 0x1fc4, 0x1fc5, 0x1fc6, 0x1fc7, 0x1f72, 0x1f73, 0x1f74, 0x1f75, 0x1fc3, 0x1fcd, 0x1fce, 0x1fcf, 0x1fd0, 0x1fd1, 0x1fd2, 0x1fd3, 0x1fd4, 0x1fd5, 0x1fd6, 0x1fd7, 0x1fd0, 0x1fd1, 0x1f76, 0x1f77, 0x1fdc, 0x1fdd, 0x1fde, 0x1fdf, 0x1fe0, 0x1fe1, 0x1fe2, 0x1fe3, 0x1fe4, 0x1fe5, 0x1fe6, 0x1fe7, 0x1fe0, 0x1fe1, 0x1f7a, 0x1f7b, 0x1fe5, 0x1fed, 0x1fee, 0x1fef, 0x1ff0, 0x1ff1, 0x1ff2, 0x1ff3, 0x1ff4, 0x1ff5, 0x1ff6, 0x1ff7, 0x1f78, 0x1f79, 0x1f7c, 0x1f7d, 0x1ff3, 0x1ffd, 0x1ffe, 0x1fff } }; static const UnicodeCaseTableVector caseTable21 = { { 0x2100, 0x2101, 0x2102, 0x2103, 0x2104, 0x2105, 0x2106, 0x2107, 0x2108, 0x2109, 0x210a, 0x210b, 0x210c, 0x210d, 0x210e, 0x210f, 0x2110, 0x2111, 0x2112, 0x2113, 0x2114, 0x2115, 0x2116, 0x2117, 0x2118, 0x2119, 0x211a, 0x211b, 0x211c, 0x211d, 0x211e, 0x211f, 0x2120, 0x2121, 0x2122, 0x2123, 0x2124, 0x2125, 0x03c9, 0x2127, 0x2128, 0x2129, 0x006b, 0x00e5, 0x212c, 0x212d, 0x212e, 0x212f, 0x2130, 0x2131, 0x2132, 0x2133, 0x2134, 0x2135, 0x2136, 0x2137, 0x2138, 0x2139, 0x213a, 0x213b, 0x213c, 0x213d, 0x213e, 0x213f, 0x2140, 0x2141, 0x2142, 0x2143, 0x2144, 0x2145, 0x2146, 0x2147, 0x2148, 0x2149, 0x214a, 0x214b, 0x214c, 0x214d, 0x214e, 0x214f, 0x2150, 0x2151, 0x2152, 0x2153, 0x2154, 0x2155, 0x2156, 0x2157, 0x2158, 0x2159, 0x215a, 0x215b, 0x215c, 0x215d, 0x215e, 0x215f, 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x217a, 0x217b, 0x217c, 0x217d, 0x217e, 0x217f, 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x217a, 0x217b, 0x217c, 0x217d, 0x217e, 0x217f, 0x2180, 0x2181, 0x2182, 0x2183, 0x2184, 0x2185, 0x2186, 0x2187, 0x2188, 0x2189, 0x218a, 0x218b, 0x218c, 0x218d, 0x218e, 0x218f, 0x2190, 0x2191, 0x2192, 0x2193, 0x2194, 0x2195, 0x2196, 0x2197, 0x2198, 0x2199, 0x219a, 0x219b, 0x219c, 0x219d, 0x219e, 0x219f, 0x21a0, 0x21a1, 0x21a2, 0x21a3, 0x21a4, 0x21a5, 0x21a6, 0x21a7, 0x21a8, 0x21a9, 0x21aa, 0x21ab, 0x21ac, 0x21ad, 0x21ae, 0x21af, 0x21b0, 0x21b1, 0x21b2, 0x21b3, 0x21b4, 0x21b5, 0x21b6, 0x21b7, 0x21b8, 0x21b9, 0x21ba, 0x21bb, 0x21bc, 0x21bd, 0x21be, 0x21bf, 0x21c0, 0x21c1, 0x21c2, 0x21c3, 0x21c4, 0x21c5, 0x21c6, 0x21c7, 0x21c8, 0x21c9, 0x21ca, 0x21cb, 0x21cc, 0x21cd, 0x21ce, 0x21cf, 0x21d0, 0x21d1, 0x21d2, 0x21d3, 0x21d4, 0x21d5, 0x21d6, 0x21d7, 0x21d8, 0x21d9, 0x21da, 0x21db, 0x21dc, 0x21dd, 0x21de, 0x21df, 0x21e0, 0x21e1, 0x21e2, 0x21e3, 0x21e4, 0x21e5, 0x21e6, 0x21e7, 0x21e8, 0x21e9, 0x21ea, 0x21eb, 0x21ec, 0x21ed, 0x21ee, 0x21ef, 0x21f0, 0x21f1, 0x21f2, 0x21f3, 0x21f4, 0x21f5, 0x21f6, 0x21f7, 0x21f8, 0x21f9, 0x21fa, 0x21fb, 0x21fc, 0x21fd, 0x21fe, 0x21ff } }; static const UnicodeCaseTableVector caseTable24 = { { 0x2400, 0x2401, 0x2402, 0x2403, 0x2404, 0x2405, 0x2406, 0x2407, 0x2408, 0x2409, 0x240a, 0x240b, 0x240c, 0x240d, 0x240e, 0x240f, 0x2410, 0x2411, 0x2412, 0x2413, 0x2414, 0x2415, 0x2416, 0x2417, 0x2418, 0x2419, 0x241a, 0x241b, 0x241c, 0x241d, 0x241e, 0x241f, 0x2420, 0x2421, 0x2422, 0x2423, 0x2424, 0x2425, 0x2426, 0x2427, 0x2428, 0x2429, 0x242a, 0x242b, 0x242c, 0x242d, 0x242e, 0x242f, 0x2430, 0x2431, 0x2432, 0x2433, 0x2434, 0x2435, 0x2436, 0x2437, 0x2438, 0x2439, 0x243a, 0x243b, 0x243c, 0x243d, 0x243e, 0x243f, 0x2440, 0x2441, 0x2442, 0x2443, 0x2444, 0x2445, 0x2446, 0x2447, 0x2448, 0x2449, 0x244a, 0x244b, 0x244c, 0x244d, 0x244e, 0x244f, 0x2450, 0x2451, 0x2452, 0x2453, 0x2454, 0x2455, 0x2456, 0x2457, 0x2458, 0x2459, 0x245a, 0x245b, 0x245c, 0x245d, 0x245e, 0x245f, 0x2460, 0x2461, 0x2462, 0x2463, 0x2464, 0x2465, 0x2466, 0x2467, 0x2468, 0x2469, 0x246a, 0x246b, 0x246c, 0x246d, 0x246e, 0x246f, 0x2470, 0x2471, 0x2472, 0x2473, 0x2474, 0x2475, 0x2476, 0x2477, 0x2478, 0x2479, 0x247a, 0x247b, 0x247c, 0x247d, 0x247e, 0x247f, 0x2480, 0x2481, 0x2482, 0x2483, 0x2484, 0x2485, 0x2486, 0x2487, 0x2488, 0x2489, 0x248a, 0x248b, 0x248c, 0x248d, 0x248e, 0x248f, 0x2490, 0x2491, 0x2492, 0x2493, 0x2494, 0x2495, 0x2496, 0x2497, 0x2498, 0x2499, 0x249a, 0x249b, 0x249c, 0x249d, 0x249e, 0x249f, 0x24a0, 0x24a1, 0x24a2, 0x24a3, 0x24a4, 0x24a5, 0x24a6, 0x24a7, 0x24a8, 0x24a9, 0x24aa, 0x24ab, 0x24ac, 0x24ad, 0x24ae, 0x24af, 0x24b0, 0x24b1, 0x24b2, 0x24b3, 0x24b4, 0x24b5, 0x24d0, 0x24d1, 0x24d2, 0x24d3, 0x24d4, 0x24d5, 0x24d6, 0x24d7, 0x24d8, 0x24d9, 0x24da, 0x24db, 0x24dc, 0x24dd, 0x24de, 0x24df, 0x24e0, 0x24e1, 0x24e2, 0x24e3, 0x24e4, 0x24e5, 0x24e6, 0x24e7, 0x24e8, 0x24e9, 0x24d0, 0x24d1, 0x24d2, 0x24d3, 0x24d4, 0x24d5, 0x24d6, 0x24d7, 0x24d8, 0x24d9, 0x24da, 0x24db, 0x24dc, 0x24dd, 0x24de, 0x24df, 0x24e0, 0x24e1, 0x24e2, 0x24e3, 0x24e4, 0x24e5, 0x24e6, 0x24e7, 0x24e8, 0x24e9, 0x24ea, 0x24eb, 0x24ec, 0x24ed, 0x24ee, 0x24ef, 0x24f0, 0x24f1, 0x24f2, 0x24f3, 0x24f4, 0x24f5, 0x24f6, 0x24f7, 0x24f8, 0x24f9, 0x24fa, 0x24fb, 0x24fc, 0x24fd, 0x24fe, 0x24ff } }; static const UnicodeCaseTableVector caseTableff = { { 0xff00, 0xff01, 0xff02, 0xff03, 0xff04, 0xff05, 0xff06, 0xff07, 0xff08, 0xff09, 0xff0a, 0xff0b, 0xff0c, 0xff0d, 0xff0e, 0xff0f, 0xff10, 0xff11, 0xff12, 0xff13, 0xff14, 0xff15, 0xff16, 0xff17, 0xff18, 0xff19, 0xff1a, 0xff1b, 0xff1c, 0xff1d, 0xff1e, 0xff1f, 0xff20, 0xff41, 0xff42, 0xff43, 0xff44, 0xff45, 0xff46, 0xff47, 0xff48, 0xff49, 0xff4a, 0xff4b, 0xff4c, 0xff4d, 0xff4e, 0xff4f, 0xff50, 0xff51, 0xff52, 0xff53, 0xff54, 0xff55, 0xff56, 0xff57, 0xff58, 0xff59, 0xff5a, 0xff3b, 0xff3c, 0xff3d, 0xff3e, 0xff3f, 0xff40, 0xff41, 0xff42, 0xff43, 0xff44, 0xff45, 0xff46, 0xff47, 0xff48, 0xff49, 0xff4a, 0xff4b, 0xff4c, 0xff4d, 0xff4e, 0xff4f, 0xff50, 0xff51, 0xff52, 0xff53, 0xff54, 0xff55, 0xff56, 0xff57, 0xff58, 0xff59, 0xff5a, 0xff5b, 0xff5c, 0xff5d, 0xff5e, 0xff5f, 0xff60, 0xff61, 0xff62, 0xff63, 0xff64, 0xff65, 0xff66, 0xff67, 0xff68, 0xff69, 0xff6a, 0xff6b, 0xff6c, 0xff6d, 0xff6e, 0xff6f, 0xff70, 0xff71, 0xff72, 0xff73, 0xff74, 0xff75, 0xff76, 0xff77, 0xff78, 0xff79, 0xff7a, 0xff7b, 0xff7c, 0xff7d, 0xff7e, 0xff7f, 0xff80, 0xff81, 0xff82, 0xff83, 0xff84, 0xff85, 0xff86, 0xff87, 0xff88, 0xff89, 0xff8a, 0xff8b, 0xff8c, 0xff8d, 0xff8e, 0xff8f, 0xff90, 0xff91, 0xff92, 0xff93, 0xff94, 0xff95, 0xff96, 0xff97, 0xff98, 0xff99, 0xff9a, 0xff9b, 0xff9c, 0xff9d, 0xff9e, 0xff9f, 0xffa0, 0xffa1, 0xffa2, 0xffa3, 0xffa4, 0xffa5, 0xffa6, 0xffa7, 0xffa8, 0xffa9, 0xffaa, 0xffab, 0xffac, 0xffad, 0xffae, 0xffaf, 0xffb0, 0xffb1, 0xffb2, 0xffb3, 0xffb4, 0xffb5, 0xffb6, 0xffb7, 0xffb8, 0xffb9, 0xffba, 0xffbb, 0xffbc, 0xffbd, 0xffbe, 0xffbf, 0xffc0, 0xffc1, 0xffc2, 0xffc3, 0xffc4, 0xffc5, 0xffc6, 0xffc7, 0xffc8, 0xffc9, 0xffca, 0xffcb, 0xffcc, 0xffcd, 0xffce, 0xffcf, 0xffd0, 0xffd1, 0xffd2, 0xffd3, 0xffd4, 0xffd5, 0xffd6, 0xffd7, 0xffd8, 0xffd9, 0xffda, 0xffdb, 0xffdc, 0xffdd, 0xffde, 0xffdf, 0xffe0, 0xffe1, 0xffe2, 0xffe3, 0xffe4, 0xffe5, 0xffe6, 0xffe7, 0xffe8, 0xffe9, 0xffea, 0xffeb, 0xffec, 0xffed, 0xffee, 0xffef, 0xfff0, 0xfff1, 0xfff2, 0xfff3, 0xfff4, 0xfff5, 0xfff6, 0xfff7, 0xfff8, 0xfff9, 0xfffa, 0xfffb, 0xfffc, 0xfffd, 0xfffe, 0xffff } }; static const UnicodeCaseTableVector caseTable104 = { { 0x10400, 0x10401, 0x10402, 0x10403, 0x10404, 0x10405, 0x10406, 0x10407, 0x10408, 0x10409, 0x1040a, 0x1040b, 0x1040c, 0x1040d, 0x1040e, 0x1040f, 0x10410, 0x10411, 0x10412, 0x10413, 0x10414, 0x10415, 0x10416, 0x10417, 0x10418, 0x10419, 0x1041a, 0x1041b, 0x1041c, 0x1041d, 0x1041e, 0x1041f, 0x10420, 0x10421, 0x10422, 0x10423, 0x10424, 0x10425, 0x10426, 0x10427, 0x10400, 0x10401, 0x10402, 0x10403, 0x10404, 0x10405, 0x10406, 0x10407, 0x10408, 0x10409, 0x1040a, 0x1040b, 0x1040c, 0x1040d, 0x1040e, 0x1040f, 0x10410, 0x10411, 0x10412, 0x10413, 0x10414, 0x10415, 0x10416, 0x10417, 0x10418, 0x10419, 0x1041a, 0x1041b, 0x1041c, 0x1041d, 0x1041e, 0x1041f, 0x10420, 0x10421, 0x10422, 0x10423, 0x10424, 0x10425, 0x10426, 0x10427, 0x10450, 0x10451, 0x10452, 0x10453, 0x10454, 0x10455, 0x10456, 0x10457, 0x10458, 0x10459, 0x1045a, 0x1045b, 0x1045c, 0x1045d, 0x1045e, 0x1045f, 0x10460, 0x10461, 0x10462, 0x10463, 0x10464, 0x10465, 0x10466, 0x10467, 0x10468, 0x10469, 0x1046a, 0x1046b, 0x1046c, 0x1046d, 0x1046e, 0x1046f, 0x10470, 0x10471, 0x10472, 0x10473, 0x10474, 0x10475, 0x10476, 0x10477, 0x10478, 0x10479, 0x1047a, 0x1047b, 0x1047c, 0x1047d, 0x1047e, 0x1047f, 0x10480, 0x10481, 0x10482, 0x10483, 0x10484, 0x10485, 0x10486, 0x10487, 0x10488, 0x10489, 0x1048a, 0x1048b, 0x1048c, 0x1048d, 0x1048e, 0x1048f, 0x10490, 0x10491, 0x10492, 0x10493, 0x10494, 0x10495, 0x10496, 0x10497, 0x10498, 0x10499, 0x1049a, 0x1049b, 0x1049c, 0x1049d, 0x1049e, 0x1049f, 0x104a0, 0x104a1, 0x104a2, 0x104a3, 0x104a4, 0x104a5, 0x104a6, 0x104a7, 0x104a8, 0x104a9, 0x104aa, 0x104ab, 0x104ac, 0x104ad, 0x104ae, 0x104af, 0x104b0, 0x104b1, 0x104b2, 0x104b3, 0x104b4, 0x104b5, 0x104b6, 0x104b7, 0x104b8, 0x104b9, 0x104ba, 0x104bb, 0x104bc, 0x104bd, 0x104be, 0x104bf, 0x104c0, 0x104c1, 0x104c2, 0x104c3, 0x104c4, 0x104c5, 0x104c6, 0x104c7, 0x104c8, 0x104c9, 0x104ca, 0x104cb, 0x104cc, 0x104cd, 0x104ce, 0x104cf, 0x104d0, 0x104d1, 0x104d2, 0x104d3, 0x104d4, 0x104d5, 0x104d6, 0x104d7, 0x104b0, 0x104b1, 0x104b2, 0x104b3, 0x104b4, 0x104b5, 0x104b6, 0x104b7, 0x104b8, 0x104b9, 0x104ba, 0x104bb, 0x104bc, 0x104bd, 0x104be, 0x104bf, 0x104c0, 0x104c1, 0x104c2, 0x104c3, 0x104c4, 0x104c5, 0x104c6, 0x104c7, 0x104c8, 0x104c9, 0x104ca, 0x104cb, 0x104cc, 0x104cd, 0x104ce, 0x104cf, 0x104d0, 0x104d1, 0x104d2, 0x104d3, 0x104fc, 0x104fd, 0x104fe, 0x104ff, } }; static const UnicodeCaseTableVector caseTable10c = { { 0x10c00, 0x10c01, 0x10c02, 0x10c03, 0x10c04, 0x10c05, 0x10c06, 0x10c07, 0x10c08, 0x10c09, 0x10c0a, 0x10c0b, 0x10c0c, 0x10c0d, 0x10c0e, 0x10c0f, 0x10c10, 0x10c11, 0x10c12, 0x10c13, 0x10c14, 0x10c15, 0x10c16, 0x10c17, 0x10c18, 0x10c19, 0x10c1a, 0x10c1b, 0x10c1c, 0x10c1d, 0x10c1e, 0x10c1f, 0x10c20, 0x10c21, 0x10c22, 0x10c23, 0x10c24, 0x10c25, 0x10c26, 0x10c27, 0x10c28, 0x10c29, 0x10c2a, 0x10c2b, 0x10c2c, 0x10c2d, 0x10c2e, 0x10c2f, 0x10c30, 0x10c31, 0x10c32, 0x10c33, 0x10c34, 0x10c35, 0x10c36, 0x10c37, 0x10c38, 0x10c39, 0x10c3a, 0x10c3b, 0x10c3c, 0x10c3d, 0x10c3e, 0x10c3f, 0x10c40, 0x10c41, 0x10c42, 0x10c43, 0x10c44, 0x10c45, 0x10c46, 0x10c47, 0x10c48, 0x10c49, 0x10c4a, 0x10c4b, 0x10c4c, 0x10c4d, 0x10c4e, 0x10c4f, 0x10c50, 0x10c51, 0x10c52, 0x10c53, 0x10c54, 0x10c55, 0x10c56, 0x10c57, 0x10c58, 0x10c59, 0x10c5a, 0x10c5b, 0x10c5c, 0x10c5d, 0x10c5e, 0x10c5f, 0x10c60, 0x10c61, 0x10c62, 0x10c63, 0x10c64, 0x10c65, 0x10c66, 0x10c67, 0x10c68, 0x10c69, 0x10c6a, 0x10c6b, 0x10c6c, 0x10c6d, 0x10c6e, 0x10c6f, 0x10c70, 0x10c71, 0x10c72, 0x10c73, 0x10c74, 0x10c75, 0x10c76, 0x10c77, 0x10c78, 0x10c79, 0x10c7a, 0x10c7b, 0x10c7c, 0x10c7d, 0x10c7e, 0x10c7f, 0x10c80, 0x10c81, 0x10c82, 0x10c83, 0x10c84, 0x10c85, 0x10c86, 0x10c87, 0x10c88, 0x10c89, 0x10c8a, 0x10c8b, 0x10c8c, 0x10c8d, 0x10c8e, 0x10c8f, 0x10c90, 0x10c91, 0x10c92, 0x10c93, 0x10c94, 0x10c95, 0x10c96, 0x10c97, 0x10c98, 0x10c99, 0x10c9a, 0x10c9b, 0x10c9c, 0x10c9d, 0x10c9e, 0x10c9f, 0x10ca0, 0x10ca1, 0x10ca2, 0x10ca3, 0x10ca4, 0x10ca5, 0x10ca6, 0x10ca7, 0x10ca8, 0x10ca9, 0x10caa, 0x10cab, 0x10cac, 0x10cad, 0x10cae, 0x10caf, 0x10cb0, 0x10cb1, 0x10cb2, 0x10cb3, 0x10cb4, 0x10cb5, 0x10cb6, 0x10cb7, 0x10cb8, 0x10cb9, 0x10cba, 0x10cbb, 0x10cbc, 0x10cbd, 0x10cbe, 0x10cbf, 0x10c80, 0x10c81, 0x10c82, 0x10c83, 0x10c84, 0x10c85, 0x10c86, 0x10c87, 0x10c88, 0x10c89, 0x10c8a, 0x10c8b, 0x10c8c, 0x10c8d, 0x10c8e, 0x10c8f, 0x10c90, 0x10c91, 0x10c92, 0x10c93, 0x10c94, 0x10c95, 0x10c96, 0x10c97, 0x10c98, 0x10c99, 0x10c9a, 0x10c9b, 0x10c9c, 0x10c9d, 0x10c9e, 0x10c9f, 0x10ca0, 0x10ca1, 0x10ca2, 0x10ca3, 0x10ca4, 0x10ca5, 0x10ca6, 0x10ca7, 0x10ca8, 0x10ca9, 0x10caa, 0x10cab, 0x10cac, 0x10cad, 0x10cae, 0x10caf, 0x10cb0, 0x10cb1, 0x10cb2, 0x10cf3, 0x10cf4, 0x10cf5, 0x10cf6, 0x10cf7, } }; static const UnicodeCaseTableVector caseTable118 = { { 0x11800, 0x11801, 0x11802, 0x11803, 0x11804, 0x11805, 0x11806, 0x11807, 0x11808, 0x11809, 0x1180a, 0x1180b, 0x1180c, 0x1180d, 0x1180e, 0x1180f, 0x11810, 0x11811, 0x11812, 0x11813, 0x11814, 0x11815, 0x11816, 0x11817, 0x11818, 0x11819, 0x1181a, 0x1181b, 0x1181c, 0x1181d, 0x1181e, 0x1181f, 0x11820, 0x11821, 0x11822, 0x11823, 0x11824, 0x11825, 0x11826, 0x11827, 0x11828, 0x11829, 0x1182a, 0x1182b, 0x1182c, 0x1182d, 0x1182e, 0x1182f, 0x11830, 0x11831, 0x11832, 0x11833, 0x11834, 0x11835, 0x11836, 0x11837, 0x11838, 0x11839, 0x1183a, 0x1183b, 0x1183c, 0x1183d, 0x1183e, 0x1183f, 0x11840, 0x11841, 0x11842, 0x11843, 0x11844, 0x11845, 0x11846, 0x11847, 0x11848, 0x11849, 0x1184a, 0x1184b, 0x1184c, 0x1184d, 0x1184e, 0x1184f, 0x11850, 0x11851, 0x11852, 0x11853, 0x11854, 0x11855, 0x11856, 0x11857, 0x11858, 0x11859, 0x1185a, 0x1185b, 0x1185c, 0x1185d, 0x1185e, 0x1185f, 0x11860, 0x11861, 0x11862, 0x11863, 0x11864, 0x11865, 0x11866, 0x11867, 0x11868, 0x11869, 0x1186a, 0x1186b, 0x1186c, 0x1186d, 0x1186e, 0x1186f, 0x11870, 0x11871, 0x11872, 0x11873, 0x11874, 0x11875, 0x11876, 0x11877, 0x11878, 0x11879, 0x1187a, 0x1187b, 0x1187c, 0x1187d, 0x1187e, 0x1187f, 0x11880, 0x11881, 0x11882, 0x11883, 0x11884, 0x11885, 0x11886, 0x11887, 0x11888, 0x11889, 0x1188a, 0x1188b, 0x1188c, 0x1188d, 0x1188e, 0x1188f, 0x11890, 0x11891, 0x11892, 0x11893, 0x11894, 0x11895, 0x11896, 0x11897, 0x11898, 0x11899, 0x1189a, 0x1189b, 0x1189c, 0x1189d, 0x1189e, 0x1189f, 0x118a0, 0x118a1, 0x118a2, 0x118a3, 0x118a4, 0x118a5, 0x118a6, 0x118a7, 0x118a8, 0x118a9, 0x118aa, 0x118ab, 0x118ac, 0x118ad, 0x118ae, 0x118af, 0x118b0, 0x118b1, 0x118b2, 0x118b3, 0x118b4, 0x118b5, 0x118b6, 0x118b7, 0x118b8, 0x118b9, 0x118ba, 0x118bb, 0x118bc, 0x118bd, 0x118be, 0x118bf, 0x118a0, 0x118a1, 0x118a2, 0x118a3, 0x118a4, 0x118a5, 0x118a6, 0x118a7, 0x118a8, 0x118a9, 0x118aa, 0x118ab, 0x118ac, 0x118ad, 0x118ae, 0x118af, 0x118b0, 0x118b1, 0x118b2, 0x118b3, 0x118b4, 0x118b5, 0x118b6, 0x118b7, 0x118b8, 0x118b9, 0x118ba, 0x118bb, 0x118bc, 0x118bd, 0x118be, 0x118bf, 0x118e0, 0x118e1, 0x118e2, 0x118e3, 0x118e4, 0x118e5, 0x118e6, 0x118e7, 0x118e8, 0x118e9, 0x118ea, 0x118eb, 0x118ec, 0x118ed, 0x118ee, 0x118ef, 0x118f0, 0x118f1, 0x118f2, 0x118f3, 0x118f4, 0x118f5, 0x118f6, 0x118f7, } }; static const UnicodeCaseTableVector caseTable16e = { { 0x16e00, 0x16e01, 0x16e02, 0x16e03, 0x16e04, 0x16e05, 0x16e06, 0x16e07, 0x16e08, 0x16e09, 0x16e0a, 0x16e0b, 0x16e0c, 0x16e0d, 0x16e0e, 0x16e0f, 0x16e10, 0x16e11, 0x16e12, 0x16e13, 0x16e14, 0x16e15, 0x16e16, 0x16e17, 0x16e18, 0x16e19, 0x16e1a, 0x16e1b, 0x16e1c, 0x16e1d, 0x16e1e, 0x16e1f, 0x16e20, 0x16e21, 0x16e22, 0x16e23, 0x16e24, 0x16e25, 0x16e26, 0x16e27, 0x16e28, 0x16e29, 0x16e2a, 0x16e2b, 0x16e2c, 0x16e2d, 0x16e2e, 0x16e2f, 0x16e30, 0x16e31, 0x16e32, 0x16e33, 0x16e34, 0x16e35, 0x16e36, 0x16e37, 0x16e38, 0x16e39, 0x16e3a, 0x16e3b, 0x16e3c, 0x16e3d, 0x16e3e, 0x16e3f, 0x16e40, 0x16e41, 0x16e42, 0x16e43, 0x16e44, 0x16e45, 0x16e46, 0x16e47, 0x16e48, 0x16e49, 0x16e4a, 0x16e4b, 0x16e4c, 0x16e4d, 0x16e4e, 0x16e4f, 0x16e50, 0x16e51, 0x16e52, 0x16e53, 0x16e54, 0x16e55, 0x16e56, 0x16e57, 0x16e58, 0x16e59, 0x16e5a, 0x16e5b, 0x16e5c, 0x16e5d, 0x16e5e, 0x16e5f, 0x16e40, 0x16e41, 0x16e42, 0x16e43, 0x16e44, 0x16e45, 0x16e46, 0x16e47, 0x16e48, 0x16e49, 0x16e4a, 0x16e4b, 0x16e4c, 0x16e4d, 0x16e4e, 0x16e4f, 0x16e50, 0x16e51, 0x16e52, 0x16e53, 0x16e54, 0x16e55, 0x16e56, 0x16e57, 0x16e58, 0x16e59, 0x16e5a, 0x16e5b, 0x16e5c, 0x16e5d, 0x16e5e, 0x16e5f, 0x16e80, 0x16e81, 0x16e82, 0x16e83, 0x16e84, 0x16e85, 0x16e86, 0x16e87, 0x16e88, 0x16e89, 0x16e8a, 0x16e8b, 0x16e8c, 0x16e8d, 0x16e8e, 0x16e8f, 0x16e90, 0x16e91, 0x16e92, 0x16e93, 0x16e94, 0x16e95, 0x16e96, 0x16e97, 0x16e98, 0x16e99, 0x16e9a, 0x16e9b, 0x16e9c, 0x16e9d, 0x16e9e, 0x16e9f, 0x16ea0, 0x16ea1, 0x16ea2, 0x16ea3, 0x16ea4, 0x16ea5, 0x16ea6, 0x16ea7, 0x16ea8, 0x16ea9, 0x16eaa, 0x16eab, 0x16eac, 0x16ead, 0x16eae, 0x16eaf, 0x16eb0, 0x16eb1, 0x16eb2, 0x16eb3, 0x16eb4, 0x16eb5, 0x16eb6, 0x16eb7, 0x16eb8, 0x16eb9, 0x16eba, 0x16ebb, 0x16ebc, 0x16ebd, 0x16ebe, 0x16ebf, 0x16ec0, 0x16ec1, 0x16ec2, 0x16ec3, 0x16ec4, 0x16ec5, 0x16ec6, 0x16ec7, 0x16ec8, 0x16ec9, 0x16eca, 0x16ecb, 0x16ecc, 0x16ecd, 0x16ece, 0x16ecf, 0x16ed0, 0x16ed1, 0x16ed2, 0x16ed3, 0x16ed4, 0x16ed5, 0x16ed6, 0x16ed7, 0x16ed8, 0x16ed9, 0x16eda, 0x16edb, 0x16edc, 0x16edd, 0x16ede, 0x16edf, 0x16ee0, 0x16ee1, 0x16ee2, 0x16ee3, 0x16ee4, 0x16ee5, 0x16ee6, 0x16ee7, 0x16ee8, 0x16ee9, 0x16eea, 0x16eeb, 0x16eec, 0x16eed, 0x16eee, 0x16eef, 0x16ef0, 0x16ef1, 0x16ef2, 0x16ef3, 0x16ef4, 0x16ef5, 0x16ef6, 0x16ef7, } }; static const UnicodeCaseTableVector caseTable1e9 = { { 0x1e900, 0x1e901, 0x1e902, 0x1e903, 0x1e904, 0x1e905, 0x1e906, 0x1e907, 0x1e908, 0x1e909, 0x1e90a, 0x1e90b, 0x1e90c, 0x1e90d, 0x1e90e, 0x1e90f, 0x1e910, 0x1e911, 0x1e912, 0x1e913, 0x1e914, 0x1e915, 0x1e916, 0x1e917, 0x1e918, 0x1e919, 0x1e91a, 0x1e91b, 0x1e91c, 0x1e91d, 0x1e91e, 0x1e91f, 0x1e920, 0x1e921, 0x1e900, 0x1e901, 0x1e902, 0x1e903, 0x1e904, 0x1e905, 0x1e906, 0x1e907, 0x1e908, 0x1e909, 0x1e90a, 0x1e90b, 0x1e90c, 0x1e90d, 0x1e90e, 0x1e90f, 0x1e910, 0x1e911, 0x1e912, 0x1e913, 0x1e914, 0x1e915, 0x1e916, 0x1e917, 0x1e918, 0x1e919, 0x1e91a, 0x1e91b, 0x1e91c, 0x1e91d, 0x1e91e, 0x1e91f, 0x1e920, 0x1e921, 0x1e944, 0x1e945, 0x1e946, 0x1e947, 0x1e948, 0x1e949, 0x1e94a, 0x1e94b, 0x1e94c, 0x1e94d, 0x1e94e, 0x1e94f, 0x1e950, 0x1e951, 0x1e952, 0x1e953, 0x1e954, 0x1e955, 0x1e956, 0x1e957, 0x1e958, 0x1e959, 0x1e95a, 0x1e95b, 0x1e95c, 0x1e95d, 0x1e95e, 0x1e95f, 0x1e960, 0x1e961, 0x1e962, 0x1e963, 0x1e964, 0x1e965, 0x1e966, 0x1e967, 0x1e968, 0x1e969, 0x1e96a, 0x1e96b, 0x1e96c, 0x1e96d, 0x1e96e, 0x1e96f, 0x1e970, 0x1e971, 0x1e972, 0x1e973, 0x1e974, 0x1e975, 0x1e976, 0x1e977, 0x1e978, 0x1e979, 0x1e97a, 0x1e97b, 0x1e97c, 0x1e97d, 0x1e97e, 0x1e97f, 0x1e980, 0x1e981, 0x1e982, 0x1e983, 0x1e984, 0x1e985, 0x1e986, 0x1e987, 0x1e988, 0x1e989, 0x1e98a, 0x1e98b, 0x1e98c, 0x1e98d, 0x1e98e, 0x1e98f, 0x1e990, 0x1e991, 0x1e992, 0x1e993, 0x1e994, 0x1e995, 0x1e996, 0x1e997, 0x1e998, 0x1e999, 0x1e99a, 0x1e99b, 0x1e99c, 0x1e99d, 0x1e99e, 0x1e99f, 0x1e9a0, 0x1e9a1, 0x1e9a2, 0x1e9a3, 0x1e9a4, 0x1e9a5, 0x1e9a6, 0x1e9a7, 0x1e9a8, 0x1e9a9, 0x1e9aa, 0x1e9ab, 0x1e9ac, 0x1e9ad, 0x1e9ae, 0x1e9af, 0x1e9b0, 0x1e9b1, 0x1e9b2, 0x1e9b3, 0x1e9b4, 0x1e9b5, 0x1e9b6, 0x1e9b7, 0x1e9b8, 0x1e9b9, 0x1e9ba, 0x1e9bb, 0x1e9bc, 0x1e9bd, 0x1e9be, 0x1e9bf, 0x1e9c0, 0x1e9c1, 0x1e9c2, 0x1e9c3, 0x1e9c4, 0x1e9c5, 0x1e9c6, 0x1e9c7, 0x1e9c8, 0x1e9c9, 0x1e9ca, 0x1e9cb, 0x1e9cc, 0x1e9cd, 0x1e9ce, 0x1e9cf, 0x1e9d0, 0x1e9d1, 0x1e9d2, 0x1e9d3, 0x1e9d4, 0x1e9d5, 0x1e9d6, 0x1e9d7, 0x1e9d8, 0x1e9d9, 0x1e9da, 0x1e9db, 0x1e9dc, 0x1e9dd, 0x1e9de, 0x1e9df, 0x1e9e0, 0x1e9e1, 0x1e9e2, 0x1e9e3, 0x1e9e4, 0x1e9e5, 0x1e9e6, 0x1e9e7, 0x1e9e8, 0x1e9e9, 0x1e9ea, 0x1e9eb, 0x1e9ec, 0x1e9ed, 0x1e9ee, 0x1e9ef, 0x1e9f0, 0x1e9f1, 0x1e9f2, 0x1e9f3, 0x1e9f4, 0x1e9f5, 0x1e9f6, 0x1e9f7, } }; static const UnicodeCaseTableVector *caseTable[] = { &caseTable00, &caseTable01, &caseTable02, &caseTable03, &caseTable04, &caseTable05, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &caseTable1e, &caseTable1f, nullptr, &caseTable21, nullptr, nullptr, &caseTable24, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &caseTableff, nullptr, nullptr, nullptr, nullptr, &caseTable104, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &caseTable10c, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &caseTable118, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &caseTable16e, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &caseTable1e9, }; static inline char getType(Unicode c) { int i; char type; if (c > 0xffff) { type = 'X'; } else { i = (c >> 8) & 0xff; if ((type = typeTable[i].type) == 'X') { type = typeTable[i].vector[c & 0xff]; } } return type; } bool unicodeTypeL(Unicode c) { return getType(c) == 'L'; } bool unicodeTypeR(Unicode c) { return getType(c) == 'R'; } bool unicodeTypeNum(Unicode c) { return getType(c) == '#'; } bool unicodeTypeAlphaNum(Unicode c) { char t; t = getType(c); return t == 'L' || t == 'R' || t == '#'; } #define UNICODE_ALPHABETIC_PRESENTATION_BLOCK_BEGIN 0xFB00 #define UNICODE_ALPHABETIC_PRESENTATION_BLOCK_END 0xFB4F bool unicodeIsAlphabeticPresentationForm(Unicode c) { return c >= UNICODE_ALPHABETIC_PRESENTATION_BLOCK_BEGIN && c <= UNICODE_ALPHABETIC_PRESENTATION_BLOCK_END; } Unicode unicodeToUpper(Unicode c) { size_t i = (c >> 8); if (i < sizeof(caseTable) / sizeof(caseTable[0]) && caseTable[i]) { return caseTable[i]->codes[c & 0xff]; } 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) #define UNICODE_PART2_START 0xE0000 #include "UnicodeCClassTables.h" #include "UnicodeCompTables.h" #include "UnicodeDecompTables.h" #define CC_PART1(Page, Char) ((combining_class_table_part1[Page] >= UNICODE_MAX_TABLE_INDEX) ? (combining_class_table_part1[Page] - UNICODE_MAX_TABLE_INDEX) : (cclass_data[combining_class_table_part1[Page]][Char])) #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)) // 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 // decomposition is returned but nothing is written. If @u is its own // decomposition, write @u into @buf and return 1. // If reverseRTL is true, then decompositions of RTL characters will be output // in reverse order. static int decomp_compat(Unicode u, Unicode *buf, bool reverseRTL = false) { // decomposition tables stored as lists {character, decomp_length, offset} // so we do a binary search int start = 0, end = DECOMP_TABLE_LENGTH; if (u >= decomp_table[start].character && u <= decomp_table[end - 1].character) { while (true) { int midpoint = (start + end) / 2; if (u == decomp_table[midpoint].character) { int offset = decomp_table[midpoint].offset; if (offset == -1) { break; } else { int length = decomp_table[midpoint].length, i; if (buf) { for (i = 0; i < length; ++i) { if (unicodeTypeR(u) && reverseRTL) { buf[i] = decomp_expansion[offset + length - i - 1]; } else { buf[i] = decomp_expansion[offset + i]; } } } return length; } } else if (midpoint == start) { break; } else if (u > decomp_table[midpoint].character) { start = midpoint; } else { end = midpoint; } } } if (buf) { *buf = u; } return 1; } #define CI(Page, Char) ((compose_table[Page] >= UNICODE_MAX_TABLE_INDEX) ? (compose_table[Page] - UNICODE_MAX_TABLE_INDEX) : (compose_data[compose_table[Page]][Char])) #define COMPOSE_INDEX(u) ((((u) / 256) > (COMPOSE_TABLE_LAST)) ? 0 : CI((u) / 256, (u) % 256)) // If @add combines with @base, write the combination to @out and return // true. Otherwise return false. static bool combine(Unicode base, Unicode add, Unicode *out) { unsigned short idx_base, idx_add; idx_base = COMPOSE_INDEX(base); if (idx_base >= COMPOSE_FIRST_SINGLE_START && idx_base < COMPOSE_SECOND_START) { if (compose_first_single[idx_base - COMPOSE_FIRST_SINGLE_START][0] == add) { *out = compose_first_single[idx_base - COMPOSE_FIRST_SINGLE_START][1]; return true; } else { return false; } } idx_add = COMPOSE_INDEX(add); if (idx_add >= COMPOSE_SECOND_SINGLE_START) { if (compose_second_single[idx_add - COMPOSE_SECOND_SINGLE_START][0] == base) { *out = compose_second_single[idx_add - COMPOSE_SECOND_SINGLE_START][1]; return true; } else { return false; } } if (idx_base >= COMPOSE_FIRST_START && idx_base < COMPOSE_FIRST_SINGLE_START && idx_add >= COMPOSE_SECOND_START && idx_add < COMPOSE_SECOND_SINGLE_START) { Unicode o = compose_array[idx_base - COMPOSE_FIRST_START][idx_add - COMPOSE_SECOND_START]; if (o) { *out = o; return true; } } return false; } #define HANGUL_S_BASE 0xAC00 #define HANGUL_L_BASE 0x1100 #define HANGUL_V_BASE 0x1161 #define HANGUL_T_BASE 0x11A7 #define HANGUL_L_COUNT 19 #define HANGUL_V_COUNT 21 #define HANGUL_T_COUNT 28 #define HANGUL_S_COUNT (HANGUL_L_COUNT * HANGUL_V_COUNT * HANGUL_T_COUNT) #define HANGUL_N_COUNT (HANGUL_V_COUNT * HANGUL_T_COUNT) #define HANGUL_IS_L(u) (((u) >= HANGUL_L_BASE) && ((u) < HANGUL_L_BASE + HANGUL_L_COUNT)) #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 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)) // Converts Unicode string @in of length @len to its normalization in form // NFKC (compatibility decomposition + canonical composition). The length of // the resulting Unicode string is returned in @out_len. If non-NULL, @indices // is assigned the location of a newly-allocated array of length @out_len + 1, // for each character in the normalized string giving the index in @in of the // corresponding unnormalized character. @indices is not guaranteed monotone or // onto. Unicode *unicodeNormalizeNFKC(const Unicode *in, int len, int *out_len, int **indices) { return unicodeNormalizeNFKC(in, len, out_len, indices, false); } Unicode *unicodeNormalizeNFKC(const Unicode *in, int len, int *out_len, int **indices, bool reverseRTL) { Unicode *out; int i, o, *classes, *idx = nullptr; for (i = 0, o = 0; i < len; ++i) { if (HANGUL_IS_L(in[i]) || HANGUL_IS_SYLLABLE(in[i])) { o += 1; } else { o += decomp_compat(in[i], nullptr); } } out = (Unicode *)gmallocn(o, sizeof(Unicode)); classes = (int *)gmallocn(o, sizeof(int)); if (indices) { idx = (int *)gmallocn(o + 1, sizeof(int)); } for (i = 0, o = 0; i < len;) { Unicode u = in[i]; if (IS_HANGUL(u)) { if (HANGUL_IS_L(u)) { Unicode l = u; if (i + 1 < len && HANGUL_IS_V(in[i + 1])) { Unicode lv = HANGUL_COMPOSE_L_V(l, in[++i]); if (i + 1 < len && HANGUL_IS_T(in[i + 1])) { out[o] = HANGUL_COMPOSE_LV_T(lv, in[++i]); } else { out[o] = lv; } } else { out[o] = l; } } else if (HANGUL_SYLLABLE_IS_LV(u)) { Unicode lv = u; if (i + 1 < len && HANGUL_IS_T(in[i + 1])) { out[o] = HANGUL_COMPOSE_LV_T(lv, in[++i]); } else { out[o] = lv; } } else { out[o] = u; } if (indices) { idx[o] = i; } ++i; ++o; } else { int j, p, q, r, s, dlen; // write compatibility decompositions into out (we have enough space) // chomp in until a starter is reached for (j = i, p = o; j < len; ++j) { u = in[j]; if (j != i && COMBINING_CLASS(u) == 0) { break; } dlen = decomp_compat(u, out + p, reverseRTL); for (q = p; q < p + dlen; ++q) { classes[q] = COMBINING_CLASS(out[q]); if (indices) { idx[q] = j; } } p += dlen; } // put out[o, p) in canonical ordering for (q = o + 1; q < p; ++q) { for (r = q; r > o + 1; --r) { // FIXME worth using a better sort? int swap; if (classes[r] >= classes[r - 1]) { break; } u = out[r]; out[r] = out[r - 1]; out[r - 1] = u; swap = classes[r]; classes[r] = classes[r - 1]; classes[r - 1] = swap; if (indices) { swap = idx[r]; idx[r] = idx[r - 1]; idx[r - 1] = swap; } } } // canonical compose out[o, p) for (q = o + 1; q < p; ++q) { if (!combine(out[o], out[q], &out[o])) { break; } } // move out[q, p) back to [o+1, ?) if (q != o + 1) { for (r = q, s = o + 1; r < p; ++r, ++s) { out[s] = out[r]; if (indices) { idx[s] = idx[r]; } } } else { s = p; } i = j; o = s; } } *out_len = o; gfree(classes); if (indices) { idx[o] = len; *indices = idx; } return out; } poppler-24.02.0/poppler/UnicodeTypeTable.h000066400000000000000000000031401455701731300203760ustar00rootroot00000000000000//======================================================================== // // UnicodeTypeTable.h // // Copyright 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 Ed Catmur // Copyright (C) 2012 Adrian Johnson // Copyright (C) 2016 Khaled Hosny // Copyright (C) 2019 Adriaan de Groot // Copyright (C) 2019 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 // //======================================================================== #ifndef UNICODETYPETABLE_H #define UNICODETYPETABLE_H #include "CharTypes.h" #include "poppler_private_export.h" extern bool unicodeTypeL(Unicode c); extern bool unicodeTypeR(Unicode c); extern bool unicodeTypeNum(Unicode c); extern bool unicodeTypeAlphaNum(Unicode c); extern bool unicodeIsAlphabeticPresentationForm(Unicode c); extern Unicode unicodeToUpper(Unicode c); extern Unicode POPPLER_PRIVATE_EXPORT *unicodeNormalizeNFKC(const Unicode *in, int len, int *out_len, int **indices); extern Unicode POPPLER_PRIVATE_EXPORT *unicodeNormalizeNFKC(const Unicode *in, int len, int *out_len, int **indices, bool reverseRTL); #endif poppler-24.02.0/poppler/ViewerPreferences.cc000066400000000000000000000070111455701731300207600ustar00rootroot00000000000000//======================================================================== // // ViewerPreferences.cc // // This file is licensed under the GPLv2 or later // // Copyright 2011 Pino Toscano // Copyright 2017, 2020, 2022 Albert Astals Cid // Copyright 2019 Marek Kasik // //======================================================================== #include #include "ViewerPreferences.h" #include "Object.h" #include "Dict.h" ViewerPreferences::ViewerPreferences(Dict *prefDict) { hideToolbar = prefDict->lookup("HideToolbar").getBoolWithDefaultValue(false); hideMenubar = prefDict->lookup("HideMenubar").getBoolWithDefaultValue(false); hideWindowUI = prefDict->lookup("HideWindowUI").getBoolWithDefaultValue(false); fitWindow = prefDict->lookup("FitWindow").getBoolWithDefaultValue(false); centerWindow = prefDict->lookup("CenterWindow").getBoolWithDefaultValue(false); displayDocTitle = prefDict->lookup("DisplayDocTitle").getBoolWithDefaultValue(false); Object obj = prefDict->lookup("NonFullScreenPageMode"); if (obj.isName()) { const char *mode = obj.getName(); if (!strcmp(mode, "UseNone")) { nonFullScreenPageMode = nfpmUseNone; } else if (!strcmp(mode, "UseOutlines")) { nonFullScreenPageMode = nfpmUseOutlines; } else if (!strcmp(mode, "UseThumbs")) { nonFullScreenPageMode = nfpmUseThumbs; } else if (!strcmp(mode, "UseOC")) { nonFullScreenPageMode = nfpmUseOC; } } obj = prefDict->lookup("Direction"); if (obj.isName()) { const char *dir = obj.getName(); if (!strcmp(dir, "L2R")) { direction = directionL2R; } else if (!strcmp(dir, "R2L")) { direction = directionR2L; } } obj = prefDict->lookup("PrintScaling"); if (obj.isName()) { const char *ps = obj.getName(); if (!strcmp(ps, "None")) { printScaling = printScalingNone; } else if (!strcmp(ps, "AppDefault")) { printScaling = printScalingAppDefault; } } obj = prefDict->lookup("Duplex"); if (obj.isName()) { const char *d = obj.getName(); if (!strcmp(d, "Simplex")) { duplex = duplexSimplex; } else if (!strcmp(d, "DuplexFlipShortEdge")) { duplex = duplexDuplexFlipShortEdge; } else if (!strcmp(d, "DuplexFlipLongEdge")) { duplex = duplexDuplexFlipLongEdge; } } pickTrayByPDFSize = prefDict->lookup("PickTrayByPDFSize").getBoolWithDefaultValue(false); obj = prefDict->lookup("NumCopies"); if (obj.isInt()) { numCopies = obj.getInt(); if (numCopies < 2) { numCopies = 1; } } obj = prefDict->lookup("PrintPageRange"); if (obj.isArray()) { Array *range = obj.getArray(); int length = range->getLength(); int pageNumber1, pageNumber2; if (length % 2 == 1) { length--; } for (int i = 0; i < length; i += 2) { Object obj2 = range->get(i); Object obj3 = range->get(i + 1); if (obj2.isInt() && (pageNumber1 = obj2.getInt()) >= 1 && obj3.isInt() && (pageNumber2 = obj3.getInt()) >= 1 && pageNumber1 < pageNumber2) { printPageRange.emplace_back(pageNumber1, pageNumber2); } else { printPageRange.clear(); break; } } } } ViewerPreferences::~ViewerPreferences() { } poppler-24.02.0/poppler/ViewerPreferences.h000066400000000000000000000046661455701731300206370ustar00rootroot00000000000000//======================================================================== // // ViewerPreferences.h // // This file is licensed under the GPLv2 or later // // Copyright 2011 Pino Toscano // Copyright 2019 Marek Kasik // Copyright 2021, 2022 Albert Astals Cid // //======================================================================== #ifndef VIEWERPREFERENCES_H #define VIEWERPREFERENCES_H #include class Dict; //------------------------------------------------------------------------ // ViewerPreferences //------------------------------------------------------------------------ class ViewerPreferences { public: enum NonFullScreenPageMode { nfpmUseNone, nfpmUseOutlines, nfpmUseThumbs, nfpmUseOC }; enum Direction { directionL2R, directionR2L }; enum PrintScaling { printScalingNone, printScalingAppDefault }; enum Duplex { duplexNone, duplexSimplex, duplexDuplexFlipShortEdge, duplexDuplexFlipLongEdge }; explicit ViewerPreferences(Dict *prefDict); ~ViewerPreferences(); bool getHideToolbar() const { return hideToolbar; } bool getHideMenubar() const { return hideMenubar; } bool getHideWindowUI() const { return hideWindowUI; } bool getFitWindow() const { return fitWindow; } bool getCenterWindow() const { return centerWindow; } bool getDisplayDocTitle() const { return displayDocTitle; } NonFullScreenPageMode getNonFullScreenPageMode() const { return nonFullScreenPageMode; } Direction getDirection() const { return direction; } PrintScaling getPrintScaling() const { return printScaling; } Duplex getDuplex() const { return duplex; } bool getPickTrayByPDFSize() const { return pickTrayByPDFSize; } int getNumCopies() const { return numCopies; } std::vector> getPrintPageRange() const { return printPageRange; } private: void init(); bool hideToolbar; bool hideMenubar; bool hideWindowUI; bool fitWindow; bool centerWindow; bool displayDocTitle; NonFullScreenPageMode nonFullScreenPageMode = nfpmUseNone; Direction direction = directionL2R; PrintScaling printScaling = printScalingAppDefault; Duplex duplex = duplexNone; bool pickTrayByPDFSize; int numCopies = 1; std::vector> printPageRange; }; #endif poppler-24.02.0/poppler/XRef.cc000066400000000000000000001600051455701731300162040ustar00rootroot00000000000000//======================================================================== // // XRef.cc // // 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) 2005 Dan Sheridan // Copyright (C) 2005 Brad Hards // Copyright (C) 2006, 2008, 2010, 2012-2014, 2016-2023 Albert Astals Cid // Copyright (C) 2007-2008 Julien Rebetez // Copyright (C) 2007 Carlos Garcia Campos // Copyright (C) 2009, 2010 Ilya Gorenbein // Copyright (C) 2010 Hib Eris // Copyright (C) 2012, 2013, 2016 Thomas Freitag // Copyright (C) 2012, 2013 Fabio D'Urso // Copyright (C) 2013, 2014, 2017, 2019 Adrian Johnson // Copyright (C) 2013 Pino Toscano // Copyright (C) 2016 Jakub Alba // Copyright (C) 2018, 2019 Adam Reichold // Copyright (C) 2018 Tobias Deiminger // Copyright (C) 2019 LE GARREC Vincent // Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by Technische Universität Dresden // Copyright (C) 2010 William Bader // Copyright (C) 2021 Mahmoud Khalil // Copyright (C) 2021 Georgiy Sgibnev . Work sponsored by lab50.net. // Copyright (C) 2023 g10 Code GmbH, Author: Sune Stolborg Vuorela // Copyright (C) 2023 Ilaï Deutel // 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 // //======================================================================== #include #include "poppler-config.h" #include #include #include #include #include #include #include #include #include "goo/gfile.h" #include "goo/gmem.h" #include "Object.h" #include "Stream.h" #include "Lexer.h" #include "Parser.h" #include "Dict.h" #include "Error.h" #include "ErrorCodes.h" #include "XRef.h" //------------------------------------------------------------------------ // Permission bits // Note that the PDF spec uses 1 base (eg bit 3 is 1<<2) //------------------------------------------------------------------------ #define permPrint (1 << 2) // bit 3 #define permChange (1 << 3) // bit 4 #define permCopy (1 << 4) // bit 5 #define permNotes (1 << 5) // bit 6 #define permFillForm (1 << 8) // bit 9 #define permAccessibility (1 << 9) // bit 10 #define permAssemble (1 << 10) // bit 11 #define permHighResPrint (1 << 11) // bit 12 #define defPermFlags 0xfffc //------------------------------------------------------------------------ // ObjectStream //------------------------------------------------------------------------ class ObjectStream { public: // Create an object stream, using object number , // generation 0. ObjectStream(XRef *xref, int objStrNumA, int recursion = 0); bool isOk() { return ok; } ~ObjectStream(); ObjectStream(const ObjectStream &) = delete; ObjectStream &operator=(const ObjectStream &) = delete; // Return the object number of this object stream. int getObjStrNum() { return objStrNum; } // Get the th object from this stream, which should be // object number , generation 0. Object getObject(int objIdx, int objNum); private: int objStrNum; // object number of the object stream int nObjects; // number of objects in the stream Object *objs; // the objects (length = nObjects) int *objNums; // the object numbers (length = nObjects) bool ok; }; ObjectStream::ObjectStream(XRef *xref, int objStrNumA, int recursion) { Stream *str; Parser *parser; Goffset *offsets; Object objStr, obj1; Goffset first; int i; objStrNum = objStrNumA; nObjects = 0; objs = nullptr; objNums = nullptr; ok = false; objStr = xref->fetch(objStrNum, 0, recursion); if (!objStr.isStream()) { return; } obj1 = objStr.streamGetDict()->lookup("N", recursion); if (!obj1.isInt()) { return; } nObjects = obj1.getInt(); if (nObjects <= 0) { return; } obj1 = objStr.streamGetDict()->lookup("First", recursion); if (!obj1.isInt() && !obj1.isInt64()) { return; } if (obj1.isInt()) { first = obj1.getInt(); } else { first = obj1.getInt64(); } if (first < 0) { return; } // this is an arbitrary limit to avoid integer overflow problems // in the 'new Object[nObjects]' call (Acrobat apparently limits // object streams to 100-200 objects) if (nObjects > 1000000) { error(errSyntaxError, -1, "Too many objects in an object stream"); 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) { obj1 = parser->getObj(); Object obj2 = parser->getObj(); if (!obj1.isInt() || !(obj2.isInt() || obj2.isInt64())) { delete parser; gfree(offsets); return; } objNums[i] = obj1.getInt(); if (obj2.isInt()) { offsets[i] = obj2.getInt(); } else { offsets[i] = obj2.getInt64(); } if (objNums[i] < 0 || offsets[i] < 0 || (i > 0 && offsets[i] < offsets[i - 1])) { delete parser; gfree(offsets); return; } } while (str->getChar() != EOF) { ; } delete parser; // skip to the first object - this shouldn't be necessary because // the First key is supposed to be equal to offsets[0], but just in // case... for (Goffset pos = first; pos < offsets[0]; ++pos) { objStr.getStream()->getChar(); } // parse the objects for (i = 0; i < nObjects; ++i) { if (i == nObjects - 1) { str = new EmbedStream(objStr.getStream(), Object(objNull), false, 0); } else { str = new EmbedStream(objStr.getStream(), Object(objNull), true, offsets[i + 1] - offsets[i]); } parser = new Parser(xref, str, false); objs[i] = parser->getObj(); while (str->getChar() != EOF) { ; } delete parser; } gfree(offsets); ok = true; } ObjectStream::~ObjectStream() { delete[] objs; gfree(objNums); } Object ObjectStream::getObject(int objIdx, int objNum) { if (objIdx < 0 || objIdx >= nObjects || objNum != objNums[objIdx]) { return Object(objNull); } return objs[objIdx].copy(); } //------------------------------------------------------------------------ // XRef //------------------------------------------------------------------------ #define xrefLocker() const std::scoped_lock locker(mutex) XRef::XRef() : objStrs { 5 } { ok = true; errCode = errNone; entries = nullptr; capacity = 0; size = 0; modified = false; streamEnds = nullptr; streamEndsLen = 0; mainXRefEntriesOffset = 0; xRefStream = false; scannedSpecialFlags = false; encrypted = false; permFlags = defPermFlags; ownerPasswordOk = false; rootNum = -1; strOwner = false; xrefReconstructed = false; encAlgorithm = cryptNone; keyLength = 0; } XRef::XRef(const Object *trailerDictA) : XRef {} { if (trailerDictA->isDict()) { trailerDict = trailerDictA->copy(); } } XRef::XRef(BaseStream *strA, Goffset pos, Goffset mainXRefEntriesOffsetA, bool *wasReconstructed, bool reconstruct, const std::function &xrefReconstructedCallback) : XRef {} { Object obj; mainXRefEntriesOffset = mainXRefEntriesOffsetA; xrefReconstructedCb = xrefReconstructedCallback; // read the trailer str = strA; start = str->getStart(); prevXRefOffset = mainXRefOffset = pos; if (reconstruct && !(ok = constructXRef(wasReconstructed))) { errCode = errDamaged; return; } else { // if there was a problem with the 'startxref' position, try to // reconstruct the xref table if (prevXRefOffset == 0) { if (!(ok = constructXRef(wasReconstructed))) { errCode = errDamaged; return; } // read the xref table } else { std::vector followedXRefStm; readXRef(&prevXRefOffset, &followedXRefStm, nullptr); // if there was a problem with the xref table, // try to reconstruct it if (!ok) { if (!(ok = constructXRef(wasReconstructed))) { errCode = errDamaged; return; } } } // set size to (at least) the size specified in trailer dict obj = trailerDict.dictLookupNF("Size").copy(); if (!obj.isInt()) { error(errSyntaxWarning, -1, "No valid XRef size in trailer"); } else { if (obj.getInt() > size) { if (resize(obj.getInt()) != obj.getInt()) { if (!(ok = constructXRef(wasReconstructed))) { errCode = errDamaged; return; } } } } // get the root dictionary (catalog) object obj = trailerDict.dictLookupNF("Root").copy(); if (obj.isRef()) { rootNum = obj.getRefNum(); rootGen = obj.getRefGen(); } else { if (!(ok = constructXRef(wasReconstructed))) { errCode = errDamaged; return; } } } // now set the trailer dictionary's xref pointer so we can fetch // indirect objects from it trailerDict.getDict()->setXRef(this); } XRef::~XRef() { for (int i = 0; i < size; i++) { if (entries[i].type == xrefEntryFree) { continue; } entries[i].obj.~Object(); } gfree(entries); if (streamEnds) { gfree(streamEnds); } if (strOwner) { delete str; } } XRef *XRef::copy() const { XRef *xref = new XRef(); xref->str = str->copy(); xref->strOwner = true; xref->encrypted = encrypted; xref->permFlags = permFlags; xref->ownerPasswordOk = ownerPasswordOk; xref->rootGen = rootGen; xref->rootNum = rootNum; xref->start = start; xref->prevXRefOffset = prevXRefOffset; xref->mainXRefEntriesOffset = mainXRefEntriesOffset; xref->xRefStream = xRefStream; xref->trailerDict = trailerDict.copy(); xref->encAlgorithm = encAlgorithm; xref->encRevision = encRevision; xref->encVersion = encVersion; xref->permFlags = permFlags; xref->keyLength = keyLength; xref->permFlags = permFlags; for (int i = 0; i < 32; i++) { xref->fileKey[i] = fileKey[i]; } if (xref->reserve(size) == 0) { error(errSyntaxError, -1, "unable to allocate {0:d} entries", size); delete xref; return nullptr; } xref->size = size; for (int i = 0; i < size; ++i) { xref->entries[i].offset = entries[i].offset; xref->entries[i].type = entries[i].type; // set the object to null, it will be fetched from the stream when needed new (&xref->entries[i].obj) Object(objNull); xref->entries[i].flags = entries[i].flags; xref->entries[i].gen = entries[i].gen; // If entry has been changed from the stream value we need to copy it // otherwise it's lost if (entries[i].getFlag(XRefEntry::Updated)) { xref->entries[i].obj = entries[i].obj.copy(); } } xref->streamEndsLen = streamEndsLen; if (streamEndsLen != 0) { xref->streamEnds = (Goffset *)gmalloc(streamEndsLen * sizeof(Goffset)); for (int i = 0; i < streamEndsLen; i++) { xref->streamEnds[i] = streamEnds[i]; } } return xref; } int XRef::reserve(int newSize) { if (newSize > capacity) { int newCapacity = 1024; if (capacity) { if (capacity <= INT_MAX / 2) { newCapacity = capacity * 2; } else { newCapacity = newSize; } } 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 = grealloc(entries, newCapacity * sizeof(XRefEntry), /* checkoverflow=*/true); if (p == nullptr) { return 0; } entries = (XRefEntry *)p; capacity = newCapacity; } return capacity; } int XRef::resize(int newSize) { if (newSize > size) { if (reserve(newSize) < newSize) { return size; } for (int i = size; i < newSize; ++i) { entries[i].offset = -1; entries[i].type = xrefEntryNone; new (&entries[i].obj) Object(objNull); entries[i].flags = 0; entries[i].gen = 0; } } else { for (int i = newSize; i < size; i++) { entries[i].obj.~Object(); } } size = newSize; return size; } /* Read one xref table section. Also reads the associated trailer * dictionary, and returns the prev pointer (if any). * Arguments: * pos Points to a Goffset containing the offset of the XRef * section to be read. If a prev pointer is found, *pos is * updated with its value * followedXRefStm Used in case of nested readXRef calls to spot circular * references in XRefStm pointers * xrefStreamObjsNum If not NULL, every time a XRef stream is encountered, * its object number is appended * Return value: * true if a prev pointer is found, otherwise false */ bool XRef::readXRef(Goffset *pos, std::vector *followedXRefStm, std::vector *xrefStreamObjsNum) { Parser *parser; Object obj; bool more; Goffset parsePos; if (unlikely(checkedAdd(start, *pos, &parsePos))) { ok = false; return false; } if (parsePos < 0) { ok = false; return false; } // start up a parser, parse one token parser = new Parser(nullptr, str->makeSubStream(parsePos, false, 0, Object(objNull)), true); obj = parser->getObj(true); // parse an old-style xref table if (obj.isCmd("xref")) { more = readXRefTable(parser, pos, followedXRefStm, xrefStreamObjsNum); // parse an xref stream } else if (obj.isInt()) { const int objNum = obj.getInt(); if (obj = parser->getObj(true), !obj.isInt()) { goto err1; } if (obj = parser->getObj(true), !obj.isCmd("obj")) { goto err1; } if (obj = parser->getObj(), !obj.isStream()) { goto err1; } if (trailerDict.isNone()) { xRefStream = true; } if (xrefStreamObjsNum) { xrefStreamObjsNum->push_back(objNum); } more = readXRefStream(obj.getStream(), pos); } else { goto err1; } delete parser; return more; err1: delete parser; ok = false; return false; } bool XRef::readXRefTable(Parser *parser, Goffset *pos, std::vector *followedXRefStm, std::vector *xrefStreamObjsNum) { XRefEntry entry; bool more; Object obj, obj2; Goffset pos2; int first, n; while (true) { obj = parser->getObj(true); if (obj.isCmd("trailer")) { break; } if (!obj.isInt()) { goto err0; } first = obj.getInt(); obj = parser->getObj(true); if (!obj.isInt()) { goto err0; } n = obj.getInt(); if (first < 0 || n < 0 || first > INT_MAX - n) { goto err0; } if (first + n > size) { if (resize(first + n) != first + n) { error(errSyntaxError, -1, "Invalid 'obj' parameters'"); goto err0; } } for (int i = first; i < first + n; ++i) { obj = parser->getObj(true); if (obj.isInt()) { entry.offset = obj.getInt(); } else if (obj.isInt64()) { entry.offset = obj.getInt64(); } else { goto err0; } obj = parser->getObj(true); if (!obj.isInt()) { goto err0; } entry.gen = obj.getInt(); entry.flags = 0; obj = parser->getObj(true); if (obj.isCmd("n")) { entry.type = xrefEntryUncompressed; } else if (obj.isCmd("f")) { entry.type = xrefEntryFree; } else { goto err0; } if (entries[i].offset == -1) { entries[i].offset = entry.offset; entries[i].gen = entry.gen; entries[i].type = entry.type; entries[i].flags = entry.flags; entries[i].obj.setToNull(); // PDF files of patents from the IBM Intellectual Property // Network have a bug: the xref table claims to start at 1 // instead of 0. if (i == 1 && first == 1 && entries[1].offset == 0 && entries[1].gen == 65535 && entries[1].type == xrefEntryFree) { i = first = 0; entries[0].offset = 0; entries[0].gen = 65535; entries[0].type = xrefEntryFree; entries[0].flags = entries[1].flags; entries[0].obj = std::move(entries[1].obj); entries[1].offset = -1; entries[1].obj.setToNull(); } } } } // read the trailer dictionary obj = parser->getObj(); if (!obj.isDict()) { goto err0; } // get the 'Prev' pointer obj2 = obj.getDict()->lookupNF("Prev").copy(); if (obj2.isInt() || obj2.isInt64()) { if (obj2.isInt()) { pos2 = obj2.getInt(); } else { pos2 = obj2.getInt64(); } if (pos2 != *pos) { *pos = pos2; more = true; } else { error(errSyntaxWarning, -1, "Infinite loop in xref table"); more = false; } } else if (obj2.isRef()) { // certain buggy PDF generators generate "/Prev NNN 0 R" instead // of "/Prev NNN" pos2 = (unsigned int)obj2.getRefNum(); if (pos2 != *pos) { *pos = pos2; more = true; } else { error(errSyntaxWarning, -1, "Infinite loop in xref table"); more = false; } } else { more = false; } // save the first trailer dictionary if (trailerDict.isNone()) { trailerDict = obj.copy(); } // check for an 'XRefStm' key obj2 = obj.getDict()->lookup("XRefStm"); if (obj2.isInt() || obj2.isInt64()) { if (obj2.isInt()) { pos2 = obj2.getInt(); } else { pos2 = obj2.getInt64(); } for (size_t i = 0; ok == true && i < followedXRefStm->size(); ++i) { if (followedXRefStm->at(i) == pos2) { ok = false; } } // Arbitrary limit because otherwise we exhaust the stack // calling readXRef + readXRefTable if (followedXRefStm->size() > 4096) { error(errSyntaxError, -1, "File has more than 4096 XRefStm, aborting"); ok = false; } if (ok) { followedXRefStm->push_back(pos2); readXRef(&pos2, followedXRefStm, xrefStreamObjsNum); } if (!ok) { goto err0; } } return more; err0: ok = false; return false; } bool XRef::readXRefStream(Stream *xrefStr, Goffset *pos) { int w[3]; bool more; Object obj; ok = false; Dict *dict = xrefStr->getDict(); obj = dict->lookupNF("Size").copy(); if (!obj.isInt()) { return false; } int newSize = obj.getInt(); if (newSize < 0) { return false; } if (newSize > size) { if (resize(newSize) != newSize) { error(errSyntaxError, -1, "Invalid 'size' parameter"); return false; } } obj = dict->lookupNF("W").copy(); if (!obj.isArray() || obj.arrayGetLength() < 3) { return false; } for (int i = 0; i < 3; ++i) { Object obj2 = obj.arrayGet(i); if (!obj2.isInt()) { return false; } w[i] = obj2.getInt(); if (w[i] < 0) { return false; } } if (w[0] > (int)sizeof(int) || w[1] > (int)sizeof(long long) || w[2] > (int)sizeof(long long)) { return false; } xrefStr->reset(); const Object &idx = dict->lookupNF("Index"); if (idx.isArray()) { for (int i = 0; i + 1 < idx.arrayGetLength(); i += 2) { obj = idx.arrayGet(i); if (!obj.isInt()) { return false; } int first = obj.getInt(); obj = idx.arrayGet(i + 1); if (!obj.isInt()) { return false; } int n = obj.getInt(); if (first < 0 || n < 0 || !readXRefStreamSection(xrefStr, w, first, n)) { return false; } } } else { if (!readXRefStreamSection(xrefStr, w, 0, newSize)) { return false; } } obj = dict->lookupNF("Prev").copy(); if (obj.isInt() && obj.getInt() >= 0) { *pos = obj.getInt(); more = true; } else if (obj.isInt64() && obj.getInt64() >= 0) { *pos = obj.getInt64(); more = true; } else { more = false; } if (trailerDict.isNone()) { trailerDict = xrefStr->getDictObject()->copy(); } ok = true; return more; } bool XRef::readXRefStreamSection(Stream *xrefStr, const int *w, int first, int n) { unsigned long long offset, gen; int type, c, i, j; if (first > INT_MAX - n) { return false; } if (first + n < 0) { return false; } if (first + n > size) { if (resize(first + n) != size) { error(errSyntaxError, -1, "Invalid 'size' inside xref table"); return false; } if (first + n > size) { error(errSyntaxError, -1, "Invalid 'first' or 'n' inside xref table"); return false; } } for (i = first; i < first + n; ++i) { if (w[0] == 0) { type = 1; } else { for (type = 0, j = 0; j < w[0]; ++j) { if ((c = xrefStr->getChar()) == EOF) { return false; } type = (type << 8) + c; } } for (offset = 0, j = 0; j < w[1]; ++j) { if ((c = xrefStr->getChar()) == EOF) { return false; } offset = (offset << 8) + c; } if (offset > (unsigned long long)GoffsetMax()) { error(errSyntaxError, -1, "Offset inside xref table too large for fseek"); return false; } for (gen = 0, j = 0; j < w[2]; ++j) { if ((c = xrefStr->getChar()) == EOF) { return false; } gen = (gen << 8) + c; } if (gen > INT_MAX) { if (i == 0 && gen == std::numeric_limits::max()) { // workaround broken generators gen = 65535; } else { error(errSyntaxError, -1, "Gen inside xref table too large (bigger than INT_MAX)"); return false; } } if (entries[i].offset == -1) { switch (type) { case 0: entries[i].offset = offset; entries[i].gen = static_cast(gen); entries[i].type = xrefEntryFree; break; case 1: entries[i].offset = offset; entries[i].gen = static_cast(gen); entries[i].type = xrefEntryUncompressed; break; case 2: entries[i].offset = offset; entries[i].gen = static_cast(gen); entries[i].type = xrefEntryCompressed; break; default: return false; } } } return true; } // Attempt to construct an xref table for a damaged file. // Warning: Reconstruction of files where last XRef section is a stream // or where some objects are defined inside an object stream is not yet supported. // Existing data in XRef::entries may get corrupted if applied anyway. bool XRef::constructXRef(bool *wasReconstructed, bool needCatalogDict) { Parser *parser; char buf[256]; Goffset pos; int num, gen; int streamEndsSize; char *p; bool gotRoot; char *token = nullptr; bool oneCycle = true; Goffset offset = 0; resize(0); // free entries properly gfree(entries); capacity = 0; size = 0; entries = nullptr; gotRoot = false; streamEndsLen = streamEndsSize = 0; if (wasReconstructed) { *wasReconstructed = true; } if (xrefReconstructedCb) { xrefReconstructedCb(); } str->reset(); while (true) { pos = str->getPos(); if (!str->getLine(buf, 256)) { break; } p = buf; // skip whitespace while (*p && Lexer::isSpace(*p & 0xff)) { ++p; } oneCycle = true; offset = 0; while ((token = strstr(p, "endobj")) || oneCycle) { oneCycle = false; if (token) { oneCycle = true; token[0] = '\0'; offset = token - p; } // got trailer dictionary if (!strncmp(p, "trailer", 7)) { parser = new Parser(nullptr, str->makeSubStream(pos + 7, false, 0, Object(objNull)), false); Object newTrailerDict = parser->getObj(); if (newTrailerDict.isDict()) { const Object &obj = newTrailerDict.dictLookupNF("Root"); if (obj.isRef() && (!gotRoot || !needCatalogDict) && rootNum != obj.getRefNum()) { rootNum = obj.getRefNum(); rootGen = obj.getRefGen(); trailerDict = newTrailerDict.copy(); gotRoot = true; } } delete parser; // look for object } else if (isdigit(*p & 0xff)) { num = atoi(p); if (num > 0) { do { ++p; } while (*p && isdigit(*p & 0xff)); if ((*p & 0xff) == 0 || isspace(*p & 0xff)) { if ((*p & 0xff) == 0) { // new line, continue with next line! str->getLine(buf, 256); p = buf; } else { ++p; } while (*p && isspace(*p & 0xff)) { ++p; } if (isdigit(*p & 0xff)) { gen = atoi(p); do { ++p; } while (*p && isdigit(*p & 0xff)); if ((*p & 0xff) == 0 || isspace(*p & 0xff)) { if ((*p & 0xff) == 0) { // new line, continue with next line! str->getLine(buf, 256); p = buf; } else { ++p; } while (*p && isspace(*p & 0xff)) { ++p; } if (!strncmp(p, "obj", 3)) { if (num >= size) { if (unlikely(num >= INT_MAX - 1 - 255)) { error(errSyntaxError, -1, "Bad object number"); return false; } const int newSize = (num + 1 + 255) & ~255; if (newSize < 0) { error(errSyntaxError, -1, "Bad object number"); return false; } if (resize(newSize) != newSize) { error(errSyntaxError, -1, "Invalid 'obj' parameters"); return false; } } if (entries[num].type == xrefEntryFree || gen >= entries[num].gen) { entries[num].offset = pos - start; entries[num].gen = gen; entries[num].type = xrefEntryUncompressed; } } } } } } } else { char *endstream = strstr(p, "endstream"); if (endstream) { intptr_t endstreamPos = endstream - p; if ((endstreamPos == 0 || Lexer::isSpace(p[endstreamPos - 1] & 0xff)) // endstream is either at beginning or preceeded by space && (endstreamPos + 9 >= 256 || Lexer::isSpace(p[endstreamPos + 9] & 0xff))) // endstream is either at end or followed by space { if (streamEndsLen == streamEndsSize) { streamEndsSize += 64; if (streamEndsSize >= INT_MAX / (int)sizeof(int)) { error(errSyntaxError, -1, "Invalid 'endstream' parameter."); return false; } streamEnds = (Goffset *)greallocn(streamEnds, streamEndsSize, sizeof(Goffset)); } streamEnds[streamEndsLen++] = pos + endstreamPos; } } } if (token) { p = token + 6; // strlen( "endobj" ) = 6 pos += offset + 6; // strlen( "endobj" ) = 6 while (*p && Lexer::isSpace(*p & 0xff)) { ++p; ++pos; } } } } if (gotRoot) { return true; } error(errSyntaxError, -1, "Couldn't find trailer dictionary"); return false; } void XRef::setEncryption(int permFlagsA, bool ownerPasswordOkA, const unsigned char *fileKeyA, int keyLengthA, int encVersionA, int encRevisionA, CryptAlgorithm encAlgorithmA) { int i; encrypted = true; permFlags = permFlagsA; ownerPasswordOk = ownerPasswordOkA; if (keyLengthA <= 32) { keyLength = keyLengthA; } else { keyLength = 32; } for (i = 0; i < keyLength; ++i) { fileKey[i] = fileKeyA[i]; } encVersion = encVersionA; encRevision = encRevisionA; encAlgorithm = encAlgorithmA; } void XRef::getEncryptionParameters(unsigned char **fileKeyA, CryptAlgorithm *encAlgorithmA, int *keyLengthA) { if (encrypted) { *fileKeyA = fileKey; *encAlgorithmA = encAlgorithm; *keyLengthA = keyLength; } else { // null encryption parameters *fileKeyA = nullptr; *encAlgorithmA = cryptRC4; *keyLengthA = 0; } } bool XRef::isRefEncrypted(Ref r) { xrefLocker(); const XRefEntry *e = getEntry(r.num); if (!e->obj.isNull()) { // check for updated object return false; } switch (e->type) { case xrefEntryUncompressed: { return encrypted && !e->getFlag(XRefEntry::Unencrypted); } case xrefEntryCompressed: { const Goffset objStrNum = e->offset; if (unlikely(objStrNum < 0 || objStrNum >= size)) { error(errSyntaxError, -1, "XRef::isRefEncrypted - Compressed object offset out of xref bounds"); return false; } const Object objStr = fetch(static_cast(e->offset), 0); return objStr.getStream()->isEncrypted(); } default: { } } return false; } bool XRef::okToPrint(bool ignoreOwnerPW) const { return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permPrint); } // we can print at high res if we are only doing security handler revision // 2 (and we are allowed to print at all), or with security handler rev // 3 and we are allowed to print, and bit 12 is set. bool XRef::okToPrintHighRes(bool ignoreOwnerPW) const { if (encrypted) { if (2 == encRevision) { return (okToPrint(ignoreOwnerPW)); } else if (encRevision >= 3) { return (okToPrint(ignoreOwnerPW) && (permFlags & permHighResPrint)); } else { // something weird - unknown security handler version return false; } } else { return true; } } bool XRef::okToChange(bool ignoreOwnerPW) const { return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permChange); } bool XRef::okToCopy(bool ignoreOwnerPW) const { return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permCopy); } bool XRef::okToAddNotes(bool ignoreOwnerPW) const { return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permNotes); } bool XRef::okToFillForm(bool ignoreOwnerPW) const { return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permFillForm); } bool XRef::okToAccessibility(bool ignoreOwnerPW) const { return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permAccessibility); } bool XRef::okToAssemble(bool ignoreOwnerPW) const { return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permAssemble); } Object XRef::getCatalog() { Object catalog = fetch(rootNum, rootGen); if (catalog.isDict()) { return catalog; } bool wasReconstructed = false; if (constructXRef(&wasReconstructed, true)) { catalog = fetch(rootNum, rootGen); } return catalog; } Object XRef::fetch(const Ref ref, int recursion) { return fetch(ref.num, ref.gen, recursion); } Object XRef::fetch(int num, int gen, int recursion, Goffset *endPos) { XRefEntry *e; Object obj1, obj2, obj3; xrefLocker(); // check for bogus ref - this can happen in corrupted PDF files if (num < 0 || num >= size) { goto err; } e = getEntry(num); if (!e->obj.isNull()) { // check for updated object return e->obj.copy(); } switch (e->type) { case xrefEntryUncompressed: { if (e->gen != gen || e->offset < 0) { goto err; } Parser parser { this, str->makeSubStream(start + e->offset, false, 0, Object(objNull)), true }; obj1 = parser.getObj(recursion); obj2 = parser.getObj(recursion); obj3 = parser.getObj(recursion); if (!obj1.isInt() || obj1.getInt() != num || !obj2.isInt() || obj2.getInt() != gen || !obj3.isCmd("obj")) { // some buggy pdf have obj1234 for ints that represent 1234 // try to recover here if (obj1.isInt() && obj1.getInt() == num && obj2.isInt() && obj2.getInt() == gen && obj3.isCmd()) { const char *cmd = obj3.getCmd(); if (strlen(cmd) > 3 && cmd[0] == 'o' && cmd[1] == 'b' && cmd[2] == 'j') { char *end_ptr; long longNumber = strtol(cmd + 3, &end_ptr, 0); if (longNumber <= INT_MAX && longNumber >= INT_MIN && *end_ptr == '\0') { int number = longNumber; error(errSyntaxWarning, -1, "Cmd was not obj but {0:s}, assuming the creator meant obj {1:d}", cmd, number); if (endPos) { *endPos = parser.getPos(); } return Object(number); } } } goto err; } Object obj = parser.getObj(false, (encrypted && !e->getFlag(XRefEntry::Unencrypted)) ? fileKey : nullptr, encAlgorithm, keyLength, num, gen, recursion); if (endPos) { *endPos = parser.getPos(); } return obj; } case xrefEntryCompressed: { #if 0 // Adobe apparently ignores the generation number on compressed objects if (gen != 0) { goto err; } #endif if (e->offset >= (unsigned int)size || (entries[e->offset].type != xrefEntryUncompressed && entries[e->offset].type != xrefEntryNone)) { error(errSyntaxError, -1, "Invalid object stream"); goto err; } ObjectStream *objStr = objStrs.lookup(e->offset); if (!objStr) { objStr = new ObjectStream(this, static_cast(e->offset), recursion + 1); if (!objStr->isOk()) { delete objStr; objStr = nullptr; goto err; } else { // XRef could be reconstructed in constructor of ObjectStream: e = getEntry(num); objStrs.put(e->offset, objStr); } } if (endPos) { *endPos = -1; } return objStr->getObject(e->gen, num); } default: goto err; } err: if (!xRefStream && !xrefReconstructed) { // Check if there has been any updated object, if there has been we can't reconstruct because that would mean losing the changes bool xrefHasChanges = false; for (int i = 0; i < size; i++) { if (entries[i].getFlag(XRefEntry::Updated)) { xrefHasChanges = true; break; } } if (xrefHasChanges) { error(errInternal, -1, "xref num {0:d} not found but needed, document has changes, reconstruct aborted\n", 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); rootNum = -1; constructXRef(&xrefReconstructed); return fetch(num, gen, ++recursion, endPos); } if (endPos) { *endPos = -1; } return Object(objNull); } void XRef::lock() { mutex.lock(); } void XRef::unlock() { mutex.unlock(); } Object XRef::getDocInfo() { return trailerDict.dictLookup("Info"); } // Added for the pdftex project. Object XRef::getDocInfoNF() { return trailerDict.dictLookupNF("Info").copy(); } Object XRef::createDocInfoIfNeeded(Ref *ref) { Object obj = trailerDict.getDict()->lookup("Info", ref); getDocInfo(); if (obj.isDict() && *ref != Ref::INVALID()) { // Info is valid if it's a dict and to pointed by an indirect reference return obj; } removeDocInfo(); obj = Object(new Dict(this)); *ref = addIndirectObject(obj); trailerDict.dictSet("Info", Object(*ref)); return obj; } void XRef::removeDocInfo() { Object infoObjRef = getDocInfoNF(); if (infoObjRef.isNull()) { return; } trailerDict.dictRemove("Info"); if (likely(infoObjRef.isRef())) { removeIndirectObject(infoObjRef.getRef()); } } bool XRef::getStreamEnd(Goffset streamStart, Goffset *streamEnd) { int a, b, m; if (streamEndsLen == 0 || streamStart > streamEnds[streamEndsLen - 1]) { return false; } a = -1; b = streamEndsLen - 1; // invariant: streamEnds[a] < streamStart <= streamEnds[b] while (b - a > 1) { m = (a + b) / 2; if (streamStart <= streamEnds[m]) { b = m; } else { a = m; } } *streamEnd = streamEnds[b]; return true; } int XRef::getNumEntry(Goffset offset) { if (size > 0) { int res = 0; Goffset resOffset = getEntry(0)->offset; XRefEntry *e; for (int i = 1; i < size; ++i) { e = getEntry(i, false); if (e->type != xrefEntryFree && e->offset < offset && e->offset >= resOffset) { res = i; resOffset = e->offset; } } return res; } else { return -1; } } void XRef::add(Ref ref, Goffset offs, bool used) { add(ref.num, ref.gen, offs, used); } bool XRef::add(int num, int gen, Goffset offs, bool used) { xrefLocker(); if (num >= size) { if (num >= capacity) { entries = (XRefEntry *)greallocn_checkoverflow(entries, num + 1, sizeof(XRefEntry)); if (unlikely(entries == nullptr)) { size = 0; capacity = 0; return false; } capacity = num + 1; } for (int i = size; i < num + 1; ++i) { entries[i].offset = -1; entries[i].type = xrefEntryFree; new (&entries[i].obj) Object(objNull); entries[i].flags = 0; entries[i].gen = 0; } size = num + 1; } XRefEntry *e = getEntry(num); e->gen = gen; e->obj.setToNull(); e->flags = 0; if (used) { e->type = xrefEntryUncompressed; e->offset = offs; } else { e->type = xrefEntryFree; e->offset = 0; } return true; } 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); 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\n", r.num, r.gen); } e->obj = o->copy(); e->setFlag(XRefEntry::Updated, true); setModified(); } Ref XRef::addIndirectObject(const Object &o) { int entryIndexToUse = -1; for (int i = 1; entryIndexToUse == -1 && i < size; ++i) { XRefEntry *e = getEntry(i, false /* complainIfMissing */); if (e->type == xrefEntryFree && e->gen < 65535) { entryIndexToUse = i; } } XRefEntry *e; if (entryIndexToUse == -1) { entryIndexToUse = size; add(entryIndexToUse, 0, 0, false); e = getEntry(entryIndexToUse); } else { // reuse a free entry e = getEntry(entryIndexToUse); // we don't touch gen number, because it should have been // incremented when the object was deleted } e->type = xrefEntryUncompressed; e->obj = o.copy(); e->setFlag(XRefEntry::Updated, true); setModified(); Ref r; r.num = entryIndexToUse; r.gen = e->gen; return r; } 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); return; } XRefEntry *e = getEntry(r.num); if (e->type == xrefEntryFree) { return; } e->obj.~Object(); e->type = xrefEntryFree; if (likely(e->gen < 65535)) { e->gen++; } e->setFlag(XRefEntry::Updated, true); setModified(); } 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, StreamCompression compression) { 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"); } int lastFreeEntry = 0; for (int i = 0; i < size; i++) { if (getEntry(i)->type == xrefEntryFree) { getEntry(lastFreeEntry)->offset = i; lastFreeEntry = i; } } getEntry(lastFreeEntry)->offset = 0; if (writeAllEntries) { writer->startSection(0, size); for (int i = 0; i < size; i++) { XRefEntry *e = getEntry(i); if (e->gen > 65535) { e->gen = 65535; // cap generation number to 65535 (required by PDFReference) } writer->writeEntry(e->offset, e->gen, e->type); } } else { int i = 0; while (i < size) { int j; for (j = i; j < size; j++) { // look for consecutive entries if ((getEntry(j)->type == xrefEntryFree) && (getEntry(j)->gen == 0)) { break; } } if (j - i != 0) { writer->startSection(i, j - i); for (int k = i; k < j; k++) { XRefEntry *e = getEntry(k); if (e->gen > 65535) { e->gen = 65535; // cap generation number to 65535 (required by PDFReference) } writer->writeEntry(e->offset, e->gen, e->type); } i = j; } else { ++i; } } } } XRef::XRefTableWriter::XRefTableWriter(OutStream *outStrA) { outStr = outStrA; } void XRef::XRefTableWriter::startSection(int first, int count) { outStr->printf("%i %i\r\n", first, count); } void XRef::XRefTableWriter::writeEntry(Goffset offset, int gen, XRefEntryType type) { outStr->printf("%010lli %05i %c\r\n", (long long)offset, gen, (type == xrefEntryFree) ? 'f' : 'n'); } void XRef::writeTableToFile(OutStream *outStr, bool writeAllEntries) { XRefTableWriter writer(outStr); outStr->printf("xref\r\n"); writeXRef(&writer, writeAllEntries); } XRef::XRefStreamWriter::XRefStreamWriter(Array *indexA, GooString *stmBufA, int offsetSizeA) { index = indexA; stmBuf = stmBufA; offsetSize = offsetSizeA; } void XRef::XRefStreamWriter::startSection(int first, int count) { index->add(Object(first)); index->add(Object(count)); } void XRef::XRefStreamWriter::writeEntry(Goffset offset, int gen, XRefEntryType type) { const int entryTotalSize = 1 + offsetSize + 2; /* type + offset + gen */ char data[16]; data[0] = (type == xrefEntryFree) ? 0 : 1; for (int i = offsetSize; i > 0; i--) { data[i] = offset & 0xff; offset >>= 8; } data[offsetSize + 1] = (gen >> 8) & 0xff; data[offsetSize + 2] = gen & 0xff; stmBuf->append(data, entryTotalSize); } XRef::XRefPreScanWriter::XRefPreScanWriter() { hasOffsetsBeyond4GB = false; } void XRef::XRefPreScanWriter::startSection(int first, int count) { } void XRef::XRefPreScanWriter::writeEntry(Goffset offset, int gen, XRefEntryType type) { if (offset >= 0x100000000ll) { hasOffsetsBeyond4GB = true; } } void XRef::writeStreamToBuffer(GooString *stmBuf, Dict *xrefDict, XRef *xref) { Array *index = new Array(xref); stmBuf->clear(); // First pass: determine whether all offsets fit in 4 bytes or not XRefPreScanWriter prescan; writeXRef(&prescan, false); const int offsetSize = prescan.hasOffsetsBeyond4GB ? sizeof(Goffset) : 4; // Second pass: actually write the xref stream XRefStreamWriter writer(index, stmBuf, offsetSize); writeXRef(&writer, false); xrefDict->set("Type", Object(objName, "XRef")); xrefDict->set("Index", Object(index)); Array *wArray = new Array(xref); wArray->add(Object(1)); wArray->add(Object(offsetSize)); wArray->add(Object(2)); xrefDict->set("W", Object(wArray)); } bool XRef::parseEntry(Goffset offset, XRefEntry *entry) { bool r; if (unlikely(entry == nullptr)) { return false; } Parser parser(nullptr, str->makeSubStream(offset, false, 20, Object(objNull)), true); Object obj1, obj2, obj3; if (((obj1 = parser.getObj(), obj1.isInt()) || obj1.isInt64()) && (obj2 = parser.getObj(), obj2.isInt()) && (obj3 = parser.getObj(), obj3.isCmd("n") || obj3.isCmd("f"))) { if (obj1.isInt64()) { entry->offset = obj1.getInt64(); } else { entry->offset = obj1.getInt(); } entry->gen = obj2.getInt(); entry->type = obj3.isCmd("n") ? xrefEntryUncompressed : xrefEntryFree; entry->obj.setToNull(); entry->flags = 0; r = true; } else { r = false; } return r; } /* Traverse all XRef tables and, if untilEntryNum != -1, stop as soon as * untilEntryNum is found, or try to reconstruct the xref table if it's not * present in any xref. * If xrefStreamObjsNum is not NULL, it is filled with the list of the object * numbers of the XRef streams that have been traversed */ void XRef::readXRefUntil(int untilEntryNum, std::vector *xrefStreamObjsNum) { std::vector followedPrev; while (prevXRefOffset && (untilEntryNum == -1 || (untilEntryNum < size && entries[untilEntryNum].type == xrefEntryNone))) { bool followed = false; for (long long j : followedPrev) { if (j == prevXRefOffset) { followed = true; break; } } if (followed) { error(errSyntaxError, -1, "Circular XRef"); if (!xRefStream && !(ok = constructXRef(nullptr))) { errCode = errDamaged; } break; } followedPrev.push_back(prevXRefOffset); std::vector followedXRefStm; if (!readXRef(&prevXRefOffset, &followedXRefStm, xrefStreamObjsNum)) { prevXRefOffset = 0; } // if there was a problem with the xref table, or we haven't found the entry // we were looking for, try to reconstruct the xref if (!ok || (!prevXRefOffset && untilEntryNum != -1 && entries[untilEntryNum].type == xrefEntryNone)) { if (!xRefStream && !(ok = constructXRef(nullptr))) { errCode = errDamaged; break; } break; } } } namespace { struct DummyXRefEntry : XRefEntry { DummyXRefEntry() { offset = -1; gen = 0; type = xrefEntryNone; flags = 0; obj = Object(objNull); } }; DummyXRefEntry dummyXRefEntry; } XRefEntry *XRef::getEntry(int i, bool complainIfMissing) { if (unlikely(i < 0)) { error(errInternal, -1, "Request for invalid XRef entry [{0:d}]", i); return &dummyXRefEntry; } if (i >= size || entries[i].type == xrefEntryNone) { if ((!xRefStream) && mainXRefEntriesOffset) { if (unlikely(i >= capacity)) { error(errInternal, -1, "Request for out-of-bounds XRef entry [{0:d}]", i); return &dummyXRefEntry; } if (!parseEntry(mainXRefEntriesOffset + 20 * i, &entries[i])) { error(errSyntaxError, -1, "Failed to parse XRef entry [{0:d}].", i); return &dummyXRefEntry; } } else { // Read XRef tables until the entry we're looking for is found readXRefUntil(i); // We might have reconstructed the xref // Check again i is in bounds if (unlikely(i >= size)) { return &dummyXRefEntry; } if (entries[i].type == xrefEntryNone) { if (complainIfMissing) { error(errSyntaxError, -1, "Invalid XRef entry {0:d}", i); } entries[i].type = xrefEntryFree; } } } return &entries[i]; } // Recursively sets the Unencrypted flag in all referenced xref entries void XRef::markUnencrypted(Object *obj) { Object obj1; switch (obj->getType()) { case objArray: { Array *array = obj->getArray(); for (int i = 0; i < array->getLength(); i++) { obj1 = array->getNF(i).copy(); markUnencrypted(&obj1); } break; } case objStream: case objDict: { Dict *dict; if (obj->getType() == objStream) { Stream *stream = obj->getStream(); dict = stream->getDict(); } else { dict = obj->getDict(); } for (int i = 0; i < dict->getLength(); i++) { obj1 = dict->getValNF(i).copy(); markUnencrypted(&obj1); } break; } case objRef: { const Ref ref = obj->getRef(); XRefEntry *e = getEntry(ref.num); if (e->getFlag(XRefEntry::Unencrypted)) { return; // We've already been here: prevent infinite recursion } e->setFlag(XRefEntry::Unencrypted, true); obj1 = fetch(ref); markUnencrypted(&obj1); break; } default: break; } } void XRef::scanSpecialFlags() { if (scannedSpecialFlags) { return; } scannedSpecialFlags = true; // "Rewind" the XRef linked list, so that readXRefUntil re-reads all XRef // tables/streams, even those that had already been parsed prevXRefOffset = mainXRefOffset; std::vector xrefStreamObjNums; if (!streamEndsLen) { // don't do it for already reconstructed xref readXRefUntil(-1 /* read all xref sections */, &xrefStreamObjNums); } // Mark object streams as DontRewrite, because we write each object // individually in full rewrite mode. for (int i = 0; i < size; ++i) { if (entries[i].type == xrefEntryCompressed) { const Goffset objStmNum = entries[i].offset; if (unlikely(objStmNum < 0 || objStmNum >= size)) { error(errSyntaxError, -1, "Compressed object offset out of xref bounds"); } else { getEntry(static_cast(objStmNum))->setFlag(XRefEntry::DontRewrite, true); } } } // Mark XRef streams objects as Unencrypted and DontRewrite for (const int objNum : xrefStreamObjNums) { getEntry(objNum)->setFlag(XRefEntry::Unencrypted, true); getEntry(objNum)->setFlag(XRefEntry::DontRewrite, true); } // Mark objects referred from the Encrypt dict as Unencrypted markUnencrypted(); } void XRef::markUnencrypted() { // Mark objects referred from the Encrypt dict as Unencrypted const Object &obj = trailerDict.dictLookupNF("Encrypt"); if (obj.isRef()) { XRefEntry *e = getEntry(obj.getRefNum()); e->setFlag(XRefEntry::Unencrypted, true); } } XRef::XRefWriter::~XRefWriter() = default; poppler-24.02.0/poppler/XRef.h000066400000000000000000000307151455701731300160520ustar00rootroot00000000000000//======================================================================== // // XRef.h // // 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) 2005 Brad Hards // Copyright (C) 2006, 2008, 2010-2013, 2017-2022 Albert Astals Cid // Copyright (C) 2007-2008 Julien Rebetez // Copyright (C) 2007 Carlos Garcia Campos // Copyright (C) 2010 Ilya Gorenbein // Copyright (C) 2010 Hib Eris // Copyright (C) 2012, 2013, 2016 Thomas Freitag // Copyright (C) 2012, 2013 Fabio D'Urso // Copyright (C) 2013, 2017, 2019 Adrian Johnson // Copyright (C) 2016 Jakub Alba // Copyright (C) 2018 Adam Reichold // 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 // //======================================================================== #ifndef XREF_H #define XREF_H #include #include "poppler-config.h" #include "poppler_private_export.h" #include "Object.h" #include "Stream.h" #include "PopplerCache.h" class Dict; class Stream; class Parser; class ObjectStream; //------------------------------------------------------------------------ // XRef //------------------------------------------------------------------------ enum XRefEntryType { xrefEntryFree, xrefEntryUncompressed, xrefEntryCompressed, xrefEntryNone }; struct XRefEntry { Goffset offset; int gen; XRefEntryType type; int flags; Object obj; // if this entry was updated, obj will contains the updated object enum Flag { // Regular flags Updated, // Entry was modified Parsing, // Entry is currently being parsed // Special flags -- available only after xref->scanSpecialFlags() is run Unencrypted, // Entry is stored in unencrypted form (meaningless in unencrypted documents) DontRewrite // Entry must not be written back in case of full rewrite }; inline bool getFlag(Flag flag) const { const int mask = (1 << (int)flag); return (flags & mask) != 0; } inline void setFlag(Flag flag, bool value) { const int mask = (1 << (int)flag); if (value) { flags |= mask; } else { flags &= ~mask; } } }; // How to compress the a added stream enum class StreamCompression { None, /* No compression */ Compress, /* Compresses the stream */ }; class POPPLER_PRIVATE_EXPORT XRef { public: // Constructor, create an empty XRef, used for PDF writing XRef(); // Constructor, create an empty XRef but with info dict, used for PDF writing explicit XRef(const Object *trailerDictA); // Constructor. Read xref table from stream. XRef(BaseStream *strA, Goffset pos, Goffset mainXRefEntriesOffsetA = 0, bool *wasReconstructed = nullptr, bool reconstruct = false, const std::function &xrefReconstructedCallback = {}); // Destructor. ~XRef(); XRef(const XRef &) = delete; XRef &operator=(const XRef &) = delete; // Copy xref but with new base stream! XRef *copy() const; // Is xref table valid? bool isOk() const { return ok; } // Is the last XRef section a stream or a table? bool isXRefStream() const { return xRefStream; } // Get the error code (if isOk() returns false). int getErrorCode() const { return errCode; } // Set the encryption parameters. void setEncryption(int permFlagsA, bool ownerPasswordOkA, const unsigned char *fileKeyA, int keyLengthA, int encVersionA, int encRevisionA, CryptAlgorithm encAlgorithmA); // Mark Encrypt entry as Unencrypted void markUnencrypted(); void getEncryptionParameters(unsigned char **fileKeyA, CryptAlgorithm *encAlgorithmA, int *keyLengthA); // Is the file encrypted? bool isEncrypted() const { return encrypted; } // Is the given Ref encrypted? bool isRefEncrypted(Ref r); // Check various permissions. bool okToPrint(bool ignoreOwnerPW = false) const; bool okToPrintHighRes(bool ignoreOwnerPW = false) const; bool okToChange(bool ignoreOwnerPW = false) const; bool okToCopy(bool ignoreOwnerPW = false) const; bool okToAddNotes(bool ignoreOwnerPW = false) const; bool okToFillForm(bool ignoreOwnerPW = false) const; bool okToAccessibility(bool ignoreOwnerPW = false) const; bool okToAssemble(bool ignoreOwnerPW = false) const; int getPermFlags() const { return permFlags; } // Get catalog object. Object getCatalog(); // Fetch an indirect reference. Object fetch(const Ref ref, int recursion = 0); // If endPos is not null, returns file position after parsing the object. This will // be a few bytes after the end of the object due to the parser reading ahead. // Returns -1 if object is in compressed stream. Object fetch(int num, int gen, int recursion = 0, Goffset *endPos = nullptr); // Return the document's Info dictionary (if any). Object getDocInfo(); Object getDocInfoNF(); // Create and return the document's Info dictionary if needed. // Otherwise return the existing one. // Returns in the given parameter the Ref the Info is in Object createDocInfoIfNeeded(Ref *ref); // Remove the document's Info dictionary and update the trailer dictionary. void removeDocInfo(); // Return the number of objects in the xref table. int getNumObjects() const { return size; } // Return the catalog object reference. int getRootNum() const { return rootNum; } int getRootGen() const { return rootGen; } Ref getRoot() const { return { rootNum, rootGen }; } // Get end position for a stream in a damaged file. // Returns false if unknown or file is not damaged. bool getStreamEnd(Goffset streamStart, Goffset *streamEnd); // Retuns the entry that belongs to the offset int getNumEntry(Goffset offset); // Scans the document and sets special flags in all xref entries. One of those // flags is Unencrypted, which affects how the object is fetched. Therefore, // this function must be called before fetching unencrypted objects (e.g. // Encrypt dictionary, XRef streams). Note that the code that initializes // decryption doesn't need to call this function, because it runs before // decryption is enabled, and therefore the Unencrypted flag is ignored. void scanSpecialFlags(); // Direct access. XRefEntry *getEntry(int i, bool complainIfMissing = true); Object *getTrailerDict() { return &trailerDict; } // Was the XRef modified? bool isModified() const { return modified; } // Set the modification flag for XRef to true. void setModified() { modified = true; } // Write access void setModifiedObject(const Object *o, Ref r); Ref addIndirectObject(const Object &o); void removeIndirectObject(Ref r); bool add(int num, int gen, Goffset offs, bool used); void add(Ref ref, Goffset offs, bool used); // 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, 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); // Output XRef stream contents to GooString and fill trailerDict fields accordingly void writeStreamToBuffer(GooString *stmBuf, Dict *xrefDict, XRef *xref); // to be thread safe during write where changes are not allowed void lock(); void unlock(); private: BaseStream *str; // input stream Goffset start; // offset in file (to allow for garbage // at beginning of file) XRefEntry *entries; // xref entries int capacity; // size of array int size; // number of entries int rootNum, rootGen; // catalog dict bool ok; // true if xref table is valid int errCode; // error code (if is false) bool xrefReconstructed; // marker, true if xref was already reconstructed Object trailerDict; // trailer dictionary bool modified; Goffset *streamEnds; // 'endstream' positions - only used in // damaged files int streamEndsLen; // number of valid entries in streamEnds PopplerCache objStrs; // cached object streams bool encrypted; // true if file is encrypted int encRevision; int encVersion; // encryption algorithm CryptAlgorithm encAlgorithm; // encryption algorithm int keyLength; // length of key, in bytes int permFlags; // permission bits unsigned char fileKey[32]; // file decryption key bool ownerPasswordOk; // true if owner password is correct Goffset prevXRefOffset; // position of prev XRef section (= next to read) Goffset mainXRefEntriesOffset; // offset of entries in main XRef table bool xRefStream; // true if last XRef section is a stream Goffset mainXRefOffset; // position of the main XRef table/stream bool scannedSpecialFlags; // true if scanSpecialFlags has been called bool strOwner; // true if str is owned by the instance mutable std::recursive_mutex mutex; std::function xrefReconstructedCb; int reserve(int newSize); int resize(int newSize); bool readXRef(Goffset *pos, std::vector *followedXRefStm, std::vector *xrefStreamObjsNum); bool readXRefTable(Parser *parser, Goffset *pos, std::vector *followedXRefStm, std::vector *xrefStreamObjsNum); bool readXRefStreamSection(Stream *xrefStr, const int *w, int first, int n); bool readXRefStream(Stream *xrefStr, Goffset *pos); bool constructXRef(bool *wasReconstructed, bool needCatalogDict = false); bool parseEntry(Goffset offset, XRefEntry *entry); void readXRefUntil(int untilEntryNum, std::vector *xrefStreamObjsNum = nullptr); void markUnencrypted(Object *obj); class XRefWriter { public: XRefWriter() = default; virtual void startSection(int first, int count) = 0; virtual void writeEntry(Goffset offset, int gen, XRefEntryType type) = 0; virtual ~XRefWriter(); XRefWriter(const XRefWriter &) = delete; XRefWriter &operator=(const XRefWriter &other) = delete; }; // XRefWriter subclass that writes a XRef table class XRefTableWriter : public XRefWriter { public: explicit XRefTableWriter(OutStream *outStrA); void startSection(int first, int count) override; void writeEntry(Goffset offset, int gen, XRefEntryType type) override; private: OutStream *outStr; }; // XRefWriter subclass that writes a XRef stream class XRefStreamWriter : public XRefWriter { public: XRefStreamWriter(Array *index, GooString *stmBuf, int offsetSize); void startSection(int first, int count) override; void writeEntry(Goffset offset, int gen, XRefEntryType type) override; private: Array *index; GooString *stmBuf; int offsetSize; }; // Dummy XRefWriter subclass that only checks if all offsets fit in 4 bytes class XRefPreScanWriter : public XRefWriter { public: XRefPreScanWriter(); void startSection(int first, int count) override; void writeEntry(Goffset offset, int gen, XRefEntryType type) override; bool hasOffsetsBeyond4GB; }; void writeXRef(XRefWriter *writer, bool writeAllEntries); }; #endif poppler-24.02.0/poppler/ZapfDingbatsWidths.gperf000066400000000000000000000041601455701731300216140ustar00rootroot00000000000000%{ #include #include "BuiltinFontWidth.h" %} %language=ANSI-C %define initializer-suffix ,0 %define lookup-function-name ZapfDingbatsWidthsLookup %struct-type %omit-struct-type %readonly-tables struct BuiltinFontWidth %% #### a81, 438 a82, 138 a83, 277 a84, 415 a85, 509 a86, 410 a87, 234 a88, 234 a89, 390 a140, 788 a141, 788 a142, 788 a143, 788 a144, 788 a145, 788 a146, 788 a147, 788 a148, 788 a149, 788 a90, 390 a91, 276 a92, 276 space, 278 a93, 317 a94, 317 a95, 334 a96, 334 a97, 392 a98, 392 a99, 668 a150, 788 a151, 788 a152, 788 a153, 788 a154, 788 a155, 788 a156, 788 a157, 788 a158, 788 a159, 788 a160, 894 a161, 838 a162, 924 a163, 1016 a164, 458 a165, 924 a166, 918 a167, 927 a168, 928 a169, 928 a170, 834 a171, 873 a172, 828 a173, 924 a174, 917 a175, 930 a176, 931 a177, 463 a178, 883 a179, 836 a180, 867 a181, 696 a182, 874 a183, 760 a184, 946 a185, 865 a186, 967 a187, 831 a188, 873 a189, 927 a1, 974 a2, 961 a3, 980 a4, 719 a5, 789 a6, 494 a7, 552 a8, 537 a9, 577 a190, 970 a191, 918 a192, 748 a193, 836 a194, 771 a195, 888 a196, 748 a197, 771 a198, 888 a199, 867 a10, 692 a11, 960 a12, 939 a13, 549 a14, 855 a15, 911 a16, 933 a17, 945 a18, 974 a19, 755 a20, 846 a21, 762 a22, 761 a23, 571 a24, 677 a25, 763 a26, 760 a27, 759 a28, 754 a29, 786 a30, 788 a31, 788 a32, 790 a33, 793 a34, 794 a35, 816 a36, 823 a37, 789 a38, 841 a39, 823 a40, 833 a41, 816 a42, 831 a43, 923 a44, 744 a45, 723 a46, 749 a47, 790 a48, 792 a49, 695 a100, 668 a101, 732 a102, 544 a103, 544 a104, 910 a105, 911 a106, 667 a107, 760 a108, 760 a109, 626 a50, 776 a51, 768 a52, 792 a53, 759 a54, 707 a55, 708 a56, 682 a57, 701 a58, 826 a59, 815 a110, 694 a111, 595 a112, 776 a117, 690 a118, 791 a119, 790 a60, 789 a61, 789 a62, 707 a63, 687 a64, 696 a65, 689 a66, 786 a67, 787 a68, 713 a69, 791 a200, 696 a201, 874 a120, 788 a121, 788 a202, 974 a122, 788 a203, 762 a123, 788 a204, 759 a124, 788 a205, 509 a125, 788 a206, 410 a126, 788 a127, 788 a128, 788 a129, 788 a70, 785 a71, 791 a72, 873 a73, 761 a74, 762 a75, 759 a76, 892 a77, 892 a78, 788 a79, 784 a130, 788 a131, 788 a132, 788 a133, 788 a134, 788 a135, 788 a136, 788 a137, 788 a138, 788 a139, 788 #### %% poppler-24.02.0/poppler/ZapfDingbatsWidths.pregenerated.c000066400000000000000000000742631455701731300234120ustar00rootroot00000000000000/* ANSI-C code produced by gperf version 3.1 */ /* Command-line: gperf poppler/ZapfDingbatsWidths.gperf */ /* Computed positions: -k'2-4' */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) && ('-' == 45) && ('.' == 46) && ('/' == 47) \ && ('0' == 48) && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) && ('=' == 61) && ('>' == 62) \ && ('?' == 63) && ('A' == 65) && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) && ('N' == 78) \ && ('O' == 79) && ('P' == 80) && ('Q' == 81) && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) && ('k' == 107) && ('l' == 108) \ && ('m' == 109) && ('n' == 110) && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) /* The character set is not based on ISO-646. */ # error "gperf generated tables don't work with this execution character set. Please report a bug to ." #endif #line 1 "poppler/ZapfDingbatsWidths.gperf" #include #include "BuiltinFontWidth.h" #define TOTAL_KEYWORDS 202 #define MIN_WORD_LENGTH 2 #define MAX_WORD_LENGTH 5 #define MIN_HASH_VALUE 2 #define MAX_HASH_VALUE 402 /* maximum key range = 401, duplicates = 0 */ #ifdef __GNUC__ __inline #else # ifdef __cplusplus inline # endif #endif static unsigned int hash(register const char *str, register size_t len) { static const unsigned short asso_values[] = { 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 35, 18, 180, 160, 140, 120, 100, 80, 60, 15, 195, 185, 217, 207, 218, 203, 0, 5, 10, 200, 195, 190, 185, 149, 15, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 0, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 0, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 0, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403 }; register unsigned int hval = len; switch (hval) { default: hval += asso_values[(unsigned char)str[3] + 9]; /*FALLTHROUGH*/ case 3: hval += asso_values[(unsigned char)str[2]]; /*FALLTHROUGH*/ case 2: hval += asso_values[(unsigned char)str[1] + 15]; break; } return hval; } const struct BuiltinFontWidth *ZapfDingbatsWidthsLookup(register const char *str, register size_t len) { static const struct BuiltinFontWidth wordlist[] = { { "", 0 }, { "", 0 }, #line 84 "poppler/ZapfDingbatsWidths.gperf" { "a1", 974 }, { "", 0 }, { "", 0 }, #line 36 "poppler/ZapfDingbatsWidths.gperf" { "space", 278 }, { "", 0 }, #line 85 "poppler/ZapfDingbatsWidths.gperf" { "a2", 961 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 86 "poppler/ZapfDingbatsWidths.gperf" { "a3", 980 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 92 "poppler/ZapfDingbatsWidths.gperf" { "a9", 577 }, #line 112 "poppler/ZapfDingbatsWidths.gperf" { "a19", 755 }, #line 100 "poppler/ZapfDingbatsWidths.gperf" { "a197", 771 }, { "", 0 }, #line 104 "poppler/ZapfDingbatsWidths.gperf" { "a11", 960 }, #line 166 "poppler/ZapfDingbatsWidths.gperf" { "a117", 690 }, #line 122 "poppler/ZapfDingbatsWidths.gperf" { "a29", 786 }, #line 101 "poppler/ZapfDingbatsWidths.gperf" { "a198", 888 }, { "", 0 }, #line 114 "poppler/ZapfDingbatsWidths.gperf" { "a21", 762 }, #line 167 "poppler/ZapfDingbatsWidths.gperf" { "a118", 791 }, #line 132 "poppler/ZapfDingbatsWidths.gperf" { "a39", 823 }, #line 102 "poppler/ZapfDingbatsWidths.gperf" { "a199", 867 }, { "", 0 }, #line 124 "poppler/ZapfDingbatsWidths.gperf" { "a31", 788 }, #line 168 "poppler/ZapfDingbatsWidths.gperf" { "a119", 790 }, #line 43 "poppler/ZapfDingbatsWidths.gperf" { "a99", 668 }, #line 93 "poppler/ZapfDingbatsWidths.gperf" { "a190", 970 }, { "", 0 }, #line 34 "poppler/ZapfDingbatsWidths.gperf" { "a91", 276 }, #line 163 "poppler/ZapfDingbatsWidths.gperf" { "a110", 694 }, #line 103 "poppler/ZapfDingbatsWidths.gperf" { "a10", 692 }, #line 150 "poppler/ZapfDingbatsWidths.gperf" { "a107", 760 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 113 "poppler/ZapfDingbatsWidths.gperf" { "a20", 846 }, #line 151 "poppler/ZapfDingbatsWidths.gperf" { "a108", 760 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 123 "poppler/ZapfDingbatsWidths.gperf" { "a30", 788 }, #line 152 "poppler/ZapfDingbatsWidths.gperf" { "a109", 626 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 33 "poppler/ZapfDingbatsWidths.gperf" { "a90", 390 }, #line 143 "poppler/ZapfDingbatsWidths.gperf" { "a100", 668 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 179 "poppler/ZapfDingbatsWidths.gperf" { "a200", 696 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 111 "poppler/ZapfDingbatsWidths.gperf" { "a18", 974 }, #line 81 "poppler/ZapfDingbatsWidths.gperf" { "a187", 831 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 121 "poppler/ZapfDingbatsWidths.gperf" { "a28", 754 }, #line 82 "poppler/ZapfDingbatsWidths.gperf" { "a188", 873 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 131 "poppler/ZapfDingbatsWidths.gperf" { "a38", 841 }, #line 83 "poppler/ZapfDingbatsWidths.gperf" { "a189", 927 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 42 "poppler/ZapfDingbatsWidths.gperf" { "a98", 392 }, #line 74 "poppler/ZapfDingbatsWidths.gperf" { "a180", 867 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 110 "poppler/ZapfDingbatsWidths.gperf" { "a17", 945 }, #line 71 "poppler/ZapfDingbatsWidths.gperf" { "a177", 463 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 120 "poppler/ZapfDingbatsWidths.gperf" { "a27", 759 }, #line 72 "poppler/ZapfDingbatsWidths.gperf" { "a178", 883 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 130 "poppler/ZapfDingbatsWidths.gperf" { "a37", 789 }, #line 73 "poppler/ZapfDingbatsWidths.gperf" { "a179", 836 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 41 "poppler/ZapfDingbatsWidths.gperf" { "a97", 392 }, #line 64 "poppler/ZapfDingbatsWidths.gperf" { "a170", 834 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 109 "poppler/ZapfDingbatsWidths.gperf" { "a16", 933 }, #line 61 "poppler/ZapfDingbatsWidths.gperf" { "a167", 927 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 119 "poppler/ZapfDingbatsWidths.gperf" { "a26", 760 }, #line 62 "poppler/ZapfDingbatsWidths.gperf" { "a168", 928 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 129 "poppler/ZapfDingbatsWidths.gperf" { "a36", 823 }, #line 63 "poppler/ZapfDingbatsWidths.gperf" { "a169", 928 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 40 "poppler/ZapfDingbatsWidths.gperf" { "a96", 334 }, #line 54 "poppler/ZapfDingbatsWidths.gperf" { "a160", 894 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 108 "poppler/ZapfDingbatsWidths.gperf" { "a15", 911 }, #line 51 "poppler/ZapfDingbatsWidths.gperf" { "a157", 788 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 118 "poppler/ZapfDingbatsWidths.gperf" { "a25", 763 }, #line 52 "poppler/ZapfDingbatsWidths.gperf" { "a158", 788 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 128 "poppler/ZapfDingbatsWidths.gperf" { "a35", 816 }, #line 53 "poppler/ZapfDingbatsWidths.gperf" { "a159", 788 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 39 "poppler/ZapfDingbatsWidths.gperf" { "a95", 334 }, #line 44 "poppler/ZapfDingbatsWidths.gperf" { "a150", 788 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 107 "poppler/ZapfDingbatsWidths.gperf" { "a14", 855 }, #line 30 "poppler/ZapfDingbatsWidths.gperf" { "a147", 788 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 117 "poppler/ZapfDingbatsWidths.gperf" { "a24", 677 }, #line 31 "poppler/ZapfDingbatsWidths.gperf" { "a148", 788 }, { "", 0 }, #line 91 "poppler/ZapfDingbatsWidths.gperf" { "a8", 537 }, { "", 0 }, #line 127 "poppler/ZapfDingbatsWidths.gperf" { "a34", 794 }, #line 32 "poppler/ZapfDingbatsWidths.gperf" { "a149", 788 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 38 "poppler/ZapfDingbatsWidths.gperf" { "a94", 317 }, #line 23 "poppler/ZapfDingbatsWidths.gperf" { "a140", 788 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 106 "poppler/ZapfDingbatsWidths.gperf" { "a13", 549 }, #line 213 "poppler/ZapfDingbatsWidths.gperf" { "a137", 788 }, { "", 0 }, { "", 0 }, #line 22 "poppler/ZapfDingbatsWidths.gperf" { "a89", 390 }, #line 116 "poppler/ZapfDingbatsWidths.gperf" { "a23", 571 }, #line 214 "poppler/ZapfDingbatsWidths.gperf" { "a138", 788 }, #line 14 "poppler/ZapfDingbatsWidths.gperf" { "a81", 438 }, { "", 0 }, { "", 0 }, #line 126 "poppler/ZapfDingbatsWidths.gperf" { "a33", 793 }, #line 215 "poppler/ZapfDingbatsWidths.gperf" { "a139", 788 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 37 "poppler/ZapfDingbatsWidths.gperf" { "a93", 317 }, #line 206 "poppler/ZapfDingbatsWidths.gperf" { "a130", 788 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 105 "poppler/ZapfDingbatsWidths.gperf" { "a12", 939 }, #line 193 "poppler/ZapfDingbatsWidths.gperf" { "a127", 788 }, { "", 0 }, { "", 0 }, #line 90 "poppler/ZapfDingbatsWidths.gperf" { "a7", 552 }, #line 115 "poppler/ZapfDingbatsWidths.gperf" { "a22", 761 }, #line 194 "poppler/ZapfDingbatsWidths.gperf" { "a128", 788 }, { "", 0 }, { "", 0 }, #line 89 "poppler/ZapfDingbatsWidths.gperf" { "a6", 494 }, #line 125 "poppler/ZapfDingbatsWidths.gperf" { "a32", 790 }, #line 195 "poppler/ZapfDingbatsWidths.gperf" { "a129", 788 }, { "", 0 }, { "", 0 }, #line 88 "poppler/ZapfDingbatsWidths.gperf" { "a5", 789 }, #line 35 "poppler/ZapfDingbatsWidths.gperf" { "a92", 276 }, #line 181 "poppler/ZapfDingbatsWidths.gperf" { "a120", 788 }, { "", 0 }, { "", 0 }, #line 87 "poppler/ZapfDingbatsWidths.gperf" { "a4", 719 }, #line 205 "poppler/ZapfDingbatsWidths.gperf" { "a79", 784 }, #line 95 "poppler/ZapfDingbatsWidths.gperf" { "a192", 748 }, { "", 0 }, #line 197 "poppler/ZapfDingbatsWidths.gperf" { "a71", 791 }, #line 165 "poppler/ZapfDingbatsWidths.gperf" { "a112", 776 }, #line 178 "poppler/ZapfDingbatsWidths.gperf" { "a69", 791 }, { "", 0 }, { "", 0 }, #line 170 "poppler/ZapfDingbatsWidths.gperf" { "a61", 789 }, #line 21 "poppler/ZapfDingbatsWidths.gperf" { "a88", 234 }, #line 162 "poppler/ZapfDingbatsWidths.gperf" { "a59", 815 }, #line 94 "poppler/ZapfDingbatsWidths.gperf" { "a191", 918 }, { "", 0 }, #line 154 "poppler/ZapfDingbatsWidths.gperf" { "a51", 768 }, #line 164 "poppler/ZapfDingbatsWidths.gperf" { "a111", 595 }, #line 142 "poppler/ZapfDingbatsWidths.gperf" { "a49", 695 }, { "", 0 }, { "", 0 }, #line 134 "poppler/ZapfDingbatsWidths.gperf" { "a41", 816 }, #line 99 "poppler/ZapfDingbatsWidths.gperf" { "a196", 748 }, #line 196 "poppler/ZapfDingbatsWidths.gperf" { "a70", 785 }, #line 145 "poppler/ZapfDingbatsWidths.gperf" { "a102", 544 }, { "", 0 }, #line 97 "poppler/ZapfDingbatsWidths.gperf" { "a194", 771 }, { "", 0 }, #line 169 "poppler/ZapfDingbatsWidths.gperf" { "a60", 789 }, #line 183 "poppler/ZapfDingbatsWidths.gperf" { "a202", 974 }, { "", 0 }, { "", 0 }, #line 20 "poppler/ZapfDingbatsWidths.gperf" { "a87", 234 }, #line 153 "poppler/ZapfDingbatsWidths.gperf" { "a50", 776 }, #line 144 "poppler/ZapfDingbatsWidths.gperf" { "a101", 732 }, { "", 0 }, #line 96 "poppler/ZapfDingbatsWidths.gperf" { "a193", 836 }, #line 98 "poppler/ZapfDingbatsWidths.gperf" { "a195", 888 }, #line 133 "poppler/ZapfDingbatsWidths.gperf" { "a40", 833 }, #line 180 "poppler/ZapfDingbatsWidths.gperf" { "a201", 874 }, { "", 0 }, { "", 0 }, #line 149 "poppler/ZapfDingbatsWidths.gperf" { "a106", 667 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 147 "poppler/ZapfDingbatsWidths.gperf" { "a104", 910 }, #line 191 "poppler/ZapfDingbatsWidths.gperf" { "a206", 410 }, #line 204 "poppler/ZapfDingbatsWidths.gperf" { "a78", 788 }, #line 76 "poppler/ZapfDingbatsWidths.gperf" { "a182", 874 }, { "", 0 }, #line 187 "poppler/ZapfDingbatsWidths.gperf" { "a204", 759 }, #line 19 "poppler/ZapfDingbatsWidths.gperf" { "a86", 410 }, #line 177 "poppler/ZapfDingbatsWidths.gperf" { "a68", 713 }, { "", 0 }, { "", 0 }, #line 146 "poppler/ZapfDingbatsWidths.gperf" { "a103", 544 }, #line 148 "poppler/ZapfDingbatsWidths.gperf" { "a105", 911 }, #line 161 "poppler/ZapfDingbatsWidths.gperf" { "a58", 826 }, #line 75 "poppler/ZapfDingbatsWidths.gperf" { "a181", 696 }, { "", 0 }, #line 185 "poppler/ZapfDingbatsWidths.gperf" { "a203", 762 }, #line 189 "poppler/ZapfDingbatsWidths.gperf" { "a205", 509 }, #line 141 "poppler/ZapfDingbatsWidths.gperf" { "a48", 792 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 80 "poppler/ZapfDingbatsWidths.gperf" { "a186", 967 }, #line 203 "poppler/ZapfDingbatsWidths.gperf" { "a77", 892 }, #line 66 "poppler/ZapfDingbatsWidths.gperf" { "a172", 828 }, { "", 0 }, #line 78 "poppler/ZapfDingbatsWidths.gperf" { "a184", 946 }, #line 18 "poppler/ZapfDingbatsWidths.gperf" { "a85", 509 }, #line 176 "poppler/ZapfDingbatsWidths.gperf" { "a67", 787 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 160 "poppler/ZapfDingbatsWidths.gperf" { "a57", 701 }, #line 65 "poppler/ZapfDingbatsWidths.gperf" { "a171", 873 }, { "", 0 }, #line 77 "poppler/ZapfDingbatsWidths.gperf" { "a183", 760 }, #line 79 "poppler/ZapfDingbatsWidths.gperf" { "a185", 865 }, #line 140 "poppler/ZapfDingbatsWidths.gperf" { "a47", 790 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 70 "poppler/ZapfDingbatsWidths.gperf" { "a176", 931 }, #line 202 "poppler/ZapfDingbatsWidths.gperf" { "a76", 892 }, #line 56 "poppler/ZapfDingbatsWidths.gperf" { "a162", 924 }, { "", 0 }, #line 68 "poppler/ZapfDingbatsWidths.gperf" { "a174", 917 }, #line 17 "poppler/ZapfDingbatsWidths.gperf" { "a84", 415 }, #line 175 "poppler/ZapfDingbatsWidths.gperf" { "a66", 786 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 159 "poppler/ZapfDingbatsWidths.gperf" { "a56", 682 }, #line 55 "poppler/ZapfDingbatsWidths.gperf" { "a161", 838 }, { "", 0 }, #line 67 "poppler/ZapfDingbatsWidths.gperf" { "a173", 924 }, #line 69 "poppler/ZapfDingbatsWidths.gperf" { "a175", 930 }, #line 139 "poppler/ZapfDingbatsWidths.gperf" { "a46", 749 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 60 "poppler/ZapfDingbatsWidths.gperf" { "a166", 918 }, #line 201 "poppler/ZapfDingbatsWidths.gperf" { "a75", 759 }, #line 46 "poppler/ZapfDingbatsWidths.gperf" { "a152", 788 }, { "", 0 }, #line 58 "poppler/ZapfDingbatsWidths.gperf" { "a164", 458 }, #line 16 "poppler/ZapfDingbatsWidths.gperf" { "a83", 277 }, #line 174 "poppler/ZapfDingbatsWidths.gperf" { "a65", 689 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 158 "poppler/ZapfDingbatsWidths.gperf" { "a55", 708 }, #line 45 "poppler/ZapfDingbatsWidths.gperf" { "a151", 788 }, { "", 0 }, #line 57 "poppler/ZapfDingbatsWidths.gperf" { "a163", 1016 }, #line 59 "poppler/ZapfDingbatsWidths.gperf" { "a165", 924 }, #line 138 "poppler/ZapfDingbatsWidths.gperf" { "a45", 723 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 50 "poppler/ZapfDingbatsWidths.gperf" { "a156", 788 }, #line 200 "poppler/ZapfDingbatsWidths.gperf" { "a74", 762 }, #line 25 "poppler/ZapfDingbatsWidths.gperf" { "a142", 788 }, { "", 0 }, #line 48 "poppler/ZapfDingbatsWidths.gperf" { "a154", 788 }, #line 15 "poppler/ZapfDingbatsWidths.gperf" { "a82", 138 }, #line 173 "poppler/ZapfDingbatsWidths.gperf" { "a64", 696 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 157 "poppler/ZapfDingbatsWidths.gperf" { "a54", 707 }, #line 24 "poppler/ZapfDingbatsWidths.gperf" { "a141", 788 }, { "", 0 }, #line 47 "poppler/ZapfDingbatsWidths.gperf" { "a153", 788 }, #line 49 "poppler/ZapfDingbatsWidths.gperf" { "a155", 788 }, #line 137 "poppler/ZapfDingbatsWidths.gperf" { "a44", 744 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 29 "poppler/ZapfDingbatsWidths.gperf" { "a146", 788 }, #line 199 "poppler/ZapfDingbatsWidths.gperf" { "a73", 761 }, #line 208 "poppler/ZapfDingbatsWidths.gperf" { "a132", 788 }, { "", 0 }, #line 27 "poppler/ZapfDingbatsWidths.gperf" { "a144", 788 }, { "", 0 }, #line 172 "poppler/ZapfDingbatsWidths.gperf" { "a63", 687 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 156 "poppler/ZapfDingbatsWidths.gperf" { "a53", 759 }, #line 207 "poppler/ZapfDingbatsWidths.gperf" { "a131", 788 }, { "", 0 }, #line 26 "poppler/ZapfDingbatsWidths.gperf" { "a143", 788 }, #line 28 "poppler/ZapfDingbatsWidths.gperf" { "a145", 788 }, #line 136 "poppler/ZapfDingbatsWidths.gperf" { "a43", 923 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 212 "poppler/ZapfDingbatsWidths.gperf" { "a136", 788 }, #line 198 "poppler/ZapfDingbatsWidths.gperf" { "a72", 873 }, #line 184 "poppler/ZapfDingbatsWidths.gperf" { "a122", 788 }, { "", 0 }, #line 210 "poppler/ZapfDingbatsWidths.gperf" { "a134", 788 }, { "", 0 }, #line 171 "poppler/ZapfDingbatsWidths.gperf" { "a62", 707 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 155 "poppler/ZapfDingbatsWidths.gperf" { "a52", 792 }, #line 182 "poppler/ZapfDingbatsWidths.gperf" { "a121", 788 }, { "", 0 }, #line 209 "poppler/ZapfDingbatsWidths.gperf" { "a133", 788 }, #line 211 "poppler/ZapfDingbatsWidths.gperf" { "a135", 788 }, #line 135 "poppler/ZapfDingbatsWidths.gperf" { "a42", 831 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 192 "poppler/ZapfDingbatsWidths.gperf" { "a126", 788 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 188 "poppler/ZapfDingbatsWidths.gperf" { "a124", 788 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, #line 186 "poppler/ZapfDingbatsWidths.gperf" { "a123", 788 }, #line 190 "poppler/ZapfDingbatsWidths.gperf" { "a125", 788 } }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) { register unsigned int key = hash(str, len); if (key <= MAX_HASH_VALUE) { register const char *s = wordlist[key].name; if (*str == *s && !strcmp(str + 1, s + 1)) return &wordlist[key]; } } return 0; } #line 217 "poppler/ZapfDingbatsWidths.gperf" poppler-24.02.0/poppler/annot_stamp_approved.h000066400000000000000000001057741455701731300214410ustar00rootroot00000000000000//======================================================================== // // annot_stamp_approved.h // // Copyright (C) 2021 Mahmoud Ahmed Khalil // Copyright (C) 2021 Albert Astals Cid // // Mechanically extracted from an SVG created for Okular by Eugene Trounev eugene.trounev@gmail.com // // Licensed under GPLv2 or later // //======================================================================== #ifndef ANNOT_STAMP_APPROVED_H #define ANNOT_STAMP_APPROVED_H #include "Dict.h" #include "Object.h" #include "PDFDoc.h" static const double ANNOT_STAMP_APPROVED_WIDTH = 127.008179; static const double ANNOT_STAMP_APPROVED_HEIGHT = 26.484743; static const char *ANNOT_STAMP_APPROVED = "1 0 0 -1 0 26.484744 cm\n" "q\n" "1 1 1 rg /a0 gs\n" "3.406 2.129 m 123.617 2.129 l 124.316 2.129 124.887 2.828 124.887 3.398\n" " c 124.887 23.09 l 124.887 23.789 124.32 24.359 123.617 24.359 c 3.406 24.359\n" " l 2.707 24.359 2.137 23.66 2.137 23.09 c 2.137 3.398 l 2.137 2.699 2.703\n" " 2.129 3.406 2.129 c h\n" "3.406 2.129 m f\n" "0 0.298039 0.431373 rg /a1 gs\n" "23.453 20.828 m 22.086 16.895 l 16.219 16.895 l 14.852 20.828 l 11.629 \n" "20.828 l 17.246 5.434 l 21.047 5.434 l 26.641 20.828 l 23.449 20.828 l 19.844\n" " 10.043 m 19.758 9.789 19.668 9.531 19.582 9.277 c 19.5 9.016 19.43 8.777\n" " 19.363 8.566 c 19.305 8.348 19.254 8.168 19.211 8.031 c 19.176 7.895 19.152\n" " 7.816 19.145 7.801 c 19.137 7.824 19.117 7.902 19.078 8.043 c 19.043 8.18\n" " 18.992 8.355 18.926 8.566 c 18.867 8.777 18.793 9.016 18.707 9.277 c 18.625\n" " 9.531 18.543 9.789 18.457 10.043 c 16.938 14.469 l 21.363 14.469 l 19.844\n" " 10.043 l 39.992 14.863 m 39.992 15.758 39.902 16.586 39.719 17.344 c 39.543\n" " 18.102 39.27 18.754 38.898 19.301 c 38.527 19.848 38.055 20.277 37.477 \n" "20.59 c 36.91 20.895 36.234 21.047 35.457 21.047 c 35.105 21.047 34.758 \n" "21.012 34.406 20.938 c 34.062 20.863 33.734 20.75 33.422 20.586 c 33.109\n" " 20.418 32.816 20.203 32.547 19.941 c 32.285 19.672 32.059 19.34 31.871 \n" "18.945 c 31.805 18.945 l 31.812 18.98 31.82 19.074 31.828 19.219 c 31.836\n" " 19.363 31.844 19.535 31.852 19.73 c 31.859 19.918 31.863 20.125 31.863 \n" "20.344 c 31.871 20.555 31.875 20.75 31.875 20.934 c 31.875 25.469 l 28.805\n" " 25.469 l 28.805 11.723 l 28.805 11.117 28.793 10.578 28.773 10.105 c 28.758\n" " 9.633 28.742 9.266 28.719 9 c 31.703 9 l 31.719 9.051 31.73 9.148 31.746\n" " 9.297 c 31.77 9.441 31.781 9.609 31.789 9.801 c 31.805 9.988 31.816 10.188\n" " 31.82 10.391 c 31.828 10.594 31.832 10.773 31.832 10.926 c 31.875 10.926\n" " l 32.246 10.145 32.758 9.59 33.406 9.254 c 34.055 8.918 34.805 8.75 35.656\n" " 8.75 c 36.406 8.75 37.055 8.902 37.602 9.207 c 38.148 9.512 38.598 9.934\n" " 38.945 10.473 c 39.301 11.012 39.566 11.656 39.73 12.406 c 39.906 13.148\n" " 39.992 13.965 39.992 14.855 c 36.789 14.855 m 36.789 13.508 36.586 12.512\n" " 36.176 11.863 c 35.77 11.207 35.16 10.879 34.352 10.879 c 34.047 10.879\n" " 33.738 10.945 33.434 11.074 c 33.137 11.199 32.867 11.418 32.625 11.73 \n" "c 32.391 12.035 32.199 12.453 32.047 12.977 c 31.902 13.492 31.828 14.148\n" " 31.828 14.945 c 31.828 15.719 31.902 16.363 32.047 16.879 c 32.191 17.391\n" " 32.383 17.797 32.613 18.102 c 32.855 18.406 33.125 18.625 33.422 18.758\n" " c 33.719 18.883 34.023 18.945 34.328 18.945 c 34.723 18.945 35.07 18.867\n" " 35.379 18.715 c 35.684 18.555 35.941 18.312 36.145 17.984 c 36.355 17.648\n" " 36.516 17.223 36.625 16.707 c 36.734 16.191 36.789 15.574 36.789 14.859\n" " c 53.672 14.859 m 53.672 15.754 53.582 16.582 53.398 17.34 c 53.223 18.098\n" " 52.949 18.75 52.578 19.297 c 52.207 19.844 51.734 20.273 51.156 20.586 \n" "c 50.59 20.891 49.914 21.043 49.137 21.043 c 48.785 21.043 48.438 21.008\n" " 48.086 20.934 c 47.742 20.859 47.414 20.746 47.102 20.582 c 46.789 20.414\n" " 46.496 20.199 46.227 19.938 c 45.965 19.668 45.738 19.336 45.551 18.941\n" " c 45.484 18.941 l 45.492 18.977 45.5 19.07 45.508 19.215 c 45.516 19.359\n" " 45.523 19.531 45.531 19.727 c 45.539 19.914 45.543 20.121 45.543 20.34 \n" "c 45.551 20.551 45.555 20.746 45.555 20.93 c 45.555 25.465 l 42.484 25.465\n" " l 42.484 11.719 l 42.484 11.113 42.473 10.574 42.453 10.102 c 42.438 9.629\n" " 42.422 9.262 42.398 8.996 c 45.383 8.996 l 45.398 9.047 45.41 9.145 45.426\n" " 9.293 c 45.449 9.438 45.461 9.605 45.469 9.797 c 45.484 9.984 45.496 10.184\n" " 45.5 10.387 c 45.508 10.59 45.512 10.77 45.512 10.922 c 45.555 10.922 l\n" " 45.926 10.141 46.438 9.586 47.086 9.25 c 47.734 8.914 48.484 8.746 49.336\n" " 8.746 c 50.086 8.746 50.734 8.898 51.281 9.203 c 51.828 9.508 52.277 9.93\n" " 52.625 10.469 c 52.98 11.008 53.246 11.652 53.41 12.402 c 53.586 13.145\n" " 53.672 13.961 53.672 14.852 c 50.469 14.852 m 50.469 13.504 50.266 12.508\n" " 49.855 11.859 c 49.449 11.203 48.84 10.875 48.031 10.875 c 47.727 10.875\n" " 47.418 10.941 47.113 11.07 c 46.816 11.195 46.547 11.414 46.305 11.727 \n" "c 46.07 12.031 45.879 12.449 45.727 12.973 c 45.582 13.488 45.508 14.145\n" " 45.508 14.941 c 45.508 15.715 45.582 16.359 45.727 16.875 c 45.871 17.387\n" " 46.062 17.793 46.293 18.098 c 46.535 18.402 46.805 18.621 47.102 18.754\n" " c 47.398 18.879 47.703 18.941 48.008 18.941 c 48.402 18.941 48.75 18.863\n" " 49.059 18.711 c 49.363 18.551 49.621 18.309 49.824 17.98 c 50.035 17.645\n" " 50.195 17.219 50.305 16.703 c 50.414 16.188 50.469 15.57 50.469 14.855 \n" "c 56.16 20.82 m 56.16 11.773 l 56.16 11.52 56.156 11.25 56.148 10.965 c \n" "56.148 10.68 56.141 10.41 56.125 10.156 c 56.117 9.895 56.109 9.66 56.102\n" " 9.457 c 56.094 9.246 56.082 9.094 56.07 9 c 59 9 l 59.016 9.086 59.027 \n" "9.242 59.043 9.457 c 59.059 9.668 59.07 9.902 59.086 10.156 c 59.102 10.41\n" " 59.113 10.668 59.117 10.922 c 59.133 11.168 59.141 11.371 59.141 11.523\n" " c 59.184 11.523 l 59.336 11.094 59.488 10.711 59.641 10.375 c 59.793 10.031\n" " 59.973 9.746 60.176 9.512 c 60.387 9.27 60.637 9.09 60.93 8.965 c 61.223\n" " 8.832 61.582 8.77 62.012 8.77 c 62.195 8.77 62.371 8.789 62.547 8.824 c\n" " 62.73 8.852 62.867 8.891 62.961 8.934 c 62.961 11.5 l 62.766 11.457 62.562\n" " 11.418 62.359 11.391 c 62.164 11.355 61.926 11.336 61.648 11.336 c 60.883\n" " 11.336 60.285 11.645 59.855 12.266 c 59.434 12.887 59.223 13.801 59.223\n" " 15.02 c 59.223 20.82 l 56.152 20.82 l 76.082 14.898 m 76.082 15.801 75.953\n" " 16.629 75.699 17.379 c 75.453 18.129 75.078 18.777 74.574 19.324 c 74.07\n" " 19.863 73.445 20.285 72.695 20.59 c 71.945 20.887 71.07 21.039 70.074 21.039\n" " c 69.113 21.039 68.266 20.891 67.527 20.59 c 66.793 20.293 66.172 19.871\n" " 65.668 19.332 c 65.172 18.793 64.797 18.148 64.543 17.398 c 64.289 16.641\n" " 64.16 15.809 64.16 14.895 c 64.16 14.012 64.281 13.199 64.52 12.457 c 64.766\n" " 11.707 65.141 11.059 65.633 10.512 c 66.129 9.965 66.75 9.539 67.5 9.234\n" " c 68.25 8.93 69.129 8.777 70.133 8.777 c 71.195 8.777 72.102 8.93 72.855\n" " 9.234 c 73.605 9.539 74.219 9.965 74.691 10.512 c 75.172 11.051 75.523 \n" "11.695 75.742 12.445 c 75.969 13.188 76.082 14.004 76.082 14.895 c 72.871\n" " 14.895 m 72.871 13.488 72.641 12.469 72.184 11.836 c 71.727 11.203 71.059\n" " 10.887 70.184 10.887 c 69.281 10.887 68.59 11.207 68.109 11.848 c 67.629\n" " 12.488 67.387 13.504 67.387 14.895 c 67.387 15.602 67.449 16.211 67.574\n" " 16.719 c 67.707 17.23 67.887 17.648 68.121 17.977 c 68.355 18.305 68.633\n" " 18.547 68.961 18.707 c 69.289 18.859 69.648 18.938 70.043 18.938 c 70.496\n" " 18.938 70.895 18.859 71.246 18.707 c 71.602 18.547 71.902 18.305 72.141\n" " 17.977 c 72.383 17.648 72.562 17.23 72.688 16.719 c 72.812 16.207 72.875\n" " 15.602 72.875 14.895 c 84.961 20.816 m 81.289 20.816 l 77.059 8.992 l 80.305\n" " 8.992 l 82.371 15.602 l 82.438 15.82 82.504 16.055 82.578 16.301 c 82.652\n" " 16.543 82.719 16.781 82.785 17.023 c 82.852 17.266 82.914 17.496 82.973\n" " 17.723 c 83.039 17.949 83.098 18.152 83.148 18.336 c 83.191 18.16 83.246\n" " 17.965 83.312 17.746 c 83.379 17.52 83.445 17.289 83.508 17.047 c 83.582\n" " 16.805 83.652 16.566 83.727 16.324 c 83.809 16.082 83.883 15.855 83.957\n" " 15.637 c 86.109 8.992 l 89.32 8.992 l 84.961 20.816 l 95.832 21.035 m 94.98\n" " 21.035 94.211 20.91 93.527 20.664 c 92.852 20.41 92.27 20.027 91.789 19.516\n" " c 91.309 19 90.941 18.355 90.684 17.582 c 90.43 16.801 90.301 15.891 90.301\n" " 14.852 c 90.301 13.723 90.449 12.773 90.75 12 c 91.055 11.227 91.465 10.605\n" " 91.973 10.133 c 92.488 9.652 93.082 9.305 93.754 9.094 c 94.426 8.883 95.129\n" " 8.777 95.875 8.777 c 96.809 8.777 97.602 8.941 98.258 9.27 c 98.922 9.59\n" " 99.465 10.043 99.887 10.625 c 100.309 11.207 100.617 11.906 100.816 12.723\n" " c 101.012 13.531 101.113 14.426 101.113 15.41 c 101.113 15.496 l 93.539\n" " 15.508 l 93.539 16.004 93.582 16.465 93.672 16.895 c 93.758 17.316 93.902\n" " 17.684 94.098 18 c 94.293 18.305 94.551 18.551 94.863 18.73 c 95.176 18.906\n" " 95.555 18.992 96 18.992 c 96.539 18.992 96.98 18.879 97.32 18.652 c 97.664\n" " 18.418 97.906 18.062 98.051 17.582 c 100.945 17.832 l 100.812 18.168 100.629\n" " 18.523 100.387 18.902 c 100.152 19.281 99.84 19.629 99.445 19.953 c 99.051\n" " 20.266 98.559 20.527 97.969 20.738 c 97.387 20.941 96.676 21.043 95.84 \n" "21.043 c 95.84 10.719 m 95.527 10.719 95.23 10.773 94.953 10.883 c 94.684\n" " 10.984 94.445 11.152 94.242 11.387 c 94.047 11.613 93.887 11.906 93.762\n" " 12.273 c 93.637 12.637 93.57 13.074 93.555 13.586 c 98.145 13.586 l 98.086\n" " 12.633 97.855 11.918 97.457 11.445 c 97.055 10.965 96.516 10.723 95.84 \n" "10.723 c 111.117 20.828 m 111.102 20.77 111.086 20.668 111.062 20.523 c \n" "111.047 20.371 111.031 20.199 111.008 20.012 c 110.992 19.824 110.98 19.629\n" " 110.965 19.434 c 110.957 19.238 110.953 19.062 110.953 18.91 c 110.91 18.91\n" " l 110.555 19.676 110.055 20.227 109.414 20.559 c 108.781 20.887 108.02 \n" "21.051 107.129 21.051 c 106.387 21.051 105.742 20.898 105.195 20.594 c 104.656\n" " 20.289 104.207 19.863 103.852 19.316 c 103.5 18.77 103.238 18.125 103.066\n" " 17.383 c 102.898 16.633 102.816 15.816 102.816 14.934 c 102.816 14.039 \n" "102.902 13.215 103.078 12.465 c 103.262 11.715 103.535 11.07 103.91 10.531\n" " c 104.281 9.984 104.75 9.559 105.32 9.254 c 105.895 8.949 106.578 8.797\n" " 107.363 8.797 c 107.75 8.797 108.121 8.836 108.477 8.918 c 108.832 9 109.164\n" " 9.121 109.473 9.289 c 109.777 9.457 110.055 9.672 110.305 9.934 c 110.551\n" " 10.195 110.762 10.512 110.938 10.883 c 110.961 10.883 l 110.961 10.809 \n" "110.957 10.703 110.949 10.566 c 110.949 10.422 110.949 10.258 110.949 10.074\n" " c 110.949 9.891 110.945 9.703 110.938 9.508 c 110.938 9.312 110.938 9.121\n" " 110.938 8.941 c 110.938 4.625 l 114.008 4.625 l 114.008 18.262 l 114.008\n" " 18.836 114.02 19.352 114.039 19.801 c 114.062 20.246 114.078 20.59 114.094\n" " 20.84 c 111.121 20.84 l 110.98 14.863 m 110.98 14.082 110.906 13.438 110.762\n" " 12.93 c 110.617 12.414 110.422 12.004 110.184 11.707 c 109.949 11.402 109.68\n" " 11.191 109.375 11.074 c 109.078 10.949 108.77 10.887 108.457 10.887 c 108.062\n" " 10.887 107.715 10.965 107.406 11.117 c 107.109 11.27 106.852 11.512 106.641\n" " 11.84 c 106.438 12.168 106.281 12.586 106.172 13.098 c 106.07 13.609 106.02\n" " 14.223 106.02 14.945 c 106.02 17.625 106.824 18.965 108.434 18.965 c 108.738\n" " 18.965 109.047 18.898 109.352 18.77 c 109.656 18.637 109.93 18.418 110.172\n" " 18.102 c 110.414 17.789 110.605 17.371 110.75 16.844 c 110.902 16.312 110.98\n" " 15.652 110.98 14.867 c f\n" "1 1 1 RG 0.531496 w\n" "0 J\n" "0 j\n" "[] 0.0 d\n" "4 M q 1 0 0 1 0 0 cm\n" "23.453 20.828 m 22.086 16.895 l 16.219 16.895 l 14.852 20.828 l 11.629 \n" "20.828 l 17.246 5.434 l 21.047 5.434 l 26.641 20.828 l 23.449 20.828 l 19.844\n" " 10.043 m 19.758 9.789 19.668 9.531 19.582 9.277 c 19.5 9.016 19.43 8.777\n" " 19.363 8.566 c 19.305 8.348 19.254 8.168 19.211 8.031 c 19.176 7.895 19.152\n" " 7.816 19.145 7.801 c 19.137 7.824 19.117 7.902 19.078 8.043 c 19.043 8.18\n" " 18.992 8.355 18.926 8.566 c 18.867 8.777 18.793 9.016 18.707 9.277 c 18.625\n" " 9.531 18.543 9.789 18.457 10.043 c 16.938 14.469 l 21.363 14.469 l 19.844\n" " 10.043 l 39.992 14.863 m 39.992 15.758 39.902 16.586 39.719 17.344 c 39.543\n" " 18.102 39.27 18.754 38.898 19.301 c 38.527 19.848 38.055 20.277 37.477 \n" "20.59 c 36.91 20.895 36.234 21.047 35.457 21.047 c 35.105 21.047 34.758 \n" "21.012 34.406 20.938 c 34.062 20.863 33.734 20.75 33.422 20.586 c 33.109\n" " 20.418 32.816 20.203 32.547 19.941 c 32.285 19.672 32.059 19.34 31.871 \n" "18.945 c 31.805 18.945 l 31.812 18.98 31.82 19.074 31.828 19.219 c 31.836\n" " 19.363 31.844 19.535 31.852 19.73 c 31.859 19.918 31.863 20.125 31.863 \n" "20.344 c 31.871 20.555 31.875 20.75 31.875 20.934 c 31.875 25.469 l 28.805\n" " 25.469 l 28.805 11.723 l 28.805 11.117 28.793 10.578 28.773 10.105 c 28.758\n" " 9.633 28.742 9.266 28.719 9 c 31.703 9 l 31.719 9.051 31.73 9.148 31.746\n" " 9.297 c 31.77 9.441 31.781 9.609 31.789 9.801 c 31.805 9.988 31.816 10.188\n" " 31.82 10.391 c 31.828 10.594 31.832 10.773 31.832 10.926 c 31.875 10.926\n" " l 32.246 10.145 32.758 9.59 33.406 9.254 c 34.055 8.918 34.805 8.75 35.656\n" " 8.75 c 36.406 8.75 37.055 8.902 37.602 9.207 c 38.148 9.512 38.598 9.934\n" " 38.945 10.473 c 39.301 11.012 39.566 11.656 39.73 12.406 c 39.906 13.148\n" " 39.992 13.965 39.992 14.855 c 36.789 14.855 m 36.789 13.508 36.586 12.512\n" " 36.176 11.863 c 35.77 11.207 35.16 10.879 34.352 10.879 c 34.047 10.879\n" " 33.738 10.945 33.434 11.074 c 33.137 11.199 32.867 11.418 32.625 11.73 \n" "c 32.391 12.035 32.199 12.453 32.047 12.977 c 31.902 13.492 31.828 14.148\n" " 31.828 14.945 c 31.828 15.719 31.902 16.363 32.047 16.879 c 32.191 17.391\n" " 32.383 17.797 32.613 18.102 c 32.855 18.406 33.125 18.625 33.422 18.758\n" " c 33.719 18.883 34.023 18.945 34.328 18.945 c 34.723 18.945 35.07 18.867\n" " 35.379 18.715 c 35.684 18.555 35.941 18.312 36.145 17.984 c 36.355 17.648\n" " 36.516 17.223 36.625 16.707 c 36.734 16.191 36.789 15.574 36.789 14.859\n" " c 53.672 14.859 m 53.672 15.754 53.582 16.582 53.398 17.34 c 53.223 18.098\n" " 52.949 18.75 52.578 19.297 c 52.207 19.844 51.734 20.273 51.156 20.586 \n" "c 50.59 20.891 49.914 21.043 49.137 21.043 c 48.785 21.043 48.438 21.008\n" " 48.086 20.934 c 47.742 20.859 47.414 20.746 47.102 20.582 c 46.789 20.414\n" " 46.496 20.199 46.227 19.938 c 45.965 19.668 45.738 19.336 45.551 18.941\n" " c 45.484 18.941 l 45.492 18.977 45.5 19.07 45.508 19.215 c 45.516 19.359\n" " 45.523 19.531 45.531 19.727 c 45.539 19.914 45.543 20.121 45.543 20.34 \n" "c 45.551 20.551 45.555 20.746 45.555 20.93 c 45.555 25.465 l 42.484 25.465\n" " l 42.484 11.719 l 42.484 11.113 42.473 10.574 42.453 10.102 c 42.438 9.629\n" " 42.422 9.262 42.398 8.996 c 45.383 8.996 l 45.398 9.047 45.41 9.145 45.426\n" " 9.293 c 45.449 9.438 45.461 9.605 45.469 9.797 c 45.484 9.984 45.496 10.184\n" " 45.5 10.387 c 45.508 10.59 45.512 10.77 45.512 10.922 c 45.555 10.922 l\n" " 45.926 10.141 46.438 9.586 47.086 9.25 c 47.734 8.914 48.484 8.746 49.336\n" " 8.746 c 50.086 8.746 50.734 8.898 51.281 9.203 c 51.828 9.508 52.277 9.93\n" " 52.625 10.469 c 52.98 11.008 53.246 11.652 53.41 12.402 c 53.586 13.145\n" " 53.672 13.961 53.672 14.852 c 50.469 14.852 m 50.469 13.504 50.266 12.508\n" " 49.855 11.859 c 49.449 11.203 48.84 10.875 48.031 10.875 c 47.727 10.875\n" " 47.418 10.941 47.113 11.07 c 46.816 11.195 46.547 11.414 46.305 11.727 \n" "c 46.07 12.031 45.879 12.449 45.727 12.973 c 45.582 13.488 45.508 14.145\n" " 45.508 14.941 c 45.508 15.715 45.582 16.359 45.727 16.875 c 45.871 17.387\n" " 46.062 17.793 46.293 18.098 c 46.535 18.402 46.805 18.621 47.102 18.754\n" " c 47.398 18.879 47.703 18.941 48.008 18.941 c 48.402 18.941 48.75 18.863\n" " 49.059 18.711 c 49.363 18.551 49.621 18.309 49.824 17.98 c 50.035 17.645\n" " 50.195 17.219 50.305 16.703 c 50.414 16.188 50.469 15.57 50.469 14.855 \n" "c 56.16 20.82 m 56.16 11.773 l 56.16 11.52 56.156 11.25 56.148 10.965 c \n" "56.148 10.68 56.141 10.41 56.125 10.156 c 56.117 9.895 56.109 9.66 56.102\n" " 9.457 c 56.094 9.246 56.082 9.094 56.07 9 c 59 9 l 59.016 9.086 59.027 \n" "9.242 59.043 9.457 c 59.059 9.668 59.07 9.902 59.086 10.156 c 59.102 10.41\n" " 59.113 10.668 59.117 10.922 c 59.133 11.168 59.141 11.371 59.141 11.523\n" " c 59.184 11.523 l 59.336 11.094 59.488 10.711 59.641 10.375 c 59.793 10.031\n" " 59.973 9.746 60.176 9.512 c 60.387 9.27 60.637 9.09 60.93 8.965 c 61.223\n" " 8.832 61.582 8.77 62.012 8.77 c 62.195 8.77 62.371 8.789 62.547 8.824 c\n" " 62.73 8.852 62.867 8.891 62.961 8.934 c 62.961 11.5 l 62.766 11.457 62.562\n" " 11.418 62.359 11.391 c 62.164 11.355 61.926 11.336 61.648 11.336 c 60.883\n" " 11.336 60.285 11.645 59.855 12.266 c 59.434 12.887 59.223 13.801 59.223\n" " 15.02 c 59.223 20.82 l 56.152 20.82 l 76.082 14.898 m 76.082 15.801 75.953\n" " 16.629 75.699 17.379 c 75.453 18.129 75.078 18.777 74.574 19.324 c 74.07\n" " 19.863 73.445 20.285 72.695 20.59 c 71.945 20.887 71.07 21.039 70.074 21.039\n" " c 69.113 21.039 68.266 20.891 67.527 20.59 c 66.793 20.293 66.172 19.871\n" " 65.668 19.332 c 65.172 18.793 64.797 18.148 64.543 17.398 c 64.289 16.641\n" " 64.16 15.809 64.16 14.895 c 64.16 14.012 64.281 13.199 64.52 12.457 c 64.766\n" " 11.707 65.141 11.059 65.633 10.512 c 66.129 9.965 66.75 9.539 67.5 9.234\n" " c 68.25 8.93 69.129 8.777 70.133 8.777 c 71.195 8.777 72.102 8.93 72.855\n" " 9.234 c 73.605 9.539 74.219 9.965 74.691 10.512 c 75.172 11.051 75.523 \n" "11.695 75.742 12.445 c 75.969 13.188 76.082 14.004 76.082 14.895 c 72.871\n" " 14.895 m 72.871 13.488 72.641 12.469 72.184 11.836 c 71.727 11.203 71.059\n" " 10.887 70.184 10.887 c 69.281 10.887 68.59 11.207 68.109 11.848 c 67.629\n" " 12.488 67.387 13.504 67.387 14.895 c 67.387 15.602 67.449 16.211 67.574\n" " 16.719 c 67.707 17.23 67.887 17.648 68.121 17.977 c 68.355 18.305 68.633\n" " 18.547 68.961 18.707 c 69.289 18.859 69.648 18.938 70.043 18.938 c 70.496\n" " 18.938 70.895 18.859 71.246 18.707 c 71.602 18.547 71.902 18.305 72.141\n" " 17.977 c 72.383 17.648 72.562 17.23 72.688 16.719 c 72.812 16.207 72.875\n" " 15.602 72.875 14.895 c 84.961 20.816 m 81.289 20.816 l 77.059 8.992 l 80.305\n" " 8.992 l 82.371 15.602 l 82.438 15.82 82.504 16.055 82.578 16.301 c 82.652\n" " 16.543 82.719 16.781 82.785 17.023 c 82.852 17.266 82.914 17.496 82.973\n" " 17.723 c 83.039 17.949 83.098 18.152 83.148 18.336 c 83.191 18.16 83.246\n" " 17.965 83.312 17.746 c 83.379 17.52 83.445 17.289 83.508 17.047 c 83.582\n" " 16.805 83.652 16.566 83.727 16.324 c 83.809 16.082 83.883 15.855 83.957\n" " 15.637 c 86.109 8.992 l 89.32 8.992 l 84.961 20.816 l 95.832 21.035 m 94.98\n" " 21.035 94.211 20.91 93.527 20.664 c 92.852 20.41 92.27 20.027 91.789 19.516\n" " c 91.309 19 90.941 18.355 90.684 17.582 c 90.43 16.801 90.301 15.891 90.301\n" " 14.852 c 90.301 13.723 90.449 12.773 90.75 12 c 91.055 11.227 91.465 10.605\n" " 91.973 10.133 c 92.488 9.652 93.082 9.305 93.754 9.094 c 94.426 8.883 95.129\n" " 8.777 95.875 8.777 c 96.809 8.777 97.602 8.941 98.258 9.27 c 98.922 9.59\n" " 99.465 10.043 99.887 10.625 c 100.309 11.207 100.617 11.906 100.816 12.723\n" " c 101.012 13.531 101.113 14.426 101.113 15.41 c 101.113 15.496 l 93.539\n" " 15.508 l 93.539 16.004 93.582 16.465 93.672 16.895 c 93.758 17.316 93.902\n" " 17.684 94.098 18 c 94.293 18.305 94.551 18.551 94.863 18.73 c 95.176 18.906\n" " 95.555 18.992 96 18.992 c 96.539 18.992 96.98 18.879 97.32 18.652 c 97.664\n" " 18.418 97.906 18.062 98.051 17.582 c 100.945 17.832 l 100.812 18.168 100.629\n" " 18.523 100.387 18.902 c 100.152 19.281 99.84 19.629 99.445 19.953 c 99.051\n" " 20.266 98.559 20.527 97.969 20.738 c 97.387 20.941 96.676 21.043 95.84 \n" "21.043 c 95.84 10.719 m 95.527 10.719 95.23 10.773 94.953 10.883 c 94.684\n" " 10.984 94.445 11.152 94.242 11.387 c 94.047 11.613 93.887 11.906 93.762\n" " 12.273 c 93.637 12.637 93.57 13.074 93.555 13.586 c 98.145 13.586 l 98.086\n" " 12.633 97.855 11.918 97.457 11.445 c 97.055 10.965 96.516 10.723 95.84 \n" "10.723 c 111.117 20.828 m 111.102 20.77 111.086 20.668 111.062 20.523 c \n" "111.047 20.371 111.031 20.199 111.008 20.012 c 110.992 19.824 110.98 19.629\n" " 110.965 19.434 c 110.957 19.238 110.953 19.062 110.953 18.91 c 110.91 18.91\n" " l 110.555 19.676 110.055 20.227 109.414 20.559 c 108.781 20.887 108.02 \n" "21.051 107.129 21.051 c 106.387 21.051 105.742 20.898 105.195 20.594 c 104.656\n" " 20.289 104.207 19.863 103.852 19.316 c 103.5 18.77 103.238 18.125 103.066\n" " 17.383 c 102.898 16.633 102.816 15.816 102.816 14.934 c 102.816 14.039 \n" "102.902 13.215 103.078 12.465 c 103.262 11.715 103.535 11.07 103.91 10.531\n" " c 104.281 9.984 104.75 9.559 105.32 9.254 c 105.895 8.949 106.578 8.797\n" " 107.363 8.797 c 107.75 8.797 108.121 8.836 108.477 8.918 c 108.832 9 109.164\n" " 9.121 109.473 9.289 c 109.777 9.457 110.055 9.672 110.305 9.934 c 110.551\n" " 10.195 110.762 10.512 110.938 10.883 c 110.961 10.883 l 110.961 10.809 \n" "110.957 10.703 110.949 10.566 c 110.949 10.422 110.949 10.258 110.949 10.074\n" " c 110.949 9.891 110.945 9.703 110.938 9.508 c 110.938 9.312 110.938 9.121\n" " 110.938 8.941 c 110.938 4.625 l 114.008 4.625 l 114.008 18.262 l 114.008\n" " 18.836 114.02 19.352 114.039 19.801 c 114.062 20.246 114.078 20.59 114.094\n" " 20.84 c 111.121 20.84 l 110.98 14.863 m 110.98 14.082 110.906 13.438 110.762\n" " 12.93 c 110.617 12.414 110.422 12.004 110.184 11.707 c 109.949 11.402 109.68\n" " 11.191 109.375 11.074 c 109.078 10.949 108.77 10.887 108.457 10.887 c 108.062\n" " 10.887 107.715 10.965 107.406 11.117 c 107.109 11.27 106.852 11.512 106.641\n" " 11.84 c 106.438 12.168 106.281 12.586 106.172 13.098 c 106.07 13.609 106.02\n" " 14.223 106.02 14.945 c 106.02 17.625 106.824 18.965 108.434 18.965 c 108.738\n" " 18.965 109.047 18.898 109.352 18.77 c 109.656 18.637 109.93 18.418 110.172\n" " 18.102 c 110.414 17.789 110.605 17.371 110.75 16.844 c 110.902 16.312 110.98\n" " 15.652 110.98 14.867 c S Q\n" "Q q\n" "0 0 127 26.484 re W n\n" "0 0.298039 0.431373 rg /a1 gs\n" "3.398 0.398 m 1.77 0.398 0.398 1.77 0.398 3.398 c 0.398 23.086 l 0.398 \n" "24.715 1.77 26.086 3.398 26.086 c 123.609 26.086 l 125.238 26.086 126.609\n" " 24.715 126.609 23.086 c 126.609 3.398 l 126.609 1.77 125.238 0.398 123.609\n" " 0.398 c h\n" "3.867 3.844 m 123.164 3.844 l 123.164 22.617 l 3.867 22.617 l h\n" "3.867 3.844 m f\n" "Q q\n" "1 1 1 RG /a1 gs\n" "0.797243 w\n" "1 J\n" "1 j\n" "[] 0.0 d\n" "4 M q 1 0 0 1 0 0 cm\n" "3.398 0.398 m 1.77 0.398 0.398 1.77 0.398 3.398 c 0.398 23.086 l 0.398 \n" "24.715 1.77 26.086 3.398 26.086 c 123.609 26.086 l 125.238 26.086 126.609\n" " 24.715 126.609 23.086 c 126.609 3.398 l 126.609 1.77 125.238 0.398 123.609\n" " 0.398 c h\n" "3.867 3.844 m 123.164 3.844 l 123.164 22.617 l 3.867 22.617 l h\n" "3.867 3.844 m S Q\n" "Q\n"; static Dict *getApprovedStampExtGStateDict(PDFDoc *doc) { Dict *a0Dict = new Dict(doc->getXRef()); a0Dict->add("CA", Object(0.588235)); a0Dict->add("ca", Object(0.588235)); Dict *a1Dict = new Dict(doc->getXRef()); a1Dict->add("CA", Object(1)); a1Dict->add("ca", Object(1)); Dict *extGStateDict = new Dict(doc->getXRef()); extGStateDict->add("a0", Object(a0Dict)); extGStateDict->add("a1", Object(a1Dict)); return extGStateDict; } #endif poppler-24.02.0/poppler/annot_stamp_as_is.h000066400000000000000000000246011455701731300207040ustar00rootroot00000000000000//======================================================================== // // annot_stamp_as_is.h // // Copyright (C) 2021 Mahmoud Ahmed Khalil // Copyright (C) 2021 Albert Astals Cid // // Mechanically extracted from an SVG created for Okular by Eugene Trounev eugene.trounev@gmail.com // // Licensed under GPLv2 or later // //======================================================================== #ifndef ANNOT_STAMP_AS_IS_H #define ANNOT_STAMP_AS_IS_H #include "Dict.h" #include "Object.h" #include "PDFDoc.h" static const double ANNOT_STAMP_AS_IS_WIDTH = 79.758179; static const double ANNOT_STAMP_AS_IS_HEIGHT = 26.484743; static const char *ANNOT_STAMP_AS_IS = "1 0 0 -1 0 26.484744 cm\n" "q\n" "1 1 1 rg /a0 gs\n" "3.406 2.129 m 76.367 2.129 l 77.066 2.129 77.637 2.828 77.637 3.398 c 77.637\n" " 23.09 l 77.637 23.789 77.07 24.359 76.367 24.359 c 3.406 24.359 l 2.707\n" " 24.359 2.137 23.66 2.137 23.09 c 2.137 3.398 l 2.137 2.699 2.703 2.129 \n" "3.406 2.129 c h\n" "3.406 2.129 m f\n" "0 0.298039 0.431373 rg /a1 gs\n" "1 1 1 RG 0.265748 w\n" "0 J\n" "0 j\n" "[] 0.0 d\n" "4 M q 1 0 0 1 0 0 cm\n" "25.703 20.828 m 24.336 16.895 l 18.469 16.895 l 17.102 20.828 l 13.879 \n" "20.828 l 19.496 5.434 l 23.297 5.434 l 28.891 20.828 l 25.699 20.828 l 22.098\n" " 10.043 m 22.012 9.789 21.922 9.531 21.836 9.277 c 21.754 9.016 21.684 8.777\n" " 21.617 8.566 c 21.559 8.348 21.508 8.168 21.465 8.031 c 21.43 7.895 21.406\n" " 7.816 21.398 7.801 c 21.391 7.824 21.371 7.902 21.332 8.043 c 21.297 8.18\n" " 21.246 8.355 21.18 8.566 c 21.121 8.777 21.047 9.016 20.961 9.277 c 20.879\n" " 9.531 20.797 9.789 20.711 10.043 c 19.191 14.469 l 23.617 14.469 l 22.098\n" " 10.043 l 41.023 17.375 m 41.023 17.941 40.902 18.453 40.664 18.906 c 40.43\n" " 19.359 40.09 19.742 39.637 20.066 c 39.184 20.379 38.633 20.625 37.977 \n" "20.797 c 37.32 20.965 36.574 21.047 35.738 21.047 c 34.988 21.047 34.309\n" " 20.992 33.707 20.883 c 33.102 20.773 32.57 20.594 32.113 20.348 c 31.66\n" " 20.094 31.281 19.766 30.977 19.363 c 30.672 18.961 30.441 18.469 30.289\n" " 17.879 c 32.988 17.477 l 33.074 17.805 33.195 18.07 33.348 18.273 c 33.5\n" " 18.477 33.691 18.633 33.914 18.742 c 34.141 18.852 34.402 18.926 34.699\n" " 18.961 c 35.004 18.996 35.352 19.016 35.738 19.016 c 36.09 19.016 36.41\n" " 18.996 36.711 18.961 c 37.016 18.918 37.277 18.848 37.496 18.754 c 37.723\n" " 18.652 37.898 18.512 38.02 18.34 c 38.145 18.156 38.207 17.934 38.207 17.664\n" " c 38.207 17.359 38.117 17.117 37.934 16.941 c 37.758 16.758 37.516 16.613\n" " 37.203 16.504 c 36.898 16.387 36.531 16.289 36.109 16.207 c 35.695 16.121\n" " 35.25 16.02 34.777 15.91 c 34.281 15.801 33.793 15.668 33.312 15.516 c \n" "32.832 15.363 32.402 15.156 32.023 14.895 c 31.645 14.633 31.34 14.301 31.105\n" " 13.898 c 30.871 13.492 30.754 12.977 30.754 12.359 c 30.754 11.797 30.863\n" " 11.301 31.082 10.863 c 31.301 10.418 31.621 10.043 32.043 9.738 c 32.465\n" " 9.426 32.984 9.188 33.605 9.027 c 34.23 8.859 34.949 8.777 35.758 8.777\n" " c 36.398 8.777 36.996 8.84 37.551 8.965 c 38.105 9.082 38.602 9.27 39.035\n" " 9.531 c 39.473 9.785 39.836 10.113 40.129 10.516 c 40.426 10.918 40.637\n" " 11.402 40.75 11.969 c 38.027 12.254 l 37.977 11.969 37.887 11.738 37.754\n" " 11.555 c 37.621 11.367 37.457 11.215 37.262 11.105 c 37.074 10.996 36.852\n" " 10.922 36.594 10.887 c 36.34 10.844 36.059 10.82 35.754 10.82 c 35.027 \n" "10.82 34.48 10.914 34.113 11.105 c 33.75 11.289 33.566 11.598 33.566 12.035\n" " c 33.566 12.305 33.641 12.52 33.785 12.68 c 33.938 12.84 34.148 12.977 \n" "34.418 13.082 c 34.695 13.184 35.02 13.273 35.391 13.355 c 35.77 13.43 36.18\n" " 13.52 36.625 13.629 c 37.172 13.746 37.707 13.883 38.23 14.043 c 38.762\n" " 14.195 39.23 14.41 39.641 14.688 c 40.055 14.957 40.387 15.309 40.637 15.738\n" " c 40.891 16.168 41.02 16.715 41.02 17.379 c 49.656 20.828 m 49.656 5.434\n" " l 52.879 5.434 l 52.879 20.828 l 49.656 20.828 l 65.891 17.375 m 65.891\n" " 17.941 65.77 18.453 65.531 18.906 c 65.297 19.359 64.957 19.742 64.504 \n" "20.066 c 64.051 20.379 63.5 20.625 62.844 20.797 c 62.188 20.965 61.441 \n" "21.047 60.605 21.047 c 59.855 21.047 59.176 20.992 58.574 20.883 c 57.969\n" " 20.773 57.438 20.594 56.98 20.348 c 56.527 20.094 56.148 19.766 55.844 \n" "19.363 c 55.539 18.961 55.309 18.469 55.156 17.879 c 57.855 17.477 l 57.941\n" " 17.805 58.062 18.07 58.215 18.273 c 58.367 18.477 58.559 18.633 58.781 \n" "18.742 c 59.008 18.852 59.27 18.926 59.566 18.961 c 59.871 18.996 60.219\n" " 19.016 60.605 19.016 c 60.957 19.016 61.277 18.996 61.578 18.961 c 61.883\n" " 18.918 62.145 18.848 62.363 18.754 c 62.59 18.652 62.766 18.512 62.887 \n" "18.34 c 63.012 18.156 63.074 17.934 63.074 17.664 c 63.074 17.359 62.984\n" " 17.117 62.801 16.941 c 62.625 16.758 62.383 16.613 62.07 16.504 c 61.766\n" " 16.387 61.398 16.289 60.977 16.207 c 60.562 16.121 60.117 16.02 59.645 \n" "15.91 c 59.148 15.801 58.66 15.668 58.18 15.516 c 57.699 15.363 57.27 15.156\n" " 56.891 14.895 c 56.512 14.633 56.207 14.301 55.973 13.898 c 55.738 13.492\n" " 55.621 12.977 55.621 12.359 c 55.621 11.797 55.73 11.301 55.949 10.863 \n" "c 56.168 10.418 56.488 10.043 56.91 9.738 c 57.332 9.426 57.852 9.188 58.473\n" " 9.027 c 59.098 8.859 59.816 8.777 60.625 8.777 c 61.266 8.777 61.863 8.84\n" " 62.418 8.965 c 62.973 9.082 63.469 9.27 63.902 9.531 c 64.34 9.785 64.703\n" " 10.113 64.996 10.516 c 65.293 10.918 65.504 11.402 65.617 11.969 c 62.895\n" " 12.254 l 62.844 11.969 62.754 11.738 62.621 11.555 c 62.488 11.367 62.324\n" " 11.215 62.129 11.105 c 61.941 10.996 61.719 10.922 61.461 10.887 c 61.207\n" " 10.844 60.926 10.82 60.621 10.82 c 59.895 10.82 59.348 10.914 58.98 11.105\n" " c 58.617 11.289 58.434 11.598 58.434 12.035 c 58.434 12.305 58.508 12.52\n" " 58.652 12.68 c 58.805 12.84 59.016 12.977 59.285 13.082 c 59.562 13.184\n" " 59.887 13.273 60.258 13.355 c 60.637 13.43 61.047 13.52 61.492 13.629 c\n" " 62.039 13.746 62.574 13.883 63.098 14.043 c 63.629 14.195 64.098 14.41 \n" "64.508 14.688 c 64.922 14.957 65.254 15.309 65.504 15.738 c 65.758 16.168\n" " 65.887 16.715 65.887 17.379 c B Q\n" "Q q\n" "0 0.298039 0.431373 rg /a1 gs\n" "1 1 1 RG 0.797243 w\n" "1 J\n" "1 j\n" "[] 0.0 d\n" "4 M q 1 0 0 1 0 0 cm\n" "3.398 0.398 m 1.77 0.398 0.398 1.77 0.398 3.398 c 0.398 23.086 l 0.398 \n" "24.715 1.77 26.086 3.398 26.086 c 76.359 26.086 l 77.988 26.086 79.359 24.715\n" " 79.359 23.086 c 79.359 3.398 l 79.359 1.77 77.988 0.398 76.359 0.398 c \n" "h\n" "3.867 3.844 m 75.914 3.844 l 75.914 22.617 l 3.867 22.617 l h\n" "3.867 3.844 m B Q\n" "Q\n"; static Dict *getAsIsStampExtGStateDict(PDFDoc *doc) { Dict *a0Dict = new Dict(doc->getXRef()); a0Dict->add("CA", Object(0.588235)); a0Dict->add("ca", Object(0.588235)); Dict *a1Dict = new Dict(doc->getXRef()); a1Dict->add("CA", Object(1)); a1Dict->add("ca", Object(1)); Dict *extGStateDict = new Dict(doc->getXRef()); extGStateDict->add("a0", Object(a0Dict)); extGStateDict->add("a1", Object(a1Dict)); return extGStateDict; } #endif poppler-24.02.0/poppler/annot_stamp_confidential.h000066400000000000000000000413221455701731300222440ustar00rootroot00000000000000//======================================================================== // // annot_stamp_confidential.h // // Copyright (C) 2021 Mahmoud Ahmed Khalil // Copyright (C) 2021 Albert Astals Cid // // Mechanically extracted from an SVG created for Okular by Eugene Trounev eugene.trounev@gmail.com // // Licensed under GPLv2 or later // //======================================================================== #ifndef ANNOT_STAMP_CONFIDENTIAL_H #define ANNOT_STAMP_CONFIDENTIAL_H #include "PDFDoc.h" #include "Dict.h" #include "Object.h" static const double ANNOT_STAMP_CONFIDENTIAL_WIDTH = 155.508179; static const double ANNOT_STAMP_CONFIDENTIAL_HEIGHT = 26.484743; static const char *ANNOT_STAMP_CONFIDENTIAL = "1 0 0 -1 0 26.484741 cm\n" "q\n" "1 1 1 rg /a0 gs\n" "3.406 2.129 m 152.117 2.129 l 152.82 2.129 153.387 2.695 153.387 3.398 \n" "c 153.387 23.09 l 153.387 23.793 152.82 24.359 152.117 24.359 c 3.406 24.355\n" " l 2.703 24.355 2.137 23.789 2.137 23.086 c 2.137 3.395 l 2.137 2.691 2.703\n" " 2.125 3.406 2.125 c h\n" "3.406 2.129 m f\n" "0.74902 0 0 rg /a1 gs\n" "1 1 1 RG 0.531496 w\n" "0 J\n" "0 j\n" "[] 0.0 d\n" "4 M q 1 0 0 1 0 0 cm\n" "22.008 18.512 m 23.953 18.512 25.305 17.535 26.062 15.582 c 28.871 16.641\n" " l 28.266 18.125 27.379 19.234 26.203 19.961 c 25.039 20.684 23.641 21.043\n" " 22.008 21.043 c 19.531 21.043 17.613 20.344 16.262 18.945 c 14.914 17.539\n" " 14.242 15.578 14.242 13.055 c 14.242 10.527 14.895 8.586 16.199 7.23 c \n" "17.504 5.875 19.395 5.199 21.871 5.199 c 23.676 5.199 25.148 5.562 26.285\n" " 6.293 c 27.422 7.016 28.219 8.078 28.68 9.484 c 25.84 10.262 l 25.598 9.488\n" " 25.125 8.879 24.418 8.426 c 23.719 7.969 22.891 7.738 21.938 7.738 c 20.48\n" " 7.738 19.375 8.191 18.617 9.094 c 17.867 9.996 17.492 11.32 17.492 13.059\n" " c 17.492 14.828 17.879 16.18 18.652 17.113 c 19.434 18.047 20.555 18.512\n" " 22.02 18.512 c h\n" "22.008 18.512 m B Q\n" "0.74902 0 0 rg 1 1 1 RG q 1 0 0 1 0 0 cm\n" "42.277 14.906 m 42.277 16.82 41.746 18.328 40.684 19.418 c 39.621 20.504\n" " 38.148 21.047 36.27 21.047 c 34.426 21.047 32.98 20.5 31.93 19.406 c 30.879\n" " 18.312 30.355 16.812 30.355 14.906 c 30.355 13.004 30.879 11.512 31.93 \n" "10.426 c 32.98 9.332 34.445 8.785 36.332 8.785 c 38.262 8.785 39.734 9.312\n" " 40.746 10.371 c 41.766 11.422 42.277 12.93 42.277 14.906 c h\n" "39.066 14.906 m 39.066 13.5 38.836 12.48 38.379 11.848 c 37.922 11.215 \n" "37.254 10.898 36.379 10.898 c 34.516 10.898 33.582 12.234 33.582 14.91 c\n" " 33.582 16.23 33.809 17.234 34.258 17.926 c 34.715 18.609 35.375 18.953 \n" "36.234 18.953 c 38.121 18.953 39.062 17.605 39.062 14.91 c h\n" "39.066 14.906 m B Q\n" "0.74902 0 0 rg 1 1 1 RG q 1 0 0 1 0 0 cm\n" "52.375 20.828 m 52.375 14.195 l 52.375 12.121 51.672 11.082 50.266 11.082\n" " c 49.523 11.082 48.922 11.402 48.461 12.043 c 48.008 12.676 47.785 13.492\n" " 47.785 14.492 c 47.785 20.828 l 44.715 20.828 l 44.715 11.652 l 44.715 \n" "11.02 44.703 10.5 44.684 10.102 c 44.668 9.695 44.652 9.328 44.629 9.008\n" " c 47.559 9.008 l 47.582 9.145 47.609 9.52 47.645 10.121 c 47.68 10.719 \n" "47.699 11.129 47.699 11.355 c 47.742 11.355 l 48.156 10.453 48.68 9.797 \n" "49.305 9.387 c 49.93 8.98 50.68 8.773 51.543 8.773 c 52.797 8.773 53.758\n" " 9.16 54.426 9.934 c 55.098 10.707 55.43 11.836 55.43 13.32 c 55.43 20.828\n" " l h\n" "52.375 20.828 m B Q\n" "0.74902 0 0 rg 1 1 1 RG q 1 0 0 1 0 0 cm\n" "61.992 11.082 m 61.992 20.828 l 58.93 20.828 l 58.93 11.082 l 57.203 11.082\n" " l 57.203 9.008 l 58.93 9.008 l 58.93 7.773 l 58.93 6.703 59.215 5.91 59.781\n" " 5.391 c 60.348 4.875 61.211 4.613 62.371 4.613 c 62.945 4.613 63.594 4.672\n" " 64.316 4.789 c 64.316 6.766 l 64.02 6.699 63.719 6.668 63.422 6.668 c 62.898\n" " 6.668 62.527 6.773 62.309 6.984 c 62.098 7.188 61.992 7.551 61.992 8.078\n" " c 61.992 9.008 l 64.32 9.008 l 64.32 11.082 l h\n" "61.992 11.082 m B Q\n" "0.74902 0 0 rg 1 1 1 RG q 1 0 0 1 0 0 cm\n" "65.836 6.875 m 65.836 4.613 l 68.906 4.613 l 68.906 6.875 l h\n" "65.836 20.828 m 65.836 9.008 l 68.906 9.008 l 68.906 20.832 l h\n" "65.836 20.828 m B Q\n" "0.74902 0 0 rg 1 1 1 RG q 1 0 0 1 0 0 cm\n" "79.715 20.828 m 79.688 20.719 79.648 20.445 79.605 20.008 c 79.57 19.562\n" " 79.551 19.195 79.551 18.902 c 79.508 18.902 l 78.844 20.332 77.586 21.043\n" " 75.727 21.043 c 74.352 21.043 73.285 20.508 72.535 19.438 c 71.785 18.359\n" " 71.41 16.855 71.41 14.926 c 71.41 12.965 71.805 11.453 72.59 10.391 c 73.383\n" " 9.32 74.504 8.785 75.957 8.785 c 76.793 8.785 77.516 8.961 78.121 9.309\n" " c 78.734 9.66 79.203 10.18 79.531 10.871 c 79.555 10.871 l 79.531 8.926\n" " l 79.531 4.609 l 82.602 4.609 l 82.602 18.246 l 82.602 18.973 82.629 19.836\n" " 82.688 20.824 c h\n" "79.574 14.852 m 79.574 13.578 79.359 12.598 78.93 11.914 c 78.508 11.223\n" " 77.879 10.875 77.051 10.875 c 76.227 10.875 75.617 11.211 75.215 11.879\n" " c 74.812 12.543 74.613 13.559 74.613 14.926 c 74.613 17.605 75.418 18.945\n" " 77.027 18.945 c 77.836 18.945 78.461 18.594 78.906 17.887 c 79.352 17.172\n" " 79.574 16.16 79.574 14.848 c h\n" "79.574 14.852 m B Q\n" "0.74902 0 0 rg 1 1 1 RG q 1 0 0 1 0 0 cm\n" "90.566 21.047 m 88.789 21.047 87.422 20.523 86.469 19.473 c 85.516 18.418\n" " 85.039 16.879 85.039 14.863 c 85.039 12.91 85.523 11.41 86.492 10.363 c\n" " 87.461 9.312 88.836 8.789 90.613 8.789 c 92.312 8.789 93.605 9.355 94.504\n" " 10.484 c 95.398 11.605 95.848 13.254 95.848 15.422 c 95.848 15.508 l 88.266\n" " 15.508 l 88.266 16.66 88.477 17.527 88.898 18.121 c 89.328 18.703 89.938\n" " 18.996 90.723 18.996 c 91.809 18.996 92.492 18.527 92.777 17.586 c 95.672\n" " 17.836 l 94.836 19.977 93.133 21.047 90.57 21.047 c h\n" "90.566 10.723 m 89.844 10.723 89.289 10.973 88.895 11.477 c 88.508 11.98\n" " 88.305 12.684 88.281 13.586 c 92.871 13.586 l 92.812 12.633 92.582 11.918\n" " 92.184 11.445 c 91.781 10.965 91.242 10.723 90.566 10.723 c h\n" "90.566 10.723 m B Q\n" "0.74902 0 0 rg 1 1 1 RG q 1 0 0 1 0 0 cm\n" "105.828 20.828 m 105.828 14.195 l 105.828 12.121 105.125 11.082 103.719\n" " 11.082 c 102.977 11.082 102.375 11.402 101.914 12.043 c 101.461 12.676 \n" "101.238 13.492 101.238 14.492 c 101.238 20.828 l 98.168 20.828 l 98.168 \n" "11.652 l 98.168 11.02 98.156 10.5 98.137 10.102 c 98.121 9.695 98.105 9.328\n" " 98.082 9.008 c 101.012 9.008 l 101.035 9.145 101.062 9.52 101.098 10.121\n" " c 101.133 10.719 101.152 11.129 101.152 11.355 c 101.195 11.355 l 101.609\n" " 10.453 102.133 9.797 102.758 9.387 c 103.383 8.98 104.133 8.773 104.996\n" " 8.773 c 106.25 8.773 107.211 9.16 107.879 9.934 c 108.551 10.707 108.883\n" " 11.836 108.883 13.32 c 108.883 20.828 l h\n" "105.828 20.828 m B Q\n" "0.74902 0 0 rg 1 1 1 RG q 1 0 0 1 0 0 cm\n" "114.867 21.027 m 113.965 21.027 113.27 20.785 112.781 20.297 c 112.293 \n" "19.801 112.051 19.055 112.051 18.059 c 112.051 11.086 l 110.555 11.086 l\n" " 110.555 9.012 l 112.203 9.012 l 113.164 6.234 l 115.086 6.234 l 115.086\n" " 9.012 l 117.324 9.012 l 117.324 11.086 l 115.086 11.086 l 115.086 17.227\n" " l 115.086 17.801 115.195 18.227 115.414 18.504 c 115.633 18.773 115.973\n" " 18.906 116.43 18.906 c 116.672 18.906 117.012 18.855 117.457 18.754 c 117.457\n" " 20.656 l 116.699 20.902 115.836 21.027 114.867 21.027 c h\n" "114.867 21.027 m B Q\n" "0.74902 0 0 rg 1 1 1 RG q 1 0 0 1 0 0 cm\n" "119.293 6.875 m 119.293 4.613 l 122.363 4.613 l 122.363 6.875 l h\n" "119.293 20.828 m 119.293 9.008 l 122.363 9.008 l 122.363 20.832 l h\n" "119.293 20.828 m B Q\n" "0.74902 0 0 rg 1 1 1 RG q 1 0 0 1 0 0 cm\n" "128.242 21.047 m 127.098 21.047 126.207 20.738 125.566 20.117 c 124.926\n" " 19.492 124.605 18.613 124.605 17.484 c 124.605 16.262 125.004 15.328 125.797\n" " 14.688 c 126.598 14.047 127.758 13.719 129.273 13.703 c 131.82 13.66 l \n" "131.82 13.059 l 131.82 12.285 131.688 11.715 131.418 11.344 c 131.148 10.965\n" " 130.707 10.777 130.098 10.777 c 129.531 10.777 129.109 10.91 128.84 11.172\n" " c 128.578 11.426 128.414 11.852 128.348 12.449 c 125.145 12.297 l 125.34\n" " 11.145 125.867 10.277 126.719 9.684 c 127.578 9.086 128.746 8.789 130.227\n" " 8.789 c 131.719 8.789 132.871 9.156 133.68 9.895 c 134.488 10.629 134.891\n" " 11.676 134.891 13.031 c 134.891 17.336 l 134.891 18 134.965 18.457 135.109\n" " 18.711 c 135.262 18.957 135.512 19.082 135.863 19.082 c 136.098 19.082 \n" "136.32 19.059 136.539 19.016 c 136.539 20.676 l 136.355 20.719 136.191 20.758\n" " 136.047 20.797 c 135.902 20.832 135.754 20.863 135.609 20.883 c 135.465\n" " 20.906 135.309 20.922 135.141 20.938 c 134.98 20.953 134.789 20.961 134.574\n" " 20.961 c 133.801 20.961 133.23 20.773 132.859 20.395 c 132.496 20.016 132.277\n" " 19.457 132.203 18.723 c 132.137 18.723 l 131.277 20.273 129.98 21.051 128.246\n" " 21.051 c h\n" "131.816 15.355 m 130.242 15.379 l 129.527 15.406 129.023 15.488 128.723\n" " 15.621 c 128.426 15.746 128.195 15.938 128.035 16.199 c 127.883 16.461 \n" "127.805 16.812 127.805 17.25 c 127.805 17.812 127.934 18.23 128.188 18.508\n" " c 128.449 18.777 128.797 18.91 129.227 18.91 c 129.707 18.91 130.145 18.777\n" " 130.539 18.516 c 130.941 18.254 131.254 17.895 131.48 17.434 c 131.707 \n" "16.969 131.82 16.477 131.82 15.957 c h\n" "131.816 15.355 m B Q\n" "0.74902 0 0 rg 1 1 1 RG q 1 0 0 1 0 0 cm\n" "137.953 4.613 3.07 16.215 re B Q\n" "Q q\n" "0.74902 0.0117647 0.0117647 rg /a1 gs\n" "1 1 1 RG 0.797243 w\n" "1 J\n" "1 j\n" "[] 0.0 d\n" "4 M q 1 0 0 1 0 0 cm\n" "3.398 0.398 m 1.77 0.398 0.398 1.77 0.398 3.398 c 0.398 23.086 l 0.398 \n" "24.715 1.77 26.086 3.398 26.086 c 152.109 26.086 l 153.738 26.086 155.109\n" " 24.715 155.109 23.086 c 155.109 3.398 l 155.109 1.77 153.738 0.398 152.109\n" " 0.398 c h\n" "3.867 3.844 m 151.664 3.844 l 151.664 22.617 l 3.867 22.617 l h\n" "3.867 3.844 m B Q\n" "Q\n"; static Dict *getConfidentialStampExtGStateDict(PDFDoc *doc) { Dict *a0Dict = new Dict(doc->getXRef()); a0Dict->add("CA", Object(0.588235)); a0Dict->add("ca", Object(0.588235)); Dict *a1Dict = new Dict(doc->getXRef()); a1Dict->add("CA", Object(1)); a1Dict->add("ca", Object(1)); Dict *extGStateDict = new Dict(doc->getXRef()); extGStateDict->add("a0", Object(a0Dict)); extGStateDict->add("a1", Object(a1Dict)); return extGStateDict; } #endif poppler-24.02.0/poppler/annot_stamp_departmental.h000066400000000000000000001477501455701731300223010ustar00rootroot00000000000000//======================================================================== // // annot_stamp_departmental.h // // Copyright (C) 2021 Mahmoud Ahmed Khalil // Copyright (C) 2021 Albert Astals Cid // // Mechanically extracted from an SVG created for Okular by Eugene Trounev eugene.trounev@gmail.com // // Licensed under GPLv2 or later // //======================================================================== #ifndef ANNOT_STAMP_DEPARTMENTAL_H #define ANNOT_STAMP_DEPARTMENTAL_H #include "Dict.h" #include "Object.h" #include "PDFDoc.h" static const double ANNOT_STAMP_DEPARTMENTAL_WIDTH = 170.508179; static const double ANNOT_STAMP_DEPARTMENTAL_HEIGHT = 26.484743; static const char *ANNOT_STAMP_DEPARTMENTAL = "1 0 0 -1 0 26.484741 cm\n" "q\n" "1 1 1 rg /a0 gs\n" "3.406 2.129 m 167.117 2.129 l 167.816 2.129 168.387 2.828 168.387 3.398\n" " c 168.387 23.09 l 168.387 23.789 167.82 24.359 167.117 24.359 c 3.406 24.359\n" " l 2.707 24.359 2.137 23.66 2.137 23.09 c 2.137 3.398 l 2.137 2.699 2.703\n" " 2.129 3.406 2.129 c h\n" "3.406 2.129 m f\n" "0 0.298039 0.431373 rg /a1 gs\n" "28.543 13.016 m 28.543 14.297 28.352 15.426 27.965 16.402 c 27.578 17.371\n" " 27.051 18.184 26.379 18.84 c 25.707 19.496 24.914 19.992 23.996 20.324 \n" "c 23.086 20.66 22.102 20.828 21.047 20.828 c 14.82 20.828 l 14.82 5.434 \n" "l 20.395 5.434 l 21.59 5.434 22.684 5.582 23.684 5.883 c 24.68 6.18 25.543\n" " 6.641 26.262 7.258 c 26.984 7.879 27.543 8.664 27.945 9.617 c 28.348 10.57\n" " 28.547 11.703 28.547 13.016 c 25.301 13.016 m 25.301 12.121 25.176 11.352\n" " 24.93 10.711 c 24.688 10.07 24.352 9.547 23.914 9.137 c 23.477 8.723 22.953\n" " 8.414 22.34 8.219 c 21.734 8.023 21.066 7.922 20.328 7.922 c 18.043 7.926\n" " l 18.043 18.34 l 20.773 18.34 l 21.422 18.34 22.02 18.227 22.566 18 c 23.121\n" " 17.773 23.598 17.438 23.996 16.996 c 24.402 16.543 24.723 15.988 24.945\n" " 15.324 c 25.18 14.66 25.297 13.895 25.297 13.02 c 35.895 21.051 m 35.043\n" " 21.051 34.273 20.926 33.59 20.68 c 32.914 20.426 32.332 20.043 31.852 19.531\n" " c 31.371 19.016 31.004 18.371 30.746 17.598 c 30.492 16.816 30.363 15.906\n" " 30.363 14.867 c 30.363 13.738 30.512 12.789 30.812 12.016 c 31.117 11.242\n" " 31.527 10.621 32.035 10.148 c 32.551 9.668 33.145 9.32 33.816 9.109 c 34.488\n" " 8.898 35.191 8.793 35.938 8.793 c 36.871 8.793 37.664 8.957 38.32 9.285\n" " c 38.984 9.605 39.527 10.059 39.949 10.641 c 40.371 11.223 40.68 11.922\n" " 40.879 12.738 c 41.074 13.547 41.176 14.441 41.176 15.426 c 41.176 15.512\n" " l 33.594 15.508 l 33.594 16.004 33.637 16.465 33.727 16.895 c 33.812 17.316\n" " 33.957 17.684 34.152 18 c 34.348 18.305 34.605 18.551 34.918 18.73 c 35.23\n" " 18.906 35.609 18.992 36.055 18.992 c 36.594 18.992 37.035 18.879 37.375\n" " 18.652 c 37.719 18.418 37.961 18.062 38.105 17.582 c 41 17.832 l 40.867\n" " 18.168 40.684 18.523 40.441 18.902 c 40.207 19.281 39.895 19.629 39.5 19.953\n" " c 39.105 20.266 38.613 20.527 38.023 20.738 c 37.441 20.941 36.73 21.043\n" " 35.895 21.043 c 35.895 10.719 m 35.582 10.719 35.285 10.773 35.008 10.883\n" " c 34.738 10.984 34.5 11.152 34.297 11.387 c 34.102 11.613 33.941 11.906\n" " 33.816 12.273 c 33.691 12.637 33.625 13.074 33.609 13.586 c 38.199 13.586\n" " l 38.141 12.633 37.91 11.918 37.512 11.445 c 37.109 10.965 36.57 10.723\n" " 35.895 10.723 c 54.699 14.863 m 54.699 15.758 54.609 16.586 54.426 17.344\n" " c 54.25 18.102 53.977 18.754 53.605 19.301 c 53.234 19.848 52.762 20.277\n" " 52.184 20.59 c 51.617 20.895 50.941 21.047 50.164 21.047 c 49.812 21.047\n" " 49.465 21.012 49.113 20.938 c 48.77 20.863 48.441 20.75 48.129 20.586 c\n" " 47.816 20.418 47.523 20.203 47.254 19.941 c 46.992 19.672 46.766 19.34 \n" "46.578 18.945 c 46.512 18.945 l 46.52 18.98 46.527 19.074 46.535 19.219 \n" "c 46.543 19.363 46.551 19.535 46.559 19.73 c 46.566 19.918 46.57 20.125 \n" "46.57 20.344 c 46.578 20.555 46.582 20.75 46.582 20.934 c 46.582 25.469 \n" "l 43.512 25.473 l 43.512 11.727 l 43.512 11.121 43.5 10.582 43.48 10.109\n" " c 43.465 9.637 43.449 9.27 43.426 9.004 c 46.41 9.004 l 46.426 9.055 46.438\n" " 9.152 46.453 9.301 c 46.477 9.445 46.488 9.613 46.496 9.805 c 46.512 9.992\n" " 46.523 10.191 46.527 10.395 c 46.535 10.598 46.539 10.777 46.539 10.93 \n" "c 46.582 10.93 l 46.953 10.148 47.465 9.594 48.113 9.258 c 48.762 8.922 \n" "49.512 8.754 50.363 8.754 c 51.113 8.754 51.762 8.906 52.309 9.211 c 52.855\n" " 9.516 53.305 9.938 53.652 10.477 c 54.008 11.016 54.273 11.66 54.438 12.41\n" " c 54.613 13.152 54.699 13.969 54.699 14.859 c 51.496 14.859 m 51.496 13.512\n" " 51.293 12.516 50.883 11.867 c 50.477 11.211 49.867 10.883 49.059 10.883\n" " c 48.754 10.883 48.445 10.949 48.141 11.078 c 47.844 11.203 47.574 11.422\n" " 47.332 11.734 c 47.098 12.039 46.906 12.457 46.754 12.98 c 46.609 13.496\n" " 46.535 14.152 46.535 14.949 c 46.535 15.723 46.609 16.367 46.754 16.883\n" " c 46.898 17.395 47.09 17.801 47.32 18.105 c 47.562 18.41 47.832 18.629 \n" "48.129 18.762 c 48.426 18.887 48.73 18.949 49.035 18.949 c 49.43 18.949 \n" "49.777 18.871 50.086 18.719 c 50.391 18.559 50.648 18.316 50.852 17.988 \n" "c 51.062 17.652 51.223 17.227 51.332 16.711 c 51.441 16.195 51.496 15.578\n" " 51.496 14.863 c 59.922 21.047 m 59.355 21.047 58.844 20.969 58.391 20.816\n" " c 57.945 20.656 57.566 20.426 57.254 20.129 c 56.941 19.824 56.699 19.449\n" " 56.531 19.004 c 56.363 18.559 56.281 18.055 56.281 17.484 c 56.281 16.785\n" " 56.402 16.199 56.641 15.727 c 56.887 15.246 57.223 14.859 57.645 14.566\n" " c 58.066 14.27 58.562 14.055 59.129 13.922 c 59.695 13.785 60.301 13.711\n" " 60.941 13.703 c 63.488 13.66 l 63.488 13.059 l 63.488 12.629 63.449 12.273\n" " 63.367 11.988 c 63.293 11.695 63.184 11.461 63.039 11.277 c 62.895 11.094\n" " 62.711 10.969 62.492 10.895 c 62.281 10.812 62.035 10.773 61.762 10.773\n" " c 61.508 10.773 61.277 10.801 61.074 10.852 c 60.879 10.902 60.707 10.992\n" " 60.562 11.125 c 60.418 11.25 60.297 11.422 60.203 11.637 c 60.117 11.848\n" " 60.055 12.117 60.016 12.445 c 56.824 12.297 l 56.91 11.781 57.07 11.309\n" " 57.305 10.887 c 57.539 10.457 57.859 10.086 58.266 9.773 c 58.68 9.461 \n" "59.188 9.219 59.785 9.051 c 60.391 8.875 61.098 8.789 61.906 8.789 c 62.641\n" " 8.789 63.297 8.879 63.875 9.062 c 64.449 9.246 64.938 9.52 65.34 9.883 \n" "c 65.742 10.238 66.047 10.68 66.258 11.203 c 66.469 11.727 66.574 12.336\n" " 66.574 13.027 c 66.574 17.332 l 66.574 17.609 66.586 17.855 66.605 18.074\n" " c 66.633 18.293 66.684 18.477 66.746 18.633 c 66.82 18.777 66.918 18.891\n" " 67.043 18.973 c 67.176 19.047 67.34 19.082 67.547 19.082 c 67.781 19.082\n" " 68.004 19.059 68.223 19.016 c 68.223 20.676 l 68.039 20.719 67.875 20.758\n" " 67.73 20.797 c 67.586 20.832 67.438 20.863 67.293 20.883 c 67.148 20.906\n" " 66.992 20.922 66.824 20.938 c 66.664 20.953 66.473 20.961 66.258 20.961\n" " c 65.484 20.961 64.914 20.773 64.543 20.395 c 64.18 20.016 63.961 19.457\n" " 63.887 18.723 c 63.82 18.723 l 63.414 19.457 62.887 20.031 62.246 20.438\n" " c 61.613 20.844 60.84 21.051 59.93 21.051 c 63.504 15.359 m 61.93 15.383\n" " l 61.602 15.398 61.289 15.426 60.988 15.469 c 60.695 15.504 60.438 15.59\n" " 60.211 15.719 c 59.992 15.844 59.816 16.023 59.688 16.266 c 59.555 16.508\n" " 59.492 16.832 59.492 17.25 c 59.492 17.812 59.621 18.23 59.875 18.508 c\n" " 60.137 18.777 60.484 18.91 60.914 18.91 c 61.309 18.91 61.664 18.828 61.984\n" " 18.66 c 62.305 18.492 62.574 18.273 62.793 18.004 c 63.02 17.727 63.195\n" " 17.41 63.316 17.055 c 63.441 16.699 63.504 16.332 63.504 15.961 c 63.504\n" " 15.359 l 69.656 20.832 m 69.652 11.781 l 69.652 11.527 69.648 11.258 69.641\n" " 10.973 c 69.641 10.688 69.633 10.418 69.617 10.164 c 69.609 9.902 69.602\n" " 9.668 69.594 9.465 c 69.586 9.254 69.574 9.102 69.562 9.008 c 72.492 9.008\n" " l 72.508 9.094 72.52 9.25 72.535 9.465 c 72.551 9.676 72.562 9.91 72.578\n" " 10.164 c 72.594 10.418 72.605 10.676 72.609 10.93 c 72.625 11.176 72.633\n" " 11.379 72.633 11.531 c 72.676 11.531 l 72.828 11.102 72.98 10.719 73.133\n" " 10.383 c 73.285 10.039 73.465 9.754 73.668 9.52 c 73.879 9.277 74.129 9.098\n" " 74.422 8.973 c 74.715 8.84 75.074 8.777 75.504 8.777 c 75.688 8.777 75.863\n" " 8.797 76.039 8.832 c 76.223 8.859 76.359 8.898 76.453 8.941 c 76.453 11.508\n" " l 76.258 11.465 76.055 11.426 75.852 11.398 c 75.656 11.363 75.418 11.344\n" " 75.141 11.344 c 74.375 11.344 73.777 11.652 73.348 12.273 c 72.926 12.895\n" " 72.715 13.809 72.715 15.027 c 72.715 20.828 l 69.645 20.828 l 81.367 21.023\n" " m 80.465 21.023 79.77 20.781 79.281 20.293 c 78.793 19.797 78.551 19.051\n" " 78.551 18.055 c 78.551 11.082 l 77.055 11.082 l 77.055 9.008 l 78.703 9.008\n" " l 79.664 6.23 l 81.586 6.23 l 81.586 9.008 l 83.824 9.008 l 83.824 11.082\n" " l 81.586 11.082 l 81.594 17.223 l 81.594 17.797 81.703 18.223 81.922 18.5\n" " c 82.141 18.77 82.48 18.902 82.938 18.902 c 83.125 18.902 83.297 18.887\n" " 83.449 18.859 c 83.602 18.832 83.773 18.793 83.961 18.75 c 83.961 20.652\n" " l 83.582 20.777 83.18 20.867 82.758 20.926 c 82.336 20.992 81.871 21.023\n" " 81.371 21.023 c 92.777 20.828 m 92.777 14.195 l 92.777 13.738 92.746 13.316\n" " 92.68 12.938 c 92.621 12.551 92.52 12.223 92.375 11.953 c 92.238 11.676\n" " 92.051 11.461 91.816 11.309 c 91.59 11.156 91.312 11.078 90.984 11.078 \n" "c 90.672 11.078 90.387 11.16 90.133 11.328 c 89.879 11.488 89.656 11.723\n" " 89.465 12.027 c 89.281 12.324 89.141 12.688 89.039 13.109 c 88.938 13.523\n" " 88.887 13.984 88.887 14.484 c 88.887 20.82 l 85.816 20.82 l 85.824 11.652\n" " l 85.824 11.398 85.82 11.137 85.812 10.867 c 85.812 10.598 85.805 10.344\n" " 85.789 10.102 c 85.781 9.855 85.773 9.637 85.766 9.445 c 85.758 9.25 85.746\n" " 9.102 85.734 9.008 c 88.664 9.008 l 88.68 9.094 88.691 9.238 88.707 9.434\n" " c 88.723 9.621 88.734 9.836 88.75 10.066 c 88.766 10.301 88.777 10.531 \n" "88.781 10.766 c 88.797 11 88.805 11.195 88.805 11.355 c 88.848 11.355 l \n" "89.227 10.453 89.695 9.797 90.258 9.387 c 90.824 8.98 91.504 8.773 92.289\n" " 8.773 c 93.191 8.773 93.918 8.996 94.465 9.441 c 95.02 9.879 95.391 10.516\n" " 95.578 11.355 c 95.645 11.355 l 95.855 10.867 96.082 10.461 96.32 10.133\n" " c 96.566 9.805 96.836 9.543 97.129 9.348 c 97.426 9.145 97.75 8.996 98.09\n" " 8.91 c 98.441 8.824 98.816 8.777 99.227 8.777 c 99.875 8.777 100.422 8.895\n" " 100.867 9.129 c 101.32 9.363 101.684 9.684 101.961 10.09 c 102.246 10.496\n" " 102.449 10.98 102.574 11.531 c 102.707 12.086 102.77 12.684 102.77 13.324\n" " c 102.77 20.832 l 99.723 20.832 l 99.723 14.199 l 99.723 13.742 99.691 \n" "13.32 99.625 12.941 c 99.566 12.555 99.465 12.227 99.32 11.957 c 99.184 \n" "11.68 98.996 11.465 98.762 11.312 c 98.535 11.16 98.258 11.082 97.93 11.082\n" " c 97.625 11.082 97.344 11.164 97.09 11.324 c 96.844 11.477 96.625 11.699\n" " 96.434 11.992 c 96.25 12.277 96.109 12.617 96.008 13.02 c 95.906 13.422\n" " 95.848 13.863 95.832 14.352 c 95.832 20.832 l 92.785 20.832 l 110.551 21.051\n" " m 109.699 21.051 108.93 20.926 108.246 20.68 c 107.57 20.426 106.988 20.043\n" " 106.508 19.531 c 106.027 19.016 105.66 18.371 105.402 17.598 c 105.148 \n" "16.816 105.02 15.906 105.02 14.867 c 105.02 13.738 105.168 12.789 105.469\n" " 12.016 c 105.773 11.242 106.184 10.621 106.691 10.148 c 107.207 9.668 107.801\n" " 9.32 108.473 9.109 c 109.145 8.898 109.848 8.793 110.594 8.793 c 111.527\n" " 8.793 112.32 8.957 112.977 9.285 c 113.641 9.605 114.184 10.059 114.605\n" " 10.641 c 115.027 11.223 115.336 11.922 115.535 12.738 c 115.73 13.547 115.832\n" " 14.441 115.832 15.426 c 115.832 15.512 l 108.25 15.512 l 108.25 16.008 \n" "108.293 16.469 108.383 16.898 c 108.469 17.32 108.613 17.688 108.809 18.004\n" " c 109.004 18.309 109.262 18.555 109.574 18.734 c 109.887 18.91 110.266 \n" "18.996 110.711 18.996 c 111.25 18.996 111.691 18.883 112.031 18.656 c 112.375\n" " 18.422 112.617 18.066 112.762 17.586 c 115.656 17.836 l 115.523 18.172 \n" "115.34 18.527 115.098 18.906 c 114.863 19.285 114.551 19.633 114.156 19.957\n" " c 113.762 20.27 113.27 20.531 112.68 20.742 c 112.098 20.945 111.387 21.047\n" " 110.551 21.047 c 110.551 10.723 m 110.238 10.723 109.941 10.777 109.664\n" " 10.887 c 109.395 10.988 109.156 11.156 108.953 11.391 c 108.758 11.617 \n" "108.598 11.91 108.473 12.277 c 108.348 12.641 108.281 13.078 108.266 13.59\n" " c 112.855 13.59 l 112.797 12.637 112.566 11.922 112.168 11.449 c 111.766\n" " 10.969 111.227 10.727 110.551 10.727 c 125.828 20.832 m 125.828 14.199 \n" "l 125.828 13.742 125.793 13.32 125.719 12.941 c 125.645 12.555 125.527 12.227\n" " 125.359 11.957 c 125.191 11.68 124.973 11.465 124.703 11.312 c 124.434 \n" "11.16 124.105 11.082 123.719 11.082 c 123.348 11.082 123.008 11.164 122.703\n" " 11.332 c 122.406 11.492 122.145 11.727 121.926 12.031 c 121.707 12.328 \n" "121.535 12.691 121.414 13.113 c 121.297 13.527 121.238 13.988 121.238 14.488\n" " c 121.238 20.824 l 118.168 20.824 l 118.168 11.652 l 118.168 11.398 118.164\n" " 11.137 118.156 10.867 c 118.156 10.598 118.148 10.344 118.133 10.102 c \n" "118.125 9.855 118.117 9.637 118.109 9.445 c 118.102 9.25 118.09 9.102 118.078\n" " 9.008 c 121.008 9.008 l 121.023 9.094 121.035 9.238 121.051 9.434 c 121.066\n" " 9.621 121.078 9.836 121.094 10.066 c 121.109 10.301 121.121 10.531 121.125\n" " 10.766 c 121.141 11 121.148 11.195 121.148 11.355 c 121.191 11.355 l 121.605\n" " 10.453 122.129 9.797 122.754 9.387 c 123.379 8.98 124.129 8.773 124.992\n" " 8.773 c 125.707 8.773 126.309 8.891 126.797 9.125 c 127.293 9.359 127.691\n" " 9.68 128 10.086 c 128.312 10.492 128.539 10.977 128.676 11.527 c 128.812\n" " 12.082 128.883 12.68 128.883 13.32 c 128.883 20.828 l 125.824 20.828 l \n" "134.871 21.023 m 133.969 21.023 133.273 20.781 132.785 20.293 c 132.297 \n" "19.797 132.055 19.051 132.055 18.055 c 132.055 11.082 l 130.559 11.082 l\n" " 130.559 9.008 l 132.207 9.008 l 133.168 6.23 l 135.09 6.23 l 135.09 9.008\n" " l 137.328 9.008 l 137.328 11.082 l 135.094 11.082 l 135.094 17.223 l 135.094\n" " 17.797 135.203 18.223 135.422 18.5 c 135.641 18.77 135.98 18.902 136.438\n" " 18.902 c 136.625 18.902 136.797 18.887 136.949 18.859 c 137.102 18.832 \n" "137.273 18.793 137.461 18.75 c 137.461 20.652 l 137.082 20.777 136.68 20.867\n" " 136.258 20.926 c 135.836 20.992 135.371 21.023 134.871 21.023 c 142.051\n" " 21.047 m 141.484 21.047 140.973 20.969 140.52 20.816 c 140.074 20.656 139.695\n" " 20.426 139.383 20.129 c 139.07 19.824 138.828 19.449 138.66 19.004 c 138.492\n" " 18.559 138.41 18.055 138.41 17.484 c 138.41 16.785 138.531 16.199 138.77\n" " 15.727 c 139.016 15.246 139.352 14.859 139.773 14.566 c 140.195 14.27 140.691\n" " 14.055 141.258 13.922 c 141.824 13.785 142.43 13.711 143.07 13.703 c 145.617\n" " 13.66 l 145.617 13.059 l 145.617 12.629 145.578 12.273 145.496 11.988 c\n" " 145.422 11.695 145.312 11.461 145.168 11.277 c 145.023 11.094 144.84 10.969\n" " 144.621 10.895 c 144.41 10.812 144.164 10.773 143.891 10.773 c 143.637 \n" "10.773 143.406 10.801 143.203 10.852 c 143.008 10.902 142.836 10.992 142.691\n" " 11.125 c 142.547 11.25 142.426 11.422 142.332 11.637 c 142.246 11.848 142.184\n" " 12.117 142.145 12.445 c 138.941 12.293 l 139.027 11.777 139.188 11.305 \n" "139.422 10.883 c 139.656 10.453 139.977 10.082 140.383 9.77 c 140.797 9.457\n" " 141.305 9.215 141.902 9.047 c 142.508 8.871 143.215 8.785 144.023 8.785\n" " c 144.758 8.785 145.414 8.875 145.992 9.059 c 146.566 9.242 147.055 9.516\n" " 147.457 9.879 c 147.859 10.234 148.164 10.676 148.375 11.199 c 148.586 \n" "11.723 148.691 12.332 148.691 13.023 c 148.691 17.328 l 148.691 17.605 148.703\n" " 17.852 148.723 18.07 c 148.75 18.289 148.801 18.473 148.863 18.629 c 148.938\n" " 18.773 149.035 18.887 149.16 18.969 c 149.293 19.043 149.457 19.078 149.664\n" " 19.078 c 149.898 19.078 150.121 19.055 150.34 19.012 c 150.34 20.672 l \n" "150.156 20.715 149.992 20.754 149.848 20.793 c 149.703 20.828 149.555 20.859\n" " 149.41 20.879 c 149.266 20.902 149.109 20.918 148.941 20.934 c 148.781 \n" "20.949 148.59 20.957 148.375 20.957 c 147.602 20.957 147.031 20.77 146.66\n" " 20.391 c 146.297 20.012 146.078 19.453 146.004 18.719 c 145.938 18.719 \n" "l 145.531 19.453 145.004 20.027 144.363 20.434 c 143.73 20.84 142.957 21.047\n" " 142.047 21.047 c 145.621 15.355 m 144.047 15.379 l 143.719 15.395 143.406\n" " 15.422 143.105 15.465 c 142.812 15.5 142.555 15.586 142.328 15.715 c 142.109\n" " 15.84 141.934 16.02 141.805 16.262 c 141.672 16.504 141.609 16.828 141.609\n" " 17.246 c 141.609 17.809 141.738 18.227 141.992 18.504 c 142.254 18.773 \n" "142.602 18.906 143.031 18.906 c 143.426 18.906 143.781 18.824 144.102 18.656\n" " c 144.422 18.488 144.691 18.27 144.91 18 c 145.137 17.723 145.312 17.406\n" " 145.434 17.051 c 145.559 16.695 145.621 16.328 145.621 15.957 c 145.621\n" " 15.355 l 151.773 20.828 m 151.777 4.613 l 154.848 4.613 l 154.848 20.828\n" " l 151.777 20.828 l f\n" "1 1 1 RG 0.531496 w\n" "0 J\n" "0 j\n" "[] 0.0 d\n" "4 M q 1 0 0 1 0 0 cm\n" "28.543 13.016 m 28.543 14.297 28.352 15.426 27.965 16.402 c 27.578 17.371\n" " 27.051 18.184 26.379 18.84 c 25.707 19.496 24.914 19.992 23.996 20.324 \n" "c 23.086 20.66 22.102 20.828 21.047 20.828 c 14.82 20.828 l 14.82 5.434 \n" "l 20.395 5.434 l 21.59 5.434 22.684 5.582 23.684 5.883 c 24.68 6.18 25.543\n" " 6.641 26.262 7.258 c 26.984 7.879 27.543 8.664 27.945 9.617 c 28.348 10.57\n" " 28.547 11.703 28.547 13.016 c 25.301 13.016 m 25.301 12.121 25.176 11.352\n" " 24.93 10.711 c 24.688 10.07 24.352 9.547 23.914 9.137 c 23.477 8.723 22.953\n" " 8.414 22.34 8.219 c 21.734 8.023 21.066 7.922 20.328 7.922 c 18.043 7.926\n" " l 18.043 18.34 l 20.773 18.34 l 21.422 18.34 22.02 18.227 22.566 18 c 23.121\n" " 17.773 23.598 17.438 23.996 16.996 c 24.402 16.543 24.723 15.988 24.945\n" " 15.324 c 25.18 14.66 25.297 13.895 25.297 13.02 c 35.895 21.051 m 35.043\n" " 21.051 34.273 20.926 33.59 20.68 c 32.914 20.426 32.332 20.043 31.852 19.531\n" " c 31.371 19.016 31.004 18.371 30.746 17.598 c 30.492 16.816 30.363 15.906\n" " 30.363 14.867 c 30.363 13.738 30.512 12.789 30.812 12.016 c 31.117 11.242\n" " 31.527 10.621 32.035 10.148 c 32.551 9.668 33.145 9.32 33.816 9.109 c 34.488\n" " 8.898 35.191 8.793 35.938 8.793 c 36.871 8.793 37.664 8.957 38.32 9.285\n" " c 38.984 9.605 39.527 10.059 39.949 10.641 c 40.371 11.223 40.68 11.922\n" " 40.879 12.738 c 41.074 13.547 41.176 14.441 41.176 15.426 c 41.176 15.512\n" " l 33.594 15.508 l 33.594 16.004 33.637 16.465 33.727 16.895 c 33.812 17.316\n" " 33.957 17.684 34.152 18 c 34.348 18.305 34.605 18.551 34.918 18.73 c 35.23\n" " 18.906 35.609 18.992 36.055 18.992 c 36.594 18.992 37.035 18.879 37.375\n" " 18.652 c 37.719 18.418 37.961 18.062 38.105 17.582 c 41 17.832 l 40.867\n" " 18.168 40.684 18.523 40.441 18.902 c 40.207 19.281 39.895 19.629 39.5 19.953\n" " c 39.105 20.266 38.613 20.527 38.023 20.738 c 37.441 20.941 36.73 21.043\n" " 35.895 21.043 c 35.895 10.719 m 35.582 10.719 35.285 10.773 35.008 10.883\n" " c 34.738 10.984 34.5 11.152 34.297 11.387 c 34.102 11.613 33.941 11.906\n" " 33.816 12.273 c 33.691 12.637 33.625 13.074 33.609 13.586 c 38.199 13.586\n" " l 38.141 12.633 37.91 11.918 37.512 11.445 c 37.109 10.965 36.57 10.723\n" " 35.895 10.723 c 54.699 14.863 m 54.699 15.758 54.609 16.586 54.426 17.344\n" " c 54.25 18.102 53.977 18.754 53.605 19.301 c 53.234 19.848 52.762 20.277\n" " 52.184 20.59 c 51.617 20.895 50.941 21.047 50.164 21.047 c 49.812 21.047\n" " 49.465 21.012 49.113 20.938 c 48.77 20.863 48.441 20.75 48.129 20.586 c\n" " 47.816 20.418 47.523 20.203 47.254 19.941 c 46.992 19.672 46.766 19.34 \n" "46.578 18.945 c 46.512 18.945 l 46.52 18.98 46.527 19.074 46.535 19.219 \n" "c 46.543 19.363 46.551 19.535 46.559 19.73 c 46.566 19.918 46.57 20.125 \n" "46.57 20.344 c 46.578 20.555 46.582 20.75 46.582 20.934 c 46.582 25.469 \n" "l 43.512 25.473 l 43.512 11.727 l 43.512 11.121 43.5 10.582 43.48 10.109\n" " c 43.465 9.637 43.449 9.27 43.426 9.004 c 46.41 9.004 l 46.426 9.055 46.438\n" " 9.152 46.453 9.301 c 46.477 9.445 46.488 9.613 46.496 9.805 c 46.512 9.992\n" " 46.523 10.191 46.527 10.395 c 46.535 10.598 46.539 10.777 46.539 10.93 \n" "c 46.582 10.93 l 46.953 10.148 47.465 9.594 48.113 9.258 c 48.762 8.922 \n" "49.512 8.754 50.363 8.754 c 51.113 8.754 51.762 8.906 52.309 9.211 c 52.855\n" " 9.516 53.305 9.938 53.652 10.477 c 54.008 11.016 54.273 11.66 54.438 12.41\n" " c 54.613 13.152 54.699 13.969 54.699 14.859 c 51.496 14.859 m 51.496 13.512\n" " 51.293 12.516 50.883 11.867 c 50.477 11.211 49.867 10.883 49.059 10.883\n" " c 48.754 10.883 48.445 10.949 48.141 11.078 c 47.844 11.203 47.574 11.422\n" " 47.332 11.734 c 47.098 12.039 46.906 12.457 46.754 12.98 c 46.609 13.496\n" " 46.535 14.152 46.535 14.949 c 46.535 15.723 46.609 16.367 46.754 16.883\n" " c 46.898 17.395 47.09 17.801 47.32 18.105 c 47.562 18.41 47.832 18.629 \n" "48.129 18.762 c 48.426 18.887 48.73 18.949 49.035 18.949 c 49.43 18.949 \n" "49.777 18.871 50.086 18.719 c 50.391 18.559 50.648 18.316 50.852 17.988 \n" "c 51.062 17.652 51.223 17.227 51.332 16.711 c 51.441 16.195 51.496 15.578\n" " 51.496 14.863 c 59.922 21.047 m 59.355 21.047 58.844 20.969 58.391 20.816\n" " c 57.945 20.656 57.566 20.426 57.254 20.129 c 56.941 19.824 56.699 19.449\n" " 56.531 19.004 c 56.363 18.559 56.281 18.055 56.281 17.484 c 56.281 16.785\n" " 56.402 16.199 56.641 15.727 c 56.887 15.246 57.223 14.859 57.645 14.566\n" " c 58.066 14.27 58.562 14.055 59.129 13.922 c 59.695 13.785 60.301 13.711\n" " 60.941 13.703 c 63.488 13.66 l 63.488 13.059 l 63.488 12.629 63.449 12.273\n" " 63.367 11.988 c 63.293 11.695 63.184 11.461 63.039 11.277 c 62.895 11.094\n" " 62.711 10.969 62.492 10.895 c 62.281 10.812 62.035 10.773 61.762 10.773\n" " c 61.508 10.773 61.277 10.801 61.074 10.852 c 60.879 10.902 60.707 10.992\n" " 60.562 11.125 c 60.418 11.25 60.297 11.422 60.203 11.637 c 60.117 11.848\n" " 60.055 12.117 60.016 12.445 c 56.824 12.297 l 56.91 11.781 57.07 11.309\n" " 57.305 10.887 c 57.539 10.457 57.859 10.086 58.266 9.773 c 58.68 9.461 \n" "59.188 9.219 59.785 9.051 c 60.391 8.875 61.098 8.789 61.906 8.789 c 62.641\n" " 8.789 63.297 8.879 63.875 9.062 c 64.449 9.246 64.938 9.52 65.34 9.883 \n" "c 65.742 10.238 66.047 10.68 66.258 11.203 c 66.469 11.727 66.574 12.336\n" " 66.574 13.027 c 66.574 17.332 l 66.574 17.609 66.586 17.855 66.605 18.074\n" " c 66.633 18.293 66.684 18.477 66.746 18.633 c 66.82 18.777 66.918 18.891\n" " 67.043 18.973 c 67.176 19.047 67.34 19.082 67.547 19.082 c 67.781 19.082\n" " 68.004 19.059 68.223 19.016 c 68.223 20.676 l 68.039 20.719 67.875 20.758\n" " 67.73 20.797 c 67.586 20.832 67.438 20.863 67.293 20.883 c 67.148 20.906\n" " 66.992 20.922 66.824 20.938 c 66.664 20.953 66.473 20.961 66.258 20.961\n" " c 65.484 20.961 64.914 20.773 64.543 20.395 c 64.18 20.016 63.961 19.457\n" " 63.887 18.723 c 63.82 18.723 l 63.414 19.457 62.887 20.031 62.246 20.438\n" " c 61.613 20.844 60.84 21.051 59.93 21.051 c 63.504 15.359 m 61.93 15.383\n" " l 61.602 15.398 61.289 15.426 60.988 15.469 c 60.695 15.504 60.438 15.59\n" " 60.211 15.719 c 59.992 15.844 59.816 16.023 59.688 16.266 c 59.555 16.508\n" " 59.492 16.832 59.492 17.25 c 59.492 17.812 59.621 18.23 59.875 18.508 c\n" " 60.137 18.777 60.484 18.91 60.914 18.91 c 61.309 18.91 61.664 18.828 61.984\n" " 18.66 c 62.305 18.492 62.574 18.273 62.793 18.004 c 63.02 17.727 63.195\n" " 17.41 63.316 17.055 c 63.441 16.699 63.504 16.332 63.504 15.961 c 63.504\n" " 15.359 l 69.656 20.832 m 69.652 11.781 l 69.652 11.527 69.648 11.258 69.641\n" " 10.973 c 69.641 10.688 69.633 10.418 69.617 10.164 c 69.609 9.902 69.602\n" " 9.668 69.594 9.465 c 69.586 9.254 69.574 9.102 69.562 9.008 c 72.492 9.008\n" " l 72.508 9.094 72.52 9.25 72.535 9.465 c 72.551 9.676 72.562 9.91 72.578\n" " 10.164 c 72.594 10.418 72.605 10.676 72.609 10.93 c 72.625 11.176 72.633\n" " 11.379 72.633 11.531 c 72.676 11.531 l 72.828 11.102 72.98 10.719 73.133\n" " 10.383 c 73.285 10.039 73.465 9.754 73.668 9.52 c 73.879 9.277 74.129 9.098\n" " 74.422 8.973 c 74.715 8.84 75.074 8.777 75.504 8.777 c 75.688 8.777 75.863\n" " 8.797 76.039 8.832 c 76.223 8.859 76.359 8.898 76.453 8.941 c 76.453 11.508\n" " l 76.258 11.465 76.055 11.426 75.852 11.398 c 75.656 11.363 75.418 11.344\n" " 75.141 11.344 c 74.375 11.344 73.777 11.652 73.348 12.273 c 72.926 12.895\n" " 72.715 13.809 72.715 15.027 c 72.715 20.828 l 69.645 20.828 l 81.367 21.023\n" " m 80.465 21.023 79.77 20.781 79.281 20.293 c 78.793 19.797 78.551 19.051\n" " 78.551 18.055 c 78.551 11.082 l 77.055 11.082 l 77.055 9.008 l 78.703 9.008\n" " l 79.664 6.23 l 81.586 6.23 l 81.586 9.008 l 83.824 9.008 l 83.824 11.082\n" " l 81.586 11.082 l 81.594 17.223 l 81.594 17.797 81.703 18.223 81.922 18.5\n" " c 82.141 18.77 82.48 18.902 82.938 18.902 c 83.125 18.902 83.297 18.887\n" " 83.449 18.859 c 83.602 18.832 83.773 18.793 83.961 18.75 c 83.961 20.652\n" " l 83.582 20.777 83.18 20.867 82.758 20.926 c 82.336 20.992 81.871 21.023\n" " 81.371 21.023 c 92.777 20.828 m 92.777 14.195 l 92.777 13.738 92.746 13.316\n" " 92.68 12.938 c 92.621 12.551 92.52 12.223 92.375 11.953 c 92.238 11.676\n" " 92.051 11.461 91.816 11.309 c 91.59 11.156 91.312 11.078 90.984 11.078 \n" "c 90.672 11.078 90.387 11.16 90.133 11.328 c 89.879 11.488 89.656 11.723\n" " 89.465 12.027 c 89.281 12.324 89.141 12.688 89.039 13.109 c 88.938 13.523\n" " 88.887 13.984 88.887 14.484 c 88.887 20.82 l 85.816 20.82 l 85.824 11.652\n" " l 85.824 11.398 85.82 11.137 85.812 10.867 c 85.812 10.598 85.805 10.344\n" " 85.789 10.102 c 85.781 9.855 85.773 9.637 85.766 9.445 c 85.758 9.25 85.746\n" " 9.102 85.734 9.008 c 88.664 9.008 l 88.68 9.094 88.691 9.238 88.707 9.434\n" " c 88.723 9.621 88.734 9.836 88.75 10.066 c 88.766 10.301 88.777 10.531 \n" "88.781 10.766 c 88.797 11 88.805 11.195 88.805 11.355 c 88.848 11.355 l \n" "89.227 10.453 89.695 9.797 90.258 9.387 c 90.824 8.98 91.504 8.773 92.289\n" " 8.773 c 93.191 8.773 93.918 8.996 94.465 9.441 c 95.02 9.879 95.391 10.516\n" " 95.578 11.355 c 95.645 11.355 l 95.855 10.867 96.082 10.461 96.32 10.133\n" " c 96.566 9.805 96.836 9.543 97.129 9.348 c 97.426 9.145 97.75 8.996 98.09\n" " 8.91 c 98.441 8.824 98.816 8.777 99.227 8.777 c 99.875 8.777 100.422 8.895\n" " 100.867 9.129 c 101.32 9.363 101.684 9.684 101.961 10.09 c 102.246 10.496\n" " 102.449 10.98 102.574 11.531 c 102.707 12.086 102.77 12.684 102.77 13.324\n" " c 102.77 20.832 l 99.723 20.832 l 99.723 14.199 l 99.723 13.742 99.691 \n" "13.32 99.625 12.941 c 99.566 12.555 99.465 12.227 99.32 11.957 c 99.184 \n" "11.68 98.996 11.465 98.762 11.312 c 98.535 11.16 98.258 11.082 97.93 11.082\n" " c 97.625 11.082 97.344 11.164 97.09 11.324 c 96.844 11.477 96.625 11.699\n" " 96.434 11.992 c 96.25 12.277 96.109 12.617 96.008 13.02 c 95.906 13.422\n" " 95.848 13.863 95.832 14.352 c 95.832 20.832 l 92.785 20.832 l 110.551 21.051\n" " m 109.699 21.051 108.93 20.926 108.246 20.68 c 107.57 20.426 106.988 20.043\n" " 106.508 19.531 c 106.027 19.016 105.66 18.371 105.402 17.598 c 105.148 \n" "16.816 105.02 15.906 105.02 14.867 c 105.02 13.738 105.168 12.789 105.469\n" " 12.016 c 105.773 11.242 106.184 10.621 106.691 10.148 c 107.207 9.668 107.801\n" " 9.32 108.473 9.109 c 109.145 8.898 109.848 8.793 110.594 8.793 c 111.527\n" " 8.793 112.32 8.957 112.977 9.285 c 113.641 9.605 114.184 10.059 114.605\n" " 10.641 c 115.027 11.223 115.336 11.922 115.535 12.738 c 115.73 13.547 115.832\n" " 14.441 115.832 15.426 c 115.832 15.512 l 108.25 15.512 l 108.25 16.008 \n" "108.293 16.469 108.383 16.898 c 108.469 17.32 108.613 17.688 108.809 18.004\n" " c 109.004 18.309 109.262 18.555 109.574 18.734 c 109.887 18.91 110.266 \n" "18.996 110.711 18.996 c 111.25 18.996 111.691 18.883 112.031 18.656 c 112.375\n" " 18.422 112.617 18.066 112.762 17.586 c 115.656 17.836 l 115.523 18.172 \n" "115.34 18.527 115.098 18.906 c 114.863 19.285 114.551 19.633 114.156 19.957\n" " c 113.762 20.27 113.27 20.531 112.68 20.742 c 112.098 20.945 111.387 21.047\n" " 110.551 21.047 c 110.551 10.723 m 110.238 10.723 109.941 10.777 109.664\n" " 10.887 c 109.395 10.988 109.156 11.156 108.953 11.391 c 108.758 11.617 \n" "108.598 11.91 108.473 12.277 c 108.348 12.641 108.281 13.078 108.266 13.59\n" " c 112.855 13.59 l 112.797 12.637 112.566 11.922 112.168 11.449 c 111.766\n" " 10.969 111.227 10.727 110.551 10.727 c 125.828 20.832 m 125.828 14.199 \n" "l 125.828 13.742 125.793 13.32 125.719 12.941 c 125.645 12.555 125.527 12.227\n" " 125.359 11.957 c 125.191 11.68 124.973 11.465 124.703 11.312 c 124.434 \n" "11.16 124.105 11.082 123.719 11.082 c 123.348 11.082 123.008 11.164 122.703\n" " 11.332 c 122.406 11.492 122.145 11.727 121.926 12.031 c 121.707 12.328 \n" "121.535 12.691 121.414 13.113 c 121.297 13.527 121.238 13.988 121.238 14.488\n" " c 121.238 20.824 l 118.168 20.824 l 118.168 11.652 l 118.168 11.398 118.164\n" " 11.137 118.156 10.867 c 118.156 10.598 118.148 10.344 118.133 10.102 c \n" "118.125 9.855 118.117 9.637 118.109 9.445 c 118.102 9.25 118.09 9.102 118.078\n" " 9.008 c 121.008 9.008 l 121.023 9.094 121.035 9.238 121.051 9.434 c 121.066\n" " 9.621 121.078 9.836 121.094 10.066 c 121.109 10.301 121.121 10.531 121.125\n" " 10.766 c 121.141 11 121.148 11.195 121.148 11.355 c 121.191 11.355 l 121.605\n" " 10.453 122.129 9.797 122.754 9.387 c 123.379 8.98 124.129 8.773 124.992\n" " 8.773 c 125.707 8.773 126.309 8.891 126.797 9.125 c 127.293 9.359 127.691\n" " 9.68 128 10.086 c 128.312 10.492 128.539 10.977 128.676 11.527 c 128.812\n" " 12.082 128.883 12.68 128.883 13.32 c 128.883 20.828 l 125.824 20.828 l \n" "134.871 21.023 m 133.969 21.023 133.273 20.781 132.785 20.293 c 132.297 \n" "19.797 132.055 19.051 132.055 18.055 c 132.055 11.082 l 130.559 11.082 l\n" " 130.559 9.008 l 132.207 9.008 l 133.168 6.23 l 135.09 6.23 l 135.09 9.008\n" " l 137.328 9.008 l 137.328 11.082 l 135.094 11.082 l 135.094 17.223 l 135.094\n" " 17.797 135.203 18.223 135.422 18.5 c 135.641 18.77 135.98 18.902 136.438\n" " 18.902 c 136.625 18.902 136.797 18.887 136.949 18.859 c 137.102 18.832 \n" "137.273 18.793 137.461 18.75 c 137.461 20.652 l 137.082 20.777 136.68 20.867\n" " 136.258 20.926 c 135.836 20.992 135.371 21.023 134.871 21.023 c 142.051\n" " 21.047 m 141.484 21.047 140.973 20.969 140.52 20.816 c 140.074 20.656 139.695\n" " 20.426 139.383 20.129 c 139.07 19.824 138.828 19.449 138.66 19.004 c 138.492\n" " 18.559 138.41 18.055 138.41 17.484 c 138.41 16.785 138.531 16.199 138.77\n" " 15.727 c 139.016 15.246 139.352 14.859 139.773 14.566 c 140.195 14.27 140.691\n" " 14.055 141.258 13.922 c 141.824 13.785 142.43 13.711 143.07 13.703 c 145.617\n" " 13.66 l 145.617 13.059 l 145.617 12.629 145.578 12.273 145.496 11.988 c\n" " 145.422 11.695 145.312 11.461 145.168 11.277 c 145.023 11.094 144.84 10.969\n" " 144.621 10.895 c 144.41 10.812 144.164 10.773 143.891 10.773 c 143.637 \n" "10.773 143.406 10.801 143.203 10.852 c 143.008 10.902 142.836 10.992 142.691\n" " 11.125 c 142.547 11.25 142.426 11.422 142.332 11.637 c 142.246 11.848 142.184\n" " 12.117 142.145 12.445 c 138.941 12.293 l 139.027 11.777 139.188 11.305 \n" "139.422 10.883 c 139.656 10.453 139.977 10.082 140.383 9.77 c 140.797 9.457\n" " 141.305 9.215 141.902 9.047 c 142.508 8.871 143.215 8.785 144.023 8.785\n" " c 144.758 8.785 145.414 8.875 145.992 9.059 c 146.566 9.242 147.055 9.516\n" " 147.457 9.879 c 147.859 10.234 148.164 10.676 148.375 11.199 c 148.586 \n" "11.723 148.691 12.332 148.691 13.023 c 148.691 17.328 l 148.691 17.605 148.703\n" " 17.852 148.723 18.07 c 148.75 18.289 148.801 18.473 148.863 18.629 c 148.938\n" " 18.773 149.035 18.887 149.16 18.969 c 149.293 19.043 149.457 19.078 149.664\n" " 19.078 c 149.898 19.078 150.121 19.055 150.34 19.012 c 150.34 20.672 l \n" "150.156 20.715 149.992 20.754 149.848 20.793 c 149.703 20.828 149.555 20.859\n" " 149.41 20.879 c 149.266 20.902 149.109 20.918 148.941 20.934 c 148.781 \n" "20.949 148.59 20.957 148.375 20.957 c 147.602 20.957 147.031 20.77 146.66\n" " 20.391 c 146.297 20.012 146.078 19.453 146.004 18.719 c 145.938 18.719 \n" "l 145.531 19.453 145.004 20.027 144.363 20.434 c 143.73 20.84 142.957 21.047\n" " 142.047 21.047 c 145.621 15.355 m 144.047 15.379 l 143.719 15.395 143.406\n" " 15.422 143.105 15.465 c 142.812 15.5 142.555 15.586 142.328 15.715 c 142.109\n" " 15.84 141.934 16.02 141.805 16.262 c 141.672 16.504 141.609 16.828 141.609\n" " 17.246 c 141.609 17.809 141.738 18.227 141.992 18.504 c 142.254 18.773 \n" "142.602 18.906 143.031 18.906 c 143.426 18.906 143.781 18.824 144.102 18.656\n" " c 144.422 18.488 144.691 18.27 144.91 18 c 145.137 17.723 145.312 17.406\n" " 145.434 17.051 c 145.559 16.695 145.621 16.328 145.621 15.957 c 145.621\n" " 15.355 l 151.773 20.828 m 151.777 4.613 l 154.848 4.613 l 154.848 20.828\n" " l 151.777 20.828 l S Q\n" "Q q\n" "0 0.298039 0.431373 rg /a1 gs\n" "1 1 1 RG 0.797243 w\n" "1 J\n" "1 j\n" "[] 0.0 d\n" "4 M q 1 0 0 1 0 0 cm\n" "3.398 0.398 m 1.77 0.398 0.398 1.77 0.398 3.398 c 0.398 23.086 l 0.398 \n" "24.715 1.77 26.086 3.398 26.086 c 167.109 26.086 l 168.738 26.086 170.109\n" " 24.715 170.109 23.086 c 170.109 3.398 l 170.109 1.77 168.738 0.398 167.109\n" " 0.398 c h\n" "3.867 3.844 m 166.664 3.844 l 166.664 22.617 l 3.867 22.617 l h\n" "3.867 3.844 m B Q\n" "Q\n"; static Dict *getDepartmentalStampExtGStateDict(PDFDoc *doc) { Dict *a0Dict = new Dict(doc->getXRef()); a0Dict->add("CA", Object(0.588235)); a0Dict->add("ca", Object(0.588235)); Dict *a1Dict = new Dict(doc->getXRef()); a1Dict->add("CA", Object(1)); a1Dict->add("ca", Object(1)); Dict *extGStateDict = new Dict(doc->getXRef()); extGStateDict->add("a0", Object(a0Dict)); extGStateDict->add("a1", Object(a1Dict)); return extGStateDict; } #endif poppler-24.02.0/poppler/annot_stamp_draft.h000066400000000000000000000260151455701731300207070ustar00rootroot00000000000000//======================================================================== // // annot_stamp_draft.h // // Copyright (C) 2021 Mahmoud Ahmed Khalil // Copyright (C) 2021 Albert Astals Cid // // Mechanically extracted from an SVG created for Okular by Eugene Trounev eugene.trounev@gmail.com // // Licensed under GPLv2 or later // //======================================================================== #ifndef ANNOT_STAMP_DRAFT_H #define ANNOT_STAMP_DRAFT_H #include "Dict.h" #include "Object.h" #include "PDFDoc.h" static const double ANNOT_STAMP_DRAFT_WIDTH = 79.758179; static const double ANNOT_STAMP_DRAFT_HEIGHT = 26.484743; static const char *ANNOT_STAMP_DRAFT = "1 0 0 -1 0 26.484741 cm\n" "q\n" "1 1 1 rg /a0 gs\n" "3.406 2.129 m 76.367 2.129 l 77.066 2.129 77.637 2.828 77.637 3.398 c 77.637\n" " 23.09 l 77.637 23.789 77.07 24.359 76.367 24.359 c 3.406 24.359 l 2.707\n" " 24.359 2.137 23.66 2.137 23.09 c 2.137 3.398 l 2.137 2.699 2.703 2.129 \n" "3.406 2.129 c h\n" "3.406 2.129 m f\n" "0.215686 0.215686 0.215686 rg /a1 gs\n" "1 1 1 RG 0.531496 w\n" "0 J\n" "0 j\n" "[] 0.0 d\n" "4 M q 1 0 0 1 0 0 cm\n" "28.543 13.016 m 28.543 14.297 28.352 15.426 27.965 16.402 c 27.578 17.371\n" " 27.051 18.184 26.379 18.84 c 25.707 19.496 24.914 19.992 23.996 20.324 \n" "c 23.086 20.66 22.102 20.828 21.047 20.828 c 14.82 20.828 l 14.82 5.434 \n" "l 20.395 5.434 l 21.59 5.434 22.684 5.582 23.684 5.883 c 24.68 6.18 25.543\n" " 6.641 26.262 7.258 c 26.984 7.879 27.543 8.664 27.945 9.617 c 28.348 10.57\n" " 28.547 11.703 28.547 13.016 c 25.301 13.016 m 25.301 12.121 25.176 11.352\n" " 24.93 10.711 c 24.688 10.07 24.352 9.547 23.914 9.137 c 23.477 8.723 22.953\n" " 8.414 22.34 8.219 c 21.734 8.023 21.066 7.922 20.328 7.922 c 18.043 7.926\n" " l 18.043 18.34 l 20.773 18.34 l 21.422 18.34 22.02 18.227 22.566 18 c 23.121\n" " 17.773 23.598 17.438 23.996 16.996 c 24.402 16.543 24.723 15.988 24.945\n" " 15.324 c 25.18 14.66 25.297 13.895 25.297 13.02 c 31.055 20.832 m 31.055\n" " 11.785 l 31.055 11.531 31.051 11.262 31.043 10.977 c 31.043 10.691 31.035\n" " 10.422 31.02 10.168 c 31.012 9.906 31.004 9.672 30.996 9.469 c 30.988 9.258\n" " 30.977 9.105 30.965 9.012 c 33.895 9.012 l 33.91 9.098 33.922 9.254 33.938\n" " 9.469 c 33.953 9.68 33.965 9.914 33.98 10.168 c 33.996 10.422 34.008 10.68\n" " 34.012 10.934 c 34.027 11.18 34.035 11.383 34.035 11.535 c 34.078 11.535\n" " l 34.23 11.105 34.383 10.723 34.535 10.387 c 34.688 10.043 34.867 9.758\n" " 35.07 9.523 c 35.281 9.281 35.531 9.102 35.824 8.977 c 36.117 8.844 36.477\n" " 8.781 36.906 8.781 c 37.09 8.781 37.266 8.801 37.441 8.836 c 37.625 8.863\n" " 37.762 8.902 37.855 8.945 c 37.855 11.512 l 37.66 11.469 37.457 11.43 37.254\n" " 11.402 c 37.059 11.367 36.82 11.348 36.543 11.348 c 35.777 11.348 35.18\n" " 11.656 34.75 12.277 c 34.328 12.898 34.117 13.812 34.117 15.031 c 34.117\n" " 20.832 l 31.047 20.832 l 42.477 21.051 m 41.91 21.051 41.398 20.973 40.945\n" " 20.82 c 40.5 20.66 40.121 20.43 39.809 20.133 c 39.496 19.828 39.254 19.453\n" " 39.086 19.008 c 38.918 18.562 38.836 18.059 38.836 17.488 c 38.836 16.789\n" " 38.957 16.203 39.195 15.73 c 39.441 15.25 39.777 14.863 40.199 14.57 c \n" "40.621 14.273 41.117 14.059 41.684 13.926 c 42.25 13.789 42.855 13.715 43.496\n" " 13.707 c 46.043 13.664 l 46.043 13.062 l 46.043 12.633 46.004 12.277 45.922\n" " 11.992 c 45.848 11.699 45.738 11.465 45.594 11.281 c 45.449 11.098 45.266\n" " 10.973 45.047 10.898 c 44.836 10.816 44.59 10.777 44.316 10.777 c 44.062\n" " 10.777 43.832 10.805 43.629 10.855 c 43.434 10.906 43.262 10.996 43.117\n" " 11.129 c 42.973 11.254 42.852 11.426 42.758 11.641 c 42.672 11.852 42.609\n" " 12.121 42.57 12.449 c 39.367 12.297 l 39.453 11.781 39.613 11.309 39.848\n" " 10.887 c 40.082 10.457 40.402 10.086 40.809 9.773 c 41.223 9.461 41.73 \n" "9.219 42.328 9.051 c 42.934 8.875 43.641 8.789 44.449 8.789 c 45.184 8.789\n" " 45.84 8.879 46.418 9.062 c 46.992 9.246 47.48 9.52 47.883 9.883 c 48.285\n" " 10.238 48.59 10.68 48.801 11.203 c 49.012 11.727 49.117 12.336 49.117 13.027\n" " c 49.117 17.332 l 49.117 17.609 49.129 17.855 49.148 18.074 c 49.176 18.293\n" " 49.227 18.477 49.289 18.633 c 49.363 18.777 49.461 18.891 49.586 18.973\n" " c 49.719 19.047 49.883 19.082 50.09 19.082 c 50.324 19.082 50.547 19.059\n" " 50.766 19.016 c 50.766 20.676 l 50.582 20.719 50.418 20.758 50.273 20.797\n" " c 50.129 20.832 49.98 20.863 49.836 20.883 c 49.691 20.906 49.535 20.922\n" " 49.367 20.938 c 49.207 20.953 49.016 20.961 48.801 20.961 c 48.027 20.961\n" " 47.457 20.773 47.086 20.395 c 46.723 20.016 46.504 19.457 46.43 18.723 \n" "c 46.375 18.719 l 45.969 19.453 45.441 20.027 44.801 20.434 c 44.168 20.84\n" " 43.395 21.047 42.484 21.047 c 46.059 15.355 m 44.484 15.379 l 44.156 15.395\n" " 43.844 15.422 43.543 15.465 c 43.25 15.5 42.992 15.586 42.766 15.715 c \n" "42.547 15.84 42.371 16.02 42.242 16.262 c 42.109 16.504 42.047 16.828 42.047\n" " 17.246 c 42.047 17.809 42.176 18.227 42.43 18.504 c 42.691 18.773 43.039\n" " 18.91 43.469 18.906 c 43.863 18.906 44.219 18.824 44.539 18.656 c 44.859\n" " 18.488 45.129 18.27 45.348 18 c 45.574 17.723 45.75 17.406 45.871 17.051\n" " c 45.996 16.695 46.059 16.328 46.059 15.957 c 46.059 15.355 l 55.816 11.082\n" " m 55.816 20.828 l 52.758 20.828 l 52.758 11.082 l 51.031 11.082 l 51.031\n" " 9.008 l 52.758 9.008 l 52.758 7.773 l 52.758 7.32 52.812 6.906 52.922 6.527\n" " c 53.031 6.141 53.219 5.805 53.48 5.523 c 53.742 5.238 54.098 5.016 54.539\n" " 4.855 c 54.984 4.695 55.535 4.613 56.199 4.613 c 56.551 4.613 56.891 4.633\n" " 57.227 4.668 c 57.57 4.703 57.875 4.746 58.145 4.789 c 58.145 6.766 l 58.012\n" " 6.738 57.863 6.715 57.695 6.699 c 57.535 6.676 57.387 6.668 57.246 6.668\n" " c 56.961 6.668 56.727 6.695 56.535 6.754 c 56.352 6.812 56.207 6.898 56.098\n" " 7.016 c 55.996 7.133 55.922 7.281 55.879 7.465 c 55.836 7.641 55.812 7.844\n" " 55.812 8.078 c 55.812 9.008 l 58.141 9.008 l 58.141 11.082 l 55.812 11.082\n" " l 62.707 21.027 m 61.805 21.027 61.109 20.785 60.621 20.297 c 60.133 19.801\n" " 59.891 19.055 59.891 18.059 c 59.891 11.086 l 58.395 11.086 l 58.395 9.012\n" " l 60.043 9.012 l 61.004 6.234 l 62.926 6.234 l 62.926 9.012 l 65.164 9.012\n" " l 65.164 11.086 l 62.926 11.086 l 62.926 17.227 l 62.926 17.801 63.035 \n" "18.227 63.254 18.504 c 63.473 18.773 63.812 18.91 64.27 18.906 c 64.457 \n" "18.906 64.629 18.891 64.781 18.863 c 64.934 18.836 65.105 18.797 65.293 \n" "18.754 c 65.293 20.656 l 64.914 20.781 64.512 20.871 64.09 20.93 c 63.668\n" " 20.996 63.203 21.027 62.703 21.027 c B Q\n" "Q q\n" "0.211765 0.211765 0.211765 rg /a1 gs\n" "1 1 1 RG 0.797243 w\n" "1 J\n" "1 j\n" "[] 0.0 d\n" "4 M q 1 0 0 1 0 0 cm\n" "3.398 0.398 m 1.77 0.398 0.398 1.77 0.398 3.398 c 0.398 23.086 l 0.398 \n" "24.715 1.77 26.086 3.398 26.086 c 76.359 26.086 l 77.988 26.086 79.359 24.715\n" " 79.359 23.086 c 79.359 3.398 l 79.359 1.77 77.988 0.398 76.359 0.398 c \n" "h\n" "3.867 3.844 m 75.914 3.844 l 75.914 22.617 l 3.867 22.617 l h\n" "3.867 3.844 m B Q\n" "Q\n"; static Dict *getDraftStampExtGStateDict(PDFDoc *doc) { Dict *a0Dict = new Dict(doc->getXRef()); a0Dict->add("CA", Object(0.588235)); a0Dict->add("ca", Object(0.588235)); Dict *a1Dict = new Dict(doc->getXRef()); a1Dict->add("CA", Object(1)); a1Dict->add("ca", Object(1)); Dict *extGStateDict = new Dict(doc->getXRef()); extGStateDict->add("a0", Object(a0Dict)); extGStateDict->add("a1", Object(a1Dict)); return extGStateDict; } #endif poppler-24.02.0/poppler/annot_stamp_experimental.h000066400000000000000000001241041455701731300223020ustar00rootroot00000000000000//======================================================================== // // annot_stamp_experimental.h // // Copyright (C) 2021 Mahmoud Ahmed Khalil // Copyright (C) 2021 Albert Astals Cid // // Mechanically extracted from an SVG created for Okular by Eugene Trounev eugene.trounev@gmail.com // // Licensed under GPLv2 or later // //======================================================================== #ifndef ANNOT_STAMP_EXPERIMENTAL_H #define ANNOT_STAMP_EXPERIMENTAL_H #include "Dict.h" #include "Object.h" #include "PDFDoc.h" static const double ANNOT_STAMP_EXPERIMENTAL_WIDTH = 170.508179; static const double ANNOT_STAMP_EXPERIMENTAL_HEIGHT = 26.484743; static const char *ANNOT_STAMP_EXPERIMENTAL = "1 0 0 -1 0 26.484741 cm\n" "q\n" "1 1 1 rg /a0 gs\n" "3.406 2.129 m 167.117 2.129 l 167.816 2.129 168.387 2.828 168.387 3.398\n" " c 168.387 23.09 l 168.387 23.789 167.82 24.359 167.117 24.359 c 3.406 24.359\n" " l 2.707 24.359 2.137 23.66 2.137 23.09 c 2.137 3.398 l 2.137 2.699 2.703\n" " 2.129 3.406 2.129 c h\n" "3.406 2.129 m f\n" "0 0.298039 0.431373 rg /a1 gs\n" "14.82 20.828 m 14.82 5.434 l 26.926 5.434 l 26.926 7.926 l 18.043 7.926\n" " l 18.043 11.793 l 26.262 11.793 l 26.262 14.285 l 18.043 14.285 l 18.043\n" " 18.34 l 27.375 18.34 l 27.375 20.832 l 14.82 20.828 l 37.219 20.828 m 34.465\n" " 16.543 l 31.688 20.828 l 28.422 20.828 l 32.75 14.719 l 28.629 9.004 l \n" "31.941 9.004 l 34.465 12.871 l 36.977 9.004 l 40.309 9.004 l 36.188 14.688\n" " l 40.547 20.828 l 37.215 20.828 l 53.473 14.863 m 53.473 15.758 53.383 \n" "16.586 53.199 17.344 c 53.023 18.102 52.75 18.754 52.379 19.301 c 52.008\n" " 19.848 51.535 20.277 50.957 20.59 c 50.391 20.895 49.715 21.047 48.938 \n" "21.047 c 48.586 21.047 48.238 21.012 47.887 20.938 c 47.543 20.863 47.215\n" " 20.75 46.902 20.586 c 46.59 20.418 46.297 20.203 46.027 19.941 c 45.766\n" " 19.672 45.539 19.34 45.352 18.945 c 45.285 18.945 l 45.293 18.98 45.301\n" " 19.074 45.309 19.219 c 45.316 19.363 45.324 19.535 45.332 19.73 c 45.34\n" " 19.918 45.344 20.125 45.344 20.344 c 45.352 20.555 45.355 20.75 45.355 \n" "20.934 c 45.355 25.469 l 42.285 25.469 l 42.285 11.723 l 42.285 11.117 42.273\n" " 10.578 42.254 10.105 c 42.238 9.633 42.223 9.266 42.199 9 c 45.184 9 l \n" "45.199 9.051 45.211 9.148 45.227 9.297 c 45.25 9.441 45.262 9.609 45.27 \n" "9.801 c 45.285 9.988 45.297 10.188 45.301 10.391 c 45.309 10.594 45.312 \n" "10.773 45.312 10.926 c 45.355 10.926 l 45.727 10.145 46.238 9.59 46.887 \n" "9.254 c 47.535 8.918 48.285 8.75 49.137 8.75 c 49.887 8.75 50.535 8.902 \n" "51.082 9.207 c 51.629 9.512 52.078 9.934 52.426 10.473 c 52.781 11.012 53.047\n" " 11.656 53.211 12.406 c 53.387 13.148 53.473 13.965 53.473 14.855 c 50.27\n" " 14.855 m 50.27 13.508 50.066 12.512 49.656 11.863 c 49.25 11.207 48.641\n" " 10.879 47.832 10.879 c 47.527 10.879 47.219 10.945 46.914 11.074 c 46.617\n" " 11.199 46.348 11.418 46.105 11.73 c 45.871 12.035 45.68 12.453 45.527 12.977\n" " c 45.383 13.492 45.309 14.148 45.309 14.945 c 45.309 15.719 45.383 16.363\n" " 45.527 16.879 c 45.672 17.391 45.863 17.797 46.094 18.102 c 46.336 18.406\n" " 46.605 18.625 46.902 18.758 c 47.199 18.883 47.504 18.945 47.809 18.945\n" " c 48.203 18.945 48.551 18.867 48.859 18.715 c 49.164 18.555 49.422 18.312\n" " 49.625 17.984 c 49.836 17.648 49.996 17.223 50.105 16.707 c 50.215 16.191\n" " 50.27 15.574 50.27 14.859 c 60.805 21.043 m 59.953 21.043 59.184 20.918\n" " 58.5 20.672 c 57.824 20.418 57.242 20.035 56.762 19.523 c 56.281 19.008\n" " 55.914 18.363 55.656 17.59 c 55.402 16.809 55.273 15.898 55.273 14.859 \n" "c 55.273 13.73 55.422 12.781 55.723 12.008 c 56.027 11.234 56.438 10.613\n" " 56.945 10.141 c 57.461 9.66 58.055 9.312 58.727 9.102 c 59.398 8.891 60.102\n" " 8.785 60.848 8.785 c 61.781 8.785 62.574 8.949 63.23 9.277 c 63.895 9.598\n" " 64.438 10.051 64.859 10.633 c 65.281 11.215 65.59 11.914 65.789 12.73 c\n" " 65.984 13.539 66.086 14.434 66.086 15.418 c 66.086 15.504 l 58.504 15.508\n" " l 58.504 16.004 58.547 16.465 58.637 16.895 c 58.723 17.316 58.867 17.684\n" " 59.062 18 c 59.258 18.305 59.516 18.551 59.828 18.73 c 60.141 18.906 60.52\n" " 18.992 60.965 18.992 c 61.504 18.992 61.945 18.879 62.285 18.652 c 62.629\n" " 18.418 62.871 18.062 63.016 17.582 c 65.91 17.832 l 65.777 18.168 65.594\n" " 18.523 65.352 18.902 c 65.117 19.281 64.805 19.629 64.41 19.953 c 64.016\n" " 20.266 63.523 20.527 62.934 20.738 c 62.352 20.941 61.641 21.043 60.805\n" " 21.043 c 60.805 10.719 m 60.492 10.719 60.195 10.773 59.918 10.883 c 59.648\n" " 10.984 59.41 11.152 59.207 11.387 c 59.012 11.613 58.852 11.906 58.727 \n" "12.273 c 58.602 12.637 58.535 13.074 58.52 13.586 c 63.109 13.586 l 63.051\n" " 12.633 62.82 11.918 62.422 11.445 c 62.02 10.965 61.48 10.723 60.805 10.723\n" " c 68.422 20.828 m 68.422 11.781 l 68.422 11.527 68.418 11.258 68.41 10.973\n" " c 68.41 10.688 68.402 10.418 68.387 10.164 c 68.379 9.902 68.371 9.668 \n" "68.363 9.465 c 68.355 9.254 68.344 9.102 68.332 9.008 c 71.262 9.008 l 71.277\n" " 9.094 71.289 9.25 71.305 9.465 c 71.32 9.676 71.332 9.91 71.348 10.164 \n" "c 71.363 10.418 71.375 10.676 71.379 10.93 c 71.395 11.176 71.402 11.379\n" " 71.402 11.531 c 71.445 11.531 l 71.598 11.102 71.75 10.719 71.902 10.383\n" " c 72.055 10.039 72.234 9.754 72.438 9.52 c 72.648 9.277 72.898 9.098 73.191\n" " 8.973 c 73.484 8.84 73.844 8.777 74.273 8.777 c 74.457 8.777 74.633 8.797\n" " 74.809 8.832 c 74.992 8.859 75.129 8.898 75.223 8.941 c 75.223 11.508 l\n" " 75.027 11.465 74.824 11.426 74.621 11.398 c 74.426 11.363 74.188 11.344\n" " 73.91 11.344 c 73.145 11.344 72.547 11.652 72.117 12.273 c 71.695 12.895\n" " 71.484 13.809 71.484 15.027 c 71.484 20.828 l 68.426 20.828 l 77.125 6.875\n" " m 77.125 4.613 l 80.195 4.613 l 80.195 6.875 l 77.125 6.875 l 77.125 20.828\n" " m 77.125 9.004 l 80.195 9.004 l 80.195 20.828 l 77.125 20.828 l 90.293 \n" "20.828 m 90.293 14.195 l 90.293 13.738 90.262 13.316 90.195 12.938 c 90.137\n" " 12.551 90.035 12.223 89.891 11.953 c 89.754 11.676 89.566 11.461 89.332\n" " 11.309 c 89.105 11.156 88.828 11.078 88.5 11.078 c 88.188 11.078 87.902\n" " 11.16 87.648 11.328 c 87.395 11.488 87.172 11.723 86.98 12.027 c 86.797\n" " 12.324 86.656 12.688 86.555 13.109 c 86.453 13.523 86.402 13.984 86.402\n" " 14.484 c 86.402 20.82 l 83.332 20.82 l 83.332 11.641 l 83.332 11.387 83.328\n" " 11.125 83.32 10.855 c 83.32 10.586 83.312 10.332 83.297 10.09 c 83.289 \n" "9.844 83.281 9.625 83.273 9.434 c 83.266 9.238 83.254 9.09 83.242 8.996 \n" "c 86.172 8.996 l 86.188 9.082 86.199 9.227 86.215 9.422 c 86.23 9.609 86.242\n" " 9.824 86.258 10.055 c 86.273 10.289 86.285 10.52 86.289 10.754 c 86.305\n" " 10.988 86.312 11.184 86.312 11.344 c 86.355 11.344 l 86.734 10.441 87.203\n" " 9.785 87.766 9.375 c 88.332 8.969 89.012 8.762 89.797 8.762 c 90.699 8.762\n" " 91.426 8.984 91.973 9.43 c 92.527 9.867 92.898 10.504 93.086 11.344 c 93.152\n" " 11.344 l 93.363 10.855 93.59 10.449 93.828 10.121 c 94.074 9.793 94.344\n" " 9.531 94.637 9.336 c 94.934 9.133 95.258 8.984 95.598 8.898 c 95.949 8.812\n" " 96.324 8.766 96.734 8.766 c 97.383 8.766 97.93 8.883 98.375 9.117 c 98.828\n" " 9.352 99.191 9.672 99.469 10.078 c 99.754 10.484 99.957 10.969 100.082 \n" "11.52 c 100.215 12.074 100.277 12.672 100.277 13.312 c 100.277 20.82 l 97.23\n" " 20.82 l 97.23 14.188 l 97.23 13.73 97.199 13.309 97.133 12.93 c 97.074 \n" "12.543 96.973 12.215 96.828 11.945 c 96.691 11.668 96.504 11.453 96.27 11.301\n" " c 96.043 11.148 95.766 11.07 95.438 11.07 c 95.133 11.07 94.852 11.152 \n" "94.598 11.312 c 94.352 11.465 94.133 11.688 93.941 11.98 c 93.758 12.266\n" " 93.617 12.605 93.516 13.008 c 93.414 13.41 93.355 13.852 93.34 14.34 c \n" "93.34 20.82 l 90.293 20.82 l 108.059 21.039 m 107.207 21.039 106.438 20.914\n" " 105.754 20.668 c 105.078 20.414 104.496 20.031 104.016 19.52 c 103.535 \n" "19.004 103.168 18.359 102.91 17.586 c 102.656 16.805 102.527 15.895 102.527\n" " 14.855 c 102.527 13.727 102.676 12.777 102.977 12.004 c 103.281 11.23 103.691\n" " 10.609 104.199 10.137 c 104.715 9.656 105.309 9.309 105.98 9.098 c 106.652\n" " 8.887 107.355 8.781 108.102 8.781 c 109.035 8.781 109.828 8.945 110.484\n" " 9.273 c 111.148 9.594 111.691 10.047 112.113 10.629 c 112.535 11.211 112.844\n" " 11.91 113.043 12.727 c 113.238 13.535 113.34 14.43 113.34 15.414 c 113.34\n" " 15.5 l 105.754 15.508 l 105.754 16.004 105.797 16.465 105.887 16.895 c \n" "105.973 17.316 106.117 17.684 106.312 18 c 106.508 18.305 106.766 18.551\n" " 107.078 18.73 c 107.391 18.906 107.77 18.992 108.215 18.992 c 108.754 18.992\n" " 109.195 18.879 109.535 18.652 c 109.879 18.418 110.121 18.062 110.266 17.582\n" " c 113.16 17.832 l 113.027 18.168 112.844 18.523 112.602 18.902 c 112.367\n" " 19.281 112.055 19.629 111.66 19.953 c 111.266 20.266 110.773 20.527 110.184\n" " 20.738 c 109.602 20.941 108.891 21.043 108.055 21.043 c 108.055 10.719 \n" "m 107.742 10.719 107.445 10.773 107.168 10.883 c 106.898 10.984 106.66 11.152\n" " 106.457 11.387 c 106.262 11.613 106.102 11.906 105.977 12.273 c 105.852\n" " 12.637 105.785 13.074 105.77 13.586 c 110.359 13.586 l 110.301 12.633 110.07\n" " 11.918 109.672 11.445 c 109.27 10.965 108.73 10.723 108.055 10.723 c 123.332\n" " 20.828 m 123.332 14.195 l 123.332 13.738 123.297 13.316 123.223 12.938 \n" "c 123.148 12.551 123.031 12.223 122.863 11.953 c 122.695 11.676 122.477 \n" "11.461 122.207 11.309 c 121.938 11.156 121.609 11.078 121.223 11.078 c 120.852\n" " 11.078 120.512 11.16 120.207 11.328 c 119.91 11.488 119.648 11.723 119.43\n" " 12.027 c 119.211 12.324 119.039 12.688 118.918 13.109 c 118.801 13.523 \n" "118.742 13.984 118.742 14.484 c 118.742 20.82 l 115.672 20.82 l 115.672 \n" "11.641 l 115.672 11.387 115.668 11.125 115.66 10.855 c 115.66 10.586 115.652\n" " 10.332 115.637 10.09 c 115.629 9.844 115.621 9.625 115.613 9.434 c 115.605\n" " 9.238 115.594 9.09 115.582 8.996 c 118.512 8.996 l 118.527 9.082 118.539\n" " 9.227 118.555 9.422 c 118.57 9.609 118.582 9.824 118.598 10.055 c 118.613\n" " 10.289 118.625 10.52 118.629 10.754 c 118.645 10.988 118.652 11.184 118.652\n" " 11.344 c 118.695 11.344 l 119.109 10.441 119.633 9.785 120.258 9.375 c \n" "120.883 8.969 121.633 8.762 122.496 8.762 c 123.211 8.762 123.812 8.879 \n" "124.301 9.113 c 124.797 9.348 125.195 9.668 125.504 10.074 c 125.816 10.48\n" " 126.043 10.965 126.18 11.516 c 126.316 12.07 126.387 12.668 126.387 13.309\n" " c 126.387 20.816 l 123.328 20.816 l 132.375 21.012 m 131.473 21.012 130.777\n" " 20.77 130.289 20.281 c 129.801 19.785 129.559 19.039 129.559 18.043 c 129.559\n" " 11.07 l 128.062 11.07 l 128.062 8.996 l 129.711 8.996 l 130.672 6.219 l\n" " 132.594 6.219 l 132.594 8.996 l 134.832 8.996 l 134.832 11.07 l 132.594\n" " 11.07 l 132.594 17.211 l 132.594 17.785 132.703 18.211 132.922 18.488 c\n" " 133.141 18.758 133.48 18.895 133.938 18.891 c 134.125 18.891 134.297 18.875\n" " 134.449 18.848 c 134.602 18.82 134.773 18.781 134.961 18.738 c 134.961 \n" "20.641 l 134.582 20.766 134.18 20.855 133.758 20.914 c 133.336 20.98 132.871\n" " 21.012 132.371 21.012 c 139.551 21.035 m 138.984 21.035 138.473 20.957 \n" "138.02 20.805 c 137.574 20.645 137.195 20.414 136.883 20.117 c 136.57 19.812\n" " 136.328 19.438 136.16 18.992 c 135.992 18.547 135.91 18.043 135.91 17.473\n" " c 135.91 16.773 136.031 16.188 136.27 15.715 c 136.516 15.234 136.852 14.848\n" " 137.273 14.555 c 137.695 14.258 138.191 14.043 138.758 13.91 c 139.324 \n" "13.773 139.93 13.699 140.57 13.691 c 143.117 13.648 l 143.117 13.047 l 143.117\n" " 12.617 143.078 12.262 142.996 11.977 c 142.922 11.684 142.812 11.449 142.668\n" " 11.266 c 142.523 11.082 142.34 10.957 142.121 10.883 c 141.91 10.801 141.664\n" " 10.762 141.391 10.762 c 141.137 10.762 140.906 10.789 140.703 10.84 c 140.508\n" " 10.891 140.336 10.98 140.191 11.113 c 140.047 11.238 139.926 11.41 139.832\n" " 11.625 c 139.746 11.836 139.684 12.105 139.645 12.434 c 136.441 12.281 \n" "l 136.527 11.766 136.688 11.293 136.922 10.871 c 137.156 10.441 137.477 \n" "10.07 137.883 9.758 c 138.297 9.445 138.805 9.203 139.402 9.035 c 140.008\n" " 8.859 140.715 8.773 141.523 8.773 c 142.258 8.773 142.914 8.863 143.492\n" " 9.047 c 144.066 9.23 144.555 9.504 144.957 9.867 c 145.359 10.223 145.664\n" " 10.664 145.875 11.188 c 146.086 11.711 146.191 12.32 146.191 13.012 c 146.191\n" " 17.316 l 146.191 17.594 146.203 17.84 146.223 18.059 c 146.25 18.277 146.301\n" " 18.461 146.363 18.617 c 146.438 18.762 146.535 18.875 146.66 18.957 c 146.793\n" " 19.031 146.957 19.066 147.164 19.066 c 147.398 19.066 147.621 19.043 147.84\n" " 19 c 147.84 20.66 l 147.656 20.703 147.492 20.742 147.348 20.781 c 147.203\n" " 20.816 147.055 20.848 146.91 20.867 c 146.766 20.891 146.609 20.906 146.441\n" " 20.922 c 146.281 20.938 146.09 20.945 145.875 20.945 c 145.102 20.945 144.531\n" " 20.758 144.16 20.379 c 143.797 20 143.578 19.441 143.504 18.707 c 143.438\n" " 18.707 l 143.031 19.441 142.504 20.016 141.863 20.422 c 141.23 20.828 140.457\n" " 21.035 139.547 21.035 c 143.121 15.344 m 141.547 15.367 l 141.219 15.383\n" " 140.906 15.41 140.605 15.453 c 140.312 15.488 140.055 15.574 139.828 15.703\n" " c 139.609 15.828 139.434 16.008 139.305 16.25 c 139.172 16.492 139.109 \n" "16.816 139.109 17.234 c 139.109 17.797 139.238 18.215 139.492 18.492 c 139.754\n" " 18.762 140.102 18.898 140.531 18.895 c 140.926 18.895 141.281 18.812 141.602\n" " 18.645 c 141.922 18.477 142.191 18.258 142.41 17.988 c 142.637 17.711 142.812\n" " 17.395 142.934 17.039 c 143.059 16.684 143.121 16.316 143.121 15.945 c \n" "143.121 15.344 l 149.273 20.816 m 149.273 4.602 l 152.344 4.602 l 152.344\n" " 20.816 l 149.273 20.816 l f\n" "1 1 1 RG 0.531496 w\n" "0 J\n" "0 j\n" "[] 0.0 d\n" "4 M q 1 0 0 1 0 0 cm\n" "14.82 20.828 m 14.82 5.434 l 26.926 5.434 l 26.926 7.926 l 18.043 7.926\n" " l 18.043 11.793 l 26.262 11.793 l 26.262 14.285 l 18.043 14.285 l 18.043\n" " 18.34 l 27.375 18.34 l 27.375 20.832 l 14.82 20.828 l 37.219 20.828 m 34.465\n" " 16.543 l 31.688 20.828 l 28.422 20.828 l 32.75 14.719 l 28.629 9.004 l \n" "31.941 9.004 l 34.465 12.871 l 36.977 9.004 l 40.309 9.004 l 36.188 14.688\n" " l 40.547 20.828 l 37.215 20.828 l 53.473 14.863 m 53.473 15.758 53.383 \n" "16.586 53.199 17.344 c 53.023 18.102 52.75 18.754 52.379 19.301 c 52.008\n" " 19.848 51.535 20.277 50.957 20.59 c 50.391 20.895 49.715 21.047 48.938 \n" "21.047 c 48.586 21.047 48.238 21.012 47.887 20.938 c 47.543 20.863 47.215\n" " 20.75 46.902 20.586 c 46.59 20.418 46.297 20.203 46.027 19.941 c 45.766\n" " 19.672 45.539 19.34 45.352 18.945 c 45.285 18.945 l 45.293 18.98 45.301\n" " 19.074 45.309 19.219 c 45.316 19.363 45.324 19.535 45.332 19.73 c 45.34\n" " 19.918 45.344 20.125 45.344 20.344 c 45.352 20.555 45.355 20.75 45.355 \n" "20.934 c 45.355 25.469 l 42.285 25.469 l 42.285 11.723 l 42.285 11.117 42.273\n" " 10.578 42.254 10.105 c 42.238 9.633 42.223 9.266 42.199 9 c 45.184 9 l \n" "45.199 9.051 45.211 9.148 45.227 9.297 c 45.25 9.441 45.262 9.609 45.27 \n" "9.801 c 45.285 9.988 45.297 10.188 45.301 10.391 c 45.309 10.594 45.312 \n" "10.773 45.312 10.926 c 45.355 10.926 l 45.727 10.145 46.238 9.59 46.887 \n" "9.254 c 47.535 8.918 48.285 8.75 49.137 8.75 c 49.887 8.75 50.535 8.902 \n" "51.082 9.207 c 51.629 9.512 52.078 9.934 52.426 10.473 c 52.781 11.012 53.047\n" " 11.656 53.211 12.406 c 53.387 13.148 53.473 13.965 53.473 14.855 c 50.27\n" " 14.855 m 50.27 13.508 50.066 12.512 49.656 11.863 c 49.25 11.207 48.641\n" " 10.879 47.832 10.879 c 47.527 10.879 47.219 10.945 46.914 11.074 c 46.617\n" " 11.199 46.348 11.418 46.105 11.73 c 45.871 12.035 45.68 12.453 45.527 12.977\n" " c 45.383 13.492 45.309 14.148 45.309 14.945 c 45.309 15.719 45.383 16.363\n" " 45.527 16.879 c 45.672 17.391 45.863 17.797 46.094 18.102 c 46.336 18.406\n" " 46.605 18.625 46.902 18.758 c 47.199 18.883 47.504 18.945 47.809 18.945\n" " c 48.203 18.945 48.551 18.867 48.859 18.715 c 49.164 18.555 49.422 18.312\n" " 49.625 17.984 c 49.836 17.648 49.996 17.223 50.105 16.707 c 50.215 16.191\n" " 50.27 15.574 50.27 14.859 c 60.805 21.043 m 59.953 21.043 59.184 20.918\n" " 58.5 20.672 c 57.824 20.418 57.242 20.035 56.762 19.523 c 56.281 19.008\n" " 55.914 18.363 55.656 17.59 c 55.402 16.809 55.273 15.898 55.273 14.859 \n" "c 55.273 13.73 55.422 12.781 55.723 12.008 c 56.027 11.234 56.438 10.613\n" " 56.945 10.141 c 57.461 9.66 58.055 9.312 58.727 9.102 c 59.398 8.891 60.102\n" " 8.785 60.848 8.785 c 61.781 8.785 62.574 8.949 63.23 9.277 c 63.895 9.598\n" " 64.438 10.051 64.859 10.633 c 65.281 11.215 65.59 11.914 65.789 12.73 c\n" " 65.984 13.539 66.086 14.434 66.086 15.418 c 66.086 15.504 l 58.504 15.508\n" " l 58.504 16.004 58.547 16.465 58.637 16.895 c 58.723 17.316 58.867 17.684\n" " 59.062 18 c 59.258 18.305 59.516 18.551 59.828 18.73 c 60.141 18.906 60.52\n" " 18.992 60.965 18.992 c 61.504 18.992 61.945 18.879 62.285 18.652 c 62.629\n" " 18.418 62.871 18.062 63.016 17.582 c 65.91 17.832 l 65.777 18.168 65.594\n" " 18.523 65.352 18.902 c 65.117 19.281 64.805 19.629 64.41 19.953 c 64.016\n" " 20.266 63.523 20.527 62.934 20.738 c 62.352 20.941 61.641 21.043 60.805\n" " 21.043 c 60.805 10.719 m 60.492 10.719 60.195 10.773 59.918 10.883 c 59.648\n" " 10.984 59.41 11.152 59.207 11.387 c 59.012 11.613 58.852 11.906 58.727 \n" "12.273 c 58.602 12.637 58.535 13.074 58.52 13.586 c 63.109 13.586 l 63.051\n" " 12.633 62.82 11.918 62.422 11.445 c 62.02 10.965 61.48 10.723 60.805 10.723\n" " c 68.422 20.828 m 68.422 11.781 l 68.422 11.527 68.418 11.258 68.41 10.973\n" " c 68.41 10.688 68.402 10.418 68.387 10.164 c 68.379 9.902 68.371 9.668 \n" "68.363 9.465 c 68.355 9.254 68.344 9.102 68.332 9.008 c 71.262 9.008 l 71.277\n" " 9.094 71.289 9.25 71.305 9.465 c 71.32 9.676 71.332 9.91 71.348 10.164 \n" "c 71.363 10.418 71.375 10.676 71.379 10.93 c 71.395 11.176 71.402 11.379\n" " 71.402 11.531 c 71.445 11.531 l 71.598 11.102 71.75 10.719 71.902 10.383\n" " c 72.055 10.039 72.234 9.754 72.438 9.52 c 72.648 9.277 72.898 9.098 73.191\n" " 8.973 c 73.484 8.84 73.844 8.777 74.273 8.777 c 74.457 8.777 74.633 8.797\n" " 74.809 8.832 c 74.992 8.859 75.129 8.898 75.223 8.941 c 75.223 11.508 l\n" " 75.027 11.465 74.824 11.426 74.621 11.398 c 74.426 11.363 74.188 11.344\n" " 73.91 11.344 c 73.145 11.344 72.547 11.652 72.117 12.273 c 71.695 12.895\n" " 71.484 13.809 71.484 15.027 c 71.484 20.828 l 68.426 20.828 l 77.125 6.875\n" " m 77.125 4.613 l 80.195 4.613 l 80.195 6.875 l 77.125 6.875 l 77.125 20.828\n" " m 77.125 9.004 l 80.195 9.004 l 80.195 20.828 l 77.125 20.828 l 90.293 \n" "20.828 m 90.293 14.195 l 90.293 13.738 90.262 13.316 90.195 12.938 c 90.137\n" " 12.551 90.035 12.223 89.891 11.953 c 89.754 11.676 89.566 11.461 89.332\n" " 11.309 c 89.105 11.156 88.828 11.078 88.5 11.078 c 88.188 11.078 87.902\n" " 11.16 87.648 11.328 c 87.395 11.488 87.172 11.723 86.98 12.027 c 86.797\n" " 12.324 86.656 12.688 86.555 13.109 c 86.453 13.523 86.402 13.984 86.402\n" " 14.484 c 86.402 20.82 l 83.332 20.82 l 83.332 11.641 l 83.332 11.387 83.328\n" " 11.125 83.32 10.855 c 83.32 10.586 83.312 10.332 83.297 10.09 c 83.289 \n" "9.844 83.281 9.625 83.273 9.434 c 83.266 9.238 83.254 9.09 83.242 8.996 \n" "c 86.172 8.996 l 86.188 9.082 86.199 9.227 86.215 9.422 c 86.23 9.609 86.242\n" " 9.824 86.258 10.055 c 86.273 10.289 86.285 10.52 86.289 10.754 c 86.305\n" " 10.988 86.312 11.184 86.312 11.344 c 86.355 11.344 l 86.734 10.441 87.203\n" " 9.785 87.766 9.375 c 88.332 8.969 89.012 8.762 89.797 8.762 c 90.699 8.762\n" " 91.426 8.984 91.973 9.43 c 92.527 9.867 92.898 10.504 93.086 11.344 c 93.152\n" " 11.344 l 93.363 10.855 93.59 10.449 93.828 10.121 c 94.074 9.793 94.344\n" " 9.531 94.637 9.336 c 94.934 9.133 95.258 8.984 95.598 8.898 c 95.949 8.812\n" " 96.324 8.766 96.734 8.766 c 97.383 8.766 97.93 8.883 98.375 9.117 c 98.828\n" " 9.352 99.191 9.672 99.469 10.078 c 99.754 10.484 99.957 10.969 100.082 \n" "11.52 c 100.215 12.074 100.277 12.672 100.277 13.312 c 100.277 20.82 l 97.23\n" " 20.82 l 97.23 14.188 l 97.23 13.73 97.199 13.309 97.133 12.93 c 97.074 \n" "12.543 96.973 12.215 96.828 11.945 c 96.691 11.668 96.504 11.453 96.27 11.301\n" " c 96.043 11.148 95.766 11.07 95.438 11.07 c 95.133 11.07 94.852 11.152 \n" "94.598 11.312 c 94.352 11.465 94.133 11.688 93.941 11.98 c 93.758 12.266\n" " 93.617 12.605 93.516 13.008 c 93.414 13.41 93.355 13.852 93.34 14.34 c \n" "93.34 20.82 l 90.293 20.82 l 108.059 21.039 m 107.207 21.039 106.438 20.914\n" " 105.754 20.668 c 105.078 20.414 104.496 20.031 104.016 19.52 c 103.535 \n" "19.004 103.168 18.359 102.91 17.586 c 102.656 16.805 102.527 15.895 102.527\n" " 14.855 c 102.527 13.727 102.676 12.777 102.977 12.004 c 103.281 11.23 103.691\n" " 10.609 104.199 10.137 c 104.715 9.656 105.309 9.309 105.98 9.098 c 106.652\n" " 8.887 107.355 8.781 108.102 8.781 c 109.035 8.781 109.828 8.945 110.484\n" " 9.273 c 111.148 9.594 111.691 10.047 112.113 10.629 c 112.535 11.211 112.844\n" " 11.91 113.043 12.727 c 113.238 13.535 113.34 14.43 113.34 15.414 c 113.34\n" " 15.5 l 105.754 15.508 l 105.754 16.004 105.797 16.465 105.887 16.895 c \n" "105.973 17.316 106.117 17.684 106.312 18 c 106.508 18.305 106.766 18.551\n" " 107.078 18.73 c 107.391 18.906 107.77 18.992 108.215 18.992 c 108.754 18.992\n" " 109.195 18.879 109.535 18.652 c 109.879 18.418 110.121 18.062 110.266 17.582\n" " c 113.16 17.832 l 113.027 18.168 112.844 18.523 112.602 18.902 c 112.367\n" " 19.281 112.055 19.629 111.66 19.953 c 111.266 20.266 110.773 20.527 110.184\n" " 20.738 c 109.602 20.941 108.891 21.043 108.055 21.043 c 108.055 10.719 \n" "m 107.742 10.719 107.445 10.773 107.168 10.883 c 106.898 10.984 106.66 11.152\n" " 106.457 11.387 c 106.262 11.613 106.102 11.906 105.977 12.273 c 105.852\n" " 12.637 105.785 13.074 105.77 13.586 c 110.359 13.586 l 110.301 12.633 110.07\n" " 11.918 109.672 11.445 c 109.27 10.965 108.73 10.723 108.055 10.723 c 123.332\n" " 20.828 m 123.332 14.195 l 123.332 13.738 123.297 13.316 123.223 12.938 \n" "c 123.148 12.551 123.031 12.223 122.863 11.953 c 122.695 11.676 122.477 \n" "11.461 122.207 11.309 c 121.938 11.156 121.609 11.078 121.223 11.078 c 120.852\n" " 11.078 120.512 11.16 120.207 11.328 c 119.91 11.488 119.648 11.723 119.43\n" " 12.027 c 119.211 12.324 119.039 12.688 118.918 13.109 c 118.801 13.523 \n" "118.742 13.984 118.742 14.484 c 118.742 20.82 l 115.672 20.82 l 115.672 \n" "11.641 l 115.672 11.387 115.668 11.125 115.66 10.855 c 115.66 10.586 115.652\n" " 10.332 115.637 10.09 c 115.629 9.844 115.621 9.625 115.613 9.434 c 115.605\n" " 9.238 115.594 9.09 115.582 8.996 c 118.512 8.996 l 118.527 9.082 118.539\n" " 9.227 118.555 9.422 c 118.57 9.609 118.582 9.824 118.598 10.055 c 118.613\n" " 10.289 118.625 10.52 118.629 10.754 c 118.645 10.988 118.652 11.184 118.652\n" " 11.344 c 118.695 11.344 l 119.109 10.441 119.633 9.785 120.258 9.375 c \n" "120.883 8.969 121.633 8.762 122.496 8.762 c 123.211 8.762 123.812 8.879 \n" "124.301 9.113 c 124.797 9.348 125.195 9.668 125.504 10.074 c 125.816 10.48\n" " 126.043 10.965 126.18 11.516 c 126.316 12.07 126.387 12.668 126.387 13.309\n" " c 126.387 20.816 l 123.328 20.816 l 132.375 21.012 m 131.473 21.012 130.777\n" " 20.77 130.289 20.281 c 129.801 19.785 129.559 19.039 129.559 18.043 c 129.559\n" " 11.07 l 128.062 11.07 l 128.062 8.996 l 129.711 8.996 l 130.672 6.219 l\n" " 132.594 6.219 l 132.594 8.996 l 134.832 8.996 l 134.832 11.07 l 132.594\n" " 11.07 l 132.594 17.211 l 132.594 17.785 132.703 18.211 132.922 18.488 c\n" " 133.141 18.758 133.48 18.895 133.938 18.891 c 134.125 18.891 134.297 18.875\n" " 134.449 18.848 c 134.602 18.82 134.773 18.781 134.961 18.738 c 134.961 \n" "20.641 l 134.582 20.766 134.18 20.855 133.758 20.914 c 133.336 20.98 132.871\n" " 21.012 132.371 21.012 c 139.551 21.035 m 138.984 21.035 138.473 20.957 \n" "138.02 20.805 c 137.574 20.645 137.195 20.414 136.883 20.117 c 136.57 19.812\n" " 136.328 19.438 136.16 18.992 c 135.992 18.547 135.91 18.043 135.91 17.473\n" " c 135.91 16.773 136.031 16.188 136.27 15.715 c 136.516 15.234 136.852 14.848\n" " 137.273 14.555 c 137.695 14.258 138.191 14.043 138.758 13.91 c 139.324 \n" "13.773 139.93 13.699 140.57 13.691 c 143.117 13.648 l 143.117 13.047 l 143.117\n" " 12.617 143.078 12.262 142.996 11.977 c 142.922 11.684 142.812 11.449 142.668\n" " 11.266 c 142.523 11.082 142.34 10.957 142.121 10.883 c 141.91 10.801 141.664\n" " 10.762 141.391 10.762 c 141.137 10.762 140.906 10.789 140.703 10.84 c 140.508\n" " 10.891 140.336 10.98 140.191 11.113 c 140.047 11.238 139.926 11.41 139.832\n" " 11.625 c 139.746 11.836 139.684 12.105 139.645 12.434 c 136.441 12.281 \n" "l 136.527 11.766 136.688 11.293 136.922 10.871 c 137.156 10.441 137.477 \n" "10.07 137.883 9.758 c 138.297 9.445 138.805 9.203 139.402 9.035 c 140.008\n" " 8.859 140.715 8.773 141.523 8.773 c 142.258 8.773 142.914 8.863 143.492\n" " 9.047 c 144.066 9.23 144.555 9.504 144.957 9.867 c 145.359 10.223 145.664\n" " 10.664 145.875 11.188 c 146.086 11.711 146.191 12.32 146.191 13.012 c 146.191\n" " 17.316 l 146.191 17.594 146.203 17.84 146.223 18.059 c 146.25 18.277 146.301\n" " 18.461 146.363 18.617 c 146.438 18.762 146.535 18.875 146.66 18.957 c 146.793\n" " 19.031 146.957 19.066 147.164 19.066 c 147.398 19.066 147.621 19.043 147.84\n" " 19 c 147.84 20.66 l 147.656 20.703 147.492 20.742 147.348 20.781 c 147.203\n" " 20.816 147.055 20.848 146.91 20.867 c 146.766 20.891 146.609 20.906 146.441\n" " 20.922 c 146.281 20.938 146.09 20.945 145.875 20.945 c 145.102 20.945 144.531\n" " 20.758 144.16 20.379 c 143.797 20 143.578 19.441 143.504 18.707 c 143.438\n" " 18.707 l 143.031 19.441 142.504 20.016 141.863 20.422 c 141.23 20.828 140.457\n" " 21.035 139.547 21.035 c 143.121 15.344 m 141.547 15.367 l 141.219 15.383\n" " 140.906 15.41 140.605 15.453 c 140.312 15.488 140.055 15.574 139.828 15.703\n" " c 139.609 15.828 139.434 16.008 139.305 16.25 c 139.172 16.492 139.109 \n" "16.816 139.109 17.234 c 139.109 17.797 139.238 18.215 139.492 18.492 c 139.754\n" " 18.762 140.102 18.898 140.531 18.895 c 140.926 18.895 141.281 18.812 141.602\n" " 18.645 c 141.922 18.477 142.191 18.258 142.41 17.988 c 142.637 17.711 142.812\n" " 17.395 142.934 17.039 c 143.059 16.684 143.121 16.316 143.121 15.945 c \n" "143.121 15.344 l 149.273 20.816 m 149.273 4.602 l 152.344 4.602 l 152.344\n" " 20.816 l 149.273 20.816 l S Q\n" "Q q\n" "0 0.298039 0.431373 rg /a1 gs\n" "1 1 1 RG 0.797243 w\n" "1 J\n" "1 j\n" "[] 0.0 d\n" "4 M q 1 0 0 1 0 0 cm\n" "3.398 0.398 m 1.77 0.398 0.398 1.77 0.398 3.398 c 0.398 23.086 l 0.398 \n" "24.715 1.77 26.086 3.398 26.086 c 167.109 26.086 l 168.738 26.086 170.109\n" " 24.715 170.109 23.086 c 170.109 3.398 l 170.109 1.77 168.738 0.398 167.109\n" " 0.398 c h\n" "3.867 3.844 m 166.664 3.844 l 166.664 22.617 l 3.867 22.617 l h\n" "3.867 3.844 m B Q\n" "Q\n"; static Dict *getExperimentalStampExtGStateDict(PDFDoc *doc) { Dict *a0Dict = new Dict(doc->getXRef()); a0Dict->add("CA", Object(0.588235)); a0Dict->add("ca", Object(0.588235)); Dict *a1Dict = new Dict(doc->getXRef()); a1Dict->add("CA", Object(1)); a1Dict->add("ca", Object(1)); Dict *extGStateDict = new Dict(doc->getXRef()); extGStateDict->add("a0", Object(a0Dict)); extGStateDict->add("a1", Object(a1Dict)); return extGStateDict; } #endif poppler-24.02.0/poppler/annot_stamp_expired.h000066400000000000000000000567251455701731300212620ustar00rootroot00000000000000//======================================================================== // // annot_stamp_expired.h // // Copyright (C) 2021 Mahmoud Ahmed Khalil // Copyright (C) 2021 Albert Astals Cid // // Mechanically extracted from an SVG created for Okular by Eugene Trounev eugene.trounev@gmail.com // // Licensed under GPLv2 or later // //======================================================================== #ifndef ANNOT_STAMP_EXPIRED_H #define ANNOT_STAMP_EXPIRED_H #include "PDFDoc.h" #include "Dict.h" #include "Object.h" static const double ANNOT_STAMP_EXPIRED_WIDTH = 106.758179; static const double ANNOT_STAMP_EXPIRED_HEIGHT = 26.484743; static const char *ANNOT_STAMP_EXPIRED = "1 0 0 -1 0 26.484741 cm\n" "q\n" "1 1 1 rg /a0 gs\n" "3.406 2.129 m 103.367 2.129 l 104.07 2.129 104.637 2.695 104.637 3.398 \n" "c 104.637 23.09 l 104.637 23.793 104.07 24.359 103.367 24.359 c 3.406 24.355\n" " l 2.703 24.355 2.137 23.789 2.137 23.086 c 2.137 3.395 l 2.137 2.691 2.703\n" " 2.125 3.406 2.125 c h\n" "3.406 2.129 m f\n" "Q q\n" "0.74902 0 0 RG /a1 gs\n" "3.454725 w\n" "1 J\n" "1 j\n" "[] 0.0 d\n" "4 M q 1 0 0 1 0 0 cm\n" "3.406 2.129 m 103.367 2.129 l 104.07 2.129 104.637 2.695 104.637 3.398 \n" "c 104.637 23.09 l 104.637 23.793 104.07 24.359 103.367 24.359 c 3.406 24.355\n" " l 2.703 24.355 2.137 23.789 2.137 23.086 c 2.137 3.395 l 2.137 2.691 2.703\n" " 2.125 3.406 2.125 c h\n" "3.406 2.129 m S Q\n" "0.74902 0 0 rg 14.82 20.828 m 14.82 5.434 l 26.926 5.434 l 26.926 7.926 l 18.043 7.926\n" " l 18.043 11.793 l 26.262 11.793 l 26.262 14.285 l 18.043 14.285 l 18.043\n" " 18.34 l 27.375 18.34 l 27.375 20.832 l 14.82 20.828 l 37.219 20.828 m 34.465\n" " 16.543 l 31.688 20.828 l 28.422 20.828 l 32.75 14.719 l 28.629 9.004 l \n" "31.941 9.004 l 34.465 12.871 l 36.977 9.004 l 40.309 9.004 l 36.188 14.688\n" " l 40.547 20.828 l 37.215 20.828 l 53.473 14.863 m 53.473 15.758 53.383 \n" "16.586 53.199 17.344 c 53.023 18.102 52.75 18.754 52.379 19.301 c 52.008\n" " 19.848 51.535 20.277 50.957 20.59 c 50.391 20.895 49.715 21.047 48.938 \n" "21.047 c 48.586 21.047 48.238 21.012 47.887 20.938 c 47.543 20.863 47.215\n" " 20.75 46.902 20.586 c 46.59 20.418 46.297 20.203 46.027 19.941 c 45.766\n" " 19.672 45.539 19.34 45.352 18.945 c 45.285 18.945 l 45.293 18.98 45.301\n" " 19.074 45.309 19.219 c 45.316 19.363 45.324 19.535 45.332 19.73 c 45.34\n" " 19.918 45.344 20.125 45.344 20.344 c 45.352 20.555 45.355 20.75 45.355 \n" "20.934 c 45.355 25.469 l 42.285 25.469 l 42.285 11.723 l 42.285 11.117 42.273\n" " 10.578 42.254 10.105 c 42.238 9.633 42.223 9.266 42.199 9 c 45.184 9 l \n" "45.199 9.051 45.211 9.148 45.227 9.297 c 45.25 9.441 45.262 9.609 45.27 \n" "9.801 c 45.285 9.988 45.297 10.188 45.301 10.391 c 45.309 10.594 45.312 \n" "10.773 45.312 10.926 c 45.355 10.926 l 45.727 10.145 46.238 9.59 46.887 \n" "9.254 c 47.535 8.918 48.285 8.75 49.137 8.75 c 49.887 8.75 50.535 8.902 \n" "51.082 9.207 c 51.629 9.512 52.078 9.934 52.426 10.473 c 52.781 11.012 53.047\n" " 11.656 53.211 12.406 c 53.387 13.148 53.473 13.965 53.473 14.855 c 50.27\n" " 14.855 m 50.27 13.508 50.066 12.512 49.656 11.863 c 49.25 11.207 48.641\n" " 10.879 47.832 10.879 c 47.527 10.879 47.219 10.945 46.914 11.074 c 46.617\n" " 11.199 46.348 11.418 46.105 11.73 c 45.871 12.035 45.68 12.453 45.527 12.977\n" " c 45.383 13.492 45.309 14.148 45.309 14.945 c 45.309 15.719 45.383 16.363\n" " 45.527 16.879 c 45.672 17.391 45.863 17.797 46.094 18.102 c 46.336 18.406\n" " 46.605 18.625 46.902 18.758 c 47.199 18.883 47.504 18.945 47.809 18.945\n" " c 48.203 18.945 48.551 18.867 48.859 18.715 c 49.164 18.555 49.422 18.312\n" " 49.625 17.984 c 49.836 17.648 49.996 17.223 50.105 16.707 c 50.215 16.191\n" " 50.27 15.574 50.27 14.859 c 55.969 6.875 m 55.969 4.613 l 59.039 4.613 \n" "l 59.039 6.875 l 55.969 6.875 l 55.969 20.828 m 55.969 9.004 l 59.039 9.004\n" " l 59.039 20.828 l 55.969 20.828 l 62.176 20.828 m 62.176 11.781 l 62.176\n" " 11.527 62.172 11.258 62.164 10.973 c 62.164 10.688 62.156 10.418 62.141\n" " 10.164 c 62.133 9.902 62.125 9.668 62.117 9.465 c 62.109 9.254 62.098 9.102\n" " 62.086 9.008 c 65.016 9.008 l 65.031 9.094 65.043 9.25 65.059 9.465 c 65.074\n" " 9.676 65.086 9.91 65.102 10.164 c 65.117 10.418 65.129 10.676 65.133 10.93\n" " c 65.148 11.176 65.156 11.379 65.156 11.531 c 65.199 11.531 l 65.352 11.102\n" " 65.504 10.719 65.656 10.383 c 65.809 10.039 65.988 9.754 66.191 9.52 c \n" "66.402 9.277 66.652 9.098 66.945 8.973 c 67.238 8.84 67.598 8.777 68.027\n" " 8.777 c 68.211 8.777 68.387 8.797 68.562 8.832 c 68.746 8.859 68.883 8.898\n" " 68.977 8.941 c 68.977 11.508 l 68.781 11.465 68.578 11.426 68.375 11.398\n" " c 68.18 11.363 67.941 11.344 67.664 11.344 c 66.898 11.344 66.301 11.652\n" " 65.871 12.273 c 65.449 12.895 65.238 13.809 65.238 15.027 c 65.238 20.828\n" " l 62.168 20.828 l 75.707 21.047 m 74.855 21.047 74.086 20.922 73.402 20.676\n" " c 72.727 20.422 72.145 20.039 71.664 19.527 c 71.184 19.012 70.816 18.367\n" " 70.559 17.594 c 70.305 16.812 70.176 15.902 70.176 14.863 c 70.176 13.734\n" " 70.324 12.785 70.625 12.012 c 70.93 11.238 71.34 10.617 71.848 10.145 c\n" " 72.363 9.664 72.957 9.316 73.629 9.105 c 74.301 8.895 75.004 8.789 75.75\n" " 8.789 c 76.684 8.789 77.477 8.953 78.133 9.281 c 78.797 9.602 79.34 10.055\n" " 79.762 10.637 c 80.184 11.219 80.492 11.918 80.691 12.734 c 80.887 13.543\n" " 80.988 14.438 80.988 15.422 c 80.988 15.508 l 73.41 15.508 l 73.41 16.004\n" " 73.453 16.465 73.543 16.895 c 73.629 17.316 73.773 17.684 73.969 18 c 74.164\n" " 18.305 74.422 18.551 74.734 18.73 c 75.047 18.906 75.426 18.992 75.871 \n" "18.992 c 76.41 18.992 76.852 18.879 77.191 18.652 c 77.535 18.418 77.777\n" " 18.062 77.922 17.582 c 80.816 17.832 l 80.684 18.168 80.5 18.523 80.258\n" " 18.902 c 80.023 19.281 79.711 19.629 79.316 19.953 c 78.922 20.266 78.43\n" " 20.527 77.84 20.738 c 77.258 20.941 76.547 21.043 75.711 21.043 c 75.711\n" " 10.719 m 75.398 10.719 75.102 10.773 74.824 10.883 c 74.555 10.984 74.316\n" " 11.152 74.113 11.387 c 73.918 11.613 73.758 11.906 73.633 12.273 c 73.508\n" " 12.637 73.441 13.074 73.426 13.586 c 78.016 13.586 l 77.957 12.633 77.727\n" " 11.918 77.328 11.445 c 76.926 10.965 76.387 10.723 75.711 10.723 c 90.988\n" " 20.828 m 90.973 20.77 90.957 20.668 90.934 20.523 c 90.918 20.371 90.902\n" " 20.199 90.879 20.012 c 90.863 19.824 90.852 19.629 90.836 19.434 c 90.828\n" " 19.238 90.824 19.062 90.824 18.91 c 90.781 18.91 l 90.426 19.676 89.926\n" " 20.227 89.285 20.559 c 88.652 20.887 87.891 21.051 87 21.051 c 86.258 21.051\n" " 85.613 20.898 85.066 20.594 c 84.527 20.289 84.078 19.863 83.723 19.316\n" " c 83.371 18.77 83.109 18.125 82.938 17.383 c 82.77 16.633 82.688 15.816\n" " 82.688 14.934 c 82.688 14.039 82.773 13.215 82.949 12.465 c 83.133 11.715\n" " 83.406 11.07 83.781 10.531 c 84.152 9.984 84.621 9.559 85.191 9.254 c 85.766\n" " 8.949 86.449 8.797 87.234 8.797 c 87.621 8.797 87.992 8.836 88.348 8.918\n" " c 88.703 9 89.035 9.121 89.344 9.289 c 89.648 9.457 89.926 9.672 90.176\n" " 9.934 c 90.422 10.195 90.633 10.512 90.809 10.883 c 90.832 10.883 l 90.832\n" " 10.809 90.828 10.703 90.82 10.566 c 90.82 10.422 90.82 10.258 90.82 10.074\n" " c 90.82 9.891 90.816 9.703 90.809 9.508 c 90.809 9.312 90.809 9.121 90.809\n" " 8.941 c 90.809 4.625 l 93.879 4.625 l 93.879 18.262 l 93.879 18.836 93.891\n" " 19.352 93.91 19.801 c 93.934 20.246 93.949 20.59 93.965 20.84 c 90.992 \n" "20.84 l 90.852 14.863 m 90.852 14.082 90.777 13.438 90.633 12.93 c 90.488\n" " 12.414 90.293 12.004 90.055 11.707 c 89.82 11.402 89.551 11.191 89.246 \n" "11.074 c 88.949 10.949 88.641 10.887 88.328 10.887 c 87.934 10.887 87.586\n" " 10.965 87.277 11.117 c 86.98 11.27 86.723 11.512 86.512 11.84 c 86.309 \n" "12.168 86.152 12.586 86.043 13.098 c 85.941 13.609 85.891 14.223 85.891 \n" "14.945 c 85.891 17.625 86.695 18.965 88.305 18.965 c 88.609 18.965 88.918\n" " 18.898 89.223 18.77 c 89.527 18.637 89.801 18.418 90.043 18.102 c 90.285\n" " 17.789 90.477 17.371 90.621 16.844 c 90.773 16.312 90.852 15.652 90.852\n" " 14.867 c f\n" "1 1 1 RG 0.531496 w\n" "0 J\n" "0 j\n" "q 1 0 0 1 0 0 cm\n" "14.82 20.828 m 14.82 5.434 l 26.926 5.434 l 26.926 7.926 l 18.043 7.926\n" " l 18.043 11.793 l 26.262 11.793 l 26.262 14.285 l 18.043 14.285 l 18.043\n" " 18.34 l 27.375 18.34 l 27.375 20.832 l 14.82 20.828 l 37.219 20.828 m 34.465\n" " 16.543 l 31.688 20.828 l 28.422 20.828 l 32.75 14.719 l 28.629 9.004 l \n" "31.941 9.004 l 34.465 12.871 l 36.977 9.004 l 40.309 9.004 l 36.188 14.688\n" " l 40.547 20.828 l 37.215 20.828 l 53.473 14.863 m 53.473 15.758 53.383 \n" "16.586 53.199 17.344 c 53.023 18.102 52.75 18.754 52.379 19.301 c 52.008\n" " 19.848 51.535 20.277 50.957 20.59 c 50.391 20.895 49.715 21.047 48.938 \n" "21.047 c 48.586 21.047 48.238 21.012 47.887 20.938 c 47.543 20.863 47.215\n" " 20.75 46.902 20.586 c 46.59 20.418 46.297 20.203 46.027 19.941 c 45.766\n" " 19.672 45.539 19.34 45.352 18.945 c 45.285 18.945 l 45.293 18.98 45.301\n" " 19.074 45.309 19.219 c 45.316 19.363 45.324 19.535 45.332 19.73 c 45.34\n" " 19.918 45.344 20.125 45.344 20.344 c 45.352 20.555 45.355 20.75 45.355 \n" "20.934 c 45.355 25.469 l 42.285 25.469 l 42.285 11.723 l 42.285 11.117 42.273\n" " 10.578 42.254 10.105 c 42.238 9.633 42.223 9.266 42.199 9 c 45.184 9 l \n" "45.199 9.051 45.211 9.148 45.227 9.297 c 45.25 9.441 45.262 9.609 45.27 \n" "9.801 c 45.285 9.988 45.297 10.188 45.301 10.391 c 45.309 10.594 45.312 \n" "10.773 45.312 10.926 c 45.355 10.926 l 45.727 10.145 46.238 9.59 46.887 \n" "9.254 c 47.535 8.918 48.285 8.75 49.137 8.75 c 49.887 8.75 50.535 8.902 \n" "51.082 9.207 c 51.629 9.512 52.078 9.934 52.426 10.473 c 52.781 11.012 53.047\n" " 11.656 53.211 12.406 c 53.387 13.148 53.473 13.965 53.473 14.855 c 50.27\n" " 14.855 m 50.27 13.508 50.066 12.512 49.656 11.863 c 49.25 11.207 48.641\n" " 10.879 47.832 10.879 c 47.527 10.879 47.219 10.945 46.914 11.074 c 46.617\n" " 11.199 46.348 11.418 46.105 11.73 c 45.871 12.035 45.68 12.453 45.527 12.977\n" " c 45.383 13.492 45.309 14.148 45.309 14.945 c 45.309 15.719 45.383 16.363\n" " 45.527 16.879 c 45.672 17.391 45.863 17.797 46.094 18.102 c 46.336 18.406\n" " 46.605 18.625 46.902 18.758 c 47.199 18.883 47.504 18.945 47.809 18.945\n" " c 48.203 18.945 48.551 18.867 48.859 18.715 c 49.164 18.555 49.422 18.312\n" " 49.625 17.984 c 49.836 17.648 49.996 17.223 50.105 16.707 c 50.215 16.191\n" " 50.27 15.574 50.27 14.859 c 55.969 6.875 m 55.969 4.613 l 59.039 4.613 \n" "l 59.039 6.875 l 55.969 6.875 l 55.969 20.828 m 55.969 9.004 l 59.039 9.004\n" " l 59.039 20.828 l 55.969 20.828 l 62.176 20.828 m 62.176 11.781 l 62.176\n" " 11.527 62.172 11.258 62.164 10.973 c 62.164 10.688 62.156 10.418 62.141\n" " 10.164 c 62.133 9.902 62.125 9.668 62.117 9.465 c 62.109 9.254 62.098 9.102\n" " 62.086 9.008 c 65.016 9.008 l 65.031 9.094 65.043 9.25 65.059 9.465 c 65.074\n" " 9.676 65.086 9.91 65.102 10.164 c 65.117 10.418 65.129 10.676 65.133 10.93\n" " c 65.148 11.176 65.156 11.379 65.156 11.531 c 65.199 11.531 l 65.352 11.102\n" " 65.504 10.719 65.656 10.383 c 65.809 10.039 65.988 9.754 66.191 9.52 c \n" "66.402 9.277 66.652 9.098 66.945 8.973 c 67.238 8.84 67.598 8.777 68.027\n" " 8.777 c 68.211 8.777 68.387 8.797 68.562 8.832 c 68.746 8.859 68.883 8.898\n" " 68.977 8.941 c 68.977 11.508 l 68.781 11.465 68.578 11.426 68.375 11.398\n" " c 68.18 11.363 67.941 11.344 67.664 11.344 c 66.898 11.344 66.301 11.652\n" " 65.871 12.273 c 65.449 12.895 65.238 13.809 65.238 15.027 c 65.238 20.828\n" " l 62.168 20.828 l 75.707 21.047 m 74.855 21.047 74.086 20.922 73.402 20.676\n" " c 72.727 20.422 72.145 20.039 71.664 19.527 c 71.184 19.012 70.816 18.367\n" " 70.559 17.594 c 70.305 16.812 70.176 15.902 70.176 14.863 c 70.176 13.734\n" " 70.324 12.785 70.625 12.012 c 70.93 11.238 71.34 10.617 71.848 10.145 c\n" " 72.363 9.664 72.957 9.316 73.629 9.105 c 74.301 8.895 75.004 8.789 75.75\n" " 8.789 c 76.684 8.789 77.477 8.953 78.133 9.281 c 78.797 9.602 79.34 10.055\n" " 79.762 10.637 c 80.184 11.219 80.492 11.918 80.691 12.734 c 80.887 13.543\n" " 80.988 14.438 80.988 15.422 c 80.988 15.508 l 73.41 15.508 l 73.41 16.004\n" " 73.453 16.465 73.543 16.895 c 73.629 17.316 73.773 17.684 73.969 18 c 74.164\n" " 18.305 74.422 18.551 74.734 18.73 c 75.047 18.906 75.426 18.992 75.871 \n" "18.992 c 76.41 18.992 76.852 18.879 77.191 18.652 c 77.535 18.418 77.777\n" " 18.062 77.922 17.582 c 80.816 17.832 l 80.684 18.168 80.5 18.523 80.258\n" " 18.902 c 80.023 19.281 79.711 19.629 79.316 19.953 c 78.922 20.266 78.43\n" " 20.527 77.84 20.738 c 77.258 20.941 76.547 21.043 75.711 21.043 c 75.711\n" " 10.719 m 75.398 10.719 75.102 10.773 74.824 10.883 c 74.555 10.984 74.316\n" " 11.152 74.113 11.387 c 73.918 11.613 73.758 11.906 73.633 12.273 c 73.508\n" " 12.637 73.441 13.074 73.426 13.586 c 78.016 13.586 l 77.957 12.633 77.727\n" " 11.918 77.328 11.445 c 76.926 10.965 76.387 10.723 75.711 10.723 c 90.988\n" " 20.828 m 90.973 20.77 90.957 20.668 90.934 20.523 c 90.918 20.371 90.902\n" " 20.199 90.879 20.012 c 90.863 19.824 90.852 19.629 90.836 19.434 c 90.828\n" " 19.238 90.824 19.062 90.824 18.91 c 90.781 18.91 l 90.426 19.676 89.926\n" " 20.227 89.285 20.559 c 88.652 20.887 87.891 21.051 87 21.051 c 86.258 21.051\n" " 85.613 20.898 85.066 20.594 c 84.527 20.289 84.078 19.863 83.723 19.316\n" " c 83.371 18.77 83.109 18.125 82.938 17.383 c 82.77 16.633 82.688 15.816\n" " 82.688 14.934 c 82.688 14.039 82.773 13.215 82.949 12.465 c 83.133 11.715\n" " 83.406 11.07 83.781 10.531 c 84.152 9.984 84.621 9.559 85.191 9.254 c 85.766\n" " 8.949 86.449 8.797 87.234 8.797 c 87.621 8.797 87.992 8.836 88.348 8.918\n" " c 88.703 9 89.035 9.121 89.344 9.289 c 89.648 9.457 89.926 9.672 90.176\n" " 9.934 c 90.422 10.195 90.633 10.512 90.809 10.883 c 90.832 10.883 l 90.832\n" " 10.809 90.828 10.703 90.82 10.566 c 90.82 10.422 90.82 10.258 90.82 10.074\n" " c 90.82 9.891 90.816 9.703 90.809 9.508 c 90.809 9.312 90.809 9.121 90.809\n" " 8.941 c 90.809 4.625 l 93.879 4.625 l 93.879 18.262 l 93.879 18.836 93.891\n" " 19.352 93.91 19.801 c 93.934 20.246 93.949 20.59 93.965 20.84 c 90.992 \n" "20.84 l 90.852 14.863 m 90.852 14.082 90.777 13.438 90.633 12.93 c 90.488\n" " 12.414 90.293 12.004 90.055 11.707 c 89.82 11.402 89.551 11.191 89.246 \n" "11.074 c 88.949 10.949 88.641 10.887 88.328 10.887 c 87.934 10.887 87.586\n" " 10.965 87.277 11.117 c 86.98 11.27 86.723 11.512 86.512 11.84 c 86.309 \n" "12.168 86.152 12.586 86.043 13.098 c 85.941 13.609 85.891 14.223 85.891 \n" "14.945 c 85.891 17.625 86.695 18.965 88.305 18.965 c 88.609 18.965 88.918\n" " 18.898 89.223 18.77 c 89.527 18.637 89.801 18.418 90.043 18.102 c 90.285\n" " 17.789 90.477 17.371 90.621 16.844 c 90.773 16.312 90.852 15.652 90.852\n" " 14.867 c S Q\n" "0.74902 0 0 rg 1 1 1 RG 0.797243 w\n" "1 J\n" "1 j\n" "q 1 0 0 1 0 0 cm\n" "3.398 0.398 m 1.77 0.398 0.398 1.77 0.398 3.398 c 0.398 23.086 l 0.398 \n" "24.715 1.77 26.086 3.398 26.086 c 103.359 26.086 l 104.988 26.086 106.359\n" " 24.715 106.359 23.086 c 106.359 3.398 l 106.359 1.77 104.988 0.398 103.359\n" " 0.398 c h\n" "3.867 3.844 m 102.914 3.844 l 102.914 22.617 l 3.867 22.617 l h\n" "3.867 3.844 m B Q\n" "Q\n"; static Dict *getExpiredStampExtGStateDict(PDFDoc *doc) { Dict *a0Dict = new Dict(doc->getXRef()); a0Dict->add("CA", Object(0.588235)); a0Dict->add("ca", Object(0.588235)); Dict *a1Dict = new Dict(doc->getXRef()); a1Dict->add("CA", Object(1)); a1Dict->add("ca", Object(1)); Dict *extGStateDict = new Dict(doc->getXRef()); extGStateDict->add("a0", Object(a0Dict)); extGStateDict->add("a1", Object(a1Dict)); return extGStateDict; } #endif poppler-24.02.0/poppler/annot_stamp_final.h000066400000000000000000000214531455701731300207010ustar00rootroot00000000000000//======================================================================== // // annot_stamp_final.h // // Copyright (C) 2021 Mahmoud Ahmed Khalil // Copyright (C) 2021 Albert Astals Cid // // Mechanically extracted from an SVG created for Okular by Eugene Trounev eugene.trounev@gmail.com // // Licensed under GPLv2 or later // //======================================================================== #ifndef ANNOT_STAMP_FINAL_H #define ANNOT_STAMP_FINAL_H #include "Dict.h" #include "Object.h" #include "PDFDoc.h" static const double ANNOT_STAMP_FINAL_WIDTH = 79.758179; static const double ANNOT_STAMP_FINAL_HEIGHT = 26.484743; static const char *ANNOT_STAMP_FINAL = "1 0 0 -1 0 26.484741 cm\n" "q\n" "1 1 1 rg /a0 gs\n" "3.406 2.129 m 76.367 2.129 l 77.066 2.129 77.637 2.828 77.637 3.398 c 77.637\n" " 23.09 l 77.637 23.789 77.07 24.359 76.367 24.359 c 3.406 24.359 l 2.707\n" " 24.359 2.137 23.66 2.137 23.09 c 2.137 3.398 l 2.137 2.699 2.703 2.129 \n" "3.406 2.129 c h\n" "3.406 2.129 m f\n" "0 0.298039 0.431373 rg /a1 gs\n" "1 1 1 RG 0.531496 w\n" "0 J\n" "0 j\n" "[] 0.0 d\n" "4 M q 1 0 0 1 0 0 cm\n" "18.043 7.926 m 18.043 12.691 l 25.922 12.691 l 25.922 15.184 l 18.043 15.18\n" " l 18.043 20.828 l 14.82 20.828 l 14.82 5.434 l 26.172 5.434 l 26.172 7.926\n" " l 18.043 7.926 l 28.566 6.875 m 28.566 4.613 l 31.637 4.613 l 31.637 6.875\n" " l 28.566 6.875 l 28.566 20.828 m 28.566 9.004 l 31.637 9.004 l 31.637 20.828\n" " l 28.566 20.828 l 42.434 20.828 m 42.434 14.195 l 42.434 13.738 42.398 \n" "13.316 42.324 12.938 c 42.25 12.551 42.133 12.223 41.965 11.953 c 41.797\n" " 11.676 41.578 11.461 41.309 11.309 c 41.039 11.156 40.711 11.078 40.324\n" " 11.078 c 39.953 11.078 39.613 11.16 39.309 11.328 c 39.012 11.488 38.75\n" " 11.723 38.531 12.027 c 38.312 12.324 38.141 12.688 38.02 13.109 c 37.902\n" " 13.523 37.844 13.984 37.844 14.484 c 37.844 20.82 l 34.773 20.82 l 34.773\n" " 11.641 l 34.773 11.387 34.77 11.125 34.762 10.855 c 34.762 10.586 34.754\n" " 10.332 34.738 10.09 c 34.73 9.844 34.723 9.625 34.715 9.434 c 34.707 9.238\n" " 34.695 9.09 34.684 8.996 c 37.613 8.996 l 37.629 9.082 37.641 9.227 37.656\n" " 9.422 c 37.672 9.609 37.684 9.824 37.699 10.055 c 37.715 10.289 37.727 \n" "10.52 37.73 10.754 c 37.746 10.988 37.754 11.184 37.754 11.344 c 37.797 \n" "11.344 l 38.211 10.441 38.734 9.785 39.359 9.375 c 39.984 8.969 40.734 8.762\n" " 41.598 8.762 c 42.312 8.762 42.914 8.879 43.402 9.113 c 43.898 9.348 44.297\n" " 9.668 44.605 10.074 c 44.918 10.48 45.145 10.965 45.281 11.516 c 45.418\n" " 12.07 45.488 12.668 45.488 13.309 c 45.488 20.816 l 42.434 20.828 l 51.188\n" " 21.047 m 50.621 21.047 50.109 20.969 49.656 20.816 c 49.211 20.656 48.832\n" " 20.426 48.52 20.129 c 48.207 19.824 47.965 19.449 47.797 19.004 c 47.629\n" " 18.559 47.547 18.055 47.547 17.484 c 47.547 16.785 47.668 16.199 47.906\n" " 15.727 c 48.152 15.246 48.488 14.859 48.91 14.566 c 49.332 14.27 49.828\n" " 14.055 50.395 13.922 c 50.961 13.785 51.566 13.711 52.207 13.703 c 54.754\n" " 13.66 l 54.754 13.059 l 54.754 12.629 54.715 12.273 54.633 11.988 c 54.559\n" " 11.695 54.449 11.461 54.305 11.277 c 54.16 11.094 53.977 10.969 53.758 \n" "10.895 c 53.547 10.812 53.301 10.773 53.027 10.773 c 52.773 10.773 52.543\n" " 10.801 52.34 10.852 c 52.145 10.902 51.973 10.992 51.828 11.125 c 51.684\n" " 11.25 51.562 11.422 51.469 11.637 c 51.383 11.848 51.32 12.117 51.281 12.445\n" " c 48.078 12.293 l 48.164 11.777 48.324 11.305 48.559 10.883 c 48.793 10.453\n" " 49.113 10.082 49.52 9.77 c 49.934 9.457 50.441 9.215 51.039 9.047 c 51.645\n" " 8.871 52.352 8.785 53.16 8.785 c 53.895 8.785 54.551 8.875 55.129 9.059\n" " c 55.703 9.242 56.191 9.516 56.594 9.879 c 56.996 10.234 57.301 10.676 \n" "57.512 11.199 c 57.723 11.723 57.828 12.332 57.828 13.023 c 57.828 17.328\n" " l 57.828 17.605 57.84 17.852 57.859 18.07 c 57.887 18.289 57.938 18.477\n" " 58 18.629 c 58.074 18.773 58.172 18.887 58.297 18.969 c 58.43 19.043 58.594\n" " 19.078 58.801 19.078 c 59.035 19.078 59.258 19.055 59.477 19.012 c 59.477\n" " 20.672 l 59.293 20.715 59.129 20.754 58.984 20.793 c 58.84 20.828 58.691\n" " 20.859 58.547 20.879 c 58.402 20.902 58.246 20.918 58.078 20.934 c 57.918\n" " 20.949 57.727 20.957 57.512 20.957 c 56.738 20.957 56.168 20.77 55.797 \n" "20.391 c 55.434 20.012 55.215 19.453 55.141 18.719 c 55.074 18.719 l 54.668\n" " 19.453 54.141 20.027 53.5 20.434 c 52.867 20.84 52.094 21.047 51.184 21.047\n" " c 54.758 15.355 m 53.184 15.379 l 52.855 15.395 52.543 15.422 52.242 15.465\n" " c 51.949 15.5 51.691 15.586 51.465 15.715 c 51.246 15.84 51.07 16.02 50.941\n" " 16.262 c 50.809 16.504 50.746 16.828 50.746 17.246 c 50.746 17.809 50.875\n" " 18.227 51.129 18.504 c 51.391 18.773 51.738 18.906 52.168 18.906 c 52.562\n" " 18.906 52.918 18.824 53.238 18.656 c 53.559 18.488 53.828 18.27 54.047 \n" "18 c 54.273 17.723 54.449 17.406 54.57 17.051 c 54.695 16.695 54.758 16.328\n" " 54.758 15.957 c 54.758 15.355 l 60.91 20.828 m 60.91 4.613 l 63.98 4.613\n" " l 63.98 20.828 l 60.91 20.828 l B Q\n" "Q q\n" "0 0.298039 0.431373 rg /a1 gs\n" "1 1 1 RG 0.797243 w\n" "1 J\n" "1 j\n" "[] 0.0 d\n" "4 M q 1 0 0 1 0 0 cm\n" "3.398 0.398 m 1.77 0.398 0.398 1.77 0.398 3.398 c 0.398 23.086 l 0.398 \n" "24.715 1.77 26.086 3.398 26.086 c 76.359 26.086 l 77.988 26.086 79.359 24.715\n" " 79.359 23.086 c 79.359 3.398 l 79.359 1.77 77.988 0.398 76.359 0.398 c \n" "h\n" "3.867 3.844 m 75.914 3.844 l 75.914 22.617 l 3.867 22.617 l h\n" "3.867 3.844 m B Q\n" "Q\n"; static Dict *getFinalStampExtGStateDict(PDFDoc *doc) { Dict *a0Dict = new Dict(doc->getXRef()); a0Dict->add("CA", Object(0.588235)); a0Dict->add("ca", Object(0.588235)); Dict *a1Dict = new Dict(doc->getXRef()); a1Dict->add("CA", Object(1)); a1Dict->add("ca", Object(1)); Dict *extGStateDict = new Dict(doc->getXRef()); extGStateDict->add("a0", Object(a0Dict)); extGStateDict->add("a1", Object(a1Dict)); return extGStateDict; } #endif poppler-24.02.0/poppler/annot_stamp_for_comment.h000066400000000000000000000544601455701731300221240ustar00rootroot00000000000000//======================================================================== // // annot_stamp_for_comment.h // // Copyright (C) 2021 Mahmoud Ahmed Khalil // Copyright (C) 2021 Albert Astals Cid // // Mechanically extracted from an SVG created for Okular by Eugene Trounev eugene.trounev@gmail.com // // Licensed under GPLv2 or later // //======================================================================== #ifndef ANNOT_STAMP_FOR_COMMENT_H #define ANNOT_STAMP_FOR_COMMENT_H #include "Dict.h" #include "Object.h" #include "PDFDoc.h" static const double ANNOT_STAMP_FOR_COMMENT_WIDTH = 170.508179; static const double ANNOT_STAMP_FOR_COMMENT_HEIGHT = 26.484743; static const char *ANNOT_STAMP_FOR_COMMENT = "1 0 0 -1 0 26.484741 cm\n" "q\n" "1 1 1 rg /a0 gs\n" "3.406 2.129 m 167.117 2.129 l 167.816 2.129 168.387 2.828 168.387 3.398\n" " c 168.387 23.09 l 168.387 23.789 167.82 24.359 167.117 24.359 c 3.406 24.359\n" " l 2.707 24.359 2.137 23.66 2.137 23.09 c 2.137 3.398 l 2.137 2.699 2.703\n" " 2.129 3.406 2.129 c h\n" "3.406 2.129 m f\n" "0 0.298039 0.431373 rg /a1 gs\n" "1 1 1 RG 0.265748 w\n" "0 J\n" "0 j\n" "[] 0.0 d\n" "4 M q 1 0 0 1 0 0 cm\n" "18.043 7.926 m 18.043 12.691 l 25.922 12.691 l 25.922 15.184 l 18.043 15.18\n" " l 18.043 20.828 l 14.82 20.828 l 14.82 5.434 l 26.172 5.434 l 26.172 7.926\n" " l 18.043 7.926 l 39.797 14.906 m 39.797 15.809 39.668 16.637 39.414 17.387\n" " c 39.168 18.137 38.793 18.785 38.289 19.332 c 37.785 19.871 37.16 20.293\n" " 36.41 20.598 c 35.66 20.895 34.785 21.047 33.789 21.047 c 32.828 21.047\n" " 31.98 20.898 31.242 20.598 c 30.508 20.301 29.887 19.879 29.383 19.34 c\n" " 28.887 18.801 28.512 18.156 28.258 17.406 c 28.004 16.648 27.875 15.816\n" " 27.875 14.902 c 27.875 14.02 27.996 13.207 28.234 12.465 c 28.48 11.715\n" " 28.855 11.066 29.348 10.52 c 29.844 9.973 30.465 9.547 31.215 9.242 c 31.965\n" " 8.938 32.844 8.785 33.848 8.785 c 34.91 8.785 35.816 8.938 36.57 9.242 \n" "c 37.32 9.547 37.934 9.973 38.406 10.52 c 38.887 11.059 39.238 11.703 39.457\n" " 12.453 c 39.684 13.195 39.797 14.012 39.797 14.902 c 36.586 14.902 m 36.586\n" " 13.496 36.355 12.477 35.898 11.844 c 35.441 11.211 34.773 10.895 33.898\n" " 10.895 c 32.996 10.895 32.305 11.215 31.824 11.855 c 31.344 12.496 31.102\n" " 13.512 31.102 14.902 c 31.102 15.609 31.164 16.219 31.289 16.727 c 31.422\n" " 17.238 31.602 17.656 31.836 17.984 c 32.07 18.312 32.348 18.555 32.676 \n" "18.715 c 33.004 18.867 33.363 18.945 33.758 18.945 c 34.211 18.945 34.609\n" " 18.867 34.961 18.715 c 35.316 18.555 35.617 18.312 35.855 17.984 c 36.098\n" " 17.656 36.277 17.238 36.402 16.727 c 36.527 16.215 36.59 15.609 36.59 14.902\n" " c 42.25 20.824 m 42.25 11.777 l 42.25 11.523 42.246 11.254 42.238 10.969\n" " c 42.238 10.684 42.23 10.414 42.215 10.16 c 42.207 9.898 42.199 9.664 42.191\n" " 9.461 c 42.184 9.25 42.172 9.098 42.16 9.004 c 45.09 9.004 l 45.105 9.09\n" " 45.117 9.246 45.133 9.461 c 45.148 9.672 45.16 9.906 45.176 10.16 c 45.191\n" " 10.414 45.203 10.672 45.207 10.926 c 45.223 11.172 45.23 11.375 45.23 11.527\n" " c 45.273 11.527 l 45.426 11.098 45.578 10.715 45.73 10.379 c 45.883 10.035\n" " 46.062 9.75 46.266 9.516 c 46.477 9.273 46.727 9.094 47.02 8.969 c 47.312\n" " 8.836 47.672 8.773 48.102 8.773 c 48.285 8.773 48.461 8.793 48.637 8.828\n" " c 48.82 8.855 48.957 8.895 49.051 8.938 c 49.051 11.504 l 48.855 11.461\n" " 48.652 11.422 48.449 11.395 c 48.254 11.359 48.016 11.34 47.738 11.34 c\n" " 46.973 11.34 46.375 11.648 45.945 12.27 c 45.523 12.891 45.312 13.805 45.312\n" " 15.023 c 45.312 20.824 l 42.242 20.824 l 64.27 18.508 m 64.875 18.508 65.395\n" " 18.426 65.832 18.258 c 66.277 18.082 66.66 17.855 66.98 17.582 c 67.309\n" " 17.305 67.578 16.992 67.789 16.641 c 68.008 16.289 68.188 15.938 68.324\n" " 15.582 c 71.133 16.641 l 70.906 17.195 70.613 17.738 70.246 18.27 c 69.883\n" " 18.801 69.426 19.27 68.879 19.68 c 68.332 20.086 67.68 20.418 66.922 20.676\n" " c 66.172 20.922 65.285 21.047 64.266 21.047 c 62.934 21.047 61.777 20.852\n" " 60.801 20.457 c 59.824 20.055 59.016 19.504 58.375 18.797 c 57.742 18.082\n" " 57.27 17.238 56.953 16.262 c 56.648 15.285 56.496 14.219 56.496 13.059 \n" "c 56.496 11.855 56.648 10.77 56.953 9.801 c 57.266 8.832 57.734 8.008 58.363\n" " 7.332 c 58.988 6.648 59.781 6.121 60.734 5.758 c 61.695 5.387 62.824 5.199\n" " 64.121 5.199 c 65.125 5.199 66.004 5.305 66.754 5.516 c 67.512 5.727 68.164\n" " 6.027 68.711 6.41 c 69.266 6.789 69.719 7.242 70.078 7.766 c 70.441 8.281\n" " 70.727 8.855 70.93 9.48 c 68.09 10.258 l 67.988 9.93 67.832 9.617 67.621\n" " 9.316 c 67.41 9.012 67.141 8.742 66.812 8.508 c 66.492 8.273 66.113 8.09\n" " 65.676 7.949 c 65.238 7.805 64.742 7.73 64.191 7.73 c 63.41 7.73 62.742\n" " 7.859 62.18 8.113 c 61.617 8.359 61.156 8.719 60.793 9.184 c 60.438 9.648\n" " 60.172 10.211 59.996 10.867 c 59.828 11.516 59.746 12.242 59.746 13.051\n" " c 59.746 13.852 59.828 14.586 59.996 15.258 c 60.172 15.922 60.441 16.492\n" " 60.805 16.973 c 61.168 17.453 61.637 17.828 62.203 18.098 c 62.777 18.367\n" " 63.469 18.504 64.27 18.5 c 84.551 14.895 m 84.551 15.797 84.422 16.625 \n" "84.168 17.375 c 83.922 18.125 83.547 18.773 83.043 19.32 c 82.539 19.859\n" " 81.914 20.281 81.164 20.586 c 80.414 20.883 79.539 21.035 78.543 21.035\n" " c 77.582 21.035 76.734 20.887 75.996 20.586 c 75.262 20.289 74.641 19.867\n" " 74.137 19.328 c 73.641 18.789 73.266 18.145 73.012 17.395 c 72.758 16.637\n" " 72.629 15.805 72.629 14.891 c 72.629 14.008 72.75 13.195 72.988 12.453 \n" "c 73.234 11.703 73.609 11.055 74.102 10.508 c 74.598 9.961 75.219 9.535 \n" "75.969 9.23 c 76.719 8.926 77.598 8.773 78.602 8.773 c 79.664 8.773 80.57\n" " 8.926 81.324 9.23 c 82.074 9.535 82.688 9.961 83.16 10.508 c 83.641 11.047\n" " 83.992 11.691 84.211 12.441 c 84.438 13.184 84.551 14 84.551 14.891 c 81.34\n" " 14.891 m 81.34 13.484 81.109 12.465 80.652 11.832 c 80.195 11.199 79.527\n" " 10.883 78.652 10.883 c 77.75 10.883 77.059 11.203 76.578 11.844 c 76.098\n" " 12.484 75.855 13.5 75.855 14.891 c 75.855 15.598 75.918 16.207 76.043 16.715\n" " c 76.176 17.227 76.355 17.645 76.59 17.973 c 76.824 18.301 77.102 18.543\n" " 77.43 18.703 c 77.758 18.855 78.117 18.934 78.512 18.934 c 78.965 18.934\n" " 79.363 18.855 79.715 18.703 c 80.07 18.543 80.371 18.301 80.609 17.973 \n" "c 80.852 17.645 81.031 17.227 81.156 16.715 c 81.281 16.203 81.344 15.598\n" " 81.344 14.891 c 93.965 20.812 m 93.965 14.18 l 93.965 13.723 93.934 13.301\n" " 93.867 12.922 c 93.809 12.535 93.707 12.207 93.562 11.938 c 93.426 11.66\n" " 93.238 11.445 93.004 11.293 c 92.777 11.141 92.5 11.062 92.172 11.062 c\n" " 91.859 11.062 91.574 11.145 91.32 11.312 c 91.066 11.473 90.844 11.707 \n" "90.652 12.012 c 90.469 12.309 90.328 12.672 90.227 13.094 c 90.125 13.508\n" " 90.074 13.969 90.074 14.469 c 90.074 20.805 l 87.004 20.805 l 87.004 11.625\n" " l 87.004 11.371 87 11.109 86.992 10.84 c 86.992 10.57 86.984 10.316 86.969\n" " 10.074 c 86.961 9.828 86.953 9.609 86.945 9.418 c 86.938 9.223 86.926 9.074\n" " 86.914 8.98 c 89.844 8.98 l 89.859 9.066 89.871 9.211 89.887 9.406 c 89.902\n" " 9.594 89.914 9.809 89.93 10.039 c 89.945 10.273 89.957 10.504 89.961 10.738\n" " c 89.977 10.973 89.984 11.168 89.984 11.328 c 90.027 11.328 l 90.406 10.426\n" " 90.875 9.77 91.438 9.359 c 92.004 8.953 92.684 8.746 93.469 8.746 c 94.371\n" " 8.746 95.098 8.969 95.645 9.414 c 96.199 9.852 96.57 10.488 96.758 11.328\n" " c 96.824 11.328 l 97.035 10.84 97.262 10.434 97.5 10.105 c 97.746 9.777\n" " 98.016 9.516 98.309 9.32 c 98.605 9.117 98.93 8.969 99.27 8.883 c 99.621\n" " 8.797 99.996 8.75 100.406 8.75 c 101.055 8.75 101.602 8.867 102.047 9.102\n" " c 102.5 9.336 102.863 9.656 103.141 10.062 c 103.426 10.469 103.629 10.953\n" " 103.754 11.504 c 103.887 12.059 103.949 12.656 103.949 13.297 c 103.949\n" " 20.805 l 100.902 20.805 l 100.902 14.172 l 100.902 13.715 100.871 13.293\n" " 100.805 12.914 c 100.746 12.527 100.645 12.199 100.5 11.93 c 100.363 11.652\n" " 100.176 11.438 99.941 11.285 c 99.715 11.133 99.438 11.055 99.109 11.055\n" " c 98.805 11.055 98.523 11.137 98.27 11.297 c 98.023 11.449 97.805 11.672\n" " 97.613 11.965 c 97.43 12.25 97.289 12.59 97.188 12.992 c 97.086 13.395 \n" "97.027 13.836 97.012 14.324 c 97.012 20.805 l 93.965 20.805 l 113.852 20.805\n" " m 113.852 14.172 l 113.852 13.715 113.82 13.293 113.754 12.914 c 113.695\n" " 12.527 113.594 12.199 113.449 11.93 c 113.312 11.652 113.125 11.438 112.891\n" " 11.285 c 112.664 11.133 112.387 11.055 112.059 11.055 c 111.746 11.055 \n" "111.461 11.137 111.207 11.305 c 110.953 11.465 110.73 11.699 110.539 12.004\n" " c 110.355 12.301 110.215 12.664 110.113 13.086 c 110.012 13.5 109.961 13.961\n" " 109.961 14.461 c 109.961 20.797 l 106.891 20.797 l 106.891 11.617 l 106.891\n" " 11.363 106.887 11.102 106.879 10.832 c 106.879 10.562 106.871 10.309 106.855\n" " 10.066 c 106.848 9.82 106.84 9.602 106.832 9.41 c 106.824 9.215 106.812\n" " 9.066 106.801 8.973 c 109.73 8.973 l 109.746 9.059 109.758 9.203 109.773\n" " 9.398 c 109.789 9.586 109.801 9.801 109.816 10.031 c 109.832 10.266 109.844\n" " 10.496 109.848 10.73 c 109.863 10.965 109.871 11.16 109.871 11.32 c 109.914\n" " 11.32 l 110.293 10.418 110.762 9.762 111.324 9.352 c 111.891 8.945 112.57\n" " 8.738 113.355 8.738 c 114.258 8.738 114.984 8.961 115.531 9.406 c 116.086\n" " 9.844 116.457 10.48 116.645 11.32 c 116.711 11.32 l 116.922 10.832 117.148\n" " 10.426 117.387 10.098 c 117.633 9.77 117.902 9.508 118.195 9.312 c 118.492\n" " 9.109 118.816 8.961 119.156 8.875 c 119.508 8.789 119.883 8.742 120.293\n" " 8.742 c 120.941 8.742 121.488 8.859 121.934 9.094 c 122.387 9.328 122.75\n" " 9.648 123.027 10.055 c 123.312 10.461 123.516 10.945 123.641 11.496 c 123.773\n" " 12.051 123.836 12.648 123.836 13.289 c 123.836 20.797 l 120.789 20.797 \n" "l 120.789 14.164 l 120.789 13.707 120.758 13.285 120.691 12.906 c 120.633\n" " 12.52 120.531 12.191 120.387 11.922 c 120.25 11.645 120.062 11.43 119.828\n" " 11.277 c 119.602 11.125 119.324 11.047 118.996 11.047 c 118.691 11.047 \n" "118.41 11.129 118.156 11.289 c 117.91 11.441 117.691 11.664 117.5 11.957\n" " c 117.316 12.242 117.176 12.582 117.074 12.984 c 116.973 13.387 116.914\n" " 13.828 116.898 14.316 c 116.898 20.797 l 113.852 20.797 l 131.617 21.016\n" " m 130.766 21.016 129.996 20.891 129.312 20.645 c 128.637 20.391 128.055\n" " 20.008 127.574 19.496 c 127.094 18.98 126.727 18.336 126.469 17.562 c 126.215\n" " 16.781 126.086 15.871 126.086 14.832 c 126.086 13.703 126.234 12.754 126.535\n" " 11.98 c 126.84 11.207 127.25 10.586 127.758 10.113 c 128.273 9.633 128.867\n" " 9.285 129.539 9.074 c 130.211 8.863 130.914 8.758 131.66 8.758 c 132.594\n" " 8.758 133.387 8.922 134.043 9.25 c 134.707 9.57 135.25 10.023 135.672 10.605\n" " c 136.094 11.188 136.402 11.887 136.602 12.703 c 136.797 13.512 136.898\n" " 14.406 136.898 15.391 c 136.898 15.477 l 129.316 15.477 l 129.316 15.973\n" " 129.359 16.434 129.449 16.863 c 129.535 17.285 129.68 17.652 129.875 17.969\n" " c 130.07 18.273 130.328 18.52 130.641 18.699 c 130.953 18.875 131.332 18.961\n" " 131.777 18.961 c 132.316 18.961 132.758 18.848 133.098 18.621 c 133.441\n" " 18.387 133.684 18.031 133.828 17.551 c 136.723 17.801 l 136.59 18.137 136.406\n" " 18.492 136.164 18.871 c 135.93 19.25 135.617 19.598 135.223 19.922 c 134.828\n" " 20.234 134.336 20.496 133.746 20.707 c 133.164 20.91 132.453 21.012 131.617\n" " 21.012 c 131.617 10.688 m 131.305 10.688 131.008 10.742 130.73 10.852 c\n" " 130.461 10.953 130.223 11.121 130.02 11.355 c 129.824 11.582 129.664 11.875\n" " 129.539 12.242 c 129.414 12.605 129.348 13.043 129.332 13.555 c 133.922\n" " 13.555 l 133.863 12.602 133.633 11.887 133.234 11.414 c 132.832 10.934 \n" "132.293 10.691 131.617 10.691 c 146.895 20.797 m 146.895 14.164 l 146.895\n" " 13.707 146.859 13.285 146.785 12.906 c 146.711 12.52 146.594 12.191 146.426\n" " 11.922 c 146.258 11.645 146.039 11.43 145.77 11.277 c 145.5 11.125 145.172\n" " 11.047 144.785 11.047 c 144.414 11.047 144.074 11.129 143.77 11.297 c 143.473\n" " 11.457 143.211 11.691 142.992 11.996 c 142.773 12.293 142.602 12.656 142.48\n" " 13.078 c 142.363 13.492 142.305 13.953 142.305 14.453 c 142.305 20.789 \n" "l 139.234 20.789 l 139.234 11.609 l 139.234 11.355 139.23 11.094 139.223\n" " 10.824 c 139.223 10.555 139.215 10.301 139.199 10.059 c 139.191 9.812 139.184\n" " 9.594 139.176 9.402 c 139.168 9.207 139.156 9.059 139.145 8.965 c 142.074\n" " 8.965 l 142.09 9.051 142.102 9.195 142.117 9.391 c 142.133 9.578 142.145\n" " 9.793 142.16 10.023 c 142.176 10.258 142.188 10.488 142.191 10.723 c 142.207\n" " 10.957 142.215 11.152 142.215 11.312 c 142.258 11.312 l 142.672 10.41 143.195\n" " 9.754 143.82 9.344 c 144.445 8.938 145.195 8.73 146.059 8.73 c 146.773 \n" "8.73 147.375 8.848 147.863 9.082 c 148.359 9.316 148.758 9.637 149.066 10.043\n" " c 149.379 10.449 149.605 10.934 149.742 11.484 c 149.879 12.039 149.949\n" " 12.637 149.949 13.277 c 149.949 20.785 l 146.891 20.785 l 155.938 20.98\n" " m 155.035 20.98 154.34 20.738 153.852 20.25 c 153.363 19.754 153.121 19.008\n" " 153.121 18.012 c 153.121 11.039 l 151.625 11.039 l 151.625 8.965 l 153.273\n" " 8.965 l 154.234 6.188 l 156.156 6.188 l 156.156 8.965 l 158.395 8.965 l\n" " 158.395 11.039 l 156.156 11.039 l 156.156 17.18 l 156.156 17.754 156.266\n" " 18.18 156.484 18.457 c 156.703 18.727 157.043 18.859 157.5 18.859 c 157.688\n" " 18.859 157.859 18.844 158.012 18.816 c 158.164 18.789 158.336 18.75 158.523\n" " 18.707 c 158.523 20.609 l 158.145 20.734 157.742 20.824 157.32 20.883 c\n" " 156.898 20.949 156.434 20.98 155.934 20.98 c B Q\n" "Q q\n" "0 0.298039 0.431373 rg /a1 gs\n" "1 1 1 RG 0.797243 w\n" "1 J\n" "1 j\n" "[] 0.0 d\n" "4 M q 1 0 0 1 0 0 cm\n" "3.398 0.398 m 1.77 0.398 0.398 1.77 0.398 3.398 c 0.398 23.086 l 0.398 \n" "24.715 1.77 26.086 3.398 26.086 c 167.109 26.086 l 168.738 26.086 170.109\n" " 24.715 170.109 23.086 c 170.109 3.398 l 170.109 1.77 168.738 0.398 167.109\n" " 0.398 c h\n" "3.867 3.844 m 166.664 3.844 l 166.664 22.617 l 3.867 22.617 l h\n" "3.867 3.844 m B Q\n" "Q\n"; static Dict *getForCommentStampExtGStateDict(PDFDoc *doc) { Dict *a0Dict = new Dict(doc->getXRef()); a0Dict->add("CA", Object(0.588235)); a0Dict->add("ca", Object(0.588235)); Dict *a1Dict = new Dict(doc->getXRef()); a1Dict->add("CA", Object(1)); a1Dict->add("ca", Object(1)); Dict *extGStateDict = new Dict(doc->getXRef()); extGStateDict->add("a0", Object(a0Dict)); extGStateDict->add("a1", Object(a1Dict)); return extGStateDict; } #endif poppler-24.02.0/poppler/annot_stamp_for_public_release.h000066400000000000000000001020101455701731300234210ustar00rootroot00000000000000//======================================================================== // // annot_stamp_for_public_release.h // // Copyright (C) 2021 Mahmoud Ahmed Khalil // Copyright (C) 2021 Albert Astals Cid // // Mechanically extracted from an SVG created for Okular by Eugene Trounev eugene.trounev@gmail.com // // Licensed under GPLv2 or later // //======================================================================== #ifndef ANNOT_STAMP_FOR_PUBLIC_RELEASE_H #define ANNOT_STAMP_FOR_PUBLIC_RELEASE_H #include "Dict.h" #include "Object.h" #include "PDFDoc.h" static const double ANNOT_STAMP_FOR_PUBLIC_RELEASE_WIDTH = 222.258179; static const double ANNOT_STAMP_FOR_PUBLIC_RELEASE_HEIGHT = 26.484743; static const char *ANNOT_STAMP_FOR_PUBLIC_RELEASE = "1 0 0 -1 0 26.484741 cm\n" "q\n" "1 1 1 rg /a0 gs\n" "3.406 2.129 m 218.867 2.129 l 219.566 2.129 220.137 2.828 220.137 3.398\n" " c 220.137 23.09 l 220.137 23.789 219.57 24.359 218.867 24.359 c 3.406 24.359\n" " l 2.707 24.359 2.137 23.66 2.137 23.09 c 2.137 3.398 l 2.137 2.699 2.703\n" " 2.129 3.406 2.129 c h\n" "3.406 2.129 m f\n" "0 0.298039 0.431373 rg /a1 gs\n" "1 1 1 RG 0.265748 w\n" "0 J\n" "0 j\n" "[] 0.0 d\n" "4 M q 1 0 0 1 0 0 cm\n" "18.043 7.926 m 18.043 12.691 l 25.922 12.691 l 25.922 15.184 l 18.043 15.18\n" " l 18.043 20.828 l 14.82 20.828 l 14.82 5.434 l 26.172 5.434 l 26.172 7.926\n" " l 18.043 7.926 l 39.797 14.906 m 39.797 15.809 39.668 16.637 39.414 17.387\n" " c 39.168 18.137 38.793 18.785 38.289 19.332 c 37.785 19.871 37.16 20.293\n" " 36.41 20.598 c 35.66 20.895 34.785 21.047 33.789 21.047 c 32.828 21.047\n" " 31.98 20.898 31.242 20.598 c 30.508 20.301 29.887 19.879 29.383 19.34 c\n" " 28.887 18.801 28.512 18.156 28.258 17.406 c 28.004 16.648 27.875 15.816\n" " 27.875 14.902 c 27.875 14.02 27.996 13.207 28.234 12.465 c 28.48 11.715\n" " 28.855 11.066 29.348 10.52 c 29.844 9.973 30.465 9.547 31.215 9.242 c 31.965\n" " 8.938 32.844 8.785 33.848 8.785 c 34.91 8.785 35.816 8.938 36.57 9.242 \n" "c 37.32 9.547 37.934 9.973 38.406 10.52 c 38.887 11.059 39.238 11.703 39.457\n" " 12.453 c 39.684 13.195 39.797 14.012 39.797 14.902 c 36.586 14.902 m 36.586\n" " 13.496 36.355 12.477 35.898 11.844 c 35.441 11.211 34.773 10.895 33.898\n" " 10.895 c 32.996 10.895 32.305 11.215 31.824 11.855 c 31.344 12.496 31.102\n" " 13.512 31.102 14.902 c 31.102 15.609 31.164 16.219 31.289 16.727 c 31.422\n" " 17.238 31.602 17.656 31.836 17.984 c 32.07 18.312 32.348 18.555 32.676 \n" "18.715 c 33.004 18.867 33.363 18.945 33.758 18.945 c 34.211 18.945 34.609\n" " 18.867 34.961 18.715 c 35.316 18.555 35.617 18.312 35.855 17.984 c 36.098\n" " 17.656 36.277 17.238 36.402 16.727 c 36.527 16.215 36.59 15.609 36.59 14.902\n" " c 42.25 20.824 m 42.25 11.777 l 42.25 11.523 42.246 11.254 42.238 10.969\n" " c 42.238 10.684 42.23 10.414 42.215 10.16 c 42.207 9.898 42.199 9.664 42.191\n" " 9.461 c 42.184 9.25 42.172 9.098 42.16 9.004 c 45.09 9.004 l 45.105 9.09\n" " 45.117 9.246 45.133 9.461 c 45.148 9.672 45.16 9.906 45.176 10.16 c 45.191\n" " 10.414 45.203 10.672 45.207 10.926 c 45.223 11.172 45.23 11.375 45.23 11.527\n" " c 45.273 11.527 l 45.426 11.098 45.578 10.715 45.73 10.379 c 45.883 10.035\n" " 46.062 9.75 46.266 9.516 c 46.477 9.273 46.727 9.094 47.02 8.969 c 47.312\n" " 8.836 47.672 8.773 48.102 8.773 c 48.285 8.773 48.461 8.793 48.637 8.828\n" " c 48.82 8.855 48.957 8.895 49.051 8.938 c 49.051 11.504 l 48.855 11.461\n" " 48.652 11.422 48.449 11.395 c 48.254 11.359 48.016 11.34 47.738 11.34 c\n" " 46.973 11.34 46.375 11.648 45.945 12.27 c 45.523 12.891 45.312 13.805 45.312\n" " 15.023 c 45.312 20.824 l 42.246 20.828 l 69.75 10.309 m 69.75 10.973 69.637\n" " 11.613 69.41 12.23 c 69.191 12.844 68.852 13.387 68.395 13.859 c 67.938\n" " 14.332 67.352 14.711 66.645 14.996 c 65.938 15.273 65.102 15.41 64.133 \n" "15.41 c 60.309 15.41 l 60.309 20.828 l 57.086 20.828 l 57.086 5.434 l 64.004\n" " 5.434 l 64.988 5.434 65.84 5.555 66.562 5.793 c 67.285 6.027 67.883 6.359\n" " 68.355 6.789 c 68.828 7.219 69.18 7.734 69.406 8.328 c 69.641 8.926 69.758\n" " 9.586 69.758 10.305 c 66.512 10.359 m 66.512 9.566 66.27 8.965 65.789 8.555\n" " c 65.316 8.141 64.602 7.934 63.648 7.934 c 60.309 7.938 l 60.309 12.93 \n" "l 63.73 12.93 l 64.211 12.93 64.621 12.867 64.965 12.742 c 65.316 12.617\n" " 65.602 12.445 65.828 12.219 c 66.062 11.992 66.23 11.723 66.34 11.41 c \n" "66.449 11.09 66.504 10.738 66.504 10.359 c 74.992 9.004 m 74.992 15.637 \n" "l 74.992 16.094 75.027 16.52 75.102 16.902 c 75.176 17.281 75.293 17.609\n" " 75.461 17.887 c 75.629 18.156 75.844 18.367 76.105 18.52 c 76.375 18.672\n" " 76.703 18.75 77.09 18.75 c 77.461 18.75 77.797 18.668 78.094 18.508 c 78.398\n" " 18.34 78.66 18.105 78.879 17.809 c 79.098 17.504 79.266 17.141 79.383 16.727\n" " c 79.508 16.305 79.57 15.84 79.57 15.34 c 79.57 9.004 l 82.641 9.004 l \n" "82.641 18.184 l 82.641 18.43 82.641 18.695 82.641 18.969 c 82.648 19.238\n" " 82.656 19.496 82.664 19.746 c 82.68 19.988 82.691 20.203 82.695 20.402 \n" "c 82.711 20.59 82.723 20.734 82.727 20.828 c 79.797 20.828 l 79.789 20.742\n" " 79.773 20.602 79.754 20.414 c 79.738 20.219 79.727 20.004 79.711 19.77 \n" "c 79.703 19.535 79.691 19.305 79.68 19.07 c 79.672 18.836 79.668 18.641 \n" "79.668 18.48 c 79.613 18.48 l 79.207 19.375 78.684 20.027 78.051 20.438 \n" "c 77.426 20.844 76.676 21.051 75.812 21.051 c 75.105 21.051 74.504 20.934\n" " 74.008 20.699 c 73.512 20.465 73.109 20.148 72.797 19.75 c 72.492 19.344\n" " 72.27 18.859 72.129 18.309 c 71.992 17.754 71.922 17.156 71.922 16.516 \n" "c 71.922 9.008 l 74.992 9.008 l 96.965 14.875 m 96.965 15.77 96.875 16.598\n" " 96.691 17.355 c 96.516 18.105 96.242 18.758 95.871 19.312 c 95.5 19.859\n" " 95.027 20.285 94.449 20.59 c 93.883 20.895 93.207 21.047 92.43 21.047 c\n" " 92.078 21.047 91.73 21.012 91.379 20.938 c 91.027 20.863 90.699 20.746 \n" "90.383 20.578 c 90.07 20.41 89.781 20.191 89.52 19.922 c 89.258 19.652 89.031\n" " 19.32 88.844 18.926 c 88.82 18.926 l 88.82 19.078 88.812 19.254 88.797 \n" "19.449 c 88.789 19.645 88.777 19.84 88.766 20.027 c 88.75 20.211 88.734 \n" "20.375 88.711 20.52 c 88.695 20.664 88.684 20.766 88.668 20.824 c 85.684\n" " 20.824 l 85.707 20.57 85.723 20.207 85.738 19.742 c 85.762 19.27 85.77 \n" "18.73 85.77 18.125 c 85.77 4.609 l 88.84 4.609 l 88.84 9.133 l 88.84 9.367\n" " 88.836 9.594 88.828 9.82 c 88.828 10.039 88.824 10.242 88.816 10.434 c \n" "88.809 10.652 88.801 10.859 88.793 11.055 c 88.836 11.055 l 89.207 10.238\n" " 89.719 9.656 90.367 9.305 c 91.016 8.953 91.766 8.781 92.617 8.781 c 93.375\n" " 8.781 94.027 8.934 94.574 9.238 c 95.121 9.535 95.57 9.961 95.918 10.504\n" " c 96.273 11.043 96.539 11.684 96.703 12.426 c 96.871 13.168 96.953 13.98\n" " 96.953 14.863 c 93.75 14.863 m 93.75 13.523 93.555 12.527 93.16 11.879 \n" "c 92.766 11.23 92.16 10.906 91.336 10.906 c 91.023 10.906 90.715 10.969 \n" "90.406 11.094 c 90.102 11.219 89.828 11.438 89.586 11.75 c 89.344 12.055\n" " 89.152 12.473 89.008 12.996 c 88.863 13.512 88.789 14.168 88.789 14.965\n" " c 88.789 15.738 88.863 16.379 89.008 16.887 c 89.152 17.398 89.344 17.805\n" " 89.574 18.109 c 89.809 18.414 90.078 18.629 90.383 18.754 c 90.688 18.879\n" " 91 18.941 91.312 18.941 c 92.094 18.941 92.691 18.621 93.117 17.98 c 93.539\n" " 17.332 93.75 16.293 93.75 14.867 c 99.441 20.824 m 99.441 4.609 l 102.512\n" " 4.609 l 102.512 20.824 l 99.441 20.824 l 105.648 6.871 m 105.648 4.609 \n" "l 108.719 4.609 l 108.719 6.871 l 105.648 6.871 l 105.648 20.824 m 105.648\n" " 9 l 108.719 9 l 108.719 20.824 l 105.648 20.824 l 116.781 21.043 m 115.84\n" " 21.043 115.02 20.898 114.312 20.605 c 113.613 20.312 113.031 19.902 112.562\n" " 19.371 c 112.098 18.84 111.746 18.203 111.512 17.457 c 111.277 16.707 111.16\n" " 15.879 111.16 14.977 c 111.16 13.992 111.289 13.117 111.543 12.355 c 111.797\n" " 11.582 112.164 10.934 112.648 10.41 c 113.129 9.879 113.719 9.473 114.418\n" " 9.199 c 115.125 8.922 115.926 8.785 116.82 8.785 c 117.586 8.785 118.262\n" " 8.887 118.852 9.09 c 119.449 9.293 119.961 9.574 120.391 9.93 c 120.82 \n" "10.281 121.168 10.695 121.43 11.176 c 121.699 11.656 121.887 12.172 121.988\n" " 12.727 c 118.895 12.879 l 118.809 12.273 118.59 11.793 118.238 11.438 c\n" " 117.887 11.074 117.395 10.891 116.754 10.891 c 115.93 10.891 115.328 11.23\n" " 114.949 11.906 c 114.57 12.582 114.383 13.566 114.383 14.855 c 114.383 \n" "17.578 115.188 18.941 116.797 18.941 c 117.379 18.941 117.867 18.758 118.262\n" " 18.395 c 118.656 18.023 118.898 17.473 118.992 16.746 c 122.074 16.887 \n" "l 122 17.434 121.832 17.957 121.57 18.461 c 121.316 18.957 120.965 19.398\n" " 120.52 19.781 c 120.082 20.16 119.551 20.465 118.926 20.699 c 118.301 20.926\n" " 117.582 21.039 116.773 21.039 c 141.02 20.82 m 137.445 14.973 l 133.664\n" " 14.973 l 133.664 20.82 l 130.441 20.82 l 130.469 5.434 l 138.16 5.434 l\n" " 139.121 5.434 139.961 5.543 140.672 5.762 c 141.395 5.973 141.992 6.277\n" " 142.477 6.68 c 142.957 7.074 143.312 7.555 143.547 8.121 c 143.789 8.684\n" " 143.906 9.316 143.906 10.023 c 143.906 10.598 143.82 11.129 143.645 11.609\n" " c 143.477 12.082 143.238 12.504 142.934 12.875 c 142.637 13.246 142.277\n" " 13.559 141.863 13.816 c 141.449 14.062 140.996 14.246 140.508 14.363 c \n" "144.672 20.832 l 141.043 20.832 l 140.66 10.156 m 140.66 9.406 140.414 8.848\n" " 139.918 8.484 c 139.43 8.121 138.73 7.938 137.82 7.938 c 133.691 7.938 \n" "l 133.691 12.484 l 137.91 12.484 l 138.391 12.484 138.801 12.43 139.145 \n" "12.32 c 139.496 12.203 139.777 12.043 139.996 11.84 c 140.223 11.629 140.391\n" " 11.383 140.5 11.098 c 140.609 10.812 140.664 10.5 140.664 10.156 c 151.547\n" " 21.051 m 150.695 21.051 149.926 20.926 149.242 20.68 c 148.566 20.426 147.984\n" " 20.043 147.504 19.531 c 147.023 19.016 146.656 18.371 146.398 17.598 c \n" "146.145 16.816 146.016 15.906 146.016 14.867 c 146.016 13.738 146.164 12.789\n" " 146.465 12.016 c 146.77 11.242 147.18 10.621 147.688 10.148 c 148.203 9.668\n" " 148.797 9.32 149.469 9.109 c 150.141 8.898 150.844 8.793 151.59 8.793 c\n" " 152.523 8.793 153.316 8.957 153.973 9.285 c 154.637 9.605 155.18 10.059\n" " 155.602 10.641 c 156.023 11.223 156.332 11.922 156.531 12.738 c 156.727\n" " 13.547 156.828 14.441 156.828 15.426 c 156.828 15.512 l 149.246 15.512 \n" "l 149.246 16.008 149.289 16.469 149.379 16.898 c 149.465 17.32 149.609 17.688\n" " 149.805 18.004 c 150 18.309 150.258 18.555 150.57 18.734 c 150.883 18.91\n" " 151.262 18.996 151.707 18.996 c 152.246 18.996 152.688 18.883 153.027 18.656\n" " c 153.371 18.422 153.613 18.066 153.758 17.586 c 156.652 17.836 l 156.52\n" " 18.172 156.336 18.527 156.094 18.906 c 155.859 19.285 155.547 19.633 155.152\n" " 19.957 c 154.758 20.27 154.266 20.531 153.676 20.742 c 153.094 20.945 152.383\n" " 21.047 151.547 21.047 c 151.547 10.723 m 151.234 10.723 150.938 10.777 \n" "150.66 10.887 c 150.391 10.988 150.152 11.156 149.949 11.391 c 149.754 11.617\n" " 149.594 11.91 149.469 12.277 c 149.344 12.641 149.277 13.078 149.262 13.59\n" " c 153.852 13.59 l 153.793 12.637 153.562 11.922 153.164 11.449 c 152.762\n" " 10.969 152.223 10.727 151.547 10.727 c 159.164 20.832 m 159.164 4.617 l\n" " 162.234 4.617 l 162.234 20.832 l 159.164 20.832 l 170.211 21.051 m 169.359\n" " 21.051 168.59 20.926 167.906 20.68 c 167.23 20.426 166.648 20.043 166.168\n" " 19.531 c 165.688 19.016 165.32 18.371 165.062 17.598 c 164.809 16.816 164.68\n" " 15.906 164.68 14.867 c 164.68 13.738 164.828 12.789 165.129 12.016 c 165.434\n" " 11.242 165.844 10.621 166.352 10.148 c 166.867 9.668 167.461 9.32 168.133\n" " 9.109 c 168.805 8.898 169.508 8.793 170.254 8.793 c 171.188 8.793 171.98\n" " 8.957 172.637 9.285 c 173.301 9.605 173.844 10.059 174.266 10.641 c 174.688\n" " 11.223 174.996 11.922 175.195 12.738 c 175.391 13.547 175.492 14.441 175.492\n" " 15.426 c 175.492 15.512 l 167.91 15.512 l 167.91 16.008 167.953 16.469 \n" "168.043 16.898 c 168.129 17.32 168.273 17.688 168.469 18.004 c 168.664 18.309\n" " 168.922 18.555 169.234 18.734 c 169.547 18.91 169.926 18.996 170.371 18.996\n" " c 170.91 18.996 171.352 18.883 171.691 18.656 c 172.035 18.422 172.277 \n" "18.066 172.422 17.586 c 175.316 17.836 l 175.184 18.172 175 18.527 174.758\n" " 18.906 c 174.523 19.285 174.211 19.633 173.816 19.957 c 173.422 20.27 172.93\n" " 20.531 172.34 20.742 c 171.758 20.945 171.047 21.047 170.211 21.047 c 170.211\n" " 10.723 m 169.898 10.723 169.602 10.777 169.324 10.887 c 169.055 10.988 \n" "168.816 11.156 168.613 11.391 c 168.418 11.617 168.258 11.91 168.133 12.277\n" " c 168.008 12.641 167.941 13.078 167.926 13.59 c 172.516 13.59 l 172.457\n" " 12.637 172.227 11.922 171.828 11.449 c 171.426 10.969 170.887 10.727 170.211\n" " 10.727 c 180.559 21.051 m 179.992 21.051 179.48 20.973 179.027 20.82 c \n" "178.582 20.66 178.203 20.43 177.891 20.133 c 177.578 19.828 177.336 19.453\n" " 177.168 19.008 c 177 18.562 176.918 18.059 176.918 17.488 c 176.918 16.789\n" " 177.039 16.203 177.277 15.73 c 177.523 15.25 177.859 14.863 178.281 14.57\n" " c 178.703 14.273 179.199 14.059 179.766 13.926 c 180.332 13.789 180.938\n" " 13.715 181.578 13.707 c 184.125 13.664 l 184.125 13.062 l 184.125 12.633\n" " 184.086 12.277 184.004 11.992 c 183.93 11.699 183.82 11.465 183.676 11.281\n" " c 183.531 11.098 183.348 10.973 183.129 10.898 c 182.918 10.816 182.672\n" " 10.777 182.398 10.777 c 182.145 10.777 181.914 10.805 181.711 10.855 c \n" "181.516 10.906 181.344 10.996 181.199 11.129 c 181.055 11.254 180.934 11.426\n" " 180.84 11.641 c 180.754 11.852 180.691 12.121 180.652 12.449 c 177.449 \n" "12.297 l 177.535 11.781 177.695 11.309 177.93 10.887 c 178.164 10.457 178.484\n" " 10.086 178.891 9.773 c 179.305 9.461 179.812 9.219 180.41 9.051 c 181.016\n" " 8.875 181.723 8.789 182.531 8.789 c 183.266 8.789 183.922 8.879 184.5 9.062\n" " c 185.074 9.246 185.562 9.52 185.965 9.883 c 186.367 10.238 186.672 10.68\n" " 186.883 11.203 c 187.094 11.727 187.199 12.336 187.199 13.027 c 187.199\n" " 17.332 l 187.199 17.609 187.211 17.855 187.23 18.074 c 187.258 18.293 187.309\n" " 18.477 187.371 18.633 c 187.445 18.777 187.543 18.891 187.668 18.973 c \n" "187.801 19.047 187.965 19.082 188.172 19.082 c 188.406 19.082 188.629 19.059\n" " 188.848 19.016 c 188.848 20.676 l 188.664 20.719 188.5 20.758 188.355 20.797\n" " c 188.211 20.832 188.062 20.863 187.918 20.883 c 187.773 20.906 187.617\n" " 20.922 187.449 20.938 c 187.289 20.953 187.098 20.961 186.883 20.961 c \n" "186.109 20.961 185.539 20.773 185.168 20.395 c 184.805 20.016 184.586 19.457\n" " 184.512 18.723 c 184.445 18.723 l 184.039 19.457 183.512 20.031 182.871\n" " 20.438 c 182.238 20.844 181.465 21.051 180.555 21.051 c 184.129 15.359 \n" "m 182.555 15.383 l 182.227 15.398 181.914 15.426 181.613 15.469 c 181.32\n" " 15.504 181.062 15.59 180.836 15.719 c 180.617 15.844 180.441 16.023 180.312\n" " 16.266 c 180.18 16.508 180.117 16.832 180.117 17.25 c 180.117 17.812 180.246\n" " 18.23 180.5 18.508 c 180.762 18.777 181.109 18.91 181.539 18.91 c 181.934\n" " 18.91 182.289 18.828 182.609 18.66 c 182.93 18.492 183.199 18.273 183.418\n" " 18.004 c 183.645 17.727 183.82 17.41 183.941 17.055 c 184.066 16.699 184.129\n" " 16.332 184.129 15.961 c 184.129 15.359 l 200.246 17.379 m 200.246 17.945\n" " 200.125 18.457 199.887 18.91 c 199.652 19.363 199.312 19.746 198.859 20.07\n" " c 198.406 20.383 197.855 20.629 197.199 20.801 c 196.543 20.969 195.797\n" " 21.051 194.961 21.051 c 194.211 21.051 193.531 20.996 192.93 20.887 c 192.324\n" " 20.777 191.793 20.598 191.336 20.352 c 190.883 20.098 190.504 19.77 190.199\n" " 19.367 c 189.895 18.965 189.664 18.473 189.512 17.883 c 192.211 17.48 l\n" " 192.297 17.809 192.418 18.074 192.57 18.277 c 192.723 18.48 192.914 18.637\n" " 193.137 18.746 c 193.363 18.855 193.625 18.93 193.922 18.965 c 194.227 \n" "19 194.574 19.02 194.961 19.02 c 195.312 19.02 195.633 19 195.934 18.965\n" " c 196.238 18.922 196.5 18.852 196.719 18.758 c 196.945 18.656 197.121 18.516\n" " 197.242 18.344 c 197.367 18.16 197.43 17.938 197.43 17.668 c 197.43 17.363\n" " 197.34 17.121 197.156 16.945 c 196.98 16.762 196.738 16.617 196.426 16.508\n" " c 196.121 16.391 195.754 16.293 195.332 16.211 c 194.918 16.125 194.473\n" " 16.023 194 15.914 c 193.504 15.805 193.016 15.672 192.535 15.52 c 192.055\n" " 15.367 191.625 15.16 191.246 14.898 c 190.867 14.637 190.562 14.305 190.328\n" " 13.902 c 190.094 13.496 189.977 12.98 189.977 12.363 c 189.977 11.801 190.086\n" " 11.305 190.305 10.867 c 190.523 10.422 190.844 10.047 191.266 9.742 c 191.688\n" " 9.43 192.207 9.191 192.828 9.031 c 193.453 8.863 194.172 8.781 194.98 8.781\n" " c 195.621 8.781 196.219 8.844 196.773 8.969 c 197.328 9.086 197.824 9.273\n" " 198.258 9.535 c 198.695 9.789 199.059 10.117 199.352 10.52 c 199.648 10.922\n" " 199.859 11.406 199.973 11.973 c 197.25 12.258 l 197.199 11.973 197.109 \n" "11.742 196.977 11.559 c 196.844 11.371 196.68 11.219 196.484 11.109 c 196.297\n" " 11 196.074 10.926 195.816 10.891 c 195.562 10.848 195.281 10.824 194.977\n" " 10.824 c 194.25 10.824 193.703 10.918 193.336 11.109 c 192.973 11.293 192.789\n" " 11.602 192.789 12.039 c 192.789 12.309 192.863 12.523 193.008 12.684 c \n" "193.16 12.844 193.371 12.98 193.641 13.086 c 193.918 13.188 194.242 13.277\n" " 194.613 13.359 c 194.992 13.434 195.402 13.523 195.848 13.633 c 196.395\n" " 13.75 196.93 13.887 197.453 14.047 c 197.984 14.199 198.453 14.414 198.863\n" " 14.691 c 199.277 14.961 199.609 15.312 199.859 15.742 c 200.113 16.172 \n" "200.242 16.719 200.242 17.383 c 207.574 21.055 m 206.723 21.055 205.953 \n" "20.93 205.27 20.684 c 204.594 20.43 204.012 20.047 203.531 19.535 c 203.051\n" " 19.02 202.684 18.375 202.426 17.602 c 202.172 16.82 202.043 15.91 202.043\n" " 14.871 c 202.043 13.742 202.191 12.793 202.492 12.02 c 202.797 11.246 203.207\n" " 10.625 203.715 10.152 c 204.23 9.672 204.824 9.324 205.496 9.113 c 206.168\n" " 8.902 206.871 8.797 207.617 8.797 c 208.551 8.797 209.344 8.961 210 9.289\n" " c 210.664 9.609 211.207 10.062 211.629 10.645 c 212.051 11.227 212.359 \n" "11.926 212.559 12.742 c 212.754 13.551 212.855 14.445 212.855 15.43 c 212.855\n" " 15.516 l 205.273 15.516 l 205.273 16.012 205.316 16.473 205.406 16.902 \n" "c 205.492 17.324 205.637 17.691 205.832 18.008 c 206.027 18.312 206.285 \n" "18.559 206.598 18.738 c 206.91 18.914 207.289 19 207.734 19 c 208.273 19\n" " 208.715 18.887 209.055 18.66 c 209.398 18.426 209.641 18.07 209.785 17.59\n" " c 212.68 17.84 l 212.547 18.176 212.363 18.531 212.121 18.91 c 211.887 \n" "19.289 211.574 19.637 211.18 19.961 c 210.785 20.273 210.293 20.535 209.703\n" " 20.746 c 209.121 20.949 208.41 21.051 207.574 21.051 c 207.574 10.727 m\n" " 207.262 10.727 206.965 10.781 206.688 10.891 c 206.418 10.992 206.18 11.16\n" " 205.977 11.395 c 205.781 11.621 205.621 11.914 205.496 12.281 c 205.371\n" " 12.645 205.305 13.082 205.289 13.594 c 209.879 13.594 l 209.82 12.641 209.59\n" " 11.926 209.191 11.453 c 208.789 10.973 208.25 10.73 207.574 10.73 c B Q\n" "Q q\n" "0 0 222 26.484 re W n\n" "0 0.298039 0.431373 rg /a1 gs\n" "3.398 0.398 m 1.77 0.398 0.398 1.77 0.398 3.398 c 0.398 23.086 l 0.398 \n" "24.715 1.77 26.086 3.398 26.086 c 218.859 26.086 l 220.488 26.086 221.859\n" " 24.715 221.859 23.086 c 221.859 3.398 l 221.859 1.77 220.488 0.398 218.859\n" " 0.398 c h\n" "3.867 3.844 m 218.414 3.844 l 218.414 22.617 l 3.867 22.617 l h\n" "3.867 3.844 m f\n" "Q q\n" "1 1 1 RG /a1 gs\n" "0.797243 w\n" "1 J\n" "1 j\n" "[] 0.0 d\n" "4 M q 1 0 0 1 0 0 cm\n" "3.398 0.398 m 1.77 0.398 0.398 1.77 0.398 3.398 c 0.398 23.086 l 0.398 \n" "24.715 1.77 26.086 3.398 26.086 c 218.859 26.086 l 220.488 26.086 221.859\n" " 24.715 221.859 23.086 c 221.859 3.398 l 221.859 1.77 220.488 0.398 218.859\n" " 0.398 c h\n" "3.867 3.844 m 218.414 3.844 l 218.414 22.617 l 3.867 22.617 l h\n" "3.867 3.844 m S Q\n" "Q\n"; static Dict *getForPublicReleaseStampExtGStateDict(PDFDoc *doc) { Dict *a0Dict = new Dict(doc->getXRef()); a0Dict->add("CA", Object(0.588235)); a0Dict->add("ca", Object(0.588235)); Dict *a1Dict = new Dict(doc->getXRef()); a1Dict->add("CA", Object(1)); a1Dict->add("ca", Object(1)); Dict *extGStateDict = new Dict(doc->getXRef()); extGStateDict->add("a0", Object(a0Dict)); extGStateDict->add("a1", Object(a1Dict)); return extGStateDict; } #endif poppler-24.02.0/poppler/annot_stamp_not_approved.h000066400000000000000000001265371455701731300223210ustar00rootroot00000000000000//======================================================================== // // annot_stamp_not_approved.h // // Copyright (C) 2021 Mahmoud Ahmed Khalil // Copyright (C) 2021 Albert Astals Cid // // Mechanically extracted from an SVG created for Okular by Eugene Trounev eugene.trounev@gmail.com // // Licensed under GPLv2 or later // //======================================================================== #ifndef ANNOT_STAMP_NOT_APPROVED_H #define ANNOT_STAMP_NOT_APPROVED_H #include "Dict.h" #include "Object.h" #include "PDFDoc.h" static const double ANNOT_STAMP_NOT_APPROVED_WIDTH = 170.508179; static const double ANNOT_STAMP_NOT_APPROVED_HEIGHT = 26.484743; static const char *ANNOT_STAMP_NOT_APPROVED = "1 0 0 -1 0 26.484741 cm\n" "q\n" "1 1 1 rg /a0 gs\n" "3.406 2.129 m 167.117 2.129 l 167.816 2.129 168.387 2.828 168.387 3.398\n" " c 168.387 23.09 l 168.387 23.789 167.82 24.359 167.117 24.359 c 3.406 24.359\n" " l 2.707 24.359 2.137 23.66 2.137 23.09 c 2.137 3.398 l 2.137 2.699 2.703\n" " 2.129 3.406 2.129 c h\n" "3.406 2.129 m f\n" "0.74902 0 0 rg /a1 gs\n" "24.195 20.828 m 17.484 8.973 l 17.52 9.293 17.555 9.613 17.582 9.934 c \n" "17.609 10.211 17.633 10.508 17.648 10.828 c 17.672 11.148 17.68 11.453 17.68\n" " 11.746 c 17.68 20.828 l 14.82 20.828 l 14.82 5.434 l 18.504 5.434 l 25.312\n" " 17.387 l 25.277 17.082 25.242 16.762 25.215 16.426 c 25.188 16.141 25.16\n" " 15.82 25.137 15.465 c 25.121 15.109 25.113 14.746 25.113 14.383 c 25.113\n" " 5.434 l 27.977 5.434 l 27.977 20.828 l 24.195 20.828 l 42.289 14.906 m \n" "42.289 15.809 42.16 16.637 41.906 17.387 c 41.66 18.137 41.285 18.785 40.781\n" " 19.332 c 40.277 19.871 39.652 20.293 38.902 20.598 c 38.152 20.895 37.277\n" " 21.047 36.281 21.047 c 35.32 21.047 34.473 20.898 33.734 20.598 c 33 20.301\n" " 32.379 19.879 31.875 19.34 c 31.379 18.801 31.004 18.156 30.75 17.406 c\n" " 30.496 16.648 30.367 15.816 30.367 14.902 c 30.367 14.02 30.488 13.207 \n" "30.727 12.465 c 30.973 11.715 31.348 11.066 31.84 10.52 c 32.336 9.973 32.957\n" " 9.547 33.707 9.242 c 34.457 8.938 35.336 8.785 36.34 8.785 c 37.402 8.785\n" " 38.309 8.938 39.062 9.242 c 39.812 9.547 40.426 9.973 40.898 10.52 c 41.379\n" " 11.059 41.73 11.703 41.949 12.453 c 42.176 13.195 42.289 14.012 42.289 \n" "14.902 c 39.078 14.902 m 39.078 13.496 38.848 12.477 38.391 11.844 c 37.934\n" " 11.211 37.266 10.895 36.391 10.895 c 35.488 10.895 34.797 11.215 34.316\n" " 11.855 c 33.836 12.496 33.594 13.512 33.594 14.902 c 33.594 15.609 33.656\n" " 16.219 33.781 16.727 c 33.914 17.238 34.094 17.656 34.328 17.984 c 34.562\n" " 18.312 34.84 18.555 35.168 18.715 c 35.496 18.867 35.855 18.945 36.25 18.945\n" " c 36.703 18.945 37.102 18.867 37.453 18.715 c 37.809 18.555 38.109 18.312\n" " 38.348 17.984 c 38.59 17.656 38.77 17.238 38.895 16.727 c 39.02 16.215 \n" "39.082 15.609 39.082 14.902 c 47.77 21.02 m 46.867 21.02 46.172 20.777 45.684\n" " 20.289 c 45.195 19.793 44.953 19.047 44.953 18.051 c 44.953 11.078 l 43.449\n" " 11.082 l 43.449 9.008 l 45.098 9.008 l 46.059 6.23 l 47.98 6.23 l 47.98\n" " 9.008 l 50.219 9.008 l 50.219 11.082 l 47.98 11.082 l 47.98 17.223 l 47.98\n" " 17.797 48.09 18.223 48.309 18.5 c 48.527 18.77 48.867 18.906 49.324 18.902\n" " c 49.512 18.902 49.684 18.887 49.836 18.859 c 49.988 18.832 50.16 18.793\n" " 50.348 18.75 c 50.348 20.652 l 49.969 20.777 49.566 20.867 49.145 20.926\n" " c 48.723 20.992 48.258 21.023 47.758 21.023 c 68.398 20.828 m 67.031 16.895\n" " l 61.164 16.895 l 59.797 20.828 l 56.574 20.828 l 62.191 5.434 l 65.992\n" " 5.434 l 71.586 20.828 l 68.395 20.828 l 64.789 10.043 m 64.703 9.789 64.613\n" " 9.531 64.527 9.277 c 64.445 9.016 64.375 8.777 64.309 8.566 c 64.25 8.348\n" " 64.199 8.168 64.156 8.031 c 64.121 7.895 64.098 7.816 64.09 7.801 c 64.082\n" " 7.824 64.062 7.902 64.023 8.043 c 63.988 8.18 63.938 8.355 63.871 8.566\n" " c 63.812 8.777 63.738 9.016 63.652 9.277 c 63.57 9.531 63.488 9.789 63.402\n" " 10.043 c 61.883 14.469 l 66.309 14.469 l 64.789 10.043 l 84.938 14.863 \n" "m 84.938 15.758 84.848 16.586 84.664 17.344 c 84.488 18.102 84.215 18.754\n" " 83.844 19.301 c 83.473 19.848 83 20.277 82.422 20.59 c 81.855 20.895 81.18\n" " 21.047 80.402 21.047 c 80.051 21.047 79.703 21.012 79.352 20.938 c 79.008\n" " 20.863 78.68 20.75 78.367 20.586 c 78.055 20.418 77.762 20.203 77.492 19.941\n" " c 77.23 19.672 77.004 19.34 76.816 18.945 c 76.75 18.945 l 76.758 18.98\n" " 76.766 19.074 76.773 19.219 c 76.781 19.363 76.789 19.535 76.797 19.73 \n" "c 76.805 19.918 76.809 20.125 76.809 20.344 c 76.816 20.555 76.82 20.75 \n" "76.82 20.934 c 76.82 25.469 l 73.75 25.469 l 73.75 11.723 l 73.75 11.117\n" " 73.738 10.578 73.719 10.105 c 73.703 9.633 73.688 9.266 73.664 9 c 76.648\n" " 9 l 76.664 9.051 76.676 9.148 76.691 9.297 c 76.715 9.441 76.727 9.609 \n" "76.734 9.801 c 76.75 9.988 76.762 10.188 76.766 10.391 c 76.773 10.594 76.777\n" " 10.773 76.777 10.926 c 76.82 10.926 l 77.191 10.145 77.703 9.59 78.352 \n" "9.254 c 79 8.918 79.75 8.75 80.602 8.75 c 81.352 8.75 82 8.902 82.547 9.207\n" " c 83.094 9.512 83.543 9.934 83.891 10.473 c 84.246 11.012 84.512 11.656\n" " 84.676 12.406 c 84.852 13.148 84.938 13.965 84.938 14.855 c 81.734 14.855\n" " m 81.734 13.508 81.531 12.512 81.121 11.863 c 80.715 11.207 80.105 10.879\n" " 79.297 10.879 c 78.992 10.879 78.684 10.945 78.379 11.074 c 78.082 11.199\n" " 77.812 11.418 77.57 11.73 c 77.336 12.035 77.145 12.453 76.992 12.977 c\n" " 76.848 13.492 76.773 14.148 76.773 14.945 c 76.773 15.719 76.848 16.363\n" " 76.992 16.879 c 77.137 17.391 77.328 17.797 77.559 18.102 c 77.801 18.406\n" " 78.07 18.625 78.367 18.758 c 78.664 18.883 78.969 18.945 79.273 18.945 \n" "c 79.668 18.945 80.016 18.867 80.324 18.715 c 80.629 18.555 80.887 18.312\n" " 81.09 17.984 c 81.301 17.648 81.461 17.223 81.57 16.707 c 81.68 16.191 \n" "81.734 15.574 81.734 14.859 c 98.617 14.859 m 98.617 15.754 98.527 16.582\n" " 98.344 17.34 c 98.168 18.098 97.895 18.75 97.523 19.297 c 97.152 19.844\n" " 96.68 20.273 96.102 20.586 c 95.535 20.891 94.859 21.043 94.082 21.043 \n" "c 93.73 21.043 93.383 21.008 93.031 20.934 c 92.688 20.859 92.359 20.746\n" " 92.047 20.582 c 91.734 20.414 91.441 20.199 91.172 19.938 c 90.91 19.668\n" " 90.684 19.336 90.496 18.941 c 90.43 18.941 l 90.438 18.977 90.445 19.07\n" " 90.453 19.215 c 90.461 19.359 90.469 19.531 90.477 19.727 c 90.484 19.914\n" " 90.488 20.121 90.488 20.34 c 90.496 20.551 90.5 20.746 90.5 20.93 c 90.5\n" " 25.465 l 87.43 25.465 l 87.43 11.719 l 87.43 11.113 87.418 10.574 87.398\n" " 10.102 c 87.383 9.629 87.367 9.262 87.344 8.996 c 90.328 8.996 l 90.344\n" " 9.047 90.355 9.145 90.371 9.293 c 90.395 9.438 90.406 9.605 90.414 9.797\n" " c 90.43 9.984 90.441 10.184 90.445 10.387 c 90.453 10.59 90.457 10.77 90.457\n" " 10.922 c 90.5 10.922 l 90.871 10.141 91.383 9.586 92.031 9.25 c 92.68 8.914\n" " 93.43 8.746 94.281 8.746 c 95.031 8.746 95.68 8.898 96.227 9.203 c 96.773\n" " 9.508 97.223 9.93 97.57 10.469 c 97.926 11.008 98.191 11.652 98.355 12.402\n" " c 98.531 13.145 98.617 13.961 98.617 14.852 c 95.414 14.852 m 95.414 13.504\n" " 95.211 12.508 94.801 11.859 c 94.395 11.203 93.785 10.875 92.977 10.875\n" " c 92.672 10.875 92.363 10.941 92.059 11.07 c 91.762 11.195 91.492 11.414\n" " 91.25 11.727 c 91.016 12.031 90.824 12.449 90.672 12.973 c 90.527 13.488\n" " 90.453 14.145 90.453 14.941 c 90.453 15.715 90.527 16.359 90.672 16.875\n" " c 90.816 17.387 91.008 17.793 91.238 18.098 c 91.48 18.402 91.75 18.621\n" " 92.047 18.754 c 92.344 18.879 92.648 18.941 92.953 18.941 c 93.348 18.941\n" " 93.695 18.863 94.004 18.711 c 94.309 18.551 94.566 18.309 94.77 17.98 c\n" " 94.98 17.645 95.141 17.219 95.25 16.703 c 95.359 16.188 95.414 15.57 95.414\n" " 14.855 c 101.105 20.82 m 101.105 11.773 l 101.105 11.52 101.102 11.25 101.094\n" " 10.965 c 101.094 10.68 101.086 10.41 101.07 10.156 c 101.062 9.895 101.055\n" " 9.66 101.047 9.457 c 101.039 9.246 101.027 9.094 101.016 9 c 103.945 9 \n" "l 103.961 9.086 103.973 9.242 103.988 9.457 c 104.004 9.668 104.016 9.902\n" " 104.031 10.156 c 104.047 10.41 104.059 10.668 104.062 10.922 c 104.078 \n" "11.168 104.086 11.371 104.086 11.523 c 104.129 11.523 l 104.281 11.094 104.434\n" " 10.711 104.586 10.375 c 104.738 10.031 104.918 9.746 105.121 9.512 c 105.332\n" " 9.27 105.582 9.09 105.875 8.965 c 106.168 8.832 106.527 8.77 106.957 8.77\n" " c 107.141 8.77 107.316 8.789 107.492 8.824 c 107.676 8.852 107.812 8.891\n" " 107.906 8.934 c 107.906 11.5 l 107.711 11.457 107.508 11.418 107.305 11.391\n" " c 107.109 11.355 106.871 11.336 106.594 11.336 c 105.828 11.336 105.23 \n" "11.645 104.801 12.266 c 104.379 12.887 104.168 13.801 104.168 15.02 c 104.168\n" " 20.82 l 101.098 20.82 l 121.027 14.898 m 121.027 15.801 120.898 16.629 \n" "120.645 17.379 c 120.398 18.129 120.023 18.777 119.52 19.324 c 119.016 19.863\n" " 118.391 20.285 117.641 20.59 c 116.891 20.887 116.016 21.039 115.02 21.039\n" " c 114.059 21.039 113.211 20.891 112.473 20.59 c 111.738 20.293 111.117 \n" "19.871 110.613 19.332 c 110.117 18.793 109.742 18.148 109.488 17.398 c 109.234\n" " 16.641 109.105 15.809 109.105 14.895 c 109.105 14.012 109.227 13.199 109.465\n" " 12.457 c 109.711 11.707 110.086 11.059 110.578 10.512 c 111.074 9.965 111.695\n" " 9.539 112.445 9.234 c 113.195 8.93 114.074 8.777 115.078 8.777 c 116.141\n" " 8.777 117.047 8.93 117.801 9.234 c 118.551 9.539 119.164 9.965 119.637 \n" "10.512 c 120.117 11.051 120.469 11.695 120.688 12.445 c 120.914 13.188 121.027\n" " 14.004 121.027 14.895 c 117.816 14.895 m 117.816 13.488 117.586 12.469 \n" "117.129 11.836 c 116.672 11.203 116.004 10.887 115.129 10.887 c 114.227 \n" "10.887 113.535 11.207 113.055 11.848 c 112.574 12.488 112.332 13.504 112.332\n" " 14.895 c 112.332 15.602 112.395 16.211 112.52 16.719 c 112.652 17.23 112.832\n" " 17.648 113.066 17.977 c 113.301 18.305 113.578 18.547 113.906 18.707 c \n" "114.234 18.859 114.594 18.938 114.988 18.938 c 115.441 18.938 115.84 18.859\n" " 116.191 18.707 c 116.547 18.547 116.848 18.305 117.086 17.977 c 117.328\n" " 17.648 117.508 17.23 117.633 16.719 c 117.758 16.207 117.82 15.602 117.82\n" " 14.895 c 129.906 20.816 m 126.234 20.816 l 122.004 8.992 l 125.25 8.992\n" " l 127.316 15.602 l 127.383 15.82 127.449 16.055 127.523 16.301 c 127.598\n" " 16.543 127.664 16.781 127.73 17.023 c 127.797 17.266 127.859 17.496 127.918\n" " 17.723 c 127.984 17.949 128.043 18.152 128.094 18.336 c 128.137 18.16 128.191\n" " 17.965 128.258 17.746 c 128.324 17.52 128.391 17.289 128.453 17.047 c 128.527\n" " 16.805 128.598 16.566 128.672 16.324 c 128.754 16.082 128.828 15.855 128.902\n" " 15.637 c 131.055 8.992 l 134.266 8.992 l 129.906 20.816 l 140.777 21.035\n" " m 139.926 21.035 139.156 20.91 138.473 20.664 c 137.797 20.41 137.215 20.027\n" " 136.734 19.516 c 136.254 19 135.887 18.355 135.629 17.582 c 135.375 16.801\n" " 135.246 15.891 135.246 14.852 c 135.246 13.723 135.395 12.773 135.695 12\n" " c 136 11.227 136.41 10.605 136.918 10.133 c 137.434 9.652 138.027 9.305\n" " 138.699 9.094 c 139.371 8.883 140.074 8.777 140.82 8.777 c 141.754 8.777\n" " 142.547 8.941 143.203 9.27 c 143.867 9.59 144.41 10.043 144.832 10.625 \n" "c 145.254 11.207 145.562 11.906 145.762 12.723 c 145.957 13.531 146.059 \n" "14.426 146.059 15.41 c 146.059 15.496 l 138.477 15.496 l 138.477 15.992 \n" "138.52 16.453 138.609 16.883 c 138.695 17.305 138.84 17.672 139.035 17.988\n" " c 139.23 18.293 139.488 18.539 139.801 18.719 c 140.113 18.895 140.492 \n" "18.98 140.938 18.98 c 141.477 18.98 141.918 18.867 142.258 18.641 c 142.602\n" " 18.406 142.844 18.051 142.988 17.57 c 145.883 17.82 l 145.75 18.156 145.566\n" " 18.512 145.324 18.891 c 145.09 19.27 144.777 19.617 144.383 19.941 c 143.988\n" " 20.254 143.496 20.516 142.906 20.727 c 142.324 20.93 141.613 21.031 140.777\n" " 21.031 c 140.777 10.707 m 140.465 10.707 140.168 10.762 139.891 10.871 \n" "c 139.621 10.973 139.383 11.141 139.18 11.375 c 138.984 11.602 138.824 11.895\n" " 138.699 12.262 c 138.574 12.625 138.508 13.062 138.492 13.574 c 143.082\n" " 13.574 l 143.023 12.621 142.793 11.906 142.395 11.434 c 141.992 10.953 \n" "141.453 10.711 140.777 10.711 c 156.055 20.816 m 156.039 20.758 156.023 \n" "20.656 156 20.512 c 155.984 20.359 155.969 20.188 155.945 20 c 155.93 19.812\n" " 155.918 19.617 155.902 19.422 c 155.895 19.227 155.891 19.051 155.891 18.898\n" " c 155.848 18.898 l 155.492 19.664 154.992 20.215 154.352 20.547 c 153.719\n" " 20.875 152.957 21.039 152.066 21.039 c 151.324 21.039 150.68 20.887 150.133\n" " 20.582 c 149.594 20.277 149.145 19.852 148.789 19.305 c 148.438 18.758 \n" "148.176 18.113 148.004 17.371 c 147.836 16.621 147.754 15.805 147.754 14.922\n" " c 147.754 14.027 147.84 13.203 148.016 12.453 c 148.199 11.703 148.473 \n" "11.059 148.848 10.52 c 149.219 9.973 149.688 9.547 150.258 9.242 c 150.832\n" " 8.938 151.516 8.785 152.301 8.785 c 152.688 8.785 153.059 8.824 153.414\n" " 8.906 c 153.77 8.988 154.102 9.109 154.41 9.277 c 154.715 9.445 154.992\n" " 9.66 155.242 9.922 c 155.488 10.184 155.699 10.5 155.875 10.871 c 155.898\n" " 10.871 l 155.898 10.797 155.895 10.691 155.887 10.555 c 155.887 10.41 155.887\n" " 10.246 155.887 10.062 c 155.887 9.879 155.883 9.691 155.875 9.496 c 155.875\n" " 9.301 155.875 9.109 155.875 8.93 c 155.875 4.613 l 158.945 4.613 l 158.945\n" " 18.25 l 158.945 18.824 158.957 19.34 158.977 19.789 c 159 20.234 159.016\n" " 20.578 159.031 20.828 c 156.059 20.828 l 155.918 14.852 m 155.918 14.07\n" " 155.844 13.426 155.699 12.918 c 155.555 12.402 155.359 11.992 155.121 11.695\n" " c 154.887 11.391 154.617 11.18 154.312 11.062 c 154.016 10.938 153.707 \n" "10.875 153.395 10.875 c 153 10.875 152.652 10.953 152.344 11.105 c 152.047\n" " 11.258 151.789 11.5 151.578 11.828 c 151.375 12.156 151.219 12.574 151.109\n" " 13.086 c 151.008 13.598 150.957 14.211 150.957 14.934 c 150.957 17.613 \n" "151.762 18.953 153.371 18.953 c 153.676 18.953 153.984 18.887 154.289 18.758\n" " c 154.594 18.625 154.867 18.406 155.109 18.09 c 155.352 17.777 155.543 \n" "17.359 155.688 16.832 c 155.84 16.301 155.918 15.641 155.918 14.855 c f\n" "1 1 1 RG 0.531496 w\n" "0 J\n" "0 j\n" "[] 0.0 d\n" "4 M q 1 0 0 1 0 0 cm\n" "24.195 20.828 m 17.484 8.973 l 17.52 9.293 17.555 9.613 17.582 9.934 c \n" "17.609 10.211 17.633 10.508 17.648 10.828 c 17.672 11.148 17.68 11.453 17.68\n" " 11.746 c 17.68 20.828 l 14.82 20.828 l 14.82 5.434 l 18.504 5.434 l 25.312\n" " 17.387 l 25.277 17.082 25.242 16.762 25.215 16.426 c 25.188 16.141 25.16\n" " 15.82 25.137 15.465 c 25.121 15.109 25.113 14.746 25.113 14.383 c 25.113\n" " 5.434 l 27.977 5.434 l 27.977 20.828 l 24.195 20.828 l 42.289 14.906 m \n" "42.289 15.809 42.16 16.637 41.906 17.387 c 41.66 18.137 41.285 18.785 40.781\n" " 19.332 c 40.277 19.871 39.652 20.293 38.902 20.598 c 38.152 20.895 37.277\n" " 21.047 36.281 21.047 c 35.32 21.047 34.473 20.898 33.734 20.598 c 33 20.301\n" " 32.379 19.879 31.875 19.34 c 31.379 18.801 31.004 18.156 30.75 17.406 c\n" " 30.496 16.648 30.367 15.816 30.367 14.902 c 30.367 14.02 30.488 13.207 \n" "30.727 12.465 c 30.973 11.715 31.348 11.066 31.84 10.52 c 32.336 9.973 32.957\n" " 9.547 33.707 9.242 c 34.457 8.938 35.336 8.785 36.34 8.785 c 37.402 8.785\n" " 38.309 8.938 39.062 9.242 c 39.812 9.547 40.426 9.973 40.898 10.52 c 41.379\n" " 11.059 41.73 11.703 41.949 12.453 c 42.176 13.195 42.289 14.012 42.289 \n" "14.902 c 39.078 14.902 m 39.078 13.496 38.848 12.477 38.391 11.844 c 37.934\n" " 11.211 37.266 10.895 36.391 10.895 c 35.488 10.895 34.797 11.215 34.316\n" " 11.855 c 33.836 12.496 33.594 13.512 33.594 14.902 c 33.594 15.609 33.656\n" " 16.219 33.781 16.727 c 33.914 17.238 34.094 17.656 34.328 17.984 c 34.562\n" " 18.312 34.84 18.555 35.168 18.715 c 35.496 18.867 35.855 18.945 36.25 18.945\n" " c 36.703 18.945 37.102 18.867 37.453 18.715 c 37.809 18.555 38.109 18.312\n" " 38.348 17.984 c 38.59 17.656 38.77 17.238 38.895 16.727 c 39.02 16.215 \n" "39.082 15.609 39.082 14.902 c 47.77 21.02 m 46.867 21.02 46.172 20.777 45.684\n" " 20.289 c 45.195 19.793 44.953 19.047 44.953 18.051 c 44.953 11.078 l 43.449\n" " 11.082 l 43.449 9.008 l 45.098 9.008 l 46.059 6.23 l 47.98 6.23 l 47.98\n" " 9.008 l 50.219 9.008 l 50.219 11.082 l 47.98 11.082 l 47.98 17.223 l 47.98\n" " 17.797 48.09 18.223 48.309 18.5 c 48.527 18.77 48.867 18.906 49.324 18.902\n" " c 49.512 18.902 49.684 18.887 49.836 18.859 c 49.988 18.832 50.16 18.793\n" " 50.348 18.75 c 50.348 20.652 l 49.969 20.777 49.566 20.867 49.145 20.926\n" " c 48.723 20.992 48.258 21.023 47.758 21.023 c 68.398 20.828 m 67.031 16.895\n" " l 61.164 16.895 l 59.797 20.828 l 56.574 20.828 l 62.191 5.434 l 65.992\n" " 5.434 l 71.586 20.828 l 68.395 20.828 l 64.789 10.043 m 64.703 9.789 64.613\n" " 9.531 64.527 9.277 c 64.445 9.016 64.375 8.777 64.309 8.566 c 64.25 8.348\n" " 64.199 8.168 64.156 8.031 c 64.121 7.895 64.098 7.816 64.09 7.801 c 64.082\n" " 7.824 64.062 7.902 64.023 8.043 c 63.988 8.18 63.938 8.355 63.871 8.566\n" " c 63.812 8.777 63.738 9.016 63.652 9.277 c 63.57 9.531 63.488 9.789 63.402\n" " 10.043 c 61.883 14.469 l 66.309 14.469 l 64.789 10.043 l 84.938 14.863 \n" "m 84.938 15.758 84.848 16.586 84.664 17.344 c 84.488 18.102 84.215 18.754\n" " 83.844 19.301 c 83.473 19.848 83 20.277 82.422 20.59 c 81.855 20.895 81.18\n" " 21.047 80.402 21.047 c 80.051 21.047 79.703 21.012 79.352 20.938 c 79.008\n" " 20.863 78.68 20.75 78.367 20.586 c 78.055 20.418 77.762 20.203 77.492 19.941\n" " c 77.23 19.672 77.004 19.34 76.816 18.945 c 76.75 18.945 l 76.758 18.98\n" " 76.766 19.074 76.773 19.219 c 76.781 19.363 76.789 19.535 76.797 19.73 \n" "c 76.805 19.918 76.809 20.125 76.809 20.344 c 76.816 20.555 76.82 20.75 \n" "76.82 20.934 c 76.82 25.469 l 73.75 25.469 l 73.75 11.723 l 73.75 11.117\n" " 73.738 10.578 73.719 10.105 c 73.703 9.633 73.688 9.266 73.664 9 c 76.648\n" " 9 l 76.664 9.051 76.676 9.148 76.691 9.297 c 76.715 9.441 76.727 9.609 \n" "76.734 9.801 c 76.75 9.988 76.762 10.188 76.766 10.391 c 76.773 10.594 76.777\n" " 10.773 76.777 10.926 c 76.82 10.926 l 77.191 10.145 77.703 9.59 78.352 \n" "9.254 c 79 8.918 79.75 8.75 80.602 8.75 c 81.352 8.75 82 8.902 82.547 9.207\n" " c 83.094 9.512 83.543 9.934 83.891 10.473 c 84.246 11.012 84.512 11.656\n" " 84.676 12.406 c 84.852 13.148 84.938 13.965 84.938 14.855 c 81.734 14.855\n" " m 81.734 13.508 81.531 12.512 81.121 11.863 c 80.715 11.207 80.105 10.879\n" " 79.297 10.879 c 78.992 10.879 78.684 10.945 78.379 11.074 c 78.082 11.199\n" " 77.812 11.418 77.57 11.73 c 77.336 12.035 77.145 12.453 76.992 12.977 c\n" " 76.848 13.492 76.773 14.148 76.773 14.945 c 76.773 15.719 76.848 16.363\n" " 76.992 16.879 c 77.137 17.391 77.328 17.797 77.559 18.102 c 77.801 18.406\n" " 78.07 18.625 78.367 18.758 c 78.664 18.883 78.969 18.945 79.273 18.945 \n" "c 79.668 18.945 80.016 18.867 80.324 18.715 c 80.629 18.555 80.887 18.312\n" " 81.09 17.984 c 81.301 17.648 81.461 17.223 81.57 16.707 c 81.68 16.191 \n" "81.734 15.574 81.734 14.859 c 98.617 14.859 m 98.617 15.754 98.527 16.582\n" " 98.344 17.34 c 98.168 18.098 97.895 18.75 97.523 19.297 c 97.152 19.844\n" " 96.68 20.273 96.102 20.586 c 95.535 20.891 94.859 21.043 94.082 21.043 \n" "c 93.73 21.043 93.383 21.008 93.031 20.934 c 92.688 20.859 92.359 20.746\n" " 92.047 20.582 c 91.734 20.414 91.441 20.199 91.172 19.938 c 90.91 19.668\n" " 90.684 19.336 90.496 18.941 c 90.43 18.941 l 90.438 18.977 90.445 19.07\n" " 90.453 19.215 c 90.461 19.359 90.469 19.531 90.477 19.727 c 90.484 19.914\n" " 90.488 20.121 90.488 20.34 c 90.496 20.551 90.5 20.746 90.5 20.93 c 90.5\n" " 25.465 l 87.43 25.465 l 87.43 11.719 l 87.43 11.113 87.418 10.574 87.398\n" " 10.102 c 87.383 9.629 87.367 9.262 87.344 8.996 c 90.328 8.996 l 90.344\n" " 9.047 90.355 9.145 90.371 9.293 c 90.395 9.438 90.406 9.605 90.414 9.797\n" " c 90.43 9.984 90.441 10.184 90.445 10.387 c 90.453 10.59 90.457 10.77 90.457\n" " 10.922 c 90.5 10.922 l 90.871 10.141 91.383 9.586 92.031 9.25 c 92.68 8.914\n" " 93.43 8.746 94.281 8.746 c 95.031 8.746 95.68 8.898 96.227 9.203 c 96.773\n" " 9.508 97.223 9.93 97.57 10.469 c 97.926 11.008 98.191 11.652 98.355 12.402\n" " c 98.531 13.145 98.617 13.961 98.617 14.852 c 95.414 14.852 m 95.414 13.504\n" " 95.211 12.508 94.801 11.859 c 94.395 11.203 93.785 10.875 92.977 10.875\n" " c 92.672 10.875 92.363 10.941 92.059 11.07 c 91.762 11.195 91.492 11.414\n" " 91.25 11.727 c 91.016 12.031 90.824 12.449 90.672 12.973 c 90.527 13.488\n" " 90.453 14.145 90.453 14.941 c 90.453 15.715 90.527 16.359 90.672 16.875\n" " c 90.816 17.387 91.008 17.793 91.238 18.098 c 91.48 18.402 91.75 18.621\n" " 92.047 18.754 c 92.344 18.879 92.648 18.941 92.953 18.941 c 93.348 18.941\n" " 93.695 18.863 94.004 18.711 c 94.309 18.551 94.566 18.309 94.77 17.98 c\n" " 94.98 17.645 95.141 17.219 95.25 16.703 c 95.359 16.188 95.414 15.57 95.414\n" " 14.855 c 101.105 20.82 m 101.105 11.773 l 101.105 11.52 101.102 11.25 101.094\n" " 10.965 c 101.094 10.68 101.086 10.41 101.07 10.156 c 101.062 9.895 101.055\n" " 9.66 101.047 9.457 c 101.039 9.246 101.027 9.094 101.016 9 c 103.945 9 \n" "l 103.961 9.086 103.973 9.242 103.988 9.457 c 104.004 9.668 104.016 9.902\n" " 104.031 10.156 c 104.047 10.41 104.059 10.668 104.062 10.922 c 104.078 \n" "11.168 104.086 11.371 104.086 11.523 c 104.129 11.523 l 104.281 11.094 104.434\n" " 10.711 104.586 10.375 c 104.738 10.031 104.918 9.746 105.121 9.512 c 105.332\n" " 9.27 105.582 9.09 105.875 8.965 c 106.168 8.832 106.527 8.77 106.957 8.77\n" " c 107.141 8.77 107.316 8.789 107.492 8.824 c 107.676 8.852 107.812 8.891\n" " 107.906 8.934 c 107.906 11.5 l 107.711 11.457 107.508 11.418 107.305 11.391\n" " c 107.109 11.355 106.871 11.336 106.594 11.336 c 105.828 11.336 105.23 \n" "11.645 104.801 12.266 c 104.379 12.887 104.168 13.801 104.168 15.02 c 104.168\n" " 20.82 l 101.098 20.82 l 121.027 14.898 m 121.027 15.801 120.898 16.629 \n" "120.645 17.379 c 120.398 18.129 120.023 18.777 119.52 19.324 c 119.016 19.863\n" " 118.391 20.285 117.641 20.59 c 116.891 20.887 116.016 21.039 115.02 21.039\n" " c 114.059 21.039 113.211 20.891 112.473 20.59 c 111.738 20.293 111.117 \n" "19.871 110.613 19.332 c 110.117 18.793 109.742 18.148 109.488 17.398 c 109.234\n" " 16.641 109.105 15.809 109.105 14.895 c 109.105 14.012 109.227 13.199 109.465\n" " 12.457 c 109.711 11.707 110.086 11.059 110.578 10.512 c 111.074 9.965 111.695\n" " 9.539 112.445 9.234 c 113.195 8.93 114.074 8.777 115.078 8.777 c 116.141\n" " 8.777 117.047 8.93 117.801 9.234 c 118.551 9.539 119.164 9.965 119.637 \n" "10.512 c 120.117 11.051 120.469 11.695 120.688 12.445 c 120.914 13.188 121.027\n" " 14.004 121.027 14.895 c 117.816 14.895 m 117.816 13.488 117.586 12.469 \n" "117.129 11.836 c 116.672 11.203 116.004 10.887 115.129 10.887 c 114.227 \n" "10.887 113.535 11.207 113.055 11.848 c 112.574 12.488 112.332 13.504 112.332\n" " 14.895 c 112.332 15.602 112.395 16.211 112.52 16.719 c 112.652 17.23 112.832\n" " 17.648 113.066 17.977 c 113.301 18.305 113.578 18.547 113.906 18.707 c \n" "114.234 18.859 114.594 18.938 114.988 18.938 c 115.441 18.938 115.84 18.859\n" " 116.191 18.707 c 116.547 18.547 116.848 18.305 117.086 17.977 c 117.328\n" " 17.648 117.508 17.23 117.633 16.719 c 117.758 16.207 117.82 15.602 117.82\n" " 14.895 c 129.906 20.816 m 126.234 20.816 l 122.004 8.992 l 125.25 8.992\n" " l 127.316 15.602 l 127.383 15.82 127.449 16.055 127.523 16.301 c 127.598\n" " 16.543 127.664 16.781 127.73 17.023 c 127.797 17.266 127.859 17.496 127.918\n" " 17.723 c 127.984 17.949 128.043 18.152 128.094 18.336 c 128.137 18.16 128.191\n" " 17.965 128.258 17.746 c 128.324 17.52 128.391 17.289 128.453 17.047 c 128.527\n" " 16.805 128.598 16.566 128.672 16.324 c 128.754 16.082 128.828 15.855 128.902\n" " 15.637 c 131.055 8.992 l 134.266 8.992 l 129.906 20.816 l 140.777 21.035\n" " m 139.926 21.035 139.156 20.91 138.473 20.664 c 137.797 20.41 137.215 20.027\n" " 136.734 19.516 c 136.254 19 135.887 18.355 135.629 17.582 c 135.375 16.801\n" " 135.246 15.891 135.246 14.852 c 135.246 13.723 135.395 12.773 135.695 12\n" " c 136 11.227 136.41 10.605 136.918 10.133 c 137.434 9.652 138.027 9.305\n" " 138.699 9.094 c 139.371 8.883 140.074 8.777 140.82 8.777 c 141.754 8.777\n" " 142.547 8.941 143.203 9.27 c 143.867 9.59 144.41 10.043 144.832 10.625 \n" "c 145.254 11.207 145.562 11.906 145.762 12.723 c 145.957 13.531 146.059 \n" "14.426 146.059 15.41 c 146.059 15.496 l 138.477 15.496 l 138.477 15.992 \n" "138.52 16.453 138.609 16.883 c 138.695 17.305 138.84 17.672 139.035 17.988\n" " c 139.23 18.293 139.488 18.539 139.801 18.719 c 140.113 18.895 140.492 \n" "18.98 140.938 18.98 c 141.477 18.98 141.918 18.867 142.258 18.641 c 142.602\n" " 18.406 142.844 18.051 142.988 17.57 c 145.883 17.82 l 145.75 18.156 145.566\n" " 18.512 145.324 18.891 c 145.09 19.27 144.777 19.617 144.383 19.941 c 143.988\n" " 20.254 143.496 20.516 142.906 20.727 c 142.324 20.93 141.613 21.031 140.777\n" " 21.031 c 140.777 10.707 m 140.465 10.707 140.168 10.762 139.891 10.871 \n" "c 139.621 10.973 139.383 11.141 139.18 11.375 c 138.984 11.602 138.824 11.895\n" " 138.699 12.262 c 138.574 12.625 138.508 13.062 138.492 13.574 c 143.082\n" " 13.574 l 143.023 12.621 142.793 11.906 142.395 11.434 c 141.992 10.953 \n" "141.453 10.711 140.777 10.711 c 156.055 20.816 m 156.039 20.758 156.023 \n" "20.656 156 20.512 c 155.984 20.359 155.969 20.188 155.945 20 c 155.93 19.812\n" " 155.918 19.617 155.902 19.422 c 155.895 19.227 155.891 19.051 155.891 18.898\n" " c 155.848 18.898 l 155.492 19.664 154.992 20.215 154.352 20.547 c 153.719\n" " 20.875 152.957 21.039 152.066 21.039 c 151.324 21.039 150.68 20.887 150.133\n" " 20.582 c 149.594 20.277 149.145 19.852 148.789 19.305 c 148.438 18.758 \n" "148.176 18.113 148.004 17.371 c 147.836 16.621 147.754 15.805 147.754 14.922\n" " c 147.754 14.027 147.84 13.203 148.016 12.453 c 148.199 11.703 148.473 \n" "11.059 148.848 10.52 c 149.219 9.973 149.688 9.547 150.258 9.242 c 150.832\n" " 8.938 151.516 8.785 152.301 8.785 c 152.688 8.785 153.059 8.824 153.414\n" " 8.906 c 153.77 8.988 154.102 9.109 154.41 9.277 c 154.715 9.445 154.992\n" " 9.66 155.242 9.922 c 155.488 10.184 155.699 10.5 155.875 10.871 c 155.898\n" " 10.871 l 155.898 10.797 155.895 10.691 155.887 10.555 c 155.887 10.41 155.887\n" " 10.246 155.887 10.062 c 155.887 9.879 155.883 9.691 155.875 9.496 c 155.875\n" " 9.301 155.875 9.109 155.875 8.93 c 155.875 4.613 l 158.945 4.613 l 158.945\n" " 18.25 l 158.945 18.824 158.957 19.34 158.977 19.789 c 159 20.234 159.016\n" " 20.578 159.031 20.828 c 156.059 20.828 l 155.918 14.852 m 155.918 14.07\n" " 155.844 13.426 155.699 12.918 c 155.555 12.402 155.359 11.992 155.121 11.695\n" " c 154.887 11.391 154.617 11.18 154.312 11.062 c 154.016 10.938 153.707 \n" "10.875 153.395 10.875 c 153 10.875 152.652 10.953 152.344 11.105 c 152.047\n" " 11.258 151.789 11.5 151.578 11.828 c 151.375 12.156 151.219 12.574 151.109\n" " 13.086 c 151.008 13.598 150.957 14.211 150.957 14.934 c 150.957 17.613 \n" "151.762 18.953 153.371 18.953 c 153.676 18.953 153.984 18.887 154.289 18.758\n" " c 154.594 18.625 154.867 18.406 155.109 18.09 c 155.352 17.777 155.543 \n" "17.359 155.688 16.832 c 155.84 16.301 155.918 15.641 155.918 14.855 c S Q\n" "Q q\n" "0.74902 0 0 rg /a1 gs\n" "1 1 1 RG 0.797243 w\n" "1 J\n" "1 j\n" "[] 0.0 d\n" "4 M q 1 0 0 1 0 0 cm\n" "3.398 0.398 m 1.77 0.398 0.398 1.77 0.398 3.398 c 0.398 23.086 l 0.398 \n" "24.715 1.77 26.086 3.398 26.086 c 167.109 26.086 l 168.738 26.086 170.109\n" " 24.715 170.109 23.086 c 170.109 3.398 l 170.109 1.77 168.738 0.398 167.109\n" " 0.398 c h\n" "3.867 3.844 m 166.664 3.844 l 166.664 22.617 l 3.867 22.617 l h\n" "3.867 3.844 m B Q\n" "Q\n"; static Dict *getNotApprovedStampExtGStateDict(PDFDoc *doc) { Dict *a0Dict = new Dict(doc->getXRef()); a0Dict->add("CA", Object(0.588235)); a0Dict->add("ca", Object(0.588235)); Dict *a1Dict = new Dict(doc->getXRef()); a1Dict->add("CA", Object(1)); a1Dict->add("ca", Object(1)); Dict *extGStateDict = new Dict(doc->getXRef()); extGStateDict->add("a0", Object(a0Dict)); extGStateDict->add("a1", Object(a1Dict)); return extGStateDict; } #endif poppler-24.02.0/poppler/annot_stamp_not_for_public_release.h000066400000000000000000001150241455701731300243120ustar00rootroot00000000000000//======================================================================== // // annot_stamp_not_for_public_release.h // // Copyright (C) 2021 Mahmoud Ahmed Khalil // Copyright (C) 2021 Albert Astals Cid // // Mechanically extracted from an SVG created for Okular by Eugene Trounev eugene.trounev@gmail.com // // Licensed under GPLv2 or later // //======================================================================== #ifndef ANNOT_STAMP_NOT_FOR_PUBLIC_RELEASE_H #define ANNOT_STAMP_NOT_FOR_PUBLIC_RELEASE_H #include "PDFDoc.h" #include "Dict.h" #include "Object.h" static const double ANNOT_STAMP_NOT_FOR_PUBLIC_RELEASE_WIDTH = 268.008179; static const double ANNOT_STAMP_NOT_FOR_PUBLIC_RELEASE_HEIGHT = 26.484743; static const char *ANNOT_STAMP_NOT_FOR_PUBLIC_RELEASE = "1 0 0 -1 0 26.484741 cm\n" "q\n" "1 1 1 rg /a0 gs\n" "3.406 2.129 m 264.617 2.129 l 265.316 2.129 265.887 2.828 265.887 3.398\n" " c 265.887 23.09 l 265.887 23.789 265.32 24.359 264.617 24.359 c 3.406 24.359\n" " l 2.707 24.359 2.137 23.66 2.137 23.09 c 2.137 3.398 l 2.137 2.699 2.703\n" " 2.129 3.406 2.129 c h\n" "3.406 2.129 m f\n" "0.74902 0 0 rg /a1 gs\n" "1 1 1 RG 0.531496 w\n" "0 J\n" "0 j\n" "[] 0.0 d\n" "4 M q 1 0 0 1 0 0 cm\n" "24.195 20.828 m 17.484 8.973 l 17.52 9.293 17.555 9.613 17.582 9.934 c \n" "17.609 10.211 17.633 10.508 17.648 10.828 c 17.672 11.148 17.68 11.453 17.68\n" " 11.746 c 17.68 20.828 l 14.82 20.828 l 14.82 5.434 l 18.504 5.434 l 25.312\n" " 17.387 l 25.277 17.082 25.242 16.762 25.215 16.426 c 25.188 16.141 25.16\n" " 15.82 25.137 15.465 c 25.121 15.109 25.113 14.746 25.113 14.383 c 25.113\n" " 5.434 l 27.977 5.434 l 27.977 20.828 l 24.195 20.828 l 42.289 14.906 m \n" "42.289 15.809 42.16 16.637 41.906 17.387 c 41.66 18.137 41.285 18.785 40.781\n" " 19.332 c 40.277 19.871 39.652 20.293 38.902 20.598 c 38.152 20.895 37.277\n" " 21.047 36.281 21.047 c 35.32 21.047 34.473 20.898 33.734 20.598 c 33 20.301\n" " 32.379 19.879 31.875 19.34 c 31.379 18.801 31.004 18.156 30.75 17.406 c\n" " 30.496 16.648 30.367 15.816 30.367 14.902 c 30.367 14.02 30.488 13.207 \n" "30.727 12.465 c 30.973 11.715 31.348 11.066 31.84 10.52 c 32.336 9.973 32.957\n" " 9.547 33.707 9.242 c 34.457 8.938 35.336 8.785 36.34 8.785 c 37.402 8.785\n" " 38.309 8.938 39.062 9.242 c 39.812 9.547 40.426 9.973 40.898 10.52 c 41.379\n" " 11.059 41.73 11.703 41.949 12.453 c 42.176 13.195 42.289 14.012 42.289 \n" "14.902 c 39.078 14.902 m 39.078 13.496 38.848 12.477 38.391 11.844 c 37.934\n" " 11.211 37.266 10.895 36.391 10.895 c 35.488 10.895 34.797 11.215 34.316\n" " 11.855 c 33.836 12.496 33.594 13.512 33.594 14.902 c 33.594 15.609 33.656\n" " 16.219 33.781 16.727 c 33.914 17.238 34.094 17.656 34.328 17.984 c 34.562\n" " 18.312 34.84 18.555 35.168 18.715 c 35.496 18.867 35.855 18.945 36.25 18.945\n" " c 36.703 18.945 37.102 18.867 37.453 18.715 c 37.809 18.555 38.109 18.312\n" " 38.348 17.984 c 38.59 17.656 38.77 17.238 38.895 16.727 c 39.02 16.215 \n" "39.082 15.609 39.082 14.902 c 47.77 21.02 m 46.867 21.02 46.172 20.777 45.684\n" " 20.289 c 45.195 19.793 44.953 19.047 44.953 18.051 c 44.953 11.078 l 43.449\n" " 11.082 l 43.449 9.008 l 45.098 9.008 l 46.059 6.23 l 47.98 6.23 l 47.98\n" " 9.008 l 50.219 9.008 l 50.219 11.082 l 47.98 11.082 l 47.98 17.223 l 47.98\n" " 17.797 48.09 18.223 48.309 18.5 c 48.527 18.77 48.867 18.906 49.324 18.902\n" " c 49.512 18.902 49.684 18.887 49.836 18.859 c 49.988 18.832 50.16 18.793\n" " 50.348 18.75 c 50.348 20.652 l 49.969 20.777 49.566 20.867 49.145 20.926\n" " c 48.723 20.992 48.258 21.023 47.758 21.023 c 61.57 7.922 m 61.57 12.688\n" " l 69.449 12.688 l 69.449 15.18 l 61.574 15.18 l 61.574 20.828 l 58.352 \n" "20.828 l 58.352 5.434 l 69.703 5.434 l 69.703 7.926 l 61.574 7.926 l 83.328\n" " 14.906 m 83.328 15.809 83.199 16.637 82.945 17.387 c 82.699 18.137 82.324\n" " 18.785 81.82 19.332 c 81.316 19.871 80.691 20.293 79.941 20.598 c 79.191\n" " 20.895 78.316 21.047 77.32 21.047 c 76.359 21.047 75.512 20.898 74.773 \n" "20.598 c 74.039 20.301 73.418 19.879 72.914 19.34 c 72.418 18.801 72.043\n" " 18.156 71.789 17.406 c 71.535 16.648 71.406 15.816 71.406 14.902 c 71.406\n" " 14.02 71.527 13.207 71.766 12.465 c 72.012 11.715 72.387 11.066 72.879 \n" "10.52 c 73.375 9.973 73.996 9.547 74.746 9.242 c 75.496 8.938 76.375 8.785\n" " 77.379 8.785 c 78.441 8.785 79.348 8.938 80.102 9.242 c 80.852 9.547 81.465\n" " 9.973 81.938 10.52 c 82.418 11.059 82.77 11.703 82.988 12.453 c 83.215 \n" "13.195 83.328 14.012 83.328 14.902 c 80.117 14.902 m 80.117 13.496 79.887\n" " 12.477 79.43 11.844 c 78.973 11.211 78.305 10.895 77.43 10.895 c 76.527\n" " 10.895 75.836 11.215 75.355 11.855 c 74.875 12.496 74.633 13.512 74.633\n" " 14.902 c 74.633 15.609 74.695 16.219 74.82 16.727 c 74.953 17.238 75.133\n" " 17.656 75.367 17.984 c 75.602 18.312 75.879 18.555 76.207 18.715 c 76.535\n" " 18.867 76.895 18.945 77.289 18.945 c 77.742 18.945 78.141 18.867 78.492\n" " 18.715 c 78.848 18.555 79.148 18.312 79.387 17.984 c 79.629 17.656 79.809\n" " 17.238 79.934 16.727 c 80.059 16.215 80.121 15.609 80.121 14.902 c 85.781\n" " 20.824 m 85.781 11.777 l 85.781 11.523 85.777 11.254 85.77 10.969 c 85.77\n" " 10.684 85.762 10.414 85.746 10.16 c 85.738 9.898 85.73 9.664 85.723 9.461\n" " c 85.715 9.25 85.703 9.098 85.691 9.004 c 88.621 9.004 l 88.637 9.09 88.648\n" " 9.246 88.664 9.461 c 88.68 9.672 88.691 9.906 88.707 10.16 c 88.723 10.414\n" " 88.734 10.672 88.738 10.926 c 88.754 11.172 88.762 11.375 88.762 11.527\n" " c 88.805 11.527 l 88.957 11.098 89.109 10.715 89.262 10.379 c 89.414 10.035\n" " 89.594 9.75 89.797 9.516 c 90.008 9.273 90.258 9.094 90.551 8.969 c 90.844\n" " 8.836 91.203 8.773 91.633 8.773 c 91.816 8.773 91.992 8.793 92.168 8.828\n" " c 92.352 8.855 92.488 8.895 92.582 8.938 c 92.582 11.504 l 92.387 11.461\n" " 92.184 11.422 91.98 11.395 c 91.785 11.359 91.547 11.34 91.27 11.34 c 90.504\n" " 11.34 89.906 11.648 89.477 12.27 c 89.055 12.891 88.844 13.805 88.844 15.023\n" " c 88.844 20.824 l 85.773 20.824 l 113.277 10.301 m 113.277 10.965 113.164\n" " 11.605 112.938 12.223 c 112.719 12.836 112.379 13.379 111.922 13.852 c \n" "111.465 14.324 110.879 14.703 110.172 14.988 c 109.465 15.266 108.629 15.402\n" " 107.66 15.402 c 103.836 15.402 l 103.836 20.82 l 100.613 20.82 l 100.613\n" " 5.426 l 107.531 5.426 l 108.516 5.426 109.367 5.547 110.09 5.785 c 110.812\n" " 6.02 111.41 6.352 111.883 6.781 c 112.355 7.211 112.707 7.727 112.934 8.32\n" " c 113.168 8.918 113.285 9.578 113.285 10.297 c 110.039 10.352 m 110.039\n" " 9.559 109.797 8.957 109.316 8.547 c 108.844 8.133 108.129 7.926 107.176\n" " 7.926 c 103.844 7.926 l 103.844 12.918 l 107.266 12.918 l 107.746 12.918\n" " 108.156 12.855 108.5 12.73 c 108.852 12.605 109.137 12.434 109.363 12.207\n" " c 109.598 11.98 109.766 11.711 109.875 11.398 c 109.984 11.078 110.039 \n" "10.727 110.039 10.348 c 118.527 8.992 m 118.527 15.625 l 118.527 16.082 \n" "118.562 16.508 118.637 16.891 c 118.711 17.27 118.828 17.598 118.996 17.875\n" " c 119.164 18.145 119.379 18.355 119.641 18.508 c 119.91 18.66 120.238 18.738\n" " 120.625 18.738 c 120.996 18.738 121.332 18.656 121.629 18.496 c 121.934\n" " 18.328 122.195 18.094 122.414 17.797 c 122.633 17.492 122.801 17.129 122.918\n" " 16.715 c 123.043 16.293 123.105 15.828 123.105 15.328 c 123.105 8.992 l\n" " 126.176 8.992 l 126.176 18.172 l 126.176 18.418 126.176 18.684 126.176 \n" "18.957 c 126.184 19.227 126.191 19.484 126.199 19.734 c 126.215 19.977 126.227\n" " 20.191 126.23 20.391 c 126.246 20.578 126.258 20.723 126.262 20.816 c 123.332\n" " 20.816 l 123.324 20.73 123.309 20.59 123.289 20.402 c 123.273 20.207 123.262\n" " 19.992 123.246 19.758 c 123.238 19.523 123.227 19.293 123.215 19.059 c \n" "123.207 18.824 123.203 18.629 123.203 18.469 c 123.148 18.469 l 122.742 \n" "19.363 122.219 20.016 121.586 20.426 c 120.961 20.832 120.211 21.039 119.348\n" " 21.039 c 118.641 21.039 118.039 20.922 117.543 20.688 c 117.047 20.453 \n" "116.645 20.137 116.332 19.738 c 116.027 19.332 115.805 18.848 115.664 18.297\n" " c 115.527 17.742 115.457 17.145 115.457 16.504 c 115.457 8.996 l 118.527\n" " 8.996 l 140.5 14.863 m 140.5 15.758 140.41 16.586 140.227 17.344 c 140.051\n" " 18.094 139.777 18.746 139.406 19.301 c 139.035 19.848 138.562 20.273 137.984\n" " 20.578 c 137.418 20.883 136.742 21.035 135.965 21.035 c 135.613 21.035 \n" "135.266 21 134.914 20.926 c 134.562 20.852 134.234 20.734 133.918 20.566\n" " c 133.605 20.398 133.316 20.18 133.055 19.91 c 132.793 19.641 132.566 19.309\n" " 132.379 18.914 c 132.355 18.914 l 132.355 19.066 132.348 19.242 132.332\n" " 19.438 c 132.324 19.633 132.312 19.828 132.301 20.016 c 132.285 20.199 \n" "132.27 20.363 132.246 20.508 c 132.23 20.652 132.219 20.754 132.203 20.812\n" " c 129.219 20.812 l 129.242 20.559 129.258 20.195 129.273 19.73 c 129.297\n" " 19.258 129.305 18.719 129.305 18.113 c 129.305 4.598 l 132.375 4.598 l \n" "132.375 9.121 l 132.375 9.355 132.371 9.582 132.363 9.809 c 132.363 10.027\n" " 132.359 10.23 132.352 10.422 c 132.344 10.641 132.336 10.848 132.328 11.043\n" " c 132.371 11.043 l 132.742 10.227 133.254 9.645 133.902 9.293 c 134.551\n" " 8.941 135.301 8.77 136.152 8.77 c 136.91 8.77 137.562 8.922 138.109 9.227\n" " c 138.656 9.523 139.105 9.949 139.453 10.492 c 139.809 11.031 140.074 11.672\n" " 140.238 12.414 c 140.406 13.156 140.488 13.969 140.488 14.852 c 137.285\n" " 14.852 m 137.285 13.512 137.09 12.516 136.695 11.867 c 136.301 11.219 135.695\n" " 10.895 134.871 10.895 c 134.559 10.895 134.25 10.957 133.941 11.082 c 133.637\n" " 11.207 133.363 11.426 133.121 11.738 c 132.879 12.043 132.688 12.461 132.543\n" " 12.984 c 132.398 13.5 132.324 14.156 132.324 14.953 c 132.324 15.727 132.398\n" " 16.367 132.543 16.875 c 132.688 17.387 132.879 17.793 133.109 18.098 c \n" "133.344 18.402 133.613 18.617 133.918 18.742 c 134.223 18.867 134.535 18.93\n" " 134.848 18.93 c 135.629 18.93 136.227 18.609 136.652 17.969 c 137.074 17.32\n" " 137.285 16.281 137.285 14.855 c 142.977 20.812 m 142.977 4.598 l 146.047\n" " 4.598 l 146.047 20.812 l 142.977 20.812 l 149.184 6.859 m 149.184 4.598\n" " l 152.254 4.598 l 152.254 6.859 l 149.184 6.859 l 149.184 20.812 m 149.184\n" " 8.988 l 152.254 8.988 l 152.254 20.812 l 149.184 20.812 l 160.316 21.031\n" " m 159.375 21.031 158.555 20.887 157.848 20.594 c 157.148 20.301 156.566\n" " 19.891 156.098 19.359 c 155.633 18.828 155.281 18.191 155.047 17.445 c \n" "154.812 16.695 154.695 15.867 154.695 14.965 c 154.695 13.98 154.824 13.105\n" " 155.078 12.344 c 155.332 11.57 155.699 10.922 156.184 10.398 c 156.664 \n" "9.867 157.254 9.461 157.953 9.188 c 158.66 8.91 159.461 8.773 160.355 8.773\n" " c 161.121 8.773 161.797 8.875 162.387 9.078 c 162.984 9.281 163.496 9.562\n" " 163.926 9.918 c 164.355 10.27 164.703 10.684 164.965 11.164 c 165.234 11.645\n" " 165.422 12.16 165.523 12.715 c 162.43 12.867 l 162.344 12.262 162.125 11.781\n" " 161.773 11.426 c 161.422 11.062 160.93 10.879 160.289 10.879 c 159.465 \n" "10.879 158.863 11.219 158.484 11.895 c 158.105 12.57 157.918 13.555 157.918\n" " 14.844 c 157.918 17.566 158.723 18.93 160.332 18.93 c 160.914 18.93 161.402\n" " 18.746 161.797 18.383 c 162.191 18.012 162.434 17.461 162.527 16.734 c \n" "165.609 16.875 l 165.535 17.422 165.367 17.945 165.105 18.449 c 164.852 \n" "18.945 164.5 19.387 164.055 19.77 c 163.617 20.148 163.086 20.453 162.461\n" " 20.688 c 161.836 20.914 161.117 21.027 160.309 21.027 c 184.555 20.809 \n" "m 180.98 14.961 l 177.199 14.961 l 177.199 20.809 l 173.977 20.809 l 173.977\n" " 5.414 l 181.668 5.414 l 182.629 5.414 183.469 5.523 184.18 5.742 c 184.902\n" " 5.953 185.5 6.258 185.984 6.66 c 186.465 7.055 186.82 7.535 187.055 8.102\n" " c 187.297 8.664 187.414 9.297 187.414 10.004 c 187.414 10.578 187.328 11.109\n" " 187.152 11.59 c 186.984 12.062 186.746 12.484 186.441 12.855 c 186.145 \n" "13.227 185.785 13.539 185.371 13.797 c 184.957 14.043 184.504 14.227 184.016\n" " 14.344 c 188.18 20.812 l 184.551 20.812 l 184.168 10.137 m 184.168 9.387\n" " 183.922 8.828 183.426 8.465 c 182.938 8.102 182.238 7.918 181.328 7.918\n" " c 177.199 7.918 l 177.199 12.465 l 181.418 12.465 l 181.898 12.465 182.309\n" " 12.41 182.652 12.301 c 183.004 12.184 183.285 12.023 183.504 11.82 c 183.73\n" " 11.609 183.898 11.363 184.008 11.078 c 184.117 10.793 184.172 10.48 184.172\n" " 10.137 c 195.055 21.031 m 194.203 21.031 193.434 20.906 192.75 20.66 c \n" "192.074 20.406 191.492 20.023 191.012 19.512 c 190.531 18.996 190.164 18.352\n" " 189.906 17.578 c 189.652 16.797 189.523 15.887 189.523 14.848 c 189.523\n" " 13.719 189.672 12.77 189.973 11.996 c 190.277 11.223 190.688 10.602 191.195\n" " 10.129 c 191.711 9.648 192.305 9.301 192.977 9.09 c 193.648 8.879 194.352\n" " 8.773 195.098 8.773 c 196.031 8.773 196.824 8.938 197.48 9.266 c 198.145\n" " 9.586 198.688 10.039 199.109 10.621 c 199.531 11.203 199.84 11.902 200.039\n" " 12.719 c 200.234 13.527 200.336 14.422 200.336 15.406 c 200.336 15.492 \n" "l 192.754 15.492 l 192.754 15.988 192.797 16.449 192.887 16.879 c 192.973\n" " 17.301 193.117 17.668 193.312 17.984 c 193.508 18.289 193.766 18.535 194.078\n" " 18.715 c 194.391 18.891 194.77 18.977 195.215 18.977 c 195.754 18.977 196.195\n" " 18.863 196.535 18.637 c 196.879 18.402 197.121 18.047 197.266 17.566 c \n" "200.16 17.816 l 200.027 18.152 199.844 18.508 199.602 18.887 c 199.367 19.266\n" " 199.055 19.613 198.66 19.938 c 198.266 20.25 197.773 20.512 197.184 20.723\n" " c 196.602 20.926 195.891 21.027 195.055 21.027 c 195.055 10.703 m 194.742\n" " 10.703 194.445 10.758 194.168 10.867 c 193.898 10.969 193.66 11.137 193.457\n" " 11.371 c 193.262 11.598 193.102 11.891 192.977 12.258 c 192.852 12.621 \n" "192.785 13.059 192.77 13.57 c 197.359 13.57 l 197.301 12.617 197.07 11.902\n" " 196.672 11.43 c 196.27 10.949 195.73 10.707 195.055 10.707 c 202.672 20.812\n" " m 202.672 4.598 l 205.742 4.598 l 205.742 20.812 l 202.672 20.812 l 213.719\n" " 21.031 m 212.867 21.031 212.098 20.906 211.414 20.66 c 210.738 20.406 210.156\n" " 20.023 209.676 19.512 c 209.195 18.996 208.828 18.352 208.57 17.578 c 208.316\n" " 16.797 208.188 15.887 208.188 14.848 c 208.188 13.719 208.336 12.77 208.637\n" " 11.996 c 208.941 11.223 209.352 10.602 209.859 10.129 c 210.375 9.648 210.969\n" " 9.301 211.641 9.09 c 212.312 8.879 213.016 8.773 213.762 8.773 c 214.695\n" " 8.773 215.488 8.938 216.145 9.266 c 216.809 9.586 217.352 10.039 217.773\n" " 10.621 c 218.195 11.203 218.504 11.902 218.703 12.719 c 218.898 13.527 \n" "219 14.422 219 15.406 c 219 15.492 l 211.418 15.492 l 211.418 15.988 211.461\n" " 16.449 211.551 16.879 c 211.637 17.301 211.781 17.668 211.977 17.984 c \n" "212.172 18.289 212.43 18.535 212.742 18.715 c 213.055 18.891 213.434 18.977\n" " 213.879 18.977 c 214.418 18.977 214.859 18.863 215.199 18.637 c 215.543\n" " 18.402 215.785 18.047 215.93 17.566 c 218.824 17.816 l 218.691 18.152 218.508\n" " 18.508 218.266 18.887 c 218.031 19.266 217.719 19.613 217.324 19.938 c \n" "216.93 20.25 216.438 20.512 215.848 20.723 c 215.266 20.926 214.555 21.027\n" " 213.719 21.027 c 213.719 10.703 m 213.406 10.703 213.109 10.758 212.832\n" " 10.867 c 212.562 10.969 212.324 11.137 212.121 11.371 c 211.926 11.598 \n" "211.766 11.891 211.641 12.258 c 211.516 12.621 211.449 13.059 211.434 13.57\n" " c 216.023 13.57 l 215.965 12.617 215.734 11.902 215.336 11.43 c 214.934\n" " 10.949 214.395 10.707 213.719 10.707 c 224.066 21.031 m 223.5 21.031 222.988\n" " 20.953 222.535 20.801 c 222.09 20.641 221.711 20.41 221.398 20.113 c 221.086\n" " 19.809 220.844 19.434 220.676 18.988 c 220.508 18.543 220.426 18.039 220.426\n" " 17.469 c 220.426 16.77 220.547 16.184 220.785 15.711 c 221.031 15.23 221.367\n" " 14.844 221.789 14.551 c 222.211 14.254 222.707 14.039 223.273 13.906 c \n" "223.84 13.77 224.445 13.695 225.086 13.688 c 227.633 13.645 l 227.633 13.043\n" " l 227.633 12.613 227.594 12.258 227.512 11.973 c 227.438 11.68 227.328 \n" "11.445 227.184 11.262 c 227.039 11.078 226.855 10.953 226.637 10.879 c 226.426\n" " 10.797 226.18 10.758 225.906 10.758 c 225.652 10.758 225.422 10.785 225.219\n" " 10.836 c 225.023 10.887 224.852 10.977 224.707 11.109 c 224.562 11.234 \n" "224.441 11.406 224.348 11.621 c 224.262 11.832 224.199 12.102 224.16 12.43\n" " c 220.957 12.277 l 221.043 11.762 221.203 11.289 221.438 10.867 c 221.672\n" " 10.438 221.992 10.066 222.398 9.754 c 222.812 9.441 223.32 9.199 223.918\n" " 9.031 c 224.523 8.855 225.23 8.77 226.039 8.77 c 226.773 8.77 227.43 8.859\n" " 228.008 9.043 c 228.582 9.227 229.07 9.5 229.473 9.863 c 229.875 10.219\n" " 230.18 10.66 230.391 11.184 c 230.602 11.707 230.707 12.316 230.707 13.008\n" " c 230.707 17.312 l 230.707 17.59 230.719 17.836 230.738 18.055 c 230.766\n" " 18.273 230.816 18.457 230.879 18.613 c 230.953 18.758 231.051 18.871 231.176\n" " 18.953 c 231.309 19.027 231.473 19.062 231.68 19.062 c 231.914 19.062 232.137\n" " 19.039 232.355 18.996 c 232.355 20.656 l 232.172 20.699 232.008 20.738 \n" "231.863 20.777 c 231.719 20.812 231.57 20.844 231.426 20.863 c 231.281 20.887\n" " 231.125 20.902 230.957 20.918 c 230.797 20.934 230.605 20.941 230.391 20.941\n" " c 229.617 20.941 229.047 20.754 228.676 20.375 c 228.312 19.996 228.094\n" " 19.438 228.02 18.703 c 227.953 18.703 l 227.547 19.438 227.02 20.012 226.379\n" " 20.418 c 225.746 20.824 224.973 21.031 224.062 21.031 c 227.637 15.34 m\n" " 226.062 15.363 l 225.734 15.379 225.422 15.406 225.121 15.449 c 224.828\n" " 15.484 224.57 15.57 224.344 15.699 c 224.125 15.824 223.949 16.004 223.82\n" " 16.246 c 223.688 16.488 223.625 16.812 223.625 17.23 c 223.625 17.793 223.754\n" " 18.211 224.008 18.488 c 224.27 18.758 224.617 18.895 225.047 18.891 c 225.441\n" " 18.891 225.797 18.809 226.117 18.641 c 226.438 18.473 226.707 18.254 226.926\n" " 17.984 c 227.152 17.707 227.328 17.391 227.449 17.035 c 227.574 16.68 227.637\n" " 16.312 227.637 15.941 c 227.637 15.34 l 243.754 17.359 m 243.754 17.926\n" " 243.633 18.438 243.395 18.891 c 243.16 19.344 242.82 19.727 242.367 20.051\n" " c 241.914 20.363 241.363 20.609 240.707 20.781 c 240.051 20.949 239.305\n" " 21.031 238.469 21.031 c 237.719 21.031 237.039 20.977 236.438 20.867 c \n" "235.832 20.758 235.301 20.578 234.844 20.332 c 234.391 20.078 234.012 19.75\n" " 233.707 19.348 c 233.402 18.945 233.172 18.453 233.02 17.863 c 235.719 \n" "17.461 l 235.805 17.789 235.926 18.055 236.078 18.258 c 236.23 18.461 236.422\n" " 18.617 236.645 18.727 c 236.871 18.836 237.133 18.91 237.43 18.945 c 237.734\n" " 18.98 238.082 19 238.469 19 c 238.82 19 239.141 18.98 239.441 18.945 c \n" "239.746 18.902 240.008 18.832 240.227 18.738 c 240.453 18.637 240.629 18.496\n" " 240.75 18.324 c 240.875 18.141 240.938 17.918 240.938 17.648 c 240.938 \n" "17.344 240.848 17.102 240.664 16.926 c 240.488 16.742 240.246 16.598 239.934\n" " 16.488 c 239.629 16.371 239.262 16.273 238.84 16.191 c 238.426 16.105 237.98\n" " 16.004 237.508 15.895 c 237.012 15.785 236.523 15.652 236.043 15.5 c 235.562\n" " 15.348 235.133 15.141 234.754 14.879 c 234.375 14.617 234.07 14.285 233.836\n" " 13.883 c 233.602 13.477 233.484 12.961 233.484 12.344 c 233.484 11.781 \n" "233.594 11.285 233.812 10.848 c 234.031 10.402 234.352 10.027 234.773 9.723\n" " c 235.195 9.41 235.715 9.172 236.336 9.012 c 236.961 8.844 237.68 8.762\n" " 238.488 8.762 c 239.129 8.762 239.727 8.824 240.281 8.949 c 240.836 9.066\n" " 241.332 9.254 241.766 9.516 c 242.203 9.77 242.566 10.098 242.859 10.5 \n" "c 243.156 10.902 243.367 11.387 243.48 11.953 c 240.758 12.238 l 240.707\n" " 11.953 240.617 11.723 240.484 11.539 c 240.352 11.352 240.188 11.199 239.992\n" " 11.09 c 239.805 10.98 239.582 10.906 239.324 10.871 c 239.07 10.828 238.789\n" " 10.805 238.484 10.805 c 237.758 10.805 237.211 10.898 236.844 11.09 c 236.48\n" " 11.273 236.297 11.582 236.297 12.02 c 236.297 12.289 236.371 12.504 236.516\n" " 12.664 c 236.668 12.824 236.879 12.961 237.148 13.066 c 237.426 13.168 \n" "237.75 13.258 238.121 13.34 c 238.5 13.414 238.91 13.504 239.355 13.613 \n" "c 239.902 13.73 240.438 13.867 240.961 14.027 c 241.492 14.18 241.961 14.395\n" " 242.371 14.672 c 242.785 14.941 243.117 15.293 243.367 15.723 c 243.621\n" " 16.152 243.75 16.699 243.75 17.363 c 251.082 21.035 m 250.23 21.035 249.461\n" " 20.91 248.777 20.664 c 248.102 20.41 247.52 20.027 247.039 19.516 c 246.559\n" " 19 246.191 18.355 245.934 17.582 c 245.68 16.801 245.551 15.891 245.551\n" " 14.852 c 245.551 13.723 245.699 12.773 246 12 c 246.305 11.227 246.715 \n" "10.605 247.223 10.133 c 247.738 9.652 248.332 9.305 249.004 9.094 c 249.676\n" " 8.883 250.379 8.777 251.125 8.777 c 252.059 8.777 252.852 8.941 253.508\n" " 9.27 c 254.172 9.59 254.715 10.043 255.137 10.625 c 255.559 11.207 255.867\n" " 11.906 256.066 12.723 c 256.262 13.531 256.363 14.426 256.363 15.41 c 256.363\n" " 15.496 l 248.781 15.496 l 248.781 15.992 248.824 16.453 248.914 16.883 \n" "c 249 17.305 249.145 17.672 249.34 17.988 c 249.535 18.293 249.793 18.539\n" " 250.105 18.719 c 250.418 18.895 250.797 18.98 251.242 18.98 c 251.781 18.98\n" " 252.223 18.867 252.562 18.641 c 252.906 18.406 253.148 18.051 253.293 17.57\n" " c 256.188 17.82 l 256.055 18.156 255.871 18.512 255.629 18.891 c 255.395\n" " 19.27 255.082 19.617 254.688 19.941 c 254.293 20.254 253.801 20.516 253.211\n" " 20.727 c 252.629 20.93 251.918 21.031 251.082 21.031 c 251.082 10.707 m\n" " 250.77 10.707 250.473 10.762 250.195 10.871 c 249.926 10.973 249.688 11.141\n" " 249.484 11.375 c 249.289 11.602 249.129 11.895 249.004 12.262 c 248.879\n" " 12.625 248.812 13.062 248.797 13.574 c 253.387 13.574 l 253.328 12.621 \n" "253.098 11.906 252.699 11.434 c 252.297 10.953 251.758 10.711 251.082 10.711\n" " c B Q\n" "Q q\n" "0 0 268 26.484 re W n\n" "0.74902 0 0 rg /a1 gs\n" "3.398 0.398 m 1.77 0.398 0.398 1.77 0.398 3.398 c 0.398 23.086 l 0.398 \n" "24.715 1.77 26.086 3.398 26.086 c 264.609 26.086 l 266.238 26.086 267.609\n" " 24.715 267.609 23.086 c 267.609 3.398 l 267.609 1.77 266.238 0.398 264.609\n" " 0.398 c h\n" "3.867 3.844 m 264.164 3.844 l 264.164 22.617 l 3.867 22.617 l h\n" "3.867 3.844 m f\n" "Q q\n" "1 1 1 RG /a1 gs\n" "0.797243 w\n" "1 J\n" "1 j\n" "[] 0.0 d\n" "4 M q 1 0 0 1 0 0 cm\n" "3.398 0.398 m 1.77 0.398 0.398 1.77 0.398 3.398 c 0.398 23.086 l 0.398 \n" "24.715 1.77 26.086 3.398 26.086 c 264.609 26.086 l 266.238 26.086 267.609\n" " 24.715 267.609 23.086 c 267.609 3.398 l 267.609 1.77 266.238 0.398 264.609\n" " 0.398 c h\n" "3.867 3.844 m 264.164 3.844 l 264.164 22.617 l 3.867 22.617 l h\n" "3.867 3.844 m S Q\n" "Q\n"; static Dict *getNotForPublicReleaseStampExtGStateDict(PDFDoc *doc) { Dict *a0Dict = new Dict(doc->getXRef()); a0Dict->add("CA", Object(0.588235)); a0Dict->add("ca", Object(0.588235)); Dict *a1Dict = new Dict(doc->getXRef()); a1Dict->add("CA", Object(1)); a1Dict->add("ca", Object(1)); Dict *extGStateDict = new Dict(doc->getXRef()); extGStateDict->add("a0", Object(a0Dict)); extGStateDict->add("a1", Object(a1Dict)); return extGStateDict; } #endif poppler-24.02.0/poppler/annot_stamp_sold.h000066400000000000000000000177551455701731300205630ustar00rootroot00000000000000//======================================================================== // // annot_stamp_sold.h // // Copyright (C) 2021 Mahmoud Ahmed Khalil // Copyright (C) 2021 Albert Astals Cid // // Mechanically extracted from an SVG created for Okular by Eugene Trounev eugene.trounev@gmail.com // // Licensed under GPLv2 or later // //======================================================================== #ifndef ANNOT_STAMP_SOLD_H #define ANNOT_STAMP_SOLD_H #include "Dict.h" #include "Object.h" #include "PDFDoc.h" static const double ANNOT_STAMP_SOLD_WIDTH = 74.508179; static const double ANNOT_STAMP_SOLD_HEIGHT = 26.484743; static const char *ANNOT_STAMP_SOLD = "1 0 0 -1 0 26.484744 cm\n" "q\n" "1 1 1 rg /a0 gs\n" "3.406 2.129 m 71.117 2.129 l 71.82 2.129 72.387 2.695 72.387 3.398 c 72.387\n" " 23.09 l 72.387 23.793 71.82 24.359 71.117 24.359 c 3.406 24.355 l 2.703\n" " 24.355 2.137 23.789 2.137 23.086 c 2.137 3.395 l 2.137 2.691 2.703 2.125\n" " 3.406 2.125 c h\n" "3.406 2.129 m f\n" "0.74902 0.0117647 0.0117647 rg /a1 gs\n" "1 1 1 RG 0.531496 w\n" "0 J\n" "0 j\n" "[] 0.0 d\n" "4 M q 1 0 0 1 0 0 cm\n" "27.375 16.395 m 27.375 17.902 26.812 19.059 25.691 19.859 c 24.578 20.652\n" " 22.938 21.051 20.773 21.051 c 18.801 21.051 17.25 20.699 16.129 20 c 15.008\n" " 19.301 14.285 18.242 13.965 16.82 c 17.078 16.309 l 17.289 17.125 17.699\n" " 17.719 18.312 18.09 c 18.926 18.453 19.773 18.637 20.859 18.637 c 23.109\n" " 18.637 24.234 17.953 24.234 16.582 c 24.234 16.145 24.102 15.785 23.84 \n" "15.5 c 23.586 15.215 23.219 14.98 22.746 14.789 c 22.281 14.602 21.379 14.371\n" " 20.047 14.102 c 18.895 13.832 18.094 13.617 17.645 13.457 c 17.191 13.289\n" " 16.785 13.098 16.422 12.879 c 16.059 12.652 15.75 12.383 15.492 12.07 c\n" " 15.238 11.758 15.035 11.391 14.891 10.965 c 14.754 10.543 14.684 10.059\n" " 14.684 9.512 c 14.684 8.121 15.203 7.059 16.246 6.32 c 17.297 5.578 18.816\n" " 5.207 20.812 5.207 c 22.723 5.207 24.152 5.504 25.105 6.102 c 26.066 6.699\n" " 26.688 7.688 26.965 9.062 c 23.84 9.488 l 23.68 8.824 23.352 8.328 22.855\n" " 7.992 c 22.367 7.656 21.664 7.488 20.746 7.488 c 18.793 7.488 17.816 8.102\n" " 17.816 9.324 c 17.816 9.727 17.918 10.051 18.121 10.309 c 18.332 10.562\n" " 18.641 10.781 19.051 10.965 c 19.457 11.141 20.285 11.363 21.531 11.633\n" " c 23.012 11.945 24.066 12.238 24.699 12.508 c 25.34 12.77 25.848 13.078\n" " 26.219 13.438 c 26.59 13.789 26.875 14.211 27.07 14.703 c 27.266 15.191\n" " 27.367 15.754 27.367 16.398 c h\n" "27.375 16.395 m B Q\n" "0.74902 0.0117647 0.0117647 rg 1 1 1 RG q 1 0 0 1 0 0 cm\n" "41.043 14.906 m 41.043 16.82 40.512 18.328 39.449 19.418 c 38.387 20.504\n" " 36.914 21.047 35.035 21.047 c 33.191 21.047 31.746 20.5 30.695 19.406 c\n" " 29.645 18.312 29.121 16.812 29.121 14.906 c 29.121 13.004 29.645 11.512\n" " 30.695 10.426 c 31.746 9.332 33.211 8.785 35.098 8.785 c 37.027 8.785 38.5\n" " 9.312 39.512 10.371 c 40.531 11.422 41.043 12.93 41.043 14.906 c h\n" "37.832 14.906 m 37.832 13.5 37.602 12.48 37.145 11.848 c 36.688 11.215 \n" "36.02 10.898 35.145 10.898 c 33.281 10.898 32.348 12.234 32.348 14.91 c \n" "32.348 16.23 32.574 17.234 33.023 17.926 c 33.48 18.609 34.141 18.953 35\n" " 18.953 c 36.887 18.953 37.828 17.605 37.828 14.91 c h\n" "37.832 14.906 m B Q\n" "0.74902 0.0117647 0.0117647 rg 1 1 1 RG q 1 0 0 1 0 0 cm\n" "43.48 4.613 3.07 16.215 re B Q\n" "0.74902 0.0117647 0.0117647 rg 1 1 1 RG q 1 0 0 1 0 0 cm\n" "57.359 20.828 m 57.332 20.719 57.293 20.445 57.25 20.008 c 57.215 19.562\n" " 57.195 19.195 57.195 18.902 c 57.152 18.902 l 56.488 20.332 55.23 21.043\n" " 53.371 21.043 c 51.996 21.043 50.93 20.508 50.18 19.438 c 49.43 18.359 \n" "49.055 16.855 49.055 14.926 c 49.055 12.965 49.449 11.453 50.234 10.391 \n" "c 51.027 9.32 52.148 8.785 53.602 8.785 c 54.438 8.785 55.16 8.961 55.766\n" " 9.309 c 56.379 9.66 56.848 10.18 57.176 10.871 c 57.199 10.871 l 57.176\n" " 8.926 l 57.176 4.609 l 60.246 4.609 l 60.246 18.246 l 60.246 18.973 60.273\n" " 19.836 60.332 20.824 c h\n" "57.219 14.852 m 57.219 13.578 57.004 12.598 56.574 11.914 c 56.152 11.223\n" " 55.523 10.875 54.695 10.875 c 53.871 10.875 53.262 11.211 52.859 11.879\n" " c 52.457 12.543 52.258 13.559 52.258 14.926 c 52.258 17.605 53.062 18.945\n" " 54.672 18.945 c 55.48 18.945 56.105 18.594 56.551 17.887 c 56.996 17.172\n" " 57.219 16.16 57.219 14.848 c h\n" "57.219 14.852 m B Q\n" "Q q\n" "0.74902 0.0117647 0.0117647 rg /a1 gs\n" "1 1 1 RG 0.797243 w\n" "1 J\n" "1 j\n" "[] 0.0 d\n" "4 M q 1 0 0 1 0 0 cm\n" "3.398 0.398 m 1.77 0.398 0.398 1.77 0.398 3.398 c 0.398 23.086 l 0.398 \n" "24.715 1.77 26.086 3.398 26.086 c 71.109 26.086 l 72.738 26.086 74.109 24.715\n" " 74.109 23.086 c 74.109 3.398 l 74.109 1.77 72.738 0.398 71.109 0.398 c \n" "h\n" "3.867 3.844 m 70.664 3.844 l 70.664 22.617 l 3.867 22.617 l h\n" "3.867 3.844 m B Q\n" "Q\n"; static Dict *getSoldStampExtGStateDict(PDFDoc *doc) { Dict *a0Dict = new Dict(doc->getXRef()); a0Dict->add("CA", Object(0.588235)); a0Dict->add("ca", Object(0.588235)); Dict *a1Dict = new Dict(doc->getXRef()); a1Dict->add("CA", Object(1)); a1Dict->add("ca", Object(1)); Dict *extGStateDict = new Dict(doc->getXRef()); extGStateDict->add("a0", Object(a0Dict)); extGStateDict->add("a1", Object(a1Dict)); return extGStateDict; } #endif poppler-24.02.0/poppler/annot_stamp_top_secret.h000066400000000000000000001136371455701731300217650ustar00rootroot00000000000000//======================================================================== // // annot_stamp_top_secret.h // // Copyright (C) 2021 Mahmoud Ahmed Khalil // Copyright (C) 2021 Albert Astals Cid // // Mechanically extracted from an SVG created for Okular by Eugene Trounev eugene.trounev@gmail.com // // Licensed under GPLv2 or later // //======================================================================== #ifndef ANNOT_STAMP_TOP_SECRET_H #define ANNOT_STAMP_TOP_SECRET_H #include "Dict.h" #include "Object.h" #include "PDFDoc.h" static const double ANNOT_STAMP_TOP_SECRET_WIDTH = 141.258179; static const double ANNOT_STAMP_TOP_SECRET_HEIGHT = 26.484743; static const char *ANNOT_STAMP_TOP_SECRET = "1 0 0 -1 0 26.484741 cm\n" "q\n" "1 1 1 rg /a0 gs\n" "3.406 2.129 m 137.867 2.129 l 138.566 2.129 139.137 2.828 139.137 3.398\n" " c 139.137 23.09 l 139.137 23.789 138.57 24.359 137.867 24.359 c 3.406 24.359\n" " l 2.707 24.359 2.137 23.66 2.137 23.09 c 2.137 3.398 l 2.137 2.699 2.703\n" " 2.129 3.406 2.129 c h\n" "3.406 2.129 m f\n" "0.74902 0 0 rg /a1 gs\n" "21.77 7.926 m 21.77 20.832 l 18.547 20.832 l 18.547 7.926 l 13.574 7.926\n" " l 13.574 5.434 l 26.754 5.434 l 26.754 7.926 l 21.77 7.926 l 38.137 14.906\n" " m 38.137 15.809 38.008 16.637 37.754 17.387 c 37.508 18.137 37.133 18.785\n" " 36.629 19.332 c 36.125 19.871 35.5 20.293 34.75 20.598 c 34 20.895 33.125\n" " 21.047 32.129 21.047 c 31.168 21.047 30.32 20.898 29.582 20.598 c 28.848\n" " 20.301 28.227 19.879 27.723 19.34 c 27.227 18.801 26.852 18.156 26.598 \n" "17.406 c 26.344 16.648 26.215 15.816 26.215 14.902 c 26.215 14.02 26.336\n" " 13.207 26.574 12.465 c 26.82 11.715 27.195 11.066 27.688 10.52 c 28.184\n" " 9.973 28.805 9.547 29.555 9.242 c 30.305 8.938 31.184 8.785 32.188 8.785\n" " c 33.25 8.785 34.156 8.938 34.91 9.242 c 35.66 9.547 36.273 9.973 36.746\n" " 10.52 c 37.227 11.059 37.578 11.703 37.797 12.453 c 38.023 13.195 38.137\n" " 14.012 38.137 14.902 c 34.926 14.902 m 34.926 13.496 34.695 12.477 34.238\n" " 11.844 c 33.781 11.211 33.113 10.895 32.238 10.895 c 31.336 10.895 30.645\n" " 11.215 30.164 11.855 c 29.684 12.496 29.441 13.512 29.441 14.902 c 29.441\n" " 15.609 29.504 16.219 29.629 16.727 c 29.762 17.238 29.941 17.656 30.176\n" " 17.984 c 30.41 18.312 30.688 18.555 31.016 18.715 c 31.344 18.867 31.703\n" " 18.945 32.098 18.945 c 32.551 18.945 32.949 18.867 33.301 18.715 c 33.656\n" " 18.555 33.957 18.312 34.195 17.984 c 34.438 17.656 34.617 17.238 34.742\n" " 16.727 c 34.867 16.215 34.93 15.609 34.93 14.902 c 51.777 14.859 m 51.777\n" " 15.754 51.688 16.582 51.504 17.34 c 51.328 18.098 51.055 18.75 50.684 19.297\n" " c 50.312 19.844 49.84 20.273 49.262 20.586 c 48.695 20.891 48.02 21.043\n" " 47.242 21.043 c 46.891 21.043 46.543 21.008 46.191 20.934 c 45.848 20.859\n" " 45.52 20.746 45.207 20.582 c 44.895 20.414 44.602 20.199 44.332 19.938 \n" "c 44.07 19.668 43.844 19.336 43.656 18.941 c 43.59 18.941 l 43.598 18.977\n" " 43.605 19.07 43.613 19.215 c 43.621 19.359 43.629 19.531 43.637 19.727 \n" "c 43.645 19.914 43.648 20.121 43.648 20.34 c 43.656 20.551 43.66 20.746 \n" "43.66 20.93 c 43.66 25.465 l 40.59 25.465 l 40.59 11.719 l 40.59 11.113 \n" "40.578 10.574 40.559 10.102 c 40.543 9.629 40.527 9.262 40.504 8.996 c 43.488\n" " 8.996 l 43.504 9.047 43.516 9.145 43.531 9.293 c 43.555 9.438 43.566 9.605\n" " 43.574 9.797 c 43.59 9.984 43.602 10.184 43.605 10.387 c 43.613 10.59 43.617\n" " 10.77 43.617 10.922 c 43.66 10.922 l 44.031 10.141 44.543 9.586 45.191 \n" "9.25 c 45.84 8.914 46.59 8.746 47.441 8.746 c 48.191 8.746 48.84 8.898 49.387\n" " 9.203 c 49.934 9.508 50.383 9.93 50.73 10.469 c 51.086 11.008 51.352 11.652\n" " 51.516 12.402 c 51.691 13.145 51.777 13.961 51.777 14.852 c 48.574 14.852\n" " m 48.574 13.504 48.371 12.508 47.961 11.859 c 47.555 11.203 46.945 10.875\n" " 46.137 10.875 c 45.832 10.875 45.523 10.941 45.219 11.07 c 44.922 11.195\n" " 44.652 11.414 44.41 11.727 c 44.176 12.031 43.984 12.449 43.832 12.973 \n" "c 43.688 13.488 43.613 14.145 43.613 14.941 c 43.613 15.715 43.688 16.359\n" " 43.832 16.875 c 43.977 17.387 44.168 17.793 44.398 18.098 c 44.641 18.402\n" " 44.91 18.621 45.207 18.754 c 45.504 18.879 45.809 18.941 46.113 18.941 \n" "c 46.508 18.941 46.855 18.863 47.164 18.711 c 47.469 18.551 47.727 18.309\n" " 47.93 17.98 c 48.141 17.645 48.301 17.219 48.41 16.703 c 48.52 16.188 48.574\n" " 15.57 48.574 14.855 c 72.965 16.387 m 72.965 17.086 72.836 17.723 72.582\n" " 18.301 c 72.328 18.867 71.93 19.355 71.391 19.766 c 70.859 20.172 70.176\n" " 20.492 69.336 20.715 c 68.504 20.934 67.516 21.043 66.363 21.043 c 65.344\n" " 21.043 64.445 20.953 63.664 20.77 c 62.883 20.586 62.215 20.316 61.652 \n" "19.961 c 61.098 19.605 60.648 19.164 60.297 18.641 c 59.953 18.109 59.707\n" " 17.5 59.555 16.816 c 62.668 16.305 l 62.754 16.648 62.883 16.961 63.051\n" " 17.246 c 63.219 17.531 63.449 17.777 63.738 17.988 c 64.035 18.191 64.406\n" " 18.352 64.844 18.469 c 65.289 18.578 65.824 18.633 66.449 18.633 c 67.543\n" " 18.633 68.375 18.469 68.953 18.141 c 69.535 17.805 69.828 17.285 69.828\n" " 16.578 c 69.828 16.156 69.711 15.809 69.477 15.539 c 69.242 15.27 68.934\n" " 15.047 68.547 14.871 c 68.16 14.695 67.715 14.551 67.215 14.434 c 66.711\n" " 14.316 66.188 14.203 65.641 14.094 c 65.203 13.992 64.766 13.887 64.328\n" " 13.777 c 63.891 13.668 63.473 13.535 63.07 13.383 c 62.676 13.23 62.309\n" " 13.047 61.965 12.836 c 61.621 12.625 61.324 12.367 61.07 12.059 c 60.824\n" " 11.746 60.625 11.383 60.48 10.965 c 60.344 10.551 60.273 10.062 60.273 \n" "9.5 c 60.273 8.734 60.426 8.078 60.73 7.531 c 61.035 6.984 61.457 6.539 \n" "61.996 6.199 c 62.543 5.848 63.191 5.594 63.941 5.434 c 64.691 5.273 65.512\n" " 5.191 66.398 5.191 c 67.402 5.191 68.262 5.273 68.977 5.434 c 69.699 5.586\n" " 70.297 5.824 70.781 6.145 c 71.27 6.465 71.652 6.871 71.93 7.355 c 72.215\n" " 7.836 72.422 8.402 72.551 9.051 c 69.426 9.477 l 69.266 8.812 68.938 8.316\n" " 68.441 7.98 c 67.953 7.645 67.25 7.477 66.332 7.477 c 65.766 7.477 65.289\n" " 7.527 64.91 7.629 c 64.539 7.723 64.238 7.855 64.016 8.023 c 63.797 8.191\n" " 63.641 8.387 63.547 8.613 c 63.453 8.832 63.406 9.066 63.406 9.312 c 63.406\n" " 9.691 63.496 10.004 63.68 10.254 c 63.863 10.496 64.117 10.699 64.445 10.867\n" " c 64.773 11.027 65.164 11.164 65.613 11.281 c 66.07 11.391 66.574 11.504\n" " 67.121 11.621 c 67.602 11.723 68.078 11.832 68.551 11.949 c 69.023 12.059\n" " 69.477 12.191 69.906 12.344 c 70.336 12.496 70.738 12.684 71.109 12.902\n" " c 71.48 13.121 71.801 13.391 72.07 13.711 c 72.348 14.023 72.562 14.398\n" " 72.715 14.836 c 72.875 15.273 72.957 15.789 72.957 16.387 c 80.258 21.043\n" " m 79.406 21.043 78.637 20.918 77.953 20.672 c 77.277 20.418 76.695 20.035\n" " 76.215 19.523 c 75.734 19.008 75.367 18.363 75.109 17.59 c 74.855 16.809\n" " 74.727 15.898 74.727 14.859 c 74.727 13.73 74.875 12.781 75.176 12.008 \n" "c 75.48 11.234 75.891 10.613 76.398 10.141 c 76.914 9.66 77.508 9.312 78.18\n" " 9.102 c 78.852 8.891 79.555 8.785 80.301 8.785 c 81.234 8.785 82.027 8.949\n" " 82.684 9.277 c 83.348 9.598 83.891 10.051 84.312 10.633 c 84.734 11.215\n" " 85.043 11.914 85.242 12.73 c 85.438 13.539 85.539 14.434 85.539 15.418 \n" "c 85.539 15.504 l 77.957 15.504 l 77.957 16 78 16.461 78.09 16.891 c 78.176\n" " 17.312 78.32 17.68 78.516 17.996 c 78.711 18.301 78.969 18.547 79.281 18.727\n" " c 79.594 18.902 79.973 18.988 80.418 18.988 c 80.957 18.988 81.398 18.875\n" " 81.738 18.648 c 82.082 18.414 82.324 18.059 82.469 17.578 c 85.363 17.828\n" " l 85.23 18.164 85.047 18.52 84.805 18.898 c 84.57 19.277 84.258 19.625 \n" "83.863 19.949 c 83.469 20.262 82.977 20.523 82.387 20.734 c 81.805 20.938\n" " 81.094 21.039 80.258 21.039 c 80.258 10.715 m 79.945 10.715 79.648 10.77\n" " 79.371 10.879 c 79.102 10.98 78.863 11.148 78.66 11.383 c 78.465 11.609\n" " 78.305 11.902 78.18 12.27 c 78.055 12.633 77.988 13.07 77.973 13.582 c \n" "82.562 13.582 l 82.504 12.629 82.273 11.914 81.875 11.441 c 81.473 10.961\n" " 80.934 10.719 80.258 10.719 c 92.801 21.043 m 91.859 21.043 91.039 20.898\n" " 90.332 20.605 c 89.633 20.312 89.051 19.902 88.582 19.371 c 88.117 18.84\n" " 87.766 18.203 87.531 17.457 c 87.297 16.707 87.18 15.879 87.18 14.977 c\n" " 87.18 13.992 87.309 13.117 87.562 12.355 c 87.816 11.582 88.184 10.934 \n" "88.668 10.41 c 89.148 9.879 89.738 9.473 90.438 9.199 c 91.145 8.922 91.945\n" " 8.785 92.84 8.785 c 93.605 8.785 94.281 8.887 94.871 9.09 c 95.469 9.293\n" " 95.98 9.574 96.41 9.93 c 96.84 10.281 97.188 10.695 97.449 11.176 c 97.719\n" " 11.656 97.906 12.172 98.008 12.727 c 94.914 12.879 l 94.828 12.273 94.609\n" " 11.793 94.258 11.438 c 93.906 11.074 93.414 10.891 92.773 10.891 c 91.949\n" " 10.891 91.348 11.23 90.969 11.906 c 90.59 12.582 90.402 13.566 90.402 14.855\n" " c 90.402 17.578 91.207 18.941 92.816 18.941 c 93.398 18.941 93.887 18.758\n" " 94.281 18.395 c 94.676 18.023 94.918 17.473 95.012 16.746 c 98.094 16.887\n" " l 98.02 17.434 97.852 17.957 97.59 18.461 c 97.336 18.957 96.984 19.398\n" " 96.539 19.781 c 96.102 20.16 95.57 20.465 94.945 20.699 c 94.32 20.926 \n" "93.602 21.039 92.793 21.039 c 100.32 20.82 m 100.32 11.773 l 100.32 11.52\n" " 100.316 11.25 100.309 10.965 c 100.309 10.68 100.301 10.41 100.285 10.156\n" " c 100.277 9.895 100.27 9.66 100.262 9.457 c 100.254 9.246 100.242 9.094\n" " 100.23 9 c 103.16 9 l 103.176 9.086 103.188 9.242 103.203 9.457 c 103.219\n" " 9.668 103.23 9.902 103.246 10.156 c 103.262 10.41 103.273 10.668 103.277\n" " 10.922 c 103.293 11.168 103.301 11.371 103.301 11.523 c 103.344 11.523 \n" "l 103.496 11.094 103.648 10.711 103.801 10.375 c 103.953 10.031 104.133 \n" "9.746 104.336 9.512 c 104.547 9.27 104.797 9.09 105.09 8.965 c 105.383 8.832\n" " 105.742 8.77 106.172 8.77 c 106.355 8.77 106.531 8.789 106.707 8.824 c \n" "106.891 8.852 107.027 8.891 107.121 8.934 c 107.121 11.5 l 106.926 11.457\n" " 106.723 11.418 106.52 11.391 c 106.324 11.355 106.086 11.336 105.809 11.336\n" " c 105.043 11.336 104.445 11.645 104.016 12.266 c 103.594 12.887 103.383\n" " 13.801 103.383 15.02 c 103.383 20.82 l 100.312 20.82 l 113.852 21.039 m\n" " 113 21.039 112.23 20.914 111.547 20.668 c 110.871 20.414 110.289 20.031\n" " 109.809 19.52 c 109.328 19.004 108.961 18.359 108.703 17.586 c 108.449 \n" "16.805 108.32 15.895 108.32 14.855 c 108.32 13.727 108.469 12.777 108.77\n" " 12.004 c 109.074 11.23 109.484 10.609 109.992 10.137 c 110.508 9.656 111.102\n" " 9.309 111.773 9.098 c 112.445 8.887 113.148 8.781 113.895 8.781 c 114.828\n" " 8.781 115.621 8.945 116.277 9.273 c 116.941 9.594 117.484 10.047 117.906\n" " 10.629 c 118.328 11.211 118.637 11.91 118.836 12.727 c 119.031 13.535 119.133\n" " 14.43 119.133 15.414 c 119.133 15.5 l 111.551 15.5 l 111.551 15.996 111.594\n" " 16.457 111.684 16.887 c 111.77 17.309 111.914 17.676 112.109 17.992 c 112.305\n" " 18.297 112.562 18.543 112.875 18.723 c 113.188 18.898 113.566 18.984 114.012\n" " 18.984 c 114.551 18.984 114.992 18.871 115.332 18.645 c 115.676 18.41 115.918\n" " 18.055 116.062 17.574 c 118.957 17.824 l 118.824 18.16 118.641 18.516 118.398\n" " 18.895 c 118.164 19.273 117.852 19.621 117.457 19.945 c 117.062 20.258 \n" "116.57 20.52 115.98 20.73 c 115.398 20.934 114.688 21.035 113.852 21.035\n" " c 113.852 10.711 m 113.539 10.711 113.242 10.766 112.965 10.875 c 112.695\n" " 10.977 112.457 11.145 112.254 11.379 c 112.059 11.605 111.898 11.898 111.773\n" " 12.266 c 111.648 12.629 111.582 13.066 111.566 13.578 c 116.156 13.578 \n" "l 116.098 12.625 115.867 11.91 115.469 11.438 c 115.066 10.957 114.527 10.715\n" " 113.852 10.715 c 124.496 21.02 m 123.594 21.02 122.898 20.777 122.41 20.289\n" " c 121.922 19.793 121.68 19.047 121.68 18.051 c 121.68 11.078 l 120.184 \n" "11.078 l 120.184 9.004 l 121.832 9.004 l 122.793 6.227 l 124.715 6.227 l\n" " 124.715 9.004 l 126.953 9.004 l 126.953 11.078 l 124.715 11.078 l 124.715\n" " 17.219 l 124.715 17.793 124.824 18.219 125.043 18.496 c 125.262 18.766 \n" "125.602 18.902 126.059 18.898 c 126.246 18.898 126.418 18.883 126.57 18.855\n" " c 126.723 18.828 126.895 18.789 127.082 18.746 c 127.082 20.648 l 126.703\n" " 20.773 126.301 20.863 125.879 20.922 c 125.457 20.988 124.992 21.02 124.492\n" " 21.02 c f\n" "1 1 1 RG 0.531496 w\n" "0 J\n" "0 j\n" "[] 0.0 d\n" "4 M q 1 0 0 1 0 0 cm\n" "21.77 7.926 m 21.77 20.832 l 18.547 20.832 l 18.547 7.926 l 13.574 7.926\n" " l 13.574 5.434 l 26.754 5.434 l 26.754 7.926 l 21.77 7.926 l 38.137 14.906\n" " m 38.137 15.809 38.008 16.637 37.754 17.387 c 37.508 18.137 37.133 18.785\n" " 36.629 19.332 c 36.125 19.871 35.5 20.293 34.75 20.598 c 34 20.895 33.125\n" " 21.047 32.129 21.047 c 31.168 21.047 30.32 20.898 29.582 20.598 c 28.848\n" " 20.301 28.227 19.879 27.723 19.34 c 27.227 18.801 26.852 18.156 26.598 \n" "17.406 c 26.344 16.648 26.215 15.816 26.215 14.902 c 26.215 14.02 26.336\n" " 13.207 26.574 12.465 c 26.82 11.715 27.195 11.066 27.688 10.52 c 28.184\n" " 9.973 28.805 9.547 29.555 9.242 c 30.305 8.938 31.184 8.785 32.188 8.785\n" " c 33.25 8.785 34.156 8.938 34.91 9.242 c 35.66 9.547 36.273 9.973 36.746\n" " 10.52 c 37.227 11.059 37.578 11.703 37.797 12.453 c 38.023 13.195 38.137\n" " 14.012 38.137 14.902 c 34.926 14.902 m 34.926 13.496 34.695 12.477 34.238\n" " 11.844 c 33.781 11.211 33.113 10.895 32.238 10.895 c 31.336 10.895 30.645\n" " 11.215 30.164 11.855 c 29.684 12.496 29.441 13.512 29.441 14.902 c 29.441\n" " 15.609 29.504 16.219 29.629 16.727 c 29.762 17.238 29.941 17.656 30.176\n" " 17.984 c 30.41 18.312 30.688 18.555 31.016 18.715 c 31.344 18.867 31.703\n" " 18.945 32.098 18.945 c 32.551 18.945 32.949 18.867 33.301 18.715 c 33.656\n" " 18.555 33.957 18.312 34.195 17.984 c 34.438 17.656 34.617 17.238 34.742\n" " 16.727 c 34.867 16.215 34.93 15.609 34.93 14.902 c 51.777 14.859 m 51.777\n" " 15.754 51.688 16.582 51.504 17.34 c 51.328 18.098 51.055 18.75 50.684 19.297\n" " c 50.312 19.844 49.84 20.273 49.262 20.586 c 48.695 20.891 48.02 21.043\n" " 47.242 21.043 c 46.891 21.043 46.543 21.008 46.191 20.934 c 45.848 20.859\n" " 45.52 20.746 45.207 20.582 c 44.895 20.414 44.602 20.199 44.332 19.938 \n" "c 44.07 19.668 43.844 19.336 43.656 18.941 c 43.59 18.941 l 43.598 18.977\n" " 43.605 19.07 43.613 19.215 c 43.621 19.359 43.629 19.531 43.637 19.727 \n" "c 43.645 19.914 43.648 20.121 43.648 20.34 c 43.656 20.551 43.66 20.746 \n" "43.66 20.93 c 43.66 25.465 l 40.59 25.465 l 40.59 11.719 l 40.59 11.113 \n" "40.578 10.574 40.559 10.102 c 40.543 9.629 40.527 9.262 40.504 8.996 c 43.488\n" " 8.996 l 43.504 9.047 43.516 9.145 43.531 9.293 c 43.555 9.438 43.566 9.605\n" " 43.574 9.797 c 43.59 9.984 43.602 10.184 43.605 10.387 c 43.613 10.59 43.617\n" " 10.77 43.617 10.922 c 43.66 10.922 l 44.031 10.141 44.543 9.586 45.191 \n" "9.25 c 45.84 8.914 46.59 8.746 47.441 8.746 c 48.191 8.746 48.84 8.898 49.387\n" " 9.203 c 49.934 9.508 50.383 9.93 50.73 10.469 c 51.086 11.008 51.352 11.652\n" " 51.516 12.402 c 51.691 13.145 51.777 13.961 51.777 14.852 c 48.574 14.852\n" " m 48.574 13.504 48.371 12.508 47.961 11.859 c 47.555 11.203 46.945 10.875\n" " 46.137 10.875 c 45.832 10.875 45.523 10.941 45.219 11.07 c 44.922 11.195\n" " 44.652 11.414 44.41 11.727 c 44.176 12.031 43.984 12.449 43.832 12.973 \n" "c 43.688 13.488 43.613 14.145 43.613 14.941 c 43.613 15.715 43.688 16.359\n" " 43.832 16.875 c 43.977 17.387 44.168 17.793 44.398 18.098 c 44.641 18.402\n" " 44.91 18.621 45.207 18.754 c 45.504 18.879 45.809 18.941 46.113 18.941 \n" "c 46.508 18.941 46.855 18.863 47.164 18.711 c 47.469 18.551 47.727 18.309\n" " 47.93 17.98 c 48.141 17.645 48.301 17.219 48.41 16.703 c 48.52 16.188 48.574\n" " 15.57 48.574 14.855 c 72.965 16.387 m 72.965 17.086 72.836 17.723 72.582\n" " 18.301 c 72.328 18.867 71.93 19.355 71.391 19.766 c 70.859 20.172 70.176\n" " 20.492 69.336 20.715 c 68.504 20.934 67.516 21.043 66.363 21.043 c 65.344\n" " 21.043 64.445 20.953 63.664 20.77 c 62.883 20.586 62.215 20.316 61.652 \n" "19.961 c 61.098 19.605 60.648 19.164 60.297 18.641 c 59.953 18.109 59.707\n" " 17.5 59.555 16.816 c 62.668 16.305 l 62.754 16.648 62.883 16.961 63.051\n" " 17.246 c 63.219 17.531 63.449 17.777 63.738 17.988 c 64.035 18.191 64.406\n" " 18.352 64.844 18.469 c 65.289 18.578 65.824 18.633 66.449 18.633 c 67.543\n" " 18.633 68.375 18.469 68.953 18.141 c 69.535 17.805 69.828 17.285 69.828\n" " 16.578 c 69.828 16.156 69.711 15.809 69.477 15.539 c 69.242 15.27 68.934\n" " 15.047 68.547 14.871 c 68.16 14.695 67.715 14.551 67.215 14.434 c 66.711\n" " 14.316 66.188 14.203 65.641 14.094 c 65.203 13.992 64.766 13.887 64.328\n" " 13.777 c 63.891 13.668 63.473 13.535 63.07 13.383 c 62.676 13.23 62.309\n" " 13.047 61.965 12.836 c 61.621 12.625 61.324 12.367 61.07 12.059 c 60.824\n" " 11.746 60.625 11.383 60.48 10.965 c 60.344 10.551 60.273 10.062 60.273 \n" "9.5 c 60.273 8.734 60.426 8.078 60.73 7.531 c 61.035 6.984 61.457 6.539 \n" "61.996 6.199 c 62.543 5.848 63.191 5.594 63.941 5.434 c 64.691 5.273 65.512\n" " 5.191 66.398 5.191 c 67.402 5.191 68.262 5.273 68.977 5.434 c 69.699 5.586\n" " 70.297 5.824 70.781 6.145 c 71.27 6.465 71.652 6.871 71.93 7.355 c 72.215\n" " 7.836 72.422 8.402 72.551 9.051 c 69.426 9.477 l 69.266 8.812 68.938 8.316\n" " 68.441 7.98 c 67.953 7.645 67.25 7.477 66.332 7.477 c 65.766 7.477 65.289\n" " 7.527 64.91 7.629 c 64.539 7.723 64.238 7.855 64.016 8.023 c 63.797 8.191\n" " 63.641 8.387 63.547 8.613 c 63.453 8.832 63.406 9.066 63.406 9.312 c 63.406\n" " 9.691 63.496 10.004 63.68 10.254 c 63.863 10.496 64.117 10.699 64.445 10.867\n" " c 64.773 11.027 65.164 11.164 65.613 11.281 c 66.07 11.391 66.574 11.504\n" " 67.121 11.621 c 67.602 11.723 68.078 11.832 68.551 11.949 c 69.023 12.059\n" " 69.477 12.191 69.906 12.344 c 70.336 12.496 70.738 12.684 71.109 12.902\n" " c 71.48 13.121 71.801 13.391 72.07 13.711 c 72.348 14.023 72.562 14.398\n" " 72.715 14.836 c 72.875 15.273 72.957 15.789 72.957 16.387 c 80.258 21.043\n" " m 79.406 21.043 78.637 20.918 77.953 20.672 c 77.277 20.418 76.695 20.035\n" " 76.215 19.523 c 75.734 19.008 75.367 18.363 75.109 17.59 c 74.855 16.809\n" " 74.727 15.898 74.727 14.859 c 74.727 13.73 74.875 12.781 75.176 12.008 \n" "c 75.48 11.234 75.891 10.613 76.398 10.141 c 76.914 9.66 77.508 9.312 78.18\n" " 9.102 c 78.852 8.891 79.555 8.785 80.301 8.785 c 81.234 8.785 82.027 8.949\n" " 82.684 9.277 c 83.348 9.598 83.891 10.051 84.312 10.633 c 84.734 11.215\n" " 85.043 11.914 85.242 12.73 c 85.438 13.539 85.539 14.434 85.539 15.418 \n" "c 85.539 15.504 l 77.957 15.504 l 77.957 16 78 16.461 78.09 16.891 c 78.176\n" " 17.312 78.32 17.68 78.516 17.996 c 78.711 18.301 78.969 18.547 79.281 18.727\n" " c 79.594 18.902 79.973 18.988 80.418 18.988 c 80.957 18.988 81.398 18.875\n" " 81.738 18.648 c 82.082 18.414 82.324 18.059 82.469 17.578 c 85.363 17.828\n" " l 85.23 18.164 85.047 18.52 84.805 18.898 c 84.57 19.277 84.258 19.625 \n" "83.863 19.949 c 83.469 20.262 82.977 20.523 82.387 20.734 c 81.805 20.938\n" " 81.094 21.039 80.258 21.039 c 80.258 10.715 m 79.945 10.715 79.648 10.77\n" " 79.371 10.879 c 79.102 10.98 78.863 11.148 78.66 11.383 c 78.465 11.609\n" " 78.305 11.902 78.18 12.27 c 78.055 12.633 77.988 13.07 77.973 13.582 c \n" "82.562 13.582 l 82.504 12.629 82.273 11.914 81.875 11.441 c 81.473 10.961\n" " 80.934 10.719 80.258 10.719 c 92.801 21.043 m 91.859 21.043 91.039 20.898\n" " 90.332 20.605 c 89.633 20.312 89.051 19.902 88.582 19.371 c 88.117 18.84\n" " 87.766 18.203 87.531 17.457 c 87.297 16.707 87.18 15.879 87.18 14.977 c\n" " 87.18 13.992 87.309 13.117 87.562 12.355 c 87.816 11.582 88.184 10.934 \n" "88.668 10.41 c 89.148 9.879 89.738 9.473 90.438 9.199 c 91.145 8.922 91.945\n" " 8.785 92.84 8.785 c 93.605 8.785 94.281 8.887 94.871 9.09 c 95.469 9.293\n" " 95.98 9.574 96.41 9.93 c 96.84 10.281 97.188 10.695 97.449 11.176 c 97.719\n" " 11.656 97.906 12.172 98.008 12.727 c 94.914 12.879 l 94.828 12.273 94.609\n" " 11.793 94.258 11.438 c 93.906 11.074 93.414 10.891 92.773 10.891 c 91.949\n" " 10.891 91.348 11.23 90.969 11.906 c 90.59 12.582 90.402 13.566 90.402 14.855\n" " c 90.402 17.578 91.207 18.941 92.816 18.941 c 93.398 18.941 93.887 18.758\n" " 94.281 18.395 c 94.676 18.023 94.918 17.473 95.012 16.746 c 98.094 16.887\n" " l 98.02 17.434 97.852 17.957 97.59 18.461 c 97.336 18.957 96.984 19.398\n" " 96.539 19.781 c 96.102 20.16 95.57 20.465 94.945 20.699 c 94.32 20.926 \n" "93.602 21.039 92.793 21.039 c 100.32 20.82 m 100.32 11.773 l 100.32 11.52\n" " 100.316 11.25 100.309 10.965 c 100.309 10.68 100.301 10.41 100.285 10.156\n" " c 100.277 9.895 100.27 9.66 100.262 9.457 c 100.254 9.246 100.242 9.094\n" " 100.23 9 c 103.16 9 l 103.176 9.086 103.188 9.242 103.203 9.457 c 103.219\n" " 9.668 103.23 9.902 103.246 10.156 c 103.262 10.41 103.273 10.668 103.277\n" " 10.922 c 103.293 11.168 103.301 11.371 103.301 11.523 c 103.344 11.523 \n" "l 103.496 11.094 103.648 10.711 103.801 10.375 c 103.953 10.031 104.133 \n" "9.746 104.336 9.512 c 104.547 9.27 104.797 9.09 105.09 8.965 c 105.383 8.832\n" " 105.742 8.77 106.172 8.77 c 106.355 8.77 106.531 8.789 106.707 8.824 c \n" "106.891 8.852 107.027 8.891 107.121 8.934 c 107.121 11.5 l 106.926 11.457\n" " 106.723 11.418 106.52 11.391 c 106.324 11.355 106.086 11.336 105.809 11.336\n" " c 105.043 11.336 104.445 11.645 104.016 12.266 c 103.594 12.887 103.383\n" " 13.801 103.383 15.02 c 103.383 20.82 l 100.312 20.82 l 113.852 21.039 m\n" " 113 21.039 112.23 20.914 111.547 20.668 c 110.871 20.414 110.289 20.031\n" " 109.809 19.52 c 109.328 19.004 108.961 18.359 108.703 17.586 c 108.449 \n" "16.805 108.32 15.895 108.32 14.855 c 108.32 13.727 108.469 12.777 108.77\n" " 12.004 c 109.074 11.23 109.484 10.609 109.992 10.137 c 110.508 9.656 111.102\n" " 9.309 111.773 9.098 c 112.445 8.887 113.148 8.781 113.895 8.781 c 114.828\n" " 8.781 115.621 8.945 116.277 9.273 c 116.941 9.594 117.484 10.047 117.906\n" " 10.629 c 118.328 11.211 118.637 11.91 118.836 12.727 c 119.031 13.535 119.133\n" " 14.43 119.133 15.414 c 119.133 15.5 l 111.551 15.5 l 111.551 15.996 111.594\n" " 16.457 111.684 16.887 c 111.77 17.309 111.914 17.676 112.109 17.992 c 112.305\n" " 18.297 112.562 18.543 112.875 18.723 c 113.188 18.898 113.566 18.984 114.012\n" " 18.984 c 114.551 18.984 114.992 18.871 115.332 18.645 c 115.676 18.41 115.918\n" " 18.055 116.062 17.574 c 118.957 17.824 l 118.824 18.16 118.641 18.516 118.398\n" " 18.895 c 118.164 19.273 117.852 19.621 117.457 19.945 c 117.062 20.258 \n" "116.57 20.52 115.98 20.73 c 115.398 20.934 114.688 21.035 113.852 21.035\n" " c 113.852 10.711 m 113.539 10.711 113.242 10.766 112.965 10.875 c 112.695\n" " 10.977 112.457 11.145 112.254 11.379 c 112.059 11.605 111.898 11.898 111.773\n" " 12.266 c 111.648 12.629 111.582 13.066 111.566 13.578 c 116.156 13.578 \n" "l 116.098 12.625 115.867 11.91 115.469 11.438 c 115.066 10.957 114.527 10.715\n" " 113.852 10.715 c 124.496 21.02 m 123.594 21.02 122.898 20.777 122.41 20.289\n" " c 121.922 19.793 121.68 19.047 121.68 18.051 c 121.68 11.078 l 120.184 \n" "11.078 l 120.184 9.004 l 121.832 9.004 l 122.793 6.227 l 124.715 6.227 l\n" " 124.715 9.004 l 126.953 9.004 l 126.953 11.078 l 124.715 11.078 l 124.715\n" " 17.219 l 124.715 17.793 124.824 18.219 125.043 18.496 c 125.262 18.766 \n" "125.602 18.902 126.059 18.898 c 126.246 18.898 126.418 18.883 126.57 18.855\n" " c 126.723 18.828 126.895 18.789 127.082 18.746 c 127.082 20.648 l 126.703\n" " 20.773 126.301 20.863 125.879 20.922 c 125.457 20.988 124.992 21.02 124.492\n" " 21.02 c S Q\n" "Q q\n" "0 0 141 26.484 re W n\n" "0.74902 0 0 rg /a1 gs\n" "3.398 0.398 m 1.77 0.398 0.398 1.77 0.398 3.398 c 0.398 23.086 l 0.398 \n" "24.715 1.77 26.086 3.398 26.086 c 137.859 26.086 l 139.488 26.086 140.859\n" " 24.715 140.859 23.086 c 140.859 3.398 l 140.859 1.77 139.488 0.398 137.859\n" " 0.398 c h\n" "3.867 3.844 m 137.414 3.844 l 137.414 22.617 l 3.867 22.617 l h\n" "3.867 3.844 m f\n" "Q q\n" "1 1 1 RG /a1 gs\n" "0.797243 w\n" "1 J\n" "1 j\n" "[] 0.0 d\n" "4 M q 1 0 0 1 0 0 cm\n" "3.398 0.398 m 1.77 0.398 0.398 1.77 0.398 3.398 c 0.398 23.086 l 0.398 \n" "24.715 1.77 26.086 3.398 26.086 c 137.859 26.086 l 139.488 26.086 140.859\n" " 24.715 140.859 23.086 c 140.859 3.398 l 140.859 1.77 139.488 0.398 137.859\n" " 0.398 c h\n" "3.867 3.844 m 137.414 3.844 l 137.414 22.617 l 3.867 22.617 l h\n" "3.867 3.844 m S Q\n" "Q\n"; static Dict *getTopSecretStampExtGStateDict(PDFDoc *doc) { Dict *a0Dict = new Dict(doc->getXRef()); a0Dict->add("CA", Object(0.588235)); a0Dict->add("ca", Object(0.588235)); Dict *a1Dict = new Dict(doc->getXRef()); a1Dict->add("CA", Object(1)); a1Dict->add("ca", Object(1)); Dict *extGStateDict = new Dict(doc->getXRef()); extGStateDict->add("a0", Object(a0Dict)); extGStateDict->add("a1", Object(a1Dict)); return extGStateDict; } #endif poppler-24.02.0/poppler/gen-unicode-tables.py000066400000000000000000000027141455701731300210520ustar00rootroot00000000000000from __future__ import absolute_import, division, print_function import sys import unicodedata if sys.version_info[0] == 2: chr = unichr UNICODE_LAST_CHAR_PART1 = 0x2FAFF HANGUL_S_BASE = 0xAC00 HANGUL_S_COUNT = 19 * 21 * 28 print("""// Generated by gen-unicode-tables.py typedef struct { Unicode character; int length; int offset; } decomposition; """) decomp_table = [] max_index = 0 decomp_expansion_index = {} decomp_expansion = [] for u in range(0, UNICODE_LAST_CHAR_PART1): if HANGUL_S_BASE <= u < HANGUL_S_BASE + HANGUL_S_COUNT: continue norm = tuple(map(ord, unicodedata.normalize("NFKD", chr(u)))) if norm != (u, ): try: i = decomp_expansion_index[norm] decomp_table.append((u, len(norm), i)) except KeyError: decomp_table.append((u, len(norm), max_index)) decomp_expansion_index[norm] = max_index decomp_expansion.append((norm, max_index)) max_index += len(norm) print("#define DECOMP_TABLE_LENGTH %d" % (len(decomp_table), )) print() print("static const decomposition decomp_table[] = {") print(*(" { 0x%x, %d, %d }" % (character, length, offset) for character, length, offset in decomp_table), sep=",\n") print("};") print() print("static const Unicode decomp_expansion[] = {") print(*(" %s /* offset %d */" % (", ".join("0x%x" % u for u in norm), index) for norm, index in decomp_expansion), sep=" ,\n") print("};") poppler-24.02.0/poppler/libpoppler.map.in000066400000000000000000000000711455701731300203010ustar00rootroot00000000000000POPPLER_@POPPLER_SOVERSION_NUMBER@ { global: *; }; poppler-24.02.0/poppler/poppler-config.h.cmake000066400000000000000000000104561455701731300212110ustar00rootroot00000000000000//================================================= -*- mode: c++ -*- ==== // // poppler-config.h // // Copyright 1996-2011, 2022 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) 2014 Bogdan Cristea // Copyright (C) 2014 Hib Eris // Copyright (C) 2016 Tor Lillqvist // Copyright (C) 2017 Adrian Johnson // Copyright (C) 2018 Adam Reichold // Copyright (C) 2018 Stefan Brüns // Copyright (C) 2020 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 // //======================================================================== #ifndef POPPLER_CONFIG_H #define POPPLER_CONFIG_H // We duplicate some of the config.h #define's here since they are // used in some of the header files we install. The #ifndef/#endif // around #undef look odd, but it's to silence warnings about // redefining those symbols. /* Defines the poppler version. */ #ifndef POPPLER_VERSION #define POPPLER_VERSION "${POPPLER_VERSION}" #endif /* Use single precision arithmetic in the Splash backend */ #ifndef USE_FLOAT #cmakedefine USE_FLOAT 1 #endif /* Include support for OPI comments. */ #ifndef OPI_SUPPORT #cmakedefine OPI_SUPPORT 1 #endif /* Enable word list support. */ #ifndef TEXTOUT_WORD_LIST #cmakedefine TEXTOUT_WORD_LIST 1 #endif /* Support for curl is compiled in. */ #ifndef POPPLER_HAS_CURL_SUPPORT #cmakedefine POPPLER_HAS_CURL_SUPPORT 1 #endif /* Use libjpeg instead of builtin jpeg decoder. */ #ifndef ENABLE_LIBJPEG #cmakedefine ENABLE_LIBJPEG 1 #endif /* Build against libtiff. */ #ifndef ENABLE_LIBTIFF #cmakedefine ENABLE_LIBTIFF 1 #endif /* Build against libpng. */ #ifndef ENABLE_LIBPNG #cmakedefine ENABLE_LIBPNG 1 #endif /* Define to 1 if you have the header file, and it defines `DIR'. */ #ifndef HAVE_DIRENT_H #cmakedefine HAVE_DIRENT_H 1 #endif /* Defines if gettimeofday is available on your system */ #ifndef HAVE_GETTIMEOFDAY #cmakedefine HAVE_GETTIMEOFDAY 1 #endif /* Define to 1 if you have the header file, and it defines `DIR'. */ #ifndef HAVE_NDIR_H #cmakedefine HAVE_NDIR_H 1 #endif /* Define to 1 if you have the header file, and it defines `DIR'. */ #ifndef HAVE_SYS_DIR_H #cmakedefine HAVE_SYS_DIR_H 1 #endif /* Define to 1 if you have the header file, and it defines `DIR'. */ #ifndef HAVE_SYS_NDIR_H #cmakedefine HAVE_SYS_NDIR_H 1 #endif /* Defines if use cms */ #ifndef USE_CMS #cmakedefine USE_CMS 1 #endif /* Use header-only classes from Boost in the Splash backend */ #ifndef USE_BOOST_HEADERS #cmakedefine USE_BOOST_HEADERS 1 #endif //------------------------------------------------------------------------ // version //------------------------------------------------------------------------ // copyright notice #define popplerCopyright "Copyright 2005-2024 The Poppler Developers - http://poppler.freedesktop.org" #define xpdfCopyright "Copyright 1996-2011, 2022 Glyph & Cog, LLC" //------------------------------------------------------------------------ // Win32 stuff //------------------------------------------------------------------------ #if defined(_WIN32) && !defined(_MSC_VER) #include #else #define CDECL #endif //------------------------------------------------------------------------ // Compiler //------------------------------------------------------------------------ #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) #include // __MINGW_PRINTF_FORMAT is defined in the mingw stdio.h #ifdef __MINGW_PRINTF_FORMAT #define GCC_PRINTF_FORMAT(fmt_index, va_index) \ __attribute__((__format__(__MINGW_PRINTF_FORMAT, fmt_index, va_index))) #else #define GCC_PRINTF_FORMAT(fmt_index, va_index) \ __attribute__((__format__(__printf__, fmt_index, va_index))) #endif #else #define GCC_PRINTF_FORMAT(fmt_index, va_index) #endif #endif /* POPPLER_CONFIG_H */ poppler-24.02.0/qt5/000077500000000000000000000000001455701731300140575ustar00rootroot00000000000000poppler-24.02.0/qt5/.gitignore000066400000000000000000000000311455701731300160410ustar00rootroot00000000000000Makefile Makefile.in *~ poppler-24.02.0/qt5/CMakeLists.txt000066400000000000000000000007201455701731300166160ustar00rootroot00000000000000set(CMAKE_AUTOMOC ON) set(ENABLE_QT_STRICT_ITERATORS ON CACHE BOOL "Select whether to compile with QT_STRICT_ITERATORS. Leave it ON, unless your Qt lacks support, or your compiler can't do SRA optimization.") if(ENABLE_QT_STRICT_ITERATORS) add_definitions(-DQT_STRICT_ITERATORS) endif() add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x050C00) add_definitions(-DQT_NO_DEPRECATED_WARNINGS) add_subdirectory(src) add_subdirectory(tests) add_subdirectory(demos) poppler-24.02.0/qt5/demos/000077500000000000000000000000001455701731300151665ustar00rootroot00000000000000poppler-24.02.0/qt5/demos/.gitignore000066400000000000000000000000431455701731300171530ustar00rootroot00000000000000.deps .libs *moc poppler_qt5viewer poppler-24.02.0/qt5/demos/CMakeLists.txt000066400000000000000000000011241455701731300177240ustar00rootroot00000000000000include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../src ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/../src ) set(poppler_qt5viewer_SRCS abstractinfodock.cpp documentobserver.cpp embeddedfiles.cpp fonts.cpp info.cpp main_viewer.cpp metadata.cpp navigationtoolbar.cpp optcontent.cpp pageview.cpp permissions.cpp thumbnails.cpp toc.cpp viewer.cpp ) poppler_add_test(poppler_qt5viewer BUILD_QT5_TESTS ${poppler_qt5viewer_SRCS}) target_link_libraries(poppler_qt5viewer poppler-qt5 Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Xml) poppler-24.02.0/qt5/demos/abstractinfodock.cpp000066400000000000000000000026451455701731300212210ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * * 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 "fonts.h" AbstractInfoDock::AbstractInfoDock(QWidget *parent) : QDockWidget(parent), m_filled(false) { connect(this, &AbstractInfoDock::visibilityChanged, this, &AbstractInfoDock::slotVisibilityChanged); } AbstractInfoDock::~AbstractInfoDock() { } void AbstractInfoDock::documentLoaded() { if (!isHidden()) { fillInfo(); m_filled = true; } } void AbstractInfoDock::documentClosed() { m_filled = false; } void AbstractInfoDock::pageChanged(int page) { Q_UNUSED(page) } void AbstractInfoDock::slotVisibilityChanged(bool visible) { if (visible && document() && !m_filled) { fillInfo(); m_filled = true; } } poppler-24.02.0/qt5/demos/abstractinfodock.h000066400000000000000000000025571455701731300206700ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * Copyright (C) 2021, 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 * 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 ABSTRACTINFODOCK_H #define ABSTRACTINFODOCK_H #include #include "documentobserver.h" class AbstractInfoDock : public QDockWidget, public DocumentObserver { Q_OBJECT public: explicit AbstractInfoDock(QWidget *parent = nullptr); ~AbstractInfoDock() override; void documentLoaded() override; void documentClosed() override; void pageChanged(int page) override; protected: virtual void fillInfo() = 0; private Q_SLOTS: void slotVisibilityChanged(bool visible); private: bool m_filled; }; #endif poppler-24.02.0/qt5/demos/documentobserver.cpp000066400000000000000000000023231455701731300212600ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * * 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 "documentobserver.h" #include "viewer.h" DocumentObserver::DocumentObserver() : m_viewer(nullptr) { } DocumentObserver::~DocumentObserver() { } Poppler::Document *DocumentObserver::document() const { return m_viewer->m_doc; } void DocumentObserver::setPage(int page) { m_viewer->setPage(page); } int DocumentObserver::page() const { return m_viewer->page(); } void DocumentObserver::reloadPage() { m_viewer->setPage(m_viewer->page()); } poppler-24.02.0/qt5/demos/documentobserver.h000066400000000000000000000027021455701731300207260ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * Copyright (C) 2018, 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 * 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 DOCUMENTOBSERVER_H #define DOCUMENTOBSERVER_H class PdfViewer; namespace Poppler { class Document; } class DocumentObserver { friend class PdfViewer; public: virtual ~DocumentObserver(); DocumentObserver(const DocumentObserver &) = delete; DocumentObserver &operator=(const DocumentObserver &) = delete; virtual void documentLoaded() = 0; virtual void documentClosed() = 0; virtual void pageChanged(int page) = 0; protected: DocumentObserver(); Poppler::Document *document() const; void setPage(int page); int page() const; void reloadPage(); private: PdfViewer *m_viewer; }; #endif poppler-24.02.0/qt5/demos/embeddedfiles.cpp000066400000000000000000000054721455701731300204560ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * * 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 "embeddedfiles.h" #include #include EmbeddedFilesDock::EmbeddedFilesDock(QWidget *parent) : AbstractInfoDock(parent) { m_table = new QTableWidget(this); setWidget(m_table); setWindowTitle(tr("Embedded files")); m_table->setColumnCount(6); m_table->setHorizontalHeaderLabels(QStringList() << tr("Name") << tr("Description") << tr("Size") << tr("Creation date") << tr("Modification date") << tr("Checksum")); m_table->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); } EmbeddedFilesDock::~EmbeddedFilesDock() { } void EmbeddedFilesDock::fillInfo() { m_table->setHorizontalHeaderLabels(QStringList() << tr("Name") << tr("Description") << tr("Size") << tr("Creation date") << tr("Modification date") << tr("Checksum")); if (!document()->hasEmbeddedFiles()) { m_table->setItem(0, 0, new QTableWidgetItem(tr("No files"))); return; } const QList files = document()->embeddedFiles(); m_table->setRowCount(files.count()); int i = 0; Q_FOREACH (Poppler::EmbeddedFile *file, files) { m_table->setItem(i, 0, new QTableWidgetItem(file->name())); m_table->setItem(i, 1, new QTableWidgetItem(file->description())); m_table->setItem(i, 2, new QTableWidgetItem(QString::number(file->size()))); m_table->setItem(i, 3, new QTableWidgetItem(file->createDate().toString(Qt::SystemLocaleDate))); m_table->setItem(i, 4, new QTableWidgetItem(file->modDate().toString(Qt::SystemLocaleDate))); const QByteArray checksum = file->checksum(); const QString checksumString = !checksum.isEmpty() ? QString::fromLatin1(checksum.toHex()) : QStringLiteral("n/a"); m_table->setItem(i, 5, new QTableWidgetItem(checksumString)); ++i; } } void EmbeddedFilesDock::documentLoaded() { if (document()->pageMode() == Poppler::Document::UseAttach) { show(); } } void EmbeddedFilesDock::documentClosed() { m_table->clear(); m_table->setRowCount(0); AbstractInfoDock::documentClosed(); } poppler-24.02.0/qt5/demos/embeddedfiles.h000066400000000000000000000023431455701731300201150ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * Copyright (C) 2021, 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 * 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 ATTACHMENTS_H #define ATTACHMENTS_H #include "abstractinfodock.h" class QTableWidget; class EmbeddedFilesDock : public AbstractInfoDock { Q_OBJECT public: explicit EmbeddedFilesDock(QWidget *parent = nullptr); ~EmbeddedFilesDock() override; void documentLoaded() override; void documentClosed() override; protected: void fillInfo() override; private: QTableWidget *m_table; }; #endif poppler-24.02.0/qt5/demos/fonts.cpp000066400000000000000000000045461455701731300170340ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * * 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 "fonts.h" #include #include static QString yesNoStatement(bool value) { return value ? QStringLiteral("yes") : QStringLiteral("no"); } FontsDock::FontsDock(QWidget *parent) : AbstractInfoDock(parent) { m_table = new QTableWidget(this); setWidget(m_table); setWindowTitle(tr("Fonts")); m_table->setColumnCount(5); m_table->setHorizontalHeaderLabels(QStringList() << tr("Name") << tr("Type") << tr("Embedded") << tr("Subset") << tr("File")); m_table->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); } FontsDock::~FontsDock() { } void FontsDock::fillInfo() { const QList fonts = document()->fonts(); m_table->setHorizontalHeaderLabels(QStringList() << tr("Name") << tr("Type") << tr("Embedded") << tr("Subset") << tr("File")); m_table->setRowCount(fonts.count()); int i = 0; Q_FOREACH (const Poppler::FontInfo &font, fonts) { if (font.name().isNull()) { m_table->setItem(i, 0, new QTableWidgetItem(QStringLiteral("[none]"))); } else { m_table->setItem(i, 0, new QTableWidgetItem(font.name())); } m_table->setItem(i, 1, new QTableWidgetItem(font.typeName())); m_table->setItem(i, 2, new QTableWidgetItem(yesNoStatement(font.isEmbedded()))); m_table->setItem(i, 3, new QTableWidgetItem(yesNoStatement(font.isSubset()))); m_table->setItem(i, 4, new QTableWidgetItem(font.file())); ++i; } } void FontsDock::documentClosed() { m_table->clear(); m_table->setRowCount(0); AbstractInfoDock::documentClosed(); } poppler-24.02.0/qt5/demos/fonts.h000066400000000000000000000022331455701731300164700ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * Copyright (C) 2021, 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 * 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 FONTS_H #define FONTS_H #include "abstractinfodock.h" class QTableWidget; class FontsDock : public AbstractInfoDock { Q_OBJECT public: explicit FontsDock(QWidget *parent = nullptr); ~FontsDock() override; void documentClosed() override; protected: void fillInfo() override; private: QTableWidget *m_table; }; #endif poppler-24.02.0/qt5/demos/info.cpp000066400000000000000000000043041455701731300166260ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * * 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 "info.h" #include #include InfoDock::InfoDock(QWidget *parent) : AbstractInfoDock(parent) { m_table = new QTableWidget(this); setWidget(m_table); setWindowTitle(tr("Information")); m_table->setColumnCount(2); m_table->setHorizontalHeaderLabels(QStringList() << tr("Key") << tr("Value")); m_table->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); } InfoDock::~InfoDock() { } void InfoDock::fillInfo() { QStringList keys = document()->infoKeys(); m_table->setHorizontalHeaderLabels(QStringList() << tr("Key") << tr("Value")); m_table->setRowCount(keys.count()); QStringList dateKeys; dateKeys << QStringLiteral("CreationDate"); dateKeys << QStringLiteral("ModDate"); int i = 0; Q_FOREACH (const QString &date, dateKeys) { const int id = keys.indexOf(date); if (id != -1) { m_table->setItem(i, 0, new QTableWidgetItem(date)); m_table->setItem(i, 1, new QTableWidgetItem(document()->date(date).toString(Qt::SystemLocaleDate))); ++i; keys.removeAt(id); } } Q_FOREACH (const QString &key, keys) { m_table->setItem(i, 0, new QTableWidgetItem(key)); m_table->setItem(i, 1, new QTableWidgetItem(document()->info(key))); ++i; } } void InfoDock::documentClosed() { m_table->clear(); m_table->setRowCount(0); AbstractInfoDock::documentClosed(); } poppler-24.02.0/qt5/demos/info.h000066400000000000000000000022261455701731300162740ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * Copyright (C) 2021, 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 * 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 INFO_H #define INFO_H #include "abstractinfodock.h" class QTableWidget; class InfoDock : public AbstractInfoDock { Q_OBJECT public: explicit InfoDock(QWidget *parent = nullptr); ~InfoDock() override; void documentClosed() override; protected: void fillInfo() override; private: QTableWidget *m_table; }; #endif poppler-24.02.0/qt5/demos/main_viewer.cpp000066400000000000000000000021311455701731300201740ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * * 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 "viewer.h" #include int main(int argc, char *argv[]) { QApplication app(argc, argv); const QStringList args = QCoreApplication::arguments(); PdfViewer *viewer = new PdfViewer(); viewer->show(); if (args.count() > 1) { viewer->loadDocument(args.at(1)); } return app.exec(); } poppler-24.02.0/qt5/demos/metadata.cpp000066400000000000000000000024321455701731300174530ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * * 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 "metadata.h" #include #include MetadataDock::MetadataDock(QWidget *parent) : AbstractInfoDock(parent) { m_edit = new QTextEdit(this); setWidget(m_edit); setWindowTitle(tr("Metadata")); m_edit->setAcceptRichText(false); m_edit->setReadOnly(true); } MetadataDock::~MetadataDock() { } void MetadataDock::fillInfo() { m_edit->setPlainText(document()->metadata()); } void MetadataDock::documentClosed() { m_edit->clear(); AbstractInfoDock::documentClosed(); } poppler-24.02.0/qt5/demos/metadata.h000066400000000000000000000022431455701731300171200ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * Copyright (C) 2021, 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 * 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 METADATA_H #define METADATA_H #include "abstractinfodock.h" class QTextEdit; class MetadataDock : public AbstractInfoDock { Q_OBJECT public: explicit MetadataDock(QWidget *parent = nullptr); ~MetadataDock() override; void documentClosed() override; protected: void fillInfo() override; private: QTextEdit *m_edit; }; #endif poppler-24.02.0/qt5/demos/navigationtoolbar.cpp000066400000000000000000000103341455701731300214150ustar00rootroot00000000000000/* * Copyright (C) 2008-2009, Pino Toscano * Copyright (C) 2013, Fabio D'Urso * Copyright (C) 2019, 2022, 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 * 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 "navigationtoolbar.h" #include #include #include NavigationToolBar::NavigationToolBar(QWidget *parent) : QToolBar(parent) { m_firstAct = addAction(tr("First"), this, SLOT(slotGoFirst())); m_prevAct = addAction(tr("Previous"), this, SLOT(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())); addSeparator(); m_zoomCombo = new QComboBox(this); m_zoomCombo->setEditable(true); m_zoomCombo->addItem(tr("10%")); m_zoomCombo->addItem(tr("25%")); m_zoomCombo->addItem(tr("33%")); m_zoomCombo->addItem(tr("50%")); m_zoomCombo->addItem(tr("66%")); m_zoomCombo->addItem(tr("75%")); m_zoomCombo->addItem(tr("100%")); m_zoomCombo->addItem(tr("125%")); m_zoomCombo->addItem(tr("150%")); m_zoomCombo->addItem(tr("200%")); m_zoomCombo->addItem(tr("300%")); m_zoomCombo->addItem(tr("400%")); m_zoomCombo->setCurrentIndex(6); // "100%" connect(m_zoomCombo, qOverload(&QComboBox::currentIndexChanged), this, &NavigationToolBar::slotZoomComboChanged); addWidget(m_zoomCombo); m_rotationCombo = new QComboBox(this); // NOTE: \302\260 = degree symbol m_rotationCombo->addItem(tr("0\302\260")); m_rotationCombo->addItem(tr("90\302\260")); m_rotationCombo->addItem(tr("180\302\260")); m_rotationCombo->addItem(tr("270\302\260")); connect(m_rotationCombo, qOverload(&QComboBox::currentIndexChanged), this, &NavigationToolBar::slotRotationComboChanged); addWidget(m_rotationCombo); documentClosed(); } NavigationToolBar::~NavigationToolBar() { } void NavigationToolBar::documentLoaded() { const int pageCount = document()->numPages(); for (int i = 0; i < pageCount; ++i) { m_pageCombo->addItem(QString::number(i + 1)); } m_pageCombo->setEnabled(true); } void NavigationToolBar::documentClosed() { m_firstAct->setEnabled(false); m_prevAct->setEnabled(false); m_nextAct->setEnabled(false); m_lastAct->setEnabled(false); m_pageCombo->clear(); m_pageCombo->setEnabled(false); } void NavigationToolBar::pageChanged(int page) { const int pageCount = document()->numPages(); m_firstAct->setEnabled(page > 0); m_prevAct->setEnabled(page > 0); m_nextAct->setEnabled(page < (pageCount - 1)); m_lastAct->setEnabled(page < (pageCount - 1)); m_pageCombo->setCurrentIndex(page); } void NavigationToolBar::slotGoFirst() { setPage(0); } void NavigationToolBar::slotGoPrev() { setPage(page() - 1); } void NavigationToolBar::slotGoNext() { setPage(page() + 1); } void NavigationToolBar::slotGoLast() { setPage(document()->numPages() - 1); } void NavigationToolBar::slotComboActivated(int index) { setPage(index); } void NavigationToolBar::slotZoomComboChanged(const QString &_text) { QString text = _text; text.remove(QLatin1Char('%')); bool ok = false; int value = text.toInt(&ok); if (ok && value >= 10) { emit zoomChanged(qreal(value) / 100); } } void NavigationToolBar::slotRotationComboChanged(int idx) { emit rotationChanged(idx * 90); } poppler-24.02.0/qt5/demos/navigationtoolbar.h000066400000000000000000000037331455701731300210670ustar00rootroot00000000000000/* * Copyright (C) 2008-2009, Pino Toscano * Copyright (C) 2013, Fabio D'Urso * Copyright (C) 2019, 2021, 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 * 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 NAVIGATIONTOOLBAR_H #define NAVIGATIONTOOLBAR_H #include #include "documentobserver.h" class QAction; class QComboBox; class NavigationToolBar : public QToolBar, public DocumentObserver { Q_OBJECT public: explicit NavigationToolBar(QWidget *parent = nullptr); ~NavigationToolBar() override; void documentLoaded() override; void documentClosed() override; void pageChanged(int page) override; Q_SIGNALS: void zoomChanged(qreal value); // NOLINT(readability-inconsistent-declaration-parameter-name) void rotationChanged(int rotation); // NOLINT(readability-inconsistent-declaration-parameter-name) private Q_SLOTS: void slotGoFirst(); void slotGoPrev(); void slotGoNext(); void slotGoLast(); void slotComboActivated(int index); void slotZoomComboChanged(const QString &text); void slotRotationComboChanged(int idx); private: QAction *m_firstAct; QAction *m_prevAct; QComboBox *m_pageCombo; QAction *m_nextAct; QAction *m_lastAct; QComboBox *m_zoomCombo; QComboBox *m_rotationCombo; }; #endif poppler-24.02.0/qt5/demos/optcontent.cpp000066400000000000000000000033541455701731300200740ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * * 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 "optcontent.h" #include #include OptContentDock::OptContentDock(QWidget *parent) : AbstractInfoDock(parent) { m_view = new QTreeView(this); setWidget(m_view); setWindowTitle(tr("Optional content")); m_view->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); } OptContentDock::~OptContentDock() { } void OptContentDock::documentLoaded() { AbstractInfoDock::documentLoaded(); if (document()->pageMode() == Poppler::Document::UseOC) { show(); } } void OptContentDock::fillInfo() { if (!document()->hasOptionalContent()) { return; } m_view->setModel(document()->optionalContentModel()); connect(m_view->model(), &QAbstractItemModel::dataChanged, this, &OptContentDock::reloadImage); m_view->expandToDepth(1); } void OptContentDock::documentClosed() { m_view->setModel(nullptr); AbstractInfoDock::documentClosed(); } void OptContentDock::reloadImage() { reloadPage(); } poppler-24.02.0/qt5/demos/optcontent.h000066400000000000000000000023731455701731300175410ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * Copyright (C) 2021, 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 * 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 OPTCONTENT_H #define OPTCONTENT_H #include "abstractinfodock.h" class QTreeView; class OptContentDock : public AbstractInfoDock { Q_OBJECT public: explicit OptContentDock(QWidget *parent = nullptr); ~OptContentDock() override; void documentLoaded() override; void documentClosed() override; protected: void fillInfo() override; private Q_SLOTS: void reloadImage(); private: QTreeView *m_view; }; #endif poppler-24.02.0/qt5/demos/pageview.cpp000066400000000000000000000053151455701731300175050ustar00rootroot00000000000000/* * Copyright (C) 2008-2009, Pino Toscano * Copyright (C) 2013, Fabio D'Urso * Copyright (C) 2017, 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 * 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 "pageview.h" #include #include #include #include #include #include #include PageView::PageView(QWidget *parent) : QScrollArea(parent), m_zoom(1.0), m_rotation(0), m_dpiX(QApplication::desktop()->physicalDpiX()), m_dpiY(QApplication::desktop()->physicalDpiY()) { m_imageLabel = new QLabel(this); m_imageLabel->resize(0, 0); setWidget(m_imageLabel); } PageView::~PageView() { } void PageView::documentLoaded() { } void PageView::documentClosed() { m_imageLabel->clear(); m_imageLabel->resize(0, 0); } void PageView::pageChanged(int page) { Poppler::Page *popplerPage = document()->page(page); if (!popplerPage) { qDebug() << "Page" << page << "is malformed"; return; } const double resX = m_dpiX * m_zoom; const double resY = m_dpiY * m_zoom; Poppler::Page::Rotation rot; if (m_rotation == 0) { rot = Poppler::Page::Rotate0; } else if (m_rotation == 90) { rot = Poppler::Page::Rotate90; } else if (m_rotation == 180) { rot = Poppler::Page::Rotate180; } else { // m_rotation == 270 rot = Poppler::Page::Rotate270; } QImage image = popplerPage->renderToImage(resX, resY, -1, -1, -1, -1, rot); if (!image.isNull()) { m_imageLabel->resize(image.size()); m_imageLabel->setPixmap(QPixmap::fromImage(image)); } else { m_imageLabel->resize(0, 0); m_imageLabel->setPixmap(QPixmap()); } delete popplerPage; } void PageView::slotZoomChanged(qreal value) { m_zoom = value; if (!document()) { return; } reloadPage(); } void PageView::slotRotationChanged(int value) { m_rotation = value; if (!document()) { return; } reloadPage(); } poppler-24.02.0/qt5/demos/pageview.h000066400000000000000000000027301455701731300171500ustar00rootroot00000000000000/* * Copyright (C) 2008-2009, Pino Toscano * Copyright (C) 2013, Fabio D'Urso * Copyright (C) 2021, 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 * 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 PAGEVIEW_H #define PAGEVIEW_H #include #include "documentobserver.h" class QLabel; class PageView : public QScrollArea, public DocumentObserver { Q_OBJECT public: explicit PageView(QWidget *parent = nullptr); ~PageView() override; void documentLoaded() override; void documentClosed() override; void pageChanged(int page) override; public Q_SLOTS: void slotZoomChanged(qreal value); void slotRotationChanged(int value); private: QLabel *m_imageLabel; qreal m_zoom; int m_rotation; int m_dpiX; int m_dpiY; }; #endif poppler-24.02.0/qt5/demos/permissions.cpp000066400000000000000000000065101455701731300202470ustar00rootroot00000000000000/* * Copyright (C) 2008-2009, Pino Toscano * * 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 "permissions.h" #include #include PermissionsDock::PermissionsDock(QWidget *parent) : AbstractInfoDock(parent) { m_table = new QListWidget(this); setWidget(m_table); setWindowTitle(tr("Permissions")); m_table->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); } PermissionsDock::~PermissionsDock() { } void PermissionsDock::fillInfo() { #define ADD_ROW(title, function) \ do { \ QListWidgetItem *item = new QListWidgetItem(); \ item->setFlags(item->flags() & ~Qt::ItemIsEnabled); \ item->setText(QStringLiteral(title)); \ item->setCheckState(document()->function() ? Qt::Checked : Qt::Unchecked); \ m_table->addItem(item); \ } while (0) ADD_ROW("Print", okToPrint); ADD_ROW("PrintHiRes", okToPrintHighRes); ADD_ROW("Change", okToChange); ADD_ROW("Copy", okToCopy); ADD_ROW("Add Notes", okToAddNotes); ADD_ROW("Fill Forms", okToFillForm); ADD_ROW("Create Forms", okToCreateFormFields); ADD_ROW("Extract for accessibility", okToExtractForAccessibility); ADD_ROW("Assemble", okToAssemble); #undef ADD_ROW } void PermissionsDock::documentClosed() { m_table->clear(); AbstractInfoDock::documentClosed(); } poppler-24.02.0/qt5/demos/permissions.h000066400000000000000000000022741455701731300177170ustar00rootroot00000000000000/* * Copyright (C) 2008-2009, Pino Toscano * Copyright (C) 2021, 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 * 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 PERMISSIONS_H #define PERMISSIONS_H #include "abstractinfodock.h" class QListWidget; class PermissionsDock : public AbstractInfoDock { Q_OBJECT public: explicit PermissionsDock(QWidget *parent = nullptr); ~PermissionsDock() override; void documentClosed() override; protected: void fillInfo() override; private: QListWidget *m_table; }; #endif poppler-24.02.0/qt5/demos/thumbnails.cpp000066400000000000000000000050011455701731300200340ustar00rootroot00000000000000/* * Copyright (C) 2009, Shawn Rutledge * Copyright (C) 2009, Pino Toscano * Copyright (C) 2020, 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 * 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 "thumbnails.h" #include #include static const int PageRole = Qt::UserRole + 1; ThumbnailsDock::ThumbnailsDock(QWidget *parent) : AbstractInfoDock(parent) { m_list = new QListWidget(this); setWidget(m_list); setWindowTitle(tr("Thumbnails")); m_list->setViewMode(QListView::ListMode); m_list->setMovement(QListView::Static); m_list->setVerticalScrollMode(QListView::ScrollPerPixel); connect(m_list, &QListWidget::itemActivated, this, &ThumbnailsDock::slotItemActivated); } ThumbnailsDock::~ThumbnailsDock() { } void ThumbnailsDock::fillInfo() { const int num = document()->numPages(); QSize maxSize; for (int i = 0; i < num; ++i) { const Poppler::Page *page = document()->page(i); const QImage image = page ? page->thumbnail() : QImage(); if (!image.isNull()) { QListWidgetItem *item = new QListWidgetItem(); item->setText(QString::number(i + 1)); item->setData(Qt::DecorationRole, QPixmap::fromImage(image)); item->setData(PageRole, i); m_list->addItem(item); maxSize.setWidth(qMax(maxSize.width(), image.width())); maxSize.setHeight(qMax(maxSize.height(), image.height())); } delete page; } if (num > 0) { m_list->setGridSize(maxSize); m_list->setIconSize(maxSize); } } void ThumbnailsDock::documentClosed() { m_list->clear(); AbstractInfoDock::documentClosed(); } void ThumbnailsDock::slotItemActivated(QListWidgetItem *item) { if (!item) { return; } setPage(item->data(PageRole).toInt()); } poppler-24.02.0/qt5/demos/thumbnails.h000066400000000000000000000025201455701731300175040ustar00rootroot00000000000000/* * Copyright (C) 2009, Shawn Rutledge * Copyright (C) 2009, Pino Toscano * Copyright (C) 2021, 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 * 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 THUMBNAILS_H #define THUMBNAILS_H #include "abstractinfodock.h" class QListWidget; class QListWidgetItem; class ThumbnailsDock : public AbstractInfoDock { Q_OBJECT public: explicit ThumbnailsDock(QWidget *parent = nullptr); ~ThumbnailsDock() override; void documentClosed() override; protected: void fillInfo() override; private Q_SLOTS: void slotItemActivated(QListWidgetItem *item); private: QListWidget *m_list; }; #endif poppler-24.02.0/qt5/demos/toc.cpp000066400000000000000000000114601455701731300164610ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * Copyright (C) 2018, Adam Reichold * Copyright (C) 2019, 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 * 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 "toc.h" #include #include #include #include #include struct Node { Node(Poppler::OutlineItem &&item, int row, Node *parent) : m_row(row), m_parent(parent), m_item(std::move(item)) { } ~Node() { qDeleteAll(m_children); } Node(const Node &) = delete; Node &operator=(const Node &) = delete; int m_row; Node *m_parent; Poppler::OutlineItem m_item; QVector m_children; }; class TocModel : public QAbstractItemModel { Q_OBJECT public: TocModel(QVector &&items, QObject *parent) : QAbstractItemModel(parent) { for (int i = 0; i < items.count(); ++i) { m_topItems << new Node(std::move(items[i]), i, nullptr); } } ~TocModel() override { qDeleteAll(m_topItems); } QVariant data(const QModelIndex &index, int role) const override { if (role != Qt::DisplayRole) { return {}; } Node *n = static_cast(index.internalPointer()); return n->m_item.name(); } QModelIndex index(int row, int column, const QModelIndex &parent) const override { Node *p = static_cast(parent.internalPointer()); const QVector &children = p ? p->m_children : m_topItems; return createIndex(row, column, children[row]); } QModelIndex parent(const QModelIndex &child) const override { Node *n = static_cast(child.internalPointer()); if (n->m_parent == nullptr) { return QModelIndex(); } else { return createIndex(n->m_parent->m_row, 0, n->m_parent); } } int rowCount(const QModelIndex &parent) const override { Node *n = static_cast(parent.internalPointer()); if (!n) { return m_topItems.count(); } if (n->m_children.isEmpty() && !n->m_item.isNull()) { QVector items = n->m_item.children(); for (int i = 0; i < items.count(); ++i) { n->m_children << new Node(std::move(items[i]), i, n); } } return n->m_children.count(); } bool hasChildren(const QModelIndex &parent) const override { Node *n = static_cast(parent.internalPointer()); if (!n) { return true; } return n->m_item.hasChildren(); } int columnCount(const QModelIndex &parent) const override { return 1; } private: QVector m_topItems; }; TocDock::TocDock(QWidget *parent) : AbstractInfoDock(parent) { m_tree = new QTreeView(this); setWidget(m_tree); m_tree->setAlternatingRowColors(true); m_tree->header()->hide(); setWindowTitle(tr("TOC")); m_tree->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); } TocDock::~TocDock() { } void TocDock::expandItemModels(const QModelIndex &parent) { TocModel *model = static_cast(m_tree->model()); for (int i = 0; i < model->rowCount(parent); ++i) { QModelIndex index = model->index(i, 0, parent); Node *n = static_cast(index.internalPointer()); if (n->m_item.isOpen()) { m_tree->setExpanded(index, true); expandItemModels(index); } } } void TocDock::fillInfo() { auto outline = document()->outline(); if (!outline.isEmpty()) { TocModel *model = new TocModel(std::move(outline), this); m_tree->setModel(model); expandItemModels(QModelIndex()); } else { QStandardItemModel *model = new QStandardItemModel(this); QStandardItem *item = new QStandardItem(tr("No TOC")); item->setFlags(item->flags() & ~Qt::ItemIsEnabled); model->appendRow(item); m_tree->setModel(model); } } void TocDock::documentClosed() { m_tree->setModel(nullptr); AbstractInfoDock::documentClosed(); } #include "toc.moc" poppler-24.02.0/qt5/demos/toc.h000066400000000000000000000023061455701731300161250ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * Copyright (C) 2019, 2021, 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 * 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 TOC_H #define TOC_H #include "abstractinfodock.h" class QTreeView; class TocDock : public AbstractInfoDock { Q_OBJECT public: explicit TocDock(QWidget *parent = nullptr); ~TocDock() override; void documentClosed() override; protected: void fillInfo() override; void expandItemModels(const QModelIndex &parent); private: QTreeView *m_tree; }; #endif poppler-24.02.0/qt5/demos/viewer.cpp000066400000000000000000000250211455701731300171730ustar00rootroot00000000000000/* * Copyright (C) 2008-2009, Pino Toscano * Copyright (C) 2008, 2019, 2022, Albert Astals Cid * Copyright (C) 2009, Shawn Rutledge * Copyright (C) 2013, Fabio D'Urso * Copyright (C) 2020, Oliver Sander * Copyright (C) 2021, Mahmoud Khalil * * 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 "viewer.h" #include "embeddedfiles.h" #include "fonts.h" #include "info.h" #include "metadata.h" #include "navigationtoolbar.h" #include "optcontent.h" #include "pageview.h" #include "permissions.h" #include "thumbnails.h" #include "toc.h" #include #include #include #include #include #include #include #include #include #include PdfViewer::PdfViewer(QWidget *parent) : QMainWindow(parent), m_currentPage(0), m_doc(nullptr) { setWindowTitle(tr("Poppler-Qt5 Demo")); // setup the menus QMenu *fileMenu = menuBar()->addMenu(tr("&File")); m_fileOpenAct = fileMenu->addAction(tr("&Open"), this, &PdfViewer::slotOpenFile); m_fileOpenAct->setShortcut(static_cast(Qt::CTRL) + Qt::Key_O); fileMenu->addSeparator(); m_fileSaveCopyAct = fileMenu->addAction(tr("&Save a Copy..."), this, &PdfViewer::slotSaveCopy); m_fileSaveCopyAct->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_S); m_fileSaveCopyAct->setEnabled(false); fileMenu->addSeparator(); QAction *act = fileMenu->addAction(tr("&Quit"), qApp, &QApplication::closeAllWindows); act->setShortcut(static_cast(Qt::CTRL) + Qt::Key_Q); QMenu *viewMenu = menuBar()->addMenu(tr("&View")); QMenu *settingsMenu = menuBar()->addMenu(tr("&Settings")); m_settingsTextAAAct = settingsMenu->addAction(tr("Text Antialias")); m_settingsTextAAAct->setCheckable(true); connect(m_settingsTextAAAct, &QAction::toggled, this, &PdfViewer::slotToggleTextAA); m_settingsGfxAAAct = settingsMenu->addAction(tr("Graphics Antialias")); m_settingsGfxAAAct->setCheckable(true); connect(m_settingsGfxAAAct, &QAction::toggled, this, &PdfViewer::slotToggleGfxAA); QMenu *settingsRenderMenu = settingsMenu->addMenu(tr("Render Backend")); m_settingsRenderBackendGrp = new QActionGroup(settingsRenderMenu); m_settingsRenderBackendGrp->setExclusive(true); act = settingsRenderMenu->addAction(tr("Splash")); act->setCheckable(true); act->setChecked(true); act->setData(QVariant::fromValue(0)); m_settingsRenderBackendGrp->addAction(act); act = settingsRenderMenu->addAction(tr("QPainter")); act->setCheckable(true); act->setData(QVariant::fromValue(1)); m_settingsRenderBackendGrp->addAction(act); connect(m_settingsRenderBackendGrp, &QActionGroup::triggered, this, &PdfViewer::slotRenderBackend); QMenu *helpMenu = menuBar()->addMenu(tr("&Help")); act = helpMenu->addAction(tr("&About"), this, &PdfViewer::slotAbout); act = helpMenu->addAction(tr("About &Qt"), this, &PdfViewer::slotAboutQt); NavigationToolBar *navbar = new NavigationToolBar(this); addToolBar(navbar); m_observers.append(navbar); PageView *view = new PageView(this); setCentralWidget(view); m_observers.append(view); InfoDock *infoDock = new InfoDock(this); addDockWidget(Qt::LeftDockWidgetArea, infoDock); infoDock->hide(); viewMenu->addAction(infoDock->toggleViewAction()); m_observers.append(infoDock); TocDock *tocDock = new TocDock(this); addDockWidget(Qt::LeftDockWidgetArea, tocDock); tocDock->hide(); viewMenu->addAction(tocDock->toggleViewAction()); m_observers.append(tocDock); FontsDock *fontsDock = new FontsDock(this); addDockWidget(Qt::LeftDockWidgetArea, fontsDock); fontsDock->hide(); viewMenu->addAction(fontsDock->toggleViewAction()); m_observers.append(fontsDock); PermissionsDock *permissionsDock = new PermissionsDock(this); addDockWidget(Qt::LeftDockWidgetArea, permissionsDock); permissionsDock->hide(); viewMenu->addAction(permissionsDock->toggleViewAction()); m_observers.append(permissionsDock); ThumbnailsDock *thumbnailsDock = new ThumbnailsDock(this); addDockWidget(Qt::LeftDockWidgetArea, thumbnailsDock); thumbnailsDock->hide(); viewMenu->addAction(thumbnailsDock->toggleViewAction()); m_observers.append(thumbnailsDock); EmbeddedFilesDock *embfilesDock = new EmbeddedFilesDock(this); addDockWidget(Qt::BottomDockWidgetArea, embfilesDock); embfilesDock->hide(); viewMenu->addAction(embfilesDock->toggleViewAction()); m_observers.append(embfilesDock); MetadataDock *metadataDock = new MetadataDock(this); addDockWidget(Qt::BottomDockWidgetArea, metadataDock); metadataDock->hide(); viewMenu->addAction(metadataDock->toggleViewAction()); m_observers.append(metadataDock); OptContentDock *optContentDock = new OptContentDock(this); addDockWidget(Qt::LeftDockWidgetArea, optContentDock); optContentDock->hide(); viewMenu->addAction(optContentDock->toggleViewAction()); m_observers.append(optContentDock); Q_FOREACH (DocumentObserver *obs, m_observers) { obs->m_viewer = this; } connect(navbar, &NavigationToolBar::zoomChanged, view, &PageView::slotZoomChanged); connect(navbar, &NavigationToolBar::rotationChanged, view, &PageView::slotRotationChanged); // activate AA by default m_settingsTextAAAct->setChecked(true); m_settingsGfxAAAct->setChecked(true); } PdfViewer::~PdfViewer() { closeDocument(); } QSize PdfViewer::sizeHint() const { return QSize(500, 600); } void PdfViewer::loadDocument(const QString &file) { // resetting xrefReconstructed each time we load new document xrefReconstructed = false; Poppler::Document *newdoc = Poppler::Document::load(file); if (!newdoc) { QMessageBox msgbox(QMessageBox::Critical, tr("Open Error"), tr("Cannot open:\n") + file, QMessageBox::Ok, this); msgbox.exec(); return; } while (newdoc->isLocked()) { bool ok = true; QString password = QInputDialog::getText(this, tr("Document Password"), tr("Please insert the password of the document:"), QLineEdit::Password, QString(), &ok); if (!ok) { delete newdoc; return; } newdoc->unlock(password.toLatin1(), password.toLatin1()); } closeDocument(); m_doc = newdoc; m_doc->setRenderHint(Poppler::Document::TextAntialiasing, m_settingsTextAAAct->isChecked()); m_doc->setRenderHint(Poppler::Document::Antialiasing, m_settingsGfxAAAct->isChecked()); m_doc->setRenderBackend((Poppler::Document::RenderBackend)m_settingsRenderBackendGrp->checkedAction()->data().toInt()); if (m_doc->xrefWasReconstructed()) { xrefReconstructedHandler(m_doc); } else { std::function cb = [this]() { xrefReconstructedHandler(m_doc); }; m_doc->setXRefReconstructedCallback(cb); } Q_FOREACH (DocumentObserver *obs, m_observers) { obs->documentLoaded(); obs->pageChanged(0); } m_fileSaveCopyAct->setEnabled(true); } void PdfViewer::closeDocument() { if (!m_doc) { return; } Q_FOREACH (DocumentObserver *obs, m_observers) { obs->documentClosed(); } m_currentPage = 0; delete m_doc; m_doc = nullptr; m_fileSaveCopyAct->setEnabled(false); } void PdfViewer::xrefReconstructedHandler(Poppler::Document *doc) { if (!xrefReconstructed) { QMessageBox msgbox(QMessageBox::Critical, tr("File may be corrupted"), tr("The PDF may be broken but we're still showing something, contents may not be correct"), QMessageBox::Ok, this); msgbox.exec(); xrefReconstructed = true; } } void PdfViewer::slotOpenFile() { QString fileName = QFileDialog::getOpenFileName(this, tr("Open PDF Document"), QDir::homePath(), tr("PDF Documents (*.pdf)")); if (fileName.isEmpty()) { return; } loadDocument(fileName); } void PdfViewer::slotSaveCopy() { if (!m_doc) { return; } QString fileName = QFileDialog::getSaveFileName(this, tr("Save Copy"), QDir::homePath(), tr("PDF Documents (*.pdf)")); if (fileName.isEmpty()) { return; } Poppler::PDFConverter *converter = m_doc->pdfConverter(); converter->setOutputFileName(fileName); converter->setPDFOptions(converter->pdfOptions() & ~Poppler::PDFConverter::WithChanges); if (!converter->convert()) { QMessageBox msgbox(QMessageBox::Critical, tr("Save Error"), tr("Cannot export to:\n%1").arg(fileName), QMessageBox::Ok, this); } delete converter; } void PdfViewer::slotAbout() { QMessageBox::about(this, tr("About Poppler-Qt5 Demo"), tr("This is a demo of the Poppler-Qt5 library.")); } void PdfViewer::slotAboutQt() { QMessageBox::aboutQt(this); } void PdfViewer::slotToggleTextAA(bool value) { if (!m_doc) { return; } m_doc->setRenderHint(Poppler::Document::TextAntialiasing, value); Q_FOREACH (DocumentObserver *obs, m_observers) { obs->pageChanged(m_currentPage); } } void PdfViewer::slotToggleGfxAA(bool value) { if (!m_doc) { return; } m_doc->setRenderHint(Poppler::Document::Antialiasing, value); Q_FOREACH (DocumentObserver *obs, m_observers) { obs->pageChanged(m_currentPage); } } void PdfViewer::slotRenderBackend(QAction *act) { if (!m_doc || !act) { return; } m_doc->setRenderBackend((Poppler::Document::RenderBackend)act->data().toInt()); Q_FOREACH (DocumentObserver *obs, m_observers) { obs->pageChanged(m_currentPage); } } void PdfViewer::setPage(int page) { Q_FOREACH (DocumentObserver *obs, m_observers) { obs->pageChanged(page); } m_currentPage = page; } int PdfViewer::page() const { return m_currentPage; } poppler-24.02.0/qt5/demos/viewer.h000066400000000000000000000037501455701731300166450ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * Copyright (C) 2021, Mahmoud Khalil * Copyright (C) 2021, 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 * 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 PDFVIEWER_H #define PDFVIEWER_H #include class QAction; class QActionGroup; class QLabel; class DocumentObserver; namespace Poppler { class Document; } class PdfViewer : public QMainWindow { Q_OBJECT friend class DocumentObserver; public: explicit PdfViewer(QWidget *parent = nullptr); ~PdfViewer() override; QSize sizeHint() const override; void loadDocument(const QString &file); void closeDocument(); private Q_SLOTS: void slotOpenFile(); void slotSaveCopy(); void slotAbout(); void slotAboutQt(); void slotToggleTextAA(bool value); void slotToggleGfxAA(bool value); void slotRenderBackend(QAction *act); private: void setPage(int page); int page() const; void xrefReconstructedHandler(Poppler::Document *doc); int m_currentPage; bool xrefReconstructed; QAction *m_fileOpenAct; QAction *m_fileSaveCopyAct; QAction *m_settingsTextAAAct; QAction *m_settingsGfxAAAct; QActionGroup *m_settingsRenderBackendGrp; QList m_observers; Poppler::Document *m_doc; }; #endif poppler-24.02.0/qt5/src/000077500000000000000000000000001455701731300146465ustar00rootroot00000000000000poppler-24.02.0/qt5/src/.gitignore000066400000000000000000000001141455701731300166320ustar00rootroot00000000000000.deps .libs *.la *.lo Makefile Makefile.in APIDOCS-html APIDOCS-latex *.moc poppler-24.02.0/qt5/src/CMakeLists.txt000066400000000000000000000041541455701731300174120ustar00rootroot00000000000000add_definitions(-DQT_NO_SIGNALS_SLOTS_KEYWORDS) include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${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 poppler-annotation.cc poppler-document.cc poppler-embeddedfile.cc poppler-fontinfo.cc poppler-form.cc poppler-link.cc poppler-link-extractor.cc poppler-movie.cc poppler-optcontent.cc poppler-page.cc poppler-base-converter.cc poppler-pdf-converter.cc poppler-private.cc poppler-ps-converter.cc poppler-qiodeviceinstream.cc poppler-qiodeviceoutstream.cc poppler-sound.cc poppler-textbox.cc poppler-page-transition.cc poppler-media.cc poppler-outline.cc QPainterOutputDev.cc poppler-version.cpp ) 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.33.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}") endif() target_link_libraries(poppler-qt5 poppler Qt5::Core Qt5::Gui Qt5::Xml Freetype::Freetype) if (ENABLE_NSS3) target_include_directories(poppler-qt5 SYSTEM PRIVATE ${NSS3_INCLUDE_DIRS}) 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 poppler-qt5.h poppler-link.h poppler-annotation.h poppler-form.h poppler-optcontent.h poppler-page-transition.h poppler-media.h ${CMAKE_CURRENT_BINARY_DIR}/poppler-export.h ${CMAKE_CURRENT_BINARY_DIR}/poppler-version.h DESTINATION include/poppler/qt5) poppler-24.02.0/qt5/src/Doxyfile000066400000000000000000002054021455701731300163570ustar00rootroot00000000000000# Doxyfile 1.7.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = "Poppler Qt5" # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = 24.02.0 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = NO # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this # tag. The format is ext=language, where ext is a file extension, and language # is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen to replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penality. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will rougly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = NO # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = YES # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = NO # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = YES # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = YES # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. The create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text " # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = Mainpage.dox \ poppler-annotation.h \ poppler-form.h \ poppler-link.h \ poppler-qt5.h \ poppler-optcontent.h \ poppler-page-transition.h # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = APIDOCS-html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the stylesheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = YES # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = poppler-qt5.qch # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.freedesktop.poppler.qt5 # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = "Poppler 0.15.0" # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = poppler # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = poppler # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = qhelpgenerator # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = NO # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvances is that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = YES # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = APIDOCS-latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = YES # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = "Q_DECL_DEPRECATED=" \ "POPPLER_QT5_EXPORT=" # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans.ttf # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES poppler-24.02.0/qt5/src/Mainpage.dox000066400000000000000000000053361455701731300171120ustar00rootroot00000000000000/** @mainpage The Poppler Qt5 interface library The %Poppler Qt5 interface library, libpoppler-qt5, is a library that allows Qt5 programmers to easily load and render PDF files. The %Poppler Qt5 interface library uses poppler internally to do its job, but the Qt5 programmer will never have to worry about poppler internals. @section help Current Status The %Poppler Qt5 interface library is quite stable and working. @section refimpl Example Programs Examples programs can be found in the qt5/test directory. The %Poppler Qt5 interface library is also used in the KDE's document viewer Okular. The source files for Okular's PDF plugin (%Poppler-based) can be found on the git server of the KDE project, under this URL. @section req How to use the Poppler Qt5 interface library in three easy steps Programmer who would like to use the %Poppler Qt5 interface library simply need to add the following line to their C++ source files: @code #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; Poppler::Document* document = Poppler::Document::load(filename); if (!document || document->isLocked()) { // ... error message .... delete document; return; } @endcode Pages can be rendered to QImages with the following commands: @code // Paranoid safety check if (document == 0) { // ... error message ... return; } // Access page of the PDF file Poppler::Page* pdfPage = document->page(pageNumber); // Document starts at page 0 if (pdfPage == 0) { // ... error message ... return; } // Generate a QImage of the rendered page QImage image = pdfPage->renderToImage(xres, yres, x, y, width, height); if (image.isNull()) { // ... error message ... return; } // ... use image ... // after the usage, the page must be deleted delete pdfPage; @endcode Finally, don't forget to destroy the document: @code delete document; @endcode */ poppler-24.02.0/qt5/src/QPainterOutputDev.cc000066400000000000000000001221401455701731300205600ustar00rootroot00000000000000//======================================================================== // // QPainterOutputDev.cc // // Copyright 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) 2005 Brad Hards // Copyright (C) 2005-2009, 2011, 2012, 2014, 2015, 2018, 2019, 2021, 2022 Albert Astals Cid // Copyright (C) 2008, 2010 Pino Toscano // Copyright (C) 2009, 2011 Carlos Garcia Campos // Copyright (C) 2009 Petr Gajdos // Copyright (C) 2010 Matthias Fauconneau // Copyright (C) 2011 Andreas Hartmetz // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2013 Dominik Haumann // Copyright (C) 2013 Mihai Niculescu // Copyright (C) 2017, 2018, 2020-2022 Oliver Sander // 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 // // 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 #include #include #include #include "goo/ft_utils.h" #include "goo/gfile.h" #include "GlobalParams.h" #include "Error.h" #include "Object.h" #include "GfxState.h" #include "GfxFont.h" #include "Link.h" #include "FontEncodingTables.h" #include #include #include "QPainterOutputDev.h" #include "Page.h" #include "Gfx.h" #include "PDFDoc.h" #include #include #include #include #include class QPainterOutputDevType3Font { public: QPainterOutputDevType3Font(PDFDoc *doc, const std::shared_ptr &font); const QPicture &getGlyph(int gid) const; private: PDFDoc *m_doc; std::shared_ptr m_font; mutable std::vector> glyphs; public: std::vector codeToGID; }; QPainterOutputDevType3Font::QPainterOutputDevType3Font(PDFDoc *doc, const std::shared_ptr &font) : m_doc(doc), m_font(font) { char *name; const Dict *charProcs = font->getCharProcs(); // Storage for the rendered glyphs glyphs.resize(charProcs->getLength()); // Compute the code-to-GID map char **enc = font->getEncoding(); codeToGID.resize(256); for (int i = 0; i < 256; ++i) { codeToGID[i] = 0; if (charProcs && (name = enc[i])) { for (int j = 0; j < charProcs->getLength(); j++) { if (strcmp(name, charProcs->getKey(j)) == 0) { codeToGID[i] = j; } } } } } const QPicture &QPainterOutputDevType3Font::getGlyph(int gid) const { if (!glyphs[gid]) { // Glyph has not been rendered before: render it now // Smallest box that contains all the glyphs from this font const double *fontBBox = m_font->getFontBBox(); PDFRectangle box(fontBBox[0], fontBBox[1], fontBBox[2], fontBBox[3]); Dict *resDict = m_font->getResources(); QPainter glyphPainter; glyphs[gid] = std::make_unique(); glyphPainter.begin(glyphs[gid].get()); auto output_dev = std::make_unique(&glyphPainter); auto gfx = std::make_unique(m_doc, output_dev.get(), resDict, &box, // pagebox nullptr // cropBox ); output_dev->startDoc(m_doc); output_dev->startPage(1, gfx->getState(), gfx->getXRef()); const Dict *charProcs = m_font->getCharProcs(); Object charProc = charProcs->getVal(gid); gfx->display(&charProc); glyphPainter.end(); } return *glyphs[gid]; } //------------------------------------------------------------------------ // QPainterOutputDev //------------------------------------------------------------------------ QPainterOutputDev::QPainterOutputDev(QPainter *painter) : m_lastTransparencyGroupPicture(nullptr), m_hintingPreference(QFont::PreferDefaultHinting) { m_painter.push(painter); m_currentBrush = QBrush(Qt::SolidPattern); auto error = FT_Init_FreeType(&m_ftLibrary); if (error) { qCritical() << "An error occurred will initializing the FreeType library"; } // as of FT 2.1.8, CID fonts are indexed by CID instead of GID FT_Int major, minor, patch; FT_Library_Version(m_ftLibrary, &major, &minor, &patch); m_useCIDs = major > 2 || (major == 2 && (minor > 1 || (minor == 1 && patch > 7))); } QPainterOutputDev::~QPainterOutputDev() { for (auto &codeToGID : m_codeToGIDCache) { gfree(const_cast(codeToGID.second)); } FT_Done_FreeType(m_ftLibrary); } void QPainterOutputDev::startDoc(PDFDoc *doc) { xref = doc->getXRef(); m_doc = doc; for (auto &codeToGID : m_codeToGIDCache) { gfree(const_cast(codeToGID.second)); } m_codeToGIDCache.clear(); } void QPainterOutputDev::startPage(int pageNum, GfxState *state, XRef *) { } void QPainterOutputDev::endPage() { } void QPainterOutputDev::saveState(GfxState *state) { m_currentPenStack.push(m_currentPen); m_currentBrushStack.push(m_currentBrush); m_rawFontStack.push(m_rawFont); m_type3FontStack.push(m_currentType3Font); m_codeToGIDStack.push(m_codeToGID); m_painter.top()->save(); } void QPainterOutputDev::restoreState(GfxState *state) { m_painter.top()->restore(); m_codeToGID = m_codeToGIDStack.top(); m_codeToGIDStack.pop(); m_rawFont = m_rawFontStack.top(); m_rawFontStack.pop(); m_currentType3Font = m_type3FontStack.top(); m_type3FontStack.pop(); m_currentBrush = m_currentBrushStack.top(); m_currentBrushStack.pop(); m_currentPen = m_currentPenStack.top(); m_currentPenStack.pop(); } void QPainterOutputDev::updateAll(GfxState *state) { OutputDev::updateAll(state); m_needFontUpdate = true; } // Set CTM (Current Transformation Matrix) to a fixed matrix void QPainterOutputDev::setDefaultCTM(const double *ctm) { m_painter.top()->setTransform(QTransform(ctm[0], ctm[1], ctm[2], ctm[3], ctm[4], ctm[5])); } // Update the CTM (Current Transformation Matrix), i.e., compose the old // CTM with a new matrix. void QPainterOutputDev::updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32) { updateLineDash(state); updateLineJoin(state); updateLineCap(state); updateLineWidth(state); QTransform update(m11, m12, m21, m22, m31, m32); // We could also set (rather than update) the painter transformation to state->getCMT(); m_painter.top()->setTransform(update, true); } void QPainterOutputDev::updateLineDash(GfxState *state) { double dashStart; const std::vector &dashPattern = state->getLineDash(&dashStart); // Special handling for zero-length patterns, i.e., solid lines. // Simply calling QPen::setDashPattern with an empty pattern does *not* // result in a solid line. Rather, the current pattern is unchanged. // See the implementation of the setDashPattern method in the file qpen.cpp. if (dashPattern.empty()) { m_currentPen.setStyle(Qt::SolidLine); m_painter.top()->setPen(m_currentPen); return; } QVector pattern(dashPattern.size()); double scaling = state->getLineWidth(); // Negative line widths are not allowed, width 0 counts as 'one pixel width'. if (scaling <= 0) { scaling = 1.0; } for (std::vector::size_type i = 0; i < dashPattern.size(); ++i) { // pdf measures the dash pattern in dots, but Qt uses the // line width as the unit. pattern[i] = dashPattern[i] / scaling; } m_currentPen.setDashPattern(pattern); m_currentPen.setDashOffset(dashStart); m_painter.top()->setPen(m_currentPen); } void QPainterOutputDev::updateFlatness(GfxState *state) { // qDebug() << "updateFlatness"; } void QPainterOutputDev::updateLineJoin(GfxState *state) { switch (state->getLineJoin()) { case 0: // The correct style here is Qt::SvgMiterJoin, *not* Qt::MiterJoin. // The two differ in what to do if the miter limit is exceeded. // See https://bugs.freedesktop.org/show_bug.cgi?id=102356 m_currentPen.setJoinStyle(Qt::SvgMiterJoin); break; case 1: m_currentPen.setJoinStyle(Qt::RoundJoin); break; case 2: m_currentPen.setJoinStyle(Qt::BevelJoin); break; } m_painter.top()->setPen(m_currentPen); } void QPainterOutputDev::updateLineCap(GfxState *state) { switch (state->getLineCap()) { case 0: m_currentPen.setCapStyle(Qt::FlatCap); break; case 1: m_currentPen.setCapStyle(Qt::RoundCap); break; case 2: m_currentPen.setCapStyle(Qt::SquareCap); break; } m_painter.top()->setPen(m_currentPen); } void QPainterOutputDev::updateMiterLimit(GfxState *state) { m_currentPen.setMiterLimit(state->getMiterLimit()); m_painter.top()->setPen(m_currentPen); } void QPainterOutputDev::updateLineWidth(GfxState *state) { m_currentPen.setWidthF(state->getLineWidth()); m_painter.top()->setPen(m_currentPen); // The updateLineDash method needs to know the line width, but it is sometimes // called before the updateLineWidth method. To make sure that the last call // to updateLineDash before a drawing operation is always with the correct line // width, we call it here, right after a change to the line width. updateLineDash(state); } void QPainterOutputDev::updateFillColor(GfxState *state) { GfxRGB rgb; QColor brushColour = m_currentBrush.color(); state->getFillRGB(&rgb); brushColour.setRgbF(colToDbl(rgb.r), colToDbl(rgb.g), colToDbl(rgb.b), brushColour.alphaF()); m_currentBrush.setColor(brushColour); } void QPainterOutputDev::updateStrokeColor(GfxState *state) { GfxRGB rgb; QColor penColour = m_currentPen.color(); state->getStrokeRGB(&rgb); penColour.setRgbF(colToDbl(rgb.r), colToDbl(rgb.g), colToDbl(rgb.b), penColour.alphaF()); m_currentPen.setColor(penColour); m_painter.top()->setPen(m_currentPen); } void QPainterOutputDev::updateBlendMode(GfxState *state) { GfxBlendMode blendMode = state->getBlendMode(); // missing composition modes in QPainter: // - CompositionMode_Hue // - CompositionMode_Color // - CompositionMode_Luminosity // - CompositionMode_Saturation switch (blendMode) { case gfxBlendMultiply: m_painter.top()->setCompositionMode(QPainter::CompositionMode_Multiply); break; case gfxBlendScreen: m_painter.top()->setCompositionMode(QPainter::CompositionMode_Screen); break; case gfxBlendDarken: m_painter.top()->setCompositionMode(QPainter::CompositionMode_Darken); break; case gfxBlendLighten: m_painter.top()->setCompositionMode(QPainter::CompositionMode_Lighten); break; case gfxBlendColorDodge: m_painter.top()->setCompositionMode(QPainter::CompositionMode_ColorDodge); break; case gfxBlendColorBurn: m_painter.top()->setCompositionMode(QPainter::CompositionMode_ColorBurn); break; case gfxBlendHardLight: m_painter.top()->setCompositionMode(QPainter::CompositionMode_HardLight); break; case gfxBlendSoftLight: m_painter.top()->setCompositionMode(QPainter::CompositionMode_SoftLight); break; case gfxBlendDifference: m_painter.top()->setCompositionMode(QPainter::CompositionMode_Difference); break; case gfxBlendExclusion: m_painter.top()->setCompositionMode(QPainter::CompositionMode_Exclusion); break; case gfxBlendColor: m_painter.top()->setCompositionMode(QPainter::CompositionMode_Plus); break; default: qDebug() << "Unsupported blend mode, falling back to CompositionMode_SourceOver"; [[fallthrough]]; case gfxBlendNormal: m_painter.top()->setCompositionMode(QPainter::CompositionMode_SourceOver); break; } } void QPainterOutputDev::updateFillOpacity(GfxState *state) { QColor brushColour = m_currentBrush.color(); brushColour.setAlphaF(state->getFillOpacity()); m_currentBrush.setColor(brushColour); } void QPainterOutputDev::updateStrokeOpacity(GfxState *state) { QColor penColour = m_currentPen.color(); penColour.setAlphaF(state->getStrokeOpacity()); m_currentPen.setColor(penColour); m_painter.top()->setPen(m_currentPen); } void QPainterOutputDev::updateFont(GfxState *state) { const std::shared_ptr &gfxFont = state->getFont(); if (!gfxFont) { return; } // The key to look in the font caches QPainterFontID fontID = { *gfxFont->getID(), state->getFontSize() }; // Current font is a type3 font if (gfxFont->getType() == fontType3) { auto cacheEntry = m_type3FontCache.find(fontID); if (cacheEntry != m_type3FontCache.end()) { // Take the font from the cache m_currentType3Font = cacheEntry->second.get(); } else { m_currentType3Font = new QPainterOutputDevType3Font(m_doc, std::static_pointer_cast(gfxFont)); m_type3FontCache.insert(std::make_pair(fontID, std::unique_ptr(m_currentType3Font))); } return; } // Non-type3: is the font in the cache? auto cacheEntry = m_rawFontCache.find(fontID); if (cacheEntry != m_rawFontCache.end()) { // Take the font from the cache m_rawFont = cacheEntry->second.get(); } else { // New font: load it into the cache float fontSize = state->getFontSize(); std::optional fontLoc = gfxFont->locateFont(xref, nullptr); if (fontLoc) { // load the font from respective location switch (fontLoc->locType) { case gfxFontLocEmbedded: { // if there is an embedded font, read it to memory const std::optional> fontData = gfxFont->readEmbFontFile(xref); // fontData gets copied in the QByteArray constructor m_rawFont = new QRawFont(QByteArray(fontData ? (const char *)fontData->data() : nullptr, fontData ? fontData->size() : 0), fontSize, m_hintingPreference); m_rawFontCache.insert(std::make_pair(fontID, std::unique_ptr(m_rawFont))); break; } case gfxFontLocExternal: { // font is in an external font file QString fontFile(fontLoc->path.c_str()); m_rawFont = new QRawFont(fontFile, fontSize, m_hintingPreference); m_rawFontCache.insert(std::make_pair(fontID, std::unique_ptr(m_rawFont))); break; } case gfxFontLocResident: { // font resides in a PS printer qDebug() << "Resident Font Resident not implemented yet!"; break; } } // end switch } else { qDebug() << "Font location not found!"; return; } } if (!m_rawFont->isValid()) { qDebug() << "RawFont is not valid"; } // ***************************************************************************** // We have now successfully loaded the font into a QRawFont object. This // allows us to draw all the glyphs in the font. However, what is missing is // the charcode-to-glyph-index mapping. Apparently, Qt does not provide this // information at all. Therefore, we need to figure it ourselves, using // FoFi and FreeType. // ***************************************************************************** m_needFontUpdate = false; GfxFontType fontType = gfxFont->getType(); // Default: no codeToGID table m_codeToGID = nullptr; // check the font file cache Ref id = *gfxFont->getID(); auto codeToGIDIt = m_codeToGIDCache.find(id); if (codeToGIDIt != m_codeToGIDCache.end()) { m_codeToGID = codeToGIDIt->second; } else { std::optional> fontBuffer; std::optional fontLoc = gfxFont->locateFont(xref, nullptr); if (!fontLoc) { error(errSyntaxError, -1, "Couldn't find a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)"); return; } // embedded font if (fontLoc->locType == gfxFontLocEmbedded) { // if there is an embedded font, read it to memory fontBuffer = gfxFont->readEmbFontFile(xref); if (!fontBuffer) { return; } // external font } else { // gfxFontLocExternal // Hmm, fontType has already been set to gfxFont->getType() above. // Can it really assume a different value here? fontType = fontLoc->fontType; } switch (fontType) { case fontType1: 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)) { 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)) { error(errSyntaxError, -1, "Couldn't create a FreeType face for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)"); return; } } const char *name; int *codeToGID = (int *)gmallocn(256, sizeof(int)); 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) { name = GfxFont::getAlternateName(name); if (name) { codeToGID[i] = FT_Get_Name_Index(freeTypeFace, (char *)name); } } } } FT_Done_Face(freeTypeFace); m_codeToGIDCache[id] = codeToGID; break; } case fontTrueType: case fontTrueTypeOT: { auto ff = (fontLoc->locType != gfxFontLocEmbedded) ? FoFiTrueType::load(fontLoc->path.c_str()) : FoFiTrueType::make(fontBuffer->data(), fontBuffer->size()); m_codeToGIDCache[id] = (ff) ? ((Gfx8BitFont *)gfxFont.get())->getCodeToGIDMap(ff.get()) : nullptr; break; } case fontCIDType0: case fontCIDType0C: { int *cidToGIDMap = nullptr; int nCIDs = 0; // 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; } m_codeToGIDCache[id] = cidToGIDMap; break; } case fontCIDType0COT: { int *codeToGID = nullptr; 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)); } int *cidToGIDMap = nullptr; int nCIDs = 0; if (!codeToGID && !m_useCIDs) { auto ff = (fontLoc->locType != gfxFontLocEmbedded) ? FoFiTrueType::load(fontLoc->path.c_str()) : FoFiTrueType::make(fontBuffer->data(), fontBuffer->size()); if (ff && ff->isOpenTypeCFF()) { cidToGIDMap = ff->getCIDToGIDMap(&nCIDs); } } m_codeToGIDCache[id] = codeToGID ? 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)); } } else { auto ff = (fontLoc->locType != gfxFontLocEmbedded) ? FoFiTrueType::load(fontLoc->path.c_str()) : FoFiTrueType::make(fontBuffer->data(), fontBuffer->size()); if (!ff) { return; } codeToGID = ((GfxCIDFont *)gfxFont.get())->getCodeToGIDMap(ff.get(), &codeToGIDLen); } m_codeToGIDCache[id] = codeToGID; break; } default: // this shouldn't happen return; } m_codeToGID = m_codeToGIDCache[id]; } } static QPainterPath convertPath(GfxState *state, const GfxPath *path, Qt::FillRule fillRule) { int i, j; QPainterPath qPath; qPath.setFillRule(fillRule); for (i = 0; i < path->getNumSubpaths(); ++i) { const GfxSubpath *subpath = path->getSubpath(i); if (subpath->getNumPoints() > 0) { qPath.moveTo(subpath->getX(0), subpath->getY(0)); j = 1; while (j < subpath->getNumPoints()) { if (subpath->getCurve(j)) { qPath.cubicTo(subpath->getX(j), subpath->getY(j), subpath->getX(j + 1), subpath->getY(j + 1), subpath->getX(j + 2), subpath->getY(j + 2)); j += 3; } else { qPath.lineTo(subpath->getX(j), subpath->getY(j)); ++j; } } if (subpath->isClosed()) { qPath.closeSubpath(); } } } return qPath; } void QPainterOutputDev::stroke(GfxState *state) { m_painter.top()->strokePath(convertPath(state, state->getPath(), Qt::OddEvenFill), m_currentPen); } void QPainterOutputDev::fill(GfxState *state) { m_painter.top()->fillPath(convertPath(state, state->getPath(), Qt::WindingFill), m_currentBrush); } void QPainterOutputDev::eoFill(GfxState *state) { m_painter.top()->fillPath(convertPath(state, state->getPath(), Qt::OddEvenFill), m_currentBrush); } bool QPainterOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) { double x0, y0, x1, y1; shading->getCoords(&x0, &y0, &x1, &y1); // get the clip region bbox double xMin, yMin, xMax, yMax; state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); // get the function domain double t0 = shading->getDomain0(); double t1 = shading->getDomain1(); // Max number of splits along the t axis constexpr int maxSplits = 256; // Max delta allowed in any color component const double colorDelta = (dblToCol(1 / 256.0)); // Number of color space components auto nComps = shading->getColorSpace()->getNComps(); // If the clipping region is a stroke, then the current operation counts as a stroke // rather than as a fill, and the opacity has to be set accordingly. // See https://gitlab.freedesktop.org/poppler/poppler/-/issues/178 auto opacity = (state->getStrokePattern()) ? state->getStrokeOpacity() : state->getFillOpacity(); // Helper function to test two color objects for 'almost-equality' auto isSameGfxColor = [&nComps, &colorDelta](const GfxColor &colorA, const GfxColor &colorB) { for (int k = 0; k < nComps; ++k) { if (abs(colorA.c[k] - colorB.c[k]) > colorDelta) { return false; } } return true; }; // Helper function: project a number into an interval // With C++17 this is part of the standard library auto clamp = [](double v, double lo, double hi) { return std::min(std::max(v, lo), hi); }; // ta stores all parameter values where we evaluate the input shading function. // In between, QLinearGradient will interpolate linearly. // We set up the array with three values. std::array ta; ta[0] = tMin; std::array next; next[0] = maxSplits / 2; ta[maxSplits / 2] = 0.5 * (tMin + tMax); next[maxSplits / 2] = maxSplits; ta[maxSplits] = tMax; // compute the color at t = tMin double tt = clamp(t0 + (t1 - t0) * tMin, t0, t1); GfxColor color0, color1; shading->getColor(tt, &color0); // Construct a gradient object and set its color at one parameter end QLinearGradient gradient(QPointF(x0 + tMin * (x1 - x0), y0 + tMin * (y1 - y0)), QPointF(x0 + tMax * (x1 - x0), y0 + tMax * (y1 - y0))); GfxRGB rgb; shading->getColorSpace()->getRGB(&color0, &rgb); QColor qColor(colToByte(rgb.r), colToByte(rgb.g), colToByte(rgb.b), dblToByte(opacity)); gradient.setColorAt(0, qColor); // Look for more relevant parameter values by bisection int i = 0; while (i < maxSplits) { int j = next[i]; while (j > i + 1) { // Next parameter value to try tt = clamp(t0 + (t1 - t0) * ta[j], t0, t1); shading->getColor(tt, &color1); // j is a good next color stop if the input shading can be approximated well // on the interval (ta[i], ta[j]) by a linear interpolation. // We test this by comparing the real color in the middle between ta[i] and ta[j] // with the linear interpolant there. auto midPoint = 0.5 * (ta[i] + ta[j]); GfxColor colorAtMidPoint; shading->getColor(midPoint, &colorAtMidPoint); GfxColor linearlyInterpolatedColor; for (int ii = 0; ii < nComps; ii++) { linearlyInterpolatedColor.c[ii] = 0.5 * (color0.c[ii] + color1.c[ii]); } // If the two colors are equal, ta[j] is a good place for the next color stop; take it! if (isSameGfxColor(colorAtMidPoint, linearlyInterpolatedColor)) { break; } // Otherwise: bisect further int k = (i + j) / 2; ta[k] = midPoint; next[i] = k; next[k] = j; j = k; } // set the color shading->getColorSpace()->getRGB(&color1, &rgb); qColor.setRgb(colToByte(rgb.r), colToByte(rgb.g), colToByte(rgb.b), dblToByte(opacity)); gradient.setColorAt((ta[j] - tMin) / (tMax - tMin), qColor); // Move to the next parameter region color0 = color1; i = next[i]; } state->moveTo(xMin, yMin); state->lineTo(xMin, yMax); state->lineTo(xMax, yMax); state->lineTo(xMax, yMin); state->closePath(); // Actually paint the shaded region QBrush newBrush(gradient); m_painter.top()->fillPath(convertPath(state, state->getPath(), Qt::WindingFill), newBrush); state->clearPath(); // True means: The shaded region has been painted return true; } void QPainterOutputDev::clip(GfxState *state) { m_painter.top()->setClipPath(convertPath(state, state->getPath(), Qt::WindingFill), Qt::IntersectClip); } void QPainterOutputDev::eoClip(GfxState *state) { m_painter.top()->setClipPath(convertPath(state, state->getPath(), Qt::OddEvenFill), Qt::IntersectClip); } void QPainterOutputDev::clipToStrokePath(GfxState *state) { QPainterPath clipPath = convertPath(state, state->getPath(), Qt::WindingFill); // Get the outline of 'clipPath' as a separate path QPainterPathStroker stroker; stroker.setWidth(state->getLineWidth()); stroker.setCapStyle(m_currentPen.capStyle()); stroker.setJoinStyle(m_currentPen.joinStyle()); stroker.setMiterLimit(state->getMiterLimit()); stroker.setDashPattern(m_currentPen.dashPattern()); stroker.setDashOffset(m_currentPen.dashOffset()); QPainterPath clipPathOutline = stroker.createStroke(clipPath); // The interior of the outline is the desired clipping region m_painter.top()->setClipPath(clipPathOutline, Qt::IntersectClip); } void QPainterOutputDev::drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, const Unicode *u, int uLen) { // First handle type3 fonts const std::shared_ptr &gfxFont = state->getFont(); GfxFontType fontType = gfxFont->getType(); if (fontType == fontType3) { ///////////////////////////////////////////////////////////////////// // Draw the QPicture that contains the glyph onto the page ///////////////////////////////////////////////////////////////////// // Store the QPainter state; we need to modify it temporarily m_painter.top()->save(); // Make the glyph position the coordinate origin -- that's our center of scaling m_painter.top()->translate(QPointF(x - originX, y - originY)); const double *mat = gfxFont->getFontMatrix(); QTransform fontMatrix(mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); // Scale with the font size fontMatrix.scale(state->getFontSize(), state->getFontSize()); m_painter.top()->setTransform(fontMatrix, true); // Apply the text matrix on top const double *textMat = state->getTextMat(); QTransform textTransform(textMat[0] * state->getHorizScaling(), textMat[1] * state->getHorizScaling(), textMat[2], textMat[3], 0, 0); m_painter.top()->setTransform(textTransform, true); // Actually draw the glyph int gid = m_currentType3Font->codeToGID[code]; m_painter.top()->drawPicture(QPointF(0, 0), m_currentType3Font->getGlyph(gid)); // Restore transformation m_painter.top()->restore(); return; } // check for invisible text -- this is used by Acrobat Capture int render = state->getRender(); if (render == 3 || !m_rawFont) { qDebug() << "Invisible text found!"; return; } if (!(render & 1)) { quint32 glyphIndex = (m_codeToGID) ? m_codeToGID[code] : code; QPointF glyphPosition = QPointF(x - originX, y - originY); // QGlyphRun objects can hold an entire sequence of glyphs, and it would possibly // be more efficient to simply note the glyph and glyph position here and then // draw several glyphs at once in the endString method. What keeps us from doing // that is the transformation below: each glyph needs to be drawn upside down, // i.e., reflected at its own baseline. Since we have no guarantee that this // baseline is the same for all glyphs in a string we have to do it one by one. QGlyphRun glyphRun; glyphRun.setRawData(&glyphIndex, &glyphPosition, 1); glyphRun.setRawFont(*m_rawFont); // Store the QPainter state; we need to modify it temporarily m_painter.top()->save(); // Apply the text matrix to the glyph. The glyph is not scaled by the font size, // because the font in m_rawFont already has the correct size. // Additionally, the CTM is upside down, i.e., it contains a negative Y-scaling // entry. Therefore, Qt will paint the glyphs upside down. We need to temporarily // reflect the page at glyphPosition.y(). // Make the glyph position the coordinate origin -- that's our center of scaling const double *textMat = state->getTextMat(); m_painter.top()->translate(QPointF(glyphPosition.x(), glyphPosition.y())); QTransform textTransform(textMat[0] * state->getHorizScaling(), textMat[1] * state->getHorizScaling(), -textMat[2], // reflect at the horizontal axis, -textMat[3], // because CTM is upside-down. 0, 0); m_painter.top()->setTransform(textTransform, true); // We are painting a filled glyph here. But QPainter uses the pen to draw even filled text, // not the brush. (see, e.g., http://doc.qt.io/qt-5/qpainter.html#setPen ) // Therefore we have to temporarily overwrite the pen color. // Since we are drawing a filled glyph, one would really expect to have m_currentBrush // have the correct color. However, somehow state->getFillRGB can change without // updateFillColor getting called. Then m_currentBrush may not contain the correct color. GfxRGB rgb; state->getFillRGB(&rgb); QColor fontColor; fontColor.setRgbF(colToDbl(rgb.r), colToDbl(rgb.g), colToDbl(rgb.b), state->getFillOpacity()); m_painter.top()->setPen(fontColor); // Actually draw the glyph m_painter.top()->drawGlyphRun(QPointF(-glyphPosition.x(), -glyphPosition.y()), glyphRun); // Restore transformation and pen color m_painter.top()->restore(); } } void QPainterOutputDev::type3D0(GfxState *state, double wx, double wy) { } void QPainterOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) { } void QPainterOutputDev::endTextObject(GfxState *state) { } void QPainterOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg) { auto imgStr = std::make_unique(str, width, 1, // numPixelComps 1 // getBits ); imgStr->reset(); // TODO: Would using QImage::Format_Mono be more efficient here? QImage image(width, height, QImage::Format_ARGB32); unsigned int *data = reinterpret_cast(image.bits()); int stride = image.bytesPerLine() / 4; QRgb fillColor = m_currentBrush.color().rgb(); for (int y = 0; y < height; y++) { unsigned char *pix = imgStr->getLine(); // Invert the vertical coordinate: y is increasing from top to bottom // on the page, but y is increasing bottom to top in the picture. unsigned int *dest = data + (height - 1 - y) * stride; for (int x = 0; x < width; x++) { bool opaque = ((bool)pix[x]) == invert; dest[x] = (opaque) ? fillColor : 0; } } // At this point, the QPainter coordinate transformation (CTM) is such // that QRect(0,0,1,1) is exactly the area of the image. m_painter.top()->drawImage(QRect(0, 0, 1, 1), image); imgStr->close(); } // TODO: lots more work here. void QPainterOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) { unsigned int *data; unsigned int *line; int x, y; unsigned char *pix; int i; QImage image; int stride; /* TODO: Do we want to cache these? */ auto imgStr = std::make_unique(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgStr->reset(); image = QImage(width, height, QImage::Format_ARGB32); data = reinterpret_cast(image.bits()); stride = image.bytesPerLine() / 4; for (y = 0; y < height; y++) { pix = imgStr->getLine(); // Invert the vertical coordinate: y is increasing from top to bottom // on the page, but y is increasing bottom to top in the picture. line = data + (height - 1 - y) * stride; colorMap->getRGBLine(pix, line, width); if (maskColors) { for (x = 0; x < width; x++) { for (i = 0; i < colorMap->getNumPixelComps(); ++i) { if (pix[i] < maskColors[2 * i] * 255 || pix[i] > maskColors[2 * i + 1] * 255) { *line = *line | 0xff000000; break; } } pix += colorMap->getNumPixelComps(); line++; } } else { for (x = 0; x < width; x++) { *line = *line | 0xff000000; line++; } } } // At this point, the QPainter coordinate transformation (CTM) is such // that QRect(0,0,1,1) is exactly the area of the image. m_painter.top()->drawImage(QRect(0, 0, 1, 1), image); } void QPainterOutputDev::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) { // Bail out if the image size doesn't match the mask size. I don't know // what to do in this case. if (width != maskWidth || height != maskHeight) { qDebug() << "Soft mask size does not match image size!"; drawImage(state, ref, str, width, height, colorMap, interpolate, nullptr, false); return; } // Bail out if the mask isn't a single channel. I don't know // what to do in this case. if (maskColorMap->getColorSpace()->getNComps() != 1) { qDebug() << "Soft mask is not a single 8-bit channel!"; drawImage(state, ref, str, width, height, colorMap, interpolate, nullptr, false); return; } /* TODO: Do we want to cache these? */ auto imgStr = std::make_unique(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgStr->reset(); auto maskImageStr = std::make_unique(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits()); maskImageStr->reset(); QImage image(width, height, QImage::Format_ARGB32); unsigned int *data = reinterpret_cast(image.bits()); int stride = image.bytesPerLine() / 4; std::vector maskLine(maskWidth); for (int y = 0; y < height; y++) { unsigned char *pix = imgStr->getLine(); unsigned char *maskPix = maskImageStr->getLine(); // Invert the vertical coordinate: y is increasing from top to bottom // on the page, but y is increasing bottom to top in the picture. unsigned int *line = data + (height - 1 - y) * stride; colorMap->getRGBLine(pix, line, width); // Apply the mask values to the image alpha channel maskColorMap->getGrayLine(maskPix, maskLine.data(), width); for (int x = 0; x < width; x++) { *line = *line | (maskLine[x] << 24); line++; } } // At this point, the QPainter coordinate transformation (CTM) is such // that QRect(0,0,1,1) is exactly the area of the image. m_painter.top()->drawImage(QRect(0, 0, 1, 1), image); } void QPainterOutputDev::beginTransparencyGroup(GfxState * /*state*/, const double * /*bbox*/, GfxColorSpace * /*blendingColorSpace*/, bool /*isolated*/, bool /*knockout*/, bool /*forSoftMask*/) { // The entire transparency group will be painted into a // freshly created QPicture object. Since an existing painter // cannot change its paint device, we need to construct a // new QPainter object as well. m_qpictures.push(new QPicture); m_painter.push(new QPainter(m_qpictures.top())); } void QPainterOutputDev::endTransparencyGroup(GfxState * /*state*/) { // Stop painting into the group m_painter.top()->end(); // Kill the painter that has been used for the transparency group delete (m_painter.top()); m_painter.pop(); // Store the QPicture object that holds the result of the transparency group // painting. It will be painted and deleted in the method paintTransparencyGroup. if (m_lastTransparencyGroupPicture) { qDebug() << "Found a transparency group that has not been painted"; delete (m_lastTransparencyGroupPicture); } m_lastTransparencyGroupPicture = m_qpictures.top(); m_qpictures.pop(); } void QPainterOutputDev::paintTransparencyGroup(GfxState * /*state*/, const double * /*bbox*/) { // Actually draw the transparency group m_painter.top()->drawPicture(0, 0, *m_lastTransparencyGroupPicture); // And delete it delete (m_lastTransparencyGroupPicture); m_lastTransparencyGroupPicture = nullptr; } poppler-24.02.0/qt5/src/QPainterOutputDev.h000066400000000000000000000175171455701731300204350ustar00rootroot00000000000000//======================================================================== // // QPainterOutputDev.h // // Copyright 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) 2005 Brad Hards // Copyright (C) 2005, 2018, 2019, 2021 Albert Astals Cid // Copyright (C) 2009, 2011 Carlos Garcia Campos // Copyright (C) 2010 Pino Toscano // Copyright (C) 2011 Andreas Hartmetz // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2013 Mihai Niculescu // Copyright (C) 2017, 2018, 2020 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 // //======================================================================== #ifndef QPAINTEROUTPUTDEV_H #define QPAINTEROUTPUTDEV_H #include #include #include #include "OutputDev.h" #include "GfxState.h" #include #include FT_FREETYPE_H #include class GfxState; class PDFDoc; class QRawFont; class QPainterOutputDevType3Font; //------------------------------------------------------------------------ // QPainterOutputDev - Qt 5 QPainter renderer //------------------------------------------------------------------------ class QPainterOutputDev : public OutputDev { public: // Constructor. explicit QPainterOutputDev(QPainter *painter); // Destructor. ~QPainterOutputDev() override; void setHintingPreference(QFont::HintingPreference hintingPreference) { m_hintingPreference = hintingPreference; } //----- get info about output device // Does this device use upside-down coordinates? // (Upside-down means (0,0) is the top left corner of the page.) bool upsideDown() override { return true; } // Does this device use drawChar() or drawString()? bool useDrawChar() override { return true; } // Does this device implement shaded fills (aka gradients) natively? // If this returns false, these shaded fills // will be reduced to a series of other drawing operations. // type==2 is 'axial shading' bool useShadedFills(int type) override { return type == 2; } // Does this device use beginType3Char/endType3Char? Otherwise, // text in Type 3 fonts will be drawn with drawChar/drawString. bool interpretType3Chars() override { return false; } //----- initialization and control // Set Current Transformation Matrix to a fixed matrix given in ctm[0],...,ctm[5] void setDefaultCTM(const double *ctm) override; // Start a page. void startPage(int pageNum, GfxState *state, XRef *xref) override; // End a page. void endPage() override; //----- save/restore graphics state void saveState(GfxState *state) override; void restoreState(GfxState *state) override; //----- update graphics state void updateAll(GfxState *state) override; void updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32) override; void updateLineDash(GfxState *state) override; void updateFlatness(GfxState *state) override; void updateLineJoin(GfxState *state) override; void updateLineCap(GfxState *state) override; void updateMiterLimit(GfxState *state) override; void updateLineWidth(GfxState *state) override; void updateFillColor(GfxState *state) override; void updateStrokeColor(GfxState *state) override; void updateBlendMode(GfxState *state) override; void updateFillOpacity(GfxState *state) override; void updateStrokeOpacity(GfxState *state) override; //----- update text state void updateFont(GfxState *state) override; //----- path painting void stroke(GfxState *state) override; void fill(GfxState *state) override; void eoFill(GfxState *state) override; bool axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) override; //----- path clipping void clip(GfxState *state) override; void eoClip(GfxState *state) override; void clipToStrokePath(GfxState *state) override; //----- text drawing // virtual void drawString(GfxState *state, GooString *s); void drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, const Unicode *u, int uLen) override; void endTextObject(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 drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) override; void 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) override; //----- Type 3 font operators void type3D0(GfxState *state, double wx, double wy) override; void type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) override; //----- 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; void paintTransparencyGroup(GfxState *state, const double *bbox) override; //----- special access // Called to indicate that a new PDF document has been loaded. void startDoc(PDFDoc *doc); bool isReverseVideo() { return false; } private: // The stack of QPainters is used to implement transparency groups. When such a group // is opened, annew Painter that paints onto a QPicture is pushed onto the stack. // It is popped again when the transparency group ends. std::stack m_painter; // This is the corresponding stack of QPicture objects std::stack m_qpictures; // endTransparencyGroup removes a QPicture from the stack, but stores // it here for later use in paintTransparencyGroup. QPicture *m_lastTransparencyGroupPicture; QFont::HintingPreference m_hintingPreference; QPen m_currentPen; // The various stacks are used to implement the 'saveState' and 'restoreState' methods std::stack m_currentPenStack; QBrush m_currentBrush; std::stack m_currentBrushStack; bool m_needFontUpdate; // set when the font needs to be updated PDFDoc *m_doc; XRef *xref; // xref table for current document // The current font in use QRawFont *m_rawFont; std::stack m_rawFontStack; QPainterOutputDevType3Font *m_currentType3Font; std::stack m_type3FontStack; // Cache all fonts by their Ref and font size using QPainterFontID = std::pair; std::map> m_rawFontCache; std::map> m_type3FontCache; std::map m_codeToGIDCache; // The table that maps character codes to glyph indexes const int *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 bool m_useCIDs; }; #endif poppler-24.02.0/qt5/src/poppler-annotation-helper.h000066400000000000000000000047311455701731300221320ustar00rootroot00000000000000/* poppler-annotation-helper.h: qt interface to poppler * Copyright (C) 2006, 2008, 2017-2019, 2021, Albert Astals Cid * Copyright (C) 2008, Pino Toscano * Copyright (C) 2012, Fabio D'Urso * Copyright (C) 2018, Dileep Sankhla * Copyright (C) 2018, Carlos Garcia Campos * Copyright (C) 2018, 2019, Oliver Sander * Adapting code from * Copyright (C) 2004 by Enrico Ros * * 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_ANNOTATION_HELPER_H_ #define _POPPLER_ANNOTATION_HELPER_H_ #include #include #include class QColor; class AnnotColor; namespace Poppler { class XPDFReader { public: // transform from user coords to normalized ones using the matrix M static inline void transform(double *M, double x, double y, QPointF &res); static inline void invTransform(const double *M, const QPointF p, double &x, double &y); }; void XPDFReader::transform(double *M, double x, double y, QPointF &res) { res.setX(M[0] * x + M[2] * y + M[4]); res.setY(M[1] * x + M[3] * y + M[5]); } void XPDFReader::invTransform(const double *M, const QPointF p, double &x, double &y) { const double det = M[0] * M[3] - M[1] * M[2]; if (det == 0) { qWarning("Tried to invert singular matrix, something won't work"); x = 0; y = 0; return; } const double invM[4] = { M[3] / det, -M[1] / det, -M[2] / det, M[0] / det }; const double xt = p.x() - M[4]; const double yt = p.y() - M[5]; x = invM[0] * xt + invM[2] * yt; y = invM[1] * xt + invM[3] * yt; } QColor convertAnnotColor(const AnnotColor *color); std::unique_ptr convertQColor(const QColor &color); } #endif poppler-24.02.0/qt5/src/poppler-annotation-private.h000066400000000000000000000107031455701731300223210ustar00rootroot00000000000000/* poppler-annotation-private.h: qt interface to poppler * Copyright (C) 2007, Pino Toscano * Copyright (C) 2012, Tobias Koenig * Copyright (C) 2012, 2013 Fabio D'Urso * 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 * * 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_ANNOTATION_PRIVATE_H_ #define _POPPLER_ANNOTATION_PRIVATE_H_ #include #include #include #include "poppler-annotation.h" #include #include class Annot; class AnnotPath; class Page; class PDFRectangle; namespace Poppler { class DocumentData; PDFRectangle boundaryToPdfRectangle(::Page *pdfPage, const QRectF &r, int flags); void getRawDataFromQImage(const QImage &qimg, int bitsPerPixel, QByteArray *data, QByteArray *sMaskData); class AnnotationPrivate : public QSharedData { public: AnnotationPrivate(); virtual ~AnnotationPrivate(); AnnotationPrivate(const AnnotationPrivate &) = delete; AnnotationPrivate &operator=(const AnnotationPrivate &) = delete; void addRevision(Annotation *ann, Annotation::RevScope scope, Annotation::RevType type); /* Returns an Annotation of the right subclass whose d_ptr points to * this AnnotationPrivate */ virtual Annotation *makeAlias() = 0; /* properties: contents related */ QString author; QString contents; QString uniqueName; QDateTime modDate; // before or equal to currentDateTime() QDateTime creationDate; // before or equal to modifyDate /* properties: look/interaction related */ int flags; QRectF boundary; /* style and popup */ Annotation::Style style; Annotation::Popup popup; /* revisions */ Annotation::RevScope revisionScope; Annotation::RevType revisionType; QList revisions; /* After this call, the Annotation object will behave like a wrapper for * the specified Annot object. All cached values are discarded */ void tieToNativeAnnot(Annot *ann, ::Page *page, DocumentData *doc); /* Creates a new Annot object on the specified page, flushes current * values to that object and ties this Annotation to that object */ virtual Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) = 0; /* Inited to 0 (i.e. untied annotation) */ Annot *pdfAnnot; ::Page *pdfPage; DocumentData *parentDoc; /* The following helpers only work if pdfPage is set */ void flushBaseAnnotationProperties(); 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; /* 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); /* Add given annotation to given page */ static void addAnnotationToPage(::Page *pdfPage, DocumentData *doc, const Annotation *ann); /* Remove annotation from page and destroy ann */ static void removeAnnotationFromPage(::Page *pdfPage, const Annotation *ann); Ref pdfObjectReference() const; Link *additionalAction(Annotation::AdditionalActionType type) const; Object annotationAppearance; }; class AnnotationAppearancePrivate { public: explicit AnnotationAppearancePrivate(Annot *annot); Object appearance; }; } #endif poppler-24.02.0/qt5/src/poppler-annotation.cc000066400000000000000000004726671455701731300210340ustar00rootroot00000000000000/* poppler-annotation.cc: qt interface to poppler * Copyright (C) 2006, 2009, 2012-2015, 2018-2022 Albert Astals Cid * Copyright (C) 2006, 2008, 2010 Pino Toscano * Copyright (C) 2012, Guillermo A. Amaral B. * Copyright (C) 2012-2014 Fabio D'Urso * Copyright (C) 2012, 2015, Tobias Koenig * Copyright (C) 2018 Adam Reichold * 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) 2018 Dileep Sankhla * Copyright (C) 2018, 2019 Tobias Deiminger * Copyright (C) 2018 Carlos Garcia Campos * 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) 2021 Mahmoud Ahmed Khalil * Adapting code from * Copyright (C) 2004 by Enrico Ros * * 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. */ // qt/kde includes #include #include #include #include #include #include // local includes #include "poppler-annotation.h" #include "poppler-link.h" #include "poppler-qt5.h" #include "poppler-annotation-helper.h" #include "poppler-annotation-private.h" #include "poppler-page-private.h" #include "poppler-private.h" // poppler includes #include #include #include #include #include #include #include /* Almost all getters directly query the underlying poppler annotation, with * the exceptions of link, file attachment, sound, movie and screen annotations, * Whose data retrieval logic has not been moved yet. Their getters return * static data set at creation time by findAnnotations */ namespace Poppler { // BEGIN AnnotationUtils implementation Annotation *AnnotationUtils::createAnnotation(const QDomElement &annElement) { // safety check on annotation element if (!annElement.hasAttribute(QStringLiteral("type"))) { return nullptr; } // build annotation of given type Annotation *annotation = nullptr; int typeNumber = annElement.attribute(QStringLiteral("type")).toInt(); switch (typeNumber) { case Annotation::AText: annotation = new TextAnnotation(annElement); break; case Annotation::ALine: annotation = new LineAnnotation(annElement); break; case Annotation::AGeom: annotation = new GeomAnnotation(annElement); break; case Annotation::AHighlight: annotation = new HighlightAnnotation(annElement); break; case Annotation::AStamp: annotation = new StampAnnotation(annElement); break; case Annotation::AInk: annotation = new InkAnnotation(annElement); break; case Annotation::ACaret: annotation = new CaretAnnotation(annElement); break; } // return created annotation return annotation; } void AnnotationUtils::storeAnnotation(const Annotation *ann, QDomElement &annElement, QDomDocument &document) { // save annotation's type as element's attribute annElement.setAttribute(QStringLiteral("type"), (uint)ann->subType()); // append all annotation data as children of this node ann->store(annElement, document); } QDomElement AnnotationUtils::findChildElement(const QDomNode &parentNode, const QString &name) { // loop through the whole children and return a 'name' named element QDomNode subNode = parentNode.firstChild(); while (subNode.isElement()) { QDomElement element = subNode.toElement(); if (element.tagName() == name) { return element; } subNode = subNode.nextSibling(); } // if the name can't be found, return a dummy null element return QDomElement(); } // END AnnotationUtils implementation // BEGIN AnnotationAppearancePrivate implementation AnnotationAppearancePrivate::AnnotationAppearancePrivate(Annot *annot) { if (annot) { appearance = annot->getAppearance(); } else { appearance.setToNull(); } } // END AnnotationAppearancePrivate implementation // BEGIN AnnotationAppearance implementation AnnotationAppearance::AnnotationAppearance(AnnotationAppearancePrivate *annotationAppearancePrivate) : d(annotationAppearancePrivate) { } AnnotationAppearance::~AnnotationAppearance() { delete d; } // END AnnotationAppearance implementation // BEGIN Annotation implementation AnnotationPrivate::AnnotationPrivate() : flags(0), revisionScope(Annotation::Root), revisionType(Annotation::None), pdfAnnot(nullptr), pdfPage(nullptr), parentDoc(nullptr) { } void getRawDataFromQImage(const QImage &qimg, int bitsPerPixel, QByteArray *data, QByteArray *sMaskData) { const int height = qimg.height(); const int width = qimg.width(); switch (bitsPerPixel) { case 1: for (int line = 0; line < height; line++) { const char *lineData = reinterpret_cast(qimg.scanLine(line)); for (int offset = 0; offset < (width + 7) / 8; offset++) { data->append(lineData[offset]); } } 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++) { const QRgb *lineData = reinterpret_cast(qimg.scanLine(line)); for (int offset = 0; offset < width; offset++) { char a = (char)qAlpha(lineData[offset]); char r = (char)qRed(lineData[offset]); char g = (char)qGreen(lineData[offset]); char b = (char)qBlue(lineData[offset]); data->append(r); data->append(g); data->append(b); sMaskData->append(a); } } break; } } void AnnotationPrivate::addRevision(Annotation *ann, Annotation::RevScope scope, Annotation::RevType type) { /* Since ownership stays with the caller, create an alias of ann */ revisions.append(ann->d_ptr->makeAlias()); /* Set revision properties */ revisionScope = scope; revisionType = type; } AnnotationPrivate::~AnnotationPrivate() { // Delete all children revisions qDeleteAll(revisions); // Release Annot object if (pdfAnnot) { pdfAnnot->decRefCnt(); } } void AnnotationPrivate::tieToNativeAnnot(Annot *ann, ::Page *page, Poppler::DocumentData *doc) { if (pdfAnnot) { error(errIO, -1, "Annotation is already tied"); return; } pdfAnnot = ann; pdfPage = page; parentDoc = doc; pdfAnnot->incRefCnt(); } /* This method is called when a new annotation is created, after pdfAnnot and * pdfPage have been set */ void AnnotationPrivate::flushBaseAnnotationProperties() { Q_ASSERT(pdfPage); Annotation *q = makeAlias(); // Setters are defined in the public class // Since pdfAnnot has been set, this calls will write in the Annot object q->setAuthor(author); q->setContents(contents); q->setUniqueName(uniqueName); q->setModificationDate(modDate); q->setCreationDate(creationDate); q->setFlags(flags); // q->setBoundary(boundary); -- already set by subclass-specific code q->setStyle(style); q->setPopup(popup); // Flush revisions foreach (Annotation *r, revisions) { // TODO: Flush revision delete r; // Object is no longer needed } delete q; // Clear some members to save memory author.clear(); contents.clear(); uniqueName.clear(); revisions.clear(); } // Returns matrix to convert from user space coords (oriented according to the // specified rotation) to normalized coords static void fillNormalizationMTX(::Page *pdfPage, double MTX[6], int pageRotation) { Q_ASSERT(pdfPage); // build a normalized transform matrix for this page at 100% scale GfxState *gfxState = new GfxState(72.0, 72.0, pdfPage->getCropBox(), pageRotation, true); const double *gfxCTM = gfxState->getCTM(); double w = pdfPage->getCropWidth(); double h = pdfPage->getCropHeight(); // Swap width and height if the page is rotated landscape or seascape if (pageRotation == 90 || pageRotation == 270) { double t = w; w = h; h = t; } for (int i = 0; i < 6; i += 2) { MTX[i] = gfxCTM[i] / w; MTX[i + 1] = gfxCTM[i + 1] / h; } delete gfxState; } // Returns matrix to convert from user space coords (i.e. those that are stored // in the PDF file) to normalized coords (i.e. those that we expose to clients). // This method also applies a rotation around the top-left corner if the // FixedRotation flag is set. void AnnotationPrivate::fillTransformationMTX(double MTX[6]) const { Q_ASSERT(pdfPage); Q_ASSERT(pdfAnnot); const int pageRotate = pdfPage->getRotate(); if (pageRotate == 0 || (pdfAnnot->getFlags() & Annot::flagNoRotate) == 0) { // Use the normalization matrix for this page's rotation fillNormalizationMTX(pdfPage, MTX, pageRotate); } else { // Clients expect coordinates relative to this page's rotation, but // FixedRotation annotations internally use unrotated coordinates: // construct matrix to both normalize and rotate coordinates using the // top-left corner as rotation pivot double MTXnorm[6]; fillNormalizationMTX(pdfPage, MTXnorm, pageRotate); QTransform transform(MTXnorm[0], MTXnorm[1], MTXnorm[2], MTXnorm[3], MTXnorm[4], MTXnorm[5]); transform.translate(+pdfAnnot->getXMin(), +pdfAnnot->getYMax()); transform.rotate(pageRotate); transform.translate(-pdfAnnot->getXMin(), -pdfAnnot->getYMax()); MTX[0] = transform.m11(); MTX[1] = transform.m12(); MTX[2] = transform.m21(); MTX[3] = transform.m22(); MTX[4] = transform.dx(); MTX[5] = transform.dy(); } } QRectF AnnotationPrivate::fromPdfRectangle(const PDFRectangle &r) const { double swp, MTX[6]; fillTransformationMTX(MTX); QPointF p1, p2; XPDFReader::transform(MTX, r.x1, r.y1, p1); XPDFReader::transform(MTX, r.x2, r.y2, p2); double tl_x = p1.x(); double tl_y = p1.y(); double br_x = p2.x(); double br_y = p2.y(); if (tl_x > br_x) { swp = tl_x; tl_x = br_x; br_x = swp; } if (tl_y > br_y) { swp = tl_y; tl_y = br_y; br_y = swp; } return QRectF(QPointF(tl_x, tl_y), QPointF(br_x, br_y)); } // This function converts a boundary QRectF in normalized coords to a // PDFRectangle in user coords. If the FixedRotation flag is set, this function // also applies a rotation around the top-left corner: it's the inverse of // the transformation produced by fillTransformationMTX, but we can't use // fillTransformationMTX here because it relies on the native annotation // object's boundary rect to be already set up. PDFRectangle boundaryToPdfRectangle(::Page *pdfPage, const QRectF &r, int rFlags) { Q_ASSERT(pdfPage); const double w = pdfPage->getCropWidth(); const double h = pdfPage->getCropHeight(); if (w == 0 || h == 0) { // page is broken, there's nothing to transform return {}; } const int pageRotate = pdfPage->getRotate(); double MTX[6]; fillNormalizationMTX(pdfPage, MTX, pageRotate); double tl_x, tl_y, br_x, br_y, swp; XPDFReader::invTransform(MTX, r.topLeft(), tl_x, tl_y); XPDFReader::invTransform(MTX, r.bottomRight(), br_x, br_y); if (tl_x > br_x) { swp = tl_x; tl_x = br_x; br_x = swp; } if (tl_y > br_y) { swp = tl_y; tl_y = br_y; br_y = swp; } const int rotationFixUp = (rFlags & Annotation::FixedRotation) ? pageRotate : 0; const double width = br_x - tl_x; const double height = br_y - tl_y; if (rotationFixUp == 0) { return PDFRectangle(tl_x, tl_y, br_x, br_y); } else if (rotationFixUp == 90) { return PDFRectangle(tl_x, tl_y - width, tl_x + height, tl_y); } else if (rotationFixUp == 180) { return PDFRectangle(br_x, tl_y - height, br_x + width, tl_y); } else { // rotationFixUp == 270 return PDFRectangle(br_x, br_y - width, br_x + height, br_y); } } PDFRectangle AnnotationPrivate::boundaryToPdfRectangle(const QRectF &r, int rFlags) const { return Poppler::boundaryToPdfRectangle(pdfPage, r, rFlags); } AnnotPath *AnnotationPrivate::toAnnotPath(const QLinkedList &list) const { const int count = list.size(); std::vector ac; ac.reserve(count); double MTX[6]; fillTransformationMTX(MTX); foreach (const QPointF &p, list) { double x, y; XPDFReader::invTransform(MTX, p, x, y); ac.emplace_back(x, y); } return new AnnotPath(std::move(ac)); } QList AnnotationPrivate::findAnnotations(::Page *pdfPage, DocumentData *doc, const QSet &subtypes, int parentID) { Annots *annots = pdfPage->getAnnots(); const bool wantTextAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AText); const bool wantLineAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::ALine); const bool wantGeomAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AGeom); const bool wantHighlightAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AHighlight); const bool wantStampAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AStamp); const bool wantInkAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AInk); const bool wantLinkAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::ALink); const bool wantCaretAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::ACaret); const bool wantFileAttachmentAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AFileAttachment); const bool wantSoundAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::ASound); const bool wantMovieAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AMovie); const bool wantScreenAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AScreen); const bool wantWidgetAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AWidget); // Create Annotation objects and tie to their native Annot QList res; for (Annot *ann : annots->getAnnots()) { if (!ann) { error(errInternal, -1, "Annot is null"); continue; } // Check parent annotation AnnotMarkup *markupann = dynamic_cast(ann); if (!markupann) { // Assume it's a root annotation, and skip if user didn't request it if (parentID != -1) { continue; } } else if (markupann->getInReplyToID() != parentID) { continue; } /* Create Annotation of the right subclass */ Annotation *annotation = nullptr; Annot::AnnotSubtype subType = ann->getType(); switch (subType) { case Annot::typeText: if (!wantTextAnnotations) { continue; } annotation = new TextAnnotation(TextAnnotation::Linked); break; case Annot::typeFreeText: if (!wantTextAnnotations) { continue; } annotation = new TextAnnotation(TextAnnotation::InPlace); break; case Annot::typeLine: if (!wantLineAnnotations) { continue; } annotation = new LineAnnotation(LineAnnotation::StraightLine); break; case Annot::typePolygon: case Annot::typePolyLine: if (!wantLineAnnotations) { continue; } annotation = new LineAnnotation(LineAnnotation::Polyline); break; case Annot::typeSquare: case Annot::typeCircle: if (!wantGeomAnnotations) { continue; } annotation = new GeomAnnotation(); break; case Annot::typeHighlight: case Annot::typeUnderline: case Annot::typeSquiggly: case Annot::typeStrikeOut: if (!wantHighlightAnnotations) { continue; } annotation = new HighlightAnnotation(); break; case Annot::typeStamp: if (!wantStampAnnotations) { continue; } annotation = new StampAnnotation(); break; case Annot::typeInk: if (!wantInkAnnotations) { continue; } annotation = new InkAnnotation(); break; case Annot::typeLink: /* TODO: Move logic to getters */ { if (!wantLinkAnnotations) { continue; } // parse Link params AnnotLink *linkann = static_cast(ann); LinkAnnotation *l = new LinkAnnotation(); annotation = l; // -> hlMode l->setLinkHighlightMode((LinkAnnotation::HighlightMode)linkann->getLinkEffect()); // -> link region // TODO // reading link action if (linkann->getAction()) { Link *popplerLink = PageData::convertLinkActionToLink(linkann->getAction(), doc, QRectF()); if (popplerLink) { l->setLinkDestination(popplerLink); } } break; } case Annot::typeCaret: if (!wantCaretAnnotations) { continue; } annotation = new CaretAnnotation(); break; case Annot::typeFileAttachment: /* TODO: Move logic to getters */ { if (!wantFileAttachmentAnnotations) { continue; } AnnotFileAttachment *attachann = static_cast(ann); FileAttachmentAnnotation *f = new FileAttachmentAnnotation(); annotation = f; // -> fileIcon f->setFileIconName(QString::fromLatin1(attachann->getName()->c_str())); // -> embeddedFile auto filespec = std::make_unique(attachann->getFile()); f->setEmbeddedFile(new EmbeddedFile(*new EmbeddedFileData(std::move(filespec)))); break; } case Annot::typeSound: /* TODO: Move logic to getters */ { if (!wantSoundAnnotations) { continue; } AnnotSound *soundann = static_cast(ann); SoundAnnotation *s = new SoundAnnotation(); annotation = s; // -> soundIcon s->setSoundIconName(QString::fromLatin1(soundann->getName()->c_str())); // -> sound s->setSound(new SoundObject(soundann->getSound())); break; } case Annot::typeMovie: /* TODO: Move logic to getters */ { if (!wantMovieAnnotations) { continue; } AnnotMovie *movieann = static_cast(ann); MovieAnnotation *m = new MovieAnnotation(); annotation = m; // -> movie MovieObject *movie = new MovieObject(movieann); m->setMovie(movie); // -> movieTitle const GooString *movietitle = movieann->getTitle(); if (movietitle) { m->setMovieTitle(QString::fromLatin1(movietitle->c_str())); } break; } case Annot::typeScreen: { if (!wantScreenAnnotations) { continue; } AnnotScreen *screenann = static_cast(ann); // TODO Support other link types than Link::Rendition in ScreenAnnotation if (!screenann->getAction() || screenann->getAction()->getKind() != actionRendition) { continue; } ScreenAnnotation *s = new ScreenAnnotation(); annotation = s; // -> screen Link *popplerLink = PageData::convertLinkActionToLink(screenann->getAction(), doc, QRectF()); s->setAction(static_cast(popplerLink)); // -> screenTitle const GooString *screentitle = screenann->getTitle(); if (screentitle) { s->setScreenTitle(UnicodeParsedString(screentitle)); } break; } case Annot::typePopup: continue; // popups are parsed by Annotation's window() getter case Annot::typeUnknown: continue; // special case for ignoring unknown annotations case Annot::typeWidget: if (!wantWidgetAnnotations) { continue; } annotation = new WidgetAnnotation(); break; case Annot::typeRichMedia: { const AnnotRichMedia *annotRichMedia = static_cast(ann); RichMediaAnnotation *richMediaAnnotation = new RichMediaAnnotation; const AnnotRichMedia::Settings *annotSettings = annotRichMedia->getSettings(); if (annotSettings) { RichMediaAnnotation::Settings *settings = new RichMediaAnnotation::Settings; if (annotSettings->getActivation()) { RichMediaAnnotation::Activation *activation = new RichMediaAnnotation::Activation; switch (annotSettings->getActivation()->getCondition()) { case AnnotRichMedia::Activation::conditionPageOpened: activation->setCondition(RichMediaAnnotation::Activation::PageOpened); break; case AnnotRichMedia::Activation::conditionPageVisible: activation->setCondition(RichMediaAnnotation::Activation::PageVisible); break; case AnnotRichMedia::Activation::conditionUserAction: activation->setCondition(RichMediaAnnotation::Activation::UserAction); break; } settings->setActivation(activation); } if (annotSettings->getDeactivation()) { RichMediaAnnotation::Deactivation *deactivation = new RichMediaAnnotation::Deactivation; switch (annotSettings->getDeactivation()->getCondition()) { case AnnotRichMedia::Deactivation::conditionPageClosed: deactivation->setCondition(RichMediaAnnotation::Deactivation::PageClosed); break; case AnnotRichMedia::Deactivation::conditionPageInvisible: deactivation->setCondition(RichMediaAnnotation::Deactivation::PageInvisible); break; case AnnotRichMedia::Deactivation::conditionUserAction: deactivation->setCondition(RichMediaAnnotation::Deactivation::UserAction); break; } settings->setDeactivation(deactivation); } richMediaAnnotation->setSettings(settings); } const AnnotRichMedia::Content *annotContent = annotRichMedia->getContent(); if (annotContent) { RichMediaAnnotation::Content *content = new RichMediaAnnotation::Content; const int configurationsCount = annotContent->getConfigurationsCount(); if (configurationsCount > 0) { QList configurations; for (int i = 0; i < configurationsCount; ++i) { const AnnotRichMedia::Configuration *annotConfiguration = annotContent->getConfiguration(i); if (!annotConfiguration) { continue; } RichMediaAnnotation::Configuration *configuration = new RichMediaAnnotation::Configuration; if (annotConfiguration->getName()) { configuration->setName(UnicodeParsedString(annotConfiguration->getName())); } switch (annotConfiguration->getType()) { case AnnotRichMedia::Configuration::type3D: configuration->setType(RichMediaAnnotation::Configuration::Type3D); break; case AnnotRichMedia::Configuration::typeFlash: configuration->setType(RichMediaAnnotation::Configuration::TypeFlash); break; case AnnotRichMedia::Configuration::typeSound: configuration->setType(RichMediaAnnotation::Configuration::TypeSound); break; case AnnotRichMedia::Configuration::typeVideo: configuration->setType(RichMediaAnnotation::Configuration::TypeVideo); break; } const int instancesCount = annotConfiguration->getInstancesCount(); if (instancesCount > 0) { QList instances; for (int j = 0; j < instancesCount; ++j) { const AnnotRichMedia::Instance *annotInstance = annotConfiguration->getInstance(j); if (!annotInstance) { continue; } RichMediaAnnotation::Instance *instance = new RichMediaAnnotation::Instance; switch (annotInstance->getType()) { case AnnotRichMedia::Instance::type3D: instance->setType(RichMediaAnnotation::Instance::Type3D); break; case AnnotRichMedia::Instance::typeFlash: instance->setType(RichMediaAnnotation::Instance::TypeFlash); break; case AnnotRichMedia::Instance::typeSound: instance->setType(RichMediaAnnotation::Instance::TypeSound); break; case AnnotRichMedia::Instance::typeVideo: instance->setType(RichMediaAnnotation::Instance::TypeVideo); break; } const AnnotRichMedia::Params *annotParams = annotInstance->getParams(); if (annotParams) { RichMediaAnnotation::Params *params = new RichMediaAnnotation::Params; if (annotParams->getFlashVars()) { params->setFlashVars(UnicodeParsedString(annotParams->getFlashVars())); } instance->setParams(params); } instances.append(instance); } configuration->setInstances(instances); } configurations.append(configuration); } content->setConfigurations(configurations); } const int assetsCount = annotContent->getAssetsCount(); if (assetsCount > 0) { QList assets; for (int i = 0; i < assetsCount; ++i) { const AnnotRichMedia::Asset *annotAsset = annotContent->getAsset(i); if (!annotAsset) { continue; } RichMediaAnnotation::Asset *asset = new RichMediaAnnotation::Asset; if (annotAsset->getName()) { asset->setName(UnicodeParsedString(annotAsset->getName())); } auto fileSpec = std::make_unique(annotAsset->getFileSpec()); asset->setEmbeddedFile(new EmbeddedFile(*new EmbeddedFileData(std::move(fileSpec)))); assets.append(asset); } content->setAssets(assets); } richMediaAnnotation->setContent(content); } annotation = richMediaAnnotation; break; } default: { #define CASE_FOR_TYPE(thetype) \ case Annot::type##thetype: \ error(errUnimplemented, -1, "Annotation " #thetype " not supported"); \ break; switch (subType) { CASE_FOR_TYPE(PrinterMark) CASE_FOR_TYPE(TrapNet) CASE_FOR_TYPE(Watermark) CASE_FOR_TYPE(3D) default: error(errUnimplemented, -1, "Annotation {0:d} not supported", subType); } continue; #undef CASE_FOR_TYPE } } annotation->d_ptr->tieToNativeAnnot(ann, pdfPage, doc); res.append(annotation); } return res; } Ref AnnotationPrivate::pdfObjectReference() const { if (pdfAnnot == nullptr) { return Ref::INVALID(); } return pdfAnnot->getRef(); } Link *AnnotationPrivate::additionalAction(Annotation::AdditionalActionType type) const { if (pdfAnnot->getType() != Annot::typeScreen && pdfAnnot->getType() != Annot::typeWidget) { return nullptr; } const Annot::AdditionalActionsType actionType = toPopplerAdditionalActionType(type); std::unique_ptr<::LinkAction> linkAction = nullptr; if (pdfAnnot->getType() == Annot::typeScreen) { linkAction = static_cast(pdfAnnot)->getAdditionalAction(actionType); } else { linkAction = static_cast(pdfAnnot)->getAdditionalAction(actionType); } Link *link = nullptr; if (linkAction) { link = PageData::convertLinkActionToLink(linkAction.get(), parentDoc, QRectF()); } return link; } void AnnotationPrivate::addAnnotationToPage(::Page *pdfPage, DocumentData *doc, const Annotation *ann) { if (ann->d_ptr->pdfAnnot != nullptr) { error(errIO, -1, "Annotation is already tied"); return; } // Unimplemented annotations can't be created by the user because their ctor // is private. Therefore, createNativeAnnot will never return 0 Annot *nativeAnnot = ann->d_ptr->createNativeAnnot(pdfPage, doc); Q_ASSERT(nativeAnnot); if (ann->d_ptr->annotationAppearance.isStream()) { nativeAnnot->setNewAppearance(ann->d_ptr->annotationAppearance.copy()); } pdfPage->addAnnot(nativeAnnot); } void AnnotationPrivate::removeAnnotationFromPage(::Page *pdfPage, const Annotation *ann) { if (ann->d_ptr->pdfAnnot == nullptr) { error(errIO, -1, "Annotation is not tied"); return; } if (ann->d_ptr->pdfPage != pdfPage) { error(errIO, -1, "Annotation doesn't belong to the specified page"); return; } // Remove annotation pdfPage->removeAnnot(ann->d_ptr->pdfAnnot); // Destroy object delete ann; } class TextAnnotationPrivate : public AnnotationPrivate { public: TextAnnotationPrivate(); Annotation *makeAlias() override; Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; void setDefaultAppearanceToNative(); std::unique_ptr getDefaultAppearanceFromNative() const; // data fields TextAnnotation::TextType textType; QString textIcon; std::optional textFont; QColor textColor = Qt::black; int inplaceAlign; // 0:left, 1:center, 2:right QVector inplaceCallout; TextAnnotation::InplaceIntent inplaceIntent; }; class Annotation::Style::Private : public QSharedData { public: Private() : opacity(1.0), width(1.0), lineStyle(Solid), xCorners(0.0), yCorners(0.0), lineEffect(NoEffect), effectIntensity(1.0) { dashArray.resize(1); dashArray[0] = 3; } QColor color; double opacity; double width; Annotation::LineStyle lineStyle; double xCorners; double yCorners; QVector dashArray; Annotation::LineEffect lineEffect; double effectIntensity; }; Annotation::Style::Style() : d(new Private) { } Annotation::Style::Style(const Style &other) : d(other.d) { } Annotation::Style &Annotation::Style::operator=(const Style &other) { if (this != &other) { d = other.d; } return *this; } Annotation::Style::~Style() { } QColor Annotation::Style::color() const { return d->color; } void Annotation::Style::setColor(const QColor &color) { d->color = color; } double Annotation::Style::opacity() const { return d->opacity; } void Annotation::Style::setOpacity(double opacity) { d->opacity = opacity; } double Annotation::Style::width() const { return d->width; } void Annotation::Style::setWidth(double width) { d->width = width; } Annotation::LineStyle Annotation::Style::lineStyle() const { return d->lineStyle; } void Annotation::Style::setLineStyle(Annotation::LineStyle style) { d->lineStyle = style; } double Annotation::Style::xCorners() const { return d->xCorners; } void Annotation::Style::setXCorners(double radius) { d->xCorners = radius; } double Annotation::Style::yCorners() const { return d->yCorners; } void Annotation::Style::setYCorners(double radius) { d->yCorners = radius; } const QVector &Annotation::Style::dashArray() const { return d->dashArray; } void Annotation::Style::setDashArray(const QVector &array) { d->dashArray = array; } Annotation::LineEffect Annotation::Style::lineEffect() const { return d->lineEffect; } void Annotation::Style::setLineEffect(Annotation::LineEffect effect) { d->lineEffect = effect; } double Annotation::Style::effectIntensity() const { return d->effectIntensity; } void Annotation::Style::setEffectIntensity(double intens) { d->effectIntensity = intens; } class Annotation::Popup::Private : public QSharedData { public: Private() : flags(-1) { } int flags; QRectF geometry; QString title; QString summary; QString text; }; Annotation::Popup::Popup() : d(new Private) { } Annotation::Popup::Popup(const Popup &other) : d(other.d) { } Annotation::Popup &Annotation::Popup::operator=(const Popup &other) { if (this != &other) { d = other.d; } return *this; } Annotation::Popup::~Popup() { } int Annotation::Popup::flags() const { return d->flags; } void Annotation::Popup::setFlags(int flags) { d->flags = flags; } QRectF Annotation::Popup::geometry() const { return d->geometry; } void Annotation::Popup::setGeometry(const QRectF &geom) { d->geometry = geom; } QString Annotation::Popup::title() const { return d->title; } void Annotation::Popup::setTitle(const QString &title) { d->title = title; } QString Annotation::Popup::summary() const { return d->summary; } void Annotation::Popup::setSummary(const QString &summary) { d->summary = summary; } QString Annotation::Popup::text() const { return d->text; } void Annotation::Popup::setText(const QString &text) { d->text = text; } Annotation::Annotation(AnnotationPrivate &dd) : d_ptr(&dd) { } Annotation::~Annotation() { } Annotation::Annotation(AnnotationPrivate &dd, const QDomNode &annNode) : d_ptr(&dd) { Q_D(Annotation); // get the [base] element of the annotation node QDomElement e = AnnotationUtils::findChildElement(annNode, QStringLiteral("base")); if (e.isNull()) { return; } Style s; Popup w; // parse -contents- attributes if (e.hasAttribute(QStringLiteral("author"))) { setAuthor(e.attribute(QStringLiteral("author"))); } if (e.hasAttribute(QStringLiteral("contents"))) { setContents(e.attribute(QStringLiteral("contents"))); } if (e.hasAttribute(QStringLiteral("uniqueName"))) { setUniqueName(e.attribute(QStringLiteral("uniqueName"))); } if (e.hasAttribute(QStringLiteral("modifyDate"))) { QDateTime dt = QDateTime::fromString(e.attribute(QStringLiteral("modifyDate"))); if (!dt.isValid()) { dt = QDateTime::fromString(e.attribute(QStringLiteral("modifyDate")), Qt::ISODate); } setModificationDate(dt); } if (e.hasAttribute(QStringLiteral("creationDate"))) { QDateTime dt = QDateTime::fromString(e.attribute(QStringLiteral("creationDate"))); if (!dt.isValid()) { dt = QDateTime::fromString(e.attribute(QStringLiteral("creationDate")), Qt::ISODate); } setCreationDate(dt); } // parse -other- attributes if (e.hasAttribute(QStringLiteral("flags"))) { setFlags(e.attribute(QStringLiteral("flags")).toInt()); } if (e.hasAttribute(QStringLiteral("color"))) { s.setColor(QColor(e.attribute(QStringLiteral("color")))); } if (e.hasAttribute(QStringLiteral("opacity"))) { s.setOpacity(e.attribute(QStringLiteral("opacity")).toDouble()); } // parse -the-subnodes- (describing Style, Window, Revision(s) structures) // Note: all subnodes if present must be 'attributes complete' QDomNode eSubNode = e.firstChild(); while (eSubNode.isElement()) { QDomElement ee = eSubNode.toElement(); eSubNode = eSubNode.nextSibling(); // parse boundary if (ee.tagName() == QLatin1String("boundary")) { QRectF brect; brect.setLeft(ee.attribute(QStringLiteral("l")).toDouble()); brect.setTop(ee.attribute(QStringLiteral("t")).toDouble()); brect.setRight(ee.attribute(QStringLiteral("r")).toDouble()); brect.setBottom(ee.attribute(QStringLiteral("b")).toDouble()); setBoundary(brect); } // parse penStyle if not default else if (ee.tagName() == QLatin1String("penStyle")) { s.setWidth(ee.attribute(QStringLiteral("width")).toDouble()); s.setLineStyle((LineStyle)ee.attribute(QStringLiteral("style")).toInt()); s.setXCorners(ee.attribute(QStringLiteral("xcr")).toDouble()); s.setYCorners(ee.attribute(QStringLiteral("ycr")).toDouble()); // Try to parse dash array (new format) QVector dashArray; QDomNode eeSubNode = ee.firstChild(); while (eeSubNode.isElement()) { QDomElement eee = eeSubNode.toElement(); eeSubNode = eeSubNode.nextSibling(); if (eee.tagName() != QLatin1String("dashsegm")) { continue; } dashArray.append(eee.attribute(QStringLiteral("len")).toDouble()); } // If no segments were found use marks/spaces (old format) if (dashArray.size() == 0) { dashArray.append(ee.attribute(QStringLiteral("marks")).toDouble()); dashArray.append(ee.attribute(QStringLiteral("spaces")).toDouble()); } s.setDashArray(dashArray); } // parse effectStyle if not default else if (ee.tagName() == QLatin1String("penEffect")) { s.setLineEffect((LineEffect)ee.attribute(QStringLiteral("effect")).toInt()); s.setEffectIntensity(ee.attribute(QStringLiteral("intensity")).toDouble()); } // parse window if present else if (ee.tagName() == QLatin1String("window")) { QRectF geom; geom.setX(ee.attribute(QStringLiteral("top")).toDouble()); geom.setY(ee.attribute(QStringLiteral("left")).toDouble()); if (ee.hasAttribute(QStringLiteral("widthDouble"))) { geom.setWidth(ee.attribute(QStringLiteral("widthDouble")).toDouble()); } else { geom.setWidth(ee.attribute(QStringLiteral("width")).toDouble()); } if (ee.hasAttribute(QStringLiteral("widthDouble"))) { geom.setHeight(ee.attribute(QStringLiteral("heightDouble")).toDouble()); } else { geom.setHeight(ee.attribute(QStringLiteral("height")).toDouble()); } w.setGeometry(geom); w.setFlags(ee.attribute(QStringLiteral("flags")).toInt()); w.setTitle(ee.attribute(QStringLiteral("title"))); w.setSummary(ee.attribute(QStringLiteral("summary"))); // parse window subnodes QDomNode winNode = ee.firstChild(); for (; winNode.isElement(); winNode = winNode.nextSibling()) { QDomElement winElement = winNode.toElement(); if (winElement.tagName() == QLatin1String("text")) { w.setText(winElement.firstChild().toCDATASection().data()); } } } } setStyle(s); // assign parsed style setPopup(w); // assign parsed window // get the [revisions] element of the annotation node QDomNode revNode = annNode.firstChild(); for (; revNode.isElement(); revNode = revNode.nextSibling()) { QDomElement revElement = revNode.toElement(); if (revElement.tagName() != QLatin1String("revision")) { continue; } Annotation *reply = AnnotationUtils::createAnnotation(revElement); if (reply) // if annotation is valid, add as a revision of this annotation { RevScope scope = (RevScope)revElement.attribute(QStringLiteral("revScope")).toInt(); RevType type = (RevType)revElement.attribute(QStringLiteral("revType")).toInt(); d->addRevision(reply, scope, type); delete reply; } } } void Annotation::storeBaseAnnotationProperties(QDomNode &annNode, QDomDocument &document) const { // create [base] element of the annotation node QDomElement e = document.createElement(QStringLiteral("base")); annNode.appendChild(e); const Style s = style(); const Popup w = popup(); // store -contents- attributes if (!author().isEmpty()) { e.setAttribute(QStringLiteral("author"), author()); } if (!contents().isEmpty()) { e.setAttribute(QStringLiteral("contents"), contents()); } if (!uniqueName().isEmpty()) { e.setAttribute(QStringLiteral("uniqueName"), uniqueName()); } if (modificationDate().isValid()) { e.setAttribute(QStringLiteral("modifyDate"), modificationDate().toString()); } if (creationDate().isValid()) { e.setAttribute(QStringLiteral("creationDate"), creationDate().toString()); } // store -other- attributes if (flags()) { e.setAttribute(QStringLiteral("flags"), flags()); } if (s.color().isValid()) { e.setAttribute(QStringLiteral("color"), s.color().name()); } if (s.opacity() != 1.0) { e.setAttribute(QStringLiteral("opacity"), QString::number(s.opacity())); } // Sub-Node-1 - boundary const QRectF brect = boundary(); QDomElement bE = document.createElement(QStringLiteral("boundary")); e.appendChild(bE); bE.setAttribute(QStringLiteral("l"), QString::number((double)brect.left())); bE.setAttribute(QStringLiteral("t"), QString::number((double)brect.top())); bE.setAttribute(QStringLiteral("r"), QString::number((double)brect.right())); bE.setAttribute(QStringLiteral("b"), QString::number((double)brect.bottom())); // Sub-Node-2 - penStyle const QVector &dashArray = s.dashArray(); if (s.width() != 1 || s.lineStyle() != Solid || s.xCorners() != 0 || s.yCorners() != 0.0 || dashArray.size() != 1 || dashArray[0] != 3) { QDomElement psE = document.createElement(QStringLiteral("penStyle")); e.appendChild(psE); psE.setAttribute(QStringLiteral("width"), QString::number(s.width())); psE.setAttribute(QStringLiteral("style"), (int)s.lineStyle()); psE.setAttribute(QStringLiteral("xcr"), QString::number(s.xCorners())); 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) { marks = (int)dashArray[0]; } if (dashArray.size() > 1) { spaces = (int)dashArray[1]; } psE.setAttribute(QStringLiteral("marks"), marks); psE.setAttribute(QStringLiteral("spaces"), spaces); foreach (double segm, dashArray) { QDomElement pattE = document.createElement(QStringLiteral("dashsegm")); pattE.setAttribute(QStringLiteral("len"), QString::number(segm)); psE.appendChild(pattE); } } // Sub-Node-3 - penEffect if (s.lineEffect() != NoEffect || s.effectIntensity() != 1.0) { QDomElement peE = document.createElement(QStringLiteral("penEffect")); e.appendChild(peE); peE.setAttribute(QStringLiteral("effect"), (int)s.lineEffect()); peE.setAttribute(QStringLiteral("intensity"), QString::number(s.effectIntensity())); } // Sub-Node-4 - window if (w.flags() != -1 || !w.title().isEmpty() || !w.summary().isEmpty() || !w.text().isEmpty()) { QDomElement wE = document.createElement(QStringLiteral("window")); const QRectF geom = w.geometry(); e.appendChild(wE); wE.setAttribute(QStringLiteral("flags"), w.flags()); wE.setAttribute(QStringLiteral("top"), QString::number(geom.x())); wE.setAttribute(QStringLiteral("left"), QString::number(geom.y())); wE.setAttribute(QStringLiteral("width"), (int)geom.width()); wE.setAttribute(QStringLiteral("height"), (int)geom.height()); wE.setAttribute(QStringLiteral("widthDouble"), QString::number(geom.width())); wE.setAttribute(QStringLiteral("heightDouble"), QString::number(geom.height())); wE.setAttribute(QStringLiteral("title"), w.title()); wE.setAttribute(QStringLiteral("summary"), w.summary()); // store window.text as a subnode, because we need escaped data if (!w.text().isEmpty()) { QDomElement escapedText = document.createElement(QStringLiteral("text")); wE.appendChild(escapedText); QDomCDATASection textCData = document.createCDATASection(w.text()); escapedText.appendChild(textCData); } } const QList revs = revisions(); // create [revision] element of the annotation node (if any) if (revs.isEmpty()) { return; } // add all revisions as children of revisions element foreach (const Annotation *rev, revs) { QDomElement r = document.createElement(QStringLiteral("revision")); annNode.appendChild(r); // set element attributes r.setAttribute(QStringLiteral("revScope"), (int)rev->revisionScope()); r.setAttribute(QStringLiteral("revType"), (int)rev->revisionType()); // use revision as the annotation element, so fill it up AnnotationUtils::storeAnnotation(rev, r, document); delete rev; } } QString Annotation::author() const { Q_D(const Annotation); if (!d->pdfAnnot) { return d->author; } const AnnotMarkup *markupann = dynamic_cast(d->pdfAnnot); return markupann ? UnicodeParsedString(markupann->getLabel()) : QString(); } void Annotation::setAuthor(const QString &author) { Q_D(Annotation); if (!d->pdfAnnot) { d->author = author; return; } AnnotMarkup *markupann = dynamic_cast(d->pdfAnnot); if (markupann) { markupann->setLabel(std::unique_ptr(QStringToUnicodeGooString(author))); } } QString Annotation::contents() const { Q_D(const Annotation); if (!d->pdfAnnot) { return d->contents; } return UnicodeParsedString(d->pdfAnnot->getContents()); } void Annotation::setContents(const QString &contents) { Q_D(Annotation); if (!d->pdfAnnot) { d->contents = contents; return; } d->pdfAnnot->setContents(std::unique_ptr(QStringToUnicodeGooString(contents))); TextAnnotationPrivate *textAnnotD = dynamic_cast(d); if (textAnnotD) { textAnnotD->setDefaultAppearanceToNative(); } } QString Annotation::uniqueName() const { Q_D(const Annotation); if (!d->pdfAnnot) { return d->uniqueName; } return UnicodeParsedString(d->pdfAnnot->getName()); } void Annotation::setUniqueName(const QString &uniqueName) { Q_D(Annotation); if (!d->pdfAnnot) { d->uniqueName = uniqueName; return; } QByteArray ascii = uniqueName.toLatin1(); GooString s(ascii.constData()); d->pdfAnnot->setName(&s); } QDateTime Annotation::modificationDate() const { Q_D(const Annotation); if (!d->pdfAnnot) { return d->modDate; } if (d->pdfAnnot->getModified()) { return convertDate(d->pdfAnnot->getModified()->c_str()); } else { return QDateTime(); } } void Annotation::setModificationDate(const QDateTime &date) { Q_D(Annotation); if (!d->pdfAnnot) { d->modDate = date; return; } if (d->pdfAnnot) { if (date.isValid()) { const time_t t = date.toSecsSinceEpoch(); GooString *s = timeToDateString(&t); d->pdfAnnot->setModified(s); delete s; } else { d->pdfAnnot->setModified(nullptr); } } } QDateTime Annotation::creationDate() const { Q_D(const Annotation); if (!d->pdfAnnot) { return d->creationDate; } const AnnotMarkup *markupann = dynamic_cast(d->pdfAnnot); if (markupann && markupann->getDate()) { return convertDate(markupann->getDate()->c_str()); } return modificationDate(); } void Annotation::setCreationDate(const QDateTime &date) { Q_D(Annotation); if (!d->pdfAnnot) { d->creationDate = date; return; } AnnotMarkup *markupann = dynamic_cast(d->pdfAnnot); if (markupann) { if (date.isValid()) { const time_t t = date.toSecsSinceEpoch(); GooString *s = timeToDateString(&t); markupann->setDate(s); delete s; } else { markupann->setDate(nullptr); } } } static int fromPdfFlags(int flags) { int qtflags = 0; if (flags & Annot::flagHidden) { qtflags |= Annotation::Hidden; } if (flags & Annot::flagNoZoom) { qtflags |= Annotation::FixedSize; } if (flags & Annot::flagNoRotate) { qtflags |= Annotation::FixedRotation; } if (!(flags & Annot::flagPrint)) { qtflags |= Annotation::DenyPrint; } if (flags & Annot::flagReadOnly) { qtflags |= (Annotation::DenyWrite | Annotation::DenyDelete); } if (flags & Annot::flagLocked) { qtflags |= Annotation::DenyDelete; } if (flags & Annot::flagToggleNoView) { qtflags |= Annotation::ToggleHidingOnMouse; } return qtflags; } static int toPdfFlags(int qtflags) { int flags = 0; if (qtflags & Annotation::Hidden) { flags |= Annot::flagHidden; } if (qtflags & Annotation::FixedSize) { flags |= Annot::flagNoZoom; } if (qtflags & Annotation::FixedRotation) { flags |= Annot::flagNoRotate; } if (!(qtflags & Annotation::DenyPrint)) { flags |= Annot::flagPrint; } if (qtflags & Annotation::DenyWrite) { flags |= Annot::flagReadOnly; } if (qtflags & Annotation::DenyDelete) { flags |= Annot::flagLocked; } if (qtflags & Annotation::ToggleHidingOnMouse) { flags |= Annot::flagToggleNoView; } return flags; } int Annotation::flags() const { Q_D(const Annotation); if (!d->pdfAnnot) { return d->flags; } return fromPdfFlags(d->pdfAnnot->getFlags()); } void Annotation::setFlags(int flags) { Q_D(Annotation); if (!d->pdfAnnot) { d->flags = flags; return; } d->pdfAnnot->setFlags(toPdfFlags(flags)); } QRectF Annotation::boundary() const { Q_D(const Annotation); if (!d->pdfAnnot) { return d->boundary; } const PDFRectangle &rect = d->pdfAnnot->getRect(); return d->fromPdfRectangle(rect); } void Annotation::setBoundary(const QRectF &boundary) { Q_D(Annotation); if (!d->pdfAnnot) { d->boundary = boundary; return; } const PDFRectangle rect = d->boundaryToPdfRectangle(boundary, flags()); if (rect == d->pdfAnnot->getRect()) { return; } d->pdfAnnot->setRect(&rect); } Annotation::Style Annotation::style() const { Q_D(const Annotation); if (!d->pdfAnnot) { return d->style; } Style s; s.setColor(convertAnnotColor(d->pdfAnnot->getColor())); const AnnotMarkup *markupann = dynamic_cast(d->pdfAnnot); if (markupann) { s.setOpacity(markupann->getOpacity()); } const AnnotBorder *border = d->pdfAnnot->getBorder(); if (border) { if (border->getType() == AnnotBorder::typeArray) { const AnnotBorderArray *border_array = static_cast(border); s.setXCorners(border_array->getHorizontalCorner()); s.setYCorners(border_array->getVerticalCorner()); } s.setWidth(border->getWidth()); 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; switch (d->pdfAnnot->getType()) { case Annot::typeFreeText: border_effect = static_cast(d->pdfAnnot)->getBorderEffect(); break; case Annot::typeSquare: case Annot::typeCircle: border_effect = static_cast(d->pdfAnnot)->getBorderEffect(); break; default: border_effect = nullptr; } if (border_effect) { s.setLineEffect((Annotation::LineEffect)border_effect->getEffectType()); s.setEffectIntensity(border_effect->getIntensity()); } return s; } void Annotation::setStyle(const Annotation::Style &style) { Q_D(Annotation); if (!d->pdfAnnot) { d->style = style; return; } d->pdfAnnot->setColor(convertQColor(style.color())); AnnotMarkup *markupann = dynamic_cast(d->pdfAnnot); if (markupann) { markupann->setOpacity(style.opacity()); } auto border = std::make_unique(); border->setWidth(style.width()); border->setHorizontalCorner(style.xCorners()); border->setVerticalCorner(style.yCorners()); d->pdfAnnot->setBorder(std::move(border)); } Annotation::Popup Annotation::popup() const { Q_D(const Annotation); if (!d->pdfAnnot) { return d->popup; } Popup w; AnnotPopup *popup = nullptr; int flags = -1; // Not initialized const AnnotMarkup *markupann = dynamic_cast(d->pdfAnnot); if (markupann) { popup = markupann->getPopup(); w.setSummary(UnicodeParsedString(markupann->getSubject())); } if (popup) { flags = fromPdfFlags(popup->getFlags()) & (Annotation::Hidden | Annotation::FixedSize | Annotation::FixedRotation); if (!popup->getOpen()) { flags |= Annotation::Hidden; } const PDFRectangle &rect = popup->getRect(); w.setGeometry(d->fromPdfRectangle(rect)); } if (d->pdfAnnot->getType() == Annot::typeText) { const AnnotText *textann = static_cast(d->pdfAnnot); // Text annotations default to same rect as annotation if (flags == -1) { flags = 0; w.setGeometry(boundary()); } // If text is not 'opened', force window hiding. if the window // was parsed from popup, the flag should already be set if (!textann->getOpen() && flags != -1) { flags |= Annotation::Hidden; } } w.setFlags(flags); return w; } void Annotation::setPopup(const Annotation::Popup &popup) { Q_D(Annotation); if (!d->pdfAnnot) { d->popup = popup; return; } #if 0 /* TODO: Remove old popup and add AnnotPopup to page */ AnnotMarkup *markupann = dynamic_cast(d->pdfAnnot); if (!markupann) return; // Create a new AnnotPopup and assign it to pdfAnnot PDFRectangle rect = d->toPdfRectangle( popup.geometry() ); AnnotPopup * p = new AnnotPopup( d->pdfPage->getDoc(), &rect ); p->setOpen( !(popup.flags() & Annotation::Hidden) ); if (!popup.summary().isEmpty()) { GooString *s = QStringToUnicodeGooString(popup.summary()); markupann->setLabel(s); delete s; } markupann->setPopup(p); #endif } Annotation::RevScope Annotation::revisionScope() const { Q_D(const Annotation); if (!d->pdfAnnot) { return d->revisionScope; } const AnnotMarkup *markupann = dynamic_cast(d->pdfAnnot); if (markupann && markupann->isInReplyTo()) { switch (markupann->getReplyTo()) { case AnnotMarkup::replyTypeR: return Annotation::Reply; case AnnotMarkup::replyTypeGroup: return Annotation::Group; } } return Annotation::Root; // It's not a revision } Annotation::RevType Annotation::revisionType() const { Q_D(const Annotation); if (!d->pdfAnnot) { return d->revisionType; } const AnnotText *textann = dynamic_cast(d->pdfAnnot); if (textann && textann->isInReplyTo()) { switch (textann->getState()) { case AnnotText::stateMarked: return Annotation::Marked; case AnnotText::stateUnmarked: return Annotation::Unmarked; case AnnotText::stateAccepted: return Annotation::Accepted; case AnnotText::stateRejected: return Annotation::Rejected; case AnnotText::stateCancelled: return Annotation::Cancelled; case AnnotText::stateCompleted: return Annotation::Completed; default: break; } } return Annotation::None; } QList Annotation::revisions() const { Q_D(const Annotation); if (!d->pdfAnnot) { /* Return aliases, whose ownership goes to the caller */ QList res; foreach (Annotation *rev, d->revisions) res.append(rev->d_ptr->makeAlias()); return res; } /* If the annotation doesn't live in a object on its own (eg bug51361), it * has no ref, therefore it can't have revisions */ if (!d->pdfAnnot->getHasRef()) { return QList(); } return AnnotationPrivate::findAnnotations(d->pdfPage, d->parentDoc, QSet(), d->pdfAnnot->getId()); } std::unique_ptr Annotation::annotationAppearance() const { Q_D(const Annotation); return std::make_unique(new AnnotationAppearancePrivate(d->pdfAnnot)); } void Annotation::setAnnotationAppearance(const AnnotationAppearance &annotationAppearance) { Q_D(Annotation); if (!d->pdfAnnot) { d->annotationAppearance = annotationAppearance.d->appearance.copy(); return; } // Moving the appearance object using std::move would result // in the object being completed moved from the AnnotationAppearancePrivate // class. So, we'll not be able to retrieve the stamp's original AP stream d->pdfAnnot->setNewAppearance(annotationAppearance.d->appearance.copy()); } // END Annotation implementation /** TextAnnotation [Annotation] */ TextAnnotationPrivate::TextAnnotationPrivate() : AnnotationPrivate(), textType(TextAnnotation::Linked), textIcon(QStringLiteral("Note")), inplaceAlign(0), inplaceIntent(TextAnnotation::Unknown) { } Annotation *TextAnnotationPrivate::makeAlias() { return new TextAnnotation(*this); } Annot *TextAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) { // Setters are defined in the public class TextAnnotation *q = static_cast(makeAlias()); // Set page and contents pdfPage = destPage; parentDoc = doc; // Set pdfAnnot PDFRectangle rect = boundaryToPdfRectangle(boundary, flags); if (textType == TextAnnotation::Linked) { pdfAnnot = new AnnotText { destPage->getDoc(), &rect }; } else { const double pointSize = textFont ? textFont->pointSizeF() : AnnotFreeText::undefinedFontPtSize; if (pointSize < 0) { qWarning() << "TextAnnotationPrivate::createNativeAnnot: font pointSize < 0"; } pdfAnnot = new AnnotFreeText { destPage->getDoc(), &rect }; } // Set properties flushBaseAnnotationProperties(); q->setTextIcon(textIcon); q->setInplaceAlign(inplaceAlign); q->setCalloutPoints(inplaceCallout); q->setInplaceIntent(inplaceIntent); delete q; inplaceCallout.clear(); // Free up memory setDefaultAppearanceToNative(); return pdfAnnot; } void TextAnnotationPrivate::setDefaultAppearanceToNative() { if (pdfAnnot && pdfAnnot->getType() == Annot::typeFreeText) { AnnotFreeText *ftextann = static_cast(pdfAnnot); const double pointSize = textFont ? textFont->pointSizeF() : AnnotFreeText::undefinedFontPtSize; if (pointSize < 0) { qWarning() << "TextAnnotationPrivate::createNativeAnnot: font pointSize < 0"; } std::string fontName = "Invalid_font"; if (textFont) { Form *form = pdfPage->getDoc()->getCatalog()->getCreateForm(); if (form) { fontName = form->findFontInDefaultResources(textFont->family().toStdString(), textFont->styleName().toStdString()); if (fontName.empty()) { fontName = form->addFontToDefaultResources(textFont->family().toStdString(), textFont->styleName().toStdString()).fontName; } if (!fontName.empty()) { form->ensureFontsForAllCharacters(pdfAnnot->getContents(), fontName); } else { fontName = "Invalid_font"; } } } DefaultAppearance da { { objName, fontName.c_str() }, pointSize, convertQColor(textColor) }; ftextann->setDefaultAppearance(da); } } std::unique_ptr TextAnnotationPrivate::getDefaultAppearanceFromNative() const { if (pdfAnnot && pdfAnnot->getType() == Annot::typeFreeText) { AnnotFreeText *ftextann = static_cast(pdfAnnot); return ftextann->getDefaultAppearance(); } else { return {}; } } TextAnnotation::TextAnnotation(TextAnnotation::TextType type) : Annotation(*new TextAnnotationPrivate()) { setTextType(type); } TextAnnotation::TextAnnotation(TextAnnotationPrivate &dd) : Annotation(dd) { } TextAnnotation::TextAnnotation(const QDomNode &node) : Annotation(*new TextAnnotationPrivate, node) { // loop through the whole children looking for a 'text' element QDomNode subNode = node.firstChild(); while (subNode.isElement()) { QDomElement e = subNode.toElement(); subNode = subNode.nextSibling(); if (e.tagName() != QLatin1String("text")) { continue; } // parse the attributes if (e.hasAttribute(QStringLiteral("type"))) { setTextType((TextAnnotation::TextType)e.attribute(QStringLiteral("type")).toInt()); } if (e.hasAttribute(QStringLiteral("icon"))) { setTextIcon(e.attribute(QStringLiteral("icon"))); } if (e.hasAttribute(QStringLiteral("font"))) { QFont font; font.fromString(e.attribute(QStringLiteral("font"))); setTextFont(font); if (e.hasAttribute(QStringLiteral("fontColor"))) { const QColor color = QColor(e.attribute(QStringLiteral("fontColor"))); setTextColor(color); } } if (e.hasAttribute(QStringLiteral("align"))) { setInplaceAlign(e.attribute(QStringLiteral("align")).toInt()); } if (e.hasAttribute(QStringLiteral("intent"))) { setInplaceIntent((TextAnnotation::InplaceIntent)e.attribute(QStringLiteral("intent")).toInt()); } // parse the subnodes QDomNode eSubNode = e.firstChild(); while (eSubNode.isElement()) { QDomElement ee = eSubNode.toElement(); eSubNode = eSubNode.nextSibling(); if (ee.tagName() == QLatin1String("escapedText")) { setContents(ee.firstChild().toCDATASection().data()); } else if (ee.tagName() == QLatin1String("callout")) { QVector points(3); points[0] = QPointF(ee.attribute(QStringLiteral("ax")).toDouble(), ee.attribute(QStringLiteral("ay")).toDouble()); points[1] = QPointF(ee.attribute(QStringLiteral("bx")).toDouble(), ee.attribute(QStringLiteral("by")).toDouble()); points[2] = QPointF(ee.attribute(QStringLiteral("cx")).toDouble(), ee.attribute(QStringLiteral("cy")).toDouble()); setCalloutPoints(points); } } // loading complete break; } } TextAnnotation::~TextAnnotation() { } void TextAnnotation::store(QDomNode &node, QDomDocument &document) const { // store base annotation properties storeBaseAnnotationProperties(node, document); // create [text] element QDomElement textElement = document.createElement(QStringLiteral("text")); node.appendChild(textElement); // store the optional attributes if (textType() != Linked) { textElement.setAttribute(QStringLiteral("type"), (int)textType()); } if (textIcon() != QLatin1String("Note")) { textElement.setAttribute(QStringLiteral("icon"), textIcon()); } if (inplaceAlign()) { textElement.setAttribute(QStringLiteral("align"), inplaceAlign()); } if (inplaceIntent() != Unknown) { textElement.setAttribute(QStringLiteral("intent"), (int)inplaceIntent()); } textElement.setAttribute(QStringLiteral("font"), textFont().toString()); textElement.setAttribute(QStringLiteral("fontColor"), textColor().name()); // Sub-Node-1 - escapedText if (!contents().isEmpty()) { QDomElement escapedText = document.createElement(QStringLiteral("escapedText")); textElement.appendChild(escapedText); QDomCDATASection textCData = document.createCDATASection(contents()); escapedText.appendChild(textCData); } // Sub-Node-2 - callout if (calloutPoint(0).x() != 0.0) { QDomElement calloutElement = document.createElement(QStringLiteral("callout")); textElement.appendChild(calloutElement); calloutElement.setAttribute(QStringLiteral("ax"), QString::number(calloutPoint(0).x())); calloutElement.setAttribute(QStringLiteral("ay"), QString::number(calloutPoint(0).y())); calloutElement.setAttribute(QStringLiteral("bx"), QString::number(calloutPoint(1).x())); calloutElement.setAttribute(QStringLiteral("by"), QString::number(calloutPoint(1).y())); calloutElement.setAttribute(QStringLiteral("cx"), QString::number(calloutPoint(2).x())); calloutElement.setAttribute(QStringLiteral("cy"), QString::number(calloutPoint(2).y())); } } Annotation::SubType TextAnnotation::subType() const { return AText; } TextAnnotation::TextType TextAnnotation::textType() const { Q_D(const TextAnnotation); if (!d->pdfAnnot) { return d->textType; } return d->pdfAnnot->getType() == Annot::typeText ? TextAnnotation::Linked : TextAnnotation::InPlace; } void TextAnnotation::setTextType(TextAnnotation::TextType type) { Q_D(TextAnnotation); if (!d->pdfAnnot) { d->textType = type; return; } // Type cannot be changed if annotation is already tied qWarning() << "You can't change the type of a TextAnnotation that is already in a page"; } QString TextAnnotation::textIcon() const { Q_D(const TextAnnotation); if (!d->pdfAnnot) { return d->textIcon; } if (d->pdfAnnot->getType() == Annot::typeText) { const AnnotText *textann = static_cast(d->pdfAnnot); return QString::fromLatin1(textann->getIcon()->c_str()); } return QString(); } void TextAnnotation::setTextIcon(const QString &icon) { Q_D(TextAnnotation); if (!d->pdfAnnot) { d->textIcon = icon; return; } if (d->pdfAnnot->getType() == Annot::typeText) { AnnotText *textann = static_cast(d->pdfAnnot); QByteArray encoded = icon.toLatin1(); GooString s(encoded.constData()); textann->setIcon(&s); } } QFont TextAnnotation::textFont() const { Q_D(const TextAnnotation); if (d->textFont) { return *d->textFont; } double fontSize { AnnotFreeText::undefinedFontPtSize }; if (d->pdfAnnot->getType() == Annot::typeFreeText) { std::unique_ptr da { d->getDefaultAppearanceFromNative() }; if (da && da->getFontPtSize() > 0) { fontSize = da->getFontPtSize(); } } QFont font; font.setPointSizeF(fontSize); return font; } void TextAnnotation::setTextFont(const QFont &font) { Q_D(TextAnnotation); if (font == d->textFont) { return; } d->textFont = font; d->setDefaultAppearanceToNative(); } QColor TextAnnotation::textColor() const { Q_D(const TextAnnotation); if (!d->pdfAnnot) { return d->textColor; } if (std::unique_ptr da { d->getDefaultAppearanceFromNative() }) { return convertAnnotColor(da->getFontColor()); } return {}; } void TextAnnotation::setTextColor(const QColor &color) { Q_D(TextAnnotation); if (color == d->textColor) { return; } d->textColor = color; d->setDefaultAppearanceToNative(); } int TextAnnotation::inplaceAlign() const { Q_D(const TextAnnotation); if (!d->pdfAnnot) { return d->inplaceAlign; } if (d->pdfAnnot->getType() == Annot::typeFreeText) { const AnnotFreeText *ftextann = static_cast(d->pdfAnnot); return static_cast(ftextann->getQuadding()); } return 0; } void TextAnnotation::setInplaceAlign(int align) { Q_D(TextAnnotation); if (!d->pdfAnnot) { d->inplaceAlign = align; return; } if (d->pdfAnnot->getType() == Annot::typeFreeText) { AnnotFreeText *ftextann = static_cast(d->pdfAnnot); ftextann->setQuadding((VariableTextQuadding)align); } } QPointF TextAnnotation::calloutPoint(int id) const { const QVector points = calloutPoints(); if (id < 0 || id >= points.size()) { return QPointF(); } else { return points[id]; } } QVector TextAnnotation::calloutPoints() const { Q_D(const TextAnnotation); if (!d->pdfAnnot) { return d->inplaceCallout; } if (d->pdfAnnot->getType() == Annot::typeText) { return QVector(); } const AnnotFreeText *ftextann = static_cast(d->pdfAnnot); const AnnotCalloutLine *callout = ftextann->getCalloutLine(); if (!callout) { return QVector(); } double MTX[6]; d->fillTransformationMTX(MTX); const AnnotCalloutMultiLine *callout_v6 = dynamic_cast(callout); QVector res(callout_v6 ? 3 : 2); XPDFReader::transform(MTX, callout->getX1(), callout->getY1(), res[0]); XPDFReader::transform(MTX, callout->getX2(), callout->getY2(), res[1]); if (callout_v6) { XPDFReader::transform(MTX, callout_v6->getX3(), callout_v6->getY3(), res[2]); } return res; } void TextAnnotation::setCalloutPoints(const QVector &points) { Q_D(TextAnnotation); if (!d->pdfAnnot) { d->inplaceCallout = points; return; } if (d->pdfAnnot->getType() != Annot::typeFreeText) { return; } AnnotFreeText *ftextann = static_cast(d->pdfAnnot); const int count = points.size(); if (count == 0) { ftextann->setCalloutLine(nullptr); return; } if (count != 2 && count != 3) { error(errSyntaxError, -1, "Expected zero, two or three points for callout"); return; } AnnotCalloutLine *callout; double x1, y1, x2, y2; double MTX[6]; d->fillTransformationMTX(MTX); XPDFReader::invTransform(MTX, points[0], x1, y1); XPDFReader::invTransform(MTX, points[1], x2, y2); if (count == 3) { double x3, y3; XPDFReader::invTransform(MTX, points[2], x3, y3); callout = new AnnotCalloutMultiLine(x1, y1, x2, y2, x3, y3); } else { callout = new AnnotCalloutLine(x1, y1, x2, y2); } ftextann->setCalloutLine(callout); delete callout; } TextAnnotation::InplaceIntent TextAnnotation::inplaceIntent() const { Q_D(const TextAnnotation); if (!d->pdfAnnot) { return d->inplaceIntent; } if (d->pdfAnnot->getType() == Annot::typeFreeText) { const AnnotFreeText *ftextann = static_cast(d->pdfAnnot); return (TextAnnotation::InplaceIntent)ftextann->getIntent(); } return TextAnnotation::Unknown; } void TextAnnotation::setInplaceIntent(TextAnnotation::InplaceIntent intent) { Q_D(TextAnnotation); if (!d->pdfAnnot) { d->inplaceIntent = intent; return; } if (d->pdfAnnot->getType() == Annot::typeFreeText) { AnnotFreeText *ftextann = static_cast(d->pdfAnnot); ftextann->setIntent((AnnotFreeText::AnnotFreeTextIntent)intent); } } /** LineAnnotation [Annotation] */ class LineAnnotationPrivate : public AnnotationPrivate { public: LineAnnotationPrivate(); Annotation *makeAlias() override; Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; // data fields (note uses border for rendering style) QLinkedList linePoints; LineAnnotation::TermStyle lineStartStyle; LineAnnotation::TermStyle lineEndStyle; bool lineClosed : 1; // (if true draw close shape) bool lineShowCaption : 1; LineAnnotation::LineType lineType; QColor lineInnerColor; double lineLeadingFwdPt; double lineLeadingBackPt; LineAnnotation::LineIntent lineIntent; }; LineAnnotationPrivate::LineAnnotationPrivate() : AnnotationPrivate(), lineStartStyle(LineAnnotation::None), lineEndStyle(LineAnnotation::None), lineClosed(false), lineShowCaption(false), lineLeadingFwdPt(0), lineLeadingBackPt(0), lineIntent(LineAnnotation::Unknown) { } Annotation *LineAnnotationPrivate::makeAlias() { return new LineAnnotation(*this); } Annot *LineAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) { // Setters are defined in the public class LineAnnotation *q = static_cast(makeAlias()); // Set page and document pdfPage = destPage; parentDoc = doc; // Set pdfAnnot PDFRectangle rect = boundaryToPdfRectangle(boundary, flags); if (lineType == LineAnnotation::StraightLine) { pdfAnnot = new AnnotLine(doc->doc, &rect); } else { pdfAnnot = new AnnotPolygon(doc->doc, &rect, lineClosed ? Annot::typePolygon : Annot::typePolyLine); } // Set properties flushBaseAnnotationProperties(); q->setLinePoints(linePoints); q->setLineStartStyle(lineStartStyle); q->setLineEndStyle(lineEndStyle); q->setLineInnerColor(lineInnerColor); q->setLineLeadingForwardPoint(lineLeadingFwdPt); q->setLineLeadingBackPoint(lineLeadingBackPt); q->setLineShowCaption(lineShowCaption); q->setLineIntent(lineIntent); delete q; linePoints.clear(); // Free up memory return pdfAnnot; } LineAnnotation::LineAnnotation(LineAnnotation::LineType type) : Annotation(*new LineAnnotationPrivate()) { setLineType(type); } LineAnnotation::LineAnnotation(LineAnnotationPrivate &dd) : Annotation(dd) { } LineAnnotation::LineAnnotation(const QDomNode &node) : Annotation(*new LineAnnotationPrivate(), node) { // loop through the whole children looking for a 'line' element QDomNode subNode = node.firstChild(); while (subNode.isElement()) { QDomElement e = subNode.toElement(); subNode = subNode.nextSibling(); if (e.tagName() != QLatin1String("line")) { continue; } // parse the attributes if (e.hasAttribute(QStringLiteral("startStyle"))) { setLineStartStyle((LineAnnotation::TermStyle)e.attribute(QStringLiteral("startStyle")).toInt()); } if (e.hasAttribute(QStringLiteral("endStyle"))) { setLineEndStyle((LineAnnotation::TermStyle)e.attribute(QStringLiteral("endStyle")).toInt()); } if (e.hasAttribute(QStringLiteral("closed"))) { setLineClosed(e.attribute(QStringLiteral("closed")).toInt()); } if (e.hasAttribute(QStringLiteral("innerColor"))) { setLineInnerColor(QColor(e.attribute(QStringLiteral("innerColor")))); } if (e.hasAttribute(QStringLiteral("leadFwd"))) { setLineLeadingForwardPoint(e.attribute(QStringLiteral("leadFwd")).toDouble()); } if (e.hasAttribute(QStringLiteral("leadBack"))) { setLineLeadingBackPoint(e.attribute(QStringLiteral("leadBack")).toDouble()); } if (e.hasAttribute(QStringLiteral("showCaption"))) { setLineShowCaption(e.attribute(QStringLiteral("showCaption")).toInt()); } if (e.hasAttribute(QStringLiteral("intent"))) { setLineIntent((LineAnnotation::LineIntent)e.attribute(QStringLiteral("intent")).toInt()); } // parse all 'point' subnodes QLinkedList points; QDomNode pointNode = e.firstChild(); while (pointNode.isElement()) { QDomElement pe = pointNode.toElement(); pointNode = pointNode.nextSibling(); if (pe.tagName() != QLatin1String("point")) { continue; } QPointF p(pe.attribute(QStringLiteral("x"), QStringLiteral("0.0")).toDouble(), pe.attribute(QStringLiteral("y"), QStringLiteral("0.0")).toDouble()); points.append(p); } setLinePoints(points); setLineType(points.size() == 2 ? StraightLine : Polyline); // loading complete break; } } LineAnnotation::~LineAnnotation() { } void LineAnnotation::store(QDomNode &node, QDomDocument &document) const { // store base annotation properties storeBaseAnnotationProperties(node, document); // create [line] element QDomElement lineElement = document.createElement(QStringLiteral("line")); node.appendChild(lineElement); // store the attributes if (lineStartStyle() != None) { lineElement.setAttribute(QStringLiteral("startStyle"), (int)lineStartStyle()); } if (lineEndStyle() != None) { lineElement.setAttribute(QStringLiteral("endStyle"), (int)lineEndStyle()); } if (isLineClosed()) { lineElement.setAttribute(QStringLiteral("closed"), isLineClosed()); } if (lineInnerColor().isValid()) { lineElement.setAttribute(QStringLiteral("innerColor"), lineInnerColor().name()); } if (lineLeadingForwardPoint() != 0.0) { lineElement.setAttribute(QStringLiteral("leadFwd"), QString::number(lineLeadingForwardPoint())); } if (lineLeadingBackPoint() != 0.0) { lineElement.setAttribute(QStringLiteral("leadBack"), QString::number(lineLeadingBackPoint())); } if (lineShowCaption()) { lineElement.setAttribute(QStringLiteral("showCaption"), lineShowCaption()); } if (lineIntent() != Unknown) { lineElement.setAttribute(QStringLiteral("intent"), lineIntent()); } // append the list of points const QLinkedList points = linePoints(); if (points.count() > 1) { QLinkedList::const_iterator it = points.begin(), end = points.end(); while (it != end) { const QPointF &p = *it; QDomElement pElement = document.createElement(QStringLiteral("point")); lineElement.appendChild(pElement); pElement.setAttribute(QStringLiteral("x"), QString::number(p.x())); pElement.setAttribute(QStringLiteral("y"), QString::number(p.y())); ++it; } } } Annotation::SubType LineAnnotation::subType() const { return ALine; } LineAnnotation::LineType LineAnnotation::lineType() const { Q_D(const LineAnnotation); if (!d->pdfAnnot) { return d->lineType; } return (d->pdfAnnot->getType() == Annot::typeLine) ? LineAnnotation::StraightLine : LineAnnotation::Polyline; } void LineAnnotation::setLineType(LineAnnotation::LineType type) { Q_D(LineAnnotation); if (!d->pdfAnnot) { d->lineType = type; return; } // Type cannot be changed if annotation is already tied qWarning() << "You can't change the type of a LineAnnotation that is already in a page"; } QLinkedList LineAnnotation::linePoints() const { Q_D(const LineAnnotation); if (!d->pdfAnnot) { return d->linePoints; } double MTX[6]; d->fillTransformationMTX(MTX); QLinkedList res; if (d->pdfAnnot->getType() == Annot::typeLine) { const AnnotLine *lineann = static_cast(d->pdfAnnot); QPointF p; XPDFReader::transform(MTX, lineann->getX1(), lineann->getY1(), p); res.append(p); XPDFReader::transform(MTX, lineann->getX2(), lineann->getY2(), p); res.append(p); } else { const AnnotPolygon *polyann = static_cast(d->pdfAnnot); const AnnotPath *vertices = polyann->getVertices(); for (int i = 0; i < vertices->getCoordsLength(); ++i) { QPointF p; XPDFReader::transform(MTX, vertices->getX(i), vertices->getY(i), p); res.append(p); } } return res; } void LineAnnotation::setLinePoints(const QLinkedList &points) { Q_D(LineAnnotation); if (!d->pdfAnnot) { d->linePoints = points; return; } if (d->pdfAnnot->getType() == Annot::typeLine) { AnnotLine *lineann = static_cast(d->pdfAnnot); if (points.size() != 2) { error(errSyntaxError, -1, "Expected two points for a straight line"); return; } double x1, y1, x2, y2; double MTX[6]; d->fillTransformationMTX(MTX); XPDFReader::invTransform(MTX, points.first(), x1, y1); XPDFReader::invTransform(MTX, points.last(), x2, y2); lineann->setVertices(x1, y1, x2, y2); } else { AnnotPolygon *polyann = static_cast(d->pdfAnnot); AnnotPath *p = d->toAnnotPath(points); polyann->setVertices(p); delete p; } } LineAnnotation::TermStyle LineAnnotation::lineStartStyle() const { Q_D(const LineAnnotation); if (!d->pdfAnnot) { return d->lineStartStyle; } if (d->pdfAnnot->getType() == Annot::typeLine) { const AnnotLine *lineann = static_cast(d->pdfAnnot); return (LineAnnotation::TermStyle)lineann->getStartStyle(); } else { const AnnotPolygon *polyann = static_cast(d->pdfAnnot); return (LineAnnotation::TermStyle)polyann->getStartStyle(); } } void LineAnnotation::setLineStartStyle(LineAnnotation::TermStyle style) { Q_D(LineAnnotation); if (!d->pdfAnnot) { d->lineStartStyle = style; return; } if (d->pdfAnnot->getType() == Annot::typeLine) { AnnotLine *lineann = static_cast(d->pdfAnnot); lineann->setStartEndStyle((AnnotLineEndingStyle)style, lineann->getEndStyle()); } else { AnnotPolygon *polyann = static_cast(d->pdfAnnot); polyann->setStartEndStyle((AnnotLineEndingStyle)style, polyann->getEndStyle()); } } LineAnnotation::TermStyle LineAnnotation::lineEndStyle() const { Q_D(const LineAnnotation); if (!d->pdfAnnot) { return d->lineEndStyle; } if (d->pdfAnnot->getType() == Annot::typeLine) { const AnnotLine *lineann = static_cast(d->pdfAnnot); return (LineAnnotation::TermStyle)lineann->getEndStyle(); } else { const AnnotPolygon *polyann = static_cast(d->pdfAnnot); return (LineAnnotation::TermStyle)polyann->getEndStyle(); } } void LineAnnotation::setLineEndStyle(LineAnnotation::TermStyle style) { Q_D(LineAnnotation); if (!d->pdfAnnot) { d->lineEndStyle = style; return; } if (d->pdfAnnot->getType() == Annot::typeLine) { AnnotLine *lineann = static_cast(d->pdfAnnot); lineann->setStartEndStyle(lineann->getStartStyle(), (AnnotLineEndingStyle)style); } else { AnnotPolygon *polyann = static_cast(d->pdfAnnot); polyann->setStartEndStyle(polyann->getStartStyle(), (AnnotLineEndingStyle)style); } } bool LineAnnotation::isLineClosed() const { Q_D(const LineAnnotation); if (!d->pdfAnnot) { return d->lineClosed; } return d->pdfAnnot->getType() == Annot::typePolygon; } void LineAnnotation::setLineClosed(bool closed) { Q_D(LineAnnotation); if (!d->pdfAnnot) { d->lineClosed = closed; return; } if (d->pdfAnnot->getType() != Annot::typeLine) { AnnotPolygon *polyann = static_cast(d->pdfAnnot); // Set new subtype and switch intent if necessary if (closed) { polyann->setType(Annot::typePolygon); if (polyann->getIntent() == AnnotPolygon::polylineDimension) { polyann->setIntent(AnnotPolygon::polygonDimension); } } else { polyann->setType(Annot::typePolyLine); if (polyann->getIntent() == AnnotPolygon::polygonDimension) { polyann->setIntent(AnnotPolygon::polylineDimension); } } } } QColor LineAnnotation::lineInnerColor() const { Q_D(const LineAnnotation); if (!d->pdfAnnot) { return d->lineInnerColor; } AnnotColor *c; if (d->pdfAnnot->getType() == Annot::typeLine) { const AnnotLine *lineann = static_cast(d->pdfAnnot); c = lineann->getInteriorColor(); } else { const AnnotPolygon *polyann = static_cast(d->pdfAnnot); c = polyann->getInteriorColor(); } return convertAnnotColor(c); } void LineAnnotation::setLineInnerColor(const QColor &color) { Q_D(LineAnnotation); if (!d->pdfAnnot) { d->lineInnerColor = color; return; } auto c = convertQColor(color); if (d->pdfAnnot->getType() == Annot::typeLine) { AnnotLine *lineann = static_cast(d->pdfAnnot); lineann->setInteriorColor(std::move(c)); } else { AnnotPolygon *polyann = static_cast(d->pdfAnnot); polyann->setInteriorColor(std::move(c)); } } double LineAnnotation::lineLeadingForwardPoint() const { Q_D(const LineAnnotation); if (!d->pdfAnnot) { return d->lineLeadingFwdPt; } if (d->pdfAnnot->getType() == Annot::typeLine) { const AnnotLine *lineann = static_cast(d->pdfAnnot); return lineann->getLeaderLineLength(); } return 0; } void LineAnnotation::setLineLeadingForwardPoint(double point) { Q_D(LineAnnotation); if (!d->pdfAnnot) { d->lineLeadingFwdPt = point; return; } if (d->pdfAnnot->getType() == Annot::typeLine) { AnnotLine *lineann = static_cast(d->pdfAnnot); lineann->setLeaderLineLength(point); } } double LineAnnotation::lineLeadingBackPoint() const { Q_D(const LineAnnotation); if (!d->pdfAnnot) { return d->lineLeadingBackPt; } if (d->pdfAnnot->getType() == Annot::typeLine) { const AnnotLine *lineann = static_cast(d->pdfAnnot); return lineann->getLeaderLineExtension(); } return 0; } void LineAnnotation::setLineLeadingBackPoint(double point) { Q_D(LineAnnotation); if (!d->pdfAnnot) { d->lineLeadingBackPt = point; return; } if (d->pdfAnnot->getType() == Annot::typeLine) { AnnotLine *lineann = static_cast(d->pdfAnnot); lineann->setLeaderLineExtension(point); } } bool LineAnnotation::lineShowCaption() const { Q_D(const LineAnnotation); if (!d->pdfAnnot) { return d->lineShowCaption; } if (d->pdfAnnot->getType() == Annot::typeLine) { const AnnotLine *lineann = static_cast(d->pdfAnnot); return lineann->getCaption(); } return false; } void LineAnnotation::setLineShowCaption(bool show) { Q_D(LineAnnotation); if (!d->pdfAnnot) { d->lineShowCaption = show; return; } if (d->pdfAnnot->getType() == Annot::typeLine) { AnnotLine *lineann = static_cast(d->pdfAnnot); lineann->setCaption(show); } } LineAnnotation::LineIntent LineAnnotation::lineIntent() const { Q_D(const LineAnnotation); if (!d->pdfAnnot) { return d->lineIntent; } if (d->pdfAnnot->getType() == Annot::typeLine) { const AnnotLine *lineann = static_cast(d->pdfAnnot); return (LineAnnotation::LineIntent)(lineann->getIntent() + 1); } else { const AnnotPolygon *polyann = static_cast(d->pdfAnnot); if (polyann->getIntent() == AnnotPolygon::polygonCloud) { return LineAnnotation::PolygonCloud; } else { // AnnotPolygon::polylineDimension, AnnotPolygon::polygonDimension return LineAnnotation::Dimension; } } } void LineAnnotation::setLineIntent(LineAnnotation::LineIntent intent) { Q_D(LineAnnotation); if (!d->pdfAnnot) { d->lineIntent = intent; return; } if (intent == LineAnnotation::Unknown) { return; // Do not set (actually, it should clear the property) } if (d->pdfAnnot->getType() == Annot::typeLine) { AnnotLine *lineann = static_cast(d->pdfAnnot); lineann->setIntent((AnnotLine::AnnotLineIntent)(intent - 1)); } else { AnnotPolygon *polyann = static_cast(d->pdfAnnot); if (intent == LineAnnotation::PolygonCloud) { polyann->setIntent(AnnotPolygon::polygonCloud); } else // LineAnnotation::Dimension { if (d->pdfAnnot->getType() == Annot::typePolygon) { polyann->setIntent(AnnotPolygon::polygonDimension); } else { // Annot::typePolyLine polyann->setIntent(AnnotPolygon::polylineDimension); } } } } /** GeomAnnotation [Annotation] */ class GeomAnnotationPrivate : public AnnotationPrivate { public: GeomAnnotationPrivate(); Annotation *makeAlias() override; Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; // data fields (note uses border for rendering style) GeomAnnotation::GeomType geomType; QColor geomInnerColor; }; GeomAnnotationPrivate::GeomAnnotationPrivate() : AnnotationPrivate(), geomType(GeomAnnotation::InscribedSquare) { } Annotation *GeomAnnotationPrivate::makeAlias() { return new GeomAnnotation(*this); } Annot *GeomAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) { // Setters are defined in the public class GeomAnnotation *q = static_cast(makeAlias()); // Set page and document pdfPage = destPage; parentDoc = doc; Annot::AnnotSubtype type; if (geomType == GeomAnnotation::InscribedSquare) { type = Annot::typeSquare; } else { // GeomAnnotation::InscribedCircle type = Annot::typeCircle; } // Set pdfAnnot PDFRectangle rect = boundaryToPdfRectangle(boundary, flags); pdfAnnot = new AnnotGeometry(destPage->getDoc(), &rect, type); // Set properties flushBaseAnnotationProperties(); q->setGeomInnerColor(geomInnerColor); delete q; return pdfAnnot; } GeomAnnotation::GeomAnnotation() : Annotation(*new GeomAnnotationPrivate()) { } GeomAnnotation::GeomAnnotation(GeomAnnotationPrivate &dd) : Annotation(dd) { } GeomAnnotation::GeomAnnotation(const QDomNode &node) : Annotation(*new GeomAnnotationPrivate(), node) { // loop through the whole children looking for a 'geom' element QDomNode subNode = node.firstChild(); while (subNode.isElement()) { QDomElement e = subNode.toElement(); subNode = subNode.nextSibling(); if (e.tagName() != QLatin1String("geom")) { continue; } // parse the attributes if (e.hasAttribute(QStringLiteral("type"))) { setGeomType((GeomAnnotation::GeomType)e.attribute(QStringLiteral("type")).toInt()); } if (e.hasAttribute(QStringLiteral("color"))) { setGeomInnerColor(QColor(e.attribute(QStringLiteral("color")))); } // loading complete break; } } GeomAnnotation::~GeomAnnotation() { } void GeomAnnotation::store(QDomNode &node, QDomDocument &document) const { // store base annotation properties storeBaseAnnotationProperties(node, document); // create [geom] element QDomElement geomElement = document.createElement(QStringLiteral("geom")); node.appendChild(geomElement); // append the optional attributes if (geomType() != InscribedSquare) { geomElement.setAttribute(QStringLiteral("type"), (int)geomType()); } if (geomInnerColor().isValid()) { geomElement.setAttribute(QStringLiteral("color"), geomInnerColor().name()); } } Annotation::SubType GeomAnnotation::subType() const { return AGeom; } GeomAnnotation::GeomType GeomAnnotation::geomType() const { Q_D(const GeomAnnotation); if (!d->pdfAnnot) { return d->geomType; } if (d->pdfAnnot->getType() == Annot::typeSquare) { return GeomAnnotation::InscribedSquare; } else { // Annot::typeCircle return GeomAnnotation::InscribedCircle; } } void GeomAnnotation::setGeomType(GeomAnnotation::GeomType type) { Q_D(GeomAnnotation); if (!d->pdfAnnot) { d->geomType = type; return; } AnnotGeometry *geomann = static_cast(d->pdfAnnot); if (type == GeomAnnotation::InscribedSquare) { geomann->setType(Annot::typeSquare); } else { // GeomAnnotation::InscribedCircle geomann->setType(Annot::typeCircle); } } QColor GeomAnnotation::geomInnerColor() const { Q_D(const GeomAnnotation); if (!d->pdfAnnot) { return d->geomInnerColor; } const AnnotGeometry *geomann = static_cast(d->pdfAnnot); return convertAnnotColor(geomann->getInteriorColor()); } void GeomAnnotation::setGeomInnerColor(const QColor &color) { Q_D(GeomAnnotation); if (!d->pdfAnnot) { d->geomInnerColor = color; return; } AnnotGeometry *geomann = static_cast(d->pdfAnnot); geomann->setInteriorColor(convertQColor(color)); } /** HighlightAnnotation [Annotation] */ class HighlightAnnotationPrivate : public AnnotationPrivate { public: HighlightAnnotationPrivate(); Annotation *makeAlias() override; Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; // data fields HighlightAnnotation::HighlightType highlightType; QList highlightQuads; // not empty // helpers static Annot::AnnotSubtype toAnnotSubType(HighlightAnnotation::HighlightType type); QList fromQuadrilaterals(AnnotQuadrilaterals *quads) const; AnnotQuadrilaterals *toQuadrilaterals(const QList &quads) const; }; HighlightAnnotationPrivate::HighlightAnnotationPrivate() : AnnotationPrivate(), highlightType(HighlightAnnotation::Highlight) { } Annotation *HighlightAnnotationPrivate::makeAlias() { return new HighlightAnnotation(*this); } Annot::AnnotSubtype HighlightAnnotationPrivate::toAnnotSubType(HighlightAnnotation::HighlightType type) { switch (type) { default: // HighlightAnnotation::Highlight: return Annot::typeHighlight; case HighlightAnnotation::Underline: return Annot::typeUnderline; case HighlightAnnotation::Squiggly: return Annot::typeSquiggly; case HighlightAnnotation::StrikeOut: return Annot::typeStrikeOut; } } QList HighlightAnnotationPrivate::fromQuadrilaterals(AnnotQuadrilaterals *hlquads) const { QList quads; if (!hlquads || !hlquads->getQuadrilateralsLength()) { return quads; } const int quadsCount = hlquads->getQuadrilateralsLength(); double MTX[6]; fillTransformationMTX(MTX); quads.reserve(quadsCount); for (int q = 0; q < quadsCount; ++q) { HighlightAnnotation::Quad quad; XPDFReader::transform(MTX, hlquads->getX1(q), hlquads->getY1(q), quad.points[0]); XPDFReader::transform(MTX, hlquads->getX2(q), hlquads->getY2(q), quad.points[1]); XPDFReader::transform(MTX, hlquads->getX3(q), hlquads->getY3(q), quad.points[2]); XPDFReader::transform(MTX, hlquads->getX4(q), hlquads->getY4(q), quad.points[3]); // ### PDF1.6 specs says that point are in ccw order, but in fact // points 3 and 4 are swapped in every PDF around! QPointF tmpPoint = quad.points[2]; quad.points[2] = quad.points[3]; quad.points[3] = tmpPoint; // initialize other properties and append quad quad.capStart = true; // unlinked quads are always capped quad.capEnd = true; // unlinked quads are always capped quad.feather = 0.1; // default feather quads.append(quad); } return quads; } AnnotQuadrilaterals *HighlightAnnotationPrivate::toQuadrilaterals(const QList &quads) const { const int count = quads.size(); auto ac = std::make_unique(count); double MTX[6]; fillTransformationMTX(MTX); int pos = 0; foreach (const HighlightAnnotation::Quad &q, quads) { double x1, y1, x2, y2, x3, y3, x4, y4; XPDFReader::invTransform(MTX, q.points[0], x1, y1); XPDFReader::invTransform(MTX, q.points[1], x2, y2); // Swap points 3 and 4 (see HighlightAnnotationPrivate::fromQuadrilaterals) XPDFReader::invTransform(MTX, q.points[3], x3, y3); XPDFReader::invTransform(MTX, q.points[2], x4, y4); ac[pos++] = AnnotQuadrilaterals::AnnotQuadrilateral(x1, y1, x2, y2, x3, y3, x4, y4); } return new AnnotQuadrilaterals(std::move(ac), count); } Annot *HighlightAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) { // Setters are defined in the public class HighlightAnnotation *q = static_cast(makeAlias()); // Set page and document pdfPage = destPage; parentDoc = doc; // Set pdfAnnot PDFRectangle rect = boundaryToPdfRectangle(boundary, flags); pdfAnnot = new AnnotTextMarkup(destPage->getDoc(), &rect, toAnnotSubType(highlightType)); // Set properties flushBaseAnnotationProperties(); q->setHighlightQuads(highlightQuads); highlightQuads.clear(); // Free up memory delete q; return pdfAnnot; } HighlightAnnotation::HighlightAnnotation() : Annotation(*new HighlightAnnotationPrivate()) { } HighlightAnnotation::HighlightAnnotation(HighlightAnnotationPrivate &dd) : Annotation(dd) { } HighlightAnnotation::HighlightAnnotation(const QDomNode &node) : Annotation(*new HighlightAnnotationPrivate(), node) { // loop through the whole children looking for a 'hl' element QDomNode subNode = node.firstChild(); while (subNode.isElement()) { QDomElement e = subNode.toElement(); subNode = subNode.nextSibling(); if (e.tagName() != QLatin1String("hl")) { continue; } // parse the attributes if (e.hasAttribute(QStringLiteral("type"))) { setHighlightType((HighlightAnnotation::HighlightType)e.attribute(QStringLiteral("type")).toInt()); } // parse all 'quad' subnodes QList quads; QDomNode quadNode = e.firstChild(); for (; quadNode.isElement(); quadNode = quadNode.nextSibling()) { QDomElement qe = quadNode.toElement(); if (qe.tagName() != QLatin1String("quad")) { continue; } Quad q; q.points[0].setX(qe.attribute(QStringLiteral("ax"), QStringLiteral("0.0")).toDouble()); q.points[0].setY(qe.attribute(QStringLiteral("ay"), QStringLiteral("0.0")).toDouble()); q.points[1].setX(qe.attribute(QStringLiteral("bx"), QStringLiteral("0.0")).toDouble()); q.points[1].setY(qe.attribute(QStringLiteral("by"), QStringLiteral("0.0")).toDouble()); q.points[2].setX(qe.attribute(QStringLiteral("cx"), QStringLiteral("0.0")).toDouble()); q.points[2].setY(qe.attribute(QStringLiteral("cy"), QStringLiteral("0.0")).toDouble()); q.points[3].setX(qe.attribute(QStringLiteral("dx"), QStringLiteral("0.0")).toDouble()); q.points[3].setY(qe.attribute(QStringLiteral("dy"), QStringLiteral("0.0")).toDouble()); q.capStart = qe.hasAttribute(QStringLiteral("start")); q.capEnd = qe.hasAttribute(QStringLiteral("end")); q.feather = qe.attribute(QStringLiteral("feather"), QStringLiteral("0.1")).toDouble(); quads.append(q); } setHighlightQuads(quads); // loading complete break; } } HighlightAnnotation::~HighlightAnnotation() { } void HighlightAnnotation::store(QDomNode &node, QDomDocument &document) const { // store base annotation properties storeBaseAnnotationProperties(node, document); // create [hl] element QDomElement hlElement = document.createElement(QStringLiteral("hl")); node.appendChild(hlElement); // append the optional attributes if (highlightType() != Highlight) { hlElement.setAttribute(QStringLiteral("type"), (int)highlightType()); } const QList quads = highlightQuads(); if (quads.count() < 1) { return; } // append highlight quads, all children describe quads QList::const_iterator it = quads.begin(), end = quads.end(); for (; it != end; ++it) { QDomElement quadElement = document.createElement(QStringLiteral("quad")); hlElement.appendChild(quadElement); const Quad &q = *it; quadElement.setAttribute(QStringLiteral("ax"), QString::number(q.points[0].x())); quadElement.setAttribute(QStringLiteral("ay"), QString::number(q.points[0].y())); quadElement.setAttribute(QStringLiteral("bx"), QString::number(q.points[1].x())); quadElement.setAttribute(QStringLiteral("by"), QString::number(q.points[1].y())); quadElement.setAttribute(QStringLiteral("cx"), QString::number(q.points[2].x())); quadElement.setAttribute(QStringLiteral("cy"), QString::number(q.points[2].y())); quadElement.setAttribute(QStringLiteral("dx"), QString::number(q.points[3].x())); quadElement.setAttribute(QStringLiteral("dy"), QString::number(q.points[3].y())); if (q.capStart) { quadElement.setAttribute(QStringLiteral("start"), 1); } if (q.capEnd) { quadElement.setAttribute(QStringLiteral("end"), 1); } quadElement.setAttribute(QStringLiteral("feather"), QString::number(q.feather)); } } Annotation::SubType HighlightAnnotation::subType() const { return AHighlight; } HighlightAnnotation::HighlightType HighlightAnnotation::highlightType() const { Q_D(const HighlightAnnotation); if (!d->pdfAnnot) { return d->highlightType; } Annot::AnnotSubtype subType = d->pdfAnnot->getType(); if (subType == Annot::typeHighlight) { return HighlightAnnotation::Highlight; } else if (subType == Annot::typeUnderline) { return HighlightAnnotation::Underline; } else if (subType == Annot::typeSquiggly) { return HighlightAnnotation::Squiggly; } else { // Annot::typeStrikeOut return HighlightAnnotation::StrikeOut; } } void HighlightAnnotation::setHighlightType(HighlightAnnotation::HighlightType type) { Q_D(HighlightAnnotation); if (!d->pdfAnnot) { d->highlightType = type; return; } AnnotTextMarkup *hlann = static_cast(d->pdfAnnot); hlann->setType(HighlightAnnotationPrivate::toAnnotSubType(type)); } QList HighlightAnnotation::highlightQuads() const { Q_D(const HighlightAnnotation); if (!d->pdfAnnot) { return d->highlightQuads; } const AnnotTextMarkup *hlann = static_cast(d->pdfAnnot); return d->fromQuadrilaterals(hlann->getQuadrilaterals()); } void HighlightAnnotation::setHighlightQuads(const QList &quads) { Q_D(HighlightAnnotation); if (!d->pdfAnnot) { d->highlightQuads = quads; return; } AnnotTextMarkup *hlann = static_cast(d->pdfAnnot); AnnotQuadrilaterals *quadrilaterals = d->toQuadrilaterals(quads); hlann->setQuadrilaterals(quadrilaterals); delete quadrilaterals; } /** StampAnnotation [Annotation] */ class StampAnnotationPrivate : public AnnotationPrivate { public: StampAnnotationPrivate(); Annotation *makeAlias() override; Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; AnnotStampImageHelper *convertQImageToAnnotStampImageHelper(const QImage &qimg); // data fields QString stampIconName; QImage stampCustomImage; }; StampAnnotationPrivate::StampAnnotationPrivate() : AnnotationPrivate(), stampIconName(QStringLiteral("Draft")) { } Annotation *StampAnnotationPrivate::makeAlias() { return new StampAnnotation(*this); } Annot *StampAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) { StampAnnotation *q = static_cast(makeAlias()); // Set page and document pdfPage = destPage; parentDoc = doc; // Set pdfAnnot PDFRectangle rect = boundaryToPdfRectangle(boundary, flags); pdfAnnot = new AnnotStamp(destPage->getDoc(), &rect); // Set properties flushBaseAnnotationProperties(); q->setStampIconName(stampIconName); q->setStampCustomImage(stampCustomImage); delete q; stampIconName.clear(); // Free up memory return pdfAnnot; } AnnotStampImageHelper *StampAnnotationPrivate::convertQImageToAnnotStampImageHelper(const QImage &qimg) { QImage convertedQImage = qimg; QByteArray data; QByteArray sMaskData; const int width = convertedQImage.width(); const int height = convertedQImage.height(); int bitsPerComponent = 1; ColorSpace colorSpace = ColorSpace::DeviceGray; switch (convertedQImage.format()) { case QImage::Format_MonoLSB: if (!convertedQImage.allGray()) { convertedQImage = convertedQImage.convertToFormat(QImage::Format_RGB888); colorSpace = ColorSpace::DeviceRGB; bitsPerComponent = 8; } else { convertedQImage = convertedQImage.convertToFormat(QImage::Format_Mono); } break; case QImage::Format_Mono: if (!convertedQImage.allGray()) { convertedQImage = convertedQImage.convertToFormat(QImage::Format_RGB888); colorSpace = ColorSpace::DeviceRGB; bitsPerComponent = 8; } break; case QImage::Format_RGB32: case QImage::Format_ARGB32_Premultiplied: case QImage::Format_ARGB8565_Premultiplied: case QImage::Format_ARGB6666_Premultiplied: case QImage::Format_ARGB8555_Premultiplied: case QImage::Format_ARGB4444_Premultiplied: case QImage::Format_Alpha8: convertedQImage = convertedQImage.convertToFormat(QImage::Format_ARGB32); colorSpace = ColorSpace::DeviceRGB; bitsPerComponent = 8; break; case QImage::Format_RGBA8888: case QImage::Format_RGBA8888_Premultiplied: case QImage::Format_RGBX8888: case QImage::Format_ARGB32: colorSpace = ColorSpace::DeviceRGB; bitsPerComponent = 8; break; 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: case QImage::Format_RGB444: convertedQImage = convertedQImage.convertToFormat(QImage::Format_RGB888); colorSpace = ColorSpace::DeviceRGB; bitsPerComponent = 8; break; case QImage::Format_RGB888: colorSpace = ColorSpace::DeviceRGB; bitsPerComponent = 8; break; default: convertedQImage = convertedQImage.convertToFormat(QImage::Format_ARGB32); colorSpace = ColorSpace::DeviceRGB; bitsPerComponent = 8; break; } getRawDataFromQImage(convertedQImage, convertedQImage.depth(), &data, &sMaskData); 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()); } else { annotImg = new AnnotStampImageHelper(parentDoc->doc, width, height, colorSpace, bitsPerComponent, data.data(), data.count()); } return annotImg; } StampAnnotation::StampAnnotation() : Annotation(*new StampAnnotationPrivate()) { } StampAnnotation::StampAnnotation(StampAnnotationPrivate &dd) : Annotation(dd) { } StampAnnotation::StampAnnotation(const QDomNode &node) : Annotation(*new StampAnnotationPrivate(), node) { // loop through the whole children looking for a 'stamp' element QDomNode subNode = node.firstChild(); while (subNode.isElement()) { QDomElement e = subNode.toElement(); subNode = subNode.nextSibling(); if (e.tagName() != QLatin1String("stamp")) { continue; } // parse the attributes if (e.hasAttribute(QStringLiteral("icon"))) { setStampIconName(e.attribute(QStringLiteral("icon"))); } // loading complete break; } } StampAnnotation::~StampAnnotation() { } void StampAnnotation::store(QDomNode &node, QDomDocument &document) const { // store base annotation properties storeBaseAnnotationProperties(node, document); // create [stamp] element QDomElement stampElement = document.createElement(QStringLiteral("stamp")); node.appendChild(stampElement); // append the optional attributes if (stampIconName() != QLatin1String("Draft")) { stampElement.setAttribute(QStringLiteral("icon"), stampIconName()); } } Annotation::SubType StampAnnotation::subType() const { return AStamp; } QString StampAnnotation::stampIconName() const { Q_D(const StampAnnotation); if (!d->pdfAnnot) { return d->stampIconName; } const AnnotStamp *stampann = static_cast(d->pdfAnnot); return QString::fromLatin1(stampann->getIcon()->c_str()); } void StampAnnotation::setStampIconName(const QString &name) { Q_D(StampAnnotation); if (!d->pdfAnnot) { d->stampIconName = name; return; } AnnotStamp *stampann = static_cast(d->pdfAnnot); QByteArray encoded = name.toLatin1(); GooString s(encoded.constData()); stampann->setIcon(&s); } void StampAnnotation::setStampCustomImage(const QImage &image) { if (image.isNull()) { return; } Q_D(StampAnnotation); if (!d->pdfAnnot) { d->stampCustomImage = QImage(image); return; } AnnotStamp *stampann = static_cast(d->pdfAnnot); AnnotStampImageHelper *annotCustomImage = d->convertQImageToAnnotStampImageHelper(image); stampann->setCustomImage(annotCustomImage); } /** InkAnnotation [Annotation] */ class InkAnnotationPrivate : public AnnotationPrivate { public: InkAnnotationPrivate(); Annotation *makeAlias() override; Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; // data fields QList> inkPaths; // helper AnnotPath **toAnnotPaths(const QList> &paths); }; InkAnnotationPrivate::InkAnnotationPrivate() : AnnotationPrivate() { } Annotation *InkAnnotationPrivate::makeAlias() { return new InkAnnotation(*this); } // Note: Caller is required to delete array elements and the array itself after use AnnotPath **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]); } return res; } Annot *InkAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) { // Setters are defined in the public class InkAnnotation *q = static_cast(makeAlias()); // Set page and document pdfPage = destPage; parentDoc = doc; // Set pdfAnnot PDFRectangle rect = boundaryToPdfRectangle(boundary, flags); pdfAnnot = new AnnotInk(destPage->getDoc(), &rect); // Set properties flushBaseAnnotationProperties(); q->setInkPaths(inkPaths); inkPaths.clear(); // Free up memory delete q; return pdfAnnot; } InkAnnotation::InkAnnotation() : Annotation(*new InkAnnotationPrivate()) { } InkAnnotation::InkAnnotation(InkAnnotationPrivate &dd) : Annotation(dd) { } InkAnnotation::InkAnnotation(const QDomNode &node) : Annotation(*new InkAnnotationPrivate(), node) { // loop through the whole children looking for a 'ink' element QDomNode subNode = node.firstChild(); while (subNode.isElement()) { QDomElement e = subNode.toElement(); subNode = subNode.nextSibling(); if (e.tagName() != QLatin1String("ink")) { continue; } // parse the 'path' subnodes QList> paths; QDomNode pathNode = e.firstChild(); while (pathNode.isElement()) { QDomElement pathElement = pathNode.toElement(); pathNode = pathNode.nextSibling(); if (pathElement.tagName() != QLatin1String("path")) { continue; } // build each path parsing 'point' subnodes QLinkedList path; QDomNode pointNode = pathElement.firstChild(); while (pointNode.isElement()) { QDomElement pointElement = pointNode.toElement(); pointNode = pointNode.nextSibling(); if (pointElement.tagName() != QLatin1String("point")) { continue; } QPointF p(pointElement.attribute(QStringLiteral("x"), QStringLiteral("0.0")).toDouble(), pointElement.attribute(QStringLiteral("y"), QStringLiteral("0.0")).toDouble()); path.append(p); } // add the path to the path list if it contains at least 2 nodes if (path.count() >= 2) { paths.append(path); } } setInkPaths(paths); // loading complete break; } } InkAnnotation::~InkAnnotation() { } void InkAnnotation::store(QDomNode &node, QDomDocument &document) const { // store base annotation properties storeBaseAnnotationProperties(node, document); // create [ink] element QDomElement inkElement = document.createElement(QStringLiteral("ink")); node.appendChild(inkElement); // append the optional attributes const QList> paths = inkPaths(); if (paths.count() < 1) { return; } QList>::const_iterator pIt = paths.begin(), pEnd = paths.end(); for (; pIt != pEnd; ++pIt) { QDomElement pathElement = document.createElement(QStringLiteral("path")); inkElement.appendChild(pathElement); const QLinkedList &path = *pIt; QLinkedList::const_iterator iIt = path.begin(), iEnd = path.end(); for (; iIt != iEnd; ++iIt) { const QPointF &point = *iIt; QDomElement pointElement = document.createElement(QStringLiteral("point")); pathElement.appendChild(pointElement); pointElement.setAttribute(QStringLiteral("x"), QString::number(point.x())); pointElement.setAttribute(QStringLiteral("y"), QString::number(point.y())); } } } Annotation::SubType InkAnnotation::subType() const { return AInk; } QList> InkAnnotation::inkPaths() const { Q_D(const InkAnnotation); if (!d->pdfAnnot) { return d->inkPaths; } const AnnotInk *inkann = static_cast(d->pdfAnnot); const AnnotPath *const *paths = inkann->getInkList(); if (!paths || !inkann->getInkListLength()) { 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) { // 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; XPDFReader::transform(MTX, path->getX(n), path->getY(n), point); localList.append(point); } // ..and add it to the annotation inkPaths.append(localList); } return inkPaths; } void InkAnnotation::setInkPaths(const QList> &paths) { Q_D(InkAnnotation); if (!d->pdfAnnot) { d->inkPaths = paths; return; } 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; } /** LinkAnnotation [Annotation] */ class LinkAnnotationPrivate : public AnnotationPrivate { public: LinkAnnotationPrivate(); ~LinkAnnotationPrivate() override; Annotation *makeAlias() override; Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; // data fields Link *linkDestination; LinkAnnotation::HighlightMode linkHLMode; QPointF linkRegion[4]; }; LinkAnnotationPrivate::LinkAnnotationPrivate() : AnnotationPrivate(), linkDestination(nullptr), linkHLMode(LinkAnnotation::Invert) { } LinkAnnotationPrivate::~LinkAnnotationPrivate() { delete linkDestination; } Annotation *LinkAnnotationPrivate::makeAlias() { return new LinkAnnotation(*this); } Annot *LinkAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) { return nullptr; // Not implemented } LinkAnnotation::LinkAnnotation() : Annotation(*new LinkAnnotationPrivate()) { } LinkAnnotation::LinkAnnotation(LinkAnnotationPrivate &dd) : Annotation(dd) { } LinkAnnotation::LinkAnnotation(const QDomNode &node) : Annotation(*new LinkAnnotationPrivate(), node) { // loop through the whole children looking for a 'link' element QDomNode subNode = node.firstChild(); while (subNode.isElement()) { QDomElement e = subNode.toElement(); subNode = subNode.nextSibling(); if (e.tagName() != QLatin1String("link")) { continue; } // parse the attributes if (e.hasAttribute(QStringLiteral("hlmode"))) { setLinkHighlightMode((LinkAnnotation::HighlightMode)e.attribute(QStringLiteral("hlmode")).toInt()); } // parse all 'quad' subnodes QDomNode quadNode = e.firstChild(); for (; quadNode.isElement(); quadNode = quadNode.nextSibling()) { QDomElement qe = quadNode.toElement(); if (qe.tagName() == QLatin1String("quad")) { setLinkRegionPoint(0, QPointF(qe.attribute(QStringLiteral("ax"), QStringLiteral("0.0")).toDouble(), qe.attribute(QStringLiteral("ay"), QStringLiteral("0.0")).toDouble())); setLinkRegionPoint(1, QPointF(qe.attribute(QStringLiteral("bx"), QStringLiteral("0.0")).toDouble(), qe.attribute(QStringLiteral("by"), QStringLiteral("0.0")).toDouble())); setLinkRegionPoint(2, QPointF(qe.attribute(QStringLiteral("cx"), QStringLiteral("0.0")).toDouble(), qe.attribute(QStringLiteral("cy"), QStringLiteral("0.0")).toDouble())); setLinkRegionPoint(3, QPointF(qe.attribute(QStringLiteral("dx"), QStringLiteral("0.0")).toDouble(), qe.attribute(QStringLiteral("dy"), QStringLiteral("0.0")).toDouble())); } else if (qe.tagName() == QLatin1String("link")) { QString type = qe.attribute(QStringLiteral("type")); if (type == QLatin1String("GoTo")) { Poppler::LinkGoto *go = new Poppler::LinkGoto(QRect(), qe.attribute(QStringLiteral("filename")), LinkDestination(qe.attribute(QStringLiteral("destination")))); setLinkDestination(go); } else if (type == QLatin1String("Exec")) { Poppler::LinkExecute *exec = new Poppler::LinkExecute(QRect(), qe.attribute(QStringLiteral("filename")), qe.attribute(QStringLiteral("parameters"))); setLinkDestination(exec); } else if (type == QLatin1String("Browse")) { Poppler::LinkBrowse *browse = new Poppler::LinkBrowse(QRect(), qe.attribute(QStringLiteral("url"))); setLinkDestination(browse); } else if (type == QLatin1String("Action")) { Poppler::LinkAction::ActionType act; QString actString = qe.attribute(QStringLiteral("action")); bool found = true; if (actString == QLatin1String("PageFirst")) { act = Poppler::LinkAction::PageFirst; } else if (actString == QLatin1String("PagePrev")) { act = Poppler::LinkAction::PagePrev; } else if (actString == QLatin1String("PageNext")) { act = Poppler::LinkAction::PageNext; } else if (actString == QLatin1String("PageLast")) { act = Poppler::LinkAction::PageLast; } else if (actString == QLatin1String("HistoryBack")) { act = Poppler::LinkAction::HistoryBack; } else if (actString == QLatin1String("HistoryForward")) { act = Poppler::LinkAction::HistoryForward; } else if (actString == QLatin1String("Quit")) { act = Poppler::LinkAction::Quit; } else if (actString == QLatin1String("Presentation")) { act = Poppler::LinkAction::Presentation; } else if (actString == QLatin1String("EndPresentation")) { act = Poppler::LinkAction::EndPresentation; } else if (actString == QLatin1String("Find")) { act = Poppler::LinkAction::Find; } else if (actString == QLatin1String("GoToPage")) { act = Poppler::LinkAction::GoToPage; } else if (actString == QLatin1String("Close")) { act = Poppler::LinkAction::Close; } else if (actString == QLatin1String("Print")) { act = Poppler::LinkAction::Print; } else { found = false; } if (found) { Poppler::LinkAction *action = new Poppler::LinkAction(QRect(), act); setLinkDestination(action); } } else { qWarning("Loading annotations of type %s from DOM nodes is not yet implemented.", type.toLocal8Bit().constData()); } } } // loading complete break; } } LinkAnnotation::~LinkAnnotation() { } void LinkAnnotation::store(QDomNode &node, QDomDocument &document) const { // store base annotation properties storeBaseAnnotationProperties(node, document); // create [hl] element QDomElement linkElement = document.createElement(QStringLiteral("link")); node.appendChild(linkElement); // append the optional attributes if (linkHighlightMode() != Invert) { linkElement.setAttribute(QStringLiteral("hlmode"), (int)linkHighlightMode()); } // saving region QDomElement quadElement = document.createElement(QStringLiteral("quad")); linkElement.appendChild(quadElement); quadElement.setAttribute(QStringLiteral("ax"), QString::number(linkRegionPoint(0).x())); quadElement.setAttribute(QStringLiteral("ay"), QString::number(linkRegionPoint(0).y())); quadElement.setAttribute(QStringLiteral("bx"), QString::number(linkRegionPoint(1).x())); quadElement.setAttribute(QStringLiteral("by"), QString::number(linkRegionPoint(1).y())); quadElement.setAttribute(QStringLiteral("cx"), QString::number(linkRegionPoint(2).x())); quadElement.setAttribute(QStringLiteral("cy"), QString::number(linkRegionPoint(2).y())); quadElement.setAttribute(QStringLiteral("dx"), QString::number(linkRegionPoint(3).x())); quadElement.setAttribute(QStringLiteral("dy"), QString::number(linkRegionPoint(3).y())); // saving link QDomElement hyperlinkElement = document.createElement(QStringLiteral("link")); linkElement.appendChild(hyperlinkElement); if (linkDestination()) { switch (linkDestination()->linkType()) { case Poppler::Link::Goto: { Poppler::LinkGoto *go = static_cast(linkDestination()); hyperlinkElement.setAttribute(QStringLiteral("type"), QStringLiteral("GoTo")); hyperlinkElement.setAttribute(QStringLiteral("filename"), go->fileName()); hyperlinkElement.setAttribute(QStringLiteral("destionation"), go->destination().toString()); // TODO remove for poppler 0.28 hyperlinkElement.setAttribute(QStringLiteral("destination"), go->destination().toString()); break; } case Poppler::Link::Execute: { Poppler::LinkExecute *exec = static_cast(linkDestination()); hyperlinkElement.setAttribute(QStringLiteral("type"), QStringLiteral("Exec")); hyperlinkElement.setAttribute(QStringLiteral("filename"), exec->fileName()); hyperlinkElement.setAttribute(QStringLiteral("parameters"), exec->parameters()); break; } case Poppler::Link::Browse: { Poppler::LinkBrowse *browse = static_cast(linkDestination()); hyperlinkElement.setAttribute(QStringLiteral("type"), QStringLiteral("Browse")); hyperlinkElement.setAttribute(QStringLiteral("url"), browse->url()); break; } case Poppler::Link::Action: { Poppler::LinkAction *action = static_cast(linkDestination()); hyperlinkElement.setAttribute(QStringLiteral("type"), QStringLiteral("Action")); switch (action->actionType()) { case Poppler::LinkAction::PageFirst: hyperlinkElement.setAttribute(QStringLiteral("action"), QStringLiteral("PageFirst")); break; case Poppler::LinkAction::PagePrev: hyperlinkElement.setAttribute(QStringLiteral("action"), QStringLiteral("PagePrev")); break; case Poppler::LinkAction::PageNext: hyperlinkElement.setAttribute(QStringLiteral("action"), QStringLiteral("PageNext")); break; case Poppler::LinkAction::PageLast: hyperlinkElement.setAttribute(QStringLiteral("action"), QStringLiteral("PageLast")); break; case Poppler::LinkAction::HistoryBack: hyperlinkElement.setAttribute(QStringLiteral("action"), QStringLiteral("HistoryBack")); break; case Poppler::LinkAction::HistoryForward: hyperlinkElement.setAttribute(QStringLiteral("action"), QStringLiteral("HistoryForward")); break; case Poppler::LinkAction::Quit: hyperlinkElement.setAttribute(QStringLiteral("action"), QStringLiteral("Quit")); break; case Poppler::LinkAction::Presentation: hyperlinkElement.setAttribute(QStringLiteral("action"), QStringLiteral("Presentation")); break; case Poppler::LinkAction::EndPresentation: hyperlinkElement.setAttribute(QStringLiteral("action"), QStringLiteral("EndPresentation")); break; case Poppler::LinkAction::Find: hyperlinkElement.setAttribute(QStringLiteral("action"), QStringLiteral("Find")); break; case Poppler::LinkAction::GoToPage: hyperlinkElement.setAttribute(QStringLiteral("action"), QStringLiteral("GoToPage")); break; case Poppler::LinkAction::Close: hyperlinkElement.setAttribute(QStringLiteral("action"), QStringLiteral("Close")); break; case Poppler::LinkAction::Print: hyperlinkElement.setAttribute(QStringLiteral("action"), QStringLiteral("Print")); break; case Poppler::LinkAction::SaveAs: hyperlinkElement.setAttribute(QStringLiteral("action"), QStringLiteral("SaveAs")); break; } break; } case Poppler::Link::Movie: { hyperlinkElement.setAttribute(QStringLiteral("type"), QStringLiteral("Movie")); break; } case Poppler::Link::Rendition: { hyperlinkElement.setAttribute(QStringLiteral("type"), QStringLiteral("Rendition")); break; } case Poppler::Link::Sound: { hyperlinkElement.setAttribute(QStringLiteral("type"), QStringLiteral("Sound")); break; } case Poppler::Link::JavaScript: { hyperlinkElement.setAttribute(QStringLiteral("type"), QStringLiteral("JavaScript")); break; } case Poppler::Link::OCGState: { hyperlinkElement.setAttribute(QStringLiteral("type"), QStringLiteral("OCGState")); break; } case Poppler::Link::Hide: { hyperlinkElement.setAttribute(QStringLiteral("type"), QStringLiteral("Hide")); break; } case Poppler::Link::None: break; } } } Annotation::SubType LinkAnnotation::subType() const { return ALink; } Link *LinkAnnotation::linkDestination() const { Q_D(const LinkAnnotation); return d->linkDestination; } void LinkAnnotation::setLinkDestination(Link *link) { Q_D(LinkAnnotation); delete d->linkDestination; d->linkDestination = link; } LinkAnnotation::HighlightMode LinkAnnotation::linkHighlightMode() const { Q_D(const LinkAnnotation); return d->linkHLMode; } void LinkAnnotation::setLinkHighlightMode(LinkAnnotation::HighlightMode mode) { Q_D(LinkAnnotation); d->linkHLMode = mode; } QPointF LinkAnnotation::linkRegionPoint(int id) const { if (id < 0 || id >= 4) { return QPointF(); } Q_D(const LinkAnnotation); return d->linkRegion[id]; } void LinkAnnotation::setLinkRegionPoint(int id, const QPointF &point) // clazy:exclude=function-args-by-value { if (id < 0 || id >= 4) { return; } Q_D(LinkAnnotation); d->linkRegion[id] = point; } /** CaretAnnotation [Annotation] */ class CaretAnnotationPrivate : public AnnotationPrivate { public: CaretAnnotationPrivate(); Annotation *makeAlias() override; Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; // data fields CaretAnnotation::CaretSymbol symbol; }; static QString caretSymbolToString(CaretAnnotation::CaretSymbol symbol) { switch (symbol) { case CaretAnnotation::None: return QStringLiteral("None"); case CaretAnnotation::P: return QStringLiteral("P"); } return QString(); } static CaretAnnotation::CaretSymbol caretSymbolFromString(const QString &symbol) { if (symbol == QLatin1String("None")) { return CaretAnnotation::None; } else if (symbol == QLatin1String("P")) { return CaretAnnotation::P; } return CaretAnnotation::None; } CaretAnnotationPrivate::CaretAnnotationPrivate() : AnnotationPrivate(), symbol(CaretAnnotation::None) { } Annotation *CaretAnnotationPrivate::makeAlias() { return new CaretAnnotation(*this); } Annot *CaretAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) { // Setters are defined in the public class CaretAnnotation *q = static_cast(makeAlias()); // Set page and document pdfPage = destPage; parentDoc = doc; // Set pdfAnnot PDFRectangle rect = boundaryToPdfRectangle(boundary, flags); pdfAnnot = new AnnotCaret(destPage->getDoc(), &rect); // Set properties flushBaseAnnotationProperties(); q->setCaretSymbol(symbol); delete q; return pdfAnnot; } CaretAnnotation::CaretAnnotation() : Annotation(*new CaretAnnotationPrivate()) { } CaretAnnotation::CaretAnnotation(CaretAnnotationPrivate &dd) : Annotation(dd) { } CaretAnnotation::CaretAnnotation(const QDomNode &node) : Annotation(*new CaretAnnotationPrivate(), node) { // loop through the whole children looking for a 'caret' element QDomNode subNode = node.firstChild(); while (subNode.isElement()) { QDomElement e = subNode.toElement(); subNode = subNode.nextSibling(); if (e.tagName() != QLatin1String("caret")) { continue; } // parse the attributes if (e.hasAttribute(QStringLiteral("symbol"))) { setCaretSymbol(caretSymbolFromString(e.attribute(QStringLiteral("symbol")))); } // loading complete break; } } CaretAnnotation::~CaretAnnotation() { } void CaretAnnotation::store(QDomNode &node, QDomDocument &document) const { // store base annotation properties storeBaseAnnotationProperties(node, document); // create [caret] element QDomElement caretElement = document.createElement(QStringLiteral("caret")); node.appendChild(caretElement); // append the optional attributes if (caretSymbol() != CaretAnnotation::None) { caretElement.setAttribute(QStringLiteral("symbol"), caretSymbolToString(caretSymbol())); } } Annotation::SubType CaretAnnotation::subType() const { return ACaret; } CaretAnnotation::CaretSymbol CaretAnnotation::caretSymbol() const { Q_D(const CaretAnnotation); if (!d->pdfAnnot) { return d->symbol; } const AnnotCaret *caretann = static_cast(d->pdfAnnot); return (CaretAnnotation::CaretSymbol)caretann->getSymbol(); } void CaretAnnotation::setCaretSymbol(CaretAnnotation::CaretSymbol symbol) { Q_D(CaretAnnotation); if (!d->pdfAnnot) { d->symbol = symbol; return; } AnnotCaret *caretann = static_cast(d->pdfAnnot); caretann->setSymbol((AnnotCaret::AnnotCaretSymbol)symbol); } /** FileAttachmentAnnotation [Annotation] */ class FileAttachmentAnnotationPrivate : public AnnotationPrivate { public: FileAttachmentAnnotationPrivate(); ~FileAttachmentAnnotationPrivate() override; Annotation *makeAlias() override; Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; // data fields QString icon; EmbeddedFile *embfile; }; FileAttachmentAnnotationPrivate::FileAttachmentAnnotationPrivate() : AnnotationPrivate(), icon(QStringLiteral("PushPin")), embfile(nullptr) { } FileAttachmentAnnotationPrivate::~FileAttachmentAnnotationPrivate() { delete embfile; } Annotation *FileAttachmentAnnotationPrivate::makeAlias() { return new FileAttachmentAnnotation(*this); } Annot *FileAttachmentAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) { return nullptr; // Not implemented } FileAttachmentAnnotation::FileAttachmentAnnotation() : Annotation(*new FileAttachmentAnnotationPrivate()) { } FileAttachmentAnnotation::FileAttachmentAnnotation(FileAttachmentAnnotationPrivate &dd) : Annotation(dd) { } FileAttachmentAnnotation::FileAttachmentAnnotation(const QDomNode &node) : Annotation(*new FileAttachmentAnnotationPrivate(), node) { // loop through the whole children looking for a 'fileattachment' element QDomNode subNode = node.firstChild(); while (subNode.isElement()) { QDomElement e = subNode.toElement(); subNode = subNode.nextSibling(); if (e.tagName() != QLatin1String("fileattachment")) { continue; } // loading complete break; } } FileAttachmentAnnotation::~FileAttachmentAnnotation() { } void FileAttachmentAnnotation::store(QDomNode &node, QDomDocument &document) const { // store base annotation properties storeBaseAnnotationProperties(node, document); // create [fileattachment] element QDomElement fileAttachmentElement = document.createElement(QStringLiteral("fileattachment")); node.appendChild(fileAttachmentElement); } Annotation::SubType FileAttachmentAnnotation::subType() const { return AFileAttachment; } QString FileAttachmentAnnotation::fileIconName() const { Q_D(const FileAttachmentAnnotation); return d->icon; } void FileAttachmentAnnotation::setFileIconName(const QString &icon) { Q_D(FileAttachmentAnnotation); d->icon = icon; } EmbeddedFile *FileAttachmentAnnotation::embeddedFile() const { Q_D(const FileAttachmentAnnotation); return d->embfile; } void FileAttachmentAnnotation::setEmbeddedFile(EmbeddedFile *ef) { Q_D(FileAttachmentAnnotation); d->embfile = ef; } /** SoundAnnotation [Annotation] */ class SoundAnnotationPrivate : public AnnotationPrivate { public: SoundAnnotationPrivate(); ~SoundAnnotationPrivate() override; Annotation *makeAlias() override; Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; // data fields QString icon; SoundObject *sound; }; SoundAnnotationPrivate::SoundAnnotationPrivate() : AnnotationPrivate(), icon(QStringLiteral("Speaker")), sound(nullptr) { } SoundAnnotationPrivate::~SoundAnnotationPrivate() { delete sound; } Annotation *SoundAnnotationPrivate::makeAlias() { return new SoundAnnotation(*this); } Annot *SoundAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) { return nullptr; // Not implemented } SoundAnnotation::SoundAnnotation() : Annotation(*new SoundAnnotationPrivate()) { } SoundAnnotation::SoundAnnotation(SoundAnnotationPrivate &dd) : Annotation(dd) { } SoundAnnotation::SoundAnnotation(const QDomNode &node) : Annotation(*new SoundAnnotationPrivate(), node) { // loop through the whole children looking for a 'sound' element QDomNode subNode = node.firstChild(); while (subNode.isElement()) { QDomElement e = subNode.toElement(); subNode = subNode.nextSibling(); if (e.tagName() != QLatin1String("sound")) { continue; } // loading complete break; } } SoundAnnotation::~SoundAnnotation() { } void SoundAnnotation::store(QDomNode &node, QDomDocument &document) const { // store base annotation properties storeBaseAnnotationProperties(node, document); // create [sound] element QDomElement soundElement = document.createElement(QStringLiteral("sound")); node.appendChild(soundElement); } Annotation::SubType SoundAnnotation::subType() const { return ASound; } QString SoundAnnotation::soundIconName() const { Q_D(const SoundAnnotation); return d->icon; } void SoundAnnotation::setSoundIconName(const QString &icon) { Q_D(SoundAnnotation); d->icon = icon; } SoundObject *SoundAnnotation::sound() const { Q_D(const SoundAnnotation); return d->sound; } void SoundAnnotation::setSound(SoundObject *s) { Q_D(SoundAnnotation); d->sound = s; } /** MovieAnnotation [Annotation] */ class MovieAnnotationPrivate : public AnnotationPrivate { public: MovieAnnotationPrivate(); ~MovieAnnotationPrivate() override; Annotation *makeAlias() override; Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; // data fields MovieObject *movie; QString title; }; MovieAnnotationPrivate::MovieAnnotationPrivate() : AnnotationPrivate(), movie(nullptr) { } MovieAnnotationPrivate::~MovieAnnotationPrivate() { delete movie; } Annotation *MovieAnnotationPrivate::makeAlias() { return new MovieAnnotation(*this); } Annot *MovieAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) { return nullptr; // Not implemented } MovieAnnotation::MovieAnnotation() : Annotation(*new MovieAnnotationPrivate()) { } MovieAnnotation::MovieAnnotation(MovieAnnotationPrivate &dd) : Annotation(dd) { } MovieAnnotation::MovieAnnotation(const QDomNode &node) : Annotation(*new MovieAnnotationPrivate(), node) { // loop through the whole children looking for a 'movie' element QDomNode subNode = node.firstChild(); while (subNode.isElement()) { QDomElement e = subNode.toElement(); subNode = subNode.nextSibling(); if (e.tagName() != QLatin1String("movie")) { continue; } // loading complete break; } } MovieAnnotation::~MovieAnnotation() { } void MovieAnnotation::store(QDomNode &node, QDomDocument &document) const { // store base annotation properties storeBaseAnnotationProperties(node, document); // create [movie] element QDomElement movieElement = document.createElement(QStringLiteral("movie")); node.appendChild(movieElement); } Annotation::SubType MovieAnnotation::subType() const { return AMovie; } MovieObject *MovieAnnotation::movie() const { Q_D(const MovieAnnotation); return d->movie; } void MovieAnnotation::setMovie(MovieObject *movie) { Q_D(MovieAnnotation); d->movie = movie; } QString MovieAnnotation::movieTitle() const { Q_D(const MovieAnnotation); return d->title; } void MovieAnnotation::setMovieTitle(const QString &title) { Q_D(MovieAnnotation); d->title = title; } /** ScreenAnnotation [Annotation] */ class ScreenAnnotationPrivate : public AnnotationPrivate { public: ScreenAnnotationPrivate(); ~ScreenAnnotationPrivate() override; Annotation *makeAlias() override; Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; // data fields LinkRendition *action; QString title; }; ScreenAnnotationPrivate::ScreenAnnotationPrivate() : AnnotationPrivate(), action(nullptr) { } ScreenAnnotationPrivate::~ScreenAnnotationPrivate() { delete action; } ScreenAnnotation::ScreenAnnotation(ScreenAnnotationPrivate &dd) : Annotation(dd) { } Annotation *ScreenAnnotationPrivate::makeAlias() { return new ScreenAnnotation(*this); } Annot *ScreenAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) { return nullptr; // Not implemented } ScreenAnnotation::ScreenAnnotation() : Annotation(*new ScreenAnnotationPrivate()) { } ScreenAnnotation::~ScreenAnnotation() { } void ScreenAnnotation::store(QDomNode &node, QDomDocument &document) const { // store base annotation properties storeBaseAnnotationProperties(node, document); // create [screen] element QDomElement screenElement = document.createElement(QStringLiteral("screen")); node.appendChild(screenElement); } Annotation::SubType ScreenAnnotation::subType() const { return AScreen; } LinkRendition *ScreenAnnotation::action() const { Q_D(const ScreenAnnotation); return d->action; } void ScreenAnnotation::setAction(LinkRendition *action) { Q_D(ScreenAnnotation); d->action = action; } QString ScreenAnnotation::screenTitle() const { Q_D(const ScreenAnnotation); return d->title; } void ScreenAnnotation::setScreenTitle(const QString &title) { Q_D(ScreenAnnotation); d->title = title; } Link *ScreenAnnotation::additionalAction(AdditionalActionType type) const { Q_D(const ScreenAnnotation); return d->additionalAction(type); } /** WidgetAnnotation [Annotation] */ class WidgetAnnotationPrivate : public AnnotationPrivate { public: Annotation *makeAlias() override; Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; }; Annotation *WidgetAnnotationPrivate::makeAlias() { return new WidgetAnnotation(*this); } Annot *WidgetAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) { return nullptr; // Not implemented } WidgetAnnotation::WidgetAnnotation(WidgetAnnotationPrivate &dd) : Annotation(dd) { } WidgetAnnotation::WidgetAnnotation() : Annotation(*new WidgetAnnotationPrivate()) { } WidgetAnnotation::~WidgetAnnotation() { } void WidgetAnnotation::store(QDomNode &node, QDomDocument &document) const { // store base annotation properties storeBaseAnnotationProperties(node, document); // create [widget] element QDomElement widgetElement = document.createElement(QStringLiteral("widget")); node.appendChild(widgetElement); } Annotation::SubType WidgetAnnotation::subType() const { return AWidget; } Link *WidgetAnnotation::additionalAction(AdditionalActionType type) const { Q_D(const WidgetAnnotation); return d->additionalAction(type); } /** RichMediaAnnotation [Annotation] */ class RichMediaAnnotation::Params::Private { public: Private() { } QString flashVars; }; RichMediaAnnotation::Params::Params() : d(new Private) { } RichMediaAnnotation::Params::~Params() { } void RichMediaAnnotation::Params::setFlashVars(const QString &flashVars) { d->flashVars = flashVars; } QString RichMediaAnnotation::Params::flashVars() const { return d->flashVars; } class RichMediaAnnotation::Instance::Private { public: Private() : params(nullptr) { } ~Private() { delete params; } Private(const Private &) = delete; Private &operator=(const Private &) = delete; RichMediaAnnotation::Instance::Type type; RichMediaAnnotation::Params *params; }; RichMediaAnnotation::Instance::Instance() : d(new Private) { } RichMediaAnnotation::Instance::~Instance() { } void RichMediaAnnotation::Instance::setType(Type type) { d->type = type; } RichMediaAnnotation::Instance::Type RichMediaAnnotation::Instance::type() const { return d->type; } void RichMediaAnnotation::Instance::setParams(RichMediaAnnotation::Params *params) { delete d->params; d->params = params; } RichMediaAnnotation::Params *RichMediaAnnotation::Instance::params() const { return d->params; } class RichMediaAnnotation::Configuration::Private { public: Private() { } ~Private() { qDeleteAll(instances); instances.clear(); } Private(const Private &) = delete; Private &operator=(const Private &) = delete; RichMediaAnnotation::Configuration::Type type; QString name; QList instances; }; RichMediaAnnotation::Configuration::Configuration() : d(new Private) { } RichMediaAnnotation::Configuration::~Configuration() { } void RichMediaAnnotation::Configuration::setType(Type type) { d->type = type; } RichMediaAnnotation::Configuration::Type RichMediaAnnotation::Configuration::type() const { return d->type; } void RichMediaAnnotation::Configuration::setName(const QString &name) { d->name = name; } QString RichMediaAnnotation::Configuration::name() const { return d->name; } void RichMediaAnnotation::Configuration::setInstances(const QList &instances) { qDeleteAll(d->instances); d->instances.clear(); d->instances = instances; } QList RichMediaAnnotation::Configuration::instances() const { return d->instances; } class RichMediaAnnotation::Asset::Private { public: Private() : embeddedFile(nullptr) { } ~Private() { delete embeddedFile; } Private(const Private &) = delete; Private &operator=(const Private &) = delete; QString name; EmbeddedFile *embeddedFile; }; RichMediaAnnotation::Asset::Asset() : d(new Private) { } RichMediaAnnotation::Asset::~Asset() { } void RichMediaAnnotation::Asset::setName(const QString &name) { d->name = name; } QString RichMediaAnnotation::Asset::name() const { return d->name; } void RichMediaAnnotation::Asset::setEmbeddedFile(EmbeddedFile *embeddedFile) { delete d->embeddedFile; d->embeddedFile = embeddedFile; } EmbeddedFile *RichMediaAnnotation::Asset::embeddedFile() const { return d->embeddedFile; } class RichMediaAnnotation::Content::Private { public: Private() { } ~Private() { qDeleteAll(configurations); configurations.clear(); qDeleteAll(assets); assets.clear(); } Private(const Private &) = delete; Private &operator=(const Private &) = delete; QList configurations; QList assets; }; RichMediaAnnotation::Content::Content() : d(new Private) { } RichMediaAnnotation::Content::~Content() { } void RichMediaAnnotation::Content::setConfigurations(const QList &configurations) { qDeleteAll(d->configurations); d->configurations.clear(); d->configurations = configurations; } QList RichMediaAnnotation::Content::configurations() const { return d->configurations; } void RichMediaAnnotation::Content::setAssets(const QList &assets) { qDeleteAll(d->assets); d->assets.clear(); d->assets = assets; } QList RichMediaAnnotation::Content::assets() const { return d->assets; } class RichMediaAnnotation::Activation::Private { public: Private() : condition(RichMediaAnnotation::Activation::UserAction) { } RichMediaAnnotation::Activation::Condition condition; }; RichMediaAnnotation::Activation::Activation() : d(new Private) { } RichMediaAnnotation::Activation::~Activation() { } void RichMediaAnnotation::Activation::setCondition(Condition condition) { d->condition = condition; } RichMediaAnnotation::Activation::Condition RichMediaAnnotation::Activation::condition() const { return d->condition; } class RichMediaAnnotation::Deactivation::Private : public QSharedData { public: Private() : condition(RichMediaAnnotation::Deactivation::UserAction) { } RichMediaAnnotation::Deactivation::Condition condition; }; RichMediaAnnotation::Deactivation::Deactivation() : d(new Private) { } RichMediaAnnotation::Deactivation::~Deactivation() { } void RichMediaAnnotation::Deactivation::setCondition(Condition condition) { d->condition = condition; } RichMediaAnnotation::Deactivation::Condition RichMediaAnnotation::Deactivation::condition() const { return d->condition; } class RichMediaAnnotation::Settings::Private : public QSharedData { public: Private() : activation(nullptr), deactivation(nullptr) { } RichMediaAnnotation::Activation *activation; RichMediaAnnotation::Deactivation *deactivation; }; RichMediaAnnotation::Settings::Settings() : d(new Private) { } RichMediaAnnotation::Settings::~Settings() { } void RichMediaAnnotation::Settings::setActivation(RichMediaAnnotation::Activation *activation) { delete d->activation; d->activation = activation; } RichMediaAnnotation::Activation *RichMediaAnnotation::Settings::activation() const { return d->activation; } void RichMediaAnnotation::Settings::setDeactivation(RichMediaAnnotation::Deactivation *deactivation) { delete d->deactivation; d->deactivation = deactivation; } RichMediaAnnotation::Deactivation *RichMediaAnnotation::Settings::deactivation() const { return d->deactivation; } class RichMediaAnnotationPrivate : public AnnotationPrivate { public: RichMediaAnnotationPrivate() : settings(nullptr), content(nullptr) { } ~RichMediaAnnotationPrivate() override; Annotation *makeAlias() override { return new RichMediaAnnotation(*this); } Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override { Q_UNUSED(destPage); Q_UNUSED(doc); return nullptr; } RichMediaAnnotation::Settings *settings; RichMediaAnnotation::Content *content; }; RichMediaAnnotationPrivate::~RichMediaAnnotationPrivate() { delete settings; delete content; } RichMediaAnnotation::RichMediaAnnotation() : Annotation(*new RichMediaAnnotationPrivate()) { } RichMediaAnnotation::RichMediaAnnotation(RichMediaAnnotationPrivate &dd) : Annotation(dd) { } RichMediaAnnotation::RichMediaAnnotation(const QDomNode &node) : Annotation(*new RichMediaAnnotationPrivate(), node) { // loop through the whole children looking for a 'richMedia' element QDomNode subNode = node.firstChild(); while (subNode.isElement()) { QDomElement e = subNode.toElement(); subNode = subNode.nextSibling(); if (e.tagName() != QLatin1String("richMedia")) { continue; } // loading complete break; } } RichMediaAnnotation::~RichMediaAnnotation() { } void RichMediaAnnotation::store(QDomNode &node, QDomDocument &document) const { // store base annotation properties storeBaseAnnotationProperties(node, document); // create [richMedia] element QDomElement richMediaElement = document.createElement(QStringLiteral("richMedia")); node.appendChild(richMediaElement); } Annotation::SubType RichMediaAnnotation::subType() const { return ARichMedia; } void RichMediaAnnotation::setSettings(RichMediaAnnotation::Settings *settings) { Q_D(RichMediaAnnotation); delete d->settings; d->settings = settings; } RichMediaAnnotation::Settings *RichMediaAnnotation::settings() const { Q_D(const RichMediaAnnotation); return d->settings; } void RichMediaAnnotation::setContent(RichMediaAnnotation::Content *content) { Q_D(RichMediaAnnotation); delete d->content; d->content = content; } RichMediaAnnotation::Content *RichMediaAnnotation::content() const { Q_D(const RichMediaAnnotation); return d->content; } // BEGIN utility annotation functions QColor convertAnnotColor(const AnnotColor *color) { if (!color) { return QColor(); } QColor newcolor; const double *color_data = color->getValues(); switch (color->getSpace()) { case AnnotColor::colorTransparent: // = 0, newcolor = Qt::transparent; break; case AnnotColor::colorGray: // = 1, newcolor.setRgbF(color_data[0], color_data[0], color_data[0]); break; case AnnotColor::colorRGB: // = 3, newcolor.setRgbF(color_data[0], color_data[1], color_data[2]); break; case AnnotColor::colorCMYK: // = 4 newcolor.setCmykF(color_data[0], color_data[1], color_data[2], color_data[3]); break; } return newcolor; } std::unique_ptr convertQColor(const QColor &c) { if (c.alpha() == 0) { return {}; // Transparent } switch (c.spec()) { case QColor::Rgb: case QColor::Hsl: case QColor::Hsv: return std::make_unique(c.redF(), c.greenF(), c.blueF()); case QColor::Cmyk: return std::make_unique(c.cyanF(), c.magentaF(), c.yellowF(), c.blackF()); case QColor::Invalid: default: return {}; } } // END utility annotation functions } poppler-24.02.0/qt5/src/poppler-annotation.h000066400000000000000000001220211455701731300206460ustar00rootroot00000000000000/* poppler-annotation.h: qt interface to poppler * Copyright (C) 2006-2008, 2012, 2013, 2018-2022 Albert Astals Cid * Copyright (C) 2006, 2008 Pino Toscano * Copyright (C) 2007, Brad Hards * Copyright (C) 2010, Philip Lorenz * Copyright (C) 2012, 2015, Tobias Koenig * Copyright (C) 2012, Guillermo A. Amaral B. * Copyright (C) 2012, 2013 Fabio D'Urso * 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) 2021, Mahmoud Ahmed Khalil * Adapting code from * Copyright (C) 2004 by Enrico Ros * * 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_ANNOTATION_H_ #define _POPPLER_ANNOTATION_H_ #include #include #include #include #include #include #include #include #include #include #include #include "poppler-export.h" #include namespace Poppler { class Annotation; class AnnotationPrivate; class AnnotationAppearancePrivate; class TextAnnotationPrivate; class LineAnnotationPrivate; class GeomAnnotationPrivate; class HighlightAnnotationPrivate; class StampAnnotationPrivate; class InkAnnotationPrivate; class LinkAnnotationPrivate; class CaretAnnotationPrivate; class FileAttachmentAnnotationPrivate; class SoundAnnotationPrivate; class MovieAnnotationPrivate; class ScreenAnnotationPrivate; class WidgetAnnotationPrivate; class RichMediaAnnotationPrivate; class EmbeddedFile; class Link; class SoundObject; class MovieObject; class LinkRendition; class Page; /** * \short Helper class for (recursive) Annotation retrieval/storage. * */ class POPPLER_QT5_EXPORT AnnotationUtils { public: /** * Restore an Annotation (with revisions if needed) from the DOM * element \p annElement. * \returns a pointer to the complete Annotation or 0 if element is * invalid. */ Q_DECL_DEPRECATED static Annotation *createAnnotation(const QDomElement &annElement); /** * Save the Annotation \p ann as a child of \p annElement taking * care of saving all revisions if \p ann has any. */ Q_DECL_DEPRECATED static void storeAnnotation(const Annotation *ann, QDomElement &annElement, QDomDocument &document); /** * Returns an element called \p name from the direct children of * \p parentNode or a null element if not found. */ Q_DECL_DEPRECATED static QDomElement findChildElement(const QDomNode &parentNode, const QString &name); }; /** * \short AnnotationAppearance class wrapping Poppler's AP stream object * * The Annotation's Appearance Stream is a Form XObject containing * information required to properly render the Annotation on the document. * * This class wraps Poppler's Object implementing the appearance stream * for the calling annotation. It can be used to preserve the current * Appearance Stream for the calling annotation. * * \since 21.10.0 */ class POPPLER_QT5_EXPORT AnnotationAppearance { friend class Annotation; public: explicit AnnotationAppearance(AnnotationAppearancePrivate *annotationAppearancePrivate); ~AnnotationAppearance(); private: AnnotationAppearancePrivate *d; Q_DISABLE_COPY(AnnotationAppearance) }; /** * \short Annotation class holding properties shared by all annotations. * * An Annotation is an object (text note, highlight, sound, popup window, ..) * contained by a Page in the document. * * \warning Different Annotation objects might point to the same annotation. * * \section annotCreation How to add annotations * * Create an Annotation object of the desired subclass (for example * TextAnnotation) and set its properties: * @code * Poppler::TextAnnotation* myann = new Poppler::TextAnnotation(Poppler::TextAnnotation::InPlace); * myann->setBoundary(QRectF(0.1, 0.1, 0.2, 0.2)); // normalized coordinates: (0,0) is top-left, (1,1) is bottom-right * myann->setContents("Hello, world!"); * @endcode * \note Always set a boundary rectangle, or nothing will be shown! * * Obtain a pointer to the Page where you want to add the annotation (refer to * \ref req for instructions) and add the annotation: * @code * Poppler::Page* mypage = ...; * mypage->addAnnotation(myann); * @endcode * * You can keep on editing the annotation after it has been added to the page: * @code * myann->setContents("World, hello!"); // Let's change text... * myann->setAuthor("Your name here"); // ...and set an author too * @endcode * * When you're done with editing the annotation, you must destroy the Annotation * object: * @code * delete myann; * @endcode * * Use the PDFConverter class to save the modified document. * * \section annotFixedRotation FixedRotation flag specifics * * According to the PDF specification, annotations whose * Annotation::FixedRotation flag is set must always be shown in their original * orientation, no matter what the current rendering rotation or the page's * Page::orientation() values are. In comparison with regular annotations, such * annotations should therefore be transformed by an extra rotation at rendering * time to "undo" such context-related rotations, which is equal to * -(rendering_rotation + page_orientation). The rotation pivot * is the top-left corner of the boundary rectangle. * * In practice, %Poppler's \ref Page::renderToImage only "unrotates" the * page orientation, and does not unrotate the rendering rotation. * This ensures consistent renderings at different Page::Rotation values: * annotations are always positioned as if they were being positioned at the * default page orientation. * * Just like regular annotations, %Poppler Qt5 exposes normalized coordinates * relative to the page's default orientation. However, behind the scenes, the * coordinate system is different and %Poppler transparently transforms each * shape. If you never call either Annotation::setFlags or * Annotation::setBoundary, you don't need to worry about this; but if you do * call them, then you need to adhere to the following rules: * - Whenever you toggle the Annotation::FixedRotation flag, you must * set again the boundary rectangle first, and then you must set * again any other geometry-related property. * - Whenever you modify the boundary rectangle of an annotation whose * Annotation::FixedRotation flag is set, you must set again any other * geometry-related property. * * These two rules are necessary to make %Poppler's transparent coordinate * conversion work properly. */ class POPPLER_QT5_EXPORT Annotation { friend class AnnotationUtils; friend class LinkMovie; friend class LinkRendition; public: // enum definitions /** * Annotation subclasses * * \sa subType() */ // WARNING!!! oKular uses that very same values so if you change them notify the author! enum SubType { AText = 1, ///< TextAnnotation ALine = 2, ///< LineAnnotation AGeom = 3, ///< GeomAnnotation AHighlight = 4, ///< HighlightAnnotation AStamp = 5, ///< StampAnnotation AInk = 6, ///< InkAnnotation ALink = 7, ///< LinkAnnotation ACaret = 8, ///< CaretAnnotation AFileAttachment = 9, ///< FileAttachmentAnnotation ASound = 10, ///< SoundAnnotation AMovie = 11, ///< MovieAnnotation AScreen = 12, ///< ScreenAnnotation \since 0.20 AWidget = 13, ///< WidgetAnnotation \since 0.22 ARichMedia = 14, ///< RichMediaAnnotation \since 0.36 A_BASE = 0 }; /** * Annotation flags * * They can be OR'd together (e.g. Annotation::FixedRotation | Annotation::DenyPrint). * * \sa flags(), setFlags(int) */ // NOTE: Only flags that are known to work are documented enum Flag { Hidden = 1, ///< Do not display or print the annotation FixedSize = 2, FixedRotation = 4, ///< Do not rotate the annotation according to page orientation and rendering rotation \warning Extra care is needed with this flag: see \ref annotFixedRotation DenyPrint = 8, ///< Do not print the annotation DenyWrite = 16, DenyDelete = 32, ToggleHidingOnMouse = 64, External = 128 }; enum LineStyle { Solid = 1, Dashed = 2, Beveled = 4, Inset = 8, Underline = 16 }; enum LineEffect { NoEffect = 1, Cloudy = 2 }; enum RevScope { Root = 0 /** \since 0.20 */, Reply = 1, Group = 2, Delete = 4 }; enum RevType { None = 1, Marked = 2, Unmarked = 4, Accepted = 8, Rejected = 16, Cancelled = 32, Completed = 64 }; /** * Returns the author of the annotation. */ QString author() const; /** * Sets a new author for the annotation. */ void setAuthor(const QString &author); QString contents() const; void setContents(const QString &contents); /** * Returns the unique name (ID) of the annotation. */ QString uniqueName() const; /** * Sets a new unique name for the annotation. * * \note no check of the new uniqueName is done */ void setUniqueName(const QString &uniqueName); QDateTime modificationDate() const; void setModificationDate(const QDateTime &date); QDateTime creationDate() const; void setCreationDate(const QDateTime &date); /** * Returns this annotation's flags * * \sa Flag, setFlags(int) */ int flags() const; /** * Sets this annotation's flags * * \sa Flag, flags(), \ref annotFixedRotation */ void setFlags(int flags); /** * Returns this annotation's boundary rectangle in normalized coordinates * * \sa setBoundary(const QRectF&) */ QRectF boundary() const; /** * Sets this annotation's boundary rectangle * * The boundary rectangle is the smallest rectangle that contains the * annotation. * * \warning This property is mandatory: you must always set this. * * \sa boundary(), \ref annotFixedRotation */ void setBoundary(const QRectF &boundary); /** * \short Container class for Annotation style information * * \since 0.20 */ class POPPLER_QT5_EXPORT Style { public: Style(); Style(const Style &other); Style &operator=(const Style &other); ~Style(); // appearance properties QColor color() const; // black void setColor(const QColor &color); double opacity() const; // 1.0 void setOpacity(double opacity); // pen properties double width() const; // 1.0 void setWidth(double width); LineStyle lineStyle() const; // LineStyle::Solid void setLineStyle(LineStyle style); double xCorners() const; // 0.0 void setXCorners(double radius); double yCorners() const; // 0.0 void setYCorners(double radius); const QVector &dashArray() const; // [ 3 ] void setDashArray(const QVector &array); // pen effects LineEffect lineEffect() const; // LineEffect::NoEffect void setLineEffect(LineEffect effect); double effectIntensity() const; // 1.0 void setEffectIntensity(double intens); private: class Private; QSharedDataPointer d; }; /// \since 0.20 Style style() const; /// \since 0.20 void setStyle(const Style &style); /** * \short Container class for Annotation pop-up window information * * \since 0.20 */ class POPPLER_QT5_EXPORT Popup { public: Popup(); Popup(const Popup &other); Popup &operator=(const Popup &other); ~Popup(); // window state (Hidden, FixedRotation, Deny* flags allowed) int flags() const; // -1 (never initialized) -> 0 (if inited and shown) void setFlags(int flags); // geometric properties QRectF geometry() const; // no default void setGeometry(const QRectF &geom); // window contents/override properties QString title() const; // '' text in the titlebar (overrides author) void setTitle(const QString &title); QString summary() const; // '' short description (displayed if not empty) void setSummary(const QString &summary); QString text() const; // '' text for the window (overrides annot->contents) void setText(const QString &text); private: class Private; QSharedDataPointer d; }; /// \since 0.20 Popup popup() const; /// \warning Currently does nothing \since 0.20 void setPopup(const Popup &popup); /// \since 0.20 RevScope revisionScope() const; // Root /// \since 0.20 RevType revisionType() const; // None /** * Returns the revisions of this annotation * * \note The caller owns the returned annotations and they should * be deleted when no longer required. * * \since 0.20 */ QList revisions() const; /** * The type of the annotation. */ virtual SubType subType() const = 0; /** * Returns the current appearance stream of this annotation. * * \since 21.10.0 */ std::unique_ptr annotationAppearance() const; /** * Sets the annotation's appearance stream with the @p annotationAppearance. * * \since 21.10.0 */ void setAnnotationAppearance(const AnnotationAppearance &annotationAppearance); /** * Destructor. */ virtual ~Annotation(); /** * Describes the flags from an annotations 'AA' dictionary. * * This flag is used by the additionalAction() method for ScreenAnnotation * and WidgetAnnotation. * * \since 0.22 */ enum AdditionalActionType { CursorEnteringAction, ///< Performed when the cursor enters the annotation's active area CursorLeavingAction, ///< Performed when the cursor exists the annotation's active area MousePressedAction, ///< Performed when the mouse button is pressed inside the annotation's active area MouseReleasedAction, ///< Performed when the mouse button is released inside the annotation's active area FocusInAction, ///< Performed when the annotation receives the input focus FocusOutAction, ///< Performed when the annotation loses the input focus PageOpeningAction, ///< Performed when the page containing the annotation is opened PageClosingAction, ///< Performed when the page containing the annotation is closed PageVisibleAction, ///< Performed when the page containing the annotation becomes visible PageInvisibleAction ///< Performed when the page containing the annotation becomes invisible }; protected: /// \cond PRIVATE explicit Annotation(AnnotationPrivate &dd); Annotation(AnnotationPrivate &dd, const QDomNode &annNode); void storeBaseAnnotationProperties(QDomNode &annNode, QDomDocument &document) const; Q_DECLARE_PRIVATE(Annotation) QExplicitlySharedDataPointer d_ptr; /// \endcond private: virtual void store(QDomNode &parentNode, QDomDocument &document) const = 0; Q_DISABLE_COPY(Annotation) }; /** * \short Annotation containing text. * * A text annotation is an object showing some text directly on the page, or * linked to the contents using an icon shown on a page. */ class POPPLER_QT5_EXPORT TextAnnotation : public Annotation { friend class AnnotationUtils; friend class AnnotationPrivate; public: // local enums enum TextType { Linked, InPlace }; enum InplaceIntent { Unknown, Callout, TypeWriter }; explicit TextAnnotation(TextType type); ~TextAnnotation() override; SubType subType() const override; /** The type of text annotation represented by this object */ TextType textType() const; /** The name of the icon for this text annotation. Standard names for text annotation icons are: - Comment - Help - Insert - Key - NewParagraph - Note (this is the default icon to use) - Paragraph */ QString textIcon() const; /** Set the name of the icon to use for this text annotation. \sa textIcon for the list of standard names */ void setTextIcon(const QString &icon); QFont textFont() const; void setTextFont(const QFont &font); /// Default text color is black \since 0.69 QColor textColor() const; /// \since 0.69 void setTextColor(const QColor &color); // 0:left, 1:center, 2:right int inplaceAlign() const; void setInplaceAlign(int align); QPointF calloutPoint(int id) const; /// \since 0.20 QVector calloutPoints() const; /// \since 0.20 void setCalloutPoints(const QVector &points); InplaceIntent inplaceIntent() const; void setInplaceIntent(InplaceIntent intent); private: explicit TextAnnotation(const QDomNode &node); explicit TextAnnotation(TextAnnotationPrivate &dd); void store(QDomNode &parentNode, QDomDocument &document) const override; void setTextType(TextType type); Q_DECLARE_PRIVATE(TextAnnotation) Q_DISABLE_COPY(TextAnnotation) }; /** * \short Polygon/polyline annotation. * * This annotation represents a polygon (or polyline) to be drawn on a page. */ class POPPLER_QT5_EXPORT LineAnnotation : public Annotation { friend class AnnotationUtils; friend class AnnotationPrivate; public: // local enums /// \since 0.20 enum LineType { StraightLine, Polyline }; enum TermStyle { Square, Circle, Diamond, OpenArrow, ClosedArrow, None, Butt, ROpenArrow, RClosedArrow, Slash }; enum LineIntent { Unknown, Arrow, Dimension, PolygonCloud }; /// \since 0.20 explicit LineAnnotation(LineType type); ~LineAnnotation() override; SubType subType() const override; /// \since 0.20 LineType lineType() const; QLinkedList linePoints() const; void setLinePoints(const QLinkedList &points); TermStyle lineStartStyle() const; void setLineStartStyle(TermStyle style); TermStyle lineEndStyle() const; void setLineEndStyle(TermStyle style); bool isLineClosed() const; void setLineClosed(bool closed); QColor lineInnerColor() const; void setLineInnerColor(const QColor &color); double lineLeadingForwardPoint() const; void setLineLeadingForwardPoint(double point); double lineLeadingBackPoint() const; void setLineLeadingBackPoint(double point); bool lineShowCaption() const; void setLineShowCaption(bool show); LineIntent lineIntent() const; void setLineIntent(LineIntent intent); private: explicit LineAnnotation(const QDomNode &node); explicit LineAnnotation(LineAnnotationPrivate &dd); void store(QDomNode &parentNode, QDomDocument &document) const override; void setLineType(LineType type); Q_DECLARE_PRIVATE(LineAnnotation) Q_DISABLE_COPY(LineAnnotation) }; /** * \short Geometric annotation. * * The geometric annotation represents a geometric figure, like a rectangle or * an ellipse. */ class POPPLER_QT5_EXPORT GeomAnnotation : public Annotation { friend class AnnotationUtils; friend class AnnotationPrivate; public: GeomAnnotation(); ~GeomAnnotation() override; SubType subType() const override; // common enums enum GeomType { InscribedSquare, InscribedCircle }; GeomType geomType() const; void setGeomType(GeomType type); QColor geomInnerColor() const; void setGeomInnerColor(const QColor &color); private: explicit GeomAnnotation(const QDomNode &node); explicit GeomAnnotation(GeomAnnotationPrivate &dd); void store(QDomNode &parentNode, QDomDocument &document) const override; Q_DECLARE_PRIVATE(GeomAnnotation) Q_DISABLE_COPY(GeomAnnotation) }; /** * \short Text highlight annotation. * * The highlight annotation represents some areas of text being "highlighted". */ class POPPLER_QT5_EXPORT HighlightAnnotation : public Annotation { friend class AnnotationUtils; friend class AnnotationPrivate; public: HighlightAnnotation(); ~HighlightAnnotation() override; SubType subType() const override; /** The type of highlight */ enum HighlightType { Highlight, ///< highlighter pen style annotation Squiggly, ///< jagged or squiggly underline Underline, ///< straight line underline StrikeOut ///< straight line through-line }; /** Structure corresponding to a QuadPoints array. This matches a quadrilateral that describes the area around a word (or set of words) that are to be highlighted. */ struct Quad { QPointF points[4]; // 8 valid coords bool capStart; // false (vtx 1-4) [K] bool capEnd; // false (vtx 2-3) [K] double feather; // 0.1 (in range 0..1) [K] }; /** The type (style) of highlighting to use for this area or these areas. */ HighlightType highlightType() const; /** Set the type of highlighting to use for the given area or areas. */ void setHighlightType(HighlightType type); /** The list of areas to highlight. */ QList highlightQuads() const; /** Set the areas to highlight. */ void setHighlightQuads(const QList &quads); private: explicit HighlightAnnotation(const QDomNode &node); explicit HighlightAnnotation(HighlightAnnotationPrivate &dd); void store(QDomNode &parentNode, QDomDocument &document) const override; Q_DECLARE_PRIVATE(HighlightAnnotation) Q_DISABLE_COPY(HighlightAnnotation) }; /** * \short Stamp annotation. * * A simple annotation drawing a stamp on a page. */ class POPPLER_QT5_EXPORT StampAnnotation : public Annotation { friend class AnnotationUtils; friend class AnnotationPrivate; public: StampAnnotation(); ~StampAnnotation() override; SubType subType() const override; /** The name of the icon for this stamp annotation. Standard names for stamp annotation icons are: - Approved - AsIs - Confidential - Departmental - Draft (this is the default icon type) - Experimental - Expired - Final - ForComment - ForPublicRelease - NotApproved - NotForPublicRelease - Sold - TopSecret */ QString stampIconName() const; /** Set the icon type for this stamp annotation. \sa stampIconName for the list of standard icon names */ void setStampIconName(const QString &name); /** Set a custom icon for this stamp annotation. \since 21.10.0 */ void setStampCustomImage(const QImage &image); private: explicit StampAnnotation(const QDomNode &node); explicit StampAnnotation(StampAnnotationPrivate &dd); void store(QDomNode &parentNode, QDomDocument &document) const override; Q_DECLARE_PRIVATE(StampAnnotation) Q_DISABLE_COPY(StampAnnotation) }; /** * \short Ink Annotation. * * Annotation representing an ink path on a page. */ class POPPLER_QT5_EXPORT InkAnnotation : public Annotation { friend class AnnotationUtils; friend class AnnotationPrivate; public: InkAnnotation(); ~InkAnnotation() override; SubType subType() const override; QList> inkPaths() const; void setInkPaths(const QList> &paths); private: explicit InkAnnotation(const QDomNode &node); void store(QDomNode &parentNode, QDomDocument &document) const override; explicit InkAnnotation(InkAnnotationPrivate &dd); Q_DECLARE_PRIVATE(InkAnnotation) Q_DISABLE_COPY(InkAnnotation) }; class POPPLER_QT5_EXPORT LinkAnnotation : public Annotation { friend class AnnotationUtils; friend class AnnotationPrivate; public: ~LinkAnnotation() override; SubType subType() const override; // local enums enum HighlightMode { None, Invert, Outline, Push }; /** \since 0.20 */ Link *linkDestination() const; void setLinkDestination(Link *link); HighlightMode linkHighlightMode() const; void setLinkHighlightMode(HighlightMode mode); QPointF linkRegionPoint(int id) const; // TODO Next ABI break, remove ref from point void setLinkRegionPoint(int id, const QPointF &point); private: LinkAnnotation(); explicit LinkAnnotation(const QDomNode &node); explicit LinkAnnotation(LinkAnnotationPrivate &dd); void store(QDomNode &parentNode, QDomDocument &document) const override; Q_DECLARE_PRIVATE(LinkAnnotation) Q_DISABLE_COPY(LinkAnnotation) }; /** * \short Caret annotation. * * The caret annotation represents a symbol to indicate the presence of text. */ class POPPLER_QT5_EXPORT CaretAnnotation : public Annotation { friend class AnnotationUtils; friend class AnnotationPrivate; public: CaretAnnotation(); ~CaretAnnotation() override; SubType subType() const override; /** * The symbols for the caret annotation. */ enum CaretSymbol { None, P }; CaretSymbol caretSymbol() const; void setCaretSymbol(CaretSymbol symbol); private: explicit CaretAnnotation(const QDomNode &node); explicit CaretAnnotation(CaretAnnotationPrivate &dd); void store(QDomNode &parentNode, QDomDocument &document) const override; Q_DECLARE_PRIVATE(CaretAnnotation) Q_DISABLE_COPY(CaretAnnotation) }; /** * \short File attachment annotation. * * The file attachment annotation represents a file embedded in the document. * * \since 0.10 */ class POPPLER_QT5_EXPORT FileAttachmentAnnotation : public Annotation { friend class AnnotationPrivate; public: ~FileAttachmentAnnotation() override; SubType subType() const override; /** * Returns the name of the icon of this annotation. */ QString fileIconName() const; /** * Sets a new name for the icon of this annotation. */ void setFileIconName(const QString &icon); /** * Returns the EmbeddedFile of this annotation. */ EmbeddedFile *embeddedFile() const; /** * Sets a new EmbeddedFile for this annotation. * * \note FileAttachmentAnnotation takes ownership of the object */ void setEmbeddedFile(EmbeddedFile *ef); private: FileAttachmentAnnotation(); explicit FileAttachmentAnnotation(const QDomNode &node); explicit FileAttachmentAnnotation(FileAttachmentAnnotationPrivate &dd); void store(QDomNode &parentNode, QDomDocument &document) const override; Q_DECLARE_PRIVATE(FileAttachmentAnnotation) Q_DISABLE_COPY(FileAttachmentAnnotation) }; /** * \short Sound annotation. * * The sound annotation represents a sound to be played when activated. * * \since 0.10 */ class POPPLER_QT5_EXPORT SoundAnnotation : public Annotation { friend class AnnotationPrivate; public: ~SoundAnnotation() override; SubType subType() const override; /** * Returns the name of the icon of this annotation. */ QString soundIconName() const; /** * Sets a new name for the icon of this annotation. */ void setSoundIconName(const QString &icon); /** * Returns the SoundObject of this annotation. */ SoundObject *sound() const; /** * Sets a new SoundObject for this annotation. * * \note SoundAnnotation takes ownership of the object */ void setSound(SoundObject *s); private: SoundAnnotation(); explicit SoundAnnotation(const QDomNode &node); explicit SoundAnnotation(SoundAnnotationPrivate &dd); void store(QDomNode &parentNode, QDomDocument &document) const override; Q_DECLARE_PRIVATE(SoundAnnotation) Q_DISABLE_COPY(SoundAnnotation) }; /** * \short Movie annotation. * * The movie annotation represents a movie to be played when activated. * * \since 0.10 */ class POPPLER_QT5_EXPORT MovieAnnotation : public Annotation { friend class AnnotationPrivate; public: ~MovieAnnotation() override; SubType subType() const override; /** * Returns the MovieObject of this annotation. */ MovieObject *movie() const; /** * Sets a new MovieObject for this annotation. * * \note MovieAnnotation takes ownership of the object */ void setMovie(MovieObject *movie); /** * Returns the title of the movie of this annotation. */ QString movieTitle() const; /** * Sets a new title for the movie of this annotation. */ void setMovieTitle(const QString &title); private: MovieAnnotation(); explicit MovieAnnotation(const QDomNode &node); explicit MovieAnnotation(MovieAnnotationPrivate &dd); void store(QDomNode &parentNode, QDomDocument &document) const override; Q_DECLARE_PRIVATE(MovieAnnotation) Q_DISABLE_COPY(MovieAnnotation) }; /** * \short Screen annotation. * * The screen annotation represents a screen to be played when activated. * * \since 0.20 */ class POPPLER_QT5_EXPORT ScreenAnnotation : public Annotation { friend class AnnotationPrivate; public: ~ScreenAnnotation() override; SubType subType() const override; /** * Returns the LinkRendition of this annotation. */ LinkRendition *action() const; /** * Sets a new LinkRendition for this annotation. * * \note ScreenAnnotation takes ownership of the object */ void setAction(LinkRendition *action); /** * Returns the title of the screen of this annotation. */ QString screenTitle() const; /** * Sets a new title for the screen of this annotation. */ void setScreenTitle(const QString &title); /** * Returns the additional action of the given @p type fo the annotation or * @c 0 if no action has been defined. * * \since 0.22 */ Link *additionalAction(AdditionalActionType type) const; private: ScreenAnnotation(); explicit ScreenAnnotation(ScreenAnnotationPrivate &dd); void store(QDomNode &parentNode, QDomDocument &document) const override; // stub Q_DECLARE_PRIVATE(ScreenAnnotation) Q_DISABLE_COPY(ScreenAnnotation) }; /** * \short Widget annotation. * * The widget annotation represents a widget (form field) on a page. * * \note This class is just provided for consistency of the annotation API, * use the FormField classes to get all the form-related information. * * \since 0.22 */ class POPPLER_QT5_EXPORT WidgetAnnotation : public Annotation { friend class AnnotationPrivate; public: ~WidgetAnnotation() override; SubType subType() const override; /** * Returns the additional action of the given @p type fo the annotation or * @c 0 if no action has been defined. * * \since 0.22 */ Link *additionalAction(AdditionalActionType type) const; private: WidgetAnnotation(); explicit WidgetAnnotation(WidgetAnnotationPrivate &dd); void store(QDomNode &parentNode, QDomDocument &document) const override; // stub Q_DECLARE_PRIVATE(WidgetAnnotation) Q_DISABLE_COPY(WidgetAnnotation) }; /** * \short RichMedia annotation. * * The RichMedia annotation represents a video or sound on a page. * * \since 0.36 */ class POPPLER_QT5_EXPORT RichMediaAnnotation : public Annotation { friend class AnnotationPrivate; public: ~RichMediaAnnotation() override; SubType subType() const override; /** * The params object of a RichMediaAnnotation::Instance object. * * The params object provides media specific parameters, to play * back the media inside the PDF viewer. * * At the moment only parameters for flash player are supported. */ class POPPLER_QT5_EXPORT Params { friend class AnnotationPrivate; public: Params(); ~Params(); /** * Returns the parameters for the flash player. */ QString flashVars() const; private: void setFlashVars(const QString &flashVars); class Private; QScopedPointer d; }; /** * The instance object of a RichMediaAnnotation::Configuration object. * * The instance object represents one media object, that should be shown * on the page. It has a media type and a Params object, to define the * media specific parameters. */ class POPPLER_QT5_EXPORT Instance { friend class AnnotationPrivate; public: /** * Describes the media type of the instance. */ enum Type { Type3D, ///< A 3D media file. TypeFlash, ///< A Flash media file. TypeSound, ///< A sound media file. TypeVideo ///< A video media file. }; Instance(); ~Instance(); /** * Returns the media type of the instance. */ Type type() const; /** * Returns the params object of the instance or @c 0 if it doesn't exist. */ RichMediaAnnotation::Params *params() const; private: void setType(Type type); void setParams(RichMediaAnnotation::Params *params); class Private; QScopedPointer d; }; /** * The configuration object of a RichMediaAnnotation::Content object. * * The configuration object provides access to the various Instance objects * of the rich media annotation. */ class POPPLER_QT5_EXPORT Configuration { friend class AnnotationPrivate; public: /** * Describes the media type of the configuration. */ enum Type { Type3D, ///< A 3D media file. TypeFlash, ///< A Flash media file. TypeSound, ///< A sound media file. TypeVideo ///< A video media file. }; Configuration(); ~Configuration(); /** * Returns the media type of the configuration. */ Type type() const; /** * Returns the name of the configuration. */ QString name() const; /** * Returns the list of Instance objects of the configuration. */ QList instances() const; private: void setType(Type type); void setName(const QString &name); void setInstances(const QList &instances); class Private; QScopedPointer d; }; /** * The asset object of a RichMediaAnnotation::Content object. * * The asset object provides a mapping between identifier name, as * used in the flash vars string of RichMediaAnnotation::Params, and the * associated file spec object. */ class POPPLER_QT5_EXPORT Asset { friend class AnnotationPrivate; public: Asset(); ~Asset(); /** * Returns the identifier name of the asset. */ QString name() const; /** * Returns the embedded file the asset points to. */ EmbeddedFile *embeddedFile() const; private: void setName(const QString &name); void setEmbeddedFile(EmbeddedFile *embeddedFile); class Private; QScopedPointer d; }; /** * The content object of a RichMediaAnnotation. * * The content object provides access to the list of configurations * and assets of the rich media annotation. */ class POPPLER_QT5_EXPORT Content { friend class AnnotationPrivate; public: Content(); ~Content(); /** * Returns the list of configuration objects of the content object. */ QList configurations() const; /** * Returns the list of asset objects of the content object. */ QList assets() const; private: void setConfigurations(const QList &configurations); void setAssets(const QList &assets); class Private; QScopedPointer d; }; /** * The activation object of the RichMediaAnnotation::Settings object. * * The activation object is a wrapper around the settings for the activation * state. At the moment it provides only the activation condition. */ class POPPLER_QT5_EXPORT Activation { friend class AnnotationPrivate; public: /** * Describes the condition for activating the rich media. */ enum Condition { PageOpened, ///< Activate when page is opened. PageVisible, ///< Activate when page becomes visible. UserAction ///< Activate when user interacts with the annotation. }; Activation(); ~Activation(); /** * Returns the activation condition. */ Condition condition() const; private: void setCondition(Condition condition); class Private; QScopedPointer d; }; /** * The deactivation object of the RichMediaAnnotation::Settings object. * * The deactivation object is a wrapper around the settings for the deactivation * state. At the moment it provides only the deactivation condition. */ class POPPLER_QT5_EXPORT Deactivation { friend class AnnotationPrivate; public: /** * Describes the condition for deactivating the rich media. */ enum Condition { PageClosed, ///< Deactivate when page is closed. PageInvisible, ///< Deactivate when page becomes invisible. UserAction ///< Deactivate when user interacts with the annotation. }; Deactivation(); ~Deactivation(); /** * Returns the deactivation condition. */ Condition condition() const; private: void setCondition(Condition condition); class Private; QScopedPointer d; }; /** * The settings object of a RichMediaAnnotation. * * The settings object provides access to the configuration objects * for annotation activation and deactivation. */ class POPPLER_QT5_EXPORT Settings { friend class AnnotationPrivate; public: Settings(); ~Settings(); /** * Returns the Activation object of the settings object or @c 0 if it doesn't exist. */ RichMediaAnnotation::Activation *activation() const; /** * Returns the Deactivation object of the settings object or @c 0 if it doesn't exist. */ RichMediaAnnotation::Deactivation *deactivation() const; private: void setActivation(RichMediaAnnotation::Activation *activation); void setDeactivation(RichMediaAnnotation::Deactivation *deactivation); class Private; QScopedPointer d; }; /** * Returns the Settings object of the rich media annotation or @c 0 if it doesn't exist. */ RichMediaAnnotation::Settings *settings() const; /** * Returns the Content object of the rich media annotation or @c 0 if it doesn't exist. */ RichMediaAnnotation::Content *content() const; private: void setSettings(RichMediaAnnotation::Settings *settings); void setContent(RichMediaAnnotation::Content *content); RichMediaAnnotation(); explicit RichMediaAnnotation(const QDomNode &node); explicit RichMediaAnnotation(RichMediaAnnotationPrivate &dd); void store(QDomNode &parentNode, QDomDocument &document) const override; Q_DECLARE_PRIVATE(RichMediaAnnotation) Q_DISABLE_COPY(RichMediaAnnotation) }; } #endif poppler-24.02.0/qt5/src/poppler-base-converter.cc000066400000000000000000000043771455701731300215660ustar00rootroot00000000000000/* poppler-base-converter.cc: qt interface to poppler * Copyright (C) 2007, 2009, Albert Astals Cid * Copyright (C) 2008, Pino Toscano * * 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 "poppler-qt5.h" #include "poppler-converter-private.h" #include namespace Poppler { BaseConverterPrivate::BaseConverterPrivate() : document(nullptr), iodev(nullptr), ownIodev(true) { } BaseConverterPrivate::~BaseConverterPrivate() { } QIODevice *BaseConverterPrivate::openDevice() { if (!iodev) { Q_ASSERT(!outputFileName.isEmpty()); QFile *f = new QFile(outputFileName); iodev = f; ownIodev = true; } Q_ASSERT(iodev); if (!iodev->isOpen()) { if (!iodev->open(QIODevice::WriteOnly)) { if (ownIodev) { delete iodev; iodev = nullptr; } else { return nullptr; } } } return iodev; } void BaseConverterPrivate::closeDevice() { if (ownIodev) { iodev->close(); delete iodev; iodev = nullptr; } } BaseConverter::BaseConverter(BaseConverterPrivate &dd) : d_ptr(&dd) { } BaseConverter::~BaseConverter() { delete d_ptr; } void BaseConverter::setOutputFileName(const QString &outputFileName) { Q_D(BaseConverter); d->outputFileName = outputFileName; } void BaseConverter::setOutputDevice(QIODevice *device) { Q_D(BaseConverter); d->iodev = device; d->ownIodev = false; } BaseConverter::Error BaseConverter::lastError() const { Q_D(const BaseConverter); return d->lastError; } } poppler-24.02.0/qt5/src/poppler-converter-private.h000066400000000000000000000027421455701731300221620ustar00rootroot00000000000000/* poppler-converter-private.h: Qt interface to poppler * Copyright (C) 2007, 2009, 2018, Albert Astals Cid * Copyright (C) 2008, Pino Toscano * * 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_QT5_CONVERTER_PRIVATE_H #define POPPLER_QT5_CONVERTER_PRIVATE_H #include class QIODevice; namespace Poppler { class DocumentData; class BaseConverterPrivate { public: BaseConverterPrivate(); virtual ~BaseConverterPrivate(); BaseConverterPrivate(const BaseConverterPrivate &) = delete; BaseConverterPrivate &operator=(const BaseConverterPrivate &) = delete; QIODevice *openDevice(); void closeDevice(); DocumentData *document; QString outputFileName; QIODevice *iodev; bool ownIodev : 1; BaseConverter::Error lastError; }; } #endif poppler-24.02.0/qt5/src/poppler-document.cc000066400000000000000000000553531455701731300204650ustar00rootroot00000000000000/* 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) 2006-2010, Pino Toscano * Copyright (C) 2010, 2011 Hib Eris * Copyright (C) 2012 Koji Otani * Copyright (C) 2012, 2013 Thomas Freitag * Copyright (C) 2012 Fabio D'Urso * Copyright (C) 2014, 2018, 2020 Adam Reichold * Copyright (C) 2015 William Bader * Copyright (C) 2016 Jakub Alba * Copyright (C) 2017, 2021 Adrian Johnson * Copyright (C) 2017 Suzuki Toshiya * 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-2021 Oliver Sander * Copyright (C) 2019 Alexander Volkov * Copyright (C) 2020 Philipp Knechtges * Copyright (C) 2020 Katarina Behrens * Copyright (C) 2020 Thorsten Behrens * Copyright (C) 2021 Mahmoud Khalil * Copyright (C) 2021 Hubert Figuiere * * 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 "poppler-qt5.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "poppler-form.h" #include "poppler-private.h" #include "poppler-page-private.h" #include "poppler-outline-private.h" #if defined(USE_CMS) # include #endif namespace Poppler { Document *Document::load(const QString &filePath, const QByteArray &ownerPassword, const QByteArray &userPassword) { DocumentData *doc = new DocumentData(filePath, GooString(ownerPassword.data()), GooString(userPassword.data())); return DocumentData::checkDocument(doc); } Document *Document::load(QIODevice *device, const QByteArray &ownerPassword, const QByteArray &userPassword) { DocumentData *doc = new DocumentData(device, GooString(ownerPassword.data()), GooString(userPassword.data())); return DocumentData::checkDocument(doc); } Document *Document::loadFromData(const QByteArray &fileContents, const QByteArray &ownerPassword, const QByteArray &userPassword) { // create stream DocumentData *doc = new DocumentData(fileContents, GooString(ownerPassword.data()), GooString(userPassword.data())); return DocumentData::checkDocument(doc); } Document *DocumentData::checkDocument(DocumentData *doc) { Document *pdoc; if (doc->doc->isOk() || doc->doc->getErrorCode() == errEncrypted) { pdoc = new Document(doc); if (doc->doc->getErrorCode() == errEncrypted) { pdoc->m_doc->locked = true; } else { pdoc->m_doc->locked = false; pdoc->m_doc->fillMembers(); } return pdoc; } else { delete doc; } return nullptr; } Document::Document(DocumentData *dataA) { m_doc = dataA; } Document::~Document() { delete m_doc; } Page *Document::page(int index) const { Page *page = new Page(m_doc, index); if (page->m_page->page == nullptr) { delete page; return nullptr; } return page; } bool Document::isLocked() const { return m_doc->locked; } bool Document::unlock(const QByteArray &ownerPassword, const QByteArray &userPassword) { if (m_doc->locked) { /* racier then it needs to be */ DocumentData *doc2; if (!m_doc->fileContents.isEmpty()) { doc2 = new DocumentData(m_doc->fileContents, GooString(ownerPassword.data()), GooString(userPassword.data())); } else if (m_doc->m_device) { doc2 = new DocumentData(m_doc->m_device, GooString(ownerPassword.data()), GooString(userPassword.data())); } else { doc2 = new DocumentData(m_doc->m_filePath, GooString(ownerPassword.data()), GooString(userPassword.data())); } if (!doc2->doc->isOk()) { delete doc2; } else { delete m_doc; m_doc = doc2; m_doc->locked = false; m_doc->fillMembers(); } } return m_doc->locked; } Document::PageMode Document::pageMode() const { switch (m_doc->doc->getCatalog()->getPageMode()) { case Catalog::pageModeNone: return UseNone; case Catalog::pageModeOutlines: return UseOutlines; case Catalog::pageModeThumbs: return UseThumbs; case Catalog::pageModeFullScreen: return FullScreen; case Catalog::pageModeOC: return UseOC; case Catalog::pageModeAttach: return UseAttach; default: return UseNone; } } Document::PageLayout Document::pageLayout() const { switch (m_doc->doc->getCatalog()->getPageLayout()) { case Catalog::pageLayoutNone: return NoLayout; case Catalog::pageLayoutSinglePage: return SinglePage; case Catalog::pageLayoutOneColumn: return OneColumn; case Catalog::pageLayoutTwoColumnLeft: return TwoColumnLeft; case Catalog::pageLayoutTwoColumnRight: return TwoColumnRight; case Catalog::pageLayoutTwoPageLeft: return TwoPageLeft; case Catalog::pageLayoutTwoPageRight: return TwoPageRight; default: return NoLayout; } } Qt::LayoutDirection Document::textDirection() const { if (!m_doc->doc->getCatalog()->getViewerPreferences()) { return Qt::LayoutDirectionAuto; } switch (m_doc->doc->getCatalog()->getViewerPreferences()->getDirection()) { case ViewerPreferences::directionL2R: return Qt::LeftToRight; case ViewerPreferences::directionR2L: return Qt::RightToLeft; default: return Qt::LayoutDirectionAuto; } } int Document::numPages() const { return m_doc->doc->getNumPages(); } QList Document::fonts() const { QList ourList; FontIterator it(0, m_doc); while (it.hasNext()) { ourList += it.next(); } return ourList; } QList Document::embeddedFiles() const { return m_doc->m_embeddedFiles; } FontIterator *Document::newFontIterator(int startPage) const { return new FontIterator(startPage, m_doc); } QByteArray Document::fontData(const FontInfo &fi) const { QByteArray result; if (fi.isEmbedded()) { XRef *xref = m_doc->doc->getXRef()->copy(); Object refObj(fi.m_data->embRef); Object strObj = refObj.fetch(xref); if (strObj.isStream()) { int c; strObj.streamReset(); while ((c = strObj.streamGetChar()) != EOF) { result.append((char)c); } strObj.streamClose(); } delete xref; } return result; } QString Document::info(const QString &type) const { if (m_doc->locked) { return QString(); } std::unique_ptr goo(m_doc->doc->getDocInfoStringEntry(type.toLatin1().constData())); return UnicodeParsedString(goo.get()); } bool Document::setInfo(const QString &key, const QString &val) { if (m_doc->locked) { return false; } GooString *goo = QStringToUnicodeGooString(val); m_doc->doc->setDocInfoStringEntry(key.toLatin1().constData(), goo); return true; } QString Document::title() const { if (m_doc->locked) { return QString(); } std::unique_ptr goo(m_doc->doc->getDocInfoTitle()); return UnicodeParsedString(goo.get()); } bool Document::setTitle(const QString &val) { if (m_doc->locked) { return false; } m_doc->doc->setDocInfoTitle(QStringToUnicodeGooString(val)); return true; } QString Document::author() const { if (m_doc->locked) { return QString(); } std::unique_ptr goo(m_doc->doc->getDocInfoAuthor()); return UnicodeParsedString(goo.get()); } bool Document::setAuthor(const QString &val) { if (m_doc->locked) { return false; } m_doc->doc->setDocInfoAuthor(QStringToUnicodeGooString(val)); return true; } QString Document::subject() const { if (m_doc->locked) { return QString(); } std::unique_ptr goo(m_doc->doc->getDocInfoSubject()); return UnicodeParsedString(goo.get()); } bool Document::setSubject(const QString &val) { if (m_doc->locked) { return false; } m_doc->doc->setDocInfoSubject(QStringToUnicodeGooString(val)); return true; } QString Document::keywords() const { if (m_doc->locked) { return QString(); } std::unique_ptr goo(m_doc->doc->getDocInfoKeywords()); return UnicodeParsedString(goo.get()); } bool Document::setKeywords(const QString &val) { if (m_doc->locked) { return false; } m_doc->doc->setDocInfoKeywords(QStringToUnicodeGooString(val)); return true; } QString Document::creator() const { if (m_doc->locked) { return QString(); } std::unique_ptr goo(m_doc->doc->getDocInfoCreator()); return UnicodeParsedString(goo.get()); } bool Document::setCreator(const QString &val) { if (m_doc->locked) { return false; } m_doc->doc->setDocInfoCreator(QStringToUnicodeGooString(val)); return true; } QString Document::producer() const { if (m_doc->locked) { return QString(); } std::unique_ptr goo(m_doc->doc->getDocInfoProducer()); return UnicodeParsedString(goo.get()); } bool Document::setProducer(const QString &val) { if (m_doc->locked) { return false; } m_doc->doc->setDocInfoProducer(QStringToUnicodeGooString(val)); return true; } bool Document::removeInfo() { if (m_doc->locked) { return false; } m_doc->doc->removeDocInfo(); return true; } QStringList Document::infoKeys() const { QStringList keys; if (m_doc->locked) { return QStringList(); } QScopedPointer xref(m_doc->doc->getXRef()->copy()); if (!xref) { return QStringList(); } Object info = xref->getDocInfo(); if (!info.isDict()) { return QStringList(); } Dict *infoDict = info.getDict(); // somehow iterate over keys in infoDict keys.reserve(infoDict->getLength()); for (int i = 0; i < infoDict->getLength(); ++i) { keys.append(QString::fromLatin1(infoDict->getKey(i))); } return keys; } QDateTime Document::date(const QString &type) const { if (m_doc->locked) { return QDateTime(); } std::unique_ptr goo(m_doc->doc->getDocInfoStringEntry(type.toLatin1().constData())); QString str = UnicodeParsedString(goo.get()); return Poppler::convertDate(str.toLatin1().constData()); } bool Document::setDate(const QString &key, const QDateTime &val) { if (m_doc->locked) { return false; } m_doc->doc->setDocInfoStringEntry(key.toLatin1().constData(), QDateTimeToUnicodeGooString(val)); return true; } QDateTime Document::creationDate() const { if (m_doc->locked) { return QDateTime(); } std::unique_ptr goo(m_doc->doc->getDocInfoCreatDate()); QString str = UnicodeParsedString(goo.get()); return Poppler::convertDate(str.toLatin1().constData()); } bool Document::setCreationDate(const QDateTime &val) { if (m_doc->locked) { return false; } m_doc->doc->setDocInfoCreatDate(QDateTimeToUnicodeGooString(val)); return true; } QDateTime Document::modificationDate() const { if (m_doc->locked) { return QDateTime(); } std::unique_ptr goo(m_doc->doc->getDocInfoModDate()); QString str = UnicodeParsedString(goo.get()); return Poppler::convertDate(str.toLatin1().constData()); } bool Document::setModificationDate(const QDateTime &val) { if (m_doc->locked) { return false; } m_doc->doc->setDocInfoModDate(QDateTimeToUnicodeGooString(val)); return true; } bool Document::isEncrypted() const { return m_doc->doc->isEncrypted(); } bool Document::isLinearized() const { return m_doc->doc->isLinearized(); } bool Document::okToPrint() const { return m_doc->doc->okToPrint(); } bool Document::okToPrintHighRes() const { return m_doc->doc->okToPrintHighRes(); } bool Document::okToChange() const { return m_doc->doc->okToChange(); } bool Document::okToCopy() const { return m_doc->doc->okToCopy(); } bool Document::okToAddNotes() const { return m_doc->doc->okToAddNotes(); } bool Document::okToFillForm() const { return m_doc->doc->okToFillForm(); } bool Document::okToCreateFormFields() const { return (okToFillForm() && okToChange()); } bool Document::okToExtractForAccessibility() const { return m_doc->doc->okToAccessibility(); } bool Document::okToAssemble() const { return m_doc->doc->okToAssemble(); } void Document::getPdfVersion(int *major, int *minor) const { if (major) { *major = m_doc->doc->getPDFMajorVersion(); } if (minor) { *minor = m_doc->doc->getPDFMinorVersion(); } } Document::PdfVersion Document::getPdfVersion() const { return PdfVersion { m_doc->doc->getPDFMajorVersion(), m_doc->doc->getPDFMinorVersion() }; } Page *Document::page(const QString &label) const { GooString label_g(label.toLatin1().data()); int index; if (!m_doc->doc->getCatalog()->labelToIndex(&label_g, &index)) { std::unique_ptr label_ug(QStringToUnicodeGooString(label)); if (!m_doc->doc->getCatalog()->labelToIndex(label_ug.get(), &index)) { return nullptr; } } return page(index); } bool Document::hasEmbeddedFiles() const { return (!(0 == m_doc->doc->getCatalog()->numEmbeddedFiles())); } QDomDocument *Document::toc() const { Outline *outline = m_doc->doc->getOutline(); if (!outline) { return nullptr; } const std::vector<::OutlineItem *> *items = outline->getItems(); if (!items || items->size() < 1) { return nullptr; } QDomDocument *toc = new QDomDocument(); if (items->size() > 0) { m_doc->addTocChildren(toc, toc, items); } return toc; } QVector Document::outline() const { QVector result; if (::Outline *outline = m_doc->doc->getOutline()) { if (const std::vector<::OutlineItem *> *items = outline->getItems()) { for (void *item : *items) { result.push_back(OutlineItem { new OutlineItemData { static_cast<::OutlineItem *>(item), m_doc } }); } } } return result; } LinkDestination *Document::linkDestination(const QString &name) { GooString *namedDest = QStringToGooString(name); LinkDestinationData ldd(nullptr, namedDest, m_doc, false); LinkDestination *ld = new LinkDestination(ldd); delete namedDest; return ld; } void Document::setPaperColor(const QColor &color) { m_doc->setPaperColor(color); } void Document::setColorDisplayProfile(void *outputProfileA) { #if defined(USE_CMS) if (m_doc->m_sRGBProfile && m_doc->m_sRGBProfile.get() == outputProfileA) { // Catch the special case that the user passes the sRGB profile m_doc->m_displayProfile = m_doc->m_sRGBProfile; return; } if (m_doc->m_displayProfile && m_doc->m_displayProfile.get() == outputProfileA) { // Catch the special case that the user passes the display profile return; } m_doc->m_displayProfile = make_GfxLCMSProfilePtr(outputProfileA); #else Q_UNUSED(outputProfileA); #endif } void Document::setColorDisplayProfileName(const QString &name) { #if defined(USE_CMS) void *rawprofile = cmsOpenProfileFromFile(name.toLocal8Bit().constData(), "r"); m_doc->m_displayProfile = make_GfxLCMSProfilePtr(rawprofile); #else Q_UNUSED(name); #endif } void *Document::colorRgbProfile() const { #if defined(USE_CMS) if (!m_doc->m_sRGBProfile) { m_doc->m_sRGBProfile = make_GfxLCMSProfilePtr(cmsCreate_sRGBProfile()); } return m_doc->m_sRGBProfile.get(); #else return nullptr; #endif } void *Document::colorDisplayProfile() const { #if defined(USE_CMS) return m_doc->m_displayProfile.get(); #else return nullptr; #endif } QColor Document::paperColor() const { return m_doc->paperColor; } void Document::setRenderBackend(Document::RenderBackend backend) { // no need to delete the outputdev as for the moment we always create a splash one // as the QPainter one does not allow "precaching" due to it's signature // delete m_doc->m_outputDev; // m_doc->m_outputDev = NULL; m_doc->m_backend = backend; } Document::RenderBackend Document::renderBackend() const { return m_doc->m_backend; } QSet Document::availableRenderBackends() { QSet ret; ret << Document::SplashBackend; ret << Document::QPainterBackend; ret << Document::ArthurBackend; // For backward compatibility return ret; } void Document::setRenderHint(Document::RenderHint hint, bool on) { const bool touchesOverprinting = hint & Document::OverprintPreview; int hintForOperation = hint; if (touchesOverprinting && !isOverprintPreviewAvailable()) { hintForOperation = hintForOperation & ~(int)Document::OverprintPreview; } if (on) { m_doc->m_hints |= hintForOperation; } else { m_doc->m_hints &= ~hintForOperation; } } Document::RenderHints Document::renderHints() const { return Document::RenderHints(m_doc->m_hints); } PSConverter *Document::psConverter() const { return new PSConverter(m_doc); } PDFConverter *Document::pdfConverter() const { return new PDFConverter(m_doc); } QString Document::metadata() const { QString result; Catalog *catalog = m_doc->doc->getCatalog(); if (catalog && catalog->isOk()) { std::unique_ptr s = catalog->readMetadata(); if (s) { result = UnicodeParsedString(s.get()); } } return result; } bool Document::hasOptionalContent() const { return (m_doc->doc->getOptContentConfig() && m_doc->doc->getOptContentConfig()->hasOCGs()); } OptContentModel *Document::optionalContentModel() { if (m_doc->m_optContentModel.isNull()) { m_doc->m_optContentModel = new OptContentModel(m_doc->doc->getOptContentConfig(), nullptr); } return (OptContentModel *)m_doc->m_optContentModel; } 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) { scripts.append(UnicodeParsedString(s)); delete s; } } return scripts; } bool Document::getPdfId(QByteArray *permanentId, QByteArray *updateId) const { GooString gooPermanentId; GooString gooUpdateId; if (!m_doc->doc->getID(permanentId ? &gooPermanentId : nullptr, updateId ? &gooUpdateId : nullptr)) { return false; } if (permanentId) { *permanentId = gooPermanentId.c_str(); } if (updateId) { *updateId = gooUpdateId.c_str(); } return true; } Document::FormType Document::formType() const { switch (m_doc->doc->getCatalog()->getFormType()) { case Catalog::NoForm: return Document::NoForm; case Catalog::AcroForm: return Document::AcroForm; case Catalog::XfaForm: return Document::XfaForm; } return Document::NoForm; // make gcc happy } QVector Document::formCalculateOrder() const { Form *form = m_doc->doc->getCatalog()->getForm(); if (!form) { return {}; } QVector result; const std::vector &calculateOrder = form->getCalculateOrder(); for (Ref r : calculateOrder) { FormWidget *w = form->findWidgetByRef(r); if (w) { result << w->getID(); } } return result; } QVector Document::signatures() const { QVector result; const std::vector<::FormFieldSignature *> pSignatures = m_doc->doc->getSignatureFields(); for (::FormFieldSignature *pSignature : pSignatures) { ::FormWidget *fw = pSignature->getCreateWidget(); ::Page *p = m_doc->doc->getPage(fw->getWidgetAnnotation()->getPageNum()); result.append(new FormFieldSignature(m_doc, p, static_cast(fw))); } return result; } bool Document::xrefWasReconstructed() const { return m_doc->xrefReconstructed; } void Document::setXRefReconstructedCallback(const std::function &callback) { m_doc->xrefReconstructedCallback = callback; } QDateTime convertDate(const char *dateString) { int year, mon, day, hour, min, sec, tzHours, tzMins; char tz; GooString date(dateString); if (parseDateString(&date, &year, &mon, &day, &hour, &min, &sec, &tz, &tzHours, &tzMins)) { QDate d(year, mon, day); QTime t(hour, min, sec); if (d.isValid() && t.isValid()) { QDateTime dt(d, t, Qt::UTC); 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); } else if ('-' == tz) { // local time is behind UTC dt = dt.addSecs(((tzHours * 60) + tzMins) * 60); } else { qWarning("unexpected tz val"); } } return dt; } } return QDateTime(); } QDateTime convertDate(char *dateString) { return convertDate((const char *)dateString); } bool isCmsAvailable() { #if defined(USE_CMS) return true; #else return false; #endif } bool isOverprintPreviewAvailable() { return true; } } poppler-24.02.0/qt5/src/poppler-embeddedfile-private.h000066400000000000000000000023741455701731300225450ustar00rootroot00000000000000/* poppler-embeddedfile-private.h: Qt interface to poppler * Copyright (C) 2005, 2008, 2009, 2012, 2018, 2021, 2022, Albert Astals Cid * Copyright (C) 2005, Brad Hards * Copyright (C) 2008, 2011, Pino Toscano * * 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_EMBEDDEDFILE_PRIVATE_H #define POPPLER_EMBEDDEDFILE_PRIVATE_H class FileSpec; namespace Poppler { class EmbeddedFileData { public: explicit EmbeddedFileData(std::unique_ptr &&fs); EmbFile *embFile() const; std::unique_ptr filespec; }; } #endif poppler-24.02.0/qt5/src/poppler-embeddedfile.cc000066400000000000000000000071771455701731300212410ustar00rootroot00000000000000/* poppler-document.cc: qt interface to poppler * Copyright (C) 2005, 2008, 2009, 2012, 2013, 2018, 2022, Albert Astals Cid * 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 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 * 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 "poppler-qt5.h" #include #include #include "Object.h" #include "Stream.h" #include "Catalog.h" #include "poppler-private.h" #include "poppler-embeddedfile-private.h" namespace Poppler { EmbeddedFileData::EmbeddedFileData(std::unique_ptr &&fs) : filespec(std::move(fs)) { } EmbFile *EmbeddedFileData::embFile() const { return filespec->isOk() ? filespec->getEmbeddedFile() : nullptr; } EmbeddedFile::EmbeddedFile(EmbFile *embfile) : m_embeddedFile(nullptr) { assert(!"You must not use this private constructor!"); } EmbeddedFile::EmbeddedFile(EmbeddedFileData &dd) : m_embeddedFile(&dd) { } EmbeddedFile::~EmbeddedFile() { delete m_embeddedFile; } QString EmbeddedFile::name() const { const GooString *goo = m_embeddedFile->filespec->getFileName(); return goo ? UnicodeParsedString(goo) : QString(); } QString EmbeddedFile::description() const { const GooString *goo = m_embeddedFile->filespec->getDescription(); return goo ? UnicodeParsedString(goo) : QString(); } int EmbeddedFile::size() const { return m_embeddedFile->embFile() ? m_embeddedFile->embFile()->size() : -1; } QDateTime EmbeddedFile::modDate() const { const GooString *goo = m_embeddedFile->embFile() ? m_embeddedFile->embFile()->modDate() : nullptr; return goo ? convertDate(goo->c_str()) : QDateTime(); } QDateTime EmbeddedFile::createDate() const { const GooString *goo = m_embeddedFile->embFile() ? m_embeddedFile->embFile()->createDate() : nullptr; return goo ? convertDate(goo->c_str()) : QDateTime(); } QByteArray EmbeddedFile::checksum() const { const GooString *goo = m_embeddedFile->embFile() ? m_embeddedFile->embFile()->checksum() : nullptr; return goo ? QByteArray::fromRawData(goo->c_str(), goo->getLength()) : QByteArray(); } QString EmbeddedFile::mimeType() const { const GooString *goo = m_embeddedFile->embFile() ? m_embeddedFile->embFile()->mimeType() : nullptr; return goo ? QString(goo->c_str()) : QString(); } QByteArray EmbeddedFile::data() { if (!isValid()) { return QByteArray(); } Stream *stream = m_embeddedFile->embFile() ? m_embeddedFile->embFile()->stream() : nullptr; if (!stream) { return QByteArray(); } stream->reset(); auto data = stream->toUnsignedChars(); return QByteArray(reinterpret_cast(data.data()), data.size()); } bool EmbeddedFile::isValid() const { return m_embeddedFile->filespec->isOk(); } } poppler-24.02.0/qt5/src/poppler-fontinfo.cc000066400000000000000000000072161455701731300204640ustar00rootroot00000000000000/* poppler-qt.h: qt interface to poppler * Copyright (C) 2005, Net Integration Technologies, Inc. * Copyright (C) 2005, Tobias Koening * Copyright (C) 2005, Brad Hards * Copyright (C) 2005-2008, 2015, Albert Astals Cid * Copyright (C) 2008, 2009, Pino Toscano * Copyright (C) 2018, Adam Reichold * Copyright (C) 2019, Oliver Sander * Copyright (C) 2019, Jan Grulich * * 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 "poppler-qt5.h" #include "poppler-private.h" namespace Poppler { FontInfo::FontInfo() { m_data = new FontInfoData(); } FontInfo::FontInfo(const FontInfoData &fid) { m_data = new FontInfoData(fid); } FontInfo::FontInfo(const FontInfo &fi) { m_data = new FontInfoData(*fi.m_data); } FontInfo::~FontInfo() { delete m_data; } QString FontInfo::name() const { return m_data->fontName; } QString FontInfo::substituteName() const { return m_data->fontSubstituteName; } QString FontInfo::file() const { return m_data->fontFile; } bool FontInfo::isEmbedded() const { return m_data->isEmbedded; } bool FontInfo::isSubset() const { return m_data->isSubset; } FontInfo::Type FontInfo::type() const { return m_data->type; } QString FontInfo::typeName() const { switch (type()) { case unknown: return QObject::tr("unknown"); case Type1: return QObject::tr("Type 1"); case Type1C: return QObject::tr("Type 1C"); case Type3: return QObject::tr("Type 3"); case TrueType: return QObject::tr("TrueType"); case CIDType0: return QObject::tr("CID Type 0"); case CIDType0C: return QObject::tr("CID Type 0C"); case CIDTrueType: return QObject::tr("CID TrueType"); case Type1COT: return QObject::tr("Type 1C (OpenType)"); case TrueTypeOT: return QObject::tr("TrueType (OpenType)"); case CIDType0COT: return QObject::tr("CID Type 0C (OpenType)"); case CIDTrueTypeOT: return QObject::tr("CID TrueType (OpenType)"); } return QObject::tr("Bug: unexpected font type. Notify poppler mailing list!"); } FontInfo &FontInfo::operator=(const FontInfo &fi) { if (this == &fi) { return *this; } *m_data = *fi.m_data; return *this; } FontIterator::FontIterator(int startPage, DocumentData *dd) : d(new FontIteratorData(startPage, dd)) { } FontIterator::~FontIterator() { delete d; } QList FontIterator::next() { ++d->currentPage; QList fonts; const std::vector<::FontInfo *> items = d->fontInfoScanner.scan(1); fonts.reserve(items.size()); for (::FontInfo *entry : items) { fonts.append(FontInfo(FontInfoData(entry))); delete entry; } return fonts; } bool FontIterator::hasNext() const { return (d->currentPage + 1) < d->totalPages; } int FontIterator::currentPage() const { return d->currentPage; } } poppler-24.02.0/qt5/src/poppler-form.cc000066400000000000000000001147731455701731300176140ustar00rootroot00000000000000/* poppler-form.h: qt interface to poppler * Copyright (C) 2007-2008, 2011, Pino Toscano * Copyright (C) 2008, 2011, 2012, 2015-2023 Albert Astals Cid * Copyright (C) 2011 Carlos Garcia Campos * Copyright (C) 2012, Adam Reichold * Copyright (C) 2016, Hanno Meyer-Thurow * Copyright (C) 2017, Hans-Ulrich Jüttner * Copyright (C) 2018, Andre Heinecke * 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 Chinmoy Ranjan Pradhan * Copyright (C) 2018, 2020 Oliver Sander * Copyright (C) 2019 João Netto * Copyright (C) 2020 David García Garzón * 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 Georgiy Sgibnev . Work sponsored by lab50.net. * Copyright (C) 2021 Theofilos Intzoglou * Copyright (C) 2022 Alexander Sulfrian * Copyright (C) 2023 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 * 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 "poppler-form.h" #include #include #include #include #include #include #include #include #include #ifdef ENABLE_NSS3 # include #endif #include "poppler-page-private.h" #include "poppler-private.h" #include "poppler-annotation-helper.h" #include #include namespace { Qt::Alignment formTextAlignment(::FormWidget *fm) { Qt::Alignment qtalign = Qt::AlignLeft; switch (fm->getField()->getTextQuadding()) { case VariableTextQuadding::centered: qtalign = Qt::AlignHCenter; break; case VariableTextQuadding::rightJustified: qtalign = Qt::AlignRight; break; case VariableTextQuadding::leftJustified: qtalign = Qt::AlignLeft; } return qtalign; } } namespace Poppler { FormFieldIcon::FormFieldIcon(FormFieldIconData *data) : d_ptr(data) { } FormFieldIcon::FormFieldIcon(const FormFieldIcon &ffIcon) { d_ptr = new FormFieldIconData; d_ptr->icon = ffIcon.d_ptr->icon; } FormFieldIcon &FormFieldIcon::operator=(const FormFieldIcon &ffIcon) { if (this != &ffIcon) { delete d_ptr; d_ptr = nullptr; d_ptr = new FormFieldIconData; *d_ptr = *ffIcon.d_ptr; } return *this; } FormFieldIcon::~FormFieldIcon() { delete d_ptr; } FormField::FormField(std::unique_ptr dd) : m_formData(std::move(dd)) { if (m_formData->page) { const int rotation = m_formData->page->getRotate(); // reading the coords double left, top, right, bottom; m_formData->fm->getRect(&left, &bottom, &right, &top); // build a normalized transform matrix for this page at 100% scale GfxState gfxState(72.0, 72.0, m_formData->page->getCropBox(), rotation, true); const double *gfxCTM = gfxState.getCTM(); double MTX[6]; double pageWidth = m_formData->page->getCropWidth(); double pageHeight = m_formData->page->getCropHeight(); // landscape and seascape page rotation: be sure to use the correct (== rotated) page size if (((rotation / 90) % 2) == 1) { qSwap(pageWidth, pageHeight); } for (int i = 0; i < 6; i += 2) { MTX[i] = gfxCTM[i] / pageWidth; MTX[i + 1] = gfxCTM[i + 1] / pageHeight; } QPointF topLeft; XPDFReader::transform(MTX, qMin(left, right), qMax(top, bottom), topLeft); QPointF bottomRight; XPDFReader::transform(MTX, qMax(left, right), qMin(top, bottom), bottomRight); m_formData->box = QRectF(topLeft, QSizeF(bottomRight.x() - topLeft.x(), bottomRight.y() - topLeft.y())); } } FormField::~FormField() = default; QRectF FormField::rect() const { return m_formData->box; } int FormField::id() const { return m_formData->fm->getID(); } QString FormField::name() const { QString name; if (const GooString *goo = m_formData->fm->getPartialName()) { name = UnicodeParsedString(goo); } return name; } void FormField::setName(const QString &name) const { GooString *goo = QStringToGooString(name); m_formData->fm->setPartialName(*goo); delete goo; } QString FormField::fullyQualifiedName() const { QString name; if (GooString *goo = m_formData->fm->getFullyQualifiedName()) { name = UnicodeParsedString(goo); } return name; } QString FormField::uiName() const { QString name; if (const GooString *goo = m_formData->fm->getAlternateUiName()) { name = UnicodeParsedString(goo); } return name; } bool FormField::isReadOnly() const { return m_formData->fm->isReadOnly(); } void FormField::setReadOnly(bool value) { m_formData->fm->setReadOnly(value); } bool FormField::isVisible() const { const unsigned int flags = m_formData->fm->getWidgetAnnotation()->getFlags(); if (flags & Annot::flagHidden) { return false; } if (flags & Annot::flagNoView) { return false; } return true; } void FormField::setVisible(bool value) { unsigned int flags = m_formData->fm->getWidgetAnnotation()->getFlags(); if (value) { flags &= ~Annot::flagHidden; flags &= ~Annot::flagNoView; } else { flags |= Annot::flagHidden; } m_formData->fm->getWidgetAnnotation()->setFlags(flags); } bool FormField::isPrintable() const { return (m_formData->fm->getWidgetAnnotation()->getFlags() & Annot::flagPrint); } void FormField::setPrintable(bool value) { unsigned int flags = m_formData->fm->getWidgetAnnotation()->getFlags(); if (value) { flags |= Annot::flagPrint; } else { flags &= ~Annot::flagPrint; } m_formData->fm->getWidgetAnnotation()->setFlags(flags); } Link *FormField::activationAction() const { Link *action = nullptr; if (::LinkAction *act = m_formData->fm->getActivationAction()) { action = PageData::convertLinkActionToLink(act, m_formData->doc, QRectF()); } return action; } Link *FormField::additionalAction(AdditionalActionType type) const { Annot::FormAdditionalActionsType actionType = Annot::actionFieldModified; switch (type) { case FieldModified: actionType = Annot::actionFieldModified; break; case FormatField: actionType = Annot::actionFormatField; break; case ValidateField: actionType = Annot::actionValidateField; break; case CalculateField: actionType = Annot::actionCalculateField; break; } Link *action = nullptr; if (std::unique_ptr<::LinkAction> act = m_formData->fm->getAdditionalAction(actionType)) { action = PageData::convertLinkActionToLink(act.get(), m_formData->doc, QRectF()); } return action; } Link *FormField::additionalAction(Annotation::AdditionalActionType type) const { ::AnnotWidget *w = m_formData->fm->getWidgetAnnotation(); if (!w) { return nullptr; } const Annot::AdditionalActionsType actionType = toPopplerAdditionalActionType(type); Link *action = nullptr; if (std::unique_ptr<::LinkAction> act = w->getAdditionalAction(actionType)) { action = PageData::convertLinkActionToLink(act.get(), m_formData->doc, QRectF()); } return action; } FormFieldButton::FormFieldButton(DocumentData *doc, ::Page *p, ::FormWidgetButton *w) : FormField(std::make_unique(doc, p, w)) { } FormFieldButton::~FormFieldButton() { } FormFieldButton::FormType FormFieldButton::type() const { return FormField::FormButton; } FormFieldButton::ButtonType FormFieldButton::buttonType() const { FormWidgetButton *fwb = static_cast(m_formData->fm); switch (fwb->getButtonType()) { case formButtonCheck: return FormFieldButton::CheckBox; break; case formButtonPush: return FormFieldButton::Push; break; case formButtonRadio: return FormFieldButton::Radio; break; } return FormFieldButton::CheckBox; } QString FormFieldButton::caption() const { FormWidgetButton *fwb = static_cast(m_formData->fm); QString ret; if (fwb->getButtonType() == formButtonPush) { Dict *dict = m_formData->fm->getObj()->getDict(); Object obj1 = dict->lookup("MK"); if (obj1.isDict()) { AnnotAppearanceCharacs appearCharacs(obj1.getDict()); if (appearCharacs.getNormalCaption()) { ret = UnicodeParsedString(appearCharacs.getNormalCaption()); } } } else { if (const char *goo = fwb->getOnStr()) { ret = QString::fromUtf8(goo); } } return ret; } FormFieldIcon FormFieldButton::icon() const { FormWidgetButton *fwb = static_cast(m_formData->fm); if (fwb->getButtonType() == formButtonPush) { Dict *dict = m_formData->fm->getObj()->getDict(); FormFieldIconData *data = new FormFieldIconData; data->icon = dict; return FormFieldIcon(data); } return FormFieldIcon(nullptr); } void FormFieldButton::setIcon(const FormFieldIcon &icon) { if (FormFieldIconData::getData(icon) == nullptr) { return; } FormWidgetButton *fwb = static_cast(m_formData->fm); if (fwb->getButtonType() == formButtonPush) { ::AnnotWidget *w = m_formData->fm->getWidgetAnnotation(); FormFieldIconData *data = FormFieldIconData::getData(icon); if (data->icon != nullptr) { w->setNewAppearance(data->icon->lookup("AP")); } } } bool FormFieldButton::state() const { FormWidgetButton *fwb = static_cast(m_formData->fm); return fwb->getState(); } void FormFieldButton::setState(bool state) { FormWidgetButton *fwb = static_cast(m_formData->fm); fwb->setState((bool)state); } QList FormFieldButton::siblings() const { FormWidgetButton *fwb = static_cast(m_formData->fm); ::FormFieldButton *ffb = static_cast<::FormFieldButton *>(fwb->getField()); if (fwb->getButtonType() == formButtonPush) { return QList(); } QList ret; for (int i = 0; i < ffb->getNumSiblings(); ++i) { ::FormFieldButton *sibling = static_cast<::FormFieldButton *>(ffb->getSibling(i)); for (int j = 0; j < sibling->getNumWidgets(); ++j) { FormWidget *w = sibling->getWidget(j); if (w) { ret.append(w->getID()); } } } return ret; } FormFieldText::FormFieldText(DocumentData *doc, ::Page *p, ::FormWidgetText *w) : FormField(std::make_unique(doc, p, w)) { } FormFieldText::~FormFieldText() { } FormField::FormType FormFieldText::type() const { return FormField::FormText; } FormFieldText::TextType FormFieldText::textType() const { FormWidgetText *fwt = static_cast(m_formData->fm); if (fwt->isFileSelect()) { return FormFieldText::FileSelect; } else if (fwt->isMultiline()) { return FormFieldText::Multiline; } return FormFieldText::Normal; } QString FormFieldText::text() const { const GooString *goo = static_cast(m_formData->fm)->getContent(); return UnicodeParsedString(goo); } void FormFieldText::setText(const QString &text) { FormWidgetText *fwt = static_cast(m_formData->fm); GooString *goo = QStringToUnicodeGooString(text); fwt->setContent(goo); delete goo; } void FormFieldText::setAppearanceText(const QString &text) { FormWidgetText *fwt = static_cast(m_formData->fm); GooString *goo = QStringToUnicodeGooString(text); fwt->setAppearanceContent(goo); delete goo; } bool FormFieldText::isPassword() const { FormWidgetText *fwt = static_cast(m_formData->fm); return fwt->isPassword(); } bool FormFieldText::isRichText() const { FormWidgetText *fwt = static_cast(m_formData->fm); return fwt->isRichText(); } int FormFieldText::maximumLength() const { FormWidgetText *fwt = static_cast(m_formData->fm); const int maxlen = fwt->getMaxLen(); return maxlen > 0 ? maxlen : -1; } Qt::Alignment FormFieldText::textAlignment() const { return formTextAlignment(m_formData->fm); } bool FormFieldText::canBeSpellChecked() const { FormWidgetText *fwt = static_cast(m_formData->fm); return !fwt->noSpellCheck(); } double FormFieldText::getFontSize() const { FormWidgetText *fwt = static_cast(m_formData->fm); return fwt->getTextFontSize(); } void FormFieldText::setFontSize(int fontSize) { FormWidgetText *fwt = static_cast(m_formData->fm); fwt->setTextFontSize(fontSize); } FormFieldChoice::FormFieldChoice(DocumentData *doc, ::Page *p, ::FormWidgetChoice *w) : FormField(std::make_unique(doc, p, w)) { } FormFieldChoice::~FormFieldChoice() { } FormFieldChoice::FormType FormFieldChoice::type() const { return FormField::FormChoice; } FormFieldChoice::ChoiceType FormFieldChoice::choiceType() const { FormWidgetChoice *fwc = static_cast(m_formData->fm); if (fwc->isCombo()) { return FormFieldChoice::ComboBox; } return FormFieldChoice::ListBox; } QStringList FormFieldChoice::choices() const { FormWidgetChoice *fwc = static_cast(m_formData->fm); QStringList ret; int num = fwc->getNumChoices(); ret.reserve(num); for (int i = 0; i < num; ++i) { ret.append(UnicodeParsedString(fwc->getChoice(i))); } return ret; } QVector> FormFieldChoice::choicesWithExportValues() const { FormWidgetChoice *fwc = static_cast(m_formData->fm); QVector> ret; const int num = fwc->getNumChoices(); ret.reserve(num); for (int i = 0; i < num; ++i) { const QString display = UnicodeParsedString(fwc->getChoice(i)); const GooString *exportValueG = fwc->getExportVal(i); const QString exportValue = exportValueG ? UnicodeParsedString(exportValueG) : display; ret.append({ display, exportValue }); } return ret; } bool FormFieldChoice::isEditable() const { FormWidgetChoice *fwc = static_cast(m_formData->fm); return fwc->isCombo() ? fwc->hasEdit() : false; } bool FormFieldChoice::multiSelect() const { FormWidgetChoice *fwc = static_cast(m_formData->fm); return !fwc->isCombo() ? fwc->isMultiSelect() : false; } QList FormFieldChoice::currentChoices() const { FormWidgetChoice *fwc = static_cast(m_formData->fm); int num = fwc->getNumChoices(); QList choices; for (int i = 0; i < num; ++i) { if (fwc->isSelected(i)) { choices.append(i); } } return choices; } void FormFieldChoice::setCurrentChoices(const QList &choice) { FormWidgetChoice *fwc = static_cast(m_formData->fm); fwc->deselectAll(); for (int i = 0; i < choice.count(); ++i) { fwc->select(choice.at(i)); } } QString FormFieldChoice::editChoice() const { FormWidgetChoice *fwc = static_cast(m_formData->fm); if (fwc->isCombo() && fwc->hasEdit()) { return UnicodeParsedString(fwc->getEditChoice()); } else { return QString(); } } 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; } } Qt::Alignment FormFieldChoice::textAlignment() const { return formTextAlignment(m_formData->fm); } bool FormFieldChoice::canBeSpellChecked() const { FormWidgetChoice *fwc = static_cast(m_formData->fm); return !fwc->noSpellCheck(); } class CertificateInfoPrivate { public: struct EntityInfo { QString common_name; QString email_address; QString org_name; QString distinguished_name; }; EntityInfo issuer_info; EntityInfo subject_info; QString nick_name; QByteArray certificate_der; QByteArray serial_number; QByteArray public_key; QDateTime validity_start; QDateTime validity_end; int public_key_type; int public_key_strength; int ku_extensions; int version; bool is_self_signed; bool is_null; CertificateInfo::KeyLocation keyLocation; }; CertificateInfo::CertificateInfo() : d_ptr(new CertificateInfoPrivate()) { d_ptr->is_null = true; } CertificateInfo::CertificateInfo(CertificateInfoPrivate *priv) : d_ptr(priv) { } CertificateInfo::CertificateInfo(const CertificateInfo &other) : d_ptr(other.d_ptr) { } CertificateInfo::~CertificateInfo() = default; CertificateInfo &CertificateInfo::operator=(const CertificateInfo &other) { if (this != &other) { d_ptr = other.d_ptr; } return *this; } bool CertificateInfo::isNull() const { Q_D(const CertificateInfo); return d->is_null; } int CertificateInfo::version() const { Q_D(const CertificateInfo); return d->version; } QByteArray CertificateInfo::serialNumber() const { Q_D(const CertificateInfo); return d->serial_number; } QString CertificateInfo::issuerInfo(EntityInfoKey key) const { Q_D(const CertificateInfo); switch (key) { case CommonName: return d->issuer_info.common_name; case DistinguishedName: return d->issuer_info.distinguished_name; case EmailAddress: return d->issuer_info.email_address; case Organization: return d->issuer_info.org_name; default: return QString(); } } QString CertificateInfo::subjectInfo(EntityInfoKey key) const { Q_D(const CertificateInfo); switch (key) { case CommonName: return d->subject_info.common_name; case DistinguishedName: return d->subject_info.distinguished_name; case EmailAddress: return d->subject_info.email_address; case Organization: return d->subject_info.org_name; default: return QString(); } } QString CertificateInfo::nickName() const { Q_D(const CertificateInfo); return d->nick_name; } QDateTime CertificateInfo::validityStart() const { Q_D(const CertificateInfo); return d->validity_start; } QDateTime CertificateInfo::validityEnd() const { Q_D(const CertificateInfo); return d->validity_end; } CertificateInfo::KeyUsageExtensions CertificateInfo::keyUsageExtensions() const { Q_D(const CertificateInfo); KeyUsageExtensions kuExtensions = KuNone; if (d->ku_extensions & KU_DIGITAL_SIGNATURE) { kuExtensions |= KuDigitalSignature; } if (d->ku_extensions & KU_NON_REPUDIATION) { kuExtensions |= KuNonRepudiation; } if (d->ku_extensions & KU_KEY_ENCIPHERMENT) { kuExtensions |= KuKeyEncipherment; } if (d->ku_extensions & KU_DATA_ENCIPHERMENT) { kuExtensions |= KuDataEncipherment; } if (d->ku_extensions & KU_KEY_AGREEMENT) { kuExtensions |= KuKeyAgreement; } if (d->ku_extensions & KU_KEY_CERT_SIGN) { kuExtensions |= KuKeyCertSign; } if (d->ku_extensions & KU_CRL_SIGN) { kuExtensions |= KuClrSign; } if (d->ku_extensions & KU_ENCIPHER_ONLY) { kuExtensions |= KuEncipherOnly; } return kuExtensions; } CertificateInfo::KeyLocation CertificateInfo::keyLocation() const { Q_D(const CertificateInfo); return d->keyLocation; } QByteArray CertificateInfo::publicKey() const { Q_D(const CertificateInfo); return d->public_key; } CertificateInfo::PublicKeyType CertificateInfo::publicKeyType() const { Q_D(const CertificateInfo); switch (d->public_key_type) { case RSAKEY: return RsaKey; case DSAKEY: return DsaKey; case ECKEY: return EcKey; default: return OtherKey; } } int CertificateInfo::publicKeyStrength() const { Q_D(const CertificateInfo); return d->public_key_strength; } bool CertificateInfo::isSelfSigned() const { Q_D(const CertificateInfo); return d->is_self_signed; } QByteArray CertificateInfo::certificateData() const { Q_D(const CertificateInfo); return d->certificate_der; } bool CertificateInfo::checkPassword(const QString &password) const { #ifdef ENABLE_SIGNATURES auto backend = CryptoSign::Factory::createActive(); if (!backend) { return false; } Q_D(const CertificateInfo); auto sigHandler = backend->createSigningHandler(d->nick_name.toStdString(), HashAlgorithm::Sha256); unsigned char buffer[5]; memcpy(buffer, "test", 5); sigHandler->addData(buffer, 5); std::optional tmpSignature = sigHandler->signDetached(password.toStdString()); return tmpSignature.has_value(); #else return false; #endif } class SignatureValidationInfoPrivate { public: explicit SignatureValidationInfoPrivate(CertificateInfo &&ci) : cert_info(ci) { } SignatureValidationInfo::SignatureStatus signature_status; SignatureValidationInfo::CertificateStatus certificate_status; CertificateInfo cert_info; QByteArray signature; QString signer_name; QString signer_subject_dn; QString location; QString reason; HashAlgorithm hash_algorithm; time_t signing_time; QList range_bounds; qint64 docLength; }; SignatureValidationInfo::SignatureValidationInfo(SignatureValidationInfoPrivate *priv) : d_ptr(priv) { } SignatureValidationInfo::SignatureValidationInfo(const SignatureValidationInfo &other) : d_ptr(other.d_ptr) { } SignatureValidationInfo::~SignatureValidationInfo() { } SignatureValidationInfo::SignatureStatus SignatureValidationInfo::signatureStatus() const { Q_D(const SignatureValidationInfo); return d->signature_status; } SignatureValidationInfo::CertificateStatus SignatureValidationInfo::certificateStatus() const { Q_D(const SignatureValidationInfo); return d->certificate_status; } QString SignatureValidationInfo::signerName() const { Q_D(const SignatureValidationInfo); return d->signer_name; } QString SignatureValidationInfo::signerSubjectDN() const { Q_D(const SignatureValidationInfo); return d->signer_subject_dn; } QString SignatureValidationInfo::location() const { Q_D(const SignatureValidationInfo); return d->location; } QString SignatureValidationInfo::reason() const { Q_D(const SignatureValidationInfo); return d->reason; } SignatureValidationInfo::HashAlgorithm SignatureValidationInfo::hashAlgorithm() const { #ifdef ENABLE_SIGNATURES Q_D(const SignatureValidationInfo); switch (d->hash_algorithm) { case ::HashAlgorithm::Md2: return HashAlgorithmMd2; case ::HashAlgorithm::Md5: return HashAlgorithmMd5; case ::HashAlgorithm::Sha1: return HashAlgorithmSha1; case ::HashAlgorithm::Sha256: return HashAlgorithmSha256; case ::HashAlgorithm::Sha384: return HashAlgorithmSha384; case ::HashAlgorithm::Sha512: return HashAlgorithmSha512; case ::HashAlgorithm::Sha224: return HashAlgorithmSha224; case ::HashAlgorithm::Unknown: return HashAlgorithmUnknown; } #endif return HashAlgorithmUnknown; } time_t SignatureValidationInfo::signingTime() const { Q_D(const SignatureValidationInfo); return d->signing_time; } QByteArray SignatureValidationInfo::signature() const { Q_D(const SignatureValidationInfo); return d->signature; } QList SignatureValidationInfo::signedRangeBounds() const { Q_D(const SignatureValidationInfo); return d->range_bounds; } bool SignatureValidationInfo::signsTotalDocument() const { Q_D(const SignatureValidationInfo); if (d->range_bounds.size() == 4 && d->range_bounds.value(0) == 0 && d->range_bounds.value(1) >= 0 && d->range_bounds.value(2) > d->range_bounds.value(1) && d->range_bounds.value(3) >= d->range_bounds.value(2)) { // The range from d->range_bounds.value(1) to d->range_bounds.value(2) is // not authenticated by the signature and should only contain the signature // itself padded with 0 bytes. This has been checked in readSignature(). // If it failed, d->signature is empty. // A potential range after d->range_bounds.value(3) would be also not // authenticated. Therefore d->range_bounds.value(3) should coincide with // the end of the document. if (d->docLength == d->range_bounds.value(3) && !d->signature.isEmpty()) { return true; } } return false; } CertificateInfo SignatureValidationInfo::certificateInfo() const { Q_D(const SignatureValidationInfo); return d->cert_info; } SignatureValidationInfo &SignatureValidationInfo::operator=(const SignatureValidationInfo &other) { if (this != &other) { d_ptr = other.d_ptr; } return *this; } FormFieldSignature::FormFieldSignature(DocumentData *doc, ::Page *p, ::FormWidgetSignature *w) : FormField(std::make_unique(doc, p, w)) { } FormFieldSignature::~FormFieldSignature() { } FormField::FormType FormFieldSignature::type() const { return FormField::FormSignature; } FormFieldSignature::SignatureType FormFieldSignature::signatureType() const { SignatureType sigType = AdbePkcs7detached; FormWidgetSignature *fws = static_cast(m_formData->fm); switch (fws->signatureType()) { case adbe_pkcs7_sha1: sigType = AdbePkcs7sha1; break; case adbe_pkcs7_detached: sigType = AdbePkcs7detached; break; case ETSI_CAdES_detached: sigType = EtsiCAdESdetached; break; case unknown_signature_type: sigType = UnknownSignatureType; break; case unsigned_signature_field: sigType = UnsignedSignature; break; } return sigType; } SignatureValidationInfo FormFieldSignature::validate(ValidateOptions opt) const { return validate(opt, QDateTime()); } 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) { CertificateInfoPrivate *certPriv = new CertificateInfoPrivate; certPriv->is_null = true; if (ci) { certPriv->version = ci->getVersion(); certPriv->ku_extensions = ci->getKeyUsageExtensions(); certPriv->keyLocation = fromPopplerCore(ci->getKeyLocation()); const GooString &certSerial = ci->getSerialNumber(); certPriv->serial_number = QByteArray(certSerial.c_str(), certSerial.getLength()); const X509CertificateInfo::EntityInfo &issuerInfo = ci->getIssuerInfo(); certPriv->issuer_info.common_name = issuerInfo.commonName.c_str(); certPriv->issuer_info.distinguished_name = issuerInfo.distinguishedName.c_str(); certPriv->issuer_info.email_address = issuerInfo.email.c_str(); certPriv->issuer_info.org_name = issuerInfo.organization.c_str(); const X509CertificateInfo::EntityInfo &subjectInfo = ci->getSubjectInfo(); certPriv->subject_info.common_name = subjectInfo.commonName.c_str(); certPriv->subject_info.distinguished_name = subjectInfo.distinguishedName.c_str(); certPriv->subject_info.email_address = subjectInfo.email.c_str(); certPriv->subject_info.org_name = subjectInfo.organization.c_str(); 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); const X509CertificateInfo::PublicKeyInfo &pkInfo = ci->getPublicKeyInfo(); certPriv->public_key = QByteArray(pkInfo.publicKey.c_str(), pkInfo.publicKey.getLength()); certPriv->public_key_type = static_cast(pkInfo.publicKeyType); certPriv->public_key_strength = pkInfo.publicKeyStrength; const GooString &certDer = ci->getCertificateDER(); certPriv->certificate_der = QByteArray(certDer.c_str(), certDer.getLength()); certPriv->is_null = false; } return certPriv; } SignatureValidationInfo FormFieldSignature::validate(int opt, const QDateTime &validationTime) const { 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); // get certificate info const X509CertificateInfo *ci = si->getCertificateInfo(); CertificateInfoPrivate *certPriv = createCertificateInfoPrivate(ci); SignatureValidationInfoPrivate *priv = new SignatureValidationInfoPrivate(CertificateInfo(certPriv)); switch (si->getSignatureValStatus()) { case SIGNATURE_VALID: priv->signature_status = SignatureValidationInfo::SignatureValid; break; case SIGNATURE_INVALID: priv->signature_status = SignatureValidationInfo::SignatureInvalid; break; case SIGNATURE_DIGEST_MISMATCH: priv->signature_status = SignatureValidationInfo::SignatureDigestMismatch; break; case SIGNATURE_DECODING_ERROR: priv->signature_status = SignatureValidationInfo::SignatureDecodingError; break; default: case SIGNATURE_GENERIC_ERROR: priv->signature_status = SignatureValidationInfo::SignatureGenericError; break; case SIGNATURE_NOT_FOUND: priv->signature_status = SignatureValidationInfo::SignatureNotFound; break; case SIGNATURE_NOT_VERIFIED: 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 = 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()); priv->signing_time = si->getSigningTime(); const std::vector ranges = fws->getSignedRangeBounds(); if (!ranges.empty()) { for (Goffset bound : ranges) { priv->range_bounds.append(bound); } } const std::optional checkedSignature = fws->getCheckedSignature(&priv->docLength); if (priv->range_bounds.size() == 4 && checkedSignature) { priv->signature = QByteArray::fromHex(checkedSignature->c_str()); } return SignatureValidationInfo(priv); } 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) { return FieldAlreadySigned; } Goffset file_size = 0; const std::optional sig = fws->getCheckedSignature(&file_size); if (sig) { // the above unsigned_signature_field check // should already catch this, but double check return FieldAlreadySigned; } const auto reason = std::unique_ptr(data.reason().isEmpty() ? nullptr : QStringToUnicodeGooString(data.reason())); 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()); const auto gSignatureText = std::unique_ptr(QStringToUnicodeGooString(data.signatureText())); const auto gSignatureLeftText = std::unique_ptr(QStringToUnicodeGooString(data.signatureLeftText())); 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())); return success ? SigningSuccess : GenericSigningError; } bool hasNSSSupport() { #ifdef ENABLE_NSS3 return true; #else return false; #endif } QVector getAvailableSigningCertificates() { auto backend = CryptoSign::Factory::createActive(); if (!backend) { return {}; } QVector vReturnCerts; std::vector> vCerts = backend->getAvailableSigningCertificates(); for (auto &cert : vCerts) { CertificateInfoPrivate *certPriv = createCertificateInfoPrivate(cert.get()); vReturnCerts.append(CertificateInfo(certPriv)); } 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(NSSSignatureConfiguration::getNSSDir().c_str()); #else return QString(); #endif } void setNSSDir(const QString &path) { #ifdef ENABLE_NSS3 if (path.isEmpty()) { return; } GooString *goo = QStringToGooString(path); NSSSignatureConfiguration::setNSSDir(*goo); delete goo; #else (void)path; #endif } namespace { std::function nssPasswordCall; } void setNSSPasswordCallback(const std::function &f) { #ifdef ENABLE_NSS3 NSSSignatureConfiguration::setNSSPasswordCallback(f); #else qWarning() << "setNSSPasswordCallback called but this poppler is built without NSS support"; (void)f; #endif } } poppler-24.02.0/qt5/src/poppler-form.h000066400000000000000000000572221455701731300174510ustar00rootroot00000000000000/* poppler-form.h: qt interface to poppler * Copyright (C) 2007-2008, Pino Toscano * Copyright (C) 2008, 2011, 2016, 2017, 2019-2022, Albert Astals Cid * Copyright (C) 2012, Adam Reichold * Copyright (C) 2016, Hanno Meyer-Thurow * Copyright (C) 2017, Hans-Ulrich Jüttner * Copyright (C) 2017, Tobias C. Berner * Copyright (C) 2018, Andre Heinecke * Copyright (C) 2018, Chinmoy Ranjan Pradhan * Copyright (C) 2018, Oliver Sander * Copyright (C) 2019 João Netto * Copyright (C) 2019, Adrian Johnson * 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, 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 * 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_QT5_FORM_H_ #define _POPPLER_QT5_FORM_H_ #include #include #include #include #include #include #include #include #include #include #include "poppler-export.h" #include "poppler-annotation.h" #include "poppler-qt5.h" class Object; class Page; class FormWidget; class FormWidgetButton; class FormWidgetText; class FormWidgetChoice; class FormWidgetSignature; namespace Poppler { class DocumentData; class Link; class FormFieldData; class FormFieldIconData; /** The class containing the appearance information \since 0.79 */ class POPPLER_QT5_EXPORT FormFieldIcon { friend class FormFieldIconData; public: explicit FormFieldIcon(FormFieldIconData *data); FormFieldIcon(const FormFieldIcon &ffIcon); ~FormFieldIcon(); FormFieldIcon &operator=(const FormFieldIcon &ffIcon); private: FormFieldIconData *d_ptr; }; /** The base class representing a form field. \since 0.6 */ class POPPLER_QT5_EXPORT FormField { friend class FormFieldData; public: /** The different types of form field. */ enum FormType { FormButton, ///< A button field. See \ref Poppler::FormFieldButton::ButtonType "ButtonType" FormText, ///< A text field. See \ref Poppler::FormFieldText::TextType "TextType" FormChoice, ///< A single choice field. See \ref Poppler::FormFieldChoice::ChoiceType "ChoiceType" FormSignature ///< A signature field. }; virtual ~FormField(); /** The type of the field. */ virtual FormType type() const = 0; /** \return The size of the field, in normalized coordinates, i.e. [0..1] with regard to the dimensions (cropbox) of the page */ QRectF rect() const; /** The ID of the field. */ int id() const; /** The internal name (T) of the field. */ QString name() const; /** Sets the internal name (T) of the field. \since 0.51 */ void setName(const QString &name) const; /** The internal fully qualified name of the field. \since 0.18 */ QString fullyQualifiedName() const; /** The name of the field to be used in user interface (eg messages to the user). */ QString uiName() const; /** Whether this form field is read-only. */ bool isReadOnly() const; /** Set whether this form field is read-only. \since 0.64 */ void setReadOnly(bool value); /** Whether this form field is visible. */ bool isVisible() const; /** Set whether this form field is visible. \since 0.64 */ void setVisible(bool value); /** Whether this field is printable. \since 0.79 */ bool isPrintable() const; /** Set whether this field is printable. \since 0.79 */ void setPrintable(bool value); /** The activation action of this form field. \note It may be null. */ Link *activationAction() const; /** * Describes the flags from the form 'AA' dictionary. * * \since 0.53 */ enum AdditionalActionType { FieldModified, ///< A JavaScript action to be performed when the user modifies the field FormatField, ///< A JavaScript action to be performed before the field is formatted to display its value ValidateField, ///< A JavaScript action to be performed when the field value changes CalculateField, ///< A JavaScript action to be performed when the field needs to be recalculated }; /** * Returns a given form additional action * * \since 0.53 */ Link *additionalAction(AdditionalActionType type) const; /** * Returns a given widget annotation additional action * * \since 0.65 */ Link *additionalAction(Annotation::AdditionalActionType type) const; protected: /// \cond PRIVATE explicit FormField(std::unique_ptr dd); std::unique_ptr m_formData; /// \endcond private: Q_DISABLE_COPY(FormField) }; /** A form field that represents a "button". \since 0.8 */ class POPPLER_QT5_EXPORT FormFieldButton : public FormField { public: /** * The types of button field. */ enum ButtonType { Push, ///< A simple push button. CheckBox, ///< A check box. Radio ///< A radio button. }; /// \cond PRIVATE FormFieldButton(DocumentData *doc, ::Page *p, ::FormWidgetButton *w); /// \endcond ~FormFieldButton() override; FormType type() const override; /** The particular type of the button field. */ ButtonType buttonType() const; /** * The caption to be used for the button. */ QString caption() const; /** * Gets the icon used by the button * * \since 0.79 */ FormFieldIcon icon() const; /** * Sets a new icon for the button, it has to be a icon * returned by FormFieldButton::icon. * * \since 0.79 */ void setIcon(const FormFieldIcon &icon); /** The state of the button. */ bool state() const; /** Sets the state of the button to the new \p state . */ void setState(bool state); /** The list with the IDs of siblings (ie, buttons belonging to the same group as the current one. Valid only for \ref Radio buttons, an empty list otherwise. */ QList siblings() const; private: Q_DISABLE_COPY(FormFieldButton) }; /** A form field that represents a text input. \since 0.6 */ class POPPLER_QT5_EXPORT FormFieldText : public FormField { public: /** The particular type of this text field. */ enum TextType { Normal, ///< A simple singleline text field. Multiline, ///< A multiline text field. FileSelect ///< An input field to select the path of a file on disk. }; /// \cond PRIVATE FormFieldText(DocumentData *doc, ::Page *p, ::FormWidgetText *w); /// \endcond ~FormFieldText() override; FormType type() const override; /** The text type of the text field. */ TextType textType() const; /** The text associated with the text field. */ QString text() const; /** Sets the text associated with the text field to the specified \p text. */ void setText(const QString &text); /** Sets the text inside the Appearance Stream to the specified \p text \since 0.80 */ void setAppearanceText(const QString &text); /** Whether this text field is a password input, eg its text \b must be replaced with asterisks. Always false for \ref FileSelect text fields. */ bool isPassword() const; /** Whether this text field should allow rich text. */ bool isRichText() const; /** The maximum length for the text of this field, or -1 if not set. */ int maximumLength() const; /** The horizontal alignment for the text of this text field. */ Qt::Alignment textAlignment() const; /** Whether the text inserted manually in the field (where possible) can be spell-checked. */ bool canBeSpellChecked() const; /** The font size of the text in the form field */ double getFontSize() const; /** Set the font size of the text in the form field (currently only as integer) */ void setFontSize(int fontSize); private: Q_DISABLE_COPY(FormFieldText) }; /** A form field that represents a choice field. \since 0.6 */ class POPPLER_QT5_EXPORT FormFieldChoice : public FormField { public: /** The particular type of this choice field. */ enum ChoiceType { ComboBox, ///< A simple singleline text field. ListBox ///< A multiline text field. }; /// \cond PRIVATE FormFieldChoice(DocumentData *doc, ::Page *p, ::FormWidgetChoice *w); /// \endcond ~FormFieldChoice() override; FormType type() const override; /** The choice type of the choice field. */ ChoiceType choiceType() const; /** The possible choices of the choice field. */ QStringList choices() const; /** The possible choices of the choice field. The first value of the pair is the display name of the choice, The second value is the export value (i.e. for use in javascript, etc) of the choice @since 0.87 */ QVector> choicesWithExportValues() const; /** Whether this FormFieldChoice::ComboBox is editable, i.e. the user can type in a custom value. Always false for the other types of choices. */ bool isEditable() const; /** Whether more than one choice of this FormFieldChoice::ListBox can be selected at the same time. Always false for the other types of choices. */ bool multiSelect() const; /** The currently selected choices. */ QList currentChoices() const; /** Sets the selected choices to \p choice. */ void setCurrentChoices(const QList &choice); /** The text entered into an editable combo box choice field. Otherwise a null string. \since 0.22 */ QString editChoice() const; /** Sets the text entered into an editable combo box choice field. Otherwise does nothing. \since 0.22 */ void setEditChoice(const QString &text); /** The horizontal alignment for the text of this text field. */ Qt::Alignment textAlignment() const; /** Whether the text inserted manually in the field (where possible) can be spell-checked. Returns false if the field is not an editable text field. */ bool canBeSpellChecked() const; private: Q_DISABLE_COPY(FormFieldChoice) }; /** A helper class to store x509 certificate information. \since 0.73 */ class CertificateInfoPrivate; class POPPLER_QT5_EXPORT CertificateInfo { public: /** The algorithm of public key. */ enum PublicKeyType { RsaKey, DsaKey, EcKey, OtherKey }; /** Certificate key usage extensions. */ enum KeyUsageExtension { KuDigitalSignature = 0x80, KuNonRepudiation = 0x40, KuKeyEncipherment = 0x20, KuDataEncipherment = 0x10, KuKeyAgreement = 0x08, KuKeyCertSign = 0x04, KuClrSign = 0x02, KuEncipherOnly = 0x01, KuNone = 0x00 }; Q_DECLARE_FLAGS(KeyUsageExtensions, KeyUsageExtension) /** Predefined keys for elements in an entity's distinguished name. */ enum EntityInfoKey { CommonName, DistinguishedName, EmailAddress, 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(); /** Returns true if certificate has no contents; otherwise returns false */ bool isNull() const; /** The certificate version string. */ int version() const; /** The certificate serial number. */ QByteArray serialNumber() const; /** Information about the issuer. */ QString issuerInfo(EntityInfoKey key) const; /** Information about the subject */ QString subjectInfo(EntityInfoKey key) const; /** The certificate internal database nickname \since 21.01 */ QString nickName() const; /** The date-time when certificate becomes valid. */ QDateTime validityStart() const; /** The date-time when certificate expires. */ QDateTime validityEnd() const; /** The uses allowed for the certificate. */ KeyUsageExtensions keyUsageExtensions() const; /** The public key value. */ QByteArray publicKey() const; /** The public key type. */ PublicKeyType publicKeyType() const; /** The strength of public key in bits. */ int publicKeyStrength() const; /** Returns true if certificate is self-signed otherwise returns false. */ bool isSelfSigned() const; /** The DER encoded certificate. */ QByteArray certificateData() const; /** Checks if the given password is the correct one for this certificate \since 21.01 */ 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); private: Q_DECLARE_PRIVATE(CertificateInfo) QSharedPointer d_ptr; }; Q_DECLARE_OPERATORS_FOR_FLAGS(CertificateInfo::KeyUsageExtensions) /** A signature validation info helper class. \since 0.51 */ class SignatureValidationInfoPrivate; class POPPLER_QT5_EXPORT SignatureValidationInfo { public: /** The verification result of the signature. */ enum SignatureStatus { SignatureValid, ///< The signature is cryptographically valid. SignatureInvalid, ///< The signature is cryptographically invalid. SignatureDigestMismatch, ///< The document content was changed after the signature was applied. SignatureDecodingError, ///< The signature CMS/PKCS7 structure is malformed. SignatureGenericError, ///< The signature could not be verified. SignatureNotFound, ///< The requested signature is not present in the document. SignatureNotVerified ///< The signature is not yet verified. }; /** The verification result of the certificate. */ enum CertificateStatus { CertificateTrusted, ///< The certificate is considered trusted. CertificateUntrustedIssuer, ///< The issuer of this certificate has been marked as untrusted by the user. CertificateUnknownIssuer, ///< The certificate trust chain has not finished in a trusted root certificate. 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. }; /** The hash algorithm of the signature \since 0.58 */ enum HashAlgorithm { HashAlgorithmUnknown, HashAlgorithmMd2, HashAlgorithmMd5, HashAlgorithmSha1, HashAlgorithmSha256, HashAlgorithmSha384, HashAlgorithmSha512, HashAlgorithmSha224 }; /// \cond PRIVATE explicit SignatureValidationInfo(SignatureValidationInfoPrivate *priv); /// \endcond ~SignatureValidationInfo(); /** The signature status of the signature. */ SignatureStatus signatureStatus() const; /** The certificate status of the signature. */ CertificateStatus certificateStatus() const; /** The signer name associated with the signature. */ QString signerName() const; /** The signer subject distinguished name associated with the signature. \since 0.58 */ QString signerSubjectDN() const; /** Get signing location. \since 0.68 */ QString location() const; /** Get signing reason. \since 0.68 */ QString reason() const; /** The hash algorithm used for the signature. \since 0.58 */ HashAlgorithm hashAlgorithm() const; /** The signing time associated with the signature. */ time_t signingTime() const; /** Get the signature binary data. \since 0.58 */ QByteArray signature() const; /** Get the bounds of the ranges of the document which are signed. \since 0.58 */ QList signedRangeBounds() const; /** Checks whether the signature authenticates the total document except for the signature itself. \since 0.58 */ bool signsTotalDocument() const; /** The signer certificate info. \since 0.73 */ CertificateInfo certificateInfo() const; SignatureValidationInfo(const SignatureValidationInfo &other); SignatureValidationInfo &operator=(const SignatureValidationInfo &other); private: Q_DECLARE_PRIVATE(SignatureValidationInfo) QSharedPointer d_ptr; }; /** A form field that represents a signature. \since 0.51 */ class POPPLER_QT5_EXPORT FormFieldSignature : public FormField { public: /** The types of signature fields. \since 0.58 */ enum SignatureType { AdbePkcs7sha1, AdbePkcs7detached, EtsiCAdESdetached, UnknownSignatureType, ///< \since 0.90 UnsignedSignature ///< \since 22.02 }; /** The validation options of this signature. */ enum ValidateOptions { ValidateVerifyCertificate = 1, ///< Validate the certificate. ValidateForceRevalidation = 2, ///< Force revalidation of the certificate. ValidateWithoutOCSPRevocationCheck = 4, ///< Do not contact OCSP servers to check for certificate revocation status \since 21.10 ValidateUseAIACertFetch = 8 ///< Use the AIA extension for certificate fetching \since 21.10 }; /// \cond PRIVATE FormFieldSignature(DocumentData *doc, ::Page *p, ::FormWidgetSignature *w); /// \endcond ~FormFieldSignature() override; FormType type() const override; /** The signature type \since 0.58 */ SignatureType signatureType() const; /** 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. */ SignatureValidationInfo validate(ValidateOptions opt) const; /** Validate the signature with @p validationTime as validation time. 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. */ SignatureValidationInfo validate(int opt, const QDateTime &validationTime) const; /** * \since 22.02 */ enum SigningResult { FieldAlreadySigned, ///< Trying to sign a field that is already signed GenericSigningError, SigningSuccess }; /** Signs a field of UnsignedSignature type. Ignores data.page(), data.fieldPartialName() and data.boundingRectangle() \since 22.02 */ SigningResult sign(const QString &outputFileName, const PDFConverter::NewSignatureData &data) const; 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_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_DEPRECATED POPPLER_QT5_EXPORT hasNSSSupport(); /** Return vector of suitable signing certificates \since 21.01 */ QVector POPPLER_QT5_EXPORT getAvailableSigningCertificates(); /** Gets the current NSS CertDB directory \since 21.01 */ QString POPPLER_QT5_EXPORT getNSSDir(); /** Set a custom NSS CertDB directory. Needs to be called before doing any other signature operation \since 21.01 */ void POPPLER_QT5_EXPORT setNSSDir(const QString &pathURL); /** Sets the callback for NSS password requests \since 21.01 */ void POPPLER_QT5_EXPORT setNSSPasswordCallback(const std::function &f); } #endif poppler-24.02.0/qt5/src/poppler-link-extractor-private.h000066400000000000000000000031531455701731300231160ustar00rootroot00000000000000/* poppler-link-extractor_p.h: qt interface to poppler * Copyright (C) 2007, 2008, 2011, Pino Toscano * Copyright (C) 2021, 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 * 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_LINK_EXTRACTOR_H_ #define _POPPLER_LINK_EXTRACTOR_H_ #include #include #include namespace Poppler { class Link; class PageData; class LinkExtractorOutputDev : public OutputDev { public: explicit LinkExtractorOutputDev(PageData *data); ~LinkExtractorOutputDev() override; // inherited from OutputDev bool upsideDown() override { return false; } bool useDrawChar() override { return false; } bool interpretType3Chars() override { return false; } void processLink(::AnnotLink *link) override; // our stuff QList links(); private: PageData *m_data; double m_pageCropWidth; double m_pageCropHeight; QList m_links; }; } #endif poppler-24.02.0/qt5/src/poppler-link-extractor.cc000066400000000000000000000050741455701731300216100ustar00rootroot00000000000000/* poppler-link-extractor_p.h: qt interface to poppler * Copyright (C) 2007, 2008, 2011, Pino Toscano * Copyright (C) 2008, 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 * 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 "poppler-link-extractor-private.h" #include #include #include #include #include #include "poppler-qt5.h" #include "poppler-page-private.h" namespace Poppler { LinkExtractorOutputDev::LinkExtractorOutputDev(PageData *data) : m_data(data) { Q_ASSERT(m_data); ::Page *popplerPage = m_data->page; m_pageCropWidth = popplerPage->getCropWidth(); m_pageCropHeight = popplerPage->getCropHeight(); if (popplerPage->getRotate() == 90 || popplerPage->getRotate() == 270) { qSwap(m_pageCropWidth, m_pageCropHeight); } GfxState gfxState(72.0, 72.0, popplerPage->getCropBox(), popplerPage->getRotate(), true); setDefaultCTM(gfxState.getCTM()); } LinkExtractorOutputDev::~LinkExtractorOutputDev() { qDeleteAll(m_links); } void LinkExtractorOutputDev::processLink(::AnnotLink *link) { if (!link->isOk()) { return; } double left, top, right, bottom; int leftAux, topAux, rightAux, bottomAux; link->getRect(&left, &top, &right, &bottom); QRectF linkArea; cvtUserToDev(left, top, &leftAux, &topAux); cvtUserToDev(right, bottom, &rightAux, &bottomAux); linkArea.setLeft((double)leftAux / m_pageCropWidth); linkArea.setTop((double)topAux / m_pageCropHeight); linkArea.setRight((double)rightAux / m_pageCropWidth); linkArea.setBottom((double)bottomAux / m_pageCropHeight); Link *popplerLink = m_data->convertLinkActionToLink(link->getAction(), linkArea); if (popplerLink) { m_links.append(popplerLink); } OutputDev::processLink(link); } QList LinkExtractorOutputDev::links() { QList ret = m_links; m_links.clear(); return ret; } } poppler-24.02.0/qt5/src/poppler-link-private.h000066400000000000000000000040531455701731300211050ustar00rootroot00000000000000/* poppler-link-private.h: qt interface to poppler * Copyright (C) 2016, 2018, 2020, 2021 Albert Astals Cid * Copyright (C) 2018 Intevation GmbH * Copyright (C) 2020 Oliver Sander * * 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_LINK_PRIVATE_H_ #define _POPPLER_LINK_PRIVATE_H_ #include #include "Link.h" class LinkOCGState; namespace Poppler { class Link; class LinkPrivate { public: explicit LinkPrivate(const QRectF &area) : linkArea(area) { } virtual ~LinkPrivate(); static LinkPrivate *get(Link *link) { return link->d_ptr; } LinkPrivate(const LinkPrivate &) = delete; LinkPrivate &operator=(const LinkPrivate &) = delete; QRectF linkArea; QVector nextLinks; }; class LinkOCGStatePrivate : public LinkPrivate { public: LinkOCGStatePrivate(const QRectF &area, const std::vector<::LinkOCGState::StateList> &sList, bool pRB) : LinkPrivate(area), stateList(sList), preserveRB(pRB) { } ~LinkOCGStatePrivate() override; std::vector<::LinkOCGState::StateList> stateList; bool preserveRB; }; class LinkHidePrivate : public LinkPrivate { public: LinkHidePrivate(const QRectF &area, const QString &tName, bool show) : LinkPrivate(area), targetName(tName), isShow(show) { } ~LinkHidePrivate() override; QString targetName; bool isShow; }; } #endif poppler-24.02.0/qt5/src/poppler-link.cc000066400000000000000000000417321455701731300176000ustar00rootroot00000000000000/* poppler-link.cc: qt interface to poppler * Copyright (C) 2006-2007, 2013, 2016-2021, Albert Astals Cid * Copyright (C) 2007-2008, Pino Toscano * Copyright (C) 2010 Hib Eris * Copyright (C) 2012, Tobias Koenig * Copyright (C) 2012, Guillermo A. Amaral B. * Copyright (C) 2018 Intevation GmbH * Copyright (C) 2018 Adam Reichold * Copyright (C) 2020 Oliver Sander * Adapting code from * Copyright (C) 2004 by Enrico Ros * * 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 #include #include #include "poppler-annotation-private.h" #include "Link.h" #include "Rendition.h" namespace Poppler { class LinkDestinationPrivate : public QSharedData { public: LinkDestinationPrivate(); LinkDestination::Kind kind; // destination type QString name; int pageNum; // page number double left, bottom; // position double right, top; double zoom; // zoom factor bool changeLeft : 1, changeTop : 1; // for destXYZ links, which position bool changeZoom : 1; // components to change }; LinkDestinationPrivate::LinkDestinationPrivate() { // sane defaults kind = LinkDestination::destXYZ; pageNum = 0; left = 0; bottom = 0; right = 0; top = 0; zoom = 1; changeLeft = true; changeTop = true; changeZoom = false; } LinkPrivate::~LinkPrivate() { qDeleteAll(nextLinks); } LinkOCGStatePrivate::~LinkOCGStatePrivate() = default; LinkHidePrivate::~LinkHidePrivate() = default; class LinkGotoPrivate : public LinkPrivate { public: LinkGotoPrivate(const QRectF &area, const LinkDestination &dest); ~LinkGotoPrivate() override; QString extFileName; LinkDestination destination; }; LinkGotoPrivate::LinkGotoPrivate(const QRectF &area, const LinkDestination &dest) : LinkPrivate(area), destination(dest) { } LinkGotoPrivate::~LinkGotoPrivate() = default; class LinkExecutePrivate : public LinkPrivate { public: explicit LinkExecutePrivate(const QRectF &area); ~LinkExecutePrivate() override; QString fileName; QString parameters; }; LinkExecutePrivate::LinkExecutePrivate(const QRectF &area) : LinkPrivate(area) { } LinkExecutePrivate::~LinkExecutePrivate() = default; class LinkBrowsePrivate : public LinkPrivate { public: explicit LinkBrowsePrivate(const QRectF &area); ~LinkBrowsePrivate() override; QString url; }; LinkBrowsePrivate::LinkBrowsePrivate(const QRectF &area) : LinkPrivate(area) { } LinkBrowsePrivate::~LinkBrowsePrivate() = default; class LinkActionPrivate : public LinkPrivate { public: explicit LinkActionPrivate(const QRectF &area); ~LinkActionPrivate() override; LinkAction::ActionType type; }; LinkActionPrivate::LinkActionPrivate(const QRectF &area) : LinkPrivate(area) { } LinkActionPrivate::~LinkActionPrivate() = default; class LinkSoundPrivate : public LinkPrivate { public: explicit LinkSoundPrivate(const QRectF &area); ~LinkSoundPrivate() override; double volume; bool sync : 1; bool repeat : 1; bool mix : 1; SoundObject *sound; }; LinkSoundPrivate::LinkSoundPrivate(const QRectF &area) : LinkPrivate(area), sound(nullptr) { } LinkSoundPrivate::~LinkSoundPrivate() { delete sound; } class LinkRenditionPrivate : public LinkPrivate { public: explicit LinkRenditionPrivate(const QRectF &area, ::MediaRendition *rendition, ::LinkRendition::RenditionOperation operation, const QString &script, const Ref ref); ~LinkRenditionPrivate() override; MediaRendition *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) { switch (operation) { case ::LinkRendition::NoRendition: action = LinkRendition::NoRendition; break; case ::LinkRendition::PlayRendition: action = LinkRendition::PlayRendition; break; case ::LinkRendition::StopRendition: action = LinkRendition::StopRendition; break; case ::LinkRendition::PauseRendition: action = LinkRendition::PauseRendition; break; case ::LinkRendition::ResumeRendition: action = LinkRendition::ResumeRendition; break; } } LinkRenditionPrivate::~LinkRenditionPrivate() { delete rendition; } class LinkJavaScriptPrivate : public LinkPrivate { public: explicit LinkJavaScriptPrivate(const QRectF &area); ~LinkJavaScriptPrivate() override; QString js; }; LinkJavaScriptPrivate::LinkJavaScriptPrivate(const QRectF &area) : LinkPrivate(area) { } LinkJavaScriptPrivate::~LinkJavaScriptPrivate() = default; class LinkMoviePrivate : public LinkPrivate { public: LinkMoviePrivate(const QRectF &area, LinkMovie::Operation operation, const QString &title, const Ref reference); ~LinkMoviePrivate() override; LinkMovie::Operation operation; QString annotationTitle; Ref annotationReference; }; LinkMoviePrivate::LinkMoviePrivate(const QRectF &area, LinkMovie::Operation _operation, const QString &title, const Ref reference) : LinkPrivate(area), operation(_operation), annotationTitle(title), annotationReference(reference) { } LinkMoviePrivate::~LinkMoviePrivate() = default; static void cvtUserToDev(::Page *page, double xu, double yu, int *xd, int *yd) { double ctm[6]; page->getDefaultCTM(ctm, 72.0, 72.0, 0, false, true); *xd = (int)(ctm[0] * xu + ctm[2] * yu + ctm[4] + 0.5); *yd = (int)(ctm[1] * xu + ctm[3] * yu + ctm[5] + 0.5); } LinkDestination::LinkDestination(const LinkDestinationData &data) : d(new LinkDestinationPrivate) { bool deleteDest = false; const LinkDest *ld = data.ld; if (data.namedDest && !ld && !data.externalDest) { deleteDest = true; ld = data.doc->doc->findDest(data.namedDest).release(); } // in case this destination was named one, and it was not resolved if (data.namedDest && !ld) { d->name = QString::fromLatin1(data.namedDest->c_str()); } if (!ld) { return; } if (ld->getKind() == ::destXYZ) { d->kind = destXYZ; } else if (ld->getKind() == ::destFit) { d->kind = destFit; } else if (ld->getKind() == ::destFitH) { d->kind = destFitH; } else if (ld->getKind() == ::destFitV) { d->kind = destFitV; } else if (ld->getKind() == ::destFitR) { d->kind = destFitR; } else if (ld->getKind() == ::destFitB) { d->kind = destFitB; } else if (ld->getKind() == ::destFitBH) { d->kind = destFitBH; } else if (ld->getKind() == ::destFitBV) { d->kind = destFitBV; } if (!ld->isPageRef()) { d->pageNum = ld->getPageNum(); } else { const Ref ref = ld->getPageRef(); d->pageNum = data.doc->doc->findPage(ref); } double left = ld->getLeft(); double bottom = ld->getBottom(); double right = ld->getRight(); double top = ld->getTop(); d->zoom = ld->getZoom(); d->changeLeft = ld->getChangeLeft(); d->changeTop = ld->getChangeTop(); d->changeZoom = ld->getChangeZoom(); int leftAux = 0, topAux = 0, rightAux = 0, bottomAux = 0; if (!data.externalDest) { ::Page *page; if (d->pageNum > 0 && d->pageNum <= data.doc->doc->getNumPages() && (page = data.doc->doc->getPage(d->pageNum))) { cvtUserToDev(page, left, top, &leftAux, &topAux); cvtUserToDev(page, right, bottom, &rightAux, &bottomAux); d->left = leftAux / (double)page->getCropWidth(); d->top = topAux / (double)page->getCropHeight(); d->right = rightAux / (double)page->getCropWidth(); d->bottom = bottomAux / (double)page->getCropHeight(); } else { d->pageNum = 0; } } if (deleteDest) { delete ld; } } LinkDestination::LinkDestination(const QString &description) : d(new LinkDestinationPrivate) { const QStringList tokens = description.split(';'); if (tokens.size() >= 10) { d->kind = static_cast(tokens.at(0).toInt()); d->pageNum = tokens.at(1).toInt(); d->left = tokens.at(2).toDouble(); d->bottom = tokens.at(3).toDouble(); d->right = tokens.at(4).toDouble(); d->top = tokens.at(5).toDouble(); d->zoom = tokens.at(6).toDouble(); d->changeLeft = static_cast(tokens.at(7).toInt()); d->changeTop = static_cast(tokens.at(8).toInt()); d->changeZoom = static_cast(tokens.at(9).toInt()); } } LinkDestination::LinkDestination(const LinkDestination &other) : d(other.d) { } LinkDestination::~LinkDestination() { } LinkDestination::Kind LinkDestination::kind() const { return d->kind; } int LinkDestination::pageNumber() const { return d->pageNum; } double LinkDestination::left() const { return d->left; } double LinkDestination::bottom() const { return d->bottom; } double LinkDestination::right() const { return d->right; } double LinkDestination::top() const { return d->top; } double LinkDestination::zoom() const { return d->zoom; } bool LinkDestination::isChangeLeft() const { return d->changeLeft; } bool LinkDestination::isChangeTop() const { return d->changeTop; } bool LinkDestination::isChangeZoom() const { return d->changeZoom; } 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); return s; } QString LinkDestination::destinationName() const { return d->name; } LinkDestination &LinkDestination::operator=(const LinkDestination &other) { if (this == &other) { return *this; } d = other.d; return *this; } // Link Link::~Link() { delete d_ptr; } Link::Link(const QRectF &linkArea) : d_ptr(new LinkPrivate(linkArea)) { } Link::Link(LinkPrivate &dd) : d_ptr(&dd) { } Link::LinkType Link::linkType() const { return None; } QRectF Link::linkArea() const { Q_D(const Link); return d->linkArea; } QVector Link::nextLinks() const { return d_ptr->nextLinks; } // LinkGoto LinkGoto::LinkGoto(const QRectF &linkArea, QString extFileName, const LinkDestination &destination) // clazy:exclude=function-args-by-ref : Link(*new LinkGotoPrivate(linkArea, destination)) { Q_D(LinkGoto); d->extFileName = std::move(extFileName); // TODO remove when extFileName moves to be a const & } LinkGoto::~LinkGoto() { } bool LinkGoto::isExternal() const { Q_D(const LinkGoto); return !d->extFileName.isEmpty(); } QString LinkGoto::fileName() const { Q_D(const LinkGoto); return d->extFileName; } LinkDestination LinkGoto::destination() const { Q_D(const LinkGoto); return d->destination; } Link::LinkType LinkGoto::linkType() const { return Goto; } // LinkExecute LinkExecute::LinkExecute(const QRectF &linkArea, const QString &file, const QString ¶ms) : Link(*new LinkExecutePrivate(linkArea)) { Q_D(LinkExecute); d->fileName = file; d->parameters = params; } LinkExecute::~LinkExecute() { } QString LinkExecute::fileName() const { Q_D(const LinkExecute); return d->fileName; } QString LinkExecute::parameters() const { Q_D(const LinkExecute); return d->parameters; } Link::LinkType LinkExecute::linkType() const { return Execute; } // LinkBrowse LinkBrowse::LinkBrowse(const QRectF &linkArea, const QString &url) : Link(*new LinkBrowsePrivate(linkArea)) { Q_D(LinkBrowse); d->url = url; } LinkBrowse::~LinkBrowse() { } QString LinkBrowse::url() const { Q_D(const LinkBrowse); return d->url; } Link::LinkType LinkBrowse::linkType() const { return Browse; } // LinkAction LinkAction::LinkAction(const QRectF &linkArea, ActionType actionType) : Link(*new LinkActionPrivate(linkArea)) { Q_D(LinkAction); d->type = actionType; } LinkAction::~LinkAction() { } LinkAction::ActionType LinkAction::actionType() const { Q_D(const LinkAction); return d->type; } Link::LinkType LinkAction::linkType() const { return Action; } // LinkSound LinkSound::LinkSound(const QRectF &linkArea, double volume, bool sync, bool repeat, bool mix, SoundObject *sound) : Link(*new LinkSoundPrivate(linkArea)) { Q_D(LinkSound); d->volume = volume; d->sync = sync; d->repeat = repeat; d->mix = mix; d->sound = sound; } LinkSound::~LinkSound() { } Link::LinkType LinkSound::linkType() const { return Sound; } double LinkSound::volume() const { Q_D(const LinkSound); return d->volume; } bool LinkSound::synchronous() const { Q_D(const LinkSound); return d->sync; } bool LinkSound::repeat() const { Q_D(const LinkSound); return d->repeat; } bool LinkSound::mix() const { Q_D(const LinkSound); return d->mix; } SoundObject *LinkSound::sound() const { Q_D(const LinkSound); return d->sound; } // 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::~LinkRendition() { } Link::LinkType LinkRendition::linkType() const { return Rendition; } MediaRendition *LinkRendition::rendition() const { Q_D(const LinkRendition); return d->rendition; } LinkRendition::RenditionAction LinkRendition::action() const { Q_D(const LinkRendition); return d->action; } QString LinkRendition::script() const { Q_D(const LinkRendition); return d->script; } bool LinkRendition::isReferencedAnnotation(const ScreenAnnotation *annotation) const { Q_D(const LinkRendition); if (d->annotationReference != Ref::INVALID() && d->annotationReference == annotation->d_ptr->pdfObjectReference()) { return true; } return false; } // LinkJavaScript LinkJavaScript::LinkJavaScript(const QRectF &linkArea, const QString &js) : Link(*new LinkJavaScriptPrivate(linkArea)) { Q_D(LinkJavaScript); d->js = js; } LinkJavaScript::~LinkJavaScript() { } Link::LinkType LinkJavaScript::linkType() const { return JavaScript; } QString LinkJavaScript::script() const { Q_D(const LinkJavaScript); return d->js; } // LinkMovie LinkMovie::LinkMovie(const QRectF &linkArea, Operation operation, const QString &annotationTitle, const Ref &annotationReference) // clazy:exclude=function-args-by-value : Link(*new LinkMoviePrivate(linkArea, operation, annotationTitle, annotationReference)) { } LinkMovie::~LinkMovie() { } Link::LinkType LinkMovie::linkType() const { return Movie; } LinkMovie::Operation LinkMovie::operation() const { Q_D(const LinkMovie); return d->operation; } bool LinkMovie::isReferencedAnnotation(const MovieAnnotation *annotation) const { Q_D(const LinkMovie); if (d->annotationReference != Ref::INVALID() && d->annotationReference == annotation->d_ptr->pdfObjectReference()) { return true; } else if (!d->annotationTitle.isNull()) { return (annotation->movieTitle() == d->annotationTitle); } return false; } LinkOCGState::LinkOCGState(LinkOCGStatePrivate *ocgp) : Link(*ocgp) { } LinkOCGState::~LinkOCGState() { } Link::LinkType LinkOCGState::linkType() const { return OCGState; } // LinkHide LinkHide::LinkHide(LinkHidePrivate *lhidep) : Link(*lhidep) { } LinkHide::~LinkHide() { } Link::LinkType LinkHide::linkType() const { return Hide; } QVector LinkHide::targets() const { Q_D(const LinkHide); return QVector() << d->targetName; } bool LinkHide::isShowAction() const { Q_D(const LinkHide); return d->isShow; } } poppler-24.02.0/qt5/src/poppler-link.h000066400000000000000000000421001455701731300174300ustar00rootroot00000000000000/* poppler-link.h: qt interface to poppler * Copyright (C) 2006, 2013, 2016, 2018, 2019, 2021, 2022, 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 * Adapting code from * Copyright (C) 2004 by Enrico Ros * * 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_LINK_H_ #define _POPPLER_LINK_H_ #include #include #include #include #include "poppler-export.h" struct Ref; class MediaRendition; namespace Poppler { class LinkPrivate; class LinkGotoPrivate; class LinkExecutePrivate; class LinkBrowsePrivate; class LinkActionPrivate; class LinkSoundPrivate; class LinkJavaScriptPrivate; class LinkMoviePrivate; class LinkDestinationData; class LinkDestinationPrivate; class LinkRenditionPrivate; class LinkOCGStatePrivate; class LinkHidePrivate; class MediaRendition; class MovieAnnotation; class ScreenAnnotation; class SoundObject; /** * \short A destination. * * The LinkDestination class represent a "destination" (in terms of visual * viewport to be displayed) for \link Poppler::LinkGoto GoTo\endlink links, * and items in the table of contents (TOC) of a document. * * Coordinates are in 0..1 range */ class POPPLER_QT5_EXPORT LinkDestination { public: /** * The possible kind of "viewport destination". */ enum Kind { /** * The new viewport is specified in terms of: * - possible new left coordinate (see isChangeLeft() ) * - possible new top coordinate (see isChangeTop() ) * - possible new zoom level (see isChangeZoom() ) */ destXYZ = 1, destFit = 2, destFitH = 3, destFitV = 4, destFitR = 5, destFitB = 6, destFitBH = 7, destFitBV = 8 }; /// \cond PRIVATE explicit LinkDestination(const LinkDestinationData &data); explicit LinkDestination(const QString &description); /// \endcond /** * Copy constructor. */ LinkDestination(const LinkDestination &other); /** * Destructor. */ ~LinkDestination(); // Accessors. /** * The kind of destination. */ Kind kind() const; /** * Which page is the target of this destination. * * \note this number is 1-based, so for a 5 pages document the * valid page numbers go from 1 to 5 (both included). */ int pageNumber() const; /** * The new left for the viewport of the target page, in case * it is specified to be changed (see isChangeLeft() ) */ double left() const; double bottom() const; double right() const; /** * The new top for the viewport of the target page, in case * it is specified to be changed (see isChangeTop() ) */ double top() const; double zoom() const; /** * Whether the left of the viewport on the target page should * be changed. * * \see left() */ bool isChangeLeft() const; /** * Whether the top of the viewport on the target page should * be changed. * * \see top() */ bool isChangeTop() const; /** * Whether the zoom level should be changed. * * \see zoom() */ bool isChangeZoom() const; /** * Return a string repesentation of this destination. */ QString toString() const; /** * Return the name of this destination. * * \since 0.12 */ QString destinationName() const; /** * Assignment operator. */ LinkDestination &operator=(const LinkDestination &other); private: QSharedDataPointer d; }; /** * \short Encapsulates data that describes a link. * * This is the base class for links. It makes mandatory for inherited * kind of links to reimplement the linkType() method and return the type of * the link described by the reimplemented class. */ class POPPLER_QT5_EXPORT Link { public: /// \cond PRIVATE explicit Link(const QRectF &linkArea); /// \endcond /** * The possible kinds of link. * * Inherited classes must return an unique identifier */ enum LinkType { None, ///< Unknown link Goto, ///< A "Go To" link Execute, ///< A command to be executed Browse, ///< An URL to be browsed (eg "http://poppler.freedesktop.org") Action, ///< A "standard" action to be executed in the viewer Sound, ///< A link representing a sound to be played Movie, ///< An action to be executed on a movie Rendition, ///< A rendition link \since 0.20 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 }; /** * The type of this link. */ virtual LinkType linkType() const; /** * Destructor. */ virtual ~Link(); /** * The area of a Page where the link should be active. * * \note this can be a null rect, in this case the link represents * a general action. The area is given in 0..1 range */ QRectF linkArea() const; /** * Get the next links to be activated / executed after this link. * * \since 0.64 */ QVector nextLinks() const; protected: /// \cond PRIVATE explicit Link(LinkPrivate &dd); Q_DECLARE_PRIVATE(Link) LinkPrivate *d_ptr; /// \endcond private: Q_DISABLE_COPY(Link) }; /** * \brief Viewport reaching request. * * With a LinkGoto link, the document requests the specified viewport to be * reached (aka, displayed in a viewer). Furthermore, if a file name is specified, * then the destination refers to that document (and not to the document the * current LinkGoto belongs to). */ class POPPLER_QT5_EXPORT LinkGoto : public Link { public: /** * Create a new Goto link. * * \param linkArea the active area of the link * \param extFileName if not empty, the file name to be open * \param destination the destination to be reached */ // TODO Next ABI break, make extFileName const & LinkGoto(const QRectF &linkArea, QString extFileName, const LinkDestination &destination); /** * Destructor. */ ~LinkGoto() override; /** * Whether the destination is in an external document * (i.e. not the current document) */ bool isExternal() const; // query for goto parameters /** * The file name of the document the destination() refers to, * or an empty string in case it refers to the current document. */ QString fileName() const; /** * The destination to reach. */ LinkDestination destination() const; LinkType linkType() const override; private: Q_DECLARE_PRIVATE(LinkGoto) Q_DISABLE_COPY(LinkGoto) }; /** * \brief Generic execution request. * * The LinkExecute link represent a "file name" execution request. The result * depends on the \link fileName() file name\endlink: * - if it is a document, then it is requested to be open * - otherwise, it represents an executable to be run with the specified parameters */ class POPPLER_QT5_EXPORT LinkExecute : public Link { public: /** * The file name to be executed */ QString fileName() const; /** * The parameters for the command. */ QString parameters() const; /** * Create a new Execute link. * * \param linkArea the active area of the link * \param file the file name to be open, or the program to be execute * \param params the parameters for the program to execute */ LinkExecute(const QRectF &linkArea, const QString &file, const QString ¶ms); /** * Destructor. */ ~LinkExecute() override; LinkType linkType() const override; private: Q_DECLARE_PRIVATE(LinkExecute) Q_DISABLE_COPY(LinkExecute) }; /** * \brief An URL to browse. * * The LinkBrowse link holds a URL (eg 'http://poppler.freedesktop.org', * 'mailto:john@some.org', etc) to be open. * * The format of the URL is specified by RFC 2396 (http://www.ietf.org/rfc/rfc2396.txt) */ class POPPLER_QT5_EXPORT LinkBrowse : public Link { public: /** * The URL to open */ QString url() const; /** * Create a new browse link. * * \param linkArea the active area of the link * \param url the URL to be open */ LinkBrowse(const QRectF &linkArea, const QString &url); /** * Destructor. */ ~LinkBrowse() override; LinkType linkType() const override; private: Q_DECLARE_PRIVATE(LinkBrowse) Q_DISABLE_COPY(LinkBrowse) }; /** * \brief "Standard" action request. * * The LinkAction class represents a link that request a "standard" action * to be performed by the viewer on the displayed document. */ class POPPLER_QT5_EXPORT LinkAction : public Link { public: /** * The possible types of actions */ enum ActionType { PageFirst = 1, PagePrev = 2, PageNext = 3, PageLast = 4, HistoryBack = 5, HistoryForward = 6, Quit = 7, Presentation = 8, EndPresentation = 9, Find = 10, GoToPage = 11, Close = 12, Print = 13, ///< \since 0.16 SaveAs = 14 ///< \since 22.04 }; /** * The action of the current LinkAction */ ActionType actionType() const; /** * Create a new Action link, that executes a specified action * on the document. * * \param linkArea the active area of the link * \param actionType which action should be executed */ LinkAction(const QRectF &linkArea, ActionType actionType); /** * Destructor. */ ~LinkAction() override; LinkType linkType() const override; private: Q_DECLARE_PRIVATE(LinkAction) Q_DISABLE_COPY(LinkAction) }; /** * Sound: a sound to be played. * * \since 0.6 */ class POPPLER_QT5_EXPORT LinkSound : public Link { public: // create a Link_Sound LinkSound(const QRectF &linkArea, double volume, bool sync, bool repeat, bool mix, SoundObject *sound); /** * Destructor. */ ~LinkSound() override; LinkType linkType() const override; /** * The volume to be used when playing the sound. * * The volume is in the range [ -1, 1 ], where: * - a negative number: no volume (mute) * - 1: full volume */ double volume() const; /** * Whether the playback of the sound should be synchronous * (thus blocking, waiting for the end of the sound playback). */ bool synchronous() const; /** * Whether the sound should be played continuously (that is, * started again when it ends) */ bool repeat() const; /** * Whether the playback of this sound can be mixed with * playbacks with other sounds of the same document. * * \note When false, any other playback must be stopped before * playing the sound. */ bool mix() const; /** * The sound object to be played */ SoundObject *sound() const; private: Q_DECLARE_PRIVATE(LinkSound) Q_DISABLE_COPY(LinkSound) }; /** * Rendition: Rendition link. * * \since 0.20 */ class POPPLER_QT5_EXPORT LinkRendition : public Link { public: /** * Describes the possible rendition actions. * * \since 0.22 */ enum RenditionAction { NoRendition, PlayRendition, StopRendition, PauseRendition, ResumeRendition }; /** * Create a new rendition link. * * \param linkArea the active area of the link * \param rendition the media rendition object. Ownership is taken * \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 * \since 0.22 */ // TODO Next ABI break, remove & from annotationReference LinkRendition(const QRectF &linkArea, ::MediaRendition *rendition, int operation, const QString &script, const Ref &annotationReference); /** * Destructor. */ ~LinkRendition() override; LinkType linkType() const override; /** * Returns the media rendition object if the redition provides one, @c 0 otherwise */ MediaRendition *rendition() const; /** * Returns the action that should be executed if a rendition object is provided. * * \since 0.22 */ RenditionAction action() const; /** * The JS code that shall be executed or an empty string. * * \since 0.22 */ QString script() const; /** * Returns whether the given @p annotation is the referenced screen annotation for this rendition @p link. * * \since 0.22 */ bool isReferencedAnnotation(const ScreenAnnotation *annotation) const; private: Q_DECLARE_PRIVATE(LinkRendition) Q_DISABLE_COPY(LinkRendition) }; /** * JavaScript: a JavaScript code to be interpreted. * * \since 0.10 */ class POPPLER_QT5_EXPORT LinkJavaScript : public Link { public: /** * Create a new JavaScript link. * * \param linkArea the active area of the link * \param js the JS code to be interpreted */ LinkJavaScript(const QRectF &linkArea, const QString &js); /** * Destructor. */ ~LinkJavaScript() override; LinkType linkType() const override; /** * The JS code */ QString script() const; private: Q_DECLARE_PRIVATE(LinkJavaScript) Q_DISABLE_COPY(LinkJavaScript) }; /** * Movie: a movie to be played. * * \since 0.20 */ class POPPLER_QT5_EXPORT LinkMovie : public Link { public: /** * Describes the operation to be performed on the movie. */ enum Operation { Play, Stop, Pause, Resume }; /** * Create a new Movie link. * * \param linkArea the active area of the link * \param operation the operation to be performed on the movie * \param annotationTitle the title of the movie annotation identifying the movie to be played * \param annotationReference the object reference of the movie annotation identifying the movie to be played * * Note: This constructor is supposed to be used by Poppler::Page only. */ // TODO Next ABI break, remove & from annotationReference LinkMovie(const QRectF &linkArea, Operation operation, const QString &annotationTitle, const Ref &annotationReference); /** * Destructor. */ ~LinkMovie() override; LinkType linkType() const override; /** * Returns the operation to be performed on the movie. */ Operation operation() const; /** * Returns whether the given @p annotation is the referenced movie annotation for this movie @p link. */ bool isReferencedAnnotation(const MovieAnnotation *annotation) const; private: Q_DECLARE_PRIVATE(LinkMovie) Q_DISABLE_COPY(LinkMovie) }; /** * OCGState: an optional content group state change. * * \since 0.50 */ class POPPLER_QT5_EXPORT LinkOCGState : public Link { friend class OptContentModel; public: /** * Create a new OCGState link. This is only used by Poppler::Page. */ explicit LinkOCGState(LinkOCGStatePrivate *ocgp); /** * Destructor. */ ~LinkOCGState() override; LinkType linkType() const override; private: Q_DECLARE_PRIVATE(LinkOCGState) Q_DISABLE_COPY(LinkOCGState) }; /** * Hide: an action to show / hide a field. * * \since 0.64 */ class POPPLER_QT5_EXPORT LinkHide : public Link { public: /** * Create a new Hide link. This is only used by Poppler::Page. */ explicit LinkHide(LinkHidePrivate *lhidep); /** * Destructor. */ ~LinkHide() override; LinkType linkType() const override; /** * The fully qualified target names of the action. */ QVector targets() const; /** * Should this action change the visibility of the target to true. */ bool isShowAction() const; private: Q_DECLARE_PRIVATE(LinkHide) Q_DISABLE_COPY(LinkHide) }; } #endif poppler-24.02.0/qt5/src/poppler-media.cc000066400000000000000000000105311455701731300177130ustar00rootroot00000000000000/* poppler-media.cc: qt interface to poppler * Copyright (C) 2012 Guillermo A. Amaral B. * Copyright (C) 2013, 2018, 2021 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 * 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 "poppler-media.h" #include "Rendition.h" #include "poppler-private.h" #include #define BUFFER_MAX 4096 namespace Poppler { class MediaRenditionPrivate { public: explicit MediaRenditionPrivate(::MediaRendition *renditionA) : rendition(renditionA) { } ~MediaRenditionPrivate() { delete rendition; } MediaRenditionPrivate(const MediaRenditionPrivate &) = delete; MediaRenditionPrivate &operator=(const MediaRenditionPrivate &) = delete; ::MediaRendition *rendition; }; MediaRendition::MediaRendition(::MediaRendition *rendition) : d_ptr(new MediaRenditionPrivate(rendition)) { } MediaRendition::~MediaRendition() { delete d_ptr; } bool MediaRendition::isValid() const { Q_D(const MediaRendition); return d->rendition && d->rendition->isOk(); } QString MediaRendition::contentType() const { Q_ASSERT(isValid() && "Invalid media rendition."); Q_D(const MediaRendition); return UnicodeParsedString(d->rendition->getContentType()); } QString MediaRendition::fileName() const { Q_ASSERT(isValid() && "Invalid media rendition."); Q_D(const MediaRendition); return UnicodeParsedString(d->rendition->getFileName()); } bool MediaRendition::isEmbedded() const { Q_ASSERT(isValid() && "Invalid media rendition."); Q_D(const MediaRendition); return d->rendition->getIsEmbedded(); } QByteArray MediaRendition::data() const { Q_ASSERT(isValid() && "Invalid media rendition."); Q_D(const MediaRendition); Stream *s = d->rendition->getEmbbededStream(); if (!s) { 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); } buffer.close(); return buffer.data(); } bool MediaRendition::autoPlay() const { Q_D(const MediaRendition); if (d->rendition->getBEParameters()) { return d->rendition->getBEParameters()->autoPlay; } else if (d->rendition->getMHParameters()) { return d->rendition->getMHParameters()->autoPlay; } else { qDebug("No BE or MH parameters to reference!"); } return false; } bool MediaRendition::showControls() const { Q_D(const MediaRendition); if (d->rendition->getBEParameters()) { return d->rendition->getBEParameters()->showControls; } else if (d->rendition->getMHParameters()) { return d->rendition->getMHParameters()->showControls; } else { qDebug("No BE or MH parameters to reference!"); } return false; } float MediaRendition::repeatCount() const { Q_D(const MediaRendition); if (d->rendition->getBEParameters()) { return d->rendition->getBEParameters()->repeatCount; } else if (d->rendition->getMHParameters()) { return d->rendition->getMHParameters()->repeatCount; } else { qDebug("No BE or MH parameters to reference!"); } return 1.f; } QSize MediaRendition::size() const { Q_D(const MediaRendition); const MediaParameters *mp = nullptr; if (d->rendition->getBEParameters()) { mp = d->rendition->getBEParameters(); } else if (d->rendition->getMHParameters()) { mp = d->rendition->getMHParameters(); } else { qDebug("No BE or MH parameters to reference!"); } if (mp) { return QSize(mp->windowParams.width, mp->windowParams.height); } return QSize(); } } poppler-24.02.0/qt5/src/poppler-media.h000066400000000000000000000045131455701731300175600ustar00rootroot00000000000000/* poppler-media.h: qt interface to poppler * Copyright (C) 2012 Guillermo A. Amaral B. * Copyright (C) 2012, 2013, 2021 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 * 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_MEDIARENDITION_H__ #define __POPPLER_MEDIARENDITION_H__ #include "poppler-export.h" #include #include class MediaRendition; class QIODevice; namespace Poppler { class MediaRenditionPrivate; /** Qt wrapper for MediaRendition. \since 0.20 */ class POPPLER_QT5_EXPORT MediaRendition { public: /** Constructs a MediaRendition. Takes ownership of the passed rendition */ explicit MediaRendition(::MediaRendition *rendition); ~MediaRendition(); /** Check if wrapper is holding a valid rendition object. */ bool isValid() const; /** Returns content type. */ QString contentType() const; /** Returns file name. */ QString fileName() const; /** Returns true if media is embedded. */ bool isEmbedded() const; /** Returns data buffer. */ QByteArray data() const; /** Convenience accessor for auto-play parameter. */ bool autoPlay() const; /** Convenience accessor for show controls parameter. */ bool showControls() const; /** Convenience accessor for repeat count parameter. */ float repeatCount() const; /** Convenience accessor for size parameter. */ QSize size() const; private: Q_DECLARE_PRIVATE(MediaRendition) MediaRenditionPrivate *d_ptr; Q_DISABLE_COPY(MediaRendition) }; } #endif /* __POPPLER_MEDIARENDITION_H__ */ poppler-24.02.0/qt5/src/poppler-movie.cc000066400000000000000000000054351455701731300177620ustar00rootroot00000000000000/* poppler-sound.cc: qt interface to poppler * Copyright (C) 2008, 2010, Pino Toscano * Copyright (C) 2008, 2018, 2022, Albert Astals Cid * Copyright (C) 2010, Carlos Garcia Campos * Copyright (C) 2012, Tobias Koenig * * 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 "poppler-qt5.h" #include "Object.h" #include "Annot.h" #include "Movie.h" #include namespace Poppler { class MovieData { public: MovieData() : m_movieObj(nullptr) { } ~MovieData() = default; MovieData(const MovieData &) = delete; MovieData &operator=(const MovieData &) = delete; std::unique_ptr m_movieObj; QSize m_size; int m_rotation; QImage m_posterImage; MovieObject::PlayMode m_playMode : 3; bool m_showControls : 1; }; MovieObject::MovieObject(AnnotMovie *ann) { m_movieData = new MovieData(); m_movieData->m_movieObj = ann->getMovie()->copy(); // TODO: copy poster image const MovieActivationParameters *mp = m_movieData->m_movieObj->getActivationParameters(); int width, height; m_movieData->m_movieObj->getFloatingWindowSize(&width, &height); m_movieData->m_size = QSize(width, height); m_movieData->m_rotation = m_movieData->m_movieObj->getRotationAngle(); m_movieData->m_showControls = mp->showControls; m_movieData->m_playMode = (MovieObject::PlayMode)mp->repeatMode; } MovieObject::~MovieObject() { delete m_movieData; } QString MovieObject::url() const { const GooString *goo = m_movieData->m_movieObj->getFileName(); return goo ? QString(goo->c_str()) : QString(); } QSize MovieObject::size() const { return m_movieData->m_size; } int MovieObject::rotation() const { return m_movieData->m_rotation; } bool MovieObject::showControls() const { return m_movieData->m_showControls; } MovieObject::PlayMode MovieObject::playMode() const { return m_movieData->m_playMode; } bool MovieObject::showPosterImage() const { return (m_movieData->m_movieObj->getShowPoster() == true); } QImage MovieObject::posterImage() const { return m_movieData->m_posterImage; } } poppler-24.02.0/qt5/src/poppler-optcontent-private.h000066400000000000000000000076611455701731300223550ustar00rootroot00000000000000/* poppler-optcontent-private.h: qt interface to poppler * * Copyright (C) 2007, Brad Hards * Copyright (C) 2008, Pino Toscano * Copyright (C) 2016, 2018, 2019, 2021, Albert Astals Cid * Copyright (C) 2017, Hubert Figuière * * 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_OPTCONTENT_PRIVATE_H #define POPPLER_OPTCONTENT_PRIVATE_H #include #include #include class Array; class OCGs; class OptionalContentGroup; class QModelIndex; namespace Poppler { class OptContentItem; class OptContentModel; class OptContentModelPrivate; class RadioButtonGroup { public: RadioButtonGroup(OptContentModelPrivate *ocModel, Array *rbarray); ~RadioButtonGroup(); QSet setItemOn(OptContentItem *itemToSetOn); private: QList itemsInGroup; }; class OptContentItem { public: enum ItemState { On, Off, HeadingOnly }; explicit OptContentItem(OptionalContentGroup *group); explicit OptContentItem(const QString &label); OptContentItem(); ~OptContentItem(); QString name() const { return m_name; } ItemState state() const { return m_stateBackup; } void setState(ItemState state, bool obeyRadioGroups, QSet &changedItems); QList childList() { return m_children; } void setParent(OptContentItem *parent) { m_parent = parent; } OptContentItem *parent() { return m_parent; } void addChild(OptContentItem *child); void appendRBGroup(RadioButtonGroup *rbgroup); bool isEnabled() const { return m_enabled; } QSet recurseListChildren(bool includeMe = false) const; OptionalContentGroup *group() const { return m_group; } private: OptionalContentGroup *m_group; QString m_name; ItemState m_state; // true for ON, false for OFF ItemState m_stateBackup; QList m_children; OptContentItem *m_parent; QList m_rbGroups; bool m_enabled; }; class OptContentModelPrivate { public: OptContentModelPrivate(OptContentModel *qq, OCGs *optContent); ~OptContentModelPrivate(); OptContentModelPrivate(const OptContentModelPrivate &) = delete; OptContentModelPrivate &operator=(const OptContentModelPrivate &) = delete; void parseRBGroupsArray(Array *rBGroupArray); OptContentItem *nodeFromIndex(const QModelIndex &index, bool canBeNull = false) const; QModelIndex indexFromItem(OptContentItem *node, int column) const; /** Get the OptContentItem corresponding to a given reference value. \param ref the reference number (e.g. from Object.getRefNum()) to look up \return the matching optional content item, or null if the reference wasn't found */ OptContentItem *itemFromRef(const QString &ref) const; void setRootNode(OptContentItem *node); OptContentModel *q; QMap m_optContentItems; QList m_headerOptContentItems; QList m_rbgroups; OptContentItem *m_rootNode; private: void addChild(OptContentItem *parent, OptContentItem *child); void parseOrderArray(OptContentItem *parentNode, Array *orderArray); }; } #endif poppler-24.02.0/qt5/src/poppler-optcontent.cc000066400000000000000000000330301455701731300210300ustar00rootroot00000000000000/* poppler-optcontent.cc: qt interface to poppler * * Copyright (C) 2007, Brad Hards * Copyright (C) 2008, 2014, Pino Toscano * Copyright (C) 2008, Carlos Garcia Campos * Copyright (C) 2015-2019, 2022, Albert Astals Cid * Copyright (C) 2017, Hubert Figuière * 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 * * 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 "poppler-optcontent.h" #include "poppler-optcontent-private.h" #include "poppler-private.h" #include "poppler-link-private.h" #include #include #include "poppler/OptionalContent.h" #include "poppler/Link.h" namespace Poppler { RadioButtonGroup::RadioButtonGroup(OptContentModelPrivate *ocModel, Array *rbarray) { itemsInGroup.reserve(rbarray->getLength()); for (int i = 0; i < rbarray->getLength(); ++i) { const Object &ref = rbarray->getNF(i); if (!ref.isRef()) { qDebug() << "expected ref, but got:" << ref.getType(); } OptContentItem *item = ocModel->itemFromRef(QString::number(ref.getRefNum())); itemsInGroup.append(item); } for (OptContentItem *item : std::as_const(itemsInGroup)) { item->appendRBGroup(this); } } RadioButtonGroup::~RadioButtonGroup() { } QSet RadioButtonGroup::setItemOn(OptContentItem *itemToSetOn) { QSet changedItems; for (OptContentItem *thisItem : std::as_const(itemsInGroup)) { if (thisItem != itemToSetOn) { QSet newChangedItems; thisItem->setState(OptContentItem::Off, false /*obeyRadioGroups*/, newChangedItems); changedItems += newChangedItems; } } return changedItems; } OptContentItem::OptContentItem(OptionalContentGroup *group) { m_group = group; m_parent = nullptr; m_name = UnicodeParsedString(group->getName()); if (group->getState() == OptionalContentGroup::On) { m_state = OptContentItem::On; } else { m_state = OptContentItem::Off; } m_stateBackup = m_state; m_enabled = true; } OptContentItem::OptContentItem(const QString &label) { m_parent = nullptr; m_name = label; m_group = nullptr; m_state = OptContentItem::HeadingOnly; m_stateBackup = m_state; m_enabled = true; } OptContentItem::OptContentItem() : m_parent(nullptr), m_enabled(true) { } OptContentItem::~OptContentItem() { } void OptContentItem::appendRBGroup(RadioButtonGroup *rbgroup) { m_rbGroups.append(rbgroup); } void OptContentItem::setState(ItemState state, bool obeyRadioGroups, QSet &changedItems) { if (state == m_state) { return; } m_state = state; m_stateBackup = m_state; changedItems.insert(this); QSet empty; Q_FOREACH (OptContentItem *child, m_children) { ItemState oldState = child->m_stateBackup; child->setState(state == OptContentItem::On ? child->m_stateBackup : OptContentItem::Off, true /*obeyRadioGroups*/, empty); child->m_enabled = state == OptContentItem::On; child->m_stateBackup = oldState; } if (!m_group) { return; } if (state == OptContentItem::On) { m_group->setState(OptionalContentGroup::On); if (obeyRadioGroups) { for (RadioButtonGroup *rbgroup : std::as_const(m_rbGroups)) { changedItems += rbgroup->setItemOn(this); } } } else if (state == OptContentItem::Off) { m_group->setState(OptionalContentGroup::Off); } } void OptContentItem::addChild(OptContentItem *child) { m_children += child; child->setParent(this); } QSet OptContentItem::recurseListChildren(bool includeMe) const { QSet ret; if (includeMe) { ret.insert(const_cast(this)); } Q_FOREACH (OptContentItem *child, m_children) { ret += child->recurseListChildren(true); } return ret; } OptContentModelPrivate::OptContentModelPrivate(OptContentModel *qq, OCGs *optContent) : q(qq) { m_rootNode = new OptContentItem(); const auto &ocgs = optContent->getOCGs(); for (const auto &ocg : ocgs) { OptContentItem *node = new OptContentItem(ocg.second.get()); m_optContentItems.insert(QString::number(ocg.first.num), node); } if (optContent->getOrderArray() == nullptr) { // no Order array, so drop them all at the top level QMapIterator i(m_optContentItems); while (i.hasNext()) { i.next(); addChild(m_rootNode, i.value()); } } else { parseOrderArray(m_rootNode, optContent->getOrderArray()); } parseRBGroupsArray(optContent->getRBGroupsArray()); } OptContentModelPrivate::~OptContentModelPrivate() { qDeleteAll(m_optContentItems); qDeleteAll(m_rbgroups); qDeleteAll(m_headerOptContentItems); delete m_rootNode; } void OptContentModelPrivate::parseOrderArray(OptContentItem *parentNode, Array *orderArray) { OptContentItem *lastItem = parentNode; for (int i = 0; i < orderArray->getLength(); ++i) { Object orderItem = orderArray->get(i); if (orderItem.isDict()) { const Object &item = orderArray->getNF(i); if (item.isRef()) { OptContentItem *ocItem = m_optContentItems.value(QString::number(item.getRefNum())); if (ocItem) { addChild(parentNode, ocItem); lastItem = ocItem; } else { qDebug() << "could not find group for object" << item.getRefNum(); } } } else if ((orderItem.isArray()) && (orderItem.arrayGetLength() > 0)) { parseOrderArray(lastItem, orderItem.getArray()); } else if (orderItem.isString()) { const GooString *label = orderItem.getString(); OptContentItem *header = new OptContentItem(UnicodeParsedString(label)); m_headerOptContentItems.append(header); addChild(parentNode, header); parentNode = header; lastItem = header; } else { qDebug() << "something unexpected"; } } } void OptContentModelPrivate::parseRBGroupsArray(Array *rBGroupArray) { if (!rBGroupArray) { return; } // This is an array of array(s) for (int i = 0; i < rBGroupArray->getLength(); ++i) { Object rbObj = rBGroupArray->get(i); if (!rbObj.isArray()) { qDebug() << "expected inner array, got:" << rbObj.getType(); return; } Array *rbarray = rbObj.getArray(); RadioButtonGroup *rbg = new RadioButtonGroup(this, rbarray); m_rbgroups.append(rbg); } } OptContentModel::OptContentModel(OCGs *optContent, QObject *parent) : QAbstractItemModel(parent) { d = new OptContentModelPrivate(this, optContent); } OptContentModel::~OptContentModel() { delete d; } void OptContentModelPrivate::setRootNode(OptContentItem *node) { q->beginResetModel(); delete m_rootNode; m_rootNode = node; q->endResetModel(); } QModelIndex OptContentModel::index(int row, int column, const QModelIndex &parent) const { if (row < 0 || column != 0) { return QModelIndex(); } OptContentItem *parentNode = d->nodeFromIndex(parent); if (row < parentNode->childList().count()) { return createIndex(row, column, parentNode->childList().at(row)); } return QModelIndex(); } QModelIndex OptContentModel::parent(const QModelIndex &child) const { OptContentItem *childNode = d->nodeFromIndex(child); if (!childNode) { return QModelIndex(); } return d->indexFromItem(childNode->parent(), child.column()); } QModelIndex OptContentModelPrivate::indexFromItem(OptContentItem *node, int column) const { if (!node) { return QModelIndex(); } OptContentItem *parentNode = node->parent(); if (!parentNode) { return QModelIndex(); } const int row = parentNode->childList().indexOf(node); return q->createIndex(row, column, node); } int OptContentModel::rowCount(const QModelIndex &parent) const { OptContentItem *parentNode = d->nodeFromIndex(parent); if (!parentNode) { return 0; } else { return parentNode->childList().count(); } } int OptContentModel::columnCount(const QModelIndex &parent) const { return 1; } QVariant OptContentModel::data(const QModelIndex &index, int role) const { OptContentItem *node = d->nodeFromIndex(index, true); if (!node) { return QVariant(); } switch (role) { case Qt::DisplayRole: return node->name(); break; case Qt::EditRole: if (node->state() == OptContentItem::On) { return true; } else if (node->state() == OptContentItem::Off) { return false; } break; case Qt::CheckStateRole: if (node->state() == OptContentItem::On) { return Qt::Checked; } else if (node->state() == OptContentItem::Off) { return Qt::Unchecked; } break; } return QVariant(); } bool OptContentModel::setData(const QModelIndex &index, const QVariant &value, int role) { OptContentItem *node = d->nodeFromIndex(index, true); if (!node) { return false; } switch (role) { case Qt::CheckStateRole: { const bool newvalue = value.toBool(); QSet changedItems; node->setState(newvalue ? OptContentItem::On : OptContentItem::Off, true /*obeyRadioGroups*/, changedItems); if (!changedItems.isEmpty()) { changedItems += node->recurseListChildren(false); QModelIndexList indexes; Q_FOREACH (OptContentItem *item, changedItems) { indexes.append(d->indexFromItem(item, 0)); } std::stable_sort(indexes.begin(), indexes.end()); Q_FOREACH (const QModelIndex &changedIndex, indexes) { emit dataChanged(changedIndex, changedIndex); } return true; } break; } } return false; } Qt::ItemFlags OptContentModel::flags(const QModelIndex &index) const { OptContentItem *node = d->nodeFromIndex(index); Qt::ItemFlags itemFlags = Qt::ItemIsSelectable | Qt::ItemIsUserCheckable; if (node->isEnabled()) { itemFlags |= Qt::ItemIsEnabled; } return itemFlags; } QVariant OptContentModel::headerData(int section, Qt::Orientation orientation, int role) const { return QAbstractItemModel::headerData(section, orientation, role); } void OptContentModel::applyLink(LinkOCGState *link) { LinkOCGStatePrivate *linkPrivate = link->d_func(); QSet changedItems; const std::vector<::LinkOCGState::StateList> &statesList = linkPrivate->stateList; for (const ::LinkOCGState::StateList &stateList : statesList) { const std::vector &refsList = stateList.list; for (const Ref &ref : refsList) { OptContentItem *item = d->itemFromRef(QString::number(ref.num)); if (stateList.st == ::LinkOCGState::On) { item->setState(OptContentItem::On, linkPrivate->preserveRB, changedItems); } else if (stateList.st == ::LinkOCGState::Off) { item->setState(OptContentItem::Off, linkPrivate->preserveRB, changedItems); } else { OptContentItem::ItemState newState = item->state() == OptContentItem::On ? OptContentItem::Off : OptContentItem::On; item->setState(newState, linkPrivate->preserveRB, changedItems); } } } if (!changedItems.isEmpty()) { QSet aux; Q_FOREACH (OptContentItem *item, aux) { changedItems += item->recurseListChildren(false); } QModelIndexList indexes; Q_FOREACH (OptContentItem *item, changedItems) { indexes.append(d->indexFromItem(item, 0)); } std::stable_sort(indexes.begin(), indexes.end()); Q_FOREACH (const QModelIndex &changedIndex, indexes) { emit dataChanged(changedIndex, changedIndex); } } } void OptContentModelPrivate::addChild(OptContentItem *parent, OptContentItem *child) { parent->addChild(child); } OptContentItem *OptContentModelPrivate::itemFromRef(const QString &ref) const { return m_optContentItems.value(ref); } OptContentItem *OptContentModelPrivate::nodeFromIndex(const QModelIndex &index, bool canBeNull) const { if (index.isValid()) { return static_cast(index.internalPointer()); } else { return canBeNull ? nullptr : m_rootNode; } } } poppler-24.02.0/qt5/src/poppler-optcontent.h000066400000000000000000000051521455701731300206760ustar00rootroot00000000000000/* poppler-optcontent.h: qt interface to poppler * * Copyright (C) 2007, Brad Hards * Copyright (C) 2008, Pino Toscano * Copyright (C) 2013, Anthony Granger * Copyright (C) 2016, 2021, 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 * 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_OPTCONTENT_H #define POPPLER_OPTCONTENT_H #include #include "poppler-export.h" #include "poppler-link.h" class OCGs; namespace Poppler { class Document; class OptContentModelPrivate; /** * \brief Model for optional content * * OptContentModel is an item model representing the optional content items * that can be found in PDF documents. * * The model offers a mostly read-only display of the data, allowing to * enable/disable some contents setting the Qt::CheckStateRole data role. * * \since 0.8 */ class POPPLER_QT5_EXPORT OptContentModel : public QAbstractItemModel { friend class Document; Q_OBJECT public: ~OptContentModel() override; QModelIndex index(int row, int column, const QModelIndex &parent) const override; QModelIndex parent(const QModelIndex &child) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent) const override; QVariant data(const QModelIndex &index, int role) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; Qt::ItemFlags flags(const QModelIndex &index) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; /** * Applies the Optional Content Changes specified by that link. * \since 0.50 */ void applyLink(LinkOCGState *link); private: explicit OptContentModel(OCGs *optContent, QObject *parent = nullptr); friend class OptContentModelPrivate; OptContentModelPrivate *d; }; } #endif poppler-24.02.0/qt5/src/poppler-outline-private.h000066400000000000000000000026541455701731300216340ustar00rootroot00000000000000/* poppler-outline-private.h: qt interface to poppler * * Copyright (C) 2018 Adam Reichold * Copyright (C) 2019 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 * 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_OUTLINE_PRIVATE_H_ #define _POPPLER_OUTLINE_PRIVATE_H_ #include #include class OutlineItem; namespace Poppler { class DocumentData; class LinkDestination; struct OutlineItemData { OutlineItemData(::OutlineItem *oi, DocumentData *dd) : data { oi }, documentData { dd } { } ::OutlineItem *data; DocumentData *documentData; mutable QString name; mutable QSharedPointer destination; mutable QString externalFileName; mutable QString uri; }; } #endif poppler-24.02.0/qt5/src/poppler-outline.cc000066400000000000000000000121641455701731300203170ustar00rootroot00000000000000/* poppler-outline.cc: qt interface to poppler * * 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 * 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 "poppler-private.h" #include "poppler-outline-private.h" #include "Link.h" #include "Outline.h" namespace Poppler { OutlineItem::OutlineItem() : m_data { new OutlineItemData { nullptr, nullptr } } { } OutlineItem::OutlineItem(OutlineItemData *data) : m_data { data } { } OutlineItem::~OutlineItem() { delete m_data; m_data = nullptr; } OutlineItem::OutlineItem(const OutlineItem &other) : m_data { new OutlineItemData { *other.m_data } } { } OutlineItem &OutlineItem::operator=(const OutlineItem &other) { if (this == &other) { return *this; } auto *data = new OutlineItemData { *other.m_data }; qSwap(m_data, data); delete data; return *this; } OutlineItem::OutlineItem(OutlineItem &&other) noexcept : m_data { other.m_data } { other.m_data = nullptr; } OutlineItem &OutlineItem::operator=(OutlineItem &&other) noexcept { qSwap(m_data, other.m_data); return *this; } bool OutlineItem::isNull() const { return !m_data->data; } QString OutlineItem::name() const { QString &name = m_data->name; if (name.isEmpty()) { if (const ::OutlineItem *data = m_data->data) { name = unicodeToQString(data->getTitle()); } } return name; } bool OutlineItem::isOpen() const { bool isOpen = false; if (const ::OutlineItem *data = m_data->data) { isOpen = data->isOpen(); } return isOpen; } QSharedPointer OutlineItem::destination() const { QSharedPointer &destination = m_data->destination; if (!destination) { if (const ::OutlineItem *data = m_data->data) { if (const ::LinkAction *action = data->getAction()) { if (action->getKind() == actionGoTo) { const auto *linkGoTo = static_cast(action); destination.reset(new LinkDestination(LinkDestinationData(linkGoTo->getDest(), linkGoTo->getNamedDest(), m_data->documentData, false))); } else if (action->getKind() == actionGoToR) { const auto *linkGoToR = static_cast(action); const bool external = linkGoToR->getFileName() != nullptr; destination.reset(new LinkDestination(LinkDestinationData(linkGoToR->getDest(), linkGoToR->getNamedDest(), m_data->documentData, external))); } } } } return destination; } QString OutlineItem::externalFileName() const { QString &externalFileName = m_data->externalFileName; if (externalFileName.isEmpty()) { if (const ::OutlineItem *data = m_data->data) { if (const ::LinkAction *action = data->getAction()) { if (action->getKind() == actionGoToR) { if (const GooString *fileName = static_cast(action)->getFileName()) { externalFileName = UnicodeParsedString(fileName); } } } } } return externalFileName; } QString OutlineItem::uri() const { QString &uri = m_data->uri; if (uri.isEmpty()) { if (const ::OutlineItem *data = m_data->data) { if (const ::LinkAction *action = data->getAction()) { if (action->getKind() == actionURI) { uri = UnicodeParsedString(static_cast(action)->getURI()); } } } } return uri; } bool OutlineItem::hasChildren() const { bool result = false; if (::OutlineItem *data = m_data->data) { result = data->hasKids(); } return result; } QVector OutlineItem::children() const { QVector result; if (::OutlineItem *data = m_data->data) { data->open(); if (const std::vector<::OutlineItem *> *kids = data->getKids()) { for (void *kid : *kids) { result.push_back(OutlineItem { new OutlineItemData { static_cast<::OutlineItem *>(kid), m_data->documentData } }); } } } return result; } } poppler-24.02.0/qt5/src/poppler-page-private.h000066400000000000000000000040361455701731300210650ustar00rootroot00000000000000/* poppler-page.cc: qt interface to poppler * Copyright (C) 2005, Net Integration Technologies, Inc. * Copyright (C) 2007, 2012, Albert Astals Cid * Copyright (C) 2008, Pino Toscano * Copyright (C) 2015 Adam Reichold * Copyright (C) 2018, 2021 Nelson Benítez León * * 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_PAGE_PRIVATE_H_ #define _POPPLER_PAGE_PRIVATE_H_ #include "CharTypes.h" class QRectF; class LinkAction; class Page; class TextPage; namespace Poppler { class DocumentData; class PageTransition; class PageData { public: Link *convertLinkActionToLink(::LinkAction *a, const QRectF &linkArea); DocumentData *parentDoc; ::Page *page; int index; PageTransition *transition; static Link *convertLinkActionToLink(::LinkAction *a, DocumentData *parentDoc, const QRectF &linkArea); TextPage *prepareTextSearch(const QString &text, Page::Rotation rotate, QVector *u); bool performSingleTextSearch(TextPage *textPage, QVector &u, double &sLeft, double &sTop, double &sRight, double &sBottom, Page::SearchDirection direction, bool sCase, bool sWords, bool sDiacritics, bool sAcrossLines); QList performMultipleTextSearch(TextPage *textPage, QVector &u, bool sCase, bool sWords, bool sDiacritics, bool sAcrossLines); }; } #endif poppler-24.02.0/qt5/src/poppler-page-transition-private.h000066400000000000000000000020211455701731300232450ustar00rootroot00000000000000/* * Copyright (C) 2005, 2019, Albert Astals Cid * Copyright (C) 2019 Oliver Sander * * 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_PAGE_TRANSITION_PRIVATE_H_ #define _POPPLER_PAGE_TRANSITION_PRIVATE_H_ class Object; namespace Poppler { class PageTransitionParams { public: Object *dictObj; }; } #endif poppler-24.02.0/qt5/src/poppler-page-transition.cc000066400000000000000000000052541455701731300217460ustar00rootroot00000000000000/* PageTransition.cc * Copyright (C) 2005, Net Integration Technologies, Inc. * Copyright (C) 2015, Arseniy Lartsev * Copyright (C) 2018, 2021, 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 * 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 "PageTransition.h" #include "poppler-page-transition.h" #include "poppler-page-transition-private.h" namespace Poppler { class PageTransitionData { public: explicit PageTransitionData(Object *trans) { pt = new ::PageTransition(trans); } PageTransitionData(const PageTransitionData &ptd) { pt = new ::PageTransition(*ptd.pt); } ~PageTransitionData() { delete pt; } PageTransitionData &operator=(const PageTransitionData &) = delete; ::PageTransition *pt; }; PageTransition::PageTransition(const PageTransitionParams ¶ms) // clazy:exclude=function-args-by-value { data = new PageTransitionData(params.dictObj); } PageTransition::PageTransition(const PageTransition &pt) { data = new PageTransitionData(*pt.data); } PageTransition::~PageTransition() { delete data; } PageTransition &PageTransition::operator=(const PageTransition &other) { if (this != &other) { delete data; data = new PageTransitionData(*other.data); } return *this; } PageTransition::Type PageTransition::type() const { return (Poppler::PageTransition::Type)data->pt->getType(); } int PageTransition::duration() const { return data->pt->getDuration(); } double PageTransition::durationReal() const { return data->pt->getDuration(); } PageTransition::Alignment PageTransition::alignment() const { return (Poppler::PageTransition::Alignment)data->pt->getAlignment(); } PageTransition::Direction PageTransition::direction() const { return (Poppler::PageTransition::Direction)data->pt->getDirection(); } int PageTransition::angle() const { return data->pt->getAngle(); } double PageTransition::scale() const { return data->pt->getScale(); } bool PageTransition::isRectangular() const { return data->pt->isRectangular(); } } poppler-24.02.0/qt5/src/poppler-page-transition.h000066400000000000000000000106671455701731300216140ustar00rootroot00000000000000/* PageTransition.h * Copyright (C) 2005, Net Integration Technologies, Inc. * Copyright (C) 2005, Brad Hards * Copyright (C) 2015, Arseniy Lartsev * Copyright (C) 2018, 2021 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 * 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 __PAGETRANSITION_X_H__ #define __PAGETRANSITION_X_H__ #include "poppler-export.h" #include namespace Poppler { class PageTransitionParams; class PageTransitionData; /** \brief Describes how a PDF file viewer shall perform the transition from one page to another In PDF files there is a way to specify if the viewer shall use certain effects to perform the transition from one page to another. This feature can be used, e.g., in a PDF-based beamer presentation. This utility class represents the transition effect, and can be used to extract the information from a PDF object. */ class POPPLER_QT5_EXPORT PageTransition { public: /** \brief transition effect that shall be used */ // if changed remember to keep in sync with PageTransition.h enum enum Type { Replace = 0, Split, Blinds, Box, Wipe, Dissolve, Glitter, Fly, Push, Cover, Uncover, Fade }; /** \brief alignment of the transition effect that shall be used */ // if changed remember to keep in sync with PageTransition.h enum enum Alignment { Horizontal = 0, Vertical }; /** \brief direction of the transition effect that shall be used */ // if changed remember to keep in sync with PageTransition.h enum enum Direction { Inward = 0, Outward }; /** \brief Construct a new PageTransition object from a page dictionary. Users of the library will rarely need to construct a PageTransition object themselves. Instead, the method Poppler::Page::transition() can be used to find out if a certain transition effect is specified. @warning In case or error, this method will print an error message to stderr, and construct a default object. @param params an object whose dictionary will be read and parsed. This must be a valid object, whose dictionaries are accessed by the constructor. The object is only accessed by this constructor, and may be deleted after the constructor returns. */ // TODO Next ABI break, make this private and remove reference explicit PageTransition(const PageTransitionParams ¶ms); /** \brief copy constructor */ PageTransition(const PageTransition &pt); /** \brief assignment operator \since 0.63 */ PageTransition &operator=(const PageTransition &other); /** Destructor */ ~PageTransition(); /** \brief Get type of the transition. */ Type type() const; /** \brief Get duration of the transition in seconds as integer \deprecated This function is left for backward compatibility, use durationReal() instead. */ Q_DECL_DEPRECATED int duration() const; /** \brief Get duration of the transition in seconds */ double durationReal() const; /** \brief Get dimension in which the transition effect occurs. */ Alignment alignment() const; /** \brief Get direction of motion of the transition effect. */ Direction direction() const; /** \brief Get direction in which the transition effect moves. */ int angle() const; /** \brief Get starting or ending scale. */ double scale() const; /** \brief Returns true if the area to be flown is rectangular and opaque. */ bool isRectangular() const; private: PageTransitionData *data; }; } #endif poppler-24.02.0/qt5/src/poppler-page.cc000066400000000000000000001050621455701731300175540ustar00rootroot00000000000000/* 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, Stefan Kebekus * Copyright (C) 2006-2011, Pino Toscano * Copyright (C) 2008 Carlos Garcia Campos * Copyright (C) 2009 Shawn Rutledge * Copyright (C) 2010, 2012, Guillermo Amaral * Copyright (C) 2010 Suzuki Toshiya * Copyright (C) 2010 Matthias Fauconneau * Copyright (C) 2010 Hib Eris * Copyright (C) 2012 Tobias Koenig * Copyright (C) 2012 Fabio D'Urso * Copyright (C) 2012, 2015 Adam Reichold * Copyright (C) 2012, 2013 Thomas Freitag * Copyright (C) 2015 William Bader * Copyright (C) 2016 Arseniy Lartsev * Copyright (C) 2016, Hanno Meyer-Thurow * Copyright (C) 2017-2020, Oliver Sander * Copyright (C) 2017 Adrian Johnson * 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 Intevation GmbH * Copyright (C) 2018, Tobias Deiminger * Copyright (C) 2018, 2021 Nelson Benítez León * Copyright (C) 2020 Oliver Sander * 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 * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "poppler-private.h" #include "poppler-page-transition-private.h" #include "poppler-page-private.h" #include "poppler-link-extractor-private.h" #include "poppler-link-private.h" #include "poppler-annotation-private.h" #include "poppler-form.h" #include "poppler-media.h" namespace Poppler { class TextExtractionAbortHelper { public: TextExtractionAbortHelper(Page::ShouldAbortQueryFunc shouldAbortCallback, const QVariant &payloadA) { shouldAbortExtractionCallback = shouldAbortCallback; payload = payloadA; } Page::ShouldAbortQueryFunc shouldAbortExtractionCallback = nullptr; QVariant payload; }; class OutputDevCallbackHelper { public: void setCallbacks(Page::RenderToImagePartialUpdateFunc callback, Page::ShouldRenderToImagePartialQueryFunc shouldDoCallback, Page::ShouldAbortQueryFunc shouldAbortCallback, const QVariant &payloadA) { partialUpdateCallback = callback; shouldDoPartialUpdateCallback = shouldDoCallback; shouldAbortRenderCallback = shouldAbortCallback; payload = payloadA; } Page::RenderToImagePartialUpdateFunc partialUpdateCallback = nullptr; Page::ShouldRenderToImagePartialQueryFunc shouldDoPartialUpdateCallback = nullptr; Page::ShouldAbortQueryFunc shouldAbortRenderCallback = nullptr; QVariant payload; }; class Qt5SplashOutputDev : public SplashOutputDev, public OutputDevCallbackHelper { public: Qt5SplashOutputDev(SplashColorMode colorModeA, int bitmapRowPadA, bool reverseVideoA, bool ignorePaperColorA, SplashColorPtr paperColorA, bool bitmapTopDownA, SplashThinLineMode thinLineMode, bool overprintPreviewA) : SplashOutputDev(colorModeA, bitmapRowPadA, reverseVideoA, paperColorA, bitmapTopDownA, thinLineMode, overprintPreviewA), ignorePaperColor(ignorePaperColorA) { } ~Qt5SplashOutputDev() override; void dump() override { if (partialUpdateCallback && shouldDoPartialUpdateCallback && shouldDoPartialUpdateCallback(payload)) { partialUpdateCallback(getXBGRImage(false /* takeImageData */), payload); } } QImage getXBGRImage(bool takeImageData) { SplashBitmap *b = getBitmap(); // 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; 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) { // Convert byte order from RGBX to XBGR. for (int i = 0; i < bh; ++i) { for (int j = 0; j < bw; ++j) { SplashColorPtr pixel = &data[i * brs + j]; qSwap(pixel[0], pixel[3]); qSwap(pixel[1], pixel[2]); } } } if (takeImageData) { // Construct a Qt image holding (and also owning) the raw bitmap data. QImage i(data, bw, bh, brs, format, gfree, data); if (i.isNull()) { gfree(data); } return i; } else { return QImage(data, bw, bh, brs, format).copy(); } } return QImage(); } private: bool ignorePaperColor; }; Qt5SplashOutputDev::~Qt5SplashOutputDev() = default; class QImageDumpingQPainterOutputDev : public QPainterOutputDev, public OutputDevCallbackHelper { public: QImageDumpingQPainterOutputDev(QPainter *painter, QImage *i) : QPainterOutputDev(painter), image(i) { } ~QImageDumpingQPainterOutputDev() override; void dump() override { if (partialUpdateCallback && shouldDoPartialUpdateCallback && shouldDoPartialUpdateCallback(payload)) { partialUpdateCallback(*image, payload); } } private: QImage *image; }; QImageDumpingQPainterOutputDev::~QImageDumpingQPainterOutputDev() = default; Link *PageData::convertLinkActionToLink(::LinkAction *a, const QRectF &linkArea) { return convertLinkActionToLink(a, parentDoc, linkArea); } Link *PageData::convertLinkActionToLink(::LinkAction *a, DocumentData *parentDoc, const QRectF &linkArea) { if (!a) { return nullptr; } Link *popplerLink = nullptr; switch (a->getKind()) { case actionGoTo: { LinkGoTo *g = (LinkGoTo *)a; const LinkDestinationData ldd(g->getDest(), g->getNamedDest(), parentDoc, false); // create link: no ext file, namedDest, object pointer popplerLink = new LinkGoto(linkArea, QString(), LinkDestination(ldd)); } break; case actionGoToR: { LinkGoToR *g = (LinkGoToR *)a; // copy link file const QString fileName = UnicodeParsedString(g->getFileName()); const LinkDestinationData ldd(g->getDest(), g->getNamedDest(), parentDoc, !fileName.isEmpty()); // create link: fileName, namedDest, object pointer popplerLink = new LinkGoto(linkArea, fileName, LinkDestination(ldd)); } break; case actionLaunch: { LinkLaunch *e = (LinkLaunch *)a; const GooString *p = e->getParams(); popplerLink = new LinkExecute(linkArea, e->getFileName()->c_str(), p ? p->c_str() : nullptr); } break; case actionNamed: { const std::string &name = ((LinkNamed *)a)->getName(); if (name == "NextPage") { popplerLink = new LinkAction(linkArea, LinkAction::PageNext); } else if (name == "PrevPage") { popplerLink = new LinkAction(linkArea, LinkAction::PagePrev); } else if (name == "FirstPage") { popplerLink = new LinkAction(linkArea, LinkAction::PageFirst); } else if (name == "LastPage") { popplerLink = new LinkAction(linkArea, LinkAction::PageLast); } else if (name == "GoBack") { popplerLink = new LinkAction(linkArea, LinkAction::HistoryBack); } else if (name == "GoForward") { popplerLink = new LinkAction(linkArea, LinkAction::HistoryForward); } else if (name == "Quit") { popplerLink = new LinkAction(linkArea, LinkAction::Quit); } else if (name == "GoToPage") { popplerLink = new LinkAction(linkArea, LinkAction::GoToPage); } else if (name == "Find") { popplerLink = new LinkAction(linkArea, LinkAction::Find); } else if (name == "FullScreen") { popplerLink = new LinkAction(linkArea, LinkAction::Presentation); } else if (name == "Print") { popplerLink = new LinkAction(linkArea, LinkAction::Print); } else if (name == "Close") { // acroread closes the document always, doesnt care whether // its presentation mode or not // popplerLink = new LinkAction( linkArea, LinkAction::EndPresentation ); popplerLink = new LinkAction(linkArea, LinkAction::Close); } else if (name == "SaveAs") { popplerLink = new LinkAction(linkArea, LinkAction::SaveAs); } else { qWarning() << "Unhandled action name" << name.c_str(); } } break; case actionURI: { popplerLink = new LinkBrowse(linkArea, ((LinkURI *)a)->getURI().c_str()); } break; case actionSound: { ::LinkSound *ls = (::LinkSound *)a; popplerLink = new LinkSound(linkArea, ls->getVolume(), ls->getSynchronous(), ls->getRepeat(), ls->getMix(), new SoundObject(ls->getSound())); } break; case actionJavaScript: { ::LinkJavaScript *ljs = (::LinkJavaScript *)a; popplerLink = new LinkJavaScript(linkArea, UnicodeParsedString(ljs->getScript())); } break; case actionMovie: { ::LinkMovie *lm = (::LinkMovie *)a; const QString title = (lm->hasAnnotTitle() ? UnicodeParsedString(lm->getAnnotTitle()) : QString()); Ref reference = Ref::INVALID(); if (lm->hasAnnotRef()) { reference = *lm->getAnnotRef(); } LinkMovie::Operation operation = LinkMovie::Play; switch (lm->getOperation()) { case ::LinkMovie::operationTypePlay: operation = LinkMovie::Play; break; case ::LinkMovie::operationTypePause: operation = LinkMovie::Pause; break; case ::LinkMovie::operationTypeResume: operation = LinkMovie::Resume; break; case ::LinkMovie::operationTypeStop: operation = LinkMovie::Stop; break; }; popplerLink = new LinkMovie(linkArea, operation, title, reference); } break; case actionRendition: { ::LinkRendition *lrn = (::LinkRendition *)a; Ref reference = Ref::INVALID(); if (lrn->hasScreenAnnot()) { reference = lrn->getScreenAnnot(); } popplerLink = new LinkRendition(linkArea, lrn->getMedia() ? lrn->getMedia()->copy() : nullptr, lrn->getOperation(), UnicodeParsedString(lrn->getScript()), reference); } break; case actionOCGState: { ::LinkOCGState *plocg = (::LinkOCGState *)a; LinkOCGStatePrivate *locgp = new LinkOCGStatePrivate(linkArea, plocg->getStateList(), plocg->getPreserveRB()); popplerLink = new LinkOCGState(locgp); } break; case actionHide: { ::LinkHide *lh = (::LinkHide *)a; LinkHidePrivate *lhp = new LinkHidePrivate(linkArea, lh->hasTargetName() ? UnicodeParsedString(lh->getTargetName()) : QString(), lh->isShowAction()); popplerLink = new LinkHide(lhp); } break; case actionResetForm: // Not handled in Qt5 front-end yet break; case actionUnknown: break; } if (popplerLink) { QVector links; for (const std::unique_ptr<::LinkAction> &nextAction : a->nextActions()) { links << convertLinkActionToLink(nextAction.get(), parentDoc, linkArea); } LinkPrivate::get(popplerLink)->nextLinks = links; } return popplerLink; } inline TextPage *PageData::prepareTextSearch(const QString &text, Page::Rotation rotate, QVector *u) { *u = text.toUcs4(); const int rotation = (int)rotate * 90; // fetch ourselves a textpage TextOutputDev td(nullptr, true, 0, false, false); parentDoc->doc->displayPage(&td, index + 1, 72, 72, rotation, false, true, false, nullptr, nullptr, nullptr, nullptr, true); TextPage *textPage = td.takeText(); return textPage; } inline bool PageData::performSingleTextSearch(TextPage *textPage, QVector &u, double &sLeft, double &sTop, double &sRight, double &sBottom, Page::SearchDirection direction, bool sCase, bool sWords, bool sDiacritics, bool sAcrossLines) { if (direction == Page::FromTop) { return textPage->findText(u.data(), u.size(), true, true, false, false, sCase, sDiacritics, sAcrossLines, false, sWords, &sLeft, &sTop, &sRight, &sBottom, nullptr, nullptr); } else if (direction == Page::NextResult) { return textPage->findText(u.data(), u.size(), false, true, true, false, sCase, sDiacritics, sAcrossLines, false, sWords, &sLeft, &sTop, &sRight, &sBottom, nullptr, nullptr); } else if (direction == Page::PreviousResult) { return textPage->findText(u.data(), u.size(), false, true, true, false, sCase, sDiacritics, sAcrossLines, true, sWords, &sLeft, &sTop, &sRight, &sBottom, nullptr, nullptr); } return false; } inline QList PageData::performMultipleTextSearch(TextPage *textPage, QVector &u, bool sCase, bool sWords, bool sDiacritics, bool sAcrossLines) { QList results; double sLeft = 0.0, sTop = 0.0, sRight = 0.0, sBottom = 0.0; bool sIgnoredHyphen = false; PDFRectangle continueMatch; continueMatch.x1 = DBL_MAX; // we use this to detect valid return values while (textPage->findText(u.data(), u.size(), false, true, true, false, sCase, sDiacritics, sAcrossLines, false, sWords, &sLeft, &sTop, &sRight, &sBottom, &continueMatch, &sIgnoredHyphen)) { QRectF result; result.setLeft(sLeft); result.setTop(sTop); result.setRight(sRight); result.setBottom(sBottom); results.append(result); if (sAcrossLines && continueMatch.x1 != DBL_MAX) { QRectF resultN; resultN.setLeft(continueMatch.x1); resultN.setTop(continueMatch.y1); resultN.setRight(continueMatch.x2); resultN.setBottom(continueMatch.y1); results.append(resultN); continueMatch.x1 = DBL_MAX; } } return results; } Page::Page(DocumentData *doc, int index) { m_page = new PageData(); m_page->index = index; m_page->parentDoc = doc; m_page->page = doc->doc->getPage(m_page->index + 1); m_page->transition = nullptr; } Page::~Page() { delete m_page->transition; delete m_page; } // Callback that filters out everything but form fields static auto annotDisplayDecideCbk = [](Annot *annot, void *user_data) { // Hide everything but forms return (annot->getType() == Annot::typeWidget); }; // A nullptr, but with the type of a function pointer // Needed to make the ternary operator happy. static bool (*nullAnnotCallBack)(Annot *annot, void *user_data) = nullptr; static auto shouldAbortRenderInternalCallback = [](void *user_data) { OutputDevCallbackHelper *helper = reinterpret_cast(user_data); return helper->shouldAbortRenderCallback(helper->payload); }; static auto shouldAbortExtractionInternalCallback = [](void *user_data) { TextExtractionAbortHelper *helper = reinterpret_cast(user_data); return helper->shouldAbortExtractionCallback(helper->payload); }; // A nullptr, but with the type of a function pointer // Needed to make the ternary operator happy. static bool (*nullAbortCallBack)(void *user_data) = nullptr; static bool renderToQPainter(QImageDumpingQPainterOutputDev *qpainter_output, QPainter *painter, PageData *page, double xres, double yres, int x, int y, int w, int h, Page::Rotation rotate, Page::PainterFlags flags) { const bool savePainter = !(flags & Page::DontSaveAndRestore); if (savePainter) { painter->save(); } if (page->parentDoc->m_hints & Document::Antialiasing) { painter->setRenderHint(QPainter::Antialiasing); } if (page->parentDoc->m_hints & Document::TextAntialiasing) { painter->setRenderHint(QPainter::TextAntialiasing); } painter->translate(x == -1 ? 0 : -x, y == -1 ? 0 : -y); qpainter_output->startDoc(page->parentDoc->doc); const bool hideAnnotations = page->parentDoc->m_hints & Document::HideAnnotations; OutputDevCallbackHelper *abortHelper = qpainter_output; page->parentDoc->doc->displayPageSlice(qpainter_output, page->index + 1, xres, yres, (int)rotate * 90, false, true, false, x, y, w, h, abortHelper->shouldAbortRenderCallback ? shouldAbortRenderInternalCallback : nullAbortCallBack, abortHelper, (hideAnnotations) ? annotDisplayDecideCbk : nullAnnotCallBack, nullptr, true); if (savePainter) { painter->restore(); } return true; } QImage Page::renderToImage(double xres, double yres, int x, int y, int w, int h, Rotation rotate) const { return renderToImage(xres, yres, x, y, w, h, rotate, nullptr, nullptr, QVariant()); } QImage Page::renderToImage(double xres, double yres, int x, int y, int w, int h, Rotation rotate, RenderToImagePartialUpdateFunc partialUpdateCallback, ShouldRenderToImagePartialQueryFunc shouldDoPartialUpdateCallback, const QVariant &payload) const { return renderToImage(xres, yres, x, y, w, h, rotate, partialUpdateCallback, shouldDoPartialUpdateCallback, nullptr, payload); } // Translate the text hinting settings from poppler-speak to Qt-speak static QFont::HintingPreference QFontHintingFromPopplerHinting(int renderHints) { QFont::HintingPreference result = QFont::PreferNoHinting; if (renderHints & Document::TextHinting) { result = (renderHints & Document::TextSlightHinting) ? QFont::PreferVerticalHinting : QFont::PreferFullHinting; } return result; } QImage Page::renderToImage(double xres, double yres, int xPos, int yPos, int w, int h, Rotation rotate, RenderToImagePartialUpdateFunc partialUpdateCallback, ShouldRenderToImagePartialQueryFunc shouldDoPartialUpdateCallback, ShouldAbortQueryFunc shouldAbortRenderCallback, const QVariant &payload) const { int rotation = (int)rotate * 90; QImage img; switch (m_page->parentDoc->m_backend) { case Poppler::Document::SplashBackend: { SplashColor bgColor; const bool overprintPreview = m_page->parentDoc->m_hints & Document::OverprintPreview ? true : false; if (overprintPreview) { unsigned char c, m, y, k; c = 255 - m_page->parentDoc->paperColor.blue(); m = 255 - m_page->parentDoc->paperColor.red(); y = 255 - m_page->parentDoc->paperColor.green(); k = c; if (m < k) { k = m; } if (y < k) { k = y; } bgColor[0] = c - k; bgColor[1] = m - k; bgColor[2] = y - k; bgColor[3] = k; for (int i = 4; i < SPOT_NCOMPS + 4; i++) { bgColor[i] = 0; } } else { bgColor[0] = m_page->parentDoc->paperColor.blue(); bgColor[1] = m_page->parentDoc->paperColor.green(); bgColor[2] = m_page->parentDoc->paperColor.red(); } const SplashColorMode colorMode = overprintPreview ? splashModeDeviceN8 : splashModeXBGR8; SplashThinLineMode thinLineMode = splashThinLineDefault; if (m_page->parentDoc->m_hints & Document::ThinLineShape) { thinLineMode = splashThinLineShape; } if (m_page->parentDoc->m_hints & Document::ThinLineSolid) { thinLineMode = splashThinLineSolid; } const bool ignorePaperColor = m_page->parentDoc->m_hints & Document::IgnorePaperColor; Qt5SplashOutputDev splash_output(colorMode, 4, false, ignorePaperColor, ignorePaperColor ? nullptr : bgColor, true, thinLineMode, overprintPreview); splash_output.setCallbacks(partialUpdateCallback, shouldDoPartialUpdateCallback, shouldAbortRenderCallback, payload); splash_output.setFontAntialias(m_page->parentDoc->m_hints & Document::TextAntialiasing ? true : false); splash_output.setVectorAntialias(m_page->parentDoc->m_hints & Document::Antialiasing ? true : false); splash_output.setFreeTypeHinting(m_page->parentDoc->m_hints & Document::TextHinting ? true : false, m_page->parentDoc->m_hints & Document::TextSlightHinting ? true : false); #ifdef USE_CMS splash_output.setDisplayProfile(m_page->parentDoc->m_displayProfile); #endif splash_output.startDoc(m_page->parentDoc->doc); const bool hideAnnotations = m_page->parentDoc->m_hints & Document::HideAnnotations; OutputDevCallbackHelper *abortHelper = &splash_output; m_page->parentDoc->doc->displayPageSlice(&splash_output, m_page->index + 1, xres, yres, rotation, false, true, false, xPos, yPos, w, h, shouldAbortRenderCallback ? shouldAbortRenderInternalCallback : nullAbortCallBack, abortHelper, (hideAnnotations) ? annotDisplayDecideCbk : nullAnnotCallBack, nullptr, true); img = splash_output.getXBGRImage(true /* takeImageData */); break; } case Poppler::Document::QPainterBackend: { QSize size = pageSize(); QImage tmpimg(w == -1 ? qRound(size.width() * xres / 72.0) : w, h == -1 ? qRound(size.height() * yres / 72.0) : h, QImage::Format_ARGB32); QColor bgColor(m_page->parentDoc->paperColor.red(), m_page->parentDoc->paperColor.green(), m_page->parentDoc->paperColor.blue(), m_page->parentDoc->paperColor.alpha()); tmpimg.fill(bgColor); QPainter painter(&tmpimg); QImageDumpingQPainterOutputDev qpainter_output(&painter, &tmpimg); qpainter_output.setHintingPreference(QFontHintingFromPopplerHinting(m_page->parentDoc->m_hints)); #ifdef USE_CMS qpainter_output.setDisplayProfile(m_page->parentDoc->m_displayProfile); #endif qpainter_output.setCallbacks(partialUpdateCallback, shouldDoPartialUpdateCallback, shouldAbortRenderCallback, payload); renderToQPainter(&qpainter_output, &painter, m_page, xres, yres, xPos, yPos, w, h, rotate, DontSaveAndRestore); painter.end(); img = tmpimg; break; } } if (shouldAbortRenderCallback && shouldAbortRenderCallback(payload)) { return QImage(); } return img; } bool Page::renderToPainter(QPainter *painter, double xres, double yres, int x, int y, int w, int h, Rotation rotate, PainterFlags flags) const { if (!painter) { return false; } switch (m_page->parentDoc->m_backend) { case Poppler::Document::SplashBackend: return false; case Poppler::Document::QPainterBackend: { QImageDumpingQPainterOutputDev qpainter_output(painter, nullptr); qpainter_output.setHintingPreference(QFontHintingFromPopplerHinting(m_page->parentDoc->m_hints)); return renderToQPainter(&qpainter_output, painter, m_page, xres, yres, x, y, w, h, rotate, flags); } } return false; } QImage Page::thumbnail() const { unsigned char *data = nullptr; int w = 0; int h = 0; int rowstride = 0; bool r = m_page->page->loadThumb(&data, &w, &h, &rowstride); QImage ret; if (r) { // first construct a temporary image with the data got, // then force a copy of it so we can free the raw thumbnail data ret = QImage(data, w, h, rowstride, QImage::Format_RGB888).copy(); gfree(data); } return ret; } QString Page::text(const QRectF &r, TextLayout textLayout) const { TextOutputDev *output_dev; GooString *s; QString result; const bool rawOrder = textLayout == RawOrderLayout; output_dev = new TextOutputDev(nullptr, false, 0, rawOrder, false); 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); } else { s = output_dev->getText(r.left(), r.top(), r.right(), r.bottom()); } result = QString::fromUtf8(s->c_str()); delete output_dev; delete s; return result; } QString Page::text(const QRectF &r) const { return text(r, PhysicalLayout); } bool Page::search(const QString &text, double &sLeft, double &sTop, double &sRight, double &sBottom, SearchDirection direction, SearchMode caseSensitive, Rotation rotate) const { const bool sCase = caseSensitive == Page::CaseSensitive ? true : false; QVector u; TextPage *textPage = m_page->prepareTextSearch(text, rotate, &u); const bool found = m_page->performSingleTextSearch(textPage, u, sLeft, sTop, sRight, sBottom, direction, sCase, false, false, false); textPage->decRefCnt(); return found; } bool Page::search(const QString &text, double &sLeft, double &sTop, double &sRight, double &sBottom, SearchDirection direction, SearchFlags flags, Rotation rotate) const { const bool sCase = flags.testFlag(IgnoreCase) ? false : true; const bool sWords = flags.testFlag(WholeWords) ? true : false; const bool sDiacritics = flags.testFlag(IgnoreDiacritics) ? true : false; const bool sAcrossLines = flags.testFlag(AcrossLines) ? true : false; QVector u; TextPage *textPage = m_page->prepareTextSearch(text, rotate, &u); const bool found = m_page->performSingleTextSearch(textPage, u, sLeft, sTop, sRight, sBottom, direction, sCase, sWords, sDiacritics, sAcrossLines); textPage->decRefCnt(); return found; } QList Page::search(const QString &text, SearchMode caseSensitive, Rotation rotate) const { const bool sCase = caseSensitive == Page::CaseSensitive ? true : false; QVector u; TextPage *textPage = m_page->prepareTextSearch(text, rotate, &u); const QList results = m_page->performMultipleTextSearch(textPage, u, sCase, false, false, false); textPage->decRefCnt(); return results; } QList Page::search(const QString &text, SearchFlags flags, Rotation rotate) const { const bool sCase = flags.testFlag(IgnoreCase) ? false : true; const bool sWords = flags.testFlag(WholeWords) ? true : false; const bool sDiacritics = flags.testFlag(IgnoreDiacritics) ? true : false; const bool sAcrossLines = flags.testFlag(AcrossLines) ? true : false; QVector u; TextPage *textPage = m_page->prepareTextSearch(text, rotate, &u); const QList results = m_page->performMultipleTextSearch(textPage, u, sCase, sWords, sDiacritics, sAcrossLines); textPage->decRefCnt(); return results; } QList Page::textList(Rotation rotate) const { return textList(rotate, nullptr, QVariant()); } QList Page::textList(Rotation rotate, ShouldAbortQueryFunc shouldAbortExtractionCallback, const QVariant &closure) const { QList output_list; TextOutputDev output_dev(nullptr, false, 0, false, false); int rotation = (int)rotate * 90; TextExtractionAbortHelper abortHelper(shouldAbortExtractionCallback, closure); m_page->parentDoc->doc->displayPageSlice(&output_dev, m_page->index + 1, 72, 72, rotation, false, false, false, -1, -1, -1, -1, shouldAbortExtractionCallback ? shouldAbortExtractionInternalCallback : nullAbortCallBack, &abortHelper, nullptr, nullptr, true); std::unique_ptr word_list = output_dev.makeWordList(); if (shouldAbortExtractionCallback && shouldAbortExtractionCallback(closure)) { return output_list; } QHash wordBoxMap; output_list.reserve(word_list->getLength()); for (int i = 0; i < word_list->getLength(); i++) { TextWord *word = word_list->get(i); GooString *gooWord = word->getText(); QString string = QString::fromUtf8(gooWord->c_str()); delete gooWord; double xMin, yMin, xMax, yMax; word->getBBox(&xMin, &yMin, &xMax, &yMax); TextBox *text_box = new TextBox(string, QRectF(xMin, yMin, xMax - xMin, yMax - yMin)); text_box->m_data->hasSpaceAfter = word->hasSpaceAfter() == true; text_box->m_data->charBBoxes.reserve(word->getLength()); for (int j = 0; j < word->getLength(); ++j) { word->getCharBBox(j, &xMin, &yMin, &xMax, &yMax); text_box->m_data->charBBoxes.append(QRectF(xMin, yMin, xMax - xMin, yMax - yMin)); } wordBoxMap.insert(word, text_box); output_list.append(text_box); } for (int i = 0; i < word_list->getLength(); i++) { TextWord *word = word_list->get(i); TextBox *text_box = wordBoxMap.value(word); text_box->m_data->nextWord = wordBoxMap.value(word->nextWord()); } return output_list; } PageTransition *Page::transition() const { if (!m_page->transition) { Object o = m_page->page->getTrans(); PageTransitionParams params; params.dictObj = &o; if (params.dictObj->isDict()) { m_page->transition = new PageTransition(params); } } return m_page->transition; } Link *Page::action(PageAction act) const { if (act == Page::Opening || act == Page::Closing) { Object o = m_page->page->getActions(); if (!o.isDict()) { return nullptr; } Dict *dict = o.getDict(); const char *key = act == Page::Opening ? "O" : "C"; Object o2 = dict->lookup((char *)key); std::unique_ptr<::LinkAction> lact = ::LinkAction::parseAction(&o2, m_page->parentDoc->doc->getCatalog()->getBaseURI()); Link *popplerLink = nullptr; if (lact != nullptr) { popplerLink = m_page->convertLinkActionToLink(lact.get(), QRectF()); } return popplerLink; } return nullptr; } QSizeF Page::pageSizeF() const { Page::Orientation orient = orientation(); if ((Page::Landscape == orient) || (Page::Seascape == orient)) { return QSizeF(m_page->page->getCropHeight(), m_page->page->getCropWidth()); } else { return QSizeF(m_page->page->getCropWidth(), m_page->page->getCropHeight()); } } QSize Page::pageSize() const { return pageSizeF().toSize(); } Page::Orientation Page::orientation() const { const int rotation = m_page->page->getRotate(); switch (rotation) { case 90: return Page::Landscape; break; case 180: return Page::UpsideDown; break; case 270: return Page::Seascape; break; default: return Page::Portrait; } } void Page::defaultCTM(double *CTM, double dpiX, double dpiY, int rotate, bool upsideDown) { m_page->page->getDefaultCTM(CTM, dpiX, dpiY, rotate, false, upsideDown); } QList Page::links() const { LinkExtractorOutputDev link_dev(m_page); m_page->parentDoc->doc->processLinks(&link_dev, m_page->index + 1); QList popplerLinks = link_dev.links(); return popplerLinks; } QList Page::annotations() const { return AnnotationPrivate::findAnnotations(m_page->page, m_page->parentDoc, QSet()); } QList Page::annotations(const QSet &subtypes) const { return AnnotationPrivate::findAnnotations(m_page->page, m_page->parentDoc, subtypes); } void Page::addAnnotation(const Annotation *ann) { AnnotationPrivate::addAnnotationToPage(m_page->page, m_page->parentDoc, ann); } void Page::removeAnnotation(const Annotation *ann) { AnnotationPrivate::removeAnnotationFromPage(m_page->page, ann); } QList Page::formFields() const { QList fields; ::Page *p = m_page->page; const std::unique_ptr form = p->getFormWidgets(); int formcount = form->getNumWidgets(); for (int i = 0; i < formcount; ++i) { ::FormWidget *fm = form->getWidget(i); FormField *ff = nullptr; switch (fm->getType()) { case formButton: { ff = new FormFieldButton(m_page->parentDoc, p, static_cast(fm)); } break; case formText: { ff = new FormFieldText(m_page->parentDoc, p, static_cast(fm)); } break; case formChoice: { ff = new FormFieldChoice(m_page->parentDoc, p, static_cast(fm)); } break; case formSignature: { ff = new FormFieldSignature(m_page->parentDoc, p, static_cast(fm)); } break; default:; } if (ff) { fields.append(ff); } } return fields; } double Page::duration() const { return m_page->page->getDuration(); } QString Page::label() const { GooString goo; if (!m_page->parentDoc->doc->getCatalog()->indexToLabel(m_page->index, &goo)) { return QString(); } return UnicodeParsedString(&goo); } int Page::index() const { return m_page->index; } } poppler-24.02.0/qt5/src/poppler-pdf-converter.cc000066400000000000000000000230551455701731300214170ustar00rootroot00000000000000/* poppler-pdf-converter.cc: qt interface to poppler * Copyright (C) 2008, Pino Toscano * Copyright (C) 2008, 2009, 2020-2022, 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, . * Copyright (C) 2021, Zachary Travis * Copyright (C) 2021, Georgiy Sgibnev . Work sponsored by lab50.net. * Copyright (C) 2022, Martin * Copyright (C) 2022, Felix Jung * * 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 "poppler-qt5.h" #include "poppler-annotation-helper.h" #include "poppler-annotation-private.h" #include "poppler-private.h" #include "poppler-converter-private.h" #include "poppler-qiodeviceoutstream-private.h" #include #include #include "Array.h" #include "Form.h" #include namespace Poppler { class PDFConverterPrivate : public BaseConverterPrivate { public: PDFConverterPrivate(); ~PDFConverterPrivate() override; PDFConverter::PDFOptions opts; }; PDFConverterPrivate::PDFConverterPrivate() : BaseConverterPrivate(), opts(nullptr) { } PDFConverterPrivate::~PDFConverterPrivate() = default; PDFConverter::PDFConverter(DocumentData *document) : BaseConverter(*new PDFConverterPrivate()) { Q_D(PDFConverter); d->document = document; } PDFConverter::~PDFConverter() { } void PDFConverter::setPDFOptions(PDFConverter::PDFOptions options) { Q_D(PDFConverter); d->opts = options; } PDFConverter::PDFOptions PDFConverter::pdfOptions() const { Q_D(const PDFConverter); return d->opts; } bool PDFConverter::convert() { Q_D(PDFConverter); d->lastError = NoError; if (d->document->locked) { d->lastError = FileLockedError; return false; } QIODevice *dev = d->openDevice(); if (!dev) { d->lastError = OpenOutputError; return false; } bool deleteFile = false; if (QFile *file = qobject_cast(dev)) { deleteFile = !file->exists(); } int errorCode = errNone; QIODeviceOutStream stream(dev); if (d->opts & WithChanges) { errorCode = d->document->doc->saveAs(&stream); } else { errorCode = d->document->doc->saveWithoutChangesAs(&stream); } d->closeDevice(); if (errorCode != errNone) { if (deleteFile) { qobject_cast(dev)->remove(); } if (errorCode == errOpenFile) { d->lastError = OpenOutputError; } else { d->lastError = NotSupportedInputFileError; } } return (errorCode == errNone); } bool PDFConverter::sign(const NewSignatureData &data) { Q_D(PDFConverter); d->lastError = NoError; if (d->document->locked) { d->lastError = FileLockedError; return false; } if (data.signatureText().isEmpty()) { qWarning() << "No signature text given"; return false; } ::PDFDoc *doc = d->document->doc; ::Page *destPage = doc->getPage(data.page() + 1); std::unique_ptr gSignatureText = std::unique_ptr(QStringToUnicodeGooString(data.signatureText())); std::unique_ptr gSignatureLeftText = std::unique_ptr(QStringToUnicodeGooString(data.signatureLeftText())); const auto reason = std::unique_ptr(data.reason().isEmpty() ? nullptr : QStringToUnicodeGooString(data.reason())); 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); } struct PDFConverter::NewSignatureData::NewSignatureDataPrivate { NewSignatureDataPrivate() = default; QString certNickname; QString password; int page; QRectF boundingRectangle; QString signatureText; QString signatureLeftText; QString reason; QString location; 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 partialName = QUuid::createUuid().toString(); QByteArray documentOwnerPassword; QByteArray documentUserPassword; QString imagePath; }; PDFConverter::NewSignatureData::NewSignatureData() : d(new NewSignatureDataPrivate()) { } PDFConverter::NewSignatureData::~NewSignatureData() { delete d; } QString PDFConverter::NewSignatureData::certNickname() const { return d->certNickname; } void PDFConverter::NewSignatureData::setCertNickname(const QString &certNickname) { d->certNickname = certNickname; } QString PDFConverter::NewSignatureData::password() const { return d->password; } void PDFConverter::NewSignatureData::setPassword(const QString &password) { d->password = password; } int PDFConverter::NewSignatureData::page() const { return d->page; } void PDFConverter::NewSignatureData::setPage(int page) { d->page = page; } QRectF PDFConverter::NewSignatureData::boundingRectangle() const { return d->boundingRectangle; } void PDFConverter::NewSignatureData::setBoundingRectangle(const QRectF &rect) { d->boundingRectangle = rect; } QString PDFConverter::NewSignatureData::signatureText() const { return d->signatureText; } void PDFConverter::NewSignatureData::setSignatureText(const QString &text) { d->signatureText = text; } QString PDFConverter::NewSignatureData::signatureLeftText() const { return d->signatureLeftText; } void PDFConverter::NewSignatureData::setSignatureLeftText(const QString &text) { d->signatureLeftText = text; } QString PDFConverter::NewSignatureData::reason() const { return d->reason; } void PDFConverter::NewSignatureData::setReason(const QString &reason) { d->reason = reason; } QString PDFConverter::NewSignatureData::location() const { return d->location; } void PDFConverter::NewSignatureData::setLocation(const QString &location) { d->location = location; } double PDFConverter::NewSignatureData::fontSize() const { return d->fontSize; } void PDFConverter::NewSignatureData::setFontSize(double fontSize) { d->fontSize = fontSize; } double PDFConverter::NewSignatureData::leftFontSize() const { return d->leftFontSize; } void PDFConverter::NewSignatureData::setLeftFontSize(double fontSize) { d->leftFontSize = fontSize; } QColor PDFConverter::NewSignatureData::fontColor() const { return d->fontColor; } void PDFConverter::NewSignatureData::setFontColor(const QColor &color) { d->fontColor = color; } QColor PDFConverter::NewSignatureData::borderColor() const { return d->borderColor; } void PDFConverter::NewSignatureData::setBorderColor(const QColor &color) { d->borderColor = color; } QColor PDFConverter::NewSignatureData::backgroundColor() const { return d->backgroundColor; } double PDFConverter::NewSignatureData::borderWidth() const { return d->borderWidth; } void PDFConverter::NewSignatureData::setBorderWidth(double width) { d->borderWidth = width; } void PDFConverter::NewSignatureData::setBackgroundColor(const QColor &color) { d->backgroundColor = color; } QString PDFConverter::NewSignatureData::fieldPartialName() const { return d->partialName; } void PDFConverter::NewSignatureData::setFieldPartialName(const QString &name) { d->partialName = name; } QByteArray PDFConverter::NewSignatureData::documentOwnerPassword() const { return d->documentOwnerPassword; } void PDFConverter::NewSignatureData::setDocumentOwnerPassword(const QByteArray &password) { d->documentOwnerPassword = password; } QByteArray PDFConverter::NewSignatureData::documentUserPassword() const { return d->documentUserPassword; } void PDFConverter::NewSignatureData::setDocumentUserPassword(const QByteArray &password) { d->documentUserPassword = password; } QString PDFConverter::NewSignatureData::imagePath() const { return d->imagePath; } void PDFConverter::NewSignatureData::setImagePath(const QString &path) { d->imagePath = path; } } poppler-24.02.0/qt5/src/poppler-private.cc000066400000000000000000000271361455701731300203170ustar00rootroot00000000000000/* 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) 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 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 g10 Code GmbH, Author: Sune Stolborg Vuorela * Inspired on code by * Copyright (C) 2004 by Albert Astals Cid * Copyright (C) 2004 by Enrico Ros * * 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 "poppler-private.h" #include "poppler-form.h" #include #include #include #include #include #include #include #ifdef ANDROID # include # include # include # include # include # include #endif namespace Poppler { namespace Debug { static void qDebugDebugFunction(const QString &message, const QVariant & /*closure*/) { qDebug() << message; } PopplerDebugFunc debugFunction = qDebugDebugFunction; QVariant debugClosure; } void setDebugErrorFunction(PopplerDebugFunc function, const QVariant &closure) { Debug::debugFunction = function ? function : Debug::qDebugDebugFunction; Debug::debugClosure = closure; } void qt5ErrorFunction(ErrorCategory /*category*/, Goffset pos, const char *msg) { QString emsg; if (pos >= 0) { emsg = QStringLiteral("Error (%1): ").arg(pos); } else { emsg = QStringLiteral("Error: "); } emsg += QString::fromLatin1(msg); (*Debug::debugFunction)(emsg, Debug::debugClosure); } QString unicodeToQString(const Unicode *u, int len) { const UnicodeMap *utf8Map = globalParams->getUtf8Map(); // ignore the last characters if they are 0x0 while ((len > 0) && (u[len - 1] == 0)) { --len; } GooString convertedStr; for (int i = 0; i < len; ++i) { char buf[8]; const int n = utf8Map->mapUnicode(u[i], buf, sizeof(buf)); convertedStr.append(buf, n); } 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(); } QString UnicodeParsedString(const std::string &s1) { if (s1.empty()) { return QString(); } if (GooString::hasUnicodeMarker(s1) || GooString::hasUnicodeMarkerLE(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; return result; } } GooString *QStringToUnicodeGooString(const QString &s) { if (s.isEmpty()) { return new GooString(); } 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(); } GooString *ret = new GooString(cstring, len); gfree(cstring); return ret; } GooString *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); gfree(cstring); return ret; } GooString *QDateTimeToUnicodeGooString(const QDateTime &dt) { if (!dt.isValid()) { return nullptr; } return QStringToUnicodeGooString(dt.toUTC().toString(QStringLiteral("yyyyMMddhhmmss+00'00'"))); } Annot::AdditionalActionsType toPopplerAdditionalActionType(Annotation::AdditionalActionType type) { switch (type) { case Annotation::CursorEnteringAction: return Annot::actionCursorEntering; case Annotation::CursorLeavingAction: return Annot::actionCursorLeaving; case Annotation::MousePressedAction: return Annot::actionMousePressed; case Annotation::MouseReleasedAction: return Annot::actionMouseReleased; case Annotation::FocusInAction: return Annot::actionFocusIn; case Annotation::FocusOutAction: return Annot::actionFocusOut; case Annotation::PageOpeningAction: return Annot::actionPageOpening; case Annotation::PageClosingAction: return Annot::actionPageClosing; case Annotation::PageVisibleAction: return Annot::actionPageVisible; case Annotation::PageInvisibleAction: return Annot::actionPageInvisible; } return Annot::actionCursorEntering; } static void linkActionToTocItem(const ::LinkAction *a, DocumentData *doc, QDomElement *e) { if (!a || !e) { return; } switch (a->getKind()) { case actionGoTo: { // page number is contained/referenced in a LinkGoTo const LinkGoTo *g = static_cast(a); const LinkDest *destination = g->getDest(); if (!destination && g->getNamedDest()) { // no 'destination' but an internal 'named reference'. we could // get the destination for the page now, but it's VERY time consuming, // so better storing the reference and provide the viewport on demand const GooString *s = g->getNamedDest(); QChar *charArray = new QChar[s->getLength()]; for (int i = 0; i < s->getLength(); ++i) { charArray[i] = QChar(s->c_str()[i]); } QString aux(charArray, s->getLength()); e->setAttribute(QStringLiteral("DestinationName"), aux); delete[] charArray; } else if (destination && destination->isOk()) { LinkDestinationData ldd(destination, nullptr, doc, false); e->setAttribute(QStringLiteral("Destination"), LinkDestination(ldd).toString()); } break; } case actionGoToR: { // page number is contained/referenced in a LinkGoToR const LinkGoToR *g = static_cast(a); const LinkDest *destination = g->getDest(); if (!destination && g->getNamedDest()) { // no 'destination' but an internal 'named reference'. we could // get the destination for the page now, but it's VERY time consuming, // so better storing the reference and provide the viewport on demand const GooString *s = g->getNamedDest(); QChar *charArray = new QChar[s->getLength()]; for (int i = 0; i < s->getLength(); ++i) { charArray[i] = QChar(s->c_str()[i]); } QString aux(charArray, s->getLength()); e->setAttribute(QStringLiteral("DestinationName"), aux); delete[] charArray; } else if (destination && destination->isOk()) { LinkDestinationData ldd(destination, nullptr, doc, g->getFileName() != nullptr); e->setAttribute(QStringLiteral("Destination"), LinkDestination(ldd).toString()); } e->setAttribute(QStringLiteral("ExternalFileName"), g->getFileName()->c_str()); break; } case actionURI: { const LinkURI *u = static_cast(a); e->setAttribute(QStringLiteral("DestinationURI"), u->getURI().c_str()); } default:; } } DocumentData::~DocumentData() { qDeleteAll(m_embeddedFiles); delete (OptContentModel *)m_optContentModel; delete doc; } void DocumentData::init() { m_backend = Document::SplashBackend; paperColor = Qt::white; m_hints = 0; 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) { for (::OutlineItem *outlineItem : *items) { // iterate over every object in 'items' // 1. create element using outlineItem's title as tagName QString name = unicodeToQString(outlineItem->getTitle()); QDomElement item = docSyn->createElement(name); parent->appendChild(item); // 2. find the page the link refers to const ::LinkAction *a = outlineItem->getAction(); linkActionToTocItem(a, this, &item); item.setAttribute(QStringLiteral("Open"), QVariant((bool)outlineItem->isOpen()).toString()); // 3. recursively descend over children outlineItem->open(); const std::vector<::OutlineItem *> *children = outlineItem->getKids(); if (children) { addTocChildren(docSyn, &item, children); } } } void DocumentData::noitfyXRefReconstructed() { if (!xrefReconstructed) { xrefReconstructed = true; } if (xrefReconstructedCallback) { xrefReconstructedCallback(); } } FormWidget *FormFieldData::getFormWidget(const FormField *f) { return f->m_formData->fm; } FormFieldIconData *FormFieldIconData::getData(const FormFieldIcon &f) { return f.d_ptr; } } poppler-24.02.0/qt5/src/poppler-private.h000066400000000000000000000225761455701731300201640ustar00rootroot00000000000000/* 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) 2007-2009, 2011, 2014 by Pino Toscano * Copyright (C) 2011 Andreas Hartmetz * Copyright (C) 2011 Hib Eris * Copyright (C) 2012, 2013 Thomas Freitag * Copyright (C) 2013 Anthony Granger * Copyright (C) 2014 Bogdan Cristea * Copyright (C) 2014 Aki Koskinen * Copyright (C) 2016 Jakub Alba * Copyright (C) 2017 Christoph Cullmann * 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 João Netto * Copyright (C) 2019 Jan Grulich * Copyright (C) 2019 Alexander Volkov * Copyright (C) 2020 Philipp Knechtges * Copyright (C) 2021 Mahmoud Khalil * Copyright (C) 2021 Hubert Figuiere * Copyright (C) 2021 Georgiy Sgibnev . Work sponsored by lab50.net. * Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela * Inspired on code by * Copyright (C) 2004 by Albert Astals Cid * Copyright (C) 2004 by Enrico Ros * * 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_PRIVATE_H_ #define _POPPLER_PRIVATE_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "poppler-qt5.h" #include "poppler-embeddedfile-private.h" #include "poppler-qiodeviceinstream-private.h" class LinkDest; class FormWidget; class OutlineItem; 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); 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 GooString *QStringToGooString(const QString &s); GooString *QDateTimeToUnicodeGooString(const QDateTime &dt); void qt5ErrorFunction(ErrorCategory /*category*/, Goffset pos, const char *msg); Annot::AdditionalActionsType toPopplerAdditionalActionType(Annotation::AdditionalActionType type); class LinkDestinationData { public: LinkDestinationData(const LinkDest *l, const GooString *nd, Poppler::DocumentData *pdfdoc, bool external) : ld(l), namedDest(nd), doc(pdfdoc), externalDest(external) { } const LinkDest *ld; const GooString *namedDest; Poppler::DocumentData *doc; bool externalDest; }; class DocumentData : private GlobalParamsIniter { public: DocumentData(const QString &filePath, const std::optional &ownerPassword, const std::optional &userPassword) : GlobalParamsIniter(qt5ErrorFunction) { init(); m_device = nullptr; m_filePath = filePath; #ifdef _WIN32 doc = new PDFDoc((wchar_t *)filePath.utf16(), filePath.length(), ownerPassword, userPassword, nullptr, std::bind(&DocumentData::noitfyXRefReconstructed, this)); #else doc = new PDFDoc(std::make_unique(QFile::encodeName(filePath).constData()), ownerPassword, userPassword, nullptr, std::bind(&DocumentData::noitfyXRefReconstructed, this)); #endif } DocumentData(QIODevice *device, const std::optional &ownerPassword, const std::optional &userPassword) : GlobalParamsIniter(qt5ErrorFunction) { 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)); } DocumentData(const QByteArray &data, const std::optional &ownerPassword, const std::optional &userPassword) : GlobalParamsIniter(qt5ErrorFunction) { m_device = nullptr; 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)); } void init(); ~DocumentData(); DocumentData(const DocumentData &) = delete; DocumentData &operator=(const DocumentData &) = delete; void addTocChildren(QDomDocument *docSyn, QDomNode *parent, const std::vector<::OutlineItem *> *items); void setPaperColor(const QColor &color) { paperColor = color; } void fillMembers() { int numEmb = doc->getCatalog()->numEmbeddedFiles(); if (!(0 == numEmb)) { // we have some embedded documents, build the list for (int yalv = 0; yalv < numEmb; ++yalv) { std::unique_ptr fs = doc->getCatalog()->embeddedFile(yalv); m_embeddedFiles.append(new EmbeddedFile(*new EmbeddedFileData(std::move(fs)))); } } } /** * a method that is being called whenever PDFDoc's XRef is reconstructed * where we'll set xrefReconstructed flag and notify users of the * reconstruction event */ void noitfyXRefReconstructed(); static Document *checkDocument(DocumentData *doc); PDFDoc *doc; QString m_filePath; QIODevice *m_device; QByteArray fileContents; bool locked; Document::RenderBackend m_backend; QList m_embeddedFiles; QPointer m_optContentModel; QColor paperColor; int m_hints; #ifdef USE_CMS GfxLCMSProfilePtr m_sRGBProfile; GfxLCMSProfilePtr m_displayProfile; #endif bool xrefReconstructed; // notifies the user whenever the backend's PDFDoc XRef is reconstructed std::function xrefReconstructedCallback; }; class FontInfoData { public: FontInfoData() { isEmbedded = false; isSubset = false; type = FontInfo::unknown; } explicit FontInfoData(::FontInfo *fi) { if (fi->getName()) { fontName = fi->getName()->c_str(); } if (fi->getFile()) { fontFile = fi->getFile()->c_str(); } if (fi->getSubstituteName()) { fontSubstituteName = fi->getSubstituteName()->c_str(); } isEmbedded = fi->getEmbedded(); isSubset = fi->getSubset(); type = (Poppler::FontInfo::Type)fi->getType(); embRef = fi->getEmbRef(); } FontInfoData(const FontInfoData &fid) = default; FontInfoData &operator=(const FontInfoData &) = default; QString fontName; QString fontSubstituteName; QString fontFile; bool isEmbedded : 1; bool isSubset : 1; FontInfo::Type type; Ref embRef; }; class FontIteratorData { public: FontIteratorData(int startPage, DocumentData *dd) : fontInfoScanner(dd->doc, startPage), totalPages(dd->doc->getNumPages()), currentPage(qMax(startPage, 0) - 1) { } ~FontIteratorData() { } FontInfoScanner fontInfoScanner; int totalPages; int currentPage; }; class TextBoxData { public: TextBoxData() : nextWord(nullptr), hasSpaceAfter(false) { } QString text; QRectF bBox; TextBox *nextWord; QVector charBBoxes; // the boundingRect of each character bool hasSpaceAfter; }; class FormFieldData { public: FormFieldData(DocumentData *_doc, ::Page *p, ::FormWidget *w) : doc(_doc), page(p), fm(w) { } DocumentData *doc; ::Page *page; // Note for some signatures it can be null since there's signatures that don't belong to a given page ::FormWidget *fm; QRectF box; static POPPLER_QT5_EXPORT ::FormWidget *getFormWidget(const FormField *f); }; class FormFieldIcon; class FormFieldIconData { public: static POPPLER_QT5_EXPORT FormFieldIconData *getData(const FormFieldIcon &f); Dict *icon; }; } #endif poppler-24.02.0/qt5/src/poppler-ps-converter.cc000066400000000000000000000163541455701731300212740ustar00rootroot00000000000000/* poppler-ps-converter.cc: qt interface to poppler * Copyright (C) 2007, 2009, 2010, 2015, 2020, 2022, Albert Astals Cid * Copyright (C) 2008, Pino Toscano * Copyright (C) 2010 Hib Eris * Copyright (C) 2011 Glad Deschrijver * Copyright (C) 2012 Fabio D'Urso * 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 * 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 "poppler-qt5.h" #include "poppler-private.h" #include "poppler-converter-private.h" #include "PSOutputDev.h" static void outputToQIODevice(void *stream, const char *data, size_t len) { static_cast(stream)->write(data, len); } namespace Poppler { class PSConverterPrivate : public BaseConverterPrivate { public: PSConverterPrivate(); ~PSConverterPrivate() override; QList pageList; QString title; double hDPI; double vDPI; int rotate; int paperWidth; int paperHeight; int marginRight; int marginBottom; int marginLeft; int marginTop; PSConverter::PSOptions opts; void (*pageConvertedCallback)(int page, void *payload); void *pageConvertedPayload; }; 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) { } PSConverterPrivate::~PSConverterPrivate() = default; PSConverter::PSConverter(DocumentData *document) : BaseConverter(*new PSConverterPrivate()) { Q_D(PSConverter); d->document = document; } PSConverter::~PSConverter() { } void PSConverter::setPageList(const QList &pageList) { Q_D(PSConverter); d->pageList = pageList; } void PSConverter::setTitle(const QString &title) { Q_D(PSConverter); d->title = title; } void PSConverter::setHDPI(double hDPI) { Q_D(PSConverter); d->hDPI = hDPI; } void PSConverter::setVDPI(double vDPI) { Q_D(PSConverter); d->vDPI = vDPI; } void PSConverter::setRotate(int rotate) { Q_D(PSConverter); d->rotate = rotate; } void PSConverter::setPaperWidth(int paperWidth) { Q_D(PSConverter); d->paperWidth = paperWidth; } void PSConverter::setPaperHeight(int paperHeight) { Q_D(PSConverter); d->paperHeight = paperHeight; } void PSConverter::setRightMargin(int marginRight) { Q_D(PSConverter); d->marginRight = marginRight; } void PSConverter::setBottomMargin(int marginBottom) { Q_D(PSConverter); d->marginBottom = marginBottom; } void PSConverter::setLeftMargin(int marginLeft) { Q_D(PSConverter); d->marginLeft = marginLeft; } void PSConverter::setTopMargin(int marginTop) { Q_D(PSConverter); d->marginTop = marginTop; } void PSConverter::setStrictMargins(bool strictMargins) { Q_D(PSConverter); if (strictMargins) { d->opts |= StrictMargins; } else { d->opts &= ~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); if (forceRasterize) { d->opts |= ForceRasterization; } else { d->opts &= ~ForceRasterization; } } void PSConverter::setPSOptions(PSConverter::PSOptions options) { Q_D(PSConverter); d->opts = options; } PSConverter::PSOptions PSConverter::psOptions() const { Q_D(const PSConverter); return d->opts; } void PSConverter::setPageConvertedCallback(void (*callback)(int page, void *payload), void *payload) { Q_D(PSConverter); d->pageConvertedCallback = callback; d->pageConvertedPayload = payload; } static bool annotDisplayDecideCbk(Annot *annot, void *user_data) { if (annot->getType() == Annot::typeWidget) { return true; // Never hide forms } else { return *(bool *)user_data; } } bool PSConverter::convert() { Q_D(PSConverter); d->lastError = NoError; Q_ASSERT(!d->pageList.isEmpty()); Q_ASSERT(d->paperWidth != -1); Q_ASSERT(d->paperHeight != -1); if (d->document->locked) { d->lastError = FileLockedError; return false; } QIODevice *dev = d->openDevice(); if (!dev) { d->lastError = OpenOutputError; return false; } QByteArray pstitle8Bit = d->title.toLocal8Bit(); char *pstitlechar; if (!d->title.isEmpty()) { pstitlechar = pstitle8Bit.data(); } else { pstitlechar = nullptr; } std::vector pages; foreach (int page, d->pageList) { pages.push_back(page); } 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; double yScale = ((double)d->paperHeight - (double)d->marginBottom - (double)d->marginTop) / (double)d->paperHeight; psOut->setScale(xScale, yScale); } if (psOut->isOk()) { bool isPrinting = (d->opts & Printing) ? true : false; bool showAnnotations = (d->opts & HideAnnotations) ? false : true; foreach (int page, d->pageList) { d->document->doc->displayPage(psOut, page, d->hDPI, d->vDPI, d->rotate, false, true, isPrinting, nullptr, nullptr, annotDisplayDecideCbk, &showAnnotations, true); if (d->pageConvertedCallback) { (*d->pageConvertedCallback)(page, d->pageConvertedPayload); } } delete psOut; d->closeDevice(); return true; } else { delete psOut; d->closeDevice(); return false; } } } poppler-24.02.0/qt5/src/poppler-qiodeviceinstream-private.h000066400000000000000000000030071455701731300236610ustar00rootroot00000000000000/* poppler-qiodeviceinstream-private.h: Qt5 interface to poppler * Copyright (C) 2019 Alexander Volkov * * 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_QIODEVICEINSTREAM_PRIVATE_H #define POPPLER_QIODEVICEINSTREAM_PRIVATE_H #include "Object.h" #include "Stream.h" class QIODevice; namespace Poppler { class QIODeviceInStream : public BaseSeekInputStream { public: QIODeviceInStream(QIODevice *device, Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA); ~QIODeviceInStream() override; BaseStream *copy() override; Stream *makeSubStream(Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA) override; private: Goffset currentPos() const override; void setCurrentPos(Goffset offset) override; Goffset read(char *buffer, Goffset count) override; QIODevice *m_device; }; } #endif poppler-24.02.0/qt5/src/poppler-qiodeviceinstream.cc000066400000000000000000000034311455701731300223500ustar00rootroot00000000000000/* poppler-qiodeviceinstream.cc: Qt5 interface to poppler * Copyright (C) 2019 Alexander Volkov * * 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 "poppler-qiodeviceinstream-private.h" #include #include namespace Poppler { QIODeviceInStream::QIODeviceInStream(QIODevice *device, Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA) : BaseSeekInputStream(startA, limitedA, lengthA, std::move(dictA)), m_device(device) { } QIODeviceInStream::~QIODeviceInStream() { close(); } BaseStream *QIODeviceInStream::copy() { return new QIODeviceInStream(m_device, start, limited, length, dict.copy()); } Stream *QIODeviceInStream::makeSubStream(Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA) { return new QIODeviceInStream(m_device, startA, limitedA, lengthA, std::move(dictA)); } Goffset QIODeviceInStream::currentPos() const { return m_device->pos(); } void QIODeviceInStream::setCurrentPos(Goffset offset) { m_device->seek(offset); } Goffset QIODeviceInStream::read(char *buffer, Goffset count) { return m_device->read(buffer, count); } } poppler-24.02.0/qt5/src/poppler-qiodeviceoutstream-private.h000066400000000000000000000027711455701731300240710ustar00rootroot00000000000000/* poppler-qiodevicestream-private.h: Qt5 interface to poppler * Copyright (C) 2008, Pino Toscano * Copyright (C) 2013 Adrian Johnson * Copyright (C) 2021, Albert Astals Cid * Copyright (C) 2021, Even Rouault * * 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_QIODEVICESTREAM_PRIVATE_H #define POPPLER_QIODEVICESTREAM_PRIVATE_H #include "Object.h" #include "Stream.h" class QIODevice; namespace Poppler { class QIODeviceOutStream : public OutStream { public: explicit QIODeviceOutStream(QIODevice *device); ~QIODeviceOutStream() override; void close() override; Goffset getPos() override; void put(char c) override; void printf(const char *format, ...) override GCC_PRINTF_FORMAT(2, 3); private: QIODevice *m_device; }; } #endif poppler-24.02.0/qt5/src/poppler-qiodeviceoutstream.cc000066400000000000000000000040471455701731300225550ustar00rootroot00000000000000/* poppler-qiodevicestream.cc: Qt5 interface to poppler * Copyright (C) 2008, Pino Toscano * Copyright (C) 2013 Adrian Johnson * Copyright (C) 2020, 2021 Albert Astals Cid * Copyright (C) 2021, Even Rouault * * 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 "poppler-qiodeviceoutstream-private.h" #include #include namespace Poppler { QIODeviceOutStream::QIODeviceOutStream(QIODevice *device) : m_device(device) { } QIODeviceOutStream::~QIODeviceOutStream() { } void QIODeviceOutStream::close() { } Goffset QIODeviceOutStream::getPos() { return m_device->pos(); } void QIODeviceOutStream::put(char c) { m_device->putChar(c); } 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) { va_list ap_copy; va_copy(ap_copy, ap); const size_t size = vsnprintf(nullptr, 0, format, ap_copy) + 1; va_end(ap_copy); *buf_ptr = new char[size]; return qvsnprintf(*buf_ptr, size, format, ap); } void QIODeviceOutStream::printf(const char *format, ...) { va_list ap; va_start(ap, format); char *buf; const size_t bufsize = poppler_vasprintf(&buf, format, ap); va_end(ap); m_device->write(buf, bufsize); delete[] buf; } } poppler-24.02.0/qt5/src/poppler-qt5.h000066400000000000000000002143241455701731300172150ustar00rootroot00000000000000/* poppler-qt.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 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_QT_H__ #define __POPPLER_QT_H__ #include #include "poppler-annotation.h" #include "poppler-link.h" #include "poppler-optcontent.h" #include "poppler-page-transition.h" #include #include #include #include #include "poppler-export.h" class EmbFile; class Sound; class AnnotMovie; /** The %Poppler Qt5 binding. */ namespace Poppler { class Document; class DocumentData; class PageData; class FormField; class FormFieldSignature; class TextBoxData; class PDFConverter; class PSConverter; struct OutlineItemData; /** Debug/error function. This function type is used for debugging & error output; the first parameter is the actual message, the second is the unaltered closure argument which was passed to the setDebugErrorFunction call. \since 0.16 */ using PopplerDebugFunc = void (*)(const QString & /*message*/, const QVariant & /*closure*/); /** Set a new debug/error output function. If not set, by default error and debug messages will be sent to the Qt \p qDebug() function. \param debugFunction the new debug function \param closure user data which will be passes as-is to the debug function \since 0.16 */ POPPLER_QT5_EXPORT void setDebugErrorFunction(PopplerDebugFunc debugFunction, const QVariant &closure); /** Describes the physical location of text on a document page This very simple class describes the physical location of text on the page. It consists of - a QString that contains the text - a QRectF that gives a box that describes where on the page the text is found. */ class POPPLER_QT5_EXPORT TextBox { friend class Page; public: /** The default constructor sets the \p text and the rectangle that contains the text. Coordinates for the \p bBox are in points = 1/72 of an inch. */ TextBox(const QString &text, const QRectF &bBox); /** Destructor. */ ~TextBox(); /** Returns the text of this text box */ QString text() const; /** Returns the position of the text, in point, i.e., 1/72 of an inch \since 0.8 */ QRectF boundingBox() const; /** Returns the pointer to the next text box, if there is one. Otherwise, it returns a null pointer. */ TextBox *nextWord() const; /** Returns the bounding box of the \p i -th characted of the word. */ QRectF charBoundingBox(int i) const; /** Returns whether there is a space character after this text box */ bool hasSpaceAfter() const; private: Q_DISABLE_COPY(TextBox) TextBoxData *m_data; }; class FontInfoData; /** Container class for information about a font within a PDF document */ class POPPLER_QT5_EXPORT FontInfo { friend class Document; public: /** The type of font. */ enum Type { unknown, Type1, Type1C, Type1COT, Type3, TrueType, TrueTypeOT, CIDType0, CIDType0C, CIDType0COT, CIDTrueType, CIDTrueTypeOT }; /// \cond PRIVATE /** Create a new font information container. */ FontInfo(); /** Create a new font information container. */ explicit FontInfo(const FontInfoData &fid); /// \endcond /** Copy constructor. */ FontInfo(const FontInfo &fi); /** Destructor. */ ~FontInfo(); /** The name of the font. Can be a null QString if the font has no name */ QString name() const; /** The name of the substitute font. Can be a null QString if the font has no substitute font @since 0.80 */ QString substituteName() const; /** The path of the font file used to represent this font on this system, or a null string is the font is embedded */ QString file() const; /** Whether the font is embedded in the file, or not \return true if the font is embedded */ bool isEmbedded() const; /** Whether the font provided is only a subset of the full font or not. This only has meaning if the font is embedded. \return true if the font is only a subset */ bool isSubset() const; /** The type of font encoding \return a enumerated value corresponding to the font encoding used \sa typeName for a string equivalent */ Type type() const; /** The name of the font encoding used \note if you are looking for the name of the font (as opposed to the encoding format used), you probably want name(). \sa type for a enumeration version */ QString typeName() const; /** Standard assignment operator */ FontInfo &operator=(const FontInfo &fi); private: FontInfoData *m_data; }; class FontIteratorData; /** Iterator for reading the fonts in a document. FontIterator provides a Java-style iterator for reading the fonts in a document. You can use it in the following way: \code Poppler::FontIterator* it = doc->newFontIterator(); while (it->hasNext()) { QList fonts = it->next(); // do something with the fonts } // after doing the job, the iterator must be freed delete it; \endcode \since 0.12 */ class POPPLER_QT5_EXPORT FontIterator { friend class Document; friend class DocumentData; public: /** Destructor. */ ~FontIterator(); /** Returns the fonts of the current page and then advances the iterator to the next page. */ QList next(); /** Checks whether there is at least one more page to iterate, ie returns false when the iterator is beyond the last page. */ bool hasNext() const; /** Returns the current page where the iterator is. */ int currentPage() const; private: Q_DISABLE_COPY(FontIterator) FontIterator(int, DocumentData *dd); FontIteratorData *d; }; class EmbeddedFileData; /** Container class for an embedded file with a PDF document */ class POPPLER_QT5_EXPORT EmbeddedFile { friend class DocumentData; friend class AnnotationPrivate; public: /// \cond PRIVATE explicit EmbeddedFile(EmbFile *embfile); /// \endcond /** Destructor. */ ~EmbeddedFile(); /** The name associated with the file */ QString name() const; /** The description associated with the file, if any. This will return an empty QString if there is no description element */ QString description() const; /** The size of the file. This will return < 0 if there is no size element */ int size() const; /** The modification date for the embedded file, if known. */ QDateTime modDate() const; /** The creation date for the embedded file, if known. */ QDateTime createDate() const; /** The MD5 checksum of the file. This will return an empty QByteArray if there is no checksum element. */ QByteArray checksum() const; /** The MIME type of the file, if known. \since 0.8 */ QString mimeType() const; /** The data as a byte array */ QByteArray data(); /** Is the embedded file valid? \since 0.12 */ bool isValid() const; /** A QDataStream for the actual data? */ // QDataStream dataStream() const; private: Q_DISABLE_COPY(EmbeddedFile) explicit EmbeddedFile(EmbeddedFileData &dd); EmbeddedFileData *m_embeddedFile; }; /** \brief A page in a document. The Page class represents a single page within a PDF document. You cannot construct a Page directly, but you have to use the Document functions that return a new Page out of an index or a label. */ class POPPLER_QT5_EXPORT Page { friend class Document; public: /** Destructor. */ ~Page(); /** The type of rotation to apply for an operation */ enum Rotation { Rotate0 = 0, ///< Do not rotate Rotate90 = 1, ///< Rotate 90 degrees clockwise Rotate180 = 2, ///< Rotate 180 degrees Rotate270 = 3 ///< Rotate 270 degrees clockwise (90 degrees counterclockwise) }; /** The kinds of page actions */ enum PageAction { Opening, ///< The action when a page is "opened" Closing ///< The action when a page is "closed" }; /** How the text is going to be returned \since 0.16 */ enum TextLayout { PhysicalLayout, ///< The text is layouted to resemble the real page layout RawOrderLayout ///< The text is returned without any type of processing }; /** Additional flags for the renderToPainter method \since 0.16 */ enum PainterFlag { NoPainterFlags = 0x00000000, ///< \since 0.63 /** Do not save/restore the caller-owned painter. renderToPainter() by default preserves, using save() + restore(), the state of the painter specified; if this is not needed, this flag can avoid this job */ DontSaveAndRestore = 0x00000001 }; Q_DECLARE_FLAGS(PainterFlags, PainterFlag) /** Render the page to a QImage using the current \link Document::renderBackend() Document renderer\endlink. If \p x = \p y = \p w = \p h = -1, the method will automatically compute the size of the image from the horizontal and vertical resolutions specified in \p xres and \p yres. Otherwise, the method renders only a part of the page, specified by the parameters (\p x, \p y, \p w, \p h) in pixel coordinates. The returned QImage then has size (\p w, \p h), independent of the page size. \param x specifies the left x-coordinate of the box, in pixels. \param y specifies the top y-coordinate of the box, in pixels. \param w specifies the width of the box, in pixels. \param h specifies the height of the box, in pixels. \param xres horizontal resolution of the graphics device, in dots per inch \param yres vertical resolution of the graphics device, in dots per inch \param rotate how to rotate the page \warning The parameter (\p x, \p y, \p w, \p h) are not well-tested. Unusual or meaningless parameters may lead to rather unexpected results. \returns a QImage of the page, or a null image on failure. \since 0.6 */ QImage renderToImage(double xres = 72.0, double yres = 72.0, int x = -1, int y = -1, int w = -1, int h = -1, Rotation rotate = Rotate0) const; /** Partial Update renderToImage callback. This function type is used for doing partial rendering updates; the first parameter is the image as rendered up to now, the second is the unaltered closure argument which was passed to the renderToImage call. \since 0.62 */ using RenderToImagePartialUpdateFunc = void (*)(const QImage & /*image*/, const QVariant & /*closure*/); /** Partial Update query renderToImage callback. This function type is used for query if the partial rendering update should happen; the parameter is the unaltered closure argument which was passed to the renderToImage call. \since 0.62 */ using ShouldRenderToImagePartialQueryFunc = bool (*)(const QVariant & /*closure*/); /** Render the page to a QImage using the current \link Document::renderBackend() Document renderer\endlink. If \p x = \p y = \p w = \p h = -1, the method will automatically compute the size of the image from the horizontal and vertical resolutions specified in \p xres and \p yres. Otherwise, the method renders only a part of the page, specified by the parameters (\p x, \p y, \p w, \p h) in pixel coordinates. The returned QImage then has size (\p w, \p h), independent of the page size. \param x specifies the left x-coordinate of the box, in pixels. \param y specifies the top y-coordinate of the box, in pixels. \param w specifies the width of the box, in pixels. \param h specifies the height of the box, in pixels. \param xres horizontal resolution of the graphics device, in dots per inch \param yres vertical resolution of the graphics device, in dots per inch \param rotate how to rotate the page \param partialUpdateCallback callback that will be called to report a partial rendering update \param shouldDoPartialUpdateCallback callback that will be called to ask if a partial rendering update is wanted. This exists because doing a partial rendering update needs to copy the image buffer so if it is not wanted it is better skipped early. \param payload opaque structure that will be passed back to partialUpdateCallback and shouldDoPartialUpdateCallback. \warning The parameter (\p x, \p y, \p w, \p h) are not well-tested. Unusual or meaningless parameters may lead to rather unexpected results. \returns a QImage of the page, or a null image on failure. \since 0.62 */ QImage renderToImage(double xres, double yres, int x, int y, int w, int h, Rotation rotate, RenderToImagePartialUpdateFunc partialUpdateCallback, ShouldRenderToImagePartialQueryFunc shouldDoPartialUpdateCallback, const QVariant &payload) const; /** Abort query function callback. This function type is used for query if the current rendering/text extraction should be cancelled. \since 0.63 */ using ShouldAbortQueryFunc = bool (*)(const QVariant & /*closure*/); /** Render the page to a QImage using the current \link Document::renderBackend() Document renderer\endlink. If \p x = \p y = \p w = \p h = -1, the method will automatically compute the size of the image from the horizontal and vertical resolutions specified in \p xres and \p yres. Otherwise, the method renders only a part of the page, specified by the parameters (\p x, \p y, \p w, \p h) in pixel coordinates. The returned QImage then has size (\p w, \p h), independent of the page size. \param x specifies the left x-coordinate of the box, in pixels. \param y specifies the top y-coordinate of the box, in pixels. \param w specifies the width of the box, in pixels. \param h specifies the height of the box, in pixels. \param xres horizontal resolution of the graphics device, in dots per inch \param yres vertical resolution of the graphics device, in dots per inch \param rotate how to rotate the page \param partialUpdateCallback callback that will be called to report a partial rendering update \param shouldDoPartialUpdateCallback callback that will be called to ask if a partial rendering update is wanted. This exists because doing a partial rendering update needs to copy the image buffer so if it is not wanted it is better skipped early. \param shouldAbortRenderCallback callback that will be called to ask if the rendering should be cancelled. \param payload opaque structure that will be passed back to partialUpdateCallback, shouldDoPartialUpdateCallback and shouldAbortRenderCallback. \warning The parameter (\p x, \p y, \p w, \p h) are not well-tested. Unusual or meaningless parameters may lead to rather unexpected results. \returns a QImage of the page, or a null image on failure. \since 0.63 */ QImage renderToImage(double xres, double yres, int x, int y, int w, int h, Rotation rotate, RenderToImagePartialUpdateFunc partialUpdateCallback, ShouldRenderToImagePartialQueryFunc shouldDoPartialUpdateCallback, ShouldAbortQueryFunc shouldAbortRenderCallback, const QVariant &payload) const; /** Render the page to the specified QPainter using the current \link Document::renderBackend() Document renderer\endlink. If \p x = \p y = \p w = \p h = -1, the method will automatically compute the size of the page area from the horizontal and vertical resolutions specified in \p xres and \p yres. Otherwise, the method renders only a part of the page, specified by the parameters (\p x, \p y, \p w, \p h) in pixel coordinates. \param painter the painter to paint on \param x specifies the left x-coordinate of the box, in pixels. \param y specifies the top y-coordinate of the box, in pixels. \param w specifies the width of the box, in pixels. \param h specifies the height of the box, in pixels. \param xres horizontal resolution of the graphics device, in dots per inch \param yres vertical resolution of the graphics device, in dots per inch \param rotate how to rotate the page \param flags additional painter flags \warning The parameter (\p x, \p y, \p w, \p h) are not well-tested. Unusual or meaningless parameters may lead to rather unexpected results. \returns whether the painting succeeded \note This method is only supported for the QPainterOutputDev \since 0.16 */ bool renderToPainter(QPainter *painter, double xres = 72.0, double yres = 72.0, int x = -1, int y = -1, int w = -1, int h = -1, Rotation rotate = Rotate0, PainterFlags flags = NoPainterFlags) const; /** Get the page thumbnail if it exists. \return a QImage of the thumbnail, or a null image if the PDF does not contain one for this page \since 0.12 */ QImage thumbnail() const; /** Returns the text that is inside a specified rectangle \param rect the rectangle specifying the area of interest, with coordinates given in points, i.e., 1/72th of an inch. If rect is null, all text on the page is given \since 0.16 **/ QString text(const QRectF &rect, TextLayout textLayout) const; /** Returns the text that is inside a specified rectangle. The text is returned using the physical layout of the page \param rect the rectangle specifying the area of interest, with coordinates given in points, i.e., 1/72th of an inch. If rect is null, all text on the page is given **/ QString text(const QRectF &rect) const; /** The starting point for a search */ enum SearchDirection { FromTop, ///< Start sorting at the top of the document NextResult, ///< Find the next result, moving "down the page" PreviousResult ///< Find the previous result, moving "up the page" }; /** The type of search to perform */ enum SearchMode { CaseSensitive, ///< Case differences cause no match in searching CaseInsensitive ///< Case differences are ignored in matching }; /** Flags to modify the search behaviour \since 0.31 */ enum SearchFlag { NoSearchFlags = 0x00000000, ///< since 0.63 IgnoreCase = 0x00000001, ///< Case differences are ignored WholeWords = 0x00000002, ///< Only whole words are matched IgnoreDiacritics = 0x00000004, ///< Diacritic differences (eg. accents, umlauts, diaeresis) are ignored. \since 0.73 ///< This option will have no effect if the search term contains characters which ///< are not pure ascii. AcrossLines = 0x00000008 ///< Allows to match on text spanning from end of a line to the next line. ///< 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. \since 21.05.0 }; Q_DECLARE_FLAGS(SearchFlags, SearchFlag) /** Returns true if the specified text was found. \param text the text the search \param rectXXX in all directions is used to return where the text was found, for NextResult and PreviousResult indicates where to continue searching for \param direction in which direction do the search \param caseSensitive be case sensitive? \param rotate the rotation to apply for the search order \since 0.14 **/ Q_DECL_DEPRECATED bool search(const QString &text, double &rectLeft, double &rectTop, double &rectRight, double &rectBottom, SearchDirection direction, SearchMode caseSensitive, Rotation rotate = Rotate0) const; /** Returns true if the specified text was found. \param text the text the search \param rectXXX in all directions is used to return where the text was found, for NextResult and PreviousResult indicates where to continue searching for \param direction in which direction do the search \param flags the flags to consider during matching \param rotate the rotation to apply for the search order \since 0.31 **/ bool search(const QString &text, double &sLeft, double &sTop, double &sRight, double &sBottom, SearchDirection direction, SearchFlags flags = NoSearchFlags, Rotation rotate = Rotate0) const; /** Returns a list of all occurrences of the specified text on the page. \param text the text to search \param caseSensitive whether to be case sensitive \param rotate the rotation to apply for the search order \warning Do not use the returned QRectF as arguments of another search call because of truncation issues if qreal is defined as float. \since 0.22 **/ Q_DECL_DEPRECATED QList search(const QString &text, SearchMode caseSensitive, Rotation rotate = Rotate0) const; /** Returns a list of all occurrences of the specified text on the page. if SearchFlags::AcrossLines is given in \param flags, then rects may just be parts of the text itself if it's split between multiple lines. \param text the text to search \param flags the flags to consider during matching \param rotate the rotation to apply for the search order \warning Do not use the returned QRectF as arguments of another search call because of truncation issues if qreal is defined as float. \since 0.31 **/ QList search(const QString &text, SearchFlags flags = NoSearchFlags, Rotation rotate = Rotate0) const; /** Returns a list of text of the page This method returns a QList of TextBoxes that contain all the text of the page, with roughly one text word of text per TextBox item. For text written in western languages (left-to-right and up-to-down), the QList contains the text in the proper order. \note The caller owns the text boxes and they should be deleted when no longer required. \warning This method is not tested with Asian scripts */ QList textList(Rotation rotate = Rotate0) const; /** Returns a list of text of the page This method returns a QList of TextBoxes that contain all the text of the page, with roughly one text word of text per TextBox item. For text written in western languages (left-to-right and up-to-down), the QList contains the text in the proper order. \param shouldAbortExtractionCallback callback that will be called to ask if the text extraction should be cancelled. \param closure opaque structure that will be passed back to shouldAbortExtractionCallback. \note The caller owns the text boxes and they should be deleted when no longer required. \warning This method is not tested with Asian scripts // \since 0.63 */ QList textList(Rotation rotate, ShouldAbortQueryFunc shouldAbortExtractionCallback, const QVariant &closure) const; /** \return The dimensions (cropbox) of the page, in points (i.e. 1/72th of an inch) */ QSizeF pageSizeF() const; /** \return The dimensions (cropbox) of the page, in points (i.e. 1/72th of an inch) */ QSize pageSize() const; /** Returns the transition of this page \returns a pointer to a PageTransition structure that defines how transition to this page shall be performed. \note The PageTransition structure is owned by this page, and will automatically be destroyed when this page class is destroyed. **/ PageTransition *transition() const; /** Gets the page action specified, or NULL if there is no action. \since 0.6 **/ Link *action(PageAction act) const; /** Types of orientations that are possible */ enum Orientation { Landscape, ///< Landscape orientation (portrait, with 90 degrees clockwise rotation ) Portrait, ///< Normal portrait orientation Seascape, ///< Seascape orientation (portrait, with 270 degrees clockwise rotation) UpsideDown ///< Upside down orientation (portrait, with 180 degrees rotation) }; /** The orientation of the page */ Orientation orientation() const; /** The default CTM */ void defaultCTM(double *CTM, double dpiX, double dpiY, int rotate, bool upsideDown); /** Gets the links of the page */ QList links() const; /** Returns the annotations of the page \note If you call this method twice, you get different objects pointing to the same annotations (see Annotation). The caller owns the returned objects and they should be deleted when no longer required. */ QList annotations() const; /** Returns the annotations of the page \param subtypes the subtypes of annotations you are interested in \note If you call this method twice, you get different objects pointing to the same annotations (see Annotation). The caller owns the returned objects and they should be deleted when no longer required. \since 0.28 */ QList annotations(const QSet &subtypes) const; /** Adds an annotation to the page \note Ownership of the annotation object stays with the caller, who can delete it at any time. \since 0.20 */ void addAnnotation(const Annotation *ann); /** Removes an annotation from the page and destroys the annotation object \note There mustn't be other Annotation objects pointing this annotation \since 0.20 */ void removeAnnotation(const Annotation *ann); /** Returns the form fields on the page The caller gets the ownership of the returned objects. \since 0.6 */ QList formFields() const; /** Returns the page duration. That is the time, in seconds, that the page should be displayed before the presentation automatically advances to the next page. Returns < 0 if duration is not set. \since 0.6 */ double duration() const; /** Returns the label of the page, or a null string is the page has no label. \since 0.6 **/ QString label() const; /** Returns the index of the page. \since 0.70 **/ int index() const; private: Q_DISABLE_COPY(Page) Page(DocumentData *doc, int index); PageData *m_page; }; /** \brief Item in the outline of a PDF document Represents an item in the outline of PDF document, i.e. a name, an internal or external link and a set of child items. \since 0.74 **/ class POPPLER_QT5_EXPORT OutlineItem { friend class Document; public: /** Constructs a null item, i.e. one that does not represent a valid item in the outline of some PDF document. **/ OutlineItem(); ~OutlineItem(); OutlineItem(const OutlineItem &other); OutlineItem &operator=(const OutlineItem &other); OutlineItem(OutlineItem &&other) noexcept; OutlineItem &operator=(OutlineItem &&other) noexcept; /** Indicates whether an item is null, i.e. whether it does not represent a valid item in the outline of some PDF document. **/ bool isNull() const; /** The name of the item which should be displayed to the user. **/ QString name() const; /** Indicates whether the item should initially be display in an expanded or collapsed state. **/ bool isOpen() const; /** The destination referred to by this item. \returns a shared pointer to an immutable link destination **/ QSharedPointer destination() const; /** The external file name of the document to which the \see destination refers \returns a string with the external file name or an empty string if there is none */ QString externalFileName() const; /** The URI to which the item links \returns a string with the URI which this item links or an empty string if there is none **/ QString uri() const; /** Determines if this item has any child items \returns true if there are any child items **/ bool hasChildren() const; /** Gets the child items of this item \returns a vector outline items, empty if there are none **/ QVector children() const; private: explicit OutlineItem(OutlineItemData *data); OutlineItemData *m_data; }; /** \brief PDF document. The Document class represents a PDF document: its pages, and all the global properties, metadata, etc. \section ownership Ownership of the returned objects All the functions that returns class pointers create new object, and the responsibility of those is given to the callee. The only exception is \link Poppler::Page::transition() Page::transition()\endlink. \section document-loading Loading To get a Document, you have to load it via the load() & loadFromData() functions. In all the functions that have passwords as arguments, they \b must be Latin1 encoded. If you have a password that is a UTF-8 string, you need to use QString::toLatin1() (or similar) to convert the password first. If you have a UTF-8 character array, consider converting it to a QString first (QString::fromUtf8(), or similar) before converting to Latin1 encoding. \section document-rendering Rendering To render pages of a document, you have different Document functions to set various options. \subsection document-rendering-backend Backends %Poppler offers a different backends for rendering the pages. Currently there are two backends (see #RenderBackend), but only the Splash engine works well and has been tested. The available rendering backends can be discovered via availableRenderBackends(). The current rendering backend can be changed using setRenderBackend(). Please note that setting a backend not listed in the available ones will always result in null QImage's. \section document-cms Color management support %Poppler, if compiled with this support, provides functions to handle color profiles. To know whether the %Poppler version you are using has support for color management, you can query Poppler::isCmsAvailable(). In case it is not available, all the color management-related functions will either do nothing or return null. */ class POPPLER_QT5_EXPORT Document { friend class Page; friend class DocumentData; public: /** The page mode */ enum PageMode { UseNone, ///< No mode - neither document outline nor thumbnail images are visible UseOutlines, ///< Document outline visible UseThumbs, ///< Thumbnail images visible FullScreen, ///< Fullscreen mode (no menubar, windows controls etc) UseOC, ///< Optional content group panel visible UseAttach ///< Attachments panel visible }; /** The page layout */ enum PageLayout { NoLayout, ///< Layout not specified SinglePage, ///< Display a single page OneColumn, ///< Display a single column of pages TwoColumnLeft, ///< Display the pages in two columns, with odd-numbered pages on the left TwoColumnRight, ///< Display the pages in two columns, with odd-numbered pages on the right TwoPageLeft, ///< Display the pages two at a time, with odd-numbered pages on the left TwoPageRight ///< Display the pages two at a time, with odd-numbered pages on the right }; /** The render backends available \since 0.6 */ enum RenderBackend { SplashBackend, ///< Splash backend ArthurBackend, ///< \deprecated The old name of the QPainter backend QPainterBackend = ArthurBackend ///< @since 20.11 }; /** The render hints available \since 0.6 */ enum RenderHint { Antialiasing = 0x00000001, ///< Antialiasing for graphics TextAntialiasing = 0x00000002, ///< Antialiasing for text TextHinting = 0x00000004, ///< Hinting for text \since 0.12.1 TextSlightHinting = 0x00000008, ///< Lighter hinting for text when combined with TextHinting \since 0.18 OverprintPreview = 0x00000010, ///< Overprint preview \since 0.22 ThinLineSolid = 0x00000020, ///< Enhance thin lines solid \since 0.24 ThinLineShape = 0x00000040, ///< Enhance thin lines shape. Wins over ThinLineSolid \since 0.24 IgnorePaperColor = 0x00000080, ///< Do not compose with the paper color \since 0.35 HideAnnotations = 0x00000100 ///< Do not render annotations \since 0.60 }; Q_DECLARE_FLAGS(RenderHints, RenderHint) /** Form types \since 0.22 */ enum FormType { NoForm, ///< Document doesn't contain forms AcroForm, ///< AcroForm XfaForm ///< Adobe XML Forms Architecture (XFA), currently unsupported }; /** Set a color display profile for the current document. \param outputProfileA is a \c cmsHPROFILE of the LCMS library. \note This should be called before any rendering happens. \note It is assumed that poppler takes over the owernship of the corresponding cmsHPROFILE. In particular, it is no longer the caller's responsibility to close the profile after use. \since 0.12 */ void setColorDisplayProfile(void *outputProfileA); /** Set a color display profile for the current document. \param name is the name of the display profile to set. \note This should be called before any rendering happens. \since 0.12 */ void setColorDisplayProfileName(const QString &name); /** Return the current RGB profile. \return a \c cmsHPROFILE of the LCMS library. \note The returned profile stays a property of poppler and shall NOT be closed by the user. It's existence is guaranteed for as long as this instance of the Document class is not deleted. \since 0.12 */ void *colorRgbProfile() const; /** Return the current display profile. \return a \c cmsHPROFILE of the LCMS library. \note The returned profile stays a property of poppler and shall NOT be closed by the user. It's existence is guaranteed for as long as this instance of the Document class is not deleted. \since 0.12 */ void *colorDisplayProfile() const; /** Load the document from a file on disk \param filePath the name (and path, if required) of the file to load \param ownerPassword the Latin1-encoded owner password to use in loading the file \param userPassword the Latin1-encoded user ("open") password to use in loading the file \return the loaded document, or NULL on error \note The caller owns the pointer to Document, and this should be deleted when no longer required. \warning The returning document may be locked if a password is required to open the file, and one is not provided (as the userPassword). */ static Document *load(const QString &filePath, const QByteArray &ownerPassword = QByteArray(), const QByteArray &userPassword = QByteArray()); /** Load the document from a device \param device the device of the data to load \param ownerPassword the Latin1-encoded owner password to use in loading the file \param userPassword the Latin1-encoded user ("open") password to use in loading the file \return the loaded document, or NULL on error \note The caller owns the pointer to Document, and this should be deleted when no longer required. \note The ownership of the device stays with the caller. \note if the file is on disk it is recommended to use the other load overload since it is less resource intensive \warning The returning document may be locked if a password is required to open the file, and one is not provided (as the userPassword). \since 0.85 */ static Document *load(QIODevice *device, const QByteArray &ownerPassword = QByteArray(), const QByteArray &userPassword = QByteArray()); /** Load the document from memory \param fileContents the file contents. They are copied so there is no need to keep the byte array around for the full life time of the document. \param ownerPassword the Latin1-encoded owner password to use in loading the file \param userPassword the Latin1-encoded user ("open") password to use in loading the file \return the loaded document, or NULL on error \note The caller owns the pointer to Document, and this should be deleted when no longer required. \warning The returning document may be locked if a password is required to open the file, and one is not provided (as the userPassword). \since 0.6 */ static Document *loadFromData(const QByteArray &fileContents, const QByteArray &ownerPassword = QByteArray(), const QByteArray &userPassword = QByteArray()); /** Get a specified Page Note that this follows the PDF standard of being zero based - if you want the first page, then you need an index of zero. The caller gets the ownership of the returned object. This function can return nullptr if for some reason the page can't be properly parsed. \param index the page number index \warning The Page object returned by this method internally stores a pointer to the document that it was created from. This pointer will go stale if you delete the Document object. Therefore the Document object needs to be kept alive as long as you want to use the Page object. */ Page *page(int index) const; /** \overload The intent is that you can pass in a label like \c "ix" and get the page with that label (which might be in the table of contents), or pass in \c "1" and get the page that the user expects (which might not be the first page, if there is a title page and a table of contents). \param label the page label */ Page *page(const QString &label) const; /** The number of pages in the document */ int numPages() const; /** The type of mode that should be used by the application when the document is opened. Note that while this is called page mode, it is really viewer application mode. */ PageMode pageMode() const; /** The layout that pages should be shown in when the document is first opened. This basically describes how pages are shown relative to each other. */ PageLayout pageLayout() const; /** The predominant reading order for text as supplied by the document's viewer preferences. \since 0.26 */ Qt::LayoutDirection textDirection() const; /** Provide the passwords required to unlock the document \param ownerPassword the Latin1-encoded owner password to use in loading the file \param userPassword the Latin1-encoded user ("open") password to use in loading the file */ bool unlock(const QByteArray &ownerPassword, const QByteArray &userPassword); /** Determine if the document is locked */ bool isLocked() const; /** The date associated with the document You would use this method with something like: \code QDateTime created = m_doc->date("CreationDate"); QDateTime modified = m_doc->date("ModDate"); \endcode The available dates are: - CreationDate: the date of creation of the document - ModDate: the date of the last change in the document \param type the type of date that is required */ QDateTime date(const QString &type) const; /** Set the Info dict date entry specified by \param key to \param val \returns true on success, false on failure */ bool setDate(const QString &key, const QDateTime &val); /** The date of the creation of the document */ QDateTime creationDate() const; /** Set the creation date of the document to \param val \returns true on success, false on failure */ bool setCreationDate(const QDateTime &val); /** The date of the last change in the document */ QDateTime modificationDate() const; /** Set the modification date of the document to \param val \returns true on success, false on failure */ bool setModificationDate(const QDateTime &val); /** Get specified information associated with the document You would use this method with something like: \code QString title = m_doc->info("Title"); QString subject = m_doc->info("Subject"); \endcode In addition to \c Title and \c Subject, other information that may be available include \c Author, \c Keywords, \c Creator and \c Producer. \param type the information that is required \sa infoKeys() to get a list of the available keys */ QString info(const QString &type) const; /** Set the value of the document's Info dictionary entry specified by \param key to \param val \returns true on success, false on failure */ bool setInfo(const QString &key, const QString &val); /** The title of the document */ QString title() const; /** Set the title of the document to \param val \returns true on success, false on failure */ bool setTitle(const QString &val); /** The author of the document */ QString author() const; /** Set the author of the document to \param val \returns true on success, false on failure */ bool setAuthor(const QString &val); /** The subject of the document */ QString subject() const; /** Set the subject of the document to \param val \returns true on success, false on failure */ bool setSubject(const QString &val); /** The keywords of the document */ QString keywords() const; /** Set the keywords of the document to \param val \returns true on success, false on failure */ bool setKeywords(const QString &val); /** The creator of the document */ QString creator() const; /** Set the creator of the document to \param val \returns true on success, false on failure */ bool setCreator(const QString &val); /** The producer of the document */ QString producer() const; /** Set the producer of the document to \param val \returns true on success, false on failure */ bool setProducer(const QString &val); /** Remove the document's Info dictionary \returns true on success, false on failure */ bool removeInfo(); /** Obtain a list of the available string information keys. */ QStringList infoKeys() const; /** Test if the document is encrypted */ bool isEncrypted() const; /** Test if the document is linearised In some cases, this is called "fast web view", since it is mostly an optimisation for viewing over the Web. */ bool isLinearized() const; /** Test if the permissions on the document allow it to be printed */ bool okToPrint() const; /** Test if the permissions on the document allow it to be printed at high resolution */ bool okToPrintHighRes() const; /** Test if the permissions on the document allow it to be changed. \note depending on the type of change, it may be more appropriate to check other properties as well. */ bool okToChange() const; /** Test if the permissions on the document allow the contents to be copied / extracted */ bool okToCopy() const; /** Test if the permissions on the document allow annotations to be added or modified, and interactive form fields (including signature fields) to be completed. */ bool okToAddNotes() const; /** Test if the permissions on the document allow interactive form fields (including signature fields) to be completed. \note this can be true even if okToAddNotes() is false - this means that only form completion is permitted. */ bool okToFillForm() const; /** Test if the permissions on the document allow interactive form fields (including signature fields) to be set, created and modified */ bool okToCreateFormFields() const; /** Test if the permissions on the document allow content extraction (text and perhaps other content) for accessibility usage (eg for a screen reader) */ bool okToExtractForAccessibility() const; /** Test if the permissions on the document allow it to be "assembled" - insertion, rotation and deletion of pages; or creation of bookmarks and thumbnail images. \note this can be true even if okToChange() is false */ bool okToAssemble() const; /** The version of the PDF specification that the document conforms to \param major an optional pointer to a variable where store the "major" number of the version \param minor an optional pointer to a variable where store the "minor" number of the version \deprecated Will be removed in the Qt6 interface. Use the method returning a PdfVersion object instead! \since 0.12 */ Q_DECL_DEPRECATED void getPdfVersion(int *major, int *minor) const; /** \brief The version specification of a pdf file */ struct PdfVersion { int major; int minor; }; /** The version of the PDF specification that the document conforms to \since 21.08 */ PdfVersion getPdfVersion() const; /** The fonts within the PDF document. This is a shorthand for getting all the fonts at once. \note this can take a very long time to run with a large document. You may wish to use a FontIterator if you have more than say 20 pages \see newFontIterator() */ QList fonts() const; /** Creates a new FontIterator object for font scanning. The new iterator can be used for reading the font information of the document, reading page by page. The caller is responsible for the returned object, ie it should freed it when no more useful. \param startPage the initial page from which start reading fonts \see fonts() \since 0.12 */ FontIterator *newFontIterator(int startPage = 0) const; /** The font data if the font is an embedded one. \since 0.10 */ QByteArray fontData(const FontInfo &fi) const; /** The documents embedded within the PDF document. \note there are two types of embedded document - this call only accesses documents that are embedded at the document level. */ QList embeddedFiles() const; /** Whether there are any documents embedded in this PDF document. */ bool hasEmbeddedFiles() const; /** Gets the table of contents (TOC) of the Document. The caller is responsible for the returned object. In the tree the tag name is the 'screen' name of the entry. A tag can have attributes. Here follows the list of tag attributes with meaning: - Destination: A string description of the referred destination - DestinationName: A 'named reference' to the viewport - ExternalFileName: A link to a external filename - Open: A bool value that tells whether the subbranch of the item is open or not Resolving the final destination for each item can be done in the following way: - first, checking for 'Destination': if not empty, then a LinkDestination can be constructed straight with it - as second step, if the 'DestinationName' is not empty, then the destination can be resolved using linkDestination() Note also that if 'ExternalFileName' is not emtpy, then the destination refers to that document (and not to the current one). \returns the TOC, or NULL if the Document does not have one */ Q_DECL_DEPRECATED QDomDocument *toc() const; /** Gets the outline of the document \returns a vector of outline items, empty if there are none \since 0.74 **/ QVector outline() const; /** Tries to resolve the named destination \p name. \note this operation starts a search through the whole document \returns a new LinkDestination object if the named destination was actually found, or NULL otherwise */ LinkDestination *linkDestination(const QString &name); /** Sets the paper color \param color the new paper color */ void setPaperColor(const QColor &color); /** The paper color The default color is white. */ QColor paperColor() const; /** Sets the backend used to render the pages. \param backend the new rendering backend \since 0.6 */ void setRenderBackend(RenderBackend backend); /** The currently set render backend The default backend is \ref SplashBackend \since 0.6 */ RenderBackend renderBackend() const; /** The available rendering backends. \since 0.6 */ static QSet availableRenderBackends(); /** Sets the render \p hint . \note some hints may not be supported by some rendering backends. \param on whether the flag should be added or removed. \since 0.6 */ void setRenderHint(RenderHint hint, bool on = true); /** The currently set render hints. \since 0.6 */ RenderHints renderHints() const; /** Gets a new PS converter for this document. The caller gets the ownership of the returned converter. \since 0.6 */ PSConverter *psConverter() const; /** Gets a new PDF converter for this document. The caller gets the ownership of the returned converter. \since 0.8 */ PDFConverter *pdfConverter() const; /** Gets the metadata stream contents \since 0.6 */ QString metadata() const; /** Test whether this document has "optional content". Optional content is used to optionally turn on (display) and turn off (not display) some elements of the document. The most common use of this is for layers in design applications, but it can be used for a range of things, such as not including some content in printing, and displaying content in the appropriate language. \since 0.8 */ bool hasOptionalContent() const; /** Itemviews model for optional content. The model is owned by the document. \since 0.8 */ OptContentModel *optionalContentModel(); /** Document-level JavaScript scripts. Returns the list of document level JavaScript scripts to be always executed before any other script. \since 0.10 */ QStringList scripts() const; /** The PDF identifiers. \param permanentId an optional pointer to a variable where store the permanent ID of the document \param updateId an optional pointer to a variable where store the update ID of the document \return whether the document has the IDs \since 0.16 */ bool getPdfId(QByteArray *permanentId, QByteArray *updateId) const; /** Returns the type of forms contained in the document \since 0.22 */ FormType formType() const; /** Returns the calculate order for forms (using their id) \since 0.53 */ QVector formCalculateOrder() const; /** Returns the signatures of this document. Prefer to use this over getting the signatures for all the pages of the document since there are documents with signatures that don't belong to a given page \since 0.88 */ QVector signatures() const; /** Returns whether the document's XRef table has been reconstructed or not \since 21.06 */ bool xrefWasReconstructed() const; /** Sets the document's XRef reconstruction callback, so whenever a XRef table reconstruction happens the callback will get triggered. \since 21.06 */ void setXRefReconstructedCallback(const std::function &callback); /** Destructor. */ ~Document(); private: Q_DISABLE_COPY(Document) DocumentData *m_doc; explicit Document(DocumentData *dataA); }; class BaseConverterPrivate; class PSConverterPrivate; class PDFConverterPrivate; /** \brief Base converter. This is the base class for the converters. \since 0.8 */ class POPPLER_QT5_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. * * \since 0.8 */ 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 \since 0.12.1 */ 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 \since 0.6 */ class POPPLER_QT5_EXPORT PSConverter : public BaseConverter { friend class Document; public: /** Options for the PS export. \since 0.10 */ enum PSOption { Printing = 0x00000001, ///< The PS is generated for printing purposes StrictMargins = 0x00000002, ForceRasterization = 0x00000004, PrintToEPS = 0x00000008, ///< Output EPS instead of PS \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) /** 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. \since 0.10 */ void setPSOptions(PSOptions options); /** The currently set options for the PS export. The default flags are: Printing. \since 0.10 */ PSOptions psOptions() const; /** Sets a function that will be called each time a page is converted. The payload belongs to the caller. \since 0.16 */ 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). \since 0.8 */ class POPPLER_QT5_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_QT5_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 */ POPPLER_QT5_EXPORT Q_DECL_DEPRECATED QDateTime convertDate(char *dateString); /** Conversion from PDF date string format to QDateTime \since 0.64 */ POPPLER_QT5_EXPORT QDateTime convertDate(const char *dateString); /** Whether the color management functions are available. \since 0.12 */ POPPLER_QT5_EXPORT bool isCmsAvailable(); /** Whether the overprint preview functionality is available. \since 0.22 */ POPPLER_QT5_EXPORT bool isOverprintPreviewAvailable(); class SoundData; /** Container class for a sound file in a PDF document. A sound can be either External (in that case should be loaded the file whose url is represented by url() ), or Embedded, and the player has to play the data contained in data(). \since 0.6 */ class POPPLER_QT5_EXPORT SoundObject { public: /** The type of sound */ enum SoundType { External, ///< The real sound file is external Embedded ///< The sound is contained in the data }; /** The encoding format used for the sound */ enum SoundEncoding { Raw, ///< Raw encoding, with unspecified or unsigned values in the range [ 0, 2^B - 1 ] Signed, ///< Twos-complement values muLaw, ///< mu-law-encoded samples ALaw ///< A-law-encoded samples }; /// \cond PRIVATE explicit SoundObject(Sound *popplersound); /// \endcond ~SoundObject(); /** Is the sound embedded (SoundObject::Embedded) or external (SoundObject::External)? */ SoundType soundType() const; /** The URL of the sound file to be played, in case of SoundObject::External */ QString url() const; /** The data of the sound, in case of SoundObject::Embedded */ QByteArray data() const; /** The sampling rate of the sound */ double samplingRate() const; /** The number of sound channels to use to play the sound */ int channels() const; /** The number of bits per sample value per channel */ int bitsPerSample() const; /** The encoding used for the sound */ SoundEncoding soundEncoding() const; private: Q_DISABLE_COPY(SoundObject) SoundData *m_soundData; }; class MovieData; /** Container class for a movie object in a PDF document. \since 0.10 */ class POPPLER_QT5_EXPORT MovieObject { friend class AnnotationPrivate; public: /** The play mode for playing the movie */ enum PlayMode { PlayOnce, ///< Play the movie once, closing the movie controls at the end PlayOpen, ///< Like PlayOnce, but leaving the controls open PlayRepeat, ///< Play continuously until stopped PlayPalindrome ///< Play forward, then backward, then again foward and so on until stopped }; ~MovieObject(); /** The URL of the movie to be played */ QString url() const; /** The size of the movie */ QSize size() const; /** The rotation (either 0, 90, 180, or 270 degrees clockwise) for the movie, */ int rotation() const; /** Whether show a bar with movie controls */ bool showControls() const; /** How to play the movie */ PlayMode playMode() const; /** Returns whether a poster image should be shown if the movie is not playing. \since 0.22 */ bool showPosterImage() const; /** Returns the poster image that should be shown if the movie is not playing. If the image is null but showImagePoster() returns @c true, the first frame of the movie should be used as poster image. \since 0.22 */ QImage posterImage() const; private: /// \cond PRIVATE explicit MovieObject(AnnotMovie *ann); /// \endcond Q_DISABLE_COPY(MovieObject) MovieData *m_movieData; }; } 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 poppler-24.02.0/qt5/src/poppler-sound.cc000066400000000000000000000060231455701731300177650ustar00rootroot00000000000000/* poppler-sound.cc: qt interface to poppler * Copyright (C) 2006-2007, Pino Toscano * Copyright (C) 2008, 2018, 2020, 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 * 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 "poppler-qt5.h" #include "Object.h" #include "Stream.h" #include "Sound.h" namespace Poppler { class SoundData { public: SoundData() : m_soundObj(nullptr) { } ~SoundData() { delete m_soundObj; } SoundData(const SoundData &) = delete; SoundData &operator=(const SoundData &) = delete; SoundObject::SoundType m_type; Sound *m_soundObj; }; SoundObject::SoundObject(Sound *popplersound) { m_soundData = new SoundData(); switch (popplersound->getSoundKind()) { case soundEmbedded: m_soundData->m_type = SoundObject::Embedded; break; case soundExternal: default: m_soundData->m_type = SoundObject::External; break; } m_soundData->m_soundObj = popplersound->copy(); } SoundObject::~SoundObject() { delete m_soundData; } SoundObject::SoundType SoundObject::soundType() const { return m_soundData->m_type; } QString SoundObject::url() const { if (m_soundData->m_type != SoundObject::External) { return QString(); } return QString(m_soundData->m_soundObj->getFileName().c_str()); } QByteArray SoundObject::data() const { if (m_soundData->m_type != SoundObject::Embedded) { return QByteArray(); } Stream *stream = m_soundData->m_soundObj->getStream(); stream->reset(); int dataLen = 0; QByteArray fileArray; int i; while ((i = stream->getChar()) != EOF) { fileArray[dataLen] = (char)i; ++dataLen; } fileArray.resize(dataLen); return fileArray; } double SoundObject::samplingRate() const { return m_soundData->m_soundObj->getSamplingRate(); } int SoundObject::channels() const { return m_soundData->m_soundObj->getChannels(); } int SoundObject::bitsPerSample() const { return m_soundData->m_soundObj->getBitsPerSample(); } SoundObject::SoundEncoding SoundObject::soundEncoding() const { switch (m_soundData->m_soundObj->getEncoding()) { case soundRaw: return SoundObject::Raw; case soundSigned: return SoundObject::Signed; case soundMuLaw: return SoundObject::muLaw; case soundALaw: return SoundObject::ALaw; } return SoundObject::Raw; } } poppler-24.02.0/qt5/src/poppler-textbox.cc000066400000000000000000000030151455701731300203300ustar00rootroot00000000000000/* poppler-qt.h: qt interface to poppler * Copyright (C) 2005, Brad Hards * Copyright (C) 2006-2008, Albert Astals Cid * Copyright (C) 2008, Pino Toscano * * 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 "poppler-qt5.h" #include "poppler-private.h" namespace Poppler { TextBox::TextBox(const QString &text, const QRectF &bBox) { m_data = new TextBoxData(); m_data->text = text; m_data->bBox = bBox; } TextBox::~TextBox() { delete m_data; } QString TextBox::text() const { return m_data->text; } QRectF TextBox::boundingBox() const { return m_data->bBox; } TextBox *TextBox::nextWord() const { return m_data->nextWord; } QRectF TextBox::charBoundingBox(int i) const { return m_data->charBBoxes.value(i); } bool TextBox::hasSpaceAfter() const { return m_data->hasSpaceAfter; } } poppler-24.02.0/qt5/src/poppler-version.cpp000066400000000000000000000022271455701731300205210ustar00rootroot00000000000000/* * Copyright (C) 2009-2010, Pino Toscano * Copyright (C) 2018, 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 * 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 "poppler-version.h" QString Poppler::Version::string() { return QStringLiteral(POPPLER_VERSION); } unsigned int Poppler::Version::major() { return POPPLER_VERSION_MAJOR; } unsigned int Poppler::Version::minor() { return POPPLER_VERSION_MINOR; } unsigned int Poppler::Version::micro() { return POPPLER_VERSION_MICRO; } poppler-24.02.0/qt5/src/poppler-version.h.in000066400000000000000000000036321455701731300205740ustar00rootroot00000000000000/* * Copyright (C) 2009, Pino Toscano * Copyright (C) 2018, 2019, 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 * 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_VERSION_H #define POPPLER_VERSION_H #include "poppler-export.h" #include // glibc < 2.28 used to include sys/sysmacros.h // from sys/types.h and sysmacros.h defines minor and major so // undefine them. You may need to undefine them in your code too. #undef minor #undef major #define POPPLER_VERSION "@POPPLER_VERSION@" #define POPPLER_VERSION_MAJOR @POPPLER_MAJOR_VERSION@ #define POPPLER_VERSION_MINOR @POPPLER_MINOR_VERSION@ #define POPPLER_VERSION_MICRO @POPPLER_MICRO_VERSION@ namespace Poppler { namespace Version { /** \since 0.73 \returns the version string of the current poppler-qt5 library */ POPPLER_QT5_EXPORT QString string(); /** \since 0.73 \returns the "major" number of the version of the current poppler-qt5 library */ POPPLER_QT5_EXPORT unsigned int major(); /** \since 0.73 \returns the "minor" number of the version of the current poppler-qt5 library */ POPPLER_QT5_EXPORT unsigned int minor(); /** \since 0.73 \returns the "micro" number of the version of the current poppler-qt5 library */ POPPLER_QT5_EXPORT unsigned int micro(); } } #endif poppler-24.02.0/qt5/tests/000077500000000000000000000000001455701731300152215ustar00rootroot00000000000000poppler-24.02.0/qt5/tests/.gitignore000066400000000000000000000007101455701731300172070ustar00rootroot00000000000000.deps .libs *.la *.lo *.moc Makefile Makefile.in stress-poppler-qt5 stress-poppler-dir test-poppler-qt5 test-password-qt5 poppler-attachments poppler-fonts poppler-texts poppler-forms stress-threads-qt5 test-render-to-file check_actualtext check_attachments check_dateConversion check_fonts check_goostring check_lexer check_links check_metadata check_optcontent check_permissions check_pagelayout check_pagemode check_password check_search check_strings poppler-24.02.0/qt5/tests/CMakeLists.txt000066400000000000000000000074201455701731300177640ustar00rootroot00000000000000add_definitions(-DTESTDATADIR=\"${TESTDATADIR}\") include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../src ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/../src ) macro(QT5_ADD_SIMPLETEST exe source) string(REPLACE "-" "" test_name ${exe}) set(${test_name}_SOURCES ${source} ) poppler_add_test(${exe} BUILD_QT5_TESTS ${${test_name}_SOURCES}) target_link_libraries(${exe} poppler-qt5 Qt5::Widgets) endmacro(QT5_ADD_SIMPLETEST) macro(QT5_ADD_QTEST exe source) if (Qt5Test_FOUND) string(REPLACE "-" "" test_name ${exe}) set(${test_name}_SOURCES ${source} ) poppler_add_test(${exe} BUILD_QT5_TESTS ${${test_name}_SOURCES}) add_test(${exe} ${EXECUTABLE_OUTPUT_PATH}/${exe}) target_link_libraries(${exe} poppler-qt5 Qt5::Widgets Qt5::Test Qt5::Core Qt5::Gui) endif () endmacro(QT5_ADD_QTEST) macro(QT_ADD_FUZZER exe) string(REPLACE "-" "" test_name ${exe}) set(${test_name}_SOURCES ${ARGN} ) poppler_add_test(${exe} BUILD_QT5_TESTS ${${test_name}_SOURCES}) target_link_libraries(${exe} poppler-qt5 Qt5::Widgets Qt5::Test Qt5::Core Qt5::Gui) endmacro(QT_ADD_FUZZER) qt5_add_simpletest(test-poppler-qt5 test-poppler-qt5.cpp) qt5_add_simpletest(test-password-qt5 test-password-qt5.cpp) qt5_add_simpletest(test-render-to-file-qt5 test-render-to-file.cpp) qt5_add_simpletest(poppler-qt5-forms poppler-forms.cpp) qt5_add_simpletest(poppler-qt5-fonts poppler-fonts.cpp) qt5_add_simpletest(poppler-qt5-attachments poppler-attachments.cpp) qt5_add_simpletest(stress-poppler-qt5 stress-poppler-qt5.cpp) qt5_add_simpletest(stress-poppler-dir-qt5 stress-poppler-dir.cpp) qt5_add_simpletest(stress-threads-qt5 stress-threads-qt5.cpp) qt5_add_simpletest(poppler-qt5-texts poppler-texts.cpp) qt5_add_simpletest(poppler-qt5-page-labels poppler-page-labels.cpp) qt5_add_qtest(check_qt5_attachments check_attachments.cpp) qt5_add_qtest(check_qt5_dateConversion check_dateConversion.cpp) qt5_add_qtest(check_qt5_fonts check_fonts.cpp) qt5_add_qtest(check_qt5_links check_links.cpp) qt5_add_qtest(check_qt5_annotations check_annotations.cpp) qt5_add_qtest(check_qt5_metadata check_metadata.cpp) qt5_add_qtest(check_qt5_optcontent check_optcontent.cpp) qt5_add_qtest(check_qt5_forms check_forms.cpp) qt5_add_qtest(check_qt5_pagelayout check_pagelayout.cpp) qt5_add_qtest(check_qt5_pagemode check_pagemode.cpp) qt5_add_qtest(check_qt5_password check_password.cpp) qt5_add_qtest(check_qt5_permissions check_permissions.cpp) qt5_add_qtest(check_qt5_search check_search.cpp) qt5_add_qtest(check_qt5_actualtext check_actualtext.cpp) qt5_add_qtest(check_qt5_lexer check_lexer.cpp) qt5_add_qtest(check_qt5_internal_outline check_internal_outline.cpp) qt5_add_qtest(check_qt5_goostring check_goostring.cpp) 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) endif () if(ENABLE_FUZZER) qt_add_fuzzer(qt_annot_fuzzer ./fuzzing/qt_annot_fuzzer.cc) qt_add_fuzzer(qt_pdf_fuzzer ./fuzzing/qt_pdf_fuzzer.cc) qt_add_fuzzer(qt_label_fuzzer ./fuzzing/qt_label_fuzzer.cc) qt_add_fuzzer(qt_search_fuzzer ./fuzzing/qt_search_fuzzer.cc) qt_add_fuzzer(qt_textbox_fuzzer ./fuzzing/qt_textbox_fuzzer.cc) endif() poppler-24.02.0/qt5/tests/README.unittest000066400000000000000000000021201455701731300177520ustar00rootroot00000000000000The unittests for the Qt5 bindings rely on the QtTestLib package, and will not be built until this is installed. If you do not have it, then you can download it from the Trolltech website. Note that there are a range of ways in which you can run the tests: 1. "make check" will run all the tests. 2. You can run a single test by executing the applicable executable. For example, you can run the PageMode tests by "./check_pagemode" 3. You can run a single function within a single test by appending the name of the function to the executable. For example, if you just want to run the FullScreen test within the PageMode tests, you can "./check_pagemode checkFullScreen". Run the executable with -functions to get a list of all the functions. 4. You can run a single function with specific data by appending the name of the function, followed by a colon, then the data label to the executable. For example, to just do the Author check within the metadata checks, you can "./check_metadata checkStrings:Author". For a full list of options, run a executable with "-help". Brad Hards bradh@frogmouth.net poppler-24.02.0/qt5/tests/check_actualtext.cpp000066400000000000000000000022221455701731300212360ustar00rootroot00000000000000#include #include #include class TestActualText : public QObject { Q_OBJECT public: explicit TestActualText(QObject *parent = nullptr) : QObject(parent) { } private slots: void checkActualText1(); void checkActualText2(); private: void checkActualText(Poppler::Document *doc); }; void TestActualText::checkActualText(Poppler::Document *doc) { Poppler::Page *page = doc->page(0); QVERIFY(page); QCOMPARE(page->text(QRectF()), QLatin1String("The slow brown fox jumps over the black dog.")); delete page; } void TestActualText::checkActualText1() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/WithActualText.pdf"); QVERIFY(doc); checkActualText(doc); delete doc; } void TestActualText::checkActualText2() { QFile file(TESTDATADIR "/unittestcases/WithActualText.pdf"); QVERIFY(file.open(QIODevice::ReadOnly)); Poppler::Document *doc; doc = Poppler::Document::load(&file); QVERIFY(doc); checkActualText(doc); delete doc; } QTEST_GUILESS_MAIN(TestActualText) #include "check_actualtext.moc" poppler-24.02.0/qt5/tests/check_annotations.cpp000066400000000000000000000235331455701731300214250ustar00rootroot00000000000000#include #include #include #include #include #include #include "poppler/Annot.h" #include "goo/GooString.h" #include "goo/gstrtod.h" class TestAnnotations : public QObject { Q_OBJECT public: explicit TestAnnotations(QObject *parent = nullptr) : QObject(parent) { } void saveAndCheck(const std::unique_ptr &doc, const std::function &checkFunction); private slots: void checkQColorPrecision(); void checkFontSizeAndColor(); void checkHighlightFromAndToQuads(); void checkUTF16LEAnnot(); void checkModificationCreationDate(); void checkNonMarkupAnnotations(); void checkDefaultAppearance(); }; /* Is .5f sufficient for 16 bit color channel roundtrip trough save and load on all architectures? */ 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()); uint16_t denormalized = std::round(deserialized * std::numeric_limits::max()); if (static_cast(i) != denormalized) { precisionOk = false; break; } } QVERIFY(precisionOk); } void TestAnnotations::checkFontSizeAndColor() { const QString contents = QStringLiteral("foobar"); const std::vector testColors { QColor::fromRgb(0xAB, 0xCD, 0xEF), QColor::fromCmyk(0xAB, 0xBC, 0xCD, 0xDE) }; const QFont testFont(QStringLiteral("Helvetica"), 20); QTemporaryFile tempFile; QVERIFY(tempFile.open()); tempFile.close(); { std::unique_ptr doc { Poppler::Document::load(TESTDATADIR "/unittestcases/UseNone.pdf") }; QVERIFY(doc.get()); std::unique_ptr page { doc->page(0) }; QVERIFY(page.get()); for (const auto &color : testColors) { auto annot = std::make_unique(Poppler::TextAnnotation::InPlace); annot->setBoundary(QRectF(0.0, 0.0, 1.0, 1.0)); annot->setContents(contents); annot->setTextFont(testFont); annot->setTextColor(color); page->addAnnotation(annot.get()); } std::unique_ptr conv(doc->pdfConverter()); QVERIFY(conv.get()); conv->setOutputFileName(tempFile.fileName()); conv->setPDFOptions(Poppler::PDFConverter::WithChanges); QVERIFY(conv->convert()); } { std::unique_ptr doc { Poppler::Document::load(tempFile.fileName()) }; QVERIFY(doc.get()); std::unique_ptr page { doc->page(0) }; QVERIFY(page.get()); auto annots = page->annotations(); QCOMPARE(annots.size(), static_cast(testColors.size())); auto &&annot = annots.constBegin(); for (const auto &color : testColors) { QCOMPARE((*annot)->subType(), Poppler::Annotation::AText); auto textAnnot = static_cast(*annot); QCOMPARE(textAnnot->contents(), contents); QCOMPARE(textAnnot->textFont().pointSize(), testFont.pointSize()); QCOMPARE(static_cast(textAnnot->textColor().spec()), static_cast(color.spec())); QCOMPARE(textAnnot->textColor(), color); if (annot != annots.constEnd()) { ++annot; } } qDeleteAll(annots); } } namespace Poppler { static bool operator==(const Poppler::HighlightAnnotation::Quad &a, const Poppler::HighlightAnnotation::Quad &b) { // FIXME We do not compare capStart, capEnd and feather since AnnotQuadrilaterals doesn't contain that info and thus // HighlightAnnotationPrivate::fromQuadrilaterals uses default values return a.points[0] == b.points[0] && a.points[1] == b.points[1] && a.points[2] == b.points[2] && a.points[3] == b.points[3]; } } void TestAnnotations::checkHighlightFromAndToQuads() { std::unique_ptr doc { Poppler::Document::load(TESTDATADIR "/unittestcases/UseNone.pdf") }; std::unique_ptr page { doc->page(0) }; auto ha = std::make_unique(); page->addAnnotation(ha.get()); const QList quads = { { { { 0, 0.1 }, { 0.2, 0.3 }, { 0.4, 0.5 }, { 0.6, 0.7 } }, false, false, 0 }, { { { 0.8, 0.9 }, { 0.1, 0.2 }, { 0.3, 0.4 }, { 0.5, 0.6 } }, true, false, 0.4 } }; ha->setHighlightQuads(quads); QCOMPARE(ha->highlightQuads(), quads); } void TestAnnotations::checkUTF16LEAnnot() { std::unique_ptr doc { Poppler::Document::load(TESTDATADIR "/unittestcases/utf16le-annot.pdf") }; QVERIFY(doc.get()); std::unique_ptr page { doc->page(0) }; QVERIFY(page.get()); auto annots = page->annotations(); QCOMPARE(annots.size(), 2); auto annot = annots[1]; QCOMPARE(annot->contents(), QString::fromUtf8("Únîcödé豰")); // clazy:exclude=qstring-allocations qDeleteAll(annots); } void TestAnnotations::saveAndCheck(const std::unique_ptr &doc, const std::function &checkFunction) { // also check that saving yields the same output QTemporaryFile tempFile; QVERIFY(tempFile.open()); tempFile.close(); std::unique_ptr conv(doc->pdfConverter()); conv->setOutputFileName(tempFile.fileName()); conv->setPDFOptions(Poppler::PDFConverter::WithChanges); conv->convert(); std::unique_ptr savedDoc { Poppler::Document::load(tempFile.fileName()) }; std::unique_ptr page { doc->page(0) }; auto annots = page->annotations(); checkFunction(annots.at(1)); qDeleteAll(annots); } void TestAnnotations::checkModificationCreationDate() { std::unique_ptr doc { Poppler::Document::load(TESTDATADIR "/unittestcases/utf16le-annot.pdf") }; QVERIFY(doc.get()); std::unique_ptr page { doc->page(0) }; auto annots = page->annotations(); auto annot = annots.at(1); QCOMPARE(annot->creationDate(), QDateTime()); QCOMPARE(annot->modificationDate(), QDateTime()); const QDateTime dt1(QDate(2020, 8, 7), QTime(18, 34, 56)); annot->setCreationDate(dt1); auto checkFunction1 = [dt1](Poppler::Annotation *a) { QCOMPARE(a->creationDate(), dt1); // setting the creation date updates the modification date QVERIFY(std::abs(a->modificationDate().secsTo(QDateTime::currentDateTime())) < 2); }; checkFunction1(annot); saveAndCheck(doc, checkFunction1); const QDateTime dt2(QDate(2020, 8, 30), QTime(8, 14, 52)); annot->setModificationDate(dt2); auto checkFunction2 = [dt2](Poppler::Annotation *a) { QCOMPARE(a->modificationDate(), dt2); }; checkFunction2(annot); saveAndCheck(doc, checkFunction2); // setting the creation date to empty means "use the modification date" and also updates the modification date // so both creation date and modification date are the same and are now annot->setCreationDate(QDateTime()); auto checkFunction3 = [](Poppler::Annotation *a) { QVERIFY(std::abs(a->creationDate().secsTo(QDateTime::currentDateTime())) < 2); QCOMPARE(a->creationDate(), a->modificationDate()); }; checkFunction3(annot); saveAndCheck(doc, checkFunction3); annot->setModificationDate(QDateTime()); auto checkFunction4 = [](Poppler::Annotation *a) { QCOMPARE(a->creationDate(), QDateTime()); QCOMPARE(a->modificationDate(), QDateTime()); }; checkFunction4(annot); saveAndCheck(doc, checkFunction4); qDeleteAll(annots); } void TestAnnotations::checkNonMarkupAnnotations() { std::unique_ptr doc { Poppler::Document::load(TESTDATADIR "/unittestcases/checkbox_issue_159.pdf") }; QVERIFY(doc.get()); std::unique_ptr page { doc->page(0) }; QVERIFY(page.get()); auto annots = page->annotations(); QCOMPARE(annots.size(), 17); qDeleteAll(annots); } void TestAnnotations::checkDefaultAppearance() { std::unique_ptr roundtripString; { GooString daString { "/Helv 10 Tf 0.1 0.2 0.3 rg" }; const DefaultAppearance da { &daString }; QCOMPARE(da.getFontPtSize(), 10.); QVERIFY(da.getFontName().isName()); QCOMPARE(da.getFontName().getName(), "Helv"); const AnnotColor *color = da.getFontColor(); QVERIFY(color); QCOMPARE(color->getSpace(), AnnotColor::colorRGB); QCOMPARE(color->getValues()[0], 0.1); QCOMPARE(color->getValues()[1], 0.2); QCOMPARE(color->getValues()[2], 0.3); roundtripString = std::make_unique(da.toAppearanceString()); } { /* roundtrip through parse/generate/parse shall preserve values */ const DefaultAppearance da { roundtripString.get() }; QCOMPARE(da.getFontPtSize(), 10.); QVERIFY(da.getFontName().isName()); QCOMPARE(da.getFontName().getName(), "Helv"); const AnnotColor *color = da.getFontColor(); QVERIFY(color); QCOMPARE(color->getSpace(), AnnotColor::colorRGB); QCOMPARE(color->getValues()[0], 0.1); QCOMPARE(color->getValues()[1], 0.2); QCOMPARE(color->getValues()[2], 0.3); } { /* parsing bad DA strings must not cause crash */ GooString daString { "/ % Tf 1 2 rg" }; const DefaultAppearance da { &daString }; QVERIFY(!da.getFontName().isName()); } } QTEST_GUILESS_MAIN(TestAnnotations) #include "check_annotations.moc" poppler-24.02.0/qt5/tests/check_attachments.cpp000066400000000000000000000123201455701731300213730ustar00rootroot00000000000000#include #include #include class TestAttachments : public QObject { Q_OBJECT public: explicit TestAttachments(QObject *parent = nullptr) : QObject(parent) { } private slots: void checkNoAttachments(); void checkAttach1(); void checkAttach2(); void checkAttach3(); void checkAttach4(); }; void TestAttachments::checkNoAttachments() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/truetype.pdf"); QVERIFY(doc); QCOMPARE(doc->hasEmbeddedFiles(), false); delete doc; } void TestAttachments::checkAttach1() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/WithAttachments.pdf"); QVERIFY(doc); QVERIFY(doc->hasEmbeddedFiles()); QList fileList = doc->embeddedFiles(); QCOMPARE(fileList.size(), 2); Poppler::EmbeddedFile *embfile = fileList.at(0); QCOMPARE(embfile->name(), QLatin1String("kroller.png")); QCOMPARE(embfile->description(), QString()); QCOMPARE(embfile->createDate(), QDateTime(QDate(), QTime())); QCOMPARE(embfile->modDate(), QDateTime(QDate(), QTime())); QCOMPARE(embfile->mimeType(), QString()); QFile file(TESTDATADIR "/unittestcases/kroller.png"); QVERIFY(file.open(QIODevice::ReadOnly)); QByteArray krollerData = file.readAll(); QByteArray embdata = embfile->data(); QCOMPARE(krollerData, embdata); Poppler::EmbeddedFile *embfile2 = fileList.at(1); QCOMPARE(embfile2->name(), QLatin1String("gnome-64.gif")); QCOMPARE(embfile2->description(), QString()); QCOMPARE(embfile2->modDate(), QDateTime(QDate(), QTime())); QCOMPARE(embfile2->createDate(), QDateTime(QDate(), QTime())); QCOMPARE(embfile2->mimeType(), QString()); QFile file2(TESTDATADIR "/unittestcases/gnome-64.gif"); QVERIFY(file2.open(QIODevice::ReadOnly)); QByteArray g64Data = file2.readAll(); QByteArray emb2data = embfile2->data(); QCOMPARE(g64Data, emb2data); delete doc; } void TestAttachments::checkAttach2() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/A6EmbeddedFiles.pdf"); QVERIFY(doc); QVERIFY(doc->hasEmbeddedFiles()); QList fileList; fileList = doc->embeddedFiles(); QCOMPARE(fileList.size(), 3); Poppler::EmbeddedFile *embfile1 = fileList.at(0); QCOMPARE(embfile1->name(), QLatin1String("Acro7 thoughts")); QCOMPARE(embfile1->description(), QString()); QCOMPARE(embfile1->createDate(), QDateTime(QDate(2003, 8, 4), QTime(13, 54, 54), Qt::UTC)); QCOMPARE(embfile1->modDate(), QDateTime(QDate(2003, 8, 4), QTime(14, 15, 27), Qt::UTC)); QCOMPARE(embfile1->mimeType(), QLatin1String("text/xml")); Poppler::EmbeddedFile *embfile2 = fileList.at(1); QCOMPARE(embfile2->name(), QLatin1String("acro transitions 1.xls")); QCOMPARE(embfile2->description(), QString()); QCOMPARE(embfile2->createDate(), QDateTime(QDate(2003, 7, 18), QTime(21, 7, 16), Qt::UTC)); QCOMPARE(embfile2->modDate(), QDateTime(QDate(2003, 7, 22), QTime(13, 4, 40), Qt::UTC)); QCOMPARE(embfile2->mimeType(), QLatin1String("application/excel")); Poppler::EmbeddedFile *embfile3 = fileList.at(2); QCOMPARE(embfile3->name(), QLatin1String("apago_pdfe_wide.gif")); QCOMPARE(embfile3->description(), QString()); QCOMPARE(embfile3->createDate(), QDateTime(QDate(2003, 1, 31), QTime(15, 54, 29), Qt::UTC)); QCOMPARE(embfile3->modDate(), QDateTime(QDate(2003, 1, 31), QTime(15, 52, 58), Qt::UTC)); QCOMPARE(embfile3->mimeType(), QString()); delete doc; } void TestAttachments::checkAttach3() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/shapes+attachments.pdf"); QVERIFY(doc); QVERIFY(doc->hasEmbeddedFiles()); QList fileList; fileList = doc->embeddedFiles(); QCOMPARE(fileList.size(), 1); Poppler::EmbeddedFile *embfile = fileList.at(0); QCOMPARE(embfile->name(), QLatin1String("ADEX1.xpdf.pgp")); QCOMPARE(embfile->description(), QString()); QCOMPARE(embfile->createDate(), QDateTime(QDate(2004, 3, 29), QTime(19, 37, 16), Qt::UTC)); QCOMPARE(embfile->modDate(), QDateTime(QDate(2004, 3, 29), QTime(19, 37, 16), Qt::UTC)); QCOMPARE(embfile->mimeType(), QString()); delete doc; } void TestAttachments::checkAttach4() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/imageretrieve+attachment.pdf"); QVERIFY(doc); QVERIFY(doc->hasEmbeddedFiles()); QList fileList; fileList = doc->embeddedFiles(); QCOMPARE(fileList.size(), 1); Poppler::EmbeddedFile *embfile = fileList.at(0); QCOMPARE(embfile->name(), QLatin1String("export-altona.csv")); QCOMPARE(embfile->description(), QLatin1String("Altona Export")); QCOMPARE(embfile->createDate(), QDateTime(QDate(2005, 8, 30), QTime(20, 49, 35), Qt::UTC)); QCOMPARE(embfile->modDate(), QDateTime(QDate(2005, 8, 30), QTime(20, 49, 52), Qt::UTC)); QCOMPARE(embfile->mimeType(), QLatin1String("application/vnd.ms-excel")); delete doc; } QTEST_GUILESS_MAIN(TestAttachments) #include "check_attachments.moc" poppler-24.02.0/qt5/tests/check_cidfontswidthsbuilder.cpp000066400000000000000000000065221455701731300234720ustar00rootroot00000000000000//======================================================================== // // 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" poppler-24.02.0/qt5/tests/check_dateConversion.cpp000066400000000000000000000062621455701731300220530ustar00rootroot00000000000000#include Q_DECLARE_METATYPE(QDate) Q_DECLARE_METATYPE(QTime) #include class TestDateConv : public QObject { Q_OBJECT public: explicit TestDateConv(QObject *parent = nullptr) : QObject(parent) { } private slots: void initTestCase(); void checkDates_data(); void checkDates(); void checkInvalidDates_data(); void checkInvalidDates(); }; void TestDateConv::initTestCase() { qRegisterMetaType("QDate"); qRegisterMetaType("QTime"); } void TestDateConv::checkDates_data() { QTest::addColumn("input"); QTest::addColumn("day"); QTest::addColumn("time"); // This is a typical case - all data provided QTest::newRow("D:20040101121110") << QByteArray("D:20040101121110Z") << QDate(2004, 1, 1) << QTime(12, 11, 10); // The D: is strongly recommended, but optional QTest::newRow("20040101121110") << QByteArray("20040101121110Z") << QDate(2004, 1, 1) << QTime(12, 11, 10); // Only the year is actually required QTest::newRow("D:2006") << QByteArray("D:2006") << QDate(2006, 1, 1) << QTime(0, 0, 0); QTest::newRow("D:200602") << QByteArray("D:200602") << QDate(2006, 2, 1) << QTime(0, 0, 0); QTest::newRow("D:20060304") << QByteArray("D:20060304") << QDate(2006, 3, 4) << QTime(0, 0, 0); QTest::newRow("D:2006030405") << QByteArray("D:2006030405") << QDate(2006, 3, 4) << QTime(5, 0, 0); QTest::newRow("D:200603040512") << QByteArray("D:200603040512") << QDate(2006, 3, 4) << QTime(5, 12, 0); // If the timezone isn't specified, I assume UTC QTest::newRow("D:20060304051226") << QByteArray("D:20060304051226") << QDate(2006, 3, 4) << QTime(5, 12, 26); // Check for real timezone conversions QTest::newRow("D:20030131115258-04'00'") << QByteArray("D:20030131115258-04'00'") << QDate(2003, 1, 31) << QTime(15, 52, 58); QTest::newRow("D:20030131115258+05'00'") << QByteArray("D:20030131115258+05'00'") << QDate(2003, 1, 31) << QTime(6, 52, 58); // There are places that have non-hour offsets // Yep, that means you Adelaide. QTest::newRow("D:20030131115258+08'30'") << QByteArray("D:20030131115258+08'30'") << QDate(2003, 1, 31) << QTime(3, 22, 58); QTest::newRow("D:20030131115258-08'30'") << QByteArray("D:20030131115258-08'30'") << QDate(2003, 1, 31) << QTime(20, 22, 58); } void TestDateConv::checkDates() { QFETCH(QByteArray, input); QFETCH(QDate, day); QFETCH(QTime, time); QCOMPARE(Poppler::convertDate(input.constData()), QDateTime(day, time, Qt::UTC)); } void TestDateConv::checkInvalidDates_data() { QTest::addColumn("input"); // Null data QTest::newRow("Null data") << QByteArray(); // Empty data QTest::newRow("Empty data") << QByteArray(""); // Empty data QTest::newRow("One character") << QByteArray("D"); // Empty data QTest::newRow("'D:'") << QByteArray("D:"); // Empty data QTest::newRow("Not a date") << QByteArray("D:IAmNotAValidDate"); } void TestDateConv::checkInvalidDates() { QFETCH(QByteArray, input); QCOMPARE(Poppler::convertDate(input.constData()), QDateTime()); } QTEST_GUILESS_MAIN(TestDateConv) #include "check_dateConversion.moc" poppler-24.02.0/qt5/tests/check_distinguished_name_parser.cpp000066400000000000000000000223001455701731300242760ustar00rootroot00000000000000//======================================================================== // // 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 { "CN=S\\69mpl\\65\\7A" } << DN::Result { { "CN", "Simplez" } }; QTest::newRow("SN=Lu\\C4\\8Di\\C4\\87") << std::string { "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 { "CN=\"Forbidden !@#$%&*()<>[]{},.?/\\| chars\"" } << DN::Result { { "CN", "Forbidden !@#$%&*()<>[]{},.?/\\| chars" } }; QTest::newRow("Quoted quotation") << std::string { "CN=\"Quotation \\\" Mark\"" } << DN::Result { { "CN", "Quotation \" Mark" } }; QTest::newRow("Quoted quotation") << std::string { "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 { "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" poppler-24.02.0/qt5/tests/check_fonts.cpp000066400000000000000000000155001455701731300202140ustar00rootroot00000000000000#include #include #include class TestFontsData : public QObject { Q_OBJECT public: explicit TestFontsData(QObject *parent = nullptr) : QObject(parent) { } private slots: void checkNoFonts(); void checkType1(); void checkType3(); void checkTrueType(); void checkFontIterator(); void checkSecondDocumentQuery(); void checkMultipleIterations(); void checkIteratorFonts(); }; static QList loadFontsViaIterator(Poppler::Document *doc, int from = 0, int count = -1) { int num = count == -1 ? doc->numPages() - from : count; QList list; std::unique_ptr it(doc->newFontIterator(from)); while (it->hasNext() && num) { list += it->next(); --num; } return list; } namespace Poppler { static bool operator==(const FontInfo &f1, const FontInfo &f2) { if (f1.name() != f2.name()) { return false; } if (f1.file() != f2.file()) { return false; } if (f1.isEmbedded() != f2.isEmbedded()) { return false; } if (f1.isSubset() != f2.isSubset()) { return false; } if (f1.type() != f2.type()) { return false; } if (f1.typeName() != f2.typeName()) { return false; } return true; } } void TestFontsData::checkNoFonts() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/tests/image.pdf"); QVERIFY(doc); QList listOfFonts = doc->fonts(); QCOMPARE(listOfFonts.size(), 0); delete doc; } void TestFontsData::checkType1() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/tests/text.pdf"); QVERIFY(doc); QList listOfFonts = doc->fonts(); QCOMPARE(listOfFonts.size(), 1); QCOMPARE(listOfFonts.at(0).name(), QLatin1String("Helvetica")); QCOMPARE(listOfFonts.at(0).type(), Poppler::FontInfo::Type1); QCOMPARE(listOfFonts.at(0).typeName(), QLatin1String("Type 1")); QCOMPARE(listOfFonts.at(0).isEmbedded(), false); QCOMPARE(listOfFonts.at(0).isSubset(), false); delete doc; } void TestFontsData::checkType3() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/tests/type3.pdf"); QVERIFY(doc); QList listOfFonts = doc->fonts(); QCOMPARE(listOfFonts.size(), 2); QCOMPARE(listOfFonts.at(0).name(), QLatin1String("Helvetica")); QCOMPARE(listOfFonts.at(0).type(), Poppler::FontInfo::Type1); QCOMPARE(listOfFonts.at(0).typeName(), QLatin1String("Type 1")); QCOMPARE(listOfFonts.at(0).isEmbedded(), false); QCOMPARE(listOfFonts.at(0).isSubset(), false); QCOMPARE(listOfFonts.at(1).name(), QString()); QCOMPARE(listOfFonts.at(1).type(), Poppler::FontInfo::Type3); QCOMPARE(listOfFonts.at(1).typeName(), QLatin1String("Type 3")); QCOMPARE(listOfFonts.at(1).isEmbedded(), true); QCOMPARE(listOfFonts.at(1).isSubset(), false); delete doc; } void TestFontsData::checkTrueType() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/truetype.pdf"); QVERIFY(doc); QList listOfFonts = doc->fonts(); QCOMPARE(listOfFonts.size(), 2); QCOMPARE(listOfFonts.at(0).name(), QLatin1String("Arial-BoldMT")); QCOMPARE(listOfFonts.at(0).type(), Poppler::FontInfo::TrueType); QCOMPARE(listOfFonts.at(0).typeName(), QLatin1String("TrueType")); QCOMPARE(listOfFonts.at(0).isEmbedded(), false); QCOMPARE(listOfFonts.at(0).isSubset(), false); QCOMPARE(listOfFonts.at(1).name(), QLatin1String("ArialMT")); QCOMPARE(listOfFonts.at(1).type(), Poppler::FontInfo::TrueType); QCOMPARE(listOfFonts.at(1).typeName(), QLatin1String("TrueType")); QCOMPARE(listOfFonts.at(1).isEmbedded(), false); QCOMPARE(listOfFonts.at(1).isSubset(), false); delete doc; } void TestFontsData::checkFontIterator() { // loading a 1-page document Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/tests/type3.pdf"); QVERIFY(doc); // loading a 6-pages document Poppler::Document *doc6 = Poppler::Document::load(TESTDATADIR "/tests/cropbox.pdf"); QVERIFY(doc6); std::unique_ptr it; // some tests with the 1-page document: // - check a default iterator it.reset(doc->newFontIterator()); QVERIFY(it->hasNext()); // - check an iterator for negative pages to behave as 0 it.reset(doc->newFontIterator(-1)); QVERIFY(it->hasNext()); // - check an iterator for pages out of the page limit it.reset(doc->newFontIterator(1)); QVERIFY(!it->hasNext()); // - check that it reaches the end after 1 iteration it.reset(doc->newFontIterator()); QVERIFY(it->hasNext()); it->next(); QVERIFY(!it->hasNext()); // some tests with the 6-page document: // - check a default iterator it.reset(doc6->newFontIterator()); QVERIFY(it->hasNext()); // - check an iterator for pages out of the page limit it.reset(doc6->newFontIterator(6)); QVERIFY(!it->hasNext()); // - check that it reaches the end after 6 iterations it.reset(doc6->newFontIterator()); QVERIFY(it->hasNext()); it->next(); QVERIFY(it->hasNext()); it->next(); QVERIFY(it->hasNext()); it->next(); QVERIFY(it->hasNext()); it->next(); QVERIFY(it->hasNext()); it->next(); QVERIFY(it->hasNext()); it->next(); QVERIFY(!it->hasNext()); delete doc; delete doc6; } void TestFontsData::checkSecondDocumentQuery() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/tests/type3.pdf"); QVERIFY(doc); QList listOfFonts = doc->fonts(); QCOMPARE(listOfFonts.size(), 2); // check we get the very same result when calling fonts() again (#19405) QList listOfFonts2 = doc->fonts(); QCOMPARE(listOfFonts, listOfFonts2); delete doc; } void TestFontsData::checkMultipleIterations() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/tests/type3.pdf"); QVERIFY(doc); QList listOfFonts = loadFontsViaIterator(doc); QCOMPARE(listOfFonts.size(), 2); QList listOfFonts2 = loadFontsViaIterator(doc); QCOMPARE(listOfFonts, listOfFonts2); delete doc; } void TestFontsData::checkIteratorFonts() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/tests/fonts.pdf"); QVERIFY(doc); QList listOfFonts = doc->fonts(); QCOMPARE(listOfFonts.size(), 3); // check we get the very same result when gatering fonts using the iterator QList listOfFonts2 = loadFontsViaIterator(doc); QCOMPARE(listOfFonts, listOfFonts2); delete doc; } QTEST_GUILESS_MAIN(TestFontsData) #include "check_fonts.moc" poppler-24.02.0/qt5/tests/check_forms.cpp000066400000000000000000000213121455701731300202070ustar00rootroot00000000000000#include #include #include #include #include class TestForms : public QObject { Q_OBJECT public: explicit TestForms(QObject *parent = nullptr) : QObject(parent) { } private slots: void testCheckbox(); // Test for issue #655 void testCheckboxIssue159(); // Test for issue #159 void testSetIcon(); // Test that setIcon will always be valid. void testSetPrintable(); void testSetAppearanceText(); void testStandAloneWidgets(); // check for 'de facto' tooltips. Issue #34 void testUnicodeFieldAttributes(); }; void TestForms::testCheckbox() { // Test for checkbox issue #655 QScopedPointer document(Poppler::Document::load(TESTDATADIR "/unittestcases/latex-hyperref-checkbox-issue-655.pdf")); QVERIFY(document); QScopedPointer page(document->page(0)); QVERIFY(page); QList forms = page->formFields(); QCOMPARE(forms.size(), 1); Poppler::FormField *form = forms.at(0); QCOMPARE(form->type(), Poppler::FormField::FormButton); Poppler::FormFieldButton *chkFormFieldButton = static_cast(form); // Test this is actually a Checkbox QCOMPARE(chkFormFieldButton->buttonType(), Poppler::FormFieldButton::CheckBox); // checkbox comes initially 'unchecked' QCOMPARE(chkFormFieldButton->state(), false); // let's mark it as 'checked' chkFormFieldButton->setState(true); // now test if it was succesfully 'checked' QCOMPARE(chkFormFieldButton->state(), true); } void TestForms::testStandAloneWidgets() { // Check for 'de facto' tooltips. Issue #34 QScopedPointer document(Poppler::Document::load(TESTDATADIR "/unittestcases/tooltip.pdf")); QVERIFY(document); QScopedPointer page(document->page(0)); QVERIFY(page); QList forms = page->formFields(); QCOMPARE(forms.size(), 3); Q_FOREACH (Poppler::FormField *field, forms) { QCOMPARE(field->type(), Poppler::FormField::FormButton); Poppler::FormFieldButton *fieldButton = static_cast(field); QCOMPARE(fieldButton->buttonType(), Poppler::FormFieldButton::Push); FormField *ff = Poppler::FormFieldData::getFormWidget(fieldButton)->getField(); QVERIFY(ff); QCOMPARE(ff->isStandAlone(), true); // tooltip.pdf has only these 3 standalone widgets QVERIFY(field->uiName() == QStringLiteral("This is a tooltip!") || // clazy:exclude=qstring-allocations field->uiName() == QStringLiteral("Sulfuric acid") || field->uiName() == QString::fromUtf8("little Gauß")); } } void TestForms::testCheckboxIssue159() { // Test for checkbox issue #159 QScopedPointer document(Poppler::Document::load(TESTDATADIR "/unittestcases/checkbox_issue_159.pdf")); QVERIFY(document); QScopedPointer page(document->page(0)); QVERIFY(page); Poppler::FormFieldButton *beerFieldButton = nullptr; Poppler::FormFieldButton *wineFieldButton = nullptr; QList forms = page->formFields(); // Let's find and assign the "Wine" and "Beer" radio buttons Q_FOREACH (Poppler::FormField *field, forms) { if (field->type() != Poppler::FormField::FormButton) { continue; } Poppler::FormFieldButton *fieldButton = static_cast(field); if (fieldButton->buttonType() != Poppler::FormFieldButton::Radio) { continue; } // printf("%s \n", fieldButton->caption().toLatin1().data()); if (fieldButton->caption() == QStringLiteral("Wine")) { wineFieldButton = fieldButton; } else if (fieldButton->caption() == QStringLiteral("Beer")) { beerFieldButton = fieldButton; } } // "Beer" and "Wine" radiobuttons belong to the same RadioButton group. // So selecting one should unselect the other. QVERIFY(beerFieldButton); QVERIFY(wineFieldButton); // Test that the RadioButton group comes with "Beer" initially selected QCOMPARE(beerFieldButton->state(), true); // Now select "Wine". As a result "Beer" should no longer be selected. wineFieldButton->setState(true); // Test that "Beer" is indeed not reporting as being selected QCOMPARE(beerFieldButton->state(), false); } void TestForms::testSetIcon() { QScopedPointer document(Poppler::Document::load(TESTDATADIR "/unittestcases/form_set_icon.pdf")); QVERIFY(document); QScopedPointer page(document->page(0)); QVERIFY(page); QList forms = page->formFields(); Poppler::FormFieldButton *anmButton = nullptr; // First we are finding the field which will have its icon changed Q_FOREACH (Poppler::FormField *field, forms) { if (field->type() != Poppler::FormField::FormButton) { continue; } Poppler::FormFieldButton *fieldButton = static_cast(field); if (field->name() == QStringLiteral("anm0")) { anmButton = fieldButton; } } QVERIFY(anmButton); // Then we set the Icon on this field, for every other field // And verify if it has a valid icon Q_FOREACH (Poppler::FormField *field, forms) { if (field->type() != Poppler::FormField::FormButton) { continue; } Poppler::FormFieldButton *fieldButton = static_cast(field); if (field->name() == QStringLiteral("anm0")) { continue; } Poppler::FormFieldIcon newIcon = fieldButton->icon(); anmButton->setIcon(newIcon); Poppler::FormFieldIcon anmIcon = anmButton->icon(); QVERIFY(Poppler::FormFieldIconData::getData(anmIcon)); QVERIFY(Poppler::FormFieldIconData::getData(anmIcon)->icon); QCOMPARE(Poppler::FormFieldIconData::getData(anmIcon)->icon->lookupNF("AP").dictLookupNF("N").getRef().num, Poppler::FormFieldIconData::getData(newIcon)->icon->lookupNF("AP").dictLookupNF("N").getRef().num); } // Just making sure that setting a invalid icon will still produce a valid icon. anmButton->setIcon(Poppler::FormFieldIcon(nullptr)); Poppler::FormFieldIcon anmIcon = anmButton->icon(); QVERIFY(Poppler::FormFieldIconData::getData(anmIcon)); QVERIFY(Poppler::FormFieldIconData::getData(anmIcon)->icon); } void TestForms::testSetPrintable() { QScopedPointer document(Poppler::Document::load(TESTDATADIR "/unittestcases/form_set_icon.pdf")); QVERIFY(document); QScopedPointer page(document->page(0)); QVERIFY(page); QList forms = page->formFields(); Q_FOREACH (Poppler::FormField *field, forms) { field->setPrintable(true); QCOMPARE(field->isPrintable(), true); field->setPrintable(false); QCOMPARE(field->isPrintable(), false); } } void TestForms::testSetAppearanceText() { QScopedPointer document(Poppler::Document::load(TESTDATADIR "/unittestcases/checkbox_issue_159.pdf")); QVERIFY(document); QScopedPointer page(document->page(0)); QVERIFY(page); QList forms = page->formFields(); int nTextForms = 0; Q_FOREACH (Poppler::FormField *field, forms) { if (field->type() != Poppler::FormField::FormText) { continue; } nTextForms++; Poppler::FormFieldText *fft = static_cast(field); const QString textToSet = "HOLA" + fft->name(); fft->setAppearanceText(textToSet); Dict *dict = Poppler::FormFieldData::getFormWidget(fft)->getObj()->getDict(); Object strObject = dict->lookup("AP").dictLookup("N"); QVERIFY(strObject.isStream()); GooString s; strObject.getStream()->fillGooString(&s); const QString textToFind = QStringLiteral("\n(%1) Tj\n").arg(textToSet); QVERIFY(s.toStr().find(textToFind.toStdString()) != std::string::npos); } QCOMPARE(nTextForms, 5); } void TestForms::testUnicodeFieldAttributes() { QScopedPointer document(Poppler::Document::load(TESTDATADIR "/unittestcases/fieldWithUtf16Names.pdf")); QVERIFY(document); QScopedPointer page(document->page(0)); QVERIFY(page); QList forms = page->formFields(); Poppler::FormField *field = forms.first(); QCOMPARE(field->name(), QStringLiteral("Tex")); QCOMPARE(field->uiName(), QStringLiteral("Texto de ayuda")); } QTEST_GUILESS_MAIN(TestForms) #include "check_forms.moc" poppler-24.02.0/qt5/tests/check_goostring.cpp000066400000000000000000000132401455701731300210750ustar00rootroot00000000000000#include #include #include "goo/GooString.h" class TestGooString : public QObject { Q_OBJECT public: explicit TestGooString(QObject *parent = nullptr) : QObject(parent) { } private slots: void testInsertData_data(); void testInsertData(); void testInsert(); void testFormat(); void testFromNullptr(); }; void TestGooString::testInsertData_data() { QTest::addColumn("string"); QTest::addColumn("addition"); QTest::addColumn("position"); QTest::addColumn("result"); QTest::newRow("foo") << QByteArray("foo") << QByteArray("bar") << 0 << QByteArray("barfoo"); QTest::newRow("") << QByteArray() << QByteArray("bar") << 0 << QByteArray("bar"); QTest::newRow("foo+bar #1") << QByteArray("f+bar") << QByteArray("oo") << 1 << QByteArray("foo+bar"); QTest::newRow("foo+bar #2") << QByteArray("fobar") << QByteArray("o+") << 2 << QByteArray("foo+bar"); QTest::newRow("foo+bar #last") << QByteArray("foo+r") << QByteArray("ba") << 4 << QByteArray("foo+bar"); QTest::newRow("foo+bar #end") << QByteArray("foo+") << QByteArray("bar") << 4 << QByteArray("foo+bar"); QTest::newRow("long #start") << QByteArray("very string") << QByteArray("long long long long long ") << 5 << QByteArray("very long long long long long string"); } void TestGooString::testInsertData() { QFETCH(QByteArray, string); QFETCH(QByteArray, addition); QFETCH(int, position); QFETCH(QByteArray, result); GooString goo(string.constData()); QCOMPARE(goo.c_str(), string.constData()); goo.insert(position, addition.constData()); QCOMPARE(goo.c_str(), result.constData()); } void TestGooString::testInsert() { { GooString goo; goo.insert(0, "."); goo.insert(0, "This is a very long long test string"); QCOMPARE(goo.c_str(), "This is a very long long test string."); } { GooString goo; goo.insert(0, "second-part-third-part"); goo.insert(0, "first-part-"); QCOMPARE(goo.c_str(), "first-part-second-part-third-part"); } } void TestGooString::testFormat() { { const std::unique_ptr 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::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::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::unique_ptr goo2(GooString::format("{0:s} {1:t}", "TEST", goo.get())); 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 QByteArray expected = QStringLiteral("%1 %2 %3").arg(UINT_MAX).arg(INT_MAX).arg(INT_MIN).toLatin1(); 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 QByteArray expected = QStringLiteral("%1 %2 %3").arg(ULONG_MAX).arg(LONG_MAX).arg(LONG_MIN).toLatin1(); 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 QByteArray expected = QStringLiteral("%1 %2 %3").arg(ULLONG_MAX).arg(LLONG_MAX).arg(LLONG_MIN).toLatin1(); 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::unique_ptr 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::unique_ptr 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 } }}"); } } void TestGooString::testFromNullptr() { { GooString str { static_cast(nullptr) }; QCOMPARE(str.getLength(), 0); } { GooString str; str.Set(static_cast(nullptr)); QCOMPARE(str.getLength(), 0); } { GooString str { static_cast(nullptr) }; QCOMPARE(str.getLength(), 0); } { GooString str { static_cast(nullptr), 0 }; QCOMPARE(str.getLength(), 0); } { GooString str; str.Set(static_cast(nullptr)); QCOMPARE(str.getLength(), 0); } { GooString str; str.Set(static_cast(nullptr), 0); QCOMPARE(str.getLength(), 0); } } QTEST_GUILESS_MAIN(TestGooString) #include "check_goostring.moc" poppler-24.02.0/qt5/tests/check_internal_outline.cpp000066400000000000000000000340161455701731300224410ustar00rootroot00000000000000#include #include "Outline.h" #include "PDFDoc.h" #include "PDFDocFactory.h" class TestInternalOutline : public QObject { Q_OBJECT public: explicit TestInternalOutline(QObject *parent = nullptr) : QObject(parent) { } private slots: void testCreateOutline(); void testSetOutline(); void testInsertChild(); void testRemoveChild(); void testSetTitleAndSetPageDest(); }; void TestInternalOutline::testCreateOutline() { QTemporaryFile tempFile; QVERIFY(tempFile.open()); tempFile.close(); const std::string tempFileName = tempFile.fileName().toStdString(); const GooString gooTempFileName { tempFileName }; std::unique_ptr doc = PDFDocFactory().createPDFDoc(GooString(TESTDATADIR "/unittestcases/truetype.pdf")); QVERIFY(doc.get()); // ensure the file has no existing outline Outline *outline = doc->getOutline(); QVERIFY(outline != nullptr); auto *outlineItems = outline->getItems(); QVERIFY(outlineItems == nullptr); // create an empty outline and save the file outline->setOutline({}); outlineItems = outline->getItems(); // no items will result in a nullptr rather than a 0 length list QVERIFY(outlineItems == nullptr); doc->saveAs(gooTempFileName); /******************************************************/ doc = PDFDocFactory().createPDFDoc(gooTempFileName); QVERIFY(doc.get()); // ensure the re-opened file has an outline with no items outline = doc->getOutline(); QVERIFY(outline != nullptr); outlineItems = outline->getItems(); QVERIFY(outlineItems == nullptr); } static std::string getTitle(const OutlineItem *item) { std::vector u = item->getTitle(); std::string s; for (auto &c : u) { s.append(1, (char)(c)); } return s; } void TestInternalOutline::testSetOutline() { QTemporaryFile tempFile; QVERIFY(tempFile.open()); tempFile.close(); const std::string tempFileName = tempFile.fileName().toStdString(); const GooString gooTempFileName { tempFileName }; std::unique_ptr doc = PDFDocFactory().createPDFDoc(GooString(TESTDATADIR "/unittestcases/truetype.pdf")); QVERIFY(doc.get()); // ensure the file has no existing outline Outline *outline = doc->getOutline(); QVERIFY(outline != nullptr); auto *outlineItems = outline->getItems(); QVERIFY(outlineItems == nullptr); // create an outline and save the file outline->setOutline( { { "1", 1, { { "1.1", 1, {} }, { "1.2", 2, {} }, { "1.3", 3, { { "1.3.1", 1, {} }, { "1.3.2", 2, {} }, { "1.3.3", 3, {} }, { "1.3.4", 4, {} } } }, { "1.4", 4, {} } } }, { "2", 2, {} }, { "3", 3, {} }, { "4", 4, {} } }); outlineItems = outline->getItems(); QVERIFY(outlineItems != nullptr); doc->saveAs(gooTempFileName); outline = nullptr; /******************************************************/ doc = PDFDocFactory().createPDFDoc(gooTempFileName); QVERIFY(doc.get()); // ensure the re-opened file has an outline outline = doc->getOutline(); QVERIFY(outline != nullptr); outlineItems = outline->getItems(); QVERIFY(outlineItems != nullptr); QVERIFY(outlineItems->size() == 4); OutlineItem *item = outlineItems->at(0); QVERIFY(item != nullptr); // c_str() is used so QCOMPARE prints string correctly on disagree QCOMPARE(getTitle(item).c_str(), "1"); item = outlineItems->at(1); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "2"); item = outlineItems->at(2); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "3"); item = outlineItems->at(3); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "4"); outlineItems = outlineItems->at(0)->getKids(); QVERIFY(outlineItems != nullptr); item = outlineItems->at(0); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "1.1"); item = outlineItems->at(1); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "1.2"); item = outlineItems->at(2); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "1.3"); item = outlineItems->at(3); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "1.4"); outlineItems = outlineItems->at(2)->getKids(); QVERIFY(outlineItems != nullptr); item = outlineItems->at(0); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "1.3.1"); item = outlineItems->at(1); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "1.3.2"); item = outlineItems->at(2); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "1.3.3"); item = outlineItems->at(3); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "1.3.4"); } void TestInternalOutline::testInsertChild() { QTemporaryFile tempFile; QVERIFY(tempFile.open()); tempFile.close(); QTemporaryFile tempFile2; QVERIFY(tempFile2.open()); tempFile2.close(); const std::string tempFileName = tempFile.fileName().toStdString(); const GooString gooTempFileName { tempFileName }; const std::string tempFileName2 = tempFile2.fileName().toStdString(); const GooString gooTempFileName2 { tempFileName2 }; std::unique_ptr doc = PDFDocFactory().createPDFDoc(GooString(TESTDATADIR "/unittestcases/truetype.pdf")); QVERIFY(doc.get()); // ensure the file has no existing outline Outline *outline = doc->getOutline(); QVERIFY(outline != nullptr); auto *outlineItems = outline->getItems(); QVERIFY(outlineItems == nullptr); // create an outline and save the file outline->setOutline({}); doc->saveAs(gooTempFileName); outline = nullptr; /******************************************************/ doc = PDFDocFactory().createPDFDoc(gooTempFileName); QVERIFY(doc.get()); // ensure the re-opened file has an outline with no items outline = doc->getOutline(); QVERIFY(outline != nullptr); // nullptr for 0-length QVERIFY(outline->getItems() == nullptr); // insert first one to empty outline->insertChild("2", 1, 0); // insert at the end outline->insertChild("3", 1, 1); // insert at the start outline->insertChild("1", 1, 0); // add an item to "2" outlineItems = outline->getItems(); QVERIFY(outlineItems != nullptr); QVERIFY(outlineItems->at(1)); outlineItems->at(1)->insertChild("2.1", 2, 0); outlineItems->at(1)->insertChild("2.2", 2, 1); outlineItems->at(1)->insertChild("2.4", 2, 2); outlineItems->at(1)->insertChild("2.3", 2, 2); // save the file doc->saveAs(gooTempFileName2); outline = nullptr; /******************************************************/ doc = PDFDocFactory().createPDFDoc(gooTempFileName2); QVERIFY(doc.get()); // ensure the re-opened file has an outline outline = doc->getOutline(); QVERIFY(outline != nullptr); outlineItems = outline->getItems(); QVERIFY(outlineItems != nullptr); QVERIFY(outlineItems->size() == 3); OutlineItem *item = outlineItems->at(0); QVERIFY(item != nullptr); // c_str() is used so QCOMPARE prints string correctly on disagree QCOMPARE(getTitle(item).c_str(), "1"); item = outlineItems->at(1); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "2"); item = outlineItems->at(2); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "3"); outlineItems = outlineItems->at(1)->getKids(); item = outlineItems->at(0); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "2.1"); item = outlineItems->at(1); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "2.2"); item = outlineItems->at(2); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "2.3"); item = outlineItems->at(3); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "2.4"); } void TestInternalOutline::testRemoveChild() { QTemporaryFile tempFile; QVERIFY(tempFile.open()); tempFile.close(); QTemporaryFile tempFile2; QVERIFY(tempFile2.open()); tempFile2.close(); const std::string tempFileName = tempFile.fileName().toStdString(); const GooString gooTempFileName { tempFileName }; const std::string tempFileName2 = tempFile2.fileName().toStdString(); const GooString gooTempFileName2 { tempFileName2 }; std::unique_ptr doc = PDFDocFactory().createPDFDoc(GooString(TESTDATADIR "/unittestcases/truetype.pdf")); QVERIFY(doc.get()); // ensure the file has no existing outline Outline *outline = doc->getOutline(); QVERIFY(outline != nullptr); auto *outlineItems = outline->getItems(); QVERIFY(outlineItems == nullptr); // create an outline and save the file outline->setOutline({ { "1", 1, { { "1.1", 1, {} }, { "1.2", 2, {} }, { "1.3", 3, { { "1.3.1", 1, {} }, { "1.3.2", 2, {} }, { "1.3.3", 3, {} }, { "1.3.4", 4, {} } } }, { "1.4", 4, {} } } }, { "2", 2, { { "2.1", 1, {} } } }, { "3", 3, { { "3.1", 1, {} }, { "3.2", 2, { { "3.2.1", 1, {} } } } } }, { "4", 4, {} } }); outlineItems = outline->getItems(); QVERIFY(outlineItems != nullptr); doc->saveAs(gooTempFileName); outline = nullptr; /******************************************************/ doc = PDFDocFactory().createPDFDoc(gooTempFileName); QVERIFY(doc.get()); outline = doc->getOutline(); QVERIFY(outline != nullptr); // remove "3" outline->removeChild(2); // remove "1.3.1" outline->getItems()->at(0)->getKids()->at(2)->removeChild(0); // remove "1.3.4" outline->getItems()->at(0)->getKids()->at(2)->removeChild(2); // remove "2.1" outline->getItems()->at(1)->removeChild(0); // save the file doc->saveAs(gooTempFileName2); outline = nullptr; /******************************************************/ doc = PDFDocFactory().createPDFDoc(gooTempFileName2); QVERIFY(doc.get()); // ensure the re-opened file has an outline outline = doc->getOutline(); QVERIFY(outline != nullptr); outlineItems = outline->getItems(); QVERIFY(outlineItems != nullptr); QVERIFY(outlineItems->size() == 3); OutlineItem *item = outlineItems->at(0); QVERIFY(item != nullptr); // c_str() is used so QCOMPARE prints string correctly on disagree QCOMPARE(getTitle(item).c_str(), "1"); item = outlineItems->at(1); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "2"); item = outlineItems->at(2); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "4"); outlineItems = outlineItems->at(0)->getKids(); outlineItems = outlineItems->at(2)->getKids(); item = outlineItems->at(0); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "1.3.2"); item = outlineItems->at(1); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "1.3.3"); // verify "2.1" is removed, lst length 0 is returned as a nullptr QVERIFY(outline->getItems()->at(1)->getKids() == nullptr); } void TestInternalOutline::testSetTitleAndSetPageDest() { QTemporaryFile tempFile; QVERIFY(tempFile.open()); tempFile.close(); QTemporaryFile tempFile2; QVERIFY(tempFile2.open()); tempFile2.close(); const std::string tempFileName = tempFile.fileName().toStdString(); const GooString gooTempFileName { tempFileName }; const std::string tempFileName2 = tempFile2.fileName().toStdString(); const GooString gooTempFileName2 { tempFileName2 }; std::unique_ptr doc = PDFDocFactory().createPDFDoc(GooString(TESTDATADIR "/unittestcases/truetype.pdf")); QVERIFY(doc.get()); // ensure the file has no existing outline Outline *outline = doc->getOutline(); QVERIFY(outline != nullptr); auto *outlineItems = outline->getItems(); QVERIFY(outlineItems == nullptr); // create an outline and save the file outline->setOutline({ { "1", 1, { { "1.1", 1, {} }, { "1.2", 2, {} }, { "1.3", 3, { { "1.3.1", 1, {} }, { "1.3.2", 2, {} }, { "1.3.3", 3, {} }, { "1.3.4", 4, {} } } }, { "1.4", 4, {} } } }, { "2", 2, { { "2.1", 1, {} } } }, { "3", 3, { { "3.1", 1, {} }, { "3.2", 2, { { "3.2.1", 1, {} } } } } }, { "4", 4, {} } }); outlineItems = outline->getItems(); QVERIFY(outlineItems != nullptr); doc->saveAs(gooTempFileName); outline = nullptr; /******************************************************/ doc = PDFDocFactory().createPDFDoc(gooTempFileName); QVERIFY(doc.get()); outline = doc->getOutline(); QVERIFY(outline != nullptr); // change "1.3.1" OutlineItem *item = outline->getItems()->at(0)->getKids()->at(2)->getKids()->at(0); QCOMPARE(getTitle(item).c_str(), "1.3.1"); item->setTitle("Changed to a different title"); item = outline->getItems()->at(2); { const LinkAction *action = item->getAction(); QVERIFY(action->getKind() == actionGoTo); const LinkGoTo *gotoAction = dynamic_cast(action); const LinkDest *dest = gotoAction->getDest(); QVERIFY(dest->isPageRef() == false); QCOMPARE(dest->getPageNum(), 3); item->setPageDest(1); } // save the file doc->saveAs(gooTempFileName2); outline = nullptr; item = nullptr; /******************************************************/ doc = PDFDocFactory().createPDFDoc(gooTempFileName2); QVERIFY(doc.get()); outline = doc->getOutline(); QVERIFY(outline != nullptr); item = outline->getItems()->at(0)->getKids()->at(2)->getKids()->at(0); QCOMPARE(getTitle(item).c_str(), "Changed to a different title"); { item = outline->getItems()->at(2); const LinkAction *action = item->getAction(); QVERIFY(action->getKind() == actionGoTo); const LinkGoTo *gotoAction = dynamic_cast(action); const LinkDest *dest = gotoAction->getDest(); QVERIFY(dest->isPageRef() == false); QCOMPARE(dest->getPageNum(), 1); } } QTEST_GUILESS_MAIN(TestInternalOutline) #include "check_internal_outline.moc" poppler-24.02.0/qt5/tests/check_lexer.cpp000066400000000000000000000055031455701731300202040ustar00rootroot00000000000000#include #include "Object.h" #include "Lexer.h" class TestLexer : public QObject { Q_OBJECT public: explicit TestLexer(QObject *parent = nullptr) : QObject(parent) { } private slots: void testNumbers(); }; void TestLexer::testNumbers() { char data[] = "0 1 -1 2147483647 -2147483647 2147483648 -2147483648 4294967297 -2147483649 0.1 1.1 -1.1 2147483647.1 -2147483647.1 2147483648.1 -2147483648.1 4294967297.1 -2147483649.1 9223372036854775807 18446744073709551615"; MemStream *stream = new MemStream(data, 0, strlen(data), Object(objNull)); Lexer *lexer = new Lexer(nullptr, stream); QVERIFY(lexer); Object obj; obj = lexer->getObj(); QCOMPARE(obj.getType(), objInt); QCOMPARE(obj.getInt(), 0); obj = lexer->getObj(); QCOMPARE(obj.getType(), objInt); QCOMPARE(obj.getInt(), 1); obj = lexer->getObj(); QCOMPARE(obj.getType(), objInt); QCOMPARE(obj.getInt(), -1); obj = lexer->getObj(); QCOMPARE(obj.getType(), objInt); QCOMPARE(obj.getInt(), 2147483647); obj = lexer->getObj(); QCOMPARE(obj.getType(), objInt); QCOMPARE(obj.getInt(), -2147483647); obj = lexer->getObj(); QCOMPARE(obj.getType(), objInt64); QCOMPARE(obj.getInt64(), 2147483648ll); obj = lexer->getObj(); QCOMPARE(obj.getType(), objInt); QCOMPARE(obj.getInt(), -2147483647 - 1); obj = lexer->getObj(); QCOMPARE(obj.getType(), objInt64); QCOMPARE(obj.getInt64(), 4294967297ll); obj = lexer->getObj(); QCOMPARE(obj.getType(), objInt64); QCOMPARE(obj.getInt64(), -2147483649ll); obj = lexer->getObj(); QCOMPARE(obj.getType(), objReal); QCOMPARE(obj.getReal(), 0.1); obj = lexer->getObj(); QCOMPARE(obj.getType(), objReal); QCOMPARE(obj.getReal(), 1.1); obj = lexer->getObj(); QCOMPARE(obj.getType(), objReal); QCOMPARE(obj.getReal(), -1.1); obj = lexer->getObj(); QCOMPARE(obj.getType(), objReal); QCOMPARE(obj.getReal(), 2147483647.1); obj = lexer->getObj(); QCOMPARE(obj.getType(), objReal); QCOMPARE(obj.getReal(), -2147483647.1); obj = lexer->getObj(); QCOMPARE(obj.getType(), objReal); QCOMPARE(obj.getReal(), 2147483648.1); obj = lexer->getObj(); QCOMPARE(obj.getType(), objReal); QCOMPARE(obj.getReal(), -2147483648.1); obj = lexer->getObj(); QCOMPARE(obj.getType(), objReal); QCOMPARE(obj.getReal(), 4294967297.1); obj = lexer->getObj(); QCOMPARE(obj.getType(), objReal); QCOMPARE(obj.getReal(), -2147483649.1); obj = lexer->getObj(); QCOMPARE(obj.getType(), objInt64); QCOMPARE(obj.getInt64(), 9223372036854775807ll); obj = lexer->getObj(); QCOMPARE(obj.getType(), objReal); QCOMPARE(obj.getReal(), 18446744073709551616.); delete lexer; } QTEST_GUILESS_MAIN(TestLexer) #include "check_lexer.moc" poppler-24.02.0/qt5/tests/check_links.cpp000066400000000000000000000071461455701731300202120ustar00rootroot00000000000000#include #include #include class TestLinks : public QObject { Q_OBJECT public: explicit TestLinks(QObject *parent = nullptr) : QObject(parent) { } private slots: void checkDocumentWithNoDests(); void checkDests_xr01(); void checkDests_xr02(); void checkDocumentURILink(); }; static bool isDestinationValid_pageNumber(const Poppler::LinkDestination *dest, const Poppler::Document *doc) { return dest->pageNumber() > 0 && dest->pageNumber() <= doc->numPages(); } static bool isDestinationValid_name(const Poppler::LinkDestination *dest) { return !dest->destinationName().isEmpty(); } void TestLinks::checkDocumentWithNoDests() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/WithAttachments.pdf"); QVERIFY(doc); std::unique_ptr dest; dest.reset(doc->linkDestination(QStringLiteral("no.dests.in.this.document"))); QVERIFY(!isDestinationValid_pageNumber(dest.get(), doc)); QVERIFY(isDestinationValid_name(dest.get())); delete doc; } void TestLinks::checkDests_xr01() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/xr01.pdf"); QVERIFY(doc); Poppler::Page *page = doc->page(0); QVERIFY(page); QList links = page->links(); QCOMPARE(links.count(), 2); { QCOMPARE(links.at(0)->linkType(), Poppler::Link::Goto); Poppler::LinkGoto *link = static_cast(links.at(0)); const Poppler::LinkDestination dest = link->destination(); QVERIFY(!isDestinationValid_pageNumber(&dest, doc)); QVERIFY(isDestinationValid_name(&dest)); QCOMPARE(dest.destinationName(), QLatin1String("section.1")); } { QCOMPARE(links.at(1)->linkType(), Poppler::Link::Goto); Poppler::LinkGoto *link = static_cast(links.at(1)); const Poppler::LinkDestination dest = link->destination(); QVERIFY(!isDestinationValid_pageNumber(&dest, doc)); QVERIFY(isDestinationValid_name(&dest)); QCOMPARE(dest.destinationName(), QLatin1String("section.2")); } qDeleteAll(links); delete page; delete doc; } void TestLinks::checkDests_xr02() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/xr02.pdf"); QVERIFY(doc); std::unique_ptr dest; dest.reset(doc->linkDestination(QStringLiteral("section.1"))); QVERIFY(isDestinationValid_pageNumber(dest.get(), doc)); QVERIFY(!isDestinationValid_name(dest.get())); dest.reset(doc->linkDestination(QStringLiteral("section.2"))); QVERIFY(isDestinationValid_pageNumber(dest.get(), doc)); QVERIFY(!isDestinationValid_name(dest.get())); dest.reset(doc->linkDestination(QStringLiteral("section.3"))); QVERIFY(!isDestinationValid_pageNumber(dest.get(), doc)); QVERIFY(isDestinationValid_name(dest.get())); delete doc; } void TestLinks::checkDocumentURILink() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/checkbox_issue_159.pdf"); QVERIFY(doc); Poppler::Page *page = doc->page(0); QVERIFY(page); QList links = page->links(); QCOMPARE(links.count(), 1); QCOMPARE(links.at(0)->linkType(), Poppler::Link::Browse); Poppler::LinkBrowse *link = static_cast(links.at(0)); QCOMPARE(link->url(), QLatin1String("http://www.tcpdf.org")); qDeleteAll(links); delete page; delete doc; } QTEST_GUILESS_MAIN(TestLinks) #include "check_links.moc" poppler-24.02.0/qt5/tests/check_metadata.cpp000066400000000000000000000171471455701731300206540ustar00rootroot00000000000000#include #include class TestMetaData : public QObject { Q_OBJECT public: explicit TestMetaData(QObject *parent = nullptr) : QObject(parent) { } private slots: void checkStrings_data(); void checkStrings(); void checkStrings2_data(); void checkStrings2(); void checkStringKeys(); void checkLinearised(); void checkNumPages(); void checkDate(); void checkPageSize(); void checkPortraitOrientation(); void checkLandscapeOrientation(); void checkUpsideDownOrientation(); void checkSeascapeOrientation(); void checkVersion(); void checkPdfId(); void checkNoPdfId(); }; void TestMetaData::checkStrings_data() { QTest::addColumn("key"); QTest::addColumn("value"); QTest::newRow("Author") << "Author" << "Brad Hards"; QTest::newRow("Title") << "Title" << "Two pages"; QTest::newRow("Subject") << "Subject" << "A two page layout for poppler testing"; QTest::newRow("Keywords") << "Keywords" << "Qt4 bindings"; QTest::newRow("Creator") << "Creator" << "iText: cgpdftops CUPS filter"; QTest::newRow("Producer") << "Producer" << "Acrobat Distiller 7.0 for Macintosh"; } void TestMetaData::checkStrings() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/doublepage.pdf"); QVERIFY(doc); QFETCH(QString, key); QFETCH(QString, value); QCOMPARE(doc->info(key), value); delete doc; } void TestMetaData::checkStrings2_data() { QTest::addColumn("key"); QTest::addColumn("value"); QTest::newRow("Title") << "Title" << "Malaga hotels"; QTest::newRow("Author") << "Author" << "Brad Hards"; QTest::newRow("Creator") << "Creator" << "Safari: cgpdftops CUPS filter"; QTest::newRow("Producer") << "Producer" << "Acrobat Distiller 7.0 for Macintosh"; QTest::newRow("Keywords") << "Keywords" << "First\rSecond\rthird"; QTest::newRow("Custom1") << "Custom1" << "CustomValue1"; QTest::newRow("Custom2") << "Custom2" << "CustomValue2"; } void TestMetaData::checkStrings2() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/truetype.pdf"); QVERIFY(doc); QFETCH(QString, key); QFETCH(QString, value); QCOMPARE(doc->info(key), value); delete doc; } void TestMetaData::checkStringKeys() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/truetype.pdf"); QVERIFY(doc); QStringList keyList; keyList << QStringLiteral("Title") << QStringLiteral("Author") << QStringLiteral("Creator") << QStringLiteral("Keywords") << QStringLiteral("CreationDate"); keyList << QStringLiteral("Producer") << QStringLiteral("ModDate") << QStringLiteral("Custom1") << QStringLiteral("Custom2"); keyList.sort(); QStringList keysInDoc = doc->infoKeys(); keysInDoc.sort(); QCOMPARE(keysInDoc, keyList); delete doc; } void TestMetaData::checkLinearised() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/orientation.pdf"); QVERIFY(doc); QVERIFY(doc->isLinearized()); delete doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/truetype.pdf"); QVERIFY(doc); QCOMPARE(doc->isLinearized(), false); delete doc; } void TestMetaData::checkPortraitOrientation() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/orientation.pdf"); QVERIFY(doc); Poppler::Page *page = doc->page(0); QCOMPARE(page->orientation(), Poppler::Page::Portrait); delete page; delete doc; } void TestMetaData::checkNumPages() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/doublepage.pdf"); QVERIFY(doc); QCOMPARE(doc->numPages(), 2); delete doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/truetype.pdf"); QVERIFY(doc); QCOMPARE(doc->numPages(), 1); delete doc; } void TestMetaData::checkDate() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/truetype.pdf"); QVERIFY(doc); QCOMPARE(doc->date(QStringLiteral("ModDate")), QDateTime(QDate(2005, 12, 5), QTime(9, 44, 46), Qt::UTC)); QCOMPARE(doc->date(QStringLiteral("CreationDate")), QDateTime(QDate(2005, 8, 13), QTime(1, 12, 11), Qt::UTC)); delete doc; } void TestMetaData::checkPageSize() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/truetype.pdf"); QVERIFY(doc); Poppler::Page *page = doc->page(0); QCOMPARE(page->pageSize(), QSize(595, 842)); QCOMPARE(page->pageSizeF(), QSizeF(595.22, 842)); delete page; delete doc; } void TestMetaData::checkLandscapeOrientation() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/orientation.pdf"); QVERIFY(doc); Poppler::Page *page = doc->page(1); QCOMPARE(page->orientation(), Poppler::Page::Landscape); delete page; delete doc; } void TestMetaData::checkUpsideDownOrientation() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/orientation.pdf"); QVERIFY(doc); Poppler::Page *page = doc->page(2); QCOMPARE(page->orientation(), Poppler::Page::UpsideDown); delete page; delete doc; } void TestMetaData::checkSeascapeOrientation() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/orientation.pdf"); QVERIFY(doc); Poppler::Page *page = doc->page(3); QCOMPARE(page->orientation(), Poppler::Page::Seascape); delete page; delete doc; } void TestMetaData::checkVersion() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/doublepage.pdf"); QVERIFY(doc); auto pdfVersion = doc->getPdfVersion(); QCOMPARE(pdfVersion.major, 1); QCOMPARE(pdfVersion.minor, 6); delete doc; } void TestMetaData::checkPdfId() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/A6EmbeddedFiles.pdf"); QVERIFY(doc); const QByteArray referencePermanentId("00C9D5B6D8FB11D7A902003065D630AA"); const QByteArray referenceUpdateId("39AECAE6D8FB11D7A902003065D630AA"); { // no IDs wanted, just existance check QVERIFY(doc->getPdfId(nullptr, nullptr)); } { // only permanent ID QByteArray permanentId; QVERIFY(doc->getPdfId(&permanentId, nullptr)); QCOMPARE(permanentId.toUpper(), referencePermanentId); } { // only update ID QByteArray updateId; QVERIFY(doc->getPdfId(nullptr, &updateId)); QCOMPARE(updateId.toUpper(), referenceUpdateId); } { // both IDs QByteArray permanentId; QByteArray updateId; QVERIFY(doc->getPdfId(&permanentId, &updateId)); QCOMPARE(permanentId.toUpper(), referencePermanentId); QCOMPARE(updateId.toUpper(), referenceUpdateId); } delete doc; } void TestMetaData::checkNoPdfId() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/WithActualText.pdf"); QVERIFY(doc); QVERIFY(!doc->getPdfId(nullptr, nullptr)); delete doc; } QTEST_GUILESS_MAIN(TestMetaData) #include "check_metadata.moc" poppler-24.02.0/qt5/tests/check_object.cpp000066400000000000000000000013111455701731300203240ustar00rootroot00000000000000#include #include #include "poppler/Object.h" class TestObject : public QObject { Q_OBJECT public: explicit TestObject(QObject *parent = nullptr) : QObject(parent) { } private slots: void benchDefaultConstructor(); void benchMoveConstructor(); void benchSetToNull(); }; void TestObject::benchDefaultConstructor() { QBENCHMARK { Object obj; } } void TestObject::benchMoveConstructor() { QBENCHMARK { Object src; Object dst { std::move(src) }; } } void TestObject::benchSetToNull() { Object obj; QBENCHMARK { obj.setToNull(); } } QTEST_GUILESS_MAIN(TestObject) #include "check_object.moc" poppler-24.02.0/qt5/tests/check_optcontent.cpp000066400000000000000000000424711455701731300212670ustar00rootroot00000000000000#include #include "PDFDoc.h" #include "GlobalParams.h" #include #include class TestOptionalContent : public QObject { Q_OBJECT public: explicit TestOptionalContent(QObject *parent = nullptr) : QObject(parent) { } private slots: void checkVisPolicy(); void checkNestedLayers(); void checkNoOptionalContent(); void checkIsVisible(); void checkVisibilitySetting(); void checkRadioButtons(); }; void TestOptionalContent::checkVisPolicy() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/vis_policy_test.pdf"); QVERIFY(doc); QVERIFY(doc->hasOptionalContent()); Poppler::OptContentModel *optContent = doc->optionalContentModel(); QModelIndex index; index = optContent->index(0, 0, QModelIndex()); QCOMPARE(optContent->data(index, Qt::DisplayRole).toString(), QLatin1String("A")); QCOMPARE(static_cast(optContent->data(index, Qt::CheckStateRole).toInt()), Qt::Checked); index = optContent->index(1, 0, QModelIndex()); QCOMPARE(optContent->data(index, Qt::DisplayRole).toString(), QLatin1String("B")); QCOMPARE(static_cast(optContent->data(index, Qt::CheckStateRole).toInt()), Qt::Checked); delete doc; } void TestOptionalContent::checkNestedLayers() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/NestedLayers.pdf"); QVERIFY(doc); QVERIFY(doc->hasOptionalContent()); Poppler::OptContentModel *optContent = doc->optionalContentModel(); QModelIndex index; index = optContent->index(0, 0, QModelIndex()); QCOMPARE(optContent->data(index, Qt::DisplayRole).toString(), QLatin1String("Black Text and Green Snow")); QCOMPARE(static_cast(optContent->data(index, Qt::CheckStateRole).toInt()), Qt::Unchecked); index = optContent->index(1, 0, QModelIndex()); QCOMPARE(optContent->data(index, Qt::DisplayRole).toString(), QLatin1String("Mountains and Image")); QCOMPARE(static_cast(optContent->data(index, Qt::CheckStateRole).toInt()), Qt::Checked); // This is a sub-item of "Mountains and Image" QModelIndex subindex = optContent->index(0, 0, index); QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("Image")); QCOMPARE(static_cast(optContent->data(index, Qt::CheckStateRole).toInt()), Qt::Checked); index = optContent->index(2, 0, QModelIndex()); QCOMPARE(optContent->data(index, Qt::DisplayRole).toString(), QLatin1String("Starburst")); QCOMPARE(static_cast(optContent->data(index, Qt::CheckStateRole).toInt()), Qt::Checked); index = optContent->index(3, 0, QModelIndex()); QCOMPARE(optContent->data(index, Qt::DisplayRole).toString(), QLatin1String("Watermark")); QCOMPARE(static_cast(optContent->data(index, Qt::CheckStateRole).toInt()), Qt::Unchecked); delete doc; } void TestOptionalContent::checkNoOptionalContent() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/orientation.pdf"); QVERIFY(doc); QCOMPARE(doc->hasOptionalContent(), false); delete doc; } void TestOptionalContent::checkIsVisible() { globalParams = std::make_unique(); PDFDoc *doc = new PDFDoc(std::make_unique(TESTDATADIR "/unittestcases/vis_policy_test.pdf")); QVERIFY(doc); OCGs *ocgs = doc->getOptContentConfig(); QVERIFY(ocgs); XRef *xref = doc->getXRef(); Object obj; // In this test, both Ref(21,0) and Ref(2,0) are set to On // AnyOn, one element array: // 22 0 obj<>endobj obj = xref->fetch(22, 0); QVERIFY(obj.isDict()); QVERIFY(ocgs->optContentIsVisible(&obj)); // Same again, looking for any leaks or dubious free()'s obj = xref->fetch(22, 0); QVERIFY(obj.isDict()); QVERIFY(ocgs->optContentIsVisible(&obj)); // AnyOff, one element array: // 29 0 obj<>endobj obj = xref->fetch(29, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // AllOn, one element array: // 36 0 obj<>endobj obj = xref->fetch(36, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // AllOff, one element array: // 43 0 obj<>endobj obj = xref->fetch(43, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // AnyOn, multi-element array: // 50 0 obj<>endobj obj = xref->fetch(50, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // AnyOff, multi-element array: // 57 0 obj<>endobj obj = xref->fetch(57, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // AllOn, multi-element array: // 64 0 obj<>endobj obj = xref->fetch(64, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // AllOff, multi-element array: // 71 0 obj<>endobj obj = xref->fetch(71, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); delete doc; globalParams.reset(); } void TestOptionalContent::checkVisibilitySetting() { globalParams = std::make_unique(); PDFDoc *doc = new PDFDoc(std::make_unique(TESTDATADIR "/unittestcases/vis_policy_test.pdf")); QVERIFY(doc); OCGs *ocgs = doc->getOptContentConfig(); QVERIFY(ocgs); XRef *xref = doc->getXRef(); Object obj; // In this test, both Ref(21,0) and Ref(28,0) start On, // based on the file settings Object ref21obj(Ref { 21, 0 }); Ref ref21 = ref21obj.getRef(); OptionalContentGroup *ocgA = ocgs->findOcgByRef(ref21); QVERIFY(ocgA); QVERIFY((ocgA->getName()->cmp("A")) == 0); QCOMPARE(ocgA->getState(), OptionalContentGroup::On); Object ref28obj(Ref { 28, 0 }); Ref ref28 = ref28obj.getRef(); OptionalContentGroup *ocgB = ocgs->findOcgByRef(ref28); QVERIFY(ocgB); QVERIFY((ocgB->getName()->cmp("B")) == 0); QCOMPARE(ocgB->getState(), OptionalContentGroup::On); // turn one Off ocgA->setState(OptionalContentGroup::Off); // AnyOn, one element array: // 22 0 obj<>endobj obj = xref->fetch(22, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // Same again, looking for any leaks or dubious free()'s obj = xref->fetch(22, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // AnyOff, one element array: // 29 0 obj<>endobj obj = xref->fetch(29, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // AllOn, one element array: // 36 0 obj<>endobj obj = xref->fetch(36, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // AllOff, one element array: // 43 0 obj<>endobj obj = xref->fetch(43, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // AnyOn, multi-element array: // 50 0 obj<>endobj obj = xref->fetch(50, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // AnyOff, multi-element array: // 57 0 obj<>endobj obj = xref->fetch(57, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // AllOn, multi-element array: // 64 0 obj<>endobj obj = xref->fetch(64, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // AllOff, multi-element array: // 71 0 obj<>endobj obj = xref->fetch(71, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // Turn the other one off as well (i.e. both are Off) ocgB->setState(OptionalContentGroup::Off); // AnyOn, one element array: // 22 0 obj<>endobj obj = xref->fetch(22, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // Same again, looking for any leaks or dubious free()'s obj = xref->fetch(22, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // AnyOff, one element array: // 29 0 obj<>endobj obj = xref->fetch(29, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // AllOn, one element array: // 36 0 obj<>endobj obj = xref->fetch(36, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // AllOff, one element array: // 43 0 obj<>endobj obj = xref->fetch(43, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // AnyOn, multi-element array: // 50 0 obj<>endobj obj = xref->fetch(50, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // AnyOff, multi-element array: // 57 0 obj<>endobj obj = xref->fetch(57, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // AllOn, multi-element array: // 64 0 obj<>endobj obj = xref->fetch(64, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // AllOff, multi-element array: // 71 0 obj<>endobj obj = xref->fetch(71, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // Turn the first one on again (21 is On, 28 is Off) ocgA->setState(OptionalContentGroup::On); // AnyOn, one element array: // 22 0 obj<>endobj obj = xref->fetch(22, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // Same again, looking for any leaks or dubious free()'s obj = xref->fetch(22, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // AnyOff, one element array: // 29 0 obj<>endobj obj = xref->fetch(29, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // AllOn, one element array: // 36 0 obj<>endobj obj = xref->fetch(36, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // AllOff, one element array: // 43 0 obj<>endobj obj = xref->fetch(43, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // AnyOn, multi-element array: // 50 0 obj<>endobj obj = xref->fetch(50, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // AnyOff, multi-element array: // 57 0 obj<>endobj obj = xref->fetch(57, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // AllOn, multi-element array: // 64 0 obj<>endobj obj = xref->fetch(64, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // AllOff, multi-element array: // 71 0 obj<>endobj obj = xref->fetch(71, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); delete doc; globalParams.reset(); } void TestOptionalContent::checkRadioButtons() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/ClarityOCGs.pdf"); QVERIFY(doc); QVERIFY(doc->hasOptionalContent()); Poppler::OptContentModel *optContent = doc->optionalContentModel(); QModelIndex index; index = optContent->index(0, 0, QModelIndex()); QCOMPARE(optContent->data(index, Qt::DisplayRole).toString(), QLatin1String("Languages")); QCOMPARE(static_cast(optContent->data(index, Qt::CheckStateRole).toInt()), Qt::Unchecked); // These are sub-items of the "Languages" label QModelIndex subindex = optContent->index(0, 0, index); QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("English")); QCOMPARE(static_cast(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Checked); subindex = optContent->index(1, 0, index); QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("French")); QCOMPARE(static_cast(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Unchecked); subindex = optContent->index(2, 0, index); QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("Japanese")); QCOMPARE(static_cast(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Unchecked); // RBGroup of languages, so turning on Japanese should turn off English QVERIFY(optContent->setData(subindex, QVariant(true), Qt::CheckStateRole)); subindex = optContent->index(0, 0, index); QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("English")); QCOMPARE(static_cast(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Unchecked); QCOMPARE(static_cast(subindex.internalPointer())->group()->getState(), OptionalContentGroup::Off); subindex = optContent->index(2, 0, index); QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("Japanese")); QCOMPARE(static_cast(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Checked); QCOMPARE(static_cast(subindex.internalPointer())->group()->getState(), OptionalContentGroup::On); subindex = optContent->index(1, 0, index); QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("French")); QCOMPARE(static_cast(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Unchecked); QCOMPARE(static_cast(subindex.internalPointer())->group()->getState(), OptionalContentGroup::Off); // and turning on French should turn off Japanese QVERIFY(optContent->setData(subindex, QVariant(true), Qt::CheckStateRole)); subindex = optContent->index(0, 0, index); QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("English")); QCOMPARE(static_cast(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Unchecked); QCOMPARE(static_cast(subindex.internalPointer())->group()->getState(), OptionalContentGroup::Off); subindex = optContent->index(2, 0, index); QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("Japanese")); QCOMPARE(static_cast(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Unchecked); QCOMPARE(static_cast(subindex.internalPointer())->group()->getState(), OptionalContentGroup::Off); subindex = optContent->index(1, 0, index); QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("French")); QCOMPARE(static_cast(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Checked); QCOMPARE(static_cast(subindex.internalPointer())->group()->getState(), OptionalContentGroup::On); // and turning off French should leave them all off QVERIFY(optContent->setData(subindex, QVariant(false), Qt::CheckStateRole)); subindex = optContent->index(0, 0, index); QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("English")); QCOMPARE(static_cast(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Unchecked); QCOMPARE(static_cast(subindex.internalPointer())->group()->getState(), OptionalContentGroup::Off); subindex = optContent->index(2, 0, index); QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("Japanese")); QCOMPARE(static_cast(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Unchecked); QCOMPARE(static_cast(subindex.internalPointer())->group()->getState(), OptionalContentGroup::Off); subindex = optContent->index(1, 0, index); QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("French")); QCOMPARE(static_cast(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Unchecked); QCOMPARE(static_cast(subindex.internalPointer())->group()->getState(), OptionalContentGroup::Off); delete doc; } QTEST_GUILESS_MAIN(TestOptionalContent) #include "check_optcontent.moc" poppler-24.02.0/qt5/tests/check_outline.cpp000066400000000000000000000026051455701731300205440ustar00rootroot00000000000000#include #include #include class TestOutline : public QObject { Q_OBJECT public: explicit TestOutline(QObject *parent = nullptr) : QObject(parent) { } private slots: void checkOutline_xr02(); }; void TestOutline::checkOutline_xr02() { std::unique_ptr document { Poppler::Document::load(TESTDATADIR "/unittestcases/xr02.pdf") }; QVERIFY(document.get()); const auto outline = document->outline(); QCOMPARE(outline.size(), 2); const auto &foo = outline[0]; QVERIFY(!foo.isNull()); QCOMPARE(foo.name(), QStringLiteral("foo")); QCOMPARE(foo.isOpen(), false); const auto fooDest = foo.destination(); QVERIFY(!fooDest.isNull()); QCOMPARE(fooDest->pageNumber(), 1); QVERIFY(foo.externalFileName().isEmpty()); QVERIFY(foo.uri().isEmpty()); QVERIFY(!foo.hasChildren()); QVERIFY(foo.children().isEmpty()); const auto &bar = outline[1]; QVERIFY(!bar.isNull()); QCOMPARE(bar.name(), QStringLiteral("bar")); QCOMPARE(bar.isOpen(), false); const auto barDest = bar.destination(); QVERIFY(!barDest.isNull()); QCOMPARE(barDest->pageNumber(), 2); QVERIFY(bar.externalFileName().isEmpty()); QVERIFY(bar.uri().isEmpty()); QVERIFY(!bar.hasChildren()); QVERIFY(bar.children().isEmpty()); } QTEST_GUILESS_MAIN(TestOutline) #include "check_outline.moc" poppler-24.02.0/qt5/tests/check_overprint.cpp000066400000000000000000000017671455701731300211250ustar00rootroot00000000000000#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" poppler-24.02.0/qt5/tests/check_pagelabelinfo.cpp000066400000000000000000000027141455701731300216560ustar00rootroot00000000000000#include #include #include "PageLabelInfo_p.h" #include "config.h" class TestPageLabelInfo : public QObject { Q_OBJECT public: explicit TestPageLabelInfo(QObject *parent = nullptr) : QObject(parent) { } private slots: void testFromDecimal(); void testFromDecimalUnicode(); void testToRoman(); void testFromRoman(); void testToLatin(); void testFromLatin(); }; void TestPageLabelInfo::testFromDecimal() { std::string str { "2342" }; const auto res = fromDecimal(str, false); QCOMPARE(res.first, 2342); QCOMPARE(res.second, true); } void TestPageLabelInfo::testFromDecimalUnicode() { std::unique_ptr str(Poppler::QStringToUnicodeGooString(QString::fromLocal8Bit("2342"))); const auto res = fromDecimal(str->toStr(), str->hasUnicodeMarker()); QCOMPARE(res.first, 2342); QCOMPARE(res.second, true); } void TestPageLabelInfo::testToRoman() { GooString str; toRoman(177, &str, false); QCOMPARE(str.c_str(), "clxxvii"); } void TestPageLabelInfo::testFromRoman() { GooString roman("clxxvii"); QCOMPARE(fromRoman(roman.c_str()), 177); } void TestPageLabelInfo::testToLatin() { GooString str; toLatin(54, &str, false); QCOMPARE(str.c_str(), "bbb"); } void TestPageLabelInfo::testFromLatin() { GooString latin("ddd"); QCOMPARE(fromLatin(latin.c_str()), 56); } QTEST_GUILESS_MAIN(TestPageLabelInfo) #include "check_pagelabelinfo.moc" poppler-24.02.0/qt5/tests/check_pagelayout.cpp000066400000000000000000000020751455701731300212400ustar00rootroot00000000000000#include #include class TestPageLayout : public QObject { Q_OBJECT public: explicit TestPageLayout(QObject *parent = nullptr) : QObject(parent) { } private slots: void checkNone(); void checkSingle(); void checkFacing(); }; void TestPageLayout::checkNone() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/UseNone.pdf"); QVERIFY(doc); QCOMPARE(doc->pageLayout(), Poppler::Document::NoLayout); delete doc; } void TestPageLayout::checkSingle() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/FullScreen.pdf"); QVERIFY(doc); QCOMPARE(doc->pageLayout(), Poppler::Document::SinglePage); delete doc; } void TestPageLayout::checkFacing() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/doublepage.pdf"); QVERIFY(doc); QCOMPARE(doc->pageLayout(), Poppler::Document::TwoPageRight); delete doc; } QTEST_GUILESS_MAIN(TestPageLayout) #include "check_pagelayout.moc" poppler-24.02.0/qt5/tests/check_pagemode.cpp000066400000000000000000000030771455701731300206520ustar00rootroot00000000000000#include #include class TestPageMode : public QObject { Q_OBJECT public: explicit TestPageMode(QObject *parent = nullptr) : QObject(parent) { } private slots: void checkNone(); void checkFullScreen(); void checkAttachments(); void checkThumbs(); void checkOC(); }; void TestPageMode::checkNone() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/UseNone.pdf"); QVERIFY(doc); QCOMPARE(doc->pageMode(), Poppler::Document::UseNone); delete doc; } void TestPageMode::checkFullScreen() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/FullScreen.pdf"); QVERIFY(doc); QCOMPARE(doc->pageMode(), Poppler::Document::FullScreen); delete doc; } void TestPageMode::checkAttachments() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/UseAttachments.pdf"); QVERIFY(doc); QCOMPARE(doc->pageMode(), Poppler::Document::UseAttach); delete doc; } void TestPageMode::checkThumbs() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/UseThumbs.pdf"); QVERIFY(doc); QCOMPARE(doc->pageMode(), Poppler::Document::UseThumbs); delete doc; } void TestPageMode::checkOC() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/UseOC.pdf"); QVERIFY(doc); QCOMPARE(doc->pageMode(), Poppler::Document::UseOC); delete doc; } QTEST_GUILESS_MAIN(TestPageMode) #include "check_pagemode.moc" poppler-24.02.0/qt5/tests/check_password.cpp000066400000000000000000000061371455701731300207330ustar00rootroot00000000000000#include #include class TestPassword : public QObject { Q_OBJECT public: explicit TestPassword(QObject *parent = nullptr) : QObject(parent) { } private slots: void password1(); void password1a(); void password2(); void password2a(); void password2b(); void password3(); void password4(); void password4b(); }; // BUG:4557 void TestPassword::password1() { Poppler::Document *doc; doc = Poppler::Document::load(QString::fromUtf8(TESTDATADIR "/unittestcases/Gday garçon - open.pdf"), "", QString::fromUtf8("garçon").toLatin1()); // clazy:exclude=qstring-allocations QVERIFY(doc); QVERIFY(!doc->isLocked()); delete doc; } void TestPassword::password1a() { Poppler::Document *doc; doc = Poppler::Document::load(QString::fromUtf8(TESTDATADIR "/unittestcases/Gday garçon - open.pdf")); // clazy:exclude=qstring-allocations QVERIFY(doc); QVERIFY(doc->isLocked()); QVERIFY(!doc->unlock("", QString::fromUtf8("garçon").toLatin1())); // clazy:exclude=qstring-allocations QVERIFY(!doc->isLocked()); delete doc; } void TestPassword::password2() { Poppler::Document *doc; doc = Poppler::Document::load(QString::fromUtf8(TESTDATADIR "/unittestcases/Gday garçon - owner.pdf"), QString::fromUtf8("garçon").toLatin1(), ""); // clazy:exclude=qstring-allocations QVERIFY(doc); QVERIFY(!doc->isLocked()); delete doc; } void TestPassword::password2a() { Poppler::Document *doc; doc = Poppler::Document::load(QString::fromUtf8(TESTDATADIR "/unittestcases/Gday garçon - owner.pdf"), QString::fromUtf8("garçon").toLatin1()); // clazy:exclude=qstring-allocations QVERIFY(doc); QVERIFY(!doc->isLocked()); delete doc; } void TestPassword::password2b() { Poppler::Document *doc; doc = Poppler::Document::load(QString::fromUtf8(TESTDATADIR "/unittestcases/Gday garçon - owner.pdf")); QVERIFY(doc); QVERIFY(!doc->isLocked()); QVERIFY(!doc->unlock(QString::fromUtf8("garçon").toLatin1(), "")); // clazy:exclude=qstring-allocations QVERIFY(!doc->isLocked()); delete doc; } void TestPassword::password3() { Poppler::Document *doc; doc = Poppler::Document::load(QString::fromUtf8(TESTDATADIR "/unittestcases/PasswordEncrypted.pdf")); QVERIFY(doc); QVERIFY(doc->isLocked()); QVERIFY(!doc->unlock("", "password")); QVERIFY(!doc->isLocked()); delete doc; } // issue 690 void TestPassword::password4() { Poppler::Document *doc; doc = Poppler::Document::load(QString::fromUtf8(TESTDATADIR "/unittestcases/encrypted-256.pdf")); QVERIFY(doc); QVERIFY(doc->isLocked()); QVERIFY(!doc->unlock("owner-secret", "")); QVERIFY(!doc->isLocked()); delete doc; } // issue 690 void TestPassword::password4b() { Poppler::Document *doc; doc = Poppler::Document::load(QString::fromUtf8(TESTDATADIR "/unittestcases/encrypted-256.pdf")); QVERIFY(doc); QVERIFY(doc->isLocked()); QVERIFY(!doc->unlock("", "user-secret")); QVERIFY(!doc->isLocked()); delete doc; } QTEST_GUILESS_MAIN(TestPassword) #include "check_password.moc" poppler-24.02.0/qt5/tests/check_permissions.cpp000066400000000000000000000020671455701731300214420ustar00rootroot00000000000000#include #include class TestPermissions : public QObject { Q_OBJECT public: explicit TestPermissions(QObject *parent = nullptr) : QObject(parent) { } private slots: void permissions1(); }; void TestPermissions::permissions1() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/orientation.pdf"); QVERIFY(doc); // we are allowed to print QVERIFY(doc->okToPrint()); // we are not allowed to change QVERIFY(!(doc->okToChange())); // we are not allowed to copy or extract content QVERIFY(!(doc->okToCopy())); // we are not allowed to print at high resolution QVERIFY(!(doc->okToPrintHighRes())); // we are not allowed to fill forms QVERIFY(!(doc->okToFillForm())); // we are allowed to extract content for accessibility QVERIFY(doc->okToExtractForAccessibility()); // we are allowed to assemble this document QVERIFY(doc->okToAssemble()); delete doc; } QTEST_GUILESS_MAIN(TestPermissions) #include "check_permissions.moc" poppler-24.02.0/qt5/tests/check_search.cpp000066400000000000000000000564111455701731300203360ustar00rootroot00000000000000#include #include // clazy:excludeall=qstring-allocations class TestSearch : public QObject { Q_OBJECT public: explicit TestSearch(QObject *parent = nullptr) : QObject(parent) { } private slots: void testAcrossLinesSearch(); // leave it first void testAcrossLinesSearchDoubleColumn(); void bug7063(); void testNextAndPrevious(); void testWholeWordsOnly(); void testIgnoreDiacritics(); void testRussianSearch(); // Issue #743 void testDeseretSearch(); // Issue #853 }; void TestSearch::bug7063() { QScopedPointer document(Poppler::Document::load(TESTDATADIR "/unittestcases/bug7063.pdf")); QVERIFY(document); QScopedPointer page(document->page(0)); QVERIFY(page); double rectLeft = 0.0, rectTop = 0.0, rectRight = page->pageSizeF().width(), rectBottom = page->pageSizeF().height(); QCOMPARE(page->search(QStringLiteral(u"non-ascii:"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true); QCOMPARE(page->search(QStringLiteral(u"Ascii"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), false); QCOMPARE(page->search(QStringLiteral(u"Ascii"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop, Poppler::Page::IgnoreCase), true); QCOMPARE(page->search(QStringLiteral(u"latin1:"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), false); QCOMPARE(page->search(QString::fromUtf8("é"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true); QCOMPARE(page->search(QString::fromUtf8("à"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true); QCOMPARE(page->search(QString::fromUtf8("ç"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true); QCOMPARE(page->search(QString::fromUtf8("search \"é\", \"à\" or \"ç\""), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true); QCOMPARE(page->search(QString::fromUtf8("¥µ©"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true); QCOMPARE(page->search(QString::fromUtf8("¥©"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), false); QCOMPARE(page->search(QStringLiteral(u"non-ascii:"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true); QCOMPARE(page->search(QStringLiteral(u"Ascii"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), false); QCOMPARE(page->search(QStringLiteral(u"Ascii"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop, Poppler::Page::IgnoreCase), true); QCOMPARE(page->search(QStringLiteral(u"latin1:"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), false); QCOMPARE(page->search(QString::fromUtf8("é"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true); QCOMPARE(page->search(QString::fromUtf8("à"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true); QCOMPARE(page->search(QString::fromUtf8("ç"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true); QCOMPARE(page->search(QString::fromUtf8("search \"é\", \"à\" or \"ç\""), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true); QCOMPARE(page->search(QString::fromUtf8("¥µ©"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true); QCOMPARE(page->search(QString::fromUtf8("¥©"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), false); } void TestSearch::testNextAndPrevious() { QScopedPointer document(Poppler::Document::load(TESTDATADIR "/unittestcases/xr01.pdf")); QVERIFY(document); QScopedPointer page(document->page(0)); QVERIFY(page); double rectLeft = 0.0, rectTop = 0.0, rectRight = page->pageSizeF().width(), rectBottom = page->pageSizeF().height(); QCOMPARE(page->search(QStringLiteral(u"is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true); QVERIFY(qAbs(rectLeft - 161.44) < 0.01); QVERIFY(qAbs(rectTop - 127.85) < 0.01); QVERIFY(qAbs(rectRight - rectLeft - 6.70) < 0.01); QVERIFY(qAbs(rectBottom - rectTop - 8.85) < 0.01); QCOMPARE(page->search(QStringLiteral(u"is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::NextResult), true); QVERIFY(qAbs(rectLeft - 171.46) < 0.01); QVERIFY(qAbs(rectTop - 127.85) < 0.01); QVERIFY(qAbs(rectRight - rectLeft - 6.70) < 0.01); QVERIFY(qAbs(rectBottom - rectTop - 8.85) < 0.01); QCOMPARE(page->search(QStringLiteral(u"is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::NextResult), true); QVERIFY(qAbs(rectLeft - 161.44) < 0.01); QVERIFY(qAbs(rectTop - 139.81) < 0.01); QVERIFY(qAbs(rectRight - rectLeft - 6.70) < 0.01); QVERIFY(qAbs(rectBottom - rectTop - 8.85) < 0.01); QCOMPARE(page->search(QStringLiteral(u"is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::NextResult), true); QVERIFY(qAbs(rectLeft - 171.46) < 0.01); QVERIFY(qAbs(rectTop - 139.81) < 0.01); QVERIFY(qAbs(rectRight - rectLeft - 6.70) < 0.01); QVERIFY(qAbs(rectBottom - rectTop - 8.85) < 0.01); QCOMPARE(page->search(QStringLiteral(u"is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::NextResult), false); QCOMPARE(page->search(QStringLiteral(u"is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::PreviousResult), true); QVERIFY(qAbs(rectLeft - 161.44) < 0.01); QVERIFY(qAbs(rectTop - 139.81) < 0.01); QVERIFY(qAbs(rectRight - rectLeft - 6.70) < 0.01); QVERIFY(qAbs(rectBottom - rectTop - 8.85) < 0.01); QCOMPARE(page->search(QStringLiteral(u"is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::PreviousResult), true); QVERIFY(qAbs(rectLeft - 171.46) < 0.01); QVERIFY(qAbs(rectTop - 127.85) < 0.01); QVERIFY(qAbs(rectRight - rectLeft - 6.70) < 0.01); QVERIFY(qAbs(rectBottom - rectTop - 8.85) < 0.01); QCOMPARE(page->search(QStringLiteral(u"is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::PreviousResult), true); QVERIFY(qAbs(rectLeft - 161.44) < 0.01); QVERIFY(qAbs(rectTop - 127.85) < 0.01); QVERIFY(qAbs(rectRight - rectLeft - 6.70) < 0.01); QVERIFY(qAbs(rectBottom - rectTop - 8.85) < 0.01); QCOMPARE(page->search(QStringLiteral(u"is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::PreviousResult), false); rectLeft = 0.0, rectTop = 0.0, rectRight = page->pageSizeF().width(), rectBottom = page->pageSizeF().height(); QCOMPARE(page->search(QStringLiteral(u"is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true); QVERIFY(qAbs(rectLeft - 161.44) < 0.01); QVERIFY(qAbs(rectTop - 127.85) < 0.01); QVERIFY(qAbs(rectRight - rectLeft - 6.70) < 0.01); QVERIFY(qAbs(rectBottom - rectTop - 8.85) < 0.01); QCOMPARE(page->search(QStringLiteral(u"is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::NextResult), true); QVERIFY(qAbs(rectLeft - 171.46) < 0.01); QVERIFY(qAbs(rectTop - 127.85) < 0.01); QVERIFY(qAbs(rectRight - rectLeft - 6.70) < 0.01); QVERIFY(qAbs(rectBottom - rectTop - 8.85) < 0.01); QCOMPARE(page->search(QStringLiteral(u"is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::NextResult), true); QVERIFY(qAbs(rectLeft - 161.44) < 0.01); QVERIFY(qAbs(rectTop - 139.81) < 0.01); QVERIFY(qAbs(rectRight - rectLeft - 6.70) < 0.01); QVERIFY(qAbs(rectBottom - rectTop - 8.85) < 0.01); QCOMPARE(page->search(QStringLiteral(u"is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::NextResult), true); QVERIFY(qAbs(rectLeft - 171.46) < 0.01); QVERIFY(qAbs(rectTop - 139.81) < 0.01); QVERIFY(qAbs(rectRight - rectLeft - 6.70) < 0.01); QVERIFY(qAbs(rectBottom - rectTop - 8.85) < 0.01); QCOMPARE(page->search(QStringLiteral(u"is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::NextResult), false); QCOMPARE(page->search(QStringLiteral(u"is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::PreviousResult), true); QVERIFY(qAbs(rectLeft - 161.44) < 0.01); QVERIFY(qAbs(rectTop - 139.81) < 0.01); QVERIFY(qAbs(rectRight - rectLeft - 6.70) < 0.01); QVERIFY(qAbs(rectBottom - rectTop - 8.85) < 0.01); QCOMPARE(page->search(QStringLiteral(u"is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::PreviousResult), true); QVERIFY(qAbs(rectLeft - 171.46) < 0.01); QVERIFY(qAbs(rectTop - 127.85) < 0.01); QVERIFY(qAbs(rectRight - rectLeft - 6.70) < 0.01); QVERIFY(qAbs(rectBottom - rectTop - 8.85) < 0.01); QCOMPARE(page->search(QStringLiteral(u"is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::PreviousResult), true); QVERIFY(qAbs(rectLeft - 161.44) < 0.01); QVERIFY(qAbs(rectTop - 127.85) < 0.01); QVERIFY(qAbs(rectRight - rectLeft - 6.70) < 0.01); QVERIFY(qAbs(rectBottom - rectTop - 8.85) < 0.01); QCOMPARE(page->search(QStringLiteral(u"is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::PreviousResult), false); } void TestSearch::testWholeWordsOnly() { QScopedPointer document(Poppler::Document::load(TESTDATADIR "/unittestcases/WithActualText.pdf")); QVERIFY(document); QScopedPointer page(document->page(0)); QVERIFY(page); const Poppler::Page::SearchDirection direction = Poppler::Page::FromTop; const Poppler::Page::SearchFlags mode0 = nullptr; const Poppler::Page::SearchFlags mode1 = Poppler::Page::IgnoreCase; const Poppler::Page::SearchFlags mode2 = Poppler::Page::WholeWords; const Poppler::Page::SearchFlags mode3 = Poppler::Page::IgnoreCase | Poppler::Page::WholeWords; double left, top, right, bottom; QCOMPARE(page->search(QStringLiteral(u"brown"), left, top, right, bottom, direction, mode0), true); QCOMPARE(page->search(QStringLiteral(u"brOwn"), left, top, right, bottom, direction, mode0), false); QCOMPARE(page->search(QStringLiteral(u"brOwn"), left, top, right, bottom, direction, mode1), true); QCOMPARE(page->search(QStringLiteral(u"brawn"), left, top, right, bottom, direction, mode1), false); QCOMPARE(page->search(QStringLiteral(u"brown"), left, top, right, bottom, direction, mode2), true); QCOMPARE(page->search(QStringLiteral(u"own"), left, top, right, bottom, direction, mode2), false); QCOMPARE(page->search(QStringLiteral(u"brOwn"), left, top, right, bottom, direction, mode3), true); QCOMPARE(page->search(QStringLiteral(u"Own"), left, top, right, bottom, direction, mode3), false); } void TestSearch::testIgnoreDiacritics() { QScopedPointer document(Poppler::Document::load(TESTDATADIR "/unittestcases/Issue637.pdf")); QVERIFY(document); QScopedPointer page(document->page(0)); QVERIFY(page); const Poppler::Page::SearchDirection direction = Poppler::Page::FromTop; const Poppler::Page::SearchFlags mode0 = nullptr; const Poppler::Page::SearchFlags mode1 = Poppler::Page::IgnoreDiacritics; const Poppler::Page::SearchFlags mode2 = Poppler::Page::IgnoreDiacritics | Poppler::Page::IgnoreCase; const Poppler::Page::SearchFlags mode3 = Poppler::Page::IgnoreDiacritics | Poppler::Page::IgnoreCase | Poppler::Page::WholeWords; const Poppler::Page::SearchFlags mode4 = Poppler::Page::IgnoreCase | Poppler::Page::WholeWords; double left, top, right, bottom; // Test pdf (Issue637.pdf) just contains the following three lines: // La cigüeña voló sobre nuestras cabezas. // La cigogne a survolé nos têtes. // Der Storch flog über unsere Köpfe hinweg. QCOMPARE(page->search(QString(), left, top, right, bottom, direction, mode0), false); QCOMPARE(page->search(QStringLiteral("ciguena"), left, top, right, bottom, direction, mode0), false); QCOMPARE(page->search(QStringLiteral("Ciguena"), left, top, right, bottom, direction, mode1), false); QCOMPARE(page->search(QStringLiteral("ciguena"), left, top, right, bottom, direction, mode1), true); QCOMPARE(page->search(QString::fromUtf8("cigüeña"), left, top, right, bottom, direction, mode1), true); QCOMPARE(page->search(QString::fromUtf8("cigüena"), left, top, right, bottom, direction, mode1), false); QCOMPARE(page->search(QString::fromUtf8("Cigüeña"), left, top, right, bottom, direction, mode1), false); QCOMPARE(page->search(QStringLiteral("Ciguena"), left, top, right, bottom, direction, mode2), true); QCOMPARE(page->search(QStringLiteral("ciguena"), left, top, right, bottom, direction, mode2), true); QCOMPARE(page->search(QStringLiteral("Ciguena"), left, top, right, bottom, direction, mode3), true); QCOMPARE(page->search(QStringLiteral("ciguena"), left, top, right, bottom, direction, mode3), true); QCOMPARE(page->search(QString::fromUtf8("cigüeña"), left, top, right, bottom, direction, mode4), true); QCOMPARE(page->search(QString::fromUtf8("Cigüeña"), left, top, right, bottom, direction, mode4), true); QCOMPARE(page->search(QString::fromUtf8("cigüena"), left, top, right, bottom, direction, mode4), false); QCOMPARE(page->search(QStringLiteral("Ciguena"), left, top, right, bottom, direction, mode4), false); QCOMPARE(page->search(QStringLiteral("kopfe"), left, top, right, bottom, direction, mode2), true); QCOMPARE(page->search(QStringLiteral("kopfe"), left, top, right, bottom, direction, mode3), true); QCOMPARE(page->search(QStringLiteral("uber"), left, top, right, bottom, direction, mode0), false); QCOMPARE(page->search(QStringLiteral("uber"), left, top, right, bottom, direction, mode1), true); QCOMPARE(page->search(QStringLiteral("uber"), left, top, right, bottom, direction, mode2), true); QCOMPARE(page->search(QStringLiteral("uber"), left, top, right, bottom, direction, mode3), true); QCOMPARE(page->search(QStringLiteral("vole"), left, top, right, bottom, direction, mode2), true); QCOMPARE(page->search(QStringLiteral("vole"), left, top, right, bottom, direction, mode3), false); QCOMPARE(page->search(QStringLiteral("survole"), left, top, right, bottom, direction, mode3), true); QCOMPARE(page->search(QStringLiteral("tete"), left, top, right, bottom, direction, mode3), false); QCOMPARE(page->search(QStringLiteral("tete"), left, top, right, bottom, direction, mode2), true); QCOMPARE(page->search(QStringLiteral("La Ciguena Volo"), left, top, right, bottom, direction, mode2), true); QCOMPARE(page->search(QStringLiteral("Survole Nos Tetes"), left, top, right, bottom, direction, mode2), true); QCOMPARE(page->search(QStringLiteral("Uber Unsere Kopfe"), left, top, right, bottom, direction, mode2), true); } void TestSearch::testRussianSearch() { // Test for issue #743 QScopedPointer document(Poppler::Document::load(TESTDATADIR "/unittestcases/russian.pdf")); QVERIFY(document); QScopedPointer page(document->page(0)); QVERIFY(page); const Poppler::Page::SearchDirection direction = Poppler::Page::FromTop; const Poppler::Page::SearchFlags mode0 = Poppler::Page::NoSearchFlags; const Poppler::Page::SearchFlags mode1 = Poppler::Page::IgnoreDiacritics; const Poppler::Page::SearchFlags mode2 = Poppler::Page::IgnoreDiacritics | Poppler::Page::IgnoreCase; const Poppler::Page::SearchFlags mode0W = mode0 | Poppler::Page::WholeWords; const Poppler::Page::SearchFlags mode1W = mode1 | Poppler::Page::WholeWords; const Poppler::Page::SearchFlags mode2W = mode2 | Poppler::Page::WholeWords; double l, t, r, b; // left, top, right, bottom // In the searched page 5, these two words do exist: простой and Простой const QString str = QString::fromUtf8("простой"); QCOMPARE(page->search(str, l, t, r, b, direction, mode0), true); QCOMPARE(page->search(str, l, t, r, b, direction, mode1), true); QCOMPARE(page->search(str, l, t, r, b, direction, mode2), true); QCOMPARE(page->search(str, l, t, r, b, direction, mode0W), true); QCOMPARE(page->search(str, l, t, r, b, direction, mode1W), true); QCOMPARE(page->search(str, l, t, r, b, direction, mode2W), true); } void TestSearch::testDeseretSearch() { QScopedPointer document(Poppler::Document::load(TESTDATADIR "/unittestcases/deseret.pdf")); QVERIFY(document); QScopedPointer page(document->page(0)); QVERIFY(page); double l, t, r, b; // left, top, right, bottom const QString str = QString::fromUtf8("𐐐𐐯𐑊𐐬"); QCOMPARE(page->search(str, l, t, r, b, Poppler::Page::FromTop, Poppler::Page::NoSearchFlags), true); const QString str2 = QString::fromUtf8("𐐸𐐯𐑊𐐬"); QCOMPARE(page->search(str2, l, t, r, b, Poppler::Page::FromTop, Poppler::Page::IgnoreCase), true); } void TestSearch::testAcrossLinesSearch() { // Test for searching across lines with new flag Poppler::Page::AcrossLines // and its automatic features like ignoring hyphen at end of line or allowing // whitespace in the search term to match on newline character. QScopedPointer document(Poppler::Document::load(TESTDATADIR "/unittestcases/searchAcrossLines.pdf")); QVERIFY(document); QScopedPointer page(document->page(1)); QVERIFY(page); const Poppler::Page::SearchDirection direction = Poppler::Page::FromTop; const Poppler::Page::SearchFlags empty = Poppler::Page::NoSearchFlags; const Poppler::Page::SearchFlags mode0 = Poppler::Page::AcrossLines; const Poppler::Page::SearchFlags mode1 = Poppler::Page::AcrossLines | Poppler::Page::IgnoreDiacritics; const Poppler::Page::SearchFlags mode2 = Poppler::Page::AcrossLines | Poppler::Page::IgnoreDiacritics | Poppler::Page::IgnoreCase; const Poppler::Page::SearchFlags mode2W = mode2 | Poppler::Page::WholeWords; double l, t, r, b; // left, top, right, bottom // In the searched page, each of "re-conocimiento" "PRUE-BA" "imáge-nes" happen split across lines const QString str1 = QString::fromUtf8("reconocimiento"); const QString str2 = QString::fromUtf8("IMagenes"); // Test it cannot be found with empty search flags QCOMPARE(page->search(str1, l, t, r, b, direction, empty), false); // Test it is found with AcrossLines option QCOMPARE(page->search(str1, l, t, r, b, direction, mode0), true); // Test AcrossLines with IgnoreDiacritics and IgnoreCase options QCOMPARE(page->search(str2, l, t, r, b, direction, mode0), false); QCOMPARE(page->search(str2, l, t, r, b, direction, mode1), false); QCOMPARE(page->search(str2, l, t, r, b, direction, mode2), true); // Test with WholeWords too QCOMPARE(page->search(str2, l, t, r, b, direction, mode2W), true); // Now test that AcrossLines also allows whitespace in the search term to match on newline char. // In the searched page, "podrá" ends a line and "acordar" starts the next line, so we // now test we match it with "podrá acordar" const QString str3 = QString::fromUtf8("podrá acordar,"); QCOMPARE(page->search(str3, l, t, r, b, direction, mode0), true); QCOMPARE(page->search(str3, l, t, r, b, direction, mode1), true); QCOMPARE(page->search(str3, l, t, r, b, direction, mode2), true); QCOMPARE(page->search(str3, l, t, r, b, direction, mode2W), true); // now test it also works with IgnoreDiacritics and IgnoreCase const QString str4 = QString::fromUtf8("PODRA acordar"); QCOMPARE(page->search(str4, l, t, r, b, direction, mode0), false); QCOMPARE(page->search(str4, l, t, r, b, direction, mode1), false); QCOMPARE(page->search(str4, l, t, r, b, direction, mode2), true); QCOMPARE(page->search(str4, l, t, r, b, direction, mode2W), false); // false as it lacks ending comma // Now test that when a hyphen char in the search term matches a hyphen at end of line, // then we don't automatically ignore it, but treat it as a normal char. // In the searched page, "CC BY-NC-SA 4.0" is split across two lines on the second hyphen const QString str5 = QString::fromUtf8("CC BY-NC-SA 4.0"); QScopedPointer page0(document->page(0)); QVERIFY(page0); QCOMPARE(page0->search(str5, l, t, r, b, direction, mode0), true); QCOMPARE(page0->search(str5, l, t, r, b, direction, mode1), true); QCOMPARE(page0->search(str5, l, t, r, b, direction, mode2), true); QCOMPARE(page0->search(str5, l, t, r, b, direction, mode2W), true); QCOMPARE(page0->search(QString::fromUtf8("NC-SA"), l, t, r, b, direction, mode2W), false); // Searching for "CC BY-NCSA 4.0" should also match, because hyphen is now ignored at end of line const QString str6 = QString::fromUtf8("CC BY-NCSA 4.0"); QCOMPARE(page0->search(str6, l, t, r, b, direction, mode0), true); 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); // 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"); 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); QCOMPARE(page->search(full2lines, l, t, r, b, direction, mode2W), true); // And now the full text of two lines split by a hyphenated word const QString full2linesHyphenated = QString::fromUtf8("Consiste básicamente en información digitalizada, codificados y alojados en un elemento contenedor digital (equipos, dispositivos periféricos, unidades de memoria, unidades " "virtualizadas, tramas"); QCOMPARE(page->search(full2linesHyphenated, l, t, r, b, direction, mode0), true); QCOMPARE(page->search(full2linesHyphenated, l, t, r, b, direction, mode1), true); QCOMPARE(page->search(full2linesHyphenated, l, t, r, b, direction, mode2), true); QCOMPARE(page->search(full2linesHyphenated, l, t, r, b, direction, mode2W), true); // BUG about false positives at start of a line. const QString bug_str = QString::fromUtf8("nes y"); // clazy:exclude=qstring-allocations // there's only 1 match, check for that QCOMPARE(page->search(bug_str, mode2).size(), 1); } void TestSearch::testAcrossLinesSearchDoubleColumn() { // Test for searching across lines with new flag Poppler::Page::AcrossLines // in a document with two columns of text. QScopedPointer document(Poppler::Document::load(TESTDATADIR "/unittestcases/searchAcrossLinesDoubleColumn.pdf")); QVERIFY(document); QScopedPointer page(document->page(0)); QVERIFY(page); const Poppler::Page::SearchFlags mode = Poppler::Page::AcrossLines | Poppler::Page::IgnoreDiacritics | Poppler::Page::IgnoreCase; // Test for a bug in double column documents where single line matches are // wrongly returned as being multiline matches. const QString bug_str = QString::fromUtf8("betw"); // clazy:exclude=qstring-allocations // there's only 3 matches for 'betw' in document, where only the last // one is a multiline match, so that's a total of 4 rects returned QCOMPARE(page->search(bug_str, mode).size(), 4); } QTEST_GUILESS_MAIN(TestSearch) #include "check_search.moc" poppler-24.02.0/qt5/tests/check_signature_basics.cpp000066400000000000000000000154451455701731300224200ustar00rootroot00000000000000//======================================================================== // // check_signature_basics.cpp // // This file is licensed under the GPLv2 or later // // Copyright 2023 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) { } private: 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::find(availableBackends.begin(), availableBackends.end(), 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::find(availableBackends.begin(), availableBackends.end(), 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()); QVERIFY(signatureFields[1]->getSignature()); QVERIFY(!signatureFields[2]->getSignature()); QVERIFY(!signatureFields[3]->getSignature()); } 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()->getLength(), 10230); // Signature data size is 2340 QCOMPARE(signatureFields[1]->getSignature()->getLength(), 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(), ETSI_CAdES_detached); auto siginfo0 = signatureFields[0]->validateSignature(false, false, -1 /* now */, false, false); #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(), ETSI_CAdES_detached); auto siginfo1 = signatureFields[1]->validateSignature(false, false, -1 /* now */, false, false); #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(), unsigned_signature_field); QCOMPARE(signatureFields[3]->getCreateWidget()->getField()->getFullyQualifiedName()->toStr(), std::string { "P2.AnA_Signature3_B_" }); QCOMPARE(signatureFields[3]->getSignatureType(), 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" poppler-24.02.0/qt5/tests/check_strings.cpp000066400000000000000000000172201455701731300205550ustar00rootroot00000000000000/* * Copyright (C) 2010, 2011, Pino Toscano * Copyright (C) 2021 Klarälvdalens Datakonsult AB, a KDAB Group company, * * 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 #include Q_DECLARE_METATYPE(GooString *) Q_DECLARE_METATYPE(Unicode *) class TestStrings : public QObject { Q_OBJECT public: explicit TestStrings(QObject *parent = nullptr) : QObject(parent) { } private slots: void initTestCase(); void cleanupTestCase(); void check_unicodeToQString_data(); void check_unicodeToQString(); void check_UnicodeParsedString_data(); void check_UnicodeParsedString(); void check_QStringToUnicodeGooString_data(); 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() { qRegisterMetaType("GooString*"); qRegisterMetaType("Unicode*"); globalParams = std::make_unique(); } void TestStrings::cleanupTestCase() { qDeleteAll(m_gooStrings); globalParams.reset(); } void TestStrings::check_unicodeToQString_data() { QTest::addColumn("data"); QTest::addColumn("length"); QTest::addColumn("result"); { const int l = 1; Unicode *u = new Unicode[l]; u[0] = int('a'); QTest::newRow("a") << u << l << QStringLiteral("a"); } { const int l = 1; Unicode *u = new Unicode[l]; u[0] = 0x0161; QTest::newRow("\u0161") << u << l << QStringLiteral("\u0161"); } { const int l = 2; Unicode *u = new Unicode[l]; u[0] = int('a'); u[1] = int('b'); QTest::newRow("ab") << u << l << QStringLiteral("ab"); } { const int l = 2; Unicode *u = new Unicode[l]; u[0] = int('a'); u[1] = 0x0161; QTest::newRow("a\u0161") << u << l << QStringLiteral("a\u0161"); } { const int l = 2; Unicode *u = new Unicode[l]; u[0] = 0x5c01; u[1] = 0x9762; QTest::newRow("\xe5\xb0\x81\xe9\x9d\xa2") << u << l << QStringLiteral("封面"); } { const int l = 3; Unicode *u = new Unicode[l]; u[0] = 0x5c01; u[1] = 0x9762; u[2] = 0x0; QTest::newRow("\xe5\xb0\x81\xe9\x9d\xa2 + 0") << u << l << QStringLiteral("封面"); } { const int l = 4; Unicode *u = new Unicode[l]; u[0] = 0x5c01; u[1] = 0x9762; u[2] = 0x0; u[3] = 0x0; QTest::newRow("\xe5\xb0\x81\xe9\x9d\xa2 + two 0") << u << l << QStringLiteral("封面"); } } void TestStrings::check_unicodeToQString() { QFETCH(Unicode *, data); QFETCH(int, length); QFETCH(QString, result); QCOMPARE(Poppler::unicodeToQString(data, length), result); delete[] data; } void TestStrings::check_UnicodeParsedString_data() { QTest::addColumn("string"); QTest::addColumn("result"); // non-unicode strings QTest::newRow("") << newGooString("") << QString(); QTest::newRow("a") << newGooString("a") << QStringLiteral("a"); QTest::newRow("ab") << newGooString("ab") << QStringLiteral("ab"); QTest::newRow("~") << newGooString("~") << QStringLiteral("~"); QTest::newRow("test string") << newGooString("test string") << QStringLiteral("test string"); // unicode strings QTest::newRow("") << newGooString("\xFE\xFF") << QString(); QTest::newRow("U a") << newGooString("\xFE\xFF\0a", 4) << QStringLiteral("a"); QTest::newRow("U ~") << newGooString("\xFE\xFF\0~", 4) << QStringLiteral("~"); QTest::newRow("U aa") << newGooString("\xFE\xFF\0a\0a", 6) << QStringLiteral("aa"); QTest::newRow("U \xC3\x9F") << newGooString("\xFE\xFF\0\xDF", 4) << QStringLiteral("ß"); QTest::newRow("U \xC3\x9F\x61") << newGooString("\xFE\xFF\0\xDF\0\x61", 6) << QStringLiteral("ßa"); QTest::newRow("U \xC5\xA1") << newGooString("\xFE\xFF\x01\x61", 4) << QStringLiteral("š"); QTest::newRow("U \xC5\xA1\x61") << newGooString("\xFE\xFF\x01\x61\0\x61", 6) << QStringLiteral("ša"); QTest::newRow("test string") << newGooString("\xFE\xFF\0t\0e\0s\0t\0 \0s\0t\0r\0i\0n\0g", 24) << QStringLiteral("test string"); QTest::newRow("UTF16-LE") << newGooString("\xFF\xFE\xDA\x00\x6E\x00\xEE\x00\x63\x00\xF6\x00\x64\x00\xE9\x00\x51\x75", 18) << QStringLiteral("Únîcödé畑"); } void TestStrings::check_UnicodeParsedString() { QFETCH(GooString *, string); QFETCH(QString, result); QCOMPARE(Poppler::UnicodeParsedString(string), result); } void TestStrings::check_QStringToUnicodeGooString_data() { QTest::addColumn("string"); QTest::addColumn("result"); QTest::newRow("") << QString() << QByteArray(""); QTest::newRow("") << QString(QLatin1String("")) << QByteArray(""); QTest::newRow("a") << QStringLiteral("a") << QByteArray("\0a", 2); QTest::newRow("ab") << QStringLiteral("ab") << QByteArray("\0a\0b", 4); QTest::newRow("test string") << QStringLiteral("test string") << QByteArray("\0t\0e\0s\0t\0 \0s\0t\0r\0i\0n\0g", 22); QTest::newRow("\xC3\x9F") << QStringLiteral("ß") << QByteArray("\0\xDF", 2); QTest::newRow("\xC3\x9F\x61") << QStringLiteral("ßa") << QByteArray("\0\xDF\0\x61", 4); } void TestStrings::check_QStringToUnicodeGooString() { QFETCH(QString, string); QFETCH(QByteArray, result); GooString *goo = Poppler::QStringToUnicodeGooString(string); if (string.isEmpty()) { QVERIFY(goo->toStr().empty()); QCOMPARE(goo->getLength(), 0); } else { QVERIFY(goo->hasUnicodeMarker()); 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() { QTest::addColumn("string"); QTest::addColumn("result"); QTest::newRow("") << QString() << newGooString(""); QTest::newRow("") << QString(QLatin1String("")) << newGooString(""); QTest::newRow("a") << QStringLiteral("a") << newGooString("a"); QTest::newRow("ab") << QStringLiteral("ab") << newGooString("ab"); } void TestStrings::check_QStringToGooString() { QFETCH(QString, string); QFETCH(GooString *, result); GooString *goo = Poppler::QStringToGooString(string); QCOMPARE(goo->c_str(), result->c_str()); delete goo; } GooString *TestStrings::newGooString(const char *s) { GooString *goo = new GooString(s); m_gooStrings.append(goo); return goo; } GooString *TestStrings::newGooString(const char *s, int l) { GooString *goo = new GooString(s, l); m_gooStrings.append(goo); return goo; } QTEST_GUILESS_MAIN(TestStrings) #include "check_strings.moc" poppler-24.02.0/qt5/tests/check_stroke_opacity.cpp000066400000000000000000000066411455701731300221300ustar00rootroot00000000000000#include #include #include #include #include // Unit tests for rendering axial shadings without full opacity class TestStrokeOpacity : public QObject { Q_OBJECT public: explicit TestStrokeOpacity(QObject *parent = nullptr) : QObject(parent) { } private slots: void checkStrokeOpacity_data(); void checkStrokeOpacity(); }; void TestStrokeOpacity::checkStrokeOpacity_data() { QTest::addColumn("backendType"); QTest::newRow("splash") << (int)Poppler::Document::SplashBackend; QTest::newRow("qpainter") << (int)Poppler::Document::QPainterBackend; } void TestStrokeOpacity::checkStrokeOpacity() { QFETCH(int, backendType); auto doc = std::unique_ptr(Poppler::Document::load(TESTDATADIR "/unittestcases/stroke-alpha-pattern.pdf")); QVERIFY(doc != nullptr); doc->setRenderBackend((Poppler::Document::RenderBackend)backendType); // BUG: For some reason splash gets the opacity wrong when antialiasing is switched off if (backendType == (int)Poppler::Document::SplashBackend) { doc->setRenderHint(Poppler::Document::Antialiasing, true); } const auto page = std::unique_ptr(doc->page(0)); QVERIFY(page != nullptr); // Render (at low resolution and with cropped marging) QImage image = page->renderToImage(36, 36, 40, 50, 200, 230); // The actual tests start here // Allow a tolerance. int tolerance; auto approximatelyEqual = [&tolerance](QRgb c0, const QColor &c1) { return std::abs(qAlpha(c0) - c1.alpha()) <= tolerance && std::abs(qRed(c0) - c1.red()) <= tolerance && std::abs(qGreen(c0) - c1.green()) <= tolerance && std::abs(qBlue(c0) - c1.blue()) <= tolerance; }; // At the lower left of the test document is a square with an axial shading, // which should be rendered with opacity 0.25. // Check that with a sample pixel auto pixel = image.pixel(70, 160); // Splash and QPainter backends implement shadings slightly differently, // hence we cannot expect to get precisely the same colors. tolerance = 2; QVERIFY(approximatelyEqual(pixel, QColor(253, 233, 196, 255))); // At the upper left of the test document is a stroked square with an axial shading. // This is implemented by filling a clip region defined by a stroke outline. // Check whether the backend really only renders the stroke, not the region // surrounded by the stroke. auto pixelUpperLeftInterior = image.pixel(70, 70); tolerance = 0; QVERIFY(approximatelyEqual(pixelUpperLeftInterior, Qt::white)); // Now check whether that stroke is semi-transparent. // Bug https://gitlab.freedesktop.org/poppler/poppler/-/issues/178 auto pixelUpperLeftOnStroke = image.pixel(70, 20); tolerance = 2; QVERIFY(approximatelyEqual(pixelUpperLeftOnStroke, QColor(253, 233, 196, 255))); // At the upper right there is a semi-transparent stroked red square // a) Make sure that the color is correct. auto pixelUpperRightOnStroke = image.pixel(130, 20); tolerance = 0; QVERIFY(approximatelyEqual(pixelUpperRightOnStroke, QColor(246, 196, 206, 255))); // b) Make sure that it is really stroked, not filled auto pixelUpperRightInterior = image.pixel(130, 50); QVERIFY(approximatelyEqual(pixelUpperRightInterior, Qt::white)); } QTEST_GUILESS_MAIN(TestStrokeOpacity) #include "check_stroke_opacity.moc" poppler-24.02.0/qt5/tests/check_utf8document.cpp000066400000000000000000000037001455701731300215070ustar00rootroot00000000000000#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" poppler-24.02.0/qt5/tests/check_utf_conversion.cpp000066400000000000000000000144511455701731300221320ustar00rootroot00000000000000#include #include #include #include #include // for uint16_t #include "GlobalParams.h" #include "UnicodeTypeTable.h" #include "UTF.h" class TestUTFConversion : public QObject { Q_OBJECT public: explicit TestUTFConversion(QObject *parent = nullptr) : QObject(parent) { } private slots: void testUTF_data(); void testUTF(); void testUnicodeToAscii7(); 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++) { if (a[i] != (Unicode)b[i]) { return false; } } return true; } static bool compare(const Unicode *a, const uint16_t *b, int len) { for (int i = 0; i < len; i++) { if (a[i] != b[i]) { return false; } } return true; } void TestUTFConversion::testUTF_data() { QTest::addColumn("s"); QTest::newRow("") << QString(QLatin1String("")); QTest::newRow("a") << QStringLiteral("a"); QTest::newRow("abc") << QStringLiteral("abc"); QTest::newRow("Latin") << QStringLiteral("Vitrum edere possum; mihi non nocet"); QTest::newRow("Greek") << QStringLiteral("Μπορώ να φάω σπασμένα γυαλιά χωρίς να πάθω τίποτα"); QTest::newRow("Icelandic") << QStringLiteral("Ég get etið gler án þess að meiða mig"); QTest::newRow("Russian") << QStringLiteral("Я могу есть стекло, оно мне не вредит."); QTest::newRow("Sanskrit") << QStringLiteral("काचं शक्नोम्यत्तुम् । नोपहिनस्ति माम् ॥"); QTest::newRow("Arabic") << QStringLiteral("أنا قادر على أكل الزجاج و هذا لا يؤلمني"); QTest::newRow("Chinese") << QStringLiteral("我能吞下玻璃而不伤身体。"); QTest::newRow("Thai") << QStringLiteral("ฉันกินกระจกได้ แต่มันไม่ทำให้ฉันเจ็บ"); QTest::newRow("non BMP") << QStringLiteral("𝓹𝓸𝓹𝓹𝓵𝓮𝓻"); } void TestUTFConversion::testUTF() { char utf8Buf[1000]; char *utf8String; uint16_t utf16Buf[1000]; uint16_t *utf16String; int len; QFETCH(QString, s); char *str = strdup(s.toUtf8().constData()); // 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, sizeof(utf16Buf), INT_MAX); QVERIFY(compare(utf16Buf, s.utf16())); QCOMPARE(len, s.size()); utf16String = utf8ToUtf16(str); QVERIFY(compare(utf16String, s.utf16())); free(utf16String); std::string sUtf8(str); std::string gsUtf16_a(utf8ToUtf16WithBom(sUtf8)); std::unique_ptr gsUtf16_b(Poppler::QStringToUnicodeGooString(s)); 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 len = utf16ToUtf8(s.utf16(), utf8Buf); QVERIFY(compare(utf8Buf, str)); QCOMPARE(len, (int)strlen(str)); utf8String = utf16ToUtf8(s.utf16()); QVERIFY(compare(utf8String, str)); free(utf8String); free(str); } void TestUTFConversion::testUnicodeToAscii7() { globalParams = std::make_unique(); // 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 const std::vector in = TextStringToUCS4(goo->toStr()); delete goo; int in_norm_len; int *in_norm_idx; 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); free(in_norm); free(in_norm_idx); // ascii7 conversion: ® -> (R) © -> (c) const char *expected_ascii = (char *)"(R)(c)(c)(c)(c)(c)(c)(c)(c)(c)(c)(c)(c)(c)(c)(c)(c)(c)(c)(c)(c)"; QCOMPARE(out_len, (int)strlen(expected_ascii)); QVERIFY(compare(out, expected_ascii, out_len)); free(out); free(out_ascii_idx); } void TestUTFConversion::testUnicodeLittleEndian() { uint16_t UTF16LE_hi[5] { 0xFFFE, 0x4800, 0x4900, 0x2100, 0x1126 }; // UTF16-LE "HI!☑" std::string GooUTF16LE(reinterpret_cast(UTF16LE_hi), sizeof(UTF16LE_hi)); uint16_t UTF16BE_hi[5] { 0xFEFF, 0x0048, 0x0049, 0x0021, 0x2611 }; // UTF16-BE "HI!☑" std::string GooUTF16BE(reinterpret_cast(UTF16BE_hi), sizeof(UTF16BE_hi)); // Let's assert both GooString's are different QVERIFY(GooUTF16LE != GooUTF16BE); 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(UCS4fromLE.size(), UCS4fromBE.size()); QCOMPARE(UCS4fromLE.size(), 4); // Check that now after conversion, UCS4fromLE and UCS4fromBE are now the same 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.data(), expected.utf16(), UCS4fromLE.size())); QVERIFY(compare(UCS4fromBE.data(), expected.utf16(), UCS4fromLE.size())); } QTEST_GUILESS_MAIN(TestUTFConversion) #include "check_utf_conversion.moc" poppler-24.02.0/qt5/tests/fuzzing/000077500000000000000000000000001455701731300167155ustar00rootroot00000000000000poppler-24.02.0/qt5/tests/fuzzing/qt_annot_fuzzer.cc000066400000000000000000000026411455701731300224570ustar00rootroot00000000000000#include #include #include static void dummy_error_function(const QString &, const QVariant &) { } extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { Poppler::setDebugErrorFunction(dummy_error_function, QVariant()); const QFont font(QStringLiteral("Helvetica"), 20); const QColor color = QColor::fromRgb(0xAB, 0xCD, 0xEF); QByteArray in_data = QByteArray::fromRawData((const char *)data, size); Poppler::Document *doc = Poppler::Document::loadFromData(in_data); if (!doc || doc->isLocked()) { delete doc; return 0; } for (int i = 0; i < doc->numPages(); i++) { Poppler::Page *p = doc->page(i); if (!p) { continue; } Poppler::TextAnnotation *ann = new Poppler::TextAnnotation(Poppler::TextAnnotation::InPlace); ann->setTextFont(font); ann->setTextColor(color); ann->setBoundary(QRectF(0.1, 0.1, 0.2, 0.2)); ann->setContents(QString(in_data)); p->addAnnotation(ann); QBuffer buffer; buffer.open(QIODevice::WriteOnly); std::unique_ptr conv(doc->pdfConverter()); conv->setOutputDevice(&buffer); conv->setPDFOptions(Poppler::PDFConverter::WithChanges); conv->convert(); buffer.close(); delete ann; delete p; } delete doc; return 0; } poppler-24.02.0/qt5/tests/fuzzing/qt_label_fuzzer.cc000066400000000000000000000015201455701731300224120ustar00rootroot00000000000000#include #include #include static void dummy_error_function(const QString &, const QVariant &) { } extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { Poppler::setDebugErrorFunction(dummy_error_function, QVariant()); QByteArray in_data = QByteArray::fromRawData((const char *)data, size); Poppler::Document *doc = Poppler::Document::loadFromData(in_data); if (!doc || doc->isLocked()) { delete doc; return 0; } for (int i = 0; i < doc->numPages(); i++) { QString label = QString(in_data); Poppler::Page *p = doc->page(label); if (!p) { continue; } QImage image = p->renderToImage(72.0, 72.0, -1, -1, -1, -1, Poppler::Page::Rotate0); delete p; } delete doc; return 0; } poppler-24.02.0/qt5/tests/fuzzing/qt_pdf_fuzzer.cc000066400000000000000000000026011455701731300221050ustar00rootroot00000000000000#include #include #include #include static void dummy_error_function(const QString &, const QVariant &) { } extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { Poppler::setDebugErrorFunction(dummy_error_function, QVariant()); QByteArray in_data = QByteArray::fromRawData((const char *)data, size); Poppler::Document *doc = Poppler::Document::loadFromData(in_data); if (!doc || doc->isLocked()) { delete doc; return 0; } for (int i = 0; i < doc->numPages(); i++) { Poppler::Page *p = doc->page(i); if (!p) { continue; } QImage image = p->renderToImage(72.0, 72.0, -1, -1, -1, -1, Poppler::Page::Rotate0); delete p; } if (doc->numPages() > 0) { QList pageList; for (int i = 0; i < doc->numPages(); i++) { pageList << (i + 1); } Poppler::PSConverter *psConverter = doc->psConverter(); QBuffer buffer; buffer.open(QIODevice::WriteOnly); psConverter->setOutputDevice(&buffer); psConverter->setPageList(pageList); psConverter->setPaperWidth(595); psConverter->setPaperHeight(842); psConverter->setTitle(doc->info("Title")); psConverter->convert(); delete psConverter; } delete doc; return 0; } poppler-24.02.0/qt5/tests/fuzzing/qt_search_fuzzer.cc000066400000000000000000000014421455701731300226030ustar00rootroot00000000000000#include #include static void dummy_error_function(const QString &, const QVariant &) { } extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { Poppler::setDebugErrorFunction(dummy_error_function, QVariant()); QByteArray in_data = QByteArray::fromRawData((const char *)data, size); Poppler::Document *doc = Poppler::Document::loadFromData(in_data); if (!doc || doc->isLocked()) { delete doc; return 0; } for (int i = 0; i < doc->numPages(); i++) { Poppler::Page *p = doc->page(i); if (!p) { continue; } QString text = QString(in_data); p->search(text, Poppler::Page::IgnoreCase, Poppler::Page::Rotate0); delete p; } delete doc; return 0; } poppler-24.02.0/qt5/tests/fuzzing/qt_textbox_fuzzer.cc000066400000000000000000000017541455701731300230410ustar00rootroot00000000000000#include #include #include #include static void dummy_error_function(const QString &, const QVariant &) { } extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { Poppler::setDebugErrorFunction(dummy_error_function, QVariant()); QByteArray in_data = QByteArray::fromRawData((const char *)data, size); Poppler::Document *doc = Poppler::Document::loadFromData(in_data); if (!doc || doc->isLocked()) { delete doc; return 0; } for (int i = 0; i < doc->numPages(); i++) { Poppler::Page *p = doc->page(i); if (!p) { continue; } QRectF rf = QRectF(0.0, 0.0, 1.0, 1.0); Poppler::TextBox tb(QString(in_data), rf); QImage image = p->renderToImage(72.0, 72.0, -1, -1, -1, -1, Poppler::Page::Rotate0); QPainter painter(&image); painter.drawRect(tb.boundingBox()); delete p; } delete doc; return 0; } poppler-24.02.0/qt5/tests/poppler-attachments.cpp000066400000000000000000000020041455701731300217130ustar00rootroot00000000000000#include #include #include #include int main(int argc, char **argv) { QCoreApplication a(argc, argv); // QApplication required! if (!(argc == 2)) { qWarning() << "usage: poppler-attachments filename"; exit(1); } Poppler::Document *doc = Poppler::Document::load(argv[1]); if (!doc) { qWarning() << "doc not loaded"; exit(1); } if (doc->hasEmbeddedFiles()) { std::cout << "Embedded files: " << std::endl; foreach (Poppler::EmbeddedFile *file, doc->embeddedFiles()) { std::cout << " " << qPrintable(file->name()) << std::endl; std::cout << " desc:" << qPrintable(file->description()) << std::endl; QByteArray data = file->data(); std::cout << " data: " << data.constData() << std::endl; } } else { std::cout << "There are no embedded document at the top level" << std::endl; } delete doc; } poppler-24.02.0/qt5/tests/poppler-fonts.cpp000066400000000000000000000051651455701731300205440ustar00rootroot00000000000000#include #include #include #include int main(int argc, char **argv) { QCoreApplication a(argc, argv); // QApplication required! if (!(argc == 2)) { qWarning() << "usage: poppler-fonts filename"; exit(1); } Poppler::Document *doc = Poppler::Document::load(argv[1]); if (!doc) { qWarning() << "doc not loaded"; exit(1); } std::cout << "name type emb sub font file"; std::cout << std::endl; std::cout << "------------------------------------ ------------ --- --- ---------"; std::cout << std::endl; foreach (const Poppler::FontInfo &font, doc->fonts()) { if (font.name().isNull()) { std::cout << qPrintable(QStringLiteral("%1").arg(QStringLiteral("[none]"), -37)); } else { std::cout << qPrintable(QStringLiteral("%1").arg(font.name(), -37)); } switch (font.type()) { case Poppler::FontInfo::unknown: std::cout << "unknown "; break; case Poppler::FontInfo::Type1: std::cout << "Type 1 "; break; case Poppler::FontInfo::Type1C: std::cout << "Type 1C "; break; case Poppler::FontInfo::Type3: std::cout << "Type 3 "; break; case Poppler::FontInfo::TrueType: std::cout << "TrueType "; break; case Poppler::FontInfo::CIDType0: std::cout << "CID Type 0 "; break; case Poppler::FontInfo::CIDType0C: std::cout << "CID Type 0C "; break; case Poppler::FontInfo::CIDTrueType: std::cout << "CID TrueType "; break; case Poppler::FontInfo::Type1COT: std::cout << "Type 1C (OT) "; break; case Poppler::FontInfo::TrueTypeOT: std::cout << "TrueType (OT) "; break; case Poppler::FontInfo::CIDType0COT: std::cout << "CID Type 0C (OT) "; break; case Poppler::FontInfo::CIDTrueTypeOT: std::cout << "CID TrueType (OT) "; break; } if (font.isEmbedded()) { std::cout << "yes "; } else { std::cout << "no "; } if (font.isSubset()) { std::cout << "yes "; } else { std::cout << "no "; } std::cout << qPrintable(font.file()); std::cout << std::endl; } delete doc; } poppler-24.02.0/qt5/tests/poppler-forms.cpp000066400000000000000000000233561455701731300205430ustar00rootroot00000000000000#include #include #include #include #include #include static std::ostream &operator<<(std::ostream &out, Poppler::FormField::FormType type) { switch (type) { case Poppler::FormField::FormButton: out << "Button"; break; case Poppler::FormField::FormText: out << "Text"; break; case Poppler::FormField::FormChoice: out << "Choice"; break; case Poppler::FormField::FormSignature: out << "Signature"; break; } return out; } static std::ostream &operator<<(std::ostream &out, Poppler::FormFieldButton::ButtonType type) { switch (type) { case Poppler::FormFieldButton::Push: out << "Push"; break; case Poppler::FormFieldButton::CheckBox: out << "CheckBox"; break; case Poppler::FormFieldButton::Radio: out << "Radio"; break; } return out; } static std::ostream &operator<<(std::ostream &out, Poppler::FormFieldText::TextType type) { switch (type) { case Poppler::FormFieldText::Normal: out << "Normal"; break; case Poppler::FormFieldText::Multiline: out << "Multiline"; break; case Poppler::FormFieldText::FileSelect: out << "FileSelect"; break; } return out; } static std::ostream &operator<<(std::ostream &out, Poppler::FormFieldChoice::ChoiceType type) { switch (type) { case Poppler::FormFieldChoice::ComboBox: out << "ComboBox"; break; case Poppler::FormFieldChoice::ListBox: out << "ListBox"; break; } return out; } static std::ostream &operator<<(std::ostream &out, Poppler::SignatureValidationInfo::SignatureStatus status) { switch (status) { case Poppler::SignatureValidationInfo::SignatureValid: out << "Valid"; break; case Poppler::SignatureValidationInfo::SignatureInvalid: out << "Invalid"; break; case Poppler::SignatureValidationInfo::SignatureDigestMismatch: out << "DigestMismatch"; break; case Poppler::SignatureValidationInfo::SignatureDecodingError: out << "DecodingError"; break; case Poppler::SignatureValidationInfo::SignatureGenericError: out << "GenericError"; break; case Poppler::SignatureValidationInfo::SignatureNotFound: out << "NotFound"; break; case Poppler::SignatureValidationInfo::SignatureNotVerified: out << "NotVerifiedYet"; break; } return out; } static std::ostream &operator<<(std::ostream &out, Poppler::SignatureValidationInfo::CertificateStatus status) { switch (status) { case Poppler::SignatureValidationInfo::CertificateTrusted: out << "Trusted"; break; case Poppler::SignatureValidationInfo::CertificateUntrustedIssuer: out << "UntrustedIssuer"; break; case Poppler::SignatureValidationInfo::CertificateUnknownIssuer: out << "UnknownIssuer"; break; case Poppler::SignatureValidationInfo::CertificateRevoked: out << "Revoked"; break; case Poppler::SignatureValidationInfo::CertificateExpired: out << "Expired"; break; case Poppler::SignatureValidationInfo::CertificateGenericError: out << "GenericError"; break; case Poppler::SignatureValidationInfo::CertificateNotVerified: out << "NotVerifiedYet"; break; } return out; } static std::ostream &operator<<(std::ostream &out, Qt::Alignment alignment) { switch (alignment) { case Qt::AlignLeft: out << "Left"; break; case Qt::AlignRight: out << "Right"; break; case Qt::AlignHCenter: out << "HCenter"; break; case Qt::AlignJustify: out << "Justify"; break; case Qt::AlignTop: out << "Top"; break; case Qt::AlignBottom: out << "Bottom"; break; case Qt::AlignVCenter: out << "VCenter"; break; case Qt::AlignCenter: out << "Center"; break; case Qt::AlignAbsolute: out << "Absolute"; break; } return out; } static std::ostream &operator<<(std::ostream &out, const QString &string) { out << string.toUtf8().constData(); return out; } static std::ostream &operator<<(std::ostream &out, const QRectF &rect) { out << QStringLiteral("top: %1 left: %2 width: %3 height: %4").arg(rect.x()).arg(rect.y()).arg(rect.width()).arg(rect.height()); return out; } template std::ostream &operator<<(std::ostream &out, const QList &elems) { bool isFirst = true; for (int i = 0; i < elems.count(); ++i) { if (!isFirst) { out << " "; } out << elems[i]; isFirst = false; } return out; } int main(int argc, char **argv) { QCoreApplication a(argc, argv); if (!(argc == 2)) { qWarning() << "usage: poppler-forms filename"; exit(1); } Poppler::Document *doc = Poppler::Document::load(argv[1]); if (!doc) { qWarning() << "doc not loaded"; exit(1); } std::cout << "Forms for file " << argv[1] << std::endl; for (int i = 0; i < doc->numPages(); ++i) { Poppler::Page *page = doc->page(i); if (page) { QList forms = page->formFields(); std::cout << "\tPage " << i + 1 << std::endl; foreach (const Poppler::FormField *form, forms) { std::cout << "\t\tForm" << std::endl; std::cout << "\t\t\tType: " << form->type() << std::endl; std::cout << "\t\t\tRect: " << form->rect() << std::endl; std::cout << "\t\t\tID: " << form->id() << std::endl; std::cout << "\t\t\tName: " << form->name() << std::endl; std::cout << "\t\t\tFullyQualifiedName: " << form->fullyQualifiedName() << std::endl; std::cout << "\t\t\tUIName: " << form->uiName() << std::endl; std::cout << "\t\t\tReadOnly: " << form->isReadOnly() << std::endl; std::cout << "\t\t\tVisible: " << form->isVisible() << std::endl; switch (form->type()) { case Poppler::FormField::FormButton: { const Poppler::FormFieldButton *buttonForm = static_cast(form); std::cout << "\t\t\tButtonType: " << buttonForm->buttonType() << std::endl; std::cout << "\t\t\tCaption: " << buttonForm->caption() << std::endl; std::cout << "\t\t\tState: " << buttonForm->state() << std::endl; std::cout << "\t\t\tSiblings: " << buttonForm->siblings() << std::endl; } break; case Poppler::FormField::FormText: { const Poppler::FormFieldText *textForm = static_cast(form); std::cout << "\t\t\tTextType: " << textForm->textType() << std::endl; std::cout << "\t\t\tText: " << textForm->text() << std::endl; std::cout << "\t\t\tIsPassword: " << textForm->isPassword() << std::endl; std::cout << "\t\t\tIsRichText: " << textForm->isRichText() << std::endl; std::cout << "\t\t\tMaximumLength: " << textForm->maximumLength() << std::endl; std::cout << "\t\t\tTextAlignment: " << textForm->textAlignment() << std::endl; std::cout << "\t\t\tCanBeSpellChecked: " << textForm->canBeSpellChecked() << std::endl; } break; case Poppler::FormField::FormChoice: { const Poppler::FormFieldChoice *choiceForm = static_cast(form); std::cout << "\t\t\tChoiceType: " << choiceForm->choiceType() << std::endl; std::cout << "\t\t\tChoices: " << choiceForm->choices() << std::endl; std::cout << "\t\t\tIsEditable: " << choiceForm->isEditable() << std::endl; std::cout << "\t\t\tIsMultiSelect: " << choiceForm->multiSelect() << std::endl; std::cout << "\t\t\tCurrentChoices: " << choiceForm->currentChoices() << std::endl; std::cout << "\t\t\tEditChoice: " << choiceForm->editChoice() << std::endl; std::cout << "\t\t\tTextAlignment: " << choiceForm->textAlignment() << std::endl; std::cout << "\t\t\tCanBeSpellChecked: " << choiceForm->canBeSpellChecked() << std::endl; } break; case Poppler::FormField::FormSignature: { const Poppler::FormFieldSignature *signatureForm = static_cast(form); const Poppler::SignatureValidationInfo svi = signatureForm->validate(Poppler::FormFieldSignature::ValidateVerifyCertificate); std::cout << "\t\t\tSignatureStatus: " << svi.signatureStatus() << std::endl; std::cout << "\t\t\tCertificateStatus: " << svi.certificateStatus() << std::endl; if (svi.signerName().isEmpty() == false) { std::cout << "\t\t\tSignerName: " << svi.signerName() << std::endl; } else { std::cout << "\t\t\tSignerName: " << "(null)" << std::endl; } const QDateTime sviTime = QDateTime::fromSecsSinceEpoch(svi.signingTime(), Qt::UTC); std::cout << "\t\t\tSigningTime: " << sviTime.toString() << std::endl; } break; } } qDeleteAll(forms); delete page; } } delete doc; } poppler-24.02.0/qt5/tests/poppler-page-labels.cpp000066400000000000000000000024751455701731300215700ustar00rootroot00000000000000#include #include #include #include #include int main(int argc, char **argv) { QCoreApplication a(argc, argv); // QApplication required! if (!(argc == 2)) { qWarning() << "usage: poppler-page-labels filename"; exit(1); } Poppler::Document *doc = Poppler::Document::load(argv[1]); if (!doc || doc->isLocked()) { qWarning() << "doc not loaded"; exit(1); } for (int i = 0; i < doc->numPages(); i++) { int j = 0; std::cout << "*** Label of Page " << i << std::endl; std::cout << std::flush; std::unique_ptr page(doc->page(i)); if (!page) { continue; } const QByteArray utf8str = page->label().toUtf8(); for (j = 0; j < utf8str.size(); j++) { std::cout << utf8str[j]; } std::cout << std::endl; std::unique_ptr pageFromPageLabel(doc->page(page->label())); const int indexFromPageLabel = pageFromPageLabel ? pageFromPageLabel->index() : -1; if (indexFromPageLabel != i) { std::cout << "WARNING: Page label didn't link back to the same page index " << indexFromPageLabel << " " << i << std::endl; } } delete doc; } poppler-24.02.0/qt5/tests/poppler-texts.cpp000066400000000000000000000016651455701731300205630ustar00rootroot00000000000000#include #include #include #include int main(int argc, char **argv) { QCoreApplication a(argc, argv); // QApplication required! if (!(argc == 2)) { qWarning() << "usage: poppler-texts filename"; exit(1); } Poppler::Document *doc = Poppler::Document::load(argv[1]); if (!doc) { qWarning() << "doc not loaded"; exit(1); } for (int i = 0; i < doc->numPages(); i++) { int j = 0; std::cout << "*** Page " << i << std::endl; std::cout << std::flush; Poppler::Page *page = doc->page(i); const QByteArray utf8str = page->text(QRectF(), Poppler::Page::RawOrderLayout).toUtf8(); std::cout << std::flush; for (j = 0; j < utf8str.size(); j++) { std::cout << utf8str[j]; } std::cout << std::endl; delete page; } delete doc; } poppler-24.02.0/qt5/tests/stress-poppler-dir.cpp000066400000000000000000000045761455701731300215170ustar00rootroot00000000000000#include #include #include #include #include #include #include int main(int argc, char **argv) { QApplication a(argc, argv); // QApplication required! QElapsedTimer t; t.start(); QDir directory(argv[1]); 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); if (!doc) { qWarning() << "doc not loaded"; } else if (doc->isLocked()) { if (!doc->unlock("", "password")) { qWarning() << "couldn't unlock document"; delete doc; } } else { auto pdfVersion = doc->getPdfVersion(); if (pdfVersion.major != 1) { qWarning() << "pdf major version is not '1'"; } doc->info(QStringLiteral("Title")); doc->info(QStringLiteral("Subject")); doc->info(QStringLiteral("Author")); doc->info(QStringLiteral("Keywords")); doc->info(QStringLiteral("Creator")); doc->info(QStringLiteral("Producer")); doc->date(QStringLiteral("CreationDate")).toString(); doc->date(QStringLiteral("ModDate")).toString(); doc->numPages(); doc->isLinearized(); doc->isEncrypted(); doc->okToPrint(); doc->okToCopy(); doc->okToChange(); doc->okToAddNotes(); doc->pageMode(); for (int index = 0; index < doc->numPages(); ++index) { Poppler::Page *page = doc->page(index); page->renderToImage(); page->pageSize(); page->orientation(); delete page; std::cout << "."; std::cout.flush(); } std::cout << std::endl; delete doc; } } } std::cout << "Elapsed time: " << (t.elapsed() / 1000) << "seconds" << std::endl; } poppler-24.02.0/qt5/tests/stress-poppler-qt5.cpp000066400000000000000000000047231455701731300214440ustar00rootroot00000000000000#include #include #include #include #include #include #include int main(int argc, char **argv) { QApplication a(argc, argv); // QApplication required! Q_UNUSED(argc); Q_UNUSED(argv); QElapsedTimer t; t.start(); QDir dbDir(QStringLiteral("./pdfdb")); if (!dbDir.exists()) { qWarning() << "Database directory does not exist"; } QStringList excludeSubDirs; excludeSubDirs << QStringLiteral("000048") << QStringLiteral("000607"); const QStringList dirs = dbDir.entryList(QStringList() << QStringLiteral("0000*"), QDir::Dirs); foreach (const QString &subdir, dirs) { if (excludeSubDirs.contains(subdir)) { // then skip it } else { QString path = "./pdfdb/" + subdir + "/data.pdf"; std::cout << "Doing " << path.toLatin1().data() << " :"; Poppler::Document *doc = Poppler::Document::load(path); if (!doc) { qWarning() << "doc not loaded"; } else { auto pdfVersion = doc->getPdfVersion(); Q_UNUSED(pdfVersion); doc->info(QStringLiteral("Title")); doc->info(QStringLiteral("Subject")); doc->info(QStringLiteral("Author")); doc->info(QStringLiteral("Keywords")); doc->info(QStringLiteral("Creator")); doc->info(QStringLiteral("Producer")); doc->date(QStringLiteral("CreationDate")).toString(); doc->date(QStringLiteral("ModDate")).toString(); doc->numPages(); doc->isLinearized(); doc->isEncrypted(); doc->okToPrint(); doc->okToCopy(); doc->okToChange(); doc->okToAddNotes(); doc->pageMode(); for (int index = 0; index < doc->numPages(); ++index) { Poppler::Page *page = doc->page(index); page->renderToImage(); page->pageSize(); page->orientation(); delete page; std::cout << "."; std::cout.flush(); } std::cout << std::endl; delete doc; } } } std::cout << "Elapsed time: " << (t.elapsed() / 1000) << std::endl; } poppler-24.02.0/qt5/tests/stress-threads-qt5.cpp000066400000000000000000000164211455701731300214130ustar00rootroot00000000000000 #ifndef _WIN32 # include #else # include # define sleep Sleep #endif #include #include #include #include #include #include #include #include class SillyThread : public QThread { Q_OBJECT public: explicit SillyThread(Poppler::Document *document, QObject *parent = nullptr); void run() override; private: Poppler::Document *m_document; QVector m_pages; }; class CrazyThread : public QThread { Q_OBJECT public: CrazyThread(uint seed, Poppler::Document *document, QMutex *annotationMutex, QObject *parent = nullptr); void run() override; private: uint m_seed; Poppler::Document *m_document; QMutex *m_annotationMutex; }; static Poppler::Page *loadPage(Poppler::Document *document, int index) { Poppler::Page *page = document->page(index); if (page == nullptr) { qDebug() << "!Document::page"; exit(EXIT_FAILURE); } return page; } 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() { m_pages.reserve(m_document->numPages()); for (int index = 0; index < m_document->numPages(); ++index) { m_pages.append(loadPage(m_document, index)); } } void SillyThread::run() { forever { foreach (Poppler::Page *page, m_pages) { QImage image = page->renderToImage(); if (image.isNull()) { qDebug() << "!Page::renderToImage"; ::exit(EXIT_FAILURE); } } } } CrazyThread::CrazyThread(uint seed, Poppler::Document *document, QMutex *annotationMutex, QObject *parent) : QThread(parent), m_seed(seed), m_document(document), m_annotationMutex(annotationMutex) { } void CrazyThread::run() { typedef QScopedPointer PagePointer; qsrand(m_seed); forever { if (qrand() % 2 == 0) { qDebug() << "search..."; PagePointer page(loadRandomPage(m_document)); page->search(QStringLiteral("c"), Poppler::Page::IgnoreCase); page->search(QStringLiteral("r")); page->search(QStringLiteral("a"), Poppler::Page::IgnoreCase); page->search(QStringLiteral("z")); page->search(QStringLiteral("y"), Poppler::Page::IgnoreCase); } if (qrand() % 2 == 0) { qDebug() << "links..."; PagePointer page(loadRandomPage(m_document)); QList links = page->links(); qDeleteAll(links); } if (qrand() % 2 == 0) { qDebug() << "form fields..."; PagePointer page(loadRandomPage(m_document)); QList formFields = page->formFields(); qDeleteAll(formFields); } if (qrand() % 2 == 0) { qDebug() << "thumbnail..."; PagePointer page(loadRandomPage(m_document)); page->thumbnail(); } if (qrand() % 2 == 0) { qDebug() << "text..."; PagePointer page(loadRandomPage(m_document)); page->text(QRectF(QPointF(), page->pageSizeF())); } if (qrand() % 2 == 0) { QMutexLocker mutexLocker(m_annotationMutex); qDebug() << "add annotation..."; PagePointer page(loadRandomPage(m_document)); Poppler::Annotation *annotation = nullptr; switch (qrand() % 3) { default: case 0: annotation = new Poppler::TextAnnotation(qrand() % 2 == 0 ? Poppler::TextAnnotation::Linked : Poppler::TextAnnotation::InPlace); break; case 1: annotation = new Poppler::HighlightAnnotation(); break; case 2: annotation = new Poppler::InkAnnotation(); break; } annotation->setBoundary(QRectF(0.0, 0.0, 0.5, 0.5)); annotation->setContents(QStringLiteral("crazy")); page->addAnnotation(annotation); delete annotation; } if (qrand() % 2 == 0) { QMutexLocker mutexLocker(m_annotationMutex); for (int index = 0; index < m_document->numPages(); ++index) { PagePointer page(loadPage(m_document, index)); QList annotations = page->annotations(); if (!annotations.isEmpty()) { qDebug() << "modify annotation..."; annotations.at(qrand() % annotations.size())->setBoundary(QRectF(0.5, 0.5, 0.25, 0.25)); annotations.at(qrand() % annotations.size())->setAuthor(QStringLiteral("foo")); annotations.at(qrand() % annotations.size())->setContents(QStringLiteral("bar")); annotations.at(qrand() % annotations.size())->setCreationDate(QDateTime::currentDateTime()); annotations.at(qrand() % annotations.size())->setModificationDate(QDateTime::currentDateTime()); } qDeleteAll(annotations); if (!annotations.isEmpty()) { break; } } } if (qrand() % 2 == 0) { QMutexLocker mutexLocker(m_annotationMutex); for (int index = 0; index < m_document->numPages(); ++index) { PagePointer page(loadPage(m_document, index)); QList annotations = page->annotations(); if (!annotations.isEmpty()) { qDebug() << "remove annotation..."; page->removeAnnotation(annotations.takeAt(qrand() % annotations.size())); } qDeleteAll(annotations); if (!annotations.isEmpty()) { break; } } } if (qrand() % 2 == 0) { qDebug() << "fonts..."; m_document->fonts(); } } } int main(int argc, char **argv) { if (argc < 5) { qDebug() << "usage: stress-threads-qt duration sillyCount crazyCount file(s)"; return EXIT_FAILURE; } const int duration = atoi(argv[1]); const int sillyCount = atoi(argv[2]); const int crazyCount = atoi(argv[3]); qsrand(time(nullptr)); for (int argi = 4; argi < argc; ++argi) { const QString file = QFile::decodeName(argv[argi]); Poppler::Document *document = Poppler::Document::load(file); if (document == nullptr) { qDebug() << "Could not load" << file; continue; } if (document->isLocked()) { qDebug() << file << "is locked"; continue; } for (int i = 0; i < sillyCount; ++i) { (new SillyThread(document))->start(); } QMutex *annotationMutex = new QMutex(); for (int i = 0; i < crazyCount; ++i) { (new CrazyThread(qrand(), document, annotationMutex))->start(); } } sleep(duration); return EXIT_SUCCESS; } #include "stress-threads-qt5.moc" poppler-24.02.0/qt5/tests/test-password-qt5.cpp000066400000000000000000000076131455701731300212620ustar00rootroot00000000000000#include #include #include #include #include #include #include class PDFDisplay : public QWidget // picture display widget { Q_OBJECT public: explicit PDFDisplay(Poppler::Document *d, QWidget *parent = nullptr); ~PDFDisplay() override; protected: void paintEvent(QPaintEvent *) override; void keyPressEvent(QKeyEvent *) override; private: void display(); int m_currentPage; QImage image; Poppler::Document *doc; }; PDFDisplay::PDFDisplay(Poppler::Document *d, QWidget *parent) : QWidget(parent) { doc = d; m_currentPage = 0; display(); } void PDFDisplay::display() { if (doc) { Poppler::Page *page = doc->page(m_currentPage); if (page) { qDebug() << "Displaying page: " << m_currentPage; image = page->renderToImage(); update(); delete page; } } else { qWarning() << "doc not loaded"; } } PDFDisplay::~PDFDisplay() { delete doc; } void PDFDisplay::paintEvent(QPaintEvent *e) { QPainter paint(this); // paint widget if (!image.isNull()) { paint.drawImage(0, 0, image); } else { qWarning() << "null image"; } } void PDFDisplay::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Down) { if (m_currentPage + 1 < doc->numPages()) { m_currentPage++; display(); } } else if (e->key() == Qt::Key_Up) { if (m_currentPage > 0) { m_currentPage--; display(); } } else if (e->key() == Qt::Key_Q) { exit(0); } } int main(int argc, char **argv) { QApplication a(argc, argv); // QApplication required! if (argc != 3) { qWarning() << "usage: test-password-qt5 owner-password filename"; exit(1); } Poppler::Document *doc = Poppler::Document::load(argv[2], argv[1]); if (!doc) { qWarning() << "doc not loaded"; exit(1); } // output some meta-data auto pdfVersion = doc->getPdfVersion(); qDebug() << " PDF Version: " << qPrintable(QStringLiteral("%1.%2").arg(pdfVersion.major).arg(pdfVersion.minor)); qDebug() << " Title: " << doc->info(QStringLiteral("Title")); qDebug() << " Subject: " << doc->info(QStringLiteral("Subject")); qDebug() << " Author: " << doc->info(QStringLiteral("Author")); qDebug() << " Key words: " << doc->info(QStringLiteral("Keywords")); qDebug() << " Creator: " << doc->info(QStringLiteral("Creator")); qDebug() << " Producer: " << doc->info(QStringLiteral("Producer")); qDebug() << " Date created: " << doc->date(QStringLiteral("CreationDate")).toString(); qDebug() << " Date modified: " << doc->date(QStringLiteral("ModDate")).toString(); qDebug() << "Number of pages: " << doc->numPages(); qDebug() << " Linearised: " << doc->isLinearized(); qDebug() << " Encrypted: " << doc->isEncrypted(); qDebug() << " OK to print: " << doc->okToPrint(); qDebug() << " OK to copy: " << doc->okToCopy(); qDebug() << " OK to change: " << doc->okToChange(); qDebug() << "OK to add notes: " << doc->okToAddNotes(); qDebug() << " Page mode: " << doc->pageMode(); QStringList fontNameList; foreach (const Poppler::FontInfo &font, doc->fonts()) fontNameList += font.name(); qDebug() << " Fonts: " << fontNameList.join(QStringLiteral(", ")); Poppler::Page *page = doc->page(0); qDebug() << " Page 1 size: " << page->pageSize().width() / 72 << "inches x " << page->pageSize().height() / 72 << "inches"; PDFDisplay test(doc); // create picture display test.setWindowTitle(QStringLiteral("Poppler-Qt5 Test")); test.show(); // show it return a.exec(); // start event loop } #include "test-password-qt5.moc" poppler-24.02.0/qt5/tests/test-poppler-qt5.cpp000066400000000000000000000154701455701731300211010ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include class PDFDisplay : public QWidget // picture display widget { Q_OBJECT public: PDFDisplay(Poppler::Document *d, bool qpainter, QWidget *parent = nullptr); ~PDFDisplay() override; void setShowTextRects(bool show); void display(); protected: void paintEvent(QPaintEvent *) override; void keyPressEvent(QKeyEvent *) override; void mousePressEvent(QMouseEvent *) override; private: int m_currentPage; QImage image; Poppler::Document *doc; QString backendString; bool showTextRects; QList textRects; }; PDFDisplay::PDFDisplay(Poppler::Document *d, bool qpainter, QWidget *parent) : QWidget(parent) { showTextRects = false; doc = d; m_currentPage = 0; if (qpainter) { backendString = QStringLiteral("QPainter"); doc->setRenderBackend(Poppler::Document::QPainterBackend); } else { backendString = QStringLiteral("Splash"); doc->setRenderBackend(Poppler::Document::SplashBackend); } doc->setRenderHint(Poppler::Document::Antialiasing, true); doc->setRenderHint(Poppler::Document::TextAntialiasing, true); } void PDFDisplay::setShowTextRects(bool show) { showTextRects = show; } void PDFDisplay::display() { if (doc) { Poppler::Page *page = doc->page(m_currentPage); if (page) { qDebug() << "Displaying page using" << backendString << "backend: " << m_currentPage; QTime t = QTime::currentTime(); image = page->renderToImage(); qDebug() << "Rendering took" << t.msecsTo(QTime::currentTime()) << "msecs"; qDeleteAll(textRects); if (showTextRects) { QPainter painter(&image); painter.setPen(Qt::red); textRects = page->textList(); foreach (Poppler::TextBox *tb, textRects) { painter.drawRect(tb->boundingBox()); } } else { textRects.clear(); } update(); delete page; } } else { qWarning() << "doc not loaded"; } } PDFDisplay::~PDFDisplay() { qDeleteAll(textRects); delete doc; } void PDFDisplay::paintEvent(QPaintEvent *e) { QPainter paint(this); // paint widget if (!image.isNull()) { paint.drawImage(0, 0, image); } else { qWarning() << "null image"; } } void PDFDisplay::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Down) { if (m_currentPage + 1 < doc->numPages()) { m_currentPage++; display(); } } else if (e->key() == Qt::Key_Up) { if (m_currentPage > 0) { m_currentPage--; display(); } } else if (e->key() == Qt::Key_Q) { exit(0); } } void PDFDisplay::mousePressEvent(QMouseEvent *e) { int i = 0; foreach (Poppler::TextBox *tb, textRects) { if (tb->boundingBox().contains(e->pos())) { const QString tt = QStringLiteral("Text: \"%1\"\nIndex in text list: %2").arg(tb->text()).arg(i); QToolTip::showText(e->globalPos(), tt, this); break; } ++i; } } int main(int argc, char **argv) { QApplication a(argc, argv); // QApplication required! if (argc < 2 || (argc == 3 && strcmp(argv[2], "-extract") != 0 && strcmp(argv[2], "-qpainter") != 0 && strcmp(argv[2], "-textRects") != 0) || argc > 3) { // use argument as file name qWarning() << "usage: test-poppler-qt5 filename [-extract|-qpainter|-textRects]"; exit(1); } Poppler::Document *doc = Poppler::Document::load(QFile::decodeName(argv[1])); if (!doc) { qWarning() << "doc not loaded"; exit(1); } if (doc->isLocked()) { qWarning() << "document locked (needs password)"; exit(0); } // output some meta-data Poppler::Document::PdfVersion pdfVersion = doc->getPdfVersion(); qDebug() << " PDF Version: " << qPrintable(QStringLiteral("%1.%2").arg(pdfVersion.major).arg(pdfVersion.minor)); qDebug() << " Title: " << doc->info(QStringLiteral("Title")); qDebug() << " Subject: " << doc->info(QStringLiteral("Subject")); qDebug() << " Author: " << doc->info(QStringLiteral("Author")); qDebug() << " Key words: " << doc->info(QStringLiteral("Keywords")); qDebug() << " Creator: " << doc->info(QStringLiteral("Creator")); qDebug() << " Producer: " << doc->info(QStringLiteral("Producer")); qDebug() << " Date created: " << doc->date(QStringLiteral("CreationDate")).toString(); qDebug() << " Date modified: " << doc->date(QStringLiteral("ModDate")).toString(); qDebug() << "Number of pages: " << doc->numPages(); qDebug() << " Linearised: " << doc->isLinearized(); qDebug() << " Encrypted: " << doc->isEncrypted(); qDebug() << " OK to print: " << doc->okToPrint(); qDebug() << " OK to copy: " << doc->okToCopy(); qDebug() << " OK to change: " << doc->okToChange(); qDebug() << "OK to add notes: " << doc->okToAddNotes(); qDebug() << " Page mode: " << doc->pageMode(); qDebug() << " Metadata: " << doc->metadata(); if (doc->hasEmbeddedFiles()) { qDebug() << "Embedded files:"; foreach (Poppler::EmbeddedFile *file, doc->embeddedFiles()) { qDebug() << " " << file->name(); } qDebug(); } else { qDebug() << "No embedded files"; } if (doc->numPages() <= 0) { delete doc; qDebug() << "Doc has no pages"; return 0; } { Poppler::Page *page = doc->page(0); if (page) { qDebug() << "Page 1 size: " << page->pageSize().width() / 72 << "inches x " << page->pageSize().height() / 72 << "inches"; delete page; } } if (argc == 2 || (argc == 3 && strcmp(argv[2], "-qpainter") == 0) || (argc == 3 && strcmp(argv[2], "-textRects") == 0)) { bool useQPainter = (argc == 3 && strcmp(argv[2], "-qpainter") == 0); PDFDisplay test(doc, useQPainter); // create picture display test.setWindowTitle(QStringLiteral("Poppler-Qt5 Test")); test.setShowTextRects(argc == 3 && strcmp(argv[2], "-textRects") == 0); test.display(); test.show(); // show it return a.exec(); // start event loop } else { Poppler::Page *page = doc->page(0); QLabel *l = new QLabel(page->text(QRectF()), nullptr); l->show(); delete page; delete doc; return a.exec(); } } #include "test-poppler-qt5.moc" poppler-24.02.0/qt5/tests/test-render-to-file.cpp000066400000000000000000000034211455701731300215160ustar00rootroot00000000000000#include #include #include #include #include int main(int argc, char **argv) { QGuiApplication a(argc, argv); // QApplication required! if (argc < 2 || (argc == 3 && strcmp(argv[2], "-qpainter") != 0) || argc > 3) { // use argument as file name qWarning() << "usage: test-render-to-file-qt5 filename [-qpainter]"; exit(1); } Poppler::Document *doc = Poppler::Document::load(QFile::decodeName(argv[1])); if (!doc) { qWarning() << "doc not loaded"; exit(1); } if (doc->isLocked()) { qWarning() << "document locked (needs password)"; exit(0); } if (doc->numPages() <= 0) { delete doc; qDebug() << "Doc has no pages"; return 0; } QString backendString; if (argc == 3 && strcmp(argv[2], "-qpainter") == 0) { backendString = QStringLiteral("QPainter"); doc->setRenderBackend(Poppler::Document::QPainterBackend); } else { backendString = QStringLiteral("Splash"); doc->setRenderBackend(Poppler::Document::SplashBackend); } doc->setRenderHint(Poppler::Document::Antialiasing, true); doc->setRenderHint(Poppler::Document::TextAntialiasing, true); for (int i = 0; i < doc->numPages(); ++i) { Poppler::Page *page = doc->page(i); if (page) { qDebug() << "Rendering page using" << backendString << "backend: " << i; QTime t = QTime::currentTime(); QImage image = page->renderToImage(); qDebug() << "Rendering took" << t.msecsTo(QTime::currentTime()) << "msecs"; image.save(QStringLiteral("test-render-to-file%1.png").arg(i)); delete page; } } return 0; } poppler-24.02.0/qt6/000077500000000000000000000000001455701731300140605ustar00rootroot00000000000000poppler-24.02.0/qt6/.gitignore000066400000000000000000000000311455701731300160420ustar00rootroot00000000000000Makefile Makefile.in *~ poppler-24.02.0/qt6/CMakeLists.txt000066400000000000000000000005521455701731300166220ustar00rootroot00000000000000set(CMAKE_AUTOMOC ON) set(ENABLE_QT_STRICT_ITERATORS ON CACHE BOOL "Select whether to compile with QT_STRICT_ITERATORS. Leave it ON, unless your Qt lacks support, or your compiler can't do SRA optimization.") if(ENABLE_QT_STRICT_ITERATORS) add_definitions(-DQT_STRICT_ITERATORS) endif() add_subdirectory(src) add_subdirectory(tests) add_subdirectory(demos) poppler-24.02.0/qt6/demos/000077500000000000000000000000001455701731300151675ustar00rootroot00000000000000poppler-24.02.0/qt6/demos/.gitignore000066400000000000000000000000431455701731300171540ustar00rootroot00000000000000.deps .libs *moc poppler_qt6viewer poppler-24.02.0/qt6/demos/CMakeLists.txt000066400000000000000000000010701455701731300177250ustar00rootroot00000000000000include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../src ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/../src ) set(poppler_qt6viewer_SRCS abstractinfodock.cpp documentobserver.cpp embeddedfiles.cpp fonts.cpp info.cpp main_viewer.cpp metadata.cpp navigationtoolbar.cpp optcontent.cpp pageview.cpp permissions.cpp thumbnails.cpp toc.cpp viewer.cpp ) poppler_add_test(poppler_qt6viewer BUILD_QT6_TESTS ${poppler_qt6viewer_SRCS}) target_link_libraries(poppler_qt6viewer poppler-qt6 Qt6::Widgets) poppler-24.02.0/qt6/demos/abstractinfodock.cpp000066400000000000000000000026451455701731300212220ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * * 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 "fonts.h" AbstractInfoDock::AbstractInfoDock(QWidget *parent) : QDockWidget(parent), m_filled(false) { connect(this, &AbstractInfoDock::visibilityChanged, this, &AbstractInfoDock::slotVisibilityChanged); } AbstractInfoDock::~AbstractInfoDock() { } void AbstractInfoDock::documentLoaded() { if (!isHidden()) { fillInfo(); m_filled = true; } } void AbstractInfoDock::documentClosed() { m_filled = false; } void AbstractInfoDock::pageChanged(int page) { Q_UNUSED(page) } void AbstractInfoDock::slotVisibilityChanged(bool visible) { if (visible && document() && !m_filled) { fillInfo(); m_filled = true; } } poppler-24.02.0/qt6/demos/abstractinfodock.h000066400000000000000000000025571455701731300206710ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * Copyright (C) 2021, 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 * 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 ABSTRACTINFODOCK_H #define ABSTRACTINFODOCK_H #include #include "documentobserver.h" class AbstractInfoDock : public QDockWidget, public DocumentObserver { Q_OBJECT public: explicit AbstractInfoDock(QWidget *parent = nullptr); ~AbstractInfoDock() override; void documentLoaded() override; void documentClosed() override; void pageChanged(int page) override; protected: virtual void fillInfo() = 0; private Q_SLOTS: void slotVisibilityChanged(bool visible); private: bool m_filled; }; #endif poppler-24.02.0/qt6/demos/documentobserver.cpp000066400000000000000000000024341455701731300212640ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * Copyright (C) 2021, Oliver Sander * * 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 "documentobserver.h" #include "viewer.h" DocumentObserver::DocumentObserver() : m_viewer(nullptr) { } DocumentObserver::~DocumentObserver() { } Poppler::Document *DocumentObserver::document() const { return m_viewer->m_doc.get(); } void DocumentObserver::setPage(int page) { m_viewer->setPage(page); } int DocumentObserver::page() const { return m_viewer->page(); } void DocumentObserver::reloadPage() { m_viewer->setPage(m_viewer->page()); } poppler-24.02.0/qt6/demos/documentobserver.h000066400000000000000000000027021455701731300207270ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * Copyright (C) 2018, 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 * 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 DOCUMENTOBSERVER_H #define DOCUMENTOBSERVER_H class PdfViewer; namespace Poppler { class Document; } class DocumentObserver { friend class PdfViewer; public: virtual ~DocumentObserver(); DocumentObserver(const DocumentObserver &) = delete; DocumentObserver &operator=(const DocumentObserver &) = delete; virtual void documentLoaded() = 0; virtual void documentClosed() = 0; virtual void pageChanged(int page) = 0; protected: DocumentObserver(); Poppler::Document *document() const; void setPage(int page); int page() const; void reloadPage(); private: PdfViewer *m_viewer; }; #endif poppler-24.02.0/qt6/demos/embeddedfiles.cpp000066400000000000000000000055201455701731300204510ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * * 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 "embeddedfiles.h" #include #include EmbeddedFilesDock::EmbeddedFilesDock(QWidget *parent) : AbstractInfoDock(parent) { m_table = new QTableWidget(this); setWidget(m_table); setWindowTitle(tr("Embedded files")); m_table->setColumnCount(6); m_table->setHorizontalHeaderLabels(QStringList() << tr("Name") << tr("Description") << tr("Size") << tr("Creation date") << tr("Modification date") << tr("Checksum")); m_table->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); } EmbeddedFilesDock::~EmbeddedFilesDock() { } void EmbeddedFilesDock::fillInfo() { m_table->setHorizontalHeaderLabels(QStringList() << tr("Name") << tr("Description") << tr("Size") << tr("Creation date") << tr("Modification date") << tr("Checksum")); if (!document()->hasEmbeddedFiles()) { m_table->setItem(0, 0, new QTableWidgetItem(tr("No files"))); return; } const QList files = document()->embeddedFiles(); m_table->setRowCount(files.count()); int i = 0; Q_FOREACH (Poppler::EmbeddedFile *file, files) { m_table->setItem(i, 0, new QTableWidgetItem(file->name())); m_table->setItem(i, 1, new QTableWidgetItem(file->description())); m_table->setItem(i, 2, new QTableWidgetItem(QString::number(file->size()))); m_table->setItem(i, 3, new QTableWidgetItem(QLocale().toString(file->createDate(), QLocale::ShortFormat))); m_table->setItem(i, 4, new QTableWidgetItem(QLocale().toString(file->modDate(), QLocale::ShortFormat))); const QByteArray checksum = file->checksum(); const QString checksumString = !checksum.isEmpty() ? QString::fromLatin1(checksum.toHex()) : QStringLiteral("n/a"); m_table->setItem(i, 5, new QTableWidgetItem(checksumString)); ++i; } } void EmbeddedFilesDock::documentLoaded() { if (document()->pageMode() == Poppler::Document::UseAttach) { show(); } } void EmbeddedFilesDock::documentClosed() { m_table->clear(); m_table->setRowCount(0); AbstractInfoDock::documentClosed(); } poppler-24.02.0/qt6/demos/embeddedfiles.h000066400000000000000000000023431455701731300201160ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * Copyright (C) 2021, 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 * 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 ATTACHMENTS_H #define ATTACHMENTS_H #include "abstractinfodock.h" class QTableWidget; class EmbeddedFilesDock : public AbstractInfoDock { Q_OBJECT public: explicit EmbeddedFilesDock(QWidget *parent = nullptr); ~EmbeddedFilesDock() override; void documentLoaded() override; void documentClosed() override; protected: void fillInfo() override; private: QTableWidget *m_table; }; #endif poppler-24.02.0/qt6/demos/fonts.cpp000066400000000000000000000045461455701731300170350ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * * 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 "fonts.h" #include #include static QString yesNoStatement(bool value) { return value ? QStringLiteral("yes") : QStringLiteral("no"); } FontsDock::FontsDock(QWidget *parent) : AbstractInfoDock(parent) { m_table = new QTableWidget(this); setWidget(m_table); setWindowTitle(tr("Fonts")); m_table->setColumnCount(5); m_table->setHorizontalHeaderLabels(QStringList() << tr("Name") << tr("Type") << tr("Embedded") << tr("Subset") << tr("File")); m_table->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); } FontsDock::~FontsDock() { } void FontsDock::fillInfo() { const QList fonts = document()->fonts(); m_table->setHorizontalHeaderLabels(QStringList() << tr("Name") << tr("Type") << tr("Embedded") << tr("Subset") << tr("File")); m_table->setRowCount(fonts.count()); int i = 0; Q_FOREACH (const Poppler::FontInfo &font, fonts) { if (font.name().isNull()) { m_table->setItem(i, 0, new QTableWidgetItem(QStringLiteral("[none]"))); } else { m_table->setItem(i, 0, new QTableWidgetItem(font.name())); } m_table->setItem(i, 1, new QTableWidgetItem(font.typeName())); m_table->setItem(i, 2, new QTableWidgetItem(yesNoStatement(font.isEmbedded()))); m_table->setItem(i, 3, new QTableWidgetItem(yesNoStatement(font.isSubset()))); m_table->setItem(i, 4, new QTableWidgetItem(font.file())); ++i; } } void FontsDock::documentClosed() { m_table->clear(); m_table->setRowCount(0); AbstractInfoDock::documentClosed(); } poppler-24.02.0/qt6/demos/fonts.h000066400000000000000000000022331455701731300164710ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * Copyright (C) 2021, 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 * 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 FONTS_H #define FONTS_H #include "abstractinfodock.h" class QTableWidget; class FontsDock : public AbstractInfoDock { Q_OBJECT public: explicit FontsDock(QWidget *parent = nullptr); ~FontsDock() override; void documentClosed() override; protected: void fillInfo() override; private: QTableWidget *m_table; }; #endif poppler-24.02.0/qt6/demos/info.cpp000066400000000000000000000043171455701731300166330ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * * 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 "info.h" #include #include InfoDock::InfoDock(QWidget *parent) : AbstractInfoDock(parent) { m_table = new QTableWidget(this); setWidget(m_table); setWindowTitle(tr("Information")); m_table->setColumnCount(2); m_table->setHorizontalHeaderLabels(QStringList() << tr("Key") << tr("Value")); m_table->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); } InfoDock::~InfoDock() { } void InfoDock::fillInfo() { QStringList keys = document()->infoKeys(); m_table->setHorizontalHeaderLabels(QStringList() << tr("Key") << tr("Value")); m_table->setRowCount(keys.count()); QStringList dateKeys; dateKeys << QStringLiteral("CreationDate"); dateKeys << QStringLiteral("ModDate"); int i = 0; Q_FOREACH (const QString &date, dateKeys) { const int id = keys.indexOf(date); if (id != -1) { m_table->setItem(i, 0, new QTableWidgetItem(date)); m_table->setItem(i, 1, new QTableWidgetItem(QLocale().toString(document()->date(date), QLocale::ShortFormat))); ++i; keys.removeAt(id); } } Q_FOREACH (const QString &key, keys) { m_table->setItem(i, 0, new QTableWidgetItem(key)); m_table->setItem(i, 1, new QTableWidgetItem(document()->info(key))); ++i; } } void InfoDock::documentClosed() { m_table->clear(); m_table->setRowCount(0); AbstractInfoDock::documentClosed(); } poppler-24.02.0/qt6/demos/info.h000066400000000000000000000022261455701731300162750ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * Copyright (C) 2021, 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 * 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 INFO_H #define INFO_H #include "abstractinfodock.h" class QTableWidget; class InfoDock : public AbstractInfoDock { Q_OBJECT public: explicit InfoDock(QWidget *parent = nullptr); ~InfoDock() override; void documentClosed() override; protected: void fillInfo() override; private: QTableWidget *m_table; }; #endif poppler-24.02.0/qt6/demos/main_viewer.cpp000066400000000000000000000021311455701731300201750ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * * 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 "viewer.h" #include int main(int argc, char *argv[]) { QApplication app(argc, argv); const QStringList args = QCoreApplication::arguments(); PdfViewer *viewer = new PdfViewer(); viewer->show(); if (args.count() > 1) { viewer->loadDocument(args.at(1)); } return app.exec(); } poppler-24.02.0/qt6/demos/metadata.cpp000066400000000000000000000024321455701731300174540ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * * 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 "metadata.h" #include #include MetadataDock::MetadataDock(QWidget *parent) : AbstractInfoDock(parent) { m_edit = new QTextEdit(this); setWidget(m_edit); setWindowTitle(tr("Metadata")); m_edit->setAcceptRichText(false); m_edit->setReadOnly(true); } MetadataDock::~MetadataDock() { } void MetadataDock::fillInfo() { m_edit->setPlainText(document()->metadata()); } void MetadataDock::documentClosed() { m_edit->clear(); AbstractInfoDock::documentClosed(); } poppler-24.02.0/qt6/demos/metadata.h000066400000000000000000000022431455701731300171210ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * Copyright (C) 2021, 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 * 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 METADATA_H #define METADATA_H #include "abstractinfodock.h" class QTextEdit; class MetadataDock : public AbstractInfoDock { Q_OBJECT public: explicit MetadataDock(QWidget *parent = nullptr); ~MetadataDock() override; void documentClosed() override; protected: void fillInfo() override; private: QTextEdit *m_edit; }; #endif poppler-24.02.0/qt6/demos/navigationtoolbar.cpp000066400000000000000000000102341455701731300214150ustar00rootroot00000000000000/* * Copyright (C) 2008-2009, Pino Toscano * Copyright (C) 2013, Fabio D'Urso * Copyright (C) 2019, 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 * 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 "navigationtoolbar.h" #include #include #include #include NavigationToolBar::NavigationToolBar(QWidget *parent) : QToolBar(parent) { m_firstAct = addAction(tr("First"), this, SLOT(slotGoFirst())); m_prevAct = addAction(tr("Previous"), this, SLOT(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())); addSeparator(); m_zoomCombo = new QComboBox(this); m_zoomCombo->setEditable(true); m_zoomCombo->addItem(tr("10%")); m_zoomCombo->addItem(tr("25%")); m_zoomCombo->addItem(tr("33%")); m_zoomCombo->addItem(tr("50%")); m_zoomCombo->addItem(tr("66%")); m_zoomCombo->addItem(tr("75%")); m_zoomCombo->addItem(tr("100%")); m_zoomCombo->addItem(tr("125%")); m_zoomCombo->addItem(tr("150%")); m_zoomCombo->addItem(tr("200%")); m_zoomCombo->addItem(tr("300%")); m_zoomCombo->addItem(tr("400%")); m_zoomCombo->setCurrentIndex(6); // "100%" connect(m_zoomCombo, &QComboBox::activated, this, &NavigationToolBar::slotZoomComboActivated); addWidget(m_zoomCombo); m_rotationCombo = new QComboBox(this); // NOTE: \302\260 = degree symbol m_rotationCombo->addItem(tr("0\302\260")); m_rotationCombo->addItem(tr("90\302\260")); m_rotationCombo->addItem(tr("180\302\260")); m_rotationCombo->addItem(tr("270\302\260")); connect(m_rotationCombo, &QComboBox::currentIndexChanged, this, &NavigationToolBar::slotRotationComboChanged); addWidget(m_rotationCombo); documentClosed(); } NavigationToolBar::~NavigationToolBar() { } void NavigationToolBar::documentLoaded() { const int pageCount = document()->numPages(); for (int i = 0; i < pageCount; ++i) { m_pageCombo->addItem(QString::number(i + 1)); } m_pageCombo->setEnabled(true); } void NavigationToolBar::documentClosed() { m_firstAct->setEnabled(false); m_prevAct->setEnabled(false); m_nextAct->setEnabled(false); m_lastAct->setEnabled(false); m_pageCombo->clear(); m_pageCombo->setEnabled(false); } void NavigationToolBar::pageChanged(int page) { const int pageCount = document()->numPages(); m_firstAct->setEnabled(page > 0); m_prevAct->setEnabled(page > 0); m_nextAct->setEnabled(page < (pageCount - 1)); m_lastAct->setEnabled(page < (pageCount - 1)); m_pageCombo->setCurrentIndex(page); } void NavigationToolBar::slotGoFirst() { setPage(0); } void NavigationToolBar::slotGoPrev() { setPage(page() - 1); } void NavigationToolBar::slotGoNext() { setPage(page() + 1); } void NavigationToolBar::slotGoLast() { setPage(document()->numPages() - 1); } void NavigationToolBar::slotComboActivated(int index) { setPage(index); } void NavigationToolBar::slotZoomComboActivated(int index) { QString text = m_zoomCombo->currentText(); text.remove(QLatin1Char('%')); bool ok = false; int value = text.toInt(&ok); if (ok && value >= 10) { emit zoomChanged(qreal(value) / 100); } } void NavigationToolBar::slotRotationComboChanged(int idx) { emit rotationChanged(idx * 90); } poppler-24.02.0/qt6/demos/navigationtoolbar.h000066400000000000000000000037231455701731300210670ustar00rootroot00000000000000/* * Copyright (C) 2008-2009, Pino Toscano * Copyright (C) 2013, Fabio D'Urso * Copyright (C) 2019, 2021, 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 * 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 NAVIGATIONTOOLBAR_H #define NAVIGATIONTOOLBAR_H #include #include "documentobserver.h" class QAction; class QComboBox; class NavigationToolBar : public QToolBar, public DocumentObserver { Q_OBJECT public: explicit NavigationToolBar(QWidget *parent = nullptr); ~NavigationToolBar() override; void documentLoaded() override; void documentClosed() override; void pageChanged(int page) override; Q_SIGNALS: void zoomChanged(qreal value); // NOLINT(readability-inconsistent-declaration-parameter-name) void rotationChanged(int rotation); // NOLINT(readability-inconsistent-declaration-parameter-name) private Q_SLOTS: void slotGoFirst(); void slotGoPrev(); void slotGoNext(); void slotGoLast(); void slotComboActivated(int index); void slotZoomComboActivated(int index); void slotRotationComboChanged(int idx); private: QAction *m_firstAct; QAction *m_prevAct; QComboBox *m_pageCombo; QAction *m_nextAct; QAction *m_lastAct; QComboBox *m_zoomCombo; QComboBox *m_rotationCombo; }; #endif poppler-24.02.0/qt6/demos/optcontent.cpp000066400000000000000000000033541455701731300200750ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * * 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 "optcontent.h" #include #include OptContentDock::OptContentDock(QWidget *parent) : AbstractInfoDock(parent) { m_view = new QTreeView(this); setWidget(m_view); setWindowTitle(tr("Optional content")); m_view->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); } OptContentDock::~OptContentDock() { } void OptContentDock::documentLoaded() { AbstractInfoDock::documentLoaded(); if (document()->pageMode() == Poppler::Document::UseOC) { show(); } } void OptContentDock::fillInfo() { if (!document()->hasOptionalContent()) { return; } m_view->setModel(document()->optionalContentModel()); connect(m_view->model(), &QAbstractItemModel::dataChanged, this, &OptContentDock::reloadImage); m_view->expandToDepth(1); } void OptContentDock::documentClosed() { m_view->setModel(nullptr); AbstractInfoDock::documentClosed(); } void OptContentDock::reloadImage() { reloadPage(); } poppler-24.02.0/qt6/demos/optcontent.h000066400000000000000000000023731455701731300175420ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * Copyright (C) 2021, 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 * 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 OPTCONTENT_H #define OPTCONTENT_H #include "abstractinfodock.h" class QTreeView; class OptContentDock : public AbstractInfoDock { Q_OBJECT public: explicit OptContentDock(QWidget *parent = nullptr); ~OptContentDock() override; void documentLoaded() override; void documentClosed() override; protected: void fillInfo() override; private Q_SLOTS: void reloadImage(); private: QTreeView *m_view; }; #endif poppler-24.02.0/qt6/demos/pageview.cpp000066400000000000000000000052701455701731300175060ustar00rootroot00000000000000/* * Copyright (C) 2008-2009, Pino Toscano * Copyright (C) 2013, Fabio D'Urso * Copyright (C) 2017, 2020, Albert Astals Cid * Copyright (C) 2021, Oliver Sander * * 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 "pageview.h" #include #include #include #include #include #include PageView::PageView(QWidget *parent) : QScrollArea(parent), m_zoom(1.0), m_rotation(0), m_dpiX(physicalDpiX()), m_dpiY(physicalDpiY()) { m_imageLabel = new QLabel(this); m_imageLabel->resize(0, 0); setWidget(m_imageLabel); } PageView::~PageView() { } void PageView::documentLoaded() { } void PageView::documentClosed() { m_imageLabel->clear(); m_imageLabel->resize(0, 0); } void PageView::pageChanged(int page) { std::unique_ptr popplerPage = document()->page(page); if (!popplerPage) { qDebug() << "Page" << page << "is malformed"; return; } const double resX = m_dpiX * m_zoom; const double resY = m_dpiY * m_zoom; Poppler::Page::Rotation rot; if (m_rotation == 0) { rot = Poppler::Page::Rotate0; } else if (m_rotation == 90) { rot = Poppler::Page::Rotate90; } else if (m_rotation == 180) { rot = Poppler::Page::Rotate180; } else { // m_rotation == 270 rot = Poppler::Page::Rotate270; } QImage image = popplerPage->renderToImage(resX, resY, -1, -1, -1, -1, rot); if (!image.isNull()) { m_imageLabel->resize(image.size()); m_imageLabel->setPixmap(QPixmap::fromImage(image)); } else { m_imageLabel->resize(0, 0); m_imageLabel->setPixmap(QPixmap()); } } void PageView::slotZoomChanged(qreal value) { m_zoom = value; if (!document()) { return; } reloadPage(); } void PageView::slotRotationChanged(int value) { m_rotation = value; if (!document()) { return; } reloadPage(); } poppler-24.02.0/qt6/demos/pageview.h000066400000000000000000000027301455701731300171510ustar00rootroot00000000000000/* * Copyright (C) 2008-2009, Pino Toscano * Copyright (C) 2013, Fabio D'Urso * Copyright (C) 2021, 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 * 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 PAGEVIEW_H #define PAGEVIEW_H #include #include "documentobserver.h" class QLabel; class PageView : public QScrollArea, public DocumentObserver { Q_OBJECT public: explicit PageView(QWidget *parent = nullptr); ~PageView() override; void documentLoaded() override; void documentClosed() override; void pageChanged(int page) override; public Q_SLOTS: void slotZoomChanged(qreal value); void slotRotationChanged(int value); private: QLabel *m_imageLabel; qreal m_zoom; int m_rotation; int m_dpiX; int m_dpiY; }; #endif poppler-24.02.0/qt6/demos/permissions.cpp000066400000000000000000000065101455701731300202500ustar00rootroot00000000000000/* * Copyright (C) 2008-2009, Pino Toscano * * 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 "permissions.h" #include #include PermissionsDock::PermissionsDock(QWidget *parent) : AbstractInfoDock(parent) { m_table = new QListWidget(this); setWidget(m_table); setWindowTitle(tr("Permissions")); m_table->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); } PermissionsDock::~PermissionsDock() { } void PermissionsDock::fillInfo() { #define ADD_ROW(title, function) \ do { \ QListWidgetItem *item = new QListWidgetItem(); \ item->setFlags(item->flags() & ~Qt::ItemIsEnabled); \ item->setText(QStringLiteral(title)); \ item->setCheckState(document()->function() ? Qt::Checked : Qt::Unchecked); \ m_table->addItem(item); \ } while (0) ADD_ROW("Print", okToPrint); ADD_ROW("PrintHiRes", okToPrintHighRes); ADD_ROW("Change", okToChange); ADD_ROW("Copy", okToCopy); ADD_ROW("Add Notes", okToAddNotes); ADD_ROW("Fill Forms", okToFillForm); ADD_ROW("Create Forms", okToCreateFormFields); ADD_ROW("Extract for accessibility", okToExtractForAccessibility); ADD_ROW("Assemble", okToAssemble); #undef ADD_ROW } void PermissionsDock::documentClosed() { m_table->clear(); AbstractInfoDock::documentClosed(); } poppler-24.02.0/qt6/demos/permissions.h000066400000000000000000000022741455701731300177200ustar00rootroot00000000000000/* * Copyright (C) 2008-2009, Pino Toscano * Copyright (C) 2021, 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 * 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 PERMISSIONS_H #define PERMISSIONS_H #include "abstractinfodock.h" class QListWidget; class PermissionsDock : public AbstractInfoDock { Q_OBJECT public: explicit PermissionsDock(QWidget *parent = nullptr); ~PermissionsDock() override; void documentClosed() override; protected: void fillInfo() override; private: QListWidget *m_table; }; #endif poppler-24.02.0/qt6/demos/thumbnails.cpp000066400000000000000000000050771455701731300200520ustar00rootroot00000000000000/* * Copyright (C) 2009, Shawn Rutledge * Copyright (C) 2009, Pino Toscano * Copyright (C) 2020, Albert Astals Cid * Copyright (C) 2021, Oliver Sander * * 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 "thumbnails.h" #include #include static const int PageRole = Qt::UserRole + 1; ThumbnailsDock::ThumbnailsDock(QWidget *parent) : AbstractInfoDock(parent) { m_list = new QListWidget(this); setWidget(m_list); setWindowTitle(tr("Thumbnails")); m_list->setViewMode(QListView::ListMode); m_list->setMovement(QListView::Static); m_list->setVerticalScrollMode(QListView::ScrollPerPixel); connect(m_list, &QListWidget::itemActivated, this, &ThumbnailsDock::slotItemActivated); } ThumbnailsDock::~ThumbnailsDock() { } void ThumbnailsDock::fillInfo() { const int num = document()->numPages(); QSize maxSize; for (int i = 0; i < num; ++i) { const std::unique_ptr page = document()->page(i); const QImage image = page ? page->thumbnail() : QImage(); if (!image.isNull()) { QListWidgetItem *item = new QListWidgetItem(); item->setText(QString::number(i + 1)); item->setData(Qt::DecorationRole, QPixmap::fromImage(image)); item->setData(PageRole, i); m_list->addItem(item); maxSize.setWidth(qMax(maxSize.width(), image.width())); maxSize.setHeight(qMax(maxSize.height(), image.height())); } } if (num > 0) { m_list->setGridSize(maxSize); m_list->setIconSize(maxSize); } } void ThumbnailsDock::documentClosed() { m_list->clear(); AbstractInfoDock::documentClosed(); } void ThumbnailsDock::slotItemActivated(QListWidgetItem *item) { if (!item) { return; } setPage(item->data(PageRole).toInt()); } poppler-24.02.0/qt6/demos/thumbnails.h000066400000000000000000000025201455701731300175050ustar00rootroot00000000000000/* * Copyright (C) 2009, Shawn Rutledge * Copyright (C) 2009, Pino Toscano * Copyright (C) 2021, 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 * 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 THUMBNAILS_H #define THUMBNAILS_H #include "abstractinfodock.h" class QListWidget; class QListWidgetItem; class ThumbnailsDock : public AbstractInfoDock { Q_OBJECT public: explicit ThumbnailsDock(QWidget *parent = nullptr); ~ThumbnailsDock() override; void documentClosed() override; protected: void fillInfo() override; private Q_SLOTS: void slotItemActivated(QListWidgetItem *item); private: QListWidget *m_list; }; #endif poppler-24.02.0/qt6/demos/toc.cpp000066400000000000000000000114601455701731300164620ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * Copyright (C) 2018, Adam Reichold * Copyright (C) 2019, 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 * 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 "toc.h" #include #include #include #include #include struct Node { Node(Poppler::OutlineItem &&item, int row, Node *parent) : m_row(row), m_parent(parent), m_item(std::move(item)) { } ~Node() { qDeleteAll(m_children); } Node(const Node &) = delete; Node &operator=(const Node &) = delete; int m_row; Node *m_parent; Poppler::OutlineItem m_item; QVector m_children; }; class TocModel : public QAbstractItemModel { Q_OBJECT public: TocModel(QVector &&items, QObject *parent) : QAbstractItemModel(parent) { for (int i = 0; i < items.count(); ++i) { m_topItems << new Node(std::move(items[i]), i, nullptr); } } ~TocModel() override { qDeleteAll(m_topItems); } QVariant data(const QModelIndex &index, int role) const override { if (role != Qt::DisplayRole) { return {}; } Node *n = static_cast(index.internalPointer()); return n->m_item.name(); } QModelIndex index(int row, int column, const QModelIndex &parent) const override { Node *p = static_cast(parent.internalPointer()); const QVector &children = p ? p->m_children : m_topItems; return createIndex(row, column, children[row]); } QModelIndex parent(const QModelIndex &child) const override { Node *n = static_cast(child.internalPointer()); if (n->m_parent == nullptr) { return QModelIndex(); } else { return createIndex(n->m_parent->m_row, 0, n->m_parent); } } int rowCount(const QModelIndex &parent) const override { Node *n = static_cast(parent.internalPointer()); if (!n) { return m_topItems.count(); } if (n->m_children.isEmpty() && !n->m_item.isNull()) { QVector items = n->m_item.children(); for (int i = 0; i < items.count(); ++i) { n->m_children << new Node(std::move(items[i]), i, n); } } return n->m_children.count(); } bool hasChildren(const QModelIndex &parent) const override { Node *n = static_cast(parent.internalPointer()); if (!n) { return true; } return n->m_item.hasChildren(); } int columnCount(const QModelIndex &parent) const override { return 1; } private: QVector m_topItems; }; TocDock::TocDock(QWidget *parent) : AbstractInfoDock(parent) { m_tree = new QTreeView(this); setWidget(m_tree); m_tree->setAlternatingRowColors(true); m_tree->header()->hide(); setWindowTitle(tr("TOC")); m_tree->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); } TocDock::~TocDock() { } void TocDock::expandItemModels(const QModelIndex &parent) { TocModel *model = static_cast(m_tree->model()); for (int i = 0; i < model->rowCount(parent); ++i) { QModelIndex index = model->index(i, 0, parent); Node *n = static_cast(index.internalPointer()); if (n->m_item.isOpen()) { m_tree->setExpanded(index, true); expandItemModels(index); } } } void TocDock::fillInfo() { auto outline = document()->outline(); if (!outline.isEmpty()) { TocModel *model = new TocModel(std::move(outline), this); m_tree->setModel(model); expandItemModels(QModelIndex()); } else { QStandardItemModel *model = new QStandardItemModel(this); QStandardItem *item = new QStandardItem(tr("No TOC")); item->setFlags(item->flags() & ~Qt::ItemIsEnabled); model->appendRow(item); m_tree->setModel(model); } } void TocDock::documentClosed() { m_tree->setModel(nullptr); AbstractInfoDock::documentClosed(); } #include "toc.moc" poppler-24.02.0/qt6/demos/toc.h000066400000000000000000000023061455701731300161260ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * Copyright (C) 2019, 2021, 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 * 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 TOC_H #define TOC_H #include "abstractinfodock.h" class QTreeView; class TocDock : public AbstractInfoDock { Q_OBJECT public: explicit TocDock(QWidget *parent = nullptr); ~TocDock() override; void documentClosed() override; protected: void fillInfo() override; void expandItemModels(const QModelIndex &parent); private: QTreeView *m_tree; }; #endif poppler-24.02.0/qt6/demos/viewer.cpp000066400000000000000000000246061455701731300172040ustar00rootroot00000000000000/* * Copyright (C) 2008-2009, Pino Toscano * Copyright (C) 2008, 2019, 2020, Albert Astals Cid * Copyright (C) 2009, Shawn Rutledge * Copyright (C) 2013, Fabio D'Urso * Copyright (C) 2020, 2021, Oliver Sander * Copyright (C) 2021, Mahmoud Khalil * * 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 "viewer.h" #include "embeddedfiles.h" #include "fonts.h" #include "info.h" #include "metadata.h" #include "navigationtoolbar.h" #include "optcontent.h" #include "pageview.h" #include "permissions.h" #include "thumbnails.h" #include "toc.h" #include #include #include #include #include #include #include #include #include #include #include PdfViewer::PdfViewer(QWidget *parent) : QMainWindow(parent), m_currentPage(0), m_doc(nullptr) { setWindowTitle(tr("Poppler-Qt6 Demo")); // setup the menus QMenu *fileMenu = menuBar()->addMenu(tr("&File")); m_fileOpenAct = fileMenu->addAction(tr("&Open"), this, &PdfViewer::slotOpenFile); m_fileOpenAct->setShortcut(Qt::CTRL | Qt::Key_O); fileMenu->addSeparator(); m_fileSaveCopyAct = fileMenu->addAction(tr("&Save a Copy..."), this, &PdfViewer::slotSaveCopy); m_fileSaveCopyAct->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_S); m_fileSaveCopyAct->setEnabled(false); fileMenu->addSeparator(); QAction *act = fileMenu->addAction(tr("&Quit"), qApp, &QApplication::closeAllWindows); act->setShortcut(Qt::CTRL | Qt::Key_Q); QMenu *viewMenu = menuBar()->addMenu(tr("&View")); QMenu *settingsMenu = menuBar()->addMenu(tr("&Settings")); m_settingsTextAAAct = settingsMenu->addAction(tr("Text Antialias")); m_settingsTextAAAct->setCheckable(true); connect(m_settingsTextAAAct, &QAction::toggled, this, &PdfViewer::slotToggleTextAA); m_settingsGfxAAAct = settingsMenu->addAction(tr("Graphics Antialias")); m_settingsGfxAAAct->setCheckable(true); connect(m_settingsGfxAAAct, &QAction::toggled, this, &PdfViewer::slotToggleGfxAA); QMenu *settingsRenderMenu = settingsMenu->addMenu(tr("Render Backend")); m_settingsRenderBackendGrp = new QActionGroup(settingsRenderMenu); m_settingsRenderBackendGrp->setExclusive(true); act = settingsRenderMenu->addAction(tr("Splash")); act->setCheckable(true); act->setChecked(true); act->setData(QVariant::fromValue(0)); m_settingsRenderBackendGrp->addAction(act); act = settingsRenderMenu->addAction(tr("QPainter")); act->setCheckable(true); act->setData(QVariant::fromValue(1)); m_settingsRenderBackendGrp->addAction(act); connect(m_settingsRenderBackendGrp, &QActionGroup::triggered, this, &PdfViewer::slotRenderBackend); QMenu *helpMenu = menuBar()->addMenu(tr("&Help")); act = helpMenu->addAction(tr("&About"), this, &PdfViewer::slotAbout); act = helpMenu->addAction(tr("About &Qt"), this, &PdfViewer::slotAboutQt); NavigationToolBar *navbar = new NavigationToolBar(this); addToolBar(navbar); m_observers.append(navbar); PageView *view = new PageView(this); setCentralWidget(view); m_observers.append(view); InfoDock *infoDock = new InfoDock(this); addDockWidget(Qt::LeftDockWidgetArea, infoDock); infoDock->hide(); viewMenu->addAction(infoDock->toggleViewAction()); m_observers.append(infoDock); TocDock *tocDock = new TocDock(this); addDockWidget(Qt::LeftDockWidgetArea, tocDock); tocDock->hide(); viewMenu->addAction(tocDock->toggleViewAction()); m_observers.append(tocDock); FontsDock *fontsDock = new FontsDock(this); addDockWidget(Qt::LeftDockWidgetArea, fontsDock); fontsDock->hide(); viewMenu->addAction(fontsDock->toggleViewAction()); m_observers.append(fontsDock); PermissionsDock *permissionsDock = new PermissionsDock(this); addDockWidget(Qt::LeftDockWidgetArea, permissionsDock); permissionsDock->hide(); viewMenu->addAction(permissionsDock->toggleViewAction()); m_observers.append(permissionsDock); ThumbnailsDock *thumbnailsDock = new ThumbnailsDock(this); addDockWidget(Qt::LeftDockWidgetArea, thumbnailsDock); thumbnailsDock->hide(); viewMenu->addAction(thumbnailsDock->toggleViewAction()); m_observers.append(thumbnailsDock); EmbeddedFilesDock *embfilesDock = new EmbeddedFilesDock(this); addDockWidget(Qt::BottomDockWidgetArea, embfilesDock); embfilesDock->hide(); viewMenu->addAction(embfilesDock->toggleViewAction()); m_observers.append(embfilesDock); MetadataDock *metadataDock = new MetadataDock(this); addDockWidget(Qt::BottomDockWidgetArea, metadataDock); metadataDock->hide(); viewMenu->addAction(metadataDock->toggleViewAction()); m_observers.append(metadataDock); OptContentDock *optContentDock = new OptContentDock(this); addDockWidget(Qt::LeftDockWidgetArea, optContentDock); optContentDock->hide(); viewMenu->addAction(optContentDock->toggleViewAction()); m_observers.append(optContentDock); Q_FOREACH (DocumentObserver *obs, m_observers) { obs->m_viewer = this; } connect(navbar, &NavigationToolBar::zoomChanged, view, &PageView::slotZoomChanged); connect(navbar, &NavigationToolBar::rotationChanged, view, &PageView::slotRotationChanged); // activate AA by default m_settingsTextAAAct->setChecked(true); m_settingsGfxAAAct->setChecked(true); } PdfViewer::~PdfViewer() { closeDocument(); } QSize PdfViewer::sizeHint() const { return QSize(500, 600); } void PdfViewer::loadDocument(const QString &file) { // resetting xrefReconstructed each time we load new document xrefReconstructed = false; std::unique_ptr newdoc = Poppler::Document::load(file); if (!newdoc) { QMessageBox msgbox(QMessageBox::Critical, tr("Open Error"), tr("Cannot open:\n") + file, QMessageBox::Ok, this); msgbox.exec(); return; } while (newdoc->isLocked()) { bool ok = true; QString password = QInputDialog::getText(this, tr("Document Password"), tr("Please insert the password of the document:"), QLineEdit::Password, QString(), &ok); if (!ok) { return; } newdoc->unlock(password.toLatin1(), password.toLatin1()); } closeDocument(); m_doc = std::move(newdoc); m_doc->setRenderHint(Poppler::Document::TextAntialiasing, m_settingsTextAAAct->isChecked()); m_doc->setRenderHint(Poppler::Document::Antialiasing, m_settingsGfxAAAct->isChecked()); m_doc->setRenderBackend((Poppler::Document::RenderBackend)m_settingsRenderBackendGrp->checkedAction()->data().toInt()); if (m_doc->xrefWasReconstructed()) { xrefReconstructedHandler(); } else { std::function cb = [this]() { xrefReconstructedHandler(); }; m_doc->setXRefReconstructedCallback(cb); } Q_FOREACH (DocumentObserver *obs, m_observers) { obs->documentLoaded(); obs->pageChanged(0); } m_fileSaveCopyAct->setEnabled(true); } void PdfViewer::closeDocument() { if (!m_doc) { return; } Q_FOREACH (DocumentObserver *obs, m_observers) { obs->documentClosed(); } m_currentPage = 0; m_doc = nullptr; m_fileSaveCopyAct->setEnabled(false); } void PdfViewer::xrefReconstructedHandler() { if (!xrefReconstructed) { QMessageBox msgbox(QMessageBox::Critical, tr("File may be corrupted"), tr("The PDF may be broken but we're still showing something, contents may not be correct"), QMessageBox::Ok, this); msgbox.exec(); xrefReconstructed = true; } } void PdfViewer::slotOpenFile() { QString fileName = QFileDialog::getOpenFileName(this, tr("Open PDF Document"), QDir::homePath(), tr("PDF Documents (*.pdf)")); if (fileName.isEmpty()) { return; } loadDocument(fileName); } void PdfViewer::slotSaveCopy() { if (!m_doc) { return; } QString fileName = QFileDialog::getSaveFileName(this, tr("Save Copy"), QDir::homePath(), tr("PDF Documents (*.pdf)")); if (fileName.isEmpty()) { return; } std::unique_ptr converter = m_doc->pdfConverter(); converter->setOutputFileName(fileName); converter->setPDFOptions(converter->pdfOptions() & ~Poppler::PDFConverter::WithChanges); if (!converter->convert()) { QMessageBox msgbox(QMessageBox::Critical, tr("Save Error"), tr("Cannot export to:\n%1").arg(fileName), QMessageBox::Ok, this); } } void PdfViewer::slotAbout() { QMessageBox::about(this, tr("About Poppler-Qt6 Demo"), tr("This is a demo of the Poppler-Qt6 library.")); } void PdfViewer::slotAboutQt() { QMessageBox::aboutQt(this); } void PdfViewer::slotToggleTextAA(bool value) { if (!m_doc) { return; } m_doc->setRenderHint(Poppler::Document::TextAntialiasing, value); Q_FOREACH (DocumentObserver *obs, m_observers) { obs->pageChanged(m_currentPage); } } void PdfViewer::slotToggleGfxAA(bool value) { if (!m_doc) { return; } m_doc->setRenderHint(Poppler::Document::Antialiasing, value); Q_FOREACH (DocumentObserver *obs, m_observers) { obs->pageChanged(m_currentPage); } } void PdfViewer::slotRenderBackend(QAction *act) { if (!m_doc || !act) { return; } m_doc->setRenderBackend((Poppler::Document::RenderBackend)act->data().toInt()); Q_FOREACH (DocumentObserver *obs, m_observers) { obs->pageChanged(m_currentPage); } } void PdfViewer::setPage(int page) { Q_FOREACH (DocumentObserver *obs, m_observers) { obs->pageChanged(page); } m_currentPage = page; } int PdfViewer::page() const { return m_currentPage; } poppler-24.02.0/qt6/demos/viewer.h000066400000000000000000000040451455701731300166440ustar00rootroot00000000000000/* * Copyright (C) 2008, Pino Toscano * Copyright (C) 2021, Mahmoud Khalil * Copyright (C) 2021, Oliver Sander * Copyright (C) 2021, 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 * 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 PDFVIEWER_H #define PDFVIEWER_H #include class QAction; class QActionGroup; class QLabel; class DocumentObserver; namespace Poppler { class Document; } class PdfViewer : public QMainWindow { Q_OBJECT friend class DocumentObserver; public: explicit PdfViewer(QWidget *parent = nullptr); ~PdfViewer() override; QSize sizeHint() const override; void loadDocument(const QString &file); void closeDocument(); private Q_SLOTS: void slotOpenFile(); void slotSaveCopy(); void slotAbout(); void slotAboutQt(); void slotToggleTextAA(bool value); void slotToggleGfxAA(bool value); void slotRenderBackend(QAction *act); private: void setPage(int page); int page() const; void xrefReconstructedHandler(); int m_currentPage; bool xrefReconstructed; QAction *m_fileOpenAct; QAction *m_fileSaveCopyAct; QAction *m_settingsTextAAAct; QAction *m_settingsGfxAAAct; QActionGroup *m_settingsRenderBackendGrp; QList m_observers; std::unique_ptr m_doc; }; #endif poppler-24.02.0/qt6/src/000077500000000000000000000000001455701731300146475ustar00rootroot00000000000000poppler-24.02.0/qt6/src/.gitignore000066400000000000000000000001141455701731300166330ustar00rootroot00000000000000.deps .libs *.la *.lo Makefile Makefile.in APIDOCS-html APIDOCS-latex *.moc poppler-24.02.0/qt6/src/CMakeLists.txt000066400000000000000000000041421455701731300174100ustar00rootroot00000000000000add_definitions(-DQT_NO_SIGNALS_SLOTS_KEYWORDS) include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${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 poppler-annotation.cc poppler-document.cc poppler-embeddedfile.cc poppler-fontinfo.cc poppler-form.cc poppler-link.cc poppler-link-extractor.cc poppler-movie.cc poppler-optcontent.cc poppler-page.cc poppler-base-converter.cc poppler-pdf-converter.cc poppler-private.cc poppler-ps-converter.cc poppler-qiodeviceinstream.cc poppler-qiodeviceoutstream.cc poppler-sound.cc poppler-textbox.cc poppler-page-transition.cc poppler-media.cc poppler-outline.cc QPainterOutputDev.cc poppler-version.cpp ) 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.4.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}") endif() target_link_libraries(poppler-qt6 poppler Qt6::Core Qt6::Gui Freetype::Freetype) if (ENABLE_NSS3) target_include_directories(poppler-qt6 SYSTEM PRIVATE ${NSS3_INCLUDE_DIRS}) 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 poppler-qt6.h poppler-link.h poppler-annotation.h poppler-form.h poppler-optcontent.h poppler-page-transition.h poppler-media.h ${CMAKE_CURRENT_BINARY_DIR}/poppler-export.h ${CMAKE_CURRENT_BINARY_DIR}/poppler-version.h DESTINATION include/poppler/qt6) poppler-24.02.0/qt6/src/Doxyfile000066400000000000000000002054021455701731300163600ustar00rootroot00000000000000# Doxyfile 1.7.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = "Poppler Qt6" # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = 24.02.0 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = NO # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this # tag. The format is ext=language, where ext is a file extension, and language # is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen to replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penality. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will rougly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = NO # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = YES # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = NO # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = YES # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = YES # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. The create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text " # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = Mainpage.dox \ poppler-annotation.h \ poppler-form.h \ poppler-link.h \ poppler-qt6.h \ poppler-optcontent.h \ poppler-page-transition.h # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = APIDOCS-html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the stylesheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = YES # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = poppler-qt6.qch # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.freedesktop.poppler.qt6 # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = "Poppler 0.15.0" # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = poppler # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = poppler # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = qhelpgenerator # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = NO # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvances is that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = YES # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = APIDOCS-latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = YES # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = "Q_DECL_DEPRECATED=" \ "POPPLER_QT6_EXPORT=" # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans.ttf # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES poppler-24.02.0/qt6/src/Mainpage.dox000066400000000000000000000053421455701731300171100ustar00rootroot00000000000000/** @mainpage The Poppler Qt6 interface library The %Poppler Qt6 interface library, libpoppler-qt6, is a library that allows Qt6 programmers to easily load and render PDF files. The %Poppler Qt6 interface library uses poppler internally to do its job, but the Qt6 programmer will never have to worry about poppler internals. @section help Current Status The %Poppler Qt6 interface library is quite stable and working. @section refimpl Example Programs Examples programs can be found in the qt6/test directory. The %Poppler Qt6 interface library is also used in the KDE's document viewer Okular. The source files for Okular's PDF plugin (%Poppler-based) can be found on the git server of the KDE project, under this URL. @section req How to use the Poppler Qt6 interface library in three easy steps Programmer who would like to use the %Poppler Qt6 interface library simply need to add the following line to their C++ source files: @code #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); if (!document || document->isLocked()) { // ... error message .... delete document; return; } @endcode Pages can be rendered to QImages with the following commands: @code // Paranoid safety check if (document == 0) { // ... error message ... return; } // Access page of the PDF file Poppler::Page* pdfPage = document->page(pageNumber); // Document starts at page 0 if (pdfPage == 0) { // ... error message ... return; } // Generate a QImage of the rendered page QImage image = pdfPage->renderToImage(xres, yres, x, y, width, height); if (image.isNull()) { // ... error message ... return; } // ... use image ... // after the usage, the page must be deleted delete pdfPage; @endcode Finally, don't forget to destroy the document: @code delete document; @endcode */ poppler-24.02.0/qt6/src/QPainterOutputDev.cc000066400000000000000000001221401455701731300205610ustar00rootroot00000000000000//======================================================================== // // QPainterOutputDev.cc // // Copyright 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) 2005 Brad Hards // Copyright (C) 2005-2009, 2011, 2012, 2014, 2015, 2018, 2019, 2021, 2022 Albert Astals Cid // Copyright (C) 2008, 2010 Pino Toscano // Copyright (C) 2009, 2011 Carlos Garcia Campos // Copyright (C) 2009 Petr Gajdos // Copyright (C) 2010 Matthias Fauconneau // Copyright (C) 2011 Andreas Hartmetz // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2013 Dominik Haumann // Copyright (C) 2013 Mihai Niculescu // Copyright (C) 2017, 2018, 2020-2022 Oliver Sander // 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 // // 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 #include #include #include #include "goo/ft_utils.h" #include "goo/gfile.h" #include "GlobalParams.h" #include "Error.h" #include "Object.h" #include "GfxState.h" #include "GfxFont.h" #include "Link.h" #include "FontEncodingTables.h" #include #include #include "QPainterOutputDev.h" #include "Page.h" #include "Gfx.h" #include "PDFDoc.h" #include #include #include #include #include class QPainterOutputDevType3Font { public: QPainterOutputDevType3Font(PDFDoc *doc, const std::shared_ptr &font); const QPicture &getGlyph(int gid) const; private: PDFDoc *m_doc; std::shared_ptr m_font; mutable std::vector> glyphs; public: std::vector codeToGID; }; QPainterOutputDevType3Font::QPainterOutputDevType3Font(PDFDoc *doc, const std::shared_ptr &font) : m_doc(doc), m_font(font) { char *name; const Dict *charProcs = font->getCharProcs(); // Storage for the rendered glyphs glyphs.resize(charProcs->getLength()); // Compute the code-to-GID map char **enc = font->getEncoding(); codeToGID.resize(256); for (int i = 0; i < 256; ++i) { codeToGID[i] = 0; if (charProcs && (name = enc[i])) { for (int j = 0; j < charProcs->getLength(); j++) { if (strcmp(name, charProcs->getKey(j)) == 0) { codeToGID[i] = j; } } } } } const QPicture &QPainterOutputDevType3Font::getGlyph(int gid) const { if (!glyphs[gid]) { // Glyph has not been rendered before: render it now // Smallest box that contains all the glyphs from this font const double *fontBBox = m_font->getFontBBox(); PDFRectangle box(fontBBox[0], fontBBox[1], fontBBox[2], fontBBox[3]); Dict *resDict = m_font->getResources(); QPainter glyphPainter; glyphs[gid] = std::make_unique(); glyphPainter.begin(glyphs[gid].get()); auto output_dev = std::make_unique(&glyphPainter); auto gfx = std::make_unique(m_doc, output_dev.get(), resDict, &box, // pagebox nullptr // cropBox ); output_dev->startDoc(m_doc); output_dev->startPage(1, gfx->getState(), gfx->getXRef()); const Dict *charProcs = m_font->getCharProcs(); Object charProc = charProcs->getVal(gid); gfx->display(&charProc); glyphPainter.end(); } return *glyphs[gid]; } //------------------------------------------------------------------------ // QPainterOutputDev //------------------------------------------------------------------------ QPainterOutputDev::QPainterOutputDev(QPainter *painter) : m_lastTransparencyGroupPicture(nullptr), m_hintingPreference(QFont::PreferDefaultHinting) { m_painter.push(painter); m_currentBrush = QBrush(Qt::SolidPattern); auto error = FT_Init_FreeType(&m_ftLibrary); if (error) { qCritical() << "An error occurred will initializing the FreeType library"; } // as of FT 2.1.8, CID fonts are indexed by CID instead of GID FT_Int major, minor, patch; FT_Library_Version(m_ftLibrary, &major, &minor, &patch); m_useCIDs = major > 2 || (major == 2 && (minor > 1 || (minor == 1 && patch > 7))); } QPainterOutputDev::~QPainterOutputDev() { for (auto &codeToGID : m_codeToGIDCache) { gfree(const_cast(codeToGID.second)); } FT_Done_FreeType(m_ftLibrary); } void QPainterOutputDev::startDoc(PDFDoc *doc) { xref = doc->getXRef(); m_doc = doc; for (auto &codeToGID : m_codeToGIDCache) { gfree(const_cast(codeToGID.second)); } m_codeToGIDCache.clear(); } void QPainterOutputDev::startPage(int pageNum, GfxState *state, XRef *) { } void QPainterOutputDev::endPage() { } void QPainterOutputDev::saveState(GfxState *state) { m_currentPenStack.push(m_currentPen); m_currentBrushStack.push(m_currentBrush); m_rawFontStack.push(m_rawFont); m_type3FontStack.push(m_currentType3Font); m_codeToGIDStack.push(m_codeToGID); m_painter.top()->save(); } void QPainterOutputDev::restoreState(GfxState *state) { m_painter.top()->restore(); m_codeToGID = m_codeToGIDStack.top(); m_codeToGIDStack.pop(); m_rawFont = m_rawFontStack.top(); m_rawFontStack.pop(); m_currentType3Font = m_type3FontStack.top(); m_type3FontStack.pop(); m_currentBrush = m_currentBrushStack.top(); m_currentBrushStack.pop(); m_currentPen = m_currentPenStack.top(); m_currentPenStack.pop(); } void QPainterOutputDev::updateAll(GfxState *state) { OutputDev::updateAll(state); m_needFontUpdate = true; } // Set CTM (Current Transformation Matrix) to a fixed matrix void QPainterOutputDev::setDefaultCTM(const double *ctm) { m_painter.top()->setTransform(QTransform(ctm[0], ctm[1], ctm[2], ctm[3], ctm[4], ctm[5])); } // Update the CTM (Current Transformation Matrix), i.e., compose the old // CTM with a new matrix. void QPainterOutputDev::updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32) { updateLineDash(state); updateLineJoin(state); updateLineCap(state); updateLineWidth(state); QTransform update(m11, m12, m21, m22, m31, m32); // We could also set (rather than update) the painter transformation to state->getCMT(); m_painter.top()->setTransform(update, true); } void QPainterOutputDev::updateLineDash(GfxState *state) { double dashStart; const std::vector &dashPattern = state->getLineDash(&dashStart); // Special handling for zero-length patterns, i.e., solid lines. // Simply calling QPen::setDashPattern with an empty pattern does *not* // result in a solid line. Rather, the current pattern is unchanged. // See the implementation of the setDashPattern method in the file qpen.cpp. if (dashPattern.empty()) { m_currentPen.setStyle(Qt::SolidLine); m_painter.top()->setPen(m_currentPen); return; } QVector pattern(dashPattern.size()); double scaling = state->getLineWidth(); // Negative line widths are not allowed, width 0 counts as 'one pixel width'. if (scaling <= 0) { scaling = 1.0; } for (std::vector::size_type i = 0; i < dashPattern.size(); ++i) { // pdf measures the dash pattern in dots, but Qt uses the // line width as the unit. pattern[i] = dashPattern[i] / scaling; } m_currentPen.setDashPattern(pattern); m_currentPen.setDashOffset(dashStart); m_painter.top()->setPen(m_currentPen); } void QPainterOutputDev::updateFlatness(GfxState *state) { // qDebug() << "updateFlatness"; } void QPainterOutputDev::updateLineJoin(GfxState *state) { switch (state->getLineJoin()) { case 0: // The correct style here is Qt::SvgMiterJoin, *not* Qt::MiterJoin. // The two differ in what to do if the miter limit is exceeded. // See https://bugs.freedesktop.org/show_bug.cgi?id=102356 m_currentPen.setJoinStyle(Qt::SvgMiterJoin); break; case 1: m_currentPen.setJoinStyle(Qt::RoundJoin); break; case 2: m_currentPen.setJoinStyle(Qt::BevelJoin); break; } m_painter.top()->setPen(m_currentPen); } void QPainterOutputDev::updateLineCap(GfxState *state) { switch (state->getLineCap()) { case 0: m_currentPen.setCapStyle(Qt::FlatCap); break; case 1: m_currentPen.setCapStyle(Qt::RoundCap); break; case 2: m_currentPen.setCapStyle(Qt::SquareCap); break; } m_painter.top()->setPen(m_currentPen); } void QPainterOutputDev::updateMiterLimit(GfxState *state) { m_currentPen.setMiterLimit(state->getMiterLimit()); m_painter.top()->setPen(m_currentPen); } void QPainterOutputDev::updateLineWidth(GfxState *state) { m_currentPen.setWidthF(state->getLineWidth()); m_painter.top()->setPen(m_currentPen); // The updateLineDash method needs to know the line width, but it is sometimes // called before the updateLineWidth method. To make sure that the last call // to updateLineDash before a drawing operation is always with the correct line // width, we call it here, right after a change to the line width. updateLineDash(state); } void QPainterOutputDev::updateFillColor(GfxState *state) { GfxRGB rgb; QColor brushColour = m_currentBrush.color(); state->getFillRGB(&rgb); brushColour.setRgbF(colToDbl(rgb.r), colToDbl(rgb.g), colToDbl(rgb.b), brushColour.alphaF()); m_currentBrush.setColor(brushColour); } void QPainterOutputDev::updateStrokeColor(GfxState *state) { GfxRGB rgb; QColor penColour = m_currentPen.color(); state->getStrokeRGB(&rgb); penColour.setRgbF(colToDbl(rgb.r), colToDbl(rgb.g), colToDbl(rgb.b), penColour.alphaF()); m_currentPen.setColor(penColour); m_painter.top()->setPen(m_currentPen); } void QPainterOutputDev::updateBlendMode(GfxState *state) { GfxBlendMode blendMode = state->getBlendMode(); // missing composition modes in QPainter: // - CompositionMode_Hue // - CompositionMode_Color // - CompositionMode_Luminosity // - CompositionMode_Saturation switch (blendMode) { case gfxBlendMultiply: m_painter.top()->setCompositionMode(QPainter::CompositionMode_Multiply); break; case gfxBlendScreen: m_painter.top()->setCompositionMode(QPainter::CompositionMode_Screen); break; case gfxBlendDarken: m_painter.top()->setCompositionMode(QPainter::CompositionMode_Darken); break; case gfxBlendLighten: m_painter.top()->setCompositionMode(QPainter::CompositionMode_Lighten); break; case gfxBlendColorDodge: m_painter.top()->setCompositionMode(QPainter::CompositionMode_ColorDodge); break; case gfxBlendColorBurn: m_painter.top()->setCompositionMode(QPainter::CompositionMode_ColorBurn); break; case gfxBlendHardLight: m_painter.top()->setCompositionMode(QPainter::CompositionMode_HardLight); break; case gfxBlendSoftLight: m_painter.top()->setCompositionMode(QPainter::CompositionMode_SoftLight); break; case gfxBlendDifference: m_painter.top()->setCompositionMode(QPainter::CompositionMode_Difference); break; case gfxBlendExclusion: m_painter.top()->setCompositionMode(QPainter::CompositionMode_Exclusion); break; case gfxBlendColor: m_painter.top()->setCompositionMode(QPainter::CompositionMode_Plus); break; default: qDebug() << "Unsupported blend mode, falling back to CompositionMode_SourceOver"; [[fallthrough]]; case gfxBlendNormal: m_painter.top()->setCompositionMode(QPainter::CompositionMode_SourceOver); break; } } void QPainterOutputDev::updateFillOpacity(GfxState *state) { QColor brushColour = m_currentBrush.color(); brushColour.setAlphaF(state->getFillOpacity()); m_currentBrush.setColor(brushColour); } void QPainterOutputDev::updateStrokeOpacity(GfxState *state) { QColor penColour = m_currentPen.color(); penColour.setAlphaF(state->getStrokeOpacity()); m_currentPen.setColor(penColour); m_painter.top()->setPen(m_currentPen); } void QPainterOutputDev::updateFont(GfxState *state) { const std::shared_ptr &gfxFont = state->getFont(); if (!gfxFont) { return; } // The key to look in the font caches QPainterFontID fontID = { *gfxFont->getID(), state->getFontSize() }; // Current font is a type3 font if (gfxFont->getType() == fontType3) { auto cacheEntry = m_type3FontCache.find(fontID); if (cacheEntry != m_type3FontCache.end()) { // Take the font from the cache m_currentType3Font = cacheEntry->second.get(); } else { m_currentType3Font = new QPainterOutputDevType3Font(m_doc, std::static_pointer_cast(gfxFont)); m_type3FontCache.insert(std::make_pair(fontID, std::unique_ptr(m_currentType3Font))); } return; } // Non-type3: is the font in the cache? auto cacheEntry = m_rawFontCache.find(fontID); if (cacheEntry != m_rawFontCache.end()) { // Take the font from the cache m_rawFont = cacheEntry->second.get(); } else { // New font: load it into the cache float fontSize = state->getFontSize(); std::optional fontLoc = gfxFont->locateFont(xref, nullptr); if (fontLoc) { // load the font from respective location switch (fontLoc->locType) { case gfxFontLocEmbedded: { // if there is an embedded font, read it to memory const std::optional> fontData = gfxFont->readEmbFontFile(xref); // fontData gets copied in the QByteArray constructor m_rawFont = new QRawFont(QByteArray(fontData ? (const char *)fontData->data() : nullptr, fontData ? fontData->size() : 0), fontSize, m_hintingPreference); m_rawFontCache.insert(std::make_pair(fontID, std::unique_ptr(m_rawFont))); break; } case gfxFontLocExternal: { // font is in an external font file QString fontFile(fontLoc->path.c_str()); m_rawFont = new QRawFont(fontFile, fontSize, m_hintingPreference); m_rawFontCache.insert(std::make_pair(fontID, std::unique_ptr(m_rawFont))); break; } case gfxFontLocResident: { // font resides in a PS printer qDebug() << "Resident Font Resident not implemented yet!"; break; } } // end switch } else { qDebug() << "Font location not found!"; return; } } if (!m_rawFont->isValid()) { qDebug() << "RawFont is not valid"; } // ***************************************************************************** // We have now successfully loaded the font into a QRawFont object. This // allows us to draw all the glyphs in the font. However, what is missing is // the charcode-to-glyph-index mapping. Apparently, Qt does not provide this // information at all. Therefore, we need to figure it ourselves, using // FoFi and FreeType. // ***************************************************************************** m_needFontUpdate = false; GfxFontType fontType = gfxFont->getType(); // Default: no codeToGID table m_codeToGID = nullptr; // check the font file cache Ref id = *gfxFont->getID(); auto codeToGIDIt = m_codeToGIDCache.find(id); if (codeToGIDIt != m_codeToGIDCache.end()) { m_codeToGID = codeToGIDIt->second; } else { std::optional> fontBuffer; std::optional fontLoc = gfxFont->locateFont(xref, nullptr); if (!fontLoc) { error(errSyntaxError, -1, "Couldn't find a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)"); return; } // embedded font if (fontLoc->locType == gfxFontLocEmbedded) { // if there is an embedded font, read it to memory fontBuffer = gfxFont->readEmbFontFile(xref); if (!fontBuffer) { return; } // external font } else { // gfxFontLocExternal // Hmm, fontType has already been set to gfxFont->getType() above. // Can it really assume a different value here? fontType = fontLoc->fontType; } switch (fontType) { case fontType1: 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)) { 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)) { error(errSyntaxError, -1, "Couldn't create a FreeType face for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)"); return; } } const char *name; int *codeToGID = (int *)gmallocn(256, sizeof(int)); 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) { name = GfxFont::getAlternateName(name); if (name) { codeToGID[i] = FT_Get_Name_Index(freeTypeFace, (char *)name); } } } } FT_Done_Face(freeTypeFace); m_codeToGIDCache[id] = codeToGID; break; } case fontTrueType: case fontTrueTypeOT: { auto ff = (fontLoc->locType != gfxFontLocEmbedded) ? FoFiTrueType::load(fontLoc->path.c_str()) : FoFiTrueType::make(fontBuffer->data(), fontBuffer->size()); m_codeToGIDCache[id] = (ff) ? ((Gfx8BitFont *)gfxFont.get())->getCodeToGIDMap(ff.get()) : nullptr; break; } case fontCIDType0: case fontCIDType0C: { int *cidToGIDMap = nullptr; int nCIDs = 0; // 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; } m_codeToGIDCache[id] = cidToGIDMap; break; } case fontCIDType0COT: { int *codeToGID = nullptr; 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)); } int *cidToGIDMap = nullptr; int nCIDs = 0; if (!codeToGID && !m_useCIDs) { auto ff = (fontLoc->locType != gfxFontLocEmbedded) ? FoFiTrueType::load(fontLoc->path.c_str()) : FoFiTrueType::make(fontBuffer->data(), fontBuffer->size()); if (ff && ff->isOpenTypeCFF()) { cidToGIDMap = ff->getCIDToGIDMap(&nCIDs); } } m_codeToGIDCache[id] = codeToGID ? 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)); } } else { auto ff = (fontLoc->locType != gfxFontLocEmbedded) ? FoFiTrueType::load(fontLoc->path.c_str()) : FoFiTrueType::make(fontBuffer->data(), fontBuffer->size()); if (!ff) { return; } codeToGID = ((GfxCIDFont *)gfxFont.get())->getCodeToGIDMap(ff.get(), &codeToGIDLen); } m_codeToGIDCache[id] = codeToGID; break; } default: // this shouldn't happen return; } m_codeToGID = m_codeToGIDCache[id]; } } static QPainterPath convertPath(GfxState *state, const GfxPath *path, Qt::FillRule fillRule) { int i, j; QPainterPath qPath; qPath.setFillRule(fillRule); for (i = 0; i < path->getNumSubpaths(); ++i) { const GfxSubpath *subpath = path->getSubpath(i); if (subpath->getNumPoints() > 0) { qPath.moveTo(subpath->getX(0), subpath->getY(0)); j = 1; while (j < subpath->getNumPoints()) { if (subpath->getCurve(j)) { qPath.cubicTo(subpath->getX(j), subpath->getY(j), subpath->getX(j + 1), subpath->getY(j + 1), subpath->getX(j + 2), subpath->getY(j + 2)); j += 3; } else { qPath.lineTo(subpath->getX(j), subpath->getY(j)); ++j; } } if (subpath->isClosed()) { qPath.closeSubpath(); } } } return qPath; } void QPainterOutputDev::stroke(GfxState *state) { m_painter.top()->strokePath(convertPath(state, state->getPath(), Qt::OddEvenFill), m_currentPen); } void QPainterOutputDev::fill(GfxState *state) { m_painter.top()->fillPath(convertPath(state, state->getPath(), Qt::WindingFill), m_currentBrush); } void QPainterOutputDev::eoFill(GfxState *state) { m_painter.top()->fillPath(convertPath(state, state->getPath(), Qt::OddEvenFill), m_currentBrush); } bool QPainterOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) { double x0, y0, x1, y1; shading->getCoords(&x0, &y0, &x1, &y1); // get the clip region bbox double xMin, yMin, xMax, yMax; state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); // get the function domain double t0 = shading->getDomain0(); double t1 = shading->getDomain1(); // Max number of splits along the t axis constexpr int maxSplits = 256; // Max delta allowed in any color component const double colorDelta = (dblToCol(1 / 256.0)); // Number of color space components auto nComps = shading->getColorSpace()->getNComps(); // If the clipping region is a stroke, then the current operation counts as a stroke // rather than as a fill, and the opacity has to be set accordingly. // See https://gitlab.freedesktop.org/poppler/poppler/-/issues/178 auto opacity = (state->getStrokePattern()) ? state->getStrokeOpacity() : state->getFillOpacity(); // Helper function to test two color objects for 'almost-equality' auto isSameGfxColor = [&nComps, &colorDelta](const GfxColor &colorA, const GfxColor &colorB) { for (int k = 0; k < nComps; ++k) { if (abs(colorA.c[k] - colorB.c[k]) > colorDelta) { return false; } } return true; }; // Helper function: project a number into an interval // With C++17 this is part of the standard library auto clamp = [](double v, double lo, double hi) { return std::min(std::max(v, lo), hi); }; // ta stores all parameter values where we evaluate the input shading function. // In between, QLinearGradient will interpolate linearly. // We set up the array with three values. std::array ta; ta[0] = tMin; std::array next; next[0] = maxSplits / 2; ta[maxSplits / 2] = 0.5 * (tMin + tMax); next[maxSplits / 2] = maxSplits; ta[maxSplits] = tMax; // compute the color at t = tMin double tt = clamp(t0 + (t1 - t0) * tMin, t0, t1); GfxColor color0, color1; shading->getColor(tt, &color0); // Construct a gradient object and set its color at one parameter end QLinearGradient gradient(QPointF(x0 + tMin * (x1 - x0), y0 + tMin * (y1 - y0)), QPointF(x0 + tMax * (x1 - x0), y0 + tMax * (y1 - y0))); GfxRGB rgb; shading->getColorSpace()->getRGB(&color0, &rgb); QColor qColor(colToByte(rgb.r), colToByte(rgb.g), colToByte(rgb.b), dblToByte(opacity)); gradient.setColorAt(0, qColor); // Look for more relevant parameter values by bisection int i = 0; while (i < maxSplits) { int j = next[i]; while (j > i + 1) { // Next parameter value to try tt = clamp(t0 + (t1 - t0) * ta[j], t0, t1); shading->getColor(tt, &color1); // j is a good next color stop if the input shading can be approximated well // on the interval (ta[i], ta[j]) by a linear interpolation. // We test this by comparing the real color in the middle between ta[i] and ta[j] // with the linear interpolant there. auto midPoint = 0.5 * (ta[i] + ta[j]); GfxColor colorAtMidPoint; shading->getColor(midPoint, &colorAtMidPoint); GfxColor linearlyInterpolatedColor; for (int ii = 0; ii < nComps; ii++) { linearlyInterpolatedColor.c[ii] = 0.5 * (color0.c[ii] + color1.c[ii]); } // If the two colors are equal, ta[j] is a good place for the next color stop; take it! if (isSameGfxColor(colorAtMidPoint, linearlyInterpolatedColor)) { break; } // Otherwise: bisect further int k = (i + j) / 2; ta[k] = midPoint; next[i] = k; next[k] = j; j = k; } // set the color shading->getColorSpace()->getRGB(&color1, &rgb); qColor.setRgb(colToByte(rgb.r), colToByte(rgb.g), colToByte(rgb.b), dblToByte(opacity)); gradient.setColorAt((ta[j] - tMin) / (tMax - tMin), qColor); // Move to the next parameter region color0 = color1; i = next[i]; } state->moveTo(xMin, yMin); state->lineTo(xMin, yMax); state->lineTo(xMax, yMax); state->lineTo(xMax, yMin); state->closePath(); // Actually paint the shaded region QBrush newBrush(gradient); m_painter.top()->fillPath(convertPath(state, state->getPath(), Qt::WindingFill), newBrush); state->clearPath(); // True means: The shaded region has been painted return true; } void QPainterOutputDev::clip(GfxState *state) { m_painter.top()->setClipPath(convertPath(state, state->getPath(), Qt::WindingFill), Qt::IntersectClip); } void QPainterOutputDev::eoClip(GfxState *state) { m_painter.top()->setClipPath(convertPath(state, state->getPath(), Qt::OddEvenFill), Qt::IntersectClip); } void QPainterOutputDev::clipToStrokePath(GfxState *state) { QPainterPath clipPath = convertPath(state, state->getPath(), Qt::WindingFill); // Get the outline of 'clipPath' as a separate path QPainterPathStroker stroker; stroker.setWidth(state->getLineWidth()); stroker.setCapStyle(m_currentPen.capStyle()); stroker.setJoinStyle(m_currentPen.joinStyle()); stroker.setMiterLimit(state->getMiterLimit()); stroker.setDashPattern(m_currentPen.dashPattern()); stroker.setDashOffset(m_currentPen.dashOffset()); QPainterPath clipPathOutline = stroker.createStroke(clipPath); // The interior of the outline is the desired clipping region m_painter.top()->setClipPath(clipPathOutline, Qt::IntersectClip); } void QPainterOutputDev::drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, const Unicode *u, int uLen) { // First handle type3 fonts const std::shared_ptr &gfxFont = state->getFont(); GfxFontType fontType = gfxFont->getType(); if (fontType == fontType3) { ///////////////////////////////////////////////////////////////////// // Draw the QPicture that contains the glyph onto the page ///////////////////////////////////////////////////////////////////// // Store the QPainter state; we need to modify it temporarily m_painter.top()->save(); // Make the glyph position the coordinate origin -- that's our center of scaling m_painter.top()->translate(QPointF(x - originX, y - originY)); const double *mat = gfxFont->getFontMatrix(); QTransform fontMatrix(mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); // Scale with the font size fontMatrix.scale(state->getFontSize(), state->getFontSize()); m_painter.top()->setTransform(fontMatrix, true); // Apply the text matrix on top const double *textMat = state->getTextMat(); QTransform textTransform(textMat[0] * state->getHorizScaling(), textMat[1] * state->getHorizScaling(), textMat[2], textMat[3], 0, 0); m_painter.top()->setTransform(textTransform, true); // Actually draw the glyph int gid = m_currentType3Font->codeToGID[code]; m_painter.top()->drawPicture(QPointF(0, 0), m_currentType3Font->getGlyph(gid)); // Restore transformation m_painter.top()->restore(); return; } // check for invisible text -- this is used by Acrobat Capture int render = state->getRender(); if (render == 3 || !m_rawFont) { qDebug() << "Invisible text found!"; return; } if (!(render & 1)) { quint32 glyphIndex = (m_codeToGID) ? m_codeToGID[code] : code; QPointF glyphPosition = QPointF(x - originX, y - originY); // QGlyphRun objects can hold an entire sequence of glyphs, and it would possibly // be more efficient to simply note the glyph and glyph position here and then // draw several glyphs at once in the endString method. What keeps us from doing // that is the transformation below: each glyph needs to be drawn upside down, // i.e., reflected at its own baseline. Since we have no guarantee that this // baseline is the same for all glyphs in a string we have to do it one by one. QGlyphRun glyphRun; glyphRun.setRawData(&glyphIndex, &glyphPosition, 1); glyphRun.setRawFont(*m_rawFont); // Store the QPainter state; we need to modify it temporarily m_painter.top()->save(); // Apply the text matrix to the glyph. The glyph is not scaled by the font size, // because the font in m_rawFont already has the correct size. // Additionally, the CTM is upside down, i.e., it contains a negative Y-scaling // entry. Therefore, Qt will paint the glyphs upside down. We need to temporarily // reflect the page at glyphPosition.y(). // Make the glyph position the coordinate origin -- that's our center of scaling const double *textMat = state->getTextMat(); m_painter.top()->translate(QPointF(glyphPosition.x(), glyphPosition.y())); QTransform textTransform(textMat[0] * state->getHorizScaling(), textMat[1] * state->getHorizScaling(), -textMat[2], // reflect at the horizontal axis, -textMat[3], // because CTM is upside-down. 0, 0); m_painter.top()->setTransform(textTransform, true); // We are painting a filled glyph here. But QPainter uses the pen to draw even filled text, // not the brush. (see, e.g., http://doc.qt.io/qt-5/qpainter.html#setPen ) // Therefore we have to temporarily overwrite the pen color. // Since we are drawing a filled glyph, one would really expect to have m_currentBrush // have the correct color. However, somehow state->getFillRGB can change without // updateFillColor getting called. Then m_currentBrush may not contain the correct color. GfxRGB rgb; state->getFillRGB(&rgb); QColor fontColor; fontColor.setRgbF(colToDbl(rgb.r), colToDbl(rgb.g), colToDbl(rgb.b), state->getFillOpacity()); m_painter.top()->setPen(fontColor); // Actually draw the glyph m_painter.top()->drawGlyphRun(QPointF(-glyphPosition.x(), -glyphPosition.y()), glyphRun); // Restore transformation and pen color m_painter.top()->restore(); } } void QPainterOutputDev::type3D0(GfxState *state, double wx, double wy) { } void QPainterOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) { } void QPainterOutputDev::endTextObject(GfxState *state) { } void QPainterOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg) { auto imgStr = std::make_unique(str, width, 1, // numPixelComps 1 // getBits ); imgStr->reset(); // TODO: Would using QImage::Format_Mono be more efficient here? QImage image(width, height, QImage::Format_ARGB32); unsigned int *data = reinterpret_cast(image.bits()); int stride = image.bytesPerLine() / 4; QRgb fillColor = m_currentBrush.color().rgb(); for (int y = 0; y < height; y++) { unsigned char *pix = imgStr->getLine(); // Invert the vertical coordinate: y is increasing from top to bottom // on the page, but y is increasing bottom to top in the picture. unsigned int *dest = data + (height - 1 - y) * stride; for (int x = 0; x < width; x++) { bool opaque = ((bool)pix[x]) == invert; dest[x] = (opaque) ? fillColor : 0; } } // At this point, the QPainter coordinate transformation (CTM) is such // that QRect(0,0,1,1) is exactly the area of the image. m_painter.top()->drawImage(QRect(0, 0, 1, 1), image); imgStr->close(); } // TODO: lots more work here. void QPainterOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) { unsigned int *data; unsigned int *line; int x, y; unsigned char *pix; int i; QImage image; int stride; /* TODO: Do we want to cache these? */ auto imgStr = std::make_unique(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgStr->reset(); image = QImage(width, height, QImage::Format_ARGB32); data = reinterpret_cast(image.bits()); stride = image.bytesPerLine() / 4; for (y = 0; y < height; y++) { pix = imgStr->getLine(); // Invert the vertical coordinate: y is increasing from top to bottom // on the page, but y is increasing bottom to top in the picture. line = data + (height - 1 - y) * stride; colorMap->getRGBLine(pix, line, width); if (maskColors) { for (x = 0; x < width; x++) { for (i = 0; i < colorMap->getNumPixelComps(); ++i) { if (pix[i] < maskColors[2 * i] * 255 || pix[i] > maskColors[2 * i + 1] * 255) { *line = *line | 0xff000000; break; } } pix += colorMap->getNumPixelComps(); line++; } } else { for (x = 0; x < width; x++) { *line = *line | 0xff000000; line++; } } } // At this point, the QPainter coordinate transformation (CTM) is such // that QRect(0,0,1,1) is exactly the area of the image. m_painter.top()->drawImage(QRect(0, 0, 1, 1), image); } void QPainterOutputDev::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) { // Bail out if the image size doesn't match the mask size. I don't know // what to do in this case. if (width != maskWidth || height != maskHeight) { qDebug() << "Soft mask size does not match image size!"; drawImage(state, ref, str, width, height, colorMap, interpolate, nullptr, false); return; } // Bail out if the mask isn't a single channel. I don't know // what to do in this case. if (maskColorMap->getColorSpace()->getNComps() != 1) { qDebug() << "Soft mask is not a single 8-bit channel!"; drawImage(state, ref, str, width, height, colorMap, interpolate, nullptr, false); return; } /* TODO: Do we want to cache these? */ auto imgStr = std::make_unique(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgStr->reset(); auto maskImageStr = std::make_unique(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits()); maskImageStr->reset(); QImage image(width, height, QImage::Format_ARGB32); unsigned int *data = reinterpret_cast(image.bits()); int stride = image.bytesPerLine() / 4; std::vector maskLine(maskWidth); for (int y = 0; y < height; y++) { unsigned char *pix = imgStr->getLine(); unsigned char *maskPix = maskImageStr->getLine(); // Invert the vertical coordinate: y is increasing from top to bottom // on the page, but y is increasing bottom to top in the picture. unsigned int *line = data + (height - 1 - y) * stride; colorMap->getRGBLine(pix, line, width); // Apply the mask values to the image alpha channel maskColorMap->getGrayLine(maskPix, maskLine.data(), width); for (int x = 0; x < width; x++) { *line = *line | (maskLine[x] << 24); line++; } } // At this point, the QPainter coordinate transformation (CTM) is such // that QRect(0,0,1,1) is exactly the area of the image. m_painter.top()->drawImage(QRect(0, 0, 1, 1), image); } void QPainterOutputDev::beginTransparencyGroup(GfxState * /*state*/, const double * /*bbox*/, GfxColorSpace * /*blendingColorSpace*/, bool /*isolated*/, bool /*knockout*/, bool /*forSoftMask*/) { // The entire transparency group will be painted into a // freshly created QPicture object. Since an existing painter // cannot change its paint device, we need to construct a // new QPainter object as well. m_qpictures.push(new QPicture); m_painter.push(new QPainter(m_qpictures.top())); } void QPainterOutputDev::endTransparencyGroup(GfxState * /*state*/) { // Stop painting into the group m_painter.top()->end(); // Kill the painter that has been used for the transparency group delete (m_painter.top()); m_painter.pop(); // Store the QPicture object that holds the result of the transparency group // painting. It will be painted and deleted in the method paintTransparencyGroup. if (m_lastTransparencyGroupPicture) { qDebug() << "Found a transparency group that has not been painted"; delete (m_lastTransparencyGroupPicture); } m_lastTransparencyGroupPicture = m_qpictures.top(); m_qpictures.pop(); } void QPainterOutputDev::paintTransparencyGroup(GfxState * /*state*/, const double * /*bbox*/) { // Actually draw the transparency group m_painter.top()->drawPicture(0, 0, *m_lastTransparencyGroupPicture); // And delete it delete (m_lastTransparencyGroupPicture); m_lastTransparencyGroupPicture = nullptr; } poppler-24.02.0/qt6/src/QPainterOutputDev.h000066400000000000000000000175121455701731300204310ustar00rootroot00000000000000//======================================================================== // // QPainterOutputDev.h // // Copyright 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) 2005 Brad Hards // Copyright (C) 2005, 2018, 2019, 2021 Albert Astals Cid // Copyright (C) 2009, 2011 Carlos Garcia Campos // Copyright (C) 2010 Pino Toscano // Copyright (C) 2011 Andreas Hartmetz // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2013 Mihai Niculescu // Copyright (C) 2017, 2018, 2020 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 // //======================================================================== #ifndef QPAINTEROUTPUTDEV_H #define QPAINTEROUTPUTDEV_H #include #include #include #include "OutputDev.h" #include "GfxState.h" #include #include FT_FREETYPE_H #include class GfxState; class PDFDoc; class QRawFont; class QPainterOutputDevType3Font; //------------------------------------------------------------------------ // QPainterOutputDev - QPainter renderer //------------------------------------------------------------------------ class QPainterOutputDev : public OutputDev { public: // Constructor. explicit QPainterOutputDev(QPainter *painter); // Destructor. ~QPainterOutputDev() override; void setHintingPreference(QFont::HintingPreference hintingPreference) { m_hintingPreference = hintingPreference; } //----- get info about output device // Does this device use upside-down coordinates? // (Upside-down means (0,0) is the top left corner of the page.) bool upsideDown() override { return true; } // Does this device use drawChar() or drawString()? bool useDrawChar() override { return true; } // Does this device implement shaded fills (aka gradients) natively? // If this returns false, these shaded fills // will be reduced to a series of other drawing operations. // type==2 is 'axial shading' bool useShadedFills(int type) override { return type == 2; } // Does this device use beginType3Char/endType3Char? Otherwise, // text in Type 3 fonts will be drawn with drawChar/drawString. bool interpretType3Chars() override { return false; } //----- initialization and control // Set Current Transformation Matrix to a fixed matrix given in ctm[0],...,ctm[5] void setDefaultCTM(const double *ctm) override; // Start a page. void startPage(int pageNum, GfxState *state, XRef *xref) override; // End a page. void endPage() override; //----- save/restore graphics state void saveState(GfxState *state) override; void restoreState(GfxState *state) override; //----- update graphics state void updateAll(GfxState *state) override; void updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32) override; void updateLineDash(GfxState *state) override; void updateFlatness(GfxState *state) override; void updateLineJoin(GfxState *state) override; void updateLineCap(GfxState *state) override; void updateMiterLimit(GfxState *state) override; void updateLineWidth(GfxState *state) override; void updateFillColor(GfxState *state) override; void updateStrokeColor(GfxState *state) override; void updateBlendMode(GfxState *state) override; void updateFillOpacity(GfxState *state) override; void updateStrokeOpacity(GfxState *state) override; //----- update text state void updateFont(GfxState *state) override; //----- path painting void stroke(GfxState *state) override; void fill(GfxState *state) override; void eoFill(GfxState *state) override; bool axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) override; //----- path clipping void clip(GfxState *state) override; void eoClip(GfxState *state) override; void clipToStrokePath(GfxState *state) override; //----- text drawing // virtual void drawString(GfxState *state, GooString *s); void drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, const Unicode *u, int uLen) override; void endTextObject(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 drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) override; void 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) override; //----- Type 3 font operators void type3D0(GfxState *state, double wx, double wy) override; void type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) override; //----- 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; void paintTransparencyGroup(GfxState *state, const double *bbox) override; //----- special access // Called to indicate that a new PDF document has been loaded. void startDoc(PDFDoc *doc); bool isReverseVideo() { return false; } private: // The stack of QPainters is used to implement transparency groups. When such a group // is opened, annew Painter that paints onto a QPicture is pushed onto the stack. // It is popped again when the transparency group ends. std::stack m_painter; // This is the corresponding stack of QPicture objects std::stack m_qpictures; // endTransparencyGroup removes a QPicture from the stack, but stores // it here for later use in paintTransparencyGroup. QPicture *m_lastTransparencyGroupPicture; QFont::HintingPreference m_hintingPreference; QPen m_currentPen; // The various stacks are used to implement the 'saveState' and 'restoreState' methods std::stack m_currentPenStack; QBrush m_currentBrush; std::stack m_currentBrushStack; bool m_needFontUpdate; // set when the font needs to be updated PDFDoc *m_doc; XRef *xref; // xref table for current document // The current font in use QRawFont *m_rawFont; std::stack m_rawFontStack; QPainterOutputDevType3Font *m_currentType3Font; std::stack m_type3FontStack; // Cache all fonts by their Ref and font size using QPainterFontID = std::pair; std::map> m_rawFontCache; std::map> m_type3FontCache; std::map m_codeToGIDCache; // The table that maps character codes to glyph indexes const int *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 bool m_useCIDs; }; #endif poppler-24.02.0/qt6/src/poppler-annotation-helper.h000066400000000000000000000047311455701731300221330ustar00rootroot00000000000000/* poppler-annotation-helper.h: qt interface to poppler * Copyright (C) 2006, 2008, 2017-2019, 2021, Albert Astals Cid * Copyright (C) 2008, Pino Toscano * Copyright (C) 2012, Fabio D'Urso * Copyright (C) 2018, Dileep Sankhla * Copyright (C) 2018, Carlos Garcia Campos * Copyright (C) 2018, 2019, Oliver Sander * Adapting code from * Copyright (C) 2004 by Enrico Ros * * 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_ANNOTATION_HELPER_H_ #define _POPPLER_ANNOTATION_HELPER_H_ #include #include #include class QColor; class AnnotColor; namespace Poppler { class XPDFReader { public: // transform from user coords to normalized ones using the matrix M static inline void transform(double *M, double x, double y, QPointF &res); static inline void invTransform(const double *M, const QPointF p, double &x, double &y); }; void XPDFReader::transform(double *M, double x, double y, QPointF &res) { res.setX(M[0] * x + M[2] * y + M[4]); res.setY(M[1] * x + M[3] * y + M[5]); } void XPDFReader::invTransform(const double *M, const QPointF p, double &x, double &y) { const double det = M[0] * M[3] - M[1] * M[2]; if (det == 0) { qWarning("Tried to invert singular matrix, something won't work"); x = 0; y = 0; return; } const double invM[4] = { M[3] / det, -M[1] / det, -M[2] / det, M[0] / det }; const double xt = p.x() - M[4]; const double yt = p.y() - M[5]; x = invM[0] * xt + invM[2] * yt; y = invM[1] * xt + invM[3] * yt; } QColor convertAnnotColor(const AnnotColor *color); std::unique_ptr convertQColor(const QColor &color); } #endif poppler-24.02.0/qt6/src/poppler-annotation-private.h000066400000000000000000000110511455701731300223170ustar00rootroot00000000000000/* poppler-annotation-private.h: qt interface to poppler * Copyright (C) 2007, Pino Toscano * Copyright (C) 2012, Tobias Koenig * Copyright (C) 2012, 2013 Fabio D'Urso * Copyright (C) 2012, 2014, 2018-2020, Albert Astals Cid * 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 * * 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_ANNOTATION_PRIVATE_H_ #define _POPPLER_ANNOTATION_PRIVATE_H_ #include #include #include #include "poppler-annotation.h" #include #include class Annot; class AnnotPath; class Page; class PDFRectangle; namespace Poppler { class DocumentData; PDFRectangle boundaryToPdfRectangle(::Page *pdfPage, const QRectF &r, int flags); void getRawDataFromQImage(const QImage &qimg, int bitsPerPixel, QByteArray *data, QByteArray *sMaskData); class AnnotationPrivate : public QSharedData { public: AnnotationPrivate(); virtual ~AnnotationPrivate(); AnnotationPrivate(const AnnotationPrivate &) = delete; AnnotationPrivate &operator=(const AnnotationPrivate &) = delete; void addRevision(Annotation *ann, Annotation::RevScope scope, Annotation::RevType type); /* Returns an Annotation of the right subclass whose d_ptr points to * this AnnotationPrivate */ virtual Annotation *makeAlias() = 0; /* properties: contents related */ QString author; QString contents; QString uniqueName; QDateTime modDate; // before or equal to currentDateTime() QDateTime creationDate; // before or equal to modifyDate /* properties: look/interaction related */ Annotation::Flags flags; QRectF boundary; /* style and popup */ Annotation::Style style; Annotation::Popup popup; /* revisions */ Annotation::RevScope revisionScope; Annotation::RevType revisionType; QList revisions; /* After this call, the Annotation object will behave like a wrapper for * the specified Annot object. All cached values are discarded */ void tieToNativeAnnot(Annot *ann, ::Page *page, DocumentData *doc); /* Creates a new Annot object on the specified page, flushes current * values to that object and ties this Annotation to that object */ virtual Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) = 0; /* Inited to 0 (i.e. untied annotation) */ Annot *pdfAnnot; ::Page *pdfPage; DocumentData *parentDoc; /* The following helpers only work if pdfPage is set */ void flushBaseAnnotationProperties(); 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; /* 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); /* Add given annotation to given page */ static void addAnnotationToPage(::Page *pdfPage, DocumentData *doc, const Annotation *ann); /* Remove annotation from page and destroy ann */ static void removeAnnotationFromPage(::Page *pdfPage, const Annotation *ann); Ref pdfObjectReference() const; std::unique_ptr additionalAction(Annotation::AdditionalActionType type) const; Object annotationAppearance; }; class AnnotationAppearancePrivate { public: explicit AnnotationAppearancePrivate(Annot *annot); Object appearance; }; } #endif poppler-24.02.0/qt6/src/poppler-annotation.cc000066400000000000000000003323321455701731300210150ustar00rootroot00000000000000/* poppler-annotation.cc: qt interface to poppler * Copyright (C) 2006, 2009, 2012-2015, 2018-2022 Albert Astals Cid * Copyright (C) 2006, 2008, 2010 Pino Toscano * Copyright (C) 2012, Guillermo A. Amaral B. * Copyright (C) 2012-2014 Fabio D'Urso * Copyright (C) 2012, 2015, Tobias Koenig * Copyright (C) 2018 Adam Reichold * 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) 2018 Dileep Sankhla * Copyright (C) 2018, 2019 Tobias Deiminger * Copyright (C) 2018 Carlos Garcia Campos * 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) 2021 Mahmoud Ahmed Khalil * Adapting code from * Copyright (C) 2004 by Enrico Ros * * 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. */ // qt/kde includes #include #include #include #include // local includes #include "poppler-annotation.h" #include "poppler-link.h" #include "poppler-qt6.h" #include "poppler-annotation-helper.h" #include "poppler-annotation-private.h" #include "poppler-page-private.h" #include "poppler-private.h" // poppler includes #include #include #include #include #include #include #include /* Almost all getters directly query the underlying poppler annotation, with * the exceptions of link, file attachment, sound, movie and screen annotations, * Whose data retrieval logic has not been moved yet. Their getters return * static data set at creation time by findAnnotations */ namespace Poppler { // BEGIN AnnotationAppearancePrivate implementation AnnotationAppearancePrivate::AnnotationAppearancePrivate(Annot *annot) { if (annot) { appearance = annot->getAppearance(); } else { appearance.setToNull(); } } // END AnnotationAppearancePrivate implementation // BEGIN AnnotationAppearance implementation AnnotationAppearance::AnnotationAppearance(AnnotationAppearancePrivate *annotationAppearancePrivate) : d(annotationAppearancePrivate) { } AnnotationAppearance::~AnnotationAppearance() { delete d; } // END AnnotationAppearance implementation // BEGIN Annotation implementation AnnotationPrivate::AnnotationPrivate() : revisionScope(Annotation::Root), revisionType(Annotation::None), pdfAnnot(nullptr), pdfPage(nullptr), parentDoc(nullptr) { } void getRawDataFromQImage(const QImage &qimg, int bitsPerPixel, QByteArray *data, QByteArray *sMaskData) { const int height = qimg.height(); const int width = qimg.width(); switch (bitsPerPixel) { case 1: for (int line = 0; line < height; line++) { const char *lineData = reinterpret_cast(qimg.scanLine(line)); for (int offset = 0; offset < (width + 7) / 8; offset++) { data->append(lineData[offset]); } } break; case 8: case 24: data->append((const char *)qimg.bits(), static_cast(qimg.sizeInBytes())); break; case 32: for (int line = 0; line < height; line++) { const QRgb *lineData = reinterpret_cast(qimg.scanLine(line)); for (int offset = 0; offset < width; offset++) { char a = (char)qAlpha(lineData[offset]); char r = (char)qRed(lineData[offset]); char g = (char)qGreen(lineData[offset]); char b = (char)qBlue(lineData[offset]); data->append(r); data->append(g); data->append(b); sMaskData->append(a); } } break; } } void AnnotationPrivate::addRevision(Annotation *ann, Annotation::RevScope scope, Annotation::RevType type) { /* Since ownership stays with the caller, create an alias of ann */ revisions.append(ann->d_ptr->makeAlias()); /* Set revision properties */ revisionScope = scope; revisionType = type; } AnnotationPrivate::~AnnotationPrivate() { // Delete all children revisions qDeleteAll(revisions); // Release Annot object if (pdfAnnot) { pdfAnnot->decRefCnt(); } } void AnnotationPrivate::tieToNativeAnnot(Annot *ann, ::Page *page, Poppler::DocumentData *doc) { if (pdfAnnot) { error(errIO, -1, "Annotation is already tied"); return; } pdfAnnot = ann; pdfPage = page; parentDoc = doc; pdfAnnot->incRefCnt(); } /* This method is called when a new annotation is created, after pdfAnnot and * pdfPage have been set */ void AnnotationPrivate::flushBaseAnnotationProperties() { Q_ASSERT(pdfPage); Annotation *q = makeAlias(); // Setters are defined in the public class // Since pdfAnnot has been set, this calls will write in the Annot object q->setAuthor(author); q->setContents(contents); q->setUniqueName(uniqueName); q->setModificationDate(modDate); q->setCreationDate(creationDate); q->setFlags(flags); // q->setBoundary(boundary); -- already set by subclass-specific code q->setStyle(style); q->setPopup(popup); // Flush revisions foreach (Annotation *r, revisions) { // TODO: Flush revision delete r; // Object is no longer needed } delete q; // Clear some members to save memory author.clear(); contents.clear(); uniqueName.clear(); revisions.clear(); } // Returns matrix to convert from user space coords (oriented according to the // specified rotation) to normalized coords static void fillNormalizationMTX(::Page *pdfPage, double MTX[6], int pageRotation) { Q_ASSERT(pdfPage); // build a normalized transform matrix for this page at 100% scale GfxState *gfxState = new GfxState(72.0, 72.0, pdfPage->getCropBox(), pageRotation, true); const double *gfxCTM = gfxState->getCTM(); double w = pdfPage->getCropWidth(); double h = pdfPage->getCropHeight(); // Swap width and height if the page is rotated landscape or seascape if (pageRotation == 90 || pageRotation == 270) { double t = w; w = h; h = t; } for (int i = 0; i < 6; i += 2) { MTX[i] = gfxCTM[i] / w; MTX[i + 1] = gfxCTM[i + 1] / h; } delete gfxState; } // Returns matrix to convert from user space coords (i.e. those that are stored // in the PDF file) to normalized coords (i.e. those that we expose to clients). // This method also applies a rotation around the top-left corner if the // FixedRotation flag is set. void AnnotationPrivate::fillTransformationMTX(double MTX[6]) const { Q_ASSERT(pdfPage); Q_ASSERT(pdfAnnot); const int pageRotate = pdfPage->getRotate(); if (pageRotate == 0 || (pdfAnnot->getFlags() & Annot::flagNoRotate) == 0) { // Use the normalization matrix for this page's rotation fillNormalizationMTX(pdfPage, MTX, pageRotate); } else { // Clients expect coordinates relative to this page's rotation, but // FixedRotation annotations internally use unrotated coordinates: // construct matrix to both normalize and rotate coordinates using the // top-left corner as rotation pivot double MTXnorm[6]; fillNormalizationMTX(pdfPage, MTXnorm, pageRotate); QTransform transform(MTXnorm[0], MTXnorm[1], MTXnorm[2], MTXnorm[3], MTXnorm[4], MTXnorm[5]); transform.translate(+pdfAnnot->getXMin(), +pdfAnnot->getYMax()); transform.rotate(pageRotate); transform.translate(-pdfAnnot->getXMin(), -pdfAnnot->getYMax()); MTX[0] = transform.m11(); MTX[1] = transform.m12(); MTX[2] = transform.m21(); MTX[3] = transform.m22(); MTX[4] = transform.dx(); MTX[5] = transform.dy(); } } QRectF AnnotationPrivate::fromPdfRectangle(const PDFRectangle &r) const { double swp, MTX[6]; fillTransformationMTX(MTX); QPointF p1, p2; XPDFReader::transform(MTX, r.x1, r.y1, p1); XPDFReader::transform(MTX, r.x2, r.y2, p2); double tl_x = p1.x(); double tl_y = p1.y(); double br_x = p2.x(); double br_y = p2.y(); if (tl_x > br_x) { swp = tl_x; tl_x = br_x; br_x = swp; } if (tl_y > br_y) { swp = tl_y; tl_y = br_y; br_y = swp; } return QRectF(QPointF(tl_x, tl_y), QPointF(br_x, br_y)); } // This function converts a boundary QRectF in normalized coords to a // PDFRectangle in user coords. If the FixedRotation flag is set, this function // also applies a rotation around the top-left corner: it's the inverse of // the transformation produced by fillTransformationMTX, but we can't use // fillTransformationMTX here because it relies on the native annotation // object's boundary rect to be already set up. PDFRectangle boundaryToPdfRectangle(::Page *pdfPage, const QRectF &r, int rFlags) { Q_ASSERT(pdfPage); const int pageRotate = pdfPage->getRotate(); double MTX[6]; fillNormalizationMTX(pdfPage, MTX, pageRotate); double tl_x, tl_y, br_x, br_y, swp; XPDFReader::invTransform(MTX, r.topLeft(), tl_x, tl_y); XPDFReader::invTransform(MTX, r.bottomRight(), br_x, br_y); if (tl_x > br_x) { swp = tl_x; tl_x = br_x; br_x = swp; } if (tl_y > br_y) { swp = tl_y; tl_y = br_y; br_y = swp; } const int rotationFixUp = (rFlags & Annotation::FixedRotation) ? pageRotate : 0; const double width = br_x - tl_x; const double height = br_y - tl_y; if (rotationFixUp == 0) { return PDFRectangle(tl_x, tl_y, br_x, br_y); } else if (rotationFixUp == 90) { return PDFRectangle(tl_x, tl_y - width, tl_x + height, tl_y); } else if (rotationFixUp == 180) { return PDFRectangle(br_x, tl_y - height, br_x + width, tl_y); } else { // rotationFixUp == 270 return PDFRectangle(br_x, br_y - width, br_x + height, br_y); } } PDFRectangle AnnotationPrivate::boundaryToPdfRectangle(const QRectF &r, int rFlags) const { return Poppler::boundaryToPdfRectangle(pdfPage, r, rFlags); } AnnotPath *AnnotationPrivate::toAnnotPath(const QVector &list) const { const int count = list.size(); std::vector ac; ac.reserve(count); double MTX[6]; fillTransformationMTX(MTX); foreach (const QPointF &p, list) { double x, y; XPDFReader::invTransform(MTX, p, x, y); ac.emplace_back(x, y); } return new AnnotPath(std::move(ac)); } std::vector> AnnotationPrivate::findAnnotations(::Page *pdfPage, DocumentData *doc, const QSet &subtypes, int parentID) { Annots *annots = pdfPage->getAnnots(); const bool wantTextAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AText); const bool wantLineAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::ALine); const bool wantGeomAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AGeom); const bool wantHighlightAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AHighlight); const bool wantStampAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AStamp); const bool wantInkAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AInk); const bool wantLinkAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::ALink); const bool wantCaretAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::ACaret); const bool wantFileAttachmentAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AFileAttachment); const bool wantSoundAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::ASound); const bool wantMovieAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AMovie); const bool wantScreenAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AScreen); const bool wantWidgetAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AWidget); // Create Annotation objects and tie to their native Annot std::vector> res; for (Annot *ann : annots->getAnnots()) { if (!ann) { error(errInternal, -1, "Annot is null"); continue; } // Check parent annotation AnnotMarkup *markupann = dynamic_cast(ann); if (!markupann) { // Assume it's a root annotation, and skip if user didn't request it if (parentID != -1) { continue; } } else if (markupann->getInReplyToID() != parentID) { continue; } /* Create Annotation of the right subclass */ std::unique_ptr annotation; Annot::AnnotSubtype subType = ann->getType(); switch (subType) { case Annot::typeText: if (!wantTextAnnotations) { continue; } annotation = std::make_unique(TextAnnotation::Linked); break; case Annot::typeFreeText: if (!wantTextAnnotations) { continue; } annotation = std::make_unique(TextAnnotation::InPlace); break; case Annot::typeLine: if (!wantLineAnnotations) { continue; } annotation = std::make_unique(LineAnnotation::StraightLine); break; case Annot::typePolygon: case Annot::typePolyLine: if (!wantLineAnnotations) { continue; } annotation = std::make_unique(LineAnnotation::Polyline); break; case Annot::typeSquare: case Annot::typeCircle: if (!wantGeomAnnotations) { continue; } annotation = std::make_unique(); break; case Annot::typeHighlight: case Annot::typeUnderline: case Annot::typeSquiggly: case Annot::typeStrikeOut: if (!wantHighlightAnnotations) { continue; } annotation = std::make_unique(); break; case Annot::typeStamp: if (!wantStampAnnotations) { continue; } annotation = std::make_unique(); break; case Annot::typeInk: if (!wantInkAnnotations) { continue; } annotation = std::make_unique(); break; case Annot::typeLink: /* TODO: Move logic to getters */ { if (!wantLinkAnnotations) { continue; } // parse Link params AnnotLink *linkann = static_cast(ann); LinkAnnotation *l = new LinkAnnotation(); // -> hlMode l->setLinkHighlightMode((LinkAnnotation::HighlightMode)linkann->getLinkEffect()); // -> link region // TODO // reading link action if (linkann->getAction()) { std::unique_ptr popplerLink = PageData::convertLinkActionToLink(linkann->getAction(), doc, QRectF()); if (popplerLink) { l->setLinkDestination(std::move(popplerLink)); } } annotation.reset(l); break; } case Annot::typeCaret: if (!wantCaretAnnotations) { continue; } annotation = std::make_unique(); break; case Annot::typeFileAttachment: /* TODO: Move logic to getters */ { if (!wantFileAttachmentAnnotations) { continue; } AnnotFileAttachment *attachann = static_cast(ann); FileAttachmentAnnotation *f = new FileAttachmentAnnotation(); // -> fileIcon f->setFileIconName(QString::fromLatin1(attachann->getName()->c_str())); // -> embeddedFile auto filespec = std::make_unique(attachann->getFile()); f->setEmbeddedFile(new EmbeddedFile(*new EmbeddedFileData(std::move(filespec)))); annotation.reset(f); break; } case Annot::typeSound: /* TODO: Move logic to getters */ { if (!wantSoundAnnotations) { continue; } AnnotSound *soundann = static_cast(ann); SoundAnnotation *s = new SoundAnnotation(); // -> soundIcon s->setSoundIconName(QString::fromLatin1(soundann->getName()->c_str())); // -> sound s->setSound(new SoundObject(soundann->getSound())); annotation.reset(s); break; } case Annot::typeMovie: /* TODO: Move logic to getters */ { if (!wantMovieAnnotations) { continue; } AnnotMovie *movieann = static_cast(ann); MovieAnnotation *m = new MovieAnnotation(); // -> movie MovieObject *movie = new MovieObject(movieann); m->setMovie(movie); // -> movieTitle const GooString *movietitle = movieann->getTitle(); if (movietitle) { m->setMovieTitle(QString::fromLatin1(movietitle->c_str())); } annotation.reset(m); break; } case Annot::typeScreen: { if (!wantScreenAnnotations) { continue; } AnnotScreen *screenann = static_cast(ann); // TODO Support other link types than Link::Rendition in ScreenAnnotation if (!screenann->getAction() || screenann->getAction()->getKind() != actionRendition) { continue; } ScreenAnnotation *s = new ScreenAnnotation(); // -> screen std::unique_ptr popplerLink = PageData::convertLinkActionToLink(screenann->getAction(), doc, QRectF()); s->setAction(static_cast(popplerLink.release())); // -> screenTitle const GooString *screentitle = screenann->getTitle(); if (screentitle) { s->setScreenTitle(UnicodeParsedString(screentitle)); } annotation.reset(s); break; } case Annot::typePopup: continue; // popups are parsed by Annotation's window() getter case Annot::typeUnknown: continue; // special case for ignoring unknown annotations case Annot::typeWidget: if (!wantWidgetAnnotations) { continue; } annotation.reset(new WidgetAnnotation()); break; case Annot::typeRichMedia: { const AnnotRichMedia *annotRichMedia = static_cast(ann); RichMediaAnnotation *richMediaAnnotation = new RichMediaAnnotation; const AnnotRichMedia::Settings *annotSettings = annotRichMedia->getSettings(); if (annotSettings) { RichMediaAnnotation::Settings *settings = new RichMediaAnnotation::Settings; if (annotSettings->getActivation()) { RichMediaAnnotation::Activation *activation = new RichMediaAnnotation::Activation; switch (annotSettings->getActivation()->getCondition()) { case AnnotRichMedia::Activation::conditionPageOpened: activation->setCondition(RichMediaAnnotation::Activation::PageOpened); break; case AnnotRichMedia::Activation::conditionPageVisible: activation->setCondition(RichMediaAnnotation::Activation::PageVisible); break; case AnnotRichMedia::Activation::conditionUserAction: activation->setCondition(RichMediaAnnotation::Activation::UserAction); break; } settings->setActivation(activation); } if (annotSettings->getDeactivation()) { RichMediaAnnotation::Deactivation *deactivation = new RichMediaAnnotation::Deactivation; switch (annotSettings->getDeactivation()->getCondition()) { case AnnotRichMedia::Deactivation::conditionPageClosed: deactivation->setCondition(RichMediaAnnotation::Deactivation::PageClosed); break; case AnnotRichMedia::Deactivation::conditionPageInvisible: deactivation->setCondition(RichMediaAnnotation::Deactivation::PageInvisible); break; case AnnotRichMedia::Deactivation::conditionUserAction: deactivation->setCondition(RichMediaAnnotation::Deactivation::UserAction); break; } settings->setDeactivation(deactivation); } richMediaAnnotation->setSettings(settings); } const AnnotRichMedia::Content *annotContent = annotRichMedia->getContent(); if (annotContent) { RichMediaAnnotation::Content *content = new RichMediaAnnotation::Content; const int configurationsCount = annotContent->getConfigurationsCount(); if (configurationsCount > 0) { QList configurations; for (int i = 0; i < configurationsCount; ++i) { const AnnotRichMedia::Configuration *annotConfiguration = annotContent->getConfiguration(i); if (!annotConfiguration) { continue; } RichMediaAnnotation::Configuration *configuration = new RichMediaAnnotation::Configuration; if (annotConfiguration->getName()) { configuration->setName(UnicodeParsedString(annotConfiguration->getName())); } switch (annotConfiguration->getType()) { case AnnotRichMedia::Configuration::type3D: configuration->setType(RichMediaAnnotation::Configuration::Type3D); break; case AnnotRichMedia::Configuration::typeFlash: configuration->setType(RichMediaAnnotation::Configuration::TypeFlash); break; case AnnotRichMedia::Configuration::typeSound: configuration->setType(RichMediaAnnotation::Configuration::TypeSound); break; case AnnotRichMedia::Configuration::typeVideo: configuration->setType(RichMediaAnnotation::Configuration::TypeVideo); break; } const int instancesCount = annotConfiguration->getInstancesCount(); if (instancesCount > 0) { QList instances; for (int j = 0; j < instancesCount; ++j) { const AnnotRichMedia::Instance *annotInstance = annotConfiguration->getInstance(j); if (!annotInstance) { continue; } RichMediaAnnotation::Instance *instance = new RichMediaAnnotation::Instance; switch (annotInstance->getType()) { case AnnotRichMedia::Instance::type3D: instance->setType(RichMediaAnnotation::Instance::Type3D); break; case AnnotRichMedia::Instance::typeFlash: instance->setType(RichMediaAnnotation::Instance::TypeFlash); break; case AnnotRichMedia::Instance::typeSound: instance->setType(RichMediaAnnotation::Instance::TypeSound); break; case AnnotRichMedia::Instance::typeVideo: instance->setType(RichMediaAnnotation::Instance::TypeVideo); break; } const AnnotRichMedia::Params *annotParams = annotInstance->getParams(); if (annotParams) { RichMediaAnnotation::Params *params = new RichMediaAnnotation::Params; if (annotParams->getFlashVars()) { params->setFlashVars(UnicodeParsedString(annotParams->getFlashVars())); } instance->setParams(params); } instances.append(instance); } configuration->setInstances(instances); } configurations.append(configuration); } content->setConfigurations(configurations); } const int assetsCount = annotContent->getAssetsCount(); if (assetsCount > 0) { QList assets; for (int i = 0; i < assetsCount; ++i) { const AnnotRichMedia::Asset *annotAsset = annotContent->getAsset(i); if (!annotAsset) { continue; } RichMediaAnnotation::Asset *asset = new RichMediaAnnotation::Asset; if (annotAsset->getName()) { asset->setName(UnicodeParsedString(annotAsset->getName())); } auto fileSpec = std::make_unique(annotAsset->getFileSpec()); asset->setEmbeddedFile(new EmbeddedFile(*new EmbeddedFileData(std::move(fileSpec)))); assets.append(asset); } content->setAssets(assets); } richMediaAnnotation->setContent(content); } annotation.reset(richMediaAnnotation); break; } default: { #define CASE_FOR_TYPE(thetype) \ case Annot::type##thetype: \ error(errUnimplemented, -1, "Annotation " #thetype " not supported"); \ break; switch (subType) { CASE_FOR_TYPE(PrinterMark) CASE_FOR_TYPE(TrapNet) CASE_FOR_TYPE(Watermark) CASE_FOR_TYPE(3D) default: error(errUnimplemented, -1, "Annotation {0:d} not supported", subType); } continue; #undef CASE_FOR_TYPE } } annotation->d_ptr->tieToNativeAnnot(ann, pdfPage, doc); res.push_back(std::move(annotation)); } return res; } Ref AnnotationPrivate::pdfObjectReference() const { if (pdfAnnot == nullptr) { return Ref::INVALID(); } return pdfAnnot->getRef(); } std::unique_ptr AnnotationPrivate::additionalAction(Annotation::AdditionalActionType type) const { if (pdfAnnot->getType() != Annot::typeScreen && pdfAnnot->getType() != Annot::typeWidget) { return {}; } const Annot::AdditionalActionsType actionType = toPopplerAdditionalActionType(type); std::unique_ptr<::LinkAction> linkAction; if (pdfAnnot->getType() == Annot::typeScreen) { linkAction = static_cast(pdfAnnot)->getAdditionalAction(actionType); } else { linkAction = static_cast(pdfAnnot)->getAdditionalAction(actionType); } if (linkAction) { return PageData::convertLinkActionToLink(linkAction.get(), parentDoc, QRectF()); } return {}; } void AnnotationPrivate::addAnnotationToPage(::Page *pdfPage, DocumentData *doc, const Annotation *ann) { if (ann->d_ptr->pdfAnnot != nullptr) { error(errIO, -1, "Annotation is already tied"); return; } // Unimplemented annotations can't be created by the user because their ctor // is private. Therefore, createNativeAnnot will never return 0 Annot *nativeAnnot = ann->d_ptr->createNativeAnnot(pdfPage, doc); Q_ASSERT(nativeAnnot); if (ann->d_ptr->annotationAppearance.isStream()) { nativeAnnot->setNewAppearance(ann->d_ptr->annotationAppearance.copy()); } pdfPage->addAnnot(nativeAnnot); } void AnnotationPrivate::removeAnnotationFromPage(::Page *pdfPage, const Annotation *ann) { if (ann->d_ptr->pdfAnnot == nullptr) { error(errIO, -1, "Annotation is not tied"); return; } if (ann->d_ptr->pdfPage != pdfPage) { error(errIO, -1, "Annotation doesn't belong to the specified page"); return; } // Remove annotation pdfPage->removeAnnot(ann->d_ptr->pdfAnnot); // Destroy object delete ann; } class TextAnnotationPrivate : public AnnotationPrivate { public: TextAnnotationPrivate(); Annotation *makeAlias() override; Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; void setDefaultAppearanceToNative(); std::unique_ptr getDefaultAppearanceFromNative() const; // data fields TextAnnotation::TextType textType; QString textIcon; std::optional textFont; QColor textColor = Qt::black; TextAnnotation::InplaceAlignPosition inplaceAlign; QVector inplaceCallout; TextAnnotation::InplaceIntent inplaceIntent; }; class Annotation::Style::Private : public QSharedData { public: Private() : opacity(1.0), width(1.0), lineStyle(Solid), xCorners(0.0), yCorners(0.0), lineEffect(NoEffect), effectIntensity(1.0) { dashArray.resize(1); dashArray[0] = 3; } QColor color; double opacity; double width; Annotation::LineStyle lineStyle; double xCorners; double yCorners; QVector dashArray; Annotation::LineEffect lineEffect; double effectIntensity; }; Annotation::Style::Style() : d(new Private) { } Annotation::Style::Style(const Style &other) : d(other.d) { } Annotation::Style &Annotation::Style::operator=(const Style &other) { if (this != &other) { d = other.d; } return *this; } Annotation::Style::~Style() { } QColor Annotation::Style::color() const { return d->color; } void Annotation::Style::setColor(const QColor &color) { d->color = color; } double Annotation::Style::opacity() const { return d->opacity; } void Annotation::Style::setOpacity(double opacity) { d->opacity = opacity; } double Annotation::Style::width() const { return d->width; } void Annotation::Style::setWidth(double width) { d->width = width; } Annotation::LineStyle Annotation::Style::lineStyle() const { return d->lineStyle; } void Annotation::Style::setLineStyle(Annotation::LineStyle style) { d->lineStyle = style; } double Annotation::Style::xCorners() const { return d->xCorners; } void Annotation::Style::setXCorners(double radius) { d->xCorners = radius; } double Annotation::Style::yCorners() const { return d->yCorners; } void Annotation::Style::setYCorners(double radius) { d->yCorners = radius; } const QVector &Annotation::Style::dashArray() const { return d->dashArray; } void Annotation::Style::setDashArray(const QVector &array) { d->dashArray = array; } Annotation::LineEffect Annotation::Style::lineEffect() const { return d->lineEffect; } void Annotation::Style::setLineEffect(Annotation::LineEffect effect) { d->lineEffect = effect; } double Annotation::Style::effectIntensity() const { return d->effectIntensity; } void Annotation::Style::setEffectIntensity(double intens) { d->effectIntensity = intens; } class Annotation::Popup::Private : public QSharedData { public: Private() : flags(-1) { } int flags; QRectF geometry; QString title; QString summary; QString text; }; Annotation::Popup::Popup() : d(new Private) { } Annotation::Popup::Popup(const Popup &other) : d(other.d) { } Annotation::Popup &Annotation::Popup::operator=(const Popup &other) { if (this != &other) { d = other.d; } return *this; } Annotation::Popup::~Popup() { } int Annotation::Popup::flags() const { return d->flags; } void Annotation::Popup::setFlags(int flags) { d->flags = flags; } QRectF Annotation::Popup::geometry() const { return d->geometry; } void Annotation::Popup::setGeometry(const QRectF &geom) { d->geometry = geom; } QString Annotation::Popup::title() const { return d->title; } void Annotation::Popup::setTitle(const QString &title) { d->title = title; } QString Annotation::Popup::summary() const { return d->summary; } void Annotation::Popup::setSummary(const QString &summary) { d->summary = summary; } QString Annotation::Popup::text() const { return d->text; } void Annotation::Popup::setText(const QString &text) { d->text = text; } Annotation::Annotation(AnnotationPrivate &dd) : d_ptr(&dd) { } Annotation::~Annotation() { } QString Annotation::author() const { Q_D(const Annotation); if (!d->pdfAnnot) { return d->author; } const AnnotMarkup *markupann = dynamic_cast(d->pdfAnnot); return markupann ? UnicodeParsedString(markupann->getLabel()) : QString(); } void Annotation::setAuthor(const QString &author) { Q_D(Annotation); if (!d->pdfAnnot) { d->author = author; return; } AnnotMarkup *markupann = dynamic_cast(d->pdfAnnot); if (markupann) { markupann->setLabel(std::unique_ptr(QStringToUnicodeGooString(author))); } } QString Annotation::contents() const { Q_D(const Annotation); if (!d->pdfAnnot) { return d->contents; } return UnicodeParsedString(d->pdfAnnot->getContents()); } void Annotation::setContents(const QString &contents) { Q_D(Annotation); if (!d->pdfAnnot) { d->contents = contents; return; } d->pdfAnnot->setContents(std::unique_ptr(QStringToUnicodeGooString(contents))); TextAnnotationPrivate *textAnnotD = dynamic_cast(d); if (textAnnotD) { textAnnotD->setDefaultAppearanceToNative(); } } QString Annotation::uniqueName() const { Q_D(const Annotation); if (!d->pdfAnnot) { return d->uniqueName; } return UnicodeParsedString(d->pdfAnnot->getName()); } void Annotation::setUniqueName(const QString &uniqueName) { Q_D(Annotation); if (!d->pdfAnnot) { d->uniqueName = uniqueName; return; } QByteArray ascii = uniqueName.toLatin1(); GooString s(ascii.constData()); d->pdfAnnot->setName(&s); } QDateTime Annotation::modificationDate() const { Q_D(const Annotation); if (!d->pdfAnnot) { return d->modDate; } if (d->pdfAnnot->getModified()) { return convertDate(d->pdfAnnot->getModified()->c_str()); } else { return QDateTime(); } } void Annotation::setModificationDate(const QDateTime &date) { Q_D(Annotation); if (!d->pdfAnnot) { d->modDate = date; return; } if (d->pdfAnnot) { if (date.isValid()) { const time_t t = date.toSecsSinceEpoch(); GooString *s = timeToDateString(&t); d->pdfAnnot->setModified(s); delete s; } else { d->pdfAnnot->setModified(nullptr); } } } QDateTime Annotation::creationDate() const { Q_D(const Annotation); if (!d->pdfAnnot) { return d->creationDate; } const AnnotMarkup *markupann = dynamic_cast(d->pdfAnnot); if (markupann && markupann->getDate()) { return convertDate(markupann->getDate()->c_str()); } return modificationDate(); } void Annotation::setCreationDate(const QDateTime &date) { Q_D(Annotation); if (!d->pdfAnnot) { d->creationDate = date; return; } AnnotMarkup *markupann = dynamic_cast(d->pdfAnnot); if (markupann) { if (date.isValid()) { const time_t t = date.toSecsSinceEpoch(); GooString *s = timeToDateString(&t); markupann->setDate(s); delete s; } else { markupann->setDate(nullptr); } } } static Annotation::Flags fromPdfFlags(int flags) { Annotation::Flags qtflags; if (flags & Annot::flagHidden) { qtflags |= Annotation::Hidden; } if (flags & Annot::flagNoZoom) { qtflags |= Annotation::FixedSize; } if (flags & Annot::flagNoRotate) { qtflags |= Annotation::FixedRotation; } if (!(flags & Annot::flagPrint)) { qtflags |= Annotation::DenyPrint; } if (flags & Annot::flagReadOnly) { qtflags |= Annotation::DenyWrite; qtflags |= Annotation::DenyDelete; } if (flags & Annot::flagLocked) { qtflags |= Annotation::DenyDelete; } if (flags & Annot::flagToggleNoView) { qtflags |= Annotation::ToggleHidingOnMouse; } return qtflags; } static int toPdfFlags(Annotation::Flags qtflags) { int flags = 0; if (qtflags & Annotation::Hidden) { flags |= Annot::flagHidden; } if (qtflags & Annotation::FixedSize) { flags |= Annot::flagNoZoom; } if (qtflags & Annotation::FixedRotation) { flags |= Annot::flagNoRotate; } if (!(qtflags & Annotation::DenyPrint)) { flags |= Annot::flagPrint; } if (qtflags & Annotation::DenyWrite) { flags |= Annot::flagReadOnly; } if (qtflags & Annotation::DenyDelete) { flags |= Annot::flagLocked; } if (qtflags & Annotation::ToggleHidingOnMouse) { flags |= Annot::flagToggleNoView; } return flags; } Annotation::Flags Annotation::flags() const { Q_D(const Annotation); if (!d->pdfAnnot) { return d->flags; } return fromPdfFlags(d->pdfAnnot->getFlags()); } void Annotation::setFlags(Annotation::Flags flags) { Q_D(Annotation); if (!d->pdfAnnot) { d->flags = flags; return; } d->pdfAnnot->setFlags(toPdfFlags(flags)); } QRectF Annotation::boundary() const { Q_D(const Annotation); if (!d->pdfAnnot) { return d->boundary; } const PDFRectangle &rect = d->pdfAnnot->getRect(); return d->fromPdfRectangle(rect); } void Annotation::setBoundary(const QRectF &boundary) { Q_D(Annotation); if (!d->pdfAnnot) { d->boundary = boundary; return; } const PDFRectangle rect = d->boundaryToPdfRectangle(boundary, flags()); if (rect == d->pdfAnnot->getRect()) { return; } d->pdfAnnot->setRect(&rect); } Annotation::Style Annotation::style() const { Q_D(const Annotation); if (!d->pdfAnnot) { return d->style; } Style s; s.setColor(convertAnnotColor(d->pdfAnnot->getColor())); const AnnotMarkup *markupann = dynamic_cast(d->pdfAnnot); if (markupann) { s.setOpacity(markupann->getOpacity()); } const AnnotBorder *border = d->pdfAnnot->getBorder(); if (border) { if (border->getType() == AnnotBorder::typeArray) { const AnnotBorderArray *border_array = static_cast(border); s.setXCorners(border_array->getHorizontalCorner()); s.setYCorners(border_array->getVerticalCorner()); } s.setWidth(border->getWidth()); s.setLineStyle((Annotation::LineStyle)(1 << border->getStyle())); const std::vector &dashArray = border->getDash(); s.setDashArray(QVector(dashArray.begin(), dashArray.end())); } AnnotBorderEffect *border_effect; switch (d->pdfAnnot->getType()) { case Annot::typeFreeText: border_effect = static_cast(d->pdfAnnot)->getBorderEffect(); break; case Annot::typeSquare: case Annot::typeCircle: border_effect = static_cast(d->pdfAnnot)->getBorderEffect(); break; default: border_effect = nullptr; } if (border_effect) { s.setLineEffect((Annotation::LineEffect)border_effect->getEffectType()); s.setEffectIntensity(border_effect->getIntensity()); } return s; } void Annotation::setStyle(const Annotation::Style &style) { Q_D(Annotation); if (!d->pdfAnnot) { d->style = style; return; } d->pdfAnnot->setColor(convertQColor(style.color())); AnnotMarkup *markupann = dynamic_cast(d->pdfAnnot); if (markupann) { markupann->setOpacity(style.opacity()); } auto border = std::make_unique(); border->setWidth(style.width()); border->setHorizontalCorner(style.xCorners()); border->setVerticalCorner(style.yCorners()); d->pdfAnnot->setBorder(std::move(border)); } Annotation::Popup Annotation::popup() const { Q_D(const Annotation); if (!d->pdfAnnot) { return d->popup; } Popup w; AnnotPopup *popup = nullptr; int flags = -1; // Not initialized const AnnotMarkup *markupann = dynamic_cast(d->pdfAnnot); if (markupann) { popup = markupann->getPopup(); w.setSummary(UnicodeParsedString(markupann->getSubject())); } if (popup) { flags = fromPdfFlags(popup->getFlags()) & (Annotation::Hidden | Annotation::FixedSize | Annotation::FixedRotation); if (!popup->getOpen()) { flags |= Annotation::Hidden; } const PDFRectangle &rect = popup->getRect(); w.setGeometry(d->fromPdfRectangle(rect)); } if (d->pdfAnnot->getType() == Annot::typeText) { const AnnotText *textann = static_cast(d->pdfAnnot); // Text annotations default to same rect as annotation if (flags == -1) { flags = 0; w.setGeometry(boundary()); } // If text is not 'opened', force window hiding. if the window // was parsed from popup, the flag should already be set if (!textann->getOpen() && flags != -1) { flags |= Annotation::Hidden; } } w.setFlags(flags); return w; } void Annotation::setPopup(const Annotation::Popup &popup) { Q_D(Annotation); if (!d->pdfAnnot) { d->popup = popup; return; } #if 0 /* TODO: Remove old popup and add AnnotPopup to page */ AnnotMarkup *markupann = dynamic_cast(d->pdfAnnot); if (!markupann) return; // Create a new AnnotPopup and assign it to pdfAnnot PDFRectangle rect = d->toPdfRectangle( popup.geometry() ); AnnotPopup * p = new AnnotPopup( d->pdfPage->getDoc(), &rect ); p->setOpen( !(popup.flags() & Annotation::Hidden) ); if (!popup.summary().isEmpty()) { GooString *s = QStringToUnicodeGooString(popup.summary()); markupann->setLabel(s); delete s; } markupann->setPopup(p); #endif } Annotation::RevScope Annotation::revisionScope() const { Q_D(const Annotation); if (!d->pdfAnnot) { return d->revisionScope; } const AnnotMarkup *markupann = dynamic_cast(d->pdfAnnot); if (markupann && markupann->isInReplyTo()) { switch (markupann->getReplyTo()) { case AnnotMarkup::replyTypeR: return Annotation::Reply; case AnnotMarkup::replyTypeGroup: return Annotation::Group; } } return Annotation::Root; // It's not a revision } Annotation::RevType Annotation::revisionType() const { Q_D(const Annotation); if (!d->pdfAnnot) { return d->revisionType; } const AnnotText *textann = dynamic_cast(d->pdfAnnot); if (textann && textann->isInReplyTo()) { switch (textann->getState()) { case AnnotText::stateMarked: return Annotation::Marked; case AnnotText::stateUnmarked: return Annotation::Unmarked; case AnnotText::stateAccepted: return Annotation::Accepted; case AnnotText::stateRejected: return Annotation::Rejected; case AnnotText::stateCancelled: return Annotation::Cancelled; case AnnotText::stateCompleted: return Annotation::Completed; default: break; } } return Annotation::None; } std::vector> Annotation::revisions() const { Q_D(const Annotation); if (!d->pdfAnnot) { /* Return aliases, whose ownership goes to the caller */ std::vector> res; foreach (Annotation *rev, d->revisions) res.push_back(std::unique_ptr(rev->d_ptr->makeAlias())); return res; } /* If the annotation doesn't live in a object on its own (eg bug51361), it * has no ref, therefore it can't have revisions */ if (!d->pdfAnnot->getHasRef()) { return std::vector>(); } return AnnotationPrivate::findAnnotations(d->pdfPage, d->parentDoc, QSet(), d->pdfAnnot->getId()); } std::unique_ptr Annotation::annotationAppearance() const { Q_D(const Annotation); return std::make_unique(new AnnotationAppearancePrivate(d->pdfAnnot)); } void Annotation::setAnnotationAppearance(const AnnotationAppearance &annotationAppearance) { Q_D(Annotation); if (!d->pdfAnnot) { d->annotationAppearance = annotationAppearance.d->appearance.copy(); return; } // Moving the appearance object using std::move would result // in the object being completed moved from the AnnotationAppearancePrivate // class. So, we'll not be able to retrieve the stamp's original AP stream d->pdfAnnot->setNewAppearance(annotationAppearance.d->appearance.copy()); } // END Annotation implementation /** TextAnnotation [Annotation] */ TextAnnotationPrivate::TextAnnotationPrivate() : AnnotationPrivate(), textType(TextAnnotation::Linked), textIcon(QStringLiteral("Note")), inplaceAlign(TextAnnotation::InplaceAlignLeft), inplaceIntent(TextAnnotation::Unknown) { } Annotation *TextAnnotationPrivate::makeAlias() { return new TextAnnotation(*this); } Annot *TextAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) { // Setters are defined in the public class TextAnnotation *q = static_cast(makeAlias()); // Set page and contents pdfPage = destPage; parentDoc = doc; // Set pdfAnnot PDFRectangle rect = boundaryToPdfRectangle(boundary, flags); if (textType == TextAnnotation::Linked) { pdfAnnot = new AnnotText { destPage->getDoc(), &rect }; } else { const double pointSize = textFont ? textFont->pointSizeF() : AnnotFreeText::undefinedFontPtSize; if (pointSize < 0) { qWarning() << "TextAnnotationPrivate::createNativeAnnot: font pointSize < 0"; } pdfAnnot = new AnnotFreeText { destPage->getDoc(), &rect }; } // Set properties flushBaseAnnotationProperties(); q->setTextIcon(textIcon); q->setInplaceAlign(inplaceAlign); q->setCalloutPoints(inplaceCallout); q->setInplaceIntent(inplaceIntent); delete q; inplaceCallout.clear(); // Free up memory setDefaultAppearanceToNative(); return pdfAnnot; } void TextAnnotationPrivate::setDefaultAppearanceToNative() { if (pdfAnnot && pdfAnnot->getType() == Annot::typeFreeText) { AnnotFreeText *ftextann = static_cast(pdfAnnot); const double pointSize = textFont ? textFont->pointSizeF() : AnnotFreeText::undefinedFontPtSize; if (pointSize < 0) { qWarning() << "TextAnnotationPrivate::createNativeAnnot: font pointSize < 0"; } std::string fontName = "Invalid_font"; if (textFont) { Form *form = pdfPage->getDoc()->getCatalog()->getCreateForm(); if (form) { fontName = form->findFontInDefaultResources(textFont->family().toStdString(), textFont->styleName().toStdString()); if (fontName.empty()) { fontName = form->addFontToDefaultResources(textFont->family().toStdString(), textFont->styleName().toStdString()).fontName; } if (!fontName.empty()) { form->ensureFontsForAllCharacters(pdfAnnot->getContents(), fontName); } else { fontName = "Invalid_font"; } } } DefaultAppearance da { { objName, fontName.c_str() }, pointSize, convertQColor(textColor) }; ftextann->setDefaultAppearance(da); } } std::unique_ptr TextAnnotationPrivate::getDefaultAppearanceFromNative() const { if (pdfAnnot && pdfAnnot->getType() == Annot::typeFreeText) { AnnotFreeText *ftextann = static_cast(pdfAnnot); return ftextann->getDefaultAppearance(); } else { return {}; } } TextAnnotation::TextAnnotation(TextAnnotation::TextType type) : Annotation(*new TextAnnotationPrivate()) { setTextType(type); } TextAnnotation::TextAnnotation(TextAnnotationPrivate &dd) : Annotation(dd) { } TextAnnotation::~TextAnnotation() { } Annotation::SubType TextAnnotation::subType() const { return AText; } TextAnnotation::TextType TextAnnotation::textType() const { Q_D(const TextAnnotation); if (!d->pdfAnnot) { return d->textType; } return d->pdfAnnot->getType() == Annot::typeText ? TextAnnotation::Linked : TextAnnotation::InPlace; } void TextAnnotation::setTextType(TextAnnotation::TextType type) { Q_D(TextAnnotation); if (!d->pdfAnnot) { d->textType = type; return; } // Type cannot be changed if annotation is already tied qWarning() << "You can't change the type of a TextAnnotation that is already in a page"; } QString TextAnnotation::textIcon() const { Q_D(const TextAnnotation); if (!d->pdfAnnot) { return d->textIcon; } if (d->pdfAnnot->getType() == Annot::typeText) { const AnnotText *textann = static_cast(d->pdfAnnot); return QString::fromLatin1(textann->getIcon()->c_str()); } return QString(); } void TextAnnotation::setTextIcon(const QString &icon) { Q_D(TextAnnotation); if (!d->pdfAnnot) { d->textIcon = icon; return; } if (d->pdfAnnot->getType() == Annot::typeText) { AnnotText *textann = static_cast(d->pdfAnnot); QByteArray encoded = icon.toLatin1(); GooString s(encoded.constData()); textann->setIcon(&s); } } QFont TextAnnotation::textFont() const { Q_D(const TextAnnotation); if (d->textFont) { return *d->textFont; } double fontSize { AnnotFreeText::undefinedFontPtSize }; if (d->pdfAnnot->getType() == Annot::typeFreeText) { std::unique_ptr da { d->getDefaultAppearanceFromNative() }; if (da && da->getFontPtSize() > 0) { fontSize = da->getFontPtSize(); } } QFont font; font.setPointSizeF(fontSize); return font; } void TextAnnotation::setTextFont(const QFont &font) { Q_D(TextAnnotation); if (font == d->textFont) { return; } d->textFont = font; d->setDefaultAppearanceToNative(); } QColor TextAnnotation::textColor() const { Q_D(const TextAnnotation); if (!d->pdfAnnot) { return d->textColor; } if (std::unique_ptr da { d->getDefaultAppearanceFromNative() }) { return convertAnnotColor(da->getFontColor()); } return {}; } void TextAnnotation::setTextColor(const QColor &color) { Q_D(TextAnnotation); if (color == d->textColor) { return; } d->textColor = color; d->setDefaultAppearanceToNative(); } TextAnnotation::InplaceAlignPosition TextAnnotation::inplaceAlign() const { Q_D(const TextAnnotation); if (!d->pdfAnnot) { return d->inplaceAlign; } if (d->pdfAnnot->getType() == Annot::typeFreeText) { const AnnotFreeText *ftextann = static_cast(d->pdfAnnot); switch (ftextann->getQuadding()) { case VariableTextQuadding::leftJustified: return InplaceAlignLeft; case VariableTextQuadding::centered: return InplaceAlignCenter; case VariableTextQuadding::rightJustified: return InplaceAlignRight; } } return InplaceAlignLeft; } static VariableTextQuadding alignToQuadding(TextAnnotation::InplaceAlignPosition align) { switch (align) { case TextAnnotation::InplaceAlignLeft: return VariableTextQuadding::leftJustified; case TextAnnotation::InplaceAlignCenter: return VariableTextQuadding::centered; case TextAnnotation::InplaceAlignRight: return VariableTextQuadding::rightJustified; } return VariableTextQuadding::leftJustified; } void TextAnnotation::setInplaceAlign(InplaceAlignPosition align) { Q_D(TextAnnotation); if (!d->pdfAnnot) { d->inplaceAlign = align; return; } if (d->pdfAnnot->getType() == Annot::typeFreeText) { AnnotFreeText *ftextann = static_cast(d->pdfAnnot); ftextann->setQuadding(alignToQuadding(align)); } } QPointF TextAnnotation::calloutPoint(int id) const { const QVector points = calloutPoints(); if (id < 0 || id >= points.size()) { return QPointF(); } else { return points[id]; } } QVector TextAnnotation::calloutPoints() const { Q_D(const TextAnnotation); if (!d->pdfAnnot) { return d->inplaceCallout; } if (d->pdfAnnot->getType() == Annot::typeText) { return QVector(); } const AnnotFreeText *ftextann = static_cast(d->pdfAnnot); const AnnotCalloutLine *callout = ftextann->getCalloutLine(); if (!callout) { return QVector(); } double MTX[6]; d->fillTransformationMTX(MTX); const AnnotCalloutMultiLine *callout_v6 = dynamic_cast(callout); QVector res(callout_v6 ? 3 : 2); XPDFReader::transform(MTX, callout->getX1(), callout->getY1(), res[0]); XPDFReader::transform(MTX, callout->getX2(), callout->getY2(), res[1]); if (callout_v6) { XPDFReader::transform(MTX, callout_v6->getX3(), callout_v6->getY3(), res[2]); } return res; } void TextAnnotation::setCalloutPoints(const QVector &points) { Q_D(TextAnnotation); if (!d->pdfAnnot) { d->inplaceCallout = points; return; } if (d->pdfAnnot->getType() != Annot::typeFreeText) { return; } AnnotFreeText *ftextann = static_cast(d->pdfAnnot); const int count = points.size(); if (count == 0) { ftextann->setCalloutLine(nullptr); return; } if (count != 2 && count != 3) { error(errSyntaxError, -1, "Expected zero, two or three points for callout"); return; } AnnotCalloutLine *callout; double x1, y1, x2, y2; double MTX[6]; d->fillTransformationMTX(MTX); XPDFReader::invTransform(MTX, points[0], x1, y1); XPDFReader::invTransform(MTX, points[1], x2, y2); if (count == 3) { double x3, y3; XPDFReader::invTransform(MTX, points[2], x3, y3); callout = new AnnotCalloutMultiLine(x1, y1, x2, y2, x3, y3); } else { callout = new AnnotCalloutLine(x1, y1, x2, y2); } ftextann->setCalloutLine(callout); delete callout; } TextAnnotation::InplaceIntent TextAnnotation::inplaceIntent() const { Q_D(const TextAnnotation); if (!d->pdfAnnot) { return d->inplaceIntent; } if (d->pdfAnnot->getType() == Annot::typeFreeText) { const AnnotFreeText *ftextann = static_cast(d->pdfAnnot); return (TextAnnotation::InplaceIntent)ftextann->getIntent(); } return TextAnnotation::Unknown; } void TextAnnotation::setInplaceIntent(TextAnnotation::InplaceIntent intent) { Q_D(TextAnnotation); if (!d->pdfAnnot) { d->inplaceIntent = intent; return; } if (d->pdfAnnot->getType() == Annot::typeFreeText) { AnnotFreeText *ftextann = static_cast(d->pdfAnnot); ftextann->setIntent((AnnotFreeText::AnnotFreeTextIntent)intent); } } /** LineAnnotation [Annotation] */ class LineAnnotationPrivate : public AnnotationPrivate { public: LineAnnotationPrivate(); Annotation *makeAlias() override; Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; // data fields (note uses border for rendering style) QVector linePoints; LineAnnotation::TermStyle lineStartStyle; LineAnnotation::TermStyle lineEndStyle; bool lineClosed : 1; // (if true draw close shape) bool lineShowCaption : 1; LineAnnotation::LineType lineType; QColor lineInnerColor; double lineLeadingFwdPt; double lineLeadingBackPt; LineAnnotation::LineIntent lineIntent; }; LineAnnotationPrivate::LineAnnotationPrivate() : AnnotationPrivate(), lineStartStyle(LineAnnotation::None), lineEndStyle(LineAnnotation::None), lineClosed(false), lineShowCaption(false), lineLeadingFwdPt(0), lineLeadingBackPt(0), lineIntent(LineAnnotation::Unknown) { } Annotation *LineAnnotationPrivate::makeAlias() { return new LineAnnotation(*this); } Annot *LineAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) { // Setters are defined in the public class LineAnnotation *q = static_cast(makeAlias()); // Set page and document pdfPage = destPage; parentDoc = doc; // Set pdfAnnot PDFRectangle rect = boundaryToPdfRectangle(boundary, flags); if (lineType == LineAnnotation::StraightLine) { pdfAnnot = new AnnotLine(doc->doc, &rect); } else { pdfAnnot = new AnnotPolygon(doc->doc, &rect, lineClosed ? Annot::typePolygon : Annot::typePolyLine); } // Set properties flushBaseAnnotationProperties(); q->setLinePoints(linePoints); q->setLineStartStyle(lineStartStyle); q->setLineEndStyle(lineEndStyle); q->setLineInnerColor(lineInnerColor); q->setLineLeadingForwardPoint(lineLeadingFwdPt); q->setLineLeadingBackPoint(lineLeadingBackPt); q->setLineShowCaption(lineShowCaption); q->setLineIntent(lineIntent); delete q; linePoints.clear(); // Free up memory return pdfAnnot; } LineAnnotation::LineAnnotation(LineAnnotation::LineType type) : Annotation(*new LineAnnotationPrivate()) { setLineType(type); } LineAnnotation::LineAnnotation(LineAnnotationPrivate &dd) : Annotation(dd) { } LineAnnotation::~LineAnnotation() { } Annotation::SubType LineAnnotation::subType() const { return ALine; } LineAnnotation::LineType LineAnnotation::lineType() const { Q_D(const LineAnnotation); if (!d->pdfAnnot) { return d->lineType; } return (d->pdfAnnot->getType() == Annot::typeLine) ? LineAnnotation::StraightLine : LineAnnotation::Polyline; } void LineAnnotation::setLineType(LineAnnotation::LineType type) { Q_D(LineAnnotation); if (!d->pdfAnnot) { d->lineType = type; return; } // Type cannot be changed if annotation is already tied qWarning() << "You can't change the type of a LineAnnotation that is already in a page"; } QVector LineAnnotation::linePoints() const { Q_D(const LineAnnotation); if (!d->pdfAnnot) { return d->linePoints; } double MTX[6]; d->fillTransformationMTX(MTX); QVector res; if (d->pdfAnnot->getType() == Annot::typeLine) { const AnnotLine *lineann = static_cast(d->pdfAnnot); QPointF p; XPDFReader::transform(MTX, lineann->getX1(), lineann->getY1(), p); res.append(p); XPDFReader::transform(MTX, lineann->getX2(), lineann->getY2(), p); res.append(p); } else { const AnnotPolygon *polyann = static_cast(d->pdfAnnot); const AnnotPath *vertices = polyann->getVertices(); for (int i = 0; i < vertices->getCoordsLength(); ++i) { QPointF p; XPDFReader::transform(MTX, vertices->getX(i), vertices->getY(i), p); res.append(p); } } return res; } void LineAnnotation::setLinePoints(const QVector &points) { Q_D(LineAnnotation); if (!d->pdfAnnot) { d->linePoints = points; return; } if (d->pdfAnnot->getType() == Annot::typeLine) { AnnotLine *lineann = static_cast(d->pdfAnnot); if (points.size() != 2) { error(errSyntaxError, -1, "Expected two points for a straight line"); return; } double x1, y1, x2, y2; double MTX[6]; d->fillTransformationMTX(MTX); XPDFReader::invTransform(MTX, points.first(), x1, y1); XPDFReader::invTransform(MTX, points.last(), x2, y2); lineann->setVertices(x1, y1, x2, y2); } else { AnnotPolygon *polyann = static_cast(d->pdfAnnot); AnnotPath *p = d->toAnnotPath(points); polyann->setVertices(p); delete p; } } LineAnnotation::TermStyle LineAnnotation::lineStartStyle() const { Q_D(const LineAnnotation); if (!d->pdfAnnot) { return d->lineStartStyle; } if (d->pdfAnnot->getType() == Annot::typeLine) { const AnnotLine *lineann = static_cast(d->pdfAnnot); return (LineAnnotation::TermStyle)lineann->getStartStyle(); } else { const AnnotPolygon *polyann = static_cast(d->pdfAnnot); return (LineAnnotation::TermStyle)polyann->getStartStyle(); } } void LineAnnotation::setLineStartStyle(LineAnnotation::TermStyle style) { Q_D(LineAnnotation); if (!d->pdfAnnot) { d->lineStartStyle = style; return; } if (d->pdfAnnot->getType() == Annot::typeLine) { AnnotLine *lineann = static_cast(d->pdfAnnot); lineann->setStartEndStyle((AnnotLineEndingStyle)style, lineann->getEndStyle()); } else { AnnotPolygon *polyann = static_cast(d->pdfAnnot); polyann->setStartEndStyle((AnnotLineEndingStyle)style, polyann->getEndStyle()); } } LineAnnotation::TermStyle LineAnnotation::lineEndStyle() const { Q_D(const LineAnnotation); if (!d->pdfAnnot) { return d->lineEndStyle; } if (d->pdfAnnot->getType() == Annot::typeLine) { const AnnotLine *lineann = static_cast(d->pdfAnnot); return (LineAnnotation::TermStyle)lineann->getEndStyle(); } else { const AnnotPolygon *polyann = static_cast(d->pdfAnnot); return (LineAnnotation::TermStyle)polyann->getEndStyle(); } } void LineAnnotation::setLineEndStyle(LineAnnotation::TermStyle style) { Q_D(LineAnnotation); if (!d->pdfAnnot) { d->lineEndStyle = style; return; } if (d->pdfAnnot->getType() == Annot::typeLine) { AnnotLine *lineann = static_cast(d->pdfAnnot); lineann->setStartEndStyle(lineann->getStartStyle(), (AnnotLineEndingStyle)style); } else { AnnotPolygon *polyann = static_cast(d->pdfAnnot); polyann->setStartEndStyle(polyann->getStartStyle(), (AnnotLineEndingStyle)style); } } bool LineAnnotation::isLineClosed() const { Q_D(const LineAnnotation); if (!d->pdfAnnot) { return d->lineClosed; } return d->pdfAnnot->getType() == Annot::typePolygon; } void LineAnnotation::setLineClosed(bool closed) { Q_D(LineAnnotation); if (!d->pdfAnnot) { d->lineClosed = closed; return; } if (d->pdfAnnot->getType() != Annot::typeLine) { AnnotPolygon *polyann = static_cast(d->pdfAnnot); // Set new subtype and switch intent if necessary if (closed) { polyann->setType(Annot::typePolygon); if (polyann->getIntent() == AnnotPolygon::polylineDimension) { polyann->setIntent(AnnotPolygon::polygonDimension); } } else { polyann->setType(Annot::typePolyLine); if (polyann->getIntent() == AnnotPolygon::polygonDimension) { polyann->setIntent(AnnotPolygon::polylineDimension); } } } } QColor LineAnnotation::lineInnerColor() const { Q_D(const LineAnnotation); if (!d->pdfAnnot) { return d->lineInnerColor; } AnnotColor *c; if (d->pdfAnnot->getType() == Annot::typeLine) { const AnnotLine *lineann = static_cast(d->pdfAnnot); c = lineann->getInteriorColor(); } else { const AnnotPolygon *polyann = static_cast(d->pdfAnnot); c = polyann->getInteriorColor(); } return convertAnnotColor(c); } void LineAnnotation::setLineInnerColor(const QColor &color) { Q_D(LineAnnotation); if (!d->pdfAnnot) { d->lineInnerColor = color; return; } auto c = convertQColor(color); if (d->pdfAnnot->getType() == Annot::typeLine) { AnnotLine *lineann = static_cast(d->pdfAnnot); lineann->setInteriorColor(std::move(c)); } else { AnnotPolygon *polyann = static_cast(d->pdfAnnot); polyann->setInteriorColor(std::move(c)); } } double LineAnnotation::lineLeadingForwardPoint() const { Q_D(const LineAnnotation); if (!d->pdfAnnot) { return d->lineLeadingFwdPt; } if (d->pdfAnnot->getType() == Annot::typeLine) { const AnnotLine *lineann = static_cast(d->pdfAnnot); return lineann->getLeaderLineLength(); } return 0; } void LineAnnotation::setLineLeadingForwardPoint(double point) { Q_D(LineAnnotation); if (!d->pdfAnnot) { d->lineLeadingFwdPt = point; return; } if (d->pdfAnnot->getType() == Annot::typeLine) { AnnotLine *lineann = static_cast(d->pdfAnnot); lineann->setLeaderLineLength(point); } } double LineAnnotation::lineLeadingBackPoint() const { Q_D(const LineAnnotation); if (!d->pdfAnnot) { return d->lineLeadingBackPt; } if (d->pdfAnnot->getType() == Annot::typeLine) { const AnnotLine *lineann = static_cast(d->pdfAnnot); return lineann->getLeaderLineExtension(); } return 0; } void LineAnnotation::setLineLeadingBackPoint(double point) { Q_D(LineAnnotation); if (!d->pdfAnnot) { d->lineLeadingBackPt = point; return; } if (d->pdfAnnot->getType() == Annot::typeLine) { AnnotLine *lineann = static_cast(d->pdfAnnot); lineann->setLeaderLineExtension(point); } } bool LineAnnotation::lineShowCaption() const { Q_D(const LineAnnotation); if (!d->pdfAnnot) { return d->lineShowCaption; } if (d->pdfAnnot->getType() == Annot::typeLine) { const AnnotLine *lineann = static_cast(d->pdfAnnot); return lineann->getCaption(); } return false; } void LineAnnotation::setLineShowCaption(bool show) { Q_D(LineAnnotation); if (!d->pdfAnnot) { d->lineShowCaption = show; return; } if (d->pdfAnnot->getType() == Annot::typeLine) { AnnotLine *lineann = static_cast(d->pdfAnnot); lineann->setCaption(show); } } LineAnnotation::LineIntent LineAnnotation::lineIntent() const { Q_D(const LineAnnotation); if (!d->pdfAnnot) { return d->lineIntent; } if (d->pdfAnnot->getType() == Annot::typeLine) { const AnnotLine *lineann = static_cast(d->pdfAnnot); return (LineAnnotation::LineIntent)(lineann->getIntent() + 1); } else { const AnnotPolygon *polyann = static_cast(d->pdfAnnot); if (polyann->getIntent() == AnnotPolygon::polygonCloud) { return LineAnnotation::PolygonCloud; } else { // AnnotPolygon::polylineDimension, AnnotPolygon::polygonDimension return LineAnnotation::Dimension; } } } void LineAnnotation::setLineIntent(LineAnnotation::LineIntent intent) { Q_D(LineAnnotation); if (!d->pdfAnnot) { d->lineIntent = intent; return; } if (intent == LineAnnotation::Unknown) { return; // Do not set (actually, it should clear the property) } if (d->pdfAnnot->getType() == Annot::typeLine) { AnnotLine *lineann = static_cast(d->pdfAnnot); lineann->setIntent((AnnotLine::AnnotLineIntent)(intent - 1)); } else { AnnotPolygon *polyann = static_cast(d->pdfAnnot); if (intent == LineAnnotation::PolygonCloud) { polyann->setIntent(AnnotPolygon::polygonCloud); } else // LineAnnotation::Dimension { if (d->pdfAnnot->getType() == Annot::typePolygon) { polyann->setIntent(AnnotPolygon::polygonDimension); } else { // Annot::typePolyLine polyann->setIntent(AnnotPolygon::polylineDimension); } } } } /** GeomAnnotation [Annotation] */ class GeomAnnotationPrivate : public AnnotationPrivate { public: GeomAnnotationPrivate(); Annotation *makeAlias() override; Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; // data fields (note uses border for rendering style) GeomAnnotation::GeomType geomType; QColor geomInnerColor; }; GeomAnnotationPrivate::GeomAnnotationPrivate() : AnnotationPrivate(), geomType(GeomAnnotation::InscribedSquare) { } Annotation *GeomAnnotationPrivate::makeAlias() { return new GeomAnnotation(*this); } Annot *GeomAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) { // Setters are defined in the public class GeomAnnotation *q = static_cast(makeAlias()); // Set page and document pdfPage = destPage; parentDoc = doc; Annot::AnnotSubtype type; if (geomType == GeomAnnotation::InscribedSquare) { type = Annot::typeSquare; } else { // GeomAnnotation::InscribedCircle type = Annot::typeCircle; } // Set pdfAnnot PDFRectangle rect = boundaryToPdfRectangle(boundary, flags); pdfAnnot = new AnnotGeometry(destPage->getDoc(), &rect, type); // Set properties flushBaseAnnotationProperties(); q->setGeomInnerColor(geomInnerColor); delete q; return pdfAnnot; } GeomAnnotation::GeomAnnotation() : Annotation(*new GeomAnnotationPrivate()) { } GeomAnnotation::GeomAnnotation(GeomAnnotationPrivate &dd) : Annotation(dd) { } GeomAnnotation::~GeomAnnotation() { } Annotation::SubType GeomAnnotation::subType() const { return AGeom; } GeomAnnotation::GeomType GeomAnnotation::geomType() const { Q_D(const GeomAnnotation); if (!d->pdfAnnot) { return d->geomType; } if (d->pdfAnnot->getType() == Annot::typeSquare) { return GeomAnnotation::InscribedSquare; } else { // Annot::typeCircle return GeomAnnotation::InscribedCircle; } } void GeomAnnotation::setGeomType(GeomAnnotation::GeomType type) { Q_D(GeomAnnotation); if (!d->pdfAnnot) { d->geomType = type; return; } AnnotGeometry *geomann = static_cast(d->pdfAnnot); if (type == GeomAnnotation::InscribedSquare) { geomann->setType(Annot::typeSquare); } else { // GeomAnnotation::InscribedCircle geomann->setType(Annot::typeCircle); } } QColor GeomAnnotation::geomInnerColor() const { Q_D(const GeomAnnotation); if (!d->pdfAnnot) { return d->geomInnerColor; } const AnnotGeometry *geomann = static_cast(d->pdfAnnot); return convertAnnotColor(geomann->getInteriorColor()); } void GeomAnnotation::setGeomInnerColor(const QColor &color) { Q_D(GeomAnnotation); if (!d->pdfAnnot) { d->geomInnerColor = color; return; } AnnotGeometry *geomann = static_cast(d->pdfAnnot); geomann->setInteriorColor(convertQColor(color)); } /** HighlightAnnotation [Annotation] */ class HighlightAnnotationPrivate : public AnnotationPrivate { public: HighlightAnnotationPrivate(); Annotation *makeAlias() override; Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; // data fields HighlightAnnotation::HighlightType highlightType; QList highlightQuads; // not empty // helpers static Annot::AnnotSubtype toAnnotSubType(HighlightAnnotation::HighlightType type); QList fromQuadrilaterals(AnnotQuadrilaterals *quads) const; AnnotQuadrilaterals *toQuadrilaterals(const QList &quads) const; }; HighlightAnnotationPrivate::HighlightAnnotationPrivate() : AnnotationPrivate(), highlightType(HighlightAnnotation::Highlight) { } Annotation *HighlightAnnotationPrivate::makeAlias() { return new HighlightAnnotation(*this); } Annot::AnnotSubtype HighlightAnnotationPrivate::toAnnotSubType(HighlightAnnotation::HighlightType type) { switch (type) { default: // HighlightAnnotation::Highlight: return Annot::typeHighlight; case HighlightAnnotation::Underline: return Annot::typeUnderline; case HighlightAnnotation::Squiggly: return Annot::typeSquiggly; case HighlightAnnotation::StrikeOut: return Annot::typeStrikeOut; } } QList HighlightAnnotationPrivate::fromQuadrilaterals(AnnotQuadrilaterals *hlquads) const { QList quads; if (!hlquads || !hlquads->getQuadrilateralsLength()) { return quads; } const int quadsCount = hlquads->getQuadrilateralsLength(); double MTX[6]; fillTransformationMTX(MTX); quads.reserve(quadsCount); for (int q = 0; q < quadsCount; ++q) { HighlightAnnotation::Quad quad; XPDFReader::transform(MTX, hlquads->getX1(q), hlquads->getY1(q), quad.points[0]); XPDFReader::transform(MTX, hlquads->getX2(q), hlquads->getY2(q), quad.points[1]); XPDFReader::transform(MTX, hlquads->getX3(q), hlquads->getY3(q), quad.points[2]); XPDFReader::transform(MTX, hlquads->getX4(q), hlquads->getY4(q), quad.points[3]); // ### PDF1.6 specs says that point are in ccw order, but in fact // points 3 and 4 are swapped in every PDF around! QPointF tmpPoint = quad.points[2]; quad.points[2] = quad.points[3]; quad.points[3] = tmpPoint; // initialize other properties and append quad quad.capStart = true; // unlinked quads are always capped quad.capEnd = true; // unlinked quads are always capped quad.feather = 0.1; // default feather quads.append(quad); } return quads; } AnnotQuadrilaterals *HighlightAnnotationPrivate::toQuadrilaterals(const QList &quads) const { const int count = quads.size(); auto ac = std::make_unique(count); double MTX[6]; fillTransformationMTX(MTX); int pos = 0; foreach (const HighlightAnnotation::Quad &q, quads) { double x1, y1, x2, y2, x3, y3, x4, y4; XPDFReader::invTransform(MTX, q.points[0], x1, y1); XPDFReader::invTransform(MTX, q.points[1], x2, y2); // Swap points 3 and 4 (see HighlightAnnotationPrivate::fromQuadrilaterals) XPDFReader::invTransform(MTX, q.points[3], x3, y3); XPDFReader::invTransform(MTX, q.points[2], x4, y4); ac[pos++] = AnnotQuadrilaterals::AnnotQuadrilateral(x1, y1, x2, y2, x3, y3, x4, y4); } return new AnnotQuadrilaterals(std::move(ac), count); } Annot *HighlightAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) { // Setters are defined in the public class HighlightAnnotation *q = static_cast(makeAlias()); // Set page and document pdfPage = destPage; parentDoc = doc; // Set pdfAnnot PDFRectangle rect = boundaryToPdfRectangle(boundary, flags); pdfAnnot = new AnnotTextMarkup(destPage->getDoc(), &rect, toAnnotSubType(highlightType)); // Set properties flushBaseAnnotationProperties(); q->setHighlightQuads(highlightQuads); highlightQuads.clear(); // Free up memory delete q; return pdfAnnot; } HighlightAnnotation::HighlightAnnotation() : Annotation(*new HighlightAnnotationPrivate()) { } HighlightAnnotation::HighlightAnnotation(HighlightAnnotationPrivate &dd) : Annotation(dd) { } HighlightAnnotation::~HighlightAnnotation() { } Annotation::SubType HighlightAnnotation::subType() const { return AHighlight; } HighlightAnnotation::HighlightType HighlightAnnotation::highlightType() const { Q_D(const HighlightAnnotation); if (!d->pdfAnnot) { return d->highlightType; } Annot::AnnotSubtype subType = d->pdfAnnot->getType(); if (subType == Annot::typeHighlight) { return HighlightAnnotation::Highlight; } else if (subType == Annot::typeUnderline) { return HighlightAnnotation::Underline; } else if (subType == Annot::typeSquiggly) { return HighlightAnnotation::Squiggly; } else { // Annot::typeStrikeOut return HighlightAnnotation::StrikeOut; } } void HighlightAnnotation::setHighlightType(HighlightAnnotation::HighlightType type) { Q_D(HighlightAnnotation); if (!d->pdfAnnot) { d->highlightType = type; return; } AnnotTextMarkup *hlann = static_cast(d->pdfAnnot); hlann->setType(HighlightAnnotationPrivate::toAnnotSubType(type)); } QList HighlightAnnotation::highlightQuads() const { Q_D(const HighlightAnnotation); if (!d->pdfAnnot) { return d->highlightQuads; } const AnnotTextMarkup *hlann = static_cast(d->pdfAnnot); return d->fromQuadrilaterals(hlann->getQuadrilaterals()); } void HighlightAnnotation::setHighlightQuads(const QList &quads) { Q_D(HighlightAnnotation); if (!d->pdfAnnot) { d->highlightQuads = quads; return; } AnnotTextMarkup *hlann = static_cast(d->pdfAnnot); AnnotQuadrilaterals *quadrilaterals = d->toQuadrilaterals(quads); hlann->setQuadrilaterals(quadrilaterals); delete quadrilaterals; } /** StampAnnotation [Annotation] */ class StampAnnotationPrivate : public AnnotationPrivate { public: StampAnnotationPrivate(); Annotation *makeAlias() override; Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; AnnotStampImageHelper *convertQImageToAnnotStampImageHelper(const QImage &qimg); // data fields QString stampIconName; QImage stampCustomImage; }; StampAnnotationPrivate::StampAnnotationPrivate() : AnnotationPrivate(), stampIconName(QStringLiteral("Draft")) { } Annotation *StampAnnotationPrivate::makeAlias() { return new StampAnnotation(*this); } Annot *StampAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) { StampAnnotation *q = static_cast(makeAlias()); // Set page and document pdfPage = destPage; parentDoc = doc; // Set pdfAnnot PDFRectangle rect = boundaryToPdfRectangle(boundary, flags); pdfAnnot = new AnnotStamp(destPage->getDoc(), &rect); // Set properties flushBaseAnnotationProperties(); q->setStampIconName(stampIconName); q->setStampCustomImage(stampCustomImage); delete q; stampIconName.clear(); // Free up memory return pdfAnnot; } AnnotStampImageHelper *StampAnnotationPrivate::convertQImageToAnnotStampImageHelper(const QImage &qimg) { QImage convertedQImage = qimg; QByteArray data; QByteArray sMaskData; const int width = convertedQImage.width(); const int height = convertedQImage.height(); int bitsPerComponent = 1; ColorSpace colorSpace = ColorSpace::DeviceGray; switch (convertedQImage.format()) { case QImage::Format_MonoLSB: if (!convertedQImage.allGray()) { convertedQImage = convertedQImage.convertToFormat(QImage::Format_RGB888); colorSpace = ColorSpace::DeviceRGB; bitsPerComponent = 8; } else { convertedQImage = convertedQImage.convertToFormat(QImage::Format_Mono); } break; case QImage::Format_Mono: if (!convertedQImage.allGray()) { convertedQImage = convertedQImage.convertToFormat(QImage::Format_RGB888); colorSpace = ColorSpace::DeviceRGB; bitsPerComponent = 8; } break; case QImage::Format_RGB32: case QImage::Format_ARGB32_Premultiplied: case QImage::Format_ARGB8565_Premultiplied: case QImage::Format_ARGB6666_Premultiplied: case QImage::Format_ARGB8555_Premultiplied: case QImage::Format_ARGB4444_Premultiplied: case QImage::Format_Alpha8: convertedQImage = convertedQImage.convertToFormat(QImage::Format_ARGB32); colorSpace = ColorSpace::DeviceRGB; bitsPerComponent = 8; break; case QImage::Format_RGBA8888: case QImage::Format_RGBA8888_Premultiplied: case QImage::Format_RGBX8888: case QImage::Format_ARGB32: colorSpace = ColorSpace::DeviceRGB; bitsPerComponent = 8; break; case QImage::Format_Grayscale8: bitsPerComponent = 8; break; case QImage::Format_Grayscale16: convertedQImage = convertedQImage.convertToFormat(QImage::Format_Grayscale8); colorSpace = ColorSpace::DeviceGray; bitsPerComponent = 8; break; case QImage::Format_RGB16: case QImage::Format_RGB666: case QImage::Format_RGB555: case QImage::Format_RGB444: convertedQImage = convertedQImage.convertToFormat(QImage::Format_RGB888); colorSpace = ColorSpace::DeviceRGB; bitsPerComponent = 8; break; case QImage::Format_RGB888: colorSpace = ColorSpace::DeviceRGB; bitsPerComponent = 8; break; default: convertedQImage = convertedQImage.convertToFormat(QImage::Format_ARGB32); colorSpace = ColorSpace::DeviceRGB; bitsPerComponent = 8; break; } getRawDataFromQImage(convertedQImage, convertedQImage.depth(), &data, &sMaskData); AnnotStampImageHelper *annotImg; 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.size()); } return annotImg; } StampAnnotation::StampAnnotation() : Annotation(*new StampAnnotationPrivate()) { } StampAnnotation::StampAnnotation(StampAnnotationPrivate &dd) : Annotation(dd) { } StampAnnotation::~StampAnnotation() { } Annotation::SubType StampAnnotation::subType() const { return AStamp; } QString StampAnnotation::stampIconName() const { Q_D(const StampAnnotation); if (!d->pdfAnnot) { return d->stampIconName; } const AnnotStamp *stampann = static_cast(d->pdfAnnot); return QString::fromLatin1(stampann->getIcon()->c_str()); } void StampAnnotation::setStampIconName(const QString &name) { Q_D(StampAnnotation); if (!d->pdfAnnot) { d->stampIconName = name; return; } AnnotStamp *stampann = static_cast(d->pdfAnnot); QByteArray encoded = name.toLatin1(); GooString s(encoded.constData()); stampann->setIcon(&s); } void StampAnnotation::setStampCustomImage(const QImage &image) { if (image.isNull()) { return; } Q_D(StampAnnotation); if (!d->pdfAnnot) { d->stampCustomImage = QImage(image); return; } AnnotStamp *stampann = static_cast(d->pdfAnnot); AnnotStampImageHelper *annotCustomImage = d->convertQImageToAnnotStampImageHelper(image); stampann->setCustomImage(annotCustomImage); } /** InkAnnotation [Annotation] */ class InkAnnotationPrivate : public AnnotationPrivate { public: InkAnnotationPrivate(); Annotation *makeAlias() override; Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; // data fields QList> inkPaths; // helper AnnotPath **toAnnotPaths(const QList> &paths); }; InkAnnotationPrivate::InkAnnotationPrivate() : AnnotationPrivate() { } Annotation *InkAnnotationPrivate::makeAlias() { return new InkAnnotation(*this); } // Note: Caller is required to delete array elements and the array itself after use AnnotPath **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]); } return res; } Annot *InkAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) { // Setters are defined in the public class InkAnnotation *q = static_cast(makeAlias()); // Set page and document pdfPage = destPage; parentDoc = doc; // Set pdfAnnot PDFRectangle rect = boundaryToPdfRectangle(boundary, flags); pdfAnnot = new AnnotInk(destPage->getDoc(), &rect); // Set properties flushBaseAnnotationProperties(); q->setInkPaths(inkPaths); inkPaths.clear(); // Free up memory delete q; return pdfAnnot; } InkAnnotation::InkAnnotation() : Annotation(*new InkAnnotationPrivate()) { } InkAnnotation::InkAnnotation(InkAnnotationPrivate &dd) : Annotation(dd) { } InkAnnotation::~InkAnnotation() { } Annotation::SubType InkAnnotation::subType() const { return AInk; } QList> InkAnnotation::inkPaths() const { Q_D(const InkAnnotation); if (!d->pdfAnnot) { return d->inkPaths; } const AnnotInk *inkann = static_cast(d->pdfAnnot); const AnnotPath *const *paths = inkann->getInkList(); if (!paths || !inkann->getInkListLength()) { return {}; } double MTX[6]; d->fillTransformationMTX(MTX); const int pathsNumber = inkann->getInkListLength(); QList> inkPaths; inkPaths.reserve(pathsNumber); for (int m = 0; m < pathsNumber; ++m) { // 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; XPDFReader::transform(MTX, path->getX(n), path->getY(n), point); localList.append(point); } // ..and add it to the annotation inkPaths.append(localList); } return inkPaths; } void InkAnnotation::setInkPaths(const QList> &paths) { Q_D(InkAnnotation); if (!d->pdfAnnot) { d->inkPaths = paths; return; } 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; } /** LinkAnnotation [Annotation] */ class LinkAnnotationPrivate : public AnnotationPrivate { public: LinkAnnotationPrivate(); ~LinkAnnotationPrivate() override; Annotation *makeAlias() override; Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; // data fields std::unique_ptr linkDestination; LinkAnnotation::HighlightMode linkHLMode; QPointF linkRegion[4]; }; LinkAnnotationPrivate::LinkAnnotationPrivate() : AnnotationPrivate(), linkHLMode(LinkAnnotation::Invert) { } LinkAnnotationPrivate::~LinkAnnotationPrivate() { } Annotation *LinkAnnotationPrivate::makeAlias() { return new LinkAnnotation(*this); } Annot *LinkAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) { return nullptr; // Not implemented } LinkAnnotation::LinkAnnotation() : Annotation(*new LinkAnnotationPrivate()) { } LinkAnnotation::LinkAnnotation(LinkAnnotationPrivate &dd) : Annotation(dd) { } LinkAnnotation::~LinkAnnotation() { } Annotation::SubType LinkAnnotation::subType() const { return ALink; } Link *LinkAnnotation::linkDestination() const { Q_D(const LinkAnnotation); return d->linkDestination.get(); } void LinkAnnotation::setLinkDestination(std::unique_ptr &&link) { Q_D(LinkAnnotation); d->linkDestination = std::move(link); } LinkAnnotation::HighlightMode LinkAnnotation::linkHighlightMode() const { Q_D(const LinkAnnotation); return d->linkHLMode; } void LinkAnnotation::setLinkHighlightMode(LinkAnnotation::HighlightMode mode) { Q_D(LinkAnnotation); d->linkHLMode = mode; } QPointF LinkAnnotation::linkRegionPoint(int id) const { if (id < 0 || id >= 4) { return QPointF(); } Q_D(const LinkAnnotation); return d->linkRegion[id]; } void LinkAnnotation::setLinkRegionPoint(int id, const QPointF point) { if (id < 0 || id >= 4) { return; } Q_D(LinkAnnotation); d->linkRegion[id] = point; } /** CaretAnnotation [Annotation] */ class CaretAnnotationPrivate : public AnnotationPrivate { public: CaretAnnotationPrivate(); Annotation *makeAlias() override; Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; // data fields CaretAnnotation::CaretSymbol symbol; }; CaretAnnotationPrivate::CaretAnnotationPrivate() : AnnotationPrivate(), symbol(CaretAnnotation::None) { } Annotation *CaretAnnotationPrivate::makeAlias() { return new CaretAnnotation(*this); } Annot *CaretAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) { // Setters are defined in the public class CaretAnnotation *q = static_cast(makeAlias()); // Set page and document pdfPage = destPage; parentDoc = doc; // Set pdfAnnot PDFRectangle rect = boundaryToPdfRectangle(boundary, flags); pdfAnnot = new AnnotCaret(destPage->getDoc(), &rect); // Set properties flushBaseAnnotationProperties(); q->setCaretSymbol(symbol); delete q; return pdfAnnot; } CaretAnnotation::CaretAnnotation() : Annotation(*new CaretAnnotationPrivate()) { } CaretAnnotation::CaretAnnotation(CaretAnnotationPrivate &dd) : Annotation(dd) { } CaretAnnotation::~CaretAnnotation() { } Annotation::SubType CaretAnnotation::subType() const { return ACaret; } CaretAnnotation::CaretSymbol CaretAnnotation::caretSymbol() const { Q_D(const CaretAnnotation); if (!d->pdfAnnot) { return d->symbol; } const AnnotCaret *caretann = static_cast(d->pdfAnnot); return (CaretAnnotation::CaretSymbol)caretann->getSymbol(); } void CaretAnnotation::setCaretSymbol(CaretAnnotation::CaretSymbol symbol) { Q_D(CaretAnnotation); if (!d->pdfAnnot) { d->symbol = symbol; return; } AnnotCaret *caretann = static_cast(d->pdfAnnot); caretann->setSymbol((AnnotCaret::AnnotCaretSymbol)symbol); } /** FileAttachmentAnnotation [Annotation] */ class FileAttachmentAnnotationPrivate : public AnnotationPrivate { public: FileAttachmentAnnotationPrivate(); ~FileAttachmentAnnotationPrivate() override; Annotation *makeAlias() override; Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; // data fields QString icon; EmbeddedFile *embfile; }; FileAttachmentAnnotationPrivate::FileAttachmentAnnotationPrivate() : AnnotationPrivate(), icon(QStringLiteral("PushPin")), embfile(nullptr) { } FileAttachmentAnnotationPrivate::~FileAttachmentAnnotationPrivate() { delete embfile; } Annotation *FileAttachmentAnnotationPrivate::makeAlias() { return new FileAttachmentAnnotation(*this); } Annot *FileAttachmentAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) { return nullptr; // Not implemented } FileAttachmentAnnotation::FileAttachmentAnnotation() : Annotation(*new FileAttachmentAnnotationPrivate()) { } FileAttachmentAnnotation::FileAttachmentAnnotation(FileAttachmentAnnotationPrivate &dd) : Annotation(dd) { } FileAttachmentAnnotation::~FileAttachmentAnnotation() { } Annotation::SubType FileAttachmentAnnotation::subType() const { return AFileAttachment; } QString FileAttachmentAnnotation::fileIconName() const { Q_D(const FileAttachmentAnnotation); return d->icon; } void FileAttachmentAnnotation::setFileIconName(const QString &icon) { Q_D(FileAttachmentAnnotation); d->icon = icon; } EmbeddedFile *FileAttachmentAnnotation::embeddedFile() const { Q_D(const FileAttachmentAnnotation); return d->embfile; } void FileAttachmentAnnotation::setEmbeddedFile(EmbeddedFile *ef) { Q_D(FileAttachmentAnnotation); d->embfile = ef; } /** SoundAnnotation [Annotation] */ class SoundAnnotationPrivate : public AnnotationPrivate { public: SoundAnnotationPrivate(); ~SoundAnnotationPrivate() override; Annotation *makeAlias() override; Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; // data fields QString icon; SoundObject *sound; }; SoundAnnotationPrivate::SoundAnnotationPrivate() : AnnotationPrivate(), icon(QStringLiteral("Speaker")), sound(nullptr) { } SoundAnnotationPrivate::~SoundAnnotationPrivate() { delete sound; } Annotation *SoundAnnotationPrivate::makeAlias() { return new SoundAnnotation(*this); } Annot *SoundAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) { return nullptr; // Not implemented } SoundAnnotation::SoundAnnotation() : Annotation(*new SoundAnnotationPrivate()) { } SoundAnnotation::SoundAnnotation(SoundAnnotationPrivate &dd) : Annotation(dd) { } SoundAnnotation::~SoundAnnotation() { } Annotation::SubType SoundAnnotation::subType() const { return ASound; } QString SoundAnnotation::soundIconName() const { Q_D(const SoundAnnotation); return d->icon; } void SoundAnnotation::setSoundIconName(const QString &icon) { Q_D(SoundAnnotation); d->icon = icon; } SoundObject *SoundAnnotation::sound() const { Q_D(const SoundAnnotation); return d->sound; } void SoundAnnotation::setSound(SoundObject *s) { Q_D(SoundAnnotation); d->sound = s; } /** MovieAnnotation [Annotation] */ class MovieAnnotationPrivate : public AnnotationPrivate { public: MovieAnnotationPrivate(); ~MovieAnnotationPrivate() override; Annotation *makeAlias() override; Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; // data fields MovieObject *movie; QString title; }; MovieAnnotationPrivate::MovieAnnotationPrivate() : AnnotationPrivate(), movie(nullptr) { } MovieAnnotationPrivate::~MovieAnnotationPrivate() { delete movie; } Annotation *MovieAnnotationPrivate::makeAlias() { return new MovieAnnotation(*this); } Annot *MovieAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) { return nullptr; // Not implemented } MovieAnnotation::MovieAnnotation() : Annotation(*new MovieAnnotationPrivate()) { } MovieAnnotation::MovieAnnotation(MovieAnnotationPrivate &dd) : Annotation(dd) { } MovieAnnotation::~MovieAnnotation() { } Annotation::SubType MovieAnnotation::subType() const { return AMovie; } MovieObject *MovieAnnotation::movie() const { Q_D(const MovieAnnotation); return d->movie; } void MovieAnnotation::setMovie(MovieObject *movie) { Q_D(MovieAnnotation); d->movie = movie; } QString MovieAnnotation::movieTitle() const { Q_D(const MovieAnnotation); return d->title; } void MovieAnnotation::setMovieTitle(const QString &title) { Q_D(MovieAnnotation); d->title = title; } /** ScreenAnnotation [Annotation] */ class ScreenAnnotationPrivate : public AnnotationPrivate { public: ScreenAnnotationPrivate(); ~ScreenAnnotationPrivate() override; Annotation *makeAlias() override; Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; // data fields LinkRendition *action; QString title; }; ScreenAnnotationPrivate::ScreenAnnotationPrivate() : AnnotationPrivate(), action(nullptr) { } ScreenAnnotationPrivate::~ScreenAnnotationPrivate() { delete action; } ScreenAnnotation::ScreenAnnotation(ScreenAnnotationPrivate &dd) : Annotation(dd) { } Annotation *ScreenAnnotationPrivate::makeAlias() { return new ScreenAnnotation(*this); } Annot *ScreenAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) { return nullptr; // Not implemented } ScreenAnnotation::ScreenAnnotation() : Annotation(*new ScreenAnnotationPrivate()) { } ScreenAnnotation::~ScreenAnnotation() { } Annotation::SubType ScreenAnnotation::subType() const { return AScreen; } LinkRendition *ScreenAnnotation::action() const { Q_D(const ScreenAnnotation); return d->action; } void ScreenAnnotation::setAction(LinkRendition *action) { Q_D(ScreenAnnotation); d->action = action; } QString ScreenAnnotation::screenTitle() const { Q_D(const ScreenAnnotation); return d->title; } void ScreenAnnotation::setScreenTitle(const QString &title) { Q_D(ScreenAnnotation); d->title = title; } std::unique_ptr ScreenAnnotation::additionalAction(AdditionalActionType type) const { Q_D(const ScreenAnnotation); return d->additionalAction(type); } /** WidgetAnnotation [Annotation] */ class WidgetAnnotationPrivate : public AnnotationPrivate { public: Annotation *makeAlias() override; Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; }; Annotation *WidgetAnnotationPrivate::makeAlias() { return new WidgetAnnotation(*this); } Annot *WidgetAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) { return nullptr; // Not implemented } WidgetAnnotation::WidgetAnnotation(WidgetAnnotationPrivate &dd) : Annotation(dd) { } WidgetAnnotation::WidgetAnnotation() : Annotation(*new WidgetAnnotationPrivate()) { } WidgetAnnotation::~WidgetAnnotation() { } Annotation::SubType WidgetAnnotation::subType() const { return AWidget; } std::unique_ptr WidgetAnnotation::additionalAction(AdditionalActionType type) const { Q_D(const WidgetAnnotation); return d->additionalAction(type); } /** RichMediaAnnotation [Annotation] */ class RichMediaAnnotation::Params::Private { public: Private() { } QString flashVars; }; RichMediaAnnotation::Params::Params() : d(new Private) { } RichMediaAnnotation::Params::~Params() { } void RichMediaAnnotation::Params::setFlashVars(const QString &flashVars) { d->flashVars = flashVars; } QString RichMediaAnnotation::Params::flashVars() const { return d->flashVars; } class RichMediaAnnotation::Instance::Private { public: Private() : params(nullptr) { } ~Private() { delete params; } Private(const Private &) = delete; Private &operator=(const Private &) = delete; RichMediaAnnotation::Instance::Type type; RichMediaAnnotation::Params *params; }; RichMediaAnnotation::Instance::Instance() : d(new Private) { } RichMediaAnnotation::Instance::~Instance() { } void RichMediaAnnotation::Instance::setType(Type type) { d->type = type; } RichMediaAnnotation::Instance::Type RichMediaAnnotation::Instance::type() const { return d->type; } void RichMediaAnnotation::Instance::setParams(RichMediaAnnotation::Params *params) { delete d->params; d->params = params; } RichMediaAnnotation::Params *RichMediaAnnotation::Instance::params() const { return d->params; } class RichMediaAnnotation::Configuration::Private { public: Private() { } ~Private() { qDeleteAll(instances); instances.clear(); } Private(const Private &) = delete; Private &operator=(const Private &) = delete; RichMediaAnnotation::Configuration::Type type; QString name; QList instances; }; RichMediaAnnotation::Configuration::Configuration() : d(new Private) { } RichMediaAnnotation::Configuration::~Configuration() { } void RichMediaAnnotation::Configuration::setType(Type type) { d->type = type; } RichMediaAnnotation::Configuration::Type RichMediaAnnotation::Configuration::type() const { return d->type; } void RichMediaAnnotation::Configuration::setName(const QString &name) { d->name = name; } QString RichMediaAnnotation::Configuration::name() const { return d->name; } void RichMediaAnnotation::Configuration::setInstances(const QList &instances) { qDeleteAll(d->instances); d->instances.clear(); d->instances = instances; } QList RichMediaAnnotation::Configuration::instances() const { return d->instances; } class RichMediaAnnotation::Asset::Private { public: Private() : embeddedFile(nullptr) { } ~Private() { delete embeddedFile; } Private(const Private &) = delete; Private &operator=(const Private &) = delete; QString name; EmbeddedFile *embeddedFile; }; RichMediaAnnotation::Asset::Asset() : d(new Private) { } RichMediaAnnotation::Asset::~Asset() { } void RichMediaAnnotation::Asset::setName(const QString &name) { d->name = name; } QString RichMediaAnnotation::Asset::name() const { return d->name; } void RichMediaAnnotation::Asset::setEmbeddedFile(EmbeddedFile *embeddedFile) { delete d->embeddedFile; d->embeddedFile = embeddedFile; } EmbeddedFile *RichMediaAnnotation::Asset::embeddedFile() const { return d->embeddedFile; } class RichMediaAnnotation::Content::Private { public: Private() { } ~Private() { qDeleteAll(configurations); configurations.clear(); qDeleteAll(assets); assets.clear(); } Private(const Private &) = delete; Private &operator=(const Private &) = delete; QList configurations; QList assets; }; RichMediaAnnotation::Content::Content() : d(new Private) { } RichMediaAnnotation::Content::~Content() { } void RichMediaAnnotation::Content::setConfigurations(const QList &configurations) { qDeleteAll(d->configurations); d->configurations.clear(); d->configurations = configurations; } QList RichMediaAnnotation::Content::configurations() const { return d->configurations; } void RichMediaAnnotation::Content::setAssets(const QList &assets) { qDeleteAll(d->assets); d->assets.clear(); d->assets = assets; } QList RichMediaAnnotation::Content::assets() const { return d->assets; } class RichMediaAnnotation::Activation::Private { public: Private() : condition(RichMediaAnnotation::Activation::UserAction) { } RichMediaAnnotation::Activation::Condition condition; }; RichMediaAnnotation::Activation::Activation() : d(new Private) { } RichMediaAnnotation::Activation::~Activation() { } void RichMediaAnnotation::Activation::setCondition(Condition condition) { d->condition = condition; } RichMediaAnnotation::Activation::Condition RichMediaAnnotation::Activation::condition() const { return d->condition; } class RichMediaAnnotation::Deactivation::Private : public QSharedData { public: Private() : condition(RichMediaAnnotation::Deactivation::UserAction) { } RichMediaAnnotation::Deactivation::Condition condition; }; RichMediaAnnotation::Deactivation::Deactivation() : d(new Private) { } RichMediaAnnotation::Deactivation::~Deactivation() { } void RichMediaAnnotation::Deactivation::setCondition(Condition condition) { d->condition = condition; } RichMediaAnnotation::Deactivation::Condition RichMediaAnnotation::Deactivation::condition() const { return d->condition; } class RichMediaAnnotation::Settings::Private : public QSharedData { public: Private() : activation(nullptr), deactivation(nullptr) { } RichMediaAnnotation::Activation *activation; RichMediaAnnotation::Deactivation *deactivation; }; RichMediaAnnotation::Settings::Settings() : d(new Private) { } RichMediaAnnotation::Settings::~Settings() { } void RichMediaAnnotation::Settings::setActivation(RichMediaAnnotation::Activation *activation) { delete d->activation; d->activation = activation; } RichMediaAnnotation::Activation *RichMediaAnnotation::Settings::activation() const { return d->activation; } void RichMediaAnnotation::Settings::setDeactivation(RichMediaAnnotation::Deactivation *deactivation) { delete d->deactivation; d->deactivation = deactivation; } RichMediaAnnotation::Deactivation *RichMediaAnnotation::Settings::deactivation() const { return d->deactivation; } class RichMediaAnnotationPrivate : public AnnotationPrivate { public: RichMediaAnnotationPrivate() : settings(nullptr), content(nullptr) { } ~RichMediaAnnotationPrivate() override; Annotation *makeAlias() override { return new RichMediaAnnotation(*this); } Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override { Q_UNUSED(destPage); Q_UNUSED(doc); return nullptr; } RichMediaAnnotation::Settings *settings; RichMediaAnnotation::Content *content; }; RichMediaAnnotationPrivate::~RichMediaAnnotationPrivate() { delete settings; delete content; } RichMediaAnnotation::RichMediaAnnotation() : Annotation(*new RichMediaAnnotationPrivate()) { } RichMediaAnnotation::RichMediaAnnotation(RichMediaAnnotationPrivate &dd) : Annotation(dd) { } RichMediaAnnotation::~RichMediaAnnotation() { } Annotation::SubType RichMediaAnnotation::subType() const { return ARichMedia; } void RichMediaAnnotation::setSettings(RichMediaAnnotation::Settings *settings) { Q_D(RichMediaAnnotation); delete d->settings; d->settings = settings; } RichMediaAnnotation::Settings *RichMediaAnnotation::settings() const { Q_D(const RichMediaAnnotation); return d->settings; } void RichMediaAnnotation::setContent(RichMediaAnnotation::Content *content) { Q_D(RichMediaAnnotation); delete d->content; d->content = content; } RichMediaAnnotation::Content *RichMediaAnnotation::content() const { Q_D(const RichMediaAnnotation); return d->content; } // BEGIN utility annotation functions QColor convertAnnotColor(const AnnotColor *color) { if (!color) { return QColor(); } QColor newcolor; const double *color_data = color->getValues(); switch (color->getSpace()) { case AnnotColor::colorTransparent: // = 0, newcolor = Qt::transparent; break; case AnnotColor::colorGray: // = 1, newcolor.setRgbF(color_data[0], color_data[0], color_data[0]); break; case AnnotColor::colorRGB: // = 3, newcolor.setRgbF(color_data[0], color_data[1], color_data[2]); break; case AnnotColor::colorCMYK: // = 4 newcolor.setCmykF(color_data[0], color_data[1], color_data[2], color_data[3]); break; } return newcolor; } std::unique_ptr convertQColor(const QColor &c) { if (c.alpha() == 0) { return {}; // Transparent } switch (c.spec()) { case QColor::Rgb: case QColor::Hsl: case QColor::Hsv: return std::make_unique(c.redF(), c.greenF(), c.blueF()); case QColor::Cmyk: return std::make_unique(c.cyanF(), c.magentaF(), c.yellowF(), c.blackF()); case QColor::Invalid: default: return {}; } } // END utility annotation functions } poppler-24.02.0/qt6/src/poppler-annotation.h000066400000000000000000001125421455701731300206560ustar00rootroot00000000000000/* poppler-annotation.h: qt interface to poppler * Copyright (C) 2006-2008, 2012, 2013, 2018-2022 Albert Astals Cid * Copyright (C) 2006, 2008 Pino Toscano * Copyright (C) 2007, Brad Hards * Copyright (C) 2010, Philip Lorenz * Copyright (C) 2012, 2015, Tobias Koenig * Copyright (C) 2012, Guillermo A. Amaral B. * Copyright (C) 2012, 2013 Fabio D'Urso * 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) 2021, Oliver Sander * Copyright (C) 2021, Mahmoud Ahmed Khalil * Adapting code from * Copyright (C) 2004 by Enrico Ros * * 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_ANNOTATION_H_ #define _POPPLER_ANNOTATION_H_ #include #include #include #include #include #include #include #include #include #include "poppler-export.h" #include namespace Poppler { class Annotation; class AnnotationPrivate; class AnnotationAppearancePrivate; class TextAnnotationPrivate; class LineAnnotationPrivate; class GeomAnnotationPrivate; class HighlightAnnotationPrivate; class StampAnnotationPrivate; class InkAnnotationPrivate; class LinkAnnotationPrivate; class CaretAnnotationPrivate; class FileAttachmentAnnotationPrivate; class SoundAnnotationPrivate; class MovieAnnotationPrivate; class ScreenAnnotationPrivate; class WidgetAnnotationPrivate; class RichMediaAnnotationPrivate; class EmbeddedFile; class Link; class SoundObject; class MovieObject; class LinkRendition; class Page; /** * \short AnnotationAppearance class wrapping Poppler's AP stream object * * The Annotation's Appearance Stream is a Form XObject containing * information required to properly render the Annotation on the document. * * This class wraps Poppler's Object implementing the appearance stream * for the calling annotation. It can be used to preserve the current * Appearance Stream for the calling annotation. * * \since 21.10.0 */ class POPPLER_QT6_EXPORT AnnotationAppearance { friend class Annotation; public: explicit AnnotationAppearance(AnnotationAppearancePrivate *annotationAppearancePrivate); ~AnnotationAppearance(); private: AnnotationAppearancePrivate *d; Q_DISABLE_COPY(AnnotationAppearance) }; /** * \short Annotation class holding properties shared by all annotations. * * An Annotation is an object (text note, highlight, sound, popup window, ..) * contained by a Page in the document. * * \warning Different Annotation objects might point to the same annotation. * * \section annotCreation How to add annotations * * Create an Annotation object of the desired subclass (for example * TextAnnotation) and set its properties: * @code * Poppler::TextAnnotation* myann = new Poppler::TextAnnotation(Poppler::TextAnnotation::InPlace); * myann->setBoundary(QRectF(0.1, 0.1, 0.2, 0.2)); // normalized coordinates: (0,0) is top-left, (1,1) is bottom-right * myann->setContents("Hello, world!"); * @endcode * \note Always set a boundary rectangle, or nothing will be shown! * * Obtain a pointer to the Page where you want to add the annotation (refer to * \ref req for instructions) and add the annotation: * @code * Poppler::Page* mypage = ...; * mypage->addAnnotation(myann); * @endcode * * You can keep on editing the annotation after it has been added to the page: * @code * myann->setContents("World, hello!"); // Let's change text... * myann->setAuthor("Your name here"); // ...and set an author too * @endcode * * When you're done with editing the annotation, you must destroy the Annotation * object: * @code * delete myann; * @endcode * * Use the PDFConverter class to save the modified document. * * \section annotFixedRotation FixedRotation flag specifics * * According to the PDF specification, annotations whose * Annotation::FixedRotation flag is set must always be shown in their original * orientation, no matter what the current rendering rotation or the page's * Page::orientation() values are. In comparison with regular annotations, such * annotations should therefore be transformed by an extra rotation at rendering * time to "undo" such context-related rotations, which is equal to * -(rendering_rotation + page_orientation). The rotation pivot * is the top-left corner of the boundary rectangle. * * In practice, %Poppler's \ref Page::renderToImage only "unrotates" the * page orientation, and does not unrotate the rendering rotation. * This ensures consistent renderings at different Page::Rotation values: * annotations are always positioned as if they were being positioned at the * default page orientation. * * Just like regular annotations, %Poppler Qt6 exposes normalized coordinates * relative to the page's default orientation. However, behind the scenes, the * coordinate system is different and %Poppler transparently transforms each * shape. If you never call either Annotation::setFlags or * Annotation::setBoundary, you don't need to worry about this; but if you do * call them, then you need to adhere to the following rules: * - Whenever you toggle the Annotation::FixedRotation flag, you must * set again the boundary rectangle first, and then you must set * again any other geometry-related property. * - Whenever you modify the boundary rectangle of an annotation whose * Annotation::FixedRotation flag is set, you must set again any other * geometry-related property. * * These two rules are necessary to make %Poppler's transparent coordinate * conversion work properly. */ class POPPLER_QT6_EXPORT Annotation { friend class LinkMovie; friend class LinkRendition; public: // enum definitions /** * Annotation subclasses * * \sa subType() */ // WARNING!!! oKular uses that very same values so if you change them notify the author! enum SubType { AText = 1, ///< TextAnnotation ALine = 2, ///< LineAnnotation AGeom = 3, ///< GeomAnnotation AHighlight = 4, ///< HighlightAnnotation AStamp = 5, ///< StampAnnotation AInk = 6, ///< InkAnnotation ALink = 7, ///< LinkAnnotation ACaret = 8, ///< CaretAnnotation AFileAttachment = 9, ///< FileAttachmentAnnotation ASound = 10, ///< SoundAnnotation AMovie = 11, ///< MovieAnnotation AScreen = 12, ///< ScreenAnnotation AWidget = 13, ///< WidgetAnnotation ARichMedia = 14 ///< RichMediaAnnotation }; /** * Annotation flags * * They can be OR'd together (e.g. Annotation::FixedRotation | Annotation::DenyPrint). * * \sa flags(), setFlags() */ // NOTE: Only flags that are known to work are documented enum Flag { Hidden = 1, ///< Do not display or print the annotation FixedSize = 2, FixedRotation = 4, ///< Do not rotate the annotation according to page orientation and rendering rotation \warning Extra care is needed with this flag: see \ref annotFixedRotation DenyPrint = 8, ///< Do not print the annotation DenyWrite = 16, DenyDelete = 32, ToggleHidingOnMouse = 64, External = 128 }; Q_DECLARE_FLAGS(Flags, Flag) enum LineStyle { Solid = 1, Dashed = 2, Beveled = 4, Inset = 8, Underline = 16 }; enum LineEffect { NoEffect = 1, Cloudy = 2 }; enum RevScope { Root = 0, Reply = 1, Group = 2, Delete = 4 }; enum RevType { None = 1, Marked = 2, Unmarked = 4, Accepted = 8, Rejected = 16, Cancelled = 32, Completed = 64 }; /** * Returns the author of the annotation. */ QString author() const; /** * Sets a new author for the annotation. */ void setAuthor(const QString &author); QString contents() const; void setContents(const QString &contents); /** * Returns the unique name (ID) of the annotation. */ QString uniqueName() const; /** * Sets a new unique name for the annotation. * * \note no check of the new uniqueName is done */ void setUniqueName(const QString &uniqueName); QDateTime modificationDate() const; void setModificationDate(const QDateTime &date); QDateTime creationDate() const; void setCreationDate(const QDateTime &date); /** * Returns this annotation's flags * * \sa Flag, setFlags() */ Flags flags() const; /** * Sets this annotation's flags * * \sa Flag, flags(), \ref annotFixedRotation */ void setFlags(Flags flags); /** * Returns this annotation's boundary rectangle in normalized coordinates * * \sa setBoundary(const QRectF&) */ QRectF boundary() const; /** * Sets this annotation's boundary rectangle * * The boundary rectangle is the smallest rectangle that contains the * annotation. * * \warning This property is mandatory: you must always set this. * * \sa boundary(), \ref annotFixedRotation */ void setBoundary(const QRectF &boundary); /** * \short Container class for Annotation style information */ class POPPLER_QT6_EXPORT Style { public: Style(); Style(const Style &other); Style &operator=(const Style &other); ~Style(); // appearance properties QColor color() const; // black void setColor(const QColor &color); double opacity() const; // 1.0 void setOpacity(double opacity); // pen properties double width() const; // 1.0 void setWidth(double width); LineStyle lineStyle() const; // LineStyle::Solid void setLineStyle(LineStyle style); double xCorners() const; // 0.0 void setXCorners(double radius); double yCorners() const; // 0.0 void setYCorners(double radius); const QVector &dashArray() const; // [ 3 ] void setDashArray(const QVector &array); // pen effects LineEffect lineEffect() const; // LineEffect::NoEffect void setLineEffect(LineEffect effect); double effectIntensity() const; // 1.0 void setEffectIntensity(double intens); private: class Private; QSharedDataPointer d; }; Style style() const; void setStyle(const Style &style); /** * \short Container class for Annotation pop-up window information */ class POPPLER_QT6_EXPORT Popup { public: Popup(); Popup(const Popup &other); Popup &operator=(const Popup &other); ~Popup(); // window state (Hidden, FixedRotation, Deny* flags allowed) int flags() const; // -1 (never initialized) -> 0 (if inited and shown) void setFlags(int flags); // geometric properties QRectF geometry() const; // no default void setGeometry(const QRectF &geom); // window contents/override properties QString title() const; // '' text in the titlebar (overrides author) void setTitle(const QString &title); QString summary() const; // '' short description (displayed if not empty) void setSummary(const QString &summary); QString text() const; // '' text for the window (overrides annot->contents) void setText(const QString &text); private: class Private; QSharedDataPointer d; }; Popup popup() const; /// \warning Currently does nothing \since 0.20 void setPopup(const Popup &popup); RevScope revisionScope() const; // Root RevType revisionType() const; // None /** * Returns the revisions of this annotation */ std::vector> revisions() const; /** * The type of the annotation. */ virtual SubType subType() const = 0; /** * Returns the current appearance stream of this annotation. * * \since 21.10.0 */ std::unique_ptr annotationAppearance() const; /** * Sets the annotation's appearance stream with the @p annotationAppearance. * * \since 21.10.0 */ void setAnnotationAppearance(const AnnotationAppearance &annotationAppearance); /** * Destructor. */ virtual ~Annotation(); /** * Describes the flags from an annotations 'AA' dictionary. * * This flag is used by the additionalAction() method for ScreenAnnotation * and WidgetAnnotation. */ enum AdditionalActionType { CursorEnteringAction, ///< Performed when the cursor enters the annotation's active area CursorLeavingAction, ///< Performed when the cursor exists the annotation's active area MousePressedAction, ///< Performed when the mouse button is pressed inside the annotation's active area MouseReleasedAction, ///< Performed when the mouse button is released inside the annotation's active area FocusInAction, ///< Performed when the annotation receives the input focus FocusOutAction, ///< Performed when the annotation loses the input focus PageOpeningAction, ///< Performed when the page containing the annotation is opened PageClosingAction, ///< Performed when the page containing the annotation is closed PageVisibleAction, ///< Performed when the page containing the annotation becomes visible PageInvisibleAction ///< Performed when the page containing the annotation becomes invisible }; protected: /// \cond PRIVATE explicit Annotation(AnnotationPrivate &dd); Q_DECLARE_PRIVATE(Annotation) QExplicitlySharedDataPointer d_ptr; /// \endcond private: Q_DISABLE_COPY(Annotation) }; /** * \short Annotation containing text. * * A text annotation is an object showing some text directly on the page, or * linked to the contents using an icon shown on a page. */ class POPPLER_QT6_EXPORT TextAnnotation : public Annotation { friend class AnnotationPrivate; public: // local enums enum TextType { Linked, InPlace }; enum InplaceIntent { Unknown, Callout, TypeWriter }; enum InplaceAlignPosition { InplaceAlignLeft, InplaceAlignCenter, InplaceAlignRight }; explicit TextAnnotation(TextType type); ~TextAnnotation() override; SubType subType() const override; /** The type of text annotation represented by this object */ TextType textType() const; /** The name of the icon for this text annotation. Standard names for text annotation icons are: - Comment - Help - Insert - Key - NewParagraph - Note (this is the default icon to use) - Paragraph */ QString textIcon() const; /** Set the name of the icon to use for this text annotation. \sa textIcon for the list of standard names */ void setTextIcon(const QString &icon); QFont textFont() const; void setTextFont(const QFont &font); /// Default text color is black QColor textColor() const; void setTextColor(const QColor &color); InplaceAlignPosition inplaceAlign() const; void setInplaceAlign(InplaceAlignPosition align); QPointF calloutPoint(int id) const; QVector calloutPoints() const; void setCalloutPoints(const QVector &points); InplaceIntent inplaceIntent() const; void setInplaceIntent(InplaceIntent intent); private: explicit TextAnnotation(TextAnnotationPrivate &dd); void setTextType(TextType type); Q_DECLARE_PRIVATE(TextAnnotation) Q_DISABLE_COPY(TextAnnotation) }; /** * \short Polygon/polyline annotation. * * This annotation represents a polygon (or polyline) to be drawn on a page. */ class POPPLER_QT6_EXPORT LineAnnotation : public Annotation { friend class AnnotationPrivate; public: // local enums enum LineType { StraightLine, Polyline }; enum TermStyle { Square, Circle, Diamond, OpenArrow, ClosedArrow, None, Butt, ROpenArrow, RClosedArrow, Slash }; enum LineIntent { Unknown, Arrow, Dimension, PolygonCloud }; explicit LineAnnotation(LineType type); ~LineAnnotation() override; SubType subType() const override; LineType lineType() const; QVector linePoints() const; void setLinePoints(const QVector &points); TermStyle lineStartStyle() const; void setLineStartStyle(TermStyle style); TermStyle lineEndStyle() const; void setLineEndStyle(TermStyle style); bool isLineClosed() const; void setLineClosed(bool closed); QColor lineInnerColor() const; void setLineInnerColor(const QColor &color); double lineLeadingForwardPoint() const; void setLineLeadingForwardPoint(double point); double lineLeadingBackPoint() const; void setLineLeadingBackPoint(double point); bool lineShowCaption() const; void setLineShowCaption(bool show); LineIntent lineIntent() const; void setLineIntent(LineIntent intent); private: explicit LineAnnotation(LineAnnotationPrivate &dd); void setLineType(LineType type); Q_DECLARE_PRIVATE(LineAnnotation) Q_DISABLE_COPY(LineAnnotation) }; /** * \short Geometric annotation. * * The geometric annotation represents a geometric figure, like a rectangle or * an ellipse. */ class POPPLER_QT6_EXPORT GeomAnnotation : public Annotation { friend class AnnotationPrivate; public: GeomAnnotation(); ~GeomAnnotation() override; SubType subType() const override; // common enums enum GeomType { InscribedSquare, InscribedCircle }; GeomType geomType() const; void setGeomType(GeomType type); QColor geomInnerColor() const; void setGeomInnerColor(const QColor &color); private: explicit GeomAnnotation(GeomAnnotationPrivate &dd); Q_DECLARE_PRIVATE(GeomAnnotation) Q_DISABLE_COPY(GeomAnnotation) }; /** * \short Text highlight annotation. * * The highlight annotation represents some areas of text being "highlighted". */ class POPPLER_QT6_EXPORT HighlightAnnotation : public Annotation { friend class AnnotationPrivate; public: HighlightAnnotation(); ~HighlightAnnotation() override; SubType subType() const override; /** The type of highlight */ enum HighlightType { Highlight, ///< highlighter pen style annotation Squiggly, ///< jagged or squiggly underline Underline, ///< straight line underline StrikeOut ///< straight line through-line }; /** Structure corresponding to a QuadPoints array. This matches a quadrilateral that describes the area around a word (or set of words) that are to be highlighted. */ struct Quad { QPointF points[4]; // 8 valid coords bool capStart; // false (vtx 1-4) [K] bool capEnd; // false (vtx 2-3) [K] double feather; // 0.1 (in range 0..1) [K] }; /** The type (style) of highlighting to use for this area or these areas. */ HighlightType highlightType() const; /** Set the type of highlighting to use for the given area or areas. */ void setHighlightType(HighlightType type); /** The list of areas to highlight. */ QList highlightQuads() const; /** Set the areas to highlight. */ void setHighlightQuads(const QList &quads); private: explicit HighlightAnnotation(HighlightAnnotationPrivate &dd); Q_DECLARE_PRIVATE(HighlightAnnotation) Q_DISABLE_COPY(HighlightAnnotation) }; /** * \short Stamp annotation. * * A simple annotation drawing a stamp on a page. */ class POPPLER_QT6_EXPORT StampAnnotation : public Annotation { friend class AnnotationPrivate; public: StampAnnotation(); ~StampAnnotation() override; SubType subType() const override; /** The name of the icon for this stamp annotation. Standard names for stamp annotation icons are: - Approved - AsIs - Confidential - Departmental - Draft (this is the default icon type) - Experimental - Expired - Final - ForComment - ForPublicRelease - NotApproved - NotForPublicRelease - Sold - TopSecret */ QString stampIconName() const; /** Set the icon type for this stamp annotation. \sa stampIconName for the list of standard icon names */ void setStampIconName(const QString &name); /** Set a custom icon for this stamp annotation. \since 21.10.0 */ void setStampCustomImage(const QImage &image); private: explicit StampAnnotation(StampAnnotationPrivate &dd); Q_DECLARE_PRIVATE(StampAnnotation) Q_DISABLE_COPY(StampAnnotation) }; /** * \short Ink Annotation. * * Annotation representing an ink path on a page. */ class POPPLER_QT6_EXPORT InkAnnotation : public Annotation { friend class AnnotationPrivate; public: InkAnnotation(); ~InkAnnotation() override; SubType subType() const override; QList> inkPaths() const; void setInkPaths(const QList> &paths); private: explicit InkAnnotation(InkAnnotationPrivate &dd); Q_DECLARE_PRIVATE(InkAnnotation) Q_DISABLE_COPY(InkAnnotation) }; class POPPLER_QT6_EXPORT LinkAnnotation : public Annotation { friend class AnnotationPrivate; public: ~LinkAnnotation() override; SubType subType() const override; // local enums enum HighlightMode { None, Invert, Outline, Push }; Link *linkDestination() const; void setLinkDestination(std::unique_ptr &&link); HighlightMode linkHighlightMode() const; void setLinkHighlightMode(HighlightMode mode); QPointF linkRegionPoint(int id) const; void setLinkRegionPoint(int id, const QPointF point); private: LinkAnnotation(); explicit LinkAnnotation(LinkAnnotationPrivate &dd); Q_DECLARE_PRIVATE(LinkAnnotation) Q_DISABLE_COPY(LinkAnnotation) }; /** * \short Caret annotation. * * The caret annotation represents a symbol to indicate the presence of text. */ class POPPLER_QT6_EXPORT CaretAnnotation : public Annotation { friend class AnnotationPrivate; public: CaretAnnotation(); ~CaretAnnotation() override; SubType subType() const override; /** * The symbols for the caret annotation. */ enum CaretSymbol { None, P }; CaretSymbol caretSymbol() const; void setCaretSymbol(CaretSymbol symbol); private: explicit CaretAnnotation(CaretAnnotationPrivate &dd); Q_DECLARE_PRIVATE(CaretAnnotation) Q_DISABLE_COPY(CaretAnnotation) }; /** * \short File attachment annotation. * * The file attachment annotation represents a file embedded in the document. */ class POPPLER_QT6_EXPORT FileAttachmentAnnotation : public Annotation { friend class AnnotationPrivate; public: ~FileAttachmentAnnotation() override; SubType subType() const override; /** * Returns the name of the icon of this annotation. */ QString fileIconName() const; /** * Sets a new name for the icon of this annotation. */ void setFileIconName(const QString &icon); /** * Returns the EmbeddedFile of this annotation. */ EmbeddedFile *embeddedFile() const; /** * Sets a new EmbeddedFile for this annotation. * * \note FileAttachmentAnnotation takes ownership of the object */ void setEmbeddedFile(EmbeddedFile *ef); private: FileAttachmentAnnotation(); explicit FileAttachmentAnnotation(FileAttachmentAnnotationPrivate &dd); Q_DECLARE_PRIVATE(FileAttachmentAnnotation) Q_DISABLE_COPY(FileAttachmentAnnotation) }; /** * \short Sound annotation. * * The sound annotation represents a sound to be played when activated. */ class POPPLER_QT6_EXPORT SoundAnnotation : public Annotation { friend class AnnotationPrivate; public: ~SoundAnnotation() override; SubType subType() const override; /** * Returns the name of the icon of this annotation. */ QString soundIconName() const; /** * Sets a new name for the icon of this annotation. */ void setSoundIconName(const QString &icon); /** * Returns the SoundObject of this annotation. */ SoundObject *sound() const; /** * Sets a new SoundObject for this annotation. * * \note SoundAnnotation takes ownership of the object */ void setSound(SoundObject *s); private: SoundAnnotation(); explicit SoundAnnotation(SoundAnnotationPrivate &dd); Q_DECLARE_PRIVATE(SoundAnnotation) Q_DISABLE_COPY(SoundAnnotation) }; /** * \short Movie annotation. * * The movie annotation represents a movie to be played when activated. */ class POPPLER_QT6_EXPORT MovieAnnotation : public Annotation { friend class AnnotationPrivate; public: ~MovieAnnotation() override; SubType subType() const override; /** * Returns the MovieObject of this annotation. */ MovieObject *movie() const; /** * Sets a new MovieObject for this annotation. * * \note MovieAnnotation takes ownership of the object */ void setMovie(MovieObject *movie); /** * Returns the title of the movie of this annotation. */ QString movieTitle() const; /** * Sets a new title for the movie of this annotation. */ void setMovieTitle(const QString &title); private: MovieAnnotation(); explicit MovieAnnotation(MovieAnnotationPrivate &dd); Q_DECLARE_PRIVATE(MovieAnnotation) Q_DISABLE_COPY(MovieAnnotation) }; /** * \short Screen annotation. * * The screen annotation represents a screen to be played when activated. */ class POPPLER_QT6_EXPORT ScreenAnnotation : public Annotation { friend class AnnotationPrivate; public: ~ScreenAnnotation() override; SubType subType() const override; /** * Returns the LinkRendition of this annotation. */ LinkRendition *action() const; /** * Sets a new LinkRendition for this annotation. * * \note ScreenAnnotation takes ownership of the object */ void setAction(LinkRendition *action); /** * Returns the title of the screen of this annotation. */ QString screenTitle() const; /** * Sets a new title for the screen of this annotation. */ void setScreenTitle(const QString &title); /** * Returns the additional action of the given @p type for the annotation or * @c 0 if no action has been defined. */ std::unique_ptr additionalAction(AdditionalActionType type) const; private: ScreenAnnotation(); explicit ScreenAnnotation(ScreenAnnotationPrivate &dd); Q_DECLARE_PRIVATE(ScreenAnnotation) Q_DISABLE_COPY(ScreenAnnotation) }; /** * \short Widget annotation. * * The widget annotation represents a widget (form field) on a page. * * \note This class is just provided for consistency of the annotation API, * use the FormField classes to get all the form-related information. */ class POPPLER_QT6_EXPORT WidgetAnnotation : public Annotation { friend class AnnotationPrivate; public: ~WidgetAnnotation() override; SubType subType() const override; /** * Returns the additional action of the given @p type for the annotation or * @c 0 if no action has been defined. */ std::unique_ptr additionalAction(AdditionalActionType type) const; private: WidgetAnnotation(); explicit WidgetAnnotation(WidgetAnnotationPrivate &dd); Q_DECLARE_PRIVATE(WidgetAnnotation) Q_DISABLE_COPY(WidgetAnnotation) }; /** * \short RichMedia annotation. * * The RichMedia annotation represents a video or sound on a page. */ class POPPLER_QT6_EXPORT RichMediaAnnotation : public Annotation { friend class AnnotationPrivate; public: ~RichMediaAnnotation() override; SubType subType() const override; /** * The params object of a RichMediaAnnotation::Instance object. * * The params object provides media specific parameters, to play * back the media inside the PDF viewer. * * At the moment only parameters for flash player are supported. */ class POPPLER_QT6_EXPORT Params { friend class AnnotationPrivate; public: Params(); ~Params(); /** * Returns the parameters for the flash player. */ QString flashVars() const; private: void setFlashVars(const QString &flashVars); class Private; QScopedPointer d; }; /** * The instance object of a RichMediaAnnotation::Configuration object. * * The instance object represents one media object, that should be shown * on the page. It has a media type and a Params object, to define the * media specific parameters. */ class POPPLER_QT6_EXPORT Instance { friend class AnnotationPrivate; public: /** * Describes the media type of the instance. */ enum Type { Type3D, ///< A 3D media file. TypeFlash, ///< A Flash media file. TypeSound, ///< A sound media file. TypeVideo ///< A video media file. }; Instance(); ~Instance(); /** * Returns the media type of the instance. */ Type type() const; /** * Returns the params object of the instance or @c 0 if it doesn't exist. */ RichMediaAnnotation::Params *params() const; private: void setType(Type type); void setParams(RichMediaAnnotation::Params *params); class Private; QScopedPointer d; }; /** * The configuration object of a RichMediaAnnotation::Content object. * * The configuration object provides access to the various Instance objects * of the rich media annotation. */ class POPPLER_QT6_EXPORT Configuration { friend class AnnotationPrivate; public: /** * Describes the media type of the configuration. */ enum Type { Type3D, ///< A 3D media file. TypeFlash, ///< A Flash media file. TypeSound, ///< A sound media file. TypeVideo ///< A video media file. }; Configuration(); ~Configuration(); /** * Returns the media type of the configuration. */ Type type() const; /** * Returns the name of the configuration. */ QString name() const; /** * Returns the list of Instance objects of the configuration. */ QList instances() const; private: void setType(Type type); void setName(const QString &name); void setInstances(const QList &instances); class Private; QScopedPointer d; }; /** * The asset object of a RichMediaAnnotation::Content object. * * The asset object provides a mapping between identifier name, as * used in the flash vars string of RichMediaAnnotation::Params, and the * associated file spec object. */ class POPPLER_QT6_EXPORT Asset { friend class AnnotationPrivate; public: Asset(); ~Asset(); /** * Returns the identifier name of the asset. */ QString name() const; /** * Returns the embedded file the asset points to. */ EmbeddedFile *embeddedFile() const; private: void setName(const QString &name); void setEmbeddedFile(EmbeddedFile *embeddedFile); class Private; QScopedPointer d; }; /** * The content object of a RichMediaAnnotation. * * The content object provides access to the list of configurations * and assets of the rich media annotation. */ class POPPLER_QT6_EXPORT Content { friend class AnnotationPrivate; public: Content(); ~Content(); /** * Returns the list of configuration objects of the content object. */ QList configurations() const; /** * Returns the list of asset objects of the content object. */ QList assets() const; private: void setConfigurations(const QList &configurations); void setAssets(const QList &assets); class Private; QScopedPointer d; }; /** * The activation object of the RichMediaAnnotation::Settings object. * * The activation object is a wrapper around the settings for the activation * state. At the moment it provides only the activation condition. */ class POPPLER_QT6_EXPORT Activation { friend class AnnotationPrivate; public: /** * Describes the condition for activating the rich media. */ enum Condition { PageOpened, ///< Activate when page is opened. PageVisible, ///< Activate when page becomes visible. UserAction ///< Activate when user interacts with the annotation. }; Activation(); ~Activation(); /** * Returns the activation condition. */ Condition condition() const; private: void setCondition(Condition condition); class Private; QScopedPointer d; }; /** * The deactivation object of the RichMediaAnnotation::Settings object. * * The deactivation object is a wrapper around the settings for the deactivation * state. At the moment it provides only the deactivation condition. */ class POPPLER_QT6_EXPORT Deactivation { friend class AnnotationPrivate; public: /** * Describes the condition for deactivating the rich media. */ enum Condition { PageClosed, ///< Deactivate when page is closed. PageInvisible, ///< Deactivate when page becomes invisible. UserAction ///< Deactivate when user interacts with the annotation. }; Deactivation(); ~Deactivation(); /** * Returns the deactivation condition. */ Condition condition() const; private: void setCondition(Condition condition); class Private; QScopedPointer d; }; /** * The settings object of a RichMediaAnnotation. * * The settings object provides access to the configuration objects * for annotation activation and deactivation. */ class POPPLER_QT6_EXPORT Settings { friend class AnnotationPrivate; public: Settings(); ~Settings(); /** * Returns the Activation object of the settings object or @c 0 if it doesn't exist. */ RichMediaAnnotation::Activation *activation() const; /** * Returns the Deactivation object of the settings object or @c 0 if it doesn't exist. */ RichMediaAnnotation::Deactivation *deactivation() const; private: void setActivation(RichMediaAnnotation::Activation *activation); void setDeactivation(RichMediaAnnotation::Deactivation *deactivation); class Private; QScopedPointer d; }; /** * Returns the Settings object of the rich media annotation or @c 0 if it doesn't exist. */ RichMediaAnnotation::Settings *settings() const; /** * Returns the Content object of the rich media annotation or @c 0 if it doesn't exist. */ RichMediaAnnotation::Content *content() const; private: void setSettings(RichMediaAnnotation::Settings *settings); void setContent(RichMediaAnnotation::Content *content); RichMediaAnnotation(); explicit RichMediaAnnotation(RichMediaAnnotationPrivate &dd); Q_DECLARE_PRIVATE(RichMediaAnnotation) Q_DISABLE_COPY(RichMediaAnnotation) }; } #endif poppler-24.02.0/qt6/src/poppler-base-converter.cc000066400000000000000000000043771455701731300215670ustar00rootroot00000000000000/* poppler-base-converter.cc: qt interface to poppler * Copyright (C) 2007, 2009, Albert Astals Cid * Copyright (C) 2008, Pino Toscano * * 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 "poppler-qt6.h" #include "poppler-converter-private.h" #include namespace Poppler { BaseConverterPrivate::BaseConverterPrivate() : document(nullptr), iodev(nullptr), ownIodev(true) { } BaseConverterPrivate::~BaseConverterPrivate() { } QIODevice *BaseConverterPrivate::openDevice() { if (!iodev) { Q_ASSERT(!outputFileName.isEmpty()); QFile *f = new QFile(outputFileName); iodev = f; ownIodev = true; } Q_ASSERT(iodev); if (!iodev->isOpen()) { if (!iodev->open(QIODevice::WriteOnly)) { if (ownIodev) { delete iodev; iodev = nullptr; } else { return nullptr; } } } return iodev; } void BaseConverterPrivate::closeDevice() { if (ownIodev) { iodev->close(); delete iodev; iodev = nullptr; } } BaseConverter::BaseConverter(BaseConverterPrivate &dd) : d_ptr(&dd) { } BaseConverter::~BaseConverter() { delete d_ptr; } void BaseConverter::setOutputFileName(const QString &outputFileName) { Q_D(BaseConverter); d->outputFileName = outputFileName; } void BaseConverter::setOutputDevice(QIODevice *device) { Q_D(BaseConverter); d->iodev = device; d->ownIodev = false; } BaseConverter::Error BaseConverter::lastError() const { Q_D(const BaseConverter); return d->lastError; } } poppler-24.02.0/qt6/src/poppler-converter-private.h000066400000000000000000000027421455701731300221630ustar00rootroot00000000000000/* poppler-converter-private.h: Qt interface to poppler * Copyright (C) 2007, 2009, 2018, Albert Astals Cid * Copyright (C) 2008, Pino Toscano * * 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_QT6_CONVERTER_PRIVATE_H #define POPPLER_QT6_CONVERTER_PRIVATE_H #include class QIODevice; namespace Poppler { class DocumentData; class BaseConverterPrivate { public: BaseConverterPrivate(); virtual ~BaseConverterPrivate(); BaseConverterPrivate(const BaseConverterPrivate &) = delete; BaseConverterPrivate &operator=(const BaseConverterPrivate &) = delete; QIODevice *openDevice(); void closeDevice(); DocumentData *document; QString outputFileName; QIODevice *iodev; bool ownIodev : 1; BaseConverter::Error lastError; }; } #endif poppler-24.02.0/qt6/src/poppler-document.cc000066400000000000000000000551301455701731300204570ustar00rootroot00000000000000/* 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) 2006-2010, Pino Toscano * Copyright (C) 2010, 2011 Hib Eris * Copyright (C) 2012 Koji Otani * Copyright (C) 2012, 2013 Thomas Freitag * Copyright (C) 2012 Fabio D'Urso * Copyright (C) 2014, 2018, 2020 Adam Reichold * Copyright (C) 2015 William Bader * Copyright (C) 2016 Jakub Alba * Copyright (C) 2017, 2021 Adrian Johnson * Copyright (C) 2017 Suzuki Toshiya * 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-2021 Oliver Sander * Copyright (C) 2019 Alexander Volkov * Copyright (C) 2020 Philipp Knechtges * Copyright (C) 2020 Katarina Behrens * Copyright (C) 2020 Thorsten Behrens * Copyright (C) 2021 Mahmoud Khalil * Copyright (C) 2021 Hubert Figuiere * * 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 "poppler-qt6.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "poppler-form.h" #include "poppler-private.h" #include "poppler-page-private.h" #include "poppler-outline-private.h" #if defined(USE_CMS) # include #endif namespace Poppler { std::unique_ptr Document::load(const QString &filePath, const QByteArray &ownerPassword, const QByteArray &userPassword) { DocumentData *doc = new DocumentData(filePath, GooString(ownerPassword.data()), GooString(userPassword.data())); return DocumentData::checkDocument(doc); } std::unique_ptr Document::load(QIODevice *device, const QByteArray &ownerPassword, const QByteArray &userPassword) { DocumentData *doc = new DocumentData(device, GooString(ownerPassword.data()), GooString(userPassword.data())); return DocumentData::checkDocument(doc); } std::unique_ptr Document::loadFromData(const QByteArray &fileContents, const QByteArray &ownerPassword, const QByteArray &userPassword) { // create stream DocumentData *doc = new DocumentData(fileContents, GooString(ownerPassword.data()), GooString(userPassword.data())); return DocumentData::checkDocument(doc); } std::unique_ptr DocumentData::checkDocument(DocumentData *doc) { if (doc->doc->isOk() || doc->doc->getErrorCode() == errEncrypted) { auto pdoc = std::unique_ptr(new Document(doc)); if (doc->doc->getErrorCode() == errEncrypted) { pdoc->m_doc->locked = true; } else { pdoc->m_doc->locked = false; pdoc->m_doc->fillMembers(); } return pdoc; } else { delete doc; } return nullptr; } Document::Document(DocumentData *dataA) { m_doc = dataA; } Document::~Document() { delete m_doc; } std::unique_ptr Document::page(int index) const { // Cannot call std::make_unique, because the constructor of Page is private auto page = std::unique_ptr(new Page(m_doc, index)); if (page->m_page->page == nullptr) { page.reset(); } return page; } bool Document::isLocked() const { return m_doc->locked; } bool Document::unlock(const QByteArray &ownerPassword, const QByteArray &userPassword) { if (m_doc->locked) { /* racier then it needs to be */ DocumentData *doc2; if (!m_doc->fileContents.isEmpty()) { doc2 = new DocumentData(m_doc->fileContents, GooString(ownerPassword.data()), GooString(userPassword.data())); } else if (m_doc->m_device) { doc2 = new DocumentData(m_doc->m_device, GooString(ownerPassword.data()), GooString(userPassword.data())); } else { doc2 = new DocumentData(m_doc->m_filePath, GooString(ownerPassword.data()), GooString(userPassword.data())); } if (!doc2->doc->isOk()) { delete doc2; } else { delete m_doc; m_doc = doc2; m_doc->locked = false; m_doc->fillMembers(); } } return m_doc->locked; } Document::PageMode Document::pageMode() const { switch (m_doc->doc->getCatalog()->getPageMode()) { case Catalog::pageModeNone: return UseNone; case Catalog::pageModeOutlines: return UseOutlines; case Catalog::pageModeThumbs: return UseThumbs; case Catalog::pageModeFullScreen: return FullScreen; case Catalog::pageModeOC: return UseOC; case Catalog::pageModeAttach: return UseAttach; default: return UseNone; } } Document::PageLayout Document::pageLayout() const { switch (m_doc->doc->getCatalog()->getPageLayout()) { case Catalog::pageLayoutNone: return NoLayout; case Catalog::pageLayoutSinglePage: return SinglePage; case Catalog::pageLayoutOneColumn: return OneColumn; case Catalog::pageLayoutTwoColumnLeft: return TwoColumnLeft; case Catalog::pageLayoutTwoColumnRight: return TwoColumnRight; case Catalog::pageLayoutTwoPageLeft: return TwoPageLeft; case Catalog::pageLayoutTwoPageRight: return TwoPageRight; default: return NoLayout; } } Qt::LayoutDirection Document::textDirection() const { if (!m_doc->doc->getCatalog()->getViewerPreferences()) { return Qt::LayoutDirectionAuto; } switch (m_doc->doc->getCatalog()->getViewerPreferences()->getDirection()) { case ViewerPreferences::directionL2R: return Qt::LeftToRight; case ViewerPreferences::directionR2L: return Qt::RightToLeft; default: return Qt::LayoutDirectionAuto; } } int Document::numPages() const { return m_doc->doc->getNumPages(); } QList Document::fonts() const { QList ourList; FontIterator it(0, m_doc); while (it.hasNext()) { ourList += it.next(); } return ourList; } QList Document::embeddedFiles() const { return m_doc->m_embeddedFiles; } std::unique_ptr Document::newFontIterator(int startPage) const { // Cannot use std::make_unique, because the FontIterator constructor is private return std::unique_ptr(new FontIterator(startPage, m_doc)); } QByteArray Document::fontData(const FontInfo &fi) const { QByteArray result; if (fi.isEmbedded()) { XRef *xref = m_doc->doc->getXRef()->copy(); Object refObj(fi.m_data->embRef); Object strObj = refObj.fetch(xref); if (strObj.isStream()) { int c; strObj.streamReset(); while ((c = strObj.streamGetChar()) != EOF) { result.append((char)c); } strObj.streamClose(); } delete xref; } return result; } QString Document::info(const QString &type) const { if (m_doc->locked) { return QString(); } std::unique_ptr goo(m_doc->doc->getDocInfoStringEntry(type.toLatin1().constData())); return UnicodeParsedString(goo.get()); } bool Document::setInfo(const QString &key, const QString &val) { if (m_doc->locked) { return false; } GooString *goo = QStringToUnicodeGooString(val); m_doc->doc->setDocInfoStringEntry(key.toLatin1().constData(), goo); return true; } QString Document::title() const { if (m_doc->locked) { return QString(); } std::unique_ptr goo(m_doc->doc->getDocInfoTitle()); return UnicodeParsedString(goo.get()); } bool Document::setTitle(const QString &val) { if (m_doc->locked) { return false; } m_doc->doc->setDocInfoTitle(QStringToUnicodeGooString(val)); return true; } QString Document::author() const { if (m_doc->locked) { return QString(); } std::unique_ptr goo(m_doc->doc->getDocInfoAuthor()); return UnicodeParsedString(goo.get()); } bool Document::setAuthor(const QString &val) { if (m_doc->locked) { return false; } m_doc->doc->setDocInfoAuthor(QStringToUnicodeGooString(val)); return true; } QString Document::subject() const { if (m_doc->locked) { return QString(); } std::unique_ptr goo(m_doc->doc->getDocInfoSubject()); return UnicodeParsedString(goo.get()); } bool Document::setSubject(const QString &val) { if (m_doc->locked) { return false; } m_doc->doc->setDocInfoSubject(QStringToUnicodeGooString(val)); return true; } QString Document::keywords() const { if (m_doc->locked) { return QString(); } std::unique_ptr goo(m_doc->doc->getDocInfoKeywords()); return UnicodeParsedString(goo.get()); } bool Document::setKeywords(const QString &val) { if (m_doc->locked) { return false; } m_doc->doc->setDocInfoKeywords(QStringToUnicodeGooString(val)); return true; } QString Document::creator() const { if (m_doc->locked) { return QString(); } std::unique_ptr goo(m_doc->doc->getDocInfoCreator()); return UnicodeParsedString(goo.get()); } bool Document::setCreator(const QString &val) { if (m_doc->locked) { return false; } m_doc->doc->setDocInfoCreator(QStringToUnicodeGooString(val)); return true; } QString Document::producer() const { if (m_doc->locked) { return QString(); } std::unique_ptr goo(m_doc->doc->getDocInfoProducer()); return UnicodeParsedString(goo.get()); } bool Document::setProducer(const QString &val) { if (m_doc->locked) { return false; } m_doc->doc->setDocInfoProducer(QStringToUnicodeGooString(val)); return true; } bool Document::removeInfo() { if (m_doc->locked) { return false; } m_doc->doc->removeDocInfo(); return true; } QStringList Document::infoKeys() const { QStringList keys; if (m_doc->locked) { return QStringList(); } QScopedPointer xref(m_doc->doc->getXRef()->copy()); if (!xref) { return QStringList(); } Object info = xref->getDocInfo(); if (!info.isDict()) { return QStringList(); } Dict *infoDict = info.getDict(); // somehow iterate over keys in infoDict keys.reserve(infoDict->getLength()); for (int i = 0; i < infoDict->getLength(); ++i) { keys.append(QString::fromLatin1(infoDict->getKey(i))); } return keys; } QDateTime Document::date(const QString &type) const { if (m_doc->locked) { return QDateTime(); } std::unique_ptr goo(m_doc->doc->getDocInfoStringEntry(type.toLatin1().constData())); QString str = UnicodeParsedString(goo.get()); return Poppler::convertDate(str.toLatin1().constData()); } bool Document::setDate(const QString &key, const QDateTime &val) { if (m_doc->locked) { return false; } m_doc->doc->setDocInfoStringEntry(key.toLatin1().constData(), QDateTimeToUnicodeGooString(val)); return true; } QDateTime Document::creationDate() const { if (m_doc->locked) { return QDateTime(); } std::unique_ptr goo(m_doc->doc->getDocInfoCreatDate()); QString str = UnicodeParsedString(goo.get()); return Poppler::convertDate(str.toLatin1().constData()); } bool Document::setCreationDate(const QDateTime &val) { if (m_doc->locked) { return false; } m_doc->doc->setDocInfoCreatDate(QDateTimeToUnicodeGooString(val)); return true; } QDateTime Document::modificationDate() const { if (m_doc->locked) { return QDateTime(); } std::unique_ptr goo(m_doc->doc->getDocInfoModDate()); QString str = UnicodeParsedString(goo.get()); return Poppler::convertDate(str.toLatin1().constData()); } bool Document::setModificationDate(const QDateTime &val) { if (m_doc->locked) { return false; } m_doc->doc->setDocInfoModDate(QDateTimeToUnicodeGooString(val)); return true; } bool Document::isEncrypted() const { return m_doc->doc->isEncrypted(); } bool Document::isLinearized() const { return m_doc->doc->isLinearized(); } bool Document::okToPrint() const { return m_doc->doc->okToPrint(); } bool Document::okToPrintHighRes() const { return m_doc->doc->okToPrintHighRes(); } bool Document::okToChange() const { return m_doc->doc->okToChange(); } bool Document::okToCopy() const { return m_doc->doc->okToCopy(); } bool Document::okToAddNotes() const { return m_doc->doc->okToAddNotes(); } bool Document::okToFillForm() const { return m_doc->doc->okToFillForm(); } bool Document::okToCreateFormFields() const { return (okToFillForm() && okToChange()); } bool Document::okToExtractForAccessibility() const { return m_doc->doc->okToAccessibility(); } bool Document::okToAssemble() const { return m_doc->doc->okToAssemble(); } Document::PdfVersion Document::getPdfVersion() const { return PdfVersion { m_doc->doc->getPDFMajorVersion(), m_doc->doc->getPDFMinorVersion() }; } std::unique_ptr Document::page(const QString &label) const { GooString label_g(label.toLatin1().data()); int index; if (!m_doc->doc->getCatalog()->labelToIndex(&label_g, &index)) { std::unique_ptr label_ug(QStringToUnicodeGooString(label)); if (!m_doc->doc->getCatalog()->labelToIndex(label_ug.get(), &index)) { return nullptr; } } return page(index); } bool Document::hasEmbeddedFiles() const { return (!(0 == m_doc->doc->getCatalog()->numEmbeddedFiles())); } QVector Document::outline() const { QVector result; if (::Outline *outline = m_doc->doc->getOutline()) { if (const std::vector<::OutlineItem *> *items = outline->getItems()) { for (void *item : *items) { result.push_back(OutlineItem { new OutlineItemData { static_cast<::OutlineItem *>(item), m_doc } }); } } } return result; } std::unique_ptr Document::linkDestination(const QString &name) { GooString *namedDest = QStringToGooString(name); LinkDestinationData ldd(nullptr, namedDest, m_doc, false); auto ld = std::make_unique(ldd); delete namedDest; return ld; } void Document::setPaperColor(const QColor &color) { m_doc->setPaperColor(color); } void Document::setColorDisplayProfile(void *outputProfileA) { #if defined(USE_CMS) if (m_doc->m_sRGBProfile && m_doc->m_sRGBProfile.get() == outputProfileA) { // Catch the special case that the user passes the sRGB profile m_doc->m_displayProfile = m_doc->m_sRGBProfile; return; } if (m_doc->m_displayProfile && m_doc->m_displayProfile.get() == outputProfileA) { // Catch the special case that the user passes the display profile return; } m_doc->m_displayProfile = make_GfxLCMSProfilePtr(outputProfileA); #else Q_UNUSED(outputProfileA); #endif } void Document::setColorDisplayProfileName(const QString &name) { #if defined(USE_CMS) void *rawprofile = cmsOpenProfileFromFile(name.toLocal8Bit().constData(), "r"); m_doc->m_displayProfile = make_GfxLCMSProfilePtr(rawprofile); #else Q_UNUSED(name); #endif } void *Document::colorRgbProfile() const { #if defined(USE_CMS) if (!m_doc->m_sRGBProfile) { m_doc->m_sRGBProfile = make_GfxLCMSProfilePtr(cmsCreate_sRGBProfile()); } return m_doc->m_sRGBProfile.get(); #else return nullptr; #endif } void *Document::colorDisplayProfile() const { #if defined(USE_CMS) return m_doc->m_displayProfile.get(); #else return nullptr; #endif } QColor Document::paperColor() const { return m_doc->paperColor; } void Document::setRenderBackend(Document::RenderBackend backend) { // no need to delete the outputdev as for the moment we always create a splash one // as the QPainter one does not allow "precaching" due to its signature // delete m_doc->m_outputDev; // m_doc->m_outputDev = NULL; m_doc->m_backend = backend; } Document::RenderBackend Document::renderBackend() const { return m_doc->m_backend; } QSet Document::availableRenderBackends() { QSet ret; ret << Document::SplashBackend; ret << Document::QPainterBackend; return ret; } void Document::setRenderHint(Document::RenderHint hint, bool on) { const bool touchesOverprinting = hint & Document::OverprintPreview; int hintForOperation = hint; if (touchesOverprinting && !isOverprintPreviewAvailable()) { hintForOperation = hintForOperation & ~(int)Document::OverprintPreview; } if (on) { m_doc->m_hints |= hintForOperation; } else { m_doc->m_hints &= ~hintForOperation; } } Document::RenderHints Document::renderHints() const { return Document::RenderHints(m_doc->m_hints); } std::unique_ptr Document::psConverter() const { // Cannot use std::make_unique, because the PSConverter constructor is private return std::unique_ptr(new PSConverter(m_doc)); } std::unique_ptr Document::pdfConverter() const { // Cannot use std::make_unique, because the PDFConverter constructor is private return std::unique_ptr(new PDFConverter(m_doc)); } QString Document::metadata() const { QString result; Catalog *catalog = m_doc->doc->getCatalog(); if (catalog && catalog->isOk()) { std::unique_ptr s = catalog->readMetadata(); if (s) { result = UnicodeParsedString(s.get()); } } return result; } bool Document::hasOptionalContent() const { return (m_doc->doc->getOptContentConfig() && m_doc->doc->getOptContentConfig()->hasOCGs()); } OptContentModel *Document::optionalContentModel() { if (m_doc->m_optContentModel.isNull()) { m_doc->m_optContentModel = new OptContentModel(m_doc->doc->getOptContentConfig(), nullptr); } return (OptContentModel *)m_doc->m_optContentModel; } 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) { scripts.append(UnicodeParsedString(s)); delete s; } } return scripts; } bool Document::getPdfId(QByteArray *permanentId, QByteArray *updateId) const { GooString gooPermanentId; GooString gooUpdateId; if (!m_doc->doc->getID(permanentId ? &gooPermanentId : nullptr, updateId ? &gooUpdateId : nullptr)) { return false; } if (permanentId) { *permanentId = gooPermanentId.c_str(); } if (updateId) { *updateId = gooUpdateId.c_str(); } return true; } Document::FormType Document::formType() const { switch (m_doc->doc->getCatalog()->getFormType()) { case Catalog::NoForm: return Document::NoForm; case Catalog::AcroForm: return Document::AcroForm; case Catalog::XfaForm: return Document::XfaForm; } return Document::NoForm; // make gcc happy } QVector Document::formCalculateOrder() const { Form *form = m_doc->doc->getCatalog()->getForm(); if (!form) { return {}; } QVector result; const std::vector &calculateOrder = form->getCalculateOrder(); for (Ref r : calculateOrder) { FormWidget *w = form->findWidgetByRef(r); if (w) { result << w->getID(); } } return result; } std::vector> Document::signatures() const { std::vector> result; const std::vector<::FormFieldSignature *> pSignatures = m_doc->doc->getSignatureFields(); for (::FormFieldSignature *pSignature : pSignatures) { ::FormWidget *fw = pSignature->getCreateWidget(); ::Page *p = m_doc->doc->getPage(fw->getWidgetAnnotation()->getPageNum()); result.push_back(std::make_unique(m_doc, p, static_cast(fw))); } return result; } bool Document::xrefWasReconstructed() const { return m_doc->xrefReconstructed; } void Document::setXRefReconstructedCallback(const std::function &callback) { m_doc->xrefReconstructedCallback = callback; } QDateTime convertDate(const char *dateString) { int year, mon, day, hour, min, sec, tzHours, tzMins; char tz; GooString date(dateString); if (parseDateString(&date, &year, &mon, &day, &hour, &min, &sec, &tz, &tzHours, &tzMins)) { QDate d(year, mon, day); QTime t(hour, min, sec); if (d.isValid() && t.isValid()) { QDateTime dt(d, t, Qt::UTC); 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); } else if ('-' == tz) { // local time is behind UTC dt = dt.addSecs(((tzHours * 60) + tzMins) * 60); } else { qWarning("unexpected tz val"); } } return dt; } } return QDateTime(); } bool isCmsAvailable() { #if defined(USE_CMS) return true; #else return false; #endif } bool isOverprintPreviewAvailable() { return true; } } poppler-24.02.0/qt6/src/poppler-embeddedfile-private.h000066400000000000000000000023741455701731300225460ustar00rootroot00000000000000/* poppler-embeddedfile-private.h: Qt interface to poppler * Copyright (C) 2005, 2008, 2009, 2012, 2018, 2021, 2022, Albert Astals Cid * Copyright (C) 2005, Brad Hards * Copyright (C) 2008, 2011, Pino Toscano * * 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_EMBEDDEDFILE_PRIVATE_H #define POPPLER_EMBEDDEDFILE_PRIVATE_H class FileSpec; namespace Poppler { class EmbeddedFileData { public: explicit EmbeddedFileData(std::unique_ptr &&fs); EmbFile *embFile() const; std::unique_ptr filespec; }; } #endif poppler-24.02.0/qt6/src/poppler-embeddedfile.cc000066400000000000000000000071771455701731300212420ustar00rootroot00000000000000/* poppler-document.cc: qt interface to poppler * Copyright (C) 2005, 2008, 2009, 2012, 2013, 2018, 2022, Albert Astals Cid * 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 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 * 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 "poppler-qt6.h" #include #include #include "Object.h" #include "Stream.h" #include "Catalog.h" #include "poppler-private.h" #include "poppler-embeddedfile-private.h" namespace Poppler { EmbeddedFileData::EmbeddedFileData(std::unique_ptr &&fs) : filespec(std::move(fs)) { } EmbFile *EmbeddedFileData::embFile() const { return filespec->isOk() ? filespec->getEmbeddedFile() : nullptr; } EmbeddedFile::EmbeddedFile(EmbFile *embfile) : m_embeddedFile(nullptr) { assert(!"You must not use this private constructor!"); } EmbeddedFile::EmbeddedFile(EmbeddedFileData &dd) : m_embeddedFile(&dd) { } EmbeddedFile::~EmbeddedFile() { delete m_embeddedFile; } QString EmbeddedFile::name() const { const GooString *goo = m_embeddedFile->filespec->getFileName(); return goo ? UnicodeParsedString(goo) : QString(); } QString EmbeddedFile::description() const { const GooString *goo = m_embeddedFile->filespec->getDescription(); return goo ? UnicodeParsedString(goo) : QString(); } int EmbeddedFile::size() const { return m_embeddedFile->embFile() ? m_embeddedFile->embFile()->size() : -1; } QDateTime EmbeddedFile::modDate() const { const GooString *goo = m_embeddedFile->embFile() ? m_embeddedFile->embFile()->modDate() : nullptr; return goo ? convertDate(goo->c_str()) : QDateTime(); } QDateTime EmbeddedFile::createDate() const { const GooString *goo = m_embeddedFile->embFile() ? m_embeddedFile->embFile()->createDate() : nullptr; return goo ? convertDate(goo->c_str()) : QDateTime(); } QByteArray EmbeddedFile::checksum() const { const GooString *goo = m_embeddedFile->embFile() ? m_embeddedFile->embFile()->checksum() : nullptr; return goo ? QByteArray::fromRawData(goo->c_str(), goo->getLength()) : QByteArray(); } QString EmbeddedFile::mimeType() const { const GooString *goo = m_embeddedFile->embFile() ? m_embeddedFile->embFile()->mimeType() : nullptr; return goo ? QString(goo->c_str()) : QString(); } QByteArray EmbeddedFile::data() { if (!isValid()) { return QByteArray(); } Stream *stream = m_embeddedFile->embFile() ? m_embeddedFile->embFile()->stream() : nullptr; if (!stream) { return QByteArray(); } stream->reset(); auto data = stream->toUnsignedChars(); return QByteArray(reinterpret_cast(data.data()), data.size()); } bool EmbeddedFile::isValid() const { return m_embeddedFile->filespec->isOk(); } } poppler-24.02.0/qt6/src/poppler-fontinfo.cc000066400000000000000000000072161455701731300204650ustar00rootroot00000000000000/* poppler-qt.h: qt interface to poppler * Copyright (C) 2005, Net Integration Technologies, Inc. * Copyright (C) 2005, Tobias Koening * Copyright (C) 2005, Brad Hards * Copyright (C) 2005-2008, 2015, Albert Astals Cid * Copyright (C) 2008, 2009, Pino Toscano * Copyright (C) 2018, Adam Reichold * Copyright (C) 2019, Oliver Sander * Copyright (C) 2019, Jan Grulich * * 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 "poppler-qt6.h" #include "poppler-private.h" namespace Poppler { FontInfo::FontInfo() { m_data = new FontInfoData(); } FontInfo::FontInfo(const FontInfoData &fid) { m_data = new FontInfoData(fid); } FontInfo::FontInfo(const FontInfo &fi) { m_data = new FontInfoData(*fi.m_data); } FontInfo::~FontInfo() { delete m_data; } QString FontInfo::name() const { return m_data->fontName; } QString FontInfo::substituteName() const { return m_data->fontSubstituteName; } QString FontInfo::file() const { return m_data->fontFile; } bool FontInfo::isEmbedded() const { return m_data->isEmbedded; } bool FontInfo::isSubset() const { return m_data->isSubset; } FontInfo::Type FontInfo::type() const { return m_data->type; } QString FontInfo::typeName() const { switch (type()) { case unknown: return QObject::tr("unknown"); case Type1: return QObject::tr("Type 1"); case Type1C: return QObject::tr("Type 1C"); case Type3: return QObject::tr("Type 3"); case TrueType: return QObject::tr("TrueType"); case CIDType0: return QObject::tr("CID Type 0"); case CIDType0C: return QObject::tr("CID Type 0C"); case CIDTrueType: return QObject::tr("CID TrueType"); case Type1COT: return QObject::tr("Type 1C (OpenType)"); case TrueTypeOT: return QObject::tr("TrueType (OpenType)"); case CIDType0COT: return QObject::tr("CID Type 0C (OpenType)"); case CIDTrueTypeOT: return QObject::tr("CID TrueType (OpenType)"); } return QObject::tr("Bug: unexpected font type. Notify poppler mailing list!"); } FontInfo &FontInfo::operator=(const FontInfo &fi) { if (this == &fi) { return *this; } *m_data = *fi.m_data; return *this; } FontIterator::FontIterator(int startPage, DocumentData *dd) : d(new FontIteratorData(startPage, dd)) { } FontIterator::~FontIterator() { delete d; } QList FontIterator::next() { ++d->currentPage; QList fonts; const std::vector<::FontInfo *> items = d->fontInfoScanner.scan(1); fonts.reserve(items.size()); for (::FontInfo *entry : items) { fonts.append(FontInfo(FontInfoData(entry))); delete entry; } return fonts; } bool FontIterator::hasNext() const { return (d->currentPage + 1) < d->totalPages; } int FontIterator::currentPage() const { return d->currentPage; } } poppler-24.02.0/qt6/src/poppler-form.cc000066400000000000000000001147111455701731300176050ustar00rootroot00000000000000/* poppler-form.h: qt interface to poppler * Copyright (C) 2007-2008, 2011, Pino Toscano * Copyright (C) 2008, 2011, 2012, 2015-2023 Albert Astals Cid * Copyright (C) 2011 Carlos Garcia Campos * Copyright (C) 2012, Adam Reichold * Copyright (C) 2016, Hanno Meyer-Thurow * Copyright (C) 2017, Hans-Ulrich Jüttner * Copyright (C) 2018, Andre Heinecke * 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 Chinmoy Ranjan Pradhan * Copyright (C) 2018, 2020, 2021 Oliver Sander * Copyright (C) 2019 João Netto * Copyright (C) 2020 David García Garzón * 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 Georgiy Sgibnev . Work sponsored by lab50.net. * Copyright (C) 2021 Theofilos Intzoglou * Copyright (C) 2022 Alexander Sulfrian * Copyright (C) 2023 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 * 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 "poppler-form.h" #include #include #include #include #include #include #include #include #include #ifdef ENABLE_NSS3 # include #endif #include "poppler-page-private.h" #include "poppler-private.h" #include "poppler-annotation-helper.h" #include #include namespace { Qt::Alignment formTextAlignment(::FormWidget *fm) { Qt::Alignment qtalign = Qt::AlignLeft; switch (fm->getField()->getTextQuadding()) { case VariableTextQuadding::centered: qtalign = Qt::AlignHCenter; break; case VariableTextQuadding::rightJustified: qtalign = Qt::AlignRight; break; case VariableTextQuadding::leftJustified: qtalign = Qt::AlignLeft; } return qtalign; } } namespace Poppler { FormFieldIcon::FormFieldIcon(FormFieldIconData *data) : d_ptr(data) { } FormFieldIcon::FormFieldIcon(const FormFieldIcon &ffIcon) { d_ptr = new FormFieldIconData; d_ptr->icon = ffIcon.d_ptr->icon; } FormFieldIcon &FormFieldIcon::operator=(const FormFieldIcon &ffIcon) { if (this != &ffIcon) { delete d_ptr; d_ptr = nullptr; d_ptr = new FormFieldIconData; *d_ptr = *ffIcon.d_ptr; } return *this; } FormFieldIcon::~FormFieldIcon() { delete d_ptr; } FormField::FormField(std::unique_ptr dd) : m_formData(std::move(dd)) { if (m_formData->page) { const int rotation = m_formData->page->getRotate(); // reading the coords double left, top, right, bottom; m_formData->fm->getRect(&left, &bottom, &right, &top); // build a normalized transform matrix for this page at 100% scale GfxState gfxState(72.0, 72.0, m_formData->page->getCropBox(), rotation, true); const double *gfxCTM = gfxState.getCTM(); double MTX[6]; double pageWidth = m_formData->page->getCropWidth(); double pageHeight = m_formData->page->getCropHeight(); // landscape and seascape page rotation: be sure to use the correct (== rotated) page size if (((rotation / 90) % 2) == 1) { qSwap(pageWidth, pageHeight); } for (int i = 0; i < 6; i += 2) { MTX[i] = gfxCTM[i] / pageWidth; MTX[i + 1] = gfxCTM[i + 1] / pageHeight; } QPointF topLeft; XPDFReader::transform(MTX, qMin(left, right), qMax(top, bottom), topLeft); QPointF bottomRight; XPDFReader::transform(MTX, qMax(left, right), qMin(top, bottom), bottomRight); m_formData->box = QRectF(topLeft, QSizeF(bottomRight.x() - topLeft.x(), bottomRight.y() - topLeft.y())); } } FormField::~FormField() = default; QRectF FormField::rect() const { return m_formData->box; } int FormField::id() const { return m_formData->fm->getID(); } QString FormField::name() const { QString name; if (const GooString *goo = m_formData->fm->getPartialName()) { name = UnicodeParsedString(goo); } return name; } void FormField::setName(const QString &name) const { GooString *goo = QStringToGooString(name); m_formData->fm->setPartialName(*goo); delete goo; } QString FormField::fullyQualifiedName() const { QString name; if (GooString *goo = m_formData->fm->getFullyQualifiedName()) { name = UnicodeParsedString(goo); } return name; } QString FormField::uiName() const { QString name; if (const GooString *goo = m_formData->fm->getAlternateUiName()) { name = UnicodeParsedString(goo); } return name; } bool FormField::isReadOnly() const { return m_formData->fm->isReadOnly(); } void FormField::setReadOnly(bool value) { m_formData->fm->setReadOnly(value); } bool FormField::isVisible() const { const unsigned int flags = m_formData->fm->getWidgetAnnotation()->getFlags(); if (flags & Annot::flagHidden) { return false; } if (flags & Annot::flagNoView) { return false; } return true; } void FormField::setVisible(bool value) { unsigned int flags = m_formData->fm->getWidgetAnnotation()->getFlags(); if (value) { flags &= ~Annot::flagHidden; flags &= ~Annot::flagNoView; } else { flags |= Annot::flagHidden; } m_formData->fm->getWidgetAnnotation()->setFlags(flags); } bool FormField::isPrintable() const { return (m_formData->fm->getWidgetAnnotation()->getFlags() & Annot::flagPrint); } void FormField::setPrintable(bool value) { unsigned int flags = m_formData->fm->getWidgetAnnotation()->getFlags(); if (value) { flags |= Annot::flagPrint; } else { flags &= ~Annot::flagPrint; } m_formData->fm->getWidgetAnnotation()->setFlags(flags); } std::unique_ptr FormField::activationAction() const { if (::LinkAction *act = m_formData->fm->getActivationAction()) { return PageData::convertLinkActionToLink(act, m_formData->doc, QRectF()); } return {}; } std::unique_ptr FormField::additionalAction(AdditionalActionType type) const { Annot::FormAdditionalActionsType actionType = Annot::actionFieldModified; switch (type) { case FieldModified: actionType = Annot::actionFieldModified; break; case FormatField: actionType = Annot::actionFormatField; break; case ValidateField: actionType = Annot::actionValidateField; break; case CalculateField: actionType = Annot::actionCalculateField; break; } if (std::unique_ptr<::LinkAction> act = m_formData->fm->getAdditionalAction(actionType)) { return PageData::convertLinkActionToLink(act.get(), m_formData->doc, QRectF()); } return {}; } std::unique_ptr FormField::additionalAction(Annotation::AdditionalActionType type) const { ::AnnotWidget *w = m_formData->fm->getWidgetAnnotation(); if (!w) { return {}; } const Annot::AdditionalActionsType actionType = toPopplerAdditionalActionType(type); if (std::unique_ptr<::LinkAction> act = w->getAdditionalAction(actionType)) { return PageData::convertLinkActionToLink(act.get(), m_formData->doc, QRectF()); } return {}; } FormFieldButton::FormFieldButton(DocumentData *doc, ::Page *p, ::FormWidgetButton *w) : FormField(std::make_unique(doc, p, w)) { } FormFieldButton::~FormFieldButton() { } FormFieldButton::FormType FormFieldButton::type() const { return FormField::FormButton; } FormFieldButton::ButtonType FormFieldButton::buttonType() const { FormWidgetButton *fwb = static_cast(m_formData->fm); switch (fwb->getButtonType()) { case formButtonCheck: return FormFieldButton::CheckBox; break; case formButtonPush: return FormFieldButton::Push; break; case formButtonRadio: return FormFieldButton::Radio; break; } return FormFieldButton::CheckBox; } QString FormFieldButton::caption() const { FormWidgetButton *fwb = static_cast(m_formData->fm); QString ret; if (fwb->getButtonType() == formButtonPush) { Dict *dict = m_formData->fm->getObj()->getDict(); Object obj1 = dict->lookup("MK"); if (obj1.isDict()) { AnnotAppearanceCharacs appearCharacs(obj1.getDict()); if (appearCharacs.getNormalCaption()) { ret = UnicodeParsedString(appearCharacs.getNormalCaption()); } } } else { if (const char *goo = fwb->getOnStr()) { ret = QString::fromUtf8(goo); } } return ret; } FormFieldIcon FormFieldButton::icon() const { FormWidgetButton *fwb = static_cast(m_formData->fm); if (fwb->getButtonType() == formButtonPush) { Dict *dict = m_formData->fm->getObj()->getDict(); FormFieldIconData *data = new FormFieldIconData; data->icon = dict; return FormFieldIcon(data); } return FormFieldIcon(nullptr); } void FormFieldButton::setIcon(const FormFieldIcon &icon) { if (FormFieldIconData::getData(icon) == nullptr) { return; } FormWidgetButton *fwb = static_cast(m_formData->fm); if (fwb->getButtonType() == formButtonPush) { ::AnnotWidget *w = m_formData->fm->getWidgetAnnotation(); FormFieldIconData *data = FormFieldIconData::getData(icon); if (data->icon != nullptr) { w->setNewAppearance(data->icon->lookup("AP")); } } } bool FormFieldButton::state() const { FormWidgetButton *fwb = static_cast(m_formData->fm); return fwb->getState(); } void FormFieldButton::setState(bool state) { FormWidgetButton *fwb = static_cast(m_formData->fm); fwb->setState((bool)state); } QList FormFieldButton::siblings() const { FormWidgetButton *fwb = static_cast(m_formData->fm); ::FormFieldButton *ffb = static_cast<::FormFieldButton *>(fwb->getField()); if (fwb->getButtonType() == formButtonPush) { return QList(); } QList ret; for (int i = 0; i < ffb->getNumSiblings(); ++i) { ::FormFieldButton *sibling = static_cast<::FormFieldButton *>(ffb->getSibling(i)); for (int j = 0; j < sibling->getNumWidgets(); ++j) { FormWidget *w = sibling->getWidget(j); if (w) { ret.append(w->getID()); } } } return ret; } FormFieldText::FormFieldText(DocumentData *doc, ::Page *p, ::FormWidgetText *w) : FormField(std::make_unique(doc, p, w)) { } FormFieldText::~FormFieldText() { } FormField::FormType FormFieldText::type() const { return FormField::FormText; } FormFieldText::TextType FormFieldText::textType() const { FormWidgetText *fwt = static_cast(m_formData->fm); if (fwt->isFileSelect()) { return FormFieldText::FileSelect; } else if (fwt->isMultiline()) { return FormFieldText::Multiline; } return FormFieldText::Normal; } QString FormFieldText::text() const { const GooString *goo = static_cast(m_formData->fm)->getContent(); return UnicodeParsedString(goo); } void FormFieldText::setText(const QString &text) { FormWidgetText *fwt = static_cast(m_formData->fm); GooString *goo = QStringToUnicodeGooString(text); fwt->setContent(goo); delete goo; } void FormFieldText::setAppearanceText(const QString &text) { FormWidgetText *fwt = static_cast(m_formData->fm); GooString *goo = QStringToUnicodeGooString(text); fwt->setAppearanceContent(goo); delete goo; } bool FormFieldText::isPassword() const { FormWidgetText *fwt = static_cast(m_formData->fm); return fwt->isPassword(); } bool FormFieldText::isRichText() const { FormWidgetText *fwt = static_cast(m_formData->fm); return fwt->isRichText(); } int FormFieldText::maximumLength() const { FormWidgetText *fwt = static_cast(m_formData->fm); const int maxlen = fwt->getMaxLen(); return maxlen > 0 ? maxlen : -1; } Qt::Alignment FormFieldText::textAlignment() const { return formTextAlignment(m_formData->fm); } bool FormFieldText::canBeSpellChecked() const { FormWidgetText *fwt = static_cast(m_formData->fm); return !fwt->noSpellCheck(); } double FormFieldText::getFontSize() const { FormWidgetText *fwt = static_cast(m_formData->fm); return fwt->getTextFontSize(); } void FormFieldText::setFontSize(int fontSize) { FormWidgetText *fwt = static_cast(m_formData->fm); fwt->setTextFontSize(fontSize); } FormFieldChoice::FormFieldChoice(DocumentData *doc, ::Page *p, ::FormWidgetChoice *w) : FormField(std::make_unique(doc, p, w)) { } FormFieldChoice::~FormFieldChoice() { } FormFieldChoice::FormType FormFieldChoice::type() const { return FormField::FormChoice; } FormFieldChoice::ChoiceType FormFieldChoice::choiceType() const { FormWidgetChoice *fwc = static_cast(m_formData->fm); if (fwc->isCombo()) { return FormFieldChoice::ComboBox; } return FormFieldChoice::ListBox; } QStringList FormFieldChoice::choices() const { FormWidgetChoice *fwc = static_cast(m_formData->fm); QStringList ret; int num = fwc->getNumChoices(); ret.reserve(num); for (int i = 0; i < num; ++i) { ret.append(UnicodeParsedString(fwc->getChoice(i))); } return ret; } QVector> FormFieldChoice::choicesWithExportValues() const { FormWidgetChoice *fwc = static_cast(m_formData->fm); QVector> ret; const int num = fwc->getNumChoices(); ret.reserve(num); for (int i = 0; i < num; ++i) { const QString display = UnicodeParsedString(fwc->getChoice(i)); const GooString *exportValueG = fwc->getExportVal(i); const QString exportValue = exportValueG ? UnicodeParsedString(exportValueG) : display; ret.append({ display, exportValue }); } return ret; } bool FormFieldChoice::isEditable() const { FormWidgetChoice *fwc = static_cast(m_formData->fm); return fwc->isCombo() ? fwc->hasEdit() : false; } bool FormFieldChoice::multiSelect() const { FormWidgetChoice *fwc = static_cast(m_formData->fm); return !fwc->isCombo() ? fwc->isMultiSelect() : false; } QList FormFieldChoice::currentChoices() const { FormWidgetChoice *fwc = static_cast(m_formData->fm); int num = fwc->getNumChoices(); QList choices; for (int i = 0; i < num; ++i) { if (fwc->isSelected(i)) { choices.append(i); } } return choices; } void FormFieldChoice::setCurrentChoices(const QList &choice) { FormWidgetChoice *fwc = static_cast(m_formData->fm); fwc->deselectAll(); for (int i = 0; i < choice.count(); ++i) { fwc->select(choice.at(i)); } } QString FormFieldChoice::editChoice() const { FormWidgetChoice *fwc = static_cast(m_formData->fm); if (fwc->isCombo() && fwc->hasEdit()) { return UnicodeParsedString(fwc->getEditChoice()); } else { return QString(); } } 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; } } Qt::Alignment FormFieldChoice::textAlignment() const { return formTextAlignment(m_formData->fm); } bool FormFieldChoice::canBeSpellChecked() const { FormWidgetChoice *fwc = static_cast(m_formData->fm); return !fwc->noSpellCheck(); } class CertificateInfoPrivate { public: struct EntityInfo { QString common_name; QString email_address; QString org_name; QString distinguished_name; }; EntityInfo issuer_info; EntityInfo subject_info; QString nick_name; QByteArray certificate_der; QByteArray serial_number; QByteArray public_key; QDateTime validity_start; QDateTime validity_end; int public_key_type; int public_key_strength; int ku_extensions; int version; bool is_self_signed; bool is_null; CertificateInfo::KeyLocation keyLocation; }; CertificateInfo::CertificateInfo() : d_ptr(new CertificateInfoPrivate()) { d_ptr->is_null = true; } CertificateInfo::CertificateInfo(CertificateInfoPrivate *priv) : d_ptr(priv) { } CertificateInfo::CertificateInfo(const CertificateInfo &other) : d_ptr(other.d_ptr) { } CertificateInfo::~CertificateInfo() = default; CertificateInfo &CertificateInfo::operator=(const CertificateInfo &other) { if (this != &other) { d_ptr = other.d_ptr; } return *this; } bool CertificateInfo::isNull() const { Q_D(const CertificateInfo); return d->is_null; } int CertificateInfo::version() const { Q_D(const CertificateInfo); return d->version; } QByteArray CertificateInfo::serialNumber() const { Q_D(const CertificateInfo); return d->serial_number; } QString CertificateInfo::issuerInfo(EntityInfoKey key) const { Q_D(const CertificateInfo); switch (key) { case CommonName: return d->issuer_info.common_name; case DistinguishedName: return d->issuer_info.distinguished_name; case EmailAddress: return d->issuer_info.email_address; case Organization: return d->issuer_info.org_name; default: return QString(); } } QString CertificateInfo::subjectInfo(EntityInfoKey key) const { Q_D(const CertificateInfo); switch (key) { case CommonName: return d->subject_info.common_name; case DistinguishedName: return d->subject_info.distinguished_name; case EmailAddress: return d->subject_info.email_address; case Organization: return d->subject_info.org_name; default: return QString(); } } QString CertificateInfo::nickName() const { Q_D(const CertificateInfo); return d->nick_name; } QDateTime CertificateInfo::validityStart() const { Q_D(const CertificateInfo); return d->validity_start; } QDateTime CertificateInfo::validityEnd() const { Q_D(const CertificateInfo); return d->validity_end; } CertificateInfo::KeyUsageExtensions CertificateInfo::keyUsageExtensions() const { Q_D(const CertificateInfo); KeyUsageExtensions kuExtensions = KuNone; if (d->ku_extensions & KU_DIGITAL_SIGNATURE) { kuExtensions |= KuDigitalSignature; } if (d->ku_extensions & KU_NON_REPUDIATION) { kuExtensions |= KuNonRepudiation; } if (d->ku_extensions & KU_KEY_ENCIPHERMENT) { kuExtensions |= KuKeyEncipherment; } if (d->ku_extensions & KU_DATA_ENCIPHERMENT) { kuExtensions |= KuDataEncipherment; } if (d->ku_extensions & KU_KEY_AGREEMENT) { kuExtensions |= KuKeyAgreement; } if (d->ku_extensions & KU_KEY_CERT_SIGN) { kuExtensions |= KuKeyCertSign; } if (d->ku_extensions & KU_CRL_SIGN) { kuExtensions |= KuClrSign; } if (d->ku_extensions & KU_ENCIPHER_ONLY) { kuExtensions |= KuEncipherOnly; } return kuExtensions; } CertificateInfo::KeyLocation CertificateInfo::keyLocation() const { Q_D(const CertificateInfo); return d->keyLocation; } QByteArray CertificateInfo::publicKey() const { Q_D(const CertificateInfo); return d->public_key; } CertificateInfo::PublicKeyType CertificateInfo::publicKeyType() const { Q_D(const CertificateInfo); switch (d->public_key_type) { case RSAKEY: return RsaKey; case DSAKEY: return DsaKey; case ECKEY: return EcKey; default: return OtherKey; } } int CertificateInfo::publicKeyStrength() const { Q_D(const CertificateInfo); return d->public_key_strength; } bool CertificateInfo::isSelfSigned() const { Q_D(const CertificateInfo); return d->is_self_signed; } QByteArray CertificateInfo::certificateData() const { Q_D(const CertificateInfo); return d->certificate_der; } bool CertificateInfo::checkPassword(const QString &password) const { #ifdef ENABLE_SIGNATURES auto backend = CryptoSign::Factory::createActive(); if (!backend) { return false; } Q_D(const CertificateInfo); auto sigHandler = backend->createSigningHandler(d->nick_name.toStdString(), HashAlgorithm::Sha256); unsigned char buffer[5]; memcpy(buffer, "test", 5); sigHandler->addData(buffer, 5); std::optional tmpSignature = sigHandler->signDetached(password.toStdString()); return tmpSignature.has_value(); #else return false; #endif } class SignatureValidationInfoPrivate { public: explicit SignatureValidationInfoPrivate(CertificateInfo &&ci) : cert_info(ci) { } SignatureValidationInfo::SignatureStatus signature_status; SignatureValidationInfo::CertificateStatus certificate_status; CertificateInfo cert_info; QByteArray signature; QString signer_name; QString signer_subject_dn; QString location; QString reason; HashAlgorithm hash_algorithm; time_t signing_time; QList range_bounds; qint64 docLength; }; SignatureValidationInfo::SignatureValidationInfo(SignatureValidationInfoPrivate *priv) : d_ptr(priv) { } SignatureValidationInfo::SignatureValidationInfo(const SignatureValidationInfo &other) : d_ptr(other.d_ptr) { } SignatureValidationInfo::~SignatureValidationInfo() { } SignatureValidationInfo::SignatureStatus SignatureValidationInfo::signatureStatus() const { Q_D(const SignatureValidationInfo); return d->signature_status; } SignatureValidationInfo::CertificateStatus SignatureValidationInfo::certificateStatus() const { Q_D(const SignatureValidationInfo); return d->certificate_status; } QString SignatureValidationInfo::signerName() const { Q_D(const SignatureValidationInfo); return d->signer_name; } QString SignatureValidationInfo::signerSubjectDN() const { Q_D(const SignatureValidationInfo); return d->signer_subject_dn; } QString SignatureValidationInfo::location() const { Q_D(const SignatureValidationInfo); return d->location; } QString SignatureValidationInfo::reason() const { Q_D(const SignatureValidationInfo); return d->reason; } SignatureValidationInfo::HashAlgorithm SignatureValidationInfo::hashAlgorithm() const { #ifdef ENABLE_SIGNATURES Q_D(const SignatureValidationInfo); switch (d->hash_algorithm) { case ::HashAlgorithm::Md2: return HashAlgorithmMd2; case ::HashAlgorithm::Md5: return HashAlgorithmMd5; case ::HashAlgorithm::Sha1: return HashAlgorithmSha1; case ::HashAlgorithm::Sha256: return HashAlgorithmSha256; case ::HashAlgorithm::Sha384: return HashAlgorithmSha384; case ::HashAlgorithm::Sha512: return HashAlgorithmSha512; case ::HashAlgorithm::Sha224: return HashAlgorithmSha224; case ::HashAlgorithm::Unknown: return HashAlgorithmUnknown; } #endif return HashAlgorithmUnknown; } time_t SignatureValidationInfo::signingTime() const { Q_D(const SignatureValidationInfo); return d->signing_time; } QByteArray SignatureValidationInfo::signature() const { Q_D(const SignatureValidationInfo); return d->signature; } QList SignatureValidationInfo::signedRangeBounds() const { Q_D(const SignatureValidationInfo); return d->range_bounds; } bool SignatureValidationInfo::signsTotalDocument() const { Q_D(const SignatureValidationInfo); if (d->range_bounds.size() == 4 && d->range_bounds.value(0) == 0 && d->range_bounds.value(1) >= 0 && d->range_bounds.value(2) > d->range_bounds.value(1) && d->range_bounds.value(3) >= d->range_bounds.value(2)) { // The range from d->range_bounds.value(1) to d->range_bounds.value(2) is // not authenticated by the signature and should only contain the signature // itself padded with 0 bytes. This has been checked in readSignature(). // If it failed, d->signature is empty. // A potential range after d->range_bounds.value(3) would be also not // authenticated. Therefore d->range_bounds.value(3) should coincide with // the end of the document. if (d->docLength == d->range_bounds.value(3) && !d->signature.isEmpty()) { return true; } } return false; } CertificateInfo SignatureValidationInfo::certificateInfo() const { Q_D(const SignatureValidationInfo); return d->cert_info; } SignatureValidationInfo &SignatureValidationInfo::operator=(const SignatureValidationInfo &other) { if (this != &other) { d_ptr = other.d_ptr; } return *this; } FormFieldSignature::FormFieldSignature(DocumentData *doc, ::Page *p, ::FormWidgetSignature *w) : FormField(std::make_unique(doc, p, w)) { } FormFieldSignature::~FormFieldSignature() { } FormField::FormType FormFieldSignature::type() const { return FormField::FormSignature; } FormFieldSignature::SignatureType FormFieldSignature::signatureType() const { SignatureType sigType = AdbePkcs7detached; FormWidgetSignature *fws = static_cast(m_formData->fm); switch (fws->signatureType()) { case adbe_pkcs7_sha1: sigType = AdbePkcs7sha1; break; case adbe_pkcs7_detached: sigType = AdbePkcs7detached; break; case ETSI_CAdES_detached: sigType = EtsiCAdESdetached; break; case unknown_signature_type: sigType = UnknownSignatureType; break; case unsigned_signature_field: sigType = UnsignedSignature; break; } return sigType; } SignatureValidationInfo FormFieldSignature::validate(ValidateOptions opt) const { return validate(opt, QDateTime()); } 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) { CertificateInfoPrivate *certPriv = new CertificateInfoPrivate; certPriv->is_null = true; if (ci) { certPriv->version = ci->getVersion(); certPriv->ku_extensions = ci->getKeyUsageExtensions(); certPriv->keyLocation = fromPopplerCore(ci->getKeyLocation()); const GooString &certSerial = ci->getSerialNumber(); certPriv->serial_number = QByteArray(certSerial.c_str(), certSerial.getLength()); const X509CertificateInfo::EntityInfo &issuerInfo = ci->getIssuerInfo(); certPriv->issuer_info.common_name = issuerInfo.commonName.c_str(); certPriv->issuer_info.distinguished_name = issuerInfo.distinguishedName.c_str(); certPriv->issuer_info.email_address = issuerInfo.email.c_str(); certPriv->issuer_info.org_name = issuerInfo.organization.c_str(); const X509CertificateInfo::EntityInfo &subjectInfo = ci->getSubjectInfo(); certPriv->subject_info.common_name = subjectInfo.commonName.c_str(); certPriv->subject_info.distinguished_name = subjectInfo.distinguishedName.c_str(); certPriv->subject_info.email_address = subjectInfo.email.c_str(); certPriv->subject_info.org_name = subjectInfo.organization.c_str(); 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); const X509CertificateInfo::PublicKeyInfo &pkInfo = ci->getPublicKeyInfo(); certPriv->public_key = QByteArray(pkInfo.publicKey.c_str(), pkInfo.publicKey.getLength()); certPriv->public_key_type = static_cast(pkInfo.publicKeyType); certPriv->public_key_strength = pkInfo.publicKeyStrength; const GooString &certDer = ci->getCertificateDER(); certPriv->certificate_der = QByteArray(certDer.c_str(), certDer.getLength()); certPriv->is_null = false; } return certPriv; } SignatureValidationInfo FormFieldSignature::validate(int opt, const QDateTime &validationTime) const { 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); // get certificate info const X509CertificateInfo *ci = si->getCertificateInfo(); CertificateInfoPrivate *certPriv = createCertificateInfoPrivate(ci); SignatureValidationInfoPrivate *priv = new SignatureValidationInfoPrivate(CertificateInfo(certPriv)); switch (si->getSignatureValStatus()) { case SIGNATURE_VALID: priv->signature_status = SignatureValidationInfo::SignatureValid; break; case SIGNATURE_INVALID: priv->signature_status = SignatureValidationInfo::SignatureInvalid; break; case SIGNATURE_DIGEST_MISMATCH: priv->signature_status = SignatureValidationInfo::SignatureDigestMismatch; break; case SIGNATURE_DECODING_ERROR: priv->signature_status = SignatureValidationInfo::SignatureDecodingError; break; default: case SIGNATURE_GENERIC_ERROR: priv->signature_status = SignatureValidationInfo::SignatureGenericError; break; case SIGNATURE_NOT_FOUND: priv->signature_status = SignatureValidationInfo::SignatureNotFound; break; case SIGNATURE_NOT_VERIFIED: 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 = 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()); priv->signing_time = si->getSigningTime(); const std::vector ranges = fws->getSignedRangeBounds(); if (!ranges.empty()) { for (Goffset bound : ranges) { priv->range_bounds.append(bound); } } const std::optional checkedSignature = fws->getCheckedSignature(&priv->docLength); if (priv->range_bounds.size() == 4 && checkedSignature) { priv->signature = QByteArray::fromHex(checkedSignature->c_str()); } return SignatureValidationInfo(priv); } 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) { return FieldAlreadySigned; } Goffset file_size = 0; const std::optional sig = fws->getCheckedSignature(&file_size); if (sig) { // the above unsigned_signature_field check // should already catch this, but double check return FieldAlreadySigned; } const auto reason = std::unique_ptr(data.reason().isEmpty() ? nullptr : QStringToUnicodeGooString(data.reason())); 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()); const auto gSignatureText = std::unique_ptr(QStringToUnicodeGooString(data.signatureText())); const auto gSignatureLeftText = std::unique_ptr(QStringToUnicodeGooString(data.signatureLeftText())); 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())); return success ? SigningSuccess : GenericSigningError; } bool hasNSSSupport() { #ifdef ENABLE_NSS3 return true; #else return false; #endif } QVector getAvailableSigningCertificates() { auto backend = CryptoSign::Factory::createActive(); if (!backend) { return {}; } QVector vReturnCerts; std::vector> vCerts = backend->getAvailableSigningCertificates(); for (auto &cert : vCerts) { CertificateInfoPrivate *certPriv = createCertificateInfoPrivate(cert.get()); vReturnCerts.append(CertificateInfo(certPriv)); } 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(NSSSignatureConfiguration::getNSSDir().c_str()); #else return QString(); #endif } void setNSSDir(const QString &path) { #ifdef ENABLE_NSS3 if (path.isEmpty()) { return; } GooString *goo = QStringToGooString(path); NSSSignatureConfiguration::setNSSDir(*goo); delete goo; #else (void)path; #endif } namespace { std::function nssPasswordCall; } void setNSSPasswordCallback(const std::function &f) { #ifdef ENABLE_NSS3 NSSSignatureConfiguration::setNSSPasswordCallback(f); #else qWarning() << "setNSSPasswordCallback called but this poppler is built without NSS support"; (void)f; #endif } } poppler-24.02.0/qt6/src/poppler-form.h000066400000000000000000000560471455701731300174560ustar00rootroot00000000000000/* poppler-form.h: qt interface to poppler * Copyright (C) 2007-2008, Pino Toscano * Copyright (C) 2008, 2011, 2016, 2017, 2019-2022, Albert Astals Cid * Copyright (C) 2012, Adam Reichold * Copyright (C) 2016, Hanno Meyer-Thurow * Copyright (C) 2017, Hans-Ulrich Jüttner * Copyright (C) 2017, Tobias C. Berner * Copyright (C) 2018, Andre Heinecke * Copyright (C) 2018, Chinmoy Ranjan Pradhan * Copyright (C) 2018, 2021 Oliver Sander * Copyright (C) 2019 João Netto * Copyright (C) 2019, Adrian Johnson * 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, 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 * 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_QT6_FORM_H_ #define _POPPLER_QT6_FORM_H_ #include #include #include #include #include #include #include #include #include #include #include "poppler-export.h" #include "poppler-annotation.h" #include "poppler-qt6.h" class Object; class Page; class FormWidget; class FormWidgetButton; class FormWidgetText; class FormWidgetChoice; class FormWidgetSignature; namespace Poppler { class DocumentData; class Link; class FormFieldData; class FormFieldIconData; /** The class containing the appearance information */ class POPPLER_QT6_EXPORT FormFieldIcon { friend class FormFieldIconData; public: explicit FormFieldIcon(FormFieldIconData *data); FormFieldIcon(const FormFieldIcon &ffIcon); ~FormFieldIcon(); FormFieldIcon &operator=(const FormFieldIcon &ffIcon); private: FormFieldIconData *d_ptr; }; /** The base class representing a form field. */ class POPPLER_QT6_EXPORT FormField { friend class FormFieldData; public: /** The different types of form field. */ enum FormType { FormButton, ///< A button field. See \ref Poppler::FormFieldButton::ButtonType "ButtonType" FormText, ///< A text field. See \ref Poppler::FormFieldText::TextType "TextType" FormChoice, ///< A single choice field. See \ref Poppler::FormFieldChoice::ChoiceType "ChoiceType" FormSignature ///< A signature field. }; virtual ~FormField(); /** The type of the field. */ virtual FormType type() const = 0; /** \return The size of the field, in normalized coordinates, i.e. [0..1] with regard to the dimensions (cropbox) of the page */ QRectF rect() const; /** The ID of the field. */ int id() const; /** The internal name (T) of the field. */ QString name() const; /** Sets the internal name (T) of the field. */ void setName(const QString &name) const; /** The internal fully qualified name of the field. */ QString fullyQualifiedName() const; /** The name of the field to be used in user interface (eg messages to the user). */ QString uiName() const; /** Whether this form field is read-only. */ bool isReadOnly() const; /** Set whether this form field is read-only. */ void setReadOnly(bool value); /** Whether this form field is visible. */ bool isVisible() const; /** Set whether this form field is visible. */ void setVisible(bool value); /** Whether this field is printable. */ bool isPrintable() const; /** Set whether this field is printable. */ void setPrintable(bool value); /** The activation action of this form field. \note It may be null. */ std::unique_ptr activationAction() const; /** * Describes the flags from the form 'AA' dictionary. */ enum AdditionalActionType { FieldModified, ///< A JavaScript action to be performed when the user modifies the field FormatField, ///< A JavaScript action to be performed before the field is formatted to display its value ValidateField, ///< A JavaScript action to be performed when the field value changes CalculateField, ///< A JavaScript action to be performed when the field needs to be recalculated }; /** * Returns a given form additional action */ std::unique_ptr additionalAction(AdditionalActionType type) const; /** * Returns a given widget annotation additional action */ std::unique_ptr additionalAction(Annotation::AdditionalActionType type) const; protected: /// \cond PRIVATE explicit FormField(std::unique_ptr dd); std::unique_ptr m_formData; /// \endcond private: Q_DISABLE_COPY(FormField) }; /** A form field that represents a "button". */ class POPPLER_QT6_EXPORT FormFieldButton : public FormField { public: /** * The types of button field. */ enum ButtonType { Push, ///< A simple push button. CheckBox, ///< A check box. Radio ///< A radio button. }; /// \cond PRIVATE FormFieldButton(DocumentData *doc, ::Page *p, ::FormWidgetButton *w); /// \endcond ~FormFieldButton() override; FormType type() const override; /** The particular type of the button field. */ ButtonType buttonType() const; /** * The caption to be used for the button. */ QString caption() const; /** * Gets the icon used by the button */ FormFieldIcon icon() const; /** * Sets a new icon for the button, it has to be a icon * returned by FormFieldButton::icon. */ void setIcon(const FormFieldIcon &icon); /** The state of the button. */ bool state() const; /** Sets the state of the button to the new \p state . */ void setState(bool state); /** The list with the IDs of siblings (ie, buttons belonging to the same group as the current one. Valid only for \ref Radio buttons, an empty list otherwise. */ QList siblings() const; private: Q_DISABLE_COPY(FormFieldButton) }; /** A form field that represents a text input. */ class POPPLER_QT6_EXPORT FormFieldText : public FormField { public: /** The particular type of this text field. */ enum TextType { Normal, ///< A simple singleline text field. Multiline, ///< A multiline text field. FileSelect ///< An input field to select the path of a file on disk. }; /// \cond PRIVATE FormFieldText(DocumentData *doc, ::Page *p, ::FormWidgetText *w); /// \endcond ~FormFieldText() override; FormType type() const override; /** The text type of the text field. */ TextType textType() const; /** The text associated with the text field. */ QString text() const; /** Sets the text associated with the text field to the specified \p text. */ void setText(const QString &text); /** Sets the text inside the Appearance Stream to the specified \p text */ void setAppearanceText(const QString &text); /** Whether this text field is a password input, eg its text \b must be replaced with asterisks. Always false for \ref FileSelect text fields. */ bool isPassword() const; /** Whether this text field should allow rich text. */ bool isRichText() const; /** The maximum length for the text of this field, or -1 if not set. */ int maximumLength() const; /** The horizontal alignment for the text of this text field. */ Qt::Alignment textAlignment() const; /** Whether the text inserted manually in the field (where possible) can be spell-checked. */ bool canBeSpellChecked() const; /** The font size of the text in the form field */ double getFontSize() const; /** Set the font size of the text in the form field (currently only as integer) */ void setFontSize(int fontSize); private: Q_DISABLE_COPY(FormFieldText) }; /** A form field that represents a choice field. */ class POPPLER_QT6_EXPORT FormFieldChoice : public FormField { public: /** The particular type of this choice field. */ enum ChoiceType { ComboBox, ///< A simple singleline text field. ListBox ///< A multiline text field. }; /// \cond PRIVATE FormFieldChoice(DocumentData *doc, ::Page *p, ::FormWidgetChoice *w); /// \endcond ~FormFieldChoice() override; FormType type() const override; /** The choice type of the choice field. */ ChoiceType choiceType() const; /** The possible choices of the choice field. */ QStringList choices() const; /** The possible choices of the choice field. The first value of the pair is the display name of the choice, The second value is the export value (i.e. for use in javascript, etc) of the choice */ QVector> choicesWithExportValues() const; /** Whether this FormFieldChoice::ComboBox is editable, i.e. the user can type in a custom value. Always false for the other types of choices. */ bool isEditable() const; /** Whether more than one choice of this FormFieldChoice::ListBox can be selected at the same time. Always false for the other types of choices. */ bool multiSelect() const; /** The currently selected choices. */ QList currentChoices() const; /** Sets the selected choices to \p choice. */ void setCurrentChoices(const QList &choice); /** The text entered into an editable combo box choice field. Otherwise a null string. */ QString editChoice() const; /** Sets the text entered into an editable combo box choice field. Otherwise does nothing. */ void setEditChoice(const QString &text); /** The horizontal alignment for the text of this text field. */ Qt::Alignment textAlignment() const; /** Whether the text inserted manually in the field (where possible) can be spell-checked. Returns false if the field is not an editable text field. */ bool canBeSpellChecked() const; private: Q_DISABLE_COPY(FormFieldChoice) }; /** A helper class to store x509 certificate information. */ class CertificateInfoPrivate; class POPPLER_QT6_EXPORT CertificateInfo { public: /** The algorithm of public key. */ enum PublicKeyType { RsaKey, DsaKey, EcKey, OtherKey }; /** Certificate key usage extensions. */ enum KeyUsageExtension { KuDigitalSignature = 0x80, KuNonRepudiation = 0x40, KuKeyEncipherment = 0x20, KuDataEncipherment = 0x10, KuKeyAgreement = 0x08, KuKeyCertSign = 0x04, KuClrSign = 0x02, KuEncipherOnly = 0x01, KuNone = 0x00 }; Q_DECLARE_FLAGS(KeyUsageExtensions, KeyUsageExtension) /** Predefined keys for elements in an entity's distinguished name. */ enum EntityInfoKey { CommonName, DistinguishedName, EmailAddress, 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(); /** Returns true if certificate has no contents; otherwise returns false */ bool isNull() const; /** The certificate version string. */ int version() const; /** The certificate serial number. */ QByteArray serialNumber() const; /** Information about the issuer. */ QString issuerInfo(EntityInfoKey key) const; /** Information about the subject */ QString subjectInfo(EntityInfoKey key) const; /** The certificate internal database nickname \since 21.01 */ QString nickName() const; /** The date-time when certificate becomes valid. */ QDateTime validityStart() const; /** The date-time when certificate expires. */ QDateTime validityEnd() const; /** The uses allowed for the certificate. */ KeyUsageExtensions keyUsageExtensions() const; /** The public key value. */ QByteArray publicKey() const; /** The public key type. */ PublicKeyType publicKeyType() const; /** The strength of public key in bits. */ int publicKeyStrength() const; /** Returns true if certificate is self-signed otherwise returns false. */ bool isSelfSigned() const; /** The DER encoded certificate. */ QByteArray certificateData() const; /** Checks if the given password is the correct one for this certificate \since 21.01 */ 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); private: Q_DECLARE_PRIVATE(CertificateInfo) QSharedPointer d_ptr; }; Q_DECLARE_OPERATORS_FOR_FLAGS(CertificateInfo::KeyUsageExtensions) /** A signature validation info helper class. */ class SignatureValidationInfoPrivate; class POPPLER_QT6_EXPORT SignatureValidationInfo { public: /** The verification result of the signature. */ enum SignatureStatus { SignatureValid, ///< The signature is cryptographically valid. SignatureInvalid, ///< The signature is cryptographically invalid. SignatureDigestMismatch, ///< The document content was changed after the signature was applied. SignatureDecodingError, ///< The signature CMS/PKCS7 structure is malformed. SignatureGenericError, ///< The signature could not be verified. SignatureNotFound, ///< The requested signature is not present in the document. SignatureNotVerified ///< The signature is not yet verified. }; /** The verification result of the certificate. */ enum CertificateStatus { CertificateTrusted, ///< The certificate is considered trusted. CertificateUntrustedIssuer, ///< The issuer of this certificate has been marked as untrusted by the user. CertificateUnknownIssuer, ///< The certificate trust chain has not finished in a trusted root certificate. 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. }; /** The hash algorithm of the signature */ enum HashAlgorithm { HashAlgorithmUnknown, HashAlgorithmMd2, HashAlgorithmMd5, HashAlgorithmSha1, HashAlgorithmSha256, HashAlgorithmSha384, HashAlgorithmSha512, HashAlgorithmSha224 }; /// \cond PRIVATE explicit SignatureValidationInfo(SignatureValidationInfoPrivate *priv); /// \endcond ~SignatureValidationInfo(); /** The signature status of the signature. */ SignatureStatus signatureStatus() const; /** The certificate status of the signature. */ CertificateStatus certificateStatus() const; /** The signer name associated with the signature. */ QString signerName() const; /** The signer subject distinguished name associated with the signature. */ QString signerSubjectDN() const; /** Get signing location. */ QString location() const; /** Get signing reason. */ QString reason() const; /** The hash algorithm used for the signature. */ HashAlgorithm hashAlgorithm() const; /** The signing time associated with the signature. */ time_t signingTime() const; /** Get the signature binary data. */ QByteArray signature() const; /** Get the bounds of the ranges of the document which are signed. */ QList signedRangeBounds() const; /** Checks whether the signature authenticates the total document except for the signature itself. */ bool signsTotalDocument() const; /** The signer certificate info. */ CertificateInfo certificateInfo() const; SignatureValidationInfo(const SignatureValidationInfo &other); SignatureValidationInfo &operator=(const SignatureValidationInfo &other); private: Q_DECLARE_PRIVATE(SignatureValidationInfo) QSharedPointer d_ptr; }; /** A form field that represents a signature. */ class POPPLER_QT6_EXPORT FormFieldSignature : public FormField { public: /** The types of signature fields. */ enum SignatureType { UnknownSignatureType, AdbePkcs7sha1, AdbePkcs7detached, EtsiCAdESdetached, UnsignedSignature ///< \since 22.02 }; /** The validation options of this signature. */ enum ValidateOptions { ValidateVerifyCertificate = 1, ///< Validate the certificate. ValidateForceRevalidation = 2, ///< Force revalidation of the certificate. ValidateWithoutOCSPRevocationCheck = 4, ///< Do not contact OCSP servers to check for certificate revocation status \since 21.10 ValidateUseAIACertFetch = 8 ///< Use the AIA extension for certificate fetching \since 21.10 }; /// \cond PRIVATE FormFieldSignature(DocumentData *doc, ::Page *p, ::FormWidgetSignature *w); /// \endcond ~FormFieldSignature() override; FormType type() const override; /** The signature type */ SignatureType signatureType() const; /** 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. */ 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. */ SignatureValidationInfo validate(int opt, const QDateTime &validationTime) const; /** * \since 22.02 */ enum SigningResult { FieldAlreadySigned, ///< Trying to sign a field that is already signed GenericSigningError, SigningSuccess }; /** Signs a field of UnsignedSignature type. Ignores data.page(), data.fieldPartialName() and data.boundingRectangle() \since 22.02 */ SigningResult sign(const QString &outputFileName, const PDFConverter::NewSignatureData &data) const; 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_DEPRECATED POPPLER_QT6_EXPORT hasNSSSupport(); /** Return vector of suitable signing certificates \since 21.01 */ QVector POPPLER_QT6_EXPORT getAvailableSigningCertificates(); /** Gets the current NSS CertDB directory \since 21.01 */ QString POPPLER_QT6_EXPORT getNSSDir(); /** Set a custom NSS CertDB directory. Needs to be called before doing any other signature operation \since 21.01 */ void POPPLER_QT6_EXPORT setNSSDir(const QString &pathURL); /** Sets the callback for NSS password requests \since 21.01 */ void POPPLER_QT6_EXPORT setNSSPasswordCallback(const std::function &f); } #endif poppler-24.02.0/qt6/src/poppler-link-extractor-private.h000066400000000000000000000033751455701731300231250ustar00rootroot00000000000000/* poppler-link-extractor_p.h: qt interface to poppler * Copyright (C) 2007, 2008, 2011, Pino Toscano * Copyright (C) 2021, Oliver Sander * Copyright (C) 2021, 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 * 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_LINK_EXTRACTOR_H_ #define _POPPLER_LINK_EXTRACTOR_H_ #include #include #include #include #include namespace Poppler { class Link; class PageData; class LinkExtractorOutputDev : public OutputDev { public: explicit LinkExtractorOutputDev(PageData *data); ~LinkExtractorOutputDev() override; // inherited from OutputDev bool upsideDown() override { return false; } bool useDrawChar() override { return false; } bool interpretType3Chars() override { return false; } void processLink(::AnnotLink *link) override; // our stuff std::vector> links(); private: PageData *m_data; double m_pageCropWidth; double m_pageCropHeight; std::vector> m_links; }; } #endif poppler-24.02.0/qt6/src/poppler-link-extractor.cc000066400000000000000000000051621455701731300216070ustar00rootroot00000000000000/* poppler-link-extractor_p.h: qt interface to poppler * Copyright (C) 2007, 2008, 2011, Pino Toscano * Copyright (C) 2008, Albert Astals Cid * Copyright (C) 2021, Oliver Sander * * 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 "poppler-link-extractor-private.h" #include #include #include #include #include #include "poppler-qt6.h" #include "poppler-page-private.h" namespace Poppler { LinkExtractorOutputDev::LinkExtractorOutputDev(PageData *data) : m_data(data) { Q_ASSERT(m_data); ::Page *popplerPage = m_data->page; m_pageCropWidth = popplerPage->getCropWidth(); m_pageCropHeight = popplerPage->getCropHeight(); if (popplerPage->getRotate() == 90 || popplerPage->getRotate() == 270) { qSwap(m_pageCropWidth, m_pageCropHeight); } GfxState gfxState(72.0, 72.0, popplerPage->getCropBox(), popplerPage->getRotate(), true); setDefaultCTM(gfxState.getCTM()); } LinkExtractorOutputDev::~LinkExtractorOutputDev() { } void LinkExtractorOutputDev::processLink(::AnnotLink *link) { if (!link->isOk()) { return; } double left, top, right, bottom; int leftAux, topAux, rightAux, bottomAux; link->getRect(&left, &top, &right, &bottom); QRectF linkArea; cvtUserToDev(left, top, &leftAux, &topAux); cvtUserToDev(right, bottom, &rightAux, &bottomAux); linkArea.setLeft((double)leftAux / m_pageCropWidth); linkArea.setTop((double)topAux / m_pageCropHeight); linkArea.setRight((double)rightAux / m_pageCropWidth); linkArea.setBottom((double)bottomAux / m_pageCropHeight); std::unique_ptr popplerLink = m_data->convertLinkActionToLink(link->getAction(), linkArea); if (popplerLink) { m_links.push_back(std::move(popplerLink)); } OutputDev::processLink(link); } std::vector> LinkExtractorOutputDev::links() { return std::move(m_links); } } poppler-24.02.0/qt6/src/poppler-link-private.h000066400000000000000000000041041455701731300211030ustar00rootroot00000000000000/* poppler-link-private.h: qt interface to poppler * Copyright (C) 2016, 2018, 2020, 2021 Albert Astals Cid * Copyright (C) 2018 Intevation GmbH * Copyright (C) 2020, 2021 Oliver Sander * * 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_LINK_PRIVATE_H_ #define _POPPLER_LINK_PRIVATE_H_ #include #include "Link.h" class LinkOCGState; namespace Poppler { class Link; class LinkPrivate { public: explicit LinkPrivate(const QRectF &area) : linkArea(area) { } virtual ~LinkPrivate(); static LinkPrivate *get(Link *link) { return link->d_ptr; } LinkPrivate(const LinkPrivate &) = delete; LinkPrivate &operator=(const LinkPrivate &) = delete; QRectF linkArea; std::vector> nextLinks; }; class LinkOCGStatePrivate : public LinkPrivate { public: LinkOCGStatePrivate(const QRectF &area, const std::vector<::LinkOCGState::StateList> &sList, bool pRB) : LinkPrivate(area), stateList(sList), preserveRB(pRB) { } ~LinkOCGStatePrivate() override; std::vector<::LinkOCGState::StateList> stateList; bool preserveRB; }; class LinkHidePrivate : public LinkPrivate { public: LinkHidePrivate(const QRectF &area, const QString &tName, bool show) : LinkPrivate(area), targetName(tName), isShow(show) { } ~LinkHidePrivate() override; QString targetName; bool isShow; }; } #endif poppler-24.02.0/qt6/src/poppler-link.cc000066400000000000000000000416371455701731300176050ustar00rootroot00000000000000/* poppler-link.cc: qt interface to poppler * Copyright (C) 2006-2007, 2013, 2016-2021, Albert Astals Cid * Copyright (C) 2007-2008, Pino Toscano * Copyright (C) 2010 Hib Eris * Copyright (C) 2012, Tobias Koenig * Copyright (C) 2012, Guillermo A. Amaral B. * Copyright (C) 2018 Intevation GmbH * Copyright (C) 2018 Adam Reichold * Copyright (C) 2020, 2021 Oliver Sander * Adapting code from * Copyright (C) 2004 by Enrico Ros * * 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 #include #include #include "poppler-annotation-private.h" #include "Link.h" #include "Rendition.h" namespace Poppler { class LinkDestinationPrivate : public QSharedData { public: LinkDestinationPrivate(); LinkDestination::Kind kind; // destination type QString name; int pageNum; // page number double left, bottom; // position double right, top; double zoom; // zoom factor bool changeLeft : 1, changeTop : 1; // for destXYZ links, which position bool changeZoom : 1; // components to change }; LinkDestinationPrivate::LinkDestinationPrivate() { // sane defaults kind = LinkDestination::destXYZ; pageNum = 0; left = 0; bottom = 0; right = 0; top = 0; zoom = 1; changeLeft = true; changeTop = true; changeZoom = false; } LinkPrivate::~LinkPrivate() = default; LinkOCGStatePrivate::~LinkOCGStatePrivate() = default; LinkHidePrivate::~LinkHidePrivate() = default; class LinkGotoPrivate : public LinkPrivate { public: LinkGotoPrivate(const QRectF &area, const LinkDestination &dest); ~LinkGotoPrivate() override; QString extFileName; LinkDestination destination; }; LinkGotoPrivate::LinkGotoPrivate(const QRectF &area, const LinkDestination &dest) : LinkPrivate(area), destination(dest) { } LinkGotoPrivate::~LinkGotoPrivate() = default; class LinkExecutePrivate : public LinkPrivate { public: explicit LinkExecutePrivate(const QRectF &area); ~LinkExecutePrivate() override; QString fileName; QString parameters; }; LinkExecutePrivate::LinkExecutePrivate(const QRectF &area) : LinkPrivate(area) { } LinkExecutePrivate::~LinkExecutePrivate() = default; class LinkBrowsePrivate : public LinkPrivate { public: explicit LinkBrowsePrivate(const QRectF &area); ~LinkBrowsePrivate() override; QString url; }; LinkBrowsePrivate::LinkBrowsePrivate(const QRectF &area) : LinkPrivate(area) { } LinkBrowsePrivate::~LinkBrowsePrivate() = default; class LinkActionPrivate : public LinkPrivate { public: explicit LinkActionPrivate(const QRectF &area); ~LinkActionPrivate() override; LinkAction::ActionType type; }; LinkActionPrivate::LinkActionPrivate(const QRectF &area) : LinkPrivate(area) { } LinkActionPrivate::~LinkActionPrivate() = default; class LinkSoundPrivate : public LinkPrivate { public: explicit LinkSoundPrivate(const QRectF &area); ~LinkSoundPrivate() override; double volume; bool sync : 1; bool repeat : 1; bool mix : 1; SoundObject *sound; }; LinkSoundPrivate::LinkSoundPrivate(const QRectF &area) : LinkPrivate(area), sound(nullptr) { } LinkSoundPrivate::~LinkSoundPrivate() { delete sound; } class LinkRenditionPrivate : public LinkPrivate { public: LinkRenditionPrivate(const QRectF &area, ::MediaRendition *rendition, ::LinkRendition::RenditionOperation operation, const QString &script, const Ref ref); ~LinkRenditionPrivate() override; MediaRendition *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) { switch (operation) { case ::LinkRendition::NoRendition: action = LinkRendition::NoRendition; break; case ::LinkRendition::PlayRendition: action = LinkRendition::PlayRendition; break; case ::LinkRendition::StopRendition: action = LinkRendition::StopRendition; break; case ::LinkRendition::PauseRendition: action = LinkRendition::PauseRendition; break; case ::LinkRendition::ResumeRendition: action = LinkRendition::ResumeRendition; break; } } LinkRenditionPrivate::~LinkRenditionPrivate() { delete rendition; } class LinkJavaScriptPrivate : public LinkPrivate { public: explicit LinkJavaScriptPrivate(const QRectF &area); ~LinkJavaScriptPrivate() override; QString js; }; LinkJavaScriptPrivate::LinkJavaScriptPrivate(const QRectF &area) : LinkPrivate(area) { } LinkJavaScriptPrivate::~LinkJavaScriptPrivate() = default; class LinkMoviePrivate : public LinkPrivate { public: LinkMoviePrivate(const QRectF &area, LinkMovie::Operation operation, const QString &title, const Ref reference); ~LinkMoviePrivate() override; LinkMovie::Operation operation; QString annotationTitle; Ref annotationReference; }; LinkMoviePrivate::LinkMoviePrivate(const QRectF &area, LinkMovie::Operation _operation, const QString &title, const Ref reference) : LinkPrivate(area), operation(_operation), annotationTitle(title), annotationReference(reference) { } LinkMoviePrivate::~LinkMoviePrivate() = default; static void cvtUserToDev(::Page *page, double xu, double yu, int *xd, int *yd) { double ctm[6]; page->getDefaultCTM(ctm, 72.0, 72.0, 0, false, true); *xd = (int)(ctm[0] * xu + ctm[2] * yu + ctm[4] + 0.5); *yd = (int)(ctm[1] * xu + ctm[3] * yu + ctm[5] + 0.5); } LinkDestination::LinkDestination(const LinkDestinationData &data) : d(new LinkDestinationPrivate) { bool deleteDest = false; const LinkDest *ld = data.ld; if (data.namedDest && !ld && !data.externalDest) { deleteDest = true; ld = data.doc->doc->findDest(data.namedDest).release(); } // in case this destination was named one, and it was not resolved if (data.namedDest && !ld) { d->name = QString::fromLatin1(data.namedDest->c_str()); } if (!ld) { return; } if (ld->getKind() == ::destXYZ) { d->kind = destXYZ; } else if (ld->getKind() == ::destFit) { d->kind = destFit; } else if (ld->getKind() == ::destFitH) { d->kind = destFitH; } else if (ld->getKind() == ::destFitV) { d->kind = destFitV; } else if (ld->getKind() == ::destFitR) { d->kind = destFitR; } else if (ld->getKind() == ::destFitB) { d->kind = destFitB; } else if (ld->getKind() == ::destFitBH) { d->kind = destFitBH; } else if (ld->getKind() == ::destFitBV) { d->kind = destFitBV; } if (!ld->isPageRef()) { d->pageNum = ld->getPageNum(); } else { const Ref ref = ld->getPageRef(); d->pageNum = data.doc->doc->findPage(ref); } double left = ld->getLeft(); double bottom = ld->getBottom(); double right = ld->getRight(); double top = ld->getTop(); d->zoom = ld->getZoom(); d->changeLeft = ld->getChangeLeft(); d->changeTop = ld->getChangeTop(); d->changeZoom = ld->getChangeZoom(); int leftAux = 0, topAux = 0, rightAux = 0, bottomAux = 0; if (!data.externalDest) { ::Page *page; if (d->pageNum > 0 && d->pageNum <= data.doc->doc->getNumPages() && (page = data.doc->doc->getPage(d->pageNum))) { cvtUserToDev(page, left, top, &leftAux, &topAux); cvtUserToDev(page, right, bottom, &rightAux, &bottomAux); d->left = leftAux / (double)page->getCropWidth(); d->top = topAux / (double)page->getCropHeight(); d->right = rightAux / (double)page->getCropWidth(); d->bottom = bottomAux / (double)page->getCropHeight(); } else { d->pageNum = 0; } } if (deleteDest) { delete ld; } } LinkDestination::LinkDestination(const QString &description) : d(new LinkDestinationPrivate) { const QStringList tokens = description.split(';'); if (tokens.size() >= 10) { d->kind = static_cast(tokens.at(0).toInt()); d->pageNum = tokens.at(1).toInt(); d->left = tokens.at(2).toDouble(); d->bottom = tokens.at(3).toDouble(); d->right = tokens.at(4).toDouble(); d->top = tokens.at(5).toDouble(); d->zoom = tokens.at(6).toDouble(); d->changeLeft = static_cast(tokens.at(7).toInt()); d->changeTop = static_cast(tokens.at(8).toInt()); d->changeZoom = static_cast(tokens.at(9).toInt()); } } LinkDestination::LinkDestination(const LinkDestination &other) : d(other.d) { } LinkDestination::~LinkDestination() { } LinkDestination::Kind LinkDestination::kind() const { return d->kind; } int LinkDestination::pageNumber() const { return d->pageNum; } double LinkDestination::left() const { return d->left; } double LinkDestination::bottom() const { return d->bottom; } double LinkDestination::right() const { return d->right; } double LinkDestination::top() const { return d->top; } double LinkDestination::zoom() const { return d->zoom; } bool LinkDestination::isChangeLeft() const { return d->changeLeft; } bool LinkDestination::isChangeTop() const { return d->changeTop; } bool LinkDestination::isChangeZoom() const { return d->changeZoom; } 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); return s; } QString LinkDestination::destinationName() const { return d->name; } LinkDestination &LinkDestination::operator=(const LinkDestination &other) { if (this == &other) { return *this; } d = other.d; return *this; } // Link Link::~Link() { delete d_ptr; } Link::Link(const QRectF &linkArea) : d_ptr(new LinkPrivate(linkArea)) { } Link::Link(LinkPrivate &dd) : d_ptr(&dd) { } Link::LinkType Link::linkType() const { return None; } QRectF Link::linkArea() const { Q_D(const Link); return d->linkArea; } QVector Link::nextLinks() const { QVector links(d_ptr->nextLinks.size()); for (qsizetype i = 0; i < links.size(); i++) { links[i] = d_ptr->nextLinks[i].get(); } return links; } // LinkGoto LinkGoto::LinkGoto(const QRectF &linkArea, const QString &extFileName, const LinkDestination &destination) : Link(*new LinkGotoPrivate(linkArea, destination)) { Q_D(LinkGoto); d->extFileName = extFileName; } LinkGoto::~LinkGoto() { } bool LinkGoto::isExternal() const { Q_D(const LinkGoto); return !d->extFileName.isEmpty(); } QString LinkGoto::fileName() const { Q_D(const LinkGoto); return d->extFileName; } LinkDestination LinkGoto::destination() const { Q_D(const LinkGoto); return d->destination; } Link::LinkType LinkGoto::linkType() const { return Goto; } // LinkExecute LinkExecute::LinkExecute(const QRectF &linkArea, const QString &file, const QString ¶ms) : Link(*new LinkExecutePrivate(linkArea)) { Q_D(LinkExecute); d->fileName = file; d->parameters = params; } LinkExecute::~LinkExecute() { } QString LinkExecute::fileName() const { Q_D(const LinkExecute); return d->fileName; } QString LinkExecute::parameters() const { Q_D(const LinkExecute); return d->parameters; } Link::LinkType LinkExecute::linkType() const { return Execute; } // LinkBrowse LinkBrowse::LinkBrowse(const QRectF &linkArea, const QString &url) : Link(*new LinkBrowsePrivate(linkArea)) { Q_D(LinkBrowse); d->url = url; } LinkBrowse::~LinkBrowse() { } QString LinkBrowse::url() const { Q_D(const LinkBrowse); return d->url; } Link::LinkType LinkBrowse::linkType() const { return Browse; } // LinkAction LinkAction::LinkAction(const QRectF &linkArea, ActionType actionType) : Link(*new LinkActionPrivate(linkArea)) { Q_D(LinkAction); d->type = actionType; } LinkAction::~LinkAction() { } LinkAction::ActionType LinkAction::actionType() const { Q_D(const LinkAction); return d->type; } Link::LinkType LinkAction::linkType() const { return Action; } // LinkSound LinkSound::LinkSound(const QRectF &linkArea, double volume, bool sync, bool repeat, bool mix, SoundObject *sound) : Link(*new LinkSoundPrivate(linkArea)) { Q_D(LinkSound); d->volume = volume; d->sync = sync; d->repeat = repeat; d->mix = mix; d->sound = sound; } LinkSound::~LinkSound() { } Link::LinkType LinkSound::linkType() const { return Sound; } double LinkSound::volume() const { Q_D(const LinkSound); return d->volume; } bool LinkSound::synchronous() const { Q_D(const LinkSound); return d->sync; } bool LinkSound::repeat() const { Q_D(const LinkSound); return d->repeat; } bool LinkSound::mix() const { Q_D(const LinkSound); return d->mix; } SoundObject *LinkSound::sound() const { Q_D(const LinkSound); return d->sound; } // 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::~LinkRendition() { } Link::LinkType LinkRendition::linkType() const { return Rendition; } MediaRendition *LinkRendition::rendition() const { Q_D(const LinkRendition); return d->rendition; } LinkRendition::RenditionAction LinkRendition::action() const { Q_D(const LinkRendition); return d->action; } QString LinkRendition::script() const { Q_D(const LinkRendition); return d->script; } bool LinkRendition::isReferencedAnnotation(const ScreenAnnotation *annotation) const { Q_D(const LinkRendition); if (d->annotationReference != Ref::INVALID() && d->annotationReference == annotation->d_ptr->pdfObjectReference()) { return true; } return false; } // LinkJavaScript LinkJavaScript::LinkJavaScript(const QRectF &linkArea, const QString &js) : Link(*new LinkJavaScriptPrivate(linkArea)) { Q_D(LinkJavaScript); d->js = js; } LinkJavaScript::~LinkJavaScript() { } Link::LinkType LinkJavaScript::linkType() const { return JavaScript; } QString LinkJavaScript::script() const { Q_D(const LinkJavaScript); return d->js; } // LinkMovie LinkMovie::LinkMovie(const QRectF &linkArea, Operation operation, const QString &annotationTitle, const Ref annotationReference) : Link(*new LinkMoviePrivate(linkArea, operation, annotationTitle, annotationReference)) { } LinkMovie::~LinkMovie() { } Link::LinkType LinkMovie::linkType() const { return Movie; } LinkMovie::Operation LinkMovie::operation() const { Q_D(const LinkMovie); return d->operation; } bool LinkMovie::isReferencedAnnotation(const MovieAnnotation *annotation) const { Q_D(const LinkMovie); if (d->annotationReference != Ref::INVALID() && d->annotationReference == annotation->d_ptr->pdfObjectReference()) { return true; } else if (!d->annotationTitle.isNull()) { return (annotation->movieTitle() == d->annotationTitle); } return false; } LinkOCGState::LinkOCGState(LinkOCGStatePrivate *ocgp) : Link(*ocgp) { } LinkOCGState::~LinkOCGState() { } Link::LinkType LinkOCGState::linkType() const { return OCGState; } // LinkHide LinkHide::LinkHide(LinkHidePrivate *lhidep) : Link(*lhidep) { } LinkHide::~LinkHide() { } Link::LinkType LinkHide::linkType() const { return Hide; } QVector LinkHide::targets() const { Q_D(const LinkHide); return QVector() << d->targetName; } bool LinkHide::isShowAction() const { Q_D(const LinkHide); return d->isShow; } } poppler-24.02.0/qt6/src/poppler-link.h000066400000000000000000000412161455701731300174400ustar00rootroot00000000000000/* poppler-link.h: qt interface to poppler * Copyright (C) 2006, 2013, 2016, 2018, 2019, 2021, 2022, 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 * Adapting code from * Copyright (C) 2004 by Enrico Ros * * 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_LINK_H_ #define _POPPLER_LINK_H_ #include #include #include #include #include "poppler-export.h" struct Ref; class MediaRendition; namespace Poppler { class LinkPrivate; class LinkGotoPrivate; class LinkExecutePrivate; class LinkBrowsePrivate; class LinkActionPrivate; class LinkSoundPrivate; class LinkJavaScriptPrivate; class LinkMoviePrivate; class LinkDestinationData; class LinkDestinationPrivate; class LinkRenditionPrivate; class LinkOCGStatePrivate; class LinkHidePrivate; class MediaRendition; class MovieAnnotation; class ScreenAnnotation; class SoundObject; /** * \short A destination. * * The LinkDestination class represent a "destination" (in terms of visual * viewport to be displayed) for \link Poppler::LinkGoto GoTo\endlink links, * and items in the table of contents (TOC) of a document. * * Coordinates are in 0..1 range */ class POPPLER_QT6_EXPORT LinkDestination { public: /** * The possible kind of "viewport destination". */ enum Kind { /** * The new viewport is specified in terms of: * - possible new left coordinate (see isChangeLeft() ) * - possible new top coordinate (see isChangeTop() ) * - possible new zoom level (see isChangeZoom() ) */ destXYZ = 1, destFit = 2, destFitH = 3, destFitV = 4, destFitR = 5, destFitB = 6, destFitBH = 7, destFitBV = 8 }; /// \cond PRIVATE explicit LinkDestination(const LinkDestinationData &data); explicit LinkDestination(const QString &description); /// \endcond /** * Copy constructor. */ LinkDestination(const LinkDestination &other); /** * Destructor. */ ~LinkDestination(); // Accessors. /** * The kind of destination. */ Kind kind() const; /** * Which page is the target of this destination. * * \note this number is 1-based, so for a 5 pages document the * valid page numbers go from 1 to 5 (both included). */ int pageNumber() const; /** * The new left for the viewport of the target page, in case * it is specified to be changed (see isChangeLeft() ) */ double left() const; double bottom() const; double right() const; /** * The new top for the viewport of the target page, in case * it is specified to be changed (see isChangeTop() ) */ double top() const; double zoom() const; /** * Whether the left of the viewport on the target page should * be changed. * * \see left() */ bool isChangeLeft() const; /** * Whether the top of the viewport on the target page should * be changed. * * \see top() */ bool isChangeTop() const; /** * Whether the zoom level should be changed. * * \see zoom() */ bool isChangeZoom() const; /** * Return a string repesentation of this destination. */ QString toString() const; /** * Return the name of this destination. */ QString destinationName() const; /** * Assignment operator. */ LinkDestination &operator=(const LinkDestination &other); private: QSharedDataPointer d; }; /** * \short Encapsulates data that describes a link. * * This is the base class for links. It makes mandatory for inherited * kind of links to reimplement the linkType() method and return the type of * the link described by the reimplemented class. */ class POPPLER_QT6_EXPORT Link { public: /// \cond PRIVATE explicit Link(const QRectF &linkArea); /// \endcond /** * The possible kinds of link. * * Inherited classes must return an unique identifier */ enum LinkType { None, ///< Unknown link Goto, ///< A "Go To" link Execute, ///< A command to be executed Browse, ///< An URL to be browsed (eg "http://poppler.freedesktop.org") Action, ///< A "standard" action to be executed in the viewer Sound, ///< A link representing a sound to be played Movie, ///< An action to be executed on a movie Rendition, ///< A rendition link JavaScript, ///< A JavaScript code to be interpreted OCGState, ///< An Optional Content Group state change Hide, ///< An action to hide a field }; /** * The type of this link. */ virtual LinkType linkType() const; /** * Destructor. */ virtual ~Link(); /** * The area of a Page where the link should be active. * * \note this can be a null rect, in this case the link represents * a general action. The area is given in 0..1 range */ QRectF linkArea() const; /** * Get the next links to be activated / executed after this link. * * \note The caller does not get ownership of the returned objects. */ QVector nextLinks() const; protected: /// \cond PRIVATE explicit Link(LinkPrivate &dd); Q_DECLARE_PRIVATE(Link) LinkPrivate *d_ptr; /// \endcond private: Q_DISABLE_COPY(Link) }; /** * \brief Viewport reaching request. * * With a LinkGoto link, the document requests the specified viewport to be * reached (aka, displayed in a viewer). Furthermore, if a file name is specified, * then the destination refers to that document (and not to the document the * current LinkGoto belongs to). */ class POPPLER_QT6_EXPORT LinkGoto : public Link { public: /** * Create a new Goto link. * * \param linkArea the active area of the link * \param extFileName if not empty, the file name to be open * \param destination the destination to be reached */ LinkGoto(const QRectF &linkArea, const QString &extFileName, const LinkDestination &destination); /** * Destructor. */ ~LinkGoto() override; /** * Whether the destination is in an external document * (i.e. not the current document) */ bool isExternal() const; // query for goto parameters /** * The file name of the document the destination() refers to, * or an empty string in case it refers to the current document. */ QString fileName() const; /** * The destination to reach. */ LinkDestination destination() const; LinkType linkType() const override; private: Q_DECLARE_PRIVATE(LinkGoto) Q_DISABLE_COPY(LinkGoto) }; /** * \brief Generic execution request. * * The LinkExecute link represent a "file name" execution request. The result * depends on the \link fileName() file name\endlink: * - if it is a document, then it is requested to be open * - otherwise, it represents an executable to be run with the specified parameters */ class POPPLER_QT6_EXPORT LinkExecute : public Link { public: /** * The file name to be executed */ QString fileName() const; /** * The parameters for the command. */ QString parameters() const; /** * Create a new Execute link. * * \param linkArea the active area of the link * \param file the file name to be open, or the program to be execute * \param params the parameters for the program to execute */ LinkExecute(const QRectF &linkArea, const QString &file, const QString ¶ms); /** * Destructor. */ ~LinkExecute() override; LinkType linkType() const override; private: Q_DECLARE_PRIVATE(LinkExecute) Q_DISABLE_COPY(LinkExecute) }; /** * \brief An URL to browse. * * The LinkBrowse link holds a URL (eg 'http://poppler.freedesktop.org', * 'mailto:john@some.org', etc) to be open. * * The format of the URL is specified by RFC 2396 (http://www.ietf.org/rfc/rfc2396.txt) */ class POPPLER_QT6_EXPORT LinkBrowse : public Link { public: /** * The URL to open */ QString url() const; /** * Create a new browse link. * * \param linkArea the active area of the link * \param url the URL to be open */ LinkBrowse(const QRectF &linkArea, const QString &url); /** * Destructor. */ ~LinkBrowse() override; LinkType linkType() const override; private: Q_DECLARE_PRIVATE(LinkBrowse) Q_DISABLE_COPY(LinkBrowse) }; /** * \brief "Standard" action request. * * The LinkAction class represents a link that request a "standard" action * to be performed by the viewer on the displayed document. */ class POPPLER_QT6_EXPORT LinkAction : public Link { public: /** * The possible types of actions */ enum ActionType { PageFirst = 1, PagePrev = 2, PageNext = 3, PageLast = 4, HistoryBack = 5, HistoryForward = 6, Quit = 7, Presentation = 8, EndPresentation = 9, Find = 10, GoToPage = 11, Close = 12, Print = 13, SaveAs = 14 ///< \since 22.04 }; /** * The action of the current LinkAction */ ActionType actionType() const; /** * Create a new Action link, that executes a specified action * on the document. * * \param linkArea the active area of the link * \param actionType which action should be executed */ LinkAction(const QRectF &linkArea, ActionType actionType); /** * Destructor. */ ~LinkAction() override; LinkType linkType() const override; private: Q_DECLARE_PRIVATE(LinkAction) Q_DISABLE_COPY(LinkAction) }; /** * Sound: a sound to be played. */ class POPPLER_QT6_EXPORT LinkSound : public Link { public: // create a Link_Sound LinkSound(const QRectF &linkArea, double volume, bool sync, bool repeat, bool mix, SoundObject *sound); /** * Destructor. */ ~LinkSound() override; LinkType linkType() const override; /** * The volume to be used when playing the sound. * * The volume is in the range [ -1, 1 ], where: * - a negative number: no volume (mute) * - 1: full volume */ double volume() const; /** * Whether the playback of the sound should be synchronous * (thus blocking, waiting for the end of the sound playback). */ bool synchronous() const; /** * Whether the sound should be played continuously (that is, * started again when it ends) */ bool repeat() const; /** * Whether the playback of this sound can be mixed with * playbacks with other sounds of the same document. * * \note When false, any other playback must be stopped before * playing the sound. */ bool mix() const; /** * The sound object to be played */ SoundObject *sound() const; private: Q_DECLARE_PRIVATE(LinkSound) Q_DISABLE_COPY(LinkSound) }; /** * Rendition: Rendition link. */ class POPPLER_QT6_EXPORT LinkRendition : public Link { public: /** * Describes the possible rendition actions. */ enum RenditionAction { NoRendition, PlayRendition, StopRendition, PauseRendition, ResumeRendition }; /** * Create a new rendition link. * * \param linkArea the active area of the link * \param rendition the media rendition object. Ownership is taken * \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, ::MediaRendition *rendition, int operation, const QString &script, const Ref annotationReference); /** * Destructor. */ ~LinkRendition() override; LinkType linkType() const override; /** * Returns the media rendition object if the redition provides one, @c 0 otherwise */ MediaRendition *rendition() const; /** * Returns the action that should be executed if a rendition object is provided. */ RenditionAction action() const; /** * The JS code that shall be executed or an empty string. */ QString script() const; /** * Returns whether the given @p annotation is the referenced screen annotation for this rendition @p link. */ bool isReferencedAnnotation(const ScreenAnnotation *annotation) const; private: Q_DECLARE_PRIVATE(LinkRendition) Q_DISABLE_COPY(LinkRendition) }; /** * JavaScript: a JavaScript code to be interpreted. */ class POPPLER_QT6_EXPORT LinkJavaScript : public Link { public: /** * Create a new JavaScript link. * * \param linkArea the active area of the link * \param js the JS code to be interpreted */ LinkJavaScript(const QRectF &linkArea, const QString &js); /** * Destructor. */ ~LinkJavaScript() override; LinkType linkType() const override; /** * The JS code */ QString script() const; private: Q_DECLARE_PRIVATE(LinkJavaScript) Q_DISABLE_COPY(LinkJavaScript) }; /** * Movie: a movie to be played. */ class POPPLER_QT6_EXPORT LinkMovie : public Link { public: /** * Describes the operation to be performed on the movie. */ enum Operation { Play, Stop, Pause, Resume }; /** * Create a new Movie link. * * \param linkArea the active area of the link * \param operation the operation to be performed on the movie * \param annotationTitle the title of the movie annotation identifying the movie to be played * \param annotationReference the object reference of the movie annotation identifying the movie to be played * * Note: This constructor is supposed to be used by Poppler::Page only. */ LinkMovie(const QRectF &linkArea, Operation operation, const QString &annotationTitle, const Ref annotationReference); /** * Destructor. */ ~LinkMovie() override; LinkType linkType() const override; /** * Returns the operation to be performed on the movie. */ Operation operation() const; /** * Returns whether the given @p annotation is the referenced movie annotation for this movie @p link. */ bool isReferencedAnnotation(const MovieAnnotation *annotation) const; private: Q_DECLARE_PRIVATE(LinkMovie) Q_DISABLE_COPY(LinkMovie) }; /** * OCGState: an optional content group state change. */ class POPPLER_QT6_EXPORT LinkOCGState : public Link { friend class OptContentModel; public: /** * Create a new OCGState link. This is only used by Poppler::Page. */ explicit LinkOCGState(LinkOCGStatePrivate *ocgp); /** * Destructor. */ ~LinkOCGState() override; LinkType linkType() const override; private: Q_DECLARE_PRIVATE(LinkOCGState) Q_DISABLE_COPY(LinkOCGState) }; /** * Hide: an action to show / hide a field. */ class POPPLER_QT6_EXPORT LinkHide : public Link { public: /** * Create a new Hide link. This is only used by Poppler::Page. */ explicit LinkHide(LinkHidePrivate *lhidep); /** * Destructor. */ ~LinkHide() override; LinkType linkType() const override; /** * The fully qualified target names of the action. */ QVector targets() const; /** * Should this action change the visibility of the target to true. */ bool isShowAction() const; private: Q_DECLARE_PRIVATE(LinkHide) Q_DISABLE_COPY(LinkHide) }; } #endif poppler-24.02.0/qt6/src/poppler-media.cc000066400000000000000000000105311455701731300177140ustar00rootroot00000000000000/* poppler-media.cc: qt interface to poppler * Copyright (C) 2012 Guillermo A. Amaral B. * Copyright (C) 2013, 2018, 2021 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 * 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 "poppler-media.h" #include "Rendition.h" #include "poppler-private.h" #include #define BUFFER_MAX 4096 namespace Poppler { class MediaRenditionPrivate { public: explicit MediaRenditionPrivate(::MediaRendition *renditionA) : rendition(renditionA) { } ~MediaRenditionPrivate() { delete rendition; } MediaRenditionPrivate(const MediaRenditionPrivate &) = delete; MediaRenditionPrivate &operator=(const MediaRenditionPrivate &) = delete; ::MediaRendition *rendition; }; MediaRendition::MediaRendition(::MediaRendition *rendition) : d_ptr(new MediaRenditionPrivate(rendition)) { } MediaRendition::~MediaRendition() { delete d_ptr; } bool MediaRendition::isValid() const { Q_D(const MediaRendition); return d->rendition && d->rendition->isOk(); } QString MediaRendition::contentType() const { Q_ASSERT(isValid() && "Invalid media rendition."); Q_D(const MediaRendition); return UnicodeParsedString(d->rendition->getContentType()); } QString MediaRendition::fileName() const { Q_ASSERT(isValid() && "Invalid media rendition."); Q_D(const MediaRendition); return UnicodeParsedString(d->rendition->getFileName()); } bool MediaRendition::isEmbedded() const { Q_ASSERT(isValid() && "Invalid media rendition."); Q_D(const MediaRendition); return d->rendition->getIsEmbedded(); } QByteArray MediaRendition::data() const { Q_ASSERT(isValid() && "Invalid media rendition."); Q_D(const MediaRendition); Stream *s = d->rendition->getEmbbededStream(); if (!s) { 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); } buffer.close(); return buffer.data(); } bool MediaRendition::autoPlay() const { Q_D(const MediaRendition); if (d->rendition->getBEParameters()) { return d->rendition->getBEParameters()->autoPlay; } else if (d->rendition->getMHParameters()) { return d->rendition->getMHParameters()->autoPlay; } else { qDebug("No BE or MH parameters to reference!"); } return false; } bool MediaRendition::showControls() const { Q_D(const MediaRendition); if (d->rendition->getBEParameters()) { return d->rendition->getBEParameters()->showControls; } else if (d->rendition->getMHParameters()) { return d->rendition->getMHParameters()->showControls; } else { qDebug("No BE or MH parameters to reference!"); } return false; } float MediaRendition::repeatCount() const { Q_D(const MediaRendition); if (d->rendition->getBEParameters()) { return d->rendition->getBEParameters()->repeatCount; } else if (d->rendition->getMHParameters()) { return d->rendition->getMHParameters()->repeatCount; } else { qDebug("No BE or MH parameters to reference!"); } return 1.f; } QSize MediaRendition::size() const { Q_D(const MediaRendition); const MediaParameters *mp = nullptr; if (d->rendition->getBEParameters()) { mp = d->rendition->getBEParameters(); } else if (d->rendition->getMHParameters()) { mp = d->rendition->getMHParameters(); } else { qDebug("No BE or MH parameters to reference!"); } if (mp) { return QSize(mp->windowParams.width, mp->windowParams.height); } return QSize(); } } poppler-24.02.0/qt6/src/poppler-media.h000066400000000000000000000044741455701731300175670ustar00rootroot00000000000000/* poppler-media.h: qt interface to poppler * Copyright (C) 2012 Guillermo A. Amaral B. * Copyright (C) 2012, 2013, 2021 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 * 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_MEDIARENDITION_H__ #define __POPPLER_MEDIARENDITION_H__ #include "poppler-export.h" #include #include class MediaRendition; class QIODevice; namespace Poppler { class MediaRenditionPrivate; /** Qt wrapper for MediaRendition. */ class POPPLER_QT6_EXPORT MediaRendition { public: /** Constructs a MediaRendition. Takes ownership of the passed rendition */ explicit MediaRendition(::MediaRendition *rendition); ~MediaRendition(); /** Check if wrapper is holding a valid rendition object. */ bool isValid() const; /** Returns content type. */ QString contentType() const; /** Returns file name. */ QString fileName() const; /** Returns true if media is embedded. */ bool isEmbedded() const; /** Returns data buffer. */ QByteArray data() const; /** Convenience accessor for auto-play parameter. */ bool autoPlay() const; /** Convenience accessor for show controls parameter. */ bool showControls() const; /** Convenience accessor for repeat count parameter. */ float repeatCount() const; /** Convenience accessor for size parameter. */ QSize size() const; private: Q_DECLARE_PRIVATE(MediaRendition) MediaRenditionPrivate *d_ptr; Q_DISABLE_COPY(MediaRendition) }; } #endif /* __POPPLER_MEDIARENDITION_H__ */ poppler-24.02.0/qt6/src/poppler-movie.cc000066400000000000000000000054351455701731300177630ustar00rootroot00000000000000/* poppler-sound.cc: qt interface to poppler * Copyright (C) 2008, 2010, Pino Toscano * Copyright (C) 2008, 2018, 2022, Albert Astals Cid * Copyright (C) 2010, Carlos Garcia Campos * Copyright (C) 2012, Tobias Koenig * * 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 "poppler-qt6.h" #include "Object.h" #include "Annot.h" #include "Movie.h" #include namespace Poppler { class MovieData { public: MovieData() : m_movieObj(nullptr) { } ~MovieData() = default; MovieData(const MovieData &) = delete; MovieData &operator=(const MovieData &) = delete; std::unique_ptr m_movieObj; QSize m_size; int m_rotation; QImage m_posterImage; MovieObject::PlayMode m_playMode : 3; bool m_showControls : 1; }; MovieObject::MovieObject(AnnotMovie *ann) { m_movieData = new MovieData(); m_movieData->m_movieObj = ann->getMovie()->copy(); // TODO: copy poster image const MovieActivationParameters *mp = m_movieData->m_movieObj->getActivationParameters(); int width, height; m_movieData->m_movieObj->getFloatingWindowSize(&width, &height); m_movieData->m_size = QSize(width, height); m_movieData->m_rotation = m_movieData->m_movieObj->getRotationAngle(); m_movieData->m_showControls = mp->showControls; m_movieData->m_playMode = (MovieObject::PlayMode)mp->repeatMode; } MovieObject::~MovieObject() { delete m_movieData; } QString MovieObject::url() const { const GooString *goo = m_movieData->m_movieObj->getFileName(); return goo ? QString(goo->c_str()) : QString(); } QSize MovieObject::size() const { return m_movieData->m_size; } int MovieObject::rotation() const { return m_movieData->m_rotation; } bool MovieObject::showControls() const { return m_movieData->m_showControls; } MovieObject::PlayMode MovieObject::playMode() const { return m_movieData->m_playMode; } bool MovieObject::showPosterImage() const { return (m_movieData->m_movieObj->getShowPoster() == true); } QImage MovieObject::posterImage() const { return m_movieData->m_posterImage; } } poppler-24.02.0/qt6/src/poppler-optcontent-private.h000066400000000000000000000076611455701731300223560ustar00rootroot00000000000000/* poppler-optcontent-private.h: qt interface to poppler * * Copyright (C) 2007, Brad Hards * Copyright (C) 2008, Pino Toscano * Copyright (C) 2016, 2018, 2019, 2021, Albert Astals Cid * Copyright (C) 2017, Hubert Figuière * * 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_OPTCONTENT_PRIVATE_H #define POPPLER_OPTCONTENT_PRIVATE_H #include #include #include class Array; class OCGs; class OptionalContentGroup; class QModelIndex; namespace Poppler { class OptContentItem; class OptContentModel; class OptContentModelPrivate; class RadioButtonGroup { public: RadioButtonGroup(OptContentModelPrivate *ocModel, Array *rbarray); ~RadioButtonGroup(); QSet setItemOn(OptContentItem *itemToSetOn); private: QList itemsInGroup; }; class OptContentItem { public: enum ItemState { On, Off, HeadingOnly }; explicit OptContentItem(OptionalContentGroup *group); explicit OptContentItem(const QString &label); OptContentItem(); ~OptContentItem(); QString name() const { return m_name; } ItemState state() const { return m_stateBackup; } void setState(ItemState state, bool obeyRadioGroups, QSet &changedItems); QList childList() { return m_children; } void setParent(OptContentItem *parent) { m_parent = parent; } OptContentItem *parent() { return m_parent; } void addChild(OptContentItem *child); void appendRBGroup(RadioButtonGroup *rbgroup); bool isEnabled() const { return m_enabled; } QSet recurseListChildren(bool includeMe = false) const; OptionalContentGroup *group() const { return m_group; } private: OptionalContentGroup *m_group; QString m_name; ItemState m_state; // true for ON, false for OFF ItemState m_stateBackup; QList m_children; OptContentItem *m_parent; QList m_rbGroups; bool m_enabled; }; class OptContentModelPrivate { public: OptContentModelPrivate(OptContentModel *qq, OCGs *optContent); ~OptContentModelPrivate(); OptContentModelPrivate(const OptContentModelPrivate &) = delete; OptContentModelPrivate &operator=(const OptContentModelPrivate &) = delete; void parseRBGroupsArray(Array *rBGroupArray); OptContentItem *nodeFromIndex(const QModelIndex &index, bool canBeNull = false) const; QModelIndex indexFromItem(OptContentItem *node, int column) const; /** Get the OptContentItem corresponding to a given reference value. \param ref the reference number (e.g. from Object.getRefNum()) to look up \return the matching optional content item, or null if the reference wasn't found */ OptContentItem *itemFromRef(const QString &ref) const; void setRootNode(OptContentItem *node); OptContentModel *q; QMap m_optContentItems; QList m_headerOptContentItems; QList m_rbgroups; OptContentItem *m_rootNode; private: void addChild(OptContentItem *parent, OptContentItem *child); void parseOrderArray(OptContentItem *parentNode, Array *orderArray); }; } #endif poppler-24.02.0/qt6/src/poppler-optcontent.cc000066400000000000000000000330211455701731300210310ustar00rootroot00000000000000/* poppler-optcontent.cc: qt interface to poppler * * Copyright (C) 2007, Brad Hards * Copyright (C) 2008, 2014, Pino Toscano * Copyright (C) 2008, Carlos Garcia Campos * Copyright (C) 2015-2019, Albert Astals Cid * Copyright (C) 2017, Hubert Figuière * 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 * * 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 "poppler-optcontent.h" #include "poppler-optcontent-private.h" #include "poppler-private.h" #include "poppler-link-private.h" #include #include #include "poppler/OptionalContent.h" #include "poppler/Link.h" namespace Poppler { RadioButtonGroup::RadioButtonGroup(OptContentModelPrivate *ocModel, Array *rbarray) { itemsInGroup.reserve(rbarray->getLength()); for (int i = 0; i < rbarray->getLength(); ++i) { const Object &ref = rbarray->getNF(i); if (!ref.isRef()) { qDebug() << "expected ref, but got:" << ref.getType(); } OptContentItem *item = ocModel->itemFromRef(QString::number(ref.getRefNum())); itemsInGroup.append(item); } for (OptContentItem *item : std::as_const(itemsInGroup)) { item->appendRBGroup(this); } } RadioButtonGroup::~RadioButtonGroup() { } QSet RadioButtonGroup::setItemOn(OptContentItem *itemToSetOn) { QSet changedItems; for (OptContentItem *thisItem : std::as_const(itemsInGroup)) { if (thisItem != itemToSetOn) { QSet newChangedItems; thisItem->setState(OptContentItem::Off, false /*obeyRadioGroups*/, newChangedItems); changedItems += newChangedItems; } } return changedItems; } OptContentItem::OptContentItem(OptionalContentGroup *group) { m_group = group; m_parent = nullptr; m_name = UnicodeParsedString(group->getName()); if (group->getState() == OptionalContentGroup::On) { m_state = OptContentItem::On; } else { m_state = OptContentItem::Off; } m_stateBackup = m_state; m_enabled = true; } OptContentItem::OptContentItem(const QString &label) { m_parent = nullptr; m_name = label; m_group = nullptr; m_state = OptContentItem::HeadingOnly; m_stateBackup = m_state; m_enabled = true; } OptContentItem::OptContentItem() : m_parent(nullptr), m_enabled(true) { } OptContentItem::~OptContentItem() { } void OptContentItem::appendRBGroup(RadioButtonGroup *rbgroup) { m_rbGroups.append(rbgroup); } void OptContentItem::setState(ItemState state, bool obeyRadioGroups, QSet &changedItems) { if (state == m_state) { return; } m_state = state; m_stateBackup = m_state; changedItems.insert(this); QSet empty; Q_FOREACH (OptContentItem *child, m_children) { ItemState oldState = child->m_stateBackup; child->setState(state == OptContentItem::On ? child->m_stateBackup : OptContentItem::Off, true /*obeyRadioGroups*/, empty); child->m_enabled = state == OptContentItem::On; child->m_stateBackup = oldState; } if (!m_group) { return; } if (state == OptContentItem::On) { m_group->setState(OptionalContentGroup::On); if (obeyRadioGroups) { for (RadioButtonGroup *rbgroup : std::as_const(m_rbGroups)) { changedItems += rbgroup->setItemOn(this); } } } else if (state == OptContentItem::Off) { m_group->setState(OptionalContentGroup::Off); } } void OptContentItem::addChild(OptContentItem *child) { m_children += child; child->setParent(this); } QSet OptContentItem::recurseListChildren(bool includeMe) const { QSet ret; if (includeMe) { ret.insert(const_cast(this)); } Q_FOREACH (OptContentItem *child, m_children) { ret += child->recurseListChildren(true); } return ret; } OptContentModelPrivate::OptContentModelPrivate(OptContentModel *qq, OCGs *optContent) : q(qq) { m_rootNode = new OptContentItem(); const auto &ocgs = optContent->getOCGs(); for (const auto &ocg : ocgs) { OptContentItem *node = new OptContentItem(ocg.second.get()); m_optContentItems.insert(QString::number(ocg.first.num), node); } if (optContent->getOrderArray() == nullptr) { // no Order array, so drop them all at the top level QMapIterator i(m_optContentItems); while (i.hasNext()) { i.next(); addChild(m_rootNode, i.value()); } } else { parseOrderArray(m_rootNode, optContent->getOrderArray()); } parseRBGroupsArray(optContent->getRBGroupsArray()); } OptContentModelPrivate::~OptContentModelPrivate() { qDeleteAll(m_optContentItems); qDeleteAll(m_rbgroups); qDeleteAll(m_headerOptContentItems); delete m_rootNode; } void OptContentModelPrivate::parseOrderArray(OptContentItem *parentNode, Array *orderArray) { OptContentItem *lastItem = parentNode; for (int i = 0; i < orderArray->getLength(); ++i) { Object orderItem = orderArray->get(i); if (orderItem.isDict()) { const Object &item = orderArray->getNF(i); if (item.isRef()) { OptContentItem *ocItem = m_optContentItems.value(QString::number(item.getRefNum())); if (ocItem) { addChild(parentNode, ocItem); lastItem = ocItem; } else { qDebug() << "could not find group for object" << item.getRefNum(); } } } else if ((orderItem.isArray()) && (orderItem.arrayGetLength() > 0)) { parseOrderArray(lastItem, orderItem.getArray()); } else if (orderItem.isString()) { const GooString *label = orderItem.getString(); OptContentItem *header = new OptContentItem(UnicodeParsedString(label)); m_headerOptContentItems.append(header); addChild(parentNode, header); parentNode = header; lastItem = header; } else { qDebug() << "something unexpected"; } } } void OptContentModelPrivate::parseRBGroupsArray(Array *rBGroupArray) { if (!rBGroupArray) { return; } // This is an array of array(s) for (int i = 0; i < rBGroupArray->getLength(); ++i) { Object rbObj = rBGroupArray->get(i); if (!rbObj.isArray()) { qDebug() << "expected inner array, got:" << rbObj.getType(); return; } Array *rbarray = rbObj.getArray(); RadioButtonGroup *rbg = new RadioButtonGroup(this, rbarray); m_rbgroups.append(rbg); } } OptContentModel::OptContentModel(OCGs *optContent, QObject *parent) : QAbstractItemModel(parent) { d = new OptContentModelPrivate(this, optContent); } OptContentModel::~OptContentModel() { delete d; } void OptContentModelPrivate::setRootNode(OptContentItem *node) { q->beginResetModel(); delete m_rootNode; m_rootNode = node; q->endResetModel(); } QModelIndex OptContentModel::index(int row, int column, const QModelIndex &parent) const { if (row < 0 || column != 0) { return QModelIndex(); } OptContentItem *parentNode = d->nodeFromIndex(parent); if (row < parentNode->childList().count()) { return createIndex(row, column, parentNode->childList().at(row)); } return QModelIndex(); } QModelIndex OptContentModel::parent(const QModelIndex &child) const { OptContentItem *childNode = d->nodeFromIndex(child); if (!childNode) { return QModelIndex(); } return d->indexFromItem(childNode->parent(), child.column()); } QModelIndex OptContentModelPrivate::indexFromItem(OptContentItem *node, int column) const { if (!node) { return QModelIndex(); } OptContentItem *parentNode = node->parent(); if (!parentNode) { return QModelIndex(); } const int row = parentNode->childList().indexOf(node); return q->createIndex(row, column, node); } int OptContentModel::rowCount(const QModelIndex &parent) const { OptContentItem *parentNode = d->nodeFromIndex(parent); if (!parentNode) { return 0; } else { return parentNode->childList().count(); } } int OptContentModel::columnCount(const QModelIndex &parent) const { return 1; } QVariant OptContentModel::data(const QModelIndex &index, int role) const { OptContentItem *node = d->nodeFromIndex(index, true); if (!node) { return QVariant(); } switch (role) { case Qt::DisplayRole: return node->name(); break; case Qt::EditRole: if (node->state() == OptContentItem::On) { return true; } else if (node->state() == OptContentItem::Off) { return false; } break; case Qt::CheckStateRole: if (node->state() == OptContentItem::On) { return Qt::Checked; } else if (node->state() == OptContentItem::Off) { return Qt::Unchecked; } break; } return QVariant(); } bool OptContentModel::setData(const QModelIndex &index, const QVariant &value, int role) { OptContentItem *node = d->nodeFromIndex(index, true); if (!node) { return false; } switch (role) { case Qt::CheckStateRole: { const bool newvalue = value.toBool(); QSet changedItems; node->setState(newvalue ? OptContentItem::On : OptContentItem::Off, true /*obeyRadioGroups*/, changedItems); if (!changedItems.isEmpty()) { changedItems += node->recurseListChildren(false); QModelIndexList indexes; Q_FOREACH (OptContentItem *item, changedItems) { indexes.append(d->indexFromItem(item, 0)); } std::stable_sort(indexes.begin(), indexes.end()); Q_FOREACH (const QModelIndex &changedIndex, indexes) { emit dataChanged(changedIndex, changedIndex); } return true; } break; } } return false; } Qt::ItemFlags OptContentModel::flags(const QModelIndex &index) const { OptContentItem *node = d->nodeFromIndex(index); Qt::ItemFlags itemFlags = Qt::ItemIsSelectable | Qt::ItemIsUserCheckable; if (node->isEnabled()) { itemFlags |= Qt::ItemIsEnabled; } return itemFlags; } QVariant OptContentModel::headerData(int section, Qt::Orientation orientation, int role) const { return QAbstractItemModel::headerData(section, orientation, role); } void OptContentModel::applyLink(LinkOCGState *link) { LinkOCGStatePrivate *linkPrivate = link->d_func(); QSet changedItems; const std::vector<::LinkOCGState::StateList> &statesList = linkPrivate->stateList; for (const ::LinkOCGState::StateList &stateList : statesList) { const std::vector &refsList = stateList.list; for (const Ref &ref : refsList) { OptContentItem *item = d->itemFromRef(QString::number(ref.num)); if (stateList.st == ::LinkOCGState::On) { item->setState(OptContentItem::On, linkPrivate->preserveRB, changedItems); } else if (stateList.st == ::LinkOCGState::Off) { item->setState(OptContentItem::Off, linkPrivate->preserveRB, changedItems); } else { OptContentItem::ItemState newState = item->state() == OptContentItem::On ? OptContentItem::Off : OptContentItem::On; item->setState(newState, linkPrivate->preserveRB, changedItems); } } } if (!changedItems.isEmpty()) { QSet aux; Q_FOREACH (OptContentItem *item, aux) { changedItems += item->recurseListChildren(false); } QModelIndexList indexes; Q_FOREACH (OptContentItem *item, changedItems) { indexes.append(d->indexFromItem(item, 0)); } std::stable_sort(indexes.begin(), indexes.end()); Q_FOREACH (const QModelIndex &changedIndex, indexes) { emit dataChanged(changedIndex, changedIndex); } } } void OptContentModelPrivate::addChild(OptContentItem *parent, OptContentItem *child) { parent->addChild(child); } OptContentItem *OptContentModelPrivate::itemFromRef(const QString &ref) const { return m_optContentItems.value(ref); } OptContentItem *OptContentModelPrivate::nodeFromIndex(const QModelIndex &index, bool canBeNull) const { if (index.isValid()) { return static_cast(index.internalPointer()); } else { return canBeNull ? nullptr : m_rootNode; } } } poppler-24.02.0/qt6/src/poppler-optcontent.h000066400000000000000000000051061455701731300206760ustar00rootroot00000000000000/* poppler-optcontent.h: qt interface to poppler * * Copyright (C) 2007, Brad Hards * Copyright (C) 2008, Pino Toscano * Copyright (C) 2013, Anthony Granger * Copyright (C) 2016, 2021, 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 * 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_OPTCONTENT_H #define POPPLER_OPTCONTENT_H #include #include "poppler-export.h" #include "poppler-link.h" class OCGs; namespace Poppler { class Document; class OptContentModelPrivate; /** * \brief Model for optional content * * OptContentModel is an item model representing the optional content items * that can be found in PDF documents. * * The model offers a mostly read-only display of the data, allowing to * enable/disable some contents setting the Qt::CheckStateRole data role. */ class POPPLER_QT6_EXPORT OptContentModel : public QAbstractItemModel { friend class Document; Q_OBJECT public: ~OptContentModel() override; QModelIndex index(int row, int column, const QModelIndex &parent) const override; QModelIndex parent(const QModelIndex &child) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent) const override; QVariant data(const QModelIndex &index, int role) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; Qt::ItemFlags flags(const QModelIndex &index) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; /** * Applies the Optional Content Changes specified by that link. */ void applyLink(LinkOCGState *link); private: explicit OptContentModel(OCGs *optContent, QObject *parent = nullptr); friend class OptContentModelPrivate; OptContentModelPrivate *d; }; } #endif poppler-24.02.0/qt6/src/poppler-outline-private.h000066400000000000000000000026541455701731300216350ustar00rootroot00000000000000/* poppler-outline-private.h: qt interface to poppler * * Copyright (C) 2018 Adam Reichold * Copyright (C) 2019 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 * 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_OUTLINE_PRIVATE_H_ #define _POPPLER_OUTLINE_PRIVATE_H_ #include #include class OutlineItem; namespace Poppler { class DocumentData; class LinkDestination; struct OutlineItemData { OutlineItemData(::OutlineItem *oi, DocumentData *dd) : data { oi }, documentData { dd } { } ::OutlineItem *data; DocumentData *documentData; mutable QString name; mutable QSharedPointer destination; mutable QString externalFileName; mutable QString uri; }; } #endif poppler-24.02.0/qt6/src/poppler-outline.cc000066400000000000000000000121641455701731300203200ustar00rootroot00000000000000/* poppler-outline.cc: qt interface to poppler * * 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 * 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 "poppler-private.h" #include "poppler-outline-private.h" #include "Link.h" #include "Outline.h" namespace Poppler { OutlineItem::OutlineItem() : m_data { new OutlineItemData { nullptr, nullptr } } { } OutlineItem::OutlineItem(OutlineItemData *data) : m_data { data } { } OutlineItem::~OutlineItem() { delete m_data; m_data = nullptr; } OutlineItem::OutlineItem(const OutlineItem &other) : m_data { new OutlineItemData { *other.m_data } } { } OutlineItem &OutlineItem::operator=(const OutlineItem &other) { if (this == &other) { return *this; } auto *data = new OutlineItemData { *other.m_data }; qSwap(m_data, data); delete data; return *this; } OutlineItem::OutlineItem(OutlineItem &&other) noexcept : m_data { other.m_data } { other.m_data = nullptr; } OutlineItem &OutlineItem::operator=(OutlineItem &&other) noexcept { qSwap(m_data, other.m_data); return *this; } bool OutlineItem::isNull() const { return !m_data->data; } QString OutlineItem::name() const { QString &name = m_data->name; if (name.isEmpty()) { if (const ::OutlineItem *data = m_data->data) { name = unicodeToQString(data->getTitle()); } } return name; } bool OutlineItem::isOpen() const { bool isOpen = false; if (const ::OutlineItem *data = m_data->data) { isOpen = data->isOpen(); } return isOpen; } QSharedPointer OutlineItem::destination() const { QSharedPointer &destination = m_data->destination; if (!destination) { if (const ::OutlineItem *data = m_data->data) { if (const ::LinkAction *action = data->getAction()) { if (action->getKind() == actionGoTo) { const auto *linkGoTo = static_cast(action); destination.reset(new LinkDestination(LinkDestinationData(linkGoTo->getDest(), linkGoTo->getNamedDest(), m_data->documentData, false))); } else if (action->getKind() == actionGoToR) { const auto *linkGoToR = static_cast(action); const bool external = linkGoToR->getFileName() != nullptr; destination.reset(new LinkDestination(LinkDestinationData(linkGoToR->getDest(), linkGoToR->getNamedDest(), m_data->documentData, external))); } } } } return destination; } QString OutlineItem::externalFileName() const { QString &externalFileName = m_data->externalFileName; if (externalFileName.isEmpty()) { if (const ::OutlineItem *data = m_data->data) { if (const ::LinkAction *action = data->getAction()) { if (action->getKind() == actionGoToR) { if (const GooString *fileName = static_cast(action)->getFileName()) { externalFileName = UnicodeParsedString(fileName); } } } } } return externalFileName; } QString OutlineItem::uri() const { QString &uri = m_data->uri; if (uri.isEmpty()) { if (const ::OutlineItem *data = m_data->data) { if (const ::LinkAction *action = data->getAction()) { if (action->getKind() == actionURI) { uri = UnicodeParsedString(static_cast(action)->getURI()); } } } } return uri; } bool OutlineItem::hasChildren() const { bool result = false; if (::OutlineItem *data = m_data->data) { result = data->hasKids(); } return result; } QVector OutlineItem::children() const { QVector result; if (::OutlineItem *data = m_data->data) { data->open(); if (const std::vector<::OutlineItem *> *kids = data->getKids()) { for (void *kid : *kids) { result.push_back(OutlineItem { new OutlineItemData { static_cast<::OutlineItem *>(kid), m_data->documentData } }); } } } return result; } } poppler-24.02.0/qt6/src/poppler-page-private.h000066400000000000000000000042011455701731300210600ustar00rootroot00000000000000/* poppler-page.cc: qt interface to poppler * Copyright (C) 2005, Net Integration Technologies, Inc. * Copyright (C) 2007, 2012, Albert Astals Cid * Copyright (C) 2008, Pino Toscano * Copyright (C) 2015 Adam Reichold * Copyright (C) 2018, 2021 Nelson Benítez León * Copyright (C) 2021, Oliver Sander * * 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_PAGE_PRIVATE_H_ #define _POPPLER_PAGE_PRIVATE_H_ #include "CharTypes.h" class QRectF; class LinkAction; class Page; class TextPage; namespace Poppler { class DocumentData; class PageTransition; class PageData { public: std::unique_ptr convertLinkActionToLink(::LinkAction *a, const QRectF &linkArea); DocumentData *parentDoc; ::Page *page; int index; PageTransition *transition; static std::unique_ptr convertLinkActionToLink(::LinkAction *a, DocumentData *parentDoc, const QRectF &linkArea); TextPage *prepareTextSearch(const QString &text, Page::Rotation rotate, QVector *u); bool performSingleTextSearch(TextPage *textPage, QVector &u, double &sLeft, double &sTop, double &sRight, double &sBottom, Page::SearchDirection direction, bool sCase, bool sWords, bool sDiacritics, bool sAcrossLines); QList performMultipleTextSearch(TextPage *textPage, QVector &u, bool sCase, bool sWords, bool sDiacritics, bool sAcrossLines); }; } #endif poppler-24.02.0/qt6/src/poppler-page-transition-private.h000066400000000000000000000020211455701731300232460ustar00rootroot00000000000000/* * Copyright (C) 2005, 2019, Albert Astals Cid * Copyright (C) 2019 Oliver Sander * * 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_PAGE_TRANSITION_PRIVATE_H_ #define _POPPLER_PAGE_TRANSITION_PRIVATE_H_ class Object; namespace Poppler { class PageTransitionParams { public: Object *dictObj; }; } #endif poppler-24.02.0/qt6/src/poppler-page-transition.cc000066400000000000000000000050641455701731300217460ustar00rootroot00000000000000/* PageTransition.cc * Copyright (C) 2005, Net Integration Technologies, Inc. * Copyright (C) 2015, Arseniy Lartsev * Copyright (C) 2018, 2021 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 * 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 "PageTransition.h" #include "poppler-page-transition.h" #include "poppler-page-transition-private.h" namespace Poppler { class PageTransitionData { public: explicit PageTransitionData(Object *trans) { pt = new ::PageTransition(trans); } PageTransitionData(const PageTransitionData &ptd) { pt = new ::PageTransition(*ptd.pt); } ~PageTransitionData() { delete pt; } PageTransitionData &operator=(const PageTransitionData &) = delete; ::PageTransition *pt; }; PageTransition::PageTransition(const PageTransitionParams params) { data = new PageTransitionData(params.dictObj); } PageTransition::PageTransition(const PageTransition &pt) { data = new PageTransitionData(*pt.data); } PageTransition::~PageTransition() { delete data; } PageTransition &PageTransition::operator=(const PageTransition &other) { if (this != &other) { delete data; data = new PageTransitionData(*other.data); } return *this; } PageTransition::Type PageTransition::type() const { return (Poppler::PageTransition::Type)data->pt->getType(); } double PageTransition::durationReal() const { return data->pt->getDuration(); } PageTransition::Alignment PageTransition::alignment() const { return (Poppler::PageTransition::Alignment)data->pt->getAlignment(); } PageTransition::Direction PageTransition::direction() const { return (Poppler::PageTransition::Direction)data->pt->getDirection(); } int PageTransition::angle() const { return data->pt->getAngle(); } double PageTransition::scale() const { return data->pt->getScale(); } bool PageTransition::isRectangular() const { return data->pt->isRectangular(); } } poppler-24.02.0/qt6/src/poppler-page-transition.h000066400000000000000000000067251455701731300216150ustar00rootroot00000000000000/* PageTransition.h * Copyright (C) 2005, Net Integration Technologies, Inc. * Copyright (C) 2005, Brad Hards * Copyright (C) 2015, Arseniy Lartsev * Copyright (C) 2018, 2021, 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 * 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 __PAGETRANSITION_X_H__ #define __PAGETRANSITION_X_H__ #include "poppler-export.h" #include namespace Poppler { class PageTransitionParams; class PageTransitionData; /** \brief Describes how a PDF file viewer shall perform the transition from one page to another In PDF files there is a way to specify if the viewer shall use certain effects to perform the transition from one page to another. This feature can be used, e.g., in a PDF-based beamer presentation. This utility class represents the transition effect, and can be used to extract the information from a PDF object. */ class POPPLER_QT6_EXPORT PageTransition { public: /** \brief transition effect that shall be used */ // if changed remember to keep in sync with PageTransition.h enum enum Type { Replace = 0, Split, Blinds, Box, Wipe, Dissolve, Glitter, Fly, Push, Cover, Uncover, Fade }; /** \brief alignment of the transition effect that shall be used */ // if changed remember to keep in sync with PageTransition.h enum enum Alignment { Horizontal = 0, Vertical }; /** \brief direction of the transition effect that shall be used */ // if changed remember to keep in sync with PageTransition.h enum enum Direction { Inward = 0, Outward }; explicit PageTransition(const PageTransitionParams params); /** \brief copy constructor */ PageTransition(const PageTransition &pt); /** \brief assignment operator */ PageTransition &operator=(const PageTransition &other); /** Destructor */ ~PageTransition(); /** \brief Get type of the transition. */ Type type() const; /** \brief Get duration of the transition in seconds */ double durationReal() const; /** \brief Get dimension in which the transition effect occurs. */ Alignment alignment() const; /** \brief Get direction of motion of the transition effect. */ Direction direction() const; /** \brief Get direction in which the transition effect moves. */ int angle() const; /** \brief Get starting or ending scale. */ double scale() const; /** \brief Returns true if the area to be flown is rectangular and opaque. */ bool isRectangular() const; private: PageTransitionData *data; }; } #endif poppler-24.02.0/qt6/src/poppler-page.cc000066400000000000000000001041511455701731300175530ustar00rootroot00000000000000/* 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, Stefan Kebekus * Copyright (C) 2006-2011, Pino Toscano * Copyright (C) 2008 Carlos Garcia Campos * Copyright (C) 2009 Shawn Rutledge * Copyright (C) 2010, 2012, Guillermo Amaral * Copyright (C) 2010 Suzuki Toshiya * Copyright (C) 2010 Matthias Fauconneau * Copyright (C) 2010 Hib Eris * Copyright (C) 2012 Tobias Koenig * Copyright (C) 2012 Fabio D'Urso * Copyright (C) 2012, 2015 Adam Reichold * Copyright (C) 2012, 2013 Thomas Freitag * Copyright (C) 2015 William Bader * Copyright (C) 2016 Arseniy Lartsev * Copyright (C) 2016, Hanno Meyer-Thurow * Copyright (C) 2017-2021, Oliver Sander * Copyright (C) 2017 Adrian Johnson * 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 Intevation GmbH * Copyright (C) 2018, Tobias Deiminger * Copyright (C) 2018, 2021 Nelson Benítez León * 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 * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "poppler-private.h" #include "poppler-page-transition-private.h" #include "poppler-page-private.h" #include "poppler-link-extractor-private.h" #include "poppler-link-private.h" #include "poppler-annotation-private.h" #include "poppler-form.h" #include "poppler-media.h" namespace Poppler { class TextExtractionAbortHelper { public: TextExtractionAbortHelper(Page::ShouldAbortQueryFunc shouldAbortCallback, const QVariant &payloadA) { shouldAbortExtractionCallback = shouldAbortCallback; payload = payloadA; } Page::ShouldAbortQueryFunc shouldAbortExtractionCallback = nullptr; QVariant payload; }; class OutputDevCallbackHelper { public: void setCallbacks(Page::RenderToImagePartialUpdateFunc callback, Page::ShouldRenderToImagePartialQueryFunc shouldDoCallback, Page::ShouldAbortQueryFunc shouldAbortCallback, const QVariant &payloadA) { partialUpdateCallback = callback; shouldDoPartialUpdateCallback = shouldDoCallback; shouldAbortRenderCallback = shouldAbortCallback; payload = payloadA; } Page::RenderToImagePartialUpdateFunc partialUpdateCallback = nullptr; Page::ShouldRenderToImagePartialQueryFunc shouldDoPartialUpdateCallback = nullptr; Page::ShouldAbortQueryFunc shouldAbortRenderCallback = nullptr; QVariant payload; }; class Qt6SplashOutputDev : public SplashOutputDev, public OutputDevCallbackHelper { public: Qt6SplashOutputDev(SplashColorMode colorModeA, int bitmapRowPadA, bool reverseVideoA, bool ignorePaperColorA, SplashColorPtr paperColorA, bool bitmapTopDownA, SplashThinLineMode thinLineMode, bool overprintPreviewA) : SplashOutputDev(colorModeA, bitmapRowPadA, reverseVideoA, paperColorA, bitmapTopDownA, thinLineMode, overprintPreviewA), ignorePaperColor(ignorePaperColorA) { } ~Qt6SplashOutputDev() override; void dump() override { if (partialUpdateCallback && shouldDoPartialUpdateCallback && shouldDoPartialUpdateCallback(payload)) { partialUpdateCallback(getXBGRImage(false /* takeImageData */), payload); } } QImage getXBGRImage(bool takeImageData) { SplashBitmap *b = getBitmap(); // 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; 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) { // Convert byte order from RGBX to XBGR. for (int i = 0; i < bh; ++i) { for (int j = 0; j < bw; ++j) { SplashColorPtr pixel = &data[i * brs + j]; qSwap(pixel[0], pixel[3]); qSwap(pixel[1], pixel[2]); } } } if (takeImageData) { // Construct a Qt image holding (and also owning) the raw bitmap data. QImage i(data, bw, bh, brs, format, gfree, data); if (i.isNull()) { gfree(data); } return i; } else { return QImage(data, bw, bh, brs, format).copy(); } } return QImage(); } private: bool ignorePaperColor; }; Qt6SplashOutputDev::~Qt6SplashOutputDev() = default; class QImageDumpingQPainterOutputDev : public QPainterOutputDev, public OutputDevCallbackHelper { public: QImageDumpingQPainterOutputDev(QPainter *painter, QImage *i) : QPainterOutputDev(painter), image(i) { } ~QImageDumpingQPainterOutputDev() override; void dump() override { if (partialUpdateCallback && shouldDoPartialUpdateCallback && shouldDoPartialUpdateCallback(payload)) { partialUpdateCallback(*image, payload); } } private: QImage *image; }; QImageDumpingQPainterOutputDev::~QImageDumpingQPainterOutputDev() = default; std::unique_ptr PageData::convertLinkActionToLink(::LinkAction *a, const QRectF &linkArea) { return convertLinkActionToLink(a, parentDoc, linkArea); } std::unique_ptr PageData::convertLinkActionToLink(::LinkAction *a, DocumentData *parentDoc, const QRectF &linkArea) { if (!a) { return nullptr; } std::unique_ptr popplerLink; switch (a->getKind()) { case actionGoTo: { LinkGoTo *g = (LinkGoTo *)a; const LinkDestinationData ldd(g->getDest(), g->getNamedDest(), parentDoc, false); // create link: no ext file, namedDest, object pointer popplerLink = std::make_unique(linkArea, QString(), LinkDestination(ldd)); } break; case actionGoToR: { LinkGoToR *g = (LinkGoToR *)a; // copy link file const QString fileName = UnicodeParsedString(g->getFileName()); const LinkDestinationData ldd(g->getDest(), g->getNamedDest(), parentDoc, !fileName.isEmpty()); // create link: fileName, namedDest, object pointer popplerLink = std::make_unique(linkArea, fileName, LinkDestination(ldd)); } break; 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); } break; case actionNamed: { const std::string &name = ((LinkNamed *)a)->getName(); if (name == "NextPage") { popplerLink = std::make_unique(linkArea, LinkAction::PageNext); } else if (name == "PrevPage") { popplerLink = std::make_unique(linkArea, LinkAction::PagePrev); } else if (name == "FirstPage") { popplerLink = std::make_unique(linkArea, LinkAction::PageFirst); } else if (name == "LastPage") { popplerLink = std::make_unique(linkArea, LinkAction::PageLast); } else if (name == "GoBack") { popplerLink = std::make_unique(linkArea, LinkAction::HistoryBack); } else if (name == "GoForward") { popplerLink = std::make_unique(linkArea, LinkAction::HistoryForward); } else if (name == "Quit") { popplerLink = std::make_unique(linkArea, LinkAction::Quit); } else if (name == "GoToPage") { popplerLink = std::make_unique(linkArea, LinkAction::GoToPage); } else if (name == "Find") { popplerLink = std::make_unique(linkArea, LinkAction::Find); } else if (name == "FullScreen") { popplerLink = std::make_unique(linkArea, LinkAction::Presentation); } else if (name == "Print") { popplerLink = std::make_unique(linkArea, LinkAction::Print); } else if (name == "Close") { // acroread closes the document always, doesn't care whether // its presentation mode or not // popplerLink = std::make_unique(linkArea, LinkAction::EndPresentation); popplerLink = std::make_unique(linkArea, LinkAction::Close); } else if (name == "SaveAs") { popplerLink = std::make_unique(linkArea, LinkAction::SaveAs); } else { qWarning() << "Unhandled action name" << name.c_str(); } } break; case actionURI: { popplerLink = std::make_unique(linkArea, ((LinkURI *)a)->getURI().c_str()); } break; case actionSound: { ::LinkSound *ls = (::LinkSound *)a; popplerLink = std::make_unique(linkArea, ls->getVolume(), ls->getSynchronous(), ls->getRepeat(), ls->getMix(), new SoundObject(ls->getSound())); } break; case actionJavaScript: { ::LinkJavaScript *ljs = (::LinkJavaScript *)a; popplerLink = std::make_unique(linkArea, UnicodeParsedString(ljs->getScript())); } break; case actionMovie: { ::LinkMovie *lm = (::LinkMovie *)a; const QString title = (lm->hasAnnotTitle() ? UnicodeParsedString(lm->getAnnotTitle()) : QString()); Ref reference = Ref::INVALID(); if (lm->hasAnnotRef()) { reference = *lm->getAnnotRef(); } LinkMovie::Operation operation = LinkMovie::Play; switch (lm->getOperation()) { case ::LinkMovie::operationTypePlay: operation = LinkMovie::Play; break; case ::LinkMovie::operationTypePause: operation = LinkMovie::Pause; break; case ::LinkMovie::operationTypeResume: operation = LinkMovie::Resume; break; case ::LinkMovie::operationTypeStop: operation = LinkMovie::Stop; break; }; popplerLink = std::make_unique(linkArea, operation, title, reference); } break; case actionRendition: { ::LinkRendition *lrn = (::LinkRendition *)a; Ref reference = Ref::INVALID(); if (lrn->hasScreenAnnot()) { reference = lrn->getScreenAnnot(); } popplerLink = std::make_unique(linkArea, lrn->getMedia() ? lrn->getMedia()->copy() : nullptr, lrn->getOperation(), UnicodeParsedString(lrn->getScript()), reference); } break; case actionOCGState: { ::LinkOCGState *plocg = (::LinkOCGState *)a; LinkOCGStatePrivate *locgp = new LinkOCGStatePrivate(linkArea, plocg->getStateList(), plocg->getPreserveRB()); popplerLink = std::make_unique(locgp); } break; case actionHide: { ::LinkHide *lh = (::LinkHide *)a; LinkHidePrivate *lhp = new LinkHidePrivate(linkArea, lh->hasTargetName() ? UnicodeParsedString(lh->getTargetName()) : QString(), lh->isShowAction()); popplerLink = std::make_unique(lhp); } break; case actionResetForm: // Not handled in Qt6 front-end yet break; case actionUnknown: break; } if (popplerLink) { std::vector> links; for (const std::unique_ptr<::LinkAction> &nextAction : a->nextActions()) { links.push_back(convertLinkActionToLink(nextAction.get(), parentDoc, linkArea)); } LinkPrivate::get(popplerLink.get())->nextLinks = std::move(links); } return popplerLink; } inline TextPage *PageData::prepareTextSearch(const QString &text, Page::Rotation rotate, QVector *u) { *u = text.toUcs4(); const int rotation = (int)rotate * 90; // fetch ourselves a textpage TextOutputDev td(nullptr, true, 0, false, false); parentDoc->doc->displayPage(&td, index + 1, 72, 72, rotation, false, true, false, nullptr, nullptr, nullptr, nullptr, true); TextPage *textPage = td.takeText(); return textPage; } inline bool PageData::performSingleTextSearch(TextPage *textPage, QVector &u, double &sLeft, double &sTop, double &sRight, double &sBottom, Page::SearchDirection direction, bool sCase, bool sWords, bool sDiacritics, bool sAcrossLines) { if (direction == Page::FromTop) { return textPage->findText(u.data(), u.size(), true, true, false, false, sCase, sDiacritics, sAcrossLines, false, sWords, &sLeft, &sTop, &sRight, &sBottom, nullptr, nullptr); } else if (direction == Page::NextResult) { return textPage->findText(u.data(), u.size(), false, true, true, false, sCase, sDiacritics, sAcrossLines, false, sWords, &sLeft, &sTop, &sRight, &sBottom, nullptr, nullptr); } else if (direction == Page::PreviousResult) { return textPage->findText(u.data(), u.size(), false, true, true, false, sCase, sDiacritics, sAcrossLines, true, sWords, &sLeft, &sTop, &sRight, &sBottom, nullptr, nullptr); } return false; } inline QList PageData::performMultipleTextSearch(TextPage *textPage, QVector &u, bool sCase, bool sWords, bool sDiacritics, bool sAcrossLines) { QList results; double sLeft = 0.0, sTop = 0.0, sRight = 0.0, sBottom = 0.0; bool sIgnoredHyphen = false; PDFRectangle continueMatch; continueMatch.x1 = DBL_MAX; // we use this to detect valid return values while (textPage->findText(u.data(), u.size(), false, true, true, false, sCase, sDiacritics, sAcrossLines, false, sWords, &sLeft, &sTop, &sRight, &sBottom, &continueMatch, &sIgnoredHyphen)) { QRectF result; result.setLeft(sLeft); result.setTop(sTop); result.setRight(sRight); result.setBottom(sBottom); results.append(result); if (sAcrossLines && continueMatch.x1 != DBL_MAX) { QRectF resultN; resultN.setLeft(continueMatch.x1); resultN.setTop(continueMatch.y1); resultN.setRight(continueMatch.x2); resultN.setBottom(continueMatch.y1); results.append(resultN); continueMatch.x1 = DBL_MAX; } } return results; } Page::Page(DocumentData *doc, int index) { m_page = new PageData(); m_page->index = index; m_page->parentDoc = doc; m_page->page = doc->doc->getPage(m_page->index + 1); m_page->transition = nullptr; } Page::~Page() { delete m_page->transition; delete m_page; } // Callback that filters out everything but form fields static auto annotDisplayDecideCbk = [](Annot *annot, void *user_data) { // Hide everything but forms return (annot->getType() == Annot::typeWidget); }; // A nullptr, but with the type of a function pointer // Needed to make the ternary operator happy. static bool (*nullAnnotCallBack)(Annot *annot, void *user_data) = nullptr; static auto shouldAbortRenderInternalCallback = [](void *user_data) { OutputDevCallbackHelper *helper = reinterpret_cast(user_data); return helper->shouldAbortRenderCallback(helper->payload); }; static auto shouldAbortExtractionInternalCallback = [](void *user_data) { TextExtractionAbortHelper *helper = reinterpret_cast(user_data); return helper->shouldAbortExtractionCallback(helper->payload); }; // A nullptr, but with the type of a function pointer // Needed to make the ternary operator happy. static bool (*nullAbortCallBack)(void *user_data) = nullptr; static bool renderToQPainter(QImageDumpingQPainterOutputDev *qpainter_output, QPainter *painter, PageData *page, double xres, double yres, int x, int y, int w, int h, Page::Rotation rotate, Page::PainterFlags flags) { const bool savePainter = !(flags & Page::DontSaveAndRestore); if (savePainter) { painter->save(); } if (page->parentDoc->m_hints & Document::Antialiasing) { painter->setRenderHint(QPainter::Antialiasing); } if (page->parentDoc->m_hints & Document::TextAntialiasing) { painter->setRenderHint(QPainter::TextAntialiasing); } painter->translate(x == -1 ? 0 : -x, y == -1 ? 0 : -y); qpainter_output->startDoc(page->parentDoc->doc); const bool hideAnnotations = page->parentDoc->m_hints & Document::HideAnnotations; OutputDevCallbackHelper *abortHelper = qpainter_output; page->parentDoc->doc->displayPageSlice(qpainter_output, page->index + 1, xres, yres, (int)rotate * 90, false, true, false, x, y, w, h, abortHelper->shouldAbortRenderCallback ? shouldAbortRenderInternalCallback : nullAbortCallBack, abortHelper, (hideAnnotations) ? annotDisplayDecideCbk : nullAnnotCallBack, nullptr, true); if (savePainter) { painter->restore(); } return true; } QImage Page::renderToImage(double xres, double yres, int x, int y, int w, int h, Rotation rotate) const { return renderToImage(xres, yres, x, y, w, h, rotate, nullptr, nullptr, QVariant()); } QImage Page::renderToImage(double xres, double yres, int x, int y, int w, int h, Rotation rotate, RenderToImagePartialUpdateFunc partialUpdateCallback, ShouldRenderToImagePartialQueryFunc shouldDoPartialUpdateCallback, const QVariant &payload) const { return renderToImage(xres, yres, x, y, w, h, rotate, partialUpdateCallback, shouldDoPartialUpdateCallback, nullptr, payload); } // Translate the text hinting settings from poppler-speak to Qt-speak static QFont::HintingPreference QFontHintingFromPopplerHinting(int renderHints) { QFont::HintingPreference result = QFont::PreferNoHinting; if (renderHints & Document::TextHinting) { result = (renderHints & Document::TextSlightHinting) ? QFont::PreferVerticalHinting : QFont::PreferFullHinting; } return result; } QImage Page::renderToImage(double xres, double yres, int xPos, int yPos, int w, int h, Rotation rotate, RenderToImagePartialUpdateFunc partialUpdateCallback, ShouldRenderToImagePartialQueryFunc shouldDoPartialUpdateCallback, ShouldAbortQueryFunc shouldAbortRenderCallback, const QVariant &payload) const { int rotation = (int)rotate * 90; QImage img; switch (m_page->parentDoc->m_backend) { case Poppler::Document::SplashBackend: { SplashColor bgColor; const bool overprintPreview = m_page->parentDoc->m_hints & Document::OverprintPreview ? true : false; if (overprintPreview) { unsigned char c, m, y, k; c = 255 - m_page->parentDoc->paperColor.blue(); m = 255 - m_page->parentDoc->paperColor.red(); y = 255 - m_page->parentDoc->paperColor.green(); k = c; if (m < k) { k = m; } if (y < k) { k = y; } bgColor[0] = c - k; bgColor[1] = m - k; bgColor[2] = y - k; bgColor[3] = k; for (int i = 4; i < SPOT_NCOMPS + 4; i++) { bgColor[i] = 0; } } else { bgColor[0] = m_page->parentDoc->paperColor.blue(); bgColor[1] = m_page->parentDoc->paperColor.green(); bgColor[2] = m_page->parentDoc->paperColor.red(); } const SplashColorMode colorMode = overprintPreview ? splashModeDeviceN8 : splashModeXBGR8; SplashThinLineMode thinLineMode = splashThinLineDefault; if (m_page->parentDoc->m_hints & Document::ThinLineShape) { thinLineMode = splashThinLineShape; } if (m_page->parentDoc->m_hints & Document::ThinLineSolid) { thinLineMode = splashThinLineSolid; } const bool ignorePaperColor = m_page->parentDoc->m_hints & Document::IgnorePaperColor; Qt6SplashOutputDev splash_output(colorMode, 4, false, ignorePaperColor, ignorePaperColor ? nullptr : bgColor, true, thinLineMode, overprintPreview); splash_output.setCallbacks(partialUpdateCallback, shouldDoPartialUpdateCallback, shouldAbortRenderCallback, payload); splash_output.setFontAntialias(m_page->parentDoc->m_hints & Document::TextAntialiasing ? true : false); splash_output.setVectorAntialias(m_page->parentDoc->m_hints & Document::Antialiasing ? true : false); splash_output.setFreeTypeHinting(m_page->parentDoc->m_hints & Document::TextHinting ? true : false, m_page->parentDoc->m_hints & Document::TextSlightHinting ? true : false); #ifdef USE_CMS splash_output.setDisplayProfile(m_page->parentDoc->m_displayProfile); #endif splash_output.startDoc(m_page->parentDoc->doc); const bool hideAnnotations = m_page->parentDoc->m_hints & Document::HideAnnotations; OutputDevCallbackHelper *abortHelper = &splash_output; m_page->parentDoc->doc->displayPageSlice(&splash_output, m_page->index + 1, xres, yres, rotation, false, true, false, xPos, yPos, w, h, shouldAbortRenderCallback ? shouldAbortRenderInternalCallback : nullAbortCallBack, abortHelper, (hideAnnotations) ? annotDisplayDecideCbk : nullAnnotCallBack, nullptr, true); img = splash_output.getXBGRImage(true /* takeImageData */); break; } case Poppler::Document::QPainterBackend: { QSize size = pageSize(); QImage tmpimg(w == -1 ? qRound(size.width() * xres / 72.0) : w, h == -1 ? qRound(size.height() * yres / 72.0) : h, QImage::Format_ARGB32); QColor bgColor(m_page->parentDoc->paperColor.red(), m_page->parentDoc->paperColor.green(), m_page->parentDoc->paperColor.blue(), m_page->parentDoc->paperColor.alpha()); tmpimg.fill(bgColor); QPainter painter(&tmpimg); QImageDumpingQPainterOutputDev qpainter_output(&painter, &tmpimg); qpainter_output.setHintingPreference(QFontHintingFromPopplerHinting(m_page->parentDoc->m_hints)); #ifdef USE_CMS qpainter_output.setDisplayProfile(m_page->parentDoc->m_displayProfile); #endif qpainter_output.setCallbacks(partialUpdateCallback, shouldDoPartialUpdateCallback, shouldAbortRenderCallback, payload); renderToQPainter(&qpainter_output, &painter, m_page, xres, yres, xPos, yPos, w, h, rotate, DontSaveAndRestore); painter.end(); img = tmpimg; break; } } if (shouldAbortRenderCallback && shouldAbortRenderCallback(payload)) { return QImage(); } return img; } bool Page::renderToPainter(QPainter *painter, double xres, double yres, int x, int y, int w, int h, Rotation rotate, PainterFlags flags) const { if (!painter) { return false; } switch (m_page->parentDoc->m_backend) { case Poppler::Document::SplashBackend: return false; case Poppler::Document::QPainterBackend: { QImageDumpingQPainterOutputDev qpainter_output(painter, nullptr); qpainter_output.setHintingPreference(QFontHintingFromPopplerHinting(m_page->parentDoc->m_hints)); return renderToQPainter(&qpainter_output, painter, m_page, xres, yres, x, y, w, h, rotate, flags); } } return false; } QImage Page::thumbnail() const { unsigned char *data = nullptr; int w = 0; int h = 0; int rowstride = 0; bool r = m_page->page->loadThumb(&data, &w, &h, &rowstride); QImage ret; if (r) { // first construct a temporary image with the data got, // then force a copy of it so we can free the raw thumbnail data ret = QImage(data, w, h, rowstride, QImage::Format_RGB888).copy(); gfree(data); } return ret; } QString Page::text(const QRectF &r, TextLayout textLayout) const { TextOutputDev *output_dev; GooString *s; QString result; const bool rawOrder = textLayout == RawOrderLayout; output_dev = new TextOutputDev(nullptr, false, 0, rawOrder, false); 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); } else { s = output_dev->getText(r.left(), r.top(), r.right(), r.bottom()); } result = QString::fromUtf8(s->c_str()); delete output_dev; delete s; return result; } QString Page::text(const QRectF &r) const { return text(r, PhysicalLayout); } bool Page::search(const QString &text, double &sLeft, double &sTop, double &sRight, double &sBottom, SearchDirection direction, SearchFlags flags, Rotation rotate) const { const bool sCase = flags.testFlag(IgnoreCase) ? false : true; const bool sWords = flags.testFlag(WholeWords) ? true : false; const bool sDiacritics = flags.testFlag(IgnoreDiacritics) ? true : false; const bool sAcrossLines = flags.testFlag(AcrossLines) ? true : false; QVector u; TextPage *textPage = m_page->prepareTextSearch(text, rotate, &u); const bool found = m_page->performSingleTextSearch(textPage, u, sLeft, sTop, sRight, sBottom, direction, sCase, sWords, sDiacritics, sAcrossLines); textPage->decRefCnt(); return found; } QList Page::search(const QString &text, SearchFlags flags, Rotation rotate) const { const bool sCase = flags.testFlag(IgnoreCase) ? false : true; const bool sWords = flags.testFlag(WholeWords) ? true : false; const bool sDiacritics = flags.testFlag(IgnoreDiacritics) ? true : false; const bool sAcrossLines = flags.testFlag(AcrossLines) ? true : false; QVector u; TextPage *textPage = m_page->prepareTextSearch(text, rotate, &u); QList results = m_page->performMultipleTextSearch(textPage, u, sCase, sWords, sDiacritics, sAcrossLines); textPage->decRefCnt(); return results; } std::vector> Page::textList(Rotation rotate) const { return textList(rotate, nullptr, QVariant()); } std::vector> Page::textList(Rotation rotate, ShouldAbortQueryFunc shouldAbortExtractionCallback, const QVariant &closure) const { std::vector> output_list; TextOutputDev output_dev(nullptr, false, 0, false, false); int rotation = (int)rotate * 90; TextExtractionAbortHelper abortHelper(shouldAbortExtractionCallback, closure); m_page->parentDoc->doc->displayPageSlice(&output_dev, m_page->index + 1, 72, 72, rotation, false, false, false, -1, -1, -1, -1, shouldAbortExtractionCallback ? shouldAbortExtractionInternalCallback : nullAbortCallBack, &abortHelper, nullptr, nullptr, true); std::unique_ptr word_list = output_dev.makeWordList(); if (shouldAbortExtractionCallback && shouldAbortExtractionCallback(closure)) { return output_list; } QHash wordBoxMap; output_list.reserve(word_list->getLength()); for (int i = 0; i < word_list->getLength(); i++) { TextWord *word = word_list->get(i); GooString *gooWord = word->getText(); QString string = QString::fromUtf8(gooWord->c_str()); delete gooWord; double xMin, yMin, xMax, yMax; word->getBBox(&xMin, &yMin, &xMax, &yMax); auto text_box = std::make_unique(string, QRectF(xMin, yMin, xMax - xMin, yMax - yMin)); text_box->m_data->hasSpaceAfter = word->hasSpaceAfter() == true; text_box->m_data->charBBoxes.reserve(word->getLength()); for (int j = 0; j < word->getLength(); ++j) { word->getCharBBox(j, &xMin, &yMin, &xMax, &yMax); text_box->m_data->charBBoxes.append(QRectF(xMin, yMin, xMax - xMin, yMax - yMin)); } wordBoxMap.insert(word, text_box.get()); output_list.push_back(std::move(text_box)); } for (int i = 0; i < word_list->getLength(); i++) { TextWord *word = word_list->get(i); TextBox *text_box = wordBoxMap.value(word); text_box->m_data->nextWord = wordBoxMap.value(word->nextWord()); } return output_list; } PageTransition *Page::transition() const { if (!m_page->transition) { Object o = m_page->page->getTrans(); PageTransitionParams params; params.dictObj = &o; if (params.dictObj->isDict()) { m_page->transition = new PageTransition(params); } } return m_page->transition; } std::unique_ptr Page::action(PageAction act) const { if (act == Page::Opening || act == Page::Closing) { Object o = m_page->page->getActions(); if (!o.isDict()) { return nullptr; } Dict *dict = o.getDict(); const char *key = act == Page::Opening ? "O" : "C"; Object o2 = dict->lookup((char *)key); std::unique_ptr<::LinkAction> lact = ::LinkAction::parseAction(&o2, m_page->parentDoc->doc->getCatalog()->getBaseURI()); if (lact != nullptr) { return m_page->convertLinkActionToLink(lact.get(), QRectF()); } } return nullptr; } QSizeF Page::pageSizeF() const { Page::Orientation orient = orientation(); if ((Page::Landscape == orient) || (Page::Seascape == orient)) { return QSizeF(m_page->page->getCropHeight(), m_page->page->getCropWidth()); } else { return QSizeF(m_page->page->getCropWidth(), m_page->page->getCropHeight()); } } QSize Page::pageSize() const { return pageSizeF().toSize(); } Page::Orientation Page::orientation() const { const int rotation = m_page->page->getRotate(); switch (rotation) { case 90: return Page::Landscape; break; case 180: return Page::UpsideDown; break; case 270: return Page::Seascape; break; default: return Page::Portrait; } } void Page::defaultCTM(double *CTM, double dpiX, double dpiY, int rotate, bool upsideDown) { m_page->page->getDefaultCTM(CTM, dpiX, dpiY, rotate, false, upsideDown); } std::vector> Page::links() const { LinkExtractorOutputDev link_dev(m_page); m_page->parentDoc->doc->processLinks(&link_dev, m_page->index + 1); return link_dev.links(); } std::vector> Page::annotations() const { return AnnotationPrivate::findAnnotations(m_page->page, m_page->parentDoc, QSet()); } std::vector> Page::annotations(const QSet &subtypes) const { return AnnotationPrivate::findAnnotations(m_page->page, m_page->parentDoc, subtypes); } void Page::addAnnotation(const Annotation *ann) { AnnotationPrivate::addAnnotationToPage(m_page->page, m_page->parentDoc, ann); } void Page::removeAnnotation(const Annotation *ann) { AnnotationPrivate::removeAnnotationFromPage(m_page->page, ann); } std::vector> Page::formFields() const { std::vector> fields; ::Page *p = m_page->page; const std::unique_ptr form = p->getFormWidgets(); int formcount = form->getNumWidgets(); for (int i = 0; i < formcount; ++i) { ::FormWidget *fm = form->getWidget(i); std::unique_ptr ff; switch (fm->getType()) { case formButton: { ff = std::make_unique(m_page->parentDoc, p, static_cast(fm)); } break; case formText: { ff = std::make_unique(m_page->parentDoc, p, static_cast(fm)); } break; case formChoice: { ff = std::make_unique(m_page->parentDoc, p, static_cast(fm)); } break; case formSignature: { ff = std::make_unique(m_page->parentDoc, p, static_cast(fm)); } break; default:; } if (ff) { fields.push_back(std::move(ff)); } } return fields; } double Page::duration() const { return m_page->page->getDuration(); } QString Page::label() const { GooString goo; if (!m_page->parentDoc->doc->getCatalog()->indexToLabel(m_page->index, &goo)) { return QString(); } return UnicodeParsedString(&goo); } int Page::index() const { return m_page->index; } } poppler-24.02.0/qt6/src/poppler-pdf-converter.cc000066400000000000000000000230361455701731300214170ustar00rootroot00000000000000/* poppler-pdf-converter.cc: qt interface to poppler * Copyright (C) 2008, Pino Toscano * Copyright (C) 2008, 2009, 2020-2022, 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, . * Copyright (C) 2021, Zachary Travis * Copyright (C) 2021, Georgiy Sgibnev . Work sponsored by lab50.net. * Copyright (C) 2022, Martin * Copyright (C) 2022, Felix Jung * * 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 "poppler-qt6.h" #include "poppler-annotation-helper.h" #include "poppler-annotation-private.h" #include "poppler-private.h" #include "poppler-converter-private.h" #include "poppler-qiodeviceoutstream-private.h" #include #include #include "Array.h" #include "Form.h" #include namespace Poppler { class PDFConverterPrivate : public BaseConverterPrivate { public: PDFConverterPrivate(); ~PDFConverterPrivate() override; PDFConverter::PDFOptions opts; }; PDFConverterPrivate::PDFConverterPrivate() : BaseConverterPrivate() { } PDFConverterPrivate::~PDFConverterPrivate() = default; PDFConverter::PDFConverter(DocumentData *document) : BaseConverter(*new PDFConverterPrivate()) { Q_D(PDFConverter); d->document = document; } PDFConverter::~PDFConverter() { } void PDFConverter::setPDFOptions(PDFConverter::PDFOptions options) { Q_D(PDFConverter); d->opts = options; } PDFConverter::PDFOptions PDFConverter::pdfOptions() const { Q_D(const PDFConverter); return d->opts; } bool PDFConverter::convert() { Q_D(PDFConverter); d->lastError = NoError; if (d->document->locked) { d->lastError = FileLockedError; return false; } QIODevice *dev = d->openDevice(); if (!dev) { d->lastError = OpenOutputError; return false; } bool deleteFile = false; if (QFile *file = qobject_cast(dev)) { deleteFile = !file->exists(); } int errorCode = errNone; QIODeviceOutStream stream(dev); if (d->opts & WithChanges) { errorCode = d->document->doc->saveAs(&stream); } else { errorCode = d->document->doc->saveWithoutChangesAs(&stream); } d->closeDevice(); if (errorCode != errNone) { if (deleteFile) { qobject_cast(dev)->remove(); } if (errorCode == errOpenFile) { d->lastError = OpenOutputError; } else { d->lastError = NotSupportedInputFileError; } } return (errorCode == errNone); } bool PDFConverter::sign(const NewSignatureData &data) { Q_D(PDFConverter); d->lastError = NoError; if (d->document->locked) { d->lastError = FileLockedError; return false; } if (data.signatureText().isEmpty()) { qWarning() << "No signature text given"; return false; } ::PDFDoc *doc = d->document->doc; ::Page *destPage = doc->getPage(data.page() + 1); std::unique_ptr gSignatureText = std::unique_ptr(QStringToUnicodeGooString(data.signatureText())); std::unique_ptr gSignatureLeftText = std::unique_ptr(QStringToUnicodeGooString(data.signatureLeftText())); const auto reason = std::unique_ptr(data.reason().isEmpty() ? nullptr : QStringToUnicodeGooString(data.reason())); 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); } struct PDFConverter::NewSignatureData::NewSignatureDataPrivate { NewSignatureDataPrivate() = default; QString certNickname; QString password; int page; QRectF boundingRectangle; QString signatureText; QString signatureLeftText; QString reason; QString location; 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 partialName = QUuid::createUuid().toString(); QByteArray documentOwnerPassword; QByteArray documentUserPassword; QString imagePath; }; PDFConverter::NewSignatureData::NewSignatureData() : d(new NewSignatureDataPrivate()) { } PDFConverter::NewSignatureData::~NewSignatureData() { delete d; } QString PDFConverter::NewSignatureData::certNickname() const { return d->certNickname; } void PDFConverter::NewSignatureData::setCertNickname(const QString &certNickname) { d->certNickname = certNickname; } QString PDFConverter::NewSignatureData::password() const { return d->password; } void PDFConverter::NewSignatureData::setPassword(const QString &password) { d->password = password; } int PDFConverter::NewSignatureData::page() const { return d->page; } void PDFConverter::NewSignatureData::setPage(int page) { d->page = page; } QRectF PDFConverter::NewSignatureData::boundingRectangle() const { return d->boundingRectangle; } void PDFConverter::NewSignatureData::setBoundingRectangle(const QRectF &rect) { d->boundingRectangle = rect; } QString PDFConverter::NewSignatureData::signatureText() const { return d->signatureText; } void PDFConverter::NewSignatureData::setSignatureText(const QString &text) { d->signatureText = text; } QString PDFConverter::NewSignatureData::signatureLeftText() const { return d->signatureLeftText; } void PDFConverter::NewSignatureData::setSignatureLeftText(const QString &text) { d->signatureLeftText = text; } QString PDFConverter::NewSignatureData::reason() const { return d->reason; } void PDFConverter::NewSignatureData::setReason(const QString &reason) { d->reason = reason; } QString PDFConverter::NewSignatureData::location() const { return d->location; } void PDFConverter::NewSignatureData::setLocation(const QString &location) { d->location = location; } double PDFConverter::NewSignatureData::fontSize() const { return d->fontSize; } void PDFConverter::NewSignatureData::setFontSize(double fontSize) { d->fontSize = fontSize; } double PDFConverter::NewSignatureData::leftFontSize() const { return d->leftFontSize; } void PDFConverter::NewSignatureData::setLeftFontSize(double fontSize) { d->leftFontSize = fontSize; } QColor PDFConverter::NewSignatureData::fontColor() const { return d->fontColor; } void PDFConverter::NewSignatureData::setFontColor(const QColor &color) { d->fontColor = color; } QColor PDFConverter::NewSignatureData::borderColor() const { return d->borderColor; } void PDFConverter::NewSignatureData::setBorderColor(const QColor &color) { d->borderColor = color; } QColor PDFConverter::NewSignatureData::backgroundColor() const { return d->backgroundColor; } double PDFConverter::NewSignatureData::borderWidth() const { return d->borderWidth; } void PDFConverter::NewSignatureData::setBorderWidth(double width) { d->borderWidth = width; } void PDFConverter::NewSignatureData::setBackgroundColor(const QColor &color) { d->backgroundColor = color; } QString PDFConverter::NewSignatureData::fieldPartialName() const { return d->partialName; } void PDFConverter::NewSignatureData::setFieldPartialName(const QString &name) { d->partialName = name; } QByteArray PDFConverter::NewSignatureData::documentOwnerPassword() const { return d->documentOwnerPassword; } void PDFConverter::NewSignatureData::setDocumentOwnerPassword(const QByteArray &password) { d->documentOwnerPassword = password; } QByteArray PDFConverter::NewSignatureData::documentUserPassword() const { return d->documentUserPassword; } void PDFConverter::NewSignatureData::setDocumentUserPassword(const QByteArray &password) { d->documentUserPassword = password; } QString PDFConverter::NewSignatureData::imagePath() const { return d->imagePath; } void PDFConverter::NewSignatureData::setImagePath(const QString &path) { d->imagePath = path; } } poppler-24.02.0/qt6/src/poppler-private.cc000066400000000000000000000200231455701731300203040ustar00rootroot00000000000000/* 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) 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 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 g10 Code GmbH, Author: Sune Stolborg Vuorela * Inspired on code by * Copyright (C) 2004 by Albert Astals Cid * Copyright (C) 2004 by Enrico Ros * * 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 "poppler-private.h" #include "poppler-form.h" #include #include #include #include #include #include #include #ifdef ANDROID # include # include # include # include # include # include #endif namespace Poppler { namespace Debug { static void qDebugDebugFunction(const QString &message, const QVariant & /*closure*/) { qDebug() << message; } PopplerDebugFunc debugFunction = qDebugDebugFunction; QVariant debugClosure; } void setDebugErrorFunction(PopplerDebugFunc function, const QVariant &closure) { Debug::debugFunction = function ? function : Debug::qDebugDebugFunction; Debug::debugClosure = closure; } void qt6ErrorFunction(ErrorCategory /*category*/, Goffset pos, const char *msg) { QString emsg; if (pos >= 0) { emsg = QStringLiteral("Error (%1): ").arg(pos); } else { emsg = QStringLiteral("Error: "); } emsg += QString::fromLatin1(msg); (*Debug::debugFunction)(emsg, Debug::debugClosure); } QString unicodeToQString(const Unicode *u, int len) { const UnicodeMap *utf8Map = globalParams->getUtf8Map(); // ignore the last characters if they are 0x0 while ((len > 0) && (u[len - 1] == 0)) { --len; } GooString convertedStr; for (int i = 0; i < len; ++i) { char buf[8]; const int n = utf8Map->mapUnicode(u[i], buf, sizeof(buf)); convertedStr.append(buf, n); } 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(); } QString UnicodeParsedString(const std::string &s1) { if (s1.empty()) { return QString(); } if (GooString::hasUnicodeMarker(s1) || GooString::hasUnicodeMarkerLE(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; return result; } } GooString *QStringToUnicodeGooString(const QString &s) { if (s.isEmpty()) { return new GooString(); } 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(); } GooString *ret = new GooString(cstring, len); gfree(cstring); return ret; } GooString *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); gfree(cstring); return ret; } GooString *QDateTimeToUnicodeGooString(const QDateTime &dt) { if (!dt.isValid()) { return nullptr; } return QStringToUnicodeGooString(dt.toUTC().toString(QStringLiteral("yyyyMMddhhmmss+00'00'"))); } Annot::AdditionalActionsType toPopplerAdditionalActionType(Annotation::AdditionalActionType type) { switch (type) { case Annotation::CursorEnteringAction: return Annot::actionCursorEntering; case Annotation::CursorLeavingAction: return Annot::actionCursorLeaving; case Annotation::MousePressedAction: return Annot::actionMousePressed; case Annotation::MouseReleasedAction: return Annot::actionMouseReleased; case Annotation::FocusInAction: return Annot::actionFocusIn; case Annotation::FocusOutAction: return Annot::actionFocusOut; case Annotation::PageOpeningAction: return Annot::actionPageOpening; case Annotation::PageClosingAction: return Annot::actionPageClosing; case Annotation::PageVisibleAction: return Annot::actionPageVisible; case Annotation::PageInvisibleAction: return Annot::actionPageInvisible; } return Annot::actionCursorEntering; } DocumentData::~DocumentData() { qDeleteAll(m_embeddedFiles); delete (OptContentModel *)m_optContentModel; delete doc; } void DocumentData::init() { m_backend = Document::SplashBackend; paperColor = Qt::white; m_hints = 0; 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() { if (!xrefReconstructed) { xrefReconstructed = true; } if (xrefReconstructedCallback) { xrefReconstructedCallback(); } } FormWidget *FormFieldData::getFormWidget(const FormField *f) { return f->m_formData->fm; } FormFieldIconData *FormFieldIconData::getData(const FormFieldIcon &f) { return f.d_ptr; } } poppler-24.02.0/qt6/src/poppler-private.h000066400000000000000000000224151455701731300201550ustar00rootroot00000000000000/* 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) 2007-2009, 2011, 2014 by Pino Toscano * Copyright (C) 2011 Andreas Hartmetz * Copyright (C) 2011 Hib Eris * Copyright (C) 2012, 2013 Thomas Freitag * Copyright (C) 2013 Anthony Granger * Copyright (C) 2014 Bogdan Cristea * Copyright (C) 2014 Aki Koskinen * Copyright (C) 2016 Jakub Alba * Copyright (C) 2017 Christoph Cullmann * 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-2021 Oliver Sander * Copyright (C) 2019 João Netto * Copyright (C) 2019 Jan Grulich * Copyright (C) 2019 Alexander Volkov * Copyright (C) 2020 Philipp Knechtges * Copyright (C) 2021 Mahmoud Khalil * Copyright (C) 2021 Hubert Figuiere * Copyright (C) 2021 Georgiy Sgibnev . Work sponsored by lab50.net. * Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela * Inspired on code by * Copyright (C) 2004 by Albert Astals Cid * Copyright (C) 2004 by Enrico Ros * * 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_PRIVATE_H_ #define _POPPLER_PRIVATE_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "poppler-qt6.h" #include "poppler-embeddedfile-private.h" #include "poppler-qiodeviceinstream-private.h" class LinkDest; class FormWidget; 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); // 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); GooString *QDateTimeToUnicodeGooString(const QDateTime &dt); void qt6ErrorFunction(ErrorCategory /*category*/, Goffset pos, const char *msg); Annot::AdditionalActionsType toPopplerAdditionalActionType(Annotation::AdditionalActionType type); class LinkDestinationData { public: LinkDestinationData(const LinkDest *l, const GooString *nd, Poppler::DocumentData *pdfdoc, bool external) : ld(l), namedDest(nd), doc(pdfdoc), externalDest(external) { } const LinkDest *ld; const GooString *namedDest; Poppler::DocumentData *doc; bool externalDest; }; class DocumentData : private GlobalParamsIniter { public: DocumentData(const QString &filePath, const std::optional &ownerPassword, const std::optional &userPassword) : GlobalParamsIniter(qt6ErrorFunction) { init(); m_device = nullptr; m_filePath = filePath; #ifdef _WIN32 doc = new PDFDoc((wchar_t *)filePath.utf16(), filePath.length(), ownerPassword, userPassword, nullptr, std::bind(&DocumentData::noitfyXRefReconstructed, this)); #else doc = new PDFDoc(std::make_unique(QFile::encodeName(filePath).constData()), ownerPassword, userPassword, nullptr, std::bind(&DocumentData::noitfyXRefReconstructed, this)); #endif } DocumentData(QIODevice *device, const std::optional &ownerPassword, const std::optional &userPassword) : GlobalParamsIniter(qt6ErrorFunction) { 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)); } DocumentData(const QByteArray &data, const std::optional &ownerPassword, const std::optional &userPassword) : GlobalParamsIniter(qt6ErrorFunction) { m_device = nullptr; 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)); } void init(); ~DocumentData(); DocumentData(const DocumentData &) = delete; DocumentData &operator=(const DocumentData &) = delete; void setPaperColor(const QColor &color) { paperColor = color; } void fillMembers() { int numEmb = doc->getCatalog()->numEmbeddedFiles(); if (!(0 == numEmb)) { // we have some embedded documents, build the list for (int yalv = 0; yalv < numEmb; ++yalv) { std::unique_ptr fs = doc->getCatalog()->embeddedFile(yalv); m_embeddedFiles.append(new EmbeddedFile(*new EmbeddedFileData(std::move(fs)))); } } } /** * a method that is being called whenever PDFDoc's XRef is reconstructed * where we'll set xrefReconstructed flag and notify users of the * reconstruction event */ void noitfyXRefReconstructed(); static std::unique_ptr checkDocument(DocumentData *doc); PDFDoc *doc; QString m_filePath; QIODevice *m_device; QByteArray fileContents; bool locked; Document::RenderBackend m_backend; QList m_embeddedFiles; QPointer m_optContentModel; QColor paperColor; int m_hints; #ifdef USE_CMS GfxLCMSProfilePtr m_sRGBProfile; GfxLCMSProfilePtr m_displayProfile; #endif bool xrefReconstructed; // notifies the user whenever the backend's PDFDoc XRef is reconstructed std::function xrefReconstructedCallback; }; class FontInfoData { public: FontInfoData() { isEmbedded = false; isSubset = false; type = FontInfo::unknown; } explicit FontInfoData(::FontInfo *fi) { if (fi->getName()) { fontName = fi->getName()->c_str(); } if (fi->getFile()) { fontFile = fi->getFile()->c_str(); } if (fi->getSubstituteName()) { fontSubstituteName = fi->getSubstituteName()->c_str(); } isEmbedded = fi->getEmbedded(); isSubset = fi->getSubset(); type = (Poppler::FontInfo::Type)fi->getType(); embRef = fi->getEmbRef(); } FontInfoData(const FontInfoData &fid) = default; FontInfoData &operator=(const FontInfoData &) = default; QString fontName; QString fontSubstituteName; QString fontFile; bool isEmbedded : 1; bool isSubset : 1; FontInfo::Type type; Ref embRef; }; class FontIteratorData { public: FontIteratorData(int startPage, DocumentData *dd) : fontInfoScanner(dd->doc, startPage), totalPages(dd->doc->getNumPages()), currentPage(qMax(startPage, 0) - 1) { } ~FontIteratorData() { } FontInfoScanner fontInfoScanner; int totalPages; int currentPage; }; class TextBoxData { public: TextBoxData() : nextWord(nullptr), hasSpaceAfter(false) { } QString text; QRectF bBox; TextBox *nextWord; QVector charBBoxes; // the boundingRect of each character bool hasSpaceAfter; }; class FormFieldData { public: FormFieldData(DocumentData *_doc, ::Page *p, ::FormWidget *w) : doc(_doc), page(p), fm(w) { } DocumentData *doc; ::Page *page; // Note for some signatures it can be null since there's signatures that don't belong to a given page ::FormWidget *fm; QRectF box; static POPPLER_QT6_EXPORT ::FormWidget *getFormWidget(const FormField *f); }; class FormFieldIcon; class FormFieldIconData { public: static POPPLER_QT6_EXPORT FormFieldIconData *getData(const FormFieldIcon &f); Dict *icon; }; } #endif poppler-24.02.0/qt6/src/poppler-ps-converter.cc000066400000000000000000000163541455701731300212750ustar00rootroot00000000000000/* poppler-ps-converter.cc: qt interface to poppler * Copyright (C) 2007, 2009, 2010, 2015, 2020, 2022, Albert Astals Cid * Copyright (C) 2008, Pino Toscano * Copyright (C) 2010 Hib Eris * Copyright (C) 2011 Glad Deschrijver * Copyright (C) 2012 Fabio D'Urso * 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 * 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 "poppler-qt6.h" #include "poppler-private.h" #include "poppler-converter-private.h" #include "PSOutputDev.h" static void outputToQIODevice(void *stream, const char *data, size_t len) { static_cast(stream)->write(data, len); } namespace Poppler { class PSConverterPrivate : public BaseConverterPrivate { public: PSConverterPrivate(); ~PSConverterPrivate() override; QList pageList; QString title; double hDPI; double vDPI; int rotate; int paperWidth; int paperHeight; int marginRight; int marginBottom; int marginLeft; int marginTop; PSConverter::PSOptions opts; void (*pageConvertedCallback)(int page, void *payload); void *pageConvertedPayload; }; 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) { } PSConverterPrivate::~PSConverterPrivate() = default; PSConverter::PSConverter(DocumentData *document) : BaseConverter(*new PSConverterPrivate()) { Q_D(PSConverter); d->document = document; } PSConverter::~PSConverter() { } void PSConverter::setPageList(const QList &pageList) { Q_D(PSConverter); d->pageList = pageList; } void PSConverter::setTitle(const QString &title) { Q_D(PSConverter); d->title = title; } void PSConverter::setHDPI(double hDPI) { Q_D(PSConverter); d->hDPI = hDPI; } void PSConverter::setVDPI(double vDPI) { Q_D(PSConverter); d->vDPI = vDPI; } void PSConverter::setRotate(int rotate) { Q_D(PSConverter); d->rotate = rotate; } void PSConverter::setPaperWidth(int paperWidth) { Q_D(PSConverter); d->paperWidth = paperWidth; } void PSConverter::setPaperHeight(int paperHeight) { Q_D(PSConverter); d->paperHeight = paperHeight; } void PSConverter::setRightMargin(int marginRight) { Q_D(PSConverter); d->marginRight = marginRight; } void PSConverter::setBottomMargin(int marginBottom) { Q_D(PSConverter); d->marginBottom = marginBottom; } void PSConverter::setLeftMargin(int marginLeft) { Q_D(PSConverter); d->marginLeft = marginLeft; } void PSConverter::setTopMargin(int marginTop) { Q_D(PSConverter); d->marginTop = marginTop; } void PSConverter::setStrictMargins(bool strictMargins) { Q_D(PSConverter); if (strictMargins) { d->opts |= StrictMargins; } else { d->opts &= ~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); if (forceRasterize) { d->opts |= ForceRasterization; } else { d->opts &= ~ForceRasterization; } } void PSConverter::setPSOptions(PSConverter::PSOptions options) { Q_D(PSConverter); d->opts = options; } PSConverter::PSOptions PSConverter::psOptions() const { Q_D(const PSConverter); return d->opts; } void PSConverter::setPageConvertedCallback(void (*callback)(int page, void *payload), void *payload) { Q_D(PSConverter); d->pageConvertedCallback = callback; d->pageConvertedPayload = payload; } static bool annotDisplayDecideCbk(Annot *annot, void *user_data) { if (annot->getType() == Annot::typeWidget) { return true; // Never hide forms } else { return *(bool *)user_data; } } bool PSConverter::convert() { Q_D(PSConverter); d->lastError = NoError; Q_ASSERT(!d->pageList.isEmpty()); Q_ASSERT(d->paperWidth != -1); Q_ASSERT(d->paperHeight != -1); if (d->document->locked) { d->lastError = FileLockedError; return false; } QIODevice *dev = d->openDevice(); if (!dev) { d->lastError = OpenOutputError; return false; } QByteArray pstitle8Bit = d->title.toLocal8Bit(); char *pstitlechar; if (!d->title.isEmpty()) { pstitlechar = pstitle8Bit.data(); } else { pstitlechar = nullptr; } std::vector pages; foreach (int page, d->pageList) { pages.push_back(page); } 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; double yScale = ((double)d->paperHeight - (double)d->marginBottom - (double)d->marginTop) / (double)d->paperHeight; psOut->setScale(xScale, yScale); } if (psOut->isOk()) { bool isPrinting = (d->opts & Printing) ? true : false; bool showAnnotations = (d->opts & HideAnnotations) ? false : true; foreach (int page, d->pageList) { d->document->doc->displayPage(psOut, page, d->hDPI, d->vDPI, d->rotate, false, true, isPrinting, nullptr, nullptr, annotDisplayDecideCbk, &showAnnotations, true); if (d->pageConvertedCallback) { (*d->pageConvertedCallback)(page, d->pageConvertedPayload); } } delete psOut; d->closeDevice(); return true; } else { delete psOut; d->closeDevice(); return false; } } } poppler-24.02.0/qt6/src/poppler-qiodeviceinstream-private.h000066400000000000000000000030071455701731300236620ustar00rootroot00000000000000/* poppler-qiodeviceinstream-private.h: Qt6 interface to poppler * Copyright (C) 2019 Alexander Volkov * * 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_QIODEVICEINSTREAM_PRIVATE_H #define POPPLER_QIODEVICEINSTREAM_PRIVATE_H #include "Object.h" #include "Stream.h" class QIODevice; namespace Poppler { class QIODeviceInStream : public BaseSeekInputStream { public: QIODeviceInStream(QIODevice *device, Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA); ~QIODeviceInStream() override; BaseStream *copy() override; Stream *makeSubStream(Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA) override; private: Goffset currentPos() const override; void setCurrentPos(Goffset offset) override; Goffset read(char *buffer, Goffset count) override; QIODevice *m_device; }; } #endif poppler-24.02.0/qt6/src/poppler-qiodeviceinstream.cc000066400000000000000000000034311455701731300223510ustar00rootroot00000000000000/* poppler-qiodeviceinstream.cc: Qt6 interface to poppler * Copyright (C) 2019 Alexander Volkov * * 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 "poppler-qiodeviceinstream-private.h" #include #include namespace Poppler { QIODeviceInStream::QIODeviceInStream(QIODevice *device, Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA) : BaseSeekInputStream(startA, limitedA, lengthA, std::move(dictA)), m_device(device) { } QIODeviceInStream::~QIODeviceInStream() { close(); } BaseStream *QIODeviceInStream::copy() { return new QIODeviceInStream(m_device, start, limited, length, dict.copy()); } Stream *QIODeviceInStream::makeSubStream(Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA) { return new QIODeviceInStream(m_device, startA, limitedA, lengthA, std::move(dictA)); } Goffset QIODeviceInStream::currentPos() const { return m_device->pos(); } void QIODeviceInStream::setCurrentPos(Goffset offset) { m_device->seek(offset); } Goffset QIODeviceInStream::read(char *buffer, Goffset count) { return m_device->read(buffer, count); } } poppler-24.02.0/qt6/src/poppler-qiodeviceoutstream-private.h000066400000000000000000000027701455701731300240710ustar00rootroot00000000000000/* poppler-qiodevicestream-private.h: Qt6 interface to poppler * Copyright (C) 2008, Pino Toscano * Copyright (C) 2013 Adrian Johnson * Copyright (C) 2021 Albert Astals Cid * Copyright (C) 2021, Even Rouault * * 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_QIODEVICESTREAM_PRIVATE_H #define POPPLER_QIODEVICESTREAM_PRIVATE_H #include "Object.h" #include "Stream.h" class QIODevice; namespace Poppler { class QIODeviceOutStream : public OutStream { public: explicit QIODeviceOutStream(QIODevice *device); ~QIODeviceOutStream() override; void close() override; Goffset getPos() override; void put(char c) override; void printf(const char *format, ...) override GCC_PRINTF_FORMAT(2, 3); private: QIODevice *m_device; }; } #endif poppler-24.02.0/qt6/src/poppler-qiodeviceoutstream.cc000066400000000000000000000040471455701731300225560ustar00rootroot00000000000000/* poppler-qiodevicestream.cc: Qt6 interface to poppler * Copyright (C) 2008, Pino Toscano * Copyright (C) 2013 Adrian Johnson * Copyright (C) 2020, 2021 Albert Astals Cid * Copyright (C) 2021, Even Rouault * * 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 "poppler-qiodeviceoutstream-private.h" #include #include namespace Poppler { QIODeviceOutStream::QIODeviceOutStream(QIODevice *device) : m_device(device) { } QIODeviceOutStream::~QIODeviceOutStream() { } void QIODeviceOutStream::close() { } Goffset QIODeviceOutStream::getPos() { return m_device->pos(); } void QIODeviceOutStream::put(char c) { m_device->putChar(c); } 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) { va_list ap_copy; va_copy(ap_copy, ap); const size_t size = vsnprintf(nullptr, 0, format, ap_copy) + 1; va_end(ap_copy); *buf_ptr = new char[size]; return qvsnprintf(*buf_ptr, size, format, ap); } void QIODeviceOutStream::printf(const char *format, ...) { va_list ap; va_start(ap, format); char *buf; const size_t bufsize = poppler_vasprintf(&buf, format, ap); va_end(ap); m_device->write(buf, bufsize); delete[] buf; } } poppler-24.02.0/qt6/src/poppler-qt6.h000066400000000000000000002020031455701731300172060ustar00rootroot00000000000000/* poppler-qt.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 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_QT_H__ #define __POPPLER_QT_H__ #include #include #include #include "poppler-annotation.h" #include "poppler-link.h" #include "poppler-optcontent.h" #include "poppler-page-transition.h" #include #include #include #include #include "poppler-export.h" class EmbFile; class Sound; class AnnotMovie; /** The %Poppler Qt6 binding. */ namespace Poppler { class Document; class DocumentData; class PageData; class FormField; class FormFieldSignature; class TextBoxData; class PDFConverter; class PSConverter; struct OutlineItemData; /** Debug/error function. This function type is used for debugging & error output; the first parameter is the actual message, the second is the unaltered closure argument which was passed to the setDebugErrorFunction call. */ using PopplerDebugFunc = void (*)(const QString & /*message*/, const QVariant & /*closure*/); /** Set a new debug/error output function. If not set, by default error and debug messages will be sent to the Qt \p qDebug() function. \param debugFunction the new debug function \param closure user data which will be passes as-is to the debug function */ POPPLER_QT6_EXPORT void setDebugErrorFunction(PopplerDebugFunc debugFunction, const QVariant &closure); /** Describes the physical location of text on a document page This very simple class describes the physical location of text on the page. It consists of - a QString that contains the text - a QRectF that gives a box that describes where on the page the text is found. */ class POPPLER_QT6_EXPORT TextBox { friend class Page; public: /** The default constructor sets the \p text and the rectangle that contains the text. Coordinates for the \p bBox are in points = 1/72 of an inch. */ TextBox(const QString &text, const QRectF &bBox); /** Destructor. */ ~TextBox(); /** Returns the text of this text box */ QString text() const; /** Returns the position of the text, in point, i.e., 1/72 of an inch */ QRectF boundingBox() const; /** Returns the pointer to the next text box, if there is one. Otherwise, it returns a null pointer. */ TextBox *nextWord() const; /** Returns the bounding box of the \p i -th characted of the word. */ QRectF charBoundingBox(int i) const; /** Returns whether there is a space character after this text box */ bool hasSpaceAfter() const; private: Q_DISABLE_COPY(TextBox) TextBoxData *m_data; }; class FontInfoData; /** Container class for information about a font within a PDF document */ class POPPLER_QT6_EXPORT FontInfo { friend class Document; public: /** The type of font. */ enum Type { unknown, Type1, Type1C, Type1COT, Type3, TrueType, TrueTypeOT, CIDType0, CIDType0C, CIDType0COT, CIDTrueType, CIDTrueTypeOT }; /// \cond PRIVATE /** Create a new font information container. */ FontInfo(); /** Create a new font information container. */ explicit FontInfo(const FontInfoData &fid); /// \endcond /** Copy constructor. */ FontInfo(const FontInfo &fi); /** Destructor. */ ~FontInfo(); /** The name of the font. Can be a null QString if the font has no name */ QString name() const; /** The name of the substitute font. Can be a null QString if the font has no substitute font */ QString substituteName() const; /** The path of the font file used to represent this font on this system, or a null string is the font is embedded */ QString file() const; /** Whether the font is embedded in the file, or not \return true if the font is embedded */ bool isEmbedded() const; /** Whether the font provided is only a subset of the full font or not. This only has meaning if the font is embedded. \return true if the font is only a subset */ bool isSubset() const; /** The type of font encoding \return a enumerated value corresponding to the font encoding used \sa typeName for a string equivalent */ Type type() const; /** The name of the font encoding used \note if you are looking for the name of the font (as opposed to the encoding format used), you probably want name(). \sa type for a enumeration version */ QString typeName() const; /** Standard assignment operator */ FontInfo &operator=(const FontInfo &fi); private: FontInfoData *m_data; }; class FontIteratorData; /** Iterator for reading the fonts in a document. FontIterator provides a Java-style iterator for reading the fonts in a document. You can use it in the following way: \code std::unique_ptr it = doc->newFontIterator(); while (it->hasNext()) { QList fonts = it->next(); // do something with the fonts } // no need to free the iterator after doing the job \endcode */ class POPPLER_QT6_EXPORT FontIterator { friend class Document; friend class DocumentData; public: /** Destructor. */ ~FontIterator(); /** Returns the fonts of the current page and then advances the iterator to the next page. */ QList next(); /** Checks whether there is at least one more page to iterate, ie returns false when the iterator is beyond the last page. */ bool hasNext() const; /** Returns the current page where the iterator is. */ int currentPage() const; private: Q_DISABLE_COPY(FontIterator) FontIterator(int, DocumentData *dd); FontIteratorData *d; }; class EmbeddedFileData; /** Container class for an embedded file with a PDF document */ class POPPLER_QT6_EXPORT EmbeddedFile { friend class DocumentData; friend class AnnotationPrivate; public: /// \cond PRIVATE explicit EmbeddedFile(EmbFile *embfile); /// \endcond /** Destructor. */ ~EmbeddedFile(); /** The name associated with the file */ QString name() const; /** The description associated with the file, if any. This will return an empty QString if there is no description element */ QString description() const; /** The size of the file. This will return < 0 if there is no size element */ int size() const; /** The modification date for the embedded file, if known. */ QDateTime modDate() const; /** The creation date for the embedded file, if known. */ QDateTime createDate() const; /** The MD5 checksum of the file. This will return an empty QByteArray if there is no checksum element. */ QByteArray checksum() const; /** The MIME type of the file, if known. */ QString mimeType() const; /** The data as a byte array */ QByteArray data(); /** Is the embedded file valid? */ bool isValid() const; /** A QDataStream for the actual data? */ // QDataStream dataStream() const; private: Q_DISABLE_COPY(EmbeddedFile) explicit EmbeddedFile(EmbeddedFileData &dd); EmbeddedFileData *m_embeddedFile; }; /** \brief A page in a document. The Page class represents a single page within a PDF document. You cannot construct a Page directly, but you have to use the Document functions that return a new Page out of an index or a label. */ class POPPLER_QT6_EXPORT Page { friend class Document; public: /** Destructor. */ ~Page(); /** The type of rotation to apply for an operation */ enum Rotation { Rotate0 = 0, ///< Do not rotate Rotate90 = 1, ///< Rotate 90 degrees clockwise Rotate180 = 2, ///< Rotate 180 degrees Rotate270 = 3 ///< Rotate 270 degrees clockwise (90 degrees counterclockwise) }; /** The kinds of page actions */ enum PageAction { Opening, ///< The action when a page is "opened" Closing ///< The action when a page is "closed" }; /** How the text is going to be returned */ enum TextLayout { PhysicalLayout, ///< The text is layouted to resemble the real page layout RawOrderLayout ///< The text is returned without any type of processing }; /** Additional flags for the renderToPainter method */ enum PainterFlag { NoPainterFlags = 0x00000000, /** Do not save/restore the caller-owned painter. renderToPainter() by default preserves, using save() + restore(), the state of the painter specified; if this is not needed, this flag can avoid this job */ DontSaveAndRestore = 0x00000001 }; Q_DECLARE_FLAGS(PainterFlags, PainterFlag) /** Render the page to a QImage using the current \link Document::renderBackend() Document renderer\endlink. If \p x = \p y = \p w = \p h = -1, the method will automatically compute the size of the image from the horizontal and vertical resolutions specified in \p xres and \p yres. Otherwise, the method renders only a part of the page, specified by the parameters (\p x, \p y, \p w, \p h) in pixel coordinates. The returned QImage then has size (\p w, \p h), independent of the page size. \param x specifies the left x-coordinate of the box, in pixels. \param y specifies the top y-coordinate of the box, in pixels. \param w specifies the width of the box, in pixels. \param h specifies the height of the box, in pixels. \param xres horizontal resolution of the graphics device, in dots per inch \param yres vertical resolution of the graphics device, in dots per inch \param rotate how to rotate the page \warning The parameter (\p x, \p y, \p w, \p h) are not well-tested. Unusual or meaningless parameters may lead to rather unexpected results. \returns a QImage of the page, or a null image on failure. */ QImage renderToImage(double xres = 72.0, double yres = 72.0, int x = -1, int y = -1, int w = -1, int h = -1, Rotation rotate = Rotate0) const; /** Partial Update renderToImage callback. This function type is used for doing partial rendering updates; the first parameter is the image as rendered up to now, the second is the unaltered closure argument which was passed to the renderToImage call. */ using RenderToImagePartialUpdateFunc = void (*)(const QImage & /*image*/, const QVariant & /*closure*/); /** Partial Update query renderToImage callback. This function type is used for query if the partial rendering update should happen; the parameter is the unaltered closure argument which was passed to the renderToImage call. */ using ShouldRenderToImagePartialQueryFunc = bool (*)(const QVariant & /*closure*/); /** Render the page to a QImage using the current \link Document::renderBackend() Document renderer\endlink. If \p x = \p y = \p w = \p h = -1, the method will automatically compute the size of the image from the horizontal and vertical resolutions specified in \p xres and \p yres. Otherwise, the method renders only a part of the page, specified by the parameters (\p x, \p y, \p w, \p h) in pixel coordinates. The returned QImage then has size (\p w, \p h), independent of the page size. \param x specifies the left x-coordinate of the box, in pixels. \param y specifies the top y-coordinate of the box, in pixels. \param w specifies the width of the box, in pixels. \param h specifies the height of the box, in pixels. \param xres horizontal resolution of the graphics device, in dots per inch \param yres vertical resolution of the graphics device, in dots per inch \param rotate how to rotate the page \param partialUpdateCallback callback that will be called to report a partial rendering update \param shouldDoPartialUpdateCallback callback that will be called to ask if a partial rendering update is wanted. This exists because doing a partial rendering update needs to copy the image buffer so if it is not wanted it is better skipped early. \param payload opaque structure that will be passed back to partialUpdateCallback and shouldDoPartialUpdateCallback. \warning The parameter (\p x, \p y, \p w, \p h) are not well-tested. Unusual or meaningless parameters may lead to rather unexpected results. \returns a QImage of the page, or a null image on failure. */ QImage renderToImage(double xres, double yres, int x, int y, int w, int h, Rotation rotate, RenderToImagePartialUpdateFunc partialUpdateCallback, ShouldRenderToImagePartialQueryFunc shouldDoPartialUpdateCallback, const QVariant &payload) const; /** Abort query function callback. This function type is used for query if the current rendering/text extraction should be cancelled. */ using ShouldAbortQueryFunc = bool (*)(const QVariant & /*closure*/); /** Render the page to a QImage using the current \link Document::renderBackend() Document renderer\endlink. If \p x = \p y = \p w = \p h = -1, the method will automatically compute the size of the image from the horizontal and vertical resolutions specified in \p xres and \p yres. Otherwise, the method renders only a part of the page, specified by the parameters (\p x, \p y, \p w, \p h) in pixel coordinates. The returned QImage then has size (\p w, \p h), independent of the page size. \param x specifies the left x-coordinate of the box, in pixels. \param y specifies the top y-coordinate of the box, in pixels. \param w specifies the width of the box, in pixels. \param h specifies the height of the box, in pixels. \param xres horizontal resolution of the graphics device, in dots per inch \param yres vertical resolution of the graphics device, in dots per inch \param rotate how to rotate the page \param partialUpdateCallback callback that will be called to report a partial rendering update \param shouldDoPartialUpdateCallback callback that will be called to ask if a partial rendering update is wanted. This exists because doing a partial rendering update needs to copy the image buffer so if it is not wanted it is better skipped early. \param shouldAbortRenderCallback callback that will be called to ask if the rendering should be cancelled. \param payload opaque structure that will be passed back to partialUpdateCallback, shouldDoPartialUpdateCallback and shouldAbortRenderCallback. \warning The parameter (\p x, \p y, \p w, \p h) are not well-tested. Unusual or meaningless parameters may lead to rather unexpected results. \returns a QImage of the page, or a null image on failure. */ QImage renderToImage(double xres, double yres, int x, int y, int w, int h, Rotation rotate, RenderToImagePartialUpdateFunc partialUpdateCallback, ShouldRenderToImagePartialQueryFunc shouldDoPartialUpdateCallback, ShouldAbortQueryFunc shouldAbortRenderCallback, const QVariant &payload) const; /** Render the page to the specified QPainter using the current \link Document::renderBackend() Document renderer\endlink. If \p x = \p y = \p w = \p h = -1, the method will automatically compute the size of the page area from the horizontal and vertical resolutions specified in \p xres and \p yres. Otherwise, the method renders only a part of the page, specified by the parameters (\p x, \p y, \p w, \p h) in pixel coordinates. \param painter the painter to paint on \param x specifies the left x-coordinate of the box, in pixels. \param y specifies the top y-coordinate of the box, in pixels. \param w specifies the width of the box, in pixels. \param h specifies the height of the box, in pixels. \param xres horizontal resolution of the graphics device, in dots per inch \param yres vertical resolution of the graphics device, in dots per inch \param rotate how to rotate the page \param flags additional painter flags \warning The parameter (\p x, \p y, \p w, \p h) are not well-tested. Unusual or meaningless parameters may lead to rather unexpected results. \returns whether the painting succeeded \note This method is only supported for the QPainterOutputDev */ bool renderToPainter(QPainter *painter, double xres = 72.0, double yres = 72.0, int x = -1, int y = -1, int w = -1, int h = -1, Rotation rotate = Rotate0, PainterFlags flags = NoPainterFlags) const; /** Get the page thumbnail if it exists. \return a QImage of the thumbnail, or a null image if the PDF does not contain one for this page */ QImage thumbnail() const; /** Returns the text that is inside a specified rectangle \param rect the rectangle specifying the area of interest, with coordinates given in points, i.e., 1/72th of an inch. If rect is null, all text on the page is given **/ QString text(const QRectF &rect, TextLayout textLayout) const; /** Returns the text that is inside a specified rectangle. The text is returned using the physical layout of the page \param rect the rectangle specifying the area of interest, with coordinates given in points, i.e., 1/72th of an inch. If rect is null, all text on the page is given **/ QString text(const QRectF &rect) const; /** The starting point for a search */ enum SearchDirection { FromTop, ///< Start sorting at the top of the document NextResult, ///< Find the next result, moving "down the page" PreviousResult ///< Find the previous result, moving "up the page" }; /** The type of search to perform */ enum SearchMode { CaseSensitive, ///< Case differences cause no match in searching CaseInsensitive ///< Case differences are ignored in matching }; /** Flags to modify the search behaviour */ enum SearchFlag { NoSearchFlags = 0x00000000, IgnoreCase = 0x00000001, ///< Case differences are ignored WholeWords = 0x00000002, ///< Only whole words are matched IgnoreDiacritics = 0x00000004, ///< Diacritic differences (eg. accents, umlauts, diaeresis) are ignored. ///< This option will have no effect if the search term contains characters which ///< are not pure ascii. AcrossLines = 0x00000008 ///< Allows to match on text spanning from end of a line to the next line. ///< 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. \since 21.05.0 }; Q_DECLARE_FLAGS(SearchFlags, SearchFlag) /** Returns true if the specified text was found. \param text the text the search \param rectXXX in all directions is used to return where the text was found, for NextResult and PreviousResult indicates where to continue searching for \param direction in which direction do the search \param flags the flags to consider during matching \param rotate the rotation to apply for the search order **/ bool search(const QString &text, double &sLeft, double &sTop, double &sRight, double &sBottom, SearchDirection direction, SearchFlags flags = NoSearchFlags, Rotation rotate = Rotate0) const; /** Returns a list of all occurrences of the specified text on the page. if SearchFlags::AcrossLines is given in \param flags, then rects may just be parts of the text itself if it's split between multiple lines. \param text the text to search \param flags the flags to consider during matching \param rotate the rotation to apply for the search order \warning Do not use the returned QRectF as arguments of another search call because of truncation issues if qreal is defined as float. **/ QList search(const QString &text, SearchFlags flags = NoSearchFlags, Rotation rotate = Rotate0) const; /** Returns a list of text of the page This method returns a QList of TextBoxes that contain all the text of the page, with roughly one text word of text per TextBox item. For text written in western languages (left-to-right and up-to-down), the QList contains the text in the proper order. \warning This method is not tested with Asian scripts */ std::vector> textList(Rotation rotate = Rotate0) const; /** Returns a list of text of the page This method returns a QList of TextBoxes that contain all the text of the page, with roughly one text word of text per TextBox item. For text written in western languages (left-to-right and up-to-down), the QList contains the text in the proper order. \param shouldAbortExtractionCallback callback that will be called to ask if the text extraction should be cancelled. \param closure opaque structure that will be passed back to shouldAbortExtractionCallback. \warning This method is not tested with Asian scripts */ std::vector> textList(Rotation rotate, ShouldAbortQueryFunc shouldAbortExtractionCallback, const QVariant &closure) const; /** \return The dimensions (cropbox) of the page, in points (i.e. 1/72th of an inch) */ QSizeF pageSizeF() const; /** \return The dimensions (cropbox) of the page, in points (i.e. 1/72th of an inch) */ QSize pageSize() const; /** Returns the transition of this page \returns a pointer to a PageTransition structure that defines how transition to this page shall be performed. \note The PageTransition structure is owned by this page, and will automatically be destroyed when this page class is destroyed. **/ PageTransition *transition() const; /** Gets the page action specified, or empty unique pointer if there is no action. **/ std::unique_ptr action(PageAction act) const; /** Types of orientations that are possible */ enum Orientation { Landscape, ///< Landscape orientation (portrait, with 90 degrees clockwise rotation ) Portrait, ///< Normal portrait orientation Seascape, ///< Seascape orientation (portrait, with 270 degrees clockwise rotation) UpsideDown ///< Upside down orientation (portrait, with 180 degrees rotation) }; /** The orientation of the page */ Orientation orientation() const; /** The default CTM */ void defaultCTM(double *CTM, double dpiX, double dpiY, int rotate, bool upsideDown); /** Gets the links of the page */ std::vector> links() const; /** Returns the annotations of the page \note If you call this method twice, you get different objects pointing to the same annotations (see Annotation). */ std::vector> annotations() const; /** Returns the annotations of the page \param subtypes the subtypes of annotations you are interested in \note If you call this method twice, you get different objects pointing to the same annotations (see Annotation). */ std::vector> annotations(const QSet &subtypes) const; /** Adds an annotation to the page \note Ownership of the annotation object stays with the caller, who can delete it at any time. */ void addAnnotation(const Annotation *ann); /** Removes an annotation from the page and destroys the annotation object \note There mustn't be other Annotation objects pointing this annotation */ void removeAnnotation(const Annotation *ann); /** Returns the form fields on the page */ std::vector> formFields() const; /** Returns the page duration. That is the time, in seconds, that the page should be displayed before the presentation automatically advances to the next page. Returns < 0 if duration is not set. */ double duration() const; /** Returns the label of the page, or a null string is the page has no label. **/ QString label() const; /** Returns the index of the page. **/ int index() const; private: Q_DISABLE_COPY(Page) Page(DocumentData *doc, int index); PageData *m_page; }; /** \brief Item in the outline of a PDF document Represents an item in the outline of PDF document, i.e. a name, an internal or external link and a set of child items. **/ class POPPLER_QT6_EXPORT OutlineItem { friend class Document; public: /** Constructs a null item, i.e. one that does not represent a valid item in the outline of some PDF document. **/ OutlineItem(); ~OutlineItem(); OutlineItem(const OutlineItem &other); OutlineItem &operator=(const OutlineItem &other); OutlineItem(OutlineItem &&other) noexcept; OutlineItem &operator=(OutlineItem &&other) noexcept; /** Indicates whether an item is null, i.e. whether it does not represent a valid item in the outline of some PDF document. **/ bool isNull() const; /** The name of the item which should be displayed to the user. **/ QString name() const; /** Indicates whether the item should initially be display in an expanded or collapsed state. **/ bool isOpen() const; /** The destination referred to by this item. \returns a shared pointer to an immutable link destination **/ QSharedPointer destination() const; /** The external file name of the document to which the \see destination refers \returns a string with the external file name or an empty string if there is none */ QString externalFileName() const; /** The URI to which the item links \returns a string with the URI which this item links or an empty string if there is none **/ QString uri() const; /** Determines if this item has any child items \returns true if there are any child items **/ bool hasChildren() const; /** Gets the child items of this item \returns a vector outline items, empty if there are none **/ QVector children() const; private: explicit OutlineItem(OutlineItemData *data); OutlineItemData *m_data; }; /** \brief PDF document. The Document class represents a PDF document: its pages, and all the global properties, metadata, etc. \section ownership Ownership of the returned objects All the functions that returns class pointers create new object, and the responsibility of those is given to the caller. The only exception is \link Poppler::Page::transition() Page::transition()\endlink. \section document-loading Loading To get a Document, you have to load it via the load() & loadFromData() functions. In all the functions that have passwords as arguments, they \b must be Latin1 encoded. If you have a password that is a UTF-8 string, you need to use QString::toLatin1() (or similar) to convert the password first. If you have a UTF-8 character array, consider converting it to a QString first (QString::fromUtf8(), or similar) before converting to Latin1 encoding. \section document-rendering Rendering To render pages of a document, you have different Document functions to set various options. \subsection document-rendering-backend Backends %Poppler offers a different backends for rendering the pages. Currently there are two backends (see #RenderBackend), but only the Splash engine works well and has been tested. The available rendering backends can be discovered via availableRenderBackends(). The current rendering backend can be changed using setRenderBackend(). Please note that setting a backend not listed in the available ones will always result in null QImage's. \section document-cms Color management support %Poppler, if compiled with this support, provides functions to handle color profiles. To know whether the %Poppler version you are using has support for color management, you can query Poppler::isCmsAvailable(). In case it is not available, all the color management-related functions will either do nothing or return null. */ class POPPLER_QT6_EXPORT Document { friend class Page; friend class DocumentData; public: /** The page mode */ enum PageMode { UseNone, ///< No mode - neither document outline nor thumbnail images are visible UseOutlines, ///< Document outline visible UseThumbs, ///< Thumbnail images visible FullScreen, ///< Fullscreen mode (no menubar, windows controls etc) UseOC, ///< Optional content group panel visible UseAttach ///< Attachments panel visible }; /** The page layout */ enum PageLayout { NoLayout, ///< Layout not specified SinglePage, ///< Display a single page OneColumn, ///< Display a single column of pages TwoColumnLeft, ///< Display the pages in two columns, with odd-numbered pages on the left TwoColumnRight, ///< Display the pages in two columns, with odd-numbered pages on the right TwoPageLeft, ///< Display the pages two at a time, with odd-numbered pages on the left TwoPageRight ///< Display the pages two at a time, with odd-numbered pages on the right }; /** The render backends available */ enum RenderBackend { SplashBackend, ///< Splash backend QPainterBackend ///< Qt backend }; /** The render hints available */ enum RenderHint { Antialiasing = 0x00000001, ///< Antialiasing for graphics TextAntialiasing = 0x00000002, ///< Antialiasing for text TextHinting = 0x00000004, ///< Hinting for text TextSlightHinting = 0x00000008, ///< Lighter hinting for text when combined with TextHinting OverprintPreview = 0x00000010, ///< Overprint preview ThinLineSolid = 0x00000020, ///< Enhance thin lines solid ThinLineShape = 0x00000040, ///< Enhance thin lines shape. Wins over ThinLineSolid IgnorePaperColor = 0x00000080, ///< Do not compose with the paper color HideAnnotations = 0x00000100 ///< Do not render annotations }; Q_DECLARE_FLAGS(RenderHints, RenderHint) /** Form types */ enum FormType { NoForm, ///< Document doesn't contain forms AcroForm, ///< AcroForm XfaForm ///< Adobe XML Forms Architecture (XFA), currently unsupported }; /** Set a color display profile for the current document. \param outputProfileA is a \c cmsHPROFILE of the LCMS library. \note This should be called before any rendering happens. \note It is assumed that poppler takes over the owernship of the corresponding cmsHPROFILE. In particular, it is no longer the caller's responsibility to close the profile after use. */ void setColorDisplayProfile(void *outputProfileA); /** Set a color display profile for the current document. \param name is the name of the display profile to set. \note This should be called before any rendering happens. */ void setColorDisplayProfileName(const QString &name); /** Return the current RGB profile. \return a \c cmsHPROFILE of the LCMS library. \note The returned profile stays a property of poppler and shall NOT be closed by the user. It's existence is guaranteed for as long as this instance of the Document class is not deleted. */ void *colorRgbProfile() const; /** Return the current display profile. \return a \c cmsHPROFILE of the LCMS library. \note The returned profile stays a property of poppler and shall NOT be closed by the user. It's existence is guaranteed for as long as this instance of the Document class is not deleted. */ void *colorDisplayProfile() const; /** Load the document from a file on disk \param filePath the name (and path, if required) of the file to load \param ownerPassword the Latin1-encoded owner password to use in loading the file \param userPassword the Latin1-encoded user ("open") password to use in loading the file \return the loaded document, or empty unique pointer on error \warning The returning document may be locked if a password is required to open the file, and one is not provided (as the userPassword). */ static std::unique_ptr load(const QString &filePath, const QByteArray &ownerPassword = QByteArray(), const QByteArray &userPassword = QByteArray()); /** Load the document from a device \param device the device of the data to load \param ownerPassword the Latin1-encoded owner password to use in loading the file \param userPassword the Latin1-encoded user ("open") password to use in loading the file \return the loaded document, or empty unique pointer on error \note if the file is on disk it is recommended to use the other load overload since it is less resource intensive \warning The returning document may be locked if a password is required to open the file, and one is not provided (as the userPassword). */ static std::unique_ptr load(QIODevice *device, const QByteArray &ownerPassword = QByteArray(), const QByteArray &userPassword = QByteArray()); /** Load the document from memory \param fileContents the file contents. They are copied so there is no need to keep the byte array around for the full life time of the document. \param ownerPassword the Latin1-encoded owner password to use in loading the file \param userPassword the Latin1-encoded user ("open") password to use in loading the file \return the loaded document, or empty unique pointer on error \warning The returning document may be locked if a password is required to open the file, and one is not provided (as the userPassword). */ static std::unique_ptr loadFromData(const QByteArray &fileContents, const QByteArray &ownerPassword = QByteArray(), const QByteArray &userPassword = QByteArray()); /** Get a specified Page Note that this follows the PDF standard of being zero based - if you want the first page, then you need an index of zero. This function can return empty unique pointer if for some reason the page can't be properly parsed. \param index the page number index \warning The Page object returned by this method internally stores a pointer to the document that it was created from. This pointer will go stale if you delete the Document object. Therefore the Document object needs to be kept alive as long as you want to use the Page object. */ std::unique_ptr page(int index) const; /** \overload The intent is that you can pass in a label like \c "ix" and get the page with that label (which might be in the table of contents), or pass in \c "1" and get the page that the user expects (which might not be the first page, if there is a title page and a table of contents). \param label the page label */ std::unique_ptr page(const QString &label) const; /** The number of pages in the document */ int numPages() const; /** The type of mode that should be used by the application when the document is opened. Note that while this is called page mode, it is really viewer application mode. */ PageMode pageMode() const; /** The layout that pages should be shown in when the document is first opened. This basically describes how pages are shown relative to each other. */ PageLayout pageLayout() const; /** The predominant reading order for text as supplied by the document's viewer preferences. */ Qt::LayoutDirection textDirection() const; /** Provide the passwords required to unlock the document \param ownerPassword the Latin1-encoded owner password to use in loading the file \param userPassword the Latin1-encoded user ("open") password to use in loading the file */ bool unlock(const QByteArray &ownerPassword, const QByteArray &userPassword); /** Determine if the document is locked */ bool isLocked() const; /** The date associated with the document You would use this method with something like: \code QDateTime created = m_doc->date("CreationDate"); QDateTime modified = m_doc->date("ModDate"); \endcode The available dates are: - CreationDate: the date of creation of the document - ModDate: the date of the last change in the document \param type the type of date that is required */ QDateTime date(const QString &type) const; /** Set the Info dict date entry specified by \param key to \param val \returns true on success, false on failure */ bool setDate(const QString &key, const QDateTime &val); /** The date of the creation of the document */ QDateTime creationDate() const; /** Set the creation date of the document to \param val \returns true on success, false on failure */ bool setCreationDate(const QDateTime &val); /** The date of the last change in the document */ QDateTime modificationDate() const; /** Set the modification date of the document to \param val \returns true on success, false on failure */ bool setModificationDate(const QDateTime &val); /** Get specified information associated with the document You would use this method with something like: \code QString title = m_doc->info("Title"); QString subject = m_doc->info("Subject"); \endcode In addition to \c Title and \c Subject, other information that may be available include \c Author, \c Keywords, \c Creator and \c Producer. \param type the information that is required \sa infoKeys() to get a list of the available keys */ QString info(const QString &type) const; /** Set the value of the document's Info dictionary entry specified by \param key to \param val \returns true on success, false on failure */ bool setInfo(const QString &key, const QString &val); /** The title of the document */ QString title() const; /** Set the title of the document to \param val \returns true on success, false on failure */ bool setTitle(const QString &val); /** The author of the document */ QString author() const; /** Set the author of the document to \param val \returns true on success, false on failure */ bool setAuthor(const QString &val); /** The subject of the document */ QString subject() const; /** Set the subject of the document to \param val \returns true on success, false on failure */ bool setSubject(const QString &val); /** The keywords of the document */ QString keywords() const; /** Set the keywords of the document to \param val \returns true on success, false on failure */ bool setKeywords(const QString &val); /** The creator of the document */ QString creator() const; /** Set the creator of the document to \param val \returns true on success, false on failure */ bool setCreator(const QString &val); /** The producer of the document */ QString producer() const; /** Set the producer of the document to \param val \returns true on success, false on failure */ bool setProducer(const QString &val); /** Remove the document's Info dictionary \returns true on success, false on failure */ bool removeInfo(); /** Obtain a list of the available string information keys. */ QStringList infoKeys() const; /** Test if the document is encrypted */ bool isEncrypted() const; /** Test if the document is linearised In some cases, this is called "fast web view", since it is mostly an optimisation for viewing over the Web. */ bool isLinearized() const; /** Test if the permissions on the document allow it to be printed */ bool okToPrint() const; /** Test if the permissions on the document allow it to be printed at high resolution */ bool okToPrintHighRes() const; /** Test if the permissions on the document allow it to be changed. \note depending on the type of change, it may be more appropriate to check other properties as well. */ bool okToChange() const; /** Test if the permissions on the document allow the contents to be copied / extracted */ bool okToCopy() const; /** Test if the permissions on the document allow annotations to be added or modified, and interactive form fields (including signature fields) to be completed. */ bool okToAddNotes() const; /** Test if the permissions on the document allow interactive form fields (including signature fields) to be completed. \note this can be true even if okToAddNotes() is false - this means that only form completion is permitted. */ bool okToFillForm() const; /** Test if the permissions on the document allow interactive form fields (including signature fields) to be set, created and modified */ bool okToCreateFormFields() const; /** Test if the permissions on the document allow content extraction (text and perhaps other content) for accessibility usage (eg for a screen reader) */ bool okToExtractForAccessibility() const; /** Test if the permissions on the document allow it to be "assembled" - insertion, rotation and deletion of pages; or creation of bookmarks and thumbnail images. \note this can be true even if okToChange() is false */ bool okToAssemble() const; /** \brief The version specification of a pdf file */ struct PdfVersion { int major; int minor; }; /** The version of the PDF specification that the document conforms to \since 21.08 */ PdfVersion getPdfVersion() const; /** The fonts within the PDF document. This is a shorthand for getting all the fonts at once. \note this can take a very long time to run with a large document. You may wish to use a FontIterator if you have more than say 20 pages \see newFontIterator() */ QList fonts() const; /** Creates a new FontIterator object for font scanning. The new iterator can be used for reading the font information of the document, reading page by page. \param startPage the initial page from which start reading fonts \see fonts() */ std::unique_ptr newFontIterator(int startPage = 0) const; /** The font data if the font is an embedded one. */ QByteArray fontData(const FontInfo &fi) const; /** The documents embedded within the PDF document. \note there are two types of embedded document - this call only accesses documents that are embedded at the document level. \note The ownership of the EmbeddedFile objects remain with the callee. */ QList embeddedFiles() const; /** Whether there are any documents embedded in this PDF document. */ bool hasEmbeddedFiles() const; /** Gets the outline of the document \returns a vector of outline items, empty if there are none **/ QVector outline() const; /** Tries to resolve the named destination \p name. \note this operation starts a search through the whole document \returns a new LinkDestination object if the named destination was actually found, or empty unique pointer otherwise */ std::unique_ptr linkDestination(const QString &name); /** Sets the paper color \param color the new paper color */ void setPaperColor(const QColor &color); /** The paper color The default color is white. */ QColor paperColor() const; /** Sets the backend used to render the pages. \param backend the new rendering backend */ void setRenderBackend(RenderBackend backend); /** The currently set render backend The default backend is \ref SplashBackend */ RenderBackend renderBackend() const; /** The available rendering backends. */ static QSet availableRenderBackends(); /** Sets the render \p hint . \note some hints may not be supported by some rendering backends. \param on whether the flag should be added or removed. */ void setRenderHint(RenderHint hint, bool on = true); /** The currently set render hints. */ RenderHints renderHints() const; /** Gets a new PS converter for this document. */ std::unique_ptr psConverter() const; /** Gets a new PDF converter for this document. */ std::unique_ptr pdfConverter() const; /** Gets the metadata stream contents */ QString metadata() const; /** Test whether this document has "optional content". Optional content is used to optionally turn on (display) and turn off (not display) some elements of the document. The most common use of this is for layers in design applications, but it can be used for a range of things, such as not including some content in printing, and displaying content in the appropriate language. */ bool hasOptionalContent() const; /** Itemviews model for optional content. The model is owned by the document. */ OptContentModel *optionalContentModel(); /** Document-level JavaScript scripts. Returns the list of document level JavaScript scripts to be always executed before any other script. */ QStringList scripts() const; /** The PDF identifiers. \param permanentId an optional pointer to a variable where store the permanent ID of the document \param updateId an optional pointer to a variable where store the update ID of the document \return whether the document has the IDs */ bool getPdfId(QByteArray *permanentId, QByteArray *updateId) const; /** Returns the type of forms contained in the document */ FormType formType() const; /** Returns the calculate order for forms (using their id) */ QVector formCalculateOrder() const; /** Returns the signatures of this document. Prefer to use this over getting the signatures for all the pages of the document since there are documents with signatures that don't belong to a given page */ std::vector> signatures() const; /** Returns whether the document's XRef table has been reconstructed or not \since 21.06 */ bool xrefWasReconstructed() const; /** Sets the document's XRef reconstruction callback, so whenever a XRef table reconstruction happens the callback will get triggered. \since 21.06 */ void setXRefReconstructedCallback(const std::function &callback); /** Destructor. */ ~Document(); private: Q_DISABLE_COPY(Document) DocumentData *m_doc; 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 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); }; /** Conversion from PDF date string format to QDateTime */ POPPLER_QT6_EXPORT QDateTime convertDate(const char *dateString); /** Whether the color management functions are available. */ POPPLER_QT6_EXPORT bool isCmsAvailable(); /** Whether the overprint preview functionality is available. */ POPPLER_QT6_EXPORT bool isOverprintPreviewAvailable(); class SoundData; /** Container class for a sound file in a PDF document. A sound can be either External (in that case should be loaded the file whose url is represented by url() ), or Embedded, and the player has to play the data contained in data(). */ class POPPLER_QT6_EXPORT SoundObject { public: /** The type of sound */ enum SoundType { External, ///< The real sound file is external Embedded ///< The sound is contained in the data }; /** The encoding format used for the sound */ enum SoundEncoding { Raw, ///< Raw encoding, with unspecified or unsigned values in the range [ 0, 2^B - 1 ] Signed, ///< Twos-complement values muLaw, ///< mu-law-encoded samples ALaw ///< A-law-encoded samples }; /** \cond PRIVATE The caller keeps the ownership of the popplersound argument */ explicit SoundObject(Sound *popplersound); /// \endcond ~SoundObject(); /** Is the sound embedded (SoundObject::Embedded) or external (SoundObject::External)? */ SoundType soundType() const; /** The URL of the sound file to be played, in case of SoundObject::External */ QString url() const; /** The data of the sound, in case of SoundObject::Embedded */ QByteArray data() const; /** The sampling rate of the sound */ double samplingRate() const; /** The number of sound channels to use to play the sound */ int channels() const; /** The number of bits per sample value per channel */ int bitsPerSample() const; /** The encoding used for the sound */ SoundEncoding soundEncoding() const; private: Q_DISABLE_COPY(SoundObject) SoundData *m_soundData; }; class MovieData; /** Container class for a movie object in a PDF document. */ class POPPLER_QT6_EXPORT MovieObject { friend class AnnotationPrivate; public: /** The play mode for playing the movie */ enum PlayMode { PlayOnce, ///< Play the movie once, closing the movie controls at the end PlayOpen, ///< Like PlayOnce, but leaving the controls open PlayRepeat, ///< Play continuously until stopped PlayPalindrome ///< Play forward, then backward, then again foward and so on until stopped }; ~MovieObject(); /** The URL of the movie to be played */ QString url() const; /** The size of the movie */ QSize size() const; /** The rotation (either 0, 90, 180, or 270 degrees clockwise) for the movie, */ int rotation() const; /** Whether show a bar with movie controls */ bool showControls() const; /** How to play the movie */ PlayMode playMode() const; /** Returns whether a poster image should be shown if the movie is not playing. */ bool showPosterImage() const; /** Returns the poster image that should be shown if the movie is not playing. If the image is null but showImagePoster() returns @c true, the first frame of the movie should be used as poster image. */ QImage posterImage() const; private: /// \cond PRIVATE explicit MovieObject(AnnotMovie *ann); /// \endcond Q_DISABLE_COPY(MovieObject) MovieData *m_movieData; }; } 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 poppler-24.02.0/qt6/src/poppler-sound.cc000066400000000000000000000060231455701731300177660ustar00rootroot00000000000000/* poppler-sound.cc: qt interface to poppler * Copyright (C) 2006-2007, Pino Toscano * Copyright (C) 2008, 2018, 2020, 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 * 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 "poppler-qt6.h" #include "Object.h" #include "Stream.h" #include "Sound.h" namespace Poppler { class SoundData { public: SoundData() : m_soundObj(nullptr) { } ~SoundData() { delete m_soundObj; } SoundData(const SoundData &) = delete; SoundData &operator=(const SoundData &) = delete; SoundObject::SoundType m_type; Sound *m_soundObj; }; SoundObject::SoundObject(Sound *popplersound) { m_soundData = new SoundData(); switch (popplersound->getSoundKind()) { case soundEmbedded: m_soundData->m_type = SoundObject::Embedded; break; case soundExternal: default: m_soundData->m_type = SoundObject::External; break; } m_soundData->m_soundObj = popplersound->copy(); } SoundObject::~SoundObject() { delete m_soundData; } SoundObject::SoundType SoundObject::soundType() const { return m_soundData->m_type; } QString SoundObject::url() const { if (m_soundData->m_type != SoundObject::External) { return QString(); } return QString(m_soundData->m_soundObj->getFileName().c_str()); } QByteArray SoundObject::data() const { if (m_soundData->m_type != SoundObject::Embedded) { return QByteArray(); } Stream *stream = m_soundData->m_soundObj->getStream(); stream->reset(); int dataLen = 0; QByteArray fileArray; int i; while ((i = stream->getChar()) != EOF) { fileArray[dataLen] = (char)i; ++dataLen; } fileArray.resize(dataLen); return fileArray; } double SoundObject::samplingRate() const { return m_soundData->m_soundObj->getSamplingRate(); } int SoundObject::channels() const { return m_soundData->m_soundObj->getChannels(); } int SoundObject::bitsPerSample() const { return m_soundData->m_soundObj->getBitsPerSample(); } SoundObject::SoundEncoding SoundObject::soundEncoding() const { switch (m_soundData->m_soundObj->getEncoding()) { case soundRaw: return SoundObject::Raw; case soundSigned: return SoundObject::Signed; case soundMuLaw: return SoundObject::muLaw; case soundALaw: return SoundObject::ALaw; } return SoundObject::Raw; } } poppler-24.02.0/qt6/src/poppler-textbox.cc000066400000000000000000000030151455701731300203310ustar00rootroot00000000000000/* poppler-qt.h: qt interface to poppler * Copyright (C) 2005, Brad Hards * Copyright (C) 2006-2008, Albert Astals Cid * Copyright (C) 2008, Pino Toscano * * 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 "poppler-qt6.h" #include "poppler-private.h" namespace Poppler { TextBox::TextBox(const QString &text, const QRectF &bBox) { m_data = new TextBoxData(); m_data->text = text; m_data->bBox = bBox; } TextBox::~TextBox() { delete m_data; } QString TextBox::text() const { return m_data->text; } QRectF TextBox::boundingBox() const { return m_data->bBox; } TextBox *TextBox::nextWord() const { return m_data->nextWord; } QRectF TextBox::charBoundingBox(int i) const { return m_data->charBBoxes.value(i); } bool TextBox::hasSpaceAfter() const { return m_data->hasSpaceAfter; } } poppler-24.02.0/qt6/src/poppler-version.cpp000066400000000000000000000022271455701731300205220ustar00rootroot00000000000000/* * Copyright (C) 2009-2010, Pino Toscano * Copyright (C) 2018, 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 * 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 "poppler-version.h" QString Poppler::Version::string() { return QStringLiteral(POPPLER_VERSION); } unsigned int Poppler::Version::major() { return POPPLER_VERSION_MAJOR; } unsigned int Poppler::Version::minor() { return POPPLER_VERSION_MINOR; } unsigned int Poppler::Version::micro() { return POPPLER_VERSION_MICRO; } poppler-24.02.0/qt6/src/poppler-version.h.in000066400000000000000000000035461455701731300206010ustar00rootroot00000000000000/* * Copyright (C) 2009, Pino Toscano * Copyright (C) 2018, 2019, 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 * 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_VERSION_H #define POPPLER_VERSION_H #include "poppler-export.h" #include // glibc < 2.28 used to include sys/sysmacros.h // from sys/types.h and sysmacros.h defines minor and major so // undefine them. You may need to undefine them in your code too. #undef minor #undef major #define POPPLER_VERSION "@POPPLER_VERSION@" #define POPPLER_VERSION_MAJOR @POPPLER_MAJOR_VERSION@ #define POPPLER_VERSION_MINOR @POPPLER_MINOR_VERSION@ #define POPPLER_VERSION_MICRO @POPPLER_MICRO_VERSION@ namespace Poppler { namespace Version { /** \returns the version string of the current poppler-qt6 library */ POPPLER_QT6_EXPORT QString string(); /** \returns the "major" number of the version of the current poppler-qt6 library */ POPPLER_QT6_EXPORT unsigned int major(); /** \returns the "minor" number of the version of the current poppler-qt6 library */ POPPLER_QT6_EXPORT unsigned int minor(); /** \returns the "micro" number of the version of the current poppler-qt6 library */ POPPLER_QT6_EXPORT unsigned int micro(); } } #endif poppler-24.02.0/qt6/tests/000077500000000000000000000000001455701731300152225ustar00rootroot00000000000000poppler-24.02.0/qt6/tests/.gitignore000066400000000000000000000007101455701731300172100ustar00rootroot00000000000000.deps .libs *.la *.lo *.moc Makefile Makefile.in stress-poppler-qt6 stress-poppler-dir test-poppler-qt6 test-password-qt6 poppler-attachments poppler-fonts poppler-texts poppler-forms stress-threads-qt6 test-render-to-file check_actualtext check_attachments check_dateConversion check_fonts check_goostring check_lexer check_links check_metadata check_optcontent check_permissions check_pagelayout check_pagemode check_password check_search check_strings poppler-24.02.0/qt6/tests/CMakeLists.txt000066400000000000000000000061611455701731300177660ustar00rootroot00000000000000add_definitions(-DTESTDATADIR=\"${TESTDATADIR}\") include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../src ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/../src ) macro(QT6_ADD_SIMPLETEST exe source) string(REPLACE "-" "" test_name ${exe}) set(${test_name}_SOURCES ${source} ) poppler_add_test(${exe} BUILD_QT6_TESTS ${${test_name}_SOURCES}) target_link_libraries(${exe} poppler-qt6 Qt6::Widgets) endmacro() macro(QT6_ADD_QTEST exe source) string(REPLACE "-" "" test_name ${exe}) set(${test_name}_SOURCES ${source} ) poppler_add_test(${exe} BUILD_QT6_TESTS ${${test_name}_SOURCES}) 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() qt6_add_simpletest(test-poppler-qt6 test-poppler-qt6.cpp) qt6_add_simpletest(test-password-qt6 test-password-qt6.cpp) qt6_add_simpletest(test-render-to-file-qt6 test-render-to-file.cpp) qt6_add_simpletest(poppler-qt6-forms poppler-forms.cpp) qt6_add_simpletest(poppler-qt6-fonts poppler-fonts.cpp) qt6_add_simpletest(poppler-qt6-attachments poppler-attachments.cpp) qt6_add_simpletest(stress-poppler-qt6 stress-poppler-qt6.cpp) qt6_add_simpletest(stress-poppler-dir-qt6 stress-poppler-dir.cpp) qt6_add_simpletest(stress-threads-qt6 stress-threads-qt6.cpp) qt6_add_simpletest(poppler-qt6-texts poppler-texts.cpp) qt6_add_simpletest(poppler-qt6-page-labels poppler-page-labels.cpp) qt6_add_qtest(check_qt6_attachments check_attachments.cpp) qt6_add_qtest(check_qt6_dateConversion check_dateConversion.cpp) qt6_add_qtest(check_qt6_fonts check_fonts.cpp) qt6_add_qtest(check_qt6_links check_links.cpp) qt6_add_qtest(check_qt6_annotations check_annotations.cpp) qt6_add_qtest(check_qt6_metadata check_metadata.cpp) qt6_add_qtest(check_qt6_optcontent check_optcontent.cpp) qt6_add_qtest(check_qt6_forms check_forms.cpp) qt6_add_qtest(check_qt6_pagelayout check_pagelayout.cpp) qt6_add_qtest(check_qt6_pagemode check_pagemode.cpp) qt6_add_qtest(check_qt6_password check_password.cpp) qt6_add_qtest(check_qt6_permissions check_permissions.cpp) qt6_add_qtest(check_qt6_search check_search.cpp) qt6_add_qtest(check_qt6_actualtext check_actualtext.cpp) qt6_add_qtest(check_qt6_lexer check_lexer.cpp) qt6_add_qtest(check_qt6_internal_outline check_internal_outline.cpp) qt6_add_qtest(check_qt6_goostring check_goostring.cpp) 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 (NOT WIN32) qt6_add_qtest(check_qt6_pagelabelinfo check_pagelabelinfo.cpp) qt6_add_qtest(check_qt6_strings check_strings.cpp) endif () poppler-24.02.0/qt6/tests/README.unittest000066400000000000000000000021201455701731300177530ustar00rootroot00000000000000The unittests for the Qt6 bindings rely on the QtTestLib package, and will not be built until this is installed. If you do not have it, then you can download it from the Trolltech website. Note that there are a range of ways in which you can run the tests: 1. "make check" will run all the tests. 2. You can run a single test by executing the applicable executable. For example, you can run the PageMode tests by "./check_pagemode" 3. You can run a single function within a single test by appending the name of the function to the executable. For example, if you just want to run the FullScreen test within the PageMode tests, you can "./check_pagemode checkFullScreen". Run the executable with -functions to get a list of all the functions. 4. You can run a single function with specific data by appending the name of the function, followed by a colon, then the data label to the executable. For example, to just do the Author check within the metadata checks, you can "./check_metadata checkStrings:Author". For a full list of options, run a executable with "-help". Brad Hards bradh@frogmouth.net poppler-24.02.0/qt6/tests/check_actualtext.cpp000066400000000000000000000021751455701731300212460ustar00rootroot00000000000000#include #include #include class TestActualText : public QObject { Q_OBJECT public: explicit TestActualText(QObject *parent = nullptr) : QObject(parent) { } private slots: void checkActualText1(); void checkActualText2(); private: void checkActualText(Poppler::Document &doc); }; void TestActualText::checkActualText(Poppler::Document &doc) { std::unique_ptr page = doc.page(0); QVERIFY(page); QCOMPARE(page->text(QRectF()), QLatin1String("The slow brown fox jumps over the black dog.")); } void TestActualText::checkActualText1() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/WithActualText.pdf"); QVERIFY(doc); checkActualText(*doc); } void TestActualText::checkActualText2() { QFile file(TESTDATADIR "/unittestcases/WithActualText.pdf"); QVERIFY(file.open(QIODevice::ReadOnly)); std::unique_ptr doc = Poppler::Document::load(&file); QVERIFY(doc); checkActualText(*doc); } QTEST_GUILESS_MAIN(TestActualText) #include "check_actualtext.moc" poppler-24.02.0/qt6/tests/check_annotations.cpp000066400000000000000000000234071455701731300214260ustar00rootroot00000000000000#include #include #include #include #include #include #include "poppler/Annot.h" #include "goo/GooString.h" #include "goo/gstrtod.h" class TestAnnotations : public QObject { Q_OBJECT public: explicit TestAnnotations(QObject *parent = nullptr) : QObject(parent) { } void saveAndCheck(const std::unique_ptr &doc, const std::function &checkFunction); private slots: void checkQColorPrecision(); void checkFontSizeAndColor(); void checkHighlightFromAndToQuads(); void checkUTF16LEAnnot(); void checkModificationCreationDate(); void checkNonMarkupAnnotations(); void checkDefaultAppearance(); }; /* Is .5f sufficient for 16 bit color channel roundtrip trough save and load on all architectures? */ 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()); uint16_t denormalized = std::round(deserialized * std::numeric_limits::max()); if (static_cast(i) != denormalized) { precisionOk = false; break; } } QVERIFY(precisionOk); } void TestAnnotations::checkFontSizeAndColor() { const QString contents = QStringLiteral("foobar"); const std::vector testColors { QColor::fromRgb(0xAB, 0xCD, 0xEF), QColor::fromCmyk(0xAB, 0xBC, 0xCD, 0xDE) }; const QFont testFont(QStringLiteral("Helvetica"), 20); QTemporaryFile tempFile; QVERIFY(tempFile.open()); tempFile.close(); { std::unique_ptr doc { Poppler::Document::load(TESTDATADIR "/unittestcases/UseNone.pdf") }; QVERIFY(doc.get()); std::unique_ptr page { doc->page(0) }; QVERIFY(page.get()); for (const auto &color : testColors) { auto annot = std::make_unique(Poppler::TextAnnotation::InPlace); annot->setBoundary(QRectF(0.0, 0.0, 1.0, 1.0)); annot->setContents(contents); annot->setTextFont(testFont); annot->setTextColor(color); page->addAnnotation(annot.get()); } std::unique_ptr conv(doc->pdfConverter()); QVERIFY(conv.get()); conv->setOutputFileName(tempFile.fileName()); conv->setPDFOptions(Poppler::PDFConverter::WithChanges); QVERIFY(conv->convert()); } { std::unique_ptr doc { Poppler::Document::load(tempFile.fileName()) }; QVERIFY(doc.get()); std::unique_ptr page { doc->page(0) }; QVERIFY(page.get()); auto annots = page->annotations(); QCOMPARE(annots.size(), static_cast(testColors.size())); auto &&annot = annots.cbegin(); for (const auto &color : testColors) { QCOMPARE((*annot)->subType(), Poppler::Annotation::AText); auto textAnnot = static_cast(annot->get()); QCOMPARE(textAnnot->contents(), contents); QCOMPARE(textAnnot->textFont().pointSize(), testFont.pointSize()); QCOMPARE(static_cast(textAnnot->textColor().spec()), static_cast(color.spec())); QCOMPARE(textAnnot->textColor(), color); if (annot != annots.cend()) { ++annot; } } } } namespace Poppler { static bool operator==(const Poppler::HighlightAnnotation::Quad &a, const Poppler::HighlightAnnotation::Quad &b) { // FIXME We do not compare capStart, capEnd and feather since AnnotQuadrilaterals doesn't contain that info and thus // HighlightAnnotationPrivate::fromQuadrilaterals uses default values return a.points[0] == b.points[0] && a.points[1] == b.points[1] && a.points[2] == b.points[2] && a.points[3] == b.points[3]; } } void TestAnnotations::checkHighlightFromAndToQuads() { std::unique_ptr doc { Poppler::Document::load(TESTDATADIR "/unittestcases/UseNone.pdf") }; std::unique_ptr page { doc->page(0) }; auto ha = std::make_unique(); page->addAnnotation(ha.get()); const QList quads = { { { { 0, 0.1 }, { 0.2, 0.3 }, { 0.4, 0.5 }, { 0.6, 0.7 } }, false, false, 0 }, { { { 0.8, 0.9 }, { 0.1, 0.2 }, { 0.3, 0.4 }, { 0.5, 0.6 } }, true, false, 0.4 } }; ha->setHighlightQuads(quads); QCOMPARE(ha->highlightQuads(), quads); } void TestAnnotations::checkUTF16LEAnnot() { std::unique_ptr doc { Poppler::Document::load(TESTDATADIR "/unittestcases/utf16le-annot.pdf") }; QVERIFY(doc.get()); std::unique_ptr page { doc->page(0) }; QVERIFY(page.get()); auto annots = page->annotations(); QCOMPARE(annots.size(), 2); const auto &annot = annots[1]; QCOMPARE(annot->contents(), QString::fromUtf8("Únîcödé豰")); // clazy:exclude=qstring-allocations } void TestAnnotations::saveAndCheck(const std::unique_ptr &doc, const std::function &checkFunction) { // also check that saving yields the same output QTemporaryFile tempFile; QVERIFY(tempFile.open()); tempFile.close(); std::unique_ptr conv(doc->pdfConverter()); conv->setOutputFileName(tempFile.fileName()); conv->setPDFOptions(Poppler::PDFConverter::WithChanges); conv->convert(); std::unique_ptr savedDoc { Poppler::Document::load(tempFile.fileName()) }; std::unique_ptr page { doc->page(0) }; auto annots = page->annotations(); checkFunction(annots.at(1).get()); } void TestAnnotations::checkModificationCreationDate() { std::unique_ptr doc { Poppler::Document::load(TESTDATADIR "/unittestcases/utf16le-annot.pdf") }; QVERIFY(doc.get()); std::unique_ptr page { doc->page(0) }; auto annots = page->annotations(); const auto &annot = annots.at(1); QCOMPARE(annot->creationDate(), QDateTime()); QCOMPARE(annot->modificationDate(), QDateTime()); const QDateTime dt1(QDate(2020, 8, 7), QTime(18, 34, 56)); annot->setCreationDate(dt1); auto checkFunction1 = [dt1](Poppler::Annotation *a) { QCOMPARE(a->creationDate(), dt1); // setting the creation date updates the modification date QVERIFY(std::abs(a->modificationDate().secsTo(QDateTime::currentDateTime())) < 2); }; checkFunction1(annot.get()); saveAndCheck(doc, checkFunction1); const QDateTime dt2(QDate(2020, 8, 30), QTime(8, 14, 52)); annot->setModificationDate(dt2); auto checkFunction2 = [dt2](Poppler::Annotation *a) { QCOMPARE(a->modificationDate(), dt2); }; checkFunction2(annot.get()); saveAndCheck(doc, checkFunction2); // setting the creation date to empty means "use the modification date" and also updates the modification date // so both creation date and modification date are the same and are now annot->setCreationDate(QDateTime()); auto checkFunction3 = [](Poppler::Annotation *a) { QVERIFY(std::abs(a->creationDate().secsTo(QDateTime::currentDateTime())) < 2); QCOMPARE(a->creationDate(), a->modificationDate()); }; checkFunction3(annot.get()); saveAndCheck(doc, checkFunction3); annot->setModificationDate(QDateTime()); auto checkFunction4 = [](Poppler::Annotation *a) { QCOMPARE(a->creationDate(), QDateTime()); QCOMPARE(a->modificationDate(), QDateTime()); }; checkFunction4(annot.get()); saveAndCheck(doc, checkFunction4); } void TestAnnotations::checkNonMarkupAnnotations() { std::unique_ptr doc { Poppler::Document::load(TESTDATADIR "/unittestcases/checkbox_issue_159.pdf") }; QVERIFY(doc.get()); std::unique_ptr page { doc->page(0) }; QVERIFY(page.get()); auto annots = page->annotations(); QCOMPARE(annots.size(), 17); } void TestAnnotations::checkDefaultAppearance() { std::unique_ptr roundtripString; { GooString daString { "/Helv 10 Tf 0.1 0.2 0.3 rg" }; const DefaultAppearance da { &daString }; QCOMPARE(da.getFontPtSize(), 10.); QVERIFY(da.getFontName().isName()); QCOMPARE(da.getFontName().getName(), "Helv"); const AnnotColor *color = da.getFontColor(); QVERIFY(color); QCOMPARE(color->getSpace(), AnnotColor::colorRGB); QCOMPARE(color->getValues()[0], 0.1); QCOMPARE(color->getValues()[1], 0.2); QCOMPARE(color->getValues()[2], 0.3); roundtripString = std::make_unique(da.toAppearanceString()); } { /* roundtrip through parse/generate/parse shall preserve values */ const DefaultAppearance da { roundtripString.get() }; QCOMPARE(da.getFontPtSize(), 10.); QVERIFY(da.getFontName().isName()); QCOMPARE(da.getFontName().getName(), "Helv"); const AnnotColor *color = da.getFontColor(); QVERIFY(color); QCOMPARE(color->getSpace(), AnnotColor::colorRGB); QCOMPARE(color->getValues()[0], 0.1); QCOMPARE(color->getValues()[1], 0.2); QCOMPARE(color->getValues()[2], 0.3); } { /* parsing bad DA strings must not cause crash */ GooString daString { "/ % Tf 1 2 rg" }; const DefaultAppearance da { &daString }; QVERIFY(!da.getFontName().isName()); } } QTEST_GUILESS_MAIN(TestAnnotations) #include "check_annotations.moc" poppler-24.02.0/qt6/tests/check_attachments.cpp000066400000000000000000000122341455701731300214000ustar00rootroot00000000000000#include #include #include class TestAttachments : public QObject { Q_OBJECT public: explicit TestAttachments(QObject *parent = nullptr) : QObject(parent) { } private slots: void checkNoAttachments(); void checkAttach1(); void checkAttach2(); void checkAttach3(); void checkAttach4(); }; void TestAttachments::checkNoAttachments() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/truetype.pdf"); QVERIFY(doc); QCOMPARE(doc->hasEmbeddedFiles(), false); } void TestAttachments::checkAttach1() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/WithAttachments.pdf"); QVERIFY(doc); QVERIFY(doc->hasEmbeddedFiles()); QList fileList = doc->embeddedFiles(); QCOMPARE(fileList.size(), 2); Poppler::EmbeddedFile *embfile = fileList.at(0); QCOMPARE(embfile->name(), QLatin1String("kroller.png")); QCOMPARE(embfile->description(), QString()); QCOMPARE(embfile->createDate(), QDateTime(QDate(), QTime())); QCOMPARE(embfile->modDate(), QDateTime(QDate(), QTime())); QCOMPARE(embfile->mimeType(), QString()); QFile file(TESTDATADIR "/unittestcases/kroller.png"); QVERIFY(file.open(QIODevice::ReadOnly)); QByteArray krollerData = file.readAll(); QByteArray embdata = embfile->data(); QCOMPARE(krollerData, embdata); Poppler::EmbeddedFile *embfile2 = fileList.at(1); QCOMPARE(embfile2->name(), QLatin1String("gnome-64.gif")); QCOMPARE(embfile2->description(), QString()); QCOMPARE(embfile2->modDate(), QDateTime(QDate(), QTime())); QCOMPARE(embfile2->createDate(), QDateTime(QDate(), QTime())); QCOMPARE(embfile2->mimeType(), QString()); QFile file2(TESTDATADIR "/unittestcases/gnome-64.gif"); QVERIFY(file2.open(QIODevice::ReadOnly)); QByteArray g64Data = file2.readAll(); QByteArray emb2data = embfile2->data(); QCOMPARE(g64Data, emb2data); } void TestAttachments::checkAttach2() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/A6EmbeddedFiles.pdf"); QVERIFY(doc); QVERIFY(doc->hasEmbeddedFiles()); QList fileList; fileList = doc->embeddedFiles(); QCOMPARE(fileList.size(), 3); Poppler::EmbeddedFile *embfile1 = fileList.at(0); QCOMPARE(embfile1->name(), QLatin1String("Acro7 thoughts")); QCOMPARE(embfile1->description(), QString()); QCOMPARE(embfile1->createDate(), QDateTime(QDate(2003, 8, 4), QTime(13, 54, 54), Qt::UTC)); QCOMPARE(embfile1->modDate(), QDateTime(QDate(2003, 8, 4), QTime(14, 15, 27), Qt::UTC)); QCOMPARE(embfile1->mimeType(), QLatin1String("text/xml")); Poppler::EmbeddedFile *embfile2 = fileList.at(1); QCOMPARE(embfile2->name(), QLatin1String("acro transitions 1.xls")); QCOMPARE(embfile2->description(), QString()); QCOMPARE(embfile2->createDate(), QDateTime(QDate(2003, 7, 18), QTime(21, 7, 16), Qt::UTC)); QCOMPARE(embfile2->modDate(), QDateTime(QDate(2003, 7, 22), QTime(13, 4, 40), Qt::UTC)); QCOMPARE(embfile2->mimeType(), QLatin1String("application/excel")); Poppler::EmbeddedFile *embfile3 = fileList.at(2); QCOMPARE(embfile3->name(), QLatin1String("apago_pdfe_wide.gif")); QCOMPARE(embfile3->description(), QString()); QCOMPARE(embfile3->createDate(), QDateTime(QDate(2003, 1, 31), QTime(15, 54, 29), Qt::UTC)); QCOMPARE(embfile3->modDate(), QDateTime(QDate(2003, 1, 31), QTime(15, 52, 58), Qt::UTC)); QCOMPARE(embfile3->mimeType(), QString()); } void TestAttachments::checkAttach3() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/shapes+attachments.pdf"); QVERIFY(doc); QVERIFY(doc->hasEmbeddedFiles()); QList fileList; fileList = doc->embeddedFiles(); QCOMPARE(fileList.size(), 1); Poppler::EmbeddedFile *embfile = fileList.at(0); QCOMPARE(embfile->name(), QLatin1String("ADEX1.xpdf.pgp")); QCOMPARE(embfile->description(), QString()); QCOMPARE(embfile->createDate(), QDateTime(QDate(2004, 3, 29), QTime(19, 37, 16), Qt::UTC)); QCOMPARE(embfile->modDate(), QDateTime(QDate(2004, 3, 29), QTime(19, 37, 16), Qt::UTC)); QCOMPARE(embfile->mimeType(), QString()); } void TestAttachments::checkAttach4() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/imageretrieve+attachment.pdf"); QVERIFY(doc); QVERIFY(doc->hasEmbeddedFiles()); QList fileList; fileList = doc->embeddedFiles(); QCOMPARE(fileList.size(), 1); Poppler::EmbeddedFile *embfile = fileList.at(0); QCOMPARE(embfile->name(), QLatin1String("export-altona.csv")); QCOMPARE(embfile->description(), QLatin1String("Altona Export")); QCOMPARE(embfile->createDate(), QDateTime(QDate(2005, 8, 30), QTime(20, 49, 35), Qt::UTC)); QCOMPARE(embfile->modDate(), QDateTime(QDate(2005, 8, 30), QTime(20, 49, 52), Qt::UTC)); QCOMPARE(embfile->mimeType(), QLatin1String("application/vnd.ms-excel")); } QTEST_GUILESS_MAIN(TestAttachments) #include "check_attachments.moc" poppler-24.02.0/qt6/tests/check_cidfontswidthsbuilder.cpp000066400000000000000000000065221455701731300234730ustar00rootroot00000000000000//======================================================================== // // 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" poppler-24.02.0/qt6/tests/check_dateConversion.cpp000066400000000000000000000062621455701731300220540ustar00rootroot00000000000000#include Q_DECLARE_METATYPE(QDate) Q_DECLARE_METATYPE(QTime) #include class TestDateConv : public QObject { Q_OBJECT public: explicit TestDateConv(QObject *parent = nullptr) : QObject(parent) { } private slots: void initTestCase(); void checkDates_data(); void checkDates(); void checkInvalidDates_data(); void checkInvalidDates(); }; void TestDateConv::initTestCase() { qRegisterMetaType("QDate"); qRegisterMetaType("QTime"); } void TestDateConv::checkDates_data() { QTest::addColumn("input"); QTest::addColumn("day"); QTest::addColumn("time"); // This is a typical case - all data provided QTest::newRow("D:20040101121110") << QByteArray("D:20040101121110Z") << QDate(2004, 1, 1) << QTime(12, 11, 10); // The D: is strongly recommended, but optional QTest::newRow("20040101121110") << QByteArray("20040101121110Z") << QDate(2004, 1, 1) << QTime(12, 11, 10); // Only the year is actually required QTest::newRow("D:2006") << QByteArray("D:2006") << QDate(2006, 1, 1) << QTime(0, 0, 0); QTest::newRow("D:200602") << QByteArray("D:200602") << QDate(2006, 2, 1) << QTime(0, 0, 0); QTest::newRow("D:20060304") << QByteArray("D:20060304") << QDate(2006, 3, 4) << QTime(0, 0, 0); QTest::newRow("D:2006030405") << QByteArray("D:2006030405") << QDate(2006, 3, 4) << QTime(5, 0, 0); QTest::newRow("D:200603040512") << QByteArray("D:200603040512") << QDate(2006, 3, 4) << QTime(5, 12, 0); // If the timezone isn't specified, I assume UTC QTest::newRow("D:20060304051226") << QByteArray("D:20060304051226") << QDate(2006, 3, 4) << QTime(5, 12, 26); // Check for real timezone conversions QTest::newRow("D:20030131115258-04'00'") << QByteArray("D:20030131115258-04'00'") << QDate(2003, 1, 31) << QTime(15, 52, 58); QTest::newRow("D:20030131115258+05'00'") << QByteArray("D:20030131115258+05'00'") << QDate(2003, 1, 31) << QTime(6, 52, 58); // There are places that have non-hour offsets // Yep, that means you Adelaide. QTest::newRow("D:20030131115258+08'30'") << QByteArray("D:20030131115258+08'30'") << QDate(2003, 1, 31) << QTime(3, 22, 58); QTest::newRow("D:20030131115258-08'30'") << QByteArray("D:20030131115258-08'30'") << QDate(2003, 1, 31) << QTime(20, 22, 58); } void TestDateConv::checkDates() { QFETCH(QByteArray, input); QFETCH(QDate, day); QFETCH(QTime, time); QCOMPARE(Poppler::convertDate(input.constData()), QDateTime(day, time, Qt::UTC)); } void TestDateConv::checkInvalidDates_data() { QTest::addColumn("input"); // Null data QTest::newRow("Null data") << QByteArray(); // Empty data QTest::newRow("Empty data") << QByteArray(""); // Empty data QTest::newRow("One character") << QByteArray("D"); // Empty data QTest::newRow("'D:'") << QByteArray("D:"); // Empty data QTest::newRow("Not a date") << QByteArray("D:IAmNotAValidDate"); } void TestDateConv::checkInvalidDates() { QFETCH(QByteArray, input); QCOMPARE(Poppler::convertDate(input.constData()), QDateTime()); } QTEST_GUILESS_MAIN(TestDateConv) #include "check_dateConversion.moc" poppler-24.02.0/qt6/tests/check_distinguished_name_parser.cpp000066400000000000000000000221161455701731300243040ustar00rootroot00000000000000//======================================================================== // // 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 { "CN=S\\69mpl\\65\\7A" } << DN::Result { { "CN", "Simplez" } }; QTest::newRow("SN=Lu\\C4\\8Di\\C4\\87") << std::string { "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 { "CN=\"Forbidden !@#$%&*()<>[]{},.?/\\| chars\"" } << DN::Result { { "CN", "Forbidden !@#$%&*()<>[]{},.?/\\| chars" } }; QTest::newRow("Quoted quotation") << std::string { "CN=\"Quotation \\\" Mark\"" } << DN::Result { { "CN", "Quotation \" Mark" } }; QTest::newRow("Quoted quotation") << std::string { "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 { "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" poppler-24.02.0/qt6/tests/check_fonts.cpp000066400000000000000000000153361455701731300202240ustar00rootroot00000000000000#include #include #include class TestFontsData : public QObject { Q_OBJECT public: explicit TestFontsData(QObject *parent = nullptr) : QObject(parent) { } private slots: void checkNoFonts(); void checkType1(); void checkType3(); void checkTrueType(); void checkFontIterator(); void checkSecondDocumentQuery(); void checkMultipleIterations(); void checkIteratorFonts(); }; static QList loadFontsViaIterator(Poppler::Document *doc, int from = 0, int count = -1) { int num = count == -1 ? doc->numPages() - from : count; QList list; std::unique_ptr it(doc->newFontIterator(from)); while (it->hasNext() && num) { list += it->next(); --num; } return list; } namespace Poppler { static bool operator==(const FontInfo &f1, const FontInfo &f2) { if (f1.name() != f2.name()) { return false; } if (f1.file() != f2.file()) { return false; } if (f1.isEmbedded() != f2.isEmbedded()) { return false; } if (f1.isSubset() != f2.isSubset()) { return false; } if (f1.type() != f2.type()) { return false; } if (f1.typeName() != f2.typeName()) { return false; } return true; } } void TestFontsData::checkNoFonts() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/tests/image.pdf"); QVERIFY(doc); QList listOfFonts = doc->fonts(); QCOMPARE(listOfFonts.size(), 0); } void TestFontsData::checkType1() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/tests/text.pdf"); QVERIFY(doc); QList listOfFonts = doc->fonts(); QCOMPARE(listOfFonts.size(), 1); QCOMPARE(listOfFonts.at(0).name(), QLatin1String("Helvetica")); QCOMPARE(listOfFonts.at(0).type(), Poppler::FontInfo::Type1); QCOMPARE(listOfFonts.at(0).typeName(), QLatin1String("Type 1")); QCOMPARE(listOfFonts.at(0).isEmbedded(), false); QCOMPARE(listOfFonts.at(0).isSubset(), false); } void TestFontsData::checkType3() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/tests/type3.pdf"); QVERIFY(doc); QList listOfFonts = doc->fonts(); QCOMPARE(listOfFonts.size(), 2); QCOMPARE(listOfFonts.at(0).name(), QLatin1String("Helvetica")); QCOMPARE(listOfFonts.at(0).type(), Poppler::FontInfo::Type1); QCOMPARE(listOfFonts.at(0).typeName(), QLatin1String("Type 1")); QCOMPARE(listOfFonts.at(0).isEmbedded(), false); QCOMPARE(listOfFonts.at(0).isSubset(), false); QCOMPARE(listOfFonts.at(1).name(), QString()); QCOMPARE(listOfFonts.at(1).type(), Poppler::FontInfo::Type3); QCOMPARE(listOfFonts.at(1).typeName(), QLatin1String("Type 3")); QCOMPARE(listOfFonts.at(1).isEmbedded(), true); QCOMPARE(listOfFonts.at(1).isSubset(), false); } void TestFontsData::checkTrueType() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/truetype.pdf"); QVERIFY(doc); QList listOfFonts = doc->fonts(); QCOMPARE(listOfFonts.size(), 2); QCOMPARE(listOfFonts.at(0).name(), QLatin1String("Arial-BoldMT")); QCOMPARE(listOfFonts.at(0).type(), Poppler::FontInfo::TrueType); QCOMPARE(listOfFonts.at(0).typeName(), QLatin1String("TrueType")); QCOMPARE(listOfFonts.at(0).isEmbedded(), false); QCOMPARE(listOfFonts.at(0).isSubset(), false); QCOMPARE(listOfFonts.at(1).name(), QLatin1String("ArialMT")); QCOMPARE(listOfFonts.at(1).type(), Poppler::FontInfo::TrueType); QCOMPARE(listOfFonts.at(1).typeName(), QLatin1String("TrueType")); QCOMPARE(listOfFonts.at(1).isEmbedded(), false); QCOMPARE(listOfFonts.at(1).isSubset(), false); } void TestFontsData::checkFontIterator() { // loading a 1-page document std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/tests/type3.pdf"); QVERIFY(doc); // loading a 6-pages document std::unique_ptr doc6 = Poppler::Document::load(TESTDATADIR "/tests/cropbox.pdf"); QVERIFY(doc6); std::unique_ptr it; // some tests with the 1-page document: // - check a default iterator it = doc->newFontIterator(); QVERIFY(it->hasNext()); // - check an iterator for negative pages to behave as 0 it = doc->newFontIterator(-1); QVERIFY(it->hasNext()); // - check an iterator for pages out of the page limit it = doc->newFontIterator(1); QVERIFY(!it->hasNext()); // - check that it reaches the end after 1 iteration it = doc->newFontIterator(); QVERIFY(it->hasNext()); it->next(); QVERIFY(!it->hasNext()); // some tests with the 6-page document: // - check a default iterator it = doc6->newFontIterator(); QVERIFY(it->hasNext()); // - check an iterator for pages out of the page limit it = doc6->newFontIterator(6); QVERIFY(!it->hasNext()); // - check that it reaches the end after 6 iterations it = doc6->newFontIterator(); QVERIFY(it->hasNext()); it->next(); QVERIFY(it->hasNext()); it->next(); QVERIFY(it->hasNext()); it->next(); QVERIFY(it->hasNext()); it->next(); QVERIFY(it->hasNext()); it->next(); QVERIFY(it->hasNext()); it->next(); QVERIFY(!it->hasNext()); } void TestFontsData::checkSecondDocumentQuery() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/tests/type3.pdf"); QVERIFY(doc); QList listOfFonts = doc->fonts(); QCOMPARE(listOfFonts.size(), 2); // check we get the very same result when calling fonts() again (#19405) QList listOfFonts2 = doc->fonts(); QCOMPARE(listOfFonts, listOfFonts2); } void TestFontsData::checkMultipleIterations() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/tests/type3.pdf"); QVERIFY(doc); QList listOfFonts = loadFontsViaIterator(doc.get()); QCOMPARE(listOfFonts.size(), 2); QList listOfFonts2 = loadFontsViaIterator(doc.get()); QCOMPARE(listOfFonts, listOfFonts2); } void TestFontsData::checkIteratorFonts() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/tests/fonts.pdf"); QVERIFY(doc); QList listOfFonts = doc->fonts(); QCOMPARE(listOfFonts.size(), 3); // check we get the very same result when gatering fonts using the iterator QList listOfFonts2 = loadFontsViaIterator(doc.get()); QCOMPARE(listOfFonts, listOfFonts2); } QTEST_GUILESS_MAIN(TestFontsData) #include "check_fonts.moc" poppler-24.02.0/qt6/tests/check_forms.cpp000066400000000000000000000220021455701731300202050ustar00rootroot00000000000000#include #include #include #include #include class TestForms : public QObject { Q_OBJECT public: explicit TestForms(QObject *parent = nullptr) : QObject(parent) { } private slots: void testCheckbox(); // Test for issue #655 void testCheckboxIssue159(); // Test for issue #159 void testSetIcon(); // Test that setIcon will always be valid. void testSetPrintable(); void testSetAppearanceText(); void testStandAloneWidgets(); // check for 'de facto' tooltips. Issue #34 void testUnicodeFieldAttributes(); }; void TestForms::testCheckbox() { // Test for checkbox issue #655 std::unique_ptr document = Poppler::Document::load(TESTDATADIR "/unittestcases/latex-hyperref-checkbox-issue-655.pdf"); QVERIFY(document); std::unique_ptr page(document->page(0)); QVERIFY(page); std::vector> forms = page->formFields(); QCOMPARE(forms.size(), 1); Poppler::FormField *form = forms.at(0).get(); QCOMPARE(form->type(), Poppler::FormField::FormButton); Poppler::FormFieldButton *chkFormFieldButton = static_cast(form); // Test this is actually a Checkbox QCOMPARE(chkFormFieldButton->buttonType(), Poppler::FormFieldButton::CheckBox); // checkbox comes initially 'unchecked' QCOMPARE(chkFormFieldButton->state(), false); // let's mark it as 'checked' chkFormFieldButton->setState(true); // now test if it was succesfully 'checked' QCOMPARE(chkFormFieldButton->state(), true); } void TestForms::testStandAloneWidgets() { // Check for 'de facto' tooltips. Issue #34 std::unique_ptr document = Poppler::Document::load(TESTDATADIR "/unittestcases/tooltip.pdf"); QVERIFY(document); std::unique_ptr page = document->page(0); QVERIFY(page); std::vector> forms = page->formFields(); QCOMPARE(forms.size(), 3); for (const std::unique_ptr &field : forms) { QCOMPARE(field->type(), Poppler::FormField::FormButton); Poppler::FormFieldButton *fieldButton = static_cast(field.get()); QCOMPARE(fieldButton->buttonType(), Poppler::FormFieldButton::Push); FormField *ff = Poppler::FormFieldData::getFormWidget(fieldButton)->getField(); QVERIFY(ff); QCOMPARE(ff->isStandAlone(), true); // tooltip.pdf has only these 3 standalone widgets QVERIFY(field->uiName() == QStringLiteral("This is a tooltip!") || // clazy:exclude=qstring-allocations field->uiName() == QStringLiteral("Sulfuric acid") || field->uiName() == QString::fromUtf8("little Gauß")); } } void TestForms::testCheckboxIssue159() { // Test for checkbox issue #159 std::unique_ptr document = Poppler::Document::load(TESTDATADIR "/unittestcases/checkbox_issue_159.pdf"); QVERIFY(document); std::unique_ptr page = document->page(0); QVERIFY(page); Poppler::FormFieldButton *beerFieldButton = nullptr; Poppler::FormFieldButton *wineFieldButton = nullptr; std::vector> forms = page->formFields(); // Let's find and assign the "Wine" and "Beer" radio buttons for (const std::unique_ptr &field : forms) { if (field->type() != Poppler::FormField::FormButton) { continue; } Poppler::FormFieldButton *fieldButton = static_cast(field.get()); if (fieldButton->buttonType() != Poppler::FormFieldButton::Radio) { continue; } // printf("%s \n", fieldButton->caption().toLatin1().data()); if (fieldButton->caption() == QStringLiteral("Wine")) { wineFieldButton = fieldButton; } else if (fieldButton->caption() == QStringLiteral("Beer")) { beerFieldButton = fieldButton; } } // "Beer" and "Wine" radiobuttons belong to the same RadioButton group. // So selecting one should unselect the other. QVERIFY(beerFieldButton); QVERIFY(wineFieldButton); // Test that the RadioButton group comes with "Beer" initially selected QCOMPARE(beerFieldButton->state(), true); // Now select "Wine". As a result "Beer" should no longer be selected. wineFieldButton->setState(true); // Test that "Beer" is indeed not reporting as being selected QCOMPARE(beerFieldButton->state(), false); } void TestForms::testSetIcon() { std::unique_ptr document = Poppler::Document::load(TESTDATADIR "/unittestcases/form_set_icon.pdf"); QVERIFY(document); std::unique_ptr page = document->page(0); QVERIFY(page); std::vector> forms = page->formFields(); Poppler::FormFieldButton *anmButton = nullptr; // First we are finding the field which will have its icon changed for (const std::unique_ptr &field : forms) { if (field->type() != Poppler::FormField::FormButton) { continue; } Poppler::FormFieldButton *fieldButton = static_cast(field.get()); if (field->name() == QStringLiteral("anm0")) { anmButton = fieldButton; } } QVERIFY(anmButton); // Then we set the Icon on this field, for every other field // And verify if it has a valid icon for (const std::unique_ptr &field : forms) { if (field->type() != Poppler::FormField::FormButton) { continue; } Poppler::FormFieldButton *fieldButton = static_cast(field.get()); if (field->name() == QStringLiteral("anm0")) { continue; } Poppler::FormFieldIcon newIcon = fieldButton->icon(); anmButton->setIcon(newIcon); Poppler::FormFieldIcon anmIcon = anmButton->icon(); QVERIFY(Poppler::FormFieldIconData::getData(anmIcon)); QVERIFY(Poppler::FormFieldIconData::getData(anmIcon)->icon); QCOMPARE(Poppler::FormFieldIconData::getData(anmIcon)->icon->lookupNF("AP").dictLookupNF("N").getRef().num, Poppler::FormFieldIconData::getData(newIcon)->icon->lookupNF("AP").dictLookupNF("N").getRef().num); } // Just making sure that setting a invalid icon will still produce a valid icon. anmButton->setIcon(Poppler::FormFieldIcon(nullptr)); Poppler::FormFieldIcon anmIcon = anmButton->icon(); QVERIFY(Poppler::FormFieldIconData::getData(anmIcon)); QVERIFY(Poppler::FormFieldIconData::getData(anmIcon)->icon); } void TestForms::testSetPrintable() { std::unique_ptr document = Poppler::Document::load(TESTDATADIR "/unittestcases/form_set_icon.pdf"); QVERIFY(document); std::unique_ptr page = document->page(0); QVERIFY(page); std::vector> forms = page->formFields(); for (std::unique_ptr &field : forms) { field->setPrintable(true); QCOMPARE(field->isPrintable(), true); field->setPrintable(false); QCOMPARE(field->isPrintable(), false); } } void TestForms::testSetAppearanceText() { std::unique_ptr document = Poppler::Document::load(TESTDATADIR "/unittestcases/checkbox_issue_159.pdf"); QVERIFY(document); std::unique_ptr page = document->page(0); QVERIFY(page); std::vector> forms = page->formFields(); int nTextForms = 0; for (std::unique_ptr &field : forms) { if (field->type() != Poppler::FormField::FormText) { continue; } nTextForms++; Poppler::FormFieldText *fft = static_cast(field.get()); const QString textToSet = "HOLA" + fft->name(); fft->setAppearanceText(textToSet); Dict *dict = Poppler::FormFieldData::getFormWidget(fft)->getObj()->getDict(); Object strObject = dict->lookup("AP").dictLookup("N"); QVERIFY(strObject.isStream()); GooString s; strObject.getStream()->fillGooString(&s); const QString textToFind = QStringLiteral("\n(%1) Tj\n").arg(textToSet); QVERIFY(s.toStr().find(textToFind.toStdString()) != std::string::npos); } QCOMPARE(nTextForms, 5); } void TestForms::testUnicodeFieldAttributes() { std::unique_ptr document = Poppler::Document::load(TESTDATADIR "/unittestcases/fieldWithUtf16Names.pdf"); QVERIFY(document); std::unique_ptr page = document->page(0); QVERIFY(page); std::vector> forms = page->formFields(); Poppler::FormField *field = forms.front().get(); QCOMPARE(field->name(), QStringLiteral("Tex")); QCOMPARE(field->uiName(), QStringLiteral("Texto de ayuda")); } QTEST_GUILESS_MAIN(TestForms) #include "check_forms.moc" poppler-24.02.0/qt6/tests/check_goostring.cpp000066400000000000000000000132401455701731300210760ustar00rootroot00000000000000#include #include #include "goo/GooString.h" class TestGooString : public QObject { Q_OBJECT public: explicit TestGooString(QObject *parent = nullptr) : QObject(parent) { } private slots: void testInsertData_data(); void testInsertData(); void testInsert(); void testFormat(); void testFromNullptr(); }; void TestGooString::testInsertData_data() { QTest::addColumn("string"); QTest::addColumn("addition"); QTest::addColumn("position"); QTest::addColumn("result"); QTest::newRow("foo") << QByteArray("foo") << QByteArray("bar") << 0 << QByteArray("barfoo"); QTest::newRow("") << QByteArray() << QByteArray("bar") << 0 << QByteArray("bar"); QTest::newRow("foo+bar #1") << QByteArray("f+bar") << QByteArray("oo") << 1 << QByteArray("foo+bar"); QTest::newRow("foo+bar #2") << QByteArray("fobar") << QByteArray("o+") << 2 << QByteArray("foo+bar"); QTest::newRow("foo+bar #last") << QByteArray("foo+r") << QByteArray("ba") << 4 << QByteArray("foo+bar"); QTest::newRow("foo+bar #end") << QByteArray("foo+") << QByteArray("bar") << 4 << QByteArray("foo+bar"); QTest::newRow("long #start") << QByteArray("very string") << QByteArray("long long long long long ") << 5 << QByteArray("very long long long long long string"); } void TestGooString::testInsertData() { QFETCH(QByteArray, string); QFETCH(QByteArray, addition); QFETCH(int, position); QFETCH(QByteArray, result); GooString goo(string.constData()); QCOMPARE(goo.c_str(), string.constData()); goo.insert(position, addition.constData()); QCOMPARE(goo.c_str(), result.constData()); } void TestGooString::testInsert() { { GooString goo; goo.insert(0, "."); goo.insert(0, "This is a very long long test string"); QCOMPARE(goo.c_str(), "This is a very long long test string."); } { GooString goo; goo.insert(0, "second-part-third-part"); goo.insert(0, "first-part-"); QCOMPARE(goo.c_str(), "first-part-second-part-third-part"); } } void TestGooString::testFormat() { { const std::unique_ptr 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::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::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::unique_ptr goo2(GooString::format("{0:s} {1:t}", "TEST", goo.get())); 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 QByteArray expected = QStringLiteral("%1 %2 %3").arg(UINT_MAX).arg(INT_MAX).arg(INT_MIN).toLatin1(); 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 QByteArray expected = QStringLiteral("%1 %2 %3").arg(ULONG_MAX).arg(LONG_MAX).arg(LONG_MIN).toLatin1(); 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 QByteArray expected = QStringLiteral("%1 %2 %3").arg(ULLONG_MAX).arg(LLONG_MAX).arg(LLONG_MIN).toLatin1(); 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::unique_ptr 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::unique_ptr 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 } }}"); } } void TestGooString::testFromNullptr() { { GooString str { static_cast(nullptr) }; QCOMPARE(str.getLength(), 0); } { GooString str; str.Set(static_cast(nullptr)); QCOMPARE(str.getLength(), 0); } { GooString str { static_cast(nullptr) }; QCOMPARE(str.getLength(), 0); } { GooString str { static_cast(nullptr), 0 }; QCOMPARE(str.getLength(), 0); } { GooString str; str.Set(static_cast(nullptr)); QCOMPARE(str.getLength(), 0); } { GooString str; str.Set(static_cast(nullptr), 0); QCOMPARE(str.getLength(), 0); } } QTEST_GUILESS_MAIN(TestGooString) #include "check_goostring.moc" poppler-24.02.0/qt6/tests/check_internal_outline.cpp000066400000000000000000000340331455701731300224410ustar00rootroot00000000000000#include #include "Outline.h" #include "PDFDoc.h" #include "PDFDocFactory.h" class TestInternalOutline : public QObject { Q_OBJECT public: explicit TestInternalOutline(QObject *parent = nullptr) : QObject(parent) { } private slots: void testCreateOutline(); void testSetOutline(); void testInsertChild(); void testRemoveChild(); void testSetTitleAndSetPageDest(); }; void TestInternalOutline::testCreateOutline() { QTemporaryFile tempFile; QVERIFY(tempFile.open()); tempFile.close(); const std::string tempFileName = tempFile.fileName().toStdString(); const GooString gooTempFileName { tempFileName }; std::unique_ptr doc = PDFDocFactory().createPDFDoc(GooString(TESTDATADIR "/unittestcases/truetype.pdf")); QVERIFY(doc.get()); // ensure the file has no existing outline Outline *outline = doc->getOutline(); QVERIFY(outline != nullptr); auto *outlineItems = outline->getItems(); QVERIFY(outlineItems == nullptr); // create an empty outline and save the file outline->setOutline({}); outlineItems = outline->getItems(); // no items will result in a nullptr rather than a 0 length list QVERIFY(outlineItems == nullptr); doc->saveAs(gooTempFileName); /******************************************************/ doc = PDFDocFactory().createPDFDoc(gooTempFileName); QVERIFY(doc.get()); // ensure the re-opened file has an outline with no items outline = doc->getOutline(); QVERIFY(outline != nullptr); outlineItems = outline->getItems(); QVERIFY(outlineItems == nullptr); } static std::string getTitle(const OutlineItem *item) { const std::vector &u = item->getTitle(); std::string s; for (const auto &c : u) { s.append(1, (char)(c)); } return s; } void TestInternalOutline::testSetOutline() { QTemporaryFile tempFile; QVERIFY(tempFile.open()); tempFile.close(); const std::string tempFileName = tempFile.fileName().toStdString(); const GooString gooTempFileName { tempFileName }; std::unique_ptr doc = PDFDocFactory().createPDFDoc(GooString(TESTDATADIR "/unittestcases/truetype.pdf")); QVERIFY(doc.get()); // ensure the file has no existing outline Outline *outline = doc->getOutline(); QVERIFY(outline != nullptr); auto *outlineItems = outline->getItems(); QVERIFY(outlineItems == nullptr); // create an outline and save the file outline->setOutline( { { "1", 1, { { "1.1", 1, {} }, { "1.2", 2, {} }, { "1.3", 3, { { "1.3.1", 1, {} }, { "1.3.2", 2, {} }, { "1.3.3", 3, {} }, { "1.3.4", 4, {} } } }, { "1.4", 4, {} } } }, { "2", 2, {} }, { "3", 3, {} }, { "4", 4, {} } }); outlineItems = outline->getItems(); QVERIFY(outlineItems != nullptr); doc->saveAs(gooTempFileName); outline = nullptr; /******************************************************/ doc = PDFDocFactory().createPDFDoc(gooTempFileName); QVERIFY(doc.get()); // ensure the re-opened file has an outline outline = doc->getOutline(); QVERIFY(outline != nullptr); outlineItems = outline->getItems(); QVERIFY(outlineItems != nullptr); QVERIFY(outlineItems->size() == 4); OutlineItem *item = outlineItems->at(0); QVERIFY(item != nullptr); // c_str() is used so QCOMPARE prints string correctly on disagree QCOMPARE(getTitle(item).c_str(), "1"); item = outlineItems->at(1); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "2"); item = outlineItems->at(2); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "3"); item = outlineItems->at(3); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "4"); outlineItems = outlineItems->at(0)->getKids(); QVERIFY(outlineItems != nullptr); item = outlineItems->at(0); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "1.1"); item = outlineItems->at(1); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "1.2"); item = outlineItems->at(2); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "1.3"); item = outlineItems->at(3); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "1.4"); outlineItems = outlineItems->at(2)->getKids(); QVERIFY(outlineItems != nullptr); item = outlineItems->at(0); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "1.3.1"); item = outlineItems->at(1); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "1.3.2"); item = outlineItems->at(2); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "1.3.3"); item = outlineItems->at(3); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "1.3.4"); } void TestInternalOutline::testInsertChild() { QTemporaryFile tempFile; QVERIFY(tempFile.open()); tempFile.close(); QTemporaryFile tempFile2; QVERIFY(tempFile2.open()); tempFile2.close(); const std::string tempFileName = tempFile.fileName().toStdString(); const GooString gooTempFileName { tempFileName }; const std::string tempFileName2 = tempFile2.fileName().toStdString(); const GooString gooTempFileName2 { tempFileName2 }; std::unique_ptr doc = PDFDocFactory().createPDFDoc(GooString(TESTDATADIR "/unittestcases/truetype.pdf")); QVERIFY(doc.get()); // ensure the file has no existing outline Outline *outline = doc->getOutline(); QVERIFY(outline != nullptr); auto *outlineItems = outline->getItems(); QVERIFY(outlineItems == nullptr); // create an outline and save the file outline->setOutline({}); doc->saveAs(gooTempFileName); outline = nullptr; /******************************************************/ doc = PDFDocFactory().createPDFDoc(gooTempFileName); QVERIFY(doc.get()); // ensure the re-opened file has an outline with no items outline = doc->getOutline(); QVERIFY(outline != nullptr); // nullptr for 0-length QVERIFY(outline->getItems() == nullptr); // insert first one to empty outline->insertChild("2", 1, 0); // insert at the end outline->insertChild("3", 1, 1); // insert at the start outline->insertChild("1", 1, 0); // add an item to "2" outlineItems = outline->getItems(); QVERIFY(outlineItems != nullptr); QVERIFY(outlineItems->at(1)); outlineItems->at(1)->insertChild("2.1", 2, 0); outlineItems->at(1)->insertChild("2.2", 2, 1); outlineItems->at(1)->insertChild("2.4", 2, 2); outlineItems->at(1)->insertChild("2.3", 2, 2); // save the file doc->saveAs(gooTempFileName2); outline = nullptr; /******************************************************/ doc = PDFDocFactory().createPDFDoc(gooTempFileName2); QVERIFY(doc.get()); // ensure the re-opened file has an outline outline = doc->getOutline(); QVERIFY(outline != nullptr); outlineItems = outline->getItems(); QVERIFY(outlineItems != nullptr); QVERIFY(outlineItems->size() == 3); OutlineItem *item = outlineItems->at(0); QVERIFY(item != nullptr); // c_str() is used so QCOMPARE prints string correctly on disagree QCOMPARE(getTitle(item).c_str(), "1"); item = outlineItems->at(1); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "2"); item = outlineItems->at(2); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "3"); outlineItems = outlineItems->at(1)->getKids(); item = outlineItems->at(0); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "2.1"); item = outlineItems->at(1); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "2.2"); item = outlineItems->at(2); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "2.3"); item = outlineItems->at(3); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "2.4"); } void TestInternalOutline::testRemoveChild() { QTemporaryFile tempFile; QVERIFY(tempFile.open()); tempFile.close(); QTemporaryFile tempFile2; QVERIFY(tempFile2.open()); tempFile2.close(); const std::string tempFileName = tempFile.fileName().toStdString(); const GooString gooTempFileName { tempFileName }; const std::string tempFileName2 = tempFile2.fileName().toStdString(); const GooString gooTempFileName2 { tempFileName2 }; std::unique_ptr doc = PDFDocFactory().createPDFDoc(GooString(TESTDATADIR "/unittestcases/truetype.pdf")); QVERIFY(doc.get()); // ensure the file has no existing outline Outline *outline = doc->getOutline(); QVERIFY(outline != nullptr); auto *outlineItems = outline->getItems(); QVERIFY(outlineItems == nullptr); // create an outline and save the file outline->setOutline({ { "1", 1, { { "1.1", 1, {} }, { "1.2", 2, {} }, { "1.3", 3, { { "1.3.1", 1, {} }, { "1.3.2", 2, {} }, { "1.3.3", 3, {} }, { "1.3.4", 4, {} } } }, { "1.4", 4, {} } } }, { "2", 2, { { "2.1", 1, {} } } }, { "3", 3, { { "3.1", 1, {} }, { "3.2", 2, { { "3.2.1", 1, {} } } } } }, { "4", 4, {} } }); outlineItems = outline->getItems(); QVERIFY(outlineItems != nullptr); doc->saveAs(gooTempFileName); outline = nullptr; /******************************************************/ doc = PDFDocFactory().createPDFDoc(gooTempFileName); QVERIFY(doc.get()); outline = doc->getOutline(); QVERIFY(outline != nullptr); // remove "3" outline->removeChild(2); // remove "1.3.1" outline->getItems()->at(0)->getKids()->at(2)->removeChild(0); // remove "1.3.4" outline->getItems()->at(0)->getKids()->at(2)->removeChild(2); // remove "2.1" outline->getItems()->at(1)->removeChild(0); // save the file doc->saveAs(gooTempFileName2); outline = nullptr; /******************************************************/ doc = PDFDocFactory().createPDFDoc(gooTempFileName2); QVERIFY(doc.get()); // ensure the re-opened file has an outline outline = doc->getOutline(); QVERIFY(outline != nullptr); outlineItems = outline->getItems(); QVERIFY(outlineItems != nullptr); QVERIFY(outlineItems->size() == 3); OutlineItem *item = outlineItems->at(0); QVERIFY(item != nullptr); // c_str() is used so QCOMPARE prints string correctly on disagree QCOMPARE(getTitle(item).c_str(), "1"); item = outlineItems->at(1); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "2"); item = outlineItems->at(2); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "4"); outlineItems = outlineItems->at(0)->getKids(); outlineItems = outlineItems->at(2)->getKids(); item = outlineItems->at(0); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "1.3.2"); item = outlineItems->at(1); QVERIFY(item != nullptr); QCOMPARE(getTitle(item).c_str(), "1.3.3"); // verify "2.1" is removed, lst length 0 is returned as a nullptr QVERIFY(outline->getItems()->at(1)->getKids() == nullptr); } void TestInternalOutline::testSetTitleAndSetPageDest() { QTemporaryFile tempFile; QVERIFY(tempFile.open()); tempFile.close(); QTemporaryFile tempFile2; QVERIFY(tempFile2.open()); tempFile2.close(); const std::string tempFileName = tempFile.fileName().toStdString(); const GooString gooTempFileName { tempFileName }; const std::string tempFileName2 = tempFile2.fileName().toStdString(); const GooString gooTempFileName2 { tempFileName2 }; std::unique_ptr doc = PDFDocFactory().createPDFDoc(GooString(TESTDATADIR "/unittestcases/truetype.pdf")); QVERIFY(doc.get()); // ensure the file has no existing outline Outline *outline = doc->getOutline(); QVERIFY(outline != nullptr); auto *outlineItems = outline->getItems(); QVERIFY(outlineItems == nullptr); // create an outline and save the file outline->setOutline({ { "1", 1, { { "1.1", 1, {} }, { "1.2", 2, {} }, { "1.3", 3, { { "1.3.1", 1, {} }, { "1.3.2", 2, {} }, { "1.3.3", 3, {} }, { "1.3.4", 4, {} } } }, { "1.4", 4, {} } } }, { "2", 2, { { "2.1", 1, {} } } }, { "3", 3, { { "3.1", 1, {} }, { "3.2", 2, { { "3.2.1", 1, {} } } } } }, { "4", 4, {} } }); outlineItems = outline->getItems(); QVERIFY(outlineItems != nullptr); doc->saveAs(gooTempFileName); outline = nullptr; /******************************************************/ doc = PDFDocFactory().createPDFDoc(gooTempFileName); QVERIFY(doc.get()); outline = doc->getOutline(); QVERIFY(outline != nullptr); // change "1.3.1" OutlineItem *item = outline->getItems()->at(0)->getKids()->at(2)->getKids()->at(0); QCOMPARE(getTitle(item).c_str(), "1.3.1"); item->setTitle("Changed to a different title"); item = outline->getItems()->at(2); { const LinkAction *action = item->getAction(); QVERIFY(action->getKind() == actionGoTo); const LinkGoTo *gotoAction = dynamic_cast(action); const LinkDest *dest = gotoAction->getDest(); QVERIFY(dest->isPageRef() == false); QCOMPARE(dest->getPageNum(), 3); item->setPageDest(1); } // save the file doc->saveAs(gooTempFileName2); outline = nullptr; item = nullptr; /******************************************************/ doc = PDFDocFactory().createPDFDoc(gooTempFileName2); QVERIFY(doc.get()); outline = doc->getOutline(); QVERIFY(outline != nullptr); item = outline->getItems()->at(0)->getKids()->at(2)->getKids()->at(0); QCOMPARE(getTitle(item).c_str(), "Changed to a different title"); { item = outline->getItems()->at(2); const LinkAction *action = item->getAction(); QVERIFY(action->getKind() == actionGoTo); const LinkGoTo *gotoAction = dynamic_cast(action); const LinkDest *dest = gotoAction->getDest(); QVERIFY(dest->isPageRef() == false); QCOMPARE(dest->getPageNum(), 1); } } QTEST_GUILESS_MAIN(TestInternalOutline) #include "check_internal_outline.moc" poppler-24.02.0/qt6/tests/check_lexer.cpp000066400000000000000000000055031455701731300202050ustar00rootroot00000000000000#include #include "Object.h" #include "Lexer.h" class TestLexer : public QObject { Q_OBJECT public: explicit TestLexer(QObject *parent = nullptr) : QObject(parent) { } private slots: void testNumbers(); }; void TestLexer::testNumbers() { char data[] = "0 1 -1 2147483647 -2147483647 2147483648 -2147483648 4294967297 -2147483649 0.1 1.1 -1.1 2147483647.1 -2147483647.1 2147483648.1 -2147483648.1 4294967297.1 -2147483649.1 9223372036854775807 18446744073709551615"; MemStream *stream = new MemStream(data, 0, strlen(data), Object(objNull)); Lexer *lexer = new Lexer(nullptr, stream); QVERIFY(lexer); Object obj; obj = lexer->getObj(); QCOMPARE(obj.getType(), objInt); QCOMPARE(obj.getInt(), 0); obj = lexer->getObj(); QCOMPARE(obj.getType(), objInt); QCOMPARE(obj.getInt(), 1); obj = lexer->getObj(); QCOMPARE(obj.getType(), objInt); QCOMPARE(obj.getInt(), -1); obj = lexer->getObj(); QCOMPARE(obj.getType(), objInt); QCOMPARE(obj.getInt(), 2147483647); obj = lexer->getObj(); QCOMPARE(obj.getType(), objInt); QCOMPARE(obj.getInt(), -2147483647); obj = lexer->getObj(); QCOMPARE(obj.getType(), objInt64); QCOMPARE(obj.getInt64(), 2147483648ll); obj = lexer->getObj(); QCOMPARE(obj.getType(), objInt); QCOMPARE(obj.getInt(), -2147483647 - 1); obj = lexer->getObj(); QCOMPARE(obj.getType(), objInt64); QCOMPARE(obj.getInt64(), 4294967297ll); obj = lexer->getObj(); QCOMPARE(obj.getType(), objInt64); QCOMPARE(obj.getInt64(), -2147483649ll); obj = lexer->getObj(); QCOMPARE(obj.getType(), objReal); QCOMPARE(obj.getReal(), 0.1); obj = lexer->getObj(); QCOMPARE(obj.getType(), objReal); QCOMPARE(obj.getReal(), 1.1); obj = lexer->getObj(); QCOMPARE(obj.getType(), objReal); QCOMPARE(obj.getReal(), -1.1); obj = lexer->getObj(); QCOMPARE(obj.getType(), objReal); QCOMPARE(obj.getReal(), 2147483647.1); obj = lexer->getObj(); QCOMPARE(obj.getType(), objReal); QCOMPARE(obj.getReal(), -2147483647.1); obj = lexer->getObj(); QCOMPARE(obj.getType(), objReal); QCOMPARE(obj.getReal(), 2147483648.1); obj = lexer->getObj(); QCOMPARE(obj.getType(), objReal); QCOMPARE(obj.getReal(), -2147483648.1); obj = lexer->getObj(); QCOMPARE(obj.getType(), objReal); QCOMPARE(obj.getReal(), 4294967297.1); obj = lexer->getObj(); QCOMPARE(obj.getType(), objReal); QCOMPARE(obj.getReal(), -2147483649.1); obj = lexer->getObj(); QCOMPARE(obj.getType(), objInt64); QCOMPARE(obj.getInt64(), 9223372036854775807ll); obj = lexer->getObj(); QCOMPARE(obj.getType(), objReal); QCOMPARE(obj.getReal(), 18446744073709551616.); delete lexer; } QTEST_GUILESS_MAIN(TestLexer) #include "check_lexer.moc" poppler-24.02.0/qt6/tests/check_links.cpp000066400000000000000000000071041455701731300202050ustar00rootroot00000000000000#include #include #include class TestLinks : public QObject { Q_OBJECT public: explicit TestLinks(QObject *parent = nullptr) : QObject(parent) { } private slots: void checkDocumentWithNoDests(); void checkDests_xr01(); void checkDests_xr02(); void checkDocumentURILink(); }; static bool isDestinationValid_pageNumber(const Poppler::LinkDestination *dest, const Poppler::Document *doc) { return dest->pageNumber() > 0 && dest->pageNumber() <= doc->numPages(); } static bool isDestinationValid_name(const Poppler::LinkDestination *dest) { return !dest->destinationName().isEmpty(); } void TestLinks::checkDocumentWithNoDests() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/WithAttachments.pdf"); QVERIFY(doc); std::unique_ptr dest = doc->linkDestination(QStringLiteral("no.dests.in.this.document")); QVERIFY(!isDestinationValid_pageNumber(dest.get(), doc.get())); QVERIFY(isDestinationValid_name(dest.get())); } void TestLinks::checkDests_xr01() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/xr01.pdf"); QVERIFY(doc); std::unique_ptr page = doc->page(0); QVERIFY(page); std::vector> links = page->links(); QCOMPARE(links.size(), 2); { QCOMPARE(links.at(0)->linkType(), Poppler::Link::Goto); Poppler::LinkGoto *link = static_cast(links.at(0).get()); const Poppler::LinkDestination dest = link->destination(); QVERIFY(!isDestinationValid_pageNumber(&dest, doc.get())); QVERIFY(isDestinationValid_name(&dest)); QCOMPARE(dest.destinationName(), QLatin1String("section.1")); } { QCOMPARE(links.at(1)->linkType(), Poppler::Link::Goto); Poppler::LinkGoto *link = static_cast(links.at(1).get()); const Poppler::LinkDestination dest = link->destination(); QVERIFY(!isDestinationValid_pageNumber(&dest, doc.get())); QVERIFY(isDestinationValid_name(&dest)); QCOMPARE(dest.destinationName(), QLatin1String("section.2")); } } void TestLinks::checkDests_xr02() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/xr02.pdf"); QVERIFY(doc); std::unique_ptr dest = doc->linkDestination(QStringLiteral("section.1")); QVERIFY(isDestinationValid_pageNumber(dest.get(), doc.get())); QVERIFY(!isDestinationValid_name(dest.get())); dest = doc->linkDestination(QStringLiteral("section.2")); QVERIFY(isDestinationValid_pageNumber(dest.get(), doc.get())); QVERIFY(!isDestinationValid_name(dest.get())); dest = doc->linkDestination(QStringLiteral("section.3")); QVERIFY(!isDestinationValid_pageNumber(dest.get(), doc.get())); QVERIFY(isDestinationValid_name(dest.get())); } void TestLinks::checkDocumentURILink() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/checkbox_issue_159.pdf"); QVERIFY(doc); std::unique_ptr page = doc->page(0); QVERIFY(page); std::vector> links = page->links(); QCOMPARE(links.size(), 1); QCOMPARE(links.at(0)->linkType(), Poppler::Link::Browse); Poppler::LinkBrowse *link = static_cast(links.at(0).get()); QCOMPARE(link->url(), QLatin1String("http://www.tcpdf.org")); } QTEST_GUILESS_MAIN(TestLinks) #include "check_links.moc" poppler-24.02.0/qt6/tests/check_metadata.cpp000066400000000000000000000166621455701731300206560ustar00rootroot00000000000000#include #include class TestMetaData : public QObject { Q_OBJECT public: explicit TestMetaData(QObject *parent = nullptr) : QObject(parent) { } private slots: void checkStrings_data(); void checkStrings(); void checkStrings2_data(); void checkStrings2(); void checkStringKeys(); void checkLinearised(); void checkNumPages(); void checkDate(); void checkPageSize(); void checkPortraitOrientation(); void checkLandscapeOrientation(); void checkUpsideDownOrientation(); void checkSeascapeOrientation(); void checkVersion(); void checkPdfId(); void checkNoPdfId(); }; void TestMetaData::checkStrings_data() { QTest::addColumn("key"); QTest::addColumn("value"); QTest::newRow("Author") << "Author" << "Brad Hards"; QTest::newRow("Title") << "Title" << "Two pages"; QTest::newRow("Subject") << "Subject" << "A two page layout for poppler testing"; QTest::newRow("Keywords") << "Keywords" << "Qt4 bindings"; QTest::newRow("Creator") << "Creator" << "iText: cgpdftops CUPS filter"; QTest::newRow("Producer") << "Producer" << "Acrobat Distiller 7.0 for Macintosh"; } void TestMetaData::checkStrings() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/doublepage.pdf"); QVERIFY(doc); QFETCH(QString, key); QFETCH(QString, value); QCOMPARE(doc->info(key), value); } void TestMetaData::checkStrings2_data() { QTest::addColumn("key"); QTest::addColumn("value"); QTest::newRow("Title") << "Title" << "Malaga hotels"; QTest::newRow("Author") << "Author" << "Brad Hards"; QTest::newRow("Creator") << "Creator" << "Safari: cgpdftops CUPS filter"; QTest::newRow("Producer") << "Producer" << "Acrobat Distiller 7.0 for Macintosh"; QTest::newRow("Keywords") << "Keywords" << "First\rSecond\rthird"; QTest::newRow("Custom1") << "Custom1" << "CustomValue1"; QTest::newRow("Custom2") << "Custom2" << "CustomValue2"; } void TestMetaData::checkStrings2() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/truetype.pdf"); QVERIFY(doc); QFETCH(QString, key); QFETCH(QString, value); QCOMPARE(doc->info(key), value); } void TestMetaData::checkStringKeys() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/truetype.pdf"); QVERIFY(doc); QStringList keyList; keyList << QStringLiteral("Title") << QStringLiteral("Author") << QStringLiteral("Creator") << QStringLiteral("Keywords") << QStringLiteral("CreationDate"); keyList << QStringLiteral("Producer") << QStringLiteral("ModDate") << QStringLiteral("Custom1") << QStringLiteral("Custom2"); keyList.sort(); QStringList keysInDoc = doc->infoKeys(); keysInDoc.sort(); QCOMPARE(keysInDoc, keyList); } void TestMetaData::checkLinearised() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/orientation.pdf"); QVERIFY(doc); QVERIFY(doc->isLinearized()); doc = Poppler::Document::load(TESTDATADIR "/unittestcases/truetype.pdf"); QVERIFY(doc); QCOMPARE(doc->isLinearized(), false); } void TestMetaData::checkPortraitOrientation() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/orientation.pdf"); QVERIFY(doc); std::unique_ptr page = doc->page(0); QCOMPARE(page->orientation(), Poppler::Page::Portrait); } void TestMetaData::checkNumPages() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/doublepage.pdf"); QVERIFY(doc); QCOMPARE(doc->numPages(), 2); doc = Poppler::Document::load(TESTDATADIR "/unittestcases/truetype.pdf"); QVERIFY(doc); QCOMPARE(doc->numPages(), 1); } void TestMetaData::checkDate() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/truetype.pdf"); QVERIFY(doc); QCOMPARE(doc->date(QStringLiteral("ModDate")), QDateTime(QDate(2005, 12, 5), QTime(9, 44, 46), Qt::UTC)); QCOMPARE(doc->date(QStringLiteral("CreationDate")), QDateTime(QDate(2005, 8, 13), QTime(1, 12, 11), Qt::UTC)); } void TestMetaData::checkPageSize() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/truetype.pdf"); QVERIFY(doc); std::unique_ptr page = doc->page(0); QCOMPARE(page->pageSize(), QSize(595, 842)); QCOMPARE(page->pageSizeF(), QSizeF(595.22, 842)); } void TestMetaData::checkLandscapeOrientation() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/orientation.pdf"); QVERIFY(doc); std::unique_ptr page = doc->page(1); QCOMPARE(page->orientation(), Poppler::Page::Landscape); } void TestMetaData::checkUpsideDownOrientation() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/orientation.pdf"); QVERIFY(doc); std::unique_ptr page = doc->page(2); QCOMPARE(page->orientation(), Poppler::Page::UpsideDown); } void TestMetaData::checkSeascapeOrientation() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/orientation.pdf"); QVERIFY(doc); std::unique_ptr page = doc->page(3); QCOMPARE(page->orientation(), Poppler::Page::Seascape); } void TestMetaData::checkVersion() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/doublepage.pdf"); QVERIFY(doc); auto pdfVersion = doc->getPdfVersion(); QCOMPARE(pdfVersion.major, 1); QCOMPARE(pdfVersion.minor, 6); } void TestMetaData::checkPdfId() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/A6EmbeddedFiles.pdf"); QVERIFY(doc); const QByteArray referencePermanentId("00C9D5B6D8FB11D7A902003065D630AA"); const QByteArray referenceUpdateId("39AECAE6D8FB11D7A902003065D630AA"); { // no IDs wanted, just existance check QVERIFY(doc->getPdfId(nullptr, nullptr)); } { // only permanent ID QByteArray permanentId; QVERIFY(doc->getPdfId(&permanentId, nullptr)); QCOMPARE(permanentId.toUpper(), referencePermanentId); } { // only update ID QByteArray updateId; QVERIFY(doc->getPdfId(nullptr, &updateId)); QCOMPARE(updateId.toUpper(), referenceUpdateId); } { // both IDs QByteArray permanentId; QByteArray updateId; QVERIFY(doc->getPdfId(&permanentId, &updateId)); QCOMPARE(permanentId.toUpper(), referencePermanentId); QCOMPARE(updateId.toUpper(), referenceUpdateId); } } void TestMetaData::checkNoPdfId() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/WithActualText.pdf"); QVERIFY(doc); QVERIFY(!doc->getPdfId(nullptr, nullptr)); } QTEST_GUILESS_MAIN(TestMetaData) #include "check_metadata.moc" poppler-24.02.0/qt6/tests/check_object.cpp000066400000000000000000000013111455701731300203250ustar00rootroot00000000000000#include #include #include "poppler/Object.h" class TestObject : public QObject { Q_OBJECT public: explicit TestObject(QObject *parent = nullptr) : QObject(parent) { } private slots: void benchDefaultConstructor(); void benchMoveConstructor(); void benchSetToNull(); }; void TestObject::benchDefaultConstructor() { QBENCHMARK { Object obj; } } void TestObject::benchMoveConstructor() { QBENCHMARK { Object src; Object dst { std::move(src) }; } } void TestObject::benchSetToNull() { Object obj; QBENCHMARK { obj.setToNull(); } } QTEST_GUILESS_MAIN(TestObject) #include "check_object.moc" poppler-24.02.0/qt6/tests/check_optcontent.cpp000066400000000000000000000424211455701731300212630ustar00rootroot00000000000000#include #include "PDFDoc.h" #include "GlobalParams.h" #include #include class TestOptionalContent : public QObject { Q_OBJECT public: explicit TestOptionalContent(QObject *parent = nullptr) : QObject(parent) { } private slots: void checkVisPolicy(); void checkNestedLayers(); void checkNoOptionalContent(); void checkIsVisible(); void checkVisibilitySetting(); void checkRadioButtons(); }; void TestOptionalContent::checkVisPolicy() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/vis_policy_test.pdf"); QVERIFY(doc); QVERIFY(doc->hasOptionalContent()); Poppler::OptContentModel *optContent = doc->optionalContentModel(); QModelIndex index; index = optContent->index(0, 0, QModelIndex()); QCOMPARE(optContent->data(index, Qt::DisplayRole).toString(), QLatin1String("A")); QCOMPARE(static_cast(optContent->data(index, Qt::CheckStateRole).toInt()), Qt::Checked); index = optContent->index(1, 0, QModelIndex()); QCOMPARE(optContent->data(index, Qt::DisplayRole).toString(), QLatin1String("B")); QCOMPARE(static_cast(optContent->data(index, Qt::CheckStateRole).toInt()), Qt::Checked); } void TestOptionalContent::checkNestedLayers() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/NestedLayers.pdf"); QVERIFY(doc); QVERIFY(doc->hasOptionalContent()); Poppler::OptContentModel *optContent = doc->optionalContentModel(); QModelIndex index; index = optContent->index(0, 0, QModelIndex()); QCOMPARE(optContent->data(index, Qt::DisplayRole).toString(), QLatin1String("Black Text and Green Snow")); QCOMPARE(static_cast(optContent->data(index, Qt::CheckStateRole).toInt()), Qt::Unchecked); index = optContent->index(1, 0, QModelIndex()); QCOMPARE(optContent->data(index, Qt::DisplayRole).toString(), QLatin1String("Mountains and Image")); QCOMPARE(static_cast(optContent->data(index, Qt::CheckStateRole).toInt()), Qt::Checked); // This is a sub-item of "Mountains and Image" QModelIndex subindex = optContent->index(0, 0, index); QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("Image")); QCOMPARE(static_cast(optContent->data(index, Qt::CheckStateRole).toInt()), Qt::Checked); index = optContent->index(2, 0, QModelIndex()); QCOMPARE(optContent->data(index, Qt::DisplayRole).toString(), QLatin1String("Starburst")); QCOMPARE(static_cast(optContent->data(index, Qt::CheckStateRole).toInt()), Qt::Checked); index = optContent->index(3, 0, QModelIndex()); QCOMPARE(optContent->data(index, Qt::DisplayRole).toString(), QLatin1String("Watermark")); QCOMPARE(static_cast(optContent->data(index, Qt::CheckStateRole).toInt()), Qt::Unchecked); } void TestOptionalContent::checkNoOptionalContent() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/orientation.pdf"); QVERIFY(doc); QCOMPARE(doc->hasOptionalContent(), false); } void TestOptionalContent::checkIsVisible() { globalParams = std::make_unique(); PDFDoc *doc = new PDFDoc(std::make_unique(TESTDATADIR "/unittestcases/vis_policy_test.pdf")); QVERIFY(doc); OCGs *ocgs = doc->getOptContentConfig(); QVERIFY(ocgs); XRef *xref = doc->getXRef(); Object obj; // In this test, both Ref(21,0) and Ref(2,0) are set to On // AnyOn, one element array: // 22 0 obj<>endobj obj = xref->fetch(22, 0); QVERIFY(obj.isDict()); QVERIFY(ocgs->optContentIsVisible(&obj)); // Same again, looking for any leaks or dubious free()'s obj = xref->fetch(22, 0); QVERIFY(obj.isDict()); QVERIFY(ocgs->optContentIsVisible(&obj)); // AnyOff, one element array: // 29 0 obj<>endobj obj = xref->fetch(29, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // AllOn, one element array: // 36 0 obj<>endobj obj = xref->fetch(36, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // AllOff, one element array: // 43 0 obj<>endobj obj = xref->fetch(43, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // AnyOn, multi-element array: // 50 0 obj<>endobj obj = xref->fetch(50, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // AnyOff, multi-element array: // 57 0 obj<>endobj obj = xref->fetch(57, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // AllOn, multi-element array: // 64 0 obj<>endobj obj = xref->fetch(64, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // AllOff, multi-element array: // 71 0 obj<>endobj obj = xref->fetch(71, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); delete doc; globalParams.reset(); } void TestOptionalContent::checkVisibilitySetting() { globalParams = std::make_unique(); PDFDoc *doc = new PDFDoc(std::make_unique(TESTDATADIR "/unittestcases/vis_policy_test.pdf")); QVERIFY(doc); OCGs *ocgs = doc->getOptContentConfig(); QVERIFY(ocgs); XRef *xref = doc->getXRef(); Object obj; // In this test, both Ref(21,0) and Ref(28,0) start On, // based on the file settings Object ref21obj(Ref { 21, 0 }); Ref ref21 = ref21obj.getRef(); OptionalContentGroup *ocgA = ocgs->findOcgByRef(ref21); QVERIFY(ocgA); QVERIFY((ocgA->getName()->cmp("A")) == 0); QCOMPARE(ocgA->getState(), OptionalContentGroup::On); Object ref28obj(Ref { 28, 0 }); Ref ref28 = ref28obj.getRef(); OptionalContentGroup *ocgB = ocgs->findOcgByRef(ref28); QVERIFY(ocgB); QVERIFY((ocgB->getName()->cmp("B")) == 0); QCOMPARE(ocgB->getState(), OptionalContentGroup::On); // turn one Off ocgA->setState(OptionalContentGroup::Off); // AnyOn, one element array: // 22 0 obj<>endobj obj = xref->fetch(22, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // Same again, looking for any leaks or dubious free()'s obj = xref->fetch(22, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // AnyOff, one element array: // 29 0 obj<>endobj obj = xref->fetch(29, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // AllOn, one element array: // 36 0 obj<>endobj obj = xref->fetch(36, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // AllOff, one element array: // 43 0 obj<>endobj obj = xref->fetch(43, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // AnyOn, multi-element array: // 50 0 obj<>endobj obj = xref->fetch(50, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // AnyOff, multi-element array: // 57 0 obj<>endobj obj = xref->fetch(57, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // AllOn, multi-element array: // 64 0 obj<>endobj obj = xref->fetch(64, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // AllOff, multi-element array: // 71 0 obj<>endobj obj = xref->fetch(71, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // Turn the other one off as well (i.e. both are Off) ocgB->setState(OptionalContentGroup::Off); // AnyOn, one element array: // 22 0 obj<>endobj obj = xref->fetch(22, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // Same again, looking for any leaks or dubious free()'s obj = xref->fetch(22, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // AnyOff, one element array: // 29 0 obj<>endobj obj = xref->fetch(29, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // AllOn, one element array: // 36 0 obj<>endobj obj = xref->fetch(36, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // AllOff, one element array: // 43 0 obj<>endobj obj = xref->fetch(43, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // AnyOn, multi-element array: // 50 0 obj<>endobj obj = xref->fetch(50, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // AnyOff, multi-element array: // 57 0 obj<>endobj obj = xref->fetch(57, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // AllOn, multi-element array: // 64 0 obj<>endobj obj = xref->fetch(64, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // AllOff, multi-element array: // 71 0 obj<>endobj obj = xref->fetch(71, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // Turn the first one on again (21 is On, 28 is Off) ocgA->setState(OptionalContentGroup::On); // AnyOn, one element array: // 22 0 obj<>endobj obj = xref->fetch(22, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // Same again, looking for any leaks or dubious free()'s obj = xref->fetch(22, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // AnyOff, one element array: // 29 0 obj<>endobj obj = xref->fetch(29, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // AllOn, one element array: // 36 0 obj<>endobj obj = xref->fetch(36, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // AllOff, one element array: // 43 0 obj<>endobj obj = xref->fetch(43, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // AnyOn, multi-element array: // 50 0 obj<>endobj obj = xref->fetch(50, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // AnyOff, multi-element array: // 57 0 obj<>endobj obj = xref->fetch(57, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), true); // AllOn, multi-element array: // 64 0 obj<>endobj obj = xref->fetch(64, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); // AllOff, multi-element array: // 71 0 obj<>endobj obj = xref->fetch(71, 0); QVERIFY(obj.isDict()); QCOMPARE(ocgs->optContentIsVisible(&obj), false); delete doc; globalParams.reset(); } void TestOptionalContent::checkRadioButtons() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/ClarityOCGs.pdf"); QVERIFY(doc); QVERIFY(doc->hasOptionalContent()); Poppler::OptContentModel *optContent = doc->optionalContentModel(); QModelIndex index; index = optContent->index(0, 0, QModelIndex()); QCOMPARE(optContent->data(index, Qt::DisplayRole).toString(), QLatin1String("Languages")); QCOMPARE(static_cast(optContent->data(index, Qt::CheckStateRole).toInt()), Qt::Unchecked); // These are sub-items of the "Languages" label QModelIndex subindex = optContent->index(0, 0, index); QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("English")); QCOMPARE(static_cast(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Checked); subindex = optContent->index(1, 0, index); QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("French")); QCOMPARE(static_cast(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Unchecked); subindex = optContent->index(2, 0, index); QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("Japanese")); QCOMPARE(static_cast(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Unchecked); // RBGroup of languages, so turning on Japanese should turn off English QVERIFY(optContent->setData(subindex, QVariant(true), Qt::CheckStateRole)); subindex = optContent->index(0, 0, index); QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("English")); QCOMPARE(static_cast(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Unchecked); QCOMPARE(static_cast(subindex.internalPointer())->group()->getState(), OptionalContentGroup::Off); subindex = optContent->index(2, 0, index); QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("Japanese")); QCOMPARE(static_cast(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Checked); QCOMPARE(static_cast(subindex.internalPointer())->group()->getState(), OptionalContentGroup::On); subindex = optContent->index(1, 0, index); QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("French")); QCOMPARE(static_cast(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Unchecked); QCOMPARE(static_cast(subindex.internalPointer())->group()->getState(), OptionalContentGroup::Off); // and turning on French should turn off Japanese QVERIFY(optContent->setData(subindex, QVariant(true), Qt::CheckStateRole)); subindex = optContent->index(0, 0, index); QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("English")); QCOMPARE(static_cast(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Unchecked); QCOMPARE(static_cast(subindex.internalPointer())->group()->getState(), OptionalContentGroup::Off); subindex = optContent->index(2, 0, index); QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("Japanese")); QCOMPARE(static_cast(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Unchecked); QCOMPARE(static_cast(subindex.internalPointer())->group()->getState(), OptionalContentGroup::Off); subindex = optContent->index(1, 0, index); QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("French")); QCOMPARE(static_cast(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Checked); QCOMPARE(static_cast(subindex.internalPointer())->group()->getState(), OptionalContentGroup::On); // and turning off French should leave them all off QVERIFY(optContent->setData(subindex, QVariant(false), Qt::CheckStateRole)); subindex = optContent->index(0, 0, index); QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("English")); QCOMPARE(static_cast(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Unchecked); QCOMPARE(static_cast(subindex.internalPointer())->group()->getState(), OptionalContentGroup::Off); subindex = optContent->index(2, 0, index); QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("Japanese")); QCOMPARE(static_cast(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Unchecked); QCOMPARE(static_cast(subindex.internalPointer())->group()->getState(), OptionalContentGroup::Off); subindex = optContent->index(1, 0, index); QCOMPARE(optContent->data(subindex, Qt::DisplayRole).toString(), QLatin1String("French")); QCOMPARE(static_cast(optContent->data(subindex, Qt::CheckStateRole).toInt()), Qt::Unchecked); QCOMPARE(static_cast(subindex.internalPointer())->group()->getState(), OptionalContentGroup::Off); } QTEST_GUILESS_MAIN(TestOptionalContent) #include "check_optcontent.moc" poppler-24.02.0/qt6/tests/check_outline.cpp000066400000000000000000000026051455701731300205450ustar00rootroot00000000000000#include #include #include class TestOutline : public QObject { Q_OBJECT public: explicit TestOutline(QObject *parent = nullptr) : QObject(parent) { } private slots: void checkOutline_xr02(); }; void TestOutline::checkOutline_xr02() { std::unique_ptr document { Poppler::Document::load(TESTDATADIR "/unittestcases/xr02.pdf") }; QVERIFY(document.get()); const auto outline = document->outline(); QCOMPARE(outline.size(), 2); const auto &foo = outline[0]; QVERIFY(!foo.isNull()); QCOMPARE(foo.name(), QStringLiteral("foo")); QCOMPARE(foo.isOpen(), false); const auto fooDest = foo.destination(); QVERIFY(!fooDest.isNull()); QCOMPARE(fooDest->pageNumber(), 1); QVERIFY(foo.externalFileName().isEmpty()); QVERIFY(foo.uri().isEmpty()); QVERIFY(!foo.hasChildren()); QVERIFY(foo.children().isEmpty()); const auto &bar = outline[1]; QVERIFY(!bar.isNull()); QCOMPARE(bar.name(), QStringLiteral("bar")); QCOMPARE(bar.isOpen(), false); const auto barDest = bar.destination(); QVERIFY(!barDest.isNull()); QCOMPARE(barDest->pageNumber(), 2); QVERIFY(bar.externalFileName().isEmpty()); QVERIFY(bar.uri().isEmpty()); QVERIFY(!bar.hasChildren()); QVERIFY(bar.children().isEmpty()); } QTEST_GUILESS_MAIN(TestOutline) #include "check_outline.moc" poppler-24.02.0/qt6/tests/check_overprint.cpp000066400000000000000000000017651455701731300211240ustar00rootroot00000000000000#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" poppler-24.02.0/qt6/tests/check_pagelabelinfo.cpp000066400000000000000000000027141455701731300216570ustar00rootroot00000000000000#include #include #include "PageLabelInfo_p.h" #include "config.h" class TestPageLabelInfo : public QObject { Q_OBJECT public: explicit TestPageLabelInfo(QObject *parent = nullptr) : QObject(parent) { } private slots: void testFromDecimal(); void testFromDecimalUnicode(); void testToRoman(); void testFromRoman(); void testToLatin(); void testFromLatin(); }; void TestPageLabelInfo::testFromDecimal() { std::string str { "2342" }; const auto res = fromDecimal(str, false); QCOMPARE(res.first, 2342); QCOMPARE(res.second, true); } void TestPageLabelInfo::testFromDecimalUnicode() { std::unique_ptr str(Poppler::QStringToUnicodeGooString(QString::fromLocal8Bit("2342"))); const auto res = fromDecimal(str->toStr(), str->hasUnicodeMarker()); QCOMPARE(res.first, 2342); QCOMPARE(res.second, true); } void TestPageLabelInfo::testToRoman() { GooString str; toRoman(177, &str, false); QCOMPARE(str.c_str(), "clxxvii"); } void TestPageLabelInfo::testFromRoman() { GooString roman("clxxvii"); QCOMPARE(fromRoman(roman.c_str()), 177); } void TestPageLabelInfo::testToLatin() { GooString str; toLatin(54, &str, false); QCOMPARE(str.c_str(), "bbb"); } void TestPageLabelInfo::testFromLatin() { GooString latin("ddd"); QCOMPARE(fromLatin(latin.c_str()), 56); } QTEST_GUILESS_MAIN(TestPageLabelInfo) #include "check_pagelabelinfo.moc" poppler-24.02.0/qt6/tests/check_pagelayout.cpp000066400000000000000000000020371455701731300212370ustar00rootroot00000000000000#include #include class TestPageLayout : public QObject { Q_OBJECT public: explicit TestPageLayout(QObject *parent = nullptr) : QObject(parent) { } private slots: void checkNone(); void checkSingle(); void checkFacing(); }; void TestPageLayout::checkNone() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/UseNone.pdf"); QVERIFY(doc); QCOMPARE(doc->pageLayout(), Poppler::Document::NoLayout); } void TestPageLayout::checkSingle() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/FullScreen.pdf"); QVERIFY(doc); QCOMPARE(doc->pageLayout(), Poppler::Document::SinglePage); } void TestPageLayout::checkFacing() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/doublepage.pdf"); QVERIFY(doc); QCOMPARE(doc->pageLayout(), Poppler::Document::TwoPageRight); } QTEST_GUILESS_MAIN(TestPageLayout) #include "check_pagelayout.moc" poppler-24.02.0/qt6/tests/check_pagemode.cpp000066400000000000000000000030151455701731300206430ustar00rootroot00000000000000#include #include class TestPageMode : public QObject { Q_OBJECT public: explicit TestPageMode(QObject *parent = nullptr) : QObject(parent) { } private slots: void checkNone(); void checkFullScreen(); void checkAttachments(); void checkThumbs(); void checkOC(); }; void TestPageMode::checkNone() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/UseNone.pdf"); QVERIFY(doc); QCOMPARE(doc->pageMode(), Poppler::Document::UseNone); } void TestPageMode::checkFullScreen() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/FullScreen.pdf"); QVERIFY(doc); QCOMPARE(doc->pageMode(), Poppler::Document::FullScreen); } void TestPageMode::checkAttachments() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/UseAttachments.pdf"); QVERIFY(doc); QCOMPARE(doc->pageMode(), Poppler::Document::UseAttach); } void TestPageMode::checkThumbs() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/UseThumbs.pdf"); QVERIFY(doc); QCOMPARE(doc->pageMode(), Poppler::Document::UseThumbs); } void TestPageMode::checkOC() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/UseOC.pdf"); QVERIFY(doc); QCOMPARE(doc->pageMode(), Poppler::Document::UseOC); } QTEST_GUILESS_MAIN(TestPageMode) #include "check_pagemode.moc" poppler-24.02.0/qt6/tests/check_password.cpp000066400000000000000000000060171455701731300207310ustar00rootroot00000000000000#include #include class TestPassword : public QObject { Q_OBJECT public: explicit TestPassword(QObject *parent = nullptr) : QObject(parent) { } private slots: void password1(); void password1a(); void password2(); void password2a(); void password2b(); void password3(); void password4(); void password4b(); }; // BUG:4557 void TestPassword::password1() { std::unique_ptr doc = Poppler::Document::load(QString::fromUtf8(TESTDATADIR "/unittestcases/Gday garçon - open.pdf"), "", QString::fromUtf8("garçon").toLatin1()); // clazy:exclude=qstring-allocations QVERIFY(doc); QVERIFY(!doc->isLocked()); } void TestPassword::password1a() { std::unique_ptr doc = Poppler::Document::load(QString::fromUtf8(TESTDATADIR "/unittestcases/Gday garçon - open.pdf")); // clazy:exclude=qstring-allocations QVERIFY(doc); QVERIFY(doc->isLocked()); QVERIFY(!doc->unlock("", QString::fromUtf8("garçon").toLatin1())); // clazy:exclude=qstring-allocations QVERIFY(!doc->isLocked()); } void TestPassword::password2() { std::unique_ptr doc = Poppler::Document::load(QString::fromUtf8(TESTDATADIR "/unittestcases/Gday garçon - owner.pdf"), QString::fromUtf8("garçon").toLatin1(), ""); // clazy:exclude=qstring-allocations QVERIFY(doc); QVERIFY(!doc->isLocked()); } void TestPassword::password2a() { std::unique_ptr doc = Poppler::Document::load(QString::fromUtf8(TESTDATADIR "/unittestcases/Gday garçon - owner.pdf"), QString::fromUtf8("garçon").toLatin1()); // clazy:exclude=qstring-allocations QVERIFY(doc); QVERIFY(!doc->isLocked()); } void TestPassword::password2b() { std::unique_ptr doc = Poppler::Document::load(QString::fromUtf8(TESTDATADIR "/unittestcases/Gday garçon - owner.pdf")); QVERIFY(doc); QVERIFY(!doc->isLocked()); QVERIFY(!doc->unlock(QString::fromUtf8("garçon").toLatin1(), "")); // clazy:exclude=qstring-allocations QVERIFY(!doc->isLocked()); } void TestPassword::password3() { std::unique_ptr doc = Poppler::Document::load(QString::fromUtf8(TESTDATADIR "/unittestcases/PasswordEncrypted.pdf")); QVERIFY(doc); QVERIFY(doc->isLocked()); QVERIFY(!doc->unlock("", "password")); QVERIFY(!doc->isLocked()); } // issue 690 void TestPassword::password4() { std::unique_ptr doc = Poppler::Document::load(QString::fromUtf8(TESTDATADIR "/unittestcases/encrypted-256.pdf")); QVERIFY(doc); QVERIFY(doc->isLocked()); QVERIFY(!doc->unlock("owner-secret", "")); QVERIFY(!doc->isLocked()); } // issue 690 void TestPassword::password4b() { std::unique_ptr doc = Poppler::Document::load(QString::fromUtf8(TESTDATADIR "/unittestcases/encrypted-256.pdf")); QVERIFY(doc); QVERIFY(doc->isLocked()); QVERIFY(!doc->unlock("", "user-secret")); QVERIFY(!doc->isLocked()); } QTEST_GUILESS_MAIN(TestPassword) #include "check_password.moc" poppler-24.02.0/qt6/tests/check_permissions.cpp000066400000000000000000000020551455701731300214400ustar00rootroot00000000000000#include #include class TestPermissions : public QObject { Q_OBJECT public: explicit TestPermissions(QObject *parent = nullptr) : QObject(parent) { } private slots: void permissions1(); }; void TestPermissions::permissions1() { std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/orientation.pdf"); QVERIFY(doc); // we are allowed to print QVERIFY(doc->okToPrint()); // we are not allowed to change QVERIFY(!(doc->okToChange())); // we are not allowed to copy or extract content QVERIFY(!(doc->okToCopy())); // we are not allowed to print at high resolution QVERIFY(!(doc->okToPrintHighRes())); // we are not allowed to fill forms QVERIFY(!(doc->okToFillForm())); // we are allowed to extract content for accessibility QVERIFY(doc->okToExtractForAccessibility()); // we are allowed to assemble this document QVERIFY(doc->okToAssemble()); } QTEST_GUILESS_MAIN(TestPermissions) #include "check_permissions.moc" poppler-24.02.0/qt6/tests/check_search.cpp000066400000000000000000000604571455701731300203440ustar00rootroot00000000000000#include #include class TestSearch : public QObject { Q_OBJECT public: explicit TestSearch(QObject *parent = nullptr) : QObject(parent) { } private slots: void testAcrossLinesSearch(); // leave it first void testAcrossLinesSearchDoubleColumn(); void bug7063(); void testNextAndPrevious(); void testWholeWordsOnly(); void testIgnoreDiacritics(); void testRussianSearch(); // Issue #743 void testDeseretSearch(); // Issue #853 }; void TestSearch::bug7063() { std::unique_ptr document = Poppler::Document::load(TESTDATADIR "/unittestcases/bug7063.pdf"); QVERIFY(document); std::unique_ptr page = document->page(0); QVERIFY(page); double rectLeft = 0.0, rectTop = 0.0, rectRight = page->pageSizeF().width(), rectBottom = page->pageSizeF().height(); QCOMPARE(page->search(QStringLiteral("non-ascii:"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true); QCOMPARE(page->search(QStringLiteral("Ascii"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), false); QCOMPARE(page->search(QStringLiteral("Ascii"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop, Poppler::Page::IgnoreCase), true); QCOMPARE(page->search(QStringLiteral("latin1:"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), false); QCOMPARE(page->search(QString::fromUtf8("é"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true); // clazy:exclude=qstring-allocations QCOMPARE(page->search(QString::fromUtf8("à"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true); // clazy:exclude=qstring-allocations QCOMPARE(page->search(QString::fromUtf8("ç"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true); // clazy:exclude=qstring-allocations QCOMPARE(page->search(QString::fromUtf8("search \"é\", \"à\" or \"ç\""), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true); // clazy:exclude=qstring-allocations QCOMPARE(page->search(QString::fromUtf8("¥µ©"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true); // clazy:exclude=qstring-allocations QCOMPARE(page->search(QString::fromUtf8("¥©"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), false); // clazy:exclude=qstring-allocations QCOMPARE(page->search(QStringLiteral("non-ascii:"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true); QCOMPARE(page->search(QStringLiteral("Ascii"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), false); QCOMPARE(page->search(QStringLiteral("Ascii"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop, Poppler::Page::IgnoreCase), true); QCOMPARE(page->search(QStringLiteral("latin1:"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), false); QCOMPARE(page->search(QString::fromUtf8("é"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true); // clazy:exclude=qstring-allocations QCOMPARE(page->search(QString::fromUtf8("à"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true); // clazy:exclude=qstring-allocations QCOMPARE(page->search(QString::fromUtf8("ç"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true); // clazy:exclude=qstring-allocations QCOMPARE(page->search(QString::fromUtf8("search \"é\", \"à\" or \"ç\""), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true); // clazy:exclude=qstring-allocations QCOMPARE(page->search(QString::fromUtf8("¥µ©"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true); // clazy:exclude=qstring-allocations QCOMPARE(page->search(QString::fromUtf8("¥©"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), false); // clazy:exclude=qstring-allocations } void TestSearch::testNextAndPrevious() { std::unique_ptr document = Poppler::Document::load(TESTDATADIR "/unittestcases/xr01.pdf"); QVERIFY(document); std::unique_ptr page = document->page(0); QVERIFY(page); double rectLeft = 0.0, rectTop = 0.0, rectRight = page->pageSizeF().width(), rectBottom = page->pageSizeF().height(); QCOMPARE(page->search(QStringLiteral("is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true); QVERIFY(qAbs(rectLeft - 161.44) < 0.01); QVERIFY(qAbs(rectTop - 127.85) < 0.01); QVERIFY(qAbs(rectRight - rectLeft - 6.70) < 0.01); QVERIFY(qAbs(rectBottom - rectTop - 8.85) < 0.01); QCOMPARE(page->search(QStringLiteral("is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::NextResult), true); QVERIFY(qAbs(rectLeft - 171.46) < 0.01); QVERIFY(qAbs(rectTop - 127.85) < 0.01); QVERIFY(qAbs(rectRight - rectLeft - 6.70) < 0.01); QVERIFY(qAbs(rectBottom - rectTop - 8.85) < 0.01); QCOMPARE(page->search(QStringLiteral("is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::NextResult), true); QVERIFY(qAbs(rectLeft - 161.44) < 0.01); QVERIFY(qAbs(rectTop - 139.81) < 0.01); QVERIFY(qAbs(rectRight - rectLeft - 6.70) < 0.01); QVERIFY(qAbs(rectBottom - rectTop - 8.85) < 0.01); QCOMPARE(page->search(QStringLiteral("is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::NextResult), true); QVERIFY(qAbs(rectLeft - 171.46) < 0.01); QVERIFY(qAbs(rectTop - 139.81) < 0.01); QVERIFY(qAbs(rectRight - rectLeft - 6.70) < 0.01); QVERIFY(qAbs(rectBottom - rectTop - 8.85) < 0.01); QCOMPARE(page->search(QStringLiteral("is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::NextResult), false); QCOMPARE(page->search(QStringLiteral("is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::PreviousResult), true); QVERIFY(qAbs(rectLeft - 161.44) < 0.01); QVERIFY(qAbs(rectTop - 139.81) < 0.01); QVERIFY(qAbs(rectRight - rectLeft - 6.70) < 0.01); QVERIFY(qAbs(rectBottom - rectTop - 8.85) < 0.01); QCOMPARE(page->search(QStringLiteral("is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::PreviousResult), true); QVERIFY(qAbs(rectLeft - 171.46) < 0.01); QVERIFY(qAbs(rectTop - 127.85) < 0.01); QVERIFY(qAbs(rectRight - rectLeft - 6.70) < 0.01); QVERIFY(qAbs(rectBottom - rectTop - 8.85) < 0.01); QCOMPARE(page->search(QStringLiteral("is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::PreviousResult), true); QVERIFY(qAbs(rectLeft - 161.44) < 0.01); QVERIFY(qAbs(rectTop - 127.85) < 0.01); QVERIFY(qAbs(rectRight - rectLeft - 6.70) < 0.01); QVERIFY(qAbs(rectBottom - rectTop - 8.85) < 0.01); QCOMPARE(page->search(QStringLiteral("is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::PreviousResult), false); rectLeft = 0.0, rectTop = 0.0, rectRight = page->pageSizeF().width(), rectBottom = page->pageSizeF().height(); QCOMPARE(page->search(QStringLiteral("is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true); QVERIFY(qAbs(rectLeft - 161.44) < 0.01); QVERIFY(qAbs(rectTop - 127.85) < 0.01); QVERIFY(qAbs(rectRight - rectLeft - 6.70) < 0.01); QVERIFY(qAbs(rectBottom - rectTop - 8.85) < 0.01); QCOMPARE(page->search(QStringLiteral("is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::NextResult), true); QVERIFY(qAbs(rectLeft - 171.46) < 0.01); QVERIFY(qAbs(rectTop - 127.85) < 0.01); QVERIFY(qAbs(rectRight - rectLeft - 6.70) < 0.01); QVERIFY(qAbs(rectBottom - rectTop - 8.85) < 0.01); QCOMPARE(page->search(QStringLiteral("is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::NextResult), true); QVERIFY(qAbs(rectLeft - 161.44) < 0.01); QVERIFY(qAbs(rectTop - 139.81) < 0.01); QVERIFY(qAbs(rectRight - rectLeft - 6.70) < 0.01); QVERIFY(qAbs(rectBottom - rectTop - 8.85) < 0.01); QCOMPARE(page->search(QStringLiteral("is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::NextResult), true); QVERIFY(qAbs(rectLeft - 171.46) < 0.01); QVERIFY(qAbs(rectTop - 139.81) < 0.01); QVERIFY(qAbs(rectRight - rectLeft - 6.70) < 0.01); QVERIFY(qAbs(rectBottom - rectTop - 8.85) < 0.01); QCOMPARE(page->search(QStringLiteral("is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::NextResult), false); QCOMPARE(page->search(QStringLiteral("is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::PreviousResult), true); QVERIFY(qAbs(rectLeft - 161.44) < 0.01); QVERIFY(qAbs(rectTop - 139.81) < 0.01); QVERIFY(qAbs(rectRight - rectLeft - 6.70) < 0.01); QVERIFY(qAbs(rectBottom - rectTop - 8.85) < 0.01); QCOMPARE(page->search(QStringLiteral("is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::PreviousResult), true); QVERIFY(qAbs(rectLeft - 171.46) < 0.01); QVERIFY(qAbs(rectTop - 127.85) < 0.01); QVERIFY(qAbs(rectRight - rectLeft - 6.70) < 0.01); QVERIFY(qAbs(rectBottom - rectTop - 8.85) < 0.01); QCOMPARE(page->search(QStringLiteral("is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::PreviousResult), true); QVERIFY(qAbs(rectLeft - 161.44) < 0.01); QVERIFY(qAbs(rectTop - 127.85) < 0.01); QVERIFY(qAbs(rectRight - rectLeft - 6.70) < 0.01); QVERIFY(qAbs(rectBottom - rectTop - 8.85) < 0.01); QCOMPARE(page->search(QStringLiteral("is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::PreviousResult), false); } void TestSearch::testWholeWordsOnly() { std::unique_ptr document = Poppler::Document::load(TESTDATADIR "/unittestcases/WithActualText.pdf"); QVERIFY(document); std::unique_ptr page = document->page(0); QVERIFY(page); const Poppler::Page::SearchDirection direction = Poppler::Page::FromTop; const Poppler::Page::SearchFlags mode0; const Poppler::Page::SearchFlags mode1 = Poppler::Page::IgnoreCase; const Poppler::Page::SearchFlags mode2 = Poppler::Page::WholeWords; const Poppler::Page::SearchFlags mode3 = Poppler::Page::IgnoreCase | Poppler::Page::WholeWords; double left, top, right, bottom; QCOMPARE(page->search(QStringLiteral("brown"), left, top, right, bottom, direction, mode0), true); QCOMPARE(page->search(QStringLiteral("brOwn"), left, top, right, bottom, direction, mode0), false); QCOMPARE(page->search(QStringLiteral("brOwn"), left, top, right, bottom, direction, mode1), true); QCOMPARE(page->search(QStringLiteral("brawn"), left, top, right, bottom, direction, mode1), false); QCOMPARE(page->search(QStringLiteral("brown"), left, top, right, bottom, direction, mode2), true); QCOMPARE(page->search(QStringLiteral("own"), left, top, right, bottom, direction, mode2), false); QCOMPARE(page->search(QStringLiteral("brOwn"), left, top, right, bottom, direction, mode3), true); QCOMPARE(page->search(QStringLiteral("Own"), left, top, right, bottom, direction, mode3), false); } void TestSearch::testIgnoreDiacritics() { std::unique_ptr document = Poppler::Document::load(TESTDATADIR "/unittestcases/Issue637.pdf"); QVERIFY(document); std::unique_ptr page = document->page(0); QVERIFY(page); const Poppler::Page::SearchDirection direction = Poppler::Page::FromTop; const Poppler::Page::SearchFlags mode0; const Poppler::Page::SearchFlags mode1 = Poppler::Page::IgnoreDiacritics; const Poppler::Page::SearchFlags mode2 = Poppler::Page::IgnoreDiacritics | Poppler::Page::IgnoreCase; const Poppler::Page::SearchFlags mode3 = Poppler::Page::IgnoreDiacritics | Poppler::Page::IgnoreCase | Poppler::Page::WholeWords; const Poppler::Page::SearchFlags mode4 = Poppler::Page::IgnoreCase | Poppler::Page::WholeWords; double left, top, right, bottom; // Test pdf (Issue637.pdf) just contains the following three lines: // La cigüeña voló sobre nuestras cabezas. // La cigogne a survolé nos têtes. // Der Storch flog über unsere Köpfe hinweg. QCOMPARE(page->search(QString(), left, top, right, bottom, direction, mode0), false); QCOMPARE(page->search(QStringLiteral("ciguena"), left, top, right, bottom, direction, mode0), false); QCOMPARE(page->search(QStringLiteral("Ciguena"), left, top, right, bottom, direction, mode1), false); QCOMPARE(page->search(QStringLiteral("ciguena"), left, top, right, bottom, direction, mode1), true); QCOMPARE(page->search(QString::fromUtf8("cigüeña"), left, top, right, bottom, direction, mode1), true); // clazy:exclude=qstring-allocations QCOMPARE(page->search(QString::fromUtf8("cigüena"), left, top, right, bottom, direction, mode1), false); // clazy:exclude=qstring-allocations QCOMPARE(page->search(QString::fromUtf8("Cigüeña"), left, top, right, bottom, direction, mode1), false); // clazy:exclude=qstring-allocations QCOMPARE(page->search(QStringLiteral("Ciguena"), left, top, right, bottom, direction, mode2), true); QCOMPARE(page->search(QStringLiteral("ciguena"), left, top, right, bottom, direction, mode2), true); QCOMPARE(page->search(QStringLiteral("Ciguena"), left, top, right, bottom, direction, mode3), true); QCOMPARE(page->search(QStringLiteral("ciguena"), left, top, right, bottom, direction, mode3), true); QCOMPARE(page->search(QString::fromUtf8("cigüeña"), left, top, right, bottom, direction, mode4), true); // clazy:exclude=qstring-allocations QCOMPARE(page->search(QString::fromUtf8("Cigüeña"), left, top, right, bottom, direction, mode4), true); // clazy:exclude=qstring-allocations QCOMPARE(page->search(QString::fromUtf8("cigüena"), left, top, right, bottom, direction, mode4), false); // clazy:exclude=qstring-allocations QCOMPARE(page->search(QStringLiteral("Ciguena"), left, top, right, bottom, direction, mode4), false); QCOMPARE(page->search(QStringLiteral("kopfe"), left, top, right, bottom, direction, mode2), true); QCOMPARE(page->search(QStringLiteral("kopfe"), left, top, right, bottom, direction, mode3), true); QCOMPARE(page->search(QStringLiteral("uber"), left, top, right, bottom, direction, mode0), false); QCOMPARE(page->search(QStringLiteral("uber"), left, top, right, bottom, direction, mode1), true); QCOMPARE(page->search(QStringLiteral("uber"), left, top, right, bottom, direction, mode2), true); QCOMPARE(page->search(QStringLiteral("uber"), left, top, right, bottom, direction, mode3), true); QCOMPARE(page->search(QStringLiteral("vole"), left, top, right, bottom, direction, mode2), true); QCOMPARE(page->search(QStringLiteral("vole"), left, top, right, bottom, direction, mode3), false); QCOMPARE(page->search(QStringLiteral("survole"), left, top, right, bottom, direction, mode3), true); QCOMPARE(page->search(QStringLiteral("tete"), left, top, right, bottom, direction, mode3), false); QCOMPARE(page->search(QStringLiteral("tete"), left, top, right, bottom, direction, mode2), true); QCOMPARE(page->search(QStringLiteral("La Ciguena Volo"), left, top, right, bottom, direction, mode2), true); QCOMPARE(page->search(QStringLiteral("Survole Nos Tetes"), left, top, right, bottom, direction, mode2), true); QCOMPARE(page->search(QStringLiteral("Uber Unsere Kopfe"), left, top, right, bottom, direction, mode2), true); } void TestSearch::testRussianSearch() { // Test for issue #743 std::unique_ptr document = Poppler::Document::load(TESTDATADIR "/unittestcases/russian.pdf"); QVERIFY(document); std::unique_ptr page = document->page(0); QVERIFY(page); const Poppler::Page::SearchDirection direction = Poppler::Page::FromTop; const Poppler::Page::SearchFlags mode0 = Poppler::Page::NoSearchFlags; const Poppler::Page::SearchFlags mode1 = Poppler::Page::IgnoreDiacritics; const Poppler::Page::SearchFlags mode2 = Poppler::Page::IgnoreDiacritics | Poppler::Page::IgnoreCase; const Poppler::Page::SearchFlags mode0W = mode0 | Poppler::Page::WholeWords; const Poppler::Page::SearchFlags mode1W = mode1 | Poppler::Page::WholeWords; const Poppler::Page::SearchFlags mode2W = mode2 | Poppler::Page::WholeWords; double l, t, r, b; // left, top, right, bottom // In the searched page 5, these two words do exist: простой and Простой const QString str = QString::fromUtf8("простой"); // clazy:exclude=qstring-allocations QCOMPARE(page->search(str, l, t, r, b, direction, mode0), true); QCOMPARE(page->search(str, l, t, r, b, direction, mode1), true); QCOMPARE(page->search(str, l, t, r, b, direction, mode2), true); QCOMPARE(page->search(str, l, t, r, b, direction, mode0W), true); QCOMPARE(page->search(str, l, t, r, b, direction, mode1W), true); QCOMPARE(page->search(str, l, t, r, b, direction, mode2W), true); } void TestSearch::testDeseretSearch() { std::unique_ptr document = Poppler::Document::load(TESTDATADIR "/unittestcases/deseret.pdf"); QVERIFY(document); std::unique_ptr page = document->page(0); QVERIFY(page); double l, t, r, b; // left, top, right, bottom const QString str = QString::fromUtf8("𐐐𐐯𐑊𐐬"); // clazy:exclude=qstring-allocations QCOMPARE(page->search(str, l, t, r, b, Poppler::Page::FromTop, Poppler::Page::NoSearchFlags), true); const QString str2 = QString::fromUtf8("𐐸𐐯𐑊𐐬"); // clazy:exclude=qstring-allocations QCOMPARE(page->search(str2, l, t, r, b, Poppler::Page::FromTop, Poppler::Page::IgnoreCase), true); } void TestSearch::testAcrossLinesSearch() { // Test for searching across lines with new flag Poppler::Page::AcrossLines // and its automatic features like ignoring hyphen at end of line or allowing // whitespace in the search term to match on newline character. std::unique_ptr document = Poppler::Document::load(TESTDATADIR "/unittestcases/searchAcrossLines.pdf"); QVERIFY(document); std::unique_ptr page = document->page(1); QVERIFY(page); const Poppler::Page::SearchDirection direction = Poppler::Page::FromTop; const Poppler::Page::SearchFlags empty = Poppler::Page::NoSearchFlags; const Poppler::Page::SearchFlags mode0 = Poppler::Page::AcrossLines; const Poppler::Page::SearchFlags mode1 = Poppler::Page::AcrossLines | Poppler::Page::IgnoreDiacritics; const Poppler::Page::SearchFlags mode2 = Poppler::Page::AcrossLines | Poppler::Page::IgnoreDiacritics | Poppler::Page::IgnoreCase; const Poppler::Page::SearchFlags mode2W = mode2 | Poppler::Page::WholeWords; double l, t, r, b; // left, top, right, bottom // In the searched page, each of "re-conocimiento" "PRUE-BA" "imáge-nes" happen split across lines const QString str1 = QString::fromUtf8("reconocimiento"); // clazy:exclude=qstring-allocations const QString str2 = QString::fromUtf8("IMagenes"); // clazy:exclude=qstring-allocations // Test it cannot be found with empty search flags QCOMPARE(page->search(str1, l, t, r, b, direction, empty), false); // Test it is found with AcrossLines option QCOMPARE(page->search(str1, l, t, r, b, direction, mode0), true); // Test AcrossLines with IgnoreDiacritics and IgnoreCase options QCOMPARE(page->search(str2, l, t, r, b, direction, mode0), false); QCOMPARE(page->search(str2, l, t, r, b, direction, mode1), false); QCOMPARE(page->search(str2, l, t, r, b, direction, mode2), true); // Test with WholeWords too QCOMPARE(page->search(str2, l, t, r, b, direction, mode2W), true); // Now test that AcrossLines also allows whitespace in the search term to match on newline char. // In the searched page, "podrá" ends a line and "acordar" starts the next line, so we // now test we match it with "podrá acordar" const QString str3 = QString::fromUtf8("podrá acordar,"); // clazy:exclude=qstring-allocations QCOMPARE(page->search(str3, l, t, r, b, direction, mode0), true); QCOMPARE(page->search(str3, l, t, r, b, direction, mode1), true); QCOMPARE(page->search(str3, l, t, r, b, direction, mode2), true); QCOMPARE(page->search(str3, l, t, r, b, direction, mode2W), true); // now test it also works with IgnoreDiacritics and IgnoreCase const QString str4 = QString::fromUtf8("PODRA acordar"); // clazy:exclude=qstring-allocations QCOMPARE(page->search(str4, l, t, r, b, direction, mode0), false); QCOMPARE(page->search(str4, l, t, r, b, direction, mode1), false); QCOMPARE(page->search(str4, l, t, r, b, direction, mode2), true); QCOMPARE(page->search(str4, l, t, r, b, direction, mode2W), false); // false as it lacks ending comma // Now test that when a hyphen char in the search term matches a hyphen at end of line, // then we don't automatically ignore it, but treat it as a normal char. // In the searched page, "CC BY-NC-SA 4.0" is split across two lines on the second hyphen const QString str5 = QString::fromUtf8("CC BY-NC-SA 4.0"); // clazy:exclude=qstring-allocations std::unique_ptr page0 = document->page(0); QVERIFY(page0); QCOMPARE(page0->search(str5, l, t, r, b, direction, mode0), true); QCOMPARE(page0->search(str5, l, t, r, b, direction, mode1), true); QCOMPARE(page0->search(str5, l, t, r, b, direction, mode2), true); QCOMPARE(page0->search(str5, l, t, r, b, direction, mode2W), true); QCOMPARE(page0->search(QString::fromUtf8("NC-SA"), l, t, r, b, direction, mode2W), false); // clazy:exclude=qstring-allocations // Searching for "CC BY-NCSA 4.0" should also match, because hyphen is now ignored at end of line const QString str6 = QString::fromUtf8("CC BY-NCSA 4.0"); // clazy:exclude=qstring-allocations QCOMPARE(page0->search(str6, l, t, r, b, direction, mode0), true); 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); // 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 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); QCOMPARE(page->search(full2lines, l, t, r, b, direction, mode2W), true); // And now the full text of two lines split by a hyphenated word const QString full2linesHyphenated = QString::fromUtf8("Consiste básicamente en información digitalizada, codificados y alojados en un elemento contenedor digital (equipos, dispositivos periféricos, unidades de memoria, unidades " "virtualizadas, tramas"); // clazy:exclude=qstring-allocations QCOMPARE(page->search(full2linesHyphenated, l, t, r, b, direction, mode0), true); QCOMPARE(page->search(full2linesHyphenated, l, t, r, b, direction, mode1), true); QCOMPARE(page->search(full2linesHyphenated, l, t, r, b, direction, mode2), true); QCOMPARE(page->search(full2linesHyphenated, l, t, r, b, direction, mode2W), true); // BUG about false positives at start of a line. const QString bug_str = QString::fromUtf8("nes y"); // clazy:exclude=qstring-allocations // there's only 1 match, check for that QCOMPARE(page->search(bug_str, mode2).size(), 1); } void TestSearch::testAcrossLinesSearchDoubleColumn() { // Test for searching across lines with new flag Poppler::Page::AcrossLines // in a document with two columns of text. std::unique_ptr document = Poppler::Document::load(TESTDATADIR "/unittestcases/searchAcrossLinesDoubleColumn.pdf"); QVERIFY(document); std::unique_ptr page = document->page(0); QVERIFY(page); const Poppler::Page::SearchFlags mode = Poppler::Page::AcrossLines | Poppler::Page::IgnoreDiacritics | Poppler::Page::IgnoreCase; // Test for a bug in double column documents where single line matches are // wrongly returned as being multiline matches. const QString bug_str = QString::fromUtf8("betw"); // clazy:exclude=qstring-allocations // there's only 3 matches for 'betw' in document, where only the last // one is a multiline match, so that's a total of 4 rects returned QCOMPARE(page->search(bug_str, mode).size(), 4); } QTEST_GUILESS_MAIN(TestSearch) #include "check_search.moc" poppler-24.02.0/qt6/tests/check_signature_basics.cpp000066400000000000000000000153651455701731300224220ustar00rootroot00000000000000//======================================================================== // // check_signature_basics.cpp // // This file is licensed under the GPLv2 or later // // Copyright 2023 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) { } private: 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(); }; 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::find(availableBackends.begin(), availableBackends.end(), 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::find(availableBackends.begin(), availableBackends.end(), 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()); QVERIFY(signatureFields[1]->getSignature()); QVERIFY(!signatureFields[2]->getSignature()); QVERIFY(!signatureFields[3]->getSignature()); } 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()->getLength(), 10230); // Signature data size is 2340 QCOMPARE(signatureFields[1]->getSignature()->getLength(), 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(), ETSI_CAdES_detached); auto siginfo0 = signatureFields[0]->validateSignature(false, false, -1 /* now */, false, false); #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(), ETSI_CAdES_detached); auto siginfo1 = signatureFields[1]->validateSignature(false, false, -1 /* now */, false, false); #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(), unsigned_signature_field); QCOMPARE(signatureFields[3]->getCreateWidget()->getField()->getFullyQualifiedName()->toStr(), std::string { "P2.AnA_Signature3_B_" }); QCOMPARE(signatureFields[3]->getSignatureType(), 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" poppler-24.02.0/qt6/tests/check_strings.cpp000066400000000000000000000172201455701731300205560ustar00rootroot00000000000000/* * Copyright (C) 2010, 2011, Pino Toscano * Copyright (C) 2021 Klarälvdalens Datakonsult AB, a KDAB Group company, * * 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 #include Q_DECLARE_METATYPE(GooString *) Q_DECLARE_METATYPE(Unicode *) class TestStrings : public QObject { Q_OBJECT public: explicit TestStrings(QObject *parent = nullptr) : QObject(parent) { } private slots: void initTestCase(); void cleanupTestCase(); void check_unicodeToQString_data(); void check_unicodeToQString(); void check_UnicodeParsedString_data(); void check_UnicodeParsedString(); void check_QStringToUnicodeGooString_data(); 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() { qRegisterMetaType("GooString*"); qRegisterMetaType("Unicode*"); globalParams = std::make_unique(); } void TestStrings::cleanupTestCase() { qDeleteAll(m_gooStrings); globalParams.reset(); } void TestStrings::check_unicodeToQString_data() { QTest::addColumn("data"); QTest::addColumn("length"); QTest::addColumn("result"); { const int l = 1; Unicode *u = new Unicode[l]; u[0] = int('a'); QTest::newRow("a") << u << l << QStringLiteral("a"); } { const int l = 1; Unicode *u = new Unicode[l]; u[0] = 0x0161; QTest::newRow("\u0161") << u << l << QStringLiteral("\u0161"); } { const int l = 2; Unicode *u = new Unicode[l]; u[0] = int('a'); u[1] = int('b'); QTest::newRow("ab") << u << l << QStringLiteral("ab"); } { const int l = 2; Unicode *u = new Unicode[l]; u[0] = int('a'); u[1] = 0x0161; QTest::newRow("a\u0161") << u << l << QStringLiteral("a\u0161"); } { const int l = 2; Unicode *u = new Unicode[l]; u[0] = 0x5c01; u[1] = 0x9762; QTest::newRow("\xe5\xb0\x81\xe9\x9d\xa2") << u << l << QStringLiteral("封面"); } { const int l = 3; Unicode *u = new Unicode[l]; u[0] = 0x5c01; u[1] = 0x9762; u[2] = 0x0; QTest::newRow("\xe5\xb0\x81\xe9\x9d\xa2 + 0") << u << l << QStringLiteral("封面"); } { const int l = 4; Unicode *u = new Unicode[l]; u[0] = 0x5c01; u[1] = 0x9762; u[2] = 0x0; u[3] = 0x0; QTest::newRow("\xe5\xb0\x81\xe9\x9d\xa2 + two 0") << u << l << QStringLiteral("封面"); } } void TestStrings::check_unicodeToQString() { QFETCH(Unicode *, data); QFETCH(int, length); QFETCH(QString, result); QCOMPARE(Poppler::unicodeToQString(data, length), result); delete[] data; } void TestStrings::check_UnicodeParsedString_data() { QTest::addColumn("string"); QTest::addColumn("result"); // non-unicode strings QTest::newRow("") << newGooString("") << QString(); QTest::newRow("a") << newGooString("a") << QStringLiteral("a"); QTest::newRow("ab") << newGooString("ab") << QStringLiteral("ab"); QTest::newRow("~") << newGooString("~") << QStringLiteral("~"); QTest::newRow("test string") << newGooString("test string") << QStringLiteral("test string"); // unicode strings QTest::newRow("") << newGooString("\xFE\xFF") << QString(); QTest::newRow("U a") << newGooString("\xFE\xFF\0a", 4) << QStringLiteral("a"); QTest::newRow("U ~") << newGooString("\xFE\xFF\0~", 4) << QStringLiteral("~"); QTest::newRow("U aa") << newGooString("\xFE\xFF\0a\0a", 6) << QStringLiteral("aa"); QTest::newRow("U \xC3\x9F") << newGooString("\xFE\xFF\0\xDF", 4) << QStringLiteral("ß"); QTest::newRow("U \xC3\x9F\x61") << newGooString("\xFE\xFF\0\xDF\0\x61", 6) << QStringLiteral("ßa"); QTest::newRow("U \xC5\xA1") << newGooString("\xFE\xFF\x01\x61", 4) << QStringLiteral("š"); QTest::newRow("U \xC5\xA1\x61") << newGooString("\xFE\xFF\x01\x61\0\x61", 6) << QStringLiteral("ša"); QTest::newRow("test string") << newGooString("\xFE\xFF\0t\0e\0s\0t\0 \0s\0t\0r\0i\0n\0g", 24) << QStringLiteral("test string"); QTest::newRow("UTF16-LE") << newGooString("\xFF\xFE\xDA\x00\x6E\x00\xEE\x00\x63\x00\xF6\x00\x64\x00\xE9\x00\x51\x75", 18) << QStringLiteral("Únîcödé畑"); } void TestStrings::check_UnicodeParsedString() { QFETCH(GooString *, string); QFETCH(QString, result); QCOMPARE(Poppler::UnicodeParsedString(string), result); } void TestStrings::check_QStringToUnicodeGooString_data() { QTest::addColumn("string"); QTest::addColumn("result"); QTest::newRow("") << QString() << QByteArray(""); QTest::newRow("") << QString(QLatin1String("")) << QByteArray(""); QTest::newRow("a") << QStringLiteral("a") << QByteArray("\0a", 2); QTest::newRow("ab") << QStringLiteral("ab") << QByteArray("\0a\0b", 4); QTest::newRow("test string") << QStringLiteral("test string") << QByteArray("\0t\0e\0s\0t\0 \0s\0t\0r\0i\0n\0g", 22); QTest::newRow("\xC3\x9F") << QStringLiteral("ß") << QByteArray("\0\xDF", 2); QTest::newRow("\xC3\x9F\x61") << QStringLiteral("ßa") << QByteArray("\0\xDF\0\x61", 4); } void TestStrings::check_QStringToUnicodeGooString() { QFETCH(QString, string); QFETCH(QByteArray, result); GooString *goo = Poppler::QStringToUnicodeGooString(string); if (string.isEmpty()) { QVERIFY(goo->toStr().empty()); QCOMPARE(goo->getLength(), 0); } else { QVERIFY(goo->hasUnicodeMarker()); 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() { QTest::addColumn("string"); QTest::addColumn("result"); QTest::newRow("") << QString() << newGooString(""); QTest::newRow("") << QString(QLatin1String("")) << newGooString(""); QTest::newRow("a") << QStringLiteral("a") << newGooString("a"); QTest::newRow("ab") << QStringLiteral("ab") << newGooString("ab"); } void TestStrings::check_QStringToGooString() { QFETCH(QString, string); QFETCH(GooString *, result); GooString *goo = Poppler::QStringToGooString(string); QCOMPARE(goo->c_str(), result->c_str()); delete goo; } GooString *TestStrings::newGooString(const char *s) { GooString *goo = new GooString(s); m_gooStrings.append(goo); return goo; } GooString *TestStrings::newGooString(const char *s, int l) { GooString *goo = new GooString(s, l); m_gooStrings.append(goo); return goo; } QTEST_GUILESS_MAIN(TestStrings) #include "check_strings.moc" poppler-24.02.0/qt6/tests/check_stroke_opacity.cpp000066400000000000000000000066331455701731300221320ustar00rootroot00000000000000#include #include #include #include #include // Unit tests for rendering axial shadings without full opacity class TestStrokeOpacity : public QObject { Q_OBJECT public: explicit TestStrokeOpacity(QObject *parent = nullptr) : QObject(parent) { } private slots: void checkStrokeOpacity_data(); void checkStrokeOpacity(); }; void TestStrokeOpacity::checkStrokeOpacity_data() { QTest::addColumn("backendType"); QTest::newRow("splash") << (int)Poppler::Document::SplashBackend; QTest::newRow("qpainter") << (int)Poppler::Document::QPainterBackend; } void TestStrokeOpacity::checkStrokeOpacity() { QFETCH(int, backendType); std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/stroke-alpha-pattern.pdf"); QVERIFY(doc != nullptr); doc->setRenderBackend((Poppler::Document::RenderBackend)backendType); // BUG: For some reason splash gets the opacity wrong when antialiasing is switched off if (backendType == (int)Poppler::Document::SplashBackend) { doc->setRenderHint(Poppler::Document::Antialiasing, true); } const auto page = std::unique_ptr(doc->page(0)); QVERIFY(page != nullptr); // Render (at low resolution and with cropped marging) QImage image = page->renderToImage(36, 36, 40, 50, 200, 230); // The actual tests start here // Allow a tolerance. int tolerance; auto approximatelyEqual = [&tolerance](QRgb c0, const QColor &c1) { return std::abs(qAlpha(c0) - c1.alpha()) <= tolerance && std::abs(qRed(c0) - c1.red()) <= tolerance && std::abs(qGreen(c0) - c1.green()) <= tolerance && std::abs(qBlue(c0) - c1.blue()) <= tolerance; }; // At the lower left of the test document is a square with an axial shading, // which should be rendered with opacity 0.25. // Check that with a sample pixel auto pixel = image.pixel(70, 160); // Splash and QPainter backends implement shadings slightly differently, // hence we cannot expect to get precisely the same colors. tolerance = 2; QVERIFY(approximatelyEqual(pixel, QColor(253, 233, 196, 255))); // At the upper left of the test document is a stroked square with an axial shading. // This is implemented by filling a clip region defined by a stroke outline. // Check whether the backend really only renders the stroke, not the region // surrounded by the stroke. auto pixelUpperLeftInterior = image.pixel(70, 70); tolerance = 0; QVERIFY(approximatelyEqual(pixelUpperLeftInterior, Qt::white)); // Now check whether that stroke is semi-transparent. // Bug https://gitlab.freedesktop.org/poppler/poppler/-/issues/178 auto pixelUpperLeftOnStroke = image.pixel(70, 20); tolerance = 2; QVERIFY(approximatelyEqual(pixelUpperLeftOnStroke, QColor(253, 233, 196, 255))); // At the upper right there is a semi-transparent stroked red square // a) Make sure that the color is correct. auto pixelUpperRightOnStroke = image.pixel(130, 20); tolerance = 0; QVERIFY(approximatelyEqual(pixelUpperRightOnStroke, QColor(246, 196, 206, 255))); // b) Make sure that it is really stroked, not filled auto pixelUpperRightInterior = image.pixel(130, 50); QVERIFY(approximatelyEqual(pixelUpperRightInterior, Qt::white)); } QTEST_GUILESS_MAIN(TestStrokeOpacity) #include "check_stroke_opacity.moc" poppler-24.02.0/qt6/tests/check_utf8document.cpp000066400000000000000000000037471455701731300215230ustar00rootroot00000000000000#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)), 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" poppler-24.02.0/qt6/tests/check_utf_conversion.cpp000066400000000000000000000143351455701731300221340ustar00rootroot00000000000000#include #include #include #include #include "GlobalParams.h" #include "UnicodeTypeTable.h" #include "UTF.h" class TestUTFConversion : public QObject { Q_OBJECT public: explicit TestUTFConversion(QObject *parent = nullptr) : QObject(parent) { } private slots: void testUTF_data(); void testUTF(); void testUnicodeToAscii7(); 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++) { if (a[i] != (Unicode)b[i]) { return false; } } return true; } static bool compare(const Unicode *a, const uint16_t *b, int len) { for (int i = 0; i < len; i++) { if (a[i] != b[i]) { return false; } } return true; } void TestUTFConversion::testUTF_data() { QTest::addColumn("s"); QTest::newRow("") << QString(QLatin1String("")); QTest::newRow("a") << QStringLiteral("a"); QTest::newRow("abc") << QStringLiteral("abc"); QTest::newRow("Latin") << QStringLiteral("Vitrum edere possum; mihi non nocet"); QTest::newRow("Greek") << QStringLiteral("Μπορώ να φάω σπασμένα γυαλιά χωρίς να πάθω τίποτα"); QTest::newRow("Icelandic") << QStringLiteral("Ég get etið gler án þess að meiða mig"); QTest::newRow("Russian") << QStringLiteral("Я могу есть стекло, оно мне не вредит."); QTest::newRow("Sanskrit") << QStringLiteral("काचं शक्नोम्यत्तुम् । नोपहिनस्ति माम् ॥"); QTest::newRow("Arabic") << QStringLiteral("أنا قادر على أكل الزجاج و هذا لا يؤلمني"); QTest::newRow("Chinese") << QStringLiteral("我能吞下玻璃而不伤身体。"); QTest::newRow("Thai") << QStringLiteral("ฉันกินกระจกได้ แต่มันไม่ทำให้ฉันเจ็บ"); QTest::newRow("non BMP") << QStringLiteral("𝓹𝓸𝓹𝓹𝓵𝓮𝓻"); } void TestUTFConversion::testUTF() { char utf8Buf[1000]; char *utf8String; uint16_t utf16Buf[1000]; uint16_t *utf16String; int len; QFETCH(QString, s); char *str = strdup(s.toUtf8().constData()); // 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, sizeof(utf16Buf), INT_MAX); QVERIFY(compare(utf16Buf, s.utf16())); QCOMPARE(len, s.size()); utf16String = utf8ToUtf16(str); QVERIFY(compare(utf16String, s.utf16())); free(utf16String); std::string sUtf8(str); std::string gsUtf16_a(utf8ToUtf16WithBom(sUtf8)); std::unique_ptr gsUtf16_b(Poppler::QStringToUnicodeGooString(s)); 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 len = utf16ToUtf8(s.utf16(), utf8Buf); QVERIFY(compare(utf8Buf, str)); QCOMPARE(len, (int)strlen(str)); utf8String = utf16ToUtf8(s.utf16()); QVERIFY(compare(utf8String, str)); free(utf8String); free(str); } void TestUTFConversion::testUnicodeToAscii7() { globalParams = std::make_unique(); // 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 const std::vector in = TextStringToUCS4(goo->toStr()); delete goo; int in_norm_len; int *in_norm_idx; 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); free(in_norm); free(in_norm_idx); // ascii7 conversion: ® -> (R) © -> (c) const char *expected_ascii = (char *)"(R)(c)(c)(c)(c)(c)(c)(c)(c)(c)(c)(c)(c)(c)(c)(c)(c)(c)(c)(c)(c)"; QCOMPARE(out_len, (int)strlen(expected_ascii)); QVERIFY(compare(out, expected_ascii, out_len)); free(out); free(out_ascii_idx); } void TestUTFConversion::testUnicodeLittleEndian() { uint16_t UTF16LE_hi[5] { 0xFFFE, 0x4800, 0x4900, 0x2100, 0x1126 }; // UTF16-LE "HI!☑" std::string GooUTF16LE(reinterpret_cast(UTF16LE_hi), sizeof(UTF16LE_hi)); uint16_t UTF16BE_hi[5] { 0xFEFF, 0x0048, 0x0049, 0x0021, 0x2611 }; // UTF16-BE "HI!☑" std::string GooUTF16BE(reinterpret_cast(UTF16BE_hi), sizeof(UTF16BE_hi)); // Let's assert both GooString's are different QVERIFY(GooUTF16LE != GooUTF16BE); 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(UCS4fromLE.size(), UCS4fromBE.size()); QCOMPARE(UCS4fromLE.size(), 4); // Check that now after conversion, UCS4fromLE and UCS4fromBE are now the same 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.data(), expected.utf16(), UCS4fromLE.size())); QVERIFY(compare(UCS4fromBE.data(), expected.utf16(), UCS4fromBE.size())); } QTEST_GUILESS_MAIN(TestUTFConversion) #include "check_utf_conversion.moc" poppler-24.02.0/qt6/tests/poppler-attachments.cpp000066400000000000000000000020041455701731300217140ustar00rootroot00000000000000#include #include #include #include int main(int argc, char **argv) { QCoreApplication a(argc, argv); // QApplication required! if (!(argc == 2)) { qWarning() << "usage: poppler-attachments filename"; exit(1); } std::unique_ptr doc = Poppler::Document::load(argv[1]); if (!doc) { qWarning() << "doc not loaded"; exit(1); } if (doc->hasEmbeddedFiles()) { std::cout << "Embedded files: " << std::endl; foreach (Poppler::EmbeddedFile *file, doc->embeddedFiles()) { std::cout << " " << qPrintable(file->name()) << std::endl; std::cout << " desc:" << qPrintable(file->description()) << std::endl; QByteArray data = file->data(); std::cout << " data: " << data.constData() << std::endl; } } else { std::cout << "There are no embedded document at the top level" << std::endl; } } poppler-24.02.0/qt6/tests/poppler-fonts.cpp000066400000000000000000000051651455701731300205450ustar00rootroot00000000000000#include #include #include #include int main(int argc, char **argv) { QCoreApplication a(argc, argv); // QApplication required! if (!(argc == 2)) { qWarning() << "usage: poppler-fonts filename"; exit(1); } std::unique_ptr doc = Poppler::Document::load(argv[1]); if (!doc) { qWarning() << "doc not loaded"; exit(1); } std::cout << "name type emb sub font file"; std::cout << std::endl; std::cout << "------------------------------------ ------------ --- --- ---------"; std::cout << std::endl; foreach (const Poppler::FontInfo &font, doc->fonts()) { if (font.name().isNull()) { std::cout << qPrintable(QStringLiteral("%1").arg(QStringLiteral("[none]"), -37)); } else { std::cout << qPrintable(QStringLiteral("%1").arg(font.name(), -37)); } switch (font.type()) { case Poppler::FontInfo::unknown: std::cout << "unknown "; break; case Poppler::FontInfo::Type1: std::cout << "Type 1 "; break; case Poppler::FontInfo::Type1C: std::cout << "Type 1C "; break; case Poppler::FontInfo::Type3: std::cout << "Type 3 "; break; case Poppler::FontInfo::TrueType: std::cout << "TrueType "; break; case Poppler::FontInfo::CIDType0: std::cout << "CID Type 0 "; break; case Poppler::FontInfo::CIDType0C: std::cout << "CID Type 0C "; break; case Poppler::FontInfo::CIDTrueType: std::cout << "CID TrueType "; break; case Poppler::FontInfo::Type1COT: std::cout << "Type 1C (OT) "; break; case Poppler::FontInfo::TrueTypeOT: std::cout << "TrueType (OT) "; break; case Poppler::FontInfo::CIDType0COT: std::cout << "CID Type 0C (OT) "; break; case Poppler::FontInfo::CIDTrueTypeOT: std::cout << "CID TrueType (OT) "; break; } if (font.isEmbedded()) { std::cout << "yes "; } else { std::cout << "no "; } if (font.isSubset()) { std::cout << "yes "; } else { std::cout << "no "; } std::cout << qPrintable(font.file()); std::cout << std::endl; } } poppler-24.02.0/qt6/tests/poppler-forms.cpp000066400000000000000000000234011455701731300205330ustar00rootroot00000000000000#include #include #include #include #include #include static std::ostream &operator<<(std::ostream &out, Poppler::FormField::FormType type) { switch (type) { case Poppler::FormField::FormButton: out << "Button"; break; case Poppler::FormField::FormText: out << "Text"; break; case Poppler::FormField::FormChoice: out << "Choice"; break; case Poppler::FormField::FormSignature: out << "Signature"; break; } return out; } static std::ostream &operator<<(std::ostream &out, Poppler::FormFieldButton::ButtonType type) { switch (type) { case Poppler::FormFieldButton::Push: out << "Push"; break; case Poppler::FormFieldButton::CheckBox: out << "CheckBox"; break; case Poppler::FormFieldButton::Radio: out << "Radio"; break; } return out; } static std::ostream &operator<<(std::ostream &out, Poppler::FormFieldText::TextType type) { switch (type) { case Poppler::FormFieldText::Normal: out << "Normal"; break; case Poppler::FormFieldText::Multiline: out << "Multiline"; break; case Poppler::FormFieldText::FileSelect: out << "FileSelect"; break; } return out; } static std::ostream &operator<<(std::ostream &out, Poppler::FormFieldChoice::ChoiceType type) { switch (type) { case Poppler::FormFieldChoice::ComboBox: out << "ComboBox"; break; case Poppler::FormFieldChoice::ListBox: out << "ListBox"; break; } return out; } static std::ostream &operator<<(std::ostream &out, Poppler::SignatureValidationInfo::SignatureStatus status) { switch (status) { case Poppler::SignatureValidationInfo::SignatureValid: out << "Valid"; break; case Poppler::SignatureValidationInfo::SignatureInvalid: out << "Invalid"; break; case Poppler::SignatureValidationInfo::SignatureDigestMismatch: out << "DigestMismatch"; break; case Poppler::SignatureValidationInfo::SignatureDecodingError: out << "DecodingError"; break; case Poppler::SignatureValidationInfo::SignatureGenericError: out << "GenericError"; break; case Poppler::SignatureValidationInfo::SignatureNotFound: out << "NotFound"; break; case Poppler::SignatureValidationInfo::SignatureNotVerified: out << "NotVerifiedYet"; break; } return out; } static std::ostream &operator<<(std::ostream &out, Poppler::SignatureValidationInfo::CertificateStatus status) { switch (status) { case Poppler::SignatureValidationInfo::CertificateTrusted: out << "Trusted"; break; case Poppler::SignatureValidationInfo::CertificateUntrustedIssuer: out << "UntrustedIssuer"; break; case Poppler::SignatureValidationInfo::CertificateUnknownIssuer: out << "UnknownIssuer"; break; case Poppler::SignatureValidationInfo::CertificateRevoked: out << "Revoked"; break; case Poppler::SignatureValidationInfo::CertificateExpired: out << "Expired"; break; case Poppler::SignatureValidationInfo::CertificateGenericError: out << "GenericError"; break; case Poppler::SignatureValidationInfo::CertificateNotVerified: out << "NotVerifiedYet"; break; } return out; } static std::ostream &operator<<(std::ostream &out, Qt::Alignment alignment) { switch (alignment) { case Qt::AlignLeft: out << "Left"; break; case Qt::AlignRight: out << "Right"; break; case Qt::AlignHCenter: out << "HCenter"; break; case Qt::AlignJustify: out << "Justify"; break; case Qt::AlignTop: out << "Top"; break; case Qt::AlignBottom: out << "Bottom"; break; case Qt::AlignVCenter: out << "VCenter"; break; case Qt::AlignCenter: out << "Center"; break; case Qt::AlignAbsolute: out << "Absolute"; break; } return out; } static std::ostream &operator<<(std::ostream &out, const QString &string) { out << string.toUtf8().constData(); return out; } static std::ostream &operator<<(std::ostream &out, const QRectF &rect) { out << QStringLiteral("top: %1 left: %2 width: %3 height: %4").arg(rect.x()).arg(rect.y()).arg(rect.width()).arg(rect.height()); return out; } template std::ostream &operator<<(std::ostream &out, const QList &elems) { bool isFirst = true; for (int i = 0; i < elems.count(); ++i) { if (!isFirst) { out << " "; } out << elems[i]; isFirst = false; } return out; } int main(int argc, char **argv) { QCoreApplication a(argc, argv); if (!(argc == 2)) { qWarning() << "usage: poppler-forms filename"; exit(1); } std::unique_ptr doc = Poppler::Document::load(argv[1]); if (!doc) { qWarning() << "doc not loaded"; exit(1); } std::cout << "Forms for file " << argv[1] << std::endl; for (int i = 0; i < doc->numPages(); ++i) { std::unique_ptr page = doc->page(i); if (page) { std::vector> forms = page->formFields(); std::cout << "\tPage " << i + 1 << std::endl; for (const std::unique_ptr &form : forms) { std::cout << "\t\tForm" << std::endl; std::cout << "\t\t\tType: " << form->type() << std::endl; std::cout << "\t\t\tRect: " << form->rect() << std::endl; std::cout << "\t\t\tID: " << form->id() << std::endl; std::cout << "\t\t\tName: " << form->name() << std::endl; std::cout << "\t\t\tFullyQualifiedName: " << form->fullyQualifiedName() << std::endl; std::cout << "\t\t\tUIName: " << form->uiName() << std::endl; std::cout << "\t\t\tReadOnly: " << form->isReadOnly() << std::endl; std::cout << "\t\t\tVisible: " << form->isVisible() << std::endl; switch (form->type()) { case Poppler::FormField::FormButton: { const Poppler::FormFieldButton *buttonForm = static_cast(form.get()); std::cout << "\t\t\tButtonType: " << buttonForm->buttonType() << std::endl; std::cout << "\t\t\tCaption: " << buttonForm->caption() << std::endl; std::cout << "\t\t\tState: " << buttonForm->state() << std::endl; std::cout << "\t\t\tSiblings: " << buttonForm->siblings() << std::endl; } break; case Poppler::FormField::FormText: { const Poppler::FormFieldText *textForm = static_cast(form.get()); std::cout << "\t\t\tTextType: " << textForm->textType() << std::endl; std::cout << "\t\t\tText: " << textForm->text() << std::endl; std::cout << "\t\t\tIsPassword: " << textForm->isPassword() << std::endl; std::cout << "\t\t\tIsRichText: " << textForm->isRichText() << std::endl; std::cout << "\t\t\tMaximumLength: " << textForm->maximumLength() << std::endl; std::cout << "\t\t\tTextAlignment: " << textForm->textAlignment() << std::endl; std::cout << "\t\t\tCanBeSpellChecked: " << textForm->canBeSpellChecked() << std::endl; } break; case Poppler::FormField::FormChoice: { const Poppler::FormFieldChoice *choiceForm = static_cast(form.get()); std::cout << "\t\t\tChoiceType: " << choiceForm->choiceType() << std::endl; std::cout << "\t\t\tChoices: " << choiceForm->choices() << std::endl; std::cout << "\t\t\tIsEditable: " << choiceForm->isEditable() << std::endl; std::cout << "\t\t\tIsMultiSelect: " << choiceForm->multiSelect() << std::endl; std::cout << "\t\t\tCurrentChoices: " << choiceForm->currentChoices() << std::endl; std::cout << "\t\t\tEditChoice: " << choiceForm->editChoice() << std::endl; std::cout << "\t\t\tTextAlignment: " << choiceForm->textAlignment() << std::endl; std::cout << "\t\t\tCanBeSpellChecked: " << choiceForm->canBeSpellChecked() << std::endl; } break; case Poppler::FormField::FormSignature: { const Poppler::FormFieldSignature *signatureForm = static_cast(form.get()); const Poppler::SignatureValidationInfo svi = signatureForm->validate(Poppler::FormFieldSignature::ValidateVerifyCertificate); std::cout << "\t\t\tSignatureStatus: " << svi.signatureStatus() << std::endl; std::cout << "\t\t\tCertificateStatus: " << svi.certificateStatus() << std::endl; if (svi.signerName().isEmpty() == false) { std::cout << "\t\t\tSignerName: " << svi.signerName() << std::endl; } else { std::cout << "\t\t\tSignerName: " << "(null)" << std::endl; } const QDateTime sviTime = QDateTime::fromSecsSinceEpoch(svi.signingTime(), Qt::UTC); std::cout << "\t\t\tSigningTime: " << sviTime.toString() << std::endl; } break; } } } } } poppler-24.02.0/qt6/tests/poppler-page-labels.cpp000066400000000000000000000024751455701731300215710ustar00rootroot00000000000000#include #include #include #include #include int main(int argc, char **argv) { QCoreApplication a(argc, argv); // QApplication required! if (!(argc == 2)) { qWarning() << "usage: poppler-page-labels filename"; exit(1); } std::unique_ptr doc = Poppler::Document::load(argv[1]); if (!doc || doc->isLocked()) { qWarning() << "doc not loaded"; exit(1); } for (int i = 0; i < doc->numPages(); i++) { int j = 0; std::cout << "*** Label of Page " << i << std::endl; std::cout << std::flush; std::unique_ptr page(doc->page(i)); if (!page) { continue; } const QByteArray utf8str = page->label().toUtf8(); for (j = 0; j < utf8str.size(); j++) { std::cout << utf8str[j]; } std::cout << std::endl; std::unique_ptr pageFromPageLabel(doc->page(page->label())); const int indexFromPageLabel = pageFromPageLabel ? pageFromPageLabel->index() : -1; if (indexFromPageLabel != i) { std::cout << "WARNING: Page label didn't link back to the same page index " << indexFromPageLabel << " " << i << std::endl; } } } poppler-24.02.0/qt6/tests/poppler-texts.cpp000066400000000000000000000016601455701731300205570ustar00rootroot00000000000000#include #include #include #include int main(int argc, char **argv) { QCoreApplication a(argc, argv); // QApplication required! if (!(argc == 2)) { qWarning() << "usage: poppler-texts filename"; exit(1); } std::unique_ptr doc = Poppler::Document::load(argv[1]); if (!doc) { qWarning() << "doc not loaded"; exit(1); } for (int i = 0; i < doc->numPages(); i++) { int j = 0; std::cout << "*** Page " << i << std::endl; std::cout << std::flush; std::unique_ptr page = doc->page(i); const QByteArray utf8str = page->text(QRectF(), Poppler::Page::RawOrderLayout).toUtf8(); std::cout << std::flush; for (j = 0; j < utf8str.size(); j++) { std::cout << utf8str[j]; } std::cout << std::endl; } } poppler-24.02.0/qt6/tests/stress-poppler-dir.cpp000066400000000000000000000043451455701731300215120ustar00rootroot00000000000000#include #include #include #include #include #include #include int main(int argc, char **argv) { QApplication a(argc, argv); // QApplication required! QElapsedTimer t; t.start(); QDir directory(argv[1]); 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); if (!doc) { qWarning() << "doc not loaded"; } else if (doc->isLocked()) { if (!doc->unlock("", "password")) { qWarning() << "couldn't unlock document"; } } else { auto pdfVersion = doc->getPdfVersion(); Q_UNUSED(pdfVersion); doc->info(QStringLiteral("Title")); doc->info(QStringLiteral("Subject")); doc->info(QStringLiteral("Author")); doc->info(QStringLiteral("Keywords")); doc->info(QStringLiteral("Creator")); doc->info(QStringLiteral("Producer")); doc->date(QStringLiteral("CreationDate")).toString(); doc->date(QStringLiteral("ModDate")).toString(); doc->numPages(); doc->isLinearized(); doc->isEncrypted(); doc->okToPrint(); doc->okToCopy(); doc->okToChange(); doc->okToAddNotes(); doc->pageMode(); for (int index = 0; index < doc->numPages(); ++index) { std::unique_ptr page = doc->page(index); page->renderToImage(); page->pageSize(); page->orientation(); std::cout << "."; std::cout.flush(); } std::cout << std::endl; } } } std::cout << "Elapsed time: " << (t.elapsed() / 1000) << "seconds" << std::endl; } poppler-24.02.0/qt6/tests/stress-poppler-qt6.cpp000066400000000000000000000046661455701731300214540ustar00rootroot00000000000000#include #include #include #include #include #include #include int main(int argc, char **argv) { QApplication a(argc, argv); // QApplication required! Q_UNUSED(argc); Q_UNUSED(argv); QElapsedTimer t; t.start(); QDir dbDir(QStringLiteral("./pdfdb")); if (!dbDir.exists()) { qWarning() << "Database directory does not exist"; } QStringList excludeSubDirs; excludeSubDirs << QStringLiteral("000048") << QStringLiteral("000607"); const QStringList dirs = dbDir.entryList(QStringList() << QStringLiteral("0000*"), QDir::Dirs); foreach (const QString &subdir, dirs) { if (excludeSubDirs.contains(subdir)) { // then skip it } else { QString path = "./pdfdb/" + subdir + "/data.pdf"; std::cout << "Doing " << path.toLatin1().data() << " :"; std::unique_ptr doc = Poppler::Document::load(path); if (!doc) { qWarning() << "doc not loaded"; } else { auto pdfVersion = doc->getPdfVersion(); Q_UNUSED(pdfVersion); doc->info(QStringLiteral("Title")); doc->info(QStringLiteral("Subject")); doc->info(QStringLiteral("Author")); doc->info(QStringLiteral("Keywords")); doc->info(QStringLiteral("Creator")); doc->info(QStringLiteral("Producer")); doc->date(QStringLiteral("CreationDate")).toString(); doc->date(QStringLiteral("ModDate")).toString(); doc->numPages(); doc->isLinearized(); doc->isEncrypted(); doc->okToPrint(); doc->okToCopy(); doc->okToChange(); doc->okToAddNotes(); doc->pageMode(); for (int index = 0; index < doc->numPages(); ++index) { std::unique_ptr page = doc->page(index); page->renderToImage(); page->pageSize(); page->orientation(); std::cout << "."; std::cout.flush(); } std::cout << std::endl; } } } std::cout << "Elapsed time: " << (t.elapsed() / 1000) << std::endl; } poppler-24.02.0/qt6/tests/stress-threads-qt6.cpp000066400000000000000000000202301455701731300214060ustar00rootroot00000000000000 #ifndef _WIN32 # include #else # include # define sleep Sleep #endif #include #include #include #include #include #include #include #include #include class SillyThread : public QThread { Q_OBJECT public: explicit SillyThread(Poppler::Document *document, QObject *parent = nullptr); void run() override; private: Poppler::Document *m_document; std::vector> m_pages; }; class CrazyThread : public QThread { Q_OBJECT public: CrazyThread(Poppler::Document *document, QMutex *annotationMutex, QObject *parent = nullptr); void run() override; private: Poppler::Document *m_document; QMutex *m_annotationMutex; }; static std::unique_ptr loadPage(Poppler::Document *document, int index) { std::unique_ptr page = document->page(index); if (page == nullptr) { qDebug() << "!Document::page"; exit(EXIT_FAILURE); } return page; } 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() { m_pages.reserve(m_document->numPages()); for (int index = 0; index < m_document->numPages(); ++index) { m_pages.push_back(loadPage(m_document, index)); } } void SillyThread::run() { forever { for (std::unique_ptr &page : m_pages) { QImage image = page->renderToImage(); if (image.isNull()) { qDebug() << "!Page::renderToImage"; ::exit(EXIT_FAILURE); } } } } CrazyThread::CrazyThread(Poppler::Document *document, QMutex *annotationMutex, QObject *parent) : QThread(parent), m_document(document), m_annotationMutex(annotationMutex) { } void CrazyThread::run() { typedef std::unique_ptr PagePointer; forever { if (QRandomGenerator::global()->bounded(2) == 0) { qDebug() << "search..."; PagePointer page(loadRandomPage(m_document)); page->search(QStringLiteral("c"), Poppler::Page::IgnoreCase); page->search(QStringLiteral("r")); page->search(QStringLiteral("a"), Poppler::Page::IgnoreCase); page->search(QStringLiteral("z")); page->search(QStringLiteral("y"), Poppler::Page::IgnoreCase); } if (QRandomGenerator::global()->bounded(2) == 0) { qDebug() << "links..."; PagePointer page(loadRandomPage(m_document)); std::vector> links = page->links(); } if (QRandomGenerator::global()->bounded(2) == 0) { qDebug() << "form fields..."; PagePointer page(loadRandomPage(m_document)); std::vector> formFields = page->formFields(); } if (QRandomGenerator::global()->bounded(2) == 0) { qDebug() << "thumbnail..."; PagePointer page(loadRandomPage(m_document)); page->thumbnail(); } if (QRandomGenerator::global()->bounded(2) == 0) { qDebug() << "text..."; PagePointer page(loadRandomPage(m_document)); page->text(QRectF(QPointF(), page->pageSizeF())); } if (QRandomGenerator::global()->bounded(2) == 0) { QMutexLocker mutexLocker(m_annotationMutex); qDebug() << "add annotation..."; PagePointer page(loadRandomPage(m_document)); Poppler::Annotation *annotation = nullptr; switch (QRandomGenerator::global()->bounded(3)) { default: case 0: annotation = new Poppler::TextAnnotation(QRandomGenerator::global()->bounded(2) == 0 ? Poppler::TextAnnotation::Linked : Poppler::TextAnnotation::InPlace); break; case 1: annotation = new Poppler::HighlightAnnotation(); break; case 2: annotation = new Poppler::InkAnnotation(); break; } annotation->setBoundary(QRectF(0.0, 0.0, 0.5, 0.5)); annotation->setContents(QStringLiteral("crazy")); page->addAnnotation(annotation); delete annotation; } if (QRandomGenerator::global()->bounded(2) == 0) { QMutexLocker mutexLocker(m_annotationMutex); for (int index = 0; index < m_document->numPages(); ++index) { PagePointer page(loadPage(m_document, index)); std::vector> annotations = page->annotations(); if (!annotations.empty()) { qDebug() << "modify annotation..."; // size is now a qsizetype which confuses bounded(), pretend we will never have that many annotations anyway const quint32 annotationsSize = annotations.size(); annotations.at(QRandomGenerator::global()->bounded(annotationsSize))->setBoundary(QRectF(0.5, 0.5, 0.25, 0.25)); annotations.at(QRandomGenerator::global()->bounded(annotationsSize))->setAuthor(QStringLiteral("foo")); annotations.at(QRandomGenerator::global()->bounded(annotationsSize))->setContents(QStringLiteral("bar")); annotations.at(QRandomGenerator::global()->bounded(annotationsSize))->setCreationDate(QDateTime::currentDateTime()); annotations.at(QRandomGenerator::global()->bounded(annotationsSize))->setModificationDate(QDateTime::currentDateTime()); } if (!annotations.empty()) { break; } } } if (QRandomGenerator::global()->bounded(2) == 0) { QMutexLocker mutexLocker(m_annotationMutex); for (int index = 0; index < m_document->numPages(); ++index) { PagePointer page(loadPage(m_document, index)); std::vector> annotations = page->annotations(); if (!annotations.empty()) { qDebug() << "remove annotation..."; // size is now a qsizetype which confuses bounded(), pretend we will never have that many annotations anyway const quint32 annotationsSize = annotations.size(); page->removeAnnotation(annotations[QRandomGenerator::global()->bounded(annotationsSize)].get()); annotations.erase(annotations.begin() + QRandomGenerator::global()->bounded(annotationsSize)); } if (!annotations.empty()) { break; } } } if (QRandomGenerator::global()->bounded(2) == 0) { qDebug() << "fonts..."; m_document->fonts(); } } } int main(int argc, char **argv) { if (argc < 5) { qDebug() << "usage: stress-threads-qt duration sillyCount crazyCount file(s)"; return EXIT_FAILURE; } const int duration = atoi(argv[1]); const int sillyCount = atoi(argv[2]); const int crazyCount = atoi(argv[3]); for (int argi = 4; argi < argc; ++argi) { const QString file = QFile::decodeName(argv[argi]); std::unique_ptr document = Poppler::Document::load(file); if (document == nullptr) { qDebug() << "Could not load" << file; continue; } if (document->isLocked()) { qDebug() << file << "is locked"; continue; } for (int i = 0; i < sillyCount; ++i) { (new SillyThread(document.get()))->start(); } QMutex *annotationMutex = new QMutex(); for (int i = 0; i < crazyCount; ++i) { (new CrazyThread(document.get(), annotationMutex))->start(); } } sleep(duration); return EXIT_SUCCESS; } #include "stress-threads-qt6.moc" poppler-24.02.0/qt6/tests/test-password-qt6.cpp000066400000000000000000000077341455701731300212700ustar00rootroot00000000000000#include #include #include #include #include #include #include class PDFDisplay : public QWidget // picture display widget { Q_OBJECT public: explicit PDFDisplay(std::unique_ptr &&d, QWidget *parent = nullptr); ~PDFDisplay() override; protected: void paintEvent(QPaintEvent *) override; void keyPressEvent(QKeyEvent *) override; private: void display(); int m_currentPage; QImage image; std::unique_ptr doc; }; PDFDisplay::PDFDisplay(std::unique_ptr &&d, QWidget *parent) : QWidget(parent) { doc = std::move(d); m_currentPage = 0; display(); } void PDFDisplay::display() { if (doc) { std::unique_ptr page = doc->page(m_currentPage); if (page) { qDebug() << "Displaying page: " << m_currentPage; image = page->renderToImage(); update(); } } else { qWarning() << "doc not loaded"; } } PDFDisplay::~PDFDisplay() { } void PDFDisplay::paintEvent(QPaintEvent *e) { QPainter paint(this); // paint widget if (!image.isNull()) { paint.drawImage(0, 0, image); } else { qWarning() << "null image"; } } void PDFDisplay::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Down) { if (m_currentPage + 1 < doc->numPages()) { m_currentPage++; display(); } } else if (e->key() == Qt::Key_Up) { if (m_currentPage > 0) { m_currentPage--; display(); } } else if (e->key() == Qt::Key_Q) { exit(0); } } int main(int argc, char **argv) { QApplication a(argc, argv); // QApplication required! if (argc != 3) { qWarning() << "usage: test-password-qt6 owner-password filename"; exit(1); } std::unique_ptr doc = Poppler::Document::load(argv[2], argv[1]); if (!doc) { qWarning() << "doc not loaded"; exit(1); } // output some meta-data auto pdfVersion = doc->getPdfVersion(); qDebug() << " PDF Version: " << qPrintable(QStringLiteral("%1.%2").arg(pdfVersion.major).arg(pdfVersion.minor)); qDebug() << " Title: " << doc->info(QStringLiteral("Title")); qDebug() << " Subject: " << doc->info(QStringLiteral("Subject")); qDebug() << " Author: " << doc->info(QStringLiteral("Author")); qDebug() << " Key words: " << doc->info(QStringLiteral("Keywords")); qDebug() << " Creator: " << doc->info(QStringLiteral("Creator")); qDebug() << " Producer: " << doc->info(QStringLiteral("Producer")); qDebug() << " Date created: " << doc->date(QStringLiteral("CreationDate")).toString(); qDebug() << " Date modified: " << doc->date(QStringLiteral("ModDate")).toString(); qDebug() << "Number of pages: " << doc->numPages(); qDebug() << " Linearised: " << doc->isLinearized(); qDebug() << " Encrypted: " << doc->isEncrypted(); qDebug() << " OK to print: " << doc->okToPrint(); qDebug() << " OK to copy: " << doc->okToCopy(); qDebug() << " OK to change: " << doc->okToChange(); qDebug() << "OK to add notes: " << doc->okToAddNotes(); qDebug() << " Page mode: " << doc->pageMode(); QStringList fontNameList; foreach (const Poppler::FontInfo &font, doc->fonts()) fontNameList += font.name(); qDebug() << " Fonts: " << fontNameList.join(QStringLiteral(", ")); std::unique_ptr page = doc->page(0); qDebug() << " Page 1 size: " << page->pageSize().width() / 72 << "inches x " << page->pageSize().height() / 72 << "inches"; PDFDisplay test(std::move(doc)); // create picture display test.setWindowTitle(QStringLiteral("Poppler-Qt6 Test")); test.show(); // show it return a.exec(); // start event loop } #include "test-password-qt6.moc" poppler-24.02.0/qt6/tests/test-poppler-qt6.cpp000066400000000000000000000154701455701731300211030ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include class PDFDisplay : public QWidget // picture display widget { Q_OBJECT public: PDFDisplay(std::unique_ptr &&d, bool qpainter, QWidget *parent = nullptr); ~PDFDisplay() override; void setShowTextRects(bool show); void display(); protected: void paintEvent(QPaintEvent *) override; void keyPressEvent(QKeyEvent *) override; void mousePressEvent(QMouseEvent *) override; private: int m_currentPage; QImage image; std::unique_ptr doc; QString backendString; bool showTextRects; std::vector> textRects; }; PDFDisplay::PDFDisplay(std::unique_ptr &&d, bool qpainter, QWidget *parent) : QWidget(parent) { showTextRects = false; doc = std::move(d); m_currentPage = 0; if (qpainter) { backendString = QStringLiteral("QPainter"); doc->setRenderBackend(Poppler::Document::QPainterBackend); } else { backendString = QStringLiteral("Splash"); doc->setRenderBackend(Poppler::Document::SplashBackend); } doc->setRenderHint(Poppler::Document::Antialiasing, true); doc->setRenderHint(Poppler::Document::TextAntialiasing, true); } void PDFDisplay::setShowTextRects(bool show) { showTextRects = show; } void PDFDisplay::display() { if (doc) { std::unique_ptr page = doc->page(m_currentPage); if (page) { qDebug() << "Displaying page using" << backendString << "backend: " << m_currentPage; QTime t = QTime::currentTime(); image = page->renderToImage(); qDebug() << "Rendering took" << t.msecsTo(QTime::currentTime()) << "msecs"; if (showTextRects) { QPainter painter(&image); painter.setPen(Qt::red); textRects = page->textList(); for (const std::unique_ptr &tb : textRects) { painter.drawRect(tb->boundingBox()); } } else { textRects.clear(); } update(); } } else { qWarning() << "doc not loaded"; } } PDFDisplay::~PDFDisplay() { } void PDFDisplay::paintEvent(QPaintEvent *e) { QPainter paint(this); // paint widget if (!image.isNull()) { paint.drawImage(0, 0, image); } else { qWarning() << "null image"; } } void PDFDisplay::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Down) { if (m_currentPage + 1 < doc->numPages()) { m_currentPage++; display(); } } else if (e->key() == Qt::Key_Up) { if (m_currentPage > 0) { m_currentPage--; display(); } } else if (e->key() == Qt::Key_Q) { exit(0); } } void PDFDisplay::mousePressEvent(QMouseEvent *e) { int i = 0; for (const std::unique_ptr &tb : textRects) { if (tb->boundingBox().contains(e->pos())) { const QString tt = QStringLiteral("Text: \"%1\"\nIndex in text list: %2").arg(tb->text()).arg(i); QToolTip::showText(e->globalPosition().toPoint(), tt, this); break; } ++i; } } int main(int argc, char **argv) { QApplication a(argc, argv); // QApplication required! if (argc < 2 || (argc == 3 && strcmp(argv[2], "-extract") != 0 && strcmp(argv[2], "-qpainter") != 0 && strcmp(argv[2], "-textRects") != 0) || argc > 3) { // use argument as file name qWarning() << "usage: test-poppler-qt6 filename [-extract|-qpainter|-textRects]"; exit(1); } std::unique_ptr doc = Poppler::Document::load(QFile::decodeName(argv[1])); if (!doc) { qWarning() << "doc not loaded"; exit(1); } if (doc->isLocked()) { qWarning() << "document locked (needs password)"; exit(0); } // output some meta-data auto pdfVersion = doc->getPdfVersion(); qDebug() << " PDF Version: " << qPrintable(QStringLiteral("%1.%2").arg(pdfVersion.major).arg(pdfVersion.minor)); qDebug() << " Title: " << doc->info(QStringLiteral("Title")); qDebug() << " Subject: " << doc->info(QStringLiteral("Subject")); qDebug() << " Author: " << doc->info(QStringLiteral("Author")); qDebug() << " Key words: " << doc->info(QStringLiteral("Keywords")); qDebug() << " Creator: " << doc->info(QStringLiteral("Creator")); qDebug() << " Producer: " << doc->info(QStringLiteral("Producer")); qDebug() << " Date created: " << doc->date(QStringLiteral("CreationDate")).toString(); qDebug() << " Date modified: " << doc->date(QStringLiteral("ModDate")).toString(); qDebug() << "Number of pages: " << doc->numPages(); qDebug() << " Linearised: " << doc->isLinearized(); qDebug() << " Encrypted: " << doc->isEncrypted(); qDebug() << " OK to print: " << doc->okToPrint(); qDebug() << " OK to copy: " << doc->okToCopy(); qDebug() << " OK to change: " << doc->okToChange(); qDebug() << "OK to add notes: " << doc->okToAddNotes(); qDebug() << " Page mode: " << doc->pageMode(); qDebug() << " Metadata: " << doc->metadata(); if (doc->hasEmbeddedFiles()) { qDebug() << "Embedded files:"; foreach (Poppler::EmbeddedFile *file, doc->embeddedFiles()) { qDebug() << " " << file->name(); } qDebug(); } else { qDebug() << "No embedded files"; } if (doc->numPages() <= 0) { qDebug() << "Doc has no pages"; return 0; } { std::unique_ptr page = doc->page(0); if (page) { qDebug() << "Page 1 size: " << page->pageSize().width() / 72 << "inches x " << page->pageSize().height() / 72 << "inches"; } } if (argc == 2 || (argc == 3 && strcmp(argv[2], "-qpainter") == 0) || (argc == 3 && strcmp(argv[2], "-textRects") == 0)) { bool useQPainter = (argc == 3 && strcmp(argv[2], "-qpainter") == 0); PDFDisplay test(std::move(doc), useQPainter); // create picture display test.setWindowTitle(QStringLiteral("Poppler-Qt6 Test")); test.setShowTextRects(argc == 3 && strcmp(argv[2], "-textRects") == 0); test.display(); test.show(); // show it return a.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(); } } #include "test-poppler-qt6.moc" poppler-24.02.0/qt6/tests/test-render-to-file.cpp000066400000000000000000000034041455701731300215200ustar00rootroot00000000000000#include #include #include #include #include int main(int argc, char **argv) { QGuiApplication a(argc, argv); // QApplication required! if (argc < 2 || (argc == 3 && strcmp(argv[2], "-qpainter") != 0) || argc > 3) { // use argument as file name qWarning() << "usage: test-render-to-file-qt6 filename [-qpainter]"; exit(1); } std::unique_ptr doc = Poppler::Document::load(QFile::decodeName(argv[1])); if (!doc) { qWarning() << "doc not loaded"; exit(1); } if (doc->isLocked()) { qWarning() << "document locked (needs password)"; exit(0); } if (doc->numPages() <= 0) { qDebug() << "Doc has no pages"; return 0; } QString backendString; if (argc == 3 && strcmp(argv[2], "-qpainter") == 0) { backendString = QStringLiteral("QPainter"); doc->setRenderBackend(Poppler::Document::QPainterBackend); } else { backendString = QStringLiteral("Splash"); doc->setRenderBackend(Poppler::Document::SplashBackend); } doc->setRenderHint(Poppler::Document::Antialiasing, true); doc->setRenderHint(Poppler::Document::TextAntialiasing, true); for (int i = 0; i < doc->numPages(); ++i) { std::unique_ptr page = doc->page(i); if (page) { qDebug() << "Rendering page using" << backendString << "backend: " << i; QTime t = QTime::currentTime(); QImage image = page->renderToImage(); qDebug() << "Rendering took" << t.msecsTo(QTime::currentTime()) << "msecs"; image.save(QStringLiteral("test-render-to-file%1.png").arg(i)); } } return 0; } poppler-24.02.0/regtest/000077500000000000000000000000001455701731300150235ustar00rootroot00000000000000poppler-24.02.0/regtest/.gitignore000066400000000000000000000000061455701731300170070ustar00rootroot00000000000000*.pyc poppler-24.02.0/regtest/Bisect.py000066400000000000000000000072171455701731300166150ustar00rootroot00000000000000# Bisect.py # # Copyright (C) 2012 Carlos Garcia Campos # # 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from __future__ import absolute_import, division, print_function from builder import get_builder from TestRun import TestRun from Config import Config import os import subprocess import sys class GitBisect: def __init__(self, srcdir): self.srcdir = srcdir def __run_cmd(self, cmd): p = subprocess.Popen(cmd, cwd=self.srcdir, stdout=subprocess.PIPE) stdout, stderr = p.communicate() if stdout: sys.stdout.write(stdout) status = p.returncode if not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 0: raise Exception('Command %s returned non-zero exit status %d' % (str(cmd), status)) return stdout def __finished(self, output): if not output: return True return "is the first bad commit" in output def start(self, bad=None, good=None): cmd = ['git', 'bisect', 'start'] if bad is not None: cmd.append(bad) if good is not None: cmd.append(good) self.__run_cmd(cmd) def good(self): cmd = ['git', 'bisect', 'good'] output = self.__run_cmd(cmd) return self.__finished(output) def bad(self): cmd = ['git', 'bisect', 'bad'] output = self.__run_cmd(cmd) return self.__finished(output) def reset(self): cmd = ['git', 'bisect', 'reset'] self.__run_cmd(cmd) class Bisect: def __init__(self, test, refsdir, outdir): self._test = test self._refsdir = refsdir self._outdir = outdir self.config = Config() self._builder = get_builder(self.config.builder) # Add run-tests options to config self.config.keep_results = False self.config.create_diffs = False self.config.update_refs = False def __get_current_revision(self): p = subprocess.Popen(['git', 'rev-parse', 'HEAD'], cwd=self.config.src_dir, stdout=subprocess.PIPE) return p.communicate()[0] def run(self): bisect = GitBisect(self.config.src_dir) # TODO: save revision in .md5 files and get the good # revision from refs when not provided by command line try: bisect.start(self.config.bad, self.config.good) except: print("Couldn't start git bisect") return finished = False while not finished: test_runner = TestRun(os.path.dirname(self._test), self._refsdir, self._outdir) try: self._builder.build() except: print("Impossible to find regression, build is broken in revision: %s" % (self.__get_current_revision())) break test_runner.run_test(os.path.basename(self._test)) if test_runner._n_passed == 0: finished = bisect.bad() else: finished = bisect.good() bisect.reset() poppler-24.02.0/regtest/Config.py000066400000000000000000000022141455701731300166010ustar00rootroot00000000000000# Config.py # # Copyright (C) 2011 Carlos Garcia Campos # # 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from __future__ import absolute_import, division, print_function class Config: shared_state = {} def __init__(self, config = None): if config is not None: self.__class__.shared_state = config self.__dict__ = self.__class__.shared_state if __name__ == '__main__': c = Config({'foo' : 25}) print(c.foo) cc = Config() print(cc.foo) poppler-24.02.0/regtest/HTMLReport.py000066400000000000000000000276411455701731300173470ustar00rootroot00000000000000# HTMLReport.py # # Copyright (C) 2012 Carlos Garcia Campos # # 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from __future__ import absolute_import, division, print_function from backends import get_backend, get_all_backends from Config import Config import os import errno import subprocess class HTMLPrettyDiff: def write(self, test, outdir, actual, expected, diff): raise NotImplementedError def _create_diff_for_test(self, outdir, test): diffdir = os.path.join(outdir, 'html', test) try: os.makedirs(diffdir) except OSError as e: if e.errno != errno.EEXIST: raise except: raise return diffdir class HTMLPrettyDiffImage(HTMLPrettyDiff): def write(self, test, outdir, result, actual, expected, diff): def get_relative_path(path): return '../' * len(path.split('/')) + path html = """ %s Difference between images: diff
Loading...
""" % (test, get_relative_path(diff), get_relative_path(actual), expected) diffdir = self._create_diff_for_test(outdir, test) pretty_diff_name = result + '-pretty-diff.html' pretty_diff = os.path.abspath(os.path.join(diffdir, pretty_diff_name)) f = open(pretty_diff, 'w') f.write(html) f.close() return os.path.join(test, pretty_diff_name) class HTMLPrettyDiffText(HTMLPrettyDiff): def write(self, test, outdir, result, actual, expected, diff): import difflib actual_file = open(os.path.join(outdir, actual), 'r') expected_file = open(expected, 'r') html = difflib.HtmlDiff().make_file(actual_file.readlines(), expected_file.readlines(), "Actual", "Expected", context=True) actual_file.close() expected_file.close() diffdir = self._create_diff_for_test(outdir, test) pretty_diff_name = result + '-pretty-diff.html' pretty_diff = os.path.abspath(os.path.join(diffdir, pretty_diff_name)) f = open(pretty_diff, 'w') f.write(html) f.close() return os.path.join(test, pretty_diff_name) def create_pretty_diff(backend): if backend.get_diff_ext() == '.diff.png': return HTMLPrettyDiffImage() # Disable pretty diff for Text files for now, since HtmlDiff().make_file() is # entering in an infinite loop with some files. We need to either fix that somehow or # find a different way to generate pretty diffs of text files. #if backend.get_diff_ext() == '.diff': # return HTMLPrettyDiffText() return None class BackendTestResult: def __init__(self, test, refsdir, outdir, backend, results): self._test = test self._refsdir = refsdir self._outdir = outdir self._backend = backend self.config = Config() self._results = [] ref_path = os.path.join(self._refsdir, self._test) if not backend.has_md5(ref_path): return ref_names = backend.get_ref_names(ref_path) for result in results: basename = os.path.basename(result) if basename in ref_names: self._results.append(basename) def is_failed(self): return len(self._results) > 0 def is_crashed(self): return self._backend.is_crashed(os.path.join(self._outdir, self._test)) def is_failed_to_run(self): return self._backend.is_failed(os.path.join(self._outdir, self._test)) def get_stderr(self): return self._backend.get_stderr(os.path.join(self._outdir, self._test)) def get_failed_html(self): html = "" for result in self._results: actual = os.path.join(self._test, result) actual_path = os.path.join(self._outdir, actual) expected = os.path.join(self._refsdir, self._test, result) if self.config.abs_paths: expected = os.path.abspath(expected) html += "
  • actual expected " % (actual, expected) if self._backend.has_diff(actual_path): diff = actual + self._backend.get_diff_ext() html += "diff " % (diff) if self.config.pretty_diff: pretty_diff = create_pretty_diff(self._backend) if pretty_diff: html += "pretty diff " % (pretty_diff.write (self._test, self._outdir, result, actual, expected, diff)) html += "
  • \n" if html: return "
      %s
    \n" % (html) return "" class TestResult: def __init__(self, docsdir, refsdir, outdir, resultdir, results, backends): self._refsdir = refsdir self._outdir = outdir self.config = Config() self._test = resultdir[len(self._outdir):].lstrip('/') self._doc = os.path.join(docsdir, self._test) self._results = {} for backend in backends: ref_path = os.path.join(self._refsdir, self._test) if not backend.has_md5(ref_path) and not backend.is_crashed(ref_path) and not backend.is_failed(ref_path): continue self._results[backend] = BackendTestResult(self._test, refsdir, outdir, backend, results) def get_test(self): return self._test def is_failed(self): for backend in self._results: if self._results[backend].is_failed(): return True return False; def get_failed_html(self): html = "" for backend in self._results: if self._results[backend].is_crashed() or self._results[backend].is_failed_to_run(): continue backend_html = self._results[backend].get_failed_html() if not backend_html: continue html += "
  • %s " % (backend.get_name()) stderr = self._results[backend].get_stderr() if os.path.exists(stderr): stderr_name = stderr[len(self._outdir):].lstrip('/') html += "stderr" % (stderr_name) html += "
  • \n%s" % (backend_html) if html: return "

    %s

    \n
      %s
    Top\n" % (self._test, self._doc, self._test, html) return "" def get_crashed_html(self): html = "" for backend in self._results: if not self._results[backend].is_crashed(): continue html += "
  • %s (%s)
  • \n" % (self._doc, self._test, backend.get_name()) if html: return "
      %s
    \n" % (html) return "" def get_failed_to_run_html(self): html = "" for backend in self._results: status = self._results[backend].is_failed_to_run() if not status: continue html += "
  • %s [Status: %d] (%s)
  • \n" % (self._doc, self._test, status, backend.get_name()) if html: return "
      %s
    \n" % (html) return "" class HTMLReport: def __init__(self, docsdir, refsdir, outdir): self._docsdir = docsdir self._refsdir = refsdir self._outdir = outdir self._htmldir = os.path.join(outdir, 'html') self.config = Config() try: os.makedirs(self._htmldir) except OSError as e: if e.errno != errno.EEXIST: raise except: raise def create(self, launch_browser): html = "" if os.path.exists(os.path.join(self._outdir, '.exited_early')): html += "

    Testing exited early

    " if self.config.backends: backends = [get_backend(name) for name in self.config.backends] else: backends = get_all_backends() results = {} for root, dirs, files in os.walk(self._outdir, False): if not files: continue if not root.lower().endswith('.pdf'): continue if root.startswith(self._htmldir): continue results[root] = TestResult(self._docsdir, self._refsdir, self._outdir, root, files, backends) failed_anchors = [] failed = "" crashed = "" failed_to_run = "" for test_name, test in sorted(results.items()): if test.is_failed(): failed_anchors.append(test.get_test()) failed += test.get_failed_html() crashed += test.get_crashed_html() failed_to_run += test.get_failed_to_run_html() if failed: failed = "

    Tests Failed (differences were found)

    \n%s" % (failed) if crashed: crashed = "

    Tests Crashed

    \n%s" % (crashed) if failed_to_run: failed_to_run = "

    Tests that failed to run (command returned an error status)

    \n%s" % (failed_to_run) if failed or crashed or failed_to_run: html += "\n" html += failed + crashed + failed_to_run + "" report_index = os.path.join(self._htmldir, 'index.html') with open(report_index, 'w') as f: f.write(html) if launch_browser: subprocess.Popen(['xdg-open', report_index]) poppler-24.02.0/regtest/InterruptibleQueue.py000066400000000000000000000045531455701731300212410ustar00rootroot00000000000000# InterruptibleQueue.py # # Copyright (C) 2016 Carlos Garcia Campos # # 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from __future__ import absolute_import, division, print_function from threading import Lock, Condition from collections import deque import sys class InterruptibleQueue: """Simpler implementation of Queue that uses wait with a timeout to make join interruptile""" def __init__(self): self._queue = deque() self._mutex = Lock() self._finished_condition = Condition(self._mutex) self._not_empty_condition = Condition(self._mutex) self._n_unfinished_tasks = 0 def task_done(self): self._finished_condition.acquire() try: n_unfinished = self._n_unfinished_tasks - 1 if n_unfinished == 0: self._finished_condition.notify_all() self._n_unfinished_tasks = n_unfinished finally: self._finished_condition.release() def join(self): self._finished_condition.acquire() try: while self._n_unfinished_tasks: self._finished_condition.wait(sys.float_info.max) finally: self._finished_condition.release() def put(self, item): self._mutex.acquire() try: self._queue.append(item) self._n_unfinished_tasks += 1 self._not_empty_condition.notify() finally: self._mutex.release() def get(self): self._not_empty_condition.acquire() try: while not len(self._queue): self._not_empty_condition.wait() return self._queue.popleft() finally: self._not_empty_condition.release() poppler-24.02.0/regtest/Printer.py000066400000000000000000000064101455701731300170210ustar00rootroot00000000000000# Printer.py # # Copyright (C) 2012 Carlos Garcia Campos # # 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from __future__ import absolute_import, division, print_function import sys from Config import Config from threading import RLock _instance = None class Printer: def __init__(self): global _instance if _instance is not None: raise RuntimeError('Printer must not be instantiated more than ' 'once. Use the get_printer() function instead.') self._verbose = Config().verbose self._stream = sys.stdout self._rewrite = self._stream.isatty() and not self._verbose self._current_line_len = 0 self._blocked = 0 self._lock = RLock() _instance = self def _erase_current_line(self): if not self._current_line_len: return line_len = self._current_line_len self._stream.write('\b' * line_len + ' ' * line_len + '\b' * line_len) self._current_line_len = 0 def _ensure_new_line(self, msg): if not msg.endswith('\n'): msg += '\n' return msg def _print(self, msg): self._stream.write(msg) self._stream.flush() def printout(self, msg): if not self._rewrite: self.printout_ln(msg) with self._lock: if self._blocked > 0: return self._erase_current_line() self._print(msg) self._current_line_len = len(msg[msg.rfind('\n') + 1:]) def printout_ln(self, msg=''): with self._lock: if self._blocked > 0: return self._erase_current_line() self._print(self._ensure_new_line(msg)) def printerr(self, msg): with self._lock: if self._blocked > 0: return sys.stderr.write(self._ensure_new_line(msg)) sys.stderr.flush() def print_test_result(self, doc_path, backend_name, n_test, total_tests, msg): self.printout("[%d/%d] %s (%s): %s" % (n_test, total_tests, doc_path, backend_name, msg)) def print_test_result_ln(self, doc_path, backend_name, n_test, total_tests, msg): self.printout_ln("[%d/%d] %s (%s): %s" % (n_test, total_tests, doc_path, backend_name, msg)) def print_default(self, msg): if self._verbose: self.printout_ln(msg) def block(self): with self._lock: self._blocked += 1 def unblock(self): with self._lock: self._blocked -= 1 def get_printer(): if _instance is None: Printer() return _instance poppler-24.02.0/regtest/TestReferences.py000066400000000000000000000106531455701731300203230ustar00rootroot00000000000000# TestReferences.py # # Copyright (C) 2011 Carlos Garcia Campos # # 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from __future__ import absolute_import, division, print_function import os import errno from backends import get_backend, get_all_backends from Config import Config from Printer import get_printer from Utils import get_document_paths_from_dir, get_skipped_tests, get_passwords from InterruptibleQueue import InterruptibleQueue from threading import Thread, RLock class TestReferences: def __init__(self, docsdir, refsdir): self._docsdir = docsdir self._refsdir = refsdir self._skipped = get_skipped_tests(docsdir) self._passwords = get_passwords(docsdir) self.config = Config() self.printer = get_printer() self._total_tests = 1 self._n_tests = 0 self._queue = InterruptibleQueue() self._lock = RLock() try: os.makedirs(self._refsdir) except OSError as e: if e.errno != errno.EEXIST: raise except: raise def _get_backends(self): if self.config.backends: return [get_backend(name) for name in self.config.backends] return get_all_backends() def create_refs_for_file(self, filename): backends = self._get_backends() if filename in self._skipped: with self._lock: self._n_tests += len(backends) self.printer.print_default("Skipping test '%s'" % (os.path.join(self._docsdir, filename))) return refs_path = os.path.join(self._refsdir, filename) try: os.makedirs(refs_path) except OSError as e: if e.errno != errno.EEXIST: raise except: raise doc_path = os.path.join(self._docsdir, filename) password = self._passwords.get(filename) for backend in backends: if not self.config.force and backend.has_results(refs_path): with self._lock: self._n_tests += 1 self.printer.print_default("Results found, skipping '%s' for %s backend" % (doc_path, backend.get_name())) continue if backend.create_refs(doc_path, refs_path, password): backend.create_checksums(refs_path, self.config.checksums_only) with self._lock: self._n_tests += 1 self.printer.printout_ln("[%d/%d] %s (%s): done" % (self._n_tests, self._total_tests, doc_path, backend.get_name())) def _worker_thread(self): while True: doc = self._queue.get() self.create_refs_for_file(doc) self._queue.task_done() def create_refs(self): docs, total_docs = get_document_paths_from_dir(self._docsdir) backends = self._get_backends() self._total_tests = total_docs * len(backends) if total_docs == 1: n_workers = 0 else: n_workers = min(self.config.threads, total_docs) self.printer.printout_ln('Found %d documents' % (total_docs)) self.printer.printout_ln('Backends: %s' % ', '.join([backend.get_name() for backend in backends])) self.printer.printout_ln('Process %d using %d worker threads' % (os.getpid(), n_workers)) self.printer.printout_ln() if n_workers > 0: self.printer.printout('Spawning %d workers...' % (n_workers)) for n_thread in range(n_workers): thread = Thread(target=self._worker_thread) thread.daemon = True thread.start() for doc in docs: self._queue.put(doc) self._queue.join() else: for doc in docs: self.create_refs_for_file(doc) poppler-24.02.0/regtest/TestRun.py000066400000000000000000000322671455701731300170130ustar00rootroot00000000000000# TestRun.py # # Copyright (C) 2011 Carlos Garcia Campos # # 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from __future__ import absolute_import, division, print_function from backends import get_backend, get_all_backends from Config import Config from Utils import get_document_paths_from_dir, get_skipped_tests, get_passwords from Printer import get_printer import sys import os import errno import shutil from InterruptibleQueue import InterruptibleQueue from threading import Thread, RLock class TestRun: def __init__(self, docsdir, refsdir, outdir, max_failures = None): self._docsdir = docsdir self._refsdir = refsdir self._outdir = outdir self._max_failures = max_failures self._skip = get_skipped_tests(docsdir) self._passwords = get_passwords(docsdir) self.config = Config() self.printer = get_printer() self._total_tests = 1 self._exited_early = False # Results self._n_tests = 0 self._n_run = 0 self._n_passed = 0 self._failed = {} self._crashed = {} self._failed_status_error = {} self._did_not_crash = {} self._did_not_fail_status_error = {} self._stderr = {} self._skipped = [] self._new = [] self._queue = InterruptibleQueue() self._lock = RLock() try: os.makedirs(self._outdir); except OSError as e: if e.errno != errno.EEXIST: raise except: raise def _get_backends(self): if self.config.backends: return [get_backend(name) for name in self.config.backends] return get_all_backends() def test(self, refs_path, doc_path, test_path, backend, password): # First check whether there are test results for the backend ref_has_md5 = backend.has_md5(refs_path) ref_is_crashed = backend.is_crashed(refs_path) ref_is_failed = backend.is_failed(refs_path) if not ref_has_md5 and not ref_is_crashed and not ref_is_failed: with self._lock: self._new.append("%s (%s)" % (doc_path, backend.get_name())) self._n_tests += 1 self.printer.print_default("Reference files not found, skipping '%s' for %s backend" % (doc_path, backend.get_name())) return test_has_md5 = backend.create_refs(doc_path, test_path, password) test_passed = False if ref_has_md5 and test_has_md5: test_passed = backend.compare_checksums(refs_path, test_path, not self.config.keep_results, self.config.create_diffs, self.config.update_refs) elif self.config.update_refs: backend.update_results(refs_path, test_path) with self._lock: self._n_tests += 1 self._n_run += 1 if backend.has_stderr(test_path): self._stderr.setdefault(backend.get_name(), []).append(doc_path) if ref_has_md5 and test_has_md5: if test_passed: # FIXME: remove dir if it's empty? self.printer.print_test_result(doc_path, backend.get_name(), self._n_tests, self._total_tests, "PASS") self._n_passed += 1 else: self.printer.print_test_result_ln(doc_path, backend.get_name(), self._n_tests, self._total_tests, "FAIL") self._failed.setdefault(backend.get_name(), []).append(doc_path) return if test_has_md5: if ref_is_crashed: self.printer.print_test_result_ln(doc_path, backend.get_name(), self._n_tests, self._total_tests, "DOES NOT CRASH") self._did_not_crash.setdefault(backend.get_name(), []).append(doc_path) elif ref_is_failed: self.printer.print_test_result_ln(doc_path, backend.get_name(), self._n_tests, self._total_tests, "DOES NOT FAIL") self._did_not_fail_status_error.setdefault(backend.get_name(), []).append(doc_path) return test_is_crashed = backend.is_crashed(test_path) if ref_is_crashed and test_is_crashed: self.printer.print_test_result(doc_path, backend.get_name(), self._n_tests, self._total_tests, "PASS (Expected crash)") self._n_passed += 1 return test_is_failed = backend.is_failed(test_path) if ref_is_failed and test_is_failed: # FIXME: compare status errors self.printer.print_test_result(doc_path, backend.get_name(), self._n_tests, self._total_tests, "PASS (Expected fail with status error %d)" % (test_is_failed)) self._n_passed += 1 return if test_is_crashed: self.printer.print_test_result_ln(doc_path, backend.get_name(), self._n_tests, self._total_tests, "CRASH") self._crashed.setdefault(backend.get_name(), []).append(doc_path) return if test_is_failed: self.printer.print_test_result_ln(doc_path, backend.get_name(), self._n_tests, self._total_tests, "FAIL (status error %d)" % (test_is_failed)) self._failed_status_error.setdefault(backend.get_name(), []).append(doc_path) return def run_test(self, filename): backends = self._get_backends() if filename in self._skip: doc_path = os.path.join(self._docsdir, filename) with self._lock: self._skipped.append("%s" % (doc_path)) self._n_tests += len(backends) self.printer.print_default("Skipping test '%s'" % (doc_path)) return out_path = os.path.join(self._outdir, filename) try: os.makedirs(out_path) except OSError as e: if e.errno != errno.EEXIST: raise except: raise doc_path = os.path.join(self._docsdir, filename) refs_path = os.path.join(self._refsdir, filename) if not os.path.isdir(refs_path): with self._lock: self._new.append("%s" % (doc_path)) self._n_tests += len(backends) self.printer.print_default("Reference dir not found for %s, skipping" % (doc_path)) return password = self._passwords.get(filename) for backend in backends: self.test(refs_path, doc_path, out_path, backend, password) def _should_exit_early(self): if self._max_failures is None: return False def _len(tests): retval = 0 for backend in tests: retval += len(tests[backend]) return retval with self._lock: if _len(self._failed) + _len(self._crashed) + _len(self._failed_status_error) >= self._max_failures: if not self._exited_early: self.printer.printout_ln('Exiting early after %d failures, waiting for running jobs to finish...' % self._max_failures) self.printer.block() self._exited_early = True return True return False def _worker_thread(self): while True: doc = self._queue.get() if not self._should_exit_early(): self.run_test(doc) self._queue.task_done() def run_tests(self, tests = []): # Clean the output dir. try: shutil.rmtree(self._outdir) except OSError as e: if e.errno != errno.ENOENT: raise except: raise if not tests: docs, total_docs = get_document_paths_from_dir(self._docsdir) else: docs = [] total_docs = 0 for test in tests: if os.path.isdir(test): test_dir = test elif os.path.isdir(os.path.join(self._docsdir, test)): test_dir = os.path.join(self._docsdir, test) else: test_dir = None if test_dir is not None: dir_docs, dir_n_docs = get_document_paths_from_dir(test_dir, self._docsdir) docs.extend(dir_docs) total_docs += dir_n_docs else: if test.startswith(self._docsdir): test = test[len(self._docsdir):].lstrip(os.path.sep) docs.append(test) total_docs += 1 backends = self._get_backends() self._total_tests = total_docs * len(backends) if total_docs == 1: n_workers = 0 else: n_workers = min(self.config.threads, total_docs) self.printer.printout_ln('Found %d documents' % (total_docs)) self.printer.printout_ln('Backends: %s' % ', '.join([backend.get_name() for backend in backends])) self.printer.printout_ln('Process %d using %d worker threads' % (os.getpid(), n_workers)) self.printer.printout_ln() if n_workers > 0: self.printer.printout('Spawning %d workers...' % (n_workers)) for n_thread in range(n_workers): thread = Thread(target=self._worker_thread) thread.daemon = True thread.start() for doc in docs: self._queue.put(doc) self._queue.join() else: for doc in docs: self.run_test(doc) if self._should_exit_early(): break if self._exited_early: open(os.path.join(self._outdir, '.exited_early'), 'w').close() self.printer.unblock() return -self._max_failures return int(self._n_passed != self._n_run) def summary(self): self.printer.printout_ln() if self._n_run: if self._exited_early: self.printer.printout_ln("Testing exited early") self.printer.printout_ln() self.printer.printout_ln("%d tests passed (%.2f%%)" % ( self._n_passed, (self._n_passed * 100) / self._n_run)) self.printer.printout_ln() def result_tests(test_dict): if not test_dict: return 0, None n_tests = 0 tests = "" for backend in test_dict: backend_docs = test_dict[backend] n_tests += len(backend_docs) tests += "\n".join([" %s (%s)" % (doc_path, backend) for doc_path in backend_docs]) tests += "\n" return n_tests, tests def backends_summary(test_dict, n_tests): percs = [] for backend in test_dict: n_docs = len(test_dict[backend]) percs.append("%d %s (%.2f%%)" % (n_docs, backend, n_docs * 100 / n_tests)) return ", ".join(percs) test_results = [(self._failed, "unexpected failures"), (self._crashed, "unexpected crashes"), (self._failed_status_error, "unexpected failures (test program returned with an exit error status)"), (self._stderr, "tests have stderr output"), (self._did_not_crash, "expected to crash, but didn't crash"), (self._did_not_fail_status_error, "expected to fail to run, but didn't fail")] for test_dict, test_msg in test_results: n_tests, tests = result_tests(test_dict) if n_tests == 0: continue self.printer.printout_ln("%d %s (%.2f%%) [%s]" % ( n_tests, test_msg, n_tests * 100 / self._n_run, backends_summary(test_dict, n_tests))) self.printer.printout_ln(tests) self.printer.printout_ln() else: self.printer.printout_ln("No tests run") if self._skipped: self.printer.printout_ln("%d tests skipped" % len(self._skipped)) self.printer.printout_ln("\n".join([" %s" % skipped for skipped in self._skipped])) self.printer.printout_ln() if self._new: self.printer.printout_ln("%d new documents" % len(self._new)) self.printer.printout_ln("\n".join([" %s" % new for new in self._new])) self.printer.printout_ln("Use create-refs command to add reference results for them") self.printer.printout_ln() poppler-24.02.0/regtest/Timer.py000066400000000000000000000040041455701731300164530ustar00rootroot00000000000000# Timer.py # # Copyright (C) 2011 Carlos Garcia Campos # # 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from __future__ import absolute_import, division, print_function from time import time, strftime, gmtime class Timer: def __init__(self, start = True): self._stop = None if start: self.start() else: self._start = None def start(self): self._start = time() def stop(self): self._stop = time() def elapsed(self): if self._start is None: return 0 if self._stop is None: return time() - self._start return self._stop - self._start def elapsed_str(self): h, m, s = [int(i) for i in strftime('%H:%M:%S', gmtime(self.elapsed())).split(':')] retval = "%d seconds" % (s) if h == 0 and m == 0: return retval retval = "%d minutes and %s" % (m, retval) if h == 0: return retval retval = "%d hours, %s" % (h, retval) return retval if __name__ == '__main__': from time import sleep t = Timer() sleep(5) print("Elapsed: %s" % (t.elapsed_str())) sleep(1) print("Elapsed: %s" % (t.elapsed_str())) t.start() sleep(2) t.stop() print("Elapsed: %s" % (t.elapsed_str())) sleep(2) print("Elapsed: %s" % (t.elapsed_str())) poppler-24.02.0/regtest/Utils.py000066400000000000000000000044151455701731300165010ustar00rootroot00000000000000# Utils.py # # Copyright (C) 2011 Carlos Garcia Campos # # 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from __future__ import absolute_import, division, print_function import os def get_document_paths_from_dir(docsdir, basedir = None): if basedir is None: basedir = docsdir paths = [] n_paths = 0 for root, dirs, files in os.walk(docsdir, False): for entry in files: if not entry.lower().endswith('.pdf'): continue test_path = os.path.join(root[len(basedir):], entry) paths.append(test_path.lstrip(os.path.sep)) n_paths += 1 paths.sort() return paths, n_paths def get_skipped_tests(docsdir): from Config import Config config = Config() if config.skipped_file: skipped_file = config.skipped_file elif os.path.exists(os.path.join(docsdir, 'Skipped')): skipped_file = os.path.join(docsdir, 'Skipped') else: return [] skipped = [] f = open(skipped_file, 'r') for line in f.readlines(): line = line.rstrip('\n \t\b\r') if not line or line[0] == '#': continue skipped.append(line) f.close() return skipped def get_passwords(docsdir): from Config import Config config = Config() if config.passwords_file: passwords_file = config.passwords_file elif os.path.exists(os.path.join(docsdir, 'Passwords')): passwords_file = os.path.join(docsdir, 'Passwords') else: return {} passwords = {} with open(passwords_file) as f: exec(f.read(), passwords) return passwords['passwords'] poppler-24.02.0/regtest/backends/000077500000000000000000000000001455701731300165755ustar00rootroot00000000000000poppler-24.02.0/regtest/backends/__init__.py000066400000000000000000000255051455701731300207150ustar00rootroot00000000000000# backends # # Copyright (C) 2011 Carlos Garcia Campos # # 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from __future__ import absolute_import, division, print_function import hashlib import os import select import shutil import errno from Config import Config from Printer import get_printer __all__ = [ 'register_backend', 'get_backend', 'get_all_backends', 'UnknownBackendError', 'Backend' ] class UnknownBackendError(Exception): '''Unknown backend type''' class Backend: def __init__(self, name, diff_ext = None): self._name = name self._diff_ext = diff_ext self._utilsdir = Config().utils_dir self.printer = get_printer() def get_name(self): return self._name def get_diff_ext(self): return self._diff_ext def __md5sum(self, ref_path): md5 = hashlib.md5() with open(ref_path,'rb') as f: for chunk in iter(lambda: f.read(128 * md5.block_size), b''): md5.update(chunk) return md5.hexdigest() def __should_have_checksum(self, entry): if not entry.startswith(self._name): return False name, ext = os.path.splitext(entry) return ext not in ('.md5', '.crashed', '.failed', '.stderr'); def create_checksums(self, refs_path, delete_refs = False): path = os.path.join(refs_path, self._name) md5_file = open(path + '.md5', 'w') for entry in sorted(os.listdir(refs_path)): if not self.__should_have_checksum(entry): continue ref_path = os.path.join(refs_path, entry) md5_file.write("%s %s\n" % (self.__md5sum(ref_path), ref_path)) if delete_refs: os.remove(ref_path) md5_file.close() def compare_checksums(self, refs_path, out_path, remove_results = True, create_diffs = True, update_refs = False): retval = True md5_path = os.path.join(refs_path, self._name) md5_file = open(md5_path + '.md5', 'r') tests = os.listdir(out_path) result_md5 = [] for line in md5_file.readlines(): md5sum, ref_path = line.strip('\n').split(' ', 1) basename = os.path.basename(ref_path) if not self.__should_have_checksum(basename): continue if not basename in tests: retval = False self.printer.print_default("%s found in md5 ref file but missing in output dir %s" % (basename, out_path)) continue result_path = os.path.join(out_path, basename) result_md5sum = self.__md5sum(result_path); matched = md5sum == result_md5sum if update_refs: result_md5.append("%s %s\n" % (result_md5sum, ref_path)) if matched: if remove_results: os.remove(result_path) else: self.printer.print_default("Differences found in %s" % (basename)) if create_diffs: if not os.path.exists(ref_path): self.printer.print_default("Reference file %s not found, skipping diff for %s" % (ref_path, result_path)) else: try: self._create_diff(ref_path, result_path) except NotImplementedError: # Diff not supported by backend pass if update_refs: if os.path.exists(ref_path): self.printer.print_default("Updating image reference %s" % (ref_path)) shutil.copyfile(result_path, ref_path) retval = False md5_file.close() if update_refs and not retval: self.printer.print_default("Updating md5 reference %s" % (md5_path)) f = open(md5_path + '.md5.tmp', 'wb') f.writelines(result_md5) f.close() os.rename(md5_path + '.md5.tmp', md5_path + '.md5') for ref in ('.crashed', '.failed', '.stderr'): src = os.path.join(out_path, self._name + ref) dest = os.path.join(refs_path, self._name + ref) try: shutil.copyfile(src, dest) except IOError as e: if e.errno != errno.ENOENT: raise return retval def update_results(self, refs_path, out_path): if not self.has_md5(refs_path): path = os.path.join(refs_path, self._name) md5_file = open(path + '.md5', 'w') for entry in sorted(os.listdir(out_path)): if not self.__should_have_checksum(entry): continue result_path = os.path.join(out_path, entry) ref_path = os.path.join(refs_path, entry) md5_file.write("%s %s\n" % (self.__md5sum(result_path), ref_path)) shutil.copyfile(result_path, ref_path) md5_file.close() for ref in ('.crashed', '.failed', '.stderr'): result_path = os.path.join(out_path, self._name + ref) ref_path = os.path.join(refs_path, self._name + ref) if os.path.exists(result_path): shutil.copyfile(result_path, ref_path) elif os.path.exists(ref_path): os.remove(ref_path) def get_ref_names(self, refs_path): retval = [] md5_path = os.path.join(refs_path, self._name) md5_file = open(md5_path + '.md5', 'r') for line in md5_file.readlines(): md5sum, ref_path = line.strip('\n').split(' ', 1) basename = os.path.basename(ref_path) if not self.__should_have_checksum(basename): continue retval.append(basename) md5_file.close() return retval def has_md5(self, test_path): return os.path.exists(os.path.join(test_path, self._name + '.md5')) def is_crashed(self, test_path): return os.path.exists(os.path.join(test_path, self._name + '.crashed')) def is_failed(self, test_path): failed_path = os.path.join(test_path, self._name + '.failed') if not os.path.exists(failed_path): return 0 f = open(failed_path, 'r') status = int(f.read()) f.close() return status def has_results(self, test_path): return self.has_md5(test_path) or self.is_crashed(test_path) or self.is_failed(test_path) def get_stderr(self, test_path): return os.path.join(test_path, self._name + '.stderr') def has_stderr(self, test_path): return os.path.exists(self.get_stderr(test_path)) def has_diff(self, test_result): if not self._diff_ext: return False basename = os.path.basename(test_result) if not basename.startswith(self._name): return False return os.path.exists(test_result + self._diff_ext) def __create_failed_file_if_needed(self, status, out_path): if os.WIFEXITED(status) or os.WEXITSTATUS(status) == 0: return False failed_file = open(out_path + '.failed', 'w') failed_file.write("%d" % (os.WEXITSTATUS(status))) failed_file.close() return True def __redirect_stderr_to_file(self, fd, out_path): stderr_file = None max_size = 1024 * 1024 read_set = [fd] while read_set: try: rlist, wlist, xlist = select.select(read_set, [], []) except select.error as e: continue if fd in rlist: try: chunk = os.read(fd, 1024) except OSError as e: if e.errno == errno.EIO: # Child process finished. chunk = '' else: raise e if chunk: if stderr_file is None: stderr_file = open(out_path + '.stderr', 'wb') if max_size > 0: stderr_file.write(chunk) max_size -= len(chunk) else: read_set.remove(fd) if stderr_file is not None: stderr_file.close() def _check_exit_status(self, p, out_path): self.__redirect_stderr_to_file(p.stderr.fileno(), out_path) status = p.wait() if not os.WIFEXITED(status): open(out_path + '.crashed', 'w').close() return False if self.__create_failed_file_if_needed(status, out_path): return False return True def _diff_png(self, ref_path, result_path): try: from PIL import Image, ImageChops except ImportError: raise NotImplementedError ref = Image.open(ref_path) result = Image.open(result_path) diff = ImageChops.difference(ref, result) diff.save(result_path + '.diff.png', 'png') def _create_diff(self, ref_path, result_path): raise NotImplementedError def create_refs(self, doc_path, refs_path, password = None): raise NotImplementedError _backends = {} def register_backend(backend_name, backend_class): _backends[backend_name] = backend_class def _get_backend(backend_name): if backend_name not in _backends: try: __import__('backends.%s' % backend_name) except ImportError: pass if backend_name not in _backends: raise UnknownBackendError('Backend %s does not exist' % backend_name) return _backends[backend_name] def get_backend(backend_name): backend_class = _get_backend(backend_name) return backend_class(backend_name) def get_all_backends(): backends = [] thisdir = os.path.abspath(os.path.dirname(__file__)) for fname in os.listdir(os.path.join(thisdir)): name, ext = os.path.splitext(fname) if not ext == '.py': continue try: __import__('backends.%s' % name) except ImportError: continue if name in _backends: backends.append(_backends[name](name)) return backends poppler-24.02.0/regtest/backends/cairo.py000066400000000000000000000031441455701731300202460ustar00rootroot00000000000000# cairo.py # # Copyright (C) 2011 Carlos Garcia Campos # # 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from __future__ import absolute_import, division, print_function from backends import Backend, register_backend import subprocess import os class Cairo(Backend): def __init__(self, name): Backend.__init__(self, name, '.diff.png') self._pdftocairo = os.path.join(self._utilsdir, 'pdftocairo'); def create_refs(self, doc_path, refs_path, password = None): out_path = os.path.join(refs_path, 'cairo') cmd = [self._pdftocairo, '-cropbox', '-r', '72', '-png', doc_path, out_path] if password is not None: cmd.extend(['-opw', password, '-upw', password]) p = subprocess.Popen(cmd, stderr = subprocess.PIPE) return self._check_exit_status(p, out_path) def _create_diff(self, ref_path, result_path): self._diff_png(ref_path, result_path) register_backend('cairo', Cairo) poppler-24.02.0/regtest/backends/postscript.py000066400000000000000000000027551455701731300213720ustar00rootroot00000000000000# postscript.py # # Copyright (C) 2011 Carlos Garcia Campos # # 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from __future__ import absolute_import, division, print_function from backends import Backend, register_backend import subprocess import os class PostScript(Backend): def __init__(self, name): Backend.__init__(self, name) self._pdftops = os.path.join(self._utilsdir, 'pdftops'); def create_refs(self, doc_path, refs_path, password = None): out_path = os.path.join(refs_path, 'postscript') cmd = [self._pdftops, doc_path, out_path + '.ps'] if password is not None: cmd.extend(['-opw', password, '-upw', password]) p = subprocess.Popen(cmd, stderr = subprocess.PIPE) return self._check_exit_status(p, out_path) register_backend('postscript', PostScript) poppler-24.02.0/regtest/backends/splash.py000066400000000000000000000031431455701731300204420ustar00rootroot00000000000000# splash.py # # Copyright (C) 2011 Carlos Garcia Campos # # 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from __future__ import absolute_import, division, print_function from backends import Backend, register_backend import subprocess import os class Splash(Backend): def __init__(self, name): Backend.__init__(self, name, '.diff.png') self._pdftoppm = os.path.join(self._utilsdir, 'pdftoppm'); def create_refs(self, doc_path, refs_path, password = None): out_path = os.path.join(refs_path, 'splash') cmd = [self._pdftoppm, '-cropbox', '-r', '72', '-png', doc_path, out_path] if password is not None: cmd.extend(['-opw', password, '-upw', password]) p = subprocess.Popen(cmd, stderr = subprocess.PIPE) return self._check_exit_status(p, out_path) def _create_diff(self, ref_path, result_path): self._diff_png(ref_path, result_path) register_backend('splash', Splash) poppler-24.02.0/regtest/backends/text.py000066400000000000000000000035631455701731300201420ustar00rootroot00000000000000# text.py # # Copyright (C) 2011 Carlos Garcia Campos # # 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from __future__ import absolute_import, division, print_function from backends import Backend, register_backend import subprocess import os class Text(Backend): def __init__(self, name): Backend.__init__(self, name, '.diff') self._pdftotext = os.path.join(self._utilsdir, 'pdftotext'); def create_refs(self, doc_path, refs_path, password = None): out_path = os.path.join(refs_path, 'text') cmd = [self._pdftotext, doc_path, out_path + '.txt'] if password is not None: cmd.extend(['-opw', password, '-upw', password]) p = subprocess.Popen(cmd, stderr = subprocess.PIPE) return self._check_exit_status(p, out_path) def _create_diff(self, ref_path, result_path): import difflib ref = open(ref_path, 'r') result = open(result_path, 'r') diff = difflib.unified_diff(ref.readlines(), result.readlines(), ref_path, result_path) ref.close() result.close() diff_file = open(result_path + '.diff', 'w') diff_file.writelines(diff) diff_file.close() register_backend('text', Text) poppler-24.02.0/regtest/builder/000077500000000000000000000000001455701731300164515ustar00rootroot00000000000000poppler-24.02.0/regtest/builder/__init__.py000066400000000000000000000046361455701731300205730ustar00rootroot00000000000000# builder # # Copyright (C) 2012 Carlos Garcia Campos # # 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from __future__ import absolute_import, division, print_function from Config import Config import os import sys import subprocess __all__ = [ 'register_builder', 'get_builder', 'UnknownBuilderError', 'Builder' ] class UnknownBuilderError(Exception): '''Unknown builder''' class Builder: def __init__(self): self.config = Config() self._srcdir = self.config.src_dir self._prefix = self.config.prefix def number_of_cpus(self): if not sys.platform.startswith("linux"): # TODO return 0 n_cpus = subprocess.Popen(['nproc'], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0] if n_cpus: return int(n_cpus) n_cpus = 0 f = open('/proc/cpuinfo', 'r') for line in f.readlines(): if 'processor' in line: n_cpus += 1 f.close() return n_cpus def _configure(self): raise NotImplementedError def _build(self): raise NotImplementedError def build(self): self._configure() self._build() _builders = {} def register_builder(builder_name, builder_class): _builders[builder_name] = builder_class def _get_builder(builder_name): if builder_name not in _builders: try: __import__('builder.%s' % builder_name) except ImportError: pass if builder_name not in _builders: raise UnknownBuilderError('Invalid %s builder' % builder_name) return _builders[builder_name] def get_builder(builder_name): builder_class = _get_builder(builder_name) return builder_class() poppler-24.02.0/regtest/builder/autotools.py000066400000000000000000000046501455701731300210610ustar00rootroot00000000000000# autotools.py # # Copyright (C) 2012 Carlos Garcia Campos # # 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from __future__ import absolute_import, division, print_function from builder import Builder, register_builder import os import subprocess class Autotools(Builder): def _configure(self): autogen = os.path.join(self._srcdir, 'autogen.sh') cmd = [autogen, '--prefix=%s' % (self._prefix), '--enable-utils'] # Disable frontends and tests cmd.extend(['--disable-poppler-glib', '--disable-poppler-qt4', '--disable-poppler-cpp', '--disable-gtk-test']) backends = self.config.backends if backends: # Disable backends. Text and ps can't be disabled. if 'cairo' not in backends: cmd.append('--disable-cairo-output') if 'splash' not in backends: cmd.append('--disable-splash-output') else: cmd.extend(['--enable-cairo-output', '--enable-splash-output']) p = subprocess.Popen(cmd, cwd=self._srcdir) status = p.wait() if not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 0: raise Exception('Command %s returned non-zero exit status %d' % (str(cmd), status)) def _build(self): make = os.environ.get('MAKE', 'make') cmd = [make] n_cpus = self.number_of_cpus() if n_cpus: cmd.append('-j%d' % (n_cpus)) p = subprocess.Popen(cmd, cwd=self._srcdir) status = p.wait() if not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 0: raise Exception('Command %s returned non-zero exit status %d' % (str(cmd), status)) register_builder('autotools', Autotools) poppler-24.02.0/regtest/commands/000077500000000000000000000000001455701731300166245ustar00rootroot00000000000000poppler-24.02.0/regtest/commands/__init__.py000066400000000000000000000054061455701731300207420ustar00rootroot00000000000000# commands # # Copyright (C) 2011 Carlos Garcia Campos # # 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from __future__ import absolute_import, division, print_function import argparse __all__ = [ 'register_command', 'print_help', 'run', 'UnknownCommandError', 'Command' ] class UnknownCommandError(Exception): '''Unknown command''' class Command: name = None usage_args = '[ options ... ]' description = None def __init__(self): self._parser = argparse.ArgumentParser( description = self.description, prog = 'poppler-regtest %s' % (self.name), usage = 'poppler-regtest %s %s' % (self.name, self.usage_args)) def _get_args_parser(self): return self._parser def execute(self, args): ns = self._parser.parse_args(args) return self.run(vars(ns)) def run(self, options): raise NotImplementedError _commands = {} def register_command(command_name, command_class): _commands[command_name] = command_class def _get_command(command_name): if command_name not in _commands: try: __import__('commands.%s' % command_name) except ImportError: pass if command_name not in _commands: raise UnknownCommandError('Invalid %s command' % command_name) return _commands[command_name] def run(args): command_class = _get_command(args[0]) command = command_class() return command.execute(args[1:]) def print_help(): import os thisdir = os.path.abspath(os.path.dirname(__file__)) for fname in os.listdir(os.path.join(thisdir)): name, ext = os.path.splitext(fname) if not ext == '.py': continue try: __import__('commands.%s' % name) except ImportError: pass print("Commands are:") commands = sorted((x.name, x.description) for x in _commands.values()) for name, description in commands: print(" %-15s %s" % (name, description)) print() print("For more information run 'poppler-regtest --help-command '") poppler-24.02.0/regtest/commands/create-refs.py000066400000000000000000000052341455701731300214020ustar00rootroot00000000000000# create-refs.py # # Copyright (C) 2011 Carlos Garcia Campos # # 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from __future__ import absolute_import, division, print_function from commands import Command, register_command from TestReferences import TestReferences from Timer import Timer from Config import Config from Printer import get_printer import os import tempfile class CreateRefs(Command): name = 'create-refs' usage_args = '[ options ... ] tests ' description = 'Create references for tests' def __init__(self): Command.__init__(self) parser = self._get_args_parser() parser.add_argument('--refs-dir', action = 'store', dest = 'refs_dir', default = os.path.join(tempfile.gettempdir(), 'refs'), help = 'Directory where the references will be created') parser.add_argument('-f', '--force', action = 'store_true', dest = 'force', default = False, help = 'Create references again for tests that already have references') parser.add_argument('-c', '--checksums-only', action = 'store_true', dest = 'checksums_only', default = False, help = 'Leave only checksum files in references dir, other files will be deleted') parser.add_argument('tests') def run(self, options): config = Config() config.force = options['force'] config.checksums_only = options['checksums_only'] t = Timer() doc = options['tests'] if os.path.isdir(doc): docs_dir = doc else: docs_dir = os.path.dirname(doc) refs = TestReferences(docs_dir, options['refs_dir']) if doc == docs_dir: refs.create_refs() else: refs.create_refs_for_file(os.path.basename(doc)) get_printer().printout_ln("Refs created in %s" % (t.elapsed_str())) return 0 register_command('create-refs', CreateRefs) poppler-24.02.0/regtest/commands/create-report.py000066400000000000000000000055251455701731300217610ustar00rootroot00000000000000# create-report.py # # Copyright (C) 2012 Carlos Garcia Campos # # 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from __future__ import absolute_import, division, print_function from commands import Command, register_command from HTMLReport import HTMLReport from Config import Config import os import tempfile class CreateReport(Command): name = 'create-report' usage_args = '[ options ... ] tests ' description = 'Create report of test results' def __init__(self): Command.__init__(self) parser = self._get_args_parser() parser.add_argument('--refs-dir', action = 'store', dest = 'refs_dir', default = os.path.join(tempfile.gettempdir(), 'refs'), help = 'Directory containing the references') parser.add_argument('-o', '--out-dir', action = 'store', dest = 'out_dir', default = os.path.join(tempfile.gettempdir(), 'out'), help = 'Directory containing the results') parser.add_argument('-p', '--pretty-diff', action = 'store_true', dest = 'pretty_diff', default = False, help = 'Include pretty diff output') parser.add_argument('-n', '--no-browser', action = 'store_false', dest = 'launch_browser', default = True, help = 'Do not launch a web browser with the results') parser.add_argument('--no-absolute-paths', action = 'store_false', dest = 'use_abs_paths', default = True, help = 'Do use absolute paths in the generated HTML') parser.add_argument('tests') def run(self, options): config = Config() config.pretty_diff = options['pretty_diff'] config.abs_paths = options['use_abs_paths'] doc = options['tests'] if os.path.isdir(doc): docs_dir = doc else: docs_dir = os.path.dirname(doc) report = HTMLReport(docs_dir, options['refs_dir'], options['out_dir']) report.create(options['launch_browser']) return 0 register_command('create-report', CreateReport) poppler-24.02.0/regtest/commands/download-files.py000066400000000000000000000054771455701731300221220ustar00rootroot00000000000000# download-files.py # # Copyright (C) 2022 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 # the Free Software Foundation; either version 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from __future__ import absolute_import, division, print_function from commands import Command, register_command from Timer import Timer from Printer import get_printer import os import tempfile import urllib.request class DownloadFiles(Command): name = 'download-files' usage_args = '[ options ... ] file-with-files ' description = 'Downloads files listed in the given file. For each file the first word is the output filename and the second is the url to download from' def __init__(self): Command.__init__(self) parser = self._get_args_parser() parser.add_argument('--files-dir', action = 'store', dest = 'files_dir', default = os.path.join(tempfile.gettempdir(), 'files'), help = 'Directory where the files will be downloaded') parser.add_argument('-f', '--force', action = 'store_true', dest = 'force', default = False, help = 'Create references again for tests that already have references') parser.add_argument('file-with-files') def run(self, options): t = Timer() filepath = options['file-with-files'] files_dir = options['files_dir'] os.makedirs(files_dir, exist_ok=True) f = open(filepath, "r") for line in f: splitted_line = line.split() if not len(splitted_line) == 2: get_printer().printout_ln("Malformed line %s" % line) return 1 target_path = os.path.join(files_dir, splitted_line[0]); if os.path.exists(target_path) and not options['force']: get_printer().printout_ln("Skipping %s already exists" % splitted_line[0]) else: get_printer().printout_ln("Downloading %s to %s" % (splitted_line[1] , target_path)) urllib.request.urlretrieve(splitted_line[1], target_path) get_printer().printout_ln("Files downloaded in %s" % (t.elapsed_str())) return 0 register_command('download-files', DownloadFiles) poppler-24.02.0/regtest/commands/find-regression.py000066400000000000000000000066601455701731300223040ustar00rootroot00000000000000# find-regression.py # # Copyright (C) 2012 Carlos Garcia Campos # # 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from __future__ import absolute_import, division, print_function from commands import Command, register_command from Bisect import Bisect from Timer import Timer from Config import Config from Printer import get_printer import os import tempfile class FindRegression(Command): name = 'find-regression' usage_args = '[ options ... ] test ' description = 'Find revision that introduced a regression for the given test' def __init__(self): Command.__init__(self) parser = self._get_args_parser() parser.add_argument('--refs-dir', action = 'store', dest = 'refs_dir', default = os.path.join(tempfile.gettempdir(), 'refs'), help = 'Directory containing the references') parser.add_argument('-o', '--out-dir', action = 'store', dest = 'out_dir', default = os.path.join(tempfile.gettempdir(), 'out'), help = 'Directory containing the results') parser.add_argument('--src-dir', action = 'store', dest = 'src_dir', default = os.path.abspath("../"), help = 'Directory of poppler sources') parser.add_argument('--builder', action = 'store', dest = 'builder', choices=['autotools'], default = 'autotools', help = 'Build system to use') parser.add_argument('--prefix', action = 'store', dest = 'prefix', default = '/usr/local', help = 'Build prefix') parser.add_argument('--good', action = 'store', dest = 'good', metavar = 'REVISION', help = 'Good revision') parser.add_argument('--bad', action = 'store', dest = 'bad', default = 'HEAD', metavar = 'REVISION', help = 'Bad revision') parser.add_argument('test') def run(self, options): config = Config() config.src_dir = options['src_dir'] config.builder = options['builder'] config.prefix = options['prefix'] config.good = options['good'] config.bad = options['bad'] doc = options['test'] if not os.path.isfile(doc): get_printer().printerr("Invalid test %s: not a regulat file" % (doc)) return 1 t = Timer() bisect = Bisect(options['test'], options['refs_dir'], options['out_dir']) bisect.run() get_printer().printout_ln("Tests run in %s" % (t.elapsed_str())) return 0 register_command('find-regression', FindRegression) poppler-24.02.0/regtest/commands/run-tests.py000066400000000000000000000100261455701731300211410ustar00rootroot00000000000000# run-tests.py # # Copyright (C) 2011 Carlos Garcia Campos # # 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from __future__ import absolute_import, division, print_function from commands import Command, register_command from TestRun import TestRun from Timer import Timer from Config import Config from Printer import get_printer import os import tempfile class RunTests(Command): name = 'run-tests' usage_args = '[ options ... ] tests ' description = 'Run tests for documents' def __init__(self): Command.__init__(self) parser = self._get_args_parser() parser.add_argument('--refs-dir', action = 'store', dest = 'refs_dir', default = os.path.join(tempfile.gettempdir(), 'refs'), help = 'Directory containing the references') parser.add_argument('-o', '--out-dir', action = 'store', dest = 'out_dir', default = os.path.join(tempfile.gettempdir(), 'out'), help = 'Directory where test results will be created') parser.add_argument('--docs-dir', action = 'store', dest = 'docs_dir', help = 'Base documents directory') parser.add_argument('--keep-results', action = 'store_true', dest = 'keep_results', default = False, help = 'Do not remove result files for passing tests') parser.add_argument('--create-diffs', action = 'store_true', dest = 'create_diffs', default = False, help = 'Create diff files for failed tests') parser.add_argument('--update-refs', action = 'store_true', dest = 'update_refs', default = False, help = 'Update references for failed tests') parser.add_argument('--exit-after-n-failures', action = 'store', dest = 'max_failures', type=int, default = None, help = 'Exit after N failures. Ignored if --update-refs is present too') parser.add_argument('tests', metavar = 'TEST', nargs = '+', help = 'Tests directory or individual test to run') def run(self, options): config = Config() config.keep_results = options['keep_results'] config.create_diffs = options['create_diffs'] config.update_refs = options['update_refs'] t = Timer() docs = options['tests'] docs_dir = options['docs_dir'] if len(docs) == 1: if os.path.isdir(docs[0]): if docs_dir is None: docs_dir = docs[0] if docs_dir == docs[0]: docs = [] else: if docs_dir is None: docs_dir = os.path.dirname(docs[0]) else: if docs_dir is None: docs_dir = os.path.commonprefix(docs).rpartition(os.path.sep)[0] max_failures = None if not config.update_refs: max_failures = options['max_failures'] tests = TestRun(docs_dir, options['refs_dir'], options['out_dir'], max_failures) status = tests.run_tests(docs) tests.summary() get_printer().printout_ln("Tests run in %s" % (t.elapsed_str())) return status register_command('run-tests', RunTests) poppler-24.02.0/regtest/main.py000066400000000000000000000074561455701731300163350ustar00rootroot00000000000000# main.py # # Copyright (C) 2011 Carlos Garcia Campos # # 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from __future__ import absolute_import, division, print_function import sys import argparse import commands import backends import os from Config import Config from multiprocessing import cpu_count class ListAction(argparse.Action): def __call__(self, parser, namespace, values, option_string = None): setattr(namespace, self.dest, values.split(',')) class HelpAction(argparse.Action): def __call__(self, parser, namespace, values, option_string = None): if option_string == '--help-command': commands.run([values, '--help']) sys.exit(0) parser.print_help() commands.print_help() sys.exit(0) def main(args): n_cpus = cpu_count() parser = argparse.ArgumentParser( description = 'Poppler regression tests', prog = 'poppler-regtest', usage = '%(prog)s [options ...] command [command-options ...] tests', add_help = False) parser.add_argument('-h', '--help', action = HelpAction, nargs = 0) parser.add_argument('--help-command', metavar = 'COMMAND', action = HelpAction, help = 'Show help for a given command') parser.add_argument('-v', '--verbose', action = 'store_true', dest = 'verbose', default = False, help = 'Run in verbose mode') parser.add_argument('--utils-dir', action = 'store', dest = 'utils_dir', default = os.path.abspath("../build/utils"), help = 'Directory of poppler utils used for the tests') parser.add_argument('-b', '--backends', action = ListAction, dest = 'backends', help = 'List of backends that will be used (separated by comma)') parser.add_argument('--skip', metavar = 'FILE', action = 'store', dest = 'skipped_file', help = 'File containing tests to skip') parser.add_argument('-p', '--passwords', metavar = 'FILE', action = 'store', dest = 'passwords_file', help = 'File containing the documents passwords') parser.add_argument('-t', '--threads', action = 'store', dest = 'threads', type = int, default = n_cpus, help = 'Number of worker threads (Default: %d)' % n_cpus) ns, args = parser.parse_known_args(args) if not args: parser.print_help() sys.exit(0) c = Config(vars(ns)) if c.threads <= 0: c.threads = n_cpus - c.threads try: return commands.run(args) except commands.UnknownCommandError: sys.stderr.write("Unknown command: %s\n" % (args[0])) commands.print_help() return 1 except backends.UnknownBackendError as e: sys.stderr.write(str(e) + "\n") sys.stdout.write("Backends are: %s\n" % (", ".join([backend.get_name() for backend in backends.get_all_backends()]))) return 1 if __name__ == '__main__': import sys sys.exit(main(sys.argv[1:])) poppler-24.02.0/regtest/poppler-regtest000077500000000000000000000001211455701731300200770ustar00rootroot00000000000000#!/usr/bin/env python import sys import main sys.exit(main.main(sys.argv[1:])) poppler-24.02.0/splash/000077500000000000000000000000001455701731300146405ustar00rootroot00000000000000poppler-24.02.0/splash/.gitignore000066400000000000000000000000741455701731300166310ustar00rootroot00000000000000.cvsignore .deps .libs Makefile Makefile.in *.la *.lo *.loT poppler-24.02.0/splash/Splash.cc000066400000000000000000007000431455701731300164050ustar00rootroot00000000000000//======================================================================== // // Splash.cc // //======================================================================== //======================================================================== // // 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) 2005-2023 Albert Astals Cid // Copyright (C) 2005 Marco Pesenti Gritti // Copyright (C) 2010-2016 Thomas Freitag // Copyright (C) 2010 Christian Feuersänger // Copyright (C) 2011-2013, 2015 William Bader // Copyright (C) 2012 Markus Trippelsdorf // Copyright (C) 2012, 2017 Adrian Johnson // Copyright (C) 2012 Matthias Kramm // Copyright (C) 2018, 2019 Stefan Brüns // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019, 2020 Oliver Sander // Copyright (C) 2019 Marek Kasik // Copyright (C) 2020 Tobias Deiminger // Copyright (C) 2021 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 // //======================================================================== #include #include #include #include #include #include #include "goo/gmem.h" #include "goo/GooLikely.h" #include "poppler/GfxState.h" #include "poppler/Error.h" #include "SplashErrorCodes.h" #include "SplashMath.h" #include "SplashBitmap.h" #include "SplashState.h" #include "SplashPath.h" #include "SplashXPath.h" #include "SplashXPathScanner.h" #include "SplashPattern.h" #include "SplashScreen.h" #include "SplashFont.h" #include "SplashGlyphBitmap.h" #include "Splash.h" #include // the MSVC math.h doesn't define this #ifndef M_PI # define M_PI 3.14159265358979323846 #endif //------------------------------------------------------------------------ #define splashAAGamma 1.5 // distance of Bezier control point from center for circle approximation // = (4 * (sqrt(2) - 1) / 3) * r #define bezierCircle ((SplashCoord)0.55228475) #define bezierCircle2 ((SplashCoord)(0.5 * 0.55228475)) // Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result. static inline unsigned char div255(int x) { return (unsigned char)((x + (x >> 8) + 0x80) >> 8); } // Clip x to lie in [0, 255]. static inline unsigned char clip255(int x) { return x < 0 ? 0 : x > 255 ? 255 : x; } template inline void Guswap(T &a, T &b) { T tmp = a; a = b; b = tmp; } // The PDF spec says that all pixels whose *centers* lie within the // image target region get painted, so we want to round n+0.5 down to // n. But this causes problems, e.g., with PDF files that fill a // rectangle with black and then draw an image to the exact same // rectangle, so we instead use the fill scan conversion rule. // However, the correct rule works better for glyphs, so we also // provide that option in fillImageMask. #if 0 static inline int imgCoordMungeLower(SplashCoord x) { return splashCeil(x + 0.5) - 1; } static inline int imgCoordMungeUpper(SplashCoord x) { return splashCeil(x + 0.5) - 1; } #else static inline int imgCoordMungeLower(SplashCoord x) { return splashFloor(x); } static inline int imgCoordMungeUpper(SplashCoord x) { return splashFloor(x) + 1; } static inline int imgCoordMungeLowerC(SplashCoord x, bool glyphMode) { return glyphMode ? (splashCeil(x + 0.5) - 1) : splashFloor(x); } static inline int imgCoordMungeUpperC(SplashCoord x, bool glyphMode) { return glyphMode ? (splashCeil(x + 0.5) - 1) : (splashFloor(x) + 1); } #endif // Used by drawImage and fillImageMask to divide the target // quadrilateral into sections. struct ImageSection { int y0, y1; // actual y range int ia0, ia1; // vertex indices for edge A int ib0, ib1; // vertex indices for edge A SplashCoord xa0, ya0, xa1, ya1; // edge A SplashCoord dxdya; // slope of edge A SplashCoord xb0, yb0, xb1, yb1; // edge B SplashCoord dxdyb; // slope of edge B }; //------------------------------------------------------------------------ // SplashPipe //------------------------------------------------------------------------ #define splashPipeMaxStages 9 struct SplashPipe { // pixel coordinates int x, y; // source pattern SplashPattern *pattern; // source alpha and color unsigned char aInput; bool usesShape; SplashColorPtr cSrc; SplashColor cSrcVal = {}; // non-isolated group alpha0 unsigned char *alpha0Ptr; // knockout groups bool knockout; unsigned char knockoutOpacity; // soft mask SplashColorPtr softMaskPtr; // destination alpha and color SplashColorPtr destColorPtr; int destColorMask; unsigned char *destAlphaPtr; // shape unsigned char shape; // result alpha and color bool noTransparency; SplashPipeResultColorCtrl resultColorCtrl; // non-isolated group correction bool nonIsolatedGroup; // the "run" function void (Splash::*run)(SplashPipe *pipe); }; SplashPipeResultColorCtrl Splash::pipeResultColorNoAlphaBlend[] = { splashPipeResultColorNoAlphaBlendMono, splashPipeResultColorNoAlphaBlendMono, splashPipeResultColorNoAlphaBlendRGB, splashPipeResultColorNoAlphaBlendRGB, splashPipeResultColorNoAlphaBlendRGB, splashPipeResultColorNoAlphaBlendCMYK, splashPipeResultColorNoAlphaBlendDeviceN }; SplashPipeResultColorCtrl Splash::pipeResultColorAlphaNoBlend[] = { splashPipeResultColorAlphaNoBlendMono, splashPipeResultColorAlphaNoBlendMono, splashPipeResultColorAlphaNoBlendRGB, splashPipeResultColorAlphaNoBlendRGB, splashPipeResultColorAlphaNoBlendRGB, splashPipeResultColorAlphaNoBlendCMYK, splashPipeResultColorAlphaNoBlendDeviceN }; SplashPipeResultColorCtrl Splash::pipeResultColorAlphaBlend[] = { splashPipeResultColorAlphaBlendMono, splashPipeResultColorAlphaBlendMono, splashPipeResultColorAlphaBlendRGB, splashPipeResultColorAlphaBlendRGB, splashPipeResultColorAlphaBlendRGB, splashPipeResultColorAlphaBlendCMYK, splashPipeResultColorAlphaBlendDeviceN }; //------------------------------------------------------------------------ static void blendXor(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; for (i = 0; i < splashColorModeNComps[cm]; ++i) { blend[i] = src[i] ^ dest[i]; } } //------------------------------------------------------------------------ // pipeline //------------------------------------------------------------------------ inline void Splash::pipeInit(SplashPipe *pipe, int x, int y, SplashPattern *pattern, SplashColorPtr cSrc, unsigned char aInput, bool usesShape, bool nonIsolatedGroup, bool knockout, unsigned char knockoutOpacity) { pipeSetXY(pipe, x, y); pipe->pattern = nullptr; // source color if (pattern) { if (pattern->isStatic()) { pattern->getColor(x, y, pipe->cSrcVal); } else { pipe->pattern = pattern; } pipe->cSrc = pipe->cSrcVal; } else { pipe->cSrc = cSrc; } // source alpha pipe->aInput = aInput; pipe->usesShape = usesShape; pipe->shape = 0; // knockout pipe->knockout = knockout; pipe->knockoutOpacity = knockoutOpacity; // result alpha if (aInput == 255 && !state->softMask && !usesShape && !state->inNonIsolatedGroup && !nonIsolatedGroup) { pipe->noTransparency = true; } else { pipe->noTransparency = false; } // result color if (pipe->noTransparency) { // the !state->blendFunc case is handled separately in pipeRun pipe->resultColorCtrl = pipeResultColorNoAlphaBlend[bitmap->mode]; } else if (!state->blendFunc) { pipe->resultColorCtrl = pipeResultColorAlphaNoBlend[bitmap->mode]; } else { pipe->resultColorCtrl = pipeResultColorAlphaBlend[bitmap->mode]; } // non-isolated group correction pipe->nonIsolatedGroup = nonIsolatedGroup; // select the 'run' function pipe->run = &Splash::pipeRun; if (!pipe->pattern && pipe->noTransparency && !state->blendFunc) { if (bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr) { pipe->run = &Splash::pipeRunSimpleMono1; } else if (bitmap->mode == splashModeMono8 && pipe->destAlphaPtr) { pipe->run = &Splash::pipeRunSimpleMono8; } else if (bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr) { pipe->run = &Splash::pipeRunSimpleRGB8; } else if (bitmap->mode == splashModeXBGR8 && pipe->destAlphaPtr) { pipe->run = &Splash::pipeRunSimpleXBGR8; } else if (bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr) { pipe->run = &Splash::pipeRunSimpleBGR8; } else if (bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr) { pipe->run = &Splash::pipeRunSimpleCMYK8; } else if (bitmap->mode == splashModeDeviceN8 && pipe->destAlphaPtr) { pipe->run = &Splash::pipeRunSimpleDeviceN8; } } else if (!pipe->pattern && !pipe->noTransparency && !state->softMask && pipe->usesShape && !(state->inNonIsolatedGroup && alpha0Bitmap->alpha) && !state->blendFunc && !pipe->nonIsolatedGroup) { if (bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr) { pipe->run = &Splash::pipeRunAAMono1; } else if (bitmap->mode == splashModeMono8 && pipe->destAlphaPtr) { pipe->run = &Splash::pipeRunAAMono8; } else if (bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr) { pipe->run = &Splash::pipeRunAARGB8; } else if (bitmap->mode == splashModeXBGR8 && pipe->destAlphaPtr) { pipe->run = &Splash::pipeRunAAXBGR8; } else if (bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr) { pipe->run = &Splash::pipeRunAABGR8; } else if (bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr) { pipe->run = &Splash::pipeRunAACMYK8; } else if (bitmap->mode == splashModeDeviceN8 && pipe->destAlphaPtr) { pipe->run = &Splash::pipeRunAADeviceN8; } } } // general case void Splash::pipeRun(SplashPipe *pipe) { unsigned char aSrc, aDest, alphaI, alphaIm1, alpha0, aResult; SplashColor cSrcNonIso, cDest, cBlend; SplashColorPtr cSrc; unsigned char cResult0, cResult1, cResult2, cResult3; int t; int cp, mask; unsigned char cResult[SPOT_NCOMPS + 4]; //----- source color // static pattern: handled in pipeInit // fixed color: handled in pipeInit // dynamic pattern if (pipe->pattern) { if (!pipe->pattern->getColor(pipe->x, pipe->y, pipe->cSrcVal)) { pipeIncX(pipe); return; } if (bitmap->mode == splashModeCMYK8 || bitmap->mode == splashModeDeviceN8) { if (state->fillOverprint && state->overprintMode && pipe->pattern->isCMYK()) { unsigned int overprintMask = 15; if (pipe->cSrcVal[0] == 0) { overprintMask &= ~1; } if (pipe->cSrcVal[1] == 0) { overprintMask &= ~2; } if (pipe->cSrcVal[2] == 0) { overprintMask &= ~4; } if (pipe->cSrcVal[3] == 0) { overprintMask &= ~8; } state->overprintMask = overprintMask; } } } if (pipe->noTransparency && !state->blendFunc) { //----- write destination pixel switch (bitmap->mode) { case splashModeMono1: cResult0 = state->grayTransfer[pipe->cSrc[0]]; if (state->screen->test(pipe->x, pipe->y, cResult0)) { *pipe->destColorPtr |= pipe->destColorMask; } else { *pipe->destColorPtr &= ~pipe->destColorMask; } if (!(pipe->destColorMask >>= 1)) { pipe->destColorMask = 0x80; ++pipe->destColorPtr; } break; case splashModeMono8: *pipe->destColorPtr++ = state->grayTransfer[pipe->cSrc[0]]; break; case splashModeRGB8: *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]]; *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]]; *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]]; break; case splashModeXBGR8: *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]]; *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]]; *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]]; *pipe->destColorPtr++ = 255; break; case splashModeBGR8: *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]]; *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]]; *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]]; break; case splashModeCMYK8: if (state->overprintMask & 1) { pipe->destColorPtr[0] = (state->overprintAdditive) ? std::min(pipe->destColorPtr[0] + state->cmykTransferC[pipe->cSrc[0]], 255) : state->cmykTransferC[pipe->cSrc[0]]; } if (state->overprintMask & 2) { pipe->destColorPtr[1] = (state->overprintAdditive) ? std::min(pipe->destColorPtr[1] + state->cmykTransferM[pipe->cSrc[1]], 255) : state->cmykTransferM[pipe->cSrc[1]]; } if (state->overprintMask & 4) { pipe->destColorPtr[2] = (state->overprintAdditive) ? std::min(pipe->destColorPtr[2] + state->cmykTransferY[pipe->cSrc[2]], 255) : state->cmykTransferY[pipe->cSrc[2]]; } if (state->overprintMask & 8) { pipe->destColorPtr[3] = (state->overprintAdditive) ? std::min(pipe->destColorPtr[3] + state->cmykTransferK[pipe->cSrc[3]], 255) : state->cmykTransferK[pipe->cSrc[3]]; } pipe->destColorPtr += 4; break; case splashModeDeviceN8: mask = 1; for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) { if (state->overprintMask & mask) { pipe->destColorPtr[cp] = state->deviceNTransfer[cp][pipe->cSrc[cp]]; } mask <<= 1; } pipe->destColorPtr += (SPOT_NCOMPS + 4); break; } if (pipe->destAlphaPtr) { *pipe->destAlphaPtr++ = 255; } } else { //----- read destination pixel unsigned char *destColorPtr; if (pipe->shape && state->blendFunc && pipe->knockout && alpha0Bitmap != nullptr) { destColorPtr = alpha0Bitmap->data + (alpha0Y + pipe->y) * alpha0Bitmap->rowSize; switch (bitmap->mode) { case splashModeMono1: destColorPtr += (alpha0X + pipe->x) / 8; break; case splashModeMono8: destColorPtr += (alpha0X + pipe->x); break; case splashModeRGB8: case splashModeBGR8: destColorPtr += (alpha0X + pipe->x) * 3; break; case splashModeXBGR8: case splashModeCMYK8: destColorPtr += (alpha0X + pipe->x) * 4; break; case splashModeDeviceN8: destColorPtr += (alpha0X + pipe->x) * (SPOT_NCOMPS + 4); break; } } else { destColorPtr = pipe->destColorPtr; } switch (bitmap->mode) { case splashModeMono1: cDest[0] = (*destColorPtr & pipe->destColorMask) ? 0xff : 0x00; break; case splashModeMono8: cDest[0] = *destColorPtr; break; case splashModeRGB8: cDest[0] = destColorPtr[0]; cDest[1] = destColorPtr[1]; cDest[2] = destColorPtr[2]; break; case splashModeXBGR8: cDest[0] = destColorPtr[2]; cDest[1] = destColorPtr[1]; cDest[2] = destColorPtr[0]; cDest[3] = 255; break; case splashModeBGR8: cDest[0] = destColorPtr[2]; cDest[1] = destColorPtr[1]; cDest[2] = destColorPtr[0]; break; case splashModeCMYK8: cDest[0] = destColorPtr[0]; cDest[1] = destColorPtr[1]; cDest[2] = destColorPtr[2]; cDest[3] = destColorPtr[3]; break; case splashModeDeviceN8: for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) { cDest[cp] = destColorPtr[cp]; } break; } if (pipe->destAlphaPtr) { aDest = *pipe->destAlphaPtr; } else { aDest = 0xff; } //----- source alpha if (state->softMask) { if (pipe->usesShape) { aSrc = div255(div255(pipe->aInput * *pipe->softMaskPtr++) * pipe->shape); } else { aSrc = div255(pipe->aInput * *pipe->softMaskPtr++); } } else if (pipe->usesShape) { aSrc = div255(pipe->aInput * pipe->shape); } else { aSrc = pipe->aInput; } //----- non-isolated group correction if (pipe->nonIsolatedGroup) { // This path is only used when Splash::composite() is called to // composite a non-isolated group onto the backdrop. In this // case, pipe->shape is the source (group) alpha. if (pipe->shape == 0) { // this value will be multiplied by zero later, so it doesn't // matter what we use cSrc = pipe->cSrc; } else { t = (aDest * 255) / pipe->shape - aDest; switch (bitmap->mode) { case splashModeDeviceN8: for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) { cSrcNonIso[cp] = clip255(pipe->cSrc[cp] + ((pipe->cSrc[cp] - cDest[cp]) * t) / 255); } break; case splashModeCMYK8: for (cp = 0; cp < 4; cp++) { cSrcNonIso[cp] = clip255(pipe->cSrc[cp] + ((pipe->cSrc[cp] - cDest[cp]) * t) / 255); } break; case splashModeXBGR8: cSrcNonIso[3] = 255; // fallthrough case splashModeRGB8: case splashModeBGR8: cSrcNonIso[2] = clip255(pipe->cSrc[2] + ((pipe->cSrc[2] - cDest[2]) * t) / 255); cSrcNonIso[1] = clip255(pipe->cSrc[1] + ((pipe->cSrc[1] - cDest[1]) * t) / 255); // fallthrough case splashModeMono1: case splashModeMono8: cSrcNonIso[0] = clip255(pipe->cSrc[0] + ((pipe->cSrc[0] - cDest[0]) * t) / 255); break; } cSrc = cSrcNonIso; // knockout: remove backdrop color if (pipe->knockout && pipe->shape >= pipe->knockoutOpacity) { aDest = 0; } } } else { cSrc = pipe->cSrc; } //----- blend function if (state->blendFunc) { if (bitmap->mode == splashModeDeviceN8) { for (int k = 4; k < 4 + SPOT_NCOMPS; k++) { cBlend[k] = 0; } } (*state->blendFunc)(cSrc, cDest, cBlend, bitmap->mode); } //----- result alpha and non-isolated group element correction if (pipe->noTransparency) { alphaI = alphaIm1 = aResult = 255; } else { aResult = aSrc + aDest - div255(aSrc * aDest); // alphaI = alpha_i // alphaIm1 = alpha_(i-1) if (pipe->alpha0Ptr) { alpha0 = *pipe->alpha0Ptr++; alphaI = aResult + alpha0 - div255(aResult * alpha0); alphaIm1 = alpha0 + aDest - div255(alpha0 * aDest); } else { alphaI = aResult; alphaIm1 = aDest; } } //----- result color cResult0 = cResult1 = cResult2 = cResult3 = 0; // make gcc happy switch (pipe->resultColorCtrl) { case splashPipeResultColorNoAlphaBlendMono: cResult0 = state->grayTransfer[div255((255 - aDest) * cSrc[0] + aDest * cBlend[0])]; break; case splashPipeResultColorNoAlphaBlendRGB: cResult0 = state->rgbTransferR[div255((255 - aDest) * cSrc[0] + aDest * cBlend[0])]; cResult1 = state->rgbTransferG[div255((255 - aDest) * cSrc[1] + aDest * cBlend[1])]; cResult2 = state->rgbTransferB[div255((255 - aDest) * cSrc[2] + aDest * cBlend[2])]; break; case splashPipeResultColorNoAlphaBlendCMYK: cResult0 = state->cmykTransferC[div255((255 - aDest) * cSrc[0] + aDest * cBlend[0])]; cResult1 = state->cmykTransferM[div255((255 - aDest) * cSrc[1] + aDest * cBlend[1])]; cResult2 = state->cmykTransferY[div255((255 - aDest) * cSrc[2] + aDest * cBlend[2])]; cResult3 = state->cmykTransferK[div255((255 - aDest) * cSrc[3] + aDest * cBlend[3])]; break; case splashPipeResultColorNoAlphaBlendDeviceN: for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) { cResult[cp] = state->deviceNTransfer[cp][div255((255 - aDest) * cSrc[cp] + aDest * cBlend[cp])]; } break; case splashPipeResultColorAlphaNoBlendMono: if (alphaI == 0) { cResult0 = 0; } else { cResult0 = state->grayTransfer[((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0]) / alphaI]; } break; case splashPipeResultColorAlphaNoBlendRGB: if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; } else { cResult0 = state->rgbTransferR[((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0]) / alphaI]; cResult1 = state->rgbTransferG[((alphaI - aSrc) * cDest[1] + aSrc * cSrc[1]) / alphaI]; cResult2 = state->rgbTransferB[((alphaI - aSrc) * cDest[2] + aSrc * cSrc[2]) / alphaI]; } break; case splashPipeResultColorAlphaNoBlendCMYK: if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; cResult3 = 0; } else { cResult0 = state->cmykTransferC[((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0]) / alphaI]; cResult1 = state->cmykTransferM[((alphaI - aSrc) * cDest[1] + aSrc * cSrc[1]) / alphaI]; cResult2 = state->cmykTransferY[((alphaI - aSrc) * cDest[2] + aSrc * cSrc[2]) / alphaI]; cResult3 = state->cmykTransferK[((alphaI - aSrc) * cDest[3] + aSrc * cSrc[3]) / alphaI]; } break; case splashPipeResultColorAlphaNoBlendDeviceN: if (alphaI == 0) { for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) { cResult[cp] = 0; } } else { for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) { cResult[cp] = state->deviceNTransfer[cp][((alphaI - aSrc) * cDest[cp] + aSrc * cSrc[cp]) / alphaI]; } } break; case splashPipeResultColorAlphaBlendMono: if (alphaI == 0) { cResult0 = 0; } else { cResult0 = state->grayTransfer[((alphaI - aSrc) * cDest[0] + aSrc * ((255 - alphaIm1) * cSrc[0] + alphaIm1 * cBlend[0]) / 255) / alphaI]; } break; case splashPipeResultColorAlphaBlendRGB: if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; } else { cResult0 = state->rgbTransferR[((alphaI - aSrc) * cDest[0] + aSrc * ((255 - alphaIm1) * cSrc[0] + alphaIm1 * cBlend[0]) / 255) / alphaI]; cResult1 = state->rgbTransferG[((alphaI - aSrc) * cDest[1] + aSrc * ((255 - alphaIm1) * cSrc[1] + alphaIm1 * cBlend[1]) / 255) / alphaI]; cResult2 = state->rgbTransferB[((alphaI - aSrc) * cDest[2] + aSrc * ((255 - alphaIm1) * cSrc[2] + alphaIm1 * cBlend[2]) / 255) / alphaI]; } break; case splashPipeResultColorAlphaBlendCMYK: if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; cResult3 = 0; } else { cResult0 = state->cmykTransferC[((alphaI - aSrc) * cDest[0] + aSrc * ((255 - alphaIm1) * cSrc[0] + alphaIm1 * cBlend[0]) / 255) / alphaI]; cResult1 = state->cmykTransferM[((alphaI - aSrc) * cDest[1] + aSrc * ((255 - alphaIm1) * cSrc[1] + alphaIm1 * cBlend[1]) / 255) / alphaI]; cResult2 = state->cmykTransferY[((alphaI - aSrc) * cDest[2] + aSrc * ((255 - alphaIm1) * cSrc[2] + alphaIm1 * cBlend[2]) / 255) / alphaI]; cResult3 = state->cmykTransferK[((alphaI - aSrc) * cDest[3] + aSrc * ((255 - alphaIm1) * cSrc[3] + alphaIm1 * cBlend[3]) / 255) / alphaI]; } break; case splashPipeResultColorAlphaBlendDeviceN: if (alphaI == 0) { for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) { cResult[cp] = 0; } } else { for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) { cResult[cp] = state->deviceNTransfer[cp][((alphaI - aSrc) * cDest[cp] + aSrc * ((255 - alphaIm1) * cSrc[cp] + alphaIm1 * cBlend[cp]) / 255) / alphaI]; } } break; } //----- write destination pixel switch (bitmap->mode) { case splashModeMono1: if (state->screen->test(pipe->x, pipe->y, cResult0)) { *pipe->destColorPtr |= pipe->destColorMask; } else { *pipe->destColorPtr &= ~pipe->destColorMask; } if (!(pipe->destColorMask >>= 1)) { pipe->destColorMask = 0x80; ++pipe->destColorPtr; } break; case splashModeMono8: *pipe->destColorPtr++ = cResult0; break; case splashModeRGB8: *pipe->destColorPtr++ = cResult0; *pipe->destColorPtr++ = cResult1; *pipe->destColorPtr++ = cResult2; break; case splashModeXBGR8: *pipe->destColorPtr++ = cResult2; *pipe->destColorPtr++ = cResult1; *pipe->destColorPtr++ = cResult0; *pipe->destColorPtr++ = 255; break; case splashModeBGR8: *pipe->destColorPtr++ = cResult2; *pipe->destColorPtr++ = cResult1; *pipe->destColorPtr++ = cResult0; break; case splashModeCMYK8: if (state->overprintMask & 1) { pipe->destColorPtr[0] = (state->overprintAdditive) ? std::min(pipe->destColorPtr[0] + cResult0, 255) : cResult0; } if (state->overprintMask & 2) { pipe->destColorPtr[1] = (state->overprintAdditive) ? std::min(pipe->destColorPtr[1] + cResult1, 255) : cResult1; } if (state->overprintMask & 4) { pipe->destColorPtr[2] = (state->overprintAdditive) ? std::min(pipe->destColorPtr[2] + cResult2, 255) : cResult2; } if (state->overprintMask & 8) { pipe->destColorPtr[3] = (state->overprintAdditive) ? std::min(pipe->destColorPtr[3] + cResult3, 255) : cResult3; } pipe->destColorPtr += 4; break; case splashModeDeviceN8: mask = 1; for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) { if (state->overprintMask & mask) { pipe->destColorPtr[cp] = cResult[cp]; } mask <<= 1; } pipe->destColorPtr += (SPOT_NCOMPS + 4); break; } if (pipe->destAlphaPtr) { *pipe->destAlphaPtr++ = aResult; } } ++pipe->x; } // special case: // !pipe->pattern && pipe->noTransparency && !state->blendFunc && // bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr) { void Splash::pipeRunSimpleMono1(SplashPipe *pipe) { unsigned char cResult0; //----- write destination pixel cResult0 = state->grayTransfer[pipe->cSrc[0]]; if (state->screen->test(pipe->x, pipe->y, cResult0)) { *pipe->destColorPtr |= pipe->destColorMask; } else { *pipe->destColorPtr &= ~pipe->destColorMask; } if (!(pipe->destColorMask >>= 1)) { pipe->destColorMask = 0x80; ++pipe->destColorPtr; } ++pipe->x; } // special case: // !pipe->pattern && pipe->noTransparency && !state->blendFunc && // bitmap->mode == splashModeMono8 && pipe->destAlphaPtr) { void Splash::pipeRunSimpleMono8(SplashPipe *pipe) { //----- write destination pixel *pipe->destColorPtr++ = state->grayTransfer[pipe->cSrc[0]]; *pipe->destAlphaPtr++ = 255; ++pipe->x; } // special case: // !pipe->pattern && pipe->noTransparency && !state->blendFunc && // bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr) { void Splash::pipeRunSimpleRGB8(SplashPipe *pipe) { //----- write destination pixel *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]]; *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]]; *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]]; *pipe->destAlphaPtr++ = 255; ++pipe->x; } // special case: // !pipe->pattern && pipe->noTransparency && !state->blendFunc && // bitmap->mode == splashModeXBGR8 && pipe->destAlphaPtr) { void Splash::pipeRunSimpleXBGR8(SplashPipe *pipe) { //----- write destination pixel *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]]; *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]]; *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]]; *pipe->destColorPtr++ = 255; *pipe->destAlphaPtr++ = 255; ++pipe->x; } // special case: // !pipe->pattern && pipe->noTransparency && !state->blendFunc && // bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr) { void Splash::pipeRunSimpleBGR8(SplashPipe *pipe) { //----- write destination pixel *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]]; *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]]; *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]]; *pipe->destAlphaPtr++ = 255; ++pipe->x; } // special case: // !pipe->pattern && pipe->noTransparency && !state->blendFunc && // bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr) { void Splash::pipeRunSimpleCMYK8(SplashPipe *pipe) { //----- write destination pixel if (state->overprintMask & 1) { pipe->destColorPtr[0] = (state->overprintAdditive) ? std::min(pipe->destColorPtr[0] + state->cmykTransferC[pipe->cSrc[0]], 255) : state->cmykTransferC[pipe->cSrc[0]]; } if (state->overprintMask & 2) { pipe->destColorPtr[1] = (state->overprintAdditive) ? std::min(pipe->destColorPtr[1] + state->cmykTransferM[pipe->cSrc[1]], 255) : state->cmykTransferM[pipe->cSrc[1]]; } if (state->overprintMask & 4) { pipe->destColorPtr[2] = (state->overprintAdditive) ? std::min(pipe->destColorPtr[2] + state->cmykTransferY[pipe->cSrc[2]], 255) : state->cmykTransferY[pipe->cSrc[2]]; } if (state->overprintMask & 8) { pipe->destColorPtr[3] = (state->overprintAdditive) ? std::min(pipe->destColorPtr[3] + state->cmykTransferK[pipe->cSrc[3]], 255) : state->cmykTransferK[pipe->cSrc[3]]; } pipe->destColorPtr += 4; *pipe->destAlphaPtr++ = 255; ++pipe->x; } // special case: // !pipe->pattern && pipe->noTransparency && !state->blendFunc && // bitmap->mode == splashModeDeviceN8 && pipe->destAlphaPtr) { void Splash::pipeRunSimpleDeviceN8(SplashPipe *pipe) { //----- write destination pixel int mask = 1; for (int cp = 0; cp < SPOT_NCOMPS + 4; cp++) { if (state->overprintMask & mask) { pipe->destColorPtr[cp] = state->deviceNTransfer[cp][pipe->cSrc[cp]]; } mask <<= 1; } pipe->destColorPtr += (SPOT_NCOMPS + 4); *pipe->destAlphaPtr++ = 255; ++pipe->x; } // special case: // !pipe->pattern && !pipe->noTransparency && !state->softMask && // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && // !pipe->nonIsolatedGroup && // bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr void Splash::pipeRunAAMono1(SplashPipe *pipe) { unsigned char aSrc; SplashColor cDest; unsigned char cResult0; //----- read destination pixel cDest[0] = (*pipe->destColorPtr & pipe->destColorMask) ? 0xff : 0x00; //----- source alpha aSrc = div255(pipe->aInput * pipe->shape); //----- result color // note: aDest = alpha2 = aResult = 0xff cResult0 = state->grayTransfer[(unsigned char)div255((0xff - aSrc) * cDest[0] + aSrc * pipe->cSrc[0])]; //----- write destination pixel if (state->screen->test(pipe->x, pipe->y, cResult0)) { *pipe->destColorPtr |= pipe->destColorMask; } else { *pipe->destColorPtr &= ~pipe->destColorMask; } if (!(pipe->destColorMask >>= 1)) { pipe->destColorMask = 0x80; ++pipe->destColorPtr; } ++pipe->x; } // special case: // !pipe->pattern && !pipe->noTransparency && !state->softMask && // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && // !pipe->nonIsolatedGroup && // bitmap->mode == splashModeMono8 && pipe->destAlphaPtr void Splash::pipeRunAAMono8(SplashPipe *pipe) { unsigned char aSrc, aDest, alpha2, aResult; SplashColor cDest; unsigned char cResult0; //----- read destination pixel cDest[0] = *pipe->destColorPtr; aDest = *pipe->destAlphaPtr; //----- source alpha aSrc = div255(pipe->aInput * pipe->shape); //----- result alpha and non-isolated group element correction aResult = aSrc + aDest - div255(aSrc * aDest); alpha2 = aResult; //----- result color if (alpha2 == 0) { cResult0 = 0; } else { cResult0 = state->grayTransfer[(unsigned char)(((alpha2 - aSrc) * cDest[0] + aSrc * pipe->cSrc[0]) / alpha2)]; } //----- write destination pixel *pipe->destColorPtr++ = cResult0; *pipe->destAlphaPtr++ = aResult; ++pipe->x; } // special case: // !pipe->pattern && !pipe->noTransparency && !state->softMask && // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && // !pipe->nonIsolatedGroup && // bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr void Splash::pipeRunAARGB8(SplashPipe *pipe) { unsigned char aSrc, aDest, alpha2, aResult; SplashColor cDest; unsigned char cResult0, cResult1, cResult2; //----- read destination alpha aDest = *pipe->destAlphaPtr; //----- source alpha aSrc = div255(pipe->aInput * pipe->shape); //----- result color if (aSrc == 255) { cResult0 = state->rgbTransferR[pipe->cSrc[0]]; cResult1 = state->rgbTransferG[pipe->cSrc[1]]; cResult2 = state->rgbTransferB[pipe->cSrc[2]]; aResult = 255; } else if (aSrc == 0 && aDest == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; aResult = 0; } else { //----- read destination pixel cDest[0] = pipe->destColorPtr[0]; cDest[1] = pipe->destColorPtr[1]; cDest[2] = pipe->destColorPtr[2]; //----- result alpha and non-isolated group element correction aResult = aSrc + aDest - div255(aSrc * aDest); alpha2 = aResult; cResult0 = state->rgbTransferR[(unsigned char)(((alpha2 - aSrc) * cDest[0] + aSrc * pipe->cSrc[0]) / alpha2)]; cResult1 = state->rgbTransferG[(unsigned char)(((alpha2 - aSrc) * cDest[1] + aSrc * pipe->cSrc[1]) / alpha2)]; cResult2 = state->rgbTransferB[(unsigned char)(((alpha2 - aSrc) * cDest[2] + aSrc * pipe->cSrc[2]) / alpha2)]; } //----- write destination pixel *pipe->destColorPtr++ = cResult0; *pipe->destColorPtr++ = cResult1; *pipe->destColorPtr++ = cResult2; *pipe->destAlphaPtr++ = aResult; ++pipe->x; } // special case: // !pipe->pattern && !pipe->noTransparency && !state->softMask && // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && // !pipe->nonIsolatedGroup && // bitmap->mode == splashModeXBGR8 && pipe->destAlphaPtr void Splash::pipeRunAAXBGR8(SplashPipe *pipe) { unsigned char aSrc, aDest, alpha2, aResult; SplashColor cDest; unsigned char cResult0, cResult1, cResult2; //----- read destination alpha aDest = *pipe->destAlphaPtr; //----- source alpha aSrc = div255(pipe->aInput * pipe->shape); //----- result color if (aSrc == 255) { cResult0 = state->rgbTransferR[pipe->cSrc[0]]; cResult1 = state->rgbTransferG[pipe->cSrc[1]]; cResult2 = state->rgbTransferB[pipe->cSrc[2]]; aResult = 255; } else if (aSrc == 0 && aDest == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; aResult = 0; } else { //----- read destination color cDest[0] = pipe->destColorPtr[2]; cDest[1] = pipe->destColorPtr[1]; cDest[2] = pipe->destColorPtr[0]; //----- result alpha and non-isolated group element correction aResult = aSrc + aDest - div255(aSrc * aDest); alpha2 = aResult; cResult0 = state->rgbTransferR[(unsigned char)(((alpha2 - aSrc) * cDest[0] + aSrc * pipe->cSrc[0]) / alpha2)]; cResult1 = state->rgbTransferG[(unsigned char)(((alpha2 - aSrc) * cDest[1] + aSrc * pipe->cSrc[1]) / alpha2)]; cResult2 = state->rgbTransferB[(unsigned char)(((alpha2 - aSrc) * cDest[2] + aSrc * pipe->cSrc[2]) / alpha2)]; } //----- write destination pixel *pipe->destColorPtr++ = cResult2; *pipe->destColorPtr++ = cResult1; *pipe->destColorPtr++ = cResult0; *pipe->destColorPtr++ = 255; *pipe->destAlphaPtr++ = aResult; ++pipe->x; } // special case: // !pipe->pattern && !pipe->noTransparency && !state->softMask && // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && // !pipe->nonIsolatedGroup && // bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr void Splash::pipeRunAABGR8(SplashPipe *pipe) { unsigned char aSrc, aDest, alpha2, aResult; SplashColor cDest; unsigned char cResult0, cResult1, cResult2; //----- read destination alpha aDest = *pipe->destAlphaPtr; //----- source alpha aSrc = div255(pipe->aInput * pipe->shape); //----- result color if (aSrc == 255) { cResult0 = state->rgbTransferR[pipe->cSrc[0]]; cResult1 = state->rgbTransferG[pipe->cSrc[1]]; cResult2 = state->rgbTransferB[pipe->cSrc[2]]; aResult = 255; } else if (aSrc == 0 && aDest == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; aResult = 0; } else { //----- read destination color cDest[0] = pipe->destColorPtr[2]; cDest[1] = pipe->destColorPtr[1]; cDest[2] = pipe->destColorPtr[0]; //----- result alpha and non-isolated group element correction aResult = aSrc + aDest - div255(aSrc * aDest); alpha2 = aResult; cResult0 = state->rgbTransferR[(unsigned char)(((alpha2 - aSrc) * cDest[0] + aSrc * pipe->cSrc[0]) / alpha2)]; cResult1 = state->rgbTransferG[(unsigned char)(((alpha2 - aSrc) * cDest[1] + aSrc * pipe->cSrc[1]) / alpha2)]; cResult2 = state->rgbTransferB[(unsigned char)(((alpha2 - aSrc) * cDest[2] + aSrc * pipe->cSrc[2]) / alpha2)]; } //----- write destination pixel *pipe->destColorPtr++ = cResult2; *pipe->destColorPtr++ = cResult1; *pipe->destColorPtr++ = cResult0; *pipe->destAlphaPtr++ = aResult; ++pipe->x; } // special case: // !pipe->pattern && !pipe->noTransparency && !state->softMask && // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && // !pipe->nonIsolatedGroup && // bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr void Splash::pipeRunAACMYK8(SplashPipe *pipe) { unsigned char aSrc, aDest, alpha2, aResult; SplashColor cDest; unsigned char cResult0, cResult1, cResult2, cResult3; //----- read destination pixel cDest[0] = pipe->destColorPtr[0]; cDest[1] = pipe->destColorPtr[1]; cDest[2] = pipe->destColorPtr[2]; cDest[3] = pipe->destColorPtr[3]; aDest = *pipe->destAlphaPtr; //----- source alpha aSrc = div255(pipe->aInput * pipe->shape); //----- result alpha and non-isolated group element correction aResult = aSrc + aDest - div255(aSrc * aDest); alpha2 = aResult; //----- result color if (alpha2 == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; cResult3 = 0; } else { cResult0 = state->cmykTransferC[(unsigned char)(((alpha2 - aSrc) * cDest[0] + aSrc * pipe->cSrc[0]) / alpha2)]; cResult1 = state->cmykTransferM[(unsigned char)(((alpha2 - aSrc) * cDest[1] + aSrc * pipe->cSrc[1]) / alpha2)]; cResult2 = state->cmykTransferY[(unsigned char)(((alpha2 - aSrc) * cDest[2] + aSrc * pipe->cSrc[2]) / alpha2)]; cResult3 = state->cmykTransferK[(unsigned char)(((alpha2 - aSrc) * cDest[3] + aSrc * pipe->cSrc[3]) / alpha2)]; } //----- write destination pixel if (state->overprintMask & 1) { pipe->destColorPtr[0] = (state->overprintAdditive && pipe->shape != 0) ? std::min(pipe->destColorPtr[0] + cResult0, 255) : cResult0; } if (state->overprintMask & 2) { pipe->destColorPtr[1] = (state->overprintAdditive && pipe->shape != 0) ? std::min(pipe->destColorPtr[1] + cResult1, 255) : cResult1; } if (state->overprintMask & 4) { pipe->destColorPtr[2] = (state->overprintAdditive && pipe->shape != 0) ? std::min(pipe->destColorPtr[2] + cResult2, 255) : cResult2; } if (state->overprintMask & 8) { pipe->destColorPtr[3] = (state->overprintAdditive && pipe->shape != 0) ? std::min(pipe->destColorPtr[3] + cResult3, 255) : cResult3; } pipe->destColorPtr += 4; *pipe->destAlphaPtr++ = aResult; ++pipe->x; } // special case: // !pipe->pattern && !pipe->noTransparency && !state->softMask && // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && // !pipe->nonIsolatedGroup && // bitmap->mode == splashModeDeviceN8 && pipe->destAlphaPtr void Splash::pipeRunAADeviceN8(SplashPipe *pipe) { unsigned char aSrc, aDest, alpha2, aResult; SplashColor cDest; unsigned char cResult[SPOT_NCOMPS + 4]; int cp, mask; //----- read destination pixel for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) { cDest[cp] = pipe->destColorPtr[cp]; } aDest = *pipe->destAlphaPtr; //----- source alpha aSrc = div255(pipe->aInput * pipe->shape); //----- result alpha and non-isolated group element correction aResult = aSrc + aDest - div255(aSrc * aDest); alpha2 = aResult; //----- result color if (alpha2 == 0) { for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) { cResult[cp] = 0; } } else { for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) { cResult[cp] = state->deviceNTransfer[cp][(unsigned char)(((alpha2 - aSrc) * cDest[cp] + aSrc * pipe->cSrc[cp]) / alpha2)]; } } //----- write destination pixel mask = 1; for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) { if (state->overprintMask & mask) { pipe->destColorPtr[cp] = cResult[cp]; } mask <<= 1; } pipe->destColorPtr += (SPOT_NCOMPS + 4); *pipe->destAlphaPtr++ = aResult; ++pipe->x; } inline void Splash::pipeSetXY(SplashPipe *pipe, int x, int y) { pipe->x = x; pipe->y = y; if (state->softMask) { pipe->softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x]; } switch (bitmap->mode) { case splashModeMono1: pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + (x >> 3)]; pipe->destColorMask = 0x80 >> (x & 7); break; case splashModeMono8: pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + x]; break; case splashModeRGB8: case splashModeBGR8: pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x]; break; case splashModeXBGR8: pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x]; break; case splashModeCMYK8: pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x]; break; case splashModeDeviceN8: pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + (SPOT_NCOMPS + 4) * x]; break; } if (bitmap->alpha) { pipe->destAlphaPtr = &bitmap->alpha[y * bitmap->width + x]; } else { pipe->destAlphaPtr = nullptr; } if (state->inNonIsolatedGroup && alpha0Bitmap->alpha) { pipe->alpha0Ptr = &alpha0Bitmap->alpha[(alpha0Y + y) * alpha0Bitmap->width + (alpha0X + x)]; } else { pipe->alpha0Ptr = nullptr; } } inline void Splash::pipeIncX(SplashPipe *pipe) { ++pipe->x; if (state->softMask) { ++pipe->softMaskPtr; } switch (bitmap->mode) { case splashModeMono1: if (!(pipe->destColorMask >>= 1)) { pipe->destColorMask = 0x80; ++pipe->destColorPtr; } break; case splashModeMono8: ++pipe->destColorPtr; break; case splashModeRGB8: case splashModeBGR8: pipe->destColorPtr += 3; break; case splashModeXBGR8: pipe->destColorPtr += 4; break; case splashModeCMYK8: pipe->destColorPtr += 4; break; case splashModeDeviceN8: pipe->destColorPtr += (SPOT_NCOMPS + 4); break; } if (pipe->destAlphaPtr) { ++pipe->destAlphaPtr; } if (pipe->alpha0Ptr) { ++pipe->alpha0Ptr; } } inline void Splash::drawPixel(SplashPipe *pipe, int x, int y, bool noClip) { if (unlikely(y < 0)) { return; } if (noClip || state->clip->test(x, y)) { pipeSetXY(pipe, x, y); (this->*pipe->run)(pipe); } } inline void Splash::drawAAPixelInit() { aaBufY = -1; } inline void Splash::drawAAPixel(SplashPipe *pipe, int x, int y) { #if splashAASize == 4 static const int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 }; int w; #else int xx, yy; #endif SplashColorPtr p; int x0, x1, t; if (x < 0 || x >= bitmap->width || y < state->clip->getYMinI() || y > state->clip->getYMaxI()) { return; } // update aaBuf if (y != aaBufY) { memset(aaBuf->getDataPtr(), 0xff, aaBuf->getRowSize() * aaBuf->getHeight()); x0 = 0; x1 = bitmap->width - 1; state->clip->clipAALine(aaBuf, &x0, &x1, y); aaBufY = y; } // compute the shape value #if splashAASize == 4 p = aaBuf->getDataPtr() + (x >> 1); w = aaBuf->getRowSize(); if (x & 1) { t = bitCount4[*p & 0x0f] + bitCount4[p[w] & 0x0f] + bitCount4[p[2 * w] & 0x0f] + bitCount4[p[3 * w] & 0x0f]; } else { t = bitCount4[*p >> 4] + bitCount4[p[w] >> 4] + bitCount4[p[2 * w] >> 4] + bitCount4[p[3 * w] >> 4]; } #else t = 0; for (yy = 0; yy < splashAASize; ++yy) { for (xx = 0; xx < splashAASize; ++xx) { p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + ((x * splashAASize + xx) >> 3); t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1; } } #endif // draw the pixel if (t != 0) { pipeSetXY(pipe, x, y); pipe->shape = div255(static_cast(aaGamma[t] * pipe->shape)); (this->*pipe->run)(pipe); } } inline void Splash::drawSpan(SplashPipe *pipe, int x0, int x1, int y, bool noClip) { int x; if (noClip) { pipeSetXY(pipe, x0, y); for (x = x0; x <= x1; ++x) { (this->*pipe->run)(pipe); } } else { if (x0 < state->clip->getXMinI()) { x0 = state->clip->getXMinI(); } if (x1 > state->clip->getXMaxI()) { x1 = state->clip->getXMaxI(); } pipeSetXY(pipe, x0, y); for (x = x0; x <= x1; ++x) { if (state->clip->test(x, y)) { (this->*pipe->run)(pipe); } else { pipeIncX(pipe); } } } } inline void Splash::drawAALine(SplashPipe *pipe, int x0, int x1, int y, bool adjustLine, unsigned char lineOpacity) { #if splashAASize == 4 static const int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 }; SplashColorPtr p0, p1, p2, p3; int t; #else SplashColorPtr p; int xx, yy, t; #endif int x; #if splashAASize == 4 p0 = aaBuf->getDataPtr() + (x0 >> 1); p1 = p0 + aaBuf->getRowSize(); p2 = p1 + aaBuf->getRowSize(); p3 = p2 + aaBuf->getRowSize(); #endif pipeSetXY(pipe, x0, y); for (x = x0; x <= x1; ++x) { // compute the shape value #if splashAASize == 4 if (x & 1) { t = bitCount4[*p0 & 0x0f] + bitCount4[*p1 & 0x0f] + bitCount4[*p2 & 0x0f] + bitCount4[*p3 & 0x0f]; ++p0; ++p1; ++p2; ++p3; } else { t = bitCount4[*p0 >> 4] + bitCount4[*p1 >> 4] + bitCount4[*p2 >> 4] + bitCount4[*p3 >> 4]; } #else t = 0; for (yy = 0; yy < splashAASize; ++yy) { for (xx = 0; xx < splashAASize; ++xx) { p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + ((x * splashAASize + xx) >> 3); t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1; } } #endif if (t != 0) { pipe->shape = (adjustLine) ? div255(static_cast((int)lineOpacity * (double)aaGamma[t])) : (int)aaGamma[t]; (this->*pipe->run)(pipe); } else { pipeIncX(pipe); } } } //------------------------------------------------------------------------ // Transform a point from user space to device space. inline void Splash::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 ] // [ m[4] m[5] 1 ] *xo = xi * matrix[0] + yi * matrix[2] + matrix[4]; *yo = xi * matrix[1] + yi * matrix[3] + matrix[5]; } //------------------------------------------------------------------------ // Splash //------------------------------------------------------------------------ Splash::Splash(SplashBitmap *bitmapA, bool vectorAntialiasA, SplashScreenParams *screenParams) { int i; bitmap = bitmapA; vectorAntialias = vectorAntialiasA; inShading = false; state = new SplashState(bitmap->width, bitmap->height, vectorAntialias, screenParams); if (vectorAntialias) { aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize, 1, splashModeMono1, false); for (i = 0; i <= splashAASize * splashAASize; ++i) { aaGamma[i] = (unsigned char)splashRound(splashPow((SplashCoord)i / (SplashCoord)(splashAASize * splashAASize), splashAAGamma) * 255); } } else { aaBuf = nullptr; } minLineWidth = 0; thinLineMode = splashThinLineDefault; debugMode = false; alpha0Bitmap = nullptr; } Splash::Splash(SplashBitmap *bitmapA, bool vectorAntialiasA, SplashScreen *screenA) { int i; bitmap = bitmapA; inShading = false; vectorAntialias = vectorAntialiasA; state = new SplashState(bitmap->width, bitmap->height, vectorAntialias, screenA); if (vectorAntialias) { aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize, 1, splashModeMono1, false); for (i = 0; i <= splashAASize * splashAASize; ++i) { aaGamma[i] = (unsigned char)splashRound(splashPow((SplashCoord)i / (SplashCoord)(splashAASize * splashAASize), splashAAGamma) * 255); } } else { aaBuf = nullptr; } minLineWidth = 0; thinLineMode = splashThinLineDefault; debugMode = false; alpha0Bitmap = nullptr; } Splash::~Splash() { while (state->next) { restoreState(); } delete state; delete aaBuf; } //------------------------------------------------------------------------ // state read //------------------------------------------------------------------------ SplashCoord *Splash::getMatrix() { return state->matrix; } SplashPattern *Splash::getStrokePattern() { return state->strokePattern; } SplashPattern *Splash::getFillPattern() { return state->fillPattern; } SplashScreen *Splash::getScreen() { return state->screen; } SplashBlendFunc Splash::getBlendFunc() { return state->blendFunc; } SplashCoord Splash::getStrokeAlpha() { return state->strokeAlpha; } SplashCoord Splash::getFillAlpha() { return state->fillAlpha; } SplashCoord Splash::getLineWidth() { return state->lineWidth; } int Splash::getLineCap() { return state->lineCap; } int Splash::getLineJoin() { return state->lineJoin; } SplashCoord Splash::getMiterLimit() { return state->miterLimit; } SplashCoord Splash::getFlatness() { return state->flatness; } SplashCoord Splash::getLineDashPhase() { return state->lineDashPhase; } bool Splash::getStrokeAdjust() { return state->strokeAdjust; } SplashClip *Splash::getClip() { return state->clip; } SplashBitmap *Splash::getSoftMask() { return state->softMask; } bool Splash::getInNonIsolatedGroup() { return state->inNonIsolatedGroup; } //------------------------------------------------------------------------ // state write //------------------------------------------------------------------------ void Splash::setMatrix(SplashCoord *matrix) { memcpy(state->matrix, matrix, 6 * sizeof(SplashCoord)); } void Splash::setStrokePattern(SplashPattern *strokePattern) { state->setStrokePattern(strokePattern); } void Splash::setFillPattern(SplashPattern *fillPattern) { state->setFillPattern(fillPattern); } void Splash::setScreen(SplashScreen *screen) { state->setScreen(screen); } void Splash::setBlendFunc(SplashBlendFunc func) { state->blendFunc = func; } void Splash::setStrokeAlpha(SplashCoord alpha) { state->strokeAlpha = (state->multiplyPatternAlpha) ? alpha * state->patternStrokeAlpha : alpha; } void Splash::setFillAlpha(SplashCoord alpha) { state->fillAlpha = (state->multiplyPatternAlpha) ? alpha * state->patternFillAlpha : alpha; } void Splash::setPatternAlpha(SplashCoord strokeAlpha, SplashCoord fillAlpha) { state->patternStrokeAlpha = strokeAlpha; state->patternFillAlpha = fillAlpha; state->multiplyPatternAlpha = true; } void Splash::clearPatternAlpha() { state->patternStrokeAlpha = 1; state->patternFillAlpha = 1; state->multiplyPatternAlpha = false; } void Splash::setFillOverprint(bool fop) { state->fillOverprint = fop; } void Splash::setStrokeOverprint(bool sop) { state->strokeOverprint = sop; } void Splash::setOverprintMode(int opm) { state->overprintMode = opm; } void Splash::setLineWidth(SplashCoord lineWidth) { state->lineWidth = lineWidth; } void Splash::setLineCap(int lineCap) { state->lineCap = lineCap; } void Splash::setLineJoin(int lineJoin) { state->lineJoin = lineJoin; } void Splash::setMiterLimit(SplashCoord miterLimit) { state->miterLimit = miterLimit; } void Splash::setFlatness(SplashCoord flatness) { if (flatness < 1) { state->flatness = 1; } else { state->flatness = flatness; } } void Splash::setLineDash(std::vector &&lineDash, SplashCoord lineDashPhase) { state->setLineDash(std::move(lineDash), lineDashPhase); } void Splash::setStrokeAdjust(bool strokeAdjust) { state->strokeAdjust = strokeAdjust; } void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) { state->clip->resetToRect(x0, y0, x1, y1); } SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) { return state->clip->clipToRect(x0, y0, x1, y1); } SplashError Splash::clipToPath(SplashPath *path, bool eo) { return state->clip->clipToPath(path, state->matrix, state->flatness, eo); } void Splash::setSoftMask(SplashBitmap *softMask) { state->setSoftMask(softMask); } void Splash::setInNonIsolatedGroup(SplashBitmap *alpha0BitmapA, int alpha0XA, int alpha0YA) { alpha0Bitmap = alpha0BitmapA; alpha0X = alpha0XA; alpha0Y = alpha0YA; state->inNonIsolatedGroup = true; } void Splash::setTransfer(unsigned char *red, unsigned char *green, unsigned char *blue, unsigned char *gray) { state->setTransfer(red, green, blue, gray); } void Splash::setOverprintMask(unsigned int overprintMask, bool additive) { state->overprintMask = overprintMask; state->overprintAdditive = additive; } //------------------------------------------------------------------------ // state save/restore //------------------------------------------------------------------------ void Splash::saveState() { SplashState *newState; newState = state->copy(); newState->next = state; state = newState; } SplashError Splash::restoreState() { SplashState *oldState; if (!state->next) { return splashErrNoSave; } oldState = state; state = state->next; delete oldState; return splashOk; } //------------------------------------------------------------------------ // drawing operations //------------------------------------------------------------------------ void Splash::clear(SplashColorPtr color, unsigned char alpha) { SplashColorPtr row, p; unsigned char mono; int x, y; switch (bitmap->mode) { case splashModeMono1: mono = (color[0] & 0x80) ? 0xff : 0x00; if (bitmap->rowSize < 0) { memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), mono, -bitmap->rowSize * bitmap->height); } else { memset(bitmap->data, mono, bitmap->rowSize * bitmap->height); } break; case splashModeMono8: if (bitmap->rowSize < 0) { memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), color[0], -bitmap->rowSize * bitmap->height); } else { memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); } break; case splashModeRGB8: if (color[0] == color[1] && color[1] == color[2]) { if (bitmap->rowSize < 0) { memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), color[0], -bitmap->rowSize * bitmap->height); } else { memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); } } else { row = bitmap->data; for (y = 0; y < bitmap->height; ++y) { p = row; for (x = 0; x < bitmap->width; ++x) { *p++ = color[2]; *p++ = color[1]; *p++ = color[0]; } row += bitmap->rowSize; } } break; case splashModeXBGR8: if (color[0] == color[1] && color[1] == color[2]) { if (bitmap->rowSize < 0) { memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), color[0], -bitmap->rowSize * bitmap->height); } else { memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); } } else { row = bitmap->data; for (y = 0; y < bitmap->height; ++y) { p = row; for (x = 0; x < bitmap->width; ++x) { *p++ = color[0]; *p++ = color[1]; *p++ = color[2]; *p++ = 255; } row += bitmap->rowSize; } } break; case splashModeBGR8: if (color[0] == color[1] && color[1] == color[2]) { if (bitmap->rowSize < 0) { memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), color[0], -bitmap->rowSize * bitmap->height); } else { memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); } } else { row = bitmap->data; for (y = 0; y < bitmap->height; ++y) { p = row; for (x = 0; x < bitmap->width; ++x) { *p++ = color[0]; *p++ = color[1]; *p++ = color[2]; } row += bitmap->rowSize; } } break; case splashModeCMYK8: if (color[0] == color[1] && color[1] == color[2] && color[2] == color[3]) { if (bitmap->rowSize < 0) { memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), color[0], -bitmap->rowSize * bitmap->height); } else { memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); } } else { row = bitmap->data; for (y = 0; y < bitmap->height; ++y) { p = row; for (x = 0; x < bitmap->width; ++x) { *p++ = color[0]; *p++ = color[1]; *p++ = color[2]; *p++ = color[3]; } row += bitmap->rowSize; } } break; case splashModeDeviceN8: row = bitmap->data; for (y = 0; y < bitmap->height; ++y) { p = row; for (x = 0; x < bitmap->width; ++x) { for (int cp = 0; cp < SPOT_NCOMPS + 4; cp++) { *p++ = color[cp]; } } row += bitmap->rowSize; } break; } if (bitmap->alpha) { memset(bitmap->alpha, alpha, bitmap->width * bitmap->height); } } SplashError Splash::stroke(SplashPath *path) { SplashPath *path2, *dPath; SplashCoord d1, d2, t1, t2, w; if (debugMode) { printf("stroke [dash:%zu] [width:%.2f]:\n", state->lineDash.size(), (double)state->lineWidth); dumpPath(path); } opClipRes = splashClipAllOutside; if (path->length == 0) { return splashErrEmptyPath; } path2 = flattenPath(path, state->matrix, state->flatness); if (!state->lineDash.empty()) { dPath = makeDashedPath(path2); delete path2; path2 = dPath; if (path2->length == 0) { delete path2; return splashErrEmptyPath; } } // transform a unit square, and take the half the max of the two // diagonals; the product of this number and the line width is the // (approximate) transformed line width t1 = state->matrix[0] + state->matrix[2]; t2 = state->matrix[1] + state->matrix[3]; d1 = t1 * t1 + t2 * t2; t1 = state->matrix[0] - state->matrix[2]; t2 = state->matrix[1] - state->matrix[3]; d2 = t1 * t1 + t2 * t2; if (d2 > d1) { d1 = d2; } d1 *= 0.5; if (d1 > 0 && d1 * state->lineWidth * state->lineWidth < minLineWidth * minLineWidth) { w = minLineWidth / splashSqrt(d1); strokeWide(path2, w); } else if (bitmap->mode == splashModeMono1) { // this gets close to Adobe's behavior in mono mode if (d1 * state->lineWidth <= 2) { strokeNarrow(path2); } else { strokeWide(path2, state->lineWidth); } } else { if (state->lineWidth == 0) { strokeNarrow(path2); } else { strokeWide(path2, state->lineWidth); } } delete path2; return splashOk; } void Splash::strokeNarrow(SplashPath *path) { SplashPipe pipe; SplashXPathSeg *seg; int x0, x1, y0, y1, xa, xb, y; SplashCoord dxdy; SplashClipResult clipRes; int nClipRes[3]; int i; nClipRes[0] = nClipRes[1] = nClipRes[2] = 0; SplashXPath xPath(path, state->matrix, state->flatness, false); pipeInit(&pipe, 0, 0, state->strokePattern, nullptr, (unsigned char)splashRound(state->strokeAlpha * 255), false, false); for (i = 0, seg = xPath.segs; i < xPath.length; ++i, ++seg) { if (seg->y0 <= seg->y1) { y0 = splashFloor(seg->y0); y1 = splashFloor(seg->y1); x0 = splashFloor(seg->x0); x1 = splashFloor(seg->x1); } else { y0 = splashFloor(seg->y1); y1 = splashFloor(seg->y0); x0 = splashFloor(seg->x1); x1 = splashFloor(seg->x0); } if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0, x0 <= x1 ? x1 : x0, y1)) != splashClipAllOutside) { if (y0 == y1) { if (x0 <= x1) { drawSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside); } else { drawSpan(&pipe, x1, x0, y0, clipRes == splashClipAllInside); } } else { dxdy = seg->dxdy; if (y0 < state->clip->getYMinI()) { y0 = state->clip->getYMinI(); x0 = splashFloor(seg->x0 + (state->clip->getYMin() - seg->y0) * dxdy); } if (y1 > state->clip->getYMaxI()) { y1 = state->clip->getYMaxI(); x1 = splashFloor(seg->x0 + (state->clip->getYMax() - seg->y0) * dxdy); } if (x0 <= x1) { xa = x0; for (y = y0; y <= y1; ++y) { if (y < y1) { xb = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy); } else { xb = x1 + 1; } if (xa == xb) { drawPixel(&pipe, xa, y, clipRes == splashClipAllInside); } else { drawSpan(&pipe, xa, xb - 1, y, clipRes == splashClipAllInside); } xa = xb; } } else { xa = x0; for (y = y0; y <= y1; ++y) { if (y < y1) { xb = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy); } else { xb = x1 - 1; } if (xa == xb) { drawPixel(&pipe, xa, y, clipRes == splashClipAllInside); } else { drawSpan(&pipe, xb + 1, xa, y, clipRes == splashClipAllInside); } xa = xb; } } } } ++nClipRes[clipRes]; } if (nClipRes[splashClipPartial] || (nClipRes[splashClipAllInside] && nClipRes[splashClipAllOutside])) { opClipRes = splashClipPartial; } else if (nClipRes[splashClipAllInside]) { opClipRes = splashClipAllInside; } else { opClipRes = splashClipAllOutside; } } void Splash::strokeWide(SplashPath *path, SplashCoord w) { SplashPath *path2; path2 = makeStrokePath(path, w, false); fillWithPattern(path2, false, state->strokePattern, state->strokeAlpha); delete path2; } SplashPath *Splash::flattenPath(SplashPath *path, SplashCoord *matrix, SplashCoord flatness) { SplashPath *fPath; SplashCoord flatness2; unsigned char flag; int i; fPath = new SplashPath(); flatness2 = flatness * flatness; i = 0; while (i < path->length) { flag = path->flags[i]; if (flag & splashPathFirst) { fPath->moveTo(path->pts[i].x, path->pts[i].y); ++i; } else { if (flag & splashPathCurve) { flattenCurve(path->pts[i - 1].x, path->pts[i - 1].y, path->pts[i].x, path->pts[i].y, path->pts[i + 1].x, path->pts[i + 1].y, path->pts[i + 2].x, path->pts[i + 2].y, matrix, flatness2, fPath); i += 3; } else { fPath->lineTo(path->pts[i].x, path->pts[i].y); ++i; } if (path->flags[i - 1] & splashPathClosed) { fPath->close(); } } } return fPath; } void Splash::flattenCurve(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1, SplashCoord x2, SplashCoord y2, SplashCoord x3, SplashCoord y3, SplashCoord *matrix, SplashCoord flatness2, SplashPath *fPath) { SplashCoord cx[splashMaxCurveSplits + 1][3]; SplashCoord cy[splashMaxCurveSplits + 1][3]; int cNext[splashMaxCurveSplits + 1]; SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh; SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh; SplashCoord dx, dy, mx, my, tx, ty, d1, d2; int p1, p2, p3; // initial segment p1 = 0; p2 = splashMaxCurveSplits; cx[p1][0] = x0; cy[p1][0] = y0; cx[p1][1] = x1; cy[p1][1] = y1; cx[p1][2] = x2; cy[p1][2] = y2; cx[p2][0] = x3; cy[p2][0] = y3; cNext[p1] = p2; while (p1 < splashMaxCurveSplits) { // get the next segment xl0 = cx[p1][0]; yl0 = cy[p1][0]; xx1 = cx[p1][1]; yy1 = cy[p1][1]; xx2 = cx[p1][2]; yy2 = cy[p1][2]; p2 = cNext[p1]; xr3 = cx[p2][0]; yr3 = cy[p2][0]; // compute the distances (in device space) from the control points // to the midpoint of the straight line (this is a bit of a hack, // but it's much faster than computing the actual distances to the // line) transform(matrix, (xl0 + xr3) * 0.5, (yl0 + yr3) * 0.5, &mx, &my); transform(matrix, xx1, yy1, &tx, &ty); dx = tx - mx; dy = ty - my; d1 = dx * dx + dy * dy; transform(matrix, xx2, yy2, &tx, &ty); dx = tx - mx; dy = ty - my; d2 = dx * dx + dy * dy; // if the curve is flat enough, or no more subdivisions are // allowed, add the straight line segment if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) { fPath->lineTo(xr3, yr3); p1 = p2; // otherwise, subdivide the curve } else { xl1 = splashAvg(xl0, xx1); yl1 = splashAvg(yl0, yy1); xh = splashAvg(xx1, xx2); yh = splashAvg(yy1, yy2); xl2 = splashAvg(xl1, xh); yl2 = splashAvg(yl1, yh); xr2 = splashAvg(xx2, xr3); yr2 = splashAvg(yy2, yr3); xr1 = splashAvg(xh, xr2); yr1 = splashAvg(yh, yr2); xr0 = splashAvg(xl2, xr1); yr0 = splashAvg(yl2, yr1); // add the new subdivision points p3 = (p1 + p2) / 2; cx[p1][1] = xl1; cy[p1][1] = yl1; cx[p1][2] = xl2; cy[p1][2] = yl2; cNext[p1] = p3; cx[p3][0] = xr0; cy[p3][0] = yr0; cx[p3][1] = xr1; cy[p3][1] = yr1; cx[p3][2] = xr2; cy[p3][2] = yr2; cNext[p3] = p2; } } } SplashPath *Splash::makeDashedPath(SplashPath *path) { SplashPath *dPath; SplashCoord lineDashTotal; SplashCoord lineDashStartPhase, lineDashDist, segLen; SplashCoord x0, y0, x1, y1, xa, ya; bool lineDashStartOn, lineDashOn, newPath; int i, j, k; lineDashTotal = 0; for (SplashCoord dash : state->lineDash) { lineDashTotal += dash; } // Acrobat simply draws nothing if the dash array is [0] if (lineDashTotal == 0) { return new SplashPath(); } lineDashStartPhase = state->lineDashPhase; i = splashFloor(lineDashStartPhase / lineDashTotal); lineDashStartPhase -= (SplashCoord)i * lineDashTotal; lineDashStartOn = true; size_t lineDashStartIdx = 0; if (lineDashStartPhase > 0) { while (lineDashStartIdx < state->lineDash.size() && lineDashStartPhase >= state->lineDash[lineDashStartIdx]) { lineDashStartOn = !lineDashStartOn; lineDashStartPhase -= state->lineDash[lineDashStartIdx]; ++lineDashStartIdx; } if (unlikely(lineDashStartIdx == state->lineDash.size())) { return new SplashPath(); } } dPath = new SplashPath(); // process each subpath i = 0; while (i < path->length) { // find the end of the subpath for (j = i; j < path->length - 1 && !(path->flags[j] & splashPathLast); ++j) { ; } // initialize the dash parameters lineDashOn = lineDashStartOn; size_t lineDashIdx = lineDashStartIdx; lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase; // process each segment of the subpath newPath = true; for (k = i; k < j; ++k) { // grab the segment x0 = path->pts[k].x; y0 = path->pts[k].y; x1 = path->pts[k + 1].x; y1 = path->pts[k + 1].y; segLen = splashDist(x0, y0, x1, y1); // process the segment while (segLen > 0) { if (lineDashDist >= segLen) { if (lineDashOn) { if (newPath) { dPath->moveTo(x0, y0); newPath = false; } dPath->lineTo(x1, y1); } lineDashDist -= segLen; segLen = 0; } else { xa = x0 + (lineDashDist / segLen) * (x1 - x0); ya = y0 + (lineDashDist / segLen) * (y1 - y0); if (lineDashOn) { if (newPath) { dPath->moveTo(x0, y0); newPath = false; } dPath->lineTo(xa, ya); } x0 = xa; y0 = ya; segLen -= lineDashDist; lineDashDist = 0; } // get the next entry in the dash array if (lineDashDist <= 0) { lineDashOn = !lineDashOn; if (++lineDashIdx == state->lineDash.size()) { lineDashIdx = 0; } lineDashDist = state->lineDash[lineDashIdx]; newPath = true; } } } i = j + 1; } if (dPath->length == 0) { bool allSame = true; for (i = 0; allSame && i < path->length - 1; ++i) { allSame = path->pts[i].x == path->pts[i + 1].x && path->pts[i].y == path->pts[i + 1].y; } if (allSame) { x0 = path->pts[0].x; y0 = path->pts[0].y; dPath->moveTo(x0, y0); dPath->lineTo(x0, y0); } } return dPath; } SplashError Splash::fill(SplashPath *path, bool eo) { if (debugMode) { printf("fill [eo:%d]:\n", eo); dumpPath(path); } return fillWithPattern(path, eo, state->fillPattern, state->fillAlpha); } inline void Splash::getBBoxFP(SplashPath *path, SplashCoord *xMinA, SplashCoord *yMinA, SplashCoord *xMaxA, SplashCoord *yMaxA) { SplashCoord xMinFP, yMinFP, xMaxFP, yMaxFP, tx, ty; // make compiler happy: xMinFP = xMaxFP = yMinFP = yMaxFP = 0; for (int i = 0; i < path->length; ++i) { transform(state->matrix, path->pts[i].x, path->pts[i].y, &tx, &ty); if (i == 0) { xMinFP = xMaxFP = tx; yMinFP = yMaxFP = ty; } else { if (tx < xMinFP) { xMinFP = tx; } if (tx > xMaxFP) { xMaxFP = tx; } if (ty < yMinFP) { yMinFP = ty; } if (ty > yMaxFP) { yMaxFP = ty; } } } *xMinA = xMinFP; *yMinA = yMinFP; *xMaxA = xMaxFP; *yMaxA = yMaxFP; } SplashError Splash::fillWithPattern(SplashPath *path, bool eo, SplashPattern *pattern, SplashCoord alpha) { SplashPipe pipe = {}; int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y; SplashClipResult clipRes, clipRes2; bool adjustLine = false; int linePosI = 0; if (path->length == 0) { return splashErrEmptyPath; } if (pathAllOutside(path)) { opClipRes = splashClipAllOutside; return splashOk; } // add stroke adjustment hints for filled rectangles -- this only // applies to paths that consist of a single subpath // (this appears to match Acrobat's behavior) if (state->strokeAdjust && !path->hints) { int n; n = path->getLength(); if (n == 4 && !(path->flags[0] & splashPathClosed) && !(path->flags[1] & splashPathLast) && !(path->flags[2] & splashPathLast)) { path->close(true); path->addStrokeAdjustHint(0, 2, 0, 4); path->addStrokeAdjustHint(1, 3, 0, 4); } else if (n == 5 && (path->flags[0] & splashPathClosed) && !(path->flags[1] & splashPathLast) && !(path->flags[2] & splashPathLast) && !(path->flags[3] & splashPathLast)) { path->addStrokeAdjustHint(0, 2, 0, 4); path->addStrokeAdjustHint(1, 3, 0, 4); } } if (thinLineMode != splashThinLineDefault) { if (state->clip->getXMinI() == state->clip->getXMaxI()) { linePosI = state->clip->getXMinI(); adjustLine = true; } else if (state->clip->getXMinI() == state->clip->getXMaxI() - 1) { adjustLine = true; linePosI = splashFloor(state->clip->getXMin() + state->lineWidth); } else if (state->clip->getYMinI() == state->clip->getYMaxI()) { linePosI = state->clip->getYMinI(); adjustLine = true; } else if (state->clip->getYMinI() == state->clip->getYMaxI() - 1) { adjustLine = true; linePosI = splashFloor(state->clip->getYMin() + state->lineWidth); } } SplashXPath xPath(path, state->matrix, state->flatness, true, adjustLine, linePosI); if (vectorAntialias && !inShading) { xPath.aaScale(); } xPath.sort(); yMinI = state->clip->getYMinI(); yMaxI = state->clip->getYMaxI(); if (vectorAntialias && !inShading) { yMinI = yMinI * splashAASize; yMaxI = (yMaxI + 1) * splashAASize - 1; } SplashXPathScanner scanner(xPath, eo, yMinI, yMaxI); // get the min and max x and y values if (vectorAntialias && !inShading) { scanner.getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI); } else { scanner.getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI); } if (eo && (yMinI == yMaxI || xMinI == xMaxI) && thinLineMode != splashThinLineDefault) { SplashCoord delta, xMinFP, yMinFP, xMaxFP, yMaxFP; getBBoxFP(path, &xMinFP, &yMinFP, &xMaxFP, &yMaxFP); delta = (yMinI == yMaxI) ? yMaxFP - yMinFP : xMaxFP - xMinFP; if (delta < 0.2) { opClipRes = splashClipAllOutside; return splashOk; } } // check clipping if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI)) != splashClipAllOutside) { if (scanner.hasPartialClip()) { clipRes = splashClipPartial; } pipeInit(&pipe, 0, yMinI, pattern, nullptr, (unsigned char)splashRound(alpha * 255), vectorAntialias && !inShading, false); // draw the spans if (vectorAntialias && !inShading) { for (y = yMinI; y <= yMaxI; ++y) { scanner.renderAALine(aaBuf, &x0, &x1, y, thinLineMode != splashThinLineDefault && xMinI == xMaxI); if (clipRes != splashClipAllInside) { state->clip->clipAALine(aaBuf, &x0, &x1, y, thinLineMode != splashThinLineDefault && xMinI == xMaxI); } unsigned char lineShape = 255; bool doAdjustLine = false; if (thinLineMode == splashThinLineShape && (xMinI == xMaxI || yMinI == yMaxI)) { // compute line shape for thin lines: SplashCoord mx, my, delta; transform(state->matrix, 0, 0, &mx, &my); transform(state->matrix, state->lineWidth, 0, &delta, &my); doAdjustLine = true; lineShape = clip255(static_cast((delta - mx) * 255)); } drawAALine(&pipe, x0, x1, y, doAdjustLine, lineShape); } } else { for (y = yMinI; y <= yMaxI; ++y) { SplashXPathScanIterator iterator(scanner, y); while (iterator.getNextSpan(&x0, &x1)) { if (clipRes == splashClipAllInside) { drawSpan(&pipe, x0, x1, y, true); } else { // limit the x range if (x0 < state->clip->getXMinI()) { x0 = state->clip->getXMinI(); } if (x1 > state->clip->getXMaxI()) { x1 = state->clip->getXMaxI(); } clipRes2 = state->clip->testSpan(x0, x1, y); drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside); } } } } } opClipRes = clipRes; return splashOk; } bool Splash::pathAllOutside(SplashPath *path) { SplashCoord xMin1, yMin1, xMax1, yMax1; SplashCoord xMin2, yMin2, xMax2, yMax2; SplashCoord x, y; int xMinI, yMinI, xMaxI, yMaxI; int i; xMin1 = xMax1 = path->pts[0].x; yMin1 = yMax1 = path->pts[0].y; for (i = 1; i < path->length; ++i) { if (path->pts[i].x < xMin1) { xMin1 = path->pts[i].x; } else if (path->pts[i].x > xMax1) { xMax1 = path->pts[i].x; } if (path->pts[i].y < yMin1) { yMin1 = path->pts[i].y; } else if (path->pts[i].y > yMax1) { yMax1 = path->pts[i].y; } } transform(state->matrix, xMin1, yMin1, &x, &y); xMin2 = xMax2 = x; yMin2 = yMax2 = y; transform(state->matrix, xMin1, yMax1, &x, &y); if (x < xMin2) { xMin2 = x; } else if (x > xMax2) { xMax2 = x; } if (y < yMin2) { yMin2 = y; } else if (y > yMax2) { yMax2 = y; } transform(state->matrix, xMax1, yMin1, &x, &y); if (x < xMin2) { xMin2 = x; } else if (x > xMax2) { xMax2 = x; } if (y < yMin2) { yMin2 = y; } else if (y > yMax2) { yMax2 = y; } transform(state->matrix, xMax1, yMax1, &x, &y); if (x < xMin2) { xMin2 = x; } else if (x > xMax2) { xMax2 = x; } if (y < yMin2) { yMin2 = y; } else if (y > yMax2) { yMax2 = y; } xMinI = splashFloor(xMin2); yMinI = splashFloor(yMin2); xMaxI = splashFloor(xMax2); yMaxI = splashFloor(yMax2); return state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI) == splashClipAllOutside; } SplashError Splash::xorFill(SplashPath *path, bool eo) { SplashPipe pipe; int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y; SplashClipResult clipRes, clipRes2; SplashBlendFunc origBlendFunc; if (path->length == 0) { return splashErrEmptyPath; } SplashXPath xPath(path, state->matrix, state->flatness, true); xPath.sort(); SplashXPathScanner scanner(xPath, eo, state->clip->getYMinI(), state->clip->getYMaxI()); // get the min and max x and y values scanner.getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI); // check clipping if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI)) != splashClipAllOutside) { if (scanner.hasPartialClip()) { clipRes = splashClipPartial; } origBlendFunc = state->blendFunc; state->blendFunc = &blendXor; pipeInit(&pipe, 0, yMinI, state->fillPattern, nullptr, 255, false, false); // draw the spans for (y = yMinI; y <= yMaxI; ++y) { SplashXPathScanIterator iterator(scanner, y); while (iterator.getNextSpan(&x0, &x1)) { if (clipRes == splashClipAllInside) { drawSpan(&pipe, x0, x1, y, true); } else { // limit the x range if (x0 < state->clip->getXMinI()) { x0 = state->clip->getXMinI(); } if (x1 > state->clip->getXMaxI()) { x1 = state->clip->getXMaxI(); } clipRes2 = state->clip->testSpan(x0, x1, y); drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside); } } } state->blendFunc = origBlendFunc; } opClipRes = clipRes; return splashOk; } SplashError Splash::fillChar(SplashCoord x, SplashCoord y, int c, SplashFont *font) { SplashGlyphBitmap glyph; SplashCoord xt, yt; int x0, y0, xFrac, yFrac; SplashClipResult clipRes; if (debugMode) { printf("fillChar: x=%.2f y=%.2f c=%3d=0x%02x='%c'\n", (double)x, (double)y, c, c, c); } transform(state->matrix, x, y, &xt, &yt); x0 = splashFloor(xt); xFrac = splashFloor((xt - x0) * splashFontFraction); y0 = splashFloor(yt); yFrac = splashFloor((yt - y0) * splashFontFraction); if (!font->getGlyph(c, xFrac, yFrac, &glyph, x0, y0, state->clip, &clipRes)) { return splashErrNoGlyph; } if (clipRes != splashClipAllOutside) { fillGlyph2(x0, y0, &glyph, clipRes == splashClipAllInside); } opClipRes = clipRes; if (glyph.freeData) { gfree(glyph.data); } return splashOk; } void Splash::fillGlyph(SplashCoord x, SplashCoord y, SplashGlyphBitmap *glyph) { SplashCoord xt, yt; int x0, y0; transform(state->matrix, x, y, &xt, &yt); x0 = splashFloor(xt); y0 = splashFloor(yt); SplashClipResult clipRes = state->clip->testRect(x0 - glyph->x, y0 - glyph->y, x0 - glyph->x + glyph->w - 1, y0 - glyph->y + glyph->h - 1); if (clipRes != splashClipAllOutside) { fillGlyph2(x0, y0, glyph, clipRes == splashClipAllInside); } opClipRes = clipRes; } void Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph, bool noClip) { SplashPipe pipe; int alpha0; unsigned char alpha; unsigned char *p; int x1, y1, xx, xx1, yy; p = glyph->data; int xStart = x0 - glyph->x; int yStart = y0 - glyph->y; int xxLimit = glyph->w; int yyLimit = glyph->h; int xShift = 0; if (yStart < 0) { p += (glyph->aa ? glyph->w : splashCeil(glyph->w / 8.0)) * -yStart; // move p to the beginning of the first painted row yyLimit += yStart; yStart = 0; } if (xStart < 0) { if (glyph->aa) { p += -xStart; } else { p += (-xStart) / 8; xShift = (-xStart) % 8; } xxLimit += xStart; xStart = 0; } if (xxLimit + xStart >= bitmap->width) { xxLimit = bitmap->width - xStart; } if (yyLimit + yStart >= bitmap->height) { yyLimit = bitmap->height - yStart; } if (noClip) { if (glyph->aa) { pipeInit(&pipe, xStart, yStart, state->fillPattern, nullptr, (unsigned char)splashRound(state->fillAlpha * 255), true, false); for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) { pipeSetXY(&pipe, xStart, y1); for (xx = 0, x1 = xStart; xx < xxLimit; ++xx, ++x1) { alpha = p[xx]; if (alpha != 0) { pipe.shape = alpha; (this->*pipe.run)(&pipe); } else { pipeIncX(&pipe); } } p += glyph->w; } } else { const int widthEight = splashCeil(glyph->w / 8.0); pipeInit(&pipe, xStart, yStart, state->fillPattern, nullptr, (unsigned char)splashRound(state->fillAlpha * 255), false, false); for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) { pipeSetXY(&pipe, xStart, y1); for (xx = 0, x1 = xStart; xx < xxLimit; xx += 8) { alpha0 = (xShift > 0 && xx < xxLimit - 8 ? (p[xx / 8] << xShift) | (p[xx / 8 + 1] >> (8 - xShift)) : p[xx / 8]); for (xx1 = 0; xx1 < 8 && xx + xx1 < xxLimit; ++xx1, ++x1) { if (alpha0 & 0x80) { (this->*pipe.run)(&pipe); } else { pipeIncX(&pipe); } alpha0 <<= 1; } } p += widthEight; } } } else { if (glyph->aa) { pipeInit(&pipe, xStart, yStart, state->fillPattern, nullptr, (unsigned char)splashRound(state->fillAlpha * 255), true, false); for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) { pipeSetXY(&pipe, xStart, y1); for (xx = 0, x1 = xStart; xx < xxLimit; ++xx, ++x1) { if (state->clip->test(x1, y1)) { alpha = p[xx]; if (alpha != 0) { pipe.shape = alpha; (this->*pipe.run)(&pipe); } else { pipeIncX(&pipe); } } else { pipeIncX(&pipe); } } p += glyph->w; } } else { const int widthEight = splashCeil(glyph->w / 8.0); pipeInit(&pipe, xStart, yStart, state->fillPattern, nullptr, (unsigned char)splashRound(state->fillAlpha * 255), false, false); for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) { pipeSetXY(&pipe, xStart, y1); for (xx = 0, x1 = xStart; xx < xxLimit; xx += 8) { alpha0 = (xShift > 0 && xx < xxLimit - 8 ? (p[xx / 8] << xShift) | (p[xx / 8 + 1] >> (8 - xShift)) : p[xx / 8]); for (xx1 = 0; xx1 < 8 && xx + xx1 < xxLimit; ++xx1, ++x1) { if (state->clip->test(x1, y1)) { if (alpha0 & 0x80) { (this->*pipe.run)(&pipe); } else { pipeIncX(&pipe); } } else { pipeIncX(&pipe); } alpha0 <<= 1; } } p += widthEight; } } } } SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData, int w, int h, SplashCoord *mat, bool glyphMode) { SplashBitmap *scaledMask; SplashClipResult clipRes; bool minorAxisZero; int x0, y0, x1, y1, scaledWidth, scaledHeight; int yp; if (debugMode) { printf("fillImageMask: w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n", w, h, (double)mat[0], (double)mat[1], (double)mat[2], (double)mat[3], (double)mat[4], (double)mat[5]); } if (w == 0 && h == 0) { return splashErrZeroImage; } // check for singular matrix if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) { return splashErrSingularMatrix; } minorAxisZero = mat[1] == 0 && mat[2] == 0; // scaling only if (mat[0] > 0 && minorAxisZero && mat[3] > 0) { x0 = imgCoordMungeLowerC(mat[4], glyphMode); y0 = imgCoordMungeLowerC(mat[5], glyphMode); x1 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode); y1 = imgCoordMungeUpperC(mat[3] + mat[5], glyphMode); // make sure narrow images cover at least one pixel if (x0 == x1) { ++x1; } if (y0 == y1) { ++y1; } clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; yp = h / scaledHeight; if (yp < 0 || yp > INT_MAX - 1) { return splashErrBadArg; } scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight); blitMask(scaledMask, x0, y0, clipRes); delete scaledMask; } // scaling plus vertical flip } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) { x0 = imgCoordMungeLowerC(mat[4], glyphMode); y0 = imgCoordMungeLowerC(mat[3] + mat[5], glyphMode); x1 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode); y1 = imgCoordMungeUpperC(mat[5], glyphMode); // make sure narrow images cover at least one pixel if (x0 == x1) { ++x1; } if (y0 == y1) { ++y1; } clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; yp = h / scaledHeight; if (yp < 0 || yp > INT_MAX - 1) { return splashErrBadArg; } scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight); vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1); blitMask(scaledMask, x0, y0, clipRes); delete scaledMask; } // all other cases } else { arbitraryTransformMask(src, srcData, w, h, mat, glyphMode); } return splashOk; } void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, SplashCoord *mat, bool glyphMode) { SplashBitmap *scaledMask; SplashClipResult clipRes, clipRes2; SplashPipe pipe; int scaledWidth, scaledHeight, t0, t1; SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11; SplashCoord vx[4], vy[4]; int xMin, yMin, xMax, yMax; ImageSection section[3]; int nSections; int y, xa, xb, x, i, xx, yy; // compute the four vertices of the target quadrilateral vx[0] = mat[4]; vy[0] = mat[5]; vx[1] = mat[2] + mat[4]; vy[1] = mat[3] + mat[5]; vx[2] = mat[0] + mat[2] + mat[4]; vy[2] = mat[1] + mat[3] + mat[5]; vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5]; // make sure vx/vy fit in integers since we're transforming them to in the next lines for (i = 0; i < 4; ++i) { if (unlikely(vx[i] < INT_MIN || vx[i] > INT_MAX || vy[i] < INT_MIN || vy[i] > INT_MAX)) { error(errInternal, -1, "arbitraryTransformMask vertices values don't fit in an integer"); return; } } // clipping xMin = imgCoordMungeLowerC(vx[0], glyphMode); xMax = imgCoordMungeUpperC(vx[0], glyphMode); yMin = imgCoordMungeLowerC(vy[0], glyphMode); yMax = imgCoordMungeUpperC(vy[0], glyphMode); for (i = 1; i < 4; ++i) { t0 = imgCoordMungeLowerC(vx[i], glyphMode); if (t0 < xMin) { xMin = t0; } t0 = imgCoordMungeUpperC(vx[i], glyphMode); if (t0 > xMax) { xMax = t0; } t1 = imgCoordMungeLowerC(vy[i], glyphMode); if (t1 < yMin) { yMin = t1; } t1 = imgCoordMungeUpperC(vy[i], glyphMode); if (t1 > yMax) { yMax = t1; } } clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1); opClipRes = clipRes; if (clipRes == splashClipAllOutside) { return; } // compute the scale factors if (mat[0] >= 0) { t0 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode) - imgCoordMungeLowerC(mat[4], glyphMode); } else { t0 = imgCoordMungeUpperC(mat[4], glyphMode) - imgCoordMungeLowerC(mat[0] + mat[4], glyphMode); } if (mat[1] >= 0) { t1 = imgCoordMungeUpperC(mat[1] + mat[5], glyphMode) - imgCoordMungeLowerC(mat[5], glyphMode); } else { t1 = imgCoordMungeUpperC(mat[5], glyphMode) - imgCoordMungeLowerC(mat[1] + mat[5], glyphMode); } scaledWidth = t0 > t1 ? t0 : t1; if (mat[2] >= 0) { t0 = imgCoordMungeUpperC(mat[2] + mat[4], glyphMode) - imgCoordMungeLowerC(mat[4], glyphMode); } else { t0 = imgCoordMungeUpperC(mat[4], glyphMode) - imgCoordMungeLowerC(mat[2] + mat[4], glyphMode); } if (mat[3] >= 0) { t1 = imgCoordMungeUpperC(mat[3] + mat[5], glyphMode) - imgCoordMungeLowerC(mat[5], glyphMode); } else { t1 = imgCoordMungeUpperC(mat[5], glyphMode) - imgCoordMungeLowerC(mat[3] + mat[5], glyphMode); } scaledHeight = t0 > t1 ? t0 : t1; if (scaledWidth == 0) { scaledWidth = 1; } if (scaledHeight == 0) { scaledHeight = 1; } // compute the inverse transform (after scaling) matrix r00 = mat[0] / scaledWidth; r01 = mat[1] / scaledWidth; r10 = mat[2] / scaledHeight; r11 = mat[3] / scaledHeight; det = r00 * r11 - r01 * r10; if (splashAbs(det) < 1e-6) { // this should be caught by the singular matrix check in fillImageMask return; } ir00 = r11 / det; ir01 = -r01 / det; ir10 = -r10 / det; ir11 = r00 / det; // scale the input image scaledMask = scaleMask(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight); if (scaledMask->data == nullptr) { error(errInternal, -1, "scaledMask->data is NULL in Splash::arbitraryTransformMask"); delete scaledMask; return; } // construct the three sections i = (vy[2] <= vy[3]) ? 2 : 3; if (vy[1] <= vy[i]) { i = 1; } if (vy[0] < vy[i] || (i != 3 && vy[0] == vy[i])) { i = 0; } if (vy[i] == vy[(i + 1) & 3]) { section[0].y0 = imgCoordMungeLowerC(vy[i], glyphMode); section[0].y1 = imgCoordMungeUpperC(vy[(i + 2) & 3], glyphMode) - 1; if (vx[i] < vx[(i + 1) & 3]) { section[0].ia0 = i; section[0].ia1 = (i + 3) & 3; section[0].ib0 = (i + 1) & 3; section[0].ib1 = (i + 2) & 3; } else { section[0].ia0 = (i + 1) & 3; section[0].ia1 = (i + 2) & 3; section[0].ib0 = i; section[0].ib1 = (i + 3) & 3; } nSections = 1; } else { section[0].y0 = imgCoordMungeLowerC(vy[i], glyphMode); section[2].y1 = imgCoordMungeUpperC(vy[(i + 2) & 3], glyphMode) - 1; section[0].ia0 = section[0].ib0 = i; section[2].ia1 = section[2].ib1 = (i + 2) & 3; if (vx[(i + 1) & 3] < vx[(i + 3) & 3]) { section[0].ia1 = section[2].ia0 = (i + 1) & 3; section[0].ib1 = section[2].ib0 = (i + 3) & 3; } else { section[0].ia1 = section[2].ia0 = (i + 3) & 3; section[0].ib1 = section[2].ib0 = (i + 1) & 3; } if (vy[(i + 1) & 3] < vy[(i + 3) & 3]) { section[1].y0 = imgCoordMungeLowerC(vy[(i + 1) & 3], glyphMode); section[2].y0 = imgCoordMungeUpperC(vy[(i + 3) & 3], glyphMode); if (vx[(i + 1) & 3] < vx[(i + 3) & 3]) { section[1].ia0 = (i + 1) & 3; section[1].ia1 = (i + 2) & 3; section[1].ib0 = i; section[1].ib1 = (i + 3) & 3; } else { section[1].ia0 = i; section[1].ia1 = (i + 3) & 3; section[1].ib0 = (i + 1) & 3; section[1].ib1 = (i + 2) & 3; } } else { section[1].y0 = imgCoordMungeLowerC(vy[(i + 3) & 3], glyphMode); section[2].y0 = imgCoordMungeUpperC(vy[(i + 1) & 3], glyphMode); if (vx[(i + 1) & 3] < vx[(i + 3) & 3]) { section[1].ia0 = i; section[1].ia1 = (i + 1) & 3; section[1].ib0 = (i + 3) & 3; section[1].ib1 = (i + 2) & 3; } else { section[1].ia0 = (i + 3) & 3; section[1].ia1 = (i + 2) & 3; section[1].ib0 = i; section[1].ib1 = (i + 1) & 3; } } section[0].y1 = section[1].y0 - 1; section[1].y1 = section[2].y0 - 1; nSections = 3; } for (i = 0; i < nSections; ++i) { section[i].xa0 = vx[section[i].ia0]; section[i].ya0 = vy[section[i].ia0]; section[i].xa1 = vx[section[i].ia1]; section[i].ya1 = vy[section[i].ia1]; section[i].xb0 = vx[section[i].ib0]; section[i].yb0 = vy[section[i].ib0]; section[i].xb1 = vx[section[i].ib1]; section[i].yb1 = vy[section[i].ib1]; section[i].dxdya = (section[i].xa1 - section[i].xa0) / (section[i].ya1 - section[i].ya0); section[i].dxdyb = (section[i].xb1 - section[i].xb0) / (section[i].yb1 - section[i].yb0); } // initialize the pixel pipe pipeInit(&pipe, 0, 0, state->fillPattern, nullptr, (unsigned char)splashRound(state->fillAlpha * 255), true, false); if (vectorAntialias) { drawAAPixelInit(); } // make sure narrow images cover at least one pixel if (nSections == 1) { if (section[0].y0 == section[0].y1) { ++section[0].y1; clipRes = opClipRes = splashClipPartial; } } else { if (section[0].y0 == section[2].y1) { ++section[1].y1; clipRes = opClipRes = splashClipPartial; } } // scan all pixels inside the target region for (i = 0; i < nSections; ++i) { for (y = section[i].y0; y <= section[i].y1; ++y) { xa = imgCoordMungeLowerC(section[i].xa0 + ((SplashCoord)y + 0.5 - section[i].ya0) * section[i].dxdya, glyphMode); xb = imgCoordMungeUpperC(section[i].xb0 + ((SplashCoord)y + 0.5 - section[i].yb0) * section[i].dxdyb, glyphMode); if (unlikely(xa < 0)) { xa = 0; } // make sure narrow images cover at least one pixel if (xa == xb) { ++xb; } if (clipRes != splashClipAllInside) { clipRes2 = state->clip->testSpan(xa, xb - 1, y); } else { clipRes2 = clipRes; } for (x = xa; x < xb; ++x) { // map (x+0.5, y+0.5) back to the scaled image xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 + ((SplashCoord)y + 0.5 - mat[5]) * ir10); yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 + ((SplashCoord)y + 0.5 - mat[5]) * ir11); // xx should always be within bounds, but floating point // inaccuracy can cause problems if (unlikely(xx < 0)) { xx = 0; clipRes2 = splashClipPartial; } else if (unlikely(xx >= scaledWidth)) { xx = scaledWidth - 1; clipRes2 = splashClipPartial; } if (unlikely(yy < 0)) { yy = 0; clipRes2 = splashClipPartial; } else if (unlikely(yy >= scaledHeight)) { yy = scaledHeight - 1; clipRes2 = splashClipPartial; } pipe.shape = scaledMask->data[yy * scaledWidth + xx]; if (vectorAntialias && clipRes2 != splashClipAllInside) { drawAAPixel(&pipe, x, y); } else { drawPixel(&pipe, x, y, clipRes2 == splashClipAllInside); } } } } delete scaledMask; } // Scale an image mask into a SplashBitmap. SplashBitmap *Splash::scaleMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight) { SplashBitmap *dest; dest = new SplashBitmap(scaledWidth, scaledHeight, 1, splashModeMono8, false); if (scaledHeight < srcHeight) { if (scaledWidth < srcWidth) { scaleMaskYdownXdown(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } else { scaleMaskYdownXup(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } } else { if (scaledWidth < srcWidth) { scaleMaskYupXdown(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } else { scaleMaskYupXup(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } } return dest; } void Splash::scaleMaskYdownXdown(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { unsigned char *lineBuf; unsigned int *pixBuf; unsigned int pix; unsigned char *destPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1; int i, j; // Bresenham parameters for y scale yp = srcHeight / scaledHeight; yq = srcHeight % scaledHeight; // Bresenham parameters for x scale xp = srcWidth / scaledWidth; xq = srcWidth % scaledWidth; // allocate buffers lineBuf = (unsigned char *)gmalloc(srcWidth); pixBuf = (unsigned int *)gmallocn_checkoverflow(srcWidth, sizeof(int)); if (unlikely(!pixBuf)) { error(errInternal, -1, "Couldn't allocate memory for pixBux in Splash::scaleMaskYdownXdown"); gfree(lineBuf); return; } // init y scale Bresenham yt = 0; destPtr = dest->data; for (y = 0; y < scaledHeight; ++y) { // y scale Bresenham if ((yt += yq) >= scaledHeight) { yt -= scaledHeight; yStep = yp + 1; } else { yStep = yp; } // read rows from image memset(pixBuf, 0, srcWidth * sizeof(int)); for (i = 0; i < yStep; ++i) { (*src)(srcData, lineBuf); for (j = 0; j < srcWidth; ++j) { pixBuf[j] += lineBuf[j]; } } // init x scale Bresenham xt = 0; d0 = (255 << 23) / (yStep * xp); d1 = (255 << 23) / (yStep * (xp + 1)); xx = 0; for (x = 0; x < scaledWidth; ++x) { // x scale Bresenham if ((xt += xq) >= scaledWidth) { xt -= scaledWidth; xStep = xp + 1; d = d1; } else { xStep = xp; d = d0; } // compute the final pixel pix = 0; for (i = 0; i < xStep; ++i) { pix += pixBuf[xx++]; } // (255 * pix) / xStep * yStep pix = (pix * d) >> 23; // store the pixel *destPtr++ = (unsigned char)pix; } } gfree(pixBuf); gfree(lineBuf); } void Splash::scaleMaskYdownXup(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { unsigned char *lineBuf; unsigned int *pixBuf; unsigned int pix; unsigned char *destPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d; int i, j; destPtr = dest->data; if (destPtr == nullptr) { error(errInternal, -1, "dest->data is NULL in Splash::scaleMaskYdownXup"); return; } // Bresenham parameters for y scale yp = srcHeight / scaledHeight; yq = srcHeight % scaledHeight; // Bresenham parameters for x scale xp = scaledWidth / srcWidth; xq = scaledWidth % srcWidth; // allocate buffers lineBuf = (unsigned char *)gmalloc(srcWidth); pixBuf = (unsigned int *)gmallocn(srcWidth, sizeof(int)); // init y scale Bresenham yt = 0; for (y = 0; y < scaledHeight; ++y) { // y scale Bresenham if ((yt += yq) >= scaledHeight) { yt -= scaledHeight; yStep = yp + 1; } else { yStep = yp; } // read rows from image memset(pixBuf, 0, srcWidth * sizeof(int)); for (i = 0; i < yStep; ++i) { (*src)(srcData, lineBuf); for (j = 0; j < srcWidth; ++j) { pixBuf[j] += lineBuf[j]; } } // init x scale Bresenham xt = 0; d = (255 << 23) / yStep; for (x = 0; x < srcWidth; ++x) { // x scale Bresenham if ((xt += xq) >= srcWidth) { xt -= srcWidth; xStep = xp + 1; } else { xStep = xp; } // compute the final pixel pix = pixBuf[x]; // (255 * pix) / yStep pix = (pix * d) >> 23; // store the pixel for (i = 0; i < xStep; ++i) { *destPtr++ = (unsigned char)pix; } } } gfree(pixBuf); gfree(lineBuf); } void Splash::scaleMaskYupXdown(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { unsigned char *lineBuf; unsigned int pix; unsigned char *destPtr0, *destPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1; int i; destPtr0 = dest->data; if (destPtr0 == nullptr) { error(errInternal, -1, "dest->data is NULL in Splash::scaleMaskYupXdown"); return; } // Bresenham parameters for y scale yp = scaledHeight / srcHeight; yq = scaledHeight % srcHeight; // Bresenham parameters for x scale xp = srcWidth / scaledWidth; xq = srcWidth % scaledWidth; // allocate buffers lineBuf = (unsigned char *)gmalloc(srcWidth); // init y scale Bresenham yt = 0; for (y = 0; y < srcHeight; ++y) { // y scale Bresenham if ((yt += yq) >= srcHeight) { yt -= srcHeight; yStep = yp + 1; } else { yStep = yp; } // read row from image (*src)(srcData, lineBuf); // init x scale Bresenham xt = 0; d0 = (255 << 23) / xp; d1 = (255 << 23) / (xp + 1); xx = 0; for (x = 0; x < scaledWidth; ++x) { // x scale Bresenham if ((xt += xq) >= scaledWidth) { xt -= scaledWidth; xStep = xp + 1; d = d1; } else { xStep = xp; d = d0; } // compute the final pixel pix = 0; for (i = 0; i < xStep; ++i) { pix += lineBuf[xx++]; } // (255 * pix) / xStep pix = (pix * d) >> 23; // store the pixel for (i = 0; i < yStep; ++i) { destPtr = destPtr0 + i * scaledWidth + x; *destPtr = (unsigned char)pix; } } destPtr0 += yStep * scaledWidth; } gfree(lineBuf); } void Splash::scaleMaskYupXup(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { unsigned char *lineBuf; unsigned int pix; unsigned char *destPtr0, *destPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx; int i, j; destPtr0 = dest->data; if (destPtr0 == nullptr) { error(errInternal, -1, "dest->data is NULL in Splash::scaleMaskYupXup"); return; } if (unlikely(srcWidth <= 0 || srcHeight <= 0)) { error(errSyntaxError, -1, "srcWidth <= 0 || srcHeight <= 0 in Splash::scaleMaskYupXup"); gfree(dest->takeData()); return; } // Bresenham parameters for y scale yp = scaledHeight / srcHeight; yq = scaledHeight % srcHeight; // Bresenham parameters for x scale xp = scaledWidth / srcWidth; xq = scaledWidth % srcWidth; // allocate buffers lineBuf = (unsigned char *)gmalloc(srcWidth); // init y scale Bresenham yt = 0; for (y = 0; y < srcHeight; ++y) { // y scale Bresenham if ((yt += yq) >= srcHeight) { yt -= srcHeight; yStep = yp + 1; } else { yStep = yp; } // read row from image (*src)(srcData, lineBuf); // init x scale Bresenham xt = 0; xx = 0; for (x = 0; x < srcWidth; ++x) { // x scale Bresenham if ((xt += xq) >= srcWidth) { xt -= srcWidth; xStep = xp + 1; } else { xStep = xp; } // compute the final pixel pix = lineBuf[x] ? 255 : 0; // store the pixel for (i = 0; i < yStep; ++i) { for (j = 0; j < xStep; ++j) { destPtr = destPtr0 + i * scaledWidth + xx + j; *destPtr++ = (unsigned char)pix; } } xx += xStep; } destPtr0 += yStep * scaledWidth; } gfree(lineBuf); } void Splash::blitMask(SplashBitmap *src, int xDest, int yDest, SplashClipResult clipRes) { SplashPipe pipe; unsigned char *p; int w, h, x, y; w = src->getWidth(); h = src->getHeight(); p = src->getDataPtr(); if (p == nullptr) { error(errInternal, -1, "src->getDataPtr() is NULL in Splash::blitMask"); return; } if (vectorAntialias && clipRes != splashClipAllInside) { pipeInit(&pipe, xDest, yDest, state->fillPattern, nullptr, (unsigned char)splashRound(state->fillAlpha * 255), true, false); drawAAPixelInit(); for (y = 0; y < h; ++y) { for (x = 0; x < w; ++x) { pipe.shape = *p++; drawAAPixel(&pipe, xDest + x, yDest + y); } } } else { pipeInit(&pipe, xDest, yDest, state->fillPattern, nullptr, (unsigned char)splashRound(state->fillAlpha * 255), true, false); if (clipRes == splashClipAllInside) { for (y = 0; y < h; ++y) { pipeSetXY(&pipe, xDest, yDest + y); for (x = 0; x < w; ++x) { if (*p) { pipe.shape = *p; (this->*pipe.run)(&pipe); } else { pipeIncX(&pipe); } ++p; } } } else { for (y = 0; y < h; ++y) { pipeSetXY(&pipe, xDest, yDest + y); for (x = 0; x < w; ++x) { if (*p && state->clip->test(xDest + x, yDest + y)) { pipe.shape = *p; (this->*pipe.run)(&pipe); } else { pipeIncX(&pipe); } ++p; } } } } } SplashError Splash::drawImage(SplashImageSource src, SplashICCTransform tf, void *srcData, SplashColorMode srcMode, bool srcAlpha, int w, int h, SplashCoord *mat, bool interpolate, bool tilingPattern) { bool ok; SplashBitmap *scaledImg; SplashClipResult clipRes; bool minorAxisZero; int x0, y0, x1, y1, scaledWidth, scaledHeight; int nComps; int yp; if (debugMode) { printf("drawImage: srcMode=%d srcAlpha=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n", srcMode, srcAlpha, w, h, (double)mat[0], (double)mat[1], (double)mat[2], (double)mat[3], (double)mat[4], (double)mat[5]); } // check color modes ok = false; // make gcc happy nComps = 0; // make gcc happy switch (bitmap->mode) { case splashModeMono1: case splashModeMono8: ok = srcMode == splashModeMono8; nComps = 1; break; case splashModeRGB8: ok = srcMode == splashModeRGB8; nComps = 3; break; case splashModeXBGR8: ok = srcMode == splashModeXBGR8; nComps = 4; break; case splashModeBGR8: ok = srcMode == splashModeBGR8; nComps = 3; break; case splashModeCMYK8: ok = srcMode == splashModeCMYK8; nComps = 4; break; case splashModeDeviceN8: ok = srcMode == splashModeDeviceN8; nComps = SPOT_NCOMPS + 4; break; default: ok = false; break; } if (!ok) { return splashErrModeMismatch; } // check for singular matrix if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) { return splashErrSingularMatrix; } minorAxisZero = mat[1] == 0 && mat[2] == 0; // scaling only if (mat[0] > 0 && minorAxisZero && mat[3] > 0) { x0 = imgCoordMungeLower(mat[4]); y0 = imgCoordMungeLower(mat[5]); x1 = imgCoordMungeUpper(mat[0] + mat[4]); y1 = imgCoordMungeUpper(mat[3] + mat[5]); // make sure narrow images cover at least one pixel if (x0 == x1) { ++x1; } if (y0 == y1) { ++y1; } clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; yp = h / scaledHeight; if (yp < 0 || yp > INT_MAX - 1) { return splashErrBadArg; } scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, scaledWidth, scaledHeight, interpolate, tilingPattern); if (scaledImg == nullptr) { return splashErrBadArg; } if (tf != nullptr) { (*tf)(srcData, scaledImg); } blitImage(scaledImg, srcAlpha, x0, y0, clipRes); delete scaledImg; } // scaling plus vertical flip } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) { x0 = imgCoordMungeLower(mat[4]); y0 = imgCoordMungeLower(mat[3] + mat[5]); x1 = imgCoordMungeUpper(mat[0] + mat[4]); y1 = imgCoordMungeUpper(mat[5]); if (x0 == x1) { if (mat[4] + mat[0] * 0.5 < x0) { --x0; } else { ++x1; } } if (y0 == y1) { if (mat[5] + mat[1] * 0.5 < y0) { --y0; } else { ++y1; } } clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; yp = h / scaledHeight; if (yp < 0 || yp > INT_MAX - 1) { return splashErrBadArg; } scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, scaledWidth, scaledHeight, interpolate, tilingPattern); if (scaledImg == nullptr) { return splashErrBadArg; } if (tf != nullptr) { (*tf)(srcData, scaledImg); } vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps); blitImage(scaledImg, srcAlpha, x0, y0, clipRes); delete scaledImg; } // all other cases } else { return arbitraryTransformImage(src, tf, srcData, srcMode, nComps, srcAlpha, w, h, mat, interpolate, tilingPattern); } return splashOk; } SplashError Splash::arbitraryTransformImage(SplashImageSource src, SplashICCTransform tf, void *srcData, SplashColorMode srcMode, int nComps, bool srcAlpha, int srcWidth, int srcHeight, SplashCoord *mat, bool interpolate, bool tilingPattern) { SplashBitmap *scaledImg; SplashClipResult clipRes, clipRes2; SplashPipe pipe; SplashColor pixel = {}; int scaledWidth, scaledHeight, t0, t1, th; SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11; SplashCoord vx[4], vy[4]; int xMin, yMin, xMax, yMax; ImageSection section[3]; int nSections; int y, xa, xb, x, i, xx, yy, yp; // compute the four vertices of the target quadrilateral vx[0] = mat[4]; vy[0] = mat[5]; vx[1] = mat[2] + mat[4]; vy[1] = mat[3] + mat[5]; vx[2] = mat[0] + mat[2] + mat[4]; vy[2] = mat[1] + mat[3] + mat[5]; vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5]; // clipping xMin = imgCoordMungeLower(vx[0]); xMax = imgCoordMungeUpper(vx[0]); yMin = imgCoordMungeLower(vy[0]); yMax = imgCoordMungeUpper(vy[0]); for (i = 1; i < 4; ++i) { t0 = imgCoordMungeLower(vx[i]); if (t0 < xMin) { xMin = t0; } t0 = imgCoordMungeUpper(vx[i]); if (t0 > xMax) { xMax = t0; } t1 = imgCoordMungeLower(vy[i]); if (t1 < yMin) { yMin = t1; } t1 = imgCoordMungeUpper(vy[i]); if (t1 > yMax) { yMax = t1; } } clipRes = state->clip->testRect(xMin, yMin, xMax, yMax); opClipRes = clipRes; if (clipRes == splashClipAllOutside) { return splashOk; } // compute the scale factors if (splashAbs(mat[0]) >= splashAbs(mat[1])) { scaledWidth = xMax - xMin; scaledHeight = yMax - yMin; } else { scaledWidth = yMax - yMin; scaledHeight = xMax - xMin; } if (scaledHeight <= 1 || scaledWidth <= 1 || tilingPattern) { if (mat[0] >= 0) { t0 = imgCoordMungeUpper(mat[0] + mat[4]) - imgCoordMungeLower(mat[4]); } else { t0 = imgCoordMungeUpper(mat[4]) - imgCoordMungeLower(mat[0] + mat[4]); } if (mat[1] >= 0) { t1 = imgCoordMungeUpper(mat[1] + mat[5]) - imgCoordMungeLower(mat[5]); } else { t1 = imgCoordMungeUpper(mat[5]) - imgCoordMungeLower(mat[1] + mat[5]); } scaledWidth = t0 > t1 ? t0 : t1; if (mat[2] >= 0) { t0 = imgCoordMungeUpper(mat[2] + mat[4]) - imgCoordMungeLower(mat[4]); if (splashAbs(mat[1]) >= 1) { th = imgCoordMungeUpper(mat[2]) - imgCoordMungeLower(mat[0] * mat[3] / mat[1]); if (th > t0) { t0 = th; } } } else { t0 = imgCoordMungeUpper(mat[4]) - imgCoordMungeLower(mat[2] + mat[4]); if (splashAbs(mat[1]) >= 1) { th = imgCoordMungeUpper(mat[0] * mat[3] / mat[1]) - imgCoordMungeLower(mat[2]); if (th > t0) { t0 = th; } } } if (mat[3] >= 0) { t1 = imgCoordMungeUpper(mat[3] + mat[5]) - imgCoordMungeLower(mat[5]); if (splashAbs(mat[0]) >= 1) { th = imgCoordMungeUpper(mat[3]) - imgCoordMungeLower(mat[1] * mat[2] / mat[0]); if (th > t1) { t1 = th; } } } else { t1 = imgCoordMungeUpper(mat[5]) - imgCoordMungeLower(mat[3] + mat[5]); if (splashAbs(mat[0]) >= 1) { th = imgCoordMungeUpper(mat[1] * mat[2] / mat[0]) - imgCoordMungeLower(mat[3]); if (th > t1) { t1 = th; } } } scaledHeight = t0 > t1 ? t0 : t1; } if (scaledWidth == 0) { scaledWidth = 1; } if (scaledHeight == 0) { scaledHeight = 1; } // compute the inverse transform (after scaling) matrix r00 = mat[0] / scaledWidth; r01 = mat[1] / scaledWidth; r10 = mat[2] / scaledHeight; r11 = mat[3] / scaledHeight; det = r00 * r11 - r01 * r10; if (splashAbs(det) < 1e-6) { // this should be caught by the singular matrix check in drawImage return splashErrBadArg; } ir00 = r11 / det; ir01 = -r01 / det; ir10 = -r10 / det; ir11 = r00 / det; // scale the input image yp = srcHeight / scaledHeight; if (yp < 0 || yp > INT_MAX - 1) { return splashErrBadArg; } scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, interpolate); if (scaledImg == nullptr) { return splashErrBadArg; } if (tf != nullptr) { (*tf)(srcData, scaledImg); } // construct the three sections i = 0; if (vy[1] < vy[i]) { i = 1; } if (vy[2] < vy[i]) { i = 2; } if (vy[3] < vy[i]) { i = 3; } // NB: if using fixed point, 0.000001 will be truncated to zero, // so these two comparisons must be <=, not < if (splashAbs(vy[i] - vy[(i - 1) & 3]) <= 0.000001 && vy[(i - 1) & 3] < vy[(i + 1) & 3]) { i = (i - 1) & 3; } if (splashAbs(vy[i] - vy[(i + 1) & 3]) <= 0.000001) { section[0].y0 = imgCoordMungeLower(vy[i]); section[0].y1 = imgCoordMungeUpper(vy[(i + 2) & 3]) - 1; if (vx[i] < vx[(i + 1) & 3]) { section[0].ia0 = i; section[0].ia1 = (i + 3) & 3; section[0].ib0 = (i + 1) & 3; section[0].ib1 = (i + 2) & 3; } else { section[0].ia0 = (i + 1) & 3; section[0].ia1 = (i + 2) & 3; section[0].ib0 = i; section[0].ib1 = (i + 3) & 3; } nSections = 1; } else { section[0].y0 = imgCoordMungeLower(vy[i]); section[2].y1 = imgCoordMungeUpper(vy[(i + 2) & 3]) - 1; section[0].ia0 = section[0].ib0 = i; section[2].ia1 = section[2].ib1 = (i + 2) & 3; if (vx[(i + 1) & 3] < vx[(i + 3) & 3]) { section[0].ia1 = section[2].ia0 = (i + 1) & 3; section[0].ib1 = section[2].ib0 = (i + 3) & 3; } else { section[0].ia1 = section[2].ia0 = (i + 3) & 3; section[0].ib1 = section[2].ib0 = (i + 1) & 3; } if (vy[(i + 1) & 3] < vy[(i + 3) & 3]) { section[1].y0 = imgCoordMungeLower(vy[(i + 1) & 3]); section[2].y0 = imgCoordMungeUpper(vy[(i + 3) & 3]); if (vx[(i + 1) & 3] < vx[(i + 3) & 3]) { section[1].ia0 = (i + 1) & 3; section[1].ia1 = (i + 2) & 3; section[1].ib0 = i; section[1].ib1 = (i + 3) & 3; } else { section[1].ia0 = i; section[1].ia1 = (i + 3) & 3; section[1].ib0 = (i + 1) & 3; section[1].ib1 = (i + 2) & 3; } } else { section[1].y0 = imgCoordMungeLower(vy[(i + 3) & 3]); section[2].y0 = imgCoordMungeUpper(vy[(i + 1) & 3]); if (vx[(i + 1) & 3] < vx[(i + 3) & 3]) { section[1].ia0 = i; section[1].ia1 = (i + 1) & 3; section[1].ib0 = (i + 3) & 3; section[1].ib1 = (i + 2) & 3; } else { section[1].ia0 = (i + 3) & 3; section[1].ia1 = (i + 2) & 3; section[1].ib0 = i; section[1].ib1 = (i + 1) & 3; } } section[0].y1 = section[1].y0 - 1; section[1].y1 = section[2].y0 - 1; nSections = 3; } for (i = 0; i < nSections; ++i) { section[i].xa0 = vx[section[i].ia0]; section[i].ya0 = vy[section[i].ia0]; section[i].xa1 = vx[section[i].ia1]; section[i].ya1 = vy[section[i].ia1]; section[i].xb0 = vx[section[i].ib0]; section[i].yb0 = vy[section[i].ib0]; section[i].xb1 = vx[section[i].ib1]; section[i].yb1 = vy[section[i].ib1]; section[i].dxdya = (section[i].xa1 - section[i].xa0) / (section[i].ya1 - section[i].ya0); section[i].dxdyb = (section[i].xb1 - section[i].xb0) / (section[i].yb1 - section[i].yb0); } // initialize the pixel pipe pipeInit(&pipe, 0, 0, nullptr, pixel, (unsigned char)splashRound(state->fillAlpha * 255), srcAlpha || (vectorAntialias && clipRes != splashClipAllInside), false); if (vectorAntialias) { drawAAPixelInit(); } // make sure narrow images cover at least one pixel if (nSections == 1) { if (section[0].y0 == section[0].y1) { ++section[0].y1; clipRes = opClipRes = splashClipPartial; } } else { if (section[0].y0 == section[2].y1) { ++section[1].y1; clipRes = opClipRes = splashClipPartial; } } // scan all pixels inside the target region for (i = 0; i < nSections; ++i) { for (y = section[i].y0; y <= section[i].y1; ++y) { xa = imgCoordMungeLower(section[i].xa0 + ((SplashCoord)y + 0.5 - section[i].ya0) * section[i].dxdya); if (unlikely(xa < 0)) { xa = 0; } xb = imgCoordMungeUpper(section[i].xb0 + ((SplashCoord)y + 0.5 - section[i].yb0) * section[i].dxdyb); // make sure narrow images cover at least one pixel 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 { clipRes2 = clipRes; } for (x = xa; x < xb; ++x) { // map (x+0.5, y+0.5) back to the scaled image xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 + ((SplashCoord)y + 0.5 - mat[5]) * ir10); yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 + ((SplashCoord)y + 0.5 - mat[5]) * ir11); // xx should always be within bounds, but floating point // inaccuracy can cause problems if (xx < 0) { xx = 0; } else if (xx >= scaledWidth) { xx = scaledWidth - 1; } if (yy < 0) { yy = 0; } else if (yy >= scaledHeight) { yy = scaledHeight - 1; } scaledImg->getPixel(xx, yy, pixel); if (srcAlpha) { pipe.shape = scaledImg->alpha[yy * scaledWidth + xx]; } else { pipe.shape = 255; } if (vectorAntialias && clipRes2 != splashClipAllInside) { drawAAPixel(&pipe, x, y); } else { drawPixel(&pipe, x, y, clipRes2 == splashClipAllInside); } } } } delete scaledImg; return splashOk; } // determine if a scaled image requires interpolation based on the scale and // the interpolate flag from the image dictionary static bool isImageInterpolationRequired(int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, bool interpolate) { if (interpolate || srcWidth == 0 || srcHeight == 0) { return true; } /* When scale factor is >= 400% we don't interpolate. See bugs #25268, #9860 */ if (scaledWidth / srcWidth >= 4 || scaledHeight / srcHeight >= 4) { return false; } return true; } // Scale an image into a SplashBitmap. SplashBitmap *Splash::scaleImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, bool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, bool interpolate, bool tilingPattern) { SplashBitmap *dest; dest = new SplashBitmap(scaledWidth, scaledHeight, 1, srcMode, srcAlpha, true, bitmap->getSeparationList()); if (dest->getDataPtr() != nullptr && srcHeight > 0 && srcWidth > 0) { bool success = true; if (scaledHeight < srcHeight) { if (scaledWidth < srcWidth) { success = scaleImageYdownXdown(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } else { success = scaleImageYdownXup(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } } else { if (scaledWidth < srcWidth) { success = scaleImageYupXdown(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } else { if (!tilingPattern && isImageInterpolationRequired(srcWidth, srcHeight, scaledWidth, scaledHeight, interpolate)) { success = scaleImageYupXupBilinear(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } else { success = scaleImageYupXup(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } } } if (unlikely(!success)) { delete dest; dest = nullptr; } } else { delete dest; dest = nullptr; } return dest; } bool Splash::scaleImageYdownXdown(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, bool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { unsigned char *lineBuf, *alphaLineBuf; unsigned int *pixBuf, *alphaPixBuf; unsigned int pix0, pix1, pix2; unsigned int pix3; unsigned int pix[SPOT_NCOMPS + 4], cp; unsigned int alpha; unsigned char *destPtr, *destAlphaPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1; int i, j; // Bresenham parameters for y scale yp = srcHeight / scaledHeight; yq = srcHeight % scaledHeight; // Bresenham parameters for x scale xp = srcWidth / scaledWidth; xq = srcWidth % scaledWidth; // allocate buffers lineBuf = (unsigned char *)gmallocn_checkoverflow(srcWidth, nComps); if (unlikely(!lineBuf)) { return false; } pixBuf = (unsigned int *)gmallocn_checkoverflow(srcWidth, nComps * sizeof(int)); if (unlikely(!pixBuf)) { gfree(lineBuf); return false; } if (srcAlpha) { alphaLineBuf = (unsigned char *)gmalloc(srcWidth); alphaPixBuf = (unsigned int *)gmallocn(srcWidth, sizeof(int)); } else { alphaLineBuf = nullptr; alphaPixBuf = nullptr; } // init y scale Bresenham yt = 0; destPtr = dest->data; destAlphaPtr = dest->alpha; for (y = 0; y < scaledHeight; ++y) { // y scale Bresenham if ((yt += yq) >= scaledHeight) { yt -= scaledHeight; yStep = yp + 1; } else { yStep = yp; } // read rows from image memset(pixBuf, 0, srcWidth * nComps * sizeof(int)); if (srcAlpha) { memset(alphaPixBuf, 0, srcWidth * sizeof(int)); } for (i = 0; i < yStep; ++i) { (*src)(srcData, lineBuf, alphaLineBuf); for (j = 0; j < srcWidth * nComps; ++j) { pixBuf[j] += lineBuf[j]; } if (srcAlpha) { for (j = 0; j < srcWidth; ++j) { alphaPixBuf[j] += alphaLineBuf[j]; } } } // init x scale Bresenham xt = 0; d0 = (1 << 23) / (yStep * xp); d1 = (1 << 23) / (yStep * (xp + 1)); xx = xxa = 0; for (x = 0; x < scaledWidth; ++x) { // x scale Bresenham if ((xt += xq) >= scaledWidth) { xt -= scaledWidth; xStep = xp + 1; d = d1; } else { xStep = xp; d = d0; } switch (srcMode) { case splashModeMono8: // compute the final pixel pix0 = 0; for (i = 0; i < xStep; ++i) { pix0 += pixBuf[xx++]; } // pix / xStep * yStep pix0 = (pix0 * d) >> 23; // store the pixel *destPtr++ = (unsigned char)pix0; break; case splashModeRGB8: // compute the final pixel pix0 = pix1 = pix2 = 0; for (i = 0; i < xStep; ++i) { pix0 += pixBuf[xx]; pix1 += pixBuf[xx + 1]; pix2 += pixBuf[xx + 2]; xx += 3; } // pix / xStep * yStep pix0 = (pix0 * d) >> 23; pix1 = (pix1 * d) >> 23; pix2 = (pix2 * d) >> 23; // store the pixel *destPtr++ = (unsigned char)pix0; *destPtr++ = (unsigned char)pix1; *destPtr++ = (unsigned char)pix2; break; case splashModeXBGR8: // compute the final pixel pix0 = pix1 = pix2 = 0; for (i = 0; i < xStep; ++i) { pix0 += pixBuf[xx]; pix1 += pixBuf[xx + 1]; pix2 += pixBuf[xx + 2]; xx += 4; } // pix / xStep * yStep pix0 = (pix0 * d) >> 23; pix1 = (pix1 * d) >> 23; pix2 = (pix2 * d) >> 23; // store the pixel *destPtr++ = (unsigned char)pix2; *destPtr++ = (unsigned char)pix1; *destPtr++ = (unsigned char)pix0; *destPtr++ = (unsigned char)255; break; case splashModeBGR8: // compute the final pixel pix0 = pix1 = pix2 = 0; for (i = 0; i < xStep; ++i) { pix0 += pixBuf[xx]; pix1 += pixBuf[xx + 1]; pix2 += pixBuf[xx + 2]; xx += 3; } // pix / xStep * yStep pix0 = (pix0 * d) >> 23; pix1 = (pix1 * d) >> 23; pix2 = (pix2 * d) >> 23; // store the pixel *destPtr++ = (unsigned char)pix2; *destPtr++ = (unsigned char)pix1; *destPtr++ = (unsigned char)pix0; break; case splashModeCMYK8: // compute the final pixel pix0 = pix1 = pix2 = pix3 = 0; for (i = 0; i < xStep; ++i) { pix0 += pixBuf[xx]; pix1 += pixBuf[xx + 1]; pix2 += pixBuf[xx + 2]; pix3 += pixBuf[xx + 3]; xx += 4; } // pix / xStep * yStep pix0 = (pix0 * d) >> 23; pix1 = (pix1 * d) >> 23; pix2 = (pix2 * d) >> 23; pix3 = (pix3 * d) >> 23; // store the pixel *destPtr++ = (unsigned char)pix0; *destPtr++ = (unsigned char)pix1; *destPtr++ = (unsigned char)pix2; *destPtr++ = (unsigned char)pix3; break; case splashModeDeviceN8: // compute the final pixel for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) { pix[cp] = 0; } for (i = 0; i < xStep; ++i) { for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) { pix[cp] += pixBuf[xx + cp]; } xx += (SPOT_NCOMPS + 4); } // pix / xStep * yStep for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) { pix[cp] = (pix[cp] * d) >> 23; } // store the pixel for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) { *destPtr++ = (unsigned char)pix[cp]; } break; case splashModeMono1: // mono1 is not allowed default: break; } // process alpha if (srcAlpha) { alpha = 0; for (i = 0; i < xStep; ++i, ++xxa) { alpha += alphaPixBuf[xxa]; } // alpha / xStep * yStep alpha = (alpha * d) >> 23; *destAlphaPtr++ = (unsigned char)alpha; } } } gfree(alphaPixBuf); gfree(alphaLineBuf); gfree(pixBuf); gfree(lineBuf); return true; } bool Splash::scaleImageYdownXup(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, bool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { unsigned char *lineBuf, *alphaLineBuf; unsigned int *pixBuf, *alphaPixBuf; unsigned int pix[splashMaxColorComps]; unsigned int alpha; unsigned char *destPtr, *destAlphaPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d; int i, j; // Bresenham parameters for y scale yp = srcHeight / scaledHeight; yq = srcHeight % scaledHeight; // Bresenham parameters for x scale xp = scaledWidth / srcWidth; xq = scaledWidth % srcWidth; // allocate buffers pixBuf = (unsigned int *)gmallocn_checkoverflow(srcWidth, nComps * sizeof(int)); if (unlikely(!pixBuf)) { error(errInternal, -1, "Splash::scaleImageYdownXup. Couldn't allocate pixBuf memory"); return false; } lineBuf = (unsigned char *)gmallocn(srcWidth, nComps); if (srcAlpha) { alphaLineBuf = (unsigned char *)gmalloc(srcWidth); alphaPixBuf = (unsigned int *)gmallocn(srcWidth, sizeof(int)); } else { alphaLineBuf = nullptr; alphaPixBuf = nullptr; } // init y scale Bresenham yt = 0; destPtr = dest->data; destAlphaPtr = dest->alpha; for (y = 0; y < scaledHeight; ++y) { // y scale Bresenham if ((yt += yq) >= scaledHeight) { yt -= scaledHeight; yStep = yp + 1; } else { yStep = yp; } // read rows from image memset(pixBuf, 0, srcWidth * nComps * sizeof(int)); if (srcAlpha) { memset(alphaPixBuf, 0, srcWidth * sizeof(int)); } for (i = 0; i < yStep; ++i) { (*src)(srcData, lineBuf, alphaLineBuf); for (j = 0; j < srcWidth * nComps; ++j) { pixBuf[j] += lineBuf[j]; } if (srcAlpha) { for (j = 0; j < srcWidth; ++j) { alphaPixBuf[j] += alphaLineBuf[j]; } } } // init x scale Bresenham xt = 0; d = (1 << 23) / yStep; for (x = 0; x < srcWidth; ++x) { // x scale Bresenham if ((xt += xq) >= srcWidth) { xt -= srcWidth; xStep = xp + 1; } else { xStep = xp; } // compute the final pixel for (i = 0; i < nComps; ++i) { // pixBuf[] / yStep pix[i] = (pixBuf[x * nComps + i] * d) >> 23; } // store the pixel switch (srcMode) { case splashModeMono1: // mono1 is not allowed break; case splashModeMono8: for (i = 0; i < xStep; ++i) { *destPtr++ = (unsigned char)pix[0]; } break; case splashModeRGB8: for (i = 0; i < xStep; ++i) { *destPtr++ = (unsigned char)pix[0]; *destPtr++ = (unsigned char)pix[1]; *destPtr++ = (unsigned char)pix[2]; } break; case splashModeXBGR8: for (i = 0; i < xStep; ++i) { *destPtr++ = (unsigned char)pix[2]; *destPtr++ = (unsigned char)pix[1]; *destPtr++ = (unsigned char)pix[0]; *destPtr++ = (unsigned char)255; } break; case splashModeBGR8: for (i = 0; i < xStep; ++i) { *destPtr++ = (unsigned char)pix[2]; *destPtr++ = (unsigned char)pix[1]; *destPtr++ = (unsigned char)pix[0]; } break; case splashModeCMYK8: for (i = 0; i < xStep; ++i) { *destPtr++ = (unsigned char)pix[0]; *destPtr++ = (unsigned char)pix[1]; *destPtr++ = (unsigned char)pix[2]; *destPtr++ = (unsigned char)pix[3]; } break; case splashModeDeviceN8: for (i = 0; i < xStep; ++i) { for (unsigned int cp : pix) { *destPtr++ = (unsigned char)cp; } } break; } // process alpha if (srcAlpha) { // alphaPixBuf[] / yStep alpha = (alphaPixBuf[x] * d) >> 23; for (i = 0; i < xStep; ++i) { *destAlphaPtr++ = (unsigned char)alpha; } } } } gfree(alphaPixBuf); gfree(alphaLineBuf); gfree(pixBuf); gfree(lineBuf); return true; } bool Splash::scaleImageYupXdown(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, bool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { unsigned char *lineBuf, *alphaLineBuf; unsigned int pix[splashMaxColorComps]; unsigned int alpha; unsigned char *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1; int i, j; // Bresenham parameters for y scale yp = scaledHeight / srcHeight; yq = scaledHeight % srcHeight; // Bresenham parameters for x scale xp = srcWidth / scaledWidth; xq = srcWidth % scaledWidth; // allocate buffers lineBuf = (unsigned char *)gmallocn_checkoverflow(srcWidth, nComps); if (unlikely(!lineBuf)) { gfree(dest->takeData()); return false; } if (srcAlpha) { alphaLineBuf = (unsigned char *)gmalloc(srcWidth); } else { alphaLineBuf = nullptr; } // init y scale Bresenham yt = 0; destPtr0 = dest->data; destAlphaPtr0 = dest->alpha; for (y = 0; y < srcHeight; ++y) { // y scale Bresenham if ((yt += yq) >= srcHeight) { yt -= srcHeight; yStep = yp + 1; } else { yStep = yp; } // read row from image (*src)(srcData, lineBuf, alphaLineBuf); // init x scale Bresenham xt = 0; d0 = (1 << 23) / xp; d1 = (1 << 23) / (xp + 1); xx = xxa = 0; for (x = 0; x < scaledWidth; ++x) { // x scale Bresenham if ((xt += xq) >= scaledWidth) { xt -= scaledWidth; xStep = xp + 1; d = d1; } else { xStep = xp; d = d0; } // compute the final pixel for (i = 0; i < nComps; ++i) { pix[i] = 0; } for (i = 0; i < xStep; ++i) { for (j = 0; j < nComps; ++j, ++xx) { pix[j] += lineBuf[xx]; } } for (i = 0; i < nComps; ++i) { // pix[] / xStep pix[i] = (pix[i] * d) >> 23; } // store the pixel switch (srcMode) { case splashModeMono1: // mono1 is not allowed break; case splashModeMono8: for (i = 0; i < yStep; ++i) { destPtr = destPtr0 + (i * scaledWidth + x) * nComps; *destPtr++ = (unsigned char)pix[0]; } break; case splashModeRGB8: for (i = 0; i < yStep; ++i) { destPtr = destPtr0 + (i * scaledWidth + x) * nComps; *destPtr++ = (unsigned char)pix[0]; *destPtr++ = (unsigned char)pix[1]; *destPtr++ = (unsigned char)pix[2]; } break; case splashModeXBGR8: for (i = 0; i < yStep; ++i) { destPtr = destPtr0 + (i * scaledWidth + x) * nComps; *destPtr++ = (unsigned char)pix[2]; *destPtr++ = (unsigned char)pix[1]; *destPtr++ = (unsigned char)pix[0]; *destPtr++ = (unsigned char)255; } break; case splashModeBGR8: for (i = 0; i < yStep; ++i) { destPtr = destPtr0 + (i * scaledWidth + x) * nComps; *destPtr++ = (unsigned char)pix[2]; *destPtr++ = (unsigned char)pix[1]; *destPtr++ = (unsigned char)pix[0]; } break; case splashModeCMYK8: for (i = 0; i < yStep; ++i) { destPtr = destPtr0 + (i * scaledWidth + x) * nComps; *destPtr++ = (unsigned char)pix[0]; *destPtr++ = (unsigned char)pix[1]; *destPtr++ = (unsigned char)pix[2]; *destPtr++ = (unsigned char)pix[3]; } break; case splashModeDeviceN8: for (i = 0; i < yStep; ++i) { destPtr = destPtr0 + (i * scaledWidth + x) * nComps; for (unsigned int cp : pix) { *destPtr++ = (unsigned char)cp; } } break; } // process alpha if (srcAlpha) { alpha = 0; for (i = 0; i < xStep; ++i, ++xxa) { alpha += alphaLineBuf[xxa]; } // alpha / xStep alpha = (alpha * d) >> 23; for (i = 0; i < yStep; ++i) { destAlphaPtr = destAlphaPtr0 + i * scaledWidth + x; *destAlphaPtr = (unsigned char)alpha; } } } destPtr0 += yStep * scaledWidth * nComps; if (srcAlpha) { destAlphaPtr0 += yStep * scaledWidth; } } gfree(alphaLineBuf); gfree(lineBuf); return true; } bool Splash::scaleImageYupXup(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, bool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { unsigned char *lineBuf, *alphaLineBuf; unsigned int pix[splashMaxColorComps]; unsigned int alpha; unsigned char *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx; int i, j; // Bresenham parameters for y scale yp = scaledHeight / srcHeight; yq = scaledHeight % srcHeight; // Bresenham parameters for x scale xp = scaledWidth / srcWidth; xq = scaledWidth % srcWidth; // allocate buffers lineBuf = (unsigned char *)gmallocn(srcWidth, nComps); if (srcAlpha) { alphaLineBuf = (unsigned char *)gmalloc(srcWidth); } else { alphaLineBuf = nullptr; } // init y scale Bresenham yt = 0; destPtr0 = dest->data; destAlphaPtr0 = dest->alpha; for (y = 0; y < srcHeight; ++y) { // y scale Bresenham if ((yt += yq) >= srcHeight) { yt -= srcHeight; yStep = yp + 1; } else { yStep = yp; } // read row from image (*src)(srcData, lineBuf, alphaLineBuf); // init x scale Bresenham xt = 0; xx = 0; for (x = 0; x < srcWidth; ++x) { // x scale Bresenham if ((xt += xq) >= srcWidth) { xt -= srcWidth; xStep = xp + 1; } else { xStep = xp; } // compute the final pixel for (i = 0; i < nComps; ++i) { pix[i] = lineBuf[x * nComps + i]; } // store the pixel switch (srcMode) { case splashModeMono1: // mono1 is not allowed break; case splashModeMono8: for (i = 0; i < yStep; ++i) { for (j = 0; j < xStep; ++j) { destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; *destPtr++ = (unsigned char)pix[0]; } } break; case splashModeRGB8: for (i = 0; i < yStep; ++i) { for (j = 0; j < xStep; ++j) { destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; *destPtr++ = (unsigned char)pix[0]; *destPtr++ = (unsigned char)pix[1]; *destPtr++ = (unsigned char)pix[2]; } } break; case splashModeXBGR8: for (i = 0; i < yStep; ++i) { for (j = 0; j < xStep; ++j) { destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; *destPtr++ = (unsigned char)pix[2]; *destPtr++ = (unsigned char)pix[1]; *destPtr++ = (unsigned char)pix[0]; *destPtr++ = (unsigned char)255; } } break; case splashModeBGR8: for (i = 0; i < yStep; ++i) { for (j = 0; j < xStep; ++j) { destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; *destPtr++ = (unsigned char)pix[2]; *destPtr++ = (unsigned char)pix[1]; *destPtr++ = (unsigned char)pix[0]; } } break; case splashModeCMYK8: for (i = 0; i < yStep; ++i) { for (j = 0; j < xStep; ++j) { destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; *destPtr++ = (unsigned char)pix[0]; *destPtr++ = (unsigned char)pix[1]; *destPtr++ = (unsigned char)pix[2]; *destPtr++ = (unsigned char)pix[3]; } } break; case splashModeDeviceN8: for (i = 0; i < yStep; ++i) { for (j = 0; j < xStep; ++j) { destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; for (unsigned int cp : pix) { *destPtr++ = (unsigned char)cp; } } } break; } // process alpha if (srcAlpha) { alpha = alphaLineBuf[x]; for (i = 0; i < yStep; ++i) { for (j = 0; j < xStep; ++j) { destAlphaPtr = destAlphaPtr0 + i * scaledWidth + xx + j; *destAlphaPtr = (unsigned char)alpha; } } } xx += xStep; } destPtr0 += yStep * scaledWidth * nComps; if (srcAlpha) { destAlphaPtr0 += yStep * scaledWidth; } } gfree(alphaLineBuf); gfree(lineBuf); return true; } // expand source row to scaledWidth using linear interpolation static void expandRow(unsigned char *srcBuf, unsigned char *dstBuf, int srcWidth, int scaledWidth, int nComps) { double xStep = (double)srcWidth / scaledWidth; double xSrc = 0.0; double xFrac, xInt; int p; // pad the source with an extra pixel equal to the last pixel // so that when xStep is inside the last pixel we still have two // pixels to interpolate between. for (int i = 0; i < nComps; i++) { srcBuf[srcWidth * nComps + i] = srcBuf[(srcWidth - 1) * nComps + i]; } for (int x = 0; x < scaledWidth; x++) { xFrac = modf(xSrc, &xInt); p = (int)xInt; for (int c = 0; c < nComps; c++) { dstBuf[nComps * x + c] = static_cast(srcBuf[nComps * p + c] * (1.0 - xFrac) + srcBuf[nComps * (p + 1) + c] * xFrac); } xSrc += xStep; } } // Scale up image using bilinear interpolation bool Splash::scaleImageYupXupBilinear(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, bool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { unsigned char *srcBuf, *lineBuf1, *lineBuf2, *alphaSrcBuf, *alphaLineBuf1, *alphaLineBuf2; unsigned int pix[splashMaxColorComps]; unsigned char *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr; int i; if (srcWidth < 1 || srcHeight < 1) { return false; } // 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); if (srcAlpha) { alphaSrcBuf = (unsigned char *)gmalloc(srcWidth + 1); // + 1 pixel of padding alphaLineBuf1 = (unsigned char *)gmalloc(scaledWidth); alphaLineBuf2 = (unsigned char *)gmalloc(scaledWidth); } else { alphaSrcBuf = nullptr; alphaLineBuf1 = nullptr; alphaLineBuf2 = nullptr; } double ySrc = 0.0; double yStep = (double)srcHeight / scaledHeight; double yFrac, yInt; int currentSrcRow = -1; (*src)(srcData, srcBuf, alphaSrcBuf); expandRow(srcBuf, lineBuf2, srcWidth, scaledWidth, nComps); if (srcAlpha) { expandRow(alphaSrcBuf, alphaLineBuf2, srcWidth, scaledWidth, 1); } destPtr0 = dest->data; destAlphaPtr0 = dest->alpha; for (int y = 0; y < scaledHeight; y++) { yFrac = modf(ySrc, &yInt); if ((int)yInt > currentSrcRow) { currentSrcRow++; // Copy line2 data to line1 and get next line2 data. // If line2 already contains the last source row we don't touch it. // This effectively adds an extra row of padding for interpolating the // last source row with. memcpy(lineBuf1, lineBuf2, scaledWidth * nComps); if (srcAlpha) { memcpy(alphaLineBuf1, alphaLineBuf2, scaledWidth); } if (currentSrcRow < srcHeight - 1) { (*src)(srcData, srcBuf, alphaSrcBuf); expandRow(srcBuf, lineBuf2, srcWidth, scaledWidth, nComps); if (srcAlpha) { expandRow(alphaSrcBuf, alphaLineBuf2, srcWidth, scaledWidth, 1); } } } // write row y using linear interpolation on lineBuf1 and lineBuf2 for (int x = 0; x < scaledWidth; ++x) { // compute the final pixel for (i = 0; i < nComps; ++i) { pix[i] = static_cast(lineBuf1[x * nComps + i] * (1.0 - yFrac) + lineBuf2[x * nComps + i] * yFrac); } // store the pixel destPtr = destPtr0 + (y * scaledWidth + x) * nComps; switch (srcMode) { case splashModeMono1: // mono1 is not allowed break; case splashModeMono8: *destPtr++ = (unsigned char)pix[0]; break; case splashModeRGB8: *destPtr++ = (unsigned char)pix[0]; *destPtr++ = (unsigned char)pix[1]; *destPtr++ = (unsigned char)pix[2]; break; case splashModeXBGR8: *destPtr++ = (unsigned char)pix[2]; *destPtr++ = (unsigned char)pix[1]; *destPtr++ = (unsigned char)pix[0]; *destPtr++ = (unsigned char)255; break; case splashModeBGR8: *destPtr++ = (unsigned char)pix[2]; *destPtr++ = (unsigned char)pix[1]; *destPtr++ = (unsigned char)pix[0]; break; case splashModeCMYK8: *destPtr++ = (unsigned char)pix[0]; *destPtr++ = (unsigned char)pix[1]; *destPtr++ = (unsigned char)pix[2]; *destPtr++ = (unsigned char)pix[3]; break; case splashModeDeviceN8: for (unsigned int cp : pix) { *destPtr++ = (unsigned char)cp; } break; } // process alpha if (srcAlpha) { destAlphaPtr = destAlphaPtr0 + y * scaledWidth + x; *destAlphaPtr = static_cast(alphaLineBuf1[x] * (1.0 - yFrac) + alphaLineBuf2[x] * yFrac); } } ySrc += yStep; } gfree(alphaSrcBuf); gfree(alphaLineBuf1); gfree(alphaLineBuf2); gfree(srcBuf); gfree(lineBuf1); gfree(lineBuf2); return true; } void Splash::vertFlipImage(SplashBitmap *img, int width, int height, int nComps) { unsigned char *lineBuf; unsigned char *p0, *p1; int w; if (unlikely(img->data == nullptr)) { error(errInternal, -1, "img->data is NULL in Splash::vertFlipImage"); return; } w = width * nComps; lineBuf = (unsigned char *)gmalloc(w); for (p0 = img->data, p1 = img->data + (height - 1) * w; p0 < p1; p0 += w, p1 -= w) { memcpy(lineBuf, p0, w); memcpy(p0, p1, w); memcpy(p1, lineBuf, w); } if (img->alpha) { for (p0 = img->alpha, p1 = img->alpha + (height - 1) * width; p0 < p1; p0 += width, p1 -= width) { memcpy(lineBuf, p0, width); memcpy(p0, p1, width); memcpy(p1, lineBuf, width); } } gfree(lineBuf); } void Splash::blitImage(SplashBitmap *src, bool srcAlpha, int xDest, int yDest) { SplashClipResult clipRes = state->clip->testRect(xDest, yDest, xDest + src->getWidth() - 1, yDest + src->getHeight() - 1); if (clipRes != splashClipAllOutside) { blitImage(src, srcAlpha, xDest, yDest, clipRes); } } void Splash::blitImage(SplashBitmap *src, bool srcAlpha, int xDest, int yDest, SplashClipResult clipRes) { SplashPipe pipe; SplashColor pixel = {}; unsigned char *ap; int w, h, x0, y0, x1, y1, x, y; // split the image into clipped and unclipped regions w = src->getWidth(); h = src->getHeight(); if (clipRes == splashClipAllInside) { x0 = 0; y0 = 0; x1 = w; y1 = h; } else { if (state->clip->getNumPaths()) { x0 = x1 = w; y0 = y1 = h; } else { if ((x0 = splashCeil(state->clip->getXMin()) - xDest) < 0) { x0 = 0; } if ((y0 = splashCeil(state->clip->getYMin()) - yDest) < 0) { y0 = 0; } if ((x1 = splashFloor(state->clip->getXMax()) - xDest) > w) { x1 = w; } if (x1 < x0) { x1 = x0; } if ((y1 = splashFloor(state->clip->getYMax()) - yDest) > h) { y1 = h; } if (y1 < y0) { y1 = y0; } } } // draw the unclipped region if (x0 < w && y0 < h && x0 < x1 && y0 < y1) { pipeInit(&pipe, xDest + x0, yDest + y0, nullptr, pixel, (unsigned char)splashRound(state->fillAlpha * 255), srcAlpha, false); if (srcAlpha) { for (y = y0; y < y1; ++y) { pipeSetXY(&pipe, xDest + x0, yDest + y); ap = src->getAlphaPtr() + y * w + x0; for (x = x0; x < x1; ++x) { src->getPixel(x, y, pixel); pipe.shape = *ap++; (this->*pipe.run)(&pipe); } } } else { for (y = y0; y < y1; ++y) { pipeSetXY(&pipe, xDest + x0, yDest + y); for (x = x0; x < x1; ++x) { src->getPixel(x, y, pixel); (this->*pipe.run)(&pipe); } } } } // draw the clipped regions if (y0 > 0) { blitImageClipped(src, srcAlpha, 0, 0, xDest, yDest, w, y0); } if (y1 < h) { blitImageClipped(src, srcAlpha, 0, y1, xDest, yDest + y1, w, h - y1); } if (x0 > 0 && y0 < y1) { blitImageClipped(src, srcAlpha, 0, y0, xDest, yDest + y0, x0, y1 - y0); } if (x1 < w && y0 < y1) { blitImageClipped(src, srcAlpha, x1, y0, xDest + x1, yDest + y0, w - x1, y1 - y0); } } void Splash::blitImageClipped(SplashBitmap *src, bool srcAlpha, int xSrc, int ySrc, int xDest, int yDest, int w, int h) { SplashPipe pipe; SplashColor pixel = {}; unsigned char *ap; int x, y; if (vectorAntialias) { pipeInit(&pipe, xDest, yDest, nullptr, pixel, (unsigned char)splashRound(state->fillAlpha * 255), true, false); drawAAPixelInit(); if (srcAlpha) { for (y = 0; y < h; ++y) { ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc; for (x = 0; x < w; ++x) { src->getPixel(xSrc + x, ySrc + y, pixel); pipe.shape = *ap++; drawAAPixel(&pipe, xDest + x, yDest + y); } } } else { for (y = 0; y < h; ++y) { for (x = 0; x < w; ++x) { src->getPixel(xSrc + x, ySrc + y, pixel); pipe.shape = 255; drawAAPixel(&pipe, xDest + x, yDest + y); } } } } else { pipeInit(&pipe, xDest, yDest, nullptr, pixel, (unsigned char)splashRound(state->fillAlpha * 255), srcAlpha, false); if (srcAlpha) { for (y = 0; y < h; ++y) { ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc; pipeSetXY(&pipe, xDest, yDest + y); for (x = 0; x < w; ++x) { if (state->clip->test(xDest + x, yDest + y)) { src->getPixel(xSrc + x, ySrc + y, pixel); pipe.shape = *ap++; (this->*pipe.run)(&pipe); } else { pipeIncX(&pipe); ++ap; } } } } else { for (y = 0; y < h; ++y) { pipeSetXY(&pipe, xDest, yDest + y); for (x = 0; x < w; ++x) { if (state->clip->test(xDest + x, yDest + y)) { src->getPixel(xSrc + x, ySrc + y, pixel); (this->*pipe.run)(&pipe); } else { pipeIncX(&pipe); } } } } } } SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc, int xDest, int yDest, int w, int h, bool noClip, bool nonIsolated, bool knockout, SplashCoord knockoutOpacity) { SplashPipe pipe; SplashColor pixel; unsigned char alpha; unsigned char *ap; int x, y; if (src->mode != bitmap->mode) { return splashErrModeMismatch; } if (unlikely(!bitmap->data)) { return splashErrZeroImage; } 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()); } } if (src->alpha) { pipeInit(&pipe, xDest, yDest, nullptr, pixel, (unsigned char)splashRound(state->fillAlpha * 255), true, nonIsolated, knockout, (unsigned char)splashRound(knockoutOpacity * 255)); if (noClip) { for (y = 0; y < h; ++y) { pipeSetXY(&pipe, xDest, yDest + y); ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc; for (x = 0; x < w; ++x) { src->getPixel(xSrc + x, ySrc + y, pixel); alpha = *ap++; // this uses shape instead of alpha, which isn't technically // correct, but works out the same pipe.shape = alpha; (this->*pipe.run)(&pipe); } } } else { for (y = 0; y < h; ++y) { pipeSetXY(&pipe, xDest, yDest + y); ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc; for (x = 0; x < w; ++x) { src->getPixel(xSrc + x, ySrc + y, pixel); alpha = *ap++; if (state->clip->test(xDest + x, yDest + y)) { // this uses shape instead of alpha, which isn't technically // correct, but works out the same pipe.shape = alpha; (this->*pipe.run)(&pipe); } else { pipeIncX(&pipe); } } } } } else { pipeInit(&pipe, xDest, yDest, nullptr, pixel, (unsigned char)splashRound(state->fillAlpha * 255), false, nonIsolated); if (noClip) { for (y = 0; y < h; ++y) { pipeSetXY(&pipe, xDest, yDest + y); for (x = 0; x < w; ++x) { src->getPixel(xSrc + x, ySrc + y, pixel); (this->*pipe.run)(&pipe); } } } else { for (y = 0; y < h; ++y) { pipeSetXY(&pipe, xDest, yDest + y); for (x = 0; x < w; ++x) { src->getPixel(xSrc + x, ySrc + y, pixel); if (state->clip->test(xDest + x, yDest + y)) { (this->*pipe.run)(&pipe); } else { pipeIncX(&pipe); } } } } } return splashOk; } void Splash::compositeBackground(SplashColorConstPtr color) { SplashColorPtr p; unsigned char *q; unsigned char alpha, alpha1, c, color0, color1, color2; unsigned char color3; unsigned char colorsp[SPOT_NCOMPS + 4], cp; int x, y, mask; if (unlikely(bitmap->alpha == nullptr)) { error(errInternal, -1, "bitmap->alpha is NULL in Splash::compositeBackground"); return; } switch (bitmap->mode) { case splashModeMono1: color0 = color[0]; for (y = 0; y < bitmap->height; ++y) { p = &bitmap->data[y * bitmap->rowSize]; q = &bitmap->alpha[y * bitmap->width]; mask = 0x80; for (x = 0; x < bitmap->width; ++x) { alpha = *q++; alpha1 = 255 - alpha; c = (*p & mask) ? 0xff : 0x00; c = div255(alpha1 * color0 + alpha * c); if (c & 0x80) { *p |= mask; } else { *p &= ~mask; } if (!(mask >>= 1)) { mask = 0x80; ++p; } } } break; case splashModeMono8: color0 = color[0]; for (y = 0; y < bitmap->height; ++y) { p = &bitmap->data[y * bitmap->rowSize]; q = &bitmap->alpha[y * bitmap->width]; for (x = 0; x < bitmap->width; ++x) { alpha = *q++; alpha1 = 255 - alpha; p[0] = div255(alpha1 * color0 + alpha * p[0]); ++p; } } break; case splashModeRGB8: case splashModeBGR8: color0 = color[0]; color1 = color[1]; color2 = color[2]; for (y = 0; y < bitmap->height; ++y) { p = &bitmap->data[y * bitmap->rowSize]; q = &bitmap->alpha[y * bitmap->width]; for (x = 0; x < bitmap->width; ++x) { alpha = *q++; if (alpha == 0) { p[0] = color0; p[1] = color1; p[2] = color2; } else if (alpha != 255) { alpha1 = 255 - alpha; p[0] = div255(alpha1 * color0 + alpha * p[0]); p[1] = div255(alpha1 * color1 + alpha * p[1]); p[2] = div255(alpha1 * color2 + alpha * p[2]); } p += 3; } } break; case splashModeXBGR8: color0 = color[0]; color1 = color[1]; color2 = color[2]; for (y = 0; y < bitmap->height; ++y) { p = &bitmap->data[y * bitmap->rowSize]; q = &bitmap->alpha[y * bitmap->width]; for (x = 0; x < bitmap->width; ++x) { alpha = *q++; if (alpha == 0) { p[0] = color0; p[1] = color1; p[2] = color2; } else if (alpha != 255) { alpha1 = 255 - alpha; p[0] = div255(alpha1 * color0 + alpha * p[0]); p[1] = div255(alpha1 * color1 + alpha * p[1]); p[2] = div255(alpha1 * color2 + alpha * p[2]); } p[3] = 255; p += 4; } } break; case splashModeCMYK8: color0 = color[0]; color1 = color[1]; color2 = color[2]; color3 = color[3]; for (y = 0; y < bitmap->height; ++y) { p = &bitmap->data[y * bitmap->rowSize]; q = &bitmap->alpha[y * bitmap->width]; for (x = 0; x < bitmap->width; ++x) { alpha = *q++; if (alpha == 0) { p[0] = color0; p[1] = color1; p[2] = color2; p[3] = color3; } else if (alpha != 255) { alpha1 = 255 - alpha; p[0] = div255(alpha1 * color0 + alpha * p[0]); p[1] = div255(alpha1 * color1 + alpha * p[1]); p[2] = div255(alpha1 * color2 + alpha * p[2]); p[3] = div255(alpha1 * color3 + alpha * p[3]); } p += 4; } } break; case splashModeDeviceN8: for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) { colorsp[cp] = color[cp]; } for (y = 0; y < bitmap->height; ++y) { p = &bitmap->data[y * bitmap->rowSize]; q = &bitmap->alpha[y * bitmap->width]; for (x = 0; x < bitmap->width; ++x) { alpha = *q++; if (alpha == 0) { for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) { p[cp] = colorsp[cp]; } } else if (alpha != 255) { alpha1 = 255 - alpha; for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) { p[cp] = div255(alpha1 * colorsp[cp] + alpha * p[cp]); } } p += (SPOT_NCOMPS + 4); } } break; } memset(bitmap->alpha, 255, bitmap->width * bitmap->height); } bool Splash::gouraudTriangleShadedFill(SplashGouraudColor *shading) { double xdbl[3] = { 0., 0., 0. }; double ydbl[3] = { 0., 0., 0. }; int x[3] = { 0, 0, 0 }; int y[3] = { 0, 0, 0 }; double xt = 0., xa = 0., yt = 0.; const int bitmapWidth = bitmap->getWidth(); SplashClip *clip = getClip(); SplashBitmap *blitTarget = bitmap; SplashColorPtr bitmapData = bitmap->getDataPtr(); const int bitmapOffLimit = bitmap->getHeight() * bitmap->getRowSize(); SplashColorPtr bitmapAlpha = bitmap->getAlphaPtr(); SplashCoord *userToCanvasMatrix = getMatrix(); const SplashColorMode bitmapMode = bitmap->getMode(); bool hasAlpha = (bitmapAlpha != nullptr); const int rowSize = bitmap->getRowSize(); const int colorComps = splashColorModeNComps[bitmapMode]; SplashPipe pipe; SplashColor cSrcVal; pipeInit(&pipe, 0, 0, nullptr, cSrcVal, (unsigned char)splashRound(state->fillAlpha * 255), false, false); if (vectorAntialias) { if (aaBuf == nullptr) { return false; // fall back to old behaviour } drawAAPixelInit(); } // idea: // 1. If pipe->noTransparency && !state->blendFunc // -> blit directly into the drawing surface! // -> disable alpha manually. // 2. Otherwise: // - blit also directly, but into an intermediate surface. // Afterwards, blit the intermediate surface using the drawing pipeline. // This is necessary because triangle elements can be on top of each // other, so the complete shading needs to be drawn before opacity is // applied. // - the final step, is performed using a SplashPipe: // - assign the actual color into cSrcVal: pipe uses cSrcVal by reference // - invoke drawPixel(&pipe,X,Y,bNoClip); const bool bDirectBlit = vectorAntialias ? false : pipe.noTransparency && !state->blendFunc && !shading->isParameterized(); if (!bDirectBlit) { blitTarget = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), bitmap->getRowPad(), bitmap->getMode(), true, bitmap->getRowSize() >= 0); bitmapData = blitTarget->getDataPtr(); bitmapAlpha = blitTarget->getAlphaPtr(); // initialisation seems to be necessary: const int S = bitmap->getWidth() * bitmap->getHeight(); for (int i = 0; i < S; ++i) { bitmapAlpha[i] = 0; } hasAlpha = true; } if (shading->isParameterized()) { double color[3]; double scanLimitMapL[2] = { 0., 0. }; double scanLimitMapR[2] = { 0., 0. }; double scanColorMapL[2] = { 0., 0. }; double scanColorMapR[2] = { 0., 0. }; int scanEdgeL[2] = { 0, 0 }; int scanEdgeR[2] = { 0, 0 }; for (int i = 0; i < shading->getNTriangles(); ++i) { shading->getParametrizedTriangle(i, xdbl + 0, ydbl + 0, color + 0, xdbl + 1, ydbl + 1, color + 1, xdbl + 2, ydbl + 2, color + 2); for (int m = 0; m < 3; ++m) { xt = xdbl[m] * (double)userToCanvasMatrix[0] + ydbl[m] * (double)userToCanvasMatrix[2] + (double)userToCanvasMatrix[4]; yt = xdbl[m] * (double)userToCanvasMatrix[1] + ydbl[m] * (double)userToCanvasMatrix[3] + (double)userToCanvasMatrix[5]; xdbl[m] = xt; ydbl[m] = yt; // we operate on scanlines which are integer offsets into the // raster image. The double offsets are of no use here. x[m] = splashRound(xt); y[m] = splashRound(yt); } // sort according to y coordinate to simplify sweep through scanlines: // INSERTION SORT. if (y[0] > y[1]) { Guswap(x[0], x[1]); Guswap(y[0], y[1]); Guswap(color[0], color[1]); } // first two are sorted. assert(y[0] <= y[1]); if (y[1] > y[2]) { const int tmpX = x[2]; const int tmpY = y[2]; const double tmpC = color[2]; x[2] = x[1]; y[2] = y[1]; color[2] = color[1]; if (y[0] > tmpY) { x[1] = x[0]; y[1] = y[0]; color[1] = color[0]; x[0] = tmpX; y[0] = tmpY; color[0] = tmpC; } else { x[1] = tmpX; y[1] = tmpY; color[1] = tmpC; } } // first three are sorted assert(y[0] <= y[1]); assert(y[1] <= y[2]); ///// // 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. } // this here initialises the scanline generation. // We start with low Y coordinates and sweep up to the large Y // coordinates. // // scanEdgeL[m] in {0,1,2} m=0,1 // scanEdgeR[m] in {0,1,2} m=0,1 // // are the two edges between which scanlines are (currently) // sweeped. The values {0,1,2} are indices into 'x' and 'y'. // scanEdgeL[0] = 0 means: the left scan edge has (x[0],y[0]) as vertex. // scanEdgeL[0] = 0; scanEdgeR[0] = 0; if (y[0] == y[1]) { scanEdgeL[0] = 1; scanEdgeL[1] = scanEdgeR[1] = 2; } else { scanEdgeL[1] = 1; scanEdgeR[1] = 2; } assert(y[scanEdgeL[0]] < y[scanEdgeL[1]]); assert(y[scanEdgeR[0]] < y[scanEdgeR[1]]); // Ok. Now prepare the linear maps which map the y coordinate of // the current scanline to the corresponding LEFT and RIGHT x // coordinate (which define the scanline). scanLimitMapL[0] = double(x[scanEdgeL[1]] - x[scanEdgeL[0]]) / (y[scanEdgeL[1]] - y[scanEdgeL[0]]); scanLimitMapL[1] = x[scanEdgeL[0]] - y[scanEdgeL[0]] * scanLimitMapL[0]; scanLimitMapR[0] = double(x[scanEdgeR[1]] - x[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]); scanLimitMapR[1] = x[scanEdgeR[0]] - y[scanEdgeR[0]] * scanLimitMapR[0]; xa = y[1] * scanLimitMapL[0] + scanLimitMapL[1]; xt = y[1] * scanLimitMapR[0] + scanLimitMapR[1]; if (xa > xt) { // I have "left" is to the right of "right". // Exchange sides! Guswap(scanEdgeL[0], scanEdgeR[0]); Guswap(scanEdgeL[1], scanEdgeR[1]); Guswap(scanLimitMapL[0], scanLimitMapR[0]); Guswap(scanLimitMapL[1], scanLimitMapR[1]); // FIXME I'm sure there is a more efficient way to check this. } // Same game: we can linearly interpolate the color based on the // current y coordinate (that's correct for triangle // interpolation due to linearity. We could also have done it in // barycentric coordinates, but that's slightly more involved) scanColorMapL[0] = (color[scanEdgeL[1]] - color[scanEdgeL[0]]) / (y[scanEdgeL[1]] - y[scanEdgeL[0]]); scanColorMapL[1] = color[scanEdgeL[0]] - y[scanEdgeL[0]] * scanColorMapL[0]; scanColorMapR[0] = (color[scanEdgeR[1]] - color[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]); scanColorMapR[1] = color[scanEdgeR[0]] - y[scanEdgeR[0]] * scanColorMapR[0]; bool hasFurtherSegment = (y[1] < y[2]); int scanLineOff = y[0] * rowSize; for (int Y = y[0]; Y <= y[2]; ++Y, scanLineOff += rowSize) { if (hasFurtherSegment && Y == y[1]) { // SWEEP EVENT: we encountered the next segment. // // switch to next segment, either at left end or at right // end: if (scanEdgeL[1] == 1) { scanEdgeL[0] = 1; scanEdgeL[1] = 2; scanLimitMapL[0] = double(x[scanEdgeL[1]] - x[scanEdgeL[0]]) / (y[scanEdgeL[1]] - y[scanEdgeL[0]]); scanLimitMapL[1] = x[scanEdgeL[0]] - y[scanEdgeL[0]] * scanLimitMapL[0]; scanColorMapL[0] = (color[scanEdgeL[1]] - color[scanEdgeL[0]]) / (y[scanEdgeL[1]] - y[scanEdgeL[0]]); scanColorMapL[1] = color[scanEdgeL[0]] - y[scanEdgeL[0]] * scanColorMapL[0]; } else if (scanEdgeR[1] == 1) { scanEdgeR[0] = 1; scanEdgeR[1] = 2; scanLimitMapR[0] = double(x[scanEdgeR[1]] - x[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]); scanLimitMapR[1] = x[scanEdgeR[0]] - y[scanEdgeR[0]] * scanLimitMapR[0]; scanColorMapR[0] = (color[scanEdgeR[1]] - color[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]); scanColorMapR[1] = color[scanEdgeR[0]] - y[scanEdgeR[0]] * scanColorMapR[0]; } assert(y[scanEdgeL[0]] < y[scanEdgeL[1]]); assert(y[scanEdgeR[0]] < y[scanEdgeR[1]]); hasFurtherSegment = false; } yt = Y; xa = yt * scanLimitMapL[0] + scanLimitMapL[1]; xt = yt * scanLimitMapR[0] + scanLimitMapR[1]; const double ca = yt * scanColorMapL[0] + scanColorMapL[1]; const double ct = yt * scanColorMapR[0] + scanColorMapR[1]; const int scanLimitL = splashRound(xa); const int scanLimitR = splashRound(xt); // Ok. Now: init the color interpolation depending on the X // coordinate inside of the current scanline: const double scanColorMap0 = (scanLimitR == scanLimitL) ? 0. : ((ct - ca) / (scanLimitR - scanLimitL)); const double scanColorMap1 = ca - scanLimitL * scanColorMap0; // handled by clipping: // assert( scanLimitL >= 0 && scanLimitR < bitmap->getWidth() ); assert(scanLimitL <= scanLimitR || abs(scanLimitL - scanLimitR) <= 2); // allow rounding inaccuracies assert(scanLineOff == Y * rowSize); double colorinterp = scanColorMap0 * scanLimitL + scanColorMap1; int bitmapOff = scanLineOff + scanLimitL * colorComps; if (likely(bitmapOff >= 0)) { for (int X = scanLimitL; X <= scanLimitR && bitmapOff + colorComps <= bitmapOffLimit; ++X, colorinterp += scanColorMap0, bitmapOff += colorComps) { // FIXME : standard rectangular clipping can be done for a // complete scanline which is faster // --> see SplashClip and its methods if (!clip->test(X, Y)) { continue; } assert(fabs(colorinterp - (scanColorMap0 * X + scanColorMap1)) < 1e-7); assert(bitmapOff == Y * rowSize + colorComps * X && scanLineOff == Y * rowSize); shading->getParameterizedColor(colorinterp, bitmapMode, &bitmapData[bitmapOff]); // make the shading visible. // Note that opacity is handled by the bDirectBlit stuff, see // above for comments and below for implementation. if (hasAlpha) { bitmapAlpha[Y * bitmapWidth + X] = 255; } } } } } } else { SplashColor color, auxColor1, auxColor2; double scanLimitMapL[2] = { 0., 0. }; double scanLimitMapR[2] = { 0., 0. }; int scanEdgeL[2] = { 0, 0 }; int scanEdgeR[2] = { 0, 0 }; for (int i = 0; i < shading->getNTriangles(); ++i) { // Sadly this current algorithm only supports shadings where the three triangle vertices have the same color shading->getNonParametrizedTriangle(i, bitmapMode, xdbl + 0, ydbl + 0, (SplashColorPtr)&color, xdbl + 1, ydbl + 1, (SplashColorPtr)&auxColor1, xdbl + 2, ydbl + 2, (SplashColorPtr)&auxColor2); if (!splashColorEqual(color, auxColor1) || !splashColorEqual(color, auxColor2)) { if (!bDirectBlit) { delete blitTarget; } return false; } for (int m = 0; m < 3; ++m) { xt = xdbl[m] * (double)userToCanvasMatrix[0] + ydbl[m] * (double)userToCanvasMatrix[2] + (double)userToCanvasMatrix[4]; yt = xdbl[m] * (double)userToCanvasMatrix[1] + ydbl[m] * (double)userToCanvasMatrix[3] + (double)userToCanvasMatrix[5]; xdbl[m] = xt; ydbl[m] = yt; // we operate on scanlines which are integer offsets into the // raster image. The double offsets are of no use here. x[m] = splashRound(xt); y[m] = splashRound(yt); } // sort according to y coordinate to simplify sweep through scanlines: // INSERTION SORT. if (y[0] > y[1]) { Guswap(x[0], x[1]); Guswap(y[0], y[1]); } // first two are sorted. assert(y[0] <= y[1]); if (y[1] > y[2]) { const int tmpX = x[2]; const int tmpY = y[2]; x[2] = x[1]; y[2] = y[1]; if (y[0] > tmpY) { x[1] = x[0]; y[1] = y[0]; x[0] = tmpX; y[0] = tmpY; } else { x[1] = tmpX; y[1] = tmpY; } } // first three are sorted assert(y[0] <= y[1]); assert(y[1] <= y[2]); ///// // 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. } // this here initialises the scanline generation. // We start with low Y coordinates and sweep up to the large Y // coordinates. // // scanEdgeL[m] in {0,1,2} m=0,1 // scanEdgeR[m] in {0,1,2} m=0,1 // // are the two edges between which scanlines are (currently) // sweeped. The values {0,1,2} are indices into 'x' and 'y'. // scanEdgeL[0] = 0 means: the left scan edge has (x[0],y[0]) as vertex. // scanEdgeL[0] = 0; scanEdgeR[0] = 0; if (y[0] == y[1]) { scanEdgeL[0] = 1; scanEdgeL[1] = scanEdgeR[1] = 2; } else { scanEdgeL[1] = 1; scanEdgeR[1] = 2; } assert(y[scanEdgeL[0]] < y[scanEdgeL[1]]); assert(y[scanEdgeR[0]] < y[scanEdgeR[1]]); // Ok. Now prepare the linear maps which map the y coordinate of // the current scanline to the corresponding LEFT and RIGHT x // coordinate (which define the scanline). scanLimitMapL[0] = double(x[scanEdgeL[1]] - x[scanEdgeL[0]]) / (y[scanEdgeL[1]] - y[scanEdgeL[0]]); scanLimitMapL[1] = x[scanEdgeL[0]] - y[scanEdgeL[0]] * scanLimitMapL[0]; scanLimitMapR[0] = double(x[scanEdgeR[1]] - x[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]); scanLimitMapR[1] = x[scanEdgeR[0]] - y[scanEdgeR[0]] * scanLimitMapR[0]; xa = y[1] * scanLimitMapL[0] + scanLimitMapL[1]; xt = y[1] * scanLimitMapR[0] + scanLimitMapR[1]; if (xa > xt) { // I have "left" is to the right of "right". // Exchange sides! Guswap(scanEdgeL[0], scanEdgeR[0]); Guswap(scanEdgeL[1], scanEdgeR[1]); Guswap(scanLimitMapL[0], scanLimitMapR[0]); Guswap(scanLimitMapL[1], scanLimitMapR[1]); // FIXME I'm sure there is a more efficient way to check this. } bool hasFurtherSegment = (y[1] < y[2]); int scanLineOff = y[0] * rowSize; for (int Y = y[0]; Y <= y[2]; ++Y, scanLineOff += rowSize) { if (hasFurtherSegment && Y == y[1]) { // SWEEP EVENT: we encountered the next segment. // // switch to next segment, either at left end or at right // end: if (scanEdgeL[1] == 1) { scanEdgeL[0] = 1; scanEdgeL[1] = 2; scanLimitMapL[0] = double(x[scanEdgeL[1]] - x[scanEdgeL[0]]) / (y[scanEdgeL[1]] - y[scanEdgeL[0]]); scanLimitMapL[1] = x[scanEdgeL[0]] - y[scanEdgeL[0]] * scanLimitMapL[0]; } else if (scanEdgeR[1] == 1) { scanEdgeR[0] = 1; scanEdgeR[1] = 2; scanLimitMapR[0] = double(x[scanEdgeR[1]] - x[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]); scanLimitMapR[1] = x[scanEdgeR[0]] - y[scanEdgeR[0]] * scanLimitMapR[0]; } assert(y[scanEdgeL[0]] < y[scanEdgeL[1]]); assert(y[scanEdgeR[0]] < y[scanEdgeR[1]]); hasFurtherSegment = false; } yt = Y; xa = yt * scanLimitMapL[0] + scanLimitMapL[1]; xt = yt * scanLimitMapR[0] + scanLimitMapR[1]; const int scanLimitL = splashRound(xa); const int scanLimitR = splashRound(xt); // handled by clipping: // assert( scanLimitL >= 0 && scanLimitR < bitmap->getWidth() ); assert(scanLimitL <= scanLimitR || abs(scanLimitL - scanLimitR) <= 2); // allow rounding inaccuracies assert(scanLineOff == Y * rowSize); int bitmapOff = scanLineOff + scanLimitL * colorComps; if (likely(bitmapOff >= 0)) { for (int X = scanLimitL; X <= scanLimitR && bitmapOff + colorComps <= bitmapOffLimit; ++X, bitmapOff += colorComps) { // FIXME : standard rectangular clipping can be done for a // complete scanline which is faster // --> see SplashClip and its methods if (!clip->test(X, Y)) { continue; } assert(bitmapOff == Y * rowSize + colorComps * X && scanLineOff == Y * rowSize); for (int k = 0; k < colorComps; ++k) { bitmapData[bitmapOff + k] = color[k]; } // make the shading visible. // Note that opacity is handled by the bDirectBlit stuff, see // above for comments and below for implementation. if (hasAlpha) { bitmapAlpha[Y * bitmapWidth + X] = 255; } } } } } } if (!bDirectBlit) { // ok. Finalize the stuff by blitting the shading into the final // geometry, this time respecting the rendering pipe. const int W = blitTarget->getWidth(); const int H = blitTarget->getHeight(); SplashColorPtr cur = cSrcVal; for (int X = 0; X < W; ++X) { for (int Y = 0; Y < H; ++Y) { if (!bitmapAlpha[Y * bitmapWidth + X]) { continue; // draw only parts of the shading! } const int bitmapOff = Y * rowSize + colorComps * X; for (int m = 0; m < colorComps; ++m) { cur[m] = bitmapData[bitmapOff + m]; } if (vectorAntialias) { drawAAPixel(&pipe, X, Y); } else { drawPixel(&pipe, X, Y, true); // no clipping - has already been done. } } } delete blitTarget; blitTarget = nullptr; } return true; } SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc, int xDest, int yDest, int w, int h) { SplashColorPtr p, sp; unsigned char *q; int x, y, mask, srcMask, width = w, height = h; if (src->mode != bitmap->mode) { return splashErrModeMismatch; } if (unlikely(!bitmap->data)) { return splashErrZeroImage; } if (src->getWidth() - xSrc < width) { width = src->getWidth() - xSrc; } if (src->getHeight() - ySrc < height) { height = src->getHeight() - ySrc; } if (bitmap->getWidth() - xDest < width) { width = bitmap->getWidth() - xDest; } if (bitmap->getHeight() - yDest < height) { height = bitmap->getHeight() - yDest; } if (width < 0) { width = 0; } if (height < 0) { height = 0; } switch (bitmap->mode) { case splashModeMono1: for (y = 0; y < height; ++y) { p = &bitmap->data[(yDest + y) * bitmap->rowSize + (xDest >> 3)]; mask = 0x80 >> (xDest & 7); sp = &src->data[(ySrc + y) * src->rowSize + (xSrc >> 3)]; srcMask = 0x80 >> (xSrc & 7); for (x = 0; x < width; ++x) { if (*sp & srcMask) { *p |= mask; } else { *p &= ~mask; } if (!(mask >>= 1)) { mask = 0x80; ++p; } if (!(srcMask >>= 1)) { srcMask = 0x80; ++sp; } } } break; case splashModeMono8: for (y = 0; y < height; ++y) { p = &bitmap->data[(yDest + y) * bitmap->rowSize + xDest]; sp = &src->data[(ySrc + y) * bitmap->rowSize + xSrc]; for (x = 0; x < width; ++x) { *p++ = *sp++; } } break; case splashModeRGB8: case splashModeBGR8: for (y = 0; y < height; ++y) { p = &bitmap->data[(yDest + y) * bitmap->rowSize + 3 * xDest]; sp = &src->data[(ySrc + y) * src->rowSize + 3 * xSrc]; for (x = 0; x < width; ++x) { *p++ = *sp++; *p++ = *sp++; *p++ = *sp++; } } break; case splashModeXBGR8: for (y = 0; y < height; ++y) { p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest]; sp = &src->data[(ySrc + y) * src->rowSize + 4 * xSrc]; for (x = 0; x < width; ++x) { *p++ = *sp++; *p++ = *sp++; *p++ = *sp++; *p++ = 255; sp++; } } break; case splashModeCMYK8: for (y = 0; y < height; ++y) { p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest]; sp = &src->data[(ySrc + y) * src->rowSize + 4 * xSrc]; for (x = 0; x < width; ++x) { *p++ = *sp++; *p++ = *sp++; *p++ = *sp++; *p++ = *sp++; } } break; case splashModeDeviceN8: for (y = 0; y < height; ++y) { p = &bitmap->data[(yDest + y) * bitmap->rowSize + (SPOT_NCOMPS + 4) * xDest]; sp = &src->data[(ySrc + y) * src->rowSize + (SPOT_NCOMPS + 4) * xSrc]; for (x = 0; x < width; ++x) { for (int cp = 0; cp < SPOT_NCOMPS + 4; cp++) { *p++ = *sp++; } } } break; } if (bitmap->alpha) { for (y = 0; y < height; ++y) { q = &bitmap->alpha[(yDest + y) * bitmap->width + xDest]; memset(q, 0x00, width); } } return splashOk; } SplashPath *Splash::makeStrokePath(SplashPath *path, SplashCoord w, bool flatten) { SplashPath *pathIn, *dashPath, *pathOut; SplashCoord d, dx, dy, wdx, wdy, dxNext, dyNext, wdxNext, wdyNext; SplashCoord crossprod, dotprod, miter, m; bool first, last, closed, hasangle; int subpathStart0, subpathStart1, seg, i0, i1, j0, j1, k0, k1; int left0, left1, left2, right0, right1, right2, join0, join1, join2; int leftFirst, rightFirst, firstPt; pathOut = new SplashPath(); if (path->length == 0) { return pathOut; } if (flatten) { pathIn = flattenPath(path, state->matrix, state->flatness); if (!state->lineDash.empty()) { dashPath = makeDashedPath(pathIn); delete pathIn; pathIn = dashPath; if (pathIn->length == 0) { delete pathIn; return pathOut; } } } else { pathIn = path; } subpathStart0 = subpathStart1 = 0; // make gcc happy seg = 0; // make gcc happy closed = false; // make gcc happy left0 = left1 = right0 = right1 = join0 = join1 = 0; // make gcc happy leftFirst = rightFirst = firstPt = 0; // make gcc happy i0 = 0; for (i1 = i0; !(pathIn->flags[i1] & splashPathLast) && i1 + 1 < pathIn->length && pathIn->pts[i1 + 1].x == pathIn->pts[i1].x && pathIn->pts[i1 + 1].y == pathIn->pts[i1].y; ++i1) { ; } while (i1 < pathIn->length) { if ((first = pathIn->flags[i0] & splashPathFirst)) { subpathStart0 = i0; subpathStart1 = i1; seg = 0; closed = pathIn->flags[i0] & splashPathClosed; } j0 = i1 + 1; if (j0 < pathIn->length) { for (j1 = j0; !(pathIn->flags[j1] & splashPathLast) && j1 + 1 < pathIn->length && pathIn->pts[j1 + 1].x == pathIn->pts[j1].x && pathIn->pts[j1 + 1].y == pathIn->pts[j1].y; ++j1) { ; } } else { j1 = j0; } if (pathIn->flags[i1] & splashPathLast) { if (first && state->lineCap == splashLineCapRound) { // special case: zero-length subpath with round line caps --> // draw a circle pathOut->moveTo(pathIn->pts[i0].x + (SplashCoord)0.5 * w, pathIn->pts[i0].y); pathOut->curveTo(pathIn->pts[i0].x + (SplashCoord)0.5 * w, pathIn->pts[i0].y + bezierCircle2 * w, pathIn->pts[i0].x + bezierCircle2 * w, pathIn->pts[i0].y + (SplashCoord)0.5 * w, pathIn->pts[i0].x, pathIn->pts[i0].y + (SplashCoord)0.5 * w); pathOut->curveTo(pathIn->pts[i0].x - bezierCircle2 * w, pathIn->pts[i0].y + (SplashCoord)0.5 * w, pathIn->pts[i0].x - (SplashCoord)0.5 * w, pathIn->pts[i0].y + bezierCircle2 * w, pathIn->pts[i0].x - (SplashCoord)0.5 * w, pathIn->pts[i0].y); pathOut->curveTo(pathIn->pts[i0].x - (SplashCoord)0.5 * w, pathIn->pts[i0].y - bezierCircle2 * w, pathIn->pts[i0].x - bezierCircle2 * w, pathIn->pts[i0].y - (SplashCoord)0.5 * w, pathIn->pts[i0].x, pathIn->pts[i0].y - (SplashCoord)0.5 * w); pathOut->curveTo(pathIn->pts[i0].x + bezierCircle2 * w, pathIn->pts[i0].y - (SplashCoord)0.5 * w, pathIn->pts[i0].x + (SplashCoord)0.5 * w, pathIn->pts[i0].y - bezierCircle2 * w, pathIn->pts[i0].x + (SplashCoord)0.5 * w, pathIn->pts[i0].y); pathOut->close(); } i0 = j0; i1 = j1; continue; } last = pathIn->flags[j1] & splashPathLast; if (last) { k0 = subpathStart1 + 1; } else { k0 = j1 + 1; } for (k1 = k0; !(pathIn->flags[k1] & splashPathLast) && k1 + 1 < pathIn->length && pathIn->pts[k1 + 1].x == pathIn->pts[k1].x && pathIn->pts[k1 + 1].y == pathIn->pts[k1].y; ++k1) { ; } // compute the deltas for segment (i1, j0) d = (SplashCoord)1 / splashDist(pathIn->pts[i1].x, pathIn->pts[i1].y, pathIn->pts[j0].x, pathIn->pts[j0].y); dx = d * (pathIn->pts[j0].x - pathIn->pts[i1].x); dy = d * (pathIn->pts[j0].y - pathIn->pts[i1].y); wdx = (SplashCoord)0.5 * w * dx; wdy = (SplashCoord)0.5 * w * dy; // draw the start cap if (pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx) != splashOk) { break; } if (i0 == subpathStart0) { firstPt = pathOut->length - 1; } if (first && !closed) { switch (state->lineCap) { case splashLineCapButt: pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx); break; case splashLineCapRound: pathOut->curveTo(pathIn->pts[i0].x - wdy - bezierCircle * wdx, pathIn->pts[i0].y + wdx - bezierCircle * wdy, pathIn->pts[i0].x - wdx - bezierCircle * wdy, pathIn->pts[i0].y - wdy + bezierCircle * wdx, pathIn->pts[i0].x - wdx, pathIn->pts[i0].y - wdy); pathOut->curveTo(pathIn->pts[i0].x - wdx + bezierCircle * wdy, pathIn->pts[i0].y - wdy - bezierCircle * wdx, pathIn->pts[i0].x + wdy - bezierCircle * wdx, pathIn->pts[i0].y - wdx - bezierCircle * wdy, pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx); break; case splashLineCapProjecting: pathOut->lineTo(pathIn->pts[i0].x - wdx - wdy, pathIn->pts[i0].y + wdx - wdy); pathOut->lineTo(pathIn->pts[i0].x - wdx + wdy, pathIn->pts[i0].y - wdx - wdy); pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx); break; } } else { pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx); } // draw the left side of the segment rectangle left2 = pathOut->length - 1; pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx); // draw the end cap if (last && !closed) { switch (state->lineCap) { case splashLineCapButt: pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); break; case splashLineCapRound: pathOut->curveTo(pathIn->pts[j0].x + wdy + bezierCircle * wdx, pathIn->pts[j0].y - wdx + bezierCircle * wdy, pathIn->pts[j0].x + wdx + bezierCircle * wdy, pathIn->pts[j0].y + wdy - bezierCircle * wdx, pathIn->pts[j0].x + wdx, pathIn->pts[j0].y + wdy); pathOut->curveTo(pathIn->pts[j0].x + wdx - bezierCircle * wdy, pathIn->pts[j0].y + wdy + bezierCircle * wdx, pathIn->pts[j0].x - wdy + bezierCircle * wdx, pathIn->pts[j0].y + wdx + bezierCircle * wdy, pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); break; case splashLineCapProjecting: pathOut->lineTo(pathIn->pts[j0].x + wdy + wdx, pathIn->pts[j0].y - wdx + wdy); pathOut->lineTo(pathIn->pts[j0].x - wdy + wdx, pathIn->pts[j0].y + wdx + wdy); pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); break; } } else { pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); } // draw the right side of the segment rectangle // (NB: if stroke adjustment is enabled, the closepath operation MUST // add a segment because this segment is used for a hint) right2 = pathOut->length - 1; pathOut->close(state->strokeAdjust); // draw the join join2 = pathOut->length; if (!last || closed) { // compute the deltas for segment (j1, k0) d = (SplashCoord)1 / splashDist(pathIn->pts[j1].x, pathIn->pts[j1].y, pathIn->pts[k0].x, pathIn->pts[k0].y); dxNext = d * (pathIn->pts[k0].x - pathIn->pts[j1].x); dyNext = d * (pathIn->pts[k0].y - pathIn->pts[j1].y); wdxNext = (SplashCoord)0.5 * w * dxNext; wdyNext = (SplashCoord)0.5 * w * dyNext; // compute the join parameters crossprod = dx * dyNext - dy * dxNext; dotprod = -(dx * dxNext + dy * dyNext); hasangle = crossprod != 0 || dx * dxNext < 0 || dy * dyNext < 0; if (dotprod > 0.9999) { // avoid a divide-by-zero -- set miter to something arbitrary // such that sqrt(miter) will exceed miterLimit (and m is never // used in that situation) // (note: the comparison value (0.9999) has to be less than // 1-epsilon, where epsilon is the smallest value // representable in the fixed point format) miter = (state->miterLimit + 1) * (state->miterLimit + 1); m = 0; } else { miter = (SplashCoord)2 / ((SplashCoord)1 - dotprod); if (miter < 1) { // this can happen because of floating point inaccuracies miter = 1; } m = splashSqrt(miter - 1); } // hasangle == false means that the current and and the next segment // are parallel. In that case no join needs to be drawn. // round join if (hasangle && state->lineJoin == splashLineJoinRound) { // join angle < 180 if (crossprod < 0) { SplashCoord angle = atan2((double)dx, (double)-dy); SplashCoord angleNext = atan2((double)dxNext, (double)-dyNext); if (angle < angleNext) { angle += 2 * M_PI; } SplashCoord dAngle = (angle - angleNext) / M_PI; if (dAngle < 0.501) { // span angle is <= 90 degrees -> draw a single arc SplashCoord kappa = dAngle * bezierCircle * w; SplashCoord cx1 = pathIn->pts[j0].x - wdy + kappa * dx; SplashCoord cy1 = pathIn->pts[j0].y + wdx + kappa * dy; SplashCoord cx2 = pathIn->pts[j0].x - wdyNext - kappa * dxNext; SplashCoord cy2 = pathIn->pts[j0].y + wdxNext - kappa * dyNext; pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y); pathOut->lineTo(pathIn->pts[j0].x - wdyNext, pathIn->pts[j0].y + wdxNext); pathOut->curveTo(cx2, cy2, cx1, cy1, pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); } else { // span angle is > 90 degrees -> split into two arcs SplashCoord dJoin = splashDist(-wdy, wdx, -wdyNext, wdxNext); if (dJoin > 0) { SplashCoord dxJoin = (-wdyNext + wdy) / dJoin; SplashCoord dyJoin = (wdxNext - wdx) / dJoin; SplashCoord xc = pathIn->pts[j0].x + (SplashCoord)0.5 * w * cos((double)((SplashCoord)0.5 * (angle + angleNext))); SplashCoord yc = pathIn->pts[j0].y + (SplashCoord)0.5 * w * sin((double)((SplashCoord)0.5 * (angle + angleNext))); SplashCoord kappa = dAngle * bezierCircle2 * w; SplashCoord cx1 = pathIn->pts[j0].x - wdy + kappa * dx; SplashCoord cy1 = pathIn->pts[j0].y + wdx + kappa * dy; SplashCoord cx2 = xc - kappa * dxJoin; SplashCoord cy2 = yc - kappa * dyJoin; SplashCoord cx3 = xc + kappa * dxJoin; SplashCoord cy3 = yc + kappa * dyJoin; SplashCoord cx4 = pathIn->pts[j0].x - wdyNext - kappa * dxNext; SplashCoord cy4 = pathIn->pts[j0].y + wdxNext - kappa * dyNext; pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y); pathOut->lineTo(pathIn->pts[j0].x - wdyNext, pathIn->pts[j0].y + wdxNext); pathOut->curveTo(cx4, cy4, cx3, cy3, xc, yc); pathOut->curveTo(cx2, cy2, cx1, cy1, pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); } } // join angle >= 180 } else { SplashCoord angle = atan2((double)-dx, (double)dy); SplashCoord angleNext = atan2((double)-dxNext, (double)dyNext); if (angleNext < angle) { angleNext += 2 * M_PI; } SplashCoord dAngle = (angleNext - angle) / M_PI; if (dAngle < 0.501) { // span angle is <= 90 degrees -> draw a single arc SplashCoord kappa = dAngle * bezierCircle * w; SplashCoord cx1 = pathIn->pts[j0].x + wdy + kappa * dx; SplashCoord cy1 = pathIn->pts[j0].y - wdx + kappa * dy; SplashCoord cx2 = pathIn->pts[j0].x + wdyNext - kappa * dxNext; SplashCoord cy2 = pathIn->pts[j0].y - wdxNext - kappa * dyNext; pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y); pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx); pathOut->curveTo(cx1, cy1, cx2, cy2, pathIn->pts[j0].x + wdyNext, pathIn->pts[j0].y - wdxNext); } else { // span angle is > 90 degrees -> split into two arcs SplashCoord dJoin = splashDist(wdy, -wdx, wdyNext, -wdxNext); if (dJoin > 0) { SplashCoord dxJoin = (wdyNext - wdy) / dJoin; SplashCoord dyJoin = (-wdxNext + wdx) / dJoin; SplashCoord xc = pathIn->pts[j0].x + (SplashCoord)0.5 * w * cos((double)((SplashCoord)0.5 * (angle + angleNext))); SplashCoord yc = pathIn->pts[j0].y + (SplashCoord)0.5 * w * sin((double)((SplashCoord)0.5 * (angle + angleNext))); SplashCoord kappa = dAngle * bezierCircle2 * w; SplashCoord cx1 = pathIn->pts[j0].x + wdy + kappa * dx; SplashCoord cy1 = pathIn->pts[j0].y - wdx + kappa * dy; SplashCoord cx2 = xc - kappa * dxJoin; SplashCoord cy2 = yc - kappa * dyJoin; SplashCoord cx3 = xc + kappa * dxJoin; SplashCoord cy3 = yc + kappa * dyJoin; SplashCoord cx4 = pathIn->pts[j0].x + wdyNext - kappa * dxNext; SplashCoord cy4 = pathIn->pts[j0].y - wdxNext - kappa * dyNext; pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y); pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx); pathOut->curveTo(cx1, cy1, cx2, cy2, xc, yc); pathOut->curveTo(cx3, cy3, cx4, cy4, pathIn->pts[j0].x + wdyNext, pathIn->pts[j0].y - wdxNext); } } } } else if (hasangle) { pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y); // angle < 180 if (crossprod < 0) { pathOut->lineTo(pathIn->pts[j0].x - wdyNext, pathIn->pts[j0].y + wdxNext); // miter join inside limit if (state->lineJoin == splashLineJoinMiter && splashSqrt(miter) <= state->miterLimit) { pathOut->lineTo(pathIn->pts[j0].x - wdy + wdx * m, pathIn->pts[j0].y + wdx + wdy * m); pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); // bevel join or miter join outside limit } else { pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); } // angle >= 180 } else { pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx); // miter join inside limit if (state->lineJoin == splashLineJoinMiter && splashSqrt(miter) <= state->miterLimit) { pathOut->lineTo(pathIn->pts[j0].x + wdy + wdx * m, pathIn->pts[j0].y - wdx + wdy * m); pathOut->lineTo(pathIn->pts[j0].x + wdyNext, pathIn->pts[j0].y - wdxNext); // bevel join or miter join outside limit } else { pathOut->lineTo(pathIn->pts[j0].x + wdyNext, pathIn->pts[j0].y - wdxNext); } } } pathOut->close(); } // add stroke adjustment hints if (state->strokeAdjust) { if (seg == 0 && !closed) { if (state->lineCap == splashLineCapButt) { pathOut->addStrokeAdjustHint(firstPt, left2 + 1, firstPt, firstPt + 1); if (last) { pathOut->addStrokeAdjustHint(firstPt, left2 + 1, left2 + 1, left2 + 2); } } else if (state->lineCap == splashLineCapProjecting) { if (last) { pathOut->addStrokeAdjustHint(firstPt + 1, left2 + 2, firstPt + 1, firstPt + 2); pathOut->addStrokeAdjustHint(firstPt + 1, left2 + 2, left2 + 2, left2 + 3); } else { pathOut->addStrokeAdjustHint(firstPt + 1, left2 + 1, firstPt + 1, firstPt + 2); } } } if (seg >= 1) { if (seg >= 2) { pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0); pathOut->addStrokeAdjustHint(left1, right1, join0, left2); } else { pathOut->addStrokeAdjustHint(left1, right1, firstPt, left2); } pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1); } left0 = left1; left1 = left2; right0 = right1; right1 = right2; join0 = join1; join1 = join2; if (seg == 0) { leftFirst = left2; rightFirst = right2; } if (last) { if (seg >= 2) { pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0); pathOut->addStrokeAdjustHint(left1, right1, join0, pathOut->length - 1); } else { pathOut->addStrokeAdjustHint(left1, right1, firstPt, pathOut->length - 1); } if (closed) { pathOut->addStrokeAdjustHint(left1, right1, firstPt, leftFirst); pathOut->addStrokeAdjustHint(left1, right1, rightFirst + 1, rightFirst + 1); pathOut->addStrokeAdjustHint(leftFirst, rightFirst, left1 + 1, right1); pathOut->addStrokeAdjustHint(leftFirst, rightFirst, join1, pathOut->length - 1); } if (!closed && seg > 0) { if (state->lineCap == splashLineCapButt) { pathOut->addStrokeAdjustHint(left1 - 1, left1 + 1, left1 + 1, left1 + 2); } else if (state->lineCap == splashLineCapProjecting) { pathOut->addStrokeAdjustHint(left1 - 1, left1 + 2, left1 + 2, left1 + 3); } } } } i0 = j0; i1 = j1; ++seg; } if (pathIn != path) { delete pathIn; } return pathOut; } void Splash::dumpPath(SplashPath *path) { int i; for (i = 0; i < path->length; ++i) { printf(" %3d: x=%8.2f y=%8.2f%s%s%s%s\n", i, (double)path->pts[i].x, (double)path->pts[i].y, (path->flags[i] & splashPathFirst) ? " first" : "", (path->flags[i] & splashPathLast) ? " last" : "", (path->flags[i] & splashPathClosed) ? " closed" : "", (path->flags[i] & splashPathCurve) ? " curve" : ""); } } void Splash::dumpXPath(SplashXPath *path) { int i; for (i = 0; i < path->length; ++i) { printf(" %4d: x0=%8.2f y0=%8.2f x1=%8.2f y1=%8.2f %s%s%s\n", i, (double)path->segs[i].x0, (double)path->segs[i].y0, (double)path->segs[i].x1, (double)path->segs[i].y1, (path->segs[i].flags & splashXPathHoriz) ? "H" : " ", (path->segs[i].flags & splashXPathVert) ? "V" : " ", (path->segs[i].flags & splashXPathFlip) ? "P" : " "); } } SplashError Splash::shadedFill(SplashPath *path, bool hasBBox, SplashPattern *pattern, bool clipToStrokePath) { SplashPipe pipe; int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y; SplashClipResult clipRes; if (vectorAntialias && aaBuf == nullptr) { // should not happen, but to be secure return splashErrGeneric; } if (path->length == 0) { return splashErrEmptyPath; } SplashXPath xPath(path, state->matrix, state->flatness, true); if (vectorAntialias) { xPath.aaScale(); } xPath.sort(); yMinI = state->clip->getYMinI(); yMaxI = state->clip->getYMaxI(); if (vectorAntialias && !inShading) { yMinI = yMinI * splashAASize; yMaxI = (yMaxI + 1) * splashAASize - 1; } SplashXPathScanner scanner(xPath, false, yMinI, yMaxI); // get the min and max x and y values if (vectorAntialias) { scanner.getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI); } else { scanner.getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI); } // check clipping if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI)) != splashClipAllOutside) { // limit the y range if (yMinI < state->clip->getYMinI()) { yMinI = state->clip->getYMinI(); } if (yMaxI > state->clip->getYMaxI()) { yMaxI = state->clip->getYMaxI(); } unsigned char alpha = splashRound((clipToStrokePath) ? state->strokeAlpha * 255 : state->fillAlpha * 255); pipeInit(&pipe, 0, yMinI, pattern, nullptr, alpha, vectorAntialias && !hasBBox, false); // draw the spans if (vectorAntialias) { for (y = yMinI; y <= yMaxI; ++y) { scanner.renderAALine(aaBuf, &x0, &x1, y); if (clipRes != splashClipAllInside) { state->clip->clipAALine(aaBuf, &x0, &x1, y); } #if splashAASize == 4 if (!hasBBox && y > yMinI && y < yMaxI) { // correct shape on left side if clip is // vertical through the middle of shading: unsigned char *p0, *p1, *p2, *p3; unsigned char c1, c2, c3, c4; p0 = aaBuf->getDataPtr() + (x0 >> 1); p1 = p0 + aaBuf->getRowSize(); p2 = p1 + aaBuf->getRowSize(); p3 = p2 + aaBuf->getRowSize(); if (x0 & 1) { c1 = (*p0 & 0x0f); c2 = (*p1 & 0x0f); c3 = (*p2 & 0x0f); c4 = (*p3 & 0x0f); } else { c1 = (*p0 >> 4); c2 = (*p1 >> 4); c3 = (*p2 >> 4); c4 = (*p3 >> 4); } if ((c1 & 0x03) == 0x03 && (c2 & 0x03) == 0x03 && (c3 & 0x03) == 0x03 && (c4 & 0x03) == 0x03 && c1 == c2 && c2 == c3 && c3 == c4 && pattern->testPosition(x0 - 1, y)) { unsigned char shapeCorrection = (x0 & 1) ? 0x0f : 0xf0; *p0 |= shapeCorrection; *p1 |= shapeCorrection; *p2 |= shapeCorrection; *p3 |= shapeCorrection; } // correct shape on right side if clip is // through the middle of shading: p0 = aaBuf->getDataPtr() + (x1 >> 1); p1 = p0 + aaBuf->getRowSize(); p2 = p1 + aaBuf->getRowSize(); p3 = p2 + aaBuf->getRowSize(); if (x1 & 1) { c1 = (*p0 & 0x0f); c2 = (*p1 & 0x0f); c3 = (*p2 & 0x0f); c4 = (*p3 & 0x0f); } else { c1 = (*p0 >> 4); c2 = (*p1 >> 4); c3 = (*p2 >> 4); c4 = (*p3 >> 4); } if ((c1 & 0xc) == 0x0c && (c2 & 0x0c) == 0x0c && (c3 & 0x0c) == 0x0c && (c4 & 0x0c) == 0x0c && c1 == c2 && c2 == c3 && c3 == c4 && pattern->testPosition(x1 + 1, y)) { unsigned char shapeCorrection = (x1 & 1) ? 0x0f : 0xf0; *p0 |= shapeCorrection; *p1 |= shapeCorrection; *p2 |= shapeCorrection; *p3 |= shapeCorrection; } } #endif drawAALine(&pipe, x0, x1, y); } } else { SplashClipResult clipRes2; for (y = yMinI; y <= yMaxI; ++y) { SplashXPathScanIterator iterator(scanner, y); while (iterator.getNextSpan(&x0, &x1)) { if (clipRes == splashClipAllInside) { drawSpan(&pipe, x0, x1, y, true); } else { // limit the x range if (x0 < state->clip->getXMinI()) { x0 = state->clip->getXMinI(); } if (x1 > state->clip->getXMaxI()) { x1 = state->clip->getXMaxI(); } clipRes2 = state->clip->testSpan(x0, x1, y); drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside); } } } } } opClipRes = clipRes; return splashOk; } poppler-24.02.0/splash/Splash.h000066400000000000000000000377231455701731300162570ustar00rootroot00000000000000//======================================================================== // // Splash.h // //======================================================================== //======================================================================== // // 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) 2005 Marco Pesenti Gritti // Copyright (C) 2007, 2011, 2018, 2019, 2021, 2022 Albert Astals Cid // Copyright (C) 2010-2013, 2015 Thomas Freitag // Copyright (C) 2010 Christian Feuersänger // Copyright (C) 2012, 2017 Adrian Johnson // Copyright (C) 2020 Oliver Sander // Copyright (C) 2020 Tobias Deiminger // // 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 SPLASH_H #define SPLASH_H #include #include "SplashTypes.h" #include "SplashClip.h" #include "SplashPattern.h" #include "poppler_private_export.h" class SplashBitmap; struct SplashGlyphBitmap; class SplashState; class SplashScreen; class SplashPath; class SplashXPath; class SplashFont; struct SplashPipe; //------------------------------------------------------------------------ // Retrieves the next line of pixels in an image mask. Normally, // fills in * and returns true. If the image stream is // exhausted, returns false. typedef bool (*SplashImageMaskSource)(void *data, SplashColorPtr pixel); // Retrieves the next line of pixels in an image. Normally, fills in // * and returns true. If the image stream is exhausted, // returns false. typedef bool (*SplashImageSource)(void *data, SplashColorPtr colorLine, unsigned char *alphaLine); // Use ICCColorSpace to transform a bitmap typedef void (*SplashICCTransform)(void *data, SplashBitmap *bitmap); //------------------------------------------------------------------------ enum SplashPipeResultColorCtrl { splashPipeResultColorNoAlphaBlendCMYK, splashPipeResultColorNoAlphaBlendDeviceN, splashPipeResultColorNoAlphaBlendRGB, splashPipeResultColorNoAlphaBlendMono, splashPipeResultColorAlphaNoBlendMono, splashPipeResultColorAlphaNoBlendRGB, splashPipeResultColorAlphaNoBlendCMYK, splashPipeResultColorAlphaNoBlendDeviceN, splashPipeResultColorAlphaBlendMono, splashPipeResultColorAlphaBlendRGB, splashPipeResultColorAlphaBlendCMYK, splashPipeResultColorAlphaBlendDeviceN }; //------------------------------------------------------------------------ // Splash //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT Splash { public: // Create a new rasterizer object. Splash(SplashBitmap *bitmapA, bool vectorAntialiasA, SplashScreenParams *screenParams = nullptr); Splash(SplashBitmap *bitmapA, bool vectorAntialiasA, SplashScreen *screenA); ~Splash(); Splash(const Splash &) = delete; Splash &operator=(const Splash &) = delete; //----- state read SplashCoord *getMatrix(); SplashPattern *getStrokePattern(); SplashPattern *getFillPattern(); SplashScreen *getScreen(); SplashBlendFunc getBlendFunc(); SplashCoord getStrokeAlpha(); SplashCoord getFillAlpha(); SplashCoord getLineWidth(); int getLineCap(); int getLineJoin(); SplashCoord getMiterLimit(); SplashCoord getFlatness(); SplashCoord getLineDashPhase(); bool getStrokeAdjust(); SplashClip *getClip(); SplashBitmap *getSoftMask(); bool getInNonIsolatedGroup(); //----- state write void setMatrix(SplashCoord *matrix); void setStrokePattern(SplashPattern *strokePattern); void setFillPattern(SplashPattern *fillPattern); void setScreen(SplashScreen *screen); void setBlendFunc(SplashBlendFunc func); void setStrokeAlpha(SplashCoord alpha); void setFillAlpha(SplashCoord alpha); void setPatternAlpha(SplashCoord strokeAlpha, SplashCoord fillAlpha); void clearPatternAlpha(); void setFillOverprint(bool fop); void setStrokeOverprint(bool sop); void setOverprintMode(int opm); void setLineWidth(SplashCoord lineWidth); void setLineCap(int lineCap); void setLineJoin(int lineJoin); void setMiterLimit(SplashCoord miterLimit); void setFlatness(SplashCoord flatness); // the array will be copied void setLineDash(std::vector &&lineDash, SplashCoord lineDashPhase); void setStrokeAdjust(bool strokeAdjust); // NB: uses transformed coordinates. void clipResetToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1); // NB: uses transformed coordinates. SplashError clipToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1); // NB: uses untransformed coordinates. SplashError clipToPath(SplashPath *path, bool eo); void setSoftMask(SplashBitmap *softMask); void setInNonIsolatedGroup(SplashBitmap *alpha0BitmapA, int alpha0XA, int alpha0YA); void setTransfer(unsigned char *red, unsigned char *green, unsigned char *blue, unsigned char *gray); void setOverprintMask(unsigned int overprintMask, bool additive); //----- state save/restore void saveState(); SplashError restoreState(); //----- drawing operations // Fill the bitmap with . This is not subject to clipping. void clear(SplashColorPtr color, unsigned char alpha = 0x00); // Stroke a path using the current stroke pattern. SplashError stroke(SplashPath *path); // Fill a path using the current fill pattern. SplashError fill(SplashPath *path, bool eo); // Fill a path, XORing with the current fill pattern. SplashError xorFill(SplashPath *path, bool eo); // Draw a character, using the current fill pattern. SplashError fillChar(SplashCoord x, SplashCoord y, int c, SplashFont *font); // Draw a glyph, using the current fill pattern. This function does // not free any data, i.e., it ignores glyph->freeData. void fillGlyph(SplashCoord x, SplashCoord y, SplashGlyphBitmap *glyph); // Draws an image mask using the fill color. This will read // lines of pixels from , starting with the top line. "1" // pixels will be drawn with the current fill color; "0" pixels are // transparent. The matrix: // [ mat[0] mat[1] 0 ] // [ mat[2] mat[3] 0 ] // [ mat[4] mat[5] 1 ] // maps a unit square to the desired destination for the image, in // PostScript style: // [x' y' 1] = [x y 1] * mat // Note that the Splash y axis points downward, and the image source // is assumed to produce pixels in raster order, starting from the // top line. SplashError fillImageMask(SplashImageMaskSource src, void *srcData, int w, int h, SplashCoord *mat, bool glyphMode); // Draw an image. This will read lines of pixels from // , starting with the top line. These pixels are assumed to // be in the source mode, . If is true, the // alpha values returned by are used; otherwise they are // ignored. The following combinations of source and target modes // are supported: // source target // ------ ------ // Mono1 Mono1 // Mono8 Mono1 -- with dithering // Mono8 Mono8 // RGB8 RGB8 // BGR8 BGR8 // CMYK8 CMYK8 // The matrix behaves as for fillImageMask. SplashError drawImage(SplashImageSource src, SplashICCTransform tf, void *srcData, SplashColorMode srcMode, bool srcAlpha, int w, int h, SplashCoord *mat, bool interpolate, bool tilingPattern = false); // Composite a rectangular region from onto this Splash // object. SplashError composite(SplashBitmap *src, int xSrc, int ySrc, int xDest, int yDest, int w, int h, bool noClip, bool nonIsolated, bool knockout = false, SplashCoord knockoutOpacity = 1.0); // Composite this Splash object onto a background color. The // background alpha is assumed to be 1. void compositeBackground(SplashColorConstPtr color); // Copy a rectangular region from onto the bitmap belonging to // this Splash object. The destination alpha values are all set to // zero. SplashError blitTransparent(SplashBitmap *src, int xSrc, int ySrc, int xDest, int yDest, int w, int h); void blitImage(SplashBitmap *src, bool srcAlpha, int xDest, int yDest); //----- misc // Construct a path for a stroke, given the path to be stroked and // the line width . All other stroke parameters are taken from // the current state. If is true, this function will // first flatten the path and handle the linedash. SplashPath *makeStrokePath(SplashPath *path, SplashCoord w, bool flatten = true); // Return the associated bitmap. SplashBitmap *getBitmap() { return bitmap; } // Set the minimum line width. void setMinLineWidth(SplashCoord w) { minLineWidth = w; } // Setter/Getter for thin line mode void setThinLineMode(SplashThinLineMode thinLineModeA) { thinLineMode = thinLineModeA; } SplashThinLineMode getThinLineMode() { return thinLineMode; } // Get clipping status for the last drawing operation subject to // clipping. SplashClipResult getClipRes() { return opClipRes; } // Toggle debug mode on or off. 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; } #endif // Do shaded fills with dynamic patterns // // clipToStrokePath: Whether the current clip region is a stroke path. // In that case, strokeAlpha is used rather than fillAlpha. SplashError shadedFill(SplashPath *path, bool hasBBox, SplashPattern *pattern, bool clipToStrokePath); // Draw a gouraud triangle shading. bool gouraudTriangleShadedFill(SplashGouraudColor *shading); private: void pipeInit(SplashPipe *pipe, int x, int y, SplashPattern *pattern, SplashColorPtr cSrc, unsigned char aInput, bool usesShape, bool nonIsolatedGroup, bool knockout = false, unsigned char knockoutOpacity = 255); void pipeRun(SplashPipe *pipe); void pipeRunSimpleMono1(SplashPipe *pipe); void pipeRunSimpleMono8(SplashPipe *pipe); void pipeRunSimpleRGB8(SplashPipe *pipe); void pipeRunSimpleXBGR8(SplashPipe *pipe); void pipeRunSimpleBGR8(SplashPipe *pipe); void pipeRunSimpleCMYK8(SplashPipe *pipe); void pipeRunSimpleDeviceN8(SplashPipe *pipe); void pipeRunAAMono1(SplashPipe *pipe); void pipeRunAAMono8(SplashPipe *pipe); void pipeRunAARGB8(SplashPipe *pipe); void pipeRunAAXBGR8(SplashPipe *pipe); void pipeRunAABGR8(SplashPipe *pipe); void pipeRunAACMYK8(SplashPipe *pipe); void pipeRunAADeviceN8(SplashPipe *pipe); void pipeSetXY(SplashPipe *pipe, int x, int y); void pipeIncX(SplashPipe *pipe); void drawPixel(SplashPipe *pipe, int x, int y, bool noClip); void drawAAPixelInit(); void drawAAPixel(SplashPipe *pipe, int x, int y); void drawSpan(SplashPipe *pipe, int x0, int x1, int y, bool noClip); void drawAALine(SplashPipe *pipe, int x0, int x1, int y, bool adjustLine = false, unsigned char lineOpacity = 0); void transform(const SplashCoord *matrix, SplashCoord xi, SplashCoord yi, SplashCoord *xo, SplashCoord *yo); void strokeNarrow(SplashPath *path); void strokeWide(SplashPath *path, SplashCoord w); SplashPath *flattenPath(SplashPath *path, SplashCoord *matrix, SplashCoord flatness); void flattenCurve(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1, SplashCoord x2, SplashCoord y2, SplashCoord x3, SplashCoord y3, SplashCoord *matrix, SplashCoord flatness2, SplashPath *fPath); SplashPath *makeDashedPath(SplashPath *xPath); void getBBoxFP(SplashPath *path, SplashCoord *xMinA, SplashCoord *yMinA, SplashCoord *xMaxA, SplashCoord *yMaxA); SplashError fillWithPattern(SplashPath *path, bool eo, SplashPattern *pattern, SplashCoord alpha); bool pathAllOutside(SplashPath *path); void fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph, bool noclip); void arbitraryTransformMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, SplashCoord *mat, bool glyphMode); SplashBitmap *scaleMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight); void scaleMaskYdownXdown(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void scaleMaskYdownXup(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void scaleMaskYupXdown(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void scaleMaskYupXup(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void blitMask(SplashBitmap *src, int xDest, int yDest, SplashClipResult clipRes); SplashError arbitraryTransformImage(SplashImageSource src, SplashICCTransform tf, void *srcData, SplashColorMode srcMode, int nComps, bool srcAlpha, int srcWidth, int srcHeight, SplashCoord *mat, bool interpolate, bool tilingPattern = false); SplashBitmap *scaleImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, bool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, bool interpolate, bool tilingPattern = false); bool scaleImageYdownXdown(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, bool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); bool scaleImageYdownXup(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, bool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); bool scaleImageYupXdown(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, bool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); bool scaleImageYupXup(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, bool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); bool scaleImageYupXupBilinear(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, bool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void vertFlipImage(SplashBitmap *img, int width, int height, int nComps); void blitImage(SplashBitmap *src, bool srcAlpha, int xDest, int yDest, SplashClipResult clipRes); void blitImageClipped(SplashBitmap *src, bool srcAlpha, int xSrc, int ySrc, int xDest, int yDest, int w, int h); void dumpPath(SplashPath *path); void dumpXPath(SplashXPath *path); static SplashPipeResultColorCtrl pipeResultColorNoAlphaBlend[]; static SplashPipeResultColorCtrl pipeResultColorAlphaNoBlend[]; static SplashPipeResultColorCtrl pipeResultColorAlphaBlend[]; static int pipeNonIsoGroupCorrection[]; SplashBitmap *bitmap; SplashState *state; SplashBitmap *aaBuf; int aaBufY; SplashBitmap *alpha0Bitmap; // for non-isolated groups, this is the // bitmap containing the alpha0 values int alpha0X, alpha0Y; // offset within alpha0Bitmap SplashCoord aaGamma[splashAASize * splashAASize + 1]; SplashCoord minLineWidth; SplashThinLineMode thinLineMode; SplashClipResult opClipRes; bool vectorAntialias; bool inShading; bool debugMode; }; #endif poppler-24.02.0/splash/SplashBitmap.cc000066400000000000000000000626661455701731300175560ustar00rootroot00000000000000//======================================================================== // // SplashBitmap.cc // //======================================================================== //======================================================================== // // 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, 2009, 2010, 2012, 2015, 2018, 2019, 2021, 2022 Albert Astals Cid // Copyright (C) 2007 Ilmari Heikkinen // Copyright (C) 2009 Shen Liang // Copyright (C) 2009 Stefan Thomas // Copyright (C) 2010, 2012, 2017 Adrian Johnson // Copyright (C) 2010 Harry Roberts // Copyright (C) 2010 Christian Feuersänger // Copyright (C) 2010, 2015, 2019 William Bader // Copyright (C) 2011-2013 Thomas Freitag // Copyright (C) 2012 Anthony Wesley // Copyright (C) 2015, 2018 Adam Reichold // Copyright (C) 2016 Kenji Uno // Copyright (C) 2018 Martin Packman // Copyright (C) 2019 Christian Persch // Copyright (C) 2019 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 // //======================================================================== #include #include #include #include #include #include "goo/gfile.h" #include "goo/gmem.h" #include "SplashErrorCodes.h" #include "SplashBitmap.h" #include "poppler/Error.h" #include "poppler/GfxState.h" #include "goo/JpegWriter.h" #include "goo/PNGWriter.h" #include "goo/TiffWriter.h" #include "goo/ImgWriter.h" //------------------------------------------------------------------------ // SplashBitmap //------------------------------------------------------------------------ SplashBitmap::SplashBitmap(int widthA, int heightA, int rowPadA, SplashColorMode modeA, bool alphaA, bool topDown, const std::vector *separationListA) { width = widthA; height = heightA; mode = modeA; rowPad = rowPadA; switch (mode) { case splashModeMono1: if (width > 0) { rowSize = (width + 7) >> 3; } else { rowSize = -1; } break; case splashModeMono8: if (width > 0) { rowSize = width; } else { rowSize = -1; } break; case splashModeRGB8: case splashModeBGR8: if (width > 0 && width <= INT_MAX / 3) { rowSize = width * 3; } else { rowSize = -1; } break; case splashModeXBGR8: if (width > 0 && width <= INT_MAX / 4) { rowSize = width * 4; } else { rowSize = -1; } break; case splashModeCMYK8: if (width > 0 && width <= INT_MAX / 4) { rowSize = width * 4; } else { rowSize = -1; } break; case splashModeDeviceN8: if (width > 0 && width <= static_cast(INT_MAX / splashMaxColorComps)) { rowSize = width * splashMaxColorComps; } else { rowSize = -1; } break; } if (rowSize > 0) { rowSize += rowPad - 1; rowSize -= rowSize % rowPad; } data = (SplashColorPtr)gmallocn_checkoverflow(rowSize, height); if (data != nullptr) { if (!topDown) { data += (height - 1) * rowSize; rowSize = -rowSize; } if (alphaA) { alpha = (unsigned char *)gmallocn_checkoverflow(width, height); } else { alpha = nullptr; } } else { alpha = nullptr; } separationList = new std::vector(); if (separationListA != nullptr) { for (const GfxSeparationColorSpace *separation : *separationListA) { separationList->push_back((GfxSeparationColorSpace *)separation->copy()); } } } SplashBitmap *SplashBitmap::copy(const SplashBitmap *src) { SplashBitmap *result = new SplashBitmap(src->getWidth(), src->getHeight(), src->getRowPad(), src->getMode(), src->getAlphaPtr() != nullptr, src->getRowSize() >= 0, src->getSeparationList()); SplashColorConstPtr dataSource = src->getDataPtr(); unsigned char *dataDest = result->getDataPtr(); int amount = src->getRowSize(); if (amount < 0) { dataSource = dataSource + (src->getHeight() - 1) * amount; dataDest = dataDest + (src->getHeight() - 1) * amount; amount *= -src->getHeight(); } else { amount *= src->getHeight(); } memcpy(dataDest, dataSource, amount); if (src->getAlphaPtr() != nullptr) { memcpy(result->getAlphaPtr(), src->getAlphaPtr(), src->getWidth() * src->getHeight()); } return result; } SplashBitmap::~SplashBitmap() { if (data) { if (rowSize < 0) { gfree(data + (height - 1) * rowSize); } else { gfree(data); } } gfree(alpha); for (auto entry : *separationList) { delete entry; } delete separationList; } SplashError SplashBitmap::writePNMFile(char *fileName) { FILE *f; SplashError e; if (!(f = openFile(fileName, "wb"))) { return splashErrOpenFile; } e = this->writePNMFile(f); fclose(f); return e; } SplashError SplashBitmap::writePNMFile(FILE *f) { SplashColorPtr row, p; int x, y; switch (mode) { case splashModeMono1: fprintf(f, "P4\n%d %d\n", width, height); row = data; for (y = 0; y < height; ++y) { p = row; for (x = 0; x < width; x += 8) { fputc(*p ^ 0xff, f); ++p; } row += rowSize; } break; case splashModeMono8: fprintf(f, "P5\n%d %d\n255\n", width, height); row = data; for (y = 0; y < height; ++y) { fwrite(row, 1, width, f); row += rowSize; } break; case splashModeRGB8: fprintf(f, "P6\n%d %d\n255\n", width, height); row = data; for (y = 0; y < height; ++y) { fwrite(row, 1, 3 * width, f); row += rowSize; } break; case splashModeXBGR8: fprintf(f, "P6\n%d %d\n255\n", width, height); row = data; for (y = 0; y < height; ++y) { p = row; for (x = 0; x < width; ++x) { fputc(splashBGR8R(p), f); fputc(splashBGR8G(p), f); fputc(splashBGR8B(p), f); p += 4; } row += rowSize; } break; case splashModeBGR8: fprintf(f, "P6\n%d %d\n255\n", width, height); row = data; for (y = 0; y < height; ++y) { p = row; for (x = 0; x < width; ++x) { fputc(splashBGR8R(p), f); fputc(splashBGR8G(p), f); fputc(splashBGR8B(p), f); p += 3; } row += rowSize; } break; case splashModeCMYK8: case splashModeDeviceN8: // PNM doesn't support CMYK error(errInternal, -1, "unsupported SplashBitmap mode"); return splashErrGeneric; break; } return splashOk; } SplashError SplashBitmap::writeAlphaPGMFile(char *fileName) { FILE *f; if (!alpha) { return splashErrModeMismatch; } if (!(f = openFile(fileName, "wb"))) { return splashErrOpenFile; } fprintf(f, "P5\n%d %d\n255\n", width, height); fwrite(alpha, 1, width * height, f); fclose(f); return splashOk; } void SplashBitmap::getPixel(int x, int y, SplashColorPtr pixel) { SplashColorPtr p; if (y < 0 || y >= height || x < 0 || x >= width || !data) { return; } switch (mode) { case splashModeMono1: p = &data[y * rowSize + (x >> 3)]; pixel[0] = (p[0] & (0x80 >> (x & 7))) ? 0xff : 0x00; break; case splashModeMono8: p = &data[y * rowSize + x]; pixel[0] = p[0]; break; case splashModeRGB8: p = &data[y * rowSize + 3 * x]; pixel[0] = p[0]; pixel[1] = p[1]; pixel[2] = p[2]; break; case splashModeXBGR8: p = &data[y * rowSize + 4 * x]; pixel[0] = p[2]; pixel[1] = p[1]; pixel[2] = p[0]; pixel[3] = p[3]; break; case splashModeBGR8: p = &data[y * rowSize + 3 * x]; pixel[0] = p[2]; pixel[1] = p[1]; pixel[2] = p[0]; break; case splashModeCMYK8: p = &data[y * rowSize + 4 * x]; pixel[0] = p[0]; pixel[1] = p[1]; pixel[2] = p[2]; pixel[3] = p[3]; break; case splashModeDeviceN8: p = &data[y * rowSize + (SPOT_NCOMPS + 4) * x]; for (int cp = 0; cp < SPOT_NCOMPS + 4; cp++) { pixel[cp] = p[cp]; } break; } } unsigned char SplashBitmap::getAlpha(int x, int y) { return alpha[y * width + x]; } SplashColorPtr SplashBitmap::takeData() { SplashColorPtr data2; data2 = data; data = nullptr; return data2; } SplashError SplashBitmap::writeImgFile(SplashImageFileFormat format, const char *fileName, double hDPI, double vDPI, WriteImgParams *params) { FILE *f; SplashError e; if (!(f = openFile(fileName, "wb"))) { return splashErrOpenFile; } e = writeImgFile(format, f, hDPI, vDPI, params); fclose(f); return e; } void SplashBitmap::setJpegParams(ImgWriter *writer, WriteImgParams *params) { #ifdef ENABLE_LIBJPEG if (params) { static_cast(writer)->setProgressive(params->jpegProgressive); static_cast(writer)->setOptimize(params->jpegOptimize); if (params->jpegQuality >= 0) { static_cast(writer)->setQuality(params->jpegQuality); } } #endif } SplashError SplashBitmap::writeImgFile(SplashImageFileFormat format, FILE *f, double hDPI, double vDPI, WriteImgParams *params) { ImgWriter *writer; SplashError e; SplashColorMode imageWriterFormat = splashModeRGB8; switch (format) { #ifdef ENABLE_LIBPNG case splashFormatPng: writer = new PNGWriter(); break; #endif #ifdef ENABLE_LIBJPEG case splashFormatJpegCMYK: writer = new JpegWriter(JpegWriter::CMYK); setJpegParams(writer, params); break; case splashFormatJpeg: writer = new JpegWriter(); setJpegParams(writer, params); break; #endif #ifdef ENABLE_LIBTIFF case splashFormatTiff: switch (mode) { case splashModeMono1: writer = new TiffWriter(TiffWriter::MONOCHROME); imageWriterFormat = splashModeMono1; break; case splashModeMono8: writer = new TiffWriter(TiffWriter::GRAY); imageWriterFormat = splashModeMono8; break; case splashModeRGB8: case splashModeBGR8: writer = new TiffWriter(TiffWriter::RGB); break; case splashModeCMYK8: case splashModeDeviceN8: writer = new TiffWriter(TiffWriter::CMYK); break; default: fprintf(stderr, "TiffWriter: Mode %d not supported\n", mode); writer = new TiffWriter(); } if (writer && params) { ((TiffWriter *)writer)->setCompressionString(params->tiffCompression.c_str()); } break; #endif default: // Not the greatest error message, but users of this function should // have already checked whether their desired format is compiled in. error(errInternal, -1, "Support for this image type not compiled in"); return splashErrGeneric; } e = writeImgFile(writer, f, hDPI, vDPI, imageWriterFormat); delete writer; return e; } #include "poppler/GfxState_helpers.h" void SplashBitmap::getRGBLine(int yl, SplashColorPtr line) { SplashColor col; double c, m, y, k, c1, m1, y1, k1, r, g, b; for (int x = 0; x < width; x++) { getPixel(x, yl, col); c = byteToDbl(col[0]); m = byteToDbl(col[1]); y = byteToDbl(col[2]); k = byteToDbl(col[3]); if (separationList->size() > 0) { 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]); sepCS->getCMYK(&input, &cmyk); col[0] = colToByte(cmyk.c); col[1] = colToByte(cmyk.m); col[2] = colToByte(cmyk.y); col[3] = colToByte(cmyk.k); c += byteToDbl(col[0]); m += byteToDbl(col[1]); y += byteToDbl(col[2]); k += byteToDbl(col[3]); } } if (c > 1) { c = 1; } if (m > 1) { m = 1; } if (y > 1) { y = 1; } if (k > 1) { k = 1; } } c1 = 1 - c; m1 = 1 - m; y1 = 1 - y; k1 = 1 - k; cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b); *line++ = dblToByte(clip01(r)); *line++ = dblToByte(clip01(g)); *line++ = dblToByte(clip01(b)); } } void SplashBitmap::getXBGRLine(int yl, SplashColorPtr line, ConversionMode conversionMode) { SplashColor col; double c, m, y, k, c1, m1, y1, k1, r, g, b; for (int x = 0; x < width; x++) { getPixel(x, yl, col); c = byteToDbl(col[0]); m = byteToDbl(col[1]); y = byteToDbl(col[2]); k = byteToDbl(col[3]); if (separationList->size() > 0) { 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]); sepCS->getCMYK(&input, &cmyk); col[0] = colToByte(cmyk.c); col[1] = colToByte(cmyk.m); col[2] = colToByte(cmyk.y); col[3] = colToByte(cmyk.k); c += byteToDbl(col[0]); m += byteToDbl(col[1]); y += byteToDbl(col[2]); k += byteToDbl(col[3]); } } if (c > 1) { c = 1; } if (m > 1) { m = 1; } if (y > 1) { y = 1; } if (k > 1) { k = 1; } } c1 = 1 - c; m1 = 1 - m; y1 = 1 - y; k1 = 1 - k; cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b); if (conversionMode == conversionAlphaPremultiplied) { const double a = getAlpha(x, yl) / 255.0; *line++ = dblToByte(clip01(b * a)); *line++ = dblToByte(clip01(g * a)); *line++ = dblToByte(clip01(r * a)); } else { *line++ = dblToByte(clip01(b)); *line++ = dblToByte(clip01(g)); *line++ = dblToByte(clip01(r)); } if (conversionMode != conversionOpaque) { *line++ = getAlpha(x, yl); } else { *line++ = 255; } } } static inline unsigned char div255(int x) { return (unsigned char)((x + (x >> 8) + 0x80) >> 8); } bool SplashBitmap::convertToXBGR(ConversionMode conversionMode) { if (mode == splashModeXBGR8) { if (conversionMode != conversionOpaque) { // Copy the alpha channel into the fourth component so that XBGR becomes ABGR. const SplashColorPtr dbegin = data; const SplashColorPtr dend = data + rowSize * height; unsigned char *const abegin = alpha; unsigned char *const aend = alpha + width * height; SplashColorPtr d = dbegin; unsigned char *a = abegin; if (conversionMode == conversionAlphaPremultiplied) { for (; d < dend && a < aend; d += 4, a += 1) { d[0] = div255(d[0] * *a); d[1] = div255(d[1] * *a); d[2] = div255(d[2] * *a); d[3] = *a; } } else { for (d += 3; d < dend && a < aend; d += 4, a += 1) { *d = *a; } } } return true; } int newrowSize = width * 4; SplashColorPtr newdata = (SplashColorPtr)gmallocn_checkoverflow(newrowSize, height); if (newdata != nullptr) { for (int y = 0; y < height; y++) { unsigned char *row = newdata + y * newrowSize; getXBGRLine(y, row, conversionMode); } if (rowSize < 0) { gfree(data + (height - 1) * rowSize); } else { gfree(data); } data = newdata; rowSize = newrowSize; mode = splashModeXBGR8; } return newdata != nullptr; } void SplashBitmap::getCMYKLine(int yl, SplashColorPtr line) { SplashColor col; for (int x = 0; x < width; x++) { getPixel(x, yl, col); if (separationList->size() > 0) { double c, m, y, k; c = byteToDbl(col[0]); m = byteToDbl(col[1]); y = byteToDbl(col[2]); k = byteToDbl(col[3]); 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]); sepCS->getCMYK(&input, &cmyk); col[0] = colToByte(cmyk.c); col[1] = colToByte(cmyk.m); col[2] = colToByte(cmyk.y); col[3] = colToByte(cmyk.k); c += byteToDbl(col[0]); m += byteToDbl(col[1]); y += byteToDbl(col[2]); k += byteToDbl(col[3]); } } col[0] = dblToByte(clip01(c)); col[1] = dblToByte(clip01(m)); col[2] = dblToByte(clip01(y)); col[3] = dblToByte(clip01(k)); } *line++ = col[0]; *line++ = col[1]; *line++ = col[2]; *line++ = col[3]; } } SplashError SplashBitmap::writeImgFile(ImgWriter *writer, FILE *f, double hDPI, double vDPI, SplashColorMode imageWriterFormat) { if (mode != splashModeRGB8 && mode != splashModeMono8 && mode != splashModeMono1 && mode != splashModeXBGR8 && mode != splashModeBGR8 && mode != splashModeCMYK8 && mode != splashModeDeviceN8) { error(errInternal, -1, "unsupported SplashBitmap mode"); return splashErrGeneric; } if (!writer->init(f, width, height, hDPI, vDPI)) { return splashErrGeneric; } switch (mode) { case splashModeCMYK8: if (writer->supportCMYK()) { SplashColorPtr row; unsigned char **row_pointers = new unsigned char *[height]; row = data; for (int y = 0; y < height; ++y) { row_pointers[y] = row; row += rowSize; } if (!writer->writePointers(row_pointers, height)) { delete[] row_pointers; return splashErrGeneric; } delete[] row_pointers; } else { unsigned char *row = new unsigned char[3 * width]; for (int y = 0; y < height; y++) { getRGBLine(y, row); if (!writer->writeRow(&row)) { delete[] row; return splashErrGeneric; } } delete[] row; } break; case splashModeDeviceN8: if (writer->supportCMYK()) { unsigned char *row = new unsigned char[4 * width]; for (int y = 0; y < height; y++) { getCMYKLine(y, row); if (!writer->writeRow(&row)) { delete[] row; return splashErrGeneric; } } delete[] row; } else { unsigned char *row = new unsigned char[3 * width]; for (int y = 0; y < height; y++) { getRGBLine(y, row); if (!writer->writeRow(&row)) { delete[] row; return splashErrGeneric; } } delete[] row; } break; case splashModeRGB8: { SplashColorPtr row; unsigned char **row_pointers = new unsigned char *[height]; row = data; for (int y = 0; y < height; ++y) { row_pointers[y] = row; row += rowSize; } if (!writer->writePointers(row_pointers, height)) { delete[] row_pointers; return splashErrGeneric; } delete[] row_pointers; } break; case splashModeBGR8: { unsigned char *row = new unsigned char[3 * width]; for (int y = 0; y < height; y++) { // Convert into a PNG row for (int x = 0; x < width; x++) { row[3 * x] = data[y * rowSize + x * 3 + 2]; row[3 * x + 1] = data[y * rowSize + x * 3 + 1]; row[3 * x + 2] = data[y * rowSize + x * 3]; } if (!writer->writeRow(&row)) { delete[] row; return splashErrGeneric; } } delete[] row; } break; case splashModeXBGR8: { unsigned char *row = new unsigned char[3 * width]; for (int y = 0; y < height; y++) { // Convert into a PNG row for (int x = 0; x < width; x++) { row[3 * x] = data[y * rowSize + x * 4 + 2]; row[3 * x + 1] = data[y * rowSize + x * 4 + 1]; row[3 * x + 2] = data[y * rowSize + x * 4]; } if (!writer->writeRow(&row)) { delete[] row; return splashErrGeneric; } } delete[] row; } break; case splashModeMono8: { if (imageWriterFormat == splashModeMono8) { SplashColorPtr row; unsigned char **row_pointers = new unsigned char *[height]; row = data; for (int y = 0; y < height; ++y) { row_pointers[y] = row; row += rowSize; } if (!writer->writePointers(row_pointers, height)) { delete[] row_pointers; return splashErrGeneric; } delete[] row_pointers; } else if (imageWriterFormat == splashModeRGB8) { unsigned char *row = new unsigned char[3 * width]; for (int y = 0; y < height; y++) { // Convert into a PNG row for (int x = 0; x < width; x++) { row[3 * x] = data[y * rowSize + x]; row[3 * x + 1] = data[y * rowSize + x]; row[3 * x + 2] = data[y * rowSize + x]; } if (!writer->writeRow(&row)) { delete[] row; return splashErrGeneric; } } delete[] row; } else { // only splashModeMono8 or splashModeRGB8 return splashErrGeneric; } } break; case splashModeMono1: { if (imageWriterFormat == splashModeMono1) { SplashColorPtr row; unsigned char **row_pointers = new unsigned char *[height]; row = data; for (int y = 0; y < height; ++y) { row_pointers[y] = row; row += rowSize; } if (!writer->writePointers(row_pointers, height)) { delete[] row_pointers; return splashErrGeneric; } delete[] row_pointers; } else if (imageWriterFormat == splashModeRGB8) { unsigned char *row = new unsigned char[3 * width]; for (int y = 0; y < height; y++) { // Convert into a PNG row for (int x = 0; x < width; x++) { getPixel(x, y, &row[3 * x]); row[3 * x + 1] = row[3 * x]; row[3 * x + 2] = row[3 * x]; } if (!writer->writeRow(&row)) { delete[] row; return splashErrGeneric; } } delete[] row; } else { // only splashModeMono1 or splashModeRGB8 return splashErrGeneric; } } break; default: // can't happen break; } if (!writer->close()) { return splashErrGeneric; } return splashOk; } poppler-24.02.0/splash/SplashBitmap.h000066400000000000000000000122641455701731300174050ustar00rootroot00000000000000//======================================================================== // // SplashBitmap.h // //======================================================================== //======================================================================== // // 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) 2007 Ilmari Heikkinen // Copyright (C) 2009 Shen Liang // Copyright (C) 2009, 2012, 2018, 2021, 2022 Albert Astals Cid // Copyright (C) 2009 Stefan Thomas // Copyright (C) 2010, 2017 Adrian Johnson // Copyright (C) 2010 Harry Roberts // Copyright (C) 2010 Christian Feuersänger // Copyright (C) 2010 William Bader // Copyright (C) 2012 Thomas Freitag // Copyright (C) 2015 Adam Reichold // Copyright (C) 2016 Kenji Uno // Copyright (C) 2018 Martin Packman // Copyright (C) 2019 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 // //======================================================================== #ifndef SPLASHBITMAP_H #define SPLASHBITMAP_H #include "SplashTypes.h" #include "poppler_private_export.h" #include #include #include class ImgWriter; class GfxSeparationColorSpace; //------------------------------------------------------------------------ // SplashBitmap //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT SplashBitmap { public: // Create a new bitmap. It will have x pixels in // 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); static SplashBitmap *copy(const SplashBitmap *src); ~SplashBitmap(); SplashBitmap(const SplashBitmap &) = delete; SplashBitmap &operator=(const SplashBitmap &) = delete; int getWidth() const { return width; } int getHeight() const { return height; } int getRowSize() const { return rowSize; } int getAlphaRowSize() const { return width; } int getRowPad() const { return rowPad; } SplashColorMode getMode() const { return mode; } SplashColorPtr getDataPtr() { return data; } unsigned char *getAlphaPtr() { return alpha; } std::vector *getSeparationList() { return separationList; } SplashColorConstPtr getDataPtr() const { return data; } const unsigned char *getAlphaPtr() const { return alpha; } const std::vector *getSeparationList() const { return separationList; } SplashError writePNMFile(char *fileName); SplashError writePNMFile(FILE *f); SplashError writeAlphaPGMFile(char *fileName); struct WriteImgParams { int jpegQuality = -1; bool jpegProgressive = false; std::string tiffCompression; bool jpegOptimize = false; }; SplashError writeImgFile(SplashImageFileFormat format, const char *fileName, double hDPI, double vDPI, WriteImgParams *params = nullptr); SplashError writeImgFile(SplashImageFileFormat format, FILE *f, double hDPI, double vDPI, WriteImgParams *params = nullptr); SplashError writeImgFile(ImgWriter *writer, FILE *f, double hDPI, double vDPI, SplashColorMode imageWriterFormat); enum ConversionMode { conversionOpaque, conversionAlpha, conversionAlphaPremultiplied }; bool convertToXBGR(ConversionMode conversionMode = conversionOpaque); void getPixel(int x, int y, SplashColorPtr pixel); void getRGBLine(int y, SplashColorPtr line); void getXBGRLine(int y, SplashColorPtr line, ConversionMode conversionMode = conversionOpaque); void getCMYKLine(int y, SplashColorPtr line); unsigned char getAlpha(int x, int y); // Caller takes ownership of the bitmap data. The SplashBitmap // object is no longer valid -- the next call should be to the // destructor. SplashColorPtr takeData(); private: int width, height; // size of bitmap int rowPad; int rowSize; // size of one row of data, in bytes // - negative for bottom-up bitmaps SplashColorMode mode; // color mode 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 friend class Splash; void setJpegParams(ImgWriter *writer, WriteImgParams *params); }; #endif poppler-24.02.0/splash/SplashClip.cc000066400000000000000000000251471455701731300172220ustar00rootroot00000000000000//======================================================================== // // SplashClip.cc // //======================================================================== //======================================================================== // // 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) 2010, 2021 Albert Astals Cid // Copyright (C) 2013, 2021 Thomas Freitag // Copyright (C) 2019 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 // //======================================================================== #include #include #include #include "goo/gmem.h" #include "SplashErrorCodes.h" #include "SplashMath.h" #include "SplashPath.h" #include "SplashXPath.h" #include "SplashXPathScanner.h" #include "SplashBitmap.h" #include "SplashClip.h" //------------------------------------------------------------------------ // SplashClip.flags //------------------------------------------------------------------------ #define splashClipEO 0x01 // use even-odd rule //------------------------------------------------------------------------ // SplashClip //------------------------------------------------------------------------ SplashClip::SplashClip(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1, bool antialiasA) { antialias = antialiasA; if (x0 < x1) { xMin = x0; xMax = x1; } else { xMin = x1; xMax = x0; } if (y0 < y1) { yMin = y0; yMax = y1; } else { yMin = y1; yMax = y0; } xMinI = splashFloor(xMin); yMinI = splashFloor(yMin); xMaxI = splashCeil(xMax) - 1; yMaxI = splashCeil(yMax) - 1; flags = nullptr; length = size = 0; } SplashClip::SplashClip(const SplashClip *clip) { int i; antialias = clip->antialias; xMin = clip->xMin; yMin = clip->yMin; xMax = clip->xMax; yMax = clip->yMax; xMinI = clip->xMinI; yMinI = clip->yMinI; xMaxI = clip->xMaxI; yMaxI = clip->yMaxI; length = clip->length; size = clip->size; flags = (unsigned char *)gmallocn(size, sizeof(unsigned char)); scanners = clip->scanners; for (i = 0; i < length; ++i) { flags[i] = clip->flags[i]; } } SplashClip::~SplashClip() { gfree(flags); } void SplashClip::grow(int nPaths) { if (length + nPaths > size) { if (size == 0) { size = 32; } while (size < length + nPaths) { size *= 2; } flags = (unsigned char *)greallocn(flags, size, sizeof(unsigned char)); } } void SplashClip::resetToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) { gfree(flags); flags = nullptr; scanners = {}; length = size = 0; if (x0 < x1) { xMin = x0; xMax = x1; } else { xMin = x1; xMax = x0; } if (y0 < y1) { yMin = y0; yMax = y1; } else { yMin = y1; yMax = y0; } xMinI = splashFloor(xMin); yMinI = splashFloor(yMin); xMaxI = splashCeil(xMax) - 1; yMaxI = splashCeil(yMax) - 1; } SplashError SplashClip::clipToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) { if (x0 < x1) { if (x0 > xMin) { xMin = x0; xMinI = splashFloor(xMin); } if (x1 < xMax) { xMax = x1; xMaxI = splashCeil(xMax) - 1; } } else { if (x1 > xMin) { xMin = x1; xMinI = splashFloor(xMin); } if (x0 < xMax) { xMax = x0; xMaxI = splashCeil(xMax) - 1; } } if (y0 < y1) { if (y0 > yMin) { yMin = y0; yMinI = splashFloor(yMin); } if (y1 < yMax) { yMax = y1; yMaxI = splashCeil(yMax) - 1; } } else { if (y1 > yMin) { yMin = y1; yMinI = splashFloor(yMin); } if (y0 < yMax) { yMax = y0; yMaxI = splashCeil(yMax) - 1; } } return splashOk; } SplashError SplashClip::clipToPath(SplashPath *path, SplashCoord *matrix, SplashCoord flatness, bool eo) { int yMinAA, yMaxAA; SplashXPath xPath(path, matrix, flatness, true); // check for an empty path if (xPath.length == 0) { xMax = xMin - 1; yMax = yMin - 1; xMaxI = splashCeil(xMax) - 1; yMaxI = splashCeil(yMax) - 1; // check for a rectangle } else if (xPath.length == 4 && ((xPath.segs[0].x0 == xPath.segs[0].x1 && xPath.segs[0].x0 == xPath.segs[1].x0 && xPath.segs[0].x0 == xPath.segs[3].x1 && xPath.segs[2].x0 == xPath.segs[2].x1 && xPath.segs[2].x0 == xPath.segs[1].x1 && xPath.segs[2].x0 == xPath.segs[3].x0 && xPath.segs[1].y0 == xPath.segs[1].y1 && xPath.segs[1].y0 == xPath.segs[0].y1 && xPath.segs[1].y0 == xPath.segs[2].y0 && xPath.segs[3].y0 == xPath.segs[3].y1 && xPath.segs[3].y0 == xPath.segs[0].y0 && xPath.segs[3].y0 == xPath.segs[2].y1) || (xPath.segs[0].y0 == xPath.segs[0].y1 && xPath.segs[0].y0 == xPath.segs[1].y0 && xPath.segs[0].y0 == xPath.segs[3].y1 && xPath.segs[2].y0 == xPath.segs[2].y1 && xPath.segs[2].y0 == xPath.segs[1].y1 && xPath.segs[2].y0 == xPath.segs[3].y0 && xPath.segs[1].x0 == xPath.segs[1].x1 && xPath.segs[1].x0 == xPath.segs[0].x1 && xPath.segs[1].x0 == xPath.segs[2].x0 && xPath.segs[3].x0 == xPath.segs[3].x1 && xPath.segs[3].x0 == xPath.segs[0].x0 && xPath.segs[3].x0 == xPath.segs[2].x1))) { clipToRect(xPath.segs[0].x0, xPath.segs[0].y0, xPath.segs[2].x0, xPath.segs[2].y0); } else { grow(1); if (antialias) { xPath.aaScale(); } xPath.sort(); flags[length] = eo ? splashClipEO : 0; if (antialias) { yMinAA = yMinI * splashAASize; yMaxAA = (yMaxI + 1) * splashAASize - 1; } else { yMinAA = yMinI; yMaxAA = yMaxI; } scanners.emplace_back(std::make_shared(xPath, eo, yMinAA, yMaxAA)); ++length; } return splashOk; } SplashClipResult SplashClip::testRect(int rectXMin, int rectYMin, int rectXMax, int rectYMax) { // This tests the rectangle: // x = [rectXMin, rectXMax + 1) (note: rect coords are ints) // y = [rectYMin, rectYMax + 1) // against the clipping region: // x = [xMin, xMax) (note: clipping coords are fp) // y = [yMin, yMax) if ((SplashCoord)(rectXMax + 1) <= xMin || (SplashCoord)rectXMin >= xMax || (SplashCoord)(rectYMax + 1) <= yMin || (SplashCoord)rectYMin >= yMax) { return splashClipAllOutside; } if ((SplashCoord)rectXMin >= xMin && (SplashCoord)(rectXMax + 1) <= xMax && (SplashCoord)rectYMin >= yMin && (SplashCoord)(rectYMax + 1) <= yMax && length == 0) { return splashClipAllInside; } return splashClipPartial; } SplashClipResult SplashClip::testSpan(int spanXMin, int spanXMax, int spanY) { int i; // This tests the rectangle: // x = [spanXMin, spanXMax + 1) (note: span coords are ints) // y = [spanY, spanY + 1) // against the clipping region: // x = [xMin, xMax) (note: clipping coords are fp) // y = [yMin, yMax) if ((SplashCoord)(spanXMax + 1) <= xMin || (SplashCoord)spanXMin >= xMax || (SplashCoord)(spanY + 1) <= yMin || (SplashCoord)spanY >= yMax) { return splashClipAllOutside; } if (!((SplashCoord)spanXMin >= xMin && (SplashCoord)(spanXMax + 1) <= xMax && (SplashCoord)spanY >= yMin && (SplashCoord)(spanY + 1) <= yMax)) { return splashClipPartial; } if (antialias) { for (i = 0; i < length; ++i) { if (!scanners[i]->testSpan(spanXMin * splashAASize, spanXMax * splashAASize + (splashAASize - 1), spanY * splashAASize)) { return splashClipPartial; } } } else { for (i = 0; i < length; ++i) { if (!scanners[i]->testSpan(spanXMin, spanXMax, spanY)) { return splashClipPartial; } } } return splashClipAllInside; } void SplashClip::clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y, bool adjustVertLine) { int xx0, xx1, xx, yy, i; SplashColorPtr p; // zero out pixels with x < xMin xx0 = *x0 * splashAASize; xx1 = splashFloor(xMin * splashAASize); if (xx1 > aaBuf->getWidth()) { xx1 = aaBuf->getWidth(); } if (xx0 < xx1) { xx0 &= ~7; for (yy = 0; yy < splashAASize; ++yy) { p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx0 >> 3); for (xx = xx0; xx + 7 < xx1; xx += 8) { *p++ = 0; } if (xx < xx1 && !adjustVertLine) { *p &= 0xff >> (xx1 & 7); } } *x0 = splashFloor(xMin); } // zero out pixels with x > xMax xx0 = splashFloor(xMax * splashAASize) + 1; if (xx0 < 0) { xx0 = 0; } xx1 = (*x1 + 1) * splashAASize; if (xx0 < xx1 && !adjustVertLine) { for (yy = 0; yy < splashAASize; ++yy) { p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx0 >> 3); xx = xx0; if (xx & 7) { *p &= 0xff00 >> (xx & 7); xx = (xx & ~7) + 8; ++p; } for (; xx < xx1; xx += 8) { *p++ = 0; } } *x1 = splashFloor(xMax); } // check the paths for (i = 0; i < length; ++i) { scanners[i]->clipAALine(aaBuf, x0, x1, y); } if (*x0 > *x1) { *x0 = *x1; } if (*x0 < 0) { *x0 = 0; } if ((*x0 >> 1) >= aaBuf->getRowSize()) { xx0 = *x0; *x0 = (aaBuf->getRowSize() - 1) << 1; if (xx0 & 1) { *x0 = *x0 + 1; } } if (*x1 < *x0) { *x1 = *x0; } if ((*x1 >> 1) >= aaBuf->getRowSize()) { xx0 = *x1; *x1 = (aaBuf->getRowSize() - 1) << 1; if (xx0 & 1) { *x1 = *x1 + 1; } } } bool SplashClip::testClipPaths(int x, int y) { if (antialias) { x *= splashAASize; y *= splashAASize; } for (int i = 0; i < length; ++i) { if (!scanners[i]->test(x, y)) { return false; } } return true; } poppler-24.02.0/splash/SplashClip.h000066400000000000000000000103651455701731300170600ustar00rootroot00000000000000//======================================================================== // // SplashClip.h // //======================================================================== //======================================================================== // // 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) 2010, 2018, 2021 Albert Astals Cid // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2019 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 // //======================================================================== #ifndef SPLASHCLIP_H #define SPLASHCLIP_H #include "SplashTypes.h" #include #include class SplashPath; class SplashXPath; class SplashXPathScanner; class SplashBitmap; //------------------------------------------------------------------------ enum SplashClipResult { splashClipAllInside, splashClipAllOutside, splashClipPartial }; //------------------------------------------------------------------------ // SplashClip //------------------------------------------------------------------------ class SplashClip { public: // Create a clip, for the given rectangle. SplashClip(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1, bool antialiasA); // Copy a clip. SplashClip *copy() const { return new SplashClip(this); } ~SplashClip(); SplashClip(const SplashClip &) = delete; SplashClip &operator=(const SplashClip &) = delete; // Reset the clip to a rectangle. void resetToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1); // Intersect the clip with a rectangle. SplashError clipToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1); // Intersect the clip with . SplashError clipToPath(SplashPath *path, SplashCoord *matrix, SplashCoord flatness, bool eo); // Returns true if (,) is inside the clip. bool test(int x, int y) { // check the rectangle if (x < xMinI || x > xMaxI || y < yMinI || y > yMaxI) { return false; } // check the paths return testClipPaths(x, y); } // Tests a rectangle against the clipping region. Returns one of: // - splashClipAllInside if the entire rectangle is inside the // clipping region, i.e., all pixels in the rectangle are // visible // - splashClipAllOutside if the entire rectangle is outside the // clipping region, i.e., all the pixels in the rectangle are // clipped // - splashClipPartial if the rectangle is part inside and part // outside the clipping region SplashClipResult testRect(int rectXMin, int rectYMin, int rectXMax, int rectYMax); // Similar to testRect, but tests a horizontal span. SplashClipResult testSpan(int spanXMin, int spanXMax, int spanY); // 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, bool adjustVertLine = false); // Get the rectangle part of the clip region. SplashCoord getXMin() { return xMin; } SplashCoord getXMax() { return xMax; } SplashCoord getYMin() { return yMin; } SplashCoord getYMax() { return yMax; } // Get the rectangle part of the clip region, in integer coordinates. int getXMinI() { return xMinI; } int getXMaxI() { return xMaxI; } int getYMinI() { return yMinI; } int getYMaxI() { return yMaxI; } // Get the number of arbitrary paths used by the clip region. int getNumPaths() { return length; } protected: explicit SplashClip(const SplashClip *clip); void grow(int nPaths); bool testClipPaths(int x, int y); bool antialias; SplashCoord xMin, yMin, xMax, yMax; int xMinI, yMinI, xMaxI, yMaxI; unsigned char *flags; std::vector> scanners; int length, size; }; #endif poppler-24.02.0/splash/SplashErrorCodes.h000066400000000000000000000027071455701731300202410ustar00rootroot00000000000000//======================================================================== // // SplashErrorCodes.h // //======================================================================== //======================================================================== // // 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, 2009 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 // //======================================================================== #ifndef SPLASHERRORCODES_H #define SPLASHERRORCODES_H //------------------------------------------------------------------------ #define splashOk 0 // no error #define splashErrNoCurPt 1 // no current point #define splashErrEmptyPath 2 // zero points in path #define splashErrBogusPath 3 // only one point in subpath #define splashErrNoSave 4 // state stack is empty #define splashErrOpenFile 5 // couldn't open file #define splashErrNoGlyph 6 // couldn't get the requested glyph #define splashErrModeMismatch 7 // invalid combination of color modes #define splashErrSingularMatrix 8 // matrix is singular #define splashErrBadArg 9 // bad argument #define splashErrZeroImage 254 // image of 0x0 #define splashErrGeneric 255 #endif poppler-24.02.0/splash/SplashFTFont.cc000066400000000000000000000335431455701731300174720ustar00rootroot00000000000000//======================================================================== // // SplashFTFont.cc // //======================================================================== //======================================================================== // // 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) 2005, 2007-2011, 2014, 2018, 2020 Albert Astals Cid // Copyright (C) 2006 Kristian Høgsberg // Copyright (C) 2009 Petr Gajdos // Copyright (C) 2010 Suzuki Toshiya // Copyright (C) 2011 Andreas Hartmetz // Copyright (C) 2012 Thomas Freitag // Copyright (C) 2017 Adrian Johnson // Copyright (C) 2018 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 // //======================================================================== #include #include #include FT_OUTLINE_H #include FT_SIZES_H #include FT_GLYPH_H #include "goo/gmem.h" #include "SplashMath.h" #include "SplashGlyphBitmap.h" #include "SplashPath.h" #include "SplashFTFontEngine.h" #include "SplashFTFontFile.h" #include "SplashFTFont.h" #include "goo/GooLikely.h" //------------------------------------------------------------------------ static int glyphPathMoveTo(const FT_Vector *pt, void *path); static int glyphPathLineTo(const FT_Vector *pt, void *path); static int glyphPathConicTo(const FT_Vector *ctrl, const FT_Vector *pt, void *path); static int glyphPathCubicTo(const FT_Vector *ctrl1, const FT_Vector *ctrl2, const FT_Vector *pt, void *path); //------------------------------------------------------------------------ // SplashFTFont //------------------------------------------------------------------------ SplashFTFont::SplashFTFont(SplashFTFontFile *fontFileA, SplashCoord *matA, const SplashCoord *textMatA) : SplashFont(fontFileA, matA, textMatA, fontFileA->engine->aa), textScale(0), enableFreeTypeHinting(fontFileA->engine->enableFreeTypeHinting), enableSlightHinting(fontFileA->engine->enableSlightHinting), isOk(false) { FT_Face face; int div; int x, y; face = fontFileA->face; if (FT_New_Size(face, &sizeObj)) { return; } face->size = sizeObj; size = splashRound(splashDist(0, 0, mat[2], mat[3])); if (size < 1) { size = 1; } if (FT_Set_Pixel_Sizes(face, 0, size)) { return; } // if the textMat values are too small, FreeType's fixed point // arithmetic doesn't work so well textScale = splashDist(0, 0, textMat[2], textMat[3]) / size; if (unlikely(textScale == 0 || face->units_per_EM == 0)) { return; } div = face->bbox.xMax > 20000 ? 65536 : 1; // transform the four corners of the font bounding box -- the min // and max values form the bounding box of the transformed font x = (int)((mat[0] * face->bbox.xMin + mat[2] * face->bbox.yMin) / (div * face->units_per_EM)); xMin = xMax = x; y = (int)((mat[1] * face->bbox.xMin + mat[3] * face->bbox.yMin) / (div * face->units_per_EM)); yMin = yMax = y; x = (int)((mat[0] * face->bbox.xMin + mat[2] * face->bbox.yMax) / (div * face->units_per_EM)); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } y = (int)((mat[1] * face->bbox.xMin + mat[3] * face->bbox.yMax) / (div * face->units_per_EM)); if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } x = (int)((mat[0] * face->bbox.xMax + mat[2] * face->bbox.yMin) / (div * face->units_per_EM)); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } y = (int)((mat[1] * face->bbox.xMax + mat[3] * face->bbox.yMin) / (div * face->units_per_EM)); if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } x = (int)((mat[0] * face->bbox.xMax + mat[2] * face->bbox.yMax) / (div * face->units_per_EM)); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } y = (int)((mat[1] * face->bbox.xMax + mat[3] * face->bbox.yMax) / (div * face->units_per_EM)); if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } // This is a kludge: some buggy PDF generators embed fonts with // zero bounding boxes. if (xMax == xMin) { xMin = 0; xMax = size; } if (yMax == yMin) { yMin = 0; yMax = (int)((SplashCoord)1.2 * size); } // compute the transform matrix matrix.xx = (FT_Fixed)((mat[0] / size) * 65536); matrix.yx = (FT_Fixed)((mat[1] / size) * 65536); matrix.xy = (FT_Fixed)((mat[2] / size) * 65536); matrix.yy = (FT_Fixed)((mat[3] / size) * 65536); textMatrix.xx = (FT_Fixed)((textMat[0] / (textScale * size)) * 65536); textMatrix.yx = (FT_Fixed)((textMat[1] / (textScale * size)) * 65536); textMatrix.xy = (FT_Fixed)((textMat[2] / (textScale * size)) * 65536); textMatrix.yy = (FT_Fixed)((textMat[3] / (textScale * size)) * 65536); isOk = true; } SplashFTFont::~SplashFTFont() { } bool SplashFTFont::getGlyph(int c, int xFrac, int yFrac, SplashGlyphBitmap *bitmap, int x0, int y0, SplashClip *clip, SplashClipResult *clipRes) { return SplashFont::getGlyph(c, xFrac, 0, bitmap, x0, y0, clip, clipRes); } static FT_Int32 getFTLoadFlags(bool type1, bool trueType, bool aa, bool enableFreeTypeHinting, bool enableSlightHinting) { int ret = FT_LOAD_DEFAULT; if (aa) { ret |= FT_LOAD_NO_BITMAP; } if (enableFreeTypeHinting) { if (enableSlightHinting) { ret |= FT_LOAD_TARGET_LIGHT; } else { if (trueType) { // FT2's autohinting doesn't always work very well (especially with // font subsets), so turn it off if anti-aliasing is enabled; if // anti-aliasing is disabled, this seems to be a tossup - some fonts // look better with hinting, some without, so leave hinting on if (aa) { ret |= FT_LOAD_NO_AUTOHINT; } } else if (type1) { // Type 1 fonts seem to look better with 'light' hinting mode ret |= FT_LOAD_TARGET_LIGHT; } } } else { ret |= FT_LOAD_NO_HINTING; } return ret; } bool SplashFTFont::makeGlyph(int c, int xFrac, int yFrac, SplashGlyphBitmap *bitmap, int x0, int y0, SplashClip *clip, SplashClipResult *clipRes) { SplashFTFontFile *ff; FT_Vector offset; FT_GlyphSlot slot; FT_UInt gid; int rowSize; unsigned char *p, *q; int i; if (unlikely(!isOk)) { return false; } ff = (SplashFTFontFile *)fontFile; ff->face->size = sizeObj; offset.x = (FT_Pos)(int)((SplashCoord)xFrac * splashFontFractionMul * 64); offset.y = 0; FT_Set_Transform(ff->face, &matrix, &offset); slot = ff->face->glyph; if (ff->codeToGID && c < ff->codeToGIDLen && c >= 0) { gid = (FT_UInt)ff->codeToGID[c]; } else { gid = (FT_UInt)c; } if (FT_Load_Glyph(ff->face, gid, getFTLoadFlags(ff->type1, ff->trueType, aa, enableFreeTypeHinting, enableSlightHinting))) { return false; } // prelimirary values based on FT_Outline_Get_CBox // we add two pixels to each side to be in the safe side FT_BBox cbox; FT_Outline_Get_CBox(&ff->face->glyph->outline, &cbox); bitmap->x = -(cbox.xMin / 64) + 2; bitmap->y = (cbox.yMax / 64) + 2; bitmap->w = ((cbox.xMax - cbox.xMin) / 64) + 4; bitmap->h = ((cbox.yMax - cbox.yMin) / 64) + 4; *clipRes = clip->testRect(x0 - bitmap->x, y0 - bitmap->y, x0 - bitmap->x + bitmap->w, y0 - bitmap->y + bitmap->h); if (*clipRes == splashClipAllOutside) { bitmap->freeData = false; return true; } if (FT_Render_Glyph(slot, aa ? ft_render_mode_normal : ft_render_mode_mono)) { return false; } if (slot->bitmap.width == 0 || slot->bitmap.rows == 0) { // this can happen if (a) the glyph is really tiny or (b) the // metrics in the TrueType file are broken return false; } bitmap->x = -slot->bitmap_left; bitmap->y = slot->bitmap_top; bitmap->w = slot->bitmap.width; bitmap->h = slot->bitmap.rows; bitmap->aa = aa; if (aa) { rowSize = bitmap->w; } else { rowSize = (bitmap->w + 7) >> 3; } bitmap->data = (unsigned char *)gmallocn_checkoverflow(rowSize, bitmap->h); if (!bitmap->data) { return false; } bitmap->freeData = true; for (i = 0, p = bitmap->data, q = slot->bitmap.buffer; i < bitmap->h; ++i, p += rowSize, q += slot->bitmap.pitch) { memcpy(p, q, rowSize); } return true; } double SplashFTFont::getGlyphAdvance(int c) { SplashFTFontFile *ff; FT_Vector offset; FT_UInt gid; FT_Matrix identityMatrix; ff = (SplashFTFontFile *)fontFile; // init the matrix identityMatrix.xx = 65536; // 1 in 16.16 format identityMatrix.xy = 0; identityMatrix.yx = 0; identityMatrix.yy = 65536; // 1 in 16.16 format // init the offset offset.x = 0; offset.y = 0; ff->face->size = sizeObj; FT_Set_Transform(ff->face, &identityMatrix, &offset); if (ff->codeToGID && c < ff->codeToGIDLen) { gid = (FT_UInt)ff->codeToGID[c]; } else { gid = (FT_UInt)c; } if (FT_Load_Glyph(ff->face, gid, getFTLoadFlags(ff->type1, ff->trueType, aa, enableFreeTypeHinting, enableSlightHinting))) { return -1; } // 64.0 is 1 in 26.6 format return ff->face->glyph->metrics.horiAdvance / 64.0 / size; } struct SplashFTFontPath { SplashPath *path; SplashCoord textScale; bool needClose; }; 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, #else &glyphPathMoveTo, &glyphPathLineTo, &glyphPathConicTo, &glyphPathCubicTo, #endif 0, 0 }; SplashFTFontFile *ff; SplashFTFontPath path; FT_GlyphSlot slot; FT_UInt gid; FT_Glyph glyph; if (unlikely(textScale == 0)) { return nullptr; } ff = (SplashFTFontFile *)fontFile; ff->face->size = sizeObj; FT_Set_Transform(ff->face, &textMatrix, nullptr); slot = ff->face->glyph; if (ff->codeToGID && c < ff->codeToGIDLen && c >= 0) { gid = ff->codeToGID[c]; } else { gid = (FT_UInt)c; } if (FT_Load_Glyph(ff->face, gid, getFTLoadFlags(ff->type1, ff->trueType, aa, enableFreeTypeHinting, enableSlightHinting))) { return nullptr; } if (FT_Get_Glyph(slot, &glyph)) { return nullptr; } if (FT_Outline_Check(&((FT_OutlineGlyph)glyph)->outline)) { return nullptr; } path.path = new SplashPath(); path.textScale = textScale; path.needClose = false; FT_Outline_Decompose(&((FT_OutlineGlyph)glyph)->outline, &outlineFuncs, &path); if (path.needClose) { path.path->close(); } FT_Done_Glyph(glyph); return path.path; } static int glyphPathMoveTo(const FT_Vector *pt, void *path) { SplashFTFontPath *p = (SplashFTFontPath *)path; if (p->needClose) { p->path->close(); p->needClose = false; } p->path->moveTo((SplashCoord)pt->x * p->textScale / 64.0, (SplashCoord)pt->y * p->textScale / 64.0); return 0; } static int glyphPathLineTo(const FT_Vector *pt, void *path) { SplashFTFontPath *p = (SplashFTFontPath *)path; p->path->lineTo((SplashCoord)pt->x * p->textScale / 64.0, (SplashCoord)pt->y * p->textScale / 64.0); p->needClose = true; return 0; } static int glyphPathConicTo(const FT_Vector *ctrl, const FT_Vector *pt, void *path) { SplashFTFontPath *p = (SplashFTFontPath *)path; SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xc, yc; if (!p->path->getCurPt(&x0, &y0)) { return 0; } xc = (SplashCoord)ctrl->x * p->textScale / 64.0; yc = (SplashCoord)ctrl->y * p->textScale / 64.0; x3 = (SplashCoord)pt->x * p->textScale / 64.0; y3 = (SplashCoord)pt->y * p->textScale / 64.0; // A second-order Bezier curve is defined by two endpoints, p0 and // p3, and one control point, pc: // // p(t) = (1-t)^2*p0 + t*(1-t)*pc + t^2*p3 // // A third-order Bezier curve is defined by the same two endpoints, // p0 and p3, and two control points, p1 and p2: // // p(t) = (1-t)^3*p0 + 3t*(1-t)^2*p1 + 3t^2*(1-t)*p2 + t^3*p3 // // Applying some algebra, we can convert a second-order curve to a // third-order curve: // // p1 = (1/3) * (p0 + 2pc) // p2 = (1/3) * (2pc + p3) x1 = (SplashCoord)(1.0 / 3.0) * (x0 + (SplashCoord)2 * xc); y1 = (SplashCoord)(1.0 / 3.0) * (y0 + (SplashCoord)2 * yc); x2 = (SplashCoord)(1.0 / 3.0) * ((SplashCoord)2 * xc + x3); y2 = (SplashCoord)(1.0 / 3.0) * ((SplashCoord)2 * yc + y3); p->path->curveTo(x1, y1, x2, y2, x3, y3); p->needClose = true; return 0; } static int glyphPathCubicTo(const FT_Vector *ctrl1, const FT_Vector *ctrl2, const FT_Vector *pt, void *path) { SplashFTFontPath *p = (SplashFTFontPath *)path; p->path->curveTo((SplashCoord)ctrl1->x * p->textScale / 64.0, (SplashCoord)ctrl1->y * p->textScale / 64.0, (SplashCoord)ctrl2->x * p->textScale / 64.0, (SplashCoord)ctrl2->y * p->textScale / 64.0, (SplashCoord)pt->x * p->textScale / 64.0, (SplashCoord)pt->y * p->textScale / 64.0); p->needClose = true; return 0; } poppler-24.02.0/splash/SplashFTFont.h000066400000000000000000000044561455701731300173350ustar00rootroot00000000000000//======================================================================== // // SplashFTFont.h // //======================================================================== //======================================================================== // // 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) 2007-2009, 2011, 2018 Albert Astals Cid // Copyright (C) 2009 Petr Gajdos // Copyright (C) 2011 Andreas Hartmetz // Copyright (C) 2017 Adrian Johnson // Copyright (C) 2018 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 // //======================================================================== #ifndef SPLASHFTFONT_H #define SPLASHFTFONT_H #include "poppler-config.h" #include #include FT_FREETYPE_H #include "SplashFont.h" class SplashFTFontFile; //------------------------------------------------------------------------ // SplashFTFont //------------------------------------------------------------------------ class SplashFTFont : public SplashFont { public: SplashFTFont(SplashFTFontFile *fontFileA, SplashCoord *matA, const SplashCoord *textMatA); ~SplashFTFont() override; // Munge xFrac and yFrac before calling SplashFont::getGlyph. bool getGlyph(int c, int xFrac, int yFrac, SplashGlyphBitmap *bitmap, int x0, int y0, SplashClip *clip, SplashClipResult *clipRes) override; // Rasterize a glyph. The and values are the same // as described for getGlyph. bool makeGlyph(int c, int xFrac, int yFrac, SplashGlyphBitmap *bitmap, int x0, int y0, SplashClip *clip, SplashClipResult *clipRes) override; // Return the path for a glyph. SplashPath *getGlyphPath(int c) override; // Return the advance of a glyph. (in 0..1 range) double getGlyphAdvance(int c) override; private: FT_Size sizeObj; FT_Matrix matrix; FT_Matrix textMatrix; SplashCoord textScale; int size; bool enableFreeTypeHinting; bool enableSlightHinting; bool isOk; }; #endif poppler-24.02.0/splash/SplashFTFontEngine.cc000066400000000000000000000064701455701731300206170ustar00rootroot00000000000000//======================================================================== // // SplashFTFontEngine.cc // //======================================================================== //======================================================================== // // 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 Takashi Iwai // Copyright (C) 2009, 2011, 2012, 2022 Albert Astals Cid // Copyright (C) 2009 Petr Gajdos // Copyright (C) 2011 Andreas Hartmetz // Copyright (C) 2017 Adrian Johnson // 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 // //======================================================================== #include #include #ifdef HAVE_UNISTD_H # include #endif #include "goo/gmem.h" #include "goo/GooString.h" #include "goo/gfile.h" #include "fofi/FoFiTrueType.h" #include "fofi/FoFiType1C.h" #include "SplashFTFontFile.h" #include "SplashFTFontEngine.h" //------------------------------------------------------------------------ // SplashFTFontEngine //------------------------------------------------------------------------ SplashFTFontEngine::SplashFTFontEngine(bool aaA, bool enableFreeTypeHintingA, bool enableSlightHintingA, FT_Library libA) { aa = aaA; enableFreeTypeHinting = enableFreeTypeHintingA; enableSlightHinting = enableSlightHintingA; lib = libA; } SplashFTFontEngine *SplashFTFontEngine::init(bool aaA, bool enableFreeTypeHintingA, bool enableSlightHintingA) { FT_Library libA; if (FT_Init_FreeType(&libA)) { return nullptr; } return new SplashFTFontEngine(aaA, enableFreeTypeHintingA, enableSlightHintingA, libA); } SplashFTFontEngine::~SplashFTFontEngine() { FT_Done_FreeType(lib); } SplashFontFile *SplashFTFontEngine::loadType1Font(SplashFontFileID *idA, SplashFontSrc *src, const char **enc) { return SplashFTFontFile::loadType1Font(this, idA, src, enc); } SplashFontFile *SplashFTFontEngine::loadType1CFont(SplashFontFileID *idA, SplashFontSrc *src, const char **enc) { return SplashFTFontFile::loadType1Font(this, idA, src, enc); } SplashFontFile *SplashFTFontEngine::loadOpenTypeT1CFont(SplashFontFileID *idA, SplashFontSrc *src, const char **enc) { return SplashFTFontFile::loadType1Font(this, idA, src, enc); } SplashFontFile *SplashFTFontEngine::loadCIDFont(SplashFontFileID *idA, SplashFontSrc *src) { return SplashFTFontFile::loadCIDFont(this, idA, src, nullptr, 0); } SplashFontFile *SplashFTFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA, SplashFontSrc *src, int *codeToGID, int codeToGIDLen) { return SplashFTFontFile::loadCIDFont(this, idA, src, codeToGID ? codeToGID : nullptr, codeToGID ? codeToGIDLen : 0); } SplashFontFile *SplashFTFontEngine::loadTrueTypeFont(SplashFontFileID *idA, SplashFontSrc *src, int *codeToGID, int codeToGIDLen, int faceIndex) { SplashFontFile *ret; ret = SplashFTFontFile::loadTrueTypeFont(this, idA, src, codeToGID, codeToGIDLen, faceIndex); return ret; } poppler-24.02.0/splash/SplashFTFontEngine.h000066400000000000000000000051711455701731300204560ustar00rootroot00000000000000//======================================================================== // // SplashFTFontEngine.h // //======================================================================== //======================================================================== // // 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 Takashi Iwai // Copyright (C) 2009 Petr Gajdos // Copyright (C) 2009, 2018, 2022 Albert Astals Cid // Copyright (C) 2011 Andreas Hartmetz // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2017 Adrian Johnson // // 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 SPLASHFTFONTENGINE_H #define SPLASHFTFONTENGINE_H #include #include FT_FREETYPE_H class SplashFontFile; class SplashFontFileID; class SplashFontSrc; //------------------------------------------------------------------------ // SplashFTFontEngine //------------------------------------------------------------------------ class SplashFTFontEngine { public: static SplashFTFontEngine *init(bool aaA, bool enableFreeTypeHintingA, bool enableSlightHinting); ~SplashFTFontEngine(); SplashFTFontEngine(const SplashFTFontEngine &) = delete; 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); bool getAA() { return aa; } void setAA(bool aaA) { aa = aaA; } private: SplashFTFontEngine(bool aaA, bool enableFreeTypeHintingA, bool enableSlightHintingA, FT_Library libA); bool aa; bool enableFreeTypeHinting; bool enableSlightHinting; FT_Library lib; friend class SplashFTFontFile; friend class SplashFTFont; }; #endif poppler-24.02.0/splash/SplashFTFontFile.cc000066400000000000000000000103621455701731300202640ustar00rootroot00000000000000//======================================================================== // // SplashFTFontFile.cc // //======================================================================== //======================================================================== // // 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 Takashi Iwai // Copyright (C) 2014, 2017, 2022 Adrian Johnson // Copyright (C) 2017, 2018, 2022 Oliver Sander // Copyright (C) 2018 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 #include "goo/ft_utils.h" #include "goo/gmem.h" #include "goo/GooString.h" #include "poppler/GfxFont.h" #include "SplashFTFontEngine.h" #include "SplashFTFont.h" #include "SplashFTFontFile.h" //------------------------------------------------------------------------ // SplashFTFontFile //------------------------------------------------------------------------ SplashFontFile *SplashFTFontFile::loadType1Font(SplashFTFontEngine *engineA, SplashFontFileID *idA, SplashFontSrc *src, const char **encA) { 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)) { return nullptr; } } else { if (FT_New_Memory_Face(engineA->lib, (const FT_Byte *)src->buf.data(), src->buf.size(), 0, &faceA)) { return nullptr; } } codeToGIDA = (int *)gmallocn(256, sizeof(int)); 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) { name = GfxFont::getAlternateName(name); if (name) { codeToGIDA[i] = FT_Get_Name_Index(faceA, (char *)name); } } } } return new SplashFTFontFile(engineA, idA, src, faceA, codeToGIDA, 256, false, true); } SplashFontFile *SplashFTFontFile::loadCIDFont(SplashFTFontEngine *engineA, SplashFontFileID *idA, SplashFontSrc *src, int *codeToGIDA, int codeToGIDLenA) { FT_Face faceA; if (src->isFile) { if (ft_new_face_from_file(engineA->lib, src->fileName.c_str(), 0, &faceA)) { return nullptr; } } else { if (FT_New_Memory_Face(engineA->lib, (const FT_Byte *)src->buf.data(), src->buf.size(), 0, &faceA)) { return nullptr; } } return new SplashFTFontFile(engineA, idA, src, faceA, codeToGIDA, codeToGIDLenA, false, false); } SplashFontFile *SplashFTFontFile::loadTrueTypeFont(SplashFTFontEngine *engineA, SplashFontFileID *idA, SplashFontSrc *src, int *codeToGIDA, int codeToGIDLenA, int faceIndexA) { FT_Face faceA; if (src->isFile) { 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(), faceIndexA, &faceA)) { return nullptr; } } return new SplashFTFontFile(engineA, idA, src, faceA, codeToGIDA, codeToGIDLenA, true, false); } SplashFTFontFile::SplashFTFontFile(SplashFTFontEngine *engineA, SplashFontFileID *idA, SplashFontSrc *srcA, FT_Face faceA, int *codeToGIDA, int codeToGIDLenA, bool trueTypeA, bool type1A) : SplashFontFile(idA, srcA) { engine = engineA; face = faceA; codeToGID = codeToGIDA; codeToGIDLen = codeToGIDLenA; trueType = trueTypeA; type1 = type1A; } SplashFTFontFile::~SplashFTFontFile() { if (face) { FT_Done_Face(face); } if (codeToGID) { gfree(codeToGID); } } SplashFont *SplashFTFontFile::makeFont(SplashCoord *mat, const SplashCoord *textMat) { SplashFont *font; font = new SplashFTFont(this, mat, textMat); font->initCache(); return font; } poppler-24.02.0/splash/SplashFTFontFile.h000066400000000000000000000044311455701731300201260ustar00rootroot00000000000000//======================================================================== // // SplashFTFontFile.h // //======================================================================== //======================================================================== // // 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 Takashi Iwai // Copyright (C) 2017, 2018 Oliver Sander // Copyright (C) 2017 Adrian Johnson // Copyright (C) 2019 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 // //======================================================================== #ifndef SPLASHFTFONTFILE_H #define SPLASHFTFONTFILE_H #include #include FT_FREETYPE_H #include "SplashFontFile.h" class SplashFontFileID; class SplashFTFontEngine; //------------------------------------------------------------------------ // SplashFTFontFile //------------------------------------------------------------------------ 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); ~SplashFTFontFile() override; // Create a new SplashFTFont, i.e., a scaled instance of this font // file. 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); SplashFTFontEngine *engine; FT_Face face; int *codeToGID; int codeToGIDLen; bool trueType; bool type1; friend class SplashFTFont; }; #endif poppler-24.02.0/splash/SplashFont.cc000066400000000000000000000143461455701731300172400ustar00rootroot00000000000000//======================================================================== // // SplashFont.cc // //======================================================================== //======================================================================== // // 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) 2007-2008, 2010, 2014, 2019 Albert Astals Cid // Copyright (C) 2018 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 // //======================================================================== #include #include #include #include "goo/gmem.h" #include "SplashMath.h" #include "SplashGlyphBitmap.h" #include "SplashFontFile.h" #include "SplashFont.h" //------------------------------------------------------------------------ struct SplashFontCacheTag { int c; short xFrac, yFrac; // x and y fractions int mru; // valid bit (0x80000000) and MRU index int x, y, w, h; // offset and size of glyph }; //------------------------------------------------------------------------ // SplashFont //------------------------------------------------------------------------ SplashFont::SplashFont(SplashFontFile *fontFileA, const SplashCoord *matA, const SplashCoord *textMatA, bool aaA) { fontFile = fontFileA; fontFile->incRefCnt(); mat[0] = matA[0]; mat[1] = matA[1]; mat[2] = matA[2]; mat[3] = matA[3]; textMat[0] = textMatA[0]; textMat[1] = textMatA[1]; textMat[2] = textMatA[2]; textMat[3] = textMatA[3]; aa = aaA; cache = nullptr; cacheTags = nullptr; xMin = yMin = xMax = yMax = 0; } void SplashFont::initCache() { int i; // this should be (max - min + 1), but we add some padding to // deal with rounding errors glyphW = xMax - xMin + 3; glyphH = yMax - yMin + 3; if (glyphW > INT_MAX / glyphH) { glyphSize = -1; } else { if (aa) { glyphSize = glyphW * glyphH; } else { glyphSize = ((glyphW + 7) >> 3) * glyphH; } } // set up the glyph pixmap cache cacheAssoc = 8; if (glyphSize <= 64) { cacheSets = 32; } else if (glyphSize <= 128) { cacheSets = 16; } else if (glyphSize <= 256) { cacheSets = 8; } else if (glyphSize <= 512) { cacheSets = 4; } else if (glyphSize <= 1024) { cacheSets = 2; } else { cacheSets = 1; } cache = (unsigned char *)gmallocn_checkoverflow(cacheSets * cacheAssoc, glyphSize); if (cache != nullptr) { cacheTags = (SplashFontCacheTag *)gmallocn(cacheSets * cacheAssoc, sizeof(SplashFontCacheTag)); for (i = 0; i < cacheSets * cacheAssoc; ++i) { cacheTags[i].mru = i & (cacheAssoc - 1); } } else { cacheAssoc = 0; } } SplashFont::~SplashFont() { fontFile->decRefCnt(); if (cache) { gfree(cache); } if (cacheTags) { gfree(cacheTags); } } bool SplashFont::getGlyph(int c, int xFrac, int yFrac, SplashGlyphBitmap *bitmap, int x0, int y0, SplashClip *clip, SplashClipResult *clipRes) { SplashGlyphBitmap bitmap2; int size; unsigned char *p; int i, j, k; // no fractional coordinates for large glyphs or non-anti-aliased // glyphs if (!aa || glyphH > 50) { xFrac = yFrac = 0; } // check the cache i = (c & (cacheSets - 1)) * cacheAssoc; for (j = 0; j < cacheAssoc; ++j) { if ((cacheTags[i + j].mru & 0x80000000) && cacheTags[i + j].c == c && (int)cacheTags[i + j].xFrac == xFrac && (int)cacheTags[i + j].yFrac == yFrac) { bitmap->x = cacheTags[i + j].x; bitmap->y = cacheTags[i + j].y; bitmap->w = cacheTags[i + j].w; bitmap->h = cacheTags[i + j].h; for (k = 0; k < cacheAssoc; ++k) { if (k != j && (cacheTags[i + k].mru & 0x7fffffff) < (cacheTags[i + j].mru & 0x7fffffff)) { ++cacheTags[i + k].mru; } } cacheTags[i + j].mru = 0x80000000; bitmap->aa = aa; bitmap->data = cache + (i + j) * glyphSize; bitmap->freeData = false; *clipRes = clip->testRect(x0 - bitmap->x, y0 - bitmap->y, x0 - bitmap->x + bitmap->w - 1, y0 - bitmap->y + bitmap->h - 1); return true; } } // generate the glyph bitmap if (!makeGlyph(c, xFrac, yFrac, &bitmap2, x0, y0, clip, clipRes)) { return false; } if (*clipRes == splashClipAllOutside) { bitmap->freeData = false; if (bitmap2.freeData) { gfree(bitmap2.data); } return true; } // if the glyph doesn't fit in the bounding box, return a temporary // uncached bitmap if (bitmap2.w > glyphW || bitmap2.h > glyphH) { *bitmap = bitmap2; return true; } // insert glyph pixmap in cache if (aa) { size = bitmap2.w * bitmap2.h; } else { size = ((bitmap2.w + 7) >> 3) * bitmap2.h; } p = nullptr; // make gcc happy if (cacheAssoc == 0) { // we had problems on the malloc of the cache, so ignore it *bitmap = bitmap2; } else { for (j = 0; j < cacheAssoc; ++j) { if ((cacheTags[i + j].mru & 0x7fffffff) == cacheAssoc - 1) { cacheTags[i + j].mru = 0x80000000; cacheTags[i + j].c = c; cacheTags[i + j].xFrac = (short)xFrac; cacheTags[i + j].yFrac = (short)yFrac; cacheTags[i + j].x = bitmap2.x; cacheTags[i + j].y = bitmap2.y; cacheTags[i + j].w = bitmap2.w; cacheTags[i + j].h = bitmap2.h; p = cache + (i + j) * glyphSize; memcpy(p, bitmap2.data, size); } else { ++cacheTags[i + j].mru; } } *bitmap = bitmap2; bitmap->data = p; bitmap->freeData = false; if (bitmap2.freeData) { gfree(bitmap2.data); } } return true; } poppler-24.02.0/splash/SplashFont.h000066400000000000000000000105401455701731300170720ustar00rootroot00000000000000//======================================================================== // // SplashFont.h // //======================================================================== //======================================================================== // // 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) 2007-2008, 2018, 2019 Albert Astals Cid // Copyright (C) 2018 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 // //======================================================================== #ifndef SPLASHFONT_H #define SPLASHFONT_H #include "SplashTypes.h" #include "SplashClip.h" struct SplashGlyphBitmap; struct SplashFontCacheTag; class SplashFontFile; class SplashPath; //------------------------------------------------------------------------ // Fractional positioning uses this many bits to the right of the // decimal points. #define splashFontFractionBits 2 #define splashFontFraction (1 << splashFontFractionBits) #define splashFontFractionMul ((SplashCoord)1 / (SplashCoord)splashFontFraction) //------------------------------------------------------------------------ // SplashFont //------------------------------------------------------------------------ class SplashFont { public: SplashFont(SplashFontFile *fontFileA, const SplashCoord *matA, const SplashCoord *textMatA, bool aaA); // This must be called after the constructor, so that the subclass // constructor has a chance to compute the bbox. void initCache(); virtual ~SplashFont(); SplashFont(const SplashFont &) = delete; SplashFont &operator=(const SplashFont &) = delete; SplashFontFile *getFontFile() { return fontFile; } // Return true if matches the specified font file and matrix. bool matches(SplashFontFile *fontFileA, const SplashCoord *matA, const SplashCoord *textMatA) const { return fontFileA == fontFile && matA[0] == mat[0] && matA[1] == mat[1] && matA[2] == mat[2] && matA[3] == mat[3] && textMatA[0] == textMat[0] && textMatA[1] == textMat[1] && textMatA[2] == textMat[2] && textMatA[3] == textMat[3]; } // Get a glyph - this does a cache lookup first, and if not found, // creates a new bitmap and adds it to the cache. The and // values are splashFontFractionBits bits each, representing // the numerators of fractions in [0, 1), where the denominator is // splashFontFraction = 1 << splashFontFractionBits. Subclasses // should override this to zero out xFrac and/or yFrac if they don't // support fractional coordinates. virtual bool getGlyph(int c, int xFrac, int yFrac, SplashGlyphBitmap *bitmap, int x0, int y0, SplashClip *clip, SplashClipResult *clipRes); // Rasterize a glyph. The and values are the same // as described for getGlyph. virtual bool makeGlyph(int c, int xFrac, int yFrac, SplashGlyphBitmap *bitmap, int x0, int y0, SplashClip *clip, SplashClipResult *clipRes) = 0; // Return the path for a glyph. virtual SplashPath *getGlyphPath(int c) = 0; // Return the advance of a glyph. (in 0..1 range) // < 0 means not known virtual double getGlyphAdvance(int c) { return -1; } // Return the font transform matrix. SplashCoord *getMatrix() { return mat; } // Return the glyph bounding box. void getBBox(int *xMinA, int *yMinA, int *xMaxA, int *yMaxA) { *xMinA = xMin; *yMinA = yMin; *xMaxA = xMax; *yMaxA = yMax; } protected: SplashFontFile *fontFile; SplashCoord mat[4]; // font transform matrix // (text space -> device space) SplashCoord textMat[4]; // text transform matrix // (text space -> user space) bool aa; // anti-aliasing int xMin, yMin, xMax, yMax; // glyph bounding box unsigned char *cache; // glyph bitmap cache SplashFontCacheTag * // cache tags cacheTags; int glyphW, glyphH; // size of glyph bitmaps int glyphSize; // size of glyph bitmaps, in bytes int cacheSets; // number of sets in cache int cacheAssoc; // cache associativity (glyphs per set) }; #endif poppler-24.02.0/splash/SplashFontEngine.cc000066400000000000000000000170011455701731300203550ustar00rootroot00000000000000//======================================================================== // // SplashFontEngine.cc // //======================================================================== //======================================================================== // // 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 Takashi Iwai // Copyright (C) 2009 Petr Gajdos // Copyright (C) 2009 Kovid Goyal // Copyright (C) 2009 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 // // 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 #include #include #ifdef HAVE_UNISTD_H # include #endif #include #include "goo/gmem.h" #include "goo/GooString.h" #include "SplashMath.h" #include "SplashFTFontEngine.h" #include "SplashFontFile.h" #include "SplashFontFileID.h" #include "SplashFont.h" #include "SplashFontEngine.h" //------------------------------------------------------------------------ // SplashFontEngine //------------------------------------------------------------------------ SplashFontEngine::SplashFontEngine(bool enableFreeType, bool enableFreeTypeHinting, bool enableSlightHinting, bool aa) { std::fill(fontCache.begin(), fontCache.end(), nullptr); if (enableFreeType) { ftEngine = SplashFTFontEngine::init(aa, enableFreeTypeHinting, enableSlightHinting); } else { ftEngine = nullptr; } } SplashFontEngine::~SplashFontEngine() { for (auto font : fontCache) { delete font; } if (ftEngine) { delete ftEngine; } } SplashFontFile *SplashFontEngine::getFontFile(SplashFontFileID *id) { for (auto font : fontCache) { if (font) { SplashFontFile *fontFile = font->getFontFile(); if (fontFile && fontFile->getID()->matches(id)) { return fontFile; } } } return nullptr; } SplashFontFile *SplashFontEngine::loadType1Font(SplashFontFileID *idA, SplashFontSrc *src, const char **enc) { SplashFontFile *fontFile = nullptr; if (ftEngine) { fontFile = ftEngine->loadType1Font(idA, src, enc); } // delete the (temporary) font file -- with Unix hard link // semantics, this will remove the last link; otherwise it will // return an error, leaving the file to be deleted later (if // loadXYZFont failed, the file will always be deleted) if (src->isFile) { src->unref(); } return fontFile; } SplashFontFile *SplashFontEngine::loadType1CFont(SplashFontFileID *idA, SplashFontSrc *src, const char **enc) { SplashFontFile *fontFile = nullptr; if (ftEngine) { fontFile = ftEngine->loadType1CFont(idA, src, enc); } // delete the (temporary) font file -- with Unix hard link // semantics, this will remove the last link; otherwise it will // return an error, leaving the file to be deleted later (if // loadXYZFont failed, the file will always be deleted) if (src->isFile) { src->unref(); } return fontFile; } SplashFontFile *SplashFontEngine::loadOpenTypeT1CFont(SplashFontFileID *idA, SplashFontSrc *src, const char **enc) { SplashFontFile *fontFile = nullptr; if (ftEngine) { fontFile = ftEngine->loadOpenTypeT1CFont(idA, src, enc); } // delete the (temporary) font file -- with Unix hard link // semantics, this will remove the last link; otherwise it will // return an error, leaving the file to be deleted later (if // loadXYZFont failed, the file will always be deleted) if (src->isFile) { src->unref(); } return fontFile; } SplashFontFile *SplashFontEngine::loadCIDFont(SplashFontFileID *idA, SplashFontSrc *src) { SplashFontFile *fontFile = nullptr; if (ftEngine) { fontFile = ftEngine->loadCIDFont(idA, src); } // delete the (temporary) font file -- with Unix hard link // semantics, this will remove the last link; otherwise it will // return an error, leaving the file to be deleted later (if // loadXYZFont failed, the file will always be deleted) if (src->isFile) { src->unref(); } return fontFile; } SplashFontFile *SplashFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA, SplashFontSrc *src, int *codeToGID, int codeToGIDLen) { SplashFontFile *fontFile = nullptr; if (ftEngine) { fontFile = ftEngine->loadOpenTypeCFFFont(idA, src, codeToGID, codeToGIDLen); } // delete the (temporary) font file -- with Unix hard link // semantics, this will remove the last link; otherwise it will // return an error, leaving the file to be deleted later (if // loadXYZFont failed, the file will always be deleted) if (src->isFile) { src->unref(); } return fontFile; } SplashFontFile *SplashFontEngine::loadTrueTypeFont(SplashFontFileID *idA, SplashFontSrc *src, int *codeToGID, int codeToGIDLen, int faceIndex) { SplashFontFile *fontFile = nullptr; if (ftEngine) { fontFile = ftEngine->loadTrueTypeFont(idA, src, codeToGID, codeToGIDLen, faceIndex); } if (!fontFile) { gfree(codeToGID); } // delete the (temporary) font file -- with Unix hard link // semantics, this will remove the last link; otherwise it will // return an error, leaving the file to be deleted later (if // loadXYZFont failed, the file will always be deleted) if (src->isFile) { src->unref(); } return fontFile; } bool SplashFontEngine::getAA() { return (ftEngine == nullptr) ? false : ftEngine->getAA(); } void SplashFontEngine::setAA(bool aa) { if (ftEngine != nullptr) { ftEngine->setAA(aa); } } SplashFont *SplashFontEngine::getFont(SplashFontFile *fontFile, const SplashCoord *textMat, const SplashCoord *ctm) { SplashCoord mat[4]; mat[0] = textMat[0] * ctm[0] + textMat[1] * ctm[2]; mat[1] = -(textMat[0] * ctm[1] + textMat[1] * ctm[3]); mat[2] = textMat[2] * ctm[0] + textMat[3] * ctm[2]; mat[3] = -(textMat[2] * ctm[1] + textMat[3] * ctm[3]); if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.01)) { // avoid a singular (or close-to-singular) matrix mat[0] = 0.01; mat[1] = 0; mat[2] = 0; mat[3] = 0.01; } // 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); }); // The requested font has been found in the cache if (fontIt != fontCache.end()) { std::rotate(fontCache.begin(), fontIt, fontIt + 1); return fontCache[0]; } // The requested font has not been found in the cache auto newFont = fontFile->makeFont(mat, textMat); if (fontCache.back()) { delete fontCache.back(); } std::rotate(fontCache.begin(), fontCache.end() - 1, fontCache.end()); fontCache[0] = newFont; return fontCache[0]; } poppler-24.02.0/splash/SplashFontEngine.h000066400000000000000000000065101455701731300202220ustar00rootroot00000000000000//======================================================================== // // SplashFontEngine.h // //======================================================================== //======================================================================== // // 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 Takashi Iwai // Copyright (C) 2009 Petr Gajdos // Copyright (C) 2009, 2011, 2018 Albert Astals Cid // Copyright (C) 2011 Andreas Hartmetz // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2017 Adrian Johnson // Copyright (C) 2018 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 // //======================================================================== #ifndef SPLASHFONTENGINE_H #define SPLASHFONTENGINE_H #include #include "SplashTypes.h" #include "poppler_private_export.h" class SplashT1FontEngine; class SplashFTFontEngine; class SplashDTFontEngine; class SplashDT4FontEngine; class SplashFontFile; class SplashFontFileID; class SplashFont; class SplashFontSrc; //------------------------------------------------------------------------ // SplashFontEngine //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT SplashFontEngine { public: // Create a font engine. SplashFontEngine(bool enableFreeType, bool enableFreeTypeHinting, bool enableSlightHinting, bool aa); ~SplashFontEngine(); SplashFontEngine(const SplashFontEngine &) = delete; SplashFontEngine &operator=(const SplashFontEngine &) = delete; // Get a font file from the cache. Returns NULL if there is no // matching entry in the cache. SplashFontFile *getFontFile(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); // 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 // matrix, mat = textMat * ctm: // [ mat[0] mat[1] ] // [ mat[2] mat[3] ] // specifies the font transform in PostScript style: // [x' y'] = [x y] * mat // Note that the Splash y axis points downward. SplashFont *getFont(SplashFontFile *fontFile, const SplashCoord *textMat, const SplashCoord *ctm); bool getAA(); void setAA(bool aa); private: std::array fontCache; SplashFTFontEngine *ftEngine; }; #endif poppler-24.02.0/splash/SplashFontFile.cc000066400000000000000000000041031455701731300200260ustar00rootroot00000000000000//======================================================================== // // SplashFontFile.cc // //======================================================================== //======================================================================== // // 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 Takashi Iwai // Copyright (C) 2008, 2022 Albert Astals Cid // Copyright (C) 2019 Christian Persch // Copyright (C) 2022 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 // //======================================================================== #include #include #ifdef HAVE_UNISTD_H # include #endif #include "goo/gmem.h" #include "goo/GooString.h" #include "SplashFontFile.h" #include "SplashFontFileID.h" //------------------------------------------------------------------------ // SplashFontFile //------------------------------------------------------------------------ SplashFontFile::SplashFontFile(SplashFontFileID *idA, SplashFontSrc *srcA) { id = idA; src = srcA; src->ref(); refCnt = 0; doAdjustMatrix = false; } SplashFontFile::~SplashFontFile() { src->unref(); delete id; } void SplashFontFile::incRefCnt() { ++refCnt; } void SplashFontFile::decRefCnt() { if (!--refCnt) { delete this; } } // SplashFontSrc::SplashFontSrc() { isFile = false; refcnt = 1; } SplashFontSrc::~SplashFontSrc() = default; void SplashFontSrc::ref() { refcnt++; } void SplashFontSrc::unref() { if (!--refcnt) { delete this; } } void SplashFontSrc::setFile(const std::string &file) { isFile = true; fileName = file; } void SplashFontSrc::setBuf(std::vector &&bufA) { isFile = false; buf = std::move(bufA); } poppler-24.02.0/splash/SplashFontFile.h000066400000000000000000000050021455701731300176670ustar00rootroot00000000000000//======================================================================== // // SplashFontFile.h // //======================================================================== //======================================================================== // // 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 Takashi Iwai // Copyright (C) 2008, 2010, 2018 Albert Astals Cid // Copyright (C) 2022 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 // //======================================================================== #ifndef SPLASHFONTFILE_H #define SPLASHFONTFILE_H #include #include #include "SplashTypes.h" #include "poppler_private_export.h" class SplashFontEngine; class SplashFont; class SplashFontFileID; //------------------------------------------------------------------------ // SplashFontFile //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT SplashFontSrc { public: SplashFontSrc(); SplashFontSrc(const SplashFontSrc &) = delete; SplashFontSrc &operator=(const SplashFontSrc &) = delete; void setFile(const std::string &file); void setBuf(char *bufA, int buflenA); void setBuf(std::vector &&bufA); void ref(); void unref(); bool isFile; std::string fileName; std::vector buf; private: ~SplashFontSrc(); int refcnt; }; class SplashFontFile { public: virtual ~SplashFontFile(); SplashFontFile(const SplashFontFile &) = delete; SplashFontFile &operator=(const SplashFontFile &) = delete; // Create a new SplashFont, i.e., a scaled instance of this font // file. virtual SplashFont *makeFont(SplashCoord *mat, const SplashCoord *textMat) = 0; // Get the font file ID. SplashFontFileID *getID() { return id; } // Increment the reference count. void incRefCnt(); // Decrement the reference count. If the new value is zero, delete // the SplashFontFile object. void decRefCnt(); bool doAdjustMatrix; protected: SplashFontFile(SplashFontFileID *idA, SplashFontSrc *srcA); SplashFontFileID *id; SplashFontSrc *src; int refCnt; friend class SplashFontEngine; }; #endif poppler-24.02.0/splash/SplashFontFileID.cc000066400000000000000000000007471455701731300202550ustar00rootroot00000000000000//======================================================================== // // SplashFontFileID.cc // //======================================================================== #include #include "SplashFontFileID.h" //------------------------------------------------------------------------ // SplashFontFileID //------------------------------------------------------------------------ SplashFontFileID::SplashFontFileID() { } SplashFontFileID::~SplashFontFileID() { } poppler-24.02.0/splash/SplashFontFileID.h000066400000000000000000000024141455701731300201100ustar00rootroot00000000000000//======================================================================== // // SplashFontFileID.h // //======================================================================== //======================================================================== // // 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) 2018 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 // //======================================================================== #ifndef SPLASHFONTFILEID_H #define SPLASHFONTFILEID_H #include "poppler_private_export.h" //------------------------------------------------------------------------ // SplashFontFileID //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT SplashFontFileID { public: SplashFontFileID(); virtual ~SplashFontFileID(); SplashFontFileID(const SplashFontFileID &) = delete; SplashFontFileID &operator=(const SplashFontFileID &) = delete; virtual bool matches(SplashFontFileID *id) = 0; }; #endif poppler-24.02.0/splash/SplashGlyphBitmap.h000066400000000000000000000012621455701731300204050ustar00rootroot00000000000000//======================================================================== // // SplashGlyphBitmap.h // //======================================================================== #ifndef SPLASHGLYPHBITMAP_H #define SPLASHGLYPHBITMAP_H //------------------------------------------------------------------------ // SplashGlyphBitmap //------------------------------------------------------------------------ struct SplashGlyphBitmap { int x, y, w, h; // offset and size of glyph bool aa; // anti-aliased: true means 8-bit alpha // bitmap; false means 1-bit unsigned char *data; // bitmap data bool freeData; // true if data memory should be freed }; #endif poppler-24.02.0/splash/SplashMath.h000066400000000000000000000142571455701731300170660ustar00rootroot00000000000000//======================================================================== // // SplashMath.h // //======================================================================== //======================================================================== // // 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) 2009-2011 Albert Astals Cid // Copyright (C) 2017 Adrian Johnson // Copyright (C) 2020 Jean Ghali // // 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 SPLASHMATH_H #define SPLASHMATH_H #include "poppler-config.h" #include #include "SplashTypes.h" static inline SplashCoord splashAbs(SplashCoord x) { #if defined(USE_FLOAT) return fabsf(x); #else return fabs(x); #endif } static inline int splashFloor(SplashCoord x) { #if defined(USE_FLOAT) return (int)floorf(x); #elif defined(__GNUC__) && defined(__i386__) // floor() and (int)() are implemented separately, which results // in changing the FPCW multiple times - so we optimize it with // some inline assembly unsigned short oldCW, newCW, t; int result; __asm__ volatile("fldl %4\n" "fnstcw %0\n" "movw %0, %3\n" "andw $0xf3ff, %3\n" "orw $0x0400, %3\n" "movw %3, %1\n" // round down "fldcw %1\n" "fistpl %2\n" "fldcw %0\n" : "=m"(oldCW), "=m"(newCW), "=m"(result), "=r"(t) : "m"(x)); return result; #elif defined(_WIN32) && defined(_M_IX86) // floor() and (int)() are implemented separately, which results // in changing the FPCW multiple times - so we optimize it with // some inline assembly unsigned short oldCW, newCW; int result; __asm fld QWORD PTR x; __asm fnstcw WORD PTR oldCW; __asm mov ax, WORD PTR oldCW; __asm and ax, 0xf3ff; __asm or ax, 0x0400; __asm mov WORD PTR newCW, ax; // round down __asm fldcw WORD PTR newCW; __asm fistp DWORD PTR result; __asm fldcw WORD PTR oldCW; return result; #else if (x > 0) { return (int)x; } else { return (int)floor(x); } #endif } static inline int splashCeil(SplashCoord x) { #if defined(USE_FLOAT) return (int)ceilf(x); #elif defined(__GNUC__) && defined(__i386__) // ceil() and (int)() are implemented separately, which results // in changing the FPCW multiple times - so we optimize it with // some inline assembly unsigned short oldCW, newCW, t; int result; __asm__ volatile("fldl %4\n" "fnstcw %0\n" "movw %0, %3\n" "andw $0xf3ff, %3\n" "orw $0x0800, %3\n" "movw %3, %1\n" // round up "fldcw %1\n" "fistpl %2\n" "fldcw %0\n" : "=m"(oldCW), "=m"(newCW), "=m"(result), "=r"(t) : "m"(x)); return result; #elif defined(_WIN32) && defined(_M_IX86) // ceil() and (int)() are implemented separately, which results // in changing the FPCW multiple times - so we optimize it with // some inline assembly unsigned short oldCW, newCW; int result; __asm fld QWORD PTR x; __asm fnstcw WORD PTR oldCW; __asm mov ax, WORD PTR oldCW; __asm and ax, 0xf3ff; __asm or ax, 0x0800; __asm mov WORD PTR newCW, ax; // round up __asm fldcw WORD PTR newCW; __asm fistp DWORD PTR result; __asm fldcw WORD PTR oldCW; return result; #else return (int)ceil(x); #endif } static inline int splashRound(SplashCoord x) { #if defined(__GNUC__) && defined(__i386__) // this could use round-to-nearest mode and avoid the "+0.5", // but that produces slightly different results (because i+0.5 // sometimes rounds up and sometimes down using the even rule) unsigned short oldCW, newCW, t; int result; x += 0.5; __asm__ volatile("fldl %4\n" "fnstcw %0\n" "movw %0, %3\n" "andw $0xf3ff, %3\n" "orw $0x0400, %3\n" "movw %3, %1\n" // round down "fldcw %1\n" "fistpl %2\n" "fldcw %0\n" : "=m"(oldCW), "=m"(newCW), "=m"(result), "=r"(t) : "m"(x)); return result; #elif defined(_WIN32) && defined(_M_IX86) // this could use round-to-nearest mode and avoid the "+0.5", // but that produces slightly different results (because i+0.5 // sometimes rounds up and sometimes down using the even rule) unsigned short oldCW, newCW; int result; x += 0.5; __asm fld QWORD PTR x; __asm fnstcw WORD PTR oldCW; __asm mov ax, WORD PTR oldCW; __asm and ax, 0xf3ff; __asm or ax, 0x0400; __asm mov WORD PTR newCW, ax; // round down __asm fldcw WORD PTR newCW; __asm fistp DWORD PTR result; __asm fldcw WORD PTR oldCW; return result; #else return (int)splashFloor(x + 0.5); #endif } static inline SplashCoord splashAvg(SplashCoord x, SplashCoord y) { return 0.5 * (x + y); } static inline SplashCoord splashSqrt(SplashCoord x) { #if defined(USE_FLOAT) return sqrtf(x); #else return sqrt(x); #endif } static inline SplashCoord splashPow(SplashCoord x, SplashCoord y) { #if defined(USE_FLOAT) return powf(x, y); #else return pow(x, y); #endif } static inline SplashCoord splashDist(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) { SplashCoord dx, dy; dx = x1 - x0; dy = y1 - y0; return splashSqrt(dx * dx + dy * dy); } static inline bool splashCheckDet(SplashCoord m11, SplashCoord m12, SplashCoord m21, SplashCoord m22, SplashCoord epsilon) { return fabs(m11 * m22 - m12 * m21) >= epsilon; } #endif poppler-24.02.0/splash/SplashPath.cc000066400000000000000000000132621455701731300172220ustar00rootroot00000000000000//======================================================================== // // SplashPath.cc // //======================================================================== //======================================================================== // // 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) 2018 Stefan Brüns // Copyright (C) 2018-2021 Albert Astals Cid // Copyright (C) 2018 Adam Reichold // // 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 #include #include "goo/gmem.h" #include "goo/GooLikely.h" #include "SplashErrorCodes.h" #include "SplashPath.h" //------------------------------------------------------------------------ // SplashPath //------------------------------------------------------------------------ // A path can be in three possible states: // // 1. no current point -- zero or more finished subpaths // [curSubpath == length] // // 2. one point in subpath // [curSubpath == length - 1] // // 3. open subpath with two or more points // [curSubpath < length - 1] SplashPath::SplashPath() { pts = nullptr; flags = nullptr; length = size = 0; curSubpath = 0; hints = nullptr; hintsLength = hintsSize = 0; } SplashPath::SplashPath(SplashPath &&path) noexcept { length = path.length; size = path.size; pts = path.pts; flags = path.flags; curSubpath = path.curSubpath; hints = path.hints; hintsLength = hintsSize = path.hintsLength; path.pts = nullptr; path.flags = nullptr; path.length = path.size = 0; path.hints = nullptr; path.hintsLength = path.hintsSize = 0; } SplashPath::~SplashPath() { gfree(pts); gfree(flags); gfree(hints); } void SplashPath::reserve(int nPts) { grow(nPts - size); } // Add space for more points. void SplashPath::grow(int nPts) { if (length + nPts > size) { if (size == 0) { size = 32; } while (size < length + nPts) { size *= 2; } pts = (SplashPathPoint *)greallocn_checkoverflow(pts, size, sizeof(SplashPathPoint)); flags = (unsigned char *)greallocn_checkoverflow(flags, size, sizeof(unsigned char)); if (unlikely(!pts || !flags)) { length = size = curSubpath = 0; } } } void SplashPath::append(SplashPath *path) { int i; grow(path->length); if (unlikely(size == 0)) { return; } curSubpath = length + path->curSubpath; for (i = 0; i < path->length; ++i) { pts[length] = path->pts[i]; flags[length] = path->flags[i]; ++length; } } SplashError SplashPath::moveTo(SplashCoord x, SplashCoord y) { if (onePointSubpath()) { return splashErrBogusPath; } grow(1); if (unlikely(size == 0)) { return splashErrBogusPath; } pts[length].x = x; pts[length].y = y; flags[length] = splashPathFirst | splashPathLast; curSubpath = length++; return splashOk; } SplashError SplashPath::lineTo(SplashCoord x, SplashCoord y) { if (noCurrentPoint()) { return splashErrNoCurPt; } flags[length - 1] &= ~splashPathLast; grow(1); if (unlikely(size == 0)) { return splashErrBogusPath; } pts[length].x = x; pts[length].y = y; flags[length] = splashPathLast; ++length; return splashOk; } SplashError SplashPath::curveTo(SplashCoord x1, SplashCoord y1, SplashCoord x2, SplashCoord y2, SplashCoord x3, SplashCoord y3) { if (noCurrentPoint()) { return splashErrNoCurPt; } flags[length - 1] &= ~splashPathLast; grow(3); if (unlikely(size == 0)) { return splashErrBogusPath; } pts[length].x = x1; pts[length].y = y1; flags[length] = splashPathCurve; ++length; pts[length].x = x2; pts[length].y = y2; flags[length] = splashPathCurve; ++length; pts[length].x = x3; pts[length].y = y3; flags[length] = splashPathLast; ++length; return splashOk; } SplashError SplashPath::close(bool force) { if (noCurrentPoint()) { return splashErrNoCurPt; } if (force || curSubpath == length - 1 || pts[length - 1].x != pts[curSubpath].x || pts[length - 1].y != pts[curSubpath].y) { const auto lineToStatus = lineTo(pts[curSubpath].x, pts[curSubpath].y); if (lineToStatus != splashOk) { return lineToStatus; } } flags[curSubpath] |= splashPathClosed; flags[length - 1] |= splashPathClosed; curSubpath = length; return splashOk; } void SplashPath::addStrokeAdjustHint(int ctrl0, int ctrl1, int firstPt, int lastPt) { if (hintsLength == hintsSize) { hintsSize = hintsLength ? 2 * hintsLength : 8; hints = (SplashPathHint *)greallocn_checkoverflow(hints, hintsSize, sizeof(SplashPathHint)); } if (unlikely(!hints)) { return; } hints[hintsLength].ctrl0 = ctrl0; hints[hintsLength].ctrl1 = ctrl1; hints[hintsLength].firstPt = firstPt; hints[hintsLength].lastPt = lastPt; ++hintsLength; } void SplashPath::offset(SplashCoord dx, SplashCoord dy) { int i; for (i = 0; i < length; ++i) { pts[i].x += dx; pts[i].y += dy; } } bool SplashPath::getCurPt(SplashCoord *x, SplashCoord *y) { if (noCurrentPoint()) { return false; } *x = pts[length - 1].x; *y = pts[length - 1].y; return true; } poppler-24.02.0/splash/SplashPath.h000066400000000000000000000104071455701731300170620ustar00rootroot00000000000000//======================================================================== // // SplashPath.h // //======================================================================== //======================================================================== // // 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) 2018, 2019, 2021 Albert Astals Cid // Copyright (C) 2018 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 // //======================================================================== #ifndef SPLASHPATH_H #define SPLASHPATH_H #include "SplashTypes.h" #include "poppler_private_export.h" //------------------------------------------------------------------------ // SplashPathPoint //------------------------------------------------------------------------ struct SplashPathPoint { SplashCoord x, y; }; //------------------------------------------------------------------------ // SplashPath.flags //------------------------------------------------------------------------ // first point on each subpath sets this flag #define splashPathFirst 0x01 // last point on each subpath sets this flag #define splashPathLast 0x02 // if the subpath is closed, its first and last points must be // identical, and must set this flag #define splashPathClosed 0x04 // curve control points set this flag #define splashPathCurve 0x08 //------------------------------------------------------------------------ // SplashPathHint //------------------------------------------------------------------------ struct SplashPathHint { int ctrl0, ctrl1; int firstPt, lastPt; }; //------------------------------------------------------------------------ // SplashPath //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT SplashPath { public: // Create an empty path. SplashPath(); ~SplashPath(); SplashPath(const SplashPath &) = delete; SplashPath &operator=(const SplashPath &) = delete; SplashPath(SplashPath &&path) noexcept; // Append to . void append(SplashPath *path); // Start a new subpath. SplashError moveTo(SplashCoord x, SplashCoord y); // Add a line segment to the last subpath. SplashError lineTo(SplashCoord x, SplashCoord y); // Add a third-order (cubic) Bezier curve segment to the last // subpath. SplashError curveTo(SplashCoord x1, SplashCoord y1, SplashCoord x2, SplashCoord y2, SplashCoord x3, SplashCoord y3); // Close the last subpath, adding a line segment if necessary. If // is true, this adds a line segment even if the current // point is equal to the first point in the subpath. SplashError close(bool force = false); // Add a stroke adjustment hint. The controlling segments are // and (where segments are identified by their first // point), and the points to be adjusted are .. . void addStrokeAdjustHint(int ctrl0, int ctrl1, int firstPt, int lastPt); // Add (, ) to every point on this path. void offset(SplashCoord dx, SplashCoord dy); // Get the points on the path. int getLength() { return length; } void getPoint(int i, double *x, double *y, unsigned char *f) { *x = pts[i].x; *y = pts[i].y; *f = flags[i]; } // Get the current point. bool getCurPt(SplashCoord *x, SplashCoord *y); // Reserve space for at least n points void reserve(int n); protected: void grow(int nPts); bool noCurrentPoint() { return curSubpath == length; } bool onePointSubpath() { return curSubpath == length - 1; } bool openSubpath() { return curSubpath < length - 1; } SplashPathPoint *pts; // array of points unsigned char *flags; // array of flags int length, size; // length/size of the pts and flags arrays int curSubpath; // index of first point in last subpath SplashPathHint *hints; // list of hints int hintsLength, hintsSize; friend class SplashXPath; friend class Splash; }; #endif poppler-24.02.0/splash/SplashPattern.cc000066400000000000000000000034111455701731300177360ustar00rootroot00000000000000//======================================================================== // // SplashPattern.cc // //======================================================================== //======================================================================== // // 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) 2010, 2011 Thomas Freitag // Copyright (C) 2020, 2021 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 #include "SplashMath.h" #include "SplashScreen.h" #include "SplashPattern.h" //------------------------------------------------------------------------ // SplashPattern //------------------------------------------------------------------------ SplashPattern::SplashPattern() { } SplashPattern::~SplashPattern() { } //------------------------------------------------------------------------ // SplashSolidColor //------------------------------------------------------------------------ SplashSolidColor::SplashSolidColor(SplashColorConstPtr colorA) { splashColorCopy(color, colorA); } SplashSolidColor::~SplashSolidColor() { } bool SplashSolidColor::getColor(int x, int y, SplashColorPtr c) { splashColorCopy(c, color); return true; } //------------------------------------------------------------------------ // SplashGouraudColor //------------------------------------------------------------------------ SplashGouraudColor::~SplashGouraudColor() = default; poppler-24.02.0/splash/SplashPattern.h000066400000000000000000000064761455701731300176160ustar00rootroot00000000000000//======================================================================== // // SplashPattern.h // //======================================================================== //======================================================================== // // 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) 2010, 2011, 2014 Thomas Freitag // Copyright (C) 2018, 2020, 2021 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 // //======================================================================== #ifndef SPLASHPATTERN_H #define SPLASHPATTERN_H #include "SplashTypes.h" #include "poppler_private_export.h" class SplashScreen; //------------------------------------------------------------------------ // SplashPattern //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT SplashPattern { public: SplashPattern(); virtual SplashPattern *copy() const = 0; virtual ~SplashPattern(); SplashPattern(const SplashPattern &) = delete; SplashPattern &operator=(const SplashPattern &) = delete; // Return the color value for a specific pixel. virtual bool getColor(int x, int y, SplashColorPtr c) = 0; // Test if x,y-position is inside pattern. virtual bool testPosition(int x, int y) = 0; // Returns true if this pattern object will return the same color // value for all pixels. virtual bool isStatic() = 0; // Returns true if this pattern colorspace is CMYK. virtual bool isCMYK() = 0; private: }; //------------------------------------------------------------------------ // SplashSolidColor //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT SplashSolidColor : public SplashPattern { public: explicit SplashSolidColor(SplashColorConstPtr colorA); SplashPattern *copy() const override { return new SplashSolidColor(color); } ~SplashSolidColor() override; bool getColor(int x, int y, SplashColorPtr c) override; bool testPosition(int x, int y) override { return false; } bool isStatic() override { return true; } bool isCMYK() override { return false; } private: SplashColor color; }; //------------------------------------------------------------------------ // SplashGouraudColor (needed for gouraudTriangleShadedFill) //------------------------------------------------------------------------ class SplashGouraudColor : public SplashPattern { public: ~SplashGouraudColor() override; virtual bool isParameterized() = 0; virtual int getNTriangles() = 0; virtual void getParametrizedTriangle(int i, double *x0, double *y0, double *color0, double *x1, double *y1, double *color1, double *x2, double *y2, double *color2) = 0; virtual void getNonParametrizedTriangle(int i, SplashColorMode mode, double *x0, double *y0, SplashColorPtr color0, double *x1, double *y1, SplashColorPtr color1, double *x2, double *y2, SplashColorPtr color2) = 0; virtual void getParameterizedColor(double t, SplashColorMode mode, SplashColorPtr c) = 0; }; #endif poppler-24.02.0/splash/SplashScreen.cc000066400000000000000000000266111455701731300175470ustar00rootroot00000000000000//======================================================================== // // SplashScreen.cc // //======================================================================== //======================================================================== // // 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) 2009, 2016, 2018, 2020, 2021 Albert Astals Cid // Copyright (C) 2012 Fabio D'Urso // // 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 #include #include #include #include "goo/gmem.h" #include "goo/grandom.h" #include "goo/GooLikely.h" #include "SplashMath.h" #include "SplashScreen.h" static const SplashScreenParams defaultParams = { splashScreenDispersed, // type 2, // size 2, // dotRadius 1.0, // gamma 0.0, // blackThreshold 1.0 // whiteThreshold }; //------------------------------------------------------------------------ struct SplashScreenPoint { int x, y; int dist; }; struct cmpDistancesFunctor { bool operator()(const SplashScreenPoint p0, const SplashScreenPoint p1) { return p0.dist < p1.dist; } }; //------------------------------------------------------------------------ // SplashScreen //------------------------------------------------------------------------ // If is true, this generates a 45 degree screen using a // circular dot spot function. DPI = resolution / ((size / 2) * // sqrt(2)). If is false, this generates an optimal // threshold matrix using recursive tesselation. Gamma correction // (gamma = 1 / 1.33) is also computed here. SplashScreen::SplashScreen(const SplashScreenParams *params) { if (!params) { params = &defaultParams; } screenParams = params; mat = nullptr; size = 0; maxVal = 0; minVal = 0; } void SplashScreen::createMatrix() { unsigned char u; int black, white, i; const SplashScreenParams *params = screenParams; // size must be a power of 2, and at least 2 for (size = 2, log2Size = 1; size < params->size; size <<= 1, ++log2Size) { ; } switch (params->type) { case splashScreenDispersed: mat = (unsigned char *)gmallocn(size * size, sizeof(unsigned char)); buildDispersedMatrix(size / 2, size / 2, 1, size / 2, 1); break; case splashScreenClustered: mat = (unsigned char *)gmallocn(size * size, sizeof(unsigned char)); buildClusteredMatrix(); break; case splashScreenStochasticClustered: // size must be at least 2*r while (size < (params->dotRadius << 1)) { size <<= 1; ++log2Size; } mat = (unsigned char *)gmallocn(size * size, sizeof(unsigned char)); buildSCDMatrix(params->dotRadius); break; } sizeM1 = size - 1; // do gamma correction and compute minVal/maxVal minVal = 255; maxVal = 0; black = splashRound((SplashCoord)255.0 * params->blackThreshold); if (black < 1) { black = 1; } int whiteAux = splashRound((SplashCoord)255.0 * params->whiteThreshold); if (whiteAux > 255) { white = 255; } else { white = whiteAux; } for (i = 0; i < size * size; ++i) { u = splashRound((SplashCoord)255.0 * splashPow((SplashCoord)mat[i] / 255.0, params->gamma)); if (u < black) { u = (unsigned char)black; } else if (u >= white) { u = (unsigned char)white; } mat[i] = u; if (u < minVal) { minVal = u; } else if (u > maxVal) { maxVal = u; } } } void SplashScreen::buildDispersedMatrix(int i, int j, int val, int delta, int offset) { if (delta == 0) { // map values in [1, size^2] --> [1, 255] mat[(i << log2Size) + j] = 1 + (254 * (val - 1)) / (size * size - 1); } else { buildDispersedMatrix(i, j, val, delta / 2, 4 * offset); buildDispersedMatrix((i + delta) % size, (j + delta) % size, val + offset, delta / 2, 4 * offset); buildDispersedMatrix((i + delta) % size, j, val + 2 * offset, delta / 2, 4 * offset); buildDispersedMatrix((i + 2 * delta) % size, (j + delta) % size, val + 3 * offset, delta / 2, 4 * offset); } } void SplashScreen::buildClusteredMatrix() { SplashCoord *dist; SplashCoord u, v, d; unsigned char val; int size2, x, y, x1, y1, i; size2 = size >> 1; // initialize the threshold matrix for (y = 0; y < size; ++y) { for (x = 0; x < size; ++x) { mat[(y << log2Size) + x] = 0; } } // build the distance matrix dist = (SplashCoord *)gmallocn(size * size2, sizeof(SplashCoord)); for (y = 0; y < size2; ++y) { for (x = 0; x < size2; ++x) { if (x + y < size2 - 1) { u = (SplashCoord)x + 0.5 - 0; v = (SplashCoord)y + 0.5 - 0; } else { u = (SplashCoord)x + 0.5 - (SplashCoord)size2; v = (SplashCoord)y + 0.5 - (SplashCoord)size2; } dist[y * size2 + x] = u * u + v * v; } } for (y = 0; y < size2; ++y) { for (x = 0; x < size2; ++x) { if (x < y) { u = (SplashCoord)x + 0.5 - 0; v = (SplashCoord)y + 0.5 - (SplashCoord)size2; } else { u = (SplashCoord)x + 0.5 - (SplashCoord)size2; v = (SplashCoord)y + 0.5 - 0; } dist[(size2 + y) * size2 + x] = u * u + v * v; } } // build the threshold matrix x1 = y1 = 0; // make gcc happy for (i = 0; i < size * size2; ++i) { d = -1; for (y = 0; y < size; ++y) { for (x = 0; x < size2; ++x) { if (mat[(y << log2Size) + x] == 0 && dist[y * size2 + x] > d) { x1 = x; y1 = y; d = dist[y1 * size2 + x1]; } } } // map values in [0, 2*size*size2-1] --> [1, 255] val = 1 + (254 * (2 * i)) / (2 * size * size2 - 1); mat[(y1 << log2Size) + x1] = val; val = 1 + (254 * (2 * i + 1)) / (2 * size * size2 - 1); if (y1 < size2) { mat[((y1 + size2) << log2Size) + x1 + size2] = val; } else { mat[((y1 - size2) << log2Size) + x1 + size2] = val; } } gfree(dist); } // Compute the distance between two points on a toroid. int SplashScreen::distance(int x0, int y0, int x1, int y1) { int dx0, dx1, dx, dy0, dy1, dy; dx0 = abs(x0 - x1); dx1 = size - dx0; dx = dx0 < dx1 ? dx0 : dx1; dy0 = abs(y0 - y1); dy1 = size - dy0; dy = dy0 < dy1 ? dy0 : dy1; return dx * dx + dy * dy; } // Algorithm taken from: // Victor Ostromoukhov and Roger D. Hersch, "Stochastic Clustered-Dot // Dithering" in Color Imaging: Device-Independent Color, Color // Hardcopy, and Graphic Arts IV, SPIE Vol. 3648, pp. 496-505, 1999. void SplashScreen::buildSCDMatrix(int r) { SplashScreenPoint *dots, *pts; int dotsLen, dotsSize; char *tmpl; char *grid; int *region, *dist; int x, y, xx, yy, x0, x1, y0, y1, i, j, d, iMin, dMin, n; // generate the random space-filling curve pts = (SplashScreenPoint *)gmallocn(size * size, sizeof(SplashScreenPoint)); i = 0; for (y = 0; y < size; ++y) { for (x = 0; x < size; ++x) { pts[i].x = x; pts[i].y = y; ++i; } } for (i = 0; i < size * size; ++i) { j = i + (int)((double)(size * size - i) * grandom_double()); x = pts[i].x; y = pts[i].y; pts[i].x = pts[j].x; pts[i].y = pts[j].y; pts[j].x = x; pts[j].y = y; } // construct the circle template tmpl = (char *)gmallocn((r + 1) * (r + 1), sizeof(char)); for (y = 0; y <= r; ++y) { for (x = 0; x <= r; ++x) { tmpl[y * (r + 1) + x] = (x * y <= r * r) ? 1 : 0; } } // mark all grid cells as free grid = (char *)gmallocn(size * size, sizeof(char)); for (y = 0; y < size; ++y) { for (x = 0; x < size; ++x) { grid[(y << log2Size) + x] = 0; } } // walk the space-filling curve, adding dots dotsLen = 0; dotsSize = 32; dots = (SplashScreenPoint *)gmallocn(dotsSize, sizeof(SplashScreenPoint)); for (i = 0; i < size * size; ++i) { x = pts[i].x; y = pts[i].y; if (!grid[(y << log2Size) + x]) { if (dotsLen == dotsSize) { dotsSize *= 2; dots = (SplashScreenPoint *)greallocn(dots, dotsSize, sizeof(SplashScreenPoint)); } dots[dotsLen++] = pts[i]; for (yy = 0; yy <= r; ++yy) { y0 = (y + yy) % size; y1 = (y - yy + size) % size; for (xx = 0; xx <= r; ++xx) { if (tmpl[yy * (r + 1) + xx]) { x0 = (x + xx) % size; x1 = (x - xx + size) % size; grid[(y0 << log2Size) + x0] = 1; grid[(y0 << log2Size) + x1] = 1; grid[(y1 << log2Size) + x0] = 1; grid[(y1 << log2Size) + x1] = 1; } } } } } gfree(tmpl); gfree(grid); // assign each cell to a dot, compute distance to center of dot region = (int *)gmallocn(size * size, sizeof(int)); dist = (int *)gmallocn(size * size, sizeof(int)); for (y = 0; y < size; ++y) { for (x = 0; x < size; ++x) { iMin = 0; dMin = distance(dots[0].x, dots[0].y, x, y); for (i = 1; i < dotsLen; ++i) { d = distance(dots[i].x, dots[i].y, x, y); if (d < dMin) { iMin = i; dMin = d; } } region[(y << log2Size) + x] = iMin; dist[(y << log2Size) + x] = dMin; } } // compute threshold values for (i = 0; i < dotsLen; ++i) { n = 0; for (y = 0; y < size; ++y) { for (x = 0; x < size; ++x) { if (region[(y << log2Size) + x] == i) { pts[n].x = x; pts[n].y = y; pts[n].dist = distance(dots[i].x, dots[i].y, x, y); ++n; } } } std::sort(pts, pts + n, cmpDistancesFunctor()); for (j = 0; j < n; ++j) { // map values in [0 .. n-1] --> [255 .. 1] mat[(pts[j].y << log2Size) + pts[j].x] = 255 - (254 * j) / (n - 1); } } gfree(pts); gfree(region); gfree(dist); gfree(dots); } SplashScreen::SplashScreen(const SplashScreen *screen) { screenParams = screen->screenParams; size = screen->size; sizeM1 = screen->sizeM1; log2Size = screen->log2Size; mat = (unsigned char *)gmallocn(size * size, sizeof(unsigned char)); if (likely(mat != nullptr)) { memcpy(mat, screen->mat, size * size * sizeof(unsigned char)); } minVal = screen->minVal; maxVal = screen->maxVal; } SplashScreen::~SplashScreen() { gfree(mat); } poppler-24.02.0/splash/SplashScreen.h000066400000000000000000000052661455701731300174140ustar00rootroot00000000000000//======================================================================== // // SplashScreen.h // //======================================================================== //======================================================================== // // 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) 2009, 2018, 2020, 2021 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 // //======================================================================== #ifndef SPLASHSCREEN_H #define SPLASHSCREEN_H #include "SplashTypes.h" #include //------------------------------------------------------------------------ // SplashScreen //------------------------------------------------------------------------ class SplashScreen { public: explicit SplashScreen(const SplashScreenParams *params); explicit SplashScreen(const SplashScreen *screen); ~SplashScreen(); SplashScreen(const SplashScreen &) = delete; SplashScreen &operator=(const SplashScreen &) = delete; SplashScreen *copy() const { return new SplashScreen(this); } // Return the computed pixel value (0=black, 1=white) for the gray // level at (, ). int test(int x, int y, unsigned char value) { int xx, yy; if (mat == nullptr) { createMatrix(); } xx = x & sizeM1; yy = y & sizeM1; return value < mat[(yy << log2Size) + xx] ? 0 : 1; } // Returns true if value is above the white threshold or below the // black threshold, i.e., if the corresponding halftone will be // solid white or black. bool isStatic(unsigned char value) { if (mat == nullptr) { createMatrix(); } return value < minVal || value >= maxVal; } private: void createMatrix(); void buildDispersedMatrix(int i, int j, int val, int delta, int offset); void buildClusteredMatrix(); int distance(int x0, int y0, int x1, int y1); void buildSCDMatrix(int r); const SplashScreenParams *screenParams; // params to create the other members unsigned char *mat; // threshold matrix int size; // size of the threshold matrix int sizeM1; // size - 1 int log2Size; // log2(size) unsigned char minVal; // any pixel value below minVal generates // solid black unsigned char maxVal; // any pixel value above maxVal generates // solid white }; #endif poppler-24.02.0/splash/SplashState.cc000066400000000000000000000174271455701731300174150ustar00rootroot00000000000000//======================================================================== // // SplashState.cc // //======================================================================== //======================================================================== // // 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) 2009, 2011, 2012, 2015 Thomas Freitag // Copyright (C) 2017 Adrian Johnson // Copyright (C) 2019, 2021, 2022 Albert Astals Cid // Copyright (C) 2020 Peter Wang // // 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 #include #include "goo/gmem.h" #include "SplashPattern.h" #include "SplashScreen.h" #include "SplashClip.h" #include "SplashBitmap.h" #include "SplashState.h" //------------------------------------------------------------------------ // SplashState //------------------------------------------------------------------------ // number of components in each color mode int splashColorModeNComps[] = { 1, 1, 3, 3, 4, 4, 4 + SPOT_NCOMPS }; SplashState::SplashState(int width, int height, bool vectorAntialias, SplashScreenParams *screenParams) { SplashColor color; int i; matrix[0] = 1; matrix[1] = 0; matrix[2] = 0; matrix[3] = 1; matrix[4] = 0; matrix[5] = 0; memset(&color, 0, sizeof(SplashColor)); strokePattern = new SplashSolidColor(color); fillPattern = new SplashSolidColor(color); screen = new SplashScreen(screenParams); blendFunc = nullptr; strokeAlpha = 1; fillAlpha = 1; multiplyPatternAlpha = false; patternStrokeAlpha = 1; patternFillAlpha = 1; lineWidth = 1; lineCap = splashLineCapButt; lineJoin = splashLineJoinMiter; miterLimit = 10; flatness = 1; lineDashPhase = 0; strokeAdjust = false; clip = new SplashClip(0, 0, width - 0.001, height - 0.001, vectorAntialias); softMask = nullptr; deleteSoftMask = false; inNonIsolatedGroup = false; fillOverprint = false; strokeOverprint = false; overprintMode = 0; for (i = 0; i < 256; ++i) { rgbTransferR[i] = (unsigned char)i; rgbTransferG[i] = (unsigned char)i; rgbTransferB[i] = (unsigned char)i; grayTransfer[i] = (unsigned char)i; cmykTransferC[i] = (unsigned char)i; cmykTransferM[i] = (unsigned char)i; cmykTransferY[i] = (unsigned char)i; cmykTransferK[i] = (unsigned char)i; for (auto &cp : deviceNTransfer) { cp[i] = (unsigned char)i; } } overprintMask = 0xffffffff; overprintAdditive = false; next = nullptr; } SplashState::SplashState(int width, int height, bool vectorAntialias, SplashScreen *screenA) { SplashColor color; int i; matrix[0] = 1; matrix[1] = 0; matrix[2] = 0; matrix[3] = 1; matrix[4] = 0; matrix[5] = 0; memset(&color, 0, sizeof(SplashColor)); strokePattern = new SplashSolidColor(color); fillPattern = new SplashSolidColor(color); screen = screenA->copy(); blendFunc = nullptr; strokeAlpha = 1; fillAlpha = 1; multiplyPatternAlpha = false; patternStrokeAlpha = 1; patternFillAlpha = 1; lineWidth = 1; lineCap = splashLineCapButt; lineJoin = splashLineJoinMiter; miterLimit = 10; flatness = 1; lineDashPhase = 0; strokeAdjust = false; clip = new SplashClip(0, 0, width - 0.001, height - 0.001, vectorAntialias); softMask = nullptr; deleteSoftMask = false; inNonIsolatedGroup = false; fillOverprint = false; strokeOverprint = false; overprintMode = 0; for (i = 0; i < 256; ++i) { rgbTransferR[i] = (unsigned char)i; rgbTransferG[i] = (unsigned char)i; rgbTransferB[i] = (unsigned char)i; grayTransfer[i] = (unsigned char)i; cmykTransferC[i] = (unsigned char)i; cmykTransferM[i] = (unsigned char)i; cmykTransferY[i] = (unsigned char)i; cmykTransferK[i] = (unsigned char)i; for (auto &cp : deviceNTransfer) { cp[i] = (unsigned char)i; } } overprintMask = 0xffffffff; overprintAdditive = false; next = nullptr; } SplashState::SplashState(const SplashState *state) { memcpy(matrix, state->matrix, 6 * sizeof(SplashCoord)); strokePattern = state->strokePattern->copy(); fillPattern = state->fillPattern->copy(); screen = state->screen->copy(); blendFunc = state->blendFunc; strokeAlpha = state->strokeAlpha; fillAlpha = state->fillAlpha; multiplyPatternAlpha = state->multiplyPatternAlpha; patternStrokeAlpha = state->patternStrokeAlpha; patternFillAlpha = state->patternFillAlpha; lineWidth = state->lineWidth; lineCap = state->lineCap; lineJoin = state->lineJoin; miterLimit = state->miterLimit; flatness = state->flatness; lineDash = state->lineDash; lineDashPhase = state->lineDashPhase; strokeAdjust = state->strokeAdjust; clip = state->clip->copy(); softMask = state->softMask; deleteSoftMask = false; inNonIsolatedGroup = state->inNonIsolatedGroup; fillOverprint = state->fillOverprint; strokeOverprint = state->strokeOverprint; overprintMode = state->overprintMode; memcpy(rgbTransferR, state->rgbTransferR, 256); memcpy(rgbTransferG, state->rgbTransferG, 256); memcpy(rgbTransferB, state->rgbTransferB, 256); memcpy(grayTransfer, state->grayTransfer, 256); memcpy(cmykTransferC, state->cmykTransferC, 256); memcpy(cmykTransferM, state->cmykTransferM, 256); memcpy(cmykTransferY, state->cmykTransferY, 256); memcpy(cmykTransferK, state->cmykTransferK, 256); for (int cp = 0; cp < SPOT_NCOMPS + 4; cp++) { memcpy(deviceNTransfer[cp], state->deviceNTransfer[cp], 256); } overprintMask = state->overprintMask; overprintAdditive = state->overprintAdditive; next = nullptr; } SplashState::~SplashState() { delete strokePattern; delete fillPattern; delete screen; delete clip; if (deleteSoftMask && softMask) { delete softMask; } } void SplashState::setStrokePattern(SplashPattern *strokePatternA) { delete strokePattern; strokePattern = strokePatternA; } void SplashState::setFillPattern(SplashPattern *fillPatternA) { delete fillPattern; fillPattern = fillPatternA; } void SplashState::setScreen(SplashScreen *screenA) { delete screen; screen = screenA; } void SplashState::setLineDash(std::vector &&lineDashA, SplashCoord lineDashPhaseA) { lineDash = lineDashA; lineDashPhase = lineDashPhaseA; } void SplashState::setSoftMask(SplashBitmap *softMaskA) { if (deleteSoftMask) { delete softMask; } softMask = softMaskA; deleteSoftMask = true; } void SplashState::setTransfer(unsigned char *red, unsigned char *green, unsigned char *blue, unsigned char *gray) { for (int i = 0; i < 256; ++i) { cmykTransferC[i] = 255 - rgbTransferR[255 - i]; cmykTransferM[i] = 255 - rgbTransferG[255 - i]; cmykTransferY[i] = 255 - rgbTransferB[255 - i]; cmykTransferK[i] = 255 - grayTransfer[255 - i]; } for (int i = 0; i < 256; ++i) { deviceNTransfer[0][i] = 255 - rgbTransferR[255 - i]; deviceNTransfer[1][i] = 255 - rgbTransferG[255 - i]; deviceNTransfer[2][i] = 255 - rgbTransferB[255 - i]; deviceNTransfer[3][i] = 255 - grayTransfer[255 - i]; } memcpy(rgbTransferR, red, 256); memcpy(rgbTransferG, green, 256); memcpy(rgbTransferB, blue, 256); memcpy(grayTransfer, gray, 256); } poppler-24.02.0/splash/SplashState.h000066400000000000000000000104101455701731300172400ustar00rootroot00000000000000//======================================================================== // // SplashState.h // //======================================================================== //======================================================================== // // 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) 2011, 2012, 2015 Thomas Freitag // Copyright (C) 2017 Adrian Johnson // Copyright (C) 2018, 2021, 2022 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 // //======================================================================== #ifndef SPLASHSTATE_H #define SPLASHSTATE_H #include "SplashTypes.h" class SplashPattern; class SplashScreen; class SplashClip; class SplashBitmap; //------------------------------------------------------------------------ // line cap values //------------------------------------------------------------------------ #define splashLineCapButt 0 #define splashLineCapRound 1 #define splashLineCapProjecting 2 //------------------------------------------------------------------------ // line join values //------------------------------------------------------------------------ #define splashLineJoinMiter 0 #define splashLineJoinRound 1 #define splashLineJoinBevel 2 //------------------------------------------------------------------------ // SplashState //------------------------------------------------------------------------ class SplashState { public: // Create a new state object, initialized with default settings. SplashState(int width, int height, bool vectorAntialias, SplashScreenParams *screenParams); SplashState(int width, int height, bool vectorAntialias, SplashScreen *screenA); // Copy a state object. SplashState *copy() const { return new SplashState(this); } ~SplashState(); SplashState(const SplashState &) = delete; SplashState &operator=(const SplashState &) = delete; // Set the stroke pattern. This does not copy . void setStrokePattern(SplashPattern *strokePatternA); // Set the fill pattern. This does not copy . void setFillPattern(SplashPattern *fillPatternA); // Set the screen. This does not copy . void setScreen(SplashScreen *screenA); // Set the line dash pattern. void setLineDash(std::vector &&lineDashA, SplashCoord lineDashPhaseA); // Set the soft mask bitmap. void setSoftMask(SplashBitmap *softMaskA); // Set the overprint parametes. void setFillOverprint(bool fillOverprintA) { fillOverprint = fillOverprintA; } void setStrokeOverprint(bool strokeOverprintA) { strokeOverprint = strokeOverprintA; } void setOverprintMode(int overprintModeA) { overprintMode = overprintModeA; } // Set the transfer function. void setTransfer(unsigned char *red, unsigned char *green, unsigned char *blue, unsigned char *gray); private: explicit SplashState(const SplashState *state); SplashCoord matrix[6]; SplashPattern *strokePattern; SplashPattern *fillPattern; SplashScreen *screen; SplashBlendFunc blendFunc; SplashCoord strokeAlpha; SplashCoord fillAlpha; bool multiplyPatternAlpha; SplashCoord patternStrokeAlpha; SplashCoord patternFillAlpha; SplashCoord lineWidth; int lineCap; int lineJoin; SplashCoord miterLimit; SplashCoord flatness; std::vector lineDash; SplashCoord lineDashPhase; bool strokeAdjust; SplashClip *clip; SplashBitmap *softMask; bool deleteSoftMask; bool inNonIsolatedGroup; bool fillOverprint; bool strokeOverprint; int overprintMode; unsigned char rgbTransferR[256], rgbTransferG[256], rgbTransferB[256]; unsigned char grayTransfer[256]; unsigned char cmykTransferC[256], cmykTransferM[256], cmykTransferY[256], cmykTransferK[256]; unsigned char deviceNTransfer[SPOT_NCOMPS + 4][256]; unsigned int overprintMask; bool overprintAdditive; SplashState *next; // used by Splash class friend class Splash; }; #endif poppler-24.02.0/splash/SplashTypes.h000066400000000000000000000154321455701731300172750ustar00rootroot00000000000000//======================================================================== // // SplashTypes.h // //======================================================================== //======================================================================== // // 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, 2010, 2019, 2020 Albert Astals Cid // Copyright (C) 2008 Tomas Are Haavet // Copyright (C) 2009, 2011-2013 Thomas Freitag // Copyright (C) 2009 Stefan Thomas // Copyright (C) 2010 William Bader // Copyright (C) 2017 Adrian Johnson // Copyright (C) 2018 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 // //======================================================================== #ifndef SPLASHTYPES_H #define SPLASHTYPES_H #include //------------------------------------------------------------------------ // coordinates //------------------------------------------------------------------------ #if defined(USE_FLOAT) typedef float SplashCoord; #else typedef double SplashCoord; #endif //------------------------------------------------------------------------ // antialiasing //------------------------------------------------------------------------ #define splashAASize 4 #ifndef SPOT_NCOMPS # define SPOT_NCOMPS 4 #endif //------------------------------------------------------------------------ // colors //------------------------------------------------------------------------ enum SplashColorMode { splashModeMono1, // 1 bit per component, 8 pixels per byte, // MSbit is on the left splashModeMono8, // 1 byte per component, 1 byte per pixel splashModeRGB8, // 1 byte per component, 3 bytes per pixel: // RGBRGB... splashModeBGR8, // 1 byte per component, 3 bytes per pixel: // BGRBGR... splashModeXBGR8, // 1 byte per component, 4 bytes per pixel: // XBGRXBGR... splashModeCMYK8, // 1 byte per component, 4 bytes per pixel: // CMYKCMYK... splashModeDeviceN8 // 1 byte per component, // 4 bytes + n bytes spot colors per pixel: // CMYKSSSSCMYKSSSS... }; enum SplashThinLineMode { splashThinLineDefault, // if SA on: draw solid if requested line width, transformed into // device space, is less than half a pixel and a shaped line else splashThinLineSolid, // draw line solid at least with 1 pixel splashThinLineShape // draw line shaped at least with 1 pixel }; // number of components in each color mode // (defined in SplashState.cc) extern int splashColorModeNComps[]; // max number of components in any SplashColor constexpr std::size_t splashMaxColorComps = SPOT_NCOMPS + 4; typedef unsigned char SplashColor[splashMaxColorComps]; typedef unsigned char *SplashColorPtr; typedef const unsigned char *SplashColorConstPtr; // RGB8 static inline unsigned char splashRGB8R(SplashColorPtr rgb8) { return rgb8[0]; } static inline unsigned char splashRGB8G(SplashColorPtr rgb8) { return rgb8[1]; } static inline unsigned char splashRGB8B(SplashColorPtr rgb8) { return rgb8[2]; } // BGR8 static inline unsigned char splashBGR8R(SplashColorPtr bgr8) { return bgr8[2]; } static inline unsigned char splashBGR8G(SplashColorPtr bgr8) { return bgr8[1]; } static inline unsigned char splashBGR8B(SplashColorPtr bgr8) { return bgr8[0]; } // CMYK8 static inline unsigned char splashCMYK8C(SplashColorPtr cmyk8) { return cmyk8[0]; } static inline unsigned char splashCMYK8M(SplashColorPtr cmyk8) { return cmyk8[1]; } static inline unsigned char splashCMYK8Y(SplashColorPtr cmyk8) { return cmyk8[2]; } static inline unsigned char splashCMYK8K(SplashColorPtr cmyk8) { return cmyk8[3]; } // DEVICEN8 static inline unsigned char splashDeviceN8C(SplashColorPtr deviceN8) { return deviceN8[0]; } static inline unsigned char splashDeviceN8M(SplashColorPtr deviceN8) { return deviceN8[1]; } static inline unsigned char splashDeviceN8Y(SplashColorPtr deviceN8) { return deviceN8[2]; } static inline unsigned char splashDeviceN8K(SplashColorPtr deviceN8) { return deviceN8[3]; } static inline unsigned char splashDeviceN8S(SplashColorPtr deviceN8, int nSpot) { return deviceN8[4 + nSpot]; } static inline void splashClearColor(SplashColorPtr dest) { dest[0] = 0; dest[1] = 0; dest[2] = 0; dest[3] = 0; for (int i = 4; i < SPOT_NCOMPS + 4; i++) { dest[i] = 0; } } static inline void splashColorCopy(SplashColorPtr dest, SplashColorConstPtr src) { dest[0] = src[0]; dest[1] = src[1]; dest[2] = src[2]; dest[3] = src[3]; for (int i = 4; i < SPOT_NCOMPS + 4; i++) { dest[i] = src[i]; } } static inline bool splashColorEqual(SplashColorConstPtr dest, SplashColorConstPtr src) { for (int i = 0; i < SPOT_NCOMPS + 4; i++) { if (dest[i] != src[i]) { return false; } } return true; } static inline void splashColorXor(SplashColorPtr dest, SplashColorConstPtr src) { dest[0] ^= src[0]; dest[1] ^= src[1]; dest[2] ^= src[2]; dest[3] ^= src[3]; for (int i = 4; i < SPOT_NCOMPS + 4; i++) { dest[i] ^= src[i]; } } //------------------------------------------------------------------------ // blend functions //------------------------------------------------------------------------ typedef void (*SplashBlendFunc)(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm); //------------------------------------------------------------------------ // screen parameters //------------------------------------------------------------------------ enum SplashScreenType { splashScreenDispersed, splashScreenClustered, splashScreenStochasticClustered }; struct SplashScreenParams { SplashScreenType type; int size; int dotRadius; SplashCoord gamma; SplashCoord blackThreshold; SplashCoord whiteThreshold; }; //------------------------------------------------------------------------ // error results //------------------------------------------------------------------------ typedef int SplashError; //------------------------------------------------------------------------ // image file formats //------------------------------------------------------------------------ enum SplashImageFileFormat { splashFormatJpeg, splashFormatPng, splashFormatTiff, splashFormatJpegCMYK }; #endif poppler-24.02.0/splash/SplashXPath.cc000066400000000000000000000332551455701731300173560ustar00rootroot00000000000000//======================================================================== // // SplashXPath.cc // //======================================================================== //======================================================================== // // 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) 2010 Paweł Wiejacha // Copyright (C) 2010, 2011, 2018, 2019, 2021 Albert Astals Cid // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2017 Adrian Johnson // // 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 #include #include #include #include "goo/gmem.h" #include "goo/GooLikely.h" #include "SplashMath.h" #include "SplashPath.h" #include "SplashXPath.h" //------------------------------------------------------------------------ struct SplashXPathPoint { SplashCoord x, y; }; struct SplashXPathAdjust { int firstPt, lastPt; // range of points bool vert; // vertical or horizontal hint SplashCoord x0a, x0b, // hint boundaries xma, xmb, x1a, x1b; SplashCoord x0, x1, xm; // adjusted coordinates }; //------------------------------------------------------------------------ // Transform a point from user space to device space. inline void SplashXPath::transform(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 ] // [ m[4] m[5] 1 ] *xo = xi * matrix[0] + yi * matrix[2] + matrix[4]; *yo = xi * matrix[1] + yi * matrix[3] + matrix[5]; } //------------------------------------------------------------------------ // SplashXPath //------------------------------------------------------------------------ SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix, SplashCoord flatness, bool closeSubpaths, bool adjustLines, int linePosI) { SplashPathHint *hint; SplashXPathPoint *pts; SplashXPathAdjust *adjusts, *adjust; SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xsp, ysp; SplashCoord adj0, adj1; int curSubpath, i, j; // transform the points pts = (SplashXPathPoint *)gmallocn(path->length, sizeof(SplashXPathPoint)); for (i = 0; i < path->length; ++i) { transform(matrix, path->pts[i].x, path->pts[i].y, &pts[i].x, &pts[i].y); } // set up the stroke adjustment hints if (path->hints) { adjusts = (SplashXPathAdjust *)gmallocn_checkoverflow(path->hintsLength, sizeof(SplashXPathAdjust)); if (adjusts) { for (i = 0; i < path->hintsLength; ++i) { hint = &path->hints[i]; if (hint->ctrl0 + 1 >= path->length || hint->ctrl1 + 1 >= path->length) { gfree(adjusts); adjusts = nullptr; break; } x0 = pts[hint->ctrl0].x; y0 = pts[hint->ctrl0].y; x1 = pts[hint->ctrl0 + 1].x; y1 = pts[hint->ctrl0 + 1].y; x2 = pts[hint->ctrl1].x; y2 = pts[hint->ctrl1].y; x3 = pts[hint->ctrl1 + 1].x; y3 = pts[hint->ctrl1 + 1].y; if (x0 == x1 && x2 == x3) { adjusts[i].vert = true; adj0 = x0; adj1 = x2; } else if (y0 == y1 && y2 == y3) { adjusts[i].vert = false; adj0 = y0; adj1 = y2; } else { gfree(adjusts); adjusts = nullptr; break; } if (adj0 > adj1) { x0 = adj0; adj0 = adj1; adj1 = x0; } adjusts[i].x0a = adj0 - 0.01; adjusts[i].x0b = adj0 + 0.01; adjusts[i].xma = (SplashCoord)0.5 * (adj0 + adj1) - 0.01; adjusts[i].xmb = (SplashCoord)0.5 * (adj0 + adj1) + 0.01; adjusts[i].x1a = adj1 - 0.01; adjusts[i].x1b = adj1 + 0.01; // rounding both edge coordinates can result in lines of // different widths (e.g., adj=10.1, adj1=11.3 --> x0=10, x1=11; // adj0=10.4, adj1=11.6 --> x0=10, x1=12), but it has the // benefit of making adjacent strokes/fills line up without any // gaps between them x0 = splashRound(adj0); x1 = splashRound(adj1); if (x1 == x0) { if (adjustLines) { // the adjustment moves thin lines (clip rectangle with // empty width or height) out of clip area, here we need // a special adjustment: x0 = linePosI; x1 = x0 + 1; } else { x1 = x1 + 1; } } adjusts[i].x0 = (SplashCoord)x0; adjusts[i].x1 = (SplashCoord)x1 - 0.01; adjusts[i].xm = (SplashCoord)0.5 * (adjusts[i].x0 + adjusts[i].x1); adjusts[i].firstPt = hint->firstPt; adjusts[i].lastPt = hint->lastPt; } } } else { adjusts = nullptr; } // perform stroke adjustment if (adjusts) { for (i = 0, adjust = adjusts; i < path->hintsLength; ++i, ++adjust) { for (j = adjust->firstPt; j <= adjust->lastPt; ++j) { strokeAdjust(adjust, &pts[j].x, &pts[j].y); } } gfree(adjusts); } segs = nullptr; length = size = 0; x0 = y0 = xsp = ysp = 0; // make gcc happy adj0 = adj1 = 0; // make gcc happy curSubpath = 0; i = 0; while (i < path->length) { // first point in subpath - skip it if (path->flags[i] & splashPathFirst) { x0 = pts[i].x; y0 = pts[i].y; xsp = x0; ysp = y0; curSubpath = i; ++i; } else { // curve segment if (path->flags[i] & splashPathCurve) { x1 = pts[i].x; y1 = pts[i].y; x2 = pts[i + 1].x; y2 = pts[i + 1].y; x3 = pts[i + 2].x; y3 = pts[i + 2].y; addCurve(x0, y0, x1, y1, x2, y2, x3, y3, flatness, (path->flags[i - 1] & splashPathFirst), (path->flags[i + 2] & splashPathLast), !closeSubpaths && (path->flags[i - 1] & splashPathFirst) && !(path->flags[i - 1] & splashPathClosed), !closeSubpaths && (path->flags[i + 2] & splashPathLast) && !(path->flags[i + 2] & splashPathClosed)); x0 = x3; y0 = y3; i += 3; // line segment } else { x1 = pts[i].x; y1 = pts[i].y; addSegment(x0, y0, x1, y1); x0 = x1; y0 = y1; ++i; } // close a subpath if (closeSubpaths && (path->flags[i - 1] & splashPathLast) && (pts[i - 1].x != pts[curSubpath].x || pts[i - 1].y != pts[curSubpath].y)) { addSegment(x0, y0, xsp, ysp); } } } gfree(pts); } // Apply the stroke adjust hints to point : (*, *). void SplashXPath::strokeAdjust(SplashXPathAdjust *adjust, SplashCoord *xp, SplashCoord *yp) { SplashCoord x, y; if (adjust->vert) { x = *xp; if (x > adjust->x0a && x < adjust->x0b) { *xp = adjust->x0; } else if (x > adjust->xma && x < adjust->xmb) { *xp = adjust->xm; } else if (x > adjust->x1a && x < adjust->x1b) { *xp = adjust->x1; } } else { y = *yp; if (y > adjust->x0a && y < adjust->x0b) { *yp = adjust->x0; } else if (y > adjust->xma && y < adjust->xmb) { *yp = adjust->xm; } else if (y > adjust->x1a && y < adjust->x1b) { *yp = adjust->x1; } } } SplashXPath::~SplashXPath() { gfree(segs); } // Add space for more segments void SplashXPath::grow(int nSegs) { if (length + nSegs > size) { if (size == 0) { size = 32; } while (size < length + nSegs) { size *= 2; } segs = (SplashXPathSeg *)greallocn_checkoverflow(segs, size, sizeof(SplashXPathSeg)); if (unlikely(!segs)) { length = 0; size = 0; } } } void SplashXPath::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) { SplashCoord *cx = new SplashCoord[(splashMaxCurveSplits + 1) * 3]; SplashCoord *cy = new SplashCoord[(splashMaxCurveSplits + 1) * 3]; int *cNext = new int[splashMaxCurveSplits + 1]; SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh; SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh; SplashCoord dx, dy, mx, my, d1, d2, flatness2; int p1, p2, p3; flatness2 = flatness * flatness; // initial segment p1 = 0; p2 = splashMaxCurveSplits; *(cx + p1 * 3 + 0) = x0; *(cx + p1 * 3 + 1) = x1; *(cx + p1 * 3 + 2) = x2; *(cx + p2 * 3 + 0) = x3; *(cy + p1 * 3 + 0) = y0; *(cy + p1 * 3 + 1) = y1; *(cy + p1 * 3 + 2) = y2; *(cy + p2 * 3 + 0) = y3; *(cNext + p1) = p2; while (p1 < splashMaxCurveSplits) { // get the next segment xl0 = *(cx + p1 * 3 + 0); xx1 = *(cx + p1 * 3 + 1); xx2 = *(cx + p1 * 3 + 2); yl0 = *(cy + p1 * 3 + 0); yy1 = *(cy + p1 * 3 + 1); yy2 = *(cy + p1 * 3 + 2); p2 = *(cNext + p1); xr3 = *(cx + p2 * 3 + 0); yr3 = *(cy + p2 * 3 + 0); // compute the distances from the control points to the // midpoint of the straight line (this is a bit of a hack, but // it's much faster than computing the actual distances to the // line) mx = (xl0 + xr3) * 0.5; my = (yl0 + yr3) * 0.5; dx = xx1 - mx; dy = yy1 - my; d1 = dx * dx + dy * dy; dx = xx2 - mx; dy = yy2 - my; d2 = dx * dx + dy * dy; // if the curve is flat enough, or no more subdivisions are // allowed, add the straight line segment if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) { addSegment(xl0, yl0, xr3, yr3); p1 = p2; // otherwise, subdivide the curve } else { xl1 = (xl0 + xx1) * 0.5; yl1 = (yl0 + yy1) * 0.5; xh = (xx1 + xx2) * 0.5; yh = (yy1 + yy2) * 0.5; xl2 = (xl1 + xh) * 0.5; yl2 = (yl1 + yh) * 0.5; xr2 = (xx2 + xr3) * 0.5; yr2 = (yy2 + yr3) * 0.5; xr1 = (xh + xr2) * 0.5; yr1 = (yh + yr2) * 0.5; xr0 = (xl2 + xr1) * 0.5; yr0 = (yl2 + yr1) * 0.5; // add the new subdivision points p3 = (p1 + p2) / 2; *(cx + p1 * 3 + 1) = xl1; *(cx + p1 * 3 + 2) = xl2; *(cy + p1 * 3 + 1) = yl1; *(cy + p1 * 3 + 2) = yl2; *(cNext + p1) = p3; *(cx + p3 * 3 + 0) = xr0; *(cx + p3 * 3 + 1) = xr1; *(cx + p3 * 3 + 2) = xr2; *(cy + p3 * 3 + 0) = yr0; *(cy + p3 * 3 + 1) = yr1; *(cy + p3 * 3 + 2) = yr2; *(cNext + p3) = p2; } } delete[] cx; delete[] cy; delete[] cNext; } void SplashXPath::addSegment(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) { grow(1); if (unlikely(!segs)) { return; } segs[length].x0 = x0; segs[length].y0 = y0; segs[length].x1 = x1; segs[length].y1 = y1; segs[length].flags = 0; if (y1 == y0) { segs[length].dxdy = segs[length].dydx = 0; segs[length].flags |= splashXPathHoriz; if (x1 == x0) { segs[length].flags |= splashXPathVert; } } else if (x1 == x0) { segs[length].dxdy = segs[length].dydx = 0; segs[length].flags |= splashXPathVert; } else { segs[length].dxdy = (x1 - x0) / (y1 - y0); segs[length].dydx = (SplashCoord)1 / segs[length].dxdy; } if (y0 > y1) { segs[length].flags |= splashXPathFlip; } ++length; } struct cmpXPathSegsFunctor { bool operator()(const SplashXPathSeg &seg0, const SplashXPathSeg &seg1) { SplashCoord x0, y0, x1, y1; if (seg0.flags & splashXPathFlip) { x0 = seg0.x1; y0 = seg0.y1; } else { x0 = seg0.x0; y0 = seg0.y0; } if (seg1.flags & splashXPathFlip) { x1 = seg1.x1; y1 = seg1.y1; } else { x1 = seg1.x0; y1 = seg1.y0; } return (y0 != y1) ? (y0 < y1) : (x0 < x1); } }; void SplashXPath::aaScale() { SplashXPathSeg *seg; int i; for (i = 0, seg = segs; i < length; ++i, ++seg) { seg->x0 *= splashAASize; seg->y0 *= splashAASize; seg->x1 *= splashAASize; seg->y1 *= splashAASize; } } void SplashXPath::sort() { std::sort(segs, segs + length, cmpXPathSegsFunctor()); } poppler-24.02.0/splash/SplashXPath.h000066400000000000000000000071731455701731300172200ustar00rootroot00000000000000//======================================================================== // // SplashXPath.h // //======================================================================== //======================================================================== // // 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) 2013 Thomas Freitag // Copyright (C) 2018, 2021 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 // //======================================================================== #ifndef SPLASHXPATH_H #define SPLASHXPATH_H #include "SplashTypes.h" class SplashPath; struct SplashXPathAdjust; //------------------------------------------------------------------------ #define splashMaxCurveSplits (1 << 10) //------------------------------------------------------------------------ // SplashXPathSeg //------------------------------------------------------------------------ struct SplashXPathSeg { SplashCoord x0, y0; // first endpoint SplashCoord x1, y1; // second endpoint SplashCoord dxdy; // slope: delta-x / delta-y SplashCoord dydx; // slope: delta-y / delta-x unsigned int flags; }; #define splashXPathHoriz \ 0x01 // segment is vertical (y0 == y1) // (dxdy is undef) #define splashXPathVert \ 0x02 // segment is horizontal (x0 == x1) // (dydx is undef) #define splashXPathFlip 0x04 // y0 > y1 //------------------------------------------------------------------------ // SplashXPath //------------------------------------------------------------------------ class SplashXPath { public: // Expands (converts to segments) and flattens (converts curves to // lines) . Transforms all points from user space to device // space, via . If is true, closes all open // subpaths. SplashXPath(SplashPath *path, SplashCoord *matrix, SplashCoord flatness, bool closeSubpaths, bool adjustLines = false, int linePosI = 0); ~SplashXPath(); SplashXPath(const SplashXPath &) = delete; SplashXPath &operator=(const SplashXPath &) = delete; // Multiply all coordinates by splashAASize, in preparation for // anti-aliased rendering. void aaScale(); // Sort by upper coordinate (lower y), in y-major order. void sort(); protected: void transform(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); void addSegment(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1); SplashXPathSeg *segs; int length, size; // length and size of segs array friend class SplashXPathScanner; friend class SplashClip; friend class Splash; }; #endif poppler-24.02.0/splash/SplashXPathScanner.cc000066400000000000000000000376731455701731300207000ustar00rootroot00000000000000//======================================================================== // // SplashXPathScanner.cc // //======================================================================== //======================================================================== // // 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) 2008, 2010, 2014, 2018, 2019, 2021, 2022 Albert Astals Cid // Copyright (C) 2010 Paweł Wiejacha // Copyright (C) 2013, 2014, 2021 Thomas Freitag // Copyright (C) 2018 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 // //======================================================================== #include #include #include #include #include "goo/gmem.h" #include "goo/GooLikely.h" #include "SplashMath.h" #include "SplashXPath.h" #include "SplashBitmap.h" #include "SplashXPathScanner.h" //------------------------------------------------------------------------ //------------------------------------------------------------------------ // SplashXPathScanner //------------------------------------------------------------------------ SplashXPathScanner::SplashXPathScanner(const SplashXPath &xPath, bool eoA, int clipYMin, int clipYMax) { const SplashXPathSeg *seg; SplashCoord xMinFP, yMinFP, xMaxFP, yMaxFP; int i; eo = eoA; partialClip = false; // compute the bbox xMin = yMin = 1; xMax = yMax = 0; if (xPath.length > 0) { seg = &xPath.segs[0]; if (unlikely(std::isnan(seg->x0) || std::isnan(seg->x1) || std::isnan(seg->y0) || std::isnan(seg->y1))) { return; } if (seg->x0 <= seg->x1) { xMinFP = seg->x0; xMaxFP = seg->x1; } else { xMinFP = seg->x1; xMaxFP = seg->x0; } if (seg->flags & splashXPathFlip) { yMinFP = seg->y1; yMaxFP = seg->y0; } else { yMinFP = seg->y0; yMaxFP = seg->y1; } for (i = 1; i < xPath.length; ++i) { seg = &xPath.segs[i]; if (unlikely(std::isnan(seg->x0) || std::isnan(seg->x1) || std::isnan(seg->y0) || std::isnan(seg->y1))) { return; } if (seg->x0 < xMinFP) { xMinFP = seg->x0; } else if (seg->x0 > xMaxFP) { xMaxFP = seg->x0; } if (seg->x1 < xMinFP) { xMinFP = seg->x1; } else if (seg->x1 > xMaxFP) { xMaxFP = seg->x1; } if (seg->flags & splashXPathFlip) { if (seg->y0 > yMaxFP) { yMaxFP = seg->y0; } } else { if (seg->y1 > yMaxFP) { yMaxFP = seg->y1; } } } xMin = splashFloor(xMinFP); xMax = splashFloor(xMaxFP); yMin = splashFloor(yMinFP); yMax = splashFloor(yMaxFP); if (clipYMin > yMin) { yMin = clipYMin; partialClip = true; } if (clipYMax < yMax) { yMax = clipYMax; partialClip = true; } } computeIntersections(xPath); } SplashXPathScanner::~SplashXPathScanner() { } void SplashXPathScanner::getBBoxAA(int *xMinA, int *yMinA, int *xMaxA, int *yMaxA) const { *xMinA = xMin / splashAASize; *yMinA = yMin / splashAASize; *xMaxA = xMax / splashAASize; *yMaxA = yMax / splashAASize; } void SplashXPathScanner::getSpanBounds(int y, int *spanXMin, int *spanXMax) const { if (y < yMin || y > yMax) { *spanXMin = xMax + 1; *spanXMax = xMax; return; } const auto &line = allIntersections[y - yMin]; if (!line.empty()) { *spanXMin = line[0].x0; int xx = line[0].x1; for (const SplashIntersect &intersect : line) { if (intersect.x1 > xx) { xx = intersect.x1; } } *spanXMax = xx; } else { *spanXMin = xMax + 1; *spanXMax = xMax; } } bool SplashXPathScanner::test(int x, int y) const { if (y < yMin || y > yMax) { return false; } const auto &line = allIntersections[y - yMin]; int count = 0; for (unsigned int i = 0; i < line.size() && line[i].x0 <= x; ++i) { if (x <= line[i].x1) { return true; } count += line[i].count; } return eo ? (count & 1) : (count != 0); } bool SplashXPathScanner::testSpan(int x0, int x1, int y) const { unsigned int i; if (y < yMin || y > yMax) { return false; } const auto &line = allIntersections[y - yMin]; int count = 0; for (i = 0; i < line.size() && line[i].x1 < x0; ++i) { count += line[i].count; } // invariant: the subspan [x0,xx1] is inside the path int xx1 = x0 - 1; while (xx1 < x1) { if (i >= line.size()) { return false; } if (line[i].x0 > xx1 + 1 && !(eo ? (count & 1) : (count != 0))) { return false; } if (line[i].x1 > xx1) { xx1 = line[i].x1; } count += line[i].count; ++i; } return true; } bool SplashXPathScanIterator::getNextSpan(int *x0, int *x1) { int xx0, xx1; if (interIdx >= line.size()) { return false; } xx0 = line[interIdx].x0; xx1 = line[interIdx].x1; interCount += line[interIdx].count; ++interIdx; while (interIdx < line.size() && (line[interIdx].x0 <= xx1 || (eo ? (interCount & 1) : (interCount != 0)))) { if (line[interIdx].x1 > xx1) { xx1 = line[interIdx].x1; } interCount += line[interIdx].count; ++interIdx; } *x0 = xx0; *x1 = xx1; return true; } SplashXPathScanIterator::SplashXPathScanIterator(const SplashXPathScanner &scanner, int y) : line((y < scanner.yMin || y > scanner.yMax) ? scanner.allIntersections[0] : scanner.allIntersections[y - scanner.yMin]), interIdx(0), interCount(0), eo(scanner.eo) { if (y < scanner.yMin || y > scanner.yMax) { // set index to line end interIdx = line.size(); } } void SplashXPathScanner::computeIntersections(const SplashXPath &xPath) { const SplashXPathSeg *seg; SplashCoord segXMin, segXMax, segYMin, segYMax, xx0, xx1; int x, y, y0, y1, i; if (yMin > yMax) { return; } // build the list of all intersections allIntersections.resize(yMax - yMin + 1); for (i = 0; i < xPath.length; ++i) { seg = &xPath.segs[i]; if (seg->flags & splashXPathFlip) { segYMin = seg->y1; segYMax = seg->y0; } else { segYMin = seg->y0; segYMax = seg->y1; } if (seg->flags & splashXPathHoriz) { y = splashFloor(seg->y0); if (y >= yMin && y <= yMax) { if (!addIntersection(segYMin, segYMax, y, splashFloor(seg->x0), splashFloor(seg->x1), 0)) { break; } } } else if (seg->flags & splashXPathVert) { y0 = splashFloor(segYMin); if (y0 < yMin) { y0 = yMin; } y1 = splashFloor(segYMax); if (y1 > yMax) { y1 = yMax; } x = splashFloor(seg->x0); int count = eo || (seg->flags & splashXPathFlip) ? 1 : -1; for (y = y0; y <= y1; ++y) { if (!addIntersection(segYMin, segYMax, y, x, x, count)) { break; } } } else { if (seg->x0 < seg->x1) { segXMin = seg->x0; segXMax = seg->x1; } else { segXMin = seg->x1; segXMax = seg->x0; } y0 = splashFloor(segYMin); if (y0 < yMin) { y0 = yMin; } y1 = splashFloor(segYMax); if (y1 > yMax) { y1 = yMax; } int count = eo || (seg->flags & splashXPathFlip) ? 1 : -1; // Calculate the projected intersection of the segment with the // X-Axis. SplashCoord xbase = seg->x0 - (seg->y0 * seg->dxdy); xx0 = xbase + ((SplashCoord)y0) * seg->dxdy; // the segment may not actually extend to the top and/or bottom edges if (xx0 < segXMin) { xx0 = segXMin; } else if (xx0 > segXMax) { xx0 = segXMax; } int x0 = splashFloor(xx0); for (y = y0; y <= y1; ++y) { xx1 = xbase + ((SplashCoord)(y + 1) * seg->dxdy); if (xx1 < segXMin) { xx1 = segXMin; } else if (xx1 > segXMax) { xx1 = segXMax; } int x1 = splashFloor(xx1); if (!addIntersection(segYMin, segYMax, y, x0, x1, count)) { break; } xx0 = xx1; x0 = x1; } } } for (auto &line : allIntersections) { std::sort(line.begin(), line.end(), [](const SplashIntersect i0, const SplashIntersect i1) { return i0.x0 < i1.x0; }); } } inline bool SplashXPathScanner::addIntersection(double segYMin, double segYMax, int y, int x0, int x1, int count) { SplashIntersect intersect; intersect.y = y; if (x0 < x1) { intersect.x0 = x0; intersect.x1 = x1; } else { intersect.x0 = x1; intersect.x1 = x0; } if (segYMin <= y && (SplashCoord)y < segYMax) { intersect.count = count; } else { intersect.count = 0; } auto &line = allIntersections[y - yMin]; #ifndef USE_BOOST_HEADERS if (line.empty()) { line.reserve(4); } #endif line.push_back(intersect); return true; } void SplashXPathScanner::renderAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y, bool adjustVertLine) const { int xx0, xx1, xx, xxMin, xxMax, yy, yyMax, interCount; size_t interIdx; unsigned char mask; SplashColorPtr p; memset(aaBuf->getDataPtr(), 0, aaBuf->getRowSize() * aaBuf->getHeight()); xxMin = aaBuf->getWidth(); xxMax = -1; if (yMin <= yMax) { yy = 0; yyMax = splashAASize - 1; // clamp start and end position if (yMin > splashAASize * y) { yy = yMin - splashAASize * y; } if (yyMax + splashAASize * y > yMax) { yyMax = yMax - splashAASize * y; } for (; yy <= yyMax; ++yy) { const auto &line = allIntersections[splashAASize * y + yy - yMin]; interIdx = 0; interCount = 0; while (interIdx < line.size()) { xx0 = line[interIdx].x0; xx1 = line[interIdx].x1; interCount += line[interIdx].count; ++interIdx; while (interIdx < line.size() && (line[interIdx].x0 <= xx1 || (eo ? (interCount & 1) : (interCount != 0)))) { if (line[interIdx].x1 > xx1) { xx1 = line[interIdx].x1; } interCount += line[interIdx].count; ++interIdx; } if (xx0 < 0) { xx0 = 0; } ++xx1; if (xx1 > aaBuf->getWidth()) { xx1 = aaBuf->getWidth(); } // set [xx0, xx1) to 1 if (xx0 < xx1) { xx = xx0; p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx >> 3); if (xx & 7) { mask = adjustVertLine ? 0xff : 0xff >> (xx & 7); if (!adjustVertLine && (xx & ~7) == (xx1 & ~7)) { mask &= (unsigned char)(0xff00 >> (xx1 & 7)); } *p++ |= mask; xx = (xx & ~7) + 8; } for (; xx + 7 < xx1; xx += 8) { *p++ |= 0xff; } if (xx < xx1) { *p |= adjustVertLine ? 0xff : (unsigned char)(0xff00 >> (xx1 & 7)); } } if (xx0 < xxMin) { xxMin = xx0; } if (xx1 > xxMax) { xxMax = xx1; } } } } if (xxMin > xxMax) { xxMin = xxMax; } *x0 = xxMin / splashAASize; *x1 = (xxMax - 1) / splashAASize; } void SplashXPathScanner::clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y) const { int xx0, xx1, xx, yy, yyMin, yyMax, interCount; size_t interIdx; unsigned char mask; SplashColorPtr p; yyMin = 0; yyMax = splashAASize - 1; // clamp start and end position if (yMin > splashAASize * y) { yyMin = yMin - splashAASize * y; } if (yyMax + splashAASize * y > yMax) { yyMax = yMax - splashAASize * y; } for (yy = 0; yy < splashAASize; ++yy) { xx = *x0 * splashAASize; if (yy >= yyMin && yy <= yyMax) { const int intersectionIndex = splashAASize * y + yy - yMin; if (unlikely(intersectionIndex < 0 || (unsigned)intersectionIndex >= allIntersections.size())) { break; } const auto &line = allIntersections[intersectionIndex]; interIdx = 0; interCount = 0; while (interIdx < line.size() && xx < (*x1 + 1) * splashAASize) { xx0 = line[interIdx].x0; xx1 = line[interIdx].x1; interCount += line[interIdx].count; ++interIdx; while (interIdx < line.size() && (line[interIdx].x0 <= xx1 || (eo ? (interCount & 1) : (interCount != 0)))) { if (line[interIdx].x1 > xx1) { xx1 = line[interIdx].x1; } interCount += line[interIdx].count; ++interIdx; } if (xx0 > aaBuf->getWidth()) { xx0 = aaBuf->getWidth(); } // set [xx, xx0) to 0 if (xx < xx0) { p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx >> 3); if (xx & 7) { mask = (unsigned char)(0xff00 >> (xx & 7)); if ((xx & ~7) == (xx0 & ~7)) { mask |= 0xff >> (xx0 & 7); } *p++ &= mask; xx = (xx & ~7) + 8; } for (; xx + 7 < xx0; xx += 8) { *p++ = 0x00; } if (xx < xx0) { *p &= 0xff >> (xx0 & 7); } } if (xx1 >= xx) { xx = xx1 + 1; } } } xx0 = (*x1 + 1) * splashAASize; if (xx0 > aaBuf->getWidth()) { xx0 = aaBuf->getWidth(); } // set [xx, xx0) to 0 if (xx < xx0 && xx >= 0) { p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx >> 3); if (xx & 7) { mask = (unsigned char)(0xff00 >> (xx & 7)); if ((xx & ~7) == (xx0 & ~7)) { mask &= 0xff >> (xx0 & 7); } *p++ &= mask; xx = (xx & ~7) + 8; } for (; xx + 7 < xx0; xx += 8) { *p++ = 0x00; } if (xx < xx0) { *p &= 0xff >> (xx0 & 7); } } } } poppler-24.02.0/splash/SplashXPathScanner.h000066400000000000000000000102651455701731300205260ustar00rootroot00000000000000//======================================================================== // // SplashXPathScanner.h // //======================================================================== //======================================================================== // // 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) 2013, 2014, 2021 Thomas Freitag // Copyright (C) 2018, 2021 Albert Astals Cid // Copyright (C) 2018 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 // //======================================================================== #ifndef SPLASHXPATHSCANNER_H #define SPLASHXPATHSCANNER_H #include "SplashTypes.h" #include #ifdef USE_BOOST_HEADERS # include #endif #include class SplashXPath; class SplashBitmap; struct SplashIntersect { int y; int x0, x1; // intersection of segment with [y, y+1) int count; // EO/NZWN counter increment }; //------------------------------------------------------------------------ // SplashXPathScanner //------------------------------------------------------------------------ class SplashXPathScanner { public: // Create a new SplashXPathScanner object. must be sorted. SplashXPathScanner(const SplashXPath &xPath, bool eoA, int clipYMin, int clipYMax); ~SplashXPathScanner(); SplashXPathScanner(const SplashXPathScanner &) = delete; SplashXPathScanner &operator=(const SplashXPathScanner &) = delete; // Return the path's bounding box. void getBBox(int *xMinA, int *yMinA, int *xMaxA, int *yMaxA) const { *xMinA = xMin; *yMinA = yMin; *xMaxA = xMax; *yMaxA = yMax; } // Return the path's bounding box. void getBBoxAA(int *xMinA, int *yMinA, int *xMaxA, int *yMaxA) const; // Returns true if at least part of the path was outside the // clipYMin/clipYMax bounds passed to the constructor. bool hasPartialClip() const { return partialClip; } // Return the min/max x values for the span at . void getSpanBounds(int y, int *spanXMin, int *spanXMax) const; // Returns true if (,) is inside the path. bool test(int x, int y) const; // Returns true if the entire span ([,], ) is inside the // path. bool testSpan(int x0, int x1, int y) const; // Renders one anti-aliased line into . Returns the min and // max x coordinates with non-zero pixels in and . void renderAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y, bool adjustVertLine = false) const; // 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; private: void computeIntersections(const SplashXPath &xPath); bool addIntersection(double segYMin, double segYMax, int y, int x0, int x1, int count); bool eo; int xMin, yMin, xMax, yMax; bool partialClip; #ifdef USE_BOOST_HEADERS typedef boost::container::small_vector IntersectionLine; #else typedef std::vector IntersectionLine; #endif std::vector allIntersections; friend class SplashXPathScanIterator; }; class SplashXPathScanIterator { public: SplashXPathScanIterator(const SplashXPathScanner &scanner, int y); // Returns the next span inside the path at the current y position // Returns false if there are no more spans. bool getNextSpan(int *x0, int *x1); private: #ifdef USE_BOOST_HEADERS typedef boost::container::small_vector IntersectionLine; #else typedef std::vector IntersectionLine; #endif const IntersectionLine &line; size_t interIdx; // current index into int interCount; // current EO/NZWN counter const bool eo; }; #endif poppler-24.02.0/test/000077500000000000000000000000001455701731300143255ustar00rootroot00000000000000poppler-24.02.0/test/.gitignore000066400000000000000000000002171455701731300163150ustar00rootroot00000000000000*.la *.lo *.loT .cvsignore .deps .libs Makefile Makefile.in gtk-cairo-test gtk-splash-test gtk-test pdf_inspector perf-test pdf-fullrewrite *~ poppler-24.02.0/test/CMakeLists.txt000066400000000000000000000123421455701731300170670ustar00rootroot00000000000000 if (HAVE_NANOSLEEP OR LIB_RT_HAS_NANOSLEEP) set (perf_test_SRCS perf-test.cc perf-test-preview-dummy.cc ) add_executable(perf-test ${perf_test_SRCS}) target_link_libraries(perf-test poppler) if (LIB_RT_HAS_NANOSLEEP) target_link_libraries(perf-test rt) endif () endif () if (GTK_FOUND) include_directories( ${CMAKE_SOURCE_DIR}/glib ${CMAKE_BINARY_DIR}/glib ) set (gtk_splash_test_SRCS gtk-test.cc ) 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) set (pdf_inspector_SRCS pdf-inspector.cc ${CMAKE_SOURCE_DIR}/poppler/CairoFontEngine.cc ${CMAKE_SOURCE_DIR}/poppler/CairoOutputDev.cc ${CMAKE_SOURCE_DIR}/poppler/CairoRescaleBox.cc ) 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(CheckCXXSymbolExists) set (CMAKE_REQUIRED_INCLUDES ${CAIRO_INCLUDE_DIRS}) check_cxx_symbol_exists(CAIRO_HAS_PNG_FUNCTIONS "cairo.h" HAVE_CAIRO_PNG) check_cxx_symbol_exists(CAIRO_HAS_PDF_SURFACE "cairo.h" HAVE_CAIRO_PDF) check_cxx_symbol_exists(CAIRO_HAS_PS_SURFACE "cairo.h" HAVE_CAIRO_PS) check_cxx_symbol_exists(CAIRO_HAS_SVG_SURFACE "cairo.h" HAVE_CAIRO_SVG) if (HAVE_CAIRO_PNG AND HAVE_CAIRO_PDF AND HAVE_CAIRO_PS AND HAVE_CAIRO_SVG) find_package(Threads) set(cairo_thread_test_SRCS cairo-thread-test.cc ${CMAKE_SOURCE_DIR}/poppler/CairoFontEngine.cc ${CMAKE_SOURCE_DIR}/poppler/CairoOutputDev.cc ${CMAKE_SOURCE_DIR}/poppler/CairoRescaleBox.cc ) 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 () set (pdf_fullrewrite_SRCS pdf-fullrewrite.cc ../utils/parseargs.cc ) add_executable(pdf-fullrewrite ${pdf_fullrewrite_SRCS}) target_link_libraries(pdf-fullrewrite poppler) # Tests for the image embedding API. if(ENABLE_LIBPNG OR ENABLE_LIBJPEG) set(image_embedding_SRCS image-embedding.cc ../utils/parseargs.cc ) add_executable(image-embedding ${image_embedding_SRCS}) target_link_libraries(image-embedding poppler) set(INPUT_PDF ${TESTDATADIR}/unittestcases/xr01.pdf) set(IMG_DIR ${TESTDATADIR}/unittestcases/images) set(IMAGE_EMBEDDING_PATH ${EXECUTABLE_OUTPUT_PATH}/image-embedding) if (ENABLE_LIBPNG) add_test( NAME embed-png-g1 COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/png-g1.png -depth 8 -colorspace DeviceGray ) add_test( NAME embed-png-g2 COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/png-g2.png -depth 8 -colorspace DeviceGray ) add_test( NAME embed-png-g4 COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/png-g4.png -depth 8 -colorspace DeviceGray ) add_test( NAME embed-png-g8 COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/png-g8.png -depth 8 -colorspace DeviceGray ) add_test( NAME embed-png-g16 COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/png-g16.png -depth 16 -colorspace DeviceGray ) add_test( NAME embed-png-ga8 COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/png-ga8.png -depth 8 -colorspace DeviceGray -smask ) add_test( NAME embed-png-ga16 COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/png-ga16.png -depth 16 -colorspace DeviceGray -smask ) add_test( NAME embed-png-palette COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/png-palette.png -depth 8 -colorspace DeviceRGB ) add_test( NAME embed-png-rgb8 COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/png-rgb8.png -depth 8 -colorspace DeviceRGB ) add_test( NAME embed-png-rgb16 COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/png-rgb16.png -depth 16 -colorspace DeviceRGB ) add_test( NAME embed-png-rgba8 COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/png-rgba8.png -depth 8 -colorspace DeviceRGB -smask ) add_test( NAME embed-png-rgba16 COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/png-rgba16.png -depth 16 -colorspace DeviceRGB -smask ) add_test( NAME embed-malformed-png COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/malformed.png -fail ) endif() if(ENABLE_LIBJPEG) add_test( NAME embed-jpeg COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/jpeg.jpg -depth 8 -colorspace DeviceRGB -filter DCTDecode ) add_test( NAME embed-malformed-jpeg COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/malformed.jpg -fail ) endif() unset(IMAGE_EMBEDDING_PATH) unset(IMG_DIR) unset(INPUT_PDF) endif() poppler-24.02.0/test/cairo-thread-test.cc000066400000000000000000000406121455701731300201560ustar00rootroot00000000000000//======================================================================== // // cairo-thread-test.cc // // This file is licensed under the GPLv2 or later // // Copyright (C) 2022 Adrian Johnson // //======================================================================== #include "config.h" #include #include #include #include #include #include #include #include #include "goo/GooString.h" #include "CairoOutputDev.h" #include "CairoFontEngine.h" #include "GlobalParams.h" #include "PDFDoc.h" #include "PDFDocFactory.h" #include "../utils/numberofcharacters.h" #include #include #include #include static const int renderResolution = 150; enum OutputType { png, pdf, ps, svg }; // Lazy creation of PDFDoc class Document { public: explicit Document(const std::string &filenameA) : filename(filenameA) { std::call_once(ftLibOnceFlag, FT_Init_FreeType, &ftLib); } std::shared_ptr getDoc() { std::call_once(docOnceFlag, &Document::openDocument, this); return doc; } const std::string &getFilename() { return filename; } CairoFontEngine *getFontEngine() { return fontEngine.get(); } private: void openDocument() { doc = PDFDocFactory().createPDFDoc(GooString(filename)); if (!doc->isOk()) { fprintf(stderr, "Error opening PDF file %s\n", filename.c_str()); exit(1); } fontEngine = std::make_unique(ftLib); } std::string filename; std::shared_ptr doc; std::once_flag docOnceFlag; std::unique_ptr fontEngine; static FT_Library ftLib; static std::once_flag ftLibOnceFlag; }; FT_Library Document::ftLib; std::once_flag Document::ftLibOnceFlag; struct Job { Job(OutputType typeA, const std::shared_ptr &documentA, int pageNumA, const std::string &outputFileA) : type(typeA), document(documentA), pageNum(pageNumA), outputFile(outputFileA) { } OutputType type; std::shared_ptr document; int pageNum; std::string outputFile; }; class JobQueue { public: JobQueue() : shutdownFlag(false) { } void pushJob(std::unique_ptr &job) { std::scoped_lock lock { mutex }; queue.push_back(std::move(job)); condition.notify_one(); } // Wait for job. If shutdownFlag true, will return null if queue empty. std::unique_ptr popJob() { std::unique_lock lock(mutex); condition.wait(lock, [this] { return !queue.empty() || shutdownFlag; }); std::unique_ptr job; if (!queue.empty()) { job = std::move(queue.front()); queue.pop_front(); } else { condition.notify_all(); // notify waitUntilEmpty() } return job; } // When called, popJob() will not block on an empty queue instead returning nullptr void shutdown() { shutdownFlag = true; condition.notify_all(); } // wait until queue is empty void waitUntilEmpty() { std::unique_lock lock(mutex); condition.wait(lock, [this] { return queue.empty(); }); } private: std::deque> queue; std::mutex mutex; std::condition_variable condition; bool shutdownFlag; }; static cairo_status_t writeStream(void *closure, const unsigned char *data, unsigned int length) { FILE *file = (FILE *)closure; if (fwrite(data, length, 1, file) == 1) { return CAIRO_STATUS_SUCCESS; } else { return CAIRO_STATUS_WRITE_ERROR; } } // PDF/PS/SVG output static void renderDocument(const Job &job) { FILE *f = openFile(job.outputFile.c_str(), "wb"); if (!f) { fprintf(stderr, "Error opening output file %s\n", job.outputFile.c_str()); exit(1); } cairo_surface_t *surface = nullptr; switch (job.type) { case OutputType::pdf: surface = cairo_pdf_surface_create_for_stream(writeStream, f, 1, 1); break; case OutputType::ps: surface = cairo_ps_surface_create_for_stream(writeStream, f, 1, 1); break; case OutputType::svg: surface = cairo_svg_surface_create_for_stream(writeStream, f, 1, 1); break; case OutputType::png: break; } cairo_surface_set_fallback_resolution(surface, renderResolution, renderResolution); std::unique_ptr cairoOut = std::make_unique(); cairoOut->startDoc(job.document->getDoc().get(), job.document->getFontEngine()); cairo_status_t status; for (int pageNum = 1; pageNum <= job.document->getDoc()->getNumPages(); pageNum++) { double width = job.document->getDoc()->getPageMediaWidth(pageNum); double height = job.document->getDoc()->getPageMediaHeight(pageNum); if (job.type == OutputType::pdf) { cairo_pdf_surface_set_size(surface, width, height); } else if (job.type == OutputType::ps) { cairo_ps_surface_set_size(surface, width, height); } cairo_t *cr = cairo_create(surface); cairoOut->setCairo(cr); cairoOut->setPrinting(true); cairo_save(cr); job.document->getDoc()->displayPageSlice(cairoOut.get(), pageNum, 72.0, 72.0, 0, /* rotate */ true, /* useMediaBox */ false, /* Crop */ true /*printing*/, -1, -1, -1, -1); cairo_restore(cr); 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_finish(surface); status = cairo_surface_status(surface); if (status) { fprintf(stderr, "cairo error: %s\n", cairo_status_to_string(status)); } cairo_surface_destroy(surface); fclose(f); } // PNG page output static void renderPage(const Job &job) { double width = job.document->getDoc()->getPageMediaWidth(job.pageNum); double height = job.document->getDoc()->getPageMediaHeight(job.pageNum); // convert from points to pixels width *= renderResolution / 72.0; height *= renderResolution / 72.0; cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, static_cast(ceil(width)), static_cast(ceil(height))); std::unique_ptr cairoOut = std::make_unique(); cairoOut->startDoc(job.document->getDoc().get(), job.document->getFontEngine()); cairo_t *cr = cairo_create(surface); cairo_status_t status; cairoOut->setCairo(cr); cairoOut->setPrinting(false); cairo_save(cr); cairo_scale(cr, renderResolution / 72.0, renderResolution / 72.0); job.document->getDoc()->displayPageSlice(cairoOut.get(), job.pageNum, 72.0, 72.0, 0, /* rotate */ true, /* useMediaBox */ false, /* Crop */ false /*printing */, -1, -1, -1, -1); cairo_restore(cr); cairoOut->setCairo(nullptr); // Blend onto white page cairo_save(cr); cairo_set_operator(cr, CAIRO_OPERATOR_DEST_OVER); cairo_set_source_rgb(cr, 1, 1, 1); cairo_paint(cr); cairo_restore(cr); status = cairo_status(cr); if (status) { fprintf(stderr, "cairo error: %s\n", cairo_status_to_string(status)); } cairo_destroy(cr); FILE *f = openFile(job.outputFile.c_str(), "wb"); if (!f) { fprintf(stderr, "Error opening output file %s\n", job.outputFile.c_str()); exit(1); } cairo_surface_write_to_png_stream(surface, writeStream, f); fclose(f); cairo_surface_finish(surface); status = cairo_surface_status(surface); if (status) { fprintf(stderr, "cairo error: %s\n", cairo_status_to_string(status)); } cairo_surface_destroy(surface); } static void runThread(const std::shared_ptr &jobQueue) { while (true) { std::unique_ptr job = jobQueue->popJob(); if (!job) { break; } switch (job->type) { case OutputType::png: renderPage(*job); break; case OutputType::pdf: case OutputType::ps: case OutputType::svg: renderDocument(*job); break; } } } static void printUsage() { int default_threads = std::max(1, (int)std::thread::hardware_concurrency()); printf("cairo-thread-test [-j jobs] [-p priority] [ ...]...\n"); printf(" -j num number of concurrent threads (default %d)\n", default_threads); printf(" -p priority is one of:\n"); printf(" page one page at a time will be queued from each document in round-robin fashion (default).\n"); printf(" document all pages in the first document will be queued before processing to the next document.\n"); printf(" Note: documents with vector output will be handled in one job. They can not be parallelized.\n"); printf(" is one of -png, -pdf, -ps, -svg\n"); printf(" The output option will apply to all documents after the option until a different option is specified\n"); } // Parse -j and -p options. These must appear before any other arguments static bool getThreadsAndPriority(int &argc, char **&argv, int &numThreads, bool &documentPriority) { numThreads = std::max(1, (int)std::thread::hardware_concurrency()); documentPriority = false; while (argc > 0) { std::string arg(*argv); if (arg == "-j") { argc--; argv++; if (argc == 0) { return false; } numThreads = atoi(*argv); if (numThreads == 0) { return false; } argc--; argv++; } else if (arg == "-p") { argc--; argv++; if (argc == 0) { return false; } arg = *argv; if (arg == "document") { documentPriority = true; } else if (arg == "page") { documentPriority = false; } else { return false; } argc--; argv++; } else { // file or output option break; } } return true; } // eg "-png doc1.pdf -ps doc2.pdf doc3.pdf -png doc4.pdf" static bool getOutputTypeAndDocument(int &argc, char **&argv, OutputType &outputType, std::string &filename) { static OutputType type; static bool typeInitialized = false; while (argc > 0) { std::string arg(*argv); if (arg == "-png") { argc--; argv++; type = OutputType::png; typeInitialized = true; } else if (arg == "-pdf") { argc--; argv++; type = OutputType::pdf; typeInitialized = true; } else if (arg == "-ps") { argc--; argv++; type = OutputType::ps; typeInitialized = true; } else if (arg == "-svg") { argc--; argv++; type = OutputType::svg; typeInitialized = true; } else { // filename if (!typeInitialized) { return false; } outputType = type; filename = *argv; argc--; argv++; return true; } } return false; } // "../a/b/foo.pdf" => "foo" static std::string getBaseName(const std::string &filename) { // strip everything up to last '/' size_t slash_pos = filename.find_last_of('/'); std::string basename; if (slash_pos != std::string::npos) { basename = filename.substr(slash_pos + 1, std::string::npos); } else { basename = filename; } // remove .pdf extension size_t dot_pos = basename.find_last_of('.'); if (dot_pos != std::string::npos) { if (basename.compare(dot_pos, std::string::npos, ".pdf") == 0) { basename.erase(dot_pos); } } return basename; } // Represents an input file on the command line struct InputFile { InputFile(const std::string &filename, OutputType typeA) : type(typeA) { document = std::make_shared(filename); basename = getBaseName(filename); currentPage = 0; numPages = 0; // filled in later numDigits = 0; // filled in later } std::shared_ptr document; OutputType type; // Used when creating jobs for this InputFile int currentPage; std::string basename; int numPages; int numDigits; }; // eg "basename.out-123.png" or "basename.out.pdf" static std::string getOutputName(const InputFile &input) { std::string output; char buf[30]; switch (input.type) { case OutputType::png: std::snprintf(buf, sizeof(buf), ".out-%0*d.png", input.numDigits, input.currentPage); output = input.basename + buf; break; case OutputType::pdf: output = input.basename + ".out.pdf"; break; case OutputType::ps: output = input.basename + ".out.ps"; break; case OutputType::svg: output = input.basename + ".out.svg"; break; } return output; } int main(int argc, char *argv[]) { if (argc < 3) { printUsage(); exit(1); } // skip program name argc--; argv++; int numThreads; bool documentPriority; if (!getThreadsAndPriority(argc, argv, numThreads, documentPriority)) { printUsage(); exit(1); } globalParams = std::make_unique(); std::shared_ptr jobQueue = std::make_shared(); std::vector threads; threads.reserve(4); for (int i = 0; i < numThreads; i++) { threads.emplace_back(runThread, jobQueue); } std::vector inputFiles; while (argc > 0) { std::string filename; OutputType type; if (!getOutputTypeAndDocument(argc, argv, type, filename)) { printUsage(); exit(1); } InputFile input(filename, type); inputFiles.push_back(input); } if (documentPriority) { while (true) { bool jobAdded = false; for (auto &input : inputFiles) { if (input.numPages == 0) { // first time seen if (input.type == OutputType::png) { input.numPages = input.document->getDoc()->getNumPages(); input.numDigits = numberOfCharacters(input.numPages); } else { input.numPages = 1; // Use 1 for vector output as there is only one output file } } if (input.currentPage < input.numPages) { input.currentPage++; std::string output = getOutputName(input); std::unique_ptr job = std::make_unique(input.type, input.document, input.currentPage, output); jobQueue->pushJob(job); jobAdded = true; } } if (!jobAdded) { break; } } } else { for (auto &input : inputFiles) { if (input.type == OutputType::png) { input.numPages = input.document->getDoc()->getNumPages(); input.numDigits = numberOfCharacters(input.numPages); for (int i = 1; i <= input.numPages; i++) { input.currentPage = i; std::string output = getOutputName(input); std::unique_ptr job = std::make_unique(input.type, input.document, input.currentPage, output); jobQueue->pushJob(job); } } else { std::string output = getOutputName(input); std::unique_ptr job = std::make_unique(input.type, input.document, 1, output); jobQueue->pushJob(job); } } } jobQueue->shutdown(); jobQueue->waitUntilEmpty(); for (int i = 0; i < numThreads; i++) { threads[i].join(); } return 0; } poppler-24.02.0/test/goostring-format-checker/000077500000000000000000000000001455701731300212305ustar00rootroot00000000000000poppler-24.02.0/test/goostring-format-checker/README000066400000000000000000000013541455701731300221130ustar00rootroot00000000000000== Clang++ compiler plugin that checks usage of GooString::format-like functions == 1) Compile the plugin with: clang++ -shared -o goostring-format-checker.so goostring-format-checker.cc -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS 2) Compile poppler and pass the following options to the clang++ compiler: -Xclang -load -Xclang goostring-format-checker.so -Xclang -plugin -Xclang goostring-format-check Example: $ clang++ -fPIC -shared -o goostring-format-checker.so goostring-format-checker.cc -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS $ export CXX=clang++ $ export CXXFLAGS="-Xclang -load -Xclang $PWD/goostring-format-checker.so -Xclang -add-plugin -Xclang goostring-format-checker" $ mkdir build $ cd build $ cmake ../../.. $ make poppler-24.02.0/test/goostring-format-checker/goostring-format-checker.cc000066400000000000000000000404561455701731300264530ustar00rootroot00000000000000/* * goostring-format-checker.cc * * This file is licensed under the GPLv2 or later * * Clang++ compiler plugin that checks usage of GooString::format-like functions * * Copyright (C) 2014 Fabio D'Urso * Copyright (C) 2021 Albert Astals Cid */ #include #include #include #include #include #include #include using namespace clang; namespace { class GooStringFormatCheckerVisitor : public RecursiveASTVisitor { public: explicit GooStringFormatCheckerVisitor(CompilerInstance *compInst); bool VisitFunctionDecl(FunctionDecl *funcDecl); bool VisitCallExpr(CallExpr *callExpr); private: /* Returns the index of the format argument, or -1 if the function must * not be checked */ int findFormatArgumentIndex(const FunctionDecl *funcDecl) const; /* Returns the SourceLocation of the n-th character */ SourceLocation getLocationOfCharacter(const StringLiteral *strLiteral, unsigned n); /* Validates usage of a placeholder and returns the corresponding * argument index, or -1 in case of errors */ int verifyPlaceholder(const CallExpr *callExpr, const SourceLocation &placeholderLocation, std::string &placeholderText, int baseArgIdx) const; CompilerInstance *compInst; DiagnosticsEngine *diag; unsigned diag_badFuncZeroArgs; unsigned diag_badFuncNonVariadic; unsigned diag_badFuncLastArgInvalidType; unsigned diag_notStringLiteral; unsigned diag_notPlainASCII; unsigned diag_wrongOrder; unsigned diag_unescapedBracket; unsigned diag_unterminatedPlaceholder; unsigned diag_unconsumedArgs; unsigned diag_missingColon; unsigned diag_missingArgNumber; unsigned diag_badArgNumber; unsigned diag_argumentNotPresent; unsigned diag_badPrecision; unsigned diag_badType; unsigned diag_wrongArgExprType; }; GooStringFormatCheckerVisitor::GooStringFormatCheckerVisitor(CompilerInstance *compInst) : compInst(compInst) { diag = &compInst->getDiagnostics(); diag_badFuncZeroArgs = diag->getCustomDiagID(DiagnosticsEngine::Error, "Cannot enforce format string checks on a function that takes no arguments"); diag_badFuncNonVariadic = diag->getCustomDiagID(DiagnosticsEngine::Error, "Cannot enforce format string checks on a non-variadic function"); diag_badFuncLastArgInvalidType = diag->getCustomDiagID(DiagnosticsEngine::Error, "Cannot enforce format string checks if the last non-variadic argument is not const char *"); diag_notStringLiteral = diag->getCustomDiagID(DiagnosticsEngine::Warning, "Format string is not a string literal. Skipping format checks"); diag_notPlainASCII = diag->getCustomDiagID(DiagnosticsEngine::Warning, "Format string contains non-ASCII or NUL characters. Skipping format checks"); diag_wrongOrder = diag->getCustomDiagID(DiagnosticsEngine::Error, "Argument %0 must be consumed before argument %1"); diag_unescapedBracket = diag->getCustomDiagID(DiagnosticsEngine::Error, "Unescaped '}' character"); diag_unterminatedPlaceholder = diag->getCustomDiagID(DiagnosticsEngine::Error, "Unterminated placeholder"); diag_unconsumedArgs = diag->getCustomDiagID(DiagnosticsEngine::Warning, "Unconsumed argument(s)"); diag_missingColon = diag->getCustomDiagID(DiagnosticsEngine::Error, "Invalid placeholder '{%0}': missing colon character"); diag_missingArgNumber = diag->getCustomDiagID(DiagnosticsEngine::Error, "Invalid placeholder '{%0}': missing number"); diag_badArgNumber = diag->getCustomDiagID(DiagnosticsEngine::Error, "Invalid placeholder '{%0}': bad number"); diag_argumentNotPresent = diag->getCustomDiagID(DiagnosticsEngine::Error, "Argument for placeholder '{%0}' is not present"); diag_badPrecision = diag->getCustomDiagID(DiagnosticsEngine::Error, "Invalid placeholder '{%0}': bad value"); diag_badType = diag->getCustomDiagID(DiagnosticsEngine::Error, "Invalid placeholder '{%0}': bad specifier"); diag_wrongArgExprType = diag->getCustomDiagID(DiagnosticsEngine::Error, "Expected %0 for placeholder '{%1}', found %2"); } bool GooStringFormatCheckerVisitor::VisitFunctionDecl(FunctionDecl *funcDecl) { findFormatArgumentIndex(funcDecl); // Spot misuse of the "gooformat" annotation return true; } bool GooStringFormatCheckerVisitor::VisitCallExpr(CallExpr *callExpr) { /*** Locate format argument or skip calls that needn't be checked ***/ const int formatArgIdx = findFormatArgumentIndex(callExpr->getDirectCallee()); if (formatArgIdx == -1) return true; /*** Obtain format string value ***/ const Expr *formatArgExpr = callExpr->getArg(formatArgIdx); while (formatArgExpr->getStmtClass() == Stmt::ImplicitCastExprClass) { formatArgExpr = static_cast(formatArgExpr)->getSubExpr(); } if (formatArgExpr->getStmtClass() != Stmt::StringLiteralClass) { diag->Report(formatArgExpr->getExprLoc(), diag_notStringLiteral); return true; } const StringLiteral *formatArgStrLiteral = static_cast(formatArgExpr); if (formatArgStrLiteral->containsNonAsciiOrNull()) { diag->Report(formatArgExpr->getExprLoc(), diag_notPlainASCII); return true; } /*** Parse format string and verify arguments ***/ const std::string format = formatArgStrLiteral->getString().str(); /* Keeps track of whether we are currently parsing a character contained * within '{' ... '}'. If set, current_placeholder contains the contents * parsed so far (without brackets) */ bool in_placeholder = false; std::string current_placeholder; // Source location of the current placeholder's opening bracket SourceLocation placeholderLoc; /* Keeps track of the next expected argument number, to check that * arguments are first consumed in order (eg {0:d}{2:d}{1:d} is wrong). * Note that it's possible to "look back" at already consumed * arguments (eg {0:d}{1:d}{0:d} is OK) */ int nextExpectedArgNum = 0; for (unsigned i = 0; i < format.length(); i++) { if (in_placeholder) { // Have we reached the end of the placeholder? if (format[i] == '}') { in_placeholder = false; // Verifies the placeholder and returns the argument number const int foundArgNum = verifyPlaceholder(callExpr, placeholderLoc, current_placeholder, formatArgIdx + 1); // If the placeholder wasn't valid, disable argument order checks if (foundArgNum == -1) { nextExpectedArgNum = -1; } // If argument order checks are enabled, let's check! if (nextExpectedArgNum != -1) { if (foundArgNum == nextExpectedArgNum) { nextExpectedArgNum++; } else if (foundArgNum > nextExpectedArgNum) { diag->Report(placeholderLoc, diag_wrongOrder) << nextExpectedArgNum << foundArgNum; nextExpectedArgNum = -1; // disable further checks } } } else { current_placeholder += format[i]; } } else if (format[i] == '{') { // If we find a '{' then a placeholder is starting... in_placeholder = true; current_placeholder = ""; placeholderLoc = getLocationOfCharacter(formatArgStrLiteral, i); // ...unless it's followed by another '{' (escape sequence) if (i + 1 < format.length() && format[i + 1] == '{') { i++; // skip next '{' character in_placeholder = false; } } else if (format[i] == '}') { /* If we have found a '}' and we're not in a placeholder, * then it *MUST* be followed by another '}' (escape sequence) */ if (i + 1 >= format.length() || format[i + 1] != '}') { diag->Report(getLocationOfCharacter(formatArgStrLiteral, i), diag_unescapedBracket); } else { i++; // skip next '}' character } } } /* If we've reached the end of the format string and in_placeholder is * still set, then the last placeholder wasn't terminated properly */ if (in_placeholder) diag->Report(placeholderLoc, diag_unterminatedPlaceholder); int unconsumedArgs = callExpr->getNumArgs() - (formatArgIdx + 1 + nextExpectedArgNum); if (unconsumedArgs > 0) diag->Report(callExpr->getArg(callExpr->getNumArgs() - unconsumedArgs)->getExprLoc(), diag_unconsumedArgs); return true; } int GooStringFormatCheckerVisitor::findFormatArgumentIndex(const FunctionDecl *funcDecl) const { if (!funcDecl) return -1; AnnotateAttr *annotation = NULL; for (specific_attr_iterator it = funcDecl->specific_attr_begin(); it != funcDecl->specific_attr_end() && !annotation; ++it) { if (it->getAnnotation() == "gooformat") annotation = *it; } // If this function hasn't got the "gooformat" annotation on it if (!annotation) return -1; if (funcDecl->getNumParams() == 0) { diag->Report(annotation->getLocation(), diag_badFuncZeroArgs); return -1; } if (!funcDecl->isVariadic()) { diag->Report(annotation->getLocation(), diag_badFuncNonVariadic); return -1; } // Assume the last non-variadic argument is the format specifier const int formatArgIdx = funcDecl->getNumParams() - 1; const QualType formatArgType = funcDecl->getParamDecl(formatArgIdx)->getType(); if (formatArgType.getAsString() != "const char *") { diag->Report(annotation->getLocation(), diag_badFuncLastArgInvalidType); return -1; } return formatArgIdx; } SourceLocation GooStringFormatCheckerVisitor::getLocationOfCharacter(const StringLiteral *strLiteral, unsigned n) { return strLiteral->getLocationOfByte(n, compInst->getSourceManager(), compInst->getLangOpts(), compInst->getTarget()); } int GooStringFormatCheckerVisitor::verifyPlaceholder(const CallExpr *callExpr, const SourceLocation &placeholderLocation, std::string &placeholderText, int baseArgIdx) const { // Find the colon that separates the argument number and the format specifier const size_t delim = placeholderText.find(':'); if (delim == std::string::npos) { diag->Report(placeholderLocation, diag_missingColon) << placeholderText; return -1; } if (delim == 0) { diag->Report(placeholderLocation, diag_missingArgNumber) << placeholderText; return -1; } for (unsigned int i = 0; i < delim; i++) { if (!isdigit(placeholderText[i])) { diag->Report(placeholderLocation, diag_badArgNumber) << placeholderText; return -1; } } // Extract argument number and its actual position in the call's argument list const int argNum = atoi(placeholderText.substr(0, delim).c_str()); const int argIdx = baseArgIdx + argNum; if (argIdx >= callExpr->getNumArgs()) { diag->Report(placeholderLocation, diag_argumentNotPresent) << placeholderText; return argNum; } // Check and strip width/precision specifiers std::string format = placeholderText.substr(delim + 1); bool dot_found = false; while (isdigit(format[0]) || format[0] == '.') { if (format[0] == '.') { if (dot_found) { diag->Report(placeholderLocation, diag_badPrecision) << placeholderText; return argNum; } dot_found = true; } format = format.substr(1); } const Expr *argExpr = callExpr->getArg(argIdx); const QualType qualType = argExpr->getType(); const Type *valueType = qualType->getUnqualifiedDesugaredType(); if (format == "d" || format == "x" || format == "X" || format == "o" || format == "b" || format == "w") { if (!valueType->isSpecificBuiltinType(BuiltinType::Int)) { diag->Report(argExpr->getExprLoc(), diag_wrongArgExprType) << "int" << placeholderText << qualType.getAsString(); } } else if (format == "ud" || format == "ux" || format == "uX" || format == "uo" || format == "ub") { if (!valueType->isSpecificBuiltinType(BuiltinType::UInt)) { diag->Report(argExpr->getExprLoc(), diag_wrongArgExprType) << "unsigned int" << placeholderText << qualType.getAsString(); } } else if (format == "ld" || format == "lx" || format == "lX" || format == "lo" || format == "lb") { if (!valueType->isSpecificBuiltinType(BuiltinType::Long)) { diag->Report(argExpr->getExprLoc(), diag_wrongArgExprType) << "long" << placeholderText << qualType.getAsString(); } } else if (format == "uld" || format == "ulx" || format == "ulX" || format == "ulo" || format == "ulb") { if (!valueType->isSpecificBuiltinType(BuiltinType::ULong)) { diag->Report(argExpr->getExprLoc(), diag_wrongArgExprType) << "unsigned long" << placeholderText << qualType.getAsString(); } } else if (format == "lld" || format == "llx" || format == "llX" || format == "llo" || format == "llb") { if (!valueType->isSpecificBuiltinType(BuiltinType::LongLong)) { diag->Report(argExpr->getExprLoc(), diag_wrongArgExprType) << "long long" << placeholderText << qualType.getAsString(); } } else if (format == "ulld" || format == "ullx" || format == "ullX" || format == "ullo" || format == "ullb") { if (!valueType->isSpecificBuiltinType(BuiltinType::ULongLong)) { diag->Report(argExpr->getExprLoc(), diag_wrongArgExprType) << "unsigned long long" << placeholderText << qualType.getAsString(); } } else if (format == "f" || format == "g" || format == "gs") { if (!valueType->isSpecificBuiltinType(BuiltinType::Double)) { diag->Report(argExpr->getExprLoc(), diag_wrongArgExprType) << "float or double" << placeholderText << qualType.getAsString(); } } else if (format == "c") { if (!valueType->isSpecificBuiltinType(BuiltinType::UInt) && !valueType->isSpecificBuiltinType(BuiltinType::Int)) { diag->Report(argExpr->getExprLoc(), diag_wrongArgExprType) << "char, short or int" << placeholderText << qualType.getAsString(); } } else if (format == "s") { if (!valueType->isPointerType() || !valueType->getPointeeType()->getUnqualifiedDesugaredType()->isCharType()) { diag->Report(argExpr->getExprLoc(), diag_wrongArgExprType) << "char *" << placeholderText << qualType.getAsString(); } } else if (format == "t") { const CXXRecordDecl *pointeeType = valueType->isPointerType() ? valueType->getPointeeType()->getAsCXXRecordDecl() : 0; if (pointeeType == 0 || pointeeType->getQualifiedNameAsString() != "GooString") { diag->Report(argExpr->getExprLoc(), diag_wrongArgExprType) << "GooString *" << placeholderText << qualType.getAsString(); } } else { diag->Report(placeholderLocation, diag_badType) << placeholderText; return argNum; } return argNum; } class GooStringFormatCheckerConsumer : public clang::ASTConsumer { public: GooStringFormatCheckerConsumer(CompilerInstance *compInst) : visitor(compInst) { } virtual void HandleTranslationUnit(clang::ASTContext &ctx) { visitor.TraverseDecl(ctx.getTranslationUnitDecl()); } private: GooStringFormatCheckerVisitor visitor; }; class GooStringFormatCheckerAction : public PluginASTAction { protected: std::unique_ptr CreateASTConsumer(CompilerInstance &compInst, llvm::StringRef inFile) { return std::make_unique(&compInst); } bool ParseArgs(const CompilerInstance &compInst, const std::vector &args) { if (args.size() != 0) { DiagnosticsEngine &D = compInst.getDiagnostics(); D.Report(D.getCustomDiagID(DiagnosticsEngine::Error, "goostring-format-checker takes no arguments")); return false; } else { return true; } } }; } static FrontendPluginRegistry::Add X("goostring-format-checker", "Checks usage of GooString::format-like functions"); poppler-24.02.0/test/gtk-test.cc000066400000000000000000000261551455701731300164070ustar00rootroot00000000000000#include #include #include #include #include "Object.h" #include "SplashOutputDev.h" #include "GfxState.h" #include #include "PDFDoc.h" #include "GlobalParams.h" #include "ErrorCodes.h" #include #include #include #include #include static int requested_page = 0; static gboolean cairo_output = FALSE; static gboolean splash_output = FALSE; #ifndef G_OS_WIN32 static gboolean args_are_fds = FALSE; #endif static const char **file_arguments = nullptr; static const GOptionEntry options[] = { { "cairo", 'c', 0, G_OPTION_ARG_NONE, &cairo_output, "Cairo Output Device", nullptr }, { "splash", 's', 0, G_OPTION_ARG_NONE, &splash_output, "Splash Output Device", nullptr }, { "page", 'p', 0, G_OPTION_ARG_INT, &requested_page, "Page number", "PAGE" }, #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…" }, {} }; static GList *view_list = nullptr; //------------------------------------------------------------------------ #define xOutMaxRGBCube 6 // max size of RGB color cube //------------------------------------------------------------------------ // GDKSplashOutputDev //------------------------------------------------------------------------ class GDKSplashOutputDev : public SplashOutputDev { public: GDKSplashOutputDev(GdkScreen *screen, void (*redrawCbkA)(void *data), void *redrawCbkDataA, SplashColor sc); ~GDKSplashOutputDev() override; //----- initialization and control // End a page. void endPage() override; // Dump page contents to display. void dump() override; //----- update text state void updateFont(GfxState *state) override; //----- special access // Clear out the document (used when displaying an empty window). void clear(); // Copy the rectangle (srcX, srcY, width, height) to (destX, destY) // in destDC. void redraw(int srcX, int srcY, cairo_t *cr, int destX, int destY, int width, int height); private: int incrementalUpdate; void (*redrawCbk)(void *data); void *redrawCbkData; }; typedef struct { PopplerDocument *doc; GtkWidget *drawing_area; GtkWidget *spin_button; cairo_surface_t *surface; GDKSplashOutputDev *out; } View; //------------------------------------------------------------------------ // Constants and macros //------------------------------------------------------------------------ #define xoutRound(x) ((int)(x + 0.5)) //------------------------------------------------------------------------ // GDKSplashOutputDev //------------------------------------------------------------------------ GDKSplashOutputDev::GDKSplashOutputDev(GdkScreen *screen, void (*redrawCbkA)(void *data), void *redrawCbkDataA, SplashColor sc) : SplashOutputDev(splashModeRGB8, 4, false, sc), incrementalUpdate(1) { redrawCbk = redrawCbkA; redrawCbkData = redrawCbkDataA; } GDKSplashOutputDev::~GDKSplashOutputDev() { } void GDKSplashOutputDev::clear() { startDoc(nullptr); startPage(0, nullptr, nullptr); } void GDKSplashOutputDev::endPage() { SplashOutputDev::endPage(); if (!incrementalUpdate) { (*redrawCbk)(redrawCbkData); } } void GDKSplashOutputDev::dump() { if (incrementalUpdate && redrawCbk) { (*redrawCbk)(redrawCbkData); } } void GDKSplashOutputDev::updateFont(GfxState *state) { SplashOutputDev::updateFont(state); } void GDKSplashOutputDev::redraw(int srcX, int srcY, cairo_t *cr, int destX, int destY, int width, int height) { GdkPixbuf *pixbuf; int gdk_rowstride; gdk_rowstride = getBitmap()->getRowSize(); pixbuf = gdk_pixbuf_new_from_data(getBitmap()->getDataPtr() + srcY * gdk_rowstride + srcX * 3, GDK_COLORSPACE_RGB, FALSE, 8, width, height, gdk_rowstride, nullptr, nullptr); gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0); cairo_paint(cr); g_object_unref(pixbuf); } static gboolean drawing_area_draw(GtkWidget *drawing_area, cairo_t *cr, View *view) { GdkRectangle document; GdkRectangle clip; GdkRectangle draw; document.x = 0; document.y = 0; if (cairo_output) { document.width = cairo_image_surface_get_width(view->surface); document.height = cairo_image_surface_get_height(view->surface); } else { document.width = view->out->getBitmapWidth(); document.height = view->out->getBitmapHeight(); } if (!gdk_cairo_get_clip_rectangle(cr, &clip)) { return FALSE; } if (!gdk_rectangle_intersect(&document, &clip, &draw)) { return FALSE; } if (cairo_output) { cairo_set_source_surface(cr, view->surface, 0, 0); cairo_paint(cr); } else { view->out->redraw(draw.x, draw.y, cr, draw.x, draw.y, draw.width, draw.height); } return TRUE; } static void view_set_page(View *view, int page) { int w, h; if (cairo_output) { cairo_t *cr; double width, height; PopplerPage *poppler_page; poppler_page = poppler_document_get_page(view->doc, page); poppler_page_get_size(poppler_page, &width, &height); w = (int)ceil(width); h = (int)ceil(height); if (view->surface) { cairo_surface_destroy(view->surface); } view->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); cr = cairo_create(view->surface); poppler_page_render(poppler_page, cr); cairo_set_operator(cr, CAIRO_OPERATOR_DEST_OVER); cairo_set_source_rgb(cr, 1., 1., 1.); cairo_paint(cr); cairo_destroy(cr); g_object_unref(poppler_page); } else { view->doc->doc->displayPage(view->out, page + 1, 72, 72, 0, false, true, true); w = view->out->getBitmapWidth(); h = view->out->getBitmapHeight(); } gtk_widget_set_size_request(view->drawing_area, w, h); gtk_widget_queue_draw(view->drawing_area); gtk_spin_button_set_value(GTK_SPIN_BUTTON(view->spin_button), page); } static void redraw_callback(void *data) { View *view = (View *)data; gtk_widget_queue_draw(view->drawing_area); } static void view_free(View *view) { if (G_UNLIKELY(!view)) { return; } g_object_unref(view->doc); delete view->out; cairo_surface_destroy(view->surface); g_slice_free(View, view); } static void destroy_window_callback(GtkWindow *window, View *view) { view_list = g_list_remove(view_list, view); view_free(view); if (!view_list) { gtk_main_quit(); } } static void page_changed_callback(GtkSpinButton *button, View *view) { int page; page = gtk_spin_button_get_value_as_int(button); view_set_page(view, page); } static View *view_new(PopplerDocument *doc) { View *view; GtkWidget *window; GtkWidget *sw; GtkWidget *vbox, *hbox; guint n_pages; PopplerPage *page; view = g_slice_new0(View); view->doc = doc; window = gtk_window_new(GTK_WINDOW_TOPLEVEL); g_signal_connect(window, "destroy", G_CALLBACK(destroy_window_callback), view); page = poppler_document_get_page(doc, 0); if (page) { double width, height; poppler_page_get_size(page, &width, &height); gtk_window_set_default_size(GTK_WINDOW(window), (gint)width, (gint)height); g_object_unref(page); } vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); 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); gtk_widget_show(sw); hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); n_pages = poppler_document_get_n_pages(doc); view->spin_button = gtk_spin_button_new_with_range(0, n_pages - 1, 1); g_signal_connect(view->spin_button, "value-changed", G_CALLBACK(page_changed_callback), view); gtk_box_pack_end(GTK_BOX(hbox), view->spin_button, FALSE, TRUE, 0); gtk_widget_show(view->spin_button); gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); gtk_widget_show(hbox); gtk_container_add(GTK_CONTAINER(window), vbox); gtk_widget_show(vbox); gtk_widget_show(window); if (!cairo_output) { SplashColor sc = { 255, 255, 255 }; view->out = new GDKSplashOutputDev(gtk_widget_get_screen(window), redraw_callback, (void *)view, sc); view->out->startDoc(view->doc->doc); } g_signal_connect(view->drawing_area, "draw", G_CALLBACK(drawing_area_draw), view); return view; } int main(int argc, char *argv[]) { GOptionContext *ctx; if (argc == 1) { char *basename = g_path_get_basename(argv[0]); g_printerr("usage: %s PDF-FILES…\n", basename); g_free(basename); return -1; } ctx = g_option_context_new(nullptr); g_option_context_add_main_entries(ctx, options, "main"); g_option_context_parse(ctx, &argc, &argv, nullptr); g_option_context_free(ctx); gtk_init(&argc, &argv); globalParams = std::make_unique(); for (int i = 0; file_arguments[i]; i++) { View *view; GFile *file; PopplerDocument *doc = nullptr; GError *error = nullptr; const char *arg; arg = file_arguments[i]; #ifndef G_OS_WIN32 if (args_are_fds) { char *end; gint64 v; errno = 0; end = nullptr; v = g_ascii_strtoll(arg, &end, 10); if (errno || end == arg || v == -1 || v < G_MININT || v > G_MAXINT) { g_set_error(&error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, "Failed to parse \"%s\" as file descriptor number", arg); } else { doc = poppler_document_new_from_fd(int(v), nullptr, &error); } } else #endif /* !G_OS_WIN32 */ { file = g_file_new_for_commandline_arg(arg); doc = poppler_document_new_from_gfile(file, nullptr, nullptr, &error); if (!doc) { gchar *uri; uri = g_file_get_uri(file); g_prefix_error(&error, "%s: ", uri); g_free(uri); } g_object_unref(file); } if (doc) { view = view_new(doc); view_list = g_list_prepend(view_list, view); view_set_page(view, CLAMP(requested_page, 0, poppler_document_get_n_pages(doc) - 1)); } else { g_printerr("Error opening document: %s\n", error->message); g_error_free(error); } } if (view_list != nullptr) { gtk_main(); } return 0; } poppler-24.02.0/test/image-embedding.cc000066400000000000000000000101111455701731300176240ustar00rootroot00000000000000//======================================================================== // // image-embedding.cc // A test util to check ImageEmbeddingUtils::embed(). // // This file is licensed under the GPLv2 or later // // Copyright (C) 2021 Georgiy Sgibnev . Work sponsored by lab50.net. // Copyright (C) 2022 by Albert Astals Cid // //======================================================================== #include #include #include #include "utils/parseargs.h" #include "goo/GooString.h" #include "Object.h" #include "Dict.h" #include "PDFDoc.h" #include "PDFDocFactory.h" #include "ImageEmbeddingUtils.h" static int depth = 0; static GooString colorSpace; static GooString filter; static bool smask = false; static bool fail = false; static bool printHelp = false; static const ArgDesc argDesc[] = { { "-depth", argInt, &depth, 0, "XObject's property 'BitsPerComponent'" }, { "-colorspace", argGooString, &colorSpace, 0, "XObject's property 'ColorSpace'" }, { "-filter", argGooString, &filter, 0, "XObject's property 'Filter'" }, { "-smask", argFlag, &smask, 0, "SMask should exist" }, { "-fail", argFlag, &fail, 0, "the image embedding API is expected to fail" }, { "-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" }, {} }; int main(int argc, char *argv[]) { // Parse args. const bool ok = parseArgs(argDesc, &argc, argv); if (!ok || (argc != 3) || printHelp) { printUsage(argv[0], "PDF-FILE IMAGE-FILE", argDesc); return (printHelp) ? 0 : 1; } const GooString docPath(argv[1]); const GooString imagePath(argv[2]); auto doc = std::unique_ptr(PDFDocFactory().createPDFDoc(docPath)); if (!doc->isOk()) { fprintf(stderr, "Error opening input PDF file.\n"); return 1; } // Embed an image. Ref baseImageRef = ImageEmbeddingUtils::embed(doc->getXRef(), imagePath.toStr()); if (baseImageRef == Ref::INVALID()) { if (fail) { return 0; } else { fprintf(stderr, "ImageEmbeddingUtils::embed() failed.\n"); return 1; } } // Save the updated PDF document. // const GooString outputPathSuffix(".pdf"); // const GooString outputPath = GooString(&imagePath, &outputPathSuffix); // doc->saveAs(&outputPath, writeForceRewrite); // Check the base image. Object baseImageObj = Object(baseImageRef).fetch(doc->getXRef()); Dict *baseImageDict = baseImageObj.streamGetDict(); if (std::string("XObject") != baseImageDict->lookup("Type").getName()) { fprintf(stderr, "A problem with Type.\n"); return 1; } if (std::string("Image") != baseImageDict->lookup("Subtype").getName()) { fprintf(stderr, "A problem with Subtype.\n"); return 1; } if (depth > 0) { if (baseImageDict->lookup("BitsPerComponent").getInt() != depth) { fprintf(stderr, "A problem with BitsPerComponent.\n"); return 1; } } if (!colorSpace.toStr().empty()) { if (colorSpace.cmp(baseImageDict->lookup("ColorSpace").getName()) != 0) { fprintf(stderr, "A problem with ColorSpace.\n"); return 1; } } if (!filter.toStr().empty()) { if (filter.cmp(baseImageDict->lookup("Filter").getName()) != 0) { fprintf(stderr, "A problem with Filter.\n"); return 1; } } if (smask) { Object maskObj = baseImageDict->lookup("SMask"); if (!maskObj.isStream()) { fprintf(stderr, "A problem with SMask.\n"); return 1; } } return 0; } poppler-24.02.0/test/pdf-fullrewrite.cc000066400000000000000000000262571455701731300177630ustar00rootroot00000000000000//======================================================================== // // pdf-fullrewrite.cc // // Copyright 2007 Julien Rebetez // Copyright 2012 Fabio D'Urso // Copyright 2022 Albert Astals Cid // //======================================================================== #include "GlobalParams.h" #include "Error.h" #include "Object.h" #include "PDFDoc.h" #include "XRef.h" #include "goo/GooString.h" #include "utils/parseargs.h" static bool compareDocuments(PDFDoc *origDoc, PDFDoc *newDoc); static bool compareObjects(const Object *objA, const Object *objB); static char ownerPassword[33] = "\001"; static char userPassword[33] = "\001"; static bool forceIncremental = false; static bool checkOutput = false; static bool printHelp = false; static const ArgDesc argDesc[] = { { "-opw", argString, ownerPassword, sizeof(ownerPassword), "owner password (for encrypted files)" }, { "-upw", argString, userPassword, sizeof(userPassword), "user password (for encrypted files)" }, { "-i", argFlag, &forceIncremental, 0, "incremental update mode" }, { "-check", argFlag, &checkOutput, 0, "verify the generated document" }, { "-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" }, {} }; int main(int argc, char *argv[]) { PDFDoc *doc = nullptr; PDFDoc *docOut = nullptr; std::optional ownerPW; std::optional userPW; int res = 0; // parse args bool ok = parseArgs(argDesc, &argc, argv); if (!ok || (argc < 3) || printHelp) { printUsage(argv[0], "INPUT-FILE OUTPUT-FILE", argDesc); if (!printHelp) { res = 1; } goto done; } if (ownerPassword[0] != '\001') { ownerPW = GooString(ownerPassword); } if (userPassword[0] != '\001') { userPW = GooString(userPassword); } // load input document globalParams = std::make_unique(); doc = new PDFDoc(std::make_unique(argv[1]), ownerPW, userPW); if (!doc->isOk()) { fprintf(stderr, "Error loading input document\n"); res = 1; goto done; } // save it back (in rewrite or incremental update mode) if (doc->saveAs(*doc->getFileName(), forceIncremental ? writeForceIncremental : writeForceRewrite) != 0) { fprintf(stderr, "Error saving document\n"); res = 1; goto done; } if (checkOutput) { // open the generated document to verify it docOut = new PDFDoc(std::make_unique(argv[2]), ownerPW, userPW); if (!docOut->isOk()) { fprintf(stderr, "Error loading generated document\n"); res = 1; } else if (!compareDocuments(doc, docOut)) { fprintf(stderr, "Verification failed\n"); res = 1; } } done: delete docOut; delete doc; return res; } static bool compareDictionaries(Dict *dictA, Dict *dictB) { const int length = dictA->getLength(); if (dictB->getLength() != length) { return false; } /* Check that every key in dictA is contained in dictB. * Since keys are unique and we've already checked that dictA and dictB * contain the same number of entries, we don't need to check that every key * in dictB is also contained in dictA */ for (int i = 0; i < length; ++i) { const char *key = dictA->getKey(i); const Object &valA = dictA->getValNF(i); const Object &valB = dictB->lookupNF(key); if (!compareObjects(&valA, &valB)) { return false; } } return true; } static bool compareObjects(const Object *objA, const Object *objB) { switch (objA->getType()) { case objBool: { if (objB->getType() != objBool) { return false; } else { return (objA->getBool() == objB->getBool()); } } case objInt: case objInt64: case objReal: { if (!objB->isNum()) { return false; } else { // Fuzzy comparison const double diff = objA->getNum() - objB->getNum(); return (-0.01 < diff) && (diff < 0.01); } } case objString: { if (objB->getType() != objString) { return false; } else { const GooString *strA = objA->getString(); const GooString *strB = objB->getString(); return (strA->cmp(strB) == 0); } } case objName: { if (objB->getType() != objName) { return false; } else { GooString nameA(objA->getName()); GooString nameB(objB->getName()); return (nameA.cmp(&nameB) == 0); } } case objNull: { if (objB->getType() != objNull) { return false; } else { return true; } } case objArray: { if (objB->getType() != objArray) { return false; } else { Array *arrayA = objA->getArray(); Array *arrayB = objB->getArray(); const int length = arrayA->getLength(); if (arrayB->getLength() != length) { return false; } else { for (int i = 0; i < length; ++i) { const Object &elemA = arrayA->getNF(i); const Object &elemB = arrayB->getNF(i); if (!compareObjects(&elemA, &elemB)) { return false; } } return true; } } } case objDict: { if (objB->getType() != objDict) { return false; } else { Dict *dictA = objA->getDict(); Dict *dictB = objB->getDict(); return compareDictionaries(dictA, dictB); } } case objStream: { if (objB->getType() != objStream) { return false; } else { Stream *streamA = objA->getStream(); Stream *streamB = objB->getStream(); if (!compareDictionaries(streamA->getDict(), streamB->getDict())) { return false; } else { int c; streamA->reset(); streamB->reset(); do { c = streamA->getChar(); if (c != streamB->getChar()) { return false; } } while (c != EOF); return true; } } return true; } case objRef: { if (objB->getType() != objRef) { return false; } else { const Ref refA = objA->getRef(); const Ref refB = objB->getRef(); return refA == refB; } } default: { fprintf(stderr, "compareObjects failed: unexpected object type %u\n", objA->getType()); return false; } } } static bool compareDocuments(PDFDoc *origDoc, PDFDoc *newDoc) { bool result = true; XRef *origXRef = origDoc->getXRef(); XRef *newXRef = newDoc->getXRef(); // Make sure that special flags are set in both documents origXRef->scanSpecialFlags(); newXRef->scanSpecialFlags(); // Compare XRef tables' size const int origNumObjects = origXRef->getNumObjects(); const int newNumObjects = newXRef->getNumObjects(); if (forceIncremental && origXRef->isXRefStream()) { // In case of incremental update, expect a new entry to be appended to store the new XRef stream if (origNumObjects + 1 != newNumObjects) { fprintf(stderr, "XRef table: Unexpected number of entries (%d+1 != %d)\n", origNumObjects, newNumObjects); result = false; } } else { // In all other cases the number of entries must be the same if (origNumObjects != newNumObjects) { fprintf(stderr, "XRef table: Different number of entries (%d != %d)\n", origNumObjects, newNumObjects); result = false; } } // Compare each XRef entry const int numObjects = (origNumObjects < newNumObjects) ? origNumObjects : newNumObjects; for (int i = 0; i < numObjects; ++i) { XRefEntryType origType = origXRef->getEntry(i)->type; XRefEntryType newType = newXRef->getEntry(i)->type; const int origGenNum = (origType != xrefEntryCompressed) ? origXRef->getEntry(i)->gen : 0; const int newGenNum = (newType != xrefEntryCompressed) ? newXRef->getEntry(i)->gen : 0; // Check that DontRewrite entries are freed in full rewrite mode if (!forceIncremental && origXRef->getEntry(i)->getFlag(XRefEntry::DontRewrite)) { if (newType != xrefEntryFree || origGenNum + 1 != newGenNum) { fprintf(stderr, "XRef entry %u: DontRewrite entry was not freed correctly\n", i); result = false; } continue; // There's nothing left to check for this entry } // Compare generation numbers // Object num 0 should always have gen 65535 according to specs, but some // documents have it set to 0. We always write 65535 in output if (i != 0) { if (origGenNum != newGenNum) { fprintf(stderr, "XRef entry %u: generation numbers differ (%d != %d)\n", i, origGenNum, newGenNum); result = false; continue; } } else { if (newGenNum != 65535) { fprintf(stderr, "XRef entry %u: generation number was expected to be 65535 (%d != 65535)\n", i, newGenNum); result = false; continue; } } // Compare object flags. A failure shows that there's some error in XRef::scanSpecialFlags() if (origXRef->getEntry(i)->flags != newXRef->getEntry(i)->flags) { fprintf(stderr, "XRef entry %u: flags detected by scanSpecialFlags differ (%d != %d)\n", i, origXRef->getEntry(i)->flags, newXRef->getEntry(i)->flags); result = false; } // Check that either both are free or both are in use if ((origType == xrefEntryFree) != (newType == xrefEntryFree)) { const char *origStatus = (origType == xrefEntryFree) ? "free" : "in use"; const char *newStatus = (newType == xrefEntryFree) ? "free" : "in use"; fprintf(stderr, "XRef entry %u: usage status differs (%s != %s)\n", i, origStatus, newStatus); result = false; continue; } // Skip free entries if (origType == xrefEntryFree) { continue; } // Compare contents Object origObj = origXRef->fetch(i, origGenNum); Object newObj = newXRef->fetch(i, newGenNum); if (!compareObjects(&origObj, &newObj)) { fprintf(stderr, "XRef entry %u: contents differ\n", i); result = false; } } return result; } poppler-24.02.0/test/pdf-inspector.cc000066400000000000000000000206241455701731300174150ustar00rootroot00000000000000//======================================================================== // // pdf-inspector.cc // // Copyright 2005 Jonathan Blandford // Copyright 2018 Adam Reichold // Copyright 2019, 2022 Albert Astals Cid // //======================================================================== #include #include #include #include #include #include "Object.h" #include "ProfileData.h" #include "GfxState.h" #include #include "CairoOutputDev.h" #include "PDFDoc.h" #include "GlobalParams.h" #include "ErrorCodes.h" #include // Mapping #include "pdf-operators.c" enum { OP_STRING, OP_COUNT, OP_TOTAL, OP_MIN, OP_MAX, N_COLUMNS }; class PdfInspector { public: PdfInspector(); void set_file_name(const char *file_name); void load(const char *file_name); void run(); void error_dialog(const char *error_message); void analyze_page(int page); private: static void on_file_activated(GtkWidget *widget, PdfInspector *inspector); static void on_selection_changed(GtkTreeSelection *selection, PdfInspector *inspector); static void on_analyze_clicked(GtkWidget *widget, PdfInspector *inspector); GtkBuilder *builder; GtkTreeModel *model; PDFDoc *doc; CairoOutputDev *output; }; PdfInspector::PdfInspector() { GtkWidget *widget; GError *error = nullptr; builder = gtk_builder_new(); if (!gtk_builder_add_from_file(builder, SRC_DIR "/pdf-inspector.ui", &error)) { g_warning("Couldn't load builder file: %s", error->message); g_error_free(error); } widget = GTK_WIDGET(gtk_builder_get_object(builder, "pdf_file_chooser_button")); g_signal_connect(widget, "selection-changed", G_CALLBACK(on_file_activated), this); widget = GTK_WIDGET(gtk_builder_get_object(builder, "analyze_button")); g_signal_connect(widget, "clicked", G_CALLBACK(on_analyze_clicked), this); // setup the TreeView widget = GTK_WIDGET(gtk_builder_get_object(builder, "pdf_tree_view")); g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)), "changed", G_CALLBACK(on_selection_changed), this); model = (GtkTreeModel *)gtk_list_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_INT, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_DOUBLE); gtk_tree_view_set_model(GTK_TREE_VIEW(widget), model); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(widget), 0, "Operation", gtk_cell_renderer_text_new(), "text", OP_STRING, NULL); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(widget), 1, "Count", gtk_cell_renderer_text_new(), "text", OP_COUNT, NULL); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(widget), 2, "Elapsed", gtk_cell_renderer_text_new(), "text", OP_TOTAL, NULL); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(widget), 3, "Min", gtk_cell_renderer_text_new(), "text", OP_MIN, NULL); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(widget), 4, "Max", gtk_cell_renderer_text_new(), "text", OP_MAX, NULL); for (int i = 0; i < N_COLUMNS; i++) { GtkTreeViewColumn *column; column = gtk_tree_view_get_column(GTK_TREE_VIEW(widget), i); gtk_tree_view_column_set_sort_column_id(column, i); } doc = nullptr; output = new CairoOutputDev(); output->setPrinting(false); // set up initial widgets load(nullptr); } void PdfInspector::set_file_name(const char *file_name) { GtkWidget *widget; widget = GTK_WIDGET(gtk_builder_get_object(builder, "pdf_file_chooser_button")); gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(widget), file_name); } void PdfInspector::on_file_activated(GtkWidget *widget, PdfInspector *inspector) { gchar *file_name; file_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget)); if (file_name) { inspector->load(file_name); } g_free(file_name); } void PdfInspector::on_selection_changed(GtkTreeSelection *selection, PdfInspector *inspector) { GtkWidget *label; size_t i; GtkTreeModel *model; GtkTreeIter iter; gchar *op = nullptr; label = GTK_WIDGET(gtk_builder_get_object(inspector->builder, "description_label")); gtk_label_set_markup(GTK_LABEL(label), "No Description"); if (gtk_tree_selection_get_selected(selection, &model, &iter)) { gtk_tree_model_get(model, &iter, OP_STRING, &op, -1); } if (op == nullptr) { return; } for (i = 0; i < G_N_ELEMENTS(op_mapping); i++) { if (!strcmp(op, op_mapping[i].op)) { gchar *text; text = g_strdup_printf("%s", op_mapping[i].description); gtk_label_set_markup(GTK_LABEL(label), text); g_free(text); break; } } g_free(op); } void PdfInspector::on_analyze_clicked(GtkWidget *widget, PdfInspector *inspector) { GtkWidget *spin; int page; spin = GTK_WIDGET(gtk_builder_get_object(inspector->builder, "pdf_spin")); page = (int)gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)); inspector->analyze_page(page); } void PdfInspector::analyze_page(int page) { GtkWidget *label; char *text; cairo_t *cr; cairo_surface_t *surface; label = GTK_WIDGET(gtk_builder_get_object(builder, "pdf_total_label")); output->startProfile(); gtk_list_store_clear(GTK_LIST_STORE(model)); GooTimer timer; surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, doc->getPageCropWidth(page + 1), doc->getPageCropHeight(page + 1)); cr = cairo_create(surface); cairo_surface_destroy(surface); output->setCairo(cr); cairo_destroy(cr); doc->displayPage(output, page + 1, 72, 72, 0, false, true, false); output->setCairo(nullptr); // Total time; text = g_strdup_printf("%g", timer.getElapsed()); gtk_label_set_text(GTK_LABEL(label), text); g_free(text); // Individual times; auto hash = output->endProfile(); for (const auto &kvp : *hash) { GtkTreeIter tree_iter; const auto *const data_p = &kvp.second; gtk_list_store_append(GTK_LIST_STORE(model), &tree_iter); gtk_list_store_set(GTK_LIST_STORE(model), &tree_iter, OP_STRING, kvp.first.c_str(), OP_COUNT, data_p->getCount(), OP_TOTAL, data_p->getTotal(), OP_MIN, data_p->getMin(), OP_MAX, data_p->getMax(), -1); } } void PdfInspector::load(const char *file_name) { GtkWidget *spin; GtkWidget *button; GtkWidget *label; // kill the old PDF file if (doc != nullptr) { delete doc; doc = nullptr; } // load the new file if (file_name) { doc = new PDFDoc(std::make_unique(file_name)); } if (doc && !doc->isOk()) { this->error_dialog("Failed to load file."); delete doc; doc = nullptr; } spin = GTK_WIDGET(gtk_builder_get_object(builder, "pdf_spin")); button = GTK_WIDGET(gtk_builder_get_object(builder, "analyze_button")); label = GTK_WIDGET(gtk_builder_get_object(builder, "pdf_total_label")); gtk_label_set_text(GTK_LABEL(label), ""); if (doc) { gtk_widget_set_sensitive(spin, TRUE); gtk_widget_set_sensitive(button, TRUE); gtk_widget_set_sensitive(label, TRUE); gtk_spin_button_set_range(GTK_SPIN_BUTTON(spin), 0, doc->getNumPages() - 1); gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), 0); output->startDoc(doc); } else { gtk_widget_set_sensitive(spin, FALSE); gtk_widget_set_sensitive(button, FALSE); gtk_widget_set_sensitive(label, FALSE); } } void PdfInspector::error_dialog(const char *error_message) { g_warning("%s", error_message); } void PdfInspector::run() { GtkWidget *dialog; dialog = GTK_WIDGET(gtk_builder_get_object(builder, "pdf_dialog")); gtk_dialog_run(GTK_DIALOG(dialog)); } int main(int argc, char *argv[]) { const char *file_name = nullptr; PdfInspector *inspector; gtk_init(&argc, &argv); globalParams = std::make_unique(); globalParams->setProfileCommands(true); globalParams->setPrintCommands(true); if (argc == 2) { file_name = argv[1]; } else if (argc > 2) { fprintf(stderr, "usage: %s [PDF-FILE]\n", argv[0]); return -1; } inspector = new PdfInspector(); if (file_name) { inspector->set_file_name(file_name); } inspector->run(); delete inspector; return 0; } poppler-24.02.0/test/pdf-inspector.ui000066400000000000000000000553521455701731300174530ustar00rootroot00000000000000 100 0 10 1 10 1 6 True PDF Inspector GTK_WINDOW_TOPLEVEL GTK_WIN_POS_NONE False 600 400 True False True False False GDK_WINDOW_TYPE_HINT_DIALOG GDK_GRAVITY_NORTH_WEST True True False 12 True GTK_BUTTONBOX_END True True True gtk-close True GTK_RELIEF_NORMAL True 0 False True GTK_PACK_END 6 True False 12 True 2 2 False 6 12 True _File: True False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 0 1 0 1 fill True Page Number False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 0 1 1 2 fill True Select A File GTK_FILE_CHOOSER_ACTION_OPEN True False -1 1 2 0 1 fill True True 1 0 False GTK_UPDATE_ALWAYS False False adjustment1 1 2 1 2 0 False True True 0 0.5 GTK_SHADOW_NONE True 0.5 0.5 1 1 0 0 12 0 True False 6 True True GTK_POLICY_AUTOMATIC GTK_POLICY_AUTOMATIC GTK_SHADOW_IN GTK_CORNER_TOP_LEFT True True True False False True False False False 0 True True True False False 6 12 True Total time elapsed: False True GTK_JUSTIFY_LEFT False False 0 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 1 0 1 1 True False True GTK_JUSTIFY_LEFT False False 0 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 1 1 1 2 True Description: False False GTK_JUSTIFY_LEFT False False 0 0 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 1 0 0 1 True <i>No Description</i> False True GTK_JUSTIFY_LEFT True False 0 0 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 1 1 0 1 True 1 0 0.0 0.0 0 0 0 0 True True True _Analyze True GTK_RELIEF_NORMAL True 1 2 0 1 0 False True True <b>PDF Instructions</b> False True GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 0 True True 0 True True closebutton1 poppler-24.02.0/test/pdf-operators.c000066400000000000000000000076211455701731300172640ustar00rootroot00000000000000typedef struct { const char *op; const char *description; } OperatorMapping; OperatorMapping op_mapping[] = { { "b", "Close, fill, and stroke path using nonzero winding number rule" }, { "B", "Fill and stroke path using nonzero winding number rule" }, { "b*", "Close, fill, and stroke path using even-odd rule" }, { "B*", "Fill and stroke path using even-odd rule" }, { "BDC", "(PDF 1.2) Begin marked-content sequence with property list" }, { "BI", "Begin inline image object" }, { "BMC", "(PDF 1.2) Begin marked-content sequence" }, { "BT", "Begin text object" }, { "BX", "(PDF 1.1) Begin compatibility section" }, { "c", "Append curved segment to path (three control points)" }, { "cm", "Concatenate matrix to current transformation matrix" }, { "CS", "(PDF 1.1) Set color space for stroking operations" }, { "cs", "(PDF 1.1) Set color space for nonstroking operations" }, { "d", "Set line dash pattern" }, { "d0", "Set glyph width in Type 3 font" }, { "d1", "Set glyph width and bounding box in Type 3 font" }, { "Do", "Invoke named XObject" }, { "DP", "(PDF 1.2) Define marked-content point with property list" }, { "EI", "End inline image object" }, { "EMC", "(PDF 1.2) End marked-content sequence" }, { "ET", "End text object" }, { "EX", "(PDF 1.1) End compatibility section" }, { "f", "Fill path using nonzero winding number rule" }, { "F", "Fill path using nonzero winding number rule (obsolete)" }, { "f*", "Fill path using even-odd rule" }, { "G", "Set gray level for stroking operations" }, { "g", "Set gray level for nonstroking operations" }, { "gs", "(PDF 1.2) Set parameters from graphics state parameter dictionary" }, { "h", "Close subpath" }, { "i", "Set flatness tolerance" }, { "ID", "Begin inline image data" }, { "j", "Set line join style" }, { "J", "Set line cap style" }, { "K", "Set CMYK color for stroking operations" }, { "k", "Set CMYK color for nonstroking operations" }, { "l", "Append straight line segment to path" }, { "m", "Begin new subpath" }, { "M", "Set miter limit" }, { "MP", "(PDF 1.2) Define marked-content point" }, { "n", "End path without filling or stroking" }, { "q", "Save graphics state" }, { "Q", "Restore graphics state" }, { "re", "Append rectangle to path" }, { "RG", "Set RGB color for stroking operations" }, { "rg", "Set RGB color for nonstroking operations" }, { "ri", "Set color rendering intent" }, { "s", "Close and stroke path" }, { "S", "Stroke path" }, { "SC", "(PDF 1.1) Set color for stroking operations" }, { "sc", "(PDF 1.1) Set color for nonstroking operations" }, { "SCN", "(PDF 1.2) Set color for stroking operations (ICCBased and special color spaces)" }, { "scn", "(PDF 1.2) Set color for nonstroking operations (ICCBased and special color spaces)" }, { "sh", "(PDF 1.3) Paint area defined by shading pattern" }, { "T*", "Move to start of next text line" }, { "Tc", "Set character spacing" }, { "Td", "Move text position" }, { "TD", "Move text position and set leading" }, { "Tf", "Set text font and size" }, { "Tj", "Show text" }, { "TJ", "Show text, allowing individual glyph positioning" }, { "TL", "Set text leading" }, { "Tm", "Set text matrix and text line matrix" }, { "Tr", "Set text rendering mode" }, { "Ts", "Set text rise" }, { "Tw", "Set word spacing" }, { "Tz", "Set horizontal text scaling" }, { "v", "Append curved segment to path (initial point replicated)" }, { "w", "Set line width" }, { "W", "Set clipping path using nonzero winding number rule" }, { "W*", "Set clipping path using even-odd rule" }, { "y", "Append curved segment to path (final point replicated)" }, { "'", "Move to next line and show text" }, { "\"", "Set word and character spacing, move to next line, and show text" }, }; poppler-24.02.0/test/perf-test-preview-dummy.cc000066400000000000000000000007521455701731300213610ustar00rootroot00000000000000/* Copyright Krzysztof Kowalczyk 2006-2007 License: GPLv2 */ /* This is a no-op preview support for perf-test. Using this perf-test still works for performance testing, you just don't get any visual feedback during testing. */ #include "splash/SplashBitmap.h" void PreviewBitmapInit(); void PreviewBitmapDestroy(); void PreviewBitmapSplash(SplashBitmap *bmpSplash); void PreviewBitmapSplash(SplashBitmap *bmpSplash) { } void PreviewBitmapDestroy() { } void PreviewBitmapInit() { } poppler-24.02.0/test/perf-test-preview-win.cc000066400000000000000000000145311455701731300210230ustar00rootroot00000000000000/* Copyright Krzysztof Kowalczyk 2006-2007 License: GPLv2 */ /* This is a preview support for perf-test for Windows */ #include #include #include "SplashBitmap.h" #define WIN_CLASS_NAME "PDFTEST_PDF_WIN" #define COL_WINDOW_BG RGB(0xff, 0xff, 0xff) static HWND gHwndSplash; static HBRUSH gBrushBg; static SplashBitmap *gBmpSplash; int rect_dx(RECT *r) { int dx = r->right - r->left; assert(dx >= 0); return dx; } int rect_dy(RECT *r) { int dy = r->bottom - r->top; assert(dy >= 0); return dy; } static HBITMAP createDIBitmapCommon(SplashBitmap *bmp, HDC hdc) { int bmpDx = bmp->getWidth(); int bmpDy = bmp->getHeight(); int bmpRowSize = bmp->getRowSize(); BITMAPINFOHEADER bmih; bmih.biSize = sizeof(bmih); bmih.biHeight = -bmpDy; bmih.biWidth = bmpDx; bmih.biPlanes = 1; bmih.biBitCount = 24; bmih.biCompression = BI_RGB; bmih.biSizeImage = bmpDy * bmpRowSize; ; bmih.biXPelsPerMeter = bmih.biYPelsPerMeter = 0; bmih.biClrUsed = bmih.biClrImportant = 0; unsigned char *bmpData = bmp->getDataPtr(); HBITMAP hbmp = ::CreateDIBitmap(hdc, &bmih, CBM_INIT, bmpData, (BITMAPINFO *)&bmih, DIB_RGB_COLORS); return hbmp; } static void stretchDIBitsCommon(SplashBitmap *bmp, HDC hdc, int leftMargin, int topMargin, int pageDx, int pageDy) { int bmpDx = bmp->getWidth(); int bmpDy = bmp->getHeight(); int bmpRowSize = bmp->getRowSize(); BITMAPINFOHEADER bmih; bmih.biSize = sizeof(bmih); bmih.biHeight = -bmpDy; bmih.biWidth = bmpDx; bmih.biPlanes = 1; // we could create this dibsection in monochrome // if the printer is monochrome, to reduce memory consumption // but splash is currently setup to return a full colour bitmap bmih.biBitCount = 24; bmih.biCompression = BI_RGB; bmih.biSizeImage = bmpDy * bmpRowSize; ; bmih.biXPelsPerMeter = bmih.biYPelsPerMeter = 0; bmih.biClrUsed = bmih.biClrImportant = 0; SplashColorPtr bmpData = bmp->getDataPtr(); ::StretchDIBits(hdc, // destination rectangle -leftMargin, -topMargin, pageDx, pageDy, // source rectangle 0, 0, bmpDx, bmpDy, bmpData, (BITMAPINFO *)&bmih, DIB_RGB_COLORS, SRCCOPY); } /* Set the client area size of the window 'hwnd' to 'dx'/'dy'. */ static void resizeClientArea(HWND hwnd, int x, int dx, int dy, int *dx_out) { RECT rc; GetClientRect(hwnd, &rc); if ((rect_dx(&rc) == dx) && (rect_dy(&rc) == dy)) return; RECT rw; GetWindowRect(hwnd, &rw); int win_dx = rect_dx(&rw) + (dx - rect_dx(&rc)); int win_dy = rect_dy(&rw) + (dy - rect_dy(&rc)); SetWindowPos(hwnd, NULL, x, 0, win_dx, win_dy, SWP_NOACTIVATE | SWP_NOREPOSITION | SWP_NOZORDER); if (dx_out) *dx_out = win_dx; } static void resizeClientAreaToRenderedBitmap(HWND hwnd, SplashBitmap *bmp, int x, int *dxOut) { int dx = bmp->getWidth(); int dy = bmp->getHeight(); resizeClientArea(hwnd, x, dx, dy, dxOut); } static void drawBitmap(HWND hwnd, SplashBitmap *bmp) { PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps); SetBkMode(hdc, TRANSPARENT); FillRect(hdc, &ps.rcPaint, gBrushBg); HBITMAP hbmp = createDIBitmapCommon(bmp, hdc); if (hbmp) { HDC bmpDC = CreateCompatibleDC(hdc); if (bmpDC) { SelectObject(bmpDC, hbmp); int xSrc = 0, ySrc = 0; int xDest = 0, yDest = 0; int bmpDx = bmp->getWidth(); int bmpDy = bmp->getHeight(); BitBlt(hdc, xDest, yDest, bmpDx, bmpDy, bmpDC, xSrc, ySrc, SRCCOPY); DeleteDC(bmpDC); bmpDC = NULL; } DeleteObject(hbmp); hbmp = NULL; } EndPaint(hwnd, &ps); } static void onPaint(HWND hwnd) { if (hwnd == gHwndSplash) { if (gBmpSplash) { drawBitmap(hwnd, gBmpSplash); } } } static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_CREATE: // do nothing break; case WM_ERASEBKGND: return TRUE; case WM_PAINT: /* it might happen that we get WM_PAINT after destroying a window */ onPaint(hwnd); break; case WM_DESTROY: /* WM_DESTROY might be sent as a result of File\Close, in which case CloseWindow() has already been called */ break; default: return DefWindowProc(hwnd, message, wParam, lParam); } return 0; } static BOOL registerWinClass(void) { WNDCLASSEX wcex; ATOM atom; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = NULL; wcex.hIcon = NULL; wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = NULL; wcex.lpszMenuName = NULL; wcex.lpszClassName = WIN_CLASS_NAME; wcex.hIconSm = NULL; atom = RegisterClassEx(&wcex); if (atom) return TRUE; return FALSE; } static bool initWinIfNecessary(void) { if (gHwndSplash) return true; if (!registerWinClass()) return false; gBrushBg = CreateSolidBrush(COL_WINDOW_BG); gHwndSplash = CreateWindow(WIN_CLASS_NAME, "Splash", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, NULL, NULL); if (!gHwndSplash) return false; ShowWindow(gHwndSplash, SW_HIDE); return true; } static void pumpMessages(void) { BOOL isMessage; MSG msg; for (;;) { isMessage = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE); if (!isMessage) return; TranslateMessage(&msg); DispatchMessage(&msg); } } void PreviewBitmapInit(void) { /* no need to do anything */ } void PreviewBitmapDestroy(void) { PostQuitMessage(0); pumpMessages(); DeleteObject(gBrushBg); } static void UpdateWindows(void) { if (gBmpSplash) { resizeClientAreaToRenderedBitmap(gHwndSplash, gBmpSplash, 0, NULL); ShowWindow(gHwndSplash, SW_SHOW); InvalidateRect(gHwndSplash, NULL, FALSE); UpdateWindow(gHwndSplash); } else { ShowWindow(gHwndSplash, SW_HIDE); } pumpMessages(); } void PreviewBitmapSplash(SplashBitmap *bmpSplash) { if (!initWinIfNecessary()) return; gBmpSplash = bmpSplash; UpdateWindows(); } poppler-24.02.0/test/perf-test.cc000066400000000000000000000600161455701731300165500ustar00rootroot00000000000000/* Copyright Krzysztof Kowalczyk 2006-2007 Copyright Hib Eris 2008, 2013 Copyright 2018, 2020, 2022 Albert Astals Cid 2018 Copyright 2019 Oliver Sander Copyright 2020 Adam Reichold License: GPLv2 */ /* A tool to stress-test poppler rendering and measure rendering times for very simplistic performance measuring. TODO: * make it work with cairo output as well * print more info about document like e.g. enumerate images, streams, compression, encryption, password-protection. Each should have a command-line arguments to turn it on/off * never over-write file given as -out argument (optionally, provide -force option to force writing the -out file). It's way too easy too lose results of a previous run. */ #ifdef _MSC_VER // this sucks but I don't know any other way # pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") #endif #include #ifdef _WIN32 # include #else # include #endif // Define COPY_FILE if you want the file to be copied to a local disk first // 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 #include #include #include #include #include #include #include #include #ifdef HAVE_DIRENT_H # include #endif #include "Error.h" #include "ErrorCodes.h" #include "goo/GooString.h" #include "goo/GooTimer.h" #include "GlobalParams.h" #include "splash/SplashBitmap.h" #include "Object.h" /* must be included before SplashOutputDev.h because of sloppiness in SplashOutputDev.h */ #include "SplashOutputDev.h" #include "TextOutputDev.h" #include "PDFDoc.h" #include "Link.h" #ifdef _MSC_VER # define strdup _strdup # define strcasecmp _stricmp #endif #define dimof(X) (sizeof(X) / sizeof((X)[0])) #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 perf-test-preview-win.cc */ extern void PreviewBitmapInit(); extern void PreviewBitmapDestroy(); extern void PreviewBitmapSplash(SplashBitmap *bmpSplash); class PdfEnginePoppler { public: PdfEnginePoppler(); ~PdfEnginePoppler(); PdfEnginePoppler(const PdfEnginePoppler &) = delete; PdfEnginePoppler &operator=(const PdfEnginePoppler &) = delete; const char *fileName() const { return _fileName; }; void setFileName(const char *fileName) { assert(!_fileName); _fileName = (char *)strdup(fileName); } int pageCount() const { return _pageCount; } bool load(const char *fileName); SplashBitmap *renderBitmap(int pageNo, double zoomReal, int rotation); SplashOutputDev *outputDevice(); private: char *_fileName; int _pageCount; PDFDoc *_pdfDoc; SplashOutputDev *_outputDev; }; typedef struct StrList { struct StrList *next; char *str; } StrList; /* List of all command-line arguments that are not switches. We assume those are: - names of PDF files - names of a file with a list of PDF files - names of directories with PDF files */ static StrList *gArgsListRoot = nullptr; /* Names of all command-line switches we recognize */ #define TIMINGS_ARG "-timings" #define RESOLUTION_ARG "-resolution" #define RECURSIVE_ARG "-recursive" #define OUT_ARG "-out" #define PREVIEW_ARG "-preview" #define SLOW_PREVIEW_ARG "-slowpreview" #define LOAD_ONLY_ARG "-loadonly" #define PAGE_ARG "-page" #define TEXT_ARG "-text" /* Should we record timings? True if -timings command-line argument was given. */ static bool gfTimings = false; /* If true, we use render each page at resolution 'gResolutionX'/'gResolutionY'. If false, we render each page at its native resolution. True if -resolution NxM command-line argument was given. */ static bool gfForceResolution = false; static int gResolutionX = 0; static int gResolutionY = 0; /* If NULL, we output the log info to stdout. If not NULL, should be a name of the file to which we output log info. Controlled by -out command-line argument. */ static char *gOutFileName = nullptr; /* FILE * corresponding to gOutFileName or stdout if gOutFileName is NULL or was invalid name */ static FILE *gOutFile = nullptr; /* FILE * corresponding to gOutFileName or stderr if gOutFileName is NULL or was invalid name */ static FILE *gErrFile = nullptr; /* If True and a directory is given as a command-line argument, we'll process pdf files in sub-directories as well. Controlled by -recursive command-line argument */ static bool gfRecursive = false; /* If true, preview rendered image. To make sure that they're being rendered correctly. */ static bool gfPreview = false; /* 1 second (1000 milliseconds) */ #define SLOW_PREVIEW_TIME 1000 /* If true, preview rendered image in a slow mode i.e. delay displaying for SLOW_PREVIEW_TIME. This is so that a human has enough time to see if the PDF renders ok. In release mode on fast processor pages take only ~100-200 ms to render and they go away too quickly to be inspected by a human. */ static bool gfSlowPreview = false; /* If true, we only dump the text, not render */ static bool gfTextOnly = false; #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 */ static int gPageNo = PAGE_NO_NOT_GIVEN; /* If true, will only load the file, not render any pages. Mostly for profiling load time */ static bool gfLoadOnly = false; #define PDF_FILE_DPI 72 #define MAX_FILENAME_SIZE 1024 /* DOS is 0xd 0xa */ #define DOS_NEWLINE "\x0d\x0a" /* Mac is single 0xd */ #define MAC_NEWLINE "\x0d" /* Unix is single 0xa (10) */ #define UNIX_NEWLINE "\x0a" #define UNIX_NEWLINE_C 0xa static void memzero(void *data, size_t len) { memset(data, 0, len); } static void *zmalloc(size_t len) { void *data = malloc(len); if (data) { memzero(data, len); } return data; } /* Concatenate 4 strings. Any string can be NULL. Caller needs to free() memory. */ static char *str_cat4(const char *str1, const char *str2, const char *str3, const char *str4) { char *str; char *tmp; size_t str1_len = 0; size_t str2_len = 0; size_t str3_len = 0; size_t str4_len = 0; if (str1) { str1_len = strlen(str1); } if (str2) { str2_len = strlen(str2); } if (str3) { str3_len = strlen(str3); } if (str4) { str4_len = strlen(str4); } str = (char *)zmalloc(str1_len + str2_len + str3_len + str4_len + 1); if (!str) { return nullptr; } tmp = str; if (str1) { memcpy(tmp, str1, str1_len); tmp += str1_len; } if (str2) { memcpy(tmp, str2, str2_len); tmp += str2_len; } if (str3) { memcpy(tmp, str3, str3_len); tmp += str3_len; } if (str4) { memcpy(tmp, str4, str1_len); } return str; } static char *str_dup(const char *str) { return str_cat4(str, nullptr, nullptr, nullptr); } static bool str_eq(const char *str1, const char *str2) { if (!str1 && !str2) { return true; } if (!str1 || !str2) { return false; } if (0 == strcmp(str1, str2)) { return true; } return false; } static bool str_ieq(const char *str1, const char *str2) { if (!str1 && !str2) { return true; } if (!str1 || !str2) { return false; } if (0 == strcasecmp(str1, str2)) { return true; } return false; } static bool str_endswith(const char *txt, const char *end) { size_t end_len; size_t txt_len; if (!txt || !end) { return false; } txt_len = strlen(txt); end_len = strlen(end); if (end_len > txt_len) { return false; } if (str_eq(txt + txt_len - end_len, end)) { return true; } return false; } /* TODO: probably should move to some other file and change name to sleep_milliseconds */ static void sleep_milliseconds(int milliseconds) { #ifdef _WIN32 Sleep((DWORD)milliseconds); #else struct timespec tv; int secs, nanosecs; secs = milliseconds / 1000; nanosecs = (milliseconds - (secs * 1000)) * 1000; tv.tv_sec = (time_t)secs; tv.tv_nsec = (long)nanosecs; while (true) { int rval = nanosleep(&tv, &tv); if (rval == 0) { /* Completed the entire sleep time; all done. */ return; } else if (errno == EINTR) { /* Interrupted by a signal. Try again. */ continue; } else { /* Some other error; bail out. */ return; } } return; #endif } static SplashColorMode gSplashColorMode = splashModeBGR8; static SplashColor splashColRed; static SplashColor splashColGreen; 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]) static SplashColorPtr gBgColor = SPLASH_COL_WHITE_PTR; static void splashColorSet(SplashColorPtr col, unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha) { switch (gSplashColorMode) { case splashModeBGR8: col[0] = blue; col[1] = green; col[2] = red; break; case splashModeRGB8: col[0] = red; col[1] = green; col[2] = blue; break; default: assert(0); break; } } static void SplashColorsInit() { splashColorSet(SPLASH_COL_RED_PTR, 0xff, 0, 0, 0); splashColorSet(SPLASH_COL_GREEN_PTR, 0, 0xff, 0, 0); splashColorSet(SPLASH_COL_BLUE_PTR, 0, 0, 0xff, 0); splashColorSet(SPLASH_COL_BLACK_PTR, 0, 0, 0, 0); splashColorSet(SPLASH_COL_WHITE_PTR, 0xff, 0xff, 0xff, 0); } PdfEnginePoppler::PdfEnginePoppler() : _fileName(nullptr), _pageCount(INVALID_PAGE_NO), _pdfDoc(nullptr), _outputDev(nullptr) { } PdfEnginePoppler::~PdfEnginePoppler() { free(_fileName); delete _outputDev; delete _pdfDoc; } bool PdfEnginePoppler::load(const char *fileName) { setFileName(fileName); _pdfDoc = new PDFDoc(std::make_unique(fileName)); if (!_pdfDoc->isOk()) { return false; } _pageCount = _pdfDoc->getNumPages(); return true; } SplashOutputDev *PdfEnginePoppler::outputDevice() { if (!_outputDev) { bool bitmapTopDown = true; _outputDev = new SplashOutputDev(gSplashColorMode, 4, false, gBgColor, bitmapTopDown); if (_outputDev) { _outputDev->startDoc(_pdfDoc); } } return _outputDev; } SplashBitmap *PdfEnginePoppler::renderBitmap(int pageNo, double zoomReal, int rotation) { assert(outputDevice()); if (!outputDevice()) { return nullptr; } double hDPI = (double)PDF_FILE_DPI * zoomReal * 0.01; double vDPI = (double)PDF_FILE_DPI * zoomReal * 0.01; bool useMediaBox = false; bool crop = true; bool doLinks = true; _pdfDoc->displayPage(_outputDev, pageNo, hDPI, vDPI, rotation, useMediaBox, crop, doLinks, nullptr, nullptr); SplashBitmap *bmp = _outputDev->takeBitmap(); return bmp; } static int StrList_Len(StrList **root) { int len = 0; StrList *cur; assert(root); if (!root) { return 0; } cur = *root; while (cur) { ++len; cur = cur->next; } return len; } static int StrList_InsertAndOwn(StrList **root, char *txt) { StrList *el; assert(root && txt); if (!root || !txt) { return false; } el = (StrList *)malloc(sizeof(StrList)); if (!el) { return false; } el->str = txt; el->next = *root; *root = el; return true; } static int StrList_Insert(StrList **root, char *txt) { char *txtDup; assert(root && txt); if (!root || !txt) { return false; } txtDup = str_dup(txt); if (!txtDup) { return false; } if (!StrList_InsertAndOwn(root, txtDup)) { free((void *)txtDup); return false; } return true; } static void StrList_FreeElement(StrList *el) { if (!el) { return; } free((void *)el->str); free((void *)el); } static void StrList_Destroy(StrList **root) { StrList *cur; StrList *next; if (!root) { return; } cur = *root; while (cur) { next = cur->next; StrList_FreeElement(cur); cur = next; } *root = nullptr; } static void my_error(ErrorCategory, Goffset pos, const char *msg) { #if 0 char buf[4096], *p = buf; // NB: this can be called before the globalParams object is created if (globalParams && globalParams->getErrQuiet()) { return; } if (pos >= 0) { p += _snprintf(p, sizeof(buf)-1, "Error (%lld): ", (long long)pos); *p = '\0'; OutputDebugString(p); } else { OutputDebugString("Error: "); } p = buf; p += vsnprintf(p, sizeof(buf) - 1, msg, args); while ( p > buf && isspace(p[-1]) ) *--p = '\0'; *p++ = '\r'; *p++ = '\n'; *p = '\0'; OutputDebugString(buf); if (pos >= 0) { p += _snprintf(p, sizeof(buf)-1, "Error (%lld): ", (long long)pos); *p = '\0'; OutputDebugString(buf); if (gErrFile) fprintf(gErrFile, buf); } else { OutputDebugString("Error: "); if (gErrFile) fprintf(gErrFile, "Error: "); } #endif #if 0 p = buf; va_start(args, msg); p += vsnprintf(p, sizeof(buf) - 3, msg, args); while ( p > buf && isspace(p[-1]) ) *--p = '\0'; *p++ = '\r'; *p++ = '\n'; *p = '\0'; OutputDebugString(buf); if (gErrFile) fprintf(gErrFile, buf); va_end(args); #endif } static void LogInfo(const char *fmt, ...) GCC_PRINTF_FORMAT(1, 2); static void LogInfo(const char *fmt, ...) { va_list args; char buf[4096], *p = buf; p = buf; va_start(args, fmt); p += vsnprintf(p, sizeof(buf) - 1, fmt, args); *p = '\0'; fprintf(gOutFile, "%s", buf); va_end(args); fflush(gOutFile); } static void PrintUsageAndExit(int argc, char **argv) { printf("Usage: pdftest [-preview|-slowpreview] [-loadonly] [-timings] [-text] [-resolution NxM] [-recursive] [-page N] [-out out.txt] pdf-files-to-process\n"); for (int i = 0; i < argc; i++) { printf("i=%d, '%s'\n", i, argv[i]); } exit(0); } static bool ShowPreview() { if (gfPreview || gfSlowPreview) { return true; } return false; } static void RenderPdfAsText(const char *fileName) { PDFDoc *pdfDoc = nullptr; GooString *txt = nullptr; int pageCount; double timeInMs; assert(fileName); if (!fileName) { return; } LogInfo("started: %s\n", fileName); TextOutputDev *textOut = new TextOutputDev(nullptr, true, 0, false, false); if (!textOut->isOk()) { delete textOut; return; } 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); goto Exit; } msTimer.stop(); timeInMs = msTimer.getElapsed(); LogInfo("load: %.2f ms\n", timeInMs); pageCount = pdfDoc->getNumPages(); LogInfo("page count: %d\n", pageCount); for (int curPage = 1; curPage <= pageCount; curPage++) { if ((gPageNo != PAGE_NO_NOT_GIVEN) && (gPageNo != curPage)) { continue; } msTimer.start(); int rotate = 0; bool useMediaBox = false; 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); 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; } Exit: LogInfo("finished: %s\n", fileName); delete textOut; delete pdfDoc; } #ifdef _MSC_VER # define POPPLER_TMP_NAME "c:\\poppler_tmp.pdf" #else # define POPPLER_TMP_NAME "/tmp/poppler_tmp.pdf" #endif static void RenderPdf(const char *fileName) { const char *fileNameSplash = nullptr; PdfEnginePoppler *engineSplash = nullptr; int pageCount; double timeInMs; #ifdef COPY_FILE // TODO: fails if file already exists and has read-only attribute CopyFile(fileName, POPPLER_TMP_NAME, false); fileNameSplash = POPPLER_TMP_NAME; #else fileNameSplash = fileName; #endif LogInfo("started: %s\n", fileName); engineSplash = new PdfEnginePoppler(); GooTimer msTimer; if (!engineSplash->load(fileNameSplash)) { LogInfo("failed to load splash\n"); goto Error; } msTimer.stop(); timeInMs = msTimer.getElapsed(); LogInfo("load splash: %.2f ms\n", timeInMs); pageCount = engineSplash->pageCount(); LogInfo("page count: %d\n", pageCount); if (gfLoadOnly) { goto Error; } for (int curPage = 1; curPage <= pageCount; curPage++) { if ((gPageNo != PAGE_NO_NOT_GIVEN) && (gPageNo != curPage)) { continue; } SplashBitmap *bmpSplash = nullptr; GooTimer msRenderTimer; bmpSplash = engineSplash->renderBitmap(curPage, 100.0, 0); msRenderTimer.stop(); timeInMs = msRenderTimer.getElapsed(); if (gfTimings) { if (!bmpSplash) { LogInfo("page splash %d: failed to render\n", curPage); } else { LogInfo("page splash %d (%dx%d): %.2f ms\n", curPage, bmpSplash->getWidth(), bmpSplash->getHeight(), timeInMs); } } if (ShowPreview()) { PreviewBitmapSplash(bmpSplash); if (gfSlowPreview) { sleep_milliseconds(SLOW_PREVIEW_TIME); } } delete bmpSplash; } Error: delete engineSplash; LogInfo("finished: %s\n", fileName); } static void RenderFile(const char *fileName) { if (gfTextOnly) { RenderPdfAsText(fileName); return; } RenderPdf(fileName); } static bool ParseInteger(const char *start, const char *end, int *intOut) { char numBuf[16]; int digitsCount; const char *tmp; assert(start && end && intOut); assert(end >= start); if (!start || !end || !intOut || (start > end)) { return false; } digitsCount = 0; tmp = start; while (tmp <= end) { if (isspace(*tmp)) { /* do nothing, we allow whitespace */ } else if (!isdigit(*tmp)) { return false; } numBuf[digitsCount] = *tmp; ++digitsCount; if (digitsCount == dimof(numBuf) - 3) { /* -3 to be safe */ return false; } ++tmp; } if (0 == digitsCount) { return false; } numBuf[digitsCount] = 0; *intOut = atoi(numBuf); return true; } /* Given 'resolutionString' in format NxM (e.g. "100x200"), parse the string and put N into 'resolutionXOut' and M into 'resolutionYOut'. Return false if there was an error (e.g. string is not in the right format */ static bool ParseResolutionString(const char *resolutionString, int *resolutionXOut, int *resolutionYOut) { const char *posOfX; assert(resolutionString); assert(resolutionXOut); assert(resolutionYOut); if (!resolutionString || !resolutionXOut || !resolutionYOut) { return false; } *resolutionXOut = 0; *resolutionYOut = 0; posOfX = strchr(resolutionString, 'X'); if (!posOfX) { posOfX = strchr(resolutionString, 'x'); } if (!posOfX) { return false; } if (posOfX == resolutionString) { return false; } if (!ParseInteger(resolutionString, posOfX - 1, resolutionXOut)) { return false; } if (!ParseInteger(posOfX + 1, resolutionString + strlen(resolutionString) - 1, resolutionYOut)) { return false; } return true; } static void ParseCommandLine(int argc, char **argv) { char *arg; if (argc < 2) { PrintUsageAndExit(argc, argv); } for (int i = 1; i < argc; i++) { arg = argv[i]; assert(arg); if ('-' == arg[0]) { if (str_ieq(arg, TIMINGS_ARG)) { gfTimings = true; } else if (str_ieq(arg, RESOLUTION_ARG)) { ++i; if (i == argc) { PrintUsageAndExit(argc, argv); /* expect a file name after that */ } if (!ParseResolutionString(argv[i], &gResolutionX, &gResolutionY)) { PrintUsageAndExit(argc, argv); } gfForceResolution = true; } else if (str_ieq(arg, RECURSIVE_ARG)) { gfRecursive = true; } else if (str_ieq(arg, OUT_ARG)) { /* expect a file name after that */ ++i; if (i == argc) { PrintUsageAndExit(argc, argv); } gOutFileName = str_dup(argv[i]); } else if (str_ieq(arg, PREVIEW_ARG)) { gfPreview = true; } else if (str_ieq(arg, TEXT_ARG)) { gfTextOnly = true; } else if (str_ieq(arg, SLOW_PREVIEW_ARG)) { gfSlowPreview = true; } else if (str_ieq(arg, LOAD_ONLY_ARG)) { gfLoadOnly = true; } else if (str_ieq(arg, PAGE_ARG)) { /* expect an integer after that */ ++i; if (i == argc) { PrintUsageAndExit(argc, argv); } gPageNo = atoi(argv[i]); if (gPageNo < 1) { PrintUsageAndExit(argc, argv); } } else { /* unknown option */ PrintUsageAndExit(argc, argv); } } else { /* we assume that this is not an option hence it must be a name of PDF/directory/file with PDF names */ StrList_Insert(&gArgsListRoot, arg); } } } static bool IsPdfFileName(char *path) { if (str_endswith(path, ".pdf")) { return true; } return false; } /* Render 'cmdLineArg', which can be: - name of PDF file */ static void RenderCmdLineArg(char *cmdLineArg) { assert(cmdLineArg); if (!cmdLineArg) { return; } if (IsPdfFileName(cmdLineArg)) { RenderFile(cmdLineArg); } else { error(errCommandLine, -1, "unexpected argument '{0:s}'", cmdLineArg); } } int main(int argc, char **argv) { setErrorCallback(my_error); ParseCommandLine(argc, argv); if (0 == StrList_Len(&gArgsListRoot)) { PrintUsageAndExit(argc, argv); } assert(gArgsListRoot); SplashColorsInit(); globalParams = std::make_unique(); if (!globalParams) { return 1; } globalParams->setErrQuiet(false); FILE *outFile = nullptr; if (gOutFileName) { outFile = fopen(gOutFileName, "wb"); if (!outFile) { printf("failed to open -out file %s\n", gOutFileName); return 1; } gOutFile = outFile; } else { gOutFile = stdout; } if (gOutFileName) { gErrFile = outFile; } else { gErrFile = stderr; } PreviewBitmapInit(); StrList *curr = gArgsListRoot; while (curr) { RenderCmdLineArg(curr->str); curr = curr->next; } if (outFile) { fclose(outFile); } PreviewBitmapDestroy(); StrList_Destroy(&gArgsListRoot); free(gOutFileName); return 0; } poppler-24.02.0/utils/000077500000000000000000000000001455701731300145065ustar00rootroot00000000000000poppler-24.02.0/utils/.gitignore000066400000000000000000000002341455701731300164750ustar00rootroot00000000000000.deps .libs *.la *.lo Makefile Makefile.in pdfseparate pdffonts pdfimages pdfinfo pdfunite pdftohtml pdftoppm pdftops pdftotext pdftocairo pdfdetach pdfsig poppler-24.02.0/utils/CMakeLists.txt000066400000000000000000000113341455701731300172500ustar00rootroot00000000000000 set(common_srcs parseargs.cc Win32Console.cc ) set(common_libs poppler ) # pdftoppm set(pdftoppm_SOURCES ${common_srcs} pdftoppm.cc sanitychecks.cc ) 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) if (HAVE_CAIRO) # pdftocairo set(pdftocairo_SOURCES ${common_srcs} pdftocairo.cc pdftocairo-win32.cc ${CMAKE_SOURCE_DIR}/poppler/CairoFontEngine.cc ${CMAKE_SOURCE_DIR}/poppler/CairoOutputDev.cc ${CMAKE_SOURCE_DIR}/poppler/CairoRescaleBox.cc ) include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) 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) endif () # pdfdetach set(pdfdetach_SOURCES ${common_srcs} pdfdetach.cc ) add_executable(pdfdetach ${pdfdetach_SOURCES}) target_link_libraries(pdfdetach ${common_libs}) install(TARGETS pdfdetach DESTINATION bin) install(FILES pdfdetach.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) # pdfdetach set(pdfattach_SOURCES ${common_srcs} pdfattach.cc ) add_executable(pdfattach ${pdfattach_SOURCES}) target_link_libraries(pdfattach ${common_libs}) install(TARGETS pdfattach DESTINATION bin) install(FILES pdfattach.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) # pdffonts set(pdffonts_SOURCES ${common_srcs} pdffonts.cc ) add_executable(pdffonts ${pdffonts_SOURCES}) target_link_libraries(pdffonts ${common_libs}) install(TARGETS pdffonts DESTINATION bin) install(FILES pdffonts.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) # pdfimages set(pdfimages_SOURCES ${common_srcs} pdfimages.cc ImageOutputDev.cc ImageOutputDev.h ) add_executable(pdfimages ${pdfimages_SOURCES}) target_link_libraries(pdfimages ${common_libs}) install(TARGETS pdfimages DESTINATION bin) install(FILES pdfimages.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) # pdfinfo set(pdfinfo_SOURCES ${common_srcs} pdfinfo.cc printencodings.cc ) add_executable(pdfinfo ${pdfinfo_SOURCES}) target_link_libraries(pdfinfo ${common_libs}) install(TARGETS pdfinfo DESTINATION bin) install(FILES pdfinfo.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) if (ENABLE_SIGNATURES) # pdfsig set(pdfsig_SOURCES ${common_srcs} pdfsig.cc ) add_executable(pdfsig ${pdfsig_SOURCES}) target_link_libraries(pdfsig ${common_libs}) if (ENABLE_NSS3) target_include_directories(pdfsig SYSTEM PRIVATE ${NSS3_INCLUDE_DIRS}) endif() install(TARGETS pdfsig DESTINATION bin) install(FILES pdfsig.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) endif () # pdftops set(pdftops_SOURCES ${common_srcs} pdftops.cc sanitychecks.cc ) 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) # pdftotext set(pdftotext_SOURCES ${common_srcs} pdftotext.cc printencodings.cc ) add_executable(pdftotext ${pdftotext_SOURCES}) target_link_libraries(pdftotext ${common_libs}) install(TARGETS pdftotext DESTINATION bin) install(FILES pdftotext.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) # pdftohtml set(pdftohtml_SOURCES ${common_srcs} InMemoryFile.cc pdftohtml.cc HtmlFonts.cc HtmlLinks.cc HtmlOutputDev.cc ) add_executable(pdftohtml ${pdftohtml_SOURCES}) target_link_libraries(pdftohtml ${common_libs}) install(TARGETS pdftohtml DESTINATION bin) install(FILES pdftohtml.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) # pdfseparate set(pdfseparate_SOURCES ${common_srcs} pdfseparate.cc ) add_executable(pdfseparate ${pdfseparate_SOURCES}) target_link_libraries(pdfseparate ${common_libs}) install(TARGETS pdfseparate DESTINATION bin) install(FILES pdfseparate.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) # pdfunite set(pdfunite_SOURCES ${common_srcs} pdfunite.cc ) add_executable(pdfunite ${pdfunite_SOURCES}) target_link_libraries(pdfunite ${common_libs}) install(TARGETS pdfunite DESTINATION bin) install(FILES pdfunite.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) poppler-24.02.0/utils/HtmlFonts.cc000066400000000000000000000257751455701731300167530ustar00rootroot00000000000000//======================================================================== // // This file comes from pdftohtml project // http://pdftohtml.sourceforge.net // // Copyright from: // Gueorgui Ovtcharov // Rainer Dorsch // Mikhail Kruk // //======================================================================== //======================================================================== // // 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) 2007, 2010, 2012, 2018, 2020 Albert Astals Cid // Copyright (C) 2008 Boris Toloknov // Copyright (C) 2008 Tomas Are Haavet // Copyright (C) 2010 OSSD CDAC Mumbai by Leena Chourey (leenac@cdacmumbai.in) and Onkar Potdar (onkar@cdacmumbai.in) // Copyright (C) 2011 Joshua Richardson // Copyright (C) 2011 Stephen Reichling // Copyright (C) 2012 Igor Slepchin // Copyright (C) 2012 Luis Parravicini // Copyright (C) 2013 Julien Nabet // Copyright (C) 2017 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 Steven Boswell // 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 // //======================================================================== #include "HtmlFonts.h" #include "HtmlUtils.h" #include "GlobalParams.h" #include "UnicodeMap.h" #include "GfxFont.h" #include namespace { const char *const defaultFamilyName = "Times"; const char *const styleSuffixes[] = { "-Regular", "-Bold", "-BoldOblique", "-BoldItalic", "-Oblique", "-Italic", "-Roman", }; void removeStyleSuffix(std::string &familyName) { for (const char *const styleSuffix : styleSuffixes) { auto pos = familyName.rfind(styleSuffix); if (pos != std::string::npos) { familyName.resize(pos); return; } } } } #define xoutRound(x) ((int)(x + 0.5)) extern bool xml; extern bool fontFullName; HtmlFontColor::HtmlFontColor(GfxRGB rgb, double opacity_) { r = static_cast(rgb.r / 65535.0 * 255.0); g = static_cast(rgb.g / 65535.0 * 255.0); b = static_cast(rgb.b / 65535.0 * 255.0); opacity = static_cast(opacity_ * 255.999); if (!(Ok(r) && Ok(b) && Ok(g) && Ok(opacity))) { if (!globalParams->getErrQuiet()) { fprintf(stderr, "Error : Bad color (%d,%d,%d,%d) reset to (0,0,0,255)\n", r, g, b, opacity); } r = 0; g = 0; b = 0; opacity = 255; } } GooString *HtmlFontColor::convtoX(unsigned int xcol) const { GooString *xret = new GooString(); char tmp; unsigned int k; k = (xcol / 16); if (k < 10) { tmp = (char)('0' + k); } else { tmp = (char)('a' + k - 10); } xret->append(tmp); k = (xcol % 16); if (k < 10) { tmp = (char)('0' + k); } else { tmp = (char)('a' + k - 10); } xret->append(tmp); return xret; } GooString *HtmlFontColor::toString() const { GooString *tmp = new GooString("#"); GooString *tmpr = convtoX(r); GooString *tmpg = convtoX(g); GooString *tmpb = convtoX(b); tmp->append(tmpr); tmp->append(tmpg); tmp->append(tmpb); delete tmpr; delete tmpg; delete tmpb; return tmp; } HtmlFont::HtmlFont(const GfxFont &font, int _size, GfxRGB rgb, double opacity) { color = HtmlFontColor(rgb, opacity); lineSize = -1; size = _size; italic = false; bold = false; rotOrSkewed = false; if (font.isBold() || font.getWeight() >= GfxFont::W700) { bold = true; } if (font.isItalic()) { italic = true; } if (const std::optional &fontname = font.getName()) { FontName = new GooString(*fontname); GooString fontnameLower(*fontname); fontnameLower.lowerCase(); if (!bold && strstr(fontnameLower.c_str(), "bold")) { bold = true; } if (!italic && (strstr(fontnameLower.c_str(), "italic") || strstr(fontnameLower.c_str(), "oblique"))) { italic = true; } familyName = fontname->c_str(); removeStyleSuffix(familyName); } else { FontName = new GooString(defaultFamilyName); familyName = defaultFamilyName; } rotSkewMat[0] = rotSkewMat[1] = rotSkewMat[2] = rotSkewMat[3] = 0; } HtmlFont::HtmlFont(const HtmlFont &x) { size = x.size; lineSize = x.lineSize; italic = x.italic; bold = x.bold; familyName = x.familyName; color = x.color; FontName = new GooString(x.FontName); rotOrSkewed = x.rotOrSkewed; memcpy(rotSkewMat, x.rotSkewMat, sizeof(rotSkewMat)); } HtmlFont::~HtmlFont() { delete FontName; } HtmlFont &HtmlFont::operator=(const HtmlFont &x) { if (this == &x) { return *this; } size = x.size; lineSize = x.lineSize; italic = x.italic; bold = x.bold; familyName = x.familyName; color = x.color; delete FontName; FontName = new GooString(x.FontName); return *this; } /* This function is used to compare font uniquely for insertion into the list of all encountered fonts */ bool HtmlFont::isEqual(const HtmlFont &x) const { return (size == x.size) && (lineSize == x.lineSize) && (FontName->cmp(x.FontName) == 0) && (bold == x.bold) && (italic == x.italic) && (color.isEqual(x.getColor())) && isRotOrSkewed() == x.isRotOrSkewed() && (!isRotOrSkewed() || rot_matrices_equal(getRotMat(), x.getRotMat())); } /* This one is used to decide whether two pieces of text can be joined together and therefore we don't care about bold/italics properties */ bool HtmlFont::isEqualIgnoreBold(const HtmlFont &x) const { return ((size == x.size) && (familyName == x.familyName) && (color.isEqual(x.getColor()))); } GooString *HtmlFont::getFontName() { return new GooString(familyName); } GooString *HtmlFont::getFullName() { return new GooString(FontName); } // this method if plain wrong todo std::unique_ptr HtmlFont::HtmlFilter(const Unicode *u, int uLen) { auto tmp = std::make_unique(); const UnicodeMap *uMap; char buf[8]; int n; // get the output encoding if (!(uMap = globalParams->getTextEncoding())) { return tmp; } for (int i = 0; i < uLen; ++i) { // skip control characters. W3C disallows them and they cause a warning // with PHP. if (u[i] <= 31 && u[i] != '\t') { continue; } switch (u[i]) { case '"': tmp->append("""); break; case '&': tmp->append("&"); break; case '<': tmp->append("<"); break; case '>': tmp->append(">"); break; case ' ': case '\t': tmp->append(!xml && (i + 1 >= uLen || !tmp->getLength() || tmp->getChar(tmp->getLength() - 1) == ' ') ? " " : " "); break; default: { // convert unicode to string if ((n = uMap->mapUnicode(u[i], buf, sizeof(buf))) > 0) { tmp->append(buf, n); } } } } return tmp; } HtmlFontAccu::HtmlFontAccu() { } HtmlFontAccu::~HtmlFontAccu() { } int HtmlFontAccu::AddFont(const HtmlFont &font) { std::vector::iterator i; for (i = accu.begin(); i != accu.end(); ++i) { if (font.isEqual(*i)) { return (int)(i - (accu.begin())); } } accu.push_back(font); return (accu.size() - 1); } // get CSS font definition for font #i GooString *HtmlFontAccu::CSStyle(int i, int j) { GooString *tmp = new GooString(); std::vector::iterator g = accu.begin(); g += i; HtmlFont font = *g; GooString *colorStr = font.getColor().toString(); GooString *fontName = (fontFullName ? font.getFullName() : font.getFontName()); if (!xml) { tmp->append(".ft"); tmp->append(std::to_string(j)); tmp->append(std::to_string(i)); tmp->append("{font-size:"); tmp->append(std::to_string(font.getSize())); if (font.getLineSize() != -1 && font.getLineSize() != 0) { tmp->append("px;line-height:"); tmp->append(std::to_string(font.getLineSize())); } tmp->append("px;font-family:"); tmp->append(fontName); // font.getFontName()); tmp->append(";color:"); tmp->append(colorStr); if (font.getColor().getOpacity() != 1.0) { tmp->append(";opacity:"); tmp->append(std::to_string(font.getColor().getOpacity())); } // if there is rotation or skew, include the matrix if (font.isRotOrSkewed()) { const double *const text_mat = font.getRotMat(); GooString matrix_str(" matrix("); matrix_str.appendf("{0:10.10g}, {1:10.10g}, {2:10.10g}, {3:10.10g}, 0, 0)", text_mat[0], text_mat[1], text_mat[2], text_mat[3]); tmp->append(";-moz-transform:"); tmp->append(&matrix_str); tmp->append(";-webkit-transform:"); tmp->append(&matrix_str); tmp->append(";-o-transform:"); tmp->append(&matrix_str); tmp->append(";-ms-transform:"); tmp->append(&matrix_str); // Todo: 75% is a wild guess that seems to work pretty well; // We probably need to calculate the real percentage // Based on the characteristic baseline and bounding box of current font // PDF origin is at baseline tmp->append(";-moz-transform-origin: left 75%"); tmp->append(";-webkit-transform-origin: left 75%"); tmp->append(";-o-transform-origin: left 75%"); tmp->append(";-ms-transform-origin: left 75%"); } tmp->append(";}"); } if (xml) { tmp->append("append(std::to_string(i)); tmp->append("\" size=\""); tmp->append(std::to_string(font.getSize())); tmp->append("\" family=\""); tmp->append(fontName); tmp->append("\" color=\""); tmp->append(colorStr); if (font.getColor().getOpacity() != 1.0) { tmp->append("\" opacity=\""); tmp->append(std::to_string(font.getColor().getOpacity())); } tmp->append("\"/>"); } delete fontName; delete colorStr; return tmp; } poppler-24.02.0/utils/HtmlFonts.h000066400000000000000000000104161455701731300165770ustar00rootroot00000000000000//======================================================================== // // This file comes from pdftohtml project // http://pdftohtml.sourceforge.net // // Copyright from: // Gueorgui Ovtcharov // Rainer Dorsch // Mikhail Kruk // //======================================================================== //======================================================================== // // 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) 2010 OSSD CDAC Mumbai by Leena Chourey (leenac@cdacmumbai.in) and Onkar Potdar (onkar@cdacmumbai.in) // Copyright (C) 2010, 2012, 2017, 2018, 2020 Albert Astals Cid // Copyright (C) 2011 Steven Murdoch // Copyright (C) 2011 Joshua Richardson // Copyright (C) 2012 Igor Slepchin // 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 // //======================================================================== #ifndef _HTML_FONTS_H #define _HTML_FONTS_H #include "goo/GooString.h" #include "GfxState.h" #include "CharTypes.h" #include class HtmlFontColor { private: unsigned int r; unsigned int g; unsigned int b; unsigned int opacity; bool Ok(unsigned int xcol) { return xcol <= 255; } GooString *convtoX(unsigned int xcol) const; 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)); } }; class HtmlFont { private: int size; int lineSize; bool italic; bool bold; bool rotOrSkewed; std::string familyName; GooString *FontName; HtmlFontColor color; double rotSkewMat[4]; // only four values needed for rotation and skew public: HtmlFont(const GfxFont &font, int _size, GfxRGB rgb, double opacity); HtmlFont(const HtmlFont &x); HtmlFont &operator=(const HtmlFont &x); HtmlFontColor getColor() const { return color; } ~HtmlFont(); GooString *getFullName(); bool isItalic() const { return italic; } bool isBold() const { return bold; } bool isRotOrSkewed() const { return rotOrSkewed; } int getSize() const { return size; } int getLineSize() const { return lineSize; } void setLineSize(int _lineSize) { lineSize = _lineSize; } void setRotMat(const double *const mat) { rotOrSkewed = true; memcpy(rotSkewMat, mat, sizeof(rotSkewMat)); } const double *getRotMat() const { return rotSkewMat; } GooString *getFontName(); 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 " : ""); }; }; class HtmlFontAccu { private: std::vector accu; public: HtmlFontAccu(); ~HtmlFontAccu(); HtmlFontAccu(const HtmlFontAccu &) = delete; HtmlFontAccu &operator=(const HtmlFontAccu &) = delete; int AddFont(const HtmlFont &font); const HtmlFont *Get(int i) const { return &accu[i]; } GooString *CSStyle(int i, int j = 0); int size() const { return accu.size(); } }; #endif poppler-24.02.0/utils/HtmlLinks.cc000066400000000000000000000074341455701731300167320ustar00rootroot00000000000000//======================================================================== // // This file comes from pdftohtml project // http://pdftohtml.sourceforge.net // // Copyright from: // Gueorgui Ovtcharov // Rainer Dorsch // Mikhail Kruk // //======================================================================== //======================================================================== // // 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) 2008 Boris Toloknov // Copyright (C) 2010, 2021, 2022 Albert Astals Cid // Copyright (C) 2013 Julien Nabet // // 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 "HtmlLinks.h" extern bool xml; HtmlLink::HtmlLink(const HtmlLink &x) { Xmin = x.Xmin; Ymin = x.Ymin; Xmax = x.Xmax; Ymax = x.Ymax; dest = new GooString(x.dest); } HtmlLink::HtmlLink(double xmin, double ymin, double xmax, double ymax, GooString *_dest) { if (xmin < xmax) { Xmin = xmin; Xmax = xmax; } else { Xmin = xmax; Xmax = xmin; } if (ymin < ymax) { Ymin = ymin; Ymax = ymax; } else { Ymin = ymax; Ymax = ymin; } dest = new GooString(_dest); } HtmlLink::~HtmlLink() { delete dest; } bool HtmlLink::isEqualDest(const HtmlLink &x) const { return (!strcmp(dest->c_str(), x.dest->c_str())); } bool HtmlLink::inLink(double xmin, double ymin, double xmax, double ymax) const { double y = (ymin + ymax) / 2; if (y > Ymax) { return false; } return (y > Ymin) && (xmin < Xmax) && (xmax > Xmin); } static GooString *EscapeSpecialChars(GooString *s) { GooString *tmp = nullptr; for (int i = 0, j = 0; i < s->getLength(); i++, j++) { const char *replace = nullptr; switch (s->getChar(i)) { case '"': replace = """; break; case '&': replace = "&"; break; case '<': replace = "<"; break; case '>': replace = ">"; break; default: continue; } if (replace) { if (!tmp) { tmp = new GooString(s); } if (tmp) { tmp->del(j, 1); int l = strlen(replace); tmp->insert(j, replace, l); j += l - 1; } } } return tmp ? tmp : s; } GooString *HtmlLink::getLinkStart() const { GooString *res = new GooString("append(d); if (d != dest) { delete d; } res->append("\">"); return res; } /*GooString* HtmlLink::Link(GooString* content){ //GooString* _dest=new GooString(dest); GooString *tmp=new GooString("append(dest); tmp->append("\">"); tmp->append(content); tmp->append(""); //delete _dest; return tmp; }*/ HtmlLinks::HtmlLinks() { } HtmlLinks::~HtmlLinks() { } bool HtmlLinks::inLink(double xmin, double ymin, double xmax, double ymax, size_t &p) const { for (std::vector::const_iterator i = accu.begin(); i != accu.end(); ++i) { if (i->inLink(xmin, ymin, xmax, ymax)) { p = (i - accu.begin()); return true; } } return false; } const HtmlLink *HtmlLinks::getLink(size_t i) const { return &accu[i]; } poppler-24.02.0/utils/HtmlLinks.h000066400000000000000000000042701455701731300165670ustar00rootroot00000000000000//======================================================================== // // This file comes from pdftohtml project // http://pdftohtml.sourceforge.net // // Copyright from: // Gueorgui Ovtcharov // Rainer Dorsch // Mikhail Kruk // //======================================================================== //======================================================================== // // 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) 2010, 2018, 2021, 2022 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 // //======================================================================== #ifndef _HTML_LINKS #define _HTML_LINKS #include #include #include #include "goo/GooString.h" class HtmlLink { private: double Xmin; double Ymin; double Xmax; double Ymax; GooString *dest; public: HtmlLink(const HtmlLink &x); HtmlLink(double xmin, double ymin, double xmax, double ymax, GooString *_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; }; class HtmlLinks { private: std::vector accu; public: HtmlLinks(); ~HtmlLinks(); HtmlLinks(const HtmlLinks &) = delete; HtmlLinks &operator=(const HtmlLinks &) = delete; void AddLink(const HtmlLink &x) { accu.push_back(x); } bool inLink(double xmin, double ymin, double xmax, double ymax, size_t &p) const; const HtmlLink *getLink(size_t i) const; }; #endif poppler-24.02.0/utils/HtmlOutputDev.cc000066400000000000000000001705231455701731300176110ustar00rootroot00000000000000//======================================================================== // // HtmlOutputDev.cc // // Copyright 1997-2002 Glyph & Cog, LLC // // Changed 1999-2000 by G.Ovtcharov // // Changed 2002 by Mikhail Kruk // //======================================================================== //======================================================================== // // 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) 2005-2013, 2016-2022 Albert Astals Cid // Copyright (C) 2008 Kjartan Maraas // Copyright (C) 2008 Boris Toloknov // Copyright (C) 2008 Haruyuki Kawabe // Copyright (C) 2008 Tomas Are Haavet // Copyright (C) 2009 Warren Toomey // Copyright (C) 2009, 2011 Carlos Garcia Campos // Copyright (C) 2009 Reece Dunn // Copyright (C) 2010, 2012, 2013, 2022 Adrian Johnson // Copyright (C) 2010 Hib Eris // Copyright (C) 2010 OSSD CDAC Mumbai by Leena Chourey (leenac@cdacmumbai.in) and Onkar Potdar (onkar@cdacmumbai.in) // Copyright (C) 2011 Joshua Richardson // Copyright (C) 2011 Stephen Reichling // Copyright (C) 2011, 2012 Igor Slepchin // Copyright (C) 2012 Ihar Filipau // Copyright (C) 2012 Gerald Schmidt // Copyright (C) 2012 Pino Toscano // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2013 Julien Nabet // Copyright (C) 2013 Johannes Brandstätter // Copyright (C) 2014 Fabio D'Urso // Copyright (C) 2016 Vincent Le Garrec // Copyright (C) 2017 Caolán McNamara // 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) 2020 Eddie Kohler // Copyright (C) 2021 Christopher Hasse // Copyright (C) 2022 Brian Rosenfield // 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 // //======================================================================== #include "config.h" #include #include #include #include #include #include #include #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 "GlobalParams.h" #include "HtmlOutputDev.h" #include "HtmlFonts.h" #include "HtmlUtils.h" #include "InMemoryFile.h" #include "Outline.h" #include "PDFDoc.h" #define DEBUG __FILE__ << ": " << __LINE__ << ": DEBUG: " class HtmlImage { public: HtmlImage(std::unique_ptr &&_fName, GfxState *state) : fName(std::move(_fName)) { state->transform(0, 0, &xMin, &yMax); state->transform(1, 1, &xMax, &yMin); } ~HtmlImage() = default; HtmlImage(const HtmlImage &) = delete; HtmlImage &operator=(const HtmlImage &) = delete; double xMin, xMax; // image x coordinates double yMin, yMax; // image y coordinates std::unique_ptr fName; // image file name }; // returns true if x is closer to y than x is to z static inline bool IS_CLOSER(double x, double y, double z) { return std::fabs((x) - (y)) < std::fabs((x) - (z)); } extern bool complexMode; extern bool singleHtml; extern bool dataUrls; extern bool ignore; extern bool printCommands; extern bool printHtml; extern bool noframes; extern bool stout; extern bool xml; extern bool noRoundedCoordinates; extern bool showHidden; extern bool noMerge; extern double wordBreakThreshold; 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) return new GooString(p,i+1); return new GooString(); } #endif static std::unique_ptr 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) { if (!uLen) { return std::make_unique(""); } std::unique_ptr 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 : '?'); } } return gstr_buff0; } //------------------------------------------------------------------------ // HtmlString //------------------------------------------------------------------------ HtmlString::HtmlString(GfxState *state, double fontSize, HtmlFontAccu *_fonts) : fonts(_fonts) { double x, y; state->transform(state->getCurX(), state->getCurY(), &x, &y); if (std::shared_ptr font = state->getFont()) { double ascent = font->getAscent(); double descent = font->getDescent(); if (ascent > 1.05) { // printf( "ascent=%.15g is too high, descent=%.15g\n", ascent, descent ); ascent = 1.05; } if (descent < -0.4) { // printf( "descent %.15g is too low, ascent=%.15g\n", descent, ascent ); descent = -0.4; } yMin = y - ascent * fontSize; yMax = y - descent * fontSize; GfxRGB rgb; state->getFillRGB(&rgb); HtmlFont hfont = HtmlFont(*font, std::lround(fontSize), rgb, state->getFillOpacity()); if (isMatRotOrSkew(state->getTextMat())) { double normalizedMatrix[4]; memcpy(normalizedMatrix, state->getTextMat(), sizeof(normalizedMatrix)); // 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; } normalizedMatrix[1] *= -1; normalizedMatrix[2] *= -1; if (debug) { std::cerr << DEBUG << "after reflecting angle: " << print_matrix(normalizedMatrix)->c_str() << std::endl; } normalizeRotMat(normalizedMatrix); if (debug) { std::cerr << DEBUG << "after norm: " << print_matrix(normalizedMatrix)->c_str() << std::endl; } hfont.setRotMat(normalizedMatrix); } fontpos = fonts->AddFont(hfont); } else { // this means that the PDF file draws text without a current font, // which should never happen yMin = y - 0.95 * fontSize; yMax = y + 0.35 * fontSize; fontpos = 0; } if (yMin == yMax) { // this is a sanity check for a case that shouldn't happen -- but // if it does happen, we want to avoid dividing by zero later yMin = y; yMax = y + 1; } col = 0; text = nullptr; xRight = nullptr; link = nullptr; len = size = 0; yxNext = nullptr; xyNext = nullptr; htext = std::make_unique(); dir = textDirUnknown; } HtmlString::~HtmlString() { gfree(text); gfree(xRight); } void HtmlString::addChar(GfxState *state, double x, double y, double dx, double dy, Unicode u) { if (dir == textDirUnknown) { // dir = UnicodeMap::getDirection(u); dir = textDirLeftRight; } if (len == size) { size += 16; text = (Unicode *)grealloc(text, size * sizeof(Unicode)); xRight = (double *)grealloc(xRight, size * sizeof(double)); } text[len] = u; if (len == 0) { xMin = x; } xMax = xRight[len] = x + dx; // printf("added char: %f %f xright = %f\n", x, dx, x+dx); ++len; } void HtmlString::endString() { if (dir == textDirRightLeft && len > 1) { // printf("will reverse!\n"); for (int i = 0; i < len / 2; i++) { Unicode ch = text[i]; text[i] = text[len - i - 1]; text[len - i - 1] = ch; } } } //------------------------------------------------------------------------ // HtmlPage //------------------------------------------------------------------------ HtmlPage::HtmlPage(bool rawOrderA) { rawOrder = rawOrderA; curStr = nullptr; yxStrings = nullptr; xyStrings = nullptr; yxCur1 = yxCur2 = nullptr; fonts = new HtmlFontAccu(); links = new HtmlLinks(); pageWidth = 0; pageHeight = 0; fontsPageMarker = 0; DocName = nullptr; firstPage = -1; } HtmlPage::~HtmlPage() { clear(); delete DocName; delete fonts; delete links; for (auto entry : imgList) { delete entry; } } void HtmlPage::updateFont(GfxState *state) { const char *name; int code; double dimLength; // adjust the font size fontSize = state->getTransformedFontSize(); const GfxFont *const font = state->getFont().get(); if (font && font->getType() == fontType3) { // Grab the font size from the font bounding box if possible - remember to // scale from the glyph coordinate system. const double *fontBBox = font->getFontBBox(); const double *fontMat = font->getFontMatrix(); dimLength = (fontBBox[3] - fontBBox[1]) * fontMat[3]; if (dimLength > 0) { fontSize *= dimLength; } else { // This is a hack which makes it possible to deal with some Type 3 // fonts. The problem is that it's impossible to know what the // base coordinate system used in the font is without actually // rendering the font. This code tries to guess by looking at the // width of the character 'm' (which breaks if the font is a // subset that doesn't contain 'm'). for (code = 0; code < 256; ++code) { if ((name = ((Gfx8BitFont *)font)->getCharName(code)) && name[0] == 'm' && name[1] == '\0') { break; } } if (code < 256) { dimLength = ((Gfx8BitFont *)font)->getWidth(code); if (dimLength != 0) { // 600 is a generic average 'm' width -- yes, this is a hack fontSize *= dimLength / 0.6; } } if (fontMat[0] != 0) { fontSize *= fabs(fontMat[3] / fontMat[0]); } } } } void HtmlPage::beginString(GfxState *state, const GooString *s) { curStr = new HtmlString(state, fontSize, fonts); } void HtmlPage::conv() { for (HtmlString *tmp = yxStrings; tmp; tmp = tmp->yxNext) { tmp->htext = HtmlFont::HtmlFilter(tmp->text, tmp->len); size_t linkIndex = 0; if (links->inLink(tmp->xMin, tmp->yMin, tmp->xMax, tmp->yMax, linkIndex)) { tmp->link = links->getLink(linkIndex); } } } void HtmlPage::addChar(GfxState *state, double x, double y, double dx, double dy, double ox, double oy, const Unicode *u, int uLen) { double x1, y1, w1, h1, dx2, dy2; int n, i; state->transform(x, y, &x1, &y1); n = curStr->len; // check that new character is in the same direction as current string // and is not too far away from it before adding // if ((UnicodeMap::getDirection(u[0]) != curStr->dir) || // XXX if (debug) { const double *text_mat = state->getTextMat(); // rotation is (cos q, sin q, -sin q, cos q, 0, 0) // 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(); } } if (n > 0 && // don't start a new string, unless there is already a string // TODO: the following line assumes that text is flowing left to // right, which will not necessarily be the case, e.g. if rotated; // It assesses whether or not two characters are close enough to // be part of the same string fabs(x1 - curStr->xRight[n - 1]) > wordBreakThreshold * (curStr->yMax - curStr->yMin) && // rotation is (cos q, sin q, -sin q, cos q, 0, 0) // sin q is zero iff there is no rotation, or 180 deg. rotation; // for 180 rotation, cos q will be negative !rot_matrices_equal(curStr->getFont().getRotMat(), state->getTextMat())) { endString(); beginString(state, nullptr); } state->textTransformDelta(state->getCharSpace() * state->getHorizScaling(), 0, &dx2, &dy2); dx -= dx2; dy -= dy2; state->transformDelta(dx, dy, &w1, &h1); if (uLen != 0) { w1 /= uLen; h1 /= uLen; } for (i = 0; i < uLen; ++i) { curStr->addChar(state, x1 + i * w1, y1 + i * h1, w1, h1, u[i]); } } void HtmlPage::endString() { HtmlString *p1, *p2; double h, y1, y2; // throw away zero-length strings -- they don't have valid xMin/xMax // values, and they're useless anyway if (curStr->len == 0) { delete curStr; curStr = nullptr; return; } curStr->endString(); #if 0 //~tmp if (curStr->yMax - curStr->yMin > 20) { delete curStr; curStr = NULL; return; } #endif // insert string in y-major list h = curStr->yMax - curStr->yMin; y1 = curStr->yMin + 0.5 * h; y2 = curStr->yMin + 0.8 * h; if (rawOrder) { p1 = yxCur1; p2 = nullptr; } else if ((!yxCur1 || (y1 >= yxCur1->yMin && (y2 >= yxCur1->yMax || curStr->xMax >= yxCur1->xMin))) && (!yxCur2 || (y1 < yxCur2->yMin || (y2 < yxCur2->yMax && curStr->xMax < yxCur2->xMin)))) { p1 = yxCur1; p2 = yxCur2; } else { for (p1 = nullptr, p2 = yxStrings; p2; p1 = p2, p2 = p2->yxNext) { if (y1 < p2->yMin || (y2 < p2->yMax && curStr->xMax < p2->xMin)) { break; } } yxCur2 = p2; } yxCur1 = curStr; if (p1) { p1->yxNext = curStr; } else { yxStrings = curStr; } curStr->yxNext = p2; curStr = nullptr; } static const char *strrstr(const char *s, const char *ss) { const char *p = strstr(s, ss); for (const char *pp = p; pp != nullptr; pp = strstr(p + 1, ss)) { p = pp; } return p; } static void CloseTags(GooString *htext, bool &finish_a, bool &finish_italic, bool &finish_bold) { const char *last_italic = finish_italic && (finish_bold || finish_a) ? strrstr(htext->c_str(), "") : nullptr; const char *last_bold = finish_bold && (finish_italic || finish_a) ? strrstr(htext->c_str(), "") : nullptr; const char *last_a = finish_a && (finish_italic || finish_bold) ? strrstr(htext->c_str(), " (last_italic > last_bold ? last_italic : last_bold)) { htext->append("", 4); finish_a = false; } if (finish_italic && finish_bold && last_italic > last_bold) { htext->append("", 4); finish_italic = false; } if (finish_bold) { htext->append("", 4); } if (finish_italic) { htext->append("", 4); } if (finish_a) { htext->append(""); } } // Strings are lines of text; // This function aims to combine strings into lines and paragraphs if !noMerge // It may also strip out duplicate strings (if they are on top of each other); sometimes they are to create a font effect void HtmlPage::coalesce() { HtmlString *str1, *str2; double space, horSpace, vertSpace, vertOverlap; bool addSpace, addLineBreak; int n, i; double curX, curY; #if 0 //~ for debugging for (str1 = yxStrings; str1; str1 = str1->yxNext) { printf("x=%f..%f y=%f..%f size=%2d '", str1->xMin, str1->xMax, str1->yMin, str1->yMax, (int)(str1->yMax - str1->yMin)); for (i = 0; i < str1->len; ++i) { fputc(str1->text[i] & 0xff, stdout); } printf("'\n"); } printf("\n------------------------------------------------------------\n\n"); #endif str1 = yxStrings; if (!str1) { return; } //----- discard duplicated text (fake boldface, drop shadows) if (!complexMode) { /* if not in complex mode get rid of duplicate strings */ HtmlString *str3; bool found; while (str1) { double size = str1->yMax - str1->yMin; double xLimit = str1->xMin + size; found = false; for (str2 = str1, str3 = str1->yxNext; str3 && str3->xMin < xLimit; str2 = str3, str3 = str2->yxNext) { if (str3->len == str1->len && !memcmp(str3->text, str1->text, str1->len * sizeof(Unicode)) && fabs(str3->yMin - str1->yMin) < size * 0.2 && fabs(str3->yMax - str1->yMax) < size * 0.2 && fabs(str3->xMax - str1->xMax) < size * 0.1) { found = true; // printf("found duplicate!\n"); break; } } if (found) { str2->xyNext = str3->xyNext; str2->yxNext = str3->yxNext; delete str3; } else { str1 = str1->yxNext; } } } /*- !complexMode */ str1 = yxStrings; const HtmlFont *hfont1 = getFont(str1); if (hfont1->isBold()) { str1->htext->insert(0, "", 3); } if (hfont1->isItalic()) { str1->htext->insert(0, "", 3); } if (str1->getLink() != nullptr) { GooString *ls = str1->getLink()->getLinkStart(); str1->htext->insert(0, ls); delete ls; } curX = str1->xMin; curY = str1->yMin; while (str1 && (str2 = str1->yxNext)) { const HtmlFont *hfont2 = getFont(str2); space = str1->yMax - str1->yMin; // the height of the font's bounding box horSpace = str2->xMin - str1->xMax; // if strings line up on left-hand side AND they are on subsequent lines, we need a line break addLineBreak = !noMerge && (fabs(str1->xMin - str2->xMin) < 0.4) && IS_CLOSER(str2->yMax, str1->yMax + space, str1->yMax); vertSpace = str2->yMin - str1->yMax; // printf("coalesce %d %d %f? ", str1->dir, str2->dir, d); if (str2->yMin >= str1->yMin && str2->yMin <= str1->yMax) { vertOverlap = str1->yMax - str2->yMin; } else if (str2->yMax >= str1->yMin && str2->yMax <= str1->yMax) { vertOverlap = str2->yMax - str1->yMin; } else { vertOverlap = 0; } // Combine strings if: // They appear to be the same font (complex mode only) && going in the same direction AND at least one of the following: // 1. They appear to be part of the same line of text // 2. They appear to be subsequent lines of a paragraph // We assume (1) or (2) above, respectively, based on: // (1) strings overlap vertically AND // horizontal space between end of str1 and start of str2 is consistent with a single space or less; // when rawOrder, the strings have to overlap vertically by at least 50% // (2) Strings flow down the page, but the space between them is not too great, and they are lined up on the left if (((((rawOrder && vertOverlap > 0.5 * space) || (!rawOrder && str2->yMin < str1->yMax)) && (horSpace > -0.5 * space && horSpace < space)) || (vertSpace >= 0 && vertSpace < 0.5 * space && addLineBreak)) && (!complexMode || (hfont1->isEqualIgnoreBold(*hfont2))) && // in complex mode fonts must be the same, in other modes fonts do not metter str1->dir == str2->dir // text direction the same ) { // printf("yes\n"); n = str1->len + str2->len; if ((addSpace = horSpace > wordBreakThreshold * space)) { ++n; } if (addLineBreak) { ++n; } str1->size = (n + 15) & ~15; str1->text = (Unicode *)grealloc(str1->text, str1->size * sizeof(Unicode)); str1->xRight = (double *)grealloc(str1->xRight, str1->size * sizeof(double)); if (addSpace) { str1->text[str1->len] = 0x20; str1->htext->append(xml ? " " : " "); str1->xRight[str1->len] = str2->xMin; ++str1->len; } if (addLineBreak) { str1->text[str1->len] = '\n'; str1->htext->append("
    "); str1->xRight[str1->len] = str2->xMin; ++str1->len; str1->yMin = str2->yMin; str1->yMax = str2->yMax; str1->xMax = str2->xMax; int fontLineSize = hfont1->getLineSize(); int curLineSize = (int)(vertSpace + space); if (curLineSize != fontLineSize) { HtmlFont *newfnt = new HtmlFont(*hfont1); newfnt->setLineSize(curLineSize); str1->fontpos = fonts->AddFont(*newfnt); delete newfnt; hfont1 = getFont(str1); // we have to reget hfont2 because it's location could have // changed on resize hfont2 = getFont(str2); } } for (i = 0; i < str2->len; ++i) { str1->text[str1->len] = str2->text[i]; str1->xRight[str1->len] = str2->xRight[i]; ++str1->len; } /* fix , if str1 and str2 differ and handle switch of links */ const HtmlLink *hlink1 = str1->getLink(); const HtmlLink *hlink2 = str2->getLink(); bool switch_links = !hlink1 || !hlink2 || !hlink1->isEqualDest(*hlink2); 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.get(), finish_a, finish_italic, finish_bold); if (switch_links && hlink2 != nullptr) { GooString *ls = hlink2->getLinkStart(); str1->htext->append(ls); delete ls; } if ((!hfont1->isItalic() || finish_italic) && hfont2->isItalic()) { str1->htext->append("", 3); } if ((!hfont1->isBold() || finish_bold) && hfont2->isBold()) { str1->htext->append("", 3); } str1->htext->append(str2->htext.get()); // str1 now contains href for link of str2 (if it is defined) str1->link = str2->link; hfont1 = hfont2; if (str2->xMax > str1->xMax) { str1->xMax = str2->xMax; } if (str2->yMax > str1->yMax) { str1->yMax = str2->yMax; } str1->yxNext = str2->yxNext; delete str2; } else { // keep strings separate // printf("no\n"); bool finish_a = str1->getLink() != nullptr; bool finish_bold = hfont1->isBold(); bool finish_italic = hfont1->isItalic(); CloseTags(str1->htext.get(), finish_a, finish_italic, finish_bold); str1->xMin = curX; str1->yMin = curY; str1 = str2; curX = str1->xMin; curY = str1->yMin; hfont1 = hfont2; if (hfont1->isBold()) { str1->htext->insert(0, "", 3); } if (hfont1->isItalic()) { str1->htext->insert(0, "", 3); } if (str1->getLink() != nullptr) { GooString *ls = str1->getLink()->getLinkStart(); str1->htext->insert(0, ls); delete ls; } } } str1->xMin = curX; str1->yMin = curY; bool finish_bold = hfont1->isBold(); bool finish_italic = hfont1->isItalic(); bool finish_a = str1->getLink() != nullptr; 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("\n------------------------------------------------------------\n\n"); #endif } void HtmlPage::dumpAsXML(FILE *f, int page) { fprintf(f, "\n", pageHeight, pageWidth); for (int i = fontsPageMarker; i < fonts->size(); i++) { GooString *fontCSStyle = fonts->CSStyle(i); fprintf(f, "\t%s\n", fontCSStyle->c_str()); delete fontCSStyle; } 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)); } else { fprintf(f, "yMin, img->xMin); fprintf(f, "width=\"%f\" height=\"%f\" ", img->xMax - img->xMin, img->yMax - img->yMin); } fprintf(f, "src=\"%s\"/>\n", img->fName->c_str()); delete img; } imgList.clear(); 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)); } else { fprintf(f, "yMin, tmp->xMin); fprintf(f, "width=\"%f\" height=\"%f\" ", tmp->xMax - tmp->xMin, tmp->yMax - tmp->yMin); } fprintf(f, "font=\"%d\">", tmp->fontpos); fputs(tmp->htext->c_str(), f); fputs("\n", f); } } fputs("\n", f); } static void printCSS(FILE *f) { // Image flip/flop CSS // Source: // http://stackoverflow.com/questions/1309055/cross-browser-way-to-flip-html-image-via-javascript-css // tested in Chrome, Fx (Linux) and IE9 (W7) static const char css[] = "" "\n"; fwrite(css, sizeof(css) - 1, 1, f); } int HtmlPage::dumpComplexHeaders(FILE *const file, FILE *&pageFile, int page) { if (!noframes) { const std::string pgNum = std::to_string(page); std::string pageFileName(DocName->toStr()); if (!singleHtml) { pageFileName += '-' + pgNum + ".html"; pageFile = fopen(pageFileName.c_str(), "w"); } else { pageFileName += "-html.html"; pageFile = fopen(pageFileName.c_str(), "a"); } if (!pageFile) { error(errIO, -1, "Couldn't open html file '{0:s}'", pageFileName.c_str()); return 1; } if (!singleHtml) { fprintf(pageFile, "%s\n\n\nPage %d\n\n", DOCTYPE, page); } else { fprintf(pageFile, "%s\n\n\n%s\n\n", DOCTYPE, pageFileName.c_str()); } const std::string htmlEncoding = HtmlOutputDev::mapEncodingToHtml(globalParams->getTextEncodingName()); if (!singleHtml) { fprintf(pageFile, "\n", htmlEncoding.c_str()); } else { fprintf(pageFile, "\n
    \n", htmlEncoding.c_str()); } } else { pageFile = file; fprintf(pageFile, "\n", page); fprintf(pageFile, "\n", page); } return 0; } void HtmlPage::dumpComplex(FILE *file, int page, const std::vector &backgroundImages) { FILE *pageFile; if (firstPage == -1) { firstPage = page; } if (dumpComplexHeaders(file, pageFile, page)) { error(errIO, -1, "Couldn't write headers."); return; } fputs("\n", pageFile); if (!noframes) { fputs("\n\n", pageFile); } fprintf(pageFile, "
    \n", page, pageWidth, pageHeight); if (!ignore && (size_t)(page - firstPage) < backgroundImages.size()) { fprintf(pageFile, "\"background\n", pageWidth, pageHeight, backgroundImages[page - firstPage].c_str()); } for (HtmlString *tmp1 = yxStrings; tmp1; tmp1 = tmp1->yxNext) { if (tmp1->htext) { fprintf(pageFile, "

    yMin), xoutRound(tmp1->xMin)); if (!singleHtml) { fputc('0', pageFile); } else { fprintf(pageFile, "%d", page); } fprintf(pageFile, "%d\">", tmp1->fontpos); fputs(tmp1->htext->c_str(), pageFile); fputs("

    \n", pageFile); } } fputs("
    \n", pageFile); if (!noframes) { fputs("\n\n", pageFile); fclose(pageFile); } } void HtmlPage::dump(FILE *f, int pageNum, const std::vector &backgroundImages) { if (complexMode || singleHtml) { if (xml) { dumpAsXML(f, pageNum); } if (!xml) { dumpComplex(f, pageNum, backgroundImages); } } else { fprintf(f, "", pageNum); // Loop over the list of image names on this page for (auto ptr : imgList) { auto img = static_cast(ptr); // see printCSS() for class names const char *styles[4] = { "", " class=\"xflip\"", " class=\"yflip\"", " class=\"xyflip\"" }; int style_index = 0; if (img->xMin > img->xMax) { style_index += 1; // xFlip } if (img->yMin > img->yMax) { style_index += 2; // yFlip } fprintf(f, "
    \n", styles[style_index], img->fName->c_str()); delete img; } imgList.clear(); for (HtmlString *tmp = yxStrings; tmp; tmp = tmp->yxNext) { if (tmp->htext) { fputs(tmp->htext->c_str(), f); fputs("
    \n", f); } } fputs("
    \n", f); } } void HtmlPage::clear() { HtmlString *p1, *p2; if (curStr) { delete curStr; curStr = nullptr; } for (p1 = yxStrings; p1; p1 = p2) { p2 = p1->yxNext; delete p1; } yxStrings = nullptr; xyStrings = nullptr; yxCur1 = yxCur2 = nullptr; if (!noframes) { delete fonts; fonts = new HtmlFontAccu(); fontsPageMarker = 0; } else { fontsPageMarker = fonts->size(); } delete links; links = new HtmlLinks(); } void HtmlPage::setDocName(const char *fname) { DocName = new GooString(fname); } void HtmlPage::addImage(std::unique_ptr &&fname, GfxState *state) { HtmlImage *img = new HtmlImage(std::move(fname), state); imgList.push_back(img); } //------------------------------------------------------------------------ // HtmlMetaVar //------------------------------------------------------------------------ HtmlMetaVar::HtmlMetaVar(const char *_name, const char *_content) { name = new GooString(_name); content = new GooString(_content); } HtmlMetaVar::~HtmlMetaVar() { delete name; delete content; } GooString *HtmlMetaVar::toString() const { GooString *result = new GooString("append(name); result->append("\" content=\""); result->append(content); result->append("\"/>"); return result; } //------------------------------------------------------------------------ // HtmlOutputDev //------------------------------------------------------------------------ static const char *HtmlEncodings[][2] = { { "Latin1", "ISO-8859-1" }, { nullptr, nullptr } }; std::string HtmlOutputDev::mapEncodingToHtml(const std::string &encoding) { for (int i = 0; HtmlEncodings[i][0] != nullptr; i++) { if (encoding == HtmlEncodings[i][0]) { return HtmlEncodings[i][1]; } } return encoding; } void HtmlOutputDev::doFrame(int firstPage) { GooString *fName = new GooString(Docname); fName->append(".html"); if (!(fContentsFrame = fopen(fName->c_str(), "w"))) { error(errIO, -1, "Couldn't open html file '{0:t}'", fName); delete fName; return; } delete fName; const std::string baseName = gbasename(Docname->c_str()); fputs(DOCTYPE, fContentsFrame); fputs("\n", fContentsFrame); fputs("\n", fContentsFrame); fprintf(fContentsFrame, "\n%s", docTitle->c_str()); const std::string htmlEncoding = mapEncodingToHtml(globalParams->getTextEncodingName()); fprintf(fContentsFrame, "\n\n", htmlEncoding.c_str()); dumpMetaVars(fContentsFrame); fprintf(fContentsFrame, "\n"); fputs("\n", fContentsFrame); fprintf(fContentsFrame, "\n", baseName.c_str()); fputs("\n\n\n", fContentsFrame); fclose(fContentsFrame); } HtmlOutputDev::HtmlOutputDev(Catalog *catalogA, const char *fileName, const char *title, const char *author, const char *keywords, const char *subject, const char *date, bool rawOrderA, int firstPage, bool outline) { catalog = catalogA; fContentsFrame = nullptr; page = nullptr; docTitle = new GooString(title); pages = nullptr; dumpJPEG = true; // write = true; rawOrder = rawOrderA; this->doOutline = outline; ok = false; // this->firstPage = firstPage; // pageNum=firstPage; // open file needClose = false; pages = new HtmlPage(rawOrder); glMetaVars.push_back(new HtmlMetaVar("generator", "pdftohtml 0.36")); if (author) { glMetaVars.push_back(new HtmlMetaVar("author", author)); } if (keywords) { glMetaVars.push_back(new HtmlMetaVar("keywords", keywords)); } if (date) { glMetaVars.push_back(new HtmlMetaVar("date", date)); } if (subject) { glMetaVars.push_back(new HtmlMetaVar("subject", subject)); } maxPageWidth = 0; maxPageHeight = 0; pages->setDocName(fileName); Docname = new GooString(fileName); // for non-xml output (complex or simple) with frames generate the left frame if (!xml && !noframes) { if (!singleHtml) { GooString *left = new GooString(fileName); left->append("_ind.html"); doFrame(firstPage); if (!(fContentsFrame = fopen(left->c_str(), "w"))) { error(errIO, -1, "Couldn't open html file '{0:t}'", left); delete left; return; } delete left; fputs(DOCTYPE, fContentsFrame); fputs("\n\n\n\n\n", fContentsFrame); if (doOutline) { fprintf(fContentsFrame, "Outline
    ", gbasename(Docname->c_str()).c_str(), complexMode ? "-outline.html" : "s.html#outline"); } } if (!complexMode) { /* not in complex mode */ GooString *right = new GooString(fileName); right->append("s.html"); if (!(page = fopen(right->c_str(), "w"))) { error(errIO, -1, "Couldn't open html file '{0:t}'", right); delete right; return; } delete right; fputs(DOCTYPE, page); fputs("\n\n\n", page); printCSS(page); fputs("\n\n", page); } } if (noframes) { if (stout) { page = stdout; } else { GooString *right = new GooString(fileName); if (!xml) { right->append(".html"); } if (xml) { right->append(".xml"); } if (!(page = fopen(right->c_str(), "w"))) { error(errIO, -1, "Couldn't open html file '{0:t}'", right); delete right; return; } delete right; } const std::string htmlEncoding = mapEncodingToHtml(globalParams->getTextEncodingName()); if (xml) { fprintf(page, "\n", htmlEncoding.c_str()); fputs("\n\n", page); fprintf(page, "\n", PACKAGE_NAME, PACKAGE_VERSION); } else { fprintf(page, "%s\n\n\n%s\n", DOCTYPE, docTitle->c_str()); fprintf(page, "\n", htmlEncoding.c_str()); dumpMetaVars(page); printCSS(page); fprintf(page, "\n"); fprintf(page, "\n"); } } ok = true; } HtmlOutputDev::~HtmlOutputDev() { delete Docname; delete docTitle; for (auto entry : glMetaVars) { delete entry; } if (fContentsFrame) { fputs("\n\n", fContentsFrame); fclose(fContentsFrame); } if (page != nullptr) { if (xml) { fputs("\n", page); fclose(page); } else if (!complexMode || xml || noframes) { fputs("\n\n", page); fclose(page); } } if (pages) { delete pages; } } void HtmlOutputDev::startPage(int pageNumA, GfxState *state, XRef *xref) { #if 0 if (mode&&!xml){ if (write){ write=false; GooString* fname=Dirname(Docname); fname->append("image.log"); if((tin=fopen(getFileNameFromPath(fname->c_str(),fname->getLength()),"w"))==NULL){ printf("Error : can not open %s",fname); exit(1); } delete fname; // 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()); } } #endif pageNum = pageNumA; const std::string str = gbasename(Docname->c_str()); pages->clear(); if (!noframes) { if (fContentsFrame) { if (complexMode) { fprintf(fContentsFrame, "Page %d
    \n", pageNum); } } pages->pageWidth = static_cast(state->getPageWidth()); pages->pageHeight = static_cast(state->getPageHeight()); } void HtmlOutputDev::endPage() { std::unique_ptr linksList = docPage->getLinks(); for (AnnotLink *link : linksList->getLinks()) { doProcessLink(link); } pages->conv(); pages->coalesce(); pages->dump(page, pageNum, backgroundImages); // I don't yet know what to do in the case when there are pages of different // sizes and we want complex output: running ghostscript many times // seems very inefficient. So for now I'll just use last page's size maxPageWidth = pages->pageWidth; maxPageHeight = pages->pageHeight; // if(!noframes&&!xml) fputs("
    \n", fContentsFrame); if (!stout && !globalParams->getErrQuiet()) { printf("Page-%d\n", (pageNum)); } } void HtmlOutputDev::addBackgroundImage(const std::string &img) { backgroundImages.push_back(img); } void HtmlOutputDev::updateFont(GfxState *state) { pages->updateFont(state); } void HtmlOutputDev::beginString(GfxState *state, const GooString *s) { pages->beginString(state, s); } void HtmlOutputDev::endString(GfxState *state) { pages->endString(); } void HtmlOutputDev::drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int /*nBytes*/, const Unicode *u, int uLen) { if (!showHidden && (state->getRender() & 3) == 3) { return; } pages->addChar(state, x, y, dx, dy, originX, originY, u, uLen); } void HtmlOutputDev::drawJpegImage(GfxState *state, Stream *str) { InMemoryFile ims; FILE *f1 = nullptr; int c; // open the image file std::unique_ptr 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()); return; } // initialize stream str = str->getNextStream(); str->reset(); // copy the stream while ((c = str->getChar()) != EOF) { fputc(c, f1); } fclose(f1); if (dataUrls) { fName = std::make_unique(std::string("data:image/jpeg;base64,") + gbase64Encode(ims.getBuffer())); } pages->addImage(std::move(fName), state); } void HtmlOutputDev::drawPngImage(GfxState *state, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool isMask) { #ifdef ENABLE_LIBPNG FILE *f1; InMemoryFile ims; if (!colorMap && !isMask) { error(errInternal, -1, "Can't have color image without a color map"); return; } // open the image file std::unique_ptr fName = createImageFileName("png"); f1 = dataUrls ? ims.open("wb") : fopen(fName->c_str(), "wb"); if (!f1) { error(errIO, -1, "Couldn't open image file '{0:t}'", fName.get()); return; } PNGWriter *writer = new PNGWriter(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); return; } if (!isMask) { unsigned char *p; GfxRGB rgb; 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(); if (!p) { error(errIO, -1, "Failed to read PNG. '{0:t}' will be incorrect", fName.get()); gfree(row); delete writer; delete imgStr; fclose(f1); return; } for (int x = 0; x < width; x++) { colorMap->getRGB(p, &rgb); // Write the RGB pixels into the row row[3 * x] = colToByte(rgb.r); row[3 * x + 1] = colToByte(rgb.g); row[3 * x + 2] = colToByte(rgb.b); 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); return; } } gfree(row); imgStr->close(); delete imgStr; } else { // isMask == true int size = (width + 7) / 8; // PDF masks use 0 = draw current color, 1 = leave unchanged. // We invert this to provide the standard interpretation of alpha // (0 = transparent, 1 = opaque). If the colorMap already inverts // the mask we leave the data unchanged. int invert_bits = 0xff; if (colorMap) { GfxGray gray; unsigned char zero[gfxColorMaxComps]; memset(zero, 0, sizeof(zero)); colorMap->getGray(zero, &gray); if (colToByte(gray) == 0) { invert_bits = 0x00; } } str->reset(); unsigned char *png_row = (unsigned char *)gmalloc(size); for (int ri = 0; ri < height; ++ri) { for (int i = 0; i < size; i++) { 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); gfree(png_row); return; } } str->close(); gfree(png_row); } str->close(); writer->close(); delete writer; fclose(f1); if (dataUrls) { fName = std::make_unique(std::string("data:image/png;base64,") + gbase64Encode(ims.getBuffer())); } pages->addImage(std::move(fName), state); #else return; #endif } std::unique_ptr HtmlOutputDev::createImageFileName(const char *ext) { return GooString::format("{0:s}-{1:d}_{2:d}.{3:s}", Docname->c_str(), pageNum, pages->getNumImages() + 1, ext); } void HtmlOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg) { if (ignore || (complexMode && !xml)) { OutputDev::drawImageMask(state, ref, str, width, height, invert, interpolate, inlineImg); return; } // dump JPEG file if (dumpJPEG && str->getKind() == strDCT) { drawJpegImage(state, str); } else { #ifdef ENABLE_LIBPNG drawPngImage(state, str, width, height, nullptr, true); #else OutputDev::drawImageMask(state, ref, str, width, height, invert, interpolate, inlineImg); #endif } } void HtmlOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) { if (ignore || (complexMode && !xml)) { OutputDev::drawImage(state, ref, str, width, height, colorMap, interpolate, maskColors, inlineImg); return; } /*if( !globalParams->getErrQuiet() ) printf("image stream of kind %d\n", str->getKind());*/ // dump JPEG file if (dumpJPEG && str->getKind() == strDCT && (colorMap->getNumPixelComps() == 1 || colorMap->getNumPixelComps() == 3) && !inlineImg) { drawJpegImage(state, str); } else { #ifdef ENABLE_LIBPNG drawPngImage(state, str, width, height, colorMap); #else OutputDev::drawImage(state, ref, str, width, height, colorMap, interpolate, maskColors, inlineImg); #endif } } void HtmlOutputDev::doProcessLink(AnnotLink *link) { double _x1, _y1, _x2, _y2; int x1, y1, x2, y2; link->getRect(&_x1, &_y1, &_x2, &_y2); cvtUserToDev(_x1, _y1, &x1, &y1); cvtUserToDev(_x2, _y2, &x2, &y2); GooString *_dest = getLinkDest(link); HtmlLink t((double)x1, (double)y2, (double)x2, (double)y1, _dest); pages->AddLink(t); delete _dest; } GooString *HtmlOutputDev::getLinkDest(AnnotLink *link) { if (!link->getAction()) { return new GooString(); } switch (link->getAction()->getKind()) { case actionGoTo: { int destPage = 1; LinkGoTo *ha = (LinkGoTo *)link->getAction(); std::unique_ptr dest; if (ha->getDest() != nullptr) { dest = std::make_unique(*ha->getDest()); } else if (ha->getNamedDest() != nullptr) { dest = catalog->findDest(ha->getNamedDest()); } if (dest) { GooString *file = new GooString(gbasename(Docname->c_str())); if (dest->isPageRef()) { const Ref pageref = dest->getPageRef(); destPage = catalog->findPage(pageref); } else { destPage = dest->getPageNum(); } /* complex simple frames file-4.html files.html#4 noframes file.html#4 file.html#4 */ if (noframes) { file->append(".html#"); file->append(std::to_string(destPage)); } else { if (complexMode) { file->append("-"); file->append(std::to_string(destPage)); file->append(".html"); } else { file->append("s.html#"); file->append(std::to_string(destPage)); } } if (printCommands) { printf(" link to page %d ", destPage); } return file; } else { return new GooString(); } } case actionGoToR: { LinkGoToR *ha = (LinkGoToR *)link->getAction(); LinkDest *dest = nullptr; int destPage = 1; GooString *file = new GooString(); if (ha->getFileName()) { delete file; file = new GooString(ha->getFileName()->c_str()); } if (ha->getDest() != nullptr) { dest = new LinkDest(*ha->getDest()); } if (dest && file) { if (!(dest->isPageRef())) { destPage = dest->getPageNum(); } delete dest; if (printCommands) { printf(" link to page %d ", destPage); } if (printHtml) { const char *p = file->c_str() + file->getLength() - 4; if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")) { file->del(file->getLength() - 4, 4); file->append(".html"); } file->append('#'); file->append(std::to_string(destPage)); } } if (printCommands && file) { printf("filename %s\n", file->c_str()); } return file; } case actionURI: { LinkURI *ha = (LinkURI *)link->getAction(); GooString *file = new GooString(ha->getURI()); // printf("uri : %s\n",file->c_str()); return file; } case actionLaunch: if (printHtml) { LinkLaunch *ha = (LinkLaunch *)link->getAction(); GooString *file = new GooString(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); file->append(".html"); } if (printCommands) { printf("filename %s", file->c_str()); } return file; } // fallthrough default: return new GooString(); } } void HtmlOutputDev::dumpMetaVars(FILE *file) { GooString *var; for (const HtmlMetaVar *t : glMetaVars) { var = t->toString(); fprintf(file, "%s\n", var->c_str()); delete var; } } bool HtmlOutputDev::dumpDocOutline(PDFDoc *doc) { FILE *output = nullptr; bool bClose = false; if (!ok) { return false; } Outline *outline = doc->getOutline(); if (!outline) { return false; } const std::vector *outlines = outline->getItems(); if (!outlines) { return false; } if (!complexMode || xml) { output = page; } else if (complexMode && !xml) { if (noframes) { output = page; fputs("
    \n", output); } else { GooString *str = Docname->copy(); str->append("-outline.html"); output = fopen(str->c_str(), "w"); delete str; if (output == nullptr) { return false; } bClose = true; const std::string htmlEncoding = HtmlOutputDev::mapEncodingToHtml(globalParams->getTextEncodingName()); fprintf(output, "\n" "\n" "Document Outline\n" "\n" "\n\n", htmlEncoding.c_str()); } } if (!xml) { bool done = newHtmlOutlineLevel(output, outlines); if (done && !complexMode) { fputs("
    \n", output); } if (bClose) { fputs("\n\n", output); fclose(output); } } else { newXmlOutlineLevel(output, outlines); } return true; } bool HtmlOutputDev::newHtmlOutlineLevel(FILE *output, const std::vector *outlines, int level) { bool atLeastOne = false; if (level == 1) { fputs("", output); fputs("

    Document Outline

    \n", output); } fputs("
      \n", output); for (OutlineItem *item : *outlines) { 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 frames file-4.html files.html#4 noframes file.html#4 file.html#4 */ linkName = new GooString(gbasename(Docname->c_str())); if (noframes) { linkName->append(".html#"); linkName->append(std::to_string(itemPage)); } else { if (complexMode) { linkName->append("-"); linkName->append(std::to_string(itemPage)); linkName->append(".html"); } else { linkName->append("s.html#"); linkName->append(std::to_string(itemPage)); } } } fputs("
    • ", output); if (linkName) { fprintf(output, "", linkName->c_str()); } if (titleStr) { fputs(titleStr->c_str(), output); } if (linkName) { fputs("", output); delete linkName; } atLeastOne = true; item->open(); if (item->hasKids() && item->getKids()) { fputs("\n", output); newHtmlOutlineLevel(output, item->getKids(), level + 1); } fputs("
    • \n", output); } fputs("
    \n", output); return atLeastOne; } void HtmlOutputDev::newXmlOutlineLevel(FILE *output, const std::vector *outlines) { fputs("\n", output); for (OutlineItem *item : *outlines) { 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()); } item->open(); if (item->hasKids() && item->getKids()) { newXmlOutlineLevel(output, item->getKids()); } } fputs("\n", output); } int HtmlOutputDev::getOutlinePageNum(OutlineItem *item) { const LinkAction *action = item->getAction(); const LinkGoTo *link = nullptr; std::unique_ptr linkdest; int pagenum = -1; if (!action || action->getKind() != actionGoTo) { return pagenum; } link = static_cast(action); if (!link || !link->isOk()) { return pagenum; } if (link->getDest()) { linkdest = std::make_unique(*link->getDest()); } else if (link->getNamedDest()) { linkdest = catalog->findDest(link->getNamedDest()); } if (!linkdest) { return pagenum; } if (linkdest->isPageRef()) { const Ref pageref = linkdest->getPageRef(); pagenum = catalog->findPage(pageref); } else { pagenum = linkdest->getPageNum(); } return pagenum; } poppler-24.02.0/utils/HtmlOutputDev.h000066400000000000000000000263441455701731300174540ustar00rootroot00000000000000//======================================================================== // // HtmlOutputDev.h // // Copyright 1997 Derek B. Noonburg // // Changed 1999 by G.Ovtcharov //======================================================================== //======================================================================== // // 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, 2007, 2009, 2012, 2018-2022 Albert Astals Cid // Copyright (C) 2008, 2009 Warren Toomey // Copyright (C) 2009, 2011 Carlos Garcia Campos // Copyright (C) 2009 Kovid Goyal // Copyright (C) 2010 Hib Eris // Copyright (C) 2011 Joshua Richardson // Copyright (C) 2011 Stephen Reichling // Copyright (C) 2012 Igor Slepchin // 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) 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 // //======================================================================== #ifndef HTMLOUTPUTDEV_H #define HTMLOUTPUTDEV_H #include #include "goo/gbasename.h" #include "GfxFont.h" #include "OutputDev.h" #include "HtmlLinks.h" #include "HtmlFonts.h" #include "Link.h" #include "Catalog.h" #include "UnicodeMap.h" #define xoutRound(x) ((int)(x + 0.5)) #define DOCTYPE "" class GfxState; class GooString; class HtmlImage; class PDFDoc; class OutlineItem; //------------------------------------------------------------------------ // HtmlString //------------------------------------------------------------------------ enum UnicodeTextDirection { textDirUnknown, textDirLeftRight, textDirRightLeft, textDirTopBottom }; class HtmlString { public: // Constructor. HtmlString(GfxState *state, double fontSize, HtmlFontAccu *fonts); // Destructor. ~HtmlString(); HtmlString(const HtmlString &) = delete; HtmlString &operator=(const HtmlString &) = delete; // Add a character to the string. void addChar(GfxState *state, double x, double y, double dx, double dy, Unicode u); const HtmlLink *getLink() const { return link; } const HtmlFont &getFont() const { return *fonts->Get(fontpos); } void endString(); // postprocessing private: // aender die text variable const HtmlLink *link; double xMin, xMax; // bounding box x coordinates double yMin, yMax; // bounding box y coordinates int col; // starting column Unicode *text; // the text double *xRight; // right-hand x coord of each char HtmlString *yxNext; // next string in y-major order HtmlString *xyNext; // next string in x-major order int fontpos; 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) HtmlFontAccu *fonts; friend class HtmlPage; }; //------------------------------------------------------------------------ // HtmlPage //------------------------------------------------------------------------ class HtmlPage { public: // Constructor. explicit HtmlPage(bool rawOrder); // Destructor. ~HtmlPage(); HtmlPage(const HtmlPage &) = delete; HtmlPage &operator=(const HtmlPage &) = delete; // Begin a new string. void beginString(GfxState *state, const GooString *s); // Add a character to the current string. void addChar(GfxState *state, double x, double y, double dx, double dy, double ox, double oy, const Unicode *u, int uLen); // unsigned char c); void updateFont(GfxState *state); // End the current string, sorting it into the list of strings. void endString(); // Coalesce strings that look like parts of the same line. void coalesce(); // Find a string. If is true, starts looking at top of page; // otherwise starts looking at ,. If is true, // stops looking at bottom of page; otherwise stops looking at // ,. If found, sets the text bounding rectangle and // returns true; otherwise returns false. // new functions void AddLink(const HtmlLink &x) { links->AddLink(x); } // add an image to the current page void addImage(std::unique_ptr &&fname, GfxState *state); // number of images on the current page int getNumImages() { return imgList.size(); } void dump(FILE *f, int pageNum, const std::vector &backgroundImages); // Clear the page. void clear(); void conv(); private: const HtmlFont *getFont(HtmlString *hStr) const { return fonts->Get(hStr->fontpos); } double fontSize; // current font size bool rawOrder; // keep strings in content stream order HtmlString *curStr; // currently active string HtmlString *yxStrings; // strings in y-major order HtmlString *xyStrings; // strings in x-major order HtmlString *yxCur1, *yxCur2; // cursors for yxStrings list void setDocName(const char *fname); void dumpAsXML(FILE *f, int page); void dumpComplex(FILE *f, int page, const std::vector &backgroundImages); int dumpComplexHeaders(FILE *const file, FILE *&pageFile, int page); // marks the position of the fonts that belong to current page (for noframes) int fontsPageMarker; HtmlFontAccu *fonts; HtmlLinks *links; std::vector imgList; GooString *DocName; int pageWidth; int pageHeight; int firstPage; // used to begin the numeration of pages friend class HtmlOutputDev; }; //------------------------------------------------------------------------ // HtmlMetaVar //------------------------------------------------------------------------ class HtmlMetaVar { public: HtmlMetaVar(const char *_name, const char *_content); ~HtmlMetaVar(); HtmlMetaVar(const HtmlMetaVar &) = delete; HtmlMetaVar &operator=(const HtmlMetaVar &) = delete; GooString *toString() const; private: GooString *name; GooString *content; }; //------------------------------------------------------------------------ // HtmlOutputDev //------------------------------------------------------------------------ class HtmlOutputDev : public OutputDev { public: // Open a text output file. If is nullptr, no file is written // (this is useful, e.g., for searching text). If is true, // text is converted to 7-bit ASCII; otherwise, text is converted to // 8-bit ISO Latin-1. should also be set for Japanese // (EUC-JP) text. If is true, the text is kept in content // stream order. HtmlOutputDev(Catalog *catalogA, const char *fileName, const char *title, const char *author, const char *keywords, const char *subject, const char *date, bool rawOrder, int firstPage = 1, bool outline = false); // Destructor. ~HtmlOutputDev() override; // Check if file was successfully created. virtual bool isOk() { return ok; } //---- get info about output device // Does this device use upside-down coordinates? // (Upside-down means (0,0) is the top left corner of the page.) bool upsideDown() override { return true; } // Does this device use drawChar() or drawString()? bool useDrawChar() 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; } // Does this device need non-text content? bool needNonText() override { return true; } //----- initialization and control bool checkPageSlice(Page *p, double hDPI, double vDPI, int rotate, bool useMediaBox, bool crop, int sliceX, int sliceY, int sliceW, int sliceH, bool printing, bool (*abortCheckCbk)(void *data) = nullptr, void *abortCheckCbkData = nullptr, bool (*annotDisplayDecideCbk)(Annot *annot, void *user_data) = nullptr, void *annotDisplayDecideCbkData = nullptr) override { docPage = p; return true; } // Start a page. void startPage(int pageNum, GfxState *state, XRef *xref) override; // End a page. void endPage() override; // add a background image to the list of background images, // as this seems to be done outside other processing. takes ownership of img. void addBackgroundImage(const std::string &img); //----- update text state void updateFont(GfxState *state) override; //----- text drawing void beginString(GfxState *state, const GooString *s) override; void endString(GfxState *state) override; void drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, const Unicode *u, int uLen) override; void drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg) override; void drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) override; // new feature virtual int DevType() { return 1234; } int getPageWidth() { return maxPageWidth; } int getPageHeight() { return maxPageHeight; } bool dumpDocOutline(PDFDoc *doc); private: // convert encoding into a HTML standard, or encoding->c_str if not // recognized. static std::string mapEncodingToHtml(const std::string &encoding); void doProcessLink(AnnotLink *link); GooString *getLinkDest(AnnotLink *link); void dumpMetaVars(FILE *); void doFrame(int firstPage); bool newHtmlOutlineLevel(FILE *output, const std::vector *outlines, int level = 1); void newXmlOutlineLevel(FILE *output, const std::vector *outlines); 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); FILE *fContentsFrame; FILE *page; // html file // FILE *tin; // image log file // bool write; bool needClose; // need to close the file? HtmlPage *pages; // text for the current page bool rawOrder; // keep text in content stream order bool doOutline; // output document outline bool ok; // set up ok? bool dumpJPEG; int pageNum; int maxPageWidth; int maxPageHeight; GooString *Docname; GooString *docTitle; std::vector glMetaVars; Catalog *catalog; Page *docPage; std::vector backgroundImages; friend class HtmlPage; }; #endif poppler-24.02.0/utils/HtmlUtils.h000066400000000000000000000033431455701731300166070ustar00rootroot00000000000000// // HtmlUtils.h // // Created on: Jun 8, 2011 // Author: Joshua Richardson // Copyright 2011 // // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // // Copyright (C) 2011 Joshua Richardson // // 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 HTMLUTILS_H_ #define HTMLUTILS_H_ #include // fabs // Returns true iff the difference between a and b is less than the threshold // We always use fuzzy math when comparing decimal numbers due to imprecision inline bool is_within(double a, double thresh, double b) { return fabs(a - b) < thresh; } inline bool rot_matrices_equal(const double *const mat0, const double *const mat1) { return is_within(mat0[0], .1, mat1[0]) && is_within(mat0[1], .1, mat1[1]) && is_within(mat0[2], .1, mat1[2]) && is_within(mat0[3], .1, mat1[3]); } // rotation is (cos q, sin q, -sin q, cos q, 0, 0) // sin q is zero iff there is no rotation, or 180 deg. rotation; // for 180 rotation, cos q will be negative inline bool isMatRotOrSkew(const double *const mat) { return mat[0] < 0 || !is_within(mat[1], .1, 0); } // Alters the matrix so that it does not scale a vector's x component; // If the matrix does not skew, then that will also normalize the y // component, keeping any rotation, but removing scaling. inline void normalizeRotMat(double *mat) { double scale = fabs(mat[0] + mat[1]); if (!scale) { return; } for (int i = 0; i < 4; i++) { mat[i] /= scale; } } #endif /* HTMLUTILS_H_ */ poppler-24.02.0/utils/ImageOutputDev.cc000066400000000000000000000546411455701731300177310ustar00rootroot00000000000000//======================================================================== // // ImageOutputDev.cc // // Copyright 1998-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) 2005, 2007, 2011, 2018, 2019, 2021, 2022 Albert Astals Cid // Copyright (C) 2006 Rainer Keller // Copyright (C) 2008 Timothy Lee // Copyright (C) 2008 Vasile Gaburici // Copyright (C) 2009 Carlos Garcia Campos // Copyright (C) 2009 William Bader // Copyright (C) 2010 Jakob Voss // Copyright (C) 2012, 2013, 2017, 2018 Adrian Johnson // Copyright (C) 2013 Thomas Fischer // Copyright (C) 2013 Hib Eris // 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 // // 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 "config.h" #include #include #include #include #include #include #include "goo/gmem.h" #include "goo/NetPBMWriter.h" #include "goo/PNGWriter.h" #include "goo/TiffWriter.h" #include "Error.h" #include "GfxState.h" #include "Object.h" #include "Stream.h" #include "JBIG2Stream.h" #include "ImageOutputDev.h" ImageOutputDev::ImageOutputDev(char *fileRootA, bool pageNamesA, bool listImagesA) { listImages = listImagesA; if (!listImages) { fileRoot = copyString(fileRootA); fileName = (char *)gmalloc(strlen(fileRoot) + 45); } outputPNG = false; outputTiff = false; dumpJPEG = false; dumpJP2 = false; dumpJBIG2 = false; dumpCCITT = false; pageNames = pageNamesA; imgNum = 0; pageNum = 0; 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"); } } ImageOutputDev::~ImageOutputDev() { if (!listImages) { gfree(fileName); gfree(fileRoot); } } void ImageOutputDev::setFilename(const char *fileExt) { if (pageNames) { sprintf(fileName, "%s-%03d-%03d.%s", fileRoot, pageNum, imgNum, fileExt); } else { sprintf(fileName, "%s-%03d.%s", fileRoot, imgNum, fileExt); } } // Print a floating point number between 0 - 9999 using 4 characters // eg '1.23', '12.3', ' 123', '1234' // // We need to be careful to handle the cases where rounding adds an // extra digit before the decimal. eg printf("%4.2f", 9.99999) // outputs "10.00" instead of "9.99". static void printNumber(double d) { char buf[10]; if (d < 10.0) { sprintf(buf, "%4.2f", d); buf[4] = 0; printf("%s", buf); } else if (d < 100.0) { sprintf(buf, "%4.1f", d); if (!isdigit(buf[3])) { buf[3] = 0; printf(" %s", buf); } else { printf("%s", buf); } } else { printf("%4.0f", d); } } void ImageOutputDev::listImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, bool inlineImg, ImageType imageType) { const char *type; const char *colorspace; const char *enc; int components, bpc; printf("%4d %5d ", pageNum, imgNum); type = ""; switch (imageType) { case imgImage: type = "image"; break; case imgStencil: type = "stencil"; break; case imgMask: type = "mask"; break; case imgSmask: type = "smask"; break; } printf("%-7s %5d %5d ", type, width, height); colorspace = "-"; /* masks and stencils default to ncomps = 1 and bpc = 1 */ components = 1; bpc = 1; if (colorMap && colorMap->isOk()) { switch (colorMap->getColorSpace()->getMode()) { case csDeviceGray: case csCalGray: colorspace = "gray"; break; case csDeviceRGB: case csCalRGB: colorspace = "rgb"; break; case csDeviceCMYK: colorspace = "cmyk"; break; case csLab: colorspace = "lab"; break; case csICCBased: colorspace = "icc"; break; case csIndexed: colorspace = "index"; break; case csSeparation: colorspace = "sep"; break; case csDeviceN: colorspace = "devn"; break; case csPattern: default: colorspace = "-"; break; } components = colorMap->getNumPixelComps(); bpc = colorMap->getBits(); } printf("%-5s %2d %2d ", colorspace, components, bpc); switch (str->getKind()) { case strCCITTFax: enc = "ccitt"; break; case strDCT: enc = "jpeg"; break; case strJPX: enc = "jpx"; break; case strJBIG2: enc = "jbig2"; break; case strFile: case strFlate: case strCachedFile: case strASCIIHex: case strASCII85: case strLZW: case strRunLength: case strWeird: default: enc = "image"; break; } printf("%-5s ", enc); printf("%-3s ", interpolate ? "yes" : "no"); if (inlineImg) { printf("[inline] "); } else if (ref->isRef()) { const Ref imageRef = ref->getRef(); if (imageRef.gen >= 100000) { printf("[none] "); } else { printf(" %6d %2d ", imageRef.num, imageRef.gen); } } else { printf("[none] "); } const double *mat = state->getCTM(); double width2 = sqrt(mat[0] * mat[0] + mat[1] * mat[1]); double height2 = sqrt(mat[2] * mat[2] + mat[3] * mat[3]); double xppi = fabs(width * 72.0 / width2); double yppi = fabs(height * 72.0 / height2); if (xppi < 1.0) { printf("%5.3f ", xppi); } else { printf("%5.0f ", xppi); } if (yppi < 1.0) { printf("%5.3f ", yppi); } else { printf("%5.0f ", yppi); } Goffset embedSize = -1; if (inlineImg) { embedSize = getInlineImageLength(str, width, height, colorMap); } else { embedSize = str->getBaseStream()->getLength(); } long long imageSize = 0; if (colorMap && colorMap->isOk()) { imageSize = ((long long)width * height * colorMap->getNumPixelComps() * colorMap->getBits()) / 8; } else { imageSize = (long long)width * height / 8; // mask } double ratio = -1.0; if (imageSize > 0) { ratio = 100.0 * embedSize / imageSize; } if (embedSize < 0) { printf(" - "); } else if (embedSize <= 9999) { printf("%4lldB", embedSize); } else { double d = embedSize / 1024.0; if (d <= 9999.0) { printNumber(d); putchar('K'); } else { d /= 1024.0; if (d <= 9999.0) { printNumber(d); putchar('M'); } else { d /= 1024.0; printNumber(d); putchar('G'); } } } if (ratio > 9.9) { printf(" %3.0f%%\n", ratio); } else if (ratio >= 0.0) { printf(" %3.1f%%\n", ratio); } else { printf(" - \n"); } ++imgNum; } long ImageOutputDev::getInlineImageLength(Stream *str, int width, int height, GfxImageColorMap *colorMap) { long len; if (colorMap) { ImageStream *imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgStr->reset(); for (int y = 0; y < height; y++) { imgStr->getLine(); } imgStr->close(); delete imgStr; } else { str->reset(); for (int y = 0; y < height; y++) { int size = (width + 7) / 8; for (int x = 0; x < size; x++) { str->getChar(); } } } EmbedStream *embedStr = (EmbedStream *)(str->getBaseStream()); embedStr->rewind(); len = 0; while (embedStr->getChar() != EOF) { len++; } embedStr->restore(); return len; } void ImageOutputDev::writeRawImage(Stream *str, const char *ext) { FILE *f; int c; // open the image file setFilename(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(); // copy the stream while ((c = str->getChar()) != EOF) { fputc(c, f); } str->close(); fclose(f); } void ImageOutputDev::writeImageFile(ImgWriter *writer, ImageFormat format, const char *ext, Stream *str, int width, int height, GfxImageColorMap *colorMap) { FILE *f = nullptr; /* squelch bogus compiler warning */ ImageStream *imgStr = nullptr; unsigned char *row; unsigned char *rowp; unsigned char *p; GfxRGB rgb; GfxCMYK cmyk; GfxGray gray; unsigned char zero[gfxColorMaxComps]; int invert_bits; if (writer) { setFilename(ext); ++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; } } int pixelSize = sizeof(unsigned int); if (format == imgRGB48) { 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); errorCode = 99; return; } if (format != imgMonochrome) { // initialize stream imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgStr->reset(); } else { // initialize stream str->reset(); } // PDF masks use 0 = draw current color, 1 = leave unchanged. // We invert this to provide the standard interpretation of alpha // (0 = transparent, 1 = opaque). If the colorMap already inverts // the mask we leave the data unchanged. invert_bits = 0xff; if (colorMap) { memset(zero, 0, sizeof(zero)); colorMap->getGray(zero, &gray); if (colToByte(gray) == 0) { invert_bits = 0x00; } } // for each line... for (int y = 0; y < height; y++) { switch (format) { case imgRGB: p = imgStr->getLine(); rowp = row; for (int x = 0; x < width; ++x) { if (p) { colorMap->getRGB(p, &rgb); *rowp++ = colToByte(rgb.r); *rowp++ = colToByte(rgb.g); *rowp++ = colToByte(rgb.b); p += colorMap->getNumPixelComps(); } else { *rowp++ = 0; *rowp++ = 0; *rowp++ = 0; } } if (writer) { writer->writeRow(&row); } break; case imgRGB48: { p = imgStr->getLine(); unsigned short *rowp16 = reinterpret_cast(row); for (int x = 0; x < width; ++x) { if (p) { colorMap->getRGB(p, &rgb); *rowp16++ = colToShort(rgb.r); *rowp16++ = colToShort(rgb.g); *rowp16++ = colToShort(rgb.b); p += colorMap->getNumPixelComps(); } else { *rowp16++ = 0; *rowp16++ = 0; *rowp16++ = 0; } } if (writer) { writer->writeRow(&row); } break; } case imgCMYK: p = imgStr->getLine(); rowp = row; for (int x = 0; x < width; ++x) { if (p) { colorMap->getCMYK(p, &cmyk); *rowp++ = colToByte(cmyk.c); *rowp++ = colToByte(cmyk.m); *rowp++ = colToByte(cmyk.y); *rowp++ = colToByte(cmyk.k); p += colorMap->getNumPixelComps(); } else { *rowp++ = 0; *rowp++ = 0; *rowp++ = 0; *rowp++ = 0; } } if (writer) { writer->writeRow(&row); } break; case imgGray: p = imgStr->getLine(); rowp = row; for (int x = 0; x < width; ++x) { if (p) { colorMap->getGray(p, &gray); *rowp++ = colToByte(gray); p += colorMap->getNumPixelComps(); } else { *rowp++ = 0; } } if (writer) { writer->writeRow(&row); } break; case imgMonochrome: int size = (width + 7) / 8; for (int x = 0; x < size; x++) { row[x] = str->getChar() ^ invert_bits; } if (writer) { writer->writeRow(&row); } break; } } gfree(row); if (format != imgMonochrome) { imgStr->close(); delete imgStr; } str->close(); if (writer) { writer->close(); fclose(f); } } void ImageOutputDev::writeImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool inlineImg) { ImageFormat format; EmbedStream *embedStr; if (inlineImg) { embedStr = (EmbedStream *)(str->getBaseStream()); // Record the stream. This determines the size. getInlineImageLength(str, width, height, colorMap); // Reading the stream again will return EOF at end of recording. embedStr->rewind(); } if (dumpJPEG && str->getKind() == strDCT) { // dump JPEG file writeRawImage(str, "jpg"); } else if (dumpJP2 && str->getKind() == strJPX && !inlineImg) { // dump JPEG2000 file writeRawImage(str, "jp2"); } else if (dumpJBIG2 && str->getKind() == strJBIG2 && !inlineImg) { // dump JBIG2 globals stream if available JBIG2Stream *jb2Str = static_cast(str); Object *globals = jb2Str->getGlobalsStream(); if (globals->isStream()) { FILE *f; int c; Stream *globalsStr = globals->getStream(); 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); } globalsStr->close(); fclose(f); } // dump JBIG2 embedded file writeRawImage(str, "jb2e"); } else if (dumpCCITT && str->getKind() == strCCITTFax) { // write CCITT parameters CCITTFaxStream *ccittStr = static_cast(str); FILE *f; 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) { fprintf(f, "-4 "); } else if (ccittStr->getEncoding() == 0) { fprintf(f, "-1 "); } else { fprintf(f, "-2 "); } if (ccittStr->getEndOfLine()) { fprintf(f, "-A "); } else { fprintf(f, "-P "); } fprintf(f, "-X %d ", ccittStr->getColumns()); if (ccittStr->getBlackIs1()) { fprintf(f, "-W "); } else { fprintf(f, "-B "); } fprintf(f, "-M\n"); // PDF uses MSB first fclose(f); // dump CCITT file writeRawImage(str, "ccitt"); } else if (outputPNG && !(outputTiff && colorMap && (colorMap->getColorSpace()->getMode() == csDeviceCMYK || (colorMap->getColorSpace()->getMode() == csICCBased && colorMap->getNumPixelComps() == 4)))) { // output in PNG format #ifdef ENABLE_LIBPNG ImgWriter *writer; if (!colorMap || (colorMap->getNumPixelComps() == 1 && colorMap->getBits() == 1)) { writer = new PNGWriter(PNGWriter::MONOCHROME); format = imgMonochrome; } else if (colorMap->getColorSpace()->getMode() == csDeviceGray || colorMap->getColorSpace()->getMode() == csCalGray) { writer = new PNGWriter(PNGWriter::GRAY); format = imgGray; } else if ((colorMap->getColorSpace()->getMode() == csDeviceRGB || colorMap->getColorSpace()->getMode() == csCalRGB || (colorMap->getColorSpace()->getMode() == csICCBased && colorMap->getNumPixelComps() == 3)) && colorMap->getBits() > 8) { writer = new PNGWriter(PNGWriter::RGB48); format = imgRGB48; } else { writer = new PNGWriter(PNGWriter::RGB); format = imgRGB; } writeImageFile(writer, format, "png", str, width, height, colorMap); delete writer; #endif } else if (outputTiff) { // output in TIFF format #ifdef ENABLE_LIBTIFF ImgWriter *writer; if (!colorMap || (colorMap->getNumPixelComps() == 1 && colorMap->getBits() == 1)) { writer = new TiffWriter(TiffWriter::MONOCHROME); format = imgMonochrome; } else if (colorMap->getColorSpace()->getMode() == csDeviceGray || colorMap->getColorSpace()->getMode() == csCalGray) { writer = new TiffWriter(TiffWriter::GRAY); format = imgGray; } else if (colorMap->getColorSpace()->getMode() == csDeviceCMYK || (colorMap->getColorSpace()->getMode() == csICCBased && colorMap->getNumPixelComps() == 4)) { writer = new TiffWriter(TiffWriter::CMYK); format = imgCMYK; } else if ((colorMap->getColorSpace()->getMode() == csDeviceRGB || colorMap->getColorSpace()->getMode() == csCalRGB || (colorMap->getColorSpace()->getMode() == csICCBased && colorMap->getNumPixelComps() == 3)) && colorMap->getBits() > 8) { writer = new TiffWriter(TiffWriter::RGB48); format = imgRGB48; } else { writer = new TiffWriter(TiffWriter::RGB); format = imgRGB; } writeImageFile(writer, format, "tif", str, width, height, colorMap); delete writer; #endif } else { // output in PPM/PBM format ImgWriter *writer; if (!colorMap || (colorMap->getNumPixelComps() == 1 && colorMap->getBits() == 1)) { writer = new NetPBMWriter(NetPBMWriter::MONOCHROME); format = imgMonochrome; } else { writer = new NetPBMWriter(NetPBMWriter::RGB); format = imgRGB; } writeImageFile(writer, format, format == imgRGB ? "ppm" : "pbm", str, width, height, colorMap); delete writer; } if (inlineImg) { embedStr->restore(); } } 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) { return true; // do nothing -- this avoids the potentially slow loop in Gfx.cc } void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg) { if (listImages) { listImage(state, ref, str, width, height, nullptr, interpolate, inlineImg, imgStencil); } else { writeImage(state, ref, str, width, height, nullptr, inlineImg); } } void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) { if (listImages) { listImage(state, ref, str, width, height, colorMap, interpolate, inlineImg, imgImage); } else { writeImage(state, ref, str, width, height, colorMap, inlineImg); } } void ImageOutputDev::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) { if (listImages) { listImage(state, ref, str, width, height, colorMap, interpolate, false, imgImage); listImage(state, ref, maskStr, maskWidth, maskHeight, nullptr, maskInterpolate, false, imgMask); } else { writeImage(state, ref, str, width, height, colorMap, false); writeImage(state, ref, maskStr, maskWidth, maskHeight, nullptr, false); } } void ImageOutputDev::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) { if (listImages) { listImage(state, ref, str, width, height, colorMap, interpolate, false, imgImage); listImage(state, ref, maskStr, maskWidth, maskHeight, maskColorMap, maskInterpolate, false, imgSmask); } else { writeImage(state, ref, str, width, height, colorMap, false); writeImage(state, ref, maskStr, maskWidth, maskHeight, maskColorMap, false); } } poppler-24.02.0/utils/ImageOutputDev.h000066400000000000000000000147751455701731300175770ustar00rootroot00000000000000//======================================================================== // // ImageOutputDev.h // // Copyright 1998-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 Rainer Keller // Copyright (C) 2008 Timothy Lee // Copyright (C) 2009 Carlos Garcia Campos // Copyright (C) 2010 Jakob Voss // Copyright (C) 2012, 2013, 2017 Adrian Johnson // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2018, 2019, 2021, 2024 Albert Astals Cid // Copyright (C) 2024 Fernando Herrera // // 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 IMAGEOUTPUTDEV_H #define IMAGEOUTPUTDEV_H #include "poppler/poppler-config.h" #include #include "goo/ImgWriter.h" #include "OutputDev.h" class GfxState; //------------------------------------------------------------------------ // ImageOutputDev //------------------------------------------------------------------------ class ImageOutputDev : public OutputDev { public: enum ImageType { imgImage, imgStencil, imgMask, imgSmask }; enum ImageFormat { imgRGB, imgRGB48, imgGray, imgMonochrome, imgCMYK }; // Create an OutputDev which will write images to files named // -NNN. or -PPP-NNN., if // is set. Normally, all images are written as PBM // (.pbm) or PPM (.ppm) files unless PNG or Tiff output is enabled // (PNG is used if both are enabled). If Jpeg is enabled, JPEG images // are written as JPEG (.jpg) files. ImageOutputDev(char *fileRootA, bool pageNamesA, bool listImagesA); // Destructor. ~ImageOutputDev() override; // Use PNG format for output void enablePNG(bool png) { outputPNG = png; } // Use TIFF format for output void enableTiff(bool tiff) { outputTiff = tiff; } // Use Jpeg format for Jpeg files void enableJpeg(bool jpeg) { dumpJPEG = jpeg; } // Use Jpeg2000 format for Jpeg2000 files void enableJpeg2000(bool jp2) { dumpJP2 = jp2; } // Use JBIG2 format for JBIG2 files void enableJBig2(bool jbig2) { dumpJBIG2 = jbig2; } // Use CCITT format for CCITT files void enableCCITT(bool ccitt) { dumpCCITT = ccitt; } // 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 errorCode == 0; } // Does this device use tilingPatternFill()? If this returns false, // tiling pattern fills will be reduced to a series of other drawing // operations. bool useTilingPatternFill() 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; } // Does this device need non-text content? bool needNonText() override { return true; } // Start a page void startPage(int pageNumA, GfxState *state, XRef *xref) override { pageNum = pageNumA; } //---- get info about output device // Does this device use upside-down coordinates? // (Upside-down means (0,0) is the top left corner of the page.) bool upsideDown() override { return true; } // Does this device use drawChar() or drawString()? bool useDrawChar() override { return false; } //----- path painting 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; //----- image drawing void drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg) override; void drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) override; 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; void 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) override; private: // Sets the output filename with a given file extension void setFilename(const char *fileExt); void listImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, bool inlineImg, ImageType imageType); void writeImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool inlineImg); void writeRawImage(Stream *str, const char *ext); void writeImageFile(ImgWriter *writer, ImageFormat format, const char *ext, Stream *str, int width, int height, GfxImageColorMap *colorMap); long getInlineImageLength(Stream *str, int width, int height, GfxImageColorMap *colorMap); char *fileRoot; // root of output file names char *fileName; // buffer for output file names bool listImages; // list images instead of dumping bool dumpJPEG; // set to dump native JPEG files bool dumpJP2; // set to dump native JPEG2000 files bool dumpJBIG2; // set to dump native JBIG2 files bool dumpCCITT; // set to dump native CCITT files 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 int pageNum; // current page number int imgNum; // current image number int errorCode; // code for any error creating the output files }; #endif poppler-24.02.0/utils/InMemoryFile.cc000066400000000000000000000052161455701731300173600ustar00rootroot00000000000000//======================================================================== // // InMemoryFile.cc // // Represents a file in-memory with GNU's stdio wrappers. // NOTE as of this writing, open() depends on the glibc 'fopencookie' // extension and is not supported on other platforms. The // HAVE_IN_MEMORY_FILE macro is intended to reflect whether this class is // usable. // // This file is licensed under the GPLv2 or later // // Copyright (C) 2018, 2019 Greg Knight // Copyright (C) 2020 Albert Astals Cid // //======================================================================== #include "InMemoryFile.h" #include #include InMemoryFile::InMemoryFile() = default; #ifdef HAVE_IN_MEMORY_FILE_FOPENCOOKIE ssize_t InMemoryFile::_read(char *buf, size_t sz) { auto toRead = std::min(data.size() - iohead, sz); memcpy(&buf[0], &data[iohead], toRead); iohead += toRead; return toRead; } ssize_t InMemoryFile::_write(const char *buf, size_t sz) { if (iohead + sz > data.size()) { data.resize(iohead + sz); } memcpy(&data[iohead], buf, sz); iohead += sz; return sz; } int InMemoryFile::_seek(off64_t *offset, int whence) { switch (whence) { case SEEK_SET: iohead = (*offset); break; case SEEK_CUR: iohead += (*offset); break; case SEEK_END: iohead -= (*offset); break; } (*offset) = std::min(std::max(iohead, 0l), data.size()); iohead = static_cast(*offset); return 0; } #endif // def HAVE_IN_MEMORY_FILE_FOPENCOOKIE FILE *InMemoryFile::open(const char *mode) { #ifdef HAVE_IN_MEMORY_FILE_FOPENCOOKIE if (fptr != nullptr) { fprintf(stderr, "InMemoryFile: BUG: Why is this opened more than once?"); return nullptr; // maybe there's some legit reason for it, whoever comes up with one can remove this line } static const cookie_io_functions_t methods = { /* .read = */ [](void *self, char *buf, size_t sz) { return ((InMemoryFile *)self)->_read(buf, sz); }, /* .write = */ [](void *self, const char *buf, size_t sz) { return ((InMemoryFile *)self)->_write(buf, sz); }, /* .seek = */ [](void *self, off64_t *offset, int whence) { return ((InMemoryFile *)self)->_seek(offset, whence); }, /* .close = */ [](void *self) { ((InMemoryFile *)self)->fptr = nullptr; return 0; }, }; return fptr = fopencookie(this, mode, methods); #else fprintf(stderr, "If you can read this, your platform does not support the features necessary to achieve your goals."); return nullptr; #endif } poppler-24.02.0/utils/InMemoryFile.h000066400000000000000000000027721455701731300172260ustar00rootroot00000000000000//======================================================================== // // InMemoryFile.h // // Represents a file in-memory with GNU's stdio wrappers. // NOTE as of this writing, open() depends on the glibc 'fopencookie' // extension and is not supported on other platforms. The // HAVE_IN_MEMORY_FILE macro is intended to reflect whether this class is // usable. // // This file is licensed under the GPLv2 or later // // Copyright (C) 2018, 2019 Greg Knight // Copyright (C) 2022 Albert Astals Cid // //======================================================================== #ifndef IN_MEMORY_FILE_H #define IN_MEMORY_FILE_H #include #include #include #if defined(__USE_GNU) && !defined(__ANDROID_API__) # define HAVE_IN_MEMORY_FILE (1) # define HAVE_IN_MEMORY_FILE_FOPENCOOKIE (1) // used internally #endif class InMemoryFile { private: #ifdef HAVE_IN_MEMORY_FILE_FOPENCOOKIE size_t iohead = 0; FILE *fptr = nullptr; #endif std::vector data; #ifdef HAVE_IN_MEMORY_FILE_FOPENCOOKIE ssize_t _read(char *buf, size_t sz); ssize_t _write(const char *buf, size_t sz); int _seek(off64_t *offset, int whence); #endif 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; } }; #endif // IN_MEMORY_FILE_H poppler-24.02.0/utils/Win32Console.cc000066400000000000000000000107331455701731300172460ustar00rootroot00000000000000//======================================================================== // // Win32Console.cc // // This file is licensed under the GPLv2 or later // // Copyright (C) 2017 Adrian Johnson // // 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 // //======================================================================== #ifdef _WIN32 # include "goo/gmem.h" # include "UTF.h" # define WIN32_CONSOLE_IMPL # include "Win32Console.h" # include # include 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; // If all = true, flush all characters to console. // If all = false, flush up to and including last newline. // Also flush all if buffer > half full to ensure space for future // writes. static void flush(bool all = false) { int nchars = 0; if (all || bufLen > BUF_SIZE / 2) { nchars = bufLen; } else if (bufLen > 0) { // find num chars up to and including last '\n' for (nchars = bufLen; nchars > 0; --nchars) { if (buf[nchars - 1] == '\n') break; } } if (nchars > 0) { DWORD wlen = utf8ToUtf16(buf, (uint16_t *)wbuf, BUF_SIZE, nchars); WriteConsoleW(consoleHandle, wbuf, wlen, &wlen, nullptr); if (nchars < bufLen) { memmove(buf, buf + nchars, bufLen - nchars); bufLen -= nchars; } else { bufLen = 0; } } } static inline bool streamIsConsole(FILE *stream) { return ((stream == stdout && stdoutIsConsole) || (stream == stderr && stderrIsConsole)); } int win32_fprintf(FILE *stream, ...) { va_list args; int ret = 0; va_start(args, stream); const char *format = va_arg(args, const char *); if (streamIsConsole(stream)) { ret = vsnprintf(buf + bufLen, BUF_SIZE - bufLen, format, args); bufLen += ret; if (ret >= BUF_SIZE - bufLen) { // output was truncated buf[BUF_SIZE - 1] = 0; bufLen = BUF_SIZE - 1; } flush(); } else { vfprintf(stream, format, args); } va_end(args); return ret; } size_t win32_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) { size_t ret = 0; if (streamIsConsole(stream)) { int n = size * nmemb; if (n > BUF_SIZE - bufLen - 1) n = BUF_SIZE - bufLen - 1; memcpy(buf + bufLen, ptr, n); bufLen += n; buf[bufLen] = 0; flush(); } else { ret = fwrite(ptr, size, nmemb, stream); } return ret; } Win32Console::Win32Console(int *argc, char **argv[]) { LPWSTR *wargv; fpos_t pos; argList = nullptr; privateArgList = nullptr; wargv = CommandLineToArgvW(GetCommandLineW(), &numArgs); if (wargv) { argList = new char *[numArgs]; privateArgList = new char *[numArgs]; for (int i = 0; i < numArgs; i++) { argList[i] = utf16ToUtf8((uint16_t *)(wargv[i])); // parseArgs will rearrange the argv list so we keep our own copy // to use for freeing all the strings privateArgList[i] = argList[i]; } LocalFree(wargv); *argc = numArgs; *argv = argList; } 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 // fgetpos() succeeds on all CHAR devices except console and CON. stdoutIsConsole = (GetFileType(GetStdHandle(STD_OUTPUT_HANDLE)) == FILE_TYPE_CHAR) && (fgetpos(stdout, &pos) != 0); stderrIsConsole = (GetFileType(GetStdHandle(STD_ERROR_HANDLE)) == FILE_TYPE_CHAR) && (fgetpos(stderr, &pos) != 0); // Need a handle to the console. Doesn't matter if we use stdout or stderr as // long as the handle output is to the console. if (stdoutIsConsole) consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); else if (stderrIsConsole) consoleHandle = GetStdHandle(STD_ERROR_HANDLE); } Win32Console::~Win32Console() { flush(true); if (argList) { for (int i = 0; i < numArgs; i++) gfree(privateArgList[i]); delete[] argList; delete[] privateArgList; } } #endif // _WIN32 poppler-24.02.0/utils/Win32Console.h000066400000000000000000000041011455701731300171000ustar00rootroot00000000000000//======================================================================== // // Win32Console.h // // This file is licensed under the GPLv2 or later // // Copyright (C) 2017 Adrian Johnson // Copyright (C) 2019 Albert Astals Cid // Copyright (C) 2019 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 // //======================================================================== #ifndef WIN32CONSOLE_H #define WIN32CONSOLE_H // UTF-8 Support for win32 console // // Converts argc/argv to UTF-8. Supports UTF-8 stdout/stderr to win32 console. // On other platforms this class is a no-op. #ifdef _WIN32 // Ensure stdio.h is included before redefining stdio functions. We need to provide // our own declarations for the redefined functions because win32 stdio.h functions // have DLL export decorations. # include # ifndef WIN32_CONSOLE_IMPL // don't redefine in Win32Console.cc so we can call original functions # define printf(...) win32_fprintf(stdout, __VA_ARGS__) # define fprintf(stream, ...) win32_fprintf(stream, __VA_ARGS__) # define puts(s) win32_fprintf(stdout, "%s\n", s) # define fputs(s, stream) win32_fprintf(stream, "%s", s) # define putc(c) win32_fprintf(stdout, "%c", c) # define putchar(c) win32_fprintf(stdout, "%c", c) # define fputc(c, stream) win32_fprintf(stream, "%c", c) # define fwrite(ptr, size, nmemb, stream) win32_fwrite(ptr, size, nmemb, stream) # endif extern "C" { int win32_fprintf(FILE *stream, ...); size_t win32_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); } class Win32Console { public: Win32Console(int *argc, char **argv[]); ~Win32Console(); private: int numArgs; char **argList; char **privateArgList; }; #else // On other platforms this class is a no-op. class Win32Console { public: Win32Console(int *argc, char ***argv) { } }; #endif // _WIN32 #endif poppler-24.02.0/utils/numberofcharacters.h000066400000000000000000000010121455701731300205260ustar00rootroot00000000000000//======================================================================== // // pdfsig.cc // // This file is licensed under the GPLv2 or later // // Copyright (C) 2010 Albert Astals Cid // //======================================================================== #ifndef NUMBEROFCHARACTERS_H #define NUMBEROFCHARACTERS_H static int numberOfCharacters(unsigned int n) { int charNum = 0; while (n >= 10) { n = n / 10; charNum++; } charNum++; return charNum; } #endif poppler-24.02.0/utils/parseargs.cc000066400000000000000000000122141455701731300170040ustar00rootroot00000000000000/* * parseargs.cc * * Command line argument parser. * * Copyright 1996-2003 Glyph & Cog, LLC */ /*======================================================================== Modified under the Poppler project - http://poppler.freedesktop.org Poppler project changes to this file are under the GPLv2 or later license All changes made under the Poppler project to this file are licensed under GPL version 2 or later Copyright (C) 2008, 2009, 2018 Albert Astals Cid Copyright (C) 2011, 2012 Adrian Johnson 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 #include #include #include #include #include "parseargs.h" #include "goo/gstrtod.h" #include "goo/GooString.h" static const ArgDesc *findArg(const ArgDesc *args, char *arg); static bool grabArg(const ArgDesc *arg, int i, int *argc, char *argv[]); bool parseArgs(const ArgDesc *args, int *argc, char *argv[]) { const ArgDesc *arg; int i, j; bool ok; ok = true; i = 1; while (i < *argc) { if (!strcmp(argv[i], "--")) { --*argc; for (j = i; j < *argc; ++j) { argv[j] = argv[j + 1]; } break; } else if ((arg = findArg(args, argv[i]))) { if (!grabArg(arg, i, argc, argv)) { ok = false; } } else { ++i; } } return ok; } void printUsage(const char *program, const char *otherArgs, const ArgDesc *args) { const ArgDesc *arg; const char *typ; int w, w1; w = 0; for (arg = args; arg->arg; ++arg) { if ((w1 = strlen(arg->arg)) > w) { w = w1; } } fprintf(stderr, "Usage: %s [options]", program); if (otherArgs) { fprintf(stderr, " %s", otherArgs); } fprintf(stderr, "\n"); for (arg = args; arg->arg; ++arg) { fprintf(stderr, " %s", arg->arg); w1 = 9 + w - strlen(arg->arg); switch (arg->kind) { case argInt: case argIntDummy: typ = " "; break; case argFP: case argFPDummy: typ = " "; break; case argString: case argStringDummy: case argGooString: typ = " "; break; case argFlag: case argFlagDummy: default: typ = ""; break; } fprintf(stderr, "%-*s", w1, typ); if (arg->usage) { fprintf(stderr, ": %s", arg->usage); } fprintf(stderr, "\n"); } } static const ArgDesc *findArg(const ArgDesc *args, char *arg) { const ArgDesc *p; for (p = args; p->arg; ++p) { if (p->kind < argFlagDummy && !strcmp(p->arg, arg)) { return p; } } return nullptr; } static bool grabArg(const ArgDesc *arg, int i, int *argc, char *argv[]) { int n; int j; bool ok; ok = true; n = 0; switch (arg->kind) { case argFlag: *(bool *)arg->val = true; n = 1; break; case argInt: if (i + 1 < *argc && isInt(argv[i + 1])) { *(int *)arg->val = atoi(argv[i + 1]); n = 2; } else { ok = false; n = 1; } break; case argFP: if (i + 1 < *argc && isFP(argv[i + 1])) { *(double *)arg->val = gatof(argv[i + 1]); n = 2; } else { ok = false; n = 1; } break; case argString: if (i + 1 < *argc) { strncpy((char *)arg->val, argv[i + 1], arg->size - 1); ((char *)arg->val)[arg->size - 1] = '\0'; n = 2; } else { ok = false; n = 1; } break; case argGooString: if (i + 1 < *argc) { ((GooString *)arg->val)->Set(argv[i + 1]); n = 2; } else { ok = false; n = 1; } break; default: fprintf(stderr, "Internal error in arg table\n"); n = 1; break; } if (n > 0) { *argc -= n; for (j = i; j < *argc; ++j) { argv[j] = argv[j + n]; } } return ok; } bool isInt(const char *s) { if (*s == '-' || *s == '+') { ++s; } while (isdigit(*s)) { ++s; } if (*s) { return false; } return true; } bool isFP(const char *s) { int n; if (*s == '-' || *s == '+') { ++s; } n = 0; while (isdigit(*s)) { ++s; ++n; } if (*s == '.') { ++s; } while (isdigit(*s)) { ++s; ++n; } if (n > 0 && (*s == 'e' || *s == 'E')) { ++s; if (*s == '-' || *s == '+') { ++s; } n = 0; if (!isdigit(*s)) { return false; } do { ++s; } while (isdigit(*s)); } if (*s) { return false; } return true; } poppler-24.02.0/utils/parseargs.h000066400000000000000000000043101455701731300166440ustar00rootroot00000000000000/* * parseargs.h * * Command line argument parser. * * 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) 2008, 2018 Albert Astals Cid Copyright (C) 2011 Adrian Johnson 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 PARSEARGS_H #define PARSEARGS_H #ifdef __cplusplus extern "C" { #endif /* * Argument kinds. */ typedef enum { argFlag, /* flag (present / not-present) */ /* [val: bool *] */ argInt, /* integer arg */ /* [val: int *] */ argFP, /* floating point arg */ /* [val: double *] */ argString, /* string arg */ /* [val: char *] */ argGooString, /* string arg */ /* [val: GooString *] */ /* dummy entries -- these show up in the usage listing only; */ /* useful for X args, for example */ argFlagDummy, argIntDummy, argFPDummy, argStringDummy } ArgKind; /* * Argument descriptor. */ typedef struct { const char *arg; /* the command line switch */ ArgKind kind; /* kind of arg */ void *val; /* place to store value */ int size; /* for argString: size of string */ const char *usage; /* usage string */ } ArgDesc; /* * Parse command line. Removes all args which are found in the arg * descriptor list . Stops parsing if "--" is found (and removes * it). Returns false if there was an error. */ extern bool parseArgs(const ArgDesc *args, int *argc, char *argv[]); /* * Print usage message, based on arg descriptor list. */ extern void printUsage(const char *program, const char *otherArgs, const ArgDesc *args); /* * Check if a string is a valid integer or floating point number. */ extern bool isInt(const char *s); extern bool isFP(const char *s); #ifdef __cplusplus } #endif #endif poppler-24.02.0/utils/pdf2xml.dtd000066400000000000000000000017761455701731300165720ustar00rootroot00000000000000 poppler-24.02.0/utils/pdfattach.1000066400000000000000000000021501455701731300165240ustar00rootroot00000000000000.\" Copyright 2019 Albert Astals Cid .TH pdfattach 1 "10 Febuary 2019" .SH NAME pdfattach \- Portable Document Format (PDF) document embedded file creator (version 3.03) .SH SYNOPSIS .B pdfattach [options] .I input-PDF-file file-to-attach output-PDF-file .SH DESCRIPTION .B Pdfattach adds a new embedded file (attachment) to an existing Portable Document Format (PDF) file. .SH OPTIONS .TP .B \-replace Replace embedded file with same name (if it exists) .TP .B \-v Print copyright and version information. .TP .B \-h Print usage information. .RB ( \-help and .B \-\-help are equivalent.) .SH EXIT CODES .TP 0 No error. .TP 1 Error opening input PDF file. .TP 2 Error opening file to attach. .TP 3 Output file already exists. .TP 3 There is already an attached file with that name. .TP 5 Error saving the output file. .SH AUTHOR The pdfattach software and documentation are copyright 2019 The Poppler developers .SH "SEE ALSO" .BR pdfdetach (1), .BR pdfimages (1), .BR pdfinfo (1), .BR pdftocairo (1), .BR pdftohtml (1), .BR pdftoppm (1), .BR pdftops (1), .BR pdftotext (1) .BR pdfseparate (1), .BR pdfsig (1), .BR pdfunite (1) poppler-24.02.0/utils/pdfattach.cc000066400000000000000000000067641455701731300167700ustar00rootroot00000000000000//======================================================================== // // pdfattach.cc // // This file is licensed under the GPLv2 or later // // Copyright (C) 2019-2022 Albert Astals Cid // Copyright (C) 2019, 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 // //======================================================================== #include "config.h" #include #include "gbasename.h" #include "parseargs.h" #include "GlobalParams.h" #include "PDFDoc.h" #include "PDFDocFactory.h" #include "Error.h" #include "ErrorCodes.h" #include "UTF.h" #include "Win32Console.h" static bool doReplace = false; static bool printVersion = false; static bool printHelp = false; static const ArgDesc argDesc[] = { { "-replace", argFlag, &doReplace, 0, "replace embedded file with same name (if it exists)" }, { "-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" }, {} }; static bool fileExists(const char *filePath) { FILE *f = openFile(filePath, "r"); if (f != nullptr) { fclose(f); return true; } return false; } int main(int argc, char *argv[]) { Win32Console win32Console(&argc, &argv); // parse args const bool ok = parseArgs(argDesc, &argc, argv); if (!ok || argc != 4 || printVersion || printHelp) { fprintf(stderr, "pdfattach version %s\n", PACKAGE_VERSION); fprintf(stderr, "%s\n", popplerCopyright); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("pdfattach", " ", argDesc); } return 99; } const GooString pdfFileName(argv[1]); const std::string attachFilePath(argv[2]); // init GlobalParams globalParams = std::make_unique(); // open PDF file std::unique_ptr doc(PDFDocFactory().createPDFDoc(pdfFileName, {}, {})); if (!doc->isOk()) { fprintf(stderr, "Couldn't open %s\n", pdfFileName.c_str()); return 1; } std::unique_ptr attachFile(GooFile::open(attachFilePath)); if (!attachFile) { fprintf(stderr, "Couldn't open %s\n", attachFilePath.c_str()); return 2; } if (fileExists(argv[3])) { fprintf(stderr, "File %s already exists.\n", argv[3]); return 3; } const std::string attachFileName = utf8ToUtf16WithBom(gbasename(attachFilePath.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); const GooString outputPdfFilePath(argv[3]); const int saveResult = doc->saveAs(outputPdfFilePath); if (saveResult != errNone) { fprintf(stderr, "Couldn't save the file properly.\n"); return 5; } return 0; } poppler-24.02.0/utils/pdfdetach.1000066400000000000000000000046401455701731300165160ustar00rootroot00000000000000.\" Copyright 2011 Glyph & Cog, LLC .TH pdfdetach 1 "15 August 2011" .SH NAME pdfdetach \- Portable Document Format (PDF) document embedded file extractor (version 3.03) .SH SYNOPSIS .B pdfdetach [options] .RI [ PDF-file ] .SH DESCRIPTION .B Pdfdetach lists or extracts embedded files (attachments) from a Portable Document Format (PDF) file. .SH OPTIONS Some of the following options can be set with configuration file commands. These are listed in square brackets with the description of the corresponding command line option. .TP .B \-list List all of the embedded files in the PDF file. File names are converted to the text encoding specified by the "\-enc" switch. .TP .BI \-save " number" Save the specified embedded file. By default, this uses the file name associated with the embedded file (as printed by the "\-list" switch); the file name can be changed with the "\-o" switch. .TP .BI \-savefile " filename" Save the specified embedded file. By default, this uses the file name associated with the embedded file (as printed by the "\-list" switch); the file name can be changed with the "\-o" switch. .TP .BI \-saveall Save all of the embedded files. This uses the file names associated with the embedded files (as printed by the "\-list" switch). By default, the files are saved in the current directory; this can be changed with the "\-o" switch. .TP .BI \-o " path" Set the file name used when saving an embedded file with the "\-save" switch, or the directory used by "\-saveall". .TP .BI \-enc " encoding-name" Sets the encoding to use for text output (embedded file names). This defaults to "UTF-8". .TP .BI \-opw " password" Specify the owner password for the PDF file. Providing this will bypass all security restrictions. .TP .BI \-upw " password" Specify the user password for the PDF file. .TP .B \-v Print copyright and version information. .TP .B \-h Print usage information. .RB ( \-help and .B \-\-help are equivalent.) .SH EXIT CODES The Xpdf tools use the following exit codes: .TP 0 No error. .TP 1 Error opening a PDF file. .TP 2 Error opening an output file. .TP 3 Error related to PDF permissions. .TP 99 Other error. .SH AUTHOR The pdfinfo software and documentation are copyright 1996-2011 Glyph & Cog, LLC. .SH "SEE ALSO" .BR pdffonts (1), .BR pdfimages (1), .BR pdfinfo (1), .BR pdftocairo (1), .BR pdftohtml (1), .BR pdftoppm (1), .BR pdftops (1), .BR pdftotext (1) .BR pdfseparate (1), .BR pdfsig (1), .BR pdfunite (1) poppler-24.02.0/utils/pdfdetach.cc000066400000000000000000000245371455701731300167520ustar00rootroot00000000000000//======================================================================== // // pdfdetach.cc // // Copyright 2010 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) 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 Adam Reichold // Copyright (C) 2019, 2021 Oliver Sander // Copyright (C) 2020 // // 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 "config.h" #include #include #include "goo/gmem.h" #include "parseargs.h" #include "Annot.h" #include "GlobalParams.h" #include "Page.h" #include "PDFDoc.h" #include "PDFDocFactory.h" #include "FileSpec.h" #include "CharTypes.h" #include "Catalog.h" #include "UnicodeMap.h" #include "PDFDocEncoding.h" #include "Error.h" #include "Win32Console.h" static bool doList = false; static int saveNum = 0; static char saveFile[128] = ""; static bool saveAll = false; static char savePath[1024] = ""; static char textEncName[128] = ""; static char ownerPassword[33] = "\001"; static char userPassword[33] = "\001"; static bool printVersion = false; static bool printHelp = false; static const ArgDesc argDesc[] = { { "-list", argFlag, &doList, 0, "list all embedded files" }, { "-save", argInt, &saveNum, 0, "save the specified embedded file (file number)" }, { "-savefile", argString, &saveFile, sizeof(saveFile), "save the specified embedded file (file name)" }, { "-saveall", argFlag, &saveAll, 0, "save all embedded files" }, { "-o", argString, savePath, sizeof(savePath), "file name for the saved embedded file" }, { "-enc", argString, textEncName, sizeof(textEncName), "output text encoding name" }, { "-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" }, {} }; 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; Page *page; Annots *annots; const GooString *s1; Unicode u; bool isUnicode; Win32Console win32Console(&argc, &argv); // parse args ok = parseArgs(argDesc, &argc, argv); hasSaveFile = strlen(saveFile) > 0; if ((doList ? 1 : 0) + ((saveNum != 0) ? 1 : 0) + ((hasSaveFile != 0) ? 1 : 0) + (saveAll ? 1 : 0) != 1) { ok = false; } if (!ok || argc != 2 || printVersion || printHelp) { fprintf(stderr, "pdfdetach version %s\n", PACKAGE_VERSION); fprintf(stderr, "%s\n", popplerCopyright); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("pdfdetach", "", argDesc); } return 99; } fileName = new GooString(argv[1]); // read config file globalParams = std::make_unique(); if (textEncName[0]) { globalParams->setTextEncoding(textEncName); } // get mapping to output encoding if (!(uMap = globalParams->getTextEncoding())) { error(errConfig, -1, "Couldn't get text encoding"); delete fileName; return 99; } // open PDF file if (ownerPassword[0] != '\001') { ownerPW = GooString(ownerPassword); } if (userPassword[0] != '\001') { userPW = GooString(userPassword); } doc = PDFDocFactory().createPDFDoc(*fileName, ownerPW, userPW); if (!doc->isOk()) { return 1; } for (i = 0; i < doc->getCatalog()->numEmbeddedFiles(); ++i) { embeddedFiles.push_back(doc->getCatalog()->embeddedFile(i)); } nPages = doc->getCatalog()->getNumPages(); for (i = 0; i < nPages; ++i) { page = doc->getCatalog()->getPage(i + 1); if (!page) { continue; } annots = page->getAnnots(); if (!annots) { break; } for (Annot *annot : annots->getAnnots()) { if (annot->getType() != Annot::typeFileAttachment) { continue; } embeddedFiles.push_back(std::make_unique(static_cast(annot)->getFile())); } } nFiles = embeddedFiles.size(); // list embedded files if (doList) { printf("%d embedded files\n", nFiles); for (i = 0; i < nFiles; ++i) { const std::unique_ptr &fileSpec = embeddedFiles[i]; printf("%d: ", i + 1); 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); } // save all embedded files } else if (saveAll) { 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(); 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; } *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); return 2; } } // save an embedded file } else { 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) { saveNum = i + 1; break; } } } if (saveNum < 1 || saveNum > nFiles) { error(errCommandLine, -1, hasSaveFile ? "Invalid file name" : "Invalid file number"); return 99; } const std::unique_ptr &fileSpec = embeddedFiles[saveNum - 1]; if (savePath[0]) { p = savePath; } else { p = path; 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; } *p = '\0'; p = path; } 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); return 2; } } return 0; } poppler-24.02.0/utils/pdffonts.1000066400000000000000000000045131455701731300164160ustar00rootroot00000000000000.\" Copyright 1999-2011 Glyph & Cog, LLC .TH pdffonts 1 "15 August 2011" .SH NAME pdffonts \- Portable Document Format (PDF) font analyzer (version 3.03) .SH SYNOPSIS .B pdffonts [options] .RI [ PDF-file ] .SH DESCRIPTION .B Pdffonts lists the fonts used in a Portable Document Format (PDF) file along with various information for each font. .PP If .I PDF-file is \'-', it reads the PDF file from stdin. .PP The following information is listed for each font: .TP .B name the font name, exactly as given in the PDF file (potentially including a subset prefix) .TP .B type the font type \(en see below for details .TP .B encoding the font encoding .TP .B emb "yes" if the font is embedded in the PDF file .TP .B sub "yes" if the font is a subset .TP .B uni "yes" if there is an explicit "ToUnicode" map in the PDF file (the absence of a ToUnicode map doesn't necessarily mean that the text can't be converted to Unicode) .TP .B object ID the font dictionary object ID (number and generation) .PP PDF files can contain the following types of fonts: .PP .RS Type 1 .RE .RS Type 1C \(en aka Compact Font Format (CFF) .RE .RS Type 3 .RE .RS TrueType .RE .RS CID Type 0 \(en 16-bit font with no specified type .RE .RS CID Type 0C \(en 16-bit PostScript CFF font .RE .RS CID TrueType \(en 16-bit TrueType font .RE .SH OPTIONS .TP .BI \-f " number" Specifies the first page to analyze. .TP .BI \-l " number" Specifies the last page to analyze. .TP .B \-subst List the substitute fonts that poppler will use for non embedded fonts. .TP .BI \-opw " password" Specify the owner password for the PDF file. Providing this will bypass all security restrictions. .TP .BI \-upw " password" Specify the user password for the PDF file. .TP .B \-v Print copyright and version information. .TP .B \-h Print usage information. .RB ( \-help and .B \-\-help are equivalent.) .SH EXIT CODES The Xpdf tools use the following exit codes: .TP 0 No error. .TP 1 Error opening a PDF file. .TP 2 Error opening an output file. .TP 3 Error related to PDF permissions. .TP 99 Other error. .SH AUTHOR The pdffonts software and documentation are copyright 1996\(en2011 Glyph & Cog, LLC. .SH "SEE ALSO" .nh .ad l .BR pdfdetach (1), .BR pdfimages (1), .BR pdfinfo (1), .BR pdftocairo (1), .BR pdftohtml (1), .BR pdftoppm (1), .BR pdftops (1), .BR pdftotext (1), .BR pdfseparate (1), .BR pdfsig (1), .BR pdfunite (1) poppler-24.02.0/utils/pdffonts.cc000066400000000000000000000151141455701731300166420ustar00rootroot00000000000000//======================================================================== // // pdffonts.cc // // Copyright 2001-2007 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 Dominic Lachowicz // Copyright (C) 2007-2008, 2010, 2018, 2022 Albert Astals Cid // Copyright (C) 2010 Hib Eris // Copyright (C) 2012, 2017 Adrian Johnson // Copyright (C) 2013 Suzuki Toshiya // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019, 2021 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 // //======================================================================== #include "config.h" #include #include #include #include #include #include #include #include "parseargs.h" #include "goo/GooString.h" #include "goo/gmem.h" #include "GlobalParams.h" #include "Object.h" #include "PDFDoc.h" #include "PDFDocFactory.h" #include "FontInfo.h" #include "Win32Console.h" static const char *fontTypeNames[] = { "unknown", "Type 1", "Type 1C", "Type 1C (OT)", "Type 3", "TrueType", "TrueType (OT)", "CID Type 0", "CID Type 0C", "CID Type 0C (OT)", "CID TrueType", "CID TrueType (OT)" }; static int firstPage = 1; static int lastPage = 0; static bool showSubst = false; static char ownerPassword[33] = "\001"; static char userPassword[33] = "\001"; static bool printVersion = false; static bool printHelp = false; static const ArgDesc argDesc[] = { { "-f", argInt, &firstPage, 0, "first page to examine" }, { "-l", argInt, &lastPage, 0, "last page to examine" }, { "-subst", argFlag, &showSubst, 0, "show font substitutions" }, { "-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" }, {} }; int main(int argc, char *argv[]) { std::optional ownerPW, userPW; bool ok; Win32Console win32Console(&argc, &argv); // parse args ok = parseArgs(argDesc, &argc, argv); if (!ok || argc != 2 || printVersion || printHelp) { fprintf(stderr, "pdffonts version %s\n", PACKAGE_VERSION); fprintf(stderr, "%s\n", popplerCopyright); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("pdffonts", "", argDesc); } if (printVersion || printHelp) { return 0; } return 99; } std::string fileName(argv[1]); if (fileName == "-") { fileName = "fd://0"; } // read config file globalParams = std::make_unique(); // open PDF file if (ownerPassword[0] != '\001') { ownerPW = GooString(ownerPassword); } if (userPassword[0] != '\001') { userPW = GooString(userPassword); } auto doc = std::unique_ptr(PDFDocFactory().createPDFDoc(GooString(fileName), ownerPW, userPW)); if (!doc->isOk()) { return 1; } // get page range if (firstPage < 1) { firstPage = 1; } if (lastPage < 1 || lastPage > doc->getNumPages()) { lastPage = doc->getNumPages(); } if (lastPage < firstPage) { fprintf(stderr, "Wrong page range given: the first page (%d) can not be after the last page (%d).\n", firstPage, lastPage); return 99; } // get the fonts { FontInfoScanner scanner(doc.get(), firstPage - 1); const std::vector fonts = scanner.scan(lastPage - firstPage + 1); if (showSubst) { // print the font substitutions 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 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()); } delete font; } } else { // print the font info 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 Ref fontRef = font->getRef(); if (fontRef.gen >= 100000) { printf(" [none]\n"); } else { printf(" %6d %2d\n", fontRef.num, fontRef.gen); } delete font; } } } return 0; } poppler-24.02.0/utils/pdfimages.1000066400000000000000000000142031455701731300165270ustar00rootroot00000000000000.\" Copyright 1998-2011 Glyph & Cog, LLC .TH pdfimages 1 "15 August 2011" .SH NAME pdfimages \- Portable Document Format (PDF) image extractor (version 3.03) .SH SYNOPSIS .B pdfimages [options] .I PDF-file image-root .SH DESCRIPTION .B Pdfimages saves images from a Portable Document Format (PDF) file as Portable Pixmap (PPM), Portable Bitmap (PBM), Portable Network Graphics (PNG), Tagged Image File Format (TIFF), JPEG, JPEG2000, or JBIG2 files. .PP Pdfimages reads the PDF file .IR PDF-file , scans one or more pages, and writes one file for each image, .IR image-root - nnn . xxx , where .I nnn is the image number and .I xxx is the image type (.ppm, .pbm, .png, .tif, .jpg, jp2, jb2e, or jb2g). If .I PDF-file is \'-', it reads the PDF file from stdin. .PP The default output format is PBM (for monochrome images) or PPM for non-monochrome. The \-png or \-tiff options change to default output to PNG or TIFF respectively. If both \-png and \-tiff are specified, CMYK images will be written as TIFF and all other images will be written as PNG. In addition the \-j, \-jp2, and \-jbig2 options will cause JPEG, JPEG2000, and JBIG2, respectively, images in the PDF file to be written in their native format. .SH OPTIONS .TP .BI \-f " number" Specifies the first page to scan. .TP .BI \-l " number" Specifies the last page to scan. .TP .B \-png Change the default output format to PNG. .TP .B \-tiff Change the default output format to TIFF. .TP .B \-j Write images in JPEG format as JPEG files instead of the default format. The JPEG file is identical to the JPEG data stored in the PDF. .TP .B \-jp2 Write images in JPEG2000 format as JP2 files instead of the default format. The JP2 file is identical to the JPEG2000 data stored in the PDF. .TP .B \-jbig2 Write images in JBIG2 format as JBIG2 files instead of the default format. JBIG2 data in PDF is of the embedded type. The embedded type of JBIG2 has an optional separate file containing global data. The embedded data is written with the extension .jb2e and the global data (if available) will be written to the same image number with the extension .jb2g. The content of both these files is identical to the JBIG2 data in the PDF. .TP .B \-ccitt Write images in CCITT format as CCITT files instead of the default format. The CCITT file is identical to the CCITT data stored in the PDF. PDF files contain additional parameters specifying how to decode the CCITT data. These parameters are translated to fax2tiff input options and written to a .params file with the same image number. The parameters are: .RS .TP .B \-1 1D Group 3 encoding .TP .B \-2 2D Group 3 encoding .TP .B \-4 Group 4 encoding .TP .B \-A Beginning of line is aligned on a byte boundary .TP .B \-P Beginning of line is not aligned on a byte boundary .TP .B \-X n The image width in pixels .TP .B \-W Encoding uses 1 for black and 0 for white .TP .B \-B Encoding uses 0 for black and 1 for white .TP .B \-M Input data fills from most significant bit to least significant bit. .RE .TP .B \-all Write JPEG, JPEG2000, JBIG2, and CCITT images in their native format. CMYK files are written as TIFF files. All other images are written as PNG files. This is equivalent to specifying the options \-png \-tiff \-j \-jp2 \-jbig2 \-ccitt. .TP .B \-list Instead of writing the images, list the images along with various information for each image. Do not specify an .IR image-root with this option. .IP The following information is listed for each image: .RS .TP .B page the page number containing the image .TP .B num the image number .TP .B type the image type: .PP .RS image - an opaque image .RE .RS mask - a monochrome mask image .RE .RS smask - a soft-mask image .RE .RS stencil - a monochrome mask image used for painting a color or pattern .RE .PP Note: Tranparency in images is represented in PDF using a separate image for the image and the mask/smask. The mask/smask used as part of a transparent image always immediately follows the image in the image list. .TP .B width image width (in pixels) .TP .B height image height (in pixels) .PP Note: the image width/height is the size of the embedded image, not the size the image will be rendered at. .TP .B color image color space: .PP .RS gray - Gray .RE .RS rgb - RGB .RE .RS cmyk - CMYK .RE .RS lab - L*a*b .RE .RS icc - ICC Based .RE .RS index - Indexed Color .RE .RS sep - Separation .RE .RS devn - DeviceN .RE .TP .B comp number of color components .TP .B bpc bits per component .TP .B enc encoding: .PP .RS image - raster image (may be Flate or LZW compressed but does not use an image encoding) .RE .RS jpeg - Joint Photographic Experts Group .RE .RS jp2 - JPEG2000 .RE .RS jbig2 - Joint Bi-Level Image Experts Group .RE .RS ccitt - CCITT Group 3 or Group 4 Fax .RE .TP .B interp "yes" if the interpolation is to be performed when scaling up the image .TP .B object ID the image dictionary object ID (number and generation) .TP .B x\-ppi The horizontal resolution of the image (in pixels per inch) when rendered on the pdf page. .TP .B y\-ppi The vertical resolution of the image (in pixels per inch) when rendered on the pdf page. .TP .B size The size of the embedded image in the pdf file. The following suffixes are used: 'B' bytes, 'K' kilobytes, 'M' megabytes, and 'G' gigabytes. .TP .B ratio The compression ratio of the embedded image. .RE .TP .BI \-opw " password" Specify the owner password for the PDF file. Providing this will bypass all security restrictions. .TP .BI \-upw " password" Specify the user password for the PDF file. .TP .B \-p Include page numbers in output file names. .TP .B \-q Don't print any messages or errors. .TP .B \-v Print copyright and version information. .TP .B \-h Print usage information. .RB ( \-help and .B \-\-help are equivalent.) .SH EXIT CODES The Xpdf tools use the following exit codes: .TP 0 No error. .TP 1 Error opening a PDF file. .TP 2 Error opening an output file. .TP 3 Error related to PDF permissions. .TP 99 Other error. .SH AUTHOR The pdfimages software and documentation are copyright 1998-2011 Glyph & Cog, LLC. .SH "SEE ALSO" .BR pdfdetach (1), .BR pdffonts (1), .BR pdfinfo (1), .BR pdftocairo (1), .BR pdftohtml (1), .BR pdftoppm (1), .BR pdftops (1), .BR pdftotext (1) .BR pdfseparate (1), .BR pdfsig (1), .BR pdfunite (1) poppler-24.02.0/utils/pdfimages.cc000066400000000000000000000167461455701731300167720ustar00rootroot00000000000000//======================================================================== // // pdfimages.cc // // Copyright 1998-2003 Glyph & Cog, LLC // // Modified for Debian by Hamish Moffatt, 22 May 2002. // //======================================================================== //======================================================================== // // 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) 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 // Copyright (C) 2013 Suzuki Toshiya // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019, 2021 Oliver Sander // Copyright (C) 2019 Hartmut Goebel // Copyright (C) 2024 Fernando Herrera // // 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 "config.h" #include #include #include #include #include #include "parseargs.h" #include "goo/GooString.h" #include "goo/gmem.h" #include "GlobalParams.h" #include "Object.h" #include "Stream.h" #include "Array.h" #include "Dict.h" #include "XRef.h" #include "Catalog.h" #include "Page.h" #include "PDFDoc.h" #include "PDFDocFactory.h" #include "ImageOutputDev.h" #include "Error.h" #include "Win32Console.h" static int firstPage = 1; static int lastPage = 0; static bool listImages = false; static bool enablePNG = false; static bool enableTiff = false; static bool dumpJPEG = false; static bool dumpJP2 = false; static bool dumpJBIG2 = false; static bool dumpCCITT = false; static bool allFormats = false; static bool pageNames = false; static char ownerPassword[33] = "\001"; static char userPassword[33] = "\001"; static bool quiet = false; static bool printVersion = false; static bool printHelp = false; static const ArgDesc argDesc[] = { { "-f", argInt, &firstPage, 0, "first page to convert" }, { "-l", argInt, &lastPage, 0, "last page to convert" }, #ifdef ENABLE_LIBPNG { "-png", argFlag, &enablePNG, 0, "change the default output format to PNG" }, #endif #ifdef ENABLE_LIBTIFF { "-tiff", argFlag, &enableTiff, 0, "change the default output format to TIFF" }, #endif { "-j", argFlag, &dumpJPEG, 0, "write JPEG images as JPEG files" }, { "-jp2", argFlag, &dumpJP2, 0, "write JPEG2000 images as JP2 files" }, { "-jbig2", argFlag, &dumpJBIG2, 0, "write JBIG2 images as JBIG2 files" }, { "-ccitt", argFlag, &dumpCCITT, 0, "write CCITT images as CCITT files" }, { "-all", argFlag, &allFormats, 0, "equivalent to -png -tiff -j -jp2 -jbig2 -ccitt" }, { "-list", argFlag, &listImages, 0, "print list of images instead of saving" }, { "-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" }, { "-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" }, { "-help", argFlag, &printHelp, 0, "print usage information" }, { "--help", argFlag, &printHelp, 0, "print usage information" }, { "-?", argFlag, &printHelp, 0, "print usage information" }, {} }; int main(int argc, char *argv[]) { char *imgRoot = nullptr; std::optional ownerPW, userPW; Win32Console win32Console(&argc, &argv); // parse args 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); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("pdfimages", " ", argDesc); } if (printVersion || printHelp) { return 0; } return 99; } GooString *fileName = new GooString(argv[1]); if (!listImages) { imgRoot = argv[2]; } // read config file globalParams = std::make_unique(); if (quiet) { globalParams->setErrQuiet(quiet); } // open PDF file if (ownerPassword[0] != '\001') { ownerPW = GooString(ownerPassword); } if (userPassword[0] != '\001') { userPW = GooString(userPassword); } if (fileName->cmp("-") == 0) { delete fileName; fileName = new GooString("fd://0"); } std::unique_ptr doc = PDFDocFactory().createPDFDoc(*fileName, ownerPW, userPW); delete fileName; if (!doc->isOk()) { return 1; } // check for copy permission #ifdef ENFORCE_PERMISSIONS if (!doc->okToCopy()) { error(errNotAllowed, -1, "Copying of images from this document is not allowed."); return 3; } #endif // get page range if (firstPage < 1) { firstPage = 1; } if (firstPage > doc->getNumPages()) { error(errCommandLine, -1, "Wrong page range given: the first page ({0:d}) can not be larger then the number of pages in the document ({1:d}).", firstPage, doc->getNumPages()); return 99; } if (lastPage < 1 || lastPage > doc->getNumPages()) { lastPage = doc->getNumPages(); } 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); return 99; } // write image files ImageOutputDev *imgOut = new ImageOutputDev(imgRoot, pageNames, listImages); if (imgOut->isOk()) { if (allFormats) { imgOut->enablePNG(true); imgOut->enableTiff(true); imgOut->enableJpeg(true); imgOut->enableJpeg2000(true); imgOut->enableJBig2(true); imgOut->enableCCITT(true); } else { imgOut->enablePNG(enablePNG); imgOut->enableTiff(enableTiff); imgOut->enableJpeg(dumpJPEG); imgOut->enableJpeg2000(dumpJP2); imgOut->enableJBig2(dumpJBIG2); imgOut->enableCCITT(dumpCCITT); } doc->displayPages(imgOut, firstPage, lastPage, 72, 72, 0, true, false, false); } const int exitCode = imgOut->isOk() ? 0 : imgOut->getErrorCode(); delete imgOut; return exitCode; } poppler-24.02.0/utils/pdfinfo.1000066400000000000000000000074711455701731300162260ustar00rootroot00000000000000.\" Copyright 1999-2011 Glyph & Cog, LLC .TH pdfinfo 1 "15 August 2011" .SH NAME pdfinfo \- Portable Document Format (PDF) document information extractor (version 3.03) .SH SYNOPSIS .B pdfinfo [options] .RI [ PDF-file ] .SH DESCRIPTION .B Pdfinfo prints the contents of the \'Info' dictionary (plus some other useful information) from a Portable Document Format (PDF) file. .PP If .I PDF-file is \'-', it reads the PDF file from stdin. .PP The \'Info' dictionary contains the following values: .PP .RS title .RE .RS subject .RE .RS keywords .RE .RS author .RE .RS creator .RE .RS producer .RE .RS creation date .RE .RS modification date .RE .PP In addition, the following information is printed: .PP .RS custom metadata (yes/no) .RE .RS metadata stream (yes/no) .RE .RS tagged (yes/no) .RE .RS userproperties (yes/no) .RE .RS suspects (yes/no) .RE .RS form (AcroForm / XFA / none) .RE .RS javascript (yes/no) .RE .RS page count .RE .RS encrypted flag (yes/no) .RE .RS print and copy permissions (if encrypted) .RE .RS page size .RE .RS file size .RE .RS linearized (yes/no) .RE .RS PDF version .RE .RS metadata (only if requested) .RE .PP The options \-listenc, \-meta, \-js, \-struct, and \-struct-text only print the requested information. The 'Info' dictionary and related data listed above is not printed. At most one of these five options may be used. .SH OPTIONS .TP .BI \-f " number" Specifies the first page to examine. If multiple pages are requested using the "\-f" and "\-l" options, the size of each requested page (and, optionally, the bounding boxes for each requested page) are printed. Otherwise, only page one is examined. .TP .BI \-l " number" Specifies the last page to examine. .TP .B \-box Prints the page box bounding boxes: MediaBox, CropBox, BleedBox, TrimBox, and ArtBox. .TP .B \-meta Prints document-level metadata. (This is the "Metadata" stream from the PDF file's Catalog object.) .TP .B \-custom Prints custom and standard metadata. .TP .B \-js Prints all JavaScript in the PDF. .TP .B \-struct Prints the logical document structure of a Tagged-PDF file. .TP .B \-struct-text Print the textual content along with the document structure of a Tagged-PDF file. Note that extracting text this way might be slow for big PDF files. (Implies .BR \-struct .) .TP .B \-url Print all URLs in the PDF. Only the URL types supported by Poppler are listed. Currently, this is limited to Annotations. Note: only URLs referenced by the PDF objects such as Link Annotations are listed. pdfinfo does not attempt to extract strings matching http://... from the text content. .TP .B \-isodates Prints dates in ISO-8601 format (including the time zone). .TP .B \-rawdates Prints the raw (undecoded) date strings, directly from the PDF file. .TP .B \-dests Print a list of all named destinations. If a page range is specified using "\-f" and "\-l", only destinations in the page range are listed. .TP .BI \-enc " encoding-name" Sets the encoding to use for text output. This defaults to "UTF-8". .TP .B \-listenc Lits the available encodings .TP .BI \-opw " password" Specify the owner password for the PDF file. Providing this will bypass all security restrictions. .TP .BI \-upw " password" Specify the user password for the PDF file. .TP .B \-v Print copyright and version information. .TP .B \-h Print usage information. .RB ( \-help and .B \-\-help are equivalent.) .SH EXIT CODES The Xpdf tools use the following exit codes: .TP 0 No error. .TP 1 Error opening a PDF file. .TP 2 Error opening an output file. .TP 3 Error related to PDF permissions. .TP 99 Other error. .SH AUTHOR The pdfinfo software and documentation are copyright 1996-2011 Glyph & Cog, LLC. .SH "SEE ALSO" .BR pdfdetach (1), .BR pdffonts (1), .BR pdfimages (1), .BR pdftocairo (1), .BR pdftohtml (1), .BR pdftoppm (1), .BR pdftops (1), .BR pdftotext (1) .BR pdfseparate (1), .BR pdfsig (1), .BR pdfunite (1) poppler-24.02.0/utils/pdfinfo.cc000066400000000000000000001117671455701731300164570ustar00rootroot00000000000000//======================================================================== // // pdfinfo.cc // // Copyright 1998-2003 Glyph & Cog, LLC // Copyright 2013 Igalia S.L. // //======================================================================== //======================================================================== // // 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 Dom Lachowicz // Copyright (C) 2007-2010, 2012, 2016-2022 Albert Astals Cid // Copyright (C) 2010 Hib Eris // Copyright (C) 2011 Vittal Aithal // Copyright (C) 2012, 2013, 2016-2018, 2021 Adrian Johnson // Copyright (C) 2012 Fabio D'Urso // Copyright (C) 2013 Adrian Perez de Castro // Copyright (C) 2013 Suzuki Toshiya // 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 Evangelos Rigas // Copyright (C) 2019 Christian Persch // Copyright (C) 2019-2021 Oliver Sander // Copyright (C) 2019 Thomas Fischer // 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 // //======================================================================== #include "config.h" #include #include #include #include #include #include #include #include #include #include "parseargs.h" #include "printencodings.h" #include "goo/GooString.h" #include "goo/gfile.h" #include "goo/glibc.h" #include "goo/gmem.h" #include "GlobalParams.h" #include "Object.h" #include "Stream.h" #include "Array.h" #include "Dict.h" #include "XRef.h" #include "Catalog.h" #include "Page.h" #include "PDFDoc.h" #include "PDFDocFactory.h" #include "CharTypes.h" #include "UnicodeMap.h" #include "UTF.h" #include "Error.h" #include "DateInfo.h" #include "JSInfo.h" #include "StructTreeRoot.h" #include "StructElement.h" #include "Win32Console.h" static int firstPage = 1; static int lastPage = 0; static bool printBoxes = false; static bool printMetadata = false; static bool printCustom = false; static bool printJS = false; static bool isoDates = false; static bool rawDates = false; static char textEncName[128] = ""; static char ownerPassword[33] = "\001"; static char userPassword[33] = "\001"; static bool printVersion = false; static bool printHelp = false; static bool printEnc = false; static bool printStructure = false; static bool printStructureText = false; static bool printDests = false; static bool printUrls = false; static const ArgDesc argDesc[] = { { "-f", argInt, &firstPage, 0, "first page to convert" }, { "-l", argInt, &lastPage, 0, "last page to convert" }, { "-box", argFlag, &printBoxes, 0, "print the page bounding boxes" }, { "-meta", argFlag, &printMetadata, 0, "print the document metadata (XML)" }, { "-custom", argFlag, &printCustom, 0, "print both custom and standard metadata" }, { "-js", argFlag, &printJS, 0, "print all JavaScript in the PDF" }, { "-struct", argFlag, &printStructure, 0, "print the logical document structure (for tagged files)" }, { "-struct-text", argFlag, &printStructureText, 0, "print text contents along with document structure (for tagged files)" }, { "-isodates", argFlag, &isoDates, 0, "print the dates in ISO-8601 format" }, { "-rawdates", argFlag, &rawDates, 0, "print the undecoded date strings directly from the PDF file" }, { "-dests", argFlag, &printDests, 0, "print all named destinations in the PDF" }, { "-url", argFlag, &printUrls, 0, "print all URLs inside PDF objects (does not scan text content)" }, { "-enc", argString, textEncName, sizeof(textEncName), "output text encoding name" }, { "-listenc", argFlag, &printEnc, 0, "list available encodings" }, { "-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" }, {} }; static void printTextString(const GooString *s, const UnicodeMap *uMap) { char buf[8]; std::vector u = TextStringToUCS4(s->toStr()); for (const auto &c : u) { int n = uMap->mapUnicode(c, buf, sizeof(buf)); fwrite(buf, 1, n, stdout); } } static void printUCS4String(const Unicode *u, int len, const UnicodeMap *uMap) { char buf[8]; for (int i = 0; i < len; i++) { int n = uMap->mapUnicode(u[i], buf, sizeof(buf)); fwrite(buf, 1, n, stdout); } } static void printInfoString(Dict *infoDict, const char *key, const char *text, const UnicodeMap *uMap) { const GooString *s1; Object obj = infoDict->lookup(key); if (obj.isString()) { fputs(text, stdout); s1 = obj.getString(); printTextString(s1, uMap); fputc('\n', stdout); } } static void printInfoDate(Dict *infoDict, const char *key, const char *text, const UnicodeMap *uMap) { int year, mon, day, hour, min, sec, tz_hour, tz_minute; char tz; struct tm tmStruct; time_t time; char buf[256]; Object obj = infoDict->lookup(key); if (obj.isString()) { fputs(text, stdout); const GooString *s = obj.getString(); // TODO do something with the timezone info if (parseDateString(s, &year, &mon, &day, &hour, &min, &sec, &tz, &tz_hour, &tz_minute)) { tmStruct.tm_year = year - 1900; tmStruct.tm_mon = mon - 1; tmStruct.tm_mday = day; tmStruct.tm_hour = hour; tmStruct.tm_min = min; tmStruct.tm_sec = sec; tmStruct.tm_wday = -1; tmStruct.tm_yday = -1; tmStruct.tm_isdst = -1; // compute the tm_wday and tm_yday fields time = timegm(&tmStruct); if (time != (time_t)-1) { int offset = (tz_hour * 60 + tz_minute) * 60; if (tz == '-') { offset *= -1; } time -= offset; localtime_r(&time, &tmStruct); strftime(buf, sizeof(buf), "%c %Z", &tmStruct); fputs(buf, stdout); } else { printTextString(s, uMap); } } else { printTextString(s, uMap); } fputc('\n', stdout); } } static void printISODate(Dict *infoDict, const char *key, const char *text, const UnicodeMap *uMap) { int year, mon, day, hour, min, sec, tz_hour, tz_minute; char tz; Object obj = infoDict->lookup(key); if (obj.isString()) { fputs(text, stdout); const GooString *s = obj.getString(); if (parseDateString(s, &year, &mon, &day, &hour, &min, &sec, &tz, &tz_hour, &tz_minute)) { fprintf(stdout, "%04d-%02d-%02dT%02d:%02d:%02d", year, mon, day, hour, min, sec); if (tz_hour == 0 && tz_minute == 0) { fprintf(stdout, "Z"); } else { fprintf(stdout, "%c%02d", tz, tz_hour); if (tz_minute) { fprintf(stdout, ":%02d", tz_minute); } } } else { printTextString(obj.getString(), uMap); } fputc('\n', stdout); } } static void printBox(const char *text, const PDFRectangle *box) { printf("%s%8.2f %8.2f %8.2f %8.2f\n", text, box->x1, box->y1, box->x2, box->y2); } static void printIndent(unsigned indent) { while (indent--) { putchar(' '); putchar(' '); } } 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()); } attribute->getValue()->print(stdout); if (attribute->getFormattedValue()) { printf(" \"%s\"", attribute->getFormattedValue()); } if (attribute->isHidden()) { printf(" [hidden]"); } } static void printStruct(const StructElement *element, unsigned indent) { if (element->isObjectRef()) { printIndent(indent); printf("Object %i %i\n", element->getObjectRef().num, element->getObjectRef().gen); return; } if (printStructureText && element->isContent()) { GooString *text = element->getText(false); printIndent(indent); if (text) { printf("\"%s\"\n", text->c_str()); } else { printf("(No content?)\n"); } delete text; } if (!element->isContent()) { printIndent(indent); printf("%s", element->getTypeName()); if (element->getID()) { printf(" <%s>", element->getID()->c_str()); } if (element->getTitle()) { printf(" \"%s\"", element->getTitle()->c_str()); } if (element->getRevision() > 0) { printf(" r%u", element->getRevision()); } if (element->isInline() || element->isBlock()) { printf(" (%s)", element->isInline() ? "inline" : "block"); } if (element->getNumAttributes()) { putchar(':'); for (unsigned i = 0; i < element->getNumAttributes(); i++) { putchar('\n'); printAttribute(element->getAttribute(i), indent + 1); } } putchar('\n'); for (unsigned i = 0; i < element->getNumChildren(); i++) { printStruct(element->getChild(i), indent + 1); } } } 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; switch (dest->getKind()) { case destXYZ: s.append("[ XYZ "); if (dest->getChangeLeft()) { s.appendf("{0:4.0g} ", dest->getLeft()); } else { s.append("null "); } if (dest->getChangeTop()) { s.appendf("{0:4.0g} ", dest->getTop()); } else { s.append("null "); } if (dest->getChangeZoom()) { s.appendf("{0:4.2f} ", dest->getZoom()); } else { s.append("null "); } break; case destFit: s.append("[ Fit "); break; case destFitH: if (dest->getChangeTop()) { s.appendf("[ FitH {0:4.0g} ", dest->getTop()); } else { s.append("[ FitH null "); } break; case destFitV: if (dest->getChangeLeft()) { s.appendf("[ FitV {0:4.0g} ", dest->getLeft()); } else { s.append("[ FitV null "); } break; case destFitR: s.appendf("[ FitR {0:4.0g} {1:4.0g} {2:4.0g} {3:4.0g} ", dest->getLeft(), dest->getBottom(), dest->getRight(), dest->getTop()); break; case destFitB: s.append("[ FitB "); break; case destFitBH: if (dest->getChangeTop()) { s.appendf("[ FitBH {0:4.0g} ", dest->getTop()); } else { s.append("[ FitBH null "); } break; case destFitBV: if (dest->getChangeLeft()) { s.appendf("[ FitBV {0:4.0g} ", dest->getLeft()); } else { s.append("[ FitBV null "); } break; } s.append(" "); s.setChar(26, ']'); s.setChar(27, '\0'); printf("%s", s.c_str()); } static void printDestinations(PDFDoc *doc, const UnicodeMap *uMap) { std::map, GooStringCompare>> map; int numDests = doc->getCatalog()->numDestNameTree(); for (int i = 0; i < numDests; i++) { GooString *name = new GooString(doc->getCatalog()->getDestNameTreeName(i)); std::unique_ptr dest = doc->getCatalog()->getDestNameTreeDest(i); if (dest && dest->isPageRef()) { Ref pageRef = dest->getPageRef(); map[pageRef].insert(std::make_pair(name, std::move(dest))); } else { delete name; } } numDests = doc->getCatalog()->numDests(); for (int i = 0; i < numDests; i++) { GooString *name = new GooString(doc->getCatalog()->getDestsName(i)); std::unique_ptr dest = doc->getCatalog()->getDestsDest(i); if (dest && dest->isPageRef()) { Ref pageRef = dest->getPageRef(); map[pageRef].insert(std::make_pair(name, std::move(dest))); } else { delete name; } } printf("Page Destination Name\n"); for (int i = firstPage; i <= lastPage; i++) { Ref *ref = doc->getCatalog()->getPageRef(i); if (ref) { auto pageDests = map.find(*ref); if (pageDests != map.end()) { for (auto &it : pageDests->second) { printf("%4d ", i); printLinkDest(it.second); printf(" \""); printTextString(it.first, uMap); printf("\"\n"); delete it.first; } } } } } static void printUrlList(PDFDoc *doc) { printf("Page Type URL\n"); for (int pg = firstPage; pg <= lastPage; pg++) { Page *page = doc->getPage(pg); if (page) { std::unique_ptr links = page->getLinks(); for (AnnotLink *annot : links->getLinks()) { LinkAction *action = annot->getAction(); if (action->getKind() == actionURI) { LinkURI *linkUri = dynamic_cast(action); std::string uri = linkUri->getURI(); printf("%4d Annotation %s\n", pg, uri.c_str()); } } } } } static void printPdfSubtype(PDFDoc *doc, const UnicodeMap *uMap) { const Object info = doc->getDocInfo(); if (info.isDict()) { const PDFSubtype pdftype = doc->getPDFSubtype(); if ((pdftype == subtypeNull) | (pdftype == subtypeNone)) { return; } std::unique_ptr part; std::unique_ptr abbr; std::unique_ptr standard; std::unique_ptr typeExp; std::unique_ptr confExp; // Form title from PDFSubtype switch (pdftype) { case subtypePDFA: printInfoString(info.getDict(), "GTS_PDFA1Version", "PDF subtype: ", uMap); typeExp = std::make_unique("ISO 19005 - Electronic document file format for long-term preservation (PDF/A)"); standard = std::make_unique("ISO 19005"); abbr = std::make_unique("PDF/A"); break; case subtypePDFE: printInfoString(info.getDict(), "GTS_PDFEVersion", "PDF subtype: ", uMap); typeExp = std::make_unique("ISO 24517 - Engineering document format using PDF (PDF/E)"); standard = std::make_unique("ISO 24517"); abbr = std::make_unique("PDF/E"); break; case subtypePDFUA: printInfoString(info.getDict(), "GTS_PDFUAVersion", "PDF subtype: ", uMap); typeExp = std::make_unique("ISO 14289 - Electronic document file format enhancement for accessibility (PDF/UA)"); standard = std::make_unique("ISO 14289"); abbr = std::make_unique("PDF/UA"); break; case subtypePDFVT: printInfoString(info.getDict(), "GTS_PDFVTVersion", "PDF subtype: ", uMap); typeExp = std::make_unique("ISO 16612 - Electronic document file format for variable data exchange (PDF/VT)"); standard = std::make_unique("ISO 16612"); abbr = std::make_unique("PDF/VT"); break; case subtypePDFX: printInfoString(info.getDict(), "GTS_PDFXVersion", "PDF subtype: ", uMap); typeExp = std::make_unique("ISO 15930 - Electronic document file format for prepress digital data exchange (PDF/X)"); standard = std::make_unique("ISO 15930"); abbr = std::make_unique("PDF/X"); break; case subtypeNone: case subtypeNull: default: return; } // Form the abbreviation from PDFSubtypePart and PDFSubtype const PDFSubtypePart subpart = doc->getPDFSubtypePart(); switch (pdftype) { case subtypePDFX: switch (subpart) { case subtypePart1: abbr->append("-1:2001"); break; case subtypePart2: abbr->append("-2"); break; case subtypePart3: abbr->append("-3:2002"); break; case subtypePart4: abbr->append("-1:2003"); break; case subtypePart5: abbr->append("-2"); break; case subtypePart6: abbr->append("-3:2003"); break; case subtypePart7: abbr->append("-4"); break; case subtypePart8: abbr->append("-5"); break; default: break; } break; case subtypeNone: case subtypeNull: break; default: abbr->appendf("-{0:d}", subpart); break; } // Form standard from PDFSubtypePart switch (subpart) { case subtypePartNone: case subtypePartNull: break; default: standard->appendf("-{0:d}", subpart); break; } // Form the subtitle from PDFSubtypePart and PDFSubtype switch (pdftype) { case subtypePDFA: switch (subpart) { case subtypePart1: part = std::make_unique("Use of PDF 1.4"); break; case subtypePart2: part = std::make_unique("Use of ISO 32000-1"); break; case subtypePart3: part = std::make_unique("Use of ISO 32000-1 with support for embedded files"); break; default: break; } break; case subtypePDFE: switch (subpart) { case subtypePart1: part = std::make_unique("Use of PDF 1.6"); break; default: break; } break; case subtypePDFUA: switch (subpart) { case subtypePart1: part = std::make_unique("Use of ISO 32000-1"); break; case subtypePart2: part = std::make_unique("Use of ISO 32000-2"); break; case subtypePart3: part = std::make_unique("Use of ISO 32000-1 with support for embedded files"); break; default: break; } break; case subtypePDFVT: switch (subpart) { case subtypePart1: part = std::make_unique("Using PPML 2.1 and PDF 1.4"); break; case subtypePart2: part = std::make_unique("Using PDF/X-4 and PDF/X-5 (PDF/VT-1 and PDF/VT-2)"); break; case subtypePart3: part = std::make_unique("Using PDF/X-6 (PDF/VT-3)"); break; default: break; } break; case subtypePDFX: switch (subpart) { case subtypePart1: part = std::make_unique("Complete exchange using CMYK data (PDF/X-1 and PDF/X-1a)"); break; case subtypePart3: part = std::make_unique("Complete exchange suitable for colour-managed workflows (PDF/X-3)"); break; case subtypePart4: part = std::make_unique("Complete exchange of CMYK and spot colour printing data using PDF 1.4 (PDF/X-1a)"); break; case subtypePart5: part = std::make_unique("Partial exchange of printing data using PDF 1.4 (PDF/X-2) [Withdrawn]"); break; case subtypePart6: part = std::make_unique("Complete exchange of printing data suitable for colour-managed workflows using PDF 1.4 (PDF/X-3)"); break; case subtypePart7: part = std::make_unique("Complete exchange of printing data (PDF/X-4) and partial exchange of printing data with external profile reference (PDF/X-4p) using PDF 1.6"); break; case subtypePart8: part = std::make_unique("Partial exchange of printing data using PDF 1.6 (PDF/X-5)"); break; default: break; } break; default: break; } // Form Conformance explanation from PDFSubtypeConformance switch (doc->getPDFSubtypeConformance()) { case subtypeConfA: confExp = std::make_unique("Level A, Accessible"); break; case subtypeConfB: confExp = std::make_unique("Level B, Basic"); break; case subtypeConfG: confExp = std::make_unique("Level G, External graphical content"); break; case subtypeConfN: confExp = std::make_unique("Level N, External ICC profile"); break; case subtypeConfP: confExp = std::make_unique("Level P, Embedded ICC profile"); break; case subtypeConfPG: confExp = std::make_unique("Level PG, Embedded ICC profile and external graphical content"); break; case subtypeConfU: confExp = std::make_unique("Level U, Unicode support"); break; case subtypeConfNone: case subtypeConfNull: default: confExp.reset(); break; } printf(" Title: %s\n", typeExp->c_str()); printf(" Abbreviation: %s\n", abbr->c_str()); if (part.get()) { 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()) { printf(" Conformance: %s\n", confExp->c_str()); } } } static void printCustomInfo(PDFDoc *doc, const UnicodeMap *uMap) { Object info = doc->getDocInfo(); if (info.isDict()) { Dict *dict = info.getDict(); // Sort keys std::set keys; for (int i = 0; i < dict->getLength(); i++) { std::string key(dict->getKey(i)); if (key != "Trapped") { keys.insert(key); } } for (const std::string &key : keys) { if (key == "CreationDate") { if (isoDates) { printISODate(info.getDict(), "CreationDate", "CreationDate: ", uMap); } else if (rawDates) { printInfoString(info.getDict(), "CreationDate", "CreationDate: ", uMap); } else { printInfoDate(info.getDict(), "CreationDate", "CreationDate: ", uMap); } } else if (key == "ModDate") { if (isoDates) { printISODate(info.getDict(), "ModDate", "ModDate: ", uMap); } else if (rawDates) { printInfoString(info.getDict(), "ModDate", "ModDate: ", uMap); } else { printInfoDate(info.getDict(), "ModDate", "ModDate: ", uMap); } } else { 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); fputs(":", stdout); while (len < 16) { fputs(" ", stdout); len++; } gfree(u); // print value GooString val_str(obj.getString()); printTextString(&val_str, uMap); fputc('\n', stdout); } } } } } static void printInfo(PDFDoc *doc, const UnicodeMap *uMap, long long filesize, bool multiPage) { Page *page; char buf[256]; double w, h, wISO, hISO, isoThreshold; int pg, i; int r; // print doc info Object info = doc->getDocInfo(); if (info.isDict()) { printInfoString(info.getDict(), "Title", "Title: ", uMap); printInfoString(info.getDict(), "Subject", "Subject: ", uMap); printInfoString(info.getDict(), "Keywords", "Keywords: ", uMap); printInfoString(info.getDict(), "Author", "Author: ", uMap); printInfoString(info.getDict(), "Creator", "Creator: ", uMap); printInfoString(info.getDict(), "Producer", "Producer: ", uMap); if (isoDates) { printISODate(info.getDict(), "CreationDate", "CreationDate: ", uMap); printISODate(info.getDict(), "ModDate", "ModDate: ", uMap); } else if (rawDates) { printInfoString(info.getDict(), "CreationDate", "CreationDate: ", uMap); printInfoString(info.getDict(), "ModDate", "ModDate: ", uMap); } else { printInfoDate(info.getDict(), "CreationDate", "CreationDate: ", uMap); printInfoDate(info.getDict(), "ModDate", "ModDate: ", uMap); } } bool hasMetadata = false; std::unique_ptr metadata = doc->readMetadata(); if (metadata) { hasMetadata = true; } const std::set docInfoStandardKeys { "Title", "Author", "Subject", "Keywords", "Creator", "Producer", "CreationDate", "ModDate", "Trapped" }; bool hasCustom = false; if (info.isDict()) { Dict *dict = info.getDict(); for (i = 0; i < dict->getLength(); i++) { std::string key(dict->getKey(i)); if (docInfoStandardKeys.find(key) == docInfoStandardKeys.end()) { hasCustom = true; break; } } } // print metadata info printf("Custom Metadata: %s\n", hasCustom ? "yes" : "no"); printf("Metadata Stream: %s\n", hasMetadata ? "yes" : "no"); // print tagging info printf("Tagged: %s\n", (doc->getCatalog()->getMarkInfo() & Catalog::markInfoMarked) ? "yes" : "no"); printf("UserProperties: %s\n", (doc->getCatalog()->getMarkInfo() & Catalog::markInfoUserProperties) ? "yes" : "no"); printf("Suspects: %s\n", (doc->getCatalog()->getMarkInfo() & Catalog::markInfoSuspects) ? "yes" : "no"); // print form info switch (doc->getCatalog()->getFormType()) { case Catalog::NoForm: printf("Form: none\n"); break; case Catalog::AcroForm: printf("Form: AcroForm\n"); break; case Catalog::XfaForm: printf("Form: XFA\n"); break; } // print javascript info { JSInfo jsInfo(doc, firstPage - 1); jsInfo.scanJS(lastPage - firstPage + 1); printf("JavaScript: %s\n", jsInfo.containsJS() ? "yes" : "no"); } // print page count printf("Pages: %d\n", doc->getNumPages()); // print encryption info printf("Encrypted: "); if (doc->isEncrypted()) { unsigned char *fileKey; CryptAlgorithm encAlgorithm; int keyLength; doc->getXRef()->getEncryptionParameters(&fileKey, &encAlgorithm, &keyLength); const char *encAlgorithmName = "unknown"; switch (encAlgorithm) { case cryptRC4: encAlgorithmName = "RC4"; break; case cryptAES: encAlgorithmName = "AES"; break; case cryptAES256: encAlgorithmName = "AES-256"; break; case cryptNone: break; } printf("yes (print:%s copy:%s change:%s addNotes:%s algorithm:%s)\n", doc->okToPrint(true) ? "yes" : "no", doc->okToCopy(true) ? "yes" : "no", doc->okToChange(true) ? "yes" : "no", doc->okToAddNotes(true) ? "yes" : "no", encAlgorithmName); } else { printf("no\n"); } // print page size for (pg = firstPage; pg <= lastPage; ++pg) { w = doc->getPageCropWidth(pg); h = doc->getPageCropHeight(pg); if (multiPage) { printf("Page %4d size: %g x %g pts", pg, w, h); } else { printf("Page size: %g x %g pts", w, h); } 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); 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)) { printf(" (A%d)", i); break; } hISO = wISO; wISO /= sqrt(2.0); isoThreshold /= sqrt(2.0); } } printf("\n"); r = doc->getPageRotate(pg); if (multiPage) { printf("Page %4d rot: %d\n", pg, r); } else { printf("Page rot: %d\n", r); } } // print the boxes if (printBoxes) { if (multiPage) { for (pg = firstPage; pg <= lastPage; ++pg) { page = doc->getPage(pg); if (!page) { error(errSyntaxError, -1, "Failed to print boxes for page {0:d}", pg); continue; } sprintf(buf, "Page %4d MediaBox: ", pg); printBox(buf, page->getMediaBox()); sprintf(buf, "Page %4d CropBox: ", pg); printBox(buf, page->getCropBox()); sprintf(buf, "Page %4d BleedBox: ", pg); printBox(buf, page->getBleedBox()); sprintf(buf, "Page %4d TrimBox: ", pg); printBox(buf, page->getTrimBox()); sprintf(buf, "Page %4d ArtBox: ", pg); printBox(buf, page->getArtBox()); } } else { page = doc->getPage(firstPage); if (!page) { error(errSyntaxError, -1, "Failed to print boxes for page {0:d}", firstPage); } else { printBox("MediaBox: ", page->getMediaBox()); printBox("CropBox: ", page->getCropBox()); printBox("BleedBox: ", page->getBleedBox()); printBox("TrimBox: ", page->getTrimBox()); printBox("ArtBox: ", page->getArtBox()); } } } // print file size printf("File size: %lld bytes\n", filesize); // print linearization info printf("Optimized: %s\n", doc->isLinearized() ? "yes" : "no"); // print PDF version printf("PDF version: %d.%d\n", doc->getPDFMajorVersion(), doc->getPDFMinorVersion()); printPdfSubtype(doc, uMap); } int main(int argc, char *argv[]) { std::unique_ptr doc; GooString *fileName; std::optional ownerPW, userPW; const UnicodeMap *uMap; FILE *f; bool ok; int exitCode; bool multiPage; exitCode = 99; // parse args Win32Console win32console(&argc, &argv); ok = parseArgs(argDesc, &argc, argv); if (!ok || (argc != 2 && !printEnc) || printVersion || printHelp) { fprintf(stderr, "pdfinfo version %s\n", PACKAGE_VERSION); fprintf(stderr, "%s\n", popplerCopyright); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("pdfinfo", "", argDesc); } if (printVersion || printHelp) { exitCode = 0; } goto err0; } if (printStructureText) { printStructure = true; } // read config file globalParams = std::make_unique(); if (printEnc) { printEncodings(); exitCode = 0; goto err0; } fileName = new GooString(argv[1]); if (textEncName[0]) { globalParams->setTextEncoding(textEncName); } // get mapping to output encoding if (!(uMap = globalParams->getTextEncoding())) { error(errCommandLine, -1, "Couldn't get text encoding"); delete fileName; goto err1; } // open PDF file if (ownerPassword[0] != '\001') { ownerPW = GooString(ownerPassword); } if (userPassword[0] != '\001') { userPW = GooString(userPassword); } if (fileName->cmp("-") == 0) { delete fileName; fileName = new GooString("fd://0"); } doc = PDFDocFactory().createPDFDoc(*fileName, ownerPW, userPW); if (!doc->isOk()) { exitCode = 1; goto err2; } // get page range if (firstPage < 1) { firstPage = 1; } if (lastPage == 0) { multiPage = false; } else { multiPage = true; } if (lastPage < 1 || lastPage > doc->getNumPages()) { lastPage = doc->getNumPages(); } 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; } if (printMetadata) { // print the metadata const std::unique_ptr metadata = doc->readMetadata(); if (metadata) { fputs(metadata->c_str(), stdout); fputc('\n', stdout); } } else if (printCustom) { printCustomInfo(doc.get(), uMap); } else if (printJS) { // print javascript JSInfo jsInfo(doc.get(), firstPage - 1); jsInfo.scanJS(lastPage - firstPage + 1, stdout, uMap); } else if (printStructure || printStructureText) { // print structure const StructTreeRoot *structTree = doc->getCatalog()->getStructTreeRoot(); if (structTree) { for (unsigned i = 0; i < structTree->getNumChildren(); i++) { printStruct(structTree->getChild(i), 0); } } } else if (printDests) { printDestinations(doc.get(), uMap); } else if (printUrls) { printUrlList(doc.get()); } else { // print info long long filesize = 0; f = fopen(fileName->c_str(), "rb"); if (f) { Gfseek(f, 0, SEEK_END); filesize = Gftell(f); fclose(f); } if (multiPage == false) { lastPage = 1; } printInfo(doc.get(), uMap, filesize, multiPage); } exitCode = 0; // clean up err2: delete fileName; err1: err0: return exitCode; } poppler-24.02.0/utils/pdfseparate.1000066400000000000000000000030611455701731300170660ustar00rootroot00000000000000.\" Copyright 2011 The Poppler Developers - http://poppler.freedesktop.org .TH pdfseparate 1 "15 September 2011" .SH NAME pdfseparate \- Portable Document Format (PDF) page extractor .SH SYNOPSIS .B pdfseparate [options] .I PDF-file PDF-page-pattern .SH DESCRIPTION .B pdfseparate extract single pages from a Portable Document Format (PDF). .PP pdfseparate reads the PDF file .IR PDF-file , extracts one or more pages, and writes one PDF file for each page to .IR PDF-page-pattern. .PP PDF-page-pattern should contain .BR %d (or any variant respecting printf format), since %d is replaced by the page number. .TP The PDF-file should not be encrypted. .SH OPTIONS .TP .BI \-f " number" Specifies the first page to extract. If \-f is omitted, extraction starts with page 1. .TP .BI \-l " number" Specifies the last page to extract. If \-l is omitted, extraction ends with the last page. .TP .B \-v Print copyright and version information. .TP .B \-h Print usage information. .RB ( \-help and .B \-\-help are equivalent.) .SH EXAMPLE pdfseparate sample.pdf sample-%d.pdf .TP extracts all pages from sample.pdf, if i.e. sample.pdf has 3 pages, it produces .TP sample-1.pdf, sample-2.pdf, sample-3.pdf .SH AUTHOR The pdfseparate software and documentation are copyright 1996-2004 Glyph & Cog, LLC and copyright 2005-2011 The Poppler Developers - http://poppler.freedesktop.org .SH "SEE ALSO" .BR pdfdetach (1), .BR pdffonts (1), .BR pdfimages (1), .BR pdfinfo (1), .BR pdftocairo (1), .BR pdftohtml (1), .BR pdftoppm (1), .BR pdftops (1), .BR pdftotext (1) .BR pdfsig (1), .BR pdfunite (1) poppler-24.02.0/utils/pdfseparate.cc000066400000000000000000000131311455701731300173120ustar00rootroot00000000000000//======================================================================== // // pdfseparate.cc // // This file is licensed under the GPLv2 or later // // Copyright (C) 2011, 2012, 2015 Thomas Freitag // Copyright (C) 2012-2014, 2017, 2018, 2021, 2022 Albert Astals Cid // Copyright (C) 2013, 2016 Pino Toscano // Copyright (C) 2013 Daniel Kahn Gillmor // Copyright (C) 2013 Suzuki Toshiya // Copyright (C) 2017 Léonard Michelet // Copyright (C) 2017 Adrian Johnson // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019 Oliver Sander // //======================================================================== #include "config.h" #include #include #include #include #include #include "parseargs.h" #include "goo/GooString.h" #include "PDFDoc.h" #include "ErrorCodes.h" #include "GlobalParams.h" #include "Win32Console.h" #include static int firstPage = 0; static int lastPage = 0; static bool printVersion = false; static bool printHelp = false; static const ArgDesc argDesc[] = { { "-f", argInt, &firstPage, 0, "first page to extract" }, { "-l", argInt, &lastPage, 0, "last page to extract" }, { "-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" }, {} }; static bool extractPages(const char *srcFileName, const char *destFileName) { char pathName[4096]; PDFDoc *doc = new PDFDoc(std::make_unique(srcFileName)); if (!doc->isOk()) { error(errSyntaxError, -1, "Could not extract page(s) from damaged file ('{0:s}')", srcFileName); delete doc; return false; } // destFileName can have multiple %% and one %d // We use auxDestFileName to replace all the valid % appearances // by 'A' (random char that is not %), if at the end of replacing // any of the valid appearances there is still any % around, the // pattern is wrong if (firstPage == 0 && lastPage == 0) { firstPage = 1; lastPage = doc->getNumPages(); } if (lastPage == 0) { lastPage = doc->getNumPages(); } if (firstPage == 0) { firstPage = 1; } 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); delete doc; return false; } bool foundmatch = false; char *auxDestFileName = strdup(destFileName); char *p = strstr(auxDestFileName, "%d"); if (p != nullptr) { foundmatch = true; *p = 'A'; } else { char pattern[6]; for (int i = 2; i < 10; i++) { sprintf(pattern, "%%0%dd", i); p = strstr(auxDestFileName, pattern); if (p != nullptr) { foundmatch = true; *p = 'A'; break; } } } if (!foundmatch && firstPage != lastPage) { error(errSyntaxError, -1, "'{0:s}' must contain '%d' (or any variant respecting printf format) if more than one page should be extracted, in order to print the page number", destFileName); free(auxDestFileName); delete doc; return false; } // at this point auxDestFileName can only contain %% p = strstr(auxDestFileName, "%%"); while (p != nullptr) { *p = 'A'; *(p + 1) = 'A'; p = strstr(p, "%%"); } // at this point any other % is wrong p = strstr(auxDestFileName, "%"); if (p != nullptr) { error(errSyntaxError, -1, "'{0:s}' can only contain one '%d' pattern", destFileName); free(auxDestFileName); delete doc; return false; } free(auxDestFileName); for (int pageNo = firstPage; pageNo <= lastPage; pageNo++) { snprintf(pathName, sizeof(pathName) - 1, destFileName, pageNo); PDFDoc *pagedoc = new PDFDoc(std::make_unique(srcFileName)); int errCode = pagedoc->savePageAs(GooString(pathName), pageNo); if (errCode != errNone) { delete doc; delete pagedoc; return false; } delete pagedoc; } delete doc; return true; } static constexpr int kOtherError = 99; int main(int argc, char *argv[]) { // parse args Win32Console win32console(&argc, &argv); const bool parseOK = parseArgs(argDesc, &argc, argv); if (!parseOK || argc != 3 || printVersion || printHelp) { fprintf(stderr, "pdfseparate version %s\n", PACKAGE_VERSION); fprintf(stderr, "%s\n", popplerCopyright); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("pdfseparate", " ", argDesc); } if (printVersion || printHelp) { return 0; } else { return kOtherError; } } globalParams = std::make_unique(); const bool extractOK = extractPages(argv[1], argv[2]); return extractOK ? 0 : kOtherError; } poppler-24.02.0/utils/pdfsig.1000066400000000000000000000113561455701731300160520ustar00rootroot00000000000000.\" Copyright 2011 The Poppler Developers - http://poppler.freedesktop.org .TH pdfsig 1 "28 October 2015" .SH NAME pdfsig \- Portable Document Format (PDF) digital signatures tool .SH SYNOPSIS .B pdfsig [options] .RI [ PDF-file ] .RI [ Output-file ] .SH DESCRIPTION .B pdfsig verifies the digital signatures in a PDF document. It also displays the identity of each signer (commonName field and full distinguished name of the signer certificate), the time and date of the signature, the hash algorithm used for signing, 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. .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: .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. .SH OPTIONS .TP .B \-nssdir "[prefix]directory" Specify the database directory containing the certificate and key database files. See certutil(1) -d option for details of the prefix. If not specified the other search locations described in .B DESCRIPTION are used. .TP .B \-nss-pwd "password" Specify the password needed to access the NSS database (if any). .TP .B \-nocert Do not validate the certificate. .TP .B \-no-ocsp Do not perform online OCSP certificate revocation check (local Certificate Revocation Lists (CRL) are still used). .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 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. .TP .B \-new-signature-field-name " name" Specifies the field name to be used when adding a new signature. A random ID will be used by default. .TP .B \-sign " field" 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 (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 (this might be missing if the key isn't password protected). .TP .B \-digest " algorithm" Use the given digest algorithm for signing (default: SHA256). .TP .B \-reason " reason" Set the given reason string for the signature (default: no reason set). .TP .B \-etsi Create a signature of type ETSI.CAdES.detached instead of adbe.pkcs7.detached. .TP .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 .B \-h Print usage information. .RB ( \-help and .B \-\-help are equivalent.) .SH EXAMPLES .TP pdfsig signed_file.pdf Displays signature info for signed_file.pdf. .TP 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 The pdfsig software and documentation are copyright 1996-2004 Glyph & Cog, LLC and copyright 2005-2015 The Poppler Developers - http://poppler.freedesktop.org .SH "SEE ALSO" .BR pdfdetach (1), .BR pdffonts (1), .BR pdfimages (1), .BR pdfinfo (1), .BR pdftocairo (1), .BR pdftohtml (1), .BR pdftoppm (1), .BR pdftops (1), .BR pdftotext (1) .BR pdfseparate (1), .BR pdfunite (1) .BR certutil (1) poppler-24.02.0/utils/pdfsig.cc000066400000000000000000000555501455701731300163030ustar00rootroot00000000000000//======================================================================== // // pdfsig.cc // // This file is licensed under the GPLv2 or later // // Copyright 2015 André Guerreiro // Copyright 2015 André Esser // Copyright 2015, 2017-2023 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. 2023 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 2023, 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== #include "config.h" #include #include #include #include #include #include #include #include #include "parseargs.h" #include "Object.h" #include "Array.h" #include "goo/gbasename.h" #include "Page.h" #include "PDFDoc.h" #include "PDFDocFactory.h" #include "Error.h" #include "GlobalParams.h" #ifdef ENABLE_NSS3 # include "NSSCryptoSignBackend.h" #endif #include "CryptoSignBackend.h" #include "SignatureInfo.h" #include "Win32Console.h" #include "numberofcharacters.h" #include "UTF.h" #if __has_include() # include #endif static const char *getReadableSigState(SignatureValidationStatus sig_vs) { switch (sig_vs) { case SIGNATURE_VALID: return "Signature is Valid."; case SIGNATURE_INVALID: return "Signature is Invalid."; case SIGNATURE_DIGEST_MISMATCH: return "Digest Mismatch."; case SIGNATURE_DECODING_ERROR: return "Document isn't signed or corrupted data."; case SIGNATURE_NOT_VERIFIED: return "Signature has not yet been verified."; default: return "Unknown Validation Failure."; } } static const char *getReadableCertState(CertificateValidationStatus cert_vs) { switch (cert_vs) { case CERTIFICATE_TRUSTED: return "Certificate is Trusted."; case CERTIFICATE_UNTRUSTED_ISSUER: return "Certificate issuer isn't Trusted."; case CERTIFICATE_UNKNOWN_ISSUER: return "Certificate issuer is unknown."; case CERTIFICATE_REVOKED: return "Certificate has been Revoked."; case CERTIFICATE_EXPIRED: return "Certificate has Expired"; case CERTIFICATE_NOT_VERIFIED: return "Certificate has not yet been verified."; default: return "Unknown issue with Certificate or corrupted data."; } } static char *getReadableTime(time_t unix_time) { char *time_str = (char *)gmalloc(64); strftime(time_str, 64, "%b %d %Y %H:%M:%S", localtime(&unix_time)); return time_str; } static bool dumpSignature(int sig_num, int sigCount, FormFieldSignature *s, const char *filename) { const GooString *signature = s->getSignature(); if (!signature) { printf("Cannot dump signature #%d\n", sig_num); return false; } const int sigCountLength = numberOfCharacters(sigCount); // 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()); outfile.close(); return true; } static GooString nssDir; static GooString nssPassword; 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 dumpSignatures = false; static bool etsiCAdESdetached = false; static char backendString[256] = ""; static char signatureName[256] = ""; static char certNickname[256] = ""; static char password[256] = ""; static char digestName[256] = "SHA256"; static GooString reason; static bool listNicknames = false; static bool addNewSignature = false; static bool useAIACertFetch = false; static GooString newSignatureFieldName; static const ArgDesc argDesc[] = { { "-nssdir", argGooString, &nssDir, 0, "path to directory of libnss3 database" }, { "-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" }, { "-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" }, { "-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" }, {} }; static void print_version_usage(bool usage) { fprintf(stderr, "pdfsig version %s\n", PACKAGE_VERSION); fprintf(stderr, "%s\n", popplerCopyright); fprintf(stderr, "%s\n", xpdfCopyright); if (usage) { printUsage("pdfsig", " []", argDesc); } } 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 * { static bool firstTime = true; if (!firstTime) { wrongPassword = true; return nullptr; } firstTime = false; if (nssPassword.getLength() > 0) { return strdup(nssPassword.c_str()); } else { passwordNeeded = true; return nullptr; } }; 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"); printf("\tPlease provide one with -nss-pwd.\n"); return {}; } if (wrongPassword) { *error = true; printf("Password was not accepted to open the NSS database.\n"); printf("\tPlease provide the correct one with -nss-pwd.\n"); 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(); std::vector u = TextStringToUCS4(str); std::string convertedStr; for (auto &c : u) { char buf[8]; const int n = utf8Map->mapUnicode(c, buf, sizeof(buf)); convertedStr.append(buf, n); } return convertedStr; } int main(int argc, char *argv[]) { char *time_str = nullptr; globalParams = std::make_unique(); Win32Console win32Console(&argc, &argv); const bool ok = parseArgs(argDesc, &argc, argv); if (!ok) { print_version_usage(true); return 99; } if (printVersion) { print_version_usage(false); return 0; } if (printHelp) { print_version_usage(true); return 0; } 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; const std::vector> vCerts = getAvailableSigningCertificates(&getCertsError); if (getCertsError) { return 2; } else { if (vCerts.empty()) { printf("There are no certificates available.\n"); } else { printf("Certificate nicknames available:\n"); for (auto &cert : vCerts) { const GooString &nick = cert->getNickName(); const auto location = locationToString(cert->getKeyLocation()); printf("%s %s\n", nick.c_str(), location.c_str()); } } } return 0; } if (argc < 2) { // no filename was given print_version_usage(true); return 99; } std::unique_ptr fileName = std::make_unique(argv[1]); std::optional ownerPW, userPW; if (ownerPassword[0] != '\001') { ownerPW = GooString(ownerPassword); } if (userPassword[0] != '\001') { userPW = GooString(userPassword); } // open PDF file std::unique_ptr doc(PDFDocFactory().createPDFDoc(*fileName, ownerPW, userPW)); if (!doc->isOk()) { return 1; } int signatureNumber; if (strlen(signatureName) > 0) { signatureNumber = atoi(signatureName); if (signatureNumber == 0) { signatureNumber = -1; } } else { signatureNumber = 0; } if (addNewSignature && signatureNumber > 0) { // incompatible options print_version_usage(true); return 99; } if (addNewSignature) { if (argc == 2) { fprintf(stderr, "An output filename for the signed document must be given\n"); return 2; } if (strlen(certNickname) == 0) { printf("A nickname of the signing certificate must be given\n"); return 2; } if (etsiCAdESdetached) { printf("-etsi is not supported yet with -add-signature\n"); printf("Please file a bug report if this is important for you\n"); return 2; } if (digestName != std::string("SHA256")) { printf("Only digest SHA256 is supported at the moment with -add-signature\n"); printf("Please file a bug report if this is important for you\n"); return 2; } if (doc->getPage(1) == nullptr) { printf("Error getting first page of the document.\n"); return 2; } bool getCertsError; // We need to call this otherwise NSS spins forever getAvailableSigningCertificates(&getCertsError); if (getCertsError) { return 2; } 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 // hopefully give us something that is not already in the document std::random_device rd; std::mt19937 gen(rd()); std::uniform_int_distribution<> distrib(1, 15); for (int i = 0; i < 32; ++i) { const int value = distrib(gen); newSignatureFieldName.append(value < 10 ? 48 + value : 65 + (value - 10)); } } // We don't provide a way to customize the UI from pdfsig for now const bool success = 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; } const std::vector signatures = doc->getSignatureFields(); const unsigned int sigCount = signatures.size(); if (signatureNumber == -1) { for (unsigned int i = 0; i < sigCount; i++) { const GooString *goo = signatures.at(i)->getCreateWidget()->getField()->getFullyQualifiedName(); if (!goo) { continue; } const std::string name = TextStringToUTF8(goo->toStr()); if (name == signatureName) { signatureNumber = i + 1; break; } } if (signatureNumber == -1) { fprintf(stderr, "Signature field not found by name\n"); return 2; } } if (signatureNumber > 0) { // We are signing an existing signature field if (argc == 2) { fprintf(stderr, "An output filename for the signed document must be given\n"); return 2; } if (signatureNumber > static_cast(sigCount)) { printf("File '%s' does not contain a signature with number %d\n", fileName->c_str(), signatureNumber); return 2; } if (strlen(certNickname) == 0) { printf("A nickname of the signing certificate must be given\n"); 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); if (getCertsError) { return 2; } FormFieldSignature *ffs = signatures.at(signatureNumber - 1); Goffset file_size = 0; const std::optional sig = ffs->getCheckedSignature(&file_size); if (sig) { printf("Signature number %d is already signed\n", signatureNumber); return 2; } if (etsiCAdESdetached) { ffs->setSignatureType(ETSI_CAdES_detached); } 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; } FormWidgetSignature *fws = static_cast(ffs->getWidget(0)); const bool success = fws->signDocument(std::string { argv[2] }, std::string { certNickname }, std::string { password }, rs.get()); return success ? 0 : 3; } if (argc > 2) { // We are not signing and more than 1 filename was given print_version_usage(true); return 99; } if (sigCount >= 1) { if (dumpSignatures) { printf("Dumping Signatures: %u\n", sigCount); for (unsigned int i = 0; i < sigCount; i++) { const bool dumpingOk = dumpSignature(i, sigCount, signatures.at(i), fileName->c_str()); if (!dumpingOk) { // 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; } else { printf("Digital Signature Info of: %s\n", fileName->c_str()); } } else { printf("File '%s' does not contain any signatures\n", fileName->c_str()); return 2; } for (unsigned int i = 0; i < sigCount; i++) { FormFieldSignature *ffs = signatures.at(i); printf("Signature #%u:\n", i + 1); const GooString *goo = ffs->getCreateWidget()->getField()->getFullyQualifiedName(); if (goo) { const std::string name = TextStringToUTF8(goo->toStr()); printf(" - Signature Field Name: %s\n", name.c_str()); } if (ffs->getSignatureType() == 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().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 HashAlgorithm::Md2: printf("MD2\n"); break; case HashAlgorithm::Md5: printf("MD5\n"); break; case HashAlgorithm::Sha1: printf("SHA1\n"); break; case HashAlgorithm::Sha256: printf("SHA-256\n"); break; case HashAlgorithm::Sha384: printf("SHA-384\n"); break; case HashAlgorithm::Sha512: printf("SHA-512\n"); break; case HashAlgorithm::Sha224: printf("SHA-224\n"); break; default: printf("unknown\n"); } printf(" - Signature Type: "); switch (ffs->getSignatureType()) { case adbe_pkcs7_sha1: printf("adbe.pkcs7.sha1\n"); break; case adbe_pkcs7_detached: printf("adbe.pkcs7.detached\n"); break; case ETSI_CAdES_detached: printf("ETSI.CAdES.detached\n"); break; default: printf("unknown\n"); } const std::vector ranges = ffs->getSignedRangeBounds(); if (ranges.size() == 4) { printf(" - Signed Ranges: [%lld - %lld], [%lld - %lld]\n", ranges[0], ranges[1], ranges[2], ranges[3]); Goffset checked_file_size; const std::optional signature = signatures.at(i)->getCheckedSignature(&checked_file_size); if (signature && checked_file_size == ranges[3]) { printf(" - Total document signed\n"); } else { printf(" - Not total document signed\n"); } } printf(" - Signature Validation: %s\n", getReadableSigState(sig_info->getSignatureValStatus())); gfree(time_str); if (sig_info->getSignatureValStatus() != SIGNATURE_VALID || dontVerifyCert) { continue; } printf(" - Certificate Validation: %s\n", getReadableCertState(sig_info->getCertificateValStatus())); } return 0; } poppler-24.02.0/utils/pdftocairo-win32.cc000066400000000000000000000460671455701731300201240ustar00rootroot00000000000000//======================================================================== // // This file is licensed under the GPLv2 or later // // Copyright (C) 2014 Rodrigo Rivas Costa // Copyright (C) 2014, 2017 Adrian Johnson // Copyright (C) 2017, 2018, 2022 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 #ifdef CAIRO_HAS_WIN32_SURFACE # include # include "parseargs.h" # include "pdftocairo-win32.h" # include "Win32Console.h" # include # include # include # include # include static HDC hdc; static HGLOBAL hDevmode = nullptr; static HGLOBAL hDevnames = nullptr; static DEVMODEA *devmode; static char *printerName; struct Win32Option { const char *name; int value; }; static const Win32Option win32PaperSource[] = { { "upper", DMBIN_UPPER }, { "onlyone", DMBIN_ONLYONE }, { "lower", DMBIN_LOWER }, { "middle", DMBIN_MIDDLE }, { "manual", DMBIN_MANUAL }, { "envelope", DMBIN_ENVELOPE }, { "envmanual", DMBIN_ENVMANUAL }, { "auto", DMBIN_AUTO }, { "tractor", DMBIN_TRACTOR }, { "smallfmt", DMBIN_SMALLFMT }, { "largefmt", DMBIN_LARGEFMT }, { "largecapacity", DMBIN_LARGECAPACITY }, { "formsource", DMBIN_FORMSOURCE }, { nullptr, 0 } }; static void parseSource(GooString *source) { const Win32Option *option = win32PaperSource; while (option->name) { if (source->cmp(option->name) == 0) { devmode->dmDefaultSource = option->value; devmode->dmFields |= DM_DEFAULTSOURCE; return; } option++; } fprintf(stderr, "Warning: Unknown paper source \"%s\"\n", source->c_str()); } static const Win32Option win32DuplexMode[] = { { "off", DMDUP_SIMPLEX }, { "short", DMDUP_HORIZONTAL }, { "long", DMDUP_VERTICAL }, { nullptr, 0 } }; static void parseDuplex(GooString *mode) { const Win32Option *option = win32DuplexMode; while (option->name) { if (mode->cmp(option->name) == 0) { devmode->dmDuplex = option->value; devmode->dmFields |= DM_DUPLEX; return; } option++; } fprintf(stderr, "Warning: Unknown duplex mode \"%s\"\n", mode->c_str()); } static void fillCommonPrinterOptions(bool duplex) { if (duplex) { devmode->dmDuplex = DMDUP_HORIZONTAL; devmode->dmFields |= DM_DUPLEX; } } static void fillPagePrinterOptions(double w, double h) { w *= 254.0 / 72.0; // units are 0.1mm h *= 254.0 / 72.0; if (w > h) { devmode->dmOrientation = DMORIENT_LANDSCAPE; devmode->dmPaperWidth = static_cast(h); devmode->dmPaperLength = static_cast(w); } else { devmode->dmOrientation = DMORIENT_PORTRAIT; devmode->dmPaperWidth = static_cast(w); devmode->dmPaperLength = static_cast(h); } devmode->dmPaperSize = 0; devmode->dmFields |= DM_ORIENTATION | DM_PAPERWIDTH | DM_PAPERLENGTH; } static void fillPrinterOptions(bool duplex, GooString *printOpt) { // printOpt format is: =,=,... const char *nextOpt = printOpt->c_str(); while (nextOpt && *nextOpt) { const char *comma = strchr(nextOpt, ','); GooString opt; if (comma) { opt.Set(nextOpt, static_cast(comma - nextOpt)); nextOpt = comma + 1; } else { opt.Set(nextOpt); nextOpt = NULL; } // here opt is "= " const char *equal = strchr(opt.c_str(), '='); if (!equal) { fprintf(stderr, "Warning: unknown printer option \"%s\"\n", opt.c_str()); continue; } const int iequal = static_cast(equal - opt.c_str()); GooString value(&opt, iequal + 1, opt.getLength() - iequal - 1); opt.del(iequal, opt.getLength() - iequal); // here opt is "" and value is "" if (opt.cmp("source") == 0) { parseSource(&value); } else if (opt.cmp("duplex") == 0) { if (duplex) fprintf(stderr, "Warning: duplex mode is specified both as standalone and printer options\n"); else parseDuplex(&value); } else { fprintf(stderr, "Warning: unknown printer option \"%s\"\n", opt.c_str()); } } } static void getLocalPos(HWND wind, HWND dlg, RECT *rect) { GetClientRect(wind, rect); MapWindowPoints(wind, dlg, (LPPOINT)rect, 2); } static HWND createGroupBox(HWND parent, HINSTANCE hInstance, HMENU id, const char *label, RECT *rect) { HWND hwnd = CreateWindowA(WC_BUTTONA, label, WS_CHILD | WS_VISIBLE | BS_GROUPBOX, rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top, parent, id, hInstance, NULL); HFONT hFont = (HFONT)SendMessage(parent, WM_GETFONT, (WPARAM)0, (LPARAM)0); if (hFont) SendMessage(hwnd, WM_SETFONT, (WPARAM)hFont, (LPARAM)0); return hwnd; } static HWND createCheckBox(HWND parent, HINSTANCE hInstance, HMENU id, const char *label, RECT *rect) { HWND hwnd = CreateWindowA(WC_BUTTONA, label, WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX | WS_TABSTOP, rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top, parent, id, hInstance, NULL); HFONT hFont = (HFONT)SendMessage(parent, WM_GETFONT, (WPARAM)0, (LPARAM)0); if (hFont) SendMessage(hwnd, WM_SETFONT, (WPARAM)hFont, (LPARAM)0); return hwnd; } static HWND createStaticText(HWND parent, HINSTANCE hinstance, HMENU id, const char *text, RECT *rect) { HWND hwnd = CreateWindowA(WC_STATICA, text, WS_CHILD | WS_VISIBLE | SS_LEFT, rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top, parent, id, hinstance, NULL); HFONT hFont = (HFONT)SendMessage(parent, WM_GETFONT, (WPARAM)0, (LPARAM)0); if (hFont) SendMessage(hwnd, WM_SETFONT, (WPARAM)hFont, (LPARAM)0); return hwnd; } static HWND createPageScaleComboBox(HWND parent, HINSTANCE hinstance, HMENU id, RECT *rect) { HWND hwnd = CreateWindowA(WC_COMBOBOX, "", WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP | CBS_DROPDOWNLIST, rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top, parent, id, hinstance, NULL); HFONT hFont = (HFONT)SendMessage(parent, WM_GETFONT, (WPARAM)0, (LPARAM)0); if (hFont) SendMessage(hwnd, WM_SETFONT, (WPARAM)hFont, (LPARAM)0); ComboBox_AddString(hwnd, "None"); ComboBox_AddString(hwnd, "Shrink to Printable Area"); ComboBox_AddString(hwnd, "Fit to Printable Area"); return hwnd; } enum PageScale { NONE = 0, SHRINK = 1, FIT = 2 }; // used to set/get option values in printDialogHookProc static PageScale pageScale; static bool centerPage; static bool useOrigPageSize; // PrintDlg callback to customize the print dialog with additional controls. static UINT_PTR CALLBACK printDialogHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) { if (uiMsg == WM_INITDIALOG) { // Get the extant controls. See dlgs.h and prnsetup.dlg for the PrintDlg control ids. HWND printerGroupWind = GetDlgItem(hdlg, grp4); HWND printerComboWind = GetDlgItem(hdlg, cmb4); HWND nameLabelWind = GetDlgItem(hdlg, stc6); HWND statusLabelWind = GetDlgItem(hdlg, stc8); HWND printRangeGroupWind = GetDlgItem(hdlg, grp1); HWND radio1Wind = GetDlgItem(hdlg, rad1); HWND radio2Wind = GetDlgItem(hdlg, rad3); HWND copiesGroupWind = GetDlgItem(hdlg, grp2); HWND okWind = GetDlgItem(hdlg, IDOK); HWND cancelWind = GetDlgItem(hdlg, IDCANCEL); if (!printerGroupWind || !printerComboWind || !nameLabelWind || !statusLabelWind || !printRangeGroupWind || !radio1Wind || !radio2Wind || !copiesGroupWind || !okWind || !cancelWind) return 0; // Get the size and position of the above controls relative to the // print dialog window RECT printerGroupRect; RECT printerComboRect; RECT nameLabelRect; RECT statusLabelRect; RECT printRangeGroupRect; RECT radio1Rect, radio2Rect; RECT copiesGroupRect; RECT okRect, cancelRect; getLocalPos(printerGroupWind, hdlg, &printerGroupRect); getLocalPos(printerComboWind, hdlg, &printerComboRect); getLocalPos(nameLabelWind, hdlg, &nameLabelRect); getLocalPos(statusLabelWind, hdlg, &statusLabelRect); getLocalPos(printRangeGroupWind, hdlg, &printRangeGroupRect); getLocalPos(radio1Wind, hdlg, &radio1Rect); getLocalPos(radio2Wind, hdlg, &radio2Rect); getLocalPos(copiesGroupWind, hdlg, &copiesGroupRect); getLocalPos(okWind, hdlg, &okRect); getLocalPos(cancelWind, hdlg, &cancelRect); // Calc space required for new group int interGroupSpace = printRangeGroupRect.top - printerGroupRect.bottom; int groupHeight = statusLabelRect.top - printerGroupRect.top + printRangeGroupRect.bottom - radio1Rect.bottom; // Increase dialog size RECT dlgRect; GetWindowRect(hdlg, &dlgRect); SetWindowPos(hdlg, nullptr, dlgRect.left, dlgRect.top, dlgRect.right - dlgRect.left, dlgRect.bottom - dlgRect.top + interGroupSpace + groupHeight, SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER); // Add new group and controls HINSTANCE hinstance = (HINSTANCE)GetWindowLongPtr(hdlg, GWLP_HINSTANCE); RECT pdfGroupBoxRect; pdfGroupBoxRect.left = printRangeGroupRect.left; pdfGroupBoxRect.right = copiesGroupRect.right; pdfGroupBoxRect.top = printRangeGroupRect.bottom + interGroupSpace; pdfGroupBoxRect.bottom = pdfGroupBoxRect.top + groupHeight; createGroupBox(hdlg, hinstance, (HMENU)grp3, "PDF Print Options", &pdfGroupBoxRect); RECT textRect; textRect.left = nameLabelRect.left; textRect.right = static_cast(nameLabelRect.left + 1.8 * (printerComboRect.left - nameLabelRect.left)); textRect.top = pdfGroupBoxRect.top + nameLabelRect.top - printerGroupRect.top; textRect.bottom = textRect.top + nameLabelRect.bottom - nameLabelRect.top; createStaticText(hdlg, hinstance, (HMENU)stc1, "Page Scaling:", &textRect); RECT comboBoxRect; comboBoxRect.left = textRect.right; comboBoxRect.right = comboBoxRect.left + printerComboRect.right - printerComboRect.left; ; comboBoxRect.top = pdfGroupBoxRect.top + printerComboRect.top - printerGroupRect.top; comboBoxRect.bottom = textRect.top + 4 * (printerComboRect.bottom - printerComboRect.top); HWND comboBoxWind = createPageScaleComboBox(hdlg, hinstance, (HMENU)cmb1, &comboBoxRect); RECT checkBox1Rect; checkBox1Rect.left = radio1Rect.left; checkBox1Rect.right = pdfGroupBoxRect.right - 10; checkBox1Rect.top = pdfGroupBoxRect.top + statusLabelRect.top - printerGroupRect.top; checkBox1Rect.bottom = checkBox1Rect.top + radio1Rect.bottom - radio1Rect.top; HWND checkBox1Wind = createCheckBox(hdlg, hinstance, (HMENU)chx3, "Center", &checkBox1Rect); RECT checkBox2Rect; checkBox2Rect.left = radio1Rect.left; checkBox2Rect.right = pdfGroupBoxRect.right - 10; checkBox2Rect.top = checkBox1Rect.top + radio2Rect.top - radio1Rect.top; checkBox2Rect.bottom = checkBox2Rect.top + radio1Rect.bottom - radio1Rect.top; HWND checkBox2Wind = createCheckBox(hdlg, hinstance, (HMENU)chx4, "Select page size using document page size", &checkBox2Rect); // Move OK and Cancel buttons down ensuring they are last in the Z order // so that the tab order is correct. SetWindowPos(okWind, HWND_BOTTOM, okRect.left, okRect.top + interGroupSpace + groupHeight, 0, 0, SWP_NOSIZE); // keep current size SetWindowPos(cancelWind, HWND_BOTTOM, cancelRect.left, cancelRect.top + interGroupSpace + groupHeight, 0, 0, SWP_NOSIZE); // keep current size // Initialize control values ComboBox_SetCurSel(comboBoxWind, pageScale); Button_SetCheck(checkBox1Wind, centerPage ? BST_CHECKED : BST_UNCHECKED); Button_SetCheck(checkBox2Wind, useOrigPageSize ? BST_CHECKED : BST_UNCHECKED); } else if (uiMsg == WM_COMMAND) { // Save settings UINT id = LOWORD(wParam); if (id == cmb1) pageScale = (PageScale)ComboBox_GetCurSel(GetDlgItem(hdlg, cmb1)); if (id == chx3) centerPage = IsDlgButtonChecked(hdlg, chx3); if (id == chx4) useOrigPageSize = IsDlgButtonChecked(hdlg, chx4); } return 0; } void win32SetupPrinter(GooString *printer, GooString *printOpt, bool duplex, bool setupdlg) { if (printer->c_str()[0] == 0) { DWORD size = 0; GetDefaultPrinterA(nullptr, &size); printerName = (char *)gmalloc(size); GetDefaultPrinterA(printerName, &size); } else { printerName = copyString(printer->c_str(), printer->getLength()); } // Query the size of the DEVMODE struct LONG szProp = DocumentPropertiesA(nullptr, nullptr, printerName, nullptr, nullptr, 0); if (szProp < 0) { fprintf(stderr, "Error: Printer \"%s\" not found\n", printerName); exit(99); } devmode = (DEVMODEA *)gmalloc(szProp); memset(devmode, 0, szProp); devmode->dmSize = sizeof(DEVMODEA); devmode->dmSpecVersion = DM_SPECVERSION; // Load the current default configuration for the printer into devmode if (DocumentPropertiesA(nullptr, nullptr, printerName, devmode, devmode, DM_OUT_BUFFER) < 0) { fprintf(stderr, "Error: Printer \"%s\" not found\n", printerName); exit(99); } // Update devmode with selected print options fillCommonPrinterOptions(duplex); fillPrinterOptions(duplex, printOpt); // Call DocumentProperties again so the driver can update its private data // with the modified print options. This will also display the printer // properties dialog if setupdlg is true. int ret; DWORD mode = DM_IN_BUFFER | DM_OUT_BUFFER; if (setupdlg) mode |= DM_IN_PROMPT; ret = DocumentPropertiesA(nullptr, nullptr, printerName, devmode, devmode, mode); if (ret < 0) { fprintf(stderr, "Error: Printer \"%s\" not found\n", printerName); exit(99); } if (setupdlg && ret == IDCANCEL) exit(0); hdc = CreateDCA(nullptr, printerName, nullptr, devmode); if (!hdc) { fprintf(stderr, "Error: Printer \"%s\" not found\n", printerName); exit(99); } } void win32ShowPrintDialog(bool *expand, bool *noShrink, bool *noCenter, bool *usePDFPageSize, bool *allPages, int *firstPage, int *lastPage, int maxPages) { PRINTDLG pd; memset(&pd, 0, sizeof(PRINTDLG)); pd.lStructSize = sizeof(PRINTDLG); pd.Flags = PD_NOSELECTION | PD_ENABLEPRINTHOOK | PD_USEDEVMODECOPIESANDCOLLATE | PD_RETURNDC; if (*allPages) { pd.nFromPage = 1; pd.nToPage = maxPages; } else { pd.Flags |= PD_PAGENUMS; pd.nFromPage = *firstPage; pd.nToPage = *lastPage; } pd.nCopies = 1; pd.nMinPage = 1; pd.nMaxPage = maxPages; pd.lpfnPrintHook = printDialogHookProc; if (!*expand && *noShrink) pageScale = NONE; else if (!*expand && !*noShrink) pageScale = SHRINK; else pageScale = FIT; centerPage = !*noCenter; useOrigPageSize = *usePDFPageSize; if (PrintDlgA(&pd)) { // Ok hDevnames = pd.hDevNames; DEVNAMES *devnames = (DEVNAMES *)GlobalLock(hDevnames); printerName = (char *)devnames + devnames->wDeviceOffset; hDevmode = pd.hDevMode; devmode = (DEVMODEA *)GlobalLock(hDevmode); hdc = pd.hDC; if (pd.Flags & PD_PAGENUMS) { *allPages = false; *firstPage = pd.nFromPage; *lastPage = pd.nToPage; } else { *allPages = true; } if (pageScale == NONE) { *expand = false; *noShrink = true; } else if (pageScale == SHRINK) { *expand = false; *noShrink = false; } else { *expand = true; *noShrink = false; } *noCenter = !centerPage; *usePDFPageSize = useOrigPageSize; } else { // Cancel exit(0); } } cairo_surface_t *win32BeginDocument(GooString *inputFileName, GooString *outputFileName) { DOCINFOA docinfo; memset(&docinfo, 0, sizeof(docinfo)); docinfo.cbSize = sizeof(docinfo); if (inputFileName->cmp("fd://0") == 0) docinfo.lpszDocName = "pdftocairo "; else docinfo.lpszDocName = inputFileName->c_str(); if (outputFileName) docinfo.lpszOutput = outputFileName->c_str(); if (StartDocA(hdc, &docinfo) <= 0) { fprintf(stderr, "Error: StartDoc failed\n"); exit(99); } return cairo_win32_printing_surface_create(hdc); } void win32BeginPage(double *w, double *h, bool changePageSize, bool useFullPage) { if (changePageSize) fillPagePrinterOptions(*w, *h); if (DocumentPropertiesA(nullptr, nullptr, printerName, devmode, devmode, DM_IN_BUFFER | DM_OUT_BUFFER) < 0) { fprintf(stderr, "Error: Printer \"%s\" not found\n", printerName); exit(99); } ResetDCA(hdc, devmode); // Get actual paper size or if useFullPage is false the printable area. // Transform the hdc scale to points to be consistent with other cairo backends int x_dpi = GetDeviceCaps(hdc, LOGPIXELSX); int y_dpi = GetDeviceCaps(hdc, LOGPIXELSY); int x_off = GetDeviceCaps(hdc, PHYSICALOFFSETX); int y_off = GetDeviceCaps(hdc, PHYSICALOFFSETY); if (useFullPage) { *w = GetDeviceCaps(hdc, PHYSICALWIDTH) * 72.0 / x_dpi; *h = GetDeviceCaps(hdc, PHYSICALHEIGHT) * 72.0 / y_dpi; } else { *w = GetDeviceCaps(hdc, HORZRES) * 72.0 / x_dpi; *h = GetDeviceCaps(hdc, VERTRES) * 72.0 / y_dpi; } XFORM xform; xform.eM11 = x_dpi / 72.0f; xform.eM12 = 0; xform.eM21 = 0; xform.eM22 = y_dpi / 72.0f; if (useFullPage) { xform.eDx = static_cast(-x_off); xform.eDy = static_cast(-y_off); } else { xform.eDx = 0; xform.eDy = 0; } SetGraphicsMode(hdc, GM_ADVANCED); SetWorldTransform(hdc, &xform); StartPage(hdc); } void win32EndPage(GooString *imageFileName) { EndPage(hdc); } void win32EndDocument() { EndDoc(hdc); DeleteDC(hdc); if (hDevmode) { GlobalUnlock(hDevmode); GlobalFree(hDevmode); } else { gfree(devmode); } if (hDevnames) { GlobalUnlock(hDevnames); GlobalFree(hDevnames); } else { gfree(printerName); } } #endif // CAIRO_HAS_WIN32_SURFACE poppler-24.02.0/utils/pdftocairo-win32.h000066400000000000000000000022251455701731300177520ustar00rootroot00000000000000//======================================================================== // // This file is licensed under the GPLv2 or later // // Copyright (C) 2014 Rodrigo Rivas Costa // Copyright (C) 2014 Adrian Johnson // // 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 #include #include "goo/gmem.h" #include "goo/GooString.h" #ifdef CAIRO_HAS_WIN32_SURFACE # include void win32SetupPrinter(GooString *printer, GooString *printOpt, bool duplex, bool setupdlg); void win32ShowPrintDialog(bool *expand, bool *noShrink, bool *noCenter, bool *usePDFPageSize, bool *allPages, int *firstPage, int *lastPage, int maxPages); cairo_surface_t *win32BeginDocument(GooString *inputFileName, GooString *outputFileName); void win32BeginPage(double *w, double *h, bool changePageSize, bool useFullPage); void win32EndPage(GooString *imageFileName); void win32EndDocument(); #endif // CAIRO_HAS_WIN32_SURFACE poppler-24.02.0/utils/pdftocairo.1000066400000000000000000000266551455701731300167400ustar00rootroot00000000000000.TH pdftocairo 1 .SH NAME pdftocairo \- Portable Document Format (PDF) to PNG/JPEG/TIFF/PDF/PS/EPS/SVG using cairo .SH SYNOPSIS .B pdftocairo [options] .IR PDF-file .RI [ output-file ] .SH DESCRIPTION .B pdftocairo converts Portable Document Format (PDF) files, using the cairo output device of the poppler PDF library, to any of the following output formats: .IP \(bu Portable Network Graphics (PNG) .IP \(bu JPEG Interchange Format (JPEG) .IP \(bu Tagged Image File Format (TIFF) .IP \(bu Portable Document Format (PDF) .IP \(bu PostScript (PS) .IP \(bu Encapsulated PostScript (EPS) .IP \(bu Scalable Vector Graphics (SVG) .IP \(bu Windows Printer .PP .B pdftocairo reads the PDF file, .IR PDF-file , and writes to .IR output-file . The image formats (PNG, JPEG, and TIFF) generate one file per page with the page number and file type appended to .IR output-file . When \-singlefile is used with the image formats, the file type is appended to .IR output-file . When the output format is a vector format (PDF, PS, EPS, and SVG), .IR output-file is the full filename. If the .IR PDF-file is \*(lq\-\*(rq , the PDF is read from stdin. If the .IR output-file is \*(lq\-\*(rq , the output file will be written to stdout. Using stdout is not valid with image formats unless \-singlefile is used. If .IR output-file is not used, the output filename will be derived from the .IR PDF-file filename. .PP Not all options are valid with all output formats. One (and only one) of the output format options (\-png, \-jpeg, \-tiff, \-pdf, \-print, \-ps, \-eps, or \-svg) must be used. .PP The resolution options (\-r, \-rx, \-ry) set the resolution of the image output formats. The image dimensions will depend on the PDF page size and the resolution. For the vector outputs, regions of the page that can not be represented natively in the output format (eg translucency in PS) will be rasterized at the resolution specified by the resolution options. .PP The \-scale-to options may be used to set a fixed image size. The image resolution will vary with the page size. .PP The cropping options (\-x, \-y, \-W, and \-H) use units of pixels with the image formats and PostScript points (1/72 inch) with the vector formats. When cropping is used with vector output the cropped region is centered unless \-nocenter is used in which case the cropped region is at the top left (SVG) or bottom left (PDF, PS, EPS). .PP .SH OPTIONS .TP .BI \-png Generates a PNG file(s) .TP .BI \-jpeg Generates a JPEG file(s). See also \-jpegopt. .TP .BI \-tiff Generates a TIFF file(s) .TP .BI \-pdf Generates a PDF file .TP .BI \-ps Generate a PS file .TP .BI \-eps Generate an EPS file. An EPS file contains a single image, so if you use this option with a multi-page PDF file, you must use \-f and \-l to specify a single page. The page size options (\-origpagesizes, \-paper, \-paperw, \-paperh) can not be used with this option. .TP .BI \-svg Generate a SVG (Scalable Vector Graphics) file .TP .BI \-print (Windows only) Prints to a system printer. See also \-printer and \-printeropt. If an output file is not specified, the output will be sent to the printer. The output file '-' can not be used with this option. .TP .BI \-printdlg (Windows only) Prints to a system printer. Displays the print dialog to allow the print options to be modified before printing. .TP .BI \-f " number" Specifies the first page to convert. .TP .BI \-l " number" Specifies the last page to convert. .TP .B \-o Generates only the odd numbered pages. .TP .B \-e Generates only the even numbered pages. .TP .BI \-singlefile Writes only the first page and does not add digits. .TP .BI \-r " number" Specifies the X and Y resolution, in pixels per inch of image files (or rasterized regions in vector output). The default is 150 PPI. .TP .BI \-rx " number" Specifies the X resolution, in pixels per inch of image files (or rasterized regions in vector output). The default is 150 PPI. .TP .BI \-ry " number" Specifies the Y resolution, in pixels per inch of image files (or rasterized regions in vector output). The default is 150 PPI. .TP .BI \-scale-to " number" Scales the long side of each page (width for landscape pages, height for portrait pages) to fit in scale-to pixels. The size of the short side will be determined by the aspect ratio of the page (PNG/JPEG/TIFF only). .TP .BI \-scale-to-x " number" Scales each page horizontally to fit in scale-to-x pixels. If scale-to-y is set to -1, the vertical size will determined by the aspect ratio of the page (PNG/JPEG/TIFF only). .TP .BI \-scale-to-y " number" Scales each page vertically to fit in scale-to-y pixels. If scale-to-x is set to -1, the horizontal size will determined by the aspect ratio of the page (PNG/JPEG/TIFF only). .TP .BI \-x " number" Specifies the x-coordinate of the crop area top left corner in pixels (image output) or points (vector output) .TP .BI \-y " number" Specifies the y-coordinate of the crop area top left corner in pixels (image output) or points (vector output) .TP .BI \-W " number" Specifies the width of crop area in pixels (image output) or points (vector output) (default is 0) .TP .BI \-H " number" Specifies the height of crop area in pixels (image output) or points (vector output) (default is 0) .TP .BI \-sz " number" Specifies the size of crop square in pixels (image output) or points (vector output) (sets \-W and \-H) .TP .B \-cropbox Uses the crop box rather than media box when generating the files (PNG/JPEG/TIFF only) .TP .B \-mono Generate a monochrome file (PNG and TIFF only). .TP .B \-gray Generate a grayscale file (PNG, JPEG, and TIFF only). .TP .B \-antialias Set the cairo antialias option used for text and drawing in image files (or rasterized regions in vector output). The options are: .RS .TP .B default Use the default antialiasing for the target device. This is the default setting if \-antialias is not used. .TP .B none Antialiasing is disabled. .TP .B gray Perform single-color antialiasing using shades of gray. .TP .B subpixel Perform antialiasing by taking advantage of the order of subpixel elements on devices such as LCD. .TP .B fast Hint that the backend should perform some antialiasing but prefer speed over quality. .TP .B good The backend should balance quality against performance. .TP .B best Hint that the backend should render at the highest quality, sacrificing speed if necessary. .RE .TP .B \-transp Use a transparent page color instead of white (PNG and TIFF only). .TP .BI \-icc " icc-file" Use the specified ICC file as the output profile (PNG only). The profile will be embedded in the PNG file. .TP .BI \-jpegopt " jpeg-options" When used with \-jpeg, takes a list of options to control the jpeg compression. See .B JPEG OPTIONS for the available options. .TP .B \-level2 Generate Level 2 PostScript (PS only). .TP .B \-level3 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 .BI \-paper " size" Set the paper size to one of "letter", "legal", "A4", or "A3" (PS,PDF,SVG only). This can also be set to "match", which will set the paper size of each page to match the size specified in the PDF file. If none the \-paper, \-paperw, or \-paperh options are specified the default is to match the paper size. .TP .BI \-paperw " size" Set the paper width, in points (PS,PDF,SVG only). .TP .BI \-paperh " size" Set the paper height, in points (PS,PDF,SVG only). .TP .B \-nocrop By default, printing output is cropped to the CropBox specified in the PDF file. This option disables cropping (PS,PDF,SVG only). .TP .B \-expand Expand PDF pages smaller than the paper to fill the paper (PS,PDF,SVG only). By default, these pages are not scaled. .TP .B \-noshrink Don't scale PDF pages which are larger than the paper (PS,PDF,SVG only). By default, pages larger than the paper are shrunk to fit. .TP .B \-nocenter By default, PDF pages smaller than the paper (after any scaling) are centered on the paper. This option causes them to be aligned to the lower-left corner of the paper instead (PS,PDF,SVG only). .TP .B \-duplex Adds the %%IncludeFeature: *Duplex DuplexNoTumble DSC comment to the PostScript file (PS only). This tells the print manager to enable duplexing. .TP .BI \-printer " printer-name" (Windows only). When used with \-print, specifies the name of the printer to be used, instead of the system default. .TP .BI \-printopt " printer-options" (Windows only). When used with \-print, takes a list of options to be used to configure the printer. See .B WINDOWS PRINTER OPTIONS for the available options. .TP .BI \-setupdlg (Windows only). When used with \-print, the printer properties dialog is displayed allowing the print settings to be modified before printing. The paper size selected in the print properties dialog will be used except when -origpagesizes is specified. .TP .BI \-opw " password" Specify the owner password for the PDF file. Providing this will bypass all security restrictions. .TP .BI \-upw " password" Specify the user password for the PDF file. .TP .B \-q Don't print any messages or errors. .TP .B \-v Print copyright and version information. .TP .B \-h Print usage information. .RB ( \-help and .B \-\-help are equivalent.) .SH EXIT CODES The poppler tools use the following exit codes: .TP 0 No error. .TP 1 Error opening a PDF file. .TP 2 Error opening an output file. .TP 3 Error related to PDF permissions. .TP 4 Error related to ICC profile. .TP 99 Other error. .SH JPEG OPTIONS When JPEG output is specified, the \-jpegopt option can be used to control the JPEG compression parameters. It takes a string of the form "=[,=]". Currently the available options are: .TP .BI quality Selects the JPEG quality value. The value must be an integer between 0 and 100. .TP .BI progressive Select progressive JPEG output. The possible values are "y", "n", indicating progressive (yes) or non-progressive (no), respectively. .TP .BI optimize Sets whether to compute optimal Huffman coding tables for the JPEG output, which will create smaller files but make an extra pass over the data. The value must be "y" or "n", with "y" performing optimization, otherwise the default Huffman tables are used. .SH WINDOWS PRINTER OPTIONS In Windows, you can use the \-print option to print directly to a system printer. Additionally, you can use the \-printopt option to configure the printer. It takes a string of the form "=[,=]". Currently the available options are: .TP .BI source Selects the source paper tray to be used (bin). The possible values are "upper", "onlyone", "lower", "middle", "manual", "envelope", "envmanual", "auto", "tractor", "smallfmt", "largefmt", "largecapacity", "formsource", or a numeric value to choose a driver specific source. .TP .BI duplex Sets the duplex mode of the printer. The possible values are "off", "short" or "long", indicating no duplexing, short-edge binding, or long-edge binding, respectively. General option \-duplex is a synonym of "duplex=long". If both options are specified, \-printopt has priority. .SH AUTHOR The pdftocairo software and documentation are copyright 1996-2004 Glyph & Cog, LLC and copyright 2005-2011 The Poppler Developers. .SH "SEE ALSO" .BR pdfdetach (1), .BR pdffonts (1), .BR pdfimages (1), .BR pdfinfo (1), .BR pdftohtml (1), .BR pdftoppm (1), .BR pdftops (1), .BR pdftotext (1) .BR pdfseparate (1), .BR pdfsig (1), .BR pdfunite (1) poppler-24.02.0/utils/pdftocairo.cc000066400000000000000000001242221455701731300171520ustar00rootroot00000000000000//======================================================================== // // pdftocairo.cc // // Copyright 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) 2007 Ilmari Heikkinen // Copyright (C) 2008 Richard Airlie // Copyright (C) 2009 Michael K. Johnson // 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, 2023 Adrian Johnson // Copyright (C) 2010, 2014 Hib Eris // Copyright (C) 2010 Jonathan Liu // Copyright (C) 2010 William Bader // Copyright (C) 2011 Thomas Freitag // Copyright (C) 2011, 2015 Carlos Garcia Campos // Copyright (C) 2012 Koji Otani // Copyright (C) 2013 Lu Wang // Copyright (C) 2013, 2017 Suzuki Toshiya // Copyright (C) 2014 Rodrigo Rivas Costa // Copyright (C) 2016 Jason Crain // Copyright (C) 2018 Martin Packman // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019, 2020 Oliver Sander // Copyright (C) 2019 Kris Jurka // Copyright (C) 2020, 2021 Oliver Sander // Copyright (C) 2020 Philipp Knechtges // Copyright (C) 2020 Salvo Miosi // 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 // //======================================================================== #include "config.h" #include #include #include #include #include #include #if defined(_WIN32) || defined(__CYGWIN__) # include // for _setmode #endif #include "parseargs.h" #include "goo/gmem.h" #include "goo/GooString.h" #include "goo/ImgWriter.h" #include "goo/JpegWriter.h" #include "goo/PNGWriter.h" #include "goo/TiffWriter.h" #include "GlobalParams.h" #include "Object.h" #include "PDFDoc.h" #include "PDFDocFactory.h" #include "CairoOutputDev.h" #include "Win32Console.h" #include "numberofcharacters.h" #ifdef USE_CMS # include #endif #include #ifdef CAIRO_HAS_PS_SURFACE # include #endif #ifdef CAIRO_HAS_PDF_SURFACE # include #endif #ifdef CAIRO_HAS_SVG_SURFACE # include #endif #include "pdftocairo-win32.h" static bool png = false; static bool jpeg = false; static bool ps = false; static bool eps = false; static bool pdf = false; static bool printToWin32 = false; static bool printdlg = false; static bool svg = false; static bool tiff = false; static int firstPage = 1; static int lastPage = 0; static bool printOnlyOdd = false; static bool printOnlyEven = false; static bool singleFile = false; static double resolution = 0.0; static double x_resolution = 150.0; static double y_resolution = 150.0; static int scaleTo = 0; static int x_scaleTo = 0; static int y_scaleTo = 0; static int crop_x = 0; static int crop_y = 0; static int crop_w = 0; static int crop_h = 0; static int sz = 0; static bool useCropBox = false; static bool mono = false; static bool gray = false; static bool transp = false; static GooString antialias; static GooString icc; static bool level2 = false; static bool level3 = false; static bool origPageSizes = false; static char paperSize[15] = ""; static int paperWidth = -1; static int paperHeight = -1; static bool noCrop = false; static bool expand = false; 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] = ""; static bool quiet = false; static bool printVersion = false; static bool printHelp = false; static GooString jpegOpt; static int jpegQuality = -1; static bool jpegProgressive = false; static bool jpegOptimize = false; static GooString printer; static GooString printOpt; #ifdef CAIRO_HAS_WIN32_SURFACE static bool setupdlg = false; #endif static const ArgDesc argDesc[] = { #ifdef ENABLE_LIBPNG { "-png", argFlag, &png, 0, "generate a PNG file" }, #endif #ifdef ENABLE_LIBJPEG { "-jpeg", argFlag, &jpeg, 0, "generate a JPEG file" }, { "-jpegopt", argGooString, &jpegOpt, 0, "jpeg options, with format =[,=]*" }, #endif #ifdef ENABLE_LIBTIFF { "-tiff", argFlag, &tiff, 0, "generate a TIFF file" }, { "-tiffcompression", argString, tiffCompressionStr, sizeof(tiffCompressionStr), "set TIFF compression: none, packbits, jpeg, lzw, deflate" }, #endif #ifdef CAIRO_HAS_PS_SURFACE { "-ps", argFlag, &ps, 0, "generate PostScript file" }, { "-eps", argFlag, &eps, 0, "generate Encapsulated PostScript (EPS)" }, #endif #ifdef CAIRO_HAS_PDF_SURFACE { "-pdf", argFlag, &pdf, 0, "generate a PDF file" }, #endif #ifdef CAIRO_HAS_SVG_SURFACE { "-svg", argFlag, &svg, 0, "generate a Scalable Vector Graphics (SVG) file" }, #endif #ifdef CAIRO_HAS_WIN32_SURFACE { "-print", argFlag, &printToWin32, 0, "print to a Windows printer" }, { "-printdlg", argFlag, &printdlg, 0, "show Windows print dialog and print" }, { "-printer", argGooString, &printer, 0, "printer name or use default if this option is not specified" }, { "-printopt", argGooString, &printOpt, 0, "printer options, with format =[,=]*" }, { "-setupdlg", argFlag, &setupdlg, 0, "show printer setup dialog before printing" }, #endif { "-f", argInt, &firstPage, 0, "first page to print" }, { "-l", argInt, &lastPage, 0, "last page to print" }, { "-o", argFlag, &printOnlyOdd, 0, "print only odd pages" }, { "-e", argFlag, &printOnlyEven, 0, "print only even pages" }, { "-singlefile", argFlag, &singleFile, 0, "write only the first page and do not add digits" }, { "-r", argFP, &resolution, 0, "resolution, in PPI (default is 150)" }, { "-rx", argFP, &x_resolution, 0, "X resolution, in PPI (default is 150)" }, { "-ry", argFP, &y_resolution, 0, "Y resolution, in PPI (default is 150)" }, { "-scale-to", argInt, &scaleTo, 0, "scales each page to fit within scale-to*scale-to pixel box" }, { "-scale-to-x", argInt, &x_scaleTo, 0, "scales each page horizontally to fit in scale-to-x pixels" }, { "-scale-to-y", argInt, &y_scaleTo, 0, "scales each page vertically to fit in scale-to-y pixels" }, { "-x", argInt, &crop_x, 0, "x-coordinate of the crop area top left corner" }, { "-y", argInt, &crop_y, 0, "y-coordinate of the crop area top left corner" }, { "-W", argInt, &crop_w, 0, "width of crop area in pixels (default is 0)" }, { "-H", argInt, &crop_h, 0, "height of crop area in pixels (default is 0)" }, { "-sz", argInt, &sz, 0, "size of crop square in pixels (sets W and H)" }, { "-cropbox", argFlag, &useCropBox, 0, "use the crop box rather than media box" }, { "-mono", argFlag, &mono, 0, "generate a monochrome image file (PNG, JPEG)" }, { "-gray", argFlag, &gray, 0, "generate a grayscale image file (PNG, JPEG)" }, { "-transp", argFlag, &transp, 0, "use a transparent background instead of white (PNG)" }, { "-antialias", argGooString, &antialias, 0, "set cairo antialias option" }, #ifdef USE_CMS { "-icc", argGooString, &icc, 0, "ICC color profile to use" }, #endif { "-level2", argFlag, &level2, 0, "generate Level 2 PostScript (PS, EPS)" }, { "-level3", argFlag, &level3, 0, "generate Level 3 PostScript (PS, EPS)" }, { "-origpagesizes", argFlag, &origPageSizes, 0, "conserve original page sizes (PS, PDF, SVG)" }, { "-paper", argString, paperSize, sizeof(paperSize), "paper size (letter, legal, A4, A3, match)" }, { "-paperw", argInt, &paperWidth, 0, "paper width, in points" }, { "-paperh", argInt, &paperHeight, 0, "paper height, in points" }, { "-nocrop", argFlag, &noCrop, 0, "don't crop pages to CropBox" }, { "-expand", argFlag, &expand, 0, "expand pages smaller than the paper size" }, { "-noshrink", argFlag, &noShrink, 0, "don't shrink pages larger than the paper size" }, { "-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)" }, { "-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" }, { "-help", argFlag, &printHelp, 0, "print usage information" }, { "--help", argFlag, &printHelp, 0, "print usage information" }, { "-?", argFlag, &printHelp, 0, "print usage information" }, {} }; static cairo_surface_t *surface; static bool printing; static FILE *output_file; static bool usePDFPageSize; static cairo_antialias_t antialiasEnum = CAIRO_ANTIALIAS_DEFAULT; #ifdef USE_CMS static unsigned char *icc_data; static int icc_data_size; static GfxLCMSProfilePtr profile; #endif struct AntialiasOption { const char *name; cairo_antialias_t value; }; static const AntialiasOption antialiasOptions[] = { { "default", CAIRO_ANTIALIAS_DEFAULT }, { "none", CAIRO_ANTIALIAS_NONE }, { "gray", CAIRO_ANTIALIAS_GRAY }, { "subpixel", CAIRO_ANTIALIAS_SUBPIXEL }, { "fast", CAIRO_ANTIALIAS_FAST }, { "good", CAIRO_ANTIALIAS_GOOD }, { "best", CAIRO_ANTIALIAS_BEST }, { nullptr, CAIRO_ANTIALIAS_DEFAULT }, }; static bool parseAntialiasOption() { const AntialiasOption *option = antialiasOptions; while (option->name) { if (antialias.cmp(option->name) == 0) { antialiasEnum = option->value; return true; } option++; } fprintf(stderr, "Error: Invalid antialias option \"%s\"\n", antialias.c_str()); fprintf(stderr, "Valid options are:\n"); option = antialiasOptions; while (option->name) { fprintf(stderr, " %s\n", option->name); option++; } return false; } static bool parseJpegOptions() { // jpegOpt format is: =,=,... const char *nextOpt = jpegOpt.c_str(); while (nextOpt && *nextOpt) { const char *comma = strchr(nextOpt, ','); GooString opt; if (comma) { opt.Set(nextOpt, static_cast(comma - nextOpt)); nextOpt = comma + 1; } else { opt.Set(nextOpt); nextOpt = nullptr; } // here opt is "= " const char *equal = strchr(opt.c_str(), '='); if (!equal) { fprintf(stderr, "Unknown jpeg option \"%s\"\n", opt.c_str()); return false; } const int iequal = static_cast(equal - opt.c_str()); GooString value(&opt, iequal + 1, opt.getLength() - iequal - 1); opt.del(iequal, opt.getLength() - iequal); // here opt is "" and value is "" if (opt.cmp("quality") == 0) { if (!isInt(value.c_str())) { fprintf(stderr, "Invalid jpeg quality\n"); return false; } jpegQuality = atoi(value.c_str()); if (jpegQuality < 0 || jpegQuality > 100) { fprintf(stderr, "jpeg quality must be between 0 and 100\n"); return false; } } else if (opt.cmp("progressive") == 0) { jpegProgressive = false; if (value.cmp("y") == 0) { jpegProgressive = true; } else if (value.cmp("n") != 0) { fprintf(stderr, "jpeg progressive option must be \"y\" or \"n\"\n"); return false; } } else if (opt.cmp("optimize") == 0 || opt.cmp("optimise") == 0) { jpegOptimize = false; if (value.cmp("y") == 0) { jpegOptimize = true; } else if (value.cmp("n") != 0) { fprintf(stderr, "jpeg optimize option must be \"y\" or \"n\"\n"); return false; } } else { fprintf(stderr, "Unknown jpeg option \"%s\"\n", opt.c_str()); return false; } } return true; } static void writePageImage(GooString *filename) { ImgWriter *writer = nullptr; FILE *file; int height, width, stride; unsigned char *data; if (png) { #ifdef ENABLE_LIBPNG if (transp) { writer = new PNGWriter(PNGWriter::RGBA); } else if (gray) { writer = new PNGWriter(PNGWriter::GRAY); } else if (mono) { writer = new PNGWriter(PNGWriter::MONOCHROME); } else { writer = new PNGWriter(PNGWriter::RGB); } # ifdef USE_CMS if (icc_data) { cmsUInt8Number profileID[17]; profileID[16] = '\0'; cmsGetHeaderProfileID(profile.get(), profileID); static_cast(writer)->setICCProfile(reinterpret_cast(profileID), icc_data, icc_data_size); } else { static_cast(writer)->setSRGBProfile(); } # endif #endif } else if (jpeg) { #ifdef ENABLE_LIBJPEG if (gray) { writer = new JpegWriter(JpegWriter::GRAY); } else { writer = new JpegWriter(JpegWriter::RGB); } static_cast(writer)->setOptimize(jpegOptimize); static_cast(writer)->setProgressive(jpegProgressive); if (jpegQuality >= 0) { static_cast(writer)->setQuality(jpegQuality); } #endif } else if (tiff) { #ifdef ENABLE_LIBTIFF if (transp) { writer = new TiffWriter(TiffWriter::RGBA_PREMULTIPLIED); } else if (gray) { writer = new TiffWriter(TiffWriter::GRAY); } else if (mono) { writer = new TiffWriter(TiffWriter::MONOCHROME); } else { writer = new TiffWriter(TiffWriter::RGB); } static_cast(writer)->setCompressionString(tiffCompressionStr); #endif } if (!writer) { return; } if (filename->cmp("fd://0") == 0) { #if defined(_WIN32) || defined(__CYGWIN__) _setmode(fileno(stdout), O_BINARY); #endif file = stdout; } else { file = fopen(filename->c_str(), "wb"); } if (!file) { fprintf(stderr, "Error opening output file %s\n", filename->c_str()); exit(2); } height = cairo_image_surface_get_height(surface); width = cairo_image_surface_get_width(surface); stride = cairo_image_surface_get_stride(surface); cairo_surface_flush(surface); data = cairo_image_surface_get_data(surface); if (!writer->init(file, width, height, x_resolution, y_resolution)) { fprintf(stderr, "Error writing %s\n", filename->c_str()); exit(2); } unsigned char *row = (unsigned char *)gmallocn(width, 4); for (int y = 0; y < height; y++) { uint32_t *pixel = reinterpret_cast((data + y * stride)); unsigned char *rowp = row; int bit = 7; for (int x = 0; x < width; x++, pixel++) { if (transp) { if (tiff) { // RGBA premultipled format *rowp++ = (*pixel & 0xff0000) >> 16; *rowp++ = (*pixel & 0x00ff00) >> 8; *rowp++ = (*pixel & 0x0000ff) >> 0; *rowp++ = (*pixel & 0xff000000) >> 24; } else { // unpremultiply into RGBA format uint8_t a; a = (*pixel & 0xff000000) >> 24; if (a == 0) { *rowp++ = 0; *rowp++ = 0; *rowp++ = 0; } else { *rowp++ = (((*pixel & 0xff0000) >> 16) * 255 + a / 2) / a; *rowp++ = (((*pixel & 0x00ff00) >> 8) * 255 + a / 2) / a; *rowp++ = (((*pixel & 0x0000ff) >> 0) * 255 + a / 2) / a; } *rowp++ = a; } } else if (gray || mono) { // convert to gray // The PDF Reference specifies the DeviceRGB to DeviceGray conversion as // gray = 0.3*red + 0.59*green + 0.11*blue const int r = (*pixel & 0x00ff0000) >> 16; const int g = (*pixel & 0x0000ff00) >> 8; const int b = (*pixel & 0x000000ff) >> 0; // an arbitrary integer approximation of .3*r + .59*g + .11*b const int grayValue = (r * 19661 + g * 38666 + b * 7209 + 32829) >> 16; if (mono) { if (bit == 7) { *rowp = 0; } if (grayValue > 127) { *rowp |= (1 << bit); } bit--; if (bit < 0) { bit = 7; rowp++; } } else { *rowp++ = grayValue; } } else { // copy into RGB format *rowp++ = (*pixel & 0x00ff0000) >> 16; *rowp++ = (*pixel & 0x0000ff00) >> 8; *rowp++ = (*pixel & 0x000000ff) >> 0; } } writer->writeRow(&row); } gfree(row); writer->close(); delete writer; if (file == stdout) { fflush(file); } else { fclose(file); } } static void getCropSize(double page_w, double page_h, double *width, double *height) { int w = crop_w; int h = crop_h; if (w == 0) { w = (int)ceil(page_w); } if (h == 0) { h = (int)ceil(page_h); } *width = (crop_x + w > page_w ? (int)ceil(page_w - crop_x) : w); *height = (crop_y + h > page_h ? (int)ceil(page_h - crop_y) : h); } static void getOutputSize(double page_w, double page_h, double *width, double *height) { if (printing) { if (usePDFPageSize) { *width = page_w; *height = page_h; } else { if (page_w > page_h) { *width = paperHeight; *height = paperWidth; } else { *width = paperWidth; *height = paperHeight; } } } else { getCropSize(page_w * x_resolution / 72.0, page_h * y_resolution / 72.0, width, height); } } static void getFitToPageTransform(double page_w, double page_h, double paper_w, double paper_h, cairo_matrix_t *m) { double x_scale, y_scale, scale; x_scale = paper_w / page_w; y_scale = paper_h / page_h; if (x_scale < y_scale) { scale = x_scale; } else { scale = y_scale; } if (scale > 1.0 && !expand) { scale = 1.0; } if (scale < 1.0 && noShrink) { scale = 1.0; } cairo_matrix_init_identity(m); if (!noCenter) { // centre page cairo_matrix_translate(m, (paper_w - page_w * scale) / 2, (paper_h - page_h * scale) / 2); } else if (!svg) { // move to PostScript origin cairo_matrix_translate(m, 0, (paper_h - page_h * scale)); } cairo_matrix_scale(m, scale, scale); } static cairo_status_t writeStream(void *closure, const unsigned char *data, unsigned int length) { FILE *file = (FILE *)closure; if (fwrite(data, length, 1, file) == 1) { return CAIRO_STATUS_SUCCESS; } else { return CAIRO_STATUS_WRITE_ERROR; } } static void beginDocument(GooString *inputFileName, GooString *outputFileName, double w, double h) { if (printing) { if (printToWin32) { output_file = nullptr; } else { if (outputFileName->cmp("fd://0") == 0) { #if defined(_WIN32) || defined(__CYGWIN__) _setmode(fileno(stdout), O_BINARY); #endif output_file = stdout; } else { output_file = fopen(outputFileName->c_str(), "wb"); if (!output_file) { fprintf(stderr, "Error opening output file %s\n", outputFileName->c_str()); exit(2); } } } if (ps || eps) { #ifdef CAIRO_HAS_PS_SURFACE surface = cairo_ps_surface_create_for_stream(writeStream, output_file, w, h); if (level2) { cairo_ps_surface_restrict_to_level(surface, CAIRO_PS_LEVEL_2); } if (eps) { cairo_ps_surface_set_eps(surface, 1); } if (duplex) { cairo_ps_surface_dsc_comment(surface, "%%Requirements: duplex"); cairo_ps_surface_dsc_begin_setup(surface); cairo_ps_surface_dsc_comment(surface, "%%IncludeFeature: *Duplex DuplexNoTumble"); } cairo_ps_surface_dsc_begin_page_setup(surface); #endif } else if (pdf) { #ifdef CAIRO_HAS_PDF_SURFACE surface = cairo_pdf_surface_create_for_stream(writeStream, output_file, w, h); #endif } else if (svg) { #ifdef CAIRO_HAS_SVG_SURFACE surface = cairo_svg_surface_create_for_stream(writeStream, output_file, w, h); cairo_svg_surface_restrict_to_version(surface, CAIRO_SVG_VERSION_1_2); #endif } #ifdef CAIRO_HAS_WIN32_SURFACE if (printToWin32) surface = win32BeginDocument(inputFileName, outputFileName); #endif } } static void beginPage(double *w, double *h) { if (printing) { if (ps) { #ifdef CAIRO_HAS_PS_SURFACE if (*w > *h) { cairo_ps_surface_dsc_comment(surface, "%%PageOrientation: Landscape"); cairo_ps_surface_set_size(surface, *h, *w); } else { cairo_ps_surface_dsc_comment(surface, "%%PageOrientation: Portrait"); cairo_ps_surface_set_size(surface, *w, *h); } #endif } #ifdef CAIRO_HAS_PDF_SURFACE if (pdf) { cairo_pdf_surface_set_size(surface, *w, *h); } #endif #ifdef CAIRO_HAS_WIN32_SURFACE if (printToWin32) { bool changePageSize = true; if (setupdlg && !origPageSizes) changePageSize = false; win32BeginPage(w, h, changePageSize, noShrink); // w,h will be changed to actual size used } #endif cairo_surface_set_fallback_resolution(surface, x_resolution, y_resolution); } else { surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, static_cast(ceil(*w)), static_cast(ceil(*h))); } } static void renderPage(PDFDoc *doc, CairoOutputDev *cairoOut, int pg, double page_w, double page_h, double output_w, double output_h) { cairo_t *cr; cairo_status_t status; cairo_matrix_t m; cairo_font_options_t *font_options; cr = cairo_create(surface); cairo_set_antialias(cr, antialiasEnum); font_options = cairo_font_options_create(); cairo_get_font_options(cr, font_options); cairo_font_options_set_antialias(font_options, antialiasEnum); cairo_set_font_options(cr, font_options); cairo_font_options_destroy(font_options); cairoOut->setCairo(cr); cairoOut->setPrinting(printing); cairo_save(cr); if (ps && output_w > output_h) { // rotate 90 deg for landscape cairo_translate(cr, 0, output_w); cairo_matrix_init(&m, 0, -1, 1, 0, 0, 0); cairo_transform(cr, &m); } cairo_translate(cr, -crop_x, -crop_y); if (printing) { double cropped_w, cropped_h; getCropSize(page_w, page_h, &cropped_w, &cropped_h); getFitToPageTransform(cropped_w, cropped_h, output_w, output_h, &m); cairo_transform(cr, &m); cairo_rectangle(cr, crop_x, crop_y, cropped_w, cropped_h); cairo_clip(cr); } else { cairo_scale(cr, x_resolution / 72.0, y_resolution / 72.0); } doc->displayPageSlice(cairoOut, pg, 72.0, 72.0, 0, /* rotate */ !useCropBox, /* useMediaBox */ false, /* Crop */ printing, -1, -1, -1, -1); cairo_restore(cr); cairoOut->setCairo(nullptr); // Blend onto white page if (!printing && !transp) { cairo_save(cr); cairo_set_operator(cr, CAIRO_OPERATOR_DEST_OVER); cairo_set_source_rgb(cr, 1, 1, 1); cairo_paint(cr); cairo_restore(cr); } status = cairo_status(cr); if (status) { fprintf(stderr, "cairo error: %s\n", cairo_status_to_string(status)); } cairo_destroy(cr); } 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 if (printToWin32) win32EndPage(imageFileName); #endif } else { writePageImage(imageFileName); cairo_surface_finish(surface); status = cairo_surface_status(surface); if (status) { fprintf(stderr, "cairo error: %s\n", cairo_status_to_string(status)); } cairo_surface_destroy(surface); } } static void endDocument() { cairo_status_t status; if (printing) { cairo_surface_finish(surface); status = cairo_surface_status(surface); if (status) { fprintf(stderr, "cairo error: %s\n", cairo_status_to_string(status)); } cairo_surface_destroy(surface); #ifdef CAIRO_HAS_WIN32_SURFACE if (printToWin32) win32EndDocument(); #endif if (output_file) { fclose(output_file); } } } static bool setPSPaperSize(char *size, int &psPaperWidth, int &psPaperHeight) { if (!strcmp(size, "match")) { psPaperWidth = psPaperHeight = -1; } else if (!strcmp(size, "letter")) { psPaperWidth = 612; psPaperHeight = 792; } else if (!strcmp(size, "legal")) { psPaperWidth = 612; psPaperHeight = 1008; } else if (!strcmp(size, "A4")) { psPaperWidth = 595; psPaperHeight = 842; } else if (!strcmp(size, "A3")) { psPaperWidth = 842; psPaperHeight = 1190; } else { return false; } return true; } static GooString *getImageFileName(GooString *outputFileName, int numDigits, int page) { char buf[10]; GooString *imageName = new GooString(outputFileName); if (!singleFile) { snprintf(buf, sizeof(buf), "-%0*d", numDigits, page); imageName->append(buf); } if (outputFileName->cmp("fd://0") != 0) { if (png) { imageName->append(".png"); } else if (jpeg) { imageName->append(".jpg"); } else if (tiff) { imageName->append(".tif"); } } return imageName; } // If (printing || singleFile) the output file name includes the // extension. Otherwise it is the file name base. static GooString *getOutputFileName(GooString *fileName, GooString *outputName) { GooString *name; if (outputName) { if (outputName->cmp("-") == 0) { if (printToWin32 || (!printing && !singleFile)) { fprintf(stderr, "Error: stdout may only be used with the ps, eps, pdf, svg output options or if -singlefile is used.\n"); exit(99); } return new GooString("fd://0"); } return new GooString(outputName); } if (printToWin32) { return nullptr; // No output file means print to printer } if (fileName->cmp("fd://0") == 0) { fprintf(stderr, "Error: an output filename or '-' must be supplied when the PDF file is stdin.\n"); exit(99); } // be careful not to overwrite the input file when the output format is PDF if (pdf && fileName->cmpN("http://", 7) != 0 && fileName->cmpN("https://", 8) != 0) { fprintf(stderr, "Error: an output filename or '-' must be supplied when the output format is PDF and input PDF file is a local file.\n"); exit(99); } // strip everything up to last '/' const char *s = fileName->c_str(); const char *p = strrchr(s, '/'); if (p) { p++; if (*p == 0) { fprintf(stderr, "Error: invalid output filename.\n"); exit(99); } name = new GooString(p); } else { name = new GooString(s); } // remove .pdf extension p = strrchr(name->c_str(), '.'); if (p && strcasecmp(p, ".pdf") == 0) { GooString *name2 = new GooString(name->c_str(), name->getLength() - 4); delete name; name = name2; } // append new extension if (ps) { name->append(".ps"); } else if (eps) { name->append(".eps"); } else if (pdf) { name->append(".pdf"); } else if (svg) { name->append(".svg"); } return name; } static void checkInvalidPrintOption(bool option, const char *option_name) { if (option) { fprintf(stderr, "Error: %s may only be used with the -png, -jpeg, or -tiff output options.\n", option_name); exit(99); } } static void checkInvalidImageOption(bool option, const char *option_name) { if (option) { fprintf(stderr, "Error: %s may only be used with the -ps, -eps, -pdf, or -svg output options.\n", option_name); exit(99); } } int main(int argc, char *argv[]) { GooString *fileName = nullptr; GooString *outputName = nullptr; GooString *outputFileName = nullptr; GooString *imageFileName = nullptr; std::optional ownerPW, userPW; CairoOutputDev *cairoOut; int pg, pg_num_len; double pg_w, pg_h, tmp, output_w, output_h; int num_outputs; // parse args Win32Console win32Console(&argc, &argv); if (!parseArgs(argDesc, &argc, argv)) { printUsage("pdftocairo", nullptr, argDesc); exit(99); } if (resolution != 0.0 && (x_resolution == 150.0 || y_resolution == 150.0)) { x_resolution = resolution; y_resolution = resolution; } if (argc < 2 || argc > 3 || printVersion || printHelp) { fprintf(stderr, "pdftocairo version %s\n", PACKAGE_VERSION); fprintf(stderr, "%s\n", popplerCopyright); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("pdftocairo", " []", argDesc); } if (printVersion || printHelp) { exit(0); } else { exit(99); } } num_outputs = (png ? 1 : 0) + (jpeg ? 1 : 0) + (tiff ? 1 : 0) + (ps ? 1 : 0) + (eps ? 1 : 0) + (pdf ? 1 : 0) + (printToWin32 ? 1 : 0) + (printdlg ? 1 : 0) + (svg ? 1 : 0); if (num_outputs == 0) { fprintf(stderr, "Error: one of the output format options (-png, -jpeg, -ps, -eps, -pdf, -print, -printdlg, -svg) must be used.\n"); exit(99); } if (num_outputs > 1) { fprintf(stderr, "Error: use only one of the output format options (-png, -jpeg, -ps, -eps, -pdf, -printdlg, -print, -svg).\n"); exit(99); } if (png || jpeg || tiff) { printing = false; } else { printing = true; } if (printing) { checkInvalidPrintOption(mono, "-mono"); checkInvalidPrintOption(gray, "-gray"); checkInvalidPrintOption(transp, "-transp"); checkInvalidPrintOption(icc.c_str()[0], "-icc"); checkInvalidPrintOption(singleFile, "-singlefile"); checkInvalidPrintOption(useCropBox, "-cropbox"); checkInvalidPrintOption(scaleTo != 0, "-scale-to"); checkInvalidPrintOption(x_scaleTo != 0, "-scale-to-x"); checkInvalidPrintOption(y_scaleTo != 0, "-scale-to-y"); } else { checkInvalidImageOption(level2, "-level2"); checkInvalidImageOption(level3, "-level3"); checkInvalidImageOption(origPageSizes, "-origpagesizes"); checkInvalidImageOption(paperSize[0], "-paper"); checkInvalidImageOption(paperWidth > 0, "-paperw"); checkInvalidImageOption(paperHeight > 0, "-paperh"); checkInvalidImageOption(noCrop, "-nocrop"); checkInvalidImageOption(expand, "-expand"); checkInvalidImageOption(noShrink, "-noshrink"); checkInvalidImageOption(noCenter, "-nocenter"); checkInvalidImageOption(duplex, "-duplex"); } if (printing) { useCropBox = !noCrop; } if (icc.c_str()[0] && !png) { fprintf(stderr, "Error: -icc may only be used with png output.\n"); exit(99); } if (antialias.getLength() > 0) { if (!parseAntialiasOption()) { exit(99); } } if (transp && !(png || tiff)) { fprintf(stderr, "Error: -transp may only be used with png or tiff output.\n"); exit(99); } if (mono && gray) { fprintf(stderr, "Error: -mono and -gray may not be used together.\n"); exit(99); } if (mono && !(png || tiff)) { fprintf(stderr, "Error: -mono may only be used with png or tiff output.\n"); exit(99); } if (jpegOpt.getLength() > 0) { if (!jpeg) { fprintf(stderr, "Error: -jpegopt may only be used with jpeg output.\n"); exit(99); } if (!parseJpegOptions()) { exit(99); } } if (strlen(tiffCompressionStr) > 0 && !tiff) { fprintf(stderr, "Error: -tiffcompression may only be used with tiff output.\n"); exit(99); } if (level2 && level3) { fprintf(stderr, "Error: use only one of the 'level' options.\n"); exit(99); } if (!level2 && !level3) { 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); } if ((paperWidth > 0 && paperHeight <= 0) || (paperWidth <= 0 && paperHeight > 0)) { fprintf(stderr, "Error: both -paperw and -paperh must be specified.\n"); exit(99); } if (paperSize[0]) { if (origPageSizes) { fprintf(stderr, "Error: -origpagesizes and -paper may not be used together.\n"); exit(99); } if (!setPSPaperSize(paperSize, paperWidth, paperHeight)) { fprintf(stderr, "Invalid paper size\n"); exit(99); } } if (origPageSizes || paperWidth < 0 || paperHeight < 0) { usePDFPageSize = true; } else { usePDFPageSize = false; } if (printdlg) { printToWin32 = true; } globalParams = std::make_unique(); if (quiet) { globalParams->setErrQuiet(quiet); } // open PDF file if (ownerPassword[0]) { ownerPW = GooString(ownerPassword); } if (userPassword[0]) { userPW = GooString(userPassword); } fileName = new GooString(argv[1]); if (fileName->cmp("-") == 0) { delete fileName; fileName = new GooString("fd://0"); } if (argc == 3) { outputName = new GooString(argv[2]); } else { outputName = nullptr; } outputFileName = getOutputFileName(fileName, outputName); #ifdef USE_CMS icc_data = nullptr; if (icc.c_str()[0]) { FILE *file = fopen(icc.c_str(), "rb"); if (!file) { fprintf(stderr, "Error: unable to open icc profile %s\n", icc.c_str()); exit(4); } fseek(file, 0, SEEK_END); icc_data_size = ftell(file); fseek(file, 0, SEEK_SET); icc_data = (unsigned char *)gmalloc(icc_data_size); if (fread(icc_data, icc_data_size, 1, file) != 1) { fprintf(stderr, "Error: unable to read icc profile %s\n", icc.c_str()); exit(4); } fclose(file); profile = make_GfxLCMSProfilePtr(cmsOpenProfileFromMem(icc_data, icc_data_size)); if (!profile) { fprintf(stderr, "Error: lcms error opening profile\n"); exit(4); } } else { profile = make_GfxLCMSProfilePtr(cmsCreate_sRGBProfile()); } #endif std::unique_ptr doc = PDFDocFactory().createPDFDoc(*fileName, ownerPW, userPW); if (!doc->isOk()) { fprintf(stderr, "Error opening PDF file.\n"); exit(1); } #ifdef ENFORCE_PERMISSIONS // check for print permission if (printing && !doc->okToPrint()) { fprintf(stderr, "Printing this document is not allowed.\n"); exit(3); } #endif // get page range if (firstPage < 1) { firstPage = 1; } if (singleFile && lastPage < 1) { lastPage = firstPage; } if (lastPage < 1 || lastPage > doc->getNumPages()) { lastPage = doc->getNumPages(); } if (lastPage < firstPage) { fprintf(stderr, "Wrong page range given: the first page (%d) can not be after the last page (%d).\n", firstPage, lastPage); exit(99); } if (eps && firstPage != lastPage) { fprintf(stderr, "EPS files can only contain one page.\n"); exit(99); } // 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. 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); } if (singleFile && firstPage < lastPage) { if (!quiet) { fprintf(stderr, "Warning: Single file will write only the first of the %d pages.\n", lastPage + 1 - firstPage); } lastPage = firstPage; } #ifdef CAIRO_HAS_WIN32_SURFACE if (printdlg) { bool allPages = false; if (firstPage == 1 && lastPage == doc->getNumPages()) allPages = true; win32ShowPrintDialog(&expand, &noShrink, &noCenter, &usePDFPageSize, &allPages, &firstPage, &lastPage, doc->getNumPages()); if (allPages) { firstPage = 1; lastPage = doc->getNumPages(); } } else if (printToWin32) { win32SetupPrinter(&printer, &printOpt, duplex, setupdlg); } #endif cairoOut = new CairoOutputDev(); cairoOut->setLogicalStructure(docStruct); #ifdef USE_CMS cairoOut->setDisplayProfile(profile); #endif cairoOut->startDoc(doc.get()); if (sz != 0) { crop_w = crop_h = sz; } pg_num_len = numberOfCharacters(doc->getNumPages()); for (pg = firstPage; pg <= lastPage; ++pg) { if (printOnlyEven && pg % 2 == 1) { continue; } if (printOnlyOdd && pg % 2 == 0) { continue; } if (useCropBox) { pg_w = doc->getPageCropWidth(pg); pg_h = doc->getPageCropHeight(pg); } else { pg_w = doc->getPageMediaWidth(pg); pg_h = doc->getPageMediaHeight(pg); } if (printing && pg == firstPage) { if (paperWidth < 0 || paperHeight < 0) { paperWidth = (int)ceil(pg_w); paperHeight = (int)ceil(pg_h); } } if ((doc->getPageRotate(pg) == 90) || (doc->getPageRotate(pg) == 270)) { tmp = pg_w; pg_w = pg_h; pg_h = tmp; } if (scaleTo != 0) { resolution = (72.0 * scaleTo) / (pg_w > pg_h ? pg_w : pg_h); x_resolution = y_resolution = resolution; } else { if (x_scaleTo > 0) { x_resolution = (72.0 * x_scaleTo) / pg_w; if (y_scaleTo == -1) { y_resolution = x_resolution; } } if (y_scaleTo > 0) { y_resolution = (72.0 * y_scaleTo) / pg_h; if (x_scaleTo == -1) { x_resolution = y_resolution; } } } if (imageFileName) { delete imageFileName; imageFileName = nullptr; } if (!printing) { imageFileName = getImageFileName(outputFileName, pg_num_len, pg); } getOutputSize(pg_w, pg_h, &output_w, &output_h); if (pg == firstPage) { beginDocument(fileName, outputFileName, output_w, output_h); } beginPage(&output_w, &output_h); renderPage(doc.get(), cairoOut, pg, pg_w, pg_h, output_w, output_h); 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; } #ifdef USE_CMS if (icc_data) { gfree(icc_data); } #endif return 0; } poppler-24.02.0/utils/pdftohtml.1000066400000000000000000000052061455701731300165740ustar00rootroot00000000000000.TH PDFTOHTML 1 .\" NAME should be all caps, SECTION should be 1-8, maybe w/ subsection .\" other parms are allowed: see man(7), man(1) .SH NAME pdftohtml \- program to convert PDF files into HTML, XML and PNG images .SH SYNOPSIS .B pdftohtml .I "[options] [ ]" .SH "DESCRIPTION" This manual page documents briefly the .BR pdftohtml command. This manual page was written for the Debian GNU/Linux distribution because the original program does not have a manual page. .PP .B pdftohtml is a program that converts PDF documents into HTML. It generates its output in the current working directory. If .I PDF-file is \'-', it reads the PDF file from stdin. .SH OPTIONS A summary of options are included below. .TP .B \-h, \-help Show summary of options. .TP .B \-f first page to print .TP .B \-l last page to print .TP .B \-q do not print any messages or errors .TP .B \-v print copyright and version info .TP .B \-p exchange .pdf links with .html .TP .B \-c generate complex output .TP .B \-s generate single HTML that includes all pages .TP .B \-dataurls use data URLs instead of external images in HTML. No available in all platforms .TP .B \-i ignore images .TP .B \-noframes generate no frames. Not supported in complex output mode. .TP .B \-stdout use standard output .TP .B \-zoom zoom the PDF document (default 1.5) (1 means 72 DPI) .TP .B \-xml output for XML post-processing .TP .B \-noroundcoord do not round coordinates (with XML output only) .TP .B \-enc output text encoding name .TP .B \-opw owner password (for encrypted files) .TP .B \-upw user password (for encrypted files) .TP .B \-hidden force hidden text extraction .TP .B \-fmt image file format for Splash output (png or jpg). If complex is selected, but \-fmt is not specified, \-fmt png will be assumed .TP .B \-nomerge do not merge paragraphs .TP .B \-nodrm override document DRM settings .TP .B \-wbt adjust the word break threshold percent. Default is 10. Word break occurs when distance between two adjacent characters is greater than this percent of character height. .TP .B \-fontfullname outputs the font name without any substitutions. .SH AUTHOR Pdftohtml was developed by Gueorgui Ovtcharov and Rainer Dorsch. It is based and benefits a lot from Derek Noonburg's xpdf package. This manual page was written by Søren Boll Overgaard , for the Debian GNU/Linux system (but may be used by others). .SH "SEE ALSO" .BR pdfdetach (1), .BR pdffonts (1), .BR pdfimages (1), .BR pdfinfo (1), .BR pdftocairo (1), .BR pdftoppm (1), .BR pdftops (1), .BR pdftotext (1) .BR pdfseparate (1), .BR pdfsig (1), .BR pdfunite (1) poppler-24.02.0/utils/pdftohtml.cc000066400000000000000000000431501455701731300170210ustar00rootroot00000000000000//======================================================================== // // pdftohtml.cc // // // Copyright 1999-2000 G. Ovtcharov //======================================================================== //======================================================================== // // 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) 2007-2008, 2010, 2012, 2015-2020, 2022 Albert Astals Cid // Copyright (C) 2010 Hib Eris // Copyright (C) 2010 Mike Slegeir // Copyright (C) 2010, 2013 Suzuki Toshiya // Copyright (C) 2010 OSSD CDAC Mumbai by Leena Chourey (leenac@cdacmumbai.in) and Onkar Potdar (onkar@cdacmumbai.in) // Copyright (C) 2011 Steven Murdoch // Copyright (C) 2012 Igor Slepchin // Copyright (C) 2012 Ihar Filipau // Copyright (C) 2012 Luis Parravicini // Copyright (C) 2014 Pino Toscano // Copyright (C) 2015 William Bader // Copyright (C) 2017, 2021 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 Thibaut Brard // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019, 2021 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 // //======================================================================== #include "config.h" #include #include #include #include #include #ifdef HAVE_DIRENT_H # include #endif #include #include "parseargs.h" #include "goo/GooString.h" #include "goo/gbase64.h" #include "goo/gbasename.h" #include "goo/gmem.h" #include "Object.h" #include "Stream.h" #include "Array.h" #include "Dict.h" #include "XRef.h" #include "Catalog.h" #include "Page.h" #include "Outline.h" #include "PDFDoc.h" #include "PDFDocFactory.h" #include "HtmlOutputDev.h" #include "SplashOutputDev.h" #include "splash/SplashBitmap.h" #include "GlobalParams.h" #include "PDFDocEncoding.h" #include "Error.h" #include "DateInfo.h" #include "goo/gfile.h" #include "Win32Console.h" #include "InMemoryFile.h" static int firstPage = 1; static int lastPage = 0; static bool rawOrder = true; bool printCommands = true; static bool printHelp = false; bool printHtml = false; bool complexMode = false; bool singleHtml = false; // singleHtml bool dataUrls = false; bool ignore = false; static char extension[5] = "png"; static double scale = 1.5; bool noframes = false; bool stout = false; bool xml = false; bool noRoundedCoordinates = false; static bool errQuiet = false; static bool noDrm = false; double wordBreakThreshold = 10; // 10%, below converted into a coefficient - 0.1 bool showHidden = false; bool noMerge = false; bool fontFullName = false; static char ownerPassword[33] = ""; static char userPassword[33] = ""; static bool printVersion = false; static std::unique_ptr getInfoString(Dict *infoDict, const char *key); static GooString *getInfoDate(Dict *infoDict, const char *key); static char textEncName[128] = ""; static const ArgDesc argDesc[] = { { "-f", argInt, &firstPage, 0, "first page to convert" }, { "-l", argInt, &lastPage, 0, "last page to convert" }, /*{"-raw", argFlag, &rawOrder, 0, "keep strings in content stream order"},*/ { "-q", argFlag, &errQuiet, 0, "don't print any messages or errors" }, { "-h", argFlag, &printHelp, 0, "print usage information" }, { "-?", argFlag, &printHelp, 0, "print usage information" }, { "-help", argFlag, &printHelp, 0, "print usage information" }, { "--help", argFlag, &printHelp, 0, "print usage information" }, { "-p", argFlag, &printHtml, 0, "exchange .pdf links by .html" }, { "-c", argFlag, &complexMode, 0, "generate complex document" }, { "-s", argFlag, &singleHtml, 0, "generate single document that includes all pages" }, #ifdef HAVE_IN_MEMORY_FILE { "-dataurls", argFlag, &dataUrls, 0, "use data URLs instead of external images in HTML" }, #endif { "-i", argFlag, &ignore, 0, "ignore images" }, { "-noframes", argFlag, &noframes, 0, "generate no frames" }, { "-stdout", argFlag, &stout, 0, "use standard output" }, { "-zoom", argFP, &scale, 0, "zoom the pdf document (default 1.5)" }, { "-xml", argFlag, &xml, 0, "output for XML post-processing" }, { "-noroundcoord", argFlag, &noRoundedCoordinates, 0, "do not round coordinates (with XML output only)" }, { "-hidden", argFlag, &showHidden, 0, "output hidden text" }, { "-nomerge", argFlag, &noMerge, 0, "do not merge paragraphs" }, { "-enc", argString, textEncName, sizeof(textEncName), "output text encoding name" }, { "-fmt", argString, extension, sizeof(extension), "image file format for Splash output (png or jpg)" }, { "-v", argFlag, &printVersion, 0, "print copyright and version info" }, { "-opw", argString, ownerPassword, sizeof(ownerPassword), "owner password (for encrypted files)" }, { "-upw", argString, userPassword, sizeof(userPassword), "user password (for encrypted files)" }, { "-nodrm", argFlag, &noDrm, 0, "override document DRM settings" }, { "-wbt", argFP, &wordBreakThreshold, 0, "word break threshold (default 10 percent)" }, { "-fontfullname", argFlag, &fontFullName, 0, "outputs font full name" }, {} }; class SplashOutputDevNoText : public SplashOutputDev { public: SplashOutputDevNoText(SplashColorMode colorModeA, int bitmapRowPadA, bool reverseVideoA, SplashColorPtr paperColorA, bool bitmapTopDownA = true) : SplashOutputDev(colorModeA, bitmapRowPadA, reverseVideoA, paperColorA, bitmapTopDownA) { } ~SplashOutputDevNoText() override; void drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, const Unicode *u, int uLen) override { } bool beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, const Unicode *u, int uLen) override { return false; } void endType3Char(GfxState *state) override { } void beginTextObject(GfxState *state) override { } void endTextObject(GfxState *state) override { } bool interpretType3Chars() override { return false; } }; SplashOutputDevNoText::~SplashOutputDevNoText() = default; int main(int argc, char *argv[]) { std::unique_ptr doc; GooString *fileName = nullptr; std::unique_ptr docTitle; std::unique_ptr author; std::unique_ptr keywords; std::unique_ptr subject; GooString *date = nullptr; GooString *htmlFileName = nullptr; HtmlOutputDev *htmlOut = nullptr; SplashOutputDev *splashOut = nullptr; bool doOutline; bool ok; std::optional ownerPW, userPW; Object info; int exit_status = EXIT_FAILURE; Win32Console win32Console(&argc, &argv); // parse args ok = parseArgs(argDesc, &argc, argv); if (!ok || argc < 2 || argc > 3 || printHelp || printVersion) { fprintf(stderr, "pdftohtml version %s\n", PACKAGE_VERSION); fprintf(stderr, "%s\n", popplerCopyright); fprintf(stderr, "%s\n", "Copyright 1999-2003 Gueorgui Ovtcharov and Rainer Dorsch"); fprintf(stderr, "%s\n\n", xpdfCopyright); if (!printVersion) { printUsage("pdftohtml", " [ ]", argDesc); } exit(printHelp || printVersion ? 0 : 1); } // init error file // errorInit(); // read config file globalParams = std::make_unique(); if (errQuiet) { globalParams->setErrQuiet(errQuiet); printCommands = false; // I'm not 100% what is the difference between them } if (textEncName[0]) { globalParams->setTextEncoding(textEncName); if (!globalParams->getTextEncoding()) { goto error; } } // convert from user-friendly percents into a coefficient wordBreakThreshold /= 100.0; // open PDF file if (ownerPassword[0]) { ownerPW = GooString(ownerPassword); } if (userPassword[0]) { userPW = GooString(userPassword); } fileName = new GooString(argv[1]); if (fileName->cmp("-") == 0) { delete fileName; fileName = new GooString("fd://0"); } doc = PDFDocFactory().createPDFDoc(*fileName, ownerPW, userPW); if (!doc->isOk()) { goto error; } // check for copy permission if (!doc->okToCopy()) { if (!noDrm) { error(errNotAllowed, -1, "Copying of text from this document is not allowed."); goto error; } fprintf(stderr, "Document has copy-protection bit set.\n"); } // construct text file name if (argc == 3) { GooString *tmp = new GooString(argv[2]); if (!xml) { 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); } } } 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); } } } if (!htmlFileName) { htmlFileName = new GooString(tmp); } delete tmp; } else if (fileName->cmp("fd://0") == 0) { error(errCommandLine, -1, "You have to provide an output filename when reading from stdin."); goto error; } 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); } else { htmlFileName = fileName->copy(); } // htmlFileName->append(".html"); } if (scale > 3.0) { scale = 3.0; } if (scale < 0.5) { scale = 0.5; } if (complexMode) { // noframes=false; stout = false; } if (stout) { noframes = true; complexMode = false; } if (xml) { complexMode = true; singleHtml = false; noframes = true; noMerge = true; } // get page range if (firstPage < 1) { firstPage = 1; } if (lastPage < 1 || lastPage > doc->getNumPages()) { lastPage = doc->getNumPages(); } 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 error; } info = doc->getDocInfo(); if (info.isDict()) { docTitle = getInfoString(info.getDict(), "Title"); author = getInfoString(info.getDict(), "Author"); keywords = getInfoString(info.getDict(), "Keywords"); subject = getInfoString(info.getDict(), "Subject"); date = getInfoDate(info.getDict(), "ModDate"); if (!date) { date = getInfoDate(info.getDict(), "CreationDate"); } } if (!docTitle) { docTitle = std::make_unique(htmlFileName); } if (!singleHtml) { rawOrder = complexMode; // todo: figure out what exactly rawOrder do :) } else { rawOrder = singleHtml; } doOutline = doc->getOutline()->getItems() != nullptr; // 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); if (date) { delete date; } if ((complexMode || singleHtml) && !xml && !ignore) { // White paper color SplashColor color; color[0] = color[1] = color[2] = 255; // If the user specified "jpg" use JPEG, otherwise PNG SplashImageFileFormat format = strcmp(extension, "jpg") ? splashFormatPng : splashFormatJpeg; splashOut = new SplashOutputDevNoText(splashModeRGB8, 4, false, color); splashOut->startDoc(doc.get()); for (int pg = firstPage; pg <= lastPage; ++pg) { InMemoryFile imf; 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"); if (!f1) { fprintf(stderr, "Could not open %s\n", imgFileName->c_str()); continue; } bitmap->writeImgFile(format, f1, 72 * scale, 72 * scale); fclose(f1); 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())); } } delete splashOut; } if (htmlOut->isOk()) { doc->displayPages(htmlOut, firstPage, lastPage, 72 * scale, 72 * scale, 0, true, false, false); htmlOut->dumpDocOutline(doc.get()); } delete htmlOut; exit_status = EXIT_SUCCESS; // clean up error: delete fileName; if (htmlFileName) { delete htmlFileName; } return exit_status; } static std::unique_ptr getInfoString(Dict *infoDict, const char *key) { Object obj; // Raw value as read from PDF (may be in pdfDocEncoding or UCS2) const GooString *rawString; // Value converted to unicode Unicode *unicodeString; int unicodeLength; // Value HTML escaped and converted to desired encoding std::unique_ptr encodedString; // Is rawString UCS2 (as opposed to pdfDocEncoding) bool isUnicode; obj = infoDict->lookup(key); if (obj.isString()) { rawString = obj.getString(); // Convert rawString to unicode if (rawString->hasUnicodeMarker()) { isUnicode = true; unicodeLength = (obj.getString()->getLength() - 2) / 2; } else { isUnicode = false; unicodeLength = obj.getString()->getLength(); } unicodeString = new Unicode[unicodeLength]; for (int i = 0; i < unicodeLength; i++) { if (isUnicode) { unicodeString[i] = ((rawString->getChar((i + 1) * 2) & 0xff) << 8) | (rawString->getChar(((i + 1) * 2) + 1) & 0xff); } else { unicodeString[i] = pdfDocEncoding[rawString->getChar(i) & 0xff]; } } // HTML escape and encode unicode encodedString = HtmlFont::HtmlFilter(unicodeString, unicodeLength); delete[] unicodeString; } return encodedString; } static GooString *getInfoDate(Dict *infoDict, const char *key) { Object obj; int year, mon, day, hour, min, sec, tz_hour, tz_minute; char tz; struct tm tmStruct; GooString *result = nullptr; char buf[256]; obj = infoDict->lookup(key); if (obj.isString()) { const GooString *s = obj.getString(); // TODO do something with the timezone info if (parseDateString(s, &year, &mon, &day, &hour, &min, &sec, &tz, &tz_hour, &tz_minute)) { tmStruct.tm_year = year - 1900; tmStruct.tm_mon = mon - 1; tmStruct.tm_mday = day; tmStruct.tm_hour = hour; tmStruct.tm_min = min; tmStruct.tm_sec = sec; tmStruct.tm_wday = -1; tmStruct.tm_yday = -1; tmStruct.tm_isdst = -1; mktime(&tmStruct); // compute the tm_wday and tm_yday fields if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S+00:00", &tmStruct)) { result = new GooString(buf); } else { result = new GooString(s); } } else { result = new GooString(s); } } return result; } poppler-24.02.0/utils/pdftoppm.1000066400000000000000000000150071455701731300164240ustar00rootroot00000000000000.\" Copyright 2005-2011 Glyph & Cog, LLC .TH pdftoppm 1 "15 August 2011" .SH NAME pdftoppm \- Portable Document Format (PDF) to Portable Pixmap (PPM) converter (version 3.03) .SH SYNOPSIS .B pdftoppm [options] .I PDF-file PPM-root .SH DESCRIPTION .B Pdftoppm converts Portable Document Format (PDF) files to color image files in Portable Pixmap (PPM) format, grayscale image files in Portable Graymap (PGM) format, or monochrome image files in Portable Bitmap (PBM) format. .PP Pdftoppm reads the PDF file, .IR PDF-file , and writes one PPM file for each page, .IR PPM-root - number .ppm, where .I number is the page number. If .I PDF-file is \'-', it reads the PDF file from stdin. .SH OPTIONS .TP .BI \-f " number" Specifies the first page to convert. .TP .BI \-l " number" Specifies the last page to convert. .TP .B \-o Generates only the odd numbered pages. .TP .B \-e Generates only the even numbered pages. .TP .BI \-singlefile Writes only the first page and does not add digits. .TP .BI \-r " number" Specifies the X and Y resolution, in DPI. The default is 150 DPI. .TP .BI \-rx " number" Specifies the X resolution, in DPI. The default is 150 DPI. .TP .BI \-ry " number" Specifies the Y resolution, in DPI. The default is 150 DPI. .TP .BI \-scale-to " number" Scales the long side of each page (width for landscape pages, height for portrait pages) to fit in scale-to pixels. The size of the short side will be determined by the aspect ratio of the page. .TP .BI \-scale-to-x " number" Scales each page horizontally to fit in scale-to-x pixels. If scale-to-y is set to -1, the vertical size will determined by the aspect ratio of the page. .TP .BI \-scale-to-y " number" Scales each page vertically to fit in scale-to-y pixels. If scale-to-x is set to -1, the horizontal size will determined by the aspect ratio of the page. .TP .B \-scale-dimension-before-rotation Swaps horizontal and vertical size for a rotated (landscape) pdf before scaling instead of after. .TP .BI \-x " number" Specifies the x-coordinate of the crop area top left corner .TP .BI \-y " number" Specifies the y-coordinate of the crop area top left corner .TP .BI \-W " number" Specifies the width of crop area in pixels (default is 0) .TP .BI \-H " number" Specifies the height of crop area in pixels (default is 0) .TP .BI \-sz " number" Specifies the size of crop square in pixels (sets W and H) .TP .B \-cropbox Uses the crop box rather than media box when generating the files .TP .B \-hide-annotations Do not show annotations .TP .B \-mono Generate a monochrome PBM file (instead of a color PPM file). .TP .B \-gray Generate a grayscale PGM file (instead of a color PPM file). .TP .BI \-displayprofile " displayprofilefile" If poppler is compiled with colour management support, this option sets the display profile to the ICC profile stored in displayprofilefile. .TP .BI \-defaultgrayprofile " defaultgrayprofilefile" If poppler is compiled with colour management support, this option sets the DefaultGray color space to the ICC profile stored in defaultgrayprofilefile. .TP .BI \-defaultrgbprofile " defaultrgbprofilefile" If poppler is compiled with colour management support, this option sets the DefaultRGB color space to the ICC profile stored in defaultrgbprofilefile. .TP .BI \-defaultcmykprofile " defaultcmykprofilefile" If poppler is compiled with colour management support, this option sets the DefaultCMYK color space to the ICC profile stored in defaultcmykprofilefile. .TP .B \-png Generates a PNG file instead a PPM file. .TP .B \-jpeg Generates a JPEG file instead a PPM file. .TP .BI \-jpegopt " jpeg-options" When used with \-jpeg, takes a list of options to control the jpeg compression. See .B JPEG OPTIONS for the available options. .TP .B \-tiff Generates a TIFF file instead a PPM file. .TP .BI \-tiffcompression " none | packbits | jpeg | lzw | deflate" Specifies the TIFF compression type. This defaults to "none". .TP .BI \-freetype " yes | no" Enable or disable FreeType (a TrueType / Type 1 font rasterizer). This defaults to "yes". .TP .BI \-thinlinemode " none | solid | shape" Specifies the thin line mode. This defaults to "none". .TP "solid": adjust lines with a width less than one pixel to pixel boundary and paint it with a width of one pixel. .TP "shape": adjust lines with a width less than one pixel to pixel boundary and paint it with a width of one pixel but with a shape in proportion to its width. .TP .BI \-aa " yes | no" Enable or disable font anti-aliasing. This defaults to "yes". .TP .BI \-aaVector " yes | no" Enable or disable vector anti-aliasing. This defaults to "yes". .TP .BI \-opw " password" Specify the owner password for the PDF file. Providing this will bypass all security restrictions. .TP .BI \-upw " password" Specify the user password for the PDF file. .TP .B \-q Don't print any messages or errors. .TP .B \-progress Print progress info as each page is generated. Three space-separated fields are printed to STDERR: the number of the current page, the number of the last page that will be generated, and the path to the file written to. .TP .BI \-sep " char" Specify single character separator between name and page number, default - . .TP .B \-forcenum Force page number even if there is only one page. .TP .B \-v Print copyright and version information. .TP .B \-h Print usage information. .RB ( \-help and .B \-\-help are equivalent.) .SH EXIT CODES The Xpdf tools use the following exit codes: .TP 0 No error. .TP 1 Error opening a PDF file. .TP 2 Error opening an output file. .TP 3 Error related to PDF permissions. .TP 99 Other error. .SH JPEG OPTIONS When JPEG output is specified, the \-jpegopt option can be used to control the JPEG compression parameters. It takes a string of the form "=[,=]". Currently the available options are: .TP .BI quality Selects the JPEG quality value. The value must be an integer between 0 and 100. .TP .BI progressive Select progressive JPEG output. The possible values are "y", "n", indicating progressive (yes) or non-progressive (no), respectively. .TP .BI optimize Sets whether to compute optimal Huffman coding tables for the JPEG output, which will create smaller files but make an extra pass over the data. The value must be "y" or "n", with "y" performing optimization, otherwise the default Huffman tables are used. .SH AUTHOR The pdftoppm software and documentation are copyright 1996-2011 Glyph & Cog, LLC. .SH "SEE ALSO" .BR pdfdetach (1), .BR pdffonts (1), .BR pdfimages (1), .BR pdfinfo (1), .BR pdftocairo (1), .BR pdftohtml (1), .BR pdftops (1), .BR pdftotext (1) .BR pdfseparate (1), .BR pdfsig (1), .BR pdfunite (1) poppler-24.02.0/utils/pdftoppm.cc000066400000000000000000000723561455701731300166630ustar00rootroot00000000000000//======================================================================== // // pdftoppm.cc // // Copyright 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) 2007 Ilmari Heikkinen // Copyright (C) 2008 Richard Airlie // Copyright (C) 2009 Michael K. Johnson // Copyright (C) 2009 Shen Liang // Copyright (C) 2009 Stefan Thomas // Copyright (C) 2009-2011, 2015, 2018-2022 Albert Astals Cid // Copyright (C) 2010, 2012, 2017 Adrian Johnson // Copyright (C) 2010 Hib Eris // Copyright (C) 2010 Jonathan Liu // Copyright (C) 2010 William Bader // Copyright (C) 2011-2013 Thomas Freitag // Copyright (C) 2013, 2015, 2018 Adam Reichold // Copyright (C) 2013 Suzuki Toshiya // Copyright (C) 2015 William Bader // Copyright (C) 2018 Martin Packman // Copyright (C) 2019 Yves-Gaël Chény // Copyright (C) 2019-2021 Oliver Sander // Copyright (C) 2019 // Copyright (C) 2019 Kris Jurka // Copyright (C) 2019 Sébastien Berthier // Copyright (C) 2020 Stéfan van der Walt // Copyright (C) 2020 Philipp Knechtges // Copyright (C) 2021 Diogo Kollross // Copyright (C) 2021 Peter Williams // Copyright (C) 2022 James Cloos // // 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 "config.h" #include #if defined(_WIN32) || defined(__CYGWIN__) # include // for O_BINARY # include // for _setmode #endif #include #include #include "parseargs.h" #include "goo/gmem.h" #include "goo/GooString.h" #include "GlobalParams.h" #include "Object.h" #include "PDFDoc.h" #include "PDFDocFactory.h" #include "splash/SplashBitmap.h" #include "splash/Splash.h" #include "splash/SplashErrorCodes.h" #include "SplashOutputDev.h" #include "Win32Console.h" #include "numberofcharacters.h" #include "sanitychecks.h" // Uncomment to build pdftoppm with pthreads // You may also have to change the buildsystem to // link pdftoppm to pthread library // This is here for developer testing not user ready // #define UTILS_USE_PTHREADS 1 #ifdef UTILS_USE_PTHREADS # include # include # include #endif // UTILS_USE_PTHREADS #ifdef USE_CMS # include #endif static int firstPage = 1; static int lastPage = 0; static bool printOnlyOdd = false; static bool printOnlyEven = false; static bool singleFile = false; static bool scaleDimensionBeforeRotation = false; static double resolution = 0.0; static double x_resolution = 150.0; static double y_resolution = 150.0; static int scaleTo = 0; static int x_scaleTo = 0; static int y_scaleTo = 0; static int param_x = 0; static int param_y = 0; static int param_w = 0; static int param_h = 0; static int sz = 0; static bool hideAnnotations = false; static bool useCropBox = false; static bool mono = false; static bool gray = false; #ifdef USE_CMS static GooString displayprofilename; static GfxLCMSProfilePtr displayprofile; static GooString defaultgrayprofilename; static GfxLCMSProfilePtr defaultgrayprofile; static GooString defaultrgbprofilename; static GfxLCMSProfilePtr defaultrgbprofile; static GooString defaultcmykprofilename; static GfxLCMSProfilePtr defaultcmykprofile; #endif static char sep[2] = "-"; static bool forceNum = false; static bool png = false; static bool jpeg = false; static bool jpegcmyk = false; static bool tiff = false; static GooString jpegOpt; static int jpegQuality = -1; static bool jpegProgressive = false; static bool jpegOptimize = false; static bool overprint = false; static bool splashOverprintPreview = false; static char enableFreeTypeStr[16] = ""; static bool enableFreeType = true; static char antialiasStr[16] = ""; static char vectorAntialiasStr[16] = ""; static bool fontAntialias = true; static bool vectorAntialias = true; static char ownerPassword[33] = ""; static char userPassword[33] = ""; static char TiffCompressionStr[16] = ""; static char thinLineModeStr[8] = ""; static SplashThinLineMode thinLineMode = splashThinLineDefault; #ifdef UTILS_USE_PTHREADS static int numberOfJobs = 1; #endif // UTILS_USE_PTHREADS static bool quiet = false; static bool progress = false; static bool printVersion = false; static bool printHelp = false; static const ArgDesc argDesc[] = { { "-f", argInt, &firstPage, 0, "first page to print" }, { "-l", argInt, &lastPage, 0, "last page to print" }, { "-o", argFlag, &printOnlyOdd, 0, "print only odd pages" }, { "-e", argFlag, &printOnlyEven, 0, "print only even pages" }, { "-singlefile", argFlag, &singleFile, 0, "write only the first page and do not add digits" }, { "-scale-dimension-before-rotation", argFlag, &scaleDimensionBeforeRotation, 0, "for rotated pdf, resize dimensions before the rotation" }, { "-r", argFP, &resolution, 0, "resolution, in DPI (default is 150)" }, { "-rx", argFP, &x_resolution, 0, "X resolution, in DPI (default is 150)" }, { "-ry", argFP, &y_resolution, 0, "Y resolution, in DPI (default is 150)" }, { "-scale-to", argInt, &scaleTo, 0, "scales each page to fit within scale-to*scale-to pixel box" }, { "-scale-to-x", argInt, &x_scaleTo, 0, "scales each page horizontally to fit in scale-to-x pixels" }, { "-scale-to-y", argInt, &y_scaleTo, 0, "scales each page vertically to fit in scale-to-y pixels" }, { "-x", argInt, ¶m_x, 0, "x-coordinate of the crop area top left corner" }, { "-y", argInt, ¶m_y, 0, "y-coordinate of the crop area top left corner" }, { "-W", argInt, ¶m_w, 0, "width of crop area in pixels (default is 0)" }, { "-H", argInt, ¶m_h, 0, "height of crop area in pixels (default is 0)" }, { "-sz", argInt, &sz, 0, "size of crop square in pixels (sets W and H)" }, { "-cropbox", argFlag, &useCropBox, 0, "use the crop box rather than media box" }, { "-hide-annotations", argFlag, &hideAnnotations, 0, "do not show annotations" }, { "-mono", argFlag, &mono, 0, "generate a monochrome PBM file" }, { "-gray", argFlag, &gray, 0, "generate a grayscale PGM file" }, #ifdef USE_CMS { "-displayprofile", argGooString, &displayprofilename, 0, "ICC color profile to use as the display profile" }, { "-defaultgrayprofile", argGooString, &defaultgrayprofilename, 0, "ICC color profile to use as the DefaultGray color space" }, { "-defaultrgbprofile", argGooString, &defaultrgbprofilename, 0, "ICC color profile to use as the DefaultRGB color space" }, { "-defaultcmykprofile", argGooString, &defaultcmykprofilename, 0, "ICC color profile to use as the DefaultCMYK color space" }, #endif { "-sep", argString, sep, sizeof(sep), "single character separator between name and page number, default - " }, { "-forcenum", argFlag, &forceNum, 0, "force page number even if there is only one page " }, #ifdef ENABLE_LIBPNG { "-png", argFlag, &png, 0, "generate a PNG file" }, #endif #ifdef ENABLE_LIBJPEG { "-jpeg", argFlag, &jpeg, 0, "generate a JPEG file" }, { "-jpegcmyk", argFlag, &jpegcmyk, 0, "generate a CMYK JPEG file" }, { "-jpegopt", argGooString, &jpegOpt, 0, "jpeg options, with format =[,=]*" }, #endif { "-overprint", argFlag, &overprint, 0, "enable overprint" }, #ifdef ENABLE_LIBTIFF { "-tiff", argFlag, &tiff, 0, "generate a TIFF file" }, { "-tiffcompression", argString, TiffCompressionStr, sizeof(TiffCompressionStr), "set TIFF compression: none, packbits, jpeg, lzw, deflate" }, #endif { "-freetype", argString, enableFreeTypeStr, sizeof(enableFreeTypeStr), "enable FreeType font rasterizer: yes, no" }, { "-thinlinemode", argString, thinLineModeStr, sizeof(thinLineModeStr), "set thin line mode: none, solid, shape. Default: none" }, { "-aa", argString, antialiasStr, sizeof(antialiasStr), "enable font anti-aliasing: yes, no" }, { "-aaVector", argString, vectorAntialiasStr, sizeof(vectorAntialiasStr), "enable vector anti-aliasing: yes, no" }, { "-opw", argString, ownerPassword, sizeof(ownerPassword), "owner password (for encrypted files)" }, { "-upw", argString, userPassword, sizeof(userPassword), "user password (for encrypted files)" }, #ifdef UTILS_USE_PTHREADS { "-j", argInt, &numberOfJobs, 0, "number of jobs to run concurrently" }, #endif // UTILS_USE_PTHREADS { "-q", argFlag, &quiet, 0, "don't print any messages or errors" }, { "-progress", argFlag, &progress, 0, "print progress info" }, { "-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" }, {} }; static constexpr int kOtherError = 99; static bool needToRotate(int angle) { return (angle == 90) || (angle == 270); } static bool parseJpegOptions() { // jpegOpt format is: =,=,... const char *nextOpt = jpegOpt.c_str(); while (nextOpt && *nextOpt) { const char *comma = strchr(nextOpt, ','); GooString opt; if (comma) { opt.Set(nextOpt, static_cast(comma - nextOpt)); nextOpt = comma + 1; } else { opt.Set(nextOpt); nextOpt = nullptr; } // here opt is "= " const char *equal = strchr(opt.c_str(), '='); if (!equal) { fprintf(stderr, "Unknown jpeg option \"%s\"\n", opt.c_str()); return false; } const int iequal = static_cast(equal - opt.c_str()); GooString value(&opt, iequal + 1, opt.getLength() - iequal - 1); opt.del(iequal, opt.getLength() - iequal); // here opt is "" and value is "" if (opt.cmp("quality") == 0) { if (!isInt(value.c_str())) { fprintf(stderr, "Invalid jpeg quality\n"); return false; } jpegQuality = atoi(value.c_str()); if (jpegQuality < 0 || jpegQuality > 100) { fprintf(stderr, "jpeg quality must be between 0 and 100\n"); return false; } } else if (opt.cmp("progressive") == 0) { jpegProgressive = false; if (value.cmp("y") == 0) { jpegProgressive = true; } else if (value.cmp("n") != 0) { fprintf(stderr, "jpeg progressive option must be \"y\" or \"n\"\n"); return false; } } else if (opt.cmp("optimize") == 0 || opt.cmp("optimise") == 0) { jpegOptimize = false; if (value.cmp("y") == 0) { jpegOptimize = true; } else if (value.cmp("n") != 0) { fprintf(stderr, "jpeg optimize option must be \"y\" or \"n\"\n"); return false; } } else { fprintf(stderr, "Unknown jpeg option \"%s\"\n", opt.c_str()); return false; } } return true; } static auto annotDisplayDecideCbk = [](Annot *annot, void *user_data) { return !hideAnnotations; }; static void savePageSlice(PDFDoc *doc, SplashOutputDev *splashOut, int pg, int x, int y, int w, int h, double pg_w, double pg_h, char *ppmFile) { if (w == 0) { w = (int)ceil(pg_w); } if (h == 0) { h = (int)ceil(pg_h); } w = (x + w > pg_w ? (int)ceil(pg_w - x) : w); h = (y + h > pg_h ? (int)ceil(pg_h - y) : h); doc->displayPageSlice(splashOut, pg, x_resolution, y_resolution, 0, !useCropBox, false, false, x, y, w, h, nullptr, nullptr, annotDisplayDecideCbk, nullptr); SplashBitmap *bitmap = splashOut->getBitmap(); SplashBitmap::WriteImgParams params; params.jpegQuality = jpegQuality; params.jpegProgressive = jpegProgressive; params.jpegOptimize = jpegOptimize; params.tiffCompression = TiffCompressionStr; if (ppmFile != nullptr) { SplashError e; if (png) { e = bitmap->writeImgFile(splashFormatPng, ppmFile, x_resolution, y_resolution); } else if (jpeg) { e = bitmap->writeImgFile(splashFormatJpeg, ppmFile, x_resolution, y_resolution, ¶ms); } else if (jpegcmyk) { e = bitmap->writeImgFile(splashFormatJpegCMYK, ppmFile, x_resolution, y_resolution, ¶ms); } else if (tiff) { e = bitmap->writeImgFile(splashFormatTiff, ppmFile, x_resolution, y_resolution, ¶ms); } else { e = bitmap->writePNMFile(ppmFile); } if (e != splashOk) { fprintf(stderr, "Could not write image to %s; exiting\n", ppmFile); exit(EXIT_FAILURE); } } else { #if defined(_WIN32) || defined(__CYGWIN__) _setmode(fileno(stdout), O_BINARY); #endif if (png) { bitmap->writeImgFile(splashFormatPng, stdout, x_resolution, y_resolution); } else if (jpeg) { bitmap->writeImgFile(splashFormatJpeg, stdout, x_resolution, y_resolution, ¶ms); } else if (tiff) { bitmap->writeImgFile(splashFormatTiff, stdout, x_resolution, y_resolution, ¶ms); } else { bitmap->writePNMFile(stdout); } } if (progress) { fprintf(stderr, "%d %d %s\n", pg, lastPage, ppmFile != nullptr ? ppmFile : ""); } } #ifdef UTILS_USE_PTHREADS struct PageJob { PDFDoc *doc; int pg; double pg_w, pg_h; SplashColor *paperColor; char *ppmFile; }; static std::deque pageJobQueue; static pthread_mutex_t pageJobMutex = PTHREAD_MUTEX_INITIALIZER; static void processPageJobs() { while (true) { // pop the next job or exit if queue is empty pthread_mutex_lock(&pageJobMutex); if (pageJobQueue.empty()) { pthread_mutex_unlock(&pageJobMutex); return; } PageJob pageJob = pageJobQueue.front(); pageJobQueue.pop_front(); pthread_mutex_unlock(&pageJobMutex); // process the job SplashOutputDev *splashOut = new SplashOutputDev(mono ? splashModeMono1 : gray ? splashModeMono8 : (jpegcmyk || overprint) ? splashModeDeviceN8 : splashModeRGB8, 4, false, *pageJob.paperColor, true, thinLineMode, splashOverprintPreview); splashOut->setFontAntialias(fontAntialias); splashOut->setVectorAntialias(vectorAntialias); splashOut->setEnableFreeType(enableFreeType); # ifdef USE_CMS splashOut->setDisplayProfile(displayprofile); splashOut->setDefaultGrayProfile(defaultgrayprofile); splashOut->setDefaultRGBProfile(defaultrgbprofile); splashOut->setDefaultCMYKProfile(defaultcmykprofile); # endif splashOut->startDoc(pageJob.doc); savePageSlice(pageJob.doc, splashOut, pageJob.pg, param_x, param_y, param_w, param_h, pageJob.pg_w, pageJob.pg_h, pageJob.ppmFile); delete splashOut; delete[] pageJob.ppmFile; } } #endif // UTILS_USE_PTHREADS int main(int argc, char *argv[]) { GooString *fileName = nullptr; char *ppmRoot = nullptr; char *ppmFile; std::optional ownerPW, userPW; SplashColor paperColor; #ifndef UTILS_USE_PTHREADS SplashOutputDev *splashOut; #else pthread_t *jobs; #endif // UTILS_USE_PTHREADS bool ok; int pg, pg_num_len; double pg_w, pg_h; #ifdef USE_CMS cmsColorSpaceSignature profilecolorspace; #endif Win32Console win32Console(&argc, &argv); // parse args ok = parseArgs(argDesc, &argc, argv); if (mono && gray) { ok = false; } if (resolution != 0.0 && (x_resolution == 150.0 || y_resolution == 150.0)) { x_resolution = resolution; y_resolution = resolution; } if (!ok || argc > 3 || printVersion || printHelp) { fprintf(stderr, "pdftoppm version %s\n", PACKAGE_VERSION); fprintf(stderr, "%s\n", popplerCopyright); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("pdftoppm", "[PDF-file [PPM-file-prefix]]", argDesc); } if (printVersion || printHelp) { return 0; } else { return kOtherError; } } if (argc > 1) { fileName = new GooString(argv[1]); } if (argc == 3) { ppmRoot = argv[2]; } if (antialiasStr[0]) { if (!GlobalParams::parseYesNo2(antialiasStr, &fontAntialias)) { fprintf(stderr, "Bad '-aa' value on command line\n"); } } if (vectorAntialiasStr[0]) { if (!GlobalParams::parseYesNo2(vectorAntialiasStr, &vectorAntialias)) { fprintf(stderr, "Bad '-aaVector' value on command line\n"); } } if (jpegOpt.getLength() > 0) { if (!jpeg) { fprintf(stderr, "Warning: -jpegopt only valid with jpeg output.\n"); } parseJpegOptions(); } // read config file globalParams = std::make_unique(); if (enableFreeTypeStr[0]) { if (!GlobalParams::parseYesNo2(enableFreeTypeStr, &enableFreeType)) { fprintf(stderr, "Bad '-freetype' value on command line\n"); } } if (thinLineModeStr[0]) { if (strcmp(thinLineModeStr, "solid") == 0) { thinLineMode = splashThinLineSolid; } else if (strcmp(thinLineModeStr, "shape") == 0) { thinLineMode = splashThinLineShape; } else if (strcmp(thinLineModeStr, "none") != 0) { fprintf(stderr, "Bad '-thinlinemode' value on command line\n"); } } if (quiet) { globalParams->setErrQuiet(quiet); } // open PDF file if (ownerPassword[0]) { ownerPW = GooString(ownerPassword); } if (userPassword[0]) { userPW = GooString(userPassword); } if (fileName == nullptr) { fileName = new GooString("fd://0"); } if (fileName->cmp("-") == 0) { delete fileName; fileName = new GooString("fd://0"); } std::unique_ptr doc(PDFDocFactory().createPDFDoc(*fileName, ownerPW, userPW)); delete fileName; if (!doc->isOk()) { return 1; } // get page range if (firstPage < 1) { firstPage = 1; } if (singleFile && lastPage < 1) { lastPage = firstPage; } if (lastPage < 1 || lastPage > doc->getNumPages()) { lastPage = doc->getNumPages(); } if (lastPage < firstPage) { fprintf(stderr, "Wrong page range given: the first page (%d) can not be after the last page (%d).\n", firstPage, lastPage); return kOtherError; } // 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))) { fprintf(stderr, "Invalid even/odd page selection, no pages match criteria.\n"); return kOtherError; } if (singleFile && firstPage < lastPage) { if (!quiet) { fprintf(stderr, "Warning: Single file will write only the first of the %d pages.\n", lastPage + 1 - firstPage); } lastPage = firstPage; } // write PPM files if (jpegcmyk || overprint) { splashOverprintPreview = true; splashClearColor(paperColor); } else { paperColor[0] = 255; paperColor[1] = 255; paperColor[2] = 255; } #ifdef USE_CMS if (!displayprofilename.toStr().empty()) { displayprofile = make_GfxLCMSProfilePtr(cmsOpenProfileFromFile(displayprofilename.c_str(), "r")); if (!displayprofile) { fprintf(stderr, "Could not open the ICC profile \"%s\".\n", displayprofilename.c_str()); return kOtherError; } if (!cmsIsIntentSupported(displayprofile.get(), INTENT_RELATIVE_COLORIMETRIC, LCMS_USED_AS_OUTPUT) && !cmsIsIntentSupported(displayprofile.get(), INTENT_ABSOLUTE_COLORIMETRIC, LCMS_USED_AS_OUTPUT) && !cmsIsIntentSupported(displayprofile.get(), INTENT_SATURATION, LCMS_USED_AS_OUTPUT) && !cmsIsIntentSupported(displayprofile.get(), INTENT_PERCEPTUAL, LCMS_USED_AS_OUTPUT)) { fprintf(stderr, "ICC profile \"%s\" is not an output profile.\n", displayprofilename.c_str()); return kOtherError; } profilecolorspace = cmsGetColorSpace(displayprofile.get()); // Note: In contrast to pdftops we do not fail if a non-matching ICC profile is supplied. // Doing so would be pretentious, since SplashOutputDev by default assumes sRGB, even for // the CMYK and Mono cases. if (jpegcmyk || overprint) { if (profilecolorspace != cmsSigCmykData) { fprintf(stderr, "Warning: Supplied ICC profile \"%s\" is not a CMYK profile.\n", displayprofilename.c_str()); } } else if (mono || gray) { if (profilecolorspace != cmsSigGrayData) { fprintf(stderr, "Warning: Supplied ICC profile \"%s\" is not a monochrome profile.\n", displayprofilename.c_str()); } } else { if (profilecolorspace != cmsSigRgbData) { fprintf(stderr, "Warning: Supplied ICC profile \"%s\" is not a RGB profile.\n", displayprofilename.c_str()); } } } if (!defaultgrayprofilename.toStr().empty()) { defaultgrayprofile = make_GfxLCMSProfilePtr(cmsOpenProfileFromFile(defaultgrayprofilename.c_str(), "r")); if (!checkICCProfile(defaultgrayprofile, defaultgrayprofilename.c_str(), LCMS_USED_AS_INPUT, cmsSigGrayData)) { return kOtherError; } } if (!defaultrgbprofilename.toStr().empty()) { defaultrgbprofile = make_GfxLCMSProfilePtr(cmsOpenProfileFromFile(defaultrgbprofilename.c_str(), "r")); if (!checkICCProfile(defaultrgbprofile, defaultrgbprofilename.c_str(), LCMS_USED_AS_INPUT, cmsSigRgbData)) { return kOtherError; } } if (!defaultcmykprofilename.toStr().empty()) { defaultcmykprofile = make_GfxLCMSProfilePtr(cmsOpenProfileFromFile(defaultcmykprofilename.c_str(), "r")); if (!checkICCProfile(defaultcmykprofile, defaultcmykprofilename.c_str(), LCMS_USED_AS_INPUT, cmsSigCmykData)) { return kOtherError; } } #endif #ifndef UTILS_USE_PTHREADS splashOut = new SplashOutputDev(mono ? splashModeMono1 : gray ? splashModeMono8 : (jpegcmyk || overprint) ? splashModeDeviceN8 : splashModeRGB8, 4, false, paperColor, true, thinLineMode, splashOverprintPreview); splashOut->setFontAntialias(fontAntialias); splashOut->setVectorAntialias(vectorAntialias); splashOut->setEnableFreeType(enableFreeType); # ifdef USE_CMS splashOut->setDisplayProfile(displayprofile); splashOut->setDefaultGrayProfile(defaultgrayprofile); splashOut->setDefaultRGBProfile(defaultrgbprofile); splashOut->setDefaultCMYKProfile(defaultcmykprofile); # endif splashOut->startDoc(doc.get()); #endif // UTILS_USE_PTHREADS if (sz != 0) { param_w = param_h = sz; } pg_num_len = numberOfCharacters(doc->getNumPages()); for (pg = firstPage; pg <= lastPage; ++pg) { if (printOnlyEven && pg % 2 == 1) { continue; } if (printOnlyOdd && pg % 2 == 0) { continue; } if (useCropBox) { pg_w = doc->getPageCropWidth(pg); pg_h = doc->getPageCropHeight(pg); } else { pg_w = doc->getPageMediaWidth(pg); pg_h = doc->getPageMediaHeight(pg); } if (scaleDimensionBeforeRotation && needToRotate(doc->getPageRotate(pg))) { std::swap(pg_w, pg_h); } // Handle requests for specific image size if (scaleTo != 0) { if (pg_w > pg_h) { resolution = (72.0 * scaleTo) / pg_w; pg_w = scaleTo; pg_h = pg_h * (resolution / 72.0); } else { resolution = (72.0 * scaleTo) / pg_h; pg_h = scaleTo; pg_w = pg_w * (resolution / 72.0); } x_resolution = y_resolution = resolution; } else { if (x_scaleTo > 0) { x_resolution = (72.0 * x_scaleTo) / pg_w; pg_w = x_scaleTo; if (y_scaleTo == -1) { y_resolution = x_resolution; } } if (y_scaleTo > 0) { y_resolution = (72.0 * y_scaleTo) / pg_h; pg_h = y_scaleTo; if (x_scaleTo == -1) { x_resolution = y_resolution; } } // No specific image size requested---compute the size from the resolution if (x_scaleTo <= 0) { pg_w = pg_w * x_resolution / 72.0; } if (y_scaleTo <= 0) { pg_h = pg_h * y_resolution / 72.0; } } if (!scaleDimensionBeforeRotation && needToRotate(doc->getPageRotate(pg))) { std::swap(pg_w, pg_h); } if (ppmRoot != nullptr) { const char *ext = png ? "png" : (jpeg || jpegcmyk) ? "jpg" : tiff ? "tif" : mono ? "pbm" : gray ? "pgm" : "ppm"; if (singleFile && !forceNum) { ppmFile = new char[strlen(ppmRoot) + 1 + strlen(ext) + 1]; sprintf(ppmFile, "%s.%s", ppmRoot, ext); } else { ppmFile = new char[strlen(ppmRoot) + 1 + pg_num_len + 1 + strlen(ext) + 1]; sprintf(ppmFile, "%s%s%0*d.%s", ppmRoot, sep, pg_num_len, pg, ext); } } else { ppmFile = nullptr; } #ifndef UTILS_USE_PTHREADS // process job in main thread savePageSlice(doc.get(), splashOut, pg, param_x, param_y, param_w, param_h, pg_w, pg_h, ppmFile); delete[] ppmFile; #else // queue job for worker threads PageJob pageJob = { .doc = doc.get(), .pg = pg, .pg_w = pg_w, .pg_h = pg_h, .paperColor = &paperColor, .ppmFile = ppmFile }; pageJobQueue.push_back(pageJob); #endif // UTILS_USE_PTHREADS } #ifndef UTILS_USE_PTHREADS delete splashOut; #else // spawn worker threads and wait on them jobs = (pthread_t *)malloc(numberOfJobs * sizeof(pthread_t)); for (int i = 0; i < numberOfJobs; ++i) { if (pthread_create(&jobs[i], NULL, (void *(*)(void *))processPageJobs, NULL) != 0) { fprintf(stderr, "pthread_create() failed with errno: %d\n", errno); exit(EXIT_FAILURE); } } for (int i = 0; i < numberOfJobs; ++i) { if (pthread_join(jobs[i], NULL) != 0) { fprintf(stderr, "pthread_join() failed with errno: %d\n", errno); exit(EXIT_FAILURE); } } free(jobs); #endif // UTILS_USE_PTHREADS return 0; } poppler-24.02.0/utils/pdftops.1000066400000000000000000000216771455701731300162640ustar00rootroot00000000000000.\" Copyright 1996-2011 Glyph & Cog, LLC .TH pdftops 1 "15 August 2011" .SH NAME pdftops \- Portable Document Format (PDF) to PostScript converter (version 3.03) .SH SYNOPSIS .B pdftops [options] .RI .RI [] .SH DESCRIPTION .B Pdftops converts Portable Document Format (PDF) files to PostScript so they can be printed. .PP Pdftops reads the PDF file, .IR PDF-file , and writes a PostScript file, .IR PS-file . If .I PS-file is not specified, pdftops converts .I file.pdf to .I file.ps (or .I file.eps with the \-eps option). If .I PS-file is \'-', the PostScript is sent to stdout. If .I PDF-file is \'-', Pdftops reads the PDF file from stdin. .SH OPTIONS .TP .BI \-f " number" Specifies the first page to print. .TP .BI \-l " number" Specifies the last page to print. .TP .B \-level1 Generate Level 1 PostScript. The resulting PostScript files will be significantly larger (if they contain images), but will print on Level 1 printers. This also converts all images to black and white. No more than one of the PostScript level options (\-level1, \-level1sep, \-level2, \-level2sep, \-level3, \-level3sep) may be given. .TP .B \-level1sep Generate Level 1 separable PostScript. All colors are converted to CMYK. Images are written with separate stream data for the four components. .TP .B \-level2 Generate Level 2 PostScript. Level 2 supports color images and image compression. This is the default setting. .TP .B \-level2sep Generate Level 2 separable PostScript. All colors are converted to CMYK. The PostScript separation convention operators are used to handle custom (spot) colors. .TP .B \-level3 Generate Level 3 PostScript. This enables all Level 2 features plus CID font embedding. .TP .B \-level3sep Generate Level 3 separable PostScript. The separation handling is the same as for \-level2sep. .TP .B \-eps Generate an Encapsulated PostScript (EPS) file. An EPS file contains a single image, so if you use this option with a multi-page PDF file, you must use \-f and \-l to specify a single page. No more than one of the mode options (\-eps, \-form) may be given. .TP .B \-form Generate a PostScript form which can be imported by software that understands forms. A form contains a single page, so if you use this option with a multi-page PDF file, you must use \-f and \-l to specify a single page. The \-level1 option cannot be used with \-form. No more than one of the mode options (\-eps, \-form) may be given. .TP .B \-opi Generate OPI comments for all images and forms which have OPI information. (This option is only available if pdftops was compiled with OPI support.) .TP .B \-binary Write binary data in Level 1 PostScript. By default, pdftops writes hex-encoded data in Level 1 PostScript. Binary data is non-standard in Level 1 PostScript but reduces the file size and can be useful when Level 1 PostScript is required only for its restricted use of PostScript operators. .TP .BI \-r " number" Set the resolution in DPI when pdftops rasterizes images with transparencies or, for Level 1 PostScript, when pdftops rasterizes images with color masks. By default, pdftops rasterizes images to 300 DPI. .TP .B \-noembt1 By default, any Type 1 fonts which are embedded in the PDF file are copied into the PostScript file. This option causes pdftops to substitute base fonts instead. Embedded fonts make PostScript files larger, but may be necessary for readable output. .TP .B \-noembtt By default, any TrueType fonts which are embedded in the PDF file are copied into the PostScript file. This option causes pdftops to substitute base fonts instead. Embedded fonts make PostScript files larger, but may be necessary for readable output. Also, some PostScript interpreters do not have TrueType rasterizers. .TP .B \-noembcidps By default, any CID PostScript fonts which are embedded in the PDF file are copied into the PostScript file. This option disables that embedding. No attempt is made to substitute for non-embedded CID PostScript fonts. .TP .B \-noembcidtt By default, any CID TrueType fonts which are embedded in the PDF file are copied into the PostScript file. This option disables that embedding. No attempt is made to substitute for non-embedded CID TrueType fonts. .TP .B \-passfonts By default, references to non-embedded 8-bit fonts in the PDF file are substituted with the closest "Helvetica", "Times-Roman", or "Courier" font. This option passes references to non-embedded fonts through to the PostScript file. .TP .BI \-aaRaster " yes | no" Enable or disable raster anti-aliasing. This defaults to "no". pdftops may need to rasterize transparencies and pattern image masks in the PDF. If the PostScript will be printed, leave \-aaRaster disabled and set \-r to the resolution of the printer. If the PostScript will be viewed, enabling \-aaRaster may make rasterized text easier to read. .TP .BI \-rasterize " always | never | whenneeded" By default, pdftops rasterizes pages as needed, for example, if they contain transparencies. To force rasterization, set \-rasterize to "always". Use this to eliminate fonts. To prevent rasterization, set \-rasterize to "never". This may produce files that display incorrectly. .TP .BI \-processcolorformat " MONO8 | CMYK8 | RGB8" Sets the process color format as it is used during rasterization and transparency reduction. The default depends on the other settings: For \-level1 the default is MONO8, for \-level{1,2,3}sep or \-overprint the default is CMYK8, and in all other cases RGB8 is the default. If \-processcolorprofile is given then \-processcolorformat is inferred from the specified ICC profile. .TP .BI \-processcolorprofile " filename" Sets the ICC profile that is assumed during rasterization and transparency reduction. .TP .BI \-defaultgrayprofile " defaultgrayprofilefile" If poppler is compiled with colour management support, this option sets the DefaultGray color space to the ICC profile stored in defaultgrayprofilefile. .TP .BI \-defaultrgbprofile " defaultrgbprofilefile" If poppler is compiled with colour management support, this option sets the DefaultRGB color space to the ICC profile stored in defaultrgbprofilefile. .TP .BI \-defaultcmykprofile " defaultcmykprofilefile" If poppler is compiled with colour management support, this option sets the DefaultCMYK color space to the ICC profile stored in defaultcmykprofilefile. .TP .B \-optimizecolorspace By default, bitmap images in the PDF pass through to the output PostScript in their original color space, which produces predictable results. This option converts RGB and CMYK images into Gray images if every pixel of the image has equal components. This can fix problems when doing color separations of PDFs that contain embedded black and white images encoded as RGB. .TP .B \-preload preload images and forms .TP .BI \-paper " size" Set the paper size to one of "letter", "legal", "A4", or "A3". This can also be set to "match", which will set the paper size of each page to match the size specified in the PDF file. If none the \-paper, \-paperw, or \-paperh options are specified the default is to match the paper size. .TP .BI \-paperw " size" Set the paper width, in points. .TP .BI \-paperh " size" Set the paper height, in points. .TP .B \-origpagesizes This option is the same as "\-paper match". .TP .B \-nocrop By default, output is cropped to the CropBox specified in the PDF file. This option disables cropping. .TP .B \-expand Expand PDF pages smaller than the paper to fill the paper. By default, these pages are not scaled. .TP .B \-noshrink Don't scale PDF pages which are larger than the paper. By default, pages larger than the paper are shrunk to fit. .TP .B \-nocenter By default, PDF pages smaller than the paper (after any scaling) are centered on the paper. This option causes them to be aligned to the lower-left corner of the paper instead. .TP .B \-duplex Set the Duplex pagedevice entry in the PostScript file. This tells duplex-capable printers to enable duplexing. .TP .BI \-opw " password" Specify the owner password for the PDF file. Providing this will bypass all security restrictions. .TP .BI \-upw " password" Specify the user password for the PDF file. .TP .B \-overprint Enable overprint emulation during rasterization. For \-processcolorformat being CMYK8 and the language level being higher than 2, this option is set to true by default. Note: This option requires \-processcolorformat to be CMYK8. .TP .B \-q Don't print any messages or errors. .TP .B \-v Print copyright and version information. .TP .B \-h Print usage information. .RB ( \-help and .B \-\-help are equivalent.) .SH EXIT CODES The Xpdf tools use the following exit codes: .TP 0 No error. .TP 1 Error opening a PDF file. .TP 2 Error opening an output file. .TP 3 Error related to PDF permissions. .TP 99 Other error. .SH AUTHOR The pdftops software and documentation are copyright 1996-2011 Glyph & Cog, LLC. .SH "SEE ALSO" .BR pdfdetach (1), .BR pdffonts (1), .BR pdfimages (1), .BR pdfinfo (1), .BR pdftocairo (1), .BR pdftohtml (1), .BR pdftoppm (1), .BR pdftotext (1) .BR pdfseparate (1), .BR pdfsig (1), .BR pdfunite (1) poppler-24.02.0/utils/pdftops.cc000066400000000000000000000530221455701731300164760ustar00rootroot00000000000000//======================================================================== // // pdftops.cc // // Copyright 1996-2003 Glyph & Cog, LLC // // Modified for Debian by Hamish Moffatt, 22 May 2002. // //======================================================================== //======================================================================== // // 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) 2007-2008, 2010, 2015, 2017, 2018, 2020-2022 Albert Astals Cid // Copyright (C) 2009 Till Kamppeter // Copyright (C) 2009 Sanjoy Mahajan // Copyright (C) 2009, 2011, 2012, 2014-2016, 2020 William Bader // Copyright (C) 2010 Hib Eris // Copyright (C) 2012 Thomas Freitag // Copyright (C) 2013 Suzuki Toshiya // Copyright (C) 2014, 2017 Adrian Johnson // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019, 2021, 2023 Oliver Sander // Copyright (C) 2020 Philipp Knechtges // Copyright (C) 2021 Hubert Figuiere // // 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 "config.h" #include #include #include #include #include #include "parseargs.h" #include "goo/GooString.h" #include "goo/gmem.h" #include "GlobalParams.h" #include "Object.h" #include "Stream.h" #include "Array.h" #include "Dict.h" #include "XRef.h" #include "Catalog.h" #include "Page.h" #include "PDFDoc.h" #include "PDFDocFactory.h" #include "PSOutputDev.h" #include "Error.h" #include "Win32Console.h" #include "sanitychecks.h" #ifdef USE_CMS # include #endif static bool setPSPaperSize(char *size, int &psPaperWidth, int &psPaperHeight) { if (!strcmp(size, "match")) { psPaperWidth = psPaperHeight = -1; } else if (!strcmp(size, "letter")) { psPaperWidth = 612; psPaperHeight = 792; } else if (!strcmp(size, "legal")) { psPaperWidth = 612; psPaperHeight = 1008; } else if (!strcmp(size, "A4")) { psPaperWidth = 595; psPaperHeight = 842; } else if (!strcmp(size, "A3")) { psPaperWidth = 842; psPaperHeight = 1190; } else { return false; } return true; } static int firstPage = 1; static int lastPage = 0; static bool level1 = false; static bool level1Sep = false; static bool level2 = false; static bool level2Sep = false; static bool level3 = false; static bool level3Sep = false; static bool origPageSizes = false; static bool doEPS = false; static bool doForm = false; #ifdef OPI_SUPPORT static bool doOPI = false; #endif static int splashResolution = 0; static bool psBinary = false; static bool noEmbedT1Fonts = false; static bool noEmbedTTFonts = false; static bool noEmbedCIDPSFonts = false; static bool noEmbedCIDTTFonts = false; static bool fontPassthrough = false; static bool optimizeColorSpace = false; static bool passLevel1CustomColor = false; static char rasterAntialiasStr[16] = ""; static char forceRasterizeStr[16] = ""; static bool preload = false; static char paperSize[15] = ""; static int paperWidth = -1; static int paperHeight = -1; static bool noCrop = false; static bool expand = false; static bool noShrink = false; static bool noCenter = false; static bool duplex = false; static char ownerPassword[33] = "\001"; static char userPassword[33] = "\001"; static bool quiet = false; static bool printVersion = false; static bool printHelp = false; static bool overprint = false; static GooString processcolorformatname; static SplashColorMode processcolorformat; static bool processcolorformatspecified = false; #ifdef USE_CMS static GooString processcolorprofilename; static GfxLCMSProfilePtr processcolorprofile; static GooString defaultgrayprofilename; static GfxLCMSProfilePtr defaultgrayprofile; static GooString defaultrgbprofilename; static GfxLCMSProfilePtr defaultrgbprofile; static GooString defaultcmykprofilename; static GfxLCMSProfilePtr defaultcmykprofile; #endif static const ArgDesc argDesc[] = { { "-f", argInt, &firstPage, 0, "first page to print" }, { "-l", argInt, &lastPage, 0, "last page to print" }, { "-level1", argFlag, &level1, 0, "generate Level 1 PostScript" }, { "-level1sep", argFlag, &level1Sep, 0, "generate Level 1 separable PostScript" }, { "-level2", argFlag, &level2, 0, "generate Level 2 PostScript" }, { "-level2sep", argFlag, &level2Sep, 0, "generate Level 2 separable PostScript" }, { "-level3", argFlag, &level3, 0, "generate Level 3 PostScript" }, { "-level3sep", argFlag, &level3Sep, 0, "generate Level 3 separable PostScript" }, { "-origpagesizes", argFlag, &origPageSizes, 0, "conserve original page sizes" }, { "-eps", argFlag, &doEPS, 0, "generate Encapsulated PostScript (EPS)" }, { "-form", argFlag, &doForm, 0, "generate a PostScript form" }, #ifdef OPI_SUPPORT { "-opi", argFlag, &doOPI, 0, "generate OPI comments" }, #endif { "-r", argInt, &splashResolution, 0, "resolution for rasterization, in DPI (default is 300)" }, { "-binary", argFlag, &psBinary, 0, "write binary data in Level 1 PostScript" }, { "-noembt1", argFlag, &noEmbedT1Fonts, 0, "don't embed Type 1 fonts" }, { "-noembtt", argFlag, &noEmbedTTFonts, 0, "don't embed TrueType fonts" }, { "-noembcidps", argFlag, &noEmbedCIDPSFonts, 0, "don't embed CID PostScript fonts" }, { "-noembcidtt", argFlag, &noEmbedCIDTTFonts, 0, "don't embed CID TrueType fonts" }, { "-passfonts", argFlag, &fontPassthrough, 0, "don't substitute missing fonts" }, { "-aaRaster", argString, rasterAntialiasStr, sizeof(rasterAntialiasStr), "enable anti-aliasing on rasterization: yes, no" }, { "-rasterize", argString, forceRasterizeStr, sizeof(forceRasterizeStr), "control rasterization: always, never, whenneeded" }, { "-processcolorformat", argGooString, &processcolorformatname, 0, "color format that is used during rasterization and transparency reduction: MONO8, RGB8, CMYK8" }, #ifdef USE_CMS { "-processcolorprofile", argGooString, &processcolorprofilename, 0, "ICC color profile to use as the process color profile during rasterization and transparency reduction" }, { "-defaultgrayprofile", argGooString, &defaultgrayprofilename, 0, "ICC color profile to use as the DefaultGray color space" }, { "-defaultrgbprofile", argGooString, &defaultrgbprofilename, 0, "ICC color profile to use as the DefaultRGB color space" }, { "-defaultcmykprofile", argGooString, &defaultcmykprofilename, 0, "ICC color profile to use as the DefaultCMYK color space" }, #endif { "-optimizecolorspace", argFlag, &optimizeColorSpace, 0, "convert gray RGB images to gray color space" }, { "-passlevel1customcolor", argFlag, &passLevel1CustomColor, 0, "pass custom color in level1sep" }, { "-preload", argFlag, &preload, 0, "preload images and forms" }, { "-paper", argString, paperSize, sizeof(paperSize), "paper size (letter, legal, A4, A3, match)" }, { "-paperw", argInt, &paperWidth, 0, "paper width, in points" }, { "-paperh", argInt, &paperHeight, 0, "paper height, in points" }, { "-nocrop", argFlag, &noCrop, 0, "don't crop pages to CropBox" }, { "-expand", argFlag, &expand, 0, "expand pages smaller than the paper size" }, { "-noshrink", argFlag, &noShrink, 0, "don't shrink pages larger than the paper size" }, { "-nocenter", argFlag, &noCenter, 0, "don't center pages smaller than the paper size" }, { "-duplex", argFlag, &duplex, 0, "enable duplex printing" }, { "-opw", argString, ownerPassword, sizeof(ownerPassword), "owner password (for encrypted files)" }, { "-upw", argString, userPassword, sizeof(userPassword), "user password (for encrypted files)" }, { "-overprint", argFlag, &overprint, 0, "enable overprint emulation during rasterization" }, { "-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" }, { "-help", argFlag, &printHelp, 0, "print usage information" }, { "--help", argFlag, &printHelp, 0, "print usage information" }, { "-?", argFlag, &printHelp, 0, "print usage information" }, {} }; int main(int argc, char *argv[]) { std::unique_ptr doc; GooString *fileName; std::string psFileName; PSLevel level; PSOutMode mode; std::optional ownerPW, userPW; PSOutputDev *psOut; bool ok; int exitCode; bool rasterAntialias = false; std::vector pages; #ifdef USE_CMS cmsColorSpaceSignature profilecolorspace; #endif Win32Console win32Console(&argc, &argv); exitCode = 99; // parse args ok = parseArgs(argDesc, &argc, argv); if (!ok || argc < 2 || argc > 3 || printVersion || printHelp) { fprintf(stderr, "pdftops version %s\n", PACKAGE_VERSION); fprintf(stderr, "%s\n", popplerCopyright); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("pdftops", " []", argDesc); } if (printVersion || printHelp) { exit(0); } else { exit(1); } } if ((level1 ? 1 : 0) + (level1Sep ? 1 : 0) + (level2 ? 1 : 0) + (level2Sep ? 1 : 0) + (level3 ? 1 : 0) + (level3Sep ? 1 : 0) > 1) { fprintf(stderr, "Error: use only one of the 'level' options.\n"); exit(1); } if ((doEPS ? 1 : 0) + (doForm ? 1 : 0) > 1) { fprintf(stderr, "Error: use only one of -eps, and -form\n"); exit(1); } if (level1) { level = psLevel1; } else if (level1Sep) { level = psLevel1Sep; } else if (level2Sep) { level = psLevel2Sep; } else if (level3) { level = psLevel3; } else if (level3Sep) { level = psLevel3Sep; } else { level = psLevel2; } if (doForm && level < psLevel2) { fprintf(stderr, "Error: forms are only available with Level 2 output.\n"); exit(1); } mode = doEPS ? psModeEPS : doForm ? psModeForm : psModePS; fileName = new GooString(argv[1]); // read config file globalParams = std::make_unique(); if (origPageSizes) { paperWidth = paperHeight = -1; } if (paperSize[0]) { if (origPageSizes) { fprintf(stderr, "Error: -origpagesizes and -paper may not be used together.\n"); exit(1); } if (!setPSPaperSize(paperSize, paperWidth, paperHeight)) { fprintf(stderr, "Invalid paper size\n"); delete fileName; goto err0; } } if (quiet) { globalParams->setErrQuiet(quiet); } if (!processcolorformatname.toStr().empty()) { if (processcolorformatname.toStr() == "MONO8") { processcolorformat = splashModeMono8; processcolorformatspecified = true; } else if (processcolorformatname.toStr() == "CMYK8") { processcolorformat = splashModeCMYK8; processcolorformatspecified = true; } else if (processcolorformatname.toStr() == "RGB8") { processcolorformat = splashModeRGB8; processcolorformatspecified = true; } else { fprintf(stderr, "Error: Unknown process color format \"%s\".\n", processcolorformatname.c_str()); goto err1; } } #ifdef USE_CMS if (!processcolorprofilename.toStr().empty()) { processcolorprofile = make_GfxLCMSProfilePtr(cmsOpenProfileFromFile(processcolorprofilename.c_str(), "r")); if (!processcolorprofile) { fprintf(stderr, "Error: Could not open the ICC profile \"%s\".\n", processcolorprofilename.c_str()); goto err1; } if (!cmsIsIntentSupported(processcolorprofile.get(), INTENT_RELATIVE_COLORIMETRIC, LCMS_USED_AS_OUTPUT) && !cmsIsIntentSupported(processcolorprofile.get(), INTENT_ABSOLUTE_COLORIMETRIC, LCMS_USED_AS_OUTPUT) && !cmsIsIntentSupported(processcolorprofile.get(), INTENT_SATURATION, LCMS_USED_AS_OUTPUT) && !cmsIsIntentSupported(processcolorprofile.get(), INTENT_PERCEPTUAL, LCMS_USED_AS_OUTPUT)) { fprintf(stderr, "Error: ICC profile \"%s\" is not an output profile.\n", processcolorprofilename.c_str()); goto err1; } profilecolorspace = cmsGetColorSpace(processcolorprofile.get()); if (profilecolorspace == cmsSigCmykData) { if (!processcolorformatspecified) { processcolorformat = splashModeCMYK8; processcolorformatspecified = true; } else if (processcolorformat != splashModeCMYK8) { fprintf(stderr, "Error: Supplied ICC profile \"%s\" is a CMYK profile, but process color format is not CMYK8.\n", processcolorprofilename.c_str()); goto err1; } } else if (profilecolorspace == cmsSigGrayData) { if (!processcolorformatspecified) { processcolorformat = splashModeMono8; processcolorformatspecified = true; } else if (processcolorformat != splashModeMono8) { fprintf(stderr, "Error: Supplied ICC profile \"%s\" is a monochrome profile, but process color format is not monochrome.\n", processcolorprofilename.c_str()); goto err1; } } else if (profilecolorspace == cmsSigRgbData) { if (!processcolorformatspecified) { processcolorformat = splashModeRGB8; processcolorformatspecified = true; } else if (processcolorformat != splashModeRGB8) { fprintf(stderr, "Error: Supplied ICC profile \"%s\" is a RGB profile, but process color format is not RGB.\n", processcolorprofilename.c_str()); goto err1; } } } #endif if (processcolorformatspecified) { if (level1 && processcolorformat != splashModeMono8) { fprintf(stderr, "Error: Setting -level1 requires -processcolorformat MONO8"); goto err1; } else if ((level1Sep || level2Sep || level3Sep || overprint) && processcolorformat != splashModeCMYK8) { fprintf(stderr, "Error: Setting -level1sep/-level2sep/-level3sep/-overprint requires -processcolorformat CMYK8"); goto err1; } } #ifdef USE_CMS if (!defaultgrayprofilename.toStr().empty()) { defaultgrayprofile = make_GfxLCMSProfilePtr(cmsOpenProfileFromFile(defaultgrayprofilename.c_str(), "r")); if (!checkICCProfile(defaultgrayprofile, defaultgrayprofilename.c_str(), LCMS_USED_AS_INPUT, cmsSigGrayData)) { goto err1; } } if (!defaultrgbprofilename.toStr().empty()) { defaultrgbprofile = make_GfxLCMSProfilePtr(cmsOpenProfileFromFile(defaultrgbprofilename.c_str(), "r")); if (!checkICCProfile(defaultrgbprofile, defaultrgbprofilename.c_str(), LCMS_USED_AS_INPUT, cmsSigRgbData)) { goto err1; } } if (!defaultcmykprofilename.toStr().empty()) { defaultcmykprofile = make_GfxLCMSProfilePtr(cmsOpenProfileFromFile(defaultcmykprofilename.c_str(), "r")); if (!checkICCProfile(defaultcmykprofile, defaultcmykprofilename.c_str(), LCMS_USED_AS_INPUT, cmsSigCmykData)) { goto err1; } } #endif // open PDF file if (ownerPassword[0] != '\001') { ownerPW = GooString(ownerPassword); } if (userPassword[0] != '\001') { userPW = GooString(userPassword); } if (fileName->cmp("-") == 0) { delete fileName; fileName = new GooString("fd://0"); } doc = PDFDocFactory().createPDFDoc(*fileName, ownerPW, userPW); if (!doc->isOk()) { exitCode = 1; goto err1; } #ifdef ENFORCE_PERMISSIONS // check for print permission if (!doc->okToPrint()) { error(errNotAllowed, -1, "Printing this document is not allowed."); exitCode = 3; goto err1; } #endif // construct PostScript file name if (argc == 3) { 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 = std::string(fileName->c_str(), fileName->getLength() - 4); } else { psFileName = fileName->toStr(); } psFileName += (doEPS ? ".eps" : ".ps"); } // get page range if (firstPage < 1) { firstPage = 1; } if (lastPage < 1 || lastPage > doc->getNumPages()) { lastPage = doc->getNumPages(); } 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 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 err1; } for (int i = firstPage; i <= lastPage; ++i) { pages.push_back(i); } // write PostScript file 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); } if (expand) { psOut->setPSExpandSmaller(true); } if (noShrink) { psOut->setPSShrinkLarger(false); } if (overprint) { psOut->setOverprintPreview(true); } if (rasterAntialiasStr[0]) { if (!GlobalParams::parseYesNo2(rasterAntialiasStr, &rasterAntialias)) { fprintf(stderr, "Bad '-aaRaster' value on command line\n"); } } if (forceRasterizeStr[0]) { PSForceRasterize forceRasterize = psRasterizeWhenNeeded; if (strcmp(forceRasterizeStr, "whenneeded") == 0) { forceRasterize = psRasterizeWhenNeeded; } else if (strcmp(forceRasterizeStr, "always") == 0) { forceRasterize = psAlwaysRasterize; } else if (strcmp(forceRasterizeStr, "never") == 0) { forceRasterize = psNeverRasterize; } else { fprintf(stderr, "Bad '-rasterize' value on command line\n"); } psOut->setForceRasterize(forceRasterize); } if (splashResolution > 0) { psOut->setRasterResolution(splashResolution); } if (processcolorformatspecified) { psOut->setProcessColorFormat(processcolorformat); } #ifdef USE_CMS psOut->setDisplayProfile(processcolorprofile); psOut->setDefaultGrayProfile(defaultgrayprofile); psOut->setDefaultRGBProfile(defaultrgbprofile); psOut->setDefaultCMYKProfile(defaultcmykprofile); #endif psOut->setEmbedType1(!noEmbedT1Fonts); psOut->setEmbedTrueType(!noEmbedTTFonts); psOut->setEmbedCIDPostScript(!noEmbedCIDPSFonts); psOut->setEmbedCIDTrueType(!noEmbedCIDTTFonts); psOut->setFontPassthrough(fontPassthrough); psOut->setPreloadImagesForms(preload); psOut->setOptimizeColorSpace(optimizeColorSpace); psOut->setPassLevel1CustomColor(passLevel1CustomColor); #ifdef OPI_SUPPORT psOut->setGenerateOPI(doOPI); #endif psOut->setUseBinary(psBinary); psOut->setRasterAntialias(rasterAntialias); if (psOut->isOk()) { for (int i = firstPage; i <= lastPage; ++i) { doc->displayPage(psOut, i, 72, 72, 0, noCrop, !noCrop, true); } } else { delete psOut; exitCode = 2; goto err1; } delete psOut; exitCode = 0; // clean up err1: delete fileName; err0: return exitCode; } poppler-24.02.0/utils/pdftotext.1000066400000000000000000000076341455701731300166230ustar00rootroot00000000000000.\" Copyright 1997-2011 Glyph & Cog, LLC .TH pdftotext 1 "15 August 2011" .SH NAME pdftotext \- Portable Document Format (PDF) to text converter (version 3.03) .SH SYNOPSIS .B pdftotext [options] .RI PDF-file .RI [ text-file ] .SH DESCRIPTION .B Pdftotext converts Portable Document Format (PDF) files to plain text. .PP Pdftotext reads the PDF file, .IR PDF-file , and writes a text file, .IR text-file . If .I text-file is not specified, pdftotext converts .I file.pdf to .IR file.txt . If .I text-file is \'-', the text is sent to stdout. If .I PDF-file is \'-', it reads the PDF file from stdin. .SH OPTIONS .TP .BI \-f " number" Specifies the first page to convert. .TP .BI \-l " number" Specifies the last page to convert. .TP .BI \-r " number" Specifies the resolution, in DPI. The default is 72 DPI. .TP .BI \-x " number" Specifies the x-coordinate of the crop area top left corner .TP .BI \-y " number" Specifies the y-coordinate of the crop area top left corner .TP .BI \-W " number" Specifies the width of crop area in pixels (default is 0) .TP .BI \-H " number" Specifies the height of crop area in pixels (default is 0) .TP .B \-layout Maintain (as best as possible) the original physical layout of the text. The default is to \'undo' physical layout (columns, hyphenation, etc.) and output the text in reading order. .TP .BI \-fixed " number" Assume fixed-pitch (or tabular) text, with the specified character width (in points). This forces physical layout mode. .TP .B \-raw Keep the text in content stream order. This is a hack which often "undoes" column formatting, etc. Use of raw mode is no longer recommended. .TP .B \-nodiag Discard diagonal text (i.e., text that is not close to one of the 0, 90, 180, or 270 degree axes). This is useful for skipping watermarks drawn on body text. .TP .B \-htmlmeta Generate a simple HTML file, including the meta information. This simply wraps the text in
     and 
    and prepends the meta headers. .TP .B \-bbox Generate an XHTML file containing bounding box information for each word in the file. .TP .B \-bbox-layout Generate an XHTML file containing bounding box information for each block, line, and word in the file. .TP .B \-tsv Generate a TSV file containing the bounding box information for each block, line, and word in the file. .TP .B \-cropbox Use the crop box rather than the media box with \-bbox and \-bbox-layout. .TP .BI \-colspacing " number" Specifies how much spacing we allow after a word before considering adjacent text to be a new column, measured as a fraction of the font size. Current default is 0.7, old releases had a 0.3 default. .TP .BI \-enc " encoding-name" Sets the encoding to use for text output. This defaults to "UTF-8". .TP .B \-listenc Lists the available encodings .TP .BI \-eol " unix | dos | mac" Sets the end-of-line convention to use for text output. .TP .B \-nopgbrk Don't insert page breaks (form feed characters) between pages. .TP .BI \-opw " password" Specify the owner password for the PDF file. Providing this will bypass all security restrictions. .TP .BI \-upw " password" Specify the user password for the PDF file. .TP .B \-q Don't print any messages or errors. .TP .B \-v Print copyright and version information. .TP .B \-h Print usage information. .RB ( \-help and .B \-\-help are equivalent.) .SH BUGS Some PDF files contain fonts whose encodings have been mangled beyond recognition. There is no way (short of OCR) to extract text from these files. .SH EXIT CODES The Xpdf tools use the following exit codes: .TP 0 No error. .TP 1 Error opening a PDF file. .TP 2 Error opening an output file. .TP 3 Error related to PDF permissions. .TP 99 Other error. .SH AUTHOR The pdftotext software and documentation are copyright 1996-2011 Glyph & Cog, LLC. .SH "SEE ALSO" .BR pdfdetach (1), .BR pdffonts (1), .BR pdfimages (1), .BR pdfinfo (1), .BR pdftocairo (1), .BR pdftohtml (1), .BR pdftoppm (1), .BR pdftops (1), .BR pdfseparate (1), .BR pdfsig (1), .BR pdfunite (1) poppler-24.02.0/utils/pdftotext.cc000066400000000000000000000626031455701731300170450ustar00rootroot00000000000000//======================================================================== // // pdftotext.cc // // Copyright 1997-2003 Glyph & Cog, LLC // // Modified for Debian by Hamish Moffatt, 22 May 2002. // //======================================================================== //======================================================================== // // 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 Dominic Lachowicz // Copyright (C) 2007-2008, 2010, 2011, 2017-2022 Albert Astals Cid // Copyright (C) 2009 Jan Jockusch // Copyright (C) 2010, 2013 Hib Eris // Copyright (C) 2010 Kenneth Berland // Copyright (C) 2011 Tom Gleason // Copyright (C) 2011 Steven Murdoch // Copyright (C) 2013 Yury G. Kudryashov // Copyright (C) 2013 Suzuki Toshiya // Copyright (C) 2015 Jeremy Echols // 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) 2018 Sanchit Anand // Copyright (C) 2019 Dan Shea // Copyright (C) 2019, 2021 Oliver Sander // Copyright (C) 2021 William Bader // Copyright (C) 2022 kVdNi // // 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 "config.h" #include #include #include #include #include #include "parseargs.h" #include "printencodings.h" #include "goo/GooString.h" #include "goo/gmem.h" #include "GlobalParams.h" #include "Object.h" #include "Stream.h" #include "Array.h" #include "Dict.h" #include "XRef.h" #include "Catalog.h" #include "Page.h" #include "PDFDoc.h" #include "PDFDocFactory.h" #include "TextOutputDev.h" #include "CharTypes.h" #include "UnicodeMap.h" #include "PDFDocEncoding.h" #include "Error.h" #include #include #include #include "Win32Console.h" #include "DateInfo.h" #include static void printInfoString(FILE *f, Dict *infoDict, const char *key, const char *text1, const char *text2, const UnicodeMap *uMap); static void printInfoDate(FILE *f, Dict *infoDict, const char *key, const char *text1, const char *text2); void printDocBBox(FILE *f, PDFDoc *doc, TextOutputDev *textOut, int first, int last); void printWordBBox(FILE *f, PDFDoc *doc, TextOutputDev *textOut, int first, int last); void printTSVBBox(FILE *f, PDFDoc *doc, TextOutputDev *textOut, int first, int last); static int firstPage = 1; static int lastPage = 0; static double resolution = 72.0; static int x = 0; static int y = 0; static int w = 0; static int h = 0; static bool bbox = false; static bool bboxLayout = false; static bool physLayout = false; static bool useCropBox = false; static double colspacing = TextOutputDev::minColSpacing1_default; static double fixedPitch = 0; static bool rawOrder = false; static bool discardDiag = false; static bool htmlMeta = false; static char textEncName[128] = ""; static char textEOLStr[16] = ""; static bool noPageBreaks = false; static char ownerPassword[33] = "\001"; static char userPassword[33] = "\001"; static bool quiet = false; static bool printVersion = false; static bool printHelp = false; static bool printEnc = false; static bool tsvMode = false; static const ArgDesc argDesc[] = { { "-f", argInt, &firstPage, 0, "first page to convert" }, { "-l", argInt, &lastPage, 0, "last page to convert" }, { "-r", argFP, &resolution, 0, "resolution, in DPI (default is 72)" }, { "-x", argInt, &x, 0, "x-coordinate of the crop area top left corner" }, { "-y", argInt, &y, 0, "y-coordinate of the crop area top left corner" }, { "-W", argInt, &w, 0, "width of crop area in pixels (default is 0)" }, { "-H", argInt, &h, 0, "height of crop area in pixels (default is 0)" }, { "-layout", argFlag, &physLayout, 0, "maintain original physical layout" }, { "-fixed", argFP, &fixedPitch, 0, "assume fixed-pitch (or tabular) text" }, { "-raw", argFlag, &rawOrder, 0, "keep strings in content stream order" }, { "-nodiag", argFlag, &discardDiag, 0, "discard diagonal text" }, { "-htmlmeta", argFlag, &htmlMeta, 0, "generate a simple HTML file, including the meta information" }, { "-tsv", argFlag, &tsvMode, 0, "generate a simple TSV file, including the meta information for bounding boxes" }, { "-enc", argString, textEncName, sizeof(textEncName), "output text encoding name" }, { "-listenc", argFlag, &printEnc, 0, "list available encodings" }, { "-eol", argString, textEOLStr, sizeof(textEOLStr), "output end-of-line convention (unix, dos, or mac)" }, { "-nopgbrk", argFlag, &noPageBreaks, 0, "don't insert page breaks between pages" }, { "-bbox", argFlag, &bbox, 0, "output bounding box for each word and page size to html. Sets -htmlmeta" }, { "-bbox-layout", argFlag, &bboxLayout, 0, "like -bbox but with extra layout bounding box data. Sets -htmlmeta" }, { "-cropbox", argFlag, &useCropBox, 0, "use the crop box rather than media box" }, { "-colspacing", argFP, &colspacing, 0, "how much spacing we allow after a word before considering adjacent text to be a new column, as a fraction of the font size (default is 0.7, old releases had a 0.3 default)" }, { "-opw", argString, ownerPassword, sizeof(ownerPassword), "owner password (for encrypted files)" }, { "-upw", argString, userPassword, sizeof(userPassword), "user password (for encrypted files)" }, { "-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" }, { "-help", argFlag, &printHelp, 0, "print usage information" }, { "--help", argFlag, &printHelp, 0, "print usage information" }, { "-?", argFlag, &printHelp, 0, "print usage information" }, {} }; static std::string myStringReplace(const std::string &inString, const std::string &oldToken, const std::string &newToken) { std::string result = inString; size_t foundLoc; int advance = 0; do { foundLoc = result.find(oldToken, advance); if (foundLoc != std::string::npos) { result.replace(foundLoc, oldToken.length(), newToken); advance = foundLoc + newToken.length(); } } while (foundLoc != std::string::npos); return result; } static std::string myXmlTokenReplace(const char *inString) { std::string myString(inString); myString = myStringReplace(myString, "&", "&"); myString = myStringReplace(myString, "'", "'"); myString = myStringReplace(myString, "\"", """); myString = myStringReplace(myString, "<", "<"); myString = myStringReplace(myString, ">", ">"); return myString; } int main(int argc, char *argv[]) { std::unique_ptr doc; std::unique_ptr textFileName; std::optional ownerPW, userPW; FILE *f; const UnicodeMap *uMap; Object info; bool ok; EndOfLineKind textEOL = TextOutputDev::defaultEndOfLine(); Win32Console win32Console(&argc, &argv); // parse args ok = parseArgs(argDesc, &argc, argv); if (bboxLayout) { bbox = true; } if (bbox) { htmlMeta = true; } if (colspacing <= 0 || colspacing > 10) { error(errCommandLine, -1, "Bogus value provided for -colspacing"); return 99; } if (!ok || (argc < 2 && !printEnc) || argc > 3 || printVersion || printHelp) { fprintf(stderr, "pdftotext version %s\n", PACKAGE_VERSION); fprintf(stderr, "%s\n", popplerCopyright); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("pdftotext", " []", argDesc); } if (printVersion || printHelp) { return 0; } return 99; } // read config file globalParams = std::make_unique(); if (printEnc) { printEncodings(); return 0; } GooString fileName(argv[1]); if (fixedPitch) { physLayout = true; } if (textEncName[0]) { globalParams->setTextEncoding(textEncName); } if (textEOLStr[0]) { if (!strcmp(textEOLStr, "unix")) { textEOL = eolUnix; } else if (!strcmp(textEOLStr, "dos")) { textEOL = eolDOS; } else if (!strcmp(textEOLStr, "mac")) { textEOL = eolMac; } else { fprintf(stderr, "Bad '-eol' value on command line\n"); } } if (quiet) { globalParams->setErrQuiet(quiet); } // get mapping to output encoding if (!(uMap = globalParams->getTextEncoding())) { error(errCommandLine, -1, "Couldn't get text encoding"); return 99; } // open PDF file if (ownerPassword[0] != '\001') { ownerPW = GooString(ownerPassword); } if (userPassword[0] != '\001') { userPW = GooString(userPassword); } if (fileName.cmp("-") == 0) { fileName = GooString("fd://0"); } doc = PDFDocFactory().createPDFDoc(fileName, ownerPW, userPW); if (!doc->isOk()) { return 1; } #ifdef ENFORCE_PERMISSIONS // check for copy permission if (!doc->okToCopy()) { error(errNotAllowed, -1, "Copying of text from this document is not allowed."); return 3; } #endif // construct text file name if (argc == 3) { textFileName = std::make_unique(argv[2]); } else if (fileName.cmp("fd://0") == 0) { error(errCommandLine, -1, "You have to provide an output filename when reading from stdin."); return 99; } else { const char *p = fileName.c_str() + fileName.getLength() - 4; if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")) { textFileName = std::make_unique(fileName.c_str(), fileName.getLength() - 4); } else { textFileName.reset(fileName.copy()); } textFileName->append(htmlMeta ? ".html" : ".txt"); } // get page range if (firstPage < 1) { firstPage = 1; } if (lastPage < 1 || lastPage > doc->getNumPages()) { lastPage = doc->getNumPages(); } 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); return 99; } // write HTML header if (htmlMeta) { if (!textFileName->cmp("-")) { f = stdout; } else { if (!(f = fopen(textFileName->c_str(), "wb"))) { error(errIO, -1, "Couldn't open text file '{0:t}'", textFileName.get()); return 2; } } fputs("", f); fputs("\n", f); fputs("\n", f); info = doc->getDocInfo(); if (info.isDict()) { Object obj = info.getDict()->lookup("Title"); if (obj.isString()) { printInfoString(f, info.getDict(), "Title", "", "\n", uMap); } 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"); } fputs("\n", f); fputs("\n", f); if (!bbox) { fputs("
    \n", f);
                if (f != stdout) {
                    fclose(f);
                }
            }
        }
    
        // write text file
        if (htmlMeta && bbox) { // htmlMeta && is superfluous but makes gcc happier
            TextOutputDev textOut(nullptr, physLayout, fixedPitch, rawOrder, htmlMeta, discardDiag);
    
            if (textOut.isOk()) {
                textOut.setTextEOL(textEOL);
                textOut.setMinColSpacing1(colspacing);
                if (noPageBreaks) {
                    textOut.setTextPageBreaks(false);
                }
                if (bboxLayout) {
                    printDocBBox(f, doc.get(), &textOut, firstPage, lastPage);
                } else {
                    printWordBBox(f, doc.get(), &textOut, firstPage, lastPage);
                }
            }
            if (f != stdout) {
                fclose(f);
            }
        } else {
    
            if (tsvMode) {
                TextOutputDev textOut(nullptr, physLayout, fixedPitch, rawOrder, htmlMeta, discardDiag);
                if (!textFileName->cmp("-")) {
                    f = stdout;
                } else {
                    if (!(f = fopen(textFileName->c_str(), "wb"))) {
                        error(errIO, -1, "Couldn't open text file '{0:t}'", textFileName.get());
                        return 2;
                    }
                }
                printTSVBBox(f, doc.get(), &textOut, firstPage, lastPage);
                if (f != stdout) {
                    fclose(f);
                }
            } else {
                TextOutputDev textOut(textFileName->c_str(), physLayout, fixedPitch, rawOrder, htmlMeta, discardDiag);
                if (textOut.isOk()) {
                    textOut.setTextEOL(textEOL);
                    textOut.setMinColSpacing1(colspacing);
                    if (noPageBreaks) {
                        textOut.setTextPageBreaks(false);
                    }
    
                    if ((w == 0) && (h == 0) && (x == 0) && (y == 0)) {
                        doc->displayPages(&textOut, firstPage, lastPage, resolution, resolution, 0, true, false, false);
                    } else {
    
                        for (int page = firstPage; page <= lastPage; ++page) {
                            doc->displayPageSlice(&textOut, page, resolution, resolution, 0, true, false, false, x, y, w, h);
                        }
                    }
    
                } else {
                    return 2;
                }
            }
        }
    
        // write end of HTML file
        if (htmlMeta) {
            if (!textFileName->cmp("-")) {
                f = stdout;
            } else {
                if (!(f = fopen(textFileName->c_str(), "ab"))) {
                    error(errIO, -1, "Couldn't open text file '{0:t}'", textFileName.get());
                    return 2;
                }
            }
            if (!bbox) {
                fputs("
    \n", f); } fputs("\n", f); fputs("\n", f); if (f != stdout) { fclose(f); } } return 0; } static void printInfoString(FILE *f, Dict *infoDict, const char *key, const char *text1, const char *text2, const UnicodeMap *uMap) { const GooString *s1; bool isUnicode; Unicode u; char buf[9]; int i, n; Object obj = infoDict->lookup(key); if (obj.isString()) { fputs(text1, f); s1 = obj.getString(); if ((s1->getChar(0) & 0xff) == 0xfe && (s1->getChar(1) & 0xff) == 0xff) { isUnicode = true; i = 2; } else { isUnicode = false; i = 0; } while (i < obj.getString()->getLength()) { if (isUnicode) { u = ((s1->getChar(i) & 0xff) << 8) | (s1->getChar(i + 1) & 0xff); i += 2; } else { u = pdfDocEncoding[s1->getChar(i) & 0xff]; ++i; } n = uMap->mapUnicode(u, buf, sizeof(buf)); buf[n] = '\0'; const std::string myString = myXmlTokenReplace(buf); fputs(myString.c_str(), f); } fputs(text2, f); } } static void printInfoDate(FILE *f, Dict *infoDict, const char *key, const char *text1, const char *text2) { int year, mon, day, hour, min, sec, tz_hour, tz_minute; char tz; Object obj = infoDict->lookup(key); if (obj.isString()) { const GooString *s = obj.getString(); if (parseDateString(s, &year, &mon, &day, &hour, &min, &sec, &tz, &tz_hour, &tz_minute)) { fputs(text1, f); fprintf(f, "%04d-%02d-%02dT%02d:%02d:%02d", year, mon, day, hour, min, sec); if (tz_hour == 0 && tz_minute == 0) { fprintf(f, "Z"); } else { fprintf(f, "%c%02d", tz, tz_hour); if (tz_minute) { fprintf(f, ":%02d", tz_minute); } } fputs(text2, f); } } } static void printLine(FILE *f, const TextLine *line) { double xMin, yMin, xMax, yMax; double lineXMin = 0, lineYMin = 0, lineXMax = 0, lineYMax = 0; const TextWord *word; std::stringstream wordXML; wordXML << std::fixed << std::setprecision(6); for (word = line->getWords(); word; word = word->getNext()) { word->getBBox(&xMin, &yMin, &xMax, &yMax); if (lineXMin == 0 || lineXMin > xMin) { lineXMin = xMin; } if (lineYMin == 0 || lineYMin > yMin) { lineYMin = yMin; } if (lineXMax < xMax) { lineXMax = xMax; } if (lineYMax < yMax) { lineYMax = yMax; } GooString *wordText = word->getText(); const std::string myString = myXmlTokenReplace(wordText->c_str()); wordXML << " " << myString << "\n"; delete wordText; } fprintf(f, " \n", lineXMin, lineYMin, lineXMax, lineYMax); fputs(wordXML.str().c_str(), f); fputs(" \n", f); } void printDocBBox(FILE *f, PDFDoc *doc, TextOutputDev *textOut, int first, int last) { double xMin, yMin, xMax, yMax; const TextFlow *flow; const TextBlock *blk; const TextLine *line; fprintf(f, "\n"); for (int page = first; page <= last; ++page) { const double wid = useCropBox ? doc->getPageCropWidth(page) : doc->getPageMediaWidth(page); const double hgt = useCropBox ? doc->getPageCropHeight(page) : doc->getPageMediaHeight(page); fprintf(f, " \n", wid, hgt); doc->displayPage(textOut, page, resolution, resolution, 0, !useCropBox, useCropBox, false); for (flow = textOut->getFlows(); flow; flow = flow->getNext()) { fprintf(f, " \n"); for (blk = flow->getBlocks(); blk; blk = blk->getNext()) { blk->getBBox(&xMin, &yMin, &xMax, &yMax); fprintf(f, " \n", xMin, yMin, xMax, yMax); for (line = blk->getLines(); line; line = line->getNext()) { printLine(f, line); } fprintf(f, " \n"); } fprintf(f, " \n"); } fprintf(f, " \n"); } fprintf(f, "\n"); } void printTSVBBox(FILE *f, PDFDoc *doc, TextOutputDev *textOut, int first, int last) { double xMin = 0, yMin = 0, xMax = 0, yMax = 0; const TextFlow *flow; const TextBlock *blk; const TextLine *line; const TextWord *word; int blockNum = 0; int lineNum = 0; int flowNum = 0; int wordNum = 0; const int pageLevel = 1; const int blockLevel = 3; const int lineLevel = 4; const int wordLevel = 5; const int metaConf = -1; const int wordConf = 100; fputs("level\tpage_num\tpar_num\tblock_num\tline_num\tword_num\tleft\ttop\twidth\theight\tconf\ttext\n", f); for (int page = first; page <= last; ++page) { const double wid = useCropBox ? doc->getPageCropWidth(page) : doc->getPageMediaWidth(page); const double hgt = useCropBox ? doc->getPageCropHeight(page) : doc->getPageMediaHeight(page); fprintf(f, "%d\t%d\t%d\t%d\t%d\t%d\t%f\t%f\t%f\t%f\t%d\t###PAGE###\n", pageLevel, page, flowNum, blockNum, lineNum, wordNum, xMin, yMin, wid, hgt, metaConf); doc->displayPage(textOut, page, resolution, resolution, 0, !useCropBox, useCropBox, false); for (flow = textOut->getFlows(); flow; flow = flow->getNext()) { // flow->getBBox(&xMin, &yMin, &xMax, &yMax); // fprintf(f, "%d\t%d\t%d\t%d\t%d\t%f\t%f\t%f\t%f\t\n", page,flowNum,blockNum,lineNum,wordNum,xMin,yMin,wid, hgt); for (blk = flow->getBlocks(); blk; blk = blk->getNext()) { blk->getBBox(&xMin, &yMin, &xMax, &yMax); fprintf(f, "%d\t%d\t%d\t%d\t%d\t%d\t%f\t%f\t%f\t%f\t%d\t###FLOW###\n", blockLevel, page, flowNum, blockNum, lineNum, wordNum, xMin, yMin, xMax - xMin, yMax - yMin, metaConf); for (line = blk->getLines(); line; line = line->getNext()) { double lxMin = 1E+37, lyMin = 1E+37; double lxMax = 0, lyMax = 0; GooString *lineWordsBuffer = new GooString(); for (word = line->getWords(); word; word = word->getNext()) { word->getBBox(&xMin, &yMin, &xMax, &yMax); if (lxMin > xMin) { lxMin = xMin; } if (lxMax < xMax) { lxMax = xMax; } if (lyMin > yMin) { lyMin = yMin; } if (lyMax < yMax) { lyMax = yMax; } lineWordsBuffer->appendf("{0:d}\t{1:d}\t{2:d}\t{3:d}\t{4:d}\t{5:d}\t{6:.2f}\t{7:.2f}\t{8:.2f}\t{9:.2f}\t{10:d}\t{11:t}\n", wordLevel, page, flowNum, blockNum, lineNum, wordNum, xMin, yMin, xMax - xMin, yMax - yMin, wordConf, word->getText()); wordNum++; } // Print Link Bounding Box info fprintf(f, "%d\t%d\t%d\t%d\t%d\t%d\t%f\t%f\t%f\t%f\t%d\t###LINE###\n", lineLevel, page, flowNum, blockNum, lineNum, 0, lxMin, lyMin, lxMax - lxMin, lyMax - lyMin, metaConf); fprintf(f, "%s", lineWordsBuffer->c_str()); delete lineWordsBuffer; wordNum = 0; lineNum++; } lineNum = 0; blockNum++; } blockNum = 0; flowNum++; } flowNum = 0; } } void printWordBBox(FILE *f, PDFDoc *doc, TextOutputDev *textOut, int first, int last) { fprintf(f, "\n"); for (int page = first; page <= last; ++page) { double wid = useCropBox ? doc->getPageCropWidth(page) : doc->getPageMediaWidth(page); double hgt = useCropBox ? doc->getPageCropHeight(page) : doc->getPageMediaHeight(page); fprintf(f, " \n", wid, hgt); doc->displayPage(textOut, page, resolution, resolution, 0, !useCropBox, useCropBox, false); std::unique_ptr wordlist = textOut->makeWordList(); const int word_length = wordlist != nullptr ? wordlist->getLength() : 0; TextWord *word; double xMinA, yMinA, xMaxA, yMaxA; if (word_length == 0) { fprintf(stderr, "no word list\n"); } for (int i = 0; i < word_length; ++i) { word = wordlist->get(i); word->getBBox(&xMinA, &yMinA, &xMaxA, &yMaxA); const std::string myString = myXmlTokenReplace(word->getText()->c_str()); fprintf(f, " %s\n", xMinA, yMinA, xMaxA, yMaxA, myString.c_str()); } fprintf(f, " \n"); } fprintf(f, "\n"); } poppler-24.02.0/utils/pdfunite.1000066400000000000000000000022151455701731300164060ustar00rootroot00000000000000.\" Copyright 2011 The Poppler Developers - http://poppler.freedesktop.org .TH pdfunite 1 "15 September 2011" .SH NAME pdfunite \- Portable Document Format (PDF) page merger .SH SYNOPSIS .B pdfunite [options] .I PDF-sourcefile1..PDF-sourcefilen PDF-destfile .SH DESCRIPTION .B pdfunite merges several PDF (Portable Document Format) files in order of their occurrence on command line to one PDF result file. .TP Neither of the PDF-sourcefile1 to PDF-sourcefilen should be encrypted. .SH OPTIONS .TP .B \-v Print copyright and version information. .TP .B \-h Print usage information. .RB ( \-help and .B \-\-help are equivalent.) .SH EXAMPLE pdfunite sample1.pdf sample2.pdf sample.pdf .TP merges all pages from sample1.pdf and sample2.pdf (in that order) and creates sample.pdf .SH AUTHOR The pdfunite software and documentation are copyright 1996-2004 Glyph & Cog, LLC and copyright 2005-2011 The Poppler Developers - http://poppler.freedesktop.org .SH "SEE ALSO" .BR pdfdetach (1), .BR pdffonts (1), .BR pdfimages (1), .BR pdfinfo (1), .BR pdftocairo (1), .BR pdftohtml (1), .BR pdftoppm (1), .BR pdftops (1), .BR pdftotext (1) .BR pdfseparate (1), .BR pdfsig (1) poppler-24.02.0/utils/pdfunite.cc000066400000000000000000000464041455701731300166430ustar00rootroot00000000000000//======================================================================== // // pdfunite.cc // // This file is licensed under the GPLv2 or later // // 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) 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, 2023 Oliver Sander // Copyright (C) 2022 crt // //======================================================================== #include #include #include "parseargs.h" #include "config.h" #include #include static bool printVersion = false; static bool printHelp = false; static const ArgDesc argDesc[] = { { "-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" }, {} }; static void doMergeNameTree(PDFDoc *doc, XRef *srcXRef, XRef *countRef, int oldRefNum, int newRefNum, Dict *srcNameTree, Dict *mergeNameTree, int numOffset) { Object mergeNameArray = mergeNameTree->lookup("Names"); Object srcNameArray = srcNameTree->lookup("Names"); if (mergeNameArray.isArray() && srcNameArray.isArray()) { Array *newNameArray = new Array(srcXRef); int j = 0; for (int i = 0; i < srcNameArray.arrayGetLength() - 1; i += 2) { const Object &key = srcNameArray.arrayGetNF(i); const Object &value = srcNameArray.arrayGetNF(i + 1); if (key.isString() && value.isRef()) { while (j < mergeNameArray.arrayGetLength() - 1) { const Object &mkey = mergeNameArray.arrayGetNF(j); 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(Ref { mvalue.getRef().num + numOffset, mvalue.getRef().gen })); j += 2; } else if (mkey.getString()->cmp(key.getString()) == 0) { j += 2; } else { break; } } else { j += 2; } } newNameArray->add(Object(new GooString(key.getString()->c_str()))); newNameArray->add(Object(value.getRef())); } } while (j < mergeNameArray.arrayGetLength() - 1) { 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(Ref { mvalue.getRef().num + numOffset, mvalue.getRef().gen })); } j += 2; } srcNameTree->set("Names", Object(newNameArray)); doc->markPageObjects(mergeNameTree, srcXRef, countRef, numOffset, oldRefNum, newRefNum); } else if (srcNameArray.isNull() && mergeNameArray.isArray()) { Array *newNameArray = new Array(srcXRef); for (int i = 0; i < mergeNameArray.arrayGetLength() - 1; i += 2) { 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(Ref { value.getRef().num + numOffset, value.getRef().gen })); } } srcNameTree->add("Names", Object(newNameArray)); doc->markPageObjects(mergeNameTree, srcXRef, countRef, numOffset, oldRefNum, newRefNum); } } static void doMergeNameDict(PDFDoc *doc, XRef *srcXRef, XRef *countRef, int oldRefNum, int newRefNum, Dict *srcNameDict, Dict *mergeNameDict, int numOffset) { for (int i = 0; i < mergeNameDict->getLength(); i++) { const char *key = mergeNameDict->getKey(i); Object mergeNameTree = mergeNameDict->lookup(key); Object srcNameTree = srcNameDict->lookup(key); if (srcNameTree.isDict() && mergeNameTree.isDict()) { doMergeNameTree(doc, srcXRef, countRef, oldRefNum, newRefNum, srcNameTree.getDict(), mergeNameTree.getDict(), numOffset); } else if (srcNameTree.isNull() && mergeNameTree.isDict()) { Object newNameTree(new Dict(srcXRef)); doMergeNameTree(doc, srcXRef, countRef, oldRefNum, newRefNum, newNameTree.getDict(), mergeNameTree.getDict(), numOffset); srcNameDict->add(key, std::move(newNameTree)); } } } static bool doMergeFormDict(Dict *srcFormDict, Dict *mergeFormDict, int numOffset) { Object srcFields = srcFormDict->lookup("Fields"); Object mergeFields = mergeFormDict->lookup("Fields"); if (srcFields.isArray() && mergeFields.isArray()) { for (int i = 0; i < mergeFields.arrayGetLength(); i++) { const Object &value = mergeFields.arrayGetNF(i); if (!value.isRef()) { error(errSyntaxError, -1, "Fields object is not a Ref."); return false; } srcFields.arrayAdd(Object(Ref { value.getRef().num + numOffset, value.getRef().gen })); } } return true; } /////////////////////////////////////////////////////////////////////////// int main(int argc, char *argv[]) /////////////////////////////////////////////////////////////////////////// // Merge PDF files given by arguments 1 to argc-2 and write the result // to the file specified by argument argc-1. /////////////////////////////////////////////////////////////////////////// { int objectsCount = 0; unsigned int numOffset = 0; std::vector pages; std::vector offsets; XRef *yRef, *countRef; FILE *f; OutStream *outStr; int i; int j, rootNum; std::vector> docs; int majorVersion = 0; int minorVersion = 0; char *fileName = argv[argc - 1]; const bool ok = parseArgs(argDesc, &argc, argv); if (!ok || argc < 3 || printVersion || printHelp) { fprintf(stderr, "pdfunite version %s\n", PACKAGE_VERSION); fprintf(stderr, "%s\n", popplerCopyright); fprintf(stderr, "%s\n", xpdfCopyright); if (!printVersion) { printUsage("pdfunite", ".. ", argDesc); } if (printVersion || printHelp) { return 0; } return 99; } globalParams = std::make_unique(); for (i = 1; i < argc - 1; i++) { std::unique_ptr doc = std::make_unique(std::make_unique(argv[i])); if (doc->isOk() && !doc->isEncrypted() && doc->getXRef()->getCatalog().isDict()) { if (doc->getPDFMajorVersion() > majorVersion) { majorVersion = doc->getPDFMajorVersion(); minorVersion = doc->getPDFMinorVersion(); } else if (doc->getPDFMajorVersion() == majorVersion) { if (doc->getPDFMinorVersion() > minorVersion) { minorVersion = doc->getPDFMinorVersion(); } } docs.push_back(std::move(doc)); } else if (doc->isOk()) { if (doc->isEncrypted()) { error(errUnimplemented, -1, "Could not merge encrypted files ('{0:s}')", argv[i]); return -1; } else if (!doc->getXRef()->getCatalog().isDict()) { error(errSyntaxError, -1, "XRef's Catalog is not a dictionary ('{0:s}')", argv[i]); return -1; } } else { error(errSyntaxError, -1, "Could not merge damaged documents ('{0:s}')", argv[i]); return -1; } } if (!(f = fopen(fileName, "wb"))) { error(errIO, -1, "Could not open file '{0:s}'", fileName); return -1; } outStr = new FileOutStream(f, 0); yRef = new XRef(); countRef = new XRef(); yRef->add(0, 65535, 0, false); PDFDoc::writeHeader(outStr, majorVersion, minorVersion); // handle OutputIntents, AcroForm, OCProperties & Names Object intents; Object names; Object afObj; Object ocObj; if (docs.size() >= 1) { Object catObj = docs[0]->getXRef()->getCatalog(); if (!catObj.isDict()) { fclose(f); delete yRef; delete countRef; delete outStr; error(errSyntaxError, -1, "XRef's Catalog is not a dictionary."); return -1; } Dict *catDict = catObj.getDict(); intents = catDict->lookup("OutputIntents"); afObj = catDict->lookupNF("AcroForm").copy(); Ref *refPage = docs[0]->getCatalog()->getPageRef(1); if (!afObj.isNull() && refPage) { docs[0]->markAcroForm(&afObj, yRef, countRef, 0, refPage->num, refPage->num); } ocObj = catDict->lookupNF("OCProperties").copy(); if (!ocObj.isNull() && ocObj.isDict() && refPage) { docs[0]->markPageObjects(ocObj.getDict(), yRef, countRef, 0, refPage->num, refPage->num); } names = catDict->lookup("Names"); if (!names.isNull() && names.isDict() && refPage) { docs[0]->markPageObjects(names.getDict(), yRef, countRef, 0, refPage->num, refPage->num); } if (intents.isArray() && intents.arrayGetLength() > 0) { for (i = 1; i < (int)docs.size(); i++) { Object pagecatObj = docs[i]->getXRef()->getCatalog(); Dict *pagecatDict = pagecatObj.getDict(); Object pageintents = pagecatDict->lookup("OutputIntents"); if (pageintents.isArray() && pageintents.arrayGetLength() > 0) { for (j = intents.arrayGetLength() - 1; j >= 0; j--) { Object intent = intents.arrayGet(j, 0); if (intent.isDict()) { Object idf = intent.dictLookup("OutputConditionIdentifier"); if (idf.isString()) { const GooString *gidf = idf.getString(); bool removeIntent = true; for (int k = 0; k < pageintents.arrayGetLength(); k++) { Object pgintent = pageintents.arrayGet(k, 0); if (pgintent.isDict()) { Object pgidf = pgintent.dictLookup("OutputConditionIdentifier"); if (pgidf.isString()) { const GooString *gpgidf = pgidf.getString(); if (gpgidf->cmp(gidf) == 0) { removeIntent = false; break; } } } } if (removeIntent) { intents.arrayRemove(j); error(errSyntaxWarning, -1, "Output intent {0:s} missing in pdf {1:s}, removed", gidf->c_str(), docs[i]->getFileName()->c_str()); } } else { intents.arrayRemove(j); error(errSyntaxWarning, -1, "Invalid output intent dict, missing required OutputConditionIdentifier"); } } else { intents.arrayRemove(j); } } } else { error(errSyntaxWarning, -1, "Output intents differs, remove them all"); break; } } } if (intents.isArray() && intents.arrayGetLength() > 0) { for (j = intents.arrayGetLength() - 1; j >= 0; j--) { Object intent = intents.arrayGet(j, 0); if (intent.isDict()) { docs[0]->markPageObjects(intent.getDict(), yRef, countRef, numOffset, 0, 0); } else { intents.arrayRemove(j); } } } } for (i = 0; i < (int)docs.size(); i++) { for (j = 1; j <= docs[i]->getNumPages(); j++) { if (!docs[i]->getCatalog()->getPage(j)) { continue; } const PDFRectangle *cropBox = nullptr; if (docs[i]->getCatalog()->getPage(j)->isCropped()) { cropBox = docs[i]->getCatalog()->getPage(j)->getCropBox(); } if (!docs[i]->replacePageDict(j, docs[i]->getCatalog()->getPage(j)->getRotate(), docs[i]->getCatalog()->getPage(j)->getMediaBox(), cropBox)) { fclose(f); delete yRef; delete countRef; delete outStr; error(errSyntaxError, -1, "PDFDoc::replacePageDict failed."); return -1; } Ref *refPage = docs[i]->getCatalog()->getPageRef(j); Object page = docs[i]->getXRef()->fetch(*refPage); Dict *pageDict = page.getDict(); Object *resDict = docs[i]->getCatalog()->getPage(j)->getResourceDictObject(); if (resDict->isDict()) { pageDict->set("Resources", resDict->copy()); } pages.push_back(std::move(page)); offsets.push_back(numOffset); docs[i]->markPageObjects(pageDict, yRef, countRef, numOffset, refPage->num, refPage->num); Object annotsObj = pageDict->lookupNF("Annots").copy(); if (!annotsObj.isNull()) { docs[i]->markAnnotations(&annotsObj, yRef, countRef, numOffset, refPage->num, refPage->num); } } Object pageCatObj = docs[i]->getXRef()->getCatalog(); if (!pageCatObj.isDict()) { fclose(f); delete yRef; delete countRef; delete outStr; error(errSyntaxError, -1, "XRef's Catalog is not a dictionary."); return -1; } Dict *pageCatDict = pageCatObj.getDict(); Object pageNames = pageCatDict->lookup("Names"); if (!pageNames.isNull() && pageNames.isDict()) { if (!names.isDict()) { names = Object(new Dict(yRef)); } doMergeNameDict(docs[i].get(), yRef, countRef, 0, 0, names.getDict(), pageNames.getDict(), numOffset); } Object pageForm = pageCatDict->lookup("AcroForm"); if (i > 0 && !pageForm.isNull() && pageForm.isDict()) { if (afObj.isNull()) { afObj = pageCatDict->lookupNF("AcroForm").copy(); } else if (afObj.isDict()) { if (!doMergeFormDict(afObj.getDict(), pageForm.getDict(), numOffset)) { fclose(f); delete yRef; delete countRef; delete outStr; return -1; } } } objectsCount += docs[i]->writePageObjects(outStr, yRef, numOffset, true); numOffset = yRef->getNumObjects() + 1; } rootNum = yRef->getNumObjects() + 1; yRef->add(rootNum, 0, outStr->getPos(), true); outStr->printf("%d 0 obj\n", rootNum); outStr->printf("<< /Type /Catalog /Pages %d 0 R", rootNum + 1); // insert OutputIntents if (intents.isArray() && intents.arrayGetLength() > 0) { outStr->printf(" /OutputIntents ["); for (j = 0; j < intents.arrayGetLength(); j++) { Object intent = intents.arrayGet(j, 0); if (intent.isDict()) { PDFDoc::writeObject(&intent, outStr, yRef, 0, nullptr, cryptRC4, 0, 0, 0); } } outStr->printf("]"); } // insert AcroForm if (!afObj.isNull()) { outStr->printf(" /AcroForm "); PDFDoc::writeObject(&afObj, outStr, yRef, 0, nullptr, cryptRC4, 0, 0, 0); } // insert OCProperties if (!ocObj.isNull() && ocObj.isDict()) { outStr->printf(" /OCProperties "); PDFDoc::writeObject(&ocObj, outStr, yRef, 0, nullptr, cryptRC4, 0, 0, 0); } // insert Names if (!names.isNull() && names.isDict()) { outStr->printf(" /Names "); PDFDoc::writeObject(&names, outStr, yRef, 0, nullptr, cryptRC4, 0, 0, 0); } outStr->printf(">>\nendobj\n"); objectsCount++; yRef->add(rootNum + 1, 0, outStr->getPos(), true); outStr->printf("%d 0 obj\n", rootNum + 1); outStr->printf("<< /Type /Pages /Kids ["); for (j = 0; j < (int)pages.size(); j++) { outStr->printf(" %d 0 R", rootNum + j + 2); } outStr->printf(" ] /Count %zd >>\nendobj\n", pages.size()); objectsCount++; for (i = 0; i < (int)pages.size(); i++) { yRef->add(rootNum + i + 2, 0, outStr->getPos(), true); outStr->printf("%d 0 obj\n", rootNum + i + 2); outStr->printf("<< "); Dict *pageDict = pages[i].getDict(); for (j = 0; j < pageDict->getLength(); j++) { if (j > 0) { outStr->printf(" "); } const char *key = pageDict->getKey(j); Object value = pageDict->getValNF(j).copy(); if (strcmp(key, "Parent") == 0) { outStr->printf("/Parent %d 0 R", rootNum + 1); } else { outStr->printf("/%s ", key); PDFDoc::writeObject(&value, outStr, yRef, offsets[i], nullptr, cryptRC4, 0, 0, 0); } } outStr->printf(" >>\nendobj\n"); objectsCount++; } Goffset uxrefOffset = outStr->getPos(); Ref ref; ref.num = rootNum; ref.gen = 0; Object trailerDict = PDFDoc::createTrailerDict(objectsCount, false, 0, &ref, yRef, fileName, outStr->getPos()); PDFDoc::writeXRefTableTrailer(std::move(trailerDict), yRef, true, // write all entries according to ISO 32000-1, 7.5.4 Cross-Reference Table: "For a file that has never been incrementally updated, the cross-reference section shall // contain only one subsection, whose object numbering begins at 0." uxrefOffset, outStr, yRef); outStr->close(); delete outStr; fclose(f); delete yRef; delete countRef; return 0; } poppler-24.02.0/utils/printencodings.cc000066400000000000000000000025241455701731300200460ustar00rootroot00000000000000/* * Copyright (C) 2008, 2019, 2021, Albert Astals Cid * Copyright (C) 2017, Adrian Johnson * Copyright (C) 2018, Adam Reichold * Copyright (C) 2019, Oliver Sander * * 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 "printencodings.h" #include "GlobalParams.h" #include "goo/GooString.h" void printEncodings() { std::vector encNames = globalParams->getEncodingNames(); std::sort(encNames.begin(), encNames.end()); printf("Available encodings are:\n"); for (const std::string &enc : encNames) { printf("%s\n", enc.c_str()); } } poppler-24.02.0/utils/printencodings.h000066400000000000000000000015261455701731300177110ustar00rootroot00000000000000/* * Copyright (C) 2008, 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 * 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 PRINTENCODINGS_H #define PRINTENCODINGS_H void printEncodings(); #endif poppler-24.02.0/utils/sanitychecks.cc000066400000000000000000000043341455701731300175110ustar00rootroot00000000000000//======================================================================== // // sanitychecks.cc // // This file is licensed under the GPLv2 or later // // Copyright (C) 2020 Philipp Knechtges // // 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 #include #include #include #include #include "sanitychecks.h" #ifdef USE_CMS bool checkICCProfile(const GfxLCMSProfilePtr &profile, const char *filename, cmsUInt32Number UsedDirection, cmsColorSpaceSignature expectedColorSpace) { if (!profile) { fprintf(stderr, "Could not open the ICC profile \"%s\".\n", filename); return false; } if (!cmsIsIntentSupported(profile.get(), INTENT_RELATIVE_COLORIMETRIC, UsedDirection) && !cmsIsIntentSupported(profile.get(), INTENT_ABSOLUTE_COLORIMETRIC, UsedDirection) && !cmsIsIntentSupported(profile.get(), INTENT_SATURATION, UsedDirection) && !cmsIsIntentSupported(profile.get(), INTENT_PERCEPTUAL, LCMS_USED_AS_OUTPUT)) { if (UsedDirection == LCMS_USED_AS_OUTPUT) { fprintf(stderr, "ICC profile \"%s\" is not an output profile.\n", filename); } else if (UsedDirection == LCMS_USED_AS_INPUT) { fprintf(stderr, "ICC profile \"%s\" is not an input profile.\n", filename); } else { fprintf(stderr, "ICC profile \"%s\" is not suitable.\n", filename); } return false; } auto profilecolorspace = cmsGetColorSpace(profile.get()); if (profilecolorspace != expectedColorSpace) { if (expectedColorSpace == cmsSigCmykData) { fprintf(stderr, "Supplied ICC profile \"%s\" is not a CMYK profile.\n", filename); } else if (expectedColorSpace == cmsSigGrayData) { fprintf(stderr, "Supplied ICC profile \"%s\" is not a monochrome profile.\n", filename); } else if (expectedColorSpace == cmsSigRgbData) { fprintf(stderr, "Supplied ICC profile \"%s\" is not a RGB profile.\n", filename); } return false; } return true; } #endif poppler-24.02.0/utils/sanitychecks.h000066400000000000000000000014631455701731300173530ustar00rootroot00000000000000//======================================================================== // // sanitychecks.h // // This file is licensed under the GPLv2 or later // // Copyright (C) 2020 Philipp Knechtges // // 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 SANITYCHECKS_H #define SANITYCHECKS_H #include "config.h" #ifdef USE_CMS # include # include "GfxState.h" /* * Check the supplied ICC profile for different criteria */ bool checkICCProfile(const GfxLCMSProfilePtr &profile, const char *filename, cmsUInt32Number UsedDirection, cmsColorSpaceSignature expectedColorSpace); #endif #endif