diff options
Diffstat (limited to 'src/xsane-scan.c')
-rw-r--r-- | src/xsane-scan.c | 2179 |
1 files changed, 2179 insertions, 0 deletions
diff --git a/src/xsane-scan.c b/src/xsane-scan.c new file mode 100644 index 0000000..a724e3e --- /dev/null +++ b/src/xsane-scan.c @@ -0,0 +1,2179 @@ +/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend + + xsane-scan.c + + Oliver Rauch <Oliver.Rauch@rauch-domain.de> + Copyright (C) 1998-2002 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-viewer.h" +#include "xsane-save.h" +#include "xsane-gamma.h" +#include "xsane-setup.h" + +#ifdef HAVE_LIBPNG +#ifdef HAVE_LIBZ +#include <png.h> +#include <zlib.h> +#endif +#endif + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +/* forward declarations: */ + +static int xsane_generate_dummy_filename(int conversion_level); +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 void xsane_create_internal_gamma_tables(void); + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static int xsane_generate_dummy_filename(int conversion_level) +/* conversion levels: */ +/* 0 = scan */ +/* 1 = rotate */ +/* 2 = pack */ +{ + char filename[PATH_MAX]; + int tempfile = FALSE; /* returns TRUE if file is a temporary file */ + + DBG(DBG_proc, "xsane_generate_dummy_filename(conversion_level=%d)\n", conversion_level); + + if (xsane.dummy_filename) + { + free(xsane.dummy_filename); + } + + if ( (conversion_level == 0) && (xsane.preview->rotation) ) /* scan level with rotation */ + { + tempfile = TRUE; + } + + if ( (conversion_level == 1) && (xsane.expand_lineart_to_grayscale) ) /* rotation level and expanded lineart*/ + { + tempfile = TRUE; + } + + if ( (xsane.xsane_mode == XSANE_COPY) || + (xsane.xsane_mode == XSANE_FAX) || + (xsane.xsane_mode == XSANE_MAIL) || + (xsane.xsane_mode == XSANE_VIEWER) || + ( (xsane.xsane_mode == XSANE_SAVE) && (xsane.xsane_output_format != XSANE_PNM) && + (xsane.xsane_output_format != XSANE_RAW16) && (xsane.xsane_output_format != XSANE_RGBA) ) ) + { + tempfile = TRUE; + } + + if (tempfile) /* save to temporary file */ + { + xsane_back_gtk_make_path(sizeof(filename), filename, 0, 0, "conversion-", xsane.dev_name, ".ppm", XSANE_PATH_TMP); + xsane.dummy_filename = strdup(filename); + DBG(DBG_info, "xsane.dummy_filename = %s\n", xsane.dummy_filename); + + return TRUE; + } + else /* no conversion following, save directly to the selected filename */ + { + xsane.dummy_filename = strdup(xsane.output_filename); + DBG(DBG_info, "xsane.dummy_filename = %s\n", xsane.dummy_filename); + + return FALSE; + } +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static void xsane_read_image_data(gpointer data, gint source, GdkInputCondition cond) +{ + SANE_Handle dev = xsane.dev; + SANE_Status status; + SANE_Int len; + int i, j; + char buf[255]; + + DBG(DBG_proc, "xsane_read_image_data\n"); + + xsane.reading_data = TRUE; + + if ( (xsane.param.depth == 1) || (xsane.param.depth == 8) ) + { + unsigned char buf8[2*32768]; + unsigned char *buf8ptr; + + DBG(DBG_info, "depth = 1 or 8 bit\n"); + + while (1) + { + if (xsane.cancel_scan) + { + break; /* leave while loop */ + } + + status = sane_read(dev, (SANE_Byte *) buf8, sizeof(buf8), &len); + + DBG(DBG_info, "sane_read returned with status %s\n", XSANE_STRSTATUS(status)); + DBG(DBG_info, "sane_read: len = %d\n", len); + + if (status == SANE_STATUS_EOF) + { + if (!xsane.param.last_frame) + { + DBG(DBG_info, "not last frame\n"); + + if (xsane.input_tag >= 0) + { + gdk_input_remove(xsane.input_tag); + xsane.input_tag = -1; + } + xsane_start_scan(); + break; /* leave while loop */ + } + + xsane_scan_done(SANE_STATUS_EOF); /* image complete, stop scanning */ + return; + } + + if (status == SANE_STATUS_CANCELLED) + { + xsane_scan_done(status); /* status = return of sane_read */ + snprintf(buf, sizeof(buf), "%s.", XSANE_STRSTATUS(status)); + xsane_back_gtk_warning(buf, TRUE); + 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.bytes_read / (gfloat) xsane.num_bytes); + + /* it is not allowed to call gtk_main_iteration when we have gdk_input active */ + /* because xsan_read_image_data will be called several times */ + if (xsane.input_tag < 0) + { + while (gtk_events_pending()) + { + DBG(DBG_info, "calling gtk_main_iteration\n"); + gtk_main_iteration(); + } + } + + switch (xsane.param.format) + { + case SANE_FRAME_GRAY: + { + int i; + u_char val; + + DBG(DBG_info, "grayscale\n"); + + if ((!xsane.scanner_gamma_gray) && (xsane.param.depth > 1)) + { + buf8ptr = buf8; + + for (i=0; i < len; ++i) /* do gamma correction by xsane */ + { + *buf8ptr = xsane.gamma_data[(int) (*buf8ptr)]; + buf8ptr++; + } + + fwrite(buf8, 1, len, xsane.out); /* write gamma corrected data */ + } + else if ((xsane.param.depth == 1) && (xsane.expand_lineart_to_grayscale)) + { + unsigned char *expanded_buf8; + unsigned char *expanded_buf8ptr; + + /* if we want to do any postprocessing (e.g. rotation) */ + /* we save lineart images in grayscale mode */ + /* to speed up transformation and saving the transformed expanded (1bit->1byte) */ + /* is written in a buffer and saved as full buffer */ + + expanded_buf8 = malloc(len * 8); /* one byte for each pixel (bit) */ + if (!expanded_buf8) + { + xsane_scan_done(-1); /* -1 = error */ + snprintf(buf, sizeof(buf), "%s", ERR_NO_MEM); + xsane_back_gtk_error(buf, TRUE); + return; + } + + expanded_buf8ptr = expanded_buf8; + buf8ptr = buf8; + + for (i = 0; i < len; ++i) + { + val = *buf8ptr; + for (j = 7; j >= 0; --j) + { + *expanded_buf8ptr = (val & (1 << j)) ? 0x00 : 0xff; + expanded_buf8ptr++; + } + buf8ptr++; + } + fwrite(expanded_buf8, 1, len*8, xsane.out); + free(expanded_buf8); + } + else /* save direct to the file */ + { + fwrite(buf8, 1, len, xsane.out); + } + } + break; /* SANE_FRAME_GRAY */ + + case SANE_FRAME_RGB: + { + int i; + + DBG(DBG_info, "1 pass color\n"); + + if (!xsane.scanner_gamma_color) /* do gamma correction by xsane */ + { + buf8ptr = buf8; + for (i=0; i < len; ++i) + { + if (xsane.pixelcolor == 0) + { + *buf8ptr = xsane.gamma_data_red[(int) (*buf8ptr)]; + buf8ptr++; + xsane.pixelcolor++; + } + else if (xsane.pixelcolor == 1) + { + *buf8ptr = xsane.gamma_data_green[(int) (*buf8ptr)]; + buf8ptr++; + xsane.pixelcolor++; + } + else + { + *buf8ptr = xsane.gamma_data_blue[(int) (*buf8ptr)]; + buf8ptr++; + xsane.pixelcolor = 0; + } + } + fwrite(buf8, 1, len, xsane.out); /* write buffer */ + } + else /* gamma correction has been done by scanner */ + { + fwrite(buf8, 1, len, xsane.out); /* write buffer */ + } + } + break; + + case SANE_FRAME_RED: + case SANE_FRAME_GREEN: + case SANE_FRAME_BLUE: + { + DBG(DBG_info, "3 pass color\n"); + + 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]]; + fputc(val, xsane.out); + fseek(xsane.out, 2, SEEK_CUR); + } + } + else /* gamma correction by scanner */ + { + for (i = 0; i < len; ++i) + { + fputc(buf8[i], xsane.out); + fseek(xsane.out, 2, SEEK_CUR); + } + } + } + break; + +#ifdef SUPPORT_RGBA + case SANE_FRAME_RGBA: /* Scanning including Infrared channel */ + { + int i; + char val; + + DBG(DBG_info, "1 pass color+alpha (RGBA)\n"); + + if (!xsane.scanner_gamma_color) /* gamma correction by xsane */ + { + buf8ptr = buf8; + + for (i=0; i < len; ++i) + { + if (xsane.pixelcolor == 0) + { + *buf8ptr = xsane.gamma_data_red[(int) (*buf8ptr)]; + buf8ptr++; + xsane.pixelcolor++; + } + else if (xsane.pixelcolor == 1) + { + *buf8ptr = xsane.gamma_data_green[(int) (*buf8ptr)]; + buf8ptr++; + xsane.pixelcolor++; + } + else if (xsane.pixelcolor == 2) + { + *buf8ptr = xsane.gamma_data_blue[(int) (*buf8ptr)]; + buf8ptr++; + xsane.pixelcolor++; + } + else + { + /* no gamma table for infrared channel */ + buf8ptr++; + xsane.pixelcolor = 0; + } + } + + fwrite(buf8, 1, len, xsane.out); + } + else /* gamma correction has been done by scanner */ + { + fwrite(buf8, 1, len, xsane.out); + } + } + break; +#endif + + default: + xsane_scan_done(-1); /* -1 = error */ + DBG(DBG_error, "xsane_read_image_data: %s %d\n", ERR_BAD_FRAME_FORMAT, xsane.param.format); + return; + break; + } + } + } + else if ( xsane.param.depth == 16 ) + { + guint16 buf16[32768]; + guint16 *buf16ptr; + unsigned char *buf8 = (unsigned char *) buf16; + unsigned char *buf8ptr; + char buf[255]; + char last = 0; + int offset = 0; + + DBG(DBG_info, "depth = 16 bit\n"); + + while (1) + { + if (xsane.cancel_scan) + { + break; /* leave while loop */ + } + + if (offset) /* if we have had an odd number of bytes */ + { + buf16[0] = last; /* ATTENTION: that is wrong! */ + 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 (!xsane.scanning) /* scan may have been canceled while sane_read was executed */ + { + return; /* ok, the scan has been canceled */ + } + + + 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) + { + DBG(DBG_info, "not last frame\n"); + if (xsane.input_tag >= 0) + { + gdk_input_remove(xsane.input_tag); + xsane.input_tag = -1; + } + xsane_start_scan(); + break; /* leave while loop */ + } + + xsane_scan_done(SANE_STATUS_EOF); /* image complete, stop scanning */ + return; + } + + if (status == SANE_STATUS_CANCELLED) + { + xsane_scan_done(status); /* status = return of sane_read */ + snprintf(buf, sizeof(buf), "%s.", XSANE_STRSTATUS(status)); + xsane_back_gtk_warning(buf, TRUE); + 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.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: + { + int i; + + if (xsane.reduce_16bit_to_8bit) /* reduce 16 bit image to 8 bit */ + { + DBG(DBG_info, "reducing 16 bit image to 8 bit\n"); + + if (!xsane.scanner_gamma_gray) /* gamma correction by xsane */ + { + buf8ptr = buf8; + buf16ptr = buf16; + + for (i=0; i < len/2; ++i) + { + *buf8ptr = (xsane.gamma_data[(*buf16ptr)]) >> 8; /* reduce to 8 bit */ + buf8ptr++; + buf16ptr++; + } + + fwrite(buf8, 1, len/2, xsane.out); + } + else /* gamma correction by scanner */ + { + buf8ptr = buf8; + buf16ptr = buf16; + + for (i=0; i < len/2; ++i) + { + *buf8ptr = (*buf16ptr) >> 8; /* reduce to 8 bit */ + buf8ptr++; + buf16ptr++; + } + + fwrite(buf8, 1, len/2, xsane.out); + } + } + else /* save as 16 bit image */ + { + if (!xsane.scanner_gamma_gray) /* gamma correction by xsane */ + { + buf16ptr = buf16; + + for (i=0; i < len/2; ++i) + { + *buf16ptr = xsane.gamma_data[(*buf16ptr)]; + buf16ptr++; + } + fwrite(buf16, 2, len/2, xsane.out); + } + else /* gamma correction by scanner */ + { + fwrite(buf16, 2, len/2, xsane.out); + } + } + } + break; + + case SANE_FRAME_RGB: + { + int i; + + if (xsane.reduce_16bit_to_8bit) /* reduce 16 bit image to 8 bit */ + { + DBG(DBG_info, "reducing 16 bit image to 8 bit\n"); + + if (!xsane.scanner_gamma_color) /* gamma correction by xsane */ + { + buf8ptr = buf8; + buf16ptr = buf16; + + for (i=0; i < len/2; ++i) + { + if (xsane.pixelcolor == 0) + { + *buf8ptr = (xsane.gamma_data_red[(*buf16ptr)]) >> 8; /* reduce to 8 bit */ + buf8ptr++; + buf16ptr++; + xsane.pixelcolor++; + } + else if (xsane.pixelcolor == 1) + { + *buf8ptr = (xsane.gamma_data_green[(*buf16ptr)]) >> 8; /* reduce to 8 bit */ + buf8ptr++; + buf16ptr++; + xsane.pixelcolor++; + } + else + { + *buf8ptr = (xsane.gamma_data_blue[(*buf16ptr)]) >> 8; /* reduce to 8 bit */ + buf8ptr++; + buf16ptr++; + xsane.pixelcolor = 0; + } + } + fwrite(buf8, 1, len/2, xsane.out); + } + else /* gamma correction by scanner */ + { + buf8ptr = buf8; + buf16ptr = buf16; + + for (i=0; i < len/2; ++i) + { + *buf8ptr = (*buf16ptr) >> 8; /* reduce to 8 bit */ + buf8ptr++; + buf16ptr++; + xsane.pixelcolor++; + } + fwrite(buf8, 1, len/2, xsane.out); + } + } + else /* save as 16 bit image */ + { + if (!xsane.scanner_gamma_color) /* gamma correction by xsane */ + { + buf16ptr = buf16; + + for (i=0; i < len/2; ++i) + { + if (xsane.pixelcolor == 0) + { + *buf16ptr = xsane.gamma_data_red[(*buf16ptr)]; + buf16ptr++; + xsane.pixelcolor++; + } + else if (xsane.pixelcolor == 1) + { + *buf16ptr = xsane.gamma_data_green[(*buf16ptr)]; + buf16ptr++; + xsane.pixelcolor++; + } + else + { + *buf16ptr = xsane.gamma_data_blue[(*buf16ptr)]; + buf16ptr++; + xsane.pixelcolor = 0; + } + } + fwrite(buf16, 2, len/2, 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: + /* this is incomplete: + - missing: gamma correction by xsane + - missing: reduction to 8 bit + but I do not think there are 3 pass scanners with more + than 24 bits/pixel */ + { + 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: + { + int i; + guint16 val; + + if (xsane.reduce_16bit_to_8bit) /* reduce 16 bit image to 8 bit */ + { + DBG(DBG_info, "reducing 16 bit image to 8 bit\n"); + + if (!xsane.scanner_gamma_color) + { + buf8ptr = buf8; + buf16ptr = buf16; + + for (i=0; i < len/2; ++i) + { + if (xsane.pixelcolor == 0) + { + *buf8ptr = (xsane.gamma_data_red[(*buf16ptr)]) >> 8; /* reduce to 8 bit */ + buf8ptr++; + buf16ptr++; + xsane.pixelcolor++; + } + else if (xsane.pixelcolor == 1) + { + *buf8ptr = (xsane.gamma_data_green[(*buf16ptr)]) >> 8; /* reduce to 8 bit */ + buf8ptr++; + buf16ptr++; + xsane.pixelcolor++; + } + else if (xsane.pixelcolor == 2) + { + *buf8ptr = (xsane.gamma_data_blue[(*buf16ptr)]) >> 8; /* reduce to 8 bit */ + buf8ptr++; + buf16ptr++; + xsane.pixelcolor++; + } + else + { + /* no gamma table for infrared channel */ + *buf8ptr = (*buf16ptr) >> 8; /* reduce to 8 bit */ + buf8ptr++; + buf16ptr++; + xsane.pixelcolor = 0; + } + } + + fwrite(buf8, 1, len, xsane.out); + } + else /* gamma correction done by scanner */ + { + buf8ptr = buf8; + buf16ptr = buf16; + + for (i=0; i < len/2; ++i) + { + *buf8ptr = (*buf16ptr) >> 8; /* reduce to 8 bit */ + buf8ptr++; + buf16ptr++; + } + + fwrite(buf8, 1, len, xsane.out); + } + } + else /* save as 16 bit image */ + { + if (!xsane.scanner_gamma_color) + { + buf16ptr = buf16; + + for (i=0; i < len/2; ++i) + { + if (xsane.pixelcolor == 0) + { + *buf16ptr = xsane.gamma_data_red[(*buf16ptr)]; + buf16ptr++; + xsane.pixelcolor++; + } + else if (xsane.pixelcolor == 1) + { + *buf16ptr = xsane.gamma_data_green[(*buf16ptr)]; + buf16ptr++; + xsane.pixelcolor++; + } + else if (xsane.pixelcolor == 2) + { + *buf16ptr = xsane.gamma_data_blue[(*buf16ptr)]; + buf16ptr++; + xsane.pixelcolor++; + } + else + { + /* no gamma table for infrared channel */ + buf16ptr++; + xsane.pixelcolor = 0; + } + } + + fwrite(buf16, 2, len/2, xsane.out); + } + else /* gamma correction done by scanner */ + { + fwrite(buf16, 2, len/2, xsane.out); + } + } + } + break; +#endif + + default: + xsane_scan_done(-1); /* -1 = error */ + DBG(DBG_error, "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; + } + + if (xsane.cancel_scan) + { + xsane.cancel_scan = FALSE; /* make sure we do not get an infinite loop */ + + xsane_read_image_data(0, -1, GDK_INPUT_READ); + } + + xsane.reading_data = FALSE; + + return; +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static RETSIGTYPE xsane_sigpipe_handler(int signal) +/* this is to catch a broken pipe while writing to printercommand */ +{ + DBG(DBG_proc, "xsane_sigpipe_handler\n"); + + xsane_cancel_save(&xsane.cancel_save); + xsane.broken_pipe = 1; +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static int xsane_test_multi_scan(void) +{ + char *set; + SANE_Status status; + const SANE_Option_Descriptor *opt; + + DBG(DBG_proc, "xsane_test_multi_scan\n"); + + opt = xsane_get_option_descriptor(xsane.dev, xsane.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 = xsane_control_option(xsane.dev, xsane.well_known.scansource, SANE_ACTION_GET_VALUE, set, 0); + + if (status == SANE_STATUS_GOOD) + { + if (xsane.adf_scansource) + { + if (!strcmp(set, xsane.adf_scansource)) + { + free(set); + 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; +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static int xsane_reduce_to_lineart() +/* returns 1 on abort, 0 on success */ +{ + char *old_dummy_filename; + int abort = 0; + + /* open progressbar */ + xsane_progress_new(PROGRESS_PACKING_DATA, PROGRESS_TRANSFERING_DATA, (GtkSignalFunc) xsane_cancel_save, &xsane.cancel_save); + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + /* on some filesystems it is not allowed to erase an opened file and access the + file after that, so we must wait until the file is closed */ + old_dummy_filename = strdup(xsane.dummy_filename); + + /* temporary file is created with permission 0600 in xsane_generate_dummy_filename */ + if (!xsane_generate_dummy_filename(2)) /* create filename for packing */ + { + /* no temporary file */ + if (xsane_create_secure_file(xsane.dummy_filename)) /* remove possibly existing symbolic links for security */ + { + char buf[256]; + + snprintf(buf, sizeof(buf), "%s %s %s\n", ERR_DURING_SAVE, ERR_CREATE_SECURE_FILE, xsane.dummy_filename); + xsane_back_gtk_error(buf, TRUE); + abort = 1; /* abort scanning */ + } + } + + if (!abort) + { + abort = xsane_save_image_as_lineart(old_dummy_filename, xsane.dummy_filename, xsane.progress_bar, &xsane.cancel_save); + } + + if (abort) + { + xsane_set_sensitivity(TRUE); /* reactivate buttons etc */ + sane_cancel(xsane.dev); /* stop scanning */ + xsane_update_histogram(TRUE /* update raw */); + xsane_update_param(0); + xsane.header_size = 0; + return 1; + } + + remove(old_dummy_filename); /* remove the unrotated image file */ + free(old_dummy_filename); /* release memory */ + xsane_progress_clear(); + + return 0; +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_scan_done(SANE_Status status) +{ + Image_info image_info; + + DBG(DBG_proc, "xsane_scan_done\n"); + + if (!xsane.scanning) + { + return; + } + + xsane.reading_data = FALSE; + xsane.scanning = FALSE; /* set marker that sane_start is called */ + + if (xsane.input_tag >= 0) + { + gdk_input_remove(xsane.input_tag); + xsane.input_tag = -1; + } + + xsane_progress_clear(); /* clear progress bar and reset cancel callback */ + + while(gtk_events_pending()) /* let gtk remove the progress bar and update everything that needs it */ + { + DBG(DBG_info, "calling gtk_main_iteration\n"); + gtk_main_iteration(); + } + + + /* we have to free the gamma tables if we used software gamma correction */ + + if (xsane.gamma_data) + { + DBG(DBG_info, "freeing gray gamma table\n"); + + free(xsane.gamma_data); + xsane.gamma_data = 0; + } + + if (xsane.gamma_data_red) + { + DBG(DBG_info, "freeing color gamma tables\n"); + + 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 */ + { + int pixel_height = xsane.bytes_read / xsane.param.bytes_per_line; + + /* correct image height if necessary, e.g. when scanning with hand scanner */ + + if (xsane.param.lines != pixel_height) + { + DBG(DBG_info, "correcting image height to %d lines\n", pixel_height); + xsane.param.lines = pixel_height; + + image_info.image_width = xsane.param.pixels_per_line; + image_info.image_height = pixel_height; + image_info.depth = xsane.depth; + image_info.colors = xsane.xsane_colors; + + image_info.resolution_x = xsane.resolution_x; + image_info.resolution_y = xsane.resolution_y; + + image_info.gamma = xsane.gamma; + image_info.gamma_red = xsane.gamma_red; + image_info.gamma_green = xsane.gamma_green; + image_info.gamma_blue = xsane.gamma_blue; + + image_info.brightness = xsane.brightness; + image_info.brightness_red = xsane.brightness_red; + image_info.brightness_green = xsane.brightness_green; + image_info.brightness_blue = xsane.brightness_blue; + + image_info.contrast = xsane.contrast; + image_info.contrast_red = xsane.contrast_red; + image_info.contrast_green = xsane.contrast_green; + image_info.contrast_blue = xsane.contrast_blue; + + image_info.threshold = xsane.threshold; + + + xsane_write_pnm_header(xsane.out, &image_info); + } + + DBG(DBG_info, "closing output file\n"); + + fflush(xsane.out); + fclose(xsane.out); + xsane.out = 0; + } + + if ( (status == SANE_STATUS_GOOD) || (status == SANE_STATUS_EOF) ) /* no error, do conversion etc. */ + { + /* do we have to rotate the image ? */ + if (xsane.preview->rotation) + { + char *old_dummy_filename; + int abort = 0; + FILE *outfile; + FILE *infile; + + infile = fopen(xsane.dummy_filename, "rb"); /* read binary (b for win32) */ + if (infile != 0) + { + xsane_read_pnm_header(infile, &image_info); + + /* open progressbar */ + xsane_progress_new(PROGRESS_ROTATING_DATA, PROGRESS_TRANSFERING_DATA, (GtkSignalFunc) xsane_cancel_save, &xsane.cancel_save); + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + /* on some filesystems it is not allowed to erase an opened file and access the + file after that, so we must wait until the file is closed */ + old_dummy_filename = strdup(xsane.dummy_filename); + + /* temporary file is created with permission 0600 in xsane_generate_dummy_filename */ + if (!xsane_generate_dummy_filename(1)) /* create filename for rotation */ + { + /* no temporary file */ + if (xsane_create_secure_file(xsane.dummy_filename)) /* remove possibly existing symbolic links for security */ + { + char buf[256]; + + snprintf(buf, sizeof(buf), "%s %s %s\n", ERR_DURING_SAVE, ERR_CREATE_SECURE_FILE, xsane.dummy_filename); + xsane_back_gtk_error(buf, TRUE); + abort = 1; /* abort scanning */ + } + } + + if (!abort) + { + /* rotate image */ + outfile = fopen(xsane.dummy_filename, "wb"); /* read binary (b for win32) */ + + if (outfile) + { + if (xsane_save_rotate_image(outfile, infile, &image_info, xsane.preview->rotation, xsane.progress_bar, &xsane.cancel_save)) + { + abort = 1; + } + } + else + { + char buf[256]; + DBG(DBG_info, "open of file `%s'failed : %s\n", xsane.dummy_filename, strerror(errno)); + snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, xsane.dummy_filename, strerror(errno)); + xsane_back_gtk_error(buf, TRUE); + abort = 1; + } + + fclose(outfile); + } + + fclose(infile); + remove(old_dummy_filename); /* remove the unrotated image file */ + + free(old_dummy_filename); /* release memory */ + xsane_progress_clear(); + } + else + { + char buf[256]; + DBG(DBG_info, "open of file `%s'failed : %s\n", xsane.dummy_filename, strerror(errno)); + snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, xsane.dummy_filename, strerror(errno)); + xsane_back_gtk_error(buf, TRUE); + abort = 1; + } + + if (abort) + { + xsane_set_sensitivity(TRUE); /* reactivate buttons etc */ + sane_cancel(xsane.dev); /* stop scanning */ + xsane_update_histogram(TRUE /* update raw */); + xsane_update_param(0); + xsane.header_size = 0; + return; + } + } + + if (xsane.xsane_mode == XSANE_VIEWER) + { + xsane_viewer_new(xsane.dummy_filename, xsane.expand_lineart_to_grayscale, 0); + xsane.expand_lineart_to_grayscale = 0; + } + + /* when we are scanning in lineart mode and we are transforming the image */ + /* it is saved as grayscale while scanning so we can use the standard transformations */ + /* but now we have to save the lineart image as packed lineart again */ + if ((xsane.param.depth == 1) && (xsane.expand_lineart_to_grayscale)) + { + if (xsane_reduce_to_lineart()) + { + return; /* aborted */ + } + } + + if (xsane.xsane_mode == XSANE_SAVE) + { + if (xsane.print_filenames) /* print created filenames to stdout? */ + { + if (xsane.output_filename[0] != '/') /* relative path */ + { + char pathname[512]; + getcwd(pathname, sizeof(pathname)); + printf("XSANE_IMAGE_FILENAME: %s/%s\n", pathname, xsane.output_filename); + fflush(stdout); + } + else /* absolute path */ + { + printf("XSANE_IMAGE_FILENAME: %s\n", xsane.output_filename); + fflush(stdout); + } + } + + if ( ( (xsane.xsane_output_format != XSANE_PNM) && /* these files do not need any transformation */ + (xsane.xsane_output_format != XSANE_RAW16) && + (xsane.xsane_output_format != XSANE_RGBA) ) || + (xsane.mode == XSANE_GIMP_EXTENSION) ) + { /* ok, we have to do a transformation */ + + /* open progressbar */ + xsane_progress_new(PROGRESS_CONVERTING_DATA, PROGRESS_TRANSFERING_DATA, (GtkSignalFunc) xsane_cancel_save, &xsane.cancel_save); + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + +#ifdef HAVE_LIBGIMP_GIMP_H + if (xsane.mode == XSANE_GIMP_EXTENSION) /* xsane runs as gimp plugin */ + { + xsane_transfer_to_gimp(xsane.dummy_filename, xsane.progress_bar, &xsane.cancel_save); + } + else +#endif /* HAVE_LIBGIMP_GIMP_H */ + { + xsane_save_image_as(xsane.dummy_filename, xsane.output_filename, xsane.xsane_output_format, xsane.progress_bar, &xsane.cancel_save); + } + + xsane_progress_clear(); + + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + remove(xsane.dummy_filename); + } + } + else if (xsane.xsane_mode == XSANE_COPY) + { + FILE *outfile; + FILE *infile; + char buf[256]; + + DBG(DBG_info, "XSANE_COPY\n"); + + 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 */ + xsane_progress_new(PROGRESS_CONVERTING_DATA, PROGRESS_TRANSFERING_DATA, (GtkSignalFunc) xsane_cancel_save, &xsane.cancel_save); + + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + xsane.broken_pipe = 0; + infile = fopen(xsane.dummy_filename, "rb"); /* read binary (b for win32) */ + + 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, imageheight; + int printer_resolution; + + switch (xsane.param.format) + { + case SANE_FRAME_GRAY: + if (xsane.depth == 1) + { + printer_resolution = preferences.printer[preferences.printernr]->lineart_resolution; + } + else + { + printer_resolution = preferences.printer[preferences.printernr]->grayscale_resolution; + } + break; /* switch format == SANE_FRAME_GRAY */ + + case SANE_FRAME_RGB: + case SANE_FRAME_RED: + case SANE_FRAME_GREEN: + case SANE_FRAME_BLUE: + default: + printer_resolution = preferences.printer[preferences.printernr]->color_resolution; + break; /* switch format == SANE_FRAME_{color} */ + } + + xsane_read_pnm_header(infile, &image_info); + + imagewidth = image_info.image_width/(float)printer_resolution; /* width in inch */ + imageheight = image_info.image_height/(float)printer_resolution; /* height in inch */ + + memset (&act, 0, sizeof (act)); /* define broken pipe handler */ + act.sa_handler = xsane_sigpipe_handler; + sigaction (SIGPIPE, &act, 0); + + + if (preferences.psrotate) /* rotate: landscape */ + { + xsane_save_ps(outfile, infile, + &image_info, + (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 */, + xsane.progress_bar, + &xsane.cancel_save); + } + else /* do not rotate: portrait */ + { + xsane_save_ps(outfile, infile, + &image_info, + (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 */, + xsane.progress_bar, + &xsane.cancel_save); + } + } + 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_clear(); + 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; + + /* open progressbar */ + xsane_progress_new(PROGRESS_CONVERTING_DATA, PROGRESS_TRANSFERING_DATA, (GtkSignalFunc) xsane_cancel_save, &xsane.cancel_save); + + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + infile = fopen(xsane.dummy_filename, "rb"); /* read binary (b for win32) */ + if (infile != 0) + { + xsane_read_pnm_header(infile, &image_info); + + umask((mode_t) preferences.image_umask); /* define image file permissions */ + outfile = fopen(xsane.fax_filename, "wb"); /* b = binary mode for win32 */ + umask(XSANE_DEFAULT_UMASK); /* define new file permissions */ + if (outfile != 0) + { + float imagewidth, imageheight; + + imagewidth = image_info.image_width/xsane.resolution_x; /* width in inch */ + imageheight = image_info.image_height/xsane.resolution_y; /* height in inch */ + + DBG(DBG_info, "imagewidth = %f\n", imagewidth); + DBG(DBG_info, "imageheight = %f\n", imageheight); + + xsane_save_ps(outfile, infile, + &image_info, + (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 */, + xsane.progress_bar, + &xsane.cancel_save); + fclose(outfile); + } + else + { + char buf[256]; + + DBG(DBG_info, "open of faxfile `%s'failed : %s\n", xsane.fax_filename, strerror(errno)); + + 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]; + + DBG(DBG_info, "open of faxfile `%s'failed : %s\n", xsane.fax_filename, strerror(errno)); + + snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, xsane.fax_filename, strerror(errno)); + xsane_back_gtk_error(buf, TRUE); + } + xsane_progress_clear(); + + while (gtk_events_pending()) + { + DBG(DBG_info, "calling gtk_main_iteration"); + gtk_main_iteration(); + } + } +#ifdef XSANE_ACTIVATE_MAIL + else if (xsane.xsane_mode == XSANE_MAIL) + { + FILE *infile; + + /* open progressbar */ + xsane_progress_new(PROGRESS_CONVERTING_DATA, PROGRESS_TRANSFERING_DATA, (GtkSignalFunc) xsane_cancel_save, &xsane.cancel_save); + + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + infile = fopen(xsane.dummy_filename, "rb"); /* read binary (b for win32) */ + if (infile != 0) + { + xsane_read_pnm_header(infile, &image_info); + + xsane_save_image_as(xsane.dummy_filename, xsane.mail_filename, XSANE_PNG, xsane.progress_bar, &xsane.cancel_save); + + fclose(infile); + remove(xsane.dummy_filename); + } + else + { + char buf[256]; + + DBG(DBG_info, "open of mailfile `%s'failed : %s\n", xsane.dummy_filename, strerror(errno)); + + snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, xsane.dummy_filename, strerror(errno)); + xsane_back_gtk_error(buf, TRUE); + } + xsane_progress_clear(); + + while (gtk_events_pending()) + { + DBG(DBG_info, "calling gtk_main_iteration"); + gtk_main_iteration(); + } + } +#endif + + if ( (xsane.xsane_mode == XSANE_SAVE) && (xsane.mode == XSANE_STANDALONE) ) + { + if (!xsane.force_filename) /* user filename selection active */ + { + if (preferences.filename_counter_step) /* increase filename counter ? */ + { + xsane_update_counter_in_filename(&preferences.filename, preferences.skip_existing_numbers, + preferences.filename_counter_step, preferences.filename_counter_len); + gtk_entry_set_text(GTK_ENTRY(xsane.outputfilename_entry), (char *) preferences.filename); /* update filename in entry */ + gtk_entry_set_position(GTK_ENTRY(xsane.outputfilename_entry), strlen(preferences.filename)); /* set cursor to right position of filename */ + } + } + else /* external filename */ + { + xsane_update_counter_in_filename(&xsane.external_filename, TRUE, 1, 0); + } + } + 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_update_counter_in_filename(&xsane.fax_filename, TRUE, 1, preferences.filename_counter_len); + xsane_fax_project_save(); + free(page); + } +#ifdef XSANE_ACTIVATE_MAIL + else if (xsane.xsane_mode == XSANE_MAIL) + { + GtkWidget *list_item; + char *image; + char *extension; + + image = strdup(strrchr(xsane.mail_filename,'/')+1); + extension = strrchr(image, '.'); + if (extension) + { + *extension = 0; + } + list_item = gtk_list_item_new_with_label(image); + gtk_object_set_data(GTK_OBJECT(list_item), "list_item_data", strdup(image)); + gtk_container_add(GTK_CONTAINER(xsane.mail_list), list_item); + gtk_widget_show(list_item); + + xsane_update_counter_in_filename(&xsane.mail_filename, TRUE, 1, preferences.filename_counter_len); + xsane_mail_project_save(); + free(image); + } +#endif + } + 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; + } + + xsane.header_size = 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 */ + + xsane.adf_page_counter += 1; + 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.dev); /* stop scanning */ + xsane_update_histogram(TRUE /* update raw */); + xsane_update_param(0); + } +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_cancel(void) +{ + DBG(DBG_proc, "xsane_cancel\n"); + + if (!xsane.scanning) + { + return; + } + + sane_cancel(xsane.dev); + + /* we have to make sure that xsane does detect that the scan has been cancled */ + /* but the select_fd does not make sure that preview_read_image_data is called */ + /* when the select_fd is closed by the backend, so we have to make sure that */ + /* preview_read_image_data is called */ + + if (xsane.reading_data) /* we are still reading data, set flag for cancel */ + { + xsane.cancel_scan = TRUE; + } + else /* we are not reading image data, so we have to call it now */ + { + xsane_read_image_data(0, -1, GDK_INPUT_READ); + } +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +/* xsane_start_scan is called 3 times in 3 pass scanning mode */ +static void xsane_start_scan(void) +{ + SANE_Status status; + SANE_Handle dev = xsane.dev; + const char *frame_type = 0; + char buf[256]; + int fd; + Image_info image_info; + + DBG(DBG_proc, "xsane_start_scan\n"); + + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + status = sane_start(dev); + DBG(DBG_info, "sane_start returned with status %s\n", XSANE_STRSTATUS(status)); + + if ((status == SANE_STATUS_NO_DOCS) && (xsane.adf_page_counter>0)) /* ADF out of docs but not first page */ + { + xsane_scan_done(status); /* ok, stop multi image scan */ + snprintf(buf, sizeof(buf), "%s %d", TEXT_ADF_PAGES_SCANNED, xsane.adf_page_counter); + xsane_back_gtk_info(buf, FALSE); + xsane.adf_page_counter = 0; + 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); + xsane.adf_page_counter = 0; + 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); + xsane.adf_page_counter = 0; + return; + } + + xsane.depth = xsane.param.depth; /* bit depth for saving, can be changed: 1->8, 16->8 */ + + xsane.num_bytes = xsane.param.lines * xsane.param.bytes_per_line; + xsane.bytes_read = 0; + xsane.expand_lineart_to_grayscale = 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.param.depth == 1) && (xsane.preview->rotation) ) + { + xsane.expand_lineart_to_grayscale = 1; /* We want to do transformation with lineart scan, so we save it as grayscale */ + } + + if ((xsane.xsane_mode == XSANE_VIEWER) && (xsane.param.depth == 1)) + { + xsane.expand_lineart_to_grayscale = 1; /* we are using the viewer, lineart is not supported, so create grayscale */ + } + + if (!xsane.header_size) /* first pass of multi pass scan or single pass scan */ + { + xsane_create_internal_gamma_tables(); /* create gamma tables that are not supported by scanner */ + + /* temporary file is created with permission 0600 in xsane_generate_dummy_filename */ + if (!xsane_generate_dummy_filename(0)) /* create filename the scanned data is saved to */ + { + /* no temporary file */ + if (xsane_create_secure_file(xsane.dummy_filename)) /* remove possibly existing symbolic links for security */ + { + snprintf(buf, sizeof(buf), "%s %s %s\n", ERR_DURING_SAVE, ERR_CREATE_SECURE_FILE, xsane.dummy_filename); + xsane_scan_done(-1); /* -1 = error */ + xsane_back_gtk_error(buf, TRUE); + return; + } + } + + xsane.out = fopen(xsane.dummy_filename, "wb"); /* b = binary mode for win32 */ + + if (!xsane.out) /* error while opening the dummy_file for writing */ + { + xsane_scan_done(-1); /* -1 = error */ + DBG(DBG_info, "open of file `%s'failed : %s\n", xsane.dummy_filename, strerror(errno)); + snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, xsane.dummy_filename, strerror(errno)); + xsane_back_gtk_error(buf, TRUE); + return; + } + + if ( (xsane.expand_lineart_to_grayscale) || (xsane.reduce_16bit_to_8bit) ) + { + xsane.depth = 8; + } + + image_info.image_width = xsane.param.pixels_per_line; + image_info.image_height = xsane.param.lines; + image_info.depth = xsane.depth; + image_info.colors = xsane.xsane_colors; + + image_info.resolution_x = xsane.resolution_x; + image_info.resolution_y = xsane.resolution_y; + + image_info.gamma = xsane.gamma; + image_info.gamma_red = xsane.gamma_red; + image_info.gamma_green = xsane.gamma_green; + image_info.gamma_blue = xsane.gamma_blue; + + image_info.brightness = xsane.brightness; + image_info.brightness_red = xsane.brightness_red; + image_info.brightness_green = xsane.brightness_green; + image_info.brightness_blue = xsane.brightness_blue; + + image_info.contrast = xsane.contrast; + image_info.contrast_red = xsane.contrast_red; + image_info.contrast_green = xsane.contrast_green; + image_info.contrast_blue = xsane.contrast_blue; + + image_info.threshold = xsane.threshold; + + xsane_write_pnm_header(xsane.out, &image_info); + + fflush(xsane.out); + xsane.header_size = ftell(xsane.out); /* store header size for 3 pass scan */ + } + + if (xsane.param.format >= SANE_FRAME_RED && xsane.param.format <= SANE_FRAME_BLUE) + { +/* correct this using read_pnm_header */ + fseek(xsane.out, xsane.header_size + xsane.param.format - SANE_FRAME_RED, SEEK_SET); + } + + xsane.pixelcolor = 0; + + snprintf(buf, sizeof(buf), PROGRESS_RECEIVING_FRAME_DATA, _(frame_type)); + xsane_progress_new(buf, PROGRESS_SCANNING, (GtkSignalFunc) xsane_cancel, NULL); + + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + xsane.input_tag = -1; + +#ifndef BUGGY_GDK_INPUT_EXCEPTION + /* for unix */ + if ((sane_set_io_mode(dev, SANE_TRUE) == SANE_STATUS_GOOD) && (sane_get_select_fd(dev, &fd) == SANE_STATUS_GOOD)) + { + DBG(DBG_info, "gdk_input_add\n"); + xsane.input_tag = gdk_input_add(fd, GDK_INPUT_READ | GDK_INPUT_EXCEPTION, xsane_read_image_data, 0); + } + else +#else + /* for win32 */ + sane_set_io_mode(dev, SANE_FALSE); +#endif + { + 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]; + const SANE_Option_Descriptor *opt; + + DBG(DBG_proc, "xsane_scan_dialog\n"); + + xsane.reduce_16bit_to_8bit = preferences.reduce_16bit_to_8bit; /* reduce 16 bit image to 8 bit ? */ + + sane_get_parameters(xsane.dev, &xsane.param); /* update xsane.param */ + + if ( (xsane.mode == XSANE_STANDALONE) && (xsane.xsane_mode == XSANE_SAVE) ) + { + /* correct length of filename counter if it is shorter than minimum length */ + if (!xsane.force_filename) + { + xsane_update_counter_in_filename(&preferences.filename, FALSE, 0, preferences.filename_counter_len); + gtk_entry_set_text(GTK_ENTRY(xsane.outputfilename_entry), preferences.filename); + } + } + + xsane_define_output_filename(); /* make xsane.output_filename up to date */ + + /* test if file exists, may be we have to output an overwrite warning */ + if (xsane.mode == XSANE_STANDALONE) /* We are running in standalone mode */ + { + char *extension; + + if ( (xsane.xsane_mode == XSANE_SAVE) && (preferences.overwrite_warning) ) /* test if filename already used */ + { + FILE *testfile; + + testfile = fopen(xsane.output_filename, "rb"); /* read binary (b for win32) */ + if (testfile) /* filename used: skip */ + { + char buf[256]; + + fclose(testfile); + snprintf(buf, sizeof(buf), WARN_FILE_EXISTS, xsane.output_filename); + if (xsane_back_gtk_decision(ERR_HEADER_WARNING, (gchar **) warning_xpm, buf, BUTTON_OVERWRITE, BUTTON_CANCEL, TRUE /* wait */) == FALSE) + { + return; + } + } + } + + xsane.xsane_output_format = xsane_identify_output_format(xsane.output_filename, &extension); + + if (xsane.xsane_mode == XSANE_SAVE) + { + if (xsane.xsane_output_format == XSANE_UNKNOWN) + { + if (extension) + { + snprintf(buf, sizeof(buf), ERR_UNSUPPORTED_OUTPUT_FORMAT, xsane.param.depth, extension); + } + else + { + snprintf(buf, sizeof(buf), "%s", ERR_NO_OUTPUT_FORMAT); + } + xsane_back_gtk_error(buf, TRUE); + return; + } + else if ( ( ( (xsane.xsane_output_format == XSANE_JPEG) && xsane.param.depth == 16) || + ( (xsane.xsane_output_format == XSANE_PS) && xsane.param.depth == 16) ) && + ( !xsane.reduce_16bit_to_8bit) ) + + { + snprintf(buf, sizeof(buf), TEXT_REDUCE_16BIT_TO_8BIT); + if (xsane_back_gtk_decision(ERR_HEADER_INFO, (gchar **) info_xpm, buf, BUTTON_REDUCE, BUTTON_CANCEL, TRUE /* wait */) == FALSE) + { + return; + } + xsane.reduce_16bit_to_8bit = TRUE; + } + +#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); + } + + if (extension) + { + free(extension); + } + } +#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 ? */ + { + if (!xsane.reduce_16bit_to_8bit) /* ask if reduce 16 to 8 bit */ + { + if (xsane.param.depth == 16) + { + snprintf(buf, sizeof(buf), TEXT_GIMP_REDUCE_16BIT_TO_8BIT); + if (xsane_back_gtk_decision(ERR_HEADER_INFO, (gchar **) info_xpm, buf, BUTTON_REDUCE, BUTTON_CANCEL, TRUE /* wait */) == FALSE) + { + return; + } + xsane.reduce_16bit_to_8bit = TRUE; + } + else /* unsupported bit depth */ + { + snprintf(buf, sizeof(buf), 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; + } + + /* create scanner gamma tables, xsane internal gamma tables are created after sane_start */ + if ( (xsane.xsane_colors > 1) && /* color scan */ + xsane.scanner_gamma_color ) /* gamma table for red, green and blue available */ + { + double gamma_red, gamma_green, gamma_blue; + int gamma_red_size, gamma_green_size, gamma_blue_size; + int gamma_red_max, gamma_green_max, gamma_blue_max; + + /* 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) + { + int gamma_gray_size; + int gamma_gray_max; + + opt = xsane_get_option_descriptor(xsane.dev, xsane.well_known.gamma_vector); + gamma_gray_size = opt->size / sizeof(opt->type); + gamma_gray_max = opt->constraint.range->max; + + xsane.gamma_data = malloc(gamma_gray_size * sizeof(SANE_Int)); + xsane_create_gamma_curve(xsane.gamma_data, 0, 1.0, 0.0, 0.0, 0.0, 100.0, 1.0, gamma_gray_size, gamma_gray_max); + xsane_back_gtk_update_vector(xsane.well_known.gamma_vector, xsane.gamma_data); + free(xsane.gamma_data); + xsane.gamma_data = 0; + } + + opt = xsane_get_option_descriptor(xsane.dev, xsane.well_known.gamma_vector_r); + gamma_red_size = opt->size / sizeof(opt->type); + gamma_red_max = opt->constraint.range->max; + + opt = xsane_get_option_descriptor(xsane.dev, xsane.well_known.gamma_vector_g); + gamma_green_size = opt->size / sizeof(opt->type); + gamma_green_max = opt->constraint.range->max; + + opt = xsane_get_option_descriptor(xsane.dev, xsane.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 != xsane.medium_negative, + gamma_red, + xsane.brightness + xsane.brightness_red, + xsane.contrast + xsane.contrast_red, + xsane.medium_shadow_red, xsane.medium_highlight_red, xsane.medium_gamma_red, + gamma_red_size, gamma_red_max); + + xsane_create_gamma_curve(xsane.gamma_data_green, xsane.negative != xsane.medium_negative, + gamma_green, + xsane.brightness + xsane.brightness_green, + xsane.contrast + xsane.contrast_green, + xsane.medium_shadow_green, xsane.medium_highlight_green, xsane.medium_gamma_green, + gamma_green_size, gamma_green_max); + + xsane_create_gamma_curve(xsane.gamma_data_blue, xsane.negative != xsane.medium_negative, + gamma_blue, + xsane.brightness + xsane.brightness_blue, + xsane.contrast + xsane.contrast_blue, + xsane.medium_shadow_blue, xsane.medium_highlight_blue, xsane.medium_gamma_blue, + gamma_blue_size, gamma_blue_max); + + xsane_back_gtk_update_vector(xsane.well_known.gamma_vector_r, xsane.gamma_data_red); + xsane_back_gtk_update_vector(xsane.well_known.gamma_vector_g, xsane.gamma_data_green); + xsane_back_gtk_update_vector(xsane.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 or grayscale scan */ + { + /* 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 */ + + double gamma; + int gamma_gray_size; + int gamma_gray_max; + + opt = xsane_get_option_descriptor(xsane.dev, xsane.well_known.gamma_vector); + gamma_gray_size = opt->size / sizeof(opt->type); + gamma_gray_max = opt->constraint.range->max; + + 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)); + + if (xsane.xsane_colors > 1) /* color scan */ + { + xsane_create_gamma_curve(xsane.gamma_data, xsane.negative, + gamma, xsane.brightness, xsane.contrast, + 0.0, 100.0, 1.0, /* medium correction is done by xsane internal gamma correction */ + gamma_gray_size, gamma_gray_max); + } + else + { + xsane_create_gamma_curve(xsane.gamma_data, xsane.negative != xsane.medium_negative, + gamma, xsane.brightness, xsane.contrast, + xsane.medium_shadow_gray, xsane.medium_highlight_gray, xsane.medium_gamma_gray, + gamma_gray_size, gamma_gray_max); + } + + xsane_back_gtk_update_vector(xsane.well_known.gamma_vector, xsane.gamma_data); + free(xsane.gamma_data); + xsane.gamma_data = 0; + } + + xsane_clear_histogram(&xsane.histogram_raw); + xsane_clear_histogram(&xsane.histogram_enh); + xsane_set_sensitivity(FALSE); + + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + xsane.reading_data = FALSE; + + xsane.scanning = TRUE; /* set marker that scan has been initiated */ + + xsane_start_scan(); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static void xsane_create_internal_gamma_tables(void) +{ + int size, maxval; + + size = (int) pow(2, xsane.param.depth); + maxval = size-1; + + if (xsane.xsane_colors > 1) /* color scan */ + { + if ( (!xsane.scanner_gamma_color) && (xsane.scanner_gamma_gray) ) + { + /* 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; + + DBG(DBG_info, "creating xsane internal color gamma tables with size %d\n", size); + + xsane.gamma_data_red = malloc(size * sizeof(SANE_Int)); + xsane.gamma_data_green = malloc(size * sizeof(SANE_Int)); + xsane.gamma_data_blue = malloc(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, xsane.medium_negative, + gamma_red, xsane.brightness_red, xsane.contrast_red, + xsane.medium_shadow_red, xsane.medium_highlight_red, xsane.medium_gamma_red, + size, maxval); + + xsane_create_gamma_curve(xsane.gamma_data_green, xsane.medium_negative, + gamma_green, xsane.brightness_green, xsane.contrast_green, + xsane.medium_shadow_green, xsane.medium_highlight_green, xsane.medium_gamma_green, + size, maxval); + + xsane_create_gamma_curve(xsane.gamma_data_blue, xsane.medium_negative, + gamma_blue, xsane.brightness_blue, xsane.contrast_blue, + xsane.medium_shadow_blue, xsane.medium_highlight_blue, xsane.medium_gamma_blue, + size, maxval); + + /* gamma tables are freed after scan */ + } + else if ( (!xsane.scanner_gamma_color) && (!xsane.scanner_gamma_gray) ) /* no scanner gamma table */ + { + double gamma_red, gamma_green, gamma_blue; + /* ok, we have to combin gray and color slider values */ + + DBG(DBG_info, "creating xsane internal complete gamma tables with size %d\n", size); + + xsane.gamma_data_red = malloc(size * sizeof(SANE_Int)); + xsane.gamma_data_green = malloc(size * sizeof(SANE_Int)); + xsane.gamma_data_blue = malloc(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 != xsane.medium_negative, + gamma_red, + xsane.brightness + xsane.brightness_red, + xsane.contrast + xsane.contrast_red, + xsane.medium_shadow_red, xsane.medium_highlight_red, xsane.medium_gamma_red, + size, maxval); + + xsane_create_gamma_curve(xsane.gamma_data_green, xsane.negative != xsane.medium_negative, + gamma_green, + xsane.brightness + xsane.brightness_green, + xsane.contrast + xsane.contrast_green, + xsane.medium_shadow_green, xsane.medium_highlight_green, xsane.medium_gamma_green, + size, maxval); + + xsane_create_gamma_curve(xsane.gamma_data_blue, xsane.negative != xsane.medium_negative, + gamma_blue, + xsane.brightness + xsane.brightness_blue, + xsane.contrast + xsane.contrast_blue, + xsane.medium_shadow_blue, xsane.medium_highlight_blue, xsane.medium_gamma_blue, + size, maxval); + + /* gamma tables are freed after scan */ + } + } + else /* grayscale scan */ + { + if (!xsane.scanner_gamma_gray) /* no gray scanner gamma table */ + { + double gamma; + + DBG(DBG_info, "creating xsane internal gray gamma table with size %d\n", size); + + if (xsane.xsane_mode == XSANE_COPY) + { + gamma = xsane.gamma * preferences.printer[preferences.printernr]->gamma; + } + else + { + gamma = xsane.gamma; + } + + xsane.gamma_data = malloc(size * sizeof(SANE_Int)); + xsane_create_gamma_curve(xsane.gamma_data, xsane.negative != xsane.medium_negative, + gamma, xsane.brightness, xsane.contrast, + xsane.medium_shadow_gray, xsane.medium_highlight_gray, xsane.medium_gamma_gray, + size, maxval); + + /* gamma table is freed after scan */ + } + } +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + |