diff options
Diffstat (limited to 'src/page.c')
-rw-r--r-- | src/page.c | 951 |
1 files changed, 951 insertions, 0 deletions
diff --git a/src/page.c b/src/page.c new file mode 100644 index 0000000..5888a46 --- /dev/null +++ b/src/page.c @@ -0,0 +1,951 @@ +/* + * Copyright (C) 2009 Canonical Ltd. + * Author: Robert Ancell <robert.ancell@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. See http://www.gnu.org/copyleft/gpl.html the full text of the + * license. + */ + +#include <string.h> +#include "page.h" + + +enum { + IMAGE_CHANGED, + SIZE_CHANGED, + SCAN_LINE_CHANGED, + ORIENTATION_CHANGED, + CROP_CHANGED, + LAST_SIGNAL +}; +static guint signals[LAST_SIGNAL] = { 0, }; + +struct PagePrivate +{ + /* Resolution of page */ + gint dpi; + + /* Number of rows in this page or -1 if currently unknown */ + gint rows; + + /* Color profile */ + gchar *color_profile; + + /* Scanned image data */ + GdkPixbuf *image; + + /* Page is getting data */ + gboolean scanning; + + /* TRUE if have some page data */ + gboolean has_data; + + /* Expected next scan row */ + gint scan_line; + + /* Rotation of scanned data */ + Orientation orientation; + + /* Crop */ + gboolean has_crop; + gchar *crop_name; + gint crop_x, crop_y, crop_width, crop_height; +}; + +G_DEFINE_TYPE (Page, page, G_TYPE_OBJECT); + + +Page * +page_new () +{ + return g_object_new (PAGE_TYPE, NULL); +} + + +void +page_setup (Page *page, gint width, gint height, gint dpi, Orientation orientation) +{ + page->priv->orientation = orientation; + page->priv->dpi = dpi; + if (orientation == LEFT_TO_RIGHT || orientation == RIGHT_TO_LEFT) + page->priv->rows = width; + else + page->priv->rows = height; + page->priv->image = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, + 8, + width, + height); + g_return_if_fail (page->priv->image != NULL); + gdk_pixbuf_fill (page->priv->image, 0xFFFFFFFF); +} + + +void +page_set_scan_area (Page *page, gint width, gint rows, gint dpi) +{ + gint height; + + g_return_if_fail (page != NULL); + + /* Variable height, try 50% of the width for now */ + if (rows < 0) + height = width / 2; + else + height = rows; + + /* Rotate page */ + if (page->priv->orientation == LEFT_TO_RIGHT || page->priv->orientation == RIGHT_TO_LEFT) { + gint t; + t = width; + width = height; + height = t; + } + + page->priv->rows = rows; + page->priv->dpi = dpi; + + /* Create a white page */ + /* NOTE: Pixbuf only supports 8 bit RGB images */ + if (page->priv->image) + g_object_unref (page->priv->image); + page->priv->image = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, + 8, + width, + height); + g_return_if_fail (page->priv->image != NULL); + + gdk_pixbuf_fill (page->priv->image, 0xFFFFFFFF); + g_signal_emit (page, signals[SIZE_CHANGED], 0); + g_signal_emit (page, signals[IMAGE_CHANGED], 0); +} + + +void +page_start (Page *page) +{ + g_return_if_fail (page != NULL); + + page->priv->scanning = TRUE; + g_signal_emit (page, signals[SCAN_LINE_CHANGED], 0); +} + + +gboolean page_is_scanning (Page *page) +{ + g_return_val_if_fail (page != NULL, FALSE); + + return page->priv->scanning; +} + + +static gint +get_sample (guchar *data, gint depth, gint index) +{ + gint i, offset, value, n_bits; + + /* Optimise if using 8 bit samples */ + if (depth == 8) + return data[index]; + + /* Bit offset for this sample */ + offset = depth * index; + + /* Get the remaining bits in the octet this sample starts in */ + i = offset / 8; + n_bits = 8 - offset % 8; + value = data[i] & (0xFF >> (8 - n_bits)); + + /* Add additional octets until get enough bits */ + while (n_bits < depth) { + value = value << 8 | data[i++]; + n_bits += 8; + } + + /* Trim remaining bits off */ + if (n_bits > depth) + value >>= n_bits - depth; + + return value; +} + + +gboolean page_has_data (Page *page) +{ + g_return_val_if_fail (page != NULL, FALSE); + return page->priv->has_data; +} + + +gint page_get_scan_line (Page *page) +{ + g_return_val_if_fail (page != NULL, -1); + return page->priv->scan_line; +} + + +static void +set_pixel (ScanLine *line, gint n, gint x, guchar *pixel) +{ + gint sample; + guchar *data; + + data = line->data + line->data_length * n; + + switch (line->format) { + case LINE_RGB: + pixel[0] = get_sample (data, line->depth, x*3) * 0xFF / ((1 << line->depth) - 1); + pixel[1] = get_sample (data, line->depth, x*3+1) * 0xFF / ((1 << line->depth) - 1); + pixel[2] = get_sample (data, line->depth, x*3+2) * 0xFF / ((1 << line->depth) - 1); + break; + case LINE_GRAY: + /* Bitmap, 0 = white, 1 = black */ + sample = get_sample (data, line->depth, x) * 0xFF / ((1 << line->depth) - 1); + if (line->depth == 1) + sample = sample ? 0x00 : 0xFF; + + pixel[0] = pixel[1] = pixel[2] = sample; + break; + case LINE_RED: + pixel[0] = get_sample (data, line->depth, x) * 0xFF / ((1 << line->depth) - 1); + break; + case LINE_GREEN: + pixel[1] = get_sample (data, line->depth, x) * 0xFF / ((1 << line->depth) - 1); + break; + case LINE_BLUE: + pixel[2] = get_sample (data, line->depth, x) * 0xFF / ((1 << line->depth) - 1); + break; + } +} + + +static void +parse_line (Page *page, ScanLine *line, gint n, gboolean *size_changed) +{ + guchar *pixels; + gint line_number; + gint i, x = 0, y = 0, x_step = 0, y_step = 0; + gint rowstride, n_channels; + + line_number = line->number + n; + + /* Extend image if necessary */ + while (line_number >= page_get_scan_height (page)) { + GdkPixbuf *image; + gint height, width, new_width, new_height; + + /* Extend image */ + new_width = width = gdk_pixbuf_get_width (page->priv->image); + new_height = height = gdk_pixbuf_get_height (page->priv->image); + if (page->priv->orientation == TOP_TO_BOTTOM || page->priv->orientation == BOTTOM_TO_TOP) { + new_height = height + width / 2; + g_debug("Extending image height from %d pixels to %d pixels", height, new_height); + } + else { + new_width = width + height / 2; + g_debug("Extending image width from %d pixels to %d pixels", width, new_width); + } + image = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, + 8, new_width, new_height); + + /* Copy old data */ + gdk_pixbuf_fill (page->priv->image, 0xFFFFFFFF); + if (page->priv->orientation == TOP_TO_BOTTOM || page->priv->orientation == LEFT_TO_RIGHT) + gdk_pixbuf_copy_area (page->priv->image, 0, 0, width, height, + image, 0, 0); + else + gdk_pixbuf_copy_area (page->priv->image, 0, 0, width, height, + image, new_width - width, new_height - height); + + g_object_unref (page->priv->image); + page->priv->image = image; + + *size_changed = TRUE; + } + + switch (page->priv->orientation) { + case TOP_TO_BOTTOM: + x = 0; + y = line_number; + x_step = 1; + y_step = 0; + break; + case BOTTOM_TO_TOP: + x = page_get_width (page) - 1; + y = page_get_height (page) - line_number - 1; + x_step = -1; + y_step = 0; + break; + case LEFT_TO_RIGHT: + x = line_number; + y = page_get_height (page) - 1; + x_step = 0; + y_step = -1; + break; + case RIGHT_TO_LEFT: + x = page_get_width (page) - line_number - 1; + y = 0; + x_step = 0; + y_step = 1; + break; + } + pixels = gdk_pixbuf_get_pixels (page->priv->image); + rowstride = gdk_pixbuf_get_rowstride (page->priv->image); + n_channels = gdk_pixbuf_get_n_channels (page->priv->image); + for (i = 0; i < line->width; i++) { + guchar *pixel; + + pixel = pixels + y * rowstride + x * n_channels; + set_pixel (line, n, i, pixel); + x += x_step; + y += y_step; + } + + page->priv->scan_line = line_number; +} + + +void +page_parse_scan_line (Page *page, ScanLine *line) +{ + gint i; + gboolean size_changed = FALSE; + + g_return_if_fail (page != NULL); + + for (i = 0; i < line->n_lines; i++) + parse_line (page, line, i, &size_changed); + + page->priv->has_data = TRUE; + + if (size_changed) + g_signal_emit (page, signals[SIZE_CHANGED], 0); + g_signal_emit (page, signals[SCAN_LINE_CHANGED], 0); + g_signal_emit (page, signals[IMAGE_CHANGED], 0); +} + + +void +page_finish (Page *page) +{ + gboolean size_changed = FALSE; + + g_return_if_fail (page != NULL); + + /* Trim page */ + if (page->priv->rows < 0 && + page->priv->scan_line != gdk_pixbuf_get_height (page->priv->image)) { + GdkPixbuf *image; + gint width, height, new_width, new_height; + + new_width = width = gdk_pixbuf_get_width (page->priv->image); + new_height = height = gdk_pixbuf_get_height (page->priv->image); + if (page->priv->orientation == TOP_TO_BOTTOM || page->priv->orientation == BOTTOM_TO_TOP) { + new_height = page->priv->scan_line; + g_debug("Trimming image height from %d pixels to %d pixels", height, new_height); + } + else { + new_width = page->priv->scan_line; + g_debug("Trimming image width from %d pixels to %d pixels", width, new_width); + } + image = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, + 8, + new_width, new_height); + + /* Copy old data */ + if (page->priv->orientation == TOP_TO_BOTTOM || page->priv->orientation == LEFT_TO_RIGHT) + gdk_pixbuf_copy_area (page->priv->image, 0, 0, width, height, + image, 0, 0); + else + gdk_pixbuf_copy_area (page->priv->image, width - new_width, height - new_height, width, height, + image, 0, 0); + + g_object_unref (page->priv->image); + page->priv->image = image; + size_changed = TRUE; + } + page->priv->scanning = FALSE; + + if (size_changed) + g_signal_emit (page, signals[SIZE_CHANGED], 0); + g_signal_emit (page, signals[SCAN_LINE_CHANGED], 0); +} + + +Orientation +page_get_orientation (Page *page) +{ + g_return_val_if_fail (page != NULL, TOP_TO_BOTTOM); + + return page->priv->orientation; +} + + +void +page_set_orientation (Page *page, Orientation orientation) +{ + gint left_steps, t; + GdkPixbuf *image; + gboolean size_changed = FALSE; + gint width, height; + + g_return_if_fail (page != NULL); + + if (page->priv->orientation == orientation) + return; + + /* Work out how many times it has been rotated to the left */ + left_steps = orientation - page->priv->orientation; + if (left_steps < 0) + left_steps += 4; + + width = page_get_width (page); + height = page_get_height (page); + + /* Rotate image */ + if (left_steps == 1) + image = gdk_pixbuf_rotate_simple (page->priv->image, GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE); + else if (left_steps == 2) + image = gdk_pixbuf_rotate_simple (page->priv->image, GDK_PIXBUF_ROTATE_UPSIDEDOWN); + else + image = gdk_pixbuf_rotate_simple (page->priv->image, GDK_PIXBUF_ROTATE_CLOCKWISE); + g_object_unref (page->priv->image); + page->priv->image = image; + if (left_steps != 2) + size_changed = TRUE; + + /* Rotate crop */ + if (page->priv->has_crop) { + switch (left_steps) { + /* 90 degrees counter-clockwise */ + case 1: + t = page->priv->crop_x; + page->priv->crop_x = page->priv->crop_y; + page->priv->crop_y = width - (t + page->priv->crop_width); + t = page->priv->crop_width; + page->priv->crop_width = page->priv->crop_height; + page->priv->crop_height = t; + break; + /* 180 degrees */ + case 2: + page->priv->crop_x = width - (page->priv->crop_x + page->priv->crop_width); + page->priv->crop_y = width - (page->priv->crop_y + page->priv->crop_height); + break; + /* 90 degrees clockwise */ + case 3: + t = page->priv->crop_y; + page->priv->crop_y = page->priv->crop_x; + page->priv->crop_x = height - (t + page->priv->crop_height); + t = page->priv->crop_width; + page->priv->crop_width = page->priv->crop_height; + page->priv->crop_height = t; + break; + } + } + + page->priv->orientation = orientation; + if (size_changed) + g_signal_emit (page, signals[SIZE_CHANGED], 0); + g_signal_emit (page, signals[IMAGE_CHANGED], 0); + g_signal_emit (page, signals[ORIENTATION_CHANGED], 0); + g_signal_emit (page, signals[CROP_CHANGED], 0); +} + + +void +page_rotate_left (Page *page) +{ + Orientation orientation; + + g_return_if_fail (page != NULL); + + orientation = page_get_orientation (page); + if (orientation == RIGHT_TO_LEFT) + orientation = TOP_TO_BOTTOM; + else + orientation++; + page_set_orientation (page, orientation); +} + + +void +page_rotate_right (Page *page) +{ + Orientation orientation; + + orientation = page_get_orientation (page); + if (orientation == TOP_TO_BOTTOM) + orientation = RIGHT_TO_LEFT; + else + orientation--; + page_set_orientation (page, orientation); +} + + +gint +page_get_dpi (Page *page) +{ + g_return_val_if_fail (page != NULL, 0); + + return page->priv->dpi; +} + + +gboolean +page_is_landscape (Page *page) +{ + return page_get_width (page) > page_get_height (page); +} + + +gint +page_get_width (Page *page) +{ + g_return_val_if_fail (page != NULL, 0); + return gdk_pixbuf_get_width (page->priv->image); +} + + +gint +page_get_height (Page *page) +{ + g_return_val_if_fail (page != NULL, 0); + return gdk_pixbuf_get_height (page->priv->image); +} + + +gint +page_get_scan_width (Page *page) +{ + g_return_val_if_fail (page != NULL, 0); + + if (page->priv->orientation == TOP_TO_BOTTOM || page->priv->orientation == BOTTOM_TO_TOP) + return gdk_pixbuf_get_width (page->priv->image); + else + return gdk_pixbuf_get_height (page->priv->image); +} + + +gint +page_get_scan_height (Page *page) +{ + g_return_val_if_fail (page != NULL, 0); + + if (page->priv->orientation == TOP_TO_BOTTOM || page->priv->orientation == BOTTOM_TO_TOP) + return gdk_pixbuf_get_height (page->priv->image); + else + return gdk_pixbuf_get_width (page->priv->image); +} + + +void page_set_color_profile (Page *page, const gchar *color_profile) +{ + g_free (page->priv->color_profile); + page->priv->color_profile = g_strdup (color_profile); +} + + +const gchar *page_get_color_profile (Page *page) +{ + return page->priv->color_profile; +} + + +void +page_set_no_crop (Page *page) +{ + g_return_if_fail (page != NULL); + + if (!page->priv->has_crop) + return; + page->priv->has_crop = FALSE; + g_signal_emit (page, signals[CROP_CHANGED], 0); +} + + +void +page_set_custom_crop (Page *page, gint width, gint height) +{ + //gint pw, ph; + + g_return_if_fail (page != NULL); + g_return_if_fail (width >= 1); + g_return_if_fail (height >= 1); + + if (!page->priv->crop_name && + page->priv->has_crop && + page->priv->crop_width == width && + page->priv->crop_height == height) + return; + g_free (page->priv->crop_name); + page->priv->crop_name = NULL; + page->priv->has_crop = TRUE; + + page->priv->crop_width = width; + page->priv->crop_height = height; + + /*pw = page_get_width (page); + ph = page_get_height (page); + if (page->priv->crop_width < pw) + page->priv->crop_x = (pw - page->priv->crop_width) / 2; + else + page->priv->crop_x = 0; + if (page->priv->crop_height < ph) + page->priv->crop_y = (ph - page->priv->crop_height) / 2; + else + page->priv->crop_y = 0;*/ + + g_signal_emit (page, signals[CROP_CHANGED], 0); +} + + +void +page_set_named_crop (Page *page, const gchar *name) +{ + struct { + const gchar *name; + /* Width and height in inches */ + gdouble width, height; + } named_crops[] = + { + {"A4", 8.3, 11.7}, + {"A5", 5.8, 8.3}, + {"A6", 4.1, 5.8}, + {"letter", 8.5, 11}, + {"legal", 8.5, 14}, + {"4x6", 4, 6}, + {NULL, 0, 0} + }; + gint i; + gint pw, ph; + double width, height; + + g_return_if_fail (page != NULL); + + for (i = 0; named_crops[i].name && strcmp (name, named_crops[i].name) != 0; i++); + width = named_crops[i].width; + height = named_crops[i].height; + + if (!named_crops[i].name) { + g_warning ("Unknown paper size '%s'", name); + return; + } + + g_free (page->priv->crop_name); + page->priv->crop_name = g_strdup (name); + page->priv->has_crop = TRUE; + + pw = page_get_width (page); + ph = page_get_height (page); + + /* Rotate to match original aspect */ + if (pw > ph) { + double t; + t = width; + width = height; + height = t; + } + + /* Custom crop, make slightly smaller than original */ + page->priv->crop_width = (int) (width * page->priv->dpi + 0.5); + page->priv->crop_height = (int) (height * page->priv->dpi + 0.5); + + if (page->priv->crop_width < pw) + page->priv->crop_x = (pw - page->priv->crop_width) / 2; + else + page->priv->crop_x = 0; + if (page->priv->crop_height < ph) + page->priv->crop_y = (ph - page->priv->crop_height) / 2; + else + page->priv->crop_y = 0; + g_signal_emit (page, signals[CROP_CHANGED], 0); +} + + +void +page_move_crop (Page *page, gint x, gint y) +{ + g_return_if_fail (x >= 0); + g_return_if_fail (y >= 0); + g_return_if_fail (x < page_get_width (page)); + g_return_if_fail (y < page_get_height (page)); + + page->priv->crop_x = x; + page->priv->crop_y = y; + g_signal_emit (page, signals[CROP_CHANGED], 0); +} + + +void +page_rotate_crop (Page *page) +{ + gint t; + + g_return_if_fail (page != NULL); + + if (!page->priv->has_crop) + return; + + t = page->priv->crop_width; + page->priv->crop_width = page->priv->crop_height; + page->priv->crop_height = t; + + /* Clip custom crops */ + if (!page->priv->crop_name) { + gint w, h; + + w = page_get_width (page); + h = page_get_height (page); + + if (page->priv->crop_x + page->priv->crop_width > w) + page->priv->crop_x = w - page->priv->crop_width; + if (page->priv->crop_x < 0) { + page->priv->crop_x = 0; + page->priv->crop_width = w; + } + if (page->priv->crop_y + page->priv->crop_height > h) + page->priv->crop_y = h - page->priv->crop_height; + if (page->priv->crop_y < 0) { + page->priv->crop_y = 0; + page->priv->crop_height = h; + } + } + + g_signal_emit (page, signals[CROP_CHANGED], 0); +} + + +gboolean +page_has_crop (Page *page) +{ + g_return_val_if_fail (page != NULL, FALSE); + return page->priv->has_crop; +} + + +void +page_get_crop (Page *page, gint *x, gint *y, gint *width, gint *height) +{ + g_return_if_fail (page != NULL); + + if (x) + *x = page->priv->crop_x; + if (y) + *y = page->priv->crop_y; + if (width) + *width = page->priv->crop_width; + if (height) + *height = page->priv->crop_height; +} + + +gchar * +page_get_named_crop (Page *page) +{ + g_return_val_if_fail (page != NULL, NULL); + + if (page->priv->crop_name) + return g_strdup (page->priv->crop_name); + else + return NULL; +} + + +GdkPixbuf * +page_get_image (Page *page) +{ + g_return_val_if_fail (page != NULL, NULL); + return g_object_ref (page->priv->image); +} + + +GdkPixbuf * +page_get_cropped_image (Page *page) +{ + GdkPixbuf *image, *cropped_image; + gint x, y, w, h, pw, ph; + + g_return_val_if_fail (page != NULL, NULL); + + image = page_get_image (page); + + if (!page->priv->has_crop) + return image; + + x = page->priv->crop_x; + y = page->priv->crop_y; + w = page->priv->crop_width; + h = page->priv->crop_height; + pw = gdk_pixbuf_get_width (image); + ph = gdk_pixbuf_get_height (image); + + /* Trim crop */ + if (x + w >= pw) + w = pw - x; + if (y + h >= ph) + h = ph - y; + + cropped_image = gdk_pixbuf_new_subpixbuf (image, x, y, w, h); + g_object_unref (image); + + return cropped_image; +} + + +static gboolean +write_pixbuf_data (const gchar *buf, gsize count, GError **error, GFileOutputStream *stream) +{ + return g_output_stream_write_all (G_OUTPUT_STREAM (stream), buf, count, NULL, NULL, error); +} + + +static gchar * +get_icc_data_encoded (const gchar *icc_profile_filename) +{ + gchar *contents = NULL; + gchar *contents_encode = NULL; + gsize length; + gboolean ret; + GError *error = NULL; + + /* Get binary data */ + ret = g_file_get_contents (icc_profile_filename, &contents, &length, &error); + if (!ret) { + g_warning ("failed to get icc profile data: %s", error->message); + g_error_free (error); + } + else { + /* Encode into base64 */ + contents_encode = g_base64_encode ((const guchar *) contents, length); + } + + g_free (contents); + return contents_encode; +} + + +gboolean +page_save (Page *page, const gchar *type, GFile *file, GError **error) +{ + GFileOutputStream *stream; + GdkPixbuf *image; + gboolean result = FALSE; + gchar *icc_profile_data = NULL; + + stream = g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error); + if (!stream) + return FALSE; + + image = page_get_cropped_image (page); + + if (page->priv->color_profile != NULL) + icc_profile_data = get_icc_data_encoded (page->priv->color_profile); + + if (strcmp (type, "jpeg") == 0) { + /* ICC profile is awaiting review in gtk2+ bugzilla */ + gchar *keys[] = { "quality", /* "icc-profile", */ NULL }; + gchar *values[] = { "90", /* icc_profile_data, */ NULL }; + result = gdk_pixbuf_save_to_callbackv (image, + (GdkPixbufSaveFunc) write_pixbuf_data, stream, + "jpeg", keys, values, error); + } + else if (strcmp (type, "png") == 0) { + gchar *keys[] = { "icc-profile", NULL }; + gchar *values[] = { icc_profile_data, NULL }; + if (icc_profile_data == NULL) + keys[0] = NULL; + result = gdk_pixbuf_save_to_callbackv (image, + (GdkPixbufSaveFunc) write_pixbuf_data, stream, + "png", keys, values, error); + } + else if (strcmp (type, "tiff") == 0) { + gchar *keys[] = { "compression", "icc-profile", NULL }; + gchar *values[] = { "8" /* Deflate compression */, icc_profile_data, NULL }; + if (icc_profile_data == NULL) + keys[1] = NULL; + result = gdk_pixbuf_save_to_callbackv (image, + (GdkPixbufSaveFunc) write_pixbuf_data, stream, + "tiff", keys, values, error); + } + else + result = FALSE; // FIXME: Set GError + + g_free (icc_profile_data); + g_object_unref (image); + g_object_unref (stream); + + return result; +} + + +static void +page_finalize (GObject *object) +{ + Page *page = PAGE (object); + if (page->priv->image) + g_object_unref (page->priv->image); + page->priv->image = NULL; + G_OBJECT_CLASS (page_parent_class)->finalize (object); +} + + +static void +page_class_init (PageClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = page_finalize; + + signals[IMAGE_CHANGED] = + g_signal_new ("image-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (PageClass, image_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + signals[SIZE_CHANGED] = + g_signal_new ("size-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (PageClass, size_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + signals[SCAN_LINE_CHANGED] = + g_signal_new ("scan-line-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (PageClass, scan_line_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + signals[ORIENTATION_CHANGED] = + g_signal_new ("orientation-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (PageClass, orientation_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + signals[CROP_CHANGED] = + g_signal_new ("crop-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (PageClass, crop_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + g_type_class_add_private (klass, sizeof (PagePrivate)); +} + + +static void +page_init (Page *page) +{ + page->priv = G_TYPE_INSTANCE_GET_PRIVATE (page, PAGE_TYPE, PagePrivate); + page->priv->orientation = TOP_TO_BOTTOM; +} |