Blob Blame History Raw
From 017d466e5d0895504fc8711f87806d3bd08d8416 Mon Sep 17 00:00:00 2001
From: David Tardon <dtardon@redhat.com>
Date: Thu, 8 Feb 2018 19:13:01 +0100
Subject: [PATCH] Revert "Remove the Qt4 frontend"

This reverts commit fb4c69d270a618bb23791e52f46ec73c86574294.
---
 .gitignore                                   |    2 +
 CMakeLists.txt                               |   17 +
 cmake/modules/FindQt4.cmake                  | 1311 +++++++
 poppler-qt4.pc.cmake                         |   12 +
 qt4/.gitignore                               |    4 +
 qt4/CMakeLists.txt                           |    6 +
 qt4/demos/.gitignore                         |    4 +
 qt4/demos/CMakeLists.txt                     |   28 +
 qt4/demos/abstractinfodock.cpp               |   57 +
 qt4/demos/abstractinfodock.h                 |   48 +
 qt4/demos/documentobserver.cpp               |   50 +
 qt4/demos/documentobserver.h                 |   50 +
 qt4/demos/embeddedfiles.cpp                  |   82 +
 qt4/demos/embeddedfiles.h                    |   44 +
 qt4/demos/fonts.cpp                          |   72 +
 qt4/demos/fonts.h                            |   43 +
 qt4/demos/info.cpp                           |   72 +
 qt4/demos/info.h                             |   43 +
 qt4/demos/main_viewer.cpp                    |   33 +
 qt4/demos/metadata.cpp                       |   50 +
 qt4/demos/metadata.h                         |   43 +
 qt4/demos/navigationtoolbar.cpp              |  144 +
 qt4/demos/navigationtoolbar.h                |   65 +
 qt4/demos/optcontent.cpp                     |   69 +
 qt4/demos/optcontent.h                       |   47 +
 qt4/demos/pageview.cpp                       |  101 +
 qt4/demos/pageview.h                         |   53 +
 qt4/demos/permissions.cpp                    |   66 +
 qt4/demos/permissions.h                      |   43 +
 qt4/demos/thumbnails.cpp                     |   84 +
 qt4/demos/thumbnails.h                       |   48 +
 qt4/demos/toc.cpp                            |   88 +
 qt4/demos/toc.h                              |   43 +
 qt4/demos/viewer.cpp                         |  319 ++
 qt4/demos/viewer.h                           |   73 +
 qt4/src/.gitignore                           |    9 +
 qt4/src/ArthurOutputDev.cc                   |  812 ++++
 qt4/src/ArthurOutputDev.h                    |  170 +
 qt4/src/CMakeLists.txt                       |   54 +
 qt4/src/Doxyfile                             | 1637 +++++++++
 qt4/src/Mainpage.dox                         |   85 +
 qt4/src/poppler-annotation-helper.h          |  181 +
 qt4/src/poppler-annotation-private.h         |  112 +
 qt4/src/poppler-annotation.cc                | 5089 ++++++++++++++++++++++++++
 qt4/src/poppler-annotation.h                 | 1375 +++++++
 qt4/src/poppler-base-converter.cc            |  105 +
 qt4/src/poppler-converter-private.h          |   49 +
 qt4/src/poppler-document.cc                  |  850 +++++
 qt4/src/poppler-embeddedfile-private.h       |   42 +
 qt4/src/poppler-embeddedfile.cc              |  135 +
 qt4/src/poppler-export.h                     |   20 +
 qt4/src/poppler-fontinfo.cc                  |  150 +
 qt4/src/poppler-form.cc                      |  416 +++
 qt4/src/poppler-form.h                       |  343 ++
 qt4/src/poppler-link-extractor-private.h     |   57 +
 qt4/src/poppler-link-extractor.cc            |   84 +
 qt4/src/poppler-link-private.h               |   57 +
 qt4/src/poppler-link.cc                      |  710 ++++
 qt4/src/poppler-link.h                       |  641 ++++
 qt4/src/poppler-media.cc                     |  168 +
 qt4/src/poppler-media.h                      |  100 +
 qt4/src/poppler-movie.cc                     |  110 +
 qt4/src/poppler-optcontent-private.h         |  124 +
 qt4/src/poppler-optcontent.cc                |  456 +++
 qt4/src/poppler-optcontent.h                 |   84 +
 qt4/src/poppler-page-private.h               |   57 +
 qt4/src/poppler-page-transition-private.h    |   28 +
 qt4/src/poppler-page-transition.cc           |  101 +
 qt4/src/poppler-page-transition.h            |  158 +
 qt4/src/poppler-page.cc                      |  810 ++++
 qt4/src/poppler-pdf-converter.cc             |  115 +
 qt4/src/poppler-private.cc                   |  296 ++
 qt4/src/poppler-private.h                    |  241 ++
 qt4/src/poppler-ps-converter.cc              |  280 ++
 qt4/src/poppler-qiodeviceoutstream-private.h |   47 +
 qt4/src/poppler-qiodeviceoutstream.cc        |   64 +
 qt4/src/poppler-qt4.h                        | 1990 ++++++++++
 qt4/src/poppler-sound.cc                     |  132 +
 qt4/src/poppler-textbox.cc                   |   63 +
 qt4/tests/.gitignore                         |   33 +
 qt4/tests/CMakeLists.txt                     |   67 +
 qt4/tests/README.unittest                    |   23 +
 qt4/tests/check_actualtext.cpp               |   33 +
 qt4/tests/check_attachments.cpp              |  157 +
 qt4/tests/check_dateConversion.cpp           |  142 +
 qt4/tests/check_fonts.cpp                    |  248 ++
 qt4/tests/check_goostring.cpp                |  127 +
 qt4/tests/check_lexer.cpp                    |  107 +
 qt4/tests/check_links.cpp                    |   98 +
 qt4/tests/check_metadata.cpp                 |  275 ++
 qt4/tests/check_optcontent.cpp               |  446 +++
 qt4/tests/check_pagelabelinfo.cpp            |   43 +
 qt4/tests/check_pagelayout.cpp               |   49 +
 qt4/tests/check_pagemode.cpp                 |   73 +
 qt4/tests/check_password.cpp                 |   88 +
 qt4/tests/check_permissions.cpp              |   44 +
 qt4/tests/check_search.cpp                   |  175 +
 qt4/tests/check_strings.cpp                  |  250 ++
 qt4/tests/poppler-attachments.cpp            |   39 +
 qt4/tests/poppler-fonts.cpp                  |   89 +
 qt4/tests/poppler-forms.cpp                  |  166 +
 qt4/tests/poppler-texts.cpp                  |   40 +
 qt4/tests/stress-poppler-dir.cpp             |   67 +
 qt4/tests/stress-poppler-qt4.cpp             |   74 +
 qt4/tests/stress-threads-qt4.cpp             |  309 ++
 qt4/tests/test-password-qt4.cpp              |  136 +
 qt4/tests/test-poppler-qt4.cpp               |  235 ++
 qt4/tests/test-render-to-file.cpp            |   69 +
 108 files changed, 25623 insertions(+)
 create mode 100644 cmake/modules/FindQt4.cmake
 create mode 100644 poppler-qt4.pc.cmake
 create mode 100644 qt4/.gitignore
 create mode 100644 qt4/CMakeLists.txt
 create mode 100644 qt4/demos/.gitignore
 create mode 100644 qt4/demos/CMakeLists.txt
 create mode 100644 qt4/demos/abstractinfodock.cpp
 create mode 100644 qt4/demos/abstractinfodock.h
 create mode 100644 qt4/demos/documentobserver.cpp
 create mode 100644 qt4/demos/documentobserver.h
 create mode 100644 qt4/demos/embeddedfiles.cpp
 create mode 100644 qt4/demos/embeddedfiles.h
 create mode 100644 qt4/demos/fonts.cpp
 create mode 100644 qt4/demos/fonts.h
 create mode 100644 qt4/demos/info.cpp
 create mode 100644 qt4/demos/info.h
 create mode 100644 qt4/demos/main_viewer.cpp
 create mode 100644 qt4/demos/metadata.cpp
 create mode 100644 qt4/demos/metadata.h
 create mode 100644 qt4/demos/navigationtoolbar.cpp
 create mode 100644 qt4/demos/navigationtoolbar.h
 create mode 100644 qt4/demos/optcontent.cpp
 create mode 100644 qt4/demos/optcontent.h
 create mode 100644 qt4/demos/pageview.cpp
 create mode 100644 qt4/demos/pageview.h
 create mode 100644 qt4/demos/permissions.cpp
 create mode 100644 qt4/demos/permissions.h
 create mode 100644 qt4/demos/thumbnails.cpp
 create mode 100644 qt4/demos/thumbnails.h
 create mode 100644 qt4/demos/toc.cpp
 create mode 100644 qt4/demos/toc.h
 create mode 100644 qt4/demos/viewer.cpp
 create mode 100644 qt4/demos/viewer.h
 create mode 100644 qt4/src/.gitignore
 create mode 100644 qt4/src/ArthurOutputDev.cc
 create mode 100644 qt4/src/ArthurOutputDev.h
 create mode 100644 qt4/src/CMakeLists.txt
 create mode 100644 qt4/src/Doxyfile
 create mode 100644 qt4/src/Mainpage.dox
 create mode 100644 qt4/src/poppler-annotation-helper.h
 create mode 100644 qt4/src/poppler-annotation-private.h
 create mode 100644 qt4/src/poppler-annotation.cc
 create mode 100644 qt4/src/poppler-annotation.h
 create mode 100644 qt4/src/poppler-base-converter.cc
 create mode 100644 qt4/src/poppler-converter-private.h
 create mode 100644 qt4/src/poppler-document.cc
 create mode 100644 qt4/src/poppler-embeddedfile-private.h
 create mode 100644 qt4/src/poppler-embeddedfile.cc
 create mode 100644 qt4/src/poppler-export.h
 create mode 100644 qt4/src/poppler-fontinfo.cc
 create mode 100644 qt4/src/poppler-form.cc
 create mode 100644 qt4/src/poppler-form.h
 create mode 100644 qt4/src/poppler-link-extractor-private.h
 create mode 100644 qt4/src/poppler-link-extractor.cc
 create mode 100644 qt4/src/poppler-link-private.h
 create mode 100644 qt4/src/poppler-link.cc
 create mode 100644 qt4/src/poppler-link.h
 create mode 100644 qt4/src/poppler-media.cc
 create mode 100644 qt4/src/poppler-media.h
 create mode 100644 qt4/src/poppler-movie.cc
 create mode 100644 qt4/src/poppler-optcontent-private.h
 create mode 100644 qt4/src/poppler-optcontent.cc
 create mode 100644 qt4/src/poppler-optcontent.h
 create mode 100644 qt4/src/poppler-page-private.h
 create mode 100644 qt4/src/poppler-page-transition-private.h
 create mode 100644 qt4/src/poppler-page-transition.cc
 create mode 100644 qt4/src/poppler-page-transition.h
 create mode 100644 qt4/src/poppler-page.cc
 create mode 100644 qt4/src/poppler-pdf-converter.cc
 create mode 100644 qt4/src/poppler-private.cc
 create mode 100644 qt4/src/poppler-private.h
 create mode 100644 qt4/src/poppler-ps-converter.cc
 create mode 100644 qt4/src/poppler-qiodeviceoutstream-private.h
 create mode 100644 qt4/src/poppler-qiodeviceoutstream.cc
 create mode 100644 qt4/src/poppler-qt4.h
 create mode 100644 qt4/src/poppler-sound.cc
 create mode 100644 qt4/src/poppler-textbox.cc
 create mode 100644 qt4/tests/.gitignore
 create mode 100644 qt4/tests/CMakeLists.txt
 create mode 100644 qt4/tests/README.unittest
 create mode 100644 qt4/tests/check_actualtext.cpp
 create mode 100644 qt4/tests/check_attachments.cpp
 create mode 100644 qt4/tests/check_dateConversion.cpp
 create mode 100644 qt4/tests/check_fonts.cpp
 create mode 100644 qt4/tests/check_goostring.cpp
 create mode 100644 qt4/tests/check_lexer.cpp
 create mode 100644 qt4/tests/check_links.cpp
 create mode 100644 qt4/tests/check_metadata.cpp
 create mode 100644 qt4/tests/check_optcontent.cpp
 create mode 100644 qt4/tests/check_pagelabelinfo.cpp
 create mode 100644 qt4/tests/check_pagelayout.cpp
 create mode 100644 qt4/tests/check_pagemode.cpp
 create mode 100644 qt4/tests/check_password.cpp
 create mode 100644 qt4/tests/check_permissions.cpp
 create mode 100644 qt4/tests/check_search.cpp
 create mode 100644 qt4/tests/check_strings.cpp
 create mode 100644 qt4/tests/poppler-attachments.cpp
 create mode 100644 qt4/tests/poppler-fonts.cpp
 create mode 100644 qt4/tests/poppler-forms.cpp
 create mode 100644 qt4/tests/poppler-texts.cpp
 create mode 100644 qt4/tests/stress-poppler-dir.cpp
 create mode 100644 qt4/tests/stress-poppler-qt4.cpp
 create mode 100644 qt4/tests/stress-threads-qt4.cpp
 create mode 100644 qt4/tests/test-password-qt4.cpp
 create mode 100644 qt4/tests/test-poppler-qt4.cpp
 create mode 100644 qt4/tests/test-render-to-file.cpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5e3d6a17..431059fb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -40,6 +40,7 @@ set (CMAKE_CXX_EXTENSIONS OFF)
 # command line switches
 option(ENABLE_UNSTABLE_API_ABI_HEADERS "Install API/ABI unstable xpdf headers." OFF)
 option(BUILD_GTK_TESTS "Whether 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_GLIB "Compile poppler glib wrapper." ON)
 option(ENABLE_GOBJECT_INTROSPECTION "Whether to generate GObject introspection." ON)
 option(ENABLE_GTK_DOC "Whether to generate glib API documentation." OFF)
+option(ENABLE_QT4 "Compile poppler qt4 wrapper." ON)
 option(ENABLE_QT5 "Compile poppler qt5 wrapper." ON)
 set(ENABLE_LIBOPENJPEG "openjpeg2" CACHE STRING "Use libopenjpeg for JPX streams. Possible values: openjpeg2, unmaintained, none. 'unmaintained' gives you the internal unmaintained decoder. Use at your own risk. 'none' compiles no JPX decoder at all. Default: openjpeg2")
 set(ENABLE_CMS "lcms2" CACHE STRING "Use color management system. Possible values: lcms2, none. 'none' disables color management system.")
@@ -112,6 +114,7 @@ set(OPI_SUPPORT ON)
 set(TEXTOUT_WORD_LIST ON)
 
 # setting the minimum required versions for some components
+set(QT4_MIN_VERSION "4.7.0")
 set(CAIRO_VERSION "1.10.0")
 set(GLIB_REQUIRED "2.41")
 
@@ -141,6 +144,13 @@ else()
   message(FATAL_ERROR "Invalid ENABLE_DCTDECODER value.")
 endif()
 
+if (ENABLE_QT4)
+  macro_optional_find_package(Qt4)
+  if (NOT QT4_FOUND)
+    set(ENABLE_QT4 OFF)
+  endif()
+endif()
+
 if (ENABLE_QT5)
   find_package(Qt5Core)
   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
+#        <basename>adaptor.{cpp,h} where basename defaults to the basename of the xml file.
+#        If <classname> 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 "<file[^<]+" _RC_FILES "${_RC_FILE_CONTENTS}")
+      SET(_RC_DEPENDS)
+      FOREACH(_RC_FILE ${_RC_FILES})
+        STRING(REGEX REPLACE "^<file[^>]*>" "" _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 <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtGui/QDockWidget>
+
+#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 <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <poppler-qt4.h>
+
+#include <QtGui/QTableWidget>
+
+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<Poppler::EmbeddedFile*> 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 <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <poppler-qt4.h>
+
+#include <QtGui/QTableWidget>
+
+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<Poppler::FontInfo> 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 <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <poppler-qt4.h>
+
+#include <QtGui/QTableWidget>
+
+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 <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtGui/QApplication>
+
+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 <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <poppler-qt4.h>
+
+#include <QtGui/QTextEdit>
+
+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 <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <pino@kde.org>
+ * Copyright (C) 2013, Fabio D'Urso <fabiodurso@hotmail.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <poppler-qt4.h>
+
+#include <QtGui/QAction>
+#include <QtGui/QComboBox>
+
+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 <pino@kde.org>
+ * Copyright (C) 2013, Fabio D'Urso <fabiodurso@hotmail.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtGui/QToolBar>
+
+#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 <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <poppler-qt4.h>
+
+#include <QtGui/QTreeView>
+
+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 <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <pino@kde.org>
+ * Copyright (C) 2013, Fabio D'Urso <fabiodurso@hotmail.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <poppler-qt4.h>
+
+#include <QtGui/QApplication>
+#include <QtGui/QDesktopWidget>
+#include <QtGui/QImage>
+#include <QtGui/QLabel>
+#include <QtGui/QPixmap>
+
+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 <pino@kde.org>
+ * Copyright (C) 2013, Fabio D'Urso <fabiodurso@hotmail.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtGui/QScrollArea>
+
+#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 <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <poppler-qt4.h>
+
+#include <QtGui/QListWidget>
+
+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 <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <shawn.t.rutledge@gmail.com>
+ * Copyright (C) 2009, Pino Toscano <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <poppler-qt4.h>
+
+#include <QtGui/QListWidget>
+
+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 <shawn.t.rutledge@gmail.com>
+ * Copyright (C) 2009, Pino Toscano <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <poppler-qt4.h>
+
+#include <QtGui/QHeaderView>
+#include <QtGui/QTreeWidget>
+
+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 <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <pino@kde.org>
+ * Copyright (C) 2008, Albert Astals Cid <aacid@kde.org>
+ * Copyright (C) 2009, Shawn Rutledge <shawn.t.rutledge@gmail.com>
+ * Copyright (C) 2013, Fabio D'Urso <fabiodurso@hotmail.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <poppler-qt4.h>
+
+#include <QtCore/QDir>
+#include <QtGui/QAction>
+#include <QtGui/QApplication>
+#include <QtGui/QFileDialog>
+#include <QtGui/QInputDialog>
+#include <QtGui/QMenu>
+#include <QtGui/QMenuBar>
+#include <QtGui/QMessageBox>
+
+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 <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtGui/QMainWindow>
+
+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<DocumentObserver *> m_observers;
+
+    Poppler::Document *m_doc;
+};
+
+#endif
diff --git a/qt4/src/.gitignore b/qt4/src/.gitignore
new file mode 100644
index 00000000..3d124ddd
--- /dev/null
+++ b/qt4/src/.gitignore
@@ -0,0 +1,9 @@
+.deps
+.libs
+*.la
+*.lo
+Makefile
+Makefile.in
+APIDOCS-html
+APIDOCS-latex
+*.moc
diff --git a/qt4/src/ArthurOutputDev.cc b/qt4/src/ArthurOutputDev.cc
new file mode 100644
index 00000000..f2fa6f17
--- /dev/null
+++ b/qt4/src/ArthurOutputDev.cc
@@ -0,0 +1,817 @@
+//========================================================================
+//
+// ArthurOutputDev.cc
+//
+// Copyright 2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+//========================================================================
+//
+// Modified under the Poppler project - http://poppler.freedesktop.org
+//
+// All changes made under the Poppler project to this file are licensed
+// under GPL version 2 or later
+//
+// Copyright (C) 2005 Brad Hards <bradh@frogmouth.net>
+// Copyright (C) 2005-2009, 2011, 2012, 2014, 2015 Albert Astals Cid <aacid@kde.org>
+// Copyright (C) 2008, 2010 Pino Toscano <pino@kde.org>
+// Copyright (C) 2009, 2011 Carlos Garcia Campos <carlosgc@gnome.org>
+// Copyright (C) 2009 Petr Gajdos <pgajdos@novell.com>
+// Copyright (C) 2010 Matthias Fauconneau <matthias.fauconneau@gmail.com>
+// Copyright (C) 2011 Andreas Hartmetz <ahartmetz@gmail.com>
+// Copyright (C) 2013 Thomas Freitag <Thomas.Freitag@alfa.de>
+// Copyright (C) 2013 Dominik Haumann <dhaumann@kde.org>
+// Copyright (C) 2017 Adrian Johnson <ajohnson@redneon.com>
+//
+// To see a description of the changes please see the Changelog file that
+// came with your tarball or type make ChangeLog if you are building from git
+//
+//========================================================================
+
+#include <config.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <string.h>
+#include <math.h>
+
+#include "goo/gfile.h"
+#include "GlobalParams.h"
+#include "Error.h"
+#include "Object.h"
+#include "GfxState.h"
+#include "GfxFont.h"
+#include "Link.h"
+#include "FontEncodingTables.h"
+#include <fofi/FoFiTrueType.h>
+#include "ArthurOutputDev.h"
+
+#include <QtCore/QtDebug>
+#include <QtGui/QPainterPath>
+//------------------------------------------------------------------------
+
+#ifdef HAVE_SPLASH
+#include "splash/SplashFontFileID.h"
+#include "splash/SplashFontFile.h"
+#include "splash/SplashFontEngine.h"
+#include "splash/SplashFont.h"
+#include "splash/SplashMath.h"
+#include "splash/SplashPath.h"
+#include "splash/SplashGlyphBitmap.h"
+//------------------------------------------------------------------------
+// SplashOutFontFileID
+//------------------------------------------------------------------------
+
+class SplashOutFontFileID: public SplashFontFileID {
+public:
+
+  SplashOutFontFileID(const Ref *rA) { r = *rA; }
+
+  ~SplashOutFontFileID() {}
+
+  bool matches(SplashFontFileID *id) {
+    return ((SplashOutFontFileID *)id)->r.num == r.num &&
+           ((SplashOutFontFileID *)id)->r.gen == r.gen;
+  }
+
+private:
+
+  Ref r;
+};
+
+#endif
+
+//------------------------------------------------------------------------
+// ArthurOutputDev
+//------------------------------------------------------------------------
+
+ArthurOutputDev::ArthurOutputDev(QPainter *painter):
+  m_painter(painter),
+  m_fontHinting(NoHinting)
+{
+  m_currentBrush = QBrush(Qt::SolidPattern);
+  m_fontEngine = 0;
+  m_font = 0;
+}
+
+ArthurOutputDev::~ArthurOutputDev()
+{
+#ifdef HAVE_SPLASH
+  delete m_fontEngine;
+#endif
+}
+
+void ArthurOutputDev::startDoc(XRef *xrefA) {
+  xref = xrefA;
+#ifdef HAVE_SPLASH
+  delete m_fontEngine;
+
+  const bool isHintingEnabled = m_fontHinting != NoHinting;
+  const bool isSlightHinting = m_fontHinting == SlightHinting;
+
+  m_fontEngine = new SplashFontEngine(
+  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<int>(state->getPageWidth());
+  int h = static_cast<int>(state->getPageHeight());
+  QColor fillColour(Qt::white);
+  QBrush fill(fillColour);
+  m_painter->save();
+  m_painter->setPen(fillColour);
+  m_painter->setBrush(fill);
+  m_painter->drawRect(0, 0, w, h);
+  m_painter->restore();
+}
+
+void ArthurOutputDev::endPage() {
+}
+
+void ArthurOutputDev::saveState(GfxState *state)
+{
+  m_painter->save();
+}
+
+void ArthurOutputDev::restoreState(GfxState *state)
+{
+  m_painter->restore();
+}
+
+void ArthurOutputDev::updateAll(GfxState *state)
+{
+  OutputDev::updateAll(state);
+  m_needFontUpdate = true;
+}
+
+// This looks wrong - why aren't adjusting the matrix?
+void ArthurOutputDev::updateCTM(GfxState *state, double m11, double m12,
+				double m21, double m22,
+				double m31, double m32)
+{
+  updateLineDash(state);
+  updateLineJoin(state);
+  updateLineCap(state);
+  updateLineWidth(state);
+}
+
+void ArthurOutputDev::updateLineDash(GfxState *state)
+{
+  double *dashPattern;
+  int dashLength;
+  double dashStart;
+  state->getLineDash(&dashPattern, &dashLength, &dashStart);
+  QVector<qreal> pattern(dashLength);
+  for (int i = 0; i < dashLength; ++i) {
+    pattern[i] = dashPattern[i];
+  }
+  m_currentPen.setDashPattern(pattern);
+  m_currentPen.setDashOffset(dashStart);
+  m_painter->setPen(m_currentPen);
+}
+
+void ArthurOutputDev::updateFlatness(GfxState *state)
+{
+  // qDebug() << "updateFlatness";
+}
+
+void ArthurOutputDev::updateLineJoin(GfxState *state)
+{
+  switch (state->getLineJoin()) {
+  case 0:
+    m_currentPen.setJoinStyle(Qt::MiterJoin);
+    break;
+  case 1:
+    m_currentPen.setJoinStyle(Qt::RoundJoin);
+    break;
+  case 2:
+    m_currentPen.setJoinStyle(Qt::BevelJoin);
+    break;
+  }
+  m_painter->setPen(m_currentPen);
+}
+
+void ArthurOutputDev::updateLineCap(GfxState *state)
+{
+  switch (state->getLineCap()) {
+  case 0:
+    m_currentPen.setCapStyle(Qt::FlatCap);
+    break;
+  case 1:
+    m_currentPen.setCapStyle(Qt::RoundCap);
+    break;
+  case 2:
+    m_currentPen.setCapStyle(Qt::SquareCap);
+    break;
+  }
+  m_painter->setPen(m_currentPen);
+}
+
+void ArthurOutputDev::updateMiterLimit(GfxState *state)
+{
+  m_currentPen.setMiterLimit(state->getMiterLimit());
+  m_painter->setPen(m_currentPen);
+}
+
+void ArthurOutputDev::updateLineWidth(GfxState *state)
+{
+  m_currentPen.setWidthF(state->getLineWidth());
+  m_painter->setPen(m_currentPen);
+}
+
+void ArthurOutputDev::updateFillColor(GfxState *state)
+{
+  GfxRGB rgb;
+  QColor brushColour = m_currentBrush.color();
+  state->getFillRGB(&rgb);
+  brushColour.setRgbF(colToDbl(rgb.r), colToDbl(rgb.g), colToDbl(rgb.b), brushColour.alphaF());
+  m_currentBrush.setColor(brushColour);
+}
+
+void ArthurOutputDev::updateStrokeColor(GfxState *state)
+{
+  GfxRGB rgb;
+  QColor penColour = m_currentPen.color();
+  state->getStrokeRGB(&rgb);
+  penColour.setRgbF(colToDbl(rgb.r), colToDbl(rgb.g), colToDbl(rgb.b), penColour.alphaF());
+  m_currentPen.setColor(penColour);
+  m_painter->setPen(m_currentPen);
+}
+
+void ArthurOutputDev::updateFillOpacity(GfxState *state)
+{
+  QColor brushColour= m_currentBrush.color();
+  brushColour.setAlphaF(state->getFillOpacity());
+  m_currentBrush.setColor(brushColour);
+}
+
+void ArthurOutputDev::updateStrokeOpacity(GfxState *state)
+{
+  QColor penColour= m_currentPen.color();
+  penColour.setAlphaF(state->getStrokeOpacity());
+  m_currentPen.setColor(penColour);
+  m_painter->setPen(m_currentPen);
+}
+
+void ArthurOutputDev::updateFont(GfxState *state)
+{
+#ifdef HAVE_SPLASH
+  GfxFont *gfxFont;
+  GfxFontLoc *fontLoc;
+  GfxFontType fontType;
+  SplashOutFontFileID *id;
+  SplashFontFile *fontFile;
+  SplashFontSrc *fontsrc = NULL;
+  FoFiTrueType *ff;
+  Object refObj, strObj;
+  GooString *fileName;
+  char *tmpBuf;
+  int tmpBufLen = 0;
+  int *codeToGID;
+  const double *textMat;
+  double m11, m12, m21, m22, fontSize;
+  SplashCoord mat[4];
+  int n;
+  int faceIndex = 0;
+  SplashCoord matrix[6];
+
+  m_needFontUpdate = false;
+  m_font = NULL;
+  fileName = NULL;
+  tmpBuf = NULL;
+  fontLoc = NULL;
+
+  if (!(gfxFont = state->getFont())) {
+    goto err1;
+  }
+  fontType = gfxFont->getType();
+  if (fontType == fontType3) {
+    goto err1;
+  }
+
+  // check the font file cache
+  id = new SplashOutFontFileID(gfxFont->getID());
+  if ((fontFile = m_fontEngine->getFontFile(id))) {
+    delete id;
+
+  } else {
+
+    if (!(fontLoc = gfxFont->locateFont(xref, NULL))) {
+      error(errSyntaxError, -1, "Couldn't find a font for '{0:s}'",
+	    gfxFont->getName() ? gfxFont->getName()->c_str()
+	                       : "(unnamed)");
+      goto err2;
+    }
+
+    // embedded font
+    if (fontLoc->locType == gfxFontLocEmbedded) {
+      // if there is an embedded font, read it to memory
+      tmpBuf = gfxFont->readEmbFontFile(xref, &tmpBufLen);
+      if (! tmpBuf)
+	goto err2;
+
+    // external font
+    } else { // gfxFontLocExternal
+      fileName = fontLoc->path;
+      fontType = fontLoc->fontType;
+    }
+
+    fontsrc = new SplashFontSrc;
+    if (fileName)
+      fontsrc->setFile(fileName, false);
+    else
+      fontsrc->setBuf(tmpBuf, tmpBufLen, true);
+    
+    // load the font file
+    switch (fontType) {
+    case fontType1:
+      if (!(fontFile = m_fontEngine->loadType1Font(
+			   id,
+			   fontsrc,
+			   (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
+	error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
+	      gfxFont->getName() ? gfxFont->getName()->c_str()
+	                         : "(unnamed)");
+	goto err2;
+      }
+      break;
+    case fontType1C:
+      if (!(fontFile = m_fontEngine->loadType1CFont(
+			   id,
+			   fontsrc,
+			   (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
+	error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
+	      gfxFont->getName() ? gfxFont->getName()->c_str()
+	                         : "(unnamed)");
+	goto err2;
+      }
+      break;
+    case fontType1COT:
+      if (!(fontFile = m_fontEngine->loadOpenTypeT1CFont(
+			   id,
+			   fontsrc,
+			   (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
+	error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
+	      gfxFont->getName() ? gfxFont->getName()->c_str()
+	                         : "(unnamed)");
+	goto err2;
+      }
+      break;
+    case fontTrueType:
+    case fontTrueTypeOT:
+	if (fileName)
+	 ff = FoFiTrueType::load(fileName->c_str());
+	else
+	ff = FoFiTrueType::make(tmpBuf, tmpBufLen);
+      if (ff) {
+	codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);
+	n = 256;
+	delete ff;
+      } else {
+	codeToGID = NULL;
+	n = 0;
+      }
+      if (!(fontFile = m_fontEngine->loadTrueTypeFont(
+			   id,
+			   fontsrc,
+			   codeToGID, n))) {
+	error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
+	      gfxFont->getName() ? gfxFont->getName()->c_str()
+	                         : "(unnamed)");
+	goto err2;
+      }
+      break;
+    case fontCIDType0:
+    case fontCIDType0C:
+      if (!(fontFile = m_fontEngine->loadCIDFont(
+			   id,
+			   fontsrc))) {
+	error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
+	      gfxFont->getName() ? gfxFont->getName()->c_str()
+	                         : "(unnamed)");
+	goto err2;
+      }
+      break;
+    case fontCIDType0COT:
+      if (((GfxCIDFont *)gfxFont)->getCIDToGID()) {
+	n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
+	codeToGID = (int *)gmallocn(n, sizeof(int));
+	memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
+	       n * sizeof(int));
+      } else {
+	codeToGID = NULL;
+	n = 0;
+      }      
+      if (!(fontFile = m_fontEngine->loadOpenTypeCFFFont(
+			   id,
+			   fontsrc,
+			   codeToGID, n))) {
+	error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
+	      gfxFont->getName() ? gfxFont->getName()->c_str()
+	                         : "(unnamed)");
+	goto err2;
+      }
+      break;
+    case fontCIDType2:
+    case fontCIDType2OT:
+      codeToGID = NULL;
+      n = 0;
+      if (((GfxCIDFont *)gfxFont)->getCIDToGID()) {
+	n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
+	if (n) {
+	  codeToGID = (int *)gmallocn(n, sizeof(int));
+	  memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
+		  n * sizeof(int));
+	}
+      } else {
+	if (fileName)
+	  ff = FoFiTrueType::load(fileName->c_str());
+	else
+	  ff = FoFiTrueType::make(tmpBuf, tmpBufLen);
+	if (! ff)
+	  goto err2;
+	codeToGID = ((GfxCIDFont *)gfxFont)->getCodeToGIDMap(ff, &n);
+	delete ff;
+      }
+      if (!(fontFile = m_fontEngine->loadTrueTypeFont(
+			   id,
+			   fontsrc,
+			   codeToGID, n, faceIndex))) {
+	error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
+	      gfxFont->getName() ? gfxFont->getName()->c_str()
+	                         : "(unnamed)");
+	goto err2;
+      }
+      break;
+    default:
+      // this shouldn't happen
+      goto err2;
+    }
+  }
+
+  // get the font matrix
+  textMat = state->getTextMat();
+  fontSize = state->getFontSize();
+  m11 = textMat[0] * fontSize * state->getHorizScaling();
+  m12 = textMat[1] * fontSize * state->getHorizScaling();
+  m21 = textMat[2] * fontSize;
+  m22 = textMat[3] * fontSize;
+
+  {
+  QMatrix painterMatrix = m_painter->worldMatrix();
+  matrix[0] = painterMatrix.m11();
+  matrix[1] = painterMatrix.m12();
+  matrix[2] = painterMatrix.m21();
+  matrix[3] = painterMatrix.m22();
+  matrix[4] = painterMatrix.dx();
+  matrix[5] = painterMatrix.dy();
+  }
+
+  // create the scaled font
+  mat[0] = m11;  mat[1] = -m12;
+  mat[2] = m21;  mat[3] = -m22;
+  m_font = m_fontEngine->getFont(fontFile, mat, matrix);
+
+  delete fontLoc;
+  if (fontsrc && !fontsrc->isFile)
+      fontsrc->unref();
+  return;
+
+ err2:
+  delete id;
+  delete fontLoc;
+ err1:
+  if (fontsrc && !fontsrc->isFile)
+      fontsrc->unref();
+  return;
+#endif
+}
+
+static QPainterPath convertPath(GfxState *state, 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;
+  double px, py;
+//   SplashPath *path;
+  int render;
+  unsigned char f;
+
+  if (m_needFontUpdate) {
+    updateFont(state);
+  }
+  if (!m_font) {
+    return;
+  }
+
+  // check for invisible text -- this is used by Acrobat Capture
+  render = state->getRender();
+  if (render == 3) {
+    return;
+  }
+
+  x -= originX;
+  y -= originY;
+
+  // fill
+  if (!(render & 1)) {
+    SplashPath * fontPath;
+    fontPath = m_font->getGlyphPath(code);
+    if (fontPath) {
+      QPainterPath qPath;
+      qPath.setFillRule(Qt::WindingFill);
+      for (int i = 0; i < fontPath->getLength(); ++i) {
+        // SplashPath.flags: bitwise or allowed
+        fontPath->getPoint(i, &px, &py, &f);
+        if (f & splashPathLast || f & splashPathClosed) {
+            qPath.closeSubpath();
+        }
+        if (f & splashPathFirst) {
+            state->transform(px+x, -py+y, &x1, &y1);
+            qPath.moveTo(x1,y1);
+        }
+        if (f & splashPathCurve) {
+            state->transform(px+x, -py+y, &x1, &y1);
+            fontPath->getPoint(i+1, &px, &py, &f);
+            state->transform(px+x, -py+y, &x2, &y2);
+            qPath.quadTo(x1,y1,x2,y2);
+            ++i;
+        }
+        // FIXME fix this
+        // 	else if (fontPath->flags[i] & splashPathArcCW) {
+        // 	  qDebug() << "Need to implement arc";
+        // 	}
+        else {
+            fontPath->getPoint(i, &px, &py, &f);
+            state->transform(px+x, -py+y, &x1, &y1);
+            qPath.lineTo(x1,y1);
+        }
+      }
+      GfxRGB rgb;
+      QColor brushColour = m_currentBrush.color();
+      state->getFillRGB(&rgb);
+      brushColour.setRgbF(colToDbl(rgb.r), colToDbl(rgb.g), colToDbl(rgb.b), state->getFillOpacity());
+      m_painter->setBrush(brushColour);
+      m_painter->setPen(Qt::NoPen);
+      m_painter->drawPath(qPath);
+      delete fontPath;
+    }
+  }
+
+  // stroke
+  if ((render & 3) == 1 || (render & 3) == 2) {
+    qDebug() << "no stroke";
+    /*
+    if ((path = m_font->getGlyphPath(code))) {
+      path->offset((SplashCoord)x1, (SplashCoord)y1);
+      splash->stroke(path);
+      delete path;
+    }
+    */
+  }
+
+  // clip
+  if (render & 4) {
+    qDebug() << "no clip";
+    /*
+    path = m_font->getGlyphPath(code);
+    path->offset((SplashCoord)x1, (SplashCoord)y1);
+    if (textClipPath) {
+      textClipPath->append(path);
+      delete path;
+    } else {
+      textClipPath = path;
+    }
+    */
+  }
+#endif
+}
+
+bool ArthurOutputDev::beginType3Char(GfxState *state, double x, double y,
+				      double dx, double dy,
+				      CharCode code, Unicode *u, int uLen)
+{
+  return false;
+}
+
+void ArthurOutputDev::endType3Char(GfxState *state)
+{
+}
+
+void ArthurOutputDev::type3D0(GfxState *state, double wx, double wy)
+{
+}
+
+void ArthurOutputDev::type3D1(GfxState *state, double wx, double wy,
+			      double llx, double lly, double urx, double ury)
+{
+}
+
+void ArthurOutputDev::endTextObject(GfxState *state)
+{
+}
+
+
+void ArthurOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
+				    int width, int height, bool invert,
+				    bool interpolate, bool inlineImg)
+{
+  qDebug() << "drawImageMask";
+#if 0
+  unsigned char *buffer;
+  unsigned char *dest;
+  cairo_surface_t *image;
+  cairo_pattern_t *pattern;
+  int x, y;
+  ImageStream *imgStr;
+  Guchar *pix;
+  double *ctm;
+  cairo_matrix_t matrix;
+  int invert_bit;
+  int row_stride;
+
+  row_stride = (width + 3) & ~3;
+  buffer = (unsigned char *) malloc (height * row_stride);
+  if (buffer == NULL) {
+    error(-1, "Unable to allocate memory for image.");
+    return;
+  }
+
+  /* TODO: Do we want to cache these? */
+  imgStr = new ImageStream(str, width, 1, 1);
+  imgStr->reset();
+
+  invert_bit = invert ? 1 : 0;
+
+  for (y = 0; y < height; y++) {
+    pix = imgStr->getLine();
+    dest = buffer + y * row_stride;
+    for (x = 0; x < width; x++) {
+
+      if (pix[x] ^ invert_bit)
+	*dest++ = 0;
+      else
+	*dest++ = 255;
+    }
+  }
+
+  image = cairo_image_surface_create_for_data (buffer, CAIRO_FORMAT_A8,
+					  width, height, row_stride);
+  if (image == NULL)
+    return;
+  pattern = cairo_pattern_create_for_surface (image);
+  if (pattern == NULL)
+    return;
+
+  ctm = state->getCTM();
+  LOG (printf ("drawImageMask %dx%d, matrix: %f, %f, %f, %f, %f, %f\n",
+	       width, height, ctm[0], ctm[1], ctm[2], ctm[3], ctm[4], ctm[5]));
+  matrix.xx = ctm[0] / width;
+  matrix.xy = -ctm[2] / height;
+  matrix.yx = ctm[1] / width;
+  matrix.yy = -ctm[3] / height;
+  matrix.x0 = ctm[2] + ctm[4];
+  matrix.y0 = ctm[3] + ctm[5];
+  cairo_matrix_invert (&matrix);
+  cairo_pattern_set_matrix (pattern, &matrix);
+
+  cairo_pattern_set_filter (pattern, CAIRO_FILTER_BEST);
+  /* FIXME: Doesn't the image mask support any colorspace? */
+  cairo_set_source_rgb (cairo, fill_color.r, fill_color.g, fill_color.b);
+  cairo_mask (cairo, pattern);
+
+  cairo_pattern_destroy (pattern);
+  cairo_surface_destroy (image);
+  free (buffer);
+  imgStr->close ();
+  delete imgStr;
+#endif
+}
+
+//TODO: lots more work here.
+void ArthurOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
+				int width, int height,
+				GfxImageColorMap *colorMap,
+				bool interpolate, int *maskColors, bool inlineImg)
+{
+  unsigned int *data;
+  unsigned int *line;
+  int x, y;
+  ImageStream *imgStr;
+  unsigned char *pix;
+  int i;
+  const double *ctm;
+  QMatrix matrix;
+  QImage image;
+  int stride;
+  
+  /* TODO: Do we want to cache these? */
+  imgStr = new ImageStream(str, width,
+			   colorMap->getNumPixelComps(),
+			   colorMap->getBits());
+  imgStr->reset();
+  
+  image = QImage(width, height, QImage::Format_ARGB32);
+  data = (unsigned int *)image.bits();
+  stride = image.bytesPerLine()/4;
+  for (y = 0; y < height; y++) {
+    pix = imgStr->getLine();
+    line = data+y*stride;
+    colorMap->getRGBLine(pix, line, width);
+
+    if (maskColors) {
+      for (x = 0; x < width; x++) {
+        for (i = 0; i < colorMap->getNumPixelComps(); ++i) {
+            if (pix[i] < maskColors[2*i] * 255||
+                pix[i] > maskColors[2*i+1] * 255) {
+                *line = *line | 0xff000000;
+                break;
+            }
+        }
+        pix += colorMap->getNumPixelComps();
+        line++;
+      }
+    } else {
+      for (x = 0; x < width; x++) { *line = *line | 0xff000000; line++; }
+    }
+  }
+
+  ctm = state->getCTM();
+  matrix.setMatrix(ctm[0] / width, ctm[1] / width, -ctm[2] / height, -ctm[3] / height, ctm[2] + ctm[4], ctm[3] + ctm[5]);
+
+  m_painter->setMatrix(matrix, true);
+  m_painter->drawImage( QPoint(0,0), image );
+  delete imgStr;
+
+}
diff --git a/qt4/src/ArthurOutputDev.h b/qt4/src/ArthurOutputDev.h
new file mode 100644
index 00000000..9d5e8679
--- /dev/null
+++ b/qt4/src/ArthurOutputDev.h
@@ -0,0 +1,169 @@
+//========================================================================
+//
+// ArthurOutputDev.h
+//
+// Copyright 2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+//========================================================================
+//
+// Modified under the Poppler project - http://poppler.freedesktop.org
+//
+// All changes made under the Poppler project to this file are licensed
+// under GPL version 2 or later
+//
+// Copyright (C) 2005 Brad Hards <bradh@frogmouth.net>
+// Copyright (C) 2005 Albert Astals Cid <aacid@kde.org>
+// Copyright (C) 2009, 2011 Carlos Garcia Campos <carlosgc@gnome.org>
+// Copyright (C) 2010 Pino Toscano <pino@kde.org>
+// Copyright (C) 2011 Andreas Hartmetz <ahartmetz@gmail.com>
+// Copyright (C) 2013 Thomas Freitag <Thomas.Freitag@alfa.de>
+//
+// To see a description of the changes please see the Changelog file that
+// came with your tarball or type make ChangeLog if you are building from git
+//
+//========================================================================
+
+#ifndef ARTHUROUTPUTDEV_H
+#define ARTHUROUTPUTDEV_H
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "OutputDev.h"
+#include "GfxState.h"
+
+#include <QtGui/QPainter>
+
+class GfxState;
+class GfxPath;
+class Gfx8BitFont;
+struct GfxRGB;
+
+class SplashFont;
+class SplashFontEngine;
+struct SplashGlyphBitmap;
+
+//------------------------------------------------------------------------
+// ArthurOutputDev - Qt 4 QPainter renderer
+//------------------------------------------------------------------------
+
+class ArthurOutputDev: public OutputDev {
+public:
+  /**
+   * Describes how fonts are distorted (aka hinted) to fit the pixel grid.
+   * More hinting means sharper edges and less adherence to the true letter shapes.
+   */
+  enum FontHinting {
+    NoHinting = 0, ///< Font shapes are left unchanged
+    SlightHinting, ///< Font shapes are distorted vertically only
+    FullHinting ///< Font shapes are distorted horizontally and vertically
+  };
+
+  // Constructor.
+  ArthurOutputDev(QPainter *painter );
+
+  // Destructor.
+  virtual ~ArthurOutputDev();
+
+  void setFontHinting(FontHinting hinting) { m_fontHinting = hinting; }
+
+  //----- get info about output device
+
+  // Does this device use upside-down coordinates?
+  // (Upside-down means (0,0) is the top left corner of the page.)
+  virtual bool upsideDown() { return true; }
+
+  // Does this device use drawChar() or drawString()?
+  virtual bool useDrawChar() { return true; }
+
+  // Does this device use beginType3Char/endType3Char?  Otherwise,
+  // text in Type 3 fonts will be drawn with drawChar/drawString.
+  virtual bool interpretType3Chars() { return true; }
+
+  //----- initialization and control
+
+  // Start a page.
+  virtual void startPage(int pageNum, GfxState *state, XRef *xref);
+
+  // End a page.
+  virtual void endPage();
+
+  //----- save/restore graphics state
+  virtual void saveState(GfxState *state);
+  virtual void restoreState(GfxState *state);
+
+  //----- update graphics state
+  virtual void updateAll(GfxState *state);
+  virtual void updateCTM(GfxState *state, double m11, double m12,
+			 double m21, double m22, double m31, double m32);
+  virtual void updateLineDash(GfxState *state);
+  virtual void updateFlatness(GfxState *state);
+  virtual void updateLineJoin(GfxState *state);
+  virtual void updateLineCap(GfxState *state);
+  virtual void updateMiterLimit(GfxState *state);
+  virtual void updateLineWidth(GfxState *state);
+  virtual void updateFillColor(GfxState *state);
+  virtual void updateStrokeColor(GfxState *state);
+  virtual void updateFillOpacity(GfxState *state);
+  virtual void updateStrokeOpacity(GfxState *state);
+
+  //----- update text state
+  virtual void updateFont(GfxState *state);
+
+  //----- path painting
+  virtual void stroke(GfxState *state);
+  virtual void fill(GfxState *state);
+  virtual void eoFill(GfxState *state);
+
+  //----- path clipping
+  virtual void clip(GfxState *state);
+  virtual void eoClip(GfxState *state);
+
+  //----- text drawing
+  //   virtual void drawString(GfxState *state, GooString *s);
+  virtual void drawChar(GfxState *state, double x, double y,
+			double dx, double dy,
+			double originX, double originY,
+			CharCode code, int nBytes, Unicode *u, int uLen);
+  virtual bool beginType3Char(GfxState *state, double x, double y,
+			       double dx, double dy,
+			       CharCode code, Unicode *u, int uLen);
+  virtual void endType3Char(GfxState *state);
+  virtual void endTextObject(GfxState *state);
+
+  //----- image drawing
+  virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
+			     int width, int height, bool invert,
+			     bool interpolate, bool inlineImg);
+  virtual void drawImage(GfxState *state, Object *ref, Stream *str,
+			 int width, int height, GfxImageColorMap *colorMap,
+			 bool interpolate, int *maskColors, bool inlineImg);
+
+  //----- Type 3 font operators
+  virtual void type3D0(GfxState *state, double wx, double wy);
+  virtual void type3D1(GfxState *state, double wx, double wy,
+		       double llx, double lly, double urx, double ury);
+
+  //----- special access
+
+  // Called to indicate that a new PDF document has been loaded.
+  void startDoc(XRef *xrefA);
+ 
+  bool isReverseVideo() { return false; }
+  
+private:
+  QPainter *m_painter;
+  FontHinting m_fontHinting;
+  QFont m_currentFont;
+  QPen m_currentPen;
+  QBrush m_currentBrush;
+  bool m_needFontUpdate;		// set when the font needs to be updated
+  SplashFontEngine *m_fontEngine;
+  SplashFont *m_font;		// current font
+  XRef *xref;			// xref table for current document
+};
+
+#endif
diff --git a/qt4/src/CMakeLists.txt b/qt4/src/CMakeLists.txt
new file mode 100644
index 00000000..f6547726
--- /dev/null
+++ b/qt4/src/CMakeLists.txt
@@ -0,0 +1,54 @@
+add_definitions(${QT4_DEFINITIONS})
+
+include_directories(
+  ${CMAKE_CURRENT_SOURCE_DIR}
+  ${QT4_INCLUDE_DIR}
+  ${CMAKE_CURRENT_BINARY_DIR}
+)
+
+set(poppler_qt4_SRCS
+  poppler-annotation.cc
+  poppler-document.cc
+  poppler-embeddedfile.cc
+  poppler-fontinfo.cc
+  poppler-form.cc
+  poppler-link.cc
+  poppler-link-extractor.cc
+  poppler-movie.cc
+  poppler-optcontent.cc
+  poppler-page.cc
+  poppler-base-converter.cc
+  poppler-pdf-converter.cc
+  poppler-private.cc
+  poppler-ps-converter.cc
+  poppler-qiodeviceoutstream.cc
+  poppler-sound.cc
+  poppler-textbox.cc
+  poppler-page-transition.cc
+  poppler-media.cc
+  ArthurOutputDev.cc
+)
+qt4_automoc(${poppler_qt4_SRCS})
+add_library(poppler-qt4 SHARED ${poppler_qt4_SRCS})
+set_target_properties(poppler-qt4 PROPERTIES VERSION 4.11.0 SOVERSION 4)
+if(MINGW)
+    get_target_property(POPPLER_QT4_SOVERSION poppler-qt4 SOVERSION)
+    set_target_properties(poppler-qt4 PROPERTIES SUFFIX "-${POPPLER_QT4_SOVERSION}${CMAKE_SHARED_LIBRARY_SUFFIX}")
+endif()
+target_link_libraries(poppler-qt4 poppler ${QT4_QTCORE_LIBRARY} ${QT4_QTGUI_LIBRARY} ${QT4_QTXML_LIBRARY})
+if(MSVC)
+target_link_libraries(poppler-qt4 poppler ${poppler_LIBS})
+endif()
+install(TARGETS poppler-qt4 RUNTIME DESTINATION bin LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+
+install(FILES
+  poppler-qt4.h
+  poppler-link.h
+  poppler-annotation.h
+  poppler-form.h
+  poppler-optcontent.h
+  poppler-export.h
+  poppler-page-transition.h
+  poppler-media.h
+  DESTINATION include/poppler/qt4)
+
diff --git a/qt4/src/Doxyfile b/qt4/src/Doxyfile
new file mode 100644
index 00000000..e68690ac
--- /dev/null
+++ b/qt4/src/Doxyfile
@@ -0,0 +1,1637 @@
+# Doxyfile 1.7.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = "Poppler Qt4 "
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER         = 0.61.1
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       =
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = NO
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF      = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penality.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will rougly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = NO
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = YES
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = NO
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS        = YES
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = YES
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES       = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> 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 <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> 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
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS  = poppler
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+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 <a href="http://okular.kde.org">Okular</a>. The source files
+for Okular's PDF plugin (%Poppler-based) can be found on the git server
+of the KDE project, under
+<a
+href="http://quickgit.kde.org/?p=okular.git&a=tree&f=generators/poppler">this
+URL</a>.
+
+
+@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 <poppler-qt4.h>
+@endcode
+
+A PDF document can then be loaded as follows:
+@code
+QString filename;
+
+Poppler::Document* document = Poppler::Document::load(filename);
+if (!document || document->isLocked()) {
+
+  // ... error message ....
+
+  delete document;
+  return;
+}
+@endcode
+
+Pages can be rendered to QImages with the following commands:
+
+@code
+// Paranoid safety check
+if (document == 0) {
+  // ... error message ...
+  return;
+}
+
+// Access page of the PDF file
+Poppler::Page* pdfPage = document->page(pageNumber);  // Document starts at page 0
+if (pdfPage == 0) {
+  // ... error message ...
+  return;
+}
+
+// Generate a QImage of the rendered page
+QImage image = pdfPage->renderToImage(xres, yres, x, y, width, height);
+if (image.isNull()) {
+  // ... error message ...
+  return;
+}
+
+// ... use image ...
+
+// after the usage, the page must be deleted
+delete pdfPage;
+@endcode
+
+Finally, don't forget to destroy the document:
+
+@code
+delete document;
+@endcode
+ */
+
diff --git a/qt4/src/poppler-annotation-helper.h b/qt4/src/poppler-annotation-helper.h
new file mode 100644
index 00000000..3150569c
--- /dev/null
+++ b/qt4/src/poppler-annotation-helper.h
@@ -0,0 +1,183 @@
+/* poppler-annotation-helper.h: qt interface to poppler
+ * Copyright (C) 2006, 2008, 2017, Albert Astals Cid <aacid@kde.org>
+ * Copyright (C) 2008, Pino Toscano <pino@kde.org>
+ * Copyright (C) 2012, Fabio D'Urso <fabiodurso@hotmail.it>
+ * Adapting code from
+ *   Copyright (C) 2004 by Enrico Ros <eros.kde@email.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <memory>
+
+#include <QtCore/QDebug>
+
+#include <Object.h>
+
+class QColor;
+
+class AnnotColor;
+
+namespace Poppler {
+
+class XPDFReader
+{
+    public:
+        // find named symbol and parse it
+        static inline void lookupName( Dict *, char *, QString & dest );
+        static inline void lookupString( Dict *, char *, QString & dest );
+        static inline void lookupBool( Dict *, char *, bool & dest );
+        static inline void lookupInt( Dict *, char *, int & dest );
+        static inline void lookupNum( Dict *, char *, double & dest );
+        static inline int lookupNumArray( Dict *, char *, double * dest, int len );
+        static inline void lookupColor( Dict *, char *, QColor & color );
+        static inline void lookupIntRef( Dict *, char *, int & dest );
+        static inline void lookupDate( Dict *, char *, QDateTime & dest );
+        // transform from user coords to normalized ones using the matrix M
+        static inline void transform( double * M, double x, double y, QPointF &res );
+        static inline void invTransform( double * M, const QPointF &p, double &x, double &y );
+};
+
+void XPDFReader::lookupName( Dict * dict, char * type, QString & dest )
+{
+    Object nameObj = dict->lookup( type );
+    if ( nameObj.isNull() )
+        return;
+    if ( nameObj.isName() )
+        dest = nameObj.getName();
+    else
+        qDebug() << type << " is not Name." << endl;
+}
+
+void XPDFReader::lookupString( Dict * dict, char * type, QString & dest )
+{
+    Object stringObj = dict->lookup( type );
+    if ( stringObj.isNull() )
+        return;
+    if ( stringObj.isString() )
+        dest = stringObj.getString()->c_str();
+    else
+        qDebug() << type << " is not String." << endl;
+}
+
+void XPDFReader::lookupBool( Dict * dict, char * type, bool & dest )
+{
+    Object boolObj = dict->lookup( type );
+    if ( boolObj.isNull() )
+        return;
+    if ( boolObj.isBool() )
+        dest = boolObj.getBool() == true;
+    else
+        qDebug() << type << " is not Bool." << endl;
+}
+
+void XPDFReader::lookupInt( Dict * dict, char * type, int & dest )
+{
+    Object intObj = dict->lookup( type );
+    if ( intObj.isNull() )
+        return;
+    if ( intObj.isInt() )
+        dest = intObj.getInt();
+    else
+        qDebug() << type << " is not Int." << endl;
+}
+
+void XPDFReader::lookupNum( Dict * dict, char * type, double & dest )
+{
+    Object numObj = dict->lookup( type );
+    if ( numObj.isNull() )
+        return;
+    if ( numObj.isNum() )
+        dest = numObj.getNum();
+    else
+        qDebug() << type << " is not Num." << endl;
+}
+
+int XPDFReader::lookupNumArray( Dict * dict, char * type, double * dest, int len )
+{
+    Object arrObj = dict->lookup( type );
+    if ( arrObj.isNull() )
+        return 0;
+    if ( arrObj.isArray() )
+    {
+        len = qMin( len, arrObj.arrayGetLength() );
+        for ( int i = 0; i < len; i++ )
+        {
+            Object numObj = arrObj.arrayGet( i );
+            dest[i] = numObj.getNum();
+        }
+    }
+    else
+    {
+        len = 0;
+        qDebug() << type << "is not Array." << endl;
+    }
+    return len;
+}
+
+void XPDFReader::lookupColor( Dict * dict, char * type, QColor & dest )
+{
+    double c[3];
+    if ( XPDFReader::lookupNumArray( dict, type, c, 3 ) == 3 )
+        dest = QColor( (int)(c[0]*255.0), (int)(c[1]*255.0), (int)(c[2]*255.0));
+}
+
+void XPDFReader::lookupIntRef( Dict * dict, char * type, int & dest )
+{
+    Object refObj = dict->lookupNF( type );
+    if ( refObj.isNull() )
+        return;
+    if ( refObj.isRef() )
+        dest = refObj.getRefNum();
+    else
+        qDebug() << type << " is not Ref." << endl;
+}
+
+void XPDFReader::lookupDate( Dict * dict, char * type, QDateTime & dest )
+{
+    Object dateObj = dict->lookup( type );
+    if ( dateObj.isNull() )
+        return;
+    if ( dateObj.isString() )
+    {
+        dest = convertDate( dateObj.getString()->c_str() );
+    }
+    else
+        qDebug() << type << " is not Date" << endl;
+}
+
+void XPDFReader::transform( double * M, double x, double y, QPointF &res )
+{
+    res.setX( M[0] * x + M[2] * y + M[4] );
+    res.setY( M[1] * x + M[3] * y + M[5] );
+}
+
+void XPDFReader::invTransform( double * M, const QPointF &p, double &x, double &y )
+{
+    const double det = M[0]*M[3] - M[1]*M[2];
+    Q_ASSERT(det != 0);
+
+    const double invM[4] = { M[3]/det, -M[1]/det, -M[2]/det, M[0]/det };
+    const double xt = p.x() - M[4];
+    const double yt = p.y() - M[5];
+
+    x = invM[0] * xt + invM[2] * yt;
+    y = invM[1] * xt + invM[3] * yt;
+}
+
+QColor convertAnnotColor( const AnnotColor *color );
+std::unique_ptr<AnnotColor> convertQColor( const QColor &c );
+
+}
diff --git a/qt4/src/poppler-annotation-private.h b/qt4/src/poppler-annotation-private.h
new file mode 100644
index 00000000..b530e2f2
--- /dev/null
+++ b/qt4/src/poppler-annotation-private.h
@@ -0,0 +1,112 @@
+/* poppler-annotation-private.h: qt interface to poppler
+ * Copyright (C) 2007, Pino Toscano <pino@kde.org>
+ * Copyright (C) 2012, Tobias Koenig <tokoe@kdab.com>
+ * Copyright (C) 2012, 2013 Fabio D'Urso <fabiodurso@hotmail.it>
+ * Copyright (C) 2012, 2014, Albert Astals Cid <aacid@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtCore/QLinkedList>
+#include <QtCore/QPointF>
+#include <QtCore/QSharedDataPointer>
+
+#include "poppler-annotation.h"
+
+#include <Object.h>
+
+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<Annotation*> 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<QPointF> &l) const;
+
+        /* Scan page for annotations, parentId=0 searches for root annotations, subtypes empty means all subtypes */
+        static QList<Annotation*> findAnnotations(::Page *pdfPage, DocumentData *doc, const QSet<Annotation::SubType> &subtypes, int parentId = 0);
+
+        /* Add given annotation to given page */
+        static void addAnnotationToPage(::Page *pdfPage, DocumentData *doc, const Annotation * ann);
+
+        /* Remove annotation from page and destroy ann */
+        static void removeAnnotationFromPage(::Page *pdfPage, const Annotation * ann);
+
+        Ref pdfObjectReference() const;
+
+        Link* additionalAction( Annotation::AdditionalActionType type ) const;
+};
+
+}
+
+#endif
diff --git a/qt4/src/poppler-annotation.cc b/qt4/src/poppler-annotation.cc
new file mode 100644
index 00000000..8a7f60b1
--- /dev/null
+++ b/qt4/src/poppler-annotation.cc
@@ -0,0 +1,5118 @@
+/* poppler-annotation.cc: qt interface to poppler
+ * Copyright (C) 2006, 2009, 2012-2015 Albert Astals Cid <aacid@kde.org>
+ * Copyright (C) 2006, 2008, 2010 Pino Toscano <pino@kde.org>
+ * Copyright (C) 2012, Guillermo A. Amaral B. <gamaral@kde.org>
+ * Copyright (C) 2012-2014 Fabio D'Urso <fabiodurso@hotmail.it>
+ * Copyright (C) 2012, 2015, Tobias Koenig <tokoe@kdab.com>
+ * Adapting code from
+ *   Copyright (C) 2004 by Enrico Ros <eros.kde@email.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtCore/QRegExp>
+#include <QtCore/QtAlgorithms>
+#include <QtXml/QDomElement>
+#include <QtGui/QColor>
+#include <QtGui/QTransform>
+
+// 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 <Page.h>
+#include <Annot.h>
+#include <Gfx.h>
+#include <Error.h>
+#include <FileSpec.h>
+#include <Link.h>
+
+/* Almost all getters directly query the underlying poppler annotation, with
+ * the exceptions of link, file attachment, sound, movie and screen annotations,
+ * Whose data retrieval logic has not been moved yet. Their getters return
+ * static data set at creation time by findAnnotations
+ */
+
+namespace Poppler {
+
+//BEGIN AnnotationUtils implementation
+Annotation * AnnotationUtils::createAnnotation( const QDomElement & annElement )
+{
+    // safety check on annotation element
+    if ( !annElement.hasAttribute( "type" ) )
+        return nullptr;
+
+    // build annotation of given type
+    Annotation * annotation = nullptr;
+    int typeNumber = annElement.attribute( "type" ).toInt();
+    switch ( typeNumber )
+    {
+        case Annotation::AText:
+            annotation = new TextAnnotation( annElement );
+            break;
+        case Annotation::ALine:
+            annotation = new LineAnnotation( annElement );
+            break;
+        case Annotation::AGeom:
+            annotation = new GeomAnnotation( annElement );
+            break;
+        case Annotation::AHighlight:
+            annotation = new HighlightAnnotation( annElement );
+            break;
+        case Annotation::AStamp:
+            annotation = new StampAnnotation( annElement );
+            break;
+        case Annotation::AInk:
+            annotation = new InkAnnotation( annElement );
+            break;
+        case Annotation::ACaret:
+            annotation = new CaretAnnotation( annElement );
+            break;
+    }
+
+    // return created annotation
+    return annotation;
+}
+
+void AnnotationUtils::storeAnnotation( const Annotation * ann, QDomElement & annElement,
+    QDomDocument & document )
+{
+    // save annotation's type as element's attribute
+    annElement.setAttribute( "type", (uint)ann->subType() );
+
+    // append all annotation data as children of this node
+    ann->store( annElement, document );
+}
+
+QDomElement AnnotationUtils::findChildElement( const QDomNode & parentNode,
+    const QString & name )
+{
+    // loop through the whole children and return a 'name' named element
+    QDomNode subNode = parentNode.firstChild();
+    while( subNode.isElement() )
+    {
+        QDomElement element = subNode.toElement();
+        if ( element.tagName() == name )
+            return element;
+        subNode = subNode.nextSibling();
+    }
+    // if the name can't be found, return a dummy null element
+    return QDomElement();
+}
+//END AnnotationUtils implementation
+
+
+//BEGIN Annotation implementation
+AnnotationPrivate::AnnotationPrivate()
+    : flags( 0 ), revisionScope ( Annotation::Root ),
+    revisionType ( Annotation::None ), pdfAnnot ( nullptr ), pdfPage ( nullptr ),
+    parentDoc ( nullptr )
+{
+}
+
+void AnnotationPrivate::addRevision( Annotation *ann, Annotation::RevScope scope, Annotation::RevType type )
+{
+    /* Since ownership stays with the caller, create an alias of ann */
+    revisions.append( ann->d_ptr->makeAlias() );
+
+    /* Set revision properties */
+    revisionScope = scope;
+    revisionType = type;
+}
+
+AnnotationPrivate::~AnnotationPrivate()
+{
+    // Delete all children revisions
+    qDeleteAll( revisions );
+
+    // Release Annot object
+    if (pdfAnnot)
+        pdfAnnot->decRefCnt();
+}
+
+void AnnotationPrivate::tieToNativeAnnot(Annot *ann, ::Page *page, Poppler::DocumentData * doc)
+{
+    if (pdfAnnot)
+    {
+        error(errIO, -1, "Annotation is already tied");
+        return;
+    }
+
+    pdfAnnot = ann;
+    pdfPage = page;
+    parentDoc = doc;
+
+    pdfAnnot->incRefCnt();
+}
+
+/* This method is called when a new annotation is created, after pdfAnnot and
+ * pdfPage have been set */
+void AnnotationPrivate::flushBaseAnnotationProperties()
+{
+    Q_ASSERT ( pdfPage );
+
+    Annotation *q = makeAlias(); // Setters are defined in the public class
+
+    // Since pdfAnnot has been set, this calls will write in the Annot object
+    q->setAuthor(author);
+    q->setContents(contents);
+    q->setUniqueName(uniqueName);
+    q->setModificationDate(modDate);
+    q->setCreationDate(creationDate);
+    q->setFlags(flags);
+    //q->setBoundary(boundary); -- already set by subclass-specific code
+    q->setStyle(style);
+    q->setPopup(popup);
+
+    // Flush revisions
+    foreach (Annotation *r, revisions)
+    {
+        // TODO: Flush revision
+        delete r; // Object is no longer needed
+    }
+
+    delete q;
+
+    // Clear some members to save memory
+    author.clear();
+    contents.clear();
+    uniqueName.clear();
+    revisions.clear();
+}
+
+// Returns matrix to convert from user space coords (oriented according to the
+// specified rotation) to normalized coords
+void AnnotationPrivate::fillNormalizationMTX(double MTX[6], int pageRotation) const
+{
+    Q_ASSERT ( pdfPage );
+
+    // build a normalized transform matrix for this page at 100% scale
+    GfxState * gfxState = new GfxState( 72.0, 72.0, pdfPage->getCropBox(), pageRotation, true );
+    const double * gfxCTM = gfxState->getCTM();
+
+    double w = pdfPage->getCropWidth();
+    double h = pdfPage->getCropHeight();
+
+    // Swap width and height if the page is rotated landscape or seascape
+    if ( pageRotation == 90 || pageRotation == 270 )
+    {
+        double t = w;
+        w = h;
+        h = t;
+    }
+
+    for ( int i = 0; i < 6; i+=2 )
+    {
+        MTX[i] = gfxCTM[i] / w;
+        MTX[i+1] = gfxCTM[i+1] / h;
+    }
+    delete gfxState;
+}
+
+// Returns matrix to convert from user space coords (i.e. those that are stored
+// in the PDF file) to normalized coords (i.e. those that we expose to clients).
+// This method also applies a rotation around the top-left corner if the
+// FixedRotation flag is set.
+void AnnotationPrivate::fillTransformationMTX(double MTX[6]) const
+{
+    Q_ASSERT ( pdfPage );
+    Q_ASSERT ( pdfAnnot );
+
+    const int pageRotate = pdfPage->getRotate();
+
+    if ( pageRotate == 0 || ( pdfAnnot->getFlags() & Annot::flagNoRotate ) == 0 )
+    {
+        // Use the normalization matrix for this page's rotation
+        fillNormalizationMTX( MTX, pageRotate );
+    }
+    else
+    {
+        // Clients expect coordinates relative to this page's rotation, but
+        // FixedRotation annotations internally use unrotated coordinates:
+        // construct matrix to both normalize and rotate coordinates using the
+        // top-left corner as rotation pivot
+
+        double MTXnorm[6];
+        fillNormalizationMTX( MTXnorm, pageRotate );
+
+        QTransform transform( MTXnorm[0], MTXnorm[1], MTXnorm[2],
+                              MTXnorm[3], MTXnorm[4], MTXnorm[5] );
+        transform.translate( +pdfAnnot->getXMin(), +pdfAnnot->getYMax() );
+        transform.rotate( pageRotate );
+        transform.translate( -pdfAnnot->getXMin(), -pdfAnnot->getYMax() );
+
+        MTX[0] = transform.m11();
+        MTX[1] = transform.m12();
+        MTX[2] = transform.m21();
+        MTX[3] = transform.m22();
+        MTX[4] = transform.dx();
+        MTX[5] = transform.dy();
+    }
+}
+
+QRectF AnnotationPrivate::fromPdfRectangle(const PDFRectangle &r) const
+{
+    double swp, MTX[6];
+    fillTransformationMTX(MTX);
+
+    QPointF p1, p2;
+    XPDFReader::transform( MTX, r.x1, r.y1, p1 );
+    XPDFReader::transform( MTX, r.x2, r.y2, p2 );
+
+    double tl_x = p1.x();
+    double tl_y = p1.y();
+    double br_x = p2.x();
+    double br_y = p2.y();
+
+    if (tl_x > br_x)
+    {
+        swp = tl_x;
+        tl_x = br_x;
+        br_x = swp;
+    }
+
+    if (tl_y > br_y)
+    {
+        swp = tl_y;
+        tl_y = br_y;
+        br_y = swp;
+    }
+
+    return QRectF( QPointF(tl_x,tl_y) , QPointF(br_x,br_y) );
+}
+
+// This function converts a boundary QRectF in normalized coords to a
+// PDFRectangle in user coords. If the FixedRotation flag is set, this function
+// also applies a rotation around the top-left corner: it's the inverse of
+// the transformation produced by fillTransformationMTX, but we can't use
+// fillTransformationMTX here because it relies on the native annotation
+// object's boundary rect to be already set up.
+PDFRectangle AnnotationPrivate::boundaryToPdfRectangle(const QRectF &r, int flags) const
+{
+    Q_ASSERT ( pdfPage );
+
+    const int pageRotate = pdfPage->getRotate();
+
+    double MTX[6];
+    fillNormalizationMTX( MTX, pageRotate );
+
+    double tl_x, tl_y, br_x, br_y, swp;
+    XPDFReader::invTransform( MTX, r.topLeft(), tl_x, tl_y );
+    XPDFReader::invTransform( MTX, r.bottomRight(), br_x, br_y );
+
+    if (tl_x > br_x)
+    {
+        swp = tl_x;
+        tl_x = br_x;
+        br_x = swp;
+    }
+
+    if (tl_y > br_y)
+    {
+        swp = tl_y;
+        tl_y = br_y;
+        br_y = swp;
+    }
+
+    const int rotationFixUp = ( flags & Annotation::FixedRotation ) ? pageRotate : 0;
+    const double width = br_x - tl_x;
+    const double height = br_y - tl_y;
+
+    if ( rotationFixUp == 0 )
+        return PDFRectangle(tl_x, tl_y, br_x, br_y);
+    else if ( rotationFixUp == 90 )
+        return PDFRectangle(tl_x, tl_y - width, tl_x + height, tl_y);
+    else if ( rotationFixUp == 180 )
+        return PDFRectangle(br_x, tl_y - height, br_x + width, tl_y);
+    else // rotationFixUp == 270
+        return PDFRectangle(br_x, br_y - width, br_x + height, br_y);
+}
+
+AnnotPath * AnnotationPrivate::toAnnotPath(const QLinkedList<QPointF> &list) const
+{
+    const int count = list.size();
+    std::vector<AnnotCoord> ac;
+    ac.reserve(count);
+
+    double MTX[6];
+    fillTransformationMTX(MTX);
+
+    foreach (const QPointF &p, list)
+    {
+        double x, y;
+        XPDFReader::invTransform( MTX, p, x, y );
+        ac.emplace_back(x, y);
+    }
+
+    return new AnnotPath(std::move(ac));
+}
+
+QList<Annotation*> AnnotationPrivate::findAnnotations(::Page *pdfPage, DocumentData *doc, const QSet<Annotation::SubType> &subtypes, int parentID)
+{
+    Annots* annots = pdfPage->getAnnots();
+    const uint numAnnotations = annots->getNumAnnots();
+    if ( numAnnotations == 0 )
+    {
+        return QList<Annotation*>();
+    }
+
+    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<Annotation*> res;
+    for ( uint j = 0; j < numAnnotations; j++ )
+    {
+        // get the j-th annotation
+        Annot * ann = annots->getAnnot( j );
+        if ( !ann )
+        {
+            error(errInternal, -1, "Annot {0:ud} is null", j);
+            continue;
+        }
+
+        // Check parent annotation
+        AnnotMarkup * markupann = dynamic_cast< AnnotMarkup * >( ann );
+        if (!markupann)
+        {
+            // Assume it's a root annotation, and skip if user didn't request it
+            if (parentID != 0)
+                continue;
+        }
+        else if (markupann->getInReplyToID() != parentID)
+            continue;
+
+        /* Create Annotation of the right subclass */
+        Annotation * annotation = nullptr;
+        Annot::AnnotSubtype subType = ann->getType();
+
+        switch ( subType )
+        {
+            case Annot::typeText:
+                if (!wantTextAnnotations)
+                    continue;
+                annotation = new TextAnnotation(TextAnnotation::Linked);
+                break;
+            case Annot::typeFreeText:
+                if (!wantTextAnnotations)
+                    continue;
+                annotation = new TextAnnotation(TextAnnotation::InPlace);
+                break;
+            case Annot::typeLine:
+                if (!wantLineAnnotations)
+                    continue;
+                annotation = new LineAnnotation(LineAnnotation::StraightLine);
+                break;
+            case Annot::typePolygon:
+            case Annot::typePolyLine:
+                if (!wantLineAnnotations)
+                    continue;
+                annotation = new LineAnnotation(LineAnnotation::Polyline);
+                break;
+            case Annot::typeSquare:
+            case Annot::typeCircle:
+                if (!wantGeomAnnotations)
+                    continue;
+                annotation = new GeomAnnotation();
+                break;
+            case Annot::typeHighlight:
+            case Annot::typeUnderline:
+            case Annot::typeSquiggly:
+            case Annot::typeStrikeOut:
+                if (!wantHighlightAnnotations)
+                    continue;
+                annotation = new HighlightAnnotation();
+                break;
+            case Annot::typeStamp:
+                if (!wantStampAnnotations)
+                    continue;
+                annotation = new StampAnnotation();
+                break;
+            case Annot::typeInk:
+                if (!wantInkAnnotations)
+                    continue;
+                annotation = new InkAnnotation();
+                break;
+            case Annot::typeLink: /* TODO: Move logic to getters */
+            {
+                if (!wantLinkAnnotations)
+                    continue;
+                // parse Link params
+                AnnotLink * linkann = static_cast< AnnotLink * >( ann );
+                LinkAnnotation * l = new LinkAnnotation();
+                annotation = l;
+
+                // -> hlMode
+                l->setLinkHighlightMode( (LinkAnnotation::HighlightMode)linkann->getLinkEffect() );
+
+                // -> link region
+                // TODO
+
+                // reading link action
+                if ( linkann->getAction() )
+                {
+                    Link * popplerLink = PageData::convertLinkActionToLink( linkann->getAction(), doc, QRectF() );
+                    if ( popplerLink )
+                    {
+                        l->setLinkDestination( popplerLink );
+                    }
+                }
+                break;
+            }
+            case Annot::typeCaret:
+                if (!wantCaretAnnotations)
+                    continue;
+                annotation = new CaretAnnotation();
+                break;
+            case Annot::typeFileAttachment: /* TODO: Move logic to getters */
+            {
+                if (!wantFileAttachmentAnnotations)
+                    continue;
+                AnnotFileAttachment * attachann = static_cast< AnnotFileAttachment * >( ann );
+                FileAttachmentAnnotation * f = new FileAttachmentAnnotation();
+                annotation = f;
+                // -> fileIcon
+                f->setFileIconName( QString::fromLatin1( attachann->getName()->c_str() ) );
+                // -> embeddedFile
+                FileSpec *filespec = new FileSpec( attachann->getFile() );
+                f->setEmbeddedFile( new EmbeddedFile( *new EmbeddedFileData( filespec ) ) );
+                break;
+            }
+            case Annot::typeSound: /* TODO: Move logic to getters */
+            {
+                if (!wantSoundAnnotations)
+                    continue;
+                AnnotSound * soundann = static_cast< AnnotSound * >( ann );
+                SoundAnnotation * s = new SoundAnnotation();
+                annotation = s;
+
+                // -> soundIcon
+                s->setSoundIconName( QString::fromLatin1( soundann->getName()->c_str() ) );
+                // -> sound
+                s->setSound( new SoundObject( soundann->getSound() ) );
+                break;
+            }
+            case Annot::typeMovie: /* TODO: Move logic to getters */
+            {
+                if (!wantMovieAnnotations)
+                    continue;
+                AnnotMovie * movieann = static_cast< AnnotMovie * >( ann );
+                MovieAnnotation * m = new MovieAnnotation();
+                annotation = m;
+
+                // -> movie
+                MovieObject *movie = new MovieObject( movieann );
+                m->setMovie( movie );
+                // -> movieTitle
+                const GooString * movietitle = movieann->getTitle();
+                if ( movietitle )
+                    m->setMovieTitle( QString::fromLatin1( movietitle->c_str() ) );
+                break;
+            }
+            case Annot::typeScreen:
+            {
+                if (!wantScreenAnnotations)
+                    continue;
+                AnnotScreen * screenann = static_cast< AnnotScreen * >( ann );
+                if (!screenann->getAction())
+                  continue;
+                ScreenAnnotation * s = new ScreenAnnotation();
+                annotation = s;
+
+                // -> screen
+                Link * popplerLink = PageData::convertLinkActionToLink( screenann->getAction(), doc, QRectF() );
+                s->setAction( static_cast<Poppler::LinkRendition *>(popplerLink) );
+
+                // -> screenTitle
+                const GooString * screentitle = screenann->getTitle();
+                if ( screentitle )
+                    s->setScreenTitle( UnicodeParsedString( screentitle ) );
+                break;
+            }
+            case Annot::typePopup:
+                continue; // popups are parsed by Annotation's window() getter
+            case Annot::typeUnknown:
+                continue; // special case for ignoring unknown annotations
+            case Annot::typeWidget:
+                if (!wantWidgetAnnotations)
+                    continue;
+                annotation = new WidgetAnnotation();
+                break;
+            case Annot::typeRichMedia:
+            {
+                const AnnotRichMedia * annotRichMedia = static_cast< AnnotRichMedia * >( ann );
+
+                RichMediaAnnotation *richMediaAnnotation = new RichMediaAnnotation;
+
+                const AnnotRichMedia::Settings *annotSettings = annotRichMedia->getSettings();
+                if ( annotSettings ) {
+                    RichMediaAnnotation::Settings *settings = new RichMediaAnnotation::Settings;
+
+                    if ( annotSettings->getActivation() ) {
+                        RichMediaAnnotation::Activation *activation = new RichMediaAnnotation::Activation;
+
+                        switch ( annotSettings->getActivation()->getCondition() )
+                        {
+                            case AnnotRichMedia::Activation::conditionPageOpened:
+                                activation->setCondition( RichMediaAnnotation::Activation::PageOpened );
+                                break;
+                            case AnnotRichMedia::Activation::conditionPageVisible:
+                                activation->setCondition( RichMediaAnnotation::Activation::PageVisible );
+                                break;
+                            case AnnotRichMedia::Activation::conditionUserAction:
+                                activation->setCondition( RichMediaAnnotation::Activation::UserAction );
+                                break;
+                        }
+
+                        settings->setActivation( activation );
+                    }
+
+                    if ( annotSettings->getDeactivation() ) {
+                        RichMediaAnnotation::Deactivation *deactivation = new RichMediaAnnotation::Deactivation;
+
+                        switch ( annotSettings->getDeactivation()->getCondition() )
+                        {
+                            case AnnotRichMedia::Deactivation::conditionPageClosed:
+                                deactivation->setCondition( RichMediaAnnotation::Deactivation::PageClosed );
+                                break;
+                            case AnnotRichMedia::Deactivation::conditionPageInvisible:
+                                deactivation->setCondition( RichMediaAnnotation::Deactivation::PageInvisible );
+                                break;
+                            case AnnotRichMedia::Deactivation::conditionUserAction:
+                                deactivation->setCondition( RichMediaAnnotation::Deactivation::UserAction );
+                                break;
+                        }
+
+                        settings->setDeactivation( deactivation );
+                    }
+
+                    richMediaAnnotation->setSettings( settings );
+                }
+
+                const AnnotRichMedia::Content *annotContent = annotRichMedia->getContent();
+                if ( annotContent ) {
+                    RichMediaAnnotation::Content *content = new RichMediaAnnotation::Content;
+
+                    const int configurationsCount = annotContent->getConfigurationsCount();
+                    if ( configurationsCount > 0 ) {
+                        QList< RichMediaAnnotation::Configuration* > configurations;
+
+                        for ( int i = 0; i < configurationsCount; ++i ) {
+                            const AnnotRichMedia::Configuration *annotConfiguration = annotContent->getConfiguration( i );
+                            if ( !annotConfiguration )
+                                continue;
+
+                            RichMediaAnnotation::Configuration *configuration = new RichMediaAnnotation::Configuration;
+
+                            if ( annotConfiguration->getName() )
+                                configuration->setName( UnicodeParsedString( annotConfiguration->getName() ) );
+
+                            switch ( annotConfiguration->getType() )
+                            {
+                                case AnnotRichMedia::Configuration::type3D:
+                                    configuration->setType( RichMediaAnnotation::Configuration::Type3D );
+                                    break;
+                                case AnnotRichMedia::Configuration::typeFlash:
+                                    configuration->setType( RichMediaAnnotation::Configuration::TypeFlash );
+                                    break;
+                                case AnnotRichMedia::Configuration::typeSound:
+                                    configuration->setType( RichMediaAnnotation::Configuration::TypeSound );
+                                    break;
+                                case AnnotRichMedia::Configuration::typeVideo:
+                                    configuration->setType( RichMediaAnnotation::Configuration::TypeVideo );
+                                    break;
+                            }
+
+                            const int instancesCount = annotConfiguration->getInstancesCount();
+                            if ( instancesCount > 0 ) {
+                                QList< RichMediaAnnotation::Instance* > instances;
+
+                                for ( int j = 0; j < instancesCount; ++j ) {
+                                    const AnnotRichMedia::Instance *annotInstance = annotConfiguration->getInstance( j );
+                                    if ( !annotInstance )
+                                        continue;
+
+                                    RichMediaAnnotation::Instance *instance = new RichMediaAnnotation::Instance;
+
+                                    switch ( annotInstance->getType() )
+                                    {
+                                        case AnnotRichMedia::Instance::type3D:
+                                            instance->setType( RichMediaAnnotation::Instance::Type3D );
+                                            break;
+                                        case AnnotRichMedia::Instance::typeFlash:
+                                            instance->setType( RichMediaAnnotation::Instance::TypeFlash );
+                                            break;
+                                        case AnnotRichMedia::Instance::typeSound:
+                                            instance->setType( RichMediaAnnotation::Instance::TypeSound );
+                                            break;
+                                        case AnnotRichMedia::Instance::typeVideo:
+                                            instance->setType( RichMediaAnnotation::Instance::TypeVideo );
+                                            break;
+                                    }
+
+                                    const AnnotRichMedia::Params *annotParams = annotInstance->getParams();
+                                    if ( annotParams ) {
+                                        RichMediaAnnotation::Params *params = new RichMediaAnnotation::Params;
+
+                                        if ( annotParams->getFlashVars() )
+                                            params->setFlashVars( UnicodeParsedString( annotParams->getFlashVars() ) );
+
+                                        instance->setParams( params );
+                                    }
+
+                                    instances.append( instance );
+                                }
+
+                                configuration->setInstances( instances );
+                            }
+
+                            configurations.append( configuration );
+                        }
+
+                        content->setConfigurations( configurations );
+                    }
+
+                    const int assetsCount = annotContent->getAssetsCount();
+                    if ( assetsCount > 0 ) {
+                        QList< RichMediaAnnotation::Asset* > assets;
+
+                        for ( int i = 0; i < assetsCount; ++i ) {
+                            const AnnotRichMedia::Asset *annotAsset = annotContent->getAsset( i );
+                            if ( !annotAsset )
+                                continue;
+
+                            RichMediaAnnotation::Asset *asset = new RichMediaAnnotation::Asset;
+
+                            if ( annotAsset->getName() )
+                                asset->setName( UnicodeParsedString( annotAsset->getName() ) );
+
+                            FileSpec *fileSpec = new FileSpec( annotAsset->getFileSpec() );
+                            asset->setEmbeddedFile( new EmbeddedFile( *new EmbeddedFileData( fileSpec ) ) );
+
+                            assets.append( asset );
+                        }
+
+                        content->setAssets( assets );
+                    }
+
+                    richMediaAnnotation->setContent( content );
+                }
+
+                annotation = richMediaAnnotation;
+
+                break;
+            }
+            default:
+            {
+#define CASE_FOR_TYPE( thetype ) \
+                    case Annot::type ## thetype: \
+                        error(errUnimplemented, -1, "Annotation " #thetype " not supported"); \
+                        break;
+                switch ( subType )
+                {
+                    CASE_FOR_TYPE( PrinterMark )
+                    CASE_FOR_TYPE( TrapNet )
+                    CASE_FOR_TYPE( Watermark )
+                    CASE_FOR_TYPE( 3D )
+                    default: error(errUnimplemented, -1, "Annotation {0:d} not supported", subType);
+                }
+                continue;
+#undef CASE_FOR_TYPE
+            }
+        }
+
+        annotation->d_ptr->tieToNativeAnnot(ann, pdfPage, doc);
+        res.append(annotation);
+    }
+
+    return res;
+}
+
+Ref AnnotationPrivate::pdfObjectReference() const
+{
+    if (pdfAnnot == nullptr)
+    {
+        const Ref invalid_ref = { -1, -1 };
+        return invalid_ref;
+    }
+
+    return pdfAnnot->getRef();
+}
+
+Link* AnnotationPrivate::additionalAction( Annotation::AdditionalActionType type ) const
+{
+    if ( pdfAnnot->getType() != Annot::typeScreen && pdfAnnot->getType() != Annot::typeWidget )
+        return nullptr;
+
+    Annot::AdditionalActionsType actionType = Annot::actionCursorEntering;
+    switch ( type )
+    {
+        case Annotation::CursorEnteringAction: actionType = Annot::actionCursorEntering; break;
+        case Annotation::CursorLeavingAction: actionType = Annot::actionCursorLeaving; break;
+        case Annotation::MousePressedAction: actionType = Annot::actionMousePressed; break;
+        case Annotation::MouseReleasedAction: actionType = Annot::actionMouseReleased; break;
+        case Annotation::FocusInAction: actionType = Annot::actionFocusIn; break;
+        case Annotation::FocusOutAction: actionType = Annot::actionFocusOut; break;
+        case Annotation::PageOpeningAction: actionType = Annot::actionPageOpening; break;
+        case Annotation::PageClosingAction: actionType = Annot::actionPageClosing; break;
+        case Annotation::PageVisibleAction: actionType = Annot::actionPageVisible; break;
+        case Annotation::PageInvisibleAction: actionType = Annot::actionPageInvisible; break;
+    }
+
+    ::LinkAction *linkAction = nullptr;
+    if ( pdfAnnot->getType() == Annot::typeScreen )
+        linkAction = static_cast<AnnotScreen*>( pdfAnnot )->getAdditionalAction( actionType );
+    else
+        linkAction = static_cast<AnnotWidget*>( pdfAnnot )->getAdditionalAction( actionType );
+
+    Link *link = nullptr;
+
+    if ( linkAction )
+        link = PageData::convertLinkActionToLink( linkAction, parentDoc, QRectF() );
+
+    return link;
+}
+
+void AnnotationPrivate::addAnnotationToPage(::Page *pdfPage, DocumentData *doc, const Annotation * ann)
+{
+    if (ann->d_ptr->pdfAnnot != nullptr)
+    {
+        error(errIO, -1, "Annotation is already tied");
+        return;
+    }
+
+    // Unimplemented annotations can't be created by the user because their ctor
+    // is private. Therefore, createNativeAnnot will never return 0
+    Annot *nativeAnnot = ann->d_ptr->createNativeAnnot(pdfPage, doc);
+    Q_ASSERT(nativeAnnot);
+    pdfPage->addAnnot(nativeAnnot);
+}
+
+void AnnotationPrivate::removeAnnotationFromPage(::Page *pdfPage, const Annotation * ann)
+{
+    if (ann->d_ptr->pdfAnnot == nullptr)
+    {
+        error(errIO, -1, "Annotation is not tied");
+        return;
+    }
+
+    if (ann->d_ptr->pdfPage != pdfPage)
+    {
+        error(errIO, -1, "Annotation doesn't belong to the specified page");
+        return;
+    }
+
+    // Remove annotation
+    pdfPage->removeAnnot(ann->d_ptr->pdfAnnot);
+
+    // Destroy object
+    delete ann;
+}
+
+class Annotation::Style::Private : public QSharedData
+{
+  public:
+    Private()
+        : opacity( 1.0 ), width( 1.0 ), lineStyle( Solid ), xCorners( 0.0 ),
+        yCorners( 0.0 ), lineEffect( NoEffect ), effectIntensity( 1.0 )
+        {
+            dashArray.resize(1);
+            dashArray[0] = 3;
+        }
+
+    QColor color;
+    double opacity;
+    double width;
+    Annotation::LineStyle lineStyle;
+    double xCorners;
+    double yCorners;
+    QVector<double> 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<double>& Annotation::Style::dashArray() const
+{
+    return d->dashArray;
+}
+
+void Annotation::Style::setDashArray(const QVector<double> &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<double> 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<double> 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<Annotation*> 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<const AnnotMarkup*>(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<AnnotMarkup*>(d->pdfAnnot);
+    if (markupann)
+    {
+        GooString *s = QStringToUnicodeGooString(author);
+        markupann->setLabel(s);
+        delete s;
+    }
+}
+
+QString Annotation::contents() const
+{
+    Q_D( const Annotation );
+
+    if (!d->pdfAnnot)
+        return d->contents;
+
+    return UnicodeParsedString( d->pdfAnnot->getContents() );
+}
+
+void Annotation::setContents( const QString &contents )
+{
+    Q_D( Annotation );
+
+    if (!d->pdfAnnot)
+    {
+        d->contents = contents;
+        return;
+    }
+
+    GooString *s = QStringToUnicodeGooString(contents);
+    d->pdfAnnot->setContents(s);
+    delete s;
+}
+
+QString Annotation::uniqueName() const
+{
+    Q_D( const Annotation );
+
+    if (!d->pdfAnnot)
+        return d->uniqueName;
+
+    return UnicodeParsedString( d->pdfAnnot->getName() );
+}
+
+void Annotation::setUniqueName( const QString &uniqueName )
+{
+    Q_D( Annotation );
+
+    if (!d->pdfAnnot)
+    {
+        d->uniqueName = uniqueName;
+        return;
+    }
+
+    QByteArray ascii = uniqueName.toAscii();
+    GooString s(ascii.constData());
+    d->pdfAnnot->setName(&s);
+}
+
+QDateTime Annotation::modificationDate() const
+{
+    Q_D( const Annotation );
+
+    if (!d->pdfAnnot)
+        return d->modDate;
+
+    if ( d->pdfAnnot->getModified() )
+        return convertDate( d->pdfAnnot->getModified()->c_str() );
+    else
+        return QDateTime();
+}
+
+void Annotation::setModificationDate( const QDateTime &date )
+{
+    Q_D( Annotation );
+
+    if (!d->pdfAnnot)
+    {
+        d->modDate = date;
+        return;
+    }
+
+#if 0 // TODO: Conversion routine is broken
+    if (d->pdfAnnot)
+    {
+        time_t t = date.toTime_t();
+        GooString *s = timeToDateString(&t);
+        d->pdfAnnot->setModified(s);
+        delete s;
+    }
+#endif
+}
+
+QDateTime Annotation::creationDate() const
+{
+    Q_D( const Annotation );
+
+    if (!d->pdfAnnot)
+        return d->creationDate;
+
+    const AnnotMarkup *markupann = dynamic_cast<const AnnotMarkup*>(d->pdfAnnot);
+
+    if (markupann && markupann->getDate())
+        return convertDate( markupann->getDate()->c_str() );
+
+    return modificationDate();
+}
+
+void Annotation::setCreationDate( const QDateTime &date )
+{
+    Q_D( Annotation );
+
+    if (!d->pdfAnnot)
+    {
+        d->creationDate = date;
+        return;
+    }
+
+#if 0 // TODO: Conversion routine is broken
+    AnnotMarkup *markupann = dynamic_cast<AnnotMarkup*>(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<const AnnotMarkup*>(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<const AnnotBorderArray*>(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<double> 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<AnnotFreeText*>(d->pdfAnnot)->getBorderEffect();
+            break;
+        case Annot::typeSquare:
+        case Annot::typeCircle:
+            border_effect = static_cast<AnnotGeometry*>(d->pdfAnnot)->getBorderEffect();
+            break;
+        default:
+            border_effect = nullptr;
+    }
+    if (border_effect)
+    {
+        s.setLineEffect( (Annotation::LineEffect)border_effect->getEffectType() );
+        s.setEffectIntensity( border_effect->getIntensity() );
+    }
+
+    return s;
+}
+
+void Annotation::setStyle( const Annotation::Style& style )
+{
+    Q_D( Annotation );
+
+    if (!d->pdfAnnot)
+    {
+        d->style = style;
+        return;
+    }
+
+    d->pdfAnnot->setColor(convertQColor( style.color() ));
+
+    AnnotMarkup *markupann = dynamic_cast<AnnotMarkup*>(d->pdfAnnot);
+    if (markupann)
+        markupann->setOpacity( style.opacity() );
+
+    auto border = std::make_unique<AnnotBorderArray>();
+    border->setWidth( style.width() );
+    border->setHorizontalCorner( style.xCorners() );
+    border->setVerticalCorner( style.yCorners() );
+    d->pdfAnnot->setBorder(std::move(border));
+}
+
+Annotation::Popup Annotation::popup() const
+{
+    Q_D( const Annotation );
+
+    if (!d->pdfAnnot)
+        return d->popup;
+
+    Popup w;
+    AnnotPopup *popup = nullptr;
+    int flags = -1; // Not initialized
+
+    const AnnotMarkup *markupann = dynamic_cast<const AnnotMarkup*>(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<const AnnotText*>(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<AnnotMarkup*>(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<const AnnotMarkup*>(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<const AnnotText*>(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*> Annotation::revisions() const
+{
+    Q_D( const Annotation );
+
+    if (!d->pdfAnnot)
+    {
+        /* Return aliases, whose ownership goes to the caller */
+        QList<Annotation*> 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<Annotation*>();
+
+    return AnnotationPrivate::findAnnotations( d->pdfPage, d->parentDoc, QSet<Annotation::SubType>(), d->pdfAnnot->getId() );
+}
+
+//END Annotation implementation
+
+
+/** TextAnnotation [Annotation] */
+class TextAnnotationPrivate : public AnnotationPrivate
+{
+    public:
+        TextAnnotationPrivate();
+        Annotation * makeAlias();
+        Annot* createNativeAnnot(::Page *destPage, DocumentData *doc);
+        void setDefaultAppearanceToNative();
+        std::unique_ptr<DefaultAppearance> getDefaultAppearanceFromNative() const;
+
+        // data fields
+        TextAnnotation::TextType textType;
+        QString textIcon;
+        QFont textFont;
+        QColor textColor;
+        int inplaceAlign; // 0:left, 1:center, 2:right
+        QVector<QPointF> inplaceCallout;
+        TextAnnotation::InplaceIntent inplaceIntent;
+};
+
+TextAnnotationPrivate::TextAnnotationPrivate()
+    : AnnotationPrivate(), textType( TextAnnotation::Linked ),
+    textIcon( "Note" ), inplaceAlign( 0 ),
+    inplaceIntent( TextAnnotation::Unknown )
+{
+}
+
+Annotation * TextAnnotationPrivate::makeAlias()
+{
+    return new TextAnnotation(*this);
+}
+
+Annot* TextAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
+{
+    // Setters are defined in the public class
+    TextAnnotation *q = static_cast<TextAnnotation*>( makeAlias() );
+
+    // Set page and contents
+    pdfPage = destPage;
+    parentDoc = doc;
+
+    // Set pdfAnnot
+    PDFRectangle rect = boundaryToPdfRectangle(boundary, flags);
+    if (textType == TextAnnotation::Linked) {
+      pdfAnnot = new AnnotText(destPage->getDoc(), &rect);
+    } else {
+      DefaultAppearance da{ { objName, "Invalid_font" }, static_cast<double>( textFont.pointSize() ), std::unique_ptr<AnnotColor>{ convertQColor( textColor ) } };
+      pdfAnnot = new AnnotFreeText(destPage->getDoc(), &rect, da);
+    }
+
+    // Set properties
+    flushBaseAnnotationProperties();
+    q->setTextIcon(textIcon);
+    q->setInplaceAlign(inplaceAlign);
+    q->setCalloutPoints(inplaceCallout);
+    q->setInplaceIntent(inplaceIntent);
+
+    delete q;
+
+    inplaceCallout.clear(); // Free up memory
+
+    return pdfAnnot;
+}
+
+void TextAnnotationPrivate::setDefaultAppearanceToNative()
+{
+    if (pdfAnnot && pdfAnnot->getType() == Annot::typeFreeText) {
+        AnnotFreeText * ftextann = static_cast<AnnotFreeText*>(pdfAnnot);
+        DefaultAppearance da{ { objName, "Invalid_font" }, static_cast<double>( textFont.pointSize() ), std::unique_ptr<AnnotColor>{ convertQColor( textColor ) } };
+        ftextann->setDefaultAppearance( da );
+    }
+}
+
+std::unique_ptr<DefaultAppearance> TextAnnotationPrivate::getDefaultAppearanceFromNative() const
+{
+    if (pdfAnnot && pdfAnnot->getType() == Annot::typeFreeText) {
+        AnnotFreeText * ftextann = static_cast<AnnotFreeText*>(pdfAnnot);
+        return ftextann->getDefaultAppearance();
+    } else {
+        return nullptr;
+    }
+}
+
+TextAnnotation::TextAnnotation( TextAnnotation::TextType type )
+    : Annotation( *new TextAnnotationPrivate() )
+{
+    setTextType( type );
+}
+
+TextAnnotation::TextAnnotation(TextAnnotationPrivate &dd)
+    : Annotation( dd )
+{}
+
+TextAnnotation::TextAnnotation( const QDomNode & node )
+    : Annotation( *new TextAnnotationPrivate, node )
+{
+    // loop through the whole children looking for a 'text' element
+    QDomNode subNode = node.firstChild();
+    while( subNode.isElement() )
+    {
+        QDomElement e = subNode.toElement();
+        subNode = subNode.nextSibling();
+        if ( e.tagName() != "text" )
+            continue;
+
+        // parse the attributes
+        if ( e.hasAttribute( "type" ) )
+            setTextType((TextAnnotation::TextType)e.attribute( "type" ).toInt());
+        if ( e.hasAttribute( "icon" ) )
+            setTextIcon(e.attribute( "icon" ));
+        if ( e.hasAttribute( "font" ) )
+        {
+            QFont font;
+            font.fromString( e.attribute( "font" ) );
+            setTextFont(font);
+            if ( e.hasAttribute( "fontColor" ) )
+            {
+                const QColor color = QColor(e.attribute( "fontColor" ) );
+                setTextColor(color);
+            }
+        }
+        if ( e.hasAttribute( "align" ) )
+            setInplaceAlign(e.attribute( "align" ).toInt());
+        if ( e.hasAttribute( "intent" ) )
+            setInplaceIntent((TextAnnotation::InplaceIntent)e.attribute( "intent" ).toInt());
+
+        // parse the subnodes
+        QDomNode eSubNode = e.firstChild();
+        while ( eSubNode.isElement() )
+        {
+            QDomElement ee = eSubNode.toElement();
+            eSubNode = eSubNode.nextSibling();
+
+            if ( ee.tagName() == "escapedText" )
+            {
+                setInplaceText(ee.firstChild().toCDATASection().data());
+            }
+            else if ( ee.tagName() == "callout" )
+            {
+                QVector<QPointF> points(3);
+                points[0] = QPointF(ee.attribute( "ax" ).toDouble(),
+                                    ee.attribute( "ay" ).toDouble());
+                points[1] = QPointF(ee.attribute( "bx" ).toDouble(),
+                                    ee.attribute( "by" ).toDouble());
+                points[2] = QPointF(ee.attribute( "cx" ).toDouble(),
+                                    ee.attribute( "cy" ).toDouble());
+                setCalloutPoints(points);
+            }
+        }
+
+        // loading complete
+        break;
+    }
+}
+
+TextAnnotation::~TextAnnotation()
+{
+}
+
+void TextAnnotation::store( QDomNode & node, QDomDocument & document ) const
+{
+    // store base annotation properties
+    storeBaseAnnotationProperties( node, document );
+
+    // create [text] element
+    QDomElement textElement = document.createElement( "text" );
+    node.appendChild( textElement );
+
+    // store the optional attributes
+    if ( textType() != Linked )
+        textElement.setAttribute( "type", (int)textType() );
+    if ( textIcon() != "Note" )
+        textElement.setAttribute( "icon", textIcon() );
+    if ( inplaceAlign() )
+        textElement.setAttribute( "align", inplaceAlign() );
+    if ( inplaceIntent() != Unknown )
+        textElement.setAttribute( "intent", (int)inplaceIntent() );
+
+    textElement.setAttribute( "font", textFont().toString() );
+    textElement.setAttribute( "fontColor", textColor().name() );
+
+    // Sub-Node-1 - escapedText
+    if ( !inplaceText().isEmpty() )
+    {
+        QDomElement escapedText = document.createElement( "escapedText" );
+        textElement.appendChild( escapedText );
+        QDomCDATASection textCData = document.createCDATASection( inplaceText() );
+        escapedText.appendChild( textCData );
+    }
+
+    // Sub-Node-2 - callout
+    if ( calloutPoint(0).x() != 0.0 )
+    {
+        QDomElement calloutElement = document.createElement( "callout" );
+        textElement.appendChild( calloutElement );
+        calloutElement.setAttribute( "ax", QString::number( calloutPoint(0).x() ) );
+        calloutElement.setAttribute( "ay", QString::number( calloutPoint(0).y() ) );
+        calloutElement.setAttribute( "bx", QString::number( calloutPoint(1).x() ) );
+        calloutElement.setAttribute( "by", QString::number( calloutPoint(1).y() ) );
+        calloutElement.setAttribute( "cx", QString::number( calloutPoint(2).x() ) );
+        calloutElement.setAttribute( "cy", QString::number( calloutPoint(2).y() ) );
+    }
+}
+
+Annotation::SubType TextAnnotation::subType() const
+{
+    return AText;
+}
+
+TextAnnotation::TextType TextAnnotation::textType() const
+{
+    Q_D( const TextAnnotation );
+
+    if (!d->pdfAnnot)
+        return d->textType;
+
+    return d->pdfAnnot->getType() == Annot::typeText ?
+        TextAnnotation::Linked : TextAnnotation::InPlace;
+}
+
+void TextAnnotation::setTextType( TextAnnotation::TextType type )
+{
+    Q_D( TextAnnotation );
+
+    if (!d->pdfAnnot)
+    {
+        d->textType = type;
+        return;
+    }
+
+    // Type cannot be changed if annotation is already tied
+}
+
+QString TextAnnotation::textIcon() const
+{
+    Q_D( const TextAnnotation );
+
+    if (!d->pdfAnnot)
+        return d->textIcon;
+
+    if (d->pdfAnnot->getType() == Annot::typeText)
+    {
+        const AnnotText * textann = static_cast<const AnnotText*>(d->pdfAnnot);
+        return QString::fromLatin1( textann->getIcon()->c_str() );
+    }
+
+    return QString();
+}
+
+void TextAnnotation::setTextIcon( const QString &icon )
+{
+    Q_D( TextAnnotation );
+
+    if (!d->pdfAnnot)
+    {
+        d->textIcon = icon;
+        return;
+    }
+
+    if (d->pdfAnnot->getType() == Annot::typeText)
+    {
+        AnnotText * textann = static_cast<AnnotText*>(d->pdfAnnot);
+        QByteArray encoded = icon.toLatin1();
+        GooString s(encoded.constData());
+        textann->setIcon(&s);
+    }
+}
+
+QFont TextAnnotation::textFont() const
+{
+    Q_D( const TextAnnotation );
+
+    if (!d->pdfAnnot)
+        return d->textFont;
+
+    QFont font;
+
+    if (d->pdfAnnot->getType() == Annot::typeFreeText)
+    {
+        if ( std::unique_ptr<DefaultAppearance> da{ d->getDefaultAppearanceFromNative() } )
+        {
+            font.setPointSize( da->getFontPtSize() );
+        }
+    }
+
+    return font;
+}
+
+void TextAnnotation::setTextFont( const QFont &font )
+{
+    Q_D( TextAnnotation );
+    d->textFont = font;
+    d->textColor = Qt::black;
+
+    d->setDefaultAppearanceToNative();
+}
+
+QColor TextAnnotation::textColor() const
+{
+    Q_D( const TextAnnotation );
+
+    if ( !d->pdfAnnot )
+        return d->textColor;
+
+    if ( std::unique_ptr<DefaultAppearance> da{ d->getDefaultAppearanceFromNative() } )
+    {
+        return convertAnnotColor( da->getFontColor() );
+    }
+
+    return {};
+}
+
+void TextAnnotation::setTextColor( const QColor &color )
+{
+    Q_D( TextAnnotation );
+    d->textColor = color;
+
+    d->setDefaultAppearanceToNative();
+}
+
+int TextAnnotation::inplaceAlign() const
+{
+    Q_D( const TextAnnotation );
+
+    if (!d->pdfAnnot)
+        return d->inplaceAlign;
+
+    if (d->pdfAnnot->getType() == Annot::typeFreeText)
+    {
+        const AnnotFreeText * ftextann = static_cast<const AnnotFreeText*>(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<AnnotFreeText*>(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<QPointF> points = calloutPoints();
+    if ( id < 0 || id >= points.size() )
+        return QPointF();
+    else
+        return points[id];
+}
+
+QVector<QPointF> TextAnnotation::calloutPoints() const
+{
+    Q_D( const TextAnnotation );
+
+    if (!d->pdfAnnot)
+        return d->inplaceCallout;
+
+    if (d->pdfAnnot->getType() == Annot::typeText)
+        return QVector<QPointF>();
+
+    const AnnotFreeText * ftextann = static_cast<const AnnotFreeText*>(d->pdfAnnot);
+    const AnnotCalloutLine *callout = ftextann->getCalloutLine();
+
+    if (!callout)
+        return QVector<QPointF>();
+
+    double MTX[6];
+    d->fillTransformationMTX(MTX);
+
+    const AnnotCalloutMultiLine * callout_v6 = dynamic_cast<const AnnotCalloutMultiLine*>(callout);
+    QVector<QPointF> 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<QPointF> &points )
+{
+    Q_D( TextAnnotation );
+    if (!d->pdfAnnot)
+    {
+        d->inplaceCallout = points;
+        return;
+    }
+
+    if (d->pdfAnnot->getType() != Annot::typeFreeText)
+        return;
+
+    AnnotFreeText * ftextann = static_cast<AnnotFreeText*>(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<const AnnotFreeText*>(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<AnnotFreeText*>(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<QPointF> 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<LineAnnotation*>( 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<QPointF> 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<QPointF> points = linePoints();
+    if ( points.count() > 1 )
+    {
+        QLinkedList<QPointF>::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<QPointF> LineAnnotation::linePoints() const
+{
+    Q_D( const LineAnnotation );
+
+    if (!d->pdfAnnot)
+        return d->linePoints;
+
+    double MTX[6];
+    d->fillTransformationMTX(MTX);
+
+    QLinkedList<QPointF> res;
+    if (d->pdfAnnot->getType() == Annot::typeLine)
+    {
+        const AnnotLine * lineann = static_cast<const AnnotLine*>(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<const AnnotPolygon*>(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<QPointF> &points )
+{
+    Q_D( LineAnnotation );
+
+    if (!d->pdfAnnot)
+    {
+        d->linePoints = points;
+        return;
+    }
+
+    if (d->pdfAnnot->getType() == Annot::typeLine)
+    {
+        AnnotLine *lineann = static_cast<AnnotLine*>(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<AnnotPolygon*>(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<const AnnotLine*>(d->pdfAnnot);
+        return (LineAnnotation::TermStyle)lineann->getStartStyle();
+    }
+    else
+    {
+        const AnnotPolygon * polyann = static_cast<const AnnotPolygon*>(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<AnnotLine*>(d->pdfAnnot);
+        lineann->setStartEndStyle((AnnotLineEndingStyle)style, lineann->getEndStyle());
+    }
+    else
+    {
+        AnnotPolygon *polyann = static_cast<AnnotPolygon*>(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<const AnnotLine*>(d->pdfAnnot);
+        return (LineAnnotation::TermStyle)lineann->getEndStyle();
+    }
+    else
+    {
+        const AnnotPolygon * polyann = static_cast<const AnnotPolygon*>(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<AnnotLine*>(d->pdfAnnot);
+        lineann->setStartEndStyle(lineann->getStartStyle(), (AnnotLineEndingStyle)style);
+    }
+    else
+    {
+        AnnotPolygon *polyann = static_cast<AnnotPolygon*>(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<AnnotPolygon*>(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<const AnnotLine*>(d->pdfAnnot);
+        c = lineann->getInteriorColor();
+    }
+    else
+    {
+        const AnnotPolygon * polyann = static_cast<const AnnotPolygon*>(d->pdfAnnot);
+        c = polyann->getInteriorColor();
+    }
+
+    return convertAnnotColor(c);
+}
+
+void LineAnnotation::setLineInnerColor( const QColor &color )
+{
+    Q_D( LineAnnotation );
+
+    if (!d->pdfAnnot)
+    {
+        d->lineInnerColor = color;
+        return;
+    }
+
+    auto c = convertQColor(color);
+
+    if (d->pdfAnnot->getType() == Annot::typeLine)
+    {
+        AnnotLine *lineann = static_cast<AnnotLine*>(d->pdfAnnot);
+        lineann->setInteriorColor(std::move(c));
+    }
+    else
+    {
+        AnnotPolygon *polyann = static_cast<AnnotPolygon*>(d->pdfAnnot);
+        polyann->setInteriorColor(std::move(c));
+    }
+}
+
+double LineAnnotation::lineLeadingForwardPoint() const
+{
+    Q_D( const LineAnnotation );
+
+    if (!d->pdfAnnot)
+        return d->lineLeadingFwdPt;
+
+    if (d->pdfAnnot->getType() == Annot::typeLine)
+    {
+        const AnnotLine * lineann = static_cast<const AnnotLine*>(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<AnnotLine*>(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<const AnnotLine*>(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<AnnotLine*>(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<const AnnotLine*>(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<AnnotLine*>(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<const AnnotLine*>(d->pdfAnnot);
+        return (LineAnnotation::LineIntent)( lineann->getIntent() + 1 );
+    }
+    else
+    {
+        const AnnotPolygon * polyann = static_cast<const AnnotPolygon*>(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<AnnotLine*>(d->pdfAnnot);
+        lineann->setIntent((AnnotLine::AnnotLineIntent)( intent - 1 ));
+    }
+    else
+    {
+        AnnotPolygon * polyann = static_cast<AnnotPolygon*>(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<GeomAnnotation*>( 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<AnnotGeometry*>(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<const AnnotGeometry*>(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<AnnotGeometry*>(d->pdfAnnot);
+    geomann->setInteriorColor(convertQColor( color ));
+}
+
+
+/** HighlightAnnotation [Annotation] */
+class HighlightAnnotationPrivate : public AnnotationPrivate
+{
+    public:
+        HighlightAnnotationPrivate();
+        Annotation * makeAlias();
+        Annot* createNativeAnnot(::Page *destPage, DocumentData *doc);
+
+        // data fields
+        HighlightAnnotation::HighlightType highlightType;
+        QList< HighlightAnnotation::Quad > highlightQuads; // not empty
+
+        // helpers
+        static Annot::AnnotSubtype toAnnotSubType( HighlightAnnotation::HighlightType type );
+        QList< HighlightAnnotation::Quad > fromQuadrilaterals(AnnotQuadrilaterals *quads) const;
+        AnnotQuadrilaterals * toQuadrilaterals(const QList< HighlightAnnotation::Quad > &quads) const;
+};
+
+HighlightAnnotationPrivate::HighlightAnnotationPrivate()
+    : AnnotationPrivate(), highlightType( HighlightAnnotation::Highlight )
+{
+}
+
+Annotation * HighlightAnnotationPrivate::makeAlias()
+{
+    return new HighlightAnnotation(*this);
+}
+
+Annot::AnnotSubtype HighlightAnnotationPrivate::toAnnotSubType( HighlightAnnotation::HighlightType type )
+{
+    switch (type)
+    {
+        default: // HighlightAnnotation::Highlight:
+            return Annot::typeHighlight;
+        case HighlightAnnotation::Underline:
+            return Annot::typeUnderline;
+        case HighlightAnnotation::Squiggly:
+            return Annot::typeSquiggly;
+        case HighlightAnnotation::StrikeOut:
+            return Annot::typeStrikeOut;
+    }
+}
+
+QList< HighlightAnnotation::Quad > HighlightAnnotationPrivate::fromQuadrilaterals(AnnotQuadrilaterals *hlquads) const
+{
+    QList< HighlightAnnotation::Quad > quads;
+
+    if ( !hlquads || !hlquads->getQuadrilateralsLength() )
+        return quads;
+    const int quadsCount = hlquads->getQuadrilateralsLength();
+
+    double MTX[6];
+    fillTransformationMTX(MTX);
+
+    quads.reserve(quadsCount);
+    for (int q = 0; q < quadsCount; ++q)
+    {
+        HighlightAnnotation::Quad quad;
+        XPDFReader::transform( MTX, hlquads->getX1( q ), hlquads->getY1( q ), quad.points[ 0 ] );
+        XPDFReader::transform( MTX, hlquads->getX2( q ), hlquads->getY2( q ), quad.points[ 1 ] );
+        XPDFReader::transform( MTX, hlquads->getX3( q ), hlquads->getY3( q ), quad.points[ 2 ] );
+        XPDFReader::transform( MTX, hlquads->getX4( q ), hlquads->getY4( q ), quad.points[ 3 ] );
+        // ### PDF1.6 specs says that point are in ccw order, but in fact
+        // points 3 and 4 are swapped in every PDF around!
+        QPointF tmpPoint = quad.points[ 2 ];
+        quad.points[ 2 ] = quad.points[ 3 ];
+        quad.points[ 3 ] = tmpPoint;
+        // initialize other properties and append quad
+        quad.capStart = true;       // unlinked quads are always capped
+        quad.capEnd = true;         // unlinked quads are always capped
+        quad.feather = 0.1;         // default feather
+        quads.append( quad );
+    }
+
+    return quads;
+}
+
+AnnotQuadrilaterals * HighlightAnnotationPrivate::toQuadrilaterals(const QList< HighlightAnnotation::Quad > &quads) const
+{
+    const int count = quads.size();
+    auto ac = std::make_unique<AnnotQuadrilaterals::AnnotQuadrilateral[]>(count);
+
+    double MTX[6];
+    fillTransformationMTX(MTX);
+
+    int pos = 0;
+    foreach (const HighlightAnnotation::Quad &q, quads)
+    {
+        double x1, y1, x2, y2, x3, y3, x4, y4;
+        XPDFReader::invTransform( MTX, q.points[0], x1, y1 );
+        XPDFReader::invTransform( MTX, q.points[1], x2, y2 );
+        // Swap points 3 and 4 (see HighlightAnnotationPrivate::fromQuadrilaterals)
+        XPDFReader::invTransform( MTX, q.points[3], x3, y3 );
+        XPDFReader::invTransform( MTX, q.points[2], x4, y4 );
+        ac[pos++] = AnnotQuadrilaterals::AnnotQuadrilateral(x1, y1, x2, y2, x3, y3, x4, y4);
+    }
+
+    return new AnnotQuadrilaterals(std::move(ac), count);
+}
+
+Annot* HighlightAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
+{
+    // Setters are defined in the public class
+    HighlightAnnotation *q = static_cast<HighlightAnnotation*>( 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<HighlightAnnotation::Quad> 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<HighlightAnnotation::Quad> 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<AnnotTextMarkup*>(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<AnnotTextMarkup*>(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<AnnotTextMarkup*>(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<StampAnnotation*>( 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<const AnnotStamp*>(d->pdfAnnot);
+    return QString::fromLatin1( stampann->getIcon()->c_str() );
+}
+
+void StampAnnotation::setStampIconName( const QString &name )
+{
+    Q_D( StampAnnotation );
+
+    if (!d->pdfAnnot)
+    {
+        d->stampIconName = name;
+        return;
+    }
+
+    AnnotStamp * stampann = static_cast<AnnotStamp*>(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<QPointF> > inkPaths;
+
+        // helper
+        AnnotPath **toAnnotPaths(const QList< QLinkedList<QPointF> > &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<QPointF> > &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<InkAnnotation*>( 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<QPointF> > 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<QPointF> 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<QPointF> > paths = inkPaths();
+    if ( paths.count() < 1 )
+        return;
+    QList< QLinkedList<QPointF> >::const_iterator pIt = paths.begin(), pEnd = paths.end();
+    for ( ; pIt != pEnd; ++pIt )
+    {
+        QDomElement pathElement = document.createElement( "path" );
+        inkElement.appendChild( pathElement );
+        const QLinkedList<QPointF> & path = *pIt;
+        QLinkedList<QPointF>::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<QPointF> > InkAnnotation::inkPaths() const
+{
+    Q_D( const InkAnnotation );
+
+    if (!d->pdfAnnot)
+        return d->inkPaths;
+
+    const AnnotInk * inkann = static_cast<const AnnotInk *>(d->pdfAnnot);
+
+    const AnnotPath * const* paths = inkann->getInkList();
+    if ( !paths || !inkann->getInkListLength() )
+        return QList< QLinkedList<QPointF> >();
+
+    double MTX[6];
+    d->fillTransformationMTX(MTX);
+
+    const int pathsNumber = inkann->getInkListLength();
+    QList< QLinkedList<QPointF> > inkPaths;
+    inkPaths.reserve(pathsNumber);
+    for (int m = 0; m < pathsNumber; ++m)
+    {
+        // transform each path in a list of normalized points ..
+        QLinkedList<QPointF> 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<QPointF> > &paths )
+{
+    Q_D( InkAnnotation );
+
+    if (!d->pdfAnnot)
+    {
+        d->inkPaths = paths;
+        return;
+    }
+
+    AnnotInk * inkann = static_cast<AnnotInk*>(d->pdfAnnot);
+    AnnotPath **annotpaths = d->toAnnotPaths(paths);
+    const int pathsNumber = paths.size();
+    inkann->setInkList(annotpaths, pathsNumber);
+
+    for (int i = 0; i < pathsNumber; ++i)
+        delete annotpaths[i];
+    delete[] annotpaths;
+}
+
+
+/** LinkAnnotation [Annotation] */
+class LinkAnnotationPrivate : public AnnotationPrivate
+{
+    public:
+        LinkAnnotationPrivate();
+        ~LinkAnnotationPrivate();
+        Annotation * makeAlias();
+        Annot* createNativeAnnot(::Page *destPage, DocumentData *doc);
+
+        // data fields
+        Link * linkDestination;
+        LinkAnnotation::HighlightMode linkHLMode;
+        QPointF linkRegion[4];
+};
+
+LinkAnnotationPrivate::LinkAnnotationPrivate()
+    : AnnotationPrivate(), linkDestination( nullptr ), linkHLMode( LinkAnnotation::Invert )
+{
+}
+
+LinkAnnotationPrivate::~LinkAnnotationPrivate()
+{
+    delete linkDestination;
+}
+
+Annotation * LinkAnnotationPrivate::makeAlias()
+{
+    return new LinkAnnotation(*this);
+}
+
+Annot* LinkAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
+{
+    return nullptr; // Not implemented
+}
+
+LinkAnnotation::LinkAnnotation()
+    : Annotation( *new LinkAnnotationPrivate() )
+{}
+
+LinkAnnotation::LinkAnnotation(LinkAnnotationPrivate &dd)
+    : Annotation( dd )
+{}
+
+LinkAnnotation::LinkAnnotation( const QDomNode & node )
+    : Annotation( *new LinkAnnotationPrivate(), node )
+{
+    // loop through the whole children looking for a 'link' element
+    QDomNode subNode = node.firstChild();
+    while( subNode.isElement() )
+    {
+        QDomElement e = subNode.toElement();
+        subNode = subNode.nextSibling();
+        if ( e.tagName() != "link" )
+            continue;
+
+        // parse the attributes
+        if ( e.hasAttribute( "hlmode" ) )
+            setLinkHighlightMode((LinkAnnotation::HighlightMode)e.attribute( "hlmode" ).toInt());
+
+        // parse all 'quad' subnodes
+        QDomNode quadNode = e.firstChild();
+        for ( ; quadNode.isElement(); quadNode = quadNode.nextSibling() )
+        {
+            QDomElement qe = quadNode.toElement();
+            if ( qe.tagName() == "quad" )
+            {
+                setLinkRegionPoint(0, QPointF(qe.attribute( "ax", "0.0" ).toDouble(),
+                                              qe.attribute( "ay", "0.0" ).toDouble()));
+                setLinkRegionPoint(1, QPointF(qe.attribute( "bx", "0.0" ).toDouble(),
+                                              qe.attribute( "by", "0.0" ).toDouble()));
+                setLinkRegionPoint(2, QPointF(qe.attribute( "cx", "0.0" ).toDouble(),
+                                              qe.attribute( "cy", "0.0" ).toDouble()));
+                setLinkRegionPoint(3, QPointF(qe.attribute( "dx", "0.0" ).toDouble(),
+                                              qe.attribute( "dy", "0.0" ).toDouble()));
+            }
+            else if ( qe.tagName() == "link" )
+            {
+                QString type = qe.attribute( "type" );
+                if ( type == "GoTo" )
+                {
+                    Poppler::LinkGoto * go = new Poppler::LinkGoto( QRect(), qe.attribute( "filename" ), LinkDestination( qe.attribute( "destination" ) ) );
+                    setLinkDestination(go);
+                }
+                else if ( type == "Exec" )
+                {
+                    Poppler::LinkExecute * exec = new Poppler::LinkExecute( QRect(), qe.attribute( "filename" ), qe.attribute( "parameters" ) );
+                    setLinkDestination(exec);
+                }
+                else if ( type == "Browse" )
+                {
+                    Poppler::LinkBrowse * browse = new Poppler::LinkBrowse( QRect(), qe.attribute( "url" ) );
+                    setLinkDestination(browse);
+                }
+                else if ( type == "Action" )
+                {
+                    Poppler::LinkAction::ActionType act;
+                    QString actString = qe.attribute( "action" );
+                    bool found = true;
+                    if ( actString == "PageFirst" )
+                        act = Poppler::LinkAction::PageFirst;
+                    else if ( actString == "PagePrev" )
+                        act = Poppler::LinkAction::PagePrev;
+                    else if ( actString == "PageNext" )
+                        act = Poppler::LinkAction::PageNext;
+                    else if ( actString == "PageLast" )
+                        act = Poppler::LinkAction::PageLast;
+                    else if ( actString == "HistoryBack" )
+                        act = Poppler::LinkAction::HistoryBack;
+                    else if ( actString == "HistoryForward" )
+                        act = Poppler::LinkAction::HistoryForward;
+                    else if ( actString == "Quit" )
+                        act = Poppler::LinkAction::Quit;
+                    else if ( actString == "Presentation" )
+                        act = Poppler::LinkAction::Presentation;
+                    else if ( actString == "EndPresentation" )
+                        act = Poppler::LinkAction::EndPresentation;
+                    else if ( actString == "Find" )
+                        act = Poppler::LinkAction::Find;
+                    else if ( actString == "GoToPage" )
+                        act = Poppler::LinkAction::GoToPage;
+                    else if ( actString == "Close" )
+                        act = Poppler::LinkAction::Close;
+                    else if ( actString == "Print" )
+                        act = Poppler::LinkAction::Print;
+                    else
+                        found = false;
+                    if (found)
+                    {
+                        Poppler::LinkAction * action = new Poppler::LinkAction( QRect(), act );
+                        setLinkDestination(action);
+                    }
+                }
+#if 0
+                else if ( type == "Movie" )
+                {
+                    Poppler::LinkMovie * movie = new Poppler::LinkMovie( QRect() );
+                    setLinkDestination(movie);
+                }
+#endif
+            }
+        }
+
+        // loading complete
+        break;
+    }
+}
+
+LinkAnnotation::~LinkAnnotation()
+{
+}
+
+void LinkAnnotation::store( QDomNode & node, QDomDocument & document ) const
+{
+    // store base annotation properties
+    storeBaseAnnotationProperties( node, document );
+
+    // create [hl] element
+    QDomElement linkElement = document.createElement( "link" );
+    node.appendChild( linkElement );
+
+    // append the optional attributes
+    if ( linkHighlightMode() != Invert )
+        linkElement.setAttribute( "hlmode", (int)linkHighlightMode() );
+
+    // saving region
+    QDomElement quadElement = document.createElement( "quad" );
+    linkElement.appendChild( quadElement );
+    quadElement.setAttribute( "ax", QString::number( linkRegionPoint(0).x() ) );
+    quadElement.setAttribute( "ay", QString::number( linkRegionPoint(0).y() ) );
+    quadElement.setAttribute( "bx", QString::number( linkRegionPoint(1).x() ) );
+    quadElement.setAttribute( "by", QString::number( linkRegionPoint(1).y() ) );
+    quadElement.setAttribute( "cx", QString::number( linkRegionPoint(2).x() ) );
+    quadElement.setAttribute( "cy", QString::number( linkRegionPoint(2).y() ) );
+    quadElement.setAttribute( "dx", QString::number( linkRegionPoint(3).x() ) );
+    quadElement.setAttribute( "dy", QString::number( linkRegionPoint(3).y() ) );
+
+    // saving link
+    QDomElement hyperlinkElement = document.createElement( "link" );
+    linkElement.appendChild( hyperlinkElement );
+    if ( linkDestination() )
+    {
+        switch( linkDestination()->linkType() )
+        {
+            case Poppler::Link::Goto:
+            {
+                Poppler::LinkGoto * go = static_cast< Poppler::LinkGoto * >( linkDestination() );
+                hyperlinkElement.setAttribute( "type", "GoTo" );
+                hyperlinkElement.setAttribute( "filename", go->fileName() );
+                hyperlinkElement.setAttribute( "destionation", go->destination().toString() ); // TODO Remove for poppler 0.28
+                hyperlinkElement.setAttribute( "destination", go->destination().toString() );
+                break;
+            }
+            case Poppler::Link::Execute:
+            {
+                Poppler::LinkExecute * exec = static_cast< Poppler::LinkExecute * >( linkDestination() );
+                hyperlinkElement.setAttribute( "type", "Exec" );
+                hyperlinkElement.setAttribute( "filename", exec->fileName() );
+                hyperlinkElement.setAttribute( "parameters", exec->parameters() );
+                break;
+            }
+            case Poppler::Link::Browse:
+            {
+                Poppler::LinkBrowse * browse = static_cast< Poppler::LinkBrowse * >( linkDestination() );
+                hyperlinkElement.setAttribute( "type", "Browse" );
+                hyperlinkElement.setAttribute( "url", browse->url() );
+                break;
+            }
+            case Poppler::Link::Action:
+            {
+                Poppler::LinkAction * action = static_cast< Poppler::LinkAction * >( linkDestination() );
+                hyperlinkElement.setAttribute( "type", "Action" );
+                switch ( action->actionType() )
+                {
+                    case Poppler::LinkAction::PageFirst:
+                        hyperlinkElement.setAttribute( "action", "PageFirst" );
+                        break;
+                    case Poppler::LinkAction::PagePrev:
+                        hyperlinkElement.setAttribute( "action", "PagePrev" );
+                        break;
+                    case Poppler::LinkAction::PageNext:
+                        hyperlinkElement.setAttribute( "action", "PageNext" );
+                        break;
+                    case Poppler::LinkAction::PageLast:
+                        hyperlinkElement.setAttribute( "action", "PageLast" );
+                        break;
+                    case Poppler::LinkAction::HistoryBack:
+                        hyperlinkElement.setAttribute( "action", "HistoryBack" );
+                        break;
+                    case Poppler::LinkAction::HistoryForward:
+                        hyperlinkElement.setAttribute( "action", "HistoryForward" );
+                        break;
+                    case Poppler::LinkAction::Quit:
+                        hyperlinkElement.setAttribute( "action", "Quit" );
+                        break;
+                    case Poppler::LinkAction::Presentation:
+                        hyperlinkElement.setAttribute( "action", "Presentation" );
+                        break;
+                    case Poppler::LinkAction::EndPresentation:
+                        hyperlinkElement.setAttribute( "action", "EndPresentation" );
+                        break;
+                    case Poppler::LinkAction::Find:
+                        hyperlinkElement.setAttribute( "action", "Find" );
+                        break;
+                    case Poppler::LinkAction::GoToPage:
+                        hyperlinkElement.setAttribute( "action", "GoToPage" );
+                        break;
+                    case Poppler::LinkAction::Close:
+                        hyperlinkElement.setAttribute( "action", "Close" );
+                        break;
+                    case Poppler::LinkAction::Print:
+                        hyperlinkElement.setAttribute( "action", "Print" );
+                        break;
+                }
+                break;
+            }
+            case Poppler::Link::Movie:
+            {
+                hyperlinkElement.setAttribute( "type", "Movie" );
+                break;
+            }
+            case Poppler::Link::Rendition:
+            {
+                hyperlinkElement.setAttribute( "type", "Rendition" );
+                break;
+            }
+            case Poppler::Link::Sound:
+            {
+                // FIXME: implement me
+                break;
+            }
+            case Poppler::Link::None:
+                break;
+        }
+    }
+}
+
+Annotation::SubType LinkAnnotation::subType() const
+{
+    return ALink;
+}
+
+Link* LinkAnnotation::linkDestination() const
+{
+    Q_D( const LinkAnnotation );
+    return d->linkDestination;
+}
+
+void LinkAnnotation::setLinkDestination( Link *link )
+{
+    Q_D( LinkAnnotation );
+    delete d->linkDestination;
+    d->linkDestination = link;
+}
+
+LinkAnnotation::HighlightMode LinkAnnotation::linkHighlightMode() const
+{
+    Q_D( const LinkAnnotation );
+    return d->linkHLMode;
+}
+
+void LinkAnnotation::setLinkHighlightMode( LinkAnnotation::HighlightMode mode )
+{
+    Q_D( LinkAnnotation );
+    d->linkHLMode = mode;
+}
+
+QPointF LinkAnnotation::linkRegionPoint( int id ) const
+{
+    if ( id < 0 || id >= 4 )
+        return QPointF();
+
+    Q_D( const LinkAnnotation );
+    return d->linkRegion[id];
+}
+
+void LinkAnnotation::setLinkRegionPoint( int id, const QPointF &point )
+{
+    if ( id < 0 || id >= 4 )
+        return;
+
+    Q_D( LinkAnnotation );
+    d->linkRegion[id] = point;
+}
+
+/** CaretAnnotation [Annotation] */
+class CaretAnnotationPrivate : public AnnotationPrivate
+{
+    public:
+        CaretAnnotationPrivate();
+        Annotation * makeAlias();
+        Annot* createNativeAnnot(::Page *destPage, DocumentData *doc);
+
+        // data fields
+        CaretAnnotation::CaretSymbol symbol;
+};
+
+static QString caretSymbolToString( CaretAnnotation::CaretSymbol symbol )
+{
+    switch ( symbol )
+    {
+        case CaretAnnotation::None:
+            return QString::fromLatin1( "None" );
+        case CaretAnnotation::P:
+            return QString::fromLatin1( "P" );
+    }
+    return QString();
+}
+
+static CaretAnnotation::CaretSymbol caretSymbolFromString( const QString &symbol )
+{
+    if ( symbol == QLatin1String( "None" ) )
+        return CaretAnnotation::None;
+    else if ( symbol == QLatin1String( "P" ) )
+        return CaretAnnotation::P;
+    return CaretAnnotation::None;
+}
+
+CaretAnnotationPrivate::CaretAnnotationPrivate()
+    : AnnotationPrivate(), symbol( CaretAnnotation::None )
+{
+}
+
+Annotation * CaretAnnotationPrivate::makeAlias()
+{
+    return new CaretAnnotation(*this);
+}
+
+Annot* CaretAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
+{
+    // Setters are defined in the public class
+    CaretAnnotation *q = static_cast<CaretAnnotation*>( 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<const AnnotCaret *>(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<AnnotCaret *>(d->pdfAnnot);
+    caretann->setSymbol((AnnotCaret::AnnotCaretSymbol)symbol);
+}
+
+/** FileAttachmentAnnotation [Annotation] */
+class FileAttachmentAnnotationPrivate : public AnnotationPrivate
+{
+    public:
+        FileAttachmentAnnotationPrivate();
+        ~FileAttachmentAnnotationPrivate();
+        Annotation * makeAlias();
+        Annot* createNativeAnnot(::Page *destPage, DocumentData *doc);
+
+        // data fields
+        QString icon;
+        EmbeddedFile *embfile;
+};
+
+FileAttachmentAnnotationPrivate::FileAttachmentAnnotationPrivate()
+    : AnnotationPrivate(), icon( "PushPin" ), embfile( nullptr )
+{
+}
+
+FileAttachmentAnnotationPrivate::~FileAttachmentAnnotationPrivate()
+{
+    delete embfile;
+}
+
+Annotation * FileAttachmentAnnotationPrivate::makeAlias()
+{
+    return new FileAttachmentAnnotation(*this);
+}
+
+Annot* FileAttachmentAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
+{
+    return nullptr; // Not implemented
+}
+
+FileAttachmentAnnotation::FileAttachmentAnnotation()
+    : Annotation( *new FileAttachmentAnnotationPrivate() )
+{
+}
+
+FileAttachmentAnnotation::FileAttachmentAnnotation(FileAttachmentAnnotationPrivate &dd)
+    : Annotation( dd )
+{
+}
+
+FileAttachmentAnnotation::FileAttachmentAnnotation( const QDomNode & node )
+    : Annotation( *new FileAttachmentAnnotationPrivate(), node )
+{
+    // loop through the whole children looking for a 'fileattachment' element
+    QDomNode subNode = node.firstChild();
+    while( subNode.isElement() )
+    {
+        QDomElement e = subNode.toElement();
+        subNode = subNode.nextSibling();
+        if ( e.tagName() != "fileattachment" )
+            continue;
+
+        // loading complete
+        break;
+    }
+}
+
+FileAttachmentAnnotation::~FileAttachmentAnnotation()
+{
+}
+
+void FileAttachmentAnnotation::store( QDomNode & node, QDomDocument & document ) const
+{
+    // store base annotation properties
+    storeBaseAnnotationProperties( node, document );
+
+    // create [fileattachment] element
+    QDomElement fileAttachmentElement = document.createElement( "fileattachment" );
+    node.appendChild( fileAttachmentElement );
+}
+
+Annotation::SubType FileAttachmentAnnotation::subType() const
+{
+    return AFileAttachment;
+}
+
+QString FileAttachmentAnnotation::fileIconName() const
+{
+    Q_D( const FileAttachmentAnnotation );
+    return d->icon;
+}
+
+void FileAttachmentAnnotation::setFileIconName( const QString &icon )
+{
+    Q_D( FileAttachmentAnnotation );
+    d->icon = icon;
+}
+
+EmbeddedFile* FileAttachmentAnnotation::embeddedFile() const
+{
+    Q_D( const FileAttachmentAnnotation );
+    return d->embfile;
+}
+
+void FileAttachmentAnnotation::setEmbeddedFile( EmbeddedFile *ef )
+{
+    Q_D( FileAttachmentAnnotation );
+    d->embfile = ef;
+}
+
+/** SoundAnnotation [Annotation] */
+class SoundAnnotationPrivate : public AnnotationPrivate
+{
+    public:
+        SoundAnnotationPrivate();
+        ~SoundAnnotationPrivate();
+        Annotation * makeAlias();
+        Annot* createNativeAnnot(::Page *destPage, DocumentData *doc);
+
+        // data fields
+        QString icon;
+        SoundObject *sound;
+};
+
+SoundAnnotationPrivate::SoundAnnotationPrivate()
+    : AnnotationPrivate(), icon( "Speaker" ), sound( nullptr )
+{
+}
+
+SoundAnnotationPrivate::~SoundAnnotationPrivate()
+{
+    delete sound;
+}
+
+Annotation * SoundAnnotationPrivate::makeAlias()
+{
+    return new SoundAnnotation(*this);
+}
+
+Annot* SoundAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
+{
+    return nullptr; // Not implemented
+}
+
+SoundAnnotation::SoundAnnotation()
+    : Annotation( *new SoundAnnotationPrivate() )
+{
+}
+
+SoundAnnotation::SoundAnnotation(SoundAnnotationPrivate &dd)
+    : Annotation( dd )
+{
+}
+
+SoundAnnotation::SoundAnnotation( const QDomNode & node )
+    : Annotation( *new SoundAnnotationPrivate(), node )
+{
+    // loop through the whole children looking for a 'sound' element
+    QDomNode subNode = node.firstChild();
+    while( subNode.isElement() )
+    {
+        QDomElement e = subNode.toElement();
+        subNode = subNode.nextSibling();
+        if ( e.tagName() != "sound" )
+            continue;
+
+        // loading complete
+        break;
+    }
+}
+
+SoundAnnotation::~SoundAnnotation()
+{
+}
+
+void SoundAnnotation::store( QDomNode & node, QDomDocument & document ) const
+{
+    // store base annotation properties
+    storeBaseAnnotationProperties( node, document );
+
+    // create [sound] element
+    QDomElement soundElement = document.createElement( "sound" );
+    node.appendChild( soundElement );
+}
+
+Annotation::SubType SoundAnnotation::subType() const
+{
+    return ASound;
+}
+
+QString SoundAnnotation::soundIconName() const
+{
+    Q_D( const SoundAnnotation );
+    return d->icon;
+}
+
+void SoundAnnotation::setSoundIconName( const QString &icon )
+{
+    Q_D( SoundAnnotation );
+    d->icon = icon;
+}
+
+SoundObject* SoundAnnotation::sound() const
+{
+    Q_D( const SoundAnnotation );
+    return d->sound;
+}
+
+void SoundAnnotation::setSound( SoundObject *s )
+{
+    Q_D( SoundAnnotation );
+    d->sound = s;
+}
+
+/** MovieAnnotation [Annotation] */
+class MovieAnnotationPrivate : public AnnotationPrivate
+{
+    public:
+        MovieAnnotationPrivate();
+        ~MovieAnnotationPrivate();
+        Annotation * makeAlias();
+        Annot* createNativeAnnot(::Page *destPage, DocumentData *doc);
+
+        // data fields
+        MovieObject *movie;
+        QString title;
+};
+
+MovieAnnotationPrivate::MovieAnnotationPrivate()
+    : AnnotationPrivate(), movie( nullptr )
+{
+}
+
+MovieAnnotationPrivate::~MovieAnnotationPrivate()
+{
+    delete movie;
+}
+
+Annotation * MovieAnnotationPrivate::makeAlias()
+{
+    return new MovieAnnotation(*this);
+}
+
+Annot* MovieAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
+{
+    return nullptr; // Not implemented
+}
+
+MovieAnnotation::MovieAnnotation()
+    : Annotation( *new MovieAnnotationPrivate() )
+{
+}
+
+MovieAnnotation::MovieAnnotation(MovieAnnotationPrivate &dd)
+    : Annotation( dd )
+{
+}
+
+MovieAnnotation::MovieAnnotation( const QDomNode & node )
+    : Annotation( *new MovieAnnotationPrivate(), node )
+{
+    // loop through the whole children looking for a 'movie' element
+    QDomNode subNode = node.firstChild();
+    while( subNode.isElement() )
+    {
+        QDomElement e = subNode.toElement();
+        subNode = subNode.nextSibling();
+        if ( e.tagName() != "movie" )
+            continue;
+
+        // loading complete
+        break;
+    }
+}
+
+MovieAnnotation::~MovieAnnotation()
+{
+}
+
+void MovieAnnotation::store( QDomNode & node, QDomDocument & document ) const
+{
+    // store base annotation properties
+    storeBaseAnnotationProperties( node, document );
+
+    // create [movie] element
+    QDomElement movieElement = document.createElement( "movie" );
+    node.appendChild( movieElement );
+}
+
+Annotation::SubType MovieAnnotation::subType() const
+{
+    return AMovie;
+}
+
+MovieObject* MovieAnnotation::movie() const
+{
+    Q_D( const MovieAnnotation );
+    return d->movie;
+}
+
+void MovieAnnotation::setMovie( MovieObject *movie )
+{
+    Q_D( MovieAnnotation );
+    d->movie = movie;
+}
+
+QString MovieAnnotation::movieTitle() const
+{
+    Q_D( const MovieAnnotation );
+    return d->title;
+}
+
+void MovieAnnotation::setMovieTitle( const QString &title )
+{
+    Q_D( MovieAnnotation );
+    d->title = title;
+}
+
+/** ScreenAnnotation [Annotation] */
+class ScreenAnnotationPrivate : public AnnotationPrivate
+{
+    public:
+        ScreenAnnotationPrivate();
+        ~ScreenAnnotationPrivate();
+        Annotation * makeAlias();
+        Annot* createNativeAnnot(::Page *destPage, DocumentData *doc);
+
+        // data fields
+        LinkRendition *action;
+        QString title;
+};
+
+ScreenAnnotationPrivate::ScreenAnnotationPrivate()
+    : AnnotationPrivate(), action( nullptr )
+{
+}
+
+ScreenAnnotationPrivate::~ScreenAnnotationPrivate()
+{
+    delete action;
+}
+
+ScreenAnnotation::ScreenAnnotation(ScreenAnnotationPrivate &dd)
+    : Annotation( dd )
+{}
+
+Annotation * ScreenAnnotationPrivate::makeAlias()
+{
+    return new ScreenAnnotation(*this);
+}
+
+Annot* ScreenAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
+{
+    return nullptr; // Not implemented
+}
+
+ScreenAnnotation::ScreenAnnotation()
+    : Annotation( *new ScreenAnnotationPrivate() )
+{
+}
+
+ScreenAnnotation::~ScreenAnnotation()
+{
+}
+
+void ScreenAnnotation::store( QDomNode & node, QDomDocument & document ) const
+{
+    // store base annotation properties
+    storeBaseAnnotationProperties( node, document );
+
+    // create [screen] element
+    QDomElement screenElement = document.createElement( "screen" );
+    node.appendChild( screenElement );
+}
+
+Annotation::SubType ScreenAnnotation::subType() const
+{
+    return AScreen;
+}
+
+LinkRendition* ScreenAnnotation::action() const
+{
+    Q_D( const ScreenAnnotation );
+    return d->action;
+}
+
+void ScreenAnnotation::setAction( LinkRendition *action )
+{
+    Q_D( ScreenAnnotation );
+    d->action = action;
+}
+
+QString ScreenAnnotation::screenTitle() const
+{
+    Q_D( const ScreenAnnotation );
+    return d->title;
+}
+
+void ScreenAnnotation::setScreenTitle( const QString &title )
+{
+    Q_D( ScreenAnnotation );
+    d->title = title;
+}
+
+Link* ScreenAnnotation::additionalAction( AdditionalActionType type ) const
+{
+    Q_D( const ScreenAnnotation );
+    return d->additionalAction( type );
+}
+
+/** WidgetAnnotation [Annotation] */
+class WidgetAnnotationPrivate : public AnnotationPrivate
+{
+    public:
+        Annotation * makeAlias();
+        Annot* createNativeAnnot(::Page *destPage, DocumentData *doc);
+};
+
+Annotation * WidgetAnnotationPrivate::makeAlias()
+{
+    return new WidgetAnnotation(*this);
+}
+
+Annot* WidgetAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
+{
+    return nullptr; // Not implemented
+}
+
+WidgetAnnotation::WidgetAnnotation(WidgetAnnotationPrivate &dd)
+    : Annotation( dd )
+{}
+
+WidgetAnnotation::WidgetAnnotation()
+    : Annotation( *new WidgetAnnotationPrivate() )
+{
+}
+
+WidgetAnnotation::~WidgetAnnotation()
+{
+}
+
+void WidgetAnnotation::store( QDomNode & node, QDomDocument & document ) const
+{
+    // store base annotation properties
+    storeBaseAnnotationProperties( node, document );
+
+    // create [widget] element
+    QDomElement widgetElement = document.createElement( "widget" );
+    node.appendChild( widgetElement );
+}
+
+Annotation::SubType WidgetAnnotation::subType() const
+{
+    return AWidget;
+}
+
+Link* WidgetAnnotation::additionalAction( AdditionalActionType type ) const
+{
+    Q_D( const WidgetAnnotation );
+    return d->additionalAction( type );
+}
+
+/** RichMediaAnnotation [Annotation] */
+class RichMediaAnnotation::Params::Private
+{
+    public:
+        Private() {}
+
+        QString flashVars;
+};
+
+RichMediaAnnotation::Params::Params()
+    : d( new Private )
+{
+}
+
+RichMediaAnnotation::Params::~Params()
+{
+}
+
+void RichMediaAnnotation::Params::setFlashVars( const QString &flashVars )
+{
+    d->flashVars = flashVars;
+}
+
+QString RichMediaAnnotation::Params::flashVars() const
+{
+    return d->flashVars;
+}
+
+
+class RichMediaAnnotation::Instance::Private
+{
+    public:
+        Private()
+            : params( nullptr )
+        {
+        }
+
+        ~Private()
+        {
+            delete params;
+        }
+
+        RichMediaAnnotation::Instance::Type type;
+        RichMediaAnnotation::Params *params;
+};
+
+RichMediaAnnotation::Instance::Instance()
+    : d( new Private )
+{
+}
+
+RichMediaAnnotation::Instance::~Instance()
+{
+}
+
+void RichMediaAnnotation::Instance::setType( Type type )
+{
+    d->type = type;
+}
+
+RichMediaAnnotation::Instance::Type RichMediaAnnotation::Instance::type() const
+{
+    return d->type;
+}
+
+void RichMediaAnnotation::Instance::setParams( RichMediaAnnotation::Params *params )
+{
+    delete d->params;
+    d->params = params;
+}
+
+RichMediaAnnotation::Params* RichMediaAnnotation::Instance::params() const
+{
+    return d->params;
+}
+
+
+class RichMediaAnnotation::Configuration::Private
+{
+    public:
+        Private() {}
+        ~Private()
+        {
+            qDeleteAll( instances );
+            instances.clear();
+        }
+
+        RichMediaAnnotation::Configuration::Type type;
+        QString name;
+        QList< RichMediaAnnotation::Instance* > instances;
+};
+
+RichMediaAnnotation::Configuration::Configuration()
+    : d( new Private )
+{
+}
+
+RichMediaAnnotation::Configuration::~Configuration()
+{
+}
+
+void RichMediaAnnotation::Configuration::setType( Type type )
+{
+    d->type = type;
+}
+
+RichMediaAnnotation::Configuration::Type RichMediaAnnotation::Configuration::type() const
+{
+    return d->type;
+}
+
+void RichMediaAnnotation::Configuration::setName( const QString &name )
+{
+    d->name = name;
+}
+
+QString RichMediaAnnotation::Configuration::name() const
+{
+    return d->name;
+}
+
+void RichMediaAnnotation::Configuration::setInstances( const QList< RichMediaAnnotation::Instance* > &instances )
+{
+    qDeleteAll( d->instances );
+    d->instances.clear();
+
+    d->instances = instances;
+}
+
+QList< RichMediaAnnotation::Instance* > RichMediaAnnotation::Configuration::instances() const
+{
+    return d->instances;
+}
+
+
+class RichMediaAnnotation::Asset::Private
+{
+    public:
+        Private()
+            : embeddedFile( nullptr )
+        {
+        }
+
+        ~Private()
+        {
+            delete embeddedFile;
+        }
+
+        QString name;
+        EmbeddedFile *embeddedFile;
+};
+
+RichMediaAnnotation::Asset::Asset()
+    : d( new Private )
+{
+}
+
+RichMediaAnnotation::Asset::~Asset()
+{
+}
+
+void RichMediaAnnotation::Asset::setName( const QString &name )
+{
+    d->name = name;
+}
+
+QString RichMediaAnnotation::Asset::name() const
+{
+    return d->name;
+}
+
+void RichMediaAnnotation::Asset::setEmbeddedFile( EmbeddedFile * embeddedFile )
+{
+    delete d->embeddedFile;
+    d->embeddedFile = embeddedFile;
+}
+
+EmbeddedFile* RichMediaAnnotation::Asset::embeddedFile() const
+{
+    return d->embeddedFile;
+}
+
+
+class RichMediaAnnotation::Content::Private
+{
+    public:
+        Private() {}
+        ~Private()
+        {
+            qDeleteAll( configurations );
+            configurations.clear();
+
+            qDeleteAll( assets );
+            assets.clear();
+        }
+
+        QList< RichMediaAnnotation::Configuration* > configurations;
+        QList< RichMediaAnnotation::Asset* > assets;
+};
+
+RichMediaAnnotation::Content::Content()
+    : d( new Private )
+{
+}
+
+RichMediaAnnotation::Content::~Content()
+{
+}
+
+void RichMediaAnnotation::Content::setConfigurations( const QList< RichMediaAnnotation::Configuration* > &configurations )
+{
+    qDeleteAll( d->configurations );
+    d->configurations.clear();
+
+    d->configurations = configurations;
+}
+
+QList< RichMediaAnnotation::Configuration* > RichMediaAnnotation::Content::configurations() const
+{
+    return d->configurations;
+}
+
+void RichMediaAnnotation::Content::setAssets( const QList< RichMediaAnnotation::Asset* > &assets )
+{
+    qDeleteAll( d->assets );
+    d->assets.clear();
+
+    d->assets = assets;
+}
+
+QList< RichMediaAnnotation::Asset* > RichMediaAnnotation::Content::assets() const
+{
+    return d->assets;
+}
+
+
+class RichMediaAnnotation::Activation::Private
+{
+    public:
+        Private()
+            : condition( RichMediaAnnotation::Activation::UserAction )
+        {
+        }
+
+        RichMediaAnnotation::Activation::Condition condition;
+};
+
+RichMediaAnnotation::Activation::Activation()
+    : d( new Private )
+{
+}
+
+RichMediaAnnotation::Activation::~Activation()
+{
+}
+
+void RichMediaAnnotation::Activation::setCondition( Condition condition )
+{
+    d->condition = condition;
+}
+
+RichMediaAnnotation::Activation::Condition RichMediaAnnotation::Activation::condition() const
+{
+    return d->condition;
+}
+
+
+class RichMediaAnnotation::Deactivation::Private : public QSharedData
+{
+    public:
+        Private()
+            : condition( RichMediaAnnotation::Deactivation::UserAction )
+        {
+        }
+
+        RichMediaAnnotation::Deactivation::Condition condition;
+};
+
+RichMediaAnnotation::Deactivation::Deactivation()
+    : d( new Private )
+{
+}
+
+RichMediaAnnotation::Deactivation::~Deactivation()
+{
+}
+
+void RichMediaAnnotation::Deactivation::setCondition( Condition condition )
+{
+    d->condition = condition;
+}
+
+RichMediaAnnotation::Deactivation::Condition RichMediaAnnotation::Deactivation::condition() const
+{
+    return d->condition;
+}
+
+
+class RichMediaAnnotation::Settings::Private : public QSharedData
+{
+    public:
+        Private()
+            : activation( nullptr ), deactivation( nullptr )
+        {
+        }
+
+        RichMediaAnnotation::Activation *activation;
+        RichMediaAnnotation::Deactivation *deactivation;
+};
+
+RichMediaAnnotation::Settings::Settings()
+    : d( new Private )
+{
+}
+
+RichMediaAnnotation::Settings::~Settings()
+{
+}
+
+void RichMediaAnnotation::Settings::setActivation( RichMediaAnnotation::Activation *activation )
+{
+    delete d->activation;
+    d->activation = activation;
+}
+
+RichMediaAnnotation::Activation* RichMediaAnnotation::Settings::activation() const
+{
+    return d->activation;
+}
+
+void RichMediaAnnotation::Settings::setDeactivation( RichMediaAnnotation::Deactivation *deactivation )
+{
+    delete d->deactivation;
+    d->deactivation = deactivation;
+}
+
+RichMediaAnnotation::Deactivation* RichMediaAnnotation::Settings::deactivation() const
+{
+    return d->deactivation;
+}
+
+
+class RichMediaAnnotationPrivate : public AnnotationPrivate
+{
+    public:
+        RichMediaAnnotationPrivate()
+            : settings( nullptr ), content( nullptr )
+        {
+        }
+
+        ~RichMediaAnnotationPrivate()
+        {
+            delete settings;
+            delete content;
+        }
+
+        Annotation * makeAlias()
+        {
+            return new RichMediaAnnotation( *this );
+        }
+
+        Annot* createNativeAnnot( ::Page *destPage, DocumentData *doc )
+        {
+            Q_UNUSED( destPage );
+            Q_UNUSED( doc );
+
+            return nullptr;
+        }
+
+        RichMediaAnnotation::Settings *settings;
+        RichMediaAnnotation::Content *content;
+};
+
+RichMediaAnnotation::RichMediaAnnotation()
+    : Annotation( *new RichMediaAnnotationPrivate() )
+{
+}
+
+RichMediaAnnotation::RichMediaAnnotation( RichMediaAnnotationPrivate &dd )
+    : Annotation( dd )
+{
+}
+
+RichMediaAnnotation::RichMediaAnnotation( const QDomNode & node )
+    : Annotation( *new RichMediaAnnotationPrivate(), node )
+{
+    // loop through the whole children looking for a 'richMedia' element
+    QDomNode subNode = node.firstChild();
+    while( subNode.isElement() )
+    {
+        QDomElement e = subNode.toElement();
+        subNode = subNode.nextSibling();
+        if ( e.tagName() != "richMedia" )
+            continue;
+
+        // loading complete
+        break;
+    }
+}
+
+RichMediaAnnotation::~RichMediaAnnotation()
+{
+}
+
+void RichMediaAnnotation::store( QDomNode & node, QDomDocument & document ) const
+{
+    // store base annotation properties
+    storeBaseAnnotationProperties( node, document );
+
+    // create [richMedia] element
+    QDomElement richMediaElement = document.createElement( "richMedia" );
+    node.appendChild( richMediaElement );
+}
+
+Annotation::SubType RichMediaAnnotation::subType() const
+{
+    return ARichMedia;
+}
+
+void RichMediaAnnotation::setSettings( RichMediaAnnotation::Settings *settings )
+{
+    Q_D( RichMediaAnnotation );
+
+    delete d->settings;
+    d->settings = settings;
+}
+
+RichMediaAnnotation::Settings* RichMediaAnnotation::settings() const
+{
+    Q_D( const RichMediaAnnotation );
+
+    return d->settings;
+}
+
+void RichMediaAnnotation::setContent( RichMediaAnnotation::Content *content )
+{
+    Q_D( RichMediaAnnotation );
+
+    delete d->content;
+    d->content = content;
+}
+
+RichMediaAnnotation::Content* RichMediaAnnotation::content() const
+{
+    Q_D( const RichMediaAnnotation );
+
+    return d->content;
+}
+
+//BEGIN utility annotation functions
+QColor convertAnnotColor( const AnnotColor *color )
+{
+    if ( !color )
+        return QColor();
+
+    QColor newcolor;
+    const double *color_data = color->getValues();
+    switch ( color->getSpace() )
+    {
+        case AnnotColor::colorTransparent: // = 0,
+            newcolor = Qt::transparent;
+            break;
+        case AnnotColor::colorGray: // = 1,
+            newcolor.setRgbF( color_data[0], color_data[0], color_data[0] );
+            break;
+        case AnnotColor::colorRGB: // = 3,
+            newcolor.setRgbF( color_data[0], color_data[1], color_data[2] );
+            break;
+        case AnnotColor::colorCMYK: // = 4
+            newcolor.setCmykF( color_data[0], color_data[1], color_data[2], color_data[3] );
+            break;
+    }
+    return newcolor;
+}
+
+std::unique_ptr<AnnotColor> convertQColor( const QColor &c )
+{
+    if ( c.alpha() == 0 )
+        return {}; // Transparent
+
+    switch ( c.spec() )
+    {
+        case QColor::Rgb:
+        case QColor::Hsl:
+        case QColor::Hsv:
+            return std::make_unique<AnnotColor>( c.redF(), c.greenF(), c.blueF() );
+        case QColor::Cmyk:
+            return std::make_unique<AnnotColor>( c.cyanF(), c.magentaF(), c.yellowF(), c.blackF() );
+        case QColor::Invalid:
+        default:
+            return {};
+    }
+}
+//END utility annotation functions
+
+}
diff --git a/qt4/src/poppler-annotation.h b/qt4/src/poppler-annotation.h
new file mode 100644
index 00000000..ac77c421
--- /dev/null
+++ b/qt4/src/poppler-annotation.h
@@ -0,0 +1,1379 @@
+/* poppler-annotation.h: qt interface to poppler
+ * Copyright (C) 2006-2008, 2012 Albert Astals Cid <aacid@kde.org>
+ * Copyright (C) 2006, 2008 Pino Toscano <pino@kde.org>
+ * Copyright (C) 2007, Brad Hards <bradh@frogmouth.net>
+ * Copyright (C) 2010, Philip Lorenz <lorenzph+freedesktop@gmail.com>
+ * Copyright (C) 2012, 2015, Tobias Koenig <tobias.koenig@kdab.com>
+ * Copyright (C) 2012, Guillermo A. Amaral B. <gamaral@kde.org>
+ * Copyright (C) 2012, 2013 Fabio D'Urso <fabiodurso@hotmail.it>
+ * Adapting code from
+ *   Copyright (C) 2004 by Enrico Ros <eros.kde@email.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtCore/QDateTime>
+#include <QtCore/QSharedDataPointer>
+#include <QtCore/QLinkedList>
+#include <QtCore/QList>
+#include <QtCore/QPointF>
+#include <QtCore/QRectF>
+#include <QtCore/QVector>
+#include <QtGui/QColor>
+#include <QtGui/QFont>
+#include <QtXml/QDomDocument>
+#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
+ * <code>-(rendering_rotation + page_orientation)</code>. 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 <b>not</b> 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 <b>must</b>
+ *    set again the boundary rectangle first, and then you <b>must</b> set
+ *    again any other geometry-related property.
+ *  - Whenever you modify the boundary rectangle of an annotation whose
+ *    Annotation::FixedRotation flag is set, you <b>must</b> 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<double>& dashArray() const; // [ 3 ]
+        void setDashArray(const QVector<double> &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<Private> 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<Private> 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<Annotation*> 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<AnnotationPrivate> d_ptr;
+    /// \endcond
+
+  private:
+    virtual void store( QDomNode & parentNode, QDomDocument & document ) const = 0;
+    Q_DISABLE_COPY( Annotation )
+};
+
+/**
+ * \short Annotation containing text.
+ *
+ * A text annotation is an object showing some text directly on the page, or
+ * linked to the contents using an icon shown on a page.
+ */
+class POPPLER_QT4_EXPORT TextAnnotation : public Annotation
+{
+  friend class AnnotationUtils;
+  friend class AnnotationPrivate;
+
+  public:
+    // local enums
+    enum TextType { Linked, InPlace };
+    enum InplaceIntent { Unknown, Callout, TypeWriter };
+
+    TextAnnotation( TextType type );
+    ~TextAnnotation();
+    SubType subType() const override;
+
+    /**
+       The type of text annotation represented by this object
+    */
+    TextType textType() const;
+
+    /**
+       The name of the icon for this text annotation.
+
+       Standard names for text annotation icons are:
+       - Comment
+       - Help
+       - Insert
+       - Key
+       - NewParagraph
+       - Note (this is the default icon to use)
+       - Paragraph
+    */
+    QString textIcon() const;
+
+    /**
+       Set the name of the icon to use for this text annotation.
+
+       \sa textIcon for the list of standard names
+    */
+    void setTextIcon( const QString &icon );
+
+    QFont textFont() const;
+    void setTextFont( const QFont &font );
+    /// \since 0.69
+    QColor textColor() const;
+    /// \since 0.69
+    void setTextColor( const QColor &color );
+
+    int inplaceAlign() const;
+    void setInplaceAlign( int align );
+
+    /**
+       Synonym for contents()
+
+       \deprecated Use contents() instead
+    */
+    QString inplaceText() const;
+    /**
+       Synonym for setContents()
+
+       \deprecated Use setContents() instead
+    */
+    void setInplaceText( const QString &text );
+
+    QPointF calloutPoint( int id ) const;
+    /// \since 0.20
+    QVector<QPointF> calloutPoints() const;
+    /// \since 0.20
+    void setCalloutPoints( const QVector<QPointF> &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<QPointF> linePoints() const;
+    void setLinePoints( const QLinkedList<QPointF> &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<QPointF> > inkPaths() const;
+    void setInkPaths( const QList< QLinkedList<QPointF> > &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<Private> 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<Private> 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<Private> 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<Private> 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<Private> 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<Private> 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<Private> 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<Private> 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 <aacid@kde.org>
+ * Copyright (C) 2008, Pino Toscano <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtCore/QFile>
+
+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 <aacid@kde.org>
+ * Copyright (C) 2008, Pino Toscano <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtCore/QString>
+
+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 <bradh@frogmouth.net>
+ * Copyright (C) 2005-2010, 2012, 2013, 2015-2017, Albert Astals Cid <aacid@kde.org>
+ * Copyright (C) 2006-2010, Pino Toscano <pino@kde.org>
+ * Copyright (C) 2010, 2011 Hib Eris <hib@hiberis.nl>
+ * Copyright (C) 2012 Koji Otani <sho@bbr.jp>
+ * Copyright (C) 2012, 2013 Thomas Freitag <Thomas.Freitag@alfa.de>
+ * Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
+ * Copyright (C) 2014 Adam Reichold <adamreichold@myopera.com>
+ * Copyright (C) 2015 William Bader <williambader@hotmail.com>
+ * Copyright (C) 2016 Jakub Alba <jakubalba@gmail.com>
+ * Copyright (C) 2017 Adrian Johnson <ajohnson@redneon.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <config.h>
+#include <ErrorCodes.h>
+#include <GlobalParams.h>
+#include <Outline.h>
+#include <PDFDoc.h>
+#include <Stream.h>
+#include <Catalog.h>
+#include <ViewerPreferences.h>
+#include <DateInfo.h>
+#include <GfxState.h>
+
+#include <QtCore/QDebug>
+#include <QtCore/QFile>
+#include <QtCore/QByteArray>
+
+#include "poppler-private.h"
+#include "poppler-page-private.h"
+
+#if defined(USE_CMS)
+#if defined(USE_LCMS1)
+#include <lcms.h>
+#else
+#include <lcms2.h>
+#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<FontInfo> Document::fonts() const
+    {
+	QList<FontInfo> ourList;
+	FontIterator it( 0, m_doc );
+	while ( it.hasNext() )
+	{
+		ourList += it.next();
+	}
+	return ourList;
+    }
+
+    QList<EmbeddedFile*> Document::embeddedFiles() const
+    {
+	return m_doc->m_embeddedFiles;
+    }
+
+    bool Document::scanForFonts( int numPages, QList<FontInfo> *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<GooString> 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<GooString> 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<GooString> 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<GooString> 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<GooString> 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<GooString> 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<GooString> 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> 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<GooString> 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<GooString> 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<GooString> 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::RenderBackend> Document::availableRenderBackends()
+    {
+        QSet<Document::RenderBackend> ret;
+#if defined(HAVE_SPLASH)
+        ret << Document::SplashBackend;
+#endif
+        ret << Document::ArthurBackend;
+        return ret;
+    }
+
+    void Document::setRenderHint( Document::RenderHint hint, bool on )
+    {
+        const bool touchesOverprinting = hint & Document::OverprintPreview;
+        
+        int hintForOperation = hint;
+        if (touchesOverprinting && !isOverprintPreviewAvailable())
+            hintForOperation = hintForOperation & ~(int)Document::OverprintPreview;
+
+        if ( on )
+            m_doc->m_hints |= hintForOperation;
+        else
+            m_doc->m_hints &= ~hintForOperation;
+
+    }
+
+    Document::RenderHints Document::renderHints() const
+    {
+        return Document::RenderHints( m_doc->m_hints );
+    }
+
+    PSConverter *Document::psConverter() const
+    {
+        return new PSConverter(m_doc);
+    }
+
+    PDFConverter *Document::pdfConverter() const
+    {
+        return new PDFConverter(m_doc);
+    }
+
+    QString Document::metadata() const
+    {
+        QString result;
+        Catalog *catalog = m_doc->doc->getCatalog();
+        if (catalog && catalog->isOk())
+        {
+            GooString *s = catalog->readMetadata();
+            if (s) result = UnicodeParsedString(s);
+            delete s;
+        }
+        return result;
+    }
+
+    bool Document::hasOptionalContent() const
+    {
+        return ( m_doc->doc->getOptContentConfig() && m_doc->doc->getOptContentConfig()->hasOCGs() );
+    }
+
+    OptContentModel *Document::optionalContentModel()
+    {
+        if (m_doc->m_optContentModel.isNull()) {
+	    m_doc->m_optContentModel = new OptContentModel(m_doc->doc->getOptContentConfig(), 0);
+	}
+        return (OptContentModel *)m_doc->m_optContentModel;
+    }
+
+    QStringList Document::scripts() const
+    {
+        Catalog *catalog = m_doc->doc->getCatalog();
+        const int numScripts = catalog->numJS();
+        QStringList scripts;
+        for (int i = 0; i < numScripts; ++i) {
+            GooString *s = catalog->getJS(i);
+            if (s) {
+                scripts.append(UnicodeParsedString(s));
+                delete s;
+            }
+        }
+        return scripts;
+    }
+
+    bool Document::getPdfId(QByteArray *permanentId, QByteArray *updateId) const
+    {
+        GooString gooPermanentId;
+        GooString gooUpdateId;
+
+        if (!m_doc->doc->getID(permanentId ? &gooPermanentId : 0, updateId ? &gooUpdateId : 0))
+            return false;
+
+        if (permanentId)
+            *permanentId = gooPermanentId.c_str();
+        if (updateId)
+            *updateId = gooUpdateId.c_str();
+
+        return true;
+    }
+
+    Document::FormType Document::formType() const
+    {
+        switch ( m_doc->doc->getCatalog()->getFormType() )
+        {
+            case Catalog::NoForm:
+                return Document::NoForm;
+            case Catalog::AcroForm:
+                return Document::AcroForm;
+            case Catalog::XfaForm:
+                return Document::XfaForm;
+        }
+
+        return Document::NoForm; // make gcc happy
+    }
+
+    QDateTime convertDate( char *dateString )
+    {
+        int year, mon, day, hour, min, sec, tzHours, tzMins;
+        char tz;
+
+        if ( parseDateString( dateString, &year, &mon, &day, &hour, &min, &sec, &tz, &tzHours, &tzMins ) )
+        {
+            QDate d( year, mon, day );
+            QTime t( hour, min, sec );
+            if ( d.isValid() && t.isValid() ) {
+                QDateTime dt( d, t, Qt::UTC );
+                if ( tz ) {
+                    // then we have some form of timezone
+                    if ( 'Z' == tz  ) {
+                        // We are already at UTC
+                    } else if ( '+' == tz ) {
+                        // local time is ahead of UTC
+                        dt = dt.addSecs(-1*((tzHours*60)+tzMins)*60);
+                    } else if ( '-' == tz ) {
+                        // local time is behind UTC
+                        dt = dt.addSecs(((tzHours*60)+tzMins)*60);
+                    } else {
+                        qWarning("unexpected tz val");
+                    }
+                }
+		return dt;
+            }
+        }
+        return QDateTime();
+    }
+
+    bool isCmsAvailable()
+    {
+#if defined(USE_CMS)
+        return true;
+#else
+        return false;
+#endif
+    }
+
+    bool isOverprintPreviewAvailable() {
+#ifdef SPLASH_CMYK
+        return true;
+#else
+        return false;
+#endif
+   }
+
+}
diff --git a/qt4/src/poppler-embeddedfile-private.h b/qt4/src/poppler-embeddedfile-private.h
new file mode 100644
index 00000000..83549dad
--- /dev/null
+++ b/qt4/src/poppler-embeddedfile-private.h
@@ -0,0 +1,42 @@
+/* poppler-embeddedfile-private.h: Qt4 interface to poppler
+ * Copyright (C) 2005, 2008, 2009, 2012, Albert Astals Cid <aacid@kde.org>
+ * Copyright (C) 2005, Brad Hards <bradh@frogmouth.net>
+ * Copyright (C) 2008, 2011, Pino Toscano <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <aacid@kde.org>
+ * Copyright (C) 2005, Brad Hards <bradh@frogmouth.net>
+ * Copyright (C) 2008, 2011, Pino Toscano <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtCore/QString>
+#include <QtCore/QDateTime>
+
+#include "Object.h"
+#include "Stream.h"
+#include "Catalog.h"
+#include "FileSpec.h"
+
+#include "poppler-private.h"
+#include "poppler-embeddedfile-private.h"
+
+namespace Poppler
+{
+
+EmbeddedFileData::EmbeddedFileData(FileSpec *fs)
+	: filespec(fs)
+{
+}
+
+EmbeddedFileData::~EmbeddedFileData()
+{
+	delete filespec;
+}
+
+EmbFile *EmbeddedFileData::embFile() const
+{
+	return filespec->isOk() ? filespec->getEmbeddedFile() : NULL;
+}
+
+
+EmbeddedFile::EmbeddedFile(EmbFile *embfile)
+	: m_embeddedFile(0)
+{
+	assert(!"You must not use this private constructor!");
+}
+
+EmbeddedFile::EmbeddedFile(EmbeddedFileData &dd)
+	: m_embeddedFile(&dd)
+{
+}
+
+EmbeddedFile::~EmbeddedFile()
+{
+	delete m_embeddedFile;
+}
+
+QString EmbeddedFile::name() const
+{
+	GooString *goo = m_embeddedFile->filespec->getFileName();
+	return goo ? UnicodeParsedString(goo) : QString();
+}
+
+QString EmbeddedFile::description() const
+{
+	GooString *goo = m_embeddedFile->filespec->getDescription();
+	return goo ? UnicodeParsedString(goo) : QString();
+}
+
+int EmbeddedFile::size() const
+{
+	return m_embeddedFile->embFile() ? m_embeddedFile->embFile()->size() : -1;
+}
+
+QDateTime EmbeddedFile::modDate() const
+{
+	GooString *goo = m_embeddedFile->embFile() ? m_embeddedFile->embFile()->modDate() : NULL;
+	return goo ? convertDate(goo->c_str()) : QDateTime();
+}
+
+QDateTime EmbeddedFile::createDate() const
+{
+	GooString *goo = m_embeddedFile->embFile() ? m_embeddedFile->embFile()->createDate() : NULL;
+	return goo ? convertDate(goo->c_str()) : QDateTime();
+}
+
+QByteArray EmbeddedFile::checksum() const
+{
+	GooString *goo = m_embeddedFile->embFile() ? m_embeddedFile->embFile()->checksum() : NULL;
+	return goo ? QByteArray::fromRawData(goo->c_str(), goo->getLength()) : QByteArray();
+}
+
+QString EmbeddedFile::mimeType() const
+{
+	GooString *goo = m_embeddedFile->embFile() ? m_embeddedFile->embFile()->mimeType() : NULL;
+	return goo ? QString(goo->c_str()) : QString();
+}
+
+QByteArray EmbeddedFile::data()
+{
+	if (!isValid())
+		return QByteArray();
+	Stream *stream = m_embeddedFile->embFile() ? m_embeddedFile->embFile()->stream() : NULL;
+	if (!stream)
+		return QByteArray();
+	
+	stream->reset();
+	int dataLen = 0;
+	QByteArray fileArray;
+	int i;
+	while ( (i = stream->getChar()) != EOF) {
+		fileArray[dataLen] = (char)i;
+		++dataLen;
+	}
+	fileArray.resize(dataLen);
+	return fileArray;
+}
+
+bool EmbeddedFile::isValid() const
+{
+	return m_embeddedFile->filespec->isOk();
+}
+
+}
diff --git a/qt4/src/poppler-export.h b/qt4/src/poppler-export.h
new file mode 100644
index 00000000..2e2f6ff8
--- /dev/null
+++ b/qt4/src/poppler-export.h
@@ -0,0 +1,20 @@
+/*
+* This file is used to set the poppler_qt4_EXPORT macros right.
+* This is needed for setting the visibility on windows, it will have no effect on other platforms.
+*/
+#if defined(_WIN32)
+# define _POPPLER_QT4_LIB_EXPORT __declspec(dllexport)
+# define _POPPLER_QT4_LIB_IMPORT __declspec(dllimport)
+#elif defined(__GNUC__)
+# define _POPPLER_QT4_LIB_EXPORT __attribute__((visibility("default")))
+# define _POPPLER_QT4_LIB_IMPORT
+#else
+# define _POPPLER_QT4_LIB_EXPORT
+# define _POPPLER_QT4_LIB_IMPORT
+#endif
+
+#ifdef poppler_qt4_EXPORTS
+# define POPPLER_QT4_EXPORT _POPPLER_QT4_LIB_EXPORT
+#else
+# define POPPLER_QT4_EXPORT _POPPLER_QT4_LIB_IMPORT
+#endif
diff --git a/qt4/src/poppler-fontinfo.cc b/qt4/src/poppler-fontinfo.cc
new file mode 100644
index 00000000..5bb9e3a8
--- /dev/null
+++ b/qt4/src/poppler-fontinfo.cc
@@ -0,0 +1,150 @@
+/* poppler-qt.h: qt interface to poppler
+ * Copyright (C) 2005, Net Integration Technologies, Inc.
+ * Copyright (C) 2005, Tobias Koening <tokoe@kde.org>
+ * Copyright (C) 2005, Brad Hards <bradh@frogmouth.net>
+ * Copyright (C) 2005-2008, 2015, Albert Astals Cid <aacid@kde.org>
+ * Copyright (C) 2008, 2009, Pino Toscano <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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<FontInfo> FontIterator::next()
+{
+	++d->currentPage;
+
+	QList<FontInfo> 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<::FontInfo>( items );
+	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 <pino@kde.org>
+ * Copyright (C) 2008, 2011, 2012, 2015, 2017, Albert Astals Cid <aacid@kde.org>
+ * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org>
+ * Copyright (C) 2012, Adam Reichold <adamreichold@myopera.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtCore/QSizeF>
+
+#include <Form.h>
+#include <Object.h>
+#include <Link.h>
+
+#include "poppler-form.h"
+#include "poppler-page-private.h"
+#include "poppler-private.h"
+#include "poppler-annotation-helper.h"
+
+#include <math.h>
+
+namespace {
+
+Qt::Alignment formTextAlignment(::FormWidget *fm)
+{
+  Qt::Alignment qtalign = Qt::AlignLeft;
+  switch (fm->getField()->getTextQuadding())
+  {
+    case quaddingCentered:
+      qtalign = Qt::AlignHCenter;
+      break;
+    case quaddingRightJustified:
+      qtalign = Qt::AlignRight;
+      break;
+    case quaddingLeftJustified:
+      qtalign = Qt::AlignLeft;
+  }
+  return qtalign;
+}
+
+}
+
+namespace Poppler {
+
+FormField::FormField(FormFieldData &dd)
+  : m_formData(&dd)
+{
+  const int rotation = m_formData->page->getRotate();
+  // reading the coords
+  double left, top, right, bottom;
+  m_formData->fm->getRect(&left, &bottom, &right, &top);
+  // build a normalized transform matrix for this page at 100% scale
+  GfxState gfxState( 72.0, 72.0, m_formData->page->getCropBox(), rotation, true );
+  const double * gfxCTM = gfxState.getCTM();
+  double MTX[6];
+  double pageWidth = m_formData->page->getCropWidth();
+  double pageHeight = m_formData->page->getCropHeight();
+  // landscape and seascape page rotation: be sure to use the correct (== rotated) page size
+  if (((rotation / 90) % 2) == 1)
+    qSwap(pageWidth, pageHeight);
+  for ( int i = 0; i < 6; i+=2 )
+  {
+    MTX[i] = gfxCTM[i] / pageWidth;
+    MTX[i+1] = gfxCTM[i+1] / pageHeight;
+  }
+  QPointF topLeft;
+  XPDFReader::transform( MTX, qMin( left, right ), qMax( top, bottom ), topLeft );
+  QPointF bottomRight;
+  XPDFReader::transform( MTX, qMax( left, right ), qMin( top, bottom ), bottomRight );
+  m_formData->box = QRectF(topLeft, QSizeF(bottomRight.x() - topLeft.x(), bottomRight.y() - topLeft.y()));
+}
+
+FormField::~FormField()
+{
+  delete m_formData;
+  m_formData = 0;
+}
+
+QRectF FormField::rect() const
+{
+  return m_formData->box;
+}
+
+int FormField::id() const
+{
+  return m_formData->fm->getID();
+}
+
+QString FormField::name() const
+{
+  QString name;
+  if (const GooString *goo = m_formData->fm->getPartialName())
+  {
+    name = QString::fromLatin1(goo->c_str());
+  }
+  return name;
+}
+
+QString FormField::fullyQualifiedName() const
+{
+  QString name;
+  if (const GooString *goo = m_formData->fm->getFullyQualifiedName())
+  {
+    name = UnicodeParsedString(goo);
+  }
+  return name;
+}
+
+QString FormField::uiName() const
+{
+  QString name;
+  if (const GooString *goo = m_formData->fm->getAlternateUiName())
+  {
+    name = QString::fromLatin1(goo->c_str());
+  }
+  return name;
+}
+
+bool FormField::isReadOnly() const
+{
+  return m_formData->fm->isReadOnly();
+}
+
+bool FormField::isVisible() const
+{
+  return !(m_formData->fm->getWidgetAnnotation()->getFlags() & Annot::flagHidden);
+}
+
+Link* FormField::activationAction() const
+{
+  Link* action = 0;
+  if (::LinkAction *act = m_formData->fm->getActivationAction())
+  {
+    action = PageData::convertLinkActionToLink(act, m_formData->doc, QRectF());
+  }
+  return action;
+}
+
+
+FormFieldButton::FormFieldButton(DocumentData *doc, ::Page *p, ::FormWidgetButton *w)
+  : FormField(*new FormFieldData(doc, p, w))
+{
+}
+
+FormFieldButton::~FormFieldButton()
+{
+}
+
+FormFieldButton::FormType FormFieldButton::type() const
+{
+  return FormField::FormButton;
+}
+
+FormFieldButton::ButtonType FormFieldButton::buttonType() const
+{
+  FormWidgetButton* fwb = static_cast<FormWidgetButton*>(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<FormWidgetButton*>(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<FormWidgetButton*>(m_formData->fm);
+  return fwb->getState();
+}
+
+void FormFieldButton::setState( bool state )
+{
+  FormWidgetButton* fwb = static_cast<FormWidgetButton*>(m_formData->fm);
+  fwb->setState((bool)state);
+}
+
+QList<int> FormFieldButton::siblings() const
+{
+  FormWidgetButton* fwb = static_cast<FormWidgetButton*>(m_formData->fm);
+  ::FormFieldButton* ffb = static_cast< ::FormFieldButton* >(fwb->getField());
+  if (fwb->getButtonType() == formButtonPush)
+    return QList<int>();
+
+  QList<int> 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<FormWidgetText*>(m_formData->fm);
+  if (fwt->isFileSelect())
+    return FormFieldText::FileSelect;
+  else if (fwt->isMultiline())
+    return FormFieldText::Multiline;
+  return FormFieldText::Normal;
+}
+
+QString FormFieldText::text() const
+{
+  const GooString *goo = static_cast<FormWidgetText*>(m_formData->fm)->getContent();
+  return UnicodeParsedString(goo);
+}
+
+void FormFieldText::setText( const QString& text )
+{
+  FormWidgetText* fwt = static_cast<FormWidgetText*>(m_formData->fm);
+  GooString * goo = QStringToUnicodeGooString( text );
+  fwt->setContent( goo );
+  delete goo;
+}
+
+bool FormFieldText::isPassword() const
+{
+  FormWidgetText* fwt = static_cast<FormWidgetText*>(m_formData->fm);
+  return fwt->isPassword();
+}
+
+bool FormFieldText::isRichText() const
+{
+  FormWidgetText* fwt = static_cast<FormWidgetText*>(m_formData->fm);
+  return fwt->isRichText();
+}
+
+int FormFieldText::maximumLength() const
+{
+  FormWidgetText* fwt = static_cast<FormWidgetText*>(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<FormWidgetText*>(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<FormWidgetChoice*>(m_formData->fm);
+  if (fwc->isCombo())
+    return FormFieldChoice::ComboBox;
+  return FormFieldChoice::ListBox;
+}
+
+QStringList FormFieldChoice::choices() const
+{
+  FormWidgetChoice* fwc = static_cast<FormWidgetChoice*>(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<FormWidgetChoice*>(m_formData->fm);
+  return fwc->isCombo() ? fwc->hasEdit() : false;
+}
+
+bool FormFieldChoice::multiSelect() const
+{
+  FormWidgetChoice* fwc = static_cast<FormWidgetChoice*>(m_formData->fm);
+  return !fwc->isCombo() ? fwc->isMultiSelect() : false;
+}
+
+QList<int> FormFieldChoice::currentChoices() const
+{
+  FormWidgetChoice* fwc = static_cast<FormWidgetChoice*>(m_formData->fm);
+  int num = fwc->getNumChoices();
+  QList<int> choices;
+  for ( int i = 0; i < num; ++i )
+    if ( fwc->isSelected( i ) )
+      choices.append( i );
+  return choices;
+}
+
+void FormFieldChoice::setCurrentChoices( const QList<int> &choice )
+{
+  FormWidgetChoice* fwc = static_cast<FormWidgetChoice*>(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<FormWidgetChoice*>(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<FormWidgetChoice*>(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<FormWidgetChoice*>(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 <pino@kde.org>
+ * Copyright (C) 2008, 2011, Albert Astals Cid <aacid@kde.org>
+ * Copyright (C) 2012, Adam Reichold <adamreichold@myopera.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtCore/QRectF>
+#include <QtCore/QStringList>
+#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<int> 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<int> currentChoices() const;
+
+	/**
+	  Sets the selected choices to \p choice.
+	 */
+	void setCurrentChoices( const QList<int> &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 <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <Object.h>
+#include <OutputDev.h>
+
+#include <QtCore/QList>
+
+namespace Poppler
+{
+
+class Link;
+class PageData;
+
+class LinkExtractorOutputDev : public OutputDev
+{
+  public:
+    LinkExtractorOutputDev(PageData *data);
+    virtual ~LinkExtractorOutputDev();
+
+    // inherited from OutputDev
+    virtual bool upsideDown() { return false; }
+    virtual bool useDrawChar() { return false; }
+    virtual bool interpretType3Chars() { return false; }
+    virtual void processLink(::AnnotLink *link);
+
+    // our stuff
+    QList< Link* > links();
+
+  private:
+    PageData *m_data;
+    double m_pageCropWidth;
+    double m_pageCropHeight;
+    QList< Link* > m_links;
+};
+
+}
+
+#endif
diff --git a/qt4/src/poppler-link-extractor.cc b/qt4/src/poppler-link-extractor.cc
new file mode 100644
index 00000000..0b1563b6
--- /dev/null
+++ b/qt4/src/poppler-link-extractor.cc
@@ -0,0 +1,84 @@
+/* poppler-link-extractor_p.h: qt interface to poppler
+ * Copyright (C) 2007, 2008, 2011, Pino Toscano <pino@kde.org>
+ * Copyright (C) 2008, Albert Astals Cid <aacid@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <GfxState.h>
+#include <Link.h>
+#include <Object.h>
+#include <Page.h>
+#include <Annot.h>
+
+#include "poppler-qt4.h"
+#include "poppler-page-private.h"
+
+namespace Poppler
+{
+
+LinkExtractorOutputDev::LinkExtractorOutputDev(PageData *data)
+  : m_data(data)
+{
+  Q_ASSERT(m_data);
+  ::Page *popplerPage = m_data->page;
+  m_pageCropWidth = popplerPage->getCropWidth();
+  m_pageCropHeight = popplerPage->getCropHeight();
+  if (popplerPage->getRotate() == 90 || popplerPage->getRotate() == 270)
+    qSwap(m_pageCropWidth, m_pageCropHeight);
+  GfxState gfxState(72.0, 72.0, popplerPage->getCropBox(), popplerPage->getRotate(), true);
+  setDefaultCTM(gfxState.getCTM());
+}
+
+LinkExtractorOutputDev::~LinkExtractorOutputDev()
+{
+  qDeleteAll(m_links);
+}
+
+void LinkExtractorOutputDev::processLink(::AnnotLink *link)
+{
+  if (!link->isOk())
+    return;
+
+  double left, top, right, bottom;
+  int leftAux, topAux, rightAux, bottomAux;
+  link->getRect(&left, &top, &right, &bottom);
+  QRectF linkArea;
+
+  cvtUserToDev(left, top, &leftAux, &topAux);
+  cvtUserToDev(right, bottom, &rightAux, &bottomAux);
+  linkArea.setLeft((double)leftAux / m_pageCropWidth);
+  linkArea.setTop((double)topAux / m_pageCropHeight);
+  linkArea.setRight((double)rightAux / m_pageCropWidth);
+  linkArea.setBottom((double)bottomAux / m_pageCropHeight);
+
+  Link *popplerLink = m_data->convertLinkActionToLink(link->getAction(), linkArea);
+  if (popplerLink)
+  {
+    m_links.append(popplerLink);
+  }
+  OutputDev::processLink(link);
+}
+
+QList< Link* > LinkExtractorOutputDev::links()
+{
+  QList< Link* > ret = m_links;
+  m_links.clear();
+  return ret;
+}
+
+}
diff --git a/qt4/src/poppler-link-private.h b/qt4/src/poppler-link-private.h
new file mode 100644
index 00000000..7b03c1c3
--- /dev/null
+++ b/qt4/src/poppler-link-private.h
@@ -0,0 +1,57 @@
+/* poppler-link-private.h: qt interface to poppler
+ * Copyright (C) 2016, Albert Astals Cid <aacid@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _POPPLER_LINK_PRIVATE_H_
+#define _POPPLER_LINK_PRIVATE_H_
+
+class LinkOCGState;
+
+namespace Poppler {
+
+class LinkPrivate
+{
+public:
+    LinkPrivate( const QRectF &area )
+        : linkArea( area )
+    {
+    }
+
+    virtual ~LinkPrivate()
+    {
+    }
+
+    QRectF linkArea;
+};
+
+
+
+class LinkOCGStatePrivate : public LinkPrivate
+{
+public:
+    LinkOCGStatePrivate( const QRectF &area, ::LinkOCGState *plocg )
+        : LinkPrivate( area )
+        , popplerLinkOCGState( plocg )
+    {
+    }
+
+    ::LinkOCGState *popplerLinkOCGState;
+};
+
+}
+
+#endif
diff --git a/qt4/src/poppler-link.cc b/qt4/src/poppler-link.cc
new file mode 100644
index 00000000..1e37f5bd
--- /dev/null
+++ b/qt4/src/poppler-link.cc
@@ -0,0 +1,705 @@
+/* poppler-link.cc: qt interface to poppler
+ * Copyright (C) 2006-2007, 2016, 2017, Albert Astals Cid
+ * Copyright (C) 2007-2008, Pino Toscano <pino@kde.org>
+ * Copyright (C) 2010 Hib Eris <hib@hiberis.nl>
+ * Copyright (C) 2012, Tobias Koenig <tokoe@kdab.com>
+ * Copyright (C) 2012, Guillermo A. Amaral B. <gamaral@kde.org>
+ * Adapting code from
+ *   Copyright (C) 2004 by Enrico Ros <eros.kde@email.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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-link-private.h>
+#include <poppler-private.h>
+#include <poppler-media.h>
+
+#include <QtCore/QStringList>
+
+#include "poppler-annotation-private.h"
+
+#include "Link.h"
+#include "Rendition.h"
+
+namespace Poppler {
+
+class LinkDestinationPrivate : public QSharedData
+{
+	public:
+		LinkDestinationPrivate();
+
+		LinkDestination::Kind kind; // destination type
+		QString name;
+		int pageNum; // page number
+		double left, bottom; // position
+		double right, top;
+		double zoom; // zoom factor
+		bool changeLeft : 1, changeTop : 1; // for destXYZ links, which position
+		bool changeZoom : 1; //   components to change
+};
+
+	LinkDestinationPrivate::LinkDestinationPrivate()
+	{
+		// sane defaults
+		kind = LinkDestination::destXYZ;
+		pageNum = 0;
+		left = 0;
+		bottom = 0;
+		right = 0;
+		top = 0;
+		zoom = 1;
+		changeLeft = true;
+		changeTop = true;
+		changeZoom = false;
+	}
+
+class LinkGotoPrivate : public LinkPrivate
+{
+	public:
+		LinkGotoPrivate( const QRectF &area, const LinkDestination &dest );
+
+		QString extFileName;
+		LinkDestination destination;
+};
+
+	LinkGotoPrivate::LinkGotoPrivate( const QRectF &area, const LinkDestination &dest )
+		: LinkPrivate( area ), destination( dest )
+	{
+	}
+
+class LinkExecutePrivate : public LinkPrivate
+{
+	public:
+		LinkExecutePrivate( const QRectF &area );
+
+		QString fileName;
+		QString parameters;
+};
+
+	LinkExecutePrivate::LinkExecutePrivate( const QRectF &area )
+		: LinkPrivate( area )
+	{
+	}
+
+class LinkBrowsePrivate : public LinkPrivate
+{
+	public:
+		LinkBrowsePrivate( const QRectF &area );
+
+		QString url;
+};
+
+	LinkBrowsePrivate::LinkBrowsePrivate( const QRectF &area )
+		: LinkPrivate( area )
+	{
+	}
+
+class LinkActionPrivate : public LinkPrivate
+{
+	public:
+		LinkActionPrivate( const QRectF &area );
+
+		LinkAction::ActionType type;
+};
+
+	LinkActionPrivate::LinkActionPrivate( const QRectF &area )
+		: LinkPrivate( area )
+	{
+	}
+
+class LinkSoundPrivate : public LinkPrivate
+{
+	public:
+		LinkSoundPrivate( const QRectF &area );
+		~LinkSoundPrivate();
+
+		double volume;
+		bool sync : 1;
+		bool repeat : 1;
+		bool mix : 1;
+		SoundObject *sound;
+};
+
+	LinkSoundPrivate::LinkSoundPrivate( const QRectF &area )
+		: LinkPrivate( area ), sound( 0 )
+	{
+	}
+
+	LinkSoundPrivate::~LinkSoundPrivate()
+	{
+		delete sound;
+	}
+
+class LinkRenditionPrivate : public LinkPrivate
+{
+	public:
+		LinkRenditionPrivate( const QRectF &area, ::MediaRendition *rendition, ::LinkRendition::RenditionOperation operation, const QString &script, const Ref &annotationReference );
+		~LinkRenditionPrivate();
+
+		MediaRendition *rendition;
+		LinkRendition::RenditionAction action;
+		QString script;
+		Ref annotationReference;
+};
+
+	LinkRenditionPrivate::LinkRenditionPrivate( const QRectF &area, ::MediaRendition *r, ::LinkRendition::RenditionOperation operation, const QString &javaScript, const Ref &ref )
+		: LinkPrivate( area )
+		, rendition( r ? new MediaRendition( r ) : 0 )
+		, action( LinkRendition::PlayRendition )
+		, script( javaScript )
+		, annotationReference( ref )
+	{
+		switch ( operation )
+		{
+			case ::LinkRendition::NoRendition:
+				action = LinkRendition::NoRendition;
+				break;
+			case ::LinkRendition::PlayRendition:
+				action = LinkRendition::PlayRendition;
+				break;
+			case ::LinkRendition::StopRendition:
+				action = LinkRendition::StopRendition;
+				break;
+			case ::LinkRendition::PauseRendition:
+				action = LinkRendition::PauseRendition;
+				break;
+			case ::LinkRendition::ResumeRendition:
+				action = LinkRendition::ResumeRendition;
+				break;
+		}
+	}
+
+	LinkRenditionPrivate::~LinkRenditionPrivate()
+	{
+		delete rendition;
+	}
+
+class LinkJavaScriptPrivate : public LinkPrivate
+{
+	public:
+		LinkJavaScriptPrivate( const QRectF &area );
+
+		QString js;
+};
+
+	LinkJavaScriptPrivate::LinkJavaScriptPrivate( const QRectF &area )
+		: LinkPrivate( area )
+	{
+	}
+
+class LinkMoviePrivate : public LinkPrivate
+{
+	public:
+		LinkMoviePrivate( const QRectF &area, LinkMovie::Operation operation, const QString &title, const Ref &reference );
+
+		LinkMovie::Operation operation;
+		QString annotationTitle;
+		Ref annotationReference;
+};
+
+	LinkMoviePrivate::LinkMoviePrivate( const QRectF &area, LinkMovie::Operation _operation, const QString &title, const Ref &reference  )
+		: LinkPrivate( area ), operation( _operation ), annotationTitle( title ), annotationReference( reference )
+	{
+	}
+
+	static void cvtUserToDev(::Page *page, double xu, double yu, int *xd, int *yd) {
+		double ctm[6];
+		
+		page->getDefaultCTM(ctm, 72.0, 72.0, 0, false, true);
+		*xd = (int)(ctm[0] * xu + ctm[2] * yu + ctm[4] + 0.5);
+		*yd = (int)(ctm[1] * xu + ctm[3] * yu + ctm[5] + 0.5);
+	}
+
+	LinkDestination::LinkDestination(const LinkDestinationData &data)
+		: d( new LinkDestinationPrivate )
+	{
+		bool deleteDest = false;
+		const LinkDest *ld = data.ld;
+		
+		if ( data.namedDest && !ld && !data.externalDest )
+		{
+			deleteDest = true;
+			ld = data.doc->doc->findDest( data.namedDest );
+		}
+		// in case this destination was named one, and it was not resolved
+		if ( data.namedDest && !ld )
+		{
+			d->name = QString::fromLatin1( data.namedDest->c_str() );
+		}
+		
+		if (!ld) return;
+		
+		if (ld->getKind() == ::destXYZ) d->kind = destXYZ;
+		else if (ld->getKind() == ::destFit) d->kind = destFit;
+		else if (ld->getKind() == ::destFitH) d->kind = destFitH;
+		else if (ld->getKind() == ::destFitV) d->kind = destFitV;
+		else if (ld->getKind() == ::destFitR) d->kind = destFitR;
+		else if (ld->getKind() == ::destFitB) d->kind = destFitB;
+		else if (ld->getKind() == ::destFitBH) d->kind = destFitBH;
+		else if (ld->getKind() == ::destFitBV) d->kind = destFitBV;
+
+		if ( !ld->isPageRef() ) d->pageNum = ld->getPageNum();
+		else
+		{
+			Ref ref = ld->getPageRef();
+			d->pageNum = data.doc->doc->findPage( ref.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<Kind>(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<bool>(tokens.at(7).toInt());
+		d->changeTop = static_cast<bool>(tokens.at(8).toInt());
+		d->changeZoom = static_cast<bool>(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<enum ::LinkRendition::RenditionOperation>(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 <aacid@kde.org>
+ * Copyright (C) 2007-2008, 2010, Pino Toscano <pino@kde.org>
+ * Copyright (C) 2010, 2012, Guillermo Amaral <gamaral@kdab.com>
+ * Copyright (C) 2012, Tobias Koenig <tokoe@kdab.com>
+ * Adapting code from
+ *   Copyright (C) 2004 by Enrico Ros <eros.kde@email.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtCore/QString>
+#include <QtCore/QRectF>
+#include <QtCore/QSharedDataPointer>
+#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. <gamaral@kde.org>
+ * Copyright (C) 2013 Albert Astals Cid <aacid@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtCore/QBuffer>
+
+#define BUFFER_MAX 4096
+
+namespace Poppler
+{
+
+class MediaRenditionPrivate
+{
+public:
+
+  MediaRenditionPrivate(::MediaRendition *rendition)
+  : rendition(rendition)
+  {
+  }
+  
+  ~MediaRenditionPrivate()
+  {
+    delete rendition;
+  }
+
+  ::MediaRendition *rendition;
+};
+
+MediaRendition::MediaRendition(::MediaRendition *rendition)
+  : d_ptr(new MediaRenditionPrivate(rendition))
+{
+}
+
+MediaRendition::~MediaRendition()
+{
+  delete d_ptr;
+}
+
+bool
+MediaRendition::isValid() const
+{
+  Q_D( const MediaRendition );
+  return d->rendition && d->rendition->isOk();
+}
+
+QString
+MediaRendition::contentType() const
+{
+  Q_ASSERT(isValid() && "Invalid media rendition.");
+  Q_D( const MediaRendition );
+  return UnicodeParsedString(d->rendition->getContentType());
+}
+
+QString
+MediaRendition::fileName() const
+{
+  Q_ASSERT(isValid() && "Invalid media rendition.");
+  Q_D( const MediaRendition );
+  return UnicodeParsedString(d->rendition->getFileName());
+}
+
+bool
+MediaRendition::isEmbedded() const
+{
+  Q_ASSERT(isValid() && "Invalid media rendition.");
+  Q_D( const MediaRendition );
+  return d->rendition->getIsEmbedded();
+}
+
+QByteArray
+MediaRendition::data() const
+{
+  Q_ASSERT(isValid() && "Invalid media rendition.");
+  Q_D( const MediaRendition );
+
+  Stream *s = d->rendition->getEmbbededStream();
+  if (!s)
+    return QByteArray();
+
+  QBuffer buffer;
+  unsigned char data[BUFFER_MAX];
+  int bread;
+
+  buffer.open(QIODevice::WriteOnly);
+  s->reset();
+  while ((bread = s->doGetChars(BUFFER_MAX, data)) != 0)
+    buffer.write(reinterpret_cast<const char *>(data), bread);
+  buffer.close();
+
+  return buffer.data();
+}
+
+bool
+MediaRendition::autoPlay() const
+{
+  Q_D( const MediaRendition );
+  if (d->rendition->getBEParameters()) {
+    return d->rendition->getBEParameters()->autoPlay;
+  } else if (d->rendition->getMHParameters()) {
+    return d->rendition->getMHParameters()->autoPlay;
+  } else qDebug("No BE or MH parameters to reference!");
+  return false;
+}
+
+bool
+MediaRendition::showControls() const
+{
+  Q_D( const MediaRendition );
+  if (d->rendition->getBEParameters()) {
+    return d->rendition->getBEParameters()->showControls;
+  } else if (d->rendition->getMHParameters()) {
+    return d->rendition->getMHParameters()->showControls;
+  } else qDebug("No BE or MH parameters to reference!");
+  return false;
+}
+
+float
+MediaRendition::repeatCount() const
+{
+  Q_D( const MediaRendition );
+  if (d->rendition->getBEParameters()) {
+    return d->rendition->getBEParameters()->repeatCount;
+  } else if (d->rendition->getMHParameters()) {
+    return d->rendition->getMHParameters()->repeatCount;
+  } else qDebug("No BE or MH parameters to reference!");
+  return 1.f;
+}
+
+QSize
+MediaRendition::size() const
+{
+  Q_D( const MediaRendition );
+  const MediaParameters *mp = 0;
+
+  if (d->rendition->getBEParameters())
+    mp = d->rendition->getBEParameters();
+  else if (d->rendition->getMHParameters())
+    mp = d->rendition->getMHParameters();
+  else qDebug("No BE or MH parameters to reference!");
+
+  if (mp)
+    return QSize(mp->windowParams.width, mp->windowParams.height);
+  return QSize();
+}
+
+}
+
diff --git a/qt4/src/poppler-media.h b/qt4/src/poppler-media.h
new file mode 100644
index 00000000..34e5c361
--- /dev/null
+++ b/qt4/src/poppler-media.h
@@ -0,0 +1,100 @@
+/* poppler-media.h: qt interface to poppler
+ * Copyright (C) 2012 Guillermo A. Amaral B. <gamaral@kde.org>
+ * Copyright (C) 2012, 2013 Albert Astals Cid <aacid@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtCore/QSize>
+#include <QtCore/QString>
+
+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 <pino@kde.org>
+ * Copyright (C) 2008, Albert Astals Cid <aacid@kde.org>
+ * Copyright (C) 2010, Carlos Garcia Campos <carlosgc@gnome.org>
+ * Copyright (C) 2012, Tobias Koenig <tobias.koenig@kdab.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtGui/QImage>
+
+namespace Poppler
+{
+
+class MovieData
+{
+public:
+	MovieData()
+	  : m_movieObj( 0 )
+	{
+	}
+
+	~MovieData()
+	{
+		delete m_movieObj;
+	}
+
+	Movie *m_movieObj;
+	QSize m_size;
+	int m_rotation;
+	QImage m_posterImage;
+	MovieObject::PlayMode m_playMode : 3;
+	bool m_showControls : 1;
+};
+
+MovieObject::MovieObject( AnnotMovie *ann )
+{
+	m_movieData = new MovieData();
+	m_movieData->m_movieObj = ann->getMovie()->copy();
+	//TODO: copy poster image
+
+	MovieActivationParameters *mp = m_movieData->m_movieObj->getActivationParameters();
+	int width, height;
+	m_movieData->m_movieObj->getFloatingWindowSize(&width, &height);
+	m_movieData->m_size = QSize(width, height);
+	m_movieData->m_rotation = m_movieData->m_movieObj->getRotationAngle();
+	m_movieData->m_showControls = mp->showControls;
+	m_movieData->m_playMode = (MovieObject::PlayMode)mp->repeatMode;
+}
+
+MovieObject::~MovieObject()
+{
+	delete m_movieData;
+}
+
+QString MovieObject::url() const
+{
+	GooString * goo = m_movieData->m_movieObj->getFileName();
+	return goo ? QString( goo->c_str() ) : QString();
+}
+
+QSize MovieObject::size() const
+{
+	return m_movieData->m_size;
+}
+
+int MovieObject::rotation() const
+{
+	return m_movieData->m_rotation;
+}
+
+bool MovieObject::showControls() const
+{
+	return m_movieData->m_showControls;
+}
+
+MovieObject::PlayMode MovieObject::playMode() const
+{
+	return m_movieData->m_playMode;
+}
+
+bool MovieObject::showPosterImage() const
+{
+	return (m_movieData->m_movieObj->getShowPoster() == true);
+}
+
+QImage MovieObject::posterImage() const
+{
+	return m_movieData->m_posterImage;
+}
+
+}
diff --git a/qt4/src/poppler-optcontent-private.h b/qt4/src/poppler-optcontent-private.h
new file mode 100644
index 00000000..b5e52999
--- /dev/null
+++ b/qt4/src/poppler-optcontent-private.h
@@ -0,0 +1,124 @@
+/* poppler-optcontent-private.h: qt interface to poppler
+ *
+ * Copyright (C) 2007, Brad Hards <bradh@kde.org>
+ * Copyright (C) 2008, Pino Toscano <pino@kde.org>
+ * Copyright (C) 2016, Albert Astals Cid <aacid@kde.org>
+ * Copyright (C) 2017, Hubert Figuière <hub@figuiere.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtCore/QMap>
+#include <QtCore/QSet>
+#include <QtCore/QString>
+
+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<OptContentItem *> setItemOn( OptContentItem *itemToSetOn );
+
+  private:
+    QList<OptContentItem*> 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<OptContentItem *> &changedItems);
+
+    QList<OptContentItem*> 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<OptContentItem*> 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<OptContentItem*> m_children;
+    OptContentItem *m_parent;
+    QList<RadioButtonGroup*> 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<QString, OptContentItem*> m_optContentItems;
+    QList<OptContentItem*> m_headerOptContentItems;
+    QList<RadioButtonGroup*> m_rbgroups;
+    OptContentItem *m_rootNode;
+
+    private:
+    void addChild( OptContentItem *parent, OptContentItem *child);
+    void parseOrderArray( OptContentItem *parentNode, Array *orderArray );
+  };
+}
+
+#endif
diff --git a/qt4/src/poppler-optcontent.cc b/qt4/src/poppler-optcontent.cc
new file mode 100644
index 00000000..0e7b5345
--- /dev/null
+++ b/qt4/src/poppler-optcontent.cc
@@ -0,0 +1,455 @@
+/* poppler-optcontent.cc: qt interface to poppler
+ *
+ * Copyright (C) 2007, Brad Hards <bradh@kde.org>
+ * Copyright (C) 2008, 2014, Pino Toscano <pino@kde.org>
+ * Copyright (C) 2008, Carlos Garcia Campos <carlosgc@gnome.org>
+ * Copyright (C) 2015-2017, Albert Astals Cid <aacid@kde.org>
+ * Copyright (C) 2017, Hubert Figuière <hub@figuiere.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtCore/QDebug>
+#include <QtCore/QtAlgorithms>
+
+#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<OptContentItem *> RadioButtonGroup::setItemOn( OptContentItem *itemToSetOn )
+  {
+    QSet<OptContentItem *> changedItems;
+    for (int i = 0; i < itemsInGroup.size(); ++i) {
+      OptContentItem *thisItem = itemsInGroup.at(i);
+      if (thisItem != itemToSetOn) {
+        QSet<OptContentItem *> 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<OptContentItem *> &changedItems)
+  {
+    if (state == m_state)
+        return;
+
+    m_state = state;
+    m_stateBackup = m_state;
+    changedItems.insert(this);
+    QSet<OptContentItem *> 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*> OptContentItem::recurseListChildren(bool includeMe) const
+  {
+    QSet<OptContentItem*> ret;
+    if (includeMe) {
+      ret.insert(const_cast<OptContentItem*>(this));
+    }
+    Q_FOREACH (OptContentItem *child, m_children) {
+      ret += child->recurseListChildren(true);
+    }
+    return ret;
+  }
+
+  OptContentModelPrivate::OptContentModelPrivate( OptContentModel *qq, OCGs *optContent )
+    : q(qq)
+  {
+    m_rootNode = new OptContentItem();
+    const auto &ocgs = optContent->getOCGs();
+
+    for (const auto& ocg : ocgs) {
+      OptContentItem *node = new OptContentItem( ocg.second.get() );
+      m_optContentItems.insert( QString::number( ocg.first.num ), node );
+    }
+
+    if ( optContent->getOrderArray() == 0 ) {
+      // no Order array, so drop them all at the top level
+      QMapIterator<QString, OptContentItem*> 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() ) {
+	const GooString *label = orderItem.getString();
+	OptContentItem *header = new OptContentItem ( UnicodeParsedString ( label ) );
+	m_headerOptContentItems.append( header );
+	addChild( parentNode, header );
+	parentNode = header;
+	lastItem = header;
+      } else {
+	qDebug() << "something unexpected";
+      }	
+    }
+  }
+
+  void OptContentModelPrivate::parseRBGroupsArray( Array *rBGroupArray )
+  {
+    if (! rBGroupArray) {
+      return;
+    }
+    // This is an array of array(s)
+    for (int i = 0; i < rBGroupArray->getLength(); ++i) {
+      Object rbObj = rBGroupArray->get(i);
+      if ( ! rbObj.isArray() ) {
+	qDebug() << "expected inner array, got:" << rbObj.getType();
+	return;
+      }
+      Array *rbarray = rbObj.getArray();
+      RadioButtonGroup *rbg = new RadioButtonGroup( this, rbarray );
+      m_rbgroups.append( rbg );
+    }
+  }
+
+  OptContentModel::OptContentModel( OCGs *optContent, QObject *parent)
+    : QAbstractItemModel(parent)
+  {
+    d = new OptContentModelPrivate( this, optContent );
+  }
+
+  OptContentModel::~OptContentModel()
+  {
+    delete d;
+  }
+
+  void OptContentModelPrivate::setRootNode(OptContentItem *node)
+  {
+    delete m_rootNode;
+    m_rootNode = node;
+    q->reset();
+  }
+
+  QModelIndex OptContentModel::index(int row, int column, const QModelIndex &parent) const
+  {
+    if (row < 0 || column != 0) {
+      return QModelIndex();
+    }
+
+    OptContentItem *parentNode = d->nodeFromIndex( parent );
+    if (row < parentNode->childList().count()) {
+      return createIndex(row, column, parentNode->childList().at(row));
+    }
+    return QModelIndex();
+  }
+
+  QModelIndex OptContentModel::parent(const QModelIndex &child) const
+  {
+    OptContentItem *childNode = d->nodeFromIndex( child );
+    if (!childNode) {
+      return QModelIndex();
+    }
+    return d->indexFromItem(childNode->parent(), child.column());
+  }
+
+  QModelIndex OptContentModelPrivate::indexFromItem(OptContentItem *node, int column) const
+  {
+    if (!node) {
+      return QModelIndex();
+    }
+    OptContentItem *parentNode = node->parent();
+    if (!parentNode) {
+      return QModelIndex();
+    }
+    const int row = parentNode->childList().indexOf(node);
+    return q->createIndex(row, column, node);
+  }
+ 
+  int OptContentModel::rowCount(const QModelIndex &parent) const
+  {
+    OptContentItem *parentNode = d->nodeFromIndex( parent );
+    if (!parentNode) {
+      return 0;
+    } else {
+      return parentNode->childList().count();
+    }
+  }
+
+  int OptContentModel::columnCount(const QModelIndex &parent) const
+  {
+    return 1;
+  }
+
+
+  QVariant OptContentModel::data(const QModelIndex &index, int role) const
+  {
+    OptContentItem *node = d->nodeFromIndex(index, true);
+    if (!node) {
+      return QVariant();
+    }
+
+    switch (role) {
+      case Qt::DisplayRole:
+        return node->name();
+        break;
+      case Qt::EditRole:
+        if (node->state() == OptContentItem::On) {
+          return true;
+        } else if (node->state() == OptContentItem::Off) {
+          return false;
+        }
+        break;
+      case Qt::CheckStateRole:
+        if (node->state() == OptContentItem::On) {
+          return Qt::Checked;
+        } else if (node->state() == OptContentItem::Off) {
+          return Qt::Unchecked;
+        }
+        break;
+    }
+
+    return QVariant();
+  }
+
+  bool OptContentModel::setData ( const QModelIndex & index, const QVariant & value, int role )
+  {
+    OptContentItem *node = d->nodeFromIndex(index, true);
+    if (!node) {
+      return false;
+    }
+
+    switch (role) {
+      case Qt::CheckStateRole:
+      {
+        const bool newvalue = value.toBool();
+        QSet<OptContentItem *> 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<LinkOCGStatePrivate*>(link->d_ptr)->popplerLinkOCGState;
+
+    QSet<OptContentItem *> changedItems;
+
+    const 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<OptContentItem *> 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<OptContentItem *>(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 <bradh@kde.org>
+ * Copyright (C) 2008, Pino Toscano <pino@kde.org>
+ * Copyright (C) 2016, Albert Astals Cid <aacid@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtCore/QAbstractListModel>
+
+#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 <aacid@kde.org>
+ * Copyright (C) 2008, Pino Toscano <pino@kde.org>
+ * Copyright (C) 2015 Adam Reichold <adamreichold@myopera.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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<Unicode> *u);
+  bool performSingleTextSearch(TextPage* textPage, QVector<Unicode> &u, double &sLeft, double &sTop, double &sRight, double &sBottom, Page::SearchDirection direction, bool sCase, bool sWords);
+  QList<QRectF> performMultipleTextSearch(TextPage* textPage, QVector<Unicode> &u, bool sCase, bool sWords);
+};
+
+}
+
+#endif
diff --git a/qt4/src/poppler-page-transition-private.h b/qt4/src/poppler-page-transition-private.h
new file mode 100644
index 00000000..63febb09
--- /dev/null
+++ b/qt4/src/poppler-page-transition-private.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2005, Albert Astals Cid
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+class Object;
+
+namespace Poppler {
+
+class PageTransitionParams {
+  public:
+    Object *dictObj;
+};
+
+}
diff --git a/qt4/src/poppler-page-transition.cc b/qt4/src/poppler-page-transition.cc
new file mode 100644
index 00000000..4fa39edd
--- /dev/null
+++ b/qt4/src/poppler-page-transition.cc
@@ -0,0 +1,101 @@
+/* PageTransition.cc
+ * Copyright (C) 2005, Net Integration Technologies, Inc.
+ * Copyright (C) 2015, Arseniy Lartsev <arseniy@alumni.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 &params)
+{
+  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 <bradh@frogmouth.net>
+ * Copyright (C) 2015, Arseniy Lartsev <arseniy@alumni.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtCore/qglobal.h>
+
+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 &params);
+
+  /** \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 <bradh@frogmouth.net>
+ * Copyright (C) 2005-2017, Albert Astals Cid <aacid@kde.org>
+ * Copyright (C) 2005, Stefan Kebekus <stefan.kebekus@math.uni-koeln.de>
+ * Copyright (C) 2006-2011, Pino Toscano <pino@kde.org>
+ * Copyright (C) 2008 Carlos Garcia Campos <carlosgc@gnome.org>
+ * Copyright (C) 2009 Shawn Rutledge <shawn.t.rutledge@gmail.com>
+ * Copyright (C) 2010, 2012, Guillermo Amaral <gamaral@kdab.com>
+ * Copyright (C) 2010 Suzuki Toshiya <mpsuzuki@hiroshima-u.ac.jp>
+ * Copyright (C) 2010 Matthias Fauconneau <matthias.fauconneau@gmail.com>
+ * Copyright (C) 2010 Hib Eris <hib@hiberis.nl>
+ * Copyright (C) 2012 Tobias Koenig <tokoe@kdab.com>
+ * Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
+ * Copyright (C) 2012, 2015 Adam Reichold <adamreichold@myopera.com>
+ * Copyright (C) 2012, 2013 Thomas Freitag <Thomas.Freitag@alfa.de>
+ * Copyright (C) 2015 William Bader <williambader@hotmail.com>
+ * Copyright (C) 2016 Arseniy Lartsev <arseniy@alumni.chalmers.se>
+ * Copyright (C) 2017 Adrian Johnson <ajohnson@redneon.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtCore/QHash>
+#include <QtCore/QMap>
+#include <QtCore/QVarLengthArray>
+#include <QtGui/QImage>
+#include <QtGui/QPainter>
+
+#include <config.h>
+#include <PDFDoc.h>
+#include <Catalog.h>
+#include <Form.h>
+#include <ErrorCodes.h>
+#include <TextOutputDev.h>
+#include <Annot.h>
+#include <Link.h>
+#include <ArthurOutputDev.h>
+#include <Rendition.h>
+#if defined(HAVE_SPLASH)
+#include <SplashOutputDev.h>
+#include <splash/SplashBitmap.h>
+#endif
+
+#include "poppler-private.h"
+#include "poppler-page-transition-private.h"
+#include "poppler-page-private.h"
+#include "poppler-link-extractor-private.h"
+#include "poppler-link-private.h"
+#include "poppler-annotation-private.h"
+#include "poppler-form.h"
+#include "poppler-media.h"
+
+namespace Poppler {
+
+Link* PageData::convertLinkActionToLink(::LinkAction * a, const QRectF &linkArea)
+{
+    return convertLinkActionToLink(a, parentDoc, linkArea);
+}
+
+Link* PageData::convertLinkActionToLink(::LinkAction * a, DocumentData *parentDoc, const QRectF &linkArea)
+{
+  if ( !a )
+    return NULL;
+
+  Link * popplerLink = NULL;
+  switch ( a->getKind() )
+  {
+    case actionGoTo:
+    {
+      LinkGoTo * g = (LinkGoTo *) a;
+      const LinkDestinationData ldd( g->getDest(), g->getNamedDest(), parentDoc, false );
+      // create link: no ext file, namedDest, object pointer
+      popplerLink = new LinkGoto( linkArea, QString::null, LinkDestination( ldd ) );
+    }
+    break;
+
+    case actionGoToR:
+    {
+      LinkGoToR * g = (LinkGoToR *) a;
+      // copy link file
+      const QString fileName = UnicodeParsedString( g->getFileName() );
+      const LinkDestinationData ldd( g->getDest(), g->getNamedDest(), parentDoc, !fileName.isEmpty() );
+      // ceate link: fileName, namedDest, object pointer
+      popplerLink = new LinkGoto( linkArea, fileName, LinkDestination( ldd ) );
+    }
+    break;
+
+    case actionLaunch:
+    {
+      LinkLaunch * e = (LinkLaunch *)a;
+      const GooString * p = e->getParams();
+      popplerLink = new LinkExecute( linkArea, e->getFileName()->c_str(), p ? p->c_str() : 0 );
+    }
+    break;
+
+    case actionNamed:
+    {
+      const char * name = ((LinkNamed *)a)->getName()->c_str();
+      if ( !strcmp( name, "NextPage" ) )
+        popplerLink = new LinkAction( linkArea, LinkAction::PageNext );
+      else if ( !strcmp( name, "PrevPage" ) )
+        popplerLink = new LinkAction( linkArea, LinkAction::PagePrev );
+      else if ( !strcmp( name, "FirstPage" ) )
+        popplerLink = new LinkAction( linkArea, LinkAction::PageFirst );
+      else if ( !strcmp( name, "LastPage" ) )
+        popplerLink = new LinkAction( linkArea, LinkAction::PageLast );
+      else if ( !strcmp( name, "GoBack" ) )
+        popplerLink = new LinkAction( linkArea, LinkAction::HistoryBack );
+      else if ( !strcmp( name, "GoForward" ) )
+        popplerLink = new LinkAction( linkArea, LinkAction::HistoryForward );
+      else if ( !strcmp( name, "Quit" ) )
+        popplerLink = new LinkAction( linkArea, LinkAction::Quit );
+      else if ( !strcmp( name, "GoToPage" ) )
+        popplerLink = new LinkAction( linkArea, LinkAction::GoToPage );
+      else if ( !strcmp( name, "Find" ) )
+        popplerLink = new LinkAction( linkArea, LinkAction::Find );
+      else if ( !strcmp( name, "FullScreen" ) )
+        popplerLink = new LinkAction( linkArea, LinkAction::Presentation );
+      else if ( !strcmp( name, "Print" ) )
+        popplerLink = new LinkAction( linkArea, LinkAction::Print );
+      else if ( !strcmp( name, "Close" ) )
+      {
+        // acroread closes the document always, doesnt care whether 
+        // its presentation mode or not
+        // popplerLink = new LinkAction( linkArea, LinkAction::EndPresentation );
+        popplerLink = new LinkAction( linkArea, LinkAction::Close );
+      }
+      else
+      {
+        // TODO
+      }
+    }
+    break;
+
+    case actionURI:
+    {
+      popplerLink = new LinkBrowse( linkArea, ((LinkURI *)a)->getURI()->c_str() );
+    }
+    break;
+
+    case actionSound:
+    {
+      ::LinkSound *ls = (::LinkSound *)a;
+      popplerLink = new LinkSound( linkArea, ls->getVolume(), ls->getSynchronous(), ls->getRepeat(), ls->getMix(), new SoundObject( ls->getSound() ) );
+    }
+    break;
+
+    case actionJavaScript:
+    {
+      ::LinkJavaScript *ljs = (::LinkJavaScript *)a;
+      popplerLink = new LinkJavaScript( linkArea, UnicodeParsedString(ljs->getScript()) );
+    }
+    break;
+
+    case actionMovie:
+    {
+      ::LinkMovie *lm = (::LinkMovie *)a;
+
+      const QString title = ( lm->hasAnnotTitle() ? UnicodeParsedString( lm->getAnnotTitle() ) : QString() );
+
+      Ref reference;
+      reference.num = reference.gen = -1;
+      if ( lm->hasAnnotRef() )
+        reference = *lm->getAnnotRef();
+
+      LinkMovie::Operation operation = LinkMovie::Play;
+      switch ( lm->getOperation() )
+      {
+        case ::LinkMovie::operationTypePlay:
+          operation = LinkMovie::Play;
+          break;
+        case ::LinkMovie::operationTypePause:
+          operation = LinkMovie::Pause;
+          break;
+        case ::LinkMovie::operationTypeResume:
+          operation = LinkMovie::Resume;
+          break;
+        case ::LinkMovie::operationTypeStop:
+          operation = LinkMovie::Stop;
+          break;
+      };
+
+      popplerLink = new LinkMovie( linkArea, operation, title, reference );
+    }
+    break;
+
+    case actionRendition:
+    {
+      ::LinkRendition *lrn = (::LinkRendition *)a;
+
+      Ref reference;
+      reference.num = reference.gen = -1;
+      if ( lrn->hasScreenAnnot() )
+        reference = lrn->getScreenAnnot();
+
+      popplerLink = new LinkRendition( linkArea, lrn->getMedia() ? lrn->getMedia()->copy() : NULL, lrn->getOperation(), UnicodeParsedString( lrn->getScript() ), reference );
+    }
+    break;
+
+    case actionOCGState:
+    {
+      ::LinkOCGState *plocg = (::LinkOCGState *)a;
+
+      LinkOCGStatePrivate *locgp = new LinkOCGStatePrivate( linkArea, plocg );
+      popplerLink = new LinkOCGState( locgp );
+    }
+
+    case actionUnknown:
+    break;
+  }
+
+  return popplerLink;
+}
+
+inline TextPage *PageData::prepareTextSearch(const QString &text, Page::Rotation rotate, QVector<Unicode> *u)
+{
+  const QChar * str = text.unicode();
+  const int len = text.length();
+  u->resize(len);
+  for (int i = 0; i < len; ++i) (*u)[i] = str[i].unicode();
+
+  const int rotation = (int)rotate * 90;
+
+  // fetch ourselves a textpage
+  TextOutputDev td(NULL, true, 0, false, false);
+  parentDoc->doc->displayPage( &td, index + 1, 72, 72, rotation, false, true, false,
+    NULL, NULL, NULL, NULL, true);
+  TextPage *textPage=td.takeText();
+  
+  return textPage;
+}
+
+inline bool PageData::performSingleTextSearch(TextPage* textPage, QVector<Unicode> &u, double &sLeft, double &sTop, double &sRight, double &sBottom, Page::SearchDirection direction, bool sCase, bool sWords)
+{
+  if (direction == Page::FromTop)
+    return textPage->findText( u.data(), u.size(),
+           true, true, false, false, sCase, false, sWords, &sLeft, &sTop, &sRight, &sBottom );
+  else if ( direction == Page::NextResult )
+    return textPage->findText( u.data(), u.size(),
+           false, true, true, false, sCase, false, sWords, &sLeft, &sTop, &sRight, &sBottom );
+  else if ( direction == Page::PreviousResult )
+    return textPage->findText( u.data(), u.size(),
+           false, true, true, false, sCase, true, sWords, &sLeft, &sTop, &sRight, &sBottom );
+
+  return false;
+}
+
+inline QList<QRectF> PageData::performMultipleTextSearch(TextPage* textPage, QVector<Unicode> &u, bool sCase, bool sWords)
+{
+  QList<QRectF> results;
+  double sLeft = 0.0, sTop = 0.0, sRight = 0.0, sBottom = 0.0;
+
+  while(textPage->findText( u.data(), u.size(),
+        false, true, true, false, sCase, false, sWords, &sLeft, &sTop, &sRight, &sBottom ))
+  {
+      QRectF result;
+
+      result.setLeft(sLeft);
+      result.setTop(sTop);
+      result.setRight(sRight);
+      result.setBottom(sBottom);
+
+      results.append(result);
+  }
+
+  return results;
+}
+
+Page::Page(DocumentData *doc, int index) {
+  m_page = new PageData();
+  m_page->index = index;
+  m_page->parentDoc = doc;
+  m_page->page = doc->doc->getPage(m_page->index + 1);
+  m_page->transition = 0;
+}
+
+Page::~Page()
+{
+  delete m_page->transition;
+  delete m_page;
+}
+
+QImage Page::renderToImage(double xres, double yres, int x, int y, int w, int h, Rotation rotate) const
+{
+  int rotation = (int)rotate * 90;
+  QImage img;
+  switch(m_page->parentDoc->m_backend)
+  {
+    case Poppler::Document::SplashBackend:
+    {
+#if defined(HAVE_SPLASH)
+      SplashColor bgColor;
+      bool overprintPreview = false;
+#ifdef SPLASH_CMYK
+      overprintPreview = m_page->parentDoc->m_hints & Document::OverprintPreview ? true : false;
+      if (overprintPreview)
+      {
+        Guchar c, m, y, k;
+
+        c = 255 - m_page->parentDoc->paperColor.blue();
+        m = 255 - m_page->parentDoc->paperColor.red();
+        y = 255 - m_page->parentDoc->paperColor.green();
+        k = c;
+        if (m < k) {
+          k = m;
+        }
+        if (y < k) {
+          k = y;
+        }
+        bgColor[0] = c - k;
+        bgColor[1] = m - k;
+        bgColor[2] = y - k;
+        bgColor[3] = k;
+        for (int i = 4; i < SPOT_NCOMPS + 4; i++) {
+          bgColor[i] = 0;
+        }
+      }
+      else
+#endif
+      {
+        bgColor[0] = m_page->parentDoc->paperColor.blue();
+        bgColor[1] = m_page->parentDoc->paperColor.green();
+        bgColor[2] = m_page->parentDoc->paperColor.red();
+      }
+
+      SplashColorMode colorMode = splashModeXBGR8;
+#ifdef SPLASH_CMYK
+      if (overprintPreview) colorMode = splashModeDeviceN8;
+#endif
+ 
+      SplashThinLineMode thinLineMode = splashThinLineDefault;
+      if (m_page->parentDoc->m_hints & Document::ThinLineShape) thinLineMode = splashThinLineShape;
+      if (m_page->parentDoc->m_hints & Document::ThinLineSolid) thinLineMode = splashThinLineSolid;
+
+      const bool ignorePaperColor = m_page->parentDoc->m_hints & Document::IgnorePaperColor;
+
+      SplashOutputDev splash_output(
+                  colorMode, 4,
+                  false,
+                  ignorePaperColor ? NULL : bgColor,
+                  true,
+                  thinLineMode,
+                  overprintPreview);
+
+      splash_output.setFontAntialias(m_page->parentDoc->m_hints & Document::TextAntialiasing ? true : false);
+      splash_output.setVectorAntialias(m_page->parentDoc->m_hints & Document::Antialiasing ? true : false);
+      splash_output.setFreeTypeHinting(m_page->parentDoc->m_hints & Document::TextHinting ? true : false,
+                                        m_page->parentDoc->m_hints & Document::TextSlightHinting ? true : false);
+
+      splash_output.startDoc(m_page->parentDoc->doc);
+
+      m_page->parentDoc->doc->displayPageSlice(&splash_output, m_page->index + 1, xres, yres,
+                                               rotation, false, true, false, x, y, w, h,
+                                               NULL, NULL, NULL, NULL, true);
+
+      SplashBitmap *bitmap = splash_output.getBitmap();
+
+      const int bw = bitmap->getWidth();
+      const int bh = bitmap->getHeight();
+      const int brs = bitmap->getRowSize();
+
+      // If we use DeviceN8, convert to XBGR8.
+      // If requested, also transfer Splash's internal alpha channel.
+      const SplashBitmap::ConversionMode mode = ignorePaperColor
+              ? SplashBitmap::conversionAlphaPremultiplied
+              : SplashBitmap::conversionOpaque;
+
+      const QImage::Format format = ignorePaperColor
+              ? QImage::Format_ARGB32_Premultiplied
+              : QImage::Format_RGB32;
+
+      if (bitmap->convertToXBGR(mode)) {
+          SplashColorPtr data = bitmap->getDataPtr();
+
+          if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
+              // Convert byte order from RGBX to XBGR.
+              for (int i = 0; i < bh; ++i) {
+                  for (int j = 0; j < bw; ++j) {
+                      SplashColorPtr pixel = &data[i * brs + j];
+
+                      qSwap(pixel[0], pixel[3]);
+                      qSwap(pixel[1], pixel[2]);
+                  }
+              }
+          }
+
+          // Construct a Qt image sharing the raw bitmap data.
+          img = QImage(data, bw, bh, brs, format).copy();
+      }
+#endif
+      break;
+    }
+    case Poppler::Document::ArthurBackend:
+    {
+      QSize size = pageSize();
+      QImage tmpimg(w == -1 ? qRound( size.width() * xres / 72.0 ) : w, h == -1 ? qRound( size.height() * yres / 72.0 ) : h, QImage::Format_ARGB32);
+
+      QPainter painter(&tmpimg);
+      renderToPainter(&painter, xres, yres, x, y, w, h, rotate, DontSaveAndRestore);
+      painter.end();
+      img = tmpimg;
+      break;
+    }
+  }
+
+  return img;
+}
+
+bool Page::renderToPainter(QPainter* painter, double xres, double yres, int x, int y, int w, int h, Rotation rotate, PainterFlags flags) const
+{
+  if (!painter)
+    return false;
+
+  switch(m_page->parentDoc->m_backend)
+  {
+    case Poppler::Document::SplashBackend:
+      return false;
+    case Poppler::Document::ArthurBackend:
+    {
+      const bool savePainter = !(flags & DontSaveAndRestore);
+      if (savePainter)
+         painter->save();
+      if (m_page->parentDoc->m_hints & Document::Antialiasing)
+          painter->setRenderHint(QPainter::Antialiasing);
+      if (m_page->parentDoc->m_hints & Document::TextAntialiasing)
+          painter->setRenderHint(QPainter::TextAntialiasing);
+      painter->translate(x == -1 ? 0 : -x, y == -1 ? 0 : -y);
+      ArthurOutputDev arthur_output(painter);
+      arthur_output.startDoc(m_page->parentDoc->doc->getXRef());
+      m_page->parentDoc->doc->displayPageSlice(&arthur_output,
+                                               m_page->index + 1,
+                                               xres,
+                                               yres,
+                                               (int)rotate * 90,
+                                               false,
+                                               true,
+                                               false,
+                                               x,
+                                               y,
+                                               w,
+                                               h);
+      if (savePainter)
+         painter->restore();
+      return true;
+    }
+  }
+  return false;
+}
+
+QImage Page::thumbnail() const
+{
+  unsigned char* data = 0;
+  int w = 0;
+  int h = 0;
+  int rowstride = 0;
+  bool r = m_page->page->loadThumb(&data, &w, &h, &rowstride);
+  QImage ret;
+  if (r)
+  {
+    // first construct a temporary image with the data got,
+    // then force a copy of it so we can free the raw thumbnail data
+    ret = QImage(data, w, h, rowstride, QImage::Format_RGB888).copy();
+    gfree(data);
+  }
+  return ret;
+}
+
+QString Page::text(const QRectF &r, TextLayout textLayout) const
+{
+  TextOutputDev *output_dev;
+  GooString *s;
+  const PDFRectangle *rect;
+  QString result;
+  
+  const bool rawOrder = textLayout == RawOrderLayout;
+  output_dev = new TextOutputDev(0, false, 0, rawOrder, false);
+  m_page->parentDoc->doc->displayPageSlice(output_dev, m_page->index + 1, 72, 72,
+      0, false, true, false, -1, -1, -1, -1,
+      NULL, NULL, NULL, NULL, true);
+  if (r.isNull())
+  {
+    rect = m_page->page->getCropBox();
+    s = output_dev->getText(rect->x1, rect->y1, rect->x2, rect->y2);
+  }
+  else
+  {
+    s = output_dev->getText(r.left(), r.top(), r.right(), r.bottom());
+  }
+
+  result = QString::fromUtf8(s->c_str());
+
+  delete output_dev;
+  delete s;
+  return result;
+}
+
+QString Page::text(const QRectF &r) const
+{
+  return text(r, PhysicalLayout);
+}
+
+bool Page::search(const QString &text, double &sLeft, double &sTop, double &sRight, double &sBottom, SearchDirection direction, SearchMode caseSensitive, Rotation rotate) const
+{
+  const bool sCase = caseSensitive == Page::CaseSensitive ? true : false;
+
+  QVector<Unicode> u;
+  TextPage *textPage = m_page->prepareTextSearch(text, rotate, &u);
+
+  const bool found = m_page->performSingleTextSearch(textPage, u, sLeft, sTop, sRight, sBottom, direction, sCase, false);
+
+  textPage->decRefCnt();
+
+  return found;
+}
+
+bool Page::search(const QString &text, double &sLeft, double &sTop, double &sRight, double &sBottom, SearchDirection direction, SearchFlags flags, Rotation rotate) const
+{
+  const bool sCase = flags.testFlag(IgnoreCase) ? false : true;
+  const bool sWords = flags.testFlag(WholeWords) ? true : false;
+
+  QVector<Unicode> 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<QRectF> Page::search(const QString &text, SearchMode caseSensitive, Rotation rotate) const
+{
+  const bool sCase = caseSensitive == Page::CaseSensitive ? true : false;
+
+  QVector<Unicode> u;
+  TextPage *textPage = m_page->prepareTextSearch(text, rotate, &u);
+
+  const QList<QRectF> results = m_page->performMultipleTextSearch(textPage, u, sCase, false);
+  
+  textPage->decRefCnt();
+
+  return results;
+}
+
+QList<QRectF> Page::search(const QString &text, SearchFlags flags, Rotation rotate) const
+{
+  const bool sCase = flags.testFlag(IgnoreCase) ? false : true;
+  const bool sWords = flags.testFlag(WholeWords) ? true : false;
+
+  QVector<Unicode> u;
+  TextPage *textPage = m_page->prepareTextSearch(text, rotate, &u);
+
+  const QList<QRectF> results = m_page->performMultipleTextSearch(textPage, u, sCase, sWords);
+
+  textPage->decRefCnt();
+
+  return results;
+}
+
+QList<TextBox*> Page::textList(Rotation rotate) const
+{
+  TextOutputDev *output_dev;
+  
+  QList<TextBox*> output_list;
+  
+  output_dev = new TextOutputDev(0, false, 0, false, false);
+  
+  int rotation = (int)rotate * 90;
+
+  m_page->parentDoc->doc->displayPageSlice(output_dev, m_page->index + 1, 72, 72,
+      rotation, false, false, false, -1, -1, -1, -1,
+      NULL, NULL, NULL, NULL, true);
+
+  TextWordList *word_list = output_dev->makeWordList();
+  
+  if (!word_list) {
+    delete output_dev;
+    return output_list;
+  }
+  
+  QHash<TextWord *, TextBox*> wordBoxMap;
+  
+  output_list.reserve(word_list->getLength());
+  for (int i = 0; i < word_list->getLength(); i++) {
+    TextWord *word = word_list->get(i);
+    GooString *gooWord = word->getText();
+    QString string = QString::fromUtf8(gooWord->c_str());
+    delete gooWord;
+    double xMin, yMin, xMax, yMax;
+    word->getBBox(&xMin, &yMin, &xMax, &yMax);
+    
+    TextBox* text_box = new TextBox(string, QRectF(xMin, yMin, xMax-xMin, yMax-yMin));
+    text_box->m_data->hasSpaceAfter = word->hasSpaceAfter() == true;
+    text_box->m_data->charBBoxes.reserve(word->getLength());
+    for (int j = 0; j < word->getLength(); ++j)
+    {
+        word->getCharBBox(j, &xMin, &yMin, &xMax, &yMax);
+        text_box->m_data->charBBoxes.append(QRectF(xMin, yMin, xMax-xMin, yMax-yMin));
+    }
+    
+    wordBoxMap.insert(word, text_box);
+    
+    output_list.append(text_box);
+  }
+  
+  for (int i = 0; i < word_list->getLength(); i++) {
+    TextWord *word = word_list->get(i);
+    TextBox* text_box = wordBoxMap.value(word);
+    text_box->m_data->nextWord = wordBoxMap.value(word->nextWord());
+  }
+  
+  delete word_list;
+  delete output_dev;
+  
+  return output_list;
+}
+
+PageTransition *Page::transition() const
+{
+  if (!m_page->transition) {
+    PageTransitionParams params;
+    Object o = m_page->page->getTrans();
+    params.dictObj = &o;
+    if (o.isDict()) m_page->transition = new PageTransition(params);
+  }
+  return m_page->transition;
+}
+
+Link *Page::action( PageAction act ) const
+{
+  if ( act == Page::Opening || act == Page::Closing )
+  {
+    Object o = m_page->page->getActions();
+    if (!o.isDict())
+    {
+      return 0;
+    }
+    Dict *dict = o.getDict();
+    const char *key = act == Page::Opening ? "O" : "C";
+    Object o2 = dict->lookup((char*)key);
+    ::LinkAction *lact = ::LinkAction::parseAction(&o2, m_page->parentDoc->doc->getCatalog()->getBaseURI() );
+    Link *popplerLink = NULL;
+    if (lact != NULL)
+    {
+      popplerLink = m_page->convertLinkActionToLink(lact, QRectF());
+      delete lact;
+    }
+    return popplerLink;
+  }
+  return 0;
+}
+
+QSizeF Page::pageSizeF() const
+{
+  Page::Orientation orient = orientation();
+  if ( ( Page::Landscape == orient ) || (Page::Seascape == orient ) ) {
+      return QSizeF( m_page->page->getCropHeight(), m_page->page->getCropWidth() );
+  } else {
+    return QSizeF( m_page->page->getCropWidth(), m_page->page->getCropHeight() );
+  }
+}
+
+QSize Page::pageSize() const
+{
+  return pageSizeF().toSize();
+}
+
+Page::Orientation Page::orientation() const
+{
+  const int rotation = m_page->page->getRotate();
+  switch (rotation) {
+  case 90:
+    return Page::Landscape;
+    break;
+  case 180:
+    return Page::UpsideDown;
+    break;
+  case 270:
+    return Page::Seascape;
+    break;
+  default:
+    return Page::Portrait;
+  }
+}
+
+void Page::defaultCTM(double *CTM, double dpiX, double dpiY, int rotate, bool upsideDown)
+{
+  m_page->page->getDefaultCTM(CTM, dpiX, dpiY, rotate, false, upsideDown);
+}
+
+QList<Link*> Page::links() const
+{
+  LinkExtractorOutputDev link_dev(m_page);
+  m_page->parentDoc->doc->processLinks(&link_dev, m_page->index + 1);
+  QList<Link*> popplerLinks = link_dev.links();
+
+  return popplerLinks;
+}
+
+QList<Annotation*> Page::annotations() const
+{
+  return AnnotationPrivate::findAnnotations(m_page->page, m_page->parentDoc, QSet<Annotation::SubType>());
+}
+
+QList<Annotation*> Page::annotations(const QSet<Annotation::SubType> &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<FormField*> Page::formFields() const
+{
+  QList<FormField*> 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<FormWidgetButton*>(fm));
+      }
+      break;
+
+      case formText:
+      {
+        ff = new FormFieldText(m_page->parentDoc, p, static_cast<FormWidgetText*>(fm));
+      }
+      break;
+
+      case formChoice:
+      {
+        ff = new FormFieldChoice(m_page->parentDoc, p, static_cast<FormWidgetChoice*>(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 <pino@kde.org>
+ * Copyright (C) 2008, 2009, Albert Astals Cid <aacid@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtCore/QFile>
+
+#include <ErrorCodes.h>
+
+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<QFile*>(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<QFile*>(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 <aacid@kde.org>
+ * Copyright (C) 2008, 2010, 2011 by Pino Toscano <pino@kde.org>
+ * Copyright (C) 2013 by Thomas Freitag <Thomas.Freitag@alfa.de>
+ * Copyright (C) 2013 Adrian Johnson <ajohnson@redneon.com>
+ * Copyright (C) 2016 Jakub Alba <jakubalba@gmail.com>
+ * Inspired on code by
+ * Copyright (C) 2004 by Albert Astals Cid <tsdgeos@terra.es>
+ * Copyright (C) 2004 by Enrico Ros <eros.kde@email.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtCore/QByteArray>
+#include <QtCore/QDebug>
+#include <QtCore/QVariant>
+
+#include <Link.h>
+#include <Outline.h>
+#include <PDFDocEncoding.h>
+#include <UnicodeMap.h>
+
+namespace Poppler {
+
+namespace Debug {
+
+    static void qDebugDebugFunction(const QString &message, const QVariant & /*closure*/)
+    {
+        qDebug() << message;
+    }
+
+    PopplerDebugFunc debugFunction = qDebugDebugFunction;
+    QVariant debugClosure;
+
+}
+
+    static UnicodeMap *utf8Map = 0;
+
+    void setDebugErrorFunction(PopplerDebugFunc function, const QVariant &closure)
+    {
+        Debug::debugFunction = function ? function : Debug::qDebugDebugFunction;
+        Debug::debugClosure = closure;
+    }
+
+    static void qt4ErrorFunction(void * /*data*/, ErrorCategory /*category*/, Goffset pos, const char *msg)
+    {
+        QString emsg;
+
+        if (pos >= 0)
+        {
+            emsg = QString::fromLatin1("Error (%1): ").arg(pos);
+        }
+        else
+        {
+            emsg = QString::fromLatin1("Error: ");
+        }
+        emsg += QString::fromAscii(msg);
+        (*Debug::debugFunction)(emsg, Debug::debugClosure);
+    }
+
+    QString unicodeToQString(const Unicode* u, int len) {
+        if (!utf8Map)
+        {
+                GooString enc("UTF-8");
+                utf8Map = globalParams->getUnicodeMap(&enc);
+                utf8Map->incRefCnt();
+        }
+
+        // ignore the last character if it is 0x0
+        if ((len > 0) && (u[len - 1] == 0))
+        {
+            --len;
+        }
+
+        GooString convertedStr;
+        for (int i = 0; i < len; ++i)
+        {
+            char buf[8];
+            const int n = utf8Map->mapUnicode(u[i], buf, sizeof(buf));
+            convertedStr.append(buf, n);
+        }
+
+        return QString::fromUtf8(convertedStr.c_str(), convertedStr.getLength());
+    }
+
+    QString UnicodeParsedString(const GooString *s1) {
+        if ( !s1 || s1->getLength() == 0 )
+            return QString();
+
+        const char *cString;
+        int stringLength;
+        bool deleteCString;
+        if ( ( s1->getChar(0) & 0xff ) == 0xfe && ( s1->getLength() > 1 && ( s1->getChar(1) & 0xff ) == 0xff ) )
+        {
+            cString = s1->c_str();
+            stringLength = s1->getLength();
+            deleteCString = false;
+        }
+        else
+        {
+            cString = pdfDocEncodingToUTF16(s1, &stringLength);
+            deleteCString = true;
+        }
+
+        QString result;
+        // i = 2 to skip the unicode marker
+        for ( int i = 2; i < stringLength; i += 2 )
+        {
+            const Unicode u = ( ( cString[i] & 0xff ) << 8 ) | ( cString[i+1] & 0xff );
+            result += QChar( u );
+        }
+        if (deleteCString)
+            delete[] cString;
+        return result;
+    }
+
+    GooString *QStringToUnicodeGooString(const QString &s) {
+        int len = s.length() * 2 + 2;
+        char *cstring = (char *)gmallocn(len, sizeof(char));
+        cstring[0] = (char)0xfe;
+        cstring[1] = (char)0xff;
+        for (int i = 0; i < s.length(); ++i)
+        {
+            cstring[2+i*2] = s.at(i).row();
+            cstring[3+i*2] = s.at(i).cell();
+        }
+        GooString *ret = new GooString(cstring, len);
+        gfree(cstring);
+        return ret;
+    }
+
+    GooString *QStringToGooString(const QString &s) {
+        int len = s.length();
+        char *cstring = (char *)gmallocn(s.length(), sizeof(char));
+        for (int i = 0; i < len; ++i)
+            cstring[i] = s.at(i).unicode();
+        GooString *ret = new GooString(cstring, len);
+        gfree(cstring);
+        return ret;
+    }
+
+    GooString *QDateTimeToUnicodeGooString(const QDateTime &dt) {
+        if (!dt.isValid()) {
+            return NULL;
+        }
+
+        return QStringToUnicodeGooString(dt.toUTC().toString("yyyyMMddhhmmss+00'00'"));
+    }
+
+    static void linkActionToTocItem( const ::LinkAction * a, DocumentData * doc, QDomElement * e )
+    {
+        if ( !a || !e )
+            return;
+
+        switch ( a->getKind() )
+        {
+            case actionGoTo:
+            {
+                // page number is contained/referenced in a LinkGoTo
+                const LinkGoTo * g = static_cast< const LinkGoTo * >( a );
+                const LinkDest * destination = g->getDest();
+                if ( !destination && g->getNamedDest() )
+                {
+                    // no 'destination' but an internal 'named reference'. we could
+                    // get the destination for the page now, but it's VERY time consuming,
+                    // so better storing the reference and provide the viewport on demand
+                    const GooString *s = g->getNamedDest();
+                    QChar *charArray = new QChar[s->getLength()];
+                    for (int i = 0; i < s->getLength(); ++i) charArray[i] = QChar(s->c_str()[i]);
+                    QString aux(charArray, s->getLength());
+                    e->setAttribute( "DestinationName", aux );
+                    delete[] charArray;
+                }
+                else if ( destination && destination->isOk() )
+                {
+                    LinkDestinationData ldd(destination, NULL, doc, false);
+                    e->setAttribute( "Destination", LinkDestination(ldd).toString() );
+                }
+                break;
+            }
+            case actionGoToR:
+            {
+                // page number is contained/referenced in a LinkGoToR
+                const LinkGoToR * g = static_cast< const LinkGoToR * >( a );
+                const LinkDest * destination = g->getDest();
+                if ( !destination && g->getNamedDest() )
+                {
+                    // no 'destination' but an internal 'named reference'. we could
+                    // get the destination for the page now, but it's VERY time consuming,
+                    // so better storing the reference and provide the viewport on demand
+                    const GooString *s = g->getNamedDest();
+                    QChar *charArray = new QChar[s->getLength()];
+                    for (int i = 0; i < s->getLength(); ++i) charArray[i] = QChar(s->c_str()[i]);
+                    QString aux(charArray, s->getLength());
+                    e->setAttribute( "DestinationName", aux );
+                    delete[] charArray;
+                }
+                else if ( destination && destination->isOk() )
+                {
+                    LinkDestinationData ldd(destination, NULL, doc, g->getFileName() != 0);
+                    e->setAttribute( "Destination", LinkDestination(ldd).toString() );
+                }
+                e->setAttribute( "ExternalFileName", g->getFileName()->c_str() );
+                break;
+            }
+            case actionURI:
+            {
+                const LinkURI * u = static_cast< const LinkURI * >( a );
+                e->setAttribute( "DestinationURI", u->getURI()->c_str() );
+            }
+            default: ;
+        }
+    }
+    
+    DocumentData::~DocumentData()
+    {
+        qDeleteAll(m_embeddedFiles);
+        delete (OptContentModel *)m_optContentModel;
+        delete doc;
+        delete m_fontInfoIterator;
+    
+        count --;
+        if ( count == 0 )
+        {
+            utf8Map = 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, nullptr);
+        }
+        count ++;
+    }
+
+
+    void DocumentData::addTocChildren( QDomDocument * docSyn, QDomNode * parent, const 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;
+            const Unicode * uniChar = outlineItem->getTitle();
+            int titleLength = outlineItem->getTitleLength();
+            name = unicodeToQString(uniChar, titleLength);
+            if ( name.isEmpty() )
+                continue;
+
+            QDomElement item = docSyn->createElement( name );
+            parent->appendChild( item );
+
+            // 2. find the page the link refers to
+            const ::LinkAction * a = outlineItem->getAction();
+            linkActionToTocItem( a, this, &item );
+
+            item.setAttribute( "Open", QVariant( (bool)outlineItem->isOpen() ).toString() );
+
+            // 3. recursively descend over children
+            outlineItem->open();
+            const 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 <bradh@frogmouth.net>
+ * Copyright (C) 2006-2009, 2011, 2012, 2017 by Albert Astals Cid <aacid@kde.org>
+ * Copyright (C) 2007-2009, 2011 by Pino Toscano <pino@kde.org>
+ * Copyright (C) 2011 Andreas Hartmetz <ahartmetz@gmail.com>
+ * Copyright (C) 2011 Hib Eris <hib@hiberis.nl>
+ * Copyright (C) 2012, 2013 Thomas Freitag <Thomas.Freitag@alfa.de>
+ * Copyright (C) 2013 Julien Nabet <serval2412@yahoo.fr>
+ * Copyright (C) 2016 Jakub Alba <jakubalba@gmail.com>
+ * Inspired on code by
+ * Copyright (C) 2004 by Albert Astals Cid <tsdgeos@terra.es>
+ * Copyright (C) 2004 by Enrico Ros <eros.kde@email.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtCore/QFile>
+#include <QtCore/QPointer>
+#include <QtCore/QVector>
+
+#include <config.h>
+#include <GfxState.h>
+#include <GlobalParams.h>
+#include <PDFDoc.h>
+#include <FontInfo.h>
+#include <OutputDev.h>
+#include <Error.h>
+#if defined(HAVE_SPLASH)
+#include <SplashOutputDev.h>
+#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<EmbeddedFile*> m_embeddedFiles;
+	QPointer<OptContentModel> m_optContentModel;
+	QColor paperColor;
+	int m_hints;
+	static int count;
+    };
+
+    class FontInfoData
+    {
+	public:
+		FontInfoData()
+		{
+			isEmbedded = false;
+			isSubset = false;
+			type = FontInfo::unknown;
+		}
+		
+		FontInfoData( const FontInfoData &fid )
+		{
+			fontName = fid.fontName;
+			fontFile = fid.fontFile;
+			isEmbedded = fid.isEmbedded;
+			isSubset = fid.isSubset;
+			type = fid.type;
+			embRef = fid.embRef;
+		}
+		
+		FontInfoData( ::FontInfo* fi )
+		{
+			if (fi->getName()) fontName = fi->getName()->c_str();
+			if (fi->getFile()) fontFile = fi->getFile()->c_str();
+			isEmbedded = fi->getEmbedded();
+			isSubset = fi->getSubset();
+			type = (Poppler::FontInfo::Type)fi->getType();
+			embRef = fi->getEmbRef();
+		}
+
+		QString fontName;
+		QString fontFile;
+		bool isEmbedded : 1;
+		bool isSubset : 1;
+		FontInfo::Type type;
+		Ref embRef;
+    };
+
+    class FontIteratorData
+    {
+	public:
+		FontIteratorData( int startPage, DocumentData *dd )
+		  : fontInfoScanner( dd->doc, startPage )
+		  , totalPages( dd->doc->getNumPages() )
+		  , currentPage( qMax( startPage, 0 ) - 1 )
+		{
+		}
+
+		~FontIteratorData()
+		{
+		}
+
+		FontInfoScanner fontInfoScanner;
+		int totalPages;
+		int currentPage;
+    };
+
+    class TextBoxData
+    {
+	public:
+		TextBoxData()
+		  : nextWord(0), hasSpaceAfter(false)
+		{
+		}
+
+		QString text;
+		QRectF bBox;
+		TextBox *nextWord;
+		QVector<QRectF> 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 <aacid@kde.org>
+ * Copyright (C) 2008, Pino Toscano <pino@kde.org>
+ * Copyright (C) 2010 Hib Eris <hib@hiberis.nl>
+ * Copyright (C) 2011 Glad Deschrijver <glad.deschrijver@gmail.com>
+ * Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
+ * Copyright (C) 2013 Thomas Freitag <Thomas.Freitag@alfa.de>
+ * Copyright (C) 2014 Adrian Johnson <ajohnson@redneon.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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<QIODevice*>(stream)->write(data, len);
+}
+
+namespace Poppler {
+
+class PSConverterPrivate : public BaseConverterPrivate
+{
+	public:
+		PSConverterPrivate();
+
+		QList<int> 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<int> &pageList)
+{
+	Q_D(PSConverter);
+	d->pageList = pageList;
+}
+
+void PSConverter::setTitle(const QString &title)
+{
+	Q_D(PSConverter);
+	d->title = title;
+}
+
+void PSConverter::setHDPI(double hDPI)
+{
+	Q_D(PSConverter);
+	d->hDPI = hDPI;
+}
+
+void PSConverter::setVDPI(double vDPI)
+{
+	Q_D(PSConverter);
+	d->vDPI = vDPI;
+}
+
+void PSConverter::setRotate(int rotate)
+{
+	Q_D(PSConverter);
+	d->rotate = rotate;
+}
+
+void PSConverter::setPaperWidth(int paperWidth)
+{
+	Q_D(PSConverter);
+	d->paperWidth = paperWidth;
+}
+
+void PSConverter::setPaperHeight(int paperHeight)
+{
+	Q_D(PSConverter);
+	d->paperHeight = paperHeight;
+}
+
+void PSConverter::setRightMargin(int marginRight)
+{
+	Q_D(PSConverter);
+	d->marginRight = marginRight;
+}
+
+void PSConverter::setBottomMargin(int marginBottom)
+{
+	Q_D(PSConverter);
+	d->marginBottom = marginBottom;
+}
+
+void PSConverter::setLeftMargin(int marginLeft)
+{
+	Q_D(PSConverter);
+	d->marginLeft = marginLeft;
+}
+
+void PSConverter::setTopMargin(int marginTop)
+{
+	Q_D(PSConverter);
+	d->marginTop = marginTop;
+}
+
+void PSConverter::setStrictMargins(bool strictMargins)
+{
+	Q_D(PSConverter);
+	if (strictMargins)
+		d->opts |= StrictMargins;
+	else
+		d->opts &= ~StrictMargins;
+}
+
+void PSConverter::setForceRasterize(bool forceRasterize)
+{
+	Q_D(PSConverter);
+	if (forceRasterize)
+		d->opts |= ForceRasterization;
+	else
+		d->opts &= ~ForceRasterization;
+}
+
+void PSConverter::setPSOptions(PSConverter::PSOptions options)
+{
+	Q_D(PSConverter);
+	d->opts = options;
+}
+
+PSConverter::PSOptions PSConverter::psOptions() const
+{
+	Q_D(const PSConverter);
+	return d->opts;
+}
+
+void PSConverter::setPageConvertedCallback(void (* callback)(int page, void *payload), void *payload)
+{
+	Q_D(PSConverter);
+	d->pageConvertedCallback = callback;
+	d->pageConvertedPayload = payload;
+}
+
+static bool annotDisplayDecideCbk(Annot *annot, void *user_data)
+{
+	if (annot->getType() == Annot::typeWidget)
+		return true; // Never hide forms
+	else
+		return *(bool*)user_data;
+}
+
+bool PSConverter::convert()
+{
+	Q_D(PSConverter);
+	d->lastError = NoError;
+
+	Q_ASSERT(!d->pageList.isEmpty());
+	Q_ASSERT(d->paperWidth != -1);
+	Q_ASSERT(d->paperHeight != -1);
+	
+	if (d->document->locked)
+	{
+		d->lastError = FileLockedError;
+		return false;
+	}
+	
+	QIODevice *dev = d->openDevice();
+	if (!dev)
+	{
+		d->lastError = OpenOutputError;
+		return false;
+	}
+
+	QByteArray pstitle8Bit = d->title.toLocal8Bit();
+	char* pstitlechar;
+	if (!d->title.isEmpty()) pstitlechar = pstitle8Bit.data();
+	else pstitlechar = 0;
+	
+	std::vector<int> pages;
+	foreach(int page, d->pageList)
+	{
+		pages.push_back(page);
+	}
+
+	PSOutputDev *psOut = new PSOutputDev(outputToQIODevice, dev,
+	                                     pstitlechar,
+	                                     d->document->doc,
+	                                     pages,
+	                                     (d->opts & PrintToEPS) ? psModeEPS : psModePS,
+	                                     d->paperWidth,
+	                                     d->paperHeight,
+	                                     false,
+	                                     false,
+	                                     d->marginLeft,
+	                                     d->marginBottom,
+	                                     d->paperWidth - d->marginRight,
+	                                     d->paperHeight - d->marginTop,
+	                                     (d->opts & ForceRasterization));
+	
+	if (d->opts & StrictMargins)
+	{
+		double xScale = ((double)d->paperWidth - (double)d->marginLeft - (double)d->marginRight) / (double)d->paperWidth;
+		double yScale = ((double)d->paperHeight - (double)d->marginBottom - (double)d->marginTop) / (double)d->paperHeight;
+		psOut->setScale(xScale, yScale);
+	}
+	
+	if (psOut->isOk())
+	{
+		bool isPrinting = (d->opts & Printing) ? true : false;
+		bool showAnnotations = (d->opts & HideAnnotations) ? false : true;
+		foreach(int page, d->pageList)
+		{
+			d->document->doc->displayPage(psOut,
+			                              page,
+			                              d->hDPI,
+			                              d->vDPI,
+			                              d->rotate,
+			                              false,
+			                              true,
+			                              isPrinting,
+			                              NULL,
+			                              NULL,
+			                              annotDisplayDecideCbk,
+			                              &showAnnotations, true);
+			if (d->pageConvertedCallback)
+				(*d->pageConvertedCallback)(page, d->pageConvertedPayload);
+		}
+		delete psOut;
+		d->closeDevice();
+		return true;
+	}
+	else
+	{
+		delete psOut;
+		d->closeDevice();
+		return false;
+	}
+}
+
+}
diff --git a/qt4/src/poppler-qiodeviceoutstream-private.h b/qt4/src/poppler-qiodeviceoutstream-private.h
new file mode 100644
index 00000000..d0d20073
--- /dev/null
+++ b/qt4/src/poppler-qiodeviceoutstream-private.h
@@ -0,0 +1,47 @@
+/* poppler-qiodevicestream-private.h: Qt4 interface to poppler
+ * Copyright (C) 2008, Pino Toscano <pino@kde.org>
+ * Copyright (C) 2013 Adrian Johnson <ajohnson@redneon.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <pino@kde.org>
+ * Copyright (C) 2013 Adrian Johnson <ajohnson@redneon.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtCore/QIODevice>
+
+#include <stdio.h>
+
+#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 <bradh@frogmouth.net>
+ * Copyright (C) 2005-2012, 2014, 2015, Albert Astals Cid <aacid@kde.org>
+ * Copyright (C) 2005, Stefan Kebekus <stefan.kebekus@math.uni-koeln.de>
+ * Copyright (C) 2006-2011, Pino Toscano <pino@kde.org>
+ * Copyright (C) 2009 Shawn Rutledge <shawn.t.rutledge@gmail.com>
+ * Copyright (C) 2010 Suzuki Toshiya <mpsuzuki@hiroshima-u.ac.jp>
+ * Copyright (C) 2010 Matthias Fauconneau <matthias.fauconneau@gmail.com>
+ * Copyright (C) 2011 Andreas Hartmetz <ahartmetz@gmail.com>
+ * Copyright (C) 2011 Glad Deschrijver <glad.deschrijver@gmail.com>
+ * Copyright (C) 2012, Guillermo A. Amaral B. <gamaral@kde.org>
+ * Copyright (C) 2012, Fabio D'Urso <fabiodurso@hotmail.it>
+ * Copyright (C) 2012, Tobias Koenig <tobias.koenig@kdab.com>
+ * Copyright (C) 2012, 2014, 2015 Adam Reichold <adamreichold@myopera.com>
+ * Copyright (C) 2012, 2013 Thomas Freitag <Thomas.Freitag@alfa.de>
+ * Copyright (C) 2016 Jakub Alba <jakubalba@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtCore/QByteArray>
+#include <QtCore/QDateTime>
+#include <QtCore/QSet>
+#include <QtXml/QDomDocument>
+#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<Poppler::FontInfo> 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<FontInfo> 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<QRectF> 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<QRectF> 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<TextBox*> 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<Link*> 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<Annotation*> 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<Annotation*> annotations(const QSet<Annotation::SubType> &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<FormField*> 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<FontInfo> 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<FontInfo> *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<EmbeddedFile*> 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<RenderBackend> 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<int> &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 <pino@kde.org>
+ * Copyright (C) 2008, Albert Astals Cid <aacid@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "poppler-qt4.h"
+
+#include "Object.h"
+#include "Stream.h"
+#include "Sound.h"
+
+namespace Poppler
+{
+
+class SoundData
+{
+public:
+	SoundData()
+	  : m_soundObj( 0 )
+	{
+	}
+
+	~SoundData()
+	{
+		delete m_soundObj;
+	}
+
+	SoundObject::SoundType m_type;
+	Sound *m_soundObj;
+};
+
+SoundObject::SoundObject(Sound *popplersound)
+{
+	m_soundData = new SoundData();
+	switch ( popplersound->getSoundKind() )
+	{
+		case soundEmbedded:
+			m_soundData->m_type = SoundObject::Embedded;
+			break;
+		case soundExternal:
+		default:
+			m_soundData->m_type = SoundObject::External;
+			break;
+	}
+
+	m_soundData->m_soundObj = popplersound->copy();
+}
+
+SoundObject::~SoundObject()
+{
+	delete m_soundData;
+}
+
+SoundObject::SoundType SoundObject::soundType() const
+{
+	return m_soundData->m_type;
+}
+
+QString SoundObject::url() const
+{
+	if ( m_soundData->m_type != SoundObject::External )
+		return QString();
+
+	GooString * goo = m_soundData->m_soundObj->getFileName();
+	return goo ? QString( goo->c_str() ) : QString();
+}
+
+QByteArray SoundObject::data() const
+{
+	if ( m_soundData->m_type != SoundObject::Embedded )
+		return QByteArray();
+
+	Stream *stream = m_soundData->m_soundObj->getStream();
+	stream->reset();
+	int dataLen = 0;
+	QByteArray fileArray;
+	int i;
+	while ( (i = stream->getChar()) != EOF) {
+		fileArray[dataLen] = (char)i;
+		++dataLen;
+	}
+	fileArray.resize(dataLen);
+
+	return fileArray;
+}
+
+double SoundObject::samplingRate() const
+{
+	return m_soundData->m_soundObj->getSamplingRate();
+}
+
+int SoundObject::channels() const
+{
+	return m_soundData->m_soundObj->getChannels();
+}
+
+int SoundObject::bitsPerSample() const
+{
+	return m_soundData->m_soundObj->getBitsPerSample();
+}
+
+SoundObject::SoundEncoding SoundObject::soundEncoding() const
+{
+	switch ( m_soundData->m_soundObj->getEncoding() )
+	{
+		case soundRaw:
+			return SoundObject::Raw;
+		case soundSigned:
+			return SoundObject::Signed;
+		case soundMuLaw:
+			return SoundObject::muLaw;
+		case soundALaw:
+			return SoundObject::ALaw;
+	}
+	return SoundObject::Raw;
+}
+
+}
diff --git a/qt4/src/poppler-textbox.cc b/qt4/src/poppler-textbox.cc
new file mode 100644
index 00000000..88cf2a9e
--- /dev/null
+++ b/qt4/src/poppler-textbox.cc
@@ -0,0 +1,63 @@
+/* poppler-qt.h: qt interface to poppler
+ * Copyright (C) 2005, Brad Hards <bradh@frogmouth.net>
+ * Copyright (C) 2006-2008, Albert Astals Cid <aacid@kde.org>
+ * Copyright (C) 2008, Pino Toscano <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtTest/QtTest>
+
+#include <poppler-qt4.h>
+
+#include <QtCore/QFile>
+
+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 <QtTest/QtTest>
+
+#include <poppler-qt4.h>
+
+#include <QtCore/QFile>
+
+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<Poppler::EmbeddedFile*> 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<Poppler::EmbeddedFile*> 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<Poppler::EmbeddedFile*> 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<Poppler::EmbeddedFile*> 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 <QtTest/QtTest>
+
+Q_DECLARE_METATYPE(QDate)
+Q_DECLARE_METATYPE(QTime)
+
+#include <poppler-qt4.h>
+
+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>("QDate");
+    qRegisterMetaType<QTime>("QTime");
+}
+
+void TestDateConv::checkDates_data()
+{
+    QTest::addColumn<QByteArray>("input");
+    QTest::addColumn<QDate>("day");
+    QTest::addColumn<QTime>("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<QByteArray>("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 <QtTest/QtTest>
+
+#include <poppler-qt4.h>
+
+#include <memory>
+
+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<Poppler::FontInfo> loadFontsViaIterator( Poppler::Document *doc, int from = 0, int count = -1 )
+{
+    int num = count == -1 ? doc->numPages() - from : count;
+    QList<Poppler::FontInfo> 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<Poppler::FontInfo> 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<Poppler::FontInfo> 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<Poppler::FontInfo> 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<Poppler::FontInfo> 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<Poppler::FontInfo> listOfFonts = doc->fonts();
+    QCOMPARE( listOfFonts.size(), 2 );
+    // check we get the very same result when calling fonts() again (#19405)
+    QList<Poppler::FontInfo> 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<Poppler::FontInfo> listOfFonts = loadFontsViaIterator( doc );
+    QCOMPARE( listOfFonts.size(), 2 );
+    QList<Poppler::FontInfo> 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<Poppler::FontInfo> listOfFonts = doc->fonts();
+    QCOMPARE( listOfFonts.size(), 3 );
+    // check we get the very same result when gatering fonts using scanForFonts
+    QList<Poppler::FontInfo> 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<Poppler::FontInfo> 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 <QtCore/QScopedPointer>
+#include <QtTest/QtTest>
+
+#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<QByteArray>("string");
+    QTest::addColumn<QByteArray>("addition");
+    QTest::addColumn<int>("position");
+    QTest::addColumn<QByteArray>("result");
+
+    QTest::newRow("foo") << QByteArray("foo") << QByteArray("bar") << 0 << QByteArray("barfoo");
+    QTest::newRow("<empty>") << QByteArray() << QByteArray("bar") << 0 << QByteArray("bar");
+    QTest::newRow("foo+bar #1") << QByteArray("f+bar") << QByteArray("oo") << 1 << QByteArray("foo+bar");
+    QTest::newRow("foo+bar #2") << QByteArray("fobar") << QByteArray("o+") << 2 << QByteArray("foo+bar");
+    QTest::newRow("foo+bar #last") << QByteArray("foo+r") << QByteArray("ba") << 4 << QByteArray("foo+bar");
+    QTest::newRow("foo+bar #end") << QByteArray("foo+") << QByteArray("bar") << 4 << QByteArray("foo+bar");
+    QTest::newRow("long #start") << QByteArray("very string") << QByteArray("long long long long long ") << 5 << QByteArray("very long long long long long string");
+}
+
+void TestGooString::testInsertData()
+{
+    QFETCH(QByteArray, string);
+    QFETCH(QByteArray, addition);
+    QFETCH(int, position);
+    QFETCH(QByteArray, result);
+
+    GooString goo(string.constData());
+    QCOMPARE(goo.c_str(), string.constData());
+    goo.insert(position, addition.constData());
+    QCOMPARE(goo.c_str(), result.constData());
+}
+
+void TestGooString::testInsert()
+{
+    {
+    GooString goo;
+    goo.insert(0, ".");
+    goo.insert(0, "This is a very long long test string");
+    QCOMPARE(goo.c_str(), "This is a very long long test string.");
+    }
+    {
+    GooString goo;
+    goo.insert(0, "second-part-third-part");
+    goo.insert(0, "first-part-");
+    QCOMPARE(goo.c_str(), "first-part-second-part-third-part");
+    }
+}
+
+void TestGooString::testFormat()
+{
+    {
+        const QScopedPointer<GooString> goo(GooString::format("{0:d},{1:x}", 1, 0xF));
+        QCOMPARE(goo->c_str(), "1,f");
+    }
+    {
+        const QScopedPointer<GooString> goo(GooString::format("{0:d},{0:x},{0:X},{0:o},{0:b},{0:w}", 0xA));
+        QCOMPARE(goo->c_str(), "10,a,A,12,1010,          ");
+    }
+    {
+        const QScopedPointer<GooString> goo(GooString::format("{0:d},{0:x},{0:X},{0:o},{0:b}", -0xA));
+        QCOMPARE(goo->c_str(), "-10,-a,-A,-12,-1010");
+    }
+    {
+        const QScopedPointer<GooString> goo(GooString::format("{0:c}{1:c}{2:c}{3:c}",
+            'T', (char)'E', (short)'S', (int)'T'));
+        QCOMPARE(goo->c_str(), "TEST");
+
+        const QScopedPointer<GooString> goo2(GooString::format("{0:s} {1:t}", "TEST", goo.data()));
+        QCOMPARE(goo2->c_str(), "TEST TEST");
+    }
+    {
+        const QScopedPointer<GooString> goo(GooString::format("{0:ud} {1:d} {2:d}",
+            UINT_MAX, INT_MAX, INT_MIN));
+        const QByteArray expected = QString("%1 %2 %3").arg(UINT_MAX).arg(INT_MAX).arg(INT_MIN).toLatin1();
+        QCOMPARE(goo->c_str(), expected.constData());
+    }
+    {
+        const QScopedPointer<GooString> goo(GooString::format("{0:uld} {1:ld} {2:ld}",
+            ULONG_MAX, LONG_MAX, LONG_MIN));
+        const QByteArray expected = QString("%1 %2 %3").arg(ULONG_MAX).arg(LONG_MAX).arg(LONG_MIN).toLatin1();
+        QCOMPARE(goo->c_str(), expected.constData());
+    }
+    {
+        const QScopedPointer<GooString> goo(GooString::format("{0:ulld} {1:lld} {2:lld}",
+            ULLONG_MAX, LLONG_MAX, LLONG_MIN));
+        const QByteArray expected = QString("%1 %2 %3").arg(ULLONG_MAX).arg(LLONG_MAX).arg(LLONG_MIN).toLatin1();
+        QCOMPARE(goo->c_str(), expected.constData());
+    }
+    {
+        const QScopedPointer<GooString> gooD(GooString::format("{0:.1f} {0:.1g} {0:.1gs} | {1:.1f} {1:.1g} {1:.1gs}", 1., .012));
+        const QScopedPointer<GooString> gooF(GooString::format("{0:.1f} {0:.1g} {0:.1gs} | {1:.1f} {1:.1g} {1:.1gs}", 1.f, .012f));
+        QCOMPARE(gooD->c_str(), "1.0 1 1 | 0.0 0 0.01");
+        QCOMPARE(gooF->c_str(), "1.0 1 1 | 0.0 0 0.01");
+    }
+    {
+        const QScopedPointer<GooString> goo(GooString::format("{0:.4f} {0:.4g} {0:.4gs}", .012));
+        QCOMPARE(goo->c_str(), "0.0120 0.012 0.012");
+    }
+    {
+        const QScopedPointer<GooString> goo(GooString::format("{{ SomeText {0:d} }}", 1));
+        QCOMPARE(goo->c_str(), "{ SomeText 1 }");
+    }
+    {
+        const QScopedPointer<GooString> goo(GooString::format("{{{{ {{ SomeText {0:d}", 2));
+        QCOMPARE(goo->c_str(), "{{ { SomeText 2");
+    }
+    {
+        const QScopedPointer<GooString> goo(GooString::format("SomeText {0:d} }} }}}}", 3));
+        QCOMPARE(goo->c_str(), "SomeText 3 } }}");
+    }
+}
+
+QTEST_MAIN(TestGooString)
+#include "check_goostring.moc"
+
diff --git a/qt4/tests/check_lexer.cpp b/qt4/tests/check_lexer.cpp
new file mode 100644
index 00000000..93c3621d
--- /dev/null
+++ b/qt4/tests/check_lexer.cpp
@@ -0,0 +1,107 @@
+#include <QtTest/QtTest>
+
+#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 <QtTest/QtTest>
+
+#include <poppler-qt4.h>
+
+#include <memory>
+
+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 <QtTest/QtTest>
+
+#include <poppler-qt4.h>
+
+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<QString>("key");
+    QTest::addColumn<QString>("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<QString>("key");
+    QTest::addColumn<QString>("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 <QtTest/QtTest>
+
+#include "PDFDoc.h"
+#include "GlobalParams.h"
+
+#include <poppler-qt4.h>
+
+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<Qt::CheckState>( 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<Qt::CheckState>( 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<Qt::CheckState>( 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<Qt::CheckState>( 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<Qt::CheckState>( 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<Qt::CheckState>( 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<Qt::CheckState>( 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<</Type/OCMD/OCGs[21 0 R]/P/AnyOn>>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<</Type/OCMD/OCGs[21 0 R]/P/AnyOff>>endobj
+    obj = xref->fetch( 29, 0 );
+    QVERIFY( obj.isDict() );
+    QCOMPARE( ocgs->optContentIsVisible( &obj ), false );
+
+    // AllOn, one element array:
+    // 36 0 obj<</Type/OCMD/OCGs[28 0 R]/P/AllOn>>endobj
+    obj = xref->fetch( 36, 0 );
+    QVERIFY( obj.isDict() );
+    QCOMPARE( ocgs->optContentIsVisible( &obj ), true );
+
+
+    // AllOff, one element array:
+    // 43 0 obj<</Type/OCMD/OCGs[28 0 R]/P/AllOff>>endobj
+    obj = xref->fetch( 43, 0 );
+    QVERIFY( obj.isDict() );
+    QCOMPARE( ocgs->optContentIsVisible( &obj ), false );
+
+    // AnyOn, multi-element array:
+    // 50 0 obj<</Type/OCMD/OCGs[21 0 R 28 0 R]/P/AnyOn>>endobj
+    obj = xref->fetch( 50, 0 );
+    QVERIFY( obj.isDict() );
+    QCOMPARE( ocgs->optContentIsVisible( &obj ), true );
+
+    // AnyOff, multi-element array:
+    // 57 0 obj<</Type/OCMD/P/AnyOff/OCGs[21 0 R 28 0 R]>>endobj
+    obj = xref->fetch( 57, 0 );
+    QVERIFY( obj.isDict() );
+    QCOMPARE( ocgs->optContentIsVisible( &obj ), false );
+
+    // AllOn, multi-element array:
+    // 64 0 obj<</Type/OCMD/P/AllOn/OCGs[21 0 R 28 0 R]>>endobj
+    obj = xref->fetch( 64, 0 );
+    QVERIFY( obj.isDict() );
+    QCOMPARE( ocgs->optContentIsVisible( &obj ), true );
+
+    // AllOff, multi-element array:
+    // 71 0 obj<</Type/OCMD/P/AllOff/OCGs[21 0 R 28 0 R]>>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<</Type/OCMD/OCGs[21 0 R]/P/AnyOn>>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<</Type/OCMD/OCGs[21 0 R]/P/AnyOff>>endobj
+    obj = xref->fetch( 29, 0 );
+    QVERIFY( obj.isDict() );
+    QCOMPARE( ocgs->optContentIsVisible( &obj ), true );
+
+    // AllOn, one element array:
+    // 36 0 obj<</Type/OCMD/OCGs[28 0 R]/P/AllOn>>endobj
+    obj = xref->fetch( 36, 0 );
+    QVERIFY( obj.isDict() );
+    QCOMPARE( ocgs->optContentIsVisible( &obj ), true );
+
+    // AllOff, one element array:
+    // 43 0 obj<</Type/OCMD/OCGs[28 0 R]/P/AllOff>>endobj
+    obj = xref->fetch( 43, 0 );
+    QVERIFY( obj.isDict() );
+    QCOMPARE( ocgs->optContentIsVisible( &obj ), false );
+
+    // AnyOn, multi-element array:
+    // 50 0 obj<</Type/OCMD/OCGs[21 0 R 28 0 R]/P/AnyOn>>endobj
+    obj = xref->fetch( 50, 0 );
+    QVERIFY( obj.isDict() );
+    QCOMPARE( ocgs->optContentIsVisible( &obj ), true );
+
+    // AnyOff, multi-element array:
+    // 57 0 obj<</Type/OCMD/P/AnyOff/OCGs[21 0 R 28 0 R]>>endobj
+    obj = xref->fetch( 57, 0 );
+    QVERIFY( obj.isDict() );
+    QCOMPARE( ocgs->optContentIsVisible( &obj ), true );
+
+    // AllOn, multi-element array:
+    // 64 0 obj<</Type/OCMD/P/AllOn/OCGs[21 0 R 28 0 R]>>endobj
+    obj = xref->fetch( 64, 0 );
+    QVERIFY( obj.isDict() );
+    QCOMPARE( ocgs->optContentIsVisible( &obj ), false );
+
+    // AllOff, multi-element array:
+    // 71 0 obj<</Type/OCMD/P/AllOff/OCGs[21 0 R 28 0 R]>>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<</Type/OCMD/OCGs[21 0 R]/P/AnyOn>>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<</Type/OCMD/OCGs[21 0 R]/P/AnyOff>>endobj
+    obj = xref->fetch( 29, 0 );
+    QVERIFY( obj.isDict() );
+    QCOMPARE( ocgs->optContentIsVisible( &obj ), true );
+
+    // AllOn, one element array:
+    // 36 0 obj<</Type/OCMD/OCGs[28 0 R]/P/AllOn>>endobj
+    obj = xref->fetch( 36, 0 );
+    QVERIFY( obj.isDict() );
+    QCOMPARE( ocgs->optContentIsVisible( &obj ), false );
+
+    // AllOff, one element array:
+    // 43 0 obj<</Type/OCMD/OCGs[28 0 R]/P/AllOff>>endobj
+    obj = xref->fetch( 43, 0 );
+    QVERIFY( obj.isDict() );
+    QCOMPARE( ocgs->optContentIsVisible( &obj ), true );
+
+    // AnyOn, multi-element array:
+    // 50 0 obj<</Type/OCMD/OCGs[21 0 R 28 0 R]/P/AnyOn>>endobj
+    obj = xref->fetch( 50, 0 );
+    QVERIFY( obj.isDict() );
+    QCOMPARE( ocgs->optContentIsVisible( &obj ), false );
+
+    // AnyOff, multi-element array:
+    // 57 0 obj<</Type/OCMD/P/AnyOff/OCGs[21 0 R 28 0 R]>>endobj
+    obj = xref->fetch( 57, 0 );
+    QVERIFY( obj.isDict() );
+    QCOMPARE( ocgs->optContentIsVisible( &obj ), true );
+
+    // AllOn, multi-element array:
+    // 64 0 obj<</Type/OCMD/P/AllOn/OCGs[21 0 R 28 0 R]>>endobj
+    obj = xref->fetch( 64, 0 );
+    QVERIFY( obj.isDict() );
+    QCOMPARE( ocgs->optContentIsVisible( &obj ), false );
+
+    // AllOff, multi-element array:
+    // 71 0 obj<</Type/OCMD/P/AllOff/OCGs[21 0 R 28 0 R]>>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<</Type/OCMD/OCGs[21 0 R]/P/AnyOn>>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<</Type/OCMD/OCGs[21 0 R]/P/AnyOff>>endobj
+    obj = xref->fetch( 29, 0);
+    QVERIFY( obj.isDict() );
+    QCOMPARE( ocgs->optContentIsVisible( &obj ), false );
+
+    // AllOn, one element array:
+    // 36 0 obj<</Type/OCMD/OCGs[28 0 R]/P/AllOn>>endobj
+    obj = xref->fetch( 36, 0);
+    QVERIFY( obj.isDict() );
+    QCOMPARE( ocgs->optContentIsVisible( &obj ), false );
+
+    // AllOff, one element array:
+    // 43 0 obj<</Type/OCMD/OCGs[28 0 R]/P/AllOff>>endobj
+    obj = xref->fetch( 43, 0);
+    QVERIFY( obj.isDict() );
+    QCOMPARE( ocgs->optContentIsVisible( &obj ), true );
+
+    // AnyOn, multi-element array:
+    // 50 0 obj<</Type/OCMD/OCGs[21 0 R 28 0 R]/P/AnyOn>>endobj
+    obj = xref->fetch( 50, 0);
+    QVERIFY( obj.isDict() );
+    QCOMPARE( ocgs->optContentIsVisible( &obj ), true );
+
+    // AnyOff, multi-element array:
+    // 57 0 obj<</Type/OCMD/P/AnyOff/OCGs[21 0 R 28 0 R]>>endobj
+    obj = xref->fetch( 57, 0);
+    QVERIFY( obj.isDict() );
+    QCOMPARE( ocgs->optContentIsVisible( &obj ), true );
+
+    // AllOn, multi-element array:
+    // 64 0 obj<</Type/OCMD/P/AllOn/OCGs[21 0 R 28 0 R]>>endobj
+    obj = xref->fetch( 64, 0);
+    QVERIFY( obj.isDict() );
+    QCOMPARE( ocgs->optContentIsVisible( &obj ), false );
+
+    // AllOff, multi-element array:
+    // 71 0 obj<</Type/OCMD/P/AllOff/OCGs[21 0 R 28 0 R]>>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<Qt::CheckState>( 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<Qt::CheckState>( 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<Qt::CheckState>( 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<Qt::CheckState>( 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<Qt::CheckState>( 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<Qt::CheckState>( 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<Qt::CheckState>( 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<Qt::CheckState>( 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<Qt::CheckState>( 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<Qt::CheckState>( 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<Qt::CheckState>( 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<Qt::CheckState>( 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<Qt::CheckState>( 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 <QtTest/QtTest>
+
+#include "PageLabelInfo_p.h"
+
+class TestPageLabelInfo : public QObject
+{
+    Q_OBJECT
+private slots:
+    void testToRoman();
+    void testFromRoman();
+    void testToLatin();
+    void testFromLatin();
+};
+
+void TestPageLabelInfo::testToRoman()
+{
+    GooString str;
+    toRoman(177, &str, false);
+    QCOMPARE (str.c_str(), "clxxvii");
+}
+
+void TestPageLabelInfo::testFromRoman()
+{
+    GooString roman("clxxvii");
+    QCOMPARE(fromRoman(roman.c_str()), 177);
+}
+
+void TestPageLabelInfo::testToLatin()
+{
+    GooString str;
+    toLatin(54, &str, false);
+    QCOMPARE(str.c_str(), "bbb");
+}
+
+void TestPageLabelInfo::testFromLatin()
+{
+    GooString latin("ddd");
+    QCOMPARE(fromLatin(latin.c_str()), 56);
+}
+
+QTEST_MAIN(TestPageLabelInfo)
+#include "check_pagelabelinfo.moc"
+
diff --git a/qt4/tests/check_pagelayout.cpp b/qt4/tests/check_pagelayout.cpp
new file mode 100644
index 00000000..6108f886
--- /dev/null
+++ b/qt4/tests/check_pagelayout.cpp
@@ -0,0 +1,49 @@
+#include <QtTest/QtTest>
+
+#include <poppler-qt4.h>
+
+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 <QtTest/QtTest>
+
+#include <poppler-qt4.h>
+
+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 <QtTest/QtTest>
+
+#include <poppler-qt4.h>
+
+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 <QtTest/QtTest>
+
+#include <poppler-qt4.h>
+
+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 <QtTest/QtTest>
+
+#include <poppler-qt4.h>
+
+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 <pino@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 <QtTest/QtTest>
+
+#include <poppler-qt4.h>
+#include <poppler-private.h>
+
+#include <GlobalParams.h>
+
+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<GooString *> m_gooStrings;
+};
+
+void TestStrings::initTestCase()
+{
+    qRegisterMetaType<GooString*>("GooString*");
+    qRegisterMetaType<Unicode*>("Unicode*");
+
+    globalParams = new GlobalParams();
+}
+
+void TestStrings::cleanupTestCase()
+{
+    qDeleteAll(m_gooStrings);
+
+    delete globalParams;
+}
+
+void TestStrings::check_unicodeToQString_data()
+{
+    QTest::addColumn<Unicode*>("data");
+    QTest::addColumn<int>("length");
+    QTest::addColumn<QString>("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<GooString*>("string");
+    QTest::addColumn<QString>("result");
+
+    // non-unicode strings
+    QTest::newRow("<empty>") << 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("<unicode marks>") << 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<QString>("string");
+    QTest::addColumn<QByteArray>("result");
+
+
+    QTest::newRow("<null>") << QString()
+                            << QByteArray("");
+    QTest::newRow("<empty>") << QString::fromUtf8("")
+                             << QByteArray("");
+    QTest::newRow("a") << QString::fromUtf8("a")
+                       << QByteArray("\0a", 2);
+    QTest::newRow("ab") << QString::fromUtf8("ab")
+                        << QByteArray("\0a\0b", 4);
+    QTest::newRow("test string") << QString::fromUtf8("test string")
+                                 << QByteArray("\0t\0e\0s\0t\0 \0s\0t\0r\0i\0n\0g", 22);
+    QTest::newRow("\xC3\x9F") << QString::fromUtf8("\xC3\x9F")
+                              << QByteArray("\0\xDF", 2);
+    QTest::newRow("\xC3\x9F\x61") << QString::fromUtf8("\xC3\x9F\x61")
+                                  << QByteArray("\0\xDF\0\x61", 4);
+}
+
+void TestStrings::check_QStringToUnicodeGooString()
+{
+    QFETCH(QString, string);
+    QFETCH(QByteArray, result);
+
+    GooString *goo = Poppler::QStringToUnicodeGooString(string);
+    QVERIFY(goo->hasUnicodeMarker());
+    QCOMPARE(goo->getLength(), string.length() * 2 + 2);
+    QCOMPARE(result, QByteArray::fromRawData(goo->c_str() + 2, goo->getLength() - 2));
+
+    delete goo;
+}
+
+void TestStrings::check_QStringToGooString_data()
+{
+    QTest::addColumn<QString>("string");
+    QTest::addColumn<GooString*>("result");
+
+    QTest::newRow("<null>") << QString()
+                            << newGooString("");
+    QTest::newRow("<empty>") << QString::fromUtf8("")
+                             << newGooString("");
+    QTest::newRow("a") << QString::fromUtf8("a")
+                       << newGooString("a");
+    QTest::newRow("ab") << QString::fromUtf8("ab")
+                        << newGooString("ab");
+}
+
+void TestStrings::check_QStringToGooString()
+{
+    QFETCH(QString, string);
+    QFETCH(GooString*, result);
+
+    GooString *goo = Poppler::QStringToGooString(string);
+    QCOMPARE(goo->c_str(), result->c_str());
+
+    delete goo;
+}
+
+GooString* TestStrings::newGooString(const char *s)
+{
+    GooString *goo = new GooString(s);
+    m_gooStrings.append(goo);
+    return goo;
+}
+
+GooString* TestStrings::newGooString(const char *s, int l)
+{
+    GooString *goo = new GooString(s, l);
+    m_gooStrings.append(goo);
+    return goo;
+}
+
+QTEST_MAIN(TestStrings)
+
+#include "check_strings.moc"
diff --git a/qt4/tests/poppler-attachments.cpp b/qt4/tests/poppler-attachments.cpp
new file mode 100644
index 00000000..992dc565
--- /dev/null
+++ b/qt4/tests/poppler-attachments.cpp
@@ -0,0 +1,39 @@
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+
+#include <iostream>
+
+#include <poppler-qt4.h>
+
+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 <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+
+#include <iostream>
+
+#include <poppler-qt4.h>
+
+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 <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+
+#include <iostream>
+
+#include <poppler-qt4.h>
+#include <poppler-form.h>
+
+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<typename T>
+std::ostream& operator<< (std::ostream &out, const QList<T> &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<Poppler::FormField*> 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<const Poppler::FormFieldButton *>(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<const Poppler::FormFieldText *>(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<const Poppler::FormFieldChoice *>(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 <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+
+#include <iostream>
+
+#include <poppler-qt4.h>
+
+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 <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QTime>
+#include <QtGui/QApplication>
+#include <QtGui/QImage>
+
+#include <iostream>
+
+#include <poppler-qt4.h>
+
+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 <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QTime>
+#include <QtGui/QApplication>
+#include <QtGui/QImage>
+
+#include <iostream>
+
+#include <poppler-qt4.h>
+
+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 <unistd.h>
+#else
+#include <windows.h>
+#define sleep Sleep
+#endif
+#include <time.h>
+
+#include <poppler-qt4.h>
+#include <poppler-form.h>
+
+#include <QtCore/QDebug>
+#include <QtCore/QFile>
+#include <QtCore/QMutex>
+#include <QtCore/QThread>
+#include <QtGui/QImage>
+
+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 <QtCore/QDebug>
+#include <QtGui/QApplication>
+#include <QtGui/QImage>
+#include <QtGui/QPainter>
+#include <QtGui/QPaintEvent>
+#include <QtGui/QWidget>
+
+#include <poppler-qt4.h>
+
+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 <QtCore/QDebug>
+#include <QtCore/QFile>
+#include <QtGui/QApplication>
+#include <QtGui/QImage>
+#include <QtGui/QLabel>
+#include <QtGui/QMouseEvent>
+#include <QtGui/QPainter>
+#include <QtGui/QPaintEvent>
+#include <QtGui/QToolTip>
+#include <QtGui/QWidget>
+
+#include <poppler-qt4.h>
+
+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<Poppler::TextBox*> 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 <QtCore/QDebug>
+#include <QtCore/QFile>
+#include <QtGui/QApplication>
+#include <QtGui/QImage>
+
+#include <poppler-qt4.h>
+
+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