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(®ion, 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(®ion, 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(®ion, 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(®ion, 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(®ion, 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(®ion, 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(®ion, 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(®ion, 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