diff --git a/.gitignore b/.gitignore index e867ae0..18c73a2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /poppler-test-2009-05-13_0d2bfd4af4c76a3bac27ccaff793d9129df7b57a.tar.xz /poppler-0.61.1.tar.xz +/poppler-0.62.0.tar.xz diff --git a/0001-Revert-Remove-the-Qt4-frontend.patch b/0001-Revert-Remove-the-Qt4-frontend.patch new file mode 100644 index 0000000..a81e02a --- /dev/null +++ b/0001-Revert-Remove-the-Qt4-frontend.patch @@ -0,0 +1,26536 @@ +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_XPDF_HEADERS "Install unsupported xpdf headers." OFF) + option(BUILD_GTK_TESTS "Whether compile the GTK+ test programs." ON) ++option(BUILD_QT4_TESTS "Whether compile the Qt4 test programs." ON) + option(BUILD_QT5_TESTS "Whether compile the Qt5 test programs." ON) + option(BUILD_CPP_TESTS "Whether 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_CPP "Compile poppler cpp wrapper." ON) + option(ENABLE_GLIB "Compile poppler glib wrapper." 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) + 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) + message(" with CMYK support") + endif() + 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,812 @@ ++//======================================================================== ++// ++// 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(Ref *rA) { r = *rA; } ++ ++ ~SplashOutFontFileID() {} ++ ++ GBool 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( ++ globalParams->getEnableFreeType(), ++ 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 = gTrue; ++} ++ ++// 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; ++ 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()->getCString() ++ : "(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, gFalse); ++ else ++ fontsrc->setBuf(tmpBuf, tmpBufLen, gTrue); ++ ++ // 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()->getCString() ++ : "(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()->getCString() ++ : "(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()->getCString() ++ : "(unnamed)"); ++ goto err2; ++ } ++ break; ++ case fontTrueType: ++ case fontTrueTypeOT: ++ if (fileName) ++ ff = FoFiTrueType::load(fileName->getCString()); ++ 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()->getCString() ++ : "(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()->getCString() ++ : "(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()->getCString() ++ : "(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(Gushort)); ++ } ++ } else { ++ if (fileName) ++ ff = FoFiTrueType::load(fileName->getCString()); ++ 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()->getCString() ++ : "(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, GfxPath *path, Qt::FillRule fillRule) ++{ ++ 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; ++// SplashPath *path; ++ int render; ++ ++ 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->length; ++i) { ++ // SplashPath.flags: bitwise or allowed ++ if (fontPath->flags[i] & splashPathLast || fontPath->flags[i] & splashPathClosed) { ++ qPath.closeSubpath(); ++ } ++ if (fontPath->flags[i] & splashPathFirst) { ++ state->transform(fontPath->pts[i].x+x, -fontPath->pts[i].y+y, &x1, &y1); ++ qPath.moveTo(x1,y1); ++ } ++ if (fontPath->flags[i] & splashPathCurve) { ++ state->transform(fontPath->pts[i].x+x, -fontPath->pts[i].y+y, &x1, &y1); ++ state->transform(fontPath->pts[i+1].x+x, -fontPath->pts[i+1].y+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 { ++ state->transform(fontPath->pts[i].x+x, -fontPath->pts[i].y+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 ++} ++ ++GBool ArthurOutputDev::beginType3Char(GfxState *state, double x, double y, ++ double dx, double dy, ++ CharCode code, Unicode *u, int uLen) ++{ ++ return gFalse; ++} ++ ++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, GBool invert, ++ GBool interpolate, GBool 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, ++ GBool interpolate, int *maskColors, GBool inlineImg) ++{ ++ unsigned int *data; ++ unsigned int *line; ++ int x, y; ++ ImageStream *imgStr; ++ Guchar *pix; ++ int i; ++ 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,170 @@ ++//======================================================================== ++// ++// 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 "goo/gtypes.h" ++#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 GBool upsideDown() { return gTrue; } ++ ++ // Does this device use drawChar() or drawString()? ++ virtual GBool useDrawChar() { return gTrue; } ++ ++ // Does this device use beginType3Char/endType3Char? Otherwise, ++ // text in Type 3 fonts will be drawn with drawChar/drawString. ++ virtual GBool interpretType3Chars() { return gTrue; } ++ ++ //----- 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 GBool 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, GBool invert, ++ GBool interpolate, GBool inlineImg); ++ virtual void drawImage(GfxState *state, Object *ref, Stream *str, ++ int width, int height, GfxImageColorMap *colorMap, ++ GBool interpolate, int *maskColors, GBool 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); ++ ++ GBool isReverseVideo() { return gFalse; } ++ ++private: ++ QPainter *m_painter; ++ FontHinting m_fontHinting; ++ QFont m_currentFont; ++ QPen m_currentPen; ++ QBrush m_currentBrush; ++ GBool 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,181 @@ ++/* 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 ++ ++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()->getCString(); ++ 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() == gTrue; ++ 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 ); ++ 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()->getCString() ); ++ } ++ 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( AnnotColor *color ); ++AnnotColor* convertQColor( const QColor &color ); ++ ++} +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,5089 @@ ++/* 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 0; ++ ++ // build annotation of given type ++ Annotation * annotation = 0; ++ 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 ( 0 ), pdfPage ( 0 ), ++ parentDoc ( 0 ) ++{ ++} ++ ++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, gTrue ); ++ 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(); ++ AnnotCoord **ac = (AnnotCoord **) gmallocn(count, sizeof(AnnotCoord*)); ++ ++ double MTX[6]; ++ fillTransformationMTX(MTX); ++ ++ int pos = 0; ++ foreach (const QPointF &p, list) ++ { ++ double x, y; ++ XPDFReader::invTransform( MTX, p, x, y ); ++ ac[pos++] = new AnnotCoord(x, y); ++ } ++ ++ return new AnnotPath(ac, count); ++} ++ ++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 = 0; ++ 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()->getCString() ) ); ++ // -> 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()->getCString() ) ); ++ // -> 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 ++ GooString * movietitle = movieann->getTitle(); ++ if ( movietitle ) ++ m->setMovieTitle( QString::fromLatin1( movietitle->getCString() ) ); ++ 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 ++ 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 == 0) ++ { ++ 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 0; ++ ++ 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 = 0; ++ if ( pdfAnnot->getType() == Annot::typeScreen ) ++ linkAction = static_cast( pdfAnnot )->getAdditionalAction( actionType ); ++ else ++ linkAction = static_cast( pdfAnnot )->getAdditionalAction( actionType ); ++ ++ Link *link = 0; ++ ++ 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 != 0) ++ { ++ 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 == 0) ++ { ++ 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()->getCString() ); ++ 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()->getCString() ); ++ ++ 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 = 0; ++ } ++ 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() ); ++ ++ AnnotBorderArray * border = new AnnotBorderArray(); ++ border->setWidth( style.width() ); ++ border->setHorizontalCorner( style.xCorners() ); ++ border->setVerticalCorner( style.yCorners() ); ++ d->pdfAnnot->setBorder(border); ++} ++ ++Annotation::Popup Annotation::popup() const ++{ ++ Q_D( const Annotation ); ++ ++ if (!d->pdfAnnot) ++ return d->popup; ++ ++ Popup w; ++ AnnotPopup *popup = 0; ++ 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); ++ ++ // data fields ++ TextAnnotation::TextType textType; ++ QString textIcon; ++ QFont textFont; ++ int inplaceAlign; // 0:left, 1:center, 2:right ++ QVector inplaceCallout; ++ TextAnnotation::InplaceIntent inplaceIntent; ++ ++ // Helper ++ static GooString * toAppearanceString(const QFont &font); ++}; ++ ++TextAnnotationPrivate::TextAnnotationPrivate() ++ : AnnotationPrivate(), textType( TextAnnotation::Linked ), ++ textIcon( "Note" ), inplaceAlign( 0 ), ++ inplaceIntent( TextAnnotation::Unknown ) ++{ ++} ++ ++Annotation * TextAnnotationPrivate::makeAlias() ++{ ++ return new TextAnnotation(*this); ++} ++ ++GooString * TextAnnotationPrivate::toAppearanceString(const QFont &font) ++{ ++ GooString * s = GooString::format("/Invalid_font {0:d} Tf", font.pointSize()); ++ // TODO: Font family, style (bold, italic, ...) and pointSize as float ++ return s; ++} ++ ++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 ++ { ++ GooString * da = toAppearanceString(textFont); ++ pdfAnnot = new AnnotFreeText(destPage->getDoc(), &rect, da); ++ delete 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; ++} ++ ++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( "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() ); ++ ++ // 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()->getCString() ); ++ } ++ ++ 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) ++ { ++ const AnnotFreeText * ftextann = static_cast(d->pdfAnnot); ++ const GooString * da = ftextann->getAppearanceString(); ++ if (da) ++ { ++ // At the moment, only font size is parsed ++ QString style = QString::fromLatin1( da->getCString() ); ++ QRegExp rx("(\\d+)(\\.\\d*)? Tf"); ++ if (rx.indexIn(style) != -1) ++ font.setPointSize( rx.cap(1).toInt() ); ++ // TODO: Other properties ++ } ++ } ++ ++ return font; ++} ++ ++void TextAnnotation::setTextFont( const QFont &font ) ++{ ++ Q_D( TextAnnotation ); ++ ++ if (!d->pdfAnnot) ++ { ++ d->textFont = font; ++ return; ++ } ++ ++ if (d->pdfAnnot->getType() != Annot::typeFreeText) ++ return; ++ ++ AnnotFreeText * ftextann = static_cast(d->pdfAnnot); ++ GooString * da = TextAnnotationPrivate::toAppearanceString(font); ++ ftextann->setAppearanceString(da); ++ delete da; ++} ++ ++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; ++ } ++ ++ AnnotColor * c = convertQColor(color); ++ ++ if (d->pdfAnnot->getType() == Annot::typeLine) ++ { ++ AnnotLine *lineann = static_cast(d->pdfAnnot); ++ lineann->setInteriorColor(c); ++ } ++ else ++ { ++ AnnotPolygon *polyann = static_cast(d->pdfAnnot); ++ polyann->setInteriorColor(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(); ++ AnnotQuadrilaterals::AnnotQuadrilateral **ac = ++ (AnnotQuadrilaterals::AnnotQuadrilateral**) ++ gmallocn( count, sizeof(AnnotQuadrilaterals::AnnotQuadrilateral*) ); ++ ++ 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++] = new AnnotQuadrilaterals::AnnotQuadrilateral(x1, y1, x2, y2, x3, y3, x4, y4); ++ } ++ ++ return new AnnotQuadrilaterals(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()->getCString() ); ++} ++ ++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( 0 ), linkHLMode( LinkAnnotation::Invert ) ++{ ++} ++ ++LinkAnnotationPrivate::~LinkAnnotationPrivate() ++{ ++ delete linkDestination; ++} ++ ++Annotation * LinkAnnotationPrivate::makeAlias() ++{ ++ return new LinkAnnotation(*this); ++} ++ ++Annot* LinkAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) ++{ ++ return 0; // 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( 0 ) ++{ ++} ++ ++FileAttachmentAnnotationPrivate::~FileAttachmentAnnotationPrivate() ++{ ++ delete embfile; ++} ++ ++Annotation * FileAttachmentAnnotationPrivate::makeAlias() ++{ ++ return new FileAttachmentAnnotation(*this); ++} ++ ++Annot* FileAttachmentAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) ++{ ++ return 0; // 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( 0 ) ++{ ++} ++ ++SoundAnnotationPrivate::~SoundAnnotationPrivate() ++{ ++ delete sound; ++} ++ ++Annotation * SoundAnnotationPrivate::makeAlias() ++{ ++ return new SoundAnnotation(*this); ++} ++ ++Annot* SoundAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) ++{ ++ return 0; // 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( 0 ) ++{ ++} ++ ++MovieAnnotationPrivate::~MovieAnnotationPrivate() ++{ ++ delete movie; ++} ++ ++Annotation * MovieAnnotationPrivate::makeAlias() ++{ ++ return new MovieAnnotation(*this); ++} ++ ++Annot* MovieAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) ++{ ++ return 0; // 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( 0 ) ++{ ++} ++ ++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 0; // 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 0; // 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( 0 ) ++ { ++ } ++ ++ ~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( 0 ) ++ { ++ } ++ ++ ~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( 0 ), deactivation( 0 ) ++ { ++ } ++ ++ 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( 0 ), content( 0 ) ++ { ++ } ++ ++ ~RichMediaAnnotationPrivate() ++ { ++ delete settings; ++ delete content; ++ } ++ ++ Annotation * makeAlias() ++ { ++ return new RichMediaAnnotation( *this ); ++ } ++ ++ Annot* createNativeAnnot( ::Page *destPage, DocumentData *doc ) ++ { ++ Q_UNUSED( destPage ); ++ Q_UNUSED( doc ); ++ ++ return 0; ++ } ++ ++ 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( 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; ++} ++ ++AnnotColor* convertQColor( const QColor &c ) ++{ ++ if (!c.isValid() || c.alpha() == 0) ++ return new AnnotColor(); // Transparent ++ else ++ return new AnnotColor(c.redF(), c.greenF(), c.blueF()); ++} ++//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,1375 @@ ++/* 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 ); ++ ++ 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.num, fi.m_data->embRef.gen); ++ 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; ++ ++ GooList * items = outline->getItems(); ++ if ( !items || items->getLength() < 1 ) ++ return NULL; ++ ++ QDomDocument *toc = new QDomDocument(); ++ if ( items->getLength() > 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.getCString(); ++ if (updateId) ++ *updateId = gooUpdateId.getCString(); ++ ++ 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->getCString()) : QDateTime(); ++} ++ ++QDateTime EmbeddedFile::createDate() const ++{ ++ GooString *goo = m_embeddedFile->embFile() ? m_embeddedFile->embFile()->createDate() : NULL; ++ return goo ? convertDate(goo->getCString()) : QDateTime(); ++} ++ ++QByteArray EmbeddedFile::checksum() const ++{ ++ GooString *goo = m_embeddedFile->embFile() ? m_embeddedFile->embFile()->checksum() : NULL; ++ return goo ? QByteArray::fromRawData(goo->getCString(), goo->getLength()) : QByteArray(); ++} ++ ++QString EmbeddedFile::mimeType() const ++{ ++ GooString *goo = m_embeddedFile->embFile() ? m_embeddedFile->embFile()->mimeType() : NULL; ++ return goo ? QString(goo->getCString()) : 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; ++ GooList *items = d->fontInfoScanner.scan( 1 ); ++ if ( !items ) ++ return fonts; ++ fonts.reserve( items->getLength() ); ++ for ( int i = 0; i < items->getLength(); ++i ) { ++ fonts.append( FontInfo( FontInfoData( ( ::FontInfo* )items->get( i ) ) ) ); ++ } ++ deleteGooList( items, ::FontInfo ); ++ 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, gTrue ); ++ 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 (GooString *goo = m_formData->fm->getPartialName()) ++ { ++ name = QString::fromLatin1(goo->getCString()); ++ } ++ return name; ++} ++ ++QString FormField::fullyQualifiedName() const ++{ ++ QString name; ++ if (GooString *goo = m_formData->fm->getFullyQualifiedName()) ++ { ++ name = UnicodeParsedString(goo); ++ } ++ return name; ++} ++ ++QString FormField::uiName() const ++{ ++ QString name; ++ if (GooString *goo = m_formData->fm->getAlternateUiName()) ++ { ++ name = QString::fromLatin1(goo->getCString()); ++ } ++ 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((GBool)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 ++{ ++ 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 GBool upsideDown() { return gFalse; } ++ virtual GBool useDrawChar() { return gFalse; } ++ virtual GBool interpretType3Chars() { return gFalse; } ++ 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(), gTrue); ++ 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,710 @@ ++/* 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" ++ ++static bool operator==( const Ref &r1, const Ref &r2 ) ++{ ++ return r1.num == r2.num && r1.gen == r2.gen; ++} ++ ++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; ++ 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->getCString() ); ++ } ++ ++ 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.num, ref.gen ); ++ } ++ 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; ++ Guchar 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 ); ++ 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->getCString() ) : 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() == gTrue); ++} ++ ++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,456 @@ ++/* 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) { ++ 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(); ++ GooList *ocgs = optContent->getOCGs(); ++ ++ for (int i = 0; i < ocgs->getLength(); ++i) { ++ OptionalContentGroup *ocg = static_cast(ocgs->get(i)); ++ OptContentItem *node = new OptContentItem( ocg ); ++ m_optContentItems.insert( QString::number(ocg->getRef().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() ) { ++ 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() ) { ++ 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; ++ ++ GooList *statesList = popplerLinkOCGState->getStateList(); ++ for (int i = 0; i < statesList->getLength(); ++i) { ++ ::LinkOCGState::StateList *stateList = (::LinkOCGState::StateList*)statesList->get(i); ++ ++ GooList *refsList = stateList->list; ++ for (int j = 0; j < refsList->getLength(); ++j) { ++ Ref *ref = (Ref *)refsList->get(j); ++ 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); ++ GBool performSingleTextSearch(TextPage* textPage, QVector &u, double &sLeft, double &sTop, double &sRight, double &sBottom, Page::SearchDirection direction, GBool sCase, GBool sWords); ++ QList performMultipleTextSearch(TextPage* textPage, QVector &u, GBool sCase, GBool 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; ++ GooString * p = e->getParams(); ++ popplerLink = new LinkExecute( linkArea, e->getFileName()->getCString(), p ? p->getCString() : 0 ); ++ } ++ break; ++ ++ case actionNamed: ++ { ++ const char * name = ((LinkNamed *)a)->getName()->getCString(); ++ 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()->getCString() ); ++ } ++ 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, gTrue, 0, gFalse, gFalse); ++ parentDoc->doc->displayPage( &td, index + 1, 72, 72, rotation, false, true, false, ++ NULL, NULL, NULL, NULL, gTrue); ++ TextPage *textPage=td.takeText(); ++ ++ return textPage; ++} ++ ++inline GBool PageData::performSingleTextSearch(TextPage* textPage, QVector &u, double &sLeft, double &sTop, double &sRight, double &sBottom, Page::SearchDirection direction, GBool sCase, GBool sWords) ++{ ++ if (direction == Page::FromTop) ++ return textPage->findText( u.data(), u.size(), ++ gTrue, gTrue, gFalse, gFalse, sCase, gFalse, sWords, &sLeft, &sTop, &sRight, &sBottom ); ++ else if ( direction == Page::NextResult ) ++ return textPage->findText( u.data(), u.size(), ++ gFalse, gTrue, gTrue, gFalse, sCase, gFalse, sWords, &sLeft, &sTop, &sRight, &sBottom ); ++ else if ( direction == Page::PreviousResult ) ++ return textPage->findText( u.data(), u.size(), ++ gFalse, gTrue, gTrue, gFalse, sCase, gTrue, sWords, &sLeft, &sTop, &sRight, &sBottom ); ++ ++ return gFalse; ++} ++ ++inline QList PageData::performMultipleTextSearch(TextPage* textPage, QVector &u, GBool sCase, GBool sWords) ++{ ++ QList results; ++ double sLeft = 0.0, sTop = 0.0, sRight = 0.0, sBottom = 0.0; ++ ++ while(textPage->findText( u.data(), u.size(), ++ gFalse, gTrue, gTrue, gFalse, sCase, gFalse, 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; ++ GBool overprintPreview = gFalse; ++#ifdef SPLASH_CMYK ++ overprintPreview = m_page->parentDoc->m_hints & Document::OverprintPreview ? gTrue : gFalse; ++ 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, ++ gFalse, ++ ignorePaperColor ? NULL : bgColor, ++ gTrue, ++ thinLineMode, ++ overprintPreview); ++ ++ splash_output.setFontAntialias(m_page->parentDoc->m_hints & Document::TextAntialiasing ? gTrue : gFalse); ++ splash_output.setVectorAntialias(m_page->parentDoc->m_hints & Document::Antialiasing ? gTrue : gFalse); ++ splash_output.setFreeTypeHinting(m_page->parentDoc->m_hints & Document::TextHinting ? gTrue : gFalse, ++ m_page->parentDoc->m_hints & Document::TextSlightHinting ? gTrue : gFalse); ++ ++ 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, gTrue); ++ ++ 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; ++ GBool 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; ++ PDFRectangle *rect; ++ QString result; ++ ++ const GBool rawOrder = textLayout == RawOrderLayout; ++ output_dev = new TextOutputDev(0, gFalse, 0, rawOrder, gFalse); ++ 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, gTrue); ++ 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->getCString()); ++ ++ 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 GBool sCase = caseSensitive == Page::CaseSensitive ? gTrue : gFalse; ++ ++ QVector u; ++ TextPage *textPage = m_page->prepareTextSearch(text, rotate, &u); ++ ++ const bool found = m_page->performSingleTextSearch(textPage, u, sLeft, sTop, sRight, sBottom, direction, sCase, gFalse); ++ ++ 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 GBool sCase = flags.testFlag(IgnoreCase) ? gFalse : gTrue; ++ const GBool sWords = flags.testFlag(WholeWords) ? gTrue : gFalse; ++ ++ 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 GBool sCase = caseSensitive == Page::CaseSensitive ? gTrue : gFalse; ++ ++ QVector u; ++ TextPage *textPage = m_page->prepareTextSearch(text, rotate, &u); ++ ++ const QList results = m_page->performMultipleTextSearch(textPage, u, sCase, gFalse); ++ ++ textPage->decRefCnt(); ++ ++ return results; ++} ++ ++QList Page::search(const QString &text, SearchFlags flags, Rotation rotate) const ++{ ++ const GBool sCase = flags.testFlag(IgnoreCase) ? gFalse : gTrue; ++ const GBool sWords = flags.testFlag(WholeWords) ? gTrue : gFalse; ++ ++ 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, gFalse, 0, gFalse, gFalse); ++ ++ 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, gTrue); ++ ++ 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->getCString()); ++ 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() == gTrue; ++ 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, gFalse, 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, 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(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.getCString(), convertedStr.getLength()); ++ } ++ ++ QString UnicodeParsedString(GooString *s1) { ++ if ( !s1 || s1->getLength() == 0 ) ++ return QString(); ++ ++ char *cString; ++ int stringLength; ++ bool deleteCString; ++ if ( ( s1->getChar(0) & 0xff ) == 0xfe && ( s1->getLength() > 1 && ( s1->getChar(1) & 0xff ) == 0xff ) ) ++ { ++ cString = s1->getCString(); ++ 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( ::LinkAction * a, DocumentData * doc, QDomElement * e ) ++ { ++ if ( !a || !e ) ++ return; ++ ++ switch ( a->getKind() ) ++ { ++ case actionGoTo: ++ { ++ // page number is contained/referenced in a LinkGoTo ++ LinkGoTo * g = static_cast< LinkGoTo * >( a ); ++ 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 ++ GooString *s = g->getNamedDest(); ++ QChar *charArray = new QChar[s->getLength()]; ++ for (int i = 0; i < s->getLength(); ++i) charArray[i] = QChar(s->getCString()[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 ++ LinkGoToR * g = static_cast< LinkGoToR * >( a ); ++ 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 ++ GooString *s = g->getNamedDest(); ++ QChar *charArray = new QChar[s->getLength()]; ++ for (int i = 0; i < s->getLength(); ++i) charArray[i] = QChar(s->getCString()[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()->getCString() ); ++ break; ++ } ++ case actionURI: ++ { ++ LinkURI * u = static_cast< LinkURI * >( a ); ++ e->setAttribute( "DestinationURI", u->getURI()->getCString() ); ++ } ++ default: ; ++ } ++ } ++ ++ DocumentData::~DocumentData() ++ { ++ qDeleteAll(m_embeddedFiles); ++ delete (OptContentModel *)m_optContentModel; ++ delete doc; ++ delete m_fontInfoIterator; ++ ++ count --; ++ if ( count == 0 ) ++ { ++ utf8Map = 0; ++ delete globalParams; ++ } ++ } ++ ++ void DocumentData::init() ++ { ++ m_fontInfoIterator = 0; ++ m_backend = Document::SplashBackend; ++ paperColor = Qt::white; ++ m_hints = 0; ++ m_optContentModel = 0; ++ ++ if ( count == 0 ) ++ { ++ utf8Map = 0; ++ globalParams = new GlobalParams(); ++ setErrorCallback(qt4ErrorFunction, NULL); ++ } ++ count ++; ++ } ++ ++ ++ void DocumentData::addTocChildren( QDomDocument * docSyn, QDomNode * parent, GooList * items ) ++ { ++ int numItems = items->getLength(); ++ for ( int i = 0; i < numItems; ++i ) ++ { ++ // iterate over every object in 'items' ++ OutlineItem * outlineItem = (OutlineItem *)items->get( i ); ++ ++ // 1. create element using outlineItem's title as tagName ++ QString name; ++ 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 ++ ::LinkAction * a = outlineItem->getAction(); ++ linkActionToTocItem( a, this, &item ); ++ ++ item.setAttribute( "Open", QVariant( (bool)outlineItem->isOpen() ).toString() ); ++ ++ // 3. recursively descend over children ++ outlineItem->open(); ++ GooList * 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,241 @@ ++/* 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 ++#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, GooList * 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()->getCString(); ++ if (fi->getFile()) fontFile = fi->getFile()->getCString(); ++ 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 GBool annotDisplayDecideCbk(Annot *annot, void *user_data) ++{ ++ if (annot->getType() == Annot::typeWidget) ++ return gTrue; // Never hide forms ++ else ++ return *(GBool*)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, ++ gFalse, ++ gFalse, ++ 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()) ++ { ++ GBool isPrinting = (d->opts & Printing) ? gTrue : gFalse; ++ GBool showAnnotations = (d->opts & HideAnnotations) ? gFalse : gTrue; ++ foreach(int page, d->pageList) ++ { ++ d->document->doc->displayPage(psOut, ++ page, ++ d->hDPI, ++ d->vDPI, ++ d->rotate, ++ gFalse, ++ gTrue, ++ isPrinting, ++ NULL, ++ NULL, ++ annotDisplayDecideCbk, ++ &showAnnotations, gTrue); ++ 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->getCString() ) : 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.getCString(), string.constData()); ++ goo.insert(position, addition.constData()); ++ QCOMPARE(goo.getCString(), result.constData()); ++} ++ ++void TestGooString::testInsert() ++{ ++ { ++ GooString goo; ++ goo.insert(0, "."); ++ goo.insert(0, "This is a very long long test string"); ++ QCOMPARE(goo.getCString(), "This is a very long long test string."); ++ } ++ { ++ GooString goo; ++ goo.insert(0, "second-part-third-part"); ++ goo.insert(0, "first-part-"); ++ QCOMPARE(goo.getCString(), "first-part-second-part-third-part"); ++ } ++} ++ ++void TestGooString::testFormat() ++{ ++ { ++ const QScopedPointer goo(GooString::format("{0:d},{1:x}", 1, 0xF)); ++ QCOMPARE(goo->getCString(), "1,f"); ++ } ++ { ++ const QScopedPointer goo(GooString::format("{0:d},{0:x},{0:X},{0:o},{0:b},{0:w}", 0xA)); ++ QCOMPARE(goo->getCString(), "10,a,A,12,1010, "); ++ } ++ { ++ const QScopedPointer goo(GooString::format("{0:d},{0:x},{0:X},{0:o},{0:b}", -0xA)); ++ QCOMPARE(goo->getCString(), "-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->getCString(), "TEST"); ++ ++ const QScopedPointer goo2(GooString::format("{0:s} {1:t}", "TEST", goo.data())); ++ QCOMPARE(goo2->getCString(), "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->getCString(), 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->getCString(), 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->getCString(), 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->getCString(), "1.0 1 1 | 0.0 0 0.01"); ++ QCOMPARE(gooF->getCString(), "1.0 1 1 | 0.0 0 0.01"); ++ } ++ { ++ const QScopedPointer goo(GooString::format("{0:.4f} {0:.4g} {0:.4gs}", .012)); ++ QCOMPARE(goo->getCString(), "0.0120 0.012 0.012"); ++ } ++ { ++ const QScopedPointer goo(GooString::format("{{ SomeText {0:d} }}", 1)); ++ QCOMPARE(goo->getCString(), "{ SomeText 1 }"); ++ } ++ { ++ const QScopedPointer goo(GooString::format("{{{{ {{ SomeText {0:d}", 2)); ++ QCOMPARE(goo->getCString(), "{{ { SomeText 2"); ++ } ++ { ++ const QScopedPointer goo(GooString::format("SomeText {0:d} }} }}}}", 3)); ++ QCOMPARE(goo->getCString(), "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 = new GlobalParams(); ++ 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; ++ delete globalParams; ++} ++ ++void TestOptionalContent::checkVisibilitySetting() ++{ ++ globalParams = new GlobalParams(); ++ 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; ++ delete globalParams; ++} ++ ++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, gFalse); ++ QCOMPARE (str.getCString(), "clxxvii"); ++} ++ ++void TestPageLabelInfo::testFromRoman() ++{ ++ GooString roman("clxxvii"); ++ QCOMPARE(fromRoman(roman.getCString()), 177); ++} ++ ++void TestPageLabelInfo::testToLatin() ++{ ++ GooString str; ++ toLatin(54, &str, gFalse); ++ QCOMPARE(str.getCString(), "bbb"); ++} ++ ++void TestPageLabelInfo::testFromLatin() ++{ ++ GooString latin("ddd"); ++ QCOMPARE(fromLatin(latin.getCString()), 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 = new GlobalParams(); ++} ++ ++void TestStrings::cleanupTestCase() ++{ ++ qDeleteAll(m_gooStrings); ++ ++ delete globalParams; ++} ++ ++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->getCString() + 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->getCString(), result->getCString()); ++ ++ 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 + diff --git a/poppler.spec b/poppler.spec index 0373cf9..deabab6 100644 --- a/poppler.spec +++ b/poppler.spec @@ -3,8 +3,8 @@ Summary: PDF rendering library Name: poppler -Version: 0.61.1 -Release: 2%{?dist} +Version: 0.62.0 +Release: 1%{?dist} License: (GPLv2 or GPLv3) and GPLv2+ and LGPLv2+ and MIT URL: http://poppler.freedesktop.org/ Source0: http://poppler.freedesktop.org/poppler-%{version}.tar.xz @@ -14,6 +14,7 @@ Source1: %{name}-test-%{test_date}_%{test_sha}.tar.xz # https://bugzilla.redhat.com/show_bug.cgi?id=1185007 Patch0: poppler-0.30.0-rotated-words-selection.patch Patch1: 0001-fix-build-with-gtk-doc-1.27.patch +Patch2: 0001-Revert-Remove-the-Qt4-frontend.patch BuildRequires: cmake BuildRequires: gettext-devel @@ -193,7 +194,7 @@ test "$(pkg-config --modversion poppler-splash)" = "%{version}" %files %doc README %license COPYING -%{_libdir}/libpoppler.so.72* +%{_libdir}/libpoppler.so.73* %files devel %{_libdir}/pkgconfig/poppler.pc @@ -250,6 +251,9 @@ test "$(pkg-config --modversion poppler-splash)" = "%{version}" %{_mandir}/man1/* %changelog +* Wed Feb 14 2018 David Tardon - 0.62.0-1 +- new upstream release + * Fri Feb 09 2018 Fedora Release Engineering - 0.61.1-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild diff --git a/sources b/sources index b63e32e..4f12134 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (poppler-0.61.1.tar.xz) = 780ebf07ad757635f3f71c7b1f61ad0849526f99f0dc514c3290c4e8db7000a68dfe50c17253d4c086aec5c5390055102478eba96699088179822f3be5ce278d +SHA512 (poppler-0.62.0.tar.xz) = 91f7eae7b05965ae97a34e658bed2a676be8a6e4d34f82148ece6eb58932632dcf9a34d50c66412f93f560ce575abf5c608ed6b1e5184604b96024801886c706 SHA512 (poppler-test-2009-05-13_0d2bfd4af4c76a3bac27ccaff793d9129df7b57a.tar.xz) = f8ce114357043a893100de2d52ada8bd850148d19f0e8c889988ea97e9a92313f0545c0b88ef32a1ce7f0e9e58edc1a8c9066278c20b7718ca619913fd4bfb3c