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