Ejemplo n.º 1
0
char*
pdf_page_get_text(zathura_page_t* page, PopplerPage* poppler_page,
    zathura_rectangle_t rectangle, zathura_error_t* error)
{
  if (page == NULL || poppler_page == NULL) {
    if (error != NULL) {
      *error = ZATHURA_ERROR_INVALID_ARGUMENTS;
    }
    return NULL;
  }

  PopplerRectangle rect;
  rect.x1 = rectangle.x1;
  rect.x2 = rectangle.x2;
#if !POPPLER_CHECK_VERSION(0,15,0)
  /* adapt y coordinates */
  rect.y1 = zathura_page_get_height(page) - rectangle.y1;
  rect.y2 = zathura_page_get_height(page) - rectangle.y2;
#else
  rect.y1 = rectangle.y1;
  rect.y2 = rectangle.y2;
#endif

  /* get selected text */
  return poppler_page_get_selected_text(poppler_page, POPPLER_SELECTION_GLYPH, &rect);
}
Ejemplo n.º 2
0
zathura_rectangle_t
recalc_rectangle(zathura_page_t* page, zathura_rectangle_t rectangle)
{
  if (page == NULL) {
    goto error_ret;
  }

  zathura_document_t* document = zathura_page_get_document(page);

  if (document == NULL) {
    goto error_ret;
  }

  double page_height = zathura_page_get_height(page);
  double page_width  = zathura_page_get_width(page);
  double scale       = zathura_document_get_scale(document);

  zathura_rectangle_t tmp = rotate_rectangle(rectangle, zathura_document_get_rotation(document), page_height, page_width);
  tmp.x1 *= scale;
  tmp.x2 *= scale;
  tmp.y1 *= scale;
  tmp.y2 *= scale;

  return tmp;

error_ret:

  return rectangle;
}
Ejemplo n.º 3
0
double
page_calc_height_width(zathura_page_t* page, unsigned int* page_height, unsigned int* page_width, bool rotate)
{
  g_return_val_if_fail(page != NULL && page_height != NULL && page_width != NULL, 0.0);

  zathura_document_t* document = zathura_page_get_document(page);
  if (document == NULL) {
    return 0.0;
  }

  double height = zathura_page_get_height(page);
  double width  = zathura_page_get_width(page);
  double scale  = zathura_document_get_scale(document);
  double real_scale;

  if (rotate && zathura_document_get_rotation(document) % 180) {
    *page_width  = ceil(height * scale);
    *page_height = ceil(width  * scale);
    real_scale = MAX(*page_width / height, *page_height / width);
  } else {
    *page_width  = ceil(width  * scale);
    *page_height = ceil(height * scale);
    real_scale = MAX(*page_width / width, *page_height / height);
  }

  return real_scale;
}
Ejemplo n.º 4
0
Archivo: print.c Proyecto: pwmt/zathura
static bool
draw_page_image(cairo_t* cairo, GtkPrintContext* context, zathura_t* zathura,
                zathura_page_t* page)
{
  /* Try to render the page on a temporary image surface. */
  const double width = gtk_print_context_get_width(context);
  const double height = gtk_print_context_get_height(context);

  const double scale_height = 5;
  const double scale_width  = 5;

  /* Render to a surface that is 5 times larger to workaround quality issues. */
  const double page_height = zathura_page_get_height(page) * scale_height;
  const double page_width  = zathura_page_get_width(page) * scale_width;
  cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, page_width, page_height);
  if (surface == NULL) {
    return false;
  }
  if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
    cairo_surface_destroy(surface);
    return false;
  }

  cairo_t* temp_cairo = cairo_create(surface);
  if (cairo == NULL) {
    cairo_surface_destroy(surface);
    return false;
  }

  /* Draw a white background. */
  cairo_save(temp_cairo);
  cairo_set_source_rgb(temp_cairo, 1, 1, 1);
  cairo_rectangle(temp_cairo, 0, 0, page_width, page_height);
  cairo_fill(temp_cairo);
  cairo_restore(temp_cairo);

  /* Render the page to the temporary surface */
  zathura_renderer_lock(zathura->sync.render_thread);
  const int err = zathura_page_render(page, temp_cairo, true);
  zathura_renderer_unlock(zathura->sync.render_thread);
  if (err != ZATHURA_ERROR_OK) {
    cairo_destroy(temp_cairo);
    cairo_surface_destroy(surface);
    return false;
  }

  /* Rescale the page and keep the aspect ratio */
  const gdouble scale = MIN(width / page_width, height / page_height);
  cairo_scale(cairo, scale, scale);

  /* Blit temporary surface to original cairo object. */
  cairo_set_source_surface(cairo, surface, 0.0, 0.0);
  cairo_paint(cairo);
  cairo_destroy(temp_cairo);
  cairo_surface_destroy(surface);

  return true;
}
Ejemplo n.º 5
0
void
cb_view_vadjustment_value_changed(GtkAdjustment* GIRARA_UNUSED(adjustment), gpointer data)
{
  zathura_t* zathura = data;
  if (zathura == NULL || zathura->document == NULL || zathura->ui.page_widget == NULL) {
    return;
  }

  GtkAdjustment* view_vadjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
  GtkAdjustment* view_hadjustment = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));

  GdkRectangle view_rect;
  /* get current adjustment values */
  view_rect.y      = 0;
  view_rect.height = gtk_adjustment_get_page_size(view_vadjustment);
  view_rect.x      = 0;
  view_rect.width  = gtk_adjustment_get_page_size(view_hadjustment);

  int page_padding = 1;
  girara_setting_get(zathura->ui.session, "page-padding", &page_padding);

  GdkRectangle center;
  center.x = (view_rect.width + 1) / 2;
  center.y = (view_rect.height + 1) / 2;
  center.height = center.width = (2 * page_padding) + 1;

  unsigned int number_of_pages = zathura_document_get_number_of_pages(zathura->document);
  double scale = zathura_document_get_scale(zathura->document);

  bool updated = false;
  /* find page that fits */
  for (unsigned int page_id = 0; page_id < number_of_pages; page_id++) {
    zathura_page_t* page = zathura_document_get_page(zathura->document, page_id);

    GdkRectangle page_rect;
    GtkWidget* page_widget = zathura_page_get_widget(zathura, page);
    gtk_widget_translate_coordinates(page_widget,
        zathura->ui.session->gtk.view, 0, 0, &page_rect.x, &page_rect.y);
    page_rect.width  = zathura_page_get_width(page)  * scale;
    page_rect.height = zathura_page_get_height(page) * scale;

    if (gdk_rectangle_intersect(&view_rect, &page_rect, NULL) == TRUE) {
      zathura_page_set_visibility(page, true);
      if (zathura->global.update_page_number == true && updated == false
          && gdk_rectangle_intersect(&center, &page_rect, NULL) == TRUE) {
        zathura_document_set_current_page_number(zathura->document, page_id);
        updated = true;
      }
    } else {
      zathura_page_set_visibility(page, false);
    }
    zathura_page_widget_update_view_time(ZATHURA_PAGE(page_widget));
  }

  statusbar_page_number_update(zathura);
}
Ejemplo n.º 6
0
Archivo: print.c Proyecto: pwmt/zathura
static void
cb_print_request_page_setup(GtkPrintOperation* UNUSED(print_operation),
                            GtkPrintContext* UNUSED(context), gint page_number,
                            GtkPageSetup* setup, zathura_t* zathura)
{
  if (zathura == NULL || zathura->document == NULL) {
    return;
  }

  zathura_page_t* page = zathura_document_get_page(zathura->document, page_number);
  double width  = zathura_page_get_width(page);
  double height = zathura_page_get_height(page);

  if (width > height) {
    gtk_page_setup_set_orientation(setup, GTK_PAGE_ORIENTATION_LANDSCAPE);
  } else {
    gtk_page_setup_set_orientation(setup, GTK_PAGE_ORIENTATION_PORTRAIT);
  }
}
Ejemplo n.º 7
0
bool
sc_scroll(girara_session_t* session, girara_argument_t* argument,
          girara_event_t* UNUSED(event), unsigned int UNUSED(t))
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
  if (zathura->document == NULL) {
    return false;
  }

  GtkAdjustment* adjustment = NULL;
  if ( (argument->n == LEFT) || (argument->n == FULL_LEFT) || (argument->n == HALF_LEFT) ||
       (argument->n == RIGHT) || (argument->n == FULL_RIGHT) || (argument->n == HALF_RIGHT)) {
    adjustment = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(session->gtk.view));
  } else {
    adjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(session->gtk.view));
  }

  gdouble view_size                  = gtk_adjustment_get_page_size(adjustment);
  gdouble value                      = gtk_adjustment_get_value(adjustment);
  gdouble max                        = gtk_adjustment_get_upper(adjustment) - view_size;
  zathura->global.update_page_number = true;

  float scroll_step = 40;
  girara_setting_get(session, "scroll-step", &scroll_step);
  float scroll_hstep = -1;
  girara_setting_get(session, "scroll-hstep", &scroll_hstep);
  if (scroll_hstep < 0) {
    scroll_hstep = scroll_step;
  }
  float scroll_full_overlap = 0.0;
  girara_setting_get(session, "scroll-full-overlap", &scroll_full_overlap);
  bool scroll_page_aware = false;
  girara_setting_get(session, "scroll-page-aware", &scroll_page_aware);

  bool scroll_wrap = false;
  girara_setting_get(session, "scroll-wrap", &scroll_wrap);

  int padding = 1;
  girara_setting_get(session, "page-padding", &padding);

  gdouble new_value;

  switch(argument->n) {
    case FULL_UP:
    case FULL_LEFT:
      new_value = value - (1.0 - scroll_full_overlap) * view_size - padding;
      break;
    case FULL_DOWN:
    case FULL_RIGHT:
      new_value = value + (1.0 - scroll_full_overlap) * view_size + padding;
      break;
    case HALF_UP:
    case HALF_LEFT:
      new_value = value - ((view_size + padding) / 2);
      break;
    case HALF_DOWN:
    case HALF_RIGHT:
      new_value = value + ((view_size + padding) / 2);
      break;
    case LEFT:
      new_value = value - scroll_hstep;
      break;
    case UP:
      new_value = value - scroll_step;
      break;
    case RIGHT:
      new_value = value + scroll_hstep;
      break;
    case DOWN:
      new_value = value + scroll_step;
      break;
    case TOP:
      new_value = 0;
      break;
    case BOTTOM:
      new_value = max;
      break;
    default:
      new_value = value;
  }

  if (scroll_wrap == true) {
    if (new_value < 0)
      new_value = max;
    else if (new_value > max)
      new_value = 0;
  }

  if (scroll_page_aware == true) {
    int page_offset;
    double page_size;

    {
      unsigned int page_id = zathura_document_get_current_page_number(zathura->document);
      zathura_page_t* page = zathura_document_get_page(zathura->document, page_id);
      page_offset_t offset;
      page_calculate_offset(zathura, page, &offset);

      double scale = zathura_document_get_scale(zathura->document);

      if ((argument->n == LEFT) || (argument->n == FULL_LEFT) || (argument->n == HALF_LEFT) ||
          (argument->n == RIGHT) || (argument->n == FULL_RIGHT) || (argument->n == HALF_RIGHT)) {
        page_offset = offset.x;
        page_size = zathura_page_get_width(page) * scale;
      } else {
        page_offset = offset.y;
        page_size = zathura_page_get_height(page) * scale;
      }

      page_offset -= padding / 2;
      page_size   += padding;
    }

    if ((argument->n == FULL_DOWN) || (argument->n == HALF_DOWN) ||
        (argument->n == FULL_RIGHT) || (argument->n == HALF_RIGHT)) {
      if ((page_offset > value) &&
          (page_offset < value + view_size)) {
        new_value = page_offset;
      } else if ((page_offset <= value) &&
                 (page_offset + page_size < value + view_size)) {
        new_value = page_offset + page_size + 1;
      } else if ((page_offset <= value) &&
                 (page_offset + page_size < new_value + view_size)) {
        new_value = page_offset + page_size - view_size + 1;
      }
    } else if ((argument->n == FULL_UP) || (argument->n == HALF_UP) ||
               (argument->n == FULL_LEFT) || (argument->n == HALF_LEFT)) {
      if ((page_offset + 1 >= value) &&
          (page_offset < value + view_size)) {
        new_value = page_offset - view_size;
      } else if ((page_offset <= value) &&
                 (page_offset + page_size + 1 < value + view_size)) {
        new_value = page_offset + page_size - view_size;
      } else if ((page_offset <= value) &&
                 (page_offset > new_value)) {
        new_value = page_offset;
      }
    }
  }

  set_adjustment(adjustment, new_value);

  return false;
}
Ejemplo n.º 8
0
zathura_document_t*
zathura_document_open(zathura_plugin_manager_t* plugin_manager, const char*
                      path, const char* password, zathura_error_t* error)
{
  if (path == NULL) {
    return NULL;
  }

  if (g_file_test(path, G_FILE_TEST_EXISTS) == FALSE) {
    girara_error("File '%s' does not exist", path);
    return NULL;
  }

  const gchar* content_type = guess_type(path);
  if (content_type == NULL) {
    girara_error("Could not determine file type.");
    return NULL;
  }

  /* determine real path */
  long path_max;
#ifdef PATH_MAX
  path_max = PATH_MAX;
#else
  path_max = pathconf(path,_PC_PATH_MAX);
  if (path_max <= 0)
    path_max = 4096;
#endif

  char* real_path              = NULL;
  zathura_document_t* document = NULL;

  real_path = malloc(sizeof(char) * path_max);
  if (real_path == NULL) {
    g_free((void*)content_type);
    return NULL;
  }

  if (realpath(path, real_path) == NULL) {
    g_free((void*)content_type);
    free(real_path);
    return NULL;
  }

  zathura_plugin_t* plugin = zathura_plugin_manager_get_plugin(plugin_manager, content_type);
  g_free((void*)content_type);

  if (plugin == NULL) {
    girara_error("unknown file type\n");
    *error = ZATHURA_ERROR_UNKNOWN;
    goto error_free;
  }

  document = g_malloc0(sizeof(zathura_document_t));

  document->file_path   = real_path;
  document->basename    = g_path_get_basename(real_path);
  document->password    = password;
  document->scale       = 1.0;
  document->plugin      = plugin;
  document->adjust_mode = ZATHURA_ADJUST_NONE;
  document->cell_width  = 0.0;
  document->cell_height = 0.0;
  document->view_height = 0;
  document->view_width  = 0;
  document->position_x  = 0.0;
  document->position_y  = 0.0;

  /* open document */
  zathura_plugin_functions_t* functions = zathura_plugin_get_functions(plugin);
  if (functions->document_open == NULL) {
    girara_error("plugin has no open function\n");
    goto error_free;
  }

  zathura_error_t int_error = functions->document_open(document);
  if (int_error != ZATHURA_ERROR_OK) {
    if (error != NULL) {
      *error = int_error;
    }

    girara_error("could not open document\n");
    goto error_free;
  }

  /* read all pages */
  document->pages = calloc(document->number_of_pages, sizeof(zathura_page_t*));
  if (document->pages == NULL) {
    goto error_free;
  }

  for (unsigned int page_id = 0; page_id < document->number_of_pages; page_id++) {
    zathura_page_t* page = zathura_page_new(document, page_id, NULL);
    if (page == NULL) {
      goto error_free;
    }

    document->pages[page_id] = page;

    /* cell_width and cell_height is the maximum of all the pages width and height */
    const double width = zathura_page_get_width(page);
    if (document->cell_width < width)
      document->cell_width = width;

    const double height = zathura_page_get_height(page);
    if (document->cell_height < height)
      document->cell_height = height;
  }

  return document;

error_free:

  free(real_path);

  if (document != NULL && document->pages != NULL) {
    for (unsigned int page_id = 0; page_id < document->number_of_pages; page_id++) {
      zathura_page_free(document->pages[page_id]);
    }

    free(document->pages);
  }

  g_free(document);
  return NULL;
}
Ejemplo n.º 9
0
zathura_image_buffer_t*
djvu_page_render(zathura_page_t* page, void* UNUSED(data), zathura_error_t* error)
{
  if (page == NULL) {
    if (error != NULL) {
      *error = ZATHURA_ERROR_INVALID_ARGUMENTS;
    }
    return NULL;
  }
 
  zathura_document_t* document = zathura_page_get_document(page);
  if (document == NULL) {
    return NULL;
  }

  /* calculate sizes */
  unsigned int page_width  = zathura_document_get_scale(document) * zathura_page_get_width(page);
  unsigned int page_height = zathura_document_get_scale(document) * zathura_page_get_height(page);

  if (page_width == 0 || page_height == 0) {
    if (error != NULL) {
      *error = ZATHURA_ERROR_UNKNOWN;
    }
    goto error_out;
  }

  /* init ddjvu render data */
  djvu_document_t* djvu_document = zathura_document_get_data(document);
  ddjvu_page_t* djvu_page        = ddjvu_page_create_by_pageno(
      djvu_document->document, zathura_page_get_index(page));

  if (djvu_page == NULL) {
    if (error != NULL) {
      *error = ZATHURA_ERROR_UNKNOWN;
    }
    goto error_out;
  }

  while (!ddjvu_page_decoding_done(djvu_page)) {
    handle_messages(djvu_document, true);
  }

  ddjvu_rect_t rrect = { 0, 0, page_width, page_height };
  ddjvu_rect_t prect = { 0, 0, page_width, page_height };

  zathura_image_buffer_t* image_buffer =
    zathura_image_buffer_create(page_width, page_height);

  if (image_buffer == NULL) {
    if (error != NULL) {
      *error = ZATHURA_ERROR_OUT_OF_MEMORY;
    }
    goto error_free;
  }

  /* set rotation */
  ddjvu_page_set_rotation(djvu_page, DDJVU_ROTATE_0);

  /* render page */
  ddjvu_page_render(djvu_page, DDJVU_RENDER_COLOR, &prect, &rrect,
      djvu_document->format, 3 * page_width, (char*) image_buffer->data);

  return image_buffer;

error_free:

    ddjvu_page_release(djvu_page);
    zathura_image_buffer_free(image_buffer);

error_out:

  return NULL;
}
Ejemplo n.º 10
0
girara_list_t*
djvu_page_links_get(zathura_page_t* page, void* UNUSED(data), zathura_error_t* error)
{
  if (page == NULL) {
    if (error != NULL) {
      *error = ZATHURA_ERROR_INVALID_ARGUMENTS;
    }
    goto error_ret;
  }

  zathura_document_t* document = zathura_page_get_document(page);
  if (document == NULL) {
    goto error_ret;
  }

  girara_list_t* list = girara_list_new2((girara_free_function_t) zathura_link_free);
  if (list == NULL) {
    if (error != NULL) {
      *error = ZATHURA_ERROR_OUT_OF_MEMORY;
    }
    goto error_ret;
  }

  djvu_document_t* djvu_document = zathura_document_get_data(document);

  miniexp_t annotations = miniexp_nil;
  while ((annotations = ddjvu_document_get_pageanno(djvu_document->document,
          zathura_page_get_index(page))) == miniexp_dummy) {
    handle_messages(djvu_document, true);
  }

  if (annotations == miniexp_nil) {
    goto error_free;
  }

  miniexp_t* hyperlinks = ddjvu_anno_get_hyperlinks(annotations);
  for (miniexp_t* iter = hyperlinks; *iter != NULL; iter++) {
    if (miniexp_car(*iter) != miniexp_symbol("maparea")) {
      continue;
    }

    miniexp_t inner = miniexp_cdr(*iter);

    /* extract url information */
    const char* target_string = NULL;

    if (miniexp_caar(inner) == miniexp_symbol("url")) {
      if (exp_to_str(miniexp_caddr(miniexp_car(inner)), &target_string) == false) {
        continue;
      }
    } else {
      if (exp_to_str(miniexp_car(inner), &target_string) == false) {
        continue;
      }
    }

    /* skip comment */
    inner = miniexp_cdr(inner);

    /* extract link area */
    inner = miniexp_cdr(inner);

    zathura_rectangle_t rect = { 0, 0, 0, 0 };
    if (exp_to_rect(miniexp_car(inner), &rect) == false) {
      continue;
    }

    /* update rect */
    unsigned int page_height = zathura_page_get_height(page) / ZATHURA_DJVU_SCALE;
    rect.x1 = rect.x1 * ZATHURA_DJVU_SCALE;
    rect.x2 = rect.x2 * ZATHURA_DJVU_SCALE;
    double tmp = rect.y1;
    rect.y1 = (page_height - rect.y2) * ZATHURA_DJVU_SCALE;
    rect.y2 = (page_height - tmp)     * ZATHURA_DJVU_SCALE;

    /* create zathura link */
    zathura_link_type_t type = ZATHURA_LINK_INVALID;
    zathura_link_target_t target = { ZATHURA_LINK_DESTINATION_UNKNOWN, NULL, 0, -1, -1, -1, -1, 0 };;

    /* goto page */
    if (target_string[0] == '#' && target_string[1] == 'p') {
      type = ZATHURA_LINK_GOTO_DEST;
      target.page_number = atoi(target_string + 2) - 1;
    /* url or other? */
    } else if (strstr(target_string, "//") != NULL) {
      type = ZATHURA_LINK_URI;
      target.value = (char*) target_string;
    /* TODO: Parse all different links */
    } else {
      continue;
    }

    zathura_link_t* link = zathura_link_new(type, rect, target);
    if (link != NULL) {
      girara_list_append(list, link);
    }
  }

  return list;

error_free:

  if (list != NULL) {
    girara_list_free(list);
  }

error_ret:

  return NULL;
}
Ejemplo n.º 11
0
char*
djvu_page_get_text(zathura_page_t* page, void* UNUSED(data), zathura_rectangle_t
    rectangle, zathura_error_t* error)
{
  if (page == NULL) {
    if (error != NULL) {
      *error = ZATHURA_ERROR_INVALID_ARGUMENTS;
    }
    goto error_ret;
  }

  zathura_document_t* document = zathura_page_get_document(page);
  if (document == NULL) {
    goto error_ret;
  }

  djvu_document_t* djvu_document = zathura_document_get_data(document);

  djvu_page_text_t* page_text = djvu_page_text_new(djvu_document, page);
  if (page_text == NULL) {
    goto error_ret;
  }

  double tmp = 0;
  double page_height = zathura_page_get_height(page);
  double page_width  = zathura_page_get_width(page);

  switch (zathura_document_get_rotation(document)) {
    case 90:
      tmp = rectangle.x1;
      rectangle.x1 = rectangle.y1;
      rectangle.y1 = tmp;
      tmp = rectangle.x2;
      rectangle.x2 = rectangle.y2;
      rectangle.y2 = tmp;
      break;
    case 180:
      tmp = rectangle.x1;
      rectangle.x1 = (page_width  - rectangle.x2);
      rectangle.x2 = (page_width  - tmp);
      break;
    case 270:
      tmp = rectangle.y2;
      rectangle.y2 = (page_height - rectangle.x1);
      rectangle.x1 = (page_width  - tmp);
      tmp = rectangle.y1;
      rectangle.y1 = (page_height - rectangle.x2);
      rectangle.x2 = (page_width  - tmp);
      break;
    default:
      tmp = rectangle.y1;
      rectangle.y1 = (page_height - rectangle.y2);
      rectangle.y2 = (page_height - tmp);
      break;
  }

  /* adjust to scale */
  rectangle.x1 /= ZATHURA_DJVU_SCALE;
  rectangle.x2 /= ZATHURA_DJVU_SCALE;
  rectangle.y1 /= ZATHURA_DJVU_SCALE;
  rectangle.y2 /= ZATHURA_DJVU_SCALE;

  char* text = djvu_page_text_select(page_text, rectangle);

  djvu_page_text_free(page_text);

  return text;

error_ret:

  if (error != NULL && *error == ZATHURA_ERROR_OK) {
    *error = ZATHURA_ERROR_UNKNOWN;
  }

  return NULL;
}
Ejemplo n.º 12
0
static void
cb_print_draw_page(GtkPrintOperation* print_operation, GtkPrintContext*
                   context, gint page_number, zathura_t* zathura)
{
  if (context == NULL || zathura == NULL || zathura->document == NULL ||
      zathura->ui.session == NULL || zathura->ui.statusbar.file == NULL) {
    gtk_print_operation_cancel(print_operation);
    return;
  }

  /* Update statusbar. */
  char* tmp = g_strdup_printf("Printing %d...", page_number);
  girara_statusbar_item_set_text(zathura->ui.session,
                                 zathura->ui.statusbar.file, tmp);
  g_free(tmp);

  /* Get the page and cairo handle.  */
  cairo_t* cairo       = gtk_print_context_get_cairo_context(context);
  zathura_page_t* page = zathura_document_get_page(zathura->document, page_number);
  if (cairo == NULL || page == NULL) {
    gtk_print_operation_cancel(print_operation);
    return;
  }

  /* Try to render the page without a temporary surface. This only works with
   * plugins that support rendering to any surface.  */
  girara_debug("printing page %d ...", page_number);
  zathura_renderer_lock(zathura->sync.render_thread);
  int err = zathura_page_render(page, cairo, true);
  zathura_renderer_unlock(zathura->sync.render_thread);
  if (err == ZATHURA_ERROR_OK) {
    return;
  }

  /* Try to render the page on a temporary image surface. */
  const gdouble width = gtk_print_context_get_width(context);
  const gdouble height = gtk_print_context_get_height(context);

  /* Render to a surface that is 5 times larger to workaround quality issues. */
  const double page_height = zathura_page_get_height(page) * 5;
  const double page_width  = zathura_page_get_width(page) * 5;
  cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, page_width, page_height);
  if (surface == NULL) {
    gtk_print_operation_cancel(print_operation);
    return;
  }

  cairo_t* temp_cairo = cairo_create(surface);
  if (cairo == NULL) {
    gtk_print_operation_cancel(print_operation);
    cairo_surface_destroy(surface);
    return;
  }

  /* Draw a white background. */
  cairo_save(temp_cairo);
  cairo_set_source_rgb(temp_cairo, 1, 1, 1);
  cairo_rectangle(temp_cairo, 0, 0, page_width, page_height);
  cairo_fill(temp_cairo);
  cairo_restore(temp_cairo);

  /* Render the page to the temporary surface */
  girara_debug("printing page %d (fallback) ...", page_number);
  zathura_renderer_lock(zathura->sync.render_thread);
  err = zathura_page_render(page, temp_cairo, true);
  zathura_renderer_unlock(zathura->sync.render_thread);
  if (err != ZATHURA_ERROR_OK) {
    cairo_destroy(temp_cairo);
    cairo_surface_destroy(surface);
    gtk_print_operation_cancel(print_operation);
    return;
  }

  /* Rescale the page and keep the aspect ratio */
  const gdouble scale = MIN(width / page_width, height / page_height);
  cairo_scale(cairo, scale, scale);

  /* Blit temporary surface to original cairo object. */
  cairo_set_source_surface(cairo, surface, 0.0, 0.0);
  cairo_paint(cairo);
  cairo_destroy(temp_cairo);
  cairo_surface_destroy(surface);
}