From 58912f68c2489bcee787599837447e0d64dfd61a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Wed, 24 May 2017 21:03:56 +0200 Subject: New upstream version 1.0.27 --- frontend/scanimage.c | 100 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 88 insertions(+), 12 deletions(-) (limited to 'frontend/scanimage.c') diff --git a/frontend/scanimage.c b/frontend/scanimage.c index 7f7c1f0..fe02750 100644 --- a/frontend/scanimage.c +++ b/frontend/scanimage.c @@ -56,6 +56,7 @@ #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" +#include "sicc.h" #include "stiff.h" #include "../include/md5.h" @@ -322,7 +323,7 @@ auth_callback (SANE_String_Const resource, } } -static RETSIGTYPE +static void sighandler (int signum) { static SANE_Bool first_time = SANE_TRUE; @@ -1165,9 +1166,14 @@ write_pnm_header (SANE_Frame format, int width, int height, int depth, FILE *ofp #ifdef HAVE_LIBPNG static void -write_png_header (SANE_Frame format, int width, int height, int depth, FILE *ofp, png_structp* png_ptr, png_infop* info_ptr) +write_png_header (SANE_Frame format, int width, int height, int depth, int dpi, const char * icc_profile, FILE *ofp, png_structp* png_ptr, png_infop* info_ptr) { int color_type; + /* PNG does not have imperial reference units, so we must convert to metric. */ + /* There are nominally 39.3700787401575 inches in a meter. */ + const double pixels_per_meter = dpi * 39.3700787401575; + size_t icc_size = 0; + void *icc_buffer; *png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); @@ -1200,13 +1206,47 @@ write_png_header (SANE_Frame format, int width, int height, int depth, FILE *ofp depth, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + png_set_pHYs(*png_ptr, *info_ptr, + pixels_per_meter, pixels_per_meter, + PNG_RESOLUTION_METER); + + if (icc_profile) + { + icc_buffer = sanei_load_icc_profile(icc_profile, &icc_size); + if (icc_size > 0) + { + /* libpng will abort if the profile and image colour spaces do not match*/ + /* The data colour space field is at bytes 16 to 20 in an ICC profile */ + /* see: ICC.1:2010 ยง 7.2.6 */ + int is_gray_profile = strncmp((char *) icc_buffer + 16, "GRAY", 4) == 0; + int is_rgb_profile = strncmp((char *) icc_buffer + 16, "RGB ", 4) == 0; + if ((is_gray_profile && color_type == PNG_COLOR_TYPE_GRAY) || + (is_rgb_profile && color_type == PNG_COLOR_TYPE_RGB)) + { + png_set_iCCP(*png_ptr, *info_ptr, basename(icc_profile), PNG_COMPRESSION_TYPE_BASE, icc_buffer, icc_size); + } + else + { + if (is_gray_profile) + { + fprintf(stderr, "Ignoring 'GRAY' space ICC profile because the image is RGB.\n"); + } + if (is_rgb_profile) + { + fprintf(stderr, "Ignoring 'RGB ' space ICC profile because the image is Grayscale.\n"); + } + } + free(icc_buffer); + } + } + png_write_info(*png_ptr, *info_ptr); } #endif #ifdef HAVE_LIBJPEG static void -write_jpeg_header (SANE_Frame format, int width, int height, FILE *ofp, struct jpeg_compress_struct *cinfo, struct jpeg_error_mgr *jerr) +write_jpeg_header (SANE_Frame format, int width, int height, int dpi, FILE *ofp, struct jpeg_compress_struct *cinfo, struct jpeg_error_mgr *jerr) { cinfo->err = jpeg_std_error(jerr); jpeg_create_compress(cinfo); @@ -1231,6 +1271,11 @@ write_jpeg_header (SANE_Frame format, int width, int height, FILE *ofp, struct j } jpeg_set_defaults(cinfo); + /* jpeg_set_defaults overrides density, be careful. */ + cinfo->density_unit = 1; /* Inches */ + cinfo->X_density = cinfo->Y_density = dpi; + cinfo->write_JFIF_header = TRUE; + jpeg_set_quality(cinfo, 75, TRUE); jpeg_start_compress(cinfo, TRUE); } @@ -1379,13 +1424,15 @@ scan_it (FILE *ofp) #ifdef HAVE_LIBPNG case OUTPUT_PNG: write_png_header (parm.format, parm.pixels_per_line, - parm.lines, parm.depth, ofp, &png_ptr, &info_ptr); + parm.lines, parm.depth, resolution_value, + icc_profile, ofp, &png_ptr, &info_ptr); break; #endif #ifdef HAVE_LIBJPEG case OUTPUT_JPEG: write_jpeg_header (parm.format, parm.pixels_per_line, - parm.lines, ofp, &cinfo, &jerr); + parm.lines, resolution_value, + ofp, &cinfo, &jerr); break; #endif } @@ -1529,6 +1576,21 @@ scan_it (FILE *ofp) for(j = 0; j < parm.bytes_per_line; j++) pngbuf[j] = ~pngbuf[j]; } +#ifndef WORDS_BIGENDIAN + /* SANE is endian-native, PNG is big-endian, */ + /* see: https://www.w3.org/TR/2003/REC-PNG-20031110/#7Integers-and-byte-order */ + if (parm.depth == 16) + { + int j; + for (j = 0; j < parm.bytes_per_line; j += 2) + { + SANE_Byte LSB; + LSB = pngbuf[j]; + pngbuf[j] = pngbuf[j + 1]; + pngbuf[j + 1] = LSB; + } + } +#endif png_write_row(png_ptr, pngbuf); i += parm.bytes_per_line - pngrow; left -= parm.bytes_per_line - pngrow; @@ -1635,13 +1697,15 @@ scan_it (FILE *ofp) #ifdef HAVE_LIBPNG case OUTPUT_PNG: write_png_header (parm.format, parm.pixels_per_line, - image.height, parm.depth, ofp, &png_ptr, &info_ptr); + image.height, parm.depth, resolution_value, + icc_profile, ofp, &png_ptr, &info_ptr); break; #endif #ifdef HAVE_LIBJPEG case OUTPUT_JPEG: write_jpeg_header (parm.format, parm.pixels_per_line, - parm.lines, ofp, &cinfo, &jerr); + parm.lines, resolution_value, + ofp, &cinfo, &jerr); break; #endif } @@ -2474,9 +2538,16 @@ List of available devices:", prog_name); ofp = stdout; if (batch) - fprintf (stderr, - "Scanning %d pages, incrementing by %d, numbering from %d\n", - batch_count, batch_increment, batch_start_at); + { + fputs("Scanning ", stderr); + if (batch_count == BATCH_COUNT_UNLIMITED) + fputs("infinity", stderr); + else + fprintf(stderr, "%d", batch_count); + fprintf (stderr, + " page%s, incrementing by %d, numbering from %d\n", + batch_count == 1 ? "" : "s", batch_increment, batch_start_at); + } else if(isatty(fileno(ofp))){ fprintf (stderr,"%s: output is not a file, exiting\n", prog_name); @@ -2509,8 +2580,6 @@ List of available devices:", prog_name); if (readbuf2 == NULL) { - fprintf (stderr, "Batch terminated, %d pages scanned\n", - (n - batch_increment)); if (ofp) { fclose (ofp); @@ -2612,6 +2681,13 @@ List of available devices:", prog_name); && (batch_count == BATCH_COUNT_UNLIMITED || --batch_count)) && SANE_STATUS_GOOD == status); + if (batch) + { + int num_pgs = (n - batch_start_at) / batch_increment; + fprintf (stderr, "Batch terminated, %d page%s scanned\n", + num_pgs, num_pgs == 1 ? "" : "s"); + } + if (batch && SANE_STATUS_NO_DOCS == status && (batch_count == BATCH_COUNT_UNLIMITED) -- cgit v1.2.3 From 1687222e1b9e74c89cafbb5910e72d8ec7bfd40f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Wed, 31 Jul 2019 16:59:49 +0200 Subject: New upstream version 1.0.28 --- frontend/scanimage.c | 130 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 115 insertions(+), 15 deletions(-) (limited to 'frontend/scanimage.c') diff --git a/frontend/scanimage.c b/frontend/scanimage.c index fe02750..6906f90 100644 --- a/frontend/scanimage.c +++ b/frontend/scanimage.c @@ -2,7 +2,7 @@ Uses the SANE library. Copyright (C) 2015 Rolf Bensch Copyright (C) 1996, 1997, 1998 Andreas Beck and David Mosberger - + Copyright (C) 1999 - 2009 by the SANE Project -- See AUTHORS and ChangeLog for details. @@ -93,6 +93,7 @@ static struct option basic_options[] = { {"help", no_argument, NULL, 'h'}, {"verbose", no_argument, NULL, 'v'}, {"progress", no_argument, NULL, 'p'}, + {"output-file", required_argument, NULL, 'o'}, {"test", no_argument, NULL, 'T'}, {"all-options", no_argument, NULL, 'A'}, {"version", no_argument, NULL, 'V'}, @@ -111,12 +112,13 @@ static struct option basic_options[] = { {0, 0, NULL, 0} }; -#define OUTPUT_PNM 0 -#define OUTPUT_TIFF 1 -#define OUTPUT_PNG 2 -#define OUTPUT_JPEG 3 +#define OUTPUT_UNKNOWN 0 +#define OUTPUT_PNM 1 +#define OUTPUT_TIFF 2 +#define OUTPUT_PNG 3 +#define OUTPUT_JPEG 4 -#define BASE_OPTSTRING "d:hi:Lf:B::nvVTAbp" +#define BASE_OPTSTRING "d:hi:Lf:o:B::nvVTAbp" #define STRIP_HEIGHT 256 /* # lines we increment image height */ static struct option *all_options; @@ -125,9 +127,10 @@ static int *option_number; static SANE_Handle device; static int verbose; static int progress = 0; +static const char* output_file = NULL; static int test; static int all; -static int output_format = OUTPUT_PNM; +static int output_format = OUTPUT_UNKNOWN; static int help; static int dont_scan = 0; static const char *prog_name; @@ -403,7 +406,7 @@ print_option (SANE_Device * device, int opt_num, const SANE_Option_Descriptor *o }*/ /* if one of these three is not set, option is useless, skip it */ - if(!(opt->cap & + if(!(opt->cap & (SANE_CAP_SOFT_SELECT | SANE_CAP_HARD_SELECT | SANE_CAP_SOFT_DETECT) )){ return; @@ -462,7 +465,7 @@ print_option (SANE_Device * device, int opt_num, const SANE_Option_Descriptor *o if (!strcmp (opt->name, "x")) { printf ("%d..%d", - opt->constraint.range->min, + opt->constraint.range->min, opt->constraint.range->max - tl_x); } else if (!strcmp (opt->name, "y")) @@ -1011,7 +1014,7 @@ set_option (SANE_Handle device, int optnum, void *valuep) prog_name, opt->name); return; } - + if (opt->size == sizeof (SANE_Word) && opt->type != SANE_TYPE_STRING) orig = *(SANE_Word *) valuep; @@ -1483,7 +1486,7 @@ scan_it (FILE *ofp) offset = parm.format - SANE_FRAME_RED; image.x = image.y = 0; } - hundred_percent = parm.bytes_per_line * parm.lines + hundred_percent = parm.bytes_per_line * parm.lines * ((parm.format == SANE_FRAME_RGB || parm.format == SANE_FRAME_GRAY) ? 1:3); while (1) @@ -1968,6 +1971,43 @@ static void print_options(SANE_Device * device, SANE_Int num_dev_options, SANE_B fputc ('\n', stdout); } +static int guess_output_format(const char* output_file) +{ + if (output_file == NULL) + { + fprintf(stderr, "Output format is not set, using pnm as a default.\n"); + return OUTPUT_PNM; + } + + // if the user passes us a path with a known extension then he won't be surprised if we figure + // out correct --format option. No warning is necessary in that case. + const char* extension = strrchr(output_file, '.'); + if (extension != NULL) + { + struct { + const char* extension; + int output_format; + } formats[] = { + { ".pnm", OUTPUT_PNM }, + { ".png", OUTPUT_PNG }, + { ".jpg", OUTPUT_JPEG }, + { ".jpeg", OUTPUT_JPEG }, + { ".tiff", OUTPUT_TIFF }, + { ".tif", OUTPUT_TIFF } + }; + for (unsigned i = 0; i < sizeof(formats) / sizeof(formats[0]); ++i) + { + if (strcmp(extension, formats[i].extension) == 0) + return formats[i].output_format; + } + } + + // it would be very confusing if user makes a typo in the filename and the output format changes. + // This is most likely not what the user wanted. + fprintf(stderr, "Could not guess output format from the given path and no --format given.\n"); + exit(1); +} + int main (int argc, char **argv) { @@ -2033,6 +2073,9 @@ main (int argc, char **argv) case 'p': progress = 1; break; + case 'o': + output_file = optarg; + break; case 'B': if (optarg) buffer_size = 1024 * atoi(optarg); @@ -2088,8 +2131,23 @@ main (int argc, char **argv) exit(1); #endif } - else - output_format = OUTPUT_PNM; + else if (strcmp (optarg, "pnm") == 0) + { + output_format = OUTPUT_PNM; + } + else + { + fprintf(stderr, "Unknown output image format '%s'.\n", optarg); + fprintf(stderr, "Supported formats: pnm, tiff"); +#ifdef HAVE_LIBPNG + fprintf(stderr, ", png"); +#endif +#ifdef HAVE_LIBJPEG + fprintf(stderr, ", jpeg"); +#endif + fprintf(stderr, ".\n"); + exit(1); + } break; case OPTION_MD5: accept_only_md5_auth = 1; @@ -2235,7 +2293,8 @@ Parameters are separated by a blank from single-character options (e.g.\n\ %%m (model), %%t (type), %%i (index number), and\n\ %%n (newline)\n\ -b, --batch[=FORMAT] working in batch mode, FORMAT is `out%%d.pnm' `out%%d.tif'\n\ - `out%%d.png' or `out%%d.jpg' by default depending on --format\n"); + `out%%d.png' or `out%%d.jpg' by default depending on --format\n\ + This option is incompatible with --output-file."); printf ("\ --batch-start=# page number to start naming files with\n\ --batch-count=# how many pages to scan in batch mode\n\ @@ -2247,6 +2306,8 @@ Parameters are separated by a blank from single-character options (e.g.\n\ printf ("\ --accept-md5-only only accept authorization requests using md5\n\ -p, --progress print progress messages\n\ +-o, --output-file=PATH save output to the given file instead of stdout.\n\ + This option is incompatible with --batch.\n\ -n, --dont-scan only set options, don't actually scan\n\ -T, --test test backend thoroughly\n\ -A, --all-options list all available backend options\n\ @@ -2257,6 +2318,15 @@ Parameters are separated by a blank from single-character options (e.g.\n\ -V, --version print version information\n"); } + if (batch && output_file != NULL) + { + fprintf(stderr, "--batch and --output-file can't be used together.\n"); + exit(1); + } + + if (output_format == OUTPUT_UNKNOWN) + output_format = guess_output_format(output_file); + if (!devname) { /* If no device name was specified explicitly, we look at the @@ -2389,6 +2459,7 @@ Parameters are separated by a blank from single-character options (e.g.\n\ case 'd': case 'h': case 'p': + case 'o': case 'v': case 'V': case 'T': @@ -2535,7 +2606,19 @@ List of available devices:", prog_name); } if (!batch) - ofp = stdout; + { + ofp = stdout; + if (output_file != NULL) + { + ofp = fopen(output_file, "w"); + if (ofp == NULL) + { + fprintf(stderr, "%s: could not open input file '%s', " + "exiting\n", prog_name, output_file); + scanimage_exit(1); + } + } + } if (batch) { @@ -2662,6 +2745,14 @@ List of available devices:", prog_name); } } } + else + { + if (output_file && ofp) + { + fclose(ofp); + ofp = NULL; + } + } break; default: if (batch) @@ -2673,6 +2764,15 @@ List of available devices:", prog_name); } unlink (part_path); } + else + { + if (output_file && ofp) + { + fclose(ofp); + ofp = NULL; + } + unlink (output_file); + } break; } /* switch */ n += batch_increment; -- cgit v1.2.3