Sources/VirtualScanner.m
// File: VirtualScanner.m |
// Abstract: n/a |
// Version: 1.2 |
// |
// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple |
// Inc. ("Apple") in consideration of your agreement to the following |
// terms, and your use, installation, modification or redistribution of |
// this Apple software constitutes acceptance of these terms. If you do |
// not agree with these terms, please do not use, install, modify or |
// redistribute this Apple software. |
// |
// In consideration of your agreement to abide by the following terms, and |
// subject to these terms, Apple grants you a personal, non-exclusive |
// license, under Apple's copyrights in this original Apple software (the |
// "Apple Software"), to use, reproduce, modify and redistribute the Apple |
// Software, with or without modifications, in source and/or binary forms; |
// provided that if you redistribute the Apple Software in its entirety and |
// without modifications, you must retain this notice and the following |
// text and disclaimers in all such redistributions of the Apple Software. |
// Neither the name, trademarks, service marks or logos of Apple Inc. may |
// be used to endorse or promote products derived from the Apple Software |
// without specific prior written permission from Apple. Except as |
// expressly stated in this notice, no other rights or licenses, express or |
// implied, are granted by Apple herein, including but not limited to any |
// patent rights that may be infringed by your derivative works or by other |
// works in which the Apple Software may be incorporated. |
// |
// The Apple Software is provided by Apple on an "AS IS" basis. APPLE |
// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION |
// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS |
// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND |
// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
// |
// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL |
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, |
// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED |
// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), |
// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE |
// POSSIBILITY OF SUCH DAMAGE. |
// |
// Copyright (C) 2012 Apple Inc. All Rights Reserved. |
// |
#import <TWAIN/TWAIN.h> |
#import <sys/stat.h> |
#import <notify.h> |
#import "VirtualScanner.h" |
#import "ScannedImage.h" |
#import <ImageCaptureCore/ICScannerFunctionalUnits.h> |
#include <dlfcn.h> |
extern BOOL _gIsVirtualScanner; |
void |
virtualButtonCallback( |
CFNotificationCenterRef center, |
void* observer, |
CFStringRef name, |
const void* object, |
CFDictionaryRef userInfo |
); |
#pragma mark - |
#pragma mark VirtualScanner Private Interface |
//---------------------------------------------------------------------------------------------- VirtualScanner(Private) |
@interface VirtualScanner(Private) |
@property(readonly) unsigned int bitDepth; |
@property(readonly) unsigned int pixelType; |
@property(readonly) double xResolution; |
@property(readonly) double yResolution; |
@property(readonly) double xNativeResolution; |
@property(readonly) double yNativeResolution; |
@property(readonly) double physicalWidth; |
@property(readonly) double physicalHeight; |
- (void)readProperties; |
- (CGColorSpaceRef)newColorspaceForCurrentFunctionalUnitSettings:(unsigned int*)bitsPerComponent andBitsPerPixel:(unsigned int*)bitsPerPixel; |
// Methods to handle button presses on the device |
- (void)startReceivingButtonPressesFromDevice; |
- (void)stopReceivingButtonPressesFromDevice; |
- (void)buttonPressed:(NSNumber*)button; |
// Methods to send notifications to client |
- (void)requestOverviewScanMsg; |
- (void)showWarmUpMsg; |
- (void)doneWarmUpMsg; |
- (void)feederNoPaperMsg; |
// Raw File Based Methods |
- (BOOL)openRawFileForWriting; |
- (void)openRawFileForReading; |
- (size_t)readRawFileWithBuffer:(void*)buffer ofSize:(size_t)size; |
- (void)writeRawFileWithBuffer:(char*)buffer ofSize:(size_t)size; |
- (void)closeRawFile; |
- (void)rewindRawFile; |
// CG Data Provider Methods |
- (off_t)skipBytes:(size_t)size; |
- (void)rewind; |
- (size_t)getBytes:(void*)buffer ofSize:(size_t)size; |
- (void)releaseProvider; |
- (void)setOutputFilePath; |
- (void)saveImageWithWidth:(UInt32)scanImageWidth andHeight:(UInt32)scanImageHeight; |
// Methods to process scan parameters |
- (void)updateScanParamsUsingValuesInDictionary:(NSDictionary*)dict; |
- (void)setValueForKey:(NSString*)key fromDictionary:(NSDictionary*)dict; |
- (void)setValuesForVendorFeaturesFromDictionary:(NSDictionary*)dict; |
- (NSNumber*)getValueForKey:(NSString*)key; |
- (NSNumber*)getValueForVendorFeature:(NSString*)featureName; |
@end |
#pragma mark - |
#pragma mark Data Provider |
//----------------------------------------------------------------------------------------------------------- dpGetBytes |
static size_t dpGetBytes( void *info, void *buffer, size_t size ) |
{ |
return [((VirtualScanner*)info) getBytes:buffer ofSize:size]; |
} |
//---------------------------------------------------------------------------------------------------------- dpSkipBytes |
static off_t dpSkipBytes( void *info, off_t size ) |
{ |
return [((VirtualScanner*)info) skipBytes:size]; |
} |
//------------------------------------------------------------------------------------------------------------- dpRewind |
static void dpRewind( void *info ) |
{ |
[((VirtualScanner*)info) rewind]; |
} |
//---------------------------------------------------------------------------------------------------- dpReleaseProvider |
static void dpReleaseProvider( void *info ) |
{ |
[((VirtualScanner*)info) releaseProvider]; |
} |
#pragma mark - |
#pragma mark Button Callbacks |
//------------------------------------------------------------------------------------------------ virtualButtonCallback |
// This method is called only for a virtual scanner device. The developer should create appriate functions for |
// handling device-event callbacks for USB, FireWire and network devices. |
void |
virtualButtonCallback( |
CFNotificationCenterRef center, |
void* observer, |
CFStringRef name, |
const void* object, |
CFDictionaryRef userInfo |
) |
{ |
VirtualScanner* s = (VirtualScanner*)observer; |
if( [(NSString*)name isEqualToString:@"com.apple.VirtualScanner.scanButtonPressed"] ) |
{ |
[s performSelectorOnMainThread:@selector(buttonPressed:) withObject:[NSNumber numberWithInt:kICAButtonScan] waitUntilDone:NO]; |
} |
if( [(NSString*)name isEqualToString:@"com.apple.VirtualScanner.copyButtonPressed"] ) |
{ |
[s performSelectorOnMainThread:@selector(buttonPressed:) withObject:[NSNumber numberWithInt:kICAButtonCopy] waitUntilDone:NO]; |
} |
if( [(NSString*)name isEqualToString:@"com.apple.VirtualScanner.emailButtonPressed"] ) |
{ |
[s performSelectorOnMainThread:@selector(buttonPressed:) withObject:[NSNumber numberWithInt:kICAButtonEMail] waitUntilDone:NO]; |
} |
if( [(NSString*)name isEqualToString:@"com.apple.VirtualScanner.webButtonPressed"] ) |
{ |
[s performSelectorOnMainThread:@selector(buttonPressed:) withObject:[NSNumber numberWithInt:kICAButtonWeb] waitUntilDone:NO]; |
} |
} |
#pragma mark - |
#pragma mark Virtual Scanner implementation |
//------------------------------------------------------------------------------------------------------- VirtualScanner |
@implementation VirtualScanner |
@synthesize usbLocationID = _usbLocationID; |
@synthesize firewireGuid = _firewireGuid; |
@synthesize networkParams = _networkParams; |
@synthesize deviceObjectInfo = _deviceObjectInfo; |
@synthesize createdForDiskImage = _createdForDiskImage; |
@synthesize scannerSessionOpened = _scannerSessionOpened; |
@synthesize bsdName = _bsdName; |
@synthesize volumePath = _volumePath; |
@synthesize propertyListPath = _propertyListPath; |
@synthesize deviceInfoPath = _deviceInfoPath; |
@synthesize sampleImageFilePath = _sampleImageFilePath; |
@synthesize documentFolderPath = _documentFolderPath; |
@synthesize documentName = _documentName; |
@synthesize documentUTI = _documentUTI; |
@synthesize documentExtension = _documentExtension;; |
@synthesize documentFilePath = _documentFilePath; |
@synthesize sendProgressNotificationsWithOverviewData = _sendProgressNotificationsWithOverviewData; |
@synthesize sendProgressNotificationsWithScanData = _sendProgressNotificationsWithScanData; |
@synthesize sendProgressNotificationsWithoutData = _sendProgressNotificationsWithoutData; |
@synthesize scannedImageMetadata = _scannedImageMetadata; |
@synthesize colorSyncMode = _colorSyncMode; |
@synthesize devicePollsForButtonPresses = _devicePollsForButtonPresses; |
@dynamic lastButtonPressed; |
@synthesize selectedFunctionalUnitTypeString = _selectedFunctionalUnitTypeString; |
@synthesize maxProgressBandSize = _maxProgressBandSize; |
#pragma mark - |
//------------------------------------------------------------------------------------ initWithDiskBSDName:andDASession: |
- (id)initWithDiskBSDName:(NSString*)diskBSDName andDASession:(DASessionRef)daSession; |
{ |
if ( ( self = [super init] ) ) |
{ |
CFDictionaryRef diskDesc = NULL; |
if ( ( diskBSDName != NULL ) && ( daSession != NULL ) ) |
{ |
_diskRef = DADiskCreateFromBSDName( kCFAllocatorDefault, daSession, [diskBSDName UTF8String] ); |
diskDesc = DADiskCopyDescription( _diskRef ); |
} |
if ( diskDesc ) |
{ |
NSString* volumePath = [NSString stringWithString:[(NSURL*)[(NSDictionary*)diskDesc objectForKey:(id)kDADiskDescriptionVolumePathKey] path]]; |
if ( volumePath ) |
{ |
struct stat status; |
NSString* tempPath = NULL; |
self.createdForDiskImage = YES; |
self.volumePath = volumePath; |
self.bsdName = diskBSDName; |
tempPath = [NSString stringWithFormat:@"%@/VSCAN/ScannerProperties.plist", volumePath]; |
if ( stat( [tempPath UTF8String], &status) == 0 ) |
self.propertyListPath = tempPath; |
else |
self.propertyListPath = NULL; |
tempPath = [NSString stringWithFormat:@"%@/VSCAN/DeviceInfo.plist", volumePath]; |
if ( stat( [tempPath UTF8String], &status) == 0 ) |
self.deviceInfoPath = tempPath; |
else |
{ |
tempPath = [[NSBundle mainBundle] pathForResource:@"DeviceInfo" ofType:@"plist"]; |
if ( stat( [tempPath UTF8String], &status) == 0 ) |
self.deviceInfoPath = tempPath; |
else |
self.deviceInfoPath = NULL; |
} |
tempPath = [NSString stringWithFormat:@"%@/VSCAN/test.tiff", volumePath]; |
if ( stat( [tempPath UTF8String], &status) == 0 ) |
self.sampleImageFilePath = tempPath; |
else |
self.sampleImageFilePath = NULL; |
} |
CFRelease( diskDesc ); |
} |
else |
{ |
struct stat status; |
NSString* tempPath = NULL; |
tempPath = [[NSBundle mainBundle] pathForResource:@"ScannerProperties" ofType:@"plist"]; |
if ( stat( [tempPath UTF8String], &status) == 0 ) |
self.propertyListPath = tempPath; |
else |
self.propertyListPath = NULL; |
tempPath = [[NSBundle mainBundle] pathForResource:@"DeviceInfo" ofType:@"plist"]; |
if ( stat( [tempPath UTF8String], &status) == 0 ) |
self.deviceInfoPath = tempPath; |
else |
self.deviceInfoPath = NULL; |
tempPath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"tiff"]; |
if ( stat( [tempPath UTF8String], &status) == 0 ) |
self.sampleImageFilePath = tempPath; |
else |
self.sampleImageFilePath = NULL; |
} |
if ( self.propertyListPath && self.deviceInfoPath && self.sampleImageFilePath ) |
{ |
_scannedImages = [[NSMutableArray alloc] init]; |
[self readProperties]; |
_devicePollsForButtonPresses = NO; // set this to YES if the device module polls the device to |
// find if a button was pressed on the device |
if ( _devicePollsForButtonPresses == NO ) |
[self startReceivingButtonPressesFromDevice]; |
} |
else |
{ |
[self release]; |
self = NULL; |
} |
} |
return self; |
} |
//----------------------------------------------------------------------------------------------------------------- init |
- (id)init |
{ |
return [self initWithDiskBSDName:NULL andDASession:NULL]; |
} |
//-------------------------------------------------------------------------------------------------- initWithLocationID: |
- (id)initWithLocationID:(UInt32)locationID |
{ |
if ( ( self = [super init] ) ) |
{ |
self.usbLocationID = locationID; |
_scannedImages = [[NSMutableArray alloc] init]; |
// add code to initialize the USB device object |
} |
return self; |
} |
//------------------------------------------------------------------------------------------- initWithGUID:andIORegPath: |
- (id)initWithGUID:(UInt64)guid andIORegPath:(io_string_t)ioRegPath |
{ |
if ( ( self = [super init] ) ) |
{ |
self.firewireGuid = guid; |
_scannedImages = [[NSMutableArray alloc] init]; |
// add code to initialize the FireWire device object |
} |
return self; |
} |
//-------------------------------------------------------------------------------------------------- initWithParameters: |
- (id)initWithParameters:(NSDictionary*)params |
{ |
if ( ( self = [super init] ) ) |
{ |
self.networkParams = params; |
_scannedImages = [[NSMutableArray alloc] init]; |
// add code to initialize the TCP/IP device object |
} |
return self; |
} |
//-------------------------------------------------------------------------------------------------------------- dealloc |
- (void)dealloc |
{ |
[self stopReceivingButtonPressesFromDevice]; |
if ( _diskRef ) |
CFRelease( _diskRef ); |
[_propertyListDictionary release]; |
[_functionalUnitSettings release]; |
[_scanImageSettings release]; |
[_scannedImages release]; |
self.volumePath = NULL; |
self.sampleImageFilePath = NULL; |
self.propertyListPath = NULL; |
self.deviceInfoPath = NULL; |
self.documentFolderPath = NULL; |
self.documentName = NULL; |
self.documentExtension = NULL; |
self.documentFilePath = NULL; |
self.scannedImageMetadata = NULL; |
self.colorSyncMode = NULL; |
self.selectedFunctionalUnitTypeString = NULL; |
[super dealloc]; |
} |
#pragma mark - |
//------------------------------------------------------------------------------------------------------- readProperties |
- (void)readProperties |
{ |
_propertyListDictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:[self propertyListPath]]; |
_functionalUnitSettings = [[NSMutableDictionary alloc] init]; |
_scanImageSettings = [[NSMutableDictionary alloc] init]; |
NSMutableArray* readFunctionalUnits = [[_propertyListDictionary objectForKey:@"Scanner Properties"] objectForKey:@"functionalUnitProperties"]; |
for( NSMutableDictionary* p in readFunctionalUnits ) |
{ |
NSNumber* unitNumber = [p objectForKey:@"unitNumber"]; |
NSMutableDictionary* unitProperties = [p objectForKey:@"unitProperties"]; |
NSNumber* unitDelay = [p objectForKey:@"unitDelay"]; |
if ( unitDelay == NULL ) |
unitDelay = [NSNumber numberWithInt:0]; |
NSString *key = [NSString stringWithFormat:@"%ld",[unitNumber unsignedIntegerValue]]; |
[_functionalUnitSettings setObject:unitProperties forKey:key]; |
NSString *delayKey = [NSString stringWithFormat:@"%d-Delay", [unitNumber unsignedIntValue]]; |
[_functionalUnitSettings setObject:unitDelay forKey:delayKey]; |
self.selectedFunctionalUnitTypeString = [NSString stringWithFormat:@"%d",0]; |
} |
} |
//-------------------------------------------------------------------------------- startReceivingButtonPressesFromDevice |
- (void)startReceivingButtonPressesFromDevice |
{ |
if ( _gIsVirtualScanner ) |
{ |
CFNotificationCenterRef noteCenter = CFNotificationCenterGetDarwinNotifyCenter(); |
if ( noteCenter ) |
{ |
CFNotificationCenterAddObserver(noteCenter, |
self, |
virtualButtonCallback, |
CFSTR("com.apple.VirtualScanner.scanButtonPressed"), |
NULL, |
CFNotificationSuspensionBehaviorDeliverImmediately); |
CFNotificationCenterAddObserver(noteCenter, |
self, |
virtualButtonCallback, |
CFSTR("com.apple.VirtualScanner.copyButtonPressed"), |
NULL, |
CFNotificationSuspensionBehaviorDeliverImmediately); |
CFNotificationCenterAddObserver(noteCenter, |
self, |
virtualButtonCallback, |
CFSTR("com.apple.VirtualScanner.emailButtonPressed"), |
NULL, |
CFNotificationSuspensionBehaviorDeliverImmediately); |
CFNotificationCenterAddObserver(noteCenter, |
self, |
virtualButtonCallback, |
CFSTR("com.apple.VirtualScanner.webButtonPressed"), |
NULL, |
CFNotificationSuspensionBehaviorDeliverImmediately); |
} |
} |
else if ( self.usbLocationID != 0 ) |
{ |
// Setup to asynchronously receive scanner button presses from a USB scanner |
} |
else if ( self.firewireGuid != 0 ) |
{ |
// Setup to asynchronously receive scanner button presses from a FireWire scanner |
} |
else if ( self.networkParams != NULL ) |
{ |
// Setup to asynchronously receive scanner button presses from a network scanner |
} |
} |
//--------------------------------------------------------------------------------- stopReceivingButtonPressesFromDevice |
- (void)stopReceivingButtonPressesFromDevice |
{ |
if ( _gIsVirtualScanner ) |
{ |
CFNotificationCenterRef noteCenter = CFNotificationCenterGetDarwinNotifyCenter(); |
if ( noteCenter ) |
{ |
CFNotificationCenterRemoveObserver(noteCenter, |
self, |
CFSTR("com.apple.VirtualScanner.scanButtonPressed"), |
NULL); |
CFNotificationCenterRemoveObserver(noteCenter, |
self, |
CFSTR("com.apple.VirtualScanner.copyButtonPressed"), |
NULL); |
CFNotificationCenterRemoveObserver(noteCenter, |
self, |
CFSTR("com.apple.VirtualScanner.emailButtonPressed"), |
NULL); |
CFNotificationCenterRemoveObserver(noteCenter, |
self, |
CFSTR("com.apple.VirtualScanner.webButtonPressed"), |
NULL); |
} |
} |
else if ( self.usbLocationID != 0 ) |
{ |
// Add code to stop receiving scanner button presses from a USB scanner |
} |
else if ( self.firewireGuid != 0 ) |
{ |
// Add code to stop receiving scanner button presses from a FireWire scanner |
} |
else if ( self.networkParams != NULL ) |
{ |
// Add code to stop receiving scanner button presses from a network scanner |
} |
} |
//----------------------------------------------------------------------------------------------------------------- name |
// Return the name of the scanner |
- (NSString*)name |
{ |
NSString* n = [_propertyListDictionary objectForKey:@"Scanner Name"]; |
if ( n ) |
return n; |
else |
return @"Unknown Scanner"; |
} |
//------------------------------------------------------------------------------------------------ numberOfScannedImages |
// Return the number of scanned images cached for remote client. Scanned images are not cached for local clients |
- (NSUInteger)numberOfScannedImages |
{ |
return [_scannedImages count]; |
} |
//------------------------------------------------------------------------------------------------ isDocumentUTI_ICA_RAW |
- (BOOL)isDocumentUTI_ICA_RAW |
{ |
return [_documentUTI isEqualToString:(NSString*)kICUTTypeRaw]; |
} |
//------------------------------------------------------ newColorspaceForCurrentFunctionalUnitSettings:bitsPerComponent:andBitsPerPixel |
- (CGColorSpaceRef)newColorspaceForCurrentFunctionalUnitSettings:(unsigned int*)bitsPerComponent andBitsPerPixel:(unsigned int*)bitsPerPixel |
{ |
NSString* tDirectoryString = NSTemporaryDirectory(); |
NSString* tPathString = [tDirectoryString stringByAppendingFormat:@"vs-%d",getpid()]; |
CGColorSpaceRef colorspace = NULL; |
switch ( self.pixelType ) |
{ |
case TWPT_BW: |
colorspace = ICDCreateColorSpace( 8, 1, _deviceObjectInfo->icaObject, (CFStringRef)(self.colorSyncMode), NULL, (char*)[tPathString UTF8String]); |
*bitsPerPixel = 8; |
*bitsPerComponent = 8; |
break; |
case TWPT_GRAY: |
colorspace = ICDCreateColorSpace( self.bitDepth, 1, _deviceObjectInfo->icaObject, (CFStringRef)(self.colorSyncMode), NULL, (char*)[tPathString UTF8String]); |
*bitsPerPixel = self.bitDepth; |
*bitsPerComponent = self.bitDepth; |
break; |
case TWPT_RGB: |
colorspace = ICDCreateColorSpace(3 * self.bitDepth, 3, _deviceObjectInfo->icaObject, (CFStringRef)(self.colorSyncMode), NULL, (char*)[tPathString UTF8String]); |
*bitsPerPixel = 24*(self.bitDepth/8); |
*bitsPerComponent = self.bitDepth; |
break; |
case TWPT_CMYK: |
colorspace = ICDCreateColorSpace(4 * self.bitDepth, 4, _deviceObjectInfo->icaObject, (CFStringRef)(self.colorSyncMode), NULL, (char*)[tPathString UTF8String]); |
*bitsPerPixel = 32*(self.bitDepth/8); |
*bitsPerComponent = self.bitDepth; |
break; |
default: |
break; |
} |
return colorspace; |
} |
#pragma mark - |
#pragma mark Methods corresponding to functions in EntryPoints.m |
//------------------------------------------------------------------------------------------- addPropertiesToDictionary: |
// Add device-specific properties to propDict |
- (void)addPropertiesToDictionary:(NSMutableDictionary*)propDict |
{ |
NSString* path = self.deviceInfoPath; |
if ( path ) |
{ |
NSDictionary* deviceInfoPlistDict = [NSDictionary dictionaryWithContentsOfFile:path]; |
if ( deviceInfoPlistDict ) |
{ |
NSString* name = self.name; |
if ( name ) |
{ |
NSDictionary* devicesDict = [deviceInfoPlistDict objectForKey:@"devices"]; |
if ( devicesDict ) |
{ |
NSDictionary* deviceDict = [devicesDict objectForKey:name]; |
NSDictionary* buttonDict = [deviceDict objectForKey:@"ButtonTypes"]; |
NSString* iconFile = [deviceDict objectForKey:@"iconFile"]; |
if ( buttonDict ) |
[propDict setObject:buttonDict forKey:@"Buttons"]; |
// If this is properly setup in the DeviceInfo.plist, this is not needed. This is mostly here to add |
// more realism to the virtual scanner when a device has been created from a disk image. |
if ( iconFile && self.createdForDiskImage ) |
[propDict setObject:[NSString stringWithFormat:@"%@/VSCAN/%@", self.volumePath,[iconFile lastPathComponent]] forKey:@"deviceIconPath"]; |
} |
} |
} |
} |
// Add kICAUserAssignedDeviceNameKey. Since this key is a simple NSString, the value may be of |
// any length. This key supercedes any name already provided in the device information before, which |
// is limited to 32 characters. |
[propDict setObject:self.name forKey:(NSString*)kICAUserAssignedDeviceNameKey]; |
// Add key indicating that the module supports using the ICA Raw File as a backing store for image io |
[propDict setObject:[NSNumber numberWithInt:1] forKey:@"supportsICARawFileFormat"]; |
} |
//----------------------------------------------------------------------------------------------- openSessionWithParams: |
// Open a session |
- (ICAError)openSessionWithParams:(ICD_ScannerOpenSessionPB*)pb |
{ |
ICAError err = noErr; |
// This is a very simplistic model for checking if there is already an open session. |
// Other items in the parameter block may be useful to save if one would like to keep |
// the session information and ID, etc. |
if ( YES == self.scannerSessionOpened ) |
err = kICAInvalidSessionErr; |
else |
{ |
if ( _gIsVirtualScanner ) |
{ |
//This is done to clue the bonjour application that's broadcasting in to set the |
//availability appropriately. |
if (notify_post("com.apple.VirtualScanner.scannerUnavailable")) |
{ |
printf("Scanner Notification Failed.\n"); |
} |
} |
self.scannerSessionOpened = YES; |
} |
return err; |
} |
//---------------------------------------------------------------------------------------------- closeSessionWithParams: |
// Close a session |
- (ICAError)closeSessionWithParams:(ICD_ScannerCloseSessionPB*)pb |
{ |
ICAError err = noErr; |
// This is a very simplistic model for checking if there is already an open session. |
// Other items in the parameter block may be useful to save if one would like to keep |
// the session information and ID, etc. |
if ( YES != self.scannerSessionOpened ) |
err = kICAInvalidSessionErr; |
else |
{ |
if ( _gIsVirtualScanner ) |
{ |
//This is done to clue the bonjour application that's broadcasting in to set the |
//availability appropriately. |
if (notify_post("com.apple.VirtualScanner.scannerAvailable")) |
{ |
printf("Scanner Notification Failed.\n"); |
} |
} |
self.scannerSessionOpened = NO; |
} |
return err; |
} |
//------------------------------------------------------------------------------------- getSelectedFunctionalUnitParams: |
// Get parameters for currently selected functional unit |
- (ICAError)getSelectedFunctionalUnitParams:(ICD_ScannerGetParametersPB*)pb |
{ |
ICAError err = noErr; |
NSMutableDictionary* paramDict; |
NSMutableDictionary* deviceDict = [[NSMutableDictionary alloc] init]; |
paramDict = (NSMutableDictionary*)(pb->theDict); |
require_action(paramDict, bail, err = paramErr); |
[paramDict setObject:[_functionalUnitSettings objectForKey:self.selectedFunctionalUnitTypeString] forKey:@"device"]; |
Log( "Parameters Sent:%s\n",[[[_functionalUnitSettings objectForKey:self.selectedFunctionalUnitTypeString] description] UTF8String] ); |
bail: |
if( deviceDict ) |
[deviceDict release]; |
return err; |
} |
//------------------------------------------------------------------------------------- setSelectedFunctionalUnitParams: |
// Set parameters for currently selected functional unit |
- (ICAError)setSelectedFunctionalUnitParams:(ICD_ScannerSetParametersPB*)pb |
{ |
ICAError err = noErr; |
NSMutableDictionary* paramDict; |
NSMutableDictionary* dict; |
// modify scan params and write it back |
paramDict = (NSMutableDictionary*)(pb->theDict); |
require_action(paramDict, bail, err = -1); |
Log( "Scan Parameters Passed In:%s\n", [[paramDict description] UTF8String] ); |
if ([paramDict objectForKey:@"userScanArea"]) |
{ |
NSObject* userScanArea = [paramDict objectForKey:@"userScanArea"]; |
// Check if the user scan area is an array or a dictionary |
if ( [userScanArea isKindOfClass:[NSArray class]] ) |
{ |
NSArray *array = (NSArray*)userScanArea; |
dict = [array objectAtIndex:1]; |
} |
else |
{ |
dict = (NSMutableDictionary*)userScanArea; |
} |
if ( dict ) |
{ |
if( [dict objectForKey:@"selectedFunctionalUnitType"] ) |
{ |
Log( " Changing Functional Unit\n" ); |
// check if we are switching to the current selected unit |
NSString* selectedFunctionalUnit = [NSString stringWithFormat:@"%ld",[[dict objectForKey:@"selectedFunctionalUnitType"] unsignedIntegerValue]]; |
if ( [selectedFunctionalUnit isEqualToString:self.selectedFunctionalUnitTypeString] ) |
return noErr; |
self.selectedFunctionalUnitTypeString = [NSString stringWithFormat:@"%ld", [[dict objectForKey:@"selectedFunctionalUnitType"] unsignedIntegerValue]]; |
NSString *delayKey = [NSString stringWithFormat:@"%s-Delay", [self.selectedFunctionalUnitTypeString UTF8String]]; |
NSNumber *delay = [_functionalUnitSettings objectForKey:delayKey]; |
if ( delay ) |
usleep( 1000000LL * [delay unsignedIntValue] ); |
return noErr; |
} |
else |
{ |
Log( " Update scan parameters: \n" ); |
[self updateScanParamsUsingValuesInDictionary:dict]; |
} |
} |
// check if we want to use progressive notification |
if ([dict objectForKey:@"progressNotificationWithData"]) |
self.sendProgressNotificationsWithOverviewData = [[dict objectForKey:@"progressNotificationWithData"] boolValue]; |
else |
self.sendProgressNotificationsWithOverviewData = NO; |
if ([dict objectForKey:@"progressNotificationNoData"]) |
self.sendProgressNotificationsWithoutData = [[dict objectForKey:@"progressNotificationNoData"] boolValue]; |
else |
self.sendProgressNotificationsWithoutData = NO; |
if ([dict objectForKey:@"progressNotificationWithScanData"]) |
self.sendProgressNotificationsWithScanData = [[dict objectForKey:@"progressNotificationWithScanData"] boolValue]; |
else |
self.sendProgressNotificationsWithScanData = NO; |
if ([dict objectForKey:@"progressNotificationWithScanDataMaxBandSize"]) |
self.maxProgressBandSize = [[dict objectForKey:@"progressNotificationWithScanDataMaxBandSize"] intValue]; |
else |
self.maxProgressBandSize = 512 * 1024; |
// save color profile mode string |
self.colorSyncMode = [dict objectForKey:@"ColorSyncMode"]; |
// The value of the "document name" key is the name of the scanned document |
self.documentName = [dict objectForKey:@"document name"]; |
// The "document folder" key will be absent when a scan is performed by a remote client |
self.documentFolderPath = [dict objectForKey:@"document folder"]; |
self.documentUTI = [dict objectForKey:@"document format"]; |
if ( ( self.documentUTI == NULL ) && [[dict objectForKey:@"progressNotificationNoData"] boolValue] ) |
self.documentUTI = (NSString*)kUTTypeTIFF; |
self.documentExtension = [dict objectForKey:@"document extension"]; |
if ( ( self.documentExtension == NULL ) && [[dict objectForKey:@"progressNotificationNoData"] boolValue] ) |
{ |
NSString* ext = (NSString*)UTTypeCopyPreferredTagWithClass( (CFStringRef)self.documentUTI, kUTTagClassFilenameExtension); |
if ( ext ) |
{ |
self.documentExtension = ext; |
[ext release]; |
} |
} |
// Save metadata like rotation, dpi info, etc. |
self.scannedImageMetadata = [dict objectForKey: @"metadata"]; |
NSNumber* imageHeight = nil; |
if( ( imageHeight = [dict objectForKey:@"height"] ) ) |
[_scanImageSettings setObject:imageHeight forKey:@"scanImageHeight"]; |
NSNumber* imageWidth = nil; |
if( ( imageWidth = [dict objectForKey:@"width"] ) ) |
[_scanImageSettings setObject:imageWidth forKey:@"scanImageWidth"]; |
NSNumber* offsetX = nil; |
if( ( offsetX = [dict objectForKey:@"offsetX"] ) ) |
[_scanImageSettings setObject:offsetX forKey:@"offsetX"]; |
NSNumber* offsetY = nil; |
if( ( offsetY = [dict objectForKey:@"offsetY"] ) ) |
[_scanImageSettings setObject:offsetY forKey:@"offsetY"]; |
} |
bail: |
return err; |
} |
//--------------------------------------------------------------------------------------------- startScanningWithParams: |
// Start scanning using parameters |
/* |
Discussion: This method goes through the motions to create a byte buffer with the selected bit depth and |
output type from an image. |
In reality, receiving data from the scanner at this point and formatting the buffers for each mode would |
be the appropriate way to go, and this is only a demonstration of the virtual scanning capability at the moment. |
The developer should modify this method when scanning from a USB, FireWire or network scanner. |
*/ |
- (ICAError)startScanningWithParams:(ICD_ScannerStartPB*)pb |
{ |
ICAError err = noErr; |
BOOL userCancel = NO; |
//Scan requested DPI settings |
double xDPI = self.xResolution; |
double yDPI = self.yResolution; |
//512k will be used for each band unless overridden by band size settings from the client |
double maxBandByteSize = self.maxProgressBandSize; |
//Loadable test image |
CFURLRef fileRef = CFURLCreateWithFileSystemPath( |
kCFAllocatorDefault, |
(CFStringRef)(self.sampleImageFilePath), |
kCFURLPOSIXPathStyle, |
0 |
); |
CGImageSourceRef imageSrcRef = CGImageSourceCreateWithURL( fileRef, NULL ); |
// set up image io if we are doing final scan |
NSDictionary* metaData = (NSMutableDictionary*)CGImageSourceCopyPropertiesAtIndex( imageSrcRef, 0, NULL ); |
//Create a data provider so we can skip around the bitmap |
CGDataProviderSequentialCallbacks scanCallbacks = {0, dpGetBytes, dpSkipBytes, dpRewind, dpReleaseProvider}; |
CGDataProviderRef scanDataProvider; |
//Grab our source images parameters so we can stretch. |
//For now, we don't maintain any aspect ratio, but in the future we could figure out which aspect to clip to. |
NSNumber* imageWidth = [metaData objectForKey:@"PixelWidth"]; |
NSNumber* imageHeight = [metaData objectForKey:@"PixelHeight"]; |
if( !( imageWidth && imageHeight ) ) |
{ |
if( metaData ) |
[metaData release]; |
if( fileRef ) |
CFRelease ( fileRef ); |
if( imageSrcRef ) |
CFRelease ( imageSrcRef ); |
return kICADeviceInternalErr; |
} |
double scanImageDPIRatioWidth = ( ( self.physicalWidth / self.xNativeResolution ) * (double)xDPI); |
double scanImageDPIRatioHeight = ( ( self.physicalHeight / self.yNativeResolution ) * (double)yDPI); |
unsigned int scanImageWidth = [[_scanImageSettings objectForKey:@"scanImageWidth"] unsignedIntValue]; |
unsigned int scanImageHeight = [[_scanImageSettings objectForKey:@"scanImageHeight"] unsignedIntValue]; |
// If we wanted to specify more than one scan here for document feeder, we could do it as an example. |
int documentFeederScans = 2; |
do |
{ |
CGImageRef imageRefFinal = NULL; |
// Set up image io if we are doing the final scan |
if ( !self.sendProgressNotificationsWithOverviewData ) |
{ |
if( self.sendProgressNotificationsWithoutData ) |
{ |
// Set a unique file path for the new scan file including the temp ICA raw file as backing store |
[self setOutputFilePath]; |
// Create a raw image data file |
[self openRawFileForWriting]; |
} |
// Compute the points and offsets using the original image to create a subimage to pull from. |
double xOffsetRatio = scanImageDPIRatioWidth / [imageWidth doubleValue]; |
double yOffsetRatio = scanImageDPIRatioHeight / [imageHeight doubleValue]; |
int xCoord = xDPI * ( [[_scanImageSettings objectForKey:@"offsetX"] doubleValue] / self.xNativeResolution ); |
int yCoord = yDPI * ( [[_scanImageSettings objectForKey:@"offsetY"] doubleValue] / self.yNativeResolution ); |
CGImageRef tempImage = ( imageSrcRef ? CGImageSourceCreateImageAtIndex( imageSrcRef, 0, nil ) : nil ); |
if( tempImage ) |
{ |
imageRefFinal = CGImageCreateWithImageInRect( |
tempImage, |
CGRectMake( |
xCoord / xOffsetRatio, |
yCoord / yOffsetRatio, |
scanImageWidth / xOffsetRatio, |
scanImageHeight / yOffsetRatio |
) |
); |
CGImageRelease( tempImage ); |
} |
} |
else |
{ |
//If we are doing an overview scan we don't need to bother with the above, because the entire image will be transferred |
//regardless of subsampling. |
imageRefFinal = ( imageSrcRef ? CGImageSourceCreateImageAtIndex( imageSrcRef, 0, nil ) : nil ); |
} |
//If something happened to the image when we were doing all of the above, bail! |
if( !imageRefFinal ) |
{ |
if( metaData ) |
[metaData release]; |
if( fileRef ) |
CFRelease ( fileRef ); |
if( imageSrcRef ) |
CFRelease ( imageSrcRef ); |
return kICADeviceInternalErr; |
} |
// Fake warmup status |
unsigned int warmupTestTime = 0500000L; |
if( warmupTestTime > 0 ) |
{ |
[self showWarmUpMsg]; |
// Warming up those transistors and tubes. |
usleep(warmupTestTime); |
[self doneWarmUpMsg]; |
} |
//Setup bitmap context information |
CGContextRef bitmapContext = nil; |
// Create a color space profile based on the settings that were passed in, retrieve bit settings |
unsigned int bitsPerComponent = 0; |
unsigned int bitsPerPixel = 0; |
CGColorSpaceRef newcolorspace = [self newColorspaceForCurrentFunctionalUnitSettings:&bitsPerComponent andBitsPerPixel:&bitsPerPixel]; |
CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel( newcolorspace ); |
int bytesPerComponent = bitsPerComponent/8; |
int numComponents = ( bitsPerPixel / bitsPerComponent ); |
CGImageAlphaInfo alphaInfo = kCGImageAlphaNone; |
ICScannerPixelDataType pixelDataType = ICScannerPixelDataTypeRGB; |
switch ( colorSpaceModel ) |
{ |
case kCGColorSpaceModelMonochrome: |
{ |
alphaInfo = kCGImageAlphaNone; |
pixelDataType = ICScannerPixelDataTypeGray; |
} |
break; |
case kCGColorSpaceModelCMYK: |
{ |
alphaInfo = kCGImageAlphaNone; |
pixelDataType = ICScannerPixelDataTypeCMYK; |
} |
break; |
case kCGColorSpaceModelRGB: |
default: |
{ |
alphaInfo = kCGImageAlphaPremultipliedLast; |
pixelDataType = ICScannerPixelDataTypeRGB; |
} |
break; |
} |
// Setting up the bitmap context variables |
unsigned int bitmapBytesPerRow = scanImageWidth * ( numComponents*bytesPerComponent + ( ( alphaInfo == kCGImageAlphaNone ) ? 0:1 )*bytesPerComponent ); |
int bitmapRowsPerBand = ( bitmapBytesPerRow >= maxBandByteSize ) ? 1 : ( maxBandByteSize / bitmapBytesPerRow ); |
int bitmapNumBands = scanImageHeight / bitmapRowsPerBand; |
int bitmapLastBandSize = scanImageHeight % bitmapRowsPerBand; |
unsigned int bitmapBytesPerBand = bitmapBytesPerRow * bitmapRowsPerBand; |
UInt8* bitmapImage = nil; |
// Setting up image context variables |
int imageBytesPerRow = 0; |
int imageBitsPerPixel = 0; |
int imageBitsPerComponent = 0; |
int imageSkipBits = 0; |
int imageContextRowsPerBand = 0; |
int imageBlackThreshold = 0x80; |
if ( self.bitDepth != 1 ) |
{ |
imageBytesPerRow = ( scanImageWidth * numComponents * ( bytesPerComponent ) ); |
imageBitsPerPixel = bitsPerPixel; |
imageBitsPerComponent = bitsPerComponent; |
} |
else |
{ |
imageBytesPerRow = ( scanImageWidth * numComponents * ( bytesPerComponent ) ) / 8; |
imageSkipBits = ( scanImageWidth * numComponents * ( bytesPerComponent ) ) % 8; |
if( imageSkipBits > 1 ) |
imageBytesPerRow++; |
imageBitsPerPixel = 1; |
imageBitsPerComponent = 1; |
} |
bitmapImage = malloc( bitmapBytesPerBand ); |
//Start point for the Translate needs to be in the negative y coordinate starting at the top of the image |
int startPoint = scanImageHeight*-1; |
//Start point for the Image to be sent to ImageCapture Extension needs to be in the positive coordinate starting at zero |
int fillPoint = 0; |
// write ica raw file image header |
if ( self.sendProgressNotificationsWithoutData ) |
{ |
ICARawFileHeader* h = (ICARawFileHeader *)bitmapImage; |
h->imageDataOffset = sizeof(ICARawFileHeader); |
h->version = 1; |
h->imageWidth = scanImageWidth; |
h->imageHeight = scanImageHeight; |
h->bytesPerRow = imageBytesPerRow; |
h->bitsPerComponent = imageBitsPerComponent; |
h->bitsPerPixel = imageBitsPerPixel; |
h->numberOfComponents = numComponents; |
h->cgColorSpaceModel = CGColorSpaceGetModel(newcolorspace); |
h->bitmapInfo = kCGImageAlphaNone; |
h->dpi = xDPI; |
h->orientation = [self.scannedImageMetadata objectForKey:@"Orientation"] ? |
[[self.scannedImageMetadata objectForKey:@"Orientation"] intValue] : 1; |
strlcpy( h->colorSyncModeStr, [self.colorSyncMode UTF8String], sizeof(h->colorSyncModeStr) ); |
[self writeRawFileWithBuffer:(char*)bitmapImage ofSize:sizeof(ICARawFileHeader)]; |
} |
for ( int i = bitmapNumBands; (i >= 0 && !userCancel); --i ) |
{ |
if( ( i == bitmapNumBands ) && ( bitmapNumBands != 0 ) ) |
{ |
bitmapContext = CGBitmapContextCreate( |
bitmapImage, |
scanImageWidth, |
bitmapRowsPerBand, |
bitsPerComponent, |
bitmapBytesPerRow, |
newcolorspace, |
alphaInfo |
); |
imageContextRowsPerBand = bitmapRowsPerBand; |
} |
else if( ( bitmapLastBandSize > 1 ? 1 : 0 ) && i == 0 ) |
{ |
CGContextRelease( bitmapContext ); |
bitmapContext = CGBitmapContextCreate( |
bitmapImage, |
scanImageWidth, |
bitmapLastBandSize, |
bitsPerComponent, |
bitmapBytesPerRow, |
newcolorspace, |
alphaInfo |
); |
imageContextRowsPerBand = bitmapLastBandSize; |
} |
if ( bitmapContext ) |
{ |
startPoint += imageContextRowsPerBand; |
CGContextSaveGState(bitmapContext); |
CGRect drawRect = CGRectMake( 0, 0, scanImageWidth, scanImageHeight ); |
CGContextTranslateCTM( bitmapContext, 0, startPoint ); |
CGContextClearRect( bitmapContext, drawRect ); |
CGContextDrawImage( bitmapContext, drawRect, imageRefFinal ); |
//If there is an alpha channel, we have to fake like we're coming from a scanner and |
//remove it here. |
if( ( kCGImageAlphaNone != alphaInfo ) ) |
{ |
int offset = 0; |
int j = 0; |
for( j=numComponents*bytesPerComponent; j < (imageContextRowsPerBand*imageBytesPerRow); j+=numComponents*bytesPerComponent ) |
{ |
int k = 0; |
for( k = 0; k < numComponents*bytesPerComponent; k++) |
bitmapImage[j+k] = bitmapImage[j+k+bytesPerComponent+offset]; |
offset+=bytesPerComponent; |
} |
} |
else if ( imageBitsPerPixel == 1 ) |
{ |
int offset = 0; |
int j = 0; |
int rowPos = 0; |
int inc = 8; |
for ( j = 0; j < imageContextRowsPerBand*bitmapBytesPerRow; j+=inc ) |
{ |
char b = 0; |
for( int k = 0; k<8 && rowPos != bitmapBytesPerRow; k++ ) |
{ |
b += ( bitmapImage[k+j] > imageBlackThreshold ? 1 : 0 ) << 7-k; |
rowPos++; |
} |
if( rowPos == bitmapBytesPerRow ) |
{ |
if( imageSkipBits!=0 ) |
inc = imageSkipBits; |
rowPos = 0; |
} |
else |
inc = 8; |
bitmapImage[offset]=b; |
offset++; |
} |
} |
ICASendNotificationPB notePB = {}; |
NSMutableDictionary* notification = [[NSMutableDictionary alloc] initWithCapacity:0]; |
[notification setObject:[NSNumber numberWithUnsignedInt:_deviceObjectInfo->icaObject] forKey:(id)kICANotificationICAObjectKey]; |
[notification setObject:(id)kICANotificationTypeScanProgressStatus forKey:(id)kICANotificationTypeKey]; |
notePB.notificationDictionary = (CFMutableDictionaryRef)notification; |
if ( self.sendProgressNotificationsWithOverviewData ) |
{ |
ICDAddImageInfoToNotificationDictionary( |
(CFMutableDictionaryRef)notification, |
scanImageWidth, |
scanImageHeight, |
imageBytesPerRow, |
fillPoint, |
imageContextRowsPerBand, |
(imageBytesPerRow*imageContextRowsPerBand), |
bitmapImage |
); |
} |
else if ( self.sendProgressNotificationsWithScanData ) |
{ |
void *dlHandle; |
// Open ICADevices to check and see if we can use the banded data function. |
// This will ensure interoperability with both 10.6 and 10.7+ |
dlHandle = dlopen("/System/Library/Frameworks/ICADevices.framework/ICADevices", RTLD_LOCAL | RTLD_LAZY ); |
if ( NULL != dlHandle ) |
{ |
int (*fPtr)(CFMutableDictionaryRef, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, ICScannerPixelDataType, unsigned int, int, unsigned int, unsigned int, void*); |
*(void **)(&fPtr) = dlsym( dlHandle, "ICDAddBandInfoToNotificationDictionary"); |
if( NULL != fPtr ) |
{ |
(*fPtr)( (CFMutableDictionaryRef)notification, |
scanImageWidth, |
scanImageHeight, |
imageBitsPerPixel, |
imageBitsPerComponent, |
numComponents, |
0, |
pixelDataType, |
imageBytesPerRow, |
fillPoint, |
imageContextRowsPerBand, |
(imageBytesPerRow*imageContextRowsPerBand), |
bitmapImage |
); |
} |
dlclose(dlHandle); |
} |
} |
else if ( self.sendProgressNotificationsWithoutData ) |
{ |
ICDAddImageInfoToNotificationDictionary( |
(CFMutableDictionaryRef)notification, |
scanImageWidth, |
scanImageHeight, |
imageBytesPerRow, |
fillPoint, |
imageContextRowsPerBand, |
0, |
NULL |
); |
// write band to raw image file |
[self writeRawFileWithBuffer:(char*)bitmapImage ofSize:(imageBytesPerRow*imageContextRowsPerBand)]; |
if( i == 0 ) |
[self closeRawFile]; |
} |
if ( ICDSendNotificationAndWaitForReply( ¬ePB ) == noErr ) |
{ |
userCancel = (notePB.replyCode == userCanceledErr); |
} |
[notification release]; |
fillPoint+= (imageContextRowsPerBand); |
CGContextRestoreGState(bitmapContext); |
} |
} |
// set up image io if we are doing final scan and we are generating real image file ( not ICA RAW file ). |
if ( self.sendProgressNotificationsWithoutData && (self.isDocumentUTI_ICA_RAW == NO) ) |
{ |
scanDataProvider = CGDataProviderCreateSequential( self, &scanCallbacks ); |
_rawCGImageRef = CGImageCreate( |
scanImageWidth, |
scanImageHeight, |
imageBitsPerComponent, |
imageBitsPerPixel, |
imageBytesPerRow, |
newcolorspace, |
kCGImageAlphaNone, |
scanDataProvider, |
NULL, |
false, |
kCGRenderingIntentDefault |
); |
if ( NULL == _rawCGImageRef ) |
{ |
printf(" ERROR - CGImageCreate failed\n"); |
} |
CGDataProviderRelease( scanDataProvider ); |
// user did not cancel |
if ( !userCancel ) |
{ |
// write image file via data provider processing raw image data file |
[self saveImageWithWidth: scanImageWidth andHeight:scanImageHeight]; |
} |
if (_rawCGImageRef) |
{ |
CGImageRelease(_rawCGImageRef); |
_rawCGImageRef = NULL; |
} |
} |
if ( bitmapContext ) |
CGContextRelease( bitmapContext ); |
//Send the page done notifications. |
ICASendNotificationPB notePB = {}; |
NSMutableDictionary* notification = [[NSMutableDictionary alloc] initWithCapacity:0]; |
// send page done notification |
[notification setObject:[NSNumber numberWithUnsignedInt:_deviceObjectInfo->icaObject] forKey:(id)kICANotificationICAObjectKey]; |
[notification setObject:(id)kICANotificationTypeScannerPageDone forKey:(id)kICANotificationTypeKey]; |
if ( self.sendProgressNotificationsWithoutData ) |
CFDictionaryAddValue( (CFMutableDictionaryRef)notification, kICANotificationScannerDocumentNameKey, self.documentFilePath ); |
notePB.notificationDictionary = (CFMutableDictionaryRef)notification; |
ICDSendNotification( ¬ePB ); |
[notification release]; |
documentFeederScans--; |
free( bitmapImage ); |
// If the client is a network client trigger a new object created for every page that has been scanned |
if ( ( !userCancel ) && |
( self.documentFolderPath == NULL ) && |
( self.sendProgressNotificationsWithoutData ) ) |
{ |
ScannedImage* newImage = [[ScannedImage alloc] initWithFilePath:self.documentFilePath scannerObject:self imageWidth:scanImageWidth imageHeight:scanImageHeight]; |
if ( newImage ) |
{ |
ICAObject newICAObject = 0; |
[_scannedImages addObject:newImage]; |
[newImage release]; |
if ( ICDScannerNewObjectInfoCreated( _deviceObjectInfo, 0, &newICAObject ) == noErr ) |
{ |
newImage.icaObject = newICAObject; |
memset( ¬ePB, 0, sizeof(notePB) ); |
notification = [[NSMutableDictionary alloc] initWithCapacity:0]; |
[notification setObject:[NSNumber numberWithUnsignedInt:newICAObject] forKey:(id)kICANotificationICAObjectKey]; |
[notification setObject:(id)kICANotificationTypeObjectAdded forKey:(id)kICANotificationTypeKey]; |
notePB.notificationDictionary = (CFMutableDictionaryRef)notification; |
ICDSendNotification( ¬ePB ); |
[notification release]; |
} |
else |
{ |
[_scannedImages removeObject:newImage]; |
} |
} |
} |
if( imageRefFinal ) |
CGImageRelease( imageRefFinal ); |
if( newcolorspace ) |
CGColorSpaceRelease( newcolorspace ); |
} |
while ( ( [self.selectedFunctionalUnitTypeString isEqualToString:@"3"] ) && ( documentFeederScans > 0 ) && !userCancel ); |
// send job done notification |
ICASendNotificationPB notePB = {}; |
NSMutableDictionary* notification = [[NSMutableDictionary alloc] initWithCapacity:0]; |
[notification setObject:[NSNumber numberWithUnsignedInt:_deviceObjectInfo->icaObject] forKey:(id)kICANotificationICAObjectKey]; |
[notification setObject:(id)kICANotificationTypeScannerScanDone forKey:(id)kICANotificationTypeKey]; |
notePB.notificationDictionary = (CFMutableDictionaryRef)notification; |
ICDSendNotification( ¬ePB ); |
[notification release]; |
if ( metaData ) |
CFRelease( metaData ); |
if ( fileRef ) |
CFRelease( fileRef ); |
if ( imageSrcRef ) |
CFRelease( imageSrcRef ); |
if ( userCancel ) |
{ |
printf("----> User Canceled Scan! \n"); |
ICASendNotificationPB notePB = {}; |
NSMutableDictionary* notification = [NSMutableDictionary dictionaryWithCapacity:0]; |
[notification setObject:[NSNumber numberWithUnsignedInt:_deviceObjectInfo->icaObject] forKey:(id)kICANotificationICAObjectKey]; |
[notification setObject:(id)kICANotificationTypeTransactionCanceled forKey:(id)kICANotificationTypeKey]; |
notePB.notificationDictionary = (CFMutableDictionaryRef)notification; |
err = ICDSendNotification( ¬ePB ); |
} |
bail: |
return err; |
} |
#pragma mark - |
#pragma mark Accessors for scan parameters |
//--------------------------------------------------------------------------------------- setValueForKey:fromDictionary: |
- (void)setValueForKey:(NSString*)key fromDictionary:(NSDictionary*)dict |
{ |
if( [dict objectForKey:key] ) |
{ |
NSMutableDictionary* selectedFunctionalUnitSettings = [_functionalUnitSettings objectForKey:self.selectedFunctionalUnitTypeString]; |
NSMutableDictionary* scannerElement = [selectedFunctionalUnitSettings objectForKey:key]; |
if( scannerElement ) |
{ |
if( [[scannerElement objectForKey:@"type"] isEqualToString:@"TWON_ONEVALUE"] ) |
{ |
[selectedFunctionalUnitSettings setObject: [dict objectForKey:key] forKey:key]; |
} |
else if( [[scannerElement objectForKey:@"type"] isEqualToString:@"TWON_ENUMERATION"] ) |
{ |
int index = 0; |
for( NSNumber* scannerElementValue in [scannerElement objectForKey:@"value"] ) |
{ |
if( [scannerElementValue isEqualToNumber:[[dict objectForKey:key] objectForKey:@"value"]] ) |
{ |
[scannerElement setObject:[NSNumber numberWithUnsignedInt:index] forKey:@"current"]; |
break; |
} |
index++; |
} |
} |
else if( [[scannerElement objectForKey:@"type"] isEqualToString:@"TWON_RANGE"] ) |
{ |
NSNumber* minValue = [scannerElement objectForKey:@"min"]; |
NSNumber* maxValue = [scannerElement objectForKey:@"max"]; |
NSNumber* value = [[dict objectForKey:key] objectForKey:@"value"]; |
if( [value unsignedIntegerValue] >= [minValue unsignedIntegerValue] && |
[value unsignedIntegerValue] <= [maxValue unsignedIntegerValue] ) |
{ |
[scannerElement setObject:[NSNumber numberWithUnsignedInt:[value unsignedIntegerValue]] forKey:@"current"]; |
} |
} |
} |
else |
{ |
[selectedFunctionalUnitSettings setObject: [dict objectForKey:key] forKey:key]; |
} |
} |
} |
//---------------------------------------------------------------------------- setValuesForVendorFeaturesFromDictionary: |
- (void)setValuesForVendorFeaturesFromDictionary:(NSDictionary*)dict |
{ |
NSMutableDictionary* selectedFunctionalUnitSettings = [_functionalUnitSettings objectForKey:self.selectedFunctionalUnitTypeString]; |
NSArray* currentVendorFeatures = [selectedFunctionalUnitSettings objectForKey:@"vendor features"]; |
NSArray* inputVendorFeatures = [dict objectForKey:@"vendor features"]; |
if( currentVendorFeatures ) |
{ |
for ( NSMutableDictionary *currentFeature in currentVendorFeatures ) |
{ |
for ( NSDictionary *inputFeature in inputVendorFeatures ) |
{ |
if( [[currentFeature objectForKey:@"feature"] isEqualToString:[inputFeature objectForKey:@"feature"]] ) |
{ |
if( [[currentFeature objectForKey:@"type"] isEqualToString:@"TWON_ONEVALUE"] ) |
{ |
[currentFeature setObject:[inputFeature objectForKey:@"value"] forKey:@"value"]; |
} |
else if( [[currentFeature objectForKey:@"type"] isEqualToString:@"TWON_ENUMERATION"] ) |
{ |
int index = 0; |
for( NSNumber* featureValue in [currentFeature objectForKey:@"value"] ) |
{ |
if( [featureValue isEqualToNumber:[inputFeature objectForKey:@"value"]] ) |
{ |
[currentFeature setObject:[NSNumber numberWithUnsignedInt:index] forKey:@"current"]; |
break; |
} |
index++; |
} |
} |
else if( [[currentFeature objectForKey:@"type"] isEqualToString:@"TWON_RANGE"] ) |
{ |
NSNumber* minValue = [currentFeature objectForKey:@"min"]; |
NSNumber* maxValue = [currentFeature objectForKey:@"max"]; |
NSNumber* value = [inputFeature objectForKey:@"value"]; |
if( [value unsignedIntegerValue] >= [minValue unsignedIntegerValue] && |
[value unsignedIntegerValue] <= [maxValue unsignedIntegerValue] ) |
{ |
[currentFeature setObject:[NSNumber numberWithUnsignedInt:[value unsignedIntegerValue]] forKey:@"current"]; |
} |
} |
break; |
} |
} |
} |
} |
} |
//------------------------------------------------------------------------------------------------------ getValueForKey: |
- (NSNumber*)getValueForKey:(NSString*)key |
{ |
NSMutableDictionary* selectedFunctionalUnitSettings = [_functionalUnitSettings objectForKey:self.selectedFunctionalUnitTypeString]; |
NSMutableDictionary* scannerElement = [selectedFunctionalUnitSettings objectForKey:key]; |
if( scannerElement ) |
{ |
if( [[scannerElement objectForKey:@"type"] isEqualToString:@"TWON_ONEVALUE"] ) |
{ |
return [scannerElement objectForKey:@"value"]; |
} |
else if( [[scannerElement objectForKey:@"type"] isEqualToString:@"TWON_ENUMERATION"] ) |
{ |
NSNumber* current = [scannerElement objectForKey:@"current"]; |
NSArray* values = [scannerElement objectForKey:@"value"]; |
return [values objectAtIndex:[current unsignedIntValue]]; |
} |
else if( [[scannerElement objectForKey:@"type"] isEqualToString:@"TWON_RANGE"] ) |
{ |
return [scannerElement objectForKey:@"current"]; |
} |
} |
return nil; |
} |
//-------------------------------------------------------------------------------------------- getValueForVendorFeature: |
- (NSNumber*)getValueForVendorFeature:(NSString*)featureName |
{ |
NSMutableDictionary* selectedFunctionalUnitSettings = [_functionalUnitSettings objectForKey:self.selectedFunctionalUnitTypeString]; |
NSArray* vendorFeatures = [selectedFunctionalUnitSettings objectForKey:@"vendor features"]; |
if( vendorFeatures ) |
{ |
for ( NSDictionary *feature in vendorFeatures ) |
{ |
if( [[feature objectForKey:@"feature"] isEqualToString:featureName] ) |
{ |
if( [[feature objectForKey:@"type"] isEqualToString:@"TWON_ONEVALUE"] ) |
{ |
return [feature objectForKey:@"value"]; |
} |
else if( [[feature objectForKey:@"type"] isEqualToString:@"TWON_ENUMERATION"] ) |
{ |
NSNumber* current = [feature objectForKey:@"current"]; |
NSArray* values = [feature objectForKey:@"value"]; |
return [values objectAtIndex:[current unsignedIntValue]]; |
} |
else if( [[feature objectForKey:@"type"] isEqualToString:@"TWON_RANGE"] ) |
{ |
return [feature objectForKey:@"current"]; |
} |
} |
} |
} |
return nil; |
} |
//--------------------------------------------------------------------------------------------------------------- setter |
- (void)updateScanParamsUsingValuesInDictionary:(NSDictionary*)dict |
{ |
[self setValueForKey:@"ICAP_BITDEPTH" fromDictionary:dict]; |
[self setValueForKey:@"ICAP_PIXELTYPE" fromDictionary:dict]; |
[self setValueForKey:@"ICAP_UNITS" fromDictionary:dict]; |
[self setValueForKey:@"ICAP_XRESOLUTION" fromDictionary:dict]; |
[self setValueForKey:@"ICAP_YRESOLUTION" fromDictionary:dict]; |
[self setValueForKey:@"ICAP_XSCALING" fromDictionary:dict]; |
[self setValueForKey:@"ICAP_YSCALING" fromDictionary:dict]; |
[self setValuesForVendorFeaturesFromDictionary:dict]; |
} |
//-------------------------------------------------------------------------------------------------------------- getters |
- (unsigned int)bitDepth { return [[self getValueForKey:@"ICAP_BITDEPTH"] unsignedIntValue]; } |
- (unsigned int)pixelType { return [[self getValueForKey:@"ICAP_PIXELTYPE"] unsignedIntValue]; } |
- (double)xResolution { return [[self getValueForKey:@"ICAP_XRESOLUTION"] doubleValue]; } |
- (double)yResolution { return [[self getValueForKey:@"ICAP_YRESOLUTION"] doubleValue]; } |
- (double)xNativeResolution { return [[self getValueForKey:@"ICAP_XNATIVERESOLUTION"] doubleValue]; } |
- (double)yNativeResolution { return [[self getValueForKey:@"ICAP_YNATIVERESOLUTION"] doubleValue]; } |
- (double)physicalWidth { return [[self getValueForKey:@"ICAP_PHYSICALWIDTH"] doubleValue]; } |
- (double)physicalHeight { return [[self getValueForKey:@"ICAP_PHYSICALHEIGHT"] doubleValue]; } |
#pragma mark - |
#pragma mark Method to send out notifications to client |
//----------------------------------------------------------------------------------------------- requestOverviewScanMsg |
- (void)requestOverviewScanMsg |
{ |
ICASendNotificationPB notePB = {}; |
NSMutableDictionary* notification = [[NSMutableDictionary alloc] initWithCapacity:0]; |
// Send a message to client to invoke an overview scan |
[notification setObject:[NSNumber numberWithUnsignedInt:_deviceObjectInfo->icaObject] forKey:(id)kICANotificationICAObjectKey]; |
[notification setObject:(id)kICANotificationTypeDeviceStatusInfo forKey:(id)kICANotificationTypeKey]; |
[notification setObject:(id)kICANotificationSubTypePerformOverviewScan forKey:(id)kICANotificationSubTypeKey]; |
notePB.notificationDictionary = (CFMutableDictionaryRef)notification; |
ICDSendNotification( ¬ePB ); |
[notification release]; |
} |
//-------------------------------------------------------------------------------------------------------- showWarmUpMsg |
- (void)showWarmUpMsg |
{ |
ICASendNotificationPB notePB = {}; |
NSMutableDictionary* notification = [[NSMutableDictionary alloc] initWithCapacity:0]; |
// Send scanner warm up started notification |
[notification setObject:[NSNumber numberWithUnsignedInt:_deviceObjectInfo->icaObject] forKey:(id)kICANotificationICAObjectKey]; |
[notification setObject:(id)kICANotificationTypeDeviceStatusInfo forKey:(id)kICANotificationTypeKey]; |
[notification setObject:(id)kICANotificationSubTypeWarmUpStarted forKey:(id)kICANotificationSubTypeKey]; |
notePB.notificationDictionary = (CFMutableDictionaryRef)notification; |
ICDSendNotification( ¬ePB ); |
[notification release]; |
} |
//-------------------------------------------------------------------------------------------------------- doneWarmUpMsg |
- (void)doneWarmUpMsg |
{ |
ICASendNotificationPB notePB = {}; |
NSMutableDictionary* notification = [[NSMutableDictionary alloc] initWithCapacity:0]; |
// Send scanner warm up done notification |
[notification setObject:[NSNumber numberWithUnsignedInt:_deviceObjectInfo->icaObject] forKey:(id)kICANotificationICAObjectKey]; |
[notification setObject:(id)kICANotificationTypeDeviceStatusInfo forKey:(id)kICANotificationTypeKey]; |
[notification setObject:(id)kICANotificationSubTypeWarmUpDone forKey:(id)kICANotificationSubTypeKey]; |
notePB.notificationDictionary = (CFMutableDictionaryRef)notification; |
ICDSendNotification( ¬ePB ); |
[notification release]; |
} |
//----------------------------------------------------------------------------------------------------- feederNoPaperMsg |
- (void)feederNoPaperMsg |
{ |
ICASendNotificationPB notePB = {}; |
NSMutableDictionary* notification = [[NSMutableDictionary alloc] initWithCapacity:0]; |
// Send scanner document feeder error notification |
[notification setObject:[NSNumber numberWithUnsignedInt:_deviceObjectInfo->icaObject] forKey:(id)kICANotificationICAObjectKey]; |
[notification setObject:(id)kICANotificationTypeDeviceStatusError forKey:(id)kICANotificationTypeKey]; |
[notification setObject:@"kICAErrStrDFEmptyErr" forKey:(id)kICANotificationSubTypeKey]; // error strings located in ICADevices.framework |
notePB.notificationDictionary = (CFMutableDictionaryRef)notification; |
ICDSendNotification( ¬ePB ); |
[notification release]; |
} |
//--------------------------------------------------------------------------------------------------- createInvertedRect |
- (NSData*)newInvertedRect:(NSDictionary*)paramDict |
{ |
if( [[self getValueForVendorFeature:@"VF_Invert"] unsignedIntValue] == YES ) |
{ |
NSNumber* xDPI = [[paramDict objectForKey:@"ICAP_XRESOLUTION"] objectForKey:@"value"]; |
NSNumber* yDPI = [[paramDict objectForKey:@"ICAP_YRESOLUTION"] objectForKey:@"value"]; |
//Loadable test image |
CFURLRef fileRef= CFURLCreateWithFileSystemPath( kCFAllocatorDefault, |
(CFStringRef)(self.sampleImageFilePath), |
kCFURLPOSIXPathStyle, 0 |
); |
CGImageSourceRef imageSrcRef = CGImageSourceCreateWithURL( fileRef, NULL ); |
CGImageRef imageRefOrig = ( imageSrcRef ? CGImageSourceCreateImageAtIndex( imageSrcRef, 0, nil ) : nil ); |
CGImageRef imageRefFinal = NULL; |
// set up image io if we are doing final scan |
NSDictionary * metaData = (NSMutableDictionary*)CGImageSourceCopyPropertiesAtIndex(imageSrcRef, 0, NULL); |
//Grab our source images parameters so we can stretch. |
//For now, we don't maintain any aspect ratio, but in the future we could figure out which aspect to clip to. |
NSNumber* imageWidth = [metaData objectForKey:@"PixelWidth"]; |
NSNumber* imageHeight = [metaData objectForKey:@"PixelHeight"]; |
if( !( imageWidth && imageHeight ) ) |
{ |
if( metaData ) |
[metaData release]; |
if( fileRef ) |
CFRelease ( fileRef ); |
if( imageSrcRef ) |
CFRelease ( imageSrcRef ); |
if( imageRefOrig ) |
CFRelease ( imageRefOrig ); |
return NULL; |
} |
double scanImageDPIRatioWidth = ( ( self.physicalWidth / self.xNativeResolution ) * [xDPI doubleValue]); |
double scanImageDPIRatioHeight = ( ( self.physicalHeight / self.yNativeResolution ) * [yDPI doubleValue]); |
unsigned int scanImageWidth = [[paramDict objectForKey:@"width"] unsignedIntValue]; |
unsigned int scanImageHeight = [[paramDict objectForKey:@"height"] unsignedIntValue]; |
// Compute the points and offsets using the original image to create a subimage to pull from. |
double xOffsetRatio = scanImageDPIRatioWidth / [imageWidth doubleValue]; |
double yOffsetRatio = scanImageDPIRatioHeight / [imageHeight doubleValue]; |
int xCoord = ( [[paramDict objectForKey:@"offsetX"] doubleValue] ); |
int yCoord = ( [[paramDict objectForKey:@"offsetY"] doubleValue] ); |
imageRefFinal = CGImageCreateWithImageInRect(imageRefOrig, |
CGRectMake( |
xCoord / xOffsetRatio, |
yCoord / yOffsetRatio, |
scanImageWidth / xOffsetRatio, |
scanImageHeight / yOffsetRatio |
) |
); |
//If something happened to the image when we were doing all of the above, bail! |
if( !imageRefFinal ) |
{ |
if( metaData ) |
[metaData release]; |
if( fileRef ) |
CFRelease ( fileRef ); |
if( imageSrcRef ) |
CFRelease ( imageSrcRef ); |
if( imageRefOrig ) |
CFRelease ( imageRefOrig ); |
return NULL; |
} |
//Setup bitmap context information |
CGContextRef bitmapContext = nil; |
unsigned int bytesPerRow = scanImageWidth * 4; |
unsigned int bytesPerBand = bytesPerRow * scanImageHeight; |
UInt8* bitmap = nil; |
//Fix this, we should be using the colorspace that we received from the client. |
CGColorSpaceRef newcolorspace = CGColorSpaceCreateDeviceRGB(); |
bitmap = malloc( bytesPerBand ); |
bitmapContext = CGBitmapContextCreate( |
bitmap, |
scanImageWidth, |
scanImageHeight, |
8, /* bits per component */ |
bytesPerRow, |
newcolorspace, |
kCGImageAlphaPremultipliedFirst |
); |
CGContextSaveGState(bitmapContext); |
CGRect drawRect = CGRectMake( 0, 0, scanImageWidth, scanImageHeight ); |
CGContextClearRect( bitmapContext, drawRect ); |
CGContextDrawImage( bitmapContext, drawRect, imageRefFinal ); |
int offset = 0; |
for(int j=0;j<(scanImageHeight*scanImageWidth*3);j+=3) |
{ |
bitmap[j] = 0xFF - bitmap[j+1+offset]; |
bitmap[j+1] = 0xFF - bitmap[j+2+offset]; |
bitmap[j+2] = 0xFF - bitmap[j+3+offset]; |
offset++; |
} |
CGContextRestoreGState(bitmapContext); |
CGContextRelease( bitmapContext ); |
if( imageSrcRef ) |
CFRelease( imageSrcRef ); |
if( imageRefOrig ) |
CFRelease( imageRefOrig ); |
if( imageRefFinal) |
CFRelease( imageRefFinal ); |
if( fileRef ) |
CFRelease( fileRef ); |
if( metaData ) |
[metaData release]; |
if( newcolorspace ) |
CGColorSpaceRelease( newcolorspace ); |
return [[NSData alloc] initWithBytesNoCopy: bitmap length: (scanImageHeight * scanImageWidth * 3) freeWhenDone:YES]; |
} |
else |
{ |
return NULL; |
} |
} |
#pragma mark - |
#pragma mark Methods to handle file I/O |
// --------------------------------------------------------------------------------------------------- setOutputFilePath |
// Set up temp file location for file I/O |
- (void)setOutputFilePath |
{ |
NSString* filePath; |
if ( self.documentFolderPath == NULL ) |
{ |
UInt32 i = 0; |
struct stat status; |
do |
{ |
if (i == 0) |
{ |
filePath = [NSString stringWithFormat: @"%@/%08x_scan.%@", NSTemporaryDirectory() ,_deviceObjectInfo->icaObject, self.documentExtension]; |
} |
else |
{ |
filePath = [NSString stringWithFormat: @"%@/%08x_scan %d.%@", NSTemporaryDirectory(), _deviceObjectInfo->icaObject, i, self.documentExtension]; |
} |
i++; |
} while ( 0 == lstat([filePath fileSystemRepresentation], &status) ); |
} |
else |
{ |
NSString* localFolderPath = [self.documentFolderPath stringByExpandingTildeInPath]; |
UInt32 i = 0; |
struct stat status; |
do |
{ |
if (i == 0) |
{ |
filePath = [NSString stringWithFormat: @"%@/%@.%@", localFolderPath, self.documentName, self.documentExtension]; |
} |
else |
{ |
filePath = [NSString stringWithFormat: @"%@/%@ %d.%@", localFolderPath, self.documentName, i, self.documentExtension]; |
} |
i++; |
} while ( 0 == lstat([filePath fileSystemRepresentation], &status) ); |
} |
// remember download file path |
self.documentFilePath = filePath; |
if ( self.isDocumentUTI_ICA_RAW == NO ) |
{ |
// The client has requested the scan file to be saved in a format other than ICA RAW format. |
// But, we will first save the scanned image in a ICA RAW file in a temporary location and then use it |
// to create an image file in the location specified by the client in the format specified by the client. |
NSString* temporaryDirectoryPath = [NSString stringWithFormat:@"%@/icaRawImageXXXXXX.ica", NSTemporaryDirectory()]; |
char template[512] = {0}; |
// creating temp raw image file as backing store |
strlcpy(template, [temporaryDirectoryPath UTF8String], 512); |
mkstemps(template, 4); |
strlcpy( _rawImageFileCachePath, template, 512 ); |
} |
} |
//------------------------------------------------------------------------------------------------ openRawFileForWriting |
- (BOOL)openRawFileForWriting |
{ |
if ( _rawImageFile != NULL ) |
{ |
[self closeRawFile]; |
_rawImageFile = NULL; |
} |
if ( self.isDocumentUTI_ICA_RAW ) |
{ |
// The client has requested final scan in ICA RAW format. Therefore we do no create an intermediary |
// raw cache file. |
_rawImageFile = fopen( [self.documentFilePath fileSystemRepresentation], "w" ); |
} |
else |
{ |
// The client has requested final scan in a format other than ICA RAW format. Therefore we need to create |
// an intermediary raw cache file. |
_rawImageFile = fopen( _rawImageFileCachePath, "w" ); |
} |
return (_rawImageFile != NULL); |
} |
//--------------------------------------------------------------------------------------- writeRawFileWithBuffer:ofSize: |
- (void)writeRawFileWithBuffer:(char*)buffer ofSize:(size_t)size |
{ |
fwrite( buffer, 1, size, _rawImageFile ); |
} |
//------------------------------------------------------------------------------------------------ openRawFileForReading |
- (void)openRawFileForReading |
{ |
_rawImageFile = fopen( _rawImageFileCachePath, "r" ); |
fseek( _rawImageFile, sizeof( ICARawFileHeader ), SEEK_SET); |
} |
//---------------------------------------------------------------------------------------- readRawFileWithBuffer:ofSize: |
- (size_t)readRawFileWithBuffer:(void *)buffer ofSize:(size_t)size |
{ |
return fread( buffer, 1, size, _rawImageFile ); |
} |
//-------------------------------------------------------------------------------------------------------- rewindRawFile |
- (void)rewindRawFile |
{ |
fseek( _rawImageFile, sizeof(ICARawFileHeader), SEEK_SET); |
} |
//--------------------------------------------------------------------------------------------------------- closeRawFile |
- (void)closeRawFile |
{ |
fflush( _rawImageFile ); |
fclose( _rawImageFile ); |
_rawImageFile = NULL; |
} |
// --------------------------------------------------------------------------------------- saveImageWithWidth:andHeight: |
- (void)saveImageWithWidth:(UInt32)scanImageWidth andHeight:(UInt32)scanImageHeight |
{ |
CGImageDestinationRef cgImgDst = NULL; |
NSURL* url = [NSURL fileURLWithPath:self.documentFilePath]; |
[self openRawFileForReading]; |
cgImgDst = CGImageDestinationCreateWithURL( (CFURLRef)url, (CFStringRef)self.documentUTI, 1, nil ); |
if ( cgImgDst ) |
{ |
CGImageDestinationAddImage( cgImgDst, _rawCGImageRef, (CFDictionaryRef)(self.scannedImageMetadata) ); |
CGImageDestinationFinalize( cgImgDst); |
CFRelease( cgImgDst ); |
} |
else |
{ |
printf(" ERROR - CGImageDestinationCreateWithURL failed\n"); |
} |
[self closeRawFile]; |
// get rid of raw image file cache |
remove( _rawImageFileCachePath ); |
} |
#pragma mark - |
#pragma mark Data Provider Methods |
//------------------------------------------------------------------------------------------------------ getBytes:ofSize: |
- (size_t)getBytes:(void *)buffer ofSize:(size_t)size |
{ |
return [self readRawFileWithBuffer:buffer ofSize:size]; |
} |
//----------------------------------------------------------------------------------------------------------- skipBytes: |
- (off_t)skipBytes:(size_t)size |
{ |
return size; |
} |
//--------------------------------------------------------------------------------------------------------------- rewind |
- (void)rewind |
{ |
[self rewindRawFile]; |
} |
//------------------------------------------------------------------------------------------------------ releaseProvider |
- (void)releaseProvider |
{ |
printf("Release Provider\n"); |
} |
#pragma mark - |
#pragma mark Methods to handle button presses on the scanner |
// ----------------------------------------------------------------------------------------------- setLastButtonPressed: |
// Set the '_lastButtonPressed' and send a notification about the button press. |
- (void)setLastButtonPressed:(OSType)button |
{ |
ICASendNotificationPB notePB = {}; |
NSMutableDictionary* notification = [[NSMutableDictionary alloc] initWithCapacity:0]; |
NSNumber* buttonType = [NSNumber numberWithUnsignedInt:(unsigned int)button]; |
_lastButtonPressed = button; |
[notification setObject:[NSNumber numberWithUnsignedInt:_deviceObjectInfo->icaObject] forKey:(id)kICANotificationICAObjectKey]; |
[notification setObject:(id)kICANotificationTypeScannerButtonPressed forKey:(id)kICANotificationTypeKey]; |
[notification setObject:buttonType forKey:(id)kICANotificationScannerButtonTypeKey]; |
notePB.notificationDictionary = (CFMutableDictionaryRef)notification; |
(void)ICDSendNotification( ¬ePB ); |
[notification release]; |
} |
// -------------------------------------------------------------------------------------------------- acquireButtonPress |
- (void)acquireButtonPress |
{ |
// If we were interacting with a real device here, we would want to acquire the status from the device |
// and save it locally for use with the polling mechanism. See the interactive mechanism as well for |
// another example of delivering the notification. |
if ( self.devicePollsForButtonPresses ) |
{ |
// Add code here to read button-press information from the device and save it to 'lastButtonPressed' property |
//self.lastButtonPressed = <button press value read from the device>; |
} |
} |
// ------------------------------------------------------------------------------------------------------ buttonPressed: |
// This method is invoked by asynchronous callback function that receives button press information from the device. |
- (void)buttonPressed:(NSNumber*)button |
{ |
self.lastButtonPressed = [button unsignedIntValue]; |
} |
#pragma mark - |
#pragma mark Support for scanning from remote clients |
//----------------------------------------------------------------------------- updateObjectInfo:forScannedImageAtIndex: |
- (ICAError)updateObjectInfo:(ScannerObjectInfo*)objectInfo forScannedImageAtIndex:(NSUInteger)index |
{ |
ScannedImage* image = ((index < [_scannedImages count]) ? [_scannedImages objectAtIndex:index] : NULL); |
if ( image ) |
{ |
ScannerObjectInfo info = [image objectInfo]; |
memcpy( objectInfo, &info , sizeof( info ) ); |
return noErr; |
} |
else |
{ |
return paramErr; |
} |
} |
//---------------------------------------------------------- readFileDataWithObjectInfo:intoBuffer:withOffset:andLength: |
- (ICAError)readFileDataWithObjectInfo:(const ScannerObjectInfo*)objectInfo |
intoBuffer:(char*)buffer |
withOffset:(UInt32)offset |
andLength:(UInt32*)length |
{ |
ICAError err = paramErr; |
ScannedImage* image = NULL; |
for ( ScannedImage* img in _scannedImages ) |
{ |
if ( img.icaObject == objectInfo->icaObject ) |
{ |
image = img; |
break; |
} |
} |
if ( image ) |
{ |
FILE* file; |
struct stat status; |
if ( 0 == lstat( [image.filePath UTF8String], &status) ) |
{ |
file = fopen( [image.filePath UTF8String], "r" ); |
if ( file ) |
{ |
fseek( file, offset, 0 ); |
fread( buffer, *length, 1, file ); |
fclose( file ); |
err = noErr; |
} |
} |
} |
return err; |
} |
//-------------------------------------------------------------------------------------------------------- removeObject: |
- (ICAError)removeObject:(const ScannerObjectInfo*)objectInfo |
{ |
ICAError err = paramErr; |
ScannedImage* image = NULL; |
for ( ScannedImage* img in _scannedImages ) |
{ |
if ( img.icaObject == objectInfo->icaObject ) |
{ |
image = img; |
break; |
} |
} |
if ( image ) |
{ |
remove ( [image.filePath UTF8String] ); |
[_scannedImages removeObject:image]; |
err = noErr; |
} |
return err; |
} |
//---------------------------------------------------------------------------------------------------------------------- |
@end |
Copyright © 2012 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2012-06-12