Server IP : 172.16.15.8 / Your IP : 18.119.122.69 Web Server : Apache System : Linux zeus.vwu.edu 4.18.0-553.27.1.el8_10.x86_64 #1 SMP Wed Nov 6 14:29:02 UTC 2024 x86_64 User : apache ( 48) PHP Version : 7.2.24 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON Directory (0555) : /bin/ |
[ Home ] | [ C0mmand ] | [ Upload File ] |
---|
#!/usr/libexec/platform-python # -*- coding: utf-8 -*- # # (c) Copyright 2003-2015 HP Development Company, L.P. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Author: Don Welch # Contributors: Sarbeswar Meher # __version__ = '2.2' __mod__ = 'hp-scan' __title__ = 'Scan Utility' __doc__ = "SANE-based scan utility for HPLIP supported all-in-one/mfp devices." # Std Lib import sys import os import os.path import getopt import signal import time import socket import operator import scanext # Local from base.g import * from base.sixext import PY3 from base import tui, device, module, utils, os_utils from prnt import cups from scan import sane username = prop.username r = res = 300 scan_mode = 'gray' tlx = None tly = None brx = None bry = None units = "mm" output = '' dest = [] email_from = '' email_to = [] email_subject = 'hp-scan from %s' % socket.gethostname() email_note = '' resize = 100 contrast = 0 set_contrast = False brightness = 0 set_brightness = False brightness = 0 page_size = '' size_desc = '' page_units = 'mm' default_res = 300 scanner_compression = 'JPEG' adf = False duplex = False dest_printer = None dest_devUri = None PAGE_SIZES = { # in mm '5x7' : (127, 178, "5x7 photo", 'in'), '4x6' : (102, 152, "4x6 photo", 'in'), '3x5' : (76, 127, "3x5 index card", 'in'), 'a2_env' : (111, 146, "A2 Envelope", 'in'), 'a3' : (297, 420, "A3", 'mm'), "a4" : (210, 297, "A4", 'mm'), "a5" : (148, 210, "A5", 'mm'), "a6" : (105, 148, "A6", 'mm'), "b4" : (257, 364, "B4", 'mm'), "b5" : (182, 257, "B5", 'mm'), "c6_env" : (114, 162, "C6 Envelope", 'in'), "dl_env" : (110, 220, "DL Envelope", 'in'), "exec" : (184, 267, "Executive", 'in'), "flsa" : (216, 330, "Flsa", 'mm'), "higaki" : (100, 148, "Hagaki", 'mm'), "japan_env_3" : (120, 235, "Japanese Envelope #3", 'mm'), "japan_env_4" : (90, 205, "Japanese Envelope #4", 'mm'), "legal" : (215, 356, "Legal", 'in'), "letter" : (215, 279, "Letter", 'in'), "no_10_env" : (105, 241, "Number 10 Envelope", 'in'), "oufufu-hagaki" : (148, 200, "Oufuku-Hagaki", 'mm'), "photo" : (102, 152, "Photo", 'in'), "super_b" : (330, 483, "Super B", 'in'), } try: viewer = '' viewer_list = ['kview', 'display', 'gwenview', 'eog', 'kuickshow',] for v in viewer_list: vv = utils.which(v) if vv: viewer = os.path.join(vv, v) break editor = '' editor_list = ['kolourpaint', 'gimp', 'krita', 'cinepaint', 'mirage',] for e in editor_list: ee = utils.which(e) if ee: editor = os.path.join(ee, e) break pdf_viewer = '' pdf_viewer_list = ['kpdf', 'acroread', 'xpdf', 'evince',] for v in pdf_viewer_list: vv = utils.which(v) if vv: pdf_viewer = os.path.join(vv, v) break mod = module.Module(__mod__, __title__, __version__, __doc__, None, (INTERACTIVE_MODE,)) mod.setUsage(module.USAGE_FLAG_DEVICE_ARGS, extra_options=[utils.USAGE_SPACE, ("[OPTIONS] (General)", "", "header", False), ("Scan destinations:", "-s<dest_list> or --dest=<dest_list>", "option", False), ("", "where <dest_list> is a comma separated list containing one or more of: 'file'\*, ", "option", False), ("", "'viewer', 'editor', 'pdf', or 'print'. Use only commas between values, no spaces.", "option", False), ("Scan mode:", "-m<mode> or --mode=<mode>. Where <mode> is 'gray'\*, 'color' or 'lineart'.", "option", False), ("Scanning resolution:", "-r<resolution_in_dpi> or --res=<resolution_in_dpi> or --resolution=<resolution_in_dpi>", "option", False), ("", "where 300 is default.", "option", False), ("Image resize:", "--resize=<scale_in_%> (min=1%, max=400%, default=100%)", "option", False), ("Image contrast:", "-c=<contrast> or --contrast=<contrast>", "option", False), ("", "The contrast range varies from device to device.", "option", False), ("Image brightness:", "-b=<brightness> or --brightness=<brightness>", "option", False), ("", "The brightness range varies from device to device.", "option", False), ("ADF mode:", "--adf (Note, only PDF output is supported when using the ADF)", "option", False), ("", "--duplex or --dup for duplex scanning using ADF.", "option", False), utils.USAGE_SPACE, ("[OPTIONS] (Scan area)", "", "header", False), ("Specify the units for area/box measurements:", "-t<units> or --units=<units>", "option", False), ("", "where <units> is 'mm'\*, 'cm', 'in', 'px', or 'pt' ('mm' is default).", "option", False), ("Scan area:", "-a<tlx>,<tly>,<brx>,<bry> or --area=<tlx>,<tly>,<brx>,<bry>", "option", False), ("", "Coordinates are relative to the upper left corner of the scan area.", "option", False), ("", "Units for tlx, tly, brx, and bry are specified by -t/--units (default is 'mm').", "option", False), ("", "Use only commas between values, no spaces.", "option", False), ("Scan box:", "--box=<tlx>,<tly>,<width>,<height>", "option", False), ("", "tlx and tly coordinates are relative to the upper left corner of the scan area.", "option", False), ("", "Units for tlx, tly, width, and height are specified by -t/--units (default is 'mm').", "option", False), ("", "Use only commas between values, no spaces.", "option", False), ("Top left x of the scan area:", "--tlx=<tlx>", "option", False), ("", "Coordinates are relative to the upper left corner of the scan area.", "option", False), ("", "Units are specified by -t/--units (default is 'mm').", "option", False), ("Top left y of the scan area:", "--tly=<tly>", "option", False), ("", "Coordinates are relative to the upper left corner of the scan area.", "option", False), ("", "Units are specified by -t/--units (default is 'mm').", "option", False), ("Bottom right x of the scan area:", "--brx=<brx>", "option", False), ("", "Coordinates are relative to the upper left corner of the scan area.", "option", False), ("", "Units are specified by -t/--units (default is 'mm').", "option", False), ("Bottom right y of the scan area:", "--bry=<bry>", "option", False), ("", "Coordinates are relative to the upper left corner of the scan area.", "option", False), ("", "Units are specified by -t/--units (default is 'mm').", "option", False), ("Specify the scan area based on a paper size:", "--size=<paper size name>", "option", False), ("", "where <paper size name> is one of: %s" % ', '.join(list(PAGE_SIZES.keys())), "option", False), utils.USAGE_SPACE, ("[OPTIONS] ('file' dest)", "", "header", False), ("Filename for 'file' destination:", "-o<file> or -f<file> or --file=<file> or --output=<file>", "option", False), utils.USAGE_SPACE, ("[OPTIONS] ('pdf' dest)", "", "header", False), ("PDF viewer application:", "--pdf=<pdf_viewer>", "option", False), utils.USAGE_SPACE, ("[OPTIONS] ('viewer' dest)", "", "header", False), ("Image viewer application:", "-v<viewer> or --viewer=<viewer>", "option", False), utils.USAGE_SPACE, ("[OPTIONS] ('editor' dest)", "", "header", False), ("Image editor application:", "-e<editor> or --editor=<editor>", "option", False), utils.USAGE_SPACE, ("[OPTIONS] ('email' dest)", "", "header", False), ("From: address for 'email' dest:", "--email-from=<email_from_address> (required for 'email' dest.)", "option", False), ("To: address for 'email' dest:", "--email-to=<email__to_address> (required for 'email' dest.)", "option", False), ("Email subject for 'email' dest:", '--email-subject="<subject>" or --subject="<subject>"', "option", False), ("", 'Use double quotes (") around the subject if it contains space characters.', "option", False), ("Note or message for the 'email' dest:", '--email-msg="<msg>" or --email-note="<note>"', "option", False), ("", 'Use double quotes (") around the note/message if it contains space characters.', "option", False), utils.USAGE_SPACE, ("[OPTIONS] ('printer' dest)", "", "header", False), ("Printer queue/printer dest:", "--dp=<printer_name> or --dest-printer=<printer_name>", "option", False), ("Printer device-URI dest:", "--dd=<device-uri> or --dest-device=<device-uri>", "option", False), utils.USAGE_SPACE, ("[OPTIONS] (advanced)", "", "header", False), ("Set the scanner compression mode:", "-x<mode> or --compression=<mode>, <mode>='raw', 'none' or 'jpeg' ('jpeg' is default) ('raw' and 'none' are equivalent)", "option", False),], see_also_list=[]) opts, device_uri, printer_name, mode, ui_toolkit, lang = \ mod.parseStdOpts('s:m:r:c:t:a:b:o:v:f:c:x:e:', ['dest=', 'mode=', 'res=', 'resolution=', 'resize=', 'contrast=', 'adf', 'duplex', 'dup', 'unit=', 'units=', 'area=', 'box=', 'tlx=', 'tly=', 'brx=', 'bry=', 'size=', 'file=', 'output=', 'pdf=', 'viewer=', 'email-from=', 'from=', 'email-to=', 'to=', 'email-msg=', 'msg=', 'printer=', 'compression=' , 'raw', 'jpeg', 'color', 'lineart', 'colour', 'bw', 'gray', 'grayscale', 'grey', 'greyscale', 'email-subject=', 'subject=', 'to=', 'from=', 'jpg', 'grey-scale', 'gray-scale', 'about=', 'editor=', 'dp=', 'dest-printer=', 'dd=', 'dest-device=', 'brightness=', ]) sane.init() sane_devices = sane.getDevices() devicelist = {} for d, mfg, mdl, t in sane_devices: try: devicelist[d] except KeyError: devicelist[d] = [mdl] else: devicelist[d].append(mdl) sane.deInit() device_uri = mod.getDeviceUri(device_uri, printer_name, back_end_filter=['hpaio'], filter={'scan-type': (operator.gt, 0)}, devices=devicelist) if not device_uri: sys.exit(1) for o, a in opts: if o in ('-x', '--compression'): a = a.strip().lower() if a in ('jpeg', 'jpg'): scanner_compression = 'JPEG' elif a in ('raw', 'none'): scanner_compression = 'None' else: log.error("Invalid compression value. Valid values are 'jpeg', 'raw', and 'none'.") log.error("Using default value of 'jpeg'.") scanner_compression = 'JPEG' elif o == 'raw': scanner_compression = 'None' elif o == 'jpeg': scanner_compression = 'JPEG' elif o in ('--color', '--colour'): scan_mode = 'color' elif o in ('--lineart', '--line-art', '--bw'): scan_mode = 'lineart' elif o in ('--gray', '--grayscale', '--gray-scale', '--grey', '--greyscale', '--grey-scale'): scan_mode = 'gray' elif o in ('-m', '--mode'): a = a.strip().lower() if a in ('color', 'colour'): scan_mode = 'color' elif a in ('lineart', 'bw', 'b&w'): scan_mode = 'lineart' elif a in ('gray', 'grayscale', 'grey', 'greyscale'): scan_mode = 'gray' else: log.error("Invalid mode. Using default of 'gray'.") log.error("Valid modes are 'color', 'lineart', or 'gray'.") scan_mode = 'gray' elif o in ('--res', '--resolution', '-r'): try: r = int(a.strip()) except ValueError: log.error("Invalid value for resolution.") res = default_res else: res = r elif o in ('-t', '--units', '--unit'): a = a.strip().lower() if a in ('in', 'inch', 'inches'): units = 'in' elif a in ('mm', 'milimeter', 'milimeters', 'millimetre', 'millimetres'): units = 'mm' elif a in ('cm', 'centimeter', 'centimeters', 'centimetre', 'centimetres'): units = 'cm' elif a in ('px', 'pixel', 'pixels', 'pel', 'pels'): units = 'px' elif a in ('pt', 'point', 'points', 'pts'): units = 'pt' else: log.error("Invalid units. Using default of 'mm'.") units = 'mm' elif o == '--tlx': a = a.strip().lower() try: f = float(a) except ValueError: log.error("Invalid value for tlx.") else: tlx = f elif o == '--tly': a = a.strip().lower() try: f = float(a) except ValueError: log.error("Invalid value for tly.") else: tly = f elif o == '--brx': a = a.strip().lower() try: f = float(a) except ValueError: log.error("Invalid value for brx.") else: brx = f elif o == '--bry': a = a.strip().lower() try: f = float(a) except ValueError: log.error("Invalid value for bry.") else: bry = f elif o in ('-a', '--area'): # tlx, tly, brx, bry a = a.strip().lower() try: tlx, tly, brx, bry = a.split(',')[:4] except ValueError: log.error("Invalid scan area. Using defaults.") else: try: tlx = float(tlx) except ValueError: log.error("Invalid value for tlx. Using defaults.") tlx = None try: tly = float(tly) except ValueError: log.error("Invalid value for tly. Using defaults.") tly = None try: brx = float(brx) except ValueError: log.error("Invalid value for brx. Using defaults.") brx = None try: bry = float(bry) except ValueError: log.error("Invalid value for bry. Using defaults.") bry = None elif o == '--box': # tlx, tly, w, h a = a.strip().lower() try: tlx, tly, width, height = a.split(',')[:4] except ValueError: log.error("Invalid scan area. Using defaults.") else: try: tlx = float(tlx) except ValueError: log.error("Invalid value for tlx. Using defaults.") tlx = None try: tly = float(tly) except ValueError: log.error("Invalid value for tly. Using defaults.") tly = None if tlx is not None: try: brx = float(width) + tlx except ValueError: log.error("Invalid value for width. Using defaults.") brx = None else: log.error("Cannot calculate brx since tlx is invalid. Using defaults.") brx = None if tly is not None: try: bry = float(height) + tly except ValueError: log.error("Invalid value for height. Using defaults.") bry = None else: log.error("Cannot calculate bry since tly is invalid. Using defaults.") bry = None elif o == '--size': size = a.strip().lower() if size in PAGE_SIZES: brx, bry, size_desc, page_units = PAGE_SIZES[size] tlx, tly = 0, 0 page_size = size else: log.error("Invalid page size. Valid page sizes are: %s" % ', '.join(list(PAGE_SIZES.keys()))) log.error("Using defaults.") elif o in ('-o', '--output', '-f', '--file'): output = os.path.abspath(os.path.normpath(os.path.expanduser(a.strip()))) try: ext = os.path.splitext(output)[1] except IndexError: log.error("Invalid filename extension.") output = '' if 'file' in dest: dest.remove('file') else: if ext.lower() not in ('.jpg', '.png', '.pdf'): log.error("Only JPG (.jpg), PNG (.png) and PDF (.pdf) output files are supported.") output = '' if 'file' in dest: dest.remove('file') else: if os.path.exists(output): log.warn("Output file '%s' exists. File will be overwritten." % output) if 'file' not in dest: dest.append('file') elif o in ('-s', '--dest', '--destination'): a = a.strip().lower().split(',') for aa in a: aa = aa.strip() if aa in ('file', 'viewer', 'editor', 'print', 'email', 'pdf') \ and aa not in dest: dest.append(aa) elif o in ('--dd', '--dest-device'): dest_devUri = a.strip() if 'print' not in dest: dest.append('print') elif o in ('--dp', '--dest-printer'): dest_printer = a.strip() if 'print' not in dest: dest.append('print') elif o in ('-v', '--viewer'): a = a.strip() b = utils.which(a) if not b: log.error("Viewer application not found.") else: viewer = os.path.join(b, a) if 'viewer' not in dest: dest.append('viewer') elif o in ('-e', '--editor'): a = a.strip() b = utils.which(a) if not b: log.error("Editor application not found.") else: editor = os.path.join(b, a) if 'editor' not in dest: dest.append('editor') elif o == '--pdf': a = a.strip() b = utils.which(a) if not b: log.error("PDF viewer application not found.") else: pdf_viewer = os.path.join(b, a) if 'pdf' not in dest: dest.append('pdf') elif o in ('--email-to', '--to'): email_to = a.split(',') if 'email' not in dest: dest.append('email') elif o in ('--email-from', '--from'): email_from = a if 'email' not in dest: dest.append('email') elif o in ('--email-subject', '--subject', '--about'): email_subject = a if 'email' not in dest: dest.append('email') elif o in ('--email-note', '--email-msg', '--msg', '--message', '--note', '--notes'): email_note = a if 'email' not in dest: dest.append('email') elif o == '--resize': a = a.replace("%", "") try: resize = int(a) except ValueError: resize = 100 log.error("Invalid resize value. Using default of 100%.") elif o in ('-b', '--brightness'): try: set_brightness = True brightness = int(a.strip()) except ValueError: log.error("Invalid brightness value. Using default of 0.") brightness = 0 elif o in ('-c', '--contrast'): try: set_contrast = True contrast = int(a.strip()) except ValueError: log.error("Invalid contrast value. Using default of 0.") contrast = 0 elif o == '--adf': adf = True output_type = 'pdf' elif o in ('--dup', '--duplex'): duplex = True adf = True output_type = 'pdf' if not dest: log.warn("No destinations specified. Adding 'file' destination by default.") dest.append('file') if 'email' in dest and (not email_from or not email_to): log.error("Email specified, but email to and/or email from address(es) were not specified.") log.error("Disabling 'email' destination.") dest.remove("email") if page_size: units = 'mm' if units == 'in': if tlx is not None: tlx = tlx * 25.4 if tly is not None: tly = tly * 25.4 if brx is not None: brx = brx * 25.4 if bry is not None: bry = bry * 25.4 elif units == 'cm': if tlx is not None: tlx = tlx * 10.0 if tly is not None: tly = tly * 10.0 if brx is not None: brx = brx * 10.0 if bry is not None: bry = bry * 10.0 elif units == 'pt': if tlx is not None: tlx = tlx * 0.3528 if tly is not None: tly = tly * 0.3528 if brx is not None: brx = brx * 0.3528 if bry is not None: bry = bry * 0.3528 elif units == 'px': log.warn("Units set to pixels. Using resolution of %ddpi for area calculations." % res) if tlx is not None: tlx = tlx / res * 25.4 if tly is not None: tly = tly / res * 25.4 if brx is not None: brx = brx / res * 25.4 if bry is not None: bry = bry / res * 25.4 if tlx is not None and brx is not None and tlx >= brx: log.error("Invalid values for tlx (%d) and brx (%d) (tlx>=brx). Using defaults." % (tlx, brx)) tlx = brx = None if tly is not None and bry is not None and tly >= bry: log.error("Invalid values for tly (%d) and bry (%d) (tly>=bry). Using defaults." % (tly, bry)) tly = bry = None if not prop.scan_build: log.error("Scanning disabled in build. Exiting") sys.exit(1) if mode == GUI_MODE: log.error("GUI mode is not implemented yet. Refer to 'hp-scan -h' for help.") sys.exit(1) else: # INTERACTIVE_MODE from base.sixext.moves import queue try: import subprocess except ImportError: # Pre-2.4 Python from base import subproc as subprocess try: from PIL import Image except ImportError: log.error("%s requires the Python Imaging Library (PIL). Exiting." % __mod__) if PY3: # Workaround due to incomplete Python3 support in Linux distros. log.notice(log.bold("Manually install the PIL package. More information is available at http://hplipopensource.com/node/369")) sys.exit(1) sane.init() devices = sane.getDevices() # Make sure SANE backend sees the device... for d, mfg, mdl, t in devices: if d == device_uri: break else: log.error("Unable to locate device %s using SANE backend hpaio:. Please check HPLIP installation." % device_uri) sys.exit(1) log.info(log.bold("Using device %s" % device_uri)) log.info("Opening connection to device...") try: device = sane.openDevice(device_uri) except scanext.error as e: sane.reportError(e.args[0]) sys.exit(1) try: source_option = device.getOptionObj("source").constraint log.debug("Supported source Options: %s size=%d" % (source_option,len(source_option))) if source_option is None: log.error("Device doesn't have scanner.") sys.exit(1) except: log.error("Failed to get the source from device.") sys.exit(1) #check if device has only ADF if len(source_option) == 1 and 'ADF' in source_option: log.debug("Device has only ADF support") adf = True if adf: try: if 'ADF' not in source_option: log.error("Failed to set ADF mode. This device doesn't support ADF.") sys.exit(1) else: if duplex == True: if 'Duplex' in source_option: device.setOption("source", "Duplex") else: log.warn("Device doesn't support Duplex scanning. Continuing with Simplex ADF scan.") device.setOption("source", "ADF") else: device.setOption("source", "ADF") device.setOption("batch-scan", True) except scanext.error: log.error("Error in setting ADF mode Duplex=%d." % duplex) sys.exit(1) else: try: device.setOption("source", "Flatbed") device.setOption("batch-scan", False) except scanext.error: log.debug("Error setting source or batch-scan option (this is probably OK).") tlx = device.getOptionObj('tl-x').limitAndSet(tlx) tly = device.getOptionObj('tl-y').limitAndSet(tly) brx = device.getOptionObj('br-x').limitAndSet(brx) bry = device.getOptionObj('br-y').limitAndSet(bry) scan_area = (brx - tlx) * (bry - tly) # mm^2 valid_res = device.getOptionObj('resolution').constraint log.debug("Device supported resolutions %s" % (valid_res,)) if 0 in valid_res: #min-max range in tuple if res < valid_res[0] or res > valid_res[1]: log.warn("Invalid resolution. Using closest valid resolution of %d dpi" % res) if res < valid_res[0]: res = valid_res[0] elif res > valid_res[1]: res = valid_res[1] else: if res not in valid_res: log.warn("Invalid resolution. Using closest valid resolution of %d dpi" % res) log.warn("Valid resolutions are %s dpi." % ', '.join([str(x) for x in valid_res])) res = valid_res[0] min_dist = sys.maxsize for x in valid_res: if abs(r-x) < min_dist: min_dist = abs(r-x) res = x res = device.getOptionObj('resolution').limitAndSet(res) scan_px = scan_area * res * res / 645.16 # res is in DPI if scan_mode == 'color': scan_size = scan_px * 3 # 3 bytes/px elif scan_mode == 'gray': scan_size = scan_px # 1 byte/px else: # lineart scan_size = scan_px // 8 if scan_size > 52428800: # 50MB if res > 600: log.warn("Using resolutions greater than 600 dpi will cause very large files to be created.") else: log.warn("The scan current parameters will cause very large files to be created.") log.warn("This can cause the scan to take a long time to complete and may cause your system to slow down.") log.warn("Approx. number of bytes to read from scanner: %s" % utils.format_bytes(scan_size, True)) device.setOption('compression', scanner_compression) if set_contrast: valid_contrast = device.getOptionObj('contrast').constraint if contrast >= int(valid_contrast[0]) and contrast <= int(valid_contrast[1]): contrast = device.getOptionObj('contrast').limitAndSet(contrast) else: log.warn("Invalid contrast. Contrast range is (%d, %d). Using closest valid contrast of %d " % (int(valid_contrast[0]), int(valid_contrast[1]), contrast)) if contrast < int(valid_contrast[0]): contrast = int(valid_contrast[0]) elif contrast > int(valid_contrast[1]): contrast = int(valid_contrast[1]) device.setOption('contrast', contrast) if set_brightness: valid_brightness = device.getOptionObj('brightness').constraint if brightness >= int(valid_brightness[0]) and brightness <= int(valid_brightness[1]): brightness = device.getOptionObj('brightness').limitAndSet(brightness) else: log.warn("Invalid brightness. Brightness range is (%d, %d). Using closest valid brightness of %d " % (int(valid_brightness[0]), int(valid_brightness[1]), brightness)) if brightness < int(valid_brightness[0]): brightness = int(valid_brightness[0]) elif brightness > int(valid_brightness[1]): brightness = int(valid_brightness[1]) device.setOption('brightness', brightness) if brx - tlx <= 0.0 or bry - tly <= 0.0: log.error("Invalid scan area (width or height is negative).") sys.exit(1) log.info("") log.info("Resolution: %ddpi" % res) log.info("Mode: %s" % scan_mode) log.info("Compression: %s" % scanner_compression) if(set_contrast): log.info("Contrast: %d" % contrast) if(set_brightness): log.info("Brightness: %d" % brightness) if units == 'mm': log.info("Scan area (mm):") log.info(" Top left (x,y): (%fmm, %fmm)" % (tlx, tly)) log.info(" Bottom right (x,y): (%fmm, %fmm)" % (brx, bry)) log.info(" Width: %fmm" % (brx - tlx)) log.info(" Height: %fmm" % (bry - tly)) if page_size: units = page_units # for display purposes only log.info("Page size: %s" % size_desc) if units != 'mm': log.note("This scan area below in '%s' units may not be exact due to rounding errors." % units) if units == 'in': log.info("Scan area (in):") log.info(" Top left (x,y): (%fin, %fin)" % (tlx/25.4, tly/25.4)) log.info(" Bottom right (x,y): (%fin, %fin)" % (brx/25.4, bry/25.4)) log.info(" Width: %fin" % ((brx - tlx)/25.4)) log.info(" Height: %fin" % ((bry - tly)/25.4)) elif units == 'cm': log.info("Scan area (cm):") log.info(" Top left (x,y): (%fcm, %fcm)" % (tlx/10.0, tly/10.0)) log.info(" Bottom right (x,y): (%fcm, %fcm)" % (brx/10.0, bry/10.0)) log.info(" Width: %fcm" % ((brx - tlx)/10.0)) log.info(" Height: %fcm" % ((bry - tly)/10.0)) elif units == 'px': log.info("Scan area (px @ %ddpi):" % res) log.info(" Top left (x,y): (%fpx, %fpx)" % (tlx*res/25.4, tly*res/25.4)) log.info(" Bottom right (x,y): (%fpx, %fpx)" % (brx*res/25.4, bry*res/25.4)) log.info(" Width: %fpx" % ((brx - tlx)*res/25.4)) log.info(" Height: %fpx" % ((bry - tly)*res/25.4)) elif units == 'pt': log.info("Scan area (pt):") log.info(" Top left (x,y): (%fpt, %fpt)" % (tlx/0.3528, tly/0.3528)) log.info(" Bottom right (x,y): (%fpt, %fpt)" % (brx/0.3528, bry/0.3528)) log.info(" Width: %fpt" % ((brx - tlx)/0.3528)) log.info(" Height: %fpt" % ((bry - tly)/0.3528)) log.info("Destination(s): %s" % ', '.join(dest)) if 'file' in dest: log.info("Output file: %s" % output) update_queue = queue.Queue() event_queue = queue.Queue() available_scan_mode = device.getOptionObj("mode").constraint available_scan_mode = [x.lower() for x in available_scan_mode] log.debug("Supported modes: %s size=%d" % (available_scan_mode,len(available_scan_mode))) if scan_mode.lower() not in available_scan_mode: log.warn("Device doesn't support %s mode. Continuing with %s mode."%(scan_mode,available_scan_mode[0])) scan_mode = available_scan_mode[0] device.setOption("mode", scan_mode) #For some devices, resolution is changed when we set 'source'. #Hence we need to set resolution here, after setting the 'source' device.setOption("resolution", res) if 'file' in dest and not output: log.warn("File destination enabled with no output file specified.") if adf: log.info("Setting output format to PDF for ADF mode.") output = utils.createSequencedFilename("hpscan", ".pdf") output_type = 'pdf' else: if scan_mode == 'gray': log.info("Setting output format to PNG for greyscale mode.") output = utils.createSequencedFilename("hpscan", ".png") output_type = 'png' else: log.info("Setting output format to JPEG for color/lineart mode.") output = utils.createSequencedFilename("hpscan", ".jpg") output_type = 'jpeg' log.warn("Defaulting to '%s'." % output) else: try: output_type = os.path.splitext(output)[1].lower()[1:] if output_type == 'jpg': output_type = 'jpeg' except IndexError: output_type = '' if output_type and output_type not in ('jpeg', 'png', 'pdf'): log.error("Invalid output file format. File formats must be 'jpeg', 'png', or 'pdf'.") sys.exit(1) if adf and output_type and output_type != 'pdf': log.error("ADF scans must be saved in PDF file format.") sys.exit(1) log.info("\nWarming up...") no_docs = False page = 1 adf_page_files = [] #adf_pages = [] cleanup_spinner() log.info("") try: while True: if adf: log.info("\nPage %d: Scanning..." % page) else: log.info("\nScanning...") bytes_read = 0 try: try: ok, expected_bytes, status = device.startScan("RGBA", update_queue, event_queue) # Note: On some scanners (Marvell) expected_bytes will be < 0 (if lines == -1) log.debug("expected_bytes = %d" % expected_bytes) except scanext.error as e: sane.reportError(e.args[0]) sys.exit(1) except KeyboardInterrupt: log.error("Aborted.") device.cancelScan() sys.exit(1) if adf and status == scanext.SANE_STATUS_NO_DOCS: if page-1 == 0: log.error("No document(s). Please load documents and try again.") sys.exit(0) else: log.info("Out of documents. Scanned %d pages total." % (page-1)) no_docs = True break if expected_bytes > 0: if adf: log.debug("Expecting to read %s from scanner (per page)." % utils.format_bytes(expected_bytes)) else: log.debug("Expecting to read %s from scanner." % utils.format_bytes(expected_bytes)) device.waitForScanActive() pm = tui.ProgressMeter("Reading data:") while device.isScanActive(): while update_queue.qsize(): try: status, bytes_read = update_queue.get(0) if not log.is_debug(): if expected_bytes > 0: pm.update(int(100*bytes_read/expected_bytes), utils.format_bytes(bytes_read)) else: pm.update(0, utils.format_bytes(bytes_read)) if status != scanext.SANE_STATUS_GOOD: log.error("Error in reading data. Status=%d bytes_read=%d." % (status, bytes_read)) sys.exit(1) except queue.Empty: break time.sleep(0.5) except KeyboardInterrupt: log.error("Aborted.") device.cancelScan() sys.exit(1) # Make sure queue is cleared out... while update_queue.qsize(): status, bytes_read = update_queue.get(0) if not log.is_debug(): if expected_bytes > 0: pm.update(int(100*bytes_read/expected_bytes), utils.format_bytes(bytes_read)) else: pm.update(0, utils.format_bytes(bytes_read)) # For Marvell devices, making scan progress bar to 100% if bytes_read and bytes_read != expected_bytes: pm.update(int(100),utils.format_bytes(bytes_read)) log.info("") if bytes_read: log.info("Read %s from scanner." % utils.format_bytes(bytes_read)) buffer, format, format_name, pixels_per_line, \ lines, depth, bytes_per_line, pad_bytes, total_read, total_write = device.getScan() log.debug("PPL=%d lines=%d depth=%d BPL=%d pad=%d total_read=%d total_write=%d" % (pixels_per_line, lines, depth, bytes_per_line, pad_bytes, total_read, total_write)) #For Marvell devices, expected bytes is not same as total_read if lines == -1 or total_read != expected_bytes: lines = int(total_read / bytes_per_line) if scan_mode in ('color', 'gray'): try: im = Image.frombuffer('RGBA', (pixels_per_line, lines), buffer.read(), 'raw', 'RGBA', 0, 1) except ValueError: log.error("Did not read enough data from scanner (I/O Error?)") sys.exit(1) elif scan_mode == 'lineart': try: pixels_per_line = bytes_per_line * 8 # Calculation of pixels_per_line for Lineart must be 8 time of bytes_per_line # Otherwise, scanned image will be corrupted (slanted) im = Image.frombuffer('RGBA', (pixels_per_line, lines), buffer.read(), 'raw', 'RGBA', 0, 1).convert('L') except ValueError: log.error("Did not read enough data from scanner (I/O Error?)") sys.exit(1) if adf or output_type == 'pdf': temp_output = utils.createSequencedFilename("hpscan_pg%d_" % page, ".png") adf_page_files.append(temp_output) im.save(temp_output) #log.debug("Saved page %d to file %s" % (page, temp_output)) else: log.error("No data read.") sys.exit(1) if not adf or (adf and no_docs): break page += 1 finally: log.info("Closing device.") device.cancelScan() if adf or output_type == 'pdf': try: from reportlab.pdfgen import canvas except ImportError: log.error("PDF output requires ReportLab.") sys.exit(1) if not output: output = utils.createSequencedFilename("hpscan", ".pdf") c = canvas.Canvas(output, (brx/0.3528, bry/0.3528)) for p in adf_page_files: #log.info("Processing page %s..." % p) image = Image.open(p) try: c.drawInlineImage(image, (tlx/0.3528), (tly/0.3528), ((brx-tlx)/0.3528),((bry-tly)/0.3528)) except NameError: log.error("A problem has occurred with PDF generation. This is a known bug in ReportLab. Please update your install of ReportLab to version 2.0 or greater.") sys.exit(1) except AssertionError as e: log.error(e) if PY3: log.note("You might be running an older version of reportlab. Please update to the latest version") log.note("More information is available at http://hplipopensource.com/node/369") sys.exit(1) except Exception as e: log.error(e) log.note("Try Updating to reportlab version >= 3.2") sys.exit(1) c.showPage() os.unlink(p) log.info("Saving to file %s" % output) c.save() log.info("Viewing PDF file in %s" % pdf_viewer) cmd = "%s %s &" % (pdf_viewer, output) os_utils.execute(cmd) sys.exit(0) if resize != 100: if resize < 1 or resize > 400: log.error("Resize parameter is incorrect. Resize must be 0% < resize < 400%.") log.error("Using resize value of 100%.") else: new_w = int(pixels_per_line * resize / 100) new_h = int(lines * resize / 100) log.info("Resizing image from %dx%d to %dx%d..." % (pixels_per_line, lines, new_w, new_h)) im = im.resize((new_w, new_h), Image.ANTIALIAS) file_saved = False if 'file' in dest: log.info("\nOutputting to destination 'file':") log.info("Saving to file %s" % output) try: im.save(output) except IOError as e: im = im.convert("RGB") try: im.save(output) except IOError as e: log.error("Error saving file: %s (I/O)" % e) try: os.remove(output) except OSError: pass sys.exit(1) except ValueError as e: log.error("Error saving file: %s (PIL)" % e) try: os.remove(output) except OSError: pass sys.exit(1) file_saved = True dest.remove("file") temp_saved = False if ('editor' in dest or 'viewer' in dest or 'email' in dest or 'print' in dest) \ and not file_saved: output_fd, output = utils.make_temp_file(dir='/var/tmp', suffix='.png') try: im.save(output) except IOError as e: log.error("Error saving temporary file: %s" % e) try: os.remove(output) except OSError: pass sys.exit(1) os.close(output_fd) temp_saved = True for d in dest: log.info("\nSending to destination '%s':" % d) if d == 'pdf': try: from reportlab.pdfgen import canvas except ImportError: log.error("PDF output requires ReportLab.") continue pdf_output = utils.createSequencedFilename("hpscan", ".pdf") c = canvas.Canvas(pdf_output, (brx/0.3528, bry/0.3528)) try: c.drawInlineImage(im, (tlx/0.3528), (tly/0.3528), ((brx-tlx)/0.3528),((bry-tly)/0.3528)) except NameError: log.error("A problem has occurred with PDF generation. This is a known bug in ReportLab. Please update your install of ReportLab to version 2.0 or greater.") continue c.showPage() log.info("Saving to file %s" % pdf_output) c.save() log.info("Viewing PDF file in %s" % pdf_viewer) cmd = "%s %s &" % (pdf_viewer, pdf_output) os_utils.execute(cmd) sys.exit(0) elif d == 'print': hp_print = utils.which("hp-print", True) if not hp_print: hp_print = 'python ./print.py' if dest_printer is not None: cmd = '%s -p %s %s &' % (hp_print, dest_printer, output) elif dest_devUri is not None: tmp = dest_devUri.partition(":")[2] dest_devUri = "hp:" + tmp cmd = '%s -d %s %s &' % (hp_print, dest_devUri, output) else: cmd = '%s %s &' % (hp_print, output) os_utils.execute(cmd) elif d == 'email': try: from email.mime.image import MIMEImage from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText except ImportError: try: from email.MIMEImage import MIMEImage from email.MIMEMultipart import MIMEMultipart from email.MIMEText import MIMEText except ImportError: log.error("hp-scan email destination requires Python 2.2+.") continue msg = MIMEMultipart() msg['Subject'] = email_subject msg['From'] = email_from msg['To'] = ','.join(email_to) msg.preamble = 'Scanned using hp-scan' if email_note: txt = MIMEText(email_note) msg.attach(txt) if file_saved: txt = MIMEText("attached: %s: %dx%d %s PNG image." % (os.path.basename(output), pixels_per_line, lines, scan_mode)) else: txt = MIMEText("attached: %dx%d %s PNG image." % (pixels_per_line, lines, scan_mode)) msg.attach(txt) fp = open(output, 'r') img = MIMEImage(fp.read()) fp.close() if file_saved: img.add_header('Content-Disposition', 'attachment', filename=os.path.basename(output)) msg.attach(img) sendmail = utils.which("sendmail") if sendmail: sendmail = os.path.join(sendmail, 'sendmail') cmd = [sendmail,'-t','-r',email_from] log.debug(repr(cmd)) err = None try: sp = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) std_out, std_err = sp.communicate(msg.as_string()) if std_err != '': err = std_err except OSError as e: err = str(e) cleanup_spinner() if err: log.error(repr(err)) else: log.error("Mail send failed. 'sendmail' not found.") elif d == 'viewer': if viewer: log.info("Viewing file in %s" % viewer) cmd = "%s %s &" % (viewer, output) os_utils.execute(cmd) else: log.error("Viewer not found.") elif d == 'editor': if editor: log.info("Editing file in %s" % editor) cmd = "%s %s &" % (editor, output) os_utils.execute(cmd) else: log.error("Editor not found.") device.freeScan() device.closeScan() sane.deInit() except KeyboardInterrupt: log.error("User exit") log.info("") log.info("Done.")