/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend xsane-scan.c Oliver Rauch Copyright (C) 1998-2000 Oliver Rauch This file is part of the XSANE package. 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 2 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, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* ---------------------------------------------------------------------------------------------------------------------- */ #include "xsane.h" #include "xsane-back-gtk.h" #include "xsane-front-gtk.h" #include "xsane-preferences.h" #include "xsane-preview.h" #include "xsane-save.h" #include "xsane-text.h" #include "xsane-gamma.h" #include "xsane-setup.h" #ifdef HAVE_LIBPNG #ifdef HAVE_LIBZ #include #include #endif #endif /* ---------------------------------------------------------------------------------------------------------------------- */ #ifdef HAVE_LIBGIMP_GIMP_H #include static void xsane_gimp_query(void); static void xsane_gimp_run(char *name, int nparams, GParam * param, int *nreturn_vals, GParam ** return_vals); GPlugInInfo PLUG_IN_INFO = { NULL, /* init_proc */ NULL, /* quit_proc */ xsane_gimp_query, /* query_proc */ xsane_gimp_run, /* run_proc */ }; #endif /* HAVE_LIBGIMP_GIMP_H */ /* ---------------------------------------------------------------------------------------------------------------------- */ /* forward declarations: */ static int xsane_generate_dummy_filename(); #ifdef HAVE_LIBGIMP_GIMP_H static int xsane_decode_devname(const char *encoded_devname, int n, char *buf); static int xsane_encode_devname(const char *devname, int n, char *buf); void null_print_func(gchar *msg); static void xsane_gimp_advance(void); #endif static void xsane_read_image_data(gpointer data, gint source, GdkInputCondition cond); static RETSIGTYPE xsane_sigpipe_handler(int signal); static int xsane_test_multi_scan(void); void xsane_scan_done(SANE_Status status); void xsane_cancel(void); static void xsane_start_scan(void); void xsane_scan_dialog(GtkWidget * widget, gpointer call_data); /* ---------------------------------------------------------------------------------------------------------------------- */ static int xsane_generate_dummy_filename() { /* returns TRUE if file is a temporary file */ if (xsane.dummy_filename) { free(xsane.dummy_filename); } if ( (xsane.xsane_mode == XSANE_COPY) || (xsane.xsane_mode == XSANE_FAX) || /* we have to do a conversion */ ( (xsane.xsane_mode == XSANE_SCAN) && (xsane.xsane_output_format != XSANE_PNM) && (xsane.xsane_output_format != XSANE_RAW16) && (xsane.xsane_output_format != XSANE_RGBA) ) ) { char filename[PATH_MAX]; xsane_back_gtk_make_path(sizeof(filename), filename, 0, 0, "conversion-", dialog->dev_name, ".ppm", XSANE_PATH_TMP); xsane.dummy_filename = strdup(filename); return TRUE; } else /* no conversion following, save directly to the selected filename */ { xsane.dummy_filename = strdup(xsane.output_filename); return FALSE; } } /* ---------------------------------------------------------------------------------------------------------------------- */ #ifdef HAVE_LIBGIMP_GIMP_H static int xsane_decode_devname(const char *encoded_devname, int n, char *buf) { char *dst, *limit; const char *src; char ch, val; limit = buf + n; for (src = encoded_devname, dst = buf; *src; ++dst) { if (dst >= limit) { return -1; } ch = *src++; /* don't use the ctype.h macros here since we don't want to allow anything non-ASCII here... */ if (ch != '-') { *dst = ch; } else /* decode */ { ch = *src++; if (ch == '-') { *dst = ch; } else { if (ch >= 'a' && ch <= 'f') { val = (ch - 'a') + 10; } else { val = (ch - '0'); } val <<= 4; ch = *src++; if (ch >= 'a' && ch <= 'f') { val |= (ch - 'a') + 10; } else { val |= (ch - '0'); } *dst = val; ++src; /* simply skip terminating '-' for now... */ } } } if (dst >= limit) { return -1; } *dst = '\0'; return 0; } /* ---------------------------------------------------------------------------------------------------------------------- */ static int xsane_encode_devname(const char *devname, int n, char *buf) { static const char hexdigit[] = "0123456789abcdef"; char *dst, *limit; const char *src; char ch; limit = buf + n; for (src = devname, dst = buf; *src; ++src) { if (dst >= limit) { return -1; } ch = *src; /* don't use the ctype.h macros here since we don't want to allow anything non-ASCII here... */ if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) { *dst++ = ch; } else /* encode */ { if (dst + 4 >= limit) { return -1; } *dst++ = '-'; if (ch == '-') { *dst++ = '-'; } else { *dst++ = hexdigit[(ch >> 4) & 0x0f]; *dst++ = hexdigit[(ch >> 0) & 0x0f]; *dst++ = '-'; } } } if (dst >= limit) { return -1; } *dst = '\0'; return 0; } /* ---------------------------------------------------------------------------------------------------------------------- */ static void xsane_gimp_query(void) { static GParamDef args[] = { {PARAM_INT32, "run_mode", "Interactive, non-interactive"}, }; static GParamDef *return_vals = NULL; static int nargs = sizeof(args) / sizeof(args[0]); static int nreturn_vals = 0; char mpath[1024]; char name[1024]; size_t len; int i, j; snprintf(name, sizeof(name), "%s", prog_name); #ifdef GIMP_CHECK_VERSION # if GIMP_CHECK_VERSION(1,1,9) snprintf(mpath, sizeof(mpath), "%s", XSANE_GIMP_MENU_DIALOG); # else snprintf(mpath, sizeof(mpath), "%s", XSANE_GIMP_MENU_DIALOG_OLD); # endif #else snprintf(mpath, sizeof(mpath), "%s", XSANE_GIMP_MENU_DIALOG_OLD); #endif gimp_install_procedure(name, XSANE_GIMP_INSTALL_BLURB, XSANE_GIMP_INSTALL_HELP, XSANE_AUTHOR, XSANE_COPYRIGHT, XSANE_DATE, mpath, 0, /* "RGB, GRAY", */ PROC_EXTENSION, nargs, nreturn_vals, args, return_vals); sane_init(&xsane.sane_backend_versioncode, (void *) xsane_authorization_callback); if (SANE_VERSION_MAJOR(xsane.sane_backend_versioncode) != SANE_V_MAJOR) { fprintf(stderr, "\n\n" "%s %s:\n" " %s\n" " %s %d\n" " %s %d\n" "%s\n\n", prog_name, ERR_ERROR, ERR_MAJOR_VERSION_NR_CONFLICT, ERR_XSANE_MAJOR_VERSION, SANE_V_MAJOR, ERR_BACKEND_MAJOR_VERSION, SANE_VERSION_MAJOR(xsane.sane_backend_versioncode), ERR_PROGRAM_ABORTED); return; } sane_get_devices(&devlist, SANE_FALSE); for (i = 0; devlist[i]; ++i) { snprintf(name, sizeof(name), "%s-", prog_name); if (xsane_encode_devname(devlist[i]->name, sizeof(name) - 6, name + 6) < 0) { continue; /* name too long... */ } #ifdef GIMP_CHECK_VERSION # if GIMP_CHECK_VERSION(1,1,9) snprintf(mpath, sizeof(mpath), "%s", XSANE_GIMP_MENU); # else snprintf(mpath, sizeof(mpath), "%s", XSANE_GIMP_MENU_OLD); # endif #else snprintf(mpath, sizeof(mpath), "%s", XSANE_GIMP_MENU_OLD); #endif len = strlen(mpath); for (j = 0; devlist[i]->name[j]; ++j) { if (devlist[i]->name[j] == '/') mpath[len++] = '\''; else mpath[len++] = devlist[i]->name[j]; } mpath[len++] = '\0'; gimp_install_procedure(name, XSANE_GIMP_INSTALL_BLURB, XSANE_GIMP_INSTALL_HELP, XSANE_AUTHOR, XSANE_COPYRIGHT, XSANE_DATE, mpath, "RGB, GRAY", PROC_EXTENSION, nargs, nreturn_vals, args, return_vals); } sane_exit(); } /* ---------------------------------------------------------------------------------------------------------------------- */ static void xsane_gimp_run(char *name, int nparams, GParam * param, int *nreturn_vals, GParam ** return_vals) { static GParam values[2]; GRunModeType run_mode; char devname[1024]; char *args[2]; int nargs; run_mode = param[0].data.d_int32; xsane.mode = XSANE_GIMP_EXTENSION; *nreturn_vals = 1; *return_vals = values; values[0].type = PARAM_STATUS; values[0].data.d_status = STATUS_CALLING_ERROR; nargs = 0; args[nargs++] = "xsane"; seldev = -1; if (strncmp(name, "xsane-", 6) == 0) { if (xsane_decode_devname(name + 6, sizeof(devname), devname) < 0) { return; /* name too long */ } args[nargs++] = devname; } switch (run_mode) { case RUN_INTERACTIVE: xsane_interface(nargs, args); values[0].data.d_status = STATUS_SUCCESS; break; case RUN_NONINTERACTIVE: /* Make sure all the arguments are there! */ break; case RUN_WITH_LAST_VALS: /* Possibly retrieve data */ break; default: break; } } /* ---------------------------------------------------------------------------------------------------------------------- */ void null_print_func(gchar *msg) { } /* ---------------------------------------------------------------------------------------------------------------------- */ static void xsane_gimp_advance(void) { if (++xsane.x >= xsane.param.pixels_per_line) { int tile_height = gimp_tile_height(); xsane.x = 0; ++xsane.y; if (xsane.y % tile_height == 0) { gimp_pixel_rgn_set_rect(&xsane.region, xsane.tile, 0, xsane.y - tile_height, xsane.param.pixels_per_line, tile_height); if (xsane.param.format >= SANE_FRAME_RED && xsane.param.format <= SANE_FRAME_BLUE) { int height; xsane.tile_offset %= 3; if (!xsane.first_frame) /* get the data for the existing tile: */ { height = tile_height; if (xsane.y + height >= xsane.param.lines) { height = xsane.param.lines - xsane.y; } gimp_pixel_rgn_get_rect(&xsane.region, xsane.tile, 0, xsane.y, xsane.param.pixels_per_line, height); } } else { xsane.tile_offset = 0; } } } } #endif /* HAVE_LIBGIMP_GIMP_H */ /* ---------------------------------------------------------------------------------------------------------------------- */ static void xsane_read_image_data(gpointer data, gint source, GdkInputCondition cond) { SANE_Handle dev = xsane_back_gtk_dialog_get_device (dialog); SANE_Status status; SANE_Int len; int i; char buf[255]; if ( (xsane.param.depth == 1) || (xsane.param.depth == 8) ) { static unsigned char buf8[32768]; while (1) { status = sane_read(dev, (SANE_Byte *) buf8, sizeof(buf8), &len); if (status == SANE_STATUS_EOF) { if (!xsane.param.last_frame) { xsane_start_scan(); break; /* leave while loop */ } xsane_scan_done(SANE_STATUS_EOF); /* image complete, stop scanning */ return; } if (status != SANE_STATUS_GOOD) { xsane_scan_done(status); /* status = return of sane_read */ snprintf(buf, sizeof(buf), "%s %s.", ERR_DURING_READ, XSANE_STRSTATUS(status)); xsane_back_gtk_error(buf, TRUE); return; } if (!len) { break; /* out of data for now, leave while loop */ } xsane.bytes_read += len; xsane_progress_update(xsane.progress, xsane.bytes_read / (gfloat) xsane.num_bytes); if (xsane.input_tag < 0) { while (gtk_events_pending()) { gtk_main_iteration(); } } switch (xsane.param.format) { case SANE_FRAME_GRAY: if (xsane.mode == XSANE_STANDALONE) { int i; char val; if ((!xsane.scanner_gamma_gray) && (xsane.param.depth > 1)) { for (i=0; i < len; ++i) { val = xsane.gamma_data[(int) buf8[i]]; fwrite(&val, 1, 1, xsane.out); } } else { fwrite(buf8, 1, len, xsane.out); } } #ifdef HAVE_LIBGIMP_GIMP_H else /* GIMP MODE GRAY 8 bit */ { switch (xsane.param.depth) { case 1: for (i = 0; i < len; ++i) { u_char mask; int j; mask = buf8[i]; for (j = 7; j >= 0; --j) { u_char gl = (mask & (1 << j)) ? 0x00 : 0xff; xsane.tile[xsane.tile_offset++] = gl; xsane_gimp_advance(); if (xsane.x == 0) { break; } } } break; case 8: if (!xsane.scanner_gamma_gray) { for (i = 0; i < len; ++i) { xsane.tile[xsane.tile_offset++] = xsane.gamma_data[(int) buf8[i]]; xsane_gimp_advance(); } } else { for (i = 0; i < len; ++i) { xsane.tile[xsane.tile_offset++] = buf8[i]; xsane_gimp_advance(); } } break; } } #endif /* HAVE_LIBGIMP_GIMP_H */ break; case SANE_FRAME_RGB: if (xsane.mode == XSANE_STANDALONE) { int i; char val; if (!xsane.scanner_gamma_color) /* gamma correction by xsane */ { for (i=0; i < len; ++i) { if (dialog->pixelcolor == 0) { val = xsane.gamma_data_red[(int) buf8[i]]; dialog->pixelcolor++; } else if (dialog->pixelcolor == 1) { val = xsane.gamma_data_green[(int) buf8[i]]; dialog->pixelcolor++; } else { val = xsane.gamma_data_blue[(int) buf8[i]]; dialog->pixelcolor = 0; } fwrite(&val, 1, 1, xsane.out); } } else /* gamma correction has been done by scanner */ { fwrite(buf8, 1, len, xsane.out); } } #ifdef HAVE_LIBGIMP_GIMP_H else /* GIMP MODE RGB 8 bit */ { switch (xsane.param.depth) { case 1: if (xsane.param.format == SANE_FRAME_RGB) { goto bad_depth; } for (i = 0; i < len; ++i) { u_char mask; int j; mask = buf8[i]; for (j = 0; j < 8; ++j) { u_char gl = (mask & 1) ? 0xff : 0x00; mask >>= 1; xsane.tile[xsane.tile_offset++] = gl; xsane_gimp_advance(); if (xsane.x == 0) break; } } break; case 8: if (!xsane.scanner_gamma_color) /* gamma correction by xsane */ { for (i = 0; i < len; ++i) { if (xsane.tile_offset % 3 == 0) { xsane.tile[xsane.tile_offset++] = xsane.gamma_data_red[(int) buf8[i]]; } else if (xsane.tile_offset % 3 == 1) { xsane.tile[xsane.tile_offset++] = xsane.gamma_data_green[(int) buf8[i]]; } else { xsane.tile[xsane.tile_offset++] = xsane.gamma_data_blue[(int) buf8[i]]; } if (xsane.tile_offset % 3 == 0) { xsane_gimp_advance(); } } } else /* gamma correction by scanner */ { for (i = 0; i < len; ++i) { xsane.tile[xsane.tile_offset++] = buf8[i]; if (xsane.tile_offset % 3 == 0) { xsane_gimp_advance(); } } } break; default: goto bad_depth; break; } } #endif /* HAVE_LIBGIMP_GIMP_H */ break; case SANE_FRAME_RED: case SANE_FRAME_GREEN: case SANE_FRAME_BLUE: if (xsane.mode == XSANE_STANDALONE) { if (!xsane.scanner_gamma_color) /* gamma correction by xsane */ { char val; SANE_Int *gamma; if (xsane.param.format == SANE_FRAME_RED) { gamma = xsane.gamma_data_red; } else if (xsane.param.format == SANE_FRAME_GREEN) { gamma = xsane.gamma_data_green; } else { gamma = xsane.gamma_data_blue; } for (i = 0; i < len; ++i) { val = gamma[(int) buf8[i]]; fwrite(&val, 1, 1, xsane.out); fseek(xsane.out, 2, SEEK_CUR); } } else /* gamma correction by scanner */ { for (i = 0; i < len; ++i) { fwrite(&buf8[i], 1, 1, xsane.out); fseek(xsane.out, 2, SEEK_CUR); } } } #ifdef HAVE_LIBGIMP_GIMP_H else /* GIMP MODE RED, GREEN, BLUE (3PASS) 8 bit */ { switch (xsane.param.depth) { case 1: for (i = 0; i < len; ++i) { u_char mask; int j; mask = buf8[i]; for (j = 0; j < 8; ++j) { u_char gl = (mask & 1) ? 0xff : 0x00; mask >>= 1; xsane.tile[xsane.tile_offset] = gl; xsane.tile_offset += 3; xsane_gimp_advance(); if (xsane.x == 0) { break; } } } break; case 8: if (!xsane.scanner_gamma_color) /* gamma correction by xsane */ { SANE_Int *gamma; if (xsane.param.format == SANE_FRAME_RED) { gamma = xsane.gamma_data_red; } else if (xsane.param.format == SANE_FRAME_GREEN) { gamma = xsane.gamma_data_green; } else { gamma = xsane.gamma_data_blue; } for (i = 0; i < len; ++i) { xsane.tile[xsane.tile_offset] = gamma[(int) buf8[i]]; xsane.tile_offset += 3; xsane_gimp_advance(); } } else /* gamma correction by scanner */ { for (i = 0; i < len; ++i) { xsane.tile[xsane.tile_offset] = buf8[i]; xsane.tile_offset += 3; xsane_gimp_advance(); } } break; default: goto bad_depth; break; } } #endif /* HAVE_LIBGIMP_GIMP_H */ break; #ifdef SUPPORT_RGBA case SANE_FRAME_RGBA: /* Scanning including Infrared channel */ if (xsane.mode == XSANE_STANDALONE) { int i; char val; if (!xsane.scanner_gamma_color) /* gamma correction by xsane */ { for (i=0; i < len; ++i) { if (dialog->pixelcolor == 0) { val = xsane.gamma_data_red[(int) buf8[i]]; dialog->pixelcolor++; } else if (dialog->pixelcolor == 1) { val = xsane.gamma_data_green[(int) buf8[i]]; dialog->pixelcolor++; } else if (dialog->pixelcolor == 2) { val = xsane.gamma_data_blue[(int) buf8[i]]; dialog->pixelcolor++; } else { val = buf8[i]; /* no gamma table for infrared channel */ dialog->pixelcolor = 0; } fwrite(&val, 1, 1, xsane.out); } } else /* gamma correction has been done by scanner */ { fwrite(buf8, 1, len, xsane.out); } } #ifdef HAVE_LIBGIMP_GIMP_H else /* GIMP MODE RGBA 8 bit */ { int i; switch (xsane.param.depth) { case 8: if (!xsane.scanner_gamma_color) /* gamma correction by xsane */ { for (i=0; i < len; ++i) { if (xsane.tile_offset % 4 == 0) { xsane.tile[xsane.tile_offset++] = xsane.gamma_data_red[(int) buf8[i]]; } else if (xsane.tile_offset % 4 == 1) { xsane.tile[xsane.tile_offset++] = xsane.gamma_data_green[(int) buf8[i]]; } else if (xsane.tile_offset % 4 == 2) { xsane.tile[xsane.tile_offset++] = xsane.gamma_data_blue[(int) buf8[i]]; } else { xsane.tile[xsane.tile_offset++] = buf8[i]; /* no gamma table for infrared channel */ } if (xsane.tile_offset % 4 == 0) { xsane_gimp_advance(); } } } else /* gamma correction has been done by scanner */ { for (i = 0; i < len; ++i) { xsane.tile[xsane.tile_offset++] = buf8[i]; if (xsane.tile_offset % 4 == 0) { xsane_gimp_advance(); } } } break; default: goto bad_depth; break; } } #endif /* HAVE_LIBGIMP_GIMP_H */ break; #endif default: xsane_scan_done(-1); /* -1 = error */ fprintf(stderr, "xsane_read_image_data: %s %d\n", ERR_BAD_FRAME_FORMAT, xsane.param.format); return; break; } } } else if ( xsane.param.depth == 16 ) { static guint16 buf16[32768]; char buf[255]; char last = 0; int offset = 0; while (1) { if (offset) /* if we have had an odd number of bytes */ { buf16[0] = last; status = sane_read(dev, (SANE_Byte *) (buf16 + 1), sizeof(buf16) - 1, &len); if (len) { len++; } } else /* last read we had an even number of bytes */ { status = sane_read(dev, (SANE_Byte *) buf16, sizeof(buf16), &len); } if (len % 2) /* odd number of bytes */ { len--; last = buf16[len]; offset = 1; } else /* even number of bytes */ { offset = 0; } if (status == SANE_STATUS_EOF) { if (!xsane.param.last_frame) { xsane_start_scan(); break; /* leave while loop */ } xsane_scan_done(SANE_STATUS_EOF); /* image complete, stop scanning */ return; } if (status != SANE_STATUS_GOOD) { xsane_scan_done(status); /* status = return of sane_read */ snprintf(buf, sizeof(buf), "%s %s.", ERR_DURING_READ, XSANE_STRSTATUS(status)); xsane_back_gtk_error(buf, TRUE); return; } if (!len) /* nothing read */ { break; /* out of data for now, leave while loop */ } xsane.bytes_read += len; xsane_progress_update(xsane.progress, xsane.bytes_read / (gfloat) xsane.num_bytes); if (xsane.input_tag < 0) { while (gtk_events_pending()) { gtk_main_iteration(); } } switch (xsane.param.format) { case SANE_FRAME_GRAY: if (xsane.mode == XSANE_STANDALONE) { int i; guint16 val; if (!xsane.scanner_gamma_gray) /* gamma correction by xsane */ { for (i=0; i < len/2; ++i) { val = xsane.gamma_data[buf16[i]]; fwrite(&val, 2, 1, xsane.out); } } else /* gamma correction by scanner */ { fwrite(buf16, 2, len/2, xsane.out); } } break; case SANE_FRAME_RGB: if (xsane.mode == XSANE_STANDALONE) { int i; guint16 val; if (!xsane.scanner_gamma_color) /* gamma correction by xsane */ { for (i=0; i < len/2; ++i) { if (dialog->pixelcolor == 0) { val = xsane.gamma_data_red[buf16[i]]; dialog->pixelcolor++; } else if (dialog->pixelcolor == 1) { val = xsane.gamma_data_green[buf16[i]]; dialog->pixelcolor++; } else { val = xsane.gamma_data_blue[buf16[i]]; dialog->pixelcolor = 0; } fwrite(&val, 2, 1, xsane.out); } } else /* gamma correction by scanner */ { fwrite(buf16, 2, len/2, xsane.out); } } break; case SANE_FRAME_RED: case SANE_FRAME_GREEN: case SANE_FRAME_BLUE: if (xsane.mode == XSANE_STANDALONE) { for (i = 0; i < len/2; ++i) { fwrite(buf16 + i*2, 2, 1, xsane.out); fseek(xsane.out, 4, SEEK_CUR); } } break; #ifdef SUPPORT_RGBA case SANE_FRAME_RGBA: if (xsane.mode == XSANE_STANDALONE) { int i; guint16 val; if (!xsane.scanner_gamma_color) { for (i=0; i < len/2; ++i) { if (dialog->pixelcolor == 0) { val = xsane.gamma_data_red[buf16[i]]; dialog->pixelcolor++; } else if (dialog->pixelcolor == 1) { val = xsane.gamma_data_green[buf16[i]]; dialog->pixelcolor++; } else if (dialog->pixelcolor == 2) { val = xsane.gamma_data_blue[buf16[i]]; dialog->pixelcolor++; } else { val = buf16[i]; /* no gamma table for infrared channel */ dialog->pixelcolor = 0; } fwrite(&val, 2, 1, xsane.out); } } else { fwrite(buf16, 2, len/2, xsane.out); } } break; #endif default: xsane_scan_done(-1); /* -1 = error */ fprintf(stderr, "xsane_read_image_data: %s %d\n", ERR_BAD_FRAME_FORMAT, xsane.param.format); return; break; } } } else { xsane_scan_done(-1); /* -1 = error */ snprintf(buf, sizeof(buf), "%s %d.", ERR_BAD_DEPTH, xsane.param.depth); xsane_back_gtk_error(buf, TRUE); return; } return; /* ---------------------- */ #ifdef HAVE_LIBGIMP_GIMP_H bad_depth: xsane_scan_done(-1); /* -1 = error */ snprintf(buf, sizeof(buf), "%s %d.", ERR_BAD_DEPTH, xsane.param.depth); xsane_back_gtk_error(buf, TRUE); return; #endif } /* ---------------------------------------------------------------------------------------------------------------------- */ static RETSIGTYPE xsane_sigpipe_handler(int signal) /* this is to catch a broken pipe while writing to printercommand */ { xsane_cancel_save(0); xsane.broken_pipe = 1; } /* ---------------------------------------------------------------------------------------------------------------------- */ static int xsane_test_multi_scan(void) { char *set; SANE_Status status; const SANE_Option_Descriptor *opt; opt = sane_get_option_descriptor(dialog->dev, dialog->well_known.scansource); if (opt) { if (SANE_OPTION_IS_ACTIVE(opt->cap)) { if (opt->constraint_type == SANE_CONSTRAINT_STRING_LIST) { set = malloc(opt->size); status = sane_control_option(dialog->dev, dialog->well_known.scansource, SANE_ACTION_GET_VALUE, set, 0); if (status == SANE_STATUS_GOOD) { if (!strcmp(set, SANE_NAME_DOCUMENT_FEEDER)) { return TRUE; } } free(set); } } } #if 0 /* this is planned for the next sane-standard */ if (xsane.param.bitfield & XSANE_PARAM_STATUS_MORE_IMAGES) { return TRUE; } #endif return FALSE; } /* ---------------------------------------------------------------------------------------------------------------------- */ void xsane_scan_done(SANE_Status status) { if (xsane.input_tag >= 0) { gdk_input_remove(xsane.input_tag); xsane.input_tag = -1; } if (xsane.progress) /* remove progressbar */ { xsane_progress_free(xsane.progress); xsane.progress = 0; } while(gtk_events_pending()) /* let gtk remove the progress bar and update everything that needs it */ { gtk_main_iteration(); } /* we have to free the gamma tables if we used software gamma correction */ if (xsane.gamma_data) { free(xsane.gamma_data); xsane.gamma_data = 0; } if (xsane.gamma_data_red) { free(xsane.gamma_data_red); free(xsane.gamma_data_green); free(xsane.gamma_data_blue); xsane.gamma_data_red = 0; xsane.gamma_data_green = 0; xsane.gamma_data_blue = 0; } if (xsane.out) /* close file - this is dummy_file but if there is no conversion it is the wanted file */ { fclose(xsane.out); xsane.out = 0; } if ( (status == SANE_STATUS_GOOD) || (status == SANE_STATUS_EOF) ) /* no error, do conversion etc. */ { if (xsane.mode == XSANE_STANDALONE) { if ( (xsane.xsane_mode == XSANE_SCAN) && (xsane.xsane_output_format != XSANE_PNM) && (xsane.xsane_output_format != XSANE_RAW16) && (xsane.xsane_output_format != XSANE_RGBA) ) { FILE *outfile; FILE *infile; char buf[256]; /* open progressbar */ snprintf(buf, sizeof(buf), PROGRESS_SAVING); xsane.progress = xsane_progress_new(PROGRESS_CONVERTING_DATA, buf, (GtkSignalFunc) xsane_cancel_save, 0); xsane_progress_update(xsane.progress, 0); while (gtk_events_pending()) { gtk_main_iteration(); } infile = fopen(xsane.dummy_filename, "r"); if (infile != 0) { fseek(infile, xsane.header_size, SEEK_SET); #ifdef HAVE_LIBTIFF if (xsane.xsane_output_format == XSANE_TIFF) /* routines that want to have filename for saving */ { if (xsane.param.depth != 1) { remove(xsane.output_filename); umask(preferences.image_umask); /* define image file permissions */ xsane_save_tiff(xsane.output_filename, infile, xsane.xsane_color, xsane.param.depth, xsane.param.pixels_per_line, xsane.param.lines, preferences.tiff_compression_nr, preferences.jpeg_quality); umask(XSANE_DEFAULT_UMASK); /* define new file permissions */ } else { remove(xsane.output_filename); umask(preferences.image_umask); /* define image file permissions */ xsane_save_tiff(xsane.output_filename, infile, xsane.xsane_color, xsane.param.depth, xsane.param.pixels_per_line, xsane.param.lines, preferences.tiff_compression_1_nr, preferences.jpeg_quality); umask(XSANE_DEFAULT_UMASK); /* define new file permissions */ } } else /* routines that want to have filedescriptor for saving */ #endif { remove(xsane.output_filename); umask(preferences.image_umask); /* define image file permissions */ outfile = fopen(xsane.output_filename, "w"); umask(XSANE_DEFAULT_UMASK); /* define new file permissions */ if (outfile != 0) { switch(xsane.xsane_output_format) { #ifdef HAVE_LIBJPEG case XSANE_JPEG: xsane_save_jpeg(outfile, infile, xsane.xsane_color, xsane.param.depth, xsane.param.pixels_per_line, xsane.param.lines, preferences.jpeg_quality); break; #endif #ifdef HAVE_LIBPNG #ifdef HAVE_LIBZ case XSANE_PNG: if (xsane.param.depth <= 8) { xsane_save_png(outfile, infile, xsane.xsane_color, xsane.param.depth, xsane.param.pixels_per_line, xsane.param.lines, preferences.png_compression); } else { xsane_save_png_16(outfile, infile, xsane.xsane_color, xsane.param.depth, xsane.param.pixels_per_line, xsane.param.lines, preferences.png_compression); } break; #endif #endif case XSANE_PNM16: xsane_save_pnm_16(outfile, infile, xsane.xsane_color, xsane.param.depth, xsane.param.pixels_per_line, xsane.param.lines); break; case XSANE_PS: /* save postscript, use original size */ { float imagewidth = xsane.param.pixels_per_line/xsane.resolution_x; /* width in inch */ float imageheight = xsane.param.lines/xsane.resolution_y; /* height in inch */ if (preferences.psrotate) /* rotate: landscape */ { xsane_save_ps(outfile, infile, xsane.xsane_color /* gray, color */, xsane.param.depth /* bits */, xsane.param.pixels_per_line, xsane.param.lines, /* pixel_width, pixel_height */ (preferences.printer[preferences.printernr]->bottomoffset + preferences.printer[preferences.printernr]->height) * 36.0/MM_PER_INCH - imagewidth * 36.0, /* left edge */ (preferences.printer[preferences.printernr]->leftoffset + preferences.printer[preferences.printernr]->width) * 36.0/MM_PER_INCH - imageheight * 36.0, /* bottom edge */ imagewidth, imageheight, (preferences.printer[preferences.printernr]->leftoffset + preferences.printer[preferences.printernr]->width ) * 72.0/MM_PER_INCH, /* paperwidth */ (preferences.printer[preferences.printernr]->bottomoffset + preferences.printer[preferences.printernr]->height) * 72.0/MM_PER_INCH, /* paperheight */ 1 /* landscape */); } else /* do not rotate: portrait */ { xsane_save_ps(outfile, infile, xsane.xsane_color /* gray, color */, xsane.param.depth /* bits */, xsane.param.pixels_per_line, xsane.param.lines, /* pixel_width, pixel_height */ (preferences.printer[preferences.printernr]->leftoffset + preferences.printer[preferences.printernr]->width) * 36.0/MM_PER_INCH - imagewidth * 36.0, (preferences.printer[preferences.printernr]->bottomoffset + preferences.printer[preferences.printernr]->height) * 36.0/MM_PER_INCH - imageheight * 36.0, imagewidth, imageheight, (preferences.printer[preferences.printernr]->leftoffset + preferences.printer[preferences.printernr]->width ) * 72.0/MM_PER_INCH, /* paperwidth */ (preferences.printer[preferences.printernr]->bottomoffset + preferences.printer[preferences.printernr]->height) * 72.0/MM_PER_INCH, /* paperheight */ 0 /* portrait */); } } break; default: snprintf(buf, sizeof(buf),"%s", ERR_UNKNOWN_SAVING_FORMAT); xsane_back_gtk_error(buf, TRUE); break; } fclose(outfile); } else { char buf[256]; snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, xsane.output_filename, strerror(errno)); xsane_back_gtk_error(buf, TRUE); } } fclose(infile); remove(xsane.dummy_filename); } else { char buf[256]; snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, xsane.output_filename, strerror(errno)); xsane_back_gtk_error(buf, TRUE); } xsane_progress_free(xsane.progress); xsane.progress = 0; while (gtk_events_pending()) { gtk_main_iteration(); } } else if (xsane.xsane_mode == XSANE_COPY) { FILE *outfile; FILE *infile; char buf[256]; xsane_update_int(xsane.copy_number_entry, &xsane.copy_number); /* get number of copies */ if (xsane.copy_number < 1) { xsane.copy_number = 1; } /* open progressbar */ snprintf(buf, sizeof(buf), PROGRESS_CONVERTING_PS); xsane.progress = xsane_progress_new(PROGRESS_CONVERTING_DATA, buf, (GtkSignalFunc) xsane_cancel_save, 0); xsane_progress_update(xsane.progress, 0); while (gtk_events_pending()) { gtk_main_iteration(); } xsane.broken_pipe = 0; infile = fopen(xsane.dummy_filename, "r"); snprintf(buf, sizeof(buf), "%s %s%d", preferences.printer[preferences.printernr]->command, preferences.printer[preferences.printernr]->copy_number_option, xsane.copy_number); outfile = popen(buf, "w"); /* outfile = popen(preferences.printer[preferences.printernr]->command, "w"); */ if ((outfile != 0) && (infile != 0)) /* copy mode, use zoom size */ { struct SIGACTION act; float imagewidth = xsane.param.pixels_per_line/(float)preferences.printer[preferences.printernr]->resolution; /* width in inch */ float imageheight = xsane.param.lines/(float)preferences.printer[preferences.printernr]->resolution; /* height in inch */ memset (&act, 0, sizeof (act)); /* define broken pipe handler */ act.sa_handler = xsane_sigpipe_handler; sigaction (SIGPIPE, &act, 0); fseek(infile, xsane.header_size, SEEK_SET); if (preferences.psrotate) /* rotate: landscape */ { xsane_save_ps(outfile, infile, xsane.xsane_color /* gray, color */, xsane.param.depth /* bits */, xsane.param.pixels_per_line, xsane.param.lines, /* pixel_width, pixel_height */ (preferences.printer[preferences.printernr]->bottomoffset + preferences.printer[preferences.printernr]->height) * 36.0/MM_PER_INCH - imagewidth * 36.0, /* left edge */ (preferences.printer[preferences.printernr]->leftoffset + preferences.printer[preferences.printernr]->width) * 36.0/MM_PER_INCH - imageheight * 36.0, /* bottom edge */ imagewidth, imageheight, (preferences.printer[preferences.printernr]->leftoffset + preferences.printer[preferences.printernr]->width ) * 72.0/MM_PER_INCH, /* paperwidth */ (preferences.printer[preferences.printernr]->bottomoffset + preferences.printer[preferences.printernr]->height) * 72.0/MM_PER_INCH, /* paperheight */ 1 /* landscape */); } else /* do not rotate: portrait */ { xsane_save_ps(outfile, infile, xsane.xsane_color /* gray, color */, xsane.param.depth /* bits */, xsane.param.pixels_per_line, xsane.param.lines, /* pixel_width, pixel_height */ (preferences.printer[preferences.printernr]->leftoffset + preferences.printer[preferences.printernr]->width) * 36.0/MM_PER_INCH - imagewidth * 36.0, /* left edge */ (preferences.printer[preferences.printernr]->bottomoffset + preferences.printer[preferences.printernr]->height) * 36.0/MM_PER_INCH - imageheight * 36.0, /* bottom edge */ imagewidth, imageheight, (preferences.printer[preferences.printernr]->leftoffset + preferences.printer[preferences.printernr]->width ) * 72.0/MM_PER_INCH, /* paperwidth */ (preferences.printer[preferences.printernr]->bottomoffset + preferences.printer[preferences.printernr]->height) * 72.0/MM_PER_INCH, /* paperheight */ 0 /* portrait */); } } else { char buf[256]; if (!infile) { snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, xsane.output_filename, strerror(errno)); xsane_back_gtk_error(buf, TRUE); } else if (!outfile) { xsane_back_gtk_error(ERR_FAILED_PRINTER_PIPE, TRUE); } } if (xsane.broken_pipe) { snprintf(buf, sizeof(buf), "%s \"%s\"", ERR_FAILED_EXEC_PRINTER_CMD, preferences.printer[preferences.printernr]->command); xsane_back_gtk_error(buf, TRUE); } xsane_progress_free(xsane.progress); xsane.progress = 0; while (gtk_events_pending()) { gtk_main_iteration(); } if (infile) { fclose(infile); remove(xsane.dummy_filename); } if (outfile) { pclose(outfile); } } else if (xsane.xsane_mode == XSANE_FAX) { FILE *outfile; FILE *infile; char buf[256]; /* open progressbar */ snprintf(buf, sizeof(buf), PROGRESS_SAVING_FAX); xsane.progress = xsane_progress_new(PROGRESS_CONVERTING_DATA, buf, (GtkSignalFunc) xsane_cancel_save, 0); xsane_progress_update(xsane.progress, 0); while (gtk_events_pending()) { gtk_main_iteration(); } infile = fopen(xsane.dummy_filename, "r"); if (infile != 0) { fseek(infile, xsane.header_size, SEEK_SET); umask(preferences.image_umask); /* define image file permissions */ outfile = fopen(xsane.fax_filename, "w"); umask(XSANE_DEFAULT_UMASK); /* define new file permissions */ if (outfile != 0) { float imagewidth, imageheight; imagewidth = xsane.param.pixels_per_line/xsane.resolution_x; /* width in inch */ imageheight = xsane.param.lines/xsane.resolution_y; /* height in inch */ /* disabled ( 0 * ...) in the moment */ if (0 * preferences.psrotate) /* rotate: landscape */ { xsane_save_ps(outfile, infile, xsane.xsane_color /* gray, color */, xsane.param.depth /* bits */, xsane.param.pixels_per_line, xsane.param.lines, /* pixel_width, pixel_height */ (preferences.fax_bottomoffset + preferences.fax_height) * 36.0/MM_PER_INCH - imagewidth * 36.0, /* left edge */ (preferences.fax_leftoffset + preferences.fax_width) * 36.0/MM_PER_INCH - imageheight * 36.0, /* bottom edge */ imagewidth, imageheight, (preferences.fax_leftoffset + preferences.fax_width ) * 72.0/MM_PER_INCH, /* paperwidth */ (preferences.fax_bottomoffset + preferences.fax_height) * 72.0/MM_PER_INCH, /* paperheight */ 1 /* landscape */); } else /* do not rotate: portrait */ { xsane_save_ps(outfile, infile, xsane.xsane_color /* gray, color */, xsane.param.depth /* bits */, xsane.param.pixels_per_line, xsane.param.lines, /* pixel_width, pixel_height */ (preferences.fax_leftoffset + preferences.fax_width) * 36.0/MM_PER_INCH - imagewidth * 36.0, (preferences.fax_bottomoffset + preferences.fax_height) * 36.0/MM_PER_INCH - imageheight * 36.0, imagewidth, imageheight, (preferences.fax_leftoffset + preferences.fax_width ) * 72.0/MM_PER_INCH, /* paperwidth */ (preferences.fax_bottomoffset + preferences.fax_height) * 72.0/MM_PER_INCH, /* paperheight */ 0 /* portrait */); } fclose(outfile); } else { char buf[256]; snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, xsane.fax_filename, strerror(errno)); xsane_back_gtk_error(buf, TRUE); } fclose(infile); remove(xsane.dummy_filename); } else { char buf[256]; snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, xsane.fax_filename, strerror(errno)); xsane_back_gtk_error(buf, TRUE); } xsane_progress_free(xsane.progress); xsane.progress = 0; while (gtk_events_pending()) { gtk_main_iteration(); } } } #ifdef HAVE_LIBGIMP_GIMP_H else { int remaining; /* GIMP mode */ if (xsane.y > xsane.param.lines) { xsane.y = xsane.param.lines; } remaining = xsane.y % gimp_tile_height(); if (remaining) { gimp_pixel_rgn_set_rect(&xsane.region, xsane.tile, 0, xsane.y - remaining, xsane.param.pixels_per_line, remaining); } gimp_drawable_flush(xsane.drawable); gimp_display_new(xsane.image_ID); gimp_drawable_detach(xsane.drawable); free(xsane.tile); xsane.tile = 0; } #endif /* HAVE_LIBGIMP_GIMP_H */ xsane.header_size = 0; if ( (preferences.increase_filename_counter) && (xsane.xsane_mode == XSANE_SCAN) && (xsane.mode == XSANE_STANDALONE) ) { xsane_increase_counter_in_filename(preferences.filename, preferences.skip_existing_numbers); gtk_entry_set_text(GTK_ENTRY(xsane.outputfilename_entry), (char *) preferences.filename); } else if (xsane.xsane_mode == XSANE_FAX) { GtkWidget *list_item; char *page; char *extension; page = strdup(strrchr(xsane.fax_filename,'/')+1); extension = strrchr(page, '.'); if (extension) { *extension = 0; } list_item = gtk_list_item_new_with_label(page); gtk_object_set_data(GTK_OBJECT(list_item), "list_item_data", strdup(page)); gtk_container_add(GTK_CONTAINER(xsane.fax_list), list_item); gtk_widget_show(list_item); xsane_increase_counter_in_filename(xsane.fax_filename, preferences.skip_existing_numbers); xsane_fax_project_save(); free(page); } } else /* an error occured, remove the dummy_file */ { if (xsane.dummy_filename) /* remove corrupt file */ { remove(xsane.dummy_filename); } } free(xsane.dummy_filename); /* no dummy_filename, needed if an error occurs */ xsane.dummy_filename = 0; if (xsane.output_filename) { free(xsane.output_filename); xsane.output_filename = 0; } if ( ( (status == SANE_STATUS_GOOD) || (status == SANE_STATUS_EOF) ) && (xsane_test_multi_scan()) ) { /* multi scan (eg ADF): scan again */ /* stopped when: */ /* a) xsane_test_multi_scan returns false */ /* b) sane_start returns SANE_STATUS_NO_DOCS */ /* c) an error occurs */ gtk_signal_emit_by_name(xsane.start_button, "clicked"); /* press START button */ } else /* last scan: update histogram */ { xsane_set_sensitivity(TRUE); /* reactivate buttons etc */ sane_cancel(xsane_back_gtk_dialog_get_device(dialog)); /* stop scanning */ xsane_update_histogram(); } } /* ---------------------------------------------------------------------------------------------------------------------- */ void xsane_cancel(void) { sane_cancel(xsane_back_gtk_dialog_get_device(dialog)); } /* ---------------------------------------------------------------------------------------------------------------------- */ static void xsane_start_scan(void) { SANE_Status status; SANE_Handle dev = xsane_back_gtk_dialog_get_device(dialog); const char *frame_type = 0; char buf[256]; int fd; xsane_clear_histogram(&xsane.histogram_raw); xsane_clear_histogram(&xsane.histogram_enh); xsane_set_sensitivity(FALSE); #ifdef HAVE_LIBGIMP_GIMP_H if (xsane.mode == XSANE_GIMP_EXTENSION && xsane.tile) { int height, remaining; /* write the last tile of the frame to the GIMP region: */ if (xsane.y > xsane.param.lines) /* sanity check */ { xsane.y = xsane.param.lines; } remaining = xsane.y % gimp_tile_height(); if (remaining) { gimp_pixel_rgn_set_rect(&xsane.region, xsane.tile, 0, xsane.y - remaining, xsane.param.pixels_per_line, remaining); } /* initialize the tile with the first tile of the GIMP region: */ height = gimp_tile_height(); if (height >= xsane.param.lines) { height = xsane.param.lines; } gimp_pixel_rgn_get_rect(&xsane.region, xsane.tile, 0, 0, xsane.param.pixels_per_line, height); } #endif /* HAVE_LIBGIMP_GIMP_H */ xsane.x = xsane.y = 0; status = sane_start(dev); if (status == SANE_STATUS_NO_DOCS) /* ADF out of docs */ { xsane_scan_done(status); /* ok, stop multi image scan */ return; } else if (status != SANE_STATUS_GOOD) /* error */ { xsane_scan_done(status); snprintf(buf, sizeof(buf), "%s %s", ERR_FAILED_START_SCANNER, XSANE_STRSTATUS(status)); xsane_back_gtk_error(buf, TRUE); return; } status = sane_get_parameters(dev, &xsane.param); if (status != SANE_STATUS_GOOD) { xsane_scan_done(status); snprintf(buf, sizeof(buf), "%s %s", ERR_FAILED_GET_PARAMS, XSANE_STRSTATUS(status)); xsane_back_gtk_error(buf, TRUE); return; } xsane.num_bytes = xsane.param.lines * xsane.param.bytes_per_line; xsane.bytes_read = 0; switch (xsane.param.format) { case SANE_FRAME_RGB: frame_type = "RGB"; break; case SANE_FRAME_RED: frame_type = "red"; break; case SANE_FRAME_GREEN: frame_type = "green"; break; case SANE_FRAME_BLUE: frame_type = "blue"; break; case SANE_FRAME_GRAY: frame_type = "gray"; break; #ifdef SUPPORT_RGBA case SANE_FRAME_RGBA: frame_type = "RGBA"; break; #endif default: frame_type = "unknown"; break; } if (xsane.mode == XSANE_STANDALONE) { /* We are running in standalone mode */ if (xsane_generate_dummy_filename()) /* create filename the scanned data is saved to */ { /* temporary file */ umask(0177); /* creare temporary file with "-rw-------" permissions */ } else { /* no temporary file */ umask(preferences.image_umask); /* define image file permissions */ } if (!xsane.header_size) /* first pass of multi pass scan */ { remove(xsane.dummy_filename); /* remove existing file */ xsane.out = fopen(xsane.dummy_filename, "w"); umask(XSANE_DEFAULT_UMASK); /* define new file permissions */ if (!xsane.out) /* error while opening the dummy_file for writing */ { xsane_scan_done(-1); /* -1 = error */ snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, xsane.output_filename, strerror(errno)); xsane_back_gtk_error(buf, TRUE); return; } switch (xsane.param.format) { case SANE_FRAME_RGB: case SANE_FRAME_RED: case SANE_FRAME_GREEN: case SANE_FRAME_BLUE: switch (xsane.param.depth) { case 8: /* color 8 bit mode, write ppm header */ fprintf(xsane.out, "P6\n# SANE data follows\n%d %d\n255\n", xsane.param.pixels_per_line, xsane.param.lines); break; default: /* color, but not 8 bit mode, write as raw data because this is not defined in pnm */ fprintf(xsane.out, "SANE_RGB_RAW\n%d %d\n65535\n", xsane.param.pixels_per_line, xsane.param.lines); break; } break; case SANE_FRAME_GRAY: switch (xsane.param.depth) { case 1: /* 1 bit lineart mode, write pbm header */ fprintf(xsane.out, "P4\n# SANE data follows\n%d %d\n", xsane.param.pixels_per_line, xsane.param.lines); break; case 8: /* 8 bit grayscale mode, write pgm header */ fprintf(xsane.out, "P5\n# SANE data follows\n%d %d\n255\n", xsane.param.pixels_per_line, xsane.param.lines); break; default: /* grayscale mode but not 1 or 8 bit, write as raw data because this is not defined in pnm */ fprintf(xsane.out, "SANE_GRAYSCALE_RAW\n%d %d\n65535\n", xsane.param.pixels_per_line, xsane.param.lines); break; } break; #ifdef SUPPORT_RGBA case SANE_FRAME_RGBA: switch (xsane.param.depth) { case 8: /* 8 bit RGBA mode */ fprintf(xsane.out, "SANE_RGBA\n%d %d\n255\n", xsane.param.pixels_per_line, xsane.param.lines); break; default: /* 16 bit RGBA mode */ fprintf(xsane.out, "SANE_RGBA\n%d %d\n65535\n", xsane.param.pixels_per_line, xsane.param.lines); break; } break; #endif default: /* unknown file format, do not write header */ break; } fflush(xsane.out); xsane.header_size = ftell(xsane.out); } if (xsane.param.format >= SANE_FRAME_RED && xsane.param.format <= SANE_FRAME_BLUE) { fseek(xsane.out, xsane.header_size + xsane.param.format - SANE_FRAME_RED, SEEK_SET); } if (xsane.xsane_mode == XSANE_SCAN) { snprintf(buf, sizeof(buf), PROGRESS_RECEIVING_SCAN, _(frame_type), xsane.output_filename); } else if (xsane.xsane_mode == XSANE_COPY) { snprintf(buf, sizeof(buf), PROGRESS_RECEIVING_COPY, _(frame_type)); } else if (xsane.xsane_mode == XSANE_FAX) { snprintf(buf, sizeof(buf), PROGRESS_RECEIVING_FAX, _(frame_type)); } } #ifdef HAVE_LIBGIMP_GIMP_H else { size_t tile_size; /* We are running under the GIMP */ xsane.tile_offset = 0; tile_size = xsane.param.pixels_per_line * gimp_tile_height(); switch(xsane.param.format) { case SANE_FRAME_RGB: case SANE_FRAME_RED: case SANE_FRAME_BLUE: case SANE_FRAME_GREEN: tile_size *= 3; /* 24 bits/pixel RGB */ break; #ifdef SUPPORT_RGBA case SANE_FRAME_RGBA: tile_size *= 4; /* 32 bits/pixel RGBA */ break; #endif default: break; } if (xsane.tile) { xsane.first_frame = 0; } else { GImageType image_type = RGB; GDrawableType drawable_type = RGB_IMAGE; gint32 layer_ID; if (xsane.param.format == SANE_FRAME_GRAY) { image_type = GRAY; drawable_type = GRAY_IMAGE; } #ifdef SUPPORT_RGBA else if (xsane.param.format == SANE_FRAME_RGBA) { image_type = RGB; drawable_type = RGBA_IMAGE; /* interpret infrared as alpha */ } #endif xsane.image_ID = gimp_image_new(xsane.param.pixels_per_line, xsane.param.lines, image_type); /* the following is supported since gimp-1.1.? */ #ifdef GIMP_HAVE_RESOLUTION_INFO if (xsane.resolution_x > 0) { gimp_image_set_resolution(xsane.image_ID, xsane.resolution_x ,xsane.resolution_y); } /* gimp_image_set_unit(xsane.image_ID, unit?); */ #endif layer_ID = gimp_layer_new(xsane.image_ID, "Background", xsane.param.pixels_per_line, xsane.param.lines, drawable_type, 100, NORMAL_MODE); gimp_image_add_layer(xsane.image_ID, layer_ID, 0); xsane.drawable = gimp_drawable_get(layer_ID); gimp_pixel_rgn_init(&xsane.region, xsane.drawable, 0, 0, xsane.drawable->width, xsane.drawable->height, TRUE, FALSE); xsane.tile = g_new(guchar, tile_size); xsane.first_frame = 1; } if (xsane.param.format >= SANE_FRAME_RED && xsane.param.format <= SANE_FRAME_BLUE) { xsane.tile_offset = xsane.param.format - SANE_FRAME_RED; } snprintf(buf, sizeof(buf), PROGRESS_RECEIVING_GIMP, _(frame_type)); } #endif /* HAVE_LIBGIMP_GIMP_H */ dialog->pixelcolor = 0; if (xsane.progress) { xsane_progress_free(xsane.progress); } xsane.progress = xsane_progress_new(PROGRESS_SCANNING, buf, (GtkSignalFunc) xsane_cancel, 0); xsane.input_tag = -1; if (sane_set_io_mode(dev, SANE_TRUE) == SANE_STATUS_GOOD && sane_get_select_fd(dev, &fd) == SANE_STATUS_GOOD) { xsane.input_tag = gdk_input_add(fd, GDK_INPUT_READ, xsane_read_image_data, 0); } else { xsane_read_image_data(0, -1, GDK_INPUT_READ); } } /* ---------------------------------------------------------------------------------------------------------------------- */ /* Invoked when the scan button is pressed */ /* or by scan_done if automatic document feeder is selected */ void xsane_scan_dialog(GtkWidget * widget, gpointer call_data) { char buf[256]; sane_get_parameters(dialog->dev, &xsane.param); /* update xsane.param */ if (xsane.output_filename) { free(xsane.output_filename); xsane.output_filename = 0; } if (xsane.filetype) { char buffer[256]; snprintf(buffer, sizeof(buffer), "%s%s", preferences.filename, xsane.filetype); xsane.output_filename = strdup(buffer); } else { xsane.output_filename = strdup(preferences.filename); } if (xsane.mode == XSANE_STANDALONE) /* We are running in standalone mode */ { char *extension; if ( (xsane.xsane_mode == XSANE_SCAN) && (preferences.overwrite_warning) ) /* test if filename already used */ { FILE *testfile; testfile = fopen(xsane.output_filename, "r"); if (testfile) /* filename used: skip */ { char buf[256]; fclose(testfile); snprintf(buf, sizeof(buf), "File %s already exists\n", xsane.output_filename); if (xsane_back_gtk_decision(ERR_HEADER_WARNING, (gchar **) warning_xpm, buf, BUTTON_OVERWRITE, BUTTON_CANCEL, TRUE /* wait */) == FALSE) { return; } } } extension = strrchr(xsane.output_filename, '.'); if (extension) { extension++; /* skip "." */ } xsane.xsane_output_format = XSANE_UNKNOWN; if (xsane.param.depth <= 8) { if (extension) { if ( (!strcasecmp(extension, "pnm")) || (!strcasecmp(extension, "ppm")) || (!strcasecmp(extension, "pgm")) || (!strcasecmp(extension, "pbm")) ) { xsane.xsane_output_format = XSANE_PNM; } #ifdef HAVE_LIBPNG #ifdef HAVE_LIBZ else if (!strcasecmp(extension, "png")) { xsane.xsane_output_format = XSANE_PNG; } #endif #endif #ifdef HAVE_LIBJPEG else if ( (!strcasecmp(extension, "jpg")) || (!strcasecmp(extension, "jpeg")) ) { xsane.xsane_output_format = XSANE_JPEG; } #endif else if (!strcasecmp(extension, "ps")) { xsane.xsane_output_format = XSANE_PS; } #ifdef HAVE_LIBTIFF else if ( (!strcasecmp(extension, "tif")) || (!strcasecmp(extension, "tiff")) ) { xsane.xsane_output_format = XSANE_TIFF; } #endif #ifdef SUPPORT_RGBA else if (!strcasecmp(extension, "rgba")) { xsane.xsane_output_format = XSANE_RGBA; } #endif } } else /* depth >8 bpp */ { if (extension) { if (!strcasecmp(extension, "raw")) { xsane.xsane_output_format = XSANE_RAW16; } else if ( (!strcasecmp(extension, "pnm")) || (!strcasecmp(extension, "ppm")) || (!strcasecmp(extension, "pgm")) || (!strcasecmp(extension, "pbm")) ) { xsane.xsane_output_format = XSANE_PNM16; } #ifdef HAVE_LIBPNG #ifdef HAVE_LIBZ else if (!strcasecmp(extension, "png")) { xsane.xsane_output_format = XSANE_PNG; } #endif #endif #ifdef SUPPORT_RGBA else if (!strcasecmp(extension, "rgba")) { xsane.xsane_output_format = XSANE_RGBA; } #endif } } if (xsane.xsane_mode == XSANE_SCAN) { if (xsane.xsane_output_format == XSANE_UNKNOWN) { if (extension) { snprintf(buf, sizeof(buf), "Unsupported %d-bit output format: %s", xsane.param.depth, extension); } else { snprintf(buf, sizeof(buf), "%s", ERR_NO_OUTPUT_FORMAT); } xsane_back_gtk_error(buf, TRUE); return; } #ifdef SUPPORT_RGBA else if ((xsane.xsane_output_format == XSANE_RGBA) && (xsane.param.format != SANE_FRAME_RGBA)) { snprintf(buf, sizeof(buf), "No RGBA data format !!!"); /* user selected output format RGBA, scanner uses other format */ xsane_back_gtk_error(buf, TRUE); return; } #endif } #ifdef SUPPORT_RGBA else if (xsane.param.format == SANE_FRAME_RGBA) /* no scanmode but format=rgba */ { snprintf(buf, sizeof(buf), "Special format RGBA only supported in scan mode !!!"); xsane_back_gtk_error(buf, TRUE); return; } #endif #ifdef SUPPORT_RGBA if (xsane.param.format == SANE_FRAME_RGBA) { if ( (xsane.xsane_output_format != XSANE_RGBA) && (xsane.xsane_output_format != XSANE_PNG) ) { snprintf(buf, sizeof(buf), "Image data of type SANE_FRAME_RGBA\ncan only be saved in rgba or png format"); xsane_back_gtk_error(buf, TRUE); return; } } #endif if (xsane.xsane_mode == XSANE_FAX) { mkdir(preferences.fax_project, 7*64 + 0*8 + 0); } } #ifdef HAVE_LIBGIMP_GIMP_H else /* We are running in gimp mode */ { if ((xsane.param.depth != 1) && (xsane.param.depth != 8)) /* not support bit depth ? */ { snprintf(buf, sizeof(buf), "%s %d.", ERR_GIMP_BAD_DEPTH, xsane.param.depth); xsane_back_gtk_error(buf, TRUE); return; } } #endif if (xsane.dummy_filename) /* no dummy filename defined - necessary if an error occurs */ { free(xsane.dummy_filename); xsane.dummy_filename = 0; } if (xsane.param.depth > 1) /* if depth > 1 use gamma correction */ { int size; int gamma_gray_size, gamma_red_size, gamma_green_size, gamma_blue_size; int gamma_gray_max, gamma_red_max, gamma_green_max, gamma_blue_max; const SANE_Option_Descriptor *opt; size = (int) pow(2, xsane.param.depth); gamma_gray_size = size; gamma_red_size = size; gamma_green_size = size; gamma_blue_size = size; size--; gamma_gray_max = size; gamma_red_max = size; gamma_green_max = size; gamma_blue_max = size; if (xsane.scanner_gamma_gray) /* gamma table for gray available */ { opt = sane_get_option_descriptor(dialog->dev, dialog->well_known.gamma_vector); gamma_gray_size = opt->size / sizeof(opt->type); gamma_gray_max = opt->constraint.range->max; } if (xsane.scanner_gamma_color) /* gamma table for red, green and blue available */ { double gamma_red, gamma_green, gamma_blue; /* ok, scanner color gamma function is supported, so we do all conversions about that */ /* we do not need any gamma tables while scanning, so we can free them after sending */ /* the data to the scanner */ /* if also gray gamma function is supported, set this to 1.0 to get the right colors */ if (xsane.scanner_gamma_gray) { xsane.gamma_data = malloc(gamma_gray_size * sizeof(SANE_Int)); xsane_create_gamma_curve(xsane.gamma_data, 0, 1.0, 0.0, 0.0, gamma_gray_size, gamma_gray_max); xsane_back_gtk_update_vector(dialog, dialog->well_known.gamma_vector, xsane.gamma_data); free(xsane.gamma_data); xsane.gamma_data = 0; } opt = sane_get_option_descriptor(dialog->dev, dialog->well_known.gamma_vector_r); gamma_red_size = opt->size / sizeof(opt->type); gamma_red_max = opt->constraint.range->max; opt = sane_get_option_descriptor(dialog->dev, dialog->well_known.gamma_vector_g); gamma_green_size = opt->size / sizeof(opt->type); gamma_green_max = opt->constraint.range->max; opt = sane_get_option_descriptor(dialog->dev, dialog->well_known.gamma_vector_b); gamma_blue_size = opt->size / sizeof(opt->type); gamma_blue_max = opt->constraint.range->max; xsane.gamma_data_red = malloc(gamma_red_size * sizeof(SANE_Int)); xsane.gamma_data_green = malloc(gamma_green_size * sizeof(SANE_Int)); xsane.gamma_data_blue = malloc(gamma_blue_size * sizeof(SANE_Int)); if (xsane.xsane_mode == XSANE_COPY) { gamma_red = xsane.gamma * xsane.gamma_red * preferences.printer[preferences.printernr]->gamma * preferences.printer[preferences.printernr]->gamma_red; gamma_green = xsane.gamma * xsane.gamma_green * preferences.printer[preferences.printernr]->gamma * preferences.printer[preferences.printernr]->gamma_green; gamma_blue = xsane.gamma * xsane.gamma_blue * preferences.printer[preferences.printernr]->gamma * preferences.printer[preferences.printernr]->gamma_blue; } else { gamma_red = xsane.gamma * xsane.gamma_red; gamma_green = xsane.gamma * xsane.gamma_green; gamma_blue = xsane.gamma * xsane.gamma_blue; } xsane_create_gamma_curve(xsane.gamma_data_red, xsane.negative, gamma_red, xsane.brightness + xsane.brightness_red, xsane.contrast + xsane.contrast_red, gamma_red_size, gamma_red_max); xsane_create_gamma_curve(xsane.gamma_data_green, xsane.negative, gamma_green, xsane.brightness + xsane.brightness_green, xsane.contrast + xsane.contrast_green, gamma_green_size, gamma_green_max); xsane_create_gamma_curve(xsane.gamma_data_blue, xsane.negative, gamma_blue, xsane.brightness + xsane.brightness_blue, xsane.contrast + xsane.contrast_blue , gamma_blue_size, gamma_blue_max); xsane_back_gtk_update_vector(dialog, dialog->well_known.gamma_vector_r, xsane.gamma_data_red); xsane_back_gtk_update_vector(dialog, dialog->well_known.gamma_vector_g, xsane.gamma_data_green); xsane_back_gtk_update_vector(dialog, dialog->well_known.gamma_vector_b, xsane.gamma_data_blue); free(xsane.gamma_data_red); free(xsane.gamma_data_green); free(xsane.gamma_data_blue); xsane.gamma_data_red = 0; xsane.gamma_data_green = 0; xsane.gamma_data_blue = 0; } else if (xsane.scanner_gamma_gray) /* only scanner gray gamma function available */ { double gamma; /* ok, the scanner only supports gray gamma function */ /* if we are doing a grayscale scan everyting is ok, */ /* for a color scan the software has to do the gamma correction set by the component slider */ if (xsane.xsane_mode == XSANE_COPY) { gamma = xsane.gamma * preferences.printer[preferences.printernr]->gamma; } else { gamma = xsane.gamma; } xsane.gamma_data = malloc(gamma_gray_size * sizeof(SANE_Int)); xsane_create_gamma_curve(xsane.gamma_data, xsane.negative, gamma, xsane.brightness, xsane.contrast, gamma_gray_size, gamma_gray_max); xsane_back_gtk_update_vector(dialog, dialog->well_known.gamma_vector, xsane.gamma_data); free(xsane.gamma_data); xsane.gamma_data = 0; if (xsane.xsane_color) /* ok, we are doing a colorscan */ { /* we have to create color gamma table for software conversion */ /* but we only have to use color slider values, because gray slider value */ /* is used by scanner gray gamma */ double gamma_red, gamma_green, gamma_blue; xsane.gamma_data_red = malloc(gamma_red_size * sizeof(SANE_Int)); xsane.gamma_data_green = malloc(gamma_green_size * sizeof(SANE_Int)); xsane.gamma_data_blue = malloc(gamma_blue_size * sizeof(SANE_Int)); if (xsane.xsane_mode == XSANE_COPY) { gamma_red = xsane.gamma_red * preferences.printer[preferences.printernr]->gamma_red; gamma_green = xsane.gamma_green * preferences.printer[preferences.printernr]->gamma_green; gamma_blue = xsane.gamma_blue * preferences.printer[preferences.printernr]->gamma_blue; } else { gamma_red = xsane.gamma_red; gamma_green = xsane.gamma_green; gamma_blue = xsane.gamma_blue; } xsane_create_gamma_curve(xsane.gamma_data_red, 0, gamma_red, xsane.brightness_red, xsane.contrast_red, gamma_red_size, gamma_red_max); xsane_create_gamma_curve(xsane.gamma_data_green, 0, gamma_green, xsane.brightness_green, xsane.contrast_green, gamma_green_size, gamma_green_max); xsane_create_gamma_curve(xsane.gamma_data_blue, 0, gamma_blue, xsane.brightness_blue, xsane.contrast_blue, gamma_blue_size, gamma_blue_max); /* gamma tables are freed after scan */ } } else /* scanner does not support any gamma correction */ { /* ok, we have to do it on our own */ if (xsane.xsane_color == 0) /* no color scan */ { double gamma; if (xsane.xsane_mode == XSANE_COPY) { gamma = xsane.gamma * preferences.printer[preferences.printernr]->gamma; } else { gamma = xsane.gamma; } xsane.gamma_data = malloc(gamma_gray_size * sizeof(SANE_Int)); xsane_create_gamma_curve(xsane.gamma_data, xsane.negative, gamma, xsane.brightness, xsane.contrast, gamma_gray_size, gamma_gray_max); /* gamma table is freed after scan */ } else /* color scan */ { double gamma_red, gamma_green, gamma_blue; /* ok, we have to combin gray and color slider values */ xsane.gamma_data_red = malloc(gamma_red_size * sizeof(SANE_Int)); xsane.gamma_data_green = malloc(gamma_green_size * sizeof(SANE_Int)); xsane.gamma_data_blue = malloc(gamma_blue_size * sizeof(SANE_Int)); if (xsane.xsane_mode == XSANE_COPY) { gamma_red = xsane.gamma * xsane.gamma_red * preferences.printer[preferences.printernr]->gamma * preferences.printer[preferences.printernr]->gamma_red; gamma_green = xsane.gamma * xsane.gamma_green * preferences.printer[preferences.printernr]->gamma * preferences.printer[preferences.printernr]->gamma_green; gamma_blue = xsane.gamma * xsane.gamma_blue * preferences.printer[preferences.printernr]->gamma * preferences.printer[preferences.printernr]->gamma_blue; } else { gamma_red = xsane.gamma * xsane.gamma_red; gamma_green = xsane.gamma * xsane.gamma_green; gamma_blue = xsane.gamma * xsane.gamma_blue; } xsane_create_gamma_curve(xsane.gamma_data_red, xsane.negative, gamma_red, xsane.brightness + xsane.brightness_red, xsane.contrast + xsane.contrast_red, gamma_red_size, gamma_red_max); xsane_create_gamma_curve(xsane.gamma_data_green, xsane.negative, gamma_green, xsane.brightness + xsane.brightness_green, xsane.contrast + xsane.contrast_green, gamma_green_size, gamma_green_max); xsane_create_gamma_curve(xsane.gamma_data_blue, xsane.negative, gamma_blue, xsane.brightness + xsane.brightness_blue, xsane.contrast + xsane.contrast_blue , gamma_blue_size, gamma_blue_max); /* gamma tables are freed after scan */ } } } while (gtk_events_pending()) { gtk_main_iteration(); } xsane_start_scan(); } /* ---------------------------------------------------------------------------------------------------------------------- */