diff options
author | Julien BLACHE <jblache@debian.org> | 2006-01-10 21:58:08 +0100 |
---|---|---|
committer | Mattia Rizzolo <mattia@mapreri.org> | 2014-10-03 14:05:22 +0000 |
commit | a1871913efc67d71e7b355a8a03655cbf12ab07f (patch) | |
tree | cfe538674b55d1d0325e83bbbe1a2703bd2262b9 /src/xsane-save.c.orig | |
parent | ab2ece358cc58b7a7d1671a72d62c2cafe8811bb (diff) | |
parent | 7fad909266d49203537a28e6acc5d96b14bf4e7f (diff) |
Imported Debian patch 0.99-1debian/0.99-1
Diffstat (limited to 'src/xsane-save.c.orig')
-rw-r--r-- | src/xsane-save.c.orig | 5801 |
1 files changed, 5801 insertions, 0 deletions
diff --git a/src/xsane-save.c.orig b/src/xsane-save.c.orig new file mode 100644 index 0000000..a495e1c --- /dev/null +++ b/src/xsane-save.c.orig @@ -0,0 +1,5801 @@ +/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend + + xsane-save.c + + Oliver Rauch <Oliver.Rauch@rauch-domain.de> + Copyright (C) 1998-2005 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 <time.h> +#include <sys/wait.h> + +/* the following test is always false */ +#ifdef _native_WIN32 +# include <winsock.h> +#else +# include <sys/socket.h> +# include <netinet/in.h> +# include <netdb.h> +#endif + +#ifdef HAVE_LIBJPEG +#include <jpeglib.h> +#endif + +#ifdef HAVE_LIBZ +#include <zlib.h> +#endif + +#ifdef HAVE_LIBPNG +#include <png.h> +#endif + +#ifdef HAVE_LIBTIFF +#include <tiffio.h> +#endif + +#ifdef HAVE_MMAP +#include <sys/mman.h> +#endif + +#ifdef HAVE_OS2_H +#include <process.h> +#endif + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +#ifdef HAVE_ANY_GIMP + +#include <libgimp/gimp.h> + +static void xsane_gimp_query(void); +#ifdef HAVE_GIMP_2 +static void xsane_gimp_run(const gchar *name, gint nparams, const GimpParam *param, gint *nreturn_vals, GimpParam **return_vals); +#else +static void xsane_gimp_run(char *name, int nparams, GimpParam *param, int *nreturn_vals, GimpParam **return_vals); +#endif + +GimpPlugInInfo PLUG_IN_INFO = +{ + NULL, /* init_proc */ + NULL, /* quit_proc */ + xsane_gimp_query, /* query_proc */ + xsane_gimp_run, /* run_proc */ +}; + + +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); + +#endif /* HAVE_ANY_GIMP */ + +/* ---------------------------------------------------------------------------------------------------------------------- */ +/* why this routine ? + Problem: link attack + Bad user wants to overwrite a file (mywork.txt) of good user. + File permissions of mywork.txt is 700 so that bad user can not + change or overwrite the file. Directory permissions allow bad user + to write into directory. Bad user sets symlink from a file that good + user will write soon (image.pnm) to mywork.txt. + ==> Good user overwrites his own file, he is allowed to do so. + + Solution: remove file. + Create outputfile and make sure that it does not exist while creation. + + The file is created with the requested image-file permissions. + + Note: This case is a bit curious because it is only a small part of a larger problem: + When other users have write access to the directory they simply can move + mywork.txt to image.pnm. If they do it in the right moment the file is + overwritten without any notice of good user. If they do it long before xsane + wants to write image.pnm then xsane will possibly ask if image.pnm shall be + overwritten. So the real solution is to make the direcoty permissions safe!!! + But some users asked for this and so I added this. + + + This routine shall not be called for temporary files because temp files shall not + be removed after they have been created safe. (Although a temporary file should + not be a symlink so there should be no problem with this) +*/ + +int xsane_create_secure_file(const char *filename) +/* returns 0 on success, -1 on error */ +{ + int fd; + + DBG(DBG_proc, "xsane_create_secure_file\n"); + + remove(filename); /* we need to remove the file because open(..., O_EXCL) will fail otherwise */ + umask((mode_t) preferences.image_umask); /* define image file permissions */ + fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0666); + umask(XSANE_DEFAULT_UMASK); /* define new file permissions */ + + if (fd > 0) + { + DBG(DBG_info, "file %s is created and secure\n", filename); + close(fd); + fd = 0; + } + else + { + DBG(DBG_info, "could not create secure file %s\n", filename); + } + + return fd; /* -1 means file is not safe !!! otherwise 0 */ +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_cancel_save(int *cancel_save) +{ + DBG(DBG_proc, "xsane_cancel_save\n"); + *cancel_save = 1; +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_convert_text_to_filename(char **text) +{ + DBG(DBG_proc, "xsane_convert_text_to_filename\n"); + + if (text) + { + char *filename = *text; + char buf[256]; + int buflen=0; + int txtlen=0; + + while((filename[txtlen] != 0) && (buflen<253)) + { + switch (filename[txtlen]) + { + case ' ': + buf[buflen++] = ':'; + buf[buflen++] = '_'; + txtlen++; + break; + + case '/': + buf[buflen++] = ':'; + buf[buflen++] = '%'; + txtlen++; + break; + + case '*': + buf[buflen++] = ':'; + buf[buflen++] = '#'; + txtlen++; + break; + + case '?': + buf[buflen++] = ':'; + buf[buflen++] = 'q'; + txtlen++; + break; + + case '\\': + buf[buflen++] = ':'; + buf[buflen++] = '='; + txtlen++; + break; + + case ';': + buf[buflen++] = ':'; + buf[buflen++] = '!'; + txtlen++; + break; + + case '&': + buf[buflen++] = ':'; + buf[buflen++] = '+'; + txtlen++; + break; + + case '<': + buf[buflen++] = ':'; + buf[buflen++] = 's'; + txtlen++; + break; + + case '>': + buf[buflen++] = ':'; + buf[buflen++] = 'g'; + txtlen++; + break; + + case '|': + buf[buflen++] = ':'; + buf[buflen++] = 'p'; + txtlen++; + break; + + case ':': + buf[buflen++] = ':'; + buf[buflen++] = ':'; + txtlen++; + break; + + default: + buf[buflen++] = filename[txtlen++]; + break; + } + } + buf[buflen] = 0; + free(filename); + *text = strdup(buf); + DBG(DBG_info, "filename = \"%s\"\n", *text); + } +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +int xsane_get_filesize(char *filename) +{ + FILE *infile; + int pos; + int size; + + infile = fopen(filename, "rb"); /* read binary (b for win32) */ + if (infile == NULL) + { + return 0; + } + + pos = ftell(infile); + fseek(infile, 0, SEEK_END); /* get size */ + size = ftell(infile); + fseek(infile, pos, SEEK_SET); /* go to previous position */ + + fclose(infile); + + return size; +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_ensure_counter_in_filename(char **filename, int counter_len) +{ + char *position_point = NULL; + char *position; + int counter = 1; + + DBG(DBG_proc, "xsane_ensure_counter_in_filename\n"); + + if (!counter_len) + { + counter_len = 1; + } + + position_point = strrchr(*filename, '.'); + + if (!position_point) /* nothing usable ? */ + { + position_point = *filename + strlen(*filename); /* position_point - 1 is last character */ + } + + if (position_point) + { + position = position_point-1; + if ( (position < *filename) || (*position < '0') || (*position >'9') ) /* we have no counter */ + { + char buf[PATH_MAX]; + int len; + + len = position_point - (*filename); /* length until "." or end of string */ + strncpy(buf, *filename, len); + snprintf(buf+len, sizeof(buf)-len, "-%0*d%s", counter_len, counter, position_point); + *filename = strdup(buf); + } + } +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_update_counter_in_filename(char **filename, int skip, int step, int min_counter_len) +{ + FILE *testfile; + char *position_point = NULL; + char *position_counter; + char buf[PATH_MAX]; + int counter; + int counter_len; + int set_counter_len = min_counter_len; + + DBG(DBG_proc, "xsane_update_counter_in_filename\n"); + + if ( (!step) && (!min_counter_len) ) + { + return; /* do not touch counter */ + } + + while (1) /* loop because we may have to skip existing files */ + { + position_point = strrchr(*filename, '.'); + + if (!position_point) /* nothing usable ? */ + { + position_point = *filename + strlen(*filename); /* here is no point, but position - 1 is last character */ + } + + if (position_point) + { + position_counter = position_point-1; /* go to last number of counter (if counter exists) */ + + /* search non numeric char */ + while ( (position_counter >= *filename) && (*position_counter >= '0') && (*position_counter <='9') ) + { + position_counter--; /* search fisrt numeric character */ + } + + position_counter++; /* go to first numeric charcter */ + + counter_len = position_point - position_counter; + + if (counter_len) /* we have a counter */ + { + sscanf(position_counter, "%d", &counter); + counter = counter + step; /* update counter */ + + if (counter < 0) + { + counter = 0; + xsane_back_gtk_warning(WARN_COUNTER_UNDERRUN, TRUE); + break; /* last available number ("..999") */ + } + + *position_counter = 0; /* set end of string mark to counter start */ + + if (set_counter_len == 0) + { + set_counter_len = counter_len; + } + + snprintf(buf, sizeof(buf), "%s%0*d%s", *filename, set_counter_len, counter, position_point); + + DBG(DBG_info, "filename = \"%s\"\n", buf); + + free(*filename); + *filename = strdup(buf); + + if (skip) /* test if filename already used */ + { + if (preferences.filetype) /* add filetype to filename */ + { + snprintf(buf, sizeof(buf), "%s%s", *filename, preferences.filetype); + testfile = fopen(buf, "rb"); /* read binary (b for win32) */ + } + else /* filetype in filename */ + { + testfile = fopen(*filename, "rb"); /* read binary (b for win32) */ + } + + if (testfile) /* filename used: skip */ + { + fclose(testfile); + } + else + { + break; /* filename not used, ok */ + } + } + else /* do not test if filename already used */ + { + break; /* filename ok */ + } + } + else /* no counter */ + { + break; /* no counter */ + } + } + } +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_read_pnm_header(FILE *file, Image_info *image_info) +{ + int max_val, filetype_nr; + char buf[256]; + + fgets(buf, sizeof(buf)-1, file); + DBG(DBG_info, "filetype header :%s", buf); + + if (buf[0] == 'P') + { + filetype_nr = atoi(buf+1); /* get filetype number */ + + image_info->resolution_x = 72.0; + image_info->resolution_y = 72.0; + image_info->reduce_to_lineart = FALSE; + + while (strcmp(buf, "# XSANE data follows\n")) + { + fgets(buf, sizeof(buf)-1, file); + + if (!strncmp(buf, "# resolution_x =", 20)) + { + sscanf(buf+20, "%lf", &image_info->resolution_x); + } + else if (!strncmp(buf, "# resolution_y =", 20)) + { + sscanf(buf+20, "%lf", &image_info->resolution_y); + } + else if (!strncmp(buf, "# threshold =", 20)) + { + sscanf(buf+20, "%lf", &image_info->threshold); + } + else if (!strncmp(buf, "# gamma =", 20)) + { + sscanf(buf+20, "%lf", &image_info->gamma); + } + else if (!strncmp(buf, "# gamma IRGB =", 20)) + { + sscanf(buf+20, "%lf %lf %lf %lf", + &image_info->gamma, + &image_info->gamma_red, + &image_info->gamma_green, + &image_info->gamma_blue); + } + else if (!strncmp(buf, "# brightness =", 20)) + { + sscanf(buf+20, "%lf", &image_info->brightness); + } + else if (!strncmp(buf, "# brightness IRGB =", 20)) + { + sscanf(buf+20, "%lf %lf %lf %lf", + &image_info->brightness, + &image_info->brightness_red, + &image_info->brightness_green, + &image_info->brightness_blue); + } + else if (!strncmp(buf, "# contrast =", 20)) + { + sscanf(buf+20, "%lf", &image_info->contrast); + } + else if (!strncmp(buf, "# contrast IRGB =", 20)) + { + sscanf(buf+20, "%lf %lf %lf %lf", + &image_info->contrast, + &image_info->contrast_red, + &image_info->contrast_green, + &image_info->contrast_blue); + } + else if (!strncmp(buf, "# reduce to lineart", 20)) + { + image_info->reduce_to_lineart = TRUE; + } + } + + fscanf(file, "%d %d", &image_info->image_width, &image_info->image_height); + + image_info->depth = 1; + + if (filetype_nr != 4) /* P4 = lineart */ + { + fscanf(file, "%d", &max_val); + + if (max_val == 255) + { + image_info->depth = 8; + } + else if (max_val == 65535) + { + image_info->depth = 16; + } + } + + fgetc(file); /* read exactly one newline character */ + + + image_info->colors = 1; + + if (filetype_nr == 6) /* ppm RGB */ + { + image_info->colors = 3; + } + } +#ifdef SUPPORT_RGBA + else if (buf[0] == 'S') /* RGBA format */ + { + fscanf(file, "%d %d\n%d", &image_info->image_width, &image_info->image_height, &max_val); + fgetc(file); /* read exactly one newline character */ + + image_info->depth = 1; + + if (max_val == 255) + { + image_info->depth = 8; + } + else if (max_val == 65535) + { + image_info->depth = 16; + } + + image_info->colors = 4; + } +#endif + + DBG(DBG_info, "xsane_read_pnm_header: width=%d, height=%d, depth=%d, colors=%d, resolution_x=%f, resolution_y=%f\n", + image_info->image_width, image_info->image_height, image_info->depth, image_info->colors, + image_info->resolution_x, image_info->resolution_y); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_write_pnm_header(FILE *file, Image_info *image_info, int save_pnm16_as_ascii) +{ + int maxval; + int magic; + + fflush(file); + rewind(file); + + if (image_info->depth > 8) + { + maxval = 65535; + + if (save_pnm16_as_ascii) + { + magic = 2; /* thats the magic number for grayscale ascii, 3 = color ascii */ + } + else /* save pnm as binary */ + { + magic = 5; /* that is the magic number for grayscake binary, 6 = color binary */ + } + } + else + { + maxval = 255; + magic = 5; /* 8 bit images are always saved in binary mode */ + } + + + if (image_info->colors == 1) + { + if (image_info->depth == 1) + { + /* do not touch the texts and length here, the reading routine needs to know the exact texts */ + fprintf(file, "P4\n" + "# XSane settings:\n" + "# resolution_x = %6.1f\n" + "# resolution_y = %6.1f\n" + "# threshold = %4.1f\n" + "# XSANE data follows\n" + "%05d %05d\n", + image_info->resolution_x, + image_info->resolution_y, + image_info->threshold, + image_info->image_width, image_info->image_height); + } + else if (image_info->reduce_to_lineart) + { + /* do not touch the texts and length here, the reading routine needs to know the exact texts */ + fprintf(file, "P%d\n" + "# XSane settings:\n" + "# resolution_x = %6.1f\n" + "# resolution_y = %6.1f\n" + "# threshold = %4.1f\n" + "# reduce to lineart\n" + "# XSANE data follows\n" + "%05d %05d\n" + "%d\n", + magic, /* P5 for binary, P2 for ascii */ + image_info->resolution_x, + image_info->resolution_y, + image_info->threshold, + image_info->image_width, image_info->image_height, + maxval); + } + else + { + fprintf(file, "P%d\n" + "# XSane settings:\n" + "# resolution_x = %6.1f\n" + "# resolution_y = %6.1f\n" + "# gamma = %3.2f\n" + "# brightness = %4.1f\n" + "# contrast = %4.1f\n" + "# XSANE data follows\n" + "%05d %05d\n" + "%d\n", + magic, /* P5 for binary, P2 for ascii */ + image_info->resolution_x, + image_info->resolution_y, + image_info->gamma, + image_info->brightness, + image_info->contrast, + image_info->image_width, image_info->image_height, + maxval); + } + } + else if (image_info->colors == 3) + { + fprintf(file, "P%d\n" + "# XSane settings:\n" + "# resolution_x = %6.1f\n" + "# resolution_y = %6.1f\n" + "# gamma IRGB = %3.2f %3.2f %3.2f %3.2f\n" + "# brightness IRGB = %4.1f %4.1f %4.1f %4.1f\n" + "# contrast IRGB = %4.1f %4.1f %4.1f %4.1f\n" + "# XSANE data follows\n" + "%05d %05d\n" \ + "%d\n", + magic+1, /* P6 for binary, P3 for ascii */ + image_info->resolution_x, + image_info->resolution_y, + image_info->gamma, image_info->gamma_red, image_info->gamma_green, image_info->gamma_blue, + image_info->brightness, image_info->brightness_red, image_info->brightness_green, image_info->brightness_blue, + image_info->contrast, image_info->contrast_red, image_info->contrast_green, image_info->contrast_blue, + image_info->image_width, image_info->image_height, + maxval); + } +#ifdef SUPPORT_RGBA + else if (image_info->colors == 4) + { + fprintf(file, "SANE_RGBA\n" \ + "%d %d\n" \ + "%d\n", + image_info->image_width, image_info->image_height, maxval); + } +#endif +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +int xsane_copy_file(FILE *outfile, FILE *infile, GtkProgressBar *progress_bar, int *cancel_save) +{ + long size; + long bytes_sum = 0; + size_t bytes; + unsigned char buf[65536]; + + DBG(DBG_proc, "copying file\n"); + + fseek(infile, 0, SEEK_END); + size = ftell(infile); + fseek(infile, 0, SEEK_SET); + + gtk_progress_bar_update(GTK_PROGRESS_BAR(progress_bar), 0.0); + + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + while (!feof(infile)) + { + bytes = fread(buf, 1, sizeof(buf), infile); + if (bytes > 0) + { + fwrite(buf, 1, bytes, outfile); + bytes_sum += bytes; + } + + gtk_progress_bar_update(progress_bar, (float) bytes_sum / size); /* update progress bar */ + + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + if (ferror(infile)) + { + char buf[255]; + + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_READ, strerror(errno)); + DBG(DBG_error, "%s\n", buf); + xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */); + *cancel_save = 1; + break; + } + + if (ferror(outfile)) + { + char buf[255]; + + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno)); + DBG(DBG_error, "%s\n", buf); + xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */); + *cancel_save = 1; + break; + } + + if (*cancel_save) + { + break; + } + } + + fflush(outfile); + + if (size != bytes_sum) + { + DBG(DBG_info, "copy errro, not complete, %ld bytes of %ld bytes copied\n", bytes_sum, size); + *cancel_save = 1; + return (*cancel_save); + } + + DBG(DBG_info, "copy complete, %ld bytes copied\n", bytes_sum); + + return (*cancel_save); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +int xsane_copy_file_by_name(char *output_filename, char *input_filename, GtkProgressBar *progress_bar, int *cancel_save) +{ + FILE *infile; + FILE *outfile; + + DBG(DBG_proc, "copying file %s to %s\n", input_filename, output_filename); + + outfile = fopen(output_filename, "wb"); /* b = binary mode for win32 */ + + if (outfile == 0) + { + char buf[255]; + + snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, output_filename, strerror(errno)); + xsane_back_gtk_error(buf, TRUE); + return -2; + } + + infile = fopen(input_filename, "rb"); /* read binary (b for win32) */ + if (infile == 0) + { + char buf[256]; + snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, input_filename, strerror(errno)); + xsane_back_gtk_error(buf, TRUE); + + fclose(outfile); + remove(output_filename); /* remove already created output file */ + return -1; + } + + xsane_copy_file(outfile, infile, progress_bar, cancel_save); + + fclose(infile); + fclose(outfile); + + gtk_progress_set_format_string(GTK_PROGRESS(progress_bar), ""); + gtk_progress_bar_update(GTK_PROGRESS_BAR(progress_bar), 0.0); + + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + return (*cancel_save); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +int xsane_save_grayscale_image_as_lineart(FILE *outfile, FILE *imagefile, Image_info *image_info, GtkProgressBar *progress_bar, int *cancel_save) +{ + int x, y, bit; + u_char bitval, packed; + + *cancel_save = 0; + + image_info->depth = 1; + + xsane_write_pnm_header(outfile, image_info, 0); + + for (y = 0; y < image_info->image_height; y++) + { + bit = 128; + packed = 0; + + for (x = 0; x < image_info->image_width; x++) + { + bitval = fgetc(imagefile); + + if (!bitval) /* white gets 0 bit, black gets 1 bit */ + { + packed |= bit; + } + + if (bit == 1) + { + fputc(packed, outfile); + bit = 128; + packed = 0; + } + else + { + bit >>= 1; + } + } + + if (bit != 128) + { + fputc(packed, outfile); + bit = 128; + packed = 0; + } + + if (ferror(outfile)) + { + char buf[255]; + + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno)); + DBG(DBG_error, "%s\n", buf); + xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */); + *cancel_save = 1; + break; + } + + + gtk_progress_bar_update(progress_bar, (float) y / image_info->image_height); /* update progress bar */ + while (gtk_events_pending()) /* give gtk the chance to display the changes */ + { + gtk_main_iteration(); + } + + if (*cancel_save) + { + break; + } + } + + return (*cancel_save); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +int xsane_save_scaled_image(FILE *outfile, FILE *imagefile, Image_info *image_info, float x_scale, float y_scale, GtkProgressBar *progress_bar, int *cancel_save) +{ + int original_image_width = image_info->image_width; + int original_image_height = image_info->image_height; + int new_image_width = image_info->image_width * x_scale + 0.5; + int new_image_height = image_info->image_height * y_scale + 0.5; + unsigned char *original_line; + guint16 *original_line16 = NULL; + unsigned char *new_line; + float *pixel_val; + float *pixel_norm; + int bytespp = 1; + float x, y; + int c; + int oldy; + int x_new, y_new; + float x_go, y_go; + float factor, x_factor, y_factor; + guint16 color; + int read_line; + + DBG(DBG_proc, "xsane_save_scaled_image\n"); + + *cancel_save = 0; + + if (image_info->depth > 8) + { + bytespp = 2; + } + + image_info->image_width = new_image_width; + image_info->image_height = new_image_height; + image_info->resolution_x *= x_scale; + image_info->resolution_y *= y_scale; + + original_line = malloc(original_image_width * image_info->colors * bytespp); + if (!original_line) + { + DBG(DBG_error, "xsane_save_scaled_image: out of memory\n"); + return -1; + } + + new_line = malloc(new_image_width * image_info->colors * bytespp); + if (!new_line) + { + free(original_line); + DBG(DBG_error, "xsane_save_scaled_image: out of memory\n"); + return -1; + } + + pixel_val = malloc(new_image_width * image_info->colors * sizeof(float)); + if (!pixel_val) + { + free(original_line); + free(new_line); + DBG(DBG_error, "xsane_save_scaled_image: out of memory\n"); + return -1; + } + + pixel_norm = malloc(new_image_width * image_info->colors * sizeof(float)); + if (!pixel_norm) + { + free(original_line); + free(new_line); + free(pixel_val); + DBG(DBG_error, "xsane_save_scaled_image: out of memory\n"); + return -1; + } + + xsane_write_pnm_header(outfile, image_info, 0); + + read_line = TRUE; + + memset(pixel_val, 0, new_image_width * image_info->colors * sizeof(float)); + memset(pixel_norm, 0, new_image_width * image_info->colors * sizeof(float)); + + y_new = 0; + y_go = 1.0 / y_scale; + y_factor = 1.0; + y = 0.0; + + while (y < original_image_height) + { + DBG(DBG_info2, "xsane_save_scaled_image: original line %d, new line %d\n", (int) y, y_new); + + gtk_progress_bar_update(progress_bar, (float) y / original_image_height); + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + if (read_line) + { + DBG(DBG_info, "xsane_save_scaled_image: reading original line %d\n", (int) y); + fread(original_line, original_image_width, image_info->colors * bytespp, imagefile); /* read one line */ + original_line16 = (guint16 *) original_line; + } + + x_new = 0; + x_go = 1.0 / x_scale; + x = 0.0; + x_factor = 1.0; + + while ( (x < original_image_width) && (x_new < new_image_width) ) /* add this line to anti aliasing buffer */ + + { + factor = x_factor * y_factor; + + for (c = 0; c < image_info->colors; c++) + { + if (bytespp == 1) + { + color = original_line[((int) x) * image_info->colors + c]; + } + else /* bytespp == 2 */ + { + color = original_line16[((int) x) * image_info->colors + c]; + } + + pixel_val [x_new * image_info->colors + c] += factor * color; + pixel_norm[x_new * image_info->colors + c] += factor; + } + + x_go -= x_factor; + + if (x_go <= 0.0) /* change of pixel in new image */ + { + x_new++; + x_go = 1.0 / x_scale; + + x_factor = x - (int) x; /* use pixel rest */ + if (x_factor > x_go) + { + x_factor = x_go; + } + } + else + { + x_factor = x_go; + } + + if (x_factor > 1.0) + { + x_factor = 1.0; + } + + x += x_factor; + } + + y_go -= y_factor; + + if (y_go <= 0.0) /* normalize one line and write to destination image file */ + { + DBG(DBG_info2, "xsane_save_scaled_image: writing new line %d\n", y_new); + + if (bytespp == 1) + { + for (x_new = 0; x_new < new_image_width * image_info->colors; x_new++) + { + new_line[x_new] = (int) (pixel_val[x_new] / pixel_norm[x_new]); + } + } + else /* bytespp == 2 */ + { + guint16 *new_line16 = (guint16 *) new_line; + + for (x_new = 0; x_new < new_image_width * image_info->colors; x_new++) + { + new_line16[x_new] = (int) (pixel_val[x_new] / pixel_norm[x_new]); + } + } + + fwrite(new_line, new_image_width, image_info->colors * bytespp, outfile); /* write one line */ + + if (ferror(outfile)) + { + char buf[255]; + + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno)); + DBG(DBG_error, "%s\n", buf); + xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */); + *cancel_save = 1; + break; + } + + /* reset values and norm factors */ + memset(pixel_val, 0, new_image_width * image_info->colors * sizeof(float)); + memset(pixel_norm, 0, new_image_width * image_info->colors * sizeof(float)); + + y_new++; + y_go = 1.0 / y_scale; + + y_factor = y - (int) y; + if (y_factor > y_go) + { + y_factor = y_go; + } + } + else + { + y_factor = y_go; + } + + if (y_factor > 1.0) + { + y_factor = 1.0; + } + + oldy = (int) y; + y += y_factor; + read_line = (oldy != (int) y); + } + + free(original_line); + free(new_line); + free(pixel_val); + free(pixel_norm); + + return (*cancel_save); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ +#if 0 +int xsane_save_scaled_image(FILE *outfile, FILE *imagefile, Image_info *image_info, float x_scale, float y_scale, GtkProgressBar *progress_bar, int *cancel_save) +{ + float original_y; + int old_original_y; + int x, y, i; + int original_image_width = image_info->image_width; + int new_image_width = image_info->image_width * x_scale; + int new_image_height = image_info->image_height * y_scale; + unsigned char *original_line; + unsigned char *new_line; + int bytespp = 1; + + DBG(DBG_proc, "xsane_save_scaled_image\n"); + + if (image_info->depth > 8) + { + bytespp = 2; + } + + image_info->image_width = new_image_width; + image_info->image_height = new_image_height; + image_info->resolution_x *= x_scale; + image_info->resolution_y *= y_scale; + + original_line = malloc(original_image_width * image_info->colors * bytespp); + if (!original_line) + { + DBG(DBG_error, "xsane_save_scaled_image: out of memory\n"); + return -1; + } + + new_line = malloc(new_image_width * image_info->colors * bytespp); + if (!new_line) + { + free(original_line); + DBG(DBG_error, "xsane_save_scaled_image: out of memory\n"); + return -1; + } + + xsane_write_pnm_header(outfile, image_info, 0); + + original_y = 0.0; + old_original_y = -1; + + for (y = 0; y < new_image_height; y++) + { + gtk_progress_bar_update(progress_bar, (float) y / image_info->image_height); + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + for (; ((int) original_y) - old_original_y; old_original_y += 1) + { + fread(original_line, original_image_width, image_info->colors * bytespp, imagefile); /* read one line */ + } + + for (x = 0; x < new_image_width; x++) + { + for (i = 0; i < image_info->colors * bytespp; i++) + { + new_line[x * image_info->colors * bytespp + i] = original_line[((int) (x / x_scale)) * image_info->colors * bytespp + i]; + } + } + + fwrite(new_line, new_image_width, image_info->colors * bytespp, outfile); /* write one line */ + + original_y += 1/y_scale; + + if (*cancel_save) + { + break; + } + } + + free(original_line); + free(new_line); + + fflush(outfile); + + return (*cancel_save); +} +#endif + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +int xsane_save_despeckle_image(FILE *outfile, FILE *imagefile, Image_info *image_info, int radius, GtkProgressBar *progress_bar, int *cancel_save) +{ + int x, y, sx, sy, i; + int xmin, xmax; + int ymin, ymax; + int count; + unsigned char *line_cache; + unsigned char *line_cache_ptr; + guint16 *color_cache; + guint16 *color_cache_ptr; + int bytespp = 1; + int color_radius; + int color_width = image_info->image_width * image_info->colors; + + radius--; /* correct radius : 1 means nothing happens */ + + if (radius < 1) + { + radius = 1; + } + + color_radius = radius * image_info->colors; + + if (image_info->depth > 8) + { + bytespp = 2; + } + + xsane_write_pnm_header(outfile, image_info, 0); + + line_cache = malloc(color_width * bytespp * (2 * radius + 1)); + if (!line_cache) + { + DBG(DBG_error, "xsane_despeckle_image: out of memory\n"); + return -1; + } + + fread(line_cache, color_width * bytespp, (2 * radius + 1), imagefile); + + color_cache = malloc((size_t) sizeof(guint16) * (2*radius+1)*(2*radius+1)); + + if (!color_cache) + { + free(line_cache); + DBG(DBG_error, "xsane_despeckle_image: out of memory\n"); + return -1; + } + + for (y = 0; y < image_info->image_height; y++) + { + gtk_progress_bar_update(progress_bar, (float) y / image_info->image_height); + + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + ymin = y - radius; + ymax = y + radius; + + if (ymin < 0) + { + ymin = 0; + } + + if (ymax > image_info->image_height) + { + ymax = image_info->image_height; + } + + for (x = 0; x < color_width; x++) + { + xmin = x - color_radius; + xmax = x + color_radius; + + if (xmin < 0) + { + xmin = x % image_info->colors; + } + + if (xmax > color_width) + { + xmax = color_width; + } + + count = 0; + + color_cache_ptr = color_cache; + + + if (bytespp == 1) + { + for (sy = ymin; sy <= ymax; sy++) /* search area defined by radius - y part */ + { + line_cache_ptr = line_cache + (sy-ymin) * color_width + xmin; + + for (sx = xmin; sx <= xmax; sx+=image_info->colors) /* x part */ + { + *color_cache_ptr = *line_cache_ptr; + color_cache_ptr++; + line_cache_ptr += image_info->colors; + } + } + + /* sort color_cache */ + + count = color_cache_ptr - color_cache; + + if (count > 1) + { + int d, j, val; + + for (d = count / 2; d > 0; d = d / 2) + { + for (i = d; i < count; i++) + { + for (j = i - d, color_cache_ptr = color_cache + j; j >= 0 && color_cache_ptr[0] > color_cache_ptr[d]; j -= d, color_cache_ptr -= d) + { + val = color_cache_ptr[0]; + color_cache_ptr[0] = color_cache_ptr[d]; + color_cache_ptr[d] = val; + }; + } + } + } + + fputc((char) (color_cache[count/2]), outfile); + } + else /* 16 bit/color */ + { + guint16 val16; + guint16 *line_cache16 = (guint16 *) line_cache; + guint16 *line_cache16_ptr; + char *bytes16 = (char *) &val16; + + for (sy = ymin; sy <= ymax; sy++) + { + line_cache16_ptr = line_cache16 + (sy-ymin) * color_width + xmin; + + for (sx = xmin; sx <= xmax; sx+=image_info->colors) + { + *color_cache_ptr = *line_cache16_ptr; + color_cache_ptr++; + line_cache16_ptr += image_info->colors; + } + } + + /* sort color_cache */ + + count = color_cache_ptr - color_cache; + + if (count > 1) + { + int d,j, val; + + for (d = count / 2; d > 0; d = d / 2) + { + for (i = d; i < count; i++) + { + for (j = i - d, color_cache_ptr = color_cache + j; j >= 0 && color_cache_ptr[0] > color_cache_ptr[d]; j -= d, color_cache_ptr -= d) + { + val = color_cache_ptr[0]; + color_cache_ptr[0] = color_cache_ptr[d]; + color_cache_ptr[d] = val; + }; + } + } + } + + val16 = color_cache[count/2]; + fputc(bytes16[0], outfile); /* write bytes in machine byte order */ + fputc(bytes16[1], outfile); + } + } + + if (ferror(outfile)) + { + char buf[255]; + + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno)); + DBG(DBG_error, "%s\n", buf); + xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */); + *cancel_save = 1; + break; + } + + if ((y > radius) && (y < image_info->image_height - radius)) + { + memcpy(line_cache, line_cache + color_width * bytespp, + color_width * bytespp * 2 * radius); + fread(line_cache + color_width * bytespp * 2 * radius, + color_width * bytespp, 1, imagefile); + } + } + + fflush(outfile); + + free(line_cache); + free(color_cache); + + return 0; +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +int xsane_save_blur_image(FILE *outfile, FILE *imagefile, Image_info *image_info, float radius, GtkProgressBar *progress_bar, int *cancel_save) +{ + int x, y, sx, sy; + int xmin, xmax; + int ymin, ymax; + double val, norm, outer_factor; + unsigned char *line_cache; + int bytespp = 1; + int intradius; + int xmin_flag; + int xmax_flag; + int ymin_flag; + int ymax_flag; + + *cancel_save = 0; + + intradius = (int) radius; + + outer_factor = radius - (int) radius; + + if (image_info->depth > 8) + { + bytespp = 2; + } + + xsane_write_pnm_header(outfile, image_info, 0); + + line_cache = malloc(image_info->image_width * image_info->colors * bytespp * (2 * intradius + 1)); + if (!line_cache) + { + DBG(DBG_error, "xsane_blur_image: out of memory\n"); + return -1; + } + + fread(line_cache, image_info->image_width * image_info->colors * bytespp, (2 * intradius + 1), imagefile); + + for (y = 0; y < image_info->image_height; y++) + { + gtk_progress_bar_update(progress_bar, (float) y / image_info->image_height); + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + for (x = 0; x < image_info->image_width * image_info->colors; x++) + { + xmin_flag = xmax_flag = ymin_flag = ymax_flag = TRUE; + + xmin = x - intradius * image_info->colors; + xmax = x + intradius * image_info->colors; + + if (xmin < 0) + { + xmin = x % image_info->colors; + xmin_flag = FALSE; + } + + if (xmax > image_info->image_width * image_info->colors) + { + xmax = image_info->image_width * image_info->colors; + xmax_flag = FALSE; + } + + ymin = y - intradius; + ymax = y + intradius; + + if (ymin < 0) + { + ymin = 0; + ymin_flag = FALSE; + } + + if (ymax > image_info->image_height) + { + ymax = image_info->image_height; + ymax_flag = FALSE; + } + + val = 0.0; + norm = 0.0; + + if (bytespp == 1) + { + if (xmin_flag) /* integrate over left margin */ + { + for (sy = ymin+1; sy <= ymax-1 ; sy++) + { + val += outer_factor * line_cache[(sy-ymin) * image_info->image_width * image_info->colors + xmin]; + norm += outer_factor; + } + } + + if (xmax_flag) /* integrate over right margin */ + { + for (sy = ymin+1; sy <= ymax-1 ; sy++) + { + val += outer_factor * line_cache[(sy-ymin) * image_info->image_width * image_info->colors + xmax]; + norm += outer_factor; + } + } + + if (ymin_flag) /* integrate over top margin */ + { + for (sx = xmin+image_info->colors; sx <= xmax-image_info->colors ; sx += image_info->colors) + { + val += outer_factor * line_cache[sx]; + norm += outer_factor; + } + } + + if (ymax_flag) /* integrate over bottom margin */ + { + for (sx = xmin+image_info->colors; sx <= xmax-image_info->colors ; sx += image_info->colors) + { + val += outer_factor * line_cache[(ymax-ymin) * image_info->image_width * image_info->colors + sx]; + norm += outer_factor; + } + } + + for (sy = ymin+1; sy <= ymax-1; sy++) /* integrate internal square */ + { + for (sx = xmin+image_info->colors; sx <= xmax-image_info->colors; sx+=image_info->colors) + { + val += line_cache[(sy-ymin) * image_info->image_width * image_info->colors + sx]; + norm += 1.0; + } + } + fputc((char) ((int) (val/norm)), outfile); + } + else /* bytespp == 2 */ + { + guint16 *line_cache16 = (guint16 *) line_cache; + guint16 val16; + char *bytes16 = (char *) &val16; + + if (xmin_flag) /* integrate over left margin */ + { + for (sy = ymin+1; sy <= ymax-1 ; sy++) + { + val += outer_factor * line_cache16[(sy-ymin) * image_info->image_width * image_info->colors + xmin]; + norm += outer_factor; + } + } + + if (xmax_flag) /* integrate over right margin */ + { + for (sy = ymin+1; sy <= ymax-1 ; sy++) + { + val += outer_factor * line_cache16[(sy-ymin) * image_info->image_width * image_info->colors + xmax]; + norm += outer_factor; + } + } + + if (ymin_flag) /* integrate over top margin */ + { + for (sx = xmin+image_info->colors; sx <= xmax-image_info->colors ; sx += image_info->colors) + { + val += outer_factor * line_cache16[sx]; + norm += outer_factor; + } + } + + if (ymax_flag) /* integrate over bottom margin */ + { + for (sx = xmin+image_info->colors; sx <= xmax-image_info->colors ; sx += image_info->colors) + { + val += outer_factor * line_cache16[(ymax-ymin) * image_info->image_width * image_info->colors + sx]; + norm += outer_factor; + } + } + + for (sy = ymin; sy <= ymax; sy++) /* integrate internal square */ + { + for (sx = xmin; sx <= xmax; sx+=image_info->colors) + { + val += line_cache16[(sy-ymin) * image_info->image_width * image_info->colors + sx]; + norm += 1.0; + } + } + + val16 = val / norm; + fputc(bytes16[0], outfile); /* write bytes in machine byte order */ + fputc(bytes16[1], outfile); + } + } + + if (ferror(outfile)) + { + char buf[255]; + + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno)); + DBG(DBG_error, "%s\n", buf); + xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */); + *cancel_save = 1; + break; + } + + /* reset values and norm factors */ + + if ((y > intradius) && (y < image_info->image_height - intradius)) + { + memcpy(line_cache, line_cache + image_info->image_width * image_info->colors * bytespp, + image_info->image_width * image_info->colors * bytespp * 2 * intradius); + fread(line_cache + image_info->image_width * image_info->colors * bytespp * 2 * intradius, + image_info->image_width * image_info->colors * bytespp, 1, imagefile); + } + } + + fflush(outfile); + free(line_cache); + + return 0; +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +#if 0 +int xsane_save_blur_image(FILE *outfile, FILE *imagefile, Image_info *image_info, int radius, GtkProgressBar *progress_bar) +{ + int x, y, sx, sy; + int xmin, xmax; + int ymin, ymax; + int pos0; + int val, count; + unsigned char *line_cache; + int bytespp = 1; + + if (image_info->depth > 8) + { + bytespp = 2; + } + + pos0 = ftell(imagefile); /* mark position to skip header */ + + xsane_write_pnm_header(outfile, image_info, 0); + + line_cache = malloc(image_info->image_width * image_info->colors * bytespp * (2 * radius + 1)); + if (!line_cache) + { + DBG(DBG_error, "xsane_blur_image: out of memory\n"); + return -1; + } + + fread(line_cache, image_info->image_width * image_info->colors * bytespp, (2 * radius + 1), imagefile); + + for (y = 0; y < image_info->image_height; y++) + { + gtk_progress_bar_update(progress_bar, (float) y / image_info->image_height); + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + for (x = 0; x < image_info->image_width * image_info->colors; x++) + { + xmin = x - radius * image_info->colors; + xmax = x + radius * image_info->colors; + + if (xmin < 0) + { + xmin = x % image_info->colors; + } + + if (xmax > image_info->image_width * image_info->colors) + { + xmax = image_info->image_width * image_info->colors; + } + + ymin = y - radius; + ymax = y + radius; + + if (ymin < 0) + { + ymin = 0; + } + + if (ymax > image_info->image_height) + { + ymax = image_info->image_height; + } + + val = 0; + count = 0; + + if (bytespp == 1) + { + for (sy = ymin; sy <= ymax; sy++) + { + for (sx = xmin; sx <= xmax; sx+=image_info->colors) + { + val += line_cache[(sy-ymin) * image_info->image_width * image_info->colors + sx]; + count++; + } + } + fputc((char) (val/count), outfile); + } + else + { + guint16 *line_cache16 = (guint16 *) line_cache; + guint16 val16; + char *bytes16 = (char *) &val16; + + for (sy = ymin; sy <= ymax; sy++) + { + for (sx = xmin; sx <= xmax; sx+=image_info->colors) + { + val += line_cache16[(sy-ymin) * image_info->image_width * image_info->colors + sx]; + count++; + } + } + + val16 = val / count; + fputc(bytes16[0], outfile); /* write bytes in machine byte order */ + fputc(bytes16[1], outfile); + } + } + + if ((y > radius) && (y < image_info->image_height - radius)) + { + memcpy(line_cache, line_cache + image_info->image_width * image_info->colors * bytespp, + image_info->image_width * image_info->colors * bytespp * 2 * radius); + fread(line_cache + image_info->image_width * image_info->colors * bytespp * 2 * radius, + image_info->image_width * image_info->colors * bytespp, 1, imagefile); + } + } + + fflush(outfile); + free(line_cache); + + return 0; +} +#endif + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +int xsane_save_rotate_image(FILE *outfile, FILE *imagefile, Image_info *image_info, int rotation, GtkProgressBar *progress_bar, int *cancel_save) +/* returns true if operation was cancelled */ +{ + int x, y, pos0, bytespp, i; + int pixel_width = image_info->image_width; + int pixel_height = image_info->image_height; + float resolution_x = image_info->resolution_x; + float resolution_y = image_info->resolution_y; + +#ifdef HAVE_MMAP + char *mmaped_imagefile = NULL; +#endif + + DBG(DBG_proc, "xsane_save_rotate_image\n"); + + *cancel_save = 0; + + pos0 = ftell(imagefile); /* mark position to skip header */ + + bytespp = image_info->colors; + + if (image_info->depth > 8) + { + bytespp *= 2; + } + + if (image_info->depth < 8) /* lineart images are expanded to grayscale until transformation is done */ + { + image_info->depth = 8; /* so we have at least 8 bits/pixel here */ + } + +#ifdef HAVE_MMAP + mmaped_imagefile = mmap(NULL, pixel_width * pixel_height * bytespp + pos0, PROT_READ, MAP_PRIVATE, fileno(imagefile), 0); + if (mmaped_imagefile == (char *) -1) /* mmap failed */ + { + DBG(DBG_info, "xsane_save_rotate_image: unable to memory map image file, using standard file access\n"); + mmaped_imagefile = NULL; + } + else + { + DBG(DBG_info, "xsane_save_rotate_image: using memory mapped image file\n"); + } +#endif + + switch (rotation) + { + default: + break; + + case 0: /* 0 degree */ + xsane_write_pnm_header(outfile, image_info, 0); + + for (y = 0; y < pixel_height; y++) + { + gtk_progress_bar_update(progress_bar, (float) y / pixel_height); + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + for (x = 0; x < pixel_width; x++) + { +#ifdef HAVE_MMAP + if (mmaped_imagefile) + { + char *p = mmaped_imagefile + pos0 + bytespp * (x + y * pixel_width); /* calculate correct position */ + + for (i=0; i<bytespp; i++) + { + fputc(*p++, outfile); + } + } + else +#endif + { + for (i = 0; i < bytespp; i++) + { + fputc(fgetc(imagefile), outfile); + } + } + } + + if (ferror(outfile)) + { + char buf[255]; + + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno)); + DBG(DBG_error, "%s\n", buf); + xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */); + *cancel_save = 1; + break; + } + + if (*cancel_save) + { + break; + } + } + break; + + case 1: /* 90 degree */ + image_info->image_width = pixel_height; + image_info->image_height = pixel_width; + + image_info->resolution_x = resolution_y; + image_info->resolution_y = resolution_x; + + xsane_write_pnm_header(outfile, image_info, 0); + + for (x=0; x<pixel_width; x++) + { + gtk_progress_bar_update(progress_bar, (float) x / pixel_width); + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + for (y=pixel_height-1; y>=0; y--) + { +#ifdef HAVE_MMAP + if (mmaped_imagefile) + { + char *p = mmaped_imagefile + pos0 + bytespp * (x + y * pixel_width); /* calculate correct position */ + + for (i=0; i<bytespp; i++) + { + fputc(*p++, outfile); + } + } + else +#endif + { + fseek(imagefile, pos0 + bytespp * (x + y * pixel_width), SEEK_SET); /* go to the correct position */ + for (i=0; i<bytespp; i++) + { + fputc(fgetc(imagefile), outfile); + } + } + } + + + if (ferror(outfile)) + { + char buf[255]; + + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno)); + DBG(DBG_error, "%s\n", buf); + xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */); + *cancel_save = 1; + break; + } + + if (*cancel_save) + { + break; + } + } + + break; + + case 2: /* 180 degree */ + xsane_write_pnm_header(outfile, image_info, 0); + + for (y = pixel_height-1; y >= 0; y--) + { + gtk_progress_bar_update(progress_bar, (float) (pixel_height - y) / pixel_height); + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + for (x = pixel_width-1; x >= 0; x--) + { +#ifdef HAVE_MMAP + if (mmaped_imagefile) + { + char *p = mmaped_imagefile + pos0 + bytespp * (x + y * pixel_width); /* calculate correct position */ + + for (i = 0; i < bytespp; i++) + { + fputc(*p++, outfile); + } + } + else +#endif + { + fseek(imagefile, pos0 + bytespp * (x + y * pixel_width), SEEK_SET); /* go to the correct position */ + for (i = 0; i < bytespp; i++) + { + fputc(fgetc(imagefile), outfile); + } + } + } + + + if (ferror(outfile)) + { + char buf[255]; + + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno)); + DBG(DBG_error, "%s\n", buf); + xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */); + *cancel_save = 1; + break; + } + + if (*cancel_save) + { + break; + } + } + break; + + case 3: /* 270 degree */ + image_info->image_width = pixel_height; + image_info->image_height = pixel_width; + + image_info->resolution_x = resolution_y; + image_info->resolution_y = resolution_x; + + xsane_write_pnm_header(outfile, image_info, 0); + + for (x = pixel_width-1; x >= 0; x--) + { + gtk_progress_bar_update(progress_bar, (float) (pixel_width - x) / pixel_width); + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + for (y = 0; y < pixel_height; y++) + { +#ifdef HAVE_MMAP + if (mmaped_imagefile) + { + char *p = mmaped_imagefile + pos0 + bytespp * (x + y * pixel_width); /* calculate correct position */ + + for (i = 0; i < bytespp; i++) + { + fputc(*p++, outfile); + } + } + else +#endif + { + fseek(imagefile, pos0 + bytespp * (x + y * pixel_width), SEEK_SET); /* go to the correct position */ + for (i = 0; i < bytespp; i++) + { + fputc(fgetc(imagefile), outfile); + } + } + } + + + if (ferror(outfile)) + { + char buf[255]; + + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno)); + DBG(DBG_error, "%s\n", buf); + xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */); + *cancel_save = 1; + break; + } + + if (*cancel_save) + { + break; + } + } + break; + + case 4: /* 0 degree, x mirror */ + xsane_write_pnm_header(outfile, image_info, 0); + + for (y = 0; y < pixel_height; y++) + { + gtk_progress_bar_update(progress_bar, (float) y / pixel_height); + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + for (x = pixel_width-1; x >= 0; x--) + { +#ifdef HAVE_MMAP + if (mmaped_imagefile) + { + char *p = mmaped_imagefile + pos0 + bytespp * (x + y * pixel_width); /* calculate correct position */ + + for (i = 0; i < bytespp; i++) + { + fputc(*p++, outfile); + } + } + else +#endif + { + fseek(imagefile, pos0 + bytespp * (x + y * pixel_width), SEEK_SET); /* go to the correct position */ + for (i = 0; i < bytespp; i++) + { + fputc(fgetc(imagefile), outfile); + } + } + } + + + if (ferror(outfile)) + { + char buf[255]; + + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno)); + DBG(DBG_error, "%s\n", buf); + xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */); + *cancel_save = 1; + break; + } + + if (*cancel_save) + { + break; + } + } + break; + + case 5: /* 90 degree, x mirror */ + image_info->image_width = pixel_height; + image_info->image_height = pixel_width; + + image_info->resolution_x = resolution_y; + image_info->resolution_y = resolution_x; + + xsane_write_pnm_header(outfile, image_info, 0); + + for (x = 0; x < pixel_width; x++) + { + gtk_progress_bar_update(progress_bar, (float) x / pixel_width); + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + for (y = 0; y < pixel_height; y++) + { +#ifdef HAVE_MMAP + if (mmaped_imagefile) + { + char *p = mmaped_imagefile + pos0 + bytespp * (x + y * pixel_width); /* calculate correct position */ + + for (i=0; i<bytespp; i++) + { + fputc(*p++, outfile); + } + } + else +#endif + { + fseek(imagefile, pos0 + bytespp * (x + y * pixel_width), SEEK_SET); /* go to the correct position */ + for (i = 0; i < bytespp; i++) + { + fputc(fgetc(imagefile), outfile); + } + } + } + + + if (ferror(outfile)) + { + char buf[255]; + + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno)); + DBG(DBG_error, "%s\n", buf); + xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */); + *cancel_save = 1; + break; + } + + if (*cancel_save) + { + break; + } + } + + break; + + case 6: /* 180 degree, x mirror */ + xsane_write_pnm_header(outfile, image_info, 0); + + for (y = pixel_height-1; y >= 0; y--) + { + gtk_progress_bar_update(progress_bar, (float) (pixel_height - y) / pixel_height); + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + for (x = 0; x < pixel_width; x++) + { +#ifdef HAVE_MMAP + if (mmaped_imagefile) + { + char *p = mmaped_imagefile + pos0 + bytespp * (x + y * pixel_width); /* calculate correct position */ + + for (i = 0; i < bytespp; i++) + { + fputc(*p++, outfile); + } + } + else +#endif + { + fseek(imagefile, pos0 + bytespp * (x + y * pixel_width), SEEK_SET); /* go to the correct position */ + for (i = 0; i < bytespp; i++) + { + fputc(fgetc(imagefile), outfile); + } + } + } + + + if (ferror(outfile)) + { + char buf[255]; + + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno)); + DBG(DBG_error, "%s\n", buf); + xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */); + *cancel_save = 1; + break; + } + + if (*cancel_save) + { + break; + } + } + break; + + case 7: /* 270 degree, x mirror */ + image_info->image_width = pixel_height; + image_info->image_height = pixel_width; + + image_info->resolution_x = resolution_y; + image_info->resolution_y = resolution_x; + + xsane_write_pnm_header(outfile, image_info, 0); + + for (x = pixel_width-1; x >= 0; x--) + { + gtk_progress_bar_update(progress_bar, (float) (pixel_width - x) / pixel_width); + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + for (y = pixel_height-1; y >= 0; y--) + { +#ifdef HAVE_MMAP + if (mmaped_imagefile) + { + char *p = mmaped_imagefile + pos0 + bytespp * (x + y * pixel_width); /* calculate correct position */ + + for (i = 0; i < bytespp; i++) + { + fputc(*p++, outfile); + } + } + else +#endif + { + fseek(imagefile, pos0 + bytespp * (x + y * pixel_width), SEEK_SET); /* go to the correct position */ + for (i = 0; i < bytespp; i++) + { + fputc(fgetc(imagefile), outfile); + } + } + } + + + if (ferror(outfile)) + { + char buf[255]; + + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno)); + DBG(DBG_error, "%s\n", buf); + xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */); + *cancel_save = 1; + break; + } + + if (*cancel_save) + { + break; + } + } + break; + } + +#ifdef HAVE_MMAP + if (mmaped_imagefile) + { + munmap(mmaped_imagefile, pos0 + pixel_width * pixel_height * bytespp); + } +#endif + + fflush(outfile); + + return (*cancel_save); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_save_ps_create_document_header(FILE *outfile, int pages, int flatdecode) +{ + DBG(DBG_proc, "xsane_save_ps_create_document_header\n"); + + fprintf(outfile, "%%!PS-Adobe-3.0\n"); + fprintf(outfile, "%%%%Creator: XSane version %s (sane %d.%d) - by Oliver Rauch\n", VERSION, + SANE_VERSION_MAJOR(xsane.sane_backend_versioncode), + SANE_VERSION_MINOR(xsane.sane_backend_versioncode)); + fprintf(outfile, "%%%%DocumentData: Clean7Bit\n"); + if (flatdecode) + { + fprintf(outfile, "%%%%LanguageLevel: 3\n"); + } + else + { + fprintf(outfile, "%%%%LanguageLevel: 2\n"); + } + + if (pages) + { + fprintf(outfile, "%%%%Pages: %d\n", pages); + } + else + { + fprintf(outfile, "%%%%Pages: (atend)\n"); + } + + fprintf(outfile, "%%%%EndComments\n"); + fprintf(outfile, "\n"); + fprintf(outfile, "/origstate save def\n"); + fprintf(outfile, "20 dict begin\n"); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_save_ps_create_document_trailer(FILE *outfile, int pages) +{ + DBG(DBG_proc, "xsane_save_ps_create_document_trailer\n"); + + fprintf(outfile, "end\n"); + fprintf(outfile, "origstate restore\n"); + + if (pages) + { + fprintf(outfile, "%%%%Trailer\n"); + fprintf(outfile, "%%%%Pages: %d\n", pages); + } + + fprintf(outfile, "%%%%EOF\n"); + fprintf(outfile, "\n"); + +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +/* page = [1 .. pages] */ +static void xsane_save_ps_create_page_header(FILE *outfile, int page, + Image_info *image_info, + float width, float height, + int paper_left_margin, int paper_bottom_margin, + int paper_width, int paper_height, + int paper_orientation, int flatdecode, + GtkProgressBar *progress_bar) +{ + int degree, position_left, position_bottom, box_left, box_bottom, box_right, box_top, depth; + int left, bottom; + + DBG(DBG_proc, "xsane_save_ps_create_page_header\n"); + + switch (paper_orientation) + { + default: + case 0: /* top left portrait */ + left = 0.0; + bottom = paper_height - height; + break; + + case 1: /* top right portrait */ + left = paper_width - width; + bottom = paper_height - height; + break; + + case 2: /* bottom right portrait */ + left = paper_width - width; + bottom = 0.0; + break; + + case 3: /* bottom left portrait */ + left = 0.0; + bottom = 0.0; + break; + + case 4: /* center portrait */ + left = paper_width / 2.0 - width / 2.0; + bottom = paper_height / 2.0 - height / 2.0; + break; + + + case 8: /* top left landscape */ + left = 0.0; + bottom = paper_width - height; + break; + + case 9: /* top right landscape */ + left = paper_height - width; + bottom = paper_width - height; + break; + + case 10: /* bottom right landscape */ + left = paper_height - width; + bottom = 0.0; + break; + + case 11: /* bottom left landscape */ + left = 0.0; + bottom = 0.0; + break; + + case 12: /* center landscape */ + left = paper_height / 2.0 - width / 2.0; + bottom = paper_width / 2.0 - height / 2.0; + break; + } + + + if (paper_orientation >= 8) /* rotate with 90 degrees - landscape mode */ + { + degree = 90; + position_left = left + paper_bottom_margin; + position_bottom = bottom - paper_width - paper_left_margin; + box_left = paper_width - paper_left_margin - bottom - height; + box_bottom = left + paper_bottom_margin; + box_right = box_left + ceil(height); + box_top = box_bottom + ceil(width); + } + else /* do not rotate, portrait mode */ + { + degree = 0; + position_left = left + paper_left_margin; + position_bottom = bottom + paper_bottom_margin; + box_left = left + paper_left_margin; + box_bottom = bottom + paper_bottom_margin; + box_right = box_left + ceil(width); + box_top = box_bottom + ceil(height); + } + + depth = image_info->depth; + + if (depth > 8) + { + depth = 8; + } + + fprintf(outfile, "\n"); + fprintf(outfile, "%%%%Page: %d %d\n", page, page); + fprintf(outfile, "%%%%PageBoundingBox: %d %d %d %d\n", box_left, box_bottom, box_right, box_top); + + if (depth == 1) + { + fprintf(outfile, "/grays %d string def\n", image_info->image_width); + fprintf(outfile, "/npixels 0 def\n"); + fprintf(outfile, "/rgbindx 0 def\n"); + } + + fprintf(outfile, "%d rotate\n", degree); + fprintf(outfile, "%d %d translate\n", position_left, position_bottom); + fprintf(outfile, "%f %f scale\n", width, height); + fprintf(outfile, "%d %d %d\n", image_info->image_width, image_info->image_height, depth); + fprintf(outfile, "[%d %d %d %d %d %d]\n", image_info->image_width, 0, 0, -image_info->image_height, 0, image_info->image_height); + fprintf(outfile, "currentfile\n"); + fprintf(outfile, "/ASCII85Decode filter\n"); +#ifdef HAVE_LIBZ + if (flatdecode) + { + fprintf(outfile, "/FlateDecode filter\n"); + } +#endif + + if (image_info->colors == 3) /* what about RGBA here ? */ + { + fprintf(outfile, "false 3 colorimage\n"); + fprintf(outfile, "\n"); + } + else + { + fprintf(outfile, "image\n"); + fprintf(outfile, "\n"); + } +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static void xsane_save_ps_create_page_trailer(FILE *outfile) +{ + fprintf(outfile, "\n"); + fprintf(outfile, "showpage\n"); + fprintf(outfile, "%%%%PageTrailer\n"); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +#ifdef HAVE_LIBZ +/* Utility function for the PostScript output */ +static int xsane_write_compressed_a85_flatdecode(FILE *outfile, unsigned char *line, int len, int finish) +{ + static unsigned char *cbuf = NULL; + static int cbuflen = 0; + static int linelen = 0; + int i, j; + int outlen; + static int init = 0; + static z_stream s; + int ret; + int flush; + static int a85count = 0; + static guint32 a85tuple = 0; + static unsigned char a85block[6] = {0, 0, 0, 0, 0, 0}; + static int count = 0; + + DBG(DBG_proc, "xsane_write_compressed_a85_flatdecode\n"); + + if (linelen != len) + { + linelen = len; + if (cbuf != NULL) + { + free(cbuf); + } + /* buffer length = length + 0.1 * length + 12 (mandatory) */ + cbuflen = len + len / 10 + 12; + cbuf = malloc(cbuflen); + } + + if (cbuf == NULL) + { + DBG(DBG_error, "cbuf allocation failed\n"); + return 1; + } + + if (!init) + { + s.zalloc = Z_NULL; + s.zfree = Z_NULL; + s.opaque = Z_NULL; + + ret = deflateInit(&s, Z_DEFAULT_COMPRESSION); + + if (ret != Z_OK) + { + DBG(DBG_error, "deflateInit failed\n"); + free(cbuf); + return 1; + } + + init = 1; + } + + s.avail_in = len; + s.next_in = line; + + do + { + s.avail_out = cbuflen; + s.next_out = cbuf; + + flush = (finish) ? Z_FINISH : Z_NO_FLUSH; + + ret = deflate(&s, flush); + + if (ret == Z_STREAM_ERROR) + { + DBG(DBG_error, "deflate failed\n"); + free(cbuf); + return 1; + } + + outlen = cbuflen - s.avail_out; + + /* ASCII85 (base 85) encoding */ + for (i = 0; i < outlen; i++) + { + switch (a85count) + { + case 0: + a85tuple |= (cbuf[i] << 24); + a85count++; + break; + + case 1: + a85tuple |= (cbuf[i] << 16); + a85count++; + break; + + case 2: + a85tuple |= (cbuf[i] << 8); + a85count++; + break; + + case 3: + a85tuple |= (cbuf[i] << 0); + + if (count == 40) + { + fprintf(outfile, "\n"); + count = 0; + } + + if (a85tuple == 0) + { + fprintf(outfile, "z"); + count++; + } + else + { + /* The ASCII chars must be written in reverse order, hence -> a85block[4-j] */ + for (j = 0; j < 5; j++) + { + a85block[4-j] = a85tuple % 85 + '!'; + a85tuple /= 85; + } + + for (j = 0; j < 5; j++) + { + fprintf(outfile, "%c", a85block[j]); + count++; + if (count == 40) + { + fprintf(outfile, "\n"); + count = 0; + } + } + } + + a85count = 0; + a85tuple = 0; + break; + + default: + break; + } + } + } while (s.avail_out == 0); + + if (finish) + { + DBG(DBG_info, "finish\n"); + if (a85count > 0) + { + a85count++; + for (j = 0; j <= a85count; j++) + { + a85block[j] = a85tuple % 85 + '!'; + a85tuple /= 85; + } + /* Reverse order */ + for (j--; j > 0; j--) + { + if (count == 40) + { + fprintf(outfile, "\n"); + count = 0; + } + fprintf(outfile, "%c", a85block[j]); + count++; + } + } + + /* ASCII85 EOD marker + newline*/ + if (count + 2 > 40) + { + fprintf(outfile, "\n"); + } + fprintf(outfile, "~>\n"); + deflateEnd(&s); + free(cbuf); + cbuf = NULL; + init = 0; + a85tuple = 0; + a85count = 0; + cbuflen = 0; + linelen = 0; + count = 0; + } + + return 0; +} +#endif + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +/* Utility function for the PostScript output */ +static int xsane_write_compressed_a85(FILE *outfile, unsigned char *line, int len, int finish) +{ + static unsigned char *cbuf = NULL; + static int cbuflen = 0; + static int linelen = 0; + int i, j; + int outlen; + static int a85count = 0; + static guint32 a85tuple = 0; + static unsigned char a85block[6] = {0, 0, 0, 0, 0, 0}; + static int count = 0; + + DBG(DBG_proc, "xsane_write_compressed_a85\n"); + + cbuf = line; + outlen = len; + + /* ASCII85 (base 85) encoding */ + for (i = 0; i < outlen; i++) + { + switch (a85count) + { + case 0: + a85tuple |= (cbuf[i] << 24); + a85count++; + break; + + case 1: + a85tuple |= (cbuf[i] << 16); + a85count++; + break; + + case 2: + a85tuple |= (cbuf[i] << 8); + a85count++; + break; + + case 3: + a85tuple |= (cbuf[i] << 0); + + if (count == 40) + { + fprintf(outfile, "\n"); + count = 0; + } + + if (a85tuple == 0) + { + fprintf(outfile, "z"); + count++; + } + else + { + /* The ASCII chars must be written in reverse order, hence -> a85block[4-j] */ + for (j = 0; j < 5; j++) + { + a85block[4-j] = a85tuple % 85 + '!'; + a85tuple /= 85; + } + + for (j = 0; j < 5; j++) + { + fprintf(outfile, "%c", a85block[j]); + count++; + if (count == 40) + { + fprintf(outfile, "\n"); + count = 0; + } + } + } + + a85count = 0; + a85tuple = 0; + break; + + default: + break; + } + } + + if (finish) + { + DBG(DBG_info, "finish\n"); + if (a85count > 0) + { + a85count++; + for (j = 0; j <= a85count; j++) + { + a85block[j] = a85tuple % 85 + '!'; + a85tuple /= 85; + } + /* Reverse order */ + for (j--; j > 0; j--) + { + if (count == 40) + { + fprintf(outfile, "\n"); + count = 0; + } + fprintf(outfile, "%c", a85block[j]); + count++; + } + } + + /* ASCII85 EOD marker + newline*/ + if (count + 2 > 40) + { + fprintf(outfile, "\n"); + } + fprintf(outfile, "~>\n"); + a85tuple = 0; + a85count = 0; + cbuflen = 0; + linelen = 0; + count = 0; + } + + return 0; +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static int xsane_save_ps_pdf_bw(FILE *outfile, FILE *imagefile, Image_info *image_info, int flatdecode, GtkProgressBar *progress_bar, int *cancel_save) +{ + int x, y; + int bytes_per_line = (image_info->image_width+7)/8; + int ret; + unsigned char *line; + + DBG(DBG_proc, "xsane_save_ps_pdf_bw\n"); + + *cancel_save = 0; + + line = (unsigned char *) malloc(bytes_per_line); + + if (line == NULL) + { + char buf[255]; + + snprintf(buf, sizeof(buf), "%s malloc failed", ERR_DURING_SAVE); + DBG(DBG_error, "%s\n", buf); + xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */); + *cancel_save = 1; + return (*cancel_save); + } + + for (y = 0; y < image_info->image_height; y++) + { + gtk_progress_bar_update(progress_bar, (float) y / image_info->image_height); + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + for (x = 0; x < bytes_per_line; x++) + { + line[x] = fgetc(imagefile) ^ 255; + } + +#ifdef HAVE_LIBZ + if (flatdecode) + { + ret = xsane_write_compressed_a85_flatdecode(outfile, line, bytes_per_line, (y == image_info->image_height - 1)); + } + else +#endif + { + ret = xsane_write_compressed_a85(outfile, line, bytes_per_line, (y == image_info->image_height - 1)); + } + + if ((ret != 0) || (ferror(outfile))) + { + char buf[255]; + + if (ret == 0) + { + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno)); + } + else + { + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_ZLIB); + } + + DBG(DBG_error, "%s\n", buf); + xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */); + *cancel_save = 1; + + break; + } + + if (*cancel_save) + { + break; + } + } + + free(line); + + return (*cancel_save); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static int xsane_save_ps_pdf_gray(FILE *outfile, FILE *imagefile, Image_info *image_info, int flatdecode, GtkProgressBar *progress_bar, int *cancel_save) +{ + int x, y; + int ret; + unsigned char *line; + + DBG(DBG_proc, "xsane_save_ps_pdf_gray\n"); + + *cancel_save = 0; + + line = (unsigned char *) malloc(image_info->image_width); + + if (line == NULL) + { + char buf[255]; + + snprintf(buf, sizeof(buf), "%s malloc failed", ERR_DURING_SAVE); + DBG(DBG_error, "%s\n", buf); + xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */); + *cancel_save = 1; + return (*cancel_save); + } + + for (y = 0; y < image_info->image_height; y++) + { + if (image_info->depth > 8) /* reduce 16 bit images */ + { + guint16 val; + + for (x = 0; x < image_info->image_width; x++) + { + fread(&val, 2, 1, imagefile); + line[x] = val/256; + } + } + else /* 8 bits/sample */ + { + for (x = 0; x < image_info->image_width; x++) + { + line[x] = fgetc(imagefile); + } + } + +#ifdef HAVE_LIBZ + if (flatdecode) + { + ret = xsane_write_compressed_a85_flatdecode(outfile, line, image_info->image_width, (y == image_info->image_height - 1)); + } + else +#endif + { + ret = xsane_write_compressed_a85(outfile, line, image_info->image_width, (y == image_info->image_height - 1)); + } + + if ((ret != 0) || (ferror(outfile))) + { + char buf[255]; + + if (ret == 0) + { + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno)); + } + else + { + snprintf(buf, sizeof(buf), "%s zlib error or memory allocation problem", ERR_DURING_SAVE); + } + + DBG(DBG_error, "%s\n", buf); + xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */); + *cancel_save = 1; + + break; + } + + gtk_progress_bar_update(progress_bar, (float) y / image_info->image_height); + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + if (*cancel_save) + { + break; + } + } + + free(line); + + return (*cancel_save); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static int xsane_save_ps_pdf_color(FILE *outfile, FILE *imagefile, Image_info *image_info, int flatdecode, GtkProgressBar *progress_bar, int *cancel_save) +{ + int x, y; + int ret; + unsigned char *line, *linep; + + DBG(DBG_proc, "xsane_save_ps_pdf_color\n"); + + *cancel_save = 0; + + line = (unsigned char *) malloc(image_info->image_width * 3); + + if (line == NULL) + { + char buf[255]; + + snprintf(buf, sizeof(buf), "%s malloc failed", ERR_DURING_SAVE); + DBG(DBG_error, "%s\n", buf); + xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */); + *cancel_save = 1; + return (*cancel_save); + } + + for (y = 0; y < image_info->image_height; y++) + { + gtk_progress_bar_update(progress_bar, (float) y / image_info->image_height); + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + linep = line; + + if (image_info->depth > 8) /* reduce 16 bit images */ + { + guint16 val; + + for (x = 0; x < image_info->image_width; x++) + { + fread(&val, 2, 1, imagefile); + *linep++ = val/256; + fread(&val, 2, 1, imagefile); + *linep++ = val/256; + fread(&val, 2, 1, imagefile); + *linep++ = val/256; + } + } + else /* 8 bits/sample */ + { + for (x = 0; x < image_info->image_width; x++) + { + *linep++ = fgetc(imagefile); + *linep++ = fgetc(imagefile); + *linep++ = fgetc(imagefile); + } + } + +#ifdef HAVE_LIBZ + if (flatdecode) + { + ret = xsane_write_compressed_a85_flatdecode(outfile, line, (image_info->image_width * 3), (y == image_info->image_height - 1)); + } + else +#endif + { + ret = xsane_write_compressed_a85(outfile, line, (image_info->image_width * 3), (y == image_info->image_height - 1)); + } + + if ((ret != 0) || (ferror(outfile))) + { + char buf[255]; + + if (ret == 0) + { + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno)); + } + else + { + snprintf(buf, sizeof(buf), "%s zlib error or memory allocation problem", ERR_DURING_SAVE); + } + + DBG(DBG_error, "%s\n", buf); + xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */); + *cancel_save = 1; + + break; + } + + if (*cancel_save) + { + break; + } + } + + free(line); + + return (*cancel_save); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +int xsane_save_ps_page(FILE *outfile, int page, + FILE *imagefile, Image_info *image_info, float width, float height, + int paper_left_margin, int paper_bottom_margin, int paperheight, int paperwidth, int paper_orientation, + int flatdecode, + GtkProgressBar *progress_bar, int *cancel_save) +{ + DBG(DBG_proc, "xsane_save_ps_page\n"); + + xsane_save_ps_create_page_header(outfile, page, + image_info, width, height, + paper_left_margin, paper_bottom_margin, paperheight, paperwidth, paper_orientation, + flatdecode, + progress_bar); + + if (image_info->colors == 1) /* lineart, halftone, grayscale */ + { + if (image_info->depth == 1) /* lineart, halftone */ + { + xsane_save_ps_pdf_bw(outfile, imagefile, image_info, flatdecode, progress_bar, cancel_save); + } + else /* grayscale */ + { + xsane_save_ps_pdf_gray(outfile, imagefile, image_info, flatdecode, progress_bar, cancel_save); + } + } + else /* color RGB */ + { + xsane_save_ps_pdf_color(outfile, imagefile, image_info, flatdecode, progress_bar, cancel_save); + } + + xsane_save_ps_create_page_trailer(outfile); + + if (ferror(outfile)) + { + char buf[255]; + + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno)); + DBG(DBG_error, "%s\n", buf); + xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */); + *cancel_save = 1; + } + + return (*cancel_save); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +int xsane_save_ps(FILE *outfile, FILE *imagefile, Image_info *image_info, float width, float height, + int paper_left_margin, int paper_bottom_margin, int paperheight, int paperwidth, int paper_orientation, + int flatdecode, + GtkProgressBar *progress_bar, int *cancel_save) +{ + DBG(DBG_proc, "xsane_save_ps\n"); + + *cancel_save = 0; + + xsane_save_ps_create_document_header(outfile, 1 /* pages */, flatdecode); + + xsane_save_ps_page(outfile, 1 /* page */, + imagefile, image_info, width, height, + paper_left_margin, paper_bottom_margin, paperheight, paperwidth, paper_orientation, + flatdecode, + progress_bar, cancel_save); + + xsane_save_ps_create_document_trailer(outfile, 0 /* we defined pages at beginning */); + + if (ferror(outfile)) + { + char buf[255]; + + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno)); + DBG(DBG_error, "%s\n", buf); + xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */); + *cancel_save = 1; + } + + return (*cancel_save); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +/* The pdf_xref struct holds byte offsets from the beginning of the PDF + * file to each object of the PDF file -- used to build the xref table + */ +struct pdf_xref +{ + unsigned long obj5; /* obj 5 0 */ + unsigned long obj6; /* obj 6 0 */ + unsigned long obj7; /* obj 7 0 */ + unsigned long xref; /* xref table */ + unsigned long slen; /* length of image stream */ + unsigned long slenp; /* position of image stream length */ +}; + +static void xsane_save_pdf_create_header(FILE *outfile, Image_info *image_info, + float width, float height, + int paper_left_margin, int paper_bottom_margin, + int paper_width, int paper_height, + int paper_orientation, int flatdecode, + GtkProgressBar *progress_bar, struct pdf_xref *xref) +{ + int position_left, position_bottom, box_left, box_bottom, box_right, box_top, depth; + int left, bottom; + float rad; + + DBG(DBG_proc, "xsane_save_pdf_create_header\n"); + + switch (paper_orientation) + { + default: + case 0: /* top left portrait */ + left = 0.0; + bottom = paper_height - height; + break; + + case 1: /* top right portrait */ + left = paper_width - width; + bottom = paper_height - height; + break; + + case 2: /* bottom right portrait */ + left = paper_width - width; + bottom = 0.0; + break; + + case 3: /* bottom left portrait */ + left = 0.0; + bottom = 0.0; + break; + + case 4: /* center portrait */ + left = paper_width / 2.0 - width / 2.0; + bottom = paper_height / 2.0 - height / 2.0; + break; + + + case 8: /* top left landscape */ + left = 0.0; + bottom = paper_width - height; + break; + + case 9: /* top right landscape */ + left = paper_height - width; + bottom = paper_width - height; + break; + + case 10: /* bottom right landscape */ + left = paper_height - width; + bottom = 0.0; + break; + + case 11: /* bottom left landscape */ + left = 0.0; + bottom = 0.0; + break; + + case 12: /* center landscape */ + left = paper_height / 2.0 - width / 2.0; + bottom = paper_width / 2.0 - height / 2.0; + break; + } + + + if (paper_orientation >= 8) /* rotate with 90 degrees - landscape mode */ + { + rad = -M_PI_2; /* pi / 2 */ + position_left = left + paper_bottom_margin; + position_bottom = bottom - paper_width - paper_left_margin; + box_left = paper_width - paper_left_margin - bottom - height; + box_bottom = left + paper_bottom_margin; + box_right = box_left + ceil(height); + box_top = box_bottom + ceil(width); + } + else /* do not rotate, portrait mode */ + { + rad = 0; + position_left = left + paper_left_margin; + position_bottom = bottom + paper_bottom_margin; + box_left = left + paper_left_margin; + box_bottom = bottom + paper_bottom_margin; + box_right = box_left + ceil(width); + box_top = box_bottom + ceil(height); + } + + depth = image_info->depth; + + if (depth > 8) + { + depth = 8; + } + + fprintf(outfile, "%%PDF-1.4\n"); + fprintf(outfile, "\n"); + fprintf(outfile, "1 0 obj\n"); + fprintf(outfile, " << /Type /Catalog\n"); + fprintf(outfile, " /Outlines 2 0 R\n"); + fprintf(outfile, " /Pages 3 0 R\n"); + fprintf(outfile, " >>\n"); + fprintf(outfile, "endobj\n"); + fprintf(outfile, "\n"); + fprintf(outfile, "2 0 obj\n"); + fprintf(outfile, " << /Type /Outlines\n"); + fprintf(outfile, " /Count 0\n"); + fprintf(outfile, " >>\n"); + fprintf(outfile, "endobj\n"); + fprintf(outfile, "\n"); + fprintf(outfile, "3 0 obj\n"); + fprintf(outfile, " << /Type /Pages\n"); + fprintf(outfile, " /Kids [4 0 R]\n"); + fprintf(outfile, " /Count 1\n"); + fprintf(outfile, " >>\n"); + fprintf(outfile, "endobj\n"); + fprintf(outfile, "\n"); + fprintf(outfile, "4 0 obj\n"); + fprintf(outfile, " << /Type /Page\n"); + fprintf(outfile, " /Parent 3 0 R\n"); + fprintf(outfile, " /MediaBox [%d %d %d %d]\n", box_left, box_bottom, box_right, box_top); + fprintf(outfile, " /Contents 5 0 R\n"); + fprintf(outfile, " /Resources << /ProcSet 6 0 R >>\n"); + fprintf(outfile, " >>\n"); + fprintf(outfile, "endobj\n"); + fprintf(outfile, "\n"); + + /* Offset of object 5, for xref */ + xref->obj5 = ftell(outfile); + + fprintf(outfile, "5 0 obj\n"); + fprintf(outfile, " << /Length >>\n"); + + /* Position of the stream length, to be written later on */ + xref->slenp = ftell(outfile) - 15; + + fprintf(outfile, "stream\n"); + + /* Start of the stream data */ + xref->slen = ftell(outfile); + + fprintf(outfile, "q\n"); + fprintf(outfile, "1 0 0 1 %d %d cm\n", position_left, position_bottom); /* translate */ + fprintf(outfile, "%f %f -%f %f 0 0 cm\n", cos(rad), sin(rad), sin(rad), cos(rad)); /* rotate */ + fprintf(outfile, "%f 0 0 %f 0 0 cm\n", width, height); /* scale */ + fprintf(outfile, "BI\n"); + fprintf(outfile, " /W %d\n", image_info->image_width); + fprintf(outfile, " /H %d\n", image_info->image_height); + + if (image_info->colors == 3) /* what about RGBA here ? */ + { + fprintf(outfile, " /CS /RGB\n"); + fprintf(outfile, " /BPC %d\n", depth); + } + else if (image_info->depth == 1) /* BW */ + { + fprintf(outfile, " /CS /G\n"); + fprintf(outfile, " /BPC 1\n"); + } + else /* gray */ + { + fprintf(outfile, " /CS /G\n"); + fprintf(outfile, " /BPC 8\n"); + } + +#ifdef HAVE_LIBZ + if (flatdecode) + { + fprintf(outfile, " /F [/A85 /FlateDecode]\n"); + } + else + { + fprintf(outfile, " /F /A85\n"); + } +#else + fprintf(outfile, " /F /A85\n"); +#endif + fprintf(outfile, "ID\n"); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +int xsane_save_pdf(FILE *outfile, FILE *imagefile, Image_info *image_info, float width, float height, + int paper_left_margin, int paper_bottom_margin, int paperheight, int paperwidth, int paper_orientation, + int flatdecode, + GtkProgressBar *progress_bar, int *cancel_save) +{ + struct tm *t; + time_t tt; + struct pdf_xref xref; + + DBG(DBG_proc, "xsane_save_pdf\n"); + + *cancel_save = 0; + + xsane_save_pdf_create_header(outfile, image_info, width, height, + paper_left_margin, paper_bottom_margin, paperheight, paperwidth, paper_orientation, + flatdecode, + progress_bar, &xref); + + if (image_info->colors == 1) /* lineart, halftone, grayscale */ + { + if (image_info->depth == 1) /* lineart, halftone */ + { + xsane_save_ps_pdf_bw(outfile, imagefile, image_info, flatdecode, progress_bar, cancel_save); + } + else /* grayscale */ + { + xsane_save_ps_pdf_gray(outfile, imagefile, image_info, flatdecode, progress_bar, cancel_save); + } + } + else /* color RGB */ + { + xsane_save_ps_pdf_color(outfile, imagefile, image_info, flatdecode, progress_bar, cancel_save); + } + + /* PDF trailer */ + fprintf(outfile, "EI\n"); + fprintf(outfile, "Q\n"); + + /* Go back and write the length of the stream */ + xref.slen = ftell(outfile) - xref.slen - 1; + fseek(outfile, xref.slenp, SEEK_SET); + fprintf(outfile, "%lu", xref.slen); + fseek(outfile, 0L, SEEK_END); + + fprintf(outfile, "endstream\n"); + fprintf(outfile, "endobj\n"); + fprintf(outfile, "\n"); + + /* Offset of object 6, for xref */ + xref.obj6 = ftell(outfile); + + fprintf(outfile, "6 0 obj\n"); + fprintf(outfile, " [/PDF]\n"); + fprintf(outfile, "endobj\n"); + fprintf(outfile, "\n"); + + /* Offset of object 7, for xref */ + xref.obj7 = ftell(outfile); + + fprintf(outfile, "7 0 obj\n"); + fprintf(outfile, " << /Title (XSane scanned image)\n"); + fprintf(outfile, " /Creator (XSane version %s (sane %d.%d) - by Oliver Rauch)\n", + VERSION, + SANE_VERSION_MAJOR(xsane.sane_backend_versioncode), + SANE_VERSION_MINOR(xsane.sane_backend_versioncode)); + fprintf(outfile, " /Producer (XSane %s)\n", VERSION); + + tt = time(NULL); + t = gmtime(&tt); + + fprintf(outfile, " /CreationDate (D:%04d%02d%02d%02d%02d%02d+00'00')\n", + 1900 + t->tm_year, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); + fprintf(outfile, " >>\n"); + fprintf(outfile, "endobj\n"); + fprintf(outfile, "\n"); + + /* Offset of xref, for startxref below */ + xref.xref = ftell(outfile); + + fprintf(outfile, "xref\n"); + fprintf(outfile, "0 8\n"); + fprintf(outfile, "0000000000 65535 f \n"); + fprintf(outfile, "0000000010 00000 n \n"); + fprintf(outfile, "0000000094 00000 n \n"); + fprintf(outfile, "0000000153 00000 n \n"); + fprintf(outfile, "0000000229 00000 n \n"); + fprintf(outfile, "%010lu 00000 n \n", xref.obj5); + fprintf(outfile, "%010lu 00000 n \n", xref.obj6); + fprintf(outfile, "%010lu 00000 n \n", xref.obj7); + fprintf(outfile, "\n"); + fprintf(outfile, "trailer\n"); + fprintf(outfile, " << /Size 8\n"); + fprintf(outfile, " /Root 1 0 R\n"); + fprintf(outfile, " /Info 7 0 R\n"); + fprintf(outfile, " >>\n"); + fprintf(outfile, "startxref\n"); + fprintf(outfile, "%lu\n", xref.xref); + fprintf(outfile, "%%%%EOF\n"); + + if (ferror(outfile)) + { + char buf[255]; + + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno)); + DBG(DBG_error, "%s\n", buf); + xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */); + *cancel_save = 1; + } + + return (*cancel_save); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ +#ifdef HAVE_LIBJPEG + +typedef struct +{ + struct jpeg_error_mgr pub;/* "public" fields */ + int *cancel_save; +} xsane_jpeg_error_mgr; + +typedef xsane_jpeg_error_mgr *xsane_jpeg_error_mgr_ptr; + +static void xsane_jpeg_error_exit(j_common_ptr cinfo) +{ + char buf[256]; + + /* cinfo->err points to a xsane_jpeg_error_mgr struct */ + xsane_jpeg_error_mgr_ptr xsane_jpeg_error_mgr_data = (xsane_jpeg_error_mgr_ptr) cinfo->err; + + + if (!*xsane_jpeg_error_mgr_data->cancel_save) + { + /* output original error message */ + (*cinfo->err->output_message) (cinfo); + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_LIBJPEG); + xsane_back_gtk_error(buf, TRUE); + } + + *xsane_jpeg_error_mgr_data->cancel_save = 1; +} + +int xsane_save_jpeg(FILE *outfile, FILE *imagefile, Image_info *image_info, int quality, GtkProgressBar *progress_bar, int *cancel_save) +{ + unsigned char *data; + char buf[256]; + int components = 1; + int x,y; + int bytespp = 1; + struct jpeg_compress_struct cinfo; + xsane_jpeg_error_mgr jerr; + JSAMPROW row_pointer[1]; + + DBG(DBG_proc, "xsane_save_jpeg\n"); + + *cancel_save = 0; + + if (image_info->colors == 3) + { + components = 3; + } + + if (image_info->depth > 8) + { + bytespp = 2; + } + + data = malloc(image_info->image_width * components * bytespp); + + if (!data) + { + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM); + xsane_back_gtk_error(buf, TRUE); + return -1; /* error */ + } + + cinfo.err = jpeg_std_error(&jerr.pub); + jerr.pub.error_exit = xsane_jpeg_error_exit; + jerr.cancel_save = cancel_save; + + jpeg_create_compress(&cinfo); + jpeg_stdio_dest(&cinfo, outfile); + cinfo.image_width = image_info->image_width; + cinfo.image_height = image_info->image_height; + cinfo.input_components = components; + if (image_info->colors == 3) + { + cinfo.in_color_space = JCS_RGB; + } + else + { + cinfo.in_color_space = JCS_GRAYSCALE; + } + jpeg_set_defaults(&cinfo); + + jpeg_set_quality(&cinfo, quality, TRUE); + + cinfo.density_unit = 1; /* dpi */ + cinfo.X_density = image_info->resolution_x; + cinfo.Y_density = image_info->resolution_y; + + jpeg_start_compress(&cinfo, TRUE); + + for (y = 0; y < image_info->image_height; y++) + { + gtk_progress_bar_update(progress_bar, (float) y / image_info->image_height); + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + if (image_info->depth == 1) + { + int byte = 0; + int mask = 128; + + for (x = 0; x < image_info->image_width; x++) + { + + if ( (x % 8) == 0) + { + byte = fgetc(imagefile); + mask = 128; + } + + if (byte & mask) + { + data[x] = 0; + } + else + { + data[x] = 255; + } + mask >>= 1; + } + } + else if (image_info->depth > 8) /* jpeg does not support 16 bits/sample, so we reduce it at first */ + { + guint16 *data16 = (guint16 *) data; + fread(data, components * 2, image_info->image_width, imagefile); + for (x = 0; x < image_info->image_width * components; x++) + { + data[x] = data16[x] / 256; + } + + } + else /* 8 bits/sample */ + { + fread(data, components, image_info->image_width, imagefile); + } + + row_pointer[0] = data; + jpeg_write_scanlines(&cinfo, row_pointer, 1); + + if (*cancel_save) + { + cinfo.image_height = y; /* correct image height */ + break; + } + } + + jpeg_finish_compress(&cinfo); + free(data); + + return (*cancel_save); +} +#endif + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +#ifdef HAVE_LIBTIFF +/* pages = 0 => single page tiff, page = 0 */ +/* pages > 0 => page = [1 .. pages] */ +int xsane_save_tiff_page(TIFF *tiffile, int page, int pages, FILE *imagefile, Image_info *image_info, int quality, + GtkProgressBar *progress_bar, int *cancel_save) +{ + char *data; + char buf[256]; + int y, w; + int components; + int compression; + int bytes; + struct tm *ptm; + time_t now; + + DBG(DBG_proc, "xsane_save_tiff_page(%d/%d\n", page, pages); + + *cancel_save = 0; + + if (image_info->depth == 1) + { + compression = preferences.tiff_compression1_nr; + } + else if (image_info->depth == 8) + { + compression = preferences.tiff_compression8_nr; + } + else + { + compression = preferences.tiff_compression16_nr; + } + + + if (image_info->colors == 3) + { + components = 3; + } + else + { + components = 1; + } + + if (image_info->depth <= 8) + { + bytes = 1; + } + else + { + bytes = 2; + } + + data = (char *)_TIFFmalloc(image_info->image_width * components * bytes); + + if (!data) + { + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM); + xsane_back_gtk_error(buf, TRUE); + return -1; /* error */ + } + + TIFFSetField(tiffile, TIFFTAG_IMAGEWIDTH, image_info->image_width); + TIFFSetField(tiffile, TIFFTAG_IMAGELENGTH, image_info->image_height); + TIFFSetField(tiffile, TIFFTAG_BITSPERSAMPLE, image_info->depth); + TIFFSetField(tiffile, TIFFTAG_SAMPLESPERPIXEL, components); + TIFFSetField(tiffile, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + TIFFSetField(tiffile, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(tiffile, TIFFTAG_COMPRESSION, compression); + TIFFSetField(tiffile, TIFFTAG_SOFTWARE, "xsane"); + + time(&now); + ptm = localtime(&now); + sprintf(buf, "%04d:%02d:%02d %02d:%02d:%02d", 1900+ptm->tm_year, ptm->tm_mon+1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec); + TIFFSetField(tiffile, TIFFTAG_DATETIME, buf); + + if (image_info->resolution_x > 0.0) + { + TIFFSetField(tiffile, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH); + TIFFSetField(tiffile, TIFFTAG_XRESOLUTION, image_info->resolution_x); + TIFFSetField(tiffile, TIFFTAG_YRESOLUTION, image_info->resolution_y); + } + + if (compression == COMPRESSION_JPEG) + { + TIFFSetField(tiffile, TIFFTAG_JPEGQUALITY, quality); + TIFFSetField(tiffile, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RAW); /* should be default, but to be sure */ + } + + if (image_info->colors == 3) + { + TIFFSetField(tiffile, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + } + else + { + if (image_info->depth == 1) /* lineart */ + { + TIFFSetField(tiffile, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE); + } + else /* grayscale */ + { + TIFFSetField(tiffile, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); + } + } + + TIFFSetField(tiffile, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tiffile, -1)); + + if (pages) + { + TIFFSetField(tiffile, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE); + TIFFSetField(tiffile, TIFFTAG_PAGENUMBER, page, pages); + } + + w = TIFFScanlineSize(tiffile); + + for (y = 0; y < image_info->image_height; y++) + { + gtk_progress_bar_update(progress_bar, (float) y / image_info->image_height); + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + fread(data, 1, w, imagefile); + + if (TIFFWriteScanline(tiffile, data, y, 0) != 1) + { + char buf[255]; + + snprintf(buf, sizeof(buf), "%s", ERR_DURING_SAVE); + DBG(DBG_error, "%s\n", buf); + xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */); + *cancel_save = 1; + break; + } + + if (*cancel_save) + { + break; + } + } + + if (pages) + { + TIFFWriteDirectory(tiffile); + } + + _TIFFfree(data); + return (*cancel_save); +} +#endif + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +#ifdef HAVE_LIBPNG +#ifdef HAVE_LIBZ +int xsane_save_png(FILE *outfile, FILE *imagefile, Image_info *image_info, int compression, GtkProgressBar *progress_bar, int *cancel_save) +{ + png_structp png_ptr; + png_infop png_info_ptr; + png_bytep row_ptr; + png_color_8 sig_bit; + unsigned char *data; + char buf[256]; + int colortype, components, byte_width; + int y; + + DBG(DBG_proc, "xsane_save_png\n"); + + *cancel_save = 0; + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); + if (!png_ptr) + { + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_LIBPNG); + xsane_back_gtk_error(buf, TRUE); + return -1; /* error */ + } + + png_info_ptr = png_create_info_struct(png_ptr); + if (!png_info_ptr) + { + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_LIBTIFF); + xsane_back_gtk_error(buf, TRUE); + return -1; /* error */ + } + + if (setjmp(png_ptr->jmpbuf)) + { + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_LIBPNG); + xsane_back_gtk_error(buf, TRUE); + png_destroy_write_struct(&png_ptr, (png_infopp) 0); + return -1; /* error */ + } + + byte_width = image_info->image_width; + + if (image_info->colors == 4) /* RGBA */ + { + components = 4; + colortype = PNG_COLOR_TYPE_RGB_ALPHA; + } + else if (image_info->colors == 3) /* RGB */ + { + components = 3; + colortype = PNG_COLOR_TYPE_RGB; + } + else /* gray or black/white */ + { + components = 1; + colortype = PNG_COLOR_TYPE_GRAY; + } + + png_init_io(png_ptr, outfile); + png_set_compression_level(png_ptr, compression); + png_set_IHDR(png_ptr, png_info_ptr, image_info->image_width, image_info->image_height, image_info->depth, + colortype, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + if (image_info->colors >=3) + { + sig_bit.red = image_info->depth; + sig_bit.green = image_info->depth; + sig_bit.blue = image_info->depth; + + if (image_info->colors == 4) + { + sig_bit.alpha = image_info->depth; + } + + } + else + { + sig_bit.gray = image_info->depth; + + if (image_info->depth == 1) + { + byte_width = (image_info->image_width+7)/8; + png_set_invert_mono(png_ptr); + } + } + + png_set_sBIT(png_ptr, png_info_ptr, &sig_bit); +#if defined(PNG_pHYs_SUPPORTED) + png_set_pHYs(png_ptr, png_info_ptr, + image_info->resolution_x * 100.0 / 2.54, + image_info->resolution_y * 100.0 / 2.54, PNG_RESOLUTION_METER); +#endif + png_write_info(png_ptr, png_info_ptr); + png_set_shift(png_ptr, &sig_bit); + + data = malloc(image_info->image_width * components); + + if (!data) + { + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM); + xsane_back_gtk_error(buf, TRUE); + png_destroy_write_struct(&png_ptr, (png_infopp) 0); + return -1; /* error */ + } + + for (y = 0; y < image_info->image_height; y++) + { + gtk_progress_bar_update(progress_bar, (float) y / image_info->image_height); + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + fread(data, components, byte_width, imagefile); + + row_ptr = data; + png_write_rows(png_ptr, &row_ptr, 1); /* errors are caught by test sor setjmp(...) */ + + if (*cancel_save) + { + break; + } + } + + free(data); + png_write_end(png_ptr, png_info_ptr); + png_destroy_write_struct(&png_ptr, (png_infopp) 0); + + return (*cancel_save); +} +#endif +#endif + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +#ifdef HAVE_LIBPNG +#ifdef HAVE_LIBZ +int xsane_save_png_16(FILE *outfile, FILE *imagefile, Image_info *image_info, int compression, GtkProgressBar *progress_bar, int *cancel_save) +{ + png_structp png_ptr; + png_infop png_info_ptr; + png_bytep row_ptr; + png_color_8 sig_bit; /* should be 16, but then I get a warning about wrong type */ + unsigned char *data; + char buf[256]; + int colortype, components; + int x,y; + guint16 val; + + DBG(DBG_proc, "xsane_save_png16\n"); + + *cancel_save = 0; + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); + if (!png_ptr) + { + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_LIBPNG); + xsane_back_gtk_error(buf, TRUE); + return -1; /* error */ + } + + png_info_ptr = png_create_info_struct(png_ptr); + if (!png_info_ptr) + { + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_LIBPNG); + xsane_back_gtk_error(buf, TRUE); + return -1; /* error */ + } + + if (setjmp(png_ptr->jmpbuf)) + { + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_LIBPNG); + xsane_back_gtk_error(buf, TRUE); + png_destroy_write_struct(&png_ptr, (png_infopp) 0); + return -1; /* error */ + } + + if (image_info->colors == 4) /* RGBA */ + { + components = 4; + colortype = PNG_COLOR_TYPE_RGB_ALPHA; + } + else if (image_info->colors == 3) /* RGB */ + { + components = 3; + colortype = PNG_COLOR_TYPE_RGB; + } + else /* gray or black/white */ + { + components = 1; + colortype = PNG_COLOR_TYPE_GRAY; + } + + png_init_io(png_ptr, outfile); + png_set_compression_level(png_ptr, compression); + png_set_IHDR(png_ptr, png_info_ptr, image_info->image_width, image_info->image_height, 16, + colortype, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + sig_bit.red = image_info->depth; + sig_bit.green = image_info->depth; + sig_bit.blue = image_info->depth; + sig_bit.alpha = image_info->depth; + sig_bit.gray = image_info->depth; + + png_set_sBIT(png_ptr, png_info_ptr, &sig_bit); + png_write_info(png_ptr, png_info_ptr); + png_set_shift(png_ptr, &sig_bit); + png_set_packing(png_ptr); + + data = malloc(image_info->image_width * components * 2); + + if (!data) + { + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM); + xsane_back_gtk_error(buf, TRUE); + png_destroy_write_struct(&png_ptr, (png_infopp) 0); + return -1; /* error */ + } + + for (y = 0; y < image_info->image_height; y++) + { + gtk_progress_bar_update(progress_bar, (float) y / image_info->image_height); + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + for (x = 0; x < image_info->image_width * components; x++) /* this must be changed in dependance of endianess */ + { + fread(&val, 2, 1, imagefile); /* get data in machine order */ + data[x*2+0] = val / 256; /* write data in network order (MSB first) */ + data[x*2+1] = val & 255; + } + + row_ptr = data; + png_write_rows(png_ptr, &row_ptr, 1); + if (*cancel_save) + { + break; + } + } + + free(data); + png_write_end(png_ptr, png_info_ptr); + png_destroy_write_struct(&png_ptr, (png_infopp) 0); + + return (*cancel_save); +} +#endif +#endif + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static int xsane_save_pnm_16_ascii_gray(FILE *outfile, FILE *imagefile, Image_info *image_info, GtkProgressBar *progress_bar, int *cancel_save) +{ + int x,y; + guint16 val; + int count = 0; + + DBG(DBG_proc, "xsane_save_pnm_16_ascii_gray\n"); + + *cancel_save = 0; + + for (y = 0; y < image_info->image_height; y++) + { + for (x = 0; x < image_info->image_width; x++) + { + fread(&val, 2, 1, imagefile); /* get data in machine order */ + fprintf(outfile, "%d ", val); + + if (++count >= 10) + { + fprintf(outfile, "\n"); + count = 0; + } + } + fprintf(outfile, "\n"); + + if (ferror(outfile)) + { + char buf[255]; + + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno)); + DBG(DBG_error, "%s\n", buf); + xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */); + *cancel_save = 1; + break; + } + + count = 0; + + gtk_progress_bar_update(progress_bar, (float) y / image_info->image_height); + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + if (*cancel_save) + { + break; + } + } + + return (*cancel_save); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static int xsane_save_pnm_16_ascii_color(FILE *outfile, FILE *imagefile, Image_info *image_info, GtkProgressBar *progress_bar, int *cancel_save) +{ + int x,y; + guint16 val; + int count = 0; + + DBG(DBG_proc, "xsane_save_pnm_16_ascii_color\n"); + + *cancel_save = 0; + + for (y = 0; y < image_info->image_height; y++) + { + for (x = 0; x < image_info->image_width; x++) + { + fread(&val, 2, 1, imagefile); /* get data in machine order */ + fprintf(outfile, "%d ", val); + + fread(&val, 2, 1, imagefile); + fprintf(outfile, "%d ", val); + + fread(&val, 2, 1, imagefile); + fprintf(outfile, "%d ", val); + + if (++count >= 3) + { + fprintf(outfile, "\n"); + count = 0; + } + } + fprintf(outfile, "\n"); + + if (ferror(outfile)) + { + char buf[255]; + + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno)); + DBG(DBG_error, "%s\n", buf); + xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */); + *cancel_save = 1; + break; + } + + count = 0; + + gtk_progress_bar_update(progress_bar, (float) y / image_info->image_height); + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + if (*cancel_save) + { + break; + } + } + + return (*cancel_save); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static int xsane_save_pnm_16_binary_gray(FILE *outfile, FILE *imagefile, Image_info *image_info, GtkProgressBar *progress_bar, int *cancel_save) +{ + int x,y; + guint16 val; + + DBG(DBG_proc, "xsane_save_pnm_16_binary_gray\n"); + + *cancel_save = 0; + + for (y = 0; y < image_info->image_height; y++) + { + for (x = 0; x < image_info->image_width; x++) + { + fread(&val, 2, 1, imagefile); /* get data in machine order */ + fputc(val / 256, outfile); /* MSB fist */ + fputc(val & 255, outfile); /* LSB */ + } + + gtk_progress_bar_update(progress_bar, (float) y / image_info->image_height); + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + if (ferror(outfile)) + { + char buf[255]; + + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno)); + DBG(DBG_error, "%s\n", buf); + xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */); + *cancel_save = 1; + break; + } + + if (*cancel_save) + { + break; + } + } + + return (*cancel_save); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static int xsane_save_pnm_16_binary_color(FILE *outfile, FILE *imagefile, Image_info *image_info, GtkProgressBar *progress_bar, int *cancel_save) +{ + int x,y; + guint16 val; + + DBG(DBG_proc, "xsane_save_pnm_16_binary_color\n"); + + *cancel_save = 0; + + for (y = 0; y < image_info->image_height; y++) + { + for (x = 0; x < image_info->image_width; x++) + { + /* red */ + fread(&val, 2, 1, imagefile); /* get data in machine order */ + fputc(val / 256, outfile); /* MSB fist */ + fputc(val & 255, outfile); /* LSB */ + + /* green */ + fread(&val, 2, 1, imagefile); /* get data in machine order */ + fputc(val / 256, outfile); /* MSB fist */ + fputc(val & 255, outfile); /* LSB */ + + /* blue */ + fread(&val, 2, 1, imagefile); /* get data in machine order */ + fputc(val / 256, outfile); /* MSB fist */ + fputc(val & 255, outfile); /* LSB */ + } + + gtk_progress_bar_update(progress_bar, (float) y / image_info->image_height); + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + + if (ferror(outfile)) + { + char buf[255]; + + snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno)); + DBG(DBG_error, "%s\n", buf); + xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */); + *cancel_save = 1; + break; + } + + if (*cancel_save) + { + break; + } + } + + return (*cancel_save); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +int xsane_save_pnm_16(FILE *outfile, FILE *imagefile, Image_info *image_info, GtkProgressBar *progress_bar, int *cancel_save) +{ + DBG(DBG_proc, "xsane_save_pnm_16\n"); + + *cancel_save = 0; + + xsane_write_pnm_header(outfile, image_info, preferences.save_pnm16_as_ascii); + + if (image_info->colors > 1) + { + if (preferences.save_pnm16_as_ascii) + { + xsane_save_pnm_16_ascii_color(outfile, imagefile, image_info, progress_bar, cancel_save); + } + else + { + xsane_save_pnm_16_binary_color(outfile, imagefile, image_info, progress_bar, cancel_save); + } + } + else + { + if (preferences.save_pnm16_as_ascii) + { + xsane_save_pnm_16_ascii_gray(outfile, imagefile, image_info, progress_bar, cancel_save); + } + else + { + xsane_save_pnm_16_binary_gray(outfile, imagefile, image_info, progress_bar, cancel_save); + } + } + + return (*cancel_save); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------------------------------------------------------- */ + +/* 0=ok, <0=error, 1=canceled */ +int xsane_save_image_as_lineart(char *output_filename, char *input_filename, GtkProgressBar *progress_bar, int *cancel_save) +{ + FILE *outfile; + FILE *infile; + char buf[256]; + Image_info image_info; + + *cancel_save = 0; + + outfile = fopen(output_filename, "wb"); /* b = binary mode for win32 */ + + if (outfile == 0) + { + snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, output_filename, strerror(errno)); + xsane_back_gtk_error(buf, TRUE); + return -2; + } + + infile = fopen(input_filename, "rb"); /* read binary (b for win32) */ + if (infile == 0) + { + char buf[256]; + snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, input_filename, strerror(errno)); + xsane_back_gtk_error(buf, TRUE); + + fclose(outfile); + remove(output_filename); /* remove already created output file */ + return -1; + } + + xsane_read_pnm_header(infile, &image_info); + + xsane_save_grayscale_image_as_lineart(outfile, infile, &image_info, progress_bar, cancel_save); + + fclose(infile); + fclose(outfile); + + if (*cancel_save) /* remove output file if saving has been canceled */ + { + remove(output_filename); + } + + return (*cancel_save); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +int xsane_save_image_as_text(char *output_filename, char *input_filename, GtkProgressBar *progress_bar, int *cancel_save) +{ + char *arg[1000]; + char buf[256]; + int argnr; + pid_t pid; + int i; + int pipefd[2]; /* for progress communication with gocr */ + FILE *ocr_progress = NULL; + + DBG(DBG_proc, "xsane_save_image_as_text\n"); + + argnr = xsane_parse_options(preferences.ocr_command, arg); + + arg[argnr++] = strdup(preferences.ocr_inputfile_option); + arg[argnr++] = strdup(input_filename); + + arg[argnr++] = strdup(preferences.ocr_outputfile_option); + arg[argnr++] = strdup(output_filename); + + if (preferences.ocr_use_gui_pipe) + { + if (!pipe(pipefd)) /* success */ + { + DBG(DBG_info, "xsane_save_image_as_text: created pipe for progress communication\n"); + + arg[argnr++] = strdup(preferences.ocr_gui_outfd_option); + + snprintf(buf, sizeof(buf),"%d", pipefd[1]); + arg[argnr++] = strdup(buf); + } + else + { + DBG(DBG_info, "xsane_save_image_as_text: could not create pipe for progress communication\n"); + pipefd[0] = 0; + pipefd[1] = 0; + } + } + else + { + DBG(DBG_info, "xsane_save_image_as_text: no pipe for progress communication requested\n"); + pipefd[0] = 0; + pipefd[1] = 0; + } + + arg[argnr] = 0; + +#ifndef HAVE_OS2_H + pid = fork(); + + if (pid == 0) /* new process */ + { + FILE *ipc_file = NULL; + + if (xsane.ipc_pipefd[0]) /* did we create the progress pipe? */ + { + close(xsane.ipc_pipefd[0]); /* close reading end of pipe */ + ipc_file = fdopen(xsane.ipc_pipefd[1], "w"); + } + + if (pipefd[0]) /* did we create the progress pipe? */ + { + close(pipefd[0]); /* close reading end of pipe */ + } + + DBG(DBG_info, "trying to change user id for new subprocess:\n"); + DBG(DBG_info, "old effective uid = %d\n", (int) geteuid()); + setuid(getuid()); + DBG(DBG_info, "new effective uid = %d\n", (int) geteuid()); + + + execvp(arg[0], arg); /* does not return if successfully */ + DBG(DBG_error, "%s %s\n", ERR_FAILED_EXEC_OCR_CMD, preferences.ocr_command); + + /* send error message via IPC pipe to parent process */ + if (ipc_file) + { + fprintf(ipc_file, "%s %s:\n%s", ERR_FAILED_EXEC_OCR_CMD, preferences.ocr_command, strerror(errno)); + fflush(ipc_file); /* make sure message is displayed */ + fclose(ipc_file); + } + + _exit(0); /* do not use exit() here! otherwise gtk gets in trouble */ + } + +#else + pid = spawnvp(P_NOWAIT, arg[0], arg); + if (pid == -1) + { + DBG(DBG_error, "%s %s\n", ERR_FAILED_EXEC_OCR_CMD, preferences.ocr_command); + } +#endif + + if (pipefd[1]) + { + close(pipefd[1]); /* close writing end of pipe */ + ocr_progress = fdopen(pipefd[0], "r"); /* open reading end of pipe as file */ + } + + for (i=0; i<argnr; i++) + { + free(arg[i]); + } + + if (ocr_progress) /* pipe available */ + { + gtk_progress_bar_update(GTK_PROGRESS_BAR(progress_bar), 0.0); + + while (!feof(ocr_progress)) + { + int progress, subprogress; + float fprogress; + + fgets(buf, sizeof(buf), ocr_progress); + + if (!strncmp(preferences.ocr_progress_keyword, buf, strlen(preferences.ocr_progress_keyword))) + { + sscanf(buf + strlen(preferences.ocr_progress_keyword), "%d %d", &progress, &subprogress); + + snprintf(buf, sizeof(buf), "%s (%d:%d)", PROGRESS_OCR, progress, subprogress); + gtk_progress_set_format_string(GTK_PROGRESS(progress_bar), buf); + + fprogress = progress / 100.0; + + if (fprogress < 0.0) + { + fprogress = 0.0; + } + + if (fprogress > 11.0) + { + fprogress = 1.0; + } + + gtk_progress_bar_update(GTK_PROGRESS_BAR(progress_bar), fprogress); + } + + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + } + + gtk_progress_set_format_string(GTK_PROGRESS(progress_bar), ""); + gtk_progress_bar_update(GTK_PROGRESS_BAR(progress_bar), 0.0); + } + else /* no pipe available */ + { + while (pid) + { + int status = 0; + pid_t pid_status = waitpid(pid, &status, WNOHANG); + + if (pid == pid_status) + { + pid = 0; /* ok, child process has terminated */ + } + + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + } + } + + if (pipefd[0]) + { + fclose(ocr_progress); /* close reading end of pipe */ + } + + return (*cancel_save); +} +/* ---------------------------------------------------------------------------------------------------------------------- */ + +/* save image in destination file format. lineart images that are stored as grayscale image are reduced to lineart! */ +int xsane_save_image_as(char *output_filename, char *input_filename, int output_format, GtkProgressBar *progress_bar, int *cancel_save) +{ + FILE *outfile; + FILE *infile; + char buf[256]; + Image_info image_info; + char lineart_filename[PATH_MAX]; + int remove_input_file = FALSE; + + DBG(DBG_proc, "xsane_save_image_as(output_file=%s, input_file=%s, type=%d)\n", output_filename, input_filename, output_format); + + *cancel_save = 0; + + infile = fopen(input_filename, "rb"); /* read binary (b for win32) */ + if (infile == 0) + { + char buf[256]; + snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, input_filename, strerror(errno)); + xsane_back_gtk_error(buf, TRUE); + + return -1; + } + + xsane_read_pnm_header(infile, &image_info); + + if ((image_info.reduce_to_lineart) && (output_format != XSANE_PNM)) + { + DBG(DBG_info, "original image is a lineart => reduce to lineart\n"); + fclose(infile); + xsane_back_gtk_make_path(sizeof(lineart_filename), lineart_filename, 0, 0, "xsane-conversion-", xsane.dev_name, ".pbm", XSANE_PATH_TMP); + + snprintf(buf, sizeof(buf), "%s: %s", PROGRESS_PACKING_DATA, output_filename); + + gtk_progress_set_format_string(GTK_PROGRESS(progress_bar), buf); + gtk_progress_bar_update(GTK_PROGRESS_BAR(progress_bar), 0.0); + + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + xsane_save_image_as_lineart(lineart_filename, input_filename, progress_bar, cancel_save); + + input_filename = lineart_filename; + remove_input_file = TRUE; + + infile = fopen(input_filename, "rb"); /* read binary (b for win32) */ + if (infile == 0) + { + char buf[256]; + snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, input_filename, strerror(errno)); + xsane_back_gtk_error(buf, TRUE); + + return -1; + } + + xsane_read_pnm_header(infile, &image_info); + } + + snprintf(buf, sizeof(buf), "%s: %s", PROGRESS_SAVING_DATA, output_filename); + + gtk_progress_set_format_string(GTK_PROGRESS(progress_bar), buf); + gtk_progress_bar_update(GTK_PROGRESS_BAR(progress_bar), 0.0); + + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + +#ifdef HAVE_LIBTIFF + if (output_format == XSANE_TIFF) /* routines that want to have filename for saving */ + { + TIFF *tiffile; + + if (xsane_create_secure_file(output_filename)) /* remove possibly existing symbolic links for security */ + { + snprintf(buf, sizeof(buf), "%s %s %s\n", ERR_DURING_SAVE, ERR_CREATE_SECURE_FILE, output_filename); + xsane_back_gtk_error(buf, TRUE); + return -1; /* error */ + } + + tiffile = TIFFOpen(output_filename, "w"); + if (!tiffile) + { + snprintf(buf, sizeof(buf), "%s %s %s\n", ERR_DURING_SAVE, ERR_OPEN_FAILED, output_filename); + xsane_back_gtk_error(buf, TRUE); + return -1; /* error */ + } + + xsane_save_tiff_page(tiffile, 0, 0, infile, &image_info, preferences.jpeg_quality, progress_bar, cancel_save); + + TIFFClose(tiffile); + } + else /* routines that want to have filedescriptor for saving */ +#endif /* HAVE_LIBTIFF */ + { + if (xsane_create_secure_file(output_filename)) /* remove possibly existing symbolic links for security */ + { + snprintf(buf, sizeof(buf), "%s %s %s\n", ERR_DURING_SAVE, ERR_CREATE_SECURE_FILE, output_filename); + xsane_back_gtk_error(buf, TRUE); + return -1; /* error */ + } + + outfile = fopen(output_filename, "wb"); /* b = binary mode for win32 */ + + if (outfile != 0) + { + switch(output_format) + { + case XSANE_PNM: + if (image_info.reduce_to_lineart) + { + xsane_save_grayscale_image_as_lineart(outfile, infile, &image_info, progress_bar, cancel_save); + } + else + { + xsane_copy_file(outfile, infile, progress_bar, cancel_save); + } + break; + +#ifdef HAVE_LIBJPEG + case XSANE_JPEG: + xsane_save_jpeg(outfile, infile, &image_info, preferences.jpeg_quality, progress_bar, cancel_save); + break; /* switch format == XSANE_JPEG */ +#endif + +#ifdef HAVE_LIBPNG +#ifdef HAVE_LIBZ + case XSANE_PNG: + if (image_info.depth <= 8) + { + xsane_save_png(outfile, infile, &image_info, preferences.png_compression, progress_bar, cancel_save); + } + else + { + xsane_save_png_16(outfile, infile, &image_info, preferences.png_compression, progress_bar, cancel_save); + } + break; /* switch format == XSANE_PNG */ +#endif +#endif + + case XSANE_PNM16: + xsane_save_pnm_16(outfile, infile, &image_info, progress_bar, cancel_save); + break; /* switch fomat == XSANE_PNM16 */ + + case XSANE_PS: /* save postscript, use original size */ + { + float imagewidth, imageheight; + + imagewidth = 72.0 * image_info.image_width/image_info.resolution_x; /* width in 1/72 inch */ + imageheight = 72.0 * image_info.image_height/image_info.resolution_y; /* height in 1/72 inch */ + + xsane_save_ps(outfile, infile, + &image_info, + imagewidth, imageheight, + 0, /* paper_left_margin */ + 0, /* paper_bottom_margin */ + (int) imagewidth, /* paper_width */ + (int) imageheight, /* paper_height */ + 0 /* portrait top left */, + preferences.save_ps_flatdecoded, + progress_bar, + cancel_save); + } + break; /* switch format == XSANE_PS */ + + case XSANE_PDF: /* save PDF, use original size */ + { + float imagewidth, imageheight; + + imagewidth = 72.0 * image_info.image_width/image_info.resolution_x; /* width in 1/72 inch */ + imageheight = 72.0 * image_info.image_height/image_info.resolution_y; /* height in 1/72 inch */ + + xsane_save_pdf(outfile, infile, + &image_info, + imagewidth, imageheight, + 0, /* paper_left_margin */ + 0, /* paper_bottom_margin */ + (int) imagewidth, /* paper_width */ + (int) imageheight, /* paper_height */ + 0 /* portrait top left */, + preferences.save_pdf_flatdecoded, + progress_bar, + cancel_save); + } + break; /* switch format == XSANE_PDF */ + + case XSANE_TEXT: /* save as text using ocr program like gocr/jocr */ + { + xsane_save_image_as_text(output_filename, input_filename, progress_bar, cancel_save); + } + break; /* switch format == XSANE_TEXT */ + + default: + snprintf(buf, sizeof(buf),"%s", ERR_UNKNOWN_SAVING_FORMAT); + xsane_back_gtk_error(buf, TRUE); + + fclose(outfile); + fclose(infile); + + remove(output_filename); /* no usable output: remove output file */ + + if (remove_input_file) + { + remove(input_filename); /* remove lineart pbm file */ + } + + gtk_progress_set_format_string(GTK_PROGRESS(progress_bar), ""); + gtk_progress_bar_update(GTK_PROGRESS_BAR(progress_bar), 0.0); + + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + return -2; + break; /* switch format == default */ + } + fclose(outfile); + } + else + { + snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, output_filename, strerror(errno)); + xsane_back_gtk_error(buf, TRUE); + + fclose(infile); + + if (remove_input_file) + { + remove(input_filename); /* remove lineart pbm file */ + } + + gtk_progress_set_format_string(GTK_PROGRESS(progress_bar), ""); + gtk_progress_bar_update(GTK_PROGRESS_BAR(progress_bar), 0.0); + + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + return -2; + } + } + + fclose (infile); + + if (remove_input_file) + { + remove(input_filename); /* remove lineart pbm file */ + } + + if (*cancel_save) /* remove output file if saving has been canceled */ + { + remove(output_filename); + } + + gtk_progress_set_format_string(GTK_PROGRESS(progress_bar), ""); + gtk_progress_bar_update(GTK_PROGRESS_BAR(progress_bar), 0.0); + + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + return (*cancel_save); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------------------------------------------------------- */ + +#ifdef HAVE_ANY_GIMP +static int xsane_decode_devname(const char *encoded_devname, int n, char *buf) +{ + char *dst, *limit; + const char *src; + char ch, val; + + DBG(DBG_proc, "xsane_decode_devname\n"); + + 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; + + DBG(DBG_proc, "xsane_encode_devname\n"); + + 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 GimpParamDef args[] = + { + {GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive"}, + }; + static GimpParamDef *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; + + DBG(DBG_proc, "xsane_gimp_query\n"); + + snprintf(name, sizeof(name), "%s", xsane.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", */ + GIMP_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) + { + DBG(DBG_error0, "\n\n" + "%s %s:\n" + " %s\n" + " %s %d\n" + " %s %d\n" + "%s\n\n", + xsane.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(&xsane.devlist, SANE_FALSE); + + for (i = 0; xsane.devlist[i]; ++i) + { + snprintf(name, sizeof(name), "%s-", xsane.prog_name); + if (xsane_encode_devname(xsane.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; xsane.devlist[i]->name[j]; ++j) + { + if (xsane.devlist[i]->name[j] == '/') + { + mpath[len++] = '\''; + } + else + { + mpath[len++] = xsane.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, + 0, /* "RGB, GRAY", */ + GIMP_EXTENSION, + nargs, nreturn_vals, + args, return_vals); + } + + sane_exit(); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +#ifdef HAVE_GIMP_2 +static void xsane_gimp_run(const gchar *name, gint nparams, const GimpParam *param, gint *nreturn_vals, GimpParam **return_vals) +{ + GimpRunMode run_mode; +#else /* GIMP-1.x */ +static void xsane_gimp_run(char *name, int nparams, GimpParam *param, int *nreturn_vals, GimpParam **return_vals) +{ + GimpRunModeType run_mode; +#endif + + static GimpParam values[2]; + char devname[1024]; + char *args[2]; + int nargs; + + DBG(DBG_proc, "xsane_gimp_run\n"); + + run_mode = param[0].data.d_int32; + xsane.mode = XSANE_GIMP_EXTENSION; + xsane.xsane_mode = XSANE_SAVE; + + *nreturn_vals = 1; + *return_vals = values; + + values[0].type = GIMP_PDB_STATUS; + values[0].data.d_status = GIMP_PDB_CALLING_ERROR; + + nargs = 0; + args[nargs++] = "xsane"; + + xsane.selected_dev = -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 GIMP_RUN_INTERACTIVE: +#ifdef HAVE_GIMP_2 + gimp_extension_ack(); +#endif + xsane_interface(nargs, args); + values[0].data.d_status = GIMP_PDB_SUCCESS; + break; + + case GIMP_RUN_NONINTERACTIVE: + /* Make sure all the arguments are there! */ + break; + + case GIMP_RUN_WITH_LAST_VALS: + /* Possibly retrieve data */ + break; + + default: + break; + } +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void null_print_func(gchar *msg) +{ +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +int xsane_transfer_to_gimp(char *input_filename, GtkProgressBar *progress_bar, int *cancel_save) +{ + int remaining; + size_t tile_size; + GimpImageType image_type = GIMP_GRAY; + GimpImageType drawable_type = GIMP_GRAY_IMAGE; + gint32 layer_ID; + gint32 image_ID; + GimpDrawable *drawable; + guchar *tile; + GimpPixelRgn region; + unsigned tile_offset; + int i, x, y; + Image_info image_info; + FILE *imagefile; + + DBG(DBG_info, "xsane_transer_to_gimp\n"); + + *cancel_save = 0; + + imagefile = fopen(input_filename, "rb"); /* read binary (b for win32) */ + if (imagefile == 0) + { + char buf[256]; + snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, input_filename, strerror(errno)); + xsane_back_gtk_error(buf, TRUE); + + return -1; + } + + xsane_read_pnm_header(imagefile, &image_info); + + x = 0; + y = 0; + tile_offset = 0; + tile_size = image_info.image_width * gimp_tile_height(); + + if (image_info.colors == 3) /* RGB */ + { + tile_size *= 3; /* 24 bits/pixel RGB */ + image_type = GIMP_RGB; + drawable_type = GIMP_RGB_IMAGE; + } + else if (image_info.colors == 4) /* RGBA */ + { + tile_size *= 4; /* 32 bits/pixel RGBA */ + image_type = GIMP_RGB; + drawable_type = GIMP_RGBA_IMAGE; /* interpret infrared as alpha */ + } + /* colors == 0/1 is predefined */ + + image_ID = gimp_image_new(image_info.image_width, image_info.image_height, image_type); + +/* the following is supported since gimp-1.1.? */ +#ifdef GIMP_HAVE_RESOLUTION_INFO + if (image_info.resolution_x > 0) + { + gimp_image_set_resolution(image_ID, image_info.resolution_x, image_info.resolution_y); + } +/* gimp_image_set_unit(image_ID, unit?); */ +#endif + + layer_ID = gimp_layer_new(image_ID, "Background", image_info.image_width, image_info.image_height, drawable_type, 100.0, GIMP_NORMAL_MODE); + gimp_image_add_layer(image_ID, layer_ID, 0); + drawable = gimp_drawable_get(layer_ID); + gimp_pixel_rgn_init(®ion, drawable, 0, 0, drawable->width, drawable->height, TRUE, FALSE); + tile = g_new(guchar, tile_size); + + + if (image_info.colors == 1) /* gray */ + { + switch (image_info.depth) + { + case 1: /* 1 bit gray => conversion to 8 bit gray */ + for (i = 0; i < ( (image_info.image_width + 7) / 8) * image_info.image_height; ++i) + { + u_char mask; + int j; + + mask = fgetc(imagefile); + for (j = 7; j >= 0; --j) + { + u_char gl = (mask & (1 << j)) ? 0x00 : 0xff; + tile[tile_offset++] = gl; + + x++; + + if (x >= image_info.image_width) + { + int tile_height = gimp_tile_height(); + + x = 0; + y++; + + if (y % tile_height == 0) + { + gimp_pixel_rgn_set_rect(®ion, tile, 0, y - tile_height, image_info.image_width, tile_height); + tile_offset = 0; + } + + gtk_progress_bar_update(progress_bar, (float) y / image_info.image_height); /* update progress bar */ + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + + break; /* leave for j loop */ + } + } + + if (*cancel_save) + { + break; + } + } + break; /* leave switch depth 1 */ + + case 8: /* 8 bit gray */ + case 16: /* 16 bit gray already has been reduced to 8 bit */ + for (i = 0; i < image_info.image_width * image_info.image_height; ++i) + { + tile[tile_offset++] = fgetc(imagefile); + x++; + + if (x >= image_info.image_width) + { + int tile_height = gimp_tile_height(); + + x = 0; + y++; + + if (y % tile_height == 0) + { + gimp_pixel_rgn_set_rect(®ion, tile, 0, y - tile_height, image_info.image_width, tile_height); + tile_offset = 0; + } + + gtk_progress_bar_update(progress_bar, (float) y / image_info.image_height); /* update progress bar */ + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + } + + if (*cancel_save) + { + break; + } + } + break; /* leave switch depth */ + + default: /* bad depth */ + break; /* default */ + } + } + else if (image_info.colors == 3) /* RGB */ + { + switch (image_info.depth) + { + case 8: /* 8 bit RGB */ + case 16: /* 16 bit RGB already has been reduced to 8 bit */ + for (i = 0; i < image_info.image_width * image_info.image_height*3; ++i) + { + tile[tile_offset++] = fgetc(imagefile); + if (tile_offset % 3 == 0) + { + x++; + + if (x >= image_info.image_width) + { + int tile_height = gimp_tile_height(); + + x = 0; + y++; + + if (y % tile_height == 0) + { + gimp_pixel_rgn_set_rect(®ion, tile, 0, y - tile_height, image_info.image_width, tile_height); + tile_offset = 0; + } + + gtk_progress_bar_update(progress_bar, (float) y / image_info.image_height); /* update progress bar */ + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + } + } + + if (*cancel_save) + { + break; + } + } + break; /* case 8 */ + + default: /* bad depth */ + break; /* default */ + } + } +#ifdef SUPPORT_RGBA + else if (image_info.colors == 4) /* RGBA */ + { + int i; + + switch (image_info.depth) + { + case 8: /* 8 bit RGBA */ + case 16: /* 16 bit RGBA already has been reduced to 8 bit */ + for (i = 0; i < image_info.image_width * image_info.image_height * 4; ++i) + { + tile[tile_offset++] = fgetc(imagefile); + if (tile_offset % 4 == 0) + { + x++; + + if (x >= image_info.image_width) + { + int tile_height = gimp_tile_height(); + + x = 0; + y++; + + if (y % tile_height == 0) + { + gimp_pixel_rgn_set_rect(®ion, tile, 0, y - tile_height, image_info.image_width, tile_height); + tile_offset = 0; + } + + gtk_progress_bar_update(progress_bar, (float) y / image_info.image_height); /* update progress bar */ + while (gtk_events_pending()) + { + gtk_main_iteration(); + } + } + } + + if (*cancel_save) + { + break; + } + } + break; + + default: /* bad depth */ + break; + } + } +#endif + +/* scan_done part */ + if (y > image_info.image_height) + { + y = image_info.image_height; + } + + remaining = y % gimp_tile_height(); + + if (remaining) + { + gimp_pixel_rgn_set_rect(®ion, tile, 0, y - remaining, image_info.image_width, remaining); + } + + gimp_drawable_flush(drawable); + gimp_display_new(image_ID); + gimp_drawable_detach(drawable); + g_free(tile); + tile = 0; + + fclose(imagefile); + + return 0; +} +#endif /* HAVE_ANY_GIMP */ + +/* ---------------------------------------------------------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------------------------------------------------------- */ + +#ifdef XSANE_ACTIVATE_MAIL + +/* character base of base64 coding */ +static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static void write_3chars_as_base64(unsigned char c1, unsigned char c2, unsigned char c3, int pads, int fd_socket) +{ + char buf[4]; + + buf[0] = base64[c1>>2]; /* wirte bits 7-2 of first char */ + buf[1] = base64[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)]; /* write bits 1,0 of first and bits 7-4 of second char */ + + if (pads == 2) /* only one byte used */ + { + buf[2] = '='; /* char not used */ + buf[3] = '='; /* char not used */ + } + else if (pads) /* only two bytes used */ + { + buf[2] = base64[((c2 & 0xF) << 2)]; /* write bits 3-0 of second char */ + buf[3] = '='; /* char not used */ + } + else + { + buf[2] = base64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)]; /* write bits 3-0 of second and bits 7,6 of third char */ + buf[3] = base64[c3 & 0x3F]; /* write bits 5-0 of third char as lsb */ + } + + write(fd_socket, buf, 4); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void write_base64(int fd_socket, FILE *infile) +{ + int c1, c2, c3; + int pos = 0; + + while ((c1 = getc(infile)) != EOF) + { + c2 = getc(infile); + if (c2 == EOF) + { + write_3chars_as_base64(c1, 0, 0, 2, fd_socket); + } + else + { + c3 = getc(infile); + if (c3 == EOF) + { + write_3chars_as_base64(c1, c2, 0, 1, fd_socket); + } + else + { + write_3chars_as_base64(c1, c2, c3, 0, fd_socket); + } + } + + pos += 4; + if (pos > 71) + { + write(fd_socket, "\n", 1); + + pos = 0; + } + + xsane.mail_progress_bytes += 3; + if ((int) ((xsane.mail_progress_bytes * 100) / xsane.mail_progress_size) != (int) (xsane.mail_progress_val * 100)) + { + xsane.mail_progress_val = (float) xsane.mail_progress_bytes / xsane.mail_progress_size; + xsane_front_gtk_mail_project_update_lockfile_status(); + } + } + + if (pos) + { + write(fd_socket, "\n", 1); + } + + xsane.mail_progress_val = 1.0; + xsane_front_gtk_mail_project_update_lockfile_status(); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void write_mail_header(int fd_socket, char *from, char *reply_to, char *to, char *subject, char *boundary, int related) +{ + char buf[1024]; + + snprintf(buf, sizeof(buf), "From: %s\n", from); + write(fd_socket, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Reply-To: %s\n", reply_to); + write(fd_socket, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "To: %s\n", to); + write(fd_socket, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Subject: %s\n", subject); + write(fd_socket, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "MIME-Version: 1.0\n"); + write(fd_socket, buf, strlen(buf)); + + if (related) /* related means that we need a special link in the html part to display the image */ + { + snprintf(buf, sizeof(buf), "Content-Type: multipart/related;\n"); + write(fd_socket, buf, strlen(buf)); + } + else + { + snprintf(buf, sizeof(buf), "Content-Type: multipart/mixed;\n"); + write(fd_socket, buf, strlen(buf)); + } + + snprintf(buf, sizeof(buf), " boundary=\"%s\"\n\n", boundary); + write(fd_socket, buf, strlen(buf)); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void write_mail_footer(int fd_socket, char *boundary) +{ + char buf[1024]; + + snprintf(buf, sizeof(buf), "--%s--\n", boundary); + write(fd_socket, buf, strlen(buf)); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void write_mail_mime_ascii(int fd_socket, char *boundary) +{ + char buf[1024]; + + snprintf(buf, sizeof(buf), "--%s\n", boundary); + write(fd_socket, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Content-Type: text/plain;\n"); + write(fd_socket, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), " charset=\"iso-8859-1\"\n"); + write(fd_socket, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Content-Transfer-Encoding: 8bit\n\n"); + write(fd_socket, buf, strlen(buf)); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void write_mail_mime_html(int fd_socket, char *boundary) +{ + char buf[1024]; + + snprintf(buf, sizeof(buf), "--%s\n", boundary); + write(fd_socket, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Content-Type: text/html;\n"); + write(fd_socket, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), " charset=\"us-ascii\"\n"); + write(fd_socket, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Content-Transfer-Encoding: 7bit\n\n"); + write(fd_socket, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "<!doctype html public \"-//w3c//dtd html 4.0 transitional//en\">\n"); + write(fd_socket, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "<html>\n"); + write(fd_socket, buf, strlen(buf)); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void write_mail_attach_image(int fd_socket, char *boundary, char *content_id, char *content_type, FILE *infile, char *filename) +{ + char buf[1024]; + + snprintf(buf, sizeof(buf), "--%s\n", boundary); + write(fd_socket, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Content-Type: %s\n", content_type); + write(fd_socket, buf, strlen(buf)); + + if (content_id) + { + snprintf(buf, sizeof(buf), "Content-ID: <%s>\n", content_id); + write(fd_socket, buf, strlen(buf)); + } + + snprintf(buf, sizeof(buf), "Content-Transfer-Encoding: base64\n"); + write(fd_socket, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Content-Disposition: inline;\n"); + write(fd_socket, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), " filename=\"%s\"\n", filename); + write(fd_socket, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "\n"); + write(fd_socket, buf, strlen(buf)); + + write_base64(fd_socket, infile); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void write_mail_attach_file(int fd_socket, char *boundary, FILE *infile, char *filename) +{ + char buf[1024]; + + snprintf(buf, sizeof(buf), "--%s\n", boundary); + write(fd_socket, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Content-Type: application/octet-stream\n"); + write(fd_socket, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), " name=\"%s\"\n", filename); + write(fd_socket, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Content-Transfer-Encoding: base64\n"); + write(fd_socket, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Content-Disposition: attachment;\n"); + write(fd_socket, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), " filename=\"%s\"\n", filename); + write(fd_socket, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "\n"); + write(fd_socket, buf, strlen(buf)); + + write_base64(fd_socket, infile); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +/* returns fd_socket if sucessfull, < 0 when error occured */ +int open_socket(char *server, int port) +{ + int fd_socket; + struct sockaddr_in sin; + struct hostent *he; + + he = gethostbyname(server); + if (!he) + { + DBG(DBG_error, "open_socket: Could not get hostname of \"%s\"\n", server); + return -1; + } + else + { + DBG(DBG_info, "open_socket: connecting to \"%s\" = %d.%d.%d.%d\n", + he->h_name, + (unsigned char) he->h_addr_list[0][0], + (unsigned char) he->h_addr_list[0][1], + (unsigned char) he->h_addr_list[0][2], + (unsigned char) he->h_addr_list[0][3]); + } + + if (he->h_addrtype != AF_INET) + { + DBG(DBG_error, "open_socket: Unknown address family: %d\n", he->h_addrtype); + return -1; + } + + fd_socket = socket(AF_INET, SOCK_STREAM, 0); + + if (fd_socket < 0) + { + DBG(DBG_error, "open_socket: Could not create socket: %s\n", strerror(errno)); + return -1; + } + +/* setsockopt (dev->ctl, level, TCP_NODELAY, &on, sizeof (on)); */ + + sin.sin_port = htons(port); + sin.sin_family = AF_INET; + memcpy(&sin.sin_addr, he->h_addr_list[0], he->h_length); + + if (connect(fd_socket, &sin, sizeof(sin))) + { + DBG(DBG_error, "open_socket: Could not connect with port %d of socket: %s\n", ntohs(sin.sin_port), strerror(errno)); + return -1; + } + + DBG(DBG_info, "open_socket: Connected with port %d\n", ntohs(sin.sin_port)); + + return fd_socket; +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +/* returns 0 if success */ +/* not only a write routine, also reads data */ +int pop3_login(int fd_socket, char *user, char *passwd) +{ + char buf[1024]; + int len; + + len = read(fd_socket, buf, sizeof(buf)); + if (len >= 0) + { + buf[len] = 0; + } + DBG(DBG_info2, "< %s\n", buf); + + snprintf(buf, sizeof(buf), "USER %s\r\n", user); + DBG(DBG_info2, "> USER xxx\n"); + write(fd_socket, buf, strlen(buf)); + len = read(fd_socket, buf, sizeof(buf)); + if (len >= 0) + { + buf[len] = 0; + } + DBG(DBG_info2, "< %s\n", buf); + if (buf[0] != '+') + { + return -1; + } + + snprintf(buf, sizeof(buf), "PASS %s\r\n", passwd); + DBG(DBG_info2, "> PASS xxx\n"); + write(fd_socket, buf, strlen(buf)); + len = read(fd_socket, buf, sizeof(buf)); + if (len >= 0) + { + buf[len] = 0; + } + DBG(DBG_info2, "< %s\n", buf); + if (buf[0] != '+') + { + return -1; + } + + snprintf(buf, sizeof(buf), "QUIT\r\n"); + DBG(DBG_info2, "> QUIT\n"); + write(fd_socket, buf, strlen(buf)); + len = read(fd_socket, buf, sizeof(buf)); + if (len >= 0) + { + buf[len] = 0; + } + DBG(DBG_info2, "< %s\n", buf); + + return 0; +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +/* not only a write routine, also reads data */ +/* returns -1 on error, 0 when ok */ +int write_smtp_header(int fd_socket, char *from, char *to) +{ + char buf[1024]; + int len; + + len = read(fd_socket, buf, sizeof(buf)); + if (len >= 0) + { + buf[len] = 0; + } + DBG(DBG_info2, "< %s\n", buf); + + snprintf(buf, sizeof(buf), "helo localhost\r\n"); + DBG(DBG_info2, "> %s", buf); + write(fd_socket, buf, strlen(buf)); + len = read(fd_socket, buf, sizeof(buf)); + if (len >= 0) + { + buf[len] = 0; + } + DBG(DBG_info2, "< %s\n", buf); + + if (buf[0] != '2') + { + DBG(DBG_info, "=> error\n"); + + if (xsane.mail_status) + { + free(xsane.mail_status); + } + xsane.mail_status = strdup(TEXT_MAIL_STATUS_SMTP_CONNECTION_FAILED); + xsane_front_gtk_mail_project_update_lockfile_status(); + return -1; + } + + snprintf(buf, sizeof(buf), "MAIL FROM: %s\r\n", from); + DBG(DBG_info2, "> %s", buf); + write(fd_socket, buf, strlen(buf)); + len = read(fd_socket, buf, sizeof(buf)); + if (len >= 0) + { + buf[len] = 0; + } + DBG(DBG_info2, "< %s\n", buf); + + if (buf[0] != '2') + { + DBG(DBG_info, "=> error\n"); + + if (xsane.mail_status) + { + free(xsane.mail_status); + } + xsane.mail_status = strdup(TEXT_MAIL_STATUS_SMTP_ERR_FROM); + xsane_front_gtk_mail_project_update_lockfile_status(); + return -1; + } + + + snprintf(buf, sizeof(buf), "RCPT TO: %s\r\n", to); + DBG(DBG_info2, "> %s", buf); + write(fd_socket, buf, strlen(buf)); + len = read(fd_socket, buf, sizeof(buf)); + if (len >= 0) + { + buf[len] = 0; + } + DBG(DBG_info2, "< %s\n", buf); + + if (buf[0] != '2') + { + DBG(DBG_info, "=> error\n"); + + if (xsane.mail_status) + { + free(xsane.mail_status); + } + xsane.mail_status = strdup(TEXT_MAIL_STATUS_SMTP_ERR_RCPT); + xsane_front_gtk_mail_project_update_lockfile_status(); + return -1; + } + + snprintf(buf, sizeof(buf), "DATA\r\n"); + DBG(DBG_info2, "> %s", buf); + write(fd_socket, buf, strlen(buf)); + len = read(fd_socket, buf, sizeof(buf)); + if (len >= 0) + { + buf[len] = 0; + } + DBG(DBG_info2, "< %s\n", buf); + + if ((buf[0] != '2') && (buf[0] != '3')) + { + DBG(DBG_info, "=> error\n"); + + if (xsane.mail_status) + { + free(xsane.mail_status); + } + xsane.mail_status = strdup(TEXT_MAIL_STATUS_SMTP_ERR_DATA); + xsane_front_gtk_mail_project_update_lockfile_status(); + return -1; + } + + return 0; +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +/* not only a write routine, also reads data */ +int write_smtp_footer(int fd_socket) +{ + char buf[1024]; + int len; + + snprintf(buf, sizeof(buf), "\r\n.\r\n"); + DBG(DBG_info2, "> %s", buf); + write(fd_socket, buf, strlen(buf)); + len = read(fd_socket, buf, sizeof(buf)); + if (len >= 0) + { + buf[len] = 0; + } + DBG(DBG_info2, "< %s\n", buf); + + snprintf(buf, sizeof(buf), "QUIT\r\n"); + DBG(DBG_info2, "> %s", buf); + write(fd_socket, buf, strlen(buf)); + len = read(fd_socket, buf, sizeof(buf)); + if (len >= 0) + { + buf[len] = 0; + } + DBG(DBG_info2, "< %s\n", buf); + + return 0; +} + +#endif +/* ---------------------------------------------------------------------------------------------------------------------- */ |