1
2
3
4 __version__ = "$Revision: 1.56 $"
5 __license__ = "GPL"
6 __author__ = """Sebastian Hilbert <Sebastian.Hilbert@gmx.net>, Karsten Hilbert <Karsten.Hilbert@gmx.net>"""
7
8
9
10 import sys, os.path, os, string, time, shutil, codecs, glob, locale, errno, stat, logging
11
12
13
14
15
16
17
18 if __name__ == '__main__':
19 sys.path.insert(0, '../../')
20 from Gnumed.pycommon import gmShellAPI
21 from Gnumed.pycommon import gmTools
22 from Gnumed.pycommon import gmI18N
23 from Gnumed.pycommon import gmLog2
24
25
26 _log = logging.getLogger('gm.scanning')
27 _log.info(__version__)
28
29 _twain_module = None
30 _sane_module = None
31
32 use_XSane = True
33
34
35
46
48
49
50
51
52
53 - def __init__(self, calling_window=None):
54 _twain_import_module()
55
56 self.__calling_window = calling_window
57 self.__src_manager = None
58 self.__scanner = None
59 self.__done_transferring_image = False
60
61 self.__register_event_handlers()
62
63
64
65 - def acquire_pages_into_files(self, delay=None, filename=None, tmpdir=None):
66 if filename is None:
67 filename = gmTools.get_unique_filename(prefix='gmScannedObj-', suffix='.bmp', tmp_dir=tmpdir)
68 else:
69 tmp, ext = os.path.splitext(filename)
70 if ext != '.bmp':
71 filename = filename + '.bmp'
72
73 self.__filename = os.path.abspath(os.path.expanduser(filename))
74
75 if not self.__init_scanner():
76 raise OSError(-1, 'cannot init TWAIN scanner device')
77
78 self.__done_transferring_image = False
79 self.__scanner.RequestAcquire(True)
80
81 return [self.__filename]
82
84 return self.__done_transferring_image
85
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103 return
104
105
106
108 if self.__scanner is not None:
109 return True
110
111 self.__init_src_manager()
112 if self.__src_manager is None:
113 return False
114
115
116 self.__src_manager.SetCallback(self._twain_event_callback)
117
118
119 try:
120 self.__scanner = self.__src_manager.OpenSource()
121 except _twain_module.excDSOpenFailed:
122 _log.exception('cannot open TWAIN data source (image capture device)')
123 gmLog2.log_stack_trace()
124 return False
125
126 if self.__scanner is None:
127 _log.error("user canceled scan source selection dialog")
128 return False
129
130 _log.info("TWAIN data source: %s" % self.__scanner.GetSourceName())
131 _log.debug("TWAIN data source config: %s" % str(self.__scanner.GetIdentity()))
132
133 return True
134
136
137 if self.__src_manager is not None:
138 return
139
140
141
142
143
144
145
146
147
148
149
150
151 try:
152 self.__src_manager = _twain_module.SourceManager(self.__calling_window.GetHandle())
153
154 except _twain_module.excSMLoadFileFailed:
155 _log.exception('failed to load TWAIN_32.DLL')
156 return
157
158 except _twain_module.excSMGetProcAddressFailed:
159 _log.exception('failed to jump into TWAIN_32.DLL')
160 return
161
162 except _twain_module.excSMOpenFailed:
163 _log.exception('failed to open Source Manager')
164 return
165
166 _log.info("TWAIN source manager config: %s" % str(self.__src_manager.GetIdentity()))
167
168
169
171 self.__twain_event_handlers = {
172 _twain_module.MSG_XFERREADY: self._twain_handle_transfer_in_memory,
173 _twain_module.MSG_CLOSEDSREQ: self._twain_close_datasource,
174 _twain_module.MSG_CLOSEDSOK: self._twain_save_state,
175 _twain_module.MSG_DEVICEEVENT: self._twain_handle_src_event
176 }
177
179 _log.debug('notification of TWAIN event <%s>' % str(twain_event))
180 self.__twain_event_handlers[twain_event]()
181 self.__scanner = None
182 return
183
185 _log.info("being asked to close data source")
186
188 _log.info("being asked to save application state")
189
191 _log.info("being asked to handle device specific event")
192
194
195
196
197 _log.debug('receiving image from TWAIN source')
198 _log.debug('image info: %s' % self.__scanner.GetImageInfo())
199 _log.debug('image layout: %s' % str(self.__scanner.GetImageLayout()))
200
201
202 (external_data_handle, more_images_pending) = self.__scanner.XferImageNatively()
203 try:
204
205 _twain_module.DIBToBMFile(external_data_handle, self.__filename)
206 finally:
207 _twain_module.GlobalHandleFree(external_data_handle)
208 _log.debug('%s pending images' % more_images_pending)
209
210
211
212
213
214 self.__done_transferring_image = True
215
217
218
219
220
221
222 _log.debug('receiving image from TWAIN source')
223 _log.debug('image info: %s' % self.__scanner.GetImageInfo())
224 _log.debug('image layout: %s' % self.__scanner.GetImageLayout())
225
226 self.__scanner.SetXferFileName(self.__filename)
227
228 more_images_pending = self.__scanner.XferImageByFile()
229 _log.debug('%s pending images' % more_images_pending)
230
231
232 self.__scanner.HideUI()
233
234
235 return
236
237
238
255
257
258
259
260 _src_manager = None
261
263 _sane_import_module()
264
265
266
267
268
269
270
271
272 self.__device = device
273 _log.info('using SANE device [%s]' % self.__device)
274
275 self.__init_scanner()
276
278 self.__scanner = _sane_module.open(self.__device)
279
280 _log.debug('opened SANE device: %s' % str(self.__scanner))
281 _log.debug('SANE device config: %s' % str(self.__scanner.get_parameters()))
282 _log.debug('SANE device opts : %s' % str(self.__scanner.optlist))
283 _log.debug('SANE device opts : %s' % str(self.__scanner.get_options()))
284
285 return True
286
288 self.__scanner.close()
289
290 - def acquire_pages_into_files(self, delay=None, filename=None, tmpdir=None):
291 if filename is None:
292 filename = gmTools.get_unique_filename(prefix='gmScannedObj-', suffix='.bmp', tmp_dir=tmpdir)
293 else:
294 tmp, ext = os.path.splitext(filename)
295 if ext != '.bmp':
296 filename = filename + '.bmp'
297
298 filename = os.path.abspath(os.path.expanduser(filename))
299
300 if delay is not None:
301 time.sleep(delay)
302 _log.debug('some sane backends report device_busy if we advance too fast. delay set to %s sec' % delay)
303
304 _log.debug('Trying to get image from scanner into [%s] !' % filename)
305 self.__scanner.start()
306 img = self.__scanner.snap()
307 img.save(filename)
308
309 return [filename]
310
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
338
339 _filetype = u'.png'
340 _xsanerc = os.path.expanduser(os.path.join('~', '.sane', 'xsane', 'xsane.rc'))
341
342 _xsanerc_gnumed = os.path.expanduser(os.path.join('~', '.gnumed', 'gnumed-xsanerc.conf'))
343 _xsanerc_backup = os.path.expanduser(os.path.join('~', '.gnumed', 'gnumed-xsanerc.conf.bak'))
344
345
347
348
349 try:
350 open(cXSaneScanner._xsanerc, 'r').close()
351 except IOError:
352 msg = (
353 'XSane not properly installed for this user:\n\n'
354 ' [%s] not found\n\n'
355 'Start XSane once before using it with GNUmed.'
356 ) % cXSaneScanner._xsanerc
357 raise ImportError(msg)
358
359
360
361 self.device_settings_file = None
362 self.default_device = None
363
366
367 - def acquire_pages_into_files(self, delay=None, filename=None, tmpdir=None):
368 """Call XSane.
369
370 <filename> name part must have format name-001.ext>
371 """
372 if filename is None:
373 filename = gmTools.get_unique_filename (
374 prefix = 'gmScannedObj-',
375 suffix = cXSaneScanner._filetype,
376 tmp_dir = tmpdir
377 )
378 name, ext = os.path.splitext(filename)
379 filename = '%s-001%s' % (name, cXSaneScanner._filetype)
380
381 filename = os.path.abspath(os.path.expanduser(filename))
382 path, name = os.path.split(filename)
383
384 self.__prepare_xsanerc(tmpdir=path)
385
386 cmd = 'xsane --no-mode-selection --save --force-filename "%s" --xsane-rc "%s" %s %s' % (
387 filename,
388 cXSaneScanner._xsanerc_gnumed,
389 gmTools.coalesce(self.device_settings_file, '', '--device-settings %s'),
390 gmTools.coalesce(self.default_device, '')
391 )
392 normal_exit = gmShellAPI.run_command_in_shell(command = cmd, blocking = True)
393
394 if normal_exit:
395 flist = glob.glob(filename.replace('001', '*'))
396 flist.sort()
397 return flist
398
399 raise OSError(-1, 'error starting XSane as [%s]' % cmd)
400
403
404
405
407
408 try:
409 open(cXSaneScanner._xsanerc_gnumed, 'r+b').close()
410 except IOError:
411 _log.info('creating [%s] from [%s]', cXSaneScanner._xsanerc_gnumed, cXSaneScanner._xsanerc)
412 shutil.copyfile(cXSaneScanner._xsanerc, cXSaneScanner._xsanerc_gnumed)
413
414 shutil.move(cXSaneScanner._xsanerc_gnumed, cXSaneScanner._xsanerc_backup)
415
416
417 enc = gmI18N.get_encoding()
418 fread = codecs.open(cXSaneScanner._xsanerc_backup, mode = "rU", encoding = enc)
419 fwrite = codecs.open(cXSaneScanner._xsanerc_gnumed, mode = "w", encoding = enc)
420
421 val_dict = {
422 u'filetype': cXSaneScanner._filetype,
423 u'tmp-path': tmpdir,
424 u'working-directory': tmpdir,
425 u'skip-existing-numbers': u'1',
426 u'filename-counter-step': u'1',
427 u'filename-counter-len': u'3'
428 }
429
430 for idx, line in enumerate(fread):
431 line = line.replace(u'\n', u'')
432 line = line.replace(u'\r', u'')
433
434 if idx % 2 == 0:
435 key = line.strip(u'"')
436 fwrite.write(u'"%s"\n' % key)
437 else:
438 try:
439 value = val_dict[key]
440 except KeyError:
441 value = line
442 fwrite.write(u'%s\n' % value)
443
444 fwrite.flush()
445 fwrite.close()
446 fread.close()
447
448
449
450 return True
451
452
453
454
455
457 try:
458 _twain_import_module()
459
460
461 return None
462 except ImportError:
463 pass
464
465 if use_XSane:
466
467 return None
468
469 _sane_import_module()
470 return _sane_module.get_devices()
471
472 -def acquire_pages_into_files(device=None, delay=None, filename=None, tmpdir=None, calling_window=None, xsane_device_settings=None):
473 """Connect to a scanner and return the scanned pages as a file list.
474
475 returns:
476 - list of filenames: names of scanned pages, may be []
477 - None: unable to connect to scanner
478 """
479 try:
480 scanner = cTwainScanner(calling_window=calling_window)
481 _log.debug('using TWAIN')
482 except ImportError:
483 if use_XSane:
484 _log.debug('using XSane')
485 scanner = cXSaneScanner()
486 scanner.device_settings_file = xsane_device_settings
487 scanner.default_device = device
488 else:
489 _log.debug('using SANE directly')
490 scanner = cSaneScanner(device=device)
491
492 _log.debug('requested filename: [%s]' % filename)
493 fnames = scanner.acquire_pages_into_files(filename=filename, delay=delay, tmpdir=tmpdir)
494 scanner.close()
495 _log.debug('acquired pages into files: %s' % str(fnames))
496
497 return fnames
498
499
500
501 if __name__ == '__main__':
502
503 if len(sys.argv) > 1 and sys.argv[1] == u'test':
504
505 logging.basicConfig(level=logging.DEBUG)
506
507 print "devices:"
508 print get_devices()
509
510 sys.exit()
511
512 setups = [
513 {'dev': 'test:0', 'file': 'x1-test0-1-0001'},
514 {'dev': 'test:1', 'file': 'x2-test1-1-0001.bmp'},
515 {'dev': 'test:0', 'file': 'x3-test0-2-0001.bmp-ccc'}
516 ]
517
518 idx = 1
519 for setup in setups:
520 print "scanning page #%s from device [%s]" % (idx, setup['dev'])
521 idx += 1
522 fnames = acquire_pages_into_files(device = setup['dev'], filename = setup['file'], delay = (idx*5))
523 if fnames is False:
524 print "error, cannot acquire page"
525 else:
526 print " image files:", fnames
527
528
529