| Server IP : 182.53.201.61 / Your IP : 216.73.217.175 Web Server : Apache/2.2.15 (Fedora) System : Linux km10.dyndns.org 2.6.31.5-127.fc12.i686.PAE #1 SMP Sat Nov 7 21:25:57 EST 2009 i686 User : apache ( 48) PHP Version : 5.3.3 Disable Function : NONE MySQL : ON | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : ON Directory : /usr/share/hplip/ |
Upload File : |
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# (c) Copyright 2003-2009 Hewlett-Packard 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
#
from __future__ import division
__version__ = '2.2'
__mod__ = 'hp-scan'
__title__ = 'Scan Utility'
__doc__ = "SANE-based scan utility for HPLIP supported all-in-one/mfp devices (EXPERIMENTAL)."
# Std Lib
import sys
import os
import os.path
import getopt
import signal
import time
import socket
import operator
# Local
from base.g import *
from base import tui, device, module, utils
from prnt import cups
username = prop.username
res = 300
scan_mode = 'color'
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 = ''
fax = ''
resize = 100
contrast = 0
brightness = 0
#printer = ''
page_size = ''
size_desc = ''
page_units = 'mm'
valid_res = (75, 150, 300, 600, 1200, 2400, 4800)
default_res = 300
scanner_compression = 'JPEG'
adf = False
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,
(NON_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', 'fax', or 'print'. Use only commas between values, no spaces.", "option", False),
("Scan mode:", "-m<mode> or --mode=<mode>. Where <mode> is 'color'\*, 'gray' or 'lineart'.", "option", False),
("Scanning resolution:", "-r<resolution_in_dpi> or --res=<resolution_in_dpi> or --resolution=<resolution_in_dpi>", "option", False),
("", "where <resolution_in_dpi> is %s (300 is default)." % ', '.join([str(x) for x in valid_res]), "option", False),
("Image resize:", "--resize=<scale_in_%> (min=1%, max=400%, default=100%)", "option", False),
("Image contrast:", "--contrast=<contrast>", "option", False),
("ADF mode (EXPERIMENTAL):", "--adf (Note, only PDF output is supported when using the 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(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] ('fax' dest)", "", "header", False),
("Fax queue/printer:", "--fax=<fax_printer_name>", "option", False),
utils.USAGE_SPACE,
("[OPTIONS] ('printer' dest)", "", "header", False),
("Printer queue/printer:", "--printer=<printer_name>", "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', 'unit=',
'units=', 'area=', 'box=', 'tlx=',
'tly=', 'brx=', 'bry=', 'size=',
'file=', 'output=', 'pdf=', 'viewer=',
'email-from=', 'from=', 'email-to=',
'to=', 'email-msg=', 'msg=', 'fax=',
'printer=', 'compression=' , 'raw',
'jpeg', 'color', 'lineart', 'colour',
'bw', 'gray', 'grayscale', 'grey',
'greyscale', 'email-subject=',
'subject=', 'to=', 'from=', 'jpg',
'grey-scale', 'gray-scale', 'about=',
'editor='
])
device_uri = mod.getDeviceUri(device_uri, printer_name,
back_end_filter=['hpaio'], filter={'scan-type': (operator.gt, 0)})
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 'color'.")
log.error("Valid modes are 'color', 'lineart', or 'gray'.")
scan_mode = 'color'
elif o in ('--res', '--resolution', '-r'):
try:
r = int(a.strip())
except ValueError:
log.error("Invalid resolution. Using default of %s dpi." % default_res)
log.error("Valid resolutions are %s dpi." % ', '.join([str(x) for x in valid_res]))
res = default_res
else:
if r in valid_res:
res = r
else:
res = valid_res[0]
min_dist = sys.maxint
for x in valid_res:
if abs(r-x) < min_dist:
min_dist = abs(r-x)
res = x
log.warn("Invalid resolution. Using closest valid resolution of %d dpi" % res)
log.error("Valid resolutions are %s dpi." % ', '.join([str(x) for x in valid_res]))
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 in ('-b', '--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(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'):
log.error("Only JPG (.jpg) and PNG (.png) 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', 'fax', 'viewer', 'editor', 'printer', 'print', 'email', 'pdf') \
and aa not in dest:
if aa == 'print': aa = 'printer'
dest.append(aa)
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 ('--fax'):
if 'fax' not in dest:
dest.append('fax')
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'):
pass
elif o in ('-c', '--contrast'):
try:
contrast = int(a.strip())
except ValueError:
log.error("Invalid contrast value. Using default of 100.")
contrast = 100
elif o == '--adf':
adf = True
output_type = 'pdf'
if printer_name is not None and \
device.getDeviceURIByPrinterName(printer_name) is not None and \
'printer' not in dest:
dest.append('printer')
if 'fax' in dest and 'file' not in dest:
log.error("Fax destination not implemented. Adding 'file' destination. Use resulting output file to fax.")
dest.append('file')
if not dest:
log.warn("No destinations specified. Adding 'file' destination by default.")
dest.append('file')
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)
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. Please use -n. Refer to 'hp-scan -h' for help.")
sys.exit(1)
if 1:
#else: # NON_INTERACTIVE_MODE
import Queue
from scan import sane
import scanext
import cStringIO
try:
import subprocess
except ImportError:
# Pre-2.4 Python
from base import subproc as subprocess
try:
import Image
except ImportError:
log.error("%s requires the Python Imaging Library (PIL). Exiting." % __mod__)
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, e:
sane.reportError(e)
sys.exit(1)
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
scan_px = scan_area * res * res / 645.16 # res is in DPI
if scan_mode == 'color':
scan_size = scan_px * 3 # 3 bytes/px
else:
scan_size = scan_px # 1 byte/px
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))
res = device.getOptionObj('resolution').limitAndSet(res)
device.setOption('compression', scanner_compression)
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)
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()
device.setOption("mode", scan_mode)
device.setOption("resolution", res)
if adf:
try:
device.setOption("source", "ADF")
device.setOption("batch-scan", True)
except scanext.error:
log.error("Failed to set ADF mode. Does this device support ADF? Disabling ADF mode.")
adf = False
if not adf:
try:
#device.setOption("source", "Auto")
device.setOption("batch-scan", False)
except scanext.error:
log.debug("Error setting source or batch-scan option (this is probably OK).")
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, e:
#log.error(e)
sane.reportError(e)
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.info("Expecting to read %s from scanner (per page)." % utils.format_bytes(expected_bytes))
else:
log.info("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 log.get_level() >= log.LOG_LEVEL_INFO:
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_EOF:
## log.debug("EOF")
## elif status == scanext.SANE_STATUS_NO_DOCS:
## log.debug("NO DOCS")
## no_docs = True
#elif status != scanext.SANE_STATUS_GOOD:
if status != scanext.SANE_STATUS_GOOD:
sane.reportError(e)
#log.error("SANE error %d during readScan()" % status)
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))
## if status == scanext.SANE_STATUS_EOF:
## log.debug("EOF")
## elif status == scanext.SANE_STATUS_NO_DOCS:
## log.debug("NO DOCS")
## no_docs = True
# if status != scanext.SANE_STATUS_GOOD:
# log.error("SANE error %d during queue clear" % status)
# sys.exit(1)
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 = device.getScan()
log.debug("PPL=%d lines=%d depth=%d BPL=%d pad=%d total=%d" %
(pixels_per_line, lines, depth, bytes_per_line, pad_bytes, total_read))
if lines == -1:
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 == 'gray':
## im = Image.frombuffer('RGBA', (pixels_per_line, lines), buffer.read(),
## 'raw', 'RGBA', 0, 1).convert('P')
elif scan_mode == 'lineart':
try:
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:
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))
#adf_pages.append(im)
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()
#print "0"
#device.freeScan()
#sane.deInit()
#if no_docs:
# sys.exit(0)
#print "1"
if adf:
try:
from reportlab.pdfgen import canvas
except ImportError:
log.error("PDF output requires ReportLab.")
sys.exit(1)
#print "2"
#print canvas
#print canvas.Canvas
tlx_max = device.getOptionObj('tl-x').constraint[1]
bry_max = device.getOptionObj('br-y').constraint[1]
if not output:
output = utils.createSequencedFilename("hpscan", ".pdf")
c = canvas.Canvas(output, (tlx_max/0.3528, bry_max/0.3528))
#print c
#for p in adf_page_files:
for p in adf_page_files:
log.info("Processing page %s..." % p)
image = Image.open(p)
#print image
try:
c.drawInlineImage(image, (tlx/0.3528), ((bry_max/0.3528)-(bry/0.3528)),
#c.drawInlineImage(image, 0, bry/0.3528,
width=None,height=None)
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)
c.showPage()
log.info("Saving to file %s" % output)
c.save()
log.info("Viewing PDF file in %s" % pdf_viewer)
os.system("%s %s &" % (pdf_viewer, output))
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 = pixels_per_line * resize / 100
new_h = lines * resize / 100
log.info("Resizing 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, e:
log.error("Error saving file: %s (I/O)" % e)
try:
os.remove(output)
except OSError:
pass
sys.exit(1)
except ValueError, 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 'printer' in dest) \
and not file_saved:
output_fd, output = utils.make_temp_file(suffix='.png')
try:
im.save(output)
except IOError, 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 == 'fax':
log.error("fax: Not implemented yet.")
elif d == 'pdf':
try:
from reportlab.pdfgen import canvas
except ImportError:
log.error("PDF output requires ReportLab.")
continue
tlx_max = device.getOptionObj('tl-x').constraint[1]
bry_max = device.getOptionObj('br-y').constraint[1]
pdf_output = utils.createSequencedFilename("hpscan", ".pdf")
c = canvas.Canvas(pdf_output, (tlx_max/0.3528, bry_max/0.3528))
try:
c.drawInlineImage(im, (tlx/0.3528), ((bry_max/0.3528)-(bry/0.3528)),
width=None,height=None)
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)
os.system("%s %s &" % (pdf_viewer, pdf_output))
elif d == 'printer':
hp_print = utils.which("hp-print")
if hp_print:
cmd = 'hp-print %s &' % output
else:
cmd = "python ./print.py %s &" % output
os.system(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, 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)
os.system("%s %s &" % (viewer, output))
else:
log.error("Viewer not found.")
elif d == 'editor':
if editor:
log.info("Editing file in %s" % editor)
os.system("%s %s &" % (editor, output))
else:
log.error("Editor not found.")
device.freeScan()
sane.deInit()
except KeyboardInterrupt:
log.error("User exit")
log.info("")
log.info("Done.")