1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | /* |
3 | * This file is part of the LibreOffice project. |
4 | * |
5 | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | * |
9 | * This file incorporates work covered by the following license notice: |
10 | * |
11 | * Licensed to the Apache Software Foundation (ASF) under one or more |
12 | * contributor license agreements. See the NOTICE file distributed |
13 | * with this work for additional information regarding copyright |
14 | * ownership. The ASF licenses this file to you under the Apache |
15 | * License, Version 2.0 (the "License"); you may not use this file |
16 | * except in compliance with the License. You may obtain a copy of |
17 | * the License at http://www.apache.org/licenses/LICENSE-2.0 . |
18 | */ |
19 | |
20 | #include <cstdarg> |
21 | #include <math.h> |
22 | #include <osl/file.h> |
23 | #include <tools/stream.hxx> |
24 | #include <sane.hxx> |
25 | #include <dlfcn.h> |
26 | #include <stdio.h> |
27 | #include <unistd.h> |
28 | #include <sys/time.h> |
29 | #include <sys/types.h> |
30 | #include <sal/config.h> |
31 | #include <sal/macros.h> |
32 | |
33 | #if (OSL_DEBUG_LEVEL > 1) || defined DBG_UTIL |
34 | #include <stdarg.h> |
35 | #define dump_state( a, b, c, d ) fprintf( stderr, a, b, c, d ); |
36 | #else |
37 | #define dump_state( a, b, c, d ) ; |
38 | #endif |
39 | inline void dbg_msg( const char* pString, ... ) |
40 | { |
41 | #if (OSL_DEBUG_LEVEL > 1) || defined DBG_UTIL |
42 | va_list ap; |
43 | va_start( ap, pString ); |
44 | vfprintf( stderr, pString, ap ); |
45 | va_end( ap ); |
46 | #else |
47 | (void)pString; |
48 | #endif |
49 | } |
50 | |
51 | #define FAIL_SHUTDOWN_STATE( x, y, z ) \ |
52 | if( x != SANE_STATUS_GOOD ) \ |
53 | { \ |
54 | dump_state( "%s returned error %d (%s)\n", \ |
55 | y, x, p_strstatus( x ) ); \ |
56 | DeInit(); \ |
57 | return z; \ |
58 | } |
59 | |
60 | #define FAIL_STATE( x, y, z ) \ |
61 | if( x != SANE_STATUS_GOOD ) \ |
62 | { \ |
63 | dump_state( "%s returned error %d (%s)\n", \ |
64 | y, x, p_strstatus( x ) ); \ |
65 | return z; \ |
66 | } |
67 | |
68 | #define DUMP_STATE( x, y ) \ |
69 | if( x != SANE_STATUS_GOOD ) \ |
70 | { \ |
71 | dump_state( "%s returned error %d (%s)\n", \ |
72 | y, x, p_strstatus( x ) ); \ |
73 | } |
74 | |
75 | int Sane::nRefCount = 0; |
76 | oslModule Sane::pSaneLib = 0; |
77 | SANE_Int Sane::nVersion = 0; |
78 | SANE_Device** Sane::ppDevices = 0; |
79 | int Sane::nDevices = 0; |
80 | |
81 | SANE_Status (*Sane::p_init)( SANE_Int*, |
82 | SANE_Auth_Callback ) = 0; |
83 | void (*Sane::p_exit)() = 0; |
84 | SANE_Status (*Sane::p_get_devices)( const SANE_Device***, |
85 | SANE_Bool ) = 0; |
86 | SANE_Status (*Sane::p_open)( SANE_String_Const, SANE_Handle ) = 0; |
87 | void (*Sane::p_close)( SANE_Handle ) = 0; |
88 | const SANE_Option_Descriptor* (*Sane::p_get_option_descriptor)( |
89 | SANE_Handle, SANE_Int ) = 0; |
90 | SANE_Status (*Sane::p_control_option)( SANE_Handle, SANE_Int, |
91 | SANE_Action, void*, |
92 | SANE_Int* ) = 0; |
93 | SANE_Status (*Sane::p_get_parameters)( SANE_Handle, |
94 | SANE_Parameters* ) = 0; |
95 | SANE_Status (*Sane::p_start)( SANE_Handle ) = 0; |
96 | SANE_Status (*Sane::p_read)( SANE_Handle, SANE_Byte*, SANE_Int, |
97 | SANE_Int* ) = 0; |
98 | void (*Sane::p_cancel)( SANE_Handle ) = 0; |
99 | SANE_Status (*Sane::p_set_io_mode)( SANE_Handle, SANE_Bool ) = 0; |
100 | SANE_Status (*Sane::p_get_select_fd)( SANE_Handle, SANE_Int* ) = 0; |
101 | SANE_String_Const (*Sane::p_strstatus)( SANE_Status ) = 0; |
102 | |
103 | static sal_Bool bSaneSymbolLoadFailed = sal_False; |
104 | |
105 | inline oslGenericFunction Sane::LoadSymbol( const char* pSymbolname ) |
106 | { |
107 | oslGenericFunction pFunction = osl_getAsciiFunctionSymbol( pSaneLib, pSymbolname ); |
108 | if( ! pFunction ) |
109 | { |
110 | fprintf( stderr, "Could not load symbol %s\n" , |
111 | pSymbolname ); |
112 | bSaneSymbolLoadFailed = sal_True; |
113 | } |
114 | return pFunction; |
115 | } |
116 | |
117 | SANE_Status Sane::ControlOption( int nOption, SANE_Action nAction, |
118 | void* pData ) |
119 | { |
120 | SANE_Int nInfo = 0; |
121 | |
122 | SANE_Status nStatus = p_control_option( maHandle, (SANE_Int)nOption, |
123 | nAction, pData, &nInfo ); |
124 | DUMP_STATE( nStatus, "sane_control_option" ); |
125 | #if OSL_DEBUG_LEVEL > 1 |
126 | if( nStatus != SANE_STATUS_GOOD ) |
127 | { |
128 | const char* pAction = "Unknown" ; |
129 | switch( nAction ) |
130 | { |
131 | case SANE_ACTION_GET_VALUE: |
132 | pAction = "SANE_ACTION_GET_VALUE" ;break; |
133 | case SANE_ACTION_SET_VALUE: |
134 | pAction = "SANE_ACTION_SET_VALUE" ;break; |
135 | case SANE_ACTION_SET_AUTO: |
136 | pAction = "SANE_ACTION_SET_AUTO" ;break; |
137 | } |
138 | dbg_msg( "Option: \"%s\" action: %s\n" , |
139 | OUStringToOString(GetOptionName(nOption), osl_getThreadTextEncoding()).getStr(), |
140 | pAction ); |
141 | } |
142 | #endif |
143 | if( nInfo & SANE_INFO_RELOAD_OPTIONS ) |
144 | ReloadOptions(); |
145 | return nStatus; |
146 | } |
147 | |
148 | Sane::Sane() : |
149 | mppOptions( 0 ), |
150 | mnOptions( 0 ), |
151 | mnDevice( -1 ), |
152 | maHandle( 0 ) |
153 | { |
154 | if( ! nRefCount || ! pSaneLib ) |
155 | Init(); |
156 | nRefCount++; |
157 | }; |
158 | |
159 | Sane::~Sane() |
160 | { |
161 | if( IsOpen() ) |
162 | Close(); |
163 | nRefCount--; |
164 | if( ! nRefCount && pSaneLib ) |
165 | DeInit(); |
166 | } |
167 | |
168 | void Sane::Init() |
169 | { |
170 | OUString sSaneLibName( "libsane" SAL_DLLEXTENSION ); |
171 | pSaneLib = osl_loadModule( sSaneLibName.pData, SAL_LOADMODULE_LAZY ); |
172 | if( ! pSaneLib ) |
173 | { |
174 | sSaneLibName = "libsane" SAL_DLLEXTENSION ".1" ; |
175 | pSaneLib = osl_loadModule( sSaneLibName.pData, SAL_LOADMODULE_LAZY ); |
176 | } |
177 | // try reasonable places that might not be in the library search path |
178 | if( ! pSaneLib ) |
179 | { |
180 | OUString sSaneLibSystemPath( "/usr/local/lib/libsane" SAL_DLLEXTENSION ); |
181 | osl_getFileURLFromSystemPath( sSaneLibSystemPath.pData, &sSaneLibName.pData ); |
182 | pSaneLib = osl_loadModule( sSaneLibName.pData, SAL_LOADMODULE_LAZY ); |
183 | } |
184 | |
185 | if( pSaneLib ) |
186 | { |
187 | bSaneSymbolLoadFailed = sal_False; |
188 | p_init = (SANE_Status(*)(SANE_Int*, SANE_Auth_Callback )) |
189 | LoadSymbol( "sane_init" ); |
190 | p_exit = (void(*)()) |
191 | LoadSymbol( "sane_exit" ); |
192 | p_get_devices = (SANE_Status(*)(const SANE_Device***, |
193 | SANE_Bool )) |
194 | LoadSymbol( "sane_get_devices" ); |
195 | p_open = (SANE_Status(*)(SANE_String_Const, SANE_Handle )) |
196 | LoadSymbol( "sane_open" ); |
197 | p_close = (void(*)(SANE_Handle)) |
198 | LoadSymbol( "sane_close" ); |
199 | p_get_option_descriptor = (const SANE_Option_Descriptor*(*)(SANE_Handle, |
200 | SANE_Int)) |
201 | LoadSymbol( "sane_get_option_descriptor" ); |
202 | p_control_option = (SANE_Status(*)(SANE_Handle, SANE_Int, |
203 | SANE_Action, void*, SANE_Int*)) |
204 | LoadSymbol( "sane_control_option" ); |
205 | p_get_parameters = (SANE_Status(*)(SANE_Handle,SANE_Parameters*)) |
206 | LoadSymbol( "sane_get_parameters" ); |
207 | p_start = (SANE_Status(*)(SANE_Handle)) |
208 | LoadSymbol( "sane_start" ); |
209 | p_read = (SANE_Status(*)(SANE_Handle, SANE_Byte*, |
210 | SANE_Int, SANE_Int* )) |
211 | LoadSymbol( "sane_read" ); |
212 | p_cancel = (void(*)(SANE_Handle)) |
213 | LoadSymbol( "sane_cancel" ); |
214 | p_set_io_mode = (SANE_Status(*)(SANE_Handle, SANE_Bool)) |
215 | LoadSymbol( "sane_set_io_mode" ); |
216 | p_get_select_fd = (SANE_Status(*)(SANE_Handle, SANE_Int*)) |
217 | LoadSymbol( "sane_get_select_fd" ); |
218 | p_strstatus = (SANE_String_Const(*)(SANE_Status)) |
219 | LoadSymbol( "sane_strstatus" ); |
220 | if( bSaneSymbolLoadFailed ) |
221 | DeInit(); |
222 | else |
223 | { |
224 | SANE_Status nStatus = p_init( &nVersion, 0 ); |
225 | FAIL_SHUTDOWN_STATE( nStatus, "sane_init" , ); |
226 | nStatus = p_get_devices( (const SANE_Device***)&ppDevices, |
227 | SANE_FALSE ); |
228 | FAIL_SHUTDOWN_STATE( nStatus, "sane_get_devices" , ); |
229 | for( nDevices = 0 ; ppDevices[ nDevices ]; nDevices++ ) ; |
230 | } |
231 | } |
232 | #if (OSL_DEBUG_LEVEL > 1) || defined DBG_UTIL |
233 | else |
234 | fprintf( stderr, "libsane%s could not be opened: %s\n" , SAL_DLLEXTENSION, |
235 | dlerror() ); |
236 | #endif |
237 | } |
238 | |
239 | void Sane::DeInit() |
240 | { |
241 | if( pSaneLib ) |
242 | { |
243 | p_exit(); |
244 | osl_unloadModule( pSaneLib ); |
245 | pSaneLib = 0; |
246 | } |
247 | } |
248 | |
249 | void Sane::ReloadDevices() |
250 | { |
251 | if( IsOpen() ) |
252 | Close(); |
253 | DeInit(); |
254 | Init(); |
255 | } |
256 | |
257 | void Sane::ReloadOptions() |
258 | { |
259 | if( ! IsOpen() ) |
260 | return; |
261 | |
262 | const SANE_Option_Descriptor* pZero = p_get_option_descriptor( maHandle, 0 ); |
263 | SANE_Word pOptions[2]; |
264 | SANE_Status nStatus = p_control_option( maHandle, 0, SANE_ACTION_GET_VALUE, |
265 | (void*)pOptions, NULL ); |
266 | if( nStatus != SANE_STATUS_GOOD ) |
267 | fprintf( stderr, "Error: sane driver returned %s while reading number of options !\n" , p_strstatus( nStatus ) ); |
268 | |
269 | mnOptions = pOptions[ 0 ]; |
270 | if( (size_t)pZero->size > sizeof( SANE_Word ) ) |
271 | fprintf( stderr, "driver returned numer of options with larger size tha SANE_Word !!!\n" ); |
272 | if( mppOptions ) |
273 | delete [] mppOptions; |
274 | mppOptions = new const SANE_Option_Descriptor*[ mnOptions ]; |
275 | mppOptions[ 0 ] = pZero; |
276 | for( int i = 1; i < mnOptions; i++ ) |
277 | mppOptions[ i ] = (SANE_Option_Descriptor*) |
278 | p_get_option_descriptor( maHandle, i ); |
279 | |
280 | CheckConsistency( NULL, sal_True ); |
281 | |
282 | maReloadOptionsLink.Call( this ); |
283 | } |
284 | |
285 | sal_Bool Sane::Open( const char* name ) |
286 | { |
287 | SANE_Status nStatus = p_open( (SANE_String_Const)name, &maHandle ); |
288 | FAIL_STATE( nStatus, "sane_open" , sal_False ); |
289 | |
290 | ReloadOptions(); |
291 | |
292 | if( mnDevice == -1 ) |
293 | { |
294 | OString aDevice( name ); |
295 | for( int i = 0; i < nDevices; i++ ) |
296 | { |
297 | if( aDevice.equals( ppDevices[i]->name ) ) |
298 | { |
299 | mnDevice = i; |
300 | break; |
301 | } |
302 | } |
303 | } |
304 | |
305 | return sal_True; |
306 | } |
307 | |
308 | sal_Bool Sane::Open( int n ) |
309 | { |
310 | if( n >= 0 && n < nDevices ) |
311 | { |
312 | mnDevice = n; |
313 | return Open( (char*)ppDevices[n]->name ); |
314 | } |
315 | return sal_False; |
316 | } |
317 | |
318 | void Sane::Close() |
319 | { |
320 | if( maHandle ) |
321 | { |
322 | p_close( maHandle ); |
323 | delete [] mppOptions; |
324 | mppOptions = 0; |
325 | maHandle = 0; |
326 | mnDevice = -1; |
327 | } |
328 | } |
329 | |
330 | int Sane::GetOptionByName( const char* rName ) |
331 | { |
332 | int i; |
333 | OString aOption( rName ); |
334 | for( i = 0; i < mnOptions; i++ ) |
335 | { |
336 | if( mppOptions[i]->name && aOption.equals( mppOptions[i]->name ) ) |
337 | return i; |
338 | } |
339 | return -1; |
340 | } |
341 | |
342 | sal_Bool Sane::GetOptionValue( int n, sal_Bool& rRet ) |
343 | { |
344 | if( ! maHandle || mppOptions[n]->type != SANE_TYPE_BOOL ) |
345 | return sal_False; |
346 | SANE_Word nRet; |
347 | SANE_Status nStatus = ControlOption( n, SANE_ACTION_GET_VALUE, &nRet ); |
348 | if( nStatus != SANE_STATUS_GOOD ) |
349 | return sal_False; |
350 | |
351 | rRet = nRet; |
352 | return sal_True; |
353 | } |
354 | |
355 | sal_Bool Sane::GetOptionValue( int n, OString& rRet ) |
356 | { |
357 | sal_Bool bSuccess = sal_False; |
358 | if( ! maHandle || mppOptions[n]->type != SANE_TYPE_STRING ) |
359 | return sal_False; |
360 | char* pRet = new char[mppOptions[n]->size+1]; |
361 | SANE_Status nStatus = ControlOption( n, SANE_ACTION_GET_VALUE, pRet ); |
362 | if( nStatus == SANE_STATUS_GOOD ) |
363 | { |
364 | bSuccess = sal_True; |
365 | rRet = pRet; |
366 | } |
367 | delete [] pRet; |
368 | return bSuccess; |
369 | } |
370 | |
371 | sal_Bool Sane::GetOptionValue( int n, double& rRet, int nElement ) |
372 | { |
373 | sal_Bool bSuccess = sal_False; |
374 | |
375 | if( ! maHandle || ( mppOptions[n]->type != SANE_TYPE_INT && |
376 | mppOptions[n]->type != SANE_TYPE_FIXED ) ) |
377 | return sal_False; |
378 | |
379 | SANE_Word* pRet = new SANE_Word[mppOptions[n]->size/sizeof(SANE_Word)]; |
380 | SANE_Status nStatus = ControlOption( n, SANE_ACTION_GET_VALUE, pRet ); |
381 | if( nStatus == SANE_STATUS_GOOD ) |
382 | { |
383 | bSuccess = sal_True; |
384 | if( mppOptions[n]->type == SANE_TYPE_INT ) |
385 | rRet = (double)pRet[ nElement ]; |
386 | else |
387 | rRet = SANE_UNFIX( pRet[nElement] ); |
388 | } |
389 | delete [] pRet; |
390 | return bSuccess; |
391 | } |
392 | |
393 | sal_Bool Sane::GetOptionValue( int n, double* pSet ) |
394 | { |
395 | if( ! maHandle || ! ( mppOptions[n]->type == SANE_TYPE_FIXED || |
396 | mppOptions[n]->type == SANE_TYPE_INT ) ) |
397 | return sal_False; |
398 | |
399 | SANE_Word* pFixedSet = new SANE_Word[mppOptions[n]->size/sizeof(SANE_Word)]; |
400 | SANE_Status nStatus = ControlOption( n, SANE_ACTION_GET_VALUE, pFixedSet ); |
401 | if( nStatus != SANE_STATUS_GOOD ) |
402 | { |
403 | delete [] pFixedSet; |
404 | return sal_False; |
405 | } |
406 | for( size_t i = 0; i <mppOptions[n]->size/sizeof(SANE_Word); i++ ) |
407 | { |
408 | if( mppOptions[n]->type == SANE_TYPE_FIXED ) |
409 | pSet[i] = SANE_UNFIX( pFixedSet[i] ); |
410 | else |
411 | pSet[i] = (double) pFixedSet[i]; |
412 | } |
413 | delete [] pFixedSet; |
414 | return sal_True; |
415 | } |
416 | |
417 | sal_Bool Sane::SetOptionValue( int n, sal_Bool bSet ) |
418 | { |
419 | if( ! maHandle || mppOptions[n]->type != SANE_TYPE_BOOL ) |
420 | return sal_False; |
421 | SANE_Word nRet = bSet ? SANE_TRUE : SANE_FALSE; |
422 | SANE_Status nStatus = ControlOption( n, SANE_ACTION_SET_VALUE, &nRet ); |
423 | if( nStatus != SANE_STATUS_GOOD ) |
424 | return sal_False; |
425 | return sal_True; |
426 | } |
427 | |
428 | sal_Bool Sane::SetOptionValue( int n, const OUString& rSet ) |
429 | { |
430 | if( ! maHandle || mppOptions[n]->type != SANE_TYPE_STRING ) |
431 | return sal_False; |
432 | OString aSet(OUStringToOString(rSet, osl_getThreadTextEncoding())); |
433 | SANE_Status nStatus = ControlOption( n, SANE_ACTION_SET_VALUE, (void*)aSet.getStr() ); |
434 | if( nStatus != SANE_STATUS_GOOD ) |
435 | return sal_False; |
436 | return sal_True; |
437 | } |
438 | |
439 | sal_Bool Sane::SetOptionValue( int n, double fSet, int nElement ) |
440 | { |
441 | sal_Bool bSuccess = sal_False; |
442 | |
443 | if( ! maHandle || ( mppOptions[n]->type != SANE_TYPE_INT && |
444 | mppOptions[n]->type != SANE_TYPE_FIXED ) ) |
445 | return sal_False; |
446 | |
447 | SANE_Status nStatus; |
448 | if( mppOptions[n]->size/sizeof(SANE_Word) > 1 ) |
449 | { |
450 | SANE_Word* pSet = new SANE_Word[mppOptions[n]->size/sizeof(SANE_Word)]; |
451 | nStatus = ControlOption( n, SANE_ACTION_GET_VALUE, pSet ); |
452 | if( nStatus == SANE_STATUS_GOOD ) |
453 | { |
454 | pSet[nElement] = mppOptions[n]->type == SANE_TYPE_INT ? |
455 | (SANE_Word)fSet : SANE_FIX( fSet ); |
456 | nStatus = ControlOption( n, SANE_ACTION_SET_VALUE, pSet ); |
457 | } |
458 | delete [] pSet; |
459 | } |
460 | else |
461 | { |
462 | SANE_Word nSetTo = |
463 | mppOptions[n]->type == SANE_TYPE_INT ? |
464 | (SANE_Word)fSet : SANE_FIX( fSet ); |
465 | |
466 | nStatus = ControlOption( n, SANE_ACTION_SET_VALUE, &nSetTo ); |
467 | if( nStatus == SANE_STATUS_GOOD ) |
468 | bSuccess = sal_True; |
469 | } |
470 | return bSuccess; |
471 | } |
472 | |
473 | sal_Bool Sane::SetOptionValue( int n, double* pSet ) |
474 | { |
475 | if( ! maHandle || ( mppOptions[n]->type != SANE_TYPE_INT && |
476 | mppOptions[n]->type != SANE_TYPE_FIXED ) ) |
477 | return sal_False; |
478 | SANE_Word* pFixedSet = new SANE_Word[mppOptions[n]->size/sizeof(SANE_Word)]; |
479 | for( size_t i = 0; i < mppOptions[n]->size/sizeof(SANE_Word); i++ ) |
480 | { |
481 | if( mppOptions[n]->type == SANE_TYPE_FIXED ) |
482 | pFixedSet[i] = SANE_FIX( pSet[i] ); |
483 | else |
484 | pFixedSet[i] = (SANE_Word)pSet[i]; |
485 | } |
486 | SANE_Status nStatus = ControlOption( n, SANE_ACTION_SET_VALUE, pFixedSet ); |
487 | delete [] pFixedSet; |
488 | if( nStatus != SANE_STATUS_GOOD ) |
489 | return sal_False; |
490 | return sal_True; |
491 | } |
492 | |
493 | enum FrameStyleType { |
494 | FrameStyle_BW, FrameStyle_Gray, FrameStyle_RGB, FrameStyle_Separated |
495 | }; |
496 | |
497 | #define BYTE_BUFFER_SIZE 32768 |
498 | |
499 | static inline sal_uInt8 _ReadValue( FILE* fp, int depth ) |
500 | { |
501 | if( depth == 16 ) |
502 | { |
503 | sal_uInt16 nWord; |
504 | // data always come in native byte order ! |
505 | // 16 bits is not really supported by backends as of now |
506 | // e.g. UMAX Astra 1200S delivers 16 bit but in BIGENDIAN |
507 | // against SANE documentation (xscanimage gets the same result |
508 | // as we do |
509 | size_t items_read = fread( &nWord, 1, 2, fp ); |
510 | |
511 | if (items_read != 2) |
512 | { |
513 | SAL_WARN( "extensions.scanner" , "short read, abandoning" ); |
514 | return 0; |
515 | } |
516 | |
517 | return (sal_uInt8)( nWord / 256 ); |
518 | } |
519 | sal_uInt8 nByte; |
520 | size_t items_read = fread( &nByte, 1, 1, fp ); |
521 | if (items_read != 1) |
522 | { |
523 | SAL_WARN( "extensions.scanner" , "short read, abandoning" ); |
524 | return 0; |
525 | } |
526 | return nByte; |
527 | } |
528 | |
529 | sal_Bool Sane::CheckConsistency( const char* pMes, sal_Bool bInit ) |
530 | { |
531 | static const SANE_Option_Descriptor** pDescArray = NULL; |
532 | static const SANE_Option_Descriptor* pZero = NULL; |
533 | |
534 | if( bInit ) |
535 | { |
536 | pDescArray = mppOptions; |
537 | if( mppOptions ) |
538 | pZero = mppOptions[0]; |
539 | return sal_True; |
540 | } |
541 | |
542 | sal_Bool bConsistent = sal_True; |
543 | |
544 | if( pDescArray != mppOptions ) |
545 | bConsistent = sal_False; |
546 | if( pZero != mppOptions[0] ) |
547 | bConsistent = sal_False; |
548 | |
549 | if( ! bConsistent ) |
550 | dbg_msg( "Sane is not consistent. (%s)\n" , pMes ); |
551 | |
552 | return bConsistent; |
553 | } |
554 | |
555 | sal_Bool Sane::Start( BitmapTransporter& rBitmap ) |
556 | { |
557 | int nStream = 0, nLine = 0, i = 0; |
558 | SANE_Parameters aParams; |
559 | FrameStyleType eType = FrameStyle_Gray; |
560 | sal_Bool bSuccess = sal_True; |
561 | sal_Bool bWidthSet = sal_False; |
562 | |
563 | if( ! maHandle ) |
564 | return sal_False; |
565 | |
566 | int nWidthMM = 0; |
567 | int nHeightMM = 0; |
568 | double fTLx, fTLy, fBRx, fBRy, fResl = 0.0; |
569 | int nOption; |
570 | if( ( nOption = GetOptionByName( "tl-x" ) ) != -1 && |
571 | GetOptionValue( nOption, fTLx, 0 ) && |
572 | GetOptionUnit( nOption ) == SANE_UNIT_MM ) |
573 | { |
574 | if( ( nOption = GetOptionByName( "br-x" ) ) != -1 && |
575 | GetOptionValue( nOption, fBRx, 0 ) && |
576 | GetOptionUnit( nOption ) == SANE_UNIT_MM ) |
577 | { |
578 | nWidthMM = (int)fabs(fBRx - fTLx); |
579 | } |
580 | } |
581 | if( ( nOption = GetOptionByName( "tl-y" ) ) != -1 && |
582 | GetOptionValue( nOption, fTLy, 0 ) && |
583 | GetOptionUnit( nOption ) == SANE_UNIT_MM ) |
584 | { |
585 | if( ( nOption = GetOptionByName( "br-y" ) ) != -1 && |
586 | GetOptionValue( nOption, fBRy, 0 ) && |
587 | GetOptionUnit( nOption ) == SANE_UNIT_MM ) |
588 | { |
589 | nHeightMM = (int)fabs(fBRy - fTLy); |
590 | } |
591 | } |
592 | if( ( nOption = GetOptionByName( "resolution" ) ) != -1 ) |
593 | GetOptionValue( nOption, fResl ); |
594 | |
595 | sal_uInt8* pBuffer = NULL; |
596 | |
597 | SANE_Status nStatus = SANE_STATUS_GOOD; |
598 | |
599 | rBitmap.lock(); |
600 | SvMemoryStream& aConverter = rBitmap.getStream(); |
601 | aConverter.Seek( 0 ); |
602 | aConverter.SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN ); |
603 | |
604 | // write bitmap stream header |
605 | aConverter.WriteChar( 'B' ).WriteChar( 'M' ); |
606 | aConverter.WriteUInt32( (sal_uInt32) 0 ); |
607 | aConverter.WriteUInt32( (sal_uInt32) 0 ); |
608 | aConverter.WriteUInt32( (sal_uInt32) 60 ); |
609 | |
610 | // write BITMAPINFOHEADER |
611 | aConverter.WriteUInt32( (sal_uInt32)40 ); |
612 | aConverter.WriteUInt32( (sal_uInt32)0 ); // fill in width later |
613 | aConverter.WriteUInt32( (sal_uInt32)0 ); // fill in height later |
614 | aConverter.WriteUInt16( (sal_uInt16)1 ); |
615 | // create header for 24 bits |
616 | // correct later if necessary |
617 | aConverter.WriteUInt16( (sal_uInt16)24 ); |
618 | aConverter.WriteUInt32( (sal_uInt32)0 ); |
619 | aConverter.WriteUInt32( (sal_uInt32)0 ); |
620 | aConverter.WriteUInt32( (sal_uInt32)0 ); |
621 | aConverter.WriteUInt32( (sal_uInt32)0 ); |
622 | aConverter.WriteUInt32( (sal_uInt32)0 ); |
623 | aConverter.WriteUInt32( (sal_uInt32)0 ); |
624 | |
625 | for( nStream=0; nStream < 3 && bSuccess ; nStream++ ) |
626 | { |
627 | nStatus = p_start( maHandle ); |
628 | DUMP_STATE( nStatus, "sane_start" ); |
629 | CheckConsistency( "sane_start" ); |
630 | if( nStatus == SANE_STATUS_GOOD ) |
631 | { |
632 | nStatus = p_get_parameters( maHandle, &aParams ); |
633 | DUMP_STATE( nStatus, "sane_get_parameters" ); |
634 | CheckConsistency( "sane_get_parameters" ); |
635 | if (nStatus != SANE_STATUS_GOOD || aParams.bytes_per_line == 0) |
636 | { |
637 | bSuccess = sal_False; |
638 | break; |
639 | } |
640 | #if (OSL_DEBUG_LEVEL > 1) || defined DBG_UTIL |
641 | const char* ppFormats[] = { "SANE_FRAME_GRAY" , "SANE_FRAME_RGB" , |
642 | "SANE_FRAME_RED" , "SANE_FRAME_GREEN" , |
643 | "SANE_FRAME_BLUE" , "Unknown !!!" }; |
644 | fprintf( stderr, "Parameters for frame %d:\n" , nStream ); |
645 | if( aParams.format < 0 || aParams.format > 4 ) |
646 | aParams.format = (SANE_Frame)5; |
647 | fprintf( stderr, "format: %s\n" , ppFormats[ (int)aParams.format ] ); |
648 | fprintf( stderr, "last_frame: %s\n" , aParams.last_frame ? "TRUE" : "FALSE" ); |
649 | fprintf( stderr, "depth: %d\n" , (int)aParams.depth ); |
650 | fprintf( stderr, "pixels_per_line: %d\n" , (int)aParams.pixels_per_line ); |
651 | fprintf( stderr, "bytes_per_line: %d\n" , (int)aParams.bytes_per_line ); |
652 | #endif |
653 | if( ! pBuffer ) |
654 | { |
655 | pBuffer = new sal_uInt8[ BYTE_BUFFER_SIZE < 4*aParams.bytes_per_line ? 4*aParams.bytes_per_line : BYTE_BUFFER_SIZE ]; |
656 | } |
657 | |
658 | if( aParams.last_frame ) |
659 | nStream=3; |
660 | |
661 | switch( aParams.format ) |
662 | { |
663 | case SANE_FRAME_GRAY: |
664 | eType = FrameStyle_Gray; |
665 | if( aParams.depth == 1 ) |
666 | eType = FrameStyle_BW; |
667 | break; |
668 | case SANE_FRAME_RGB: |
669 | eType = FrameStyle_RGB; |
670 | break; |
671 | case SANE_FRAME_RED: |
672 | case SANE_FRAME_GREEN: |
673 | case SANE_FRAME_BLUE: |
674 | eType = FrameStyle_Separated; |
675 | break; |
676 | default: |
677 | fprintf( stderr, "Warning: unknown frame style !!!\n" ); |
678 | } |
679 | |
680 | sal_Bool bSynchronousRead = sal_True; |
681 | |
682 | // should be fail safe, but ... ?? |
683 | nStatus = p_set_io_mode( maHandle, SANE_FALSE ); |
684 | CheckConsistency( "sane_set_io_mode" ); |
685 | if( nStatus != SANE_STATUS_GOOD ) |
686 | { |
687 | bSynchronousRead = sal_False; |
688 | nStatus = p_set_io_mode( maHandle, SANE_TRUE ); |
689 | CheckConsistency( "sane_set_io_mode" ); |
690 | #if (OSL_DEBUG_LEVEL > 1) || defined DBG_UTIL |
691 | if( nStatus != SANE_STATUS_GOOD ) |
692 | // what ?!? |
693 | fprintf( stderr, "Sane::Start: driver is confused\n" ); |
694 | #endif |
695 | } |
696 | |
697 | SANE_Int nLen=0; |
698 | SANE_Int fd = 0; |
699 | |
700 | if( ! bSynchronousRead ) |
701 | { |
702 | nStatus = p_get_select_fd( maHandle, &fd ); |
703 | DUMP_STATE( nStatus, "sane_get_select_fd" ); |
704 | CheckConsistency( "sane_get_select_fd" ); |
705 | if( nStatus != SANE_STATUS_GOOD ) |
706 | bSynchronousRead = sal_True; |
707 | } |
708 | FILE* pFrame = tmpfile(); |
709 | if( ! pFrame ) |
710 | { |
711 | bSuccess = sal_False; |
712 | break; |
713 | } |
714 | do { |
715 | if( ! bSynchronousRead ) |
716 | { |
717 | fd_set fdset; |
718 | struct timeval tv; |
719 | |
720 | FD_ZERO( &fdset ); |
721 | FD_SET( (int)fd, &fdset ); |
722 | tv.tv_sec = 5; |
723 | tv.tv_usec = 0; |
724 | if( select( fd+1, &fdset, NULL, NULL, &tv ) == 0 ) |
725 | fprintf( stderr, "Timout on sane_read descriptor\n" ); |
726 | } |
727 | nLen = 0; |
728 | nStatus = p_read( maHandle, pBuffer, BYTE_BUFFER_SIZE, &nLen ); |
729 | CheckConsistency( "sane_read" ); |
730 | if( nLen && ( nStatus == SANE_STATUS_GOOD || |
731 | nStatus == SANE_STATUS_EOF ) ) |
732 | { |
733 | bSuccess = (static_cast<size_t>(nLen) == fwrite( pBuffer, 1, nLen, pFrame )); |
734 | if (!bSuccess) |
735 | break; |
736 | } |
737 | else |
738 | DUMP_STATE( nStatus, "sane_read" ); |
739 | } while( nStatus == SANE_STATUS_GOOD ); |
740 | if (nStatus != SANE_STATUS_EOF || !bSuccess) |
741 | { |
742 | fclose( pFrame ); |
743 | bSuccess = sal_False; |
744 | break; |
745 | } |
746 | |
747 | int nFrameLength = ftell( pFrame ); |
748 | fseek( pFrame, 0, SEEK_SET ); |
749 | sal_uInt32 nWidth = (sal_uInt32) aParams.pixels_per_line; |
750 | sal_uInt32 nHeight = (sal_uInt32) (nFrameLength / aParams.bytes_per_line); |
751 | if( ! bWidthSet ) |
752 | { |
753 | if( ! fResl ) |
754 | fResl = 300; // if all else fails that's a good guess |
755 | if( ! nWidthMM ) |
756 | nWidthMM = (int)(((double)nWidth / fResl) * 25.4); |
757 | if( ! nHeightMM ) |
758 | nHeightMM = (int)(((double)nHeight / fResl) * 25.4); |
759 | #if OSL_DEBUG_LEVEL > 1 |
760 | fprintf( stderr, "set dimensions to (%d, %d) Pixel, (%d, %d) mm, resolution is %lg\n" , (int)nWidth, (int)nHeight, (int)nWidthMM, (int)nHeightMM, fResl ); |
761 | #endif |
762 | |
763 | aConverter.Seek( 18 ); |
764 | aConverter.WriteUInt32( (sal_uInt32)nWidth ); |
765 | aConverter.WriteUInt32( (sal_uInt32)nHeight ); |
766 | aConverter.Seek( 38 ); |
767 | aConverter.WriteUInt32( (sal_uInt32)(1000*nWidth/nWidthMM) ); |
768 | aConverter.WriteUInt32( (sal_uInt32)(1000*nHeight/nHeightMM) ); |
769 | bWidthSet = sal_True; |
770 | } |
771 | aConverter.Seek(60); |
772 | |
773 | if( eType == FrameStyle_BW ) |
774 | { |
775 | aConverter.Seek( 10 ); |
776 | aConverter.WriteUInt32( (sal_uInt32)64 ); |
777 | aConverter.Seek( 28 ); |
778 | aConverter.WriteUInt16( (sal_uInt16) 1 ); |
779 | aConverter.Seek( 54 ); |
780 | // write color table |
781 | aConverter.WriteUInt16( (sal_uInt16)0xffff ); |
782 | aConverter.WriteUChar( (sal_uInt8)0xff ); |
783 | aConverter.WriteUChar( (sal_uInt8)0 ); |
784 | aConverter.WriteUInt32( (sal_uInt32)0 ); |
785 | aConverter.Seek( 64 ); |
786 | } |
787 | else if( eType == FrameStyle_Gray ) |
788 | { |
789 | aConverter.Seek( 10 ); |
790 | aConverter.WriteUInt32( (sal_uInt32)1084 ); |
791 | aConverter.Seek( 28 ); |
792 | aConverter.WriteUInt16( (sal_uInt16) 8 ); |
793 | aConverter.Seek( 54 ); |
794 | // write color table |
795 | for( nLine = 0; nLine < 256; nLine++ ) |
796 | { |
797 | aConverter.WriteUChar( (sal_uInt8)nLine ); |
798 | aConverter.WriteUChar( (sal_uInt8)nLine ); |
799 | aConverter.WriteUChar( (sal_uInt8)nLine ); |
800 | aConverter.WriteUChar( (sal_uInt8)0 ); |
801 | } |
802 | aConverter.Seek( 1084 ); |
803 | } |
804 | |
805 | for (nLine = nHeight-1; nLine >= 0; --nLine) |
806 | { |
807 | fseek( pFrame, nLine * aParams.bytes_per_line, SEEK_SET ); |
808 | if( eType == FrameStyle_BW || |
809 | ( eType == FrameStyle_Gray && aParams.depth == 8 ) |
810 | ) |
811 | { |
812 | SANE_Int items_read = fread( pBuffer, 1, aParams.bytes_per_line, pFrame ); |
813 | if (items_read != aParams.bytes_per_line) |
814 | { |
815 | SAL_WARN( "extensions.scanner" , "short read, padding with zeros" ); |
816 | memset(pBuffer + items_read, 0, aParams.bytes_per_line - items_read); |
817 | } |
818 | aConverter.Write( pBuffer, aParams.bytes_per_line ); |
819 | } |
820 | else if( eType == FrameStyle_Gray ) |
821 | { |
822 | for( i = 0; i < (aParams.pixels_per_line); i++ ) |
823 | { |
824 | sal_uInt8 nGray = _ReadValue( pFrame, aParams.depth ); |
825 | aConverter.WriteUChar( nGray ); |
826 | } |
827 | } |
828 | else if( eType == FrameStyle_RGB ) |
829 | { |
830 | for( i = 0; i < (aParams.pixels_per_line); i++ ) |
831 | { |
832 | sal_uInt8 nRed, nGreen, nBlue; |
833 | nRed = _ReadValue( pFrame, aParams.depth ); |
834 | nGreen = _ReadValue( pFrame, aParams.depth ); |
835 | nBlue = _ReadValue( pFrame, aParams.depth ); |
836 | aConverter.WriteUChar( nBlue ); |
837 | aConverter.WriteUChar( nGreen ); |
838 | aConverter.WriteUChar( nRed ); |
839 | } |
840 | } |
841 | else if( eType == FrameStyle_Separated ) |
842 | { |
843 | for( i = 0; i < (aParams.pixels_per_line); i++ ) |
844 | { |
845 | sal_uInt8 nValue = _ReadValue( pFrame, aParams.depth ); |
846 | switch( aParams.format ) |
847 | { |
848 | case SANE_FRAME_RED: |
849 | aConverter.SeekRel( 2 ); |
850 | aConverter.WriteUChar( nValue ); |
851 | break; |
852 | case SANE_FRAME_GREEN: |
853 | aConverter.SeekRel( 1 ); |
854 | aConverter.WriteUChar( nValue ); |
855 | aConverter.SeekRel( 1 ); |
856 | break; |
857 | case SANE_FRAME_BLUE: |
858 | aConverter.WriteUChar( nValue ); |
859 | aConverter.SeekRel( 2 ); |
860 | break; |
861 | case SANE_FRAME_GRAY: |
862 | case SANE_FRAME_RGB: |
863 | break; |
864 | } |
865 | } |
866 | } |
867 | int nGap = aConverter.Tell() & 3; |
868 | if( nGap ) |
869 | aConverter.SeekRel( 4-nGap ); |
870 | } |
871 | fclose( pFrame ); // deletes tmpfile |
872 | if( eType != FrameStyle_Separated ) |
873 | break; |
874 | } |
875 | else |
876 | bSuccess = sal_False; |
877 | } |
878 | // get stream length |
879 | aConverter.Seek( STREAM_SEEK_TO_END ); |
880 | int nPos = aConverter.Tell(); |
881 | |
882 | aConverter.Seek( 2 ); |
883 | aConverter.WriteUInt32( (sal_uInt32) nPos+1 ); |
884 | aConverter.Seek( 0 ); |
885 | |
886 | rBitmap.unlock(); |
887 | |
888 | if( bSuccess ) |
889 | { |
890 | // only cancel a successful operation |
891 | // sane disrupts memory else |
892 | p_cancel( maHandle ); |
893 | CheckConsistency( "sane_cancel" ); |
894 | } |
895 | if( pBuffer ) |
896 | delete [] pBuffer; |
897 | |
898 | ReloadOptions(); |
899 | |
900 | |
901 | dbg_msg( "Sane::Start returns with %s\n" , bSuccess ? "TRUE" : "FALSE" ); |
902 | |
903 | return bSuccess; |
904 | } |
905 | |
906 | int Sane::GetRange( int n, double*& rpDouble ) |
907 | { |
908 | if( mppOptions[n]->constraint_type != SANE_CONSTRAINT_RANGE && |
909 | mppOptions[n]->constraint_type != SANE_CONSTRAINT_WORD_LIST ) |
910 | { |
911 | return -1; |
912 | } |
913 | |
914 | rpDouble = 0; |
915 | int nItems, i; |
916 | sal_Bool bIsFixed = mppOptions[n]->type == SANE_TYPE_FIXED ? sal_True : sal_False; |
917 | |
918 | dbg_msg( "Sane::GetRange of option %s " , mppOptions[n]->name ); |
919 | if(mppOptions[n]->constraint_type == SANE_CONSTRAINT_RANGE ) |
920 | { |
921 | double fMin, fMax, fQuant; |
922 | if( bIsFixed ) |
923 | { |
924 | fMin = SANE_UNFIX( mppOptions[n]->constraint.range->min ); |
925 | fMax = SANE_UNFIX( mppOptions[n]->constraint.range->max ); |
926 | fQuant = SANE_UNFIX( mppOptions[n]->constraint.range->quant ); |
927 | } |
928 | else |
929 | { |
930 | fMin = (double)mppOptions[n]->constraint.range->min; |
931 | fMax = (double)mppOptions[n]->constraint.range->max; |
932 | fQuant = (double)mppOptions[n]->constraint.range->quant; |
933 | } |
934 | if( fQuant != 0.0 ) |
935 | { |
936 | dbg_msg( "quantum range [ %lg ; %lg ; %lg ]\n" , |
937 | fMin, fQuant, fMax ); |
938 | nItems = (int)((fMax - fMin)/fQuant)+1; |
939 | rpDouble = new double[ nItems ]; |
940 | double fValue = fMin; |
941 | for( i = 0; i < nItems; i++, fValue += fQuant ) |
942 | rpDouble[i] = fValue; |
943 | rpDouble[ nItems-1 ] = fMax; |
944 | return nItems; |
945 | } |
946 | else |
947 | { |
948 | dbg_msg( "normal range [ %lg %lg ]\n" , |
949 | fMin, fMax ); |
950 | rpDouble = new double[2]; |
951 | rpDouble[0] = fMin; |
952 | rpDouble[1] = fMax; |
953 | return 0; |
954 | } |
955 | } |
956 | else |
957 | { |
958 | nItems = mppOptions[n]->constraint.word_list[0]; |
959 | rpDouble = new double[nItems]; |
960 | for( i=0; i<nItems; i++ ) |
961 | { |
962 | rpDouble[i] = bIsFixed ? |
963 | SANE_UNFIX( mppOptions[n]->constraint.word_list[i+1] ) : |
964 | (double)mppOptions[n]->constraint.word_list[i+1]; |
965 | } |
966 | dbg_msg( "wordlist [ %lg ... %lg ]\n" , |
967 | rpDouble[ 0 ], rpDouble[ nItems-1 ] ); |
968 | return nItems; |
969 | } |
970 | } |
971 | |
972 | static const char *ppUnits[] = { |
973 | "" , |
974 | "[Pixel]" , |
975 | "[Bit]" , |
976 | "[mm]" , |
977 | "[DPI]" , |
978 | "[%]" , |
979 | "[usec]" |
980 | }; |
981 | |
982 | OUString Sane::GetOptionUnitName( int n ) |
983 | { |
984 | OUString aText; |
985 | SANE_Unit nUnit = mppOptions[n]->unit; |
986 | size_t nUnitAsSize = (size_t)nUnit; |
987 | if (nUnitAsSize >= SAL_N_ELEMENTS( ppUnits )) |
988 | aText = "[unknown units]" ; |
989 | else |
990 | aText = OUString( ppUnits[ nUnit ], strlen(ppUnits[ nUnit ]), osl_getThreadTextEncoding() ); |
991 | return aText; |
992 | } |
993 | |
994 | sal_Bool Sane::ActivateButtonOption( int n ) |
995 | { |
996 | SANE_Status nStatus = ControlOption( n, SANE_ACTION_SET_VALUE, NULL ); |
997 | if( nStatus != SANE_STATUS_GOOD ) |
998 | return sal_False; |
999 | return sal_True; |
1000 | } |
1001 | |
1002 | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |
1003 | |