1 /* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
2 
3    xsane-save.c
4 
5    Oliver Rauch <Oliver.Rauch@rauch-domain.de>
6    Copyright (C) 1998-2010 Oliver Rauch
7    This file is part of the XSANE package.
8 
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
22 
23 /* ---------------------------------------------------------------------------------------------------------------------- */
24 
25 #include "xsane.h"
26 #include "xsane-back-gtk.h"
27 #include "xsane-front-gtk.h"
28 #include "xsane-save.h"
29 #include <time.h>
30 #include <sys/wait.h>
31 
32 /* the following test is always false */
33 #ifdef _native_WIN32
34 # include <winsock.h>
35 #else
36 # include <sys/socket.h>
37 # include <netinet/in.h>
38 # include <netdb.h>
39 #endif
40 
41 #ifdef HAVE_LIBJPEG
42 #include <jpeglib.h>
43 #endif
44 
45 #ifdef HAVE_LIBZ
46 #include <zlib.h>
47 #endif
48 
49 #ifdef HAVE_LIBPNG
50 #include <png.h>
51 #endif
52 
53 #ifdef HAVE_LIBTIFF
54 #include <tiffio.h>
55 #endif
56 
57 #ifdef HAVE_MMAP
58 #include <sys/mman.h>
59 #endif
60 
61 #ifdef HAVE_OS2_H
62 #include <process.h>
63 #endif
64 
65 /* ---------------------------------------------------------------------------------------------------------------------- */
66 
67 #ifdef HAVE_ANY_GIMP
68 
69 #include <libgimp/gimp.h>
70 
71 static void xsane_gimp_query(void);
72 #ifdef HAVE_GIMP_2
73 static void xsane_gimp_run(const gchar *name, gint nparams, const GimpParam *param, gint *nreturn_vals, GimpParam **return_vals);
74 #else
75 static void xsane_gimp_run(char *name, int nparams, GimpParam *param, int *nreturn_vals, GimpParam **return_vals);
76 #endif
77 
78 GimpPlugInInfo PLUG_IN_INFO =
79 {
80 #if 1
81   NULL,                         /* init_proc */
82 #else
83   xsane_gimp_query,             /* init_proc that queries xsane each time gimp is started */
84 #endif
85   NULL,                         /* quit_proc */
86   xsane_gimp_query,             /* query_proc */
87   xsane_gimp_run,               /* run_proc */
88 };
89 
90 
91 static int xsane_decode_devname(const char *encoded_devname, int n,
92 char *buf);
93 static int xsane_encode_devname(const char *devname, int n, char *buf);
94 void null_print_func(gchar *msg);
95 
96 #endif /* HAVE_ANY_GIMP */
97 
98 /* ---------------------------------------------------------------------------------------------------------------------- */
99 /* why this routine ?
100  Problem: link attack
101           Bad user wants to overwrite a file (mywork.txt) of good user.
102           File permissions of mywork.txt is 700 so that bad user can not
103           change or overwrite the file. Directory permissions allow bad user
104           to write into directory. Bad user sets symlink from a file that good
105           user will write soon (image.pnm) to mywork.txt.
106           ==> Good user overwrites his own file, he is allowed to do so.
107 
108  Solution: remove file.
109            Create outputfile and make sure that it does not exist while creation.
110 
111            The file is created with the requested image-file permissions.
112 
113  Note: This case is a bit curious because it is only a small part of a larger problem:
114  When other users have write access to the directory they simply can move
115  mywork.txt to image.pnm. If they do it in the right moment the file is
116  overwritten without any notice of good user. If they do it long before xsane
117  wants to write image.pnm then xsane will possibly ask if image.pnm shall be
118  overwritten. So the real solution is to make the direcoty permissions safe!!!
119  But some users asked for this and so I added this.
120 
121 
122  This routine shall not be called for temporary files because temp files shall not
123  be removed after they have been created safe. (Although a temporary file should
124  not be a symlink so there should be no problem with this)
125 */
126 
xsane_create_secure_file(const char * filename)127 int xsane_create_secure_file(const char *filename)
128 /* returns 0 on success, -1 on error */
129 {
130  int fd;
131 
132   DBG(DBG_proc, "xsane_create_secure_file\n");
133 
134   remove(filename); /* we need to remove the file because open(..., O_EXCL) will fail otherwise */
135   umask((mode_t) preferences.image_umask); /* define image file permissions */
136   fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0666);
137   umask(XSANE_DEFAULT_UMASK); /* define new file permissions */
138 
139   if (fd > 0)
140   {
141     DBG(DBG_info, "file %s is created and secure\n", filename);
142     close(fd);
143     fd = 0;
144   }
145   else
146   {
147     DBG(DBG_info, "could not create secure file %s\n", filename);
148   }
149 
150  return fd; /* -1 means file is not safe !!! otherwise 0 */
151 }
152 
153 /* ---------------------------------------------------------------------------------------------------------------------- */
154 
xsane_cancel_save(int * cancel_save)155 void xsane_cancel_save(int *cancel_save)
156 {
157   DBG(DBG_proc, "xsane_cancel_save\n");
158   *cancel_save = 1;
159 }
160 
161 /* ---------------------------------------------------------------------------------------------------------------------- */
162 
xsane_convert_text_to_filename(char ** text)163 void xsane_convert_text_to_filename(char **text)
164 {
165   DBG(DBG_proc, "xsane_convert_text_to_filename\n");
166 
167   if (text)
168   {
169    char *filename = *text;
170    char buf[TEXTBUFSIZE];
171    int buflen=0;
172    int txtlen=0;
173 
174     while((filename[txtlen] != 0) && (buflen<253))
175     {
176       switch (filename[txtlen])
177       {
178         case ' ':
179           buf[buflen++] = ':';
180           buf[buflen++] = '_';
181           txtlen++;
182           break;
183 
184         case '/':
185           buf[buflen++] = ':';
186           buf[buflen++] = '%';
187           txtlen++;
188           break;
189 
190         case '*':
191           buf[buflen++] = ':';
192           buf[buflen++] = '#';
193           txtlen++;
194           break;
195 
196         case '?':
197           buf[buflen++] = ':';
198           buf[buflen++] = 'q';
199           txtlen++;
200           break;
201 
202         case '\\':
203           buf[buflen++] = ':';
204           buf[buflen++] = '=';
205           txtlen++;
206           break;
207 
208         case ';':
209           buf[buflen++] = ':';
210           buf[buflen++] = '!';
211           txtlen++;
212           break;
213 
214         case '&':
215           buf[buflen++] = ':';
216           buf[buflen++] = '+';
217           txtlen++;
218           break;
219 
220         case '<':
221           buf[buflen++] = ':';
222           buf[buflen++] = 's';
223           txtlen++;
224           break;
225 
226         case '>':
227           buf[buflen++] = ':';
228           buf[buflen++] = 'g';
229           txtlen++;
230           break;
231 
232         case '|':
233           buf[buflen++] = ':';
234           buf[buflen++] = 'p';
235           txtlen++;
236           break;
237 
238         case ':':
239           buf[buflen++] = ':';
240           buf[buflen++] = ':';
241           txtlen++;
242           break;
243 
244         default:
245           buf[buflen++] = filename[txtlen++];
246           break;
247       }
248     }
249     buf[buflen] = 0;
250     free(filename);
251     *text = strdup(buf);
252     DBG(DBG_info, "filename = \"%s\"\n", *text);
253   }
254 }
255 
256 /* ---------------------------------------------------------------------------------------------------------------------- */
257 
xsane_get_filesize(char * filename)258 int xsane_get_filesize(char *filename)
259 {
260  FILE *infile;
261  int pos;
262  int size;
263 
264   infile = fopen(filename, "rb"); /* read binary (b for win32) */
265   if (infile == NULL)
266   {
267    return 0;
268   }
269 
270   pos = ftell(infile);
271   fseek(infile, 0, SEEK_END); /* get size */
272   size = ftell(infile);
273   fseek(infile, pos, SEEK_SET); /* go to previous position */
274 
275   fclose(infile);
276 
277  return size;
278 }
279 
280 /* ---------------------------------------------------------------------------------------------------------------------- */
281 
xsane_ensure_counter_in_filename(char ** filename,int counter_len)282 void xsane_ensure_counter_in_filename(char **filename, int counter_len)
283 {
284  char *position_point = NULL;
285  char *position;
286  int counter = 1;
287 
288   DBG(DBG_proc, "xsane_ensure_counter_in_filename\n");
289 
290   if (!counter_len)
291   {
292    counter_len = 1;
293   }
294 
295   position_point = strrchr(*filename, '.');
296 
297   if (!position_point) /* nothing usable ? */
298   {
299     position_point = *filename + strlen(*filename); /* position_point - 1 is last character */
300   }
301 
302   if (position_point)
303   {
304     position = position_point-1;
305     if ( (position < *filename) || (*position < '0') || (*position >'9') ) /* we have no counter */
306     {
307      char buf[PATH_MAX];
308      int len;
309 
310       len = position_point - (*filename); /* length until "." or end of string */
311       strncpy(buf, *filename, len);
312       snprintf(buf+len, sizeof(buf)-len, "-%0*d%s", counter_len,  counter, position_point);
313       *filename = strdup(buf);
314     }
315   }
316 }
317 
318 /* ---------------------------------------------------------------------------------------------------------------------- */
319 
xsane_update_counter_in_filename(char ** filename,int skip,int step,int min_counter_len)320 void xsane_update_counter_in_filename(char **filename, int skip, int step, int min_counter_len)
321 {
322  FILE *testfile;
323  char *position_point = NULL;
324  char *position_counter;
325  char buf[PATH_MAX];
326  int counter;
327  int counter_len;
328  int set_counter_len = min_counter_len;
329 
330   DBG(DBG_proc, "xsane_update_counter_in_filename\n");
331 
332   if ( (!step) && (!min_counter_len) )
333   {
334     return; /* do not touch counter */
335   }
336 
337   while (1) /* loop because we may have to skip existing files */
338   {
339     position_point = strrchr(*filename, '.');
340 
341     if (!position_point) /* nothing usable ? */
342     {
343       position_point = *filename + strlen(*filename); /* here is no point, but position - 1 is last character */
344     }
345 
346     if (position_point)
347     {
348       position_counter = position_point-1; /* go to last number of counter (if counter exists) */
349 
350       /* search non numeric char */
351       while ( (position_counter >= *filename) && (*position_counter >= '0') && (*position_counter <='9') )
352       {
353         position_counter--; /* search fisrt numeric character */
354       }
355 
356       position_counter++; /* go to first numeric charcter */
357 
358       counter_len = position_point - position_counter;
359 
360       if (counter_len) /* we have a counter */
361       {
362         sscanf(position_counter, "%d", &counter);
363         counter = counter + step; /* update counter */
364 
365         if (counter < 0)
366         {
367           counter = 0;
368           xsane_back_gtk_warning(WARN_COUNTER_UNDERRUN, TRUE);
369           break; /* last available number ("..999") */
370         }
371 
372         *position_counter = 0; /* set end of string mark to counter start */
373 
374         if (set_counter_len == 0)
375         {
376            set_counter_len = counter_len;
377         }
378 
379         snprintf(buf, sizeof(buf), "%s%0*d%s", *filename, set_counter_len,  counter, position_point);
380 
381         DBG(DBG_info, "filename = \"%s\"\n", buf);
382 
383         free(*filename);
384         *filename = strdup(buf);
385 
386         if (skip) /* test if filename already used */
387         {
388           if (preferences.filetype) /* add filetype to filename */
389           {
390             snprintf(buf, sizeof(buf), "%s%s", *filename, preferences.filetype);
391             testfile = fopen(buf, "rb"); /* read binary (b for win32) */
392           }
393           else /* filetype in filename */
394           {
395             testfile = fopen(*filename, "rb"); /* read binary (b for win32) */
396           }
397 
398           if (testfile) /* filename used: skip */
399           {
400             fclose(testfile);
401           }
402           else
403           {
404             break; /* filename not used, ok */
405           }
406         }
407         else /* do not test if filename already used */
408         {
409           break; /* filename ok */
410         }
411       }
412       else /* no counter */
413       {
414         break; /* no counter */
415       }
416     }
417   }
418 }
419 
420 /* ---------------------------------------------------------------------------------------------------------------------- */
421 
xsane_read_pnm_header(FILE * file,Image_info * image_info)422 void xsane_read_pnm_header(FILE *file, Image_info *image_info)
423 {
424  int max_val, filetype_nr;
425  char buf[TEXTBUFSIZE];
426  int items_done;
427 
428   fgets(buf, sizeof(buf)-1, file);
429   DBG(DBG_info, "filetype header :%s", buf);
430 
431   if (buf[0] == 'P')
432   {
433     filetype_nr = atoi(buf+1); /* get filetype number */
434 
435     image_info->resolution_x = 72.0;
436     image_info->resolution_y = 72.0;
437     image_info->reduce_to_lineart = FALSE;
438     image_info->enable_color_management = FALSE;
439 
440     while (strcmp(buf, "# XSANE data follows\n"))
441     {
442       fgets(buf, sizeof(buf)-1, file);
443 
444       if (!strncmp(buf, "#  resolution_x    =", 20))
445       {
446         sscanf(buf+20, "%lf", &image_info->resolution_x);
447       }
448       else if (!strncmp(buf, "#  resolution_y    =", 20))
449       {
450         sscanf(buf+20, "%lf", &image_info->resolution_y);
451       }
452       else if (!strncmp(buf, "#  threshold       =", 20))
453       {
454         sscanf(buf+20, "%lf", &image_info->threshold);
455       }
456       else if (!strncmp(buf, "#  gamma           =", 20))
457       {
458         sscanf(buf+20, "%lf", &image_info->gamma);
459       }
460       else if (!strncmp(buf, "#  gamma      IRGB =", 20))
461       {
462         sscanf(buf+20, "%lf %lf %lf %lf",
463                         &image_info->gamma,
464                              &image_info->gamma_red,
465                                 &image_info->gamma_green,
466                                     &image_info->gamma_blue);
467       }
468       else if (!strncmp(buf, "#  brightness      =", 20))
469       {
470         sscanf(buf+20, "%lf", &image_info->brightness);
471       }
472       else if (!strncmp(buf, "#  brightness IRGB =", 20))
473       {
474         sscanf(buf+20, "%lf %lf %lf %lf",
475                         &image_info->brightness,
476                              &image_info->brightness_red,
477                                 &image_info->brightness_green,
478                                     &image_info->brightness_blue);
479       }
480       else if (!strncmp(buf, "#  contrast        =", 20))
481       {
482         sscanf(buf+20, "%lf", &image_info->contrast);
483       }
484       else if (!strncmp(buf, "#  contrast   IRGB =", 20))
485       {
486         sscanf(buf+20, "%lf %lf %lf %lf",
487                         &image_info->contrast,
488                              &image_info->contrast_red,
489                                 &image_info->contrast_green,
490                                     &image_info->contrast_blue);
491       }
492       else if (!strncmp(buf, "#  color-management=", 20))
493       {
494         sscanf(buf+20, "%d", &image_info->enable_color_management);
495       }
496       else if (!strncmp(buf, "#  cms-function    =", 20))
497       {
498         sscanf(buf+20, "%d", &image_info->cms_function);
499       }
500       else if (!strncmp(buf, "#  cms-intent      =", 20))
501       {
502         sscanf(buf+20, "%d", &image_info->cms_intent);
503       }
504       else if (!strncmp(buf, "#  cms-bpc         =", 20))
505       {
506         sscanf(buf+20, "%d", &image_info->cms_bpc);
507       }
508       else if (!strncmp(buf, "#  icm-profile     =", 20))
509       {
510         sscanf(buf+20, "%s", image_info->icm_profile);
511       }
512       else if (!strncmp(buf, "#  reduce to lineart", 20))
513       {
514         image_info->reduce_to_lineart = TRUE;
515       }
516     }
517 
518     items_done = fscanf(file, "%d %d", &image_info->image_width, &image_info->image_height);
519 
520     image_info->depth = 1;
521 
522     if (filetype_nr != 4) /* P4 = lineart */
523     {
524       items_done = fscanf(file, "%d", &max_val);
525 
526       if (max_val == 255)
527       {
528         image_info->depth = 8;
529       }
530       else if (max_val == 65535)
531       {
532         image_info->depth = 16;
533       }
534     }
535 
536     fgetc(file); /* read exactly one newline character */
537 
538 
539     image_info->channels = 1;
540 
541     if (filetype_nr == 6) /* ppm RGB */
542     {
543       image_info->channels = 3;
544     }
545   }
546 #ifdef SUPPORT_RGBA
547   else if (buf[0] == 'S') /* RGBA format */
548   {
549     items_done = fscanf(file, "%d %d\n%d", &image_info->image_width, &image_info->image_height, &max_val);
550     fgetc(file); /* read exactly one newline character */
551 
552     image_info->depth = 1;
553 
554     if (max_val == 255)
555     {
556       image_info->depth = 8;
557     }
558     else if (max_val == 65535)
559     {
560       image_info->depth = 16;
561     }
562 
563     image_info->channels = 4;
564   }
565 #endif
566 
567   DBG(DBG_info, "xsane_read_pnm_header: width=%d, height=%d, depth=%d, colors=%d, resolution_x=%f, resolution_y=%f\n",
568       image_info->image_width, image_info->image_height, image_info->depth, image_info->channels,
569       image_info->resolution_x, image_info->resolution_y);
570 }
571 
572 /* ---------------------------------------------------------------------------------------------------------------------- */
573 
xsane_write_pnm_header(FILE * file,Image_info * image_info,int save_pnm16_as_ascii)574 void xsane_write_pnm_header(FILE *file, Image_info *image_info, int save_pnm16_as_ascii)
575 {
576  int maxval;
577  int magic;
578 
579   fflush(file);
580   rewind(file);
581 
582   if (image_info->depth > 8)
583   {
584     maxval = 65535;
585 
586     if (save_pnm16_as_ascii)
587     {
588       magic = 2; /* thats the magic number for grayscale ascii, 3 = color ascii */
589     }
590     else /* save pnm as binary */
591     {
592       magic = 5; /* that is the magic number for grayscake binary, 6 = color binary */
593     }
594   }
595   else
596   {
597     maxval = 255;
598     magic = 5; /* 8 bit images are always saved in binary mode */
599   }
600 
601 
602   if (image_info->channels == 1)
603   {
604     if (image_info->depth == 1)
605     {
606       /* do not touch the texts and length here, the reading routine needs to know the exact texts */
607       fprintf(file, "P4\n"
608                        "# XSane settings:\n"
609                        "#  resolution_x    = %6.1f\n"
610                        "#  resolution_y    = %6.1f\n"
611                        "#  threshold       = %4.1f\n"
612                        "# XSANE data follows\n"
613                        "%05d %05d\n",
614                        image_info->resolution_x,
615                        image_info->resolution_y,
616                        image_info->threshold,
617                        image_info->image_width, image_info->image_height);
618     }
619     else if (image_info->reduce_to_lineart)
620     {
621       /* do not touch the texts and length here, the reading routine needs to know the exact texts */
622       fprintf(file, "P%d\n"
623                        "# XSane settings:\n"
624                        "#  resolution_x    = %6.1f\n"
625                        "#  resolution_y    = %6.1f\n"
626                        "#  threshold       = %4.1f\n"
627                        "#  reduce to lineart\n"
628                        "# XSANE data follows\n"
629                        "%05d %05d\n"
630                        "%d\n",
631                        magic, /* P5 for binary, P2 for ascii */
632                        image_info->resolution_x,
633                        image_info->resolution_y,
634                        image_info->threshold,
635                        image_info->image_width, image_info->image_height,
636                        maxval);
637     }
638     else
639     {
640       fprintf(file, "P%d\n"
641                        "# XSane settings:\n"
642                        "#  resolution_x    = %6.1f\n"
643                        "#  resolution_y    = %6.1f\n"
644                        "#  gamma           = %3.2f\n"
645                        "#  brightness      = %4.1f\n"
646                        "#  contrast        = %4.1f\n"
647                        "#  color-management= %d\n"
648                        "#  cms-function    = %d\n"
649                        "#  cms-intent      = %d\n"
650                        "#  cms-bpc         = %d\n"
651                        "#  icm-profile     = %s\n"
652                        "# XSANE data follows\n"
653                        "%05d %05d\n"
654                        "%d\n",
655                        magic, /* P5 for binary, P2 for ascii */
656                        image_info->resolution_x,
657                        image_info->resolution_y,
658                        image_info->gamma,
659                        image_info->brightness,
660                        image_info->contrast,
661 		       image_info->enable_color_management,
662 		       image_info->cms_function,
663 		       image_info->cms_intent,
664 		       image_info->cms_bpc,
665 		       image_info->icm_profile,
666                        image_info->image_width, image_info->image_height,
667                        maxval);
668     }
669   }
670   else if (image_info->channels == 3)
671   {
672     fprintf(file, "P%d\n"
673                      "# XSane settings:\n"
674                      "#  resolution_x    = %6.1f\n"
675                      "#  resolution_y    = %6.1f\n"
676                      "#  gamma      IRGB = %3.2f %3.2f %3.2f %3.2f\n"
677                      "#  brightness IRGB = %4.1f %4.1f %4.1f %4.1f\n"
678                      "#  contrast   IRGB = %4.1f %4.1f %4.1f %4.1f\n"
679                      "#  color-management= %d\n"
680                      "#  cms-function    = %d\n"
681                      "#  cms-intent      = %d\n"
682                      "#  cms-bpc         = %d\n"
683                      "#  icm-profile     = %s\n"
684                      "# XSANE data follows\n"
685                      "%05d %05d\n" \
686                      "%d\n",
687                      magic+1, /* P6 for binary, P3 for ascii */
688                      image_info->resolution_x,
689                      image_info->resolution_y,
690                      image_info->gamma,      image_info->gamma_red,      image_info->gamma_green,      image_info->gamma_blue,
691                      image_info->brightness, image_info->brightness_red, image_info->brightness_green, image_info->brightness_blue,
692                      image_info->contrast,   image_info->contrast_red,   image_info->contrast_green,   image_info->contrast_blue,
693 		     image_info->enable_color_management,
694 		     image_info->cms_function,
695 		     image_info->cms_intent,
696 		     image_info->cms_bpc,
697 		     image_info->icm_profile,
698                      image_info->image_width, image_info->image_height,
699                      maxval);
700   }
701 #ifdef SUPPORT_RGBA
702   else if (image_info->channels == 4)
703   {
704         fprintf(file, "SANE_RGBA\n" \
705                          "%d %d\n" \
706                          "%d\n",
707                          image_info->image_width, image_info->image_height, maxval);
708   }
709 #endif
710 }
711 
712 /* ---------------------------------------------------------------------------------------------------------------------- */
713 
xsane_copy_file(FILE * outfile,FILE * infile,GtkProgressBar * progress_bar,int * cancel_save)714 int xsane_copy_file(FILE *outfile, FILE *infile, GtkProgressBar *progress_bar, int *cancel_save)
715 {
716  long size;
717  long bytes_sum = 0;
718  size_t bytes;
719  unsigned char buf[65536];
720 
721   DBG(DBG_proc, "copying file\n");
722 
723   fseek(infile, 0, SEEK_END);
724   size = ftell(infile);
725   fseek(infile, 0, SEEK_SET);
726 
727   xsane_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress_bar), 0.0);
728 
729   while (!feof(infile))
730   {
731     bytes = fread(buf, 1, sizeof(buf), infile);
732     if (bytes > 0)
733     {
734       fwrite(buf, 1, bytes, outfile);
735       bytes_sum += bytes;
736     }
737 
738     xsane_progress_bar_set_fraction(progress_bar, (float) bytes_sum / size); /* update progress bar */
739 
740     if (ferror(infile))
741     {
742      char buf[TEXTBUFSIZE];
743 
744       snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_READ, strerror(errno));
745       DBG(DBG_error, "%s\n", buf);
746       xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
747       *cancel_save = 1;
748      break;
749     }
750 
751     if (ferror(outfile))
752     {
753      char buf[TEXTBUFSIZE];
754 
755       snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno));
756       DBG(DBG_error, "%s\n", buf);
757       xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
758       *cancel_save = 1;
759      break;
760     }
761 
762     if (*cancel_save)
763     {
764       break;
765     }
766   }
767 
768   fflush(outfile);
769 
770   if (size != bytes_sum)
771   {
772     DBG(DBG_info, "copy errro, not complete, %ld bytes of %ld bytes copied\n", bytes_sum, size);
773     *cancel_save = 1;
774    return (*cancel_save);
775   }
776 
777   DBG(DBG_info, "copy complete, %ld bytes copied\n", bytes_sum);
778 
779  return (*cancel_save);
780 }
781 
782 /* ---------------------------------------------------------------------------------------------------------------------- */
783 
xsane_copy_file_by_name(char * output_filename,char * input_filename,GtkProgressBar * progress_bar,int * cancel_save)784 int xsane_copy_file_by_name(char *output_filename, char *input_filename, GtkProgressBar *progress_bar, int *cancel_save)
785 {
786  FILE *infile;
787  FILE *outfile;
788 
789   DBG(DBG_proc, "copying file %s to %s\n", input_filename, output_filename);
790 
791   outfile = fopen(output_filename, "wb"); /* b = binary mode for win32 */
792 
793   if (outfile == 0)
794   {
795    char buf[TEXTBUFSIZE];
796 
797     snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, output_filename, strerror(errno));
798     xsane_back_gtk_error(buf, TRUE);
799    return -2;
800   }
801 
802   infile = fopen(input_filename, "rb"); /* read binary (b for win32) */
803   if (infile == 0)
804   {
805    char buf[TEXTBUFSIZE];
806     snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, input_filename, strerror(errno));
807     xsane_back_gtk_error(buf, TRUE);
808 
809     fclose(outfile);
810     remove(output_filename); /* remove already created output file */
811    return -1;
812   }
813 
814   xsane_copy_file(outfile, infile, progress_bar, cancel_save);
815 
816   fclose(infile);
817   fclose(outfile);
818 
819   gtk_progress_set_format_string(GTK_PROGRESS(progress_bar), "");
820   xsane_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress_bar), 0.0);
821 
822  return (*cancel_save);
823 }
824 
825 /* ---------------------------------------------------------------------------------------------------------------------- */
826 
827 #ifdef HAVE_LIBLCMS
xsane_create_cms_transform(Image_info * image_info,int cms_function,int cms_intent,int cms_bpc)828 cmsHTRANSFORM xsane_create_cms_transform(Image_info *image_info, int cms_function, int cms_intent, int cms_bpc)
829 {
830  cmsHPROFILE hInProfile = NULL;
831  cmsHPROFILE hOutProfile = NULL;
832  cmsHTRANSFORM hTransform = NULL;
833  DWORD cms_input_format;
834  DWORD cms_output_format;
835  DWORD cms_flags = 0;
836 
837   if (cms_function == XSANE_CMS_FUNCTION_EMBED_SCANNER_ICM_PROFILE)
838   {
839     return NULL;
840   }
841 
842   DBG(DBG_info, "Prepare CMS transform\n");
843 
844   cmsErrorAction(LCMS_ERROR_SHOW);
845 
846   if (cms_bpc)
847   {
848     cms_flags |= cmsFLAGS_BLACKPOINTCOMPENSATION;
849   }
850 
851   if (image_info->channels == 1) /* == 1 (grayscale) */
852   {
853     if (image_info->depth == 8)
854     {
855       cms_input_format  = TYPE_GRAY_8;
856       cms_output_format = TYPE_GRAY_8;
857     }
858     else
859     {
860       cms_input_format  = TYPE_GRAY_16;
861       cms_output_format = TYPE_GRAY_16;
862     }
863   }
864   else /* color */
865   {
866     if (image_info->depth == 8)
867     {
868       cms_input_format  = TYPE_RGB_8;
869       cms_output_format = TYPE_RGB_8;
870     }
871     else
872     {
873       cms_input_format  = TYPE_RGB_16;
874       cms_output_format = TYPE_RGB_16;
875     }
876   }
877 
878   hInProfile  = cmsOpenProfileFromFile(image_info->icm_profile, "r");
879   if (!hInProfile)
880   {
881    char buf[TEXTBUFSIZE];
882 
883     snprintf(buf, sizeof(buf), "%s\n%s %s: %s\n", ERR_CMS_CONVERSION, ERR_CMS_OPEN_ICM_FILE, CMS_SCANNER_ICM, image_info->icm_profile);
884     xsane_back_gtk_error(buf, TRUE);
885   }
886   if (cms_function == XSANE_CMS_FUNCTION_CONVERT_TO_SRGB)
887   {
888     if (image_info->channels == 1) /* == 1 (grayscale) */
889     {
890 #if 1 /* xxx oli */
891      LPGAMMATABLE Gamma = cmsBuildGamma(256, 2.2);
892 
893       hOutProfile = cmsCreateGrayProfile(cmsD50_xyY(), Gamma);
894       cmsFreeGamma(Gamma);
895 #endif
896     }
897     else
898     {
899       hOutProfile = cmsCreate_sRGBProfile();
900     }
901   }
902   else
903   {
904     hOutProfile = cmsOpenProfileFromFile(preferences.working_color_space_icm_profile, "r");
905     if (!hOutProfile)
906     {
907      char buf[TEXTBUFSIZE];
908 
909       cmsCloseProfile(hInProfile);
910 
911       snprintf(buf, sizeof(buf), "%s\n%s %s: %s\n", ERR_CMS_CONVERSION, ERR_CMS_OPEN_ICM_FILE, CMS_DISPLAY_ICM, preferences.display_icm_profile);
912       xsane_back_gtk_error(buf, TRUE);
913     }
914   }
915 
916   if (!hOutProfile)
917   {
918    char buf[TEXTBUFSIZE];
919 
920     cmsCloseProfile(hInProfile);
921 
922     snprintf(buf, sizeof(buf), "%s\n", ERR_CMS_CONVERSION);
923     xsane_back_gtk_error(buf, TRUE);
924   }
925 
926   hTransform = cmsCreateTransform(hInProfile, cms_input_format,
927                                   hOutProfile, cms_output_format,
928                                   cms_intent, cms_flags);
929 
930   cmsCloseProfile(hInProfile);
931   cmsCloseProfile(hOutProfile);
932 
933   if (!hTransform)
934   {
935    char buf[TEXTBUFSIZE];
936 
937     snprintf(buf, sizeof(buf), "%s\n%s\n", ERR_CMS_CONVERSION, ERR_CMS_CREATE_TRANSFORM);
938     xsane_back_gtk_error(buf, TRUE);
939   }
940 
941  return hTransform;
942 }
943 #endif
944 
945 /* ---------------------------------------------------------------------------------------------------------------------- */
946 
xsane_save_grayscale_image_as_lineart(FILE * outfile,FILE * imagefile,Image_info * image_info,GtkProgressBar * progress_bar,int * cancel_save)947 int xsane_save_grayscale_image_as_lineart(FILE *outfile, FILE *imagefile, Image_info *image_info, GtkProgressBar *progress_bar, int *cancel_save)
948 {
949  int x, y, bit;
950  u_char bitval, packed;
951 
952   *cancel_save = 0;
953 
954   image_info->depth = 1;
955 
956   xsane_write_pnm_header(outfile, image_info, 0);
957 
958   for (y = 0; y < image_info->image_height; y++)
959   {
960     bit = 128;
961     packed = 0;
962 
963     for (x = 0; x < image_info->image_width; x++)
964     {
965       bitval = fgetc(imagefile);
966 
967       if (!bitval) /* white gets 0 bit, black gets 1 bit */
968       {
969         packed |= bit;
970       }
971 
972       if (bit == 1)
973       {
974         fputc(packed, outfile);
975         bit = 128;
976         packed = 0;
977       }
978       else
979       {
980         bit >>= 1;
981       }
982     }
983 
984     if (bit != 128)
985     {
986       fputc(packed, outfile);
987       bit = 128;
988       packed = 0;
989     }
990 
991     if (ferror(outfile))
992     {
993      char buf[TEXTBUFSIZE];
994 
995       snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno));
996       DBG(DBG_error, "%s\n", buf);
997       xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
998       *cancel_save = 1;
999      break;
1000     }
1001 
1002 
1003     xsane_progress_bar_set_fraction(progress_bar, (float) y / image_info->image_height); /* update progress bar */
1004 
1005     if (*cancel_save)
1006     {
1007       break;
1008     }
1009   }
1010 
1011  return (*cancel_save);
1012 }
1013 
1014 /* ---------------------------------------------------------------------------------------------------------------------- */
1015 
xsane_save_scaled_image(FILE * outfile,FILE * imagefile,Image_info * image_info,float x_scale,float y_scale,GtkProgressBar * progress_bar,int * cancel_save)1016 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)
1017 {
1018  int original_image_width  = image_info->image_width;
1019  int original_image_height = image_info->image_height;
1020  int new_image_width  = image_info->image_width  * x_scale + 0.5;
1021  int new_image_height = image_info->image_height * y_scale + 0.5;
1022  unsigned char *original_line;
1023  guint16 *original_line16 = NULL;
1024  unsigned char *new_line;
1025  float *pixel_val;
1026  float *pixel_norm;
1027  int bytespp = 1;
1028  float x, y;
1029  int c;
1030  int oldy;
1031  int x_new, y_new;
1032  float x_go, y_go;
1033  float factor, x_factor, y_factor;
1034  guint16 color;
1035  int read_line;
1036  size_t bytes_read;
1037 
1038   DBG(DBG_proc, "xsane_save_scaled_image\n");
1039 
1040   *cancel_save = 0;
1041 
1042   if (image_info->depth > 8)
1043   {
1044     bytespp = 2;
1045   }
1046 
1047   image_info->image_width  = new_image_width;
1048   image_info->image_height = new_image_height;
1049   image_info->resolution_x *= x_scale;
1050   image_info->resolution_y *= y_scale;
1051 
1052   original_line = malloc(original_image_width * image_info->channels * bytespp);
1053   if (!original_line)
1054   {
1055     DBG(DBG_error, "xsane_save_scaled_image: out of memory\n");
1056    return -1;
1057   }
1058 
1059   new_line = malloc(new_image_width * image_info->channels * bytespp);
1060   if (!new_line)
1061   {
1062     free(original_line);
1063     DBG(DBG_error, "xsane_save_scaled_image: out of memory\n");
1064    return -1;
1065   }
1066 
1067   pixel_val = malloc(new_image_width * image_info->channels * sizeof(float));
1068   if (!pixel_val)
1069   {
1070     free(original_line);
1071     free(new_line);
1072     DBG(DBG_error, "xsane_save_scaled_image: out of memory\n");
1073    return -1;
1074   }
1075 
1076   pixel_norm = malloc(new_image_width * image_info->channels * sizeof(float));
1077   if (!pixel_norm)
1078   {
1079     free(original_line);
1080     free(new_line);
1081     free(pixel_val);
1082     DBG(DBG_error, "xsane_save_scaled_image: out of memory\n");
1083    return -1;
1084   }
1085 
1086   xsane_write_pnm_header(outfile, image_info, 0);
1087 
1088   read_line = TRUE;
1089 
1090   memset(pixel_val,  0, new_image_width * image_info->channels * sizeof(float));
1091   memset(pixel_norm, 0, new_image_width * image_info->channels * sizeof(float));
1092 
1093   y_new = 0;
1094   y_go = 1.0 / y_scale;
1095   y_factor = 1.0;
1096   y = 0.0;
1097 
1098   while (y < original_image_height)
1099   {
1100     DBG(DBG_info2, "xsane_save_scaled_image: original line %d, new line %d\n", (int) y, y_new);
1101 
1102     xsane_progress_bar_set_fraction(progress_bar, (float) y / original_image_height);
1103 
1104     if (read_line)
1105     {
1106       DBG(DBG_info, "xsane_save_scaled_image: reading original line %d\n", (int) y);
1107       bytes_read = fread(original_line, original_image_width, image_info->channels * bytespp, imagefile); /* read one line */
1108       original_line16 = (guint16 *) original_line;
1109     }
1110 
1111     x_new = 0;
1112     x_go = 1.0 / x_scale;
1113     x = 0.0;
1114     x_factor = 1.0;
1115 
1116     while ( (x < original_image_width) && (x_new < new_image_width) ) /* add this line to anti aliasing buffer */
1117     {
1118       factor = x_factor * y_factor;
1119 
1120       for (c = 0; c < image_info->channels; c++)
1121       {
1122         if (bytespp == 1)
1123         {
1124           color = original_line[((int) x) * image_info->channels + c];
1125         }
1126         else /* bytespp == 2 */
1127         {
1128           color = original_line16[((int) x) * image_info->channels + c];
1129         }
1130 
1131         pixel_val [x_new * image_info->channels + c] += factor * color;
1132         pixel_norm[x_new * image_info->channels + c] += factor;
1133       }
1134 
1135       x_go -= x_factor;
1136 
1137       if (x_go <= 0.0) /* change of pixel in new image */
1138       {
1139         x_new++;
1140         x_go = 1.0 / x_scale;
1141 
1142         x_factor = x - (int) x; /* use pixel rest */
1143         if (x_factor > x_go)
1144         {
1145           x_factor = x_go;
1146         }
1147       }
1148       else
1149       {
1150         x_factor = x_go;
1151       }
1152 
1153       if (x_factor > 1.0)
1154       {
1155         x_factor = 1.0;
1156       }
1157 
1158       x += x_factor;
1159     }
1160 
1161     y_go -= y_factor;
1162 
1163     if (y_go <= 0.0) /* normalize one line and write to destination image file */
1164     {
1165       DBG(DBG_info2, "xsane_save_scaled_image: writing new line %d\n", y_new);
1166 
1167       if (bytespp == 1)
1168       {
1169         for (x_new = 0; x_new < new_image_width * image_info->channels; x_new++)
1170         {
1171           new_line[x_new] = (int) (pixel_val[x_new] / pixel_norm[x_new]);
1172         }
1173       }
1174       else /* bytespp == 2 */
1175       {
1176        guint16 *new_line16 = (guint16 *) new_line;
1177 
1178         for (x_new = 0; x_new < new_image_width * image_info->channels; x_new++)
1179         {
1180           new_line16[x_new] = (int) (pixel_val[x_new] / pixel_norm[x_new]);
1181         }
1182       }
1183 
1184       fwrite(new_line, new_image_width, image_info->channels * bytespp, outfile); /* write one line */
1185 
1186       if (ferror(outfile))
1187       {
1188        char buf[TEXTBUFSIZE];
1189 
1190         snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno));
1191         DBG(DBG_error, "%s\n", buf);
1192         xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
1193         *cancel_save = 1;
1194        break;
1195       }
1196 
1197       /* reset values and norm factors */
1198       memset(pixel_val,  0, new_image_width * image_info->channels * sizeof(float));
1199       memset(pixel_norm, 0, new_image_width * image_info->channels * sizeof(float));
1200 
1201       y_new++;
1202       y_go = 1.0 / y_scale;
1203 
1204       y_factor = y - (int) y;
1205       if (y_factor > y_go)
1206       {
1207         y_factor = y_go;
1208       }
1209     }
1210     else
1211     {
1212       y_factor = y_go;
1213     }
1214 
1215     if (y_factor > 1.0)
1216     {
1217       y_factor = 1.0;
1218     }
1219 
1220     oldy = (int) y;
1221     y += y_factor;
1222     read_line = (oldy != (int) y);
1223   }
1224 
1225   if (read_line) /* we have to write one more line */
1226   {
1227     fwrite(new_line, new_image_width, image_info->channels * bytespp, outfile); /* write one line */
1228   }
1229 
1230   free(original_line);
1231   free(new_line);
1232   free(pixel_val);
1233   free(pixel_norm);
1234 
1235  return (*cancel_save);
1236 }
1237 
1238 /* ---------------------------------------------------------------------------------------------------------------------- */
1239 #if 0
1240 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)
1241 {
1242  float original_y;
1243  int old_original_y;
1244  int x, y, i;
1245  int original_image_width  = image_info->image_width;
1246  int new_image_width  = image_info->image_width * x_scale;
1247  int new_image_height = image_info->image_height * y_scale;
1248  unsigned char *original_line;
1249  unsigned char *new_line;
1250  int bytespp = 1;
1251 
1252   DBG(DBG_proc, "xsane_save_scaled_image\n");
1253 
1254   if (image_info->depth > 8)
1255   {
1256     bytespp = 2;
1257   }
1258 
1259   image_info->image_width  = new_image_width;
1260   image_info->image_height = new_image_height;
1261   image_info->resolution_x *= x_scale;
1262   image_info->resolution_y *= y_scale;
1263 
1264   original_line = malloc(original_image_width * image_info->channels * bytespp);
1265   if (!original_line)
1266   {
1267     DBG(DBG_error, "xsane_save_scaled_image: out of memory\n");
1268    return -1;
1269   }
1270 
1271   new_line = malloc(new_image_width * image_info->channels * bytespp);
1272   if (!new_line)
1273   {
1274     free(original_line);
1275     DBG(DBG_error, "xsane_save_scaled_image: out of memory\n");
1276    return -1;
1277   }
1278 
1279   xsane_write_pnm_header(outfile, image_info, 0);
1280 
1281   original_y = 0.0;
1282   old_original_y = -1;
1283 
1284   for (y = 0; y < new_image_height; y++)
1285   {
1286     xsane_progress_bar_set_fraction(progress_bar, (float) y / image_info->image_height);
1287 
1288     for (; ((int) original_y) - old_original_y; old_original_y += 1)
1289     {
1290       bytes_read = fread(original_line, original_image_width, image_info->channels * bytespp, imagefile); /* read one line */
1291     }
1292 
1293     for (x = 0; x < new_image_width; x++)
1294     {
1295       for (i = 0; i < image_info->channels * bytespp; i++)
1296       {
1297         new_line[x * image_info->channels * bytespp + i] = original_line[((int) (x / x_scale)) * image_info->channels * bytespp + i];
1298       }
1299     }
1300 
1301     fwrite(new_line, new_image_width, image_info->channels * bytespp, outfile); /* write one line */
1302 
1303     original_y += 1/y_scale;
1304 
1305     if (*cancel_save)
1306     {
1307       break;
1308     }
1309   }
1310 
1311   free(original_line);
1312   free(new_line);
1313 
1314   fflush(outfile);
1315 
1316  return (*cancel_save);
1317 }
1318 #endif
1319 
1320 /* ---------------------------------------------------------------------------------------------------------------------- */
1321 
xsane_save_despeckle_image(FILE * outfile,FILE * imagefile,Image_info * image_info,int radius,GtkProgressBar * progress_bar,int * cancel_save)1322 int xsane_save_despeckle_image(FILE *outfile, FILE *imagefile, Image_info *image_info, int radius, GtkProgressBar *progress_bar, int *cancel_save)
1323 {
1324  int x, y, sx, sy, i;
1325  int xmin, xmax;
1326  int ymin, ymax;
1327  int count;
1328  unsigned char *line_cache;
1329  unsigned char *line_cache_ptr;
1330  guint16 *color_cache;
1331  guint16 *color_cache_ptr;
1332  int bytespp = 1;
1333  int color_radius;
1334  int color_width  = image_info->image_width * image_info->channels;
1335  size_t bytes_read;
1336 
1337   radius--; /* correct radius : 1 means nothing happens */
1338 
1339   if (radius < 1)
1340   {
1341     radius = 1;
1342   }
1343 
1344   color_radius = radius * image_info->channels;
1345 
1346   if (image_info->depth > 8)
1347   {
1348     bytespp = 2;
1349   }
1350 
1351   xsane_write_pnm_header(outfile, image_info, 0);
1352 
1353   line_cache = malloc(color_width * bytespp * (2 * radius + 1));
1354   if (!line_cache)
1355   {
1356     DBG(DBG_error, "xsane_despeckle_image: out of memory\n");
1357    return -1;
1358   }
1359 
1360   bytes_read = fread(line_cache, color_width * bytespp, (2 * radius + 1), imagefile);
1361 
1362   color_cache = malloc((size_t) sizeof(guint16) * (2*radius+1)*(2*radius+1));
1363 
1364   if (!color_cache)
1365   {
1366     free(line_cache);
1367     DBG(DBG_error, "xsane_despeckle_image: out of memory\n");
1368    return -1;
1369   }
1370 
1371   for (y = 0; y < image_info->image_height; y++)
1372   {
1373     xsane_progress_bar_set_fraction(progress_bar, (float)  y / image_info->image_height);
1374 
1375     ymin = y - radius;
1376     ymax = y + radius;
1377 
1378     if (ymin < 0)
1379     {
1380       ymin = 0;
1381     }
1382 
1383     if (ymax > image_info->image_height)
1384     {
1385       ymax = image_info->image_height;
1386     }
1387 
1388     for (x = 0; x < color_width; x++)
1389     {
1390       xmin = x - color_radius;
1391       xmax = x + color_radius;
1392 
1393       if (xmin < 0)
1394       {
1395         xmin = x % image_info->channels;
1396       }
1397 
1398       if (xmax > color_width)
1399       {
1400         xmax = color_width;
1401       }
1402 
1403       count = 0;
1404 
1405       color_cache_ptr = color_cache;
1406 
1407 
1408       if (bytespp == 1)
1409       {
1410         for (sy = ymin; sy <= ymax; sy++) /* search area defined by radius  - y part */
1411         {
1412           line_cache_ptr = line_cache + (sy-ymin) * color_width + xmin;
1413 
1414           for (sx = xmin; sx <= xmax; sx+=image_info->channels) /* x part */
1415           {
1416             *color_cache_ptr = *line_cache_ptr;
1417             color_cache_ptr++;
1418             line_cache_ptr += image_info->channels;
1419           }
1420         }
1421 
1422         /* sort color_cache */
1423 
1424         count = color_cache_ptr - color_cache;
1425 
1426         if (count > 1)
1427         {
1428          int d, j, val;
1429 
1430           for (d = count / 2; d > 0; d = d / 2)
1431           {
1432             for (i = d; i < count; i++)
1433             {
1434               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)
1435               {
1436                 val                = color_cache_ptr[0];
1437                 color_cache_ptr[0] = color_cache_ptr[d];
1438                 color_cache_ptr[d] = val;
1439               };
1440             }
1441           }
1442         }
1443 
1444         fputc((char) (color_cache[count/2]), outfile);
1445       }
1446       else /* 16 bit/color */
1447       {
1448        guint16 val16;
1449        guint16 *line_cache16 = (guint16 *) line_cache;
1450        guint16 *line_cache16_ptr;
1451        char *bytes16 = (char *) &val16;
1452 
1453         for (sy = ymin; sy <= ymax; sy++)
1454         {
1455           line_cache16_ptr = line_cache16 + (sy-ymin) * color_width + xmin;
1456 
1457           for (sx = xmin; sx <= xmax; sx+=image_info->channels)
1458           {
1459             *color_cache_ptr = *line_cache16_ptr;
1460             color_cache_ptr++;
1461             line_cache16_ptr += image_info->channels;
1462           }
1463         }
1464 
1465         /* sort color_cache */
1466 
1467         count = color_cache_ptr - color_cache;
1468 
1469         if (count > 1)
1470         {
1471          int d,j, val;
1472 
1473           for (d = count / 2; d > 0; d = d / 2)
1474           {
1475             for (i = d; i < count; i++)
1476             {
1477               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)
1478               {
1479                 val                = color_cache_ptr[0];
1480                 color_cache_ptr[0] = color_cache_ptr[d];
1481                 color_cache_ptr[d] = val;
1482               };
1483             }
1484           }
1485         }
1486 
1487         val16 = color_cache[count/2];
1488         fputc(bytes16[0], outfile); /* write bytes in machine byte order */
1489         fputc(bytes16[1], outfile);
1490       }
1491     }
1492 
1493     if (ferror(outfile))
1494     {
1495      char buf[TEXTBUFSIZE];
1496 
1497       snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno));
1498       DBG(DBG_error, "%s\n", buf);
1499       xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
1500       *cancel_save = 1;
1501      break;
1502     }
1503 
1504     if ((y > radius) && (y < image_info->image_height - radius))
1505     {
1506       memcpy(line_cache, line_cache + color_width * bytespp,
1507              color_width * bytespp * 2 * radius);
1508       bytes_read = fread(line_cache + color_width * bytespp * 2 * radius,
1509             color_width * bytespp, 1, imagefile);
1510     }
1511   }
1512 
1513   fflush(outfile);
1514 
1515   free(line_cache);
1516   free(color_cache);
1517 
1518  return 0;
1519 }
1520 
1521 /* ---------------------------------------------------------------------------------------------------------------------- */
1522 
xsane_save_blur_image(FILE * outfile,FILE * imagefile,Image_info * image_info,float radius,GtkProgressBar * progress_bar,int * cancel_save)1523 int xsane_save_blur_image(FILE *outfile, FILE *imagefile, Image_info *image_info, float radius, GtkProgressBar *progress_bar, int *cancel_save)
1524 {
1525  int x, y, sx, sy;
1526  int xmin, xmax;
1527  int ymin, ymax;
1528  double val, norm, outer_factor;
1529  unsigned char *line_cache;
1530  int bytespp = 1;
1531  int intradius;
1532  int xmin_flag;
1533  int xmax_flag;
1534  int ymin_flag;
1535  int ymax_flag;
1536  size_t bytes_read;
1537 
1538   *cancel_save = 0;
1539 
1540   intradius = (int) radius;
1541 
1542   outer_factor = radius - (int) radius;
1543 
1544   if (image_info->depth > 8)
1545   {
1546     bytespp = 2;
1547   }
1548 
1549   xsane_write_pnm_header(outfile, image_info, 0);
1550 
1551   line_cache = malloc(image_info->image_width * image_info->channels * bytespp * (2 * intradius + 1));
1552   if (!line_cache)
1553   {
1554     DBG(DBG_error, "xsane_blur_image: out of memory\n");
1555    return -1;
1556   }
1557 
1558   bytes_read = fread(line_cache, image_info->image_width * image_info->channels * bytespp, (2 * intradius + 1), imagefile);
1559 
1560   for (y = 0; y < image_info->image_height; y++)
1561   {
1562     xsane_progress_bar_set_fraction(progress_bar, (float)  y / image_info->image_height);
1563 
1564     for (x = 0; x < image_info->image_width * image_info->channels; x++)
1565     {
1566       xmin_flag = xmax_flag = ymin_flag = ymax_flag = TRUE;
1567 
1568       xmin = x - intradius * image_info->channels;
1569       xmax = x + intradius * image_info->channels;
1570 
1571       if (xmin < 0)
1572       {
1573         xmin = x % image_info->channels;
1574         xmin_flag = FALSE;
1575       }
1576 
1577       if (xmax > image_info->image_width * image_info->channels)
1578       {
1579         xmax = image_info->image_width * image_info->channels;
1580         xmax_flag = FALSE;
1581       }
1582 
1583       ymin = y - intradius;
1584       ymax = y + intradius;
1585 
1586       if (ymin < 0)
1587       {
1588         ymin = 0;
1589         ymin_flag = FALSE;
1590       }
1591 
1592       if (ymax > image_info->image_height)
1593       {
1594         ymax = image_info->image_height;
1595         ymax_flag = FALSE;
1596       }
1597 
1598       val  = 0.0;
1599       norm = 0.0;
1600 
1601       if (bytespp == 1)
1602       {
1603         if (xmin_flag) /* integrate over left margin */
1604         {
1605           for (sy = ymin+1; sy <= ymax-1 ; sy++)
1606           {
1607             val += outer_factor * line_cache[(sy-ymin) * image_info->image_width * image_info->channels + xmin];
1608             norm += outer_factor;
1609           }
1610         }
1611 
1612         if (xmax_flag) /* integrate over right margin */
1613         {
1614           for (sy = ymin+1; sy <= ymax-1 ; sy++)
1615           {
1616             val += outer_factor * line_cache[(sy-ymin) * image_info->image_width * image_info->channels + xmax];
1617             norm += outer_factor;
1618           }
1619         }
1620 
1621         if (ymin_flag) /* integrate over top margin */
1622         {
1623           for (sx = xmin+image_info->channels; sx <= xmax-image_info->channels ; sx += image_info->channels)
1624           {
1625             val += outer_factor * line_cache[sx];
1626             norm += outer_factor;
1627           }
1628         }
1629 
1630         if (ymax_flag) /* integrate over bottom margin */
1631         {
1632           for (sx = xmin+image_info->channels; sx <= xmax-image_info->channels ; sx += image_info->channels)
1633           {
1634             val += outer_factor * line_cache[(ymax-ymin) * image_info->image_width * image_info->channels + sx];
1635             norm += outer_factor;
1636           }
1637         }
1638 
1639         for (sy = ymin+1; sy <= ymax-1; sy++) /* integrate internal square */
1640         {
1641           for (sx = xmin+image_info->channels; sx <= xmax-image_info->channels; sx+=image_info->channels)
1642           {
1643             val += line_cache[(sy-ymin) * image_info->image_width * image_info->channels + sx];
1644             norm += 1.0;
1645           }
1646         }
1647         fputc((char) ((int) (val/norm)), outfile);
1648       }
1649       else /* bytespp == 2 */
1650       {
1651        guint16 *line_cache16 = (guint16 *) line_cache;
1652        guint16 val16;
1653        char *bytes16 = (char *) &val16;
1654 
1655         if (xmin_flag) /* integrate over left margin */
1656         {
1657           for (sy = ymin+1; sy <= ymax-1 ; sy++)
1658           {
1659             val += outer_factor * line_cache16[(sy-ymin) * image_info->image_width * image_info->channels + xmin];
1660             norm += outer_factor;
1661           }
1662         }
1663 
1664         if (xmax_flag) /* integrate over right margin */
1665         {
1666           for (sy = ymin+1; sy <= ymax-1 ; sy++)
1667           {
1668             val += outer_factor * line_cache16[(sy-ymin) * image_info->image_width * image_info->channels + xmax];
1669             norm += outer_factor;
1670           }
1671         }
1672 
1673         if (ymin_flag) /* integrate over top margin */
1674         {
1675           for (sx = xmin+image_info->channels; sx <= xmax-image_info->channels ; sx += image_info->channels)
1676           {
1677             val += outer_factor * line_cache16[sx];
1678             norm += outer_factor;
1679           }
1680         }
1681 
1682         if (ymax_flag) /* integrate over bottom margin */
1683         {
1684           for (sx = xmin+image_info->channels; sx <= xmax-image_info->channels ; sx += image_info->channels)
1685           {
1686             val += outer_factor * line_cache16[(ymax-ymin) * image_info->image_width * image_info->channels + sx];
1687             norm += outer_factor;
1688           }
1689         }
1690 
1691         for (sy = ymin; sy <= ymax; sy++) /* integrate internal square */
1692         {
1693           for (sx = xmin; sx <= xmax; sx+=image_info->channels)
1694           {
1695             val += line_cache16[(sy-ymin) * image_info->image_width * image_info->channels + sx];
1696             norm += 1.0;
1697           }
1698         }
1699 
1700         val16 = val / norm;
1701         fputc(bytes16[0], outfile); /* write bytes in machine byte order */
1702         fputc(bytes16[1], outfile);
1703       }
1704     }
1705 
1706     if (ferror(outfile))
1707     {
1708      char buf[TEXTBUFSIZE];
1709 
1710       snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno));
1711       DBG(DBG_error, "%s\n", buf);
1712       xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
1713       *cancel_save = 1;
1714      break;
1715     }
1716 
1717       /* reset values and norm factors */
1718 
1719     if ((y > intradius) && (y < image_info->image_height - intradius))
1720     {
1721       memcpy(line_cache, line_cache + image_info->image_width * image_info->channels * bytespp,
1722              image_info->image_width * image_info->channels * bytespp * 2 * intradius);
1723       bytes_read = fread(line_cache + image_info->image_width * image_info->channels * bytespp * 2 * intradius,
1724             image_info->image_width * image_info->channels * bytespp, 1, imagefile);
1725     }
1726   }
1727 
1728   fflush(outfile);
1729   free(line_cache);
1730 
1731  return 0;
1732 }
1733 
1734 /* ---------------------------------------------------------------------------------------------------------------------- */
1735 
1736 #if 0
1737 int xsane_save_blur_image(FILE *outfile, FILE *imagefile, Image_info *image_info, int radius, GtkProgressBar *progress_bar)
1738 {
1739  int x, y, sx, sy;
1740  int xmin, xmax;
1741  int ymin, ymax;
1742  int pos0;
1743  int val, count;
1744  unsigned char *line_cache;
1745  int bytespp = 1;
1746 
1747   if (image_info->depth > 8)
1748   {
1749     bytespp = 2;
1750   }
1751 
1752   pos0 = ftell(imagefile); /* mark position to skip header */
1753 
1754   xsane_write_pnm_header(outfile, image_info, 0);
1755 
1756   line_cache = malloc(image_info->image_width * image_info->channels * bytespp * (2 * radius + 1));
1757   if (!line_cache)
1758   {
1759     DBG(DBG_error, "xsane_blur_image: out of memory\n");
1760    return -1;
1761   }
1762 
1763   bytes_read = fread(line_cache, image_info->image_width * image_info->channels * bytespp, (2 * radius + 1), imagefile);
1764 
1765   for (y = 0; y < image_info->image_height; y++)
1766   {
1767     xsane_progress_bar_set_fraction(progress_bar, (float)  y / image_info->image_height);
1768 
1769     for (x = 0; x < image_info->image_width * image_info->channels; x++)
1770     {
1771       xmin = x - radius * image_info->channels;
1772       xmax = x + radius * image_info->channels;
1773 
1774       if (xmin < 0)
1775       {
1776         xmin = x % image_info->channels;
1777       }
1778 
1779       if (xmax > image_info->image_width * image_info->channels)
1780       {
1781         xmax = image_info->image_width * image_info->channels;
1782       }
1783 
1784       ymin = y - radius;
1785       ymax = y + radius;
1786 
1787       if (ymin < 0)
1788       {
1789         ymin = 0;
1790       }
1791 
1792       if (ymax > image_info->image_height)
1793       {
1794         ymax = image_info->image_height;
1795       }
1796 
1797       val = 0;
1798       count = 0;
1799 
1800       if (bytespp == 1)
1801       {
1802         for (sy = ymin; sy <= ymax; sy++)
1803         {
1804           for (sx = xmin; sx <= xmax; sx+=image_info->channels)
1805           {
1806             val += line_cache[(sy-ymin) * image_info->image_width * image_info->channels + sx];
1807             count++;
1808           }
1809         }
1810         fputc((char) (val/count), outfile);
1811       }
1812       else
1813       {
1814        guint16 *line_cache16 = (guint16 *) line_cache;
1815        guint16 val16;
1816        char *bytes16 = (char *) &val16;
1817 
1818         for (sy = ymin; sy <= ymax; sy++)
1819         {
1820           for (sx = xmin; sx <= xmax; sx+=image_info->channels)
1821           {
1822             val += line_cache16[(sy-ymin) * image_info->image_width * image_info->channels + sx];
1823             count++;
1824           }
1825         }
1826 
1827         val16 = val / count;
1828         fputc(bytes16[0], outfile); /* write bytes in machine byte order */
1829         fputc(bytes16[1], outfile);
1830       }
1831     }
1832 
1833     if ((y > radius) && (y < image_info->image_height - radius))
1834     {
1835       memcpy(line_cache, line_cache + image_info->image_width * image_info->channels * bytespp,
1836              image_info->image_width * image_info->channels * bytespp * 2 * radius);
1837       bytes_read = fread(line_cache + image_info->image_width * image_info->channels * bytespp * 2 * radius,
1838             image_info->image_width * image_info->channels * bytespp, 1, imagefile);
1839     }
1840   }
1841 
1842   fflush(outfile);
1843   free(line_cache);
1844 
1845  return 0;
1846 }
1847 #endif
1848 
1849 /* ---------------------------------------------------------------------------------------------------------------------- */
1850 
xsane_save_rotate_image(FILE * outfile,FILE * imagefile,Image_info * image_info,int rotation,GtkProgressBar * progress_bar,int * cancel_save)1851 int xsane_save_rotate_image(FILE *outfile, FILE *imagefile, Image_info *image_info, int rotation, GtkProgressBar *progress_bar, int *cancel_save)
1852 /* returns true if operation was cancelled */
1853 {
1854  int x, y, pos0, bytespp, i;
1855  int pixel_width  = image_info->image_width;
1856  int pixel_height = image_info->image_height;
1857  float resolution_x = image_info->resolution_x;
1858  float resolution_y = image_info->resolution_y;
1859 
1860 #ifdef HAVE_MMAP
1861  char *mmaped_imagefile = NULL;
1862 #endif
1863 
1864   DBG(DBG_proc, "xsane_save_rotate_image\n");
1865 
1866   *cancel_save = 0;
1867 
1868   pos0 = ftell(imagefile); /* mark position to skip header */
1869 
1870   bytespp = image_info->channels;
1871 
1872   if (image_info->depth > 8)
1873   {
1874     bytespp *= 2;
1875   }
1876 
1877   if (image_info->depth < 8) /* lineart images are expanded to grayscale until transformation is done */
1878   {
1879     image_info->depth = 8; /* so we have at least 8 bits/pixel here */
1880   }
1881 
1882 #ifdef HAVE_MMAP
1883   mmaped_imagefile = mmap(NULL, pixel_width * pixel_height * bytespp + pos0, PROT_READ, MAP_PRIVATE, fileno(imagefile), 0);
1884   if (mmaped_imagefile == (char *) -1) /* mmap failed */
1885   {
1886     DBG(DBG_info, "xsane_save_rotate_image: unable to memory map image file, using standard file access\n");
1887     mmaped_imagefile = NULL;
1888   }
1889   else
1890   {
1891     DBG(DBG_info, "xsane_save_rotate_image: using memory mapped image file\n");
1892   }
1893 #endif
1894 
1895   switch (rotation)
1896   {
1897     default:
1898      break;
1899 
1900     case 0: /* 0 degree */
1901       xsane_write_pnm_header(outfile, image_info, 0);
1902 
1903       for (y = 0; y < pixel_height; y++)
1904       {
1905         xsane_progress_bar_set_fraction(progress_bar, (float) y / pixel_height);
1906 
1907         for (x = 0; x < pixel_width; x++)
1908         {
1909 #ifdef HAVE_MMAP
1910           if (mmaped_imagefile)
1911           {
1912            char *p = mmaped_imagefile + pos0 + bytespp * (x + y * pixel_width);  /* calculate correct position */
1913 
1914             for (i=0; i<bytespp; i++)
1915             {
1916               fputc(*p++, outfile);
1917             }
1918           }
1919           else
1920 #endif
1921           {
1922             for (i = 0; i < bytespp; i++)
1923             {
1924               fputc(fgetc(imagefile), outfile);
1925             }
1926           }
1927         }
1928 
1929         if (ferror(outfile))
1930         {
1931          char buf[TEXTBUFSIZE];
1932 
1933           snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno));
1934           DBG(DBG_error, "%s\n", buf);
1935           xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
1936           *cancel_save = 1;
1937          break;
1938         }
1939 
1940         if (*cancel_save)
1941         {
1942           break;
1943         }
1944       }
1945      break;
1946 
1947     case 1: /* 90 degree */
1948       image_info->image_width  = pixel_height;
1949       image_info->image_height = pixel_width;
1950 
1951       image_info->resolution_x = resolution_y;
1952       image_info->resolution_y = resolution_x;
1953 
1954       xsane_write_pnm_header(outfile, image_info, 0);
1955 
1956       for (x=0; x<pixel_width; x++)
1957       {
1958         xsane_progress_bar_set_fraction(progress_bar, (float) x / pixel_width);
1959 
1960         for (y=pixel_height-1; y>=0; y--)
1961         {
1962 #ifdef HAVE_MMAP
1963           if (mmaped_imagefile)
1964           {
1965            char *p = mmaped_imagefile + pos0 + bytespp * (x + y * pixel_width); /* calculate correct position */
1966 
1967             for (i=0; i<bytespp; i++)
1968             {
1969               fputc(*p++, outfile);
1970             }
1971           }
1972           else
1973 #endif
1974           {
1975             fseek(imagefile, pos0 + bytespp * (x + y * pixel_width), SEEK_SET); /* go to the correct position */
1976             for (i=0; i<bytespp; i++)
1977             {
1978               fputc(fgetc(imagefile), outfile);
1979             }
1980           }
1981         }
1982 
1983 
1984         if (ferror(outfile))
1985         {
1986          char buf[TEXTBUFSIZE];
1987 
1988           snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno));
1989           DBG(DBG_error, "%s\n", buf);
1990           xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
1991           *cancel_save = 1;
1992          break;
1993         }
1994 
1995         if (*cancel_save)
1996         {
1997           break;
1998         }
1999       }
2000 
2001      break;
2002 
2003     case 2: /* 180 degree */
2004       xsane_write_pnm_header(outfile, image_info, 0);
2005 
2006       for (y = pixel_height-1; y >= 0; y--)
2007       {
2008         xsane_progress_bar_set_fraction(progress_bar, (float) (pixel_height - y) / pixel_height);
2009 
2010         for (x = pixel_width-1; x >= 0; x--)
2011         {
2012 #ifdef HAVE_MMAP
2013           if (mmaped_imagefile)
2014           {
2015            char *p = mmaped_imagefile + pos0 + bytespp * (x + y * pixel_width);  /* calculate correct position */
2016 
2017             for (i = 0; i < bytespp; i++)
2018             {
2019               fputc(*p++, outfile);
2020             }
2021           }
2022           else
2023 #endif
2024           {
2025             fseek(imagefile, pos0 + bytespp * (x + y * pixel_width), SEEK_SET); /* go to the correct position */
2026             for (i = 0; i < bytespp; i++)
2027             {
2028               fputc(fgetc(imagefile), outfile);
2029             }
2030           }
2031         }
2032 
2033 
2034         if (ferror(outfile))
2035         {
2036          char buf[TEXTBUFSIZE];
2037 
2038           snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno));
2039           DBG(DBG_error, "%s\n", buf);
2040           xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
2041           *cancel_save = 1;
2042          break;
2043         }
2044 
2045         if (*cancel_save)
2046         {
2047           break;
2048         }
2049       }
2050      break;
2051 
2052     case 3: /* 270 degree */
2053       image_info->image_width  = pixel_height;
2054       image_info->image_height = pixel_width;
2055 
2056       image_info->resolution_x = resolution_y;
2057       image_info->resolution_y = resolution_x;
2058 
2059       xsane_write_pnm_header(outfile, image_info, 0);
2060 
2061       for (x = pixel_width-1; x >= 0; x--)
2062       {
2063         xsane_progress_bar_set_fraction(progress_bar, (float) (pixel_width - x) / pixel_width);
2064 
2065         for (y = 0; y < pixel_height; y++)
2066         {
2067 #ifdef HAVE_MMAP
2068           if (mmaped_imagefile)
2069           {
2070            char *p = mmaped_imagefile + pos0 + bytespp * (x + y * pixel_width);  /* calculate correct position */
2071 
2072             for (i = 0; i < bytespp; i++)
2073             {
2074               fputc(*p++, outfile);
2075             }
2076           }
2077           else
2078 #endif
2079           {
2080             fseek(imagefile, pos0 + bytespp * (x + y * pixel_width), SEEK_SET); /* go to the correct position */
2081             for (i = 0; i < bytespp; i++)
2082             {
2083               fputc(fgetc(imagefile), outfile);
2084             }
2085           }
2086         }
2087 
2088 
2089         if (ferror(outfile))
2090         {
2091          char buf[TEXTBUFSIZE];
2092 
2093           snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno));
2094           DBG(DBG_error, "%s\n", buf);
2095           xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
2096           *cancel_save = 1;
2097          break;
2098         }
2099 
2100         if (*cancel_save)
2101         {
2102           break;
2103         }
2104       }
2105      break;
2106 
2107     case 4: /* 0 degree, x mirror */
2108       xsane_write_pnm_header(outfile, image_info, 0);
2109 
2110       for (y = 0; y < pixel_height; y++)
2111       {
2112         xsane_progress_bar_set_fraction(progress_bar, (float) y / pixel_height);
2113 
2114         for (x = pixel_width-1; x >= 0; x--)
2115         {
2116 #ifdef HAVE_MMAP
2117           if (mmaped_imagefile)
2118           {
2119            char *p = mmaped_imagefile + pos0 + bytespp * (x + y * pixel_width);  /* calculate correct position */
2120 
2121             for (i = 0; i < bytespp; i++)
2122             {
2123               fputc(*p++, outfile);
2124             }
2125           }
2126           else
2127 #endif
2128           {
2129             fseek(imagefile, pos0 + bytespp * (x + y * pixel_width), SEEK_SET); /* go to the correct position */
2130             for (i = 0; i < bytespp; i++)
2131             {
2132               fputc(fgetc(imagefile), outfile);
2133             }
2134           }
2135         }
2136 
2137 
2138         if (ferror(outfile))
2139         {
2140          char buf[TEXTBUFSIZE];
2141 
2142           snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno));
2143           DBG(DBG_error, "%s\n", buf);
2144           xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
2145           *cancel_save = 1;
2146          break;
2147         }
2148 
2149         if (*cancel_save)
2150         {
2151           break;
2152         }
2153       }
2154      break;
2155 
2156     case 5: /* 90 degree, x mirror */
2157       image_info->image_width  = pixel_height;
2158       image_info->image_height = pixel_width;
2159 
2160       image_info->resolution_x = resolution_y;
2161       image_info->resolution_y = resolution_x;
2162 
2163       xsane_write_pnm_header(outfile, image_info, 0);
2164 
2165       for (x = 0; x < pixel_width; x++)
2166       {
2167         xsane_progress_bar_set_fraction(progress_bar, (float) x / pixel_width);
2168 
2169         for (y = 0; y < pixel_height; y++)
2170         {
2171 #ifdef HAVE_MMAP
2172           if (mmaped_imagefile)
2173           {
2174            char *p = mmaped_imagefile + pos0 + bytespp * (x + y * pixel_width); /* calculate correct position */
2175 
2176             for (i=0; i<bytespp; i++)
2177             {
2178               fputc(*p++, outfile);
2179             }
2180           }
2181           else
2182 #endif
2183           {
2184             fseek(imagefile, pos0 + bytespp * (x + y * pixel_width), SEEK_SET); /* go to the correct position */
2185             for (i = 0; i < bytespp; i++)
2186             {
2187               fputc(fgetc(imagefile), outfile);
2188             }
2189           }
2190         }
2191 
2192 
2193         if (ferror(outfile))
2194         {
2195          char buf[TEXTBUFSIZE];
2196 
2197           snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno));
2198           DBG(DBG_error, "%s\n", buf);
2199           xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
2200           *cancel_save = 1;
2201          break;
2202         }
2203 
2204         if (*cancel_save)
2205         {
2206           break;
2207         }
2208       }
2209 
2210      break;
2211 
2212     case 6: /* 180 degree, x mirror */
2213       xsane_write_pnm_header(outfile, image_info, 0);
2214 
2215       for (y = pixel_height-1; y >= 0; y--)
2216       {
2217         xsane_progress_bar_set_fraction(progress_bar, (float) (pixel_height - y) / pixel_height);
2218 
2219         for (x = 0; x < pixel_width; x++)
2220         {
2221 #ifdef HAVE_MMAP
2222           if (mmaped_imagefile)
2223           {
2224            char *p = mmaped_imagefile + pos0 + bytespp * (x + y * pixel_width);  /* calculate correct position */
2225 
2226             for (i = 0; i < bytespp; i++)
2227             {
2228               fputc(*p++, outfile);
2229             }
2230           }
2231           else
2232 #endif
2233           {
2234             fseek(imagefile, pos0 + bytespp * (x + y * pixel_width), SEEK_SET); /* go to the correct position */
2235             for (i = 0; i < bytespp; i++)
2236             {
2237               fputc(fgetc(imagefile), outfile);
2238             }
2239           }
2240         }
2241 
2242 
2243         if (ferror(outfile))
2244         {
2245          char buf[TEXTBUFSIZE];
2246 
2247           snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno));
2248           DBG(DBG_error, "%s\n", buf);
2249           xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
2250           *cancel_save = 1;
2251          break;
2252         }
2253 
2254         if (*cancel_save)
2255         {
2256           break;
2257         }
2258       }
2259      break;
2260 
2261     case 7: /* 270 degree, x mirror */
2262       image_info->image_width  = pixel_height;
2263       image_info->image_height = pixel_width;
2264 
2265       image_info->resolution_x = resolution_y;
2266       image_info->resolution_y = resolution_x;
2267 
2268       xsane_write_pnm_header(outfile, image_info, 0);
2269 
2270       for (x = pixel_width-1; x >= 0; x--)
2271       {
2272         xsane_progress_bar_set_fraction(progress_bar, (float) (pixel_width - x) / pixel_width);
2273 
2274         for (y = pixel_height-1; y >= 0; y--)
2275         {
2276 #ifdef HAVE_MMAP
2277           if (mmaped_imagefile)
2278           {
2279            char *p = mmaped_imagefile + pos0 + bytespp * (x + y * pixel_width);  /* calculate correct position */
2280 
2281             for (i = 0; i < bytespp; i++)
2282             {
2283               fputc(*p++, outfile);
2284             }
2285           }
2286           else
2287 #endif
2288           {
2289             fseek(imagefile, pos0 + bytespp * (x + y * pixel_width), SEEK_SET); /* go to the correct position */
2290             for (i = 0; i < bytespp; i++)
2291             {
2292               fputc(fgetc(imagefile), outfile);
2293             }
2294           }
2295         }
2296 
2297 
2298         if (ferror(outfile))
2299         {
2300          char buf[TEXTBUFSIZE];
2301 
2302           snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno));
2303           DBG(DBG_error, "%s\n", buf);
2304           xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
2305           *cancel_save = 1;
2306          break;
2307         }
2308 
2309         if (*cancel_save)
2310         {
2311           break;
2312         }
2313       }
2314      break;
2315   }
2316 
2317 #ifdef HAVE_MMAP
2318   if (mmaped_imagefile)
2319   {
2320     munmap(mmaped_imagefile, pos0 + pixel_width * pixel_height * bytespp);
2321   }
2322 #endif
2323 
2324   fflush(outfile);
2325 
2326   return (*cancel_save);
2327 }
2328 
2329 /* ---------------------------------------------------------------------------------------------------------------------- */
2330 
xsane_save_ps_create_document_header(FILE * outfile,int pages,int paper_left_margin,int paper_bottom_margin,int paper_width,int paper_height,int paper_orientation,int flatedecode)2331 void xsane_save_ps_create_document_header(FILE *outfile, int pages,
2332                                           int paper_left_margin, int paper_bottom_margin,
2333                                           int paper_width, int paper_height,
2334                                           int paper_orientation, int flatedecode)
2335 {
2336  int box_left, box_bottom, box_right, box_top;
2337 
2338   DBG(DBG_proc, "xsane_save_ps_create_document_header\n");
2339 
2340   if (paper_orientation >= 8) /* rotate with 90 degrees - landscape mode */
2341   {
2342     box_left        = paper_width - paper_left_margin - paper_height;
2343     box_bottom      = paper_bottom_margin;
2344     box_right       = box_left   + ceil(paper_height);
2345     box_top         = box_bottom + ceil(paper_width);
2346   }
2347   else /* do not rotate, portrait mode */
2348   {
2349     box_left        = paper_left_margin;
2350     box_bottom      = paper_bottom_margin;
2351     box_right       = box_left   + ceil(paper_width);
2352     box_top         = box_bottom + ceil(paper_height);
2353   }
2354 
2355   fprintf(outfile, "%%!PS-Adobe-3.0\n");
2356   fprintf(outfile, "%%%%Creator: XSane version %s (sane %d.%d) - by Oliver Rauch\n", VERSION,
2357                         SANE_VERSION_MAJOR(xsane.sane_backend_versioncode),
2358                         SANE_VERSION_MINOR(xsane.sane_backend_versioncode));
2359   fprintf(outfile, "%%%%DocumentData: Clean7Bit\n");
2360   if (flatedecode)
2361   {
2362     fprintf(outfile, "%%%%LanguageLevel: 3\n");
2363   }
2364   else
2365   {
2366     fprintf(outfile, "%%%%LanguageLevel: 2\n");
2367   }
2368 
2369   fprintf(outfile, "%%%%BoundingBox: %d %d %d %d\n", box_left, box_bottom, box_right, box_top);
2370 
2371   if (pages)
2372   {
2373     fprintf(outfile, "%%%%Pages: %d\n", pages);
2374   }
2375   else
2376   {
2377     fprintf(outfile, "%%%%Pages: (atend)\n");
2378   }
2379 
2380   fprintf(outfile, "%%%%EndComments\n");
2381   fprintf(outfile, "%%%%BeginDocument: xsane.ps\n");
2382   fprintf(outfile, "\n");
2383 /*  fprintf(outfile, "/origstate save def\n"); */
2384   fprintf(outfile, "20 dict begin\n");
2385 }
2386 
2387 /* ---------------------------------------------------------------------------------------------------------------------- */
2388 
xsane_save_ps_create_document_trailer(FILE * outfile,int pages)2389 void xsane_save_ps_create_document_trailer(FILE *outfile, int pages)
2390 {
2391   DBG(DBG_proc, "xsane_save_ps_create_document_trailer\n");
2392 
2393   fprintf(outfile, "end\n");
2394 /*  fprintf(outfile, "origstate restore\n"); */
2395 
2396   if (pages)
2397   {
2398     fprintf(outfile, "%%%%Trailer\n");
2399     fprintf(outfile, "%%%%Pages: %d\n", pages);
2400   }
2401 
2402   fprintf(outfile, "%%%%EOF\n");
2403   fprintf(outfile, "%%%%EndDocument\n");
2404   fprintf(outfile, "\n");
2405 
2406 }
2407 
2408 /* ---------------------------------------------------------------------------------------------------------------------- */
2409 
xsane_save_ps_create_image_header(FILE * outfile,Image_info * image_info,float width,float height,int degree,int position_left,int position_bottom,int box_left,int box_bottom,int box_right,int box_top,int flatedecode)2410 static void xsane_save_ps_create_image_header(FILE *outfile,
2411                                               Image_info *image_info,
2412                                               float width, float height,
2413                                               int degree, int position_left, int position_bottom,
2414                                               int box_left, int box_bottom, int box_right, int box_top,
2415                                               int flatedecode)
2416 {
2417  int depth;
2418 
2419   depth = image_info->depth;
2420 
2421   if (depth > 8)
2422   {
2423     depth = 12;
2424   }
2425 
2426   if (depth == 1)
2427   {
2428     fprintf(outfile, "/grays %d string def\n", image_info->image_width);
2429     fprintf(outfile, "/npixels 0 def\n");
2430     fprintf(outfile, "/rgbindx 0 def\n");
2431   }
2432 
2433   fprintf(outfile, "%d rotate\n", degree);
2434   fprintf(outfile, "%d %d translate\n", position_left, position_bottom);
2435   fprintf(outfile, "%f %f scale\n", width, height);
2436 
2437   fprintf(outfile, "<<\n");
2438   fprintf(outfile, " /ImageType 1\n");
2439   fprintf(outfile, " /Width %d\n", image_info->image_width);
2440   fprintf(outfile, " /Height %d\n", image_info->image_height);
2441   fprintf(outfile, " /BitsPerComponent %d\n", depth);
2442   if (image_info->channels == 3)
2443   {
2444     fprintf(outfile, " /Decode [0 1 0 1 0 1]\n");
2445   }
2446   else
2447   {
2448     fprintf(outfile, " /Decode [0 1]\n");
2449   }
2450   fprintf(outfile, " /ImageMatrix [%d %d %d %d %d %d]\n", image_info->image_width, 0, 0, -image_info->image_height, 0, image_info->image_height);
2451   fprintf(outfile, " /DataSource currentfile /ASCII85Decode filter");
2452 #ifdef HAVE_LIBZ
2453   if (flatedecode)
2454   {
2455     fprintf(outfile, " /FlateDecode filter");
2456   }
2457 #endif
2458   fprintf(outfile, "\n");
2459   fprintf(outfile, ">>\n");
2460   {
2461     fprintf(outfile, "image\n");
2462     fprintf(outfile, "\n");
2463   }
2464 }
2465 
2466 /* ---------------------------------------------------------------------------------------------------------------------- */
2467 
xsane_save_ps_create_page_trailer(FILE * outfile)2468 static void xsane_save_ps_create_page_trailer(FILE *outfile)
2469 {
2470   fprintf(outfile, "\n");
2471   fprintf(outfile, "showpage\n");
2472   fprintf(outfile, "%%%%PageTrailer\n");
2473 }
2474 
2475 /* ---------------------------------------------------------------------------------------------------------------------- */
2476 
2477 #ifdef HAVE_LIBZ
2478 /* Utility function for the PDF output */
xsane_write_flatedecode(FILE * outfile,unsigned char * line,int len,int finish)2479 static int xsane_write_flatedecode(FILE *outfile, unsigned char *line, int len, int finish)
2480 {
2481   static unsigned char *cbuf = NULL;
2482   static int cbuflen = 0;
2483   static int linelen = 0;
2484   int outlen;
2485   static int init = 0;
2486   static z_stream s;
2487   int ret;
2488   int flush;
2489   static int count = 0;
2490 
2491   DBG(DBG_proc, "xsane_write_flatedecode\n");
2492 
2493   if (linelen != len)
2494   {
2495     linelen = len;
2496     if (cbuf != NULL)
2497     {
2498       free(cbuf);
2499     }
2500     /* buffer length = length + 0.1 * length + 12 (mandatory) */
2501     cbuflen = len + len / 10 + 12;
2502     cbuf = malloc(cbuflen);
2503   }
2504 
2505   if (cbuf == NULL)
2506   {
2507     DBG(DBG_error, "cbuf allocation failed\n");
2508    return 1;
2509   }
2510 
2511   if (!init)
2512   {
2513     s.zalloc = Z_NULL;
2514     s.zfree = Z_NULL;
2515     s.opaque = Z_NULL;
2516 
2517     ret = deflateInit(&s, Z_DEFAULT_COMPRESSION);
2518 
2519     if (ret != Z_OK)
2520     {
2521       DBG(DBG_error, "deflateInit failed\n");
2522       free(cbuf);
2523      return 1;
2524     }
2525 
2526     init = 1;
2527   }
2528 
2529   s.avail_in = len;
2530   s.next_in = line;
2531 
2532   do
2533   {
2534     s.avail_out = cbuflen;
2535     s.next_out = cbuf;
2536 
2537     flush = (finish) ? Z_FINISH : Z_NO_FLUSH;
2538 
2539     ret = deflate(&s, flush);
2540 
2541     if (ret == Z_STREAM_ERROR)
2542     {
2543       DBG(DBG_error, "deflate failed\n");
2544       free(cbuf);
2545      return 1;
2546     }
2547 
2548     outlen = cbuflen - s.avail_out;
2549 
2550     fwrite(cbuf, outlen, 1, outfile);
2551   } while (s.avail_out == 0);
2552 
2553   if (finish)
2554   {
2555     DBG(DBG_info, "xsane_write_flatedecode finished\n");
2556     deflateEnd(&s);
2557     free(cbuf);
2558     cbuf = NULL;
2559     init = 0;
2560     cbuflen = 0;
2561     linelen = 0;
2562     count = 0;
2563   }
2564 
2565  return 0;
2566 }
2567 #endif
2568 
2569 /* ---------------------------------------------------------------------------------------------------------------------- */
2570 
2571 #ifdef HAVE_LIBZ
2572 /* Utility function for the PostScript output */
xsane_write_compressed_a85_flatedecode(FILE * outfile,unsigned char * line,int len,int finish)2573 static int xsane_write_compressed_a85_flatedecode(FILE *outfile, unsigned char *line, int len, int finish)
2574 {
2575   static unsigned char *cbuf = NULL;
2576   static int cbuflen = 0;
2577   static int linelen = 0;
2578   int i, j;
2579   int outlen;
2580   static int init = 0;
2581   static z_stream s;
2582   int ret;
2583   int flush;
2584   static int a85count = 0;
2585   static guint32 a85tuple = 0;
2586   static unsigned char a85block[6] = {0, 0, 0, 0, 0, 0};
2587   static int count = 0;
2588 
2589   DBG(DBG_proc, "xsane_write_compressed_a85_flatedecode\n");
2590 
2591   if (linelen != len)
2592   {
2593     linelen = len;
2594     if (cbuf != NULL)
2595     {
2596       free(cbuf);
2597     }
2598     /* buffer length = length + 0.1 * length + 12 (mandatory) */
2599     cbuflen = len + len / 10 + 12;
2600     cbuf = malloc(cbuflen);
2601   }
2602 
2603   if (cbuf == NULL)
2604   {
2605     DBG(DBG_error, "cbuf allocation failed\n");
2606    return 1;
2607   }
2608 
2609   if (!init)
2610   {
2611     s.zalloc = Z_NULL;
2612     s.zfree = Z_NULL;
2613     s.opaque = Z_NULL;
2614 
2615     ret = deflateInit(&s, Z_DEFAULT_COMPRESSION);
2616 
2617     if (ret != Z_OK)
2618     {
2619       DBG(DBG_error, "deflateInit failed\n");
2620       free(cbuf);
2621      return 1;
2622     }
2623 
2624     init = 1;
2625   }
2626 
2627   s.avail_in = len;
2628   s.next_in = line;
2629 
2630   do
2631   {
2632     s.avail_out = cbuflen;
2633     s.next_out = cbuf;
2634 
2635     flush = (finish) ? Z_FINISH : Z_NO_FLUSH;
2636 
2637     ret = deflate(&s, flush);
2638 
2639     if (ret == Z_STREAM_ERROR)
2640     {
2641       DBG(DBG_error, "deflate failed\n");
2642       free(cbuf);
2643      return 1;
2644     }
2645 
2646     outlen = cbuflen - s.avail_out;
2647 
2648     /* ASCII85 (base 85) encoding */
2649     for (i = 0; i < outlen; i++)
2650     {
2651       switch (a85count)
2652       {
2653         case 0:
2654           a85tuple |= (cbuf[i] << 24);
2655           a85count++;
2656          break;
2657 
2658         case 1:
2659           a85tuple |= (cbuf[i] << 16);
2660           a85count++;
2661          break;
2662 
2663         case 2:
2664           a85tuple |= (cbuf[i] << 8);
2665           a85count++;
2666          break;
2667 
2668         case 3:
2669           a85tuple |= (cbuf[i] << 0);
2670 
2671           if (count == 40)
2672           {
2673             fprintf(outfile, "\n");
2674             count = 0;
2675           }
2676 
2677           if (a85tuple == 0)
2678           {
2679             fprintf(outfile, "z");
2680             count++;
2681           }
2682           else
2683           {
2684             /* The ASCII chars must be written in reverse order, hence -> a85block[4-j] */
2685             for (j = 0; j < 5; j++)
2686             {
2687               a85block[4-j] = a85tuple % 85 + '!';
2688               a85tuple /= 85;
2689             }
2690 
2691             for (j = 0; j < 5; j++)
2692             {
2693               fprintf(outfile, "%c", a85block[j]);
2694               count++;
2695               if (count == 40)
2696               {
2697                 fprintf(outfile, "\n");
2698                 count = 0;
2699               }
2700             }
2701           }
2702 
2703           a85count = 0;
2704           a85tuple = 0;
2705          break;
2706 
2707         default:
2708          break;
2709       }
2710     }
2711   } while (s.avail_out == 0);
2712 
2713   if (finish)
2714   {
2715     DBG(DBG_info, "finish\n");
2716     if (a85count > 0)
2717     {
2718       a85count++;
2719       for (j = 0; j <= a85count; j++)
2720       {
2721         a85block[j] = a85tuple % 85 + '!';
2722         a85tuple /= 85;
2723       }
2724       /* Reverse order */
2725       for (j--; j > 0; j--)
2726       {
2727         if (count == 40)
2728         {
2729           fprintf(outfile, "\n");
2730           count = 0;
2731         }
2732         fprintf(outfile, "%c", a85block[j]);
2733         count++;
2734       }
2735     }
2736 
2737     /* ASCII85 EOD marker + newline*/
2738     if (count + 2 > 40)
2739     {
2740       fprintf(outfile, "\n");
2741     }
2742     fprintf(outfile, "~>\n");
2743     deflateEnd(&s);
2744     free(cbuf);
2745     cbuf = NULL;
2746     init = 0;
2747     a85tuple = 0;
2748     a85count = 0;
2749     cbuflen = 0;
2750     linelen = 0;
2751     count = 0;
2752   }
2753 
2754  return 0;
2755 }
2756 #endif
2757 
2758 /* ---------------------------------------------------------------------------------------------------------------------- */
2759 
2760 /* Utility function for the PostScript output */
xsane_write_compressed_a85(FILE * outfile,unsigned char * line,int len,int finish)2761 static int xsane_write_compressed_a85(FILE *outfile, unsigned char *line, int len, int finish)
2762 {
2763   static unsigned char *cbuf = NULL;
2764   static int cbuflen = 0;
2765   static int linelen = 0;
2766   int i, j;
2767   int outlen;
2768   static int a85count = 0;
2769   static guint32 a85tuple = 0;
2770   static unsigned char a85block[6] = {0, 0, 0, 0, 0, 0};
2771   static int count = 0;
2772 
2773   DBG(DBG_proc, "xsane_write_compressed_a85\n");
2774 
2775     cbuf = line;
2776     outlen = len;
2777 
2778     /* ASCII85 (base 85) encoding */
2779     for (i = 0; i < outlen; i++)
2780     {
2781       switch (a85count)
2782       {
2783         case 0:
2784           a85tuple |= (cbuf[i] << 24);
2785           a85count++;
2786          break;
2787 
2788         case 1:
2789           a85tuple |= (cbuf[i] << 16);
2790           a85count++;
2791          break;
2792 
2793         case 2:
2794           a85tuple |= (cbuf[i] << 8);
2795           a85count++;
2796          break;
2797 
2798         case 3:
2799           a85tuple |= (cbuf[i] << 0);
2800 
2801           if (count == 40)
2802           {
2803             fprintf(outfile, "\n");
2804             count = 0;
2805           }
2806 
2807           if (a85tuple == 0)
2808           {
2809             fprintf(outfile, "z");
2810             count++;
2811           }
2812           else
2813           {
2814             /* The ASCII chars must be written in reverse order, hence -> a85block[4-j] */
2815             for (j = 0; j < 5; j++)
2816             {
2817               a85block[4-j] = a85tuple % 85 + '!';
2818               a85tuple /= 85;
2819             }
2820 
2821             for (j = 0; j < 5; j++)
2822             {
2823               fprintf(outfile, "%c", a85block[j]);
2824               count++;
2825               if (count == 40)
2826               {
2827                 fprintf(outfile, "\n");
2828                 count = 0;
2829               }
2830             }
2831           }
2832 
2833           a85count = 0;
2834           a85tuple = 0;
2835          break;
2836 
2837         default:
2838          break;
2839       }
2840     }
2841 
2842   if (finish)
2843   {
2844     DBG(DBG_info, "finish\n");
2845     if (a85count > 0)
2846     {
2847       a85count++;
2848       for (j = 0; j <= a85count; j++)
2849       {
2850         a85block[j] = a85tuple % 85 + '!';
2851         a85tuple /= 85;
2852       }
2853       /* Reverse order */
2854       for (j--; j > 0; j--)
2855       {
2856         if (count == 40)
2857         {
2858           fprintf(outfile, "\n");
2859           count = 0;
2860         }
2861         fprintf(outfile, "%c", a85block[j]);
2862         count++;
2863       }
2864     }
2865 
2866     /* ASCII85 EOD marker + newline*/
2867     if (count + 2 > 40)
2868     {
2869       fprintf(outfile, "\n");
2870     }
2871     fprintf(outfile, "~>\n");
2872     a85tuple = 0;
2873     a85count = 0;
2874     cbuflen = 0;
2875     linelen = 0;
2876     count = 0;
2877   }
2878 
2879  return 0;
2880 }
2881 
2882 /* ---------------------------------------------------------------------------------------------------------------------- */
2883 
2884 #ifdef HAVE_LIBLCMS
xsane_write_CSA(FILE * outfile,char * input_profile,int intent)2885 static int xsane_write_CSA(FILE *outfile, char *input_profile, int intent)
2886 {
2887  cmsHPROFILE hProfile;
2888  size_t n;
2889  char* buffer;
2890 
2891   hProfile = cmsOpenProfileFromFile(input_profile, "r");
2892   if (!hProfile)
2893   {
2894     return -1;
2895   }
2896 
2897   n = cmsGetPostScriptCSA(hProfile, intent, NULL, 0);
2898   if (n == 0)
2899   {
2900     return -2;
2901   }
2902 
2903   buffer = (char*) malloc(n + 1);
2904   if (!buffer)
2905   {
2906     return -3;
2907   }
2908 
2909   cmsGetPostScriptCSA(hProfile, intent, buffer, n);
2910   buffer[n] = 0;
2911 
2912   fprintf(outfile, "%s", buffer);
2913   fprintf(outfile, "setcolorspace\n");
2914 
2915   free(buffer);
2916   cmsCloseProfile(hProfile);
2917 
2918  return 0;
2919 }
2920 
2921 /* ---------------------------------------------------------------------------------------------------------------------- */
2922 
xsane_write_CRD(FILE * outfile,char * output_profile,int intent,int cms_bpc)2923 static int xsane_write_CRD(FILE *outfile, char *output_profile, int intent, int cms_bpc)
2924 {
2925  cmsHPROFILE hProfile;
2926  size_t n;
2927  char* buffer;
2928  DWORD flags = cmsFLAGS_NODEFAULTRESOURCEDEF;
2929 
2930   hProfile = cmsOpenProfileFromFile(output_profile, "r");
2931   if (!hProfile)
2932   {
2933     return -1;
2934   }
2935 
2936   if (cms_bpc)
2937   {
2938     flags |= cmsFLAGS_BLACKPOINTCOMPENSATION;
2939   }
2940 
2941   n = cmsGetPostScriptCRDEx(hProfile, intent, flags, NULL, 0);
2942   if (n == 0)
2943   {
2944     return -2;
2945   }
2946 
2947   buffer = (char*) malloc(n + 1);
2948   if (!buffer)
2949   {
2950     return -3;
2951   }
2952 
2953   cmsGetPostScriptCRDEx(hProfile, intent, flags, buffer, n);
2954   buffer[n] = 0;
2955 
2956   fprintf(outfile, "%s", buffer);
2957   fprintf(outfile, "setcolorrendering\n");
2958 
2959   free(buffer);
2960   cmsCloseProfile(hProfile);
2961 
2962  return 0;
2963 }
2964 #endif
2965 
2966 /* ---------------------------------------------------------------------------------------------------------------------- */
2967 
xsane_save_ps_pdf_bw(FILE * outfile,FILE * imagefile,Image_info * image_info,int ascii85decode,int flatedecode,GtkProgressBar * progress_bar,int * cancel_save)2968 static int xsane_save_ps_pdf_bw(FILE *outfile, FILE *imagefile, Image_info *image_info, int ascii85decode, int flatedecode, GtkProgressBar *progress_bar, int *cancel_save)
2969 {
2970  int x, y;
2971  int bytes_per_line = (image_info->image_width+7)/8;
2972  int ret = 0;
2973  unsigned char *line;
2974 
2975   DBG(DBG_proc, "xsane_save_ps_pdf_bw\n");
2976 
2977   *cancel_save = 0;
2978 
2979   line = (unsigned char *) malloc(bytes_per_line);
2980 
2981   if (line == NULL)
2982   {
2983     char buf[TEXTBUFSIZE];
2984 
2985     snprintf(buf, sizeof(buf), "%s malloc failed", ERR_DURING_SAVE);
2986     DBG(DBG_error, "%s\n", buf);
2987     xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
2988     *cancel_save = 1;
2989    return (*cancel_save);
2990   }
2991 
2992   for (y = 0; y < image_info->image_height; y++)
2993   {
2994     xsane_progress_bar_set_fraction(progress_bar, (float) y / image_info->image_height);
2995 
2996     for (x = 0; x < bytes_per_line; x++)
2997     {
2998       line[x] = fgetc(imagefile) ^ 255;
2999     }
3000 
3001     if (ascii85decode)
3002     {
3003 #ifdef HAVE_LIBZ
3004       if (flatedecode)
3005       {
3006         ret = xsane_write_compressed_a85_flatedecode(outfile, line, bytes_per_line, (y == image_info->image_height - 1));
3007       }
3008       else
3009 #endif
3010       {
3011         ret = xsane_write_compressed_a85(outfile, line, bytes_per_line, (y == image_info->image_height - 1));
3012       }
3013     }
3014 #ifdef HAVE_LIBZ
3015     else if (flatedecode)
3016     {
3017       ret = xsane_write_flatedecode(outfile, line, bytes_per_line, (y == image_info->image_height - 1));
3018     }
3019 #endif
3020     else
3021     {
3022       fwrite(line, bytes_per_line, 1, outfile);
3023       ret = 0;
3024     }
3025 
3026     if ((ret != 0) || (ferror(outfile)))
3027     {
3028      char buf[TEXTBUFSIZE];
3029 
3030       if (ret == 0)
3031       {
3032         snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno));
3033       }
3034       else
3035       {
3036         snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_ZLIB);
3037       }
3038 
3039       DBG(DBG_error, "%s\n", buf);
3040       xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
3041       *cancel_save = 1;
3042 
3043      break;
3044     }
3045 
3046     if (*cancel_save)
3047     {
3048       break;
3049     }
3050   }
3051 
3052   free(line);
3053 
3054  return (*cancel_save);
3055 }
3056 
3057 /* ---------------------------------------------------------------------------------------------------------------------- */
3058 
xsane_save_ps_pdf_gray(FILE * outfile,FILE * imagefile,Image_info * image_info,int ascii85decode,int flatedecode,cmsHTRANSFORM hTransform,int do_transform,GtkProgressBar * progress_bar,int * cancel_save)3059 static int xsane_save_ps_pdf_gray(FILE *outfile, FILE *imagefile, Image_info *image_info, int ascii85decode, int flatedecode, cmsHTRANSFORM hTransform, int do_transform, GtkProgressBar *progress_bar, int *cancel_save)
3060 {
3061  int x, y;
3062  int ret = 0;
3063  unsigned char *line = NULL, *linep = NULL, *line16 = NULL;
3064  int bytes_per_line;
3065  int bytes_per_line16 = 0;
3066  size_t bytes_read;
3067 #ifdef HAVE_LIBLCMS
3068  unsigned char *line_raw = NULL;
3069 #endif
3070 
3071 
3072   DBG(DBG_proc, "xsane_save_ps_pdf_gray\n");
3073 
3074   *cancel_save = 0;
3075 
3076   if (image_info->depth > 8) /* reduce 16 bit images to 12 bit */
3077   {
3078     bytes_per_line16 = image_info->image_width * 2;
3079 
3080     bytes_per_line   = (image_info->image_width/2) * 3;
3081 
3082     if (image_info->image_width & 1)
3083     {
3084       bytes_per_line += 2;
3085     }
3086 
3087     DBG(DBG_info, "bytes_per_line16 = %d\n", bytes_per_line16);
3088     DBG(DBG_info, "bytes_per_line   = %d\n", bytes_per_line);
3089 
3090     line16 = (unsigned char *) malloc(bytes_per_line16);
3091 
3092     if (line16 == NULL)
3093     {
3094      char buf[TEXTBUFSIZE];
3095 
3096       snprintf(buf, sizeof(buf), "%s malloc for line16 failed", ERR_DURING_SAVE);
3097       DBG(DBG_error, "%s\n", buf);
3098       xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
3099       *cancel_save = 1;
3100      return (*cancel_save);
3101     }
3102     DBG(DBG_info, "line16 allocated\n");
3103   }
3104   else
3105   {
3106     bytes_per_line   = image_info->image_width;
3107     bytes_per_line16 = image_info->image_width;
3108     DBG(DBG_info, "bytes_per_line = %d\n", bytes_per_line);
3109   }
3110 
3111   line = (unsigned char *) malloc(bytes_per_line);
3112 
3113   if (line == NULL)
3114   {
3115    char buf[TEXTBUFSIZE];
3116 
3117     snprintf(buf, sizeof(buf), "%s malloc failed", ERR_DURING_SAVE);
3118     DBG(DBG_error, "%s\n", buf);
3119     xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
3120     *cancel_save = 1;
3121    return (*cancel_save);
3122   }
3123 
3124   DBG(DBG_info, "line allocated\n");
3125 
3126 #ifdef HAVE_LIBLCMS
3127   if (do_transform && (hTransform != NULL))
3128   {
3129     DBG(DBG_info, "Doing CMS color conversion\n");
3130 
3131     line_raw = (unsigned char *) malloc(bytes_per_line16);
3132 
3133     if (line_raw == NULL)
3134     {
3135      char buf[TEXTBUFSIZE];
3136 
3137       snprintf(buf, sizeof(buf), "%s malloc for line_raw failed", ERR_DURING_SAVE);
3138       DBG(DBG_error, "%s\n", buf);
3139       xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
3140 
3141       free(line);
3142 
3143       if (line16)
3144       {
3145         free(line16);
3146       }
3147 
3148       *cancel_save = 1;
3149      return (*cancel_save);
3150     }
3151 
3152     DBG(DBG_info, "line_raw allocated\n");
3153   }
3154 #endif
3155 
3156   for (y = 0; y < image_info->image_height; y++)
3157   {
3158     if (image_info->depth > 8) /* reduce 16 bit images */
3159     {
3160 #if 0
3161      guint16 val;
3162 
3163       for (x = 0; x < image_info->image_width; x++)
3164       {
3165         bytes_read = fread(&val, 2, 1, imagefile);
3166         line[x] = val/256;
3167       }
3168 #endif
3169 #if 1
3170 #ifdef HAVE_LIBLCMS
3171       if (do_transform && (hTransform != NULL))
3172       {
3173         bytes_read = fread(line_raw, 2, image_info->image_width, imagefile);
3174         cmsDoTransform(hTransform, line_raw, line16, image_info->image_width);
3175       }
3176       else
3177 #endif
3178       {
3179         bytes_read = fread(line16, 2, image_info->image_width, imagefile);
3180       }
3181 
3182       linep = line;
3183 
3184 #if __BYTE_ORDER == __LITTLE_ENDIAN
3185       for (x = 0; x < image_info->image_width; x=x+2)
3186       {
3187         *linep++ =   line16[2*x+1]; /* pixel0 high+middle */
3188 
3189         if (x == image_info->image_width-1)
3190         {
3191           *linep++ = (line16[2*x+0] & 240); /* pixel0 low */
3192           break;
3193         }
3194 
3195         *linep++ =  (line16[2*x+0] & 240)      |  (line16[2*x+3] >> 4); /* pixel0 low | pixel1 high */
3196         *linep++ = ((line16[2*x+3] & 15) << 4) | ((line16[2*x+2] & 240) >> 4); /* pixel1 middle | pixel1 low */
3197       }
3198 #else
3199       for (x = 0; x < image_info->image_width; x=x+2)
3200       {
3201         *linep++ =   line16[2*x+0]; /* pixel0 high+middle */
3202 
3203         if (x == image_info->image_width-1)
3204         {
3205           *linep++ = (line16[2*x+1] & 240); /* pixel0 low */
3206           break;
3207         }
3208 
3209         *linep++ =  (line16[2*x+1] & 240)      |  (line16[2*x+2] >> 4); /* pixel0 low | pixel1 high */
3210         *linep++ = ((line16[2*x+2] & 15) << 4) | ((line16[2*x+3] & 240) >> 4); /* pixel1 middle | pixel1 low */
3211       }
3212 #endif
3213 #endif
3214     }
3215     else /* 8 bits/sample */
3216     {
3217 #if 0
3218       for (x = 0; x < image_info->image_width; x++)
3219       {
3220         line[x] = fgetc(imagefile);
3221       }
3222 #endif
3223 #ifdef HAVE_LIBLCMS
3224       if (do_transform && (hTransform != NULL))
3225       {
3226         bytes_read = fread(line_raw, 1, image_info->image_width, imagefile);
3227         cmsDoTransform(hTransform, line_raw, line, image_info->image_width);
3228       }
3229       else
3230 #endif
3231       {
3232         bytes_read = fread(line, 1, image_info->image_width, imagefile);
3233       }
3234     }
3235 
3236     if (ascii85decode)
3237     {
3238 #ifdef HAVE_LIBZ
3239       if (flatedecode)
3240       {
3241         ret = xsane_write_compressed_a85_flatedecode(outfile, line, bytes_per_line, (y == image_info->image_height - 1));
3242       }
3243       else
3244 #endif
3245       {
3246         ret = xsane_write_compressed_a85(outfile, line, bytes_per_line, (y == image_info->image_height - 1));
3247       }
3248     }
3249 #ifdef HAVE_LIBZ
3250     else if (flatedecode)
3251     {
3252       ret = xsane_write_flatedecode(outfile, line, bytes_per_line, (y == image_info->image_height - 1));
3253     }
3254 #endif
3255     else
3256     {
3257       fwrite(line, bytes_per_line, 1, outfile);
3258       ret = 0;
3259     }
3260 
3261     if ((ret != 0) || (ferror(outfile)))
3262     {
3263      char buf[TEXTBUFSIZE];
3264 
3265       if (ret == 0)
3266       {
3267         snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno));
3268       }
3269       else
3270       {
3271         snprintf(buf, sizeof(buf), "%s zlib error or memory allocation problem", ERR_DURING_SAVE);
3272       }
3273 
3274       DBG(DBG_error, "%s\n", buf);
3275       xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
3276       *cancel_save = 1;
3277 
3278      break;
3279     }
3280 
3281     xsane_progress_bar_set_fraction(progress_bar, (float) y / image_info->image_height);
3282 
3283     if (*cancel_save)
3284     {
3285       break;
3286     }
3287   }
3288 
3289 #ifdef HAVE_LIBLCMS
3290   if (line_raw)
3291   {
3292     free(line_raw);
3293   }
3294 #endif
3295 
3296   if (line16)
3297   {
3298     free(line16);
3299   }
3300 
3301   free(line);
3302 
3303  return (*cancel_save);
3304 }
3305 
3306 /* ---------------------------------------------------------------------------------------------------------------------- */
3307 
xsane_save_ps_pdf_color(FILE * outfile,FILE * imagefile,Image_info * image_info,int ascii85decode,int flatedecode,cmsHTRANSFORM hTransform,int do_transform,GtkProgressBar * progress_bar,int * cancel_save)3308 static int xsane_save_ps_pdf_color(FILE *outfile, FILE *imagefile, Image_info *image_info, int ascii85decode, int flatedecode,
3309                                    cmsHTRANSFORM hTransform, int do_transform,
3310                                    GtkProgressBar *progress_bar, int *cancel_save)
3311 {
3312  int x, y;
3313  int ret = 0;
3314  unsigned char *line = NULL, *linep = NULL, *line16 = NULL;
3315  int bytes_per_line;
3316  int bytes_per_line16 = 0;
3317  size_t bytes_read;
3318 #ifdef HAVE_LIBLCMS
3319  unsigned char *line_raw = NULL;
3320 #endif
3321 
3322   DBG(DBG_proc, "xsane_save_ps_pdf_color\n");
3323 
3324   *cancel_save = 0;
3325 
3326   if (image_info->depth > 8) /* reduce 16 bit images to 12 bit */
3327   {
3328     bytes_per_line16 = image_info->image_width * 3 * 2;
3329 
3330     bytes_per_line   = (image_info->image_width/2) * 3 * 3;
3331     if (image_info->image_width & 1)
3332     {
3333       bytes_per_line += 5;
3334     }
3335 
3336     DBG(DBG_info, "bytes_per_line16 = %d\n", bytes_per_line16);
3337     DBG(DBG_info, "bytes_per_line   = %d\n", bytes_per_line);
3338 
3339     line16 = (unsigned char *) malloc(bytes_per_line16);
3340 
3341     if (line16 == NULL)
3342     {
3343      char buf[TEXTBUFSIZE];
3344 
3345       snprintf(buf, sizeof(buf), "%s malloc for line16 failed", ERR_DURING_SAVE);
3346       DBG(DBG_error, "%s\n", buf);
3347       xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
3348       *cancel_save = 1;
3349      return (*cancel_save);
3350     }
3351     DBG(DBG_info, "line16 allocated\n");
3352   }
3353   else
3354   {
3355     bytes_per_line   = image_info->image_width * 3;
3356     bytes_per_line16 = image_info->image_width * 3;
3357     DBG(DBG_info, "bytes_per_line = %d\n", bytes_per_line);
3358   }
3359 
3360   line = (unsigned char *) malloc(bytes_per_line);
3361 
3362   if (line == NULL)
3363   {
3364    char buf[TEXTBUFSIZE];
3365 
3366     snprintf(buf, sizeof(buf), "%s malloc for line failed", ERR_DURING_SAVE);
3367     DBG(DBG_error, "%s\n", buf);
3368     xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
3369 
3370     if (line16)
3371     {
3372       free(line16);
3373     }
3374 
3375     *cancel_save = 1;
3376    return (*cancel_save);
3377   }
3378   DBG(DBG_info, "line allocated\n");
3379 
3380 #ifdef HAVE_LIBLCMS
3381   if (do_transform && (hTransform != NULL))
3382   {
3383     DBG(DBG_info, "Doing CMS color conversion\n");
3384 
3385     line_raw = (unsigned char *) malloc(bytes_per_line16);
3386 
3387     if (line_raw == NULL)
3388     {
3389      char buf[TEXTBUFSIZE];
3390 
3391       snprintf(buf, sizeof(buf), "%s malloc for line_raw failed", ERR_DURING_SAVE);
3392       DBG(DBG_error, "%s\n", buf);
3393       xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
3394 
3395       free(line);
3396 
3397       if (line16)
3398       {
3399         free(line16);
3400       }
3401 
3402       *cancel_save = 1;
3403      return (*cancel_save);
3404     }
3405 
3406     DBG(DBG_info, "line_raw allocated\n");
3407   }
3408 #endif
3409 
3410   for (y = 0; y < image_info->image_height; y++)
3411   {
3412     xsane_progress_bar_set_fraction(progress_bar, (float) y / image_info->image_height);
3413 
3414     linep = line;
3415 
3416     if (image_info->depth > 8) /* reduce 16 bit images to 12 bit */
3417     {
3418 #ifdef HAVE_LIBLCMS
3419       if (do_transform && (hTransform != NULL))
3420       {
3421         bytes_read = fread(line_raw, 6, image_info->image_width, imagefile);
3422         cmsDoTransform(hTransform, line_raw, line16, image_info->image_width);
3423       }
3424       else
3425 #endif
3426       {
3427         bytes_read = fread(line16, 6, image_info->image_width, imagefile);
3428       }
3429 
3430 #if __BYTE_ORDER == __LITTLE_ENDIAN
3431       for (x = 0; x < image_info->image_width; x=x+2)
3432       {
3433         *linep++ =   line16[6*x+1]; /* red high+middle */
3434         *linep++ =  (line16[6*x+0] & 240)      |  (line16[6*x+3] >> 4); /* red low | green high */
3435         *linep++ = ((line16[6*x+3] & 15) << 4) | ((line16[6*x+2] & 240) >> 4); /* green middle | green low */
3436 
3437         *linep++ =  line16[6*x+5]; /* blue high+middle */
3438 
3439         if (x == image_info->image_width-1)
3440         {
3441           *linep++ = (line16[6*x+4] & 240); /* blue low */
3442           break;
3443         }
3444 
3445         *linep++ =  (line16[6*x+4] & 240)      |  (line16[6*x+7] >> 4); /* blue low | red high */
3446         *linep++ = ((line16[6*x+7] & 15) << 4) | ((line16[6*x+6] & 240) >> 4); /* red middle | red low */
3447 
3448         *linep++ =   line16[6*x+9]; /* green high+middle */
3449         *linep++ =  (line16[6*x+8] & 240)       |  (line16[6*x+11] >> 4); /* green low | blue high */
3450         *linep++ = ((line16[6*x+11] & 15) << 4) | ((line16[6*x+10] & 240) >> 4); /* blue middle | blue low */
3451       }
3452 #else
3453       for (x = 0; x < image_info->image_width; x=x+2)
3454       {
3455         *linep++ =   line16[6*x+0]; /* red high+middle */
3456         *linep++ =  (line16[6*x+1] & 240)      |  (line16[6*x+2] >> 4); /* red low | green high */
3457         *linep++ = ((line16[6*x+2] & 15) << 4) | ((line16[6*x+3] & 240) >> 4); /* green middle | green low */
3458 
3459         *linep++ =  line16[6*x+4]; /* blue high+middle */
3460 
3461         if (x == image_info->image_width-1)
3462         {
3463           *linep++ = (line16[6*x+5] & 240); /* blue low */
3464           break;
3465         }
3466 
3467         *linep++ =  (line16[6*x+5] & 240)      |  (line16[6*x+6] >> 4); /* blue low | red high */
3468         *linep++ = ((line16[6*x+6] & 15) << 4) | ((line16[6*x+7] & 240) >> 4); /* red middle | red low */
3469 
3470         *linep++ =   line16[6*x+8]; /* green high+middle */
3471         *linep++ =  (line16[6*x+9] & 240)       |  (line16[6*x+10] >> 4); /* green low | blue high */
3472         *linep++ = ((line16[6*x+10] & 15) << 4) | ((line16[6*x+11] & 240) >> 4); /* blue middle | blue low */
3473       }
3474 #endif
3475     }
3476     else /* 8 bits/sample */
3477     {
3478 #ifdef HAVE_LIBLCMS
3479       if (do_transform && (hTransform != NULL))
3480       {
3481         bytes_read = fread(line_raw, 3, image_info->image_width, imagefile);
3482         cmsDoTransform(hTransform, line_raw, line, image_info->image_width);
3483       }
3484       else
3485 #endif
3486       {
3487         bytes_read = fread(line, 3, image_info->image_width, imagefile);
3488       }
3489     }
3490 
3491     if (ascii85decode)
3492     {
3493 #ifdef HAVE_LIBZ
3494       if (flatedecode)
3495       {
3496         ret = xsane_write_compressed_a85_flatedecode(outfile, line, bytes_per_line, (y == image_info->image_height - 1));
3497       }
3498       else
3499 #endif
3500       {
3501         ret = xsane_write_compressed_a85(outfile, line, bytes_per_line, (y == image_info->image_height - 1));
3502       }
3503     }
3504 #ifdef HAVE_LIBZ
3505     else if (flatedecode)
3506     {
3507       ret = xsane_write_flatedecode(outfile, line, bytes_per_line, (y == image_info->image_height - 1));
3508     }
3509 #endif
3510     else
3511     {
3512       fwrite(line, bytes_per_line, 1, outfile);
3513       ret = 0;
3514     }
3515 
3516     if ((ret != 0) || (ferror(outfile)))
3517     {
3518      char buf[TEXTBUFSIZE];
3519 
3520       if (ret == 0)
3521       {
3522         snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno));
3523       }
3524       else
3525       {
3526         snprintf(buf, sizeof(buf), "%s zlib error or memory allocation problem", ERR_DURING_SAVE);
3527       }
3528 
3529       DBG(DBG_error, "%s\n", buf);
3530       xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
3531       *cancel_save = 1;
3532 
3533      break;
3534     }
3535 
3536     if (*cancel_save)
3537     {
3538       break;
3539     }
3540   }
3541 
3542 #ifdef HAVE_LIBLCMS
3543   if (line_raw)
3544   {
3545     free(line_raw);
3546   }
3547 #endif
3548 
3549   if (line16)
3550   {
3551     free(line16);
3552   }
3553 
3554   free(line);
3555 
3556  return (*cancel_save);
3557 }
3558 
3559 /* ---------------------------------------------------------------------------------------------------------------------- */
3560 
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 paper_width,int paper_height,int paper_orientation,int flatedecode,cmsHTRANSFORM hTransform,int apply_ICM_profile,int embed_CSA,char * CSA_profile,int intent,GtkProgressBar * progress_bar,int * cancel_save)3561 int xsane_save_ps_page(FILE *outfile, int page,
3562                        FILE *imagefile, Image_info *image_info, float width, float height,
3563                        int paper_left_margin, int paper_bottom_margin, int paper_width, int paper_height, int paper_orientation,
3564                        int flatedecode,
3565                        cmsHTRANSFORM hTransform, int apply_ICM_profile, int embed_CSA, char *CSA_profile, int intent,
3566                        GtkProgressBar *progress_bar, int *cancel_save)
3567 {
3568  int degree, position_left, position_bottom, box_left, box_bottom, box_right, box_top;
3569  int left, bottom;
3570 
3571   DBG(DBG_proc, "xsane_save_ps_page\n");
3572 
3573   switch (paper_orientation)
3574   {
3575     default:
3576     case 0: /* top left portrait */
3577       left   = 0.0;
3578       bottom = paper_height - height;
3579      break;
3580 
3581     case 1: /* top right portrait */
3582       left   = paper_width  - width;
3583       bottom = paper_height - height;
3584      break;
3585 
3586     case 2: /* bottom right portrait */
3587       left   = paper_width - width;
3588       bottom = 0.0;
3589      break;
3590 
3591     case 3: /* bottom left portrait */
3592       left   = 0.0;
3593       bottom = 0.0;
3594      break;
3595 
3596     case 4: /* center portrait */
3597       left   = paper_width  / 2.0 - width  / 2.0;
3598       bottom = paper_height / 2.0 - height / 2.0;
3599      break;
3600 
3601 
3602     case 8: /* top left landscape */
3603       left   = 0.0;
3604       bottom = paper_width - height;
3605      break;
3606 
3607     case 9: /* top right landscape */
3608       left   = paper_height - width;
3609       bottom = paper_width  - height;
3610      break;
3611 
3612     case 10: /* bottom right landscape */
3613       left   = paper_height - width;
3614       bottom = 0.0;
3615      break;
3616 
3617     case 11: /* bottom left landscape */
3618       left   = 0.0;
3619       bottom = 0.0;
3620      break;
3621 
3622     case 12: /* center landscape */
3623       left   = paper_height / 2.0 - width  / 2.0;
3624       bottom = paper_width  / 2.0 - height / 2.0;
3625      break;
3626   }
3627 
3628 
3629   if (paper_orientation >= 8) /* rotate with 90 degrees - landscape mode */
3630   {
3631     degree = 90;
3632     position_left   = left   + paper_bottom_margin;
3633     position_bottom = bottom - paper_width - paper_left_margin;
3634     box_left        = paper_width - paper_left_margin - bottom - height;
3635     box_bottom      = left   + paper_bottom_margin;
3636     box_right       = box_left   + ceil(height);
3637     box_top         = box_bottom + ceil(width);
3638   }
3639   else /* do not rotate, portrait mode */
3640   {
3641     degree = 0;
3642     position_left   = left   + paper_left_margin;
3643     position_bottom = bottom + paper_bottom_margin;
3644     box_left        = left   + paper_left_margin;
3645     box_bottom      = bottom + paper_bottom_margin;
3646     box_right       = box_left   + ceil(width);
3647     box_top         = box_bottom + ceil(height);
3648   }
3649 
3650   fprintf(outfile, "\n");
3651   fprintf(outfile, "%%%%Page: %d %d\n", page, page);
3652   fprintf(outfile, "%%%%PageBoundingBox: %d %d %d %d\n", box_left, box_bottom, box_right, box_top);
3653 
3654 #ifdef HAVE_LIBLCMS
3655   if ((apply_ICM_profile) && (embed_CSA))
3656   {
3657     xsane_write_CSA(outfile, CSA_profile, intent); /* write scanner profile to ps file */
3658   }
3659   else
3660 #endif
3661   {
3662     if (image_info->channels == 1) /* lineart, halftone, grayscale */
3663     {
3664       fprintf(outfile, "/DeviceGray setcolorspace\n");
3665     }
3666     else
3667     {
3668       fprintf(outfile, "/DeviceRGB setcolorspace\n");
3669     }
3670   }
3671 
3672   xsane_save_ps_create_image_header(outfile, image_info, width, height,
3673                                     degree, position_left, position_bottom,
3674                                     box_left, box_bottom, box_right, box_top,
3675 	                            flatedecode);
3676 
3677   if (image_info->channels == 1) /* lineart, halftone, grayscale */
3678   {
3679     if (image_info->depth == 1) /* lineart, halftone */
3680     {
3681       xsane_save_ps_pdf_bw(outfile, imagefile, image_info, TRUE, flatedecode, progress_bar, cancel_save);
3682     }
3683     else /* grayscale */
3684     {
3685       xsane_save_ps_pdf_gray(outfile, imagefile, image_info, TRUE, flatedecode, hTransform, apply_ICM_profile && (!embed_CSA), progress_bar, cancel_save);
3686     }
3687   }
3688   else /* color RGB */
3689   {
3690     xsane_save_ps_pdf_color(outfile, imagefile, image_info, TRUE, flatedecode, hTransform, apply_ICM_profile && (!embed_CSA), progress_bar, cancel_save);
3691   }
3692 
3693   xsane_save_ps_create_page_trailer(outfile);
3694 
3695   if (ferror(outfile))
3696   {
3697    char buf[TEXTBUFSIZE];
3698 
3699     snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno));
3700     DBG(DBG_error, "%s\n", buf);
3701     xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
3702     *cancel_save = 1;
3703   }
3704 
3705  return (*cancel_save);
3706 }
3707 
3708 /* ---------------------------------------------------------------------------------------------------------------------- */
3709 
xsane_save_ps(FILE * outfile,FILE * imagefile,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 flatedecode,cmsHTRANSFORM hTransform,int apply_ICM_profile,int embed_CSA,char * CSA_profile,int embed_CRD,char * CRD_profile,int cms_bpc,int intent,GtkProgressBar * progress_bar,int * cancel_save)3710 int xsane_save_ps(FILE *outfile, FILE *imagefile, Image_info *image_info, float width, float height,
3711                   int paper_left_margin, int paper_bottom_margin, int paper_width, int paper_height, int paper_orientation,
3712                   int flatedecode,
3713 		  cmsHTRANSFORM hTransform, int apply_ICM_profile, int embed_CSA, char *CSA_profile,
3714                   int embed_CRD, char *CRD_profile, int cms_bpc, int intent,
3715                   GtkProgressBar *progress_bar, int *cancel_save)
3716 {
3717   DBG(DBG_proc, "xsane_save_ps\n");
3718 
3719   *cancel_save = 0;
3720 
3721   xsane_save_ps_create_document_header(outfile, 1 /* pages */, paper_left_margin, paper_bottom_margin, paper_width, paper_height, paper_orientation, flatedecode);
3722 
3723 #ifdef HAVE_LIBLCMS
3724   if ((apply_ICM_profile) && (embed_CRD))
3725   {
3726       xsane_write_CRD(outfile, CRD_profile, intent, cms_bpc); /* write printer profile to ps file */
3727   }
3728 #endif
3729 
3730 
3731 
3732   xsane_save_ps_page(outfile, 1 /* page */,
3733                      imagefile, image_info, width, height,
3734                      paper_left_margin, paper_bottom_margin, paper_width, paper_height, paper_orientation,
3735                      flatedecode,
3736 		     hTransform, apply_ICM_profile, embed_CSA, CSA_profile, intent,
3737                      progress_bar, cancel_save);
3738 
3739   xsane_save_ps_create_document_trailer(outfile, 0 /* we defined pages at beginning */);
3740 
3741   if (ferror(outfile))
3742   {
3743    char buf[TEXTBUFSIZE];
3744 
3745     snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno));
3746     DBG(DBG_error, "%s\n", buf);
3747     xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
3748     *cancel_save = 1;
3749   }
3750 
3751  return (*cancel_save);
3752 }
3753 
3754 /* ---------------------------------------------------------------------------------------------------------------------- */
3755 
xsane_embed_pdf_icm_profile(FILE * outfile,struct pdf_xref * xref,char * icm_filename,int flatedecode,int icc_object)3756 static int xsane_embed_pdf_icm_profile(FILE *outfile, struct pdf_xref *xref, char *icm_filename, int flatedecode, int icc_object)
3757 {
3758  FILE *icm_profile;
3759  size_t size, embed_len;
3760  unsigned char *embed_buffer;
3761  int ret;
3762 
3763   DBG(DBG_proc, "xsane_embed_pdf_icm_profile(%s)\n", icm_filename);
3764 
3765   icm_profile = fopen(icm_filename, "rb");
3766   if (icm_profile == NULL)
3767   {
3768     DBG(DBG_error, "Could not open ICM profile \"%s\" for reading\n", icm_filename);
3769    return -1;
3770   }
3771 
3772   fseek(icm_profile, 0, SEEK_END);
3773   size = ftell(icm_profile);
3774   fseek(icm_profile, 0, SEEK_SET);
3775 
3776   embed_buffer = malloc(size + 1);
3777   if (embed_buffer)
3778   {
3779     xref->obj[icc_object] = ftell(outfile);
3780     fprintf(outfile, "%d 0 obj\n", icc_object);
3781     fprintf(outfile, "   << /N 3\n"); /* 3 channels */
3782     fprintf(outfile, "      /Alternate /DeviceRGB\n");
3783 #ifdef HAVE_LIBZ
3784     if (flatedecode)
3785     {
3786       fprintf(outfile, "      /Filter /FlateDecode\n");
3787     }
3788 #endif
3789 
3790     fprintf(outfile, "      /Length             >>\n");
3791 
3792     /* Position of the stream length, to be written later on */
3793     xref->slenp = ftell(outfile) - 15;
3794 
3795     fprintf(outfile, "stream\n");
3796 
3797     /* Start of the stream data */
3798     xref->slen = ftell(outfile);
3799 
3800     embed_len = fread(embed_buffer, 1, size, icm_profile);
3801     embed_buffer[embed_len] = 0;
3802     fclose(icm_profile);
3803 
3804 #ifdef HAVE_LIBZ
3805     if (flatedecode)
3806     {
3807       ret = xsane_write_flatedecode(outfile, embed_buffer, size, TRUE);
3808     }
3809     else
3810 #endif
3811     {
3812       fwrite(embed_buffer, size, 1, outfile);
3813       ret = 0;
3814     }
3815 
3816     /* Go back and write the length of the stream */
3817     xref->slen = ftell(outfile) - xref->slen;
3818     fseek(outfile, xref->slenp, SEEK_SET);
3819     fprintf(outfile, "%lu", xref->slen);
3820     fseek(outfile, 0L, SEEK_END);
3821 
3822     fprintf(outfile, "endstream\n");
3823     fprintf(outfile, "endobj\n");
3824     fprintf(outfile, "\n");
3825 
3826     free(embed_buffer);
3827   }
3828   else
3829   {
3830     DBG(DBG_info, "Embedding ICM profile \"%s\" to PDF: no mem\n", icm_filename);
3831     fclose(icm_profile);
3832    return -2;
3833   }
3834 
3835 
3836   DBG(DBG_info, "Embedding ICM profile \"%s\" to PDF file retuned with status %d\n", icm_filename, ret);
3837  return ret;
3838 }
3839 
3840 /* ---------------------------------------------------------------------------------------------------------------------- */
3841 
xsane_save_pdf_create_document_header(FILE * outfile,struct pdf_xref * xref,int pages,int flatedecode)3842 void xsane_save_pdf_create_document_header(FILE *outfile, struct pdf_xref *xref, int pages, int flatedecode)
3843 {
3844  int i;
3845 
3846   DBG(DBG_proc, "xsane_save_pdf_create_document_header\n");
3847 
3848   fprintf(outfile, "%%PDF-1.4\n");
3849   fprintf(outfile, "\n");
3850   xref->obj[1] = ftell(outfile);
3851   fprintf(outfile, "1 0 obj\n");
3852   fprintf(outfile, "   << /Type /Catalog\n");
3853   fprintf(outfile, "      /Outlines 2 0 R\n");
3854   fprintf(outfile, "      /Pages 3 0 R\n");
3855   fprintf(outfile, "   >>\n");
3856   fprintf(outfile, "endobj\n");
3857   fprintf(outfile, "\n");
3858   xref->obj[2] = ftell(outfile);
3859   fprintf(outfile, "2 0 obj\n");
3860   fprintf(outfile, "   << /Type /Outlines\n");
3861   fprintf(outfile, "      /Count 0\n");
3862   fprintf(outfile, "   >>\n");
3863   fprintf(outfile, "endobj\n");
3864   fprintf(outfile, "\n");
3865   xref->obj[3] = ftell(outfile);
3866   fprintf(outfile, "3 0 obj\n");
3867   fprintf(outfile, "   << /Type /Pages\n");
3868   fprintf(outfile, "      /Kids [\n");
3869   for (i=0; i < pages; i++)
3870   {
3871     fprintf(outfile, "             %d 0 R\n", i * 2 + 6);
3872   }
3873   fprintf(outfile, "            ]\n");
3874   fprintf(outfile, "      /Count %d\n", pages);
3875   fprintf(outfile, "   >>\n");
3876   fprintf(outfile, "endobj\n");
3877   fprintf(outfile, "\n");
3878 
3879   xref->obj[4] = 0;
3880   xref->obj[5] = 0;
3881 }
3882 
3883 /* ---------------------------------------------------------------------------------------------------------------------- */
3884 
3885 /* page = [1 .. pages] */
xsane_save_pdf_create_page_header(FILE * outfile,struct pdf_xref * xref,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 flatedecode,int icc_object,GtkProgressBar * progress_bar)3886 static void xsane_save_pdf_create_page_header(FILE *outfile, struct pdf_xref *xref, int page,
3887                                               Image_info *image_info,
3888                                               float width, float height,
3889                                               int paper_left_margin, int paper_bottom_margin,
3890                                               int paper_width, int paper_height,
3891                                               int paper_orientation,
3892                                               int flatedecode, int icc_object,
3893                                               GtkProgressBar *progress_bar)
3894 {
3895  int position_left, position_bottom, box_left, box_bottom, box_right, box_top, depth;
3896  int left, bottom;
3897  float rad;
3898 
3899   DBG(DBG_proc, "xsane_save_pdf_create_page_header\n");
3900 
3901   switch (paper_orientation)
3902   {
3903     default:
3904     case 0: /* top left portrait */
3905       left   = 0.0;
3906       bottom = paper_height - height;
3907      break;
3908 
3909     case 1: /* top right portrait */
3910       left   = paper_width  - width;
3911       bottom = paper_height - height;
3912      break;
3913 
3914     case 2: /* bottom right portrait */
3915       left   = paper_width - width;
3916       bottom = 0.0;
3917      break;
3918 
3919     case 3: /* bottom left portrait */
3920       left   = 0.0;
3921       bottom = 0.0;
3922      break;
3923 
3924     case 4: /* center portrait */
3925       left   = paper_width  / 2.0 - width  / 2.0;
3926       bottom = paper_height / 2.0 - height / 2.0;
3927      break;
3928 
3929 
3930     case 8: /* top left landscape */
3931       left   = 0.0;
3932       bottom = paper_width - height;
3933      break;
3934 
3935     case 9: /* top right landscape */
3936       left   = paper_height - width;
3937       bottom = paper_width  - height;
3938      break;
3939 
3940     case 10: /* bottom right landscape */
3941       left   = paper_height - width;
3942       bottom = 0.0;
3943      break;
3944 
3945     case 11: /* bottom left landscape */
3946       left   = 0.0;
3947       bottom = 0.0;
3948      break;
3949 
3950     case 12: /* center landscape */
3951       left   = paper_height / 2.0 - width  / 2.0;
3952       bottom = paper_width  / 2.0 - height / 2.0;
3953      break;
3954   }
3955 
3956 
3957   if (paper_orientation >= 8) /* rotate with 90 degrees - landscape mode */
3958   {
3959     rad = -M_PI_2; /* pi / 2 */
3960     position_left   = left   + paper_bottom_margin;
3961     position_bottom = bottom - paper_width - paper_left_margin;
3962     box_left        = paper_width - paper_left_margin - bottom - height;
3963     box_bottom      = left   + paper_bottom_margin;
3964     box_right       = box_left   + ceil(height);
3965     box_top         = box_bottom + ceil(width);
3966   }
3967   else /* do not rotate, portrait mode */
3968   {
3969     rad = 0;
3970     position_left   = left   + paper_left_margin;
3971     position_bottom = bottom + paper_bottom_margin;
3972     box_left        = left   + paper_left_margin;
3973     box_bottom      = bottom + paper_bottom_margin;
3974     box_right       = box_left   + ceil(width);
3975     box_top         = box_bottom + ceil(height);
3976   }
3977 
3978   depth = image_info->depth;
3979 
3980   if (depth > 8) /* PDF does not support 16bits/sample in a standard image */
3981   {
3982     depth = 8;
3983   }
3984 
3985   xref->obj[page * 2 + 4] = ftell(outfile);
3986   fprintf(outfile, "%d 0 obj\n", page * 2 + 4);
3987   fprintf(outfile, "    << /Type /Page\n");
3988   fprintf(outfile, "       /Parent 3 0 R\n");
3989   fprintf(outfile, "       /MediaBox [%d %d %d %d]\n", box_left, box_bottom, box_right, box_top);
3990   fprintf(outfile, "       /Contents %d 0 R\n", page * 2 + 5);
3991   fprintf(outfile, "       /Resources << /ProcSet %d 0 R >>\n", page * 2 + 6);
3992   fprintf(outfile, "    >>\n");
3993   fprintf(outfile, "endobj\n");
3994   fprintf(outfile, "\n");
3995 
3996   /* Offset of object 5, for xref */
3997   xref->obj[page * 2 + 5] = ftell(outfile);
3998 
3999   fprintf(outfile, "%d 0 obj\n", page * 2 + 5);
4000   fprintf(outfile, "    << /Length             >>\n");
4001 
4002   /* Position of the stream length, to be written later on */
4003   xref->slenp = ftell(outfile) - 15;
4004 
4005   fprintf(outfile, "stream\n");
4006 
4007   /* Start of the stream data */
4008   xref->slen = ftell(outfile);
4009 
4010   fprintf(outfile, "q\n");
4011   fprintf(outfile, "1 0 0 1 %d %d cm\n", position_left, position_bottom); /* translate */
4012   fprintf(outfile, "%f %f -%f %f 0 0 cm\n", cos(rad), sin(rad), sin(rad), cos(rad)); /* rotate */
4013   fprintf(outfile, "%f 0 0 %f 0 0 cm\n", width, height); /* scale */
4014   fprintf(outfile, "BI\n");
4015   fprintf(outfile, "  /W %d\n", image_info->image_width);
4016   fprintf(outfile, "  /H %d\n", image_info->image_height);
4017 
4018   if ((icc_object) && (image_info->depth != 1))
4019   {
4020     fprintf(outfile, "  /ColorSpace [/ICCBased %d 0 R]\n", icc_object);
4021   }
4022 
4023   if (image_info->channels == 3) /* what about RGBA here ? */
4024   {
4025     if (icc_object == 0)
4026     {
4027       fprintf(outfile, "  /CS /RGB\n");
4028     }
4029     fprintf(outfile, "  /BPC %d\n", depth);
4030   }
4031   else if (image_info->depth == 1) /* BW */
4032   {
4033     fprintf(outfile, "  /CS /G\n");
4034     fprintf(outfile, "  /BPC 1\n");
4035   }
4036   else /* gray */
4037   {
4038     if (icc_object == 0)
4039     {
4040       fprintf(outfile, "  /CS /G\n");
4041     }
4042     fprintf(outfile, "  /BPC %d\n", depth);
4043   }
4044 
4045 #ifdef HAVE_LIBZ
4046   if (flatedecode)
4047   {
4048     fprintf(outfile, "  /F /FlateDecode\n");
4049   }
4050 #endif
4051 
4052   fprintf(outfile, "ID\n");
4053 }
4054 
4055 /* ---------------------------------------------------------------------------------------------------------------------- */
4056 
xsane_save_pdf_create_document_trailer(FILE * outfile,struct pdf_xref * xref,int pages)4057 void xsane_save_pdf_create_document_trailer(FILE *outfile, struct pdf_xref *xref, int pages)
4058 {
4059  struct tm *t;
4060  time_t tt;
4061  int i;
4062 
4063   /* PDF document trailer */
4064 
4065   /* Offset of object 6, for xref */
4066   xref->obj[pages * 2 + 6] = ftell(outfile);
4067 
4068   fprintf(outfile, "%d 0 obj\n", pages * 2 + 6);
4069   fprintf(outfile, "    [/PDF]\n");
4070   fprintf(outfile, "endobj\n");
4071   fprintf(outfile, "\n");
4072 
4073   /* Offset of object 7, for xref */
4074   xref->obj[pages * 2 + 7] = ftell(outfile);
4075 
4076   fprintf(outfile, "%d 0 obj\n", pages * 2 + 7);
4077   fprintf(outfile, "   << /Title (XSane scanned image)\n");
4078   fprintf(outfile, "      /Creator (XSane version %s (sane %d.%d) - by Oliver Rauch)\n",
4079 	  VERSION,
4080 	  SANE_VERSION_MAJOR(xsane.sane_backend_versioncode),
4081 	  SANE_VERSION_MINOR(xsane.sane_backend_versioncode));
4082   fprintf(outfile, "      /Producer (XSane %s)\n", VERSION);
4083 
4084   tt = time(NULL);
4085   t = gmtime(&tt);
4086 
4087   fprintf(outfile, "      /CreationDate (D:%04d%02d%02d%02d%02d%02d+00'00')\n",
4088 	  1900 + t->tm_year, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
4089   fprintf(outfile, "   >>\n");
4090   fprintf(outfile, "endobj\n");
4091   fprintf(outfile, "\n");
4092 
4093   /* Offset of xref, for startxref below */
4094   xref->xref = ftell(outfile);
4095 
4096   fprintf(outfile, "xref\n");
4097   fprintf(outfile, "0 %d\n", pages * 2 + 8);
4098   fprintf(outfile, "0000000000 65535 f \n");
4099 
4100   for (i=1; i <= pages * 2 + 7; i++)
4101   {
4102     if (xref->obj[i] > 0)
4103     {
4104       fprintf(outfile, "%010lu 00000 n \n", xref->obj[i]);
4105     }
4106     else
4107     {
4108       fprintf(outfile, "%010lu 00000 f \n", 0L);
4109     }
4110   }
4111 
4112   fprintf(outfile, "\n");
4113   fprintf(outfile, "trailer\n");
4114   fprintf(outfile, "    << /Size %d\n", pages * 2 + 8);
4115   fprintf(outfile, "       /Root 1 0 R\n");
4116   fprintf(outfile, "       /Info %d 0 R\n", pages * 2 + 7);
4117   fprintf(outfile, "    >>\n");
4118   fprintf(outfile, "startxref\n");
4119   fprintf(outfile, "%lu\n", xref->xref);
4120   fprintf(outfile, "%%%%EOF\n");
4121 }
4122 
4123 /* ---------------------------------------------------------------------------------------------------------------------- */
4124 
xsane_save_pdf_create_page_trailer(FILE * outfile,struct pdf_xref * xref)4125 static void xsane_save_pdf_create_page_trailer(FILE *outfile, struct pdf_xref *xref)
4126 {
4127   /* PDF page trailer */
4128   fprintf(outfile, "EI\n");
4129   fprintf(outfile, "Q\n");
4130 
4131   /* Go back and write the length of the stream */
4132   xref->slen = ftell(outfile) - xref->slen; /* we had a "-1" at the end but I do not understand the reason for -1, without looks better */
4133   fseek(outfile, xref->slenp, SEEK_SET);
4134   fprintf(outfile, "%lu", xref->slen);
4135   fseek(outfile, 0L, SEEK_END);
4136 
4137   fprintf(outfile, "endstream\n");
4138   fprintf(outfile, "endobj\n");
4139   fprintf(outfile, "\n");
4140 }
4141 
4142 /* ---------------------------------------------------------------------------------------------------------------------- */
4143 
xsane_save_pdf_page(FILE * outfile,struct pdf_xref * xref,int page,FILE * imagefile,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 flatedecode,cmsHTRANSFORM hTransform,int do_transform,int icc_object,GtkProgressBar * progress_bar,int * cancel_save)4144 int xsane_save_pdf_page(FILE *outfile, struct pdf_xref *xref, int page,
4145                         FILE *imagefile, Image_info *image_info, float width, float height,
4146                         int paper_left_margin, int paper_bottom_margin, int paper_width, int paper_height, int paper_orientation,
4147 			int flatedecode,
4148                         cmsHTRANSFORM hTransform, int do_transform, int icc_object,
4149                         GtkProgressBar *progress_bar, int *cancel_save)
4150 {
4151 
4152   DBG(DBG_proc, "xsane_save_pdf_page\n");
4153 
4154   xsane_save_pdf_create_page_header(outfile, xref, page,
4155                                     image_info, width, height,
4156 			            paper_left_margin, paper_bottom_margin, paper_width, paper_height, paper_orientation,
4157                                     flatedecode, icc_object,
4158 			            progress_bar);
4159 
4160   if (image_info->channels == 1) /* lineart, halftone, grayscale */
4161   {
4162     if (image_info->depth == 1) /* lineart, halftone */
4163     {
4164       xsane_save_ps_pdf_bw(outfile, imagefile, image_info, FALSE, flatedecode, progress_bar, cancel_save);
4165     }
4166     else /* grayscale */
4167     {
4168       xsane_save_ps_pdf_gray(outfile, imagefile, image_info, FALSE, flatedecode, hTransform, do_transform, progress_bar, cancel_save);
4169     }
4170   }
4171   else /* color RGB */
4172   {
4173     xsane_save_ps_pdf_color(outfile, imagefile, image_info, FALSE, flatedecode, hTransform, do_transform, progress_bar, cancel_save);
4174   }
4175 
4176   xsane_save_pdf_create_page_trailer(outfile, xref);
4177 
4178   if (ferror(outfile))
4179   {
4180    char buf[TEXTBUFSIZE];
4181 
4182     snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno));
4183     DBG(DBG_error, "%s\n", buf);
4184     xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
4185     *cancel_save = 1;
4186   }
4187 
4188  return (*cancel_save);
4189 }
4190 
4191 /* ---------------------------------------------------------------------------------------------------------------------- */
4192 
xsane_save_pdf(FILE * outfile,FILE * imagefile,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 flatedecode,cmsHTRANSFORM hTransform,int apply_ICM_profile,int cms_function,GtkProgressBar * progress_bar,int * cancel_save)4193 int xsane_save_pdf(FILE *outfile, FILE *imagefile, Image_info *image_info, float width, float height,
4194                    int paper_left_margin, int paper_bottom_margin, int paper_width, int paper_height, int paper_orientation,
4195                    int flatedecode,
4196                    cmsHTRANSFORM hTransform, int apply_ICM_profile, int cms_function,
4197                    GtkProgressBar *progress_bar, int *cancel_save)
4198 {
4199  struct pdf_xref xref;
4200  int icc_object = 0;
4201 
4202   DBG(DBG_proc, "xsane_save_pdf\n");
4203 
4204   *cancel_save = 0;
4205 
4206   xsane_save_pdf_create_document_header(outfile, &xref, 1, flatedecode);
4207 
4208   if (apply_ICM_profile && (cms_function == XSANE_CMS_FUNCTION_EMBED_SCANNER_ICM_PROFILE))
4209   {
4210     icc_object = 4;
4211     xsane_embed_pdf_icm_profile(outfile, &xref, image_info->icm_profile, flatedecode, icc_object);
4212   }
4213 
4214   xsane_save_pdf_page(outfile, &xref, 1,
4215                    imagefile, image_info, width, height,
4216                    paper_left_margin, paper_bottom_margin, paper_width, paper_height, paper_orientation,
4217                    flatedecode,
4218                    hTransform, apply_ICM_profile && ((cms_function != XSANE_CMS_FUNCTION_EMBED_SCANNER_ICM_PROFILE)) /* do_transform */, icc_object,
4219                    progress_bar, cancel_save);
4220 
4221   xsane_save_pdf_create_document_trailer(outfile, &xref, 1);
4222 
4223   if (ferror(outfile))
4224   {
4225    char buf[TEXTBUFSIZE];
4226 
4227     snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno));
4228     DBG(DBG_error, "%s\n", buf);
4229     xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
4230     *cancel_save = 1;
4231   }
4232 
4233  return (*cancel_save);
4234 }
4235 
4236 /* ---------------------------------------------------------------------------------------------------------------------- */
4237 
4238 #ifdef HAVE_LIBJPEG
4239 typedef struct
4240 {
4241   struct jpeg_error_mgr pub;/* "public" fields */
4242   int *cancel_save;
4243 } xsane_jpeg_error_mgr;
4244 
4245 typedef xsane_jpeg_error_mgr *xsane_jpeg_error_mgr_ptr;
4246 
xsane_jpeg_error_exit(j_common_ptr cinfo)4247 static void xsane_jpeg_error_exit(j_common_ptr cinfo)
4248 {
4249  char buf[TEXTBUFSIZE];
4250 
4251   /* cinfo->err points to a xsane_jpeg_error_mgr struct */
4252   xsane_jpeg_error_mgr_ptr xsane_jpeg_error_mgr_data = (xsane_jpeg_error_mgr_ptr) cinfo->err;
4253 
4254 
4255   if (!*xsane_jpeg_error_mgr_data->cancel_save)
4256   {
4257     /* output original error message */
4258     (*cinfo->err->output_message) (cinfo);
4259     snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_LIBJPEG);
4260     xsane_back_gtk_error(buf, TRUE);
4261   }
4262 
4263   *xsane_jpeg_error_mgr_data->cancel_save = 1;
4264 }
4265 
4266 /* ---------------------------------------------------------- */
4267 
4268 #ifdef HAVE_LIBLCMS
xsane_jpeg_write_icm_profile(j_compress_ptr cinfo_ptr,const JOCTET * icm_data_ptr,unsigned int icm_data_len)4269 static void xsane_jpeg_write_icm_profile(j_compress_ptr cinfo_ptr, const JOCTET *icm_data_ptr, unsigned int icm_data_len)
4270 {
4271 #define ICM_MARKER  (JPEG_APP0 + 2)     /* JPEG marker code for ICM */
4272 #define ICM_OVERHEAD_LEN  14            /* size of non-profile data in APP2 */
4273 #define MAX_BYTES_IN_MARKER  65533      /* maximum data len of a JPEG marker */
4274 #define MAX_DATA_BYTES_IN_MARKER  (MAX_BYTES_IN_MARKER - ICM_OVERHEAD_LEN)
4275 
4276   unsigned int num_markers;     /* total number of markers we'll write */
4277   int cur_marker = 1;           /* per spec, counting starts at 1 */
4278   unsigned int length;          /* number of bytes to write in this marker */
4279 
4280   /* Calculate the number of markers we'll need, rounding up of course */
4281   num_markers = icm_data_len / MAX_DATA_BYTES_IN_MARKER;
4282   if (num_markers * MAX_DATA_BYTES_IN_MARKER != icm_data_len)
4283   {
4284     num_markers++;
4285   }
4286 
4287   while (icm_data_len > 0)
4288   {
4289     length = icm_data_len; /* length of profile to put in this marker */
4290     if (length > MAX_DATA_BYTES_IN_MARKER)
4291     {
4292       length = MAX_DATA_BYTES_IN_MARKER;
4293     }
4294     icm_data_len -= length;
4295 
4296     /* Write the JPEG marker header (APP2 code and marker length) */
4297     jpeg_write_m_header(cinfo_ptr, ICM_MARKER, (unsigned int) (length + ICM_OVERHEAD_LEN));
4298 
4299     /* Write the marker identifying string "ICC_PROFILE" (null-terminated).
4300      * We code it in this less-than-transparent way so that the code works
4301      * even if the local character set is not ASCII.
4302      */
4303     jpeg_write_m_byte(cinfo_ptr, 0x49);
4304     jpeg_write_m_byte(cinfo_ptr, 0x43);
4305     jpeg_write_m_byte(cinfo_ptr, 0x43);
4306     jpeg_write_m_byte(cinfo_ptr, 0x5F);
4307     jpeg_write_m_byte(cinfo_ptr, 0x50);
4308     jpeg_write_m_byte(cinfo_ptr, 0x52);
4309     jpeg_write_m_byte(cinfo_ptr, 0x4F);
4310     jpeg_write_m_byte(cinfo_ptr, 0x46);
4311     jpeg_write_m_byte(cinfo_ptr, 0x49);
4312     jpeg_write_m_byte(cinfo_ptr, 0x4C);
4313     jpeg_write_m_byte(cinfo_ptr, 0x45);
4314     jpeg_write_m_byte(cinfo_ptr, 0x0);
4315 
4316     /* Add the sequencing info */
4317     jpeg_write_m_byte(cinfo_ptr, cur_marker);
4318     jpeg_write_m_byte(cinfo_ptr, (int) num_markers);
4319 
4320     /* Add the profile data */
4321     while (length--)
4322     {
4323       jpeg_write_m_byte(cinfo_ptr, *icm_data_ptr);
4324       icm_data_ptr++;
4325     }
4326     cur_marker++;
4327   }
4328 }
4329 
4330 /* ---------------------------------------------------------- */
4331 
xsane_jpeg_embed_scanner_icm_profile(j_compress_ptr cinfo_ptr,const char * icm_filename)4332 static void xsane_jpeg_embed_scanner_icm_profile(j_compress_ptr cinfo_ptr, const char *icm_filename)
4333 {
4334  FILE *icm_profile;
4335  size_t size, embed_len;
4336  LPBYTE embed_buffer;
4337 
4338   DBG(DBG_proc, "xsane_jpeg_embed_scanner_icm_profile(%s)\n", icm_filename);
4339 
4340   icm_profile = fopen(icm_filename, "rb");
4341   if (icm_profile == NULL)
4342   {
4343     return;
4344   }
4345 
4346   fseek(icm_profile, 0, SEEK_END);
4347   size = ftell(icm_profile);
4348   fseek(icm_profile, 0, SEEK_SET);
4349 
4350   embed_buffer = (LPBYTE) malloc(size + 1);
4351   if (embed_buffer)
4352   {
4353     embed_len = fread(embed_buffer, 1, size, icm_profile);
4354     fclose(icm_profile);
4355     embed_buffer[embed_len] = 0;
4356 
4357     xsane_jpeg_write_icm_profile(cinfo_ptr, embed_buffer, embed_len);
4358     free(embed_buffer);
4359 
4360     DBG(DBG_info, "ICM profile %s has been embedded to jpeg file\n", icm_filename);
4361   }
4362 }
4363 #endif
4364 
4365 /* ---------------------------------------------------------- */
4366 
xsane_save_jpeg(FILE * outfile,int quality,FILE * imagefile,Image_info * image_info,cmsHTRANSFORM hTransform,int apply_ICM_profile,int cms_function,GtkProgressBar * progress_bar,int * cancel_save)4367 int xsane_save_jpeg(FILE *outfile, int quality, FILE *imagefile, Image_info *image_info,
4368                     cmsHTRANSFORM hTransform, int apply_ICM_profile, int cms_function,
4369                     GtkProgressBar *progress_bar, int *cancel_save)
4370 {
4371  unsigned char *data;
4372  char buf[TEXTBUFSIZE];
4373  int components = 1;
4374  int x,y;
4375  int bytespp = 1;
4376  struct jpeg_compress_struct cinfo;
4377  xsane_jpeg_error_mgr jerr;
4378  JSAMPROW row_pointer[1];
4379  size_t bytes_read;
4380 #ifdef HAVE_LIBLCMS
4381  unsigned char *data_raw = NULL;
4382 #endif
4383 
4384   DBG(DBG_proc, "xsane_save_jpeg\n");
4385 
4386   *cancel_save = 0;
4387 
4388   if (image_info->channels == 3)
4389   {
4390     components = 3;
4391   }
4392 
4393   if (image_info->depth > 8)
4394   {
4395     bytespp = 2;
4396   }
4397 
4398   data = malloc(image_info->image_width * components * bytespp);
4399 
4400   if (!data)
4401   {
4402     snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM);
4403     xsane_back_gtk_error(buf, TRUE);
4404     return -1; /* error */
4405   }
4406 
4407 #ifdef HAVE_LIBLCMS
4408   if (apply_ICM_profile && (cms_function != XSANE_CMS_FUNCTION_EMBED_SCANNER_ICM_PROFILE) && (hTransform != NULL))
4409   {
4410     DBG(DBG_info, "Doing CMS color conversion\n");
4411 
4412     data_raw = malloc(image_info->image_width * components * bytespp);
4413 
4414     if (!data_raw)
4415     {
4416       free(data);
4417 
4418       snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM);
4419       xsane_back_gtk_error(buf, TRUE);
4420      return -1; /* error */
4421     }
4422   }
4423 #endif
4424 
4425   cinfo.err = jpeg_std_error(&jerr.pub);
4426   jerr.pub.error_exit = xsane_jpeg_error_exit;
4427   jerr.cancel_save = cancel_save;
4428 
4429   jpeg_create_compress(&cinfo);
4430   jpeg_stdio_dest(&cinfo, outfile);
4431   cinfo.image_width      = image_info->image_width;
4432   cinfo.image_height     = image_info->image_height;
4433   cinfo.input_components = components;
4434   if (image_info->channels == 3)
4435   {
4436     cinfo.in_color_space   = JCS_RGB;
4437   }
4438   else
4439   {
4440     cinfo.in_color_space   = JCS_GRAYSCALE;
4441   }
4442   jpeg_set_defaults(&cinfo);
4443 
4444   jpeg_set_quality(&cinfo, quality, TRUE);
4445 
4446   cinfo.density_unit     = 1; /* dpi */
4447   cinfo.X_density        = image_info->resolution_x;
4448   cinfo.Y_density        = image_info->resolution_y;
4449 
4450 #if 0
4451   cinfo.smoothing_factor = 0.0; /* 0 .. 100 */
4452   cinfo.dct_method = JDCT_FLOAT; /* JDCT_ISLOW, JDCT_IFAST, JDCT_FLOAT */
4453 #endif
4454 
4455   jpeg_start_compress(&cinfo, TRUE);
4456 
4457 #ifdef HAVE_LIBLCMS
4458   if (apply_ICM_profile)
4459   {
4460     if (cms_function == XSANE_CMS_FUNCTION_EMBED_SCANNER_ICM_PROFILE)
4461     {
4462       xsane_jpeg_embed_scanner_icm_profile(&cinfo, image_info->icm_profile);
4463     }
4464     else if (cms_function == XSANE_CMS_FUNCTION_CONVERT_TO_WORKING_CS)
4465     {
4466       xsane_jpeg_embed_scanner_icm_profile(&cinfo, preferences.working_color_space_icm_profile);
4467     }
4468   }
4469 #endif
4470 
4471   for (y = 0; y < image_info->image_height; y++)
4472   {
4473     xsane_progress_bar_set_fraction(progress_bar, (float) y / image_info->image_height);
4474 
4475     if (image_info->depth == 1)
4476     {
4477      int byte = 0;
4478      int mask = 128;
4479 
4480       for (x = 0; x < image_info->image_width; x++)
4481       {
4482 
4483         if ( (x % 8) == 0)
4484 	{
4485           byte = fgetc(imagefile);
4486           mask = 128;
4487 	}
4488 
4489         if (byte & mask)
4490         {
4491           data[x] = 0;
4492         }
4493         else
4494         {
4495           data[x] = 255;
4496         }
4497         mask >>= 1;
4498       }
4499     }
4500     else if (image_info->depth > 8) /* jpeg does not support 16 bits/sample, so we reduce it at first */
4501     {
4502       guint16 *data16 = (guint16 *) data;
4503 #ifdef HAVE_LIBLCMS
4504       if (apply_ICM_profile && (cms_function != XSANE_CMS_FUNCTION_EMBED_SCANNER_ICM_PROFILE) && (hTransform != NULL))
4505       {
4506         bytes_read = fread(data_raw, components * 2, image_info->image_width, imagefile);
4507         cmsDoTransform(hTransform, data_raw, data, image_info->image_width);
4508       }
4509       else
4510 #endif
4511       {
4512         bytes_read = fread(data, components * 2, image_info->image_width, imagefile);
4513       }
4514 
4515       for (x = 0; x < image_info->image_width * components; x++)
4516       {
4517         data[x] = data16[x] / 256;
4518       }
4519 
4520     }
4521     else /* 8 bits/sample */
4522     {
4523 #ifdef HAVE_LIBLCMS
4524       if (apply_ICM_profile && (cms_function != XSANE_CMS_FUNCTION_EMBED_SCANNER_ICM_PROFILE) && (hTransform != NULL))
4525       {
4526         bytes_read = fread(data_raw, components, image_info->image_width, imagefile);
4527         cmsDoTransform(hTransform, data_raw, data, image_info->image_width);
4528       }
4529       else
4530 #endif
4531       {
4532         bytes_read = fread(data, components, image_info->image_width, imagefile);
4533       }
4534     }
4535 
4536     row_pointer[0] = data;
4537     jpeg_write_scanlines(&cinfo, row_pointer, 1);
4538 
4539     if (*cancel_save)
4540     {
4541       cinfo.image_height = y; /* correct image height */
4542       break;
4543     }
4544   }
4545 
4546   jpeg_finish_compress(&cinfo);
4547 
4548 #ifdef HAVE_LIBLCMS
4549   if (data_raw)
4550   {
4551     free(data_raw);
4552   }
4553 #endif
4554   free(data);
4555 
4556  return (*cancel_save);
4557 }
4558 #endif
4559 
4560 /* ---------------------------------------------------------------------------------------------------------------------- */
4561 
4562 #ifdef HAVE_LIBTIFF
4563 #ifdef HAVE_LIBLCMS
xsane_tiff_embed_scanner_icm_profile(TIFF * tiffile,const char * icm_filename)4564 static void xsane_tiff_embed_scanner_icm_profile(TIFF *tiffile, const char *icm_filename)
4565 {
4566  FILE *icm_profile;
4567  size_t size;
4568  char *icm_profile_buffer;
4569 
4570   DBG(DBG_proc, "xsane_tiff_embed_scanner_icm_profile(%s)\n", icm_filename);
4571   if((icm_profile = fopen(icm_filename, "rb")))
4572   {
4573     fseek(icm_profile, 0, SEEK_END);
4574     size = ftell(icm_profile);
4575     fseek(icm_profile, 0, SEEK_SET);
4576 
4577     icm_profile_buffer = (char *) malloc(size + 1);
4578 
4579     if (icm_profile_buffer)
4580     {
4581       if (fread(icm_profile_buffer, 1, size, icm_profile) == size)
4582       {
4583         icm_profile_buffer[size] = 0;
4584 
4585         TIFFSetField(tiffile, TIFFTAG_ICCPROFILE, size, icm_profile_buffer);
4586       }
4587       else
4588       {
4589         DBG(DBG_error, "Can not read ICM profile data\n");
4590       }
4591 
4592       free(icm_profile_buffer);
4593     }
4594     else
4595     {
4596       DBG(DBG_error, "Can not get enogh memory for ICM profile\n");
4597     }
4598 
4599 
4600     fclose(icm_profile);
4601   }
4602   else
4603   {
4604     DBG(DBG_error, "Can not embed ICM profile\n");
4605   }
4606 }
4607 #endif
4608 
4609 /* ---------------------------------------------------------------------------------------------------------------------- */
4610 
4611 /* pages = 0 => single page tiff, page = 0 */
4612 /* pages > 0 => page = [1 .. pages] */
xsane_save_tiff_page(TIFF * tiffile,int page,int pages,int quality,FILE * imagefile,Image_info * image_info,cmsHTRANSFORM hTransform,int apply_ICM_profile,int cms_function,GtkProgressBar * progress_bar,int * cancel_save)4613 int xsane_save_tiff_page(TIFF *tiffile, int page, int pages, int quality, FILE *imagefile, Image_info *image_info,
4614                          cmsHTRANSFORM hTransform, int apply_ICM_profile, int cms_function,
4615                          GtkProgressBar *progress_bar, int *cancel_save)
4616 {
4617  char *data;
4618  char buf[TEXTBUFSIZE];
4619  int y, w;
4620  int components;
4621  int compression;
4622  int bytes;
4623  struct tm *ptm;
4624  time_t now;
4625  size_t bytes_read;
4626 #ifdef HAVE_LIBLCMS
4627  char *data_raw = NULL;
4628 #endif
4629 
4630   DBG(DBG_proc, "xsane_save_tiff_page(%d/%d\n", page, pages);
4631 
4632   *cancel_save = 0;
4633 
4634   if (image_info->depth == 1)
4635   {
4636     compression = preferences.tiff_compression1_nr;
4637   }
4638   else if (image_info->depth == 8)
4639   {
4640     compression = preferences.tiff_compression8_nr;
4641   }
4642   else
4643   {
4644     compression = preferences.tiff_compression16_nr;
4645   }
4646 
4647 
4648   if (image_info->channels == 3)
4649   {
4650     components = 3;
4651   }
4652   else
4653   {
4654     components = 1;
4655   }
4656 
4657   if (image_info->depth <= 8)
4658   {
4659     bytes = 1;
4660   }
4661   else
4662   {
4663     bytes = 2;
4664   }
4665 
4666   data = (char *)_TIFFmalloc(image_info->image_width * components * bytes);
4667 
4668   if (!data)
4669   {
4670     snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM);
4671     xsane_back_gtk_error(buf, TRUE);
4672    return -1; /* error */
4673   }
4674 
4675 #ifdef HAVE_LIBLCMS
4676   if ((cms_function != XSANE_CMS_FUNCTION_EMBED_SCANNER_ICM_PROFILE) && (hTransform != NULL))
4677   {
4678     DBG(DBG_info, "Doing CMS color conversion\n");
4679 
4680     data_raw = (char *) malloc(image_info->image_width * components * bytes);
4681 
4682     if (!data_raw)
4683     {
4684       _TIFFfree(data);
4685 
4686       snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM);
4687       xsane_back_gtk_error(buf, TRUE);
4688      return -1; /* error */
4689     }
4690   }
4691 #endif
4692 
4693 
4694   TIFFSetField(tiffile, TIFFTAG_IMAGEWIDTH, image_info->image_width);
4695   TIFFSetField(tiffile, TIFFTAG_IMAGELENGTH, image_info->image_height);
4696   TIFFSetField(tiffile, TIFFTAG_BITSPERSAMPLE, image_info->depth);
4697   TIFFSetField(tiffile, TIFFTAG_SAMPLESPERPIXEL, components);
4698   TIFFSetField(tiffile, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
4699   TIFFSetField(tiffile, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
4700   TIFFSetField(tiffile, TIFFTAG_COMPRESSION, compression);
4701   TIFFSetField(tiffile, TIFFTAG_SOFTWARE, "xsane");
4702 
4703   time(&now);
4704   ptm = localtime(&now);
4705   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);
4706   TIFFSetField(tiffile, TIFFTAG_DATETIME, buf);
4707 
4708   if (image_info->resolution_x > 0.0)
4709   {
4710     TIFFSetField(tiffile, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
4711     TIFFSetField(tiffile, TIFFTAG_XRESOLUTION, image_info->resolution_x);
4712     TIFFSetField(tiffile, TIFFTAG_YRESOLUTION, image_info->resolution_y);
4713   }
4714 
4715   if (compression == COMPRESSION_DEFLATE)
4716   {
4717     TIFFSetField(tiffile, TIFFTAG_ZIPQUALITY, (int) preferences.tiff_zip_compression);
4718   }
4719   else if (compression == COMPRESSION_JPEG)
4720   {
4721     TIFFSetField(tiffile, TIFFTAG_JPEGQUALITY, quality);
4722   }
4723 
4724   if (image_info->channels == 3)
4725   {
4726     if (compression == COMPRESSION_JPEG)
4727     {
4728       TIFFSetField(tiffile, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_YCBCR);
4729       TIFFSetField(tiffile, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);  /* convert from RGB (to YCBCR) */
4730     }
4731     else /* no jpeg compression */
4732     {
4733       TIFFSetField(tiffile, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
4734     }
4735 
4736 #ifdef HAVE_LIBLCMS
4737     if (apply_ICM_profile)
4738     {
4739       if (cms_function == XSANE_CMS_FUNCTION_EMBED_SCANNER_ICM_PROFILE)
4740       {
4741         xsane_tiff_embed_scanner_icm_profile(tiffile, image_info->icm_profile);
4742       }
4743       else if (cms_function == XSANE_CMS_FUNCTION_CONVERT_TO_WORKING_CS)
4744       {
4745         xsane_tiff_embed_scanner_icm_profile(tiffile, preferences.working_color_space_icm_profile);
4746       }
4747     }
4748 #endif
4749   }
4750   else
4751   {
4752     if (image_info->depth == 1) /* lineart */
4753     {
4754       TIFFSetField(tiffile, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
4755     }
4756     else /* grayscale */
4757     {
4758       TIFFSetField(tiffile, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
4759       /* we have to do nothing special for jpeg! */
4760     }
4761   }
4762 
4763   TIFFSetField(tiffile, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tiffile, -1));
4764 
4765   if (pages)
4766   {
4767     TIFFSetField(tiffile, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE);
4768     TIFFSetField(tiffile, TIFFTAG_PAGENUMBER, page, pages);
4769   }
4770 
4771   w = TIFFScanlineSize(tiffile);
4772 
4773   for (y = 0; y < image_info->image_height; y++)
4774   {
4775     xsane_progress_bar_set_fraction(progress_bar, (float) y / image_info->image_height);
4776 
4777 #ifdef HAVE_LIBLCMS
4778     if ((cms_function != XSANE_CMS_FUNCTION_EMBED_SCANNER_ICM_PROFILE) && (hTransform != NULL))
4779     {
4780       bytes_read = fread(data_raw, 1, w, imagefile);
4781       cmsDoTransform(hTransform, data_raw, data, image_info->image_width);
4782     }
4783     else
4784 #endif
4785     {
4786       bytes_read = fread(data, 1, w, imagefile);
4787     }
4788 
4789     if (TIFFWriteScanline(tiffile, data, y, 0) != 1)
4790     {
4791      char buf[TEXTBUFSIZE];
4792 
4793       snprintf(buf, sizeof(buf), "%s", ERR_DURING_SAVE);
4794       DBG(DBG_error, "%s\n", buf);
4795       xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
4796       *cancel_save = 1;
4797      break;
4798     }
4799 
4800     if (*cancel_save)
4801     {
4802       break;
4803     }
4804   }
4805 
4806   if (pages)
4807   {
4808     TIFFWriteDirectory(tiffile);
4809   }
4810 
4811 #ifdef HAVE_LIBLCMS
4812   if (data_raw)
4813   {
4814     free(data_raw);
4815   }
4816 #endif
4817 
4818   _TIFFfree(data);
4819  return (*cancel_save);
4820 }
4821 #endif
4822 
4823 /* ---------------------------------------------------------------------------------------------------------------------- */
4824 
4825 #if defined(PNG_iCCP_SUPPORTED)
4826 #ifdef HAVE_LIBLCMS
xsane_png_embed_scanner_icm_profile(png_structp png_ptr,png_infop png_info_ptr,const char * icm_filename)4827 static void xsane_png_embed_scanner_icm_profile(png_structp png_ptr, png_infop png_info_ptr, const char *icm_filename)
4828 {
4829  FILE *icm_profile;
4830  gchar *profile_buffer;
4831  size_t size;
4832 
4833   DBG(DBG_proc, "xsane_png_embed_scanner_icm_profile(%s)\n", icm_filename);
4834   icm_profile = fopen(icm_filename, "rb");
4835 
4836   if (icm_profile)
4837   {
4838     fseek(icm_profile, 0, SEEK_END);
4839     size = ftell(icm_profile);
4840     fseek(icm_profile, 0, SEEK_SET);
4841 
4842     profile_buffer = malloc(size);
4843 
4844     if (profile_buffer)
4845     {
4846       if (fread(profile_buffer, 1, size, icm_profile) == size)
4847       {
4848         png_set_iCCP(png_ptr, png_info_ptr, "ICC profile", 0, profile_buffer, size);
4849       }
4850       else
4851       {
4852         DBG(DBG_error, "can not read ICC profile data\n");
4853       }
4854 
4855       free(profile_buffer);
4856     }
4857     else
4858     {
4859       DBG(DBG_error, "can not allocate profile_buffer\n");
4860     }
4861 
4862     fclose(icm_profile);
4863   }
4864   else
4865   {
4866     DBG(DBG_error, "can not open ICM-profile\n");
4867   }
4868 }
4869 #endif
4870 #endif
4871 
4872 /* ---------------------------------------------------------------------------------------------------------------------- */
4873 
4874 #ifdef HAVE_LIBPNG
4875 #ifdef HAVE_LIBZ
xsane_save_png(FILE * outfile,int compression,FILE * imagefile,Image_info * image_info,cmsHTRANSFORM hTransform,int apply_ICM_profile,int cms_function,GtkProgressBar * progress_bar,int * cancel_save)4876 int xsane_save_png(FILE *outfile, int compression, FILE *imagefile, Image_info *image_info,
4877                    cmsHTRANSFORM hTransform, int apply_ICM_profile, int cms_function,
4878                    GtkProgressBar *progress_bar, int *cancel_save)
4879 {
4880  png_structp png_ptr;
4881  png_infop png_info_ptr;
4882  png_bytep row_ptr;
4883  png_color_8 sig_bit;
4884  unsigned char *data;
4885  char buf[TEXTBUFSIZE];
4886  int colortype, components, byte_width;
4887  int y;
4888  size_t bytes_read;
4889 #ifdef HAVE_LIBLCMS
4890  unsigned char *data_raw = NULL;
4891 #endif
4892 
4893   DBG(DBG_proc, "xsane_save_png\n");
4894 
4895   *cancel_save = 0;
4896 
4897   png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
4898   if (!png_ptr)
4899   {
4900     snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_LIBPNG);
4901     xsane_back_gtk_error(buf, TRUE);
4902     return -1; /* error */
4903   }
4904 
4905   png_info_ptr = png_create_info_struct(png_ptr);
4906   if (!png_info_ptr)
4907   {
4908     snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_LIBTIFF);
4909     xsane_back_gtk_error(buf, TRUE);
4910     return -1; /* error */
4911   }
4912 
4913   if (setjmp(png_jmpbuf(png_ptr)))
4914   {
4915     snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_LIBPNG);
4916     xsane_back_gtk_error(buf, TRUE);
4917     png_destroy_write_struct(&png_ptr, (png_infopp) 0);
4918     return -1; /* error */
4919   }
4920 
4921   byte_width = image_info->image_width;
4922 
4923   if (image_info->channels == 4) /* RGBA */
4924   {
4925     components = 4;
4926     colortype = PNG_COLOR_TYPE_RGB_ALPHA;
4927   }
4928   else if (image_info->channels == 3) /* RGB */
4929   {
4930     components = 3;
4931     colortype = PNG_COLOR_TYPE_RGB;
4932   }
4933   else /* gray or black/white */
4934   {
4935     components = 1;
4936     colortype = PNG_COLOR_TYPE_GRAY;
4937   }
4938 
4939   png_init_io(png_ptr, outfile);
4940   png_set_compression_level(png_ptr, compression);
4941   png_set_IHDR(png_ptr, png_info_ptr, image_info->image_width, image_info->image_height, image_info->depth,
4942                colortype, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
4943 
4944   if (image_info->channels >=3)
4945   {
4946     sig_bit.red   = image_info->depth;
4947     sig_bit.green = image_info->depth;
4948     sig_bit.blue  = image_info->depth;
4949 
4950     if (image_info->channels == 4)
4951     {
4952       sig_bit.alpha = image_info->depth;
4953     }
4954 
4955   }
4956   else
4957   {
4958     sig_bit.gray  = image_info->depth;
4959 
4960     if (image_info->depth == 1)
4961     {
4962       byte_width = (image_info->image_width+7)/8;
4963       png_set_invert_mono(png_ptr);
4964     }
4965   }
4966 
4967   png_set_sBIT(png_ptr, png_info_ptr, &sig_bit);
4968 #if defined(PNG_pHYs_SUPPORTED)
4969   png_set_pHYs(png_ptr, png_info_ptr,
4970                image_info->resolution_x * 100.0 / 2.54,
4971                image_info->resolution_y * 100.0 / 2.54, PNG_RESOLUTION_METER);
4972 #endif
4973 
4974 #if defined(PNG_iCCP_SUPPORTED)
4975 #ifdef HAVE_LIBLCMS
4976   if (apply_ICM_profile)
4977   {
4978     if (cms_function == XSANE_CMS_FUNCTION_EMBED_SCANNER_ICM_PROFILE)
4979     {
4980       xsane_png_embed_scanner_icm_profile(png_ptr, png_info_ptr, image_info->icm_profile);
4981     }
4982     else if (cms_function == XSANE_CMS_FUNCTION_CONVERT_TO_WORKING_CS)
4983     {
4984       xsane_png_embed_scanner_icm_profile(png_ptr, png_info_ptr, preferences.working_color_space_icm_profile);
4985     }
4986   }
4987 #endif
4988 #endif
4989 
4990   png_write_info(png_ptr, png_info_ptr);
4991   png_set_shift(png_ptr, &sig_bit);
4992 
4993   data = malloc(image_info->image_width * components);
4994 
4995   if (!data)
4996   {
4997     snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM);
4998     xsane_back_gtk_error(buf, TRUE);
4999     png_destroy_write_struct(&png_ptr, (png_infopp) 0);
5000     return -1; /* error */
5001   }
5002 
5003 #ifdef HAVE_LIBLCMS
5004   if ((cms_function != XSANE_CMS_FUNCTION_EMBED_SCANNER_ICM_PROFILE) && (hTransform != NULL))
5005   {
5006     DBG(DBG_info, "Doing CMS color conversion\n");
5007 
5008     data_raw = malloc(image_info->image_width * components);
5009 
5010     if (!data_raw)
5011     {
5012       free(data);
5013 
5014       snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM);
5015       xsane_back_gtk_error(buf, TRUE);
5016       png_destroy_write_struct(&png_ptr, (png_infopp) 0);
5017      return -1; /* error */
5018     }
5019   }
5020 #endif
5021 
5022   for (y = 0; y < image_info->image_height; y++)
5023   {
5024     xsane_progress_bar_set_fraction(progress_bar, (float) y / image_info->image_height);
5025 
5026 #ifdef HAVE_LIBLCMS
5027     if ((cms_function != XSANE_CMS_FUNCTION_EMBED_SCANNER_ICM_PROFILE) && (hTransform != NULL))
5028     {
5029       bytes_read = fread(data_raw, components, byte_width, imagefile);
5030       cmsDoTransform(hTransform, data_raw, data, image_info->image_width);
5031     }
5032     else
5033 #endif
5034     {
5035       bytes_read = fread(data, components, byte_width, imagefile);
5036     }
5037 
5038     row_ptr = data;
5039     png_write_rows(png_ptr, &row_ptr, 1); /* errors are caught by test sor setjmp(...) */
5040 
5041     if (*cancel_save)
5042     {
5043       break;
5044     }
5045   }
5046 
5047 #ifdef HAVE_LIBLCMS
5048   if (data_raw)
5049   {
5050     free(data_raw);
5051   }
5052 #endif
5053   free(data);
5054   png_write_end(png_ptr, png_info_ptr);
5055   png_destroy_write_struct(&png_ptr, (png_infopp) 0);
5056 
5057  return (*cancel_save);
5058 }
5059 #endif
5060 #endif
5061 
5062 /* ---------------------------------------------------------------------------------------------------------------------- */
5063 
5064 #ifdef HAVE_LIBPNG
5065 #ifdef HAVE_LIBZ
xsane_save_png_16(FILE * outfile,int compression,FILE * imagefile,Image_info * image_info,cmsHTRANSFORM hTransform,int apply_ICM_profile,int cms_function,GtkProgressBar * progress_bar,int * cancel_save)5066 int xsane_save_png_16(FILE *outfile, int compression, FILE *imagefile, Image_info *image_info,
5067                       cmsHTRANSFORM hTransform, int apply_ICM_profile, int cms_function,
5068                       GtkProgressBar *progress_bar, int *cancel_save)
5069 {
5070  png_structp png_ptr;
5071  png_infop png_info_ptr;
5072  png_bytep row_ptr;
5073  png_color_8 sig_bit; /* should be 16, but then I get a warning about wrong type */
5074  unsigned char *data;
5075  char buf[TEXTBUFSIZE];
5076  int colortype, components;
5077  int y;
5078  size_t bytes_read;
5079 #ifdef HAVE_LIBLCMS
5080  unsigned char *data_raw = NULL;
5081 #endif
5082 
5083   DBG(DBG_proc, "xsane_save_png16\n");
5084 
5085   *cancel_save = 0;
5086 
5087   png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
5088   if (!png_ptr)
5089   {
5090     snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_LIBPNG);
5091     xsane_back_gtk_error(buf, TRUE);
5092     return -1; /* error */
5093   }
5094 
5095   png_info_ptr = png_create_info_struct(png_ptr);
5096   if (!png_info_ptr)
5097   {
5098     snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_LIBPNG);
5099     xsane_back_gtk_error(buf, TRUE);
5100     return -1; /* error */
5101   }
5102 
5103   if (setjmp(png_jmpbuf(png_ptr)))
5104   {
5105     snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_LIBPNG);
5106     xsane_back_gtk_error(buf, TRUE);
5107     png_destroy_write_struct(&png_ptr, (png_infopp) 0);
5108     return -1; /* error */
5109   }
5110 
5111   if (image_info->channels == 4) /* RGBA */
5112   {
5113     components = 4;
5114     colortype = PNG_COLOR_TYPE_RGB_ALPHA;
5115   }
5116   else if (image_info->channels == 3) /* RGB */
5117   {
5118     components = 3;
5119     colortype = PNG_COLOR_TYPE_RGB;
5120   }
5121   else /* gray or black/white */
5122   {
5123     components = 1;
5124     colortype = PNG_COLOR_TYPE_GRAY;
5125   }
5126 
5127   png_init_io(png_ptr, outfile);
5128   png_set_compression_level(png_ptr, compression);
5129   png_set_IHDR(png_ptr, png_info_ptr, image_info->image_width, image_info->image_height, 16,
5130                colortype, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
5131 
5132   sig_bit.red   = image_info->depth;
5133   sig_bit.green = image_info->depth;
5134   sig_bit.blue  = image_info->depth;
5135   sig_bit.alpha = image_info->depth;
5136   sig_bit.gray  = image_info->depth;
5137 
5138   png_set_sBIT(png_ptr, png_info_ptr, &sig_bit);
5139 
5140 #if defined(PNG_pHYs_SUPPORTED)
5141   png_set_pHYs(png_ptr, png_info_ptr,
5142                image_info->resolution_x * 100.0 / 2.54,
5143                image_info->resolution_y * 100.0 / 2.54, PNG_RESOLUTION_METER);
5144 #endif
5145 
5146 #if defined(PNG_iCCP_SUPPORTED)
5147 #ifdef HAVE_LIBLCMS
5148   if (apply_ICM_profile)
5149   {
5150     if (cms_function == XSANE_CMS_FUNCTION_EMBED_SCANNER_ICM_PROFILE)
5151     {
5152       xsane_png_embed_scanner_icm_profile(png_ptr, png_info_ptr, image_info->icm_profile);
5153     }
5154     else if (cms_function == XSANE_CMS_FUNCTION_CONVERT_TO_WORKING_CS)
5155     {
5156       xsane_png_embed_scanner_icm_profile(png_ptr, png_info_ptr, preferences.working_color_space_icm_profile);
5157     }
5158   }
5159 #endif
5160 #endif
5161 
5162   png_write_info(png_ptr, png_info_ptr);
5163   png_set_shift(png_ptr, &sig_bit);
5164   png_set_packing(png_ptr);
5165 
5166   data = malloc(image_info->image_width * components * 2);
5167 
5168   if (!data)
5169   {
5170     snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM);
5171     xsane_back_gtk_error(buf, TRUE);
5172     png_destroy_write_struct(&png_ptr, (png_infopp) 0);
5173     return -1; /* error */
5174   }
5175 
5176 #ifdef HAVE_LIBLCMS
5177   if ((cms_function != XSANE_CMS_FUNCTION_EMBED_SCANNER_ICM_PROFILE) && (hTransform != NULL))
5178   {
5179     DBG(DBG_info, "Doing CMS color conversion\n");
5180 
5181     data_raw = malloc(image_info->image_width * components * 2);
5182 
5183     if (!data_raw)
5184     {
5185       free(data);
5186 
5187       snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM);
5188       xsane_back_gtk_error(buf, TRUE);
5189       png_destroy_write_struct(&png_ptr, (png_infopp) 0);
5190      return -1; /* error */
5191     }
5192   }
5193 #endif
5194 
5195   for (y = 0; y < image_info->image_height; y++)
5196   {
5197     xsane_progress_bar_set_fraction(progress_bar, (float) y / image_info->image_height);
5198 
5199 #ifdef HAVE_LIBLCMS
5200     if ((cms_function != XSANE_CMS_FUNCTION_EMBED_SCANNER_ICM_PROFILE) && (hTransform != NULL))
5201     {
5202       bytes_read = fread(data_raw, components * 2, image_info->image_width, imagefile);
5203       cmsDoTransform(hTransform, data_raw, data, image_info->image_width);
5204     }
5205     else
5206 #endif
5207     {
5208       bytes_read = fread(data, components * 2, image_info->image_width, imagefile);
5209     }
5210 
5211 #if __BYTE_ORDER == __LITTLE_ENDIAN
5212     /* we have to write data in network order (MSB first), so when we run on a low endian machine then we have to swap bytes */
5213     {
5214      int x;
5215 
5216       for (x = 0; x < image_info->image_width * components; x++)
5217       {
5218         unsigned char help;
5219 
5220         help = data[x*2+0];
5221         data[x*2+0] = data[x*2+1];
5222         data[x*2+1] = help;
5223       }
5224     }
5225 #endif /* LITTLE_ENDIAN */
5226 
5227     row_ptr = data;
5228     png_write_rows(png_ptr, &row_ptr, 1);
5229     if (*cancel_save)
5230     {
5231       break;
5232     }
5233   }
5234 
5235 #ifdef HAVE_LIBLCMS
5236   if (data_raw)
5237   {
5238     free(data_raw);
5239   }
5240 #endif
5241   free(data);
5242   png_write_end(png_ptr, png_info_ptr);
5243   png_destroy_write_struct(&png_ptr, (png_infopp) 0);
5244 
5245  return (*cancel_save);
5246 }
5247 #endif
5248 #endif
5249 
5250 /* ---------------------------------------------------------------------------------------------------------------------- */
5251 
xsane_save_pnm_16_ascii_gray(FILE * outfile,FILE * imagefile,Image_info * image_info,cmsHTRANSFORM hTransform,int apply_ICM_profile,GtkProgressBar * progress_bar,int * cancel_save)5252 static int xsane_save_pnm_16_ascii_gray(FILE *outfile, FILE *imagefile, Image_info *image_info,
5253                                         cmsHTRANSFORM hTransform, int apply_ICM_profile,
5254                                         GtkProgressBar *progress_bar, int *cancel_save)
5255 {
5256  int x,y;
5257  guint16 *data;
5258  int count = 0;
5259  size_t bytes_read;
5260 #ifdef HAVE_LIBLCMS
5261  guint16 *data_raw = NULL;
5262 #endif
5263 
5264   DBG(DBG_proc, "xsane_save_pnm_16_ascii_gray\n");
5265 
5266   *cancel_save = 0;
5267 
5268   data = malloc(image_info->image_width * 2);
5269 
5270   if (!data)
5271   {
5272    char buf[TEXTBUFSIZE];
5273 
5274     snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM);
5275     xsane_back_gtk_error(buf, TRUE);
5276    return -1; /* error */
5277   }
5278 
5279 #ifdef HAVE_LIBLCMS
5280   if ((apply_ICM_profile) && (hTransform != NULL))
5281   {
5282     DBG(DBG_info, "Doing CMS color conversion\n");
5283 
5284     data_raw = malloc(image_info->image_width * 2);
5285 
5286     if (!data_raw)
5287     {
5288      char buf[TEXTBUFSIZE];
5289 
5290       free(data);
5291 
5292       snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM);
5293       xsane_back_gtk_error(buf, TRUE);
5294      return -1; /* error */
5295     }
5296   }
5297 #endif
5298 
5299   for (y = 0; y < image_info->image_height; y++)
5300   {
5301 #ifdef HAVE_LIBLCMS
5302     if ((apply_ICM_profile) && (hTransform != NULL))
5303     {
5304       bytes_read = fread(data_raw, 2, image_info->image_width, imagefile);
5305       cmsDoTransform(hTransform, data_raw, data, image_info->image_width);
5306     }
5307     else
5308 #endif
5309     {
5310       bytes_read = fread(data, 2, image_info->image_width, imagefile);
5311     }
5312 
5313     for (x = 0; x < image_info->image_width; x++)
5314     {
5315       fprintf(outfile, "%d ", data[x]);
5316 
5317       if (++count >= 10)
5318       {
5319         fprintf(outfile, "\n");
5320 	count = 0;
5321       }
5322     }
5323 
5324     fprintf(outfile, "\n");
5325 
5326     if (ferror(outfile))
5327     {
5328      char buf[TEXTBUFSIZE];
5329 
5330       snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno));
5331       DBG(DBG_error, "%s\n", buf);
5332       xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
5333       *cancel_save = 1;
5334      break;
5335     }
5336 
5337     count = 0;
5338 
5339     xsane_progress_bar_set_fraction(progress_bar, (float) y / image_info->image_height);
5340 
5341     if (*cancel_save)
5342     {
5343       break;
5344     }
5345   }
5346 
5347 #ifdef HAVE_LIBLCMS
5348   if (data_raw)
5349   {
5350     free(data_raw);
5351   }
5352 #endif
5353   free(data);
5354 
5355  return (*cancel_save);
5356 }
5357 
5358 /* ---------------------------------------------------------------------------------------------------------------------- */
5359 
xsane_save_pnm_16_ascii_color(FILE * outfile,FILE * imagefile,Image_info * image_info,cmsHTRANSFORM hTransform,int apply_ICM_profile,GtkProgressBar * progress_bar,int * cancel_save)5360 static int xsane_save_pnm_16_ascii_color(FILE *outfile, FILE *imagefile, Image_info *image_info,
5361                                          cmsHTRANSFORM hTransform, int apply_ICM_profile,
5362                                          GtkProgressBar *progress_bar, int *cancel_save)
5363 {
5364  int x,y;
5365  guint16 *data;
5366  int count = 0;
5367  size_t bytes_read;
5368 #ifdef HAVE_LIBLCMS
5369  guint16 *data_raw = NULL;
5370 #endif
5371 
5372   DBG(DBG_proc, "xsane_save_pnm_16_ascii_color\n");
5373 
5374   *cancel_save = 0;
5375 
5376 
5377   data = malloc(image_info->image_width * 6);
5378 
5379   if (!data)
5380   {
5381    char buf[TEXTBUFSIZE];
5382 
5383     snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM);
5384     xsane_back_gtk_error(buf, TRUE);
5385    return -1; /* error */
5386   }
5387 
5388 #ifdef HAVE_LIBLCMS
5389   if ((apply_ICM_profile) && (hTransform != NULL))
5390   {
5391     DBG(DBG_info, "Doing CMS color conversion\n");
5392 
5393     data_raw = malloc(image_info->image_width * 6);
5394 
5395     if (!data_raw)
5396     {
5397      char buf[TEXTBUFSIZE];
5398 
5399       free(data);
5400 
5401       snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM);
5402       xsane_back_gtk_error(buf, TRUE);
5403      return -1; /* error */
5404     }
5405   }
5406 #endif
5407 
5408   for (y = 0; y < image_info->image_height; y++)
5409   {
5410 #ifdef HAVE_LIBLCMS
5411     if ((apply_ICM_profile) && (hTransform != NULL))
5412     {
5413       bytes_read = fread(data_raw, 6, image_info->image_width, imagefile);
5414       cmsDoTransform(hTransform, data_raw, data, image_info->image_width);
5415     }
5416     else
5417 #endif
5418     {
5419       bytes_read = fread(data, 6, image_info->image_width, imagefile);
5420     }
5421 
5422     for (x = 0; x < image_info->image_width; x++)
5423     {
5424       fprintf(outfile, "%d ", data[3*x+0]);
5425       fprintf(outfile, "%d ", data[3*x+1]);
5426       fprintf(outfile, "%d ", data[3*x+2]);
5427 
5428       if (++count >= 3)
5429       {
5430         fprintf(outfile, "\n");
5431 	count = 0;
5432       }
5433     }
5434     fprintf(outfile, "\n");
5435 
5436     if (ferror(outfile))
5437     {
5438      char buf[TEXTBUFSIZE];
5439 
5440       snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno));
5441       DBG(DBG_error, "%s\n", buf);
5442       xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
5443       *cancel_save = 1;
5444      break;
5445     }
5446 
5447     count = 0;
5448 
5449     xsane_progress_bar_set_fraction(progress_bar, (float) y / image_info->image_height);
5450 
5451     if (*cancel_save)
5452     {
5453       break;
5454     }
5455   }
5456 
5457 #ifdef HAVE_LIBLCMS
5458   if (data_raw)
5459   {
5460     free(data_raw);
5461   }
5462 #endif
5463   free(data);
5464 
5465  return (*cancel_save);
5466 }
5467 
5468 /* ---------------------------------------------------------------------------------------------------------------------- */
5469 
xsane_save_pnm_16_binary_gray(FILE * outfile,FILE * imagefile,Image_info * image_info,cmsHTRANSFORM hTransform,int apply_ICM_profile,GtkProgressBar * progress_bar,int * cancel_save)5470 static int xsane_save_pnm_16_binary_gray(FILE *outfile, FILE *imagefile, Image_info *image_info,
5471                                          cmsHTRANSFORM hTransform, int apply_ICM_profile,
5472                                          GtkProgressBar *progress_bar, int *cancel_save)
5473 {
5474  int x,y;
5475  guint16 *data;
5476  size_t bytes_read;
5477 #ifdef HAVE_LIBLCMS
5478  guint16 *data_raw = NULL;
5479 #endif
5480 
5481   DBG(DBG_proc, "xsane_save_pnm_16_binary_gray\n");
5482 
5483   *cancel_save = 0;
5484 
5485   data = malloc(image_info->image_width * 2);
5486 
5487   if (!data)
5488   {
5489    char buf[TEXTBUFSIZE];
5490 
5491     snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM);
5492     xsane_back_gtk_error(buf, TRUE);
5493    return -1; /* error */
5494   }
5495 
5496 #ifdef HAVE_LIBLCMS
5497   if (hTransform != NULL)
5498   {
5499     DBG(DBG_info, "Doing CMS color conversion\n");
5500 
5501     data_raw = malloc(image_info->image_width * 2);
5502 
5503     if (!data_raw)
5504     {
5505      char buf[TEXTBUFSIZE];
5506 
5507       free(data);
5508 
5509       snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM);
5510       xsane_back_gtk_error(buf, TRUE);
5511      return -1; /* error */
5512     }
5513   }
5514 #endif
5515 
5516   for (y = 0; y < image_info->image_height; y++)
5517   {
5518 #ifdef HAVE_LIBLCMS
5519     if (hTransform != NULL)
5520     {
5521       bytes_read = fread(data_raw, 2, image_info->image_width, imagefile);
5522       cmsDoTransform(hTransform, data_raw, data, image_info->image_width);
5523     }
5524     else
5525 #endif
5526     {
5527       bytes_read = fread(data, 2, image_info->image_width, imagefile);
5528     }
5529 
5530     for (x = 0; x < image_info->image_width; x++)
5531     {
5532       fputc(data[3*x+0] / 256, outfile);
5533       fputc(data[3*x+0] & 255, outfile);
5534     }
5535 
5536     xsane_progress_bar_set_fraction(progress_bar, (float) y / image_info->image_height);
5537 
5538     if (ferror(outfile))
5539     {
5540      char buf[TEXTBUFSIZE];
5541 
5542       snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno));
5543       DBG(DBG_error, "%s\n", buf);
5544       xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
5545       *cancel_save = 1;
5546      break;
5547     }
5548 
5549     if (*cancel_save)
5550     {
5551       break;
5552     }
5553   }
5554 
5555 #ifdef HAVE_LIBLCMS
5556   if (data_raw)
5557   {
5558     free(data_raw);
5559   }
5560 #endif
5561   free(data);
5562 
5563  return (*cancel_save);
5564 }
5565 
5566 /* ---------------------------------------------------------------------------------------------------------------------- */
5567 
xsane_save_pnm_16_binary_color(FILE * outfile,FILE * imagefile,Image_info * image_info,cmsHTRANSFORM hTransform,int apply_ICM_profile,GtkProgressBar * progress_bar,int * cancel_save)5568 static int xsane_save_pnm_16_binary_color(FILE *outfile, FILE *imagefile, Image_info *image_info,
5569                                           cmsHTRANSFORM hTransform, int apply_ICM_profile,
5570                                           GtkProgressBar *progress_bar, int *cancel_save)
5571 {
5572  int x,y;
5573  guint16 *data;
5574  size_t bytes_read;
5575 #ifdef HAVE_LIBLCMS
5576  guint16 *data_raw = NULL;
5577 #endif
5578 
5579   DBG(DBG_proc, "xsane_save_pnm_16_binary_color\n");
5580 
5581   *cancel_save = 0;
5582 
5583   data = malloc(image_info->image_width * 6);
5584 
5585   if (!data)
5586   {
5587    char buf[TEXTBUFSIZE];
5588 
5589     snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM);
5590     xsane_back_gtk_error(buf, TRUE);
5591    return -1; /* error */
5592   }
5593 
5594 #ifdef HAVE_LIBLCMS
5595   if (hTransform != NULL)
5596   {
5597     DBG(DBG_info, "Doing CMS color conversion\n");
5598 
5599     data_raw = malloc(image_info->image_width * 6);
5600 
5601     if (!data_raw)
5602     {
5603      char buf[TEXTBUFSIZE];
5604 
5605       free(data);
5606 
5607       snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM);
5608       xsane_back_gtk_error(buf, TRUE);
5609      return -1; /* error */
5610     }
5611   }
5612 #endif
5613 
5614   for (y = 0; y < image_info->image_height; y++)
5615   {
5616 #ifdef HAVE_LIBLCMS
5617     if (hTransform != NULL)
5618     {
5619       bytes_read = fread(data_raw, 6, image_info->image_width, imagefile);
5620       cmsDoTransform(hTransform, data_raw, data, image_info->image_width);
5621     }
5622     else
5623 #endif
5624     {
5625       bytes_read = fread(data, 6, image_info->image_width, imagefile);
5626     }
5627 
5628     for (x = 0; x < image_info->image_width; x++)
5629     {
5630       fputc(data[3*x+0] / 256, outfile);
5631       fputc(data[3*x+0] & 255, outfile);
5632       fputc(data[3*x+1] / 256, outfile);
5633       fputc(data[3*x+1] & 255, outfile);
5634       fputc(data[3*x+2] / 256, outfile);
5635       fputc(data[3*x+2] & 255, outfile);
5636     }
5637 
5638     xsane_progress_bar_set_fraction(progress_bar, (float) y / image_info->image_height);
5639 
5640     if (ferror(outfile))
5641     {
5642      char buf[TEXTBUFSIZE];
5643 
5644       snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno));
5645       DBG(DBG_error, "%s\n", buf);
5646       xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
5647       *cancel_save = 1;
5648      break;
5649     }
5650 
5651     if (*cancel_save)
5652     {
5653       break;
5654     }
5655   }
5656 
5657 #ifdef HAVE_LIBLCMS
5658   if (data_raw)
5659   {
5660     free(data_raw);
5661   }
5662 #endif
5663   free(data);
5664 
5665  return (*cancel_save);
5666 }
5667 
5668 /* ---------------------------------------------------------------------------------------------------------------------- */
5669 
xsane_save_pnm_8_gray(FILE * outfile,FILE * imagefile,Image_info * image_info,cmsHTRANSFORM hTransform,int apply_ICM_profile,GtkProgressBar * progress_bar,int * cancel_save)5670 static int xsane_save_pnm_8_gray(FILE *outfile, FILE *imagefile, Image_info *image_info,
5671                                  cmsHTRANSFORM hTransform, int apply_ICM_profile,
5672                                  GtkProgressBar *progress_bar, int *cancel_save)
5673 {
5674  int x,y;
5675  guint8 *data;
5676  size_t bytes_read;
5677 #ifdef HAVE_LIBLCMS
5678  guint8 *data_raw = NULL;
5679 #endif
5680 
5681   DBG(DBG_proc, "xsane_save_pnm_8_gray\n");
5682 
5683   *cancel_save = 0;
5684 
5685   data = malloc(image_info->image_width);
5686 
5687   if (!data)
5688   {
5689    char buf[TEXTBUFSIZE];
5690 
5691     snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM);
5692     xsane_back_gtk_error(buf, TRUE);
5693    return -1; /* error */
5694   }
5695 
5696 #ifdef HAVE_LIBLCMS
5697   if (hTransform != NULL)
5698   {
5699     DBG(DBG_info, "Doing CMS color conversion\n");
5700 
5701     data_raw = malloc(image_info->image_width);
5702 
5703     if (!data_raw)
5704     {
5705      char buf[TEXTBUFSIZE];
5706 
5707       free(data);
5708 
5709       snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM);
5710       xsane_back_gtk_error(buf, TRUE);
5711      return -1; /* error */
5712     }
5713   }
5714 #endif
5715 
5716   for (y = 0; y < image_info->image_height; y++)
5717   {
5718 #ifdef HAVE_LIBLCMS
5719     if (hTransform != NULL)
5720     {
5721       bytes_read = fread(data_raw, 1, image_info->image_width, imagefile);
5722       cmsDoTransform(hTransform, data_raw, data, image_info->image_width);
5723     }
5724     else
5725 #endif
5726     {
5727       bytes_read = fread(data, 1, image_info->image_width, imagefile);
5728     }
5729 
5730     for (x = 0; x < image_info->image_width; x++)
5731     {
5732       fputc(data[x], outfile);
5733     }
5734 
5735     xsane_progress_bar_set_fraction(progress_bar, (float) y / image_info->image_height);
5736 
5737     if (ferror(outfile))
5738     {
5739      char buf[TEXTBUFSIZE];
5740 
5741       snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno));
5742       DBG(DBG_error, "%s\n", buf);
5743       xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
5744       *cancel_save = 1;
5745      break;
5746     }
5747 
5748     if (*cancel_save)
5749     {
5750       break;
5751     }
5752   }
5753 
5754 #ifdef HAVE_LIBLCMS
5755   if (data_raw)
5756   {
5757     free(data_raw);
5758   }
5759 #endif
5760   free(data);
5761 
5762  return (*cancel_save);
5763 }
5764 /* ---------------------------------------------------------------------------------------------------------------------- */
5765 
xsane_save_pnm_8_color(FILE * outfile,FILE * imagefile,Image_info * image_info,cmsHTRANSFORM hTransform,int apply_ICM_profile,GtkProgressBar * progress_bar,int * cancel_save)5766 static int xsane_save_pnm_8_color(FILE *outfile, FILE *imagefile, Image_info *image_info,
5767                                   cmsHTRANSFORM hTransform, int apply_ICM_profile,
5768                                   GtkProgressBar *progress_bar, int *cancel_save)
5769 {
5770  int x,y;
5771  guint8 *data;
5772  size_t bytes_read;
5773 #ifdef HAVE_LIBLCMS
5774  guint8 *data_raw = NULL;
5775 #endif
5776 
5777   DBG(DBG_proc, "xsane_save_pnm_8_color\n");
5778 
5779   *cancel_save = 0;
5780 
5781   data = malloc(image_info->image_width * 3);
5782 
5783   if (!data)
5784   {
5785    char buf[TEXTBUFSIZE];
5786 
5787     snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM);
5788     xsane_back_gtk_error(buf, TRUE);
5789    return -1; /* error */
5790   }
5791 
5792 #ifdef HAVE_LIBLCMS
5793   if (hTransform != NULL)
5794   {
5795     DBG(DBG_info, "Doing CMS color conversion\n");
5796 
5797     data_raw = malloc(image_info->image_width * 3);
5798 
5799     if (!data_raw)
5800     {
5801      char buf[TEXTBUFSIZE];
5802 
5803       free(data);
5804 
5805       snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM);
5806       xsane_back_gtk_error(buf, TRUE);
5807      return -1; /* error */
5808     }
5809   }
5810 #endif
5811 
5812   for (y = 0; y < image_info->image_height; y++)
5813   {
5814 #ifdef HAVE_LIBLCMS
5815     if (hTransform != NULL)
5816     {
5817       bytes_read = fread(data_raw, 3, image_info->image_width, imagefile);
5818       cmsDoTransform(hTransform, data_raw, data, image_info->image_width);
5819     }
5820     else
5821 #endif
5822     {
5823       bytes_read = fread(data, 3, image_info->image_width, imagefile);
5824     }
5825 
5826     for (x = 0; x < image_info->image_width; x++)
5827     {
5828       fputc(data[3*x+0], outfile);
5829       fputc(data[3*x+1], outfile);
5830       fputc(data[3*x+2], outfile);
5831     }
5832 
5833     xsane_progress_bar_set_fraction(progress_bar, (float) y / image_info->image_height);
5834 
5835     if (ferror(outfile))
5836     {
5837      char buf[TEXTBUFSIZE];
5838 
5839       snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno));
5840       DBG(DBG_error, "%s\n", buf);
5841       xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
5842       *cancel_save = 1;
5843      break;
5844     }
5845 
5846     if (*cancel_save)
5847     {
5848       break;
5849     }
5850   }
5851 
5852 #ifdef HAVE_LIBLCMS
5853   if (data_raw)
5854   {
5855     free(data_raw);
5856   }
5857 #endif
5858   free(data);
5859 
5860  return (*cancel_save);
5861 }
5862 
5863 /* ---------------------------------------------------------------------------------------------------------------------- */
5864 
xsane_save_pnm_8(FILE * outfile,FILE * imagefile,Image_info * image_info,cmsHTRANSFORM hTransform,int apply_ICM_profile,GtkProgressBar * progress_bar,int * cancel_save)5865 static int xsane_save_pnm_8(FILE *outfile, FILE *imagefile, Image_info *image_info,
5866                             cmsHTRANSFORM hTransform, int apply_ICM_profile,
5867                             GtkProgressBar *progress_bar, int *cancel_save)
5868 {
5869   DBG(DBG_proc, "xsane_save_pnm_8\n");
5870 
5871   *cancel_save = 0;
5872 
5873   xsane_write_pnm_header(outfile, image_info, preferences.save_pnm16_as_ascii);
5874 
5875   if (image_info->channels > 1)
5876   {
5877     xsane_save_pnm_8_color(outfile, imagefile, image_info, hTransform, apply_ICM_profile, progress_bar, cancel_save);
5878   }
5879   else
5880   {
5881     xsane_save_pnm_8_gray(outfile, imagefile, image_info, hTransform, apply_ICM_profile, progress_bar, cancel_save);
5882   }
5883 
5884  return (*cancel_save);
5885 }
5886 
5887 /* ---------------------------------------------------------------------------------------------------------------------- */
5888 
xsane_save_pnm_16(FILE * outfile,FILE * imagefile,Image_info * image_info,cmsHTRANSFORM hTransform,int apply_ICM_profile,GtkProgressBar * progress_bar,int * cancel_save)5889 int xsane_save_pnm_16(FILE *outfile, FILE *imagefile, Image_info *image_info,
5890                       cmsHTRANSFORM hTransform, int apply_ICM_profile,
5891                       GtkProgressBar *progress_bar, int *cancel_save)
5892 {
5893   DBG(DBG_proc, "xsane_save_pnm_16\n");
5894 
5895   *cancel_save = 0;
5896 
5897   xsane_write_pnm_header(outfile, image_info, preferences.save_pnm16_as_ascii);
5898 
5899   if (image_info->channels > 1)
5900   {
5901     if (preferences.save_pnm16_as_ascii)
5902     {
5903       xsane_save_pnm_16_ascii_color(outfile, imagefile, image_info, hTransform, apply_ICM_profile, progress_bar, cancel_save);
5904     }
5905     else
5906     {
5907       xsane_save_pnm_16_binary_color(outfile, imagefile, image_info, hTransform, apply_ICM_profile, progress_bar, cancel_save);
5908     }
5909   }
5910   else
5911   {
5912     if (preferences.save_pnm16_as_ascii)
5913     {
5914       xsane_save_pnm_16_ascii_gray(outfile, imagefile, image_info, hTransform, apply_ICM_profile, progress_bar, cancel_save);
5915     }
5916     else
5917     {
5918       xsane_save_pnm_16_binary_gray(outfile, imagefile, image_info, hTransform, apply_ICM_profile, progress_bar, cancel_save);
5919     }
5920   }
5921 
5922  return (*cancel_save);
5923 }
5924 
5925 /* ---------------------------------------------------------------------------------------------------------------------- */
5926 
5927 /* 0=ok, <0=error, 1=canceled */
xsane_save_image_as_lineart(char * output_filename,char * input_filename,GtkProgressBar * progress_bar,int * cancel_save)5928 int xsane_save_image_as_lineart(char *output_filename, char *input_filename, GtkProgressBar *progress_bar, int *cancel_save)
5929 {
5930  FILE *outfile;
5931  FILE *infile;
5932  char buf[TEXTBUFSIZE];
5933  Image_info image_info;
5934 
5935   *cancel_save = 0;
5936 
5937   outfile = fopen(output_filename, "wb"); /* b = binary mode for win32 */
5938 
5939   if (outfile == 0)
5940   {
5941     snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, output_filename, strerror(errno));
5942     xsane_back_gtk_error(buf, TRUE);
5943    return -2;
5944   }
5945 
5946   infile = fopen(input_filename, "rb"); /* read binary (b for win32) */
5947   if (infile == 0)
5948   {
5949    char buf[TEXTBUFSIZE];
5950     snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, input_filename, strerror(errno));
5951     xsane_back_gtk_error(buf, TRUE);
5952 
5953     fclose(outfile);
5954     remove(output_filename); /* remove already created output file */
5955    return -1;
5956   }
5957 
5958   xsane_read_pnm_header(infile, &image_info);
5959 
5960   xsane_save_grayscale_image_as_lineart(outfile, infile, &image_info, progress_bar, cancel_save);
5961 
5962   fclose(infile);
5963   fclose(outfile);
5964 
5965   if (*cancel_save) /* remove output file if saving has been canceled */
5966   {
5967     remove(output_filename);
5968   }
5969 
5970  return (*cancel_save);
5971 }
5972 
5973 /* ---------------------------------------------------------------------------------------------------------------------- */
5974 
xsane_save_image_as_text(char * output_filename,char * input_filename,GtkProgressBar * progress_bar,int * cancel_save)5975 int xsane_save_image_as_text(char *output_filename, char *input_filename, GtkProgressBar *progress_bar, int *cancel_save)
5976 {
5977  char *arg[1000];
5978  char buf[TEXTBUFSIZE];
5979  int argnr;
5980  pid_t pid;
5981  int i;
5982  int pipefd[2]; /* for progress communication with gocr */
5983  FILE *ocr_progress = NULL;
5984 
5985   DBG(DBG_proc, "xsane_save_image_as_text\n");
5986 
5987   argnr = xsane_parse_options(preferences.ocr_command, arg);
5988 
5989   arg[argnr++] = strdup(preferences.ocr_inputfile_option);
5990   arg[argnr++] = strdup(input_filename);
5991 
5992   arg[argnr++] = strdup(preferences.ocr_outputfile_option);
5993   arg[argnr++] = strdup(output_filename);
5994 
5995   if (preferences.ocr_use_gui_pipe)
5996   {
5997     if (!pipe(pipefd)) /* success */
5998     {
5999       DBG(DBG_info, "xsane_save_image_as_text: created pipe for progress communication\n");
6000 
6001       arg[argnr++] = strdup(preferences.ocr_gui_outfd_option);
6002 
6003       snprintf(buf, sizeof(buf),"%d", pipefd[1]);
6004       arg[argnr++] = strdup(buf);
6005     }
6006     else
6007     {
6008       DBG(DBG_info, "xsane_save_image_as_text: could not create pipe for progress communication\n");
6009       pipefd[0] = 0;
6010       pipefd[1] = 0;
6011     }
6012   }
6013   else
6014   {
6015     DBG(DBG_info, "xsane_save_image_as_text: no pipe for progress communication requested\n");
6016     pipefd[0] = 0;
6017     pipefd[1] = 0;
6018   }
6019 
6020   arg[argnr] = 0;
6021 
6022 #ifndef HAVE_OS2_H
6023   pid = fork();
6024 
6025   if (pid == 0) /* new process */
6026   {
6027    FILE *ipc_file = NULL;
6028 
6029     if (xsane.ipc_pipefd[0]) /* did we create the progress pipe? */
6030     {
6031       close(xsane.ipc_pipefd[0]); /* close reading end of pipe */
6032       ipc_file = fdopen(xsane.ipc_pipefd[1], "w");
6033     }
6034 
6035     if (pipefd[0]) /* did we create the progress pipe? */
6036     {
6037       close(pipefd[0]); /* close reading end of pipe */
6038     }
6039 
6040     DBG(DBG_info, "trying to change user id for new subprocess:\n");
6041     DBG(DBG_info, "old effective uid = %d\n", (int) geteuid());
6042     setuid(getuid());
6043     DBG(DBG_info, "new effective uid = %d\n", (int) geteuid());
6044 
6045 
6046     execvp(arg[0], arg); /* does not return if successfully */
6047     DBG(DBG_error, "%s %s\n", ERR_FAILED_EXEC_OCR_CMD, preferences.ocr_command);
6048 
6049     /* send error message via IPC pipe to parent process */
6050     if (ipc_file)
6051     {
6052       fprintf(ipc_file, "%s %s:\n%s", ERR_FAILED_EXEC_OCR_CMD, preferences.ocr_command, strerror(errno));
6053       fflush(ipc_file); /* make sure message is displayed */
6054       fclose(ipc_file);
6055     }
6056 
6057     _exit(0); /* do not use exit() here! otherwise gtk gets in trouble */
6058   }
6059 
6060 #else
6061   pid = spawnvp(P_NOWAIT, arg[0], arg);
6062   if (pid == -1)
6063   {
6064    DBG(DBG_error, "%s %s\n", ERR_FAILED_EXEC_OCR_CMD, preferences.ocr_command);
6065   }
6066 #endif
6067 
6068   if (pipefd[1])
6069   {
6070     close(pipefd[1]); /* close writing end of pipe */
6071     ocr_progress = fdopen(pipefd[0], "r"); /* open reading end of pipe as file */
6072   }
6073 
6074   for (i=0; i<argnr; i++)
6075   {
6076     free(arg[i]);
6077   }
6078 
6079   if (ocr_progress) /* pipe available */
6080   {
6081     xsane_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress_bar), 0.0);
6082 
6083     while (!feof(ocr_progress))
6084     {
6085      int progress, subprogress;
6086      float fprogress;
6087 
6088       fgets(buf, sizeof(buf), ocr_progress);
6089 
6090       if (!strncmp(preferences.ocr_progress_keyword, buf, strlen(preferences.ocr_progress_keyword)))
6091       {
6092         sscanf(buf + strlen(preferences.ocr_progress_keyword), "%d %d", &progress, &subprogress);
6093 
6094         snprintf(buf, sizeof(buf), "%s (%d:%d)", PROGRESS_OCR, progress, subprogress);
6095         gtk_progress_set_format_string(GTK_PROGRESS(progress_bar), buf);
6096 
6097         fprogress = progress / 100.0;
6098 
6099         if (fprogress < 0.0)
6100         {
6101           fprogress = 0.0;
6102         }
6103 
6104         if (fprogress > 11.0)
6105         {
6106           fprogress = 1.0;
6107         }
6108 
6109         xsane_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress_bar), fprogress);
6110       }
6111 
6112       while (gtk_events_pending())
6113       {
6114         gtk_main_iteration();
6115       }
6116     }
6117 
6118     gtk_progress_set_format_string(GTK_PROGRESS(progress_bar), "");
6119     xsane_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress_bar), 0.0);
6120   }
6121   else /* no pipe available */
6122   {
6123     while (pid)
6124     {
6125      int status = 0;
6126      pid_t pid_status = waitpid(pid, &status, WNOHANG);
6127 
6128       if (pid == pid_status)
6129       {
6130         pid = 0; /* ok, child process has terminated */
6131       }
6132 
6133       while (gtk_events_pending())
6134       {
6135         gtk_main_iteration();
6136       }
6137     }
6138   }
6139 
6140   if (pipefd[0])
6141   {
6142     fclose(ocr_progress); /* close reading end of pipe */
6143   }
6144 
6145  return (*cancel_save);
6146 }
6147 /* ---------------------------------------------------------------------------------------------------------------------- */
6148 
6149 /* save image in destination file format. lineart images that are stored as grayscale image are reduced to lineart! */
xsane_save_image_as(char * output_filename,char * input_filename,int output_format,int apply_ICM_profile,int cms_function,int cms_intent,int cms_bpc,GtkProgressBar * progress_bar,int * cancel_save)6150 int xsane_save_image_as(char *output_filename, char *input_filename, int output_format,
6151                         int apply_ICM_profile, int cms_function, int cms_intent, int cms_bpc,
6152                         GtkProgressBar *progress_bar, int *cancel_save)
6153 {
6154  FILE *outfile;
6155  FILE *infile;
6156  char buf[TEXTBUFSIZE];
6157  Image_info image_info;
6158  char temporary_filename[PATH_MAX];
6159  int remove_input_file = FALSE;
6160  cmsHTRANSFORM hTransform = NULL;
6161 
6162   DBG(DBG_proc, "xsane_save_image_as(output_file=%s, input_file=%s, type=%d)\n", output_filename, input_filename, output_format);
6163 
6164   *cancel_save = 0;
6165 
6166   infile = fopen(input_filename, "rb"); /* read binary (b for win32) */
6167   if (infile == 0)
6168   {
6169    char buf[TEXTBUFSIZE];
6170     snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, input_filename, strerror(errno));
6171     xsane_back_gtk_error(buf, TRUE);
6172 
6173    return -1;
6174   }
6175 
6176   xsane_read_pnm_header(infile, &image_info);
6177 
6178   if ((image_info.reduce_to_lineart) && (output_format != XSANE_PNM))
6179   {
6180     DBG(DBG_info, "original image is a lineart => reduce to lineart\n");
6181     fclose(infile);
6182     xsane_back_gtk_make_path(sizeof(temporary_filename), temporary_filename, 0, 0, "xsane-conversion-", xsane.dev_name, ".pbm", XSANE_PATH_TMP);
6183 
6184     snprintf(buf, sizeof(buf), "%s: %s", PROGRESS_PACKING_DATA, output_filename);
6185 
6186     gtk_progress_set_format_string(GTK_PROGRESS(progress_bar), buf);
6187     xsane_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress_bar), 0.0);
6188 
6189     xsane_save_image_as_lineart(temporary_filename, input_filename, progress_bar, cancel_save);
6190 
6191     input_filename = temporary_filename;
6192     remove_input_file = TRUE;
6193 
6194     infile = fopen(input_filename, "rb"); /* read binary (b for win32) */
6195     if (infile == 0)
6196     {
6197      char buf[TEXTBUFSIZE];
6198       snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, input_filename, strerror(errno));
6199       xsane_back_gtk_error(buf, TRUE);
6200 
6201      return -1;
6202     }
6203 
6204     xsane_read_pnm_header(infile, &image_info);
6205   }
6206 
6207 #ifdef HAVE_LIBLCMS
6208   if (apply_ICM_profile && ((cms_function != XSANE_CMS_FUNCTION_EMBED_SCANNER_ICM_PROFILE) || ((output_format == XSANE_PNM) || (output_format == XSANE_PNM16))))
6209   {
6210     hTransform = xsane_create_cms_transform(&image_info, cms_function, cms_intent, cms_bpc);
6211   }
6212 #endif
6213 
6214   if (1)
6215   {
6216     snprintf(buf, sizeof(buf), "%s: %s", PROGRESS_SAVING_DATA, output_filename);
6217   }
6218   else
6219   {
6220     snprintf(buf, sizeof(buf), "%s", PROGRESS_SAVING_DATA);
6221   }
6222 
6223   gtk_progress_bar_set_ellipsize(GTK_PROGRESS_BAR(progress_bar), PANGO_ELLIPSIZE_START); /* this is new API, can be removed for old GTK versions */
6224   gtk_progress_set_format_string(GTK_PROGRESS(progress_bar), buf);
6225   xsane_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress_bar), 0.0);
6226 
6227 
6228 #ifdef HAVE_LIBTIFF
6229   if (output_format == XSANE_TIFF)		/* routines that want to have filename  for saving */
6230   {
6231    TIFF *tiffile;
6232 
6233     if (xsane_create_secure_file(output_filename)) /* remove possibly existing symbolic links for security */
6234     {
6235       snprintf(buf, sizeof(buf), "%s %s %s\n", ERR_DURING_SAVE, ERR_CREATE_SECURE_FILE, output_filename);
6236       xsane_back_gtk_error(buf, TRUE);
6237      return -1; /* error */
6238     }
6239 
6240     tiffile = TIFFOpen(output_filename, "w");
6241     if (!tiffile)
6242     {
6243       snprintf(buf, sizeof(buf), "%s %s %s\n", ERR_DURING_SAVE, ERR_OPEN_FAILED, output_filename);
6244       xsane_back_gtk_error(buf, TRUE);
6245      return -1; /* error */
6246     }
6247 
6248     xsane_save_tiff_page(tiffile, 0, 0, preferences.jpeg_quality, infile, &image_info, hTransform, apply_ICM_profile, cms_function, progress_bar, cancel_save);
6249 
6250     TIFFClose(tiffile);
6251   }
6252   else							/* routines that want to have filedescriptor for saving */
6253 #endif /* HAVE_LIBTIFF */
6254   {
6255     if (xsane_create_secure_file(output_filename)) /* remove possibly existing symbolic links for security */
6256     {
6257       snprintf(buf, sizeof(buf), "%s %s %s\n", ERR_DURING_SAVE, ERR_CREATE_SECURE_FILE, output_filename);
6258       xsane_back_gtk_error(buf, TRUE);
6259      return -1; /* error */
6260     }
6261 
6262     outfile = fopen(output_filename, "wb"); /* b = binary mode for win32 */
6263 
6264     if (outfile != 0)
6265     {
6266       switch(output_format)
6267       {
6268         case XSANE_PNM:
6269           if (image_info.reduce_to_lineart)
6270           {
6271             xsane_save_grayscale_image_as_lineart(outfile, infile, &image_info, progress_bar, cancel_save);
6272           }
6273           else
6274           {
6275             xsane_save_pnm_8(outfile, infile, &image_info, hTransform, apply_ICM_profile, progress_bar, cancel_save);
6276           }
6277          break;
6278 
6279 #ifdef HAVE_LIBJPEG
6280         case XSANE_JPEG:
6281           xsane_save_jpeg(outfile, preferences.jpeg_quality, infile, &image_info, hTransform, apply_ICM_profile, cms_function, progress_bar, cancel_save);
6282          break; /* switch format == XSANE_JPEG */
6283 #endif
6284 
6285 #ifdef HAVE_LIBPNG
6286 #ifdef HAVE_LIBZ
6287         case XSANE_PNG:
6288           if (image_info.depth <= 8)
6289           {
6290             xsane_save_png(outfile, preferences.png_compression, infile, &image_info, hTransform, apply_ICM_profile, cms_function, progress_bar, cancel_save);
6291           }
6292           else
6293           {
6294             xsane_save_png_16(outfile, preferences.png_compression, infile, &image_info, hTransform, apply_ICM_profile, cms_function, progress_bar, cancel_save);
6295           }
6296          break; /* switch format == XSANE_PNG */
6297 #endif
6298 #endif
6299 
6300         case XSANE_PNM16:
6301           xsane_save_pnm_16(outfile, infile, &image_info, hTransform, apply_ICM_profile, progress_bar, cancel_save);
6302          break; /* switch fomat == XSANE_PNM16 */
6303 
6304         case XSANE_PS: /* save postscript, use original size */
6305         {
6306          float imagewidth, imageheight;
6307 
6308            imagewidth  = 72.0 * image_info.image_width/image_info.resolution_x; /* width in 1/72 inch */
6309            imageheight = 72.0 * image_info.image_height/image_info.resolution_y; /* height in 1/72 inch */
6310 
6311             xsane_save_ps(outfile, infile,
6312                           &image_info,
6313                           imagewidth, imageheight,
6314                           0, /* paper_left_margin */
6315                           0, /* paper_bottom_margin */
6316                           (int) imagewidth, /* paper_width */
6317                           (int) imageheight, /* paper_height */
6318                           0 /* portrait top left */,
6319                           preferences.save_ps_flatedecoded,
6320                           hTransform, apply_ICM_profile,
6321                           (cms_function == XSANE_CMS_FUNCTION_EMBED_SCANNER_ICM_PROFILE), image_info.icm_profile,
6322 		          0, NULL, 0, /* no CRD */
6323 			  0 /* intent */,
6324                           progress_bar,
6325                           cancel_save);
6326         }
6327         break; /* switch format == XSANE_PS */
6328 
6329         case XSANE_PDF: /* save PDF, use original size */
6330         {
6331          float imagewidth, imageheight;
6332 
6333            imagewidth  = 72.0 * image_info.image_width/image_info.resolution_x; /* width in 1/72 inch */
6334            imageheight = 72.0 * image_info.image_height/image_info.resolution_y; /* height in 1/72 inch */
6335 
6336             xsane_save_pdf(outfile, infile,
6337                           &image_info,
6338                           imagewidth, imageheight,
6339                           0, /* paper_left_margin */
6340                           0, /* paper_bottom_margin */
6341                           (int) imagewidth, /* paper_width */
6342                           (int) imageheight, /* paper_height */
6343                           0 /* portrait top left */,
6344                           preferences.save_pdf_flatedecoded,
6345                           hTransform, apply_ICM_profile, cms_function,
6346                           progress_bar,
6347                           cancel_save);
6348         }
6349         break; /* switch format == XSANE_PDF */
6350 
6351         case XSANE_TEXT: /* save as text using ocr program like gocr/jocr */
6352         {
6353           xsane_save_image_as_text(output_filename, input_filename, progress_bar, cancel_save);
6354         }
6355         break; /* switch format == XSANE_TEXT */
6356 
6357         default:
6358           snprintf(buf, sizeof(buf),"%s", ERR_UNKNOWN_SAVING_FORMAT);
6359           xsane_back_gtk_error(buf, TRUE);
6360 
6361           fclose(outfile);
6362           fclose(infile);
6363 
6364           remove(output_filename); /* no usable output: remove output file  */
6365 
6366           if (remove_input_file)
6367           {
6368             remove(input_filename); /* remove lineart pbm file  */
6369           }
6370 
6371           gtk_progress_set_format_string(GTK_PROGRESS(progress_bar), "");
6372           xsane_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress_bar), 0.0);
6373 
6374          return -2;
6375          break; /* switch format == default */
6376       }
6377       fclose(outfile);
6378     }
6379     else
6380     {
6381       snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, output_filename, strerror(errno));
6382       xsane_back_gtk_error(buf, TRUE);
6383 
6384       fclose(infile);
6385 
6386       if (remove_input_file)
6387       {
6388         remove(input_filename); /* remove lineart pbm file  */
6389       }
6390 
6391       gtk_progress_set_format_string(GTK_PROGRESS(progress_bar), "");
6392       xsane_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress_bar), 0.0);
6393 
6394      return -2;
6395     }
6396   }
6397 
6398   fclose (infile);
6399 
6400 #ifdef HAVE_LIBLCMS
6401   if (hTransform != NULL)
6402   {
6403     cmsDeleteTransform(hTransform);
6404   }
6405 #endif
6406 
6407   if (remove_input_file)
6408   {
6409     remove(input_filename); /* remove lineart pbm file  */
6410   }
6411 
6412   if (*cancel_save) /* remove output file if saving has been canceled */
6413   {
6414     remove(output_filename);
6415   }
6416 
6417   gtk_progress_set_format_string(GTK_PROGRESS(progress_bar), "");
6418   xsane_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress_bar), 0.0);
6419 
6420  return (*cancel_save);
6421 }
6422 
6423 /* ---------------------------------------------------------------------------------------------------------------------- */
6424 /* ---------------------------------------------------------------------------------------------------------------------- */
6425 /* ---------------------------------------------------------------------------------------------------------------------- */
6426 /* ---------------------------------------------------------------------------------------------------------------------- */
6427 
6428 #ifdef HAVE_ANY_GIMP
xsane_decode_devname(const char * encoded_devname,int n,char * buf)6429 static int xsane_decode_devname(const char *encoded_devname, int n, char *buf)
6430 {
6431  char *dst, *limit;
6432  const char *src;
6433  char ch, val;
6434 
6435   DBG(DBG_proc, "xsane_decode_devname\n");
6436 
6437   limit = buf + n;
6438   for (src = encoded_devname, dst = buf; *src; ++dst)
6439   {
6440     if (dst >= limit)
6441     {
6442       return -1;
6443     }
6444 
6445     ch = *src++;
6446     /* don't use the ctype.h macros here since we don't want to allow anything non-ASCII here... */
6447     if (ch != '-')
6448     {
6449       *dst = ch;
6450     }
6451     else /* decode */
6452     {
6453       ch = *src++;
6454       if (ch == '-')
6455       {
6456         *dst = ch;
6457       }
6458       else
6459       {
6460         if (ch >= 'a' && ch <= 'f')
6461         {
6462           val = (ch - 'a') + 10;
6463         }
6464         else
6465         {
6466           val = (ch - '0');
6467         }
6468         val <<= 4;
6469 
6470         ch = *src++;
6471         if (ch >= 'a' && ch <= 'f')
6472         {
6473           val |= (ch - 'a') + 10;
6474         }
6475         else
6476         {
6477           val |= (ch - '0');
6478         }
6479 
6480         *dst = val;
6481 
6482         ++src;    /* simply skip terminating '-' for now... */
6483       }
6484     }
6485   }
6486 
6487   if (dst >= limit)
6488   {
6489     return -1;
6490   }
6491 
6492   *dst = '\0';
6493  return 0;
6494 }
6495 
6496 /* ---------------------------------------------------------------------------------------------------------------------- */
6497 
xsane_encode_devname(const char * devname,int n,char * buf)6498 static int xsane_encode_devname(const char *devname, int n, char *buf)
6499 {
6500  static const char hexdigit[] = "0123456789abcdef";
6501  char *dst, *limit;
6502  const char *src;
6503  char ch;
6504 
6505   DBG(DBG_proc, "xsane_encode_devname\n");
6506 
6507   limit = buf + n;
6508   for (src = devname, dst = buf; *src; ++src)
6509   {
6510     if (dst >= limit)
6511     {
6512       return -1;
6513     }
6514 
6515     ch = *src;
6516     /* don't use the ctype.h macros here since we don't want to allow anything non-ASCII here... */
6517     if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
6518     {
6519       *dst++ = ch;
6520     }
6521     else /* encode */
6522     {
6523       if (dst + 4 >= limit)
6524       {
6525         return -1;
6526       }
6527 
6528       *dst++ = '-';
6529       if (ch == '-')
6530       {
6531         *dst++ = '-';
6532       }
6533       else
6534       {
6535         *dst++ = hexdigit[(ch >> 4) & 0x0f];
6536         *dst++ = hexdigit[(ch >> 0) & 0x0f];
6537         *dst++ = '-';
6538       }
6539     }
6540   }
6541 
6542   if (dst >= limit)
6543   {
6544     return -1;
6545   }
6546 
6547   *dst = '\0';
6548  return 0;
6549 }
6550 
6551 /* ---------------------------------------------------------------------------------------------------------------------- */
6552 
xsane_gimp_query(void)6553 static void xsane_gimp_query(void)
6554 {
6555  static GimpParamDef args[] =
6556  {
6557      {GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive"},
6558  };
6559  static GimpParamDef *return_vals = NULL;
6560  static int nargs = sizeof(args) / sizeof(args[0]);
6561  static int nreturn_vals = 0;
6562  char mpath[1024];
6563  char name[1024];
6564  size_t len;
6565  int i, j;
6566 
6567   DBG(DBG_proc, "xsane_gimp_query\n");
6568 
6569   snprintf(name, sizeof(name), "%s", xsane.prog_name);
6570 #ifdef GIMP_CHECK_VERSION
6571 # if GIMP_CHECK_VERSION(1,1,9)
6572   snprintf(mpath, sizeof(mpath), "%s", XSANE_GIMP_MENU_DIALOG);
6573 # else
6574   snprintf(mpath, sizeof(mpath), "%s", XSANE_GIMP_MENU_DIALOG_OLD);
6575 # endif
6576 #else
6577   snprintf(mpath, sizeof(mpath), "%s", XSANE_GIMP_MENU_DIALOG_OLD);
6578 #endif
6579   gimp_install_procedure(name,
6580 			 XSANE_GIMP_INSTALL_BLURB,
6581 			 XSANE_GIMP_INSTALL_HELP,
6582 			 XSANE_AUTHOR,
6583 			 XSANE_COPYRIGHT,
6584 			 XSANE_DATE,
6585 			 mpath,
6586 			 0, /* "RGB, GRAY", */
6587 			 GIMP_EXTENSION,
6588 			 nargs, nreturn_vals,
6589 			 args, return_vals);
6590 
6591   sane_init(&xsane.sane_backend_versioncode, (void *) xsane_authorization_callback);
6592   if (SANE_VERSION_MAJOR(xsane.sane_backend_versioncode) != SANE_V_MAJOR)
6593   {
6594     DBG(DBG_error0, "\n\n"
6595                     "%s %s:\n"
6596                     "  %s\n"
6597                     "  %s %d\n"
6598                     "  %s %d\n"
6599                     "%s\n\n",
6600                      xsane.prog_name, ERR_ERROR,
6601                      ERR_MAJOR_VERSION_NR_CONFLICT,
6602                      ERR_XSANE_MAJOR_VERSION, SANE_V_MAJOR,
6603                      ERR_BACKEND_MAJOR_VERSION, SANE_VERSION_MAJOR(xsane.sane_backend_versioncode),
6604                      ERR_PROGRAM_ABORTED);
6605     return;
6606   }
6607 
6608   sane_get_devices(&xsane.devlist, SANE_FALSE);
6609 
6610   for (i = 0; xsane.devlist[i]; ++i)
6611   {
6612     snprintf(name, sizeof(name), "%s-", xsane.prog_name);
6613     if (xsane_encode_devname(xsane.devlist[i]->name, sizeof(name) - 6, name + 6) < 0)
6614     {
6615       continue;	/* name too long... */
6616     }
6617 
6618 #ifdef GIMP_CHECK_VERSION
6619 # if GIMP_CHECK_VERSION(1,1,9)
6620     snprintf(mpath, sizeof(mpath), "%s", XSANE_GIMP_MENU);
6621 # else
6622     snprintf(mpath, sizeof(mpath), "%s", XSANE_GIMP_MENU_OLD);
6623 # endif
6624 #else
6625     snprintf(mpath, sizeof(mpath), "%s", XSANE_GIMP_MENU_OLD);
6626 #endif
6627     len = strlen(mpath);
6628     for (j = 0; xsane.devlist[i]->name[j]; ++j)
6629     {
6630       if (xsane.devlist[i]->name[j] == '/')
6631       {
6632         mpath[len++] = '\'';
6633       }
6634       else
6635       {
6636         mpath[len++] = xsane.devlist[i]->name[j];
6637       }
6638     }
6639     mpath[len++] = '\0';
6640 
6641     gimp_install_procedure(name,
6642                            XSANE_GIMP_INSTALL_BLURB,
6643                            XSANE_GIMP_INSTALL_HELP,
6644                            XSANE_AUTHOR,
6645                            XSANE_COPYRIGHT,
6646                            XSANE_DATE,
6647                            mpath,
6648                            0, /* "RGB, GRAY", */
6649                            GIMP_EXTENSION,
6650                            nargs, nreturn_vals,
6651                            args, return_vals);
6652   }
6653 
6654   sane_exit();
6655 }
6656 
6657 /* ---------------------------------------------------------------------------------------------------------------------- */
6658 
6659 #ifdef HAVE_GIMP_2
xsane_gimp_run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)6660 static void xsane_gimp_run(const gchar *name, gint nparams, const GimpParam *param, gint *nreturn_vals, GimpParam **return_vals)
6661 {
6662  GimpRunMode run_mode;
6663 #else /* GIMP-1.x */
6664 static void xsane_gimp_run(char *name, int nparams, GimpParam *param, int *nreturn_vals, GimpParam **return_vals)
6665 {
6666  GimpRunModeType run_mode;
6667 #endif
6668 
6669  static GimpParam values[2];
6670  char devname[1024];
6671  char *args[2];
6672  int nargs;
6673 
6674   DBG(DBG_proc, "xsane_gimp_run\n");
6675 
6676   run_mode = param[0].data.d_int32;
6677   xsane.mode = XSANE_GIMP_EXTENSION;
6678   xsane.xsane_mode = XSANE_SAVE;
6679 
6680   *nreturn_vals = 1;
6681   *return_vals = values;
6682 
6683   values[0].type = GIMP_PDB_STATUS;
6684   values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
6685 
6686   nargs = 0;
6687   args[nargs++] = "xsane";
6688 
6689   xsane.selected_dev = -1;
6690   if (strncmp(name, "xsane-", 6) == 0)
6691   {
6692     if (xsane_decode_devname(name + 6, sizeof(devname), devname) < 0)
6693     {
6694       return;			/* name too long */
6695     }
6696     args[nargs++] = devname;
6697   }
6698 
6699   switch (run_mode)
6700   {
6701     case GIMP_RUN_INTERACTIVE:
6702 #ifdef HAVE_GIMP_2
6703       gimp_extension_ack();
6704 #endif
6705       xsane_interface(nargs, args);
6706       values[0].data.d_status = GIMP_PDB_SUCCESS;
6707       break;
6708 
6709     case GIMP_RUN_NONINTERACTIVE:
6710       /*  Make sure all the arguments are there!  */
6711       break;
6712 
6713     case GIMP_RUN_WITH_LAST_VALS:
6714       /*  Possibly retrieve data  */
6715       break;
6716 
6717     default:
6718       break;
6719   }
6720 }
6721 
6722 /* ---------------------------------------------------------------------------------------------------------------------- */
6723 
6724 void null_print_func(gchar *msg)
6725 {
6726 }
6727 
6728 /* ---------------------------------------------------------------------------------------------------------------------- */
6729 /* ---------------------------------------------------------------------------------------------------------------------- */
6730 
6731 int xsane_transfer_to_gimp(char *input_filename, int apply_ICM_profile, int cms_function, GtkProgressBar *progress_bar, int *cancel_save)
6732 {
6733  int remaining;
6734  size_t tile_size;
6735  GimpImageType image_type    = GIMP_GRAY;
6736  GimpImageType drawable_type = GIMP_GRAY_IMAGE;
6737  gint32 layer_ID;
6738  gint32 image_ID;
6739  GimpDrawable *drawable;
6740  guchar *tile;
6741  GimpPixelRgn region;
6742  unsigned tile_offset;
6743  int i, x, y;
6744  Image_info image_info;
6745  FILE *imagefile;
6746  int bytes;
6747  unsigned char *data = NULL;
6748  guint16 *data16 = NULL;
6749  size_t bytes_read;
6750 #ifdef HAVE_LIBLCMS
6751  unsigned char *data_raw = NULL;
6752  cmsHTRANSFORM hTransform = NULL;
6753 #endif
6754 
6755   DBG(DBG_info, "xsane_transer_to_gimp\n");
6756 
6757   *cancel_save = 0;
6758 
6759   imagefile = fopen(input_filename, "rb"); /* read binary (b for win32) */
6760   if (imagefile == 0)
6761   {
6762    char buf[TEXTBUFSIZE];
6763     snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, input_filename, strerror(errno));
6764     xsane_back_gtk_error(buf, TRUE);
6765 
6766    return -1;
6767   }
6768 
6769   xsane_read_pnm_header(imagefile, &image_info);
6770 
6771   if (image_info.depth == 16)
6772   {
6773     bytes = 2;
6774   }
6775   else
6776   {
6777     bytes = 1;
6778   }
6779 
6780   data = malloc(image_info.image_width * 3 * bytes);
6781   data16 = (guint16 *) data;
6782 
6783   if (!data)
6784   {
6785    char buf[TEXTBUFSIZE];
6786 
6787     snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM);
6788     xsane_back_gtk_error(buf, TRUE);
6789    return -1; /* error */
6790   }
6791 
6792 #ifdef HAVE_LIBLCMS
6793   if ((cms_function != XSANE_CMS_FUNCTION_EMBED_SCANNER_ICM_PROFILE)  && apply_ICM_profile && (image_info.depth != 1))
6794   {
6795     hTransform = xsane_create_cms_transform(&image_info, cms_function, preferences.cms_intent, preferences.cms_bpc);
6796   }
6797 
6798   if (hTransform != NULL)
6799   {
6800     DBG(DBG_info, "Doing CMS color conversion\n");
6801 
6802     data_raw = malloc(image_info.image_width * 3 * bytes);
6803 
6804     if (!data_raw)
6805     {
6806      char buf[TEXTBUFSIZE];
6807 
6808       free(data);
6809 
6810       snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, ERR_NO_MEM);
6811       xsane_back_gtk_error(buf, TRUE);
6812      return -1; /* error */
6813     }
6814   }
6815 #endif
6816 
6817   x = 0;
6818   y = 0;
6819   tile_offset = 0;
6820   tile_size = image_info.image_width * gimp_tile_height();
6821 
6822   if (image_info.channels == 3) /* RGB */
6823   {
6824     tile_size *= 3;  /* 24 bits/pixel RGB */
6825     image_type    = GIMP_RGB;
6826     drawable_type = GIMP_RGB_IMAGE;
6827   }
6828   else if (image_info.channels == 4) /* RGBA */
6829   {
6830     tile_size *= 4;  /* 32 bits/pixel RGBA */
6831     image_type    = GIMP_RGB;
6832     drawable_type = GIMP_RGBA_IMAGE; /* interpret infrared as alpha */
6833   }
6834   /* colors == 0/1 is predefined */
6835 
6836   image_ID = gimp_image_new(image_info.image_width, image_info.image_height, image_type);
6837 
6838 #ifdef HAVE_LIBLCMS
6839   if ((cms_function != XSANE_CMS_FUNCTION_CONVERT_TO_SRGB) && apply_ICM_profile) /* embed profile */
6840   {
6841    GimpParasite *parasite;
6842    FILE *icm_profile;
6843    guchar *profile_buffer;
6844    gint32 size;
6845 
6846     DBG(DBG_error, "Opening ICM profile %s\n", image_info.icm_profile);
6847     icm_profile = fopen(image_info.icm_profile, "rb");
6848 
6849     if (icm_profile)
6850     {
6851       fseek(icm_profile, 0, SEEK_END);
6852       size = ftell(icm_profile);
6853       fseek(icm_profile, 0, SEEK_SET);
6854 
6855       profile_buffer = malloc(size);
6856 
6857       if (profile_buffer)
6858       {
6859         if (fread(profile_buffer, 1, size, icm_profile) == size)
6860         {
6861           parasite = gimp_parasite_new("icc-profile", 0, size, profile_buffer);
6862           gimp_image_parasite_attach(image_ID, parasite);
6863           gimp_parasite_free(parasite);
6864         }
6865         else
6866         {
6867           DBG(DBG_error, "can not read profile data\n");
6868         }
6869 
6870         free(profile_buffer);
6871       }
6872       else
6873       {
6874         DBG(DBG_error, "can not allocate profile_buffer\n");
6875       }
6876 
6877       fclose(icm_profile);
6878     }
6879     else
6880     {
6881       DBG(DBG_error, "can not open ICM-profile\n");
6882     }
6883   }
6884 #endif
6885 
6886 
6887 /* the following is supported since gimp-1.1.? */
6888 #ifdef GIMP_HAVE_RESOLUTION_INFO
6889   if (image_info.resolution_x > 0)
6890   {
6891     gimp_image_set_resolution(image_ID, image_info.resolution_x, image_info.resolution_y);
6892   }
6893 /*          gimp_image_set_unit(image_ID, unit?); */
6894 #endif
6895 
6896   layer_ID = gimp_layer_new(image_ID, "Background", image_info.image_width, image_info.image_height, drawable_type, 100.0, GIMP_NORMAL_MODE);
6897   gimp_image_add_layer(image_ID, layer_ID, 0);
6898   drawable = gimp_drawable_get(layer_ID);
6899   gimp_pixel_rgn_init(&region, drawable, 0, 0, drawable->width, drawable->height, TRUE, FALSE);
6900   tile = g_new(guchar, tile_size);
6901 
6902 
6903   if (image_info.channels == 1) /* gray */
6904   {
6905     switch (image_info.depth)
6906     {
6907       case 1: /* 1 bit gray => conversion to 8 bit gray */
6908         for (i = 0; i < ( (image_info.image_width + 7) / 8) * image_info.image_height; ++i)
6909         {
6910          u_char mask;
6911          int j;
6912 
6913           mask = fgetc(imagefile);
6914           for (j = 7; j >= 0; --j)
6915           {
6916             u_char gl = (mask & (1 << j)) ? 0x00 : 0xff;
6917             tile[tile_offset++] = gl;
6918 
6919             x++;
6920 
6921             if (x >= image_info.image_width)
6922             {
6923              int tile_height = gimp_tile_height();
6924 
6925               x = 0;
6926               y++;
6927 
6928               if (y % tile_height == 0)
6929               {
6930                 gimp_pixel_rgn_set_rect(&region, tile, 0, y - tile_height, image_info.image_width, tile_height);
6931                 tile_offset = 0;
6932               }
6933 
6934               xsane_progress_bar_set_fraction(progress_bar, (float) y / image_info.image_height); /* update progress bar */
6935 
6936               break; /* leave for j loop */
6937             }
6938           }
6939 
6940           if (*cancel_save)
6941           {
6942             break;
6943           }
6944         }
6945        break; /* leave switch depth 1 */
6946 
6947       case 8: /* 8 bit gray */
6948         for (y = 1; y <= image_info.image_height; y++)
6949         {
6950          int tile_height = gimp_tile_height();
6951 
6952 #ifdef HAVE_LIBLCMS
6953           if ((cms_function != XSANE_CMS_FUNCTION_EMBED_SCANNER_ICM_PROFILE) && apply_ICM_profile && (hTransform != NULL))
6954           {
6955             bytes_read = fread(data_raw, 1, image_info.image_width, imagefile);
6956             cmsDoTransform(hTransform, data_raw, data, image_info.image_width);
6957           }
6958           else
6959 #endif
6960           {
6961             bytes_read = fread(data, 1, image_info.image_width, imagefile);
6962           }
6963 
6964           for (x = 0; x < image_info.image_width; x++)
6965 	  {
6966             tile[tile_offset++] = data[x];
6967 	  }
6968 
6969           if (y % tile_height == 0)
6970           {
6971             gimp_pixel_rgn_set_rect(&region, tile, 0, y - tile_height, image_info.image_width, tile_height);
6972             tile_offset = 0;
6973           }
6974 
6975           xsane_progress_bar_set_fraction(progress_bar, (float) y / image_info.image_height); /* update progress bar */
6976 
6977           if (*cancel_save)
6978           {
6979             break;
6980           }
6981         }
6982        break; /* case 8 */
6983 
6984 
6985       case 16: /* 16 bit gray has to be reduced to 8 bit */
6986         for (y = 1; y <= image_info.image_height; y++)
6987         {
6988          int tile_height = gimp_tile_height();
6989 
6990 #ifdef HAVE_LIBLCMS
6991           if ((cms_function != XSANE_CMS_FUNCTION_EMBED_SCANNER_ICM_PROFILE) && apply_ICM_profile && (hTransform != NULL))
6992           {
6993             bytes_read = fread(data_raw, 2, image_info.image_width, imagefile);
6994             cmsDoTransform(hTransform, data_raw, data, image_info.image_width);
6995           }
6996           else
6997 #endif
6998           {
6999             bytes_read = fread(data, 2, image_info.image_width, imagefile);
7000           }
7001 
7002           for (x = 0; x < image_info.image_width; x++)
7003 	  {
7004             tile[tile_offset++] = data16[x]/256;
7005 	  }
7006 
7007           if (y % tile_height == 0)
7008           {
7009             gimp_pixel_rgn_set_rect(&region, tile, 0, y - tile_height, image_info.image_width, tile_height);
7010             tile_offset = 0;
7011           }
7012 
7013           xsane_progress_bar_set_fraction(progress_bar, (float) y / image_info.image_height); /* update progress bar */
7014 
7015           if (*cancel_save)
7016           {
7017             break;
7018           }
7019         }
7020        break; /* case 16 */
7021 
7022       default: /* bad depth */
7023        break; /* default */
7024     }
7025   }
7026   else if (image_info.channels == 3) /* RGB */
7027   {
7028     switch (image_info.depth)
7029     {
7030       case 8: /* 8 bit RGB */
7031 
7032         for (y = 1; y <= image_info.image_height; y++)
7033         {
7034          int tile_height = gimp_tile_height();
7035 
7036 #ifdef HAVE_LIBLCMS
7037           if ((cms_function != XSANE_CMS_FUNCTION_EMBED_SCANNER_ICM_PROFILE) && apply_ICM_profile && (hTransform != NULL))
7038           {
7039             bytes_read = fread(data_raw, 3, image_info.image_width, imagefile);
7040             cmsDoTransform(hTransform, data_raw, data, image_info.image_width);
7041           }
7042           else
7043 #endif
7044           {
7045             bytes_read = fread(data, 3, image_info.image_width, imagefile);
7046           }
7047 
7048           for (x = 0; x < image_info.image_width; x++)
7049 	  {
7050             tile[tile_offset++] = data[3*x+0];
7051             tile[tile_offset++] = data[3*x+1];
7052             tile[tile_offset++] = data[3*x+2];
7053 	  }
7054 
7055           if (y % tile_height == 0)
7056           {
7057             gimp_pixel_rgn_set_rect(&region, tile, 0, y - tile_height, image_info.image_width, tile_height);
7058             tile_offset = 0;
7059           }
7060 
7061           xsane_progress_bar_set_fraction(progress_bar, (float) y / image_info.image_height); /* update progress bar */
7062 
7063           if (*cancel_save)
7064           {
7065             break;
7066           }
7067         }
7068        break; /* case 8 */
7069 
7070 
7071       case 16: /* 16 bit RGB has to be reduced to 8 bit */
7072 
7073         for (y = 1; y <= image_info.image_height; y++)
7074         {
7075          int tile_height = gimp_tile_height();
7076 
7077 #ifdef HAVE_LIBLCMS
7078           if ((cms_function != XSANE_CMS_FUNCTION_EMBED_SCANNER_ICM_PROFILE) && apply_ICM_profile && (hTransform != NULL))
7079           {
7080             bytes_read = fread(data_raw, 6, image_info.image_width, imagefile);
7081             cmsDoTransform(hTransform, data_raw, data, image_info.image_width);
7082           }
7083           else
7084 #endif
7085           {
7086             bytes_read = fread(data, 6, image_info.image_width, imagefile);
7087           }
7088 
7089           for (x = 0; x < image_info.image_width; x++)
7090 	  {
7091             tile[tile_offset++] = data16[3*x+0]/256;
7092             tile[tile_offset++] = data16[3*x+1]/256;
7093             tile[tile_offset++] = data16[3*x+2]/256;
7094 	  }
7095 
7096           if (y % tile_height == 0)
7097           {
7098             gimp_pixel_rgn_set_rect(&region, tile, 0, y - tile_height, image_info.image_width, tile_height);
7099             tile_offset = 0;
7100           }
7101 
7102           xsane_progress_bar_set_fraction(progress_bar, (float) y / image_info.image_height); /* update progress bar */
7103 
7104           if (*cancel_save)
7105           {
7106             break;
7107           }
7108         }
7109        break; /* case 16 */
7110 
7111       default: /* bad depth */
7112        break; /* default */
7113     }
7114   }
7115 #ifdef SUPPORT_RGBA
7116   else if (image_info.channels == 4) /* RGBA */
7117   {
7118    int i;
7119 
7120     switch (image_info.depth)
7121     {
7122       case 8: /* 8 bit RGBA */
7123       case 16: /* 16 bit RGBA already has been reduced to 8 bit */
7124         for (i = 0; i < image_info.image_width * image_info.image_height * 4; ++i)
7125         {
7126           tile[tile_offset++] = fgetc(imagefile);
7127           if (tile_offset % 4 == 0)
7128           {
7129             x++;
7130 
7131             if (x >= image_info.image_width)
7132             {
7133              int tile_height = gimp_tile_height();
7134 
7135               x = 0;
7136               y++;
7137 
7138               if (y % tile_height == 0)
7139               {
7140                 gimp_pixel_rgn_set_rect(&region, tile, 0, y - tile_height, image_info.image_width, tile_height);
7141                 tile_offset = 0;
7142               }
7143 
7144               xsane_progress_bar_set_fraction(progress_bar, (float) y / image_info.image_height); /* update progress bar */
7145             }
7146           }
7147 
7148           if (*cancel_save)
7149           {
7150             break;
7151           }
7152         }
7153        break;
7154 
7155       default: /* bad depth */
7156        break;
7157     }
7158   }
7159 #endif
7160 
7161 /* scan_done part */
7162   if (y > image_info.image_height)
7163   {
7164     y = image_info.image_height;
7165   }
7166 
7167   remaining = y % gimp_tile_height();
7168 
7169   if (remaining)
7170   {
7171     gimp_pixel_rgn_set_rect(&region, tile, 0, y - remaining, image_info.image_width, remaining);
7172   }
7173 
7174   gimp_drawable_flush(drawable);
7175   gimp_display_new(image_ID);
7176   gimp_drawable_detach(drawable);
7177   g_free(tile);
7178   tile = 0;
7179 
7180   fclose(imagefile);
7181 
7182 #ifdef HAVE_LIBLCMS
7183   if (hTransform != NULL)
7184   {
7185     cmsDeleteTransform(hTransform);
7186   }
7187 
7188   if (data_raw)
7189   {
7190     free(data_raw);
7191   }
7192 #endif
7193   free(data);
7194 
7195  return 0;
7196 }
7197 #endif /* HAVE_ANY_GIMP */
7198 
7199 /* ---------------------------------------------------------------------------------------------------------------------- */
7200 /* ---------------------------------------------------------------------------------------------------------------------- */
7201 /* ---------------------------------------------------------------------------------------------------------------------- */
7202 /* ---------------------------------------------------------------------------------------------------------------------- */
7203 
7204 #ifdef XSANE_ACTIVATE_EMAIL
7205 
7206 /* character base of base64 coding */
7207 static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
7208 
7209 /* ---------------------------------------------------------------------------------------------------------------------- */
7210 
7211 static void write_3chars_as_base64(unsigned char c1, unsigned char c2, unsigned char c3, int pads, int fd_socket)
7212 {
7213  char buf[4];
7214  ssize_t bytes_written;
7215 
7216   buf[0] = base64[c1>>2]; /* wirte bits 7-2 of first char */
7217   buf[1] = base64[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)]; /* write bits 1,0 of first and bits 7-4 of second char */
7218 
7219   if (pads == 2) /* only one byte used */
7220   {
7221     buf[2] = '='; /* char not used */
7222     buf[3] = '='; /* char not used */
7223   }
7224   else if (pads) /* only two bytes used */
7225   {
7226     buf[2] = base64[((c2 & 0xF) << 2)]; /* write bits 3-0 of second char */
7227     buf[3] = '='; /* char not used */
7228   }
7229   else
7230   {
7231     buf[2] = base64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)]; /* write bits 3-0 of second and bits 7,6 of third char */
7232     buf[3] = base64[c3 & 0x3F]; /* write bits 5-0 of third char as lsb */
7233   }
7234 
7235   bytes_written = write(fd_socket, buf, 4);
7236 }
7237 
7238 /* ---------------------------------------------------------------------------------------------------------------------- */
7239 
7240 void write_string_base64(int fd_socket, char *string, int len)
7241 {
7242  int i;
7243  int pad;
7244  unsigned char c1, c2, c3;
7245  ssize_t bytes_written;
7246 
7247   for (i = 0; i < len; i+=3)
7248   {
7249     c1 = (unsigned char) string[i];
7250     c2 = (unsigned char) string[i+1];
7251     c3 = (unsigned char) string[i+2];
7252 
7253     pad = i - len + 3;
7254 
7255     if (pad < 0)
7256     {
7257       pad = 0;
7258     }
7259     else if (pad)
7260     {
7261       c3 = 0;
7262 
7263       if (pad == 2)
7264       {
7265         c2 = 0;
7266       }
7267     }
7268 
7269     write_3chars_as_base64(c1, c2, c3, pad, fd_socket);
7270   }
7271   bytes_written = write(fd_socket, "\r\n", 2);
7272 }
7273 
7274 /* ---------------------------------------------------------------------------------------------------------------------- */
7275 
7276 void write_base64(int fd_socket, FILE *infile)
7277 {
7278  int c1, c2, c3;
7279  int pos = 0;
7280  ssize_t bytes_written;
7281 
7282   while ((c1 = getc(infile)) != EOF)
7283   {
7284     c2 = getc(infile);
7285     if (c2 == EOF)
7286     {
7287       write_3chars_as_base64(c1, 0, 0, 2, fd_socket);
7288     }
7289     else
7290     {
7291       c3 = getc(infile);
7292       if (c3 == EOF)
7293       {
7294         write_3chars_as_base64(c1, c2, 0, 1, fd_socket);
7295       }
7296       else
7297       {
7298         write_3chars_as_base64(c1, c2, c3, 0, fd_socket);
7299       }
7300     }
7301 
7302     pos += 4;
7303     if (pos > 71)
7304     {
7305       bytes_written = write(fd_socket, "\r\n", 2);
7306 
7307       pos = 0;
7308     }
7309 
7310     xsane.email_progress_bytes += 3;
7311     if ((int)  ((xsane.email_progress_bytes * 100) / xsane.email_progress_size) != (int) (xsane.email_progress_val * 100))
7312     {
7313       xsane.email_progress_val = (float) xsane.email_progress_bytes / xsane.email_progress_size;
7314       xsane_front_gtk_email_project_update_lockfile_status();
7315     }
7316   }
7317 
7318   if (pos)
7319   {
7320     bytes_written = write(fd_socket, "\r\n", 2);
7321   }
7322 
7323   xsane.email_progress_val = 1.0;
7324   xsane_front_gtk_email_project_update_lockfile_status();
7325 }
7326 
7327 /* ---------------------------------------------------------------------------------------------------------------------- */
7328 
7329 void write_email_header(int fd_socket, char *from, char *reply_to, char *to, char *subject, char *boundary, int related)
7330 {
7331  char buf[1024];
7332  ssize_t bytes_written;
7333 
7334   snprintf(buf, sizeof(buf), "From: %s\r\n", from);
7335   bytes_written = write(fd_socket, buf, strlen(buf));
7336 
7337   snprintf(buf, sizeof(buf), "Reply-To: %s\r\n", reply_to);
7338   bytes_written = write(fd_socket, buf, strlen(buf));
7339 
7340   snprintf(buf, sizeof(buf), "To: %s\r\n", to);
7341   bytes_written = write(fd_socket, buf, strlen(buf));
7342 
7343   snprintf(buf, sizeof(buf), "Subject: %s\r\n", subject);
7344   bytes_written = write(fd_socket, buf, strlen(buf));
7345 
7346   snprintf(buf, sizeof(buf), "MIME-Version: 1.0\r\n");
7347   bytes_written = write(fd_socket, buf, strlen(buf));
7348 
7349   if (related) /* related means that we need a special link in the html part to display the image */
7350   {
7351     snprintf(buf, sizeof(buf), "Content-Type: multipart/related;\r\n");
7352     bytes_written = write(fd_socket, buf, strlen(buf));
7353   }
7354   else
7355   {
7356     snprintf(buf, sizeof(buf), "Content-Type: multipart/mixed;\r\n");
7357     bytes_written = write(fd_socket, buf, strlen(buf));
7358   }
7359 
7360   snprintf(buf, sizeof(buf), " boundary=\"%s\"\r\n\r\n", boundary);
7361   bytes_written = write(fd_socket, buf, strlen(buf));
7362 }
7363 
7364 /* ---------------------------------------------------------------------------------------------------------------------- */
7365 
7366 void write_email_footer(int fd_socket, char *boundary)
7367 {
7368  char buf[1024];
7369  ssize_t bytes_written;
7370 
7371   snprintf(buf, sizeof(buf), "--%s--\r\n", boundary);
7372   bytes_written = write(fd_socket, buf, strlen(buf));
7373 }
7374 
7375 /* ---------------------------------------------------------------------------------------------------------------------- */
7376 
7377 void write_email_mime_ascii(int fd_socket, char *boundary)
7378 {
7379  char buf[1024];
7380  ssize_t bytes_written;
7381 
7382   snprintf(buf, sizeof(buf), "--%s\r\n", boundary);
7383   bytes_written = write(fd_socket, buf, strlen(buf));
7384 
7385   snprintf(buf, sizeof(buf), "Content-Type: text/plain;\r\n");
7386   bytes_written = write(fd_socket, buf, strlen(buf));
7387 
7388   snprintf(buf, sizeof(buf), "        charset=\"iso-8859-1\"\r\n");
7389   bytes_written = write(fd_socket, buf, strlen(buf));
7390 
7391   snprintf(buf, sizeof(buf), "Content-Transfer-Encoding: 8bit\r\n\r\n");
7392   bytes_written = write(fd_socket, buf, strlen(buf));
7393 }
7394 
7395 /* ---------------------------------------------------------------------------------------------------------------------- */
7396 
7397 void write_email_mime_html(int fd_socket, char *boundary)
7398 {
7399  char buf[1024];
7400  ssize_t bytes_written;
7401 
7402   snprintf(buf, sizeof(buf), "--%s\r\n", boundary);
7403   bytes_written = write(fd_socket, buf, strlen(buf));
7404 
7405   snprintf(buf, sizeof(buf), "Content-Type: text/html;\r\n");
7406   bytes_written = write(fd_socket, buf, strlen(buf));
7407 
7408   snprintf(buf, sizeof(buf), "        charset=\"us-ascii\"\r\n");
7409   bytes_written = write(fd_socket, buf, strlen(buf));
7410 
7411   snprintf(buf, sizeof(buf), "Content-Transfer-Encoding: 7bit\r\n\r\n");
7412   bytes_written = write(fd_socket, buf, strlen(buf));
7413 
7414   snprintf(buf, sizeof(buf), "<!doctype html public \"-//w3c//dtd html 4.0 transitional//en\">\r\n");
7415   bytes_written = write(fd_socket, buf, strlen(buf));
7416 
7417   snprintf(buf, sizeof(buf), "<html>\r\n");
7418   bytes_written = write(fd_socket, buf, strlen(buf));
7419 }
7420 
7421 /* ---------------------------------------------------------------------------------------------------------------------- */
7422 
7423 void write_email_attach_image(int fd_socket, char *boundary, char *content_id, char *content_type, FILE *infile, char *filename)
7424 {
7425  char buf[1024];
7426  ssize_t bytes_written;
7427 
7428   snprintf(buf, sizeof(buf), "--%s\r\n", boundary);
7429   bytes_written = write(fd_socket, buf, strlen(buf));
7430 
7431   snprintf(buf, sizeof(buf), "Content-Type: %s\r\n", content_type);
7432   bytes_written = write(fd_socket, buf, strlen(buf));
7433 
7434   if (content_id)
7435   {
7436     snprintf(buf, sizeof(buf), "Content-ID: <%s>\r\n", content_id);
7437     bytes_written = write(fd_socket, buf, strlen(buf));
7438   }
7439 
7440   snprintf(buf, sizeof(buf), "Content-Transfer-Encoding: base64\r\n");
7441   bytes_written = write(fd_socket, buf, strlen(buf));
7442 
7443   snprintf(buf, sizeof(buf), "Content-Disposition: inline;\r\n");
7444   bytes_written = write(fd_socket, buf, strlen(buf));
7445 
7446   snprintf(buf, sizeof(buf), "        filename=\"%s\"\r\n", filename);
7447   bytes_written = write(fd_socket, buf, strlen(buf));
7448 
7449   snprintf(buf, sizeof(buf), "\r\n");
7450   bytes_written = write(fd_socket, buf, strlen(buf));
7451 
7452   write_base64(fd_socket, infile);
7453 }
7454 
7455 /* ---------------------------------------------------------------------------------------------------------------------- */
7456 
7457 void write_email_attach_file(int fd_socket, char *boundary, FILE *infile, char *filename)
7458 {
7459  char buf[1024];
7460  ssize_t bytes_written;
7461 
7462   snprintf(buf, sizeof(buf), "--%s\r\n", boundary);
7463   bytes_written = write(fd_socket, buf, strlen(buf));
7464 
7465   snprintf(buf, sizeof(buf), "Content-Type: application/octet-stream\r\n");
7466   bytes_written = write(fd_socket, buf, strlen(buf));
7467 
7468   snprintf(buf, sizeof(buf), "        name=\"%s\"\r\n", filename);
7469   bytes_written = write(fd_socket, buf, strlen(buf));
7470 
7471   snprintf(buf, sizeof(buf), "Content-Transfer-Encoding: base64\r\n");
7472   bytes_written = write(fd_socket, buf, strlen(buf));
7473 
7474   snprintf(buf, sizeof(buf), "Content-Disposition: attachment;\r\n");
7475   bytes_written = write(fd_socket, buf, strlen(buf));
7476 
7477   snprintf(buf, sizeof(buf), "        filename=\"%s\"\r\n", filename);
7478   bytes_written = write(fd_socket, buf, strlen(buf));
7479 
7480   snprintf(buf, sizeof(buf), "\r\n");
7481   bytes_written = write(fd_socket, buf, strlen(buf));
7482 
7483   write_base64(fd_socket, infile);
7484 }
7485 
7486 /* ---------------------------------------------------------------------------------------------------------------------- */
7487 
7488 /* returns fd_socket if sucessfull, < 0 when error occured */
7489 int open_socket(char *server, int port)
7490 {
7491  int fd_socket;
7492  struct sockaddr_in sin;
7493  struct hostent *he;
7494 
7495   he = gethostbyname(server);
7496   if (!he)
7497   {
7498     DBG(DBG_error, "open_socket: Could not get hostname of \"%s\"\n", server);
7499    return -1;
7500   }
7501   else
7502   {
7503     DBG(DBG_info, "open_socket: connecting to \"%s\" = %d.%d.%d.%d\n",
7504         he->h_name,
7505         (unsigned char) he->h_addr_list[0][0],
7506         (unsigned char) he->h_addr_list[0][1],
7507         (unsigned char) he->h_addr_list[0][2],
7508         (unsigned char) he->h_addr_list[0][3]);
7509   }
7510 
7511   if (he->h_addrtype != AF_INET)
7512   {
7513     DBG(DBG_error, "open_socket: Unknown address family: %d\n", he->h_addrtype);
7514    return -1;
7515   }
7516 
7517   fd_socket = socket(AF_INET, SOCK_STREAM, 0);
7518 
7519   if (fd_socket < 0)
7520   {
7521     DBG(DBG_error, "open_socket: Could not create socket: %s\n", strerror(errno));
7522    return -1;
7523   }
7524 
7525 /*  setsockopt (dev->ctl, level, TCP_NODELAY, &on, sizeof (on)); */
7526 
7527   sin.sin_port = htons(port);
7528   sin.sin_family = AF_INET;
7529   memcpy(&sin.sin_addr, he->h_addr_list[0], he->h_length);
7530 
7531   if (connect(fd_socket, &sin, sizeof(sin)))
7532   {
7533     DBG(DBG_error, "open_socket: Could not connect with port %d of socket: %s\n", ntohs(sin.sin_port), strerror(errno));
7534    return -1;
7535   }
7536 
7537   DBG(DBG_info, "open_socket: Connected with port %d\n", ntohs(sin.sin_port));
7538 
7539  return fd_socket;
7540 }
7541 
7542 /* ---------------------------------------------------------------------------------------------------------------------- */
7543 
7544 /* returns 0 if success */
7545 /* not only a write routine, also reads data */
7546 int pop3_login(int fd_socket, char *user, char *passwd)
7547 {
7548  char buf[1024];
7549  int len;
7550  ssize_t bytes_written;
7551 
7552   len = read(fd_socket, buf, sizeof(buf));
7553   if (len >= 0)
7554   {
7555     buf[len] = 0;
7556   }
7557   DBG(DBG_info2, "< %s\n", buf);
7558 
7559   snprintf(buf, sizeof(buf), "USER %s\r\n", user);
7560   DBG(DBG_info2, "> USER xxx\n");
7561   bytes_written = write(fd_socket, buf, strlen(buf));
7562   len = read(fd_socket, buf, sizeof(buf));
7563   if (len >= 0)
7564   {
7565     buf[len] = 0;
7566   }
7567   DBG(DBG_info2, "< %s\n", buf);
7568   if (buf[0] != '+')
7569   {
7570     return -1;
7571   }
7572 
7573   snprintf(buf, sizeof(buf), "PASS %s\r\n", passwd);
7574   DBG(DBG_info2, "> PASS xxx\n");
7575   bytes_written = write(fd_socket, buf, strlen(buf));
7576   len = read(fd_socket, buf, sizeof(buf));
7577   if (len >= 0)
7578   {
7579     buf[len] = 0;
7580   }
7581   DBG(DBG_info2, "< %s\n", buf);
7582   if (buf[0] != '+')
7583   {
7584     return -1;
7585   }
7586 
7587   snprintf(buf, sizeof(buf), "QUIT\r\n");
7588   DBG(DBG_info2, "> QUIT\n");
7589   bytes_written = write(fd_socket, buf, strlen(buf));
7590   len = read(fd_socket, buf, sizeof(buf));
7591   if (len >= 0)
7592   {
7593     buf[len] = 0;
7594   }
7595   DBG(DBG_info2, "< %s\n", buf);
7596 
7597  return 0;
7598 }
7599 
7600 /* ---------------------------------------------------------------------------------------------------------------------- */
7601 
7602 int asmtp_authentication(int fd_socket, int auth_type, char *user, char *passwd)
7603 {
7604  int len;
7605  char buf[1024];
7606  ssize_t bytes_written;
7607 
7608   DBG(DBG_proc, "asmtp_authentication\n");
7609 
7610   switch (auth_type)
7611   {
7612     case EMAIL_AUTH_ASMTP_PLAIN:
7613       snprintf(buf, sizeof(buf), "AUTH PLAIN ");
7614       DBG(DBG_info2, "> %s\\0(USER)\\0(PASSWORD)\n", buf);
7615       bytes_written = write(fd_socket, buf, strlen(buf));
7616       snprintf(buf, sizeof(buf), "%c%s%c%s", 0, user, 0, passwd);
7617       write_string_base64(fd_socket, buf, strlen(user)+strlen(passwd)+2);
7618       len = read(fd_socket, buf, sizeof(buf));
7619       if (len >= 0)
7620       {
7621         buf[len] = 0;
7622       }
7623       DBG(DBG_info2, "< %s", buf);
7624      break;
7625 
7626     case EMAIL_AUTH_ASMTP_LOGIN:
7627       snprintf(buf, sizeof(buf), "AUTH LOGIN\r\n");
7628       DBG(DBG_info2, "> %s", buf);
7629       bytes_written = write(fd_socket, buf, strlen(buf));
7630       len = read(fd_socket, buf, sizeof(buf));
7631       if (len >= 0)
7632       {
7633         buf[len] = 0;
7634       }
7635       DBG(DBG_info2, "< %s", buf);
7636       if (buf[0] != '3')
7637       {
7638         DBG(DBG_info, "=> error\n");
7639        return (-1);
7640       }
7641 
7642       DBG(DBG_info2, "> (USERNAME)\n");
7643       write_string_base64(fd_socket, user, strlen(user));
7644 
7645       len = read(fd_socket, buf, sizeof(buf));
7646       if (len >= 0)
7647       {
7648         buf[len] = 0;
7649       }
7650       DBG(DBG_info2, "< %s", buf);
7651       if (buf[0] != '3')
7652       {
7653         DBG(DBG_info, "=> error\n");
7654        return (-1);
7655       }
7656 
7657       DBG(DBG_info2, "> (PASSWORD)\n");
7658       write_string_base64(fd_socket, passwd, strlen(passwd));
7659 
7660       len = read(fd_socket, buf, sizeof(buf));
7661       if (len >= 0)
7662       {
7663         buf[len] = 0;
7664       }
7665       DBG(DBG_info2, "< %s", buf);
7666       if (buf[0] != '2')
7667       {
7668         DBG(DBG_info, "=> error\n");
7669        return (-1);
7670       }
7671      break;
7672 
7673 #if 0
7674     case EMAIL_AUTH_ASMTP_CRAM_MD5:
7675       snprintf(buf, sizeof(buf), "AUTH CRAM-MD5\r\n");
7676       DBG(DBG_info2, "> %s", buf);
7677       bytes_written = write(fd_socket, buf, strlen(buf));
7678       len = read(fd_socket, buf, sizeof(buf));
7679       if (len >= 0)
7680       {
7681         buf[len] = 0;
7682       }
7683       DBG(DBG_info2, "< %s", buf);
7684      break;
7685 #endif
7686 
7687     default:
7688        DBG(DBG_proc, "no valid asmtp authentication type\n");
7689      break;
7690   }
7691 
7692  return 0;
7693 }
7694 
7695 /* ---------------------------------------------------------------------------------------------------------------------- */
7696 
7697 /* not only a write routine, also reads data */
7698 /* returns -1 on error, 0 when ok */
7699 int write_smtp_header(int fd_socket, char *from, char *to, int auth_type, char *user, char *passwd)
7700 {
7701  char buf[1024];
7702  int len;
7703  char to_line[1024];
7704  char *to_pos = NULL;
7705  char *pos = NULL;
7706  ssize_t bytes_written;
7707 
7708   len = read(fd_socket, buf, sizeof(buf));
7709   if (len >= 0)
7710   {
7711     buf[len] = 0;
7712   }
7713   DBG(DBG_info2, "< %s\n", buf);
7714 
7715   if (auth_type < EMAIL_AUTH_ASMTP_PLAIN)
7716   {
7717     snprintf(buf, sizeof(buf), "HELO localhost\r\n");
7718   }
7719   else
7720   {
7721     snprintf(buf, sizeof(buf), "EHLO localhost\r\n");
7722   }
7723   DBG(DBG_info2, "> %s", buf);
7724   bytes_written = write(fd_socket, buf, strlen(buf));
7725   len = read(fd_socket, buf, sizeof(buf));
7726   if (len >= 0)
7727   {
7728     buf[len] = 0;
7729   }
7730   DBG(DBG_info2, "< %s\n", buf);
7731 
7732   if (buf[0] != '2')
7733   {
7734     DBG(DBG_info, "=> error\n");
7735 
7736     if (xsane.email_status)
7737     {
7738       free(xsane.email_status);
7739     }
7740     xsane.email_status = strdup(TEXT_EMAIL_STATUS_SMTP_CONNECTION_FAILED);
7741     xsane_front_gtk_email_project_update_lockfile_status();
7742    return -1;
7743   }
7744 
7745   if (asmtp_authentication(fd_socket, auth_type, user, passwd))
7746   {
7747     xsane.email_status = strdup(TEXT_EMAIL_STATUS_ASMTP_AUTH_FAILED);
7748     xsane_front_gtk_email_project_update_lockfile_status();
7749    return -1;
7750   }
7751 
7752   while (from[0] == ' ')
7753   {
7754     from = from + 1;
7755   }
7756   snprintf(buf, sizeof(buf), "MAIL FROM: <%s>\r\n", from);
7757   DBG(DBG_info2, "> %s", buf);
7758   bytes_written = write(fd_socket, buf, strlen(buf));
7759   len = read(fd_socket, buf, sizeof(buf));
7760   if (len >= 0)
7761   {
7762     buf[len] = 0;
7763   }
7764   DBG(DBG_info2, "< %s\n", buf);
7765 
7766   if (buf[0] != '2')
7767   {
7768     DBG(DBG_info, "=> error\n");
7769 
7770     if (xsane.email_status)
7771     {
7772       free(xsane.email_status);
7773     }
7774     xsane.email_status = strdup(TEXT_EMAIL_STATUS_SMTP_ERR_FROM);
7775     xsane_front_gtk_email_project_update_lockfile_status();
7776    return -1;
7777   }
7778 
7779 
7780   strncpy(to_line, to, sizeof(to_line)); /* it is not allowed to modify the "to" string, so we make a copy */
7781   to_pos = to_line;
7782   while (to_pos != NULL)
7783   {
7784     while (*to_pos == ' ')
7785     {
7786       to_pos = to_pos + 1;
7787     }
7788     pos = strchr(to_pos, ',');
7789 
7790     if (pos)
7791     {
7792       *pos = 0; /* end of string marker */
7793     }
7794 
7795     snprintf(buf, sizeof(buf), "RCPT TO: <%s>\r\n", to_pos);
7796 
7797     DBG(DBG_info2, "> %s", buf);
7798     bytes_written = write(fd_socket, buf, strlen(buf));
7799     len = read(fd_socket, buf, sizeof(buf));
7800     if (len >= 0)
7801     {
7802       buf[len] = 0;
7803     }
7804     DBG(DBG_info2, "< %s\n", buf);
7805 
7806     if (buf[0] != '2')
7807     {
7808       DBG(DBG_info, "=> error\n");
7809 
7810       if (xsane.email_status)
7811       {
7812         free(xsane.email_status);
7813       }
7814       xsane.email_status = strdup(TEXT_EMAIL_STATUS_SMTP_ERR_RCPT);
7815       xsane_front_gtk_email_project_update_lockfile_status();
7816      return -1;
7817     }
7818 
7819     if (pos)
7820     {
7821       to_pos = pos+1;
7822     }
7823     else
7824     {
7825       to_pos = NULL;
7826     }
7827   }
7828 
7829   snprintf(buf, sizeof(buf), "DATA\r\n");
7830   DBG(DBG_info2, "> %s", buf);
7831   bytes_written = write(fd_socket, buf, strlen(buf));
7832   len = read(fd_socket, buf, sizeof(buf));
7833   if (len >= 0)
7834   {
7835     buf[len] = 0;
7836   }
7837   DBG(DBG_info2, "< %s\n", buf);
7838 
7839   if ((buf[0] != '2') && (buf[0] != '3'))
7840   {
7841     DBG(DBG_info, "=> error\n");
7842 
7843     if (xsane.email_status)
7844     {
7845       free(xsane.email_status);
7846     }
7847     xsane.email_status = strdup(TEXT_EMAIL_STATUS_SMTP_ERR_DATA);
7848     xsane_front_gtk_email_project_update_lockfile_status();
7849    return -1;
7850   }
7851 
7852  return 0;
7853 }
7854 
7855 /* ---------------------------------------------------------------------------------------------------------------------- */
7856 
7857 /* not only a write routine, also reads data */
7858 int write_smtp_footer(int fd_socket)
7859 {
7860  char buf[1024];
7861  int len;
7862  ssize_t bytes_written;
7863 
7864   snprintf(buf, sizeof(buf), "\r\n.\r\n");
7865   DBG(DBG_info2, "> %s", buf);
7866   bytes_written = write(fd_socket, buf, strlen(buf));
7867   len = read(fd_socket, buf, sizeof(buf));
7868   if (len >= 0)
7869   {
7870     buf[len] = 0;
7871   }
7872   DBG(DBG_info2, "< %s\n", buf);
7873 
7874   snprintf(buf, sizeof(buf), "QUIT\r\n");
7875   DBG(DBG_info2, "> %s", buf);
7876   bytes_written = write(fd_socket, buf, strlen(buf));
7877   len = read(fd_socket, buf, sizeof(buf));
7878   if (len >= 0)
7879   {
7880     buf[len] = 0;
7881   }
7882   DBG(DBG_info2, "< %s\n", buf);
7883 
7884  return 0;
7885 }
7886 
7887 #endif
7888 
7889 /* ---------------------------------------------------------------------------------------------------------------------- */
7890