4
#include <cairo/cairo-pdf.h>
10
#define DEFAULT_DPI 75 // FIXME
12
static SimpleScan *ui;
14
static Scanner *scanner;
16
static GdkPixbuf *raw_image = NULL;
18
static gboolean scan_complete = FALSE;
20
static int current_line;
24
update_scan_devices_cb (Scanner *scanner, GList *devices)
28
/* Mark existing values as undetected */
29
ui_mark_devices_undetected (ui);
31
/* Add/update detected devices */
32
for (dev_iter = devices; dev_iter; dev_iter = dev_iter->next) {
33
ScanDevice *device = dev_iter->data;
35
ui_add_scan_device (ui, device->name, device->label);
36
g_free (device->name);
37
g_free (device->label);
40
g_list_free (devices);
45
scanner_page_info_cb (Scanner *scanner, ScanPageInfo *info)
49
g_debug ("Page is %d pixels wide, %d pixels high, %d bits per pixel",
50
info->width, info->height, info->depth);
52
/* Variable heigh, try 50% of the width for now */
54
height = info->width / 2;
56
height = info->height;
58
raw_image = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE,
65
scan_complete = FALSE;
70
get_sample (guchar *data, int depth, int index)
72
int i, offset, value, n_bits;
74
/* Optimise if using 8 bit samples */
78
/* Bit offset for this sample */
79
offset = depth * index;
81
/* Get the remaining bits in the octet this sample starts in */
83
n_bits = 8 - offset % 8;
84
value = data[i] & (0xFF >> (8 - n_bits));
86
/* Add additional octets until get enough bits */
87
while (n_bits < depth) {
88
value = value << 8 | data[i++];
92
/* Trim remaining bits off */
94
value >>= n_bits - depth;
101
scanner_line_cb (Scanner *scanner, ScanLine *line)
106
/* Extend image if necessary */
107
while (line->number >= gdk_pixbuf_get_height (raw_image)) {
109
gint height, width, new_height;
111
width = gdk_pixbuf_get_width (raw_image);
112
height = gdk_pixbuf_get_height (raw_image);
113
new_height = height + width / 2;
114
g_debug("Resizing image height from %d pixels to %d pixels", height, new_height);
116
image = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE,
117
gdk_pixbuf_get_bits_per_sample (raw_image),
119
memcpy (gdk_pixbuf_get_pixels (image),
120
gdk_pixbuf_get_pixels (raw_image),
121
height * gdk_pixbuf_get_rowstride (raw_image));
122
g_object_unref (raw_image);
126
pixels = gdk_pixbuf_get_pixels (raw_image) + line->number * gdk_pixbuf_get_rowstride (raw_image);
127
switch (line->format) {
129
memcpy (pixels, line->data, line->data_length);
132
for (i = 0; i < line->width; i++) {
133
pixels[j] = get_sample (line->data, line->depth, i);
134
pixels[j+1] = get_sample (line->data, line->depth, i);
135
pixels[j+2] = get_sample (line->data, line->depth, i);
140
for (i = 0; i < line->width; i++) {
141
pixels[j] = get_sample (line->data, line->depth, i);
146
for (i = 0; i < line->width; i++) {
147
pixels[j+1] = get_sample (line->data, line->depth, i);
152
for (i = 0; i < line->width; i++) {
153
pixels[j+2] = get_sample (line->data, line->depth, i);
159
current_line = line->number + 1;
164
ui_redraw_preview (ui);
169
scanner_image_done_cb (Scanner *scanner)
172
if (current_line != gdk_pixbuf_get_height (raw_image)) {
175
gint height, width, new_height;
177
width = gdk_pixbuf_get_width (raw_image);
178
height = gdk_pixbuf_get_height (raw_image);
179
new_height = current_line;
180
g_debug("Trimming image height from %d pixels to %d pixels", height, new_height);
182
image = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE,
183
gdk_pixbuf_get_bits_per_sample (raw_image),
185
memcpy (gdk_pixbuf_get_pixels (image),
186
gdk_pixbuf_get_pixels (raw_image),
187
new_height * gdk_pixbuf_get_rowstride (raw_image));
188
g_object_unref (raw_image);
192
scan_complete = TRUE;
193
ui_redraw_preview (ui);
194
ui_set_have_scan (ui, TRUE);
199
scanner_ready_cb (Scanner *scanner)
201
ui_set_scanning (ui, FALSE);
206
render_scan (cairo_t *context, GdkPixbuf *image, Orientation orientation, double canvas_width, double canvas_height, gboolean show_scan_line)
208
double orig_img_width, orig_img_height, img_width, img_height;
209
double source_aspect, canvas_aspect;
210
double x_offset = 0.0, y_offset = 0.0, scale = 1.0, rotation = 0.0;
212
orig_img_width = img_width = gdk_pixbuf_get_width (image);
213
orig_img_height = img_height = gdk_pixbuf_get_height (image);
215
switch (orientation) {
223
img_width = orig_img_height;
224
img_height = orig_img_width;
228
img_width = orig_img_height;
229
img_height = orig_img_width;
234
/* Scale if cannot fit into canvas */
235
if (img_width > canvas_width || img_height > canvas_height) {
236
canvas_aspect = canvas_width / canvas_height;
237
source_aspect = img_width / img_height;
239
/* Scale to canvas height */
240
if (canvas_aspect > source_aspect) {
241
scale = canvas_height / img_height;
242
x_offset = (int) (canvas_width - (img_width * scale)) / 2;
244
/* Otherwise scale to canvas width */
246
scale = canvas_width / img_width;
247
y_offset = (int) (canvas_height - (img_height * scale)) / 2;
251
/* Render the image */
252
cairo_save (context);
254
cairo_translate (context, x_offset, y_offset);
255
cairo_scale (context, scale, scale);
256
cairo_translate (context, img_width / 2, img_height / 2);
257
cairo_rotate (context, rotation);
258
cairo_translate (context, -orig_img_width / 2, -orig_img_height / 2);
260
gdk_cairo_set_source_pixbuf (context, image, 0, 0);
261
cairo_pattern_set_filter (cairo_get_source (context), CAIRO_FILTER_BEST);
262
cairo_paint (context);
264
cairo_restore (context);
267
if (show_scan_line && !scan_complete) {
268
double h = scale * (double)(current_line * orig_img_height) / (double)img_height;
270
switch (orientation) {
272
cairo_translate (context, x_offset, y_offset);
275
cairo_translate (context, canvas_width - x_offset, canvas_height - y_offset);
278
cairo_translate (context, x_offset, canvas_height - y_offset);
281
cairo_translate (context, canvas_width - x_offset, y_offset);
284
cairo_rotate (context, rotation);
286
cairo_set_source_rgb (context, 1.0, 0.0, 0.0);
287
cairo_move_to (context, 0, h);
288
cairo_line_to (context, scale * orig_img_width, h);
289
cairo_stroke (context);
295
render_cb (SimpleScan *ui, cairo_t *context, double width, double height)
298
render_scan (context, raw_image, ui_get_orientation (ui),
299
width, height, TRUE);
302
cairo_set_source_rgb (context, 0.0, 0.0, 0.0);
303
cairo_rectangle (context, 0, 0, width, height);
304
cairo_fill (context);
310
scan_cb (SimpleScan *ui, const gchar *device)
312
g_debug ("Requesting scan from device '%s'", device);
313
ui_set_have_scan (ui, FALSE);
314
ui_set_scanning (ui, TRUE);
315
scanner_scan (scanner, device, DEFAULT_DPI);
320
cancel_cb (SimpleScan *ui)
322
scanner_cancel (scanner);
327
write_pixbuf_data (const gchar *buf, gsize count, GError **error, GFileOutputStream *stream)
329
return g_output_stream_write_all (G_OUTPUT_STREAM (stream), buf, count, NULL, NULL, error);
334
save_jpeg (GdkPixbuf *image, GFileOutputStream *stream, GError **error)
336
return gdk_pixbuf_save_to_callback (image,
337
(GdkPixbufSaveFunc) write_pixbuf_data, stream,
345
save_png (GdkPixbuf *image, GFileOutputStream *stream, GError **error)
347
return gdk_pixbuf_save_to_callback (image,
348
(GdkPixbufSaveFunc) write_pixbuf_data, stream,
354
static cairo_status_t
355
write_pdf_data (GFileOutputStream *stream, unsigned char *data, unsigned int length)
358
GError *error = NULL;
360
result = g_output_stream_write_all (G_OUTPUT_STREAM (stream), data, length, NULL, NULL, &error);
363
g_warning ("Error writing PDF data: %s", error->message);
364
g_error_free (error);
367
return result ? CAIRO_STATUS_SUCCESS : CAIRO_STATUS_WRITE_ERROR;
372
save_pdf (GdkPixbuf *image, GFileOutputStream *stream, GError **error)
374
cairo_surface_t *surface;
376
double width, height;
378
width = gdk_pixbuf_get_width (image) * 72.0 / DEFAULT_DPI;
379
height = gdk_pixbuf_get_height (image) * 72.0 / DEFAULT_DPI;
381
surface = cairo_pdf_surface_create_for_stream ((cairo_write_func_t) write_pdf_data,
385
context = cairo_create (surface);
387
cairo_scale (context, 72.0 / DEFAULT_DPI, 72.0 / DEFAULT_DPI);
388
gdk_cairo_set_source_pixbuf (context, image, 0, 0);
389
cairo_pattern_set_filter (cairo_get_source (context), CAIRO_FILTER_BEST);
390
cairo_paint (context);
392
cairo_destroy (context);
393
cairo_surface_destroy (surface);
399
static GdkPixbuf *get_rotated_image (Orientation orientation)
401
switch (orientation) {
404
return gdk_pixbuf_ref (raw_image);
406
return gdk_pixbuf_rotate_simple (raw_image, GDK_PIXBUF_ROTATE_UPSIDEDOWN);
408
return gdk_pixbuf_rotate_simple (raw_image, GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE);
410
return gdk_pixbuf_rotate_simple (raw_image, GDK_PIXBUF_ROTATE_CLOCKWISE);
416
save_cb (SimpleScan *ui, gchar *uri)
419
GError *error = NULL;
420
GFileOutputStream *stream;
422
file = g_file_new_for_uri (uri);
424
stream = g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error);
427
g_error_free (error);
434
image = get_rotated_image (ui_get_orientation (ui));
436
uri_lower = g_utf8_strdown (uri, -1);
437
if (g_str_has_suffix (uri_lower, ".pdf"))
438
result = save_pdf (image, stream, &error);
439
else if (g_str_has_suffix (uri_lower, ".png"))
440
result = save_png (image, stream, &error);
442
result = save_jpeg (image, stream, &error);
445
g_object_unref (image);
449
g_error_free (error);
456
print_cb (SimpleScan *ui, cairo_t *context)
460
image = get_rotated_image (ui_get_orientation (ui));
462
gdk_cairo_set_source_pixbuf (context, image, 0, 0);
463
cairo_pattern_set_filter (cairo_get_source (context), CAIRO_FILTER_BEST);
464
cairo_paint (context);
466
g_object_unref (image);
471
main(int argc, char **argv)
473
g_thread_init (NULL);
474
gtk_init (&argc, &argv);
476
scanner = scanner_new ();
477
g_signal_connect (G_OBJECT (scanner), "ready", G_CALLBACK (scanner_ready_cb), NULL);
478
g_signal_connect (G_OBJECT (scanner), "update-devices", G_CALLBACK (update_scan_devices_cb), NULL);
479
g_signal_connect (G_OBJECT (scanner), "got-page-info", G_CALLBACK (scanner_page_info_cb), NULL);
480
g_signal_connect (G_OBJECT (scanner), "got-line", G_CALLBACK (scanner_line_cb), NULL);
481
g_signal_connect (G_OBJECT (scanner), "image-done", G_CALLBACK (scanner_image_done_cb), NULL);
484
g_signal_connect (ui, "render-preview", G_CALLBACK (render_cb), NULL);
485
g_signal_connect (ui, "start-scan", G_CALLBACK (scan_cb), NULL);
486
g_signal_connect (ui, "stop-scan", G_CALLBACK (cancel_cb), NULL);
487
g_signal_connect (ui, "save", G_CALLBACK (save_cb), NULL);
488
g_signal_connect (ui, "print", G_CALLBACK (print_cb), NULL);