]> www.fi.muni.cz Git - evince.git/blobdiff - libview/ev-jobs.c
[dualscreen] fix crash on ctrl+w and fix control window closing
[evince.git] / libview / ev-jobs.c
index 102cd9083d83e982b8f8eab541d531e69b1941a3..816a56bbb3c7987351291af6d82f778eb0c41243 100644 (file)
  *
  * You 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.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
 #include <config.h>
 
 #include "ev-jobs.h"
-#include "ev-document-thumbnails.h"
 #include "ev-document-links.h"
 #include "ev-document-images.h"
 #include "ev-document-forms.h"
 #include "ev-document-security.h"
 #include "ev-document-find.h"
 #include "ev-document-layers.h"
+#include "ev-document-print.h"
+#include "ev-document-annotations.h"
+#include "ev-document-attachments.h"
+#include "ev-document-text.h"
 #include "ev-debug.h"
 
 #include <errno.h>
 #include <glib/gstdio.h>
-#include <glib/gi18n.h>
+#include <glib/gi18n-lib.h>
 #include <unistd.h>
 
 static void ev_job_init                   (EvJob                 *job);
@@ -46,8 +49,12 @@ static void ev_job_links_init             (EvJobLinks            *job);
 static void ev_job_links_class_init       (EvJobLinksClass       *class);
 static void ev_job_attachments_init       (EvJobAttachments      *job);
 static void ev_job_attachments_class_init (EvJobAttachmentsClass *class);
+static void ev_job_annots_init            (EvJobAnnots           *job);
+static void ev_job_annots_class_init      (EvJobAnnotsClass      *class);
 static void ev_job_render_init            (EvJobRender           *job);
 static void ev_job_render_class_init      (EvJobRenderClass      *class);
+static void ev_job_page_data_init         (EvJobPageData         *job);
+static void ev_job_page_data_class_init   (EvJobPageDataClass    *class);
 static void ev_job_thumbnail_init         (EvJobThumbnail        *job);
 static void ev_job_thumbnail_class_init   (EvJobThumbnailClass   *class);
 static void ev_job_load_init             (EvJobLoad             *job);
@@ -60,6 +67,8 @@ static void ev_job_layers_init            (EvJobLayers           *job);
 static void ev_job_layers_class_init      (EvJobLayersClass      *class);
 static void ev_job_export_init            (EvJobExport           *job);
 static void ev_job_export_class_init      (EvJobExportClass      *class);
+static void ev_job_print_init             (EvJobPrint            *job);
+static void ev_job_print_class_init       (EvJobPrintClass       *class);
 
 enum {
        CANCELLED,
@@ -67,11 +76,6 @@ enum {
        LAST_SIGNAL
 };
 
-enum {
-       PAGE_READY,
-       RENDER_LAST_SIGNAL
-};
-
 enum {
        FONTS_UPDATED,
        FONTS_LAST_SIGNAL
@@ -83,14 +87,15 @@ enum {
 };
 
 static guint job_signals[LAST_SIGNAL] = { 0 };
-static guint job_render_signals[RENDER_LAST_SIGNAL] = { 0 };
 static guint job_fonts_signals[FONTS_LAST_SIGNAL] = { 0 };
 static guint job_find_signals[FIND_LAST_SIGNAL] = { 0 };
 
 G_DEFINE_ABSTRACT_TYPE (EvJob, ev_job, G_TYPE_OBJECT)
 G_DEFINE_TYPE (EvJobLinks, ev_job_links, EV_TYPE_JOB)
 G_DEFINE_TYPE (EvJobAttachments, ev_job_attachments, EV_TYPE_JOB)
+G_DEFINE_TYPE (EvJobAnnots, ev_job_annots, EV_TYPE_JOB)
 G_DEFINE_TYPE (EvJobRender, ev_job_render, EV_TYPE_JOB)
+G_DEFINE_TYPE (EvJobPageData, ev_job_page_data, EV_TYPE_JOB)
 G_DEFINE_TYPE (EvJobThumbnail, ev_job_thumbnail, EV_TYPE_JOB)
 G_DEFINE_TYPE (EvJobFonts, ev_job_fonts, EV_TYPE_JOB)
 G_DEFINE_TYPE (EvJobLoad, ev_job_load, EV_TYPE_JOB)
@@ -98,6 +103,7 @@ G_DEFINE_TYPE (EvJobSave, ev_job_save, EV_TYPE_JOB)
 G_DEFINE_TYPE (EvJobFind, ev_job_find, EV_TYPE_JOB)
 G_DEFINE_TYPE (EvJobLayers, ev_job_layers, EV_TYPE_JOB)
 G_DEFINE_TYPE (EvJobExport, ev_job_export, EV_TYPE_JOB)
+G_DEFINE_TYPE (EvJobPrint, ev_job_print, EV_TYPE_JOB)
 
 /* EvJob */
 static void
@@ -210,7 +216,7 @@ ev_job_run (EvJob *job)
 void
 ev_job_cancel (EvJob *job)
 {
-       if (job->cancelled || (job->finished && job->idle_finished_id == 0))
+       if (job->cancelled)
                return;
 
        ev_debug_message (DEBUG_JOBS, "job %s (%p) cancelled", EV_GET_TYPE_NAME (job), job);
@@ -219,6 +225,10 @@ ev_job_cancel (EvJob *job)
        /* This should never be called from a thread */
        job->cancelled = TRUE;
        g_cancellable_cancel (job->cancellable);
+
+        if (job->finished && job->idle_finished_id == 0)
+                return;
+
        g_signal_emit (job, job_signals[CANCELLED], 0);
 }
 
@@ -325,6 +335,38 @@ ev_job_links_dispose (GObject *object)
        (* G_OBJECT_CLASS (ev_job_links_parent_class)->dispose) (object);
 }
 
+static gboolean
+fill_page_labels (GtkTreeModel   *tree_model,
+                 GtkTreePath    *path,
+                 GtkTreeIter    *iter,
+                 EvJob          *job)
+{
+       EvDocumentLinks *document_links;
+       EvLink          *link;
+       gchar           *page_label;
+
+       gtk_tree_model_get (tree_model, iter,
+                           EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
+                           -1);
+
+       if (!link)
+               return FALSE;
+
+       document_links = EV_DOCUMENT_LINKS (job->document);
+       page_label = ev_document_links_get_link_page_label (document_links, link);
+       if (!page_label)
+               return FALSE;
+
+       gtk_tree_store_set (GTK_TREE_STORE (tree_model), iter,
+                           EV_DOCUMENT_LINKS_COLUMN_PAGE_LABEL, page_label,
+                           -1);
+
+       g_free (page_label);
+       g_object_unref (link);
+
+       return FALSE;
+}
+
 static gboolean
 ev_job_links_run (EvJob *job)
 {
@@ -336,7 +378,9 @@ ev_job_links_run (EvJob *job)
        ev_document_doc_mutex_lock ();
        job_links->model = ev_document_links_get_links_model (EV_DOCUMENT_LINKS (job->document));
        ev_document_doc_mutex_unlock ();
-       
+
+       gtk_tree_model_foreach (job_links->model, (GtkTreeModelForeachFunc)fill_page_labels, job);
+
        ev_job_succeeded (job);
        
        return FALSE;
@@ -397,13 +441,14 @@ ev_job_attachments_run (EvJob *job)
 
        ev_debug_message (DEBUG_JOBS, NULL);
        ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
-       
+
        ev_document_doc_mutex_lock ();
-       job_attachments->attachments = ev_document_get_attachments (job->document);
+       job_attachments->attachments =
+               ev_document_attachments_get_attachments (EV_DOCUMENT_ATTACHMENTS (job->document));
        ev_document_doc_mutex_unlock ();
-       
+
        ev_job_succeeded (job);
-       
+
        return FALSE;
 }
 
@@ -430,6 +475,85 @@ ev_job_attachments_new (EvDocument *document)
        return job;
 }
 
+/* EvJobAnnots */
+static void
+ev_job_annots_init (EvJobAnnots *job)
+{
+       EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
+}
+
+static void
+ev_job_annots_dispose (GObject *object)
+{
+       EvJobAnnots *job;
+
+       ev_debug_message (DEBUG_JOBS, NULL);
+
+       job = EV_JOB_ANNOTS (object);
+
+       if (job->annots) {
+               g_list_foreach (job->annots, (GFunc)ev_mapping_list_unref, NULL);
+               g_list_free (job->annots);
+               job->annots = NULL;
+       }
+
+       G_OBJECT_CLASS (ev_job_annots_parent_class)->dispose (object);
+}
+
+static gboolean
+ev_job_annots_run (EvJob *job)
+{
+       EvJobAnnots *job_annots = EV_JOB_ANNOTS (job);
+       gint         i;
+
+       ev_debug_message (DEBUG_JOBS, NULL);
+       ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
+
+       ev_document_doc_mutex_lock ();
+       for (i = 0; i < ev_document_get_n_pages (job->document); i++) {
+               EvMappingList *mapping_list;
+               EvPage        *page;
+
+               page = ev_document_get_page (job->document, i);
+               mapping_list = ev_document_annotations_get_annotations (EV_DOCUMENT_ANNOTATIONS (job->document),
+                                                                       page);
+               g_object_unref (page);
+
+               if (mapping_list)
+                       job_annots->annots = g_list_prepend (job_annots->annots, mapping_list);
+       }
+       ev_document_doc_mutex_unlock ();
+
+       job_annots->annots = g_list_reverse (job_annots->annots);
+
+       ev_job_succeeded (job);
+
+       return FALSE;
+}
+
+static void
+ev_job_annots_class_init (EvJobAnnotsClass *class)
+{
+       GObjectClass *oclass = G_OBJECT_CLASS (class);
+       EvJobClass   *job_class = EV_JOB_CLASS (class);
+
+       oclass->dispose = ev_job_annots_dispose;
+       job_class->run = ev_job_annots_run;
+}
+
+EvJob *
+ev_job_annots_new (EvDocument *document)
+{
+       EvJob *job;
+
+       ev_debug_message (DEBUG_JOBS, NULL);
+
+       job = g_object_new (EV_TYPE_JOB_ANNOTS, NULL);
+       job->document = g_object_ref (document);
+
+       return job;
+}
+
 /* EvJobRender */
 static void
 ev_job_render_init (EvJobRender *job)
@@ -444,12 +568,8 @@ ev_job_render_dispose (GObject *object)
 
        job = EV_JOB_RENDER (object);
 
-       if (job->ev_page) {
-               ev_debug_message (DEBUG_JOBS, "page: %d (%p)", job->ev_page->index, job);
-               g_object_unref (job->ev_page);
-               job->ev_page = NULL;
-       }
-       
+       ev_debug_message (DEBUG_JOBS, "page: %d (%p)", job->page, job);
+
        if (job->surface) {
                cairo_surface_destroy (job->surface);
                job->surface = NULL;
@@ -461,44 +581,18 @@ ev_job_render_dispose (GObject *object)
        }
 
        if (job->selection_region) {
-               gdk_region_destroy (job->selection_region);
+               cairo_region_destroy (job->selection_region);
                job->selection_region = NULL;
        }
 
        (* G_OBJECT_CLASS (ev_job_render_parent_class)->dispose) (object);
 }
 
-static gboolean
-notify_page_ready (EvJobRender *job)
-{
-       ev_debug_message (DEBUG_JOBS, "%d (%p)", job->ev_page->index, job);
-       ev_profiler_stop (EV_PROFILE_JOBS, "Rendering page %d", job->ev_page->index);
-
-       if (EV_JOB (job)->cancelled) {
-               ev_debug_message (DEBUG_JOBS, "%s (%p) job was cancelled, do not emit page_ready", EV_GET_TYPE_NAME (job), job);
-       } else {
-               g_signal_emit (job, job_render_signals[PAGE_READY], 0);
-       }
-
-       return FALSE;
-}
-
-static void
-ev_job_render_page_ready (EvJobRender *job)
-{
-       ev_debug_message (DEBUG_JOBS, "%d (%p)", job->ev_page->index, job);
-       
-       job->page_ready = TRUE;
-       g_idle_add_full (G_PRIORITY_HIGH_IDLE,
-                        (GSourceFunc)notify_page_ready,
-                        g_object_ref (job),
-                        (GDestroyNotify)g_object_unref);
-}
-
 static gboolean
 ev_job_render_run (EvJob *job)
 {
        EvJobRender     *job_render = EV_JOB_RENDER (job);
+       EvPage          *ev_page;
        EvRenderContext *rc;
 
        ev_debug_message (DEBUG_JOBS, "page: %d (%p)", job_render->page, job);
@@ -510,9 +604,10 @@ ev_job_render_run (EvJob *job)
                
        ev_document_fc_mutex_lock ();
 
-       job_render->ev_page = ev_document_get_page (job->document, job_render->page);
-       rc = ev_render_context_new (job_render->ev_page, job_render->rotation, job_render->scale);
-               
+       ev_page = ev_document_get_page (job->document, job_render->page);
+       rc = ev_render_context_new (ev_page, job_render->rotation, job_render->scale);
+       g_object_unref (ev_page);
+
        job_render->surface = ev_document_render (job->document, rc);
        /* If job was cancelled during the page rendering,
         * we return now, so that the thread is finished ASAP
@@ -524,8 +619,8 @@ ev_job_render_run (EvJob *job)
 
                return FALSE;
        }
-       
-       if ((job_render->flags & EV_RENDER_INCLUDE_SELECTION) && EV_IS_SELECTION (job->document)) {
+
+       if (job_render->include_selection && EV_IS_SELECTION (job->document)) {
                ev_selection_render_selection (EV_SELECTION (job->document),
                                               rc,
                                               &(job_render->selection),
@@ -540,25 +635,9 @@ ev_job_render_run (EvJob *job)
                                                           &(job_render->selection_points));
        }
 
-       ev_job_render_page_ready (job_render);
-               
-       ev_document_fc_mutex_unlock ();
-               
-       if ((job_render->flags & EV_RENDER_INCLUDE_TEXT) && EV_IS_SELECTION (job->document))
-               job_render->text_mapping =
-                       ev_selection_get_selection_map (EV_SELECTION (job->document), rc);
-       if ((job_render->flags & EV_RENDER_INCLUDE_LINKS) && EV_IS_DOCUMENT_LINKS (job->document))
-               job_render->link_mapping =
-                       ev_document_links_get_links (EV_DOCUMENT_LINKS (job->document), job_render->page);
-       if ((job_render->flags & EV_RENDER_INCLUDE_FORMS) && EV_IS_DOCUMENT_FORMS (job->document))
-               job_render->form_field_mapping =
-                       ev_document_forms_get_form_fields (EV_DOCUMENT_FORMS (job->document),
-                                                          job_render->ev_page);
-       if ((job_render->flags & EV_RENDER_INCLUDE_IMAGES) && EV_IS_DOCUMENT_IMAGES (job->document))
-               job_render->image_mapping =
-                       ev_document_images_get_image_mapping (EV_DOCUMENT_IMAGES (job->document),
-                                                             job_render->page);
        g_object_unref (rc);
+
+       ev_document_fc_mutex_unlock ();
        ev_document_doc_mutex_unlock ();
        
        ev_job_succeeded (job);
@@ -572,15 +651,6 @@ ev_job_render_class_init (EvJobRenderClass *class)
        GObjectClass *oclass = G_OBJECT_CLASS (class);
        EvJobClass   *job_class = EV_JOB_CLASS (class);
 
-       job_render_signals [PAGE_READY] =
-               g_signal_new ("page-ready",
-                             EV_TYPE_JOB_RENDER,
-                             G_SIGNAL_RUN_LAST,
-                             G_STRUCT_OFFSET (EvJobRenderClass, page_ready),
-                             NULL, NULL,
-                             g_cclosure_marshal_VOID__VOID,
-                             G_TYPE_NONE, 0);
-
        oclass->dispose = ev_job_render_dispose;
        job_class->run = ev_job_render_run;
 }
@@ -589,10 +659,9 @@ EvJob *
 ev_job_render_new (EvDocument   *document,
                   gint          page,
                   gint          rotation,
-                  gdouble       scale, 
+                  gdouble       scale,
                   gint          width,
-                  gint          height,
-                  EvRenderFlags flags)
+                  gint          height)
 {
        EvJobRender *job;
 
@@ -606,7 +675,6 @@ ev_job_render_new (EvDocument   *document,
        job->scale = scale;
        job->target_width = width;
        job->target_height = height;
-       job->flags = flags;
 
        return EV_JOB (job);
 }
@@ -618,14 +686,93 @@ ev_job_render_set_selection_info (EvJobRender     *job,
                                  GdkColor        *text,
                                  GdkColor        *base)
 {
-       job->flags |= EV_RENDER_INCLUDE_SELECTION;
-       
+       job->include_selection = TRUE;
+
        job->selection_points = *selection_points;
        job->selection_style = selection_style;
        job->text = *text;
        job->base = *base;
 }
 
+/* EvJobPageData */
+static void
+ev_job_page_data_init (EvJobPageData *job)
+{
+       EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
+}
+
+static gboolean
+ev_job_page_data_run (EvJob *job)
+{
+       EvJobPageData *job_pd = EV_JOB_PAGE_DATA (job);
+       EvPage        *ev_page;
+
+       ev_debug_message (DEBUG_JOBS, "page: %d (%p)", job_pd->page, job);
+       ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
+
+       ev_document_doc_mutex_lock ();
+       ev_page = ev_document_get_page (job->document, job_pd->page);
+
+       if ((job_pd->flags & EV_PAGE_DATA_INCLUDE_TEXT_MAPPING) && EV_IS_DOCUMENT_TEXT (job->document))
+               job_pd->text_mapping =
+                       ev_document_text_get_text_mapping (EV_DOCUMENT_TEXT (job->document), ev_page);
+       if ((job_pd->flags & EV_PAGE_DATA_INCLUDE_TEXT) && EV_IS_DOCUMENT_TEXT (job->document))
+               job_pd->text =
+                       ev_document_text_get_text (EV_DOCUMENT_TEXT (job->document), ev_page);
+       if ((job_pd->flags & EV_PAGE_DATA_INCLUDE_TEXT_LAYOUT) && EV_IS_DOCUMENT_TEXT (job->document))
+               ev_document_text_get_text_layout (EV_DOCUMENT_TEXT (job->document),
+                                                 ev_page,
+                                                 &(job_pd->text_layout),
+                                                 &(job_pd->text_layout_length));
+       if ((job_pd->flags & EV_PAGE_DATA_INCLUDE_LINKS) && EV_IS_DOCUMENT_LINKS (job->document))
+               job_pd->link_mapping =
+                       ev_document_links_get_links (EV_DOCUMENT_LINKS (job->document), ev_page);
+       if ((job_pd->flags & EV_PAGE_DATA_INCLUDE_FORMS) && EV_IS_DOCUMENT_FORMS (job->document))
+               job_pd->form_field_mapping =
+                       ev_document_forms_get_form_fields (EV_DOCUMENT_FORMS (job->document),
+                                                          ev_page);
+       if ((job_pd->flags & EV_PAGE_DATA_INCLUDE_IMAGES) && EV_IS_DOCUMENT_IMAGES (job->document))
+               job_pd->image_mapping =
+                       ev_document_images_get_image_mapping (EV_DOCUMENT_IMAGES (job->document),
+                                                             ev_page);
+       if ((job_pd->flags & EV_PAGE_DATA_INCLUDE_ANNOTS) && EV_IS_DOCUMENT_ANNOTATIONS (job->document))
+               job_pd->annot_mapping =
+                       ev_document_annotations_get_annotations (EV_DOCUMENT_ANNOTATIONS (job->document),
+                                                                ev_page);
+       g_object_unref (ev_page);
+       ev_document_doc_mutex_unlock ();
+
+       ev_job_succeeded (job);
+
+       return FALSE;
+}
+
+static void
+ev_job_page_data_class_init (EvJobPageDataClass *class)
+{
+       EvJobClass *job_class = EV_JOB_CLASS (class);
+
+       job_class->run = ev_job_page_data_run;
+}
+
+EvJob *
+ev_job_page_data_new (EvDocument        *document,
+                     gint               page,
+                     EvJobPageDataFlags flags)
+{
+       EvJobPageData *job;
+
+       ev_debug_message (DEBUG_JOBS, "%d", page);
+
+       job = g_object_new (EV_TYPE_JOB_PAGE_DATA, NULL);
+
+       EV_JOB (job)->document = g_object_ref (document);
+       job->page = page;
+       job->flags = flags;
+
+       return EV_JOB (job);
+}
+
 /* EvJobThumbnail */
 static void
 ev_job_thumbnail_init (EvJobThumbnail *job)
@@ -655,6 +802,7 @@ ev_job_thumbnail_run (EvJob *job)
 {
        EvJobThumbnail  *job_thumb = EV_JOB_THUMBNAIL (job);
        EvRenderContext *rc;
+       GdkPixbuf       *pixbuf;
        EvPage          *page;
 
        ev_debug_message (DEBUG_JOBS, "%d (%p)", job_thumb->page, job);
@@ -666,11 +814,14 @@ ev_job_thumbnail_run (EvJob *job)
        rc = ev_render_context_new (page, job_thumb->rotation, job_thumb->scale);
        g_object_unref (page);
 
-       job_thumb->thumbnail = ev_document_thumbnails_get_thumbnail (EV_DOCUMENT_THUMBNAILS (job->document),
-                                                                    rc, TRUE);
+       pixbuf = ev_document_get_thumbnail (job->document, rc);
        g_object_unref (rc);
        ev_document_doc_mutex_unlock ();
 
+       if (pixbuf)
+               job_thumb->thumbnail = ev_document_misc_get_thumbnail_frame (-1, -1, pixbuf);
+       g_object_unref (pixbuf);
+
        ev_job_succeeded (job);
        
        return FALSE;
@@ -821,6 +972,8 @@ ev_job_load_run (EvJob *job)
           because, e.g., a password is required - if so, just reload rather than
           creating a new instance */
        if (job->document) {
+               const gchar *uncompressed_uri;
+
                if (job_load->password) {
                        ev_document_security_set_password (EV_DOCUMENT_SECURITY (job->document),
                                                           job_load->password);
@@ -829,9 +982,11 @@ ev_job_load_run (EvJob *job)
                job->failed = FALSE;
                job->finished = FALSE;
                g_clear_error (&job->error);
-               
+
+               uncompressed_uri = g_object_get_data (G_OBJECT (job->document),
+                                                     "uri-uncompressed");
                ev_document_load (job->document,
-                                 job_load->uri,
+                                 uncompressed_uri ? uncompressed_uri : job_load->uri,
                                  &error);
        } else {
                job->document = ev_document_factory_get_document (job_load->uri,
@@ -925,31 +1080,17 @@ ev_job_save_run (EvJob *job)
 {
        EvJobSave *job_save = EV_JOB_SAVE (job);
        gint       fd;
-       gchar     *filename;
-       gchar     *tmp_filename;
+       gchar     *tmp_filename = NULL;
        gchar     *local_uri;
        GError    *error = NULL;
        
        ev_debug_message (DEBUG_JOBS, "uri: %s, document_uri: %s", job_save->uri, job_save->document_uri);
        ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
-       
-       filename = ev_tmp_filename ("saveacopy");
-       tmp_filename = g_strdup_printf ("%s.XXXXXX", filename);
-       g_free (filename);
 
-       fd = g_mkstemp (tmp_filename);
-       if (fd == -1) {
-               gchar *display_name;
-               gint   save_errno = errno;
-
-               display_name = g_filename_display_name (tmp_filename);
-               ev_job_failed (job,
-                              G_FILE_ERROR,
-                              g_file_error_from_errno (save_errno),
-                              _("Failed to create file ā€œ%sā€: %s"),
-                              display_name, g_strerror (save_errno));
-               g_free (display_name);
-               g_free (tmp_filename);
+        fd = ev_mkstemp ("saveacopy.XXXXXX", &tmp_filename, &error);
+        if (fd == -1) {
+                ev_job_failed_from_error (job, error);
+                g_error_free (error);
 
                return FALSE;
        }
@@ -957,8 +1098,11 @@ ev_job_save_run (EvJob *job)
        ev_document_doc_mutex_lock ();
 
        /* Save document to temp filename */
-       local_uri = g_filename_to_uri (tmp_filename, NULL, NULL);
-       ev_document_save (job->document, local_uri, &error);
+       local_uri = g_filename_to_uri (tmp_filename, NULL, &error);
+        if (local_uri != NULL) {
+                ev_document_save (job->document, local_uri, &error);
+        }
+
        close (fd);
 
        ev_document_doc_mutex_unlock ();
@@ -989,7 +1133,7 @@ ev_job_save_run (EvJob *job)
 
                uri_comp = ev_file_compress (local_uri, ctype, &error);
                g_free (local_uri);
-               ev_tmp_filename_unlink (tmp_filename);
+               g_unlink (tmp_filename);
 
                if (!uri_comp || error) {
                        local_uri = NULL;
@@ -999,7 +1143,7 @@ ev_job_save_run (EvJob *job)
        }
 
        g_free (tmp_filename);
-       
+
        if (error) {
                g_free (local_uri);
                ev_job_failed_from_error (job, error);
@@ -1075,7 +1219,7 @@ ev_job_find_dispose (GObject *object)
                gint i;
 
                for (i = 0; i < job->n_pages; i++) {
-                       g_list_foreach (job->pages[i], (GFunc)g_free, NULL);
+                       g_list_foreach (job->pages[i], (GFunc)ev_rectangle_free, NULL);
                        g_list_free (job->pages[i]);
                }
 
@@ -1364,3 +1508,114 @@ ev_job_export_set_page (EvJobExport *job,
 {
        job->page = page;
 }
+
+/* EvJobPrint */
+static void
+ev_job_print_init (EvJobPrint *job)
+{
+       EV_JOB (job)->run_mode = EV_JOB_RUN_THREAD;
+       job->page = -1;
+}
+
+static void
+ev_job_print_dispose (GObject *object)
+{
+       EvJobPrint *job;
+
+       ev_debug_message (DEBUG_JOBS, NULL);
+
+       job = EV_JOB_PRINT (object);
+
+       if (job->cr) {
+               cairo_destroy (job->cr);
+               job->cr = NULL;
+       }
+
+       (* G_OBJECT_CLASS (ev_job_print_parent_class)->dispose) (object);
+}
+
+static gboolean
+ev_job_print_run (EvJob *job)
+{
+       EvJobPrint     *job_print = EV_JOB_PRINT (job);
+       EvPage         *ev_page;
+       cairo_status_t  cr_status;
+
+       g_assert (job_print->page != -1);
+       g_assert (job_print->cr != NULL);
+
+       ev_debug_message (DEBUG_JOBS, NULL);
+       ev_profiler_start (EV_PROFILE_JOBS, "%s (%p)", EV_GET_TYPE_NAME (job), job);
+
+       job->failed = FALSE;
+       job->finished = FALSE;
+       g_clear_error (&job->error);
+
+       ev_document_doc_mutex_lock ();
+
+       ev_page = ev_document_get_page (job->document, job_print->page);
+       ev_document_print_print_page (EV_DOCUMENT_PRINT (job->document),
+                                     ev_page, job_print->cr);
+       g_object_unref (ev_page);
+
+       ev_document_doc_mutex_unlock ();
+
+        if (g_cancellable_is_cancelled (job->cancellable))
+                return FALSE;
+
+       cr_status = cairo_status (job_print->cr);
+       if (cr_status == CAIRO_STATUS_SUCCESS) {
+               ev_job_succeeded (job);
+       } else {
+               ev_job_failed (job,
+                              GTK_PRINT_ERROR,
+                              GTK_PRINT_ERROR_GENERAL,
+                              _("Failed to print page %d: %s"),
+                              job_print->page,
+                              cairo_status_to_string (cr_status));
+       }
+
+       return FALSE;
+}
+
+static void
+ev_job_print_class_init (EvJobPrintClass *class)
+{
+       GObjectClass *oclass = G_OBJECT_CLASS (class);
+       EvJobClass   *job_class = EV_JOB_CLASS (class);
+
+       oclass->dispose = ev_job_print_dispose;
+       job_class->run = ev_job_print_run;
+}
+
+EvJob *
+ev_job_print_new (EvDocument *document)
+{
+       EvJob *job;
+
+       ev_debug_message (DEBUG_JOBS, NULL);
+
+       job = g_object_new (EV_TYPE_JOB_PRINT, NULL);
+       job->document = g_object_ref (document);
+
+       return job;
+}
+
+void
+ev_job_print_set_page (EvJobPrint *job,
+                      gint        page)
+{
+       job->page = page;
+}
+
+void
+ev_job_print_set_cairo (EvJobPrint *job,
+                       cairo_t    *cr)
+{
+       if (job->cr == cr)
+               return;
+
+       if (job->cr)
+               cairo_destroy (job->cr);
+       job->cr = cr ? cairo_reference (cr) : NULL;
+}