From 017d466e5d0895504fc8711f87806d3bd08d8416 Mon Sep 17 00:00:00 2001 From: David Tardon Date: Thu, 8 Feb 2018 19:13:01 +0100 Subject: [PATCH] Revert "Remove the Qt4 frontend" This reverts commit fb4c69d270a618bb23791e52f46ec73c86574294. --- .gitignore | 2 + CMakeLists.txt | 17 + cmake/modules/FindQt4.cmake | 1311 +++++++ poppler-qt4.pc.cmake | 12 + qt4/.gitignore | 4 + qt4/CMakeLists.txt | 6 + qt4/demos/.gitignore | 4 + qt4/demos/CMakeLists.txt | 28 + qt4/demos/abstractinfodock.cpp | 57 + qt4/demos/abstractinfodock.h | 48 + qt4/demos/documentobserver.cpp | 50 + qt4/demos/documentobserver.h | 50 + qt4/demos/embeddedfiles.cpp | 82 + qt4/demos/embeddedfiles.h | 44 + qt4/demos/fonts.cpp | 72 + qt4/demos/fonts.h | 43 + qt4/demos/info.cpp | 72 + qt4/demos/info.h | 43 + qt4/demos/main_viewer.cpp | 33 + qt4/demos/metadata.cpp | 50 + qt4/demos/metadata.h | 43 + qt4/demos/navigationtoolbar.cpp | 144 + qt4/demos/navigationtoolbar.h | 65 + qt4/demos/optcontent.cpp | 69 + qt4/demos/optcontent.h | 47 + qt4/demos/pageview.cpp | 101 + qt4/demos/pageview.h | 53 + qt4/demos/permissions.cpp | 66 + qt4/demos/permissions.h | 43 + qt4/demos/thumbnails.cpp | 84 + qt4/demos/thumbnails.h | 48 + qt4/demos/toc.cpp | 88 + qt4/demos/toc.h | 43 + qt4/demos/viewer.cpp | 319 ++ qt4/demos/viewer.h | 73 + qt4/src/.gitignore | 9 + qt4/src/ArthurOutputDev.cc | 812 ++++ qt4/src/ArthurOutputDev.h | 170 + qt4/src/CMakeLists.txt | 54 + qt4/src/Doxyfile | 1637 +++++++++ qt4/src/Mainpage.dox | 85 + qt4/src/poppler-annotation-helper.h | 181 + qt4/src/poppler-annotation-private.h | 112 + qt4/src/poppler-annotation.cc | 5089 ++++++++++++++++++++++++++ qt4/src/poppler-annotation.h | 1375 +++++++ qt4/src/poppler-base-converter.cc | 105 + qt4/src/poppler-converter-private.h | 49 + qt4/src/poppler-document.cc | 850 +++++ qt4/src/poppler-embeddedfile-private.h | 42 + qt4/src/poppler-embeddedfile.cc | 135 + qt4/src/poppler-export.h | 20 + qt4/src/poppler-fontinfo.cc | 150 + qt4/src/poppler-form.cc | 416 +++ qt4/src/poppler-form.h | 343 ++ qt4/src/poppler-link-extractor-private.h | 57 + qt4/src/poppler-link-extractor.cc | 84 + qt4/src/poppler-link-private.h | 57 + qt4/src/poppler-link.cc | 710 ++++ qt4/src/poppler-link.h | 641 ++++ qt4/src/poppler-media.cc | 168 + qt4/src/poppler-media.h | 100 + qt4/src/poppler-movie.cc | 110 + qt4/src/poppler-optcontent-private.h | 124 + qt4/src/poppler-optcontent.cc | 456 +++ qt4/src/poppler-optcontent.h | 84 + qt4/src/poppler-page-private.h | 57 + qt4/src/poppler-page-transition-private.h | 28 + qt4/src/poppler-page-transition.cc | 101 + qt4/src/poppler-page-transition.h | 158 + qt4/src/poppler-page.cc | 810 ++++ qt4/src/poppler-pdf-converter.cc | 115 + qt4/src/poppler-private.cc | 296 ++ qt4/src/poppler-private.h | 241 ++ qt4/src/poppler-ps-converter.cc | 280 ++ qt4/src/poppler-qiodeviceoutstream-private.h | 47 + qt4/src/poppler-qiodeviceoutstream.cc | 64 + qt4/src/poppler-qt4.h | 1990 ++++++++++ qt4/src/poppler-sound.cc | 132 + qt4/src/poppler-textbox.cc | 63 + qt4/tests/.gitignore | 33 + qt4/tests/CMakeLists.txt | 67 + qt4/tests/README.unittest | 23 + qt4/tests/check_actualtext.cpp | 33 + qt4/tests/check_attachments.cpp | 157 + qt4/tests/check_dateConversion.cpp | 142 + qt4/tests/check_fonts.cpp | 248 ++ qt4/tests/check_goostring.cpp | 127 + qt4/tests/check_lexer.cpp | 107 + qt4/tests/check_links.cpp | 98 + qt4/tests/check_metadata.cpp | 275 ++ qt4/tests/check_optcontent.cpp | 446 +++ qt4/tests/check_pagelabelinfo.cpp | 43 + qt4/tests/check_pagelayout.cpp | 49 + qt4/tests/check_pagemode.cpp | 73 + qt4/tests/check_password.cpp | 88 + qt4/tests/check_permissions.cpp | 44 + qt4/tests/check_search.cpp | 175 + qt4/tests/check_strings.cpp | 250 ++ qt4/tests/poppler-attachments.cpp | 39 + qt4/tests/poppler-fonts.cpp | 89 + qt4/tests/poppler-forms.cpp | 166 + qt4/tests/poppler-texts.cpp | 40 + qt4/tests/stress-poppler-dir.cpp | 67 + qt4/tests/stress-poppler-qt4.cpp | 74 + qt4/tests/stress-threads-qt4.cpp | 309 ++ qt4/tests/test-password-qt4.cpp | 136 + qt4/tests/test-poppler-qt4.cpp | 235 ++ qt4/tests/test-render-to-file.cpp | 69 + 108 files changed, 25623 insertions(+) create mode 100644 cmake/modules/FindQt4.cmake create mode 100644 poppler-qt4.pc.cmake create mode 100644 qt4/.gitignore create mode 100644 qt4/CMakeLists.txt create mode 100644 qt4/demos/.gitignore create mode 100644 qt4/demos/CMakeLists.txt create mode 100644 qt4/demos/abstractinfodock.cpp create mode 100644 qt4/demos/abstractinfodock.h create mode 100644 qt4/demos/documentobserver.cpp create mode 100644 qt4/demos/documentobserver.h create mode 100644 qt4/demos/embeddedfiles.cpp create mode 100644 qt4/demos/embeddedfiles.h create mode 100644 qt4/demos/fonts.cpp create mode 100644 qt4/demos/fonts.h create mode 100644 qt4/demos/info.cpp create mode 100644 qt4/demos/info.h create mode 100644 qt4/demos/main_viewer.cpp create mode 100644 qt4/demos/metadata.cpp create mode 100644 qt4/demos/metadata.h create mode 100644 qt4/demos/navigationtoolbar.cpp create mode 100644 qt4/demos/navigationtoolbar.h create mode 100644 qt4/demos/optcontent.cpp create mode 100644 qt4/demos/optcontent.h create mode 100644 qt4/demos/pageview.cpp create mode 100644 qt4/demos/pageview.h create mode 100644 qt4/demos/permissions.cpp create mode 100644 qt4/demos/permissions.h create mode 100644 qt4/demos/thumbnails.cpp create mode 100644 qt4/demos/thumbnails.h create mode 100644 qt4/demos/toc.cpp create mode 100644 qt4/demos/toc.h create mode 100644 qt4/demos/viewer.cpp create mode 100644 qt4/demos/viewer.h create mode 100644 qt4/src/.gitignore create mode 100644 qt4/src/ArthurOutputDev.cc create mode 100644 qt4/src/ArthurOutputDev.h create mode 100644 qt4/src/CMakeLists.txt create mode 100644 qt4/src/Doxyfile create mode 100644 qt4/src/Mainpage.dox create mode 100644 qt4/src/poppler-annotation-helper.h create mode 100644 qt4/src/poppler-annotation-private.h create mode 100644 qt4/src/poppler-annotation.cc create mode 100644 qt4/src/poppler-annotation.h create mode 100644 qt4/src/poppler-base-converter.cc create mode 100644 qt4/src/poppler-converter-private.h create mode 100644 qt4/src/poppler-document.cc create mode 100644 qt4/src/poppler-embeddedfile-private.h create mode 100644 qt4/src/poppler-embeddedfile.cc create mode 100644 qt4/src/poppler-export.h create mode 100644 qt4/src/poppler-fontinfo.cc create mode 100644 qt4/src/poppler-form.cc create mode 100644 qt4/src/poppler-form.h create mode 100644 qt4/src/poppler-link-extractor-private.h create mode 100644 qt4/src/poppler-link-extractor.cc create mode 100644 qt4/src/poppler-link-private.h create mode 100644 qt4/src/poppler-link.cc create mode 100644 qt4/src/poppler-link.h create mode 100644 qt4/src/poppler-media.cc create mode 100644 qt4/src/poppler-media.h create mode 100644 qt4/src/poppler-movie.cc create mode 100644 qt4/src/poppler-optcontent-private.h create mode 100644 qt4/src/poppler-optcontent.cc create mode 100644 qt4/src/poppler-optcontent.h create mode 100644 qt4/src/poppler-page-private.h create mode 100644 qt4/src/poppler-page-transition-private.h create mode 100644 qt4/src/poppler-page-transition.cc create mode 100644 qt4/src/poppler-page-transition.h create mode 100644 qt4/src/poppler-page.cc create mode 100644 qt4/src/poppler-pdf-converter.cc create mode 100644 qt4/src/poppler-private.cc create mode 100644 qt4/src/poppler-private.h create mode 100644 qt4/src/poppler-ps-converter.cc create mode 100644 qt4/src/poppler-qiodeviceoutstream-private.h create mode 100644 qt4/src/poppler-qiodeviceoutstream.cc create mode 100644 qt4/src/poppler-qt4.h create mode 100644 qt4/src/poppler-sound.cc create mode 100644 qt4/src/poppler-textbox.cc create mode 100644 qt4/tests/.gitignore create mode 100644 qt4/tests/CMakeLists.txt create mode 100644 qt4/tests/README.unittest create mode 100644 qt4/tests/check_actualtext.cpp create mode 100644 qt4/tests/check_attachments.cpp create mode 100644 qt4/tests/check_dateConversion.cpp create mode 100644 qt4/tests/check_fonts.cpp create mode 100644 qt4/tests/check_goostring.cpp create mode 100644 qt4/tests/check_lexer.cpp create mode 100644 qt4/tests/check_links.cpp create mode 100644 qt4/tests/check_metadata.cpp create mode 100644 qt4/tests/check_optcontent.cpp create mode 100644 qt4/tests/check_pagelabelinfo.cpp create mode 100644 qt4/tests/check_pagelayout.cpp create mode 100644 qt4/tests/check_pagemode.cpp create mode 100644 qt4/tests/check_password.cpp create mode 100644 qt4/tests/check_permissions.cpp create mode 100644 qt4/tests/check_search.cpp create mode 100644 qt4/tests/check_strings.cpp create mode 100644 qt4/tests/poppler-attachments.cpp create mode 100644 qt4/tests/poppler-fonts.cpp create mode 100644 qt4/tests/poppler-forms.cpp create mode 100644 qt4/tests/poppler-texts.cpp create mode 100644 qt4/tests/stress-poppler-dir.cpp create mode 100644 qt4/tests/stress-poppler-qt4.cpp create mode 100644 qt4/tests/stress-threads-qt4.cpp create mode 100644 qt4/tests/test-password-qt4.cpp create mode 100644 qt4/tests/test-poppler-qt4.cpp create mode 100644 qt4/tests/test-render-to-file.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e3d6a17..431059fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,7 @@ set (CMAKE_CXX_EXTENSIONS OFF) # 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_QT4_TESTS "Whether to compile the Qt4 test programs." ON) option(BUILD_QT5_TESTS "Whether to compile the Qt5 test programs." ON) option(BUILD_CPP_TESTS "Whether to compile the CPP test programs." ON) option(ENABLE_SPLASH "Build the Splash graphics backend." ON) @@ -47,6 +48,7 @@ option(ENABLE_UTILS "Compile poppler command line utils." 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_QT4 "Compile poppler qt4 wrapper." ON) option(ENABLE_QT5 "Compile poppler qt5 wrapper." ON) set(ENABLE_LIBOPENJPEG "openjpeg2" CACHE STRING "Use libopenjpeg for JPX streams. Possible values: openjpeg2, unmaintained, none. 'unmaintained' gives you the internal unmaintained decoder. Use at your own risk. 'none' compiles no JPX decoder at all. Default: openjpeg2") set(ENABLE_CMS "lcms2" CACHE STRING "Use color management system. Possible values: lcms2, none. 'none' disables color management system.") @@ -112,6 +114,7 @@ set(OPI_SUPPORT ON) set(TEXTOUT_WORD_LIST ON) # setting the minimum required versions for some components +set(QT4_MIN_VERSION "4.7.0") set(CAIRO_VERSION "1.10.0") set(GLIB_REQUIRED "2.41") @@ -141,6 +144,13 @@ else() message(FATAL_ERROR "Invalid ENABLE_DCTDECODER value.") endif() +if (ENABLE_QT4) + macro_optional_find_package(Qt4) + if (NOT QT4_FOUND) + set(ENABLE_QT4 OFF) + endif() +endif() + if (ENABLE_QT5) find_package(Qt5Core 5.5) # Update QT_DISABLE_DEPRECATED_BEFORE in qt5/CMakeLists.txt when increasing this find_package(Qt5Gui) @@ -661,6 +671,9 @@ if(ENABLE_GLIB) add_subdirectory(glib) endif() add_subdirectory(test) +if(ENABLE_QT4) + add_subdirectory(qt4) +endif() if(ENABLE_QT5) add_subdirectory(qt5) endif() @@ -685,6 +698,9 @@ poppler_create_install_pkgconfig(poppler.pc ${CMAKE_INSTALL_LIBDIR}/pkgconfig) if(ENABLE_SPLASH) poppler_create_install_pkgconfig(poppler-splash.pc ${CMAKE_INSTALL_LIBDIR}/pkgconfig) endif() + if(ENABLE_QT4) + poppler_create_install_pkgconfig(poppler-qt4.pc ${CMAKE_INSTALL_LIBDIR}/pkgconfig) + endif() if(ENABLE_QT5) poppler_create_install_pkgconfig(poppler-qt5.pc ${CMAKE_INSTALL_LIBDIR}/pkgconfig) endif() @@ -706,6 +722,7 @@ if(SPLASH_CMYK) show_end_message("font configuration" ${font_configuration}) show_end_message_yesno("splash output" ENABLE_SPLASH) show_end_message_yesno("cairo output" CAIRO_FOUND) +show_end_message_yesno("qt4 wrapper" ENABLE_QT4) show_end_message_yesno("qt5 wrapper" ENABLE_QT5) show_end_message_yesno("glib wrapper" ENABLE_GLIB) show_end_message_yesno(" introspection" INTROSPECTION_FOUND) diff --git a/cmake/modules/FindQt4.cmake b/cmake/modules/FindQt4.cmake new file mode 100644 index 00000000..79378b0c --- /dev/null +++ b/cmake/modules/FindQt4.cmake @@ -0,0 +1,1311 @@ +# - Find QT 4 +# This module can be used to find Qt4. +# The most important issue is that the Qt4 qmake is available via the system path. +# This qmake is then used to detect basically everything else. +# This module defines a number of key variables and macros. First is +# QT_USE_FILE which is the path to a CMake file that can be included to compile +# Qt 4 applications and libraries. By default, the QtCore and QtGui +# libraries are loaded. This behavior can be changed by setting one or more +# of the following variables to true: +# QT_DONT_USE_QTCORE +# QT_DONT_USE_QTGUI +# QT_USE_QT3SUPPORT +# QT_USE_QTASSISTANT +# QT_USE_QTDESIGNER +# QT_USE_QTMOTIF +# QT_USE_QTMAIN +# QT_USE_QTNETWORK +# QT_USE_QTNSPLUGIN +# QT_USE_QTOPENGL +# QT_USE_QTSQL +# QT_USE_QTXML +# QT_USE_QTSVG +# QT_USE_QTTEST +# QT_USE_QTUITOOLS +# QT_USE_QTDBUS +# QT_USE_QTSCRIPT +# +# All the libraries required are stored in a variable called QT_LIBRARIES. +# Add this variable to your TARGET_LINK_LIBRARIES. +# +# macro QT4_WRAP_CPP(outfiles inputfile ... OPTIONS ...) +# create moc code from a list of files containing Qt class with +# the Q_OBJECT declaration. Options may be given to moc, such as those found +# when executing "moc -help" +# +# macro QT4_WRAP_UI(outfiles inputfile ... OPTIONS ...) +# create code from a list of Qt designer ui files. +# Options may be given to uic, such as those found +# when executing "uic -help" +# +# macro QT4_ADD_RESOURCES(outfiles inputfile ... OPTIONS ...) +# create code from a list of Qt resource files. +# Options may be given to rcc, such as those found +# when executing "rcc -help" +# +# macro QT4_AUTOMOC(inputfile ... ) +# macro QT4_GENERATE_MOC(inputfile outputfile ) +# +# macro QT4_ADD_DBUS_INTERFACE(outfiles interface basename) +# create a the interface header and implementation files with the +# given basename from the given interface xml file and add it to +# the list of sources. +# To disable generating a namespace header, set the source file property +# NO_NAMESPACE to TRUE on the interface file. +# +# macro QT4_ADD_DBUS_INTERFACES(outfiles inputfile ... ) +# create the interface header and implementation files +# for all listed interface xml files +# the name will be automatically determined from the name of the xml file +# To disable generating namespace headers, set the source file property +# NO_NAMESPACE to TRUE for these inputfiles. +# +# macro QT4_ADD_DBUS_ADAPTOR(outfiles xmlfile parentheader parentclassname [basename] [classname]) +# create a dbus adaptor (header and implementation file) from the xml file +# describing the interface, and add it to the list of sources. The adaptor +# forwards the calls to a parent class, defined in parentheader and named +# parentclassname. The name of the generated files will be +# adaptor.{cpp,h} where basename defaults to the basename of the xml file. +# If is provided, then it will be used as the classname of the +# adaptor itself. +# +# macro QT4_GENERATE_DBUS_INTERFACE( header [interfacename] OPTIONS ...) +# generate the xml interface file from the given header. +# If the optional argument interfacename is omitted, the name of the +# interface file is constructed from the basename of the header with +# the suffix .xml appended. +# Options may be given to uic, such as those found when executing "qdbuscpp2xml --help" +# +# QT_FOUND If false, don't try to use Qt. +# QT4_FOUND If false, don't try to use Qt 4. +# +# QT4_QTCORE_FOUND True if QtCore was found. +# QT4_QTGUI_FOUND True if QtGui was found. +# QT4_QT3SUPPORT_FOUND True if Qt3Support was found. +# QT4_QTASSISTANT_FOUND True if QtAssistant was found. +# QT4_QTDBUS_FOUND True if QtDBus was found. +# QT4_QTDESIGNER_FOUND True if QtDesigner was found. +# QT4_QTDESIGNERCOMPONENTS True if QtDesignerComponents was found. +# QT4_QTMOTIF_FOUND True if QtMotif was found. +# QT4_QTNETWORK_FOUND True if QtNetwork was found. +# QT4_QTNSPLUGIN_FOUND True if QtNsPlugin was found. +# QT4_QTOPENGL_FOUND True if QtOpenGL was found. +# QT4_QTSQL_FOUND True if QtSql was found. +# QT4_QTXML_FOUND True if QtXml was found. +# QT4_QTSVG_FOUND True if QtSvg was found. +# QT4_QTSCRIPT_FOUND True if QtScript was found. +# QT4_QTTEST_FOUND True if QtTest was found. +# QT4_QTUITOOLS_FOUND True if QtUiTools was found. +# +# QT4_DEFINITIONS Definitions to use when compiling code that uses Qt. +# +# QT4_INCLUDES List of paths to all include directories of +# Qt4 QT4_INCLUDE_DIR and QT4_QTCORE_INCLUDE_DIR are +# always in this variable even if NOTFOUND, +# all other INCLUDE_DIRS are +# only added if they are found. +# +# QT4_INCLUDE_DIR Path to "include" of Qt4 +# QT4_QT4_INCLUDE_DIR Path to "include/Qt" +# QT4_QT3SUPPORT_INCLUDE_DIR Path to "include/Qt3Support" +# QT4_QTASSISTANT_INCLUDE_DIR Path to "include/QtAssistant" +# QT4_QTCORE_INCLUDE_DIR Path to "include/QtCore" +# QT4_QTDESIGNER_INCLUDE_DIR Path to "include/QtDesigner" +# QT4_QTDESIGNERCOMPONENTS_INCLUDE_DIR Path to "include/QtDesigner" +# QT4_QTDBUS_INCLUDE_DIR Path to "include/QtDBus" +# QT4_QTGUI_INCLUDE_DIR Path to "include/QtGui" +# QT4_QTMOTIF_INCLUDE_DIR Path to "include/QtMotif" +# QT4_QTNETWORK_INCLUDE_DIR Path to "include/QtNetwork" +# QT4_QTNSPLUGIN_INCLUDE_DIR Path to "include/QtNsPlugin" +# QT4_QTOPENGL_INCLUDE_DIR Path to "include/QtOpenGL" +# QT4_QTSQL_INCLUDE_DIR Path to "include/QtSql" +# QT4_QTXML_INCLUDE_DIR Path to "include/QtXml" +# QT4_QTSVG_INCLUDE_DIR Path to "include/QtSvg" +# QT4_QTSCRIPT_INCLUDE_DIR Path to "include/QtScript" +# QT4_QTTEST_INCLUDE_DIR Path to "include/QtTest" +# +# QT4_LIBRARY_DIR Path to "lib" of Qt4 +# +# QT4_PLUGINS_DIR Path to "plugins" for Qt4 +# +# For every library of Qt, a QT4_QTFOO_LIBRARY variable is defined, with the full path to the library. +# +# So there are the following variables: +# The Qt3Support library: QT4_QT3SUPPORT_LIBRARY +# +# The QtAssistant library: QT4_QTASSISTANT_LIBRARY +# +# The QtCore library: QT4_QTCORE_LIBRARY +# +# The QtDBus library: QT4_QTDBUS_LIBRARY +# +# The QtDesigner library: QT4_QTDESIGNER_LIBRARY +# +# The QtDesignerComponents library: QT4_QTDESIGNERCOMPONENTS_LIBRARY +# +# The QtGui library: QT4_QTGUI_LIBRARY +# +# The QtMotif library: QT4_QTMOTIF_LIBRARY +# +# The QtNetwork library: QT4_QTNETWORK_LIBRARY +# +# The QtNsPLugin library: QT4_QTNSPLUGIN_LIBRARY +# +# The QtOpenGL library: QT4_QTOPENGL_LIBRARY +# +# The QtSql library: QT4_QTSQL_LIBRARY +# +# The QtXml library: QT4_QTXML_LIBRARY +# +# The QtSvg library: QT4_QTSVG_LIBRARY +# +# The QtScript library: QT4_QTSCRIPT_LIBRARY +# +# The QtTest library: QT4_QTTEST_LIBRARY +# +# The qtmain library for Windows QT4_QTMAIN_LIBRARY +# +# The QtUiTools library: QT4_QTUITOOLS_LIBRARY +# +# also defined, but NOT for general use are +# QT4_MOC_EXECUTABLE Where to find the moc tool. +# QT4_UIC_EXECUTABLE Where to find the uic tool. +# QT_UIC3_EXECUTABLE Where to find the uic3 tool. +# QT_RCC_EXECUTABLE Where to find the rcc tool +# QT_DBUSCPP2XML_EXECUTABLE Where to find the qdbuscpp2xml tool. +# QT_DBUSXML2CPP_EXECUTABLE Where to find the qdbusxml2cpp tool. +# +# QT_DOC_DIR Path to "doc" of Qt4 +# QT_MKSPECS_DIR Path to "mkspecs" of Qt4 +# +# +# These are around for backwards compatibility +# they will be set +# QT_WRAP_CPP Set true if QT4_MOC_EXECUTABLE is found +# QT_WRAP_UI Set true if QT4_UIC_EXECUTABLE is found +# +# These variables do _NOT_ have any effect anymore (compared to FindQt.cmake) +# QT_MT_REQUIRED Qt4 is now always multithreaded +# +# These variables are set to "" Because Qt structure changed +# (They make no sense in Qt4) +# QT4_QT_LIBRARY Qt-Library is now split + +# Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved. +# See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details. + +if (QT4_QMAKE_FOUND) + # Check already done in this cmake run, nothing more to do + +else (QT4_QMAKE_FOUND) + +# check that QT_NO_DEBUG is defined for release configurations +MACRO(QT_CHECK_FLAG_EXISTS FLAG VAR DOC) + IF(NOT ${VAR} MATCHES "${FLAG}") + SET(${VAR} "${${VAR}} ${FLAG}" + CACHE STRING "Flags used by the compiler during ${DOC} builds." FORCE) + ENDIF(NOT ${VAR} MATCHES "${FLAG}") +ENDMACRO(QT_CHECK_FLAG_EXISTS FLAG VAR) +QT_CHECK_FLAG_EXISTS(-DQT_NO_DEBUG CMAKE_CXX_FLAGS_RELWITHDEBINFO "Release with Debug Info") +QT_CHECK_FLAG_EXISTS(-DQT_NO_DEBUG CMAKE_CXX_FLAGS_RELEASE "release") +QT_CHECK_FLAG_EXISTS(-DQT_NO_DEBUG CMAKE_CXX_FLAGS_MINSIZEREL "release minsize") + +INCLUDE(CheckSymbolExists) +INCLUDE(MacroAddFileDependencies) +INCLUDE(MacroPushRequiredVars) + +SET(QT_USE_FILE ${CMAKE_ROOT}/Modules/UseQt4.cmake) + +SET( QT4_DEFINITIONS "") + +IF (WIN32) + SET(QT4_DEFINITIONS -DQT_DLL) +ENDIF(WIN32) + +SET(QT4_INSTALLED_VERSION_TOO_OLD FALSE) + +# macro for asking qmake to process pro files +MACRO(QT_QUERY_QMAKE outvar invar) + FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmpQmake/tmp.pro + "message(CMAKE_MESSAGE<$$${invar}>)") + + # Invoke qmake with the tmp.pro program to get the desired + # information. Use the same variable for both stdout and stderr + # to make sure we get the output on all platforms. + EXECUTE_PROCESS(COMMAND ${QT_QMAKE_EXECUTABLE} + WORKING_DIRECTORY + ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmpQmake + OUTPUT_VARIABLE _qmake_query_output + RESULT_VARIABLE _qmake_result + ERROR_VARIABLE _qmake_query_output ) + + FILE(REMOVE_RECURSE + "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmpQmake") + + IF(_qmake_result) + MESSAGE(WARNING " querying qmake for ${invar}. qmake reported:\n${_qmake_query_output}") + ELSE(_qmake_result) + STRING(REGEX REPLACE ".*CMAKE_MESSAGE<([^>]*).*" "\\1" ${outvar} "${_qmake_query_output}") + ENDIF(_qmake_result) + +ENDMACRO(QT_QUERY_QMAKE) + +MACRO(VERIFY_QMAKE_QT4) + EXEC_PROGRAM(${QT_QMAKE_EXECUTABLE} ARGS "-query QT_VERSION" OUTPUT_VARIABLE QTVERSION) + + # check for qt3 qmake and then try and find qmake4 or qmake-qt4 in the path + IF("${QTVERSION}" MATCHES "Unknown") + SET(QT_QMAKE_EXECUTABLE NOTFOUND CACHE FILEPATH "" FORCE) + FIND_PROGRAM(QT_QMAKE_EXECUTABLE NAMES qmake4 qmake-qt4 PATHS + "[HKEY_CURRENT_USER\\Software\\Trolltech\\Qt3Versions\\4.0.0;InstallDir]/bin" + "[HKEY_CURRENT_USER\\Software\\Trolltech\\Versions\\4.0.0;InstallDir]/bin" + $ENV{QTDIR}/bin + ) + IF(QT_QMAKE_EXECUTABLE) + EXEC_PROGRAM(${QT_QMAKE_EXECUTABLE} + ARGS "-query QT_VERSION" OUTPUT_VARIABLE QTVERSION) + ENDIF(QT_QMAKE_EXECUTABLE) + ENDIF("${QTVERSION}" MATCHES "Unknown") + + # check that we found the Qt4 qmake, Qt3 qmake output won't match here + STRING(REGEX MATCH "^[0-9]+\\.[0-9]+\\.[0-9]+" qt_version_tmp "${QTVERSION}") + IF (qt_version_tmp) + + # we need at least version 4.0.0 + IF (NOT QT4_MIN_VERSION) + SET(QT4_MIN_VERSION "4.0.0") + ENDIF (NOT QT4_MIN_VERSION) + + #now parse the parts of the user given version string into variables + STRING(REGEX MATCH "^[0-9]+\\.[0-9]+\\.[0-9]+" req_qt_major_vers "${QT4_MIN_VERSION}") + IF (NOT req_qt_major_vers) + MESSAGE( FATAL_ERROR "Invalid Qt version string given: \"${QT4_MIN_VERSION}\", expected e.g. \"4.0.1\"") + ENDIF (NOT req_qt_major_vers) + + # now parse the parts of the user given version string into variables + STRING(REGEX REPLACE "^([0-9]+)\\.[0-9]+\\.[0-9]+" "\\1" req_qt_major_vers "${QT4_MIN_VERSION}") + STRING(REGEX REPLACE "^[0-9]+\\.([0-9])+\\.[0-9]+" "\\1" req_qt_minor_vers "${QT4_MIN_VERSION}") + STRING(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+)" "\\1" req_qt_patch_vers "${QT4_MIN_VERSION}") + + IF (NOT req_qt_major_vers EQUAL 4) + MESSAGE( FATAL_ERROR "Invalid Qt version string given: \"${QT4_MIN_VERSION}\", major version 4 is required, e.g. \"4.0.1\"") + ENDIF (NOT req_qt_major_vers EQUAL 4) + + # and now the version string given by qmake + STRING(REGEX REPLACE "^([0-9]+)\\.[0-9]+\\.[0-9]+.*" "\\1" found_qt_major_vers "${QTVERSION}") + STRING(REGEX REPLACE "^[0-9]+\\.([0-9])+\\.[0-9]+.*" "\\1" found_qt_minor_vers "${QTVERSION}") + STRING(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" found_qt_patch_vers "${QTVERSION}") + IF (${found_qt_major_vers} EQUAL 4) + # compute an overall version number which can be compared at once + MATH(EXPR req_vers "${req_qt_major_vers}*10000 + ${req_qt_minor_vers}*100 + ${req_qt_patch_vers}") + MATH(EXPR found_vers "${found_qt_major_vers}*10000 + ${found_qt_minor_vers}*100 + ${found_qt_patch_vers}") + + + IF (found_vers LESS req_vers) + SET(QT4_QMAKE_FOUND FALSE) + SET(QT4_INSTALLED_VERSION_TOO_OLD TRUE) + ELSE (found_vers LESS req_vers) + SET(QT4_QMAKE_FOUND TRUE) + ENDIF (found_vers LESS req_vers) + ENDIF () + ENDIF (qt_version_tmp) +ENDMACRO() + +GET_FILENAME_COMPONENT(qt_install_version "[HKEY_CURRENT_USER\\Software\\trolltech\\Versions;DefaultQtVersion]" NAME) +# check for qmake +FIND_PROGRAM(QT_QMAKE_EXECUTABLE NAMES qmake qmake4 qmake-qt4 PATHS + "[HKEY_CURRENT_USER\\Software\\Trolltech\\Qt3Versions\\4.0.0;InstallDir]/bin" + "[HKEY_CURRENT_USER\\Software\\Trolltech\\Versions\\4.0.0;InstallDir]/bin" + "[HKEY_CURRENT_USER\\Software\\Trolltech\\Versions\\${qt_install_version};InstallDir]/bin" + $ENV{QTDIR}/bin +) + +IF (QT_QMAKE_EXECUTABLE) + + SET(QT4_QMAKE_FOUND FALSE) + VERIFY_QMAKE_QT4() + + IF (NOT QT4_QMAKE_FOUND) + FIND_PROGRAM(QT_QMAKE_EXECUTABLE2 NAMES qmake4 qmake-qt4 PATHS + "[HKEY_CURRENT_USER\\Software\\Trolltech\\Qt3Versions\\4.0.0;InstallDir]/bin" + "[HKEY_CURRENT_USER\\Software\\Trolltech\\Versions\\4.0.0;InstallDir]/bin" + "[HKEY_CURRENT_USER\\Software\\Trolltech\\Versions\\${qt_install_version};InstallDir]/bin" + $ENV{QTDIR}/bin + ) + SET(QT_QMAKE_EXECUTABLE ${QT_QMAKE_EXECUTABLE2}) + VERIFY_QMAKE_QT4() + ENDIF() + +ENDIF (QT_QMAKE_EXECUTABLE) + +IF (QT4_QMAKE_FOUND) + + if (WIN32) + # get qt install dir + get_filename_component(_DIR ${QT_QMAKE_EXECUTABLE} PATH ) + get_filename_component(QT_INSTALL_DIR ${_DIR} PATH ) + endif (WIN32) + + # ask qmake for the library dir + # Set QT4_LIBRARY_DIR + IF (NOT QT4_LIBRARY_DIR) + EXEC_PROGRAM( ${QT_QMAKE_EXECUTABLE} + ARGS "-query QT_INSTALL_LIBS" + OUTPUT_VARIABLE QT4_LIBRARY_DIR_TMP ) + IF(EXISTS "${QT4_LIBRARY_DIR_TMP}") + SET(QT4_LIBRARY_DIR ${QT4_LIBRARY_DIR_TMP} CACHE PATH "Qt library dir") + ELSE(EXISTS "${QT4_LIBRARY_DIR_TMP}") + MESSAGE("Warning: QT_QMAKE_EXECUTABLE reported QT_INSTALL_LIBS as ${QT4_LIBRARY_DIR_TMP}") + MESSAGE("Warning: ${QT4_LIBRARY_DIR_TMP} does NOT exist, Qt must NOT be installed correctly.") + ENDIF(EXISTS "${QT4_LIBRARY_DIR_TMP}") + ENDIF(NOT QT4_LIBRARY_DIR) + + IF (APPLE) + IF (EXISTS ${QT4_LIBRARY_DIR}/QtCore.framework) + SET(QT_USE_FRAMEWORKS ON + CACHE BOOL "Set to ON if Qt build uses frameworks.") + ELSE (EXISTS ${QT4_LIBRARY_DIR}/QtCore.framework) + SET(QT_USE_FRAMEWORKS OFF + CACHE BOOL "Set to ON if Qt build uses frameworks.") + ENDIF (EXISTS ${QT4_LIBRARY_DIR}/QtCore.framework) + + MARK_AS_ADVANCED(QT_USE_FRAMEWORKS) + ENDIF (APPLE) + + # ask qmake for the binary dir + IF (NOT QT_BINARY_DIR) + EXEC_PROGRAM(${QT_QMAKE_EXECUTABLE} + ARGS "-query QT_INSTALL_BINS" + OUTPUT_VARIABLE qt_bins ) + SET(QT_BINARY_DIR ${qt_bins} CACHE INTERNAL "") + ENDIF (NOT QT_BINARY_DIR) + + # ask qmake for the include dir + IF (NOT QT_HEADERS_DIR) + EXEC_PROGRAM( ${QT_QMAKE_EXECUTABLE} + ARGS "-query QT_INSTALL_HEADERS" + OUTPUT_VARIABLE qt_headers ) + SET(QT_HEADERS_DIR ${qt_headers} CACHE INTERNAL "") + ENDIF(NOT QT_HEADERS_DIR) + + + # ask qmake for the documentation directory + IF (NOT QT_DOC_DIR) + EXEC_PROGRAM( ${QT_QMAKE_EXECUTABLE} + ARGS "-query QT_INSTALL_DOCS" + OUTPUT_VARIABLE qt_doc_dir ) + SET(QT_DOC_DIR ${qt_doc_dir} CACHE PATH "The location of the Qt docs") + ENDIF (NOT QT_DOC_DIR) + + # ask qmake for the mkspecs directory + IF (NOT QT_MKSPECS_DIR) + EXEC_PROGRAM( ${QT_QMAKE_EXECUTABLE} + ARGS "-query QMAKE_MKSPECS" + OUTPUT_VARIABLE qt_mkspecs_dirs ) + STRING(REPLACE ":" ";" qt_mkspecs_dirs "${qt_mkspecs_dirs}") + FIND_PATH(QT_MKSPECS_DIR qconfig.pri PATHS ${qt_mkspecs_dirs} + DOC "The location of the Qt mkspecs containing qconfig.pri" + NO_DEFAULT_PATH ) + ENDIF (NOT QT_MKSPECS_DIR) + + # ask qmake for the plugins directory + IF (NOT QT4_PLUGINS_DIR) + EXEC_PROGRAM( ${QT_QMAKE_EXECUTABLE} + ARGS "-query QT_INSTALL_PLUGINS" + OUTPUT_VARIABLE qt_plugins_dir ) + SET(QT4_PLUGINS_DIR ${qt_plugins_dir} CACHE PATH "The location of the Qt plugins") + ENDIF (NOT QT4_PLUGINS_DIR) + ######################################## + # + # Setting the INCLUDE-Variables + # + ######################################## + + FIND_PATH(QT4_QTCORE_INCLUDE_DIR QtGlobal + ${QT_HEADERS_DIR}/QtCore + ${QT4_LIBRARY_DIR}/QtCore.framework/Headers + NO_DEFAULT_PATH + ) + + # Set QT4_INCLUDE_DIR by removine "/QtCore" in the string ${QT4_QTCORE_INCLUDE_DIR} + IF( QT4_QTCORE_INCLUDE_DIR AND NOT QT4_INCLUDE_DIR) + IF (QT_USE_FRAMEWORKS) + SET(QT4_INCLUDE_DIR ${QT_HEADERS_DIR}) + ELSE (QT_USE_FRAMEWORKS) + STRING( REGEX REPLACE "/QtCore$" "" qt4_include_dir ${QT4_QTCORE_INCLUDE_DIR}) + SET( QT4_INCLUDE_DIR ${qt4_include_dir} CACHE PATH "") + ENDIF (QT_USE_FRAMEWORKS) + ENDIF( QT4_QTCORE_INCLUDE_DIR AND NOT QT4_INCLUDE_DIR) + + IF( NOT QT4_INCLUDE_DIR) + IF( NOT Qt4_FIND_QUIETLY AND Qt4_FIND_REQUIRED) + MESSAGE( FATAL_ERROR "Could NOT find QtGlobal header") + ENDIF( NOT Qt4_FIND_QUIETLY AND Qt4_FIND_REQUIRED) + ENDIF( NOT QT4_INCLUDE_DIR) + + ############################################# + # + # Find out what window system we're using + # + ############################################# + # Save required includes and required_flags variables + macro_push_required_vars() + # Add QT4_INCLUDE_DIR to CMAKE_REQUIRED_INCLUDES + SET(CMAKE_REQUIRED_INCLUDES "${CMAKE_REQUIRED_INCLUDES};${QT4_INCLUDE_DIR}") + # On Mac OS X when Qt has framework support, also add the framework path + IF( QT_USE_FRAMEWORKS ) + SET(CMAKE_REQUIRED_FLAGS "-F${QT4_LIBRARY_DIR} ") + ENDIF( QT_USE_FRAMEWORKS ) + # Check for Window system symbols (note: only one should end up being set) + CHECK_SYMBOL_EXISTS(Q_WS_X11 "QtCore/qglobal.h" Q_WS_X11) + CHECK_SYMBOL_EXISTS(Q_WS_WIN "QtCore/qglobal.h" Q_WS_WIN) + CHECK_SYMBOL_EXISTS(Q_WS_QWS "QtCore/qglobal.h" Q_WS_QWS) + CHECK_SYMBOL_EXISTS(Q_WS_MAC "QtCore/qglobal.h" Q_WS_MAC) + + IF (QT4_QTCOPY_REQUIRED) + CHECK_SYMBOL_EXISTS(QT_IS_QTCOPY "QtCore/qglobal.h" QT_KDE_QT_COPY) + IF (NOT QT_IS_QTCOPY) + MESSAGE(FATAL_ERROR "qt-copy is required, but hasn't been found") + ENDIF (NOT QT_IS_QTCOPY) + ENDIF (QT4_QTCOPY_REQUIRED) + + # Restore CMAKE_REQUIRED_INCLUDES+CMAKE_REQUIRED_FLAGS variables + macro_pop_required_vars() + # + ############################################# + + IF (QT_USE_FRAMEWORKS) + SET(QT4_DEFINITIONS ${QT4_DEFINITIONS} -F${QT4_LIBRARY_DIR} -L${QT4_LIBRARY_DIR} ) + ENDIF (QT_USE_FRAMEWORKS) + + # Set QT4_QT3SUPPORT_INCLUDE_DIR + FIND_PATH(QT4_QT3SUPPORT_INCLUDE_DIR Qt3Support + PATHS + ${QT4_INCLUDE_DIR}/Qt3Support + ${QT4_LIBRARY_DIR}/Qt3Support.framework/Headers + NO_DEFAULT_PATH + ) + + # Set QT4_QT4_INCLUDE_DIR + FIND_PATH(QT4_QT4_INCLUDE_DIR qglobal.h + PATHS + ${QT4_INCLUDE_DIR}/Qt + ${QT4_LIBRARY_DIR}/QtCore.framework/Headers + NO_DEFAULT_PATH + ) + + # Set QT4_QTGUI_INCLUDE_DIR + FIND_PATH(QT4_QTGUI_INCLUDE_DIR QtGui + PATHS + ${QT4_INCLUDE_DIR}/QtGui + ${QT4_LIBRARY_DIR}/QtGui.framework/Headers + NO_DEFAULT_PATH + ) + + # Set QT4_QTSVG_INCLUDE_DIR + FIND_PATH(QT4_QTSVG_INCLUDE_DIR QtSvg + PATHS + ${QT4_INCLUDE_DIR}/QtSvg + ${QT4_LIBRARY_DIR}/QtSvg.framework/Headers + NO_DEFAULT_PATH + ) + + # Set QT4_QTSCRIPT_INCLUDE_DIR + FIND_PATH(QT4_QTSCRIPT_INCLUDE_DIR QtScript + PATHS + ${QT4_INCLUDE_DIR}/QtScript + ${QT4_LIBRARY_DIR}/QtScript.framework/Headers + NO_DEFAULT_PATH + ) + + # Set QT4_QTTEST_INCLUDE_DIR + FIND_PATH(QT4_QTTEST_INCLUDE_DIR QtTest + PATHS + ${QT4_INCLUDE_DIR}/QtTest + ${QT4_LIBRARY_DIR}/QtTest.framework/Headers + NO_DEFAULT_PATH + ) + + # Set QT4_QTUITOOLS_INCLUDE_DIR + FIND_PATH(QT4_QTUITOOLS_INCLUDE_DIR QtUiTools + PATHS + ${QT4_INCLUDE_DIR}/QtUiTools + ${QT4_LIBRARY_DIR}/QtUiTools.framework/Headers + NO_DEFAULT_PATH + ) + + + + # Set QT4_QTMOTIF_INCLUDE_DIR + IF(Q_WS_X11) + FIND_PATH(QT4_QTMOTIF_INCLUDE_DIR QtMotif PATHS ${QT4_INCLUDE_DIR}/QtMotif NO_DEFAULT_PATH ) + ENDIF(Q_WS_X11) + + # Set QT4_QTNETWORK_INCLUDE_DIR + FIND_PATH(QT4_QTNETWORK_INCLUDE_DIR QtNetwork + PATHS + ${QT4_INCLUDE_DIR}/QtNetwork + ${QT4_LIBRARY_DIR}/QtNetwork.framework/Headers + NO_DEFAULT_PATH + ) + + # Set QT4_QTNSPLUGIN_INCLUDE_DIR + FIND_PATH(QT4_QTNSPLUGIN_INCLUDE_DIR QtNsPlugin + PATHS + ${QT4_INCLUDE_DIR}/QtNsPlugin + ${QT4_LIBRARY_DIR}/QtNsPlugin.framework/Headers + NO_DEFAULT_PATH + ) + + # Set QT4_QTOPENGL_INCLUDE_DIR + FIND_PATH(QT4_QTOPENGL_INCLUDE_DIR QtOpenGL + PATHS + ${QT4_INCLUDE_DIR}/QtOpenGL + ${QT4_LIBRARY_DIR}/QtOpenGL.framework/Headers + NO_DEFAULT_PATH + ) + + # Set QT4_QTSQL_INCLUDE_DIR + FIND_PATH(QT4_QTSQL_INCLUDE_DIR QtSql + PATHS + ${QT4_INCLUDE_DIR}/QtSql + ${QT4_LIBRARY_DIR}/QtSql.framework/Headers + NO_DEFAULT_PATH + ) + + # Set QT4_QTXML_INCLUDE_DIR + FIND_PATH(QT4_QTXML_INCLUDE_DIR QtXml + PATHS + ${QT4_INCLUDE_DIR}/QtXml + ${QT4_LIBRARY_DIR}/QtXml.framework/Headers + NO_DEFAULT_PATH + ) + + # Set QT4_QTASSISTANT_INCLUDE_DIR + FIND_PATH(QT4_QTASSISTANT_INCLUDE_DIR QtAssistant + PATHS + ${QT4_INCLUDE_DIR}/QtAssistant + ${QT_HEADERS_DIR}/QtAssistant + ${QT4_LIBRARY_DIR}/QtAssistant.framework/Headers + NO_DEFAULT_PATH + ) + + # Set QT4_QTDESIGNER_INCLUDE_DIR + FIND_PATH(QT4_QTDESIGNER_INCLUDE_DIR QDesignerComponents + PATHS + ${QT4_INCLUDE_DIR}/QtDesigner + ${QT_HEADERS_DIR}/QtDesigner + ${QT4_LIBRARY_DIR}/QtDesigner.framework/Headers + NO_DEFAULT_PATH + ) + + # Set QT4_QTDESIGNERCOMPONENTS_INCLUDE_DIR + FIND_PATH(QT4_QTDESIGNERCOMPONENTS_INCLUDE_DIR QDesignerComponents + PATHS + ${QT4_INCLUDE_DIR}/QtDesigner + ${QT_HEADERS_DIR}/QtDesigner + NO_DEFAULT_PATH + ) + + + # Set QT4_QTDBUS_INCLUDE_DIR + FIND_PATH(QT4_QTDBUS_INCLUDE_DIR QtDBus + PATHS + ${QT4_INCLUDE_DIR}/QtDBus + ${QT_HEADERS_DIR}/QtDBus + NO_DEFAULT_PATH + ) + + # Make variables changeble to the advanced user + MARK_AS_ADVANCED( QT4_LIBRARY_DIR QT4_INCLUDE_DIR QT4_QT4_INCLUDE_DIR QT_DOC_DIR QT_MKSPECS_DIR QT4_PLUGINS_DIR) + + # Set QT4_INCLUDES + SET( QT4_INCLUDES ${QT4_QT4_INCLUDE_DIR} ${QT_MKSPECS_DIR}/default ${QT4_INCLUDE_DIR}) + + + ######################################## + # + # Setting the LIBRARY-Variables + # + ######################################## + + IF (QT_USE_FRAMEWORKS) + # If FIND_LIBRARY found libraries in Apple frameworks, we would NOT have + # to jump through these hoops. + IF(EXISTS ${QT4_LIBRARY_DIR}/QtCore.framework) + SET(QT4_QTCORE_FOUND TRUE) + SET(QT4_QTCORE_LIBRARY "-F${QT4_LIBRARY_DIR} -framework QtCore" CACHE STRING "The QtCore library.") + ELSE(EXISTS ${QT4_LIBRARY_DIR}/QtCore.framework) + SET(QT4_QTCORE_FOUND FALSE) + ENDIF(EXISTS ${QT4_LIBRARY_DIR}/QtCore.framework) + + IF(EXISTS ${QT4_LIBRARY_DIR}/QtGui.framework) + SET(QT4_QTGUI_FOUND TRUE) + SET(QT4_QTGUI_LIBRARY "-F${QT4_LIBRARY_DIR} -framework QtGui" CACHE STRING "The QtGui library.") + ELSE(EXISTS ${QT4_LIBRARY_DIR}/QtGui.framework) + SET(QT4_QTGUI_FOUND FALSE) + ENDIF(EXISTS ${QT4_LIBRARY_DIR}/QtGui.framework) + + IF(EXISTS ${QT4_LIBRARY_DIR}/Qt3Support.framework) + SET(QT4_QT3SUPPORT_FOUND TRUE) + SET(QT4_QT3SUPPORT_LIBRARY "-F${QT4_LIBRARY_DIR} -framework Qt3Support" CACHE STRING "The Qt3Support library.") + ELSE(EXISTS ${QT4_LIBRARY_DIR}/Qt3Support.framework) + SET(QT4_QT3SUPPORT_FOUND FALSE) + ENDIF(EXISTS ${QT4_LIBRARY_DIR}/Qt3Support.framework) + + IF(EXISTS ${QT4_LIBRARY_DIR}/QtNetwork.framework) + SET(QT4_QTNETWORK_FOUND TRUE) + SET(QT4_QTNETWORK_LIBRARY "-F${QT4_LIBRARY_DIR} -framework QtNetwork" CACHE STRING "The QtNetwork library.") + ELSE(EXISTS ${QT4_LIBRARY_DIR}/QtNetwork.framework) + SET(QT4_QTNETWORK_FOUND FALSE) + ENDIF(EXISTS ${QT4_LIBRARY_DIR}/QtNetwork.framework) + + IF(EXISTS ${QT4_LIBRARY_DIR}/QtOpenGL.framework) + SET(QT4_QTOPENGL_FOUND TRUE) + SET(QT4_QTOPENGL_LIBRARY "-F${QT4_LIBRARY_DIR} -framework QtOpenGL" CACHE STRING "The QtOpenGL library.") + ELSE(EXISTS ${QT4_LIBRARY_DIR}/QtOpenGL.framework) + SET(QT4_QTOPENGL_FOUND FALSE) + ENDIF(EXISTS ${QT4_LIBRARY_DIR}/QtOpenGL.framework) + + IF(EXISTS ${QT4_LIBRARY_DIR}/QtSql.framework) + SET(QT4_QTSQL_FOUND TRUE) + SET(QT4_QTSQL_LIBRARY "-F${QT4_LIBRARY_DIR} -framework QtSql" CACHE STRING "The QtSql library.") + ELSE(EXISTS ${QT4_LIBRARY_DIR}/QtSql.framework) + SET(QT4_QTSQL_FOUND FALSE) + ENDIF(EXISTS ${QT4_LIBRARY_DIR}/QtSql.framework) + + IF(EXISTS ${QT4_LIBRARY_DIR}/QtXml.framework) + SET(QT4_QTXML_FOUND TRUE) + SET(QT4_QTXML_LIBRARY "-F${QT4_LIBRARY_DIR} -framework QtXml" CACHE STRING "The QtXml library.") + ELSE(EXISTS ${QT4_LIBRARY_DIR}/QtXml.framework) + SET(QT4_QTXML_FOUND FALSE) + ENDIF(EXISTS ${QT4_LIBRARY_DIR}/QtXml.framework) + + IF(EXISTS ${QT4_LIBRARY_DIR}/QtSvg.framework) + SET(QT4_QTSVG_FOUND TRUE) + SET(QT4_QTSVG_LIBRARY "-F${QT4_LIBRARY_DIR} -framework QtSvg" CACHE STRING "The QtSvg library.") + ELSE(EXISTS ${QT4_LIBRARY_DIR}/QtSvg.framework) + SET(QT4_QTSVG_FOUND FALSE) + ENDIF(EXISTS ${QT4_LIBRARY_DIR}/QtSvg.framework) + + IF(EXISTS ${QT4_LIBRARY_DIR}/QtDBus.framework) + SET(QT4_QTDBUS_FOUND TRUE) + SET(QT4_QTDBUS_LIBRARY "-F${QT4_LIBRARY_DIR} -framework QtDBus" CACHE STRING "The QtDBus library.") + ELSE(EXISTS ${QT4_LIBRARY_DIR}/QtDBus.framework) + SET(QT4_QTDBUS_FOUND FALSE) + ENDIF(EXISTS ${QT4_LIBRARY_DIR}/QtDBus.framework) + + IF(EXISTS ${QT4_LIBRARY_DIR}/QtTest.framework) + SET(QT4_QTTEST_FOUND TRUE) + SET(QT4_QTTEST_LIBRARY "-F${QT4_LIBRARY_DIR} -framework QtTest" CACHE STRING "The QtTest library.") + ELSE(EXISTS ${QT4_LIBRARY_DIR}/QtTest.framework) + SET(QT4_QTTEST_FOUND FALSE) + ENDIF(EXISTS ${QT4_LIBRARY_DIR}/QtTest.framework) + + # WTF? why don't we have frameworks? :P + # Set QT4_QTUITOOLS_LIBRARY + FIND_LIBRARY(QT4_QTUITOOLS_LIBRARY NAMES QtUiTools QtUiTools4 PATHS ${QT4_LIBRARY_DIR} ) + # Set QT4_QTSCRIPT_LIBRARY + FIND_LIBRARY(QT4_QTSCRIPT_LIBRARY NAMES QtScript QtScript4 PATHS ${QT4_LIBRARY_DIR} ) + + ELSE (QT_USE_FRAMEWORKS) + + # Set QT4_QTCORE_LIBRARY by searching for a lib with "QtCore." as part of the filename + FIND_LIBRARY(QT4_QTCORE_LIBRARY NAMES QtCore QtCore4 QtCored4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH ) + + # Set QT4_QT3SUPPORT_LIBRARY + FIND_LIBRARY(QT4_QT3SUPPORT_LIBRARY NAMES Qt3Support Qt3Support4 Qt3Supportd4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + + # Set QT4_QTGUI_LIBRARY + FIND_LIBRARY(QT4_QTGUI_LIBRARY NAMES QtGui QtGui4 QtGuid4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + + # Set QT4_QTMOTIF_LIBRARY + IF(Q_WS_X11) + FIND_LIBRARY(QT4_QTMOTIF_LIBRARY NAMES QtMotif PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + ENDIF(Q_WS_X11) + + # Set QT4_QTNETWORK_LIBRARY + FIND_LIBRARY(QT4_QTNETWORK_LIBRARY NAMES QtNetwork QtNetwork4 QtNetworkd4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + + # Set QT4_QTNSPLUGIN_LIBRARY + FIND_LIBRARY(QT4_QTNSPLUGIN_LIBRARY NAMES QtNsPlugin PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + + # Set QT4_QTOPENGL_LIBRARY + FIND_LIBRARY(QT4_QTOPENGL_LIBRARY NAMES QtOpenGL QtOpenGL4 QtOpenGLd4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + + # Set QT4_QTSQL_LIBRARY + FIND_LIBRARY(QT4_QTSQL_LIBRARY NAMES QtSql QtSql4 QtSqld4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + + # Set QT4_QTXML_LIBRARY + FIND_LIBRARY(QT4_QTXML_LIBRARY NAMES QtXml QtXml4 QtXmld4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + + # Set QT4_QTSVG_LIBRARY + FIND_LIBRARY(QT4_QTSVG_LIBRARY NAMES QtSvg QtSvg4 QtSvgd4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + + # Set QT4_QTSCRIPT_LIBRARY + FIND_LIBRARY(QT4_QTSCRIPT_LIBRARY NAMES QtScript QtScript4 QtScriptd4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + + # Set QT4_QTUITOOLS_LIBRARY + FIND_LIBRARY(QT4_QTUITOOLS_LIBRARY NAMES QtUiTools QtUiTools4 QtUiToolsd4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + + # Set QT4_QTTEST_LIBRARY + FIND_LIBRARY(QT4_QTTEST_LIBRARY NAMES QtTest QtTest4 QtTestd4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + + FIND_LIBRARY(QT4_QTDBUS_LIBRARY NAMES QtDBus QtDBus4 QtDBusd4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + + IF(MSVC) + FIND_LIBRARY(QT4_QTCORE_LIBRARY_RELEASE NAMES QtCore4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTCORE_LIBRARY_DEBUG NAMES QtCored4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QT3SUPPORT_LIBRARY_RELEASE NAMES Qt3Support4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QT3SUPPORT_LIBRARY_DEBUG NAMES Qt3Supportd4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTGUI_LIBRARY_RELEASE NAMES QtGui4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTGUI_LIBRARY_DEBUG NAMES QtGuid4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTNETWORK_LIBRARY_RELEASE NAMES QtNetwork4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTNETWORK_LIBRARY_DEBUG NAMES QtNetworkd4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTOPENGL_LIBRARY_RELEASE NAMES QtOpenGL4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTOPENGL_LIBRARY_DEBUG NAMES QtOpenGLd4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTSQL_LIBRARY_RELEASE NAMES QtSql4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTSQL_LIBRARY_DEBUG NAMES QtSqld4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTXML_LIBRARY_RELEASE NAMES QtXml4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTXML_LIBRARY_DEBUG NAMES QtXmld4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTSVG_LIBRARY_RELEASE NAMES QtSvg4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTSVG_LIBRARY_DEBUG NAMES QtSvgd4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTSCRIPT_LIBRARY_RELEASE NAMES QtScript4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTSCRIPT_LIBRARY_DEBUG NAMES QtScriptd4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTUITOOLS_LIBRARY_RELEASE NAMES QtUiTools QtUiTools4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTUITOOLS_LIBRARY_DEBUG NAMES QtUiToolsd QtUiToolsd4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTTEST_LIBRARY_RELEASE NAMES QtTest4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTTEST_LIBRARY_DEBUG NAMES QtTestd4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTDBUS_LIBRARY_RELEASE NAMES QtDBus4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTDBUS_LIBRARY_DEBUG NAMES QtDBusd4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTASSISTANT_LIBRARY_RELEASE NAMES QtAssistantClient4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTASSISTANT_LIBRARY_DEBUG NAMES QtAssistantClientd4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTDESIGNER_LIBRARY_RELEASE NAMES QtDesigner4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTDESIGNER_LIBRARY_DEBUG NAMES QtDesignerd4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTDESIGNERCOMPONENTS_LIBRARY_RELEASE NAMES QtDesignerComponents4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTDESIGNERCOMPONENTS_LIBRARY_DEBUG NAMES QtDesignerComponentsd4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTMAIN_LIBRARY_RELEASE NAMES qtmain PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + FIND_LIBRARY(QT4_QTMAIN_LIBRARY_DEBUG NAMES qtmaind PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + ENDIF(MSVC) + ENDIF (QT_USE_FRAMEWORKS) + + IF( NOT QT4_QTCORE_LIBRARY ) + IF( NOT Qt4_FIND_QUIETLY AND Qt4_FIND_REQUIRED) + MESSAGE( FATAL_ERROR "Could NOT find QtCore. Check ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log for more details.") + ENDIF( NOT Qt4_FIND_QUIETLY AND Qt4_FIND_REQUIRED) + ENDIF( NOT QT4_QTCORE_LIBRARY ) + + # Set QT4_QTASSISTANT_LIBRARY + FIND_LIBRARY(QT4_QTASSISTANT_LIBRARY NAMES QtAssistantClient QtAssistantClient4 QtAssistant QtAssistant4 QtAssistantd4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + + # Set QT4_QTDESIGNER_LIBRARY + FIND_LIBRARY(QT4_QTDESIGNER_LIBRARY NAMES QtDesigner QtDesigner4 QtDesignerd4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + + # Set QT4_QTDESIGNERCOMPONENTS_LIBRARY + FIND_LIBRARY(QT4_QTDESIGNERCOMPONENTS_LIBRARY NAMES QtDesignerComponents QtDesignerComponents4 QtDesignerComponentsd4 PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + + # Set QT4_QTMAIN_LIBRARY + IF(WIN32) + FIND_LIBRARY(QT4_QTMAIN_LIBRARY NAMES qtmain qtmaind PATHS ${QT4_LIBRARY_DIR} NO_DEFAULT_PATH) + ENDIF(WIN32) + + ############################################ + # + # Check the existence of the libraries. + # + ############################################ + + MACRO (_QT4_ADJUST_LIB_VARS basename) + IF (QT4_${basename}_LIBRARY OR QT4_${basename}_LIBRARY_DEBUG) + + IF(MSVC) + # Both set + IF (QT4_${basename}_LIBRARY_RELEASE AND QT4_${basename}_LIBRARY_DEBUG) + SET(QT4_${basename}_LIBRARY optimized ${QT4_${basename}_LIBRARY_RELEASE} debug ${QT4_${basename}_LIBRARY_DEBUG}) + ENDIF (QT4_${basename}_LIBRARY_RELEASE AND QT4_${basename}_LIBRARY_DEBUG) + + # Only debug was found + IF (NOT QT4_${basename}_LIBRARY_RELEASE AND QT4_${basename}_LIBRARY_DEBUG) + SET(QT4_${basename}_LIBRARY ${QT4_${basename}_LIBRARY_DEBUG}) + ENDIF (NOT QT4_${basename}_LIBRARY_RELEASE AND QT4_${basename}_LIBRARY_DEBUG) + + # Only release was found + IF (QT4_${basename}_LIBRARY_RELEASE AND NOT QT4_${basename}_LIBRARY_DEBUG) + SET(QT4_${basename}_LIBRARY ${QT4_${basename}_LIBRARY_RELEASE}) + ENDIF (QT4_${basename}_LIBRARY_RELEASE AND NOT QT4_${basename}_LIBRARY_DEBUG) + + # Hmm, is this used anywhere ? Yes, in UseQt4.cmake. We are currently incompatible :-( + SET(QT4_${basename}_LIBRARIES optimized ${QT4_${basename}_LIBRARY} debug ${QT4_${basename}_LIBRARY_DEBUG}) + + ENDIF(MSVC) + + SET(QT4_${basename}_LIBRARY ${QT4_${basename}_LIBRARY} CACHE FILEPATH "The Qt4 ${basename} library") + + IF (QT4_${basename}_LIBRARY) + SET(QT4_${basename}_FOUND 1) + ENDIF (QT4_${basename}_LIBRARY) + + ENDIF (QT4_${basename}_LIBRARY OR QT4_${basename}_LIBRARY_DEBUG) + + IF (QT4_${basename}_INCLUDE_DIR) + #add the include directory to QT4_INCLUDES + SET(QT4_INCLUDES "${QT4_${basename}_INCLUDE_DIR}" ${QT4_INCLUDES}) + ENDIF (QT4_${basename}_INCLUDE_DIR) + + # Make variables changeble to the advanced user + MARK_AS_ADVANCED(QT4_${basename}_LIBRARY QT4_${basename}_INCLUDE_DIR) + ENDMACRO (_QT4_ADJUST_LIB_VARS) + + + # Set QT_xyz_LIBRARY variable and add + # library include path to QT4_INCLUDES + _QT4_ADJUST_LIB_VARS(QTCORE) + _QT4_ADJUST_LIB_VARS(QTGUI) + _QT4_ADJUST_LIB_VARS(QT3SUPPORT) + _QT4_ADJUST_LIB_VARS(QTASSISTANT) + _QT4_ADJUST_LIB_VARS(QTDESIGNER) + _QT4_ADJUST_LIB_VARS(QTDESIGNERCOMPONENTS) + _QT4_ADJUST_LIB_VARS(QTNETWORK) + _QT4_ADJUST_LIB_VARS(QTNSPLUGIN) + _QT4_ADJUST_LIB_VARS(QTOPENGL) + _QT4_ADJUST_LIB_VARS(QTSQL) + _QT4_ADJUST_LIB_VARS(QTXML) + _QT4_ADJUST_LIB_VARS(QTSVG) + _QT4_ADJUST_LIB_VARS(QTSCRIPT) + _QT4_ADJUST_LIB_VARS(QTUITOOLS) + _QT4_ADJUST_LIB_VARS(QTTEST) + _QT4_ADJUST_LIB_VARS(QTDBUS) + + # platform dependent libraries + IF(Q_WS_X11) + _QT4_ADJUST_LIB_VARS(QTMOTIF) + ENDIF(Q_WS_X11) + IF(WIN32) + _QT4_ADJUST_LIB_VARS(QTMAIN) + ENDIF(WIN32) + + + ####################################### + # + # Check the executables of Qt + # ( moc, uic, rcc ) + # + ####################################### + + + # find moc and uic using qmake + QT_QUERY_QMAKE(QT4_MOC_EXECUTABLE_INTERNAL "QMAKE_MOC") + QT_QUERY_QMAKE(QT4_UIC_EXECUTABLE_INTERNAL "QMAKE_UIC") + + FILE(TO_CMAKE_PATH + "${QT4_MOC_EXECUTABLE_INTERNAL}" QT4_MOC_EXECUTABLE_INTERNAL) + FILE(TO_CMAKE_PATH + "${QT4_UIC_EXECUTABLE_INTERNAL}" QT4_UIC_EXECUTABLE_INTERNAL) + + SET(QT4_MOC_EXECUTABLE + ${QT4_MOC_EXECUTABLE_INTERNAL} CACHE FILEPATH "The moc executable") + SET(QT4_UIC_EXECUTABLE + ${QT4_UIC_EXECUTABLE_INTERNAL} CACHE FILEPATH "The uic executable") + + FIND_PROGRAM(QT_UIC3_EXECUTABLE + NAMES uic3 + PATHS ${QT_BINARY_DIR} + NO_DEFAULT_PATH + ) + + FIND_PROGRAM(QT_RCC_EXECUTABLE + NAMES rcc + PATHS ${QT_BINARY_DIR} + NO_DEFAULT_PATH + ) + + FIND_PROGRAM(QT_DBUSCPP2XML_EXECUTABLE + NAMES qdbuscpp2xml + PATHS ${QT_BINARY_DIR} + NO_DEFAULT_PATH + ) + + FIND_PROGRAM(QT_DBUSXML2CPP_EXECUTABLE + NAMES qdbusxml2cpp + PATHS ${QT_BINARY_DIR} + NO_DEFAULT_PATH + ) + + IF (QT4_MOC_EXECUTABLE) + SET(QT_WRAP_CPP "YES") + ENDIF (QT4_MOC_EXECUTABLE) + + IF (QT4_UIC_EXECUTABLE) + SET(QT_WRAP_UI "YES") + ENDIF (QT4_UIC_EXECUTABLE) + + + + MARK_AS_ADVANCED( QT4_UIC_EXECUTABLE QT_UIC3_EXECUTABLE QT4_MOC_EXECUTABLE QT_RCC_EXECUTABLE QT_DBUSXML2CPP_EXECUTABLE QT_DBUSCPP2XML_EXECUTABLE) + + ###################################### + # + # Macros for building Qt files + # + ###################################### + MACRO (QT4_EXTRACT_OPTIONS _qt4_files _qt4_options) + SET(${_qt4_files}) + SET(${_qt4_options}) + SET(_QT4_DOING_OPTIONS FALSE) + FOREACH(_currentArg ${ARGN}) + IF ("${_currentArg}" STREQUAL "OPTIONS") + SET(_QT4_DOING_OPTIONS TRUE) + ELSE ("${_currentArg}" STREQUAL "OPTIONS") + IF(_QT4_DOING_OPTIONS) + LIST(APPEND ${_qt4_options} "${_currentArg}") + ELSE(_QT4_DOING_OPTIONS) + LIST(APPEND ${_qt4_files} "${_currentArg}") + ENDIF(_QT4_DOING_OPTIONS) + ENDIF ("${_currentArg}" STREQUAL "OPTIONS") + ENDFOREACH(_currentArg) + ENDMACRO (QT4_EXTRACT_OPTIONS) + + MACRO (QT4_GET_MOC_INC_DIRS _moc_INC_DIRS) + SET(${_moc_INC_DIRS}) + GET_DIRECTORY_PROPERTY(_inc_DIRS INCLUDE_DIRECTORIES) + + FOREACH(_current ${_inc_DIRS}) + SET(${_moc_INC_DIRS} ${${_moc_INC_DIRS}} "-I" ${_current}) + ENDFOREACH(_current ${_inc_DIRS}) + + ENDMACRO(QT4_GET_MOC_INC_DIRS) + + + MACRO (QT4_GENERATE_MOC infile outfile ) + # get include dirs + QT4_GET_MOC_INC_DIRS(moc_includes) + + GET_FILENAME_COMPONENT(abs_infile ${infile} ABSOLUTE) + + IF (MSVC_IDE) + SET (_moc_parameter_file ${outfile}_parameters) + SET (_moc_param "${moc_includes} \n-o${outfile} \n${abs_infile}") + STRING(REGEX REPLACE ";-I;" "\\n-I" _moc_param "${_moc_param}") + FILE (WRITE ${_moc_parameter_file} "${_moc_param}") + ADD_CUSTOM_COMMAND(OUTPUT ${outfile} + COMMAND ${QT4_MOC_EXECUTABLE} + ARGS @"${_moc_parameter_file}" + DEPENDS ${abs_infile}) + ELSE (MSVC_IDE) + ADD_CUSTOM_COMMAND(OUTPUT ${outfile} + COMMAND ${QT4_MOC_EXECUTABLE} + ARGS ${moc_includes} -o ${outfile} ${abs_infile} + DEPENDS ${abs_infile}) + ENDIF (MSVC_IDE) + + SET_SOURCE_FILES_PROPERTIES(${outfile} PROPERTIES SKIP_AUTOMOC TRUE) # dont run automoc on this file + + MACRO_ADD_FILE_DEPENDENCIES(${abs_infile} ${outfile}) + ENDMACRO (QT4_GENERATE_MOC) + + + # QT4_WRAP_CPP(outfiles inputfile ... ) + # TODO perhaps add support for -D, -U and other minor options + + MACRO (QT4_WRAP_CPP outfiles ) + # get include dirs + QT4_GET_MOC_INC_DIRS(moc_includes) + QT4_EXTRACT_OPTIONS(moc_files moc_options ${ARGN}) + + FOREACH (it ${moc_files}) + GET_FILENAME_COMPONENT(it ${it} ABSOLUTE) + GET_FILENAME_COMPONENT(outfile ${it} NAME_WE) + + SET(outfile ${CMAKE_CURRENT_BINARY_DIR}/moc_${outfile}.cxx) + ADD_CUSTOM_COMMAND(OUTPUT ${outfile} + COMMAND ${QT4_MOC_EXECUTABLE} + ARGS ${moc_includes} ${moc_options} -o ${outfile} ${it} + DEPENDS ${it}) + SET(${outfiles} ${${outfiles}} ${outfile}) + ENDFOREACH(it) + + ENDMACRO (QT4_WRAP_CPP) + + + # QT4_WRAP_UI(outfiles inputfile ... ) + + MACRO (QT4_WRAP_UI outfiles ) + QT4_EXTRACT_OPTIONS(ui_files ui_options ${ARGN}) + + FOREACH (it ${ui_files}) + GET_FILENAME_COMPONENT(outfile ${it} NAME_WE) + GET_FILENAME_COMPONENT(infile ${it} ABSOLUTE) + SET(outfile ${CMAKE_CURRENT_BINARY_DIR}/ui_${outfile}.h) + ADD_CUSTOM_COMMAND(OUTPUT ${outfile} + COMMAND ${QT4_UIC_EXECUTABLE} + ARGS ${ui_options} -o ${outfile} ${infile} + MAIN_DEPENDENCY ${infile}) + SET(${outfiles} ${${outfiles}} ${outfile}) + ENDFOREACH (it) + + ENDMACRO (QT4_WRAP_UI) + + + # QT4_ADD_RESOURCES(outfiles inputfile ... ) + # TODO perhaps consider adding support for compression and root options to rcc + + MACRO (QT4_ADD_RESOURCES outfiles ) + QT4_EXTRACT_OPTIONS(rcc_files rcc_options ${ARGN}) + + FOREACH (it ${rcc_files}) + GET_FILENAME_COMPONENT(outfilename ${it} NAME_WE) + GET_FILENAME_COMPONENT(infile ${it} ABSOLUTE) + GET_FILENAME_COMPONENT(rc_path ${infile} PATH) + SET(outfile ${CMAKE_CURRENT_BINARY_DIR}/qrc_${outfilename}.cxx) + # parse file for dependencies + # all files are absolute paths or relative to the location of the qrc file + FILE(READ "${infile}" _RC_FILE_CONTENTS) + STRING(REGEX MATCHALL "]*>" "" _RC_FILE "${_RC_FILE}") + STRING(REGEX MATCH "^/|([A-Za-z]:/)" _ABS_PATH_INDICATOR "${_RC_FILE}") + IF(NOT _ABS_PATH_INDICATOR) + SET(_RC_FILE "${rc_path}/${_RC_FILE}") + ENDIF(NOT _ABS_PATH_INDICATOR) + SET(_RC_DEPENDS ${_RC_DEPENDS} "${_RC_FILE}") + ENDFOREACH(_RC_FILE) + ADD_CUSTOM_COMMAND(OUTPUT ${outfile} + COMMAND ${QT_RCC_EXECUTABLE} + ARGS ${rcc_options} -name ${outfilename} -o ${outfile} ${infile} + MAIN_DEPENDENCY ${infile} + DEPENDS ${_RC_DEPENDS}) + SET(${outfiles} ${${outfiles}} ${outfile}) + ENDFOREACH (it) + + ENDMACRO (QT4_ADD_RESOURCES) + + MACRO(QT4_ADD_DBUS_INTERFACE _sources _interface _basename) + GET_FILENAME_COMPONENT(_infile ${_interface} ABSOLUTE) + SET(_header ${CMAKE_CURRENT_BINARY_DIR}/${_basename}.h) + SET(_impl ${CMAKE_CURRENT_BINARY_DIR}/${_basename}.cpp) + SET(_moc ${CMAKE_CURRENT_BINARY_DIR}/${_basename}.moc) + + GET_SOURCE_FILE_PROPERTY(_nonamespace ${_interface} NO_NAMESPACE) + IF ( _nonamespace ) + SET(_params -N -m) + ELSE ( _nonamespace ) + SET(_params -m) + ENDIF ( _nonamespace ) + + GET_SOURCE_FILE_PROPERTY(_include ${_interface} INCLUDE) + IF ( _include ) + SET(_params ${_params} -i ${_include}) + ENDIF ( _include ) + + ADD_CUSTOM_COMMAND(OUTPUT ${_impl} ${_header} + COMMAND ${QT_DBUSXML2CPP_EXECUTABLE} ${_params} -p ${_basename} ${_infile} + DEPENDS ${_infile}) + + SET_SOURCE_FILES_PROPERTIES(${_impl} PROPERTIES SKIP_AUTOMOC TRUE) + + QT4_GENERATE_MOC(${_header} ${_moc}) + + SET(${_sources} ${${_sources}} ${_impl} ${_header} ${_moc}) + MACRO_ADD_FILE_DEPENDENCIES(${_impl} ${_moc}) + + ENDMACRO(QT4_ADD_DBUS_INTERFACE) + + + MACRO(QT4_ADD_DBUS_INTERFACES _sources) + FOREACH (_current_FILE ${ARGN}) + GET_FILENAME_COMPONENT(_infile ${_current_FILE} ABSOLUTE) + # get the part before the ".xml" suffix + STRING(REGEX REPLACE "(.*[/\\.])?([^\\.]+)\\.xml" "\\2" _basename ${_current_FILE}) + STRING(TOLOWER ${_basename} _basename) + QT4_ADD_DBUS_INTERFACE(${_sources} ${_infile} ${_basename}interface) + ENDFOREACH (_current_FILE) + ENDMACRO(QT4_ADD_DBUS_INTERFACES) + + + MACRO(QT4_GENERATE_DBUS_INTERFACE _header) # _customName OPTIONS -some -options ) + QT4_EXTRACT_OPTIONS(_customName _qt4_dbus_options ${ARGN}) + + GET_FILENAME_COMPONENT(_in_file ${_header} ABSOLUTE) + GET_FILENAME_COMPONENT(_basename ${_header} NAME_WE) + + IF (_customName) + SET(_target ${CMAKE_CURRENT_BINARY_DIR}/${_customName}) + ELSE (_customName) + SET(_target ${CMAKE_CURRENT_BINARY_DIR}/${_basename}.xml) + ENDIF (_customName) + + ADD_CUSTOM_COMMAND(OUTPUT ${_target} + COMMAND ${QT_DBUSCPP2XML_EXECUTABLE} ${_qt4_dbus_options} ${_in_file} > ${_target} + DEPENDS ${_in_file} + ) + ENDMACRO(QT4_GENERATE_DBUS_INTERFACE) + + + MACRO(QT4_ADD_DBUS_ADAPTOR _sources _xml_file _include _parentClass) # _optionalBasename _optionalClassName) + GET_FILENAME_COMPONENT(_infile ${_xml_file} ABSOLUTE) + + SET(_optionalBasename "${ARGV4}") + IF (_optionalBasename) + SET(_basename ${_optionalBasename} ) + ELSE (_optionalBasename) + STRING(REGEX REPLACE "(.*[/\\.])?([^\\.]+)\\.xml" "\\2adaptor" _basename ${_infile}) + STRING(TOLOWER ${_basename} _basename) + ENDIF (_optionalBasename) + + SET(_optionalClassName "${ARGV5}") + SET(_header ${CMAKE_CURRENT_BINARY_DIR}/${_basename}.h) + SET(_impl ${CMAKE_CURRENT_BINARY_DIR}/${_basename}.cpp) + SET(_moc ${CMAKE_CURRENT_BINARY_DIR}/${_basename}.moc) + + IF(_optionalClassName) + ADD_CUSTOM_COMMAND(OUTPUT ${_impl} ${_header} + COMMAND ${QT_DBUSXML2CPP_EXECUTABLE} -m -a ${_basename} -c ${_optionalClassName} -i ${_include} -l ${_parentClass} ${_infile} + DEPENDS ${_infile} + ) + ELSE(_optionalClassName) + ADD_CUSTOM_COMMAND(OUTPUT ${_impl} ${_header} + COMMAND ${QT_DBUSXML2CPP_EXECUTABLE} -m -a ${_basename} -i ${_include} -l ${_parentClass} ${_infile} + DEPENDS ${_infile} + ) + ENDIF(_optionalClassName) + + QT4_GENERATE_MOC(${_header} ${_moc}) + SET_SOURCE_FILES_PROPERTIES(${_impl} PROPERTIES SKIP_AUTOMOC TRUE) + MACRO_ADD_FILE_DEPENDENCIES(${_impl} ${_moc}) + + SET(${_sources} ${${_sources}} ${_impl} ${_header} ${_moc}) + ENDMACRO(QT4_ADD_DBUS_ADAPTOR) + + MACRO(QT4_AUTOMOC) + QT4_GET_MOC_INC_DIRS(_moc_INCS) + + SET(_matching_FILES ) + FOREACH (_current_FILE ${ARGN}) + + GET_FILENAME_COMPONENT(_abs_FILE ${_current_FILE} ABSOLUTE) + # if "SKIP_AUTOMOC" is set to true, we will not handle this file here. + # here. this is required to make bouic work correctly: + # we need to add generated .cpp files to the sources (to compile them), + # but we cannot let automoc handle them, as the .cpp files don't exist yet when + # cmake is run for the very first time on them -> however the .cpp files might + # exist at a later run. at that time we need to skip them, so that we don't add two + # different rules for the same moc file + GET_SOURCE_FILE_PROPERTY(_skip ${_abs_FILE} SKIP_AUTOMOC) + + IF ( NOT _skip AND EXISTS ${_abs_FILE} ) + + FILE(READ ${_abs_FILE} _contents) + + GET_FILENAME_COMPONENT(_abs_PATH ${_abs_FILE} PATH) + + STRING(REGEX MATCHALL "#include +[^ ]+\\.moc[\">]" _match "${_contents}") + IF(_match) + FOREACH (_current_MOC_INC ${_match}) + STRING(REGEX MATCH "[^ <\"]+\\.moc" _current_MOC "${_current_MOC_INC}") + + GET_filename_component(_basename ${_current_MOC} NAME_WE) + # SET(_header ${CMAKE_CURRENT_SOURCE_DIR}/${_basename}.h) + IF (EXISTS ${_abs_PATH}/${_basename}.h) + SET(_header ${_abs_PATH}/${_basename}.h) + ELSE (EXISTS ${_abs_PATH}/${_basename}.h) + SET(_header ${_abs_FILE}) + ENDIF (EXISTS ${_abs_PATH}/${_basename}.h) + SET(_moc ${CMAKE_CURRENT_BINARY_DIR}/${_current_MOC}) + ADD_CUSTOM_COMMAND(OUTPUT ${_moc} + COMMAND ${QT4_MOC_EXECUTABLE} + ARGS ${_moc_INCS} ${_header} -o ${_moc} + DEPENDS ${_header} + ) + + MACRO_ADD_FILE_DEPENDENCIES(${_abs_FILE} ${_moc}) + ENDFOREACH (_current_MOC_INC) + ENDIF(_match) + ENDIF ( NOT _skip AND EXISTS ${_abs_FILE} ) + ENDFOREACH (_current_FILE) + ENDMACRO(QT4_AUTOMOC) + + + + ###################################### + # + # decide if Qt got found + # + ###################################### + + # if the includes,libraries,moc,uic and rcc are found then we have it + IF( QT4_LIBRARY_DIR AND QT4_INCLUDE_DIR AND QT4_MOC_EXECUTABLE AND QT4_UIC_EXECUTABLE AND QT_RCC_EXECUTABLE) + SET( QT4_FOUND "YES" ) + IF( NOT Qt4_FIND_QUIETLY) + MESSAGE(STATUS "Found Qt-Version ${QTVERSION} (using ${QT_QMAKE_EXECUTABLE})") + ENDIF( NOT Qt4_FIND_QUIETLY) + ELSE( QT4_LIBRARY_DIR AND QT4_INCLUDE_DIR AND QT4_MOC_EXECUTABLE AND QT4_UIC_EXECUTABLE AND QT_RCC_EXECUTABLE) + SET( QT4_FOUND "NO") + SET(QT_QMAKE_EXECUTABLE "${QT_QMAKE_EXECUTABLE}-NOTFOUND" CACHE FILEPATH "Invalid qmake found" FORCE) + IF( Qt4_FIND_REQUIRED) + IF ( NOT QT4_LIBRARY_DIR ) + MESSAGE(STATUS "Qt libraries NOT found!") + ENDIF(NOT QT4_LIBRARY_DIR ) + IF ( NOT QT4_INCLUDE_DIR ) + MESSAGE(STATUS "Qt includes NOT found!") + ENDIF( NOT QT4_INCLUDE_DIR ) + IF ( NOT QT4_MOC_EXECUTABLE ) + MESSAGE(STATUS "Qt's moc NOT found!") + ENDIF( NOT QT4_MOC_EXECUTABLE ) + IF ( NOT QT4_UIC_EXECUTABLE ) + MESSAGE(STATUS "Qt's uic NOT found!") + ENDIF( NOT QT4_UIC_EXECUTABLE ) + IF ( NOT QT_RCC_EXECUTABLE ) + MESSAGE(STATUS "Qt's rcc NOT found!") + ENDIF( NOT QT_RCC_EXECUTABLE ) + MESSAGE( FATAL_ERROR "Qt libraries, includes, moc, uic or/and rcc NOT found!") + ENDIF( Qt4_FIND_REQUIRED) + ENDIF( QT4_LIBRARY_DIR AND QT4_INCLUDE_DIR AND QT4_MOC_EXECUTABLE AND QT4_UIC_EXECUTABLE AND QT_RCC_EXECUTABLE) + SET(QT_FOUND ${QT4_FOUND}) + + + ####################################### + # + # System dependent settings + # + ####################################### + # for unix add X11 stuff + IF(UNIX) + # on OS X X11 may not be required + IF (Q_WS_X11) + FIND_PACKAGE(X11 REQUIRED) + ENDIF (Q_WS_X11) + FIND_PACKAGE(Threads) + SET(QT4_QTCORE_LIBRARY ${QT4_QTCORE_LIBRARY} ${CMAKE_THREAD_LIBS_INIT}) + ENDIF(UNIX) + + + ####################################### + # + # compatibility settings + # + ####################################### + # Backwards compatibility for CMake1.4 and 1.2 + SET (QT_MOC_EXE ${QT4_MOC_EXECUTABLE} ) + SET (QT_UIC_EXE ${QT4_UIC_EXECUTABLE} ) + + SET( QT4_QT_LIBRARY "") + +ELSE(QT4_QMAKE_FOUND) + + SET(QT_QMAKE_EXECUTABLE "${QT_QMAKE_EXECUTABLE}-NOTFOUND" CACHE FILEPATH "Invalid qmake found" FORCE) + IF(Qt4_FIND_REQUIRED) + IF(QT4_INSTALLED_VERSION_TOO_OLD) + MESSAGE(FATAL_ERROR "The installed Qt version ${QTVERSION} is too old, at least version ${QT4_MIN_VERSION} is required") + ELSE(QT4_INSTALLED_VERSION_TOO_OLD) + MESSAGE( FATAL_ERROR "Qt qmake not found!") + ENDIF(QT4_INSTALLED_VERSION_TOO_OLD) + ELSE(Qt4_FIND_REQUIRED) + IF(QT4_INSTALLED_VERSION_TOO_OLD AND NOT Qt4_FIND_QUIETLY) + MESSAGE(STATUS "The installed Qt version ${QTVERSION} is too old, at least version ${QT4_MIN_VERSION} is required") + ENDIF(QT4_INSTALLED_VERSION_TOO_OLD AND NOT Qt4_FIND_QUIETLY) + ENDIF(Qt4_FIND_REQUIRED) + +ENDIF (QT4_QMAKE_FOUND) +ENDIF (QT4_QMAKE_FOUND) + diff --git a/poppler-qt4.pc.cmake b/poppler-qt4.pc.cmake new file mode 100644 index 00000000..46a37f6d --- /dev/null +++ b/poppler-qt4.pc.cmake @@ -0,0 +1,12 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +libdir=@CMAKE_INSTALL_FULL_LIBDIR@ +includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ + +Name: poppler-qt4 +Description: Qt4 bindings for poppler +Version: @POPPLER_VERSION@ +Requires: @PC_REQUIRES@ +@PC_REQUIRES_PRIVATE@ + +Libs: -L${libdir} -lpoppler-qt4 +Cflags: -I${includedir}/poppler/qt4 diff --git a/qt4/.gitignore b/qt4/.gitignore new file mode 100644 index 00000000..5540f35d --- /dev/null +++ b/qt4/.gitignore @@ -0,0 +1,4 @@ +Makefile +Makefile.in +*~ + diff --git a/qt4/CMakeLists.txt b/qt4/CMakeLists.txt new file mode 100644 index 00000000..4d345681 --- /dev/null +++ b/qt4/CMakeLists.txt @@ -0,0 +1,6 @@ +# Qt4 headers are not override clean so disable suggest-override if it's there +string(REPLACE "-Wsuggest-override" " " CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) + +add_subdirectory(src) +add_subdirectory(tests) +add_subdirectory(demos) diff --git a/qt4/demos/.gitignore b/qt4/demos/.gitignore new file mode 100644 index 00000000..9639e685 --- /dev/null +++ b/qt4/demos/.gitignore @@ -0,0 +1,4 @@ +.deps +.libs +*moc +poppler_qt4viewer diff --git a/qt4/demos/CMakeLists.txt b/qt4/demos/CMakeLists.txt new file mode 100644 index 00000000..76accf81 --- /dev/null +++ b/qt4/demos/CMakeLists.txt @@ -0,0 +1,28 @@ +add_definitions(${QT4_DEFINITIONS}) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/../src + ${CMAKE_CURRENT_BINARY_DIR} + ${QT4_INCLUDE_DIR} +) + +set(poppler_qt4viewer_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 +) +qt4_automoc(${poppler_qt4viewer_SRCS}) +poppler_add_test(poppler_qt4viewer BUILD_QT4_TESTS ${poppler_qt4viewer_SRCS}) +target_link_libraries(poppler_qt4viewer poppler-qt4) diff --git a/qt4/demos/abstractinfodock.cpp b/qt4/demos/abstractinfodock.cpp new file mode 100644 index 00000000..7b306d82 --- /dev/null +++ b/qt4/demos/abstractinfodock.cpp @@ -0,0 +1,57 @@ +/* + * 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, SIGNAL(visibilityChanged(bool)), SLOT(slotVisibilityChanged(bool))); +} + +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; + } +} + +#include "abstractinfodock.moc" diff --git a/qt4/demos/abstractinfodock.h b/qt4/demos/abstractinfodock.h new file mode 100644 index 00000000..2593325a --- /dev/null +++ b/qt4/demos/abstractinfodock.h @@ -0,0 +1,48 @@ +/* + * 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 ABSTRACTINFODOCK_H +#define ABSTRACTINFODOCK_H + +#include + +#include "documentobserver.h" + +class AbstractInfoDock : public QDockWidget, public DocumentObserver +{ + Q_OBJECT + +public: + AbstractInfoDock(QWidget *parent = 0); + ~AbstractInfoDock(); + + /*virtual*/ void documentLoaded(); + /*virtual*/ void documentClosed(); + /*virtual*/ void pageChanged(int page); + +protected: + virtual void fillInfo() = 0; + +private Q_SLOTS: + void slotVisibilityChanged(bool visible); + +private: + bool m_filled; +}; + +#endif diff --git a/qt4/demos/documentobserver.cpp b/qt4/demos/documentobserver.cpp new file mode 100644 index 00000000..e5c283db --- /dev/null +++ b/qt4/demos/documentobserver.cpp @@ -0,0 +1,50 @@ +/* + * 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(0) +{ +} + +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()); +} diff --git a/qt4/demos/documentobserver.h b/qt4/demos/documentobserver.h new file mode 100644 index 00000000..38fe2043 --- /dev/null +++ b/qt4/demos/documentobserver.h @@ -0,0 +1,50 @@ +/* + * 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 DOCUMENTOBSERVER_H +#define DOCUMENTOBSERVER_H + +class PdfViewer; +namespace Poppler { +class Document; +} + +class DocumentObserver +{ +friend class PdfViewer; + +public: + virtual ~DocumentObserver(); + + 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 diff --git a/qt4/demos/embeddedfiles.cpp b/qt4/demos/embeddedfiles.cpp new file mode 100644 index 00000000..22f9da7a --- /dev/null +++ b/qt4/demos/embeddedfiles.cpp @@ -0,0 +1,82 @@ +/* + * 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::fromAscii(checksum.toHex()) : QString::fromLatin1("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(); +} + +#include "embeddedfiles.moc" diff --git a/qt4/demos/embeddedfiles.h b/qt4/demos/embeddedfiles.h new file mode 100644 index 00000000..7cd60397 --- /dev/null +++ b/qt4/demos/embeddedfiles.h @@ -0,0 +1,44 @@ +/* + * 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 ATTACHMENTS_H +#define ATTACHMENTS_H + +#include "abstractinfodock.h" + +class QTableWidget; + +class EmbeddedFilesDock : public AbstractInfoDock +{ + Q_OBJECT + +public: + EmbeddedFilesDock(QWidget *parent = 0); + ~EmbeddedFilesDock(); + + virtual void documentLoaded(); + /*virtual*/ void documentClosed(); + +protected: + /*virtual*/ void fillInfo(); + +private: + QTableWidget *m_table; +}; + +#endif diff --git a/qt4/demos/fonts.cpp b/qt4/demos/fonts.cpp new file mode 100644 index 00000000..bd342bd2 --- /dev/null +++ b/qt4/demos/fonts.cpp @@ -0,0 +1,72 @@ +/* + * 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 ? QString::fromLatin1("yes") : QString::fromLatin1("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(QString::fromLatin1("[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(); +} + +#include "fonts.moc" diff --git a/qt4/demos/fonts.h b/qt4/demos/fonts.h new file mode 100644 index 00000000..81afa579 --- /dev/null +++ b/qt4/demos/fonts.h @@ -0,0 +1,43 @@ +/* + * 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 FONTS_H +#define FONTS_H + +#include "abstractinfodock.h" + +class QTableWidget; + +class FontsDock : public AbstractInfoDock +{ + Q_OBJECT + +public: + FontsDock(QWidget *parent = 0); + ~FontsDock(); + + /*virtual*/ void documentClosed(); + +protected: + /*virtual*/ void fillInfo(); + +private: + QTableWidget *m_table; +}; + +#endif diff --git a/qt4/demos/info.cpp b/qt4/demos/info.cpp new file mode 100644 index 00000000..6491e0e4 --- /dev/null +++ b/qt4/demos/info.cpp @@ -0,0 +1,72 @@ +/* + * 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 << QString::fromLatin1("CreationDate"); + dateKeys << QString::fromLatin1("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(); +} + +#include "info.moc" diff --git a/qt4/demos/info.h b/qt4/demos/info.h new file mode 100644 index 00000000..d294b430 --- /dev/null +++ b/qt4/demos/info.h @@ -0,0 +1,43 @@ +/* + * 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 INFO_H +#define INFO_H + +#include "abstractinfodock.h" + +class QTableWidget; + +class InfoDock : public AbstractInfoDock +{ + Q_OBJECT + +public: + InfoDock(QWidget *parent = 0); + ~InfoDock(); + + /*virtual*/ void documentClosed(); + +protected: + /*virtual*/ void fillInfo(); + +private: + QTableWidget *m_table; +}; + +#endif diff --git a/qt4/demos/main_viewer.cpp b/qt4/demos/main_viewer.cpp new file mode 100644 index 00000000..3f7080f2 --- /dev/null +++ b/qt4/demos/main_viewer.cpp @@ -0,0 +1,33 @@ +/* + * 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(); +} diff --git a/qt4/demos/metadata.cpp b/qt4/demos/metadata.cpp new file mode 100644 index 00000000..e5c7111d --- /dev/null +++ b/qt4/demos/metadata.cpp @@ -0,0 +1,50 @@ +/* + * 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(); +} + +#include "metadata.moc" diff --git a/qt4/demos/metadata.h b/qt4/demos/metadata.h new file mode 100644 index 00000000..6f1507a6 --- /dev/null +++ b/qt4/demos/metadata.h @@ -0,0 +1,43 @@ +/* + * 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 METADATA_H +#define METADATA_H + +#include "abstractinfodock.h" + +class QTextEdit; + +class MetadataDock : public AbstractInfoDock +{ + Q_OBJECT + +public: + MetadataDock(QWidget *parent = 0); + ~MetadataDock(); + + /*virtual*/ void documentClosed(); + +protected: + /*virtual*/ void fillInfo(); + +private: + QTextEdit *m_edit; +}; + +#endif diff --git a/qt4/demos/navigationtoolbar.cpp b/qt4/demos/navigationtoolbar.cpp new file mode 100644 index 00000000..79dd418a --- /dev/null +++ b/qt4/demos/navigationtoolbar.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2008-2009, Pino Toscano + * Copyright (C) 2013, Fabio D'Urso + * + * This program is free software; you can redistribute 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, SIGNAL(activated(int)), this, SLOT(slotComboActivated(int))); + 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, SIGNAL(currentIndexChanged(QString)), this, SLOT(slotZoomComboChanged(QString))); + addWidget(m_zoomCombo); + + m_rotationCombo = new QComboBox(this); + // NOTE: \302\260 = degree symbol + m_rotationCombo->addItem(trUtf8("0\302\260")); + m_rotationCombo->addItem(trUtf8("90\302\260")); + m_rotationCombo->addItem(trUtf8("180\302\260")); + m_rotationCombo->addItem(trUtf8("270\302\260")); + connect(m_rotationCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(slotRotationComboChanged(int))); + 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); +} + +#include "navigationtoolbar.moc" diff --git a/qt4/demos/navigationtoolbar.h b/qt4/demos/navigationtoolbar.h new file mode 100644 index 00000000..d7dbd5dd --- /dev/null +++ b/qt4/demos/navigationtoolbar.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2008-2009, Pino Toscano + * Copyright (C) 2013, Fabio D'Urso + * + * This program is free software; you can redistribute 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: + NavigationToolBar(QWidget *parent = 0); + ~NavigationToolBar(); + + /*virtual*/ void documentLoaded(); + /*virtual*/ void documentClosed(); + /*virtual*/ void pageChanged(int page); + +Q_SIGNALS: + void zoomChanged(qreal value); + void rotationChanged(int rotation); + +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 diff --git a/qt4/demos/optcontent.cpp b/qt4/demos/optcontent.cpp new file mode 100644 index 00000000..0c1015b9 --- /dev/null +++ b/qt4/demos/optcontent.cpp @@ -0,0 +1,69 @@ +/* + * 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(), SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(reloadImage())); + m_view->expandToDepth(1); +} + +void OptContentDock::documentClosed() +{ + m_view->setModel(0); + AbstractInfoDock::documentClosed(); +} + +void OptContentDock::reloadImage() +{ + reloadPage(); +} + +#include "optcontent.moc" diff --git a/qt4/demos/optcontent.h b/qt4/demos/optcontent.h new file mode 100644 index 00000000..b933f5cd --- /dev/null +++ b/qt4/demos/optcontent.h @@ -0,0 +1,47 @@ +/* + * 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 OPTCONTENT_H +#define OPTCONTENT_H + +#include "abstractinfodock.h" + +class QTreeView; + +class OptContentDock : public AbstractInfoDock +{ + Q_OBJECT + +public: + OptContentDock(QWidget *parent = 0); + ~OptContentDock(); + + /*virtual*/ void documentLoaded(); + /*virtual*/ void documentClosed(); + +protected: + /*virtual*/ void fillInfo(); + +private Q_SLOTS: + void reloadImage(); + +private: + QTreeView *m_view; +}; + +#endif diff --git a/qt4/demos/pageview.cpp b/qt4/demos/pageview.cpp new file mode 100644 index 00000000..0dfa5e9e --- /dev/null +++ b/qt4/demos/pageview.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2008-2009, Pino Toscano + * Copyright (C) 2013, Fabio D'Urso + * + * This program is free software; you can redistribute 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(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); + 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(); +} + +#include "pageview.moc" diff --git a/qt4/demos/pageview.h b/qt4/demos/pageview.h new file mode 100644 index 00000000..24065028 --- /dev/null +++ b/qt4/demos/pageview.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2008-2009, Pino Toscano + * Copyright (C) 2013, Fabio D'Urso + * + * This program is free software; you can redistribute 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: + PageView(QWidget *parent = 0); + ~PageView(); + + /*virtual*/ void documentLoaded(); + /*virtual*/ void documentClosed(); + /*virtual*/ void pageChanged(int page); + +private 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 diff --git a/qt4/demos/permissions.cpp b/qt4/demos/permissions.cpp new file mode 100644 index 00000000..38205b2e --- /dev/null +++ b/qt4/demos/permissions.cpp @@ -0,0 +1,66 @@ +/* + * 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(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(); +} + +#include "permissions.moc" diff --git a/qt4/demos/permissions.h b/qt4/demos/permissions.h new file mode 100644 index 00000000..13bcbbf0 --- /dev/null +++ b/qt4/demos/permissions.h @@ -0,0 +1,43 @@ +/* + * 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. + */ + +#ifndef PERMISSIONS_H +#define PERMISSIONS_H + +#include "abstractinfodock.h" + +class QListWidget; + +class PermissionsDock : public AbstractInfoDock +{ + Q_OBJECT + +public: + PermissionsDock(QWidget *parent = 0); + ~PermissionsDock(); + + /*virtual*/ void documentClosed(); + +protected: + /*virtual*/ void fillInfo(); + +private: + QListWidget *m_table; +}; + +#endif diff --git a/qt4/demos/thumbnails.cpp b/qt4/demos/thumbnails.cpp new file mode 100644 index 00000000..07b19ca7 --- /dev/null +++ b/qt4/demos/thumbnails.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2009, Shawn Rutledge + * 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. + */ + +#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, SIGNAL(itemActivated(QListWidgetItem*)), + this, SLOT(slotItemActivated(QListWidgetItem*))); +} + +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->thumbnail(); + 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()); +} + +#include "thumbnails.moc" diff --git a/qt4/demos/thumbnails.h b/qt4/demos/thumbnails.h new file mode 100644 index 00000000..076d5aee --- /dev/null +++ b/qt4/demos/thumbnails.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2009, Shawn Rutledge + * 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 THUMBNAILS_H +#define THUMBNAILS_H + +#include "abstractinfodock.h" + +class QListWidget; +class QListWidgetItem; + +class ThumbnailsDock : public AbstractInfoDock +{ + Q_OBJECT + +public: + ThumbnailsDock(QWidget *parent = 0); + ~ThumbnailsDock(); + + /*virtual*/ void documentClosed(); + +protected: + /*virtual*/ void fillInfo(); + +private Q_SLOTS: + void slotItemActivated(QListWidgetItem *item); + +private: + QListWidget *m_list; +}; + +#endif diff --git a/qt4/demos/toc.cpp b/qt4/demos/toc.cpp new file mode 100644 index 00000000..bf3e5cbb --- /dev/null +++ b/qt4/demos/toc.cpp @@ -0,0 +1,88 @@ +/* + * 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 "toc.h" + +#include + +#include +#include + +static void fillToc(const QDomNode &parent, QTreeWidget *tree, QTreeWidgetItem *parentItem) +{ + QTreeWidgetItem *newitem = 0; + for (QDomNode node = parent.firstChild(); !node.isNull(); node = node.nextSibling()) { + QDomElement e = node.toElement(); + + if (!parentItem) { + newitem = new QTreeWidgetItem(tree, newitem); + } else { + newitem = new QTreeWidgetItem(parentItem, newitem); + } + newitem->setText(0, e.tagName()); + + bool isOpen = false; + if (e.hasAttribute(QString::fromLatin1("Open"))) { + isOpen = QVariant(e.attribute(QString::fromLatin1("Open"))).toBool(); + } + if (isOpen) { + tree->expandItem(newitem); + } + + if (e.hasChildNodes()) { + fillToc(node, tree, newitem); + } + } +} + + +TocDock::TocDock(QWidget *parent) + : AbstractInfoDock(parent) +{ + m_tree = new QTreeWidget(this); + setWidget(m_tree); + m_tree->setAlternatingRowColors(true); + m_tree->header()->hide(); + setWindowTitle(tr("TOC")); + m_tree->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); +} + +TocDock::~TocDock() +{ +} + +void TocDock::fillInfo() +{ + const QDomDocument *toc = document()->toc(); + if (toc) { + fillToc(*toc, m_tree, 0); + } else { + QTreeWidgetItem *item = new QTreeWidgetItem(); + item->setText(0, tr("No TOC")); + item->setFlags(item->flags() & ~Qt::ItemIsEnabled); + m_tree->addTopLevelItem(item); + } +} + +void TocDock::documentClosed() +{ + m_tree->clear(); + AbstractInfoDock::documentClosed(); +} + +#include "toc.moc" diff --git a/qt4/demos/toc.h b/qt4/demos/toc.h new file mode 100644 index 00000000..bbc90827 --- /dev/null +++ b/qt4/demos/toc.h @@ -0,0 +1,43 @@ +/* + * 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 TOC_H +#define TOC_H + +#include "abstractinfodock.h" + +class QTreeWidget; + +class TocDock : public AbstractInfoDock +{ + Q_OBJECT + +public: + TocDock(QWidget *parent = 0); + ~TocDock(); + + /*virtual*/ void documentClosed(); + +protected: + /*virtual*/ void fillInfo(); + +private: + QTreeWidget *m_tree; +}; + +#endif diff --git a/qt4/demos/viewer.cpp b/qt4/demos/viewer.cpp new file mode 100644 index 00000000..c34af23f --- /dev/null +++ b/qt4/demos/viewer.cpp @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2008-2009, Pino Toscano + * Copyright (C) 2008, Albert Astals Cid + * Copyright (C) 2009, Shawn Rutledge + * Copyright (C) 2013, Fabio D'Urso + * + * This program is free software; you can redistribute 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 + +PdfViewer::PdfViewer() + : QMainWindow(), m_currentPage(0), m_doc(0) +{ + setWindowTitle(tr("Poppler-Qt4 Demo")); + + // setup the menus + QMenu *fileMenu = menuBar()->addMenu(tr("&File")); + m_fileOpenAct = fileMenu->addAction(tr("&Open"), this, SLOT(slotOpenFile())); + m_fileOpenAct->setShortcut(Qt::CTRL + Qt::Key_O); + fileMenu->addSeparator(); + m_fileSaveCopyAct = fileMenu->addAction(tr("&Save a Copy..."), this, SLOT(slotSaveCopy())); + m_fileSaveCopyAct->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_S); + m_fileSaveCopyAct->setEnabled(false); + fileMenu->addSeparator(); + QAction *act = fileMenu->addAction(tr("&Quit"), qApp, SLOT(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, SIGNAL(toggled(bool)), this, SLOT(slotToggleTextAA(bool))); + m_settingsGfxAAAct = settingsMenu->addAction(tr("Graphics Antialias")); + m_settingsGfxAAAct->setCheckable(true); + connect(m_settingsGfxAAAct, SIGNAL(toggled(bool)), this, SLOT(slotToggleGfxAA(bool))); + 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(qVariantFromValue(0)); + m_settingsRenderBackendGrp->addAction(act); + act = settingsRenderMenu->addAction(tr("Arthur")); + act->setCheckable(true); + act->setData(qVariantFromValue(1)); + m_settingsRenderBackendGrp->addAction(act); + connect(m_settingsRenderBackendGrp, SIGNAL(triggered(QAction*)), + this, SLOT(slotRenderBackend(QAction*))); + + QMenu *helpMenu = menuBar()->addMenu(tr("&Help")); + act = helpMenu->addAction(tr("&About"), this, SLOT(slotAbout())); + act = helpMenu->addAction(tr("About &Qt"), this, SLOT(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, SIGNAL(zoomChanged(qreal)), view, SLOT(slotZoomChanged(qreal))); + connect(navbar, SIGNAL(rotationChanged(int)), view, SLOT(slotRotationChanged(int))); + + // 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) +{ + 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()); + + 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 = 0; + + m_fileSaveCopyAct->setEnabled(false); +} + +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() +{ + const QString text("This is a demo of the Poppler-Qt4 library."); + QMessageBox::about(this, QString::fromLatin1("About Poppler-Qt4 Demo"), text); +} + +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; +} + +#include "viewer.moc" diff --git a/qt4/demos/viewer.h b/qt4/demos/viewer.h new file mode 100644 index 00000000..5e0eaaff --- /dev/null +++ b/qt4/demos/viewer.h @@ -0,0 +1,73 @@ +/* + * 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 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: + PdfViewer(); + ~PdfViewer(); + + /*virtual*/ QSize sizeHint() const; + + 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; + + int m_currentPage; + + QAction *m_fileOpenAct; + QAction *m_fileSaveCopyAct; + QAction *m_settingsTextAAAct; + QAction *m_settingsGfxAAAct; + QActionGroup *m_settingsRenderBackendGrp; + + QList m_observers; + + Poppler::Document *m_doc; +}; + +#endif diff --git a/qt4/src/.gitignore b/qt4/src/.gitignore new file mode 100644 index 00000000..3d124ddd --- /dev/null +++ b/qt4/src/.gitignore @@ -0,0 +1,9 @@ +.deps +.libs +*.la +*.lo +Makefile +Makefile.in +APIDOCS-html +APIDOCS-latex +*.moc diff --git a/qt4/src/ArthurOutputDev.cc b/qt4/src/ArthurOutputDev.cc new file mode 100644 index 00000000..f2fa6f17 --- /dev/null +++ b/qt4/src/ArthurOutputDev.cc @@ -0,0 +1,817 @@ +//======================================================================== +// +// ArthurOutputDev.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 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) 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 + +#ifdef USE_GCC_PRAGMAS +#pragma implementation +#endif + +#include +#include + +#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 "ArthurOutputDev.h" + +#include +#include +//------------------------------------------------------------------------ + +#ifdef HAVE_SPLASH +#include "splash/SplashFontFileID.h" +#include "splash/SplashFontFile.h" +#include "splash/SplashFontEngine.h" +#include "splash/SplashFont.h" +#include "splash/SplashMath.h" +#include "splash/SplashPath.h" +#include "splash/SplashGlyphBitmap.h" +//------------------------------------------------------------------------ +// SplashOutFontFileID +//------------------------------------------------------------------------ + +class SplashOutFontFileID: public SplashFontFileID { +public: + + SplashOutFontFileID(const Ref *rA) { r = *rA; } + + ~SplashOutFontFileID() {} + + bool matches(SplashFontFileID *id) { + return ((SplashOutFontFileID *)id)->r.num == r.num && + ((SplashOutFontFileID *)id)->r.gen == r.gen; + } + +private: + + Ref r; +}; + +#endif + +//------------------------------------------------------------------------ +// ArthurOutputDev +//------------------------------------------------------------------------ + +ArthurOutputDev::ArthurOutputDev(QPainter *painter): + m_painter(painter), + m_fontHinting(NoHinting) +{ + m_currentBrush = QBrush(Qt::SolidPattern); + m_fontEngine = 0; + m_font = 0; +} + +ArthurOutputDev::~ArthurOutputDev() +{ +#ifdef HAVE_SPLASH + delete m_fontEngine; +#endif +} + +void ArthurOutputDev::startDoc(XRef *xrefA) { + xref = xrefA; +#ifdef HAVE_SPLASH + delete m_fontEngine; + + const bool isHintingEnabled = m_fontHinting != NoHinting; + const bool isSlightHinting = m_fontHinting == SlightHinting; + + m_fontEngine = new SplashFontEngine( + true, // I just set this to true. We should finally remove the QT4. + isHintingEnabled, + isSlightHinting, + m_painter->testRenderHint(QPainter::TextAntialiasing)); +#endif +} + +void ArthurOutputDev::startPage(int pageNum, GfxState *state, XRef *xref) +{ + // fill page with white background. + int w = static_cast(state->getPageWidth()); + int h = static_cast(state->getPageHeight()); + QColor fillColour(Qt::white); + QBrush fill(fillColour); + m_painter->save(); + m_painter->setPen(fillColour); + m_painter->setBrush(fill); + m_painter->drawRect(0, 0, w, h); + m_painter->restore(); +} + +void ArthurOutputDev::endPage() { +} + +void ArthurOutputDev::saveState(GfxState *state) +{ + m_painter->save(); +} + +void ArthurOutputDev::restoreState(GfxState *state) +{ + m_painter->restore(); +} + +void ArthurOutputDev::updateAll(GfxState *state) +{ + OutputDev::updateAll(state); + m_needFontUpdate = true; +} + +// This looks wrong - why aren't adjusting the matrix? +void ArthurOutputDev::updateCTM(GfxState *state, double m11, double m12, + double m21, double m22, + double m31, double m32) +{ + updateLineDash(state); + updateLineJoin(state); + updateLineCap(state); + updateLineWidth(state); +} + +void ArthurOutputDev::updateLineDash(GfxState *state) +{ + double *dashPattern; + int dashLength; + double dashStart; + state->getLineDash(&dashPattern, &dashLength, &dashStart); + QVector pattern(dashLength); + for (int i = 0; i < dashLength; ++i) { + pattern[i] = dashPattern[i]; + } + m_currentPen.setDashPattern(pattern); + m_currentPen.setDashOffset(dashStart); + m_painter->setPen(m_currentPen); +} + +void ArthurOutputDev::updateFlatness(GfxState *state) +{ + // qDebug() << "updateFlatness"; +} + +void ArthurOutputDev::updateLineJoin(GfxState *state) +{ + switch (state->getLineJoin()) { + case 0: + m_currentPen.setJoinStyle(Qt::MiterJoin); + break; + case 1: + m_currentPen.setJoinStyle(Qt::RoundJoin); + break; + case 2: + m_currentPen.setJoinStyle(Qt::BevelJoin); + break; + } + m_painter->setPen(m_currentPen); +} + +void ArthurOutputDev::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->setPen(m_currentPen); +} + +void ArthurOutputDev::updateMiterLimit(GfxState *state) +{ + m_currentPen.setMiterLimit(state->getMiterLimit()); + m_painter->setPen(m_currentPen); +} + +void ArthurOutputDev::updateLineWidth(GfxState *state) +{ + m_currentPen.setWidthF(state->getLineWidth()); + m_painter->setPen(m_currentPen); +} + +void ArthurOutputDev::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 ArthurOutputDev::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->setPen(m_currentPen); +} + +void ArthurOutputDev::updateFillOpacity(GfxState *state) +{ + QColor brushColour= m_currentBrush.color(); + brushColour.setAlphaF(state->getFillOpacity()); + m_currentBrush.setColor(brushColour); +} + +void ArthurOutputDev::updateStrokeOpacity(GfxState *state) +{ + QColor penColour= m_currentPen.color(); + penColour.setAlphaF(state->getStrokeOpacity()); + m_currentPen.setColor(penColour); + m_painter->setPen(m_currentPen); +} + +void ArthurOutputDev::updateFont(GfxState *state) +{ +#ifdef HAVE_SPLASH + GfxFont *gfxFont; + GfxFontLoc *fontLoc; + GfxFontType fontType; + SplashOutFontFileID *id; + SplashFontFile *fontFile; + SplashFontSrc *fontsrc = NULL; + FoFiTrueType *ff; + Object refObj, strObj; + GooString *fileName; + char *tmpBuf; + int tmpBufLen = 0; + int *codeToGID; + const double *textMat; + double m11, m12, m21, m22, fontSize; + SplashCoord mat[4]; + int n; + int faceIndex = 0; + SplashCoord matrix[6]; + + m_needFontUpdate = false; + m_font = NULL; + fileName = NULL; + tmpBuf = NULL; + fontLoc = NULL; + + if (!(gfxFont = state->getFont())) { + goto err1; + } + fontType = gfxFont->getType(); + if (fontType == fontType3) { + goto err1; + } + + // check the font file cache + id = new SplashOutFontFileID(gfxFont->getID()); + if ((fontFile = m_fontEngine->getFontFile(id))) { + delete id; + + } else { + + if (!(fontLoc = gfxFont->locateFont(xref, NULL))) { + 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) { + // if there is an embedded font, read it to memory + tmpBuf = gfxFont->readEmbFontFile(xref, &tmpBufLen); + if (! tmpBuf) + goto err2; + + // external font + } else { // gfxFontLocExternal + fileName = fontLoc->path; + fontType = fontLoc->fontType; + } + + fontsrc = new SplashFontSrc; + if (fileName) + fontsrc->setFile(fileName, false); + else + fontsrc->setBuf(tmpBuf, tmpBufLen, true); + + // load the font file + switch (fontType) { + case fontType1: + if (!(fontFile = m_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)"); + goto err2; + } + break; + case fontType1C: + if (!(fontFile = m_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)"); + goto err2; + } + break; + case fontType1COT: + if (!(fontFile = m_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)"); + goto err2; + } + break; + case fontTrueType: + case fontTrueTypeOT: + if (fileName) + ff = FoFiTrueType::load(fileName->c_str()); + else + ff = FoFiTrueType::make(tmpBuf, tmpBufLen); + if (ff) { + codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff); + n = 256; + delete ff; + } else { + codeToGID = NULL; + n = 0; + } + if (!(fontFile = m_fontEngine->loadTrueTypeFont( + id, + fontsrc, + codeToGID, n))) { + error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", + gfxFont->getName() ? gfxFont->getName()->c_str() + : "(unnamed)"); + goto err2; + } + break; + case fontCIDType0: + case fontCIDType0C: + if (!(fontFile = m_fontEngine->loadCIDFont( + id, + fontsrc))) { + error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", + gfxFont->getName() ? gfxFont->getName()->c_str() + : "(unnamed)"); + goto err2; + } + break; + case fontCIDType0COT: + if (((GfxCIDFont *)gfxFont)->getCIDToGID()) { + n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(); + codeToGID = (int *)gmallocn(n, sizeof(int)); + memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(), + n * sizeof(int)); + } else { + codeToGID = NULL; + n = 0; + } + if (!(fontFile = m_fontEngine->loadOpenTypeCFFFont( + id, + fontsrc, + codeToGID, n))) { + error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", + gfxFont->getName() ? gfxFont->getName()->c_str() + : "(unnamed)"); + goto err2; + } + break; + case fontCIDType2: + case fontCIDType2OT: + codeToGID = NULL; + 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 { + if (fileName) + ff = FoFiTrueType::load(fileName->c_str()); + else + ff = FoFiTrueType::make(tmpBuf, tmpBufLen); + if (! ff) + goto err2; + codeToGID = ((GfxCIDFont *)gfxFont)->getCodeToGIDMap(ff, &n); + delete ff; + } + if (!(fontFile = m_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)"); + goto err2; + } + break; + default: + // this shouldn't happen + goto err2; + } + } + + // 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; + + { + QMatrix painterMatrix = m_painter->worldMatrix(); + matrix[0] = painterMatrix.m11(); + matrix[1] = painterMatrix.m12(); + matrix[2] = painterMatrix.m21(); + matrix[3] = painterMatrix.m22(); + matrix[4] = painterMatrix.dx(); + matrix[5] = painterMatrix.dy(); + } + + // create the scaled font + mat[0] = m11; mat[1] = -m12; + mat[2] = m21; mat[3] = -m22; + m_font = m_fontEngine->getFont(fontFile, mat, matrix); + + delete fontLoc; + if (fontsrc && !fontsrc->isFile) + fontsrc->unref(); + return; + + err2: + delete id; + delete fontLoc; + err1: + if (fontsrc && !fontsrc->isFile) + fontsrc->unref(); + return; +#endif +} + +static QPainterPath convertPath(GfxState *state, const GfxPath *path, Qt::FillRule fillRule) +{ + const GfxSubpath *subpath; + double x1, y1, x2, y2, x3, y3; + int i, j; + + QPainterPath qPath; + qPath.setFillRule(fillRule); + for (i = 0; i < path->getNumSubpaths(); ++i) { + subpath = path->getSubpath(i); + if (subpath->getNumPoints() > 0) { + state->transform(subpath->getX(0), subpath->getY(0), &x1, &y1); + qPath.moveTo(x1, y1); + j = 1; + while (j < subpath->getNumPoints()) { + if (subpath->getCurve(j)) { + state->transform(subpath->getX(j), subpath->getY(j), &x1, &y1); + state->transform(subpath->getX(j+1), subpath->getY(j+1), &x2, &y2); + state->transform(subpath->getX(j+2), subpath->getY(j+2), &x3, &y3); + qPath.cubicTo( x1, y1, x2, y2, x3, y3); + j += 3; + } else { + state->transform(subpath->getX(j), subpath->getY(j), &x1, &y1); + qPath.lineTo(x1, y1); + ++j; + } + } + if (subpath->isClosed()) { + qPath.closeSubpath(); + } + } + } + return qPath; +} + +void ArthurOutputDev::stroke(GfxState *state) +{ + m_painter->strokePath( convertPath( state, state->getPath(), Qt::OddEvenFill ), m_currentPen ); +} + +void ArthurOutputDev::fill(GfxState *state) +{ + m_painter->fillPath( convertPath( state, state->getPath(), Qt::WindingFill ), m_currentBrush ); +} + +void ArthurOutputDev::eoFill(GfxState *state) +{ + m_painter->fillPath( convertPath( state, state->getPath(), Qt::OddEvenFill ), m_currentBrush ); +} + +void ArthurOutputDev::clip(GfxState *state) +{ + m_painter->setClipPath(convertPath( state, state->getPath(), Qt::WindingFill ) ); +} + +void ArthurOutputDev::eoClip(GfxState *state) +{ + m_painter->setClipPath(convertPath( state, state->getPath(), Qt::OddEvenFill ) ); +} + +void ArthurOutputDev::drawChar(GfxState *state, double x, double y, + double dx, double dy, + double originX, double originY, + CharCode code, int nBytes, Unicode *u, int uLen) { +#ifdef HAVE_SPLASH + double x1, y1; + double x2, y2; + double px, py; +// SplashPath *path; + int render; + unsigned char f; + + if (m_needFontUpdate) { + updateFont(state); + } + if (!m_font) { + return; + } + + // check for invisible text -- this is used by Acrobat Capture + render = state->getRender(); + if (render == 3) { + return; + } + + x -= originX; + y -= originY; + + // fill + if (!(render & 1)) { + SplashPath * fontPath; + fontPath = m_font->getGlyphPath(code); + if (fontPath) { + QPainterPath qPath; + qPath.setFillRule(Qt::WindingFill); + for (int i = 0; i < fontPath->getLength(); ++i) { + // SplashPath.flags: bitwise or allowed + fontPath->getPoint(i, &px, &py, &f); + if (f & splashPathLast || f & splashPathClosed) { + qPath.closeSubpath(); + } + if (f & splashPathFirst) { + state->transform(px+x, -py+y, &x1, &y1); + qPath.moveTo(x1,y1); + } + if (f & splashPathCurve) { + state->transform(px+x, -py+y, &x1, &y1); + fontPath->getPoint(i+1, &px, &py, &f); + state->transform(px+x, -py+y, &x2, &y2); + qPath.quadTo(x1,y1,x2,y2); + ++i; + } + // FIXME fix this + // else if (fontPath->flags[i] & splashPathArcCW) { + // qDebug() << "Need to implement arc"; + // } + else { + fontPath->getPoint(i, &px, &py, &f); + state->transform(px+x, -py+y, &x1, &y1); + qPath.lineTo(x1,y1); + } + } + GfxRGB rgb; + QColor brushColour = m_currentBrush.color(); + state->getFillRGB(&rgb); + brushColour.setRgbF(colToDbl(rgb.r), colToDbl(rgb.g), colToDbl(rgb.b), state->getFillOpacity()); + m_painter->setBrush(brushColour); + m_painter->setPen(Qt::NoPen); + m_painter->drawPath(qPath); + delete fontPath; + } + } + + // stroke + if ((render & 3) == 1 || (render & 3) == 2) { + qDebug() << "no stroke"; + /* + if ((path = m_font->getGlyphPath(code))) { + path->offset((SplashCoord)x1, (SplashCoord)y1); + splash->stroke(path); + delete path; + } + */ + } + + // clip + if (render & 4) { + qDebug() << "no clip"; + /* + path = m_font->getGlyphPath(code); + path->offset((SplashCoord)x1, (SplashCoord)y1); + if (textClipPath) { + textClipPath->append(path); + delete path; + } else { + textClipPath = path; + } + */ + } +#endif +} + +bool ArthurOutputDev::beginType3Char(GfxState *state, double x, double y, + double dx, double dy, + CharCode code, Unicode *u, int uLen) +{ + return false; +} + +void ArthurOutputDev::endType3Char(GfxState *state) +{ +} + +void ArthurOutputDev::type3D0(GfxState *state, double wx, double wy) +{ +} + +void ArthurOutputDev::type3D1(GfxState *state, double wx, double wy, + double llx, double lly, double urx, double ury) +{ +} + +void ArthurOutputDev::endTextObject(GfxState *state) +{ +} + + +void ArthurOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, + int width, int height, bool invert, + bool interpolate, bool inlineImg) +{ + qDebug() << "drawImageMask"; +#if 0 + unsigned char *buffer; + unsigned char *dest; + cairo_surface_t *image; + cairo_pattern_t *pattern; + int x, y; + ImageStream *imgStr; + Guchar *pix; + double *ctm; + cairo_matrix_t matrix; + int invert_bit; + int row_stride; + + row_stride = (width + 3) & ~3; + buffer = (unsigned char *) malloc (height * row_stride); + if (buffer == NULL) { + error(-1, "Unable to allocate memory for image."); + return; + } + + /* TODO: Do we want to cache these? */ + imgStr = new ImageStream(str, width, 1, 1); + imgStr->reset(); + + invert_bit = invert ? 1 : 0; + + for (y = 0; y < height; y++) { + pix = imgStr->getLine(); + dest = buffer + y * row_stride; + for (x = 0; x < width; x++) { + + if (pix[x] ^ invert_bit) + *dest++ = 0; + else + *dest++ = 255; + } + } + + image = cairo_image_surface_create_for_data (buffer, CAIRO_FORMAT_A8, + width, height, row_stride); + if (image == NULL) + return; + pattern = cairo_pattern_create_for_surface (image); + if (pattern == NULL) + return; + + ctm = state->getCTM(); + LOG (printf ("drawImageMask %dx%d, matrix: %f, %f, %f, %f, %f, %f\n", + width, height, ctm[0], ctm[1], ctm[2], ctm[3], ctm[4], ctm[5])); + matrix.xx = ctm[0] / width; + matrix.xy = -ctm[2] / height; + matrix.yx = ctm[1] / width; + matrix.yy = -ctm[3] / height; + matrix.x0 = ctm[2] + ctm[4]; + matrix.y0 = ctm[3] + ctm[5]; + cairo_matrix_invert (&matrix); + cairo_pattern_set_matrix (pattern, &matrix); + + cairo_pattern_set_filter (pattern, CAIRO_FILTER_BEST); + /* FIXME: Doesn't the image mask support any colorspace? */ + cairo_set_source_rgb (cairo, fill_color.r, fill_color.g, fill_color.b); + cairo_mask (cairo, pattern); + + cairo_pattern_destroy (pattern); + cairo_surface_destroy (image); + free (buffer); + imgStr->close (); + delete imgStr; +#endif +} + +//TODO: lots more work here. +void ArthurOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, + int width, int height, + GfxImageColorMap *colorMap, + bool interpolate, int *maskColors, bool inlineImg) +{ + unsigned int *data; + unsigned int *line; + int x, y; + ImageStream *imgStr; + unsigned char *pix; + int i; + const double *ctm; + QMatrix matrix; + QImage image; + int stride; + + /* TODO: Do we want to cache these? */ + imgStr = new ImageStream(str, width, + colorMap->getNumPixelComps(), + colorMap->getBits()); + imgStr->reset(); + + image = QImage(width, height, QImage::Format_ARGB32); + data = (unsigned int *)image.bits(); + stride = image.bytesPerLine()/4; + for (y = 0; y < height; y++) { + pix = imgStr->getLine(); + line = data+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++; } + } + } + + ctm = state->getCTM(); + matrix.setMatrix(ctm[0] / width, ctm[1] / width, -ctm[2] / height, -ctm[3] / height, ctm[2] + ctm[4], ctm[3] + ctm[5]); + + m_painter->setMatrix(matrix, true); + m_painter->drawImage( QPoint(0,0), image ); + delete imgStr; + +} diff --git a/qt4/src/ArthurOutputDev.h b/qt4/src/ArthurOutputDev.h new file mode 100644 index 00000000..9d5e8679 --- /dev/null +++ b/qt4/src/ArthurOutputDev.h @@ -0,0 +1,169 @@ +//======================================================================== +// +// ArthurOutputDev.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 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 +// +// To see a 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 ARTHUROUTPUTDEV_H +#define ARTHUROUTPUTDEV_H + +#ifdef USE_GCC_PRAGMAS +#pragma interface +#endif + +#include "OutputDev.h" +#include "GfxState.h" + +#include + +class GfxState; +class GfxPath; +class Gfx8BitFont; +struct GfxRGB; + +class SplashFont; +class SplashFontEngine; +struct SplashGlyphBitmap; + +//------------------------------------------------------------------------ +// ArthurOutputDev - Qt 4 QPainter renderer +//------------------------------------------------------------------------ + +class ArthurOutputDev: public OutputDev { +public: + /** + * Describes how fonts are distorted (aka hinted) to fit the pixel grid. + * More hinting means sharper edges and less adherence to the true letter shapes. + */ + enum FontHinting { + NoHinting = 0, ///< Font shapes are left unchanged + SlightHinting, ///< Font shapes are distorted vertically only + FullHinting ///< Font shapes are distorted horizontally and vertically + }; + + // Constructor. + ArthurOutputDev(QPainter *painter ); + + // Destructor. + virtual ~ArthurOutputDev(); + + void setFontHinting(FontHinting hinting) { m_fontHinting = hinting; } + + //----- 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() { return true; } + + // Does this device use drawChar() or drawString()? + virtual bool useDrawChar() { return true; } + + // Does this device use beginType3Char/endType3Char? Otherwise, + // text in Type 3 fonts will be drawn with drawChar/drawString. + virtual bool interpretType3Chars() { return true; } + + //----- initialization and control + + // Start a page. + virtual void startPage(int pageNum, GfxState *state, XRef *xref); + + // End a page. + virtual void endPage(); + + //----- save/restore graphics state + virtual void saveState(GfxState *state); + virtual void restoreState(GfxState *state); + + //----- update graphics state + virtual void updateAll(GfxState *state); + 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 updateFillColor(GfxState *state); + virtual void updateStrokeColor(GfxState *state); + virtual void updateFillOpacity(GfxState *state); + virtual void updateStrokeOpacity(GfxState *state); + + //----- update text state + virtual void updateFont(GfxState *state); + + //----- path painting + virtual void stroke(GfxState *state); + virtual void fill(GfxState *state); + virtual void eoFill(GfxState *state); + + //----- path clipping + virtual void clip(GfxState *state); + virtual void eoClip(GfxState *state); + + //----- text drawing + // virtual void drawString(GfxState *state, GooString *s); + virtual void drawChar(GfxState *state, double x, double y, + double dx, double dy, + double originX, double originY, + CharCode code, int nBytes, Unicode *u, int uLen); + virtual bool beginType3Char(GfxState *state, double x, double y, + double dx, double dy, + CharCode code, Unicode *u, int uLen); + virtual void endType3Char(GfxState *state); + virtual void endTextObject(GfxState *state); + + //----- image drawing + virtual void drawImageMask(GfxState *state, Object *ref, Stream *str, + int width, int height, bool invert, + bool interpolate, bool inlineImg); + virtual void drawImage(GfxState *state, Object *ref, Stream *str, + int width, int height, GfxImageColorMap *colorMap, + bool interpolate, int *maskColors, bool inlineImg); + + //----- 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); + + //----- special access + + // Called to indicate that a new PDF document has been loaded. + void startDoc(XRef *xrefA); + + bool isReverseVideo() { return false; } + +private: + QPainter *m_painter; + FontHinting m_fontHinting; + QFont m_currentFont; + QPen m_currentPen; + QBrush m_currentBrush; + bool m_needFontUpdate; // set when the font needs to be updated + SplashFontEngine *m_fontEngine; + SplashFont *m_font; // current font + XRef *xref; // xref table for current document +}; + +#endif diff --git a/qt4/src/CMakeLists.txt b/qt4/src/CMakeLists.txt new file mode 100644 index 00000000..f6547726 --- /dev/null +++ b/qt4/src/CMakeLists.txt @@ -0,0 +1,54 @@ +add_definitions(${QT4_DEFINITIONS}) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR} + ${QT4_INCLUDE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} +) + +set(poppler_qt4_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-qiodeviceoutstream.cc + poppler-sound.cc + poppler-textbox.cc + poppler-page-transition.cc + poppler-media.cc + ArthurOutputDev.cc +) +qt4_automoc(${poppler_qt4_SRCS}) +add_library(poppler-qt4 SHARED ${poppler_qt4_SRCS}) +set_target_properties(poppler-qt4 PROPERTIES VERSION 4.11.0 SOVERSION 4) +if(MINGW) + get_target_property(POPPLER_QT4_SOVERSION poppler-qt4 SOVERSION) + set_target_properties(poppler-qt4 PROPERTIES SUFFIX "-${POPPLER_QT4_SOVERSION}${CMAKE_SHARED_LIBRARY_SUFFIX}") +endif() +target_link_libraries(poppler-qt4 poppler ${QT4_QTCORE_LIBRARY} ${QT4_QTGUI_LIBRARY} ${QT4_QTXML_LIBRARY}) +if(MSVC) +target_link_libraries(poppler-qt4 poppler ${poppler_LIBS}) +endif() +install(TARGETS poppler-qt4 RUNTIME DESTINATION bin LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + +install(FILES + poppler-qt4.h + poppler-link.h + poppler-annotation.h + poppler-form.h + poppler-optcontent.h + poppler-export.h + poppler-page-transition.h + poppler-media.h + DESTINATION include/poppler/qt4) + diff --git a/qt4/src/Doxyfile b/qt4/src/Doxyfile new file mode 100644 index 00000000..e68690ac --- /dev/null +++ b/qt4/src/Doxyfile @@ -0,0 +1,1637 @@ +# 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 Qt4 " + +# 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 = 0.61.1 + +# 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-qt4.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-qt4.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.qt4 + +# 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_QT4_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 diff --git a/qt4/src/Mainpage.dox b/qt4/src/Mainpage.dox new file mode 100644 index 00000000..30398c3d --- /dev/null +++ b/qt4/src/Mainpage.dox @@ -0,0 +1,85 @@ +/** +@mainpage The Poppler Qt4 interface library + +The %Poppler Qt4 interface library, libpoppler-qt4, is a library that +allows Qt4 programmers to easily load and render PDF files. The +%Poppler Qt4 interface library uses poppler internally to do its job, +but the Qt4 programmer will never have to worry about poppler +internals. + + +@section help Current Status + +The %Poppler Qt4 interface library is quite stable and working. + +@section refimpl Example Programs + +Examples programs can be found in the qt4/test directory. The %Poppler +Qt4 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 Qt4 interface library in three easy steps + +Programmer who would like to use the %Poppler Qt4 interface library +simply need to add the following line to their C++ source files: + +@code +#include +@endcode + +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 + */ + diff --git a/qt4/src/poppler-annotation-helper.h b/qt4/src/poppler-annotation-helper.h new file mode 100644 index 00000000..3150569c --- /dev/null +++ b/qt4/src/poppler-annotation-helper.h @@ -0,0 +1,183 @@ +/* poppler-annotation-helper.h: qt interface to poppler + * Copyright (C) 2006, 2008, 2017, Albert Astals Cid + * Copyright (C) 2008, Pino Toscano + * Copyright (C) 2012, Fabio D'Urso + * 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 + +class QColor; + +class AnnotColor; + +namespace Poppler { + +class XPDFReader +{ + public: + // find named symbol and parse it + static inline void lookupName( Dict *, char *, QString & dest ); + static inline void lookupString( Dict *, char *, QString & dest ); + static inline void lookupBool( Dict *, char *, bool & dest ); + static inline void lookupInt( Dict *, char *, int & dest ); + static inline void lookupNum( Dict *, char *, double & dest ); + static inline int lookupNumArray( Dict *, char *, double * dest, int len ); + static inline void lookupColor( Dict *, char *, QColor & color ); + static inline void lookupIntRef( Dict *, char *, int & dest ); + static inline void lookupDate( Dict *, char *, QDateTime & dest ); + // 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( double * M, const QPointF &p, double &x, double &y ); +}; + +void XPDFReader::lookupName( Dict * dict, char * type, QString & dest ) +{ + Object nameObj = dict->lookup( type ); + if ( nameObj.isNull() ) + return; + if ( nameObj.isName() ) + dest = nameObj.getName(); + else + qDebug() << type << " is not Name." << endl; +} + +void XPDFReader::lookupString( Dict * dict, char * type, QString & dest ) +{ + Object stringObj = dict->lookup( type ); + if ( stringObj.isNull() ) + return; + if ( stringObj.isString() ) + dest = stringObj.getString()->c_str(); + else + qDebug() << type << " is not String." << endl; +} + +void XPDFReader::lookupBool( Dict * dict, char * type, bool & dest ) +{ + Object boolObj = dict->lookup( type ); + if ( boolObj.isNull() ) + return; + if ( boolObj.isBool() ) + dest = boolObj.getBool() == true; + else + qDebug() << type << " is not Bool." << endl; +} + +void XPDFReader::lookupInt( Dict * dict, char * type, int & dest ) +{ + Object intObj = dict->lookup( type ); + if ( intObj.isNull() ) + return; + if ( intObj.isInt() ) + dest = intObj.getInt(); + else + qDebug() << type << " is not Int." << endl; +} + +void XPDFReader::lookupNum( Dict * dict, char * type, double & dest ) +{ + Object numObj = dict->lookup( type ); + if ( numObj.isNull() ) + return; + if ( numObj.isNum() ) + dest = numObj.getNum(); + else + qDebug() << type << " is not Num." << endl; +} + +int XPDFReader::lookupNumArray( Dict * dict, char * type, double * dest, int len ) +{ + Object arrObj = dict->lookup( type ); + if ( arrObj.isNull() ) + return 0; + if ( arrObj.isArray() ) + { + len = qMin( len, arrObj.arrayGetLength() ); + for ( int i = 0; i < len; i++ ) + { + Object numObj = arrObj.arrayGet( i ); + dest[i] = numObj.getNum(); + } + } + else + { + len = 0; + qDebug() << type << "is not Array." << endl; + } + return len; +} + +void XPDFReader::lookupColor( Dict * dict, char * type, QColor & dest ) +{ + double c[3]; + if ( XPDFReader::lookupNumArray( dict, type, c, 3 ) == 3 ) + dest = QColor( (int)(c[0]*255.0), (int)(c[1]*255.0), (int)(c[2]*255.0)); +} + +void XPDFReader::lookupIntRef( Dict * dict, char * type, int & dest ) +{ + Object refObj = dict->lookupNF( type ).copy(); + if ( refObj.isNull() ) + return; + if ( refObj.isRef() ) + dest = refObj.getRefNum(); + else + qDebug() << type << " is not Ref." << endl; +} + +void XPDFReader::lookupDate( Dict * dict, char * type, QDateTime & dest ) +{ + Object dateObj = dict->lookup( type ); + if ( dateObj.isNull() ) + return; + if ( dateObj.isString() ) + { + dest = convertDate( dateObj.getString()->c_str() ); + } + else + qDebug() << type << " is not Date" << endl; +} + +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( double * M, const QPointF &p, double &x, double &y ) +{ + const double det = M[0]*M[3] - M[1]*M[2]; + Q_ASSERT(det != 0); + + 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 &c ); + +} diff --git a/qt4/src/poppler-annotation-private.h b/qt4/src/poppler-annotation-private.h new file mode 100644 index 00000000..b530e2f2 --- /dev/null +++ b/qt4/src/poppler-annotation-private.h @@ -0,0 +1,112 @@ +/* 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, 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_ANNOTATION_PRIVATE_H_ +#define _POPPLER_ANNOTATION_PRIVATE_H_ + +#include +#include +#include + +#include "poppler-annotation.h" + +#include + +class Annot; +class AnnotPath; +class Link; +class Page; +class PDFRectangle; + +namespace Poppler +{ +class DocumentData; + +class AnnotationPrivate : public QSharedData +{ + public: + AnnotationPrivate(); + virtual ~AnnotationPrivate(); + + 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 fillNormalizationMTX(double MTX[6], int pageRotation) const; + 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 = 0); + + /* 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; +}; + +} + +#endif diff --git a/qt4/src/poppler-annotation.cc b/qt4/src/poppler-annotation.cc new file mode 100644 index 00000000..8a7f60b1 --- /dev/null +++ b/qt4/src/poppler-annotation.cc @@ -0,0 +1,5118 @@ +/* poppler-annotation.cc: qt interface to poppler + * Copyright (C) 2006, 2009, 2012-2015 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 + * 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 + +// local includes +#include "poppler-annotation.h" +#include "poppler-link.h" +#include "poppler-qt4.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 + +/* 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( "type" ) ) + return nullptr; + + // build annotation of given type + Annotation * annotation = nullptr; + int typeNumber = annElement.attribute( "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( "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 Annotation implementation +AnnotationPrivate::AnnotationPrivate() + : flags( 0 ), revisionScope ( Annotation::Root ), + revisionType ( Annotation::None ), pdfAnnot ( nullptr ), pdfPage ( nullptr ), + parentDoc ( nullptr ) +{ +} + +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 +void AnnotationPrivate::fillNormalizationMTX(double MTX[6], int pageRotation) const +{ + 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( 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( 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 AnnotationPrivate::boundaryToPdfRectangle(const QRectF &r, int flags) const +{ + Q_ASSERT ( pdfPage ); + + const int pageRotate = pdfPage->getRotate(); + + double MTX[6]; + fillNormalizationMTX( 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 = ( flags & 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); +} + +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 uint numAnnotations = annots->getNumAnnots(); + if ( numAnnotations == 0 ) + { + return QList(); + } + + 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 ( uint j = 0; j < numAnnotations; j++ ) + { + // get the j-th annotation + Annot * ann = annots->getAnnot( j ); + if ( !ann ) + { + error(errInternal, -1, "Annot {0:ud} is null", j); + continue; + } + + // Check parent annotation + AnnotMarkup * markupann = dynamic_cast< AnnotMarkup * >( ann ); + if (!markupann) + { + // Assume it's a root annotation, and skip if user didn't request it + if (parentID != 0) + 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< AnnotLink * >( 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< AnnotFileAttachment * >( ann ); + FileAttachmentAnnotation * f = new FileAttachmentAnnotation(); + annotation = f; + // -> fileIcon + f->setFileIconName( QString::fromLatin1( attachann->getName()->c_str() ) ); + // -> embeddedFile + FileSpec *filespec = new FileSpec( attachann->getFile() ); + f->setEmbeddedFile( new EmbeddedFile( *new EmbeddedFileData( filespec ) ) ); + break; + } + case Annot::typeSound: /* TODO: Move logic to getters */ + { + if (!wantSoundAnnotations) + continue; + AnnotSound * soundann = static_cast< AnnotSound * >( 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< AnnotMovie * >( 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< AnnotScreen * >( ann ); + if (!screenann->getAction()) + 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< AnnotRichMedia * >( 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< RichMediaAnnotation::Configuration* > 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< RichMediaAnnotation::Instance* > 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< RichMediaAnnotation::Asset* > 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() ) ); + + FileSpec *fileSpec = new FileSpec( annotAsset->getFileSpec() ); + asset->setEmbeddedFile( new EmbeddedFile( *new EmbeddedFileData( 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) + { + const Ref invalid_ref = { -1, -1 }; + return invalid_ref; + } + + return pdfAnnot->getRef(); +} + +Link* AnnotationPrivate::additionalAction( Annotation::AdditionalActionType type ) const +{ + if ( pdfAnnot->getType() != Annot::typeScreen && pdfAnnot->getType() != Annot::typeWidget ) + return nullptr; + + Annot::AdditionalActionsType actionType = Annot::actionCursorEntering; + switch ( type ) + { + case Annotation::CursorEnteringAction: actionType = Annot::actionCursorEntering; break; + case Annotation::CursorLeavingAction: actionType = Annot::actionCursorLeaving; break; + case Annotation::MousePressedAction: actionType = Annot::actionMousePressed; break; + case Annotation::MouseReleasedAction: actionType = Annot::actionMouseReleased; break; + case Annotation::FocusInAction: actionType = Annot::actionFocusIn; break; + case Annotation::FocusOutAction: actionType = Annot::actionFocusOut; break; + case Annotation::PageOpeningAction: actionType = Annot::actionPageOpening; break; + case Annotation::PageClosingAction: actionType = Annot::actionPageClosing; break; + case Annotation::PageVisibleAction: actionType = Annot::actionPageVisible; break; + case Annotation::PageInvisibleAction: actionType = Annot::actionPageInvisible; break; + } + + ::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, 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); + 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 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 ) +{ + window.width = window.height = 0; +} + +Annotation::~Annotation() +{ +} + +Annotation::Annotation( AnnotationPrivate &dd, const QDomNode &annNode ) + : d_ptr( &dd ) +{ + Q_D( Annotation ); + + window.width = window.height = 0; + + // get the [base] element of the annotation node + QDomElement e = AnnotationUtils::findChildElement( annNode, "base" ); + if ( e.isNull() ) + return; + + Style s; + Popup w; + + // parse -contents- attributes + if ( e.hasAttribute( "author" ) ) + setAuthor(e.attribute( "author" )); + if ( e.hasAttribute( "contents" ) ) + setContents(e.attribute( "contents" )); + if ( e.hasAttribute( "uniqueName" ) ) + setUniqueName(e.attribute( "uniqueName" )); + if ( e.hasAttribute( "modifyDate" ) ) + setModificationDate(QDateTime::fromString( e.attribute( "modifyDate" ) )); + if ( e.hasAttribute( "creationDate" ) ) + setCreationDate(QDateTime::fromString( e.attribute( "creationDate" ) )); + + // parse -other- attributes + if ( e.hasAttribute( "flags" ) ) + setFlags(e.attribute( "flags" ).toInt()); + if ( e.hasAttribute( "color" ) ) + s.setColor(QColor( e.attribute( "color" ) )); + if ( e.hasAttribute( "opacity" ) ) + s.setOpacity(e.attribute( "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() == "boundary" ) + { + QRectF brect; + brect.setLeft(ee.attribute( "l" ).toDouble()); + brect.setTop(ee.attribute( "t" ).toDouble()); + brect.setRight(ee.attribute( "r" ).toDouble()); + brect.setBottom(ee.attribute( "b" ).toDouble()); + setBoundary(brect); + } + // parse penStyle if not default + else if ( ee.tagName() == "penStyle" ) + { + s.setWidth(ee.attribute( "width" ).toDouble()); + s.setLineStyle((LineStyle)ee.attribute( "style" ).toInt()); + s.setXCorners(ee.attribute( "xcr" ).toDouble()); + s.setYCorners(ee.attribute( "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() != "dashsegm" ) + continue; + + dashArray.append(eee.attribute( "len" ).toDouble()); + } + + // If no segments were found use marks/spaces (old format) + if ( dashArray.size() == 0 ) + { + dashArray.append(ee.attribute( "marks" ).toDouble()); + dashArray.append(ee.attribute( "spaces" ).toDouble()); + } + + s.setDashArray(dashArray); + } + // parse effectStyle if not default + else if ( ee.tagName() == "penEffect" ) + { + s.setLineEffect((LineEffect)ee.attribute( "effect" ).toInt()); + s.setEffectIntensity(ee.attribute( "intensity" ).toDouble()); + } + // parse window if present + else if ( ee.tagName() == "window" ) + { + QRectF geom; + geom.setX(ee.attribute( "top" ).toDouble()); + geom.setY(ee.attribute( "left" ).toDouble()); + + if (ee.hasAttribute("widthDouble")) + geom.setWidth(ee.attribute( "widthDouble" ).toDouble()); + else + geom.setWidth(ee.attribute( "width" ).toDouble()); + + if (ee.hasAttribute("widthDouble")) + geom.setHeight(ee.attribute( "heightDouble" ).toDouble()); + else + geom.setHeight(ee.attribute( "height" ).toDouble()); + + w.setGeometry(geom); + + w.setFlags(ee.attribute( "flags" ).toInt()); + w.setTitle(ee.attribute( "title" )); + w.setSummary(ee.attribute( "summary" )); + // parse window subnodes + QDomNode winNode = ee.firstChild(); + for ( ; winNode.isElement(); winNode = winNode.nextSibling() ) + { + QDomElement winElement = winNode.toElement(); + if ( winElement.tagName() == "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() != "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( "revScope" ).toInt(); + RevType type = (RevType)revElement.attribute( "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( "base" ); + annNode.appendChild( e ); + + const Style s = style(); + const Popup w = popup(); + + // store -contents- attributes + if ( !author().isEmpty() ) + e.setAttribute( "author", author() ); + if ( !contents().isEmpty() ) + e.setAttribute( "contents", contents() ); + if ( !uniqueName().isEmpty() ) + e.setAttribute( "uniqueName", uniqueName() ); + if ( modificationDate().isValid() ) + e.setAttribute( "modifyDate", modificationDate().toString() ); + if ( creationDate().isValid() ) + e.setAttribute( "creationDate", creationDate().toString() ); + + // store -other- attributes + if ( flags() ) + e.setAttribute( "flags", flags() ); + if ( s.color().isValid() ) + e.setAttribute( "color", s.color().name() ); + if ( s.opacity() != 1.0 ) + e.setAttribute( "opacity", QString::number( s.opacity() ) ); + + // Sub-Node-1 - boundary + const QRectF brect = boundary(); + QDomElement bE = document.createElement( "boundary" ); + e.appendChild( bE ); + bE.setAttribute( "l", QString::number( (double)brect.left() ) ); + bE.setAttribute( "t", QString::number( (double)brect.top() ) ); + bE.setAttribute( "r", QString::number( (double)brect.right() ) ); + bE.setAttribute( "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( "penStyle" ); + e.appendChild( psE ); + psE.setAttribute( "width", QString::number( s.width() ) ); + psE.setAttribute( "style", (int)s.lineStyle() ); + psE.setAttribute( "xcr", QString::number( s.xCorners() ) ); + psE.setAttribute( "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( "marks", marks ); + psE.setAttribute( "spaces", spaces ); + + foreach (double segm, dashArray) + { + QDomElement pattE = document.createElement( "dashsegm" ); + pattE.setAttribute( "len", QString::number( segm ) ); + psE.appendChild(pattE); + } + } + + // Sub-Node-3 - penEffect + if ( s.lineEffect() != NoEffect || s.effectIntensity() != 1.0 ) + { + QDomElement peE = document.createElement( "penEffect" ); + e.appendChild( peE ); + peE.setAttribute( "effect", (int)s.lineEffect() ); + peE.setAttribute( "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( "window" ); + const QRectF geom = w.geometry(); + e.appendChild( wE ); + wE.setAttribute( "flags", w.flags() ); + wE.setAttribute( "top", QString::number( geom.x() ) ); + wE.setAttribute( "left", QString::number( geom.y() ) ); + wE.setAttribute( "width", (int)geom.width() ); + wE.setAttribute( "height", (int)geom.height() ); + wE.setAttribute( "widthDouble", QString::number( geom.width() ) ); + wE.setAttribute( "heightDouble", QString::number( geom.height() ) ); + wE.setAttribute( "title", w.title() ); + wE.setAttribute( "summary", w.summary() ); + // store window.text as a subnode, because we need escaped data + if ( !w.text().isEmpty() ) + { + QDomElement escapedText = document.createElement( "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( "revision" ); + annNode.appendChild( r ); + // set element attributes + r.setAttribute( "revScope", (int)rev->revisionScope() ); + r.setAttribute( "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) + { + GooString *s = QStringToUnicodeGooString(author); + markupann->setLabel(s); + delete s; + } +} + +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; + } + + GooString *s = QStringToUnicodeGooString(contents); + d->pdfAnnot->setContents(s); + delete s; +} + +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.toAscii(); + 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 0 // TODO: Conversion routine is broken + if (d->pdfAnnot) + { + time_t t = date.toTime_t(); + GooString *s = timeToDateString(&t); + d->pdfAnnot->setModified(s); + delete s; + } +#endif +} + +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; + } + +#if 0 // TODO: Conversion routine is broken + AnnotMarkup *markupann = dynamic_cast(d->pdfAnnot); + if (markupann) + { + time_t t = date.toTime_t(); + GooString *s = timeToDateString(&t); + markupann->setDate(s); + delete s; + } +#endif +} + +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; + } + + PDFRectangle rect = d->boundaryToPdfRectangle( boundary, flags() ); + 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 int dashArrLen = border->getDashLength(); + const double* dashArrData = border->getDash(); + QVector dashArrVect( dashArrLen ); + for (int i = 0; i < dashArrLen; ++i) + dashArrVect[i] = dashArrData[i]; + s.setDashArray(dashArrVect); + } + + 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->getInReplyToID() != 0) + { + 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->getInReplyToID() != 0) + { + 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() ); +} + +//END Annotation implementation + + +/** TextAnnotation [Annotation] */ +class TextAnnotationPrivate : public AnnotationPrivate +{ + public: + TextAnnotationPrivate(); + Annotation * makeAlias(); + Annot* createNativeAnnot(::Page *destPage, DocumentData *doc); + void setDefaultAppearanceToNative(); + std::unique_ptr getDefaultAppearanceFromNative() const; + + // data fields + TextAnnotation::TextType textType; + QString textIcon; + QFont textFont; + QColor textColor; + int inplaceAlign; // 0:left, 1:center, 2:right + QVector inplaceCallout; + TextAnnotation::InplaceIntent inplaceIntent; +}; + +TextAnnotationPrivate::TextAnnotationPrivate() + : AnnotationPrivate(), textType( TextAnnotation::Linked ), + textIcon( "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 { + DefaultAppearance da{ { objName, "Invalid_font" }, static_cast( textFont.pointSize() ), std::unique_ptr{ convertQColor( textColor ) } }; + pdfAnnot = new AnnotFreeText(destPage->getDoc(), &rect, da); + } + + // Set properties + flushBaseAnnotationProperties(); + q->setTextIcon(textIcon); + q->setInplaceAlign(inplaceAlign); + q->setCalloutPoints(inplaceCallout); + q->setInplaceIntent(inplaceIntent); + + delete q; + + inplaceCallout.clear(); // Free up memory + + return pdfAnnot; +} + +void TextAnnotationPrivate::setDefaultAppearanceToNative() +{ + if (pdfAnnot && pdfAnnot->getType() == Annot::typeFreeText) { + AnnotFreeText * ftextann = static_cast(pdfAnnot); + DefaultAppearance da{ { objName, "Invalid_font" }, static_cast( textFont.pointSize() ), std::unique_ptr{ 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 nullptr; + } +} + +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() != "text" ) + continue; + + // parse the attributes + if ( e.hasAttribute( "type" ) ) + setTextType((TextAnnotation::TextType)e.attribute( "type" ).toInt()); + if ( e.hasAttribute( "icon" ) ) + setTextIcon(e.attribute( "icon" )); + if ( e.hasAttribute( "font" ) ) + { + QFont font; + font.fromString( e.attribute( "font" ) ); + setTextFont(font); + if ( e.hasAttribute( "fontColor" ) ) + { + const QColor color = QColor(e.attribute( "fontColor" ) ); + setTextColor(color); + } + } + if ( e.hasAttribute( "align" ) ) + setInplaceAlign(e.attribute( "align" ).toInt()); + if ( e.hasAttribute( "intent" ) ) + setInplaceIntent((TextAnnotation::InplaceIntent)e.attribute( "intent" ).toInt()); + + // parse the subnodes + QDomNode eSubNode = e.firstChild(); + while ( eSubNode.isElement() ) + { + QDomElement ee = eSubNode.toElement(); + eSubNode = eSubNode.nextSibling(); + + if ( ee.tagName() == "escapedText" ) + { + setInplaceText(ee.firstChild().toCDATASection().data()); + } + else if ( ee.tagName() == "callout" ) + { + QVector points(3); + points[0] = QPointF(ee.attribute( "ax" ).toDouble(), + ee.attribute( "ay" ).toDouble()); + points[1] = QPointF(ee.attribute( "bx" ).toDouble(), + ee.attribute( "by" ).toDouble()); + points[2] = QPointF(ee.attribute( "cx" ).toDouble(), + ee.attribute( "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( "text" ); + node.appendChild( textElement ); + + // store the optional attributes + if ( textType() != Linked ) + textElement.setAttribute( "type", (int)textType() ); + if ( textIcon() != "Note" ) + textElement.setAttribute( "icon", textIcon() ); + if ( inplaceAlign() ) + textElement.setAttribute( "align", inplaceAlign() ); + if ( inplaceIntent() != Unknown ) + textElement.setAttribute( "intent", (int)inplaceIntent() ); + + textElement.setAttribute( "font", textFont().toString() ); + textElement.setAttribute( "fontColor", textColor().name() ); + + // Sub-Node-1 - escapedText + if ( !inplaceText().isEmpty() ) + { + QDomElement escapedText = document.createElement( "escapedText" ); + textElement.appendChild( escapedText ); + QDomCDATASection textCData = document.createCDATASection( inplaceText() ); + escapedText.appendChild( textCData ); + } + + // Sub-Node-2 - callout + if ( calloutPoint(0).x() != 0.0 ) + { + QDomElement calloutElement = document.createElement( "callout" ); + textElement.appendChild( calloutElement ); + calloutElement.setAttribute( "ax", QString::number( calloutPoint(0).x() ) ); + calloutElement.setAttribute( "ay", QString::number( calloutPoint(0).y() ) ); + calloutElement.setAttribute( "bx", QString::number( calloutPoint(1).x() ) ); + calloutElement.setAttribute( "by", QString::number( calloutPoint(1).y() ) ); + calloutElement.setAttribute( "cx", QString::number( calloutPoint(2).x() ) ); + calloutElement.setAttribute( "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 +} + +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->pdfAnnot) + return d->textFont; + + QFont font; + + if (d->pdfAnnot->getType() == Annot::typeFreeText) + { + if ( std::unique_ptr da{ d->getDefaultAppearanceFromNative() } ) + { + font.setPointSize( da->getFontPtSize() ); + } + } + + return font; +} + +void TextAnnotation::setTextFont( const QFont &font ) +{ + Q_D( TextAnnotation ); + d->textFont = font; + d->textColor = Qt::black; + + 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 ); + 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 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((AnnotFreeText::AnnotFreeTextQuadding)align); + } +} + +QString TextAnnotation::inplaceText() const +{ + return contents(); +} + +void TextAnnotation::setInplaceText( const QString &text ) +{ + setContents(text); +} + +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(0); + 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(); + Annot* createNativeAnnot(::Page *destPage, DocumentData *doc); + + // 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() != "line" ) + continue; + + // parse the attributes + if ( e.hasAttribute( "startStyle" ) ) + setLineStartStyle((LineAnnotation::TermStyle)e.attribute( "startStyle" ).toInt()); + if ( e.hasAttribute( "endStyle" ) ) + setLineEndStyle((LineAnnotation::TermStyle)e.attribute( "endStyle" ).toInt()); + if ( e.hasAttribute( "closed" ) ) + setLineClosed(e.attribute( "closed" ).toInt()); + if ( e.hasAttribute( "innerColor" ) ) + setLineInnerColor(QColor( e.attribute( "innerColor" ) )); + if ( e.hasAttribute( "leadFwd" ) ) + setLineLeadingForwardPoint(e.attribute( "leadFwd" ).toDouble()); + if ( e.hasAttribute( "leadBack" ) ) + setLineLeadingBackPoint(e.attribute( "leadBack" ).toDouble()); + if ( e.hasAttribute( "showCaption" ) ) + setLineShowCaption(e.attribute( "showCaption" ).toInt()); + if ( e.hasAttribute( "intent" ) ) + setLineIntent((LineAnnotation::LineIntent)e.attribute( "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() != "point" ) + continue; + + QPointF p(pe.attribute( "x", "0.0" ).toDouble(), pe.attribute( "y", "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( "line" ); + node.appendChild( lineElement ); + + // store the attributes + if ( lineStartStyle() != None ) + lineElement.setAttribute( "startStyle", (int)lineStartStyle() ); + if ( lineEndStyle() != None ) + lineElement.setAttribute( "endStyle", (int)lineEndStyle() ); + if ( isLineClosed() ) + lineElement.setAttribute( "closed", isLineClosed() ); + if ( lineInnerColor().isValid() ) + lineElement.setAttribute( "innerColor", lineInnerColor().name() ); + if ( lineLeadingForwardPoint() != 0.0 ) + lineElement.setAttribute( "leadFwd", QString::number( lineLeadingForwardPoint() ) ); + if ( lineLeadingBackPoint() != 0.0 ) + lineElement.setAttribute( "leadBack", QString::number( lineLeadingBackPoint() ) ); + if ( lineShowCaption() ) + lineElement.setAttribute( "showCaption", lineShowCaption() ); + if ( lineIntent() != Unknown ) + lineElement.setAttribute( "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( "point" ); + lineElement.appendChild( pElement ); + pElement.setAttribute( "x", QString::number( p.x() ) ); + pElement.setAttribute( "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 +} + +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(); + Annot* createNativeAnnot(::Page *destPage, DocumentData *doc); + + // 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() != "geom" ) + continue; + + // parse the attributes + if ( e.hasAttribute( "type" ) ) + setGeomType((GeomAnnotation::GeomType)e.attribute( "type" ).toInt()); + if ( e.hasAttribute( "color" ) ) + setGeomInnerColor(QColor( e.attribute( "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( "geom" ); + node.appendChild( geomElement ); + + // append the optional attributes + if ( geomType() != InscribedSquare ) + geomElement.setAttribute( "type", (int)geomType() ); + if ( geomInnerColor().isValid() ) + geomElement.setAttribute( "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(); + Annot* createNativeAnnot(::Page *destPage, DocumentData *doc); + + // data fields + HighlightAnnotation::HighlightType highlightType; + QList< HighlightAnnotation::Quad > highlightQuads; // not empty + + // helpers + static Annot::AnnotSubtype toAnnotSubType( HighlightAnnotation::HighlightType type ); + QList< HighlightAnnotation::Quad > fromQuadrilaterals(AnnotQuadrilaterals *quads) const; + AnnotQuadrilaterals * toQuadrilaterals(const QList< HighlightAnnotation::Quad > &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< HighlightAnnotation::Quad > HighlightAnnotationPrivate::fromQuadrilaterals(AnnotQuadrilaterals *hlquads) const +{ + QList< HighlightAnnotation::Quad > 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< HighlightAnnotation::Quad > &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() != "hl" ) + continue; + + // parse the attributes + if ( e.hasAttribute( "type" ) ) + setHighlightType((HighlightAnnotation::HighlightType)e.attribute( "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() != "quad" ) + continue; + + Quad q; + q.points[0].setX(qe.attribute( "ax", "0.0" ).toDouble()); + q.points[0].setY(qe.attribute( "ay", "0.0" ).toDouble()); + q.points[1].setX(qe.attribute( "bx", "0.0" ).toDouble()); + q.points[1].setY(qe.attribute( "by", "0.0" ).toDouble()); + q.points[2].setX(qe.attribute( "cx", "0.0" ).toDouble()); + q.points[2].setY(qe.attribute( "cy", "0.0" ).toDouble()); + q.points[3].setX(qe.attribute( "dx", "0.0" ).toDouble()); + q.points[3].setY(qe.attribute( "dy", "0.0" ).toDouble()); + q.capStart = qe.hasAttribute( "start" ); + q.capEnd = qe.hasAttribute( "end" ); + q.feather = qe.attribute( "feather", "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( "hl" ); + node.appendChild( hlElement ); + + // append the optional attributes + if ( highlightType() != Highlight ) + hlElement.setAttribute( "type", (int)highlightType() ); + + const QList quads = highlightQuads(); + if ( quads.count() < 1 ) + return; + // append highlight quads, all children describe quads + QList< HighlightAnnotation::Quad >::const_iterator it = quads.begin(), end = quads.end(); + for ( ; it != end; ++it ) + { + QDomElement quadElement = document.createElement( "quad" ); + hlElement.appendChild( quadElement ); + const Quad & q = *it; + quadElement.setAttribute( "ax", QString::number( q.points[0].x() ) ); + quadElement.setAttribute( "ay", QString::number( q.points[0].y() ) ); + quadElement.setAttribute( "bx", QString::number( q.points[1].x() ) ); + quadElement.setAttribute( "by", QString::number( q.points[1].y() ) ); + quadElement.setAttribute( "cx", QString::number( q.points[2].x() ) ); + quadElement.setAttribute( "cy", QString::number( q.points[2].y() ) ); + quadElement.setAttribute( "dx", QString::number( q.points[3].x() ) ); + quadElement.setAttribute( "dy", QString::number( q.points[3].y() ) ); + if ( q.capStart ) + quadElement.setAttribute( "start", 1 ); + if ( q.capEnd ) + quadElement.setAttribute( "end", 1 ); + quadElement.setAttribute( "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::Quad > 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< HighlightAnnotation::Quad > &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(); + Annot* createNativeAnnot(::Page *destPage, DocumentData *doc); + + // data fields + QString stampIconName; +}; + +StampAnnotationPrivate::StampAnnotationPrivate() + : AnnotationPrivate(), stampIconName( "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); + + delete q; + + stampIconName.clear(); // Free up memory + + return pdfAnnot; +} + +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() != "stamp" ) + continue; + + // parse the attributes + if ( e.hasAttribute( "icon" ) ) + setStampIconName(e.attribute( "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( "stamp" ); + node.appendChild( stampElement ); + + // append the optional attributes + if ( stampIconName() != "Draft" ) + stampElement.setAttribute( "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); +} + +/** InkAnnotation [Annotation] */ +class InkAnnotationPrivate : public AnnotationPrivate +{ + public: + InkAnnotationPrivate(); + Annotation * makeAlias(); + Annot* createNativeAnnot(::Page *destPage, DocumentData *doc); + + // data fields + QList< QLinkedList > inkPaths; + + // helper + AnnotPath **toAnnotPaths(const QList< QLinkedList > &inkPaths); +}; + +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< QLinkedList > &inkPaths) +{ + const int pathsNumber = inkPaths.size(); + AnnotPath **res = new AnnotPath*[pathsNumber]; + for (int i = 0; i < pathsNumber; ++i) + res[i] = toAnnotPath( inkPaths[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() != "ink" ) + continue; + + // parse the 'path' subnodes + QList< QLinkedList > paths; + QDomNode pathNode = e.firstChild(); + while ( pathNode.isElement() ) + { + QDomElement pathElement = pathNode.toElement(); + pathNode = pathNode.nextSibling(); + + if ( pathElement.tagName() != "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() != "point" ) + continue; + + QPointF p(pointElement.attribute( "x", "0.0" ).toDouble(), pointElement.attribute( "y", "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( "ink" ); + node.appendChild( inkElement ); + + // append the optional attributes + const QList< QLinkedList > paths = inkPaths(); + if ( paths.count() < 1 ) + return; + QList< QLinkedList >::const_iterator pIt = paths.begin(), pEnd = paths.end(); + for ( ; pIt != pEnd; ++pIt ) + { + QDomElement pathElement = document.createElement( "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( "point" ); + pathElement.appendChild( pointElement ); + pointElement.setAttribute( "x", QString::number( point.x() ) ); + pointElement.setAttribute( "y", QString::number( point.y() ) ); + } + } +} + +Annotation::SubType InkAnnotation::subType() const +{ + return AInk; +} + +QList< QLinkedList > 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< QLinkedList >(); + + double MTX[6]; + d->fillTransformationMTX(MTX); + + const int pathsNumber = inkann->getInkListLength(); + QList< QLinkedList > 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< QLinkedList > &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(); + Annotation * makeAlias(); + Annot* createNativeAnnot(::Page *destPage, DocumentData *doc); + + // 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() != "link" ) + continue; + + // parse the attributes + if ( e.hasAttribute( "hlmode" ) ) + setLinkHighlightMode((LinkAnnotation::HighlightMode)e.attribute( "hlmode" ).toInt()); + + // parse all 'quad' subnodes + QDomNode quadNode = e.firstChild(); + for ( ; quadNode.isElement(); quadNode = quadNode.nextSibling() ) + { + QDomElement qe = quadNode.toElement(); + if ( qe.tagName() == "quad" ) + { + setLinkRegionPoint(0, QPointF(qe.attribute( "ax", "0.0" ).toDouble(), + qe.attribute( "ay", "0.0" ).toDouble())); + setLinkRegionPoint(1, QPointF(qe.attribute( "bx", "0.0" ).toDouble(), + qe.attribute( "by", "0.0" ).toDouble())); + setLinkRegionPoint(2, QPointF(qe.attribute( "cx", "0.0" ).toDouble(), + qe.attribute( "cy", "0.0" ).toDouble())); + setLinkRegionPoint(3, QPointF(qe.attribute( "dx", "0.0" ).toDouble(), + qe.attribute( "dy", "0.0" ).toDouble())); + } + else if ( qe.tagName() == "link" ) + { + QString type = qe.attribute( "type" ); + if ( type == "GoTo" ) + { + Poppler::LinkGoto * go = new Poppler::LinkGoto( QRect(), qe.attribute( "filename" ), LinkDestination( qe.attribute( "destination" ) ) ); + setLinkDestination(go); + } + else if ( type == "Exec" ) + { + Poppler::LinkExecute * exec = new Poppler::LinkExecute( QRect(), qe.attribute( "filename" ), qe.attribute( "parameters" ) ); + setLinkDestination(exec); + } + else if ( type == "Browse" ) + { + Poppler::LinkBrowse * browse = new Poppler::LinkBrowse( QRect(), qe.attribute( "url" ) ); + setLinkDestination(browse); + } + else if ( type == "Action" ) + { + Poppler::LinkAction::ActionType act; + QString actString = qe.attribute( "action" ); + bool found = true; + if ( actString == "PageFirst" ) + act = Poppler::LinkAction::PageFirst; + else if ( actString == "PagePrev" ) + act = Poppler::LinkAction::PagePrev; + else if ( actString == "PageNext" ) + act = Poppler::LinkAction::PageNext; + else if ( actString == "PageLast" ) + act = Poppler::LinkAction::PageLast; + else if ( actString == "HistoryBack" ) + act = Poppler::LinkAction::HistoryBack; + else if ( actString == "HistoryForward" ) + act = Poppler::LinkAction::HistoryForward; + else if ( actString == "Quit" ) + act = Poppler::LinkAction::Quit; + else if ( actString == "Presentation" ) + act = Poppler::LinkAction::Presentation; + else if ( actString == "EndPresentation" ) + act = Poppler::LinkAction::EndPresentation; + else if ( actString == "Find" ) + act = Poppler::LinkAction::Find; + else if ( actString == "GoToPage" ) + act = Poppler::LinkAction::GoToPage; + else if ( actString == "Close" ) + act = Poppler::LinkAction::Close; + else if ( actString == "Print" ) + act = Poppler::LinkAction::Print; + else + found = false; + if (found) + { + Poppler::LinkAction * action = new Poppler::LinkAction( QRect(), act ); + setLinkDestination(action); + } + } +#if 0 + else if ( type == "Movie" ) + { + Poppler::LinkMovie * movie = new Poppler::LinkMovie( QRect() ); + setLinkDestination(movie); + } +#endif + } + } + + // 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( "link" ); + node.appendChild( linkElement ); + + // append the optional attributes + if ( linkHighlightMode() != Invert ) + linkElement.setAttribute( "hlmode", (int)linkHighlightMode() ); + + // saving region + QDomElement quadElement = document.createElement( "quad" ); + linkElement.appendChild( quadElement ); + quadElement.setAttribute( "ax", QString::number( linkRegionPoint(0).x() ) ); + quadElement.setAttribute( "ay", QString::number( linkRegionPoint(0).y() ) ); + quadElement.setAttribute( "bx", QString::number( linkRegionPoint(1).x() ) ); + quadElement.setAttribute( "by", QString::number( linkRegionPoint(1).y() ) ); + quadElement.setAttribute( "cx", QString::number( linkRegionPoint(2).x() ) ); + quadElement.setAttribute( "cy", QString::number( linkRegionPoint(2).y() ) ); + quadElement.setAttribute( "dx", QString::number( linkRegionPoint(3).x() ) ); + quadElement.setAttribute( "dy", QString::number( linkRegionPoint(3).y() ) ); + + // saving link + QDomElement hyperlinkElement = document.createElement( "link" ); + linkElement.appendChild( hyperlinkElement ); + if ( linkDestination() ) + { + switch( linkDestination()->linkType() ) + { + case Poppler::Link::Goto: + { + Poppler::LinkGoto * go = static_cast< Poppler::LinkGoto * >( linkDestination() ); + hyperlinkElement.setAttribute( "type", "GoTo" ); + hyperlinkElement.setAttribute( "filename", go->fileName() ); + hyperlinkElement.setAttribute( "destionation", go->destination().toString() ); // TODO Remove for poppler 0.28 + hyperlinkElement.setAttribute( "destination", go->destination().toString() ); + break; + } + case Poppler::Link::Execute: + { + Poppler::LinkExecute * exec = static_cast< Poppler::LinkExecute * >( linkDestination() ); + hyperlinkElement.setAttribute( "type", "Exec" ); + hyperlinkElement.setAttribute( "filename", exec->fileName() ); + hyperlinkElement.setAttribute( "parameters", exec->parameters() ); + break; + } + case Poppler::Link::Browse: + { + Poppler::LinkBrowse * browse = static_cast< Poppler::LinkBrowse * >( linkDestination() ); + hyperlinkElement.setAttribute( "type", "Browse" ); + hyperlinkElement.setAttribute( "url", browse->url() ); + break; + } + case Poppler::Link::Action: + { + Poppler::LinkAction * action = static_cast< Poppler::LinkAction * >( linkDestination() ); + hyperlinkElement.setAttribute( "type", "Action" ); + switch ( action->actionType() ) + { + case Poppler::LinkAction::PageFirst: + hyperlinkElement.setAttribute( "action", "PageFirst" ); + break; + case Poppler::LinkAction::PagePrev: + hyperlinkElement.setAttribute( "action", "PagePrev" ); + break; + case Poppler::LinkAction::PageNext: + hyperlinkElement.setAttribute( "action", "PageNext" ); + break; + case Poppler::LinkAction::PageLast: + hyperlinkElement.setAttribute( "action", "PageLast" ); + break; + case Poppler::LinkAction::HistoryBack: + hyperlinkElement.setAttribute( "action", "HistoryBack" ); + break; + case Poppler::LinkAction::HistoryForward: + hyperlinkElement.setAttribute( "action", "HistoryForward" ); + break; + case Poppler::LinkAction::Quit: + hyperlinkElement.setAttribute( "action", "Quit" ); + break; + case Poppler::LinkAction::Presentation: + hyperlinkElement.setAttribute( "action", "Presentation" ); + break; + case Poppler::LinkAction::EndPresentation: + hyperlinkElement.setAttribute( "action", "EndPresentation" ); + break; + case Poppler::LinkAction::Find: + hyperlinkElement.setAttribute( "action", "Find" ); + break; + case Poppler::LinkAction::GoToPage: + hyperlinkElement.setAttribute( "action", "GoToPage" ); + break; + case Poppler::LinkAction::Close: + hyperlinkElement.setAttribute( "action", "Close" ); + break; + case Poppler::LinkAction::Print: + hyperlinkElement.setAttribute( "action", "Print" ); + break; + } + break; + } + case Poppler::Link::Movie: + { + hyperlinkElement.setAttribute( "type", "Movie" ); + break; + } + case Poppler::Link::Rendition: + { + hyperlinkElement.setAttribute( "type", "Rendition" ); + break; + } + case Poppler::Link::Sound: + { + // FIXME: implement me + 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 ) +{ + if ( id < 0 || id >= 4 ) + return; + + Q_D( LinkAnnotation ); + d->linkRegion[id] = point; +} + +/** CaretAnnotation [Annotation] */ +class CaretAnnotationPrivate : public AnnotationPrivate +{ + public: + CaretAnnotationPrivate(); + Annotation * makeAlias(); + Annot* createNativeAnnot(::Page *destPage, DocumentData *doc); + + // data fields + CaretAnnotation::CaretSymbol symbol; +}; + +static QString caretSymbolToString( CaretAnnotation::CaretSymbol symbol ) +{ + switch ( symbol ) + { + case CaretAnnotation::None: + return QString::fromLatin1( "None" ); + case CaretAnnotation::P: + return QString::fromLatin1( "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() != "caret" ) + continue; + + // parse the attributes + if ( e.hasAttribute( "symbol" ) ) + setCaretSymbol(caretSymbolFromString( e.attribute( "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( "caret" ); + node.appendChild( caretElement ); + + // append the optional attributes + if ( caretSymbol() != CaretAnnotation::None ) + caretElement.setAttribute( "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(); + Annotation * makeAlias(); + Annot* createNativeAnnot(::Page *destPage, DocumentData *doc); + + // data fields + QString icon; + EmbeddedFile *embfile; +}; + +FileAttachmentAnnotationPrivate::FileAttachmentAnnotationPrivate() + : AnnotationPrivate(), icon( "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() != "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( "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(); + Annotation * makeAlias(); + Annot* createNativeAnnot(::Page *destPage, DocumentData *doc); + + // data fields + QString icon; + SoundObject *sound; +}; + +SoundAnnotationPrivate::SoundAnnotationPrivate() + : AnnotationPrivate(), icon( "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() != "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( "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(); + Annotation * makeAlias(); + Annot* createNativeAnnot(::Page *destPage, DocumentData *doc); + + // 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() != "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( "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(); + Annotation * makeAlias(); + Annot* createNativeAnnot(::Page *destPage, DocumentData *doc); + + // 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( "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(); + Annot* createNativeAnnot(::Page *destPage, DocumentData *doc); +}; + +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( "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; + } + + 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(); + } + + RichMediaAnnotation::Configuration::Type type; + QString name; + QList< RichMediaAnnotation::Instance* > 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< RichMediaAnnotation::Instance* > &instances ) +{ + qDeleteAll( d->instances ); + d->instances.clear(); + + d->instances = instances; +} + +QList< RichMediaAnnotation::Instance* > RichMediaAnnotation::Configuration::instances() const +{ + return d->instances; +} + + +class RichMediaAnnotation::Asset::Private +{ + public: + Private() + : embeddedFile( nullptr ) + { + } + + ~Private() + { + delete embeddedFile; + } + + 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(); + } + + QList< RichMediaAnnotation::Configuration* > configurations; + QList< RichMediaAnnotation::Asset* > assets; +}; + +RichMediaAnnotation::Content::Content() + : d( new Private ) +{ +} + +RichMediaAnnotation::Content::~Content() +{ +} + +void RichMediaAnnotation::Content::setConfigurations( const QList< RichMediaAnnotation::Configuration* > &configurations ) +{ + qDeleteAll( d->configurations ); + d->configurations.clear(); + + d->configurations = configurations; +} + +QList< RichMediaAnnotation::Configuration* > RichMediaAnnotation::Content::configurations() const +{ + return d->configurations; +} + +void RichMediaAnnotation::Content::setAssets( const QList< RichMediaAnnotation::Asset* > &assets ) +{ + qDeleteAll( d->assets ); + d->assets.clear(); + + d->assets = assets; +} + +QList< RichMediaAnnotation::Asset* > 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() + { + delete settings; + delete content; + } + + Annotation * makeAlias() + { + return new RichMediaAnnotation( *this ); + } + + Annot* createNativeAnnot( ::Page *destPage, DocumentData *doc ) + { + Q_UNUSED( destPage ); + Q_UNUSED( doc ); + + return nullptr; + } + + RichMediaAnnotation::Settings *settings; + RichMediaAnnotation::Content *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() != "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( "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 + +} diff --git a/qt4/src/poppler-annotation.h b/qt4/src/poppler-annotation.h new file mode 100644 index 00000000..ac77c421 --- /dev/null +++ b/qt4/src/poppler-annotation.h @@ -0,0 +1,1379 @@ +/* poppler-annotation.h: qt interface to poppler + * Copyright (C) 2006-2008, 2012 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 + * 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 "poppler-export.h" + +namespace Poppler { + +class Annotation; +class AnnotationPrivate; +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_QT4_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. + */ + 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. + */ + 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. + */ + static QDomElement findChildElement( const QDomNode & parentNode, + const QString & name ); +}; + + +/** + * \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 Qt4 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_QT4_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_QT4_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_QT4_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 contens/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 ); + + /// \cond PRIVATE + // This field is deprecated and not used any more. Use popup + Q_DECL_DEPRECATED struct { int width, height; } window; // Always set to zero + /// \endcond + + /// \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; + + /** + * 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 + Annotation( AnnotationPrivate &dd ); + Annotation( AnnotationPrivate &dd, const QDomNode &description ); + void storeBaseAnnotationProperties( QDomNode & parentNode, 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_QT4_EXPORT TextAnnotation : public Annotation +{ + friend class AnnotationUtils; + friend class AnnotationPrivate; + + public: + // local enums + enum TextType { Linked, InPlace }; + enum InplaceIntent { Unknown, Callout, TypeWriter }; + + TextAnnotation( TextType type ); + ~TextAnnotation(); + 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 ); + /// \since 0.69 + QColor textColor() const; + /// \since 0.69 + void setTextColor( const QColor &color ); + + int inplaceAlign() const; + void setInplaceAlign( int align ); + + /** + Synonym for contents() + + \deprecated Use contents() instead + */ + QString inplaceText() const; + /** + Synonym for setContents() + + \deprecated Use setContents() instead + */ + void setInplaceText( const QString &text ); + + 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: + TextAnnotation( const QDomNode &node ); + TextAnnotation( TextAnnotationPrivate &dd ); + virtual void store( QDomNode &parentNode, QDomDocument &document ) const; + 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_QT4_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 + LineAnnotation( LineType type ); + ~LineAnnotation(); + 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: + LineAnnotation( const QDomNode &node ); + LineAnnotation( LineAnnotationPrivate &dd ); + virtual void store( QDomNode &parentNode, QDomDocument &document ) const; + 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_QT4_EXPORT GeomAnnotation : public Annotation +{ + friend class AnnotationUtils; + friend class AnnotationPrivate; + + public: + GeomAnnotation(); + virtual ~GeomAnnotation(); + virtual SubType subType() const; + + // common enums + enum GeomType { InscribedSquare, InscribedCircle }; + + GeomType geomType() const; + void setGeomType( GeomType style ); + + QColor geomInnerColor() const; + void setGeomInnerColor( const QColor &color ); + + private: + GeomAnnotation( const QDomNode &node ); + GeomAnnotation( GeomAnnotationPrivate &dd ); + virtual void store( QDomNode &parentNode, QDomDocument &document ) const; + Q_DECLARE_PRIVATE( GeomAnnotation ) + Q_DISABLE_COPY( GeomAnnotation ) +}; + +/** + * \short Text highlight annotation. + * + * The higlight annotation represents some areas of text being "highlighted". + */ +class POPPLER_QT4_EXPORT HighlightAnnotation : public Annotation +{ + friend class AnnotationUtils; + friend class AnnotationPrivate; + + public: + HighlightAnnotation(); + virtual ~HighlightAnnotation(); + virtual SubType subType() const; + + /** + 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< Quad > highlightQuads() const; + + /** + Set the areas to highlight. + */ + void setHighlightQuads( const QList< Quad > &quads ); + + private: + HighlightAnnotation( const QDomNode &node ); + HighlightAnnotation( HighlightAnnotationPrivate &dd ); + virtual void store( QDomNode &parentNode, QDomDocument &document ) const; + Q_DECLARE_PRIVATE( HighlightAnnotation ) + Q_DISABLE_COPY( HighlightAnnotation ) +}; + +/** + * \short Stamp annotation. + * + * A simple annotation drawing a stamp on a page. + */ +class POPPLER_QT4_EXPORT StampAnnotation : public Annotation +{ + friend class AnnotationUtils; + friend class AnnotationPrivate; + + public: + StampAnnotation(); + virtual ~StampAnnotation(); + virtual SubType subType() const; + + /** + 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 ); + + private: + StampAnnotation( const QDomNode &node ); + StampAnnotation( StampAnnotationPrivate &dd ); + virtual void store( QDomNode &parentNode, QDomDocument &document ) const; + Q_DECLARE_PRIVATE( StampAnnotation ) + Q_DISABLE_COPY( StampAnnotation ) +}; + +/** + * \short Ink Annotation. + * + * Annotation representing an ink path on a page. + */ +class POPPLER_QT4_EXPORT InkAnnotation : public Annotation +{ + friend class AnnotationUtils; + friend class AnnotationPrivate; + + public: + InkAnnotation(); + virtual ~InkAnnotation(); + virtual SubType subType() const; + + QList< QLinkedList > inkPaths() const; + void setInkPaths( const QList< QLinkedList > &paths ); + + private: + InkAnnotation( const QDomNode &node ); + virtual void store( QDomNode &parentNode, QDomDocument &document ) const; + InkAnnotation(InkAnnotationPrivate &dd); + Q_DECLARE_PRIVATE( InkAnnotation ) + Q_DISABLE_COPY( InkAnnotation ) +}; + +class POPPLER_QT4_EXPORT LinkAnnotation : public Annotation +{ + friend class AnnotationUtils; + friend class AnnotationPrivate; + + public: + virtual ~LinkAnnotation(); + virtual SubType subType() const; + + // 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; + void setLinkRegionPoint( int id, const QPointF &point ); + + private: + LinkAnnotation(); + LinkAnnotation( const QDomNode &node ); + LinkAnnotation( LinkAnnotationPrivate &dd ); + virtual void store( QDomNode &parentNode, QDomDocument &document ) const; + 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_QT4_EXPORT CaretAnnotation : public Annotation +{ + friend class AnnotationUtils; + friend class AnnotationPrivate; + + public: + CaretAnnotation(); + virtual ~CaretAnnotation(); + virtual SubType subType() const; + + /** + * The symbols for the caret annotation. + */ + enum CaretSymbol { None, P }; + + CaretSymbol caretSymbol() const; + void setCaretSymbol( CaretSymbol symbol ); + + private: + CaretAnnotation( const QDomNode &node ); + CaretAnnotation( CaretAnnotationPrivate &dd ); + virtual void store( QDomNode &parentNode, QDomDocument &document ) const; + 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_QT4_EXPORT FileAttachmentAnnotation : public Annotation +{ + friend class AnnotationPrivate; + + public: + virtual ~FileAttachmentAnnotation(); + virtual SubType subType() const; + + /** + * 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(); + FileAttachmentAnnotation( const QDomNode &node ); + FileAttachmentAnnotation( FileAttachmentAnnotationPrivate &dd ); + virtual void store( QDomNode &parentNode, QDomDocument &document ) const; + 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_QT4_EXPORT SoundAnnotation : public Annotation +{ + friend class AnnotationPrivate; + + public: + virtual ~SoundAnnotation(); + virtual SubType subType() const; + + /** + * 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 *ef ); + + private: + SoundAnnotation(); + SoundAnnotation( const QDomNode &node ); + SoundAnnotation( SoundAnnotationPrivate &dd ); + virtual void store( QDomNode &parentNode, QDomDocument &document ) const; + 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_QT4_EXPORT MovieAnnotation : public Annotation +{ + friend class AnnotationPrivate; + + public: + virtual ~MovieAnnotation(); + virtual SubType subType() const; + + /** + * 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(); + MovieAnnotation( const QDomNode &node ); + MovieAnnotation( MovieAnnotationPrivate &dd ); + virtual void store( QDomNode &parentNode, QDomDocument &document ) const; + 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_QT4_EXPORT ScreenAnnotation : public Annotation +{ + friend class AnnotationPrivate; + + public: + virtual ~ScreenAnnotation(); + + virtual SubType subType() const; + + /** + * 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(); + ScreenAnnotation( ScreenAnnotationPrivate &dd ); + virtual void store( QDomNode &parentNode, QDomDocument &document ) const; // 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_QT4_EXPORT WidgetAnnotation : public Annotation +{ + friend class AnnotationPrivate; + + public: + virtual ~WidgetAnnotation(); + + virtual SubType subType() const; + + /** + * 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(); + WidgetAnnotation( WidgetAnnotationPrivate &dd ); + virtual void store( QDomNode &parentNode, QDomDocument &document ) const; // 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_QT4_EXPORT RichMediaAnnotation : public Annotation +{ + friend class AnnotationPrivate; + + public: + virtual ~RichMediaAnnotation(); + + virtual SubType subType() const; + + /** + * 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_QT4_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_QT4_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_QT4_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< RichMediaAnnotation::Instance* > instances() const; + + private: + void setType( Type type ); + void setName( const QString &name ); + void setInstances( const QList< RichMediaAnnotation::Instance* > &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_QT4_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_QT4_EXPORT Content + { + friend class AnnotationPrivate; + + public: + Content(); + ~Content(); + + /** + * Returns the list of configuration objects of the content object. + */ + QList< RichMediaAnnotation::Configuration* > configurations() const; + + /** + * Returns the list of asset objects of the content object. + */ + QList< RichMediaAnnotation::Asset* > assets() const; + + private: + void setConfigurations( const QList< RichMediaAnnotation::Configuration* > &configurations ); + void setAssets( const QList< RichMediaAnnotation::Asset* > &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_QT4_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_QT4_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_QT4_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(); + RichMediaAnnotation( const QDomNode &node ); + RichMediaAnnotation( RichMediaAnnotationPrivate &dd ); + virtual void store( QDomNode &parentNode, QDomDocument &document ) const; + Q_DECLARE_PRIVATE( RichMediaAnnotation ) + Q_DISABLE_COPY( RichMediaAnnotation ) +}; + +} + +#endif diff --git a/qt4/src/poppler-base-converter.cc b/qt4/src/poppler-base-converter.cc new file mode 100644 index 00000000..11ff17ca --- /dev/null +++ b/qt4/src/poppler-base-converter.cc @@ -0,0 +1,105 @@ +/* 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-qt4.h" + +#include "poppler-converter-private.h" + +#include + +namespace Poppler { + +BaseConverterPrivate::BaseConverterPrivate() + : document(0), iodev(0), 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 = 0; + } + else + { + return 0; + } + } + } + return iodev; +} + +void BaseConverterPrivate::closeDevice() +{ + if (ownIodev) + { + iodev->close(); + delete iodev; + iodev = 0; + } +} + + +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; +} + +} diff --git a/qt4/src/poppler-converter-private.h b/qt4/src/poppler-converter-private.h new file mode 100644 index 00000000..dc3e9437 --- /dev/null +++ b/qt4/src/poppler-converter-private.h @@ -0,0 +1,49 @@ +/* poppler-converter-private.h: Qt4 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. + */ + +#ifndef POPPLER_QT4_CONVERTER_PRIVATE_H +#define POPPLER_QT4_CONVERTER_PRIVATE_H + +#include + +class QIODevice; + +namespace Poppler { + +class DocumentData; + +class BaseConverterPrivate +{ + public: + BaseConverterPrivate(); + virtual ~BaseConverterPrivate(); + + QIODevice* openDevice(); + void closeDevice(); + + DocumentData *document; + QString outputFileName; + QIODevice *iodev; + bool ownIodev : 1; + BaseConverter::Error lastError; +}; + +} + +#endif diff --git a/qt4/src/poppler-document.cc b/qt4/src/poppler-document.cc new file mode 100644 index 00000000..d6e2fbf7 --- /dev/null +++ b/qt4/src/poppler-document.cc @@ -0,0 +1,850 @@ +/* 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, 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 Adam Reichold + * Copyright (C) 2015 William Bader + * Copyright (C) 2016 Jakub Alba + * Copyright (C) 2017 Adrian Johnson + * + * This program is free software; you can redistribute 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-qt4.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "poppler-private.h" +#include "poppler-page-private.h" + +#if defined(USE_CMS) +#if defined(USE_LCMS1) +#include +#else +#include +#endif +#endif + +namespace Poppler { + + int DocumentData::count = 0; + + Document *Document::load(const QString &filePath, const QByteArray &ownerPassword, + const QByteArray &userPassword) + { + DocumentData *doc = new DocumentData(filePath, + new GooString(ownerPassword.data()), + new 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, + new GooString(ownerPassword.data()), + new 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 NULL; + } + + 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 == NULL) { + delete page; + return NULL; + } + + 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, + new GooString(ownerPassword.data()), + new GooString(userPassword.data())); + } + else + { + doc2 = new DocumentData(m_doc->m_filePath, + new GooString(ownerPassword.data()), + new 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; + } + + bool Document::scanForFonts( int numPages, QList *fontList ) const + { + if ( !m_doc->m_fontInfoIterator ) + return false; + if ( !m_doc->m_fontInfoIterator->hasNext() ) + return false; + while ( m_doc->m_fontInfoIterator->hasNext() && numPages ) + { + (*fontList) += m_doc->m_fontInfoIterator->next(); + --numPages; + } + return true; + } + + 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(); + } + + QScopedPointer goo(m_doc->doc->getDocInfoStringEntry(type.toLatin1().constData())); + return UnicodeParsedString(goo.data()); + } + + 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(); + } + + QScopedPointer goo(m_doc->doc->getDocInfoTitle()); + return UnicodeParsedString(goo.data()); + } + + 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(); + } + + QScopedPointer goo(m_doc->doc->getDocInfoAuthor()); + return UnicodeParsedString(goo.data()); + } + + 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(); + } + + QScopedPointer goo(m_doc->doc->getDocInfoSubject()); + return UnicodeParsedString(goo.data()); + } + + 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(); + } + + QScopedPointer goo(m_doc->doc->getDocInfoKeywords()); + return UnicodeParsedString(goo.data()); + } + + 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(); + } + + QScopedPointer goo(m_doc->doc->getDocInfoCreator()); + return UnicodeParsedString(goo.data()); + } + + 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(); + } + + QScopedPointer goo(m_doc->doc->getDocInfoProducer()); + return UnicodeParsedString(goo.data()); + } + + 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::fromAscii(infoDict->getKey(i)) ); + } + + return keys; + } + + QDateTime Document::date( const QString & type ) const + { + if (m_doc->locked) { + return QDateTime(); + } + + QScopedPointer goo(m_doc->doc->getDocInfoStringEntry(type.toLatin1().constData())); + QString str = UnicodeParsedString(goo.data()); + return Poppler::convertDate(str.toLatin1().data()); + } + + 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(); + } + + QScopedPointer goo(m_doc->doc->getDocInfoCreatDate()); + QString str = UnicodeParsedString(goo.data()); + return Poppler::convertDate(str.toLatin1().data()); + } + + 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(); + } + + QScopedPointer goo(m_doc->doc->getDocInfoModDate()); + QString str = UnicodeParsedString(goo.data()); + return Poppler::convertDate(str.toLatin1().data()); + } + + 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(); + } + + double Document::pdfVersion() const + { + return m_doc->doc->getPDFMajorVersion () + m_doc->doc->getPDFMinorVersion() / 10.0; + } + + void Document::getPdfVersion(int *major, int *minor) const + { + if (major) + *major = m_doc->doc->getPDFMajorVersion(); + if (minor) + *minor = m_doc->doc->getPDFMinorVersion(); + } + + Page *Document::page(const QString &label) const + { + GooString label_g(label.toAscii().data()); + int index; + + if (!m_doc->doc->getCatalog()->labelToIndex (&label_g, &index)) + return NULL; + + 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 NULL; + + const std::vector<::OutlineItem*> * items = outline->getItems(); + if ( !items || items->size() < 1 ) + return NULL; + + QDomDocument *toc = new QDomDocument(); + if ( items->size() > 0 ) + m_doc->addTocChildren( toc, toc, items ); + + return toc; + } + + LinkDestination *Document::linkDestination( const QString &name ) + { + GooString * namedDest = QStringToGooString( name ); + LinkDestinationData ldd(NULL, 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) + GfxColorSpace::setDisplayProfile((cmsHPROFILE)outputProfileA); +#else + Q_UNUSED(outputProfileA); +#endif + } + + void Document::setColorDisplayProfileName(const QString &name) + { +#if defined(USE_CMS) + GooString *profileName = QStringToGooString( name ); + GfxColorSpace::setDisplayProfileName(profileName); + delete profileName; +#else + Q_UNUSED(name); +#endif + } + + void* Document::colorRgbProfile() const + { +#if defined(USE_CMS) + return (void*)GfxColorSpace::getRGBProfile(); +#else + return NULL; +#endif + } + + void* Document::colorDisplayProfile() const + { +#if defined(USE_CMS) + return (void*)GfxColorSpace::getDisplayProfile(); +#else + return NULL; +#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 arthur 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; +#if defined(HAVE_SPLASH) + ret << Document::SplashBackend; +#endif + ret << Document::ArthurBackend; + 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()) + { + GooString *s = catalog->readMetadata(); + if (s) result = UnicodeParsedString(s); + delete s; + } + 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(), 0); + } + 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 : 0, updateId ? &gooUpdateId : 0)) + 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 + } + + QDateTime convertDate( char *dateString ) + { + int year, mon, day, hour, min, sec, tzHours, tzMins; + char tz; + + if ( parseDateString( dateString, &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() { +#ifdef SPLASH_CMYK + return true; +#else + return false; +#endif + } + +} diff --git a/qt4/src/poppler-embeddedfile-private.h b/qt4/src/poppler-embeddedfile-private.h new file mode 100644 index 00000000..83549dad --- /dev/null +++ b/qt4/src/poppler-embeddedfile-private.h @@ -0,0 +1,42 @@ +/* poppler-embeddedfile-private.h: Qt4 interface to poppler + * Copyright (C) 2005, 2008, 2009, 2012, 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: + EmbeddedFileData(FileSpec *fs); + ~EmbeddedFileData(); + + EmbFile *embFile() const; + + FileSpec *filespec; +}; + +} + +#endif diff --git a/qt4/src/poppler-embeddedfile.cc b/qt4/src/poppler-embeddedfile.cc new file mode 100644 index 00000000..f70573ac --- /dev/null +++ b/qt4/src/poppler-embeddedfile.cc @@ -0,0 +1,135 @@ +/* poppler-document.cc: qt interface to poppler + * Copyright (C) 2005, 2008, 2009, 2012, 2013, 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. + */ + +#include "poppler-qt4.h" + +#include +#include + +#include "Object.h" +#include "Stream.h" +#include "Catalog.h" +#include "FileSpec.h" + +#include "poppler-private.h" +#include "poppler-embeddedfile-private.h" + +namespace Poppler +{ + +EmbeddedFileData::EmbeddedFileData(FileSpec *fs) + : filespec(fs) +{ +} + +EmbeddedFileData::~EmbeddedFileData() +{ + delete filespec; +} + +EmbFile *EmbeddedFileData::embFile() const +{ + return filespec->isOk() ? filespec->getEmbeddedFile() : NULL; +} + + +EmbeddedFile::EmbeddedFile(EmbFile *embfile) + : m_embeddedFile(0) +{ + assert(!"You must not use this private constructor!"); +} + +EmbeddedFile::EmbeddedFile(EmbeddedFileData &dd) + : m_embeddedFile(&dd) +{ +} + +EmbeddedFile::~EmbeddedFile() +{ + delete m_embeddedFile; +} + +QString EmbeddedFile::name() const +{ + GooString *goo = m_embeddedFile->filespec->getFileName(); + return goo ? UnicodeParsedString(goo) : QString(); +} + +QString EmbeddedFile::description() 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 +{ + GooString *goo = m_embeddedFile->embFile() ? m_embeddedFile->embFile()->modDate() : NULL; + return goo ? convertDate(goo->c_str()) : QDateTime(); +} + +QDateTime EmbeddedFile::createDate() const +{ + GooString *goo = m_embeddedFile->embFile() ? m_embeddedFile->embFile()->createDate() : NULL; + return goo ? convertDate(goo->c_str()) : QDateTime(); +} + +QByteArray EmbeddedFile::checksum() const +{ + GooString *goo = m_embeddedFile->embFile() ? m_embeddedFile->embFile()->checksum() : NULL; + return goo ? QByteArray::fromRawData(goo->c_str(), goo->getLength()) : QByteArray(); +} + +QString EmbeddedFile::mimeType() const +{ + GooString *goo = m_embeddedFile->embFile() ? m_embeddedFile->embFile()->mimeType() : NULL; + return goo ? QString(goo->c_str()) : QString(); +} + +QByteArray EmbeddedFile::data() +{ + if (!isValid()) + return QByteArray(); + Stream *stream = m_embeddedFile->embFile() ? m_embeddedFile->embFile()->stream() : NULL; + if (!stream) + return QByteArray(); + + stream->reset(); + int dataLen = 0; + QByteArray fileArray; + int i; + while ( (i = stream->getChar()) != EOF) { + fileArray[dataLen] = (char)i; + ++dataLen; + } + fileArray.resize(dataLen); + return fileArray; +} + +bool EmbeddedFile::isValid() const +{ + return m_embeddedFile->filespec->isOk(); +} + +} diff --git a/qt4/src/poppler-export.h b/qt4/src/poppler-export.h new file mode 100644 index 00000000..2e2f6ff8 --- /dev/null +++ b/qt4/src/poppler-export.h @@ -0,0 +1,20 @@ +/* +* This file is used to set the poppler_qt4_EXPORT macros right. +* This is needed for setting the visibility on windows, it will have no effect on other platforms. +*/ +#if defined(_WIN32) +# define _POPPLER_QT4_LIB_EXPORT __declspec(dllexport) +# define _POPPLER_QT4_LIB_IMPORT __declspec(dllimport) +#elif defined(__GNUC__) +# define _POPPLER_QT4_LIB_EXPORT __attribute__((visibility("default"))) +# define _POPPLER_QT4_LIB_IMPORT +#else +# define _POPPLER_QT4_LIB_EXPORT +# define _POPPLER_QT4_LIB_IMPORT +#endif + +#ifdef poppler_qt4_EXPORTS +# define POPPLER_QT4_EXPORT _POPPLER_QT4_LIB_EXPORT +#else +# define POPPLER_QT4_EXPORT _POPPLER_QT4_LIB_IMPORT +#endif diff --git a/qt4/src/poppler-fontinfo.cc b/qt4/src/poppler-fontinfo.cc new file mode 100644 index 00000000..5bb9e3a8 --- /dev/null +++ b/qt4/src/poppler-fontinfo.cc @@ -0,0 +1,150 @@ +/* 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 + * + * This program is free software; you can redistribute 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-qt4.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::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; +} + +} diff --git a/qt4/src/poppler-form.cc b/qt4/src/poppler-form.cc new file mode 100644 index 00000000..57cde574 --- /dev/null +++ b/qt4/src/poppler-form.cc @@ -0,0 +1,416 @@ +/* poppler-form.h: qt4 interface to poppler + * Copyright (C) 2007-2008, 2011, Pino Toscano + * Copyright (C) 2008, 2011, 2012, 2015, 2017, Albert Astals Cid + * Copyright (C) 2011 Carlos Garcia Campos + * Copyright (C) 2012, 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-qt4.h" + +#include + +#include +#include +#include + +#include "poppler-form.h" +#include "poppler-page-private.h" +#include "poppler-private.h" +#include "poppler-annotation-helper.h" + +#include + +namespace { + +Qt::Alignment formTextAlignment(::FormWidget *fm) +{ + Qt::Alignment qtalign = Qt::AlignLeft; + switch (fm->getField()->getTextQuadding()) + { + case quaddingCentered: + qtalign = Qt::AlignHCenter; + break; + case quaddingRightJustified: + qtalign = Qt::AlignRight; + break; + case quaddingLeftJustified: + qtalign = Qt::AlignLeft; + } + return qtalign; +} + +} + +namespace Poppler { + +FormField::FormField(FormFieldData &dd) + : m_formData(&dd) +{ + 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() +{ + delete m_formData; + m_formData = 0; +} + +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 = QString::fromLatin1(goo->c_str()); + } + return name; +} + +QString FormField::fullyQualifiedName() const +{ + QString name; + if (const 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 = QString::fromLatin1(goo->c_str()); + } + return name; +} + +bool FormField::isReadOnly() const +{ + return m_formData->fm->isReadOnly(); +} + +bool FormField::isVisible() const +{ + return !(m_formData->fm->getWidgetAnnotation()->getFlags() & Annot::flagHidden); +} + +Link* FormField::activationAction() const +{ + Link* action = 0; + if (::LinkAction *act = m_formData->fm->getActivationAction()) + { + action = PageData::convertLinkActionToLink(act, m_formData->doc, QRectF()); + } + return action; +} + + +FormFieldButton::FormFieldButton(DocumentData *doc, ::Page *p, ::FormWidgetButton *w) + : FormField(*new FormFieldData(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; +} + +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(*new FormFieldData(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; +} + +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(); +} + + +FormFieldChoice::FormFieldChoice(DocumentData *doc, ::Page *p, ::FormWidgetChoice *w) + : FormField(*new FormFieldData(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; +} + +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(); +} + +} diff --git a/qt4/src/poppler-form.h b/qt4/src/poppler-form.h new file mode 100644 index 00000000..79ed3932 --- /dev/null +++ b/qt4/src/poppler-form.h @@ -0,0 +1,343 @@ +/* poppler-form.h: qt4 interface to poppler + * Copyright (C) 2007-2008, Pino Toscano + * Copyright (C) 2008, 2011, Albert Astals Cid + * Copyright (C) 2012, 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_QT4_FORM_H_ +#define _POPPLER_QT4_FORM_H_ + +#include +#include +#include "poppler-export.h" + +class Page; +class FormWidget; +class FormWidgetButton; +class FormWidgetText; +class FormWidgetChoice; + +namespace Poppler { + + class DocumentData; + class Link; + + class FormFieldData; + /** + The base class representing a form field. + + \since 0.6 + */ + class POPPLER_QT4_EXPORT FormField { + 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 of the field. + */ + 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; + + /** + Whether this form field is visible. + */ + bool isVisible() const; + + /** + The activation action of this form field. + + \note It may be null. + */ + Link* activationAction() const; + + protected: + /// \cond PRIVATE + FormField(FormFieldData &dd); + + FormFieldData *m_formData; + /// \endcond + + private: + Q_DISABLE_COPY(FormField) + }; + + /** + A form field that represents a "button". + + \since 0.8 + */ + class POPPLER_QT4_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 + virtual ~FormFieldButton(); + + virtual FormType type() const; + + /** + The particular type of the button field. + */ + ButtonType buttonType() const; + + /** + * The caption to be used for the button. + */ + QString caption() const; + + /** + 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_QT4_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 + virtual ~FormFieldText(); + + virtual FormType type() const; + + /** + 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 ); + + /** + 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; + + private: + Q_DISABLE_COPY(FormFieldText) + }; + + /** + A form field that represents a choice field. + + \since 0.6 + */ + class POPPLER_QT4_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 + virtual ~FormFieldChoice(); + + virtual FormType type() const; + + /** + The choice type of the choice field. + */ + ChoiceType choiceType() const; + + /** + The possible choices of the choice field. + */ + QStringList choices() 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) + }; + +} + +#endif diff --git a/qt4/src/poppler-link-extractor-private.h b/qt4/src/poppler-link-extractor-private.h new file mode 100644 index 00000000..32ddd038 --- /dev/null +++ b/qt4/src/poppler-link-extractor-private.h @@ -0,0 +1,57 @@ +/* poppler-link-extractor_p.h: qt interface to poppler + * Copyright (C) 2007, 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_LINK_EXTRACTOR_H_ +#define _POPPLER_LINK_EXTRACTOR_H_ + +#include +#include + +#include + +namespace Poppler +{ + +class Link; +class PageData; + +class LinkExtractorOutputDev : public OutputDev +{ + public: + LinkExtractorOutputDev(PageData *data); + virtual ~LinkExtractorOutputDev(); + + // inherited from OutputDev + virtual bool upsideDown() { return false; } + virtual bool useDrawChar() { return false; } + virtual bool interpretType3Chars() { return false; } + virtual void processLink(::AnnotLink *link); + + // our stuff + QList< Link* > links(); + + private: + PageData *m_data; + double m_pageCropWidth; + double m_pageCropHeight; + QList< Link* > m_links; +}; + +} + +#endif diff --git a/qt4/src/poppler-link-extractor.cc b/qt4/src/poppler-link-extractor.cc new file mode 100644 index 00000000..0b1563b6 --- /dev/null +++ b/qt4/src/poppler-link-extractor.cc @@ -0,0 +1,84 @@ +/* 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-qt4.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< Link* > LinkExtractorOutputDev::links() +{ + QList< Link* > ret = m_links; + m_links.clear(); + return ret; +} + +} diff --git a/qt4/src/poppler-link-private.h b/qt4/src/poppler-link-private.h new file mode 100644 index 00000000..7b03c1c3 --- /dev/null +++ b/qt4/src/poppler-link-private.h @@ -0,0 +1,57 @@ +/* poppler-link-private.h: qt interface to poppler + * Copyright (C) 2016, 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_PRIVATE_H_ +#define _POPPLER_LINK_PRIVATE_H_ + +class LinkOCGState; + +namespace Poppler { + +class LinkPrivate +{ +public: + LinkPrivate( const QRectF &area ) + : linkArea( area ) + { + } + + virtual ~LinkPrivate() + { + } + + QRectF linkArea; +}; + + + +class LinkOCGStatePrivate : public LinkPrivate +{ +public: + LinkOCGStatePrivate( const QRectF &area, ::LinkOCGState *plocg ) + : LinkPrivate( area ) + , popplerLinkOCGState( plocg ) + { + } + + ::LinkOCGState *popplerLinkOCGState; +}; + +} + +#endif diff --git a/qt4/src/poppler-link.cc b/qt4/src/poppler-link.cc new file mode 100644 index 00000000..1e37f5bd --- /dev/null +++ b/qt4/src/poppler-link.cc @@ -0,0 +1,705 @@ +/* poppler-link.cc: qt interface to poppler + * Copyright (C) 2006-2007, 2016, 2017, 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. + * 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; + } + +class LinkGotoPrivate : public LinkPrivate +{ + public: + LinkGotoPrivate( const QRectF &area, const LinkDestination &dest ); + + QString extFileName; + LinkDestination destination; +}; + + LinkGotoPrivate::LinkGotoPrivate( const QRectF &area, const LinkDestination &dest ) + : LinkPrivate( area ), destination( dest ) + { + } + +class LinkExecutePrivate : public LinkPrivate +{ + public: + LinkExecutePrivate( const QRectF &area ); + + QString fileName; + QString parameters; +}; + + LinkExecutePrivate::LinkExecutePrivate( const QRectF &area ) + : LinkPrivate( area ) + { + } + +class LinkBrowsePrivate : public LinkPrivate +{ + public: + LinkBrowsePrivate( const QRectF &area ); + + QString url; +}; + + LinkBrowsePrivate::LinkBrowsePrivate( const QRectF &area ) + : LinkPrivate( area ) + { + } + +class LinkActionPrivate : public LinkPrivate +{ + public: + LinkActionPrivate( const QRectF &area ); + + LinkAction::ActionType type; +}; + + LinkActionPrivate::LinkActionPrivate( const QRectF &area ) + : LinkPrivate( area ) + { + } + +class LinkSoundPrivate : public LinkPrivate +{ + public: + LinkSoundPrivate( const QRectF &area ); + ~LinkSoundPrivate(); + + double volume; + bool sync : 1; + bool repeat : 1; + bool mix : 1; + SoundObject *sound; +}; + + LinkSoundPrivate::LinkSoundPrivate( const QRectF &area ) + : LinkPrivate( area ), sound( 0 ) + { + } + + LinkSoundPrivate::~LinkSoundPrivate() + { + delete sound; + } + +class LinkRenditionPrivate : public LinkPrivate +{ + public: + LinkRenditionPrivate( const QRectF &area, ::MediaRendition *rendition, ::LinkRendition::RenditionOperation operation, const QString &script, const Ref &annotationReference ); + ~LinkRenditionPrivate(); + + 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 ) : 0 ) + , 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: + LinkJavaScriptPrivate( const QRectF &area ); + + QString js; +}; + + LinkJavaScriptPrivate::LinkJavaScriptPrivate( const QRectF &area ) + : LinkPrivate( area ) + { + } + +class LinkMoviePrivate : public LinkPrivate +{ + public: + LinkMoviePrivate( const QRectF &area, LinkMovie::Operation operation, const QString &title, const Ref &reference ); + + 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 ) + { + } + + 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 ); + } + // 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 + { + 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 ) + { + QStringList tokens = description.split( ';' ); + 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; + } + + // LinkGoto + LinkGoto::LinkGoto( const QRectF &linkArea, 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 & params ) + : 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 ) + : Link( *new LinkRenditionPrivate( linkArea, rendition, ::LinkRendition::NoRendition, QString(), Ref() ) ) + { + } + + 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.num != -1 && 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.num != -1 && 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; + } +} diff --git a/qt4/src/poppler-link.h b/qt4/src/poppler-link.h new file mode 100644 index 00000000..42a8c1fc --- /dev/null +++ b/qt4/src/poppler-link.h @@ -0,0 +1,641 @@ +/* poppler-link.h: qt interface to poppler + * Copyright (C) 2006, 2013, 2016, Albert Astals Cid + * Copyright (C) 2007-2008, 2010, Pino Toscano + * Copyright (C) 2010, 2012, Guillermo Amaral + * Copyright (C) 2012, Tobias Koenig + * 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 "poppler-export.h" + +struct Ref; +class MediaRendition; +class MovieAnnotation; +class ScreenAnnotation; + +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 MediaRendition; +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_QT4_EXPORT LinkDestination +{ + public: + /** + * The possible kind of "viewport destination". + */ + enum Kind + { + /** + * The new viewport is specified in terms of: + * - possibile new left coordinate (see isChangeLeft() ) + * - possibile new top coordinate (see isChangeTop() ) + * - possibile new zoom level (see isChangeZoom() ) + */ + destXYZ = 1, + destFit = 2, + destFitH = 3, + destFitV = 4, + destFitR = 5, + destFitB = 6, + destFitBH = 7, + destFitBV = 8 + }; + + /// \cond PRIVATE + LinkDestination(const LinkDestinationData &data); + 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< LinkDestinationPrivate > 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_QT4_EXPORT Link +{ + friend class OptContentModel; + + public: + /// \cond PRIVATE + 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 + }; + + /** + * 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; + + protected: + /// \cond PRIVATE + 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_QT4_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, QString extFileName, const LinkDestination & destination ); + /** + * Destructor. + */ + ~LinkGoto(); + + /** + * 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_QT4_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 & params ); + /** + * Destructor. + */ + ~LinkExecute(); + LinkType linkType() const; + + 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_QT4_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(); + LinkType linkType() const; + + 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_QT4_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 + }; + + /** + * 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(); + LinkType linkType() const; + + private: + Q_DECLARE_PRIVATE( LinkAction ) + Q_DISABLE_COPY( LinkAction ) +}; + +/** + * Sound: a sound to be played. + * + * \since 0.6 + */ +class POPPLER_QT4_EXPORT LinkSound : public Link +{ + public: + // create a Link_Sound + LinkSound( const QRectF &linkArea, double volume, bool sync, bool repeat, bool mix, SoundObject *sound ); + /** + * Destructor. + */ + virtual ~LinkSound(); + + LinkType linkType() const; + + /** + * 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_QT4_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 + * + * \deprecated Use the constructor that takes all parameter instead + */ + Q_DECL_DEPRECATED LinkRendition( const QRectF &linkArea, ::MediaRendition *rendition ); + + /** + * 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 + */ + LinkRendition( const QRectF &linkArea, ::MediaRendition *rendition, int operation, const QString &script, const Ref &annotationReference ); + + /** + * Destructor. + */ + virtual ~LinkRendition(); + + LinkType linkType() const; + + /** + * 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_QT4_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. + */ + virtual ~LinkJavaScript(); + + LinkType linkType() const; + + /** + * 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_QT4_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(); + LinkType linkType() const; + /** + * 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_QT4_EXPORT LinkOCGState : public Link +{ + public: + /** + * Create a new OCGState link. This is only used by Poppler::Page. + */ + LinkOCGState( LinkOCGStatePrivate *ocgp ); + /** + * Destructor. + */ + ~LinkOCGState(); + + LinkType linkType() const; + + private: + Q_DECLARE_PRIVATE( LinkOCGState ) + Q_DISABLE_COPY( LinkOCGState ) +}; + +} + +#endif diff --git a/qt4/src/poppler-media.cc b/qt4/src/poppler-media.cc new file mode 100644 index 00000000..f385f02e --- /dev/null +++ b/qt4/src/poppler-media.cc @@ -0,0 +1,168 @@ +/* poppler-media.cc: qt interface to poppler + * Copyright (C) 2012 Guillermo A. Amaral B. + * Copyright (C) 2013 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: + + MediaRenditionPrivate(::MediaRendition *rendition) + : rendition(rendition) + { + } + + ~MediaRenditionPrivate() + { + delete rendition; + } + + ::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 = 0; + + 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(); +} + +} + diff --git a/qt4/src/poppler-media.h b/qt4/src/poppler-media.h new file mode 100644 index 00000000..34e5c361 --- /dev/null +++ b/qt4/src/poppler-media.h @@ -0,0 +1,100 @@ +/* poppler-media.h: qt interface to poppler + * Copyright (C) 2012 Guillermo A. Amaral B. + * Copyright (C) 2012, 2013 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_QT4_EXPORT MediaRendition { + public: + /** + Constructs a MediaRendition. Takes ownership of the passed rendition + */ + 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__ */ diff --git a/qt4/src/poppler-movie.cc b/qt4/src/poppler-movie.cc new file mode 100644 index 00000000..a64847c0 --- /dev/null +++ b/qt4/src/poppler-movie.cc @@ -0,0 +1,110 @@ +/* poppler-sound.cc: qt interface to poppler + * Copyright (C) 2008, 2010, Pino Toscano + * Copyright (C) 2008, 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-qt4.h" + +#include "Object.h" +#include "Annot.h" +#include "Movie.h" + +#include + +namespace Poppler +{ + +class MovieData +{ +public: + MovieData() + : m_movieObj( 0 ) + { + } + + ~MovieData() + { + delete m_movieObj; + } + + Movie *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 + + 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 +{ + 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; +} + +} diff --git a/qt4/src/poppler-optcontent-private.h b/qt4/src/poppler-optcontent-private.h new file mode 100644 index 00000000..b5e52999 --- /dev/null +++ b/qt4/src/poppler-optcontent-private.h @@ -0,0 +1,124 @@ +/* poppler-optcontent-private.h: qt interface to poppler + * + * Copyright (C) 2007, Brad Hards + * Copyright (C) 2008, Pino Toscano + * Copyright (C) 2016, 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 }; + + OptContentItem( OptionalContentGroup *group ); + 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; + + 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(); + + 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 diff --git a/qt4/src/poppler-optcontent.cc b/qt4/src/poppler-optcontent.cc new file mode 100644 index 00000000..0e7b5345 --- /dev/null +++ b/qt4/src/poppler-optcontent.cc @@ -0,0 +1,455 @@ +/* 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-2017, 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. + */ + +#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 (int i = 0; i < itemsInGroup.size(); ++i) { + OptContentItem *item = itemsInGroup.at(i); + item->appendRBGroup( this ); + } + } + + RadioButtonGroup::~RadioButtonGroup() + { + } + + QSet RadioButtonGroup::setItemOn( OptContentItem *itemToSetOn ) + { + QSet changedItems; + for (int i = 0; i < itemsInGroup.size(); ++i) { + OptContentItem *thisItem = itemsInGroup.at(i); + 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 = 0; + 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 = 0; + m_name = label; + m_group = 0; + m_state = OptContentItem::HeadingOnly; + m_stateBackup = m_state; + m_enabled = true; + } + + OptContentItem::OptContentItem() : + m_parent( 0 ), 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 || !obeyRadioGroups) { + return; + } + if ( state == OptContentItem::On ) { + m_group->setState( OptionalContentGroup::On ); + for (int i = 0; i < m_rbGroups.size(); ++i) { + RadioButtonGroup *rbgroup = m_rbGroups.at(i); + 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() == 0 ) { + // 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()), 0); + 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) + { + delete m_rootNode; + m_rootNode = node; + q->reset(); + } + + 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)); + } + qStableSort(indexes); + 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 ) + { + ::LinkOCGState *popplerLinkOCGState = static_cast(link->d_ptr)->popplerLinkOCGState; + + QSet changedItems; + + const std::vector<::LinkOCGState::StateList>& statesList = popplerLinkOCGState->getStateList(); + 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, popplerLinkOCGState->getPreserveRB(), changedItems); + } else if (stateList.st == ::LinkOCGState::Off) { + item->setState(OptContentItem::Off, popplerLinkOCGState->getPreserveRB(), changedItems); + } else { + OptContentItem::ItemState newState = item->state() == OptContentItem::On ? OptContentItem::Off : OptContentItem::On; + item->setState(newState, popplerLinkOCGState->getPreserveRB(), 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)); + } + qStableSort(indexes); + 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, 0); + } + + OptContentItem* OptContentModelPrivate::nodeFromIndex(const QModelIndex &index, bool canBeNull) const + { + if (index.isValid()) { + return static_cast(index.internalPointer()); + } else { + return canBeNull ? 0 : m_rootNode; + } + } +} + +#include "poppler-optcontent.moc" diff --git a/qt4/src/poppler-optcontent.h b/qt4/src/poppler-optcontent.h new file mode 100644 index 00000000..bf00a88d --- /dev/null +++ b/qt4/src/poppler-optcontent.h @@ -0,0 +1,84 @@ +/* poppler-optcontent.h: qt interface to poppler + * + * Copyright (C) 2007, Brad Hards + * Copyright (C) 2008, Pino Toscano + * Copyright (C) 2016, 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_QT4_EXPORT OptContentModel : public QAbstractItemModel + { + friend class Document; + + Q_OBJECT + + public: + virtual ~OptContentModel(); + + QModelIndex index(int row, int column, const QModelIndex &parent) const; + QModelIndex parent(const QModelIndex &child) const; + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent) const; + + QVariant data(const QModelIndex &index, int role) const; + virtual bool setData ( const QModelIndex & index, const QVariant & value, int role = Qt::EditRole ); + + Qt::ItemFlags flags ( const QModelIndex & index ) const; + + virtual QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const; + + /** + * Applies the Optional Content Changes specified by that link. + * \since 0.50 + */ + void applyLink( LinkOCGState *link ); + + private: + OptContentModel( OCGs *optContent, QObject *parent = 0); + + friend class OptContentModelPrivate; + OptContentModelPrivate *d; + }; +} + +#endif diff --git a/qt4/src/poppler-page-private.h b/qt4/src/poppler-page-private.h new file mode 100644 index 00000000..1cb63e9f --- /dev/null +++ b/qt4/src/poppler-page-private.h @@ -0,0 +1,57 @@ +/* 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 + * + * This program is free software; you can redistribute 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); + QList performMultipleTextSearch(TextPage* textPage, QVector &u, bool sCase, bool sWords); +}; + +} + +#endif diff --git a/qt4/src/poppler-page-transition-private.h b/qt4/src/poppler-page-transition-private.h new file mode 100644 index 00000000..63febb09 --- /dev/null +++ b/qt4/src/poppler-page-transition-private.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2005, 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. + */ + +class Object; + +namespace Poppler { + +class PageTransitionParams { + public: + Object *dictObj; +}; + +} diff --git a/qt4/src/poppler-page-transition.cc b/qt4/src/poppler-page-transition.cc new file mode 100644 index 00000000..4fa39edd --- /dev/null +++ b/qt4/src/poppler-page-transition.cc @@ -0,0 +1,101 @@ +/* PageTransition.cc + * Copyright (C) 2005, Net Integration Technologies, Inc. + * 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" +#include "poppler-page-transition.h" +#include "poppler-page-transition-private.h" + +namespace Poppler { + +class PageTransitionData +{ + public: + PageTransitionData(Object *trans) + { + pt = new ::PageTransition(trans); + } + + PageTransitionData(const PageTransitionData &ptd) + { + pt = new ::PageTransition(*ptd.pt); + } + + ~PageTransitionData() + { + delete pt; + } + + ::PageTransition *pt; +}; + +PageTransition::PageTransition(const PageTransitionParams ¶ms) +{ + data = new PageTransitionData(params.dictObj); +} + +PageTransition::PageTransition(const PageTransition &pt) +{ + data = new PageTransitionData(*pt.data); +} + +PageTransition::~PageTransition() +{ + delete data; +} + +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(); +} + +} diff --git a/qt4/src/poppler-page-transition.h b/qt4/src/poppler-page-transition.h new file mode 100644 index 00000000..e92adbd8 --- /dev/null +++ b/qt4/src/poppler-page-transition.h @@ -0,0 +1,158 @@ +/* PageTransition.h + * Copyright (C) 2005, Net Integration Technologies, Inc. + * Copyright (C) 2005, Brad Hards + * 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. + */ + +#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_QT4_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. + */ + PageTransition(const PageTransitionParams ¶ms); + + /** \brief copy constructor */ + PageTransition(const PageTransition &pt); + + /** + 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 diff --git a/qt4/src/poppler-page.cc b/qt4/src/poppler-page.cc new file mode 100644 index 00000000..ffe6e99c --- /dev/null +++ b/qt4/src/poppler-page.cc @@ -0,0 +1,810 @@ +/* poppler-page.cc: qt interface to poppler + * Copyright (C) 2005, Net Integration Technologies, Inc. + * Copyright (C) 2005, Brad Hards + * Copyright (C) 2005-2017, 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) 2017 Adrian Johnson + * + * This program is free software; you can redistribute 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 +#if defined(HAVE_SPLASH) +#include +#include +#endif + +#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 { + +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 NULL; + + Link * popplerLink = NULL; + 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::null, 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() ); + // ceate 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() : 0 ); + } + break; + + case actionNamed: + { + const char * name = ((LinkNamed *)a)->getName()->c_str(); + if ( !strcmp( name, "NextPage" ) ) + popplerLink = new LinkAction( linkArea, LinkAction::PageNext ); + else if ( !strcmp( name, "PrevPage" ) ) + popplerLink = new LinkAction( linkArea, LinkAction::PagePrev ); + else if ( !strcmp( name, "FirstPage" ) ) + popplerLink = new LinkAction( linkArea, LinkAction::PageFirst ); + else if ( !strcmp( name, "LastPage" ) ) + popplerLink = new LinkAction( linkArea, LinkAction::PageLast ); + else if ( !strcmp( name, "GoBack" ) ) + popplerLink = new LinkAction( linkArea, LinkAction::HistoryBack ); + else if ( !strcmp( name, "GoForward" ) ) + popplerLink = new LinkAction( linkArea, LinkAction::HistoryForward ); + else if ( !strcmp( name, "Quit" ) ) + popplerLink = new LinkAction( linkArea, LinkAction::Quit ); + else if ( !strcmp( name, "GoToPage" ) ) + popplerLink = new LinkAction( linkArea, LinkAction::GoToPage ); + else if ( !strcmp( name, "Find" ) ) + popplerLink = new LinkAction( linkArea, LinkAction::Find ); + else if ( !strcmp( name, "FullScreen" ) ) + popplerLink = new LinkAction( linkArea, LinkAction::Presentation ); + else if ( !strcmp( name, "Print" ) ) + popplerLink = new LinkAction( linkArea, LinkAction::Print ); + else if ( !strcmp( 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 + { + // TODO + } + } + 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; + reference.num = reference.gen = -1; + 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; + reference.num = reference.gen = -1; + if ( lrn->hasScreenAnnot() ) + reference = lrn->getScreenAnnot(); + + popplerLink = new LinkRendition( linkArea, lrn->getMedia() ? lrn->getMedia()->copy() : NULL, lrn->getOperation(), UnicodeParsedString( lrn->getScript() ), reference ); + } + break; + + case actionOCGState: + { + ::LinkOCGState *plocg = (::LinkOCGState *)a; + + LinkOCGStatePrivate *locgp = new LinkOCGStatePrivate( linkArea, plocg ); + popplerLink = new LinkOCGState( locgp ); + } + + case actionUnknown: + break; + } + + return popplerLink; +} + +inline TextPage *PageData::prepareTextSearch(const QString &text, Page::Rotation rotate, QVector *u) +{ + const QChar * str = text.unicode(); + const int len = text.length(); + u->resize(len); + for (int i = 0; i < len; ++i) (*u)[i] = str[i].unicode(); + + const int rotation = (int)rotate * 90; + + // fetch ourselves a textpage + TextOutputDev td(NULL, true, 0, false, false); + parentDoc->doc->displayPage( &td, index + 1, 72, 72, rotation, false, true, false, + NULL, NULL, NULL, NULL, 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) +{ + if (direction == Page::FromTop) + return textPage->findText( u.data(), u.size(), + true, true, false, false, sCase, false, sWords, &sLeft, &sTop, &sRight, &sBottom ); + else if ( direction == Page::NextResult ) + return textPage->findText( u.data(), u.size(), + false, true, true, false, sCase, false, sWords, &sLeft, &sTop, &sRight, &sBottom ); + else if ( direction == Page::PreviousResult ) + return textPage->findText( u.data(), u.size(), + false, true, true, false, sCase, true, sWords, &sLeft, &sTop, &sRight, &sBottom ); + + return false; +} + +inline QList PageData::performMultipleTextSearch(TextPage* textPage, QVector &u, bool sCase, bool sWords) +{ + QList results; + double sLeft = 0.0, sTop = 0.0, sRight = 0.0, sBottom = 0.0; + + while(textPage->findText( u.data(), u.size(), + false, true, true, false, sCase, false, sWords, &sLeft, &sTop, &sRight, &sBottom )) + { + QRectF result; + + result.setLeft(sLeft); + result.setTop(sTop); + result.setRight(sRight); + result.setBottom(sBottom); + + results.append(result); + } + + 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 = 0; +} + +Page::~Page() +{ + delete m_page->transition; + delete m_page; +} + +QImage Page::renderToImage(double xres, double yres, int x, int y, int w, int h, Rotation rotate) const +{ + int rotation = (int)rotate * 90; + QImage img; + switch(m_page->parentDoc->m_backend) + { + case Poppler::Document::SplashBackend: + { +#if defined(HAVE_SPLASH) + SplashColor bgColor; + bool overprintPreview = false; +#ifdef SPLASH_CMYK + overprintPreview = m_page->parentDoc->m_hints & Document::OverprintPreview ? true : false; + if (overprintPreview) + { + Guchar 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 +#endif + { + bgColor[0] = m_page->parentDoc->paperColor.blue(); + bgColor[1] = m_page->parentDoc->paperColor.green(); + bgColor[2] = m_page->parentDoc->paperColor.red(); + } + + SplashColorMode colorMode = splashModeXBGR8; +#ifdef SPLASH_CMYK + if (overprintPreview) colorMode = splashModeDeviceN8; +#endif + + 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; + + SplashOutputDev splash_output( + colorMode, 4, + false, + ignorePaperColor ? NULL : bgColor, + true, + thinLineMode, + overprintPreview); + + 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); + + splash_output.startDoc(m_page->parentDoc->doc); + + m_page->parentDoc->doc->displayPageSlice(&splash_output, m_page->index + 1, xres, yres, + rotation, false, true, false, x, y, w, h, + NULL, NULL, NULL, NULL, true); + + SplashBitmap *bitmap = splash_output.getBitmap(); + + const int bw = bitmap->getWidth(); + const int bh = bitmap->getHeight(); + const int brs = bitmap->getRowSize(); + + // If we use DeviceN8, convert to XBGR8. + // If requested, also transfer Splash's internal alpha channel. + const SplashBitmap::ConversionMode mode = ignorePaperColor + ? SplashBitmap::conversionAlphaPremultiplied + : SplashBitmap::conversionOpaque; + + const QImage::Format format = ignorePaperColor + ? QImage::Format_ARGB32_Premultiplied + : QImage::Format_RGB32; + + if (bitmap->convertToXBGR(mode)) { + SplashColorPtr data = bitmap->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]); + } + } + } + + // Construct a Qt image sharing the raw bitmap data. + img = QImage(data, bw, bh, brs, format).copy(); + } +#endif + break; + } + case Poppler::Document::ArthurBackend: + { + 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); + + QPainter painter(&tmpimg); + renderToPainter(&painter, xres, yres, x, y, w, h, rotate, DontSaveAndRestore); + painter.end(); + img = tmpimg; + break; + } + } + + 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::ArthurBackend: + { + const bool savePainter = !(flags & DontSaveAndRestore); + if (savePainter) + painter->save(); + if (m_page->parentDoc->m_hints & Document::Antialiasing) + painter->setRenderHint(QPainter::Antialiasing); + if (m_page->parentDoc->m_hints & Document::TextAntialiasing) + painter->setRenderHint(QPainter::TextAntialiasing); + painter->translate(x == -1 ? 0 : -x, y == -1 ? 0 : -y); + ArthurOutputDev arthur_output(painter); + arthur_output.startDoc(m_page->parentDoc->doc->getXRef()); + m_page->parentDoc->doc->displayPageSlice(&arthur_output, + m_page->index + 1, + xres, + yres, + (int)rotate * 90, + false, + true, + false, + x, + y, + w, + h); + if (savePainter) + painter->restore(); + return true; + } + } + return false; +} + +QImage Page::thumbnail() const +{ + unsigned char* data = 0; + 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; + const PDFRectangle *rect; + QString result; + + const bool rawOrder = textLayout == RawOrderLayout; + output_dev = new TextOutputDev(0, 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, + NULL, NULL, NULL, NULL, true); + if (r.isNull()) + { + 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); + + 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; + + 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); + + textPage->decRefCnt(); + + return found; +} + +bool Page::search(const QString &text, QRectF &rect, SearchDirection direction, SearchMode caseSensitive, Rotation rotate) const +{ + double sLeft, sTop, sRight, sBottom; + sLeft = rect.left(); + sTop = rect.top(); + sRight = rect.right(); + sBottom = rect.bottom(); + + bool found = search(text, sLeft, sTop, sRight, sBottom, direction, caseSensitive, rotate); + + rect.setLeft( sLeft ); + rect.setTop( sTop ); + rect.setRight( sRight ); + rect.setBottom( sBottom ); + + 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); + + 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; + + QVector u; + TextPage *textPage = m_page->prepareTextSearch(text, rotate, &u); + + const QList results = m_page->performMultipleTextSearch(textPage, u, sCase, sWords); + + textPage->decRefCnt(); + + return results; +} + +QList Page::textList(Rotation rotate) const +{ + TextOutputDev *output_dev; + + QList output_list; + + output_dev = new TextOutputDev(0, false, 0, false, false); + + int rotation = (int)rotate * 90; + + m_page->parentDoc->doc->displayPageSlice(output_dev, m_page->index + 1, 72, 72, + rotation, false, false, false, -1, -1, -1, -1, + NULL, NULL, NULL, NULL, true); + + TextWordList *word_list = output_dev->makeWordList(); + + if (!word_list) { + delete output_dev; + 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()); + } + + delete word_list; + delete output_dev; + + return output_list; +} + +PageTransition *Page::transition() const +{ + if (!m_page->transition) { + PageTransitionParams params; + Object o = m_page->page->getTrans(); + params.dictObj = &o; + if (o.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 0; + } + Dict *dict = o.getDict(); + const char *key = act == Page::Opening ? "O" : "C"; + Object o2 = dict->lookup((char*)key); + ::LinkAction *lact = ::LinkAction::parseAction(&o2, m_page->parentDoc->doc->getCatalog()->getBaseURI() ); + Link *popplerLink = NULL; + if (lact != NULL) + { + popplerLink = m_page->convertLinkActionToLink(lact, QRectF()); + delete lact; + } + return popplerLink; + } + return 0; +} + +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; + ::FormPageWidgets * form = p->getFormWidgets(); + int formcount = form->getNumWidgets(); + for (int i = 0; i < formcount; ++i) + { + ::FormWidget *fm = form->getWidget(i); + FormField * ff = NULL; + 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; + + default: ; + } + + if (ff) + fields.append(ff); + } + + delete form; + + 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); +} + + +} diff --git a/qt4/src/poppler-pdf-converter.cc b/qt4/src/poppler-pdf-converter.cc new file mode 100644 index 00000000..9699b75b --- /dev/null +++ b/qt4/src/poppler-pdf-converter.cc @@ -0,0 +1,115 @@ +/* poppler-pdf-converter.cc: qt4 interface to poppler + * Copyright (C) 2008, Pino Toscano + * Copyright (C) 2008, 2009, 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-qt4.h" + +#include "poppler-private.h" +#include "poppler-converter-private.h" +#include "poppler-qiodeviceoutstream-private.h" + +#include + +#include + +namespace Poppler { + +class PDFConverterPrivate : public BaseConverterPrivate +{ + public: + PDFConverterPrivate(); + + PDFConverter::PDFOptions opts; +}; + +PDFConverterPrivate::PDFConverterPrivate() + : BaseConverterPrivate(), opts(0) +{ +} + + +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); +} + +} diff --git a/qt4/src/poppler-private.cc b/qt4/src/poppler-private.cc new file mode 100644 index 00000000..1338a185 --- /dev/null +++ b/qt4/src/poppler-private.cc @@ -0,0 +1,296 @@ +/* poppler-private.cc: qt interface to poppler + * Copyright (C) 2005, Net Integration Technologies, Inc. + * Copyright (C) 2006, 2011, 2015, 2017 by Albert Astals Cid + * Copyright (C) 2008, 2010, 2011 by Pino Toscano + * Copyright (C) 2013 by Thomas Freitag + * Copyright (C) 2013 Adrian Johnson + * Copyright (C) 2016 Jakub Alba + * 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 +#include +#include + +#include +#include +#include +#include + +namespace Poppler { + +namespace Debug { + + static void qDebugDebugFunction(const QString &message, const QVariant & /*closure*/) + { + qDebug() << message; + } + + PopplerDebugFunc debugFunction = qDebugDebugFunction; + QVariant debugClosure; + +} + + static UnicodeMap *utf8Map = 0; + + void setDebugErrorFunction(PopplerDebugFunc function, const QVariant &closure) + { + Debug::debugFunction = function ? function : Debug::qDebugDebugFunction; + Debug::debugClosure = closure; + } + + static void qt4ErrorFunction(void * /*data*/, ErrorCategory /*category*/, Goffset pos, const char *msg) + { + QString emsg; + + if (pos >= 0) + { + emsg = QString::fromLatin1("Error (%1): ").arg(pos); + } + else + { + emsg = QString::fromLatin1("Error: "); + } + emsg += QString::fromAscii(msg); + (*Debug::debugFunction)(emsg, Debug::debugClosure); + } + + QString unicodeToQString(const Unicode* u, int len) { + if (!utf8Map) + { + GooString enc("UTF-8"); + utf8Map = globalParams->getUnicodeMap(&enc); + utf8Map->incRefCnt(); + } + + // ignore the last character if it is 0x0 + if ((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 UnicodeParsedString(const GooString *s1) { + if ( !s1 || s1->getLength() == 0 ) + return QString(); + + const char *cString; + int stringLength; + bool deleteCString; + if ( ( s1->getChar(0) & 0xff ) == 0xfe && ( s1->getLength() > 1 && ( s1->getChar(1) & 0xff ) == 0xff ) ) + { + cString = s1->c_str(); + stringLength = s1->getLength(); + deleteCString = false; + } + else + { + cString = pdfDocEncodingToUTF16(s1, &stringLength); + deleteCString = true; + } + + QString result; + // i = 2 to skip the unicode marker + for ( int i = 2; i < stringLength; i += 2 ) + { + const Unicode u = ( ( cString[i] & 0xff ) << 8 ) | ( cString[i+1] & 0xff ); + result += QChar( u ); + } + if (deleteCString) + delete[] cString; + return result; + } + + GooString *QStringToUnicodeGooString(const QString &s) { + 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 NULL; + } + + return QStringToUnicodeGooString(dt.toUTC().toString("yyyyMMddhhmmss+00'00'")); + } + + 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< const LinkGoTo * >( 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( "DestinationName", aux ); + delete[] charArray; + } + else if ( destination && destination->isOk() ) + { + LinkDestinationData ldd(destination, NULL, doc, false); + e->setAttribute( "Destination", LinkDestination(ldd).toString() ); + } + break; + } + case actionGoToR: + { + // page number is contained/referenced in a LinkGoToR + const LinkGoToR * g = static_cast< const LinkGoToR * >( 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( "DestinationName", aux ); + delete[] charArray; + } + else if ( destination && destination->isOk() ) + { + LinkDestinationData ldd(destination, NULL, doc, g->getFileName() != 0); + e->setAttribute( "Destination", LinkDestination(ldd).toString() ); + } + e->setAttribute( "ExternalFileName", g->getFileName()->c_str() ); + break; + } + case actionURI: + { + const LinkURI * u = static_cast< const LinkURI * >( a ); + e->setAttribute( "DestinationURI", u->getURI()->c_str() ); + } + default: ; + } + } + + DocumentData::~DocumentData() + { + qDeleteAll(m_embeddedFiles); + delete (OptContentModel *)m_optContentModel; + delete doc; + delete m_fontInfoIterator; + + count --; + if ( count == 0 ) + { + utf8Map = nullptr; + globalParams.reset(); + } + } + + void DocumentData::init() + { + m_fontInfoIterator = 0; + m_backend = Document::SplashBackend; + paperColor = Qt::white; + m_hints = 0; + m_optContentModel = 0; + + if ( count == 0 ) + { + utf8Map = nullptr; + globalParams = std::make_unique(); + setErrorCallback(qt4ErrorFunction, nullptr); + } + count ++; + } + + + 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; + const Unicode * uniChar = outlineItem->getTitle(); + int titleLength = outlineItem->getTitleLength(); + name = unicodeToQString(uniChar, titleLength); + if ( name.isEmpty() ) + continue; + + 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( "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 ); + } + } + +} diff --git a/qt4/src/poppler-private.h b/qt4/src/poppler-private.h new file mode 100644 index 00000000..a5ad3f3e --- /dev/null +++ b/qt4/src/poppler-private.h @@ -0,0 +1,242 @@ +/* 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 by Albert Astals Cid + * Copyright (C) 2007-2009, 2011 by Pino Toscano + * Copyright (C) 2011 Andreas Hartmetz + * Copyright (C) 2011 Hib Eris + * Copyright (C) 2012, 2013 Thomas Freitag + * Copyright (C) 2013 Julien Nabet + * Copyright (C) 2016 Jakub Alba + * 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 +#if defined(HAVE_SPLASH) +#include +#endif + +#include "poppler-qt4.h" +#include "poppler-embeddedfile-private.h" + +class LinkDest; +class FormWidget; + +namespace Poppler { + + /* borrowed from kpdf */ + QString unicodeToQString(Unicode* u, int len); + + QString UnicodeParsedString(GooString *s1); + + GooString *QStringToUnicodeGooString(const QString &s); + + GooString *QStringToGooString(const QString &s); + + GooString *QDateTimeToUnicodeGooString(const QDateTime &dt); + + void qt4ErrorFunction(int pos, char *msg, va_list args); + + class LinkDestinationData + { + public: + LinkDestinationData( LinkDest *l, GooString *nd, Poppler::DocumentData *pdfdoc, bool external ) + : ld(l), namedDest(nd), doc(pdfdoc), externalDest(external) + { + } + + LinkDest *ld; + GooString *namedDest; + Poppler::DocumentData *doc; + bool externalDest; + }; + + class DocumentData { + public: + DocumentData(const QString &filePath, GooString *ownerPassword, GooString *userPassword) + { + init(); + m_filePath = filePath; + +#if defined(_WIN32) + wchar_t *fileName = new WCHAR[filePath.length()]; + int length = filePath.toWCharArray(fileName); + doc = new PDFDoc(fileName, length, ownerPassword, userPassword); + delete[] fileName; +#else + GooString *fileName = new GooString(QFile::encodeName(filePath)); + doc = new PDFDoc(fileName, ownerPassword, userPassword); +#endif + + delete ownerPassword; + delete userPassword; + } + + DocumentData(const QByteArray &data, GooString *ownerPassword, GooString *userPassword) + { + fileContents = data; + MemStream *str = new MemStream((char*)fileContents.data(), 0, fileContents.length(), Object(objNull)); + init(); + doc = new PDFDoc(str, ownerPassword, userPassword); + delete ownerPassword; + delete userPassword; + } + + void init(); + + ~DocumentData(); + + void addTocChildren( QDomDocument * docSyn, QDomNode * parent, const std::vector * items ); + + void setPaperColor(const QColor &color) + { + paperColor = color; + } + + void fillMembers() + { + m_fontInfoIterator = new FontIterator(0, this); + int numEmb = doc->getCatalog()->numEmbeddedFiles(); + if (!(0 == numEmb)) { + // we have some embedded documents, build the list + for (int yalv = 0; yalv < numEmb; ++yalv) { + FileSpec *fs = doc->getCatalog()->embeddedFile(yalv); + m_embeddedFiles.append(new EmbeddedFile(*new EmbeddedFileData(fs))); + } + } + } + + static Document *checkDocument(DocumentData *doc); + + PDFDoc *doc; + QString m_filePath; + QByteArray fileContents; + bool locked; + FontIterator *m_fontInfoIterator; + Document::RenderBackend m_backend; + QList m_embeddedFiles; + QPointer m_optContentModel; + QColor paperColor; + int m_hints; + static int count; + }; + + class FontInfoData + { + public: + FontInfoData() + { + isEmbedded = false; + isSubset = false; + type = FontInfo::unknown; + } + + FontInfoData( const FontInfoData &fid ) + { + fontName = fid.fontName; + fontFile = fid.fontFile; + isEmbedded = fid.isEmbedded; + isSubset = fid.isSubset; + type = fid.type; + embRef = fid.embRef; + } + + FontInfoData( ::FontInfo* fi ) + { + if (fi->getName()) fontName = fi->getName()->c_str(); + if (fi->getFile()) fontFile = fi->getFile()->c_str(); + isEmbedded = fi->getEmbedded(); + isSubset = fi->getSubset(); + type = (Poppler::FontInfo::Type)fi->getType(); + embRef = fi->getEmbRef(); + } + + QString fontName; + 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(0), 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; + ::FormWidget *fm; + QRectF box; + }; + +} + +#endif diff --git a/qt4/src/poppler-ps-converter.cc b/qt4/src/poppler-ps-converter.cc new file mode 100644 index 00000000..4b43d8ec --- /dev/null +++ b/qt4/src/poppler-ps-converter.cc @@ -0,0 +1,280 @@ +/* poppler-ps-converter.cc: qt interface to poppler + * Copyright (C) 2007, 2009, 2010, 2015, 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 + * + * This program is free software; you can redistribute 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-qt4.h" + +#include "poppler-private.h" +#include "poppler-converter-private.h" + +#include "PSOutputDev.h" + +static void outputToQIODevice(void *stream, const char *data, int len) +{ + static_cast(stream)->write(data, len); +} + +namespace Poppler { + +class PSConverterPrivate : public BaseConverterPrivate +{ + public: + PSConverterPrivate(); + + 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(0), + pageConvertedPayload(0) +{ +} + + +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::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 = 0; + + 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)); + + 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, + NULL, + NULL, + annotDisplayDecideCbk, + &showAnnotations, true); + if (d->pageConvertedCallback) + (*d->pageConvertedCallback)(page, d->pageConvertedPayload); + } + delete psOut; + d->closeDevice(); + return true; + } + else + { + delete psOut; + d->closeDevice(); + return false; + } +} + +} diff --git a/qt4/src/poppler-qiodeviceoutstream-private.h b/qt4/src/poppler-qiodeviceoutstream-private.h new file mode 100644 index 00000000..d0d20073 --- /dev/null +++ b/qt4/src/poppler-qiodeviceoutstream-private.h @@ -0,0 +1,47 @@ +/* poppler-qiodevicestream-private.h: Qt4 interface to poppler + * Copyright (C) 2008, Pino Toscano + * Copyright (C) 2013 Adrian Johnson + * + * This program is free software; you can redistribute 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: + QIODeviceOutStream(QIODevice* device); + virtual ~QIODeviceOutStream(); + + virtual void close(); + virtual Goffset getPos(); + virtual void put(char c); + virtual void printf(const char *format, ...); + + private: + QIODevice *m_device; +}; + +} + +#endif diff --git a/qt4/src/poppler-qiodeviceoutstream.cc b/qt4/src/poppler-qiodeviceoutstream.cc new file mode 100644 index 00000000..e3e9f895 --- /dev/null +++ b/qt4/src/poppler-qiodeviceoutstream.cc @@ -0,0 +1,64 @@ +/* poppler-qiodevicestream.cc: Qt4 interface to poppler + * Copyright (C) 2008, Pino Toscano + * Copyright (C) 2013 Adrian Johnson + * + * This program is free software; you can redistribute 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 + +#define QIODeviceOutStreamBufSize 8192 + +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); +} + +void QIODeviceOutStream::printf(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + char buf[QIODeviceOutStreamBufSize]; + size_t bufsize = 0; + bufsize = qvsnprintf(buf, QIODeviceOutStreamBufSize - 1, format, ap); + va_end(ap); + m_device->write(buf, bufsize); +} + +} diff --git a/qt4/src/poppler-qt4.h b/qt4/src/poppler-qt4.h new file mode 100644 index 00000000..1b5afb2e --- /dev/null +++ b/qt4/src/poppler-qt4.h @@ -0,0 +1,1990 @@ +/* poppler-qt.h: qt interface to poppler + * Copyright (C) 2005, Net Integration Technologies, Inc. + * Copyright (C) 2005, 2007, Brad Hards + * Copyright (C) 2005-2012, 2014, 2015, 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 Adam Reichold + * Copyright (C) 2012, 2013 Thomas Freitag + * Copyright (C) 2016 Jakub Alba + * + * This program is free software; you can redistribute 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 "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 Qt4 binding. +*/ +namespace Poppler { + + class Document; + class DocumentData; + + class PageData; + + class FormField; + + class TextBoxData; + + class PDFConverter; + class PSConverter; + + /** + 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 + */ + typedef void (*PopplerDebugFunc)(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_QT4_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_QT4_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_QT4_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. + */ + FontInfo( const FontInfoData &fid ); + /// \endcond + + /** + Copy constructor. + */ + FontInfo( const FontInfo &fi ); + + /** + Destructor. + */ + ~FontInfo(); + + /** + The name of the font. Can be QString::null if the font has no name + */ + QString name() 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_QT4_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_QT4_EXPORT EmbeddedFile { + friend class DocumentData; + friend class AnnotationPrivate; + public: + /// \cond PRIVATE + 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) + 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_QT4_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 { + /** + 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; + + /** + 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 Arthur + + \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 = 0) 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 + { + IgnoreCase = 0x00000001, ///< Case differences are ignored + WholeWords = 0x00000002 ///< Only whole words are matched + }; + Q_DECLARE_FLAGS( SearchFlags, SearchFlag ) + + /** + Returns true if the specified text was found. + + \param text the text the search + \param rect 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 + **/ + Q_DECL_DEPRECATED bool search(const QString &text, QRectF &rect, 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 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 &rectLeft, double &rectTop, double &rectRight, double &rectBottom, SearchDirection direction, SearchFlags flags = 0, 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. + + \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 = 0, 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; + + /** + \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; + + private: + Q_DISABLE_COPY(Page) + + Page(DocumentData *doc, int index); + PageData *m_page; + }; + +/** + \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 + responsability 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 + avilable, all the color management-related functions will either do nothing + or return null. +*/ + class POPPLER_QT4_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 ///< Arthur (Qt4) backend + }; + + /** + 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 + }; + 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. + + \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. + + \since 0.12 + */ + void setColorDisplayProfileName(const QString &name); + /** + Return the current RGB profile. + + \return a \c cmsHPROFILE of the LCMS library. + + \since 0.12 + */ + void* colorRgbProfile() const; + /** + Return the current display profile. + + \return a \c cmsHPROFILE of the LCMS library. + + \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 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. + + \param index the page number index + */ + 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 data the type of date that is required + */ + QDateTime date( const QString & data ) 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 data the information that is required + + \sa infoKeys() to get a list of the available keys + */ + QString info( const QString & data ) 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 + + \deprecated use getPdfVersion and avoid float point + comparisons/handling + */ + Q_DECL_DEPRECATED double pdfVersion() 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 + + \since 0.12 + */ + void getPdfVersion(int *major, int *minor) 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; + + /** + Scans for fonts within the PDF document. + + \param numPages the number of pages to scan + \param fontList pointer to the list where the font information + should be placed + + \note with this method you can scan for fonts only \em once for each + document; once the end is reached, no more scanning with this method + can be done + + \return false if the end of the document has been reached + + \deprecated this function is quite limited in its job (see note), + better use fonts() or newFontIterator() + + \see fonts(), newFontIterator() + */ + Q_DECL_DEPRECATED bool scanForFonts( int numPages, QList *fontList ) 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 &font) 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 responsable 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 + */ + QDomDocument *toc() 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; + + /** + Destructor. + */ + ~Document(); + + private: + Q_DISABLE_COPY(Document) + + DocumentData *m_doc; + + 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_QT4_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 + 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_QT4_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 + }; + Q_DECLARE_FLAGS( PSOptions, PSOption ) + + /** + Destructor. + */ + ~PSConverter(); + + /** Sets the list of pages to print. Mandatory. */ + void setPageList(const QList &pageList); + + /** + Sets the title of the PS Document. Optional + */ + void setTitle(const QString &title); + + /** + Sets the horizontal DPI. Defaults to 72.0 + */ + void setHDPI(double hDPI); + + /** + Sets the vertical DPI. Defaults to 72.0 + */ + void setVDPI(double vDPI); + + /** + Sets the rotate. Defaults to not rotated + */ + void setRotate(int rotate); + + /** + Sets the output paper width. Has to be set. + */ + void setPaperWidth(int paperWidth); + + /** + Sets the output paper height. Has to be set. + */ + void setPaperHeight(int paperHeight); + + /** + Sets the output right margin. Defaults to 0 + */ + void setRightMargin(int marginRight); + + /** + Sets the output bottom margin. Defaults to 0 + */ + void setBottomMargin(int marginBottom); + + /** + Sets the output left margin. Defaults to 0 + */ + void setLeftMargin(int marginLeft); + + /** + Sets the output top margin. Defaults to 0 + */ + void setTopMargin(int marginTop); + + /** + Defines if margins have to be strictly followed (even if that + means changing aspect ratio), or if the margins can be adapted + to keep aspect ratio. + + Defaults to false. + */ + void setStrictMargins(bool strictMargins); + + /** Defines if the page will be rasterized to an image before printing. Defaults to false */ + void setForceRasterize(bool forceRasterize); + + /** + Sets the options for the PS export. + + \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(); + + private: + Q_DECLARE_PRIVATE(PSConverter) + Q_DISABLE_COPY(PSConverter) + + PSConverter(DocumentData *document); + }; + + /** + Converts a PDF to PDF (thus saves a copy of the document). + + \since 0.8 + */ + class POPPLER_QT4_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. + */ + virtual ~PDFConverter(); + + /** + Sets the options for the PDF export. + */ + void setPDFOptions(PDFOptions options); + /** + The currently set options for the PDF export. + */ + PDFOptions pdfOptions() const; + + bool convert(); + + private: + Q_DECLARE_PRIVATE(PDFConverter) + Q_DISABLE_COPY(PDFConverter) + + PDFConverter(DocumentData *document); + }; + + /** + Conversion from PDF date string format to QDateTime + */ + POPPLER_QT4_EXPORT QDateTime convertDate( char *dateString ); + + /** + Whether the color management functions are available. + + \since 0.12 + */ + POPPLER_QT4_EXPORT bool isCmsAvailable(); + + /** + Whether the overprint preview functionality is available. + + \since 0.22 + */ + POPPLER_QT4_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_QT4_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 + 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_QT4_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 + 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 diff --git a/qt4/src/poppler-sound.cc b/qt4/src/poppler-sound.cc new file mode 100644 index 00000000..eb19b9d3 --- /dev/null +++ b/qt4/src/poppler-sound.cc @@ -0,0 +1,132 @@ +/* poppler-sound.cc: qt interface to poppler + * Copyright (C) 2006-2007, 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-qt4.h" + +#include "Object.h" +#include "Stream.h" +#include "Sound.h" + +namespace Poppler +{ + +class SoundData +{ +public: + SoundData() + : m_soundObj( 0 ) + { + } + + ~SoundData() + { + delete m_soundObj; + } + + 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(); + + GooString * goo = m_soundData->m_soundObj->getFileName(); + return goo ? QString( goo->c_str() ) : QString(); +} + +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; +} + +} diff --git a/qt4/src/poppler-textbox.cc b/qt4/src/poppler-textbox.cc new file mode 100644 index 00000000..88cf2a9e --- /dev/null +++ b/qt4/src/poppler-textbox.cc @@ -0,0 +1,63 @@ +/* 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-qt4.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; +} + +} diff --git a/qt4/tests/.gitignore b/qt4/tests/.gitignore new file mode 100644 index 00000000..3746eb87 --- /dev/null +++ b/qt4/tests/.gitignore @@ -0,0 +1,33 @@ +.deps +.libs +*.la +*.lo +*.moc +Makefile +Makefile.in +stress-poppler-qt4 +stress-poppler-dir +test-poppler-qt4 +test-password-qt4 +poppler-attachments +poppler-fonts +poppler-texts +poppler-forms +stress-threads-qt4 +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 + diff --git a/qt4/tests/CMakeLists.txt b/qt4/tests/CMakeLists.txt new file mode 100644 index 00000000..a01a638a --- /dev/null +++ b/qt4/tests/CMakeLists.txt @@ -0,0 +1,67 @@ +add_definitions(${QT4_DEFINITIONS}) +add_definitions(-DTESTDATADIR=\"${TESTDATADIR}\") + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/../src + ${CMAKE_CURRENT_BINARY_DIR} + ${QT4_INCLUDE_DIR} +) + +macro(QT4_ADD_SIMPLETEST exe source) + string(REPLACE "-" "" test_name ${exe}) + set(${test_name}_SOURCES + ${source} + ) + poppler_add_test(${exe} BUILD_QT4_TESTS ${${test_name}_SOURCES}) + target_link_libraries(${exe} poppler-qt4) + if(MSVC) + target_link_libraries(${exe} poppler ${poppler_LIBS}) + endif() +endmacro(QT4_ADD_SIMPLETEST) + +macro(QT4_ADD_QTEST exe source) + if (QT4_QTTEST_FOUND) + string(REPLACE "-" "" test_name ${exe}) + set(${test_name}_SOURCES + ${source} + ) + poppler_add_unittest(${exe} BUILD_QT4_TESTS ${${test_name}_SOURCES}) + qt4_automoc(${${test_name}_SOURCES}) + target_link_libraries(${exe} poppler-qt4 ${QT4_QTTEST_LIBRARY}) + if(MSVC) + target_link_libraries(${exe} poppler ${poppler_LIBS}) + endif() + endif () +endmacro(QT4_ADD_QTEST) + + +qt4_add_simpletest(test-poppler-qt4 test-poppler-qt4.cpp) +qt4_add_simpletest(test-password-qt4 test-password-qt4.cpp) +qt4_add_simpletest(test-render-to-file-qt4 test-render-to-file.cpp) +qt4_add_simpletest(poppler-qt4-forms poppler-forms.cpp) +qt4_add_simpletest(poppler-qt4-fonts poppler-fonts.cpp) +qt4_add_simpletest(poppler-qt4-attachments poppler-attachments.cpp) +qt4_add_simpletest(stress-poppler-qt4 stress-poppler-qt4.cpp) +qt4_add_simpletest(stress-poppler-dir-qt4 stress-poppler-dir.cpp) +qt4_add_simpletest(stress-threads-qt4 stress-threads-qt4.cpp) +qt4_add_simpletest(poppler-qt4-texts poppler-texts.cpp) + +qt4_add_qtest(check_qt4_attachments check_attachments.cpp) +qt4_add_qtest(check_qt4_dateConversion check_dateConversion.cpp) +qt4_add_qtest(check_qt4_fonts check_fonts.cpp) +qt4_add_qtest(check_qt4_links check_links.cpp) +qt4_add_qtest(check_qt4_metadata check_metadata.cpp) +qt4_add_qtest(check_qt4_optcontent check_optcontent.cpp) +qt4_add_qtest(check_qt4_pagelayout check_pagelayout.cpp) +qt4_add_qtest(check_qt4_pagemode check_pagemode.cpp) +qt4_add_qtest(check_qt4_password check_password.cpp) +qt4_add_qtest(check_qt4_permissions check_permissions.cpp) +qt4_add_qtest(check_qt4_search check_search.cpp) +qt4_add_qtest(check_qt4_actualtext check_actualtext.cpp) +qt4_add_qtest(check_qt4_lexer check_lexer.cpp) +qt4_add_qtest(check_qt4_pagelabelinfo check_pagelabelinfo.cpp) +qt4_add_qtest(check_qt4_goostring check_goostring.cpp) +if (NOT WIN32) + qt4_add_qtest(check_qt4_strings check_strings.cpp) +endif () diff --git a/qt4/tests/README.unittest b/qt4/tests/README.unittest new file mode 100644 index 00000000..02296e08 --- /dev/null +++ b/qt4/tests/README.unittest @@ -0,0 +1,23 @@ +The unittests for the Qt4 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 diff --git a/qt4/tests/check_actualtext.cpp b/qt4/tests/check_actualtext.cpp new file mode 100644 index 00000000..5c765c51 --- /dev/null +++ b/qt4/tests/check_actualtext.cpp @@ -0,0 +1,33 @@ +#include + +#include + +#include + +class TestActualText: public QObject +{ + Q_OBJECT +private slots: + void checkActualText1(); +}; + +void TestActualText::checkActualText1() +{ + Poppler::Document *doc; + doc = Poppler::Document::load(TESTDATADIR "/unittestcases/WithActualText.pdf"); + QVERIFY( doc ); + + Poppler::Page *page = doc->page(0); + QVERIFY( page ); + + QCOMPARE( page->text(QRectF()), QString("The slow brown fox jumps over the black dog.") ); + + delete page; + + delete doc; +} + +QTEST_MAIN(TestActualText) + +#include "check_actualtext.moc" + diff --git a/qt4/tests/check_attachments.cpp b/qt4/tests/check_attachments.cpp new file mode 100644 index 00000000..73e31502 --- /dev/null +++ b/qt4/tests/check_attachments.cpp @@ -0,0 +1,157 @@ +#include + +#include + +#include + +class TestAttachments: public QObject +{ + Q_OBJECT +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(), QString( "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(), QString("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(), QString("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(), QString("text/xml") ); + + Poppler::EmbeddedFile *embfile2 = fileList.at(1); + QCOMPARE( embfile2->name(), QString("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(), QString("application/excel") ); + + Poppler::EmbeddedFile *embfile3 = fileList.at(2); + QCOMPARE( embfile3->name(), QString("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(), QString( "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(), QString( "export-altona.csv" ) ); + QCOMPARE( embfile->description(), QString("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(), QString("application/vnd.ms-excel") ); + delete doc; + +} + +QTEST_MAIN(TestAttachments) +#include "check_attachments.moc" + diff --git a/qt4/tests/check_dateConversion.cpp b/qt4/tests/check_dateConversion.cpp new file mode 100644 index 00000000..c1f84e2f --- /dev/null +++ b/qt4/tests/check_dateConversion.cpp @@ -0,0 +1,142 @@ +#include + +Q_DECLARE_METATYPE(QDate) +Q_DECLARE_METATYPE(QTime) + +#include + +class TestDateConv: public QObject +{ + Q_OBJECT +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.data()), 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.data()), QDateTime()); +} + +QTEST_MAIN(TestDateConv) + +#include "check_dateConversion.moc" diff --git a/qt4/tests/check_fonts.cpp b/qt4/tests/check_fonts.cpp new file mode 100644 index 00000000..77579a97 --- /dev/null +++ b/qt4/tests/check_fonts.cpp @@ -0,0 +1,248 @@ +#include + +#include + +#include + +class TestFontsData: public QObject +{ + Q_OBJECT +private slots: + void checkNoFonts(); + void checkType1(); + void checkType3(); + void checkTrueType(); + void checkFontIterator(); + void checkSecondDocumentQuery(); + void checkMultipleIterations(); + void checkScanForFonts(); +}; + + +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< Poppler::FontIterator > 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(), QString("Helvetica") ); + QCOMPARE( listOfFonts.at(0).type(), Poppler::FontInfo::Type1 ); + QCOMPARE( listOfFonts.at(0).typeName(), QString("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(), QString("Helvetica") ); + QCOMPARE( listOfFonts.at(0).type(), Poppler::FontInfo::Type1 ); + QCOMPARE( listOfFonts.at(0).typeName(), QString("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(), QString("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(), QString("Arial-BoldMT") ); + QCOMPARE( listOfFonts.at(0).type(), Poppler::FontInfo::TrueType ); + QCOMPARE( listOfFonts.at(0).typeName(), QString("TrueType") ); + + QCOMPARE( listOfFonts.at(0).isEmbedded(), false ); + QCOMPARE( listOfFonts.at(0).isSubset(), false ); + + QCOMPARE( listOfFonts.at(1).name(), QString("ArialMT") ); + QCOMPARE( listOfFonts.at(1).type(), Poppler::FontInfo::TrueType ); + QCOMPARE( listOfFonts.at(1).typeName(), QString("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< Poppler::FontIterator > 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::checkScanForFonts() +{ + 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 scanForFonts + QList listOfFonts2; + for ( int i = 0; i < doc->numPages(); ++i ) + { + doc->scanForFonts( 1, &listOfFonts2 ); + } + QCOMPARE( listOfFonts, listOfFonts2 ); + + // check doing a second scanForFonts gives no result + QList listOfFonts3; + for ( int i = 0; i < doc->numPages(); ++i ) + { + doc->scanForFonts( 1, &listOfFonts3 ); + } + QVERIFY( listOfFonts3.isEmpty() ); + + delete doc; +} + +QTEST_MAIN(TestFontsData) +#include "check_fonts.moc" + diff --git a/qt4/tests/check_goostring.cpp b/qt4/tests/check_goostring.cpp new file mode 100644 index 00000000..69f7cdc5 --- /dev/null +++ b/qt4/tests/check_goostring.cpp @@ -0,0 +1,127 @@ +#include +#include + +#include "goo/GooString.h" + +class TestGooString : public QObject +{ + Q_OBJECT +private slots: + void testInsertData_data(); + void testInsertData(); + void testInsert(); + void testFormat(); +}; + +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 QScopedPointer goo(GooString::format("{0:d},{1:x}", 1, 0xF)); + QCOMPARE(goo->c_str(), "1,f"); + } + { + const QScopedPointer 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 QScopedPointer goo(GooString::format("{0:d},{0:x},{0:X},{0:o},{0:b}", -0xA)); + QCOMPARE(goo->c_str(), "-10,-a,-A,-12,-1010"); + } + { + const QScopedPointer goo(GooString::format("{0:c}{1:c}{2:c}{3:c}", + 'T', (char)'E', (short)'S', (int)'T')); + QCOMPARE(goo->c_str(), "TEST"); + + const QScopedPointer goo2(GooString::format("{0:s} {1:t}", "TEST", goo.data())); + QCOMPARE(goo2->c_str(), "TEST TEST"); + } + { + const QScopedPointer goo(GooString::format("{0:ud} {1:d} {2:d}", + UINT_MAX, INT_MAX, INT_MIN)); + const QByteArray expected = QString("%1 %2 %3").arg(UINT_MAX).arg(INT_MAX).arg(INT_MIN).toLatin1(); + QCOMPARE(goo->c_str(), expected.constData()); + } + { + const QScopedPointer goo(GooString::format("{0:uld} {1:ld} {2:ld}", + ULONG_MAX, LONG_MAX, LONG_MIN)); + const QByteArray expected = QString("%1 %2 %3").arg(ULONG_MAX).arg(LONG_MAX).arg(LONG_MIN).toLatin1(); + QCOMPARE(goo->c_str(), expected.constData()); + } + { + const QScopedPointer goo(GooString::format("{0:ulld} {1:lld} {2:lld}", + ULLONG_MAX, LLONG_MAX, LLONG_MIN)); + const QByteArray expected = QString("%1 %2 %3").arg(ULLONG_MAX).arg(LLONG_MAX).arg(LLONG_MIN).toLatin1(); + QCOMPARE(goo->c_str(), expected.constData()); + } + { + const QScopedPointer gooD(GooString::format("{0:.1f} {0:.1g} {0:.1gs} | {1:.1f} {1:.1g} {1:.1gs}", 1., .012)); + const QScopedPointer 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 QScopedPointer goo(GooString::format("{0:.4f} {0:.4g} {0:.4gs}", .012)); + QCOMPARE(goo->c_str(), "0.0120 0.012 0.012"); + } + { + const QScopedPointer goo(GooString::format("{{ SomeText {0:d} }}", 1)); + QCOMPARE(goo->c_str(), "{ SomeText 1 }"); + } + { + const QScopedPointer goo(GooString::format("{{{{ {{ SomeText {0:d}", 2)); + QCOMPARE(goo->c_str(), "{{ { SomeText 2"); + } + { + const QScopedPointer goo(GooString::format("SomeText {0:d} }} }}}}", 3)); + QCOMPARE(goo->c_str(), "SomeText 3 } }}"); + } +} + +QTEST_MAIN(TestGooString) +#include "check_goostring.moc" + diff --git a/qt4/tests/check_lexer.cpp b/qt4/tests/check_lexer.cpp new file mode 100644 index 00000000..93c3621d --- /dev/null +++ b/qt4/tests/check_lexer.cpp @@ -0,0 +1,107 @@ +#include + +#include "Object.h" +#include "Lexer.h" + +class TestLexer : public QObject +{ + Q_OBJECT +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(NULL, 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_MAIN(TestLexer) +#include "check_lexer.moc" + diff --git a/qt4/tests/check_links.cpp b/qt4/tests/check_links.cpp new file mode 100644 index 00000000..e5c17368 --- /dev/null +++ b/qt4/tests/check_links.cpp @@ -0,0 +1,98 @@ +#include + +#include + +#include + +class TestLinks : public QObject +{ + Q_OBJECT +private slots: + void checkDocumentWithNoDests(); + void checkDests_xr01(); + void checkDests_xr02(); +}; + +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< Poppler::LinkDestination > dest; + dest.reset( doc->linkDestination("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< Poppler::Link* > links = page->links(); + QCOMPARE( links.count(), 2 ); + + { + QCOMPARE( links.at(0)->linkType(), Poppler::Link::Goto ); + Poppler::LinkGoto *link = static_cast< Poppler::LinkGoto * >( links.at(0) ); + const Poppler::LinkDestination dest = link->destination(); + QVERIFY( !isDestinationValid_pageNumber( &dest, doc ) ); + QVERIFY( isDestinationValid_name( &dest ) ); + QCOMPARE( dest.destinationName(), QString::fromLatin1("section.1") ); + } + + { + QCOMPARE( links.at(1)->linkType(), Poppler::Link::Goto ); + Poppler::LinkGoto *link = static_cast< Poppler::LinkGoto * >( links.at(1) ); + const Poppler::LinkDestination dest = link->destination(); + QVERIFY( !isDestinationValid_pageNumber( &dest, doc ) ); + QVERIFY( isDestinationValid_name( &dest ) ); + QCOMPARE( dest.destinationName(), QString::fromLatin1("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< Poppler::LinkDestination > dest; + dest.reset( doc->linkDestination("section.1") ); + QVERIFY( isDestinationValid_pageNumber( dest.get(), doc ) ); + QVERIFY( !isDestinationValid_name( dest.get() ) ); + dest.reset( doc->linkDestination("section.2") ); + QVERIFY( isDestinationValid_pageNumber( dest.get(), doc ) ); + QVERIFY( !isDestinationValid_name( dest.get() ) ); + dest.reset( doc->linkDestination("section.3") ); + QVERIFY( !isDestinationValid_pageNumber( dest.get(), doc ) ); + QVERIFY( isDestinationValid_name( dest.get() ) ); + + delete doc; +} + +QTEST_MAIN(TestLinks) + +#include "check_links.moc" diff --git a/qt4/tests/check_metadata.cpp b/qt4/tests/check_metadata.cpp new file mode 100644 index 00000000..fb4f7163 --- /dev/null +++ b/qt4/tests/check_metadata.cpp @@ -0,0 +1,275 @@ +#include + +#include + +class TestMetaData: public QObject +{ + Q_OBJECT +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 << "Title" << "Author" << "Creator" << "Keywords" << "CreationDate"; + keyList << "Producer" << "ModDate" << "Custom1" << "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("ModDate"), QDateTime(QDate(2005, 12, 5), QTime(9,44,46), Qt::UTC ) ); + QCOMPARE( doc->date("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 ); + + QCOMPARE( doc->pdfVersion(), 1.6 ); + int major = 0, minor = 0; + doc->getPdfVersion( &major, &minor ); + QCOMPARE( major, 1 ); + QCOMPARE( 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( 0, 0 ) ); + } + { + // only permanent ID + QByteArray permanentId; + QVERIFY( doc->getPdfId( &permanentId, 0 ) ); + QCOMPARE( permanentId.toUpper(), referencePermanentId ); + } + { + // only update ID + QByteArray updateId; + QVERIFY( doc->getPdfId( 0, &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( 0, 0 ) ); + + delete doc; +} + +QTEST_MAIN(TestMetaData) +#include "check_metadata.moc" + diff --git a/qt4/tests/check_optcontent.cpp b/qt4/tests/check_optcontent.cpp new file mode 100644 index 00000000..2de29952 --- /dev/null +++ b/qt4/tests/check_optcontent.cpp @@ -0,0 +1,446 @@ +#include + +#include "PDFDoc.h" +#include "GlobalParams.h" + +#include + +class TestOptionalContent: public QObject +{ + Q_OBJECT +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(), QString( "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(), QString( "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(), QString( "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(), QString( "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(), QString( "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(), QString( "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(), QString( "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() +{ + GooString *fileName = new GooString(TESTDATADIR "/unittestcases/vis_policy_test.pdf"); + globalParams = std::make_unique(); + PDFDoc *doc = new PDFDoc( fileName ); + 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(); + GooString *fileName = new GooString(TESTDATADIR "/unittestcases/vis_policy_test.pdf"); + PDFDoc *doc = new PDFDoc( fileName ); + 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( { 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( { 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(), QString( "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(), QString( "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(), QString( "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(), QString( "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(), QString( "English" ) ); + QCOMPARE( static_cast( optContent->data( subindex, Qt::CheckStateRole ).toInt() ), Qt::Unchecked ); + + subindex = optContent->index( 2, 0, index ); + QCOMPARE( optContent->data( subindex, Qt::DisplayRole ).toString(), QString( "Japanese" ) ); + QCOMPARE( static_cast( optContent->data( subindex, Qt::CheckStateRole ).toInt() ), Qt::Checked ); + + subindex = optContent->index( 1, 0, index ); + QCOMPARE( optContent->data( subindex, Qt::DisplayRole ).toString(), QString( "French" ) ); + QCOMPARE( static_cast( optContent->data( subindex, Qt::CheckStateRole ).toInt() ), Qt::Unchecked ); + + // 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(), QString( "English" ) ); + QCOMPARE( static_cast( optContent->data( subindex, Qt::CheckStateRole ).toInt() ), Qt::Unchecked ); + + subindex = optContent->index( 2, 0, index ); + QCOMPARE( optContent->data( subindex, Qt::DisplayRole ).toString(), QString( "Japanese" ) ); + QCOMPARE( static_cast( optContent->data( subindex, Qt::CheckStateRole ).toInt() ), Qt::Unchecked ); + + subindex = optContent->index( 1, 0, index ); + QCOMPARE( optContent->data( subindex, Qt::DisplayRole ).toString(), QString( "French" ) ); + QCOMPARE( static_cast( optContent->data( subindex, Qt::CheckStateRole ).toInt() ), Qt::Checked ); + + + // 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(), QString( "English" ) ); + QCOMPARE( static_cast( optContent->data( subindex, Qt::CheckStateRole ).toInt() ), Qt::Unchecked ); + + subindex = optContent->index( 2, 0, index ); + QCOMPARE( optContent->data( subindex, Qt::DisplayRole ).toString(), QString( "Japanese" ) ); + QCOMPARE( static_cast( optContent->data( subindex, Qt::CheckStateRole ).toInt() ), Qt::Unchecked ); + + subindex = optContent->index( 1, 0, index ); + QCOMPARE( optContent->data( subindex, Qt::DisplayRole ).toString(), QString( "French" ) ); + QCOMPARE( static_cast( optContent->data( subindex, Qt::CheckStateRole ).toInt() ), Qt::Unchecked ); + + delete doc; +} + +QTEST_MAIN(TestOptionalContent) + +#include "check_optcontent.moc" + diff --git a/qt4/tests/check_pagelabelinfo.cpp b/qt4/tests/check_pagelabelinfo.cpp new file mode 100644 index 00000000..4eb1ec36 --- /dev/null +++ b/qt4/tests/check_pagelabelinfo.cpp @@ -0,0 +1,43 @@ +#include + +#include "PageLabelInfo_p.h" + +class TestPageLabelInfo : public QObject +{ + Q_OBJECT +private slots: + void testToRoman(); + void testFromRoman(); + void testToLatin(); + void testFromLatin(); +}; + +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_MAIN(TestPageLabelInfo) +#include "check_pagelabelinfo.moc" + diff --git a/qt4/tests/check_pagelayout.cpp b/qt4/tests/check_pagelayout.cpp new file mode 100644 index 00000000..6108f886 --- /dev/null +++ b/qt4/tests/check_pagelayout.cpp @@ -0,0 +1,49 @@ +#include + +#include + +class TestPageLayout: public QObject +{ + Q_OBJECT +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_MAIN(TestPageLayout) +#include "check_pagelayout.moc" + diff --git a/qt4/tests/check_pagemode.cpp b/qt4/tests/check_pagemode.cpp new file mode 100644 index 00000000..0565fe20 --- /dev/null +++ b/qt4/tests/check_pagemode.cpp @@ -0,0 +1,73 @@ +#include + +#include + +class TestPageMode: public QObject +{ + Q_OBJECT +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_MAIN(TestPageMode) +#include "check_pagemode.moc" + diff --git a/qt4/tests/check_password.cpp b/qt4/tests/check_password.cpp new file mode 100644 index 00000000..4c7dcd1c --- /dev/null +++ b/qt4/tests/check_password.cpp @@ -0,0 +1,88 @@ +#include + +#include + +class TestPassword: public QObject +{ + Q_OBJECT +private slots: + void password1(); + void password1a(); + void password2(); + void password2a(); + void password2b(); + void password3(); +}; + + +// 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() ); + 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") ); + QVERIFY( doc ); + QVERIFY( doc->isLocked() ); + QVERIFY( !doc->unlock( "", QString::fromUtf8("garçon").toLatin1() ) ); + 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(), "" ); + 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() ); + 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(), "" ) ); + 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; +} + +QTEST_MAIN(TestPassword) +#include "check_password.moc" + diff --git a/qt4/tests/check_permissions.cpp b/qt4/tests/check_permissions.cpp new file mode 100644 index 00000000..a3f3bdc6 --- /dev/null +++ b/qt4/tests/check_permissions.cpp @@ -0,0 +1,44 @@ +#include + +#include + +class TestPermissions: public QObject +{ + Q_OBJECT +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_MAIN(TestPermissions) +#include "check_permissions.moc" + diff --git a/qt4/tests/check_search.cpp b/qt4/tests/check_search.cpp new file mode 100644 index 00000000..99659e04 --- /dev/null +++ b/qt4/tests/check_search.cpp @@ -0,0 +1,175 @@ +#include + +#include + +class TestSearch: public QObject +{ + Q_OBJECT +private slots: + void bug7063(); + void testNextAndPrevious(); + void testWholeWordsOnly(); +}; + +void TestSearch::bug7063() +{ + QScopedPointer< Poppler::Document > document(Poppler::Document::load(TESTDATADIR "/unittestcases/bug7063.pdf")); + QVERIFY( document ); + + QScopedPointer< Poppler::Page > page(document->page(0)); + QVERIFY( page ); + + QRectF pageRegion( QPointF(0,0), page->pageSize() ); + QCOMPARE( page->search(QString("non-ascii:"), pageRegion, Poppler::Page::FromTop, Poppler::Page::CaseSensitive), true ); + + QCOMPARE( page->search(QString("Ascii"), pageRegion, Poppler::Page::FromTop, Poppler::Page::CaseSensitive), false ); + QCOMPARE( page->search(QString("Ascii"), pageRegion, Poppler::Page::FromTop, Poppler::Page::CaseInsensitive), true ); + + QCOMPARE( page->search(QString("latin1:"), pageRegion, Poppler::Page::FromTop, Poppler::Page::CaseSensitive), false ); + + QCOMPARE( page->search(QString::fromUtf8("é"), pageRegion, Poppler::Page::FromTop, Poppler::Page::CaseSensitive), true ); + QCOMPARE( page->search(QString::fromUtf8("à"), pageRegion, Poppler::Page::FromTop, Poppler::Page::CaseSensitive), true ); + QCOMPARE( page->search(QString::fromUtf8("ç"), pageRegion, Poppler::Page::FromTop, Poppler::Page::CaseSensitive), true ); + QCOMPARE( page->search(QString::fromUtf8("search \"é\", \"à\" or \"ç\""), pageRegion, Poppler::Page::FromTop, Poppler::Page::CaseSensitive), true ); + QCOMPARE( page->search(QString::fromUtf8("¥µ©"), pageRegion, Poppler::Page::FromTop, Poppler::Page::CaseSensitive), true ); + QCOMPARE( page->search(QString::fromUtf8("¥©"), pageRegion, Poppler::Page::FromTop, Poppler::Page::CaseSensitive), false ); + + double rectLeft = 0.0, rectTop = 0.0, rectRight = page->pageSizeF().width(), rectBottom = page->pageSizeF().height(); + + QCOMPARE( page->search(QString("non-ascii:"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), true ); + + QCOMPARE( page->search(QString("Ascii"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop), false ); + QCOMPARE( page->search(QString("Ascii"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::FromTop, Poppler::Page::IgnoreCase), true ); + + QCOMPARE( page->search(QString("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< Poppler::Document > document(Poppler::Document::load(TESTDATADIR "/unittestcases/xr01.pdf")); + QVERIFY( document ); + + QScopedPointer< Poppler::Page > page(document->page(0)); + QVERIFY( page ); + + QRectF region( QPointF(0,0), page->pageSize() ); + + QCOMPARE( page->search(QString("is"), region, Poppler::Page::FromTop, Poppler::Page::CaseSensitive), true ); + QVERIFY( qAbs(region.x() - 161.44) < 0.01 ); + QVERIFY( qAbs(region.y() - 127.85) < 0.01 ); + QVERIFY( qAbs(region.width() - 6.70) < 0.01 ); + QVERIFY( qAbs(region.height() - 8.85) < 0.01 ); + QCOMPARE( page->search(QString("is"), region, Poppler::Page::NextResult, Poppler::Page::CaseSensitive), true ); + QVERIFY( qAbs(region.x() - 171.46) < 0.01 ); + QVERIFY( qAbs(region.y() - 127.85) < 0.01 ); + QVERIFY( qAbs(region.width() - 6.70) < 0.01 ); + QVERIFY( qAbs(region.height() - 8.85) < 0.01 ); + QCOMPARE( page->search(QString("is"), region, Poppler::Page::NextResult, Poppler::Page::CaseSensitive), true ); + QVERIFY( qAbs(region.x() - 161.44) < 0.01 ); + QVERIFY( qAbs(region.y() - 139.81) < 0.01 ); + QVERIFY( qAbs(region.width() - 6.70) < 0.01 ); + QVERIFY( qAbs(region.height() - 8.85) < 0.01 ); + QCOMPARE( page->search(QString("is"), region, Poppler::Page::NextResult, Poppler::Page::CaseSensitive), true ); + QVERIFY( qAbs(region.x() - 171.46) < 0.01 ); + QVERIFY( qAbs(region.y() - 139.81) < 0.01 ); + QVERIFY( qAbs(region.width() - 6.70) < 0.01 ); + QVERIFY( qAbs(region.height() - 8.85) < 0.01 ); + QCOMPARE( page->search(QString("is"), region, Poppler::Page::NextResult, Poppler::Page::CaseSensitive), false ); + QCOMPARE( page->search(QString("is"), region, Poppler::Page::PreviousResult, Poppler::Page::CaseSensitive), true ); + QVERIFY( qAbs(region.x() - 161.44) < 0.01 ); + QVERIFY( qAbs(region.y() - 139.81) < 0.01 ); + QVERIFY( qAbs(region.width() - 6.70) < 0.01 ); + QVERIFY( qAbs(region.height() - 8.85) < 0.01 ); + QCOMPARE( page->search(QString("is"), region, Poppler::Page::PreviousResult, Poppler::Page::CaseSensitive), true ); + QVERIFY( qAbs(region.x() - 171.46) < 0.01 ); + QVERIFY( qAbs(region.y() - 127.85) < 0.01 ); + QVERIFY( qAbs(region.width() - 6.70) < 0.01 ); + QVERIFY( qAbs(region.height() - 8.85) < 0.01 ); + QCOMPARE( page->search(QString("is"), region, Poppler::Page::PreviousResult, Poppler::Page::CaseSensitive), true ); + QVERIFY( qAbs(region.x() - 161.44) < 0.01 ); + QVERIFY( qAbs(region.y() - 127.85) < 0.01 ); + QVERIFY( qAbs(region.width() - 6.70) < 0.01 ); + QVERIFY( qAbs(region.height() - 8.85) < 0.01 ); + QCOMPARE( page->search(QString("is"), region, Poppler::Page::PreviousResult, Poppler::Page::CaseSensitive), false ); + + double rectLeft = 0.0, rectTop = 0.0, rectRight = page->pageSizeF().width(), rectBottom = page->pageSizeF().height(); + + QCOMPARE( page->search(QString("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(QString("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(QString("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(QString("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(QString("is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::NextResult), false ); + QCOMPARE( page->search(QString("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(QString("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(QString("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(QString("is"), rectLeft, rectTop, rectRight, rectBottom, Poppler::Page::PreviousResult), false ); +} + +void TestSearch::testWholeWordsOnly() +{ + QScopedPointer< Poppler::Document > document(Poppler::Document::load(TESTDATADIR "/unittestcases/WithActualText.pdf")); + QVERIFY( document ); + + QScopedPointer< Poppler::Page > page(document->page(0)); + QVERIFY( page ); + + const Poppler::Page::SearchDirection direction = Poppler::Page::FromTop; + + const Poppler::Page::SearchFlags mode0 = 0; + 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(QLatin1String("brown"), left, top, right, bottom, direction, mode0), true ); + QCOMPARE( page->search(QLatin1String("brOwn"), left, top, right, bottom, direction, mode0), false ); + + QCOMPARE( page->search(QLatin1String("brOwn"), left, top, right, bottom, direction, mode1), true ); + QCOMPARE( page->search(QLatin1String("brawn"), left, top, right, bottom, direction, mode1), false ); + + QCOMPARE( page->search(QLatin1String("brown"), left, top, right, bottom, direction, mode2), true ); + QCOMPARE( page->search(QLatin1String("own"), left, top, right, bottom, direction, mode2), false ); + + QCOMPARE( page->search(QLatin1String("brOwn"), left, top, right, bottom, direction, mode3), true ); + QCOMPARE( page->search(QLatin1String("Own"), left, top, right, bottom, direction, mode3), false ); +} + +QTEST_MAIN(TestSearch) +#include "check_search.moc" + diff --git a/qt4/tests/check_strings.cpp b/qt4/tests/check_strings.cpp new file mode 100644 index 00000000..700ae9c2 --- /dev/null +++ b/qt4/tests/check_strings.cpp @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2010, 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. + */ + +#include + +#include +#include + +#include + +Q_DECLARE_METATYPE(GooString*) +Q_DECLARE_METATYPE(Unicode*) + +class TestStrings : public QObject +{ + Q_OBJECT + +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 << QString::fromUtf8("a"); + } + { + const int l = 1; + Unicode *u = new Unicode[l]; + u[0] = 0x0161; + QTest::newRow("\u0161") << u << l << QString::fromUtf8("\u0161"); + } + { + const int l = 2; + Unicode *u = new Unicode[l]; + u[0] = int('a'); + u[1] = int('b'); + QTest::newRow("ab") << u << l << QString::fromUtf8("ab"); + } + { + const int l = 2; + Unicode *u = new Unicode[l]; + u[0] = int('a'); + u[1] = 0x0161; + QTest::newRow("a\u0161") << u << l << QString::fromUtf8("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 << QString::fromUtf8("\xe5\xb0\x81\xe9\x9d\xa2"); + } + { + 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 << QString::fromUtf8("\xe5\xb0\x81\xe9\x9d\xa2"); + } +} + +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") + << QString::fromUtf8("a"); + QTest::newRow("ab") << newGooString("ab") + << QString::fromUtf8("ab"); + QTest::newRow("~") << newGooString("~") + << QString::fromUtf8("~"); + QTest::newRow("test string") << newGooString("test string") + << QString::fromUtf8("test string"); + + // unicode strings + QTest::newRow("") << newGooString("\xFE\xFF") + << QString(); + QTest::newRow("U a") << newGooString("\xFE\xFF\0a", 4) + << QString::fromUtf8("a"); + QTest::newRow("U ~") << newGooString("\xFE\xFF\0~", 4) + << QString::fromUtf8("~"); + QTest::newRow("U aa") << newGooString("\xFE\xFF\0a\0a", 6) + << QString::fromUtf8("aa"); + QTest::newRow("U \xC3\x9F") << newGooString("\xFE\xFF\0\xDF", 4) + << QString::fromUtf8("\xC3\x9F"); + QTest::newRow("U \xC3\x9F\x61") << newGooString("\xFE\xFF\0\xDF\0\x61", 6) + << QString::fromUtf8("\xC3\x9F\x61"); + QTest::newRow("U \xC5\xA1") << newGooString("\xFE\xFF\x01\x61", 4) + << QString::fromUtf8("\xC5\xA1"); + QTest::newRow("U \xC5\xA1\x61") << newGooString("\xFE\xFF\x01\x61\0\x61", 6) + << QString::fromUtf8("\xC5\xA1\x61"); + QTest::newRow("test string") << newGooString("\xFE\xFF\0t\0e\0s\0t\0 \0s\0t\0r\0i\0n\0g", 24) + << QString::fromUtf8("test string"); +} + +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::fromUtf8("") + << QByteArray(""); + QTest::newRow("a") << QString::fromUtf8("a") + << QByteArray("\0a", 2); + QTest::newRow("ab") << QString::fromUtf8("ab") + << QByteArray("\0a\0b", 4); + QTest::newRow("test string") << QString::fromUtf8("test string") + << QByteArray("\0t\0e\0s\0t\0 \0s\0t\0r\0i\0n\0g", 22); + QTest::newRow("\xC3\x9F") << QString::fromUtf8("\xC3\x9F") + << QByteArray("\0\xDF", 2); + QTest::newRow("\xC3\x9F\x61") << QString::fromUtf8("\xC3\x9F\x61") + << QByteArray("\0\xDF\0\x61", 4); +} + +void TestStrings::check_QStringToUnicodeGooString() +{ + QFETCH(QString, string); + QFETCH(QByteArray, result); + + GooString *goo = Poppler::QStringToUnicodeGooString(string); + 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::fromUtf8("") + << newGooString(""); + QTest::newRow("a") << QString::fromUtf8("a") + << newGooString("a"); + QTest::newRow("ab") << QString::fromUtf8("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_MAIN(TestStrings) + +#include "check_strings.moc" diff --git a/qt4/tests/poppler-attachments.cpp b/qt4/tests/poppler-attachments.cpp new file mode 100644 index 00000000..992dc565 --- /dev/null +++ b/qt4/tests/poppler-attachments.cpp @@ -0,0 +1,39 @@ +#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; + +} diff --git a/qt4/tests/poppler-fonts.cpp b/qt4/tests/poppler-fonts.cpp new file mode 100644 index 00000000..6b66ec42 --- /dev/null +++ b/qt4/tests/poppler-fonts.cpp @@ -0,0 +1,89 @@ +#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( QString("%1").arg(QString("[none]"), -37) ); + } else { + std::cout << qPrintable( QString("%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( QString("%1").arg(font.file()) ); + std::cout << std::endl; + } + delete doc; +} diff --git a/qt4/tests/poppler-forms.cpp b/qt4/tests/poppler-forms.cpp new file mode 100644 index 00000000..98891a91 --- /dev/null +++ b/qt4/tests/poppler-forms.cpp @@ -0,0 +1,166 @@ +#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, 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 << QString("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: + break; + } + } + qDeleteAll(forms); + } + } + delete doc; +} diff --git a/qt4/tests/poppler-texts.cpp b/qt4/tests/poppler-texts.cpp new file mode 100644 index 00000000..ec283531 --- /dev/null +++ b/qt4/tests/poppler-texts.cpp @@ -0,0 +1,40 @@ +#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; +} diff --git a/qt4/tests/stress-poppler-dir.cpp b/qt4/tests/stress-poppler-dir.cpp new file mode 100644 index 00000000..6eeab6fa --- /dev/null +++ b/qt4/tests/stress-poppler-dir.cpp @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include + +#include + +#include + +int main( int argc, char **argv ) +{ + QApplication a( argc, argv ); // QApplication required! + + QTime t; + t.start(); + + QDir directory( argv[1] ); + foreach ( const QString &fileName, directory.entryList() ) { + if (fileName.endsWith("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 { + int major = 0, minor = 0; + doc->getPdfVersion( &major, &minor ); + doc->info("Title"); + doc->info("Subject"); + doc->info("Author"); + doc->info("Keywords"); + doc->info("Creator"); + doc->info("Producer"); + doc->date("CreationDate").toString(); + doc->date("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 ); + QImage image = 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; + +} diff --git a/qt4/tests/stress-poppler-qt4.cpp b/qt4/tests/stress-poppler-qt4.cpp new file mode 100644 index 00000000..56844543 --- /dev/null +++ b/qt4/tests/stress-poppler-qt4.cpp @@ -0,0 +1,74 @@ +#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 ); + + QTime t; + t.start(); + QDir dbDir( QString( "./pdfdb" ) ); + if ( !dbDir.exists() ) { + qWarning() << "Database directory does not exist"; + } + + QStringList excludeSubDirs; + excludeSubDirs << "000048" << "000607"; + + foreach ( const QString &subdir, dbDir.entryList(QStringList() << "0000*", QDir::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 { + int major = 0, minor = 0; + doc->getPdfVersion( &major, &minor ); + doc->info("Title"); + doc->info("Subject"); + doc->info("Author"); + doc->info("Keywords"); + doc->info("Creator"); + doc->info("Producer"); + doc->date("CreationDate").toString(); + doc->date("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 ); + QImage image = 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; + +} diff --git a/qt4/tests/stress-threads-qt4.cpp b/qt4/tests/stress-threads-qt4.cpp new file mode 100644 index 00000000..00968de1 --- /dev/null +++ b/qt4/tests/stress-threads-qt4.cpp @@ -0,0 +1,309 @@ + +#ifndef _WIN32 +#include +#else +#include +#define sleep Sleep +#endif +#include + +#include +#include + +#include +#include +#include +#include +#include + +class SillyThread : public QThread +{ +public: + SillyThread(Poppler::Document* document, QObject* parent = 0); + + void run(); + +private: + Poppler::Document* m_document; + QVector< Poppler::Page* > m_pages; + +}; + +class CrazyThread : public QThread +{ +public: + CrazyThread(uint seed, Poppler::Document* document, QMutex* annotationMutex, QObject* parent = 0); + + void run(); + +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 == 0) + { + 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< Poppler::Page > PagePointer; + + qsrand(m_seed); + + forever + { + if(qrand() % 2 == 0) + { + qDebug() << "search..."; + + PagePointer page(loadRandomPage(m_document)); + + page->search("c", Poppler::Page::CaseInsensitive); + page->search("r", Poppler::Page::CaseSensitive); + page->search("a", Poppler::Page::CaseInsensitive); + page->search("z", Poppler::Page::CaseSensitive); + page->search("y", Poppler::Page::CaseInsensitive); + } + + if(qrand() % 2 == 0) + { + qDebug() << "links..."; + + PagePointer page(loadRandomPage(m_document)); + + QList< Poppler::Link* > links = page->links(); + + qDeleteAll(links); + } + + if(qrand() % 2 == 0) + { + qDebug() << "form fields..."; + + PagePointer page(loadRandomPage(m_document)); + + QList< Poppler::FormField* > 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 = 0; + + 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("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< Poppler::Annotation* > 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("foo"); + annotations.at(qrand() % annotations.size())->setContents("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< Poppler::Annotation* > 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(0)); + + for(int argi = 4; argi < argc; ++argi) + { + const QString file = QFile::decodeName(argv[argi]); + Poppler::Document* document = Poppler::Document::load(file); + + if(document == 0) + { + 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; +} diff --git a/qt4/tests/test-password-qt4.cpp b/qt4/tests/test-password-qt4.cpp new file mode 100644 index 00000000..c961874d --- /dev/null +++ b/qt4/tests/test-password-qt4.cpp @@ -0,0 +1,136 @@ +#include +#include +#include +#include +#include +#include + +#include + +class PDFDisplay : public QWidget // picture display widget +{ +public: + PDFDisplay( Poppler::Document *d ); + ~PDFDisplay(); +protected: + void paintEvent( QPaintEvent * ); + void keyPressEvent( QKeyEvent * ); +private: + void display(); + int m_currentPage; + QImage image; + Poppler::Document *doc; +}; + +PDFDisplay::PDFDisplay( Poppler::Document *d ) +{ + 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-qt4 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 + int major = 0, minor = 0; + doc->getPdfVersion( &major, &minor ); + qDebug() << " PDF Version: " << qPrintable(QString::fromLatin1("%1.%2").arg(major).arg(minor)); + qDebug() << " Title: " << doc->info("Title"); + qDebug() << " Subject: " << doc->info("Subject"); + qDebug() << " Author: " << doc->info("Author"); + qDebug() << " Key words: " << doc->info("Keywords"); + qDebug() << " Creator: " << doc->info("Creator"); + qDebug() << " Producer: " << doc->info("Producer"); + qDebug() << " Date created: " << doc->date("CreationDate").toString(); + qDebug() << " Date modified: " << doc->date("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( ", " ); + + 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("Poppler-Qt4 Test"); + test.show(); // show it + + return a.exec(); // start event loop +} diff --git a/qt4/tests/test-poppler-qt4.cpp b/qt4/tests/test-poppler-qt4.cpp new file mode 100644 index 00000000..ae6b11f3 --- /dev/null +++ b/qt4/tests/test-poppler-qt4.cpp @@ -0,0 +1,235 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class PDFDisplay : public QWidget // picture display widget +{ +public: + PDFDisplay( Poppler::Document *d, bool arthur ); + ~PDFDisplay(); + void setShowTextRects(bool show); + void display(); +protected: + void paintEvent( QPaintEvent * ); + void keyPressEvent( QKeyEvent * ); + void mousePressEvent( QMouseEvent * ); +private: + int m_currentPage; + QImage image; + Poppler::Document *doc; + QString backendString; + bool showTextRects; + QList textRects; +}; + +PDFDisplay::PDFDisplay( Poppler::Document *d, bool arthur ) +{ + showTextRects = false; + doc = d; + m_currentPage = 0; + if (arthur) + { + backendString = "Arthur"; + doc->setRenderBackend(Poppler::Document::ArthurBackend); + } + else + { + backendString = "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())) + { + QString tt = QString("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], "-arthur") != 0 && strcmp(argv[2], "-textRects") != 0) || + argc > 3) + { + // use argument as file name + qWarning() << "usage: test-poppler-qt4 filename [-extract|-arthur|-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 + int major = 0, minor = 0; + doc->getPdfVersion( &major, &minor ); + qDebug() << " PDF Version: " << qPrintable(QString::fromLatin1("%1.%2").arg(major).arg(minor)); + qDebug() << " Title: " << doc->info("Title"); + qDebug() << " Subject: " << doc->info("Subject"); + qDebug() << " Author: " << doc->info("Author"); + qDebug() << " Key words: " << doc->info("Keywords"); + qDebug() << " Creator: " << doc->info("Creator"); + qDebug() << " Producer: " << doc->info("Producer"); + qDebug() << " Date created: " << doc->date("CreationDate").toString(); + qDebug() << " Date modified: " << doc->date("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], "-arthur") == 0) || (argc == 3 && strcmp(argv[2], "-textRects") == 0)) + { + bool useArthur = (argc == 3 && strcmp(argv[2], "-arthur") == 0); + PDFDisplay test( doc, useArthur ); // create picture display + test.setWindowTitle("Poppler-Qt4 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()), 0); + l->show(); + delete page; + delete doc; + return a.exec(); + } +} diff --git a/qt4/tests/test-render-to-file.cpp b/qt4/tests/test-render-to-file.cpp new file mode 100644 index 00000000..b01aa03c --- /dev/null +++ b/qt4/tests/test-render-to-file.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include + +#include + +int main( int argc, char **argv ) +{ + QApplication a( argc, argv ); // QApplication required! + + if ( argc < 2 || + (argc == 3 && strcmp(argv[2], "-arthur") != 0) || + argc > 3) + { + // use argument as file name + qWarning() << "usage: test-poppler-qt4 filename [-arthur]"; + 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], "-arthur") == 0) + { + backendString = "Arthur"; + doc->setRenderBackend(Poppler::Document::ArthurBackend); + } + else + { + backendString = "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(QString("test-rennder-to-file%1.ppm").arg(i)); + delete page; + } + } + + return 0; +} -- 2.14.3