summaryrefslogtreecommitdiffstats
path: root/plug-ins/print/print-preview.c
diff options
context:
space:
mode:
Diffstat (limited to 'plug-ins/print/print-preview.c')
-rw-r--r--plug-ins/print/print-preview.c880
1 files changed, 880 insertions, 0 deletions
diff --git a/plug-ins/print/print-preview.c b/plug-ins/print/print-preview.c
new file mode 100644
index 0000000..366a4c4
--- /dev/null
+++ b/plug-ins/print/print-preview.c
@@ -0,0 +1,880 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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 3 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "print-preview.h"
+
+
+enum
+{
+ OFFSETS_CHANGED,
+ LAST_SIGNAL
+};
+
+
+#define SIZE_REQUEST 200
+
+
+struct _PrintPreview
+{
+ GtkEventBox parent_instance;
+
+ GdkCursor *cursor;
+
+ GtkPageSetup *page;
+ cairo_surface_t *thumbnail;
+ gboolean dragging;
+ gboolean inside;
+
+ gint32 drawable_id;
+
+ gdouble image_offset_x;
+ gdouble image_offset_y;
+ gdouble image_offset_x_max;
+ gdouble image_offset_y_max;
+ gdouble image_width;
+ gdouble image_height;
+
+ gboolean use_full_page;
+
+ /* for mouse drags */
+ gdouble orig_offset_x;
+ gdouble orig_offset_y;
+ gint start_x;
+ gint start_y;
+};
+
+struct _PrintPreviewClass
+{
+ GtkEventBoxClass parent_class;
+
+ void (* offsets_changed) (PrintPreview *print_preview,
+ gint offset_x,
+ gint offset_y);
+};
+
+
+static void print_preview_finalize (GObject *object);
+
+static void print_preview_realize (GtkWidget *widget);
+static void print_preview_unrealize (GtkWidget *widget);
+static void print_preview_size_request (GtkWidget *widget,
+ GtkRequisition *requisition);
+static void print_preview_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static gboolean print_preview_expose_event (GtkWidget *widget,
+ GdkEventExpose *event);
+static gboolean print_preview_button_press_event (GtkWidget *widget,
+ GdkEventButton *event);
+static gboolean print_preview_button_release_event (GtkWidget *widget,
+ GdkEventButton *event);
+static gboolean print_preview_motion_notify_event (GtkWidget *widget,
+ GdkEventMotion *event);
+static gboolean print_preview_leave_notify_event (GtkWidget *widget,
+ GdkEventCrossing *event);
+
+static gboolean print_preview_is_inside (PrintPreview *preview,
+ gdouble x,
+ gdouble y);
+static void print_preview_set_inside (PrintPreview *preview,
+ gboolean inside);
+
+static gdouble print_preview_get_scale (PrintPreview *preview);
+
+static void print_preview_get_page_size (PrintPreview *preview,
+ gdouble *paper_width,
+ gdouble *paper_height);
+static void print_preview_get_page_margins (PrintPreview *preview,
+ gdouble *left_margin,
+ gdouble *right_margin,
+ gdouble *top_margin,
+ gdouble *bottom_margin);
+static cairo_surface_t * print_preview_get_thumbnail (gint32 drawable_id,
+ gint width,
+ gint height);
+
+
+G_DEFINE_TYPE (PrintPreview, print_preview, GTK_TYPE_EVENT_BOX)
+
+#define parent_class print_preview_parent_class
+
+static guint print_preview_signals[LAST_SIGNAL] = { 0 };
+
+
+#define g_marshal_value_peek_double(v) (v)->data[0].v_double
+
+static void
+marshal_VOID__DOUBLE_DOUBLE (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__DOUBLE_DOUBLE) (gpointer data1,
+ gdouble arg_1,
+ gdouble arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__DOUBLE_DOUBLE callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+
+ callback = (GMarshalFunc_VOID__DOUBLE_DOUBLE) (marshal_data ?
+ marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_double (param_values + 1),
+ g_marshal_value_peek_double (param_values + 2),
+ data2);
+}
+
+static void
+print_preview_class_init (PrintPreviewClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ print_preview_signals[OFFSETS_CHANGED] =
+ g_signal_new ("offsets-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (PrintPreviewClass, offsets_changed),
+ NULL, NULL,
+ marshal_VOID__DOUBLE_DOUBLE,
+ G_TYPE_NONE, 2,
+ G_TYPE_DOUBLE,
+ G_TYPE_DOUBLE);
+
+ object_class->finalize = print_preview_finalize;
+
+ widget_class->realize = print_preview_realize;
+ widget_class->unrealize = print_preview_unrealize;
+ widget_class->size_request = print_preview_size_request;
+ widget_class->size_allocate = print_preview_size_allocate;
+ widget_class->expose_event = print_preview_expose_event;
+ widget_class->button_press_event = print_preview_button_press_event;
+ widget_class->button_release_event = print_preview_button_release_event;
+ widget_class->motion_notify_event = print_preview_motion_notify_event;
+ widget_class->leave_notify_event = print_preview_leave_notify_event;
+
+ klass->offsets_changed = NULL;
+}
+
+static void
+print_preview_init (PrintPreview *preview)
+{
+ gtk_event_box_set_visible_window (GTK_EVENT_BOX (preview), FALSE);
+
+ gtk_widget_add_events (GTK_WIDGET (preview),
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK);
+}
+
+
+static void
+print_preview_finalize (GObject *object)
+{
+ PrintPreview *preview = PRINT_PREVIEW (object);
+
+ if (preview->thumbnail)
+ {
+ cairo_surface_destroy (preview->thumbnail);
+ preview->thumbnail = NULL;
+ }
+
+ if (preview->page)
+ {
+ g_object_unref (preview->page);
+ preview->page = NULL;
+ }
+
+ G_OBJECT_CLASS (print_preview_parent_class)->finalize (object);
+}
+
+static void
+print_preview_realize (GtkWidget *widget)
+{
+ PrintPreview *preview = PRINT_PREVIEW (widget);
+
+ GTK_WIDGET_CLASS (print_preview_parent_class)->realize (widget);
+
+ preview->cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
+ GDK_HAND1);
+}
+
+static void
+print_preview_unrealize (GtkWidget *widget)
+{
+ PrintPreview *preview = PRINT_PREVIEW (widget);
+
+ if (preview->cursor)
+ gdk_cursor_unref (preview->cursor);
+
+ GTK_WIDGET_CLASS (print_preview_parent_class)->unrealize (widget);
+}
+
+static void
+print_preview_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ PrintPreview *preview = PRINT_PREVIEW (widget);
+ gdouble paper_width;
+ gdouble paper_height;
+ gint border;
+
+ border = gtk_container_get_border_width (GTK_CONTAINER (widget)) + 1;
+
+ print_preview_get_page_size (preview, &paper_width, &paper_height);
+
+ if (paper_width > paper_height)
+ {
+ requisition->height = SIZE_REQUEST;
+ requisition->width = paper_width * SIZE_REQUEST / paper_height;
+ requisition->width = MIN (requisition->width, 2 * SIZE_REQUEST);
+ }
+ else
+ {
+ requisition->width = SIZE_REQUEST;
+ requisition->height = paper_height * SIZE_REQUEST / paper_width;
+ requisition->height = MIN (requisition->height, 2 * SIZE_REQUEST);
+ }
+
+ requisition->width += 2 * border;
+ requisition->height += 2 * border;
+}
+
+static void
+print_preview_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ PrintPreview *preview = PRINT_PREVIEW (widget);
+
+ GTK_WIDGET_CLASS (print_preview_parent_class)->size_allocate (widget,
+ allocation);
+
+ if (preview->thumbnail)
+ {
+ cairo_surface_destroy (preview->thumbnail);
+ preview->thumbnail = NULL;
+ }
+}
+
+static gboolean
+print_preview_button_press_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ PrintPreview *preview = PRINT_PREVIEW (widget);
+
+ if (event->type == GDK_BUTTON_PRESS && event->button == 1 && preview->inside)
+ {
+ GdkCursor *cursor;
+
+ cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
+ GDK_FLEUR);
+
+ if (gdk_pointer_grab (event->window, FALSE,
+ (GDK_BUTTON1_MOTION_MASK |
+ GDK_BUTTON_RELEASE_MASK),
+ NULL, cursor, event->time) == GDK_GRAB_SUCCESS)
+ {
+ preview->orig_offset_x = preview->image_offset_x;
+ preview->orig_offset_y = preview->image_offset_y;
+
+ preview->start_x = event->x;
+ preview->start_y = event->y;
+
+ preview->dragging = TRUE;
+ }
+
+ gdk_cursor_unref (cursor);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+print_preview_button_release_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ PrintPreview *preview = PRINT_PREVIEW (widget);
+
+ if (preview->dragging)
+ {
+ gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
+ event->time);
+ preview->dragging = FALSE;
+
+ print_preview_set_inside (preview,
+ print_preview_is_inside (preview,
+ event->x, event->y));
+ }
+
+ return FALSE;
+}
+
+static gboolean
+print_preview_motion_notify_event (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ PrintPreview *preview = PRINT_PREVIEW (widget);
+
+ if (preview->dragging)
+ {
+ gdouble scale = print_preview_get_scale (preview);
+ gdouble offset_x;
+ gdouble offset_y;
+
+ offset_x = (preview->orig_offset_x +
+ (event->x - preview->start_x) / scale);
+ offset_y = (preview->orig_offset_y +
+ (event->y - preview->start_y) / scale);
+
+ offset_x = CLAMP (offset_x, 0, preview->image_offset_x_max);
+ offset_y = CLAMP (offset_y, 0, preview->image_offset_y_max);
+
+ if (preview->image_offset_x != offset_x ||
+ preview->image_offset_y != offset_y)
+ {
+ print_preview_set_image_offsets (preview, offset_x, offset_y);
+
+ g_signal_emit (preview,
+ print_preview_signals[OFFSETS_CHANGED], 0,
+ preview->image_offset_x,
+ preview->image_offset_y);
+ }
+ }
+ else
+ {
+ print_preview_set_inside (preview,
+ print_preview_is_inside (preview,
+ event->x, event->y));
+ }
+
+ return FALSE;
+}
+
+static gboolean
+print_preview_leave_notify_event (GtkWidget *widget,
+ GdkEventCrossing *event)
+{
+ PrintPreview *preview = PRINT_PREVIEW (widget);
+
+ if (event->mode == GDK_CROSSING_NORMAL)
+ print_preview_set_inside (preview, FALSE);
+
+ return FALSE;
+}
+
+static gboolean
+print_preview_expose_event (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ PrintPreview *preview = PRINT_PREVIEW (widget);
+ GtkStyle *style = gtk_widget_get_style (widget);
+ GtkAllocation allocation;
+ cairo_t *cr;
+ gdouble paper_width;
+ gdouble paper_height;
+ gdouble left_margin;
+ gdouble right_margin;
+ gdouble top_margin;
+ gdouble bottom_margin;
+ gdouble scale;
+ gint border;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ border = gtk_container_get_border_width (GTK_CONTAINER (widget)) + 1;
+
+ print_preview_get_page_size (preview, &paper_width, &paper_height);
+ print_preview_get_page_margins (preview,
+ &left_margin, &right_margin,
+ &top_margin, &bottom_margin);
+
+ scale = print_preview_get_scale (preview);
+
+ cr = gdk_cairo_create (gtk_widget_get_window (widget));
+
+ cairo_translate (cr,
+ allocation.x + border,
+ allocation.y + border);
+
+ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+ {
+ gint width = allocation.width - 2 * border;
+
+ cairo_translate (cr, width - scale * paper_width, 0);
+ }
+
+ cairo_set_line_width (cr, 1.0);
+
+ /* draw page background */
+ cairo_rectangle (cr, 0, 0, scale * paper_width, scale * paper_height);
+
+ gdk_cairo_set_source_color (cr, &style->black);
+ cairo_stroke_preserve (cr);
+
+ gdk_cairo_set_source_color (cr, &style->white);
+ cairo_fill (cr);
+
+ /* draw page_margins */
+ cairo_rectangle (cr,
+ scale * left_margin,
+ scale * top_margin,
+ scale * (paper_width - left_margin - right_margin),
+ scale * (paper_height - top_margin - bottom_margin));
+
+ gdk_cairo_set_source_color (cr, &style->mid[gtk_widget_get_state (widget)]);
+ cairo_stroke (cr);
+
+ cairo_translate (cr,
+ scale * (left_margin + preview->image_offset_x),
+ scale * (top_margin + preview->image_offset_y));
+
+ if (preview->dragging || preview->inside)
+ {
+ cairo_rectangle (cr,
+ 0, 0,
+ scale * preview->image_width,
+ scale * preview->image_height);
+
+ gdk_cairo_set_source_color (cr, &style->black);
+ cairo_stroke (cr);
+ }
+
+ if (preview->thumbnail == NULL &&
+ gimp_item_is_valid (preview->drawable_id))
+ {
+ preview->thumbnail =
+ print_preview_get_thumbnail (preview->drawable_id,
+ MIN (allocation.width, 1024),
+ MIN (allocation.height, 1024));
+ }
+
+ if (preview->thumbnail != NULL)
+ {
+ gdouble scale_x;
+ gdouble scale_y;
+
+ scale_x = (preview->image_width /
+ cairo_image_surface_get_width (preview->thumbnail));
+ scale_y = (preview->image_height /
+ cairo_image_surface_get_height (preview->thumbnail));
+
+ cairo_rectangle (cr, 0, 0, preview->image_width, preview->image_height);
+
+ cairo_scale (cr, scale_x * scale, scale_y * scale);
+
+ cairo_set_source_surface (cr, preview->thumbnail, 0, 0);
+ cairo_fill (cr);
+ }
+
+ cairo_destroy (cr);
+
+ return FALSE;
+}
+
+/**
+ * print_preview_new:
+ * @page: page setup
+ * @drawable_id: the drawable to print
+ *
+ * Creates a new #PrintPreview widget.
+ *
+ * Return value: the new #PrintPreview widget.
+ **/
+GtkWidget *
+print_preview_new (GtkPageSetup *page,
+ gint32 drawable_id)
+{
+ PrintPreview *preview;
+
+ g_return_val_if_fail (GTK_IS_PAGE_SETUP (page), NULL);
+
+ preview = g_object_new (PRINT_TYPE_PREVIEW, NULL);
+
+ preview->drawable_id = drawable_id;
+
+ print_preview_set_page_setup (preview, page);
+
+ return GTK_WIDGET (preview);
+}
+
+/**
+ * print_preview_set_image_dpi:
+ * @preview: a #PrintPreview.
+ * @xres: the X resolution
+ * @yres: the Y resolution
+ *
+ * Sets the resolution of the image/drawable displayed by the
+ * #PrintPreview.
+ **/
+void
+print_preview_set_image_dpi (PrintPreview *preview,
+ gdouble xres,
+ gdouble yres)
+{
+ gdouble width;
+ gdouble height;
+
+ g_return_if_fail (PRINT_IS_PREVIEW (preview));
+ g_return_if_fail (xres > 0.0 && yres > 0.0);
+
+ width = gimp_drawable_width (preview->drawable_id) * 72.0 / xres;
+ height = gimp_drawable_height (preview->drawable_id) * 72.0 / yres;
+
+ if (width != preview->image_width || height != preview->image_height)
+ {
+ preview->image_width = width;
+ preview->image_height = height;
+
+ gtk_widget_queue_draw (GTK_WIDGET (preview));
+ }
+}
+
+/**
+ * print_preview_set_page_setup:
+ * @preview: a #PrintPreview.
+ * @page: the page setup to use
+ *
+ * Sets the page setup to use by the #PrintPreview.
+ **/
+void
+print_preview_set_page_setup (PrintPreview *preview,
+ GtkPageSetup *page)
+{
+ g_return_if_fail (PRINT_IS_PREVIEW (preview));
+ g_return_if_fail (GTK_IS_PAGE_SETUP (page));
+
+ if (preview->page)
+ g_object_unref (preview->page);
+
+ preview->page = gtk_page_setup_copy (page);
+
+ gtk_widget_queue_resize (GTK_WIDGET (preview));
+}
+
+/**
+ * print_preview_set_image_offsets:
+ * @preview: a #PrintPreview.
+ * @offset_x: the X offset
+ * @offset_y: the Y offset
+ *
+ * Sets the offsets of the image/drawable displayed by the #PrintPreview.
+ * It does not emit the "offsets-changed" signal.
+ **/
+void
+print_preview_set_image_offsets (PrintPreview *preview,
+ gdouble offset_x,
+ gdouble offset_y)
+{
+ g_return_if_fail (PRINT_IS_PREVIEW (preview));
+
+ preview->image_offset_x = offset_x;
+ preview->image_offset_y = offset_y;
+
+ gtk_widget_queue_draw (GTK_WIDGET (preview));
+}
+
+/**
+ * print_preview_set_image_offsets_max:
+ * @preview: a #PrintPreview.
+ * @offset_x_max: the maximum X offset allowed
+ * @offset_y_max: the maximum Y offset allowed
+ *
+ * Sets the maximum offsets of the image/drawable displayed by the
+ * #PrintPreview. It does not emit the "offsets-changed" signal.
+ **/
+void
+print_preview_set_image_offsets_max (PrintPreview *preview,
+ gdouble offset_x_max,
+ gdouble offset_y_max)
+{
+ g_return_if_fail (PRINT_IS_PREVIEW (preview));
+
+ preview->image_offset_x_max = offset_x_max;
+ preview->image_offset_y_max = offset_y_max;
+
+ gtk_widget_queue_draw (GTK_WIDGET (preview));
+}
+
+/**
+ * print_preview_set_use_full_page:
+ * @preview: a #PrintPreview.
+ * @full_page: TRUE to ignore the page margins
+ *
+ * If @full_page is TRUE, the page margins are ignored and the full page
+ * can be used to setup printing.
+ **/
+void
+print_preview_set_use_full_page (PrintPreview *preview,
+ gboolean full_page)
+{
+ g_return_if_fail (PRINT_IS_PREVIEW (preview));
+
+ preview->use_full_page = full_page;
+
+ gtk_widget_queue_draw (GTK_WIDGET (preview));
+}
+
+static gboolean
+print_preview_is_inside (PrintPreview *preview,
+ gdouble x,
+ gdouble y)
+{
+ GtkWidget *widget = GTK_WIDGET (preview);
+ GtkAllocation allocation;
+ gdouble left_margin;
+ gdouble right_margin;
+ gdouble top_margin;
+ gdouble bottom_margin;
+ gdouble scale;
+ gint border;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ border = gtk_container_get_border_width (GTK_CONTAINER (widget)) + 1;
+
+ x -= border;
+
+ scale = print_preview_get_scale (preview);
+
+ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+ {
+ gdouble paper_width;
+ gdouble paper_height;
+ gint width = allocation.width - 2 * border;
+
+ print_preview_get_page_size (preview, &paper_width, &paper_height);
+
+ x -= width - scale * paper_width;
+ }
+
+ print_preview_get_page_margins (preview,
+ &left_margin, &right_margin,
+ &top_margin, &bottom_margin);
+
+ x = x / scale - left_margin;
+ y = y / scale - top_margin;
+
+ return (x > preview->image_offset_x &&
+ x < preview->image_offset_x + preview->image_width &&
+ y > preview->image_offset_y &&
+ y < preview->image_offset_y + preview->image_height);
+}
+
+static void
+print_preview_set_inside (PrintPreview *preview,
+ gboolean inside)
+{
+ if (inside != preview->inside)
+ {
+ GtkWidget *widget = GTK_WIDGET (preview);
+
+ preview->inside = inside;
+
+ if (gtk_widget_is_drawable (widget))
+ gdk_window_set_cursor (gtk_widget_get_window (widget),
+ inside ? preview->cursor : NULL);
+
+ gtk_widget_queue_draw (widget);
+ }
+}
+
+static gdouble
+print_preview_get_scale (PrintPreview *preview)
+{
+ GtkWidget *widget = GTK_WIDGET (preview);
+ GtkAllocation allocation;
+ gdouble paper_width;
+ gdouble paper_height;
+ gdouble scale_x;
+ gdouble scale_y;
+ gint border;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ border = gtk_container_get_border_width (GTK_CONTAINER (widget)) + 1;
+
+ print_preview_get_page_size (preview, &paper_width, &paper_height);
+
+ scale_x = (gdouble) (allocation.width - 2 * border) / paper_width;
+ scale_y = (gdouble) (allocation.height - 2 * border) / paper_height;
+
+ return MIN (scale_x, scale_y);
+}
+
+static void
+print_preview_get_page_size (PrintPreview *preview,
+ gdouble *paper_width,
+ gdouble *paper_height)
+{
+ *paper_width = gtk_page_setup_get_paper_width (preview->page,
+ GTK_UNIT_POINTS);
+ *paper_height = gtk_page_setup_get_paper_height (preview->page,
+ GTK_UNIT_POINTS);
+}
+
+static void
+print_preview_get_page_margins (PrintPreview *preview,
+ gdouble *left_margin,
+ gdouble *right_margin,
+ gdouble *top_margin,
+ gdouble *bottom_margin)
+{
+ if (preview->use_full_page)
+ {
+ *left_margin = 0.0;
+ *right_margin = 0.0;
+ *top_margin = 0.0;
+ *bottom_margin = 0.0;
+ }
+ else
+ {
+ *left_margin = gtk_page_setup_get_left_margin (preview->page,
+ GTK_UNIT_POINTS);
+ *right_margin = gtk_page_setup_get_right_margin (preview->page,
+ GTK_UNIT_POINTS);
+ *top_margin = gtk_page_setup_get_top_margin (preview->page,
+ GTK_UNIT_POINTS);
+ *bottom_margin = gtk_page_setup_get_bottom_margin (preview->page,
+ GTK_UNIT_POINTS);
+ }
+}
+
+
+/* This thumbnail code should eventually end up in libgimpui. */
+
+static cairo_surface_t *
+print_preview_get_thumbnail (gint32 drawable_id,
+ gint width,
+ gint height)
+{
+ cairo_surface_t *surface;
+ cairo_format_t format;
+ guchar *data;
+ guchar *dest;
+ const guchar *src;
+ gint src_stride;
+ gint dest_stride;
+ gint y;
+ gint bpp;
+
+ g_return_val_if_fail (width > 0 && width <= 1024, NULL);
+ g_return_val_if_fail (height > 0 && height <= 1024, NULL);
+
+ data = gimp_drawable_get_thumbnail_data (drawable_id,
+ &width, &height, &bpp);
+
+ switch (bpp)
+ {
+ case 1:
+ case 3:
+ format = CAIRO_FORMAT_RGB24;
+ break;
+
+ case 2:
+ case 4:
+ format = CAIRO_FORMAT_ARGB32;
+ break;
+
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ surface = cairo_image_surface_create (format, width, height);
+
+ src = data;
+ src_stride = width * bpp;
+
+ dest = cairo_image_surface_get_data (surface);
+ dest_stride = cairo_image_surface_get_stride (surface);
+
+ for (y = 0; y < height; y++)
+ {
+ const guchar *s = src;
+ guchar *d = dest;
+ gint w = width;
+
+ switch (bpp)
+ {
+ case 1:
+ while (w--)
+ {
+ GIMP_CAIRO_RGB24_SET_PIXEL (d, s[0], s[0], s[0]);
+ s += 1;
+ d += 4;
+ }
+ break;
+
+ case 2:
+ while (w--)
+ {
+ GIMP_CAIRO_ARGB32_SET_PIXEL (d, s[0], s[0], s[0], s[1]);
+ s += 2;
+ d += 4;
+ }
+ break;
+
+ case 3:
+ while (w--)
+ {
+ GIMP_CAIRO_RGB24_SET_PIXEL (d, s[0], s[1], s[2]);
+ s += 3;
+ d += 4;
+ }
+ break;
+
+ case 4:
+ while (w--)
+ {
+ GIMP_CAIRO_ARGB32_SET_PIXEL (d, s[0], s[1], s[2], s[3]);
+ s += 4;
+ d += 4;
+ }
+ break;
+ }
+
+ src += src_stride;
+ dest += dest_stride;
+ }
+
+ g_free (data);
+
+ cairo_surface_mark_dirty (surface);
+
+ return surface;
+}