Merge remote-tracking branch 'origin/poppler-0.20'

Conflicts:
	poppler/Form.cc
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7386cfd..8836ae4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -15,8 +15,8 @@
 test_big_endian(WORDS_BIGENDIAN)
 
 set(POPPLER_MAJOR_VERSION "0")
-set(POPPLER_MINOR_VERSION "20")
-set(POPPLER_MICRO_VERSION "5")
+set(POPPLER_MINOR_VERSION "21")
+set(POPPLER_MICRO_VERSION "0")
 set(POPPLER_VERSION "${POPPLER_MAJOR_VERSION}.${POPPLER_MINOR_VERSION}.${POPPLER_MICRO_VERSION}")
 
 # command line switches
@@ -28,7 +28,7 @@
 option(ENABLE_UTILS "Compile poppler command line utils." ON)
 option(ENABLE_CPP "Compile poppler cpp wrapper." ON)
 option(ENABLE_LIBOPENJPEG "Use libopenjpeg for JPX streams." ON)
-option(ENABLE_LCMS "Use liblcms for color management." ON)
+set(ENABLE_CMS "auto" CACHE STRING "Use color management system. Possible values: auto, lcms1, lcms2. 'auto' prefers lcms2 over lcms1 if both are available. Unset to disable color management system.")
 option(ENABLE_LIBCURL "Build libcurl based HTTP support." OFF)
 option(ENABLE_ZLIB "Build with zlib (not totally safe)." OFF)
 option(USE_FIXEDPOINT "Use fixed point arithmetic in the Splash backend" OFF)
@@ -136,16 +136,22 @@
   set(ENABLE_LIBOPENJPEG ${LIBOPENJPEG_FOUND})
   set(HAVE_OPENJPEG_H ON)
 endif(ENABLE_LIBOPENJPEG)
-if(ENABLE_LCMS)
+if(ENABLE_CMS STREQUAL "auto")
   find_package(LCMS2)
-  if(LCMS2_FOUND)
-    set(USE_CMS ${LCMS2_FOUND})
-  else(LCMS2_FOUND)
+  set(USE_CMS ${LCMS2_FOUND})
+  if(NOT LCMS2_FOUND)
     find_package(LCMS)
     set(USE_CMS ${LCMS_FOUND})
     set(USE_LCMS1 ${LCMS_FOUND})
-  endif(LCMS2_FOUND)
-endif(ENABLE_LCMS)
+  endif(NOT LCMS2_FOUND)
+elseif(ENABLE_CMS STREQUAL "lcms1")
+  find_package(LCMS)
+  set(USE_CMS ${LCMS_FOUND})
+  set(USE_LCMS1 ${LCMS_FOUND})
+elseif(ENABLE_CMS STREQUAL "lcms2")
+  find_package(LCMS2)
+  set(USE_CMS ${LCMS2_FOUND})
+endif()
 if(ENABLE_LIBCURL)
   find_package(CURL)
   include_directories(${CURL_INCLUDE_DIR})
@@ -237,6 +243,7 @@
   goo/JpegWriter.cc
   goo/ImgWriter.cc
   goo/gstrtod.cc
+  goo/grandom.cc
   fofi/FoFiBase.cc
   fofi/FoFiEncodings.cc
   fofi/FoFiTrueType.cc
@@ -290,6 +297,7 @@
   poppler/strtok_r.cpp
   poppler/UnicodeMap.cc
   poppler/UnicodeTypeTable.cc
+  poppler/UTF.cc
   poppler/XRef.cc
   poppler/PSOutputDev.cc
   poppler/TextOutputDev.cc
@@ -386,7 +394,7 @@
 else(MSVC)
 add_library(poppler SHARED ${poppler_SRCS})
 endif(MSVC)
-set_target_properties(poppler PROPERTIES VERSION 28.0.0 SOVERSION 28)
+set_target_properties(poppler PROPERTIES VERSION 29.0.0 SOVERSION 29)
 target_link_libraries(poppler ${poppler_LIBS})
 target_link_libraries(poppler LINK_INTERFACE_LIBRARIES "")
 install(TARGETS poppler RUNTIME DESTINATION bin LIBRARY DESTINATION lib${LIB_SUFFIX} ARCHIVE DESTINATION lib${LIB_SUFFIX})
@@ -460,6 +468,7 @@
     poppler/SecurityHandler.h
     poppler/StdinCachedFile.h
     poppler/StdinPDFDocBuilder.h
+    poppler/UTF.h
     poppler/UTF8.h
     poppler/XpdfPluginAPI.h
     poppler/Sound.h
@@ -478,6 +487,7 @@
     goo/ImgWriter.h
     goo/GooLikely.h
     goo/gstrtod.h
+    goo/grandom.h
     DESTINATION include/poppler/goo)
   if(PNG_FOUND)
     install(FILES
diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake
index 04de970..6e547ed 100644
--- a/ConfigureChecks.cmake
+++ b/ConfigureChecks.cmake
@@ -33,6 +33,7 @@
 check_function_exists(popen HAVE_POPEN)
 check_function_exists(mkstemp HAVE_MKSTEMP)
 check_function_exists(mkstemps HAVE_MKSTEMPS)
+check_function_exists(rand_r HAVE_RAND_R)
 
 macro(CHECK_FOR_DIR include var)
   check_c_source_compiles(
diff --git a/NEWS b/NEWS
index f29ea93..c0381a5 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,42 @@
+Release 0.21.0
+        core:
+         * Support the modification of files with Encrypt
+         * Annotation improvements
+         * Form improvements
+         * Splash: Implement DeviceN support
+         * Splash: Avoid bogus memory error for tilingPattern
+         * TextOutputDev: Allow multiple fonts in a TextWord
+         * Kill the concept of base dir
+         * PSOutputDev: Always write HiResBoundingBox (Bug #53159)
+         * Convert UTF-16 to UCS-4 when reading toUnicode cmap
+         * GooString formatting: add support for uppercase hexadecimal
+         * Use error() instead of fprintf(stderr, ...) in Annot::layoutText
+         * poppler-config.h: remove WITH_FONTCONFIGURATION_* macros
+
+        glib:
+         * Annotation improvements
+         * Add poppler_page_remove_annot()
+         * Add poppler_document_new_from_stream
+         * Add poppler_document_new_from_gfile
+         * Add poppler_page_find_text_with_options (Bug #2951)
+         * Demo improvements
+         * Port tests and demo to GTK+3
+
+        qt4:
+         * Add accessor methods for movie poster information
+         * Make 'additional actions' available in Annotation API (Bug #53589)
+         * Add whole-page search method to Poppler::Page
+         * Small changes in tests
+
+        utils:
+         * pdftohtml: Make the output more xhtml compliant
+         * pdftohtml: Add -fontfullname. (Bug #49872)
+         * pdftohtml: Do not invoke gs anymore
+
+        build system:
+         * Add the possibility of using lcms1 even if lcms2 is installed
+         * Remove extra fontconfig CFLAGS and LIBS
+
 Release 0.20.5
         core:
          * Fix crashes in malformed documents
diff --git a/cmake/modules/FindGTK.cmake b/cmake/modules/FindGTK.cmake
index 4d66bd3..1a32483 100644
--- a/cmake/modules/FindGTK.cmake
+++ b/cmake/modules/FindGTK.cmake
@@ -2,8 +2,8 @@
 # Once done this will define
 #
 #  GTK_FOUND - system has GTK
-#  GTK2_CFLAGS - the GTK CFlags
-#  GTK2_LIBRARIES - Link these to use GTK
+#  GTK3_CFLAGS - the GTK CFlags
+#  GTK3_LIBRARIES - Link these to use GTK
 #
 # Copyright 2008-2010 Pino Toscano, <pino@kde.org>
 #
@@ -15,8 +15,8 @@
 if (NOT WIN32)
   find_package(PkgConfig REQUIRED)
 
-  pkg_check_modules(GTK2 "gtk+-2.0>=2.14" "gdk-pixbuf-2.0" "gthread-2.0" "gio-2.0")
+  pkg_check_modules(GTK3 "gtk+-3.0>=3.0" "gdk-pixbuf-2.0" "gthread-2.0" "gio-2.0")
 
-  find_package_handle_standard_args(GTK DEFAULT_MSG GTK2_LIBRARIES GTK2_CFLAGS)
+  find_package_handle_standard_args(GTK DEFAULT_MSG GTK3_LIBRARIES GTK3_CFLAGS)
 
 endif(NOT WIN32)
diff --git a/config.h.cmake b/config.h.cmake
index 3c7b968..cde219f 100644
--- a/config.h.cmake
+++ b/config.h.cmake
@@ -76,6 +76,9 @@
 /* Define to 1 if you have the `mkstemps' function. */
 #cmakedefine HAVE_MKSTEMPS 1
 
+/* Define to 1 if you have the `rand_r' function. */
+#cmakedefine HAVE_RAND_R 1
+
 /* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
 #cmakedefine HAVE_NDIR_H 1
 
diff --git a/configure.ac b/configure.ac
index 7b6fb31..2c44efb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
 m4_define([poppler_version_major],[0])
-m4_define([poppler_version_minor],[20])
-m4_define([poppler_version_micro],[5])
+m4_define([poppler_version_minor],[21])
+m4_define([poppler_version_micro],[0])
 m4_define([poppler_version],[poppler_version_major.poppler_version_minor.poppler_version_micro])
 
 AC_PREREQ(2.59)
@@ -133,6 +133,7 @@
 AC_CHECK_DECL(gettimeofday, [AC_CHECK_FUNC(gettimeofday, AC_DEFINE(HAVE_GETTIMEOFDAY, 1, [Defines if gettimeofday is available on your system]))],[],[#include <sys/time.h>])
 AC_CHECK_FUNC(localtime_r, AC_DEFINE(HAVE_LOCALTIME_R, 1, [Defines if localtime_r is available on your system]))
 AC_CHECK_FUNC(gmtime_r, AC_DEFINE(HAVE_GMTIME_R, 1, [Defines if gmtime_r is available on your system]))
+AC_CHECK_FUNC(rand_r, AC_DEFINE(HAVE_RAND_R, 1, [Defines if rand_r is available on your system]))
 
 dnl ##### Check for extra libraries needed by X.  (LynxOS needs this.)
 AC_CHECK_FUNC(gethostbyname)
@@ -531,12 +532,12 @@
               		       enable_poppler_glib=$enableval,
 			       enable_poppler_glib="try")
   if test x$enable_poppler_glib = xyes; then
-    PKG_CHECK_MODULES(POPPLER_GLIB, glib-2.0 >= $GLIB_REQUIRED gobject-2.0 >= $GLIB_REQUIRED cairo >= $CAIRO_VERSION)
+    PKG_CHECK_MODULES(POPPLER_GLIB, glib-2.0 >= $GLIB_REQUIRED gobject-2.0 >= $GLIB_REQUIRED gio-2.0 >= $GLIB_REQUIRED cairo >= $CAIRO_VERSION)
   elif test x$enable_poppler_glib = xtry; then
-    PKG_CHECK_MODULES(POPPLER_GLIB, glib-2.0 >= $GLIB_REQUIRED gobject-2.0 >= $GLIB_REQUIRED cairo >= $CAIRO_VERSION,
+    PKG_CHECK_MODULES(POPPLER_GLIB, glib-2.0 >= $GLIB_REQUIRED gobject-2.0 >= $GLIB_REQUIRED gio-2.0 >= $GLIB_REQUIRED cairo >= $CAIRO_VERSION,
                       [enable_poppler_glib="yes"],
                       [enable_poppler_glib="no"
-                      use_glib="no (requires glib-2.0 >= $GLIB_REQUIRED gobject-2.0 >= $GLIB_REQUIRED cairo >= $CAIRO_VERSION)"])
+                      use_glib="no (requires glib-2.0 >= $GLIB_REQUIRED gobject-2.0 >= $GLIB_REQUIRED gio-2.0 >= $GLIB_REQUIRED cairo >= $CAIRO_VERSION)"])
   fi
   if test x$enable_poppler_glib = xyes; then
     # Check for introspection
@@ -644,9 +645,9 @@
               enable_gtk_test=$enableval,
               enable_gtk_test="try")
 if test x$enable_gtk_test = xyes; then
-  PKG_CHECK_MODULES(GTK_TEST, gtk+-2.0 >= 2.14 gdk-pixbuf-2.0 gthread-2.0 gio-2.0)
+  PKG_CHECK_MODULES(GTK_TEST, gtk+-3.0 >= 3.0 gdk-pixbuf-2.0)
 elif test x$enable_gtk_test = xtry; then
-  PKG_CHECK_MODULES(GTK_TEST, gtk+-2.0 >= 2.14 gdk-pixbuf-2.0 gthread-2.0 gio-2.0,
+  PKG_CHECK_MODULES(GTK_TEST, gtk+-3.0 >= 3.0 gdk-pixbuf-2.0,
                     [enable_gtk_test="yes"],
                     [enable_gtk_test="no"])
 fi
@@ -660,7 +661,7 @@
 AM_CONDITIONAL(BUILD_UTILS, test x$enable_utils = xyes)
 
 AC_ARG_ENABLE(compile-warnings,
-              AC_HELP_STRING([--enable-compile-warnings=@<:@no/yes/kde@:>@]
+              AC_HELP_STRING([--enable-compile-warnings=@<:@no/yes/kde@:>@],
                              [Turn on compiler warnings.]),,
               [enable_compile_warnings="yes"])
 
@@ -669,31 +670,30 @@
 dnl
 
 AC_ARG_ENABLE(cms,
-	      AC_HELP_STRING([--disable-cms],
-	                     [Don't use color management system.]),
-              enable_cms=$enableval,
-              enable_cms="try")
-if test x$enable_cms = xyes; then
+              AC_HELP_STRING([--enable-cms=@<:@auto/lcms1/lcms2/none@:>@],
+                             [Use color management system. 'auto' prefers lcms2 over lcms1 if both are available [[default=auto]]]),
+              [enable_cms=$enableval],
+              [enable_cms="auto"])
+if test x$enable_cms = xauto; then
   PKG_CHECK_MODULES(LCMS, lcms2, [lcms2=yes], [lcms2=no])
   if test x$lcms2 = xno; then
-      PKG_CHECK_MODULES(LCMS, lcms)
+      PKG_CHECK_MODULES(LCMS, lcms, [lcms1=yes], [lcms1=no])
   fi
-elif test x$enable_cms = xtry; then
-  PKG_CHECK_MODULES(LCMS, lcms2,[lcms2=yes],[lcms2=no])
-  if test x$lcms2 = xyes; then
-    enable_cms=yes
-  else
-      PKG_CHECK_MODULES(LCMS, lcms,[enable_cms=yes],[enable_cms=no])
-  fi
+elif test x$enable_cms = xlcms1; then
+  PKG_CHECK_MODULES(LCMS, lcms, [lcms1=yes], [lcms1=no])
+elif test x$enable_cms = xlcms2; then
+  PKG_CHECK_MODULES(LCMS, lcms2, [lcms2=yes], [lcms2=no])
 fi
 
-if test "x$enable_cms" = "xyes"; then
+if test x$lcms1 = xyes || test x$lcms2 = xyes; then
+  enable_cms=yes
   AC_DEFINE(USE_CMS, 1, [Defines if use cms])
-  if test "x$lcms2" != "xyes"; then
+  if test x$lcms1 = xyes; then
     lcms1=yes;
     AC_DEFINE(USE_LCMS1, 1, [Defines if use lcms1])
   fi
 fi
+
 AM_CONDITIONAL(USE_CMS, test x$enable_cms = xyes)
 AM_CONDITIONAL(USE_LCMS1, test x$lcms1 = xyes)
 
diff --git a/cpp/Doxyfile b/cpp/Doxyfile
index c63ae5a..8a2aa70 100644
--- a/cpp/Doxyfile
+++ b/cpp/Doxyfile
@@ -31,7 +31,7 @@
 # This could be handy for archiving the generated documentation or
 # if some version control system is used.
 
-PROJECT_NUMBER         = 0.20.5
+PROJECT_NUMBER         = 0.21.0
 
 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
 # base path where the generated documentation will be put.
diff --git a/glib/CMakeLists.txt b/glib/CMakeLists.txt
index 92e23bd..a6f1e89 100644
--- a/glib/CMakeLists.txt
+++ b/glib/CMakeLists.txt
@@ -69,6 +69,8 @@
   poppler-movie.cc
   poppler-media.cc
   poppler.cc
+  poppler-cached-file-loader.cc
+  poppler-input-stream.cc
 )
 set(poppler_glib_generated_SRCS
   ${CMAKE_CURRENT_BINARY_DIR}/poppler-enums.c
@@ -77,7 +79,7 @@
   ${CMAKE_SOURCE_DIR}/poppler/CairoRescaleBox.cc
 )
 add_library(poppler-glib SHARED ${poppler_glib_SRCS} ${poppler_glib_generated_SRCS})
-set_target_properties(poppler-glib PROPERTIES VERSION 8.4.0 SOVERSION 8)
+set_target_properties(poppler-glib PROPERTIES VERSION 8.5.0 SOVERSION 8)
 target_link_libraries(poppler-glib poppler ${GLIB2_LIBRARIES} ${CAIRO_LIBRARIES} ${FREETYPE_LIBRARIES})
 install(TARGETS poppler-glib RUNTIME DESTINATION bin LIBRARY DESTINATION lib${LIB_SUFFIX} ARCHIVE DESTINATION lib${LIB_SUFFIX})
 
diff --git a/glib/Makefile.am b/glib/Makefile.am
index 83c724f..f1a8b7a 100644
--- a/glib/Makefile.am
+++ b/glib/Makefile.am
@@ -7,7 +7,6 @@
 	-I$(top_srcdir)/poppler			\
 	$(POPPLER_GLIB_CFLAGS)			\
 	$(FREETYPE_CFLAGS)			\
-	$(FONTCONFIG_CFLAGS)                    \
 	$(POPPLER_GLIB_DISABLE_DEPRECATED)	\
 	$(POPPLER_GLIB_DISABLE_SINGLE_INCLUDES)
 
@@ -64,6 +63,10 @@
 	poppler-layer.cc			\
 	poppler-media.cc			\
 	poppler-movie.cc			\
+	poppler-cached-file-loader.cc		\
+	poppler-cached-file-loader.h		\
+	poppler-input-stream.cc			\
+	poppler-input-stream.h			\
 	poppler.cc				\
 	poppler-private.h
 
@@ -71,10 +74,9 @@
 	$(top_builddir)/poppler/libpoppler.la		\
 	$(top_builddir)/poppler/libpoppler-cairo.la	\
 	$(POPPLER_GLIB_LIBS)				\
-	$(FREETYPE_LIBS)				\
-	$(FONTCONFIG_LIBS)
+	$(FREETYPE_LIBS)
 
-libpoppler_glib_la_LDFLAGS = -version-info 12:0:4 @create_shared_lib@ @auto_import_flags@
+libpoppler_glib_la_LDFLAGS = -version-info 13:0:5 @create_shared_lib@ @auto_import_flags@
 
 BUILT_SOURCES =					\
 	poppler-enums.c				\
diff --git a/glib/demo/CMakeLists.txt b/glib/demo/CMakeLists.txt
index 286fbd7..316371e 100644
--- a/glib/demo/CMakeLists.txt
+++ b/glib/demo/CMakeLists.txt
@@ -1,8 +1,8 @@
 include_directories(
-  ${GTK2_INCLUDE_DIRS}
+  ${GTK3_INCLUDE_DIRS}
 )
 
-add_definitions(${GTK2_CFLAGS_OTHER})
+add_definitions(${GTK3_CFLAGS_OTHER})
 
 set(poppler_glib_demo_SRCS
   main.c
@@ -25,4 +25,4 @@
   selections.c
 )
 poppler_add_test(poppler-glib-demo BUILD_GTK_TESTS ${poppler_glib_demo_SRCS})
-target_link_libraries(poppler-glib-demo poppler-glib ${GTK2_LIBRARIES})
+target_link_libraries(poppler-glib-demo poppler-glib ${GTK3_LIBRARIES})
diff --git a/glib/demo/annots.c b/glib/demo/annots.c
index 7075028..e0b5d91 100644
--- a/glib/demo/annots.c
+++ b/glib/demo/annots.c
@@ -40,6 +40,7 @@
     PopplerDocument *doc;
     PopplerPage     *page;
 
+    GtkWidget       *tree_view;
     GtkListStore    *model;
     GtkWidget       *annot_view;
     GtkWidget       *timer_label;
@@ -281,6 +282,28 @@
 }
 
 static void
+pgd_annots_remove_annot (GtkWidget     *button,
+                         PgdAnnotsDemo *demo)
+{
+    GtkTreeSelection *selection;
+    GtkTreeModel *model;
+    GtkTreeIter   iter;
+
+    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (demo->tree_view));
+
+    if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+        PopplerAnnot *annot;
+
+        gtk_tree_model_get (model, &iter,
+                            ANNOTS_COLUMN, &annot,
+                           -1);
+        poppler_page_remove_annot (demo->page, annot);
+        g_object_unref (annot);
+        gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
+    }
+}
+
+static void
 pgd_annot_view_set_annot_markup (GtkWidget          *table,
                                  PopplerAnnotMarkup *markup,
                                  gint               *row)
@@ -289,35 +312,35 @@
     PopplerRectangle rect;
 
     text = poppler_annot_markup_get_label (markup);
-    pgd_table_add_property (GTK_TABLE (table), "<b>Label:</b>", text, row);
+    pgd_table_add_property (GTK_GRID (table), "<b>Label:</b>", text, row);
     g_free (text);
 
     if (poppler_annot_markup_has_popup (markup)) {
-	    pgd_table_add_property (GTK_TABLE (table), "<b>Popup is open:</b>",
+	    pgd_table_add_property (GTK_GRID (table), "<b>Popup is open:</b>",
 				    poppler_annot_markup_get_popup_is_open (markup) ? "Yes" : "No", row);
 
 	    poppler_annot_markup_get_popup_rectangle (markup, &rect);
 	    text = g_strdup_printf ("X1: %.2f, Y1: %.2f, X2: %.2f, Y2: %.2f",
 				    rect.x1, rect.y1, rect.x2, rect.y2);
-	    pgd_table_add_property (GTK_TABLE (table), "<b>Popup Rectangle:</b>", text, row);
+	    pgd_table_add_property (GTK_GRID (table), "<b>Popup Rectangle:</b>", text, row);
 	    g_free (text);
     }
 
     text = g_strdup_printf ("%f", poppler_annot_markup_get_opacity (markup));
-    pgd_table_add_property (GTK_TABLE (table), "<b>Opacity:</b>", text, row);
+    pgd_table_add_property (GTK_GRID (table), "<b>Opacity:</b>", text, row);
     g_free (text);
 
     text = get_markup_date (markup);
-    pgd_table_add_property (GTK_TABLE (table), "<b>Date:</b>", text, row);
+    pgd_table_add_property (GTK_GRID (table), "<b>Date:</b>", text, row);
     g_free (text);
 
     text = poppler_annot_markup_get_subject (markup);
-    pgd_table_add_property (GTK_TABLE (table), "<b>Subject:</b>", text, row);
+    pgd_table_add_property (GTK_GRID (table), "<b>Subject:</b>", text, row);
     g_free (text);
 
-    pgd_table_add_property (GTK_TABLE (table), "<b>Reply To:</b>", get_markup_reply_to (markup), row);
+    pgd_table_add_property (GTK_GRID (table), "<b>Reply To:</b>", get_markup_reply_to (markup), row);
 
-    pgd_table_add_property (GTK_TABLE (table), "<b>External Data:</b>", get_markup_external_data (markup), row);
+    pgd_table_add_property (GTK_GRID (table), "<b>External Data:</b>", get_markup_external_data (markup), row);
 }
 
 static void
@@ -327,14 +350,14 @@
 {
     gchar *text;
 
-    pgd_table_add_property (GTK_TABLE (table), "<b>Is open:</b>",
+    pgd_table_add_property (GTK_GRID (table), "<b>Is open:</b>",
                             poppler_annot_text_get_is_open (annot) ? "Yes" : "No", row);
 
     text = poppler_annot_text_get_icon (annot);
-    pgd_table_add_property (GTK_TABLE (table), "<b>Icon:</b>", text, row);
+    pgd_table_add_property (GTK_GRID (table), "<b>Icon:</b>", text, row);
     g_free (text);
 
-    pgd_table_add_property (GTK_TABLE (table), "<b>State:</b>", get_text_state (annot), row);
+    pgd_table_add_property (GTK_GRID (table), "<b>State:</b>", get_text_state (annot), row);
 }
 
 static void
@@ -344,10 +367,10 @@
 {
     gchar *text;
 
-    pgd_table_add_property (GTK_TABLE (table), "<b>Quadding:</b>", get_free_text_quadding (annot), row);
+    pgd_table_add_property (GTK_GRID (table), "<b>Quadding:</b>", get_free_text_quadding (annot), row);
 
     text = get_free_text_callout_line (annot);
-    pgd_table_add_property (GTK_TABLE (table), "<b>Callout:</b>", text, row);
+    pgd_table_add_property (GTK_GRID (table), "<b>Callout:</b>", text, row);
     g_free (text);
 }
 
@@ -408,14 +431,14 @@
     gchar *text;
 
     text = poppler_annot_file_attachment_get_name (annot);
-    pgd_table_add_property (GTK_TABLE (table), "<b>Attachment Name:</b>", text, row);
+    pgd_table_add_property (GTK_GRID (table), "<b>Attachment Name:</b>", text, row);
     g_free (text);
 
     button = gtk_button_new_with_label ("Save Attachment");
     g_signal_connect (G_OBJECT (button), "clicked",
 		      G_CALLBACK (pgd_annot_save_file_attachment_button_clicked),
 		      (gpointer)annot);
-    pgd_table_add_property_with_custom_widget (GTK_TABLE (table), "<b>File Attachment:</b>", button, row);
+    pgd_table_add_property_with_custom_widget (GTK_GRID (table), "<b>File Attachment:</b>", button, row);
     gtk_widget_show (button);
 
 }
@@ -429,12 +452,12 @@
     gchar *text;
 
     text = poppler_annot_movie_get_title (annot);
-    pgd_table_add_property (GTK_TABLE (table), "<b>Movie Title:</b>", text, row);
+    pgd_table_add_property (GTK_GRID (table), "<b>Movie Title:</b>", text, row);
     g_free (text);
 
     movie_view = pgd_movie_view_new ();
     pgd_movie_view_set_movie (movie_view, poppler_annot_movie_get_movie (annot));
-    pgd_table_add_property_with_custom_widget (GTK_TABLE (table), "<b>Movie:</b>", movie_view, row);
+    pgd_table_add_property_with_custom_widget (GTK_GRID (table), "<b>Movie:</b>", movie_view, row);
     gtk_widget_show (movie_view);
 }
 
@@ -447,44 +470,45 @@
 
     action_view = pgd_action_view_new (NULL);
     pgd_action_view_set_action (action_view, poppler_annot_screen_get_action (annot));
-    pgd_table_add_property_with_custom_widget (GTK_TABLE (table), "<b>Action:</b>", action_view, row);
+    pgd_table_add_property_with_custom_widget (GTK_GRID (table), "<b>Action:</b>", action_view, row);
     gtk_widget_show (action_view);
 }
 
 static void
-pgd_annot_view_set_annot (GtkWidget    *annot_view,
-                          PopplerAnnot *annot)
+pgd_annot_view_set_annot (PgdAnnotsDemo *demo,
+                          PopplerAnnot  *annot)
 {
     GtkWidget  *alignment;
     GtkWidget  *table;
+    GtkWidget  *button;
     GEnumValue *enum_value;
     gint        row = 0;
     gchar      *text, *warning;
     time_t      timet;
 
-    alignment = gtk_bin_get_child (GTK_BIN (annot_view));
+    alignment = gtk_bin_get_child (GTK_BIN (demo->annot_view));
     if (alignment) {
-        gtk_container_remove (GTK_CONTAINER (annot_view), alignment);
+        gtk_container_remove (GTK_CONTAINER (demo->annot_view), alignment);
     }
 
     alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
     gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 5, 5, 12, 5);
-    gtk_container_add (GTK_CONTAINER (annot_view), alignment);
+    gtk_container_add (GTK_CONTAINER (demo->annot_view), alignment);
     gtk_widget_show (alignment);
 
     if (!annot)
         return;
 
-    table = gtk_table_new (10, 2, FALSE);
-    gtk_table_set_col_spacings (GTK_TABLE (table), 6);
-    gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+    table = gtk_grid_new ();
+    gtk_grid_set_column_spacing (GTK_GRID (table), 6);
+    gtk_grid_set_row_spacing (GTK_GRID (table), 6);
 
     text = poppler_annot_get_contents (annot);
-    pgd_table_add_property (GTK_TABLE (table), "<b>Contents:</b>", text, &row);
+    pgd_table_add_property (GTK_GRID (table), "<b>Contents:</b>", text, &row);
     g_free (text);
 
     text = poppler_annot_get_name (annot);
-    pgd_table_add_property (GTK_TABLE (table), "<b>Name:</b>", text, &row);
+    pgd_table_add_property (GTK_GRID (table), "<b>Name:</b>", text, &row);
     g_free (text);
 
     text = poppler_annot_get_modified (annot);
@@ -492,15 +516,15 @@
 	    g_free (text);
 	    text = pgd_format_date (timet);
     }
-    pgd_table_add_property (GTK_TABLE (table), "<b>Modified:</b>", text, &row);
+    pgd_table_add_property (GTK_GRID (table), "<b>Modified:</b>", text, &row);
     g_free (text);
 
     text = g_strdup_printf ("%d", poppler_annot_get_flags (annot));
-    pgd_table_add_property (GTK_TABLE (table), "<b>Flags:</b>", text, &row);
+    pgd_table_add_property (GTK_GRID (table), "<b>Flags:</b>", text, &row);
     g_free (text);
 
     text = g_strdup_printf ("%d", poppler_annot_get_page_index (annot));
-    pgd_table_add_property (GTK_TABLE (table), "<b>Page:</b>", text, &row);
+    pgd_table_add_property (GTK_GRID (table), "<b>Page:</b>", text, &row);
     g_free (text);
 
     if (POPPLER_IS_ANNOT_MARKUP (annot))
@@ -527,6 +551,13 @@
           break;
     }
 
+    button = gtk_button_new_from_stock (GTK_STOCK_REMOVE);
+    g_signal_connect (G_OBJECT (button), "clicked",
+                      G_CALLBACK (pgd_annots_remove_annot),
+                      (gpointer) demo);
+    gtk_grid_attach (GTK_GRID (table), button, 0, row, 2, 1);
+    gtk_widget_show (button);
+
     gtk_container_add (GTK_CONTAINER (alignment), table);
     gtk_widget_show (table);
 }
@@ -540,7 +571,7 @@
     GTimer      *timer;
 
     gtk_list_store_clear (demo->model);
-    pgd_annot_view_set_annot (demo->annot_view, NULL);
+    pgd_annot_view_set_annot (demo, NULL);
 
     if (demo->page) {
         g_object_unref (demo->page);
@@ -632,8 +663,10 @@
         gtk_tree_model_get (model, &iter,
                             ANNOTS_COLUMN, &annot,
                            -1);
-        pgd_annot_view_set_annot (demo->annot_view, annot);
+        pgd_annot_view_set_annot (demo, annot);
         g_object_unref (annot);
+    } else {
+        pgd_annot_view_set_annot (demo, NULL);
     }
 }
 
@@ -666,22 +699,16 @@
 
     vbox = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
 
-#if GTK_CHECK_VERSION (2, 24, 0)
     type_selector = gtk_combo_box_text_new ();
     gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (type_selector), "POPPLER_ANNOT_UNKNOWN");
     gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (type_selector), "POPPLER_ANNOT_TEXT");
-#else
-    type_selector = gtk_combo_box_new_text ();
-    gtk_combo_box_append_text (GTK_COMBO_BOX (type_selector), "POPPLER_ANNOT_UNKNOWN");
-    gtk_combo_box_append_text (GTK_COMBO_BOX (type_selector), "POPPLER_ANNOT_TEXT");
-#endif
     gtk_combo_box_set_active (GTK_COMBO_BOX (type_selector), 1);
     gtk_box_pack_start (GTK_BOX (vbox), type_selector, TRUE, TRUE, 0);
     gtk_widget_show (type_selector);
 
-    hbox = gtk_hbox_new (FALSE, 6);
+    hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 
-    rect_hbox = gtk_hbox_new (FALSE, 6);
+    rect_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 
     label = gtk_label_new ("x1:");
     gtk_box_pack_start (GTK_BOX (rect_hbox), label, TRUE, TRUE, 0);
@@ -694,7 +721,7 @@
     gtk_box_pack_start (GTK_BOX (hbox), rect_hbox, FALSE, TRUE, 0);
     gtk_widget_show (rect_hbox);
 
-    rect_hbox = gtk_hbox_new (FALSE, 6);
+    rect_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 
     label = gtk_label_new ("x2:");
     gtk_box_pack_start (GTK_BOX (rect_hbox), label, TRUE, TRUE, 0);
@@ -707,7 +734,7 @@
     gtk_box_pack_start (GTK_BOX (hbox), rect_hbox, FALSE, TRUE, 0);
     gtk_widget_show (rect_hbox);
 
-    rect_hbox = gtk_hbox_new (FALSE, 6);
+    rect_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 
     label = gtk_label_new ("y1:");
     gtk_box_pack_start (GTK_BOX (rect_hbox), label, TRUE, TRUE, 0);
@@ -720,7 +747,7 @@
     gtk_box_pack_start (GTK_BOX (hbox), rect_hbox, FALSE, TRUE, 0);
     gtk_widget_show (rect_hbox);
 
-    rect_hbox = gtk_hbox_new (FALSE, 6);
+    rect_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 
     label = gtk_label_new ("y2:");
     gtk_box_pack_start (GTK_BOX (rect_hbox), label, TRUE, TRUE, 0);
@@ -771,9 +798,9 @@
 
     n_pages = poppler_document_get_n_pages (document);
 
-    vbox = gtk_vbox_new (FALSE, 12);
+    vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
 
-    hbox = gtk_hbox_new (FALSE, 6);
+    hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 
     label = gtk_label_new ("Page:");
     gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
@@ -815,7 +842,7 @@
     gtk_box_pack_start (GTK_BOX (vbox), demo->timer_label, FALSE, TRUE, 0);
     gtk_widget_show (demo->timer_label);
 
-    hpaned = gtk_hpaned_new ();
+    hpaned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
 
     demo->annot_view = pgd_annot_view_new ();
 
@@ -831,6 +858,7 @@
 				      G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
 				      G_TYPE_OBJECT);
     treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL (demo->model));
+    demo->tree_view = treeview;
 
     renderer = gtk_cell_renderer_text_new ();
     gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
diff --git a/glib/demo/attachments.c b/glib/demo/attachments.c
index 63565dd..1b74274 100644
--- a/glib/demo/attachments.c
+++ b/glib/demo/attachments.c
@@ -280,7 +280,7 @@
 	GtkWidget    *hbox, *button;
 	gboolean      has_attachments;
 
-	vbox = gtk_vbox_new (FALSE, 12);
+	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
 
 	swindow = gtk_scrolled_window_new (NULL, NULL);
 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
@@ -327,7 +327,7 @@
 	if (!has_attachments)
 		return vbox;
 	
-	hbox = gtk_hbutton_box_new ();
+	hbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
 	gtk_button_box_set_layout (GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_SPREAD);
 
 	button = gtk_button_new_with_label ("Save");
diff --git a/glib/demo/find.c b/glib/demo/find.c
index 632e610..664c597 100644
--- a/glib/demo/find.c
+++ b/glib/demo/find.c
@@ -26,20 +26,28 @@
 	Y2_COLUMN,
 
 	VISIBLE_COLUMN,
+        PAGE_COLUMN,
+        PAGE_RECT,
 	N_COLUMNS
 };
 
 typedef struct {
 	PopplerDocument *doc;
 
-	GtkTreeModel    *model;
+	GtkWidget       *treeview;
+        GtkWidget       *darea;
 	GtkWidget       *entry;
 	GtkWidget       *progress;
 
+        PopplerFindFlags options;
 	gint             n_pages;
 	gint             page_index;
-	
+
 	guint            idle_id;
+
+        cairo_surface_t *surface;
+        gint             selected_page;
+        GdkRectangle     selected_match;
 } PgdFindDemo;
 
 static void
@@ -58,10 +66,10 @@
 		demo->doc = NULL;
 	}
 
-	if (demo->model) {
-		g_object_unref (demo->model);
-		demo->model = NULL;
-	}
+        if (demo->surface) {
+                cairo_surface_destroy (demo->surface);
+                demo->surface = NULL;
+        }
 
 	g_free (demo);
 }
@@ -83,9 +91,10 @@
 static gboolean
 pgd_find_find_text (PgdFindDemo *demo)
 {
-	PopplerPage *page;
-	GList       *matches;
-	GTimer      *timer;
+	PopplerPage  *page;
+	GList        *matches;
+	GTimer       *timer;
+        GtkTreeModel *model;
 
 	page = poppler_document_get_page (demo->doc, demo->page_index);
 	if (!page) {
@@ -93,52 +102,66 @@
 		return demo->page_index < demo->n_pages;
 	}
 
+        model = gtk_tree_view_get_model (GTK_TREE_VIEW (demo->treeview));
 	timer = g_timer_new ();
-	matches = poppler_page_find_text (page, gtk_entry_get_text (GTK_ENTRY (demo->entry)));
+	matches = poppler_page_find_text_with_options (page, gtk_entry_get_text (GTK_ENTRY (demo->entry)), demo->options);
 	g_timer_stop (timer);
 	if (matches) {
 		GtkTreeIter iter;
 		gchar      *str;
 		GList      *l;
+                gdouble     height;
 		gint        n_match = 0;
 
 		str = g_strdup_printf ("%d matches found on page %d in %.4f seconds",
 				       g_list_length (matches), demo->page_index + 1,
 				       g_timer_elapsed (timer, NULL));
 		
-		gtk_tree_store_append (GTK_TREE_STORE (demo->model), &iter, NULL);
-		gtk_tree_store_set (GTK_TREE_STORE (demo->model), &iter,
+		gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
+		gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
 				    TITLE_COLUMN, str,
 				    VISIBLE_COLUMN, FALSE,
+                                    PAGE_COLUMN, demo->page_index,
 				    -1);
 		g_free (str);
-		
+
+                poppler_page_get_size (page, NULL, &height);
+
 		for (l = matches; l && l->data; l = g_list_next (l)) {
 			PopplerRectangle *rect = (PopplerRectangle *)l->data;
 			GtkTreeIter       iter_child;
 			gchar            *x1, *y1, *x2, *y2;
+                        gdouble           tmp;
 
 			str = g_strdup_printf ("Match %d", ++n_match);
 			x1 = g_strdup_printf ("%.2f", rect->x1);
 			y1 = g_strdup_printf ("%.2f", rect->y1);
 			x2 = g_strdup_printf ("%.2f", rect->x2);
 			y2 = g_strdup_printf ("%.2f", rect->y2);
-			
-			gtk_tree_store_append (GTK_TREE_STORE (demo->model), &iter_child, &iter);
-			gtk_tree_store_set (GTK_TREE_STORE (demo->model), &iter_child,
+
+                        tmp = rect->y1;
+                        rect->y1 = height - rect->y2;
+                        rect->y2 = height - tmp;
+
+			gtk_tree_store_append (GTK_TREE_STORE (model), &iter_child, &iter);
+			gtk_tree_store_set (GTK_TREE_STORE (model), &iter_child,
 					    TITLE_COLUMN, str,
 					    X1_COLUMN, x1,
 					    Y1_COLUMN, y1,
 					    X2_COLUMN, x2,
 					    Y2_COLUMN, y2,
 					    VISIBLE_COLUMN, TRUE,
+                                            PAGE_COLUMN, demo->page_index,
+                                            PAGE_RECT, rect,
 					    -1);
 			g_free (str);
 			g_free (x1);
 			g_free (y1);
 			g_free (x2);
 			g_free (y2);
-			poppler_rectangle_free (rect);
+                        g_object_weak_ref (G_OBJECT (model),
+                                           (GWeakNotify)poppler_rectangle_free,
+                                           rect);
 		}
 		g_list_free (matches);
 	}
@@ -152,11 +175,115 @@
 	return demo->page_index < demo->n_pages;
 }
 
+static cairo_surface_t *
+pgd_find_render_page (PgdFindDemo *demo)
+{
+        cairo_t *cr;
+        PopplerPage *page;
+        gdouble width, height;
+        cairo_surface_t *surface = NULL;
+
+        page = poppler_document_get_page (demo->doc, demo->selected_page);
+        if (!page)
+                return NULL;
+
+        poppler_page_get_size (page, &width, &height);
+        gtk_widget_set_size_request (demo->darea, width, height);
+
+        surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+                                              width, height);
+        cr = cairo_create (surface);
+
+        cairo_save (cr);
+        cairo_set_source_rgb (cr, 1, 1, 1);
+        cairo_rectangle (cr, 0, 0, width, height);
+        cairo_fill (cr);
+        cairo_restore (cr);
+
+        cairo_save (cr);
+        poppler_page_render (page, cr);
+        cairo_restore (cr);
+
+        cairo_destroy (cr);
+        g_object_unref (page);
+
+        return surface;
+}
+
+static gboolean
+pgd_find_viewer_drawing_area_draw (GtkWidget   *area,
+                                   cairo_t     *cr,
+                                   PgdFindDemo *demo)
+{
+        if (demo->selected_page == -1)
+                return FALSE;
+
+        if (!demo->surface) {
+                demo->surface = pgd_find_render_page (demo);
+                if (!demo->surface)
+                        return FALSE;
+        }
+
+        cairo_set_source_surface (cr, demo->surface, 0, 0);
+        cairo_paint (cr);
+
+        if (demo->selected_match.width > 0 && demo->selected_match.height > 0) {
+                cairo_set_source_rgb (cr, 1., 1., 0.);
+                cairo_set_operator (cr, CAIRO_OPERATOR_MULTIPLY);
+                gdk_cairo_rectangle (cr, &demo->selected_match);
+                cairo_fill (cr);
+        }
+
+        return TRUE;
+}
+
+static gboolean
+pgd_find_viewer_redraw (PgdFindDemo *demo)
+{
+        cairo_surface_destroy (demo->surface);
+        demo->surface = NULL;
+
+        gtk_widget_queue_draw (demo->darea);
+
+        return FALSE;
+}
+
+static void
+pgd_find_viewer_queue_redraw (PgdFindDemo *demo)
+{
+        g_idle_add ((GSourceFunc)pgd_find_viewer_redraw, demo);
+}
+
+static GtkTreeModel *
+pgd_find_create_model ()
+{
+        return GTK_TREE_MODEL (gtk_tree_store_new (N_COLUMNS,
+                                                   G_TYPE_STRING,
+                                                   G_TYPE_STRING, G_TYPE_STRING,
+                                                   G_TYPE_STRING, G_TYPE_STRING,
+                                                   G_TYPE_BOOLEAN, G_TYPE_UINT,
+                                                   G_TYPE_POINTER));
+}
+
 static void
 pgd_find_button_clicked (GtkButton   *button,
 			 PgdFindDemo *demo)
 {
-	gtk_tree_store_clear (GTK_TREE_STORE (demo->model));
+        GtkTreeModel *model;
+
+        /* Delete the model and create a new one instead of
+         * just clearing it to make sure rectangle are free.
+         * This is a workaround because GtkTreeModel doesn't
+         * support boxed types and we have to store rectangles
+         * as pointers that are freed when the model is deleted.
+         */
+        model = pgd_find_create_model ();
+        gtk_tree_view_set_model (GTK_TREE_VIEW (demo->treeview), model);
+        g_object_unref (model);
+
+        demo->selected_page = -1;
+        pgd_find_viewer_queue_redraw (demo);
+
 	demo->page_index = 0;
 	pgd_find_update_progress (demo, demo->page_index);
 	if (demo->idle_id > 0)
@@ -174,25 +301,99 @@
 	gtk_widget_set_sensitive (button, text != NULL && text[0] != '\0');
 }
 
+static void
+pgd_find_selection_changed (GtkTreeSelection *treeselection,
+                            PgdFindDemo      *demo)
+{
+        GtkTreeModel *model;
+        GtkTreeIter   iter;
+
+        if (gtk_tree_selection_get_selected (treeselection, &model, &iter)) {
+                guint page_index;
+                PopplerRectangle *rect;
+
+                gtk_tree_model_get (model, &iter,
+                                    PAGE_COLUMN, &page_index,
+                                    PAGE_RECT, &rect,
+                                    -1);
+
+                if (rect) {
+                        demo->selected_match.x = rect->x1;
+                        demo->selected_match.y = rect->y1;
+                        demo->selected_match.width = rect->x2 - rect->x1;
+                        demo->selected_match.height = rect->y2 - rect->y1;
+                } else {
+                        demo->selected_match.width = 0;
+                        demo->selected_match.height = 0;
+                }
+
+                if (page_index != demo->selected_page) {
+                        demo->selected_page = page_index;
+                        pgd_find_viewer_queue_redraw (demo);
+                } else {
+                        gtk_widget_queue_draw (demo->darea);
+                }
+        }
+}
+
+static void
+pgd_find_case_sensitive_toggled (GtkToggleButton *togglebutton,
+                                 PgdFindDemo     *demo)
+{
+        if (gtk_toggle_button_get_active (togglebutton))
+                demo->options |= POPPLER_FIND_CASE_SENSITIVE;
+        else
+                demo->options &= ~POPPLER_FIND_CASE_SENSITIVE;
+}
+
+static void
+pgd_find_backwards_toggled (GtkToggleButton *togglebutton,
+                            PgdFindDemo     *demo)
+{
+        if (gtk_toggle_button_get_active (togglebutton))
+                demo->options |= POPPLER_FIND_BACKWARDS;
+        else
+                demo->options &= ~POPPLER_FIND_BACKWARDS;
+}
+
+static void
+pgd_find_whole_words_toggled (GtkToggleButton *togglebutton,
+                              PgdFindDemo     *demo)
+{
+        if (gtk_toggle_button_get_active (togglebutton))
+                demo->options |= POPPLER_FIND_WHOLE_WORDS_ONLY;
+        else
+                demo->options &= ~POPPLER_FIND_WHOLE_WORDS_ONLY;
+}
+
 GtkWidget *
 pgd_find_create_widget (PopplerDocument *document)
 {
-	PgdFindDemo     *demo;
-	GtkWidget       *vbox, *hbox;
-	GtkWidget       *button;
-	GtkWidget       *swindow;
-	GtkWidget       *treeview;
-	GtkCellRenderer *renderer;
+	PgdFindDemo      *demo;
+	GtkWidget        *vbox, *hbox;
+	GtkWidget        *button;
+	GtkWidget        *swindow;
+        GtkWidget        *checkbutton;
+        GtkTreeModel     *model;
+	GtkWidget        *treeview;
+	GtkCellRenderer  *renderer;
+        GtkWidget        *hpaned;
+        GtkTreeSelection *selection;
 
 	demo = g_new0 (PgdFindDemo, 1);
 
 	demo->doc = g_object_ref (document);
 
 	demo->n_pages = poppler_document_get_n_pages (document);
+        demo->selected_page = -1;
+        demo->options = POPPLER_FIND_DEFAULT;
 
-	vbox = gtk_vbox_new (FALSE, 12);
+        hpaned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
+        gtk_paned_set_position (GTK_PANED (hpaned), 300);
 
-	hbox = gtk_hbox_new (FALSE, 6);
+	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+
+	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 
 	demo->entry = gtk_entry_new ();
 	gtk_box_pack_start (GTK_BOX (hbox), demo->entry, FALSE, TRUE, 0);
@@ -218,21 +419,47 @@
 	gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 6);
 	gtk_widget_show (hbox);
 
+        hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+
+        checkbutton = gtk_check_button_new_with_label ("Case sensitive");
+        g_signal_connect (checkbutton, "toggled",
+                          G_CALLBACK (pgd_find_case_sensitive_toggled),
+                          demo);
+        gtk_box_pack_start (GTK_BOX (hbox), checkbutton, FALSE, FALSE, 0);
+        gtk_widget_show (checkbutton);
+
+        checkbutton = gtk_check_button_new_with_label ("Backwards");
+        g_signal_connect (checkbutton, "toggled",
+                          G_CALLBACK (pgd_find_backwards_toggled),
+                          demo);
+        gtk_box_pack_start (GTK_BOX (hbox), checkbutton, FALSE, FALSE, 0);
+        gtk_widget_show (checkbutton);
+
+        checkbutton = gtk_check_button_new_with_label ("Whole words only");
+        g_signal_connect (checkbutton, "toggled",
+                          G_CALLBACK (pgd_find_whole_words_toggled),
+                          demo);
+        gtk_box_pack_start (GTK_BOX (hbox), checkbutton, FALSE, FALSE, 0);
+        gtk_widget_show (checkbutton);
+
+        gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+        gtk_widget_show (hbox);
+
 	swindow = gtk_scrolled_window_new (NULL, NULL);
 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
 					GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
 
-	demo->model = GTK_TREE_MODEL (
-		gtk_tree_store_new (N_COLUMNS,
-				    G_TYPE_STRING,
-				    G_TYPE_STRING, G_TYPE_STRING,
-				    G_TYPE_STRING, G_TYPE_STRING,
-				    G_TYPE_BOOLEAN));
-	treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL (demo->model));
+	model = pgd_find_create_model ();
+	treeview = gtk_tree_view_new_with_model (model);
+        g_object_unref (model);
+        demo->treeview = treeview;
 	gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (treeview), TRUE);
-	gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)),
-				     GTK_SELECTION_NONE);
-	
+        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
+	gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+        g_signal_connect (selection, "changed",
+                          G_CALLBACK (pgd_find_selection_changed),
+                          demo);
+
 	renderer = gtk_cell_renderer_text_new ();
 	gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
 						     TITLE_COLUMN, "Matches",
@@ -271,8 +498,23 @@
 	gtk_container_add (GTK_CONTAINER (swindow), treeview);
 	gtk_widget_show (treeview);
 
-	gtk_box_pack_start (GTK_BOX (vbox), swindow, TRUE, TRUE, 0);
-	gtk_widget_show (swindow);
+        gtk_paned_add1 (GTK_PANED (hpaned), swindow);
+        gtk_widget_show (swindow);
+
+        demo->darea = gtk_drawing_area_new ();
+        g_signal_connect (demo->darea, "draw",
+                          G_CALLBACK (pgd_find_viewer_drawing_area_draw),
+                          demo);
+
+        swindow = gtk_scrolled_window_new (NULL, NULL);
+        gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (swindow), demo->darea);
+        gtk_widget_show (demo->darea);
+
+        gtk_paned_add2 (GTK_PANED (hpaned), swindow);
+        gtk_widget_show (swindow);
+
+        gtk_box_pack_start (GTK_BOX (vbox), hpaned, TRUE, TRUE, 0);
+        gtk_widget_show (hpaned);
 
 	g_object_weak_ref (G_OBJECT (vbox),
 			   (GWeakNotify)pgd_find_free,
diff --git a/glib/demo/fonts.c b/glib/demo/fonts.c
index 24eb87c..d0ffa3d 100644
--- a/glib/demo/fonts.c
+++ b/glib/demo/fonts.c
@@ -228,9 +228,9 @@
 
 	demo->doc = g_object_ref (document);
 	
-	vbox = gtk_vbox_new (FALSE, 12);
+	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
 
-	hbox = gtk_hbox_new (FALSE, 6);
+	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 	
 	demo->progress = gtk_progress_bar_new ();
 	gtk_progress_bar_set_ellipsize (GTK_PROGRESS_BAR (demo->progress),
diff --git a/glib/demo/forms.c b/glib/demo/forms.c
index 3a74ef5..9dfe3cd 100644
--- a/glib/demo/forms.c
+++ b/glib/demo/forms.c
@@ -79,7 +79,7 @@
 }
 
 static void
-pgd_form_field_view_add_choice_items (GtkTable         *table,
+pgd_form_field_view_add_choice_items (GtkGrid          *table,
 				      PopplerFormField *field,
 				      gint             *selected,
 				      gint             *row)
@@ -92,8 +92,7 @@
 	label = gtk_label_new (NULL);
 	g_object_set (G_OBJECT (label), "xalign", 0.0, NULL);
 	gtk_label_set_markup (GTK_LABEL (label), "<b>Items:</b>");
-	gtk_table_attach (GTK_TABLE (table), label, 0, 1, *row, *row + 1,
-			  GTK_FILL, GTK_FILL, 0, 0);
+	gtk_grid_attach (GTK_GRID (table), label, 0, *row, 1, 1);
 	gtk_widget_show (label);
 
 	swindow = gtk_scrolled_window_new (NULL, NULL);
@@ -120,8 +119,7 @@
 	gtk_container_add (GTK_CONTAINER (swindow), textview);
 	gtk_widget_show (textview);
 	
-	gtk_table_attach (GTK_TABLE (table), swindow, 1, 2, *row, *row + 1,
-			  GTK_FILL, GTK_FILL, 0, 0);
+	gtk_grid_attach (GTK_GRID (table), swindow, 1, *row, 1, 1);
 	gtk_widget_show (swindow); 
 	
 	*row += 1;
@@ -151,23 +149,23 @@
 	if (!field)
 		return;
 
-	table = gtk_table_new (13, 2, FALSE);
-	gtk_table_set_col_spacings (GTK_TABLE (table), 6);
-	gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+	table = gtk_grid_new ();
+	gtk_grid_set_column_spacing (GTK_GRID (table), 6);
+	gtk_grid_set_row_spacing (GTK_GRID (table), 6);
 
 	text = poppler_form_field_get_name (field);
 	if (text) {
-		pgd_table_add_property (GTK_TABLE (table), "<b>Name:</b>", text, &row);
+		pgd_table_add_property (GTK_GRID (table), "<b>Name:</b>", text, &row);
 		g_free (text);
 	}
 	text = poppler_form_field_get_partial_name (field);
 	if (text) {
-		pgd_table_add_property (GTK_TABLE (table), "<b>Partial Name:</b>", text, &row);
+		pgd_table_add_property (GTK_GRID (table), "<b>Partial Name:</b>", text, &row);
 		g_free (text);
 	}
 	text = poppler_form_field_get_mapping_name (field);
 	if (text) {
-		pgd_table_add_property (GTK_TABLE (table), "<b>Mapping Name:</b>", text, &row);
+		pgd_table_add_property (GTK_GRID (table), "<b>Mapping Name:</b>", text, &row);
 		g_free (text);
 	}
 
@@ -177,7 +175,7 @@
 
                 action_view = pgd_action_view_new (NULL);
                 pgd_action_view_set_action (action_view, action);
-                pgd_table_add_property_with_custom_widget (GTK_TABLE (table), "<b>Action:</b>", action_view, &row);
+                pgd_table_add_property_with_custom_widget (GTK_GRID (table), "<b>Action:</b>", action_view, &row);
                 gtk_widget_show (action_view);
         }
 
@@ -185,30 +183,30 @@
 	case POPPLER_FORM_FIELD_BUTTON:
 		enum_value = g_enum_get_value ((GEnumClass *) g_type_class_ref (POPPLER_TYPE_FORM_BUTTON_TYPE),
 					       poppler_form_field_button_get_button_type (field));
-		pgd_table_add_property (GTK_TABLE (table), "<b>Button Type:</b>", enum_value->value_name, &row);
-		pgd_table_add_property (GTK_TABLE (table), "<b>Button State:</b>",
+		pgd_table_add_property (GTK_GRID (table), "<b>Button Type:</b>", enum_value->value_name, &row);
+		pgd_table_add_property (GTK_GRID (table), "<b>Button State:</b>",
 					poppler_form_field_button_get_state (field) ? "Active" : "Inactive", &row);
 		break;
 	case POPPLER_FORM_FIELD_TEXT:
 		enum_value = g_enum_get_value ((GEnumClass *) g_type_class_ref (POPPLER_TYPE_FORM_TEXT_TYPE),
 					       poppler_form_field_text_get_text_type (field));
-		pgd_table_add_property (GTK_TABLE (table), "<b>Text Type:</b>", enum_value->value_name, &row);
+		pgd_table_add_property (GTK_GRID (table), "<b>Text Type:</b>", enum_value->value_name, &row);
 
 		text = poppler_form_field_text_get_text (field);
-		pgd_table_add_property (GTK_TABLE (table), "<b>Contents:</b>", text, &row);
+		pgd_table_add_property (GTK_GRID (table), "<b>Contents:</b>", text, &row);
 		g_free (text);
 
 		text = g_strdup_printf ("%d", poppler_form_field_text_get_max_len (field));
-		pgd_table_add_property (GTK_TABLE (table), "<b>Max Length:</b>", text, &row);
+		pgd_table_add_property (GTK_GRID (table), "<b>Max Length:</b>", text, &row);
 		g_free (text);
 
-		pgd_table_add_property (GTK_TABLE (table), "<b>Do spellcheck:</b>",
+		pgd_table_add_property (GTK_GRID (table), "<b>Do spellcheck:</b>",
 					poppler_form_field_text_do_spell_check (field) ? "Yes" : "No", &row);
-		pgd_table_add_property (GTK_TABLE (table), "<b>Do scroll:</b>",
+		pgd_table_add_property (GTK_GRID (table), "<b>Do scroll:</b>",
 					poppler_form_field_text_do_scroll (field) ? "Yes" : "No", &row);
-		pgd_table_add_property (GTK_TABLE (table), "<b>Rich Text:</b>",
+		pgd_table_add_property (GTK_GRID (table), "<b>Rich Text:</b>",
 					poppler_form_field_text_is_rich_text (field) ? "Yes" : "No", &row);
-		pgd_table_add_property (GTK_TABLE (table), "<b>Pasword type:</b>",
+		pgd_table_add_property (GTK_GRID (table), "<b>Pasword type:</b>",
 					poppler_form_field_text_is_password (field) ? "Yes" : "No", &row);
 		break;
 	case POPPLER_FORM_FIELD_CHOICE: {
@@ -217,32 +215,32 @@
 		
 		enum_value = g_enum_get_value ((GEnumClass *) g_type_class_ref (POPPLER_TYPE_FORM_CHOICE_TYPE),
 					       poppler_form_field_choice_get_choice_type (field));
-		pgd_table_add_property (GTK_TABLE (table), "<b>Choice Type:</b>", enum_value->value_name, &row);
-		pgd_table_add_property (GTK_TABLE (table), "<b>Editable:</b>",
+		pgd_table_add_property (GTK_GRID (table), "<b>Choice Type:</b>", enum_value->value_name, &row);
+		pgd_table_add_property (GTK_GRID (table), "<b>Editable:</b>",
 					poppler_form_field_choice_is_editable (field) ? "Yes" : "No", &row);
-		pgd_table_add_property (GTK_TABLE (table), "<b>Multiple Selection:</b>",
+		pgd_table_add_property (GTK_GRID (table), "<b>Multiple Selection:</b>",
 					poppler_form_field_choice_can_select_multiple (field) ? "Yes" : "No", &row);
-		pgd_table_add_property (GTK_TABLE (table), "<b>Do spellcheck:</b>",
+		pgd_table_add_property (GTK_GRID (table), "<b>Do spellcheck:</b>",
 					poppler_form_field_choice_do_spell_check (field) ? "Yes" : "No", &row);
-		pgd_table_add_property (GTK_TABLE (table), "<b>Commit on Change:</b>",
+		pgd_table_add_property (GTK_GRID (table), "<b>Commit on Change:</b>",
 					poppler_form_field_choice_commit_on_change (field) ? "Yes" : "No", &row);
 
 		text = g_strdup_printf ("%d", poppler_form_field_choice_get_n_items (field));
-		pgd_table_add_property (GTK_TABLE (table), "<b>Number of items:</b>", text, &row);
+		pgd_table_add_property (GTK_GRID (table), "<b>Number of items:</b>", text, &row);
 		g_free (text);
 
-		pgd_form_field_view_add_choice_items (GTK_TABLE (table), field, &selected, &row);
+		pgd_form_field_view_add_choice_items (GTK_GRID (table), field, &selected, &row);
 
 		if (selected >= 0 && poppler_form_field_choice_get_n_items (field) > selected) {
 			item = poppler_form_field_choice_get_item (field, selected);
 			text = g_strdup_printf ("%d (%s)", selected, item);
 			g_free (item);
-			pgd_table_add_property (GTK_TABLE (table), "<b>Selected item:</b>", text, &row);
+			pgd_table_add_property (GTK_GRID (table), "<b>Selected item:</b>", text, &row);
 			g_free (text);
 		}
 
 		text = poppler_form_field_choice_get_text (field);
-		pgd_table_add_property (GTK_TABLE (table), "<b>Contents:</b>", text, &row);
+		pgd_table_add_property (GTK_GRID (table), "<b>Contents:</b>", text, &row);
 		g_free (text);
 	}
 		break;
@@ -390,9 +388,9 @@
 	
 	n_pages = poppler_document_get_n_pages (document);
 
-	vbox = gtk_vbox_new (FALSE, 12);
+	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
 
-	hbox = gtk_hbox_new (FALSE, 6);
+	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 
 	label = gtk_label_new ("Page:");
 	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
@@ -427,7 +425,7 @@
 	gtk_box_pack_start (GTK_BOX (vbox), demo->timer_label, FALSE, TRUE, 0);
 	gtk_widget_show (demo->timer_label);
 
-	hpaned = gtk_hpaned_new ();
+	hpaned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
 
 	demo->field_view = pgd_form_field_view_new ();
 
diff --git a/glib/demo/images.c b/glib/demo/images.c
index 6e987d2..3623fd7 100644
--- a/glib/demo/images.c
+++ b/glib/demo/images.c
@@ -61,11 +61,10 @@
 }
 
 static gboolean
-pgd_image_view_drawing_area_expose (GtkWidget      *area,
-				    GdkEventExpose *event,
-				    GtkWidget      *image_view)
+pgd_image_view_drawing_area_draw (GtkWidget *area,
+                                  cairo_t   *cr,
+                                  GtkWidget *image_view)
 {
-	cairo_t         *cr;
 	cairo_surface_t *image;
 
 	image = g_object_get_data (G_OBJECT (image_view), "image-surface");
@@ -75,11 +74,9 @@
 	gtk_widget_set_size_request (area,
 				     cairo_image_surface_get_width (image),
 				     cairo_image_surface_get_height (image));
-	
-	cr = gdk_cairo_create (gtk_widget_get_window (area));
+
 	cairo_set_source_surface (cr, image, 0, 0);
 	cairo_paint (cr);
-	cairo_destroy (cr);
 
 	return TRUE;
 }
@@ -93,8 +90,8 @@
 	swindow = gtk_scrolled_window_new (NULL, NULL);
 	
 	darea = gtk_drawing_area_new ();
-	g_signal_connect (G_OBJECT (darea), "expose_event",
-			  G_CALLBACK (pgd_image_view_drawing_area_expose),
+	g_signal_connect (G_OBJECT (darea), "draw",
+			  G_CALLBACK (pgd_image_view_drawing_area_draw),
 			  (gpointer)swindow);
 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
 					GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
@@ -229,9 +226,9 @@
 	
 	n_pages = poppler_document_get_n_pages (document);
 
-	vbox = gtk_vbox_new (FALSE, 12);
+	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
 
-	hbox = gtk_hbox_new (FALSE, 6);
+	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 
 	label = gtk_label_new ("Page:");
 	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
@@ -266,7 +263,7 @@
 	gtk_box_pack_start (GTK_BOX (vbox), demo->timer_label, FALSE, TRUE, 0);
 	gtk_widget_show (demo->timer_label);
 
-	hpaned = gtk_hpaned_new ();
+	hpaned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
 
 	demo->image_view = pgd_image_view_new ();
 
diff --git a/glib/demo/info.cc b/glib/demo/info.cc
index e05cc15..590ddc7 100644
--- a/glib/demo/info.cc
+++ b/glib/demo/info.cc
@@ -23,7 +23,7 @@
 #include "utils.h"
 
 static void
-pgd_info_add_permissions (GtkTable           *table,
+pgd_info_add_permissions (GtkGrid            *table,
 			  PopplerPermissions  permissions,
 			  gint               *row)
 {
@@ -33,12 +33,11 @@
 	label = gtk_label_new (NULL);
 	g_object_set (G_OBJECT (label), "xalign", 0.0, NULL);
 	gtk_label_set_markup (GTK_LABEL (label), "<b>Permissions:</b>");
-	gtk_table_attach (GTK_TABLE (table), label, 0, 1, *row, *row + 1,
-			  GTK_FILL, GTK_FILL, 0, 0);
+	gtk_grid_attach (GTK_GRID (table), label, 0, *row, 1, 1);
 	gtk_widget_show (label);
 
-        vbox = gtk_vbox_new (FALSE, 0);
-	hbox = gtk_hbox_new (FALSE, 6);
+        vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 
 	checkbox = gtk_check_button_new_with_label ("Print");
 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbox),
@@ -73,7 +72,7 @@
         gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
         gtk_widget_show (hbox);
 
-        hbox = gtk_hbox_new (FALSE, 6);
+        hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 
         checkbox = gtk_check_button_new_with_label ("Extract contents");
         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbox),
@@ -96,15 +95,14 @@
         gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
         gtk_widget_show (hbox);
 
-	gtk_table_attach (GTK_TABLE (table), vbox, 1, 2, *row, *row + 1,
-			  GTK_FILL, GTK_FILL, 0, 0);
+	gtk_grid_attach (GTK_GRID (table), vbox, 1, *row, 1, 1);
 	gtk_widget_show (vbox);
 	
 	*row += 1;
 }
 
 static void
-pgd_info_add_metadata (GtkTable    *table,
+pgd_info_add_metadata (GtkGrid     *table,
 		       const gchar *metadata,
 		       gint        *row)
 {
@@ -115,8 +113,7 @@
 	label = gtk_label_new (NULL);
 	g_object_set (G_OBJECT (label), "xalign", 0.0, NULL);
 	gtk_label_set_markup (GTK_LABEL (label), "<b>Metadata:</b>");
-	gtk_table_attach (GTK_TABLE (table), label, 0, 1, *row, *row + 1,
-			  GTK_FILL, GTK_FILL, 0, 0);
+	gtk_grid_attach (GTK_GRID (table), label, 0, *row, 1, 1);
 	gtk_widget_show (label);
 
 	swindow = gtk_scrolled_window_new (NULL, NULL);
@@ -132,10 +129,10 @@
 
 	gtk_container_add (GTK_CONTAINER (swindow), textview);
 	gtk_widget_show (textview);
-	
-	gtk_table_attach (GTK_TABLE (table), swindow, 1, 2, *row, *row + 1,
-			  (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
-			  (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
+
+	gtk_grid_attach (GTK_GRID (table), swindow, 1, *row, 1, 1);
+        gtk_widget_set_hexpand (swindow, TRUE);
+        gtk_widget_set_vexpand (swindow, TRUE);
 	gtk_widget_show (swindow); 
 	
 	*row += 1;
@@ -181,7 +178,7 @@
 		      "metadata", &metadata,
 		      NULL);
 	
-	vbox = gtk_vbox_new (FALSE, 12);
+	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
 
 	backend = poppler_get_backend ();
 	enum_value = g_enum_get_value ((GEnumClass *) g_type_class_ref (POPPLER_TYPE_BACKEND), backend);
@@ -205,61 +202,61 @@
 	gtk_container_add (GTK_CONTAINER (frame), alignment);
 	gtk_widget_show (alignment);
 
-	table = gtk_table_new (14, 2, FALSE);
-	gtk_table_set_col_spacings (GTK_TABLE (table), 6);
-	gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+	table = gtk_grid_new ();
+	gtk_grid_set_column_spacing (GTK_GRID (table), 6);
+	gtk_grid_set_row_spacing (GTK_GRID (table), 6);
 
-	pgd_table_add_property (GTK_TABLE (table), "<b>Format:</b>", format, &row);
+	pgd_table_add_property (GTK_GRID (table), "<b>Format:</b>", format, &row);
 	g_free (format);
 	
-	pgd_table_add_property (GTK_TABLE (table), "<b>Title:</b>", title, &row);
+	pgd_table_add_property (GTK_GRID (table), "<b>Title:</b>", title, &row);
 	g_free (title);
 	
-	pgd_table_add_property (GTK_TABLE (table), "<b>Author:</b>", author, &row);
+	pgd_table_add_property (GTK_GRID (table), "<b>Author:</b>", author, &row);
 	g_free (author);
 	
-	pgd_table_add_property (GTK_TABLE (table), "<b>Subject:</b>", subject, &row);
+	pgd_table_add_property (GTK_GRID (table), "<b>Subject:</b>", subject, &row);
 	g_free (subject);
 	
-	pgd_table_add_property (GTK_TABLE (table), "<b>Keywords:</b>", keywords, &row);
+	pgd_table_add_property (GTK_GRID (table), "<b>Keywords:</b>", keywords, &row);
 	g_free (keywords);
 	
-	pgd_table_add_property (GTK_TABLE (table), "<b>Creator:</b>", creator, &row);
+	pgd_table_add_property (GTK_GRID (table), "<b>Creator:</b>", creator, &row);
 	g_free (creator);
 	
-	pgd_table_add_property (GTK_TABLE (table), "<b>Producer:</b>", producer, &row);
+	pgd_table_add_property (GTK_GRID (table), "<b>Producer:</b>", producer, &row);
 	g_free (producer);
 	
-	pgd_table_add_property (GTK_TABLE (table), "<b>Linearized:</b>", linearized ? "Yes" : "No", &row);
+	pgd_table_add_property (GTK_GRID (table), "<b>Linearized:</b>", linearized ? "Yes" : "No", &row);
 
 	str = pgd_format_date (creation_date);
-	pgd_table_add_property (GTK_TABLE (table), "<b>Creation Date:</b>", str, &row);
+	pgd_table_add_property (GTK_GRID (table), "<b>Creation Date:</b>", str, &row);
 	g_free (str);
 
 	str = pgd_format_date (mod_date);
-	pgd_table_add_property (GTK_TABLE (table), "<b>Modification Date:</b>", str, &row);
+	pgd_table_add_property (GTK_GRID (table), "<b>Modification Date:</b>", str, &row);
 	g_free (str);
 
 	enum_value = g_enum_get_value ((GEnumClass *) g_type_class_peek (POPPLER_TYPE_PAGE_MODE), mode);
-	pgd_table_add_property (GTK_TABLE (table), "<b>Page Mode:</b>", enum_value->value_name, &row);
+	pgd_table_add_property (GTK_GRID (table), "<b>Page Mode:</b>", enum_value->value_name, &row);
 
 	enum_value = g_enum_get_value ((GEnumClass *) g_type_class_peek (POPPLER_TYPE_PAGE_LAYOUT), layout);
-	pgd_table_add_property (GTK_TABLE (table), "<b>Page Layout:</b>", enum_value->value_name, &row);
+	pgd_table_add_property (GTK_GRID (table), "<b>Page Layout:</b>", enum_value->value_name, &row);
 
 	if (poppler_document_get_id (document, &perm_id, &up_id)) {
 		str = g_strndup (perm_id, 32);
 		g_free (perm_id);
-		pgd_table_add_property (GTK_TABLE (table), "<b>Permanent ID:</b>", str, &row);
+		pgd_table_add_property (GTK_GRID (table), "<b>Permanent ID:</b>", str, &row);
 		g_free (str);
 		str = g_strndup (up_id, 32);
 		g_free (up_id);
-		pgd_table_add_property (GTK_TABLE (table), "<b>Update ID:</b>", str, &row);
+		pgd_table_add_property (GTK_GRID (table), "<b>Update ID:</b>", str, &row);
 		g_free (str);
 	}
 
-	pgd_info_add_permissions (GTK_TABLE (table), permissions, &row);
+	pgd_info_add_permissions (GTK_GRID (table), permissions, &row);
 
-	pgd_info_add_metadata (GTK_TABLE (table), metadata, &row);
+	pgd_info_add_metadata (GTK_GRID (table), metadata, &row);
 	g_free (metadata);
 
 	/* TODO: view_prefs */
diff --git a/glib/demo/layers.c b/glib/demo/layers.c
index fc0ba35..c55de42 100644
--- a/glib/demo/layers.c
+++ b/glib/demo/layers.c
@@ -181,24 +181,18 @@
 }
 
 static gboolean
-pgd_layers_viewer_drawing_area_expose (GtkWidget      *area,
-				       GdkEventExpose *event,
-				       PgdLayersDemo  *demo)
+pgd_layers_viewer_drawing_area_draw (GtkWidget     *area,
+                                     cairo_t       *cr,
+                                     PgdLayersDemo *demo)
 {
-	cairo_t *cr;
-	
 	if (!demo->surface) {
 		demo->surface = pgd_layers_render_page (demo);
 		if (!demo->surface)
 			return FALSE;
 	}
 
-	gdk_window_clear (gtk_widget_get_window (area));
-
-	cr = gdk_cairo_create (gtk_widget_get_window (area));
 	cairo_set_source_surface (cr, demo->surface, 0, 0);
 	cairo_paint (cr);
-	cairo_destroy (cr);
 
 	return TRUE;
 }
@@ -238,9 +232,9 @@
 	guint      n_pages;
 	gchar     *str;
 
-	vbox = gtk_vbox_new (FALSE, 6);
+	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
 
-	hbox = gtk_hbox_new (FALSE, 6);
+	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 
 	label = gtk_label_new ("Page:");
 	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
@@ -264,8 +258,8 @@
 	gtk_widget_show (hbox);
 
 	demo->darea = gtk_drawing_area_new ();
-	g_signal_connect (G_OBJECT (demo->darea), "expose_event",
-			  G_CALLBACK (pgd_layers_viewer_drawing_area_expose),
+	g_signal_connect (G_OBJECT (demo->darea), "draw",
+			  G_CALLBACK (pgd_layers_viewer_drawing_area_draw),
 			  (gpointer)demo);
 
 	swindow = gtk_scrolled_window_new (NULL, NULL);
@@ -385,7 +379,7 @@
 	demo = g_new0 (PgdLayersDemo, 1);
 	demo->doc = g_object_ref (document);
 	
-	hpaned = gtk_hpaned_new ();
+	hpaned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
 
 	viewer = pgd_layers_create_viewer (demo);
 	
diff --git a/glib/demo/links.c b/glib/demo/links.c
index 6cb4a5d..3670436 100644
--- a/glib/demo/links.c
+++ b/glib/demo/links.c
@@ -178,9 +178,9 @@
 	
 	n_pages = poppler_document_get_n_pages (document);
 
-	vbox = gtk_vbox_new (FALSE, 12);
+	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
 
-	hbox = gtk_hbox_new (FALSE, 6);
+	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 
 	label = gtk_label_new ("Page:");
 	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
@@ -215,7 +215,7 @@
 	gtk_box_pack_start (GTK_BOX (vbox), demo->timer_label, FALSE, TRUE, 0);
 	gtk_widget_show (demo->timer_label);
 
-	hpaned = gtk_hpaned_new ();
+	hpaned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
 
 	demo->action_view = pgd_action_view_new (document);
 
diff --git a/glib/demo/main.c b/glib/demo/main.c
index f52c5b8..2523800 100644
--- a/glib/demo/main.c
+++ b/glib/demo/main.c
@@ -180,9 +180,6 @@
 	action_area = gtk_dialog_get_action_area (dialog);
 
 	/* Set the dialog up with HIG properties */
-#if !GTK_CHECK_VERSION (2, 22, 0)
-	gtk_dialog_set_has_separator (dialog, FALSE);
-#endif
 	gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
 	gtk_box_set_spacing (GTK_BOX (content_area), 2); /* 2 * 5 + 2 = 12 */
 	gtk_container_set_border_width (GTK_CONTAINER (action_area), 5);
@@ -206,7 +203,7 @@
 						 -1);
 
 	/* Build contents */
-	hbox = gtk_hbox_new (FALSE, 12);
+	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
 	gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
 	gtk_box_pack_start (GTK_BOX (content_area), hbox, TRUE, TRUE, 0);
 	gtk_widget_show (hbox);
@@ -218,7 +215,7 @@
 	gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
 	gtk_widget_show (icon);
 
-	main_vbox = gtk_vbox_new (FALSE, 18);
+	main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 18);
 	gtk_box_pack_start (GTK_BOX (hbox), main_vbox, TRUE, TRUE, 0);
 	gtk_widget_show (main_vbox);
 
@@ -238,7 +235,7 @@
 			    FALSE, FALSE, 0);
 	gtk_widget_show (label);
 
-	vbox = gtk_vbox_new (FALSE, 6);
+	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
 	gtk_box_pack_start (GTK_BOX (main_vbox), vbox, FALSE, FALSE, 0);
 	gtk_widget_show (vbox);
 
@@ -252,9 +249,9 @@
 			    FALSE, FALSE, 0);
 	gtk_widget_show (entry_container);
 
-	table = gtk_table_new (1, 2, FALSE);
-	gtk_table_set_col_spacings (GTK_TABLE (table), 12);
-	gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+	table = gtk_grid_new ();
+	gtk_grid_set_column_spacing (GTK_GRID (table), 12);
+	gtk_grid_set_row_spacing (GTK_GRID (table), 6);
 	gtk_container_add (GTK_CONTAINER (entry_container), table);
 	gtk_widget_show (table);
 
@@ -270,13 +267,11 @@
 			  G_CALLBACK (pgd_demo_auth_dialog_entry_activated),
 			  dialog);
 
-	gtk_table_attach (GTK_TABLE (table), label,
-			  0, 1, 0, 1,
-			  GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+	gtk_grid_attach (GTK_GRID (table), label, 0, 0, 1, 1);
 	gtk_widget_show (label);
 
-	gtk_table_attach_defaults (GTK_TABLE (table), password_entry,
-				   1, 2, 0, 1);
+        gtk_grid_attach (GTK_GRID (table), password_entry, 1, 0, 1, 1);
+        gtk_widget_set_hexpand (password_entry, TRUE);
 	gtk_widget_show (password_entry);
 
 	gtk_label_set_mnemonic_widget (GTK_LABEL (label), password_entry);
@@ -293,7 +288,6 @@
 	GtkWidget        *treeview;
 	GtkTreeSelection *selection;
 	GFile            *file;
-	gchar            *uri;
 	GTimer           *timer;
 	GError           *error = NULL;
 	GtkAccelGroup    *gtk_accel;
@@ -304,19 +298,12 @@
 		return 1;
 	}
 
-/* Threading is always enabled starting from GLib 2.24.0 */
-#if !GLIB_CHECK_VERSION (2, 24, 0)
-	if (!g_thread_supported ())
-		g_thread_init (NULL);
-#endif
-
 	gtk_init (&argc, &argv);
 
 	file = g_file_new_for_commandline_arg (argv[1]);
-	uri = g_file_get_uri (file);
 
 	timer = g_timer_new ();
-	document = poppler_document_new_from_file (uri, NULL, &error);
+	document = poppler_document_new_from_gfile (file, NULL, NULL, &error);
 	g_timer_stop (timer);
 	if (error) {
 		while (g_error_matches (error, POPPLER_ERROR, POPPLER_ERROR_ENCRYPTED)) {
@@ -327,7 +314,6 @@
 			if (gtk_dialog_run (dialog) != GTK_RESPONSE_OK) {
 				g_print ("Error: no password provided\n");
 				g_object_unref (file);
-				g_free (uri);
 
 				return 1;
 			}
@@ -336,7 +322,7 @@
 			password = g_object_get_data (G_OBJECT (dialog), "pgd-password");
 
 			g_timer_start (timer);
-			document = poppler_document_new_from_file (uri, password, &error);
+			document = poppler_document_new_from_gfile (file, password, NULL, &error);
 			g_timer_stop (timer);
 
 			gtk_widget_destroy (GTK_WIDGET (dialog));
@@ -346,14 +332,12 @@
 			g_print ("Error: %s\n", error->message);
 			g_error_free (error);
 			g_object_unref (file);
-			g_free (uri);
 
 			return 1;
 		}
 	}
 
 	g_object_unref (file);
-	g_free (uri);
 
 	g_print ("Document successfully loaded in %.4f seconds\n",
 		 g_timer_elapsed (timer, NULL));
@@ -373,7 +357,7 @@
 	g_closure_unref (closure);
 	gtk_window_add_accel_group (GTK_WINDOW(win), gtk_accel);
 
-	hbox = gtk_hbox_new (FALSE, 6);
+	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 
 	treeview = pgd_demo_list_create ();
 	gtk_box_pack_start (GTK_BOX (hbox), treeview, FALSE, TRUE, 0);
diff --git a/glib/demo/outline.c b/glib/demo/outline.c
index 5dc7189..55874cc 100644
--- a/glib/demo/outline.c
+++ b/glib/demo/outline.c
@@ -161,7 +161,7 @@
 	GtkTreeSelection *selection;
 	GtkWidget        *hpaned, *action;
 
-	hpaned = gtk_hpaned_new ();
+	hpaned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
 
 	action = pgd_action_view_new (document);
 	
diff --git a/glib/demo/page.c b/glib/demo/page.c
index 7ab008c..ddf7789 100644
--- a/glib/demo/page.c
+++ b/glib/demo/page.c
@@ -229,9 +229,9 @@
 
 	n_pages = poppler_document_get_n_pages (document);
 
-	vbox = gtk_vbox_new (FALSE, 12);
+	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
 
-	hbox = gtk_hbox_new (FALSE, 6);
+	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 
 	label = gtk_label_new ("Page:");
 	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
@@ -260,7 +260,7 @@
 	gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
 	gtk_widget_show (hbox);
 
-	hbox = gtk_hbox_new (FALSE, 6);
+	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 
 	frame = gtk_frame_new (NULL);
 	gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);
@@ -274,18 +274,17 @@
 	gtk_container_add (GTK_CONTAINER (frame), alignment);
 	gtk_widget_show (alignment);
 
-	table = gtk_table_new (3, 2, FALSE);
+	table = gtk_grid_new ();
+	gtk_grid_set_column_spacing (GTK_GRID (table), 6);
+	gtk_grid_set_row_spacing (GTK_GRID (table), 6);
 
-	gtk_table_set_col_spacings (GTK_TABLE (table), 6);
-	gtk_table_set_row_spacings (GTK_TABLE (table), 6);
-
-	pgd_table_add_property_with_value_widget (GTK_TABLE (table), "<b>Page Index:</b>",
+	pgd_table_add_property_with_value_widget (GTK_GRID (table), "<b>Page Index:</b>",
 						  &(demo->index), NULL, &row);
-	pgd_table_add_property_with_value_widget (GTK_TABLE (table), "<b>Page Label:</b>",
+	pgd_table_add_property_with_value_widget (GTK_GRID (table), "<b>Page Label:</b>",
 						  &(demo->label), NULL, &row);
-	pgd_table_add_property_with_value_widget (GTK_TABLE (table), "<b>Page Size:</b>",
+	pgd_table_add_property_with_value_widget (GTK_GRID (table), "<b>Page Size:</b>",
 						  &(demo->size), NULL, &row);
-	pgd_table_add_property_with_value_widget (GTK_TABLE (table), "<b>Page Duration:</b>",
+	pgd_table_add_property_with_value_widget (GTK_GRID (table), "<b>Page Duration:</b>",
 						  &(demo->duration), NULL, &row);
 
 	gtk_container_add (GTK_CONTAINER (alignment), table);
@@ -307,7 +306,7 @@
 	gtk_container_add (GTK_CONTAINER (frame), alignment);
 	gtk_widget_show (alignment);
 	
-	thumnail_box = gtk_vbox_new (FALSE, 6);
+	thumnail_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
 	
 	demo->thumbnail = gtk_image_new ();
 	gtk_box_pack_start (GTK_BOX (thumnail_box), demo->thumbnail, TRUE, TRUE, 0);
diff --git a/glib/demo/print.c b/glib/demo/print.c
index 17bbe0e..8147036 100644
--- a/glib/demo/print.c
+++ b/glib/demo/print.c
@@ -114,24 +114,18 @@
                                                            PGD_PRINT_OPTIONS,
                                                            PRINT_DOCUMENT_MARKUPS);
 
-        hbox = gtk_hbox_new (FALSE, 0);
+        hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
         gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
 
         label = gtk_label_new ("Print: ");
         gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
         gtk_widget_show (label);
 
-#if GTK_CHECK_VERSION (2, 24, 0)
         combo = gtk_combo_box_text_new ();
         gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "Document");
         gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "Document and markup");
         gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "Document and stamps");
-#else
-        combo = gtk_combo_box_new_text ();
-        gtk_combo_box_append_text (GTK_COMBO_BOX (combo), "Document");
-        gtk_combo_box_append_text (GTK_COMBO_BOX (combo), "Document and markup");
-        gtk_combo_box_append_text (GTK_COMBO_BOX (combo), "Document and stamps");
-#endif
+
         demo->options_combo = combo;
         gtk_combo_box_set_active (GTK_COMBO_BOX (combo), options);
         gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0);
@@ -210,9 +204,9 @@
 
 	demo->doc = g_object_ref (document);
 
-	vbox = gtk_vbox_new (FALSE, 12);
+	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
 
-	hbox = gtk_hbox_new (FALSE, 6);
+	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 
 	button = gtk_button_new_with_label ("Print...");
 	g_signal_connect (G_OBJECT (button), "clicked",
diff --git a/glib/demo/render.c b/glib/demo/render.c
index bb7afbd..48e9210 100644
--- a/glib/demo/render.c
+++ b/glib/demo/render.c
@@ -23,16 +23,10 @@
 
 #include "render.h"
 
-typedef enum {
-	PGD_RENDER_CAIRO,
-	PGD_RENDER_PIXBUF
-} PgdRenderMode;
-
 typedef struct {
 	PopplerDocument *doc;
 
 	/* Properties */
-	PgdRenderMode    mode;
 	gint             page;
 	gdouble          scale;
 	gint             rotate;
@@ -48,7 +42,6 @@
 	GtkWidget       *timer_label;
 
 	cairo_surface_t *surface;
-	GdkPixbuf       *pixbuf;
 } PgdRenderDemo;
 
 static void
@@ -61,53 +54,25 @@
 		g_object_unref (demo->doc);
 		demo->doc = NULL;
 	}
-	
+
 	if (demo->surface) {
 		cairo_surface_destroy (demo->surface);
 		demo->surface = NULL;
 	}
 
-	if (demo->pixbuf) {
-		g_object_unref (demo->pixbuf);
-		demo->pixbuf = NULL;
-	}
-
 	g_free (demo);
 }
 
 static gboolean
-pgd_render_drawing_area_expose (GtkWidget      *area,
-				GdkEventExpose *event,
-				PgdRenderDemo  *demo)
+pgd_render_drawing_area_draw (GtkWidget     *area,
+                              cairo_t       *cr,
+                              PgdRenderDemo *demo)
 {
-	if (demo->mode == PGD_RENDER_CAIRO && !demo->surface)
+	if (!demo->surface)
 		return FALSE;
 
-	if (demo->mode == PGD_RENDER_PIXBUF && !demo->pixbuf)
-		return FALSE;
-
-	gdk_window_clear (gtk_widget_get_window (area));
-
-	if (demo->mode == PGD_RENDER_CAIRO) {
-		cairo_t *cr;
-
-		cr = gdk_cairo_create (gtk_widget_get_window (area));
-		cairo_set_source_surface (cr, demo->surface, 0, 0);
-		cairo_paint (cr);
-		cairo_destroy (cr);
-	} else if (demo->mode == PGD_RENDER_PIXBUF) {
-		gdk_draw_pixbuf (gtk_widget_get_window (area),
-				 gtk_widget_get_style(area)->fg_gc[GTK_STATE_NORMAL],
-				 demo->pixbuf,
-				 0, 0,
-				 0, 0,
-				 gdk_pixbuf_get_width (demo->pixbuf),
-				 gdk_pixbuf_get_height (demo->pixbuf),
-				 GDK_RGB_DITHER_NORMAL,
-				 0, 0);
-	} else {
-		g_assert_not_reached ();
-	}
+        cairo_set_source_surface (cr, demo->surface, 0, 0);
+        cairo_paint (cr);
 
 	return TRUE;
 }
@@ -122,6 +87,7 @@
 	gint         x, y;
 	gchar       *str;
 	GTimer      *timer;
+        cairo_t     *cr;
 
 	page = poppler_document_get_page (demo->doc, demo->page);
 	if (!page)
@@ -131,10 +97,6 @@
 		cairo_surface_destroy (demo->surface);
 	demo->surface = NULL;
 
-	if (demo->pixbuf)
-		g_object_unref (demo->pixbuf);
-	demo->pixbuf = NULL;
-	
 	poppler_page_get_size (page, &page_width, &page_height);
 
 	if (demo->rotate == 0 || demo->rotate == 180) {
@@ -149,90 +111,58 @@
 		y = demo->slice.x * demo->scale;
 	}
 
-	if (demo->mode == PGD_RENDER_CAIRO) {
-		cairo_t *cr;
+        timer = g_timer_new ();
+        demo->surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                                    width, height);
+        cr = cairo_create (demo->surface);
 
-		timer = g_timer_new ();
-		demo->surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
-							    width, height);
-		cr = cairo_create (demo->surface);
+        cairo_save (cr);
+        switch (demo->rotate) {
+        case 90:
+                cairo_translate (cr, x + width, -y);
+                break;
+        case 180:
+                cairo_translate (cr, x + width, y + height);
+                break;
+        case 270:
+                cairo_translate (cr, -x, y + height);
+                break;
+        default:
+                cairo_translate (cr, -x, -y);
+        }
 
-		cairo_save (cr);
-		switch (demo->rotate) {
-		case 90:
-			cairo_translate (cr, x + width, -y);
-			break;
-		case 180:
-			cairo_translate (cr, x + width, y + height);
-			break;
-		case 270:
-			cairo_translate (cr, -x, y + height);
-			break;
-		default:
-			cairo_translate (cr, -x, -y);
-		}
+        if (demo->scale != 1.0)
+                cairo_scale (cr, demo->scale, demo->scale);
 
-		if (demo->scale != 1.0)
-			cairo_scale (cr, demo->scale, demo->scale);
-		
-		if (demo->rotate != 0)
-			cairo_rotate (cr, demo->rotate * G_PI / 180.0);
+        if (demo->rotate != 0)
+                cairo_rotate (cr, demo->rotate * G_PI / 180.0);
 
-		if (demo->printing)
-			poppler_page_render_for_printing (page, cr);
-		else
-			poppler_page_render (page, cr);
-		cairo_restore (cr);
+        if (demo->printing)
+                poppler_page_render_for_printing (page, cr);
+        else
+                poppler_page_render (page, cr);
+        cairo_restore (cr);
 
-		cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER);
-		cairo_set_source_rgb (cr, 1., 1., 1.);
-		cairo_paint (cr);
+        cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER);
+        cairo_set_source_rgb (cr, 1., 1., 1.);
+        cairo_paint (cr);
 
-		g_timer_stop (timer);
-		
-		cairo_destroy (cr);
-	} else if (demo->mode == PGD_RENDER_PIXBUF) {
-#ifdef POPPLER_WITH_GDK
-		timer = g_timer_new ();
-		demo->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
-					       FALSE, 8, width, height);
-		gdk_pixbuf_fill (demo->pixbuf, 0xffffff);
-		if (demo->printing) {
-			poppler_page_render_to_pixbuf_for_printing (page,
-								    x, y,
-								    width,
-								    height,
-								    demo->scale,
-								    demo->rotate,
-								    demo->pixbuf);
-		} else {
-			poppler_page_render_to_pixbuf (page,
-						       x, y,
-						       width,
-						       height,
-						       demo->scale,
-						       demo->rotate,
-						       demo->pixbuf);
-		}
-		g_timer_stop (timer);
-#endif /* POPPLER_WITH_GDK */
-	} else {
-		g_assert_not_reached ();
-	}
+        g_timer_stop (timer);
 
+        cairo_destroy (cr);
 	g_object_unref (page);
-	
+
 	str = g_strdup_printf ("<i>Page rendered in %.4f seconds</i>",
 			       g_timer_elapsed (timer, NULL));
 	gtk_label_set_markup (GTK_LABEL (demo->timer_label), str);
 	g_free (str);
-	
+
 	g_timer_destroy (timer);
-	
+
 	gtk_widget_set_size_request (demo->darea, width, height);
 	gtk_widget_queue_draw (demo->darea);
 }
-	
+
 static void
 pgd_render_slice_selector_setup (PgdRenderDemo *demo)
 {
@@ -288,13 +218,6 @@
 }
 
 static void
-pgd_render_mode_selector_changed (GtkComboBox   *combobox,
-				  PgdRenderDemo *demo)
-{
-	demo->mode = gtk_combo_box_get_active (combobox);
-}
-
-static void
 pgd_render_slice_selector_value_changed (GtkSpinButton *spinbutton,
 					 PgdRenderDemo *demo)
 {
@@ -312,7 +235,6 @@
 	GtkWidget *page_hbox, *page_selector;
 	GtkWidget *scale_hbox, *scale_selector;
 	GtkWidget *rotate_hbox, *rotate_selector;
-	GtkWidget *mode_hbox, *mode_selector;
 	GtkWidget *printing_selector;
 	GtkWidget *slice_hbox, *slice_selector;
 	GtkWidget *button;
@@ -321,18 +243,18 @@
 
 	n_pages = poppler_document_get_n_pages (demo->doc);
 
-	vbox = gtk_vbox_new (FALSE, 6);
-	
-	hbox = gtk_hbox_new (FALSE, 12);
+	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+
+	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
 	gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
 	gtk_widget_show (hbox);
 
-	page_hbox = gtk_hbox_new (FALSE, 6);
+	page_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 
 	label = gtk_label_new ("Page:");
 	gtk_box_pack_start (GTK_BOX (page_hbox), label, TRUE, TRUE, 0);
 	gtk_widget_show (label);
-	
+
 	page_selector = gtk_spin_button_new_with_range (1, n_pages, 1);
 	g_signal_connect (G_OBJECT (page_selector), "value-changed",
 			  G_CALLBACK (pgd_render_page_selector_value_changed),
@@ -349,12 +271,12 @@
 	gtk_box_pack_start (GTK_BOX (hbox), page_hbox, FALSE, TRUE, 0);
 	gtk_widget_show (page_hbox);
 
-	scale_hbox = gtk_hbox_new (FALSE, 6);
-	
+	scale_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+
 	label = gtk_label_new ("Scale:");
 	gtk_box_pack_start (GTK_BOX (scale_hbox), label, TRUE, TRUE, 0);
 	gtk_widget_show (label);
-	
+
 	scale_selector = gtk_spin_button_new_with_range (0, 10.0, 0.1);
 	gtk_spin_button_set_value (GTK_SPIN_BUTTON (scale_selector), 1.0);
 	g_signal_connect (G_OBJECT (scale_selector), "value-changed",
@@ -366,25 +288,17 @@
 	gtk_box_pack_start (GTK_BOX (hbox), scale_hbox, FALSE, TRUE, 0);
 	gtk_widget_show (scale_hbox);
 
-	rotate_hbox = gtk_hbox_new (FALSE, 6);
+	rotate_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 
 	label = gtk_label_new ("Rotate:");
 	gtk_box_pack_start (GTK_BOX (rotate_hbox), label, TRUE, TRUE, 0);
 	gtk_widget_show (label);
 
-#if GTK_CHECK_VERSION (2, 24, 0)
 	rotate_selector = gtk_combo_box_text_new ();
 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (rotate_selector), "0");
 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (rotate_selector), "90");
 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (rotate_selector), "180");
 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (rotate_selector), "270");
-#else
-	rotate_selector = gtk_combo_box_new_text ();
-	gtk_combo_box_append_text (GTK_COMBO_BOX (rotate_selector), "0");
-	gtk_combo_box_append_text (GTK_COMBO_BOX (rotate_selector), "90");
-	gtk_combo_box_append_text (GTK_COMBO_BOX (rotate_selector), "180");
-	gtk_combo_box_append_text (GTK_COMBO_BOX (rotate_selector), "270");
-#endif
 	gtk_combo_box_set_active (GTK_COMBO_BOX (rotate_selector), 0);
 	g_signal_connect (G_OBJECT (rotate_selector), "changed",
 			  G_CALLBACK (pgd_render_rotate_selector_changed),
@@ -395,35 +309,6 @@
 	gtk_box_pack_start (GTK_BOX (hbox), rotate_hbox, FALSE, TRUE, 0);
 	gtk_widget_show (rotate_hbox);
 
-	mode_hbox = gtk_hbox_new (FALSE, 6);
-
-	label = gtk_label_new ("Mode:");
-	gtk_box_pack_start (GTK_BOX (mode_hbox), label, TRUE, TRUE, 0);
-	gtk_widget_show (label);
-
-#if GTK_CHECK_VERSION (2, 24, 0)
-	mode_selector = gtk_combo_box_text_new ();
-	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (mode_selector), "cairo");
-#ifdef POPPLER_WITH_GDK
-	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (mode_selector), "pixbuf");
-#endif
-#else // ! GTK_CHECK_VERSION (2, 24, 0)
-	mode_selector = gtk_combo_box_new_text ();
-	gtk_combo_box_append_text (GTK_COMBO_BOX (mode_selector), "cairo");
-#ifdef POPPLER_WITH_GDK
-	gtk_combo_box_append_text (GTK_COMBO_BOX (mode_selector), "pixbuf");
-#endif
-#endif // GTK_CHECK_VERSION (2, 24, 0)
-	gtk_combo_box_set_active (GTK_COMBO_BOX (mode_selector), 0);
-	g_signal_connect (G_OBJECT (mode_selector), "changed",
-			  G_CALLBACK (pgd_render_mode_selector_changed),
-			  (gpointer)demo);
-	gtk_box_pack_start (GTK_BOX (mode_hbox), mode_selector, TRUE, TRUE, 0);
-	gtk_widget_show (mode_selector);
-
-	gtk_box_pack_start (GTK_BOX (hbox), mode_hbox, FALSE, TRUE, 0);
-	gtk_widget_show (mode_hbox);
-
 	printing_selector = gtk_check_button_new_with_label ("Printing");
 	g_signal_connect (printing_selector, "toggled",
 			  G_CALLBACK (pgd_render_printing_selector_changed),
@@ -431,11 +316,11 @@
 	gtk_box_pack_start (GTK_BOX (hbox), printing_selector, FALSE, TRUE, 0);
 	gtk_widget_show (printing_selector);
 
-	hbox = gtk_hbox_new (FALSE, 12);
+	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
 	gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
 	gtk_widget_show (hbox);
 
-	slice_hbox = gtk_hbox_new (FALSE, 6);
+	slice_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 
 	label = gtk_label_new ("x:");
 	gtk_box_pack_start (GTK_BOX (slice_hbox), label, TRUE, TRUE, 0);
@@ -451,8 +336,8 @@
 	gtk_box_pack_start (GTK_BOX (hbox), slice_hbox, FALSE, TRUE, 0);
 	gtk_widget_show (slice_hbox);
 
-	slice_hbox = gtk_hbox_new (FALSE, 6);
-	
+	slice_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+
 	label = gtk_label_new ("y:");
 	gtk_box_pack_start (GTK_BOX (slice_hbox), label, TRUE, TRUE, 0);
 	gtk_widget_show (label);
@@ -466,9 +351,9 @@
 
 	gtk_box_pack_start (GTK_BOX (hbox), slice_hbox, FALSE, TRUE, 0);
 	gtk_widget_show (slice_hbox);
-	
-	slice_hbox = gtk_hbox_new (FALSE, 6);
-	
+
+	slice_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+
 	label = gtk_label_new ("width:");
 	gtk_box_pack_start (GTK_BOX (slice_hbox), label, TRUE, TRUE, 0);
 	gtk_widget_show (label);
@@ -482,9 +367,9 @@
 
 	gtk_box_pack_start (GTK_BOX (hbox), slice_hbox, FALSE, TRUE, 0);
 	gtk_widget_show (slice_hbox);
-	
-	slice_hbox = gtk_hbox_new (FALSE, 6);
-	
+
+	slice_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+
 	label = gtk_label_new ("height:");
 	gtk_box_pack_start (GTK_BOX (slice_hbox), label, TRUE, TRUE, 0);
 	gtk_widget_show (label);
@@ -528,17 +413,17 @@
 	demo->doc = g_object_ref (document);
 	demo->scale = 1.0;
 
-	vbox = gtk_vbox_new (FALSE, 6);
+	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
 
 	hbox = pgd_render_properties_selector_create (demo);
 	gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 6);
 	gtk_widget_show (hbox);
 
 	demo->darea = gtk_drawing_area_new ();
-	g_signal_connect (G_OBJECT (demo->darea), "expose_event",
-			  G_CALLBACK (pgd_render_drawing_area_expose),
+	g_signal_connect (G_OBJECT (demo->darea), "draw",
+			  G_CALLBACK (pgd_render_drawing_area_draw),
 			  (gpointer)demo);
-	
+
 	demo->swindow = gtk_scrolled_window_new (NULL, NULL);
 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (demo->swindow),
 					GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
diff --git a/glib/demo/selections.c b/glib/demo/selections.c
index bfd9070..4ec5d6b 100644
--- a/glib/demo/selections.c
+++ b/glib/demo/selections.c
@@ -77,7 +77,6 @@
 		cairo_region_destroy (demo->selected_region);
 		demo->selected_region = NULL;
 	}
-	gtk_widget_set_sensitive(demo->copy_button, FALSE);
 }
 
 static void
@@ -172,7 +171,7 @@
 	gdk_window_set_cursor (window, cursor);
 	gdk_flush ();
 	if (cursor)
-		gdk_cursor_unref (cursor);
+		g_object_unref (cursor);
 }
 
 static gboolean
@@ -220,19 +219,13 @@
 }
 
 static gboolean
-pgd_selections_drawing_area_expose (GtkWidget         *area,
-				    GdkEventExpose    *event,
-				    PgdSelectionsDemo *demo)
+pgd_selections_drawing_area_draw (GtkWidget         *area,
+                                  cairo_t           *cr,
+                                  PgdSelectionsDemo *demo)
 {
-	cairo_t *cr;
-
 	if (!demo->surface)
 		return FALSE;
 
-	gdk_window_clear (gtk_widget_get_window (area));
-
-	cr = gdk_cairo_create (gtk_widget_get_window (area));
-
 	cairo_save (cr);
 	cairo_set_source_surface (cr, demo->surface, 0, 0);
 	cairo_paint (cr);
@@ -243,8 +236,6 @@
 		cairo_paint (cr);
 	}
 
-	cairo_destroy (cr);
-
 	return TRUE;
 }
 
@@ -335,7 +326,8 @@
 pgd_selections_drawing_area_realize (GtkWidget         *area,
 				     PgdSelectionsDemo *demo)
 {
-	GtkStyle *style = gtk_widget_get_style (area);
+	GtkStyleContext *style_context = gtk_widget_get_style_context (area);
+        GdkRGBA rgba;
 
 	gtk_widget_add_events (area,
 			       GDK_POINTER_MOTION_HINT_MASK |
@@ -344,10 +336,10 @@
 			       GDK_BUTTON_RELEASE_MASK);
 	g_object_set (area, "has-tooltip", TRUE, NULL);
 
-	gtk_color_button_set_color (GTK_COLOR_BUTTON (demo->fg_color_button),
-				    &style->text[GTK_STATE_SELECTED]);
-	gtk_color_button_set_color (GTK_COLOR_BUTTON (demo->bg_color_button),
-				    &style->base[GTK_STATE_SELECTED]);
+        gtk_style_context_get_color (style_context, GTK_STATE_FLAG_SELECTED, &rgba);
+        gtk_color_button_set_rgba (GTK_COLOR_BUTTON (demo->fg_color_button), &rgba);
+        gtk_style_context_get_background_color (style_context, GTK_STATE_FLAG_SELECTED, &rgba);
+        gtk_color_button_set_rgba (GTK_COLOR_BUTTON (demo->bg_color_button), &rgba);
 }
 
 static gboolean
@@ -401,6 +393,7 @@
 
 	pgd_selections_clear_selections (demo);
 	pgd_selections_update_selection_region (demo);
+        gtk_widget_set_sensitive (demo->copy_button, FALSE);
 
 	if (demo->surface)
 		cairo_surface_destroy (demo->surface);
@@ -463,12 +456,12 @@
 				 GParamSpec        *pspec,
 				 PgdSelectionsDemo *demo)
 {
-	GdkColor color;
+	GdkRGBA color;
 
-	gtk_color_button_get_color (GTK_COLOR_BUTTON (button), &color);
-	demo->glyph_color.red = color.red;
-	demo->glyph_color.green = color.green;
-	demo->glyph_color.blue = color.blue;
+	gtk_color_button_get_rgba (GTK_COLOR_BUTTON (button), &color);
+	demo->glyph_color.red = CLAMP ((guint) (color.red * 65535), 0, 65535);
+	demo->glyph_color.green = CLAMP ((guint) (color.green * 65535), 0, 65535);
+	demo->glyph_color.blue = CLAMP ((guint) (color.blue * 65535), 0, 65535);
 }
 
 static void
@@ -476,12 +469,12 @@
 				 GParamSpec        *pspec,
 				 PgdSelectionsDemo *demo)
 {
-	GdkColor color;
+	GdkRGBA color;
 
-	gtk_color_button_get_color (GTK_COLOR_BUTTON (button), &color);
-	demo->background_color.red = color.red;
-	demo->background_color.green = color.green;
-	demo->background_color.blue = color.blue;
+	gtk_color_button_get_rgba (GTK_COLOR_BUTTON (button), &color);
+	demo->background_color.red = CLAMP ((guint) (color.red * 65535), 0, 65535);
+	demo->background_color.green = CLAMP ((guint) (color.green * 65535), 0, 65535);
+	demo->background_color.blue = CLAMP ((guint) (color.blue * 65535), 0, 65535);
 }
 
 GtkWidget *
@@ -499,13 +492,13 @@
 
 	n_pages = poppler_document_get_n_pages (demo->doc);
 
-	vbox = gtk_vbox_new (FALSE, 6);
+	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
 
-	hbox = gtk_hbox_new (FALSE, 12);
+	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
 	gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
 	gtk_widget_show (hbox);
 
-	page_hbox = gtk_hbox_new (FALSE, 6);
+	page_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 
 	label = gtk_label_new ("Page:");
 	gtk_box_pack_start (GTK_BOX (page_hbox), label, TRUE, TRUE, 0);
@@ -527,7 +520,7 @@
 	gtk_box_pack_start (GTK_BOX (hbox), page_hbox, FALSE, TRUE, 0);
 	gtk_widget_show (page_hbox);
 
-	scale_hbox = gtk_hbox_new (FALSE, 6);
+	scale_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 
 	label = gtk_label_new ("Scale:");
 	gtk_box_pack_start (GTK_BOX (scale_hbox), label, TRUE, TRUE, 0);
@@ -544,25 +537,17 @@
 	gtk_box_pack_start (GTK_BOX (hbox), scale_hbox, FALSE, TRUE, 0);
 	gtk_widget_show (scale_hbox);
 
-	rotate_hbox = gtk_hbox_new (FALSE, 6);
+	rotate_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 
 	label = gtk_label_new ("Rotate:");
 	gtk_box_pack_start (GTK_BOX (rotate_hbox), label, TRUE, TRUE, 0);
 	gtk_widget_show (label);
 
-#if GTK_CHECK_VERSION (2, 24, 0)
 	rotate_selector = gtk_combo_box_text_new ();
 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (rotate_selector), "0");
 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (rotate_selector), "90");
 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (rotate_selector), "180");
 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (rotate_selector), "270");
-#else
-	rotate_selector = gtk_combo_box_new_text ();
-	gtk_combo_box_append_text (GTK_COMBO_BOX (rotate_selector), "0");
-	gtk_combo_box_append_text (GTK_COMBO_BOX (rotate_selector), "90");
-	gtk_combo_box_append_text (GTK_COMBO_BOX (rotate_selector), "180");
-	gtk_combo_box_append_text (GTK_COMBO_BOX (rotate_selector), "270");
-#endif
 	gtk_combo_box_set_active (GTK_COMBO_BOX (rotate_selector), 0);
 #if 0
 	g_signal_connect (G_OBJECT (rotate_selector), "changed",
@@ -575,11 +560,11 @@
 	gtk_box_pack_start (GTK_BOX (hbox), rotate_hbox, FALSE, TRUE, 0);
 	gtk_widget_show (rotate_hbox);
 
-	hbox = gtk_hbox_new (FALSE, 12);
+	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
 	gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
 	gtk_widget_show (hbox);
 
-	color_hbox = gtk_hbox_new (FALSE, 6);
+	color_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 
 	label = gtk_label_new ("Foreground Color:");
 	gtk_box_pack_start (GTK_BOX (color_hbox), label, TRUE, TRUE, 0);
@@ -595,7 +580,7 @@
 	gtk_box_pack_start (GTK_BOX (hbox), color_hbox, FALSE, TRUE, 0);
 	gtk_widget_show (color_hbox);
 
-	color_hbox = gtk_hbox_new (FALSE, 6);
+	color_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 
 	label = gtk_label_new ("Background Color:");
 	gtk_box_pack_start (GTK_BOX (color_hbox), label, TRUE, TRUE, 0);
@@ -643,7 +628,7 @@
 
 	pgd_selections_clear_selections (demo);
 
-	vbox = gtk_vbox_new (FALSE, 6);
+	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
 
 	hbox = pgd_selections_properties_selector_create (demo);
 	gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 6);
@@ -653,8 +638,8 @@
 	g_signal_connect (demo->darea, "realize",
 			  G_CALLBACK (pgd_selections_drawing_area_realize),
 			  (gpointer)demo);
-	g_signal_connect (demo->darea, "expose_event",
-			  G_CALLBACK (pgd_selections_drawing_area_expose),
+	g_signal_connect (demo->darea, "draw",
+			  G_CALLBACK (pgd_selections_drawing_area_draw),
 			  (gpointer)demo);
 	g_signal_connect (demo->darea, "button_press_event",
 			  G_CALLBACK (pgd_selections_drawing_area_button_press),
diff --git a/glib/demo/text.c b/glib/demo/text.c
index 3a7b2cd..b05581d 100644
--- a/glib/demo/text.c
+++ b/glib/demo/text.c
@@ -303,11 +303,11 @@
 
 	n_pages = poppler_document_get_n_pages (document);
 
-	vbox = gtk_vbox_new (FALSE, 12);
-	vbox2 = gtk_vbox_new (FALSE, 12);
+	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+	vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
 	textinfo = gtk_label_new ("TextInfo");
 
-	hbox = gtk_hbox_new (FALSE, 6);
+	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 
 	label = gtk_label_new ("Page:");
 	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
@@ -342,7 +342,7 @@
 	gtk_box_pack_start (GTK_BOX (vbox), demo->timer_label, FALSE, TRUE, 0);
 	gtk_widget_show (demo->timer_label);
 
-	hpaned = gtk_hpaned_new ();
+	hpaned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
 	gtk_paned_set_position (GTK_PANED (hpaned), 300);
 
 	swindow = gtk_scrolled_window_new (NULL, NULL);
@@ -398,7 +398,7 @@
 	gtk_container_add (GTK_CONTAINER (swindow), treeview);
 	gtk_widget_show (treeview);
 
-	gtk_container_add (GTK_CONTAINER (vbox2), swindow);
+        gtk_box_pack_start (GTK_BOX (vbox2), swindow, TRUE, TRUE, 0);
         gtk_widget_show (swindow);
 
         /* Text attributes */
@@ -414,18 +414,18 @@
         gtk_container_add (GTK_CONTAINER (frame), alignment);
         gtk_widget_show (alignment);
 
-        table = gtk_table_new (4, 2, FALSE);
-        gtk_table_set_col_spacings (GTK_TABLE (table), 6);
-        gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+        table = gtk_grid_new ();
+        gtk_grid_set_column_spacing (GTK_GRID (table), 6);
+        gtk_grid_set_row_spacing (GTK_GRID (table), 6);
 
         demo->font_name = gtk_label_new (NULL);
-        pgd_table_add_property_with_custom_widget (GTK_TABLE (table), "<b>Font Name:</b>", demo->font_name, &row);
+        pgd_table_add_property_with_custom_widget (GTK_GRID (table), "<b>Font Name:</b>", demo->font_name, &row);
         demo->font_size = gtk_label_new (NULL);
-        pgd_table_add_property_with_custom_widget (GTK_TABLE (table), "<b>Font Size:</b>", demo->font_size, &row);
+        pgd_table_add_property_with_custom_widget (GTK_GRID (table), "<b>Font Size:</b>", demo->font_size, &row);
         demo->is_underlined = gtk_label_new (NULL);
-        pgd_table_add_property_with_custom_widget (GTK_TABLE (table), "<b>Underlined:</b>", demo->is_underlined, &row);
+        pgd_table_add_property_with_custom_widget (GTK_GRID (table), "<b>Underlined:</b>", demo->is_underlined, &row);
         demo->text_color = gtk_image_new ();
-        pgd_table_add_property_with_custom_widget (GTK_TABLE (table), "<b>Color:</b>", demo->text_color, &row);
+        pgd_table_add_property_with_custom_widget (GTK_GRID (table), "<b>Color:</b>", demo->text_color, &row);
 
         gtk_container_add (GTK_CONTAINER (alignment), table);
         gtk_widget_show (table);
diff --git a/glib/demo/transitions.c b/glib/demo/transitions.c
index b6188b5..e6e2ab4 100644
--- a/glib/demo/transitions.c
+++ b/glib/demo/transitions.c
@@ -275,9 +275,9 @@
 
 	demo->doc = g_object_ref (document);
 
-	vbox = gtk_vbox_new (FALSE, 12);
+	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
 
-	hbox = gtk_hbox_new (FALSE, 6);
+	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 
 	demo->progress = gtk_progress_bar_new ();
 	gtk_progress_bar_set_ellipsize (GTK_PROGRESS_BAR (demo->progress),
diff --git a/glib/demo/utils.c b/glib/demo/utils.c
index 625596c..f1d47c8 100644
--- a/glib/demo/utils.c
+++ b/glib/demo/utils.c
@@ -24,7 +24,7 @@
 #include "utils.h"
 
 void
-pgd_table_add_property_with_custom_widget (GtkTable    *table,
+pgd_table_add_property_with_custom_widget (GtkGrid     *table,
 					   const gchar *markup,
 					   GtkWidget   *widget,
 					   gint        *row)
@@ -34,19 +34,18 @@
 	label = gtk_label_new (NULL);
 	g_object_set (G_OBJECT (label), "xalign", 0.0, NULL);
 	gtk_label_set_markup (GTK_LABEL (label), markup);
-	gtk_table_attach (GTK_TABLE (table), label, 0, 1, *row, *row + 1,
-			  GTK_FILL, GTK_FILL, 0, 0);
+	gtk_grid_attach (GTK_GRID (table), label, 0, *row, 1, 1);
 	gtk_widget_show (label);
 
-	gtk_table_attach (GTK_TABLE (table), widget, 1, 2, *row, *row + 1,
-			  GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+	gtk_grid_attach (GTK_GRID (table), widget, 1, *row, 1, 1);
+        gtk_widget_set_hexpand (widget, TRUE);
 	gtk_widget_show (widget);
 
 	*row += 1;
 }
 
 void
-pgd_table_add_property_with_value_widget (GtkTable    *table,
+pgd_table_add_property_with_value_widget (GtkGrid     *table,
 					  const gchar *markup,
 					  GtkWidget  **value_widget,
 					  const gchar *value,
@@ -64,7 +63,7 @@
 }
 
 void
-pgd_table_add_property (GtkTable    *table,
+pgd_table_add_property (GtkGrid     *table,
 			const gchar *markup,
 			const gchar *value,
 			gint        *row)
@@ -93,7 +92,7 @@
 
 static void
 pgd_action_view_add_destination (GtkWidget   *action_view,
-				 GtkTable    *table,
+				 GtkGrid     *table,
 				 PopplerDest *dest,
 				 gboolean     remote,
 				 gint        *row)
@@ -165,13 +164,13 @@
 				alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
 				gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 5, 5, 12, 5);
 				
-				new_table = gtk_table_new (8, 2, FALSE);
-				gtk_table_set_col_spacings (GTK_TABLE (new_table), 6);
-				gtk_table_set_row_spacings (GTK_TABLE (new_table), 6);
-				gtk_table_attach_defaults (table, alignment, 0, 2, *row, *row + 1);
+				new_table = gtk_grid_new ();
+				gtk_grid_set_column_spacing (GTK_GRID (new_table), 6);
+				gtk_grid_set_row_spacing (GTK_GRID (new_table), 6);
+				gtk_grid_attach (GTK_GRID(table), alignment, 0, *row, 1, 1);
 				gtk_widget_show (alignment);
 				
-				pgd_action_view_add_destination (action_view, GTK_TABLE (new_table),
+				pgd_action_view_add_destination (action_view, GTK_GRID (new_table),
 								 new_dest, FALSE, &new_row);
 				poppler_dest_free (new_dest);
 
@@ -329,68 +328,68 @@
 	if (!action)
 		return;
 
-	table = gtk_table_new (10, 2, FALSE);
-	gtk_table_set_col_spacings (GTK_TABLE (table), 6);
-	gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+	table = gtk_grid_new ();
+	gtk_grid_set_column_spacing (GTK_GRID (table), 6);
+	gtk_grid_set_row_spacing (GTK_GRID (table), 6);
 
-	pgd_table_add_property (GTK_TABLE (table), "<b>Title:</b>", action->any.title, &row);
+	pgd_table_add_property (GTK_GRID (table), "<b>Title:</b>", action->any.title, &row);
 	
 	switch (action->type) {
 	case POPPLER_ACTION_UNKNOWN:
-		pgd_table_add_property (GTK_TABLE (table), "<b>Type:</b>", "Unknown", &row);
+		pgd_table_add_property (GTK_GRID (table), "<b>Type:</b>", "Unknown", &row);
 		break;
 	case POPPLER_ACTION_NONE:
-		pgd_table_add_property (GTK_TABLE (table), "<b>Type:</b>", "None", &row);
+		pgd_table_add_property (GTK_GRID (table), "<b>Type:</b>", "None", &row);
 		break;
 	case POPPLER_ACTION_GOTO_DEST:
-		pgd_action_view_add_destination (action_view, GTK_TABLE (table), action->goto_dest.dest, FALSE, &row);
+		pgd_action_view_add_destination (action_view, GTK_GRID (table), action->goto_dest.dest, FALSE, &row);
 		break;
 	case POPPLER_ACTION_GOTO_REMOTE:
-		pgd_table_add_property (GTK_TABLE (table), "<b>Type:</b>", "Remote Destination", &row);
-		pgd_table_add_property (GTK_TABLE (table), "<b>Filename:</b>", action->goto_remote.file_name, &row);
-		pgd_action_view_add_destination (action_view, GTK_TABLE (table), action->goto_remote.dest, TRUE, &row);
+		pgd_table_add_property (GTK_GRID (table), "<b>Type:</b>", "Remote Destination", &row);
+		pgd_table_add_property (GTK_GRID (table), "<b>Filename:</b>", action->goto_remote.file_name, &row);
+		pgd_action_view_add_destination (action_view, GTK_GRID (table), action->goto_remote.dest, TRUE, &row);
 		break;
 	case POPPLER_ACTION_LAUNCH:
-		pgd_table_add_property (GTK_TABLE (table), "<b>Type:</b>", "Launch", &row);
-		pgd_table_add_property (GTK_TABLE (table), "<b>Filename:</b>", action->launch.file_name, &row);
-		pgd_table_add_property (GTK_TABLE (table), "<b>Params:</b>", action->launch.params, &row);
+		pgd_table_add_property (GTK_GRID (table), "<b>Type:</b>", "Launch", &row);
+		pgd_table_add_property (GTK_GRID (table), "<b>Filename:</b>", action->launch.file_name, &row);
+		pgd_table_add_property (GTK_GRID (table), "<b>Params:</b>", action->launch.params, &row);
 		break;
 	case POPPLER_ACTION_URI:
-		pgd_table_add_property (GTK_TABLE (table), "<b>Type:</b>", "External URI", &row);
-		pgd_table_add_property (GTK_TABLE (table), "<b>URI</b>", action->uri.uri, &row);
+		pgd_table_add_property (GTK_GRID (table), "<b>Type:</b>", "External URI", &row);
+		pgd_table_add_property (GTK_GRID (table), "<b>URI</b>", action->uri.uri, &row);
 		break;
 	case POPPLER_ACTION_NAMED:
-		pgd_table_add_property (GTK_TABLE (table), "<b>Type:</b>", "Named Action", &row);
-		pgd_table_add_property (GTK_TABLE (table), "<b>Name:</b>", action->named.named_dest, &row);
+		pgd_table_add_property (GTK_GRID (table), "<b>Type:</b>", "Named Action", &row);
+		pgd_table_add_property (GTK_GRID (table), "<b>Name:</b>", action->named.named_dest, &row);
 		break;
 	case POPPLER_ACTION_MOVIE: {
 		GtkWidget *movie_view = pgd_movie_view_new ();
 
-		pgd_table_add_property (GTK_TABLE (table), "<b>Type:</b>", "Movie", &row);
-		pgd_table_add_property (GTK_TABLE (table), "<b>Operation:</b>", get_movie_op (action->movie.operation), &row);
+		pgd_table_add_property (GTK_GRID (table), "<b>Type:</b>", "Movie", &row);
+		pgd_table_add_property (GTK_GRID (table), "<b>Operation:</b>", get_movie_op (action->movie.operation), &row);
 		pgd_movie_view_set_movie (movie_view, action->movie.movie);
-		pgd_table_add_property_with_custom_widget (GTK_TABLE (table), "<b>Movie:</b>", movie_view, &row);
+		pgd_table_add_property_with_custom_widget (GTK_GRID (table), "<b>Movie:</b>", movie_view, &row);
 	}
 		break;
 	case POPPLER_ACTION_RENDITION: {
 		gchar *text;
 
-		pgd_table_add_property (GTK_TABLE (table), "<b>Type:</b>", "Rendition", &row);
+		pgd_table_add_property (GTK_GRID (table), "<b>Type:</b>", "Rendition", &row);
 		text = g_strdup_printf ("%d", action->rendition.op);
-		pgd_table_add_property (GTK_TABLE (table), "<b>Operation:</b>", text, &row);
+		pgd_table_add_property (GTK_GRID (table), "<b>Operation:</b>", text, &row);
 		g_free (text);
 		if (action->rendition.media) {
 			gboolean   embedded = poppler_media_is_embedded (action->rendition.media);
 			GtkWidget *button;
 
-			pgd_table_add_property (GTK_TABLE (table), "<b>Embedded:</b>", embedded ? "Yes": "No", &row);
+			pgd_table_add_property (GTK_GRID (table), "<b>Embedded:</b>", embedded ? "Yes": "No", &row);
 			if (embedded) {
 				const gchar *mime_type = poppler_media_get_mime_type (action->rendition.media);
-				pgd_table_add_property (GTK_TABLE (table), "<b>Mime type:</b>",
+				pgd_table_add_property (GTK_GRID (table), "<b>Mime type:</b>",
 							mime_type ? mime_type : "",
 							&row);
 			} else {
-				pgd_table_add_property (GTK_TABLE (table), "<b>Filename:</b>",
+				pgd_table_add_property (GTK_GRID (table), "<b>Filename:</b>",
 							poppler_media_get_filename (action->rendition.media),
 							&row);
 			}
@@ -399,7 +398,7 @@
 			g_signal_connect (button, "clicked",
 					  G_CALLBACK (pgd_action_view_play_rendition),
 					  action->rendition.media);
-			pgd_table_add_property_with_custom_widget (GTK_TABLE (table), NULL, button, &row);
+			pgd_table_add_property_with_custom_widget (GTK_GRID (table), NULL, button, &row);
 			gtk_widget_show (button);
 		}
 	}
@@ -408,7 +407,7 @@
 		GList     *l;
 		GtkWidget *button;
 
-		pgd_table_add_property (GTK_TABLE (table), "<b>Type:</b>", "OCGState", &row);
+		pgd_table_add_property (GTK_GRID (table), "<b>Type:</b>", "OCGState", &row);
 
 		for (l = action->ocg_state.state_list; l; l = g_list_next (l)) {
 			PopplerActionLayer *action_layer = (PopplerActionLayer *)l->data;
@@ -426,7 +425,7 @@
 				text = g_strdup_printf ("%d layers Toggle", n_layers);
 				break;
 			}
-			pgd_table_add_property (GTK_TABLE (table), "<b>Action:</b>", text, &row);
+			pgd_table_add_property (GTK_GRID (table), "<b>Action:</b>", text, &row);
 			g_free (text);
 		}
 
@@ -434,7 +433,7 @@
 		g_signal_connect (button, "clicked",
 				  G_CALLBACK (pgd_action_view_do_action_layer),
 				  action->ocg_state.state_list);
-		pgd_table_add_property_with_custom_widget (GTK_TABLE (table), NULL, button, &row);
+		pgd_table_add_property_with_custom_widget (GTK_GRID (table), NULL, button, &row);
 		gtk_widget_show (button);
 	}
 		break;
@@ -443,7 +442,7 @@
                 GtkWidget     *textview;
                 GtkWidget     *swindow;
 
-                pgd_table_add_property (GTK_TABLE (table), "<b>Type:</b>", "JavaScript", &row);
+                pgd_table_add_property (GTK_GRID (table), "<b>Type:</b>", "JavaScript", &row);
 
                 buffer = gtk_text_buffer_new (NULL);
                 if (action->javascript.script)
@@ -459,7 +458,7 @@
                 gtk_container_add (GTK_CONTAINER (swindow), textview);
                 gtk_widget_show (textview);
 
-                pgd_table_add_property_with_custom_widget (GTK_TABLE (table), NULL, swindow, &row);
+                pgd_table_add_property_with_custom_widget (GTK_GRID (table), NULL, swindow, &row);
                 gtk_widget_show (swindow);
         }
                 break;
@@ -565,19 +564,19 @@
 	if (!movie)
 		return;
 
-	table = gtk_table_new (10, 2, FALSE);
-	gtk_table_set_col_spacings (GTK_TABLE (table), 6);
-	gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+	table = gtk_grid_new ();
+	gtk_grid_set_column_spacing (GTK_GRID (table), 6);
+	gtk_grid_set_row_spacing (GTK_GRID (table), 6);
 
-	pgd_table_add_property (GTK_TABLE (table), "<b>Filename:</b>", poppler_movie_get_filename (movie), &row);
-	pgd_table_add_property (GTK_TABLE (table), "<b>Need Poster:</b>", poppler_movie_need_poster (movie) ? "Yes" : "No", &row);
-	pgd_table_add_property (GTK_TABLE (table), "<b>Show Controls:</b>", poppler_movie_show_controls (movie) ? "Yes" : "No", &row);
+	pgd_table_add_property (GTK_GRID (table), "<b>Filename:</b>", poppler_movie_get_filename (movie), &row);
+	pgd_table_add_property (GTK_GRID (table), "<b>Need Poster:</b>", poppler_movie_need_poster (movie) ? "Yes" : "No", &row);
+	pgd_table_add_property (GTK_GRID (table), "<b>Show Controls:</b>", poppler_movie_show_controls (movie) ? "Yes" : "No", &row);
 
 	button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_PLAY);
 	g_signal_connect (button, "clicked",
 			  G_CALLBACK (pgd_movie_view_play_movie),
 			  movie);
-	pgd_table_add_property_with_custom_widget (GTK_TABLE (table), NULL, button, &row);
+	pgd_table_add_property_with_custom_widget (GTK_GRID (table), NULL, button, &row);
 	gtk_widget_show (button);
 
 	gtk_container_add (GTK_CONTAINER (alignment), table);
diff --git a/glib/demo/utils.h b/glib/demo/utils.h
index 452f3fa..026cfb4 100644
--- a/glib/demo/utils.h
+++ b/glib/demo/utils.h
@@ -24,16 +24,16 @@
 
 G_BEGIN_DECLS
 
-void       pgd_table_add_property                    (GtkTable        *table,
+void       pgd_table_add_property                    (GtkGrid         *table,
 						      const gchar     *markup,
 						      const gchar     *value,
 						      gint            *row);
-void       pgd_table_add_property_with_value_widget  (GtkTable        *table,
+void       pgd_table_add_property_with_value_widget  (GtkGrid         *table,
 						      const gchar     *markup,
 						      GtkWidget      **value_widget,
 						      const gchar     *value,
 						      gint            *row);
-void       pgd_table_add_property_with_custom_widget (GtkTable       *table,
+void       pgd_table_add_property_with_custom_widget (GtkGrid        *table,
 						      const gchar    *markup,
 						      GtkWidget      *widget,
 						      gint           *row);
diff --git a/glib/poppler-annot.cc b/glib/poppler-annot.cc
index 2a544f1..728e64b 100644
--- a/glib/poppler-annot.cc
+++ b/glib/poppler-annot.cc
@@ -114,12 +114,27 @@
 G_DEFINE_TYPE (PopplerAnnotMovie, poppler_annot_movie, POPPLER_TYPE_ANNOT)
 G_DEFINE_TYPE (PopplerAnnotScreen, poppler_annot_screen, POPPLER_TYPE_ANNOT)
 
+static PopplerAnnot *
+_poppler_create_annot (GType annot_type, Annot *annot)
+{
+  PopplerAnnot *poppler_annot;
+
+  poppler_annot = POPPLER_ANNOT (g_object_new (annot_type, NULL));
+  poppler_annot->annot = annot;
+  annot->incRefCnt();
+
+  return poppler_annot;
+}
+
 static void
 poppler_annot_finalize (GObject *object)
 {
   PopplerAnnot *poppler_annot = POPPLER_ANNOT (object);
 
-  poppler_annot->annot = NULL;
+  if (poppler_annot->annot) {
+    poppler_annot->annot->decRefCnt();
+    poppler_annot->annot = NULL;
+  }
 
   G_OBJECT_CLASS (poppler_annot_parent_class)->finalize (object);
 }
@@ -140,12 +155,7 @@
 PopplerAnnot *
 _poppler_annot_new (Annot *annot)
 {
-  PopplerAnnot *poppler_annot;
-
-  poppler_annot = POPPLER_ANNOT (g_object_new (POPPLER_TYPE_ANNOT, NULL));
-  poppler_annot->annot = annot;
-
-  return poppler_annot;
+  return _poppler_create_annot (POPPLER_TYPE_ANNOT, annot);
 }
 
 static void
@@ -171,12 +181,7 @@
 PopplerAnnot *
 _poppler_annot_text_new (Annot *annot)
 {
-  PopplerAnnot *poppler_annot;
-
-  poppler_annot = POPPLER_ANNOT (g_object_new (POPPLER_TYPE_ANNOT_TEXT, NULL));
-  poppler_annot->annot = annot;
-
-  return poppler_annot;
+  return _poppler_create_annot (POPPLER_TYPE_ANNOT_TEXT, annot);
 }
 
 /**
@@ -218,12 +223,7 @@
 PopplerAnnot *
 _poppler_annot_free_text_new (Annot *annot)
 {
-  PopplerAnnot *poppler_annot;
-
-  poppler_annot = POPPLER_ANNOT (g_object_new (POPPLER_TYPE_ANNOT_FREE_TEXT, NULL));
-  poppler_annot->annot = annot;
-
-  return poppler_annot;
+  return _poppler_create_annot (POPPLER_TYPE_ANNOT_FREE_TEXT, annot);
 }
 
 static void
@@ -239,12 +239,7 @@
 PopplerAnnot *
 _poppler_annot_file_attachment_new (Annot *annot)
 {
-  PopplerAnnot *poppler_annot;
-
-  poppler_annot = POPPLER_ANNOT (g_object_new (POPPLER_TYPE_ANNOT_FILE_ATTACHMENT, NULL));
-  poppler_annot->annot = annot;
-
-  return poppler_annot;
+  return _poppler_create_annot (POPPLER_TYPE_ANNOT_FILE_ATTACHMENT, annot);
 }
 
 
@@ -280,9 +275,7 @@
   PopplerAnnot *poppler_annot;
   AnnotMovie   *annot_movie;
 
-  poppler_annot = POPPLER_ANNOT (g_object_new (POPPLER_TYPE_ANNOT_MOVIE, NULL));
-  poppler_annot->annot = annot;
-
+  poppler_annot = _poppler_create_annot (POPPLER_TYPE_ANNOT_MOVIE, annot);
   annot_movie = static_cast<AnnotMovie *>(poppler_annot->annot);
   POPPLER_ANNOT_MOVIE (poppler_annot)->movie = _poppler_movie_new (annot_movie->getMovie());
 
@@ -322,9 +315,7 @@
   AnnotScreen  *annot_screen;
   LinkAction   *action;
 
-  poppler_annot = POPPLER_ANNOT (g_object_new (POPPLER_TYPE_ANNOT_SCREEN, NULL));
-  poppler_annot->annot = annot;
-
+  poppler_annot = _poppler_create_annot (POPPLER_TYPE_ANNOT_SCREEN, annot);
   annot_screen = static_cast<AnnotScreen *>(poppler_annot->annot);
   action = annot_screen->getAction();
   if (action)
diff --git a/glib/poppler-cached-file-loader.cc b/glib/poppler-cached-file-loader.cc
new file mode 100644
index 0000000..bf462b6
--- /dev/null
+++ b/glib/poppler-cached-file-loader.cc
@@ -0,0 +1,108 @@
+/* poppler-cached-file-loader.h: glib interface to poppler
+ *
+ * Copyright (C) 2012 Carlos Garcia Campos <carlosgc@gnome.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 "config.h"
+#include "poppler-cached-file-loader.h"
+
+PopplerCachedFileLoader::PopplerCachedFileLoader(GInputStream *inputStreamA, GCancellable *cancellableA, goffset lengthA)
+{
+  inputStream = (GInputStream *)g_object_ref(inputStreamA);
+  cancellable = cancellableA ? (GCancellable *)g_object_ref(cancellableA) : NULL;
+  length = lengthA;
+  url = NULL;
+  cachedFile = NULL;
+}
+
+PopplerCachedFileLoader::~PopplerCachedFileLoader()
+{
+  g_object_unref(inputStream);
+  if (cancellable)
+    g_object_unref(cancellable);
+}
+
+size_t PopplerCachedFileLoader::init(GooString *urlA, CachedFile *cachedFileA)
+{
+  size_t size;
+  gssize bytesRead;
+  char buf[CachedFileChunkSize];
+
+  url = urlA;
+  cachedFile = cachedFileA;
+
+  if (length != (goffset)-1)
+    return length;
+
+  if (G_IS_FILE_INPUT_STREAM(inputStream)) {
+    GFileInfo *info;
+
+    info = g_file_input_stream_query_info(G_FILE_INPUT_STREAM (inputStream), G_FILE_ATTRIBUTE_STANDARD_SIZE, cancellable, NULL);
+    if (!info) {
+      error(errInternal, -1, "Failed to get size of '{0:t}'.", urlA);
+      return (size_t)-1;
+    }
+
+    length = g_file_info_get_size(info);
+    g_object_unref(info);
+
+    return length;
+  }
+
+  // Unknown stream length, read the whole stream and return the size.
+  CachedFileWriter writer = CachedFileWriter(cachedFile, NULL);
+  size = 0;
+  do {
+    bytesRead = g_input_stream_read(inputStream, buf, CachedFileChunkSize, cancellable, NULL);
+    if (bytesRead == -1)
+      break;
+
+    writer.write(buf, bytesRead);
+    size += bytesRead;
+  } while (bytesRead > 0);
+
+  return size;
+}
+
+int PopplerCachedFileLoader::load(const std::vector<ByteRange> &ranges, CachedFileWriter *writer)
+{
+  char buf[CachedFileChunkSize];
+  gssize bytesRead;
+  size_t rangeBytesRead, bytesToRead, size;
+
+  if (length == (goffset)-1)
+    return 0;
+
+  size = 0;
+  for (size_t i = 0; i < ranges.size(); i++) {
+    bytesToRead = MIN(CachedFileChunkSize, ranges[i].length);
+    rangeBytesRead = 0;
+    g_seekable_seek(G_SEEKABLE(inputStream), ranges[i].offset, G_SEEK_SET, cancellable, NULL);
+    do {
+      bytesRead = g_input_stream_read(inputStream, buf, bytesToRead, cancellable, NULL);
+      if (bytesRead == -1)
+        return -1;
+
+      writer->write(buf, bytesRead);
+      size += bytesRead;
+      rangeBytesRead += bytesRead;
+      bytesToRead = ranges[i].length - rangeBytesRead;
+    } while (bytesRead > 0 && bytesToRead > 0);
+  }
+
+  return 0;
+}
diff --git a/glib/poppler-cached-file-loader.h b/glib/poppler-cached-file-loader.h
new file mode 100644
index 0000000..3660435
--- /dev/null
+++ b/glib/poppler-cached-file-loader.h
@@ -0,0 +1,44 @@
+/* poppler-cached-file-loader.h: glib interface to poppler
+ *
+ * Copyright (C) 2012 Carlos Garcia Campos <carlosgc@gnome.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_CACHED_FILE_LOADER_H__
+#define __POPPLER_CACHED_FILE_LOADER_H__
+
+#include <gio/gio.h>
+#ifndef __GI_SCANNER__
+#include <CachedFile.h>
+
+class PopplerCachedFileLoader: public CachedFileLoader {
+public:
+  PopplerCachedFileLoader(GInputStream* inputStreamA, GCancellable *cancellableA, goffset lengthA = -1);
+  ~PopplerCachedFileLoader();
+  size_t init(GooString *url, CachedFile* cachedFile);
+  int load(const std::vector<ByteRange> &ranges, CachedFileWriter *writer);
+
+private:
+  GInputStream *inputStream;
+  GCancellable *cancellable;
+  goffset length;
+  GooString *url;
+  CachedFile *cachedFile;
+};
+
+#endif /* __GI_SCANNER__ */
+
+#endif /* __POPPLER_CACHED_FILE_LOADER_H__ */
diff --git a/glib/poppler-document.cc b/glib/poppler-document.cc
index 74d7635..5670300 100644
--- a/glib/poppler-document.cc
+++ b/glib/poppler-document.cc
@@ -38,6 +38,8 @@
 #include "poppler.h"
 #include "poppler-private.h"
 #include "poppler-enums.h"
+#include "poppler-input-stream.h"
+#include "poppler-cached-file-loader.h"
 
 /**
  * SECTION:poppler-document
@@ -245,6 +247,117 @@
   return _poppler_document_new_from_pdfdoc (newDoc, error);
 }
 
+static inline gboolean
+stream_is_memory_buffer_or_local_file (GInputStream *stream)
+{
+  return G_IS_MEMORY_INPUT_STREAM(stream) ||
+    (G_IS_FILE_INPUT_STREAM(stream) && strcmp(g_type_name_from_instance((GTypeInstance*)stream), "GLocalFileInputStream") == 0);
+}
+
+/**
+ * poppler_document_new_from_stream:
+ * @stream: a #GInputStream to read from
+ * @length: the stream length, or -1 if not known
+ * @password: (allow-none): password to unlock the file with, or %NULL
+ * @cancellable: (allow-none): a #GCancellable, or %NULL
+ * @error: (allow-none): Return location for an error, or %NULL
+ *
+ * Creates a new #PopplerDocument reading the PDF contents from @stream.
+ * Note that the given #GInputStream must be seekable or %G_IO_ERROR_NOT_SUPPORTED
+ * will be returned.
+ * Possible errors include those in the #POPPLER_ERROR and #G_FILE_ERROR
+ * domains.
+ *
+ * Returns: (transfer full): a new #PopplerDocument, or %NULL
+ *
+ * Since: 0.22
+ */
+PopplerDocument *
+poppler_document_new_from_stream (GInputStream *stream,
+                                  goffset       length,
+                                  const char   *password,
+                                  GCancellable *cancellable,
+                                  GError      **error)
+{
+  Object obj;
+  PDFDoc *newDoc;
+  BaseStream *str;
+  GooString *password_g;
+
+  g_return_val_if_fail(G_IS_INPUT_STREAM(stream), NULL);
+  g_return_val_if_fail(length == (goffset)-1 || length > 0, NULL);
+
+  if (!globalParams) {
+    globalParams = new GlobalParams();
+  }
+
+  if (!G_IS_SEEKABLE(stream) || !g_seekable_can_seek(G_SEEKABLE(stream))) {
+    g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                        "Stream is not seekable");
+    return NULL;
+  }
+
+  obj.initNull();
+  if (stream_is_memory_buffer_or_local_file(stream)) {
+    str = new PopplerInputStream(stream, cancellable, 0, gFalse, 0, &obj);
+  } else {
+    CachedFile *cachedFile = new CachedFile(new PopplerCachedFileLoader(stream, cancellable, length), new GooString());
+    str = new CachedFileStream(cachedFile, 0, gFalse, cachedFile->getLength(), &obj);
+  }
+
+  password_g = poppler_password_to_latin1(password);
+  newDoc = new PDFDoc(str, password_g, password_g);
+  delete password_g;
+
+  return _poppler_document_new_from_pdfdoc (newDoc, error);
+}
+
+/**
+ * poppler_document_new_from_gfile:
+ * @file: a #GFile to load
+ * @password: (allow-none): password to unlock the file with, or %NULL
+ * @cancellable: (allow-none): a #GCancellable, or %NULL
+ * @error: (allow-none): Return location for an error, or %NULL
+ *
+ * Creates a new #PopplerDocument reading the PDF contents from @file.
+ * Possible errors include those in the #POPPLER_ERROR and #G_FILE_ERROR
+ * domains.
+ *
+ * Returns: (transfer full): a new #PopplerDocument, or %NULL
+ *
+ * Since: 0.22
+ */
+PopplerDocument *
+poppler_document_new_from_gfile (GFile        *file,
+                                 const char   *password,
+                                 GCancellable *cancellable,
+                                 GError      **error)
+{
+  PopplerDocument *document;
+  GFileInputStream *stream;
+
+  g_return_val_if_fail(G_IS_FILE(file), NULL);
+
+  if (g_file_is_native(file)) {
+    gchar *uri;
+
+    uri = g_file_get_uri(file);
+    document = poppler_document_new_from_file(uri, password, error);
+    g_free(uri);
+
+    return document;
+  }
+
+  stream = g_file_read(file, cancellable, error);
+  if (!stream)
+    return NULL;
+
+  document = poppler_document_new_from_stream(G_INPUT_STREAM(stream), -1, password, cancellable, error);
+  g_object_unref(stream);
+
+  return document;
+}
+
 static gboolean
 handle_save_error (int      err_code,
 		   GError **error)
diff --git a/glib/poppler-document.h b/glib/poppler-document.h
index 7051830..a34e88c 100644
--- a/glib/poppler-document.h
+++ b/glib/poppler-document.h
@@ -20,6 +20,7 @@
 #define __POPPLER_DOCUMENT_H__
 
 #include <glib-object.h>
+#include <gio/gio.h>
 #include "poppler.h"
 
 G_BEGIN_DECLS
@@ -172,6 +173,15 @@
 							    int              length,
 							    const char      *password,
 							    GError         **error);
+PopplerDocument   *poppler_document_new_from_stream        (GInputStream    *stream,
+                                                            goffset          length,
+                                                            const char      *password,
+                                                            GCancellable    *cancellable,
+                                                            GError         **error);
+PopplerDocument   *poppler_document_new_from_gfile         (GFile           *file,
+                                                            const char      *password,
+                                                            GCancellable    *cancellable,
+                                                            GError         **error);
 gboolean           poppler_document_save                   (PopplerDocument *document,
 							    const char      *uri,
 							    GError         **error);
diff --git a/glib/poppler-input-stream.cc b/glib/poppler-input-stream.cc
new file mode 100644
index 0000000..99fcb6f
--- /dev/null
+++ b/glib/poppler-input-stream.cc
@@ -0,0 +1,141 @@
+/* poppler-input-stream.cc: glib interface to poppler
+ *
+ * Copyright (C) 2012 Carlos Garcia Campos <carlosgc@gnome.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 "config.h"
+#include "poppler-input-stream.h"
+
+PopplerInputStream::PopplerInputStream(GInputStream *inputStreamA, GCancellable *cancellableA,
+                                       Guint startA, GBool limitedA, Guint lengthA, Object *dictA)
+  : BaseStream(dictA, lengthA)
+{
+  inputStream = (GInputStream *)g_object_ref(inputStreamA);
+  cancellable = cancellableA ? (GCancellable *)g_object_ref(cancellableA) : NULL;
+  start = startA;
+  limited = limitedA;
+  length = lengthA;
+  bufPtr = bufEnd = buf;
+  bufPos = start;
+}
+
+PopplerInputStream::~PopplerInputStream()
+{
+  close();
+  g_object_unref(inputStream);
+  if (cancellable)
+    g_object_unref(cancellable);
+}
+
+Stream *PopplerInputStream::makeSubStream(Guint startA, GBool limitedA,
+                                          Guint lengthA, Object *dictA)
+{
+  return new PopplerInputStream(inputStream, cancellable, startA, limitedA, lengthA, dictA);
+}
+
+void PopplerInputStream::reset()
+{
+  GSeekable *seekable = G_SEEKABLE(inputStream);
+
+  savePos = (Guint)g_seekable_tell(seekable);
+  g_seekable_seek(seekable, start, G_SEEK_SET, cancellable, NULL);
+  saved = gTrue;
+  bufPtr = bufEnd = buf;
+  bufPos = start;
+}
+
+void PopplerInputStream::close()
+{
+  if (!saved)
+    return;
+  g_seekable_seek(G_SEEKABLE(inputStream), savePos, G_SEEK_SET, cancellable, NULL);
+  saved = gFalse;
+}
+
+void PopplerInputStream::setPos(Guint pos, int dir)
+{
+  Guint size;
+  GSeekable *seekable = G_SEEKABLE(inputStream);
+
+  if (dir >= 0) {
+    g_seekable_seek(seekable, pos, G_SEEK_SET, cancellable, NULL);
+  } else {
+    g_seekable_seek(seekable, 0, G_SEEK_END, cancellable, NULL);
+    size = (Guint)g_seekable_tell(seekable);
+
+    if (pos > size)
+      pos = size;
+
+    g_seekable_seek(seekable, -(goffset)pos, G_SEEK_END, cancellable, NULL);
+    bufPos = (Guint)g_seekable_tell(seekable);
+  }
+  bufPtr = bufEnd = buf;
+}
+
+void PopplerInputStream::moveStart(int delta)
+{
+  start += delta;
+  bufPtr = bufEnd = buf;
+  bufPos = start;
+}
+
+GBool PopplerInputStream::fillBuf()
+{
+  int n;
+
+  bufPos += bufEnd - buf;
+  bufPtr = bufEnd = buf;
+  if (limited && bufPos >= start + length) {
+    return gFalse;
+  }
+
+  if (limited && bufPos + inputStreamBufSize > start + length) {
+    n = start + length - bufPos;
+  } else {
+    n = inputStreamBufSize;
+  }
+
+  n = g_input_stream_read(inputStream, buf, n, cancellable, NULL);
+  bufEnd = buf + n;
+  if (bufPtr >= bufEnd) {
+    return gFalse;
+  }
+
+  return gTrue;
+}
+
+int PopplerInputStream::getChars(int nChars, Guchar *buffer)
+{
+  int n, m;
+
+  n = 0;
+  while (n < nChars) {
+    if (bufPtr >= bufEnd) {
+      if (!fillBuf()) {
+        break;
+      }
+    }
+    m = (int)(bufEnd - bufPtr);
+    if (m > nChars - n) {
+      m = nChars - n;
+    }
+    memcpy(buffer + n, bufPtr, m);
+    bufPtr += m;
+    n += m;
+  }
+  return n;
+}
diff --git a/glib/poppler-input-stream.h b/glib/poppler-input-stream.h
new file mode 100644
index 0000000..0d795f4
--- /dev/null
+++ b/glib/poppler-input-stream.h
@@ -0,0 +1,74 @@
+/* poppler-input-stream.h: glib interface to poppler
+ *
+ * Copyright (C) 2012 Carlos Garcia Campos <carlosgc@gnome.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_INPUT_STREAM_H__
+#define __POPPLER_INPUT_STREAM_H__
+
+#include <gio/gio.h>
+#ifndef __GI_SCANNER__
+#include <Object.h>
+#include <Stream.h>
+
+#define inputStreamBufSize 1024
+
+class PopplerInputStream: public BaseStream {
+public:
+
+  PopplerInputStream(GInputStream *inputStream, GCancellable *cancellableA,
+                     Guint startA, GBool limitedA, Guint lengthA, Object *dictA);
+  virtual ~PopplerInputStream();
+  virtual Stream *makeSubStream(Guint start, GBool limited,
+                                Guint lengthA, Object *dictA);
+  virtual StreamKind getKind() { return strWeird; }
+  virtual void reset();
+  virtual void close();
+  virtual int getChar()
+    { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
+  virtual int lookChar()
+    { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
+  virtual int getPos() { return bufPos + (bufPtr - buf); }
+  virtual void setPos(Guint pos, int dir = 0);
+  virtual Guint getStart() { return start; }
+  virtual void moveStart(int delta);
+
+  virtual int getUnfilteredChar() { return getChar(); }
+  virtual void unfilteredReset() { reset(); }
+
+private:
+
+  GBool fillBuf();
+
+  virtual GBool hasGetChars() { return true; }
+  virtual int getChars(int nChars, Guchar *buffer);
+
+  GInputStream *inputStream;
+  GCancellable *cancellable;
+  Guint start;
+  GBool limited;
+  char buf[inputStreamBufSize];
+  char *bufPtr;
+  char *bufEnd;
+  Guint bufPos;
+  int savePos;
+  GBool saved;
+};
+
+#endif /* __GI_SCANNER__ */
+
+#endif /* __POPPLER_INPUT_STREAM_H__ */
diff --git a/glib/poppler-page.cc b/glib/poppler-page.cc
index 2e0e44e..b362a62 100644
--- a/glib/poppler-page.cc
+++ b/glib/poppler-page.cc
@@ -852,18 +852,23 @@
 }
 
 /**
- * poppler_page_find_text:
+ * poppler_page_find_text_with_options:
  * @page: a #PopplerPage
  * @text: the text to search for (UTF-8 encoded)
- * 
- * A #GList of rectangles for each occurance of the text on the page.
+ * @options: find options
+ *
+ * Finds @text in @page with the given #PopplerFindFlags options and
+ * returns a #GList of rectangles for each occurance of the text on the page.
  * The coordinates are in PDF points.
- * 
+ *
  * Return value: (element-type PopplerRectangle) (transfer full): a #GList of #PopplerRectangle,
+ *
+ * Since: 0.22
  **/
 GList *
-poppler_page_find_text (PopplerPage *page,
-			const char  *text)
+poppler_page_find_text_with_options (PopplerPage     *page,
+                                     const char      *text,
+                                     PopplerFindFlags options)
 {
   PopplerRectangle *match;
   GList *matches;
@@ -872,6 +877,7 @@
   glong ucs4_len;
   double height;
   TextPage *text_dev;
+  gboolean backwards;
 
   g_return_val_if_fail (POPPLER_IS_PAGE (page), NULL);
   g_return_val_if_fail (text != NULL, NULL);
@@ -880,17 +886,19 @@
 
   ucs4 = g_utf8_to_ucs4_fast (text, -1, &ucs4_len);
   poppler_page_get_size (page, NULL, &height);
-  
+
+  backwards = options & POPPLER_FIND_BACKWARDS;
   matches = NULL;
   xMin = 0;
-  yMin = 0;
+  yMin = backwards ? height : 0;
 
   while (text_dev->findText (ucs4, ucs4_len,
-			     gFalse, gTrue, // startAtTop, stopAtBottom
-			     gFalse, gFalse, // startAtLast, stopAtLast
-			     gFalse, gFalse, // caseSensitive, backwards
-			     gFalse, // wholeWord
-			     &xMin, &yMin, &xMax, &yMax))
+                             gFalse, gTrue, // startAtTop, stopAtBottom
+                             gTrue, gFalse, // startAtLast, stopAtLast
+                             options & POPPLER_FIND_CASE_SENSITIVE,
+                             backwards,
+                             options & POPPLER_FIND_WHOLE_WORDS_ONLY,
+                             &xMin, &yMin, &xMax, &yMax))
     {
       match = poppler_rectangle_new ();
       match->x1 = xMin;
@@ -905,6 +913,24 @@
   return g_list_reverse (matches);
 }
 
+/**
+ * poppler_page_find_text:
+ * @page: a #PopplerPage
+ * @text: the text to search for (UTF-8 encoded)
+ *
+ * Finds @text in @page with the default options (%POPPLER_FIND_DEFAULT) and
+ * returns a #GList of rectangles for each occurance of the text on the page.
+ * The coordinates are in PDF points.
+ *
+ * Return value: (element-type PopplerRectangle) (transfer full): a #GList of #PopplerRectangle,
+ **/
+GList *
+poppler_page_find_text (PopplerPage *page,
+			const char  *text)
+{
+  return poppler_page_find_text_with_options (page, text, POPPLER_FIND_DEFAULT);
+}
+
 static CairoImageOutputDev *
 poppler_page_get_image_output_dev (PopplerPage *page,
 				   GBool (*imgDrawDeviceCbk)(int img_id, void *data),
@@ -1433,6 +1459,25 @@
   page->page->addAnnot (annot->annot);
 }
 
+/**
+ * poppler_page_remove_annot:
+ * @page: a #PopplerPage
+ * @annot: a #PopplerAnnot to remove
+ *
+ * Removes annotation @annot from @page
+ *
+ * Since: 0.22
+ */
+void
+poppler_page_remove_annot (PopplerPage  *page,
+                           PopplerAnnot *annot)
+{
+  g_return_if_fail (POPPLER_IS_PAGE (page));
+  g_return_if_fail (POPPLER_IS_ANNOT (annot));
+
+  page->page->removeAnnot (annot->annot);
+}
+
 /* PopplerRectangle type */
 
 POPPLER_DEFINE_BOXED_TYPE (PopplerRectangle, poppler_rectangle,
@@ -1502,9 +1547,9 @@
 }
 
 static gchar *
-get_font_name_from_word (TextWord *word)
+get_font_name_from_word (TextWord *word, gint word_i)
 {
-  GooString *font_name = word->getFontName();
+  GooString *font_name = word->getFontName(word_i);
   const gchar *name;
   gboolean subset;
   gint i;
@@ -1530,12 +1575,12 @@
  * Allocates a new PopplerTextAttributes with word attributes
  */
 static PopplerTextAttributes *
-poppler_text_attributes_new_from_word (TextWord *word)
+poppler_text_attributes_new_from_word (TextWord *word, gint i)
 {
   PopplerTextAttributes *attrs = poppler_text_attributes_new ();
   gdouble r, g, b;
 
-  attrs->font_name = get_font_name_from_word (word);
+  attrs->font_name = get_font_name_from_word (word, i);
   attrs->font_size = word->getFontSize();
   attrs->is_underlined = word->isUnderlined();
   word->getColor (&r, &g, &b);
@@ -2028,11 +2073,11 @@
 }
 
 static gboolean
-word_text_attributes_equal (TextWord *a, TextWord *b)
+word_text_attributes_equal (TextWord *a, gint ai, TextWord *b, gint bi)
 {
   double ar, ag, ab, br, bg, bb;
 
-  if (!a->getFontInfo()->matches (b->getFontInfo()))
+  if (!a->getFontInfo(ai)->matches (b->getFontInfo(bi)))
     return FALSE;
 
   if (a->getFontSize() != b->getFontSize())
@@ -2082,23 +2127,32 @@
       return NULL;
     }
 
+  TextWord *word, *prev_word = NULL;
+  gint word_i, prev_word_i;
+
   // Calculating each word attributes
   for (i = 0; i < wordlist->getLength (); i++)
     {
-      TextWord *word = wordlist->get (i);
+      word = wordlist->get (i);
 
-      // each char of the word has the same attributes
-      if (i > 0 && word_text_attributes_equal (word, wordlist->get (i - 1))) {
-        attrs = previous;
-      } else {
-        attrs = poppler_text_attributes_new_from_word (word);
-        attrs->start_index = offset;
-        if (previous)
-          previous->end_index--;
-        previous = attrs;
-        attributes = g_list_prepend (attributes, attrs);
-      }
-      offset += word->getLength () + 1;
+      for (word_i = 0; word_i < word->getLength (); word_i++)
+	{
+	  if (prev_word && word_text_attributes_equal (word, word_i, prev_word, prev_word_i)) {
+	    attrs = previous;
+	  } else {
+	    attrs = poppler_text_attributes_new_from_word (word, word_i);
+	    attrs->start_index = offset;
+	    if (previous)
+	      previous->end_index--;
+	    previous = attrs;
+	    attributes = g_list_prepend (attributes, attrs);
+	  }
+	  offset++;
+	  attrs->end_index = offset;
+	  prev_word = word;
+	  prev_word_i = word_i;
+	}
+      offset++;
       attrs->end_index = offset;
     }
   if (attrs)
diff --git a/glib/poppler-page.h b/glib/poppler-page.h
index 7b36843..c081b8c 100644
--- a/glib/poppler-page.h
+++ b/glib/poppler-page.h
@@ -60,6 +60,9 @@
 gboolean               poppler_page_get_thumbnail_size   (PopplerPage        *page,
 							  int                *width,
 							  int                *height);
+GList             *poppler_page_find_text_with_options   (PopplerPage        *page,
+							  const  char        *text,
+							  PopplerFindFlags    options);
 GList     	      *poppler_page_find_text            (PopplerPage        *page,
 							  const  char        *text);
 void                   poppler_page_render_to_ps         (PopplerPage        *page,
@@ -89,6 +92,8 @@
 void                   poppler_page_free_annot_mapping   (GList              *list);
 void                   poppler_page_add_annot            (PopplerPage        *page,
 							  PopplerAnnot       *annot);
+void                   poppler_page_remove_annot         (PopplerPage        *page,
+                                                          PopplerAnnot       *annot);
 void 		      poppler_page_get_crop_box 	 (PopplerPage        *page,
 							  PopplerRectangle   *rect);
 gboolean               poppler_page_get_text_layout      (PopplerPage        *page,
diff --git a/glib/poppler.h b/glib/poppler.h
index 6c2eefd..2d190f3 100644
--- a/glib/poppler.h
+++ b/glib/poppler.h
@@ -153,6 +153,24 @@
   POPPLER_PRINT_ALL               = POPPLER_PRINT_MARKUP_ANNOTS
 } PopplerPrintFlags;
 
+/**
+ * PopplerFindFlags:
+ * @POPPLER_FIND_CASE_SENSITIVE: do case sensitive search
+ * @POPPLER_FIND_BACKWARDS: search backwards
+ * @POPPLER_FIND_WHOLE_WORDS_ONLY: search only whole words
+ *
+ * Flags using while searching text in a page
+ *
+ * Since: 0.22
+ */
+typedef enum /*< flags >*/
+{
+  POPPLER_FIND_DEFAULT          = 0,
+  POPPLER_FIND_CASE_SENSITIVE   = 1 << 0,
+  POPPLER_FIND_BACKWARDS        = 1 << 1,
+  POPPLER_FIND_WHOLE_WORDS_ONLY = 1 << 2
+} PopplerFindFlags;
+
 typedef struct _PopplerDocument            PopplerDocument;
 typedef struct _PopplerIndexIter           PopplerIndexIter;
 typedef struct _PopplerFontsIter           PopplerFontsIter;
diff --git a/glib/reference/poppler-docs.sgml b/glib/reference/poppler-docs.sgml
index d15bd18..a9d5158 100644
--- a/glib/reference/poppler-docs.sgml
+++ b/glib/reference/poppler-docs.sgml
@@ -54,6 +54,10 @@
     <title>Index of new symbols in 0.20</title>
     <xi:include href="xml/api-index-0.20.xml"><xi:fallback /></xi:include>
   </index>
+  <index id="api-index-0-22">
+    <title>Index of new symbols in 0.22</title>
+    <xi:include href="xml/api-index-0.22.xml"><xi:fallback /></xi:include>
+  </index>
 
   <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
 </book>
diff --git a/glib/reference/poppler-sections.txt b/glib/reference/poppler-sections.txt
index 5a6708b..6fb14bc 100644
--- a/glib/reference/poppler-sections.txt
+++ b/glib/reference/poppler-sections.txt
@@ -33,6 +33,7 @@
 poppler_page_selection_region_free
 poppler_page_get_selected_text
 poppler_page_find_text
+poppler_page_find_text_with_options
 poppler_page_get_text
 poppler_page_get_text_layout
 poppler_page_get_text_attributes
@@ -47,6 +48,7 @@
 poppler_page_get_annot_mapping
 poppler_page_free_annot_mapping
 poppler_page_add_annot
+poppler_page_remove_annot
 poppler_rectangle_new
 poppler_rectangle_copy
 poppler_rectangle_free
@@ -115,6 +117,8 @@
 PopplerPermissions
 poppler_document_new_from_file
 poppler_document_new_from_data
+poppler_document_new_from_stream
+poppler_document_new_from_gfile
 poppler_document_save
 poppler_document_save_a_copy
 poppler_document_get_id
@@ -335,6 +339,7 @@
 PopplerBackend
 PopplerColor
 PopplerPrintFlags
+PopplerFindFlags
 poppler_get_backend
 poppler_get_version
 poppler_date_parse
diff --git a/goo/GooString.cc b/goo/GooString.cc
index fc78d90..451a70e 100644
--- a/goo/GooString.cc
+++ b/goo/GooString.cc
@@ -20,6 +20,8 @@
 // Copyright (C) 2007 Jeff Muizelaar <jeff@infidigm.net>
 // Copyright (C) 2008-2011 Albert Astals Cid <aacid@kde.org>
 // Copyright (C) 2011 Kenji Uno <ku@digitaldolphins.jp>
+// Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
+// Copyright (C) 2012 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
@@ -65,29 +67,35 @@
 enum GooStringFormatType {
   fmtIntDecimal,
   fmtIntHex,
+  fmtIntHexUpper,
   fmtIntOctal,
   fmtIntBinary,
   fmtUIntDecimal,
   fmtUIntHex,
+  fmtUIntHexUpper,
   fmtUIntOctal,
   fmtUIntBinary,
   fmtLongDecimal,
   fmtLongHex,
+  fmtLongHexUpper,
   fmtLongOctal,
   fmtLongBinary,
   fmtULongDecimal,
   fmtULongHex,
+  fmtULongHexUpper,
   fmtULongOctal,
   fmtULongBinary,
 #ifdef LLONG_MAX
   fmtLongLongDecimal,
   fmtLongLongHex,
+  fmtLongLongHexUpper,
   fmtLongLongOctal,
   fmtLongLongBinary,
 #endif
 #ifdef ULLONG_MAX
   fmtULongLongDecimal,
   fmtULongLongHex,
+  fmtULongLongHexUpper,
   fmtULongLongOctal,
   fmtULongLongBinary,
 #endif
@@ -101,13 +109,13 @@
 };
 
 static const char *formatStrings[] = {
-  "d", "x", "o", "b", "ud", "ux", "uo", "ub",
-  "ld", "lx", "lo", "lb", "uld", "ulx", "ulo", "ulb",
+  "d", "x", "X", "o", "b", "ud", "ux", "uX", "uo", "ub",
+  "ld", "lx", "lX", "lo", "lb", "uld", "ulx", "ulX", "ulo", "ulb",
 #ifdef LLONG_MAX
-  "lld", "llx", "llo", "llb",
+  "lld", "llx", "llX", "llo", "llb",
 #endif
 #ifdef ULLONG_MAX
-  "ulld", "ullx", "ullo", "ullb",
+  "ulld", "ullx", "ullX", "ullo", "ullb",
 #endif
   "f", "gs", "g",
   "c",
@@ -388,6 +396,7 @@
 	  switch (ft) {
 	  case fmtIntDecimal:
 	  case fmtIntHex:
+	  case fmtIntHexUpper:
 	  case fmtIntOctal:
 	  case fmtIntBinary:
 	  case fmtSpace:
@@ -395,18 +404,21 @@
 	    break;
 	  case fmtUIntDecimal:
 	  case fmtUIntHex:
+	  case fmtUIntHexUpper:
 	  case fmtUIntOctal:
 	  case fmtUIntBinary:
 	    args[argsLen].ui = va_arg(argList, Guint);
 	    break;
 	  case fmtLongDecimal:
 	  case fmtLongHex:
+	  case fmtLongHexUpper:
 	  case fmtLongOctal:
 	  case fmtLongBinary:
 	    args[argsLen].l = va_arg(argList, long);
 	    break;
 	  case fmtULongDecimal:
 	  case fmtULongHex:
+	  case fmtULongHexUpper:
 	  case fmtULongOctal:
 	  case fmtULongBinary:
 	    args[argsLen].ul = va_arg(argList, Gulong);
@@ -414,6 +426,7 @@
 #ifdef LLONG_MAX
 	  case fmtLongLongDecimal:
 	  case fmtLongLongHex:
+	  case fmtLongLongHexUpper:
 	  case fmtLongLongOctal:
 	  case fmtLongLongBinary:
 	    args[argsLen].ll = va_arg(argList, long long);
@@ -422,6 +435,7 @@
 #ifdef ULLONG_MAX
 	  case fmtULongLongDecimal:
 	  case fmtULongLongHex:
+	  case fmtULongLongHexUpper:
 	  case fmtULongLongOctal:
 	  case fmtULongLongBinary:
 	    args[argsLen].ull = va_arg(argList, unsigned long long);
@@ -454,6 +468,10 @@
 	case fmtIntHex:
 	  formatInt(arg.i, buf, sizeof(buf), zeroFill, width, 16, &str, &len);
 	  break;
+	case fmtIntHexUpper:
+	  formatInt(arg.i, buf, sizeof(buf), zeroFill, width, 16, &str, &len,
+		    gTrue);
+	  break;
 	case fmtIntOctal:
 	  formatInt(arg.i, buf, sizeof(buf), zeroFill, width, 8, &str, &len);
 	  break;
@@ -468,6 +486,10 @@
 	  formatUInt(arg.ui, buf, sizeof(buf), zeroFill, width, 16,
 		     &str, &len);
 	  break;
+	case fmtUIntHexUpper:
+	  formatUInt(arg.ui, buf, sizeof(buf), zeroFill, width, 16,
+		     &str, &len, gTrue);
+	  break;
 	case fmtUIntOctal:
 	  formatUInt(arg.ui, buf, sizeof(buf), zeroFill, width, 8, &str, &len);
 	  break;
@@ -480,6 +502,10 @@
 	case fmtLongHex:
 	  formatInt(arg.l, buf, sizeof(buf), zeroFill, width, 16, &str, &len);
 	  break;
+	case fmtLongHexUpper:
+	  formatInt(arg.l, buf, sizeof(buf), zeroFill, width, 16, &str, &len,
+		    gTrue);
+	  break;
 	case fmtLongOctal:
 	  formatInt(arg.l, buf, sizeof(buf), zeroFill, width, 8, &str, &len);
 	  break;
@@ -494,6 +520,10 @@
 	  formatUInt(arg.ul, buf, sizeof(buf), zeroFill, width, 16,
 		     &str, &len);
 	  break;
+	case fmtULongHexUpper:
+	  formatUInt(arg.ul, buf, sizeof(buf), zeroFill, width, 16,
+		     &str, &len, gTrue);
+	  break;
 	case fmtULongOctal:
 	  formatUInt(arg.ul, buf, sizeof(buf), zeroFill, width, 8, &str, &len);
 	  break;
@@ -507,6 +537,10 @@
 	case fmtLongLongHex:
 	  formatInt(arg.ll, buf, sizeof(buf), zeroFill, width, 16, &str, &len);
 	  break;
+	case fmtLongLongHexUpper:
+	  formatInt(arg.ll, buf, sizeof(buf), zeroFill, width, 16, &str, &len,
+		    gTrue);
+	  break;
 	case fmtLongLongOctal:
 	  formatInt(arg.ll, buf, sizeof(buf), zeroFill, width, 8, &str, &len);
 	  break;
@@ -523,6 +557,10 @@
 	  formatUInt(arg.ull, buf, sizeof(buf), zeroFill, width, 16,
 		     &str, &len);
 	  break;
+	case fmtULongLongHexUpper:
+	  formatUInt(arg.ull, buf, sizeof(buf), zeroFill, width, 16,
+		     &str, &len, gTrue);
+	  break;
 	case fmtULongLongOctal:
 	  formatUInt(arg.ull, buf, sizeof(buf), zeroFill, width, 8,
 		     &str, &len);
@@ -595,16 +633,20 @@
   gfree(args);
   return this;
 }
+
+static const char lowerCaseDigits[17] = "0123456789abcdef";
+static const char upperCaseDigits[17] = "0123456789ABCDEF";
+
 #ifdef LLONG_MAX
 void GooString::formatInt(long long x, char *buf, int bufSize,
                           GBool zeroFill, int width, int base,
-                          char **p, int *len) {
+                          char **p, int *len, GBool upperCase) {
 #else
 void GooString::formatInt(long x, char *buf, int bufSize,
                           GBool zeroFill, int width, int base,
-                          char **p, int *len) {
+                          char **p, int *len, GBool upperCase) {
 #endif
-  static char vals[17] = "0123456789abcdef";
+  const char *vals = upperCase ? upperCaseDigits : lowerCaseDigits;
   GBool neg;
   int start, i, j;
 
@@ -636,13 +678,13 @@
 #ifdef ULLONG_MAX
 void GooString::formatUInt(unsigned long long x, char *buf, int bufSize,
                            GBool zeroFill, int width, int base,
-                           char **p, int *len) {
+                           char **p, int *len, GBool upperCase) {
 #else
 void GooString::formatUInt(Gulong x, char *buf, int bufSize,
                            GBool zeroFill, int width, int base,
-                           char **p, int *len) {
+                           char **p, int *len, GBool upperCase) {
 #endif
-  static char vals[17] = "0123456789abcdef";
+  const char *vals = upperCase ? upperCaseDigits : lowerCaseDigits;
   int i, j;
 
   i = bufSize;
@@ -854,7 +896,7 @@
 
 GBool GooString::hasUnicodeMarker(void)
 {
-    return (s[0] & 0xff) == 0xfe && (s[1] & 0xff) == 0xff;
+  return length > 1 && (s[0] & 0xff) == 0xfe && (s[1] & 0xff) == 0xff;
 }
 
 GooString *GooString::sanitizedName(GBool psmode)
diff --git a/goo/GooString.h b/goo/GooString.h
index 23558b0..b24051b 100644
--- a/goo/GooString.h
+++ b/goo/GooString.h
@@ -18,6 +18,7 @@
 // Copyright (C) 2006 Kristian Høgsberg <krh@redhat.com>
 // Copyright (C) 2006 Krzysztof Kowalczyk <kkowalczyk@gmail.com>
 // Copyright (C) 2008-2010, 2012 Albert Astals Cid <aacid@kde.org>
+// Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
 //
 // To see a description of the changes please see the Changelog file that
 // came with your tarball or type make ChangeLog if you are building from git
@@ -80,10 +81,10 @@
   // - <precision> is the number of digits to the right of the decimal
   //   point (for floating point numbers)
   // - <type> is one of:
-  //     d, x, o, b -- int in decimal, hex, octal, binary
-  //     ud, ux, uo, ub -- unsigned int
-  //     ld, lx, lo, lb, uld, ulx, ulo, ulb -- long, unsigned long
-  //     lld, llx, llo, llb, ulld, ullx, ullo, ullb
+  //     d, x, X, o, b -- int in decimal, lowercase hex, uppercase hex, octal, binary
+  //     ud, ux, uX, uo, ub -- unsigned int
+  //     ld, lx, lX, lo, lb, uld, ulx, ulX, ulo, ulb -- long, unsigned long
+  //     lld, llx, llX, llo, llb, ulld, ullx, ullX, ullo, ullb
   //         -- long long, unsigned long long
   //     f, g -- double
   //     c -- char
@@ -170,20 +171,20 @@
 #ifdef LLONG_MAX
   static void formatInt(long long x, char *buf, int bufSize,
 			GBool zeroFill, int width, int base,
-			char **p, int *len);
+			char **p, int *len, GBool upperCase = gFalse);
 #else
   static void formatInt(long x, char *buf, int bufSize,
 			GBool zeroFill, int width, int base,
-			char **p, int *len);
+			char **p, int *len, GBool upperCase = gFalse);
 #endif
 #ifdef ULLONG_MAX
   static void formatUInt(unsigned long long x, char *buf, int bufSize,
 			 GBool zeroFill, int width, int base,
-			 char **p, int *len);
+			 char **p, int *len, GBool upperCase = gFalse);
 #else
   static void formatUInt(Gulong x, char *buf, int bufSize,
 			 GBool zeroFill, int width, int base,
-			 char **p, int *len);
+			 char **p, int *len, GBool upperCase = gFalse);
 #endif
   static void formatDouble(double x, char *buf, int bufSize, int prec,
 			   GBool trim, char **p, int *len);
diff --git a/goo/Makefile.am b/goo/Makefile.am
index f4f9730..0764e79 100644
--- a/goo/Makefile.am
+++ b/goo/Makefile.am
@@ -18,7 +18,8 @@
 	TiffWriter.h				\
 	ImgWriter.h				\
 	GooLikely.h				\
-	gstrtod.h
+	gstrtod.h				\
+	grandom.h
 
 endif
 
@@ -59,4 +60,5 @@
 	TiffWriter.cc				\
 	ImgWriter.cc				\
 	gtypes_p.h				\
-	gstrtod.cc
+	gstrtod.cc				\
+	grandom.cc
diff --git a/goo/TiffWriter.cc b/goo/TiffWriter.cc
index f63b245..9c134f9 100644
--- a/goo/TiffWriter.cc
+++ b/goo/TiffWriter.cc
@@ -4,7 +4,8 @@
 //
 // This file is licensed under the GPLv2 or later
 //
-// Copyright (C) 2010 William Bader <williambader@hotmail.com>
+// Copyright (C) 2010, 2012 William Bader <williambader@hotmail.com>
+// Copyright (C) 2012 Albert Astals Cid <aacid@kde.org>
 //
 //========================================================================
 
@@ -126,6 +127,14 @@
     photometric = PHOTOMETRIC_RGB;
     break;
 
+#if SPLASH_CMYK
+  case splashModeCMYK8:
+  case splashModeDeviceN8:
+    samplesperpixel = 4;
+    photometric = PHOTOMETRIC_SEPARATED;
+    break;
+#endif
+
   default:
     fprintf(stderr, "TiffWriter: Mode %d not supported\n", splashMode);
     return false;
@@ -159,6 +168,13 @@
   TIFFSetField(f, TIFFTAG_YRESOLUTION, (double) vDPI);
   TIFFSetField(f, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
 
+#if SPLASH_CMYK
+  if (splashMode == splashModeCMYK8 || splashMode == splashModeDeviceN8) {
+    TIFFSetField(f, TIFFTAG_INKSET, INKSET_CMYK);
+    TIFFSetField(f, TIFFTAG_NUMBEROFINKS, 4);
+  }
+#endif
+
   return true;
 }
 
diff --git a/goo/TiffWriter.h b/goo/TiffWriter.h
index 1f4b5eb..bffd17a 100644
--- a/goo/TiffWriter.h
+++ b/goo/TiffWriter.h
@@ -4,7 +4,7 @@
 //
 // This file is licensed under the GPLv2 or later
 //
-// Copyright (C) 2010 William Bader <williambader@hotmail.com>
+// Copyright (C) 2010, 2012 William Bader <williambader@hotmail.com>
 // Copyright (C) 2011 Albert Astals Cid <aacid@kde.org>
 //
 //========================================================================
@@ -38,6 +38,8 @@
 		bool writePointers(unsigned char **rowPointers, int rowCount);
 		bool writeRow(unsigned char **rowData);
 		
+		bool supportCMYK() { return true; }
+
 		bool close();
 	
 	private:
diff --git a/goo/gfile.cc b/goo/gfile.cc
index 7522424..604c0fa 100644
--- a/goo/gfile.cc
+++ b/goo/gfile.cc
@@ -19,7 +19,7 @@
 // Copyright (C) 2006 Kristian Høgsberg <krh@redhat.com>
 // Copyright (C) 2008 Adam Batkin <adam@batkin.net>
 // Copyright (C) 2008, 2010 Hib Eris <hib@hiberis.nl>
-// Copyright (C) 2009 Albert Astals Cid <aacid@kde.org>
+// Copyright (C) 2009, 2012 Albert Astals Cid <aacid@kde.org>
 // Copyright (C) 2009 Kovid Goyal <kovid@kovidgoyal.net>
 //
 // To see a description of the changes please see the Changelog file that
@@ -60,52 +60,6 @@
 
 //------------------------------------------------------------------------
 
-GooString *getHomeDir() {
-#ifdef VMS
-  //---------- VMS ----------
-  return new GooString("SYS$LOGIN:");
-
-#elif defined(__EMX__) || defined(_WIN32)
-  //---------- OS/2+EMX and Win32 ----------
-  char *s;
-  GooString *ret;
-
-  if ((s = getenv("HOME")))
-    ret = new GooString(s);
-  else
-    ret = new GooString(".");
-  return ret;
-
-#elif defined(ACORN)
-  //---------- RISCOS ----------
-  return new GooString("@");
-
-#elif defined(MACOS)
-  //---------- MacOS ----------
-  return new GooString(":");
-
-#else
-  //---------- Unix ----------
-  char *s;
-  struct passwd *pw;
-  GooString *ret;
-
-  if ((s = getenv("HOME"))) {
-    ret = new GooString(s);
-  } else {
-    if ((s = getenv("USER")))
-      pw = getpwnam(s);
-    else
-      pw = getpwuid(getuid());
-    if (pw)
-      ret = new GooString(pw->pw_dir);
-    else
-      ret = new GooString(".");
-  }
-  return ret;
-#endif
-}
-
 GooString *getCurrentDir() {
   char buf[PATH_MAX+1];
 
@@ -363,88 +317,6 @@
 #endif
 }
 
-GooString *makePathAbsolute(GooString *path) {
-#ifdef VMS
-  //---------- VMS ----------
-  char buf[PATH_MAX+1];
-
-  if (!isAbsolutePath(path->getCString())) {
-    if (getcwd(buf, sizeof(buf))) {
-      path->insert(0, buf);
-    }
-  }
-  return path;
-
-#elif defined(_WIN32)
-  //---------- Win32 ----------
-  char buf[MAX_PATH];
-  char *fp;
-
-  buf[0] = '\0';
-  if (!GetFullPathName(path->getCString(), MAX_PATH, buf, &fp)) {
-    path->clear();
-    return path;
-  }
-  path->clear();
-  path->append(buf);
-  return path;
-
-#elif defined(ACORN)
-  //---------- RISCOS ----------
-  path->insert(0, '@');
-  return path;
-
-#elif defined(MACOS)
-  //---------- MacOS ----------
-  path->del(0, 1);
-  return path;
-
-#else
-  //---------- Unix and OS/2+EMX ----------
-  struct passwd *pw;
-  char buf[PATH_MAX+1];
-  GooString *s;
-  char *p1, *p2;
-  int n;
-
-  if (path->getChar(0) == '~') {
-    if (path->getChar(1) == '/' ||
-#ifdef __EMX__
-	path->getChar(1) == '\\' ||
-#endif
-	path->getLength() == 1) {
-      path->del(0, 1);
-      s = getHomeDir();
-      path->insert(0, s);
-      delete s;
-    } else {
-      p1 = path->getCString() + 1;
-#ifdef __EMX__
-      for (p2 = p1; *p2 && *p2 != '/' && *p2 != '\\'; ++p2) ;
-#else
-      for (p2 = p1; *p2 && *p2 != '/'; ++p2) ;
-#endif
-      if ((n = p2 - p1) > PATH_MAX)
-	n = PATH_MAX;
-      strncpy(buf, p1, n);
-      buf[n] = '\0';
-      if ((pw = getpwnam(buf))) {
-	path->del(0, p2 - p1 + 1);
-	path->insert(0, pw->pw_dir);
-      }
-    }
-  } else if (!isAbsolutePath(path->getCString())) {
-    if (getcwd(buf, sizeof(buf))) {
-#ifndef __EMX__
-      path->insert(0, '/');
-#endif
-      path->insert(0, buf);
-    }
-  }
-  return path;
-#endif
-}
-
 time_t getModTime(char *fileName) {
 #ifdef _WIN32
   //~ should implement this, but it's (currently) only used in xpdf
@@ -545,14 +417,6 @@
 #endif
 }
 
-GBool executeCommand(char *cmd) {
-#ifdef VMS
-  return system(cmd) ? gTrue : gFalse;
-#else
-  return system(cmd) ? gFalse : gTrue;
-#endif
-}
-
 #ifdef WIN32
 GooString *fileNameToUTF8(char *path) {
   GooString *s;
diff --git a/goo/gfile.h b/goo/gfile.h
index 46b8958..cefd679 100644
--- a/goo/gfile.h
+++ b/goo/gfile.h
@@ -16,7 +16,7 @@
 // under GPL version 2 or later
 //
 // Copyright (C) 2006 Kristian Høgsberg <krh@redhat.com>
-// Copyright (C) 2009, 2011 Albert Astals Cid <aacid@kde.org>
+// Copyright (C) 2009, 2011, 2012 Albert Astals Cid <aacid@kde.org>
 // Copyright (C) 2009 Kovid Goyal <kovid@kovidgoyal.net>
 //
 // To see a description of the changes please see the Changelog file that
@@ -71,9 +71,6 @@
 
 //------------------------------------------------------------------------
 
-// Get home directory path.
-extern GooString *getHomeDir();
-
 // Get current directory.
 extern GooString *getCurrentDir();
 
@@ -88,10 +85,6 @@
 // Is this an absolute path or file name?
 extern GBool isAbsolutePath(char *path);
 
-// Make this path absolute by prepending current directory (if path is
-// relative) or prepending user's directory (if path starts with '~').
-extern GooString *makePathAbsolute(GooString *path);
-
 // Get the modification time for <fileName>.  Returns 0 if there is an
 // error.
 extern time_t getModTime(char *fileName);
@@ -104,9 +97,6 @@
 // should be "w" or "wb".  Returns true on success.
 extern GBool openTempFile(GooString **name, FILE **f, const char *mode);
 
-// Execute <command>.  Returns true on success.
-extern GBool executeCommand(char *cmd);
-
 #ifdef WIN32
 // Convert a file name from Latin-1 to UTF-8.
 extern GooString *fileNameToUTF8(char *path);
diff --git a/goo/grandom.cc b/goo/grandom.cc
new file mode 100644
index 0000000..1237175
--- /dev/null
+++ b/goo/grandom.cc
@@ -0,0 +1,70 @@
+/*
+ * grandom.cc
+ *
+ * This file is licensed under the GPLv2 or later
+ *
+ * Pseudo-random number generation
+ *
+ * Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
+ */
+
+#include <config.h>
+#include "grandom.h"
+#include "gtypes.h"
+
+#ifdef HAVE_RAND_R // rand_r backend (POSIX)
+
+static GBool initialized = gFalse;
+
+#include <stdlib.h>
+#include <time.h>
+static unsigned int seed;
+
+static void initialize() {
+  if (!initialized) {
+    seed = time(NULL);
+    initialized = gTrue;
+  }
+}
+
+void grandom_fill(Guchar *buff, int size)
+{
+  initialize();
+  while (size--)
+    *buff++ = rand_r(&seed) % 256;
+}
+
+double grandom_double()
+{
+  initialize();
+  return rand_r(&seed) / (1 + (double)RAND_MAX);
+}
+
+#else // srand+rand backend (unsafe, because it may interfere with the application)
+
+static GBool initialized = gFalse;
+
+#include <stdlib.h>
+#include <time.h>
+
+static void initialize() {
+  if (!initialized) {
+    srand(time(NULL));
+    initialized = gTrue;
+  }
+}
+
+void grandom_fill(Guchar *buff, int size)
+{
+  initialize();
+  while (size--)
+    *buff++ = rand() % 256;
+}
+
+double grandom_double()
+{
+  initialize();
+  return rand() / (1 + (double)RAND_MAX);
+}
+
+#endif
diff --git a/goo/grandom.h b/goo/grandom.h
new file mode 100644
index 0000000..45fa791
--- /dev/null
+++ b/goo/grandom.h
@@ -0,0 +1,34 @@
+/*
+ * grandom.h
+ *
+ * This file is licensed under the GPLv2 or later
+ *
+ * Pseudo-random number generation
+ *
+ * Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
+ */
+
+#ifndef GRANDOM_H
+#define GRANDOM_H
+
+#include "gtypes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Fills the given buffer with random bytes
+ */
+extern void grandom_fill(Guchar *buff, int size);
+
+/*
+ * Returns a random number in [0,1)
+ */
+extern double grandom_double();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/poppler-glib-uninstalled.pc.in b/poppler-glib-uninstalled.pc.in
index 5fcb6f2..5506c4e 100644
--- a/poppler-glib-uninstalled.pc.in
+++ b/poppler-glib-uninstalled.pc.in
@@ -1,7 +1,7 @@
 Name: poppler-glib
 Description: GLib wrapper for poppler - uninstalled
 Version: @VERSION@
-Requires: glib-2.0 >= @GLIB_REQUIRED@ gobject-2.0 >= @GLIB_REQUIRED@ cairo >= @CAIRO_VERSION@
+Requires: glib-2.0 >= @GLIB_REQUIRED@ gobject-2.0 >= @GLIB_REQUIRED@ gio-2.0 >= @GLIB_REQUIRED@ cairo >= @CAIRO_VERSION@
 
 Libs: ${pc_top_builddir}/${pcfiledir}/glib/libpoppler-glib.la
 Cflags: -I${pc_top_builddir}/${pcfiledir}/glib
diff --git a/poppler-glib.pc.in b/poppler-glib.pc.in
index 9ba8978..cd30feb 100644
--- a/poppler-glib.pc.in
+++ b/poppler-glib.pc.in
@@ -6,7 +6,7 @@
 Name: poppler-glib
 Description: GLib wrapper for poppler
 Version: @VERSION@
-Requires: glib-2.0 >= @GLIB_REQUIRED@ gobject-2.0 >= @GLIB_REQUIRED@ cairo >= @CAIRO_VERSION@
+Requires: glib-2.0 >= @GLIB_REQUIRED@ gobject-2.0 >= @GLIB_REQUIRED@ gio-2.0 >= @GLIB_REQUIRED@ cairo >= @CAIRO_VERSION@
 @PC_REQUIRES_PRIVATE@
 
 Libs: -L${libdir} -lpoppler-glib
diff --git a/poppler/Annot.cc b/poppler/Annot.cc
index 675ec84..dae0f62 100644
--- a/poppler/Annot.cc
+++ b/poppler/Annot.cc
@@ -16,7 +16,7 @@
 // Copyright (C) 2006 Scott Turner <scotty1024@mac.com>
 // Copyright (C) 2007, 2008 Julien Rebetez <julienr@svn.gnome.org>
 // Copyright (C) 2007-2012 Albert Astals Cid <aacid@kde.org>
-// Copyright (C) 2007-2011 Carlos Garcia Campos <carlosgc@gnome.org>
+// Copyright (C) 2007-2012 Carlos Garcia Campos <carlosgc@gnome.org>
 // Copyright (C) 2007, 2008 Iñigo Martínez <inigomartinez@gmail.com>
 // Copyright (C) 2007 Jeff Muizelaar <jeff@infidigm.net>
 // Copyright (C) 2008, 2011 Pino Toscano <pino@kde.org>
@@ -26,6 +26,7 @@
 // Copyright (C) 2011 José Aliste <jaliste@src.gnome.org>
 // Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
 // Copyright (C) 2012 Thomas Freitag <Thomas.Freitag@alfa.de>
+// Copyright (C) 2012 Tobias Koenig <tokoe@kdab.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
@@ -196,6 +197,34 @@
   return newRect;
 }
 
+static LinkAction* getAdditionalAction(Annot::AdditionalActionsType type, Object *additionalActions, PDFDoc *doc) {
+  Object additionalActionsObject;
+  LinkAction *linkAction = NULL;
+
+  if (additionalActions->fetch(doc->getXRef(), &additionalActionsObject)->isDict()) {
+    const char *key = (type == Annot::actionCursorEntering ? "E" :
+                       type == Annot::actionCursorLeaving ?  "X" :
+                       type == Annot::actionMousePressed ?   "D" :
+                       type == Annot::actionMouseReleased ?  "U" :
+                       type == Annot::actionFocusIn ?       "Fo" :
+                       type == Annot::actionFocusOut ?      "BI" :
+                       type == Annot::actionPageOpening ?   "PO" :
+                       type == Annot::actionPageClosing ?   "PC" :
+                       type == Annot::actionPageVisible ?   "PV" :
+                       type == Annot::actionPageInvisible ? "PI" : NULL);
+
+    Object actionObject;
+
+    if (additionalActionsObject.dictLookup(key, &actionObject)->isDict())
+      linkAction = LinkAction::parseAction(&actionObject, doc->getCatalog()->getBaseURI());
+    actionObject.free();
+  }
+
+  additionalActionsObject.free();
+
+  return linkAction;
+}
+
 //------------------------------------------------------------------------
 // AnnotBorderEffect
 //------------------------------------------------------------------------
@@ -784,50 +813,38 @@
   appearDict.free();
 }
 
-void AnnotAppearance::getAppearanceStream(AnnotAppearance::AnnotAppearanceType type, const char *state, Object *dest) {
+void AnnotAppearance::getAppearanceStream(AnnotAppearanceType type, const char *state, Object *dest) {
   Object apData, stream;
   apData.initNull();
 
   // Obtain dictionary or stream associated to appearance type
-  if (type == appearRollover) {
-    appearDict.dictLookupNF("R", &apData);
-  } else if (type == appearDown) {
-    appearDict.dictLookupNF("D", &apData);
-  }
-  if (apData.isNull()) { // Normal appearance, also used as fallback
+  switch (type) {
+  case appearRollover:
+    if (appearDict.dictLookupNF("R", &apData)->isNull())
+      appearDict.dictLookupNF("N", &apData);
+    break;
+  case appearDown:
+    if (appearDict.dictLookupNF("D", &apData)->isNull())
+      appearDict.dictLookupNF("N", &apData);
+    break;
+  case appearNormal:
     appearDict.dictLookupNF("N", &apData);
-  }
-
-  // Search state if it's a subdictionary
-  if (apData.isDict() && state) {
-    Object obj1;
-    apData.dictLookupNF(state, &obj1);
-    apData.free();
-    obj1.copy(&apData);
-    obj1.free();
+    break;
   }
 
   dest->initNull();
-  // Sanity check on the value we are about to return: it must be a ref to stream
-  if (apData.isRef()) {
-    apData.fetch(xref, &stream);
-    if (stream.isStream()) {
-      apData.copy(dest);
-    } else {
-      error(errSyntaxWarning, -1, "AP points to a non-stream object");
-    }
-    stream.free();
-  }
+  if (apData.isDict() && state)
+    apData.dictLookupNF(state, dest);
+  else if (apData.isRef())
+    apData.copy(dest);
   apData.free();
 }
 
 GooString * AnnotAppearance::getStateKey(int i) {
   Object obj1;
   GooString * res = NULL;
-  appearDict.dictLookupNF("N", &obj1);
-  if (obj1.isDict()) {
+  if (appearDict.dictLookupNF("N", &obj1)->isDict())
     res = new GooString(obj1.dictGetKey(i));
-  }
   obj1.free();
   return res;
 }
@@ -835,10 +852,8 @@
 int AnnotAppearance::getNumStates() {
   Object obj1;
   int res = 0;
-  appearDict.dictLookupNF("N", &obj1);
-  if (obj1.isDict()) {
+  if (appearDict.dictLookupNF("N", &obj1)->isDict())
     res = obj1.dictGetLength();
-  }
   obj1.free();
   return res;
 }
@@ -1186,6 +1201,7 @@
   }
   obj1.free();
 
+  // Note: This value is overwritten by Annots ctor
   if (dict->lookupNF("P", &obj1)->isRef()) {
     Ref ref = obj1.getRef();
 
@@ -1417,13 +1433,22 @@
   }
 }
 
-void Annot::setPage(Ref *pageRef, int pageIndex)
-{
+void Annot::setPage(int pageIndex, GBool updateP) {
+  Page *pageobj = doc->getPage(pageIndex);
   Object obj1;
 
-  obj1.initRef(pageRef->num, pageRef->gen);
-  update("P", &obj1);
-  page = pageIndex;
+  if (pageobj) {
+    Ref pageRef = pageobj->getRef();
+    obj1.initRef(pageRef.num, pageRef.gen);
+    page = pageIndex;
+  } else {
+    obj1.initNull();
+    page = 0;
+  }
+
+  if (updateP) {
+    update("P", &obj1);
+  }
 }
 
 void Annot::setAppearanceState(const char *state) {
@@ -1485,6 +1510,11 @@
   valueObject.free();
 }
 
+void Annot::removeReferencedObjects() {
+  // Remove appearance streams (if any)
+  invalidateAppearance();
+}
+
 void Annot::incRefCnt() {
   refCnt++;
 }
@@ -1935,6 +1965,18 @@
   update ("CreationDate", &obj1);
 }
 
+void AnnotMarkup::removeReferencedObjects() {
+  Page *pageobj = doc->getPage(page);
+  assert(pageobj != NULL); // We're called when removing an annot from a page
+
+  // Remove popup
+  if (popup) {
+    pageobj->removeAnnot(popup);
+  }
+
+  Annot::removeReferencedObjects();
+}
+
 //------------------------------------------------------------------------
 // AnnotText
 //------------------------------------------------------------------------
@@ -2350,7 +2392,7 @@
     appearBuf->append ("Q\n");
 
     // Force 24x24 rectangle
-    PDFRectangle fixedRect(rect->x1, rect->y1, rect->x1 + 24, rect->y1 + 24);
+    PDFRectangle fixedRect(rect->x1, rect->y2 - 24, rect->x1 + 24, rect->y2);
     appearBBox = new AnnotAppearanceBBox(&fixedRect);
     double bbox[4];
     appearBBox->getBBoxRect(bbox);
@@ -3718,10 +3760,9 @@
   
   if (action)
     delete action;
-    
-  if (additionActions)
-    delete additionActions;
-    
+
+  additionalActions.free();
+
   if (parent)
     delete parent;
 }
@@ -3761,12 +3802,7 @@
   }
   obj1.free();
 
-  if(dict->lookup("AA", &obj1)->isDict()) {
-    additionActions = NULL;
-  } else {
-    additionActions = NULL;
-  }
-  obj1.free();
+  dict->lookupNF("AA", &additionalActions);
 
   if(dict->lookup("Parent", &obj1)->isDict()) {
     parent = NULL;
@@ -3778,6 +3814,11 @@
   updatedAppearanceStream.num = updatedAppearanceStream.gen = -1;
 }
 
+LinkAction* AnnotWidget::getAdditionalAction(AdditionalActionsType type)
+{
+  return ::getAdditionalAction(type, &additionalActions, doc);
+}
+
 // Grand unified handler for preparing text strings to be drawn into form
 // fields.  Takes as input a text string (in PDFDocEncoding or UTF-16).
 // Converts some or all of this string to the appropriate encoding for the
@@ -3894,8 +3935,7 @@
         }
       } else {
         ccToUnicode->decRefCnt();
-        fprintf(stderr,
-                "warning: layoutText: cannot convert U+%04X\n", uChar);
+        error(errSyntaxError, -1, "AnnotWidget::layoutText, cannot convert U+{0:04uX}", uChar);
       }
     }
 
@@ -5140,7 +5180,7 @@
   if (action)
     delete action;
 
-  additionAction.free();
+  additionalActions.free();
 }
 
 void AnnotScreen::initialize(PDFDoc *docA, Dict* dict) {
@@ -5164,14 +5204,21 @@
   }
   obj1.free();
 
-  dict->lookup("AA", &additionAction);
+  dict->lookupNF("AA", &additionalActions);
 
   appearCharacs = NULL;
   if(dict->lookup("MK", &obj1)->isDict()) {
     appearCharacs = new AnnotAppearanceCharacs(obj1.getDict());
   }
   obj1.free();
+}
 
+LinkAction* AnnotScreen::getAdditionalAction(AdditionalActionsType type)
+{
+  if (type == actionFocusIn || type == actionFocusOut) // not defined for screen annotation
+    return NULL;
+
+  return ::getAdditionalAction(type, &additionalActions, doc);
 }
 
 //------------------------------------------------------------------------
@@ -6450,7 +6497,7 @@
 // Annots
 //------------------------------------------------------------------------
 
-Annots::Annots(PDFDoc *docA, Object *annotsObj) {
+Annots::Annots(PDFDoc *docA, int page, Object *annotsObj) {
   Annot *annot;
   Object obj1;
   int i;
@@ -6471,6 +6518,7 @@
         annot = createAnnot (obj1.getDict(), &obj2);
         if (annot) {
           if (annot->isOk()) {
+            annot->setPage(page, gFalse); // Don't change /P
             appendAnnot(annot);
           }
           annot->decRefCnt();
diff --git a/poppler/Annot.h b/poppler/Annot.h
index 7cbc143..68ddeb7 100644
--- a/poppler/Annot.h
+++ b/poppler/Annot.h
@@ -23,6 +23,7 @@
 // Copyright (C) 2008 Tomas Are Haavet <tomasare@gmail.com>
 // Copyright (C) 2009-2011 Albert Astals Cid <aacid@kde.org>
 // Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
+// Copyright (C) 2012 Tobias Koenig <tokoe@kdab.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
@@ -43,6 +44,7 @@
 class CharCodeToUnicode;
 class GfxFont;
 class GfxResources;
+class Page;
 class PDFDoc;
 class Form;
 class FormWidget;
@@ -471,6 +473,8 @@
 //------------------------------------------------------------------------
 
 class Annot {
+  friend class Annots;
+  friend class Page;
 public:
   enum AnnotFlag {
     flagUnknown        = 0x0000,
@@ -515,6 +519,22 @@
     type3D              // 3D             25
   };
 
+  /**
+   * Describes the additional actions of a screen or widget annotation.
+   */
+  enum AdditionalActionsType {
+    actionCursorEntering, ///< Performed when the cursor enters the annotation's active area
+    actionCursorLeaving,  ///< Performed when the cursor exists the annotation's active area
+    actionMousePressed,   ///< Performed when the mouse button is pressed inside the annotation's active area
+    actionMouseReleased,  ///< Performed when the mouse button is released inside the annotation's active area
+    actionFocusIn,        ///< Performed when the annotation receives the input focus
+    actionFocusOut,       ///< Performed when the annotation loses the input focus
+    actionPageOpening,    ///< Performed when the page containing the annotation is opened
+    actionPageClosing,    ///< Performed when the page containing the annotation is closed
+    actionPageVisible,    ///< Performed when the page containing the annotation becomes visible
+    actionPageInvisible   ///< Performed when the page containing the annotation becomes invisible
+  };
+
   Annot(PDFDoc *docA, PDFRectangle *rectA);
   Annot(PDFDoc *docA, Dict *dict);
   Annot(PDFDoc *docA, Dict *dict, Object *obj);
@@ -551,8 +571,6 @@
   // new_color. 
   void setColor(AnnotColor *new_color);
 
-  void setPage(Ref *pageRef, int pageIndex);
-
   void setAppearanceState(const char *state);
 
   // Delete appearance streams and reset appearance state
@@ -587,10 +605,12 @@
   // write vStr[i:j[ in appearBuf
 
   void initialize (PDFDoc *docA, Dict *dict);
+  void setPage (int new_page, GBool updateP); // Called by Page::addAnnot and Annots ctor
 
 
 protected:
   virtual ~Annot();
+  virtual void removeReferencedObjects(); // Called by Page::removeAnnot
   void setColor(AnnotColor *color, GBool fill);
   void drawCircle(double cx, double cy, double r, GBool fill);
   void drawCircleTopLeft(double cx, double cy, double r);
@@ -699,6 +719,8 @@
   void setDate(GooString *new_date);
 
 protected:
+  virtual void removeReferencedObjects();
+
   GooString *label;             // T            (Default autor)
   AnnotPopup *popup;            // Popup
   double opacity;               // CA           (Default 1.0)
@@ -800,7 +822,7 @@
 
   AnnotAppearanceCharacs *getAppearCharacs() { return appearCharacs; }
   LinkAction* getAction() { return action; }
-  Object* getAdditionActions() { return &additionAction; }
+  LinkAction *getAdditionalAction(AdditionalActionsType type);
 
  private:
   void initialize(PDFDoc *docA, Dict *dict);
@@ -811,7 +833,7 @@
   AnnotAppearanceCharacs* appearCharacs; // MK
 
   LinkAction *action;                    // A
-  Object additionAction;                 // AA
+  Object additionalActions;              // AA
 };
 
 //------------------------------------------------------------------------
@@ -1274,7 +1296,7 @@
   AnnotWidgetHighlightMode getMode() { return mode; }
   AnnotAppearanceCharacs *getAppearCharacs() { return appearCharacs; }
   LinkAction *getAction() { return action; }
-  Dict *getAdditionActions() { return additionActions; }
+  LinkAction *getAdditionalAction(AdditionalActionsType type);
   Dict *getParent() { return parent; }
 
 private:
@@ -1293,7 +1315,7 @@
   AnnotWidgetHighlightMode mode;          // H  (Default I)
   AnnotAppearanceCharacs *appearCharacs;  // MK
   LinkAction *action;                     // A
-  Dict *additionActions;                  // AA
+  Object additionalActions;               // AA
   // inherited  from Annot
   // AnnotBorderBS border;                // BS
   Dict *parent;                           // Parent
@@ -1367,8 +1389,8 @@
 class Annots {
 public:
 
-  // Build a list of Annot objects.
-  Annots(PDFDoc *docA, Object *annotsObj);
+  // Build a list of Annot objects and call setPage on them
+  Annots(PDFDoc *docA, int page, Object *annotsObj);
 
   ~Annots();
 
diff --git a/poppler/CairoOutputDev.cc b/poppler/CairoOutputDev.cc
index b70183e..2cd67c9 100644
--- a/poppler/CairoOutputDev.cc
+++ b/poppler/CairoOutputDev.cc
@@ -23,7 +23,7 @@
 // Copyright (C) 2008-2012 Adrian Johnson <ajohnson@redneon.com>
 // Copyright (C) 2008 Michael Vrable <mvrable@cs.ucsd.edu>
 // Copyright (C) 2008, 2009 Chris Wilson <chris@chris-wilson.co.uk>
-// Copyright (C) 2008 Hib Eris <hib@hiberis.nl>
+// Copyright (C) 2008, 2012 Hib Eris <hib@hiberis.nl>
 // Copyright (C) 2009, 2010 David Benjamin <davidben@mit.edu>
 // Copyright (C) 2011, 2012 Thomas Freitag <Thomas.Freitag@alfa.de>
 // Copyright (C) 2012 Patrick Pfeifer <p2000@mailinator.com>
@@ -61,7 +61,7 @@
 #include "CairoOutputDev.h"
 #include "CairoFontEngine.h"
 #include "CairoRescaleBox.h"
-#include "UTF8.h"
+#include "UnicodeMap.h"
 //------------------------------------------------------------------------
 
 // #define LOG_CAIRO
@@ -945,10 +945,21 @@
   fill_pattern = cairo_pattern_create_mesh ();
 
   for (i = 0; i < shading->getNTriangles(); i++) {
-    shading->getTriangle(i,
-			 &x0, &y0, &color[0],
-			 &x1, &y1, &color[1],
-			 &x2, &y2, &color[2]);
+    if (shading->isParameterized()) {
+      double color0, color1, color2;
+      shading->getTriangle(i, &x0, &y0, &color0,
+                              &x1, &y1, &color1,
+                              &x2, &y2, &color2);
+      shading->getParameterizedColor(color0, &color[0]);
+      shading->getParameterizedColor(color1, &color[1]);
+      shading->getParameterizedColor(color2, &color[2]);
+    } else {
+      shading->getTriangle(i,
+                           &x0, &y0, &color[0],
+                           &x1, &y1, &color[1],
+                           &x2, &y2, &color[2]);
+
+    }
 
     cairo_mesh_pattern_begin_patch (fill_pattern);
 
@@ -1169,6 +1180,8 @@
     glyphs[glyphCount].y = y - originY;
     glyphCount++;
     if (use_show_text_glyphs) {
+      GooString enc("UTF-8");
+      UnicodeMap *utf8Map = globalParams->getUnicodeMap(&enc);
       if (utf8Max - utf8Count < uLen*6) {
         // utf8 encoded characters can be up to 6 bytes
 	if (utf8Max > uLen*6)
@@ -1179,7 +1192,7 @@
       }
       clusters[clusterCount].num_bytes = 0;
       for (int i = 0; i < uLen; i++) {
-	int size = mapUTF8 (u[i], utf8 + utf8Count, utf8Max - utf8Count);
+	int size = utf8Map->mapUnicode(u[i], utf8 + utf8Count, utf8Max - utf8Count);
 	utf8Count += size;
 	clusters[clusterCount].num_bytes += size;
       }
diff --git a/poppler/Catalog.cc b/poppler/Catalog.cc
index 0f42356..cf6dff0 100644
--- a/poppler/Catalog.cc
+++ b/poppler/Catalog.cc
@@ -24,6 +24,7 @@
 // Copyright (C) 2008, 2011 Pino Toscano <pino@kde.org>
 // Copyright (C) 2009 Ilya Gorenbein <igorenbein@finjan.com>
 // Copyright (C) 2010 Hib Eris <hib@hiberis.nl>
+// Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
 //
 // To see a description of the changes please see the Changelog file that
 // came with your tarball or type make ChangeLog if you are building from git
@@ -864,6 +865,24 @@
   return &dests;
 }
 
+Catalog::FormType Catalog::getFormType()
+{
+  Object xfa;
+  FormType res = NoForm;
+
+  if (acroForm.isDict()) {
+    acroForm.dictLookup("XFA", &xfa);
+    if (xfa.isStream() || xfa.isArray()) {
+      res = XfaForm;
+    } else {
+      res = AcroForm;
+    }
+    xfa.free();
+  }
+
+  return res;
+}
+
 Form *Catalog::getForm()
 {
   if (!form) {
diff --git a/poppler/Catalog.h b/poppler/Catalog.h
index cdb1f13..ef469ec 100644
--- a/poppler/Catalog.h
+++ b/poppler/Catalog.h
@@ -20,6 +20,7 @@
 // Copyright (C) 2007 Julien Rebetez <julienr@svn.gnome.org>
 // Copyright (C) 2008, 2011 Pino Toscano <pino@kde.org>
 // Copyright (C) 2010 Hib Eris <hib@hiberis.nl>
+// Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
 //
 // To see a description of the changes please see the Changelog file that
 // came with your tarball or type make ChangeLog if you are building from git
@@ -153,6 +154,14 @@
 
   OCGs *getOptContentConfig() { return optContent; }
 
+  enum FormType
+  {
+    NoForm,
+    AcroForm,
+    XfaForm
+  };
+
+  FormType getFormType();
   Form* getForm();
 
   ViewerPreferences *getViewerPreferences();
diff --git a/poppler/CharCodeToUnicode.cc b/poppler/CharCodeToUnicode.cc
index d0e6c7f..16ba6d4 100644
--- a/poppler/CharCodeToUnicode.cc
+++ b/poppler/CharCodeToUnicode.cc
@@ -21,6 +21,7 @@
 // Copyright (C) 2010 William Bader <williambader@hotmail.com>
 // Copyright (C) 2010 Jakub Wilk <ubanus@users.sf.net>
 // Copyright (C) 2012 Thomas Freitag <Thomas.Freitag@alfa.de>
+// Copyright (C) 2012 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
@@ -43,6 +44,7 @@
 #include "GlobalParams.h"
 #include "PSTokenizer.h"
 #include "CharCodeToUnicode.h"
+#include "UTF.h"
 
 //------------------------------------------------------------------------
 
@@ -437,7 +439,7 @@
       for (i = oldLen; i < mapLen; ++i) {
         map[i] = 0;
       }
-	}
+    }
   }
   if (n <= 4) {
     if (!parseHex(uStr, n, &u)) {
@@ -445,6 +447,9 @@
       return;
     }
     map[code] = u + offset;
+    if (!UnicodeIsValid(map[code])) {
+      map[code] = 0xfffd;
+    }
   } else {
     if (sMapLen >= sMapSize) {
       sMapSize = sMapSize + 16;
@@ -453,15 +458,18 @@
     }
     map[code] = 0;
     sMap[sMapLen].c = code;
-    sMap[sMapLen].len = n / 4;
-    sMap[sMapLen].u = (Unicode*)gmallocn(sMap[sMapLen].len, sizeof(Unicode));
-    for (j = 0; j < sMap[sMapLen].len; ++j) {
-      if (!parseHex(uStr + j*4, 4, &sMap[sMapLen].u[j])) {
+    int utf16Len = n / 4;
+    Unicode *utf16 = (Unicode*)gmallocn(utf16Len, sizeof(Unicode));
+    for (j = 0; j < utf16Len; ++j) {
+      if (!parseHex(uStr + j*4, 4, &utf16[j])) {
+	gfree(utf16);
 	error(errSyntaxWarning, -1, "Illegal entry in ToUnicode CMap");
 	return;
       }
     }
-    sMap[sMapLen].u[sMap[sMapLen].len - 1] += offset;
+    utf16[utf16Len - 1] += offset;
+    sMap[sMapLen].len = UTF16toUCS4(utf16, utf16Len, &sMap[sMapLen].u);
+    gfree(utf16);
     ++sMapLen;
   }
 }
@@ -590,7 +598,11 @@
     sMap[i].len = len;
     sMap[i].u = (Unicode*)gmallocn(len, sizeof(Unicode));
     for (j = 0; j < len; ++j) {
-      sMap[i].u[j] = u[j];
+      if (UnicodeIsValid(u[j])) {
+        sMap[i].u[j] = u[j];
+      } else {
+        sMap[i].u[j] = 0xfffd;
+      }
     }
   }
 }
diff --git a/poppler/Decrypt.cc b/poppler/Decrypt.cc
index 24af996..44f6961 100644
--- a/poppler/Decrypt.cc
+++ b/poppler/Decrypt.cc
@@ -17,6 +17,7 @@
 // Copyright (C) 2008, 2010 Albert Astals Cid <aacid@kde.org>
 // Copyright (C) 2009 Matthias Franz <matthias@ktug.or.kr>
 // Copyright (C) 2009 David Benjamin <davidben@mit.edu>
+// Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
 //
 // To see a description of the changes please see the Changelog file that
 // came with your tarball or type make ChangeLog if you are building from git
@@ -31,15 +32,23 @@
 
 #include <string.h>
 #include "goo/gmem.h"
+#include "goo/grandom.h"
 #include "Decrypt.h"
 #include "Error.h"
 
-static void aesKeyExpansion(DecryptAESState *s,
-			    Guchar *objKey, int objKeyLen);
+static void rc4InitKey(Guchar *key, int keyLen, Guchar *state);
+static Guchar rc4DecryptByte(Guchar *state, Guchar *x, Guchar *y, Guchar c);
+
+static GBool aesReadBlock(Stream  *str, Guchar *in, GBool addPadding);
+
+static void aesKeyExpansion(DecryptAESState *s, Guchar *objKey, int objKeyLen, GBool decrypt);
+static void aesEncryptBlock(DecryptAESState *s, Guchar *in);
 static void aesDecryptBlock(DecryptAESState *s, Guchar *in, GBool last);
-static void aes256KeyExpansion(DecryptAES256State *s,
-			       Guchar *objKey, int objKeyLen);
+
+static void aes256KeyExpansion(DecryptAES256State *s, Guchar *objKey, int objKeyLen, GBool decrypt);
+static void aes256EncryptBlock(DecryptAES256State *s, Guchar *in);
 static void aes256DecryptBlock(DecryptAES256State *s, Guchar *in, GBool last);
+
 static void sha256(Guchar *msg, int msgLen, Guchar *hash);
 
 static const Guchar passwordPad[32] = {
@@ -90,7 +99,7 @@
 	memcpy(test + len, ownerKey->getCString() + 40, 8);
 	memcpy(test + len + 8, userKey->getCString(), 48);
 	sha256(test, len + 56, test);
-	aes256KeyExpansion(&state, test, 32);
+	aes256KeyExpansion(&state, test, 32, gTrue);
 	for (i = 0; i < 16; ++i) {
 	  state.cbc[i] = 0;
 	}
@@ -121,7 +130,7 @@
 	memcpy(test, userPassword->getCString(), len);
 	memcpy(test + len, userKey->getCString() + 40, 8);
 	sha256(test, len + 8, test);
-	aes256KeyExpansion(&state, test, 32);
+	aes256KeyExpansion(&state, test, 32, gTrue);
 	for (i = 0; i < 16; ++i) {
 	  state.cbc[i] = 0;
 	}
@@ -269,12 +278,11 @@
 }
 
 //------------------------------------------------------------------------
-// DecryptStream
+// BaseCryptStream
 //------------------------------------------------------------------------
 
-DecryptStream::DecryptStream(Stream *strA, Guchar *fileKey,
-			     CryptAlgorithm algoA, int keyLength,
-			     int objNum, int objGen):
+BaseCryptStream::BaseCryptStream(Stream *strA, Guchar *fileKey, CryptAlgorithm algoA,
+				 int keyLength, int objNum, int objGen):
   FilterStream(strA)
 {
   int i;
@@ -318,32 +326,162 @@
   }
 
   charactersRead = 0;
+  autoDelete = gTrue;
 }
 
-DecryptStream::~DecryptStream() {
-  delete str;
+BaseCryptStream::~BaseCryptStream() {
+  if (autoDelete) {
+    delete str;
+  }
 }
 
-void DecryptStream::reset() {
-  int i;
-
+void BaseCryptStream::reset() {
   charactersRead = 0;
+  nextCharBuff = EOF;
   str->reset();
+}
+
+int BaseCryptStream::getPos() {
+  return charactersRead;
+}
+
+int BaseCryptStream::getChar() {
+  // Read next character and empty the buffer, so that a new character will be read next time
+  int c = lookChar();
+  nextCharBuff = EOF;
+
+  if (c != EOF)
+    charactersRead++;
+  return c;
+}
+
+GBool BaseCryptStream::isBinary(GBool last) {
+  return str->isBinary(last);
+}
+
+void BaseCryptStream::setAutoDelete(GBool val) {
+  autoDelete = val;
+}
+
+//------------------------------------------------------------------------
+// EncryptStream
+//------------------------------------------------------------------------
+
+EncryptStream::EncryptStream(Stream *strA, Guchar *fileKey, CryptAlgorithm algoA,
+			     int keyLength, int objNum, int objGen):
+  BaseCryptStream(strA, fileKey, algoA, keyLength, objNum, objGen)
+{
+  // Fill the CBC initialization vector for AES and AES-256
+  switch (algo) {
+  case cryptAES:
+    grandom_fill(state.aes.cbc, 16);
+    break;
+  case cryptAES256:
+    grandom_fill(state.aes256.cbc, 16);
+    break;
+  default:
+    break;
+  }
+}
+
+EncryptStream::~EncryptStream() {
+}
+
+void EncryptStream::reset() {
+  BaseCryptStream::reset();
+
   switch (algo) {
   case cryptRC4:
     state.rc4.x = state.rc4.y = 0;
     rc4InitKey(objKey, objKeyLength, state.rc4.state);
-    state.rc4.buf = EOF;
     break;
   case cryptAES:
-    aesKeyExpansion(&state.aes, objKey, objKeyLength);
+    aesKeyExpansion(&state.aes, objKey, objKeyLength, gFalse);
+    memcpy(state.aes.buf, state.aes.cbc, 16); // Copy CBC IV to buf
+    state.aes.bufIdx = 0;
+    state.aes.paddingReached = gFalse;
+    break;
+  case cryptAES256:
+    aes256KeyExpansion(&state.aes256, objKey, objKeyLength, gFalse);
+    memcpy(state.aes256.buf, state.aes256.cbc, 16); // Copy CBC IV to buf
+    state.aes256.bufIdx = 0;
+    state.aes256.paddingReached = gFalse;
+    break;
+  }
+}
+
+int EncryptStream::lookChar() {
+  Guchar in[16];
+  int c;
+
+  if (nextCharBuff != EOF)
+    return nextCharBuff;
+
+  c = EOF; // make gcc happy
+  switch (algo) {
+  case cryptRC4:
+    if ((c = str->getChar()) != EOF) {
+      // RC4 is XOR-based: the decryption algorithm works for encryption too
+      c = rc4DecryptByte(state.rc4.state, &state.rc4.x, &state.rc4.y, (Guchar)c);
+    }
+    break;
+  case cryptAES:
+    if (state.aes.bufIdx == 16 && !state.aes.paddingReached) {
+      state.aes.paddingReached = !aesReadBlock(str, in, gTrue);
+      aesEncryptBlock(&state.aes, in);
+    }
+    if (state.aes.bufIdx == 16) {
+      c = EOF;
+    } else {
+      c = state.aes.buf[state.aes.bufIdx++];
+    }
+    break;
+  case cryptAES256:
+    if (state.aes256.bufIdx == 16 && !state.aes256.paddingReached) {
+      state.aes256.paddingReached = !aesReadBlock(str, in, gTrue);
+      aes256EncryptBlock(&state.aes256, in);
+    }
+    if (state.aes256.bufIdx == 16) {
+      c = EOF;
+    } else {
+      c = state.aes256.buf[state.aes256.bufIdx++];
+    }
+    break;
+  }
+  return (nextCharBuff = c);
+}
+
+//------------------------------------------------------------------------
+// DecryptStream
+//------------------------------------------------------------------------
+
+DecryptStream::DecryptStream(Stream *strA, Guchar *fileKey, CryptAlgorithm algoA,
+			     int keyLength, int objNum, int objGen):
+  BaseCryptStream(strA, fileKey, algoA, keyLength, objNum, objGen)
+{
+}
+
+DecryptStream::~DecryptStream() {
+}
+
+void DecryptStream::reset() {
+  int i;
+  BaseCryptStream::reset();
+
+  switch (algo) {
+  case cryptRC4:
+    state.rc4.x = state.rc4.y = 0;
+    rc4InitKey(objKey, objKeyLength, state.rc4.state);
+    break;
+  case cryptAES:
+    aesKeyExpansion(&state.aes, objKey, objKeyLength, gTrue);
     for (i = 0; i < 16; ++i) {
       state.aes.cbc[i] = str->getChar();
     }
     state.aes.bufIdx = 16;
     break;
   case cryptAES256:
-    aes256KeyExpansion(&state.aes256, objKey, objKeyLength);
+    aes256KeyExpansion(&state.aes256, objKey, objKeyLength, gTrue);
     for (i = 0; i < 16; ++i) {
       state.aes256.cbc[i] = str->getChar();
     }
@@ -352,36 +490,25 @@
   }
 }
 
-int DecryptStream::getPos() {
-  return charactersRead;
-}
-
-int DecryptStream::getChar() {
+int DecryptStream::lookChar() {
   Guchar in[16];
-  int c, i;
+  int c;
+
+  if (nextCharBuff != EOF)
+    return nextCharBuff;
 
   c = EOF; // make gcc happy
   switch (algo) {
   case cryptRC4:
-    if (state.rc4.buf == EOF) {
-      c = str->getChar();
-      if (c != EOF) {
-	state.rc4.buf = rc4DecryptByte(state.rc4.state, &state.rc4.x,
-				       &state.rc4.y, (Guchar)c);
-      }
+    if ((c = str->getChar()) != EOF) {
+      c = rc4DecryptByte(state.rc4.state, &state.rc4.x, &state.rc4.y, (Guchar)c);
     }
-    c = state.rc4.buf;
-    state.rc4.buf = EOF;
     break;
   case cryptAES:
     if (state.aes.bufIdx == 16) {
-      for (i = 0; i < 16; ++i) {
-	if ((c = str->getChar()) == EOF) {
-	  return EOF;
-	}
-	in[i] = (Guchar)c;
+      if (aesReadBlock(str, in, gFalse)) {
+        aesDecryptBlock(&state.aes, in, str->lookChar() == EOF);
       }
-      aesDecryptBlock(&state.aes, in, str->lookChar() == EOF);
     }
     if (state.aes.bufIdx == 16) {
       c = EOF;
@@ -391,13 +518,9 @@
     break;
   case cryptAES256:
     if (state.aes256.bufIdx == 16) {
-      for (i = 0; i < 16; ++i) {
-	if ((c = str->getChar()) == EOF) {
-	  return EOF;
-	}
-	in[i] = (Guchar)c;
+      if (aesReadBlock(str, in, gFalse)) {
+        aes256DecryptBlock(&state.aes256, in, str->lookChar() == EOF);
       }
-      aes256DecryptBlock(&state.aes256, in, str->lookChar() == EOF);
     }
     if (state.aes256.bufIdx == 16) {
       c = EOF;
@@ -406,72 +529,14 @@
     }
     break;
   }
-  if (c != EOF)
-    charactersRead++;
-  return c;
-}
-
-int DecryptStream::lookChar() {
-  Guchar in[16];
-  int c, i;
-
-  c = EOF; // make gcc happy
-  switch (algo) {
-  case cryptRC4:
-    if (state.rc4.buf == EOF) {
-      c = str->getChar();
-      if (c != EOF) {
-	state.rc4.buf = rc4DecryptByte(state.rc4.state, &state.rc4.x,
-				       &state.rc4.y, (Guchar)c);
-      }
-    }
-    c = state.rc4.buf;
-    break;
-  case cryptAES:
-    if (state.aes.bufIdx == 16) {
-      for (i = 0; i < 16; ++i) {
-	if ((c = str->getChar()) == EOF) {
-	  return EOF;
-	}
-	in[i] = c;
-      }
-      aesDecryptBlock(&state.aes, in, str->lookChar() == EOF);
-    }
-    if (state.aes.bufIdx == 16) {
-      c = EOF;
-    } else {
-      c = state.aes.buf[state.aes.bufIdx];
-    }
-    break;
-  case cryptAES256:
-    if (state.aes256.bufIdx == 16) {
-      for (i = 0; i < 16; ++i) {
-	if ((c = str->getChar()) == EOF) {
-	  return EOF;
-	}
-	in[i] = c;
-      }
-      aes256DecryptBlock(&state.aes256, in, str->lookChar() == EOF);
-    }
-    if (state.aes256.bufIdx == 16) {
-      c = EOF;
-    } else {
-      c = state.aes256.buf[state.aes256.bufIdx];
-    }
-    break;
-  }
-  return c;
-}
-
-GBool DecryptStream::isBinary(GBool last) {
-  return str->isBinary(last);
+  return (nextCharBuff = c);
 }
 
 //------------------------------------------------------------------------
 // RC4-compatible decryption
 //------------------------------------------------------------------------
 
-void rc4InitKey(Guchar *key, int keyLen, Guchar *state) {
+static void rc4InitKey(Guchar *key, int keyLen, Guchar *state) {
   Guchar index1, index2;
   Guchar t;
   int i;
@@ -492,7 +557,7 @@
   }
 }
 
-Guchar rc4DecryptByte(Guchar *state, Guchar *x, Guchar *y, Guchar c) {
+static Guchar rc4DecryptByte(Guchar *state, Guchar *x, Guchar *y, Guchar c) {
   Guchar x1, y1, tx, ty;
 
   x1 = *x = (*x + 1) % 256;
@@ -508,6 +573,32 @@
 // AES decryption
 //------------------------------------------------------------------------
 
+// Returns gFalse if EOF was reached, gTrue otherwise
+static GBool aesReadBlock(Stream *str, Guchar *in, GBool addPadding)
+{
+  int c, i;
+
+  for (i = 0; i < 16; ++i) {
+    if ((c = str->getChar()) != EOF) {
+      in[i] = (Guchar)c;
+    } else {
+      break;
+    }
+  }
+
+  if (i == 16) {
+    return gTrue;
+  } else {
+    if (addPadding) {
+      c = 16 - i;
+      while (i < 16) {
+        in[i++] = (Guchar)c;
+      }
+    }
+    return gFalse;
+  }
+}
+
 static const Guchar sbox[256] = {
   0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
   0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
@@ -571,6 +662,14 @@
   return ((x << 8) & 0xffffffff) | (x >> 24);
 }
 
+static inline void subBytes(Guchar *state) {
+  int i;
+
+  for (i = 0; i < 16; ++i) {
+    state[i] = sbox[state[i]];
+  }
+}
+
 static inline void invSubBytes(Guchar *state) {
   int i;
 
@@ -579,6 +678,29 @@
   }
 }
 
+static inline void shiftRows(Guchar *state) {
+  Guchar t;
+
+  t = state[4];
+  state[4] = state[5];
+  state[5] = state[6];
+  state[6] = state[7];
+  state[7] = t;
+
+  t = state[8];
+  state[8] = state[10];
+  state[10] = t;
+  t = state[9];
+  state[9] = state[11];
+  state[11] = t;
+
+  t = state[15];
+  state[15] = state[14];
+  state[14] = state[13];
+  state[13] = state[12];
+  state[12] = t;
+}
+
 static inline void invShiftRows(Guchar *state) {
   Guchar t;
 
@@ -602,6 +724,17 @@
   state[15] = t;
 }
 
+// {02} \cdot s
+static inline Guchar mul02(Guchar s) {
+  return (s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1);
+}
+
+// {03} \cdot s
+static inline Guchar mul03(Guchar s) {
+  Guchar s2 = (s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1);
+  return s ^ s2;
+}
+
 // {09} \cdot s
 static inline Guchar mul09(Guchar s) {
   Guchar s2, s4, s8;
@@ -642,6 +775,22 @@
   return s2 ^ s4 ^ s8;
 }
 
+static inline void mixColumns(Guchar *state) {
+  int c;
+  Guchar s0, s1, s2, s3;
+
+  for (c = 0; c < 4; ++c) {
+    s0 = state[c];
+    s1 = state[4+c];
+    s2 = state[8+c];
+    s3 = state[12+c];
+    state[c] =    mul02(s0) ^ mul03(s1) ^ s2 ^ s3;
+    state[4+c] =  s0 ^ mul02(s1) ^ mul03(s2) ^ s3;
+    state[8+c] =  s0 ^ s1 ^ mul02(s2) ^ mul03(s3);
+    state[12+c] = mul03(s0) ^ s1 ^ s2 ^ mul02(s3);
+  }
+}
+
 static inline void invMixColumns(Guchar *state) {
   int c;
   Guchar s0, s1, s2, s3;
@@ -686,7 +835,7 @@
 }
 
 static void aesKeyExpansion(DecryptAESState *s,
-			    Guchar *objKey, int /*objKeyLen*/) {
+			    Guchar *objKey, int /*objKeyLen*/, GBool decrypt) {
   Guint temp;
   int i, round;
 
@@ -703,11 +852,52 @@
     }
     s->w[i] = s->w[i-4] ^ temp;
   }
-  for (round = 1; round <= 9; ++round) {
-    invMixColumnsW(&s->w[round * 4]);
+
+  /* In case of decryption, adjust the key schedule for the equivalent inverse cipher */
+  if (decrypt) {
+    for (round = 1; round <= 9; ++round) {
+      invMixColumnsW(&s->w[round * 4]);
+    }
   }
 }
 
+static void aesEncryptBlock(DecryptAESState *s, Guchar *in) {
+  int c, round;
+
+  // initial state (input is xor'd with previous output because of CBC)
+  for (c = 0; c < 4; ++c) {
+    s->state[c] = in[4*c] ^ s->buf[4*c];
+    s->state[4+c] = in[4*c+1] ^ s->buf[4*c+1];
+    s->state[8+c] = in[4*c+2] ^ s->buf[4*c+2];
+    s->state[12+c] = in[4*c+3] ^ s->buf[4*c+3];
+  }
+
+  // round 0
+  addRoundKey(s->state, &s->w[0]);
+
+  // rounds 1-9
+  for (round = 1; round <= 9; ++round) {
+    subBytes(s->state);
+    shiftRows(s->state);
+    mixColumns(s->state);
+    addRoundKey(s->state, &s->w[round * 4]);
+  }
+
+  // round 10
+  subBytes(s->state);
+  shiftRows(s->state);
+  addRoundKey(s->state, &s->w[10 * 4]);
+
+  for (c = 0; c < 4; ++c) {
+    s->buf[4*c] = s->state[c];
+    s->buf[4*c+1] = s->state[4+c];
+    s->buf[4*c+2] = s->state[8+c];
+    s->buf[4*c+3] = s->state[12+c];
+  }
+
+  s->bufIdx = 0;
+}
+
 static void aesDecryptBlock(DecryptAESState *s, Guchar *in, GBool last) {
   int c, round, n, i;
 
@@ -767,7 +957,7 @@
 //------------------------------------------------------------------------
 
 static void aes256KeyExpansion(DecryptAES256State *s,
-			       Guchar *objKey, int objKeyLen) {
+			       Guchar *objKey, int objKeyLen, GBool decrypt) {
   Guint temp;
   int i, round;
 
@@ -786,11 +976,52 @@
     }
     s->w[i] = s->w[i-8] ^ temp;
   }
-  for (round = 1; round <= 13; ++round) {
-    invMixColumnsW(&s->w[round * 4]);
+
+  /* In case of decryption, adjust the key schedule for the equivalent inverse cipher */
+  if (decrypt) {
+    for (round = 1; round <= 13; ++round) {
+      invMixColumnsW(&s->w[round * 4]);
+    }
   }
 }
 
+static void aes256EncryptBlock(DecryptAES256State *s, Guchar *in) {
+  int c, round;
+
+  // initial state (input is xor'd with previous output because of CBC)
+  for (c = 0; c < 4; ++c) {
+    s->state[c] = in[4*c] ^ s->buf[4*c];
+    s->state[4+c] = in[4*c+1] ^ s->buf[4*c+1];
+    s->state[8+c] = in[4*c+2] ^ s->buf[4*c+2];
+    s->state[12+c] = in[4*c+3] ^ s->buf[4*c+3];
+  }
+
+  // round 0
+  addRoundKey(s->state, &s->w[0]);
+
+  // rounds 1-13
+  for (round = 1; round <= 13; ++round) {
+    subBytes(s->state);
+    shiftRows(s->state);
+    mixColumns(s->state);
+    addRoundKey(s->state, &s->w[round * 4]);
+  }
+
+  // round 14
+  subBytes(s->state);
+  shiftRows(s->state);
+  addRoundKey(s->state, &s->w[14 * 4]);
+
+  for (c = 0; c < 4; ++c) {
+    s->buf[4*c] = s->state[c];
+    s->buf[4*c+1] = s->state[4+c];
+    s->buf[4*c+2] = s->state[8+c];
+    s->buf[4*c+3] = s->state[12+c];
+  }
+
+  s->bufIdx = 0;
+}
+
 static void aes256DecryptBlock(DecryptAES256State *s, Guchar *in, GBool last) {
   int c, round, n, i;
 
diff --git a/poppler/Decrypt.h b/poppler/Decrypt.h
index d947f41..c049f5c 100644
--- a/poppler/Decrypt.h
+++ b/poppler/Decrypt.h
@@ -15,6 +15,7 @@
 //
 // Copyright (C) 2008 Julien Rebetez <julien@fhtagn.net>
 // Copyright (C) 2009 David Benjamin <davidben@mit.edu>
+// Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
 //
 // To see a description of the changes please see the Changelog file that
 // came with your tarball or type make ChangeLog if you are building from git
@@ -63,13 +64,19 @@
 };
 
 //------------------------------------------------------------------------
-// DecryptStream
+// Helper classes
 //------------------------------------------------------------------------
 
+/* DecryptRC4State, DecryptAESState, DecryptAES256State are named like this for
+ * historical reasons, but they're used for encryption too.
+ * In case of decryption, the cbc field in AES and AES-256 contains the previous
+ * input block or the CBC initialization vector (IV) if the stream has just been
+ * reset). In case of encryption, it always contains the IV, whereas the
+ * previous output is kept in buf. The paddingReached field is only used in
+ * case of encryption. */
 struct DecryptRC4State {
   Guchar state[256];
   Guchar x, y;
-  int buf;
 };
 
 struct DecryptAESState {
@@ -77,6 +84,7 @@
   Guchar state[16];
   Guchar cbc[16];
   Guchar buf[16];
+  GBool paddingReached; // encryption only
   int bufIdx;
 };
 
@@ -85,30 +93,32 @@
   Guchar state[16];
   Guchar cbc[16];
   Guchar buf[16];
+  GBool paddingReached; // encryption only
   int bufIdx;
 };
 
-class DecryptStream: public FilterStream {
+class BaseCryptStream : public FilterStream {
 public:
 
-  DecryptStream(Stream *strA, Guchar *fileKey,
-		CryptAlgorithm algoA, int keyLength,
-		int objNum, int objGen);
-  virtual ~DecryptStream();
+  BaseCryptStream(Stream *strA, Guchar *fileKey, CryptAlgorithm algoA,
+                  int keyLength, int objNum, int objGen);
+  virtual ~BaseCryptStream();
   virtual StreamKind getKind() { return strWeird; }
   virtual void reset();
   virtual int getChar();
-  virtual int lookChar();
+  virtual int lookChar() = 0;
   virtual int getPos();
   virtual GBool isBinary(GBool last);
   virtual Stream *getUndecodedStream() { return this; }
+  void setAutoDelete(GBool val);
 
-private:
-
+protected:
   CryptAlgorithm algo;
   int objKeyLength;
   Guchar objKey[32];
   int charactersRead; // so that getPos() can be correct
+  int nextCharBuff;   // EOF means not read yet
+  GBool autoDelete;
 
   union {
     DecryptRC4State rc4;
@@ -116,11 +126,33 @@
     DecryptAES256State aes256;
   } state;
 };
+
+//------------------------------------------------------------------------
+// EncryptStream / DecryptStream
+//------------------------------------------------------------------------
+
+class EncryptStream : public BaseCryptStream {
+public:
+
+  EncryptStream(Stream *strA, Guchar *fileKey, CryptAlgorithm algoA,
+                int keyLength, int objNum, int objGen);
+  ~EncryptStream();
+  virtual void reset();
+  virtual int lookChar();
+};
+
+class DecryptStream : public BaseCryptStream {
+public:
+
+  DecryptStream(Stream *strA, Guchar *fileKey, CryptAlgorithm algoA,
+                int keyLength, int objNum, int objGen);
+  ~DecryptStream();
+  virtual void reset();
+  virtual int lookChar();
+};
  
 //------------------------------------------------------------------------
 
-extern void rc4InitKey(Guchar *key, int keyLen, Guchar *state);
-extern Guchar rc4DecryptByte(Guchar *state, Guchar *x, Guchar *y, Guchar c);
 extern void md5(Guchar *msg, int msgLen, Guchar *digest);
 
 #endif
diff --git a/poppler/Form.cc b/poppler/Form.cc
index 0940932..78c25e3 100644
--- a/poppler/Form.cc
+++ b/poppler/Form.cc
@@ -162,8 +162,6 @@
   type = formButton;
   parent = static_cast<FormFieldButton*>(field);
   onStr = NULL;
-  siblingsID = NULL;
-  numSiblingsID = 0;
 
   Object obj1, obj2;
 
@@ -196,8 +194,6 @@
 
 FormWidgetButton::~FormWidgetButton ()
 {
-  if (siblingsID)
-    gfree(siblingsID);
   delete onStr;
 }
 
@@ -236,12 +232,6 @@
   return onStr ? parent->getState(onStr->getCString()) : gFalse;
 }
 
-void FormWidgetButton::setNumSiblingsID (int i)
-{ 
-  numSiblingsID = i; 
-  siblingsID = (unsigned*)greallocn(siblingsID, numSiblingsID, sizeof(unsigned));
-}
-
 
 FormWidgetText::FormWidgetText (PDFDoc *docA, Object *aobj, unsigned num, Ref ref, FormField *p) :
 	FormWidget(docA, aobj, num, ref, p)
@@ -803,6 +793,8 @@
   Dict* dict = obj.getDict();
   active_child = -1;
   noAllOff = false;
+  siblings = NULL;
+  numSiblings = 0;
   appearanceState.initNull();
 
   Object obj1;
@@ -853,20 +845,31 @@
 }
 #endif
 
+void FormFieldButton::setNumSiblings (int num)
+{ 
+  numSiblings = num; 
+  siblings = (FormFieldButton**)greallocn(siblings, numSiblings, sizeof(FormFieldButton*));
+}
+
 void FormFieldButton::fillChildrenSiblingsID()
 {
   if (!terminal) {
     for(int i=0; i<numChildren; i++) {
-      children[i]->fillChildrenSiblingsID();
-    }
-  } else {
-    for(int i=0; i<numChildren; i++) {
-      FormWidgetButton *btn = static_cast<FormWidgetButton*>(widgets[i]);
-      btn->setNumSiblingsID(numChildren-1);
-      for(int j=0, counter=0; j<numChildren; j++) {
-        if (i == j) continue;
-        btn->setSiblingsID(counter, widgets[j]->getID());
-        counter++;
+      FormFieldButton *child = dynamic_cast<FormFieldButton*>(children[i]);
+      if (child != NULL) {
+        // Fill the siblings of this node childs
+        child->setNumSiblings(numChildren-1);
+        for(int j=0, counter=0; j<numChildren; j++) {
+          FormFieldButton *otherChild = dynamic_cast<FormFieldButton*>(children[j]);
+          if (i == j) continue;
+          if (child == otherChild) continue;
+          child->setSibling(counter, otherChild);
+          counter++;
+        }
+
+        // now call ourselves on the child
+        // to fill its children data
+        child->fillChildrenSiblingsID();
       }
     }
   }
@@ -957,6 +960,8 @@
 FormFieldButton::~FormFieldButton()
 {
   appearanceState.free();
+  if (siblings)
+    gfree(siblings);
 }
 
 //------------------------------------------------------------------------
@@ -1125,44 +1130,70 @@
   }
   obj1.free();
 
-  // find selected items and convert choice's human readable strings to UTF16
-  if (Form::fieldLookup(dict, "V", &obj1)->isString()) {
-    for (int i = 0; i < numChoices; i++) {
-      if (!choices[i].optionName)
-        continue;
-
-      if (choices[i].optionName->cmp(obj1.getString()) == 0)
-        choices[i].selected = true;
-
-      if (!choices[i].optionName->hasUnicodeMarker()) {
-        int len;
-        char* buffer = pdfDocEncodingToUTF16(choices[i].optionName, &len);
-        choices[i].optionName->Set(buffer, len);
-        delete [] buffer;
+  // Find selected items
+  // Note: PDF specs say that /V has precedence over /I, but acroread seems to
+  // do the opposite. We do the same.
+  if (Form::fieldLookup(dict, "I", &obj1)->isArray()) {
+    for (int i = 0; i < obj1.arrayGetLength(); i++) {
+      Object obj2;
+      if (obj1.arrayGet(i, &obj2)->isInt() && obj2.getInt() >= 0 && obj2.getInt() < numChoices) {
+        choices[obj2.getInt()].selected = true;
       }
+      obj2.free();
     }
-  } else if (obj1.isArray()) {
-    for (int i = 0; i < numChoices; i++) {
-      if (!choices[i].optionName)
-        continue;
+  } else {
+    obj1.free();
+    // Note: According to PDF specs, /V should *never* contain the exportVal.
+    // However, if /Opt is an array of (exportVal,optionName) pairs, acroread
+    // seems to expect the exportVal instead of the optionName and so we do too.
+    if (Form::fieldLookup(dict, "V", &obj1)->isString()) {
+      GBool optionFound = gFalse;
 
-      for (int j = 0; j < obj1.arrayGetLength(); j++) {
-        Object obj2;
-
-        obj1.arrayGet(j, &obj2);
-        if (choices[i].optionName->cmp(obj2.getString()) == 0) {
-          choices[i].selected = true;
-          obj2.free();
-          break;
+      for (int i = 0; i < numChoices; i++) {
+        if (choices[i].exportVal) {
+          if (choices[i].exportVal->cmp(obj1.getString()) == 0) {
+            optionFound = gTrue;
+          }
+        } else if (choices[i].optionName) {
+          if (choices[i].optionName->cmp(obj1.getString()) == 0) {
+            optionFound = gTrue;
+          }
         }
-        obj2.free();
+
+        if (optionFound) {
+          choices[i].selected = true;
+          break; // We've determined that this option is selected. No need to keep on scanning
+        }
       }
 
-      if (!choices[i].optionName->hasUnicodeMarker()) {
-        int len;
-        char* buffer = pdfDocEncodingToUTF16(choices[i].optionName, &len);
-        choices[i].optionName->Set(buffer, len);
-        delete [] buffer;
+      // Set custom value if /V doesn't refer to any predefined option and the field is user-editable
+      if (!optionFound && edit) {
+        editedChoice = obj1.getString()->copy();
+      }
+    } else if (obj1.isArray()) {
+      for (int i = 0; i < numChoices; i++) {
+        for (int j = 0; j < obj1.arrayGetLength(); j++) {
+          Object obj2;
+          obj1.arrayGet(j, &obj2);
+          GBool matches = gFalse;
+
+          if (choices[i].exportVal) {
+            if (choices[i].exportVal->cmp(obj2.getString()) == 0) {
+              matches = gTrue;
+            }
+          } else if (choices[i].optionName) {
+            if (choices[i].optionName->cmp(obj2.getString()) == 0) {
+              matches = gTrue;
+            }
+          }
+
+          obj2.free();
+
+          if (matches) {
+            choices[i].selected = true;
+            break; // We've determined that this option is selected. No need to keep on scanning
+          }
+        }
       }
     }
   }
@@ -1188,35 +1219,61 @@
 #endif
 
 void FormFieldChoice::updateSelection() {
-  Object obj1;
+  Object objV, objI, obj1;
+  objI.initNull();
 
-  //this is an editable combo-box with user-entered text
   if (edit && editedChoice) {
-    obj1.initString(editedChoice->copy());
+    // This is an editable combo-box with user-entered text
+    objV.initString(editedChoice->copy());
   } else {
-    int numSelected = getNumSelected();
+    const int numSelected = getNumSelected();
+
+    // Create /I array only if multiple selection is allowed (as per PDF spec)
+    if (multiselect) {
+      objI.initArray(xref);
+    }
+
     if (numSelected == 0) {
-      obj1.initString(new GooString(""));
+      // No options are selected
+      objV.initString(new GooString(""));
     } else if (numSelected == 1) {
+      // Only one option is selected
       for (int i = 0; i < numChoices; i++) {
-        if (choices[i].optionName && choices[i].selected) {
-          obj1.initString(choices[i].optionName->copy());
-          break;
+        if (choices[i].selected) {
+          if (multiselect) {
+            objI.arrayAdd(obj1.initInt(i));
+          }
+
+          if (choices[i].exportVal) {
+            objV.initString(choices[i].exportVal->copy());
+          } else if (choices[i].optionName) {
+            objV.initString(choices[i].optionName->copy());
+          }
+
+          break; // We've just written the selected option. No need to keep on scanning
         }
       }
     } else {
-      obj1.initArray(xref);
+      // More than one option is selected
+      objV.initArray(xref);
       for (int i = 0; i < numChoices; i++) {
-        if (choices[i].optionName && choices[i].selected) {
-          Object obj2;
-          obj2.initString(choices[i].optionName->copy());
-          obj1.arrayAdd(&obj2);
+        if (choices[i].selected) {
+          if (multiselect) {
+            objI.arrayAdd(obj1.initInt(i));
+          }
+
+          if (choices[i].exportVal) {
+            objV.arrayAdd(obj1.initString(choices[i].exportVal->copy()));
+          } else if (choices[i].optionName) {
+            objV.arrayAdd(obj1.initString(choices[i].optionName->copy()));
+          }
         }
       }
     }
   }
 
-  obj.getDict()->set("V", &obj1);
+  obj.getDict()->set("V", &objV);
+  obj.getDict()->set("I", &objI);
   xref->setModifiedObject(&obj, ref);
   updateChildrenAppearance();
 }
@@ -1229,20 +1286,30 @@
 }
 
 void FormFieldChoice::deselectAll () {
+  delete editedChoice;
+  editedChoice = NULL;
+
   unselectAll();
   updateSelection();
 }
 
 void FormFieldChoice::toggle (int i)
 {
+  delete editedChoice;
+  editedChoice = NULL;
+
   choices[i].selected = !choices[i].selected;
   updateSelection();
 }
 
 void FormFieldChoice::select (int i)
 {
+  delete editedChoice;
+  editedChoice = NULL;
+
   if (!multiselect)
     unselectAll();
+
   choices[i].selected = true;
   updateSelection();
 }
diff --git a/poppler/Form.h b/poppler/Form.h
index 4146728..ef67748 100644
--- a/poppler/Form.h
+++ b/poppler/Form.h
@@ -6,7 +6,7 @@
 //
 // Copyright 2006 Julien Rebetez <julienr@svn.gnome.org>
 // Copyright 2007, 2008, 2011 Carlos Garcia Campos <carlosgc@gnome.org>
-// Copyright 2007-2010 Albert Astals Cid <aacid@kde.org>
+// Copyright 2007-2010, 2012 Albert Astals Cid <aacid@kde.org>
 // Copyright 2010 Mark Riedesel <mark@klowner.com>
 // Copyright 2011 Pino Toscano <pino@kde.org>
 // Copyright 2012 Fabio D'Urso <fabiodurso@hotmail.it>
@@ -159,17 +159,7 @@
   void setAppearanceState(const char *state);
   void updateWidgetAppearance();
 
-  void setNumSiblingsID (int i);
-  void setSiblingsID (int i, unsigned id) { siblingsID[i] = id; }
-
-  //For radio buttons, return the IDs of the other radio buttons in the same group
-  unsigned* getSiblingsID () const { return siblingsID; }
-  int getNumSiblingsID () const { return numSiblingsID; }
-
 protected:
-  unsigned* siblingsID; // IDs of dependent buttons (each button of a radio field has all the others buttons
-                        // of the same field in this array)
-  int numSiblingsID;
   GooString *onStr;
   FormFieldButton *parent;
 };
@@ -287,6 +277,7 @@
   GooString *getFullyQualifiedName();
 
   FormWidget* findWidgetByRef (Ref aref);
+  int getNumWidgets() { return terminal ? numChildren : 0; }
   FormWidget *getWidget(int i) { return terminal ? widgets[i] : NULL; }
 
   // only implemented in FormFieldButton
@@ -351,6 +342,13 @@
   char *getAppearanceState() { return appearanceState.isName() ? appearanceState.getName() : NULL; }
 
   void fillChildrenSiblingsID ();
+  
+  void setNumSiblings (int num);
+  void setSibling (int i, FormFieldButton *id) { siblings[i] = id; }
+
+  //For radio buttons, return the fields of the other radio buttons in the same group
+  FormFieldButton* getSibling (int i) const { return siblings[i]; }
+  int getNumSiblings () const { return numSiblings; }
 
 #ifdef DEBUG_FORMS
   void print(int indent = 0);
@@ -360,6 +358,10 @@
 protected:
   void updateState(char *state);
 
+  FormFieldButton** siblings; // IDs of dependent buttons (each button of a radio field has all the others buttons
+                               // of the same field in this array)
+  int numSiblings;
+
   FormButtonType btype;
   int size;
   int active_child; //only used for combo box
diff --git a/poppler/Function.cc b/poppler/Function.cc
index 2c3aa8a..d26aed8 100644
--- a/poppler/Function.cc
+++ b/poppler/Function.cc
@@ -510,6 +510,20 @@
   }
 }
 
+GBool SampledFunction::hasDifferentResultSet(Function *func) {
+  if (func->getType() == 0) {
+    SampledFunction *compTo = (SampledFunction *) func;
+    if (compTo->getSampleNumber() != nSamples)
+      return gTrue;
+    double *compSamples = compTo->getSamples();
+    for (int i = 0; i < nSamples; i++) {
+      if (samples[i] != compSamples[i])
+        return gTrue;
+    }
+  }
+  return gFalse;
+}
+
 //------------------------------------------------------------------------
 // ExponentialFunction
 //------------------------------------------------------------------------
diff --git a/poppler/Function.h b/poppler/Function.h
index a456dfe..25df133 100644
--- a/poppler/Function.h
+++ b/poppler/Function.h
@@ -16,6 +16,7 @@
 // Copyright (C) 2009, 2010 Albert Astals Cid <aacid@kde.org>
 // Copyright (C) 2010 Christian Feuersänger <cfeuersaenger@googlemail.com>
 // Copyright (C) 2011 Andrea Canciani <ranma42@gmail.com>
+// Copyright (C) 2012 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
@@ -79,6 +80,7 @@
   double getRangeMin(int i) { return range[i][0]; }
   double getRangeMax(int i) { return range[i][1]; }
   GBool getHasRange() { return hasRange; }
+  virtual GBool hasDifferentResultSet(Function *func) { return gFalse; }
 
   // Transform an input tuple into an output tuple.
   virtual void transform(double *in, double *out) = 0;
@@ -126,6 +128,7 @@
   virtual int getType() { return 0; }
   virtual void transform(double *in, double *out);
   virtual GBool isOk() { return ok; }
+  virtual GBool hasDifferentResultSet(Function *func);
 
   int getSampleSize(int i) { return sampleSize[i]; }
   double getEncodeMin(int i) { return encode[i][0]; }
@@ -133,6 +136,7 @@
   double getDecodeMin(int i) { return decode[i][0]; }
   double getDecodeMax(int i) { return decode[i][1]; }
   double *getSamples() { return samples; }
+  int getSampleNumber() { return nSamples; }
 
 private:
 
diff --git a/poppler/Gfx.cc b/poppler/Gfx.cc
index 4e663b4..8ee2026 100644
--- a/poppler/Gfx.cc
+++ b/poppler/Gfx.cc
@@ -36,6 +36,7 @@
 // Copyright (C) 2011 Axel Strübing <axel.struebing@freenet.de>
 // Copyright (C) 2012 Even Rouault <even.rouault@mines-paris.org>
 // Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
+// Copyright (C) 2012 Lu Wang <coolwanglu@gmail.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
@@ -4285,7 +4286,7 @@
     obj1.free();
 
     // if drawing is disabled, skip over inline image data
-    if (!ocState) {
+    if (!ocState || !out->needNonText()) {
       str->reset();
       n = height * ((width + 7) / 8);
       for (i = 0; i < n; ++i) {
@@ -4544,7 +4545,7 @@
     }
 
     // if drawing is disabled, skip over inline image data
-    if (!ocState) {
+    if (!ocState || !out->needNonText()) {
       str->reset();
       n = height * ((width * colorMap->getNumPixelComps() *
 		     colorMap->getBits() + 7) / 8);
diff --git a/poppler/GfxState.cc b/poppler/GfxState.cc
index b8cb007..73a3781 100644
--- a/poppler/GfxState.cc
+++ b/poppler/GfxState.cc
@@ -248,6 +248,7 @@
 
 GfxColorSpace::GfxColorSpace() {
   overprintMask = 0x0f;
+  mapping = NULL;
 }
 
 GfxColorSpace::~GfxColorSpace() {
@@ -321,6 +322,10 @@
   return cs;
 }
 
+void GfxColorSpace::createMapping(GooList *separationList, int maxSepComps) {
+  return;
+}
+
 void GfxColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange,
 				     int maxImgPixel) {
   int i;
@@ -354,9 +359,8 @@
     }
     return hp;
   }
-  // try to load from user directory
-  GooString *path = globalParams->getBaseDir();
-  path->append(COLOR_PROFILE_DIR);
+  // try to load from global directory
+  GooString *path = new GooString(GLOBAL_COLOR_PROFILE_DIR);
   path->append(fileName);
   // check if open the file
   if ((fp = fopen(path->getCString(),"r")) != NULL) {
@@ -364,17 +368,6 @@
     hp = cmsOpenProfileFromFile(path->getCString(),"r");
   }
   delete path;
-  if (hp == NULL) {
-    // load from global directory
-    path = new GooString(GLOBAL_COLOR_PROFILE_DIR);
-    path->append(fileName);
-    // check if open the file
-    if ((fp = fopen(path->getCString(),"r")) != NULL) {
-      fclose(fp);
-      hp = cmsOpenProfileFromFile(path->getCString(),"r");
-    }
-    delete path;
-  }
   return hp;
 }
 
@@ -614,6 +607,12 @@
   cmyk->k = clip01(gfxColorComp1 - color->c[0]);
 }
 
+void GfxDeviceGrayColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
+  for (int i = 0; i < gfxColorMaxComps; i++)
+    deviceN->c[i] = 0;
+  deviceN->c[3] = clip01(gfxColorComp1 - color->c[0]);
+}
+
 void GfxDeviceGrayColorSpace::getDefaultColor(GfxColor *color) {
   color->c[0] = 0;
 }
@@ -822,6 +821,17 @@
   cmyk->k = k;
 }
 
+void GfxCalGrayColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
+  GfxCMYK cmyk;
+  for (int i = 0; i < gfxColorMaxComps; i++)
+    deviceN->c[i] = 0;
+  getCMYK(color, &cmyk);
+  deviceN->c[0] = cmyk.c;
+  deviceN->c[1] = cmyk.m;
+  deviceN->c[2] = cmyk.y;
+  deviceN->c[3] = cmyk.k;
+}
+
 void GfxCalGrayColorSpace::getDefaultColor(GfxColor *color) {
   color->c[0] = 0;
 }
@@ -907,6 +917,17 @@
   cmyk->k = k;
 }
 
+void GfxDeviceRGBColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
+  GfxCMYK cmyk;
+  for (int i = 0; i < gfxColorMaxComps; i++)
+    deviceN->c[i] = 0;
+  getCMYK(color, &cmyk);
+  deviceN->c[0] = cmyk.c;
+  deviceN->c[1] = cmyk.m;
+  deviceN->c[2] = cmyk.y;
+  deviceN->c[3] = cmyk.k;
+}
+
 void GfxDeviceRGBColorSpace::getDefaultColor(GfxColor *color) {
   color->c[0] = 0;
   color->c[1] = 0;
@@ -1139,6 +1160,17 @@
   cmyk->k = k;
 }
 
+void GfxCalRGBColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
+  GfxCMYK cmyk;
+  for (int i = 0; i < gfxColorMaxComps; i++)
+    deviceN->c[i] = 0;
+  getCMYK(color, &cmyk);
+  deviceN->c[0] = cmyk.c;
+  deviceN->c[1] = cmyk.m;
+  deviceN->c[2] = cmyk.y;
+  deviceN->c[3] = cmyk.k;
+}
+
 void GfxCalRGBColorSpace::getDefaultColor(GfxColor *color) {
   color->c[0] = 0;
   color->c[1] = 0;
@@ -1239,6 +1271,15 @@
   cmyk->k = clip01(color->c[3]);
 }
 
+void GfxDeviceCMYKColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
+  for (int i = 0; i < gfxColorMaxComps; i++)
+    deviceN->c[i] = 0;
+  deviceN->c[0] = clip01(color->c[0]);
+  deviceN->c[1] = clip01(color->c[1]);
+  deviceN->c[2] = clip01(color->c[2]);
+  deviceN->c[3] = clip01(color->c[3]);
+}
+
 void GfxDeviceCMYKColorSpace::getDefaultColor(GfxColor *color) {
   color->c[0] = 0;
   color->c[1] = 0;
@@ -1465,6 +1506,17 @@
   cmyk->k = k;
 }
 
+void GfxLabColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
+  GfxCMYK cmyk;
+  for (int i = 0; i < gfxColorMaxComps; i++)
+    deviceN->c[i] = 0;
+  getCMYK(color, &cmyk);
+  deviceN->c[0] = cmyk.c;
+  deviceN->c[1] = cmyk.m;
+  deviceN->c[2] = cmyk.y;
+  deviceN->c[3] = cmyk.k;
+}
+
 void GfxLabColorSpace::getDefaultColor(GfxColor *color) {
   color->c[0] = 0;
   if (aMin > 0) {
@@ -1870,6 +1922,17 @@
 #endif
 }
 
+void GfxICCBasedColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
+  GfxCMYK cmyk;
+  for (int i = 0; i < gfxColorMaxComps; i++)
+    deviceN->c[i] = 0;
+  getCMYK(color, &cmyk);
+  deviceN->c[0] = cmyk.c;
+  deviceN->c[1] = cmyk.m;
+  deviceN->c[2] = cmyk.y;
+  deviceN->c[3] = cmyk.k;
+}
+
 void GfxICCBasedColorSpace::getDefaultColor(GfxColor *color) {
   int i;
 
@@ -2089,6 +2152,12 @@
   base->getCMYK(mapColorToBase(color, &color2), cmyk);
 }
 
+void GfxIndexedColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
+  GfxColor color2;
+
+  base->getDeviceN(mapColorToBase(color, &color2), deviceN);
+}
+
 void GfxIndexedColorSpace::getDefaultColor(GfxColor *color) {
   color->c[0] = 0;
 }
@@ -2119,6 +2188,8 @@
     overprintMask = 0x04;
   } else if (!name->cmp("Black")) {
     overprintMask = 0x08;
+  } else if (!name->cmp("All")) {
+    overprintMask = 0xffffffff;
   }
 }
 
@@ -2126,23 +2197,32 @@
 						 GfxColorSpace *altA,
 						 Function *funcA,
 						 GBool nonMarkingA,
-						 Guint overprintMaskA) {
+						 Guint overprintMaskA,
+						 int *mappingA) {
   name = nameA;
   alt = altA;
   func = funcA;
   nonMarking = nonMarkingA;
   overprintMask = overprintMaskA;
+  mapping = mappingA;
 }
 
 GfxSeparationColorSpace::~GfxSeparationColorSpace() {
   delete name;
   delete alt;
   delete func;
+  if (mapping != NULL)
+    gfree(mapping);
 }
 
 GfxColorSpace *GfxSeparationColorSpace::copy() {
+  int *mappingA = NULL;
+  if (mapping != NULL) {
+    mappingA = (int *) gmalloc(sizeof(int));
+    *mappingA = *mapping;
+  }
   return new GfxSeparationColorSpace(name->copy(), alt->copy(), func->copy(),
-				     nonMarking, overprintMask);
+				     nonMarking, overprintMask, mappingA);
 }
 
 //~ handle the 'All' and 'None' colorants
@@ -2235,10 +2315,75 @@
   alt->getCMYK(&color2, cmyk);
 }
 
+void GfxSeparationColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
+  for (int i = 0; i < gfxColorMaxComps; i++)
+    deviceN->c[i] = 0;
+  if (mapping == NULL || mapping[0] == -1) {
+    GfxCMYK cmyk;
+
+    getCMYK(color, &cmyk);
+    deviceN->c[0] = cmyk.c;
+    deviceN->c[1] = cmyk.m;
+    deviceN->c[2] = cmyk.y;
+    deviceN->c[3] = cmyk.k;
+  } else {
+    deviceN->c[mapping[0]] = color->c[0];
+  }
+}
+
 void GfxSeparationColorSpace::getDefaultColor(GfxColor *color) {
   color->c[0] = gfxColorComp1;
 }
 
+void GfxSeparationColorSpace::createMapping(GooList *separationList, int maxSepComps) {
+  if (nonMarking)
+    return;
+  mapping = (int *)gmalloc(sizeof(int));
+  switch (overprintMask) {
+    case 0x01:
+      *mapping = 0;
+      break;
+    case 0x02:
+      *mapping = 1;
+      break;
+    case 0x04:
+      *mapping = 2;
+      break;
+    case 0x08:
+      *mapping = 3;
+      break;
+    default:
+      Guint newOverprintMask = 0x10;
+      for (int i = 0; i < separationList->getLength(); i++) {
+        GfxSeparationColorSpace *sepCS = (GfxSeparationColorSpace *)separationList->get(i);
+        if (!sepCS->getName()->cmp(name)) {
+          if (sepCS->getFunc()->hasDifferentResultSet(func)) {
+            error(errSyntaxWarning, -1,
+              "Different functions found for '{0:s}', convert immediately", name);
+            gfree(mapping);
+            mapping = NULL;
+            return;
+          }
+          *mapping = i+4;
+          overprintMask = newOverprintMask;
+          return;
+        }
+        newOverprintMask <<=1;
+      }
+      if (separationList->getLength() == maxSepComps) {
+        error(errSyntaxWarning, -1,
+	        "Too many ({0:d}) spots, convert '{1:s}' immediately", maxSepComps, name);
+        gfree(mapping);
+        mapping = NULL;
+        return;
+      }
+      *mapping = separationList->getLength() + 4;
+      separationList->append(copy());
+      overprintMask = newOverprintMask;
+      break;
+  }
+}
+
 //------------------------------------------------------------------------
 // GfxDeviceNColorSpace
 //------------------------------------------------------------------------
@@ -2246,14 +2391,17 @@
 GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA,
 					   GooString **namesA,
 					   GfxColorSpace *altA,
-					   Function *funcA) {
+					   Function *funcA,
+					   GooList *sepsCSA) {
   int i;
 
   nComps = nCompsA;
   alt = altA;
   func = funcA;
+  sepsCS = sepsCSA;
   nonMarking = gTrue;
   overprintMask = 0;
+  mapping = NULL;
   for (i = 0; i < nComps; ++i) {
     names[i] = namesA[i];
     if (names[i]->cmp("None")) {
@@ -2267,6 +2415,8 @@
       overprintMask |= 0x04;
     } else if (!names[i]->cmp("Black")) {
       overprintMask |= 0x08;
+    } else if (!names[i]->cmp("All")) {
+      overprintMask = 0xffffffff;
     } else {
       overprintMask = 0x0f;
     }
@@ -2277,6 +2427,8 @@
 					   GooString **namesA,
 					   GfxColorSpace *altA,
 					   Function *funcA,
+					   GooList *sepsCSA,
+					   int *mappingA,
 					   GBool nonMarkingA,
 					   Guint overprintMaskA) {
   int i;
@@ -2284,6 +2436,8 @@
   nComps = nCompsA;
   alt = altA;
   func = funcA;
+  sepsCS = sepsCSA;
+  mapping = mappingA;
   nonMarking = nonMarkingA;
   overprintMask = overprintMaskA;
   for (i = 0; i < nComps; ++i) {
@@ -2299,11 +2453,25 @@
   }
   delete alt;
   delete func;
+  deleteGooList(sepsCS, GfxSeparationColorSpace);
+  if (mapping != NULL)
+    gfree(mapping);
 }
 
 GfxColorSpace *GfxDeviceNColorSpace::copy() {
+  int i;
+  int *mappingA = NULL;
+
+  GooList *sepsCSA = new GooList(sepsCS->getLength());
+  for (i = 0; i < sepsCS->getLength(); i++)
+    sepsCSA->append(((GfxSeparationColorSpace *) sepsCS->get(i))->copy());
+  if (mapping != NULL) {
+    mappingA = (int *)gmalloc(sizeof(int) * nComps);
+    for (i = 0; i < nComps; i++)
+      mappingA[i] = mapping[i];
+  }
   return new GfxDeviceNColorSpace(nComps, names, alt->copy(), func->copy(),
-				  nonMarking, overprintMask);
+				  sepsCSA, mappingA, nonMarking, overprintMask);
 }
 
 //~ handle the 'None' colorant
@@ -2315,6 +2483,7 @@
   Function *funcA;
   Object obj1, obj2;
   int i;
+  GooList *separationList = new GooList();
 
   if (arr->getLength() != 4 && arr->getLength() != 5) {
     error(errSyntaxWarning, -1, "Bad DeviceN color space");
@@ -2351,7 +2520,26 @@
     goto err4;
   }
   obj1.free();
-  cs = new GfxDeviceNColorSpace(nCompsA, namesA, altA, funcA);
+  if (arr->getLength() == 5) {
+    if (!arr->get(4, &obj1)->isDict()) {
+      error(errSyntaxWarning, -1, "Bad DeviceN color space (attributes)");
+      goto err4;
+    }
+    Dict *attribs = obj1.getDict();
+    attribs->lookup("Colorants", &obj2);
+    if (obj2.isDict()) {
+      Dict *colorants = obj2.getDict();
+      for (i = 0; i < colorants->getLength(); i++) {
+        Object obj3;
+        colorants->getVal(i, &obj3);
+        separationList->append(GfxSeparationColorSpace::parse(obj3.getArray(), gfx, recursion));
+        obj3.free();
+      }
+    }
+    obj2.free();
+    obj1.free();
+  }
+  cs = new GfxDeviceNColorSpace(nCompsA, namesA, altA, funcA, separationList);
   return cs;
 
  err4:
@@ -2363,6 +2551,7 @@
  err2:
   obj1.free();
  err1:
+  delete separationList;
   return NULL;
 }
 
@@ -2411,6 +2600,24 @@
   alt->getCMYK(&color2, cmyk);
 }
 
+void GfxDeviceNColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
+  for (int i = 0; i < gfxColorMaxComps; i++)
+    deviceN->c[i] = 0;
+  if (mapping == NULL) {
+    GfxCMYK cmyk;
+
+    getCMYK(color, &cmyk);
+    deviceN->c[0] = cmyk.c;
+    deviceN->c[1] = cmyk.m;
+    deviceN->c[2] = cmyk.y;
+    deviceN->c[3] = cmyk.k;
+  } else {
+    for (int j = 0; j < nComps; j++)
+      if (mapping[j] != -1)
+        deviceN->c[mapping[j]] = color->c[j];
+  }
+}
+
 void GfxDeviceNColorSpace::getDefaultColor(GfxColor *color) {
   int i;
 
@@ -2419,6 +2626,95 @@
   }
 }
 
+void GfxDeviceNColorSpace::createMapping(GooList *separationList, int maxSepComps) {
+  if (nonMarking)               // None
+    return;
+  mapping = (int *)gmalloc(sizeof(int) * nComps);
+  Guint newOverprintMask = 0;
+  for (int i = 0; i < nComps; i++) {
+    if (!names[i]->cmp("None")) {
+      mapping[i] = -1;
+    } else if (!names[i]->cmp("Cyan")) {
+      newOverprintMask |= 0x01;
+      mapping[i] = 0;
+    } else if (!names[i]->cmp("Magenta")) {
+      newOverprintMask |= 0x02;
+      mapping[i] = 1;
+    } else if (!names[i]->cmp("Yellow")) {
+      newOverprintMask |= 0x04;
+      mapping[i] = 2;
+    } else if (!names[i]->cmp("Black")) {
+      newOverprintMask |= 0x08;
+      mapping[i] = 3;
+    } else {
+      Guint startOverprintMask = 0x10;
+      GBool found = gFalse;
+      Function *sepFunc = NULL;
+      if (nComps == 1)
+        sepFunc = func;
+      else {
+        for (int k = 0; k < sepsCS->getLength(); k++) {
+          GfxSeparationColorSpace *sepCS = (GfxSeparationColorSpace *)sepsCS->get(k);
+          if (!sepCS->getName()->cmp(names[i])) {
+            sepFunc = sepCS->getFunc();
+            break;
+          }
+        }
+      }
+      for (int j = 0; j < separationList->getLength(); j++) {
+        GfxSeparationColorSpace *sepCS = (GfxSeparationColorSpace *)separationList->get(j);
+        if (!sepCS->getName()->cmp(names[i])) {
+          if (sepFunc != NULL && sepCS->getFunc()->hasDifferentResultSet(sepFunc)) {
+            error(errSyntaxWarning, -1,
+              "Different functions found for '{0:s}', convert immediately", names[i]);
+            gfree(mapping);
+            mapping = NULL;
+            overprintMask = 0xffffffff;
+            return;
+          }
+          mapping[i] = j+4;
+          newOverprintMask |= startOverprintMask;
+          found = gTrue;
+          break;
+        }
+        startOverprintMask <<=1;
+      }
+      if (!found) {
+        if (separationList->getLength() == maxSepComps) {
+          error(errSyntaxWarning, -1,
+            "Too many ({0:d}) spots, convert '{1:s}' immediately", maxSepComps, names[i]);
+          gfree(mapping);
+          mapping = NULL;
+          overprintMask = 0xffffffff;
+          return;
+        }
+        mapping[i] = separationList->getLength() + 4;
+        newOverprintMask |= startOverprintMask;
+        if (nComps == 1)
+          separationList->append(new GfxSeparationColorSpace(names[i]->copy(),alt->copy(), func->copy()));
+        else {
+          for (int k = 0; k < sepsCS->getLength(); k++) {
+            GfxSeparationColorSpace *sepCS = (GfxSeparationColorSpace *)sepsCS->get(k);
+            if (!sepCS->getName()->cmp(names[i])) {
+              found = gTrue;
+              separationList->append(sepCS->copy());
+              break;
+            }
+          }
+          if(!found) {
+            error(errSyntaxWarning, -1, "DeviceN has no suitable colorant");
+            gfree(mapping);
+            mapping = NULL;
+            overprintMask = 0xffffffff;
+            return;
+          }
+        }
+      }
+    }
+  }
+  overprintMask = newOverprintMask;
+}
+
 //------------------------------------------------------------------------
 // GfxPatternColorSpace
 //------------------------------------------------------------------------
@@ -2474,6 +2770,12 @@
   cmyk->k = 1;
 }
 
+void GfxPatternColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
+  for (int i = 0; i < gfxColorMaxComps; i++)
+    deviceN->c[i] = 0;
+  deviceN->c[3] = 1;
+}
+
 void GfxPatternColorSpace::getDefaultColor(GfxColor *color) {
   color->c[0]=0;
 }
@@ -5305,6 +5607,23 @@
   }
 }
 
+void GfxImageColorMap::getDeviceN(Guchar *x, GfxColor *deviceN) {
+  GfxColor color;
+  int i;
+
+  if (colorSpace2) {
+    for (i = 0; i < nComps2; ++i) {
+      color.c[i] = lookup2[i][x[0]];
+    }
+    colorSpace2->getDeviceN(&color, deviceN);
+  } else {
+    for (i = 0; i < nComps; ++i) {
+      color.c[i] = lookup[i][x[i]];
+    }
+    colorSpace->getDeviceN(&color, deviceN);
+  }
+}
+
 void GfxImageColorMap::getColor(Guchar *x, GfxColor *color) {
   int maxPixel, i;
 
diff --git a/poppler/GfxState.h b/poppler/GfxState.h
index 856ec3f..f2ce6b2 100644
--- a/poppler/GfxState.h
+++ b/poppler/GfxState.h
@@ -20,7 +20,7 @@
 // Copyright (C) 2009-2011 Albert Astals Cid <aacid@kde.org>
 // Copyright (C) 2010 Christian Feuersänger <cfeuersaenger@googlemail.com>
 // Copyright (C) 2011 Andrea Canciani <ranma42@gmail.com>
-// Copyright (C) 2011 Thomas Freitag <Thomas.Freitag@alfa.de>
+// Copyright (C) 2011, 2012 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
@@ -48,6 +48,7 @@
 class PDFRectangle;
 class GfxShading;
 class PopplerCache;
+class GooList;
 
 class Matrix {
 public:
@@ -202,11 +203,15 @@
   virtual void getGray(GfxColor *color, GfxGray *gray) = 0;
   virtual void getRGB(GfxColor *color, GfxRGB *rgb) = 0;
   virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk) = 0;
+  virtual void getDeviceN(GfxColor *color, GfxColor *deviceN) = 0;
   virtual void getGrayLine(Guchar * /*in*/, Guchar * /*out*/, int /*length*/) { error(errInternal, -1, "GfxColorSpace::getGrayLine this should not happen"); }
   virtual void getRGBLine(Guchar * /*in*/, unsigned int * /*out*/, int /*length*/) { error(errInternal, -1, "GfxColorSpace::getRGBLine (first variant) this should not happen"); }
   virtual void getRGBLine(Guchar * /*in*/, Guchar * /*out*/, int /*length*/) {  error(errInternal, -1, "GfxColorSpace::getRGBLine (second variant) this should not happen"); }
   virtual void getRGBXLine(Guchar * /*in*/, Guchar * /*out*/, int /*length*/) {  error(errInternal, -1, "GfxColorSpace::getRGBXLine this should not happen"); }
 
+  // create mapping for spot colorants
+  virtual void createMapping(GooList *separationList, int maxSepComps);
+
   // Does this ColorSpace support getRGBLine?
   virtual GBool useGetRGBLine() { return gFalse; }
   // Does this ColorSpace support getGrayLine?
@@ -249,6 +254,7 @@
 protected:
 
   Guint overprintMask;
+  int *mapping;
 };
 
 //------------------------------------------------------------------------
@@ -266,6 +272,7 @@
   virtual void getGray(GfxColor *color, GfxGray *gray);
   virtual void getRGB(GfxColor *color, GfxRGB *rgb);
   virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+  virtual void getDeviceN(GfxColor *color, GfxColor *deviceN);
   virtual void getGrayLine(Guchar *in, Guchar *out, int length);
   virtual void getRGBLine(Guchar *in, unsigned int *out, int length);
   virtual void getRGBLine(Guchar *in, Guchar *out, int length);
@@ -298,6 +305,7 @@
   virtual void getGray(GfxColor *color, GfxGray *gray);
   virtual void getRGB(GfxColor *color, GfxRGB *rgb);
   virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+  virtual void getDeviceN(GfxColor *color, GfxColor *deviceN);
 
   virtual int getNComps() { return 1; }
   virtual void getDefaultColor(GfxColor *color);
@@ -335,6 +343,7 @@
   virtual void getGray(GfxColor *color, GfxGray *gray);
   virtual void getRGB(GfxColor *color, GfxRGB *rgb);
   virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+  virtual void getDeviceN(GfxColor *color, GfxColor *deviceN);
   virtual void getGrayLine(Guchar *in, Guchar *out, int length);
   virtual void getRGBLine(Guchar *in, unsigned int *out, int length);
   virtual void getRGBLine(Guchar *in, Guchar *out, int length);
@@ -367,6 +376,7 @@
   virtual void getGray(GfxColor *color, GfxGray *gray);
   virtual void getRGB(GfxColor *color, GfxRGB *rgb);
   virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+  virtual void getDeviceN(GfxColor *color, GfxColor *deviceN);
 
   virtual int getNComps() { return 3; }
   virtual void getDefaultColor(GfxColor *color);
@@ -408,6 +418,7 @@
   virtual void getGray(GfxColor *color, GfxGray *gray);
   virtual void getRGB(GfxColor *color, GfxRGB *rgb);
   virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+  virtual void getDeviceN(GfxColor *color, GfxColor *deviceN);
   virtual void getRGBLine(Guchar *in, unsigned int *out, int length);
   virtual void getRGBLine(Guchar *, Guchar *out, int length);
   virtual void getRGBXLine(Guchar *in, Guchar *out, int length);
@@ -437,6 +448,7 @@
   virtual void getGray(GfxColor *color, GfxGray *gray);
   virtual void getRGB(GfxColor *color, GfxRGB *rgb);
   virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+  virtual void getDeviceN(GfxColor *color, GfxColor *deviceN);
 
   virtual int getNComps() { return 3; }
   virtual void getDefaultColor(GfxColor *color);
@@ -484,6 +496,7 @@
   virtual void getGray(GfxColor *color, GfxGray *gray);
   virtual void getRGB(GfxColor *color, GfxRGB *rgb);
   virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+  virtual void getDeviceN(GfxColor *color, GfxColor *deviceN);
   virtual void getRGBLine(Guchar *in, unsigned int *out, int length);
   virtual void getRGBLine(Guchar *in, Guchar *out, int length);
   virtual void getRGBXLine(Guchar *in, Guchar *out, int length);
@@ -529,6 +542,7 @@
   virtual void getGray(GfxColor *color, GfxGray *gray);
   virtual void getRGB(GfxColor *color, GfxRGB *rgb);
   virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+  virtual void getDeviceN(GfxColor *color, GfxColor *deviceN);
   virtual void getRGBLine(Guchar *in, unsigned int *out, int length);
   virtual void getRGBLine(Guchar *in, Guchar *out, int length);
   virtual void getRGBXLine(Guchar *in, Guchar *out, int length);
@@ -546,6 +560,10 @@
   int getIndexHigh() { return indexHigh; }
   Guchar *getLookup() { return lookup; }
   GfxColor *mapColorToBase(GfxColor *color, GfxColor *baseColor);
+  Guint getOverprintMask() { return base->getOverprintMask(); }
+  virtual void createMapping(GooList *separationList, int maxSepComps)
+    { base->createMapping(separationList, maxSepComps); }
+
 
 private:
 
@@ -573,6 +591,9 @@
   virtual void getGray(GfxColor *color, GfxGray *gray);
   virtual void getRGB(GfxColor *color, GfxRGB *rgb);
   virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+  virtual void getDeviceN(GfxColor *color, GfxColor *deviceN);
+
+  virtual void createMapping(GooList *separationList, int maxSepComps);
 
   virtual int getNComps() { return 1; }
   virtual void getDefaultColor(GfxColor *color);
@@ -588,7 +609,7 @@
 
   GfxSeparationColorSpace(GooString *nameA, GfxColorSpace *altA,
 			  Function *funcA, GBool nonMarkingA,
-			  Guint overprintMaskA);
+			  Guint overprintMaskA, int *mappingA);
 
   GooString *name;		// colorant name
   GfxColorSpace *alt;		// alternate color space
@@ -604,7 +625,7 @@
 public:
 
   GfxDeviceNColorSpace(int nCompsA, GooString **namesA,
-		       GfxColorSpace *alt, Function *func);
+		       GfxColorSpace *alt, Function *func, GooList *sepsCS);
   virtual ~GfxDeviceNColorSpace();
   virtual GfxColorSpace *copy();
   virtual GfxColorSpaceMode getMode() { return csDeviceN; }
@@ -615,6 +636,9 @@
   virtual void getGray(GfxColor *color, GfxGray *gray);
   virtual void getRGB(GfxColor *color, GfxRGB *rgb);
   virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+  virtual void getDeviceN(GfxColor *color, GfxColor *deviceN);
+
+  virtual void createMapping(GooList *separationList, int maxSepComps);
 
   virtual int getNComps() { return nComps; }
   virtual void getDefaultColor(GfxColor *color);
@@ -629,8 +653,8 @@
 private:
 
   GfxDeviceNColorSpace(int nCompsA, GooString **namesA,
-		       GfxColorSpace *alt, Function *func,
-		       GBool nonMarkingA, Guint overprintMaskA);
+		       GfxColorSpace *alt, Function *func, GooList *sepsCSA,
+		       int *mappingA, GBool nonMarkingA, Guint overprintMaskA);
 
   int nComps;			// number of components
   GooString			// colorant names
@@ -638,6 +662,7 @@
   GfxColorSpace *alt;		// alternate color space
   Function *func;		// tint transform (into alternate color space)
   GBool nonMarking;
+  GooList *sepsCS; // list of separation cs for spot colorants;
 };
 
 //------------------------------------------------------------------------
@@ -658,6 +683,7 @@
   virtual void getGray(GfxColor *color, GfxGray *gray);
   virtual void getRGB(GfxColor *color, GfxRGB *rgb);
   virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+  virtual void getDeviceN(GfxColor *color, GfxColor *deviceN);
 
   virtual int getNComps() { return 0; }
   virtual void getDefaultColor(GfxColor *color);
@@ -1107,6 +1133,7 @@
   void getRGBXLine(Guchar *in, Guchar *out, int length);
   void getGrayLine(Guchar *in, Guchar *out, int length);
   void getCMYK(Guchar *x, GfxCMYK *cmyk);
+  void getDeviceN(Guchar *x, GfxColor *deviceN);
   void getColor(Guchar *x, GfxColor *color);
 
 private:
@@ -1342,8 +1369,12 @@
     { strokeColorSpace->getRGB(&strokeColor, rgb); }
   void getFillCMYK(GfxCMYK *cmyk)
     { fillColorSpace->getCMYK(&fillColor, cmyk); }
+  void getFillDeviceN(GfxColor *deviceN)
+    { fillColorSpace->getDeviceN(&fillColor, deviceN); }
   void getStrokeCMYK(GfxCMYK *cmyk)
     { strokeColorSpace->getCMYK(&strokeColor, cmyk); }
+  void getStrokeDeviceN(GfxColor *deviceN)
+    { strokeColorSpace->getDeviceN(&strokeColor, deviceN); }
   GfxColorSpace *getFillColorSpace() { return fillColorSpace; }
   GfxColorSpace *getStrokeColorSpace() { return strokeColorSpace; }
   GfxPattern *getFillPattern() { return fillPattern; }
diff --git a/poppler/GlobalParams.cc b/poppler/GlobalParams.cc
index 76394ca..098e4a4 100644
--- a/poppler/GlobalParams.cc
+++ b/poppler/GlobalParams.cc
@@ -439,7 +439,7 @@
   void *dlA;
 #endif
 
-  path = globalParams->getBaseDir();
+  path = new GooString(POPPLER_DATADIR);
   appendToPath(path, "plugins");
   appendToPath(path, type);
   appendToPath(path, name);
@@ -574,11 +574,7 @@
   }
 
 #ifdef _WIN32
-  // baseDir will be set by a call to setBaseDir
-  baseDir = new GooString();
   substFiles = new GooHash(gTrue);
-#else
-  baseDir = appendToPath(getHomeDir(), ".xpdf");
 #endif
   nameToUnicode = new NameToCharCode();
   cidToUnicodes = new GooHash(gTrue);
@@ -800,7 +796,6 @@
 
   delete macRomanReverseMap;
 
-  delete baseDir;
   delete nameToUnicode;
   deleteGooHash(cidToUnicodes, GooString);
   deleteGooHash(unicodeToUnicodes, GooString);
@@ -850,13 +845,6 @@
 }
 
 //------------------------------------------------------------------------
-
-void GlobalParams::setBaseDir(const char *dir) {
-  delete baseDir;
-  baseDir = new GooString(dir);
-}
-
-//------------------------------------------------------------------------
 // accessors
 //------------------------------------------------------------------------
 
@@ -865,15 +853,6 @@
   return macRomanReverseMap->lookup(charName);
 }
 
-GooString *GlobalParams::getBaseDir() {
-  GooString *s;
-
-  lockGlobalParams;
-  s = baseDir->copy();
-  unlockGlobalParams;
-  return s;
-}
-
 Unicode GlobalParams::mapNameToUnicode(const char *charName) {
   // no need to lock - nameToUnicode is constant
   return nameToUnicode->lookup(charName);
diff --git a/poppler/GlobalParams.h b/poppler/GlobalParams.h
index 375ac2c..bc11684 100644
--- a/poppler/GlobalParams.h
+++ b/poppler/GlobalParams.h
@@ -13,7 +13,7 @@
 // All changes made under the Poppler project to this file are licensed
 // under GPL version 2 or later
 //
-// Copyright (C) 2005, 2007-2010 Albert Astals Cid <aacid@kde.org>
+// Copyright (C) 2005, 2007-2010, 2012 Albert Astals Cid <aacid@kde.org>
 // Copyright (C) 2005 Jonathan Blandford <jrb@redhat.com>
 // Copyright (C) 2006 Takashi Iwai <tiwai@suse.de>
 // Copyright (C) 2006 Kristian Høgsberg <krh@redhat.com>
@@ -133,14 +133,12 @@
 
   ~GlobalParams();
 
-  void setBaseDir(const char *dir);
   void setupBaseFonts(char *dir);
 
   //----- accessors
 
   CharCode getMacRomanCharCode(char *charName);
 
-  GooString *getBaseDir();
   Unicode mapNameToUnicode(const char *charName);
   UnicodeMap *getResidentUnicodeMap(GooString *encodingName);
   FILE *getUnicodeMapFile(GooString *encodingName);
@@ -273,7 +271,6 @@
 
   //----- user-modifiable settings
 
-  GooString *baseDir;		// base directory - for plugins, etc.
   NameToCharCode *		// mapping from char name to Unicode
     nameToUnicode;
   GooHash *cidToUnicodes;		// files for mappings from char collections
diff --git a/poppler/Link.cc b/poppler/Link.cc
index b90b1c1..b5b2bd3 100644
--- a/poppler/Link.cc
+++ b/poppler/Link.cc
@@ -19,6 +19,7 @@
 // Copyright (C) 2008-2010, 2012 Albert Astals Cid <aacid@kde.org>
 // Copyright (C) 2009 Kovid Goyal <kovid@kovidgoyal.net>
 // Copyright (C) 2009 Ilya Gorenbein <igorenbein@finjan.com>
+// Copyright (C) 2012 Tobias Koening <tobias.koenig@kdab.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
@@ -700,9 +701,10 @@
 //------------------------------------------------------------------------
 
 LinkRendition::LinkRendition(Object *obj) {
-  operation = -1;
+  operation = NoRendition;
   media = NULL;
   js = NULL;
+  int operationCode = -1;
 
   if (obj->isDict()) {
     Object tmp;
@@ -721,25 +723,43 @@
     tmp.free();
 
     if (obj->dictLookup("OP", &tmp)->isInt()) {
-      operation = tmp.getInt();
-      if (!js && (operation < 0 || operation > 4)) {
-        error(errSyntaxWarning, -1, "Invalid Rendition Action: unrecognized operation valued: {0:d}", operation);
+      operationCode = tmp.getInt();
+      if (!js && (operationCode < 0 || operationCode > 4)) {
+        error(errSyntaxWarning, -1, "Invalid Rendition Action: unrecognized operation valued: {0:d}", operationCode);
       } else {
         Object obj1;
 
         // retrieve rendition object
         if (obj->dictLookup("R", &renditionObj)->isDict()) {
           media = new MediaRendition(&renditionObj);
-	} else if (operation == 0 || operation == 4) {
-          error(errSyntaxWarning, -1, "Invalid Rendition Action: no R field with op = {0:d}", operation);
+	} else if (operationCode == 0 || operationCode == 4) {
+          error(errSyntaxWarning, -1, "Invalid Rendition Action: no R field with op = {0:d}", operationCode);
 	  renditionObj.free();
 	}
 
 	if (!obj->dictLookupNF("AN", &screenRef)->isRef() && operation >= 0 && operation <= 4) {
-	  error(errSyntaxWarning, -1, "Invalid Rendition Action: no AN field with op = {0:d}", operation);
+	  error(errSyntaxWarning, -1, "Invalid Rendition Action: no AN field with op = {0:d}", operationCode);
 	  screenRef.free();
 	}
       }
+
+      switch (operationCode) {
+        case 0:
+          operation = PlayRendition;
+          break;
+        case 1:
+          operation = StopRendition;
+          break;
+        case 2:
+          operation = PauseRendition;
+          break;
+        case 3:
+          operation = ResumeRendition;
+          break;
+        case 4:
+          operation = PlayRendition;
+          break;
+      }
     } else if (!js) {
       error(errSyntaxWarning, -1, "Invalid Rendition action: no OP or JS field defined");
     }
diff --git a/poppler/Link.h b/poppler/Link.h
index 8e2df24..fc2abe6 100644
--- a/poppler/Link.h
+++ b/poppler/Link.h
@@ -16,6 +16,7 @@
 // Copyright (C) 2006, 2008 Pino Toscano <pino@kde.org>
 // Copyright (C) 2008 Hugo Mercier <hmercier31@gmail.com>
 // Copyright (C) 2010, 2011 Carlos Garcia Campos <carlosgc@gnome.org>
+// Copyright (C) 2012 Tobias Koening <tobias.koenig@kdab.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
@@ -51,7 +52,7 @@
   actionURI,			// URI
   actionNamed,			// named action
   actionMovie,			// movie action
-  actionRendition,
+  actionRendition,		// rendition action
   actionSound,			// sound action
   actionJavaScript,		// JavaScript action
   actionOCGState,               // Set-OCG-State action
@@ -319,6 +320,16 @@
 
 class LinkRendition: public LinkAction {
 public:
+  /**
+   * Describes the possible rendition operations.
+   */
+  enum RenditionOperation {
+    NoRendition,
+    PlayRendition,
+    StopRendition,
+    PauseRendition,
+    ResumeRendition
+  };
 
   LinkRendition(Object *Obj);
 
@@ -334,7 +345,7 @@
   GBool hasScreenAnnot() { return screenRef.isRef(); }
   Ref getScreenAnnot() { return screenRef.getRef(); }
 
-  int getOperation() { return operation; }
+  RenditionOperation getOperation() { return operation; }
 
   MediaRendition* getMedia() { return media; }
 
@@ -344,7 +355,7 @@
 
   Object screenRef;
   Object renditionObj;
-  int operation;
+  RenditionOperation operation;
 
   MediaRendition* media;
 
diff --git a/poppler/Makefile.am b/poppler/Makefile.am
index c2e81d9..e10d19d 100644
--- a/poppler/Makefile.am
+++ b/poppler/Makefile.am
@@ -177,7 +177,7 @@
 	$(PTHREAD_LIBS)				\
 	$(win32_libs)
 
-libpoppler_la_LDFLAGS = -version-info 28:0:0 @create_shared_lib@ @auto_import_flags@
+libpoppler_la_LDFLAGS = -version-info 29:0:0 @create_shared_lib@ @auto_import_flags@
 
 if ENABLE_XPDF_HEADERS
 
@@ -251,6 +251,7 @@
 	PSOutputDev.h		\
 	TextOutputDev.h		\
 	SecurityHandler.h	\
+	UTF.h			\
 	UTF8.h			\
 	XpdfPluginAPI.h		\
 	Sound.h
@@ -317,6 +318,7 @@
 	strtok_r.cpp		\
 	UnicodeMap.cc		\
 	UnicodeTypeTable.cc	\
+	UTF.cc                  \
 	ViewerPreferences.cc	\
 	XRef.cc			\
 	PSOutputDev.cc		\
diff --git a/poppler/PDFDoc.cc b/poppler/PDFDoc.cc
index ce01ca3..f52d498 100644
--- a/poppler/PDFDoc.cc
+++ b/poppler/PDFDoc.cc
@@ -610,6 +610,15 @@
   OutStream *outStr;
   XRef *yRef, *countRef;
   int rootNum = getXRef()->getNumObjects() + 1;
+

+  // Make sure that special flags are set, because we are going to read

+  // all objects, including Unencrypted ones.

+  xref->scanSpecialFlags();

+

+  Guchar *fileKey;

+  CryptAlgorithm encAlgorithm;

+  int keyLength;

+  xref->getEncryptionParameters(&fileKey, &encAlgorithm, &keyLength);

 
   if (pageNo < 1 || pageNo > getNumPages()) {
     error(errInternal, -1, "Illegal pageNo: {0:d}({1:d})", pageNo, getNumPages() );
@@ -634,7 +643,18 @@
   outStr = new FileOutStream(f,0);
 
   yRef = new XRef(getXRef()->getTrailerDict());
+  Object encrypt;
+  getXRef()->getTrailerDict()->dictLookup("Encrypt", &encrypt);
+
+  if (secHdlr != NULL && !secHdlr->isUnencrypted()) {
+    yRef->setEncryption(secHdlr->getPermissionFlags(), 
+      secHdlr->getOwnerPasswordOk(), fileKey, keyLength, secHdlr->getEncVersion(), secHdlr->getEncRevision(), encAlgorithm);
+  }
   countRef = new XRef();
+  Object *trailerObj = getXRef()->getTrailerDict();
+  if (trailerObj->isDict()) {
+    markPageObjects(trailerObj->getDict(), yRef, countRef, 0);
+  }
   yRef->add(0, 65535, 0, gFalse);
   writeHeader(outStr, getPDFMajorVersion(), getPDFMinorVersion());
 
@@ -644,7 +664,6 @@
   if (infoObj.isDict()) {
     Dict *infoDict = infoObj.getDict();
     markPageObjects(infoDict, yRef, countRef, 0);
-    Object *trailerObj = getXRef()->getTrailerDict();
     if (trailerObj->isDict()) {
       Dict *trailerDict = trailerObj->getDict();
       Object ref;
@@ -673,6 +692,7 @@
 
   Dict *pageDict = page.getDict();
   markPageObjects(pageDict, yRef, countRef, 0);
+  yRef->markUnencrypted();
   Guint objectsCount = writePageObjects(outStr, yRef, 0);
 
   yRef->add(rootNum,0,outStr->getPos(),gTrue);
@@ -687,7 +707,7 @@
       if (j > 0) outStr->printf(" ");
       Object value; catDict->getValNF(j, &value);
       outStr->printf("/%s ", key);
-      writeObject(&value, NULL, outStr, getXRef(), 0);
+      writeObject(&value, outStr, getXRef(), 0, NULL, cryptRC4, 0, 0, 0);
       value.free();
     }
   }
@@ -701,7 +721,7 @@
   outStr->printf("<< /Type /Pages /Kids [ %d 0 R ] /Count 1 ", rootNum + 2);
   if (resourcesObj.isDict()) {
     outStr->printf("/Resources ");
-    writeObject(&resourcesObj, NULL, outStr, getXRef(), 0);
+    writeObject(&resourcesObj, outStr, getXRef(), 0, NULL, cryptRC4, 0, 0, 0);
     resourcesObj.free();
   }
   outStr->printf(">>\n");
@@ -719,7 +739,7 @@
       outStr->printf("/Parent %d 0 R", rootNum + 1);
     } else {
       outStr->printf("/%s ", key);
-      writeObject(&value, NULL, outStr, getXRef(), 0); 
+      writeObject(&value, outStr, getXRef(), 0, NULL, cryptRC4, 0, 0, 0);
     }
     value.free();
   }
@@ -766,41 +786,19 @@
   // find if we have updated objects
   GBool updated = gFalse;
   for(int i=0; i<xref->getNumObjects(); i++) {
-    if (xref->getEntry(i)->updated) {
+    if (xref->getEntry(i)->getFlag(XRefEntry::Updated)) {
       updated = gTrue;
       break;
     }
   }
 
-  // we don't support rewriting files with Encrypt at the moment
-  Object obj;
-  xref->getTrailerDict()->getDict()->lookupNF("Encrypt", &obj);
-  if (!obj.isNull())
-  {
-    obj.free();
-    if (!updated && mode == writeStandard) {
-      // simply copy the original file
-      saveWithoutChangesAs (outStr);
-    } else {
-      return errEncrypted;
-    }
-  }
-  else
-  {
-    obj.free();
-
-    if (mode == writeForceRewrite) {
-      saveCompleteRewrite(outStr);
-    } else if (mode == writeForceIncremental) {
-      saveIncrementalUpdate(outStr); 
-    } else { // let poppler decide
-      if(updated) { 
-        saveIncrementalUpdate(outStr);
-      } else {
-        // simply copy the original file
-        saveWithoutChangesAs (outStr);
-      }
-    }
+  if (!updated && mode == writeStandard) {
+    // simply copy the original file
+    saveWithoutChangesAs (outStr);
+  } if (mode == writeForceRewrite) {
+    saveCompleteRewrite(outStr);
+  } else {
+    saveIncrementalUpdate(outStr);
   }
 
   return errNone;
@@ -848,6 +846,11 @@
   }
   str->close();
 
+  Guchar *fileKey;
+  CryptAlgorithm encAlgorithm;
+  int keyLength;
+  xref->getEncryptionParameters(&fileKey, &encAlgorithm, &keyLength);
+
   uxref = new XRef();
   uxref->add(0, 65535, 0, gFalse);
   for(int i=0; i<xref->getNumObjects(); i++) {
@@ -855,14 +858,16 @@
         (xref->getEntry(i)->gen == 0)) //we skip the irrelevant free objects
       continue;
 
-    if (xref->getEntry(i)->updated) { //we have an updated object
+    if (xref->getEntry(i)->getFlag(XRefEntry::Updated)) { //we have an updated object
       Ref ref;
       ref.num = i;
       ref.gen = xref->getEntry(i)->type == xrefEntryCompressed ? 0 : xref->getEntry(i)->gen;
       if (xref->getEntry(i)->type != xrefEntryFree) {
         Object obj1;
         xref->fetch(ref.num, ref.gen, &obj1);
-        Guint offset = writeObject(&obj1, &ref, outStr);
+        Guint offset = writeObjectHeader(&ref, outStr);
+        writeObject(&obj1, outStr, fileKey, encAlgorithm, keyLength, ref.num, ref.gen);
+        writeObjectFooter(outStr);
         uxref->add(ref.num, ref.gen, offset, gTrue);
         obj1.free();
       } else {
@@ -905,6 +910,15 @@
 
 void PDFDoc::saveCompleteRewrite (OutStream* outStr)
 {
+  // Make sure that special flags are set, because we are going to read
+  // all objects, including Unencrypted ones.
+  xref->scanSpecialFlags();
+
+  Guchar *fileKey;
+  CryptAlgorithm encAlgorithm;
+  int keyLength;
+  xref->getEncryptionParameters(&fileKey, &encAlgorithm, &keyLength);
+
   outStr->printf("%%PDF-%d.%d\r\n",pdfMajorVersion,pdfMinorVersion);
   XRef *uxref = new XRef();
   uxref->add(0, 65535, 0, gFalse);
@@ -919,18 +933,32 @@
           and we don't want the one with num=0 because it has already been added (gen = 65535)*/
       if (ref.gen > 0 && ref.num > 0)
         uxref->add(ref.num, ref.gen, 0, gFalse);
+    } else if (xref->getEntry(i)->getFlag(XRefEntry::DontRewrite)) {
+      // This entry must not be written, put a free entry instead (with incremented gen)
+      ref.num = i;
+      ref.gen = xref->getEntry(i)->gen + 1;
+      uxref->add(ref.num, ref.gen, 0, gFalse);
     } else if (type == xrefEntryUncompressed){ 
       ref.num = i;
       ref.gen = xref->getEntry(i)->gen;
       xref->fetch(ref.num, ref.gen, &obj1);
-      Guint offset = writeObject(&obj1, &ref, outStr);
+      Guint offset = writeObjectHeader(&ref, outStr);
+      // Write unencrypted objects in unencrypted form
+      if (xref->getEntry(i)->getFlag(XRefEntry::Unencrypted)) {
+        writeObject(&obj1, outStr, NULL, cryptRC4, 0, 0, 0);
+      } else {
+        writeObject(&obj1, outStr, fileKey, encAlgorithm, keyLength, ref.num, ref.gen);
+      }
+      writeObjectFooter(outStr);
       uxref->add(ref.num, ref.gen, offset, gTrue);
       obj1.free();
     } else if (type == xrefEntryCompressed) {
       ref.num = i;
       ref.gen = 0; //compressed entries have gen == 0
       xref->fetch(ref.num, ref.gen, &obj1);
-      Guint offset = writeObject(&obj1, &ref, outStr);
+      Guint offset = writeObjectHeader(&ref, outStr);
+      writeObject(&obj1, outStr, fileKey, encAlgorithm, keyLength, ref.num, ref.gen);
+      writeObjectFooter(outStr);
       uxref->add(ref.num, ref.gen, offset, gTrue);
       obj1.free();
     }
@@ -941,7 +969,8 @@
   delete uxref;
 }
 
-void PDFDoc::writeDictionnary (Dict* dict, OutStream* outStr, XRef *xRef, Guint numOffset)
+void PDFDoc::writeDictionnary (Dict* dict, OutStream* outStr, XRef *xRef, Guint numOffset, Guchar *fileKey,
+                               CryptAlgorithm encAlgorithm, int keyLength, int objNum, int objGen)
 {
   Object obj1;
   outStr->printf("<<");
@@ -950,7 +979,7 @@
     GooString *keyNameToPrint = keyName.sanitizedName(gFalse /* non ps mode */);
     outStr->printf("/%s ", keyNameToPrint->getCString());
     delete keyNameToPrint;
-    writeObject(dict->getValNF(i, &obj1), NULL, outStr, xRef, numOffset);
+    writeObject(dict->getValNF(i, &obj1), outStr, xRef, numOffset, fileKey, encAlgorithm, keyLength, objNum, objGen);
     obj1.free();
   }
   outStr->printf(">> ");
@@ -988,8 +1017,27 @@
   outStr->printf("\r\nendstream\r\n");
 }
 
-void PDFDoc::writeString (GooString* s, OutStream* outStr)
+void PDFDoc::writeString (GooString* s, OutStream* outStr, Guchar *fileKey,
+                          CryptAlgorithm encAlgorithm, int keyLength, int objNum, int objGen)
 {
+  // Encrypt string if encryption is enabled
+  GooString *sEnc = NULL;
+  if (fileKey) {
+    Object obj;
+    EncryptStream *enc = new EncryptStream(new MemStream(s->getCString(), 0, s->getLength(), obj.initNull()),
+                                           fileKey, encAlgorithm, keyLength, objNum, objGen);
+    sEnc = new GooString();
+    int c;
+    enc->reset();
+    while ((c = enc->getChar()) != EOF) {
+      sEnc->append((char)c);
+    }
+
+    delete enc;
+    s = sEnc;
+  }
+
+  // Write data
   if (s->hasUnicodeMarker()) {
     //unicode string don't necessary end with \0
     const char* c = s->getCString();
@@ -1021,18 +1069,24 @@
     }
     outStr->printf(") ");
   }
+
+  delete sEnc;
 }
 
-Guint PDFDoc::writeObject (Object* obj, Ref* ref, OutStream* outStr, XRef *xRef, Guint numOffset)
+Guint PDFDoc::writeObjectHeader (Ref *ref, OutStream* outStr)
+{
+  Guint offset = outStr->getPos();
+  outStr->printf("%i %i obj ", ref->num, ref->gen);
+  return offset;
+}
+
+void PDFDoc::writeObject (Object* obj, OutStream* outStr, XRef *xRef, Guint numOffset, Guchar *fileKey,
+                          CryptAlgorithm encAlgorithm, int keyLength, int objNum, int objGen)
 {
   Array *array;
   Object obj1;
-  Guint offset = outStr->getPos();
   int tmp;
 
-  if(ref) 
-    outStr->printf("%i %i obj ", ref->num, ref->gen);
-
   switch (obj->getType()) {
     case objBool:
       outStr->printf("%s ", obj->getBool()?"true":"false");
@@ -1040,6 +1094,9 @@
     case objInt:
       outStr->printf("%i ", obj->getInt());
       break;
+    case objUint:
+      outStr->printf("%u ", obj->getUint());
+      break;
     case objReal:
     {
       GooString s;
@@ -1048,7 +1105,7 @@
       break;
     }
     case objString:
-      writeString(obj->getString(), outStr);
+      writeString(obj->getString(), outStr, fileKey, encAlgorithm, keyLength, objNum, objGen);
       break;
     case objName:
     {
@@ -1065,13 +1122,13 @@
       array = obj->getArray();
       outStr->printf("[");
       for (int i=0; i<array->getLength(); i++) {
-        writeObject(array->getNF(i, &obj1), NULL,outStr, xRef, numOffset);
+        writeObject(array->getNF(i, &obj1), outStr, xRef, numOffset, fileKey, encAlgorithm, keyLength, objNum, objGen);
         obj1.free();
       }
       outStr->printf("] ");
       break;
     case objDict:
-      writeDictionnary (obj->getDict(),outStr, xRef, numOffset);
+      writeDictionnary (obj->getDict(), outStr, xRef, numOffset, fileKey, encAlgorithm, keyLength, objNum, objGen);
       break;
     case objStream: 
       {
@@ -1080,6 +1137,15 @@
         Stream *stream = obj->getStream();
         if (stream->getKind() == strWeird) {
           //we write the stream unencoded => TODO: write stream encoder
+
+          // Encrypt stream
+          EncryptStream *encStream = NULL;
+          if (fileKey) {
+            encStream = new EncryptStream(stream, fileKey, encAlgorithm, keyLength, objNum, objGen);
+            encStream->setAutoDelete(gFalse);
+            stream = encStream;
+          }
+
           stream->reset();
           //recalculate stream length
           tmp = 0;
@@ -1093,8 +1159,9 @@
           stream->getDict()->remove("Filter");
           stream->getDict()->remove("DecodeParms");
 
-          writeDictionnary (stream->getDict(),outStr, xRef, numOffset);
+          writeDictionnary (stream->getDict(),outStr, xRef, numOffset, fileKey, encAlgorithm, keyLength, objNum, objGen);
           writeStream (stream,outStr);
+          delete encStream;
           obj1.free();
         } else {
           //raw stream copy
@@ -1110,7 +1177,7 @@
                 }
               }
           }
-          writeDictionnary (stream->getDict(), outStr, xRef, numOffset);
+          writeDictionnary (stream->getDict(), outStr, xRef, numOffset, fileKey, encAlgorithm, keyLength, objNum, objGen);
           writeRawStream (stream, outStr);
         }
         break;
@@ -1134,9 +1201,11 @@
       error(errUnimplemented, -1,"Unhandled objType : {0:d}, please report a bug with a testcase\r\n", obj->getType());
       break;
   }
-  if (ref)
-    outStr->printf("endobj\r\n");
-  return offset;
+}
+
+void PDFDoc::writeObjectFooter (OutStream* outStr)
+{
+  outStr->printf("endobj\r\n");
 }
 
 Dict *PDFDoc::createTrailerDict(int uxrefSize, GBool incrUpdate, Guint startxRef,
@@ -1177,6 +1246,17 @@
   }
   obj1.free();
 
+  GBool hasEncrypt = gFalse;
+  if (!xRef->getTrailerDict()->isNone()) {
+    Object obj2;
+    xRef->getTrailerDict()->dictLookupNF("Encrypt", &obj2);
+    if (!obj2.isNull()) {
+      trailerDict->set("Encrypt", &obj2);
+      hasEncrypt = gTrue;
+      obj2.free();
+    }
+  }
+
   //calculate md5 digest
   Guchar digest[16];
   md5((Guchar*)message.getCString(), message.getLength(), digest);
@@ -1186,7 +1266,8 @@
   Object obj2,obj3,obj5;
   obj2.initArray(xRef);
 
-  if (incrUpdate) {
+  // In case of encrypted files, the ID must not be changed because it's used to calculate the key
+  if (incrUpdate || hasEncrypt) {
     Object obj4;
     //only update the second part of the array
     xRef->getTrailerDict()->getDict()->lookup("ID", &obj4);
@@ -1231,7 +1312,7 @@
 {
   uxref->writeTableToFile( outStr, writeAllEntries );
   outStr->printf( "trailer\r\n");
-  writeDictionnary(trailerDict, outStr, xRef, 0);
+  writeDictionnary(trailerDict, outStr, xRef, 0, NULL, cryptRC4, 0, 0, 0);
   outStr->printf( "\r\nstartxref\r\n");
   outStr->printf( "%i\r\n", uxrefOffset);
   outStr->printf( "%%%%EOF\r\n");
@@ -1248,7 +1329,9 @@
   Object obj1;
   MemStream *mStream = new MemStream( stmData.getCString(), 0,
                                       stmData.getLength(), obj1.initDict(trailerDict) );
-  writeObject(obj1.initStream(mStream), uxrefStreamRef, outStr, xRef, 0);
+  writeObjectHeader(uxrefStreamRef, outStr);
+  writeObject(obj1.initStream(mStream), outStr, xRef, 0, NULL, cryptRC4, 0, 0, 0);
+  writeObjectFooter(outStr);
   obj1.free();
 
   outStr->printf( "startxref\r\n");
@@ -1435,7 +1518,8 @@
     const char *key = pageDict->getKey(n);
     Object value; pageDict->getValNF(n, &value);
     if (strcmp(key, "Parent") != 0 &&
-	      strcmp(key, "Pages") != 0) {
+	      strcmp(key, "Pages") != 0 &&
+        strcmp(key, "Root") != 0) {
       markObject(&value, xRef, countRef, numOffset);
     }
     value.free();
@@ -1445,6 +1529,10 @@
 Guint PDFDoc::writePageObjects(OutStream *outStr, XRef *xRef, Guint numOffset) 
 {
   Guint objectsCount = 0; //count the number of objects in the XRef(s)
+  Guchar *fileKey;

+  CryptAlgorithm encAlgorithm;

+  int keyLength;

+  xRef->getEncryptionParameters(&fileKey, &encAlgorithm, &keyLength);

 
   for (int n = numOffset; n < xRef->getNumObjects(); n++) {
     if (xRef->getEntry(n)->type != xrefEntryFree) {
@@ -1454,7 +1542,13 @@
       ref.gen = xRef->getEntry(n)->gen;
       objectsCount++;
       getXRef()->fetch(ref.num - numOffset, ref.gen, &obj);
-      Guint offset = writeObject(&obj, &ref, outStr, xRef, numOffset);
+      Guint offset = writeObjectHeader(&ref, outStr);
+      if (xRef->getEntry(n)->getFlag(XRefEntry::Unencrypted)) {

+        writeObject(&obj, outStr, NULL, cryptRC4, 0, 0, 0);

+      } else {

+        writeObject(&obj, outStr, fileKey, encAlgorithm, keyLength, ref.num, ref.gen);

+      }

+      writeObjectFooter(outStr);
       xRef->add(ref.num, ref.gen, offset, gTrue);
       obj.free();
     }
diff --git a/poppler/PDFDoc.h b/poppler/PDFDoc.h
index 468f698..f3e9f68 100644
--- a/poppler/PDFDoc.h
+++ b/poppler/PDFDoc.h
@@ -43,6 +43,7 @@
 #include "Page.h"
 #include "Annot.h"
 #include "OptionalContent.h"
+#include "Stream.h"
 
 class GooString;
 class BaseStream;
@@ -243,7 +244,8 @@
   void markPageObjects(Dict *pageDict, XRef *xRef, XRef *countRef, Guint numOffset);
   // write all objects used by pageDict to outStr
   Guint writePageObjects(OutStream *outStr, XRef *xRef, Guint numOffset);
-  static Guint writeObject (Object *obj, Ref *ref, OutStream* outStr, XRef *xref, Guint numOffset);
+  static void writeObject (Object *obj, OutStream* outStr, XRef *xref, Guint numOffset, Guchar *fileKey,
+                           CryptAlgorithm encAlgorithm, int keyLength, int objNum, int objGen);
   static void writeHeader(OutStream *outStr, int major, int minor);
 
   // Ownership goes to the caller
@@ -258,18 +260,25 @@
   // insert referenced objects in XRef
   void markDictionnary (Dict* dict, XRef *xRef, XRef *countRef, Guint numOffset);
   void markObject (Object *obj, XRef *xRef, XRef *countRef, Guint numOffset);
-  static void writeDictionnary (Dict* dict, OutStream* outStr, XRef *xRef, Guint numOffset);
+  static void writeDictionnary (Dict* dict, OutStream* outStr, XRef *xRef, Guint numOffset, Guchar *fileKey,
+                                CryptAlgorithm encAlgorithm, int keyLength, int objNum, int objGen);
 
-  // Add object to current file stream and return the offset of the beginning of the object
-  Guint writeObject (Object *obj, Ref *ref, OutStream* outStr)
-  { return writeObject(obj, ref, outStr, getXRef(), 0); }
-  void writeDictionnary (Dict* dict, OutStream* outStr)
-  { writeDictionnary(dict, outStr, getXRef(), 0); }
+  // Write object header to current file stream and return its offset
+  static Guint writeObjectHeader (Ref *ref, OutStream* outStr);
+  static void writeObjectFooter (OutStream* outStr);
+
+  void writeObject (Object *obj, OutStream* outStr, Guchar *fileKey, CryptAlgorithm encAlgorithm,
+                    int keyLength, int objNum, int objGen)
+  { writeObject(obj, outStr, getXRef(), 0, fileKey, encAlgorithm, keyLength, objNum, objGen); }
+  void writeDictionnary (Dict* dict, OutStream* outStr, Guchar *fileKey, CryptAlgorithm encAlgorithm,
+                         int keyLength, int objNum, int objGen)
+  { writeDictionnary(dict, outStr, getXRef(), 0, fileKey, encAlgorithm, keyLength, objNum, objGen); }
   static void writeStream (Stream* str, OutStream* outStr);
   static void writeRawStream (Stream* str, OutStream* outStr);
   void writeXRefTableTrailer (Guint uxrefOffset, XRef *uxref, GBool writeAllEntries,
                               int uxrefSize, OutStream* outStr, GBool incrUpdate);
-  static void writeString (GooString* s, OutStream* outStr);
+  static void writeString (GooString* s, OutStream* outStr, Guchar *fileKey,
+                           CryptAlgorithm encAlgorithm, int keyLength, int objNum, int objGen);
   void saveIncrementalUpdate (OutStream* outStr);
   void saveCompleteRewrite (OutStream* outStr);
 
diff --git a/poppler/PSOutputDev.cc b/poppler/PSOutputDev.cc
index e4c925c..95be97f 100644
--- a/poppler/PSOutputDev.cc
+++ b/poppler/PSOutputDev.cc
@@ -15,7 +15,7 @@
 //
 // Copyright (C) 2005 Martin Kretzschmar <martink@gnome.org>
 // Copyright (C) 2005, 2006 Kristian Høgsberg <krh@redhat.com>
-// Copyright (C) 2006-2009, 2011 Albert Astals Cid <aacid@kde.org>
+// Copyright (C) 2006-2009, 2011, 2012 Albert Astals Cid <aacid@kde.org>
 // Copyright (C) 2006 Jeff Muizelaar <jeff@infidigm.net>
 // Copyright (C) 2007, 2008 Brad Hards <bradh@kde.org>
 // Copyright (C) 2008, 2009 Koji Otani <sho@bbr.jp>
@@ -1470,11 +1470,8 @@
     }
     writePSFmt("%%BoundingBox: {0:d} {1:d} {2:d} {3:d}\n",
 	       (int)floor(x1), (int)floor(y1), (int)ceil(x2), (int)ceil(y2));
-    if (floor(x1) != ceil(x1) || floor(y1) != ceil(y1) ||
-	floor(x2) != ceil(x2) || floor(y2) != ceil(y2)) {
-      writePSFmt("%%HiResBoundingBox: {0:.6g} {1:.6g} {2:.6g} {3:.6g}\n",
+    writePSFmt("%%HiResBoundingBox: {0:.6g} {1:.6g} {2:.6g} {3:.6g}\n",
 		 x1, y1, x2, y2);
-    }
     writePS("%%DocumentSuppliedResources: (atend)\n");
     writePS("%%EndComments\n");
     break;
diff --git a/poppler/Page.cc b/poppler/Page.cc
index eccc198..87bc3a4 100644
--- a/poppler/Page.cc
+++ b/poppler/Page.cc
@@ -348,7 +348,7 @@
 Annots *Page::getAnnots() {
   if (!annots) {
     Object obj;
-    annots = new Annots(doc, getAnnots(&obj));
+    annots = new Annots(doc, num, getAnnots(&obj));
     obj.free();
   }
 
@@ -391,8 +391,7 @@
   }
 
   annots->appendAnnot(annot);
-
-  annot->setPage(&pageRef, num);
+  annot->setPage(num, gTrue);
 }
 
 void Page::removeAnnot(Annot *annot) {
@@ -428,6 +427,8 @@
     }
   }
   annArray.free();
+  annot->removeReferencedObjects(); // Note: Might recurse in removeAnnot again
+  annot->setPage(0, gFalse);
 }
 
 Links *Page::getLinks() {
diff --git a/poppler/Page.h b/poppler/Page.h
index e2e666c..78cedc4 100644
--- a/poppler/Page.h
+++ b/poppler/Page.h
@@ -164,6 +164,7 @@
   Dict *getPieceInfo() { return attrs->getPieceInfo(); }
   Dict *getSeparationInfo() { return attrs->getSeparationInfo(); }
   PDFDoc *getDoc() { return doc; }
+  Ref getRef() { return pageRef; }
 
   // Get resource dictionary.
   Dict *getResourceDict() { return attrs->getResourceDict(); }
@@ -173,7 +174,6 @@
   // Add a new annotation to the page
   void addAnnot(Annot *annot);
   // Remove an existing annotation from the page
-  // Note: Caller is responsible for deleting popup and appearance streams too
   void removeAnnot(Annot *annot);
 
   // Return a list of links.
diff --git a/poppler/SplashOutputDev.cc b/poppler/SplashOutputDev.cc
index 79c4900..de09f49 100644
--- a/poppler/SplashOutputDev.cc
+++ b/poppler/SplashOutputDev.cc
@@ -94,6 +94,7 @@
   GfxRGB rgb;
 #if SPLASH_CMYK
   GfxCMYK cmyk;
+  GfxColor deviceN;
 #endif
 
   // make gcc happy
@@ -124,6 +125,11 @@
       color[2] = colToByte(cmyk.y);
       color[3] = colToByte(cmyk.k);
     break;
+    case splashModeDeviceN8:
+      colorSpace->getDeviceN(src, &deviceN);
+      for (int i = 0; i < SPOT_NCOMPS + 4; i++)
+        color[i] = colToByte(deviceN.c[i]);
+    break;
 #endif
   }
   splashColorCopy(dest, color);
@@ -154,6 +160,8 @@
 #if SPLASH_CMYK
   if (mode == splashModeCMYK8)
     colorComps=4;
+  else if (mode == splashModeDeviceN8)
+    colorComps=4 + SPOT_NCOMPS;
 #endif
 
   shading->getParameterizedColor(colorinterp, &src);
@@ -436,7 +444,7 @@
   int i;
 
 #if SPLASH_CMYK
-  if (cm == splashModeCMYK8) {
+  if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
     SplashColor rgbSrc;
     SplashColor rgbDest;
     SplashColor rgbBlend;
@@ -460,7 +468,7 @@
   int i;
 
 #if SPLASH_CMYK
-  if (cm == splashModeCMYK8) {
+  if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
     SplashColor rgbSrc;
     SplashColor rgbDest;
     SplashColor rgbBlend;
@@ -484,7 +492,7 @@
   int i;
 
 #if SPLASH_CMYK
-  if (cm == splashModeCMYK8) {
+  if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
     SplashColor rgbSrc;
     SplashColor rgbDest;
     SplashColor rgbBlend;
@@ -512,7 +520,7 @@
   int i;
 
 #if SPLASH_CMYK
-  if (cm == splashModeCMYK8) {
+  if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
     SplashColor rgbSrc;
     SplashColor rgbDest;
     SplashColor rgbBlend;
@@ -536,7 +544,7 @@
   int i;
 
 #if SPLASH_CMYK
-  if (cm == splashModeCMYK8) {
+  if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
     SplashColor rgbSrc;
     SplashColor rgbDest;
     SplashColor rgbBlend;
@@ -561,7 +569,7 @@
   int i, x;
 
 #if SPLASH_CMYK
-  if (cm == splashModeCMYK8) {
+  if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
     SplashColor rgbSrc;
     SplashColor rgbDest;
     SplashColor rgbBlend;
@@ -595,7 +603,7 @@
   int i, x;
 
 #if SPLASH_CMYK
-  if (cm == splashModeCMYK8) {
+  if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
     SplashColor rgbSrc;
     SplashColor rgbDest;
     SplashColor rgbBlend;
@@ -629,7 +637,7 @@
   int i;
 
 #if SPLASH_CMYK
-  if (cm == splashModeCMYK8) {
+  if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
     SplashColor rgbSrc;
     SplashColor rgbDest;
     SplashColor rgbBlend;
@@ -657,7 +665,7 @@
   int i, x;
 
 #if SPLASH_CMYK
-  if (cm == splashModeCMYK8) {
+  if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
     SplashColor rgbSrc;
     SplashColor rgbDest;
     SplashColor rgbBlend;
@@ -700,7 +708,7 @@
   int i;
 
 #if SPLASH_CMYK
-  if (cm == splashModeCMYK8) {
+  if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
     SplashColor rgbSrc;
     SplashColor rgbDest;
     SplashColor rgbBlend;
@@ -724,7 +732,7 @@
   int i;
 
 #if SPLASH_CMYK
-  if (cm == splashModeCMYK8) {
+  if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
     SplashColor rgbSrc;
     SplashColor rgbDest;
     SplashColor rgbBlend;
@@ -859,6 +867,7 @@
     break;
 #if SPLASH_CMYK
   case splashModeCMYK8:
+  case splashModeDeviceN8:
     for (i = 0; i < 4; i++) {
       // convert to additive
       src2[i] = 0xff - src[i];
@@ -908,6 +917,7 @@
     break;
 #if SPLASH_CMYK
   case splashModeCMYK8:
+  case splashModeDeviceN8:
     for (i = 0; i < 4; i++) {
       // convert to additive
       src2[i] = 0xff - src[i];
@@ -952,6 +962,7 @@
     break;
 #if SPLASH_CMYK
   case splashModeCMYK8:
+  case splashModeDeviceN8:
     for (i = 0; i < 4; i++) {
       // convert to additive
       src2[i] = 0xff - src[i];
@@ -995,6 +1006,7 @@
     break;
 #if SPLASH_CMYK
   case splashModeCMYK8:
+  case splashModeDeviceN8:
     for (i = 0; i < 4; i++) {
       // convert to additive
       src2[i] = 0xff - src[i];
@@ -1391,6 +1403,10 @@
   case splashModeCMYK8:
     color[0] = color[1] = color[2] = color[3] = 0;
     break;
+  case splashModeDeviceN8:
+    for (int i = 0; i < 4 + SPOT_NCOMPS; i++)
+      color[i] = 0;
+    break;
 #endif
   }
   splash->setStrokePattern(new SplashSolidColor(color));
@@ -1429,7 +1445,9 @@
   updateFlatness(state);
   updateMiterLimit(state);
   updateStrokeAdjust(state);
+  updateFillColorSpace(state);
   updateFillColor(state);
+  updateStrokeColorSpace(state);
   updateStrokeColor(state);
   needFontUpdate = gTrue;
 }
@@ -1500,11 +1518,26 @@
 #endif
 }
 
+void SplashOutputDev::updateFillColorSpace(GfxState *state) {
+#if SPLASH_CMYK
+  if (colorMode == splashModeDeviceN8)
+    state->getFillColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS);
+#endif
+}
+
+void SplashOutputDev::updateStrokeColorSpace(GfxState *state) {
+#if SPLASH_CMYK
+  if (colorMode == splashModeDeviceN8)
+    state->getStrokeColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS);
+#endif
+}
+
 void SplashOutputDev::updateFillColor(GfxState *state) {
   GfxGray gray;
   GfxRGB rgb;
 #if SPLASH_CMYK
   GfxCMYK cmyk;
+  GfxColor deviceN;
 #endif
 
   switch (colorMode) {
@@ -1524,6 +1557,10 @@
     state->getFillCMYK(&cmyk);
     splash->setFillPattern(getColor(&cmyk));
     break;
+  case splashModeDeviceN8:
+    state->getFillDeviceN(&deviceN);
+    splash->setFillPattern(getColor(&deviceN));
+    break;
 #endif
   }
 }
@@ -1533,6 +1570,7 @@
   GfxRGB rgb;
 #if SPLASH_CMYK
   GfxCMYK cmyk;
+  GfxColor deviceN;
 #endif
 
   switch (colorMode) {
@@ -1552,6 +1590,10 @@
     state->getStrokeCMYK(&cmyk);
     splash->setStrokePattern(getColor(&cmyk));
     break;
+  case splashModeDeviceN8:
+    state->getStrokeDeviceN(&deviceN);
+    splash->setStrokePattern(getColor(&deviceN));
+    break;
 #endif
   }
 }
@@ -1596,6 +1638,14 @@
   color[3] = colToByte(cmyk->k);
   return new SplashSolidColor(color);
 }
+
+SplashPattern *SplashOutputDev::getColor(GfxColor *deviceN) {
+  SplashColor color;
+
+  for (int i = 0; i < 4 + SPOT_NCOMPS; i++)
+    color[i] = colToByte(deviceN->c[i]);
+  return new SplashSolidColor(color);
+}
 #endif
 
 void SplashOutputDev::setOverprintMask(GfxColorSpace *colorSpace,
@@ -2725,6 +2775,7 @@
   GfxGray gray;
 #if SPLASH_CMYK
   GfxCMYK cmyk;
+  GfxColor deviceN;
 #endif
   int nComps, x;
 
@@ -2773,6 +2824,13 @@
 	*q++ = col[3];
       }
       break;
+    case splashModeDeviceN8:
+      for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
+	col = &imgData->lookup[(SPOT_NCOMPS+4) * *p];
+  for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
+    *q++ = col[cp];
+      }
+      break;
 #endif
     }
   } else {
@@ -2812,6 +2870,13 @@
 	*q++ = colToByte(cmyk.k);
       }
       break;
+    case splashModeDeviceN8:
+      for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) {
+	imgData->colorMap->getDeviceN(p, &deviceN);
+  for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
+    *q++ = colToByte(deviceN.c[cp]);
+      }
+      break;
 #endif
     }
   }
@@ -2829,6 +2894,7 @@
   GfxGray gray;
 #if SPLASH_CMYK
   GfxCMYK cmyk;
+  GfxColor deviceN;
 #endif
   Guchar alpha;
   int nComps, x, i;
@@ -2881,6 +2947,11 @@
 	*q++ = col[2];
 	*q++ = col[3];
 	break;
+      case splashModeDeviceN8:
+	col = &imgData->lookup[(SPOT_NCOMPS+4) * *p];
+  for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
+    *q++ = col[cp];
+  break;
 #endif
       }
       *aq++ = alpha;
@@ -2908,6 +2979,11 @@
 	*q++ = colToByte(cmyk.y);
 	*q++ = colToByte(cmyk.k);
 	break;
+    case splashModeDeviceN8:
+	imgData->colorMap->getDeviceN(p, &deviceN);
+  for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
+    *q++ = colToByte(deviceN.c[cp]);
+	break;
 #endif
       }
       *aq++ = alpha;
@@ -2984,7 +3060,7 @@
         imgData->pattern->getColor(x, imgData->y, pat);
         for (int i = 0; i < splashColorModeNComps[imgData->colorMode]; ++i) {
 #if SPLASH_CMYK
-          if (imgData->colorMode == splashModeCMYK8)
+          if (imgData->colorMode == splashModeCMYK8 || imgData->colorMode == splashModeDeviceN8)
             dest[i] = div255(pat[i] * (255 - col[0]));
           else
 #endif
@@ -3025,6 +3101,7 @@
 #if SPLASH_CMYK
   GfxCMYK cmyk;
   GBool grayIndexed = gFalse;
+  GfxColor deviceN;
 #endif
   Guchar pix;
   int n, i;
@@ -3104,6 +3181,21 @@
 	imgData.lookup[4*i+3] = colToByte(cmyk.k);
       }
       break;
+    case splashModeDeviceN8:
+      colorMap->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS);
+      grayIndexed = colorMap->getColorSpace()->getMode() != csDeviceGray;
+      imgData.lookup = (SplashColorPtr)gmallocn(n, SPOT_NCOMPS+4);
+      for (i = 0; i < n; ++i) {
+        pix = (Guchar)i;
+        colorMap->getCMYK(&pix, &cmyk);
+        if (cmyk.c != 0 || cmyk.m != 0 || cmyk.y != 0) {
+          grayIndexed = gFalse;
+        }
+        colorMap->getDeviceN(&pix, &deviceN);
+        for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
+          imgData.lookup[(SPOT_NCOMPS+4)*i +cp] = colToByte(deviceN.c[cp]);
+      }
+      break;
 #endif
     }
   }
@@ -3154,6 +3246,7 @@
   GfxGray gray;
 #if SPLASH_CMYK
   GfxCMYK cmyk;
+  GfxColor deviceN;
 #endif
   Guchar alpha;
   Guchar *maskPtr;
@@ -3208,6 +3301,11 @@
 	*q++ = col[2];
 	*q++ = col[3];
 	break;
+      case splashModeDeviceN8:
+	col = &imgData->lookup[(SPOT_NCOMPS+4) * *p];
+  for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
+    *q++ = col[cp];
+	break;
 #endif
       }
       *aq++ = alpha;
@@ -3235,6 +3333,11 @@
 	*q++ = colToByte(cmyk.y);
 	*q++ = colToByte(cmyk.k);
 	break;
+      case splashModeDeviceN8:
+	imgData->colorMap->getDeviceN(p, &deviceN);
+  for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
+    *q++ = colToByte(deviceN.c[cp]);
+	break;
 #endif
       }
       *aq++ = alpha;
@@ -3266,10 +3369,14 @@
   GfxRGB rgb;
 #if SPLASH_CMYK
   GfxCMYK cmyk;
+  GfxColor deviceN;
 #endif
   Guchar pix;
   int n, i;
 
+#if SPLASH_CMYK
+  colorMap->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS);
+#endif
   setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(),
 		   state->getOverprintMode(), NULL);
 
@@ -3391,6 +3498,15 @@
 	  imgData.lookup[4*i+3] = colToByte(cmyk.k);
 	}
 	break;
+      case splashModeDeviceN8:
+	imgData.lookup = (SplashColorPtr)gmallocn(n, SPOT_NCOMPS+4);
+	for (i = 0; i < n; ++i) {
+	  pix = (Guchar)i;
+	  colorMap->getDeviceN(&pix, &deviceN);
+    for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
+      imgData.lookup[(SPOT_NCOMPS+4)*i + cp] = colToByte(deviceN.c[cp]);
+	}
+	break;
 #endif
       }
     }
@@ -3429,10 +3545,14 @@
   GfxRGB rgb;
 #if SPLASH_CMYK
   GfxCMYK cmyk;
+  GfxColor deviceN;
 #endif
   Guchar pix;
   int n, i;
 
+#if SPLASH_CMYK
+  colorMap->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS);
+#endif
   setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(),
 		   state->getOverprintMode(), NULL);
 
@@ -3541,6 +3661,15 @@
 	imgData.lookup[4*i+3] = colToByte(cmyk.k);
       }
       break;
+    case splashModeDeviceN8:
+      imgData.lookup = (SplashColorPtr)gmallocn(n, SPOT_NCOMPS+4);
+      for (i = 0; i < n; ++i) {
+	pix = (Guchar)i;
+	colorMap->getDeviceN(&pix, &deviceN);
+  for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
+    imgData.lookup[(SPOT_NCOMPS+4)*i + cp] = colToByte(deviceN.c[cp]);
+      }
+      break;
 #endif
     }
   }
@@ -3684,7 +3813,7 @@
 
   // create the temporary bitmap
   bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue,
-			    bitmapTopDown);
+			    bitmapTopDown, bitmap->getSeparationList());
   splash = new Splash(bitmap, vectorAntialias,
 		      transpGroup->origSplash->getScreen());
   splash->setMinLineWidth(globalParams->getMinLineWidth());
@@ -3773,6 +3902,7 @@
   GfxRGB rgb;
 #if SPLASH_CMYK
   GfxCMYK cmyk;
+  GfxColor deviceN;
 #endif
   double lum, lum2;
   int tx, ty, x, y;
@@ -3816,6 +3946,12 @@
 	color[3] = colToByte(cmyk.k);
 	tSplash->compositeBackground(color);
 	break;
+      case splashModeDeviceN8:
+	transpGroupStack->blendingColorSpace->getDeviceN(backdropColor, &deviceN);
+  for (int cp=0; cp < SPOT_NCOMPS+4; cp++)
+    color[cp] = colToByte(deviceN.c[cp]);
+	tSplash->compositeBackground(color);
+	break;
 #endif
       }
       delete tSplash;
@@ -3857,6 +3993,7 @@
 	    break;
 #if SPLASH_CMYK
 	  case splashModeCMYK8:
+    case splashModeDeviceN8:
 	    lum = (1 - color[3] / 255.0)
 	          - (0.3 / 255.0) * color[0]
 	          - (0.59 / 255.0) * color[1]
@@ -3955,6 +4092,7 @@
   Matrix m1;
   double *ctm, savedCTM[6];
   double kx, ky, sx, sy;
+  GBool retValue = gFalse;
 
   width = bbox[2] - bbox[0];
   height = bbox[3] - bbox[1];
@@ -4014,6 +4152,8 @@
     repeatX = x1 - x0;
     repeatY = y1 - y0;
   } else {
+    if ((unsigned long) result_width * result_height > 0x800000L)
+      return gFalse;
     while(fabs(kx) > 16384 || fabs(ky) > 16384) {
       // limit pattern bitmap size
       m1.m[0] /= 2;
@@ -4055,7 +4195,9 @@
   memset(bitmap->getAlphaPtr(), 0, bitmap->getWidth() * bitmap->getHeight());
   if (paintType == 2) {
 #if SPLASH_CMYK
-    memset(bitmap->getDataPtr(), (colorMode == splashModeCMYK8) ? 0x00 : 0xFF, bitmap->getRowSize() * bitmap->getHeight());
+    memset(bitmap->getDataPtr(), 
+      (colorMode == splashModeCMYK8 || colorMode == splashModeDeviceN8) ? 0x00 : 0xFF, 
+      bitmap->getRowSize() * bitmap->getHeight());
 #else
     memset(bitmap->getDataPtr(), 0xFF, bitmap->getRowSize() * bitmap->getHeight());
 #endif
@@ -4099,10 +4241,10 @@
   matc[1] = ctm[1];
   matc[2] = ctm[2];
   matc[3] = ctm[3];
-  splash->drawImage(&tilingBitmapSrc, &imgData, colorMode, gTrue, result_width, result_height, matc, gTrue);
+  retValue = splash->drawImage(&tilingBitmapSrc, &imgData, colorMode, gTrue, result_width, result_height, matc, gTrue) == splashOk;
   delete tBitmap;
   delete gfx;
-  return gTrue;
+  return retValue;
 }
 
 GBool SplashOutputDev::gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading)
@@ -4115,7 +4257,8 @@
     break;
 #if SPLASH_CMYK
     case splashModeCMYK8:
-      bDirectColorTranslation = (shadingMode == csDeviceCMYK);
+    case splashModeDeviceN8:
+      bDirectColorTranslation = (shadingMode == csDeviceCMYK || shadingMode == csDeviceN);
     break;
 #endif
     default:
@@ -4185,6 +4328,9 @@
   state->closePath();
   path = convertPath(state, state->getPath(), gTrue);
 
+#if SPLASH_CMYK
+  pattern->getShading()->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS);
+#endif
   setOverprintMask(pattern->getShading()->getColorSpace(), state->getFillOverprint(),
 		   state->getOverprintMode(), state->getFillColor());
   retVal = (splash->shadedFill(path, pattern->getShading()->getHasBBox(), pattern) == splashOk);
diff --git a/poppler/SplashOutputDev.h b/poppler/SplashOutputDev.h
index f1c87ec..de7934d 100644
--- a/poppler/SplashOutputDev.h
+++ b/poppler/SplashOutputDev.h
@@ -215,6 +215,8 @@
   virtual void updateMiterLimit(GfxState *state);
   virtual void updateLineWidth(GfxState *state);
   virtual void updateStrokeAdjust(GfxState *state);
+  virtual void updateFillColorSpace(GfxState *state);
+  virtual void updateStrokeColorSpace(GfxState *state);
   virtual void updateFillColor(GfxState *state);
   virtual void updateStrokeColor(GfxState *state);
   virtual void updateBlendMode(GfxState *state);
@@ -362,6 +364,7 @@
   SplashPattern *getColor(GfxRGB *rgb);
 #if SPLASH_CMYK
   SplashPattern *getColor(GfxCMYK *cmyk);
+  SplashPattern *getColor(GfxColor *deviceN);
 #endif
   void setOverprintMask(GfxColorSpace *colorSpace, GBool overprintFlag,
 			int overprintMode, GfxColor *singleColor, GBool grayIndexed = gFalse);
diff --git a/poppler/Stream.cc b/poppler/Stream.cc
index f287406..842f0c6 100644
--- a/poppler/Stream.cc
+++ b/poppler/Stream.cc
@@ -25,6 +25,7 @@
 // Copyright (C) 2011, 2012 William Bader <williambader@hotmail.com>
 // Copyright (C) 2012 Thomas Freitag <Thomas.Freitag@alfa.de>
 // Copyright (C) 2012 Oliver Sander <sander@mi.fu-berlin.de>
+// Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
 //
 // To see a description of the changes please see the Changelog file that
 // came with your tarball or type make ChangeLog if you are building from git
@@ -4495,7 +4496,12 @@
   delete str;
 }
 
-void FlateStream::unfilteredReset() {
+void FlateStream::flateReset(GBool unfiltered) {
+  if (unfiltered)
+    str->unfilteredReset();
+  else
+    str->reset();
+
   index = 0;
   remain = 0;
   codeBuf = 0;
@@ -4503,14 +4509,16 @@
   compressedBlock = gFalse;
   endOfBlock = gTrue;
   eof = gTrue;
+}
 
-  str->reset();
+void FlateStream::unfilteredReset() {
+  flateReset(gTrue);
 }
 
 void FlateStream::reset() {
   int cmf, flg;
 
-  unfilteredReset();
+  flateReset(gFalse);
 
   // read header
   //~ need to look at window size?
diff --git a/poppler/Stream.h b/poppler/Stream.h
index a270fdf..20b5fd6 100644
--- a/poppler/Stream.h
+++ b/poppler/Stream.h
@@ -21,6 +21,7 @@
 // Copyright (C) 2010 Hib Eris <hib@hiberis.nl>
 // Copyright (C) 2011, 2012 William Bader <williambader@hotmail.com>
 // Copyright (C) 2012 Thomas Freitag <Thomas.Freitag@alfa.de>
+// Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
 //
 // To see a description of the changes please see the Changelog file that
 // came with your tarball or type make ChangeLog if you are building from git
@@ -970,6 +971,7 @@
   virtual void unfilteredReset ();
 
 private:
+  void flateReset(GBool unfiltered);
   inline int doGetRawChar() {
     int c;
 
diff --git a/poppler/TextOutputDev.cc b/poppler/TextOutputDev.cc
index a9c9d70..2be0b42 100644
--- a/poppler/TextOutputDev.cc
+++ b/poppler/TextOutputDev.cc
@@ -15,10 +15,10 @@
 //
 // Copyright (C) 2005-2007 Kristian Høgsberg <krh@redhat.com>
 // Copyright (C) 2005 Nickolay V. Shmyrev <nshmyrev@yandex.ru>
-// Copyright (C) 2006-2008, 2011 Carlos Garcia Campos <carlosgc@gnome.org>
+// Copyright (C) 2006-2008, 2011, 2012 Carlos Garcia Campos <carlosgc@gnome.org>
 // Copyright (C) 2006, 2007 Ed Catmur <ed@catmur.co.uk>
 // Copyright (C) 2006 Jeff Muizelaar <jeff@infidigm.net>
-// Copyright (C) 2007, 2008 Adrian Johnson <ajohnson@redneon.com>
+// Copyright (C) 2007, 2008, 2012 Adrian Johnson <ajohnson@redneon.com>
 // Copyright (C) 2008 Koji Otani <sho@bbr.jp>
 // Copyright (C) 2008, 2010-2012 Albert Astals Cid <aacid@kde.org>
 // Copyright (C) 2008 Pino Toscano <pino@kde.org>
@@ -64,7 +64,7 @@
 #include "TextOutputDev.h"
 #include "Page.h"
 #include "Annot.h"
-#include "PDFDocEncoding.h"
+#include "UTF.h"
 
 #ifdef MACOS
 // needed for setting type/creator of MacOS files
@@ -128,7 +128,7 @@
 
 // Minimum spacing between characters within a word, as a fraction of
 // the font size.
-#define minCharSpacing -0.2
+#define minCharSpacing -0.5
 
 // Maximum spacing between characters within a word, as a fraction of
 // the font size, when there is no obvious extra-wide character
@@ -235,104 +235,14 @@
 // TextWord
 //------------------------------------------------------------------------
 
-TextWord::TextWord(GfxState *state, int rotA, double x0, double y0,
-		   TextFontInfo *fontA, double fontSizeA) {
-  GfxFont *gfxFont;
-  double x, y, ascent, descent;
-  int wMode;
-  
+TextWord::TextWord(GfxState *state, int rotA, double fontSizeA) {
   rot = rotA;
-  font = fontA;
   fontSize = fontSizeA;
-  state->transform(x0, y0, &x, &y);
-  if ((gfxFont = font->gfxFont)) {
-    ascent = gfxFont->getAscent() * fontSize;
-    descent = gfxFont->getDescent() * fontSize;
-    wMode = gfxFont->getWMode();
-  } else {
-    // this means that the PDF file draws text without a current font,
-    // which should never happen
-    ascent = 0.95 * fontSize;
-    descent = -0.35 * fontSize;
-    wMode = 0;
-  }
-  if (wMode) { // vertical writing mode
-    // NB: the rotation value has been incremented by 1 (in
-    // TextPage::beginWord()) for vertical writing mode
-    switch (rot) {
-    case 0:
-      yMin = y - fontSize;
-      yMax = y;
-      base = y;
-      break;
-    case 1:
-      xMin = x;
-      xMax = x + fontSize;
-      base = x;
-      break;
-    case 2:
-      yMin = y;
-      yMax = y + fontSize;
-      base = y;
-      break;
-    case 3:
-      xMin = x - fontSize;
-      xMax = x;
-      base = x;
-      break;
-    }
-  } else { // horizontal writing mode
-    switch (rot) {
-    case 0:
-      yMin = y - ascent;
-      yMax = y - descent;
-      if (yMin == yMax) {
-	// this is a sanity check for a case that shouldn't happen -- but
-	// if it does happen, we want to avoid dividing by zero later
-	yMin = y;
-	yMax = y + 1;
-      }
-      base = y;
-      break;
-    case 1:
-      xMin = x + descent;
-      xMax = x + ascent;
-      if (xMin == xMax) {
-	// this is a sanity check for a case that shouldn't happen -- but
-	// if it does happen, we want to avoid dividing by zero later
-	xMin = x;
-	xMax = x + 1;
-      }
-      base = x;
-      break;
-    case 2:
-      yMin = y + descent;
-      yMax = y + ascent;
-      if (yMin == yMax) {
-	// this is a sanity check for a case that shouldn't happen -- but
-	// if it does happen, we want to avoid dividing by zero later
-	yMin = y;
-	yMax = y + 1;
-      }
-      base = y;
-      break;
-    case 3:
-      xMin = x - ascent;
-      xMax = x - descent;
-      if (xMin == xMax) {
-	// this is a sanity check for a case that shouldn't happen -- but
-	// if it does happen, we want to avoid dividing by zero later
-	xMin = x;
-	xMax = x + 1;
-      }
-      base = x;
-      break;
-    }
-  }
   text = NULL;
   charcode = NULL;
   edge = NULL;
   charPos = NULL;
+  font = NULL;
   len = size = 0;
   spaceAfter = gFalse;
   next = NULL;
@@ -359,12 +269,15 @@
   gfree(charcode);
   gfree(edge);
   gfree(charPos);
+  gfree(font);
 }
 
-void TextWord::addChar(GfxState *state, double x, double y,
+void TextWord::addChar(GfxState *state, TextFontInfo *fontA, double x, double y,
 		       double dx, double dy, int charPosA, int charLen,
 		       CharCode c, Unicode u) {
-  int wMode;
+  GfxFont *gfxFont;
+  double ascent, descent;
+  ascent = descent = 0; // make gcc happy
 
   if (len == size) {
     size += 16;
@@ -372,12 +285,28 @@
     charcode = (Unicode *)greallocn(charcode, size, sizeof(CharCode));
     edge = (double *)greallocn(edge, (size + 1), sizeof(double));
     charPos = (int *)greallocn(charPos, size + 1, sizeof(int));
+    font = (TextFontInfo **)greallocn(font, size, sizeof(TextFontInfo *));
   }
   text[len] = u;
   charcode[len] = c;
   charPos[len] = charPosA;
   charPos[len + 1] = charPosA + charLen;
-  wMode = font->gfxFont ? font->gfxFont->getWMode() : 0;
+  font[len] = fontA;
+
+  if (len == 0) {
+    if ((gfxFont = fontA->gfxFont)) {
+      ascent = gfxFont->getAscent() * fontSize;
+      descent = gfxFont->getDescent() * fontSize;
+      wMode = gfxFont->getWMode();
+    } else {
+      // this means that the PDF file draws text without a current font,
+      // which should never happen
+      ascent = 0.95 * fontSize;
+      descent = -0.35 * fontSize;
+      wMode = 0;
+    }
+  }
+
   if (wMode) { // vertical writing mode
     // NB: the rotation value has been incremented by 1 (in
     // TextPage::beginWord()) for vertical writing mode
@@ -385,27 +314,39 @@
     case 0:
       if (len == 0) {
 	xMin = x - fontSize;
+	yMin = y - fontSize;
+	yMax = y;
+	base = y;
       }
       edge[len] = x - fontSize;
       xMax = edge[len+1] = x;
       break;
     case 1:
       if (len == 0) {
+	xMin = x;
 	yMin = y - fontSize;
+	xMax = x + fontSize;
+	base = x;
       }
       edge[len] = y - fontSize;
       yMax = edge[len+1] = y;
       break;
     case 2:
       if (len == 0) {
+	yMin = y;
 	xMax = x + fontSize;
+	yMax = y + fontSize;
+	base = y;
       }
       edge[len] = x + fontSize;
       xMin = edge[len+1] = x;
       break;
     case 3:
       if (len == 0) {
+	xMin = x - fontSize;
+	xMax = x;
 	yMax = y + fontSize;
+	base = x;
       }
       edge[len] = y + fontSize;
       yMin = edge[len+1] = y;
@@ -416,27 +357,63 @@
     case 0:
       if (len == 0) {
 	xMin = x;
+	yMin = y - ascent;
+	yMax = y - descent;
+	if (yMin == yMax) {
+	  // this is a sanity check for a case that shouldn't happen -- but
+	  // if it does happen, we want to avoid dividing by zero later
+	  yMin = y;
+	  yMax = y + 1;
+	}
+	base = y;
       }
       edge[len] = x;
       xMax = edge[len+1] = x + dx;
       break;
     case 1:
       if (len == 0) {
+	xMin = x + descent;
 	yMin = y;
+	xMax = x + ascent;
+	if (xMin == xMax) {
+	  // this is a sanity check for a case that shouldn't happen -- but
+	  // if it does happen, we want to avoid dividing by zero later
+	  xMin = x;
+	  xMax = x + 1;
+	}
+	base = x;
       }
       edge[len] = y;
       yMax = edge[len+1] = y + dy;
       break;
     case 2:
       if (len == 0) {
+	yMin = y + descent;
 	xMax = x;
+	yMax = y + ascent;
+	if (yMin == yMax) {
+	  // this is a sanity check for a case that shouldn't happen -- but
+	  // if it does happen, we want to avoid dividing by zero later
+	  yMin = y;
+	  yMax = y + 1;
+	}
+	base = y;
       }
       edge[len] = x;
       xMin = edge[len+1] = x + dx;
       break;
     case 3:
       if (len == 0) {
+	xMin = x - ascent;
+	xMax = x - descent;
 	yMax = y;
+	if (xMin == xMax) {
+	  // this is a sanity check for a case that shouldn't happen -- but
+	  // if it does happen, we want to avoid dividing by zero later
+	  xMin = x;
+	  xMax = x + 1;
+	}
+	base = x;
       }
       edge[len] = y;
       yMin = edge[len+1] = y + dy;
@@ -467,12 +444,14 @@
     charcode = (CharCode *)greallocn(charcode, (size + 1), sizeof(CharCode));
     edge = (double *)greallocn(edge, (size + 1), sizeof(double));
     charPos = (int *)greallocn(charPos, size + 1, sizeof(int));
+    font = (TextFontInfo **)greallocn(font, size, sizeof(TextFontInfo *));
   }
   for (i = 0; i < word->len; ++i) {
     text[len + i] = word->text[i];
     charcode[len + i] = word->charcode[i];
     edge[len + i] = word->edge[i];
     charPos[len + i] = word->charPos[i];
+    font[len + i] = word->font[i];
   }
   edge[len + word->len] = word->edge[word->len];
   charPos[len + word->len] = word->charPos[word->len];
@@ -864,7 +843,7 @@
 	word0->spaceAfter = gTrue;
 	word0 = word1;
 	word1 = word1->next;
-      } else if (word0->font == word1->font &&
+      } else if (word0->font[word0->len - 1] == word1->font[0] &&
 		 word0->underlined == word1->underlined &&
 		 fabs(word0->fontSize - word1->fontSize) <
 		   maxWordFontSizeDelta * words->fontSize &&
@@ -2235,7 +2214,7 @@
   }
 }
 
-void TextPage::beginWord(GfxState *state, double x0, double y0) {
+void TextPage::beginWord(GfxState *state) {
   GfxFont *gfxFont;
   double *fontm;
   double m[4], m2[4];
@@ -2275,7 +2254,7 @@
     rot = (rot + 1) & 3;
   }
 
-  curWord = new TextWord(state, rot, x0, y0, curFont, curFontSize);
+  curWord = new TextWord(state, rot, curFontSize);
 }
 
 void TextPage::addChar(GfxState *state, double x, double y,
@@ -2284,6 +2263,7 @@
   double x1, y1, w1, h1, dx2, dy2, base, sp, delta;
   GBool overlap;
   int i;
+  int wMode;
 
   // subtract char and word spacing from the dx,dy values
   sp = state->getCharSpace();
@@ -2332,6 +2312,7 @@
   // (3) the previous character was an overlap (we want each duplicated
   //     character to be in a word by itself at this stage),
   // (4) the font size has changed
+  // (5) the WMode changed
   if (curWord && curWord->len > 0) {
     base = sp = delta = 0; // make gcc happy
     switch (curWord->rot) {
@@ -2358,11 +2339,13 @@
     }
     overlap = fabs(delta) < dupMaxPriDelta * curWord->fontSize &&
               fabs(base - curWord->base) < dupMaxSecDelta * curWord->fontSize;
+    wMode = curFont->gfxFont ? curFont->gfxFont->getWMode() : 0;
     if (overlap || lastCharOverlap ||
 	sp < -minDupBreakOverlap * curWord->fontSize ||
 	sp > minWordBreakSpace * curWord->fontSize ||
 	fabs(base - curWord->base) > 0.5 ||
-	curFontSize != curWord->fontSize) {
+	curFontSize != curWord->fontSize ||
+	wMode != curWord->wMode) {
       endWord();
     }
     lastCharOverlap = overlap;
@@ -2373,7 +2356,7 @@
   if (uLen != 0) {
     // start a new word if needed
     if (!curWord) {
-      beginWord(state, x, y);
+      beginWord(state);
     }
 
     // page rotation and/or transform matrices can cause text to be
@@ -2384,7 +2367,7 @@
         (curWord->rot == 2 && w1 > 0) ||
         (curWord->rot == 3 && h1 > 0)) {
       endWord();
-      beginWord(state, x + dx, y + dy);
+      beginWord(state);
       x1 += w1;
       y1 += h1;
       w1 = -w1;
@@ -2395,24 +2378,7 @@
     w1 /= uLen;
     h1 /= uLen;
     for (i = 0; i < uLen; ++i) {
-      if (u[i] >= 0xd800 && u[i] < 0xdc00) { /* surrogate pair */
-	if (i + 1 < uLen && u[i+1] >= 0xdc00 && u[i+1] < 0xe000) {
-	  /* next code is a low surrogate */
-	  Unicode uu = (((u[i] & 0x3ff) << 10) | (u[i+1] & 0x3ff)) + 0x10000;
-	  i++;
-	  curWord->addChar(state, x1 + i*w1, y1 + i*h1, w1, h1, charPos, nBytes, c, uu);
-	} else {
-	    /* missing low surrogate
-	     replace it with REPLACEMENT CHARACTER (U+FFFD) */
-	  curWord->addChar(state, x1 + i*w1, y1 + i*h1, w1, h1, charPos, nBytes, c, 0xfffd);
-	}
-      } else if (u[i] >= 0xdc00 && u[i] < 0xe000) {
-	  /* invalid low surrogate
-	   replace it with REPLACEMENT CHARACTER (U+FFFD) */
-	curWord->addChar(state, x1 + i*w1, y1 + i*h1, w1, h1, charPos, nBytes, c, 0xfffd);
-      } else {
-	curWord->addChar(state, x1 + i*w1, y1 + i*h1, w1, h1, charPos, nBytes, c, u[i]);
-      }
+      curWord->addChar(state, curFont, x1 + i*w1, y1 + i*h1, w1, h1, charPos, nBytes, c, u[i]);
     }
   }
   charPos += nBytes;
@@ -3656,76 +3622,80 @@
       j = backward ? m - len : 0;
       p = txt + j;
       while (backward ? j >= 0 : j <= m - len) {
+        if (!wholeWord ||
+            ((j == 0 || !unicodeTypeAlphaNum(txt[j - 1])) &&
+             (j + len == m || !unicodeTypeAlphaNum(txt[j + len])))) {
 
-	// compare the strings
-	for (k = 0; k < len; ++k) {
-	  if (p[k] != s2[k]) {
-	    break;
-	  }
-	}
+          // compare the strings
+          for (k = 0; k < len; ++k) {
+            if (p[k] != s2[k]) {
+              break;
+            }
+          }
 
-	// found it
-	if (k == len) {
-	  // where s2 matches a subsequence of a compatibility equivalence
-	  // decomposition, highlight the entire glyph, since we don't know
-	  // the internal layout of subglyph components
-	  int normStart = line->normalized_idx[j];
-	  int normAfterEnd = line->normalized_idx[j + len - 1] + 1;
-	  switch (line->rot) {
-	  case 0:
-	    xMin1 = line->edge[normStart];
-	    xMax1 = line->edge[normAfterEnd];
-	    yMin1 = line->yMin;
-	    yMax1 = line->yMax;
-	    break;
-	  case 1:
-	    xMin1 = line->xMin;
-	    xMax1 = line->xMax;
-	    yMin1 = line->edge[normStart];
-	    yMax1 = line->edge[normAfterEnd];
-	    break;
-	  case 2:
-	    xMin1 = line->edge[normAfterEnd];
-	    xMax1 = line->edge[normStart];
-	    yMin1 = line->yMin;
-	    yMax1 = line->yMax;
-	    break;
-	  case 3:
-	    xMin1 = line->xMin;
-	    xMax1 = line->xMax;
-	    yMin1 = line->edge[normAfterEnd];
-	    yMax1 = line->edge[normStart];
-	    break;
-	  }
-	  if (backward) {
-	    if ((startAtTop ||
-		 yMin1 < yStart || (yMin1 == yStart && xMin1 < xStart)) &&
-		(stopAtBottom ||
-		 yMin1 > yStop || (yMin1 == yStop && xMin1 > xStop))) {
-	      if (!found ||
-		  yMin1 > yMin0 || (yMin1 == yMin0 && xMin1 > xMin0)) {
-		xMin0 = xMin1;
-		xMax0 = xMax1;
-		yMin0 = yMin1;
-		yMax0 = yMax1;
-		found = gTrue;
-	      }
-	    }
-	  } else {
-	    if ((startAtTop ||
-		 yMin1 > yStart || (yMin1 == yStart && xMin1 > xStart)) &&
-		(stopAtBottom ||
-		 yMin1 < yStop || (yMin1 == yStop && xMin1 < xStop))) {
-	      if (!found ||
-		  yMin1 < yMin0 || (yMin1 == yMin0 && xMin1 < xMin0)) {
-		xMin0 = xMin1;
-		xMax0 = xMax1;
-		yMin0 = yMin1;
-		yMax0 = yMax1;
-		found = gTrue;
-	      }
-	    }
-	  }
+          // found it
+          if (k == len) {
+            // where s2 matches a subsequence of a compatibility equivalence
+            // decomposition, highlight the entire glyph, since we don't know
+            // the internal layout of subglyph components
+            int normStart = line->normalized_idx[j];
+            int normAfterEnd = line->normalized_idx[j + len - 1] + 1;
+            switch (line->rot) {
+            case 0:
+              xMin1 = line->edge[normStart];
+              xMax1 = line->edge[normAfterEnd];
+              yMin1 = line->yMin;
+              yMax1 = line->yMax;
+              break;
+            case 1:
+              xMin1 = line->xMin;
+              xMax1 = line->xMax;
+              yMin1 = line->edge[normStart];
+              yMax1 = line->edge[normAfterEnd];
+              break;
+            case 2:
+              xMin1 = line->edge[normAfterEnd];
+              xMax1 = line->edge[normStart];
+              yMin1 = line->yMin;
+              yMax1 = line->yMax;
+              break;
+            case 3:
+              xMin1 = line->xMin;
+              xMax1 = line->xMax;
+              yMin1 = line->edge[normAfterEnd];
+              yMax1 = line->edge[normStart];
+              break;
+            }
+            if (backward) {
+              if ((startAtTop ||
+                   yMin1 < yStart || (yMin1 == yStart && xMin1 < xStart)) &&
+                  (stopAtBottom ||
+                   yMin1 > yStop || (yMin1 == yStop && xMin1 > xStop))) {
+                if (!found ||
+                    yMin1 > yMin0 || (yMin1 == yMin0 && xMin1 > xMin0)) {
+                  xMin0 = xMin1;
+                  xMax0 = xMax1;
+                  yMin0 = yMin1;
+                  yMax0 = yMax1;
+                  found = gTrue;
+                }
+              }
+            } else {
+              if ((startAtTop ||
+                   yMin1 > yStart || (yMin1 == yStart && xMin1 > xStart)) &&
+                  (stopAtBottom ||
+                   yMin1 < yStop || (yMin1 == yStop && xMin1 < xStop))) {
+                if (!found ||
+                    yMin1 < yMin0 || (yMin1 == yMin0 && xMin1 < xMin0)) {
+                  xMin0 = xMin1;
+                  xMax0 = xMax1;
+                  yMin0 = yMin1;
+                  yMax0 = yMax1;
+                  found = gTrue;
+                }
+              }
+            }
+          }
 	}
 	if (backward) {
 	  --j;
@@ -3736,7 +3706,7 @@
 	}
       }
     }
-    }
+  }
 
   gfree(s2);
   if (!caseSensitive) {
@@ -4355,29 +4325,32 @@
 void TextSelectionPainter::visitWord (TextWord *word, int begin, int end,
 				      PDFRectangle *selection)
 {
-  GooString *string;
-  int i;
-
   state->setFillColor(glyph_color);
   out->updateFillColor(state);
-  word->font->gfxFont->incRefCnt();
-  state->setFont(word->font->gfxFont, word->fontSize);
-  out->updateFont(state);
 
-  /* The only purpose of this string is to let the output device query
-   * it's length.  Might want to change this interface later. */
+  while (begin < end) {
+    TextFontInfo *font = word->font[begin];
+    font->gfxFont->incRefCnt();
+    state->setFont(font->gfxFont, word->fontSize);
+    out->updateFont(state);
 
-  string = new GooString ((char *) word->charcode, end - begin);
+    int fEnd = begin + 1;
+    while (fEnd < end && font->matches(word->font[fEnd]))
+      fEnd++;
 
-  out->beginString(state, string);
+    /* The only purpose of this string is to let the output device query
+     * it's length.  Might want to change this interface later. */
+    GooString *string = new GooString ((char *) word->charcode, fEnd - begin);
+    out->beginString(state, string);
 
-  for (i = begin; i < end; i++)
-    out->drawChar(state, word->edge[i], word->base, 0, 0, 0, 0,
-		  word->charcode[i], 1, NULL, 0);
-  
-  out->endString(state);
-
-  delete string;
+    for (int i = begin; i < fEnd; i++) {
+      out->drawChar(state, word->edge[i], word->base, 0, 0, 0, 0,
+		    word->charcode[i], 1, NULL, 0);
+    }
+    out->endString(state);
+    delete string;
+    begin = fEnd;
+  }
 }
 
 void TextWord::visitSelection(TextSelectionVisitor *visitor,
@@ -5246,41 +5219,17 @@
   // extents of all the glyphs inside the span
 
   if (actualTextNBytes) {
-    char *uniString = NULL;
     Unicode *uni;
-    int length, i;
-
-    if (!actualText->hasUnicodeMarker()) {
-      if (actualText->getLength() > 0) {
-        //non-unicode string -- assume pdfDocEncoding and
-        //try to convert to UTF16BE
-        uniString = pdfDocEncodingToUTF16(actualText, &length);
-      } else {
-        length = 0;
-      }
-    } else {
-      uniString = actualText->getCString();
-      length = actualText->getLength();
-    }
-
-    if (length < 3)
-      length = 0;
-    else
-      length = length/2 - 1;
-    uni = new Unicode[length];
-    for (i = 0 ; i < length; i++)
-      uni[i] = ((uniString[2 + i*2] & 0xff)<<8)|(uniString[3 + i*2] & 0xff);
+    int length;
 
     // now that we have the position info for all of the text inside
     // the marked content span, we feed the "ActualText" back through
     // text->addChar()
+    length = TextStringToUCS4(actualText, &uni);
     text->addChar(state, actualTextX0, actualTextY0,
                   actualTextX1 - actualTextX0, actualTextY1 - actualTextY0,
                   0, actualTextNBytes, uni, length);
-
-    delete [] uni;
-    if (!actualText->hasUnicodeMarker())
-      delete [] uniString;
+    gfree(uni);
   }
 
   delete actualText;
diff --git a/poppler/TextOutputDev.h b/poppler/TextOutputDev.h
index e31876b..100f23e 100644
--- a/poppler/TextOutputDev.h
+++ b/poppler/TextOutputDev.h
@@ -19,6 +19,7 @@
 // Copyright (C) 2007 Adrian Johnson <ajohnson@redneon.com>
 // Copyright (C) 2008, 2010 Albert Astals Cid <aacid@kde.org>
 // Copyright (C) 2010 Brian Ewins <brian.ewins@gmail.com>
+// Copyright (C) 2012 Jason Crain <jason@aquaticape.us>
 //
 // To see a description of the changes please see the Changelog file that
 // came with your tarball or type make ChangeLog if you are building from git
@@ -113,14 +114,13 @@
 public:
 
   // Constructor.
-  TextWord(GfxState *state, int rotA, double x0, double y0,
-	   TextFontInfo *fontA, double fontSize);
+  TextWord(GfxState *state, int rotA, double fontSize);
 
   // Destructor.
   ~TextWord();
 
   // Add a character to the word.
-  void addChar(GfxState *state, double x, double y,
+  void addChar(GfxState *state, TextFontInfo *fontA, double x, double y,
 	       double dx, double dy, int charPosA, int charLen,
 	       CharCode c, Unicode u);
 
@@ -141,8 +141,8 @@
 		      PDFRectangle *selection,
 		      SelectionStyle style);
 
-  // Get the TextFontInfo object associated with this word.
-  TextFontInfo *getFontInfo() { return font; }
+  // Get the TextFontInfo object associated with a character.
+  TextFontInfo *getFontInfo(int idx) { return font[idx]; }
 
   // Get the next TextWord on the linked list.
   TextWord *getNext() { return next; }
@@ -151,7 +151,7 @@
   int getLength() { return len; }
   const Unicode *getChar(int idx) { return &text[idx]; }
   GooString *getText();
-  GooString *getFontName() { return font->fontName; }
+  GooString *getFontName(int idx) { return font[idx]->fontName; }
   void getColor(double *r, double *g, double *b)
     { *r = colorR; *g = colorG; *b = colorB; }
   void getBBox(double *xMinA, double *yMinA, double *xMaxA, double *yMaxA)
@@ -184,13 +184,14 @@
   int *charPos;			// character position (within content stream)
 				//   of each char (plus one extra entry for
 				//   the last char)
-  int len;			// length of text/edge/charPos arrays
-  int size;			// size of text/edge/charPos arrays
-  TextFontInfo *font;		// font information
+  int len;			// length of text/edge/charPos/font arrays
+  int size;			// size of text/edge/charPos/font arrays
+  TextFontInfo **font;		// font information for each char
   double fontSize;		// font size
   GBool spaceAfter;		// set if there is a space between this
 				//   word and the next word on the line
   TextWord *next;		// next word in line
+  int wMode;			// horizontal (0) or vertical (1) writing mode
 
 #if TEXTOUT_WORD_LIST
   double colorR,		// word color
@@ -498,7 +499,7 @@
   void updateFont(GfxState *state);
 
   // Begin a new word.
-  void beginWord(GfxState *state, double x0, double y0);
+  void beginWord(GfxState *state);
 
   // Add a character to the current word.
   void addChar(GfxState *state, double x, double y,
diff --git a/poppler/UTF.cc b/poppler/UTF.cc
new file mode 100644
index 0000000..42c7836
--- /dev/null
+++ b/poppler/UTF.cc
@@ -0,0 +1,116 @@
+//========================================================================
+//
+// UTF.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+//========================================================================
+//
+// Modified under the Poppler project - http://poppler.freedesktop.org
+//
+// All changes made under the Poppler project to this file are licensed
+// under GPL version 2 or later
+//
+// Copyright (C) 2008 Koji Otani <sho@bbr.jp>
+// Copyright (C) 2012 Adrian Johnson <ajohnson@redneon.com>
+// Copyright (C) 2012 Hib Eris <hib@hiberis.nl>
+//
+// To see a description of the changes please see the Changelog file that
+// came with your tarball or type make ChangeLog if you are building from git
+//
+//========================================================================
+
+#include "goo/gmem.h"
+#include "PDFDocEncoding.h"
+#include "UTF.h"
+
+bool UnicodeIsValid(Unicode ucs4)
+{
+  return (ucs4 < 0x110000) &&
+    ((ucs4 & 0xfffff800) != 0xd800) &&
+    (ucs4 < 0xfdd0 || ucs4 > 0xfdef) &&
+    ((ucs4 & 0xfffe) != 0xfffe);
+}
+
+int UTF16toUCS4(const Unicode *utf16, int utf16Len, Unicode **ucs4)
+{
+  int i, n, len;
+  Unicode *u;
+
+  // count characters
+  len = 0;
+  for (i = 0; i < utf16Len; i++) {
+    if (utf16[i] >= 0xd800 && utf16[i] < 0xdc00 && i + 1 < utf16Len &&
+        utf16[i+1] >= 0xdc00 && utf16[i+1] < 0xe000) {
+      i++; /* surrogate pair */
+    }
+    len++;
+  }
+  if (ucs4 == NULL)
+    return len;
+
+  u = (Unicode*)gmallocn(len, sizeof(Unicode));
+  n = 0;
+  // convert string
+  for (i = 0; i < utf16Len; i++) {
+    if (utf16[i] >= 0xd800 && utf16[i] < 0xdc00) { /* surrogate pair */
+      if (i + 1 < utf16Len && utf16[i+1] >= 0xdc00 && utf16[i+1] < 0xe000) {
+	/* next code is a low surrogate */
+	u[n] = (((utf16[i] & 0x3ff) << 10) | (utf16[i+1] & 0x3ff)) + 0x10000;
+	++i;
+      } else {
+	/* missing low surrogate
+	   replace it with REPLACEMENT CHARACTER (U+FFFD) */
+	u[n] = 0xfffd;
+      }
+    } else if (utf16[i] >= 0xdc00 && utf16[i] < 0xe000) {
+      /* invalid low surrogate
+	 replace it with REPLACEMENT CHARACTER (U+FFFD) */
+      u[n] = 0xfffd;
+    } else {
+      u[n] = utf16[i];
+    }
+    if (!UnicodeIsValid(u[n])) {
+      u[n] = 0xfffd;
+    }
+    n++;
+  }
+  *ucs4 = u;
+  return len;
+}
+
+int TextStringToUCS4(GooString *textStr, Unicode **ucs4)
+{
+  int i, len;
+  const char *s;
+  Unicode *u;
+
+  len = textStr->getLength();
+  s = textStr->getCString();
+  if (len == 0)
+    return 0;
+
+  if (textStr->hasUnicodeMarker()) {
+    Unicode *utf16;
+    len = len/2 - 1;
+    if (len > 0) {
+      utf16 = new Unicode[len];
+      for (i = 0 ; i < len; i++) {
+        utf16[i] = (s[2 + i*2] & 0xff) << 8 | (s[3 + i*2] & 0xff);
+      }
+      len = UTF16toUCS4(utf16, len, &u);
+      delete utf16;
+    } else {
+      u = NULL;
+    }
+  } else {
+    u = (Unicode*)gmallocn(len, sizeof(Unicode));
+    for (i = 0 ; i < len; i++) {
+      u[i] = pdfDocEncoding[s[i] & 0xff];
+    }
+  }
+  *ucs4 = u;
+  return len;
+}
diff --git a/poppler/UTF.h b/poppler/UTF.h
new file mode 100644
index 0000000..248c168
--- /dev/null
+++ b/poppler/UTF.h
@@ -0,0 +1,39 @@
+//========================================================================
+//
+// UTF.h
+//
+// This file is licensed under the GPLv2 or later
+//
+// Copyright (C) 2012 Adrian Johnson <ajohnson@redneon.com>
+//
+//========================================================================
+
+#ifndef UTF_H
+#define UTF_H
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "goo/GooString.h"
+#include "CharTypes.h"
+
+// Convert a UTF-16 string to a UCS-4
+//   utf16      - utf16 bytes
+//   utf16_len  - number of UTF-16 characters
+//   ucs4_out   - if not NULL, allocates and returns UCS-4 string. Free with gfree.
+//   returns number of UCS-4 characters
+int UTF16toUCS4(const Unicode *utf16, int utf16_len, Unicode **ucs4_out);
+
+// Convert a PDF Text String to UCS-4
+//   s          - PDF text string
+//   ucs4       - if the number of UCS-4 characters is > 0, allocates and
+//                returns UCS-4 string. Free with gfree.
+//   returns number of UCS-4 characters
+int TextStringToUCS4(GooString *textStr, Unicode **ucs4);
+
+// check if UCS-4 character is valid
+bool UnicodeIsValid(Unicode ucs4);
+
+
+#endif
diff --git a/poppler/XRef.cc b/poppler/XRef.cc
index b516a0f..81e939a 100644
--- a/poppler/XRef.cc
+++ b/poppler/XRef.cc
@@ -267,6 +267,10 @@
   objStrs = new PopplerCache(5);
   mainXRefEntriesOffset = 0;
   xRefStream = gFalse;
+  scannedSpecialFlags = gFalse;
+  encrypted = gFalse;
+  permFlags = defPermFlags;
+  ownerPasswordOk = gFalse;
   rootNum = -1;
 }
 
@@ -276,6 +280,7 @@
 
 XRef::XRef(Object *trailerDictA) {
   init();
+
   if (trailerDictA->isDict())
     trailerDict.initDict(trailerDictA->getDict());
 }
@@ -286,14 +291,10 @@
   init();
   mainXRefEntriesOffset = mainXRefEntriesOffsetA;
 
-  encrypted = gFalse;
-  permFlags = defPermFlags;
-  ownerPasswordOk = gFalse;
-
   // read the trailer
   str = strA;
   start = str->getStart();
-  prevXRefOffset = pos;
+  prevXRefOffset = mainXRefOffset = pos;
 
   if (reconstruct && !(ok = constructXRef(wasReconstructed)))
   {
@@ -313,7 +314,7 @@
     // read the xref table
     } else {
       std::vector<Guint> followedXRefStm;
-      readXRef(&prevXRefOffset, &followedXRefStm);
+      readXRef(&prevXRefOffset, &followedXRefStm, NULL);
 
       // if there was a problem with the xref table,
       // try to reconstruct it
@@ -412,7 +413,7 @@
       entries[i].offset = 0xffffffff;
       entries[i].type = xrefEntryNone;
       entries[i].obj.initNull ();
-      entries[i].updated = false;
+      entries[i].flags = 0;
       entries[i].gen = 0;
     }
   } else {
@@ -426,9 +427,20 @@
   return size;
 }
 
-// Read one xref table section.  Also reads the associated trailer
-// dictionary, and returns the prev pointer (if any).
-GBool XRef::readXRef(Guint *pos, std::vector<Guint> *followedXRefStm) {
+/* Read one xref table section.  Also reads the associated trailer
+ * dictionary, and returns the prev pointer (if any).
+ * Arguments:
+ *   pos                Points to a Guint containing the offset of the XRef
+ *                      section to be read. If a prev pointer is found, *pos is
+ *                      updated with its value
+ *   followedXRefStm    Used in case of nested readXRef calls to spot circular
+ *                      references in XRefStm pointers
+ *   xrefStreamObjsNum  If not NULL, every time a XRef stream is encountered,
+ *                      its object number is appended
+ * Return value:
+ *   gTrue if a prev pointer is found, otherwise gFalse
+ */
+GBool XRef::readXRef(Guint *pos, std::vector<Guint> *followedXRefStm, std::vector<int> *xrefStreamObjsNum) {
   Parser *parser;
   Object obj;
   GBool more;
@@ -444,10 +456,11 @@
   // parse an old-style xref table
   if (obj.isCmd("xref")) {
     obj.free();
-    more = readXRefTable(parser, pos, followedXRefStm);
+    more = readXRefTable(parser, pos, followedXRefStm, xrefStreamObjsNum);
 
   // parse an xref stream
   } else if (obj.isInt()) {
+    const int objNum = obj.getInt();
     obj.free();
     if (!parser->getObj(&obj, gTrue)->isInt()) {
       goto err1;
@@ -463,6 +476,9 @@
     if (trailerDict.isNone()) {
       xRefStream = gTrue;
     }
+    if (xrefStreamObjsNum) {
+      xrefStreamObjsNum->push_back(objNum);
+    }
     more = readXRefStream(obj.getStream(), pos);
     obj.free();
 
@@ -480,7 +496,7 @@
   return gFalse;
 }
 
-GBool XRef::readXRefTable(Parser *parser, Guint *pos, std::vector<Guint> *followedXRefStm) {
+GBool XRef::readXRefTable(Parser *parser, Guint *pos, std::vector<Guint> *followedXRefStm, std::vector<int> *xrefStreamObjsNum) {
   XRefEntry entry;
   GBool more;
   Object obj, obj2;
@@ -523,7 +539,7 @@
       }
       entry.gen = obj.getInt();
       entry.obj.initNull ();
-      entry.updated = false;
+      entry.flags = 0;
       obj.free();
       parser->getObj(&obj, gTrue);
       if (obj.isCmd("n")) {
@@ -597,7 +613,7 @@
     }
     if (ok) {
       followedXRefStm->push_back(pos2);
-      readXRef(&pos2, followedXRefStm);
+      readXRef(&pos2, followedXRefStm, xrefStreamObjsNum);
     }
     if (!ok) {
       obj2.free();
@@ -945,6 +961,20 @@
   encAlgorithm = encAlgorithmA;
 }
 
+void XRef::getEncryptionParameters(Guchar **fileKeyA, CryptAlgorithm *encAlgorithmA,
+                              int *keyLengthA) {
+  if (encrypted) {
+    *fileKeyA = fileKey;
+    *encAlgorithmA = encAlgorithm;
+    *keyLengthA = keyLength;
+  } else {
+    // null encryption parameters
+    *fileKeyA = NULL;
+    *encAlgorithmA = cryptRC4;
+    *keyLengthA = 0;
+  }
+}
+
 GBool XRef::okToPrint(GBool ignoreOwnerPW) {
   return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permPrint);
 }
@@ -1064,7 +1094,7 @@
       delete parser;
       goto err;
     }
-    parser->getObj(obj, gFalse, encrypted ? fileKey : (Guchar *)NULL,
+    parser->getObj(obj, gFalse, (encrypted && !e->getFlag(XRefEntry::Unencrypted)) ? fileKey : NULL,
 		   encAlgorithm, keyLength, num, gen, recursion);
     obj1.free();
     obj2.free();
@@ -1184,7 +1214,7 @@
       entries[i].offset = 0xffffffff;
       entries[i].type = xrefEntryFree;
       entries[i].obj.initNull ();
-      entries[i].updated = false;
+      entries[i].flags = 0;
       entries[i].gen = 0;
     }
     size = num + 1;
@@ -1192,7 +1222,7 @@
   XRefEntry *e = getEntry(num);
   e->gen = gen;
   e->obj.initNull ();
-  e->updated = false;
+  e->flags = 0;
   if (used) {
     e->type = xrefEntryUncompressed;
     e->offset = offs;
@@ -1210,7 +1240,7 @@
   XRefEntry *e = getEntry(r.num);
   e->obj.free();
   o->copy(&(e->obj));
-  e->updated = true;
+  e->setFlag(XRefEntry::Updated, gTrue);
 }
 
 Ref XRef::addIndirectObject (Object* o) {
@@ -1235,7 +1265,7 @@
   }
   e->type = xrefEntryUncompressed;
   o->copy(&e->obj);
-  e->updated = true;
+  e->setFlag(XRefEntry::Updated, gTrue);
 
   Ref r;
   r.num = entryIndexToUse;
@@ -1254,7 +1284,7 @@
   e->obj.free();
   e->type = xrefEntryFree;
   e->gen++;
-  e->updated = true;
+  e->setFlag(XRefEntry::Updated, gTrue);
 }
 
 void XRef::writeXRef(XRef::XRefWriter *writer, GBool writeAllEntries) {
@@ -1377,7 +1407,7 @@
     entry->gen = obj2.getInt();
     entry->type = obj3.isCmd("n") ? xrefEntryUncompressed : xrefEntryFree;
     entry->obj.initNull ();
-    entry->updated = false;
+    entry->flags = 0;
     r = gTrue;
   } else {
     r = gFalse;
@@ -1389,6 +1419,50 @@
   return r;
 }
 
+/* Traverse all XRef tables and, if untilEntryNum != -1, stop as soon as
+ * untilEntryNum is found, or try to reconstruct the xref table if it's not
+ * present in any xref.
+ * If xrefStreamObjsNum is not NULL, it is filled with the list of the object
+ * numbers of the XRef streams that have been traversed */
+void XRef::readXRefUntil(int untilEntryNum, std::vector<int> *xrefStreamObjsNum)
+{
+  std::vector<Guint> followedPrev;
+  while (prevXRefOffset && (untilEntryNum == -1 || entries[untilEntryNum].type == xrefEntryNone)) {
+    bool followed = false;
+    for (size_t j = 0; j < followedPrev.size(); j++) {
+      if (followedPrev.at(j) == prevXRefOffset) {
+        followed = true;
+        break;
+      }
+    }
+    if (followed) {
+      error(errSyntaxError, -1, "Circular XRef");
+      if (!(ok = constructXRef(NULL))) {
+        errCode = errDamaged;
+      }
+      break;
+    }
+
+    followedPrev.push_back (prevXRefOffset);
+
+    std::vector<Guint> followedXRefStm;
+    if (!readXRef(&prevXRefOffset, &followedXRefStm, xrefStreamObjsNum)) {
+        prevXRefOffset = 0;
+    }
+
+    // if there was a problem with the xref table, or we haven't found the entry
+    // we were looking for, try to reconstruct the xref
+    if (!ok || (!prevXRefOffset && untilEntryNum != -1 && entries[untilEntryNum].type == xrefEntryNone)) {
+        GBool wasReconstructed = false;
+        if (!(ok = constructXRef(&wasReconstructed))) {
+            errCode = errDamaged;
+            break;
+        }
+        break;
+    }
+  }
+}
+
 XRefEntry *XRef::getEntry(int i, GBool complainIfMissing)
 {
   if (entries[i].type == xrefEntryNone) {
@@ -1398,41 +1472,8 @@
         error(errSyntaxError, -1, "Failed to parse XRef entry [{0:d}].", i);
       }
     } else {
-      std::vector<Guint> followedPrev;
-      while (prevXRefOffset && entries[i].type == xrefEntryNone) {
-        bool followed = false;
-        for (size_t j = 0; j < followedPrev.size(); j++) {
-          if (followedPrev.at(j) == prevXRefOffset) {
-            followed = true;
-            break;
-          }
-        }
-        if (followed) {
-          error(errSyntaxError, -1, "Circular XRef");
-          if (!(ok = constructXRef(NULL))) {
-            errCode = errDamaged;
-          }
-          break;
-        }
-
-        followedPrev.push_back (prevXRefOffset);
-
-        std::vector<Guint> followedXRefStm;
-        if (!readXRef(&prevXRefOffset, &followedXRefStm)) {
-            prevXRefOffset = 0;
-        }
-
-        // if there was a problem with the xref table,
-        // try to reconstruct it
-        if (!ok || (!prevXRefOffset && entries[i].type == xrefEntryNone)) {
-           GBool wasReconstructed = false;
-           if (!(ok = constructXRef(&wasReconstructed))) {
-               errCode = errDamaged;
-               break;
-           }
-           break;
-        }
-      }
+      // Read XRef tables until the entry we're looking for is found
+      readXRefUntil(i);
       
       // We might have reconstructed the xref
       // Check again i is in bounds
@@ -1441,7 +1482,7 @@
         dummy.offset = 0;
         dummy.gen = -1;
         dummy.type = xrefEntryNone;
-        dummy.updated = false;
+        dummy.flags = 0;
         return &dummy;
       }
 
@@ -1457,4 +1498,100 @@
   return &entries[i];
 }
 
+// Recursively sets the Unencrypted flag in all referenced xref entries
+void XRef::markUnencrypted(Object *obj) {
+  Object obj1;
+
+  switch (obj->getType()) {
+    case objArray:
+    {
+      Array *array = obj->getArray();
+      for (int i = 0; i < array->getLength(); i++) {
+        markUnencrypted(array->getNF(i, &obj1));
+        obj1.free();
+      }
+      break;
+    }
+    case objStream:
+    case objDict:
+    {
+      Dict *dict;
+      if (obj->getType() == objStream) {
+        Stream *stream = obj->getStream();
+        dict = stream->getDict();
+      } else {
+        dict = obj->getDict();
+      }
+      for (int i = 0; i < dict->getLength(); i++) {
+        markUnencrypted(dict->getValNF(i, &obj1));
+        obj1.free();
+      }
+      break;
+    }
+    case objRef:
+    {
+      Ref ref = obj->getRef();
+      XRefEntry *e = getEntry(ref.num);
+      if (e->getFlag(XRefEntry::Unencrypted))
+        return; // We've already been here: prevent infinite recursion
+      e->setFlag(XRefEntry::Unencrypted, gTrue);
+      fetch(ref.num, ref.gen, &obj1);
+      markUnencrypted(&obj1);
+      obj1.free();
+      break;
+    }
+    default:
+      break;
+  }
+}
+
+void XRef::scanSpecialFlags() {
+  if (scannedSpecialFlags) {
+    return;
+  }
+  scannedSpecialFlags = gTrue;
+
+  // "Rewind" the XRef linked list, so that readXRefUntil re-reads all XRef
+  // tables/streams, even those that had already been parsed
+  prevXRefOffset = mainXRefOffset;
+
+  std::vector<int> xrefStreamObjNums;
+  readXRefUntil(-1 /* read all xref sections */, &xrefStreamObjNums);
+
+  // Mark object streams as DontRewrite, because we write each object
+  // individually in full rewrite mode.
+  for (int i = 0; i < size; ++i) {
+    if (entries[i].type == xrefEntryCompressed) {
+      const int objStmNum = entries[i].offset;
+      if (unlikely(objStmNum < 0 || objStmNum >= size)) {
+        error(errSyntaxError, -1, "Compressed object offset out of xref bounds");
+      } else {
+        getEntry(objStmNum)->setFlag(XRefEntry::DontRewrite, gTrue);
+      }
+    }
+  }
+
+  // Mark XRef streams objects as Unencrypted and DontRewrite
+  for (size_t i = 0; i < xrefStreamObjNums.size(); ++i) {
+    const int objNum = xrefStreamObjNums.at(i);
+    getEntry(objNum)->setFlag(XRefEntry::Unencrypted, gTrue);
+    getEntry(objNum)->setFlag(XRefEntry::DontRewrite, gTrue);
+  }
+
+  // Mark objects referred from the Encrypt dict as Unencrypted
+  Object obj;
+  markUnencrypted(trailerDict.dictLookupNF("Encrypt", &obj));
+  obj.free();
+}
+
+void XRef::markUnencrypted() {
+  // Mark objects referred from the Encrypt dict as Unencrypted
+  Object obj;
+  trailerDict.dictLookupNF("Encrypt", &obj);
+  if (obj.isRef()) {
+    XRefEntry *e = getEntry(obj.getRefNum());
+    e->setFlag(XRefEntry::Unencrypted, gTrue);
+  }
+  obj.free();
+}
 
diff --git a/poppler/XRef.h b/poppler/XRef.h
index 2add0a1..9af4a13 100644
--- a/poppler/XRef.h
+++ b/poppler/XRef.h
@@ -36,6 +36,7 @@
 
 #include "goo/gtypes.h"
 #include "Object.h"
+#include "Stream.h"
 
 #include <vector>
 
@@ -59,8 +60,31 @@
   Guint offset;
   int gen;
   XRefEntryType type;
-  bool updated;
+  int flags;
   Object obj; //if this entry was updated, obj will contains the updated object
+
+  enum Flag {
+    // Regular flags
+    Updated,     // Entry was modified
+
+    // Special flags -- available only after xref->scanSpecialFlags() is run
+    Unencrypted, // Entry is stored in unencrypted form (meaningless in unencrypted documents)
+    DontRewrite  // Entry must not be written back in case of full rewrite
+  };
+
+  inline GBool getFlag(Flag flag) {
+    const int mask = (1 << (int)flag);
+    return (flags & mask) != 0;
+  }
+
+  inline void setFlag(Flag flag, GBool value) {
+    const int mask = (1 << (int)flag);
+    if (value) {
+      flags |= mask;
+    } else {
+      flags &= ~mask;
+    }
+  }
 };
 
 class XRef {
@@ -90,6 +114,10 @@
 		     Guchar *fileKeyA, int keyLengthA,
 		     int encVersionA, int encRevisionA,
 		     CryptAlgorithm encAlgorithmA);
+  // Mark Encrypt entry as Unencrypted
+  void markUnencrypted();
+
+  void getEncryptionParameters(Guchar **fileKeyA, CryptAlgorithm *encAlgorithmA, int *keyLengthA);
 
   // Is the file encrypted?
   GBool isEncrypted() { return encrypted; }
@@ -129,6 +157,14 @@
   // Retuns the entry that belongs to the offset
   int getNumEntry(Guint offset);
 
+  // Scans the document and sets special flags in all xref entries. One of those
+  // flags is Unencrypted, which affects how the object is fetched. Therefore,
+  // this function must be called before fetching unencrypted objects (e.g.
+  // Encrypt dictionary, XRef streams). Note that the code that initializes
+  // decryption doesn't need to call this function, because it runs before
+  // decryption is enabled, and therefore the Unencrypted flag is ignored.
+  void scanSpecialFlags();
+
   // Direct access.
   XRefEntry *getEntry(int i, GBool complainIfMissing = gTrue);
   Object *getTrailerDict() { return &trailerDict; }
@@ -171,16 +207,20 @@
   Guint prevXRefOffset;		// position of prev XRef section (= next to read)
   Guint mainXRefEntriesOffset;	// offset of entries in main XRef table
   GBool xRefStream;		// true if last XRef section is a stream
+  Guint mainXRefOffset;		// position of the main XRef table/stream
+  GBool scannedSpecialFlags;	// true if scanSpecialFlags has been called
 
   void init();
   int reserve(int newSize);
   int resize(int newSize);
-  GBool readXRef(Guint *pos, std::vector<Guint> *followedXRefStm);
-  GBool readXRefTable(Parser *parser, Guint *pos, std::vector<Guint> *followedXRefStm);
+  GBool readXRef(Guint *pos, std::vector<Guint> *followedXRefStm, std::vector<int> *xrefStreamObjsNum);
+  GBool readXRefTable(Parser *parser, Guint *pos, std::vector<Guint> *followedXRefStm, std::vector<int> *xrefStreamObjsNum);
   GBool readXRefStreamSection(Stream *xrefStr, int *w, int first, int n);
   GBool readXRefStream(Stream *xrefStr, Guint *pos);
   GBool constructXRef(GBool *wasReconstructed, GBool needCatalogDict = gFalse);
   GBool parseEntry(Guint offset, XRefEntry *entry);
+  void readXRefUntil(int untilEntryNum, std::vector<int> *xrefStreamObjsNum = NULL);
+  void markUnencrypted(Object *obj);
 
   class XRefWriter {
   public:
diff --git a/poppler/poppler-config.h.cmake b/poppler/poppler-config.h.cmake
index 382ca45..2425f6a 100644
--- a/poppler/poppler-config.h.cmake
+++ b/poppler/poppler-config.h.cmake
@@ -44,16 +44,6 @@
 #cmakedefine TEXTOUT_WORD_LIST 1
 #endif
 
-/* Use fontconfig font configuration backend */
-#ifndef WITH_FONTCONFIGURATION_FONTCONFIG
-#cmakedefine WITH_FONTCONFIGURATION_FONTCONFIG 1
-#endif
-
-/* Use win32 font configuration backend */
-#ifndef WITH_FONTCONFIGURATION_WIN32
-#cmakedefine WITH_FONTCONFIGURATION_WIN32 1
-#endif
-
 /* Support for curl is compiled in. */
 #ifndef POPPLER_HAS_CURL_SUPPORT
 #cmakedefine POPPLER_HAS_CURL_SUPPORT 1
diff --git a/poppler/poppler-config.h.in b/poppler/poppler-config.h.in
index 0d8b379..5c56ea4 100644
--- a/poppler/poppler-config.h.in
+++ b/poppler/poppler-config.h.in
@@ -44,16 +44,6 @@
 #undef TEXTOUT_WORD_LIST
 #endif
 
-/* Use fontconfig font configuration backend */
-#ifndef WITH_FONTCONFIGURATION_FONTCONFIG
-#undef WITH_FONTCONFIGURATION_FONTCONFIG
-#endif
-
-/* Use win32 font configuration backend */
-#ifndef WITH_FONTCONFIGURATION_WIN32
-#undef WITH_FONTCONFIGURATION_WIN32
-#endif
-
 /* Support for curl is compiled in. */
 #ifndef POPPLER_HAS_CURL_SUPPORT
 #undef POPPLER_HAS_CURL_SUPPORT
diff --git a/qt4/demos/Makefile.am b/qt4/demos/Makefile.am
index 155fc1c..3cadda9 100644
--- a/qt4/demos/Makefile.am
+++ b/qt4/demos/Makefile.am
@@ -4,13 +4,11 @@
 	-I$(top_srcdir)				\
 	-I$(top_srcdir)/poppler			\
 	-I$(top_srcdir)/qt4/src			\
-	$(FONTCONFIG_CFLAGS)			\
 	$(POPPLER_QT4_CFLAGS)
 
 LDADDS =					\
 	$(top_builddir)/poppler/libpoppler.la	\
 	$(top_builddir)/qt4/src/libpoppler-qt4.la	\
-	$(FONTCONFIG_LIBS)				\
 	$(POPPLER_QT4_LIBS)
 
 SUFFIXES: .moc
diff --git a/qt4/src/CMakeLists.txt b/qt4/src/CMakeLists.txt
index 8b6edaa..c06730b 100644
--- a/qt4/src/CMakeLists.txt
+++ b/qt4/src/CMakeLists.txt
@@ -30,7 +30,7 @@
 )
 qt4_automoc(${poppler_qt4_SRCS})
 add_library(poppler-qt4 SHARED ${poppler_qt4_SRCS})
-set_target_properties(poppler-qt4 PROPERTIES VERSION 4.0.0 SOVERSION 4)
+set_target_properties(poppler-qt4 PROPERTIES VERSION 4.1.0 SOVERSION 4)
 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})
diff --git a/qt4/src/Doxyfile b/qt4/src/Doxyfile
index c356ebd..85673f4 100644
--- a/qt4/src/Doxyfile
+++ b/qt4/src/Doxyfile
@@ -31,7 +31,7 @@
 # This could be handy for archiving the generated documentation or
 # if some version control system is used.
 
-PROJECT_NUMBER         = 0.20.5
+PROJECT_NUMBER         = 0.21.0
 
 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
 # base path where the generated documentation will be put.
diff --git a/qt4/src/Makefile.am b/qt4/src/Makefile.am
index 22033e3..b91daf0 100644
--- a/qt4/src/Makefile.am
+++ b/qt4/src/Makefile.am
@@ -1,7 +1,6 @@
 INCLUDES =					\
 	-I$(top_srcdir)				\
 	-I$(top_srcdir)/poppler			\
-	$(FONTCONFIG_CFLAGS)			\
 	$(POPPLER_QT4_CFLAGS)			\
 	$(LCMS_CFLAGS)
 
@@ -60,10 +59,9 @@
 libpoppler_qt4_la_LIBADD = 			\
 	$(top_builddir)/poppler/libpoppler.la	\
 	$(top_builddir)/poppler/libpoppler-arthur.la	\
-	$(FONTCONFIG_LIBS)				\
 	$(POPPLER_QT4_LIBS)
 
-libpoppler_qt4_la_LDFLAGS = -version-info 4:0:0 @create_shared_lib@ @auto_import_flags@
+libpoppler_qt4_la_LDFLAGS = -version-info 5:0:1 @create_shared_lib@ @auto_import_flags@
 
 # This rule lets GNU make create any *.moc from the equivalent *.h
 .h.moc:
diff --git a/qt4/src/poppler-annotation-private.h b/qt4/src/poppler-annotation-private.h
index 2ee7d77..3bfb5da 100644
--- a/qt4/src/poppler-annotation-private.h
+++ b/qt4/src/poppler-annotation-private.h
@@ -1,7 +1,8 @@
-/* poppler-link-extractor-private.h: qt interface to poppler
+/* 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, Fabio D'Urso <fabiodurso@hotmail.it>
+ * Copyright (C) 2012, 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
@@ -31,6 +32,7 @@
 
 class Annot;
 class AnnotPath;
+class Link;
 class Page;
 class PDFRectangle;
 
@@ -100,6 +102,8 @@
         static void removeAnnotationFromPage(::Page *pdfPage, const Annotation * ann);
 
         Ref pdfObjectReference() const;
+
+        Link* additionalAction( Annotation::AdditionalActionType type ) const;
 };
 
 }
diff --git a/qt4/src/poppler-annotation.cc b/qt4/src/poppler-annotation.cc
index 67dbbae..5ecea80 100644
--- a/qt4/src/poppler-annotation.cc
+++ b/qt4/src/poppler-annotation.cc
@@ -3,6 +3,7 @@
  * Copyright (C) 2006, 2008, 2010 Pino Toscano <pino@kde.org>
  * Copyright (C) 2012, Guillermo A. Amaral B. <gamaral@kde.org>
  * Copyright (C) 2012, Fabio D'Urso <fabiodurso@hotmail.it>
+ * Copyright (C) 2012, Tobias Koenig <tokoe@kdab.com>
  * Adapting code from
  *   Copyright (C) 2004 by Enrico Ros <eros.kde@email.it>
  *
@@ -42,6 +43,7 @@
 #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,
@@ -455,7 +457,8 @@
             case Annot::typeUnknown:
                 continue; // special case for ignoring unknown annotations
             case Annot::typeWidget:
-                continue; // handled as forms or some other way
+                annotation = new WidgetAnnotation();
+                break;
             default:
             {
 #define CASE_FOR_TYPE( thetype ) \
@@ -493,6 +496,40 @@
     return pdfAnnot->getRef();
 }
 
+Link* AnnotationPrivate::additionalAction( Annotation::AdditionalActionType type ) const
+{
+    if ( pdfAnnot->getType() != Annot::typeScreen && pdfAnnot->getType() != Annot::typeWidget )
+        return 0;
+
+    Annot::AdditionalActionsType actionType = Annot::actionCursorEntering;
+    switch ( type )
+    {
+        case Annotation::CursorEnteringAction: actionType = Annot::actionCursorEntering; break;
+        case Annotation::CursorLeavingAction: actionType = Annot::actionCursorLeaving; break;
+        case Annotation::MousePressedAction: actionType = Annot::actionMousePressed; break;
+        case Annotation::MouseReleasedAction: actionType = Annot::actionMouseReleased; break;
+        case Annotation::FocusInAction: actionType = Annot::actionFocusIn; break;
+        case Annotation::FocusOutAction: actionType = Annot::actionFocusOut; break;
+        case Annotation::PageOpeningAction: actionType = Annot::actionPageOpening; break;
+        case Annotation::PageClosingAction: actionType = Annot::actionPageClosing; break;
+        case Annotation::PageVisibleAction: actionType = Annot::actionPageVisible; break;
+        case Annotation::PageInvisibleAction: actionType = Annot::actionPageInvisible; break;
+    }
+
+    ::LinkAction *linkAction = 0;
+    if ( pdfAnnot->getType() == Annot::typeScreen )
+        linkAction = static_cast<AnnotScreen*>( pdfAnnot )->getAdditionalAction( actionType );
+    else
+        linkAction = static_cast<AnnotWidget*>( pdfAnnot )->getAdditionalAction( actionType );
+
+    Link *link = 0;
+
+    if ( linkAction )
+        link = PageData::convertLinkActionToLink( linkAction, parentDoc, QRectF() );
+
+    return link;
+}
+
 void AnnotationPrivate::addAnnotationToPage(::Page *pdfPage, DocumentData *doc, const Annotation * ann)
 {
     if (ann->d_ptr->pdfAnnot != 0)
@@ -522,14 +559,6 @@
         return;
     }
 
-    // Remove popup window
-    AnnotMarkup *markupann = dynamic_cast<AnnotMarkup*>(ann->d_ptr->pdfAnnot);
-    if (markupann && markupann->getPopup())
-        pdfPage->removeAnnot(markupann->getPopup());
-
-    // Remove appearance streams (if any)
-    ann->d_ptr->pdfAnnot->invalidateAppearance();
-
     // Remove annotation
     pdfPage->removeAnnot(ann->d_ptr->pdfAnnot);
 
@@ -4269,6 +4298,64 @@
     d->title = title;
 }
 
+Link* ScreenAnnotation::additionalAction( AdditionalActionType type ) const
+{
+    Q_D( const ScreenAnnotation );
+    return d->additionalAction( type );
+}
+
+/** WidgetAnnotation [Annotation] */
+class WidgetAnnotationPrivate : public AnnotationPrivate
+{
+    public:
+        Annotation * makeAlias();
+        Annot* createNativeAnnot(::Page *destPage, DocumentData *doc);
+};
+
+Annotation * WidgetAnnotationPrivate::makeAlias()
+{
+    return new WidgetAnnotation(*this);
+}
+
+Annot* WidgetAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
+{
+    return 0; // Not implemented
+}
+
+WidgetAnnotation::WidgetAnnotation(WidgetAnnotationPrivate &dd)
+    : Annotation( dd )
+{}
+
+WidgetAnnotation::WidgetAnnotation()
+    : Annotation( *new WidgetAnnotationPrivate() )
+{
+}
+
+WidgetAnnotation::~WidgetAnnotation()
+{
+}
+
+void WidgetAnnotation::store( QDomNode & node, QDomDocument & document ) const
+{
+    // store base annotation properties
+    storeBaseAnnotationProperties( node, document );
+
+    // create [widget] element
+    QDomElement widgetElement = document.createElement( "widget" );
+    node.appendChild( widgetElement );
+}
+
+Annotation::SubType WidgetAnnotation::subType() const
+{
+    return AWidget;
+}
+
+Link* WidgetAnnotation::additionalAction( AdditionalActionType type ) const
+{
+    Q_D( const WidgetAnnotation );
+    return d->additionalAction( type );
+}
+
 //BEGIN utility annotation functions
 QColor convertAnnotColor( AnnotColor *color )
 {
diff --git a/qt4/src/poppler-annotation.h b/qt4/src/poppler-annotation.h
index e511ec0..9208ca7 100644
--- a/qt4/src/poppler-annotation.h
+++ b/qt4/src/poppler-annotation.h
@@ -55,6 +55,7 @@
 class SoundAnnotationPrivate;
 class MovieAnnotationPrivate;
 class ScreenAnnotationPrivate;
+class WidgetAnnotationPrivate;
 class EmbeddedFile;
 class Link;
 class SoundObject;
@@ -106,13 +107,14 @@
 {
   friend class AnnotationUtils;
   friend class LinkMovie;
+  friend class LinkRendition;
 
   public:
     // enum definitions
     // WARNING!!! oKular uses that very same values so if you change them notify the author!
     enum SubType { AText = 1, ALine = 2, AGeom = 3, AHighlight = 4, AStamp = 5,
                    AInk = 6, ALink = 7, ACaret = 8, AFileAttachment = 9, ASound = 10,
-                   AMovie = 11, AScreen = 12 /** \since 0.20 */, A_BASE = 0 };
+                   AMovie = 11, AScreen = 12 /** \since 0.20 */, AWidget = 13 /** \since 0.22 */, A_BASE = 0 };
     enum Flag { Hidden = 1, FixedSize = 2, FixedRotation = 4, DenyPrint = 8,
                 DenyWrite = 16, DenyDelete = 32, ToggleHidingOnMouse = 64, External = 128 };
     enum LineStyle { Solid = 1, Dashed = 2, Beveled = 4, Inset = 8, Underline = 16 };
@@ -272,6 +274,28 @@
      */
     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 );
@@ -840,6 +864,14 @@
      */
     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 );
@@ -848,6 +880,41 @@
     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 )
+};
+
 }
 
 #endif
diff --git a/qt4/src/poppler-document.cc b/qt4/src/poppler-document.cc
index e89b51e..ee56ed6 100644
--- a/qt4/src/poppler-document.cc
+++ b/qt4/src/poppler-document.cc
@@ -5,6 +5,8 @@
  * 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 Thomas Freitag <Thomas.Freitag@alfa.de>
+ * Copyright (C) 2012 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
@@ -519,14 +521,20 @@
 
     void Document::setRenderHint( Document::RenderHint hint, bool on )
     {
+        const bool touchesAntialias = hint & ( Document::Antialiasing | Document::TextAntialiasing | Document::TextHinting );
+        const bool touchesOverprinting = hint & Document::OverprintPreview;
+        
+        int hintForOperation = hint;
+        if (touchesOverprinting && !isOverprintPreviewAvailable())
+            hintForOperation = hintForOperation & ~(int)Document::OverprintPreview;
+
         if ( on )
-            m_doc->m_hints |= hint;
+            m_doc->m_hints |= hintForOperation;
         else
-            m_doc->m_hints &= ~(int)hint;
+            m_doc->m_hints &= ~hintForOperation;
 
         // the only way to set antialiasing for Splash is on creation
-        if ( m_doc->m_backend == Document::SplashBackend &&
-             ( hint & ( Document::Antialiasing | Document::TextAntialiasing | Document::TextHinting ) ) )
+        if ( m_doc->m_backend == Document::SplashBackend && (touchesAntialias || touchesOverprinting) )
         {
             delete m_doc->m_outputDev;
             m_doc->m_outputDev = NULL;
@@ -605,6 +613,21 @@
         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;
@@ -645,4 +668,12 @@
 #endif
     }
 
+    bool isOverprintPreviewAvailable() {
+#if defined(SPLASH_CMYK)
+        return true;
+#else
+        return false;
+#endif
+   }
+
 }
diff --git a/qt4/src/poppler-form.cc b/qt4/src/poppler-form.cc
index 82309ff..a5e5adf 100644
--- a/qt4/src/poppler-form.cc
+++ b/qt4/src/poppler-form.cc
@@ -2,6 +2,7 @@
  * Copyright (C) 2007-2008, 2011, Pino Toscano <pino@kde.org>
  * Copyright (C) 2008, 2011, 2012 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
@@ -226,13 +227,20 @@
 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;
-  unsigned *sibls = fwb->getSiblingsID();
-  for (int i = 0; i < fwb->getNumSiblingsID(); ++i)
-    ret.append(sibls[i]);
+  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;
 }
@@ -372,6 +380,28 @@
     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);
diff --git a/qt4/src/poppler-form.h b/qt4/src/poppler-form.h
index 9af367b..79ed393 100644
--- a/qt4/src/poppler-form.h
+++ b/qt4/src/poppler-form.h
@@ -1,6 +1,7 @@
 /* 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
@@ -305,6 +306,20 @@
 	  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.
diff --git a/qt4/src/poppler-link.cc b/qt4/src/poppler-link.cc
index a4bc55b..199e2db 100644
--- a/qt4/src/poppler-link.cc
+++ b/qt4/src/poppler-link.cc
@@ -168,16 +168,40 @@
 class LinkRenditionPrivate : public LinkPrivate
 {
 	public:
-		LinkRenditionPrivate( const QRectF &area, ::MediaRendition *rendition );
+		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 )
+	LinkRenditionPrivate::LinkRenditionPrivate( const QRectF &area, ::MediaRendition *r, ::LinkRendition::RenditionOperation operation, const QString &javaScript, const Ref &ref )
 		: LinkPrivate( area )
-		, rendition( new MediaRendition( r ) )
+		, 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()
@@ -579,10 +603,15 @@
 
 	// LinkRendition
 	LinkRendition::LinkRendition( const QRectF &linkArea, ::MediaRendition *rendition )
-		: Link( *new LinkRenditionPrivate( linkArea, 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()
 	{
 	}
@@ -598,6 +627,29 @@
 		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 ) )
diff --git a/qt4/src/poppler-link.h b/qt4/src/poppler-link.h
index a2ef2d3..ef93bf0 100644
--- a/qt4/src/poppler-link.h
+++ b/qt4/src/poppler-link.h
@@ -455,12 +455,40 @@
 {
 	public:
 		/**
-		 * Create a new media rendition link.
+		 * 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 
+		 * \param rendition the media rendition object
+		 *
+		 * \deprecated Use the constructor that takes all parameter instead
 		 */
-		LinkRendition( const QRectF &linkArea, ::MediaRendition *rendition );
+		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
+		 * \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.
 		 */
@@ -469,10 +497,31 @@
 		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 )
diff --git a/qt4/src/poppler-movie.cc b/qt4/src/poppler-movie.cc
index 869e381..a64847c 100644
--- a/qt4/src/poppler-movie.cc
+++ b/qt4/src/poppler-movie.cc
@@ -2,6 +2,7 @@
  * 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
@@ -24,6 +25,8 @@
 #include "Annot.h"
 #include "Movie.h"
 
+#include <QtGui/QImage>
+
 namespace Poppler
 {
 
@@ -43,6 +46,7 @@
 	Movie *m_movieObj;
 	QSize m_size;
 	int m_rotation;
+	QImage m_posterImage;
 	MovieObject::PlayMode m_playMode : 3;
 	bool m_showControls : 1;
 };
@@ -51,6 +55,7 @@
 {
 	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;
@@ -92,4 +97,14 @@
 	return m_movieData->m_playMode;
 }
 
+bool MovieObject::showPosterImage() const
+{
+	return (m_movieData->m_movieObj->getShowPoster() == gTrue);
+}
+
+QImage MovieObject::posterImage() const
+{
+	return m_movieData->m_posterImage;
+}
+
 }
diff --git a/qt4/src/poppler-page-private.h b/qt4/src/poppler-page-private.h
index ecdca17..91955e0 100644
--- a/qt4/src/poppler-page-private.h
+++ b/qt4/src/poppler-page-private.h
@@ -1,6 +1,6 @@
 /* poppler-page.cc: qt interface to poppler
  * Copyright (C) 2005, Net Integration Technologies, Inc.
- * Copyright (C) 2007, Albert Astals Cid <aacid@kde.org>
+ * Copyright (C) 2007, 2012, 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
@@ -21,10 +21,13 @@
 #ifndef _POPPLER_PAGE_PRIVATE_H_
 #define _POPPLER_PAGE_PRIVATE_H_
 
+#include "CharTypes.h"
+
 class QRectF;
 
 class LinkAction;
 class Page;
+class TextPage;
 
 namespace Poppler
 {
@@ -42,6 +45,8 @@
   PageTransition *transition;
 
   static Link* convertLinkActionToLink(::LinkAction * a, DocumentData *parentDoc, const QRectF &linkArea);
+  
+  TextPage *prepareTextSearch(const QString &text, Page::SearchMode caseSensitive, Page::Rotation rotate, GBool *sCase, QVector<Unicode> *u);
 };
 
 }
diff --git a/qt4/src/poppler-page.cc b/qt4/src/poppler-page.cc
index 6a16d03..5475614 100644
--- a/qt4/src/poppler-page.cc
+++ b/qt4/src/poppler-page.cc
@@ -1,7 +1,7 @@
 /* poppler-page.cc: qt interface to poppler
  * Copyright (C) 2005, Net Integration Technologies, Inc.
  * Copyright (C) 2005, Brad Hards <bradh@frogmouth.net>
- * Copyright (C) 2005-2011, Albert Astals Cid <aacid@kde.org>
+ * Copyright (C) 2005-2012, 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>
@@ -12,6 +12,8 @@
  * 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 Adam Reichold <adamreichold@myopera.com>
+ * Copyright (C) 2012 Thomas Freitag <Thomas.Freitag@alfa.de>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -196,7 +198,13 @@
     case actionRendition:
     {
       ::LinkRendition *lrn = (::LinkRendition *)a;
-      popplerLink = new LinkRendition( linkArea, lrn->getMedia() );
+
+      Ref reference;
+      reference.num = reference.gen = -1;
+      if ( lrn->hasScreenAnnot() )
+        reference = lrn->getScreenAnnot();
+
+      popplerLink = new LinkRendition( linkArea, lrn->getMedia(), lrn->getOperation(), UnicodeParsedString( lrn->getScript() ), reference );
     }
     break;
 
@@ -207,6 +215,25 @@
   return popplerLink;
 }
 
+TextPage *PageData::prepareTextSearch(const QString &text, Page::SearchMode caseSensitive, Page::Rotation rotate, GBool *sCase, 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();
+
+  if (caseSensitive == Page::CaseSensitive) *sCase = gTrue;
+  else *sCase = gFalse;
+
+  const int rotation = (int)rotate * 90;
+
+  // fetch ourselves a textpage
+  TextOutputDev td(NULL, gTrue, 0, gFalse, gFalse);
+  parentDoc->doc->displayPage( &td, index + 1, 72, 72, rotation, false, true, false );
+  TextPage *textPage=td.takeText();
+  
+  return textPage;
+} 
 
 Page::Page(DocumentData *doc, int index) {
   m_page = new PageData();
@@ -240,27 +267,30 @@
       int bw = bitmap->getWidth();
       int bh = bitmap->getHeight();
 
-      SplashColorPtr dataPtr = splash_output->getBitmap()->getDataPtr();
-
-      if (QSysInfo::BigEndian == QSysInfo::ByteOrder)
+      if (bitmap->convertToXBGR())
       {
-        uchar c;
-        int count = bw * bh * 4;
-        for (int k = 0; k < count; k += 4)
+        SplashColorPtr dataPtr = bitmap->getDataPtr();
+
+        if (QSysInfo::BigEndian == QSysInfo::ByteOrder)
         {
-          c = dataPtr[k];
-          dataPtr[k] = dataPtr[k+3];
-          dataPtr[k+3] = c;
+            uchar c;
+            int count = bw * bh * 4;
+            for (int k = 0; k < count; k += 4)
+            {
+            c = dataPtr[k];
+            dataPtr[k] = dataPtr[k+3];
+            dataPtr[k+3] = c;
 
-          c = dataPtr[k+1];
-          dataPtr[k+1] = dataPtr[k+2];
-          dataPtr[k+2] = c;
+            c = dataPtr[k+1];
+            dataPtr[k+1] = dataPtr[k+2];
+            dataPtr[k+2] = c;
+            }
         }
-      }
 
-      // construct a qimage SHARING the raw bitmap data in memory
-      QImage tmpimg( dataPtr, bw, bh, QImage::Format_ARGB32 );
-      img = tmpimg.copy();
+        // construct a qimage SHARING the raw bitmap data in memory
+        QImage tmpimg( dataPtr, bw, bh, QImage::Format_ARGB32 );
+        img = tmpimg.copy();
+      }
       // unload underlying xpdf bitmap
       splash_output->startPage( 0, NULL );
 #endif
@@ -376,32 +406,19 @@
 
 bool Page::search(const QString &text, double &sLeft, double &sTop, double &sRight, double &sBottom, SearchDirection direction, SearchMode caseSensitive, Rotation rotate) const
 {
-  const QChar * str = text.unicode();
-  int len = text.length();
-  QVector<Unicode> u(len);
-  for (int i = 0; i < len; ++i) u[i] = str[i].unicode();
-
   GBool sCase;
-  if (caseSensitive == CaseSensitive) sCase = gTrue;
-  else sCase = gFalse;
+  QVector<Unicode> u;
+  TextPage *textPage = m_page->prepareTextSearch(text, caseSensitive, rotate, &sCase, &u);
 
   bool found = false;
-
-  int rotation = (int)rotate * 90;
-
-  // fetch ourselves a textpage
-  TextOutputDev td(NULL, gTrue, 0, gFalse, gFalse);
-  m_page->parentDoc->doc->displayPage( &td, m_page->index + 1, 72, 72, rotation, false, true, false );
-  TextPage *textPage=td.takeText();
-
   if (direction == FromTop)
-    found = textPage->findText( u.data(), len, 
+    found = textPage->findText( u.data(), u.size(), 
             gTrue, gTrue, gFalse, gFalse, sCase, gFalse, gFalse, &sLeft, &sTop, &sRight, &sBottom );
   else if ( direction == NextResult )
-    found = textPage->findText( u.data(), len, 
+    found = textPage->findText( u.data(), u.size(), 
             gFalse, gTrue, gTrue, gFalse, sCase, gFalse, gFalse, &sLeft, &sTop, &sRight, &sBottom );
   else if ( direction == PreviousResult )
-    found = textPage->findText( u.data(), len, 
+    found = textPage->findText( u.data(), u.size(), 
             gFalse, gTrue, gTrue, gFalse, sCase, gTrue, gFalse, &sLeft, &sTop, &sRight, &sBottom );
 
   textPage->decRefCnt();
@@ -427,6 +444,33 @@
   return found;
 }
 
+QList<QRectF> Page::search(const QString &text, SearchMode caseSensitive, Rotation rotate) const
+{
+  GBool sCase;
+  QVector<Unicode> u;
+  TextPage *textPage = m_page->prepareTextSearch(text, caseSensitive, rotate, &sCase, &u);
+
+  QList<QRectF> results;
+  double sLeft = 0.0, sTop = 0.0, sRight = 0.0, sBottom = 0.0;
+  
+  while(textPage->findText( u.data(), u.size(), 
+        gFalse, gTrue, gTrue, gFalse, sCase, gFalse, gFalse, &sLeft, &sTop, &sRight, &sBottom ))
+  {
+      QRectF result;
+      
+      result.setLeft(sLeft);
+      result.setTop(sTop);
+      result.setRight(sRight);
+      result.setBottom(sBottom);
+      
+      results.append(result);
+  }
+  
+  textPage->decRefCnt();
+
+  return results;
+}
+
 QList<TextBox*> Page::textList(Rotation rotate) const
 {
   TextOutputDev *output_dev;
diff --git a/qt4/src/poppler-private.h b/qt4/src/poppler-private.h
index 6d2ef2a..5a9e1b8 100644
--- a/qt4/src/poppler-private.h
+++ b/qt4/src/poppler-private.h
@@ -1,10 +1,11 @@
 /* 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 by Albert Astals Cid <aacid@kde.org>
+ * Copyright (C) 2006-2009, 2011, 2012 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 Thomas Freitag <Thomas.Freitag@alfa.de>
  * Inspired on code by
  * Copyright (C) 2004 by Albert Astals Cid <tsdgeos@terra.es>
  * Copyright (C) 2004 by Enrico Ros <eros.kde@email.it>
@@ -124,11 +125,46 @@
 			{
 #if defined(HAVE_SPLASH)
 			SplashColor bgColor;
-			bgColor[0] = paperColor.blue();
-			bgColor[1] = paperColor.green();
-			bgColor[2] = paperColor.red();
+			GBool overprint = m_hints & Document::OverprintPreview ? gTrue : gFalse;
+			globalParams->setOverprintPreview(overprint);
+#if defined(SPLASH_CMYK)
+			if (overprint)
+			{
+				Guchar c, m, y, k;
+
+				c = 255 - paperColor.blue();
+				m = 255 - paperColor.red();
+				y = 255 - 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] = paperColor.blue();
+				bgColor[1] = paperColor.green();
+				bgColor[2] = paperColor.red();
+			}
 			GBool AA = m_hints & Document::TextAntialiasing ? gTrue : gFalse;
-			SplashOutputDev * splashOutputDev = new SplashOutputDev(splashModeXBGR8, 4, gFalse, bgColor, gTrue, AA);
+			SplashOutputDev * splashOutputDev = new SplashOutputDev(
+#if defined(SPLASH_CMYK)
+				(overprint) ? splashModeDeviceN8 : splashModeXBGR8,
+#else
+				splashModeXBGR8,
+#endif 
+				4, gFalse, bgColor, gTrue, AA);
 			splashOutputDev->setVectorAntialias(m_hints & Document::Antialiasing ? gTrue : gFalse);
 			splashOutputDev->setFreeTypeHinting(m_hints & Document::TextHinting ? gTrue : gFalse, m_hints & Document::TextSlightHinting ? gTrue : gFalse);
 			splashOutputDev->startDoc(doc);
@@ -149,25 +185,10 @@
 			return;
 
 		paperColor = color;
-		if ( m_outputDev == NULL )
-			return;
-
-		switch ( m_backend )
-		{
-			case Document::SplashBackend:
-			{
-#if defined(HAVE_SPLASH)
-				SplashOutputDev *splash_output = static_cast<SplashOutputDev *>( m_outputDev );
-				SplashColor bgColor;
-				bgColor[0] = paperColor.blue();
-				bgColor[1] = paperColor.green();
-				bgColor[2] = paperColor.red();
-				splash_output->setPaperColor(bgColor);
-#endif
-				break;
-			}
-			default: ;
-		}
+		
+		// Make sure the new paper color will be picked up for the next rendering
+		delete m_outputDev;
+		m_outputDev = NULL;
 	}
 	
 	void fillMembers()
diff --git a/qt4/src/poppler-qt4.h b/qt4/src/poppler-qt4.h
index f314108..f4f6fc6 100644
--- a/qt4/src/poppler-qt4.h
+++ b/qt4/src/poppler-qt4.h
@@ -11,6 +11,9 @@
  * 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 Adam Reichold <adamreichold@myopera.com>
+ * Copyright (C) 2012 Thomas Freitag <Thomas.Freitag@alfa.de>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -601,6 +604,19 @@
 	   \since 0.14
 	**/
 	bool search(const QString &text, double &rectLeft, double &rectTop, double &rectRight, double &rectBottom, SearchDirection direction, 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 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
+	**/
+	QList<QRectF> search(const QString &text, SearchMode caseSensitive, Rotation rotate = Rotate0) const;
 
 	/**
 	   Returns a list of text of the page
@@ -831,11 +847,23 @@
 	    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
+	    TextSlightHinting = 0x00000008, ///< Lighter hinting for text when combined with TextHinting \since 0.18
+	    OverprintPreview = 0x00000010   ///< Overprint preview \since 0.22
 	};
 	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.
@@ -1347,6 +1375,13 @@
 	bool getPdfId(QByteArray *permanentId, QByteArray *updateId) const;
 
 	/**
+	   Returns the type of forms contained in the document
+
+	   \since 0.22
+	*/
+	FormType formType() const;
+
+	/**
 	   Destructor.
 	*/
 	~Document();
@@ -1609,6 +1644,13 @@
        \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;
     /**
@@ -1733,6 +1775,20 @@
 	*/
 	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 );
diff --git a/qt4/tests/.gitignore b/qt4/tests/.gitignore
index de8b3b4..05b2dbf 100644
--- a/qt4/tests/.gitignore
+++ b/qt4/tests/.gitignore
@@ -12,9 +12,11 @@
 poppler-attachments
 poppler-fonts
 poppler-texts
+check_actualtext
 check_attachments
 check_dateConversion
 check_fonts
+check_links
 check_metadata
 check_optcontent
 check_permissions
@@ -22,4 +24,5 @@
 check_pagemode
 check_password
 check_search
+check_strings
 
diff --git a/qt4/tests/Makefile.am b/qt4/tests/Makefile.am
index 6286d8c..ed38d17 100644
--- a/qt4/tests/Makefile.am
+++ b/qt4/tests/Makefile.am
@@ -5,13 +5,11 @@
 	-I$(top_srcdir)/poppler			\
 	-I$(top_srcdir)/qt4/src			\
 	-DTESTDATADIR=\"$(TESTDATADIR)\"        \
-	$(FONTCONFIG_CFLAGS)			\
 	$(POPPLER_QT4_CFLAGS)
 
 LDADDS =					\
 	$(top_builddir)/poppler/libpoppler.la	\
 	$(top_builddir)/qt4/src/libpoppler-qt4.la	\
-	$(FONTCONFIG_LIBS)				\
 	$(POPPLER_QT4_LIBS)
 
 SUFFIXES: .moc
diff --git a/qt4/tests/check_fonts.cpp b/qt4/tests/check_fonts.cpp
index c57b6da..267595d 100644
--- a/qt4/tests/check_fonts.cpp
+++ b/qt4/tests/check_fonts.cpp
@@ -19,7 +19,7 @@
 };
 
 
-QList<Poppler::FontInfo> loadFontsViaIterator( Poppler::Document *doc, int from = 0, int count = -1 )
+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;
@@ -32,7 +32,9 @@
     return list;
 }
 
-bool operator==( const Poppler::FontInfo &f1, const Poppler::FontInfo &f2 )
+namespace Poppler
+{
+static bool operator==( const FontInfo &f1, const FontInfo &f2 )
 {
     if ( f1.name() != f2.name() )
         return false;
@@ -48,7 +50,7 @@
         return false;
     return true;
 }
-
+}
 
 void TestFontsData::checkNoFonts()
 {
diff --git a/qt4/tests/test-poppler-qt4.cpp b/qt4/tests/test-poppler-qt4.cpp
index 503b35a..46875ef 100644
--- a/qt4/tests/test-poppler-qt4.cpp
+++ b/qt4/tests/test-poppler-qt4.cpp
@@ -186,10 +186,6 @@
     qDebug() << "OK to add notes: " << doc->okToAddNotes();
     qDebug() << "      Page mode: " << doc->pageMode();
     qDebug() << "       Metadata: " << doc->metadata();
-    QStringList fontNameList;
-    foreach( const Poppler::FontInfo &font, doc->fonts() )
-	fontNameList += font.name();
-    qDebug() << "          Fonts: " << fontNameList.join( ", " );
 
     if ( doc->hasEmbeddedFiles() ) {
         qDebug() << "Embedded files:";
diff --git a/regtest/Printer.py b/regtest/Printer.py
new file mode 100644
index 0000000..008f46b
--- /dev/null
+++ b/regtest/Printer.py
@@ -0,0 +1,99 @@
+# Printer.py
+#
+# Copyright (C) 2012 Carlos Garcia Campos <carlosgc@gnome.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 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+import sys
+from Config import Config
+
+class Printer:
+
+    __single = None
+
+    def __init__(self):
+        if Printer.__single is not None:
+            raise Printer.__single
+
+        self._verbose = Config().verbose
+        self._stream = sys.stdout
+        self._rewrite = self._stream.isatty() and not self._verbose
+        self._current_line = None
+
+        Printer.__single = self
+
+    def _erase_current_line(self):
+        if not self._rewrite or self._current_line is None:
+            return
+
+        line_len = len(self._current_line)
+        self._stream.write('\b' * line_len + ' ' * line_len + '\b' * line_len)
+        self._current_line = None
+
+    def _ensure_new_line(self, msg):
+        if not msg.endswith('\n'):
+            msg += '\n'
+        return msg
+
+    def _print(self, msg):
+        self._stream.write(msg)
+        self._stream.flush()
+
+    def printout(self, msg):
+        self._erase_current_line()
+        self._print(msg)
+        self._current_line = msg[msg.rfind('\n') + 1:]
+
+    def printout_update(self, msg):
+        if self._rewrite and self._current_line is not None:
+            msg = self._current_line + msg
+        elif not self._rewrite:
+            msg = self._ensure_new_line(msg)
+        self.printout(msg)
+
+    def printout_ln(self, msg):
+        if self._current_line is not None:
+            self._current_line = None
+            msg = '\n' + msg
+
+        self._print(self._ensure_new_line(msg))
+
+    def printerr(self, msg):
+        self.stderr.write(self._ensure_new_line(msg))
+        self.stderr.flush()
+
+    def print_test_start(self, msg):
+        self.printout(msg)
+
+    def print_test_result(self, msg):
+        self.printout_update(msg)
+
+    def print_test_result_ln(self, msg):
+        self.printout_update(self._ensure_new_line(msg))
+
+    def print_default(self, msg):
+        if self._verbose:
+            self.printout_ln(msg)
+
+def get_printer():
+    try:
+        instance = Printer()
+    except Printer, i:
+        instance = i
+
+    return instance
+
+
+
diff --git a/regtest/TestReferences.py b/regtest/TestReferences.py
index d65d30d..9a9e923 100644
--- a/regtest/TestReferences.py
+++ b/regtest/TestReferences.py
@@ -20,6 +20,7 @@
 import errno
 from backends import get_backend, get_all_backends
 from Config import Config
+from Printer import get_printer
 from Utils import get_document_paths_from_dir, get_skipped_tests
 
 class TestReferences:
@@ -29,6 +30,7 @@
         self._refsdir = refsdir
         self._skipped = get_skipped_tests(docsdir)
         self.config = Config()
+        self.printer = get_printer()
 
         try:
             os.makedirs(self._refsdir)
@@ -40,7 +42,7 @@
 
     def create_refs_for_file(self, filename, n_doc = 1, total_docs = 1):
         if filename in self._skipped:
-            print("Skipping test '%s' (%d/%d)" % (os.path.join(self._docsdir, filename), n_doc, total_docs))
+            self.printer.print_default("Skipping test '%s' (%d/%d)" % (os.path.join(self._docsdir, filename), n_doc, total_docs))
             return
 
         refs_path = os.path.join(self._refsdir, filename)
@@ -60,9 +62,9 @@
 
         for backend in backends:
             if not self.config.force and backend.has_results(refs_path):
-                print("Results found, skipping '%s' for %s backend (%d/%d)" % (doc_path, backend.get_name(), n_doc, total_docs))
+                self.printer.print_default("Results found, skipping '%s' for %s backend (%d/%d)" % (doc_path, backend.get_name(), n_doc, total_docs))
                 continue
-            print("Creating refs for '%s' using %s backend (%d/%d)" % (doc_path, backend.get_name(), n_doc, total_docs))
+            self.printer.printout_ln("Creating refs for '%s' using %s backend (%d/%d)" % (doc_path, backend.get_name(), n_doc, total_docs))
             if backend.create_refs(doc_path, refs_path):
                 backend.create_checksums(refs_path, self.config.checksums_only)
 
diff --git a/regtest/TestRun.py b/regtest/TestRun.py
index cffd00b..f4e5051 100644
--- a/regtest/TestRun.py
+++ b/regtest/TestRun.py
@@ -19,6 +19,7 @@
 from backends import get_backend, get_all_backends
 from Config import Config
 from Utils import get_document_paths_from_dir, get_skipped_tests
+from Printer import get_printer
 import sys
 import os
 import errno
@@ -31,6 +32,7 @@
         self._outdir = outdir
         self._skip = get_skipped_tests(docsdir)
         self.config = Config()
+        self.printer = get_printer()
 
         # Results
         self._n_tests = 0
@@ -56,12 +58,11 @@
         ref_is_failed = backend.is_failed(refs_path)
         if not ref_has_md5 and not ref_is_crashed and not ref_is_failed:
             self._skipped.append("%s (%s)" % (doc_path, backend.get_name()))
-            print("Reference files not found, skipping '%s' for %s backend" % (doc_path, backend.get_name()))
+            self.printer.print_default("Reference files not found, skipping '%s' for %s backend" % (doc_path, backend.get_name()))
             return
 
         self._n_tests += 1
-        sys.stdout.write("Testing '%s' using %s backend (%d/%d): " % (doc_path, backend.get_name(), n_doc, total_docs))
-        sys.stdout.flush()
+        self.printer.print_test_start("Testing '%s' using %s backend (%d/%d): " % (doc_path, backend.get_name(), n_doc, total_docs))
         test_has_md5 = backend.create_refs(doc_path, test_path)
 
         if backend.has_stderr(test_path):
@@ -70,40 +71,39 @@
         if ref_has_md5 and test_has_md5:
             if backend.compare_checksums(refs_path, test_path, not self.config.keep_results, self.config.create_diffs, self.config.update_refs):
                 # FIXME: remove dir if it's empty?
-                print("PASS")
+                self.printer.print_test_result("PASS")
                 self._n_passed += 1
             else:
-                print("FAIL")
+                self.printer.print_test_result_ln("FAIL")
                 self._failed.append("%s (%s)" % (doc_path, backend.get_name()))
             return
         elif test_has_md5:
             if ref_is_crashed:
-                print("DOES NOT CRASH")
+                self.printer.print_test_result_ln("DOES NOT CRASH")
             elif ref_is_failed:
-                print("DOES NOT FAIL")
-
+                self.printer.print_test_result_ln("DOES NOT FAIL")
             return
 
         test_is_crashed = backend.is_crashed(test_path)
         if ref_is_crashed and test_is_crashed:
-            print("PASS (Expected crash)")
+            self.printer.print_test_result("PASS (Expected crash)")
             self._n_passed += 1
             return
 
         test_is_failed = backend.is_failed(test_path)
         if ref_is_failed and test_is_failed:
             # FIXME: compare status errors
-            print("PASS (Expected fail with status error %d)" % (test_is_failed))
+            self.printer.print_test_result("PASS (Expected fail with status error %d)" % (test_is_failed))
             self._n_passed += 1
             return
 
         if test_is_crashed:
-            print("CRASH")
+            self.printer.print_test_result_ln("CRASH")
             self._crashed.append("%s (%s)" % (doc_path, backend.get_name()))
             return
 
         if test_is_failed:
-            print("FAIL (status error %d)" % (test_is_failed))
+            self.printer.print_test_result_ln("FAIL (status error %d)" % (test_is_failed))
             self._failed_status_error("%s (%s)" % (doc_path, backend.get_name()))
             return
 
@@ -111,7 +111,7 @@
         if filename in self._skip:
             doc_path = os.path.join(self._docsdir, filename)
             self._skipped.append("%s" % (doc_path))
-            print("Skipping test '%s' (%d/%d)" % (doc_path, n_doc, total_docs))
+            self.printer.print_default("Skipping test '%s' (%d/%d)" % (doc_path, n_doc, total_docs))
             return
 
         out_path = os.path.join(self._outdir, filename)
@@ -127,7 +127,7 @@
 
         if not os.path.isdir(refs_path):
             self._skipped.append("%s" % (doc_path))
-            print("Reference dir not found for %s, skipping (%d/%d)" % (doc_path, n_doc, total_docs))
+            self.printer.print_default("Reference dir not found for %s, skipping (%d/%d)" % (doc_path, n_doc, total_docs))
             return
 
         if self.config.backends:
@@ -147,16 +147,17 @@
 
     def summary(self):
         if not self._n_tests:
-            print("No tests run")
+            self.printer.printout_ln("No tests run")
             return
 
-        print("Total %d tests" % (self._n_tests))
-        print("%d tests passed (%.2f%%)" % (self._n_passed, (self._n_passed * 100.) / self._n_tests))
+        self.printer.printout_ln("Total %d tests" % (self._n_tests))
+        self.printer.printout_ln("%d tests passed (%.2f%%)" % (self._n_passed, (self._n_passed * 100.) / self._n_tests))
         def report_tests(test_list, test_type):
             n_tests = len(test_list)
             if not n_tests:
                 return
-            print("%d tests %s (%.2f%%): %s" % (n_tests, test_type, (n_tests * 100.) / self._n_tests, ", ".join(test_list)))
+            self.printer.printout_ln("%d tests %s (%.2f%%): %s" % (n_tests, test_type, (n_tests * 100.) / self._n_tests, ", ".join(test_list)))
+
         report_tests(self._failed, "failed")
         report_tests(self._crashed, "crashed")
         report_tests(self._failed_status_error, "failed to run")
diff --git a/regtest/backends/__init__.py b/regtest/backends/__init__.py
index 26be0b0..ff6ef84 100644
--- a/regtest/backends/__init__.py
+++ b/regtest/backends/__init__.py
@@ -21,6 +21,7 @@
 import shutil
 import errno
 from Config import Config
+from Printer import get_printer
 
 __all__ = [ 'register_backend',
             'get_backend',
@@ -38,6 +39,8 @@
         self._diff_ext = diff_ext
         self._utilsdir = Config().utils_dir
 
+        self.printer = get_printer()
+
     def get_name(self):
         return self._name
 
@@ -83,7 +86,7 @@
 
             if not basename in tests:
                 retval = False
-                print("%s found in md5 ref file but missing in output dir %s" % (basename, out_path))
+                self.printer.print_default("%s found in md5 ref file but missing in output dir %s" % (basename, out_path))
                 continue
 
             result_path = os.path.join(out_path, basename)
@@ -99,10 +102,10 @@
                 if remove_results:
                     os.remove(result_path)
             else:
-                print("Differences found in %s" % (basename))
+                self.printer.print_default("Differences found in %s" % (basename))
                 if create_diffs:
                     if not os.path.exists(ref_path):
-                        print("Reference file %s not found, skipping diff for %s" % (ref_path, result_path))
+                        self.printer.print_default("Reference file %s not found, skipping diff for %s" % (ref_path, result_path))
                     else:
                         try:
                             self._create_diff(ref_path, result_path)
@@ -112,14 +115,14 @@
 
                 if update_refs:
                     if os.path.exists(ref_path):
-                        print("Updating image reference %s" % (ref_path))
+                        self.printer.print_default("Updating image reference %s" % (ref_path))
                         shutil.copyfile(result_path, ref_path)
 
                 retval = False
         md5_file.close()
 
         if update_refs and not retval:
-            print("Updating md5 reference %s" % (md5_path))
+            self.printer.print_default("Updating md5 reference %s" % (md5_path))
             f = open(md5_path + '.md5.tmp', 'wb')
             f.writelines(result_md5)
             f.close()
diff --git a/regtest/commands/create-refs.py b/regtest/commands/create-refs.py
index b055703..d559fb3 100644
--- a/regtest/commands/create-refs.py
+++ b/regtest/commands/create-refs.py
@@ -20,6 +20,7 @@
 from TestReferences import TestReferences
 from Timer import Timer
 from Config import Config
+from Printer import get_printer
 import os
 import tempfile
 
@@ -60,6 +61,6 @@
             refs.create_refs()
         else:
             refs.create_refs_for_file(os.path.basename(doc))
-        print("Refs created in %s" % (t.elapsed_str()))
+        get_printer().printout_ln("Refs created in %s" % (t.elapsed_str()))
 
 register_command('create-refs', CreateRefs)
diff --git a/regtest/commands/find-regression.py b/regtest/commands/find-regression.py
index 1a46eee..8f5f811 100644
--- a/regtest/commands/find-regression.py
+++ b/regtest/commands/find-regression.py
@@ -20,6 +20,7 @@
 from Bisect import Bisect
 from Timer import Timer
 from Config import Config
+from Printer import get_printer
 import os
 import tempfile
 
@@ -66,12 +67,12 @@
 
         doc = options['test']
         if not os.path.isfile(doc):
-            print("Invalid test %s: not a regulat file" % (doc))
+            get_printer().printerr("Invalid test %s: not a regulat file" % (doc))
             return
 
         t = Timer()
         bisect = Bisect(options['test'], options['refs_dir'], options['out_dir'])
         bisect.run()
-        print("Tests run in %s" % (t.elapsed_str()))
+        get_printer().printout_ln("Tests run in %s" % (t.elapsed_str()))
 
 register_command('find-regression', FindRegression)
diff --git a/regtest/commands/run-tests.py b/regtest/commands/run-tests.py
index d05d815..c5d87f9 100644
--- a/regtest/commands/run-tests.py
+++ b/regtest/commands/run-tests.py
@@ -20,6 +20,7 @@
 from TestRun import TestRun
 from Timer import Timer
 from Config import Config
+from Printer import get_printer
 import os
 import tempfile
 
@@ -68,6 +69,6 @@
         else:
             tests.run_test(os.path.basename(doc))
         tests.summary()
-        print("Tests run in %s" % (t.elapsed_str()))
+        get_printer().printout_ln("Tests run in %s" % (t.elapsed_str()))
 
 register_command('run-tests', RunTests)
diff --git a/regtest/main.py b/regtest/main.py
index a46a64c..290c8bc 100644
--- a/regtest/main.py
+++ b/regtest/main.py
@@ -49,6 +49,9 @@
     parser.add_argument('--help-command', metavar = 'COMMAND',
                         action = HelpAction,
                         help = 'Show help for a given command')
+    parser.add_argument('-v', '--verbose',
+                        action = 'store_true', dest = 'verbose', default = False,
+                        help = 'Run in verbose mode')
     parser.add_argument('--utils-dir',
                         action = 'store', dest = 'utils_dir', default = os.path.abspath("../utils"),
                         help = 'Directory of poppler utils used for the tests')
diff --git a/splash/Splash.cc b/splash/Splash.cc
index e6559f4..e46a496 100644
--- a/splash/Splash.cc
+++ b/splash/Splash.cc
@@ -36,6 +36,7 @@
 #include <math.h>
 #include "goo/gmem.h"
 #include "goo/GooLikely.h"
+#include "goo/GooList.h"
 #include "poppler/Error.h"
 #include "SplashErrorCodes.h"
 #include "SplashMath.h"
@@ -170,7 +171,8 @@
   splashPipeResultColorNoAlphaBlendRGB
 #if SPLASH_CMYK
   ,
-  splashPipeResultColorNoAlphaBlendCMYK
+  splashPipeResultColorNoAlphaBlendCMYK,
+  splashPipeResultColorNoAlphaBlendDeviceN
 #endif
 };
 
@@ -182,7 +184,8 @@
   splashPipeResultColorAlphaNoBlendRGB
 #if SPLASH_CMYK
   ,
-  splashPipeResultColorAlphaNoBlendCMYK
+  splashPipeResultColorAlphaNoBlendCMYK,
+  splashPipeResultColorAlphaNoBlendDeviceN
 #endif
 };
 
@@ -194,7 +197,8 @@
   splashPipeResultColorAlphaBlendRGB
 #if SPLASH_CMYK
   ,
-  splashPipeResultColorAlphaBlendCMYK
+  splashPipeResultColorAlphaBlendCMYK,
+  splashPipeResultColorAlphaBlendDeviceN
 #endif
 };
 
@@ -307,6 +311,8 @@
 #if SPLASH_CMYK
     } else if (bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr) {
       pipe->run = &Splash::pipeRunSimpleCMYK8;
+    } else if (bitmap->mode == splashModeDeviceN8 && pipe->destAlphaPtr) {
+      pipe->run = &Splash::pipeRunSimpleDeviceN8;
 #endif
     }
   } else if (!pipe->pattern && !pipe->noTransparency && !state->softMask &&
@@ -326,6 +332,8 @@
 #if SPLASH_CMYK
     } else if (bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr) {
       pipe->run = &Splash::pipeRunAACMYK8;
+    } else if (bitmap->mode == splashModeDeviceN8 && pipe->destAlphaPtr) {
+      pipe->run = &Splash::pipeRunAADeviceN8;
 #endif
     }
   }
@@ -338,6 +346,10 @@
   SplashColorPtr cSrc;
   Guchar cResult0, cResult1, cResult2, cResult3;
   int t;
+#if SPLASH_CMYK
+  int cp, mask;
+  Guchar cResult[SPOT_NCOMPS+4];
+#endif
 
   //----- source color
 
@@ -412,6 +424,16 @@
       }
       pipe->destColorPtr += 4;
       break;
+    case splashModeDeviceN8:
+      mask = 1;
+      for (cp = 0; cp < SPOT_NCOMPS + 4; cp ++) {
+        if (state->overprintMask & mask) {
+          pipe->destColorPtr[cp] = state->deviceNTransfer[cp][pipe->cSrc[cp]];
+        }
+        mask <<= 1;
+      }
+      pipe->destColorPtr += (SPOT_NCOMPS+4);
+      break;
 #endif
     }
     if (pipe->destAlphaPtr) {
@@ -452,6 +474,10 @@
       cDest[2] = pipe->destColorPtr[2];
       cDest[3] = pipe->destColorPtr[3];
       break;
+    case splashModeDeviceN8:
+      for (cp = 0; cp < SPOT_NCOMPS + 4; cp++)
+        cDest[cp] = pipe->destColorPtr[cp];
+      break;
 #endif
     }
     if (pipe->destAlphaPtr) {
@@ -489,6 +515,10 @@
 	t = (aDest * 255) / pipe->shape - aDest;
 	switch (bitmap->mode) {
 #if SPLASH_CMYK
+	case splashModeDeviceN8:
+	  for (cp = 4; cp < SPOT_NCOMPS + 4; cp++)
+	    cSrcNonIso[cp] = clip255(pipe->cSrc[cp] +
+				  ((pipe->cSrc[cp] - cDest[cp]) * t) / 255);
 	case splashModeCMYK8:
 	  cSrcNonIso[3] = clip255(pipe->cSrc[3] +
 				  ((pipe->cSrc[3] - cDest[3]) * t) / 255);
@@ -570,6 +600,11 @@
       cResult3 = state->cmykTransferK[div255((255 - aDest) * cSrc[3] +
 					     aDest * cBlend[3])];
       break;
+    case splashPipeResultColorNoAlphaBlendDeviceN:
+      for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
+        cResult[cp] = state->deviceNTransfer[cp][div255((255 - aDest) * cSrc[cp] +
+					     aDest * cBlend[cp])];
+      break;
 #endif
 
     case splashPipeResultColorAlphaNoBlendMono:
@@ -612,6 +647,16 @@
 					 aSrc * cSrc[3]) / alphaI];
       }
       break;
+    case splashPipeResultColorAlphaNoBlendDeviceN:
+      if (alphaI == 0) {
+        for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
+          cResult[cp] = 0;
+      } else {
+        for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
+          cResult[cp] = state->deviceNTransfer[cp][((alphaI - aSrc) * cDest[cp] +
+					 aSrc * cSrc[cp]) / alphaI];
+      }
+      break;
 #endif
 
     case splashPipeResultColorAlphaBlendMono:
@@ -670,6 +715,18 @@
 					alphaI];
       }
       break;
+    case splashPipeResultColorAlphaBlendDeviceN:
+      if (alphaI == 0) {
+        for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
+          cResult[cp] = 0;
+      } else {
+        for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
+          cResult[cp] = state->deviceNTransfer[cp][((alphaI - aSrc) * cDest[cp] +
+            aSrc * ((255 - alphaIm1) * cSrc[cp] +
+						alphaIm1 * cBlend[cp]) / 255) /
+					  alphaI];
+      }
+      break;
 #endif
     }
 
@@ -730,6 +787,16 @@
       }
       pipe->destColorPtr += 4;
       break;
+    case splashModeDeviceN8:
+      mask = 1;
+      for (cp = 0; cp < SPOT_NCOMPS+4; cp++) {
+        if (state->overprintMask & mask) {
+          pipe->destColorPtr[cp] = cResult[cp];
+        }
+        mask <<=1;
+      }
+      pipe->destColorPtr += (SPOT_NCOMPS+4);
+      break;
 #endif
     }
     if (pipe->destAlphaPtr) {
@@ -844,8 +911,25 @@
 
   ++pipe->x;
 }
-#endif
 
+// special case:
+// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
+// bitmap->mode == splashModeDeviceN8 && pipe->destAlphaPtr) {
+void Splash::pipeRunSimpleDeviceN8(SplashPipe *pipe) {
+  //----- write destination pixel
+  int mask = 1;
+  for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) {
+    if (state->overprintMask & mask) {
+      pipe->destColorPtr[cp] = state->deviceNTransfer[cp][pipe->cSrc[cp]];
+    }
+    mask <<=1;
+  }
+  pipe->destColorPtr += (SPOT_NCOMPS+4);
+  *pipe->destAlphaPtr++ = 255;
+
+  ++pipe->x;
+}
+#endif
 
 // special case:
 // !pipe->pattern && !pipe->noTransparency && !state->softMask &&
@@ -1125,6 +1209,53 @@
 
   ++pipe->x;
 }
+
+// special case:
+// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
+// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
+// !pipe->nonIsolatedGroup &&
+// bitmap->mode == splashModeDeviceN8 && pipe->destAlphaPtr
+void Splash::pipeRunAADeviceN8(SplashPipe *pipe) {
+  Guchar aSrc, aDest, alpha2, aResult;
+  SplashColor cDest;
+  Guchar cResult[SPOT_NCOMPS+4];
+  int cp, mask;
+
+  //----- read destination pixel
+  for (cp=0; cp < SPOT_NCOMPS+4; cp++)
+    cDest[cp] = pipe->destColorPtr[cp];
+  aDest = *pipe->destAlphaPtr;
+
+  //----- source alpha
+  aSrc = div255(pipe->aInput * pipe->shape);
+
+  //----- result alpha and non-isolated group element correction
+  aResult = aSrc + aDest - div255(aSrc * aDest);
+  alpha2 = aResult;
+
+  //----- result color
+  if (alpha2 == 0) {
+    for (cp=0; cp < SPOT_NCOMPS+4; cp++)
+      cResult[cp] = 0;
+  } else {
+    for (cp=0; cp < SPOT_NCOMPS+4; cp++)
+      cResult[cp] = state->deviceNTransfer[cp][(Guchar)(((alpha2 - aSrc) * cDest[cp] +
+					      aSrc * pipe->cSrc[cp]) / alpha2)];
+  }
+
+  //----- write destination pixel
+  mask = 1;
+  for (cp=0; cp < SPOT_NCOMPS+4; cp++) {
+    if (state->overprintMask & mask) {
+      pipe->destColorPtr[cp] = cResult[cp];
+    }
+    mask <<= 1;
+  }
+  pipe->destColorPtr += (SPOT_NCOMPS+4);
+  *pipe->destAlphaPtr++ = aResult;
+
+  ++pipe->x;
+}
 #endif
 
 inline void Splash::pipeSetXY(SplashPipe *pipe, int x, int y) {
@@ -1153,6 +1284,9 @@
   case splashModeCMYK8:
     pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x];
     break;
+  case splashModeDeviceN8:
+    pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + (SPOT_NCOMPS + 4) * x];
+    break;
 #endif
   }
   if (bitmap->alpha) {
@@ -1195,6 +1329,9 @@
   case splashModeCMYK8:
     pipe->destColorPtr += 4;
     break;
+  case splashModeDeviceN8:
+    pipe->destColorPtr += (SPOT_NCOMPS+4);
+    break;
 #endif
   }
   if (pipe->destAlphaPtr) {
@@ -1774,6 +1911,17 @@
       }
     }
     break;
+  case splashModeDeviceN8:
+    row = bitmap->data;
+    for (y = 0; y < bitmap->height; ++y) {
+      p = row;
+      for (x = 0; x < bitmap->width; ++x) {
+        for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
+          *p++ = color[cp];
+      }
+      row += bitmap->rowSize;
+    }
+    break;
 #endif
   }
 
@@ -3355,7 +3503,7 @@
 SplashError Splash::drawImage(SplashImageSource src, void *srcData,
 			      SplashColorMode srcMode, GBool srcAlpha,
 			      int w, int h, SplashCoord *mat,
-			      GBool tilingPattern) {
+            GBool tilingPattern) {
   GBool ok;
   SplashBitmap *scaledImg;
   SplashClipResult clipRes;
@@ -3396,6 +3544,10 @@
     ok = srcMode == splashModeCMYK8;
     nComps = 4;
     break;
+  case splashModeDeviceN8:
+    ok = srcMode == splashModeDeviceN8;
+    nComps = SPOT_NCOMPS+4;
+    break;
 #endif
   default:
     ok = gFalse;
@@ -3496,7 +3648,7 @@
 				     GBool srcAlpha,
 				     int srcWidth, int srcHeight,
 				     SplashCoord *mat,
-				     GBool tilingPattern) {
+             GBool tilingPattern) {
   SplashBitmap *scaledImg;
   SplashClipResult clipRes, clipRes2;
   SplashPipe pipe;
@@ -3621,7 +3773,7 @@
   }
   scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha,
 			 srcWidth, srcHeight, scaledWidth, scaledHeight);
-  
+
   if (scaledImg == NULL) {
     return splashErrBadArg;
   }
@@ -3802,7 +3954,7 @@
 				 int scaledWidth, int scaledHeight) {
   SplashBitmap *dest;
 
-  dest = new SplashBitmap(scaledWidth, scaledHeight, 1, srcMode, srcAlpha);
+  dest = new SplashBitmap(scaledWidth, scaledHeight, 1, srcMode, srcAlpha, gTrue, bitmap->getSeparationList());
   if (dest->getDataPtr() != NULL) {
     if (scaledHeight < srcHeight) {
       if (scaledWidth < srcWidth) {
@@ -3838,6 +3990,7 @@
   Guint pix0, pix1, pix2;
 #if SPLASH_CMYK
   Guint pix3;
+  Guint pix[SPOT_NCOMPS+4], cp;
 #endif
   Guint alpha;
   Guchar *destPtr, *destAlphaPtr;
@@ -4017,6 +4170,25 @@
 	*destPtr++ = (Guchar)pix2;
 	*destPtr++ = (Guchar)pix3;
 	break;
+      case splashModeDeviceN8:
+
+	// compute the final pixel
+  for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
+    pix[cp] = 0;
+	for (i = 0; i < xStep; ++i) {
+    for (cp = 0; cp < SPOT_NCOMPS+4; cp++) {
+      pix[cp] += pixBuf[xx + cp];
+    }
+    xx += (SPOT_NCOMPS+4);
+	}
+	// pix / xStep * yStep
+  for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
+    pix[cp] = (pix[cp] * d) >> 23;
+
+	// store the pixel
+  for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
+    *destPtr++ = (Guchar)pix[cp];
+	break;
 #endif
 
 
@@ -4168,6 +4340,12 @@
 	  *destPtr++ = (Guchar)pix[3];
 	}
 	break;
+      case splashModeDeviceN8:
+	for (i = 0; i < xStep; ++i) {
+    for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
+      *destPtr++ = (Guchar)pix[cp];
+	}
+	break;
 #endif
       }
 
@@ -4311,6 +4489,13 @@
 	  *destPtr++ = (Guchar)pix[3];
 	}
 	break;
+      case splashModeDeviceN8:
+	for (i = 0; i < yStep; ++i) {
+	  destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
+    for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
+      *destPtr++ = (Guchar)pix[cp];
+	}
+	break;
 #endif
       }
 
@@ -4459,6 +4644,15 @@
 	  }
 	}
 	break;
+      case splashModeDeviceN8:
+	for (i = 0; i < yStep; ++i) {
+	  for (j = 0; j < xStep; ++j) {
+	    destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
+      for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
+        *destPtr++ = (Guchar)pix[cp];
+	  }
+	}
+	break;
 #endif
       }
 
@@ -4681,6 +4875,10 @@
     return splashErrModeMismatch;
   }
 
+  if(src->getSeparationList()->getLength() > bitmap->getSeparationList()->getLength()) {
+    for (x = bitmap->getSeparationList()->getLength(); x < src->getSeparationList()->getLength(); x++)
+      bitmap->getSeparationList()->append(((GfxSeparationColorSpace *)src->getSeparationList()->get(x))->copy());
+  }
   if (src->alpha) {
     pipeInit(&pipe, xDest, yDest, NULL, pixel,
 	     (Guchar)splashRound(state->fillAlpha * 255), gTrue, nonIsolated,
@@ -4694,7 +4892,7 @@
 	  alpha = *ap++;
 	  // this uses shape instead of alpha, which isn't technically
 	  // correct, but works out the same
-	  pipe.shape = alpha;
+    pipe.shape = alpha;
 	  (this->*pipe.run)(&pipe);
 	}
       }
@@ -4712,7 +4910,7 @@
 	  if (state->clip->test(xDest + x, yDest + y)) {
 	    // this uses shape instead of alpha, which isn't technically
 	    // correct, but works out the same
-	    pipe.shape = alpha;
+      pipe.shape = alpha;
 	    (this->*pipe.run)(&pipe);
 	    updateModX(xDest + x);
 	    updateModY(yDest + y);
@@ -4763,6 +4961,7 @@
   Guchar alpha, alpha1, c, color0, color1, color2;
 #if SPLASH_CMYK
   Guchar color3;
+  Guchar colorsp[SPOT_NCOMPS+4], cp;
 #endif
   int x, y, mask;
 
@@ -4892,6 +5091,29 @@
       }
     }
     break;
+  case splashModeDeviceN8:
+    for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
+      colorsp[cp] = color[cp];
+    for (y = 0; y < bitmap->height; ++y) {
+      p = &bitmap->data[y * bitmap->rowSize];
+      q = &bitmap->alpha[y * bitmap->width];
+      for (x = 0; x < bitmap->width; ++x) {
+	alpha = *q++;
+	if (alpha == 0)
+	{
+    for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
+      p[cp] = colorsp[cp];
+	}
+	else if (alpha != 255)
+	{
+	  alpha1 = 255 - alpha;
+    for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
+      p[cp] = div255(alpha1 * colorsp[cp] + alpha * p[cp]);
+	}
+	p += (SPOT_NCOMPS+4);
+      }
+    }
+    break;
 #endif
   }
   memset(bitmap->alpha, 255, bitmap->width * bitmap->height);
@@ -4951,6 +5173,9 @@
     case splashModeCMYK8:
       colorComps=4;
     break;
+    case splashModeDeviceN8:
+      colorComps=SPOT_NCOMPS+4;
+    break;
 #endif
   }
 
@@ -5289,6 +5514,16 @@
       }
     }
     break;
+  case splashModeDeviceN8:
+    for (y = 0; y < h; ++y) {
+      p = &bitmap->data[(yDest + y) * bitmap->rowSize + (SPOT_NCOMPS+4) * xDest];
+      sp = &src->data[(ySrc + y) * src->rowSize + (SPOT_NCOMPS+4) * xSrc];
+      for (x = 0; x < w; ++x) {
+        for (int cp=0; cp < SPOT_NCOMPS+4; cp++)
+          *p++ = *sp++;
+      }
+    }
+    break;
 #endif
   }
 
diff --git a/splash/Splash.h b/splash/Splash.h
index f4fb542..223afdd 100644
--- a/splash/Splash.h
+++ b/splash/Splash.h
@@ -60,6 +60,7 @@
 enum SplashPipeResultColorCtrl {
 #if SPLASH_CMYK
   splashPipeResultColorNoAlphaBlendCMYK,
+  splashPipeResultColorNoAlphaBlendDeviceN,
 #endif
   splashPipeResultColorNoAlphaBlendRGB,
   splashPipeResultColorNoAlphaBlendMono,
@@ -67,12 +68,14 @@
   splashPipeResultColorAlphaNoBlendRGB,
 #if SPLASH_CMYK
   splashPipeResultColorAlphaNoBlendCMYK,
+  splashPipeResultColorAlphaNoBlendDeviceN,
 #endif
   splashPipeResultColorAlphaBlendMono,
   splashPipeResultColorAlphaBlendRGB
 #if SPLASH_CMYK
   ,
-  splashPipeResultColorAlphaBlendCMYK
+  splashPipeResultColorAlphaBlendCMYK,
+  splashPipeResultColorAlphaBlendDeviceN
 #endif
 };
 
@@ -285,6 +288,7 @@
   void pipeRunSimpleBGR8(SplashPipe *pipe);
 #if SPLASH_CMYK
   void pipeRunSimpleCMYK8(SplashPipe *pipe);
+  void pipeRunSimpleDeviceN8(SplashPipe *pipe);
 #endif
   void pipeRunAAMono1(SplashPipe *pipe);
   void pipeRunAAMono8(SplashPipe *pipe);
@@ -293,6 +297,7 @@
   void pipeRunAABGR8(SplashPipe *pipe);
 #if SPLASH_CMYK
   void pipeRunAACMYK8(SplashPipe *pipe);
+  void pipeRunAADeviceN8(SplashPipe *pipe);
 #endif
   void pipeSetXY(SplashPipe *pipe, int x, int y);
   void pipeIncX(SplashPipe *pipe);
diff --git a/splash/SplashBitmap.cc b/splash/SplashBitmap.cc
index cd85543..996f0aa 100644
--- a/splash/SplashBitmap.cc
+++ b/splash/SplashBitmap.cc
@@ -45,6 +45,7 @@
 #include "goo/PNGWriter.h"
 #include "goo/TiffWriter.h"
 #include "goo/ImgWriter.h"
+#include "goo/GooList.h"
 
 //------------------------------------------------------------------------
 // SplashBitmap
@@ -52,7 +53,7 @@
 
 SplashBitmap::SplashBitmap(int widthA, int heightA, int rowPadA,
 			   SplashColorMode modeA, GBool alphaA,
-			   GBool topDown) {
+			   GBool topDown, GooList *separationListA) {
   width = widthA;
   height = heightA;
   mode = modeA;
@@ -95,6 +96,13 @@
       rowSize = -1;
     }
     break;
+  case splashModeDeviceN8:
+    if (width > 0 && width <= INT_MAX / 4) {
+      rowSize = width * (SPOT_NCOMPS + 4);
+    } else {
+      rowSize = -1;
+    }
+    break;
 #endif
   }
   if (rowSize > 0) {
@@ -115,11 +123,15 @@
   } else {
     alpha = NULL;
   }
+  separationList = new GooList();
+  if (separationListA != NULL)
+    for (int i = 0; i < separationListA->getLength(); i++)
+      separationList->append(((GfxSeparationColorSpace *) separationListA->get(i))->copy());
 }
 
 SplashBitmap *SplashBitmap::copy(SplashBitmap *src) {
   SplashBitmap *result = new SplashBitmap(src->getWidth(), src->getHeight(), src->getRowPad(), 
-        src->getMode(), src->getAlphaPtr() != NULL, src->getRowSize() >= 0);
+    src->getMode(), src->getAlphaPtr() != NULL, src->getRowSize() >= 0, src->getSeparationList());
   Guchar *dataSource = src->getDataPtr();
   Guchar *dataDest = result->getDataPtr();
   int amount = src->getRowSize();
@@ -146,6 +158,7 @@
     }
   }
   gfree(alpha);
+  deleteGooList(separationList, GfxSeparationColorSpace);
 }
 
 
@@ -234,6 +247,7 @@
 
 #if SPLASH_CMYK
   case splashModeCMYK8:
+  case splashModeDeviceN8:
     // PNM doesn't support CMYK
     error(errInternal, -1, "unsupported SplashBitmap mode");
     return splashErrGeneric;
@@ -300,6 +314,11 @@
     pixel[2] = p[2];
     pixel[3] = p[3];
     break;
+  case splashModeDeviceN8:
+    p = &data[y * rowSize + (SPOT_NCOMPS + 4) * x];
+    for (int cp = 0; cp < SPOT_NCOMPS + 4; cp++)
+      pixel[cp] = p[cp];
+    break;
 #endif
   }
 }
@@ -386,6 +405,31 @@
     m = byteToDbl(col[1]);
     y = byteToDbl(col[2]);
     k = byteToDbl(col[3]);
+#if SPLASH_CMYK
+    if (separationList->getLength() > 0) {
+      for (int i = 0; i < separationList->getLength(); i++) {
+        if (col[i+4] > 0) {
+          GfxCMYK cmyk;
+          GfxColor input;
+          input.c[0] = byteToCol(col[i+4]);
+          GfxSeparationColorSpace *sepCS = (GfxSeparationColorSpace *)separationList->get(i);
+          sepCS->getCMYK(&input, &cmyk);
+          col[0] = colToByte(cmyk.c);
+          col[1] = colToByte(cmyk.m);
+          col[2] = colToByte(cmyk.y);
+          col[3] = colToByte(cmyk.k);
+          c += byteToDbl(col[0]);
+          m += byteToDbl(col[1]);
+          y += byteToDbl(col[2]);
+          k += byteToDbl(col[3]);
+        }
+      }
+      if (c > 1) c = 1;
+      if (m > 1) m = 1;
+      if (y > 1) y = 1;
+      if (k > 1) k = 1;
+    }
+#endif
     c1 = 1 - c;
     m1 = 1 - m;
     y1 = 1 - y;
@@ -397,10 +441,122 @@
   }
 }
 
+void SplashBitmap::getXBGRLine(int yl, SplashColorPtr line) {
+  SplashColor col;
+  double c, m, y, k, c1, m1, y1, k1, r, g, b;
+
+  for (int x = 0; x < width; x++) {
+    getPixel(x, yl, col);
+    c = byteToDbl(col[0]);
+    m = byteToDbl(col[1]);
+    y = byteToDbl(col[2]);
+    k = byteToDbl(col[3]);
+#if SPLASH_CMYK
+    if (separationList->getLength() > 0) {
+      for (int i = 0; i < separationList->getLength(); i++) {
+        if (col[i+4] > 0) {
+          GfxCMYK cmyk;
+          GfxColor input;
+          input.c[0] = byteToCol(col[i+4]);
+          GfxSeparationColorSpace *sepCS = (GfxSeparationColorSpace *)separationList->get(i);
+          sepCS->getCMYK(&input, &cmyk);
+          col[0] = colToByte(cmyk.c);
+          col[1] = colToByte(cmyk.m);
+          col[2] = colToByte(cmyk.y);
+          col[3] = colToByte(cmyk.k);
+          c += byteToDbl(col[0]);
+          m += byteToDbl(col[1]);
+          y += byteToDbl(col[2]);
+          k += byteToDbl(col[3]);
+        }
+      }
+      if (c > 1) c = 1;
+      if (m > 1) m = 1;
+      if (y > 1) y = 1;
+      if (k > 1) k = 1;
+    }
+#endif
+    c1 = 1 - c;
+    m1 = 1 - m;
+    y1 = 1 - y;
+    k1 = 1 - k;
+    cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
+    *line++ = dblToByte(clip01(b));
+    *line++ = dblToByte(clip01(g));
+    *line++ = dblToByte(clip01(r));
+    *line++ = 255;
+  }
+}
+
+GBool SplashBitmap::convertToXBGR() {
+  if (mode == splashModeXBGR8)
+    return gTrue;
+  
+  int newrowSize = width * 4;
+  SplashColorPtr newdata = (SplashColorPtr)gmallocn_checkoverflow(newrowSize, height);
+  if (newdata != NULL) {
+    for (int y = 0; y < height; y++) {
+      unsigned char *row = newdata + y * newrowSize;
+      getXBGRLine(y, row);
+    }
+    if (rowSize < 0) {
+      gfree(data + (height - 1) * rowSize);
+    } else {
+      gfree(data);
+    }
+    data = newdata;
+    rowSize = newrowSize;
+    mode = splashModeXBGR8;
+  }
+  return newdata != NULL;
+}
+
+#if SPLASH_CMYK
+void SplashBitmap::getCMYKLine(int yl, SplashColorPtr line) {
+  SplashColor col;
+
+  for (int x = 0; x < width; x++) {
+    getPixel(x, yl, col);
+    if (separationList->getLength() > 0) {
+      double c, m, y, k;
+      c = byteToDbl(col[0]);
+      m = byteToDbl(col[1]);
+      y = byteToDbl(col[2]);
+      k = byteToDbl(col[3]);
+      for (int i = 0; i < separationList->getLength(); i++) {
+        if (col[i+4] > 0) {
+          GfxCMYK cmyk;
+          GfxColor input;
+          input.c[0] = byteToCol(col[i+4]);
+          GfxSeparationColorSpace *sepCS = (GfxSeparationColorSpace *)separationList->get(i);
+          sepCS->getCMYK(&input, &cmyk);
+          col[0] = colToByte(cmyk.c);
+          col[1] = colToByte(cmyk.m);
+          col[2] = colToByte(cmyk.y);
+          col[3] = colToByte(cmyk.k);
+          c += byteToDbl(col[0]);
+          m += byteToDbl(col[1]);
+          y += byteToDbl(col[2]);
+          k += byteToDbl(col[3]);
+        }
+      }
+      col[0] = dblToByte(clip01(c));
+      col[1] = dblToByte(clip01(m));
+      col[2] = dblToByte(clip01(y));
+      col[3] = dblToByte(clip01(k));
+    }
+    *line++ = col[0];
+    *line++ = col[1];
+    *line++ = col[2];
+    *line++ = col[3];
+  }
+}
+#endif
+
 SplashError SplashBitmap::writeImgFile(ImgWriter *writer, FILE *f, int hDPI, int vDPI) {
   if (mode != splashModeRGB8 && mode != splashModeMono8 && mode != splashModeMono1 && mode != splashModeXBGR8
 #if SPLASH_CMYK
-      && mode != splashModeCMYK8
+      && mode != splashModeCMYK8 && mode != splashModeDeviceN8
 #endif
      ) {
     error(errInternal, -1, "unsupported SplashBitmap mode");
@@ -440,6 +596,29 @@
         delete[] row;
       }
     break;
+    case splashModeDeviceN8:
+      if (writer->supportCMYK()) {
+        unsigned char *row = new unsigned char[4 * width];
+        for (int y = 0; y < height; y++) {
+          getCMYKLine(y, row);
+          if (!writer->writeRow(&row)) {
+            delete[] row;
+            return splashErrGeneric;
+          }
+        }
+        delete[] row;
+      } else {
+        unsigned char *row = new unsigned char[3 * width];
+        for (int y = 0; y < height; y++) {
+          getRGBLine(y, row);
+          if (!writer->writeRow(&row)) {
+            delete[] row;
+            return splashErrGeneric;
+          }
+        }
+        delete[] row;
+      }
+    break;
 #endif
     case splashModeRGB8:
     {
diff --git a/splash/SplashBitmap.h b/splash/SplashBitmap.h
index 5ef5573..70509ab 100644
--- a/splash/SplashBitmap.h
+++ b/splash/SplashBitmap.h
@@ -13,7 +13,7 @@
 //
 // Copyright (C) 2007 Ilmari Heikkinen <ilmari.heikkinen@gmail.com>
 // Copyright (C) 2009 Shen Liang <shenzhuxi@gmail.com>
-// Copyright (C) 2009 Albert Astals Cid <aacid@kde.org>
+// Copyright (C) 2009, 2012 Albert Astals Cid <aacid@kde.org>
 // Copyright (C) 2009 Stefan Thomas <thomas@eload24.com>
 // Copyright (C) 2010 Adrian Johnson <ajohnson@redneon.com>
 // Copyright (C) 2010 Harry Roberts <harry.roberts@midnight-labs.org>
@@ -34,6 +34,7 @@
 #endif
 
 #include "SplashTypes.h"
+#include "poppler/GfxState.h"
 #include <stdio.h>
 
 class ImgWriter;
@@ -51,7 +52,7 @@
   // upside-down, i.e., with the last row first in memory.
   SplashBitmap(int widthA, int heightA, int rowPad,
 	       SplashColorMode modeA, GBool alphaA,
-	       GBool topDown = gTrue);
+	       GBool topDown = gTrue, GooList *separationList = NULL);
   static SplashBitmap *copy(SplashBitmap *src);
 
   ~SplashBitmap();
@@ -64,6 +65,7 @@
   SplashColorMode getMode() { return mode; }
   SplashColorPtr getDataPtr() { return data; }
   Guchar *getAlphaPtr() { return alpha; }
+  GooList *getSeparationList() { return separationList; }
 
   SplashError writePNMFile(char *fileName);
   SplashError writePNMFile(FILE *f);
@@ -73,8 +75,14 @@
   SplashError writeImgFile(SplashImageFileFormat format, FILE *f, int hDPI, int vDPI, const char *compressionString = "");
   SplashError writeImgFile(ImgWriter *writer, FILE *f, int hDPI, int vDPI);
 
+  GBool convertToXBGR();
+
   void getPixel(int x, int y, SplashColorPtr pixel);
   void getRGBLine(int y, SplashColorPtr line);
+  void getXBGRLine(int y, SplashColorPtr line);
+#if SPLASH_CMYK
+  void getCMYKLine(int y, SplashColorPtr line);
+#endif
   Guchar getAlpha(int x, int y);
 
   // Caller takes ownership of the bitmap data.  The SplashBitmap
@@ -92,6 +100,7 @@
   SplashColorPtr data;		// pointer to row zero of the color data
   Guchar *alpha;		// pointer to row zero of the alpha data
 				//   (always top-down)
+  GooList *separationList; // list of spot colorants and their mapping functions
 
   friend class Splash;
 };
diff --git a/splash/SplashScreen.cc b/splash/SplashScreen.cc
index d741246..68ccd7d 100644
--- a/splash/SplashScreen.cc
+++ b/splash/SplashScreen.cc
@@ -12,6 +12,7 @@
 // under GPL version 2 or later
 //
 // Copyright (C) 2009 Albert Astals Cid <aacid@kde.org>
+// Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
 //
 // To see a description of the changes please see the Changelog file that
 // came with your tarball or type make ChangeLog if you are building from git
@@ -28,6 +29,7 @@
 #include <string.h>
 #include <algorithm>
 #include "goo/gmem.h"
+#include "goo/grandom.h"
 #include "SplashMath.h"
 #include "SplashScreen.h"
 
@@ -253,9 +255,6 @@
   int *region, *dist;
   int x, y, xx, yy, x0, x1, y0, y1, i, j, d, iMin, dMin, n;
 
-  //~ this should probably happen somewhere else
-  srand(123);
-
   // generate the random space-filling curve
   pts = (SplashScreenPoint *)gmallocn(size * size, sizeof(SplashScreenPoint));
   i = 0;
@@ -267,8 +266,7 @@
     }
   }
   for (i = 0; i < size * size; ++i) {
-    j = i + (int)((double)(size * size - i) *
-		  (double)rand() / ((double)RAND_MAX + 1.0));
+    j = i + (int)((double)(size * size - i) * grandom_double());
     x = pts[i].x;
     y = pts[i].y;
     pts[i].x = pts[j].x;
diff --git a/splash/SplashState.cc b/splash/SplashState.cc
index e258f66..fd2789d 100644
--- a/splash/SplashState.cc
+++ b/splash/SplashState.cc
@@ -40,7 +40,7 @@
 int splashColorModeNComps[] = {
   1, 1, 3, 3, 4
 #if SPLASH_CMYK
-  ,4
+  , 4, 4 + SPOT_NCOMPS
 #endif
 };
 
@@ -80,10 +80,14 @@
     rgbTransferG[i] = (Guchar)i;
     rgbTransferB[i] = (Guchar)i;
     grayTransfer[i] = (Guchar)i;
+#if SPLASH_CMYK
     cmykTransferC[i] = (Guchar)i;
     cmykTransferM[i] = (Guchar)i;
     cmykTransferY[i] = (Guchar)i;
     cmykTransferK[i] = (Guchar)i;
+    for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
+      deviceNTransfer[cp][i] = (Guchar)i;
+#endif
   }
   overprintMask = 0xffffffff;
   overprintAdditive = gFalse;
@@ -126,10 +130,14 @@
     rgbTransferG[i] = (Guchar)i;
     rgbTransferB[i] = (Guchar)i;
     grayTransfer[i] = (Guchar)i;
+#if SPLASH_CMYK
     cmykTransferC[i] = (Guchar)i;
     cmykTransferM[i] = (Guchar)i;
     cmykTransferY[i] = (Guchar)i;
     cmykTransferK[i] = (Guchar)i;
+    for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
+      deviceNTransfer[cp][i] = (Guchar)i;
+#endif
   }
   overprintMask = 0xffffffff;
   overprintAdditive = gFalse;
@@ -170,10 +178,14 @@
   memcpy(rgbTransferG, state->rgbTransferG, 256);
   memcpy(rgbTransferB, state->rgbTransferB, 256);
   memcpy(grayTransfer, state->grayTransfer, 256);
+#if SPLASH_CMYK
   memcpy(cmykTransferC, state->cmykTransferC, 256);
   memcpy(cmykTransferM, state->cmykTransferM, 256);
   memcpy(cmykTransferY, state->cmykTransferY, 256);
   memcpy(cmykTransferK, state->cmykTransferK, 256);
+  for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
+    memcpy(deviceNTransfer[cp], state->deviceNTransfer[cp], 256);
+#endif
   overprintMask = state->overprintMask;
   overprintAdditive = state->overprintAdditive;
   next = NULL;
@@ -228,16 +240,24 @@
 
 void SplashState::setTransfer(Guchar *red, Guchar *green, Guchar *blue,
 			      Guchar *gray) {
+#if SPLASH_CMYK
   int i;
 
-  memcpy(rgbTransferR, red, 256);
-  memcpy(rgbTransferG, green, 256);
-  memcpy(rgbTransferB, blue, 256);
-  memcpy(grayTransfer, gray, 256);
   for (i = 0; i < 256; ++i) {
     cmykTransferC[i] = 255 - rgbTransferR[255 - i];
     cmykTransferM[i] = 255 - rgbTransferG[255 - i];
     cmykTransferY[i] = 255 - rgbTransferB[255 - i];
     cmykTransferK[i] = 255 - grayTransfer[255 - i];
   }
+  for (i = 0; i < 256; ++i) {
+    deviceNTransfer[0][i] = 255 - rgbTransferR[255 - i];
+    deviceNTransfer[1][i] = 255 - rgbTransferG[255 - i];
+    deviceNTransfer[2][i] = 255 - rgbTransferB[255 - i];
+    deviceNTransfer[3][i] = 255 - grayTransfer[255 - i];
+  }
+#endif
+  memcpy(rgbTransferR, red, 256);
+  memcpy(rgbTransferG, green, 256);
+  memcpy(rgbTransferB, blue, 256);
+  memcpy(grayTransfer, gray, 256);
 }
diff --git a/splash/SplashState.h b/splash/SplashState.h
index 01e7772..13d5478 100644
--- a/splash/SplashState.h
+++ b/splash/SplashState.h
@@ -121,10 +121,13 @@
          rgbTransferG[256],

          rgbTransferB[256];

   Guchar grayTransfer[256];

+#if SPLASH_CMYK

   Guchar cmykTransferC[256],

          cmykTransferM[256],

          cmykTransferY[256],

          cmykTransferK[256];

+  Guchar deviceNTransfer[SPOT_NCOMPS+4][256];

+#endif

   Guint overprintMask;

   GBool overprintAdditive;

 

diff --git a/splash/SplashTypes.h b/splash/SplashTypes.h
index 65525b1..531b945 100644
--- a/splash/SplashTypes.h
+++ b/splash/SplashTypes.h
@@ -13,7 +13,7 @@
 //
 // Copyright (C) 2006, 2010 Albert Astals Cid <aacid@kde.org>
 // Copyright (C) 2008 Tomas Are Haavet <tomasare@gmail.com>
-// Copyright (C) 2009, 2011 Thomas Freitag <Thomas.Freitag@alfa.de>
+// Copyright (C) 2009, 2011, 2012 Thomas Freitag <Thomas.Freitag@alfa.de>
 // Copyright (C) 2009 Stefan Thomas <thomas@eload24.com>
 // Copyright (C) 2010 William Bader <williambader@hotmail.com>
 //
@@ -46,6 +46,12 @@
 
 #define splashAASize 4
 
+#ifdef SPLASH_CMYK
+#ifndef SPOT_NCOMPS
+#define SPOT_NCOMPS 4
+#endif
+#endif
+
 //------------------------------------------------------------------------
 // colors
 //------------------------------------------------------------------------
@@ -62,8 +68,11 @@
 				//   XBGRXBGR...
 #if SPLASH_CMYK
   ,
-  splashModeCMYK8		// 1 byte per component, 4 bytes per pixel:
+  splashModeCMYK8,	// 1 byte per component, 4 bytes per pixel:
 				//   CMYKCMYK...
+  splashModeDeviceN8		// 1 byte per component, 
+                        // 4 bytes + n bytes spot colors per pixel:
+				                // CMYKSSSSCMYKSSSS...
 #endif
 };
 
@@ -72,7 +81,11 @@
 extern int splashColorModeNComps[];
 
 // max number of components in any SplashColor
+#if SPLASH_CMYK
+#define splashMaxColorComps SPOT_NCOMPS+4
+#else
 #define splashMaxColorComps 4
+#endif
 
 typedef Guchar SplashColor[splashMaxColorComps];
 typedef Guchar *SplashColorPtr;
@@ -93,6 +106,13 @@
 static inline Guchar splashCMYK8M(SplashColorPtr cmyk8) { return cmyk8[1]; }
 static inline Guchar splashCMYK8Y(SplashColorPtr cmyk8) { return cmyk8[2]; }
 static inline Guchar splashCMYK8K(SplashColorPtr cmyk8) { return cmyk8[3]; }
+
+// DEVICEN8
+static inline Guchar splashDeviceN8C(SplashColorPtr deviceN8) { return deviceN8[0]; }
+static inline Guchar splashDeviceN8M(SplashColorPtr deviceN8) { return deviceN8[1]; }
+static inline Guchar splashDeviceN8Y(SplashColorPtr deviceN8) { return deviceN8[2]; }
+static inline Guchar splashDeviceN8K(SplashColorPtr deviceN8) { return deviceN8[3]; }
+static inline Guchar splashDeviceN8S(SplashColorPtr deviceN8, int nSpot) { return deviceN8[4 + nSpot]; }
 #endif
 
 static inline void splashClearColor(SplashColorPtr dest) {
@@ -101,6 +121,8 @@
   dest[2] = 0;
 #if SPLASH_CMYK
   dest[3] = 0;
+  for (int i = SPOT_NCOMPS; i < SPOT_NCOMPS + 4; i++)
+    dest[i] = 0;
 #endif
 }
 
@@ -110,6 +132,8 @@
   dest[2] = src[2];
 #if SPLASH_CMYK
   dest[3] = src[3];
+  for (int i = SPOT_NCOMPS; i < SPOT_NCOMPS + 4; i++)
+    dest[i] = src[i];
 #endif
 }
 
@@ -119,6 +143,8 @@
   dest[2] ^= src[2];
 #if SPLASH_CMYK
   dest[3] ^= src[3];
+  for (int i = SPOT_NCOMPS; i < SPOT_NCOMPS + 4; i++)
+    dest[i] ^= src[i];
 #endif
 }
 
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index cff715c..a89a4cf 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -17,7 +17,7 @@
 
 if (GTK_FOUND)
 
-  add_definitions(${GTK2_CFLAGS})
+  add_definitions(${GTK3_CFLAGS})
 
   include_directories(
     ${CMAKE_SOURCE_DIR}/glib
@@ -28,7 +28,7 @@
     gtk-test.cc
   )
   poppler_add_test(gtk-test BUILD_GTK_TESTS ${gtk_splash_test_SRCS})
-  target_link_libraries(gtk-test poppler-glib ${GTK2_LIBRARIES})
+  target_link_libraries(gtk-test poppler-glib ${GTK3_LIBRARIES})
 
   if (HAVE_CAIRO)
 
@@ -36,7 +36,7 @@
       pdf-inspector.cc
     )
     poppler_add_test(pdf-inspector BUILD_GTK_TESTS ${pdf_inspector_SRCS})
-    target_link_libraries(pdf-inspector poppler-glib ${GTK2_LIBRARIES})
+    target_link_libraries(pdf-inspector poppler-glib ${GTK3_LIBRARIES})
 
   endif (HAVE_CAIRO)
 
@@ -44,6 +44,7 @@
 
 set (pdf_fullrewrite_SRCS
   pdf-fullrewrite.cc
+  ../utils/parseargs.cc
 )
 add_executable(pdf-fullrewrite ${pdf_fullrewrite_SRCS})
 target_link_libraries(pdf-fullrewrite poppler)
diff --git a/test/Makefile.am b/test/Makefile.am
index 0252911..b3289c7 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -39,8 +39,7 @@
 	-I$(top_srcdir)/glib			\
 	-I$(top_builddir)/glib			\
 	$(cairo_includes)			\
-	$(GTK_TEST_CFLAGS)			\
-	$(FONTCONFIG_CFLAGS)
+	$(GTK_TEST_CFLAGS)
 
 noinst_PROGRAMS = $(pdf_inspector) $(perf_test) $(pdf_fullrewrite) $(gtk_test)
 
@@ -53,8 +52,7 @@
 	$(top_builddir)/poppler/libpoppler.la           \
 	$(top_builddir)/glib/libpoppler-glib.la         \
 	$(CAIRO_LIBS)					\
-	$(GTK_TEST_LIBS)				\
-	$(FONTCONFIG_LIBS)
+	$(GTK_TEST_LIBS)
 
 
 pdf_inspector_SOURCES =			\
@@ -78,7 +76,8 @@
 	$(X_EXTRA_LIBS)
 
 pdf_fullrewrite_SOURCES = \
-	pdf-fullrewrite.cc
+	pdf-fullrewrite.cc		\
+	../utils/parseargs.cc
 
 pdf_fullrewrite_LDADD = \
 	$(top_builddir)/poppler/libpoppler.la
diff --git a/test/gtk-test.cc b/test/gtk-test.cc
index dd339fb..a5759d7 100644
--- a/test/gtk-test.cc
+++ b/test/gtk-test.cc
@@ -156,16 +156,14 @@
 }
 
 static gboolean
-drawing_area_expose (GtkWidget      *drawing_area,
-                     GdkEventExpose *event,
-                     View           *view)
+drawing_area_draw (GtkWidget *drawing_area,
+                   cairo_t   *cr,
+                   View      *view)
 {
-  cairo_t *cr;
   GdkRectangle document;
+  GdkRectangle clip;
   GdkRectangle draw;
 
-  gdk_window_clear (drawing_area->window);
-
   document.x = 0;
   document.y = 0;
   if (cairo_output) {
@@ -176,8 +174,10 @@
     document.height = view->out->getBitmapHeight();
   }
 
-  cr = gdk_cairo_create (drawing_area->window);
-  if (!gdk_rectangle_intersect (&document, &event->area, &draw))
+  if (!gdk_cairo_get_clip_rectangle (cr, &clip))
+    return FALSE;
+
+  if (!gdk_rectangle_intersect (&document, &clip, &draw))
     return FALSE;
 
   if (cairo_output) {
@@ -190,8 +190,6 @@
                        draw.width, draw.height);
   }
 
-  cairo_destroy (cr);
-
   return TRUE;
 }
 
@@ -281,6 +279,7 @@
   GtkWidget *sw;
   GtkWidget *vbox, *hbox;
   guint n_pages;
+  PopplerPage *page;
 
   view = g_slice_new0 (View);
 
@@ -291,7 +290,16 @@
                     G_CALLBACK (destroy_window_callback),
                     view);
 
-  vbox = gtk_vbox_new (FALSE, 5);
+  page = poppler_document_get_page (doc, 0);
+  if (page) {
+    double width, height;
+
+    poppler_page_get_size (page, &width, &height);
+    gtk_window_set_default_size (GTK_WINDOW (window), (gint)width, (gint)height);
+    g_object_unref (page);
+  }
+
+  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
 
   view->drawing_area = gtk_drawing_area_new ();
   sw = gtk_scrolled_window_new (NULL, NULL);
@@ -305,7 +313,7 @@
   gtk_box_pack_end (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
   gtk_widget_show (sw);
 
-  hbox = gtk_hbox_new (FALSE, 5);
+  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
 
   n_pages = poppler_document_get_n_pages (doc);
   view->spin_button = gtk_spin_button_new_with_range  (0, n_pages - 1, 1);
@@ -333,8 +341,8 @@
   }
 
   g_signal_connect (view->drawing_area,
-                    "expose_event",
-                    G_CALLBACK (drawing_area_expose),
+                    "draw",
+                    G_CALLBACK (drawing_area_draw),
                     view);
 
   return view;
@@ -366,27 +374,27 @@
   for (int i = 0; file_arguments[i]; i++) {
     View            *view;
     GFile           *file;
-    gchar           *uri;
     PopplerDocument *doc;
     GError          *error = NULL;
 
     file = g_file_new_for_commandline_arg (file_arguments[i]);
-    uri = g_file_get_uri (file);
-    g_object_unref (file);
-
-    doc = poppler_document_new_from_file (uri, NULL, &error);
+    doc = poppler_document_new_from_gfile (file, NULL, NULL, &error);
     if (!doc) {
+      gchar *uri;
+
+      uri = g_file_get_uri (file);
       g_printerr ("Error opening document %s: %s\n", uri, error->message);
       g_error_free (error);
       g_free (uri);
+      g_object_unref (file);
 
       continue;
     }
+    g_object_unref (file);
 
     view = view_new (doc);
     view_list = g_list_prepend (view_list, view);
     view_set_page (view, CLAMP (page, 0, poppler_document_get_n_pages (doc) - 1));
-    g_free (uri);
   }
 
   gtk_main ();
diff --git a/test/pdf-fullrewrite.cc b/test/pdf-fullrewrite.cc
index 9658e55..2b912f7 100644
--- a/test/pdf-fullrewrite.cc
+++ b/test/pdf-fullrewrite.cc
@@ -3,44 +3,368 @@
 // pdf-fullrewrite.cc
 //
 // Copyright 2007 Julien Rebetez
+// Copyright 2012 Fabio D'Urso
 //
 //========================================================================
-#include "config.h"
-#include <poppler-config.h>
+
 #include "GlobalParams.h"
 #include "Error.h"
+#include "Object.h"
 #include "PDFDoc.h"
+#include "XRef.h"
 #include "goo/GooString.h"
+#include "utils/parseargs.h"
+
+static GBool compareDocuments(PDFDoc *origDoc, PDFDoc *newDoc);
+static GBool compareObjects(Object *objA, Object *objB);
+
+static char ownerPassword[33] = "\001";
+static char userPassword[33] = "\001";
+static GBool forceIncremental = gFalse;
+static GBool checkOutput = gFalse;
+static GBool printHelp = gFalse;
+
+static const ArgDesc argDesc[] = {
+  {"-opw",    argString,   ownerPassword,    sizeof(ownerPassword),
+   "owner password (for encrypted files)"},
+  {"-upw",    argString,   userPassword,     sizeof(userPassword),
+   "user password (for encrypted files)"},
+  {"-i",      argFlag,     &forceIncremental,0,
+   "incremental update mode"},
+  {"-check",  argFlag,     &checkOutput,     0,
+   "verify the generated document"},
+  {"-h",      argFlag,     &printHelp,       0,
+   "print usage information"},
+  {"-help",   argFlag,     &printHelp,       0,
+   "print usage information"},
+  {"--help",  argFlag,     &printHelp,       0,
+   "print usage information"},
+  {"-?",      argFlag,     &printHelp,       0,
+   "print usage information"},
+  {NULL}
+};
 
 int main (int argc, char *argv[])
 {
-  PDFDoc *doc;
-  GooString *inputName, *outputName;
+  PDFDoc *doc = NULL;
+  PDFDoc *docOut = NULL;
+  GooString *inputName = NULL;
+  GooString *outputName = NULL;
+  GooString *ownerPW = NULL;
+  GooString *userPW = NULL;
+  int res = 0;
 
   // parse args
-  if (argc < 3) {
-    fprintf(stderr, "usage: %s INPUT-FILE OUTPUT-FILE\n", argv[0]);
-    return 1;
+  GBool ok = parseArgs(argDesc, &argc, argv);
+  if (!ok || (argc < 3) || printHelp) {
+    printUsage(argv[0], "INPUT-FILE OUTPUT-FILE", argDesc);
+    if (!printHelp) {
+      res = 1;
+    }
+    goto done;
   }
 
   inputName = new GooString(argv[1]);
   outputName = new GooString(argv[2]);
 
-  globalParams = new GlobalParams();
-
-  doc = new PDFDoc(inputName);
-
-  if (!doc->isOk()) {
-    delete doc;
-    fprintf(stderr, "Error loading document !\n");
-    return 1;
+  if (ownerPassword[0] != '\001') {
+    ownerPW = new GooString(ownerPassword);
+  }
+  if (userPassword[0] != '\001') {
+    userPW = new GooString(userPassword);
   }
 
+  // load input document
+  globalParams = new GlobalParams();
+  doc = new PDFDoc(inputName, ownerPW, userPW);
+  if (!doc->isOk()) {
+    fprintf(stderr, "Error loading input document\n");
+    res = 1;
+    goto done;
+  }
 
-  int res = doc->saveAs(outputName, writeForceRewrite);
+  // save it back (in rewrite or incremental update mode)
+  if (doc->saveAs(outputName, forceIncremental ? writeForceIncremental : writeForceRewrite) != 0) {
+    fprintf(stderr, "Error saving document\n");
+    res = 1;
+    goto done;
+  }
 
+  if (checkOutput) {
+    // open the generated document to verify it
+    docOut = new PDFDoc(outputName, ownerPW, userPW);
+    if (!docOut->isOk()) {
+      fprintf(stderr, "Error loading generated document\n");
+      res = 1;
+    } else if (!compareDocuments(doc, docOut)) {
+      fprintf(stderr, "Verification failed\n");
+      res = 1;
+    }
+  } else {
+    delete outputName;
+  }
+
+done:
+  delete docOut;
   delete doc;
   delete globalParams;
-  delete outputName;
+  delete userPW;
+  delete ownerPW;
   return res;
 }
+
+static GBool compareDictionaries(Dict *dictA, Dict *dictB)
+{
+  const int length = dictA->getLength();
+  if (dictB->getLength() != length)
+    return gFalse;
+
+  /* Check that every key in dictA is contained in dictB.
+   * Since keys are unique and we've already checked that dictA and dictB
+   * contain the same number of entries, we don't need to check that every key
+   * in dictB is also contained in dictA */
+  for (int i = 0; i < length; ++i) {
+    Object valA, valB;
+    const char *key = dictA->getKey(i);
+    dictA->getValNF(i, &valA);
+    dictB->lookupNF(key, &valB);
+    if (!compareObjects(&valA, &valB))
+      return gFalse;
+    valA.free();
+    valB.free();
+  }
+
+  return gTrue;
+}
+
+static GBool compareObjects(Object *objA, Object *objB)
+{
+  switch (objA->getType()) {
+    case objBool:
+    {
+      if (objB->getType() != objBool) {
+        return gFalse;
+      } else {
+        return (objA->getBool() == objB->getBool());
+      }
+    }
+    case objInt:
+    case objReal:
+    {
+      if (!objB->isNum()) {
+        return gFalse;
+      } else {
+        // Fuzzy comparison
+        const double diff = objA->getNum() - objB->getNum();
+        return (-0.01 < diff) && (diff < 0.01);
+      }
+    }
+    case objUint:
+    {
+      if (objB->getType() != objUint) {
+        return gFalse;
+      } else {
+        return (objA->getUint() == objB->getUint());
+      }
+    }
+    case objString:
+    {
+      if (objB->getType() != objString) {
+        return gFalse;
+      } else {
+        GooString *strA = objA->getString();
+        GooString *strB = objB->getString();
+        return (strA->cmp(strB) == 0);
+      }
+    }
+    case objName:
+    {
+      if (objB->getType() != objName) {
+        return gFalse;
+      } else {
+        GooString nameA(objA->getName());
+        GooString nameB(objB->getName());
+        return (nameA.cmp(&nameB) == 0);
+      }
+    }
+    case objNull:
+    {
+      if (objB->getType() != objNull) {
+        return gFalse;
+      } else {
+        return gTrue;
+      }
+    }
+    case objArray:
+    {
+      if (objB->getType() != objArray) {
+        return gFalse;
+      } else {
+        Array *arrayA = objA->getArray();
+        Array *arrayB = objB->getArray();
+        const int length = arrayA->getLength();
+        if (arrayB->getLength() != length) {
+          return gFalse;
+        } else {
+          for (int i = 0; i < length; ++i) {
+            Object elemA, elemB;
+            arrayA->getNF(i, &elemA);
+            arrayB->getNF(i, &elemB);
+            if (!compareObjects(&elemA, &elemB)) {
+              return gFalse;
+            }
+            elemA.free();
+            elemB.free();
+          }
+          return gTrue;
+        }
+      }
+    }
+    case objDict:
+    {
+      if (objB->getType() != objDict) {
+        return gFalse;
+      } else {
+        Dict *dictA = objA->getDict();
+        Dict *dictB = objB->getDict();
+        return compareDictionaries(dictA, dictB);
+      }
+    }
+    case objStream:
+    {
+      if (objB->getType() != objStream) {
+        return gFalse;
+      } else {
+        Stream *streamA = objA->getStream();
+        Stream *streamB = objB->getStream();
+        if (!compareDictionaries(streamA->getDict(), streamB->getDict())) {
+          return gFalse;
+        } else {
+          int c;
+          streamA->reset();
+          streamB->reset();
+          do
+          {
+            c = streamA->getChar();
+            if (c != streamB->getChar()) {
+              return gFalse;
+            }
+          } while (c != EOF);
+          return gTrue;
+        }
+      }
+      return gTrue;
+    }
+    case objRef:
+    {
+      if (objB->getType() != objRef) {
+        return gFalse;
+      } else {
+        Ref refA = objA->getRef();
+        Ref refB = objB->getRef();
+        return (refA.num == refB.num) && (refA.gen == refB.gen);
+      }
+    }
+    default:
+    {
+      fprintf(stderr, "compareObjects failed: unexpected object type %u\n", objA->getType());
+      return gFalse;
+    }
+  }
+}
+
+static GBool compareDocuments(PDFDoc *origDoc, PDFDoc *newDoc)
+{
+  GBool result = gTrue;
+  XRef *origXRef = origDoc->getXRef();
+  XRef *newXRef = newDoc->getXRef();
+
+  // Make sure that special flags are set in both documents
+  origXRef->scanSpecialFlags();
+  newXRef->scanSpecialFlags();
+
+  // Compare XRef tables' size
+  const int origNumObjects = origXRef->getNumObjects();
+  const int newNumObjects = newXRef->getNumObjects();
+  if (forceIncremental && origXRef->isXRefStream()) {
+    // In case of incremental update, expect a new entry to be appended to store the new XRef stream
+    if (origNumObjects+1 != newNumObjects) {
+      fprintf(stderr, "XRef table: Unexpected number of entries (%d+1 != %d)\n", origNumObjects, newNumObjects);
+      result = gFalse;
+    }
+  } else {
+    // In all other cases the number of entries must be the same
+    if (origNumObjects != newNumObjects) {
+      fprintf(stderr, "XRef table: Different number of entries (%d != %d)\n", origNumObjects, newNumObjects);
+      result = gFalse;
+    }
+  }
+
+  // Compare each XRef entry
+  const int numObjects = (origNumObjects < newNumObjects) ? origNumObjects : newNumObjects;
+  for (int i = 0; i < numObjects; ++i) {
+    XRefEntryType origType = origXRef->getEntry(i)->type;
+    XRefEntryType newType = newXRef->getEntry(i)->type;
+    const int origGenNum = (origType != xrefEntryCompressed) ? origXRef->getEntry(i)->gen : 0;
+    const int newGenNum = (newType != xrefEntryCompressed) ? newXRef->getEntry(i)->gen : 0;
+
+    // Check that DontRewrite entries are freed in full rewrite mode
+    if (!forceIncremental && origXRef->getEntry(i)->getFlag(XRefEntry::DontRewrite)) {
+      if (newType != xrefEntryFree || origGenNum+1 != newGenNum) {
+        fprintf(stderr, "XRef entry %u: DontRewrite entry was not freed correctly\n", i);
+        result = gFalse;
+      }
+      continue; // There's nothing left to check for this entry
+    }
+
+    // Compare generation numbers
+    // Object num 0 should always have gen 65535 according to specs, but some
+    // documents have it set to 0. We always write 65535 in output
+    if (i != 0) {
+      if (origGenNum != newGenNum) {
+        fprintf(stderr, "XRef entry %u: generation numbers differ (%d != %d)\n", i, origGenNum, newGenNum);
+        result = gFalse;
+        continue;
+      }
+    } else {
+      if (newGenNum != 65535) {
+        fprintf(stderr, "XRef entry %u: generation number was expected to be 65535 (%d != 65535)\n", i, newGenNum);
+        result = gFalse;
+        continue;
+      }
+    }
+
+    // Compare object flags. A failure shows that there's some error in XRef::scanSpecialFlags()
+    if (origXRef->getEntry(i)->flags != newXRef->getEntry(i)->flags) {
+      fprintf(stderr, "XRef entry %u: flags detected by scanSpecialFlags differ (%d != %d)\n", i, origXRef->getEntry(i)->flags, newXRef->getEntry(i)->flags);
+      result = gFalse;
+    }
+
+    // Check that either both are free or both are in use
+    if ((origType == xrefEntryFree) != (newType == xrefEntryFree)) {
+      const char *origStatus = (origType == xrefEntryFree) ? "free" : "in use";
+      const char *newStatus = (newType == xrefEntryFree) ? "free" : "in use";
+      fprintf(stderr, "XRef entry %u: usage status differs (%s != %s)\n", i, origStatus, newStatus);
+      result = gFalse;
+      continue;
+    }
+
+    // Skip free entries
+    if (origType == xrefEntryFree) {
+      continue;
+    }
+
+    // Compare contents
+    Object origObj, newObj;
+    origXRef->fetch(i, origGenNum, &origObj);
+    newXRef->fetch(i, newGenNum, &newObj);
+    if (!compareObjects(&origObj, &newObj)) {
+      fprintf(stderr, "XRef entry %u: contents differ\n", i);
+      result = gFalse;
+    }
+    origObj.free();
+    newObj.free();
+  }
+
+  return result;
+}
diff --git a/test/perf-test.cc b/test/perf-test.cc
index 6bdda97..21fbdee 100644
--- a/test/perf-test.cc
+++ b/test/perf-test.cc
@@ -1242,7 +1242,6 @@
     if (!globalParams)

         return 1;

     globalParams->setErrQuiet(gFalse);

-    globalParams->setBaseDir("");

 

     FILE * outFile = NULL;

     if (gOutFileName) {

diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt
index 4a29841..06378bd 100644
--- a/utils/CMakeLists.txt
+++ b/utils/CMakeLists.txt
@@ -5,9 +5,6 @@
 set(common_libs
   poppler
 )
-if (FONTCONFIG_FOUND)
-  set(common_libs ${common_libs} ${FONTCONFIG_LIBRARIES})
-endif (FONTCONFIG_FOUND)
 
 if (ENABLE_SPLASH)
   # pdftoppm
diff --git a/utils/HtmlFonts.cc b/utils/HtmlFonts.cc
index be02c5f..e0ea8a7 100644
--- a/utils/HtmlFonts.cc
+++ b/utils/HtmlFonts.cc
@@ -24,6 +24,7 @@
 // Copyright (C) 2011 Joshua Richardson <jric@chegg.com>
 // Copyright (C) 2011 Stephen Reichling <sreichling@chegg.com>
 // Copyright (C) 2012 Igor Slepchin <igor.slepchin@gmail.com>
+// Copyright (C) 2012 Luis Parravicini <lparravi@gmail.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
@@ -63,6 +64,7 @@
 
 #define xoutRound(x) ((int)(x + 0.5))
 extern GBool xml;
+extern GBool fontFullName;
 
 GooString* HtmlFont::DefaultFont=new GooString("Times"); // Arial,Helvetica,sans-serif
 
@@ -318,7 +320,7 @@
    HtmlFont font=*g;
    GooString *Size=GooString::fromInt(font.getSize());
    GooString *colorStr=font.getColor().toString();
-   GooString *fontName=font.getFontName();
+   GooString *fontName=(fontFullName ? font.getFullName() : font.getFontName());
    GooString *lSize;
    
    if(!xml){
diff --git a/utils/HtmlOutputDev.cc b/utils/HtmlOutputDev.cc
index 1d1b628..e4bd0b1 100644
--- a/utils/HtmlOutputDev.cc
+++ b/utils/HtmlOutputDev.cc
@@ -25,13 +25,14 @@
 // Copyright (C) 2009 Warren Toomey <wkt@tuhs.org>
 // Copyright (C) 2009, 2011 Carlos Garcia Campos <carlosgc@gnome.org>
 // Copyright (C) 2009 Reece Dunn <msclrhd@gmail.com>
-// Copyright (C) 2010 Adrian Johnson <ajohnson@redneon.com>
+// Copyright (C) 2010, 2012 Adrian Johnson <ajohnson@redneon.com>
 // Copyright (C) 2010 Hib Eris <hib@hiberis.nl>
 // Copyright (C) 2010 OSSD CDAC Mumbai by Leena Chourey (leenac@cdacmumbai.in) and Onkar Potdar (onkar@cdacmumbai.in)
 // Copyright (C) 2011 Joshua Richardson <jric@chegg.com>
 // Copyright (C) 2011 Stephen Reichling <sreichling@chegg.com>
 // Copyright (C) 2011, 2012 Igor Slepchin <igor.slepchin@gmail.com>
 // Copyright (C) 2012 Ihar Filipau <thephilips@gmail.com>
+// Copyright (C) 2012 Gerald Schmidt <solahcin@gmail.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
@@ -399,19 +400,7 @@
     h1 /= uLen;
   }
   for (i = 0; i < uLen; ++i) {
-    Unicode u1 = u[i];
-    if (u1 >= 0xd800 && u1 <= 0xdbff && i < uLen) {
-      // surrogate pair
-      const Unicode u2 = u[i + 1];
-      if (u2 >= 0xdc00 && u2 <= 0xdfff) {
-	u1 = 0x10000 + ((u1 - 0xd800) << 10) + (u2 - 0xdc00);
-	
-	curStr->addChar(state, x1 + i*w1, y1 + i*h1, w1, h1, u1);
-      }
-      ++i;
-    } else {
-      curStr->addChar(state, x1 + i*w1, y1 + i*h1, w1, h1, u1);
-    }
+    curStr->addChar(state, x1 + i*w1, y1 + i*h1, w1, h1, u[i]);
   }
 }
 
@@ -782,7 +771,7 @@
   // http://stackoverflow.com/questions/1309055/cross-browser-way-to-flip-html-image-via-javascript-css
   // tested in Chrome, Fx (Linux) and IE9 (W7)
   static const char css[] = 
-    "<STYLE type=\"text/css\">" "\n"
+    "<style type=\"text/css\">" "\n"
     "<!--" "\n"
     ".xflip {" "\n"
     "    -moz-transform: scaleX(-1);" "\n"
@@ -806,7 +795,7 @@
     "    filter: fliph + flipv;" "\n"
     "}" "\n"
     "-->" "\n"
-    "</STYLE>" "\n";
+    "</style>" "\n";
 
   fwrite( css, sizeof(css)-1, 1, f );
 }
@@ -833,17 +822,17 @@
       } 
 
       if (!singleHtml)
-        fprintf(pageFile,"%s\n<HTML xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n<HEAD>\n<TITLE>Page %d</TITLE>\n\n", DOCTYPE, page);
+        fprintf(pageFile,"%s\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n<head>\n<title>Page %d</title>\n\n", DOCTYPE, page);
       else
-        fprintf(pageFile,"%s\n<HTML xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n<HEAD>\n<TITLE>%s</TITLE>\n\n", DOCTYPE, tmp->getCString());
+        fprintf(pageFile,"%s\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n<head>\n<title>%s</title>\n\n", DOCTYPE, tmp->getCString());
 
       delete tmp;
 
       GooString *htmlEncoding = HtmlOutputDev::mapEncodingToHtml(globalParams->getTextEncodingName());
       if (!singleHtml)
-        fprintf(pageFile, "<META http-equiv=\"Content-Type\" content=\"text/html; charset=%s\"/>\n", htmlEncoding->getCString());
+        fprintf(pageFile, "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\"/>\n", htmlEncoding->getCString());
       else
-        fprintf(pageFile, "<META http-equiv=\"Content-Type\" content=\"text/html; charset=%s\"/>\n <br/>\n", htmlEncoding->getCString());
+        fprintf(pageFile, "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\"/>\n <br/>\n", htmlEncoding->getCString());
       delete htmlEncoding;
   }
   else 
@@ -866,7 +855,7 @@
 
   tmp=basename(DocName);
    
-  fputs("<STYLE type=\"text/css\">\n<!--\n",pageFile);
+  fputs("<style type=\"text/css\">\n<!--\n",pageFile);
   fputs("\tp {margin: 0; padding: 0;}",pageFile);
   for(int i=fontsPageMarker;i!=fonts->size();i++) {
     GooString *fontCSStyle;
@@ -878,20 +867,20 @@
     delete fontCSStyle;
   }
  
-  fputs("-->\n</STYLE>\n",pageFile);
+  fputs("-->\n</style>\n",pageFile);
   
   if( !noframes )
   {  
-      fputs("</HEAD>\n<BODY bgcolor=\"#A0A0A0\" vlink=\"blue\" link=\"blue\">\n",pageFile); 
+      fputs("</head>\n<body bgcolor=\"#A0A0A0\" vlink=\"blue\" link=\"blue\">\n",pageFile); 
   }
   
-  fprintf(pageFile,"<DIV id=\"page%d-div\" style=\"position:relative;width:%dpx;height:%dpx;\">\n",
+  fprintf(pageFile,"<div id=\"page%d-div\" style=\"position:relative;width:%dpx;height:%dpx;\">\n",
       page, pageWidth, pageHeight);
 
   if( !ignore ) 
   {
     fprintf(pageFile,
-	    "<IMG width=\"%d\" height=\"%d\" src=\"%s%03d.%s\" alt=\"background image\"/>\n",
+	    "<img width=\"%d\" height=\"%d\" src=\"%s%03d.%s\" alt=\"background image\"/>\n",
 	    pageWidth, pageHeight, tmp->getCString(), 
 		(page-firstPage+1), imgExt->getCString());
   }
@@ -901,7 +890,7 @@
   for(HtmlString *tmp1=yxStrings;tmp1;tmp1=tmp1->yxNext){
     if (tmp1->htext){
       fprintf(pageFile,
-	      "<P style=\"position:absolute;top:%dpx;left:%dpx;white-space:nowrap\" class=\"ft",
+	      "<p style=\"position:absolute;top:%dpx;left:%dpx;white-space:nowrap\" class=\"ft",
 	      xoutRound(tmp1->yMin),
 	      xoutRound(tmp1->xMin));
       if (!singleHtml) {
@@ -911,15 +900,15 @@
       }
       fprintf(pageFile,"%d\">", tmp1->fontpos);
       fputs(tmp1->htext->getCString(), pageFile);
-      fputs("</P>\n", pageFile);
+      fputs("</p>\n", pageFile);
     }
   }
 
-  fputs("</DIV>\n", pageFile);
+  fputs("</div>\n", pageFile);
   
   if( !noframes )
   {
-      fputs("</BODY>\n</HTML>\n",pageFile);
+      fputs("</body>\n</html>\n",pageFile);
       fclose(pageFile);
   }
 }
@@ -934,7 +923,7 @@
   }
   else
   {
-    fprintf(f,"<A name=%d></a>",pageNum);
+    fprintf(f,"<a name=%d></a>",pageNum);
     // Loop over the list of image names on this page
     int listlen=imgList->getLength();
     for (int i = 0; i < listlen; i++) {
@@ -946,7 +935,7 @@
       if (img->xMin > img->xMax) style_index += 1; // xFlip
       if (img->yMin > img->yMax) style_index += 2; // yFlip
 
-      fprintf(f,"<IMG%s src=\"%s\"/><br/>\n",styles[style_index],img->fName->getCString());
+      fprintf(f,"<img%s src=\"%s\"/><br/>\n",styles[style_index],img->fName->getCString());
       delete img;
     }
 
@@ -959,7 +948,7 @@
 		fputs("<br/>\n",f);
       }
     }
-	fputs("<hr>\n",f);  
+	fputs("<hr/>\n",f);  
   }
 }
 
@@ -1024,7 +1013,7 @@
     
 GooString* HtmlMetaVar::toString()	
 {
-    GooString *result = new GooString("<META name=\"");
+    GooString *result = new GooString("<meta name=\"");
     result->append(name);
     result->append("\" content=\"");
     result->append(content);
@@ -1070,22 +1059,22 @@
     
   fName=basename(Docname);
   fputs(DOCTYPE, fContentsFrame);
-  fputs("\n<HTML>",fContentsFrame);
-  fputs("\n<HEAD>",fContentsFrame);
-  fprintf(fContentsFrame,"\n<TITLE>%s</TITLE>",docTitle->getCString());
+  fputs("\n<html>",fContentsFrame);
+  fputs("\n<head>",fContentsFrame);
+  fprintf(fContentsFrame,"\n<title>%s</title>",docTitle->getCString());
   htmlEncoding = mapEncodingToHtml(globalParams->getTextEncodingName());
-  fprintf(fContentsFrame, "\n<META http-equiv=\"Content-Type\" content=\"text/html; charset=%s\"/>\n", htmlEncoding->getCString());
+  fprintf(fContentsFrame, "\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\"/>\n", htmlEncoding->getCString());
   dumpMetaVars(fContentsFrame);
-  fprintf(fContentsFrame, "</HEAD>\n");
-  fputs("<FRAMESET cols=\"100,*\">\n",fContentsFrame);
-  fprintf(fContentsFrame,"<FRAME name=\"links\" src=\"%s_ind.html\">\n",fName->getCString());
-  fputs("<FRAME name=\"contents\" src=",fContentsFrame); 
+  fprintf(fContentsFrame, "</head>\n");
+  fputs("<frameset cols=\"100,*\">\n",fContentsFrame);
+  fprintf(fContentsFrame,"<frame name=\"links\" src=\"%s_ind.html\"/>\n",fName->getCString());
+  fputs("<frame name=\"contents\" src=",fContentsFrame); 
   if (complexMode) 
       fprintf(fContentsFrame,"\"%s-%d.html\"",fName->getCString(), firstPage);
   else
       fprintf(fContentsFrame,"\"%ss.html\"",fName->getCString());
   
-  fputs(">\n</FRAMESET>\n</HTML>\n",fContentsFrame);
+  fputs("/>\n</frameset>\n</html>\n",fContentsFrame);
  
   delete fName;
   delete htmlEncoding;
@@ -1143,12 +1132,12 @@
          }
          delete left;
          fputs(DOCTYPE, fContentsFrame);
-         fputs("<HTML xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n<HEAD>\n<TITLE></TITLE>\n</HEAD>\n<BODY>\n", fContentsFrame);
+         fputs("<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n<head>\n<title></title>\n</head>\n<body>\n", fContentsFrame);
 
          if (doOutline)
          {
              GooString *str = basename(Docname);
-             fprintf(fContentsFrame, "<A href=\"%s%s\" target=\"contents\">Outline</a><br/>", str->getCString(), complexMode ? "-outline.html" : "s.html#outline");
+             fprintf(fContentsFrame, "<a href=\"%s%s\" target=\"contents\">Outline</a><br/>", str->getCString(), complexMode ? "-outline.html" : "s.html#outline");
              delete str;
          }
      }
@@ -1165,9 +1154,9 @@
        }
        delete right;
        fputs(DOCTYPE, page);
-       fputs("<HTML>\n<HEAD>\n<TITLE></TITLE>\n",page);
+       fputs("<html>\n<head>\n<title></title>\n",page);
        printCSS(page);
-       fputs("</HEAD>\n<BODY>\n",page);
+       fputs("</head>\n<body>\n",page);
      }
   }
 
@@ -1194,14 +1183,14 @@
     } 
     else 
     {
-      fprintf(page,"%s\n<HTML xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n<HEAD>\n<TITLE>%s</TITLE>\n", DOCTYPE, docTitle->getCString());
+      fprintf(page,"%s\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n<head>\n<title>%s</title>\n", DOCTYPE, docTitle->getCString());
       
-      fprintf(page, "<META http-equiv=\"Content-Type\" content=\"text/html; charset=%s\"/>\n", htmlEncoding->getCString());
+      fprintf(page, "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\"/>\n", htmlEncoding->getCString());
       
       dumpMetaVars(page);
       printCSS(page);
-      fprintf(page,"</HEAD>\n");
-      fprintf(page,"<BODY bgcolor=\"#A0A0A0\" vlink=\"blue\" link=\"blue\">\n");
+      fprintf(page,"</head>\n");
+      fprintf(page,"<body bgcolor=\"#A0A0A0\" vlink=\"blue\" link=\"blue\">\n");
     }
     delete htmlEncoding;
   }
@@ -1217,7 +1206,7 @@
     deleteGooList(glMetaVars, HtmlMetaVar);
 
     if (fContentsFrame){
-      fputs("</BODY>\n</HTML>\n",fContentsFrame);  
+      fputs("</body>\n</html>\n",fContentsFrame);  
       fclose(fContentsFrame);
     }
     if (page != NULL) {
@@ -1227,7 +1216,7 @@
       } else
       if ( !complexMode || xml || noframes )
       { 
-        fputs("</BODY>\n</HTML>\n",page);  
+        fputs("</body>\n</html>\n",page);  
         fclose(page);
       }
     }
@@ -1263,9 +1252,9 @@
     if (fContentsFrame)
 	{
       if (complexMode)
-		fprintf(fContentsFrame,"<A href=\"%s-%d.html\"",str->getCString(),pageNum);
+		fprintf(fContentsFrame,"<a href=\"%s-%d.html\"",str->getCString(),pageNum);
       else 
-		fprintf(fContentsFrame,"<A href=\"%ss.html#%d\"",str->getCString(),pageNum);
+		fprintf(fContentsFrame,"<a href=\"%ss.html#%d\"",str->getCString(),pageNum);
       fprintf(fContentsFrame," target=\"contents\" >Page %d</a><br/>\n",pageNum);
     }
   }
@@ -1295,7 +1284,7 @@
   maxPageWidth = pages->pageWidth;
   maxPageHeight = pages->pageHeight;
   
-  //if(!noframes&&!xml) fputs("<br>\n", fContentsFrame);
+  //if(!noframes&&!xml) fputs("<br/>\n", fContentsFrame);
   if(!stout && !globalParams->getErrQuiet()) printf("Page-%d\n",(pageNum));
 }
 
@@ -1698,7 +1687,7 @@
 		if (noframes)
 		{
 			output = page; 
-			fputs("<hr>\n", output);
+			fputs("<hr/>\n", output);
 		}
 		else
 		{
@@ -1713,13 +1702,13 @@
 			GooString *htmlEncoding =
 				HtmlOutputDev::mapEncodingToHtml(globalParams->getTextEncodingName());
 
-			fprintf(output, "<HTML xmlns=\"http://www.w3.org/1999/xhtml\" " \
+			fprintf(output, "<html xmlns=\"http://www.w3.org/1999/xhtml\" " \
                                 "lang=\"\" xml:lang=\"\">\n"            \
-                                "<HEAD>\n"                              \
-                                "<TITLE>Document Outline</TITLE>\n"     \
-                                "<META http-equiv=\"Content-Type\" content=\"text/html; " \
+                                "<head>\n"                              \
+                                "<title>Document Outline</title>\n"     \
+                                "<meta http-equiv=\"Content-Type\" content=\"text/html; " \
                                 "charset=%s\"/>\n"                      \
-                                "</HEAD>\n<BODY>\n", htmlEncoding->getCString());
+                                "</head>\n<body>\n", htmlEncoding->getCString());
 			delete htmlEncoding;
 		}
 	}
@@ -1728,11 +1717,11 @@
 	{
 		GBool done = newHtmlOutlineLevel(output, outlines, catalog);
 		if (done && !complexMode)
-			fputs("<hr>\n", output);
+			fputs("<hr/>\n", output);
 	
 		if (bClose)
 		{
-			fputs("</BODY>\n</HTML>\n", output);
+			fputs("</body>\n</html>\n", output);
 			fclose(output);
 		}
 	}
@@ -1752,7 +1741,7 @@
 
 	if (level == 1)
 	{
-		fputs("<A name=\"outline\"></a>", output);
+		fputs("<a name=\"outline\"></a>", output);
 		fputs("<h1>Document Outline</h1>\n", output);
 	}
 	fputs("<ul>\n",output);
@@ -1791,10 +1780,10 @@
 
 		fputs("<li>",output);
 		if (linkName)
-			fprintf(output,"<A href=\"%s\">", linkName->getCString());
+			fprintf(output,"<a href=\"%s\">", linkName->getCString());
 		fputs(titleStr->getCString(),output);
 		if (linkName) {
-			fputs("</A>",output);
+			fputs("</a>",output);
 			delete linkName;
 		}
 		delete titleStr;
diff --git a/utils/Makefile.am b/utils/Makefile.am
index ad845c1..d56cc9c 100644
--- a/utils/Makefile.am
+++ b/utils/Makefile.am
@@ -16,13 +16,11 @@
 	-I$(top_srcdir)/utils			\
 	-I$(top_srcdir)/poppler			\
 	$(UTILS_CFLAGS)				\
-	$(FONTCONFIG_CFLAGS)			\
 	$(PDFTOCAIRO_CFLAGS)
 
 LDADD =						\
 	$(top_builddir)/poppler/libpoppler.la	\
-	$(UTILS_LIBS)				\
-	$(FONTCONFIG_LIBS)
+	$(UTILS_LIBS)
 
 if BUILD_CAIRO_OUTPUT
 
diff --git a/utils/pdfinfo.cc b/utils/pdfinfo.cc
index cdc5375..bd8b56d 100644
--- a/utils/pdfinfo.cc
+++ b/utils/pdfinfo.cc
@@ -18,6 +18,7 @@
 // Copyright (C) 2010 Hib Eris <hib@hiberis.nl>
 // Copyright (C) 2011 Vittal Aithal <vittal.aithal@cognidox.com>
 // Copyright (C) 2012 Adrian Johnson <ajohnson@redneon.com>
+// Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
 //
 // To see a description of the changes please see the Changelog file that
 // came with your tarball or type make ChangeLog if you are building from git
@@ -48,7 +49,7 @@
 #include "PDFDocFactory.h"
 #include "CharTypes.h"
 #include "UnicodeMap.h"
-#include "PDFDocEncoding.h"
+#include "UTF.h"
 #include "Error.h"
 #include "DateInfo.h"
 
@@ -228,16 +229,17 @@
 	 doc->getStructTreeRoot()->isDict() ? "yes" : "no");
 
   // print form info
-  if ((acroForm = doc->getCatalog()->getAcroForm())->isDict()) {
-    acroForm->dictLookup("XFA", &xfa);
-    if (xfa.isStream() || xfa.isArray()) {
-      printf("Form:           XFA\n");
-    } else {
+  switch (doc->getCatalog()->getFormType())
+  {
+    case Catalog::NoForm:
+      printf("Form:           none\n");
+      break;
+    case Catalog::AcroForm:
       printf("Form:           AcroForm\n");
-    }
-    xfa.free();
-  } else {
-    printf("Form:           none\n");
+      break;
+    case Catalog::XfaForm:
+      printf("Form:           XFA\n");
+      break;
   }
 
   // print page count
@@ -246,11 +248,31 @@
   // print encryption info
   printf("Encrypted:      ");
   if (doc->isEncrypted()) {
-    printf("yes (print:%s copy:%s change:%s addNotes:%s)\n",
+    Guchar *fileKey;
+    CryptAlgorithm encAlgorithm;
+    int keyLength;
+    doc->getXRef()->getEncryptionParameters(&fileKey, &encAlgorithm, &keyLength);
+
+    const char *encAlgorithmName = "unknown";
+    switch (encAlgorithm)
+    {
+      case cryptRC4:
+	encAlgorithmName = "RC4";
+	break;
+      case cryptAES:
+	encAlgorithmName = "AES";
+	break;
+      case cryptAES256:
+	encAlgorithmName = "AES-256";
+	break;
+    }
+
+    printf("yes (print:%s copy:%s change:%s addNotes:%s algorithm:%s)\n",
 	   doc->okToPrint(gTrue) ? "yes" : "no",
 	   doc->okToCopy(gTrue) ? "yes" : "no",
 	   doc->okToChange(gTrue) ? "yes" : "no",
-	   doc->okToAddNotes(gTrue) ? "yes" : "no");
+	   doc->okToAddNotes(gTrue) ? "yes" : "no",
+	   encAlgorithmName);
   } else {
     printf("no\n");
   }
@@ -379,41 +401,16 @@
 			    UnicodeMap *uMap) {
   Object obj;
   GooString *s1;
-  GBool isUnicode;
-  Unicode u, u2;
+  Unicode *u;
   char buf[8];
-  int i, n;
+  int i, n, len;
 
   if (infoDict->lookup(key, &obj)->isString()) {
     fputs(text, stdout);
     s1 = obj.getString();
-    if ((s1->getChar(0) & 0xff) == 0xfe &&
-	(s1->getChar(1) & 0xff) == 0xff) {
-      isUnicode = gTrue;
-      i = 2;
-    } else {
-      isUnicode = gFalse;
-      i = 0;
-    }
-    while (i < obj.getString()->getLength()) {
-      if (isUnicode) {
-	u = ((s1->getChar(i) & 0xff) << 8) |
-	    (s1->getChar(i+1) & 0xff);
-	i += 2;
-	if (u >= 0xd800 && u <= 0xdbff && i < obj.getString()->getLength()) {
-	  // surrogate pair
-	  u2 = ((s1->getChar(i) & 0xff) << 8) |
-	    (s1->getChar(i+1) & 0xff);
-	  i += 2;
-	  if (u2 >= 0xdc00 && u2 <= 0xdfff) {
-	    u = 0x10000 + ((u - 0xd800) << 10) + (u2 - 0xdc00);
-	  }
-	}
-      } else {
-	u = pdfDocEncoding[s1->getChar(i) & 0xff];
-	++i;
-      }
-      n = uMap->mapUnicode(u, buf, sizeof(buf));
+    len = TextStringToUCS4(s1, &u);
+    for (i = 0; i < len; i++) {
+      n = uMap->mapUnicode(u[i], buf, sizeof(buf));
       fwrite(buf, 1, n, stdout);
     }
     fputc('\n', stdout);
diff --git a/utils/pdfseparate.cc b/utils/pdfseparate.cc
index 25fac5a..35ae020 100644
--- a/utils/pdfseparate.cc
+++ b/utils/pdfseparate.cc
@@ -52,10 +52,6 @@
     error(errSyntaxError, -1, "Could not extract page(s) from damaged file ('{0:s}')", srcFileName);
     return false;
   }
-  if (doc->isEncrypted()) {
-    error(errSyntaxError, -1, "Could not extract page(s) from encrypted file ('{0:s}')", srcFileName);
-    return false;
-  }
 
   if (firstPage == 0 && lastPage == 0) {
     firstPage = 1;
diff --git a/utils/pdftohtml.1 b/utils/pdftohtml.1
index 44137e4..f08fccb 100644
--- a/utils/pdftohtml.1
+++ b/utils/pdftohtml.1
@@ -70,13 +70,9 @@
 .B \-hidden
 force hidden text extraction
 .TP
-.B \-dev 
-output device name for Ghostscript (png16m, jpeg etc).
-Unless this option is specified, Splash will be used
-.TP
-.B \-fmt
+3.B \-fmt
 image file format for Splash output (png or jpg).
-If complex is selected, but neither \-fmt or \-dev are specified,
+If complex is selected, but \-fmt is not specified,
 \-fmt png will be assumed
 .TP
 .B \-nomerge
@@ -89,6 +85,9 @@
 adjust the word break threshold percent. Default is 10.
 Word break occurs when distance between two adjacent characters is
 greater than this percent of character height.
+.TP
+.B \-fontfullname
+outputs the font name without any substitutions.
 
 .SH AUTHOR
 
diff --git a/utils/pdftohtml.cc b/utils/pdftohtml.cc
index d6475b0..97372be 100644
--- a/utils/pdftohtml.cc
+++ b/utils/pdftohtml.cc
@@ -21,6 +21,7 @@
 // Copyright (C) 2011 Steven Murdoch <Steven.Murdoch@cl.cam.ac.uk>
 // Copyright (C) 2012 Igor Slepchin <igor.redhat@gmail.com>
 // Copyright (C) 2012 Ihar Filipau <thephilips@gmail.com>
+// Copyright (C) 2012 Luis Parravicini <lparravi@gmail.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
@@ -62,10 +63,6 @@
 #include "DateInfo.h"
 #include "goo/gfile.h"
 
-#ifndef GHOSTSCRIPT
-# define GHOSTSCRIPT "gs"
-#endif
-
 static int firstPage = 1;
 static int lastPage = 0;
 static GBool rawOrder = gTrue;
@@ -75,7 +72,6 @@
 GBool complexMode=gFalse;
 GBool singleHtml=gFalse; // singleHtml
 GBool ignore=gFalse;
-static GBool useSplash=gTrue;
 static char extension[5]="png";
 static double scale=1.5;
 GBool noframes=gFalse;
@@ -87,9 +83,9 @@
 
 GBool showHidden = gFalse;
 GBool noMerge = gFalse;
+GBool fontFullName = gFalse;
 static char ownerPassword[33] = "";
 static char userPassword[33] = "";
-static char gsDevice[33] = "none";
 static GBool printVersion = gFalse;
 
 static GooString* getInfoString(Dict *infoDict, const char *key);
@@ -132,8 +128,6 @@
    "do not merge paragraphs"},   
   {"-enc",    argString,   textEncName,    sizeof(textEncName),
    "output text encoding name"},
-  {"-dev",    argString,   gsDevice,       sizeof(gsDevice),
-   "output device name for Ghostscript (png16m, jpeg etc)"},
   {"-fmt",    argString,   extension,      sizeof(extension),
    "image file format for Splash output (png or jpg)"},
   {"-v",      argFlag,     &printVersion,  0,
@@ -146,6 +140,8 @@
    "override document DRM settings"},
   {"-wbt",    argFP,    &wordBreakThreshold, 0,
    "word break threshold (default 10 percent)"},
+  {"-fontfullname", argFlag, &fontFullName, 0,
+   "outputs font full name"},   
   {NULL}
 };
 
@@ -181,18 +177,15 @@
   GooString *docTitle = NULL;
   GooString *author = NULL, *keywords = NULL, *subject = NULL, *date = NULL;
   GooString *htmlFileName = NULL;
-  GooString *psFileName = NULL;
   HtmlOutputDev *htmlOut = NULL;
 #ifdef HAVE_SPLASH
   SplashOutputDev *splashOut = NULL;
 #endif
-  PSOutputDev *psOut = NULL;
   GBool doOutline;
   GBool ok;
   char *p;
   GooString *ownerPW, *userPW;
   Object info;
-  const char * extsList[] = {"png", "jpeg", "bmp", "pcx", "tiff", "pbm", NULL};
 
   // parse args
   ok = parseArgs(argDesc, &argc, argv);
@@ -344,36 +337,6 @@
   info.free();
   if( !docTitle ) docTitle = new GooString(htmlFileName);
 
-  if( strcmp("none", gsDevice) ) {
-    useSplash = gFalse;
-    /* determine extensions of output background images */
-    int i;
-    for(i = 0; extsList[i]; i++)
-    {
-      if( strstr(gsDevice, extsList[i]) != (char *) NULL )
-      {
-        strncpy(extension, extsList[i], sizeof(extension));
-        break;
-      }
-    }
-  }
-
-#ifndef HAVE_SPLASH
-  if( useSplash ) {
-    fprintf(stderr, "You are trying to use the -fmt option but your pdftohtml was built without support for it. Please use the -dev option\n");
-    delete docTitle;
-    delete author;
-    delete keywords;
-    delete subject;
-    delete date;
-    delete htmlFileName;
-    delete globalParams;
-    delete fileName;
-    delete doc;
-    return -1;
-  }
-#endif
-
   if (!singleHtml)
       rawOrder = complexMode; // todo: figure out what exactly rawOrder do :)
   else
@@ -421,87 +384,43 @@
   }
   
   if ((complexMode || singleHtml) && !xml && !ignore) {
-    if(useSplash) {
 #ifdef HAVE_SPLASH
-      GooString *imgFileName = NULL;
-      // White paper color
-      SplashColor color;
-      color[0] = color[1] = color[2] = 255;
-      // If the user specified "jpg" use JPEG, otherwise PNG
-      SplashImageFileFormat format = strcmp(extension, "jpg") ?
-          splashFormatPng : splashFormatJpeg;
+    GooString *imgFileName = NULL;
+    // White paper color
+    SplashColor color;
+    color[0] = color[1] = color[2] = 255;
+    // If the user specified "jpg" use JPEG, otherwise PNG
+    SplashImageFileFormat format = strcmp(extension, "jpg") ?
+        splashFormatPng : splashFormatJpeg;
 
-      splashOut = new SplashOutputDevNoText(splashModeRGB8, 4, gFalse, color);
-      splashOut->startDoc(doc);
+    splashOut = new SplashOutputDevNoText(splashModeRGB8, 4, gFalse, color);
+    splashOut->startDoc(doc);
 
-      for (int pg = firstPage; pg <= lastPage; ++pg) {
-        doc->displayPage(splashOut, pg,
-                         72 * scale, 72 * scale,
-                         0, gTrue, gFalse, gFalse);
-        SplashBitmap *bitmap = splashOut->getBitmap();
+    for (int pg = firstPage; pg <= lastPage; ++pg) {
+      doc->displayPage(splashOut, pg,
+                       72 * scale, 72 * scale,
+                       0, gTrue, gFalse, gFalse);
+      SplashBitmap *bitmap = splashOut->getBitmap();
 
-        imgFileName = GooString::format("{0:s}{1:03d}.{2:s}", 
-            htmlFileName->getCString(), pg, extension);
+      imgFileName = GooString::format("{0:s}{1:03d}.{2:s}", 
+          htmlFileName->getCString(), pg, extension);
 
-        bitmap->writeImgFile(format, imgFileName->getCString(),
-                             72 * scale, 72 * scale);
+      bitmap->writeImgFile(format, imgFileName->getCString(),
+                           72 * scale, 72 * scale);
 
-        delete imgFileName;
-      }
-
-      delete splashOut;
-#endif
-    } else {
-      int h=xoutRound(htmlOut->getPageHeight()/scale);
-      int w=xoutRound(htmlOut->getPageWidth()/scale);
-      //int h=xoutRound(doc->getPageHeight(1)/scale);
-      //int w=xoutRound(doc->getPageWidth(1)/scale);
-
-      psFileName = new GooString(htmlFileName->getCString());
-      psFileName->append(".ps");
-
-      psOut = new PSOutputDev(psFileName->getCString(), doc,
-          NULL, firstPage, lastPage, psModePS, w, h);
-      psOut->setDisplayText(gFalse);
-      doc->displayPages(psOut, firstPage, lastPage, 72, 72, 0,
-          gTrue, gFalse, gFalse);
-      delete psOut;
-
-      /*sprintf(buf, "%s -sDEVICE=png16m -dBATCH -dNOPROMPT -dNOPAUSE -r%d -sOutputFile=%s%%03d.png -g%dx%d -q %s", GHOSTSCRIPT, resolution, htmlFileName->getCString(), w, h,
-      psFileName->getCString());*/
-
-      GooString *gsCmd = new GooString(GHOSTSCRIPT);
-      GooString *tw, *th, *sc;
-      gsCmd->append(" -sDEVICE=");
-      gsCmd->append(gsDevice);
-      gsCmd->append(" -dBATCH -dNOPROMPT -dNOPAUSE -r");
-      sc = GooString::fromInt(static_cast<int>(72*scale));
-      gsCmd->append(sc);
-      gsCmd->append(" -sOutputFile=");
-      gsCmd->append("\"");
-      gsCmd->append(htmlFileName);
-      gsCmd->append("%03d.");
-      gsCmd->append(extension);
-      gsCmd->append("\" -g");
-      tw = GooString::fromInt(static_cast<int>(scale*w));
-      gsCmd->append(tw);
-      gsCmd->append("x");
-      th = GooString::fromInt(static_cast<int>(scale*h));
-      gsCmd->append(th);
-      gsCmd->append(" -q \"");
-      gsCmd->append(psFileName);
-      gsCmd->append("\"");
-      //    printf("running: %s\n", gsCmd->getCString());
-      if( !executeCommand(gsCmd->getCString()) && !errQuiet) {
-        error(errIO, -1, "Failed to launch Ghostscript!\n");
-      }
-      unlink(psFileName->getCString());
-      delete tw;
-      delete th;
-      delete sc;
-      delete gsCmd;
-      delete psFileName;
+      delete imgFileName;
     }
+
+    delete splashOut;
+#else
+    fprintf(stderr, "Your pdftohtml was built without splash backend support. It is needed for the option you want to use.\n");
+    delete htmlOut;
+    delete htmlFileName;
+    delete globalParams;
+    delete fileName;
+    delete doc;
+    return -1;
+#endif
   }
   
   delete htmlOut;
diff --git a/utils/pdftoppm.cc b/utils/pdftoppm.cc
index 04a0dfb..cebb12a 100644
--- a/utils/pdftoppm.cc
+++ b/utils/pdftoppm.cc
@@ -23,7 +23,7 @@
 // Copyright (C) 2010 Hib Eris <hib@hiberis.nl>
 // Copyright (C) 2010 Jonathan Liu <net147@gmail.com>
 // Copyright (C) 2010 William Bader <williambader@hotmail.com>
-// Copyright (C) 2011 Thomas Freitag <Thomas.Freitag@alfa.de>
+// Copyright (C) 2011, 2012 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
@@ -353,10 +353,8 @@
 #if SPLASH_CMYK
   if (jpegcmyk || overprint) {
     globalParams->setOverprintPreview(gTrue);
-    paperColor[0] = 0;
-    paperColor[1] = 0;
-    paperColor[2] = 0;
-    paperColor[3] = 0;
+    for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
+      paperColor[cp] = 0;
   } else 
 #endif
   {
@@ -367,7 +365,7 @@
   splashOut = new SplashOutputDev(mono ? splashModeMono1 :
 				    gray ? splashModeMono8 :
 #if SPLASH_CMYK
-				    (jpegcmyk || overprint) ? splashModeCMYK8 :
+				    (jpegcmyk || overprint) ? splashModeDeviceN8 :
 #endif
 				             splashModeRGB8, 4,
 				  gFalse, paperColor);
diff --git a/utils/pdfunite.cc b/utils/pdfunite.cc
index 212f89b..a16f4dd 100644
--- a/utils/pdfunite.cc
+++ b/utils/pdfunite.cc
@@ -154,7 +154,7 @@
         outStr->printf("/Parent %d 0 R", rootNum + 1);
       } else {
         outStr->printf("/%s ", key);
-        PDFDoc::writeObject(&value, NULL, outStr, yRef, offsets[i]);
+        PDFDoc::writeObject(&value, outStr, yRef, offsets[i], NULL, cryptRC4, 0, 0, 0);
       }
       value.free();
     }